aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile2
-rw-r--r--drivers/acpi/namespace/nsnames.c8
-rw-r--r--drivers/acpi/pci_link.c31
-rw-r--r--drivers/acpi/pci_slot.c5
-rw-r--r--drivers/acpi/sleep/main.c42
-rw-r--r--drivers/acpi/system.c1
-rw-r--r--drivers/acpi/tables/tbfadt.c17
-rw-r--r--drivers/acpi/thermal.c43
-rw-r--r--drivers/acpi/utilities/utalloc.c4
-rw-r--r--drivers/acpi/video.c14
-rw-r--r--drivers/ata/ahci.c2
-rw-r--r--drivers/atm/fore200e.c15
-rw-r--r--drivers/auxdisplay/cfag12864b.c13
-rw-r--r--drivers/base/firmware_class.c12
-rw-r--r--drivers/base/memory.c19
-rw-r--r--drivers/block/aoe/aoechr.c9
-rw-r--r--drivers/block/virtio_blk.c10
-rw-r--r--drivers/char/Kconfig26
-rw-r--r--drivers/char/Makefile7
-rw-r--r--drivers/char/ds1302.c17
-rw-r--r--drivers/char/dsp56k.c20
-rw-r--r--drivers/char/efirtc.c35
-rw-r--r--drivers/char/hpet.c2
-rw-r--r--drivers/char/hvc_console.c85
-rw-r--r--drivers/char/hvc_console.h35
-rw-r--r--drivers/char/hvc_irq.c44
-rw-r--r--drivers/char/hvc_iseries.c2
-rw-r--r--drivers/char/hvc_vio.c2
-rw-r--r--drivers/char/hvc_xen.c2
-rw-r--r--drivers/char/ip2/ip2main.c13
-rw-r--r--drivers/char/lcd.c516
-rw-r--r--drivers/char/lcd.h154
-rw-r--r--drivers/char/mem.c5
-rw-r--r--drivers/char/mspec.c23
-rw-r--r--drivers/char/mwave/mwavedd.c39
-rw-r--r--drivers/char/mwave/mwavedd.h2
-rw-r--r--drivers/char/mwave/tp3780i.c2
-rw-r--r--drivers/char/mxser.c337
-rw-r--r--drivers/char/nwflash.c31
-rw-r--r--drivers/char/ppdev.c18
-rw-r--r--drivers/char/random.c1
-rw-r--r--drivers/char/rio/rio_linux.c8
-rw-r--r--drivers/char/rtc.c19
-rw-r--r--drivers/char/stallion.c1
-rw-r--r--drivers/char/sx.c73
-rw-r--r--drivers/char/tty_io.c14
-rw-r--r--drivers/char/virtio_console.c40
-rw-r--r--drivers/char/vt.c8
-rw-r--r--drivers/char/xilinx_hwicap/xilinx_hwicap.c1
-rw-r--r--drivers/edac/Kconfig7
-rw-r--r--drivers/edac/Makefile1
-rw-r--r--drivers/edac/e752x_edac.c59
-rw-r--r--drivers/edac/edac_mc_sysfs.c158
-rw-r--r--drivers/edac/edac_pci_sysfs.c30
-rw-r--r--drivers/edac/i5100_edac.c981
-rw-r--r--drivers/edac/mpc85xx_edac.c67
-rw-r--r--drivers/edac/mv64x60_edac.c37
-rw-r--r--drivers/firmware/dcdbas.c13
-rw-r--r--drivers/firmware/dell_rbu.c28
-rw-r--r--drivers/gpio/Kconfig85
-rw-r--r--drivers/gpio/Makefile4
-rw-r--r--drivers/gpio/bt8xxgpio.c348
-rw-r--r--drivers/gpio/gpiolib.c536
-rw-r--r--drivers/gpio/max732x.c385
-rw-r--r--drivers/gpio/mcp23s08.c134
-rw-r--r--drivers/gpio/pca953x.c1
-rw-r--r--drivers/gpio/pcf857x.c1
-rw-r--r--drivers/i2c/chips/Kconfig2
-rw-r--r--drivers/i2c/chips/tps65010.c2
-rw-r--r--drivers/ide/Kconfig2
-rw-r--r--drivers/ide/Makefile9
-rw-r--r--drivers/ide/arm/icside.c6
-rw-r--r--drivers/ide/arm/palm_bk3710.c9
-rw-r--r--drivers/ide/arm/rapide.c6
-rw-r--r--drivers/ide/ide-cd.c39
-rw-r--r--drivers/ide/ide-disk.c12
-rw-r--r--drivers/ide/ide-dma.c2
-rw-r--r--drivers/ide/ide-floppy.c12
-rw-r--r--drivers/ide/ide-generic.c36
-rw-r--r--drivers/ide/ide-iops.c6
-rw-r--r--drivers/ide/ide-probe.c71
-rw-r--r--drivers/ide/ide-proc.c4
-rw-r--r--drivers/ide/ide-tape.c32
-rw-r--r--drivers/ide/ide-taskfile.c12
-rw-r--r--drivers/ide/ide.c47
-rw-r--r--drivers/ide/legacy/gayle.c8
-rw-r--r--drivers/ide/legacy/ht6560b.c24
-rw-r--r--drivers/ide/pci/aec62xx.c76
-rw-r--r--drivers/ide/pci/alim15x3.c18
-rw-r--r--drivers/ide/pci/amd74xx.c155
-rw-r--r--drivers/ide/pci/atiixp.c20
-rw-r--r--drivers/ide/pci/cmd64x.c50
-rw-r--r--drivers/ide/pci/cs5520.c24
-rw-r--r--drivers/ide/pci/cs5530.c20
-rw-r--r--drivers/ide/pci/cs5535.c13
-rw-r--r--drivers/ide/pci/cy82c693.c35
-rw-r--r--drivers/ide/pci/generic.c78
-rw-r--r--drivers/ide/pci/hpt34x.c21
-rw-r--r--drivers/ide/pci/hpt366.c183
-rw-r--r--drivers/ide/pci/it8213.c35
-rw-r--r--drivers/ide/pci/it821x.c74
-rw-r--r--drivers/ide/pci/jmicron.c13
-rw-r--r--drivers/ide/pci/ns87415.c13
-rw-r--r--drivers/ide/pci/opti621.c13
-rw-r--r--drivers/ide/pci/pdc202xx_new.c78
-rw-r--r--drivers/ide/pci/pdc202xx_old.c49
-rw-r--r--drivers/ide/pci/piix.c118
-rw-r--r--drivers/ide/pci/rz1000.c13
-rw-r--r--drivers/ide/pci/sc1200.c50
-rw-r--r--drivers/ide/pci/serverworks.c40
-rw-r--r--drivers/ide/pci/siimage.c161
-rw-r--r--drivers/ide/pci/sis5513.c39
-rw-r--r--drivers/ide/pci/sl82c105.c17
-rw-r--r--drivers/ide/pci/slc90e66.c13
-rw-r--r--drivers/ide/pci/tc86c001.c57
-rw-r--r--drivers/ide/pci/triflex.c13
-rw-r--r--drivers/ide/pci/trm290.c17
-rw-r--r--drivers/ide/pci/via82cxxx.c139
-rw-r--r--drivers/ide/setup-pci.c240
-rw-r--r--drivers/ieee1394/iso.c1
-rw-r--r--drivers/infiniband/core/cma.c99
-rw-r--r--drivers/infiniband/core/iwcm.c3
-rw-r--r--drivers/infiniband/core/sa_query.c3
-rw-r--r--drivers/infiniband/hw/ehca/ehca_classes.h1
-rw-r--r--drivers/infiniband/hw/ehca/ehca_hca.c4
-rw-r--r--drivers/infiniband/hw/ehca/ehca_irq.c4
-rw-r--r--drivers/infiniband/hw/ehca/ehca_qp.c2
-rw-r--r--drivers/infiniband/hw/ehca/ipz_pt_fn.c1
-rw-r--r--drivers/infiniband/hw/mlx4/cq.c12
-rw-r--r--drivers/infiniband/hw/mlx4/main.c11
-rw-r--r--drivers/infiniband/hw/mlx4/mlx4_ib.h15
-rw-r--r--drivers/infiniband/hw/mlx4/mr.c70
-rw-r--r--drivers/infiniband/hw/mlx4/qp.c74
-rw-r--r--drivers/infiniband/hw/mthca/mthca_dev.h1
-rw-r--r--drivers/infiniband/hw/mthca/mthca_mr.c26
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c2
-rw-r--r--drivers/infiniband/ulp/iser/iser_verbs.c1
-rw-r--r--drivers/input/keyboard/hil_kbd.c1
-rw-r--r--drivers/input/misc/hp_sdc_rtc.c1
-rw-r--r--drivers/input/serio/hp_sdc.c2
-rw-r--r--drivers/isdn/gigaset/asyncdata.c3
-rw-r--r--drivers/isdn/gigaset/bas-gigaset.c12
-rw-r--r--drivers/isdn/gigaset/common.c2
-rw-r--r--drivers/isdn/gigaset/gigaset.h3
-rw-r--r--drivers/isdn/gigaset/i4l.c56
-rw-r--r--drivers/isdn/gigaset/interface.c25
-rw-r--r--drivers/isdn/gigaset/usb-gigaset.c7
-rw-r--r--drivers/isdn/hisax/st5481.h4
-rw-r--r--drivers/isdn/hisax/st5481_b.c4
-rw-r--r--drivers/isdn/hisax/st5481_d.c6
-rw-r--r--drivers/isdn/hisax/st5481_usb.c18
-rw-r--r--drivers/leds/Kconfig16
-rw-r--r--drivers/leds/Makefile2
-rw-r--r--drivers/leds/led-triggers.c3
-rw-r--r--drivers/leds/leds-atmel-pwm.c2
-rw-r--r--drivers/leds/leds-h1940.c9
-rw-r--r--drivers/leds/leds-pca9532.c337
-rw-r--r--drivers/leds/leds-pca955x.c384
-rw-r--r--drivers/lguest/lguest_device.c14
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-dvb.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-ioread.c1
-rw-r--r--drivers/media/video/uvc/uvc_queue.c1
-rw-r--r--drivers/media/video/videobuf-core.c1
-rw-r--r--drivers/message/i2o/device.c54
-rw-r--r--drivers/mfd/Kconfig12
-rw-r--r--drivers/mfd/htc-egpio.c2
-rw-r--r--drivers/mfd/htc-pasic3.c2
-rw-r--r--drivers/mfd/mcp-sa11x0.c2
-rw-r--r--drivers/mfd/mfd-core.c2
-rw-r--r--drivers/mfd/sm501.c439
-rw-r--r--drivers/misc/Kconfig19
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/atmel_pwm.c3
-rw-r--r--drivers/misc/hp-wmi.c494
-rw-r--r--drivers/misc/phantom.c2
-rw-r--r--drivers/misc/thinkpad_acpi.c475
-rw-r--r--drivers/mtd/maps/uclinux.c1
-rw-r--r--drivers/mtd/ubi/build.c99
-rw-r--r--drivers/mtd/ubi/cdev.c234
-rw-r--r--drivers/mtd/ubi/debug.c158
-rw-r--r--drivers/mtd/ubi/debug.h74
-rw-r--r--drivers/mtd/ubi/eba.c77
-rw-r--r--drivers/mtd/ubi/gluebi.c16
-rw-r--r--drivers/mtd/ubi/io.c48
-rw-r--r--drivers/mtd/ubi/kapi.c50
-rw-r--r--drivers/mtd/ubi/misc.c2
-rw-r--r--drivers/mtd/ubi/scan.c136
-rw-r--r--drivers/mtd/ubi/scan.h21
-rw-r--r--drivers/mtd/ubi/ubi-media.h38
-rw-r--r--drivers/mtd/ubi/ubi.h75
-rw-r--r--drivers/mtd/ubi/upd.c32
-rw-r--r--drivers/mtd/ubi/vmt.c148
-rw-r--r--drivers/mtd/ubi/vtbl.c127
-rw-r--r--drivers/mtd/ubi/wl.c208
-rw-r--r--drivers/net/e1000e/netdev.c2
-rw-r--r--drivers/net/fec.c54
-rw-r--r--drivers/net/ibmveth.c189
-rw-r--r--drivers/net/ibmveth.h5
-rw-r--r--drivers/net/mlx4/cmd.c3
-rw-r--r--drivers/net/mlx4/eq.c1
-rw-r--r--drivers/net/mlx4/fw.c18
-rw-r--r--drivers/net/mlx4/fw.h2
-rw-r--r--drivers/net/mlx4/main.c2
-rw-r--r--drivers/net/mlx4/mlx4.h1
-rw-r--r--drivers/net/mlx4/mr.c49
-rw-r--r--drivers/net/mlx4/pd.c7
-rw-r--r--drivers/net/sky2.c5
-rw-r--r--drivers/net/virtio_net.c114
-rw-r--r--drivers/of/Kconfig2
-rw-r--r--drivers/of/of_i2c.c2
-rw-r--r--drivers/parport/parport_ax88796.c2
-rw-r--r--drivers/pci/dmar.c4
-rw-r--r--drivers/pci/pci.c34
-rw-r--r--drivers/pci/proc.c18
-rw-r--r--drivers/pcmcia/electra_cf.c1
-rw-r--r--drivers/power/ds2760_battery.c2
-rw-r--r--drivers/power/pda_power.c2
-rw-r--r--drivers/rtc/Kconfig19
-rw-r--r--drivers/rtc/Makefile2
-rw-r--r--drivers/rtc/rtc-at91rm9200.c16
-rw-r--r--drivers/rtc/rtc-cmos.c294
-rw-r--r--drivers/rtc/rtc-dev.c58
-rw-r--r--drivers/rtc/rtc-ds1305.c847
-rw-r--r--drivers/rtc/rtc-m41t80.c20
-rw-r--r--drivers/rtc/rtc-m41t94.c173
-rw-r--r--drivers/rtc/rtc-omap.c21
-rw-r--r--drivers/rtc/rtc-pcf8583.c129
-rw-r--r--drivers/rtc/rtc-s3c.c89
-rw-r--r--drivers/rtc/rtc-vr41xx.c65
-rw-r--r--drivers/s390/kvm/kvm_virtio.c34
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c15
-rw-r--r--drivers/scsi/ibmvscsi/ibmvscsi.c45
-rw-r--r--drivers/scsi/ibmvscsi/ibmvscsi.h2
-rw-r--r--drivers/scsi/ide-scsi.c33
-rw-r--r--drivers/scsi/sun_esp.c1
-rw-r--r--drivers/serial/8250.c17
-rw-r--r--drivers/serial/8250_gsc.c2
-rw-r--r--drivers/serial/8250_pci.c17
-rw-r--r--drivers/serial/Kconfig16
-rw-r--r--drivers/serial/cpm_uart/cpm_uart_core.c21
-rw-r--r--drivers/serial/dz.c24
-rw-r--r--drivers/serial/zs.c21
-rw-r--r--drivers/spi/Kconfig45
-rw-r--r--drivers/spi/au1550_spi.c207
-rw-r--r--drivers/spi/spi.c4
-rw-r--r--drivers/spi/spi_mpc83xx.c29
-rw-r--r--drivers/spi/spidev.c19
-rw-r--r--drivers/spi/xilinx_spi.c5
-rw-r--r--drivers/telephony/ixj.c17
-rw-r--r--drivers/usb/gadget/at91_udc.h2
-rw-r--r--drivers/usb/gadget/cdc2.c2
-rw-r--r--drivers/usb/gadget/ether.c2
-rw-r--r--drivers/usb/gadget/file_storage.c14
-rw-r--r--drivers/usb/gadget/fsl_usb2_udc.c2
-rw-r--r--drivers/usb/gadget/fsl_usb2_udc.h2
-rw-r--r--drivers/usb/gadget/gmidi.c2
-rw-r--r--drivers/usb/gadget/goku_udc.c2
-rw-r--r--drivers/usb/gadget/goku_udc.h2
-rw-r--r--drivers/usb/gadget/inode.c2
-rw-r--r--drivers/usb/gadget/net2280.c2
-rw-r--r--drivers/usb/gadget/net2280.h2
-rw-r--r--drivers/usb/gadget/omap_udc.c6
-rw-r--r--drivers/usb/gadget/omap_udc.h2
-rw-r--r--drivers/usb/gadget/printer.c2
-rw-r--r--drivers/usb/gadget/pxa25x_udc.c6
-rw-r--r--drivers/usb/gadget/pxa25x_udc.h2
-rw-r--r--drivers/usb/gadget/u_ether.c3
-rw-r--r--drivers/usb/host/isp116x-hcd.c2
-rw-r--r--drivers/usb/host/isp116x.h2
-rw-r--r--drivers/usb/host/sl811-hcd.c2
-rw-r--r--drivers/usb/host/sl811.h2
-rw-r--r--drivers/usb/misc/usbtest.c4
-rw-r--r--drivers/video/Kconfig53
-rw-r--r--drivers/video/Makefile5
-rw-r--r--drivers/video/acornfb.c1
-rw-r--r--drivers/video/amifb.c24
-rw-r--r--drivers/video/atafb.c7
-rw-r--r--drivers/video/atmel_lcdfb.c92
-rw-r--r--drivers/video/aty/aty128fb.c6
-rw-r--r--drivers/video/aty/atyfb_base.c100
-rw-r--r--drivers/video/aty/radeon_base.c20
-rw-r--r--drivers/video/backlight/Kconfig45
-rw-r--r--drivers/video/backlight/Makefile8
-rw-r--r--drivers/video/backlight/atmel-pwm-bl.c244
-rw-r--r--drivers/video/backlight/backlight.c1
-rw-r--r--drivers/video/backlight/ili9320.c330
-rw-r--r--drivers/video/backlight/ili9320.h80
-rw-r--r--drivers/video/backlight/lcd.c2
-rw-r--r--drivers/video/backlight/mbp_nvidia_bl.c116
-rw-r--r--drivers/video/backlight/platform_lcd.c172
-rw-r--r--drivers/video/backlight/vgg2432a4.c284
-rw-r--r--drivers/video/bf54x-lq043fb.c2
-rw-r--r--drivers/video/bfin-t350mcqb-fb.c2
-rw-r--r--drivers/video/carminefb.c790
-rw-r--r--drivers/video/carminefb.h64
-rw-r--r--drivers/video/carminefb_regs.h159
-rw-r--r--drivers/video/cobalt_lcdfb.c371
-rw-r--r--drivers/video/console/fbcon.c5
-rw-r--r--drivers/video/console/fbcon.h8
-rw-r--r--drivers/video/console/mdacon.c4
-rw-r--r--drivers/video/fbmem.c10
-rw-r--r--drivers/video/fbmon.c2
-rw-r--r--drivers/video/fsl-diu-fb.c60
-rw-r--r--drivers/video/geode/lxfb.h2
-rw-r--r--drivers/video/geode/lxfb_ops.c28
-rw-r--r--drivers/video/hgafb.c36
-rw-r--r--drivers/video/imxfb.c1
-rw-r--r--drivers/video/neofb.c215
-rw-r--r--drivers/video/omap/dispc.c1
-rw-r--r--drivers/video/omap/omapfb_main.c1
-rw-r--r--drivers/video/pxafb.c8
-rw-r--r--drivers/video/pxafb.h2
-rw-r--r--drivers/video/sa1100fb.c8
-rw-r--r--drivers/video/sa1100fb.h2
-rw-r--r--drivers/video/sh7760fb.c658
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c725
-rw-r--r--drivers/video/sis/init.h1
-rw-r--r--drivers/video/sis/init301.h1
-rw-r--r--drivers/video/sis/initextlfb.c1
-rw-r--r--drivers/video/sis/osdef.h1
-rw-r--r--drivers/video/sis/sis.h22
-rw-r--r--drivers/video/sis/sis_accel.c1
-rw-r--r--drivers/video/sis/sis_main.c44
-rw-r--r--drivers/video/sis/sis_main.h4
-rw-r--r--drivers/video/sis/vgatypes.h4
-rw-r--r--drivers/video/skeletonfb.c37
-rw-r--r--drivers/video/sm501fb.c329
-rw-r--r--drivers/video/tdfxfb.c8
-rw-r--r--drivers/video/tridentfb.c1350
-rw-r--r--drivers/video/uvesafb.c4
-rw-r--r--drivers/video/vfb.c14
-rw-r--r--drivers/video/vga16fb.c122
-rw-r--r--drivers/virtio/virtio.c26
-rw-r--r--drivers/virtio/virtio_pci.c13
-rw-r--r--drivers/virtio/virtio_ring.c23
-rw-r--r--drivers/watchdog/Kconfig2
-rw-r--r--drivers/watchdog/Makefile2
-rw-r--r--drivers/zorro/zorro-sysfs.c1
338 files changed, 15758 insertions, 5127 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index 808e0ae66aa..54ec5e718c0 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -5,7 +5,7 @@
# Rewritten to use lists instead of if-statements.
#
-obj-$(CONFIG_HAVE_GPIO_LIB) += gpio/
+obj-y += gpio/
obj-$(CONFIG_PCI) += pci/
obj-$(CONFIG_PARISC) += parisc/
obj-$(CONFIG_RAPIDIO) += rapidio/
diff --git a/drivers/acpi/namespace/nsnames.c b/drivers/acpi/namespace/nsnames.c
index cffef1bcbdb..549db42f16c 100644
--- a/drivers/acpi/namespace/nsnames.c
+++ b/drivers/acpi/namespace/nsnames.c
@@ -137,6 +137,10 @@ char *acpi_ns_get_external_pathname(struct acpi_namespace_node *node)
/* Calculate required buffer size based on depth below root */
size = acpi_ns_get_pathname_length(node);
+ if (!size) {
+ ACPI_ERROR((AE_INFO, "Invalid node failure"));
+ return_PTR(NULL);
+ }
/* Allocate a buffer to be returned to caller */
@@ -229,6 +233,10 @@ acpi_ns_handle_to_pathname(acpi_handle target_handle,
/* Determine size required for the caller buffer */
required_size = acpi_ns_get_pathname_length(node);
+ if (!required_size) {
+ ACPI_ERROR((AE_INFO, "Invalid node failure"));
+ return_ACPI_STATUS(AE_ERROR);
+ }
/* Validate/Allocate/Clear caller buffer */
diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c
index 233c40c5168..89f3b2abfdc 100644
--- a/drivers/acpi/pci_link.c
+++ b/drivers/acpi/pci_link.c
@@ -113,20 +113,23 @@ acpi_pci_link_check_possible(struct acpi_resource *resource, void *context)
switch (resource->type) {
case ACPI_RESOURCE_TYPE_START_DEPENDENT:
+ case ACPI_RESOURCE_TYPE_END_TAG:
return AE_OK;
case ACPI_RESOURCE_TYPE_IRQ:
{
struct acpi_resource_irq *p = &resource->data.irq;
if (!p || !p->interrupt_count) {
- printk(KERN_WARNING PREFIX "Blank IRQ resource\n");
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Blank _PRS IRQ resource\n"));
return AE_OK;
}
for (i = 0;
(i < p->interrupt_count
&& i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) {
if (!p->interrupts[i]) {
- printk(KERN_WARNING PREFIX "Invalid IRQ %d\n",
- p->interrupts[i]);
+ printk(KERN_WARNING PREFIX
+ "Invalid _PRS IRQ %d\n",
+ p->interrupts[i]);
continue;
}
link->irq.possible[i] = p->interrupts[i];
@@ -143,15 +146,16 @@ acpi_pci_link_check_possible(struct acpi_resource *resource, void *context)
&resource->data.extended_irq;
if (!p || !p->interrupt_count) {
printk(KERN_WARNING PREFIX
- "Blank EXT IRQ resource\n");
+ "Blank _PRS EXT IRQ resource\n");
return AE_OK;
}
for (i = 0;
(i < p->interrupt_count
&& i < ACPI_PCI_LINK_MAX_POSSIBLE); i++) {
if (!p->interrupts[i]) {
- printk(KERN_WARNING PREFIX "Invalid IRQ %d\n",
- p->interrupts[i]);
+ printk(KERN_WARNING PREFIX
+ "Invalid _PRS IRQ %d\n",
+ p->interrupts[i]);
continue;
}
link->irq.possible[i] = p->interrupts[i];
@@ -163,7 +167,8 @@ acpi_pci_link_check_possible(struct acpi_resource *resource, void *context)
break;
}
default:
- printk(KERN_ERR PREFIX "Resource is not an IRQ entry\n");
+ printk(KERN_ERR PREFIX "_PRS resource type 0x%x isn't an IRQ\n",
+ resource->type);
return AE_OK;
}
@@ -199,6 +204,9 @@ acpi_pci_link_check_current(struct acpi_resource *resource, void *context)
switch (resource->type) {
+ case ACPI_RESOURCE_TYPE_START_DEPENDENT:
+ case ACPI_RESOURCE_TYPE_END_TAG:
+ return AE_OK;
case ACPI_RESOURCE_TYPE_IRQ:
{
struct acpi_resource_irq *p = &resource->data.irq;
@@ -208,7 +216,7 @@ acpi_pci_link_check_current(struct acpi_resource *resource, void *context)
* particularly those those w/ _STA disabled
*/
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "Blank IRQ resource\n"));
+ "Blank _CRS IRQ resource\n"));
return AE_OK;
}
*irq = p->interrupts[0];
@@ -224,7 +232,7 @@ acpi_pci_link_check_current(struct acpi_resource *resource, void *context)
* return at least 1 IRQ
*/
printk(KERN_WARNING PREFIX
- "Blank EXT IRQ resource\n");
+ "Blank _CRS EXT IRQ resource\n");
return AE_OK;
}
*irq = p->interrupts[0];
@@ -232,10 +240,11 @@ acpi_pci_link_check_current(struct acpi_resource *resource, void *context)
}
break;
default:
- printk(KERN_ERR PREFIX "Resource %d isn't an IRQ\n", resource->type);
- case ACPI_RESOURCE_TYPE_END_TAG:
+ printk(KERN_ERR PREFIX "_CRS resource type 0x%x isn't an IRQ\n",
+ resource->type);
return AE_OK;
}
+
return AE_CTRL_TERMINATE;
}
diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c
index b9ab030a52d..dd376f7ad09 100644
--- a/drivers/acpi/pci_slot.c
+++ b/drivers/acpi/pci_slot.c
@@ -6,8 +6,8 @@
* Thanks to Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> for code
* review and fixes.
*
- * Copyright (C) 2007 Alex Chiang <achiang@hp.com>
- * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
+ * Copyright (C) 2007-2008 Hewlett-Packard Development Company, L.P.
+ * Alex Chiang <achiang@hp.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -158,6 +158,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
if (IS_ERR(pci_slot)) {
err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot));
kfree(slot);
+ return AE_OK;
}
slot->root_handle = parent_context->root_handle;
diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c
index 0489a7d1d42..d13194a031b 100644
--- a/drivers/acpi/sleep/main.c
+++ b/drivers/acpi/sleep/main.c
@@ -280,9 +280,36 @@ static struct platform_suspend_ops acpi_suspend_ops_old = {
.end = acpi_pm_end,
.recover = acpi_pm_finish,
};
+
+static int __init init_old_suspend_ordering(const struct dmi_system_id *d)
+{
+ old_suspend_ordering = true;
+ return 0;
+}
+
+static struct dmi_system_id __initdata acpisleep_dmi_table[] = {
+ {
+ .callback = init_old_suspend_ordering,
+ .ident = "Abit KN9 (nForce4 variant)",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "http://www.abit.com.tw/"),
+ DMI_MATCH(DMI_BOARD_NAME, "KN9 Series(NF-CK804)"),
+ },
+ },
+ {},
+};
#endif /* CONFIG_SUSPEND */
#ifdef CONFIG_HIBERNATION
+static unsigned long s4_hardware_signature;
+static struct acpi_table_facs *facs;
+static bool nosigcheck;
+
+void __init acpi_no_s4_hw_signature(void)
+{
+ nosigcheck = true;
+}
+
static int acpi_hibernation_begin(void)
{
acpi_target_sleep_state = ACPI_STATE_S4;
@@ -316,6 +343,12 @@ static void acpi_hibernation_leave(void)
acpi_enable();
/* Reprogram control registers and execute _BFS */
acpi_leave_sleep_state_prep(ACPI_STATE_S4);
+ /* Check the hardware signature */
+ if (facs && s4_hardware_signature != facs->hardware_signature) {
+ printk(KERN_EMERG "ACPI: Hardware changed while hibernated, "
+ "cannot resume!\n");
+ panic("ACPI S4 hardware signature mismatch");
+ }
}
static void acpi_pm_enable_gpes(void)
@@ -516,6 +549,8 @@ int __init acpi_sleep_init(void)
u8 type_a, type_b;
#ifdef CONFIG_SUSPEND
int i = 0;
+
+ dmi_check_system(acpisleep_dmi_table);
#endif
if (acpi_disabled)
@@ -544,6 +579,13 @@ int __init acpi_sleep_init(void)
&acpi_hibernation_ops_old : &acpi_hibernation_ops);
sleep_states[ACPI_STATE_S4] = 1;
printk(" S4");
+ if (!nosigcheck) {
+ acpi_get_table_by_index(ACPI_TABLE_INDEX_FACS,
+ (struct acpi_table_header **)&facs);
+ if (facs)
+ s4_hardware_signature =
+ facs->hardware_signature;
+ }
}
#endif
status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b);
diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c
index d8e3f153b29..91dec448b3e 100644
--- a/drivers/acpi/system.c
+++ b/drivers/acpi/system.c
@@ -26,6 +26,7 @@
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/init.h>
+#include <linux/string.h>
#include <asm/uaccess.h>
#include <acpi/acpi_drivers.h>
diff --git a/drivers/acpi/tables/tbfadt.c b/drivers/acpi/tables/tbfadt.c
index ccb5b64bbef..a4a41ba2484 100644
--- a/drivers/acpi/tables/tbfadt.c
+++ b/drivers/acpi/tables/tbfadt.c
@@ -124,7 +124,7 @@ static struct acpi_fadt_info fadt_info_table[] = {
static void inline
acpi_tb_init_generic_address(struct acpi_generic_address *generic_address,
- u8 byte_width, u64 address)
+ u8 bit_width, u64 address)
{
/*
@@ -136,7 +136,7 @@ acpi_tb_init_generic_address(struct acpi_generic_address *generic_address,
/* All other fields are byte-wide */
generic_address->space_id = ACPI_ADR_SPACE_SYSTEM_IO;
- generic_address->bit_width = byte_width << 3;
+ generic_address->bit_width = bit_width;
generic_address->bit_offset = 0;
generic_address->access_width = 0;
}
@@ -343,11 +343,9 @@ static void acpi_tb_convert_fadt(void)
*
* The PM event blocks are split into two register blocks, first is the
* PM Status Register block, followed immediately by the PM Enable Register
- * block. Each is of length (xpm1x_event_block.bit_width/2)
+ * block. Each is of length (pm1_event_length/2)
*/
- WARN_ON(ACPI_MOD_16(acpi_gbl_FADT.xpm1a_event_block.bit_width));
- pm1_register_length = (u8) ACPI_DIV_16(acpi_gbl_FADT
- .xpm1a_event_block.bit_width);
+ pm1_register_length = (u8) ACPI_DIV_2(acpi_gbl_FADT.pm1_event_length);
/* The PM1A register block is required */
@@ -362,17 +360,14 @@ static void acpi_tb_convert_fadt(void)
/* The PM1B register block is optional, ignore if not present */
if (acpi_gbl_FADT.xpm1b_event_block.address) {
- WARN_ON(ACPI_MOD_16(acpi_gbl_FADT.xpm1b_event_block.bit_width));
- pm1_register_length = (u8) ACPI_DIV_16(acpi_gbl_FADT
- .xpm1b_event_block
- .bit_width);
acpi_tb_init_generic_address(&acpi_gbl_xpm1b_enable,
pm1_register_length,
(acpi_gbl_FADT.xpm1b_event_block.
address + pm1_register_length));
/* Don't forget to copy space_id of the GAS */
acpi_gbl_xpm1b_enable.space_id =
- acpi_gbl_FADT.xpm1b_event_block.space_id;
+ acpi_gbl_FADT.xpm1a_event_block.space_id;
+
}
}
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 30a34133793..912703691d3 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -769,6 +769,47 @@ static void acpi_thermal_run(unsigned long data)
acpi_os_execute(OSL_GPE_HANDLER, acpi_thermal_check, (void *)data);
}
+static void acpi_thermal_active_off(void *data)
+{
+ int result = 0;
+ struct acpi_thermal *tz = data;
+ int i = 0;
+ int j = 0;
+ struct acpi_thermal_active *active = NULL;
+
+ if (!tz) {
+ printk(KERN_ERR PREFIX "Invalid (NULL) context\n");
+ return;
+ }
+
+ result = acpi_thermal_get_temperature(tz);
+ if (result)
+ return;
+
+ for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
+ active = &(tz->trips.active[i]);
+ if (!active || !active->flags.valid)
+ break;
+ if (tz->temperature >= active->temperature) {
+ /*
+ * If the thermal temperature is greater than the
+ * active threshod, unnecessary to turn off the
+ * the active cooling device.
+ */
+ continue;
+ }
+ /*
+ * Below Threshold?
+ * ----------------
+ * Turn OFF all cooling devices associated with this
+ * threshold.
+ */
+ for (j = 0; j < active->devices.count; j++)
+ result = acpi_bus_set_power(active->devices.handles[j],
+ ACPI_STATE_D3);
+ }
+}
+
static void acpi_thermal_check(void *data)
{
int result = 0;
@@ -1624,6 +1665,8 @@ static int acpi_thermal_add(struct acpi_device *device)
init_timer(&tz->timer);
+ acpi_thermal_active_off(tz);
+
acpi_thermal_check(tz);
status = acpi_install_notify_handler(device->handle,
diff --git a/drivers/acpi/utilities/utalloc.c b/drivers/acpi/utilities/utalloc.c
index 3dfb8a442b2..e7bf34a7b1d 100644
--- a/drivers/acpi/utilities/utalloc.c
+++ b/drivers/acpi/utilities/utalloc.c
@@ -242,6 +242,10 @@ acpi_ut_initialize_buffer(struct acpi_buffer * buffer,
{
acpi_status status = AE_OK;
+ if (!required_length) {
+ WARN_ON(1);
+ return AE_ERROR;
+ }
switch (buffer->length) {
case ACPI_NO_BUFFER:
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index 37b9e16710d..e8a51a1700f 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
@@ -741,7 +741,7 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
max_level = acpi_video_init_brightness(device);
- if (device->cap._BCL && device->cap._BCM && device->cap._BQC && max_level > 0){
+ if (device->cap._BCL && device->cap._BCM && max_level > 0) {
int result;
static int count = 0;
char *name;
@@ -753,7 +753,17 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
device->backlight = backlight_device_register(name,
NULL, device, &acpi_backlight_ops);
device->backlight->props.max_brightness = device->brightness->count-3;
- device->backlight->props.brightness = acpi_video_get_brightness(device->backlight);
+ /*
+ * If there exists the _BQC object, the _BQC object will be
+ * called to get the current backlight brightness. Otherwise
+ * the brightness will be set to the maximum.
+ */
+ if (device->cap._BQC)
+ device->backlight->props.brightness =
+ acpi_video_get_brightness(device->backlight);
+ else
+ device->backlight->props.brightness =
+ device->backlight->props.max_brightness;
backlight_update_status(device->backlight);
kfree(name);
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index dc7596f028b..ef3e5522e1a 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -1273,7 +1273,7 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state,
void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
u32 em_ctl;
u32 message[] = {0, 0};
- unsigned int flags;
+ unsigned long flags;
int pmp;
struct ahci_em_priv *emp;
diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c
index d5c1bbfbe79..73338d231db 100644
--- a/drivers/atm/fore200e.c
+++ b/drivers/atm/fore200e.c
@@ -2562,7 +2562,8 @@ fore200e_load_and_start_fw(struct fore200e* fore200e)
const struct firmware *firmware;
struct device *device;
struct fw_header *fw_header;
- u32 *fw_data, fw_size;
+ const __le32 *fw_data;
+ u32 fw_size;
u32 __iomem *load_addr;
char buf[48];
int err = -ENODEV;
@@ -2582,7 +2583,7 @@ fore200e_load_and_start_fw(struct fore200e* fore200e)
return err;
}
- fw_data = (u32 *) firmware->data;
+ fw_data = (__le32 *) firmware->data;
fw_size = firmware->size / sizeof(u32);
fw_header = (struct fw_header *) firmware->data;
load_addr = fore200e->virt_base + le32_to_cpu(fw_header->load_offset);
@@ -3199,6 +3200,14 @@ static const struct fore200e_bus fore200e_bus[] = {
{}
};
-#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
+#ifdef CONFIG_PCI
+#ifdef __LITTLE_ENDIAN__
+MODULE_FIRMWARE("pca200e.bin");
+#else
+MODULE_FIRMWARE("pca200e_ecd.bin2");
+#endif
+#endif /* CONFIG_PCI */
+#ifdef CONFIG_SBUS
+MODULE_FIRMWARE("sba200e_ecd.bin2");
#endif
diff --git a/drivers/auxdisplay/cfag12864b.c b/drivers/auxdisplay/cfag12864b.c
index 683509f013a..eacb175f6bd 100644
--- a/drivers/auxdisplay/cfag12864b.c
+++ b/drivers/auxdisplay/cfag12864b.c
@@ -336,16 +336,9 @@ static int __init cfag12864b_init(void)
"ks0108 is not initialized\n");
goto none;
}
+ BUILD_BUG_ON(PAGE_SIZE < CFAG12864B_SIZE);
- if (PAGE_SIZE < CFAG12864B_SIZE) {
- printk(KERN_ERR CFAG12864B_NAME ": ERROR: "
- "page size (%i) < cfag12864b size (%i)\n",
- (unsigned int)PAGE_SIZE, CFAG12864B_SIZE);
- ret = -ENOMEM;
- goto none;
- }
-
- cfag12864b_buffer = (unsigned char *) __get_free_page(GFP_KERNEL);
+ cfag12864b_buffer = (unsigned char *) get_zeroed_page(GFP_KERNEL);
if (cfag12864b_buffer == NULL) {
printk(KERN_ERR CFAG12864B_NAME ": ERROR: "
"can't get a free page\n");
@@ -367,8 +360,6 @@ static int __init cfag12864b_init(void)
if (cfag12864b_workqueue == NULL)
goto cachealloced;
- memset(cfag12864b_buffer, 0, CFAG12864B_SIZE);
-
cfag12864b_clear();
cfag12864b_on();
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index b0be1d18fee..c9c92b00fd5 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -184,7 +184,7 @@ firmware_data_read(struct kobject *kobj, struct bin_attribute *bin_attr,
struct device *dev = to_dev(kobj);
struct firmware_priv *fw_priv = dev_get_drvdata(dev);
struct firmware *fw;
- ssize_t ret_count = count;
+ ssize_t ret_count;
mutex_lock(&fw_lock);
fw = fw_priv->fw;
@@ -192,14 +192,8 @@ firmware_data_read(struct kobject *kobj, struct bin_attribute *bin_attr,
ret_count = -ENODEV;
goto out;
}
- if (offset > fw->size) {
- ret_count = 0;
- goto out;
- }
- if (offset + ret_count > fw->size)
- ret_count = fw->size - offset;
-
- memcpy(buffer, fw->data + offset, ret_count);
+ ret_count = memory_read_from_buffer(buffer, count, &offset,
+ fw->data, fw->size);
out:
mutex_unlock(&fw_lock);
return ret_count;
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 4d4e0e7b6e9..855ed1a9f97 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -101,6 +101,21 @@ static ssize_t show_mem_phys_index(struct sys_device *dev,
}
/*
+ * Show whether the section of memory is likely to be hot-removable
+ */
+static ssize_t show_mem_removable(struct sys_device *dev, char *buf)
+{
+ unsigned long start_pfn;
+ int ret;
+ struct memory_block *mem =
+ container_of(dev, struct memory_block, sysdev);
+
+ start_pfn = section_nr_to_pfn(mem->phys_index);
+ ret = is_mem_section_removable(start_pfn, PAGES_PER_SECTION);
+ return sprintf(buf, "%d\n", ret);
+}
+
+/*
* online, offline, going offline, etc.
*/
static ssize_t show_mem_state(struct sys_device *dev,
@@ -262,6 +277,7 @@ static ssize_t show_phys_device(struct sys_device *dev,
static SYSDEV_ATTR(phys_index, 0444, show_mem_phys_index, NULL);
static SYSDEV_ATTR(state, 0644, show_mem_state, store_mem_state);
static SYSDEV_ATTR(phys_device, 0444, show_phys_device, NULL);
+static SYSDEV_ATTR(removable, 0444, show_mem_removable, NULL);
#define mem_create_simple_file(mem, attr_name) \
sysdev_create_file(&mem->sysdev, &attr_##attr_name)
@@ -350,6 +366,8 @@ static int add_memory_block(unsigned long node_id, struct mem_section *section,
ret = mem_create_simple_file(mem, state);
if (!ret)
ret = mem_create_simple_file(mem, phys_device);
+ if (!ret)
+ ret = mem_create_simple_file(mem, removable);
return ret;
}
@@ -394,6 +412,7 @@ int remove_memory_block(unsigned long node_id, struct mem_section *section,
mem_remove_simple_file(mem, phys_index);
mem_remove_simple_file(mem, state);
mem_remove_simple_file(mem, phys_device);
+ mem_remove_simple_file(mem, removable);
unregister_memory(mem, section);
return 0;
diff --git a/drivers/block/aoe/aoechr.c b/drivers/block/aoe/aoechr.c
index c04440cd6a3..181ebb85f0b 100644
--- a/drivers/block/aoe/aoechr.c
+++ b/drivers/block/aoe/aoechr.c
@@ -6,6 +6,7 @@
#include <linux/hdreg.h>
#include <linux/blkdev.h>
+#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/smp_lock.h>
#include "aoe.h"
@@ -36,7 +37,7 @@ struct ErrMsg {
static struct ErrMsg emsgs[NMSG];
static int emsgs_head_idx, emsgs_tail_idx;
-static struct semaphore emsgs_sema;
+static struct completion emsgs_comp;
static spinlock_t emsgs_lock;
static int nblocked_emsgs_readers;
static struct class *aoe_class;
@@ -141,7 +142,7 @@ bail: spin_unlock_irqrestore(&emsgs_lock, flags);
spin_unlock_irqrestore(&emsgs_lock, flags);
if (nblocked_emsgs_readers)
- up(&emsgs_sema);
+ complete(&emsgs_comp);
}
static ssize_t
@@ -221,7 +222,7 @@ aoechr_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
spin_unlock_irqrestore(&emsgs_lock, flags);
- n = down_interruptible(&emsgs_sema);
+ n = wait_for_completion_interruptible(&emsgs_comp);
spin_lock_irqsave(&emsgs_lock, flags);
@@ -269,7 +270,7 @@ aoechr_init(void)
printk(KERN_ERR "aoe: can't register char device\n");
return n;
}
- sema_init(&emsgs_sema, 0);
+ init_completion(&emsgs_comp);
spin_lock_init(&emsgs_lock);
aoe_class = class_create(THIS_MODULE, "aoe");
if (IS_ERR(aoe_class)) {
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index dd7ea203f94..42251095134 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -196,6 +196,7 @@ static int virtblk_probe(struct virtio_device *vdev)
int err;
u64 cap;
u32 v;
+ u32 blk_size;
if (index_to_minor(index) >= 1 << MINORBITS)
return -ENOSPC;
@@ -290,6 +291,13 @@ static int virtblk_probe(struct virtio_device *vdev)
if (!err)
blk_queue_max_hw_segments(vblk->disk->queue, v);
+ /* Host can optionally specify the block size of the device */
+ err = virtio_config_val(vdev, VIRTIO_BLK_F_BLK_SIZE,
+ offsetof(struct virtio_blk_config, blk_size),
+ &blk_size);
+ if (!err)
+ blk_queue_hardsect_size(vblk->disk->queue, blk_size);
+
add_disk(vblk->disk);
return 0;
@@ -330,7 +338,7 @@ static struct virtio_device_id id_table[] = {
static unsigned int features[] = {
VIRTIO_BLK_F_BARRIER, VIRTIO_BLK_F_SEG_MAX, VIRTIO_BLK_F_SIZE_MAX,
- VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_RO,
+ VIRTIO_BLK_F_GEOMETRY, VIRTIO_BLK_F_RO, VIRTIO_BLK_F_BLK_SIZE,
};
static struct virtio_driver virtio_blk = {
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index e0bbbfb6a36..d0ac944e169 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -36,6 +36,14 @@ config VT
If unsure, say Y, or else you won't be able to do much with your new
shiny Linux system :-)
+config CONSOLE_TRANSLATIONS
+ depends on VT
+ default y
+ bool "Enable character translations in console" if EMBEDDED
+ ---help---
+ This enables support for font mapping and Unicode translation
+ on virtual consoles.
+
config VT_CONSOLE
bool "Support for console on virtual terminal" if EMBEDDED
depends on VT
@@ -578,11 +586,14 @@ config HVC_DRIVER
It will automatically be selected if one of the back-end console drivers
is selected.
+config HVC_IRQ
+ bool
config HVC_CONSOLE
bool "pSeries Hypervisor Virtual Console support"
depends on PPC_PSERIES
select HVC_DRIVER
+ select HVC_IRQ
help
pSeries machines when partitioned support a hypervisor virtual
console. This driver allows each pSeries partition to have a console
@@ -593,6 +604,7 @@ config HVC_ISERIES
depends on PPC_ISERIES
default y
select HVC_DRIVER
+ select HVC_IRQ
help
iSeries machines support a hypervisor virtual console.
@@ -614,13 +626,18 @@ config HVC_XEN
bool "Xen Hypervisor Console support"
depends on XEN
select HVC_DRIVER
+ select HVC_IRQ
default y
help
Xen virtual console device driver
config VIRTIO_CONSOLE
- bool
+ tristate "Virtio console"
+ depends on VIRTIO
select HVC_DRIVER
+ help
+ Virtio console for use with lguest and other hypervisors.
+
config HVCS
tristate "IBM Hypervisor Virtual Console Server support"
@@ -857,13 +874,6 @@ config DS1302
endif # RTC_LIB
-config COBALT_LCD
- bool "Support for Cobalt LCD"
- depends on MIPS_COBALT
- help
- This option enables support for the LCD display and buttons found
- on Cobalt systems through a misc device.
-
config DTLK
tristate "Double Talk PC internal speech card support"
depends on ISA
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index dc5a327d72d..8a161c30e1d 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -12,8 +12,8 @@ obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o
obj-$(CONFIG_LEGACY_PTYS) += pty.o
obj-$(CONFIG_UNIX98_PTYS) += pty.o
obj-y += misc.o
-obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o consolemap.o \
- consolemap_deftbl.o selection.o keyboard.o
+obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o selection.o keyboard.o
+obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o
obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o
obj-$(CONFIG_AUDIT) += tty_audit.o
obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o
@@ -48,6 +48,7 @@ obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o
obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o
obj-$(CONFIG_HVC_BEAT) += hvc_beat.o
obj-$(CONFIG_HVC_DRIVER) += hvc_console.o
+obj-$(CONFIG_HVC_IRQ) += hvc_irq.o
obj-$(CONFIG_HVC_XEN) += hvc_xen.o
obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o
obj-$(CONFIG_RAW_DRIVER) += raw.o
@@ -63,7 +64,6 @@ obj-$(CONFIG_BRIQ_PANEL) += briq_panel.o
obj-$(CONFIG_BFIN_OTP) += bfin-otp.o
obj-$(CONFIG_PRINTER) += lp.o
-obj-$(CONFIG_TIPAR) += tipar.o
obj-$(CONFIG_APM_EMULATION) += apm-emulation.o
@@ -88,7 +88,6 @@ obj-$(CONFIG_TOSHIBA) += toshiba.o
obj-$(CONFIG_I8K) += i8k.o
obj-$(CONFIG_DS1620) += ds1620.o
obj-$(CONFIG_HW_RANDOM) += hw_random/
-obj-$(CONFIG_COBALT_LCD) += lcd.o
obj-$(CONFIG_PPDEV) += ppdev.o
obj-$(CONFIG_NWBUTTON) += nwbutton.o
obj-$(CONFIG_NWFLASH) += nwflash.o
diff --git a/drivers/char/ds1302.c b/drivers/char/ds1302.c
index fada6ddefba..c5e67a62395 100644
--- a/drivers/char/ds1302.c
+++ b/drivers/char/ds1302.c
@@ -20,10 +20,11 @@
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/bcd.h>
+#include <linux/smp_lock.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
-#include <asm/uaccess.h>
#include <asm/system.h>
-#include <asm/io.h>
#include <asm/rtc.h>
#if defined(CONFIG_M32R)
#include <asm/m32r.h>
@@ -153,9 +154,7 @@ static unsigned char days_in_mo[] =
/* ioctl that supports RTC_RD_TIME and RTC_SET_TIME (read and set time/date). */
-static int
-rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
- unsigned long arg)
+static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
unsigned long flags;
@@ -165,7 +164,9 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
struct rtc_time rtc_tm;
memset(&rtc_tm, 0, sizeof (struct rtc_time));
+ lock_kernel();
get_rtc_time(&rtc_tm);
+ unlock_kernel();
if (copy_to_user((struct rtc_time*)arg, &rtc_tm, sizeof(struct rtc_time)))
return -EFAULT;
return 0;
@@ -217,6 +218,7 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
BIN_TO_BCD(mon);
BIN_TO_BCD(yrs);
+ lock_kernel();
local_irq_save(flags);
CMOS_WRITE(yrs, RTC_YEAR);
CMOS_WRITE(mon, RTC_MONTH);
@@ -225,6 +227,7 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
CMOS_WRITE(min, RTC_MINUTES);
CMOS_WRITE(sec, RTC_SECONDS);
local_irq_restore(flags);
+ unlock_kernel();
/* Notice that at this point, the RTC is updated but
* the kernel is still running with the old time.
@@ -244,8 +247,10 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
if(copy_from_user(&tcs_val, (int*)arg, sizeof(int)))
return -EFAULT;
+ lock_kernel();
tcs_val = RTC_TCR_PATTERN | (tcs_val & 0x0F);
ds1302_writereg(RTC_TRICKLECHARGER, tcs_val);
+ unlock_kernel();
return 0;
}
default:
@@ -282,7 +287,7 @@ get_rtc_status(char *buf)
static const struct file_operations rtc_fops = {
.owner = THIS_MODULE,
- .ioctl = rtc_ioctl,
+ .unlocked_ioctl = rtc_ioctl,
};
/* Probe for the chip by writing something to its RAM and try reading it back. */
diff --git a/drivers/char/dsp56k.c b/drivers/char/dsp56k.c
index 33c466a4888..19b88504e96 100644
--- a/drivers/char/dsp56k.c
+++ b/drivers/char/dsp56k.c
@@ -36,10 +36,10 @@
#include <linux/smp_lock.h>
#include <linux/firmware.h>
#include <linux/platform_device.h>
+#include <linux/uaccess.h> /* For put_user and get_user */
#include <asm/atarihw.h>
#include <asm/traps.h>
-#include <asm/uaccess.h> /* For put_user and get_user */
#include <asm/dsp56k.h>
@@ -303,8 +303,8 @@ static ssize_t dsp56k_write(struct file *file, const char __user *buf, size_t co
}
}
-static int dsp56k_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static long dsp56k_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
int dev = iminor(inode) & 0x0f;
void __user *argp = (void __user *)arg;
@@ -331,8 +331,9 @@ static int dsp56k_ioctl(struct inode *inode, struct file *file,
if (len > DSP56K_MAX_BINARY_LENGTH) {
return -EINVAL;
}
-
+ lock_kernel();
r = dsp56k_upload(bin, len);
+ unlock_kernel();
if (r < 0) {
return r;
}
@@ -342,12 +343,16 @@ static int dsp56k_ioctl(struct inode *inode, struct file *file,
case DSP56K_SET_TX_WSIZE:
if (arg > 4 || arg < 1)
return -EINVAL;
+ lock_kernel();
dsp56k.tx_wsize = (int) arg;
+ unlock_kernel();
break;
case DSP56K_SET_RX_WSIZE:
if (arg > 4 || arg < 1)
return -EINVAL;
+ lock_kernel();
dsp56k.rx_wsize = (int) arg;
+ unlock_kernel();
break;
case DSP56K_HOST_FLAGS:
{
@@ -359,6 +364,7 @@ static int dsp56k_ioctl(struct inode *inode, struct file *file,
if(get_user(out, &hf->out) < 0)
return -EFAULT;
+ lock_kernel();
if ((dir & 0x1) && (out & 0x1))
dsp56k_host_interface.icr |= DSP56K_ICR_HF0;
else if (dir & 0x1)
@@ -373,14 +379,16 @@ static int dsp56k_ioctl(struct inode *inode, struct file *file,
if (dsp56k_host_interface.icr & DSP56K_ICR_HF1) status |= 0x2;
if (dsp56k_host_interface.isr & DSP56K_ISR_HF2) status |= 0x4;
if (dsp56k_host_interface.isr & DSP56K_ISR_HF3) status |= 0x8;
-
+ unlock_kernel();
return put_user(status, &hf->status);
}
case DSP56K_HOST_CMD:
if (arg > 31 || arg < 0)
return -EINVAL;
+ lock_kernel();
dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) |
DSP56K_CVR_HC);
+ unlock_kernel();
break;
default:
return -EINVAL;
@@ -472,7 +480,7 @@ static const struct file_operations dsp56k_fops = {
.owner = THIS_MODULE,
.read = dsp56k_read,
.write = dsp56k_write,
- .ioctl = dsp56k_ioctl,
+ .unlocked_ioctl = dsp56k_ioctl,
.open = dsp56k_open,
.release = dsp56k_release,
};
diff --git a/drivers/char/efirtc.c b/drivers/char/efirtc.c
index d57ca3e4e53..67fbd7aab5d 100644
--- a/drivers/char/efirtc.c
+++ b/drivers/char/efirtc.c
@@ -37,8 +37,9 @@
#include <linux/rtc.h>
#include <linux/proc_fs.h>
#include <linux/efi.h>
+#include <linux/smp_lock.h>
+#include <linux/uaccess.h>
-#include <asm/uaccess.h>
#include <asm/system.h>
#define EFI_RTC_VERSION "0.4"
@@ -51,8 +52,8 @@
static DEFINE_SPINLOCK(efi_rtc_lock);
-static int efi_rtc_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg);
+static long efi_rtc_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
#define is_leap(year) \
((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
@@ -146,9 +147,8 @@ convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime)
}
}
-static int
-efi_rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
- unsigned long arg)
+static long efi_rtc_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
efi_status_t status;
@@ -175,13 +175,13 @@ efi_rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
return -EINVAL;
case RTC_RD_TIME:
-
+ lock_kernel();
spin_lock_irqsave(&efi_rtc_lock, flags);
status = efi.get_time(&eft, &cap);
spin_unlock_irqrestore(&efi_rtc_lock,flags);
-
+ unlock_kernel();
if (status != EFI_SUCCESS) {
/* should never happen */
printk(KERN_ERR "efitime: can't read time\n");
@@ -203,11 +203,13 @@ efi_rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
convert_to_efi_time(&wtime, &eft);
+ lock_kernel();
spin_lock_irqsave(&efi_rtc_lock, flags);
status = efi.set_time(&eft);
spin_unlock_irqrestore(&efi_rtc_lock,flags);
+ unlock_kernel();
return status == EFI_SUCCESS ? 0 : -EINVAL;
@@ -223,6 +225,7 @@ efi_rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
convert_to_efi_time(&wtime, &eft);
+ lock_kernel();
spin_lock_irqsave(&efi_rtc_lock, flags);
/*
* XXX Fixme:
@@ -233,16 +236,19 @@ efi_rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
status = efi.set_wakeup_time((efi_bool_t)enabled, &eft);
spin_unlock_irqrestore(&efi_rtc_lock,flags);
+ unlock_kernel();
return status == EFI_SUCCESS ? 0 : -EINVAL;
case RTC_WKALM_RD:
+ lock_kernel();
spin_lock_irqsave(&efi_rtc_lock, flags);
status = efi.get_wakeup_time((efi_bool_t *)&enabled, (efi_bool_t *)&pending, &eft);
spin_unlock_irqrestore(&efi_rtc_lock,flags);
+ unlock_kernel();
if (status != EFI_SUCCESS) return -EINVAL;
@@ -256,7 +262,7 @@ efi_rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
return copy_to_user(&ewp->time, &wtime,
sizeof(struct rtc_time)) ? -EFAULT : 0;
}
- return -EINVAL;
+ return -ENOTTY;
}
/*
@@ -265,8 +271,7 @@ efi_rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
* up things on a close.
*/
-static int
-efi_rtc_open(struct inode *inode, struct file *file)
+static int efi_rtc_open(struct inode *inode, struct file *file)
{
/*
* nothing special to do here
@@ -277,8 +282,7 @@ efi_rtc_open(struct inode *inode, struct file *file)
return 0;
}
-static int
-efi_rtc_close(struct inode *inode, struct file *file)
+static int efi_rtc_close(struct inode *inode, struct file *file)
{
return 0;
}
@@ -289,13 +293,12 @@ efi_rtc_close(struct inode *inode, struct file *file)
static const struct file_operations efi_rtc_fops = {
.owner = THIS_MODULE,
- .ioctl = efi_rtc_ioctl,
+ .unlocked_ioctl = efi_rtc_ioctl,
.open = efi_rtc_open,
.release = efi_rtc_close,
};
-static struct miscdevice efi_rtc_dev=
-{
+static struct miscdevice efi_rtc_dev= {
EFI_RTC_MINOR,
"efirtc",
&efi_rtc_fops
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index fb0a85a1eb3..b3f5dbc6d88 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -623,6 +623,7 @@ static inline int hpet_tpcheck(struct hpet_task *tp)
return -ENXIO;
}
+#if 0
int hpet_unregister(struct hpet_task *tp)
{
struct hpet_dev *devp;
@@ -652,6 +653,7 @@ int hpet_unregister(struct hpet_task *tp)
return 0;
}
+#endif /* 0 */
static ctl_table hpet_table[] = {
{
diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c
index 2f9759d625c..02aac104842 100644
--- a/drivers/char/hvc_console.c
+++ b/drivers/char/hvc_console.c
@@ -27,7 +27,6 @@
#include <linux/init.h>
#include <linux/kbd_kern.h>
#include <linux/kernel.h>
-#include <linux/kref.h>
#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/module.h>
@@ -75,23 +74,6 @@ static int hvc_init(void);
static int sysrq_pressed;
#endif
-struct hvc_struct {
- spinlock_t lock;
- int index;
- struct tty_struct *tty;
- unsigned int count;
- int do_wakeup;
- char *outbuf;
- int outbuf_size;
- int n_outbuf;
- uint32_t vtermno;
- struct hv_ops *ops;
- int irq_requested;
- int irq;
- struct list_head next;
- struct kref kref; /* ref count & hvc_struct lifetime */
-};
-
/* dynamic list of hvc_struct instances */
static LIST_HEAD(hvc_structs);
@@ -298,27 +280,15 @@ int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops)
return 0;
}
+EXPORT_SYMBOL_GPL(hvc_instantiate);
/* Wake the sleeping khvcd */
-static void hvc_kick(void)
+void hvc_kick(void)
{
hvc_kicked = 1;
wake_up_process(hvc_task);
}
-
-static int hvc_poll(struct hvc_struct *hp);
-
-/*
- * NOTE: This API isn't used if the console adapter doesn't support interrupts.
- * In this case the console is poll driven.
- */
-static irqreturn_t hvc_handle_interrupt(int irq, void *dev_instance)
-{
- /* if hvc_poll request a repoll, then kick the hvcd thread */
- if (hvc_poll(dev_instance))
- hvc_kick();
- return IRQ_HANDLED;
-}
+EXPORT_SYMBOL_GPL(hvc_kick);
static void hvc_unthrottle(struct tty_struct *tty)
{
@@ -333,7 +303,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
{
struct hvc_struct *hp;
unsigned long flags;
- int irq = 0;
int rc = 0;
/* Auto increments kref reference if found. */
@@ -352,18 +321,15 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
tty->low_latency = 1; /* Makes flushes to ldisc synchronous. */
hp->tty = tty;
- /* Save for request_irq outside of spin_lock. */
- irq = hp->irq;
- if (irq)
- hp->irq_requested = 1;
+
+ if (hp->ops->notifier_add)
+ rc = hp->ops->notifier_add(hp, hp->data);
spin_unlock_irqrestore(&hp->lock, flags);
- /* check error, fallback to non-irq */
- if (irq)
- rc = request_irq(irq, hvc_handle_interrupt, IRQF_DISABLED, "hvc_console", hp);
+
/*
- * If the request_irq() fails and we return an error. The tty layer
+ * If the notifier fails we return an error. The tty layer
* will call hvc_close() after a failed open but we don't want to clean
* up there so we'll clean up here and clear out the previously set
* tty fields and return the kref reference.
@@ -371,7 +337,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
if (rc) {
spin_lock_irqsave(&hp->lock, flags);
hp->tty = NULL;
- hp->irq_requested = 0;
spin_unlock_irqrestore(&hp->lock, flags);
tty->driver_data = NULL;
kref_put(&hp->kref, destroy_hvc_struct);
@@ -386,7 +351,6 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
static void hvc_close(struct tty_struct *tty, struct file * filp)
{
struct hvc_struct *hp;
- int irq = 0;
unsigned long flags;
if (tty_hung_up_p(filp))
@@ -404,9 +368,8 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
spin_lock_irqsave(&hp->lock, flags);
if (--hp->count == 0) {
- if (hp->irq_requested)
- irq = hp->irq;
- hp->irq_requested = 0;
+ if (hp->ops->notifier_del)
+ hp->ops->notifier_del(hp, hp->data);
/* We are done with the tty pointer now. */
hp->tty = NULL;
@@ -418,10 +381,6 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
* waking periodically to check chars_in_buffer().
*/
tty_wait_until_sent(tty, HVC_CLOSE_WAIT);
-
- if (irq)
- free_irq(irq, hp);
-
} else {
if (hp->count < 0)
printk(KERN_ERR "hvc_close %X: oops, count is %d\n",
@@ -436,7 +395,6 @@ static void hvc_hangup(struct tty_struct *tty)
{
struct hvc_struct *hp = tty->driver_data;
unsigned long flags;
- int irq = 0;
int temp_open_count;
if (!hp)
@@ -458,13 +416,12 @@ static void hvc_hangup(struct tty_struct *tty)
hp->count = 0;
hp->n_outbuf = 0;
hp->tty = NULL;
- if (hp->irq_requested)
- /* Saved for use outside of spin_lock. */
- irq = hp->irq;
- hp->irq_requested = 0;
+
+ if (hp->ops->notifier_del)
+ hp->ops->notifier_del(hp, hp->data);
+
spin_unlock_irqrestore(&hp->lock, flags);
- if (irq)
- free_irq(irq, hp);
+
while(temp_open_count) {
--temp_open_count;
kref_put(&hp->kref, destroy_hvc_struct);
@@ -575,7 +532,7 @@ static u32 timeout = MIN_TIMEOUT;
#define HVC_POLL_READ 0x00000001
#define HVC_POLL_WRITE 0x00000002
-static int hvc_poll(struct hvc_struct *hp)
+int hvc_poll(struct hvc_struct *hp)
{
struct tty_struct *tty;
int i, n, poll_mask = 0;
@@ -602,10 +559,10 @@ static int hvc_poll(struct hvc_struct *hp)
if (test_bit(TTY_THROTTLED, &tty->flags))
goto throttled;
- /* If we aren't interrupt driven and aren't throttled, we always
+ /* If we aren't notifier driven and aren't throttled, we always
* request a reschedule
*/
- if (hp->irq == 0)
+ if (!hp->irq_requested)
poll_mask |= HVC_POLL_READ;
/* Read data if any */
@@ -674,6 +631,7 @@ static int hvc_poll(struct hvc_struct *hp)
return poll_mask;
}
+EXPORT_SYMBOL_GPL(hvc_poll);
/*
* This kthread is either polling or interrupt driven. This is determined by
@@ -733,7 +691,7 @@ static const struct tty_operations hvc_ops = {
.chars_in_buffer = hvc_chars_in_buffer,
};
-struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq,
+struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int data,
struct hv_ops *ops, int outbuf_size)
{
struct hvc_struct *hp;
@@ -754,7 +712,7 @@ struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq,
memset(hp, 0x00, sizeof(*hp));
hp->vtermno = vtermno;
- hp->irq = irq;
+ hp->data = data;
hp->ops = ops;
hp->outbuf_size = outbuf_size;
hp->outbuf = &((char *)hp)[ALIGN(sizeof(*hp), sizeof(long))];
@@ -784,6 +742,7 @@ struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq,
return hp;
}
+EXPORT_SYMBOL_GPL(hvc_alloc);
int __devexit hvc_remove(struct hvc_struct *hp)
{
diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h
index 42ffb17e15d..d9ce1091562 100644
--- a/drivers/char/hvc_console.h
+++ b/drivers/char/hvc_console.h
@@ -26,6 +26,7 @@
#ifndef HVC_CONSOLE_H
#define HVC_CONSOLE_H
+#include <linux/kref.h>
/*
* This is the max number of console adapters that can/will be found as
@@ -42,24 +43,50 @@
*/
#define HVC_ALLOC_TTY_ADAPTERS 8
+struct hvc_struct {
+ spinlock_t lock;
+ int index;
+ struct tty_struct *tty;
+ unsigned int count;
+ int do_wakeup;
+ char *outbuf;
+ int outbuf_size;
+ int n_outbuf;
+ uint32_t vtermno;
+ struct hv_ops *ops;
+ int irq_requested;
+ int data;
+ struct list_head next;
+ struct kref kref; /* ref count & hvc_struct lifetime */
+};
/* implemented by a low level driver */
struct hv_ops {
int (*get_chars)(uint32_t vtermno, char *buf, int count);
int (*put_chars)(uint32_t vtermno, const char *buf, int count);
-};
-struct hvc_struct;
+ /* Callbacks for notification. Called in open and close */
+ int (*notifier_add)(struct hvc_struct *hp, int irq);
+ void (*notifier_del)(struct hvc_struct *hp, int irq);
+};
/* Register a vterm and a slot index for use as a console (console_init) */
extern int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops);
/* register a vterm for hvc tty operation (module_init or hotplug add) */
-extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int irq,
+extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int data,
struct hv_ops *ops, int outbuf_size);
-/* remove a vterm from hvc tty operation (modele_exit or hotplug remove) */
+/* remove a vterm from hvc tty operation (module_exit or hotplug remove) */
extern int __devexit hvc_remove(struct hvc_struct *hp);
+/* data available */
+int hvc_poll(struct hvc_struct *hp);
+void hvc_kick(void);
+
+/* default notifier for irq based notification */
+extern int notifier_add_irq(struct hvc_struct *hp, int data);
+extern void notifier_del_irq(struct hvc_struct *hp, int data);
+
#if defined(CONFIG_XMON) && defined(CONFIG_SMP)
#include <asm/xmon.h>
diff --git a/drivers/char/hvc_irq.c b/drivers/char/hvc_irq.c
new file mode 100644
index 00000000000..73a59cdb894
--- /dev/null
+++ b/drivers/char/hvc_irq.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright IBM Corp. 2001,2008
+ *
+ * This file contains the IRQ specific code for hvc_console
+ *
+ */
+
+#include <linux/interrupt.h>
+
+#include "hvc_console.h"
+
+static irqreturn_t hvc_handle_interrupt(int irq, void *dev_instance)
+{
+ /* if hvc_poll request a repoll, then kick the hvcd thread */
+ if (hvc_poll(dev_instance))
+ hvc_kick();
+ return IRQ_HANDLED;
+}
+
+/*
+ * For IRQ based systems these callbacks can be used
+ */
+int notifier_add_irq(struct hvc_struct *hp, int irq)
+{
+ int rc;
+
+ if (!irq) {
+ hp->irq_requested = 0;
+ return 0;
+ }
+ rc = request_irq(irq, hvc_handle_interrupt, IRQF_DISABLED,
+ "hvc_console", hp);
+ if (!rc)
+ hp->irq_requested = 1;
+ return rc;
+}
+
+void notifier_del_irq(struct hvc_struct *hp, int irq)
+{
+ if (!irq)
+ return;
+ free_irq(irq, hp);
+ hp->irq_requested = 0;
+}
diff --git a/drivers/char/hvc_iseries.c b/drivers/char/hvc_iseries.c
index a08f8f981c1..b71c610fe5a 100644
--- a/drivers/char/hvc_iseries.c
+++ b/drivers/char/hvc_iseries.c
@@ -200,6 +200,8 @@ done:
static struct hv_ops hvc_get_put_ops = {
.get_chars = get_chars,
.put_chars = put_chars,
+ .notifier_add = notifier_add_irq,
+ .notifier_del = notifier_del_irq,
};
static int __devinit hvc_vio_probe(struct vio_dev *vdev,
diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c
index 79711aa4b41..93f3840c168 100644
--- a/drivers/char/hvc_vio.c
+++ b/drivers/char/hvc_vio.c
@@ -80,6 +80,8 @@ static int filtered_get_chars(uint32_t vtermno, char *buf, int count)
static struct hv_ops hvc_get_put_ops = {
.get_chars = filtered_get_chars,
.put_chars = hvc_put_chars,
+ .notifier_add = notifier_add_irq,
+ .notifier_del = notifier_del_irq,
};
static int __devinit hvc_vio_probe(struct vio_dev *vdev,
diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c
index db2ae421627..6b70aa66a58 100644
--- a/drivers/char/hvc_xen.c
+++ b/drivers/char/hvc_xen.c
@@ -100,6 +100,8 @@ static int read_console(uint32_t vtermno, char *buf, int len)
static struct hv_ops hvc_ops = {
.get_chars = read_console,
.put_chars = write_console,
+ .notifier_add = notifier_add_irq,
+ .notifier_del = notifier_del_irq,
};
static int __init xen_init(void)
diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c
index 9cb48fcd316..689f9dcd3b8 100644
--- a/drivers/char/ip2/ip2main.c
+++ b/drivers/char/ip2/ip2main.c
@@ -203,7 +203,7 @@ static int set_serial_info(i2ChanStrPtr, struct serial_struct __user *);
static ssize_t ip2_ipl_read(struct file *, char __user *, size_t, loff_t *);
static ssize_t ip2_ipl_write(struct file *, const char __user *, size_t, loff_t *);
-static int ip2_ipl_ioctl(struct inode *, struct file *, UINT, ULONG);
+static long ip2_ipl_ioctl(struct file *, UINT, ULONG);
static int ip2_ipl_open(struct inode *, struct file *);
static int DumpTraceBuffer(char __user *, int);
@@ -236,7 +236,7 @@ static const struct file_operations ip2_ipl = {
.owner = THIS_MODULE,
.read = ip2_ipl_read,
.write = ip2_ipl_write,
- .ioctl = ip2_ipl_ioctl,
+ .unlocked_ioctl = ip2_ipl_ioctl,
.open = ip2_ipl_open,
};
@@ -2845,10 +2845,10 @@ ip2_ipl_write(struct file *pFile, const char __user *pData, size_t count, loff_t
/* */
/* */
/******************************************************************************/
-static int
-ip2_ipl_ioctl ( struct inode *pInode, struct file *pFile, UINT cmd, ULONG arg )
+static long
+ip2_ipl_ioctl (struct file *pFile, UINT cmd, ULONG arg )
{
- unsigned int iplminor = iminor(pInode);
+ unsigned int iplminor = iminor(pFile->f_path.dentry->d_inode);
int rc = 0;
void __user *argp = (void __user *)arg;
ULONG __user *pIndex = argp;
@@ -2859,6 +2859,8 @@ ip2_ipl_ioctl ( struct inode *pInode, struct file *pFile, UINT cmd, ULONG arg )
printk (KERN_DEBUG "IP2IPL: ioctl cmd %d, arg %ld\n", cmd, arg );
#endif
+ lock_kernel();
+
switch ( iplminor ) {
case 0: // IPL device
rc = -EINVAL;
@@ -2919,6 +2921,7 @@ ip2_ipl_ioctl ( struct inode *pInode, struct file *pFile, UINT cmd, ULONG arg )
rc = -ENODEV;
break;
}
+ unlock_kernel();
return rc;
}
diff --git a/drivers/char/lcd.c b/drivers/char/lcd.c
deleted file mode 100644
index 1c29b20e4f4..00000000000
--- a/drivers/char/lcd.c
+++ /dev/null
@@ -1,516 +0,0 @@
-/*
- * LCD, LED and Button interface for Cobalt
- *
- * 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.
- *
- * Copyright (C) 1996, 1997 by Andrew Bose
- *
- * Linux kernel version history:
- * March 2001: Ported from 2.0.34 by Liam Davies
- *
- */
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/miscdevice.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <linux/fcntl.h>
-#include <linux/mc146818rtc.h>
-#include <linux/netdevice.h>
-#include <linux/sched.h>
-#include <linux/smp_lock.h>
-#include <linux/delay.h>
-
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-
-#include "lcd.h"
-
-static int lcd_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg);
-
-static unsigned int lcd_present = 1;
-
-/* used in arch/mips/cobalt/reset.c */
-int led_state = 0;
-
-#if defined(CONFIG_TULIP) && 0
-
-#define MAX_INTERFACES 8
-static linkcheck_func_t linkcheck_callbacks[MAX_INTERFACES];
-static void *linkcheck_cookies[MAX_INTERFACES];
-
-int lcd_register_linkcheck_func(int iface_num, void *func, void *cookie)
-{
- if (iface_num < 0 ||
- iface_num >= MAX_INTERFACES ||
- linkcheck_callbacks[iface_num] != NULL)
- return -1;
- linkcheck_callbacks[iface_num] = (linkcheck_func_t) func;
- linkcheck_cookies[iface_num] = cookie;
- return 0;
-}
-#endif
-
-static int lcd_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct lcd_display button_display;
- unsigned long address, a;
-
- switch (cmd) {
- case LCD_On:
- udelay(150);
- BusyCheck();
- LCDWriteInst(0x0F);
- break;
-
- case LCD_Off:
- udelay(150);
- BusyCheck();
- LCDWriteInst(0x08);
- break;
-
- case LCD_Reset:
- udelay(150);
- LCDWriteInst(0x3F);
- udelay(150);
- LCDWriteInst(0x3F);
- udelay(150);
- LCDWriteInst(0x3F);
- udelay(150);
- LCDWriteInst(0x3F);
- udelay(150);
- LCDWriteInst(0x01);
- udelay(150);
- LCDWriteInst(0x06);
- break;
-
- case LCD_Clear:
- udelay(150);
- BusyCheck();
- LCDWriteInst(0x01);
- break;
-
- case LCD_Cursor_Left:
- udelay(150);
- BusyCheck();
- LCDWriteInst(0x10);
- break;
-
- case LCD_Cursor_Right:
- udelay(150);
- BusyCheck();
- LCDWriteInst(0x14);
- break;
-
- case LCD_Cursor_Off:
- udelay(150);
- BusyCheck();
- LCDWriteInst(0x0C);
- break;
-
- case LCD_Cursor_On:
- udelay(150);
- BusyCheck();
- LCDWriteInst(0x0F);
- break;
-
- case LCD_Blink_Off:
- udelay(150);
- BusyCheck();
- LCDWriteInst(0x0E);
- break;
-
- case LCD_Get_Cursor_Pos:{
- struct lcd_display display;
-
- udelay(150);
- BusyCheck();
- display.cursor_address = (LCDReadInst);
- display.cursor_address =
- (display.cursor_address & 0x07F);
- if (copy_to_user
- ((struct lcd_display *) arg, &display,
- sizeof(struct lcd_display)))
- return -EFAULT;
-
- break;
- }
-
-
- case LCD_Set_Cursor_Pos:{
- struct lcd_display display;
-
- if (copy_from_user
- (&display, (struct lcd_display *) arg,
- sizeof(struct lcd_display)))
- return -EFAULT;
-
- a = (display.cursor_address | kLCD_Addr);
-
- udelay(150);
- BusyCheck();
- LCDWriteInst(a);
-
- break;
- }
-
- case LCD_Get_Cursor:{
- struct lcd_display display;
-
- udelay(150);
- BusyCheck();
- display.character = LCDReadData;
-
- if (copy_to_user
- ((struct lcd_display *) arg, &display,
- sizeof(struct lcd_display)))
- return -EFAULT;
- udelay(150);
- BusyCheck();
- LCDWriteInst(0x10);
-
- break;
- }
-
- case LCD_Set_Cursor:{
- struct lcd_display display;
-
- if (copy_from_user
- (&display, (struct lcd_display *) arg,
- sizeof(struct lcd_display)))
- return -EFAULT;
-
- udelay(150);
- BusyCheck();
- LCDWriteData(display.character);
- udelay(150);
- BusyCheck();
- LCDWriteInst(0x10);
-
- break;
- }
-
-
- case LCD_Disp_Left:
- udelay(150);
- BusyCheck();
- LCDWriteInst(0x18);
- break;
-
- case LCD_Disp_Right:
- udelay(150);
- BusyCheck();
- LCDWriteInst(0x1C);
- break;
-
- case LCD_Home:
- udelay(150);
- BusyCheck();
- LCDWriteInst(0x02);
- break;
-
- case LCD_Write:{
- struct lcd_display display;
- unsigned int index;
-
-
- if (copy_from_user
- (&display, (struct lcd_display *) arg,
- sizeof(struct lcd_display)))
- return -EFAULT;
-
- udelay(150);
- BusyCheck();
- LCDWriteInst(0x80);
- udelay(150);
- BusyCheck();
-
- for (index = 0; index < (display.size1); index++) {
- udelay(150);
- BusyCheck();
- LCDWriteData(display.line1[index]);
- BusyCheck();
- }
-
- udelay(150);
- BusyCheck();
- LCDWriteInst(0xC0);
- udelay(150);
- BusyCheck();
- for (index = 0; index < (display.size2); index++) {
- udelay(150);
- BusyCheck();
- LCDWriteData(display.line2[index]);
- }
-
- break;
- }
-
- case LCD_Read:{
- struct lcd_display display;
-
- BusyCheck();
- for (address = kDD_R00; address <= kDD_R01;
- address++) {
- a = (address | kLCD_Addr);
-
- udelay(150);
- BusyCheck();
- LCDWriteInst(a);
- udelay(150);
- BusyCheck();
- display.line1[address] = LCDReadData;
- }
-
- display.line1[0x27] = '\0';
-
- for (address = kDD_R10; address <= kDD_R11;
- address++) {
- a = (address | kLCD_Addr);
-
- udelay(150);
- BusyCheck();
- LCDWriteInst(a);
-
- udelay(150);
- BusyCheck();
- display.line2[address - 0x40] =
- LCDReadData;
- }
-
- display.line2[0x27] = '\0';
-
- if (copy_to_user
- ((struct lcd_display *) arg, &display,
- sizeof(struct lcd_display)))
- return -EFAULT;
- break;
- }
-
-// set all GPIO leds to led_display.leds
-
- case LED_Set:{
- struct lcd_display led_display;
-
-
- if (copy_from_user
- (&led_display, (struct lcd_display *) arg,
- sizeof(struct lcd_display)))
- return -EFAULT;
-
- led_state = led_display.leds;
- LEDSet(led_state);
-
- break;
- }
-
-
-// set only bit led_display.leds
-
- case LED_Bit_Set:{
- unsigned int i;
- int bit = 1;
- struct lcd_display led_display;
-
-
- if (copy_from_user
- (&led_display, (struct lcd_display *) arg,
- sizeof(struct lcd_display)))
- return -EFAULT;
-
- for (i = 0; i < (int) led_display.leds; i++) {
- bit = 2 * bit;
- }
-
- led_state = led_state | bit;
- LEDSet(led_state);
- break;
- }
-
-// clear only bit led_display.leds
-
- case LED_Bit_Clear:{
- unsigned int i;
- int bit = 1;
- struct lcd_display led_display;
-
-
- if (copy_from_user
- (&led_display, (struct lcd_display *) arg,
- sizeof(struct lcd_display)))
- return -EFAULT;
-
- for (i = 0; i < (int) led_display.leds; i++) {
- bit = 2 * bit;
- }
-
- led_state = led_state & ~bit;
- LEDSet(led_state);
- break;
- }
-
-
- case BUTTON_Read:{
- button_display.buttons = GPIRead;
- if (copy_to_user
- ((struct lcd_display *) arg, &button_display,
- sizeof(struct lcd_display)))
- return -EFAULT;
- break;
- }
-
- case LINK_Check:{
- button_display.buttons =
- *((volatile unsigned long *) (0xB0100060));
- if (copy_to_user
- ((struct lcd_display *) arg, &button_display,
- sizeof(struct lcd_display)))
- return -EFAULT;
- break;
- }
-
- case LINK_Check_2:{
- int iface_num;
-
- /* panel-utils should pass in the desired interface status is wanted for
- * in "buttons" of the structure. We will set this to non-zero if the
- * link is in fact up for the requested interface. --DaveM
- */
- if (copy_from_user
- (&button_display, (struct lcd_display *) arg,
- sizeof(button_display)))
- return -EFAULT;
- iface_num = button_display.buttons;
-#if defined(CONFIG_TULIP) && 0
- if (iface_num >= 0 &&
- iface_num < MAX_INTERFACES &&
- linkcheck_callbacks[iface_num] != NULL) {
- button_display.buttons =
- linkcheck_callbacks[iface_num]
- (linkcheck_cookies[iface_num]);
- } else
-#endif
- button_display.buttons = 0;
-
- if (__copy_to_user
- ((struct lcd_display *) arg, &button_display,
- sizeof(struct lcd_display)))
- return -EFAULT;
- break;
- }
-
- default:
- return -EINVAL;
-
- }
-
- return 0;
-
-}
-
-static int lcd_open(struct inode *inode, struct file *file)
-{
- cycle_kernel_lock();
-
- if (!lcd_present)
- return -ENXIO;
- else
- return 0;
-}
-
-/* Only RESET or NEXT counts as button pressed */
-
-static inline int button_pressed(void)
-{
- unsigned long buttons = GPIRead;
-
- if ((buttons == BUTTON_Next) || (buttons == BUTTON_Next_B)
- || (buttons == BUTTON_Reset_B))
- return buttons;
- return 0;
-}
-
-/* LED daemon sits on this and we wake him up once a key is pressed. */
-
-static int lcd_waiters = 0;
-
-static ssize_t lcd_read(struct file *file, char *buf,
- size_t count, loff_t *ofs)
-{
- long buttons_now;
-
- if (lcd_waiters > 0)
- return -EINVAL;
-
- lcd_waiters++;
- while (((buttons_now = (long) button_pressed()) == 0) &&
- !(signal_pending(current))) {
- msleep_interruptible(2000);
- }
- lcd_waiters--;
-
- if (signal_pending(current))
- return -ERESTARTSYS;
- return buttons_now;
-}
-
-/*
- * The various file operations we support.
- */
-
-static const struct file_operations lcd_fops = {
- .read = lcd_read,
- .ioctl = lcd_ioctl,
- .open = lcd_open,
-};
-
-static struct miscdevice lcd_dev = {
- MISC_DYNAMIC_MINOR,
- "lcd",
- &lcd_fops
-};
-
-static int lcd_init(void)
-{
- int ret;
- unsigned long data;
-
- pr_info("%s\n", LCD_DRIVER);
- ret = misc_register(&lcd_dev);
- if (ret) {
- printk(KERN_WARNING LCD "Unable to register misc device.\n");
- return ret;
- }
-
- /* Check region? Naaah! Just snarf it up. */
-/* request_region(RTC_PORT(0), RTC_IO_EXTENT, "lcd");*/
-
- udelay(150);
- data = LCDReadData;
- if ((data & 0x000000FF) == (0x00)) {
- lcd_present = 0;
- pr_info(LCD "LCD Not Present\n");
- } else {
- lcd_present = 1;
- WRITE_GAL(kGal_DevBank2PReg, kGal_DevBank2Cfg);
- WRITE_GAL(kGal_DevBank3PReg, kGal_DevBank3Cfg);
- }
-
- return 0;
-}
-
-static void __exit lcd_exit(void)
-{
- misc_deregister(&lcd_dev);
-}
-
-module_init(lcd_init);
-module_exit(lcd_exit);
-
-MODULE_AUTHOR("Andrew Bose");
-MODULE_LICENSE("GPL");
diff --git a/drivers/char/lcd.h b/drivers/char/lcd.h
deleted file mode 100644
index 290b3ff23b0..00000000000
--- a/drivers/char/lcd.h
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * LED, LCD and Button panel driver for Cobalt
- *
- * 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.
- *
- * Copyright (C) 1996, 1997 by Andrew Bose
- *
- * Linux kernel version history:
- * March 2001: Ported from 2.0.34 by Liam Davies
- *
- */
-
-// function headers
-
-#define LCD_CHARS_PER_LINE 40
-#define MAX_IDLE_TIME 120
-
-struct lcd_display {
- unsigned buttons;
- int size1;
- int size2;
- unsigned char line1[LCD_CHARS_PER_LINE];
- unsigned char line2[LCD_CHARS_PER_LINE];
- unsigned char cursor_address;
- unsigned char character;
- unsigned char leds;
- unsigned char *RomImage;
-};
-
-
-
-#define LCD_DRIVER "Cobalt LCD Driver v2.10"
-
-#define LCD "lcd: "
-
-#define kLCD_IR 0x0F000000
-#define kLCD_DR 0x0F000010
-#define kGPI 0x0D000000
-#define kLED 0x0C000000
-
-#define kDD_R00 0x00
-#define kDD_R01 0x27
-#define kDD_R10 0x40
-#define kDD_R11 0x67
-
-#define kLCD_Addr 0x00000080
-
-#define LCDTimeoutValue 0xfff
-
-
-// Macros
-
-#define LCDWriteData(x) outl((x << 24), kLCD_DR)
-#define LCDWriteInst(x) outl((x << 24), kLCD_IR)
-
-#define LCDReadData (inl(kLCD_DR) >> 24)
-#define LCDReadInst (inl(kLCD_IR) >> 24)
-
-#define GPIRead (inl(kGPI) >> 24)
-
-#define LEDSet(x) outb((char)x, kLED)
-
-#define WRITE_GAL(x,y) outl(y, 0x04000000 | (x))
-#define BusyCheck() while ((LCDReadInst & 0x80) == 0x80)
-
-
-
-/*
- * Function command codes for io_ctl.
- */
-#define LCD_On 1
-#define LCD_Off 2
-#define LCD_Clear 3
-#define LCD_Reset 4
-#define LCD_Cursor_Left 5
-#define LCD_Cursor_Right 6
-#define LCD_Disp_Left 7
-#define LCD_Disp_Right 8
-#define LCD_Get_Cursor 9
-#define LCD_Set_Cursor 10
-#define LCD_Home 11
-#define LCD_Read 12
-#define LCD_Write 13
-#define LCD_Cursor_Off 14
-#define LCD_Cursor_On 15
-#define LCD_Get_Cursor_Pos 16
-#define LCD_Set_Cursor_Pos 17
-#define LCD_Blink_Off 18
-
-#define LED_Set 40
-#define LED_Bit_Set 41
-#define LED_Bit_Clear 42
-
-
-// Button defs
-#define BUTTON_Read 50
-
-
-// Ethernet LINK check hackaroo
-#define LINK_Check 90
-#define LINK_Check_2 91
-
-// Button patterns _B - single layer lcd boards
-
-#define BUTTON_NONE 0x3F
-#define BUTTON_NONE_B 0xFE
-
-#define BUTTON_Left 0x3B
-#define BUTTON_Left_B 0xFA
-
-#define BUTTON_Right 0x37
-#define BUTTON_Right_B 0xDE
-
-#define BUTTON_Up 0x2F
-#define BUTTON_Up_B 0xF6
-
-#define BUTTON_Down 0x1F
-#define BUTTON_Down_B 0xEE
-
-#define BUTTON_Next 0x3D
-#define BUTTON_Next_B 0x7E
-
-#define BUTTON_Enter 0x3E
-#define BUTTON_Enter_B 0xBE
-
-#define BUTTON_Reset_B 0xFC
-
-
-// debounce constants
-
-#define BUTTON_SENSE 160000
-#define BUTTON_DEBOUNCE 5000
-
-
-// Galileo register stuff
-
-#define kGal_DevBank2Cfg 0x1466DB33
-#define kGal_DevBank2PReg 0x464
-#define kGal_DevBank3Cfg 0x146FDFFB
-#define kGal_DevBank3PReg 0x468
-
-// Network
-
-#define kIPADDR 1
-#define kNETMASK 2
-#define kGATEWAY 3
-#define kDNS 4
-
-#define kClassA 5
-#define kClassB 6
-#define kClassC 7
-
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index c2dba82eb5f..672b08e694d 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -327,7 +327,10 @@ static void mmap_mem_close(struct vm_area_struct *vma)
static struct vm_operations_struct mmap_mem_ops = {
.open = mmap_mem_open,
- .close = mmap_mem_close
+ .close = mmap_mem_close,
+#ifdef CONFIG_HAVE_IOREMAP_PROT
+ .access = generic_access_phys
+#endif
};
static int mmap_mem(struct file * file, struct vm_area_struct * vma)
diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c
index fe2a95b5d3c..30f095a8c2d 100644
--- a/drivers/char/mspec.c
+++ b/drivers/char/mspec.c
@@ -193,25 +193,23 @@ mspec_close(struct vm_area_struct *vma)
}
/*
- * mspec_nopfn
+ * mspec_fault
*
* Creates a mspec page and maps it to user space.
*/
-static unsigned long
-mspec_nopfn(struct vm_area_struct *vma, unsigned long address)
+static int
+mspec_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
unsigned long paddr, maddr;
unsigned long pfn;
- int index;
+ pgoff_t index = vmf->pgoff;
struct vma_data *vdata = vma->vm_private_data;
- BUG_ON(address < vdata->vm_start || address >= vdata->vm_end);
- index = (address - vdata->vm_start) >> PAGE_SHIFT;
maddr = (volatile unsigned long) vdata->maddr[index];
if (maddr == 0) {
maddr = uncached_alloc_page(numa_node_id(), 1);
if (maddr == 0)
- return NOPFN_OOM;
+ return VM_FAULT_OOM;
spin_lock(&vdata->lock);
if (vdata->maddr[index] == 0) {
@@ -231,13 +229,20 @@ mspec_nopfn(struct vm_area_struct *vma, unsigned long address)
pfn = paddr >> PAGE_SHIFT;
- return pfn;
+ /*
+ * vm_insert_pfn can fail with -EBUSY, but in that case it will
+ * be because another thread has installed the pte first, so it
+ * is no problem.
+ */
+ vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn);
+
+ return VM_FAULT_NOPAGE;
}
static struct vm_operations_struct mspec_vm_ops = {
.open = mspec_open,
.close = mspec_close,
- .nopfn = mspec_nopfn
+ .fault = mspec_fault,
};
/*
diff --git a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c
index 50243fcd87e..4f8d67fed29 100644
--- a/drivers/char/mwave/mwavedd.c
+++ b/drivers/char/mwave/mwavedd.c
@@ -86,8 +86,8 @@ module_param(mwave_uart_io, int, 0);
static int mwave_open(struct inode *inode, struct file *file);
static int mwave_close(struct inode *inode, struct file *file);
-static int mwave_ioctl(struct inode *inode, struct file *filp,
- unsigned int iocmd, unsigned long ioarg);
+static long mwave_ioctl(struct file *filp, unsigned int iocmd,
+ unsigned long ioarg);
MWAVE_DEVICE_DATA mwave_s_mdd;
@@ -119,16 +119,16 @@ static int mwave_close(struct inode *inode, struct file *file)
return retval;
}
-static int mwave_ioctl(struct inode *inode, struct file *file,
- unsigned int iocmd, unsigned long ioarg)
+static long mwave_ioctl(struct file *file, unsigned int iocmd,
+ unsigned long ioarg)
{
unsigned int retval = 0;
pMWAVE_DEVICE_DATA pDrvData = &mwave_s_mdd;
void __user *arg = (void __user *)ioarg;
- PRINTK_5(TRACE_MWAVE,
- "mwavedd::mwave_ioctl, entry inode %p file %p cmd %x arg %x\n",
- inode, file, iocmd, (int) ioarg);
+ PRINTK_4(TRACE_MWAVE,
+ "mwavedd::mwave_ioctl, entry file %p cmd %x arg %x\n",
+ file, iocmd, (int) ioarg);
switch (iocmd) {
@@ -136,7 +136,9 @@ static int mwave_ioctl(struct inode *inode, struct file *file,
PRINTK_1(TRACE_MWAVE,
"mwavedd::mwave_ioctl, IOCTL_MW_RESET"
" calling tp3780I_ResetDSP\n");
+ lock_kernel();
retval = tp3780I_ResetDSP(&pDrvData->rBDData);
+ unlock_kernel();
PRINTK_2(TRACE_MWAVE,
"mwavedd::mwave_ioctl, IOCTL_MW_RESET"
" retval %x from tp3780I_ResetDSP\n",
@@ -147,7 +149,9 @@ static int mwave_ioctl(struct inode *inode, struct file *file,
PRINTK_1(TRACE_MWAVE,
"mwavedd::mwave_ioctl, IOCTL_MW_RUN"
" calling tp3780I_StartDSP\n");
+ lock_kernel();
retval = tp3780I_StartDSP(&pDrvData->rBDData);
+ unlock_kernel();
PRINTK_2(TRACE_MWAVE,
"mwavedd::mwave_ioctl, IOCTL_MW_RUN"
" retval %x from tp3780I_StartDSP\n",
@@ -161,8 +165,10 @@ static int mwave_ioctl(struct inode *inode, struct file *file,
"mwavedd::mwave_ioctl,"
" IOCTL_MW_DSP_ABILITIES calling"
" tp3780I_QueryAbilities\n");
+ lock_kernel();
retval = tp3780I_QueryAbilities(&pDrvData->rBDData,
&rAbilities);
+ unlock_kernel();
PRINTK_2(TRACE_MWAVE,
"mwavedd::mwave_ioctl, IOCTL_MW_DSP_ABILITIES"
" retval %x from tp3780I_QueryAbilities\n",
@@ -193,11 +199,13 @@ static int mwave_ioctl(struct inode *inode, struct file *file,
"mwavedd::mwave_ioctl IOCTL_MW_READ_DATA,"
" size %lx, ioarg %lx pusBuffer %p\n",
rReadData.ulDataLength, ioarg, pusBuffer);
+ lock_kernel();
retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData,
iocmd,
pusBuffer,
rReadData.ulDataLength,
rReadData.usDspAddress);
+ unlock_kernel();
}
break;
@@ -215,10 +223,12 @@ static int mwave_ioctl(struct inode *inode, struct file *file,
" size %lx, ioarg %lx pusBuffer %p\n",
rReadData.ulDataLength / 2, ioarg,
pusBuffer);
+ lock_kernel();
retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData,
iocmd, pusBuffer,
rReadData.ulDataLength / 2,
rReadData.usDspAddress);
+ unlock_kernel();
}
break;
@@ -236,10 +246,12 @@ static int mwave_ioctl(struct inode *inode, struct file *file,
" size %lx, ioarg %lx pusBuffer %p\n",
rWriteData.ulDataLength, ioarg,
pusBuffer);
+ lock_kernel();
retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData,
iocmd, pusBuffer,
rWriteData.ulDataLength,
rWriteData.usDspAddress);
+ unlock_kernel();
}
break;
@@ -257,10 +269,12 @@ static int mwave_ioctl(struct inode *inode, struct file *file,
" size %lx, ioarg %lx pusBuffer %p\n",
rWriteData.ulDataLength, ioarg,
pusBuffer);
+ lock_kernel();
retval = tp3780I_ReadWriteDspIStore(&pDrvData->rBDData,
iocmd, pusBuffer,
rWriteData.ulDataLength,
rWriteData.usDspAddress);
+ unlock_kernel();
}
break;
@@ -281,8 +295,10 @@ static int mwave_ioctl(struct inode *inode, struct file *file,
ipcnum);
return -EINVAL;
}
+ lock_kernel();
pDrvData->IPCs[ipcnum].bIsHere = FALSE;
pDrvData->IPCs[ipcnum].bIsEnabled = TRUE;
+ unlock_kernel();
PRINTK_2(TRACE_MWAVE,
"mwavedd::mwave_ioctl IOCTL_MW_REGISTER_IPC"
@@ -307,6 +323,7 @@ static int mwave_ioctl(struct inode *inode, struct file *file,
return -EINVAL;
}
+ lock_kernel();
if (pDrvData->IPCs[ipcnum].bIsEnabled == TRUE) {
DECLARE_WAITQUEUE(wait, current);
@@ -347,6 +364,7 @@ static int mwave_ioctl(struct inode *inode, struct file *file,
" processing\n",
ipcnum);
}
+ unlock_kernel();
}
break;
@@ -365,19 +383,18 @@ static int mwave_ioctl(struct inode *inode, struct file *file,
ipcnum);
return -EINVAL;
}
+ lock_kernel();
if (pDrvData->IPCs[ipcnum].bIsEnabled == TRUE) {
pDrvData->IPCs[ipcnum].bIsEnabled = FALSE;
if (pDrvData->IPCs[ipcnum].bIsHere == TRUE) {
wake_up_interruptible(&pDrvData->IPCs[ipcnum].ipc_wait_queue);
}
}
+ unlock_kernel();
}
break;
default:
- PRINTK_ERROR(KERN_ERR_MWAVE "mwavedd::mwave_ioctl:"
- " Error: Unrecognized iocmd %x\n",
- iocmd);
return -ENOTTY;
break;
} /* switch */
@@ -460,7 +477,7 @@ static const struct file_operations mwave_fops = {
.owner = THIS_MODULE,
.read = mwave_read,
.write = mwave_write,
- .ioctl = mwave_ioctl,
+ .unlocked_ioctl = mwave_ioctl,
.open = mwave_open,
.release = mwave_close
};
diff --git a/drivers/char/mwave/mwavedd.h b/drivers/char/mwave/mwavedd.h
index 8eca61e0a19..7e0d530e2e0 100644
--- a/drivers/char/mwave/mwavedd.h
+++ b/drivers/char/mwave/mwavedd.h
@@ -147,4 +147,6 @@ typedef struct _MWAVE_DEVICE_DATA {
} MWAVE_DEVICE_DATA, *pMWAVE_DEVICE_DATA;
+extern MWAVE_DEVICE_DATA mwave_s_mdd;
+
#endif
diff --git a/drivers/char/mwave/tp3780i.c b/drivers/char/mwave/tp3780i.c
index f282976daaa..c6896970806 100644
--- a/drivers/char/mwave/tp3780i.c
+++ b/drivers/char/mwave/tp3780i.c
@@ -57,8 +57,6 @@
#include "3780i.h"
#include "mwavepub.h"
-extern MWAVE_DEVICE_DATA mwave_s_mdd;
-
static unsigned short s_ausThinkpadIrqToField[16] =
{ 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, 0x0002, 0x0003, 0xFFFF, 0x0004,
0xFFFF, 0xFFFF, 0x0005, 0x0006, 0xFFFF, 0xFFFF, 0xFFFF, 0x0007 };
diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c
index 4c756bbba94..e30575e8764 100644
--- a/drivers/char/mxser.c
+++ b/drivers/char/mxser.c
@@ -16,7 +16,6 @@
* Fed through a cleanup, indent and remove of non 2.6 code by Alan Cox
* <alan@redhat.com>. The original 1.8 code is available on www.moxa.com.
* - Fixed x86_64 cleanness
- * - Fixed sleep with spinlock held in mxser_send_break
*/
#include <linux/module.h>
@@ -49,18 +48,12 @@
#define MXSER_VERSION "2.0.4" /* 1.12 */
#define MXSERMAJOR 174
-#define MXSERCUMAJOR 175
#define MXSER_BOARDS 4 /* Max. boards */
#define MXSER_PORTS_PER_BOARD 8 /* Max. ports per board */
#define MXSER_PORTS (MXSER_BOARDS * MXSER_PORTS_PER_BOARD)
#define MXSER_ISR_PASS_LIMIT 100
-#define MXSER_ERR_IOADDR -1
-#define MXSER_ERR_IRQ -2
-#define MXSER_ERR_IRQ_CONFLIT -3
-#define MXSER_ERR_VECTOR -4
-
/*CheckIsMoxaMust return value*/
#define MOXA_OTHER_UART 0x00
#define MOXA_MUST_MU150_HWID 0x01
@@ -179,14 +172,15 @@ static struct pci_device_id mxser_pcibrds[] = {
};
MODULE_DEVICE_TABLE(pci, mxser_pcibrds);
-static int ioaddr[MXSER_BOARDS] = { 0, 0, 0, 0 };
+static unsigned long ioaddr[MXSER_BOARDS];
static int ttymajor = MXSERMAJOR;
/* Variables for insmod */
MODULE_AUTHOR("Casper Yang");
MODULE_DESCRIPTION("MOXA Smartio/Industio Family Multiport Board Device Driver");
-module_param_array(ioaddr, int, NULL, 0);
+module_param_array(ioaddr, ulong, NULL, 0);
+MODULE_PARM_DESC(ioaddr, "ISA io addresses to look for a moxa board");
module_param(ttymajor, int, 0);
MODULE_LICENSE("GPL");
@@ -196,7 +190,6 @@ struct mxser_log {
unsigned long txcnt[MXSER_PORTS];
};
-
struct mxser_mon {
unsigned long rxcnt;
unsigned long txcnt;
@@ -287,19 +280,9 @@ struct mxser_mstatus {
int dcd;
};
-static struct mxser_mstatus GMStatus[MXSER_PORTS];
-
-static int mxserBoardCAP[MXSER_BOARDS] = {
- 0, 0, 0, 0
- /* 0x180, 0x280, 0x200, 0x320 */
-};
-
static struct mxser_board mxser_boards[MXSER_BOARDS];
static struct tty_driver *mxvar_sdriver;
static struct mxser_log mxvar_log;
-static int mxvar_diagflag;
-static unsigned char mxser_msr[MXSER_PORTS + 1];
-static struct mxser_mon_ext mon_data_ext;
static int mxser_set_baud_method[MXSER_PORTS + 1];
static void mxser_enable_must_enchance_mode(unsigned long baseio)
@@ -543,6 +526,7 @@ static void process_txrx_fifo(struct mxser_port *info)
static unsigned char mxser_get_msr(int baseaddr, int mode, int port)
{
+ static unsigned char mxser_msr[MXSER_PORTS + 1];
unsigned char status = 0;
status = inb(baseaddr + UART_MSR);
@@ -1319,13 +1303,9 @@ static void mxser_flush_chars(struct tty_struct *tty)
struct mxser_port *info = tty->driver_data;
unsigned long flags;
- if (info->xmit_cnt <= 0 ||
- tty->stopped ||
- !info->port.xmit_buf ||
- (tty->hw_stopped &&
- (info->type != PORT_16550A) &&
- (!info->board->chip_flag)
- ))
+ if (info->xmit_cnt <= 0 || tty->stopped || !info->port.xmit_buf ||
+ (tty->hw_stopped && info->type != PORT_16550A &&
+ !info->board->chip_flag))
return;
spin_lock_irqsave(&info->slock, flags);
@@ -1343,9 +1323,7 @@ static int mxser_write_room(struct tty_struct *tty)
int ret;
ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
- if (ret < 0)
- ret = 0;
- return ret;
+ return ret < 0 ? 0 : ret;
}
static int mxser_chars_in_buffer(struct tty_struct *tty)
@@ -1634,6 +1612,8 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
switch (cmd) {
case MOXA_GET_MAJOR:
+ printk(KERN_WARNING "mxser: '%s' uses deprecated ioctl %x, fix "
+ "your userspace\n", current->comm, cmd);
return put_user(ttymajor, (int __user *)argp);
case MOXA_CHKPORTENABLE:
@@ -1651,62 +1631,60 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
ret = -EFAULT;
unlock_kernel();
return ret;
- case MOXA_GETMSTATUS:
+ case MOXA_GETMSTATUS: {
+ struct mxser_mstatus ms, __user *msu = argp;
lock_kernel();
for (i = 0; i < MXSER_BOARDS; i++)
for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) {
port = &mxser_boards[i].ports[j];
+ memset(&ms, 0, sizeof(ms));
- GMStatus[i].ri = 0;
- if (!port->ioaddr) {
- GMStatus[i].dcd = 0;
- GMStatus[i].dsr = 0;
- GMStatus[i].cts = 0;
- continue;
- }
+ if (!port->ioaddr)
+ goto copy;
if (!port->port.tty || !port->port.tty->termios)
- GMStatus[i].cflag =
- port->normal_termios.c_cflag;
+ ms.cflag = port->normal_termios.c_cflag;
else
- GMStatus[i].cflag =
- port->port.tty->termios->c_cflag;
+ ms.cflag = port->port.tty->termios->c_cflag;
status = inb(port->ioaddr + UART_MSR);
- if (status & 0x80 /*UART_MSR_DCD */ )
- GMStatus[i].dcd = 1;
- else
- GMStatus[i].dcd = 0;
-
- if (status & 0x20 /*UART_MSR_DSR */ )
- GMStatus[i].dsr = 1;
- else
- GMStatus[i].dsr = 0;
-
-
- if (status & 0x10 /*UART_MSR_CTS */ )
- GMStatus[i].cts = 1;
- else
- GMStatus[i].cts = 0;
+ if (status & UART_MSR_DCD)
+ ms.dcd = 1;
+ if (status & UART_MSR_DSR)
+ ms.dsr = 1;
+ if (status & UART_MSR_CTS)
+ ms.cts = 1;
+ copy:
+ if (copy_to_user(msu, &ms, sizeof(ms))) {
+ unlock_kernel();
+ return -EFAULT;
+ }
+ msu++;
}
unlock_kernel();
- if (copy_to_user(argp, GMStatus,
- sizeof(struct mxser_mstatus) * MXSER_PORTS))
- return -EFAULT;
return 0;
+ }
case MOXA_ASPP_MON_EXT: {
- int p, shiftbit;
- unsigned long opmode;
- unsigned cflag, iflag;
+ struct mxser_mon_ext *me; /* it's 2k, stack unfriendly */
+ unsigned int cflag, iflag, p;
+ u8 opmode;
+
+ me = kzalloc(sizeof(*me), GFP_KERNEL);
+ if (!me)
+ return -ENOMEM;
lock_kernel();
- for (i = 0; i < MXSER_BOARDS; i++) {
- for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) {
+ for (i = 0, p = 0; i < MXSER_BOARDS; i++) {
+ for (j = 0; j < MXSER_PORTS_PER_BOARD; j++, p++) {
+ if (p >= ARRAY_SIZE(me->rx_cnt)) {
+ i = MXSER_BOARDS;
+ break;
+ }
port = &mxser_boards[i].ports[j];
if (!port->ioaddr)
continue;
- status = mxser_get_msr(port->ioaddr, 0, i);
+ status = mxser_get_msr(port->ioaddr, 0, p);
if (status & UART_MSR_TERI)
port->icount.rng++;
@@ -1718,16 +1696,13 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
port->icount.cts++;
port->mon_data.modem_status = status;
- mon_data_ext.rx_cnt[i] = port->mon_data.rxcnt;
- mon_data_ext.tx_cnt[i] = port->mon_data.txcnt;
- mon_data_ext.up_rxcnt[i] =
- port->mon_data.up_rxcnt;
- mon_data_ext.up_txcnt[i] =
- port->mon_data.up_txcnt;
- mon_data_ext.modem_status[i] =
+ me->rx_cnt[p] = port->mon_data.rxcnt;
+ me->tx_cnt[p] = port->mon_data.txcnt;
+ me->up_rxcnt[p] = port->mon_data.up_rxcnt;
+ me->up_txcnt[p] = port->mon_data.up_txcnt;
+ me->modem_status[p] =
port->mon_data.modem_status;
- mon_data_ext.baudrate[i] =
- tty_get_baud_rate(port->port.tty);
+ me->baudrate[p] = tty_get_baud_rate(port->port.tty);
if (!port->port.tty || !port->port.tty->termios) {
cflag = port->normal_termios.c_cflag;
@@ -1737,40 +1712,31 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
iflag = port->port.tty->termios->c_iflag;
}
- mon_data_ext.databits[i] = cflag & CSIZE;
-
- mon_data_ext.stopbits[i] = cflag & CSTOPB;
-
- mon_data_ext.parity[i] =
- cflag & (PARENB | PARODD | CMSPAR);
-
- mon_data_ext.flowctrl[i] = 0x00;
+ me->databits[p] = cflag & CSIZE;
+ me->stopbits[p] = cflag & CSTOPB;
+ me->parity[p] = cflag & (PARENB | PARODD |
+ CMSPAR);
if (cflag & CRTSCTS)
- mon_data_ext.flowctrl[i] |= 0x03;
+ me->flowctrl[p] |= 0x03;
if (iflag & (IXON | IXOFF))
- mon_data_ext.flowctrl[i] |= 0x0C;
+ me->flowctrl[p] |= 0x0C;
if (port->type == PORT_16550A)
- mon_data_ext.fifo[i] = 1;
- else
- mon_data_ext.fifo[i] = 0;
+ me->fifo[p] = 1;
- p = i % 4;
- shiftbit = p * 2;
- opmode = inb(port->opmode_ioaddr) >> shiftbit;
+ opmode = inb(port->opmode_ioaddr) >>
+ ((p % 4) * 2);
opmode &= OP_MODE_MASK;
-
- mon_data_ext.iftype[i] = opmode;
-
+ me->iftype[p] = opmode;
}
}
unlock_kernel();
- if (copy_to_user(argp, &mon_data_ext,
- sizeof(mon_data_ext)))
- return -EFAULT;
- return 0;
+ if (copy_to_user(argp, me, sizeof(*me)))
+ ret = -EFAULT;
+ kfree(me);
+ return ret;
}
default:
return -ENOIOCTLCMD;
@@ -1804,7 +1770,6 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
{
struct mxser_port *info = tty->driver_data;
struct async_icount cnow;
- struct serial_icounter_struct __user *p_cuser;
unsigned long flags;
void __user *argp = (void __user *)arg;
int retval;
@@ -1884,30 +1849,26 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
* NB: both 1->0 and 0->1 transitions are counted except for
* RI where only 0->1 is counted.
*/
- case TIOCGICOUNT:
+ case TIOCGICOUNT: {
+ struct serial_icounter_struct icnt = { 0 };
spin_lock_irqsave(&info->slock, flags);
cnow = info->icount;
spin_unlock_irqrestore(&info->slock, flags);
- p_cuser = argp;
- if (put_user(cnow.frame, &p_cuser->frame))
- return -EFAULT;
- if (put_user(cnow.brk, &p_cuser->brk))
- return -EFAULT;
- if (put_user(cnow.overrun, &p_cuser->overrun))
- return -EFAULT;
- if (put_user(cnow.buf_overrun, &p_cuser->buf_overrun))
- return -EFAULT;
- if (put_user(cnow.parity, &p_cuser->parity))
- return -EFAULT;
- if (put_user(cnow.rx, &p_cuser->rx))
- return -EFAULT;
- if (put_user(cnow.tx, &p_cuser->tx))
- return -EFAULT;
- put_user(cnow.cts, &p_cuser->cts);
- put_user(cnow.dsr, &p_cuser->dsr);
- put_user(cnow.rng, &p_cuser->rng);
- put_user(cnow.dcd, &p_cuser->dcd);
- return 0;
+
+ icnt.frame = cnow.frame;
+ icnt.brk = cnow.brk;
+ icnt.overrun = cnow.overrun;
+ icnt.buf_overrun = cnow.buf_overrun;
+ icnt.parity = cnow.parity;
+ icnt.rx = cnow.rx;
+ icnt.tx = cnow.tx;
+ icnt.cts = cnow.cts;
+ icnt.dsr = cnow.dsr;
+ icnt.rng = cnow.rng;
+ icnt.dcd = cnow.dcd;
+
+ return copy_to_user(argp, &icnt, sizeof(icnt)) ? -EFAULT : 0;
+ }
case MOXA_HighSpeedOn:
return put_user(info->baud_base != 115200 ? 1 : 0, (int __user *)argp);
case MOXA_SDS_RSTICOUNTER:
@@ -2503,7 +2464,8 @@ static int __devinit mxser_initbrd(struct mxser_board *brd,
unsigned int i;
int retval;
- printk(KERN_INFO "max. baud rate = %d bps.\n", brd->ports[0].max_baud);
+ printk(KERN_INFO "mxser: max. baud rate = %d bps\n",
+ brd->ports[0].max_baud);
for (i = 0; i < brd->info->nports; i++) {
info = &brd->ports[i];
@@ -2586,28 +2548,32 @@ static int __init mxser_get_ISA_conf(int cap, struct mxser_board *brd)
irq = regs[9] & 0xF000;
irq = irq | (irq >> 4);
if (irq != (regs[9] & 0xFF00))
- return MXSER_ERR_IRQ_CONFLIT;
+ goto err_irqconflict;
} else if (brd->info->nports == 4) {
irq = regs[9] & 0xF000;
irq = irq | (irq >> 4);
irq = irq | (irq >> 8);
if (irq != regs[9])
- return MXSER_ERR_IRQ_CONFLIT;
+ goto err_irqconflict;
} else if (brd->info->nports == 8) {
irq = regs[9] & 0xF000;
irq = irq | (irq >> 4);
irq = irq | (irq >> 8);
if ((irq != regs[9]) || (irq != regs[10]))
- return MXSER_ERR_IRQ_CONFLIT;
+ goto err_irqconflict;
}
- if (!irq)
- return MXSER_ERR_IRQ;
+ if (!irq) {
+ printk(KERN_ERR "mxser: interrupt number unset\n");
+ return -EIO;
+ }
brd->irq = ((int)(irq & 0xF000) >> 12);
for (i = 0; i < 8; i++)
brd->ports[i].ioaddr = (int) regs[i + 1] & 0xFFF8;
- if ((regs[12] & 0x80) == 0)
- return MXSER_ERR_VECTOR;
+ if ((regs[12] & 0x80) == 0) {
+ printk(KERN_ERR "mxser: invalid interrupt vector\n");
+ return -EIO;
+ }
brd->vector = (int)regs[11]; /* interrupt vector */
if (id == 1)
brd->vector_mask = 0x00FF;
@@ -2634,13 +2600,26 @@ static int __init mxser_get_ISA_conf(int cap, struct mxser_board *brd)
else
brd->uart_type = PORT_16450;
if (!request_region(brd->ports[0].ioaddr, 8 * brd->info->nports,
- "mxser(IO)"))
- return MXSER_ERR_IOADDR;
+ "mxser(IO)")) {
+ printk(KERN_ERR "mxser: can't request ports I/O region: "
+ "0x%.8lx-0x%.8lx\n",
+ brd->ports[0].ioaddr, brd->ports[0].ioaddr +
+ 8 * brd->info->nports - 1);
+ return -EIO;
+ }
if (!request_region(brd->vector, 1, "mxser(vector)")) {
release_region(brd->ports[0].ioaddr, 8 * brd->info->nports);
- return MXSER_ERR_VECTOR;
+ printk(KERN_ERR "mxser: can't request interrupt vector region: "
+ "0x%.8lx-0x%.8lx\n",
+ brd->ports[0].ioaddr, brd->ports[0].ioaddr +
+ 8 * brd->info->nports - 1);
+ return -EIO;
}
return brd->info->nports;
+
+err_irqconflict:
+ printk(KERN_ERR "mxser: invalid interrupt number\n");
+ return -EIO;
}
static int __devinit mxser_probe(struct pci_dev *pdev,
@@ -2657,20 +2636,20 @@ static int __devinit mxser_probe(struct pci_dev *pdev,
break;
if (i >= MXSER_BOARDS) {
- printk(KERN_ERR "Too many Smartio/Industio family boards found "
- "(maximum %d), board not configured\n", MXSER_BOARDS);
+ dev_err(&pdev->dev, "too many boards found (maximum %d), board "
+ "not configured\n", MXSER_BOARDS);
goto err;
}
brd = &mxser_boards[i];
brd->idx = i * MXSER_PORTS_PER_BOARD;
- printk(KERN_INFO "Found MOXA %s board (BusNo=%d, DevNo=%d)\n",
+ dev_info(&pdev->dev, "found MOXA %s board (BusNo=%d, DevNo=%d)\n",
mxser_cards[ent->driver_data].name,
pdev->bus->number, PCI_SLOT(pdev->devfn));
retval = pci_enable_device(pdev);
if (retval) {
- printk(KERN_ERR "Moxa SmartI/O PCI enable fail !\n");
+ dev_err(&pdev->dev, "PCI enable failed\n");
goto err;
}
@@ -2772,11 +2751,8 @@ static struct pci_driver mxser_driver = {
static int __init mxser_module_init(void)
{
struct mxser_board *brd;
- unsigned long cap;
- unsigned int i, m, isaloop;
- int retval, b;
-
- pr_debug("Loading module mxser ...\n");
+ unsigned int b, i, m;
+ int retval;
mxvar_sdriver = alloc_tty_driver(MXSER_PORTS + 1);
if (!mxvar_sdriver)
@@ -2806,74 +2782,43 @@ static int __init mxser_module_init(void)
goto err_put;
}
- mxvar_diagflag = 0;
-
- m = 0;
/* Start finding ISA boards here */
- for (isaloop = 0; isaloop < 2; isaloop++)
- for (b = 0; b < MXSER_BOARDS && m < MXSER_BOARDS; b++) {
- if (!isaloop)
- cap = mxserBoardCAP[b]; /* predefined */
- else
- cap = ioaddr[b]; /* module param */
-
- if (!cap)
- continue;
+ for (m = 0, b = 0; b < MXSER_BOARDS; b++) {
+ if (!ioaddr[b])
+ continue;
+
+ brd = &mxser_boards[m];
+ retval = mxser_get_ISA_conf(!ioaddr[b], brd);
+ if (retval <= 0) {
+ brd->info = NULL;
+ continue;
+ }
- brd = &mxser_boards[m];
- retval = mxser_get_ISA_conf(cap, brd);
-
- if (retval != 0)
- printk(KERN_INFO "Found MOXA %s board "
- "(CAP=0x%x)\n",
- brd->info->name, ioaddr[b]);
-
- if (retval <= 0) {
- if (retval == MXSER_ERR_IRQ)
- printk(KERN_ERR "Invalid interrupt "
- "number, board not "
- "configured\n");
- else if (retval == MXSER_ERR_IRQ_CONFLIT)
- printk(KERN_ERR "Invalid interrupt "
- "number, board not "
- "configured\n");
- else if (retval == MXSER_ERR_VECTOR)
- printk(KERN_ERR "Invalid interrupt "
- "vector, board not "
- "configured\n");
- else if (retval == MXSER_ERR_IOADDR)
- printk(KERN_ERR "Invalid I/O address, "
- "board not configured\n");
-
- brd->info = NULL;
- continue;
- }
+ printk(KERN_INFO "mxser: found MOXA %s board (CAP=0x%lx)\n",
+ brd->info->name, ioaddr[b]);
- /* mxser_initbrd will hook ISR. */
- if (mxser_initbrd(brd, NULL) < 0) {
- brd->info = NULL;
- continue;
- }
+ /* mxser_initbrd will hook ISR. */
+ if (mxser_initbrd(brd, NULL) < 0) {
+ brd->info = NULL;
+ continue;
+ }
- brd->idx = m * MXSER_PORTS_PER_BOARD;
- for (i = 0; i < brd->info->nports; i++)
- tty_register_device(mxvar_sdriver, brd->idx + i,
- NULL);
+ brd->idx = m * MXSER_PORTS_PER_BOARD;
+ for (i = 0; i < brd->info->nports; i++)
+ tty_register_device(mxvar_sdriver, brd->idx + i, NULL);
- m++;
- }
+ m++;
+ }
retval = pci_register_driver(&mxser_driver);
if (retval) {
- printk(KERN_ERR "Can't register pci driver\n");
+ printk(KERN_ERR "mxser: can't register pci driver\n");
if (!m) {
retval = -ENODEV;
goto err_unr;
} /* else: we have some ISA cards under control */
}
- pr_debug("Done.\n");
-
return 0;
err_unr:
tty_unregister_driver(mxvar_sdriver);
@@ -2886,8 +2831,6 @@ static void __exit mxser_module_exit(void)
{
unsigned int i, j;
- pr_debug("Unloading module mxser ...\n");
-
pci_unregister_driver(&mxser_driver);
for (i = 0; i < MXSER_BOARDS; i++) /* ISA remains */
@@ -2901,8 +2844,6 @@ static void __exit mxser_module_exit(void)
for (i = 0; i < MXSER_BOARDS; i++)
if (mxser_boards[i].info != NULL)
mxser_release_res(&mxser_boards[i], NULL, 1);
-
- pr_debug("Done.\n");
}
module_init(mxser_module_init);
diff --git a/drivers/char/nwflash.c b/drivers/char/nwflash.c
index ba012c2bdf7..f9f72a21129 100644
--- a/drivers/char/nwflash.c
+++ b/drivers/char/nwflash.c
@@ -122,35 +122,20 @@ static int flash_ioctl(struct inode *inodep, struct file *filep, unsigned int cm
static ssize_t flash_read(struct file *file, char __user *buf, size_t size,
loff_t *ppos)
{
- unsigned long p = *ppos;
- unsigned int count = size;
- int ret = 0;
+ ssize_t ret;
if (flashdebug)
printk(KERN_DEBUG "flash_read: flash_read: offset=0x%lX, "
"buffer=%p, count=0x%X.\n", p, buf, count);
+ /*
+ * We now lock against reads and writes. --rmk
+ */
+ if (mutex_lock_interruptible(&nwflash_mutex))
+ return -ERESTARTSYS;
- if (count)
- ret = -ENXIO;
-
- if (p < gbFlashSize) {
- if (count > gbFlashSize - p)
- count = gbFlashSize - p;
+ ret = simple_read_from_buffer(buf, size, ppos, FLASH_BASE, gbFlashSize);
+ mutex_unlock(&nwflash_mutex);
- /*
- * We now lock against reads and writes. --rmk
- */
- if (mutex_lock_interruptible(&nwflash_mutex))
- return -ERESTARTSYS;
-
- ret = copy_to_user(buf, (void *)(FLASH_BASE + p), count);
- if (ret == 0) {
- ret = count;
- *ppos += count;
- } else
- ret = -EFAULT;
- mutex_unlock(&nwflash_mutex);
- }
return ret;
}
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c
index 7af7a7e6b9c..bee39fdfba7 100644
--- a/drivers/char/ppdev.c
+++ b/drivers/char/ppdev.c
@@ -67,7 +67,7 @@
#include <linux/major.h>
#include <linux/ppdev.h>
#include <linux/smp_lock.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#define PP_VERSION "ppdev: user-space parallel port driver"
#define CHRDEV "ppdev"
@@ -328,10 +328,9 @@ static enum ieee1284_phase init_phase (int mode)
return IEEE1284_PH_FWD_IDLE;
}
-static int pp_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- unsigned int minor = iminor(inode);
+ unsigned int minor = iminor(file->f_path.dentry->d_inode);
struct pp_struct *pp = file->private_data;
struct parport * port;
void __user *argp = (void __user *)arg;
@@ -634,6 +633,15 @@ static int pp_ioctl(struct inode *inode, struct file *file,
return 0;
}
+static long pp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ long ret;
+ lock_kernel();
+ ret = pp_do_ioctl(file, cmd, arg);
+ unlock_kernel();
+ return ret;
+}
+
static int pp_open (struct inode * inode, struct file * file)
{
unsigned int minor = iminor(inode);
@@ -745,7 +753,7 @@ static const struct file_operations pp_fops = {
.read = pp_read,
.write = pp_write,
.poll = pp_poll,
- .ioctl = pp_ioctl,
+ .unlocked_ioctl = pp_ioctl,
.open = pp_open,
.release = pp_release,
};
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 0cf98bd4f2d..e0d0e371909 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -236,6 +236,7 @@
#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/interrupt.h>
+#include <linux/mm.h>
#include <linux/spinlock.h>
#include <linux/percpu.h>
#include <linux/cryptohash.h>
diff --git a/drivers/char/rio/rio_linux.c b/drivers/char/rio/rio_linux.c
index 0cdfee15291..a8f68a3f14d 100644
--- a/drivers/char/rio/rio_linux.c
+++ b/drivers/char/rio/rio_linux.c
@@ -179,7 +179,7 @@ static int rio_set_real_termios(void *ptr);
static void rio_hungup(void *ptr);
static void rio_close(void *ptr);
static int rio_chars_in_buffer(void *ptr);
-static int rio_fw_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
+static long rio_fw_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
static int rio_init_drivers(void);
static void my_hd(void *addr, int len);
@@ -240,7 +240,7 @@ static struct real_driver rio_real_driver = {
static const struct file_operations rio_fw_fops = {
.owner = THIS_MODULE,
- .ioctl = rio_fw_ioctl,
+ .unlocked_ioctl = rio_fw_ioctl,
};
static struct miscdevice rio_fw_device = {
@@ -560,13 +560,15 @@ static void rio_close(void *ptr)
-static int rio_fw_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+static long rio_fw_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int rc = 0;
func_enter();
/* The "dev" argument isn't used. */
+ lock_kernel();
rc = riocontrol(p, 0, cmd, arg, capable(CAP_SYS_ADMIN));
+ unlock_kernel();
func_exit();
return rc;
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index fa92a8af5a5..dbefbb30ed4 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -78,9 +78,10 @@
#include <linux/wait.h>
#include <linux/bcd.h>
#include <linux/delay.h>
+#include <linux/smp_lock.h>
+#include <linux/uaccess.h>
#include <asm/current.h>
-#include <asm/uaccess.h>
#include <asm/system.h>
#ifdef CONFIG_X86
@@ -120,8 +121,6 @@ static irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id)
return 0;
}
#endif
-#else
-extern irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id);
#endif
/*
@@ -144,8 +143,7 @@ static DEFINE_TIMER(rtc_irq_timer, rtc_dropped_irq, 0, 0);
static ssize_t rtc_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos);
-static int rtc_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg);
+static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
#ifdef RTC_IRQ
static unsigned int rtc_poll(struct file *file, poll_table *wait);
@@ -719,10 +717,13 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
&wtime, sizeof wtime) ? -EFAULT : 0;
}
-static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
- unsigned long arg)
+static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- return rtc_do_ioctl(cmd, arg, 0);
+ long ret;
+ lock_kernel();
+ ret = rtc_do_ioctl(cmd, arg, 0);
+ unlock_kernel();
+ return ret;
}
/*
@@ -915,7 +916,7 @@ static const struct file_operations rtc_fops = {
#ifdef RTC_IRQ
.poll = rtc_poll,
#endif
- .ioctl = rtc_ioctl,
+ .unlocked_ioctl = rtc_ioctl,
.open = rtc_open,
.release = rtc_release,
.fasync = rtc_fasync,
diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c
index b976248e107..19db1eb87c2 100644
--- a/drivers/char/stallion.c
+++ b/drivers/char/stallion.c
@@ -1256,7 +1256,6 @@ static int stl_tiocmset(struct tty_struct *tty, struct file *file,
static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
{
struct stlport *portp;
- unsigned int ival;
int rc;
void __user *argp = (void __user *)arg;
diff --git a/drivers/char/sx.c b/drivers/char/sx.c
index 2162439bbe4..c385206f9db 100644
--- a/drivers/char/sx.c
+++ b/drivers/char/sx.c
@@ -286,8 +286,8 @@ static void sx_close(void *ptr);
static int sx_chars_in_buffer(void *ptr);
static int sx_init_board(struct sx_board *board);
static int sx_init_portstructs(int nboards, int nports);
-static int sx_fw_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg);
+static long sx_fw_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg);
static int sx_init_drivers(void);
static struct tty_driver *sx_driver;
@@ -396,7 +396,7 @@ static struct real_driver sx_real_driver = {
static const struct file_operations sx_fw_fops = {
.owner = THIS_MODULE,
- .ioctl = sx_fw_ioctl,
+ .unlocked_ioctl = sx_fw_ioctl,
};
static struct miscdevice sx_fw_device = {
@@ -1686,10 +1686,10 @@ static int do_memtest_w(struct sx_board *board, int min, int max)
}
#endif
-static int sx_fw_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg)
+static long sx_fw_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
{
- int rc = 0;
+ long rc = 0;
int __user *descr = (int __user *)arg;
int i;
static struct sx_board *board = NULL;
@@ -1699,13 +1699,10 @@ static int sx_fw_ioctl(struct inode *inode, struct file *filp,
func_enter();
-#if 0
- /* Removed superuser check: Sysops can use the permissions on the device
- file to restrict access. Recommendation: Root only. (root.root 600) */
- if (!capable(CAP_SYS_ADMIN)) {
+ if (!capable(CAP_SYS_RAWIO))
return -EPERM;
- }
-#endif
+
+ lock_kernel();
sx_dprintk(SX_DEBUG_FIRMWARE, "IOCTL %x: %lx\n", cmd, arg);
@@ -1720,19 +1717,23 @@ static int sx_fw_ioctl(struct inode *inode, struct file *filp,
for (i = 0; i < SX_NBOARDS; i++)
sx_dprintk(SX_DEBUG_FIRMWARE, "<%x> ", boards[i].flags);
sx_dprintk(SX_DEBUG_FIRMWARE, "\n");
+ unlock_kernel();
return -EIO;
}
switch (cmd) {
case SXIO_SET_BOARD:
sx_dprintk(SX_DEBUG_FIRMWARE, "set board to %ld\n", arg);
+ rc = -EIO;
if (arg >= SX_NBOARDS)
- return -EIO;
+ break;
sx_dprintk(SX_DEBUG_FIRMWARE, "not out of range\n");
if (!(boards[arg].flags & SX_BOARD_PRESENT))
- return -EIO;
+ break;
sx_dprintk(SX_DEBUG_FIRMWARE, ".. and present!\n");
board = &boards[arg];
+ rc = 0;
+ /* FIXME: And this does ... nothing?? */
break;
case SXIO_GET_TYPE:
rc = -ENOENT; /* If we manage to miss one, return error. */
@@ -1746,7 +1747,7 @@ static int sx_fw_ioctl(struct inode *inode, struct file *filp,
rc = SX_TYPE_SI;
if (IS_EISA_BOARD(board))
rc = SX_TYPE_SI;
- sx_dprintk(SX_DEBUG_FIRMWARE, "returning type= %d\n", rc);
+ sx_dprintk(SX_DEBUG_FIRMWARE, "returning type= %ld\n", rc);
break;
case SXIO_DO_RAMTEST:
if (sx_initialized) /* Already initialized: better not ramtest the board. */
@@ -1760,19 +1761,26 @@ static int sx_fw_ioctl(struct inode *inode, struct file *filp,
rc = do_memtest(board, 0, 0x7ff8);
/* if (!rc) rc = do_memtest_w (board, 0, 0x7ff8); */
}
- sx_dprintk(SX_DEBUG_FIRMWARE, "returning memtest result= %d\n",
- rc);
+ sx_dprintk(SX_DEBUG_FIRMWARE,
+ "returning memtest result= %ld\n", rc);
break;
case SXIO_DOWNLOAD:
- if (sx_initialized) /* Already initialized */
- return -EEXIST;
- if (!sx_reset(board))
- return -EIO;
+ if (sx_initialized) {/* Already initialized */
+ rc = -EEXIST;
+ break;
+ }
+ if (!sx_reset(board)) {
+ rc = -EIO;
+ break;
+ }
sx_dprintk(SX_DEBUG_INIT, "reset the board...\n");
tmp = kmalloc(SX_CHUNK_SIZE, GFP_USER);
- if (!tmp)
- return -ENOMEM;
+ if (!tmp) {
+ rc = -ENOMEM;
+ break;
+ }
+ /* FIXME: check returns */
get_user(nbytes, descr++);
get_user(offset, descr++);
get_user(data, descr++);
@@ -1782,7 +1790,8 @@ static int sx_fw_ioctl(struct inode *inode, struct file *filp,
(i + SX_CHUNK_SIZE > nbytes) ?
nbytes - i : SX_CHUNK_SIZE)) {
kfree(tmp);
- return -EFAULT;
+ rc = -EFAULT;
+ break;
}
memcpy_toio(board->base2 + offset + i, tmp,
(i + SX_CHUNK_SIZE > nbytes) ?
@@ -1798,13 +1807,17 @@ static int sx_fw_ioctl(struct inode *inode, struct file *filp,
rc = sx_nports;
break;
case SXIO_INIT:
- if (sx_initialized) /* Already initialized */
- return -EEXIST;
+ if (sx_initialized) { /* Already initialized */
+ rc = -EEXIST;
+ break;
+ }
/* This is not allowed until all boards are initialized... */
for (i = 0; i < SX_NBOARDS; i++) {
if ((boards[i].flags & SX_BOARD_PRESENT) &&
- !(boards[i].flags & SX_BOARD_INITIALIZED))
- return -EIO;
+ !(boards[i].flags & SX_BOARD_INITIALIZED)) {
+ rc = -EIO;
+ break;
+ }
}
for (i = 0; i < SX_NBOARDS; i++)
if (!(boards[i].flags & SX_BOARD_PRESENT))
@@ -1832,10 +1845,10 @@ static int sx_fw_ioctl(struct inode *inode, struct file *filp,
rc = sx_nports;
break;
default:
- printk(KERN_WARNING "Unknown ioctl on firmware device (%x).\n",
- cmd);
+ rc = -ENOTTY;
break;
}
+ unlock_kernel();
func_exit();
return rc;
}
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index fa48dba5ba5..e1b46bc7e43 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -1119,19 +1119,6 @@ int tty_hung_up_p(struct file *filp)
EXPORT_SYMBOL(tty_hung_up_p);
-/**
- * is_tty - checker whether file is a TTY
- * @filp: file handle that may be a tty
- *
- * Check if the file handle is a tty handle.
- */
-
-int is_tty(struct file *filp)
-{
- return filp->f_op->read == tty_read
- || filp->f_op->read == hung_up_tty_read;
-}
-
static void session_clear_tty(struct pid *session)
{
struct task_struct *p;
@@ -3593,7 +3580,6 @@ void proc_clear_tty(struct task_struct *p)
p->signal->tty = NULL;
spin_unlock_irq(&p->sighand->siglock);
}
-EXPORT_SYMBOL(proc_clear_tty);
/* Called under the sighand lock */
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index dc17fe3a88b..d0f4eb6fdb7 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -46,6 +46,9 @@ static char *in, *inbuf;
/* The operations for our console. */
static struct hv_ops virtio_cons;
+/* The hvc device */
+static struct hvc_struct *hvc;
+
/*D:310 The put_chars() callback is pretty straightforward.
*
* We turn the characters into a scatter-gather list, add it to the output
@@ -134,6 +137,27 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int))
return hvc_instantiate(0, 0, &virtio_cons);
}
+/*
+ * we support only one console, the hvc struct is a global var
+ * There is no need to do anything
+ */
+static int notifier_add_vio(struct hvc_struct *hp, int data)
+{
+ hp->irq_requested = 1;
+ return 0;
+}
+
+static void notifier_del_vio(struct hvc_struct *hp, int data)
+{
+ hp->irq_requested = 0;
+}
+
+static void hvc_handle_input(struct virtqueue *vq)
+{
+ if (hvc_poll(hvc))
+ hvc_kick();
+}
+
/*D:370 Once we're further in boot, we get probed like any other virtio device.
* At this stage we set up the output virtqueue.
*
@@ -144,7 +168,6 @@ int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int))
static int __devinit virtcons_probe(struct virtio_device *dev)
{
int err;
- struct hvc_struct *hvc;
vdev = dev;
@@ -158,7 +181,7 @@ static int __devinit virtcons_probe(struct virtio_device *dev)
/* Find the input queue. */
/* FIXME: This is why we want to wean off hvc: we do nothing
* when input comes in. */
- in_vq = vdev->config->find_vq(vdev, 0, NULL);
+ in_vq = vdev->config->find_vq(vdev, 0, hvc_handle_input);
if (IS_ERR(in_vq)) {
err = PTR_ERR(in_vq);
goto free;
@@ -173,15 +196,18 @@ static int __devinit virtcons_probe(struct virtio_device *dev)
/* Start using the new console output. */
virtio_cons.get_chars = get_chars;
virtio_cons.put_chars = put_chars;
+ virtio_cons.notifier_add = notifier_add_vio;
+ virtio_cons.notifier_del = notifier_del_vio;
/* The first argument of hvc_alloc() is the virtual console number, so
- * we use zero. The second argument is the interrupt number; we
- * currently leave this as zero: it would be better not to use the
- * hvc mechanism and fix this (FIXME!).
+ * we use zero. The second argument is the parameter for the
+ * notification mechanism (like irq number). We currently leave this
+ * as zero, virtqueues have implicit notifications.
*
* The third argument is a "struct hv_ops" containing the put_chars()
- * and get_chars() pointers. The final argument is the output buffer
- * size: we can do any size, so we put PAGE_SIZE here. */
+ * get_chars(), notifier_add() and notifier_del() pointers.
+ * The final argument is the output buffer size: we can do any size,
+ * so we put PAGE_SIZE here. */
hvc = hvc_alloc(0, 0, &virtio_cons, PAGE_SIZE);
if (IS_ERR(hvc)) {
err = PTR_ERR(hvc);
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index e32a076d5f1..82a51f38a54 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -261,7 +261,7 @@ static void notify_update(struct vc_data *vc)
#ifdef VT_BUF_VRAM_ONLY
#define DO_UPDATE(vc) 0
#else
-#define DO_UPDATE(vc) CON_IS_VISIBLE(vc)
+#define DO_UPDATE(vc) (CON_IS_VISIBLE(vc) && !console_blanked)
#endif
static inline unsigned short *screenpos(struct vc_data *vc, int offset, int viewed)
@@ -2211,7 +2211,7 @@ rescan_last_byte:
c = 0xfffd;
tc = c;
} else { /* no utf or alternate charset mode */
- tc = vc->vc_translate[vc->vc_toggle_meta ? (c | 0x80) : c];
+ tc = vc_translate(vc, c);
}
param.c = tc;
@@ -2749,8 +2749,8 @@ static int con_open(struct tty_struct *tty, struct file *filp)
tty->termios->c_iflag |= IUTF8;
else
tty->termios->c_iflag &= ~IUTF8;
- release_console_sem();
vcs_make_sysfs(tty);
+ release_console_sem();
return ret;
}
}
@@ -2775,8 +2775,8 @@ static void con_close(struct tty_struct *tty, struct file *filp)
if (vc)
vc->vc_tty = NULL;
tty->driver_data = NULL;
- release_console_sem();
vcs_remove_sysfs(tty);
+ release_console_sem();
mutex_unlock(&tty_mutex);
/*
* tty_mutex is released, but we still hold BKL, so there is
diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
index 51966ccf4ea..8bfee5fb722 100644
--- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c
+++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
@@ -87,7 +87,6 @@
#include <linux/mutex.h>
#include <linux/smp_lock.h>
#include <linux/sysctl.h>
-#include <linux/version.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 6e6c3c4aea6..5a11e3cbcae 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -123,6 +123,13 @@ config EDAC_I5000
Support for error detection and correction the Intel
Greekcreek/Blackford chipsets.
+config EDAC_I5100
+ tristate "Intel San Clemente MCH"
+ depends on EDAC_MM_EDAC && X86 && PCI
+ help
+ Support for error detection and correction the Intel
+ San Clemente MCH.
+
config EDAC_MPC85XX
tristate "Freescale MPC85xx"
depends on EDAC_MM_EDAC && FSL_SOC && MPC85xx
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index 83807731d4a..e5e9104b552 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -19,6 +19,7 @@ endif
obj-$(CONFIG_EDAC_AMD76X) += amd76x_edac.o
obj-$(CONFIG_EDAC_I5000) += i5000_edac.o
+obj-$(CONFIG_EDAC_I5100) += i5100_edac.o
obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o
obj-$(CONFIG_EDAC_E752X) += e752x_edac.o
obj-$(CONFIG_EDAC_I82443BXGX) += i82443bxgx_edac.o
diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c
index c94a0eb492c..facfdb1fa71 100644
--- a/drivers/edac/e752x_edac.c
+++ b/drivers/edac/e752x_edac.c
@@ -28,6 +28,7 @@
#define E752X_REVISION " Ver: 2.0.2 " __DATE__
#define EDAC_MOD_STR "e752x_edac"
+static int report_non_memory_errors;
static int force_function_unhide;
static int sysbus_parity = -1;
@@ -117,7 +118,7 @@ static struct edac_pci_ctl_info *e752x_pci;
#define E752X_BUF_FERR 0x70 /* Memory buffer first error reg (8b) */
#define E752X_BUF_NERR 0x72 /* Memory buffer next error reg (8b) */
#define E752X_BUF_ERRMASK 0x74 /* Memory buffer error mask reg (8b) */
-#define E752X_BUF_SMICMD 0x7A /* Memory buffer SMI command reg (8b) */
+#define E752X_BUF_SMICMD 0x7A /* Memory buffer SMI cmd reg (8b) */
#define E752X_DRAM_FERR 0x80 /* DRAM first error register (16b) */
#define E752X_DRAM_NERR 0x82 /* DRAM next error register (16b) */
#define E752X_DRAM_ERRMASK 0x84 /* DRAM error mask register (8b) */
@@ -127,7 +128,7 @@ static struct edac_pci_ctl_info *e752x_pci;
/* error address register (32b) */
/*
* 31 Reserved
- * 30:2 CE address (64 byte block 34:6)
+ * 30:2 CE address (64 byte block 34:6
* 1 Reserved
* 0 HiLoCS
*/
@@ -147,11 +148,11 @@ static struct edac_pci_ctl_info *e752x_pci;
* 1 Reserved
* 0 HiLoCS
*/
-#define E752X_DRAM_SCRB_ADD 0xA8 /* DRAM first uncorrectable scrub memory */
+#define E752X_DRAM_SCRB_ADD 0xA8 /* DRAM 1st uncorrectable scrub mem */
/* error address register (32b) */
/*
* 31 Reserved
- * 30:2 CE address (64 byte block 34:6)
+ * 30:2 CE address (64 byte block 34:6
* 1 Reserved
* 0 HiLoCS
*/
@@ -394,9 +395,12 @@ static void do_process_ded_retry(struct mem_ctl_info *mci, u16 error,
struct e752x_pvt *pvt = (struct e752x_pvt *)mci->pvt_info;
error_1b = retry_add;
- page = error_1b >> (PAGE_SHIFT - 4); /* convert the addr to 4k page */
- row = pvt->mc_symmetric ? ((page >> 1) & 3) : /* chip select are bits 14 & 13 */
+ page = error_1b >> (PAGE_SHIFT - 4); /* convert the addr to 4k page */
+
+ /* chip select are bits 14 & 13 */
+ row = pvt->mc_symmetric ? ((page >> 1) & 3) :
edac_mc_find_csrow_by_page(mci, page);
+
e752x_mc_printk(mci, KERN_WARNING,
"CE page 0x%lx, row %d : Memory read retry\n",
(long unsigned int)page, row);
@@ -422,12 +426,21 @@ static inline void process_threshold_ce(struct mem_ctl_info *mci, u16 error,
}
static char *global_message[11] = {
- "PCI Express C1", "PCI Express C", "PCI Express B1",
- "PCI Express B", "PCI Express A1", "PCI Express A",
- "DMA Controler", "HUB or NS Interface", "System Bus",
- "DRAM Controler", "Internal Buffer"
+ "PCI Express C1",
+ "PCI Express C",
+ "PCI Express B1",
+ "PCI Express B",
+ "PCI Express A1",
+ "PCI Express A",
+ "DMA Controller",
+ "HUB or NS Interface",
+ "System Bus",
+ "DRAM Controller", /* 9th entry */
+ "Internal Buffer"
};
+#define DRAM_ENTRY 9
+
static char *fatal_message[2] = { "Non-Fatal ", "Fatal " };
static void do_global_error(int fatal, u32 errors)
@@ -435,9 +448,16 @@ static void do_global_error(int fatal, u32 errors)
int i;
for (i = 0; i < 11; i++) {
- if (errors & (1 << i))
- e752x_printk(KERN_WARNING, "%sError %s\n",
- fatal_message[fatal], global_message[i]);
+ if (errors & (1 << i)) {
+ /* If the error is from DRAM Controller OR
+ * we are to report ALL errors, then
+ * report the error
+ */
+ if ((i == DRAM_ENTRY) || report_non_memory_errors)
+ e752x_printk(KERN_WARNING, "%sError %s\n",
+ fatal_message[fatal],
+ global_message[i]);
+ }
}
}
@@ -1021,7 +1041,7 @@ static int e752x_get_devs(struct pci_dev *pdev, int dev_idx,
struct pci_dev *dev;
pvt->bridge_ck = pci_get_device(PCI_VENDOR_ID_INTEL,
- pvt->dev_info->err_dev, pvt->bridge_ck);
+ pvt->dev_info->err_dev, pvt->bridge_ck);
if (pvt->bridge_ck == NULL)
pvt->bridge_ck = pci_scan_single_device(pdev->bus,
@@ -1034,8 +1054,9 @@ static int e752x_get_devs(struct pci_dev *pdev, int dev_idx,
return 1;
}
- dev = pci_get_device(PCI_VENDOR_ID_INTEL, e752x_devs[dev_idx].ctl_dev,
- NULL);
+ dev = pci_get_device(PCI_VENDOR_ID_INTEL,
+ e752x_devs[dev_idx].ctl_dev,
+ NULL);
if (dev == NULL)
goto fail;
@@ -1316,7 +1337,8 @@ MODULE_DESCRIPTION("MC support for Intel e752x/3100 memory controllers");
module_param(force_function_unhide, int, 0444);
MODULE_PARM_DESC(force_function_unhide, "if BIOS sets Dev0:Fun1 up as hidden:"
- " 1=force unhide and hope BIOS doesn't fight driver for Dev0:Fun1 access");
+ " 1=force unhide and hope BIOS doesn't fight driver for "
+ "Dev0:Fun1 access");
module_param(edac_op_state, int, 0444);
MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
@@ -1324,3 +1346,6 @@ MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
module_param(sysbus_parity, int, 0444);
MODULE_PARM_DESC(sysbus_parity, "0=disable system bus parity checking,"
" 1=enable system bus parity checking, default=auto-detect");
+module_param(report_non_memory_errors, int, 0644);
+MODULE_PARM_DESC(report_non_memory_errors, "0=disable non-memory error "
+ "reporting, 1=enable non-memory error reporting");
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index 021d1879514..ad218fe4942 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -44,6 +44,25 @@ int edac_mc_get_poll_msec(void)
return edac_mc_poll_msec;
}
+static int edac_set_poll_msec(const char *val, struct kernel_param *kp)
+{
+ long l;
+ int ret;
+
+ if (!val)
+ return -EINVAL;
+
+ ret = strict_strtol(val, 0, &l);
+ if (ret == -EINVAL || ((int)l != l))
+ return -EINVAL;
+ *((int *)kp->arg) = l;
+
+ /* notify edac_mc engine to reset the poll period */
+ edac_mc_reset_delay_period(l);
+
+ return 0;
+}
+
/* Parameter declarations for above */
module_param(edac_mc_panic_on_ue, int, 0644);
MODULE_PARM_DESC(edac_mc_panic_on_ue, "Panic on uncorrected error: 0=off 1=on");
@@ -53,7 +72,8 @@ MODULE_PARM_DESC(edac_mc_log_ue,
module_param(edac_mc_log_ce, int, 0644);
MODULE_PARM_DESC(edac_mc_log_ce,
"Log correctable error to console: 0=off 1=on");
-module_param(edac_mc_poll_msec, int, 0644);
+module_param_call(edac_mc_poll_msec, edac_set_poll_msec, param_get_int,
+ &edac_mc_poll_msec, 0644);
MODULE_PARM_DESC(edac_mc_poll_msec, "Polling period in milliseconds");
/*
@@ -103,16 +123,6 @@ static const char *edac_caps[] = {
-/*
- * /sys/devices/system/edac/mc;
- * data structures and methods
- */
-static ssize_t memctrl_int_show(void *ptr, char *buffer)
-{
- int *value = (int *)ptr;
- return sprintf(buffer, "%u\n", *value);
-}
-
static ssize_t memctrl_int_store(void *ptr, const char *buffer, size_t count)
{
int *value = (int *)ptr;
@@ -123,23 +133,6 @@ static ssize_t memctrl_int_store(void *ptr, const char *buffer, size_t count)
return count;
}
-/*
- * mc poll_msec time value
- */
-static ssize_t poll_msec_int_store(void *ptr, const char *buffer, size_t count)
-{
- int *value = (int *)ptr;
-
- if (isdigit(*buffer)) {
- *value = simple_strtoul(buffer, NULL, 0);
-
- /* notify edac_mc engine to reset the poll period */
- edac_mc_reset_delay_period(*value);
- }
-
- return count;
-}
-
/* EDAC sysfs CSROW data structures and methods
*/
@@ -185,7 +178,11 @@ static ssize_t csrow_edac_mode_show(struct csrow_info *csrow, char *data,
static ssize_t channel_dimm_label_show(struct csrow_info *csrow,
char *data, int channel)
{
- return snprintf(data, EDAC_MC_LABEL_LEN, "%s",
+ /* if field has not been initialized, there is nothing to send */
+ if (!csrow->channels[channel].label[0])
+ return 0;
+
+ return snprintf(data, EDAC_MC_LABEL_LEN, "%s\n",
csrow->channels[channel].label);
}
@@ -649,98 +646,10 @@ static struct kobj_type ktype_mci = {
.default_attrs = (struct attribute **)mci_attr,
};
-/* show/store, tables, etc for the MC kset */
-
-
-struct memctrl_dev_attribute {
- struct attribute attr;
- void *value;
- ssize_t(*show) (void *, char *);
- ssize_t(*store) (void *, const char *, size_t);
-};
-
-/* Set of show/store abstract level functions for memory control object */
-static ssize_t memctrl_dev_show(struct kobject *kobj,
- struct attribute *attr, char *buffer)
-{
- struct memctrl_dev_attribute *memctrl_dev;
- memctrl_dev = (struct memctrl_dev_attribute *)attr;
-
- if (memctrl_dev->show)
- return memctrl_dev->show(memctrl_dev->value, buffer);
-
- return -EIO;
-}
-
-static ssize_t memctrl_dev_store(struct kobject *kobj, struct attribute *attr,
- const char *buffer, size_t count)
-{
- struct memctrl_dev_attribute *memctrl_dev;
- memctrl_dev = (struct memctrl_dev_attribute *)attr;
-
- if (memctrl_dev->store)
- return memctrl_dev->store(memctrl_dev->value, buffer, count);
-
- return -EIO;
-}
-
-static struct sysfs_ops memctrlfs_ops = {
- .show = memctrl_dev_show,
- .store = memctrl_dev_store
-};
-
-#define MEMCTRL_ATTR(_name, _mode, _show, _store) \
-static struct memctrl_dev_attribute attr_##_name = { \
- .attr = {.name = __stringify(_name), .mode = _mode }, \
- .value = &_name, \
- .show = _show, \
- .store = _store, \
-};
-
-#define MEMCTRL_STRING_ATTR(_name, _data, _mode, _show, _store) \
-static struct memctrl_dev_attribute attr_##_name = { \
- .attr = {.name = __stringify(_name), .mode = _mode }, \
- .value = _data, \
- .show = _show, \
- .store = _store, \
-};
-
-/* csrow<id> control files */
-MEMCTRL_ATTR(edac_mc_panic_on_ue,
- S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store);
-
-MEMCTRL_ATTR(edac_mc_log_ue,
- S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store);
-
-MEMCTRL_ATTR(edac_mc_log_ce,
- S_IRUGO | S_IWUSR, memctrl_int_show, memctrl_int_store);
-
-MEMCTRL_ATTR(edac_mc_poll_msec,
- S_IRUGO | S_IWUSR, memctrl_int_show, poll_msec_int_store);
-
-/* Base Attributes of the memory ECC object */
-static struct memctrl_dev_attribute *memctrl_attr[] = {
- &attr_edac_mc_panic_on_ue,
- &attr_edac_mc_log_ue,
- &attr_edac_mc_log_ce,
- &attr_edac_mc_poll_msec,
- NULL,
-};
-
-
-/* the ktype for the mc_kset internal kobj */
-static struct kobj_type ktype_mc_set_attribs = {
- .sysfs_ops = &memctrlfs_ops,
- .default_attrs = (struct attribute **)memctrl_attr,
-};
-
/* EDAC memory controller sysfs kset:
* /sys/devices/system/edac/mc
*/
-static struct kset mc_kset = {
- .kobj = {.ktype = &ktype_mc_set_attribs },
-};
-
+static struct kset *mc_kset;
/*
* edac_mc_register_sysfs_main_kobj
@@ -771,7 +680,7 @@ int edac_mc_register_sysfs_main_kobj(struct mem_ctl_info *mci)
}
/* this instance become part of the mc_kset */
- kobj_mci->kset = &mc_kset;
+ kobj_mci->kset = mc_kset;
/* register the mc<id> kobject to the mc_kset */
err = kobject_init_and_add(kobj_mci, &ktype_mci, NULL,
@@ -1001,12 +910,9 @@ int edac_sysfs_setup_mc_kset(void)
}
/* Init the MC's kobject */
- kobject_set_name(&mc_kset.kobj, "mc");
- mc_kset.kobj.parent = &edac_class->kset.kobj;
-
- /* register the mc_kset */
- err = kset_register(&mc_kset);
- if (err) {
+ mc_kset = kset_create_and_add("mc", NULL, &edac_class->kset.kobj);
+ if (!mc_kset) {
+ err = -ENOMEM;
debugf1("%s() Failed to register '.../edac/mc'\n", __func__);
goto fail_out;
}
@@ -1028,6 +934,6 @@ fail_out:
*/
void edac_sysfs_teardown_mc_kset(void)
{
- kset_unregister(&mc_kset);
+ kset_unregister(mc_kset);
}
diff --git a/drivers/edac/edac_pci_sysfs.c b/drivers/edac/edac_pci_sysfs.c
index 2c1fa1bb6df..5c153dccc95 100644
--- a/drivers/edac/edac_pci_sysfs.c
+++ b/drivers/edac/edac_pci_sysfs.c
@@ -28,7 +28,7 @@ static int edac_pci_poll_msec = 1000; /* one second workq period */
static atomic_t pci_parity_count = ATOMIC_INIT(0);
static atomic_t pci_nonparity_count = ATOMIC_INIT(0);
-static struct kobject edac_pci_top_main_kobj;
+static struct kobject *edac_pci_top_main_kobj;
static atomic_t edac_pci_sysfs_refcount = ATOMIC_INIT(0);
/* getter functions for the data variables */
@@ -83,7 +83,7 @@ static void edac_pci_instance_release(struct kobject *kobj)
pci = to_instance(kobj);
/* decrement reference count on top main kobj */
- kobject_put(&edac_pci_top_main_kobj);
+ kobject_put(edac_pci_top_main_kobj);
kfree(pci); /* Free the control struct */
}
@@ -166,7 +166,7 @@ static int edac_pci_create_instance_kobj(struct edac_pci_ctl_info *pci, int idx)
* track the number of PCI instances we have, and thus nest
* properly on keeping the module loaded
*/
- main_kobj = kobject_get(&edac_pci_top_main_kobj);
+ main_kobj = kobject_get(edac_pci_top_main_kobj);
if (!main_kobj) {
err = -ENODEV;
goto error_out;
@@ -174,11 +174,11 @@ static int edac_pci_create_instance_kobj(struct edac_pci_ctl_info *pci, int idx)
/* And now register this new kobject under the main kobj */
err = kobject_init_and_add(&pci->kobj, &ktype_pci_instance,
- &edac_pci_top_main_kobj, "pci%d", idx);
+ edac_pci_top_main_kobj, "pci%d", idx);
if (err != 0) {
debugf2("%s() failed to register instance pci%d\n",
__func__, idx);
- kobject_put(&edac_pci_top_main_kobj);
+ kobject_put(edac_pci_top_main_kobj);
goto error_out;
}
@@ -316,9 +316,10 @@ static struct edac_pci_dev_attribute *edac_pci_attr[] = {
*/
static void edac_pci_release_main_kobj(struct kobject *kobj)
{
-
debugf0("%s() here to module_put(THIS_MODULE)\n", __func__);
+ kfree(kobj);
+
/* last reference to top EDAC PCI kobject has been removed,
* NOW release our ref count on the core module
*/
@@ -369,8 +370,16 @@ static int edac_pci_main_kobj_setup(void)
goto decrement_count_fail;
}
+ edac_pci_top_main_kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL);
+ if (!edac_pci_top_main_kobj) {
+ debugf1("Failed to allocate\n");
+ err = -ENOMEM;
+ goto kzalloc_fail;
+ }
+
/* Instanstiate the pci object */
- err = kobject_init_and_add(&edac_pci_top_main_kobj, &ktype_edac_pci_main_kobj,
+ err = kobject_init_and_add(edac_pci_top_main_kobj,
+ &ktype_edac_pci_main_kobj,
&edac_class->kset.kobj, "pci");
if (err) {
debugf1("Failed to register '.../edac/pci'\n");
@@ -381,13 +390,16 @@ static int edac_pci_main_kobj_setup(void)
* for EDAC PCI, then edac_pci_main_kobj_teardown()
* must be used, for resources to be cleaned up properly
*/
- kobject_uevent(&edac_pci_top_main_kobj, KOBJ_ADD);
+ kobject_uevent(edac_pci_top_main_kobj, KOBJ_ADD);
debugf1("Registered '.../edac/pci' kobject\n");
return 0;
/* Error unwind statck */
kobject_init_and_add_fail:
+ kfree(edac_pci_top_main_kobj);
+
+kzalloc_fail:
module_put(THIS_MODULE);
decrement_count_fail:
@@ -414,7 +426,7 @@ static void edac_pci_main_kobj_teardown(void)
if (atomic_dec_return(&edac_pci_sysfs_refcount) == 0) {
debugf0("%s() called kobject_put on main kobj\n",
__func__);
- kobject_put(&edac_pci_top_main_kobj);
+ kobject_put(edac_pci_top_main_kobj);
}
}
diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c
new file mode 100644
index 00000000000..22db05a67bf
--- /dev/null
+++ b/drivers/edac/i5100_edac.c
@@ -0,0 +1,981 @@
+/*
+ * Intel 5100 Memory Controllers kernel module
+ *
+ * This file may be distributed under the terms of the
+ * GNU General Public License.
+ *
+ * This module is based on the following document:
+ *
+ * Intel 5100X Chipset Memory Controller Hub (MCH) - Datasheet
+ * http://download.intel.com/design/chipsets/datashts/318378.pdf
+ *
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/slab.h>
+#include <linux/edac.h>
+#include <linux/delay.h>
+#include <linux/mmzone.h>
+
+#include "edac_core.h"
+
+/* register addresses */
+
+/* device 16, func 1 */
+#define I5100_MC 0x40 /* Memory Control Register */
+#define I5100_MS 0x44 /* Memory Status Register */
+#define I5100_SPDDATA 0x48 /* Serial Presence Detect Status Reg */
+#define I5100_SPDCMD 0x4c /* Serial Presence Detect Command Reg */
+#define I5100_TOLM 0x6c /* Top of Low Memory */
+#define I5100_MIR0 0x80 /* Memory Interleave Range 0 */
+#define I5100_MIR1 0x84 /* Memory Interleave Range 1 */
+#define I5100_AMIR_0 0x8c /* Adjusted Memory Interleave Range 0 */
+#define I5100_AMIR_1 0x90 /* Adjusted Memory Interleave Range 1 */
+#define I5100_FERR_NF_MEM 0xa0 /* MC First Non Fatal Errors */
+#define I5100_FERR_NF_MEM_M16ERR_MASK (1 << 16)
+#define I5100_FERR_NF_MEM_M15ERR_MASK (1 << 15)
+#define I5100_FERR_NF_MEM_M14ERR_MASK (1 << 14)
+#define I5100_FERR_NF_MEM_M12ERR_MASK (1 << 12)
+#define I5100_FERR_NF_MEM_M11ERR_MASK (1 << 11)
+#define I5100_FERR_NF_MEM_M10ERR_MASK (1 << 10)
+#define I5100_FERR_NF_MEM_M6ERR_MASK (1 << 6)
+#define I5100_FERR_NF_MEM_M5ERR_MASK (1 << 5)
+#define I5100_FERR_NF_MEM_M4ERR_MASK (1 << 4)
+#define I5100_FERR_NF_MEM_M1ERR_MASK 1
+#define I5100_FERR_NF_MEM_ANY_MASK \
+ (I5100_FERR_NF_MEM_M16ERR_MASK | \
+ I5100_FERR_NF_MEM_M15ERR_MASK | \
+ I5100_FERR_NF_MEM_M14ERR_MASK | \
+ I5100_FERR_NF_MEM_M12ERR_MASK | \
+ I5100_FERR_NF_MEM_M11ERR_MASK | \
+ I5100_FERR_NF_MEM_M10ERR_MASK | \
+ I5100_FERR_NF_MEM_M6ERR_MASK | \
+ I5100_FERR_NF_MEM_M5ERR_MASK | \
+ I5100_FERR_NF_MEM_M4ERR_MASK | \
+ I5100_FERR_NF_MEM_M1ERR_MASK)
+#define I5100_NERR_NF_MEM 0xa4 /* MC Next Non-Fatal Errors */
+#define I5100_EMASK_MEM 0xa8 /* MC Error Mask Register */
+
+/* device 21 and 22, func 0 */
+#define I5100_MTR_0 0x154 /* Memory Technology Registers 0-3 */
+#define I5100_DMIR 0x15c /* DIMM Interleave Range */
+#define I5100_VALIDLOG 0x18c /* Valid Log Markers */
+#define I5100_NRECMEMA 0x190 /* Non-Recoverable Memory Error Log Reg A */
+#define I5100_NRECMEMB 0x194 /* Non-Recoverable Memory Error Log Reg B */
+#define I5100_REDMEMA 0x198 /* Recoverable Memory Data Error Log Reg A */
+#define I5100_REDMEMB 0x19c /* Recoverable Memory Data Error Log Reg B */
+#define I5100_RECMEMA 0x1a0 /* Recoverable Memory Error Log Reg A */
+#define I5100_RECMEMB 0x1a4 /* Recoverable Memory Error Log Reg B */
+#define I5100_MTR_4 0x1b0 /* Memory Technology Registers 4,5 */
+
+/* bit field accessors */
+
+static inline u32 i5100_mc_errdeten(u32 mc)
+{
+ return mc >> 5 & 1;
+}
+
+static inline u16 i5100_spddata_rdo(u16 a)
+{
+ return a >> 15 & 1;
+}
+
+static inline u16 i5100_spddata_sbe(u16 a)
+{
+ return a >> 13 & 1;
+}
+
+static inline u16 i5100_spddata_busy(u16 a)
+{
+ return a >> 12 & 1;
+}
+
+static inline u16 i5100_spddata_data(u16 a)
+{
+ return a & ((1 << 8) - 1);
+}
+
+static inline u32 i5100_spdcmd_create(u32 dti, u32 ckovrd, u32 sa, u32 ba,
+ u32 data, u32 cmd)
+{
+ return ((dti & ((1 << 4) - 1)) << 28) |
+ ((ckovrd & 1) << 27) |
+ ((sa & ((1 << 3) - 1)) << 24) |
+ ((ba & ((1 << 8) - 1)) << 16) |
+ ((data & ((1 << 8) - 1)) << 8) |
+ (cmd & 1);
+}
+
+static inline u16 i5100_tolm_tolm(u16 a)
+{
+ return a >> 12 & ((1 << 4) - 1);
+}
+
+static inline u16 i5100_mir_limit(u16 a)
+{
+ return a >> 4 & ((1 << 12) - 1);
+}
+
+static inline u16 i5100_mir_way1(u16 a)
+{
+ return a >> 1 & 1;
+}
+
+static inline u16 i5100_mir_way0(u16 a)
+{
+ return a & 1;
+}
+
+static inline u32 i5100_ferr_nf_mem_chan_indx(u32 a)
+{
+ return a >> 28 & 1;
+}
+
+static inline u32 i5100_ferr_nf_mem_any(u32 a)
+{
+ return a & I5100_FERR_NF_MEM_ANY_MASK;
+}
+
+static inline u32 i5100_nerr_nf_mem_any(u32 a)
+{
+ return i5100_ferr_nf_mem_any(a);
+}
+
+static inline u32 i5100_dmir_limit(u32 a)
+{
+ return a >> 16 & ((1 << 11) - 1);
+}
+
+static inline u32 i5100_dmir_rank(u32 a, u32 i)
+{
+ return a >> (4 * i) & ((1 << 2) - 1);
+}
+
+static inline u16 i5100_mtr_present(u16 a)
+{
+ return a >> 10 & 1;
+}
+
+static inline u16 i5100_mtr_ethrottle(u16 a)
+{
+ return a >> 9 & 1;
+}
+
+static inline u16 i5100_mtr_width(u16 a)
+{
+ return a >> 8 & 1;
+}
+
+static inline u16 i5100_mtr_numbank(u16 a)
+{
+ return a >> 6 & 1;
+}
+
+static inline u16 i5100_mtr_numrow(u16 a)
+{
+ return a >> 2 & ((1 << 2) - 1);
+}
+
+static inline u16 i5100_mtr_numcol(u16 a)
+{
+ return a & ((1 << 2) - 1);
+}
+
+
+static inline u32 i5100_validlog_redmemvalid(u32 a)
+{
+ return a >> 2 & 1;
+}
+
+static inline u32 i5100_validlog_recmemvalid(u32 a)
+{
+ return a >> 1 & 1;
+}
+
+static inline u32 i5100_validlog_nrecmemvalid(u32 a)
+{
+ return a & 1;
+}
+
+static inline u32 i5100_nrecmema_merr(u32 a)
+{
+ return a >> 15 & ((1 << 5) - 1);
+}
+
+static inline u32 i5100_nrecmema_bank(u32 a)
+{
+ return a >> 12 & ((1 << 3) - 1);
+}
+
+static inline u32 i5100_nrecmema_rank(u32 a)
+{
+ return a >> 8 & ((1 << 3) - 1);
+}
+
+static inline u32 i5100_nrecmema_dm_buf_id(u32 a)
+{
+ return a & ((1 << 8) - 1);
+}
+
+static inline u32 i5100_nrecmemb_cas(u32 a)
+{
+ return a >> 16 & ((1 << 13) - 1);
+}
+
+static inline u32 i5100_nrecmemb_ras(u32 a)
+{
+ return a & ((1 << 16) - 1);
+}
+
+static inline u32 i5100_redmemb_ecc_locator(u32 a)
+{
+ return a & ((1 << 18) - 1);
+}
+
+static inline u32 i5100_recmema_merr(u32 a)
+{
+ return i5100_nrecmema_merr(a);
+}
+
+static inline u32 i5100_recmema_bank(u32 a)
+{
+ return i5100_nrecmema_bank(a);
+}
+
+static inline u32 i5100_recmema_rank(u32 a)
+{
+ return i5100_nrecmema_rank(a);
+}
+
+static inline u32 i5100_recmema_dm_buf_id(u32 a)
+{
+ return i5100_nrecmema_dm_buf_id(a);
+}
+
+static inline u32 i5100_recmemb_cas(u32 a)
+{
+ return i5100_nrecmemb_cas(a);
+}
+
+static inline u32 i5100_recmemb_ras(u32 a)
+{
+ return i5100_nrecmemb_ras(a);
+}
+
+/* some generic limits */
+#define I5100_MAX_RANKS_PER_CTLR 6
+#define I5100_MAX_CTLRS 2
+#define I5100_MAX_RANKS_PER_DIMM 4
+#define I5100_DIMM_ADDR_LINES (6 - 3) /* 64 bits / 8 bits per byte */
+#define I5100_MAX_DIMM_SLOTS_PER_CTLR 4
+#define I5100_MAX_RANK_INTERLEAVE 4
+#define I5100_MAX_DMIRS 5
+
+struct i5100_priv {
+ /* ranks on each dimm -- 0 maps to not present -- obtained via SPD */
+ int dimm_numrank[I5100_MAX_CTLRS][I5100_MAX_DIMM_SLOTS_PER_CTLR];
+
+ /*
+ * mainboard chip select map -- maps i5100 chip selects to
+ * DIMM slot chip selects. In the case of only 4 ranks per
+ * controller, the mapping is fairly obvious but not unique.
+ * we map -1 -> NC and assume both controllers use the same
+ * map...
+ *
+ */
+ int dimm_csmap[I5100_MAX_DIMM_SLOTS_PER_CTLR][I5100_MAX_RANKS_PER_DIMM];
+
+ /* memory interleave range */
+ struct {
+ u64 limit;
+ unsigned way[2];
+ } mir[I5100_MAX_CTLRS];
+
+ /* adjusted memory interleave range register */
+ unsigned amir[I5100_MAX_CTLRS];
+
+ /* dimm interleave range */
+ struct {
+ unsigned rank[I5100_MAX_RANK_INTERLEAVE];
+ u64 limit;
+ } dmir[I5100_MAX_CTLRS][I5100_MAX_DMIRS];
+
+ /* memory technology registers... */
+ struct {
+ unsigned present; /* 0 or 1 */
+ unsigned ethrottle; /* 0 or 1 */
+ unsigned width; /* 4 or 8 bits */
+ unsigned numbank; /* 2 or 3 lines */
+ unsigned numrow; /* 13 .. 16 lines */
+ unsigned numcol; /* 11 .. 12 lines */
+ } mtr[I5100_MAX_CTLRS][I5100_MAX_RANKS_PER_CTLR];
+
+ u64 tolm; /* top of low memory in bytes */
+ unsigned ranksperctlr; /* number of ranks per controller */
+
+ struct pci_dev *mc; /* device 16 func 1 */
+ struct pci_dev *ch0mm; /* device 21 func 0 */
+ struct pci_dev *ch1mm; /* device 22 func 0 */
+};
+
+/* map a rank/ctlr to a slot number on the mainboard */
+static int i5100_rank_to_slot(const struct mem_ctl_info *mci,
+ int ctlr, int rank)
+{
+ const struct i5100_priv *priv = mci->pvt_info;
+ int i;
+
+ for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CTLR; i++) {
+ int j;
+ const int numrank = priv->dimm_numrank[ctlr][i];
+
+ for (j = 0; j < numrank; j++)
+ if (priv->dimm_csmap[i][j] == rank)
+ return i * 2 + ctlr;
+ }
+
+ return -1;
+}
+
+static const char *i5100_err_msg(unsigned err)
+{
+ static const char *merrs[] = {
+ "unknown", /* 0 */
+ "uncorrectable data ECC on replay", /* 1 */
+ "unknown", /* 2 */
+ "unknown", /* 3 */
+ "aliased uncorrectable demand data ECC", /* 4 */
+ "aliased uncorrectable spare-copy data ECC", /* 5 */
+ "aliased uncorrectable patrol data ECC", /* 6 */
+ "unknown", /* 7 */
+ "unknown", /* 8 */
+ "unknown", /* 9 */
+ "non-aliased uncorrectable demand data ECC", /* 10 */
+ "non-aliased uncorrectable spare-copy data ECC", /* 11 */
+ "non-aliased uncorrectable patrol data ECC", /* 12 */
+ "unknown", /* 13 */
+ "correctable demand data ECC", /* 14 */
+ "correctable spare-copy data ECC", /* 15 */
+ "correctable patrol data ECC", /* 16 */
+ "unknown", /* 17 */
+ "SPD protocol error", /* 18 */
+ "unknown", /* 19 */
+ "spare copy initiated", /* 20 */
+ "spare copy completed", /* 21 */
+ };
+ unsigned i;
+
+ for (i = 0; i < ARRAY_SIZE(merrs); i++)
+ if (1 << i & err)
+ return merrs[i];
+
+ return "none";
+}
+
+/* convert csrow index into a rank (per controller -- 0..5) */
+static int i5100_csrow_to_rank(const struct mem_ctl_info *mci, int csrow)
+{
+ const struct i5100_priv *priv = mci->pvt_info;
+
+ return csrow % priv->ranksperctlr;
+}
+
+/* convert csrow index into a controller (0..1) */
+static int i5100_csrow_to_cntlr(const struct mem_ctl_info *mci, int csrow)
+{
+ const struct i5100_priv *priv = mci->pvt_info;
+
+ return csrow / priv->ranksperctlr;
+}
+
+static unsigned i5100_rank_to_csrow(const struct mem_ctl_info *mci,
+ int ctlr, int rank)
+{
+ const struct i5100_priv *priv = mci->pvt_info;
+
+ return ctlr * priv->ranksperctlr + rank;
+}
+
+static void i5100_handle_ce(struct mem_ctl_info *mci,
+ int ctlr,
+ unsigned bank,
+ unsigned rank,
+ unsigned long syndrome,
+ unsigned cas,
+ unsigned ras,
+ const char *msg)
+{
+ const int csrow = i5100_rank_to_csrow(mci, ctlr, rank);
+
+ printk(KERN_ERR
+ "CE ctlr %d, bank %u, rank %u, syndrome 0x%lx, "
+ "cas %u, ras %u, csrow %u, label \"%s\": %s\n",
+ ctlr, bank, rank, syndrome, cas, ras,
+ csrow, mci->csrows[csrow].channels[0].label, msg);
+
+ mci->ce_count++;
+ mci->csrows[csrow].ce_count++;
+ mci->csrows[csrow].channels[0].ce_count++;
+}
+
+static void i5100_handle_ue(struct mem_ctl_info *mci,
+ int ctlr,
+ unsigned bank,
+ unsigned rank,
+ unsigned long syndrome,
+ unsigned cas,
+ unsigned ras,
+ const char *msg)
+{
+ const int csrow = i5100_rank_to_csrow(mci, ctlr, rank);
+
+ printk(KERN_ERR
+ "UE ctlr %d, bank %u, rank %u, syndrome 0x%lx, "
+ "cas %u, ras %u, csrow %u, label \"%s\": %s\n",
+ ctlr, bank, rank, syndrome, cas, ras,
+ csrow, mci->csrows[csrow].channels[0].label, msg);
+
+ mci->ue_count++;
+ mci->csrows[csrow].ue_count++;
+}
+
+static void i5100_read_log(struct mem_ctl_info *mci, int ctlr,
+ u32 ferr, u32 nerr)
+{
+ struct i5100_priv *priv = mci->pvt_info;
+ struct pci_dev *pdev = (ctlr) ? priv->ch1mm : priv->ch0mm;
+ u32 dw;
+ u32 dw2;
+ unsigned syndrome = 0;
+ unsigned ecc_loc = 0;
+ unsigned merr;
+ unsigned bank;
+ unsigned rank;
+ unsigned cas;
+ unsigned ras;
+
+ pci_read_config_dword(pdev, I5100_VALIDLOG, &dw);
+
+ if (i5100_validlog_redmemvalid(dw)) {
+ pci_read_config_dword(pdev, I5100_REDMEMA, &dw2);
+ syndrome = dw2;
+ pci_read_config_dword(pdev, I5100_REDMEMB, &dw2);
+ ecc_loc = i5100_redmemb_ecc_locator(dw2);
+ }
+
+ if (i5100_validlog_recmemvalid(dw)) {
+ const char *msg;
+
+ pci_read_config_dword(pdev, I5100_RECMEMA, &dw2);
+ merr = i5100_recmema_merr(dw2);
+ bank = i5100_recmema_bank(dw2);
+ rank = i5100_recmema_rank(dw2);
+
+ pci_read_config_dword(pdev, I5100_RECMEMB, &dw2);
+ cas = i5100_recmemb_cas(dw2);
+ ras = i5100_recmemb_ras(dw2);
+
+ /* FIXME: not really sure if this is what merr is...
+ */
+ if (!merr)
+ msg = i5100_err_msg(ferr);
+ else
+ msg = i5100_err_msg(nerr);
+
+ i5100_handle_ce(mci, ctlr, bank, rank, syndrome, cas, ras, msg);
+ }
+
+ if (i5100_validlog_nrecmemvalid(dw)) {
+ const char *msg;
+
+ pci_read_config_dword(pdev, I5100_NRECMEMA, &dw2);
+ merr = i5100_nrecmema_merr(dw2);
+ bank = i5100_nrecmema_bank(dw2);
+ rank = i5100_nrecmema_rank(dw2);
+
+ pci_read_config_dword(pdev, I5100_NRECMEMB, &dw2);
+ cas = i5100_nrecmemb_cas(dw2);
+ ras = i5100_nrecmemb_ras(dw2);
+
+ /* FIXME: not really sure if this is what merr is...
+ */
+ if (!merr)
+ msg = i5100_err_msg(ferr);
+ else
+ msg = i5100_err_msg(nerr);
+
+ i5100_handle_ue(mci, ctlr, bank, rank, syndrome, cas, ras, msg);
+ }
+
+ pci_write_config_dword(pdev, I5100_VALIDLOG, dw);
+}
+
+static void i5100_check_error(struct mem_ctl_info *mci)
+{
+ struct i5100_priv *priv = mci->pvt_info;
+ u32 dw;
+
+
+ pci_read_config_dword(priv->mc, I5100_FERR_NF_MEM, &dw);
+ if (i5100_ferr_nf_mem_any(dw)) {
+ u32 dw2;
+
+ pci_read_config_dword(priv->mc, I5100_NERR_NF_MEM, &dw2);
+ if (dw2)
+ pci_write_config_dword(priv->mc, I5100_NERR_NF_MEM,
+ dw2);
+ pci_write_config_dword(priv->mc, I5100_FERR_NF_MEM, dw);
+
+ i5100_read_log(mci, i5100_ferr_nf_mem_chan_indx(dw),
+ i5100_ferr_nf_mem_any(dw),
+ i5100_nerr_nf_mem_any(dw2));
+ }
+}
+
+static struct pci_dev *pci_get_device_func(unsigned vendor,
+ unsigned device,
+ unsigned func)
+{
+ struct pci_dev *ret = NULL;
+
+ while (1) {
+ ret = pci_get_device(vendor, device, ret);
+
+ if (!ret)
+ break;
+
+ if (PCI_FUNC(ret->devfn) == func)
+ break;
+ }
+
+ return ret;
+}
+
+static unsigned long __devinit i5100_npages(struct mem_ctl_info *mci,
+ int csrow)
+{
+ struct i5100_priv *priv = mci->pvt_info;
+ const unsigned ctlr_rank = i5100_csrow_to_rank(mci, csrow);
+ const unsigned ctlr = i5100_csrow_to_cntlr(mci, csrow);
+ unsigned addr_lines;
+
+ /* dimm present? */
+ if (!priv->mtr[ctlr][ctlr_rank].present)
+ return 0ULL;
+
+ addr_lines =
+ I5100_DIMM_ADDR_LINES +
+ priv->mtr[ctlr][ctlr_rank].numcol +
+ priv->mtr[ctlr][ctlr_rank].numrow +
+ priv->mtr[ctlr][ctlr_rank].numbank;
+
+ return (unsigned long)
+ ((unsigned long long) (1ULL << addr_lines) / PAGE_SIZE);
+}
+
+static void __devinit i5100_init_mtr(struct mem_ctl_info *mci)
+{
+ struct i5100_priv *priv = mci->pvt_info;
+ struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm };
+ int i;
+
+ for (i = 0; i < I5100_MAX_CTLRS; i++) {
+ int j;
+ struct pci_dev *pdev = mms[i];
+
+ for (j = 0; j < I5100_MAX_RANKS_PER_CTLR; j++) {
+ const unsigned addr =
+ (j < 4) ? I5100_MTR_0 + j * 2 :
+ I5100_MTR_4 + (j - 4) * 2;
+ u16 w;
+
+ pci_read_config_word(pdev, addr, &w);
+
+ priv->mtr[i][j].present = i5100_mtr_present(w);
+ priv->mtr[i][j].ethrottle = i5100_mtr_ethrottle(w);
+ priv->mtr[i][j].width = 4 + 4 * i5100_mtr_width(w);
+ priv->mtr[i][j].numbank = 2 + i5100_mtr_numbank(w);
+ priv->mtr[i][j].numrow = 13 + i5100_mtr_numrow(w);
+ priv->mtr[i][j].numcol = 10 + i5100_mtr_numcol(w);
+ }
+ }
+}
+
+/*
+ * FIXME: make this into a real i2c adapter (so that dimm-decode
+ * will work)?
+ */
+static int i5100_read_spd_byte(const struct mem_ctl_info *mci,
+ u8 ch, u8 slot, u8 addr, u8 *byte)
+{
+ struct i5100_priv *priv = mci->pvt_info;
+ u16 w;
+ unsigned long et;
+
+ pci_read_config_word(priv->mc, I5100_SPDDATA, &w);
+ if (i5100_spddata_busy(w))
+ return -1;
+
+ pci_write_config_dword(priv->mc, I5100_SPDCMD,
+ i5100_spdcmd_create(0xa, 1, ch * 4 + slot, addr,
+ 0, 0));
+
+ /* wait up to 100ms */
+ et = jiffies + HZ / 10;
+ udelay(100);
+ while (1) {
+ pci_read_config_word(priv->mc, I5100_SPDDATA, &w);
+ if (!i5100_spddata_busy(w))
+ break;
+ udelay(100);
+ }
+
+ if (!i5100_spddata_rdo(w) || i5100_spddata_sbe(w))
+ return -1;
+
+ *byte = i5100_spddata_data(w);
+
+ return 0;
+}
+
+/*
+ * fill dimm chip select map
+ *
+ * FIXME:
+ * o only valid for 4 ranks per controller
+ * o not the only way to may chip selects to dimm slots
+ * o investigate if there is some way to obtain this map from the bios
+ */
+static void __devinit i5100_init_dimm_csmap(struct mem_ctl_info *mci)
+{
+ struct i5100_priv *priv = mci->pvt_info;
+ int i;
+
+ WARN_ON(priv->ranksperctlr != 4);
+
+ for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CTLR; i++) {
+ int j;
+
+ for (j = 0; j < I5100_MAX_RANKS_PER_DIMM; j++)
+ priv->dimm_csmap[i][j] = -1; /* default NC */
+ }
+
+ /* only 2 chip selects per slot... */
+ priv->dimm_csmap[0][0] = 0;
+ priv->dimm_csmap[0][1] = 3;
+ priv->dimm_csmap[1][0] = 1;
+ priv->dimm_csmap[1][1] = 2;
+ priv->dimm_csmap[2][0] = 2;
+ priv->dimm_csmap[3][0] = 3;
+}
+
+static void __devinit i5100_init_dimm_layout(struct pci_dev *pdev,
+ struct mem_ctl_info *mci)
+{
+ struct i5100_priv *priv = mci->pvt_info;
+ int i;
+
+ for (i = 0; i < I5100_MAX_CTLRS; i++) {
+ int j;
+
+ for (j = 0; j < I5100_MAX_DIMM_SLOTS_PER_CTLR; j++) {
+ u8 rank;
+
+ if (i5100_read_spd_byte(mci, i, j, 5, &rank) < 0)
+ priv->dimm_numrank[i][j] = 0;
+ else
+ priv->dimm_numrank[i][j] = (rank & 3) + 1;
+ }
+ }
+
+ i5100_init_dimm_csmap(mci);
+}
+
+static void __devinit i5100_init_interleaving(struct pci_dev *pdev,
+ struct mem_ctl_info *mci)
+{
+ u16 w;
+ u32 dw;
+ struct i5100_priv *priv = mci->pvt_info;
+ struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm };
+ int i;
+
+ pci_read_config_word(pdev, I5100_TOLM, &w);
+ priv->tolm = (u64) i5100_tolm_tolm(w) * 256 * 1024 * 1024;
+
+ pci_read_config_word(pdev, I5100_MIR0, &w);
+ priv->mir[0].limit = (u64) i5100_mir_limit(w) << 28;
+ priv->mir[0].way[1] = i5100_mir_way1(w);
+ priv->mir[0].way[0] = i5100_mir_way0(w);
+
+ pci_read_config_word(pdev, I5100_MIR1, &w);
+ priv->mir[1].limit = (u64) i5100_mir_limit(w) << 28;
+ priv->mir[1].way[1] = i5100_mir_way1(w);
+ priv->mir[1].way[0] = i5100_mir_way0(w);
+
+ pci_read_config_word(pdev, I5100_AMIR_0, &w);
+ priv->amir[0] = w;
+ pci_read_config_word(pdev, I5100_AMIR_1, &w);
+ priv->amir[1] = w;
+
+ for (i = 0; i < I5100_MAX_CTLRS; i++) {
+ int j;
+
+ for (j = 0; j < 5; j++) {
+ int k;
+
+ pci_read_config_dword(mms[i], I5100_DMIR + j * 4, &dw);
+
+ priv->dmir[i][j].limit =
+ (u64) i5100_dmir_limit(dw) << 28;
+ for (k = 0; k < I5100_MAX_RANKS_PER_DIMM; k++)
+ priv->dmir[i][j].rank[k] =
+ i5100_dmir_rank(dw, k);
+ }
+ }
+
+ i5100_init_mtr(mci);
+}
+
+static void __devinit i5100_init_csrows(struct mem_ctl_info *mci)
+{
+ int i;
+ unsigned long total_pages = 0UL;
+ struct i5100_priv *priv = mci->pvt_info;
+
+ for (i = 0; i < mci->nr_csrows; i++) {
+ const unsigned long npages = i5100_npages(mci, i);
+ const unsigned cntlr = i5100_csrow_to_cntlr(mci, i);
+ const unsigned rank = i5100_csrow_to_rank(mci, i);
+
+ if (!npages)
+ continue;
+
+ /*
+ * FIXME: these two are totally bogus -- I don't see how to
+ * map them correctly to this structure...
+ */
+ mci->csrows[i].first_page = total_pages;
+ mci->csrows[i].last_page = total_pages + npages - 1;
+ mci->csrows[i].page_mask = 0UL;
+
+ mci->csrows[i].nr_pages = npages;
+ mci->csrows[i].grain = 32;
+ mci->csrows[i].csrow_idx = i;
+ mci->csrows[i].dtype =
+ (priv->mtr[cntlr][rank].width == 4) ? DEV_X4 : DEV_X8;
+ mci->csrows[i].ue_count = 0;
+ mci->csrows[i].ce_count = 0;
+ mci->csrows[i].mtype = MEM_RDDR2;
+ mci->csrows[i].edac_mode = EDAC_SECDED;
+ mci->csrows[i].mci = mci;
+ mci->csrows[i].nr_channels = 1;
+ mci->csrows[i].channels[0].chan_idx = 0;
+ mci->csrows[i].channels[0].ce_count = 0;
+ mci->csrows[i].channels[0].csrow = mci->csrows + i;
+ snprintf(mci->csrows[i].channels[0].label,
+ sizeof(mci->csrows[i].channels[0].label),
+ "DIMM%u", i5100_rank_to_slot(mci, cntlr, rank));
+
+ total_pages += npages;
+ }
+}
+
+static int __devinit i5100_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ int rc;
+ struct mem_ctl_info *mci;
+ struct i5100_priv *priv;
+ struct pci_dev *ch0mm, *ch1mm;
+ int ret = 0;
+ u32 dw;
+ int ranksperch;
+
+ if (PCI_FUNC(pdev->devfn) != 1)
+ return -ENODEV;
+
+ rc = pci_enable_device(pdev);
+ if (rc < 0) {
+ ret = rc;
+ goto bail;
+ }
+
+ /* ECC enabled? */
+ pci_read_config_dword(pdev, I5100_MC, &dw);
+ if (!i5100_mc_errdeten(dw)) {
+ printk(KERN_INFO "i5100_edac: ECC not enabled.\n");
+ ret = -ENODEV;
+ goto bail_pdev;
+ }
+
+ /* figure out how many ranks, from strapped state of 48GB_Mode input */
+ pci_read_config_dword(pdev, I5100_MS, &dw);
+ ranksperch = !!(dw & (1 << 8)) * 2 + 4;
+
+ if (ranksperch != 4) {
+ /* FIXME: get 6 ranks / controller to work - need hw... */
+ printk(KERN_INFO "i5100_edac: unsupported configuration.\n");
+ ret = -ENODEV;
+ goto bail_pdev;
+ }
+
+ /* enable error reporting... */
+ pci_read_config_dword(pdev, I5100_EMASK_MEM, &dw);
+ dw &= ~I5100_FERR_NF_MEM_ANY_MASK;
+ pci_write_config_dword(pdev, I5100_EMASK_MEM, dw);
+
+ /* device 21, func 0, Channel 0 Memory Map, Error Flag/Mask, etc... */
+ ch0mm = pci_get_device_func(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_5100_21, 0);
+ if (!ch0mm) {
+ ret = -ENODEV;
+ goto bail_pdev;
+ }
+
+ rc = pci_enable_device(ch0mm);
+ if (rc < 0) {
+ ret = rc;
+ goto bail_ch0;
+ }
+
+ /* device 22, func 0, Channel 1 Memory Map, Error Flag/Mask, etc... */
+ ch1mm = pci_get_device_func(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_5100_22, 0);
+ if (!ch1mm) {
+ ret = -ENODEV;
+ goto bail_disable_ch0;
+ }
+
+ rc = pci_enable_device(ch1mm);
+ if (rc < 0) {
+ ret = rc;
+ goto bail_ch1;
+ }
+
+ mci = edac_mc_alloc(sizeof(*priv), ranksperch * 2, 1, 0);
+ if (!mci) {
+ ret = -ENOMEM;
+ goto bail_disable_ch1;
+ }
+
+ mci->dev = &pdev->dev;
+
+ priv = mci->pvt_info;
+ priv->ranksperctlr = ranksperch;
+ priv->mc = pdev;
+ priv->ch0mm = ch0mm;
+ priv->ch1mm = ch1mm;
+
+ i5100_init_dimm_layout(pdev, mci);
+ i5100_init_interleaving(pdev, mci);
+
+ mci->mtype_cap = MEM_FLAG_FB_DDR2;
+ mci->edac_ctl_cap = EDAC_FLAG_SECDED;
+ mci->edac_cap = EDAC_FLAG_SECDED;
+ mci->mod_name = "i5100_edac.c";
+ mci->mod_ver = "not versioned";
+ mci->ctl_name = "i5100";
+ mci->dev_name = pci_name(pdev);
+ mci->ctl_page_to_phys = NULL;
+
+ mci->edac_check = i5100_check_error;
+
+ i5100_init_csrows(mci);
+
+ /* this strange construction seems to be in every driver, dunno why */
+ switch (edac_op_state) {
+ case EDAC_OPSTATE_POLL:
+ case EDAC_OPSTATE_NMI:
+ break;
+ default:
+ edac_op_state = EDAC_OPSTATE_POLL;
+ break;
+ }
+
+ if (edac_mc_add_mc(mci)) {
+ ret = -ENODEV;
+ goto bail_mc;
+ }
+
+ return ret;
+
+bail_mc:
+ edac_mc_free(mci);
+
+bail_disable_ch1:
+ pci_disable_device(ch1mm);
+
+bail_ch1:
+ pci_dev_put(ch1mm);
+
+bail_disable_ch0:
+ pci_disable_device(ch0mm);
+
+bail_ch0:
+ pci_dev_put(ch0mm);
+
+bail_pdev:
+ pci_disable_device(pdev);
+
+bail:
+ return ret;
+}
+
+static void __devexit i5100_remove_one(struct pci_dev *pdev)
+{
+ struct mem_ctl_info *mci;
+ struct i5100_priv *priv;
+
+ mci = edac_mc_del_mc(&pdev->dev);
+
+ if (!mci)
+ return;
+
+ priv = mci->pvt_info;
+ pci_disable_device(pdev);
+ pci_disable_device(priv->ch0mm);
+ pci_disable_device(priv->ch1mm);
+ pci_dev_put(priv->ch0mm);
+ pci_dev_put(priv->ch1mm);
+
+ edac_mc_free(mci);
+}
+
+static const struct pci_device_id i5100_pci_tbl[] __devinitdata = {
+ /* Device 16, Function 0, Channel 0 Memory Map, Error Flag/Mask, ... */
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5100_16) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, i5100_pci_tbl);
+
+static struct pci_driver i5100_driver = {
+ .name = KBUILD_BASENAME,
+ .probe = i5100_init_one,
+ .remove = __devexit_p(i5100_remove_one),
+ .id_table = i5100_pci_tbl,
+};
+
+static int __init i5100_init(void)
+{
+ int pci_rc;
+
+ pci_rc = pci_register_driver(&i5100_driver);
+
+ return (pci_rc < 0) ? pci_rc : 0;
+}
+
+static void __exit i5100_exit(void)
+{
+ pci_unregister_driver(&i5100_driver);
+}
+
+module_init(i5100_init);
+module_exit(i5100_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR
+ ("Arthur Jones <ajones@riverbed.com>");
+MODULE_DESCRIPTION("MC Driver for Intel I5100 memory controllers");
diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c
index d49361bfe67..2265d9ca153 100644
--- a/drivers/edac/mpc85xx_edac.c
+++ b/drivers/edac/mpc85xx_edac.c
@@ -195,14 +195,15 @@ static irqreturn_t mpc85xx_pci_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int __devinit mpc85xx_pci_err_probe(struct platform_device *pdev)
+static int __devinit mpc85xx_pci_err_probe(struct of_device *op,
+ const struct of_device_id *match)
{
struct edac_pci_ctl_info *pci;
struct mpc85xx_pci_pdata *pdata;
- struct resource *r;
+ struct resource r;
int res = 0;
- if (!devres_open_group(&pdev->dev, mpc85xx_pci_err_probe, GFP_KERNEL))
+ if (!devres_open_group(&op->dev, mpc85xx_pci_err_probe, GFP_KERNEL))
return -ENOMEM;
pci = edac_pci_alloc_ctl_info(sizeof(*pdata), "mpc85xx_pci_err");
@@ -212,34 +213,37 @@ static int __devinit mpc85xx_pci_err_probe(struct platform_device *pdev)
pdata = pci->pvt_info;
pdata->name = "mpc85xx_pci_err";
pdata->irq = NO_IRQ;
- platform_set_drvdata(pdev, pci);
- pci->dev = &pdev->dev;
+ dev_set_drvdata(&op->dev, pci);
+ pci->dev = &op->dev;
pci->mod_name = EDAC_MOD_STR;
pci->ctl_name = pdata->name;
- pci->dev_name = pdev->dev.bus_id;
+ pci->dev_name = op->dev.bus_id;
if (edac_op_state == EDAC_OPSTATE_POLL)
pci->edac_check = mpc85xx_pci_check;
pdata->edac_idx = edac_pci_idx++;
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r) {
+ res = of_address_to_resource(op->node, 0, &r);
+ if (res) {
printk(KERN_ERR "%s: Unable to get resource for "
"PCI err regs\n", __func__);
goto err;
}
- if (!devm_request_mem_region(&pdev->dev, r->start,
- r->end - r->start + 1, pdata->name)) {
+ /* we only need the error registers */
+ r.start += 0xe00;
+
+ if (!devm_request_mem_region(&op->dev, r.start,
+ r.end - r.start + 1, pdata->name)) {
printk(KERN_ERR "%s: Error while requesting mem region\n",
__func__);
res = -EBUSY;
goto err;
}
- pdata->pci_vbase = devm_ioremap(&pdev->dev, r->start,
- r->end - r->start + 1);
+ pdata->pci_vbase = devm_ioremap(&op->dev, r.start,
+ r.end - r.start + 1);
if (!pdata->pci_vbase) {
printk(KERN_ERR "%s: Unable to setup PCI err regs\n", __func__);
res = -ENOMEM;
@@ -266,14 +270,15 @@ static int __devinit mpc85xx_pci_err_probe(struct platform_device *pdev)
}
if (edac_op_state == EDAC_OPSTATE_INT) {
- pdata->irq = platform_get_irq(pdev, 0);
- res = devm_request_irq(&pdev->dev, pdata->irq,
+ pdata->irq = irq_of_parse_and_map(op->node, 0);
+ res = devm_request_irq(&op->dev, pdata->irq,
mpc85xx_pci_isr, IRQF_DISABLED,
"[EDAC] PCI err", pci);
if (res < 0) {
printk(KERN_ERR
"%s: Unable to requiest irq %d for "
"MPC85xx PCI err\n", __func__, pdata->irq);
+ irq_dispose_mapping(pdata->irq);
res = -ENODEV;
goto err2;
}
@@ -282,23 +287,23 @@ static int __devinit mpc85xx_pci_err_probe(struct platform_device *pdev)
pdata->irq);
}
- devres_remove_group(&pdev->dev, mpc85xx_pci_err_probe);
+ devres_remove_group(&op->dev, mpc85xx_pci_err_probe);
debugf3("%s(): success\n", __func__);
printk(KERN_INFO EDAC_MOD_STR " PCI err registered\n");
return 0;
err2:
- edac_pci_del_device(&pdev->dev);
+ edac_pci_del_device(&op->dev);
err:
edac_pci_free_ctl_info(pci);
- devres_release_group(&pdev->dev, mpc85xx_pci_err_probe);
+ devres_release_group(&op->dev, mpc85xx_pci_err_probe);
return res;
}
-static int mpc85xx_pci_err_remove(struct platform_device *pdev)
+static int mpc85xx_pci_err_remove(struct of_device *op)
{
- struct edac_pci_ctl_info *pci = platform_get_drvdata(pdev);
+ struct edac_pci_ctl_info *pci = dev_get_drvdata(&op->dev);
struct mpc85xx_pci_pdata *pdata = pci->pvt_info;
debugf0("%s()\n", __func__);
@@ -318,12 +323,26 @@ static int mpc85xx_pci_err_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_driver mpc85xx_pci_err_driver = {
+static struct of_device_id mpc85xx_pci_err_of_match[] = {
+ {
+ .compatible = "fsl,mpc8540-pcix",
+ },
+ {
+ .compatible = "fsl,mpc8540-pci",
+ },
+ {},
+};
+
+static struct of_platform_driver mpc85xx_pci_err_driver = {
+ .owner = THIS_MODULE,
+ .name = "mpc85xx_pci_err",
+ .match_table = mpc85xx_pci_err_of_match,
.probe = mpc85xx_pci_err_probe,
.remove = __devexit_p(mpc85xx_pci_err_remove),
.driver = {
- .name = "mpc85xx_pci_err",
- }
+ .name = "mpc85xx_pci_err",
+ .owner = THIS_MODULE,
+ },
};
#endif /* CONFIG_PCI */
@@ -1002,7 +1021,7 @@ static int __init mpc85xx_mc_init(void)
printk(KERN_WARNING EDAC_MOD_STR "L2 fails to register\n");
#ifdef CONFIG_PCI
- res = platform_driver_register(&mpc85xx_pci_err_driver);
+ res = of_register_platform_driver(&mpc85xx_pci_err_driver);
if (res)
printk(KERN_WARNING EDAC_MOD_STR "PCI fails to register\n");
#endif
@@ -1025,7 +1044,7 @@ static void __exit mpc85xx_mc_exit(void)
{
mtspr(SPRN_HID1, orig_hid1);
#ifdef CONFIG_PCI
- platform_driver_unregister(&mpc85xx_pci_err_driver);
+ of_unregister_platform_driver(&mpc85xx_pci_err_driver);
#endif
of_unregister_platform_driver(&mpc85xx_l2_err_driver);
of_unregister_platform_driver(&mpc85xx_mc_err_driver);
diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c
index bf071f140a0..083ce8d0c63 100644
--- a/drivers/edac/mv64x60_edac.c
+++ b/drivers/edac/mv64x60_edac.c
@@ -71,6 +71,35 @@ static irqreturn_t mv64x60_pci_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
+/*
+ * Bit 0 of MV64x60_PCIx_ERR_MASK does not exist on the 64360 and because of
+ * errata FEr-#11 and FEr-##16 for the 64460, it should be 0 on that chip as
+ * well. IOW, don't set bit 0.
+ */
+
+/* Erratum FEr PCI-#16: clear bit 0 of PCI SERRn Mask reg. */
+static int __init mv64x60_pci_fixup(struct platform_device *pdev)
+{
+ struct resource *r;
+ void __iomem *pci_serr;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!r) {
+ printk(KERN_ERR "%s: Unable to get resource for "
+ "PCI err regs\n", __func__);
+ return -ENOENT;
+ }
+
+ pci_serr = ioremap(r->start, r->end - r->start + 1);
+ if (!pci_serr)
+ return -ENOMEM;
+
+ out_le32(pci_serr, in_le32(pci_serr) & ~0x1);
+ iounmap(pci_serr);
+
+ return 0;
+}
+
static int __devinit mv64x60_pci_err_probe(struct platform_device *pdev)
{
struct edac_pci_ctl_info *pci;
@@ -128,6 +157,12 @@ static int __devinit mv64x60_pci_err_probe(struct platform_device *pdev)
goto err;
}
+ res = mv64x60_pci_fixup(pdev);
+ if (res < 0) {
+ printk(KERN_ERR "%s: PCI fixup failed\n", __func__);
+ goto err;
+ }
+
out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE, 0);
out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_MASK, 0);
out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_MASK,
@@ -612,7 +647,7 @@ static void get_total_mem(struct mv64x60_mc_pdata *pdata)
if (!np)
return;
- reg = get_property(np, "reg", NULL);
+ reg = of_get_property(np, "reg", NULL);
pdata->total_mem = reg[1];
}
diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c
index 0b624e927a6..c66817e7717 100644
--- a/drivers/firmware/dcdbas.c
+++ b/drivers/firmware/dcdbas.c
@@ -152,20 +152,11 @@ static ssize_t smi_data_read(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t count)
{
- size_t max_read;
ssize_t ret;
mutex_lock(&smi_data_lock);
-
- if (pos >= smi_data_buf_size) {
- ret = 0;
- goto out;
- }
-
- max_read = smi_data_buf_size - pos;
- ret = min(max_read, count);
- memcpy(buf, smi_data_buf + pos, ret);
-out:
+ ret = memory_read_from_buffer(buf, count, &pos, smi_data_buf,
+ smi_data_buf_size);
mutex_unlock(&smi_data_lock);
return ret;
}
diff --git a/drivers/firmware/dell_rbu.c b/drivers/firmware/dell_rbu.c
index 7430e218cda..13946ebd77d 100644
--- a/drivers/firmware/dell_rbu.c
+++ b/drivers/firmware/dell_rbu.c
@@ -507,11 +507,6 @@ static ssize_t read_packet_data(char *buffer, loff_t pos, size_t count)
static ssize_t read_rbu_mono_data(char *buffer, loff_t pos, size_t count)
{
- unsigned char *ptemp = NULL;
- size_t bytes_left = 0;
- size_t data_length = 0;
- ssize_t ret_count = 0;
-
/* check to see if we have something to return */
if ((rbu_data.image_update_buffer == NULL) ||
(rbu_data.bios_image_size == 0)) {
@@ -519,28 +514,11 @@ static ssize_t read_rbu_mono_data(char *buffer, loff_t pos, size_t count)
"bios_image_size %lu\n",
rbu_data.image_update_buffer,
rbu_data.bios_image_size);
- ret_count = -ENOMEM;
- goto read_rbu_data_exit;
- }
-
- if (pos > rbu_data.bios_image_size) {
- ret_count = 0;
- goto read_rbu_data_exit;
+ return -ENOMEM;
}
- bytes_left = rbu_data.bios_image_size - pos;
- data_length = min(bytes_left, count);
-
- ptemp = rbu_data.image_update_buffer;
- memcpy(buffer, (ptemp + pos), data_length);
-
- if ((pos + count) > rbu_data.bios_image_size)
- /* this was the last copy */
- ret_count = bytes_left;
- else
- ret_count = count;
- read_rbu_data_exit:
- return ret_count;
+ return memory_read_from_buffer(buffer, count, &pos,
+ rbu_data.image_update_buffer, rbu_data.bios_image_size);
}
static ssize_t read_rbu_data(struct kobject *kobj,
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index fced1909cbb..dbd42d6c93a 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -2,15 +2,40 @@
# GPIO infrastructure and expanders
#
-config HAVE_GPIO_LIB
+config ARCH_WANT_OPTIONAL_GPIOLIB
bool
help
+ Select this config option from the architecture Kconfig, if
+ it is possible to use gpiolib on the architecture, but let the
+ user decide whether to actually build it or not.
+ Select this instead of ARCH_REQUIRE_GPIOLIB, if your architecture does
+ not depend on GPIOs being available, but rather let the user
+ decide whether he needs it or not.
+
+config ARCH_REQUIRE_GPIOLIB
+ bool
+ select GPIOLIB
+ help
Platforms select gpiolib if they use this infrastructure
for all their GPIOs, usually starting with ones integrated
into SOC processors.
+ Selecting this from the architecture code will cause the gpiolib
+ code to always get built in.
+
+
+
+menuconfig GPIOLIB
+ bool "GPIO Support"
+ depends on ARCH_WANT_OPTIONAL_GPIOLIB || ARCH_REQUIRE_GPIOLIB
+ select GENERIC_GPIO
+ help
+ This enables GPIO support through the generic GPIO library.
+ You only need to enable this, if you also want to enable
+ one or more of the GPIO expansion card drivers below.
-menu "GPIO Support"
- depends on HAVE_GPIO_LIB
+ If unsure, say N.
+
+if GPIOLIB
config DEBUG_GPIO
bool "Debug GPIO calls"
@@ -23,10 +48,44 @@ config DEBUG_GPIO
slower. The diagnostics help catch the type of setup errors
that are most common when setting up new platforms or boards.
+config GPIO_SYSFS
+ bool "/sys/class/gpio/... (sysfs interface)"
+ depends on SYSFS && EXPERIMENTAL
+ help
+ Say Y here to add a sysfs interface for GPIOs.
+
+ This is mostly useful to work around omissions in a system's
+ kernel support. Those are common in custom and semicustom
+ hardware assembled using standard kernels with a minimum of
+ custom patches. In those cases, userspace code may import
+ a given GPIO from the kernel, if no kernel driver requested it.
+
+ Kernel drivers may also request that a particular GPIO be
+ exported to userspace; this can be useful when debugging.
+
# put expanders in the right section, in alphabetical order
comment "I2C GPIO expanders:"
+config GPIO_MAX732X
+ tristate "MAX7319, MAX7320-7327 I2C Port Expanders"
+ depends on I2C
+ help
+ Say yes here to support the MAX7319, MAX7320-7327 series of I2C
+ Port Expanders. Each IO port on these chips has a fixed role of
+ Input (designated by 'I'), Push-Pull Output ('O'), or Open-Drain
+ Input and Output (designed by 'P'). The combinations are listed
+ below:
+
+ 8 bits: max7319 (8I), max7320 (8O), max7321 (8P),
+ max7322 (4I4O), max7323 (4P4O)
+
+ 16 bits: max7324 (8I8O), max7325 (8P8O),
+ max7326 (4I12O), max7327 (4P12O)
+
+ Board setup code must specify the model to use, and the start
+ number for these GPIOs.
+
config GPIO_PCA953X
tristate "PCA953x, PCA955x, and MAX7310 I/O ports"
depends on I2C
@@ -68,6 +127,24 @@ config GPIO_PCF857X
This driver provides an in-kernel interface to those GPIOs using
platform-neutral GPIO calls.
+comment "PCI GPIO expanders:"
+
+config GPIO_BT8XX
+ tristate "BT8XX GPIO abuser"
+ depends on PCI && VIDEO_BT848=n
+ help
+ The BT8xx frame grabber chip has 24 GPIO pins than can be abused
+ as a cheap PCI GPIO card.
+
+ This chip can be found on Miro, Hauppauge and STB TV-cards.
+
+ The card needs to be physically altered for using it as a
+ GPIO card. For more information on how to build a GPIO card
+ from a BT8xx TV card, see the documentation file at
+ Documentation/bt8xxgpio.txt
+
+ If unsure, say N.
+
comment "SPI GPIO expanders:"
config GPIO_MAX7301
@@ -83,4 +160,4 @@ config GPIO_MCP23S08
SPI driver for Microchip MCP23S08 I/O expander. This provides
a GPIO interface supporting inputs and outputs.
-endmenu
+endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 16e796dc541..01b4bbde195 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -2,9 +2,11 @@
ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG
-obj-$(CONFIG_HAVE_GPIO_LIB) += gpiolib.o
+obj-$(CONFIG_GPIOLIB) += gpiolib.o
obj-$(CONFIG_GPIO_MAX7301) += max7301.o
+obj-$(CONFIG_GPIO_MAX732X) += max732x.o
obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o
obj-$(CONFIG_GPIO_PCA953X) += pca953x.o
obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o
+obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o
diff --git a/drivers/gpio/bt8xxgpio.c b/drivers/gpio/bt8xxgpio.c
new file mode 100644
index 00000000000..7a1168249dd
--- /dev/null
+++ b/drivers/gpio/bt8xxgpio.c
@@ -0,0 +1,348 @@
+/*
+
+ bt8xx GPIO abuser
+
+ Copyright (C) 2008 Michael Buesch <mb@bu3sch.de>
+
+ Please do _only_ contact the people listed _above_ with issues related to this driver.
+ All the other people listed below are not related to this driver. Their names
+ are only here, because this driver is derived from the bt848 driver.
+
+
+ Derived from the bt848 driver:
+
+ Copyright (C) 1996,97,98 Ralph Metzler
+ & Marcus Metzler
+ (c) 1999-2002 Gerd Knorr
+
+ some v4l2 code lines are taken from Justin's bttv2 driver which is
+ (c) 2000 Justin Schoeman
+
+ V4L1 removal from:
+ (c) 2005-2006 Nickolay V. Shmyrev
+
+ Fixes to be fully V4L2 compliant by
+ (c) 2006 Mauro Carvalho Chehab
+
+ Cropping and overscan support
+ Copyright (C) 2005, 2006 Michael H. Schimek
+ Sponsored by OPQ Systems AB
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+
+#include <asm/gpio.h>
+
+/* Steal the hardware definitions from the bttv driver. */
+#include "../media/video/bt8xx/bt848.h"
+
+
+#define BT8XXGPIO_NR_GPIOS 24 /* We have 24 GPIO pins */
+
+
+struct bt8xxgpio {
+ spinlock_t lock;
+
+ void __iomem *mmio;
+ struct pci_dev *pdev;
+ struct gpio_chip gpio;
+
+#ifdef CONFIG_PM
+ u32 saved_outen;
+ u32 saved_data;
+#endif
+};
+
+#define bgwrite(dat, adr) writel((dat), bg->mmio+(adr))
+#define bgread(adr) readl(bg->mmio+(adr))
+
+
+static int modparam_gpiobase = -1/* dynamic */;
+module_param_named(gpiobase, modparam_gpiobase, int, 0444);
+MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, which is the default.");
+
+
+static int bt8xxgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
+{
+ struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio);
+ unsigned long flags;
+ u32 outen, data;
+
+ spin_lock_irqsave(&bg->lock, flags);
+
+ data = bgread(BT848_GPIO_DATA);
+ data &= ~(1 << nr);
+ bgwrite(data, BT848_GPIO_DATA);
+
+ outen = bgread(BT848_GPIO_OUT_EN);
+ outen &= ~(1 << nr);
+ bgwrite(outen, BT848_GPIO_OUT_EN);
+
+ spin_unlock_irqrestore(&bg->lock, flags);
+
+ return 0;
+}
+
+static int bt8xxgpio_gpio_get(struct gpio_chip *gpio, unsigned nr)
+{
+ struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio);
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&bg->lock, flags);
+ val = bgread(BT848_GPIO_DATA);
+ spin_unlock_irqrestore(&bg->lock, flags);
+
+ return !!(val & (1 << nr));
+}
+
+static int bt8xxgpio_gpio_direction_output(struct gpio_chip *gpio,
+ unsigned nr, int val)
+{
+ struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio);
+ unsigned long flags;
+ u32 outen, data;
+
+ spin_lock_irqsave(&bg->lock, flags);
+
+ outen = bgread(BT848_GPIO_OUT_EN);
+ outen |= (1 << nr);
+ bgwrite(outen, BT848_GPIO_OUT_EN);
+
+ data = bgread(BT848_GPIO_DATA);
+ if (val)
+ data |= (1 << nr);
+ else
+ data &= ~(1 << nr);
+ bgwrite(data, BT848_GPIO_DATA);
+
+ spin_unlock_irqrestore(&bg->lock, flags);
+
+ return 0;
+}
+
+static void bt8xxgpio_gpio_set(struct gpio_chip *gpio,
+ unsigned nr, int val)
+{
+ struct bt8xxgpio *bg = container_of(gpio, struct bt8xxgpio, gpio);
+ unsigned long flags;
+ u32 data;
+
+ spin_lock_irqsave(&bg->lock, flags);
+
+ data = bgread(BT848_GPIO_DATA);
+ if (val)
+ data |= (1 << nr);
+ else
+ data &= ~(1 << nr);
+ bgwrite(data, BT848_GPIO_DATA);
+
+ spin_unlock_irqrestore(&bg->lock, flags);
+}
+
+static void bt8xxgpio_gpio_setup(struct bt8xxgpio *bg)
+{
+ struct gpio_chip *c = &bg->gpio;
+
+ c->label = bg->pdev->dev.bus_id;
+ c->owner = THIS_MODULE;
+ c->direction_input = bt8xxgpio_gpio_direction_input;
+ c->get = bt8xxgpio_gpio_get;
+ c->direction_output = bt8xxgpio_gpio_direction_output;
+ c->set = bt8xxgpio_gpio_set;
+ c->dbg_show = NULL;
+ c->base = modparam_gpiobase;
+ c->ngpio = BT8XXGPIO_NR_GPIOS;
+ c->can_sleep = 0;
+}
+
+static int bt8xxgpio_probe(struct pci_dev *dev,
+ const struct pci_device_id *pci_id)
+{
+ struct bt8xxgpio *bg;
+ int err;
+
+ bg = kzalloc(sizeof(*bg), GFP_KERNEL);
+ if (!bg)
+ return -ENOMEM;
+
+ bg->pdev = dev;
+ spin_lock_init(&bg->lock);
+
+ err = pci_enable_device(dev);
+ if (err) {
+ printk(KERN_ERR "bt8xxgpio: Can't enable device.\n");
+ goto err_freebg;
+ }
+ if (!request_mem_region(pci_resource_start(dev, 0),
+ pci_resource_len(dev, 0),
+ "bt8xxgpio")) {
+ printk(KERN_WARNING "bt8xxgpio: Can't request iomem (0x%llx).\n",
+ (unsigned long long)pci_resource_start(dev, 0));
+ err = -EBUSY;
+ goto err_disable;
+ }
+ pci_set_master(dev);
+ pci_set_drvdata(dev, bg);
+
+ bg->mmio = ioremap(pci_resource_start(dev, 0), 0x1000);
+ if (!bg->mmio) {
+ printk(KERN_ERR "bt8xxgpio: ioremap() failed\n");
+ err = -EIO;
+ goto err_release_mem;
+ }
+
+ /* Disable interrupts */
+ bgwrite(0, BT848_INT_MASK);
+
+ /* gpio init */
+ bgwrite(0, BT848_GPIO_DMA_CTL);
+ bgwrite(0, BT848_GPIO_REG_INP);
+ bgwrite(0, BT848_GPIO_OUT_EN);
+
+ bt8xxgpio_gpio_setup(bg);
+ err = gpiochip_add(&bg->gpio);
+ if (err) {
+ printk(KERN_ERR "bt8xxgpio: Failed to register GPIOs\n");
+ goto err_release_mem;
+ }
+
+ printk(KERN_INFO "bt8xxgpio: Abusing BT8xx card for GPIOs %d to %d\n",
+ bg->gpio.base, bg->gpio.base + BT8XXGPIO_NR_GPIOS - 1);
+
+ return 0;
+
+err_release_mem:
+ release_mem_region(pci_resource_start(dev, 0),
+ pci_resource_len(dev, 0));
+ pci_set_drvdata(dev, NULL);
+err_disable:
+ pci_disable_device(dev);
+err_freebg:
+ kfree(bg);
+
+ return err;
+}
+
+static void bt8xxgpio_remove(struct pci_dev *pdev)
+{
+ struct bt8xxgpio *bg = pci_get_drvdata(pdev);
+
+ gpiochip_remove(&bg->gpio);
+
+ bgwrite(0, BT848_INT_MASK);
+ bgwrite(~0x0, BT848_INT_STAT);
+ bgwrite(0x0, BT848_GPIO_OUT_EN);
+
+ iounmap(bg->mmio);
+ release_mem_region(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0));
+ pci_disable_device(pdev);
+
+ pci_set_drvdata(pdev, NULL);
+ kfree(bg);
+}
+
+#ifdef CONFIG_PM
+static int bt8xxgpio_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct bt8xxgpio *bg = pci_get_drvdata(pdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bg->lock, flags);
+
+ bg->saved_outen = bgread(BT848_GPIO_OUT_EN);
+ bg->saved_data = bgread(BT848_GPIO_DATA);
+
+ bgwrite(0, BT848_INT_MASK);
+ bgwrite(~0x0, BT848_INT_STAT);
+ bgwrite(0x0, BT848_GPIO_OUT_EN);
+
+ spin_unlock_irqrestore(&bg->lock, flags);
+
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+ return 0;
+}
+
+static int bt8xxgpio_resume(struct pci_dev *pdev)
+{
+ struct bt8xxgpio *bg = pci_get_drvdata(pdev);
+ unsigned long flags;
+ int err;
+
+ pci_set_power_state(pdev, 0);
+ err = pci_enable_device(pdev);
+ if (err)
+ return err;
+ pci_restore_state(pdev);
+
+ spin_lock_irqsave(&bg->lock, flags);
+
+ bgwrite(0, BT848_INT_MASK);
+ bgwrite(0, BT848_GPIO_DMA_CTL);
+ bgwrite(0, BT848_GPIO_REG_INP);
+ bgwrite(bg->saved_outen, BT848_GPIO_OUT_EN);
+ bgwrite(bg->saved_data & bg->saved_outen,
+ BT848_GPIO_DATA);
+
+ spin_unlock_irqrestore(&bg->lock, flags);
+
+ return 0;
+}
+#else
+#define bt8xxgpio_suspend NULL
+#define bt8xxgpio_resume NULL
+#endif /* CONFIG_PM */
+
+static struct pci_device_id bt8xxgpio_pci_tbl[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879) },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, bt8xxgpio_pci_tbl);
+
+static struct pci_driver bt8xxgpio_pci_driver = {
+ .name = "bt8xxgpio",
+ .id_table = bt8xxgpio_pci_tbl,
+ .probe = bt8xxgpio_probe,
+ .remove = bt8xxgpio_remove,
+ .suspend = bt8xxgpio_suspend,
+ .resume = bt8xxgpio_resume,
+};
+
+static int bt8xxgpio_init(void)
+{
+ return pci_register_driver(&bt8xxgpio_pci_driver);
+}
+module_init(bt8xxgpio_init)
+
+static void bt8xxgpio_exit(void)
+{
+ pci_unregister_driver(&bt8xxgpio_pci_driver);
+}
+module_exit(bt8xxgpio_exit)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michael Buesch");
+MODULE_DESCRIPTION("Abuse a BT8xx framegrabber card as generic GPIO card");
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index beaf6b3a37d..8d2940517c9 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2,8 +2,11 @@
#include <linux/module.h>
#include <linux/irq.h>
#include <linux/spinlock.h>
-
-#include <asm/gpio.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/gpio.h>
/* Optional implementation infrastructure for GPIO interfaces.
@@ -44,6 +47,8 @@ struct gpio_desc {
#define FLAG_REQUESTED 0
#define FLAG_IS_OUT 1
#define FLAG_RESERVED 2
+#define FLAG_EXPORT 3 /* protected by sysfs_lock */
+#define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */
#ifdef CONFIG_DEBUG_FS
const char *label;
@@ -151,6 +156,482 @@ err:
return ret;
}
+#ifdef CONFIG_GPIO_SYSFS
+
+/* lock protects against unexport_gpio() being called while
+ * sysfs files are active.
+ */
+static DEFINE_MUTEX(sysfs_lock);
+
+/*
+ * /sys/class/gpio/gpioN... only for GPIOs that are exported
+ * /direction
+ * * MAY BE OMITTED if kernel won't allow direction changes
+ * * is read/write as "in" or "out"
+ * * may also be written as "high" or "low", initializing
+ * output value as specified ("out" implies "low")
+ * /value
+ * * always readable, subject to hardware behavior
+ * * may be writable, as zero/nonzero
+ *
+ * REVISIT there will likely be an attribute for configuring async
+ * notifications, e.g. to specify polling interval or IRQ trigger type
+ * that would for example trigger a poll() on the "value".
+ */
+
+static ssize_t gpio_direction_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_desc *desc = dev_get_drvdata(dev);
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(FLAG_EXPORT, &desc->flags))
+ status = -EIO;
+ else
+ status = sprintf(buf, "%s\n",
+ test_bit(FLAG_IS_OUT, &desc->flags)
+ ? "out" : "in");
+
+ mutex_unlock(&sysfs_lock);
+ return status;
+}
+
+static ssize_t gpio_direction_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ const struct gpio_desc *desc = dev_get_drvdata(dev);
+ unsigned gpio = desc - gpio_desc;
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(FLAG_EXPORT, &desc->flags))
+ status = -EIO;
+ else if (sysfs_streq(buf, "high"))
+ status = gpio_direction_output(gpio, 1);
+ else if (sysfs_streq(buf, "out") || sysfs_streq(buf, "low"))
+ status = gpio_direction_output(gpio, 0);
+ else if (sysfs_streq(buf, "in"))
+ status = gpio_direction_input(gpio);
+ else
+ status = -EINVAL;
+
+ mutex_unlock(&sysfs_lock);
+ return status ? : size;
+}
+
+static const DEVICE_ATTR(direction, 0644,
+ gpio_direction_show, gpio_direction_store);
+
+static ssize_t gpio_value_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_desc *desc = dev_get_drvdata(dev);
+ unsigned gpio = desc - gpio_desc;
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(FLAG_EXPORT, &desc->flags))
+ status = -EIO;
+ else
+ status = sprintf(buf, "%d\n", gpio_get_value_cansleep(gpio));
+
+ mutex_unlock(&sysfs_lock);
+ return status;
+}
+
+static ssize_t gpio_value_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ const struct gpio_desc *desc = dev_get_drvdata(dev);
+ unsigned gpio = desc - gpio_desc;
+ ssize_t status;
+
+ mutex_lock(&sysfs_lock);
+
+ if (!test_bit(FLAG_EXPORT, &desc->flags))
+ status = -EIO;
+ else if (!test_bit(FLAG_IS_OUT, &desc->flags))
+ status = -EPERM;
+ else {
+ long value;
+
+ status = strict_strtol(buf, 0, &value);
+ if (status == 0) {
+ gpio_set_value_cansleep(gpio, value != 0);
+ status = size;
+ }
+ }
+
+ mutex_unlock(&sysfs_lock);
+ return status;
+}
+
+static /*const*/ DEVICE_ATTR(value, 0644,
+ gpio_value_show, gpio_value_store);
+
+static const struct attribute *gpio_attrs[] = {
+ &dev_attr_direction.attr,
+ &dev_attr_value.attr,
+ NULL,
+};
+
+static const struct attribute_group gpio_attr_group = {
+ .attrs = (struct attribute **) gpio_attrs,
+};
+
+/*
+ * /sys/class/gpio/gpiochipN/
+ * /base ... matching gpio_chip.base (N)
+ * /label ... matching gpio_chip.label
+ * /ngpio ... matching gpio_chip.ngpio
+ */
+
+static ssize_t chip_base_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_chip *chip = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", chip->base);
+}
+static DEVICE_ATTR(base, 0444, chip_base_show, NULL);
+
+static ssize_t chip_label_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_chip *chip = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", chip->label ? : "");
+}
+static DEVICE_ATTR(label, 0444, chip_label_show, NULL);
+
+static ssize_t chip_ngpio_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ const struct gpio_chip *chip = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", chip->ngpio);
+}
+static DEVICE_ATTR(ngpio, 0444, chip_ngpio_show, NULL);
+
+static const struct attribute *gpiochip_attrs[] = {
+ &dev_attr_base.attr,
+ &dev_attr_label.attr,
+ &dev_attr_ngpio.attr,
+ NULL,
+};
+
+static const struct attribute_group gpiochip_attr_group = {
+ .attrs = (struct attribute **) gpiochip_attrs,
+};
+
+/*
+ * /sys/class/gpio/export ... write-only
+ * integer N ... number of GPIO to export (full access)
+ * /sys/class/gpio/unexport ... write-only
+ * integer N ... number of GPIO to unexport
+ */
+static ssize_t export_store(struct class *class, const char *buf, size_t len)
+{
+ long gpio;
+ int status;
+
+ status = strict_strtol(buf, 0, &gpio);
+ if (status < 0)
+ goto done;
+
+ /* No extra locking here; FLAG_SYSFS just signifies that the
+ * request and export were done by on behalf of userspace, so
+ * they may be undone on its behalf too.
+ */
+
+ status = gpio_request(gpio, "sysfs");
+ if (status < 0)
+ goto done;
+
+ status = gpio_export(gpio, true);
+ if (status < 0)
+ gpio_free(gpio);
+ else
+ set_bit(FLAG_SYSFS, &gpio_desc[gpio].flags);
+
+done:
+ if (status)
+ pr_debug("%s: status %d\n", __func__, status);
+ return status ? : len;
+}
+
+static ssize_t unexport_store(struct class *class, const char *buf, size_t len)
+{
+ long gpio;
+ int status;
+
+ status = strict_strtol(buf, 0, &gpio);
+ if (status < 0)
+ goto done;
+
+ status = -EINVAL;
+
+ /* reject bogus commands (gpio_unexport ignores them) */
+ if (!gpio_is_valid(gpio))
+ goto done;
+
+ /* No extra locking here; FLAG_SYSFS just signifies that the
+ * request and export were done by on behalf of userspace, so
+ * they may be undone on its behalf too.
+ */
+ if (test_and_clear_bit(FLAG_SYSFS, &gpio_desc[gpio].flags)) {
+ status = 0;
+ gpio_free(gpio);
+ }
+done:
+ if (status)
+ pr_debug("%s: status %d\n", __func__, status);
+ return status ? : len;
+}
+
+static struct class_attribute gpio_class_attrs[] = {
+ __ATTR(export, 0200, NULL, export_store),
+ __ATTR(unexport, 0200, NULL, unexport_store),
+ __ATTR_NULL,
+};
+
+static struct class gpio_class = {
+ .name = "gpio",
+ .owner = THIS_MODULE,
+
+ .class_attrs = gpio_class_attrs,
+};
+
+
+/**
+ * gpio_export - export a GPIO through sysfs
+ * @gpio: gpio to make available, already requested
+ * @direction_may_change: true if userspace may change gpio direction
+ * Context: arch_initcall or later
+ *
+ * When drivers want to make a GPIO accessible to userspace after they
+ * have requested it -- perhaps while debugging, or as part of their
+ * public interface -- they may use this routine. If the GPIO can
+ * change direction (some can't) and the caller allows it, userspace
+ * will see "direction" sysfs attribute which may be used to change
+ * the gpio's direction. A "value" attribute will always be provided.
+ *
+ * Returns zero on success, else an error.
+ */
+int gpio_export(unsigned gpio, bool direction_may_change)
+{
+ unsigned long flags;
+ struct gpio_desc *desc;
+ int status = -EINVAL;
+
+ /* can't export until sysfs is available ... */
+ if (!gpio_class.p) {
+ pr_debug("%s: called too early!\n", __func__);
+ return -ENOENT;
+ }
+
+ if (!gpio_is_valid(gpio))
+ goto done;
+
+ mutex_lock(&sysfs_lock);
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ desc = &gpio_desc[gpio];
+ if (test_bit(FLAG_REQUESTED, &desc->flags)
+ && !test_bit(FLAG_EXPORT, &desc->flags)) {
+ status = 0;
+ if (!desc->chip->direction_input
+ || !desc->chip->direction_output)
+ direction_may_change = false;
+ }
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ if (status == 0) {
+ struct device *dev;
+
+ dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
+ desc, "gpio%d", gpio);
+ if (dev) {
+ if (direction_may_change)
+ status = sysfs_create_group(&dev->kobj,
+ &gpio_attr_group);
+ else
+ status = device_create_file(dev,
+ &dev_attr_value);
+ if (status != 0)
+ device_unregister(dev);
+ } else
+ status = -ENODEV;
+ if (status == 0)
+ set_bit(FLAG_EXPORT, &desc->flags);
+ }
+
+ mutex_unlock(&sysfs_lock);
+
+done:
+ if (status)
+ pr_debug("%s: gpio%d status %d\n", __func__, gpio, status);
+
+ return status;
+}
+EXPORT_SYMBOL_GPL(gpio_export);
+
+static int match_export(struct device *dev, void *data)
+{
+ return dev_get_drvdata(dev) == data;
+}
+
+/**
+ * gpio_unexport - reverse effect of gpio_export()
+ * @gpio: gpio to make unavailable
+ *
+ * This is implicit on gpio_free().
+ */
+void gpio_unexport(unsigned gpio)
+{
+ struct gpio_desc *desc;
+ int status = -EINVAL;
+
+ if (!gpio_is_valid(gpio))
+ goto done;
+
+ mutex_lock(&sysfs_lock);
+
+ desc = &gpio_desc[gpio];
+ if (test_bit(FLAG_EXPORT, &desc->flags)) {
+ struct device *dev = NULL;
+
+ dev = class_find_device(&gpio_class, NULL, desc, match_export);
+ if (dev) {
+ clear_bit(FLAG_EXPORT, &desc->flags);
+ put_device(dev);
+ device_unregister(dev);
+ status = 0;
+ } else
+ status = -ENODEV;
+ }
+
+ mutex_unlock(&sysfs_lock);
+done:
+ if (status)
+ pr_debug("%s: gpio%d status %d\n", __func__, gpio, status);
+}
+EXPORT_SYMBOL_GPL(gpio_unexport);
+
+static int gpiochip_export(struct gpio_chip *chip)
+{
+ int status;
+ struct device *dev;
+
+ /* Many systems register gpio chips for SOC support very early,
+ * before driver model support is available. In those cases we
+ * export this later, in gpiolib_sysfs_init() ... here we just
+ * verify that _some_ field of gpio_class got initialized.
+ */
+ if (!gpio_class.p)
+ return 0;
+
+ /* use chip->base for the ID; it's already known to be unique */
+ mutex_lock(&sysfs_lock);
+ dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0), chip,
+ "gpiochip%d", chip->base);
+ if (dev) {
+ status = sysfs_create_group(&dev->kobj,
+ &gpiochip_attr_group);
+ } else
+ status = -ENODEV;
+ chip->exported = (status == 0);
+ mutex_unlock(&sysfs_lock);
+
+ if (status) {
+ unsigned long flags;
+ unsigned gpio;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ gpio = chip->base;
+ while (gpio_desc[gpio].chip == chip)
+ gpio_desc[gpio++].chip = NULL;
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ pr_debug("%s: chip %s status %d\n", __func__,
+ chip->label, status);
+ }
+
+ return status;
+}
+
+static void gpiochip_unexport(struct gpio_chip *chip)
+{
+ int status;
+ struct device *dev;
+
+ mutex_lock(&sysfs_lock);
+ dev = class_find_device(&gpio_class, NULL, chip, match_export);
+ if (dev) {
+ put_device(dev);
+ device_unregister(dev);
+ chip->exported = 0;
+ status = 0;
+ } else
+ status = -ENODEV;
+ mutex_unlock(&sysfs_lock);
+
+ if (status)
+ pr_debug("%s: chip %s status %d\n", __func__,
+ chip->label, status);
+}
+
+static int __init gpiolib_sysfs_init(void)
+{
+ int status;
+ unsigned long flags;
+ unsigned gpio;
+
+ status = class_register(&gpio_class);
+ if (status < 0)
+ return status;
+
+ /* Scan and register the gpio_chips which registered very
+ * early (e.g. before the class_register above was called).
+ *
+ * We run before arch_initcall() so chip->dev nodes can have
+ * registered, and so arch_initcall() can always gpio_export().
+ */
+ spin_lock_irqsave(&gpio_lock, flags);
+ for (gpio = 0; gpio < ARCH_NR_GPIOS; gpio++) {
+ struct gpio_chip *chip;
+
+ chip = gpio_desc[gpio].chip;
+ if (!chip || chip->exported)
+ continue;
+
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ status = gpiochip_export(chip);
+ spin_lock_irqsave(&gpio_lock, flags);
+ }
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+
+ return status;
+}
+postcore_initcall(gpiolib_sysfs_init);
+
+#else
+static inline int gpiochip_export(struct gpio_chip *chip)
+{
+ return 0;
+}
+
+static inline void gpiochip_unexport(struct gpio_chip *chip)
+{
+}
+
+#endif /* CONFIG_GPIO_SYSFS */
+
/**
* gpiochip_add() - register a gpio_chip
* @chip: the chip to register, with chip->base initialized
@@ -160,6 +641,11 @@ err:
* because the chip->base is invalid or already associated with a
* different chip. Otherwise it returns zero as a success code.
*
+ * When gpiochip_add() is called very early during boot, so that GPIOs
+ * can be freely used, the chip->dev device must be registered before
+ * the gpio framework's arch_initcall(). Otherwise sysfs initialization
+ * for GPIOs will fail rudely.
+ *
* If chip->base is negative, this requests dynamic assignment of
* a range of valid GPIOs.
*/
@@ -182,7 +668,7 @@ int gpiochip_add(struct gpio_chip *chip)
base = gpiochip_find_base(chip->ngpio);
if (base < 0) {
status = base;
- goto fail_unlock;
+ goto unlock;
}
chip->base = base;
}
@@ -197,12 +683,23 @@ int gpiochip_add(struct gpio_chip *chip)
if (status == 0) {
for (id = base; id < base + chip->ngpio; id++) {
gpio_desc[id].chip = chip;
- gpio_desc[id].flags = 0;
+
+ /* REVISIT: most hardware initializes GPIOs as
+ * inputs (often with pullups enabled) so power
+ * usage is minimized. Linux code should set the
+ * gpio direction first thing; but until it does,
+ * we may expose the wrong direction in sysfs.
+ */
+ gpio_desc[id].flags = !chip->direction_input
+ ? (1 << FLAG_IS_OUT)
+ : 0;
}
}
-fail_unlock:
+unlock:
spin_unlock_irqrestore(&gpio_lock, flags);
+ if (status == 0)
+ status = gpiochip_export(chip);
fail:
/* failures here can mean systems won't boot... */
if (status)
@@ -239,6 +736,10 @@ int gpiochip_remove(struct gpio_chip *chip)
}
spin_unlock_irqrestore(&gpio_lock, flags);
+
+ if (status == 0)
+ gpiochip_unexport(chip);
+
return status;
}
EXPORT_SYMBOL_GPL(gpiochip_remove);
@@ -296,6 +797,8 @@ void gpio_free(unsigned gpio)
return;
}
+ gpio_unexport(gpio);
+
spin_lock_irqsave(&gpio_lock, flags);
desc = &gpio_desc[gpio];
@@ -534,10 +1037,6 @@ EXPORT_SYMBOL_GPL(gpio_set_value_cansleep);
#ifdef CONFIG_DEBUG_FS
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-
-
static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
unsigned i;
@@ -614,17 +1113,28 @@ static int gpiolib_show(struct seq_file *s, void *unused)
/* REVISIT this isn't locked against gpio_chip removal ... */
for (gpio = 0; gpio_is_valid(gpio); gpio++) {
+ struct device *dev;
+
if (chip == gpio_desc[gpio].chip)
continue;
chip = gpio_desc[gpio].chip;
if (!chip)
continue;
- seq_printf(s, "%sGPIOs %d-%d, %s%s:\n",
+ seq_printf(s, "%sGPIOs %d-%d",
started ? "\n" : "",
- chip->base, chip->base + chip->ngpio - 1,
- chip->label ? : "generic",
- chip->can_sleep ? ", can sleep" : "");
+ chip->base, chip->base + chip->ngpio - 1);
+ dev = chip->dev;
+ if (dev)
+ seq_printf(s, ", %s/%s",
+ dev->bus ? dev->bus->name : "no-bus",
+ dev->bus_id);
+ if (chip->label)
+ seq_printf(s, ", %s", chip->label);
+ if (chip->can_sleep)
+ seq_printf(s, ", can sleep");
+ seq_printf(s, ":\n");
+
started = 1;
if (chip->dbg_show)
chip->dbg_show(s, chip);
diff --git a/drivers/gpio/max732x.c b/drivers/gpio/max732x.c
new file mode 100644
index 00000000000..b51c8135ca2
--- /dev/null
+++ b/drivers/gpio/max732x.c
@@ -0,0 +1,385 @@
+/*
+ * max732x.c - I2C Port Expander with 8/16 I/O
+ *
+ * Copyright (C) 2007 Marvell International Ltd.
+ * Copyright (C) 2008 Jack Ren <jack.ren@marvell.com>
+ * Copyright (C) 2008 Eric Miao <eric.miao@marvell.com>
+ *
+ * Derived from drivers/gpio/pca953x.c
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/gpio.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c/max732x.h>
+
+
+/*
+ * Each port of MAX732x (including MAX7319) falls into one of the
+ * following three types:
+ *
+ * - Push Pull Output
+ * - Input
+ * - Open Drain I/O
+ *
+ * designated by 'O', 'I' and 'P' individually according to MAXIM's
+ * datasheets.
+ *
+ * There are two groups of I/O ports, each group usually includes
+ * up to 8 I/O ports, and is accessed by a specific I2C address:
+ *
+ * - Group A : by I2C address 0b'110xxxx
+ * - Group B : by I2C address 0b'101xxxx
+ *
+ * where 'xxxx' is decided by the connections of pin AD2/AD0. The
+ * address used also affects the initial state of output signals.
+ *
+ * Within each group of ports, there are five known combinations of
+ * I/O ports: 4I4O, 4P4O, 8I, 8P, 8O, see the definitions below for
+ * the detailed organization of these ports.
+ *
+ * GPIO numbers start from 'gpio_base + 0' to 'gpio_base + 8/16',
+ * and GPIOs from GROUP_A are numbered before those from GROUP_B
+ * (if there are two groups).
+ *
+ * NOTE: MAX7328/MAX7329 are drop-in replacements for PCF8574/a, so
+ * they are not supported by this driver.
+ */
+
+#define PORT_NONE 0x0 /* '/' No Port */
+#define PORT_OUTPUT 0x1 /* 'O' Push-Pull, Output Only */
+#define PORT_INPUT 0x2 /* 'I' Input Only */
+#define PORT_OPENDRAIN 0x3 /* 'P' Open-Drain, I/O */
+
+#define IO_4I4O 0x5AA5 /* O7 O6 I5 I4 I3 I2 O1 O0 */
+#define IO_4P4O 0x5FF5 /* O7 O6 P5 P4 P3 P2 O1 O0 */
+#define IO_8I 0xAAAA /* I7 I6 I5 I4 I3 I2 I1 I0 */
+#define IO_8P 0xFFFF /* P7 P6 P5 P4 P3 P2 P1 P0 */
+#define IO_8O 0x5555 /* O7 O6 O5 O4 O3 O2 O1 O0 */
+
+#define GROUP_A(x) ((x) & 0xffff) /* I2C Addr: 0b'110xxxx */
+#define GROUP_B(x) ((x) << 16) /* I2C Addr: 0b'101xxxx */
+
+static const struct i2c_device_id max732x_id[] = {
+ { "max7319", GROUP_A(IO_8I) },
+ { "max7320", GROUP_B(IO_8O) },
+ { "max7321", GROUP_A(IO_8P) },
+ { "max7322", GROUP_A(IO_4I4O) },
+ { "max7323", GROUP_A(IO_4P4O) },
+ { "max7324", GROUP_A(IO_8I) | GROUP_B(IO_8O) },
+ { "max7325", GROUP_A(IO_8P) | GROUP_B(IO_8O) },
+ { "max7326", GROUP_A(IO_4I4O) | GROUP_B(IO_8O) },
+ { "max7327", GROUP_A(IO_4P4O) | GROUP_B(IO_8O) },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, max732x_id);
+
+struct max732x_chip {
+ struct gpio_chip gpio_chip;
+
+ struct i2c_client *client; /* "main" client */
+ struct i2c_client *client_dummy;
+ struct i2c_client *client_group_a;
+ struct i2c_client *client_group_b;
+
+ unsigned int mask_group_a;
+ unsigned int dir_input;
+ unsigned int dir_output;
+
+ struct mutex lock;
+ uint8_t reg_out[2];
+};
+
+static int max732x_write(struct max732x_chip *chip, int group_a, uint8_t val)
+{
+ struct i2c_client *client;
+ int ret;
+
+ client = group_a ? chip->client_group_a : chip->client_group_b;
+ ret = i2c_smbus_write_byte(client, val);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed writing\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max732x_read(struct max732x_chip *chip, int group_a, uint8_t *val)
+{
+ struct i2c_client *client;
+ int ret;
+
+ client = group_a ? chip->client_group_a : chip->client_group_b;
+ ret = i2c_smbus_read_byte(client);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed reading\n");
+ return ret;
+ }
+
+ *val = (uint8_t)ret;
+ return 0;
+}
+
+static inline int is_group_a(struct max732x_chip *chip, unsigned off)
+{
+ return (1u << off) & chip->mask_group_a;
+}
+
+static int max732x_gpio_get_value(struct gpio_chip *gc, unsigned off)
+{
+ struct max732x_chip *chip;
+ uint8_t reg_val;
+ int ret;
+
+ chip = container_of(gc, struct max732x_chip, gpio_chip);
+
+ ret = max732x_read(chip, is_group_a(chip, off), &reg_val);
+ if (ret < 0)
+ return 0;
+
+ return reg_val & (1u << (off & 0x7));
+}
+
+static void max732x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
+{
+ struct max732x_chip *chip;
+ uint8_t reg_out, mask = 1u << (off & 0x7);
+ int ret;
+
+ chip = container_of(gc, struct max732x_chip, gpio_chip);
+
+ mutex_lock(&chip->lock);
+
+ reg_out = (off > 7) ? chip->reg_out[1] : chip->reg_out[0];
+ reg_out = (val) ? reg_out | mask : reg_out & ~mask;
+
+ ret = max732x_write(chip, is_group_a(chip, off), reg_out);
+ if (ret < 0)
+ goto out;
+
+ /* update the shadow register then */
+ if (off > 7)
+ chip->reg_out[1] = reg_out;
+ else
+ chip->reg_out[0] = reg_out;
+out:
+ mutex_unlock(&chip->lock);
+}
+
+static int max732x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
+{
+ struct max732x_chip *chip;
+ unsigned int mask = 1u << off;
+
+ chip = container_of(gc, struct max732x_chip, gpio_chip);
+
+ if ((mask & chip->dir_input) == 0) {
+ dev_dbg(&chip->client->dev, "%s port %d is output only\n",
+ chip->client->name, off);
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+static int max732x_gpio_direction_output(struct gpio_chip *gc,
+ unsigned off, int val)
+{
+ struct max732x_chip *chip;
+ unsigned int mask = 1u << off;
+
+ chip = container_of(gc, struct max732x_chip, gpio_chip);
+
+ if ((mask & chip->dir_output) == 0) {
+ dev_dbg(&chip->client->dev, "%s port %d is input only\n",
+ chip->client->name, off);
+ return -EACCES;
+ }
+
+ max732x_gpio_set_value(gc, off, val);
+ return 0;
+}
+
+static int __devinit max732x_setup_gpio(struct max732x_chip *chip,
+ const struct i2c_device_id *id,
+ unsigned gpio_start)
+{
+ struct gpio_chip *gc = &chip->gpio_chip;
+ uint32_t id_data = id->driver_data;
+ int i, port = 0;
+
+ for (i = 0; i < 16; i++, id_data >>= 2) {
+ unsigned int mask = 1 << port;
+
+ switch (id_data & 0x3) {
+ case PORT_OUTPUT:
+ chip->dir_output |= mask;
+ break;
+ case PORT_INPUT:
+ chip->dir_input |= mask;
+ break;
+ case PORT_OPENDRAIN:
+ chip->dir_output |= mask;
+ chip->dir_input |= mask;
+ break;
+ default:
+ continue;
+ }
+
+ if (i < 8)
+ chip->mask_group_a |= mask;
+ port++;
+ }
+
+ if (chip->dir_input)
+ gc->direction_input = max732x_gpio_direction_input;
+ if (chip->dir_output) {
+ gc->direction_output = max732x_gpio_direction_output;
+ gc->set = max732x_gpio_set_value;
+ }
+ gc->get = max732x_gpio_get_value;
+ gc->can_sleep = 1;
+
+ gc->base = gpio_start;
+ gc->ngpio = port;
+ gc->label = chip->client->name;
+ gc->owner = THIS_MODULE;
+
+ return port;
+}
+
+static int __devinit max732x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max732x_platform_data *pdata;
+ struct max732x_chip *chip;
+ struct i2c_client *c;
+ uint16_t addr_a, addr_b;
+ int ret, nr_port;
+
+ pdata = client->dev.platform_data;
+ if (pdata == NULL)
+ return -ENODEV;
+
+ chip = kzalloc(sizeof(struct max732x_chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+ chip->client = client;
+
+ nr_port = max732x_setup_gpio(chip, id, pdata->gpio_base);
+
+ addr_a = (client->addr & 0x0f) | 0x60;
+ addr_b = (client->addr & 0x0f) | 0x50;
+
+ switch (client->addr & 0x70) {
+ case 0x60:
+ chip->client_group_a = client;
+ if (nr_port > 7) {
+ c = i2c_new_dummy(client->adapter, addr_b);
+ chip->client_group_b = chip->client_dummy = c;
+ }
+ break;
+ case 0x50:
+ chip->client_group_b = client;
+ if (nr_port > 7) {
+ c = i2c_new_dummy(client->adapter, addr_a);
+ chip->client_group_a = chip->client_dummy = c;
+ }
+ break;
+ default:
+ dev_err(&client->dev, "invalid I2C address specified %02x\n",
+ client->addr);
+ ret = -EINVAL;
+ goto out_failed;
+ }
+
+ mutex_init(&chip->lock);
+
+ max732x_read(chip, is_group_a(chip, 0), &chip->reg_out[0]);
+ if (nr_port > 7)
+ max732x_read(chip, is_group_a(chip, 8), &chip->reg_out[1]);
+
+ ret = gpiochip_add(&chip->gpio_chip);
+ if (ret)
+ goto out_failed;
+
+ if (pdata->setup) {
+ ret = pdata->setup(client, chip->gpio_chip.base,
+ chip->gpio_chip.ngpio, pdata->context);
+ if (ret < 0)
+ dev_warn(&client->dev, "setup failed, %d\n", ret);
+ }
+
+ i2c_set_clientdata(client, chip);
+ return 0;
+
+out_failed:
+ kfree(chip);
+ return ret;
+}
+
+static int __devexit max732x_remove(struct i2c_client *client)
+{
+ struct max732x_platform_data *pdata = client->dev.platform_data;
+ struct max732x_chip *chip = i2c_get_clientdata(client);
+ int ret;
+
+ if (pdata->teardown) {
+ ret = pdata->teardown(client, chip->gpio_chip.base,
+ chip->gpio_chip.ngpio, pdata->context);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s failed, %d\n",
+ "teardown", ret);
+ return ret;
+ }
+ }
+
+ ret = gpiochip_remove(&chip->gpio_chip);
+ if (ret) {
+ dev_err(&client->dev, "%s failed, %d\n",
+ "gpiochip_remove()", ret);
+ return ret;
+ }
+
+ /* unregister any dummy i2c_client */
+ if (chip->client_dummy)
+ i2c_unregister_device(chip->client_dummy);
+
+ kfree(chip);
+ return 0;
+}
+
+static struct i2c_driver max732x_driver = {
+ .driver = {
+ .name = "max732x",
+ .owner = THIS_MODULE,
+ },
+ .probe = max732x_probe,
+ .remove = __devexit_p(max732x_remove),
+ .id_table = max732x_id,
+};
+
+static int __init max732x_init(void)
+{
+ return i2c_add_driver(&max732x_driver);
+}
+module_init(max732x_init);
+
+static void __exit max732x_exit(void)
+{
+ i2c_del_driver(&max732x_driver);
+}
+module_exit(max732x_exit);
+
+MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
+MODULE_DESCRIPTION("GPIO expander driver for MAX732X");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/mcp23s08.c b/drivers/gpio/mcp23s08.c
index 7f92fdd5f0e..8a1b405fefd 100644
--- a/drivers/gpio/mcp23s08.c
+++ b/drivers/gpio/mcp23s08.c
@@ -40,15 +40,26 @@ struct mcp23s08 {
struct spi_device *spi;
u8 addr;
+ u8 cache[11];
/* lock protects the cached values */
struct mutex lock;
- u8 cache[11];
struct gpio_chip chip;
struct work_struct work;
};
+/* A given spi_device can represent up to four mcp23s08 chips
+ * sharing the same chipselect but using different addresses
+ * (e.g. chips #0 and #3 might be populated, but not #1 or $2).
+ * Driver data holds all the per-chip data.
+ */
+struct mcp23s08_driver_data {
+ unsigned ngpio;
+ struct mcp23s08 *mcp[4];
+ struct mcp23s08 chip[];
+};
+
static int mcp23s08_read(struct mcp23s08 *mcp, unsigned reg)
{
u8 tx[2], rx[1];
@@ -208,25 +219,18 @@ done:
/*----------------------------------------------------------------------*/
-static int mcp23s08_probe(struct spi_device *spi)
+static int mcp23s08_probe_one(struct spi_device *spi, unsigned addr,
+ unsigned base, unsigned pullups)
{
- struct mcp23s08 *mcp;
- struct mcp23s08_platform_data *pdata;
+ struct mcp23s08_driver_data *data = spi_get_drvdata(spi);
+ struct mcp23s08 *mcp = data->mcp[addr];
int status;
int do_update = 0;
- pdata = spi->dev.platform_data;
- if (!pdata || pdata->slave > 3 || !pdata->base)
- return -ENODEV;
-
- mcp = kzalloc(sizeof *mcp, GFP_KERNEL);
- if (!mcp)
- return -ENOMEM;
-
mutex_init(&mcp->lock);
mcp->spi = spi;
- mcp->addr = 0x40 | (pdata->slave << 1);
+ mcp->addr = 0x40 | (addr << 1);
mcp->chip.label = "mcp23s08",
@@ -236,26 +240,28 @@ static int mcp23s08_probe(struct spi_device *spi)
mcp->chip.set = mcp23s08_set;
mcp->chip.dbg_show = mcp23s08_dbg_show;
- mcp->chip.base = pdata->base;
+ mcp->chip.base = base;
mcp->chip.ngpio = 8;
mcp->chip.can_sleep = 1;
+ mcp->chip.dev = &spi->dev;
mcp->chip.owner = THIS_MODULE;
- spi_set_drvdata(spi, mcp);
-
- /* verify MCP_IOCON.SEQOP = 0, so sequential reads work */
+ /* verify MCP_IOCON.SEQOP = 0, so sequential reads work,
+ * and MCP_IOCON.HAEN = 1, so we work with all chips.
+ */
status = mcp23s08_read(mcp, MCP_IOCON);
if (status < 0)
goto fail;
- if (status & IOCON_SEQOP) {
+ if ((status & IOCON_SEQOP) || !(status & IOCON_HAEN)) {
status &= ~IOCON_SEQOP;
+ status |= IOCON_HAEN;
status = mcp23s08_write(mcp, MCP_IOCON, (u8) status);
if (status < 0)
goto fail;
}
/* configure ~100K pullups */
- status = mcp23s08_write(mcp, MCP_GPPU, pdata->pullups);
+ status = mcp23s08_write(mcp, MCP_GPPU, pullups);
if (status < 0)
goto fail;
@@ -282,11 +288,58 @@ static int mcp23s08_probe(struct spi_device *spi)
tx[1] = MCP_IPOL;
memcpy(&tx[2], &mcp->cache[MCP_IPOL], sizeof(tx) - 2);
status = spi_write_then_read(mcp->spi, tx, sizeof tx, NULL, 0);
-
- /* FIXME check status... */
+ if (status < 0)
+ goto fail;
}
status = gpiochip_add(&mcp->chip);
+fail:
+ if (status < 0)
+ dev_dbg(&spi->dev, "can't setup chip %d, --> %d\n",
+ addr, status);
+ return status;
+}
+
+static int mcp23s08_probe(struct spi_device *spi)
+{
+ struct mcp23s08_platform_data *pdata;
+ unsigned addr;
+ unsigned chips = 0;
+ struct mcp23s08_driver_data *data;
+ int status;
+ unsigned base;
+
+ pdata = spi->dev.platform_data;
+ if (!pdata || !gpio_is_valid(pdata->base))
+ return -ENODEV;
+
+ for (addr = 0; addr < 4; addr++) {
+ if (!pdata->chip[addr].is_present)
+ continue;
+ chips++;
+ }
+ if (!chips)
+ return -ENODEV;
+
+ data = kzalloc(sizeof *data + chips * sizeof(struct mcp23s08),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ spi_set_drvdata(spi, data);
+
+ base = pdata->base;
+ for (addr = 0; addr < 4; addr++) {
+ if (!pdata->chip[addr].is_present)
+ continue;
+ chips--;
+ data->mcp[addr] = &data->chip[chips];
+ status = mcp23s08_probe_one(spi, addr, base,
+ pdata->chip[addr].pullups);
+ if (status < 0)
+ goto fail;
+ base += 8;
+ }
+ data->ngpio = base - pdata->base;
/* NOTE: these chips have a relatively sane IRQ framework, with
* per-signal masking and level/edge triggering. It's not yet
@@ -294,8 +347,9 @@ static int mcp23s08_probe(struct spi_device *spi)
*/
if (pdata->setup) {
- status = pdata->setup(spi, mcp->chip.base,
- mcp->chip.ngpio, pdata->context);
+ status = pdata->setup(spi,
+ pdata->base, data->ngpio,
+ pdata->context);
if (status < 0)
dev_dbg(&spi->dev, "setup --> %d\n", status);
}
@@ -303,19 +357,29 @@ static int mcp23s08_probe(struct spi_device *spi)
return 0;
fail:
- kfree(mcp);
+ for (addr = 0; addr < 4; addr++) {
+ int tmp;
+
+ if (!data->mcp[addr])
+ continue;
+ tmp = gpiochip_remove(&data->mcp[addr]->chip);
+ if (tmp < 0)
+ dev_err(&spi->dev, "%s --> %d\n", "remove", tmp);
+ }
+ kfree(data);
return status;
}
static int mcp23s08_remove(struct spi_device *spi)
{
- struct mcp23s08 *mcp = spi_get_drvdata(spi);
+ struct mcp23s08_driver_data *data = spi_get_drvdata(spi);
struct mcp23s08_platform_data *pdata = spi->dev.platform_data;
+ unsigned addr;
int status = 0;
if (pdata->teardown) {
status = pdata->teardown(spi,
- mcp->chip.base, mcp->chip.ngpio,
+ pdata->base, data->ngpio,
pdata->context);
if (status < 0) {
dev_err(&spi->dev, "%s --> %d\n", "teardown", status);
@@ -323,11 +387,20 @@ static int mcp23s08_remove(struct spi_device *spi)
}
}
- status = gpiochip_remove(&mcp->chip);
+ for (addr = 0; addr < 4; addr++) {
+ int tmp;
+
+ if (!data->mcp[addr])
+ continue;
+
+ tmp = gpiochip_remove(&data->mcp[addr]->chip);
+ if (tmp < 0) {
+ dev_err(&spi->dev, "%s --> %d\n", "remove", tmp);
+ status = tmp;
+ }
+ }
if (status == 0)
- kfree(mcp);
- else
- dev_err(&spi->dev, "%s --> %d\n", "remove", status);
+ kfree(data);
return status;
}
@@ -355,4 +428,3 @@ static void __exit mcp23s08_exit(void)
module_exit(mcp23s08_exit);
MODULE_LICENSE("GPL");
-
diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c
index a380730b61a..cc8468692ae 100644
--- a/drivers/gpio/pca953x.c
+++ b/drivers/gpio/pca953x.c
@@ -188,6 +188,7 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios)
gc->base = chip->gpio_start;
gc->ngpio = gpios;
gc->label = chip->client->name;
+ gc->dev = &chip->client->dev;
gc->owner = THIS_MODULE;
}
diff --git a/drivers/gpio/pcf857x.c b/drivers/gpio/pcf857x.c
index d25d356c4f2..fc9c6ae739e 100644
--- a/drivers/gpio/pcf857x.c
+++ b/drivers/gpio/pcf857x.c
@@ -200,6 +200,7 @@ static int pcf857x_probe(struct i2c_client *client,
gpio->chip.base = pdata->gpio_base;
gpio->chip.can_sleep = 1;
+ gpio->chip.dev = &client->dev;
gpio->chip.owner = THIS_MODULE;
/* NOTE: the OnSemi jlc1562b is also largely compatible with
diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
index 50e0a465374..a95cb9465d6 100644
--- a/drivers/i2c/chips/Kconfig
+++ b/drivers/i2c/chips/Kconfig
@@ -126,7 +126,7 @@ config ISP1301_OMAP
config TPS65010
tristate "TPS6501x Power Management chips"
- depends on HAVE_GPIO_LIB
+ depends on GPIOLIB
default y if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_OSK
help
If you say yes here you get support for the TPS6501x series of
diff --git a/drivers/i2c/chips/tps65010.c b/drivers/i2c/chips/tps65010.c
index 85949685191..cf02e8fceb4 100644
--- a/drivers/i2c/chips/tps65010.c
+++ b/drivers/i2c/chips/tps65010.c
@@ -636,6 +636,8 @@ static int tps65010_probe(struct i2c_client *client,
tps->outmask = board->outmask;
tps->chip.label = client->name;
+ tps->chip.dev = &client->dev;
+ tps->chip.owner = THIS_MODULE;
tps->chip.set = tps65010_gpio_set;
tps->chip.direction_output = tps65010_output;
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
index 04d9c4d459d..130ef64b44f 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -314,7 +314,7 @@ comment "IDE chipset support/bugfixes"
config IDE_GENERIC
tristate "generic/default IDE chipset support"
- depends on ALPHA || X86 || IA64 || M32R || MIPS || PPC32
+ depends on ALPHA || X86 || IA64 || M32R || MIPS
help
If unsure, say N.
diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile
index 5d414e301a5..64e0ecdc4ed 100644
--- a/drivers/ide/Makefile
+++ b/drivers/ide/Makefile
@@ -1,13 +1,6 @@
#
-# Makefile for the kernel ata, atapi, and ide block device drivers.
-#
-# 12 September 2000, Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
-# Rewritten to use lists instead of if-statements.
-#
-# Note : at this point, these files are compiled on all systems.
-# In the future, some of these should be built conditionally.
-#
# link order is important here
+#
EXTRA_CFLAGS += -Idrivers/ide
diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c
index f575e8341ae..df4af408395 100644
--- a/drivers/ide/arm/icside.c
+++ b/drivers/ide/arm/icside.c
@@ -710,8 +710,14 @@ static int __init icside_init(void)
return ecard_register_driver(&icside_driver);
}
+static void __exit icside_exit(void);
+{
+ ecard_unregister_driver(&icside_driver);
+}
+
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ICS IDE driver");
module_init(icside_init);
+module_exit(icside_exit);
diff --git a/drivers/ide/arm/palm_bk3710.c b/drivers/ide/arm/palm_bk3710.c
index 65bb4b8fd57..3e842d60eae 100644
--- a/drivers/ide/arm/palm_bk3710.c
+++ b/drivers/ide/arm/palm_bk3710.c
@@ -82,6 +82,7 @@ static const struct palm_bk3710_udmatiming palm_bk3710_udmatimings[6] = {
{100, 120}, /* UDMA Mode 2 */
{100, 90}, /* UDMA Mode 3 */
{100, 60}, /* UDMA Mode 4 */
+ {85, 40}, /* UDMA Mode 5 */
};
static void palm_bk3710_setudmamode(void __iomem *base, unsigned int dev,
@@ -334,12 +335,11 @@ static const struct ide_port_ops palm_bk3710_ports_ops = {
.cable_detect = palm_bk3710_cable_detect,
};
-static const struct ide_port_info __devinitdata palm_bk3710_port_info = {
+static struct ide_port_info __devinitdata palm_bk3710_port_info = {
.init_dma = palm_bk3710_init_dma,
.port_ops = &palm_bk3710_ports_ops,
.host_flags = IDE_HFLAG_MMIO,
.pio_mask = ATA_PIO4,
- .udma_mask = ATA_UDMA4, /* (input clk 99MHz) */
.mwdma_mask = ATA_MWDMA2,
};
@@ -352,7 +352,7 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev)
int i, rc;
hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
- clk = clk_get(NULL, "IDECLK");
+ clk = clk_get(&pdev->dev, "IDECLK");
if (IS_ERR(clk))
return -ENODEV;
@@ -392,6 +392,9 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev)
hw.irq = irq->start;
hw.chipset = ide_palm3710;
+ palm_bk3710_port_info.udma_mask = rate < 100000000 ? ATA_UDMA4 :
+ ATA_UDMA5;
+
rc = ide_host_add(&palm_bk3710_port_info, hws, NULL);
if (rc)
goto out;
diff --git a/drivers/ide/arm/rapide.c b/drivers/ide/arm/rapide.c
index 2bdd8b734af..78d27d9ae43 100644
--- a/drivers/ide/arm/rapide.c
+++ b/drivers/ide/arm/rapide.c
@@ -95,7 +95,13 @@ static int __init rapide_init(void)
return ecard_register_driver(&rapide_driver);
}
+static void __exit rapide_exit(void)
+{
+ ecard_unregister_driver(&rapide_driver);
+}
+
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Yellowstone RAPIDE driver");
module_init(rapide_init);
+module_exit(rapide_exit);
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
index 4e73aeee405..e617cf08aef 100644
--- a/drivers/ide/ide-cd.c
+++ b/drivers/ide/ide-cd.c
@@ -57,23 +57,29 @@ static DEFINE_MUTEX(idecd_ref_mutex);
#define ide_cd_g(disk) \
container_of((disk)->private_data, struct cdrom_info, driver)
+static void ide_cd_release(struct kref *);
+
static struct cdrom_info *ide_cd_get(struct gendisk *disk)
{
struct cdrom_info *cd = NULL;
mutex_lock(&idecd_ref_mutex);
cd = ide_cd_g(disk);
- if (cd)
+ if (cd) {
kref_get(&cd->kref);
+ if (ide_device_get(cd->drive)) {
+ kref_put(&cd->kref, ide_cd_release);
+ cd = NULL;
+ }
+ }
mutex_unlock(&idecd_ref_mutex);
return cd;
}
-static void ide_cd_release(struct kref *);
-
static void ide_cd_put(struct cdrom_info *cd)
{
mutex_lock(&idecd_ref_mutex);
+ ide_device_put(cd->drive);
kref_put(&cd->kref, ide_cd_release);
mutex_unlock(&idecd_ref_mutex);
}
@@ -1305,13 +1311,30 @@ static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity,
stat = ide_cd_queue_pc(drive, cmd, 0, &capbuf, &len, sense, 0,
REQ_QUIET);
- if (stat == 0) {
- *capacity = 1 + be32_to_cpu(capbuf.lba);
- *sectors_per_frame =
- be32_to_cpu(capbuf.blocklen) >> SECTOR_BITS;
+ if (stat)
+ return stat;
+
+ /*
+ * Sanity check the given block size
+ */
+ switch (capbuf.blocklen) {
+ case __constant_cpu_to_be32(512):
+ case __constant_cpu_to_be32(1024):
+ case __constant_cpu_to_be32(2048):
+ case __constant_cpu_to_be32(4096):
+ break;
+ default:
+ printk(KERN_ERR "%s: weird block size %u\n",
+ drive->name, capbuf.blocklen);
+ printk(KERN_ERR "%s: default to 2kb block size\n",
+ drive->name);
+ capbuf.blocklen = __constant_cpu_to_be32(2048);
+ break;
}
- return stat;
+ *capacity = 1 + be32_to_cpu(capbuf.lba);
+ *sectors_per_frame = be32_to_cpu(capbuf.blocklen) >> SECTOR_BITS;
+ return 0;
}
static int cdrom_read_tocentry(ide_drive_t *drive, int trackno, int msf_flag,
diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c
index df5fe575687..28d85b410f7 100644
--- a/drivers/ide/ide-disk.c
+++ b/drivers/ide/ide-disk.c
@@ -56,23 +56,29 @@ static DEFINE_MUTEX(idedisk_ref_mutex);
#define ide_disk_g(disk) \
container_of((disk)->private_data, struct ide_disk_obj, driver)
+static void ide_disk_release(struct kref *);
+
static struct ide_disk_obj *ide_disk_get(struct gendisk *disk)
{
struct ide_disk_obj *idkp = NULL;
mutex_lock(&idedisk_ref_mutex);
idkp = ide_disk_g(disk);
- if (idkp)
+ if (idkp) {
kref_get(&idkp->kref);
+ if (ide_device_get(idkp->drive)) {
+ kref_put(&idkp->kref, ide_disk_release);
+ idkp = NULL;
+ }
+ }
mutex_unlock(&idedisk_ref_mutex);
return idkp;
}
-static void ide_disk_release(struct kref *);
-
static void ide_disk_put(struct ide_disk_obj *idkp)
{
mutex_lock(&idedisk_ref_mutex);
+ ide_device_put(idkp->drive);
kref_put(&idkp->kref, ide_disk_release);
mutex_unlock(&idedisk_ref_mutex);
}
diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c
index be99d463dcc..71c377a7bcf 100644
--- a/drivers/ide/ide-dma.c
+++ b/drivers/ide/ide-dma.c
@@ -173,7 +173,7 @@ EXPORT_SYMBOL_GPL(ide_build_sglist);
int ide_build_dmatable (ide_drive_t *drive, struct request *rq)
{
ide_hwif_t *hwif = HWIF(drive);
- unsigned int *table = hwif->dmatable_cpu;
+ __le32 *table = (__le32 *)hwif->dmatable_cpu;
unsigned int is_trm290 = (hwif->chipset == ide_trm290) ? 1 : 0;
unsigned int count = 0;
int i;
diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c
index 3d8e6dd0f41..ca11a26746f 100644
--- a/drivers/ide/ide-floppy.c
+++ b/drivers/ide/ide-floppy.c
@@ -158,23 +158,29 @@ static DEFINE_MUTEX(idefloppy_ref_mutex);
#define ide_floppy_g(disk) \
container_of((disk)->private_data, struct ide_floppy_obj, driver)
+static void idefloppy_cleanup_obj(struct kref *);
+
static struct ide_floppy_obj *ide_floppy_get(struct gendisk *disk)
{
struct ide_floppy_obj *floppy = NULL;
mutex_lock(&idefloppy_ref_mutex);
floppy = ide_floppy_g(disk);
- if (floppy)
+ if (floppy) {
kref_get(&floppy->kref);
+ if (ide_device_get(floppy->drive)) {
+ kref_put(&floppy->kref, idefloppy_cleanup_obj);
+ floppy = NULL;
+ }
+ }
mutex_unlock(&idefloppy_ref_mutex);
return floppy;
}
-static void idefloppy_cleanup_obj(struct kref *);
-
static void ide_floppy_put(struct ide_floppy_obj *floppy)
{
mutex_lock(&idefloppy_ref_mutex);
+ ide_device_put(floppy->drive);
kref_put(&floppy->kref, idefloppy_cleanup_obj);
mutex_unlock(&idefloppy_ref_mutex);
}
diff --git a/drivers/ide/ide-generic.c b/drivers/ide/ide-generic.c
index 31d98fec775..8fe8b5b9cf7 100644
--- a/drivers/ide/ide-generic.c
+++ b/drivers/ide/ide-generic.c
@@ -20,6 +20,11 @@
#include <linux/module.h>
#include <linux/ide.h>
+/* FIXME: convert m32r to use ide_platform host driver */
+#ifdef CONFIG_M32R
+#include <asm/m32r.h>
+#endif
+
#define DRV_NAME "ide_generic"
static int probe_mask = 0x03;
@@ -80,6 +85,21 @@ static int __init ide_generic_sysfs_init(void)
return 0;
}
+#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_MAPPI2) \
+ || defined(CONFIG_PLAT_OPSPUT)
+static const u16 legacy_bases[] = { 0x1f0 };
+static const int legacy_irqs[] = { PLD_IRQ_CFIREQ };
+#elif defined(CONFIG_PLAT_MAPPI3)
+static const u16 legacy_bases[] = { 0x1f0, 0x170 };
+static const int legacy_irqs[] = { PLD_IRQ_CFIREQ, PLD_IRQ_IDEIREQ };
+#elif defined(CONFIG_ALPHA)
+static const u16 legacy_bases[] = { 0x1f0, 0x170, 0x1e8, 0x168 };
+static const int legacy_irqs[] = { 14, 15, 11, 10 };
+#else
+static const u16 legacy_bases[] = { 0x1f0, 0x170, 0x1e8, 0x168, 0x1e0, 0x160 };
+static const int legacy_irqs[] = { 14, 15, 11, 10, 8, 12 };
+#endif
+
static int __init ide_generic_init(void)
{
hw_regs_t hw[MAX_HWIFS], *hws[MAX_HWIFS];
@@ -87,11 +107,17 @@ static int __init ide_generic_init(void)
unsigned long io_addr;
int i, rc;
+#ifdef CONFIG_MIPS
+ if (!ide_probe_legacy())
+ return -ENODEV;
+#endif
printk(KERN_INFO DRV_NAME ": please use \"probe_mask=0x3f\" module "
"parameter for probing all legacy ISA IDE ports\n");
- for (i = 0; i < MAX_HWIFS; i++) {
- io_addr = ide_default_io_base(i);
+ memset(hws, 0, sizeof(hw_regs_t *) * MAX_HWIFS);
+
+ for (i = 0; i < ARRAY_SIZE(legacy_bases); i++) {
+ io_addr = legacy_bases[i];
hws[i] = NULL;
@@ -113,7 +139,11 @@ static int __init ide_generic_init(void)
memset(&hw[i], 0, sizeof(hw[i]));
ide_std_init_ports(&hw[i], io_addr, io_addr + 0x206);
- hw[i].irq = ide_default_irq(io_addr);
+#ifdef CONFIG_IA64
+ hw[i].irq = isa_irq_to_vector(legacy_irqs[i]);
+#else
+ hw[i].irq = legacy_irqs[i];
+#endif
hw[i].chipset = ide_generic;
hws[i] = &hw[i];
diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c
index 07da5fb9eaf..8aae9176451 100644
--- a/drivers/ide/ide-iops.c
+++ b/drivers/ide/ide-iops.c
@@ -510,10 +510,8 @@ void ide_fixstring (u8 *s, const int bytecount, const int byteswap)
if (byteswap) {
/* convert from big-endian to host byte order */
- for (p = end ; p != s;) {
- unsigned short *pp = (unsigned short *) (p -= 2);
- *pp = ntohs(*pp);
- }
+ for (p = end ; p != s;)
+ be16_to_cpus((u16 *)(p -= 2));
}
/* strip leading blanks */
while (s != end && *s == ' ')
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
index 4aa76c45375..994e41099b4 100644
--- a/drivers/ide/ide-probe.c
+++ b/drivers/ide/ide-probe.c
@@ -134,18 +134,6 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd)
#endif
ide_fix_driveid(id);
-#if defined (CONFIG_SCSI_EATA_PIO) || defined (CONFIG_SCSI_EATA)
- /*
- * EATA SCSI controllers do a hardware ATA emulation:
- * Ignore them if there is a driver for them available.
- */
- if ((id->model[0] == 'P' && id->model[1] == 'M') ||
- (id->model[0] == 'S' && id->model[1] == 'K')) {
- printk("%s: EATA SCSI HBA %.10s\n", drive->name, id->model);
- goto err_misc;
- }
-#endif /* CONFIG_SCSI_EATA || CONFIG_SCSI_EATA_PIO */
-
/*
* WIN_IDENTIFY returns little-endian info,
* WIN_PIDENTIFY *usually* returns little-endian info.
@@ -167,7 +155,8 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd)
if (strstr(id->model, "E X A B Y T E N E S T"))
goto err_misc;
- printk("%s: %s, ", drive->name, id->model);
+ printk(KERN_INFO "%s: %s, ", drive->name, id->model);
+
drive->present = 1;
drive->dead = 0;
@@ -176,16 +165,17 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd)
*/
if (cmd == WIN_PIDENTIFY) {
u8 type = (id->config >> 8) & 0x1f;
- printk("ATAPI ");
+
+ printk(KERN_CONT "ATAPI ");
switch (type) {
case ide_floppy:
if (!strstr(id->model, "CD-ROM")) {
if (!strstr(id->model, "oppy") &&
!strstr(id->model, "poyp") &&
!strstr(id->model, "ZIP"))
- printk("cdrom or floppy?, assuming ");
+ printk(KERN_CONT "cdrom or floppy?, assuming ");
if (drive->media != ide_cdrom) {
- printk ("FLOPPY");
+ printk(KERN_CONT "FLOPPY");
drive->removable = 1;
break;
}
@@ -198,25 +188,25 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd)
/* kludge for Apple PowerBook internal zip */
if (!strstr(id->model, "CD-ROM") &&
strstr(id->model, "ZIP")) {
- printk ("FLOPPY");
+ printk(KERN_CONT "FLOPPY");
type = ide_floppy;
break;
}
#endif
- printk ("CD/DVD-ROM");
+ printk(KERN_CONT "CD/DVD-ROM");
break;
case ide_tape:
- printk ("TAPE");
+ printk(KERN_CONT "TAPE");
break;
case ide_optical:
- printk ("OPTICAL");
+ printk(KERN_CONT "OPTICAL");
drive->removable = 1;
break;
default:
- printk("UNKNOWN (type %d)", type);
+ printk(KERN_CONT "UNKNOWN (type %d)", type);
break;
}
- printk (" drive\n");
+ printk(KERN_CONT " drive\n");
drive->media = type;
/* an ATAPI device ignores DRDY */
drive->ready_stat = 0;
@@ -236,7 +226,9 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd)
drive->removable = 1;
drive->media = ide_disk;
- printk("%s DISK drive\n", (id->config == 0x848a) ? "CFA" : "ATA" );
+
+ printk(KERN_CONT "%s DISK drive\n",
+ (id->config == 0x848a) ? "CFA" : "ATA");
return;
@@ -387,7 +379,7 @@ static int try_to_identify (ide_drive_t *drive, u8 cmd)
/* Mmmm.. multiple IRQs..
* don't know which was ours
*/
- printk("%s: IRQ probe failed (0x%lx)\n",
+ printk(KERN_ERR "%s: IRQ probe failed (0x%lx)\n",
drive->name, cookie);
}
}
@@ -456,7 +448,7 @@ static int do_probe (ide_drive_t *drive, u8 cmd)
return 4;
}
#ifdef DEBUG
- printk("probing for %s: present=%d, media=%d, probetype=%s\n",
+ printk(KERN_INFO "probing for %s: present=%d, media=%d, probetype=%s\n",
drive->name, drive->present, drive->media,
(cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI");
#endif
@@ -534,7 +526,8 @@ static void enable_nest (ide_drive_t *drive)
const struct ide_tp_ops *tp_ops = hwif->tp_ops;
u8 stat;
- printk("%s: enabling %s -- ", hwif->name, drive->id->model);
+ printk(KERN_INFO "%s: enabling %s -- ", hwif->name, drive->id->model);
+
SELECT_DRIVE(drive);
msleep(50);
tp_ops->exec_command(hwif, EXABYTE_ENABLE_NEST);
@@ -883,7 +876,7 @@ static void save_match(ide_hwif_t *hwif, ide_hwif_t *new, ide_hwif_t **match)
if (m && m->hwgroup && m->hwgroup != new->hwgroup) {
if (!new->hwgroup)
return;
- printk("%s: potential irq problem with %s and %s\n",
+ printk(KERN_WARNING "%s: potential IRQ problem with %s and %s\n",
hwif->name, new->name, m->name);
}
if (!m || m->irq != hwif->irq) /* don't undo a prior perfect match */
@@ -1142,17 +1135,17 @@ static int init_irq (ide_hwif_t *hwif)
}
#if !defined(__mc68000__)
- printk("%s at 0x%03lx-0x%03lx,0x%03lx on irq %d", hwif->name,
+ printk(KERN_INFO "%s at 0x%03lx-0x%03lx,0x%03lx on irq %d", hwif->name,
io_ports->data_addr, io_ports->status_addr,
io_ports->ctl_addr, hwif->irq);
#else
- printk("%s at 0x%08lx on irq %d", hwif->name,
+ printk(KERN_INFO "%s at 0x%08lx on irq %d", hwif->name,
io_ports->data_addr, hwif->irq);
#endif /* __mc68000__ */
if (match)
- printk(" (%sed with %s)",
+ printk(KERN_CONT " (%sed with %s)",
hwif->sharing_irq ? "shar" : "serializ", match->name);
- printk("\n");
+ printk(KERN_CONT "\n");
mutex_unlock(&ide_cfg_mtx);
return 0;
@@ -1287,7 +1280,7 @@ static int hwif_init(ide_hwif_t *hwif)
if (!hwif->irq) {
hwif->irq = __ide_default_irq(hwif->io_ports.data_addr);
if (!hwif->irq) {
- printk("%s: DISABLED, NO IRQ\n", hwif->name);
+ printk(KERN_ERR "%s: disabled, no IRQ\n", hwif->name);
return 0;
}
}
@@ -1317,16 +1310,16 @@ static int hwif_init(ide_hwif_t *hwif)
*/
hwif->irq = __ide_default_irq(hwif->io_ports.data_addr);
if (!hwif->irq) {
- printk("%s: Disabled unable to get IRQ %d.\n",
+ printk(KERN_ERR "%s: disabled, unable to get IRQ %d\n",
hwif->name, old_irq);
goto out;
}
if (init_irq(hwif)) {
- printk("%s: probed IRQ %d and default IRQ %d failed.\n",
+ printk(KERN_ERR "%s: probed IRQ %d and default IRQ %d failed\n",
hwif->name, old_irq, hwif->irq);
goto out;
}
- printk("%s: probed IRQ %d failed, using default.\n",
+ printk(KERN_WARNING "%s: probed IRQ %d failed, using default\n",
hwif->name, hwif->irq);
done:
@@ -1595,6 +1588,8 @@ struct ide_host *ide_host_alloc_all(const struct ide_port_info *d,
ide_init_port_data(hwif, idx);
+ hwif->host = host;
+
host->ports[i] = hwif;
host->n_ports++;
}
@@ -1604,6 +1599,12 @@ struct ide_host *ide_host_alloc_all(const struct ide_port_info *d,
return NULL;
}
+ if (hws[0])
+ host->dev[0] = hws[0]->dev;
+
+ if (d)
+ host->host_flags = d->host_flags;
+
return host;
}
EXPORT_SYMBOL_GPL(ide_host_alloc_all);
diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c
index 151c91e933d..f66c9c3f6fc 100644
--- a/drivers/ide/ide-proc.c
+++ b/drivers/ide/ide-proc.c
@@ -105,7 +105,7 @@ static int proc_ide_read_identify
len = sprintf(page, "\n");
if (drive) {
- unsigned short *val = (unsigned short *) page;
+ __le16 *val = (__le16 *)page;
err = taskfile_lib_get_identify(drive, page);
if (!err) {
@@ -113,7 +113,7 @@ static int proc_ide_read_identify
page = out;
do {
out += sprintf(out, "%04x%c",
- le16_to_cpu(*val), (++i & 7) ? ' ' : '\n');
+ le16_to_cpup(val), (++i & 7) ? ' ' : '\n');
val += 1;
} while (i < (SECTOR_WORDS * 2));
len = out - page;
diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c
index 6962ca4891a..82c2afe4d28 100644
--- a/drivers/ide/ide-tape.c
+++ b/drivers/ide/ide-tape.c
@@ -322,23 +322,29 @@ static struct class *idetape_sysfs_class;
#define ide_tape_g(disk) \
container_of((disk)->private_data, struct ide_tape_obj, driver)
+static void ide_tape_release(struct kref *);
+
static struct ide_tape_obj *ide_tape_get(struct gendisk *disk)
{
struct ide_tape_obj *tape = NULL;
mutex_lock(&idetape_ref_mutex);
tape = ide_tape_g(disk);
- if (tape)
+ if (tape) {
kref_get(&tape->kref);
+ if (ide_device_get(tape->drive)) {
+ kref_put(&tape->kref, ide_tape_release);
+ tape = NULL;
+ }
+ }
mutex_unlock(&idetape_ref_mutex);
return tape;
}
-static void ide_tape_release(struct kref *);
-
static void ide_tape_put(struct ide_tape_obj *tape)
{
mutex_lock(&idetape_ref_mutex);
+ ide_device_put(tape->drive);
kref_put(&tape->kref, ide_tape_release);
mutex_unlock(&idetape_ref_mutex);
}
@@ -649,10 +655,10 @@ static void ide_tape_callback(ide_drive_t *drive)
uptodate = 0;
} else {
debug_log(DBG_SENSE, "Block Location - %u\n",
- be32_to_cpu(*(u32 *)&readpos[4]));
+ be32_to_cpup((__be32 *)&readpos[4]));
tape->partition = readpos[1];
- tape->first_frame = be32_to_cpu(*(u32 *)&readpos[4]);
+ tape->first_frame = be32_to_cpup((__be32 *)&readpos[4]);
set_bit(IDE_AFLAG_ADDRESS_VALID, &drive->atapi_flags);
}
}
@@ -2375,23 +2381,23 @@ static void idetape_get_mode_sense_results(ide_drive_t *drive)
caps = pc.buf + 4 + pc.buf[3];
/* convert to host order and save for later use */
- speed = be16_to_cpu(*(u16 *)&caps[14]);
- max_speed = be16_to_cpu(*(u16 *)&caps[8]);
+ speed = be16_to_cpup((__be16 *)&caps[14]);
+ max_speed = be16_to_cpup((__be16 *)&caps[8]);
- put_unaligned(max_speed, (u16 *)&caps[8]);
- put_unaligned(be16_to_cpu(*(u16 *)&caps[12]), (u16 *)&caps[12]);
- put_unaligned(speed, (u16 *)&caps[14]);
- put_unaligned(be16_to_cpu(*(u16 *)&caps[16]), (u16 *)&caps[16]);
+ *(u16 *)&caps[8] = max_speed;
+ *(u16 *)&caps[12] = be16_to_cpup((__be16 *)&caps[12]);
+ *(u16 *)&caps[14] = speed;
+ *(u16 *)&caps[16] = be16_to_cpup((__be16 *)&caps[16]);
if (!speed) {
printk(KERN_INFO "ide-tape: %s: invalid tape speed "
"(assuming 650KB/sec)\n", drive->name);
- put_unaligned(650, (u16 *)&caps[14]);
+ *(u16 *)&caps[14] = 650;
}
if (!max_speed) {
printk(KERN_INFO "ide-tape: %s: invalid max_speed "
"(assuming 650KB/sec)\n", drive->name);
- put_unaligned(650, (u16 *)&caps[8]);
+ *(u16 *)&caps[8] = 650;
}
memcpy(&tape->caps, caps, 20);
diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c
index aeddbbd69e8..7fb6f1c8627 100644
--- a/drivers/ide/ide-taskfile.c
+++ b/drivers/ide/ide-taskfile.c
@@ -126,7 +126,10 @@ EXPORT_SYMBOL_GPL(do_rw_taskfile);
static ide_startstop_t set_multmode_intr(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
- u8 stat = hwif->tp_ops->read_status(hwif);
+ u8 stat;
+
+ local_irq_enable_in_hardirq();
+ stat = hwif->tp_ops->read_status(hwif);
if (OK_STAT(stat, READY_STAT, BAD_STAT))
drive->mult_count = drive->mult_req;
@@ -147,6 +150,8 @@ static ide_startstop_t set_geometry_intr(ide_drive_t *drive)
int retries = 5;
u8 stat;
+ local_irq_enable_in_hardirq();
+
while (1) {
stat = hwif->tp_ops->read_status(hwif);
if ((stat & BUSY_STAT) == 0 || retries-- == 0)
@@ -170,7 +175,10 @@ static ide_startstop_t set_geometry_intr(ide_drive_t *drive)
static ide_startstop_t recal_intr(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
- u8 stat = hwif->tp_ops->read_status(hwif);
+ u8 stat;
+
+ local_irq_enable_in_hardirq();
+ stat = hwif->tp_ops->read_status(hwif);
if (!OK_STAT(stat, READY_STAT, BAD_STAT))
return ide_error(drive, "recal_intr", stat);
diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c
index 60f0ca66aa9..772451600e4 100644
--- a/drivers/ide/ide.c
+++ b/drivers/ide/ide.c
@@ -618,6 +618,53 @@ set_val:
EXPORT_SYMBOL(generic_ide_ioctl);
+/**
+ * ide_device_get - get an additional reference to a ide_drive_t
+ * @drive: device to get a reference to
+ *
+ * Gets a reference to the ide_drive_t and increments the use count of the
+ * underlying LLDD module.
+ */
+int ide_device_get(ide_drive_t *drive)
+{
+ struct device *host_dev;
+ struct module *module;
+
+ if (!get_device(&drive->gendev))
+ return -ENXIO;
+
+ host_dev = drive->hwif->host->dev[0];
+ module = host_dev ? host_dev->driver->owner : NULL;
+
+ if (module && !try_module_get(module)) {
+ put_device(&drive->gendev);
+ return -ENXIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ide_device_get);
+
+/**
+ * ide_device_put - release a reference to a ide_drive_t
+ * @drive: device to release a reference on
+ *
+ * Release a reference to the ide_drive_t and decrements the use count of
+ * the underlying LLDD module.
+ */
+void ide_device_put(ide_drive_t *drive)
+{
+#ifdef CONFIG_MODULE_UNLOAD
+ struct device *host_dev = drive->hwif->host->dev[0];
+ struct module *module = host_dev ? host_dev->driver->owner : NULL;
+
+ if (module)
+ module_put(module);
+#endif
+ put_device(&drive->gendev);
+}
+EXPORT_SYMBOL_GPL(ide_device_put);
+
static int ide_bus_match(struct device *dev, struct device_driver *drv)
{
return 1;
diff --git a/drivers/ide/legacy/gayle.c b/drivers/ide/legacy/gayle.c
index dd5c467d8dd..51ba085d7aa 100644
--- a/drivers/ide/legacy/gayle.c
+++ b/drivers/ide/legacy/gayle.c
@@ -127,7 +127,7 @@ static int __init gayle_init(void)
unsigned long phys_base, res_start, res_n;
unsigned long base, ctrlport, irqport;
ide_ack_intr_t *ack_intr;
- int a4000, i;
+ int a4000, i, rc;
hw_regs_t hw[GAYLE_NUM_HWIFS], *hws[] = { NULL, NULL, NULL, NULL };
if (!MACH_IS_AMIGA)
@@ -179,7 +179,11 @@ found:
hws[i] = &hw[i];
}
- return ide_host_add(NULL, hws, NULL);
+ rc = ide_host_add(NULL, hws, NULL);
+ if (rc)
+ release_mem_region(res_start, res_n);
+
+ return rc;
}
module_init(gayle_init);
diff --git a/drivers/ide/legacy/ht6560b.c b/drivers/ide/legacy/ht6560b.c
index 7bc8fd59ea9..98f7c95e39e 100644
--- a/drivers/ide/legacy/ht6560b.c
+++ b/drivers/ide/legacy/ht6560b.c
@@ -3,34 +3,12 @@
*/
/*
- *
- * Version 0.01 Initial version hacked out of ide.c
- *
- * Version 0.02 Added support for PIO modes, auto-tune
- *
- * Version 0.03 Some cleanups
- *
- * Version 0.05 PIO mode cycle timings auto-tune using bus-speed
- *
- * Version 0.06 Prefetch mode now defaults no OFF. To set
- * prefetch mode OFF/ON use "hdparm -p8/-p9".
- * Unmask irq is disabled when prefetch mode
- * is enabled.
- *
- * Version 0.07 Trying to fix CD-ROM detection problem.
- * "Prefetch" mode bit OFF for ide disks and
- * ON for anything else.
- *
- * Version 0.08 Need to force prefetch for CDs and other non-disk
- * devices. (not sure which devices exactly need
- * prefetch)
- *
* HT-6560B EIDE-controller support
* To activate controller support use kernel parameter "ide0=ht6560b".
* Use hdparm utility to enable PIO mode support.
*
* Author: Mikko Ala-Fossi <maf@iki.fi>
- * Jan Evert van Grootheest <janevert@caiway.nl>
+ * Jan Evert van Grootheest <j.e.van.grootheest@caiway.nl>
*
* Try: http://www.maf.iki.fi/~maf/ht6560b/
*/
diff --git a/drivers/ide/pci/aec62xx.c b/drivers/ide/pci/aec62xx.c
index fbc43e121e6..e0c8fe7d9fe 100644
--- a/drivers/ide/pci/aec62xx.c
+++ b/drivers/ide/pci/aec62xx.c
@@ -13,6 +13,8 @@
#include <asm/io.h>
+#define DRV_NAME "aec62xx"
+
struct chipset_bus_clock_list_entry {
u8 xfer_speed;
u8 chipset_settings;
@@ -59,10 +61,6 @@ static const struct chipset_bus_clock_list_entry aec6xxx_34_base [] = {
{ 0, 0x00, 0x00 }
};
-#define BUSCLOCK(D) \
- ((struct chipset_bus_clock_list_entry *) pci_get_drvdata((D)))
-
-
/*
* TO DO: active tuning and correction of cards without a bios.
*/
@@ -88,6 +86,8 @@ static void aec6210_set_mode(ide_drive_t *drive, const u8 speed)
{
ide_hwif_t *hwif = HWIF(drive);
struct pci_dev *dev = to_pci_dev(hwif->dev);
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct chipset_bus_clock_list_entry *bus_clock = host->host_priv;
u16 d_conf = 0;
u8 ultra = 0, ultra_conf = 0;
u8 tmp0 = 0, tmp1 = 0, tmp2 = 0;
@@ -96,7 +96,7 @@ static void aec6210_set_mode(ide_drive_t *drive, const u8 speed)
local_irq_save(flags);
/* 0x40|(2*drive->dn): Active, 0x41|(2*drive->dn): Recovery */
pci_read_config_word(dev, 0x40|(2*drive->dn), &d_conf);
- tmp0 = pci_bus_clock_list(speed, BUSCLOCK(dev));
+ tmp0 = pci_bus_clock_list(speed, bus_clock);
d_conf = ((tmp0 & 0xf0) << 4) | (tmp0 & 0xf);
pci_write_config_word(dev, 0x40|(2*drive->dn), d_conf);
@@ -104,7 +104,7 @@ static void aec6210_set_mode(ide_drive_t *drive, const u8 speed)
tmp2 = 0x00;
pci_read_config_byte(dev, 0x54, &ultra);
tmp1 = ((0x00 << (2*drive->dn)) | (ultra & ~(3 << (2*drive->dn))));
- ultra_conf = pci_bus_clock_list_ultra(speed, BUSCLOCK(dev));
+ ultra_conf = pci_bus_clock_list_ultra(speed, bus_clock);
tmp2 = ((ultra_conf << (2*drive->dn)) | (tmp1 & ~(3 << (2*drive->dn))));
pci_write_config_byte(dev, 0x54, tmp2);
local_irq_restore(flags);
@@ -114,6 +114,8 @@ static void aec6260_set_mode(ide_drive_t *drive, const u8 speed)
{
ide_hwif_t *hwif = HWIF(drive);
struct pci_dev *dev = to_pci_dev(hwif->dev);
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct chipset_bus_clock_list_entry *bus_clock = host->host_priv;
u8 unit = (drive->select.b.unit & 0x01);
u8 tmp1 = 0, tmp2 = 0;
u8 ultra = 0, drive_conf = 0, ultra_conf = 0;
@@ -122,12 +124,12 @@ static void aec6260_set_mode(ide_drive_t *drive, const u8 speed)
local_irq_save(flags);
/* high 4-bits: Active, low 4-bits: Recovery */
pci_read_config_byte(dev, 0x40|drive->dn, &drive_conf);
- drive_conf = pci_bus_clock_list(speed, BUSCLOCK(dev));
+ drive_conf = pci_bus_clock_list(speed, bus_clock);
pci_write_config_byte(dev, 0x40|drive->dn, drive_conf);
pci_read_config_byte(dev, (0x44|hwif->channel), &ultra);
tmp1 = ((0x00 << (4*unit)) | (ultra & ~(7 << (4*unit))));
- ultra_conf = pci_bus_clock_list_ultra(speed, BUSCLOCK(dev));
+ ultra_conf = pci_bus_clock_list_ultra(speed, bus_clock);
tmp2 = ((ultra_conf << (4*unit)) | (tmp1 & ~(7 << (4*unit))));
pci_write_config_byte(dev, (0x44|hwif->channel), tmp2);
local_irq_restore(flags);
@@ -138,15 +140,8 @@ static void aec_set_pio_mode(ide_drive_t *drive, const u8 pio)
drive->hwif->port_ops->set_dma_mode(drive, pio + XFER_PIO_0);
}
-static unsigned int __devinit init_chipset_aec62xx(struct pci_dev *dev, const char *name)
+static unsigned int __devinit init_chipset_aec62xx(struct pci_dev *dev)
{
- int bus_speed = ide_pci_clk ? ide_pci_clk : 33;
-
- if (bus_speed <= 33)
- pci_set_drvdata(dev, (void *) aec6xxx_33_base);
- else
- pci_set_drvdata(dev, (void *) aec6xxx_34_base);
-
/* These are necessary to get AEC6280 Macintosh cards to work */
if ((dev->device == PCI_DEVICE_ID_ARTOP_ATP865) ||
(dev->device == PCI_DEVICE_ID_ARTOP_ATP865R)) {
@@ -187,8 +182,8 @@ static const struct ide_port_ops atp86x_port_ops = {
};
static const struct ide_port_info aec62xx_chipsets[] __devinitdata = {
- { /* 0 */
- .name = "AEC6210",
+ { /* 0: AEC6210 */
+ .name = DRV_NAME,
.init_chipset = init_chipset_aec62xx,
.enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}},
.port_ops = &atp850_port_ops,
@@ -199,8 +194,9 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = {
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA2,
- },{ /* 1 */
- .name = "AEC6260",
+ },
+ { /* 1: AEC6260 */
+ .name = DRV_NAME,
.init_chipset = init_chipset_aec62xx,
.port_ops = &atp86x_port_ops,
.host_flags = IDE_HFLAG_NO_ATAPI_DMA | IDE_HFLAG_NO_AUTODMA |
@@ -208,8 +204,9 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = {
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA4,
- },{ /* 2 */
- .name = "AEC6260R",
+ },
+ { /* 2: AEC6260R */
+ .name = DRV_NAME,
.init_chipset = init_chipset_aec62xx,
.enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}},
.port_ops = &atp86x_port_ops,
@@ -218,8 +215,9 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = {
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA4,
- },{ /* 3 */
- .name = "AEC6280",
+ },
+ { /* 3: AEC6280 */
+ .name = DRV_NAME,
.init_chipset = init_chipset_aec62xx,
.port_ops = &atp86x_port_ops,
.host_flags = IDE_HFLAG_NO_ATAPI_DMA |
@@ -227,8 +225,9 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = {
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA5,
- },{ /* 4 */
- .name = "AEC6280R",
+ },
+ { /* 4: AEC6280R */
+ .name = DRV_NAME,
.init_chipset = init_chipset_aec62xx,
.enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}},
.port_ops = &atp86x_port_ops,
@@ -254,10 +253,17 @@ static const struct ide_port_info aec62xx_chipsets[] __devinitdata = {
static int __devinit aec62xx_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
+ const struct chipset_bus_clock_list_entry *bus_clock;
struct ide_port_info d;
u8 idx = id->driver_data;
+ int bus_speed = ide_pci_clk ? ide_pci_clk : 33;
int err;
+ if (bus_speed <= 33)
+ bus_clock = aec6xxx_33_base;
+ else
+ bus_clock = aec6xxx_34_base;
+
err = pci_enable_device(dev);
if (err)
return err;
@@ -268,18 +274,25 @@ static int __devinit aec62xx_init_one(struct pci_dev *dev, const struct pci_devi
unsigned long dma_base = pci_resource_start(dev, 4);
if (inb(dma_base + 2) & 0x10) {
- d.name = (idx == 4) ? "AEC6880R" : "AEC6880";
+ printk(KERN_INFO DRV_NAME " %s: AEC6880%s card detected"
+ "\n", pci_name(dev), (idx == 4) ? "R" : "");
d.udma_mask = ATA_UDMA6;
}
}
- err = ide_setup_pci_device(dev, &d);
+ err = ide_pci_init_one(dev, &d, (void *)bus_clock);
if (err)
pci_disable_device(dev);
return err;
}
+static void __devexit aec62xx_remove(struct pci_dev *dev)
+{
+ ide_pci_remove(dev);
+ pci_disable_device(dev);
+}
+
static const struct pci_device_id aec62xx_pci_tbl[] = {
{ PCI_VDEVICE(ARTOP, PCI_DEVICE_ID_ARTOP_ATP850UF), 0 },
{ PCI_VDEVICE(ARTOP, PCI_DEVICE_ID_ARTOP_ATP860), 1 },
@@ -294,6 +307,7 @@ static struct pci_driver driver = {
.name = "AEC62xx_IDE",
.id_table = aec62xx_pci_tbl,
.probe = aec62xx_init_one,
+ .remove = aec62xx_remove,
};
static int __init aec62xx_ide_init(void)
@@ -301,7 +315,13 @@ static int __init aec62xx_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit aec62xx_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(aec62xx_ide_init);
+module_exit(aec62xx_ide_exit);
MODULE_AUTHOR("Andre Hedrick");
MODULE_DESCRIPTION("PCI driver module for ARTOP AEC62xx IDE");
diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c
index 5ef7817ac64..b582687e0cd 100644
--- a/drivers/ide/pci/alim15x3.c
+++ b/drivers/ide/pci/alim15x3.c
@@ -38,6 +38,8 @@
#include <asm/io.h>
+#define DRV_NAME "alim15x3"
+
/*
* Allow UDMA on M1543C-E chipset for WDC disks that ignore CRC checking
* (this is DANGEROUS and could result in data corruption).
@@ -207,13 +209,12 @@ static int ali15x3_dma_setup(ide_drive_t *drive)
/**
* init_chipset_ali15x3 - Initialise an ALi IDE controller
* @dev: PCI device
- * @name: Name of the controller
*
* This function initializes the ALI IDE controller and where
* appropriate also sets up the 1533 southbridge.
*/
-
-static unsigned int __devinit init_chipset_ali15x3 (struct pci_dev *dev, const char *name)
+
+static unsigned int __devinit init_chipset_ali15x3(struct pci_dev *dev)
{
unsigned long flags;
u8 tmpbyte;
@@ -515,7 +516,7 @@ static const struct ide_dma_ops ali_dma_ops = {
};
static const struct ide_port_info ali15x3_chipset __devinitdata = {
- .name = "ALI15X3",
+ .name = DRV_NAME,
.init_chipset = init_chipset_ali15x3,
.init_hwif = init_hwif_ali15x3,
.init_dma = init_dma_ali15x3,
@@ -565,7 +566,7 @@ static int __devinit alim15x3_init_one(struct pci_dev *dev, const struct pci_dev
if (idx == 0)
d.host_flags |= IDE_HFLAG_CLEAR_SIMPLEX;
- return ide_setup_pci_device(dev, &d);
+ return ide_pci_init_one(dev, &d, NULL);
}
@@ -580,6 +581,7 @@ static struct pci_driver driver = {
.name = "ALI15x3_IDE",
.id_table = alim15x3_pci_tbl,
.probe = alim15x3_init_one,
+ .remove = ide_pci_remove,
};
static int __init ali15x3_ide_init(void)
@@ -587,7 +589,13 @@ static int __init ali15x3_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit ali15x3_ide_exit(void)
+{
+ return pci_unregister_driver(&driver);
+}
+
module_init(ali15x3_ide_init);
+module_exit(ali15x3_ide_exit);
MODULE_AUTHOR("Michael Aubry, Andrzej Krzysztofowicz, CJ, Andre Hedrick, Alan Cox");
MODULE_DESCRIPTION("PCI driver module for ALi 15x3 IDE");
diff --git a/drivers/ide/pci/amd74xx.c b/drivers/ide/pci/amd74xx.c
index ef7d971031e..2cea7bf51a0 100644
--- a/drivers/ide/pci/amd74xx.c
+++ b/drivers/ide/pci/amd74xx.c
@@ -21,6 +21,8 @@
#include <linux/init.h>
#include <linux/ide.h>
+#define DRV_NAME "amd74xx"
+
enum {
AMD_IDE_CONFIG = 0x41,
AMD_CABLE_DETECT = 0x42,
@@ -110,15 +112,13 @@ static void amd_set_pio_mode(ide_drive_t *drive, const u8 pio)
amd_set_drive(drive, XFER_PIO_0 + pio);
}
-static void __devinit amd7409_cable_detect(struct pci_dev *dev,
- const char *name)
+static void __devinit amd7409_cable_detect(struct pci_dev *dev)
{
/* no host side cable detection */
amd_80w = 0x03;
}
-static void __devinit amd7411_cable_detect(struct pci_dev *dev,
- const char *name)
+static void __devinit amd7411_cable_detect(struct pci_dev *dev)
{
int i;
u32 u = 0;
@@ -129,9 +129,9 @@ static void __devinit amd7411_cable_detect(struct pci_dev *dev,
amd_80w = ((t & 0x3) ? 1 : 0) | ((t & 0xc) ? 2 : 0);
for (i = 24; i >= 0; i -= 8)
if (((u >> i) & 4) && !(amd_80w & (1 << (1 - (i >> 4))))) {
- printk(KERN_WARNING "%s: BIOS didn't set cable bits "
- "correctly. Enabling workaround.\n",
- name);
+ printk(KERN_WARNING DRV_NAME " %s: BIOS didn't set "
+ "cable bits correctly. Enabling workaround.\n",
+ pci_name(dev));
amd_80w |= (1 << (1 - (i >> 4)));
}
}
@@ -140,8 +140,7 @@ static void __devinit amd7411_cable_detect(struct pci_dev *dev,
* The initialization callback. Initialize drive independent registers.
*/
-static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev,
- const char *name)
+static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev)
{
u8 t = 0, offset = amd_offset(dev);
@@ -154,9 +153,9 @@ static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev,
; /* no UDMA > 2 */
else if (dev->vendor == PCI_VENDOR_ID_AMD &&
dev->device == PCI_DEVICE_ID_AMD_VIPER_7409)
- amd7409_cable_detect(dev, name);
+ amd7409_cable_detect(dev);
else
- amd7411_cable_detect(dev, name);
+ amd7411_cable_detect(dev);
/*
* Take care of prefetch & postwrite.
@@ -173,24 +172,6 @@ static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev,
t |= 0xf0;
pci_write_config_byte(dev, AMD_IDE_CONFIG + offset, t);
-/*
- * Determine the system bus clock.
- */
-
- amd_clock = (ide_pci_clk ? ide_pci_clk : 33) * 1000;
-
- switch (amd_clock) {
- case 33000: amd_clock = 33333; break;
- case 37000: amd_clock = 37500; break;
- case 41000: amd_clock = 41666; break;
- }
-
- if (amd_clock < 20000 || amd_clock > 50000) {
- printk(KERN_WARNING "%s: User given PCI clock speed impossible (%d), using 33 MHz instead.\n",
- name, amd_clock);
- amd_clock = 33333;
- }
-
return dev->irq;
}
@@ -222,9 +203,9 @@ static const struct ide_port_ops amd_port_ops = {
IDE_HFLAG_IO_32BIT | \
IDE_HFLAG_UNMASK_IRQS)
-#define DECLARE_AMD_DEV(name_str, swdma, udma) \
+#define DECLARE_AMD_DEV(swdma, udma) \
{ \
- .name = name_str, \
+ .name = DRV_NAME, \
.init_chipset = init_chipset_amd74xx, \
.init_hwif = init_hwif_amd74xx, \
.enablebits = {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, \
@@ -236,9 +217,9 @@ static const struct ide_port_ops amd_port_ops = {
.udma_mask = udma, \
}
-#define DECLARE_NV_DEV(name_str, udma) \
+#define DECLARE_NV_DEV(udma) \
{ \
- .name = name_str, \
+ .name = DRV_NAME, \
.init_chipset = init_chipset_amd74xx, \
.init_hwif = init_hwif_amd74xx, \
.enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, \
@@ -251,31 +232,15 @@ static const struct ide_port_ops amd_port_ops = {
}
static const struct ide_port_info amd74xx_chipsets[] __devinitdata = {
- /* 0 */ DECLARE_AMD_DEV("AMD7401", 0x00, ATA_UDMA2),
- /* 1 */ DECLARE_AMD_DEV("AMD7409", ATA_SWDMA2, ATA_UDMA4),
- /* 2 */ DECLARE_AMD_DEV("AMD7411", ATA_SWDMA2, ATA_UDMA5),
- /* 3 */ DECLARE_AMD_DEV("AMD7441", ATA_SWDMA2, ATA_UDMA5),
- /* 4 */ DECLARE_AMD_DEV("AMD8111", ATA_SWDMA2, ATA_UDMA6),
-
- /* 5 */ DECLARE_NV_DEV("NFORCE", ATA_UDMA5),
- /* 6 */ DECLARE_NV_DEV("NFORCE2", ATA_UDMA6),
- /* 7 */ DECLARE_NV_DEV("NFORCE2-U400R", ATA_UDMA6),
- /* 8 */ DECLARE_NV_DEV("NFORCE2-U400R-SATA", ATA_UDMA6),
- /* 9 */ DECLARE_NV_DEV("NFORCE3-150", ATA_UDMA6),
- /* 10 */ DECLARE_NV_DEV("NFORCE3-250", ATA_UDMA6),
- /* 11 */ DECLARE_NV_DEV("NFORCE3-250-SATA", ATA_UDMA6),
- /* 12 */ DECLARE_NV_DEV("NFORCE3-250-SATA2", ATA_UDMA6),
- /* 13 */ DECLARE_NV_DEV("NFORCE-CK804", ATA_UDMA6),
- /* 14 */ DECLARE_NV_DEV("NFORCE-MCP04", ATA_UDMA6),
- /* 15 */ DECLARE_NV_DEV("NFORCE-MCP51", ATA_UDMA6),
- /* 16 */ DECLARE_NV_DEV("NFORCE-MCP55", ATA_UDMA6),
- /* 17 */ DECLARE_NV_DEV("NFORCE-MCP61", ATA_UDMA6),
- /* 18 */ DECLARE_NV_DEV("NFORCE-MCP65", ATA_UDMA6),
- /* 19 */ DECLARE_NV_DEV("NFORCE-MCP67", ATA_UDMA6),
- /* 20 */ DECLARE_NV_DEV("NFORCE-MCP73", ATA_UDMA6),
- /* 21 */ DECLARE_NV_DEV("NFORCE-MCP77", ATA_UDMA6),
-
- /* 22 */ DECLARE_AMD_DEV("AMD5536", ATA_SWDMA2, ATA_UDMA5),
+ /* 0: AMD7401 */ DECLARE_AMD_DEV(0x00, ATA_UDMA2),
+ /* 1: AMD7409 */ DECLARE_AMD_DEV(ATA_SWDMA2, ATA_UDMA4),
+ /* 2: AMD7411/7441 */ DECLARE_AMD_DEV(ATA_SWDMA2, ATA_UDMA5),
+ /* 3: AMD8111 */ DECLARE_AMD_DEV(ATA_SWDMA2, ATA_UDMA6),
+
+ /* 4: NFORCE */ DECLARE_NV_DEV(ATA_UDMA5),
+ /* 5: >= NFORCE2 */ DECLARE_NV_DEV(ATA_UDMA6),
+
+ /* 6: AMD5536 */ DECLARE_AMD_DEV(ATA_SWDMA2, ATA_UDMA5),
};
static int __devinit amd74xx_probe(struct pci_dev *dev, const struct pci_device_id *id)
@@ -292,47 +257,64 @@ static int __devinit amd74xx_probe(struct pci_dev *dev, const struct pci_device_
if (dev->revision <= 7)
d.swdma_mask = 0;
d.host_flags |= IDE_HFLAG_CLEAR_SIMPLEX;
- } else if (idx == 4) {
+ } else if (idx == 3) {
if (dev->subsystem_vendor == PCI_VENDOR_ID_AMD &&
dev->subsystem_device == PCI_DEVICE_ID_AMD_SERENADE)
d.udma_mask = ATA_UDMA5;
}
- printk(KERN_INFO "%s: %s (rev %02x) UDMA%s controller\n",
- d.name, pci_name(dev), dev->revision,
- amd_dma[fls(d.udma_mask) - 1]);
+ printk(KERN_INFO "%s %s: UDMA%s controller\n",
+ d.name, pci_name(dev), amd_dma[fls(d.udma_mask) - 1]);
+
+ /*
+ * Determine the system bus clock.
+ */
+ amd_clock = (ide_pci_clk ? ide_pci_clk : 33) * 1000;
+
+ switch (amd_clock) {
+ case 33000: amd_clock = 33333; break;
+ case 37000: amd_clock = 37500; break;
+ case 41000: amd_clock = 41666; break;
+ }
+
+ if (amd_clock < 20000 || amd_clock > 50000) {
+ printk(KERN_WARNING "%s: User given PCI clock speed impossible"
+ " (%d), using 33 MHz instead.\n",
+ d.name, amd_clock);
+ amd_clock = 33333;
+ }
- return ide_setup_pci_device(dev, &d);
+ return ide_pci_init_one(dev, &d, NULL);
}
static const struct pci_device_id amd74xx_pci_tbl[] = {
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_COBRA_7401), 0 },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_VIPER_7409), 1 },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_VIPER_7411), 2 },
- { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_OPUS_7441), 3 },
- { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_8111_IDE), 4 },
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_IDE), 5 },
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE), 6 },
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE), 7 },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_OPUS_7441), 2 },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_8111_IDE), 3 },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_IDE), 4 },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE), 5 },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE), 5 },
#ifdef CONFIG_BLK_DEV_IDE_SATA
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA), 8 },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA), 5 },
#endif
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE), 9 },
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE), 10 },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE), 5 },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE), 5 },
#ifdef CONFIG_BLK_DEV_IDE_SATA
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA), 11 },
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2), 12 },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA), 5 },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2), 5 },
#endif
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE), 13 },
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE), 14 },
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE), 15 },
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE), 16 },
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE), 17 },
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE), 18 },
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE), 19 },
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE), 20 },
- { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE), 21 },
- { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5536_IDE), 22 },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE), 5 },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE), 5 },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE), 5 },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE), 5 },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE), 5 },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE), 5 },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE), 5 },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE), 5 },
+ { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE), 5 },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5536_IDE), 6 },
{ 0, },
};
MODULE_DEVICE_TABLE(pci, amd74xx_pci_tbl);
@@ -341,6 +323,7 @@ static struct pci_driver driver = {
.name = "AMD_IDE",
.id_table = amd74xx_pci_tbl,
.probe = amd74xx_probe,
+ .remove = ide_pci_remove,
};
static int __init amd74xx_ide_init(void)
@@ -348,7 +331,13 @@ static int __init amd74xx_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit amd74xx_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(amd74xx_ide_init);
+module_exit(amd74xx_ide_exit);
MODULE_AUTHOR("Vojtech Pavlik");
MODULE_DESCRIPTION("AMD PCI IDE driver");
diff --git a/drivers/ide/pci/atiixp.c b/drivers/ide/pci/atiixp.c
index 8b637181681..332f08f43b5 100644
--- a/drivers/ide/pci/atiixp.c
+++ b/drivers/ide/pci/atiixp.c
@@ -11,6 +11,8 @@
#include <linux/ide.h>
#include <linux/init.h>
+#define DRV_NAME "atiixp"
+
#define ATIIXP_IDE_PIO_TIMING 0x40
#define ATIIXP_IDE_MDMA_TIMING 0x44
#define ATIIXP_IDE_PIO_CONTROL 0x48
@@ -137,16 +139,17 @@ static const struct ide_port_ops atiixp_port_ops = {
};
static const struct ide_port_info atiixp_pci_info[] __devinitdata = {
- { /* 0 */
- .name = "ATIIXP",
+ { /* 0: IXP200/300/400/700 */
+ .name = DRV_NAME,
.enablebits = {{0x48,0x01,0x00}, {0x48,0x08,0x00}},
.port_ops = &atiixp_port_ops,
.host_flags = IDE_HFLAG_LEGACY_IRQS,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA5,
- },{ /* 1 */
- .name = "SB600_PATA",
+ },
+ { /* 1: IXP600 */
+ .name = DRV_NAME,
.enablebits = {{0x48,0x01,0x00}, {0x00,0x00,0x00}},
.port_ops = &atiixp_port_ops,
.host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_LEGACY_IRQS,
@@ -167,7 +170,7 @@ static const struct ide_port_info atiixp_pci_info[] __devinitdata = {
static int __devinit atiixp_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
- return ide_setup_pci_device(dev, &atiixp_pci_info[id->driver_data]);
+ return ide_pci_init_one(dev, &atiixp_pci_info[id->driver_data], NULL);
}
static const struct pci_device_id atiixp_pci_tbl[] = {
@@ -184,6 +187,7 @@ static struct pci_driver driver = {
.name = "ATIIXP_IDE",
.id_table = atiixp_pci_tbl,
.probe = atiixp_init_one,
+ .remove = ide_pci_remove,
};
static int __init atiixp_ide_init(void)
@@ -191,7 +195,13 @@ static int __init atiixp_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit atiixp_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(atiixp_ide_init);
+module_exit(atiixp_ide_exit);
MODULE_AUTHOR("HUI YU");
MODULE_DESCRIPTION("PCI driver module for ATI IXP IDE");
diff --git a/drivers/ide/pci/cmd64x.c b/drivers/ide/pci/cmd64x.c
index ce58bfcdb3c..1360b4fa9fd 100644
--- a/drivers/ide/pci/cmd64x.c
+++ b/drivers/ide/pci/cmd64x.c
@@ -19,6 +19,8 @@
#include <asm/io.h>
+#define DRV_NAME "cmd64x"
+
#define CMD_DEBUG 0
#if CMD_DEBUG
@@ -330,28 +332,10 @@ static int cmd646_1_dma_end(ide_drive_t *drive)
return (dma_stat & 7) != 4;
}
-static unsigned int __devinit init_chipset_cmd64x(struct pci_dev *dev, const char *name)
+static unsigned int __devinit init_chipset_cmd64x(struct pci_dev *dev)
{
u8 mrdmode = 0;
- if (dev->device == PCI_DEVICE_ID_CMD_646) {
-
- switch (dev->revision) {
- case 0x07:
- case 0x05:
- printk("%s: UltraDMA capable\n", name);
- break;
- case 0x03:
- default:
- printk("%s: MultiWord DMA force limited\n", name);
- break;
- case 0x01:
- printk("%s: MultiWord DMA limited, "
- "IRQ workaround enabled\n", name);
- break;
- }
- }
-
/* Set a good latency timer and cache line size value. */
(void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
/* FIXME: pci_set_master() to ensure a good latency timer value */
@@ -425,8 +409,8 @@ static const struct ide_dma_ops cmd648_dma_ops = {
};
static const struct ide_port_info cmd64x_chipsets[] __devinitdata = {
- { /* 0 */
- .name = "CMD643",
+ { /* 0: CMD643 */
+ .name = DRV_NAME,
.init_chipset = init_chipset_cmd64x,
.enablebits = {{0x00,0x00,0x00}, {0x51,0x08,0x08}},
.port_ops = &cmd64x_port_ops,
@@ -436,8 +420,9 @@ static const struct ide_port_info cmd64x_chipsets[] __devinitdata = {
.pio_mask = ATA_PIO5,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = 0x00, /* no udma */
- },{ /* 1 */
- .name = "CMD646",
+ },
+ { /* 1: CMD646 */
+ .name = DRV_NAME,
.init_chipset = init_chipset_cmd64x,
.enablebits = {{0x51,0x04,0x04}, {0x51,0x08,0x08}},
.chipset = ide_cmd646,
@@ -447,8 +432,9 @@ static const struct ide_port_info cmd64x_chipsets[] __devinitdata = {
.pio_mask = ATA_PIO5,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA2,
- },{ /* 2 */
- .name = "CMD648",
+ },
+ { /* 2: CMD648 */
+ .name = DRV_NAME,
.init_chipset = init_chipset_cmd64x,
.enablebits = {{0x51,0x04,0x04}, {0x51,0x08,0x08}},
.port_ops = &cmd64x_port_ops,
@@ -457,8 +443,9 @@ static const struct ide_port_info cmd64x_chipsets[] __devinitdata = {
.pio_mask = ATA_PIO5,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA4,
- },{ /* 3 */
- .name = "CMD649",
+ },
+ { /* 3: CMD649 */
+ .name = DRV_NAME,
.init_chipset = init_chipset_cmd64x,
.enablebits = {{0x51,0x04,0x04}, {0x51,0x08,0x08}},
.port_ops = &cmd64x_port_ops,
@@ -507,7 +494,7 @@ static int __devinit cmd64x_init_one(struct pci_dev *dev, const struct pci_devic
}
}
- return ide_setup_pci_device(dev, &d);
+ return ide_pci_init_one(dev, &d, NULL);
}
static const struct pci_device_id cmd64x_pci_tbl[] = {
@@ -523,6 +510,7 @@ static struct pci_driver driver = {
.name = "CMD64x_IDE",
.id_table = cmd64x_pci_tbl,
.probe = cmd64x_init_one,
+ .remove = ide_pci_remove,
};
static int __init cmd64x_ide_init(void)
@@ -530,7 +518,13 @@ static int __init cmd64x_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit cmd64x_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(cmd64x_ide_init);
+module_exit(cmd64x_ide_exit);
MODULE_AUTHOR("Eddie Dost, David Miller, Andre Hedrick");
MODULE_DESCRIPTION("PCI driver module for CMD64x IDE");
diff --git a/drivers/ide/pci/cs5520.c b/drivers/ide/pci/cs5520.c
index b03d8ae947e..c0364b287f1 100644
--- a/drivers/ide/pci/cs5520.c
+++ b/drivers/ide/pci/cs5520.c
@@ -41,6 +41,8 @@
#include <linux/ide.h>
#include <linux/dma-mapping.h>
+#define DRV_NAME "cs5520"
+
struct pio_clocks
{
int address;
@@ -92,18 +94,11 @@ static const struct ide_port_ops cs5520_port_ops = {
.set_dma_mode = cs5520_set_dma_mode,
};
-#define DECLARE_CS_DEV(name_str) \
- { \
- .name = name_str, \
- .port_ops = &cs5520_port_ops, \
- .host_flags = IDE_HFLAG_ISA_PORTS | \
- IDE_HFLAG_CS5520, \
- .pio_mask = ATA_PIO4, \
- }
-
-static const struct ide_port_info cyrix_chipsets[] __devinitdata = {
- /* 0 */ DECLARE_CS_DEV("Cyrix 5510"),
- /* 1 */ DECLARE_CS_DEV("Cyrix 5520")
+static const struct ide_port_info cyrix_chipset __devinitdata = {
+ .name = DRV_NAME,
+ .port_ops = &cs5520_port_ops,
+ .host_flags = IDE_HFLAG_ISA_PORTS | IDE_HFLAG_CS5520,
+ .pio_mask = ATA_PIO4,
};
/*
@@ -114,7 +109,7 @@ static const struct ide_port_info cyrix_chipsets[] __devinitdata = {
static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
- const struct ide_port_info *d = &cyrix_chipsets[id->driver_data];
+ const struct ide_port_info *d = &cyrix_chipset;
hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL };
ide_setup_pci_noise(dev, d);
@@ -128,7 +123,8 @@ static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_devic
}
pci_set_master(dev);
if (pci_set_dma_mask(dev, DMA_32BIT_MASK)) {
- printk(KERN_WARNING "cs5520: No suitable DMA available.\n");
+ printk(KERN_WARNING "%s: No suitable DMA available.\n",
+ d->name);
return -ENODEV;
}
diff --git a/drivers/ide/pci/cs5530.c b/drivers/ide/pci/cs5530.c
index f5534c1ff34..f235db8c678 100644
--- a/drivers/ide/pci/cs5530.c
+++ b/drivers/ide/pci/cs5530.c
@@ -22,6 +22,8 @@
#include <asm/io.h>
+#define DRV_NAME "cs5530"
+
/*
* Here are the standard PIO mode 0-4 timings for each "format".
* Format-0 uses fast data reg timings, with slower command reg timings.
@@ -127,12 +129,11 @@ static void cs5530_set_dma_mode(ide_drive_t *drive, const u8 mode)
/**
* init_chipset_5530 - set up 5530 bridge
* @dev: PCI device
- * @name: device name
*
* Initialize the cs5530 bridge for reliable IDE DMA operation.
*/
-static unsigned int __devinit init_chipset_cs5530 (struct pci_dev *dev, const char *name)
+static unsigned int __devinit init_chipset_cs5530(struct pci_dev *dev)
{
struct pci_dev *master_0 = NULL, *cs5530_0 = NULL;
@@ -151,11 +152,11 @@ static unsigned int __devinit init_chipset_cs5530 (struct pci_dev *dev, const ch
}
}
if (!master_0) {
- printk(KERN_ERR "%s: unable to locate PCI MASTER function\n", name);
+ printk(KERN_ERR DRV_NAME ": unable to locate PCI MASTER function\n");
goto out;
}
if (!cs5530_0) {
- printk(KERN_ERR "%s: unable to locate CS5530 LEGACY function\n", name);
+ printk(KERN_ERR DRV_NAME ": unable to locate CS5530 LEGACY function\n");
goto out;
}
@@ -243,7 +244,7 @@ static const struct ide_port_ops cs5530_port_ops = {
};
static const struct ide_port_info cs5530_chipset __devinitdata = {
- .name = "CS5530",
+ .name = DRV_NAME,
.init_chipset = init_chipset_cs5530,
.init_hwif = init_hwif_cs5530,
.port_ops = &cs5530_port_ops,
@@ -256,7 +257,7 @@ static const struct ide_port_info cs5530_chipset __devinitdata = {
static int __devinit cs5530_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
- return ide_setup_pci_device(dev, &cs5530_chipset);
+ return ide_pci_init_one(dev, &cs5530_chipset, NULL);
}
static const struct pci_device_id cs5530_pci_tbl[] = {
@@ -269,6 +270,7 @@ static struct pci_driver driver = {
.name = "CS5530 IDE",
.id_table = cs5530_pci_tbl,
.probe = cs5530_init_one,
+ .remove = ide_pci_remove,
};
static int __init cs5530_ide_init(void)
@@ -276,7 +278,13 @@ static int __init cs5530_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit cs5530_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(cs5530_ide_init);
+module_exit(cs5530_ide_exit);
MODULE_AUTHOR("Mark Lord");
MODULE_DESCRIPTION("PCI driver module for Cyrix/NS 5530 IDE");
diff --git a/drivers/ide/pci/cs5535.c b/drivers/ide/pci/cs5535.c
index 5404fe4f701..f7b50cdeefa 100644
--- a/drivers/ide/pci/cs5535.c
+++ b/drivers/ide/pci/cs5535.c
@@ -26,6 +26,8 @@
#include <linux/pci.h>
#include <linux/ide.h>
+#define DRV_NAME "cs5535"
+
#define MSR_ATAC_BASE 0x51300000
#define ATAC_GLD_MSR_CAP (MSR_ATAC_BASE+0)
#define ATAC_GLD_MSR_CONFIG (MSR_ATAC_BASE+0x01)
@@ -169,7 +171,7 @@ static const struct ide_port_ops cs5535_port_ops = {
};
static const struct ide_port_info cs5535_chipset __devinitdata = {
- .name = "CS5535",
+ .name = DRV_NAME,
.port_ops = &cs5535_port_ops,
.host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_POST_SET_MODE,
.pio_mask = ATA_PIO4,
@@ -180,7 +182,7 @@ static const struct ide_port_info cs5535_chipset __devinitdata = {
static int __devinit cs5535_init_one(struct pci_dev *dev,
const struct pci_device_id *id)
{
- return ide_setup_pci_device(dev, &cs5535_chipset);
+ return ide_pci_init_one(dev, &cs5535_chipset, NULL);
}
static const struct pci_device_id cs5535_pci_tbl[] = {
@@ -194,6 +196,7 @@ static struct pci_driver driver = {
.name = "CS5535_IDE",
.id_table = cs5535_pci_tbl,
.probe = cs5535_init_one,
+ .remove = ide_pci_remove,
};
static int __init cs5535_ide_init(void)
@@ -201,7 +204,13 @@ static int __init cs5535_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit cs5535_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(cs5535_ide_init);
+module_exit(cs5535_ide_exit);
MODULE_AUTHOR("AMD");
MODULE_DESCRIPTION("PCI driver module for AMD/NS CS5535 IDE");
diff --git a/drivers/ide/pci/cy82c693.c b/drivers/ide/pci/cy82c693.c
index e14ad5530fa..bfae2f882f4 100644
--- a/drivers/ide/pci/cy82c693.c
+++ b/drivers/ide/pci/cy82c693.c
@@ -48,6 +48,8 @@
#include <asm/io.h>
+#define DRV_NAME "cy82c693"
+
/* the current version */
#define CY82_VERSION "CY82C693U driver v0.34 99-13-12 Andreas S. Krebs (akrebs@altavista.net)"
@@ -330,7 +332,7 @@ static void cy82c693_set_pio_mode(ide_drive_t *drive, const u8 pio)
/*
* this function is called during init and is used to setup the cy82c693 chip
*/
-static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev, const char *name)
+static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev)
{
if (PCI_FUNC(dev->devfn) != 1)
return 0;
@@ -349,8 +351,8 @@ static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev, const c
data = inb(CY82_DATA_PORT);
#if CY82C693_DEBUG_INFO
- printk(KERN_INFO "%s: Peripheral Configuration Register: 0x%X\n",
- name, data);
+ printk(KERN_INFO DRV_NAME ": Peripheral Configuration Register: 0x%X\n",
+ data);
#endif /* CY82C693_DEBUG_INFO */
/*
@@ -371,8 +373,8 @@ static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev, const c
outb(data, CY82_DATA_PORT);
#if CY82C693_DEBUG_INFO
- printk(KERN_INFO "%s: New Peripheral Configuration Register: 0x%X\n",
- name, data);
+ printk(KERN_INFO ": New Peripheral Configuration Register: 0x%X\n",
+ data);
#endif /* CY82C693_DEBUG_INFO */
#endif /* CY82C693_SETDMA_CLOCK */
@@ -398,7 +400,7 @@ static const struct ide_port_ops cy82c693_port_ops = {
};
static const struct ide_port_info cy82c693_chipset __devinitdata = {
- .name = "CY82C693",
+ .name = DRV_NAME,
.init_chipset = init_chipset_cy82c693,
.init_iops = init_iops_cy82c693,
.port_ops = &cy82c693_port_ops,
@@ -419,12 +421,22 @@ static int __devinit cy82c693_init_one(struct pci_dev *dev, const struct pci_dev
if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE &&
PCI_FUNC(dev->devfn) == 1) {
dev2 = pci_get_slot(dev->bus, dev->devfn + 1);
- ret = ide_setup_pci_devices(dev, dev2, &cy82c693_chipset);
- /* We leak pci refs here but thats ok - we can't be unloaded */
+ ret = ide_pci_init_two(dev, dev2, &cy82c693_chipset, NULL);
+ if (ret)
+ pci_dev_put(dev2);
}
return ret;
}
+static void __devexit cy82c693_remove(struct pci_dev *dev)
+{
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct pci_dev *dev2 = host->dev[1] ? to_pci_dev(host->dev[1]) : NULL;
+
+ ide_pci_remove(dev);
+ pci_dev_put(dev2);
+}
+
static const struct pci_device_id cy82c693_pci_tbl[] = {
{ PCI_VDEVICE(CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693), 0 },
{ 0, },
@@ -435,6 +447,7 @@ static struct pci_driver driver = {
.name = "Cypress_IDE",
.id_table = cy82c693_pci_tbl,
.probe = cy82c693_init_one,
+ .remove = cy82c693_remove,
};
static int __init cy82c693_ide_init(void)
@@ -442,7 +455,13 @@ static int __init cy82c693_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit cy82c693_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(cy82c693_ide_init);
+module_exit(cy82c693_ide_exit);
MODULE_AUTHOR("Andreas Krebs, Andre Hedrick");
MODULE_DESCRIPTION("PCI driver module for the Cypress CY82C693 IDE");
diff --git a/drivers/ide/pci/generic.c b/drivers/ide/pci/generic.c
index 041720e2276..b07d4f4273b 100644
--- a/drivers/ide/pci/generic.c
+++ b/drivers/ide/pci/generic.c
@@ -27,6 +27,8 @@
#include <linux/ide.h>
#include <linux/init.h>
+#define DRV_NAME "ide_pci_generic"
+
static int ide_generic_all; /* Set to claim all devices */
module_param_named(all_generic_ide, ide_generic_all, bool, 0444);
@@ -34,9 +36,9 @@ MODULE_PARM_DESC(all_generic_ide, "IDE generic will claim all unknown PCI IDE st
#define IDE_HFLAGS_UMC (IDE_HFLAG_NO_DMA | IDE_HFLAG_FORCE_LEGACY_IRQS)
-#define DECLARE_GENERIC_PCI_DEV(name_str, extra_flags) \
+#define DECLARE_GENERIC_PCI_DEV(extra_flags) \
{ \
- .name = name_str, \
+ .name = DRV_NAME, \
.host_flags = IDE_HFLAG_TRUST_BIOS_FOR_DMA | \
extra_flags, \
.swdma_mask = ATA_SWDMA2, \
@@ -45,10 +47,11 @@ MODULE_PARM_DESC(all_generic_ide, "IDE generic will claim all unknown PCI IDE st
}
static const struct ide_port_info generic_chipsets[] __devinitdata = {
- /* 0 */ DECLARE_GENERIC_PCI_DEV("Unknown", 0),
+ /* 0: Unknown */
+ DECLARE_GENERIC_PCI_DEV(0),
- { /* 1 */
- .name = "NS87410",
+ { /* 1: NS87410 */
+ .name = DRV_NAME,
.enablebits = { {0x43, 0x08, 0x08}, {0x47, 0x08, 0x08} },
.host_flags = IDE_HFLAG_TRUST_BIOS_FOR_DMA,
.swdma_mask = ATA_SWDMA2,
@@ -56,17 +59,15 @@ static const struct ide_port_info generic_chipsets[] __devinitdata = {
.udma_mask = ATA_UDMA6,
},
- /* 2 */ DECLARE_GENERIC_PCI_DEV("SAMURAI", 0),
- /* 3 */ DECLARE_GENERIC_PCI_DEV("HT6565", 0),
- /* 4 */ DECLARE_GENERIC_PCI_DEV("UM8673F", IDE_HFLAGS_UMC),
- /* 5 */ DECLARE_GENERIC_PCI_DEV("UM8886A", IDE_HFLAGS_UMC),
- /* 6 */ DECLARE_GENERIC_PCI_DEV("UM8886BF", IDE_HFLAGS_UMC),
- /* 7 */ DECLARE_GENERIC_PCI_DEV("HINT_IDE", 0),
- /* 8 */ DECLARE_GENERIC_PCI_DEV("VIA_IDE", IDE_HFLAG_NO_AUTODMA),
- /* 9 */ DECLARE_GENERIC_PCI_DEV("OPTI621V", IDE_HFLAG_NO_AUTODMA),
-
- { /* 10 */
- .name = "VIA8237SATA",
+ /* 2: SAMURAI / HT6565 / HINT_IDE */
+ DECLARE_GENERIC_PCI_DEV(0),
+ /* 3: UM8673F / UM8886A / UM8886BF */
+ DECLARE_GENERIC_PCI_DEV(IDE_HFLAGS_UMC),
+ /* 4: VIA_IDE / OPTI621V / Piccolo010{2,3,5} */
+ DECLARE_GENERIC_PCI_DEV(IDE_HFLAG_NO_AUTODMA),
+
+ { /* 5: VIA8237SATA */
+ .name = DRV_NAME,
.host_flags = IDE_HFLAG_TRUST_BIOS_FOR_DMA |
IDE_HFLAG_OFF_BOARD,
.swdma_mask = ATA_SWDMA2,
@@ -74,12 +75,8 @@ static const struct ide_port_info generic_chipsets[] __devinitdata = {
.udma_mask = ATA_UDMA6,
},
- /* 11 */ DECLARE_GENERIC_PCI_DEV("Piccolo0102", IDE_HFLAG_NO_AUTODMA),
- /* 12 */ DECLARE_GENERIC_PCI_DEV("Piccolo0103", IDE_HFLAG_NO_AUTODMA),
- /* 13 */ DECLARE_GENERIC_PCI_DEV("Piccolo0105", IDE_HFLAG_NO_AUTODMA),
-
- { /* 14 */
- .name = "Revolution",
+ { /* 6: Revolution */
+ .name = DRV_NAME,
.host_flags = IDE_HFLAG_CLEAR_SIMPLEX |
IDE_HFLAG_TRUST_BIOS_FOR_DMA |
IDE_HFLAG_OFF_BOARD,
@@ -134,12 +131,12 @@ static int __devinit generic_init_one(struct pci_dev *dev, const struct pci_devi
u16 command;
pci_read_config_word(dev, PCI_COMMAND, &command);
if (!(command & PCI_COMMAND_IO)) {
- printk(KERN_INFO "Skipping disabled %s IDE "
- "controller.\n", d->name);
+ printk(KERN_INFO "%s %s: skipping disabled "
+ "controller\n", d->name, pci_name(dev));
goto out;
}
}
- ret = ide_setup_pci_device(dev, d);
+ ret = ide_pci_init_one(dev, d, NULL);
out:
return ret;
}
@@ -147,20 +144,20 @@ out:
static const struct pci_device_id generic_pci_tbl[] = {
{ PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_87410), 1 },
{ PCI_VDEVICE(PCTECH, PCI_DEVICE_ID_PCTECH_SAMURAI_IDE), 2 },
- { PCI_VDEVICE(HOLTEK, PCI_DEVICE_ID_HOLTEK_6565), 3 },
- { PCI_VDEVICE(UMC, PCI_DEVICE_ID_UMC_UM8673F), 4 },
- { PCI_VDEVICE(UMC, PCI_DEVICE_ID_UMC_UM8886A), 5 },
- { PCI_VDEVICE(UMC, PCI_DEVICE_ID_UMC_UM8886BF), 6 },
- { PCI_VDEVICE(HINT, PCI_DEVICE_ID_HINT_VXPROII_IDE), 7 },
- { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C561), 8 },
- { PCI_VDEVICE(OPTI, PCI_DEVICE_ID_OPTI_82C558), 9 },
+ { PCI_VDEVICE(HOLTEK, PCI_DEVICE_ID_HOLTEK_6565), 2 },
+ { PCI_VDEVICE(UMC, PCI_DEVICE_ID_UMC_UM8673F), 3 },
+ { PCI_VDEVICE(UMC, PCI_DEVICE_ID_UMC_UM8886A), 3 },
+ { PCI_VDEVICE(UMC, PCI_DEVICE_ID_UMC_UM8886BF), 3 },
+ { PCI_VDEVICE(HINT, PCI_DEVICE_ID_HINT_VXPROII_IDE), 2 },
+ { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C561), 4 },
+ { PCI_VDEVICE(OPTI, PCI_DEVICE_ID_OPTI_82C558), 4 },
#ifdef CONFIG_BLK_DEV_IDE_SATA
- { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_8237_SATA), 10 },
+ { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_8237_SATA), 5 },
#endif
- { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO), 11 },
- { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_1), 12 },
- { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_2), 13 },
- { PCI_VDEVICE(NETCELL, PCI_DEVICE_ID_REVOLUTION), 14 },
+ { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO), 4 },
+ { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_1), 4 },
+ { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_2), 4 },
+ { PCI_VDEVICE(NETCELL, PCI_DEVICE_ID_REVOLUTION), 6 },
/*
* Must come last. If you add entries adjust
* this table and generic_chipsets[] appropriately.
@@ -174,6 +171,7 @@ static struct pci_driver driver = {
.name = "PCI_IDE",
.id_table = generic_pci_tbl,
.probe = generic_init_one,
+ .remove = ide_pci_remove,
};
static int __init generic_ide_init(void)
@@ -181,7 +179,13 @@ static int __init generic_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit generic_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(generic_ide_init);
+module_exit(generic_ide_exit);
MODULE_AUTHOR("Andre Hedrick");
MODULE_DESCRIPTION("PCI driver module for generic PCI IDE");
diff --git a/drivers/ide/pci/hpt34x.c b/drivers/ide/pci/hpt34x.c
index 9e1d1c4741d..6009b0b9655 100644
--- a/drivers/ide/pci/hpt34x.c
+++ b/drivers/ide/pci/hpt34x.c
@@ -33,6 +33,8 @@
#include <linux/init.h>
#include <linux/ide.h>
+#define DRV_NAME "hpt34x"
+
#define HPT343_DEBUG_DRIVE_INFO 0
static void hpt34x_set_mode(ide_drive_t *drive, const u8 speed)
@@ -77,7 +79,7 @@ static void hpt34x_set_pio_mode(ide_drive_t *drive, const u8 pio)
*/
#define HPT34X_PCI_INIT_REG 0x80
-static unsigned int __devinit init_chipset_hpt34x(struct pci_dev *dev, const char *name)
+static unsigned int __devinit init_chipset_hpt34x(struct pci_dev *dev)
{
int i = 0;
unsigned long hpt34xIoBase = pci_resource_start(dev, 4);
@@ -126,15 +128,15 @@ static const struct ide_port_ops hpt34x_port_ops = {
IDE_HFLAG_NO_AUTODMA)
static const struct ide_port_info hpt34x_chipsets[] __devinitdata = {
- { /* 0 */
- .name = "HPT343",
+ { /* 0: HPT343 */
+ .name = DRV_NAME,
.init_chipset = init_chipset_hpt34x,
.port_ops = &hpt34x_port_ops,
.host_flags = IDE_HFLAGS_HPT34X | IDE_HFLAG_NON_BOOTABLE,
.pio_mask = ATA_PIO5,
},
- { /* 1 */
- .name = "HPT345",
+ { /* 1: HPT345 */
+ .name = DRV_NAME,
.init_chipset = init_chipset_hpt34x,
.port_ops = &hpt34x_port_ops,
.host_flags = IDE_HFLAGS_HPT34X | IDE_HFLAG_OFF_BOARD,
@@ -156,7 +158,7 @@ static int __devinit hpt34x_init_one(struct pci_dev *dev, const struct pci_devic
d = &hpt34x_chipsets[(pcicmd & PCI_COMMAND_MEMORY) ? 1 : 0];
- return ide_setup_pci_device(dev, d);
+ return ide_pci_init_one(dev, d, NULL);
}
static const struct pci_device_id hpt34x_pci_tbl[] = {
@@ -169,6 +171,7 @@ static struct pci_driver driver = {
.name = "HPT34x_IDE",
.id_table = hpt34x_pci_tbl,
.probe = hpt34x_init_one,
+ .remove = ide_pci_remove,
};
static int __init hpt34x_ide_init(void)
@@ -176,7 +179,13 @@ static int __init hpt34x_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit hpt34x_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(hpt34x_ide_init);
+module_exit(hpt34x_ide_exit);
MODULE_AUTHOR("Andre Hedrick");
MODULE_DESCRIPTION("PCI driver module for Highpoint 34x IDE");
diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c
index 1f1135ce7cd..5271b246b88 100644
--- a/drivers/ide/pci/hpt366.c
+++ b/drivers/ide/pci/hpt366.c
@@ -131,6 +131,8 @@
#include <asm/uaccess.h>
#include <asm/io.h>
+#define DRV_NAME "hpt366"
+
/* various tuning parameters */
#define HPT_RESET_STATE_ENGINE
#undef HPT_DELAY_INTERRUPT
@@ -620,7 +622,8 @@ static u8 hpt3xx_udma_filter(ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
struct pci_dev *dev = to_pci_dev(hwif->dev);
- struct hpt_info *info = pci_get_drvdata(dev);
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct hpt_info *info = host->host_priv + (hwif->dev == host->dev[1]);
u8 mask = hwif->ultra_mask;
switch (info->chip_type) {
@@ -660,7 +663,8 @@ static u8 hpt3xx_mdma_filter(ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
struct pci_dev *dev = to_pci_dev(hwif->dev);
- struct hpt_info *info = pci_get_drvdata(dev);
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct hpt_info *info = host->host_priv + (hwif->dev == host->dev[1]);
switch (info->chip_type) {
case HPT372 :
@@ -694,8 +698,10 @@ static u32 get_speed_setting(u8 speed, struct hpt_info *info)
static void hpt3xx_set_mode(ide_drive_t *drive, const u8 speed)
{
- struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
- struct hpt_info *info = pci_get_drvdata(dev);
+ ide_hwif_t *hwif = drive->hwif;
+ struct pci_dev *dev = to_pci_dev(hwif->dev);
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct hpt_info *info = host->host_priv + (hwif->dev == host->dev[1]);
struct hpt_timings *t = info->timings;
u8 itr_addr = 0x40 + (drive->dn * 4);
u32 old_itr = 0;
@@ -738,7 +744,8 @@ static void hpt3xx_maskproc(ide_drive_t *drive, int mask)
{
ide_hwif_t *hwif = HWIF(drive);
struct pci_dev *dev = to_pci_dev(hwif->dev);
- struct hpt_info *info = pci_get_drvdata(dev);
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct hpt_info *info = host->host_priv + (hwif->dev == host->dev[1]);
if (drive->quirk_list) {
if (info->chip_type >= HPT370) {
@@ -963,24 +970,16 @@ static int __devinit hpt37x_calibrate_dpll(struct pci_dev *dev, u16 f_low, u16 f
return 1;
}
-static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const char *name)
+static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev)
{
- struct hpt_info *info = kmalloc(sizeof(struct hpt_info), GFP_KERNEL);
unsigned long io_base = pci_resource_start(dev, 4);
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct hpt_info *info = host->host_priv + (&dev->dev == host->dev[1]);
+ const char *name = DRV_NAME;
u8 pci_clk, dpll_clk = 0; /* PCI and DPLL clock in MHz */
u8 chip_type;
enum ata_clock clock;
- if (info == NULL) {
- printk(KERN_ERR "%s: out of memory!\n", name);
- return -ENOMEM;
- }
-
- /*
- * Copy everything from a static "template" structure
- * to just allocated per-chip hpt_info structure.
- */
- memcpy(info, pci_get_drvdata(dev), sizeof(struct hpt_info));
chip_type = info->chip_type;
pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, (L1_CACHE_BYTES / 4));
@@ -1048,8 +1047,8 @@ static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const cha
if ((temp & 0xFFFFF000) != 0xABCDE000) {
int i;
- printk(KERN_WARNING "%s: no clock data saved by BIOS\n",
- name);
+ printk(KERN_WARNING "%s %s: no clock data saved by "
+ "BIOS\n", name, pci_name(dev));
/* Calculate the average value of f_CNT. */
for (temp = i = 0; i < 128; i++) {
@@ -1074,8 +1073,9 @@ static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const cha
else
pci_clk = 66;
- printk(KERN_INFO "%s: DPLL base: %d MHz, f_CNT: %d, "
- "assuming %d MHz PCI\n", name, dpll_clk, f_cnt, pci_clk);
+ printk(KERN_INFO "%s %s: DPLL base: %d MHz, f_CNT: %d, "
+ "assuming %d MHz PCI\n", name, pci_name(dev),
+ dpll_clk, f_cnt, pci_clk);
} else {
u32 itr1 = 0;
@@ -1141,8 +1141,8 @@ static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const cha
}
if (info->timings->clock_table[clock] == NULL) {
- printk(KERN_ERR "%s: unknown bus timing!\n", name);
- kfree(info);
+ printk(KERN_ERR "%s %s: unknown bus timing!\n",
+ name, pci_name(dev));
return -EIO;
}
@@ -1168,17 +1168,19 @@ static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const cha
f_low += adjust >> 1;
}
if (adjust == 8) {
- printk(KERN_ERR "%s: DPLL did not stabilize!\n", name);
- kfree(info);
+ printk(KERN_ERR "%s %s: DPLL did not stabilize!\n",
+ name, pci_name(dev));
return -EIO;
}
- printk("%s: using %d MHz DPLL clock\n", name, dpll_clk);
+ printk(KERN_INFO "%s %s: using %d MHz DPLL clock\n",
+ name, pci_name(dev), dpll_clk);
} else {
/* Mark the fact that we're not using the DPLL. */
dpll_clk = 0;
- printk("%s: using %d MHz PCI clock\n", name, pci_clk);
+ printk(KERN_INFO "%s %s: using %d MHz PCI clock\n",
+ name, pci_name(dev), pci_clk);
}
/* Store the clock frequencies. */
@@ -1186,9 +1188,6 @@ static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const cha
info->pci_clk = pci_clk;
info->clock = clock;
- /* Point to this chip's own instance of the hpt_info structure. */
- pci_set_drvdata(dev, info);
-
if (chip_type >= HPT370) {
u8 mcr1, mcr4;
@@ -1218,7 +1217,8 @@ static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const cha
static u8 __devinit hpt3xx_cable_detect(ide_hwif_t *hwif)
{
struct pci_dev *dev = to_pci_dev(hwif->dev);
- struct hpt_info *info = pci_get_drvdata(dev);
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct hpt_info *info = host->host_priv + (hwif->dev == host->dev[1]);
u8 chip_type = info->chip_type;
u8 scr1 = 0, ata66 = hwif->channel ? 0x01 : 0x02;
@@ -1262,7 +1262,8 @@ static u8 __devinit hpt3xx_cable_detect(ide_hwif_t *hwif)
static void __devinit init_hwif_hpt366(ide_hwif_t *hwif)
{
struct pci_dev *dev = to_pci_dev(hwif->dev);
- struct hpt_info *info = pci_get_drvdata(dev);
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct hpt_info *info = host->host_priv + (hwif->dev == host->dev[1]);
int serialize = HPT_SERIALIZE_IO;
u8 chip_type = info->chip_type;
u8 new_mcr, old_mcr = 0;
@@ -1364,7 +1365,8 @@ static void __devinit hpt374_init(struct pci_dev *dev, struct pci_dev *dev2)
if (dev2->irq != dev->irq) {
/* FIXME: we need a core pci_set_interrupt() */
dev2->irq = dev->irq;
- printk(KERN_INFO "HPT374: PCI config space interrupt fixed\n");
+ printk(KERN_INFO DRV_NAME " %s: PCI config space interrupt "
+ "fixed\n", pci_name(dev2));
}
}
@@ -1399,8 +1401,8 @@ static int __devinit hpt36x_init(struct pci_dev *dev, struct pci_dev *dev2)
pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin2);
if (pin1 != pin2 && dev->irq == dev2->irq) {
- printk(KERN_INFO "HPT36x: onboard version of chipset, "
- "pin1=%d pin2=%d\n", pin1, pin2);
+ printk(KERN_INFO DRV_NAME " %s: onboard version of chipset, "
+ "pin1=%d pin2=%d\n", pci_name(dev), pin1, pin2);
return 1;
}
@@ -1455,8 +1457,8 @@ static const struct ide_dma_ops hpt36x_dma_ops = {
};
static const struct ide_port_info hpt366_chipsets[] __devinitdata = {
- { /* 0 */
- .name = "HPT36x",
+ { /* 0: HPT36x */
+ .name = DRV_NAME,
.init_chipset = init_chipset_hpt366,
.init_hwif = init_hwif_hpt366,
.init_dma = init_dma_hpt366,
@@ -1472,53 +1474,9 @@ static const struct ide_port_info hpt366_chipsets[] __devinitdata = {
.host_flags = IDE_HFLAGS_HPT3XX | IDE_HFLAG_SINGLE,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
- },{ /* 1 */
- .name = "HPT372A",
- .init_chipset = init_chipset_hpt366,
- .init_hwif = init_hwif_hpt366,
- .init_dma = init_dma_hpt366,
- .enablebits = {{0x50,0x04,0x04}, {0x54,0x04,0x04}},
- .port_ops = &hpt3xx_port_ops,
- .dma_ops = &hpt37x_dma_ops,
- .host_flags = IDE_HFLAGS_HPT3XX,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA2,
- },{ /* 2 */
- .name = "HPT302",
- .init_chipset = init_chipset_hpt366,
- .init_hwif = init_hwif_hpt366,
- .init_dma = init_dma_hpt366,
- .enablebits = {{0x50,0x04,0x04}, {0x54,0x04,0x04}},
- .port_ops = &hpt3xx_port_ops,
- .dma_ops = &hpt37x_dma_ops,
- .host_flags = IDE_HFLAGS_HPT3XX,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA2,
- },{ /* 3 */
- .name = "HPT371",
- .init_chipset = init_chipset_hpt366,
- .init_hwif = init_hwif_hpt366,
- .init_dma = init_dma_hpt366,
- .enablebits = {{0x50,0x04,0x04}, {0x54,0x04,0x04}},
- .port_ops = &hpt3xx_port_ops,
- .dma_ops = &hpt37x_dma_ops,
- .host_flags = IDE_HFLAGS_HPT3XX,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA2,
- },{ /* 4 */
- .name = "HPT374",
- .init_chipset = init_chipset_hpt366,
- .init_hwif = init_hwif_hpt366,
- .init_dma = init_dma_hpt366,
- .enablebits = {{0x50,0x04,0x04}, {0x54,0x04,0x04}},
- .udma_mask = ATA_UDMA5,
- .port_ops = &hpt3xx_port_ops,
- .dma_ops = &hpt37x_dma_ops,
- .host_flags = IDE_HFLAGS_HPT3XX,
- .pio_mask = ATA_PIO4,
- .mwdma_mask = ATA_MWDMA2,
- },{ /* 5 */
- .name = "HPT372N",
+ },
+ { /* 1: HPT3xx */
+ .name = DRV_NAME,
.init_chipset = init_chipset_hpt366,
.init_hwif = init_hwif_hpt366,
.init_dma = init_dma_hpt366,
@@ -1542,10 +1500,12 @@ static const struct ide_port_info hpt366_chipsets[] __devinitdata = {
static int __devinit hpt366_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
const struct hpt_info *info = NULL;
+ struct hpt_info *dyn_info;
struct pci_dev *dev2 = NULL;
struct ide_port_info d;
u8 idx = id->driver_data;
u8 rev = dev->revision;
+ int ret;
if ((idx == 0 || idx == 4) && (PCI_FUNC(dev->devfn) & 1))
return -ENODEV;
@@ -1582,24 +1542,35 @@ static int __devinit hpt366_init_one(struct pci_dev *dev, const struct pci_devic
break;
}
- d = hpt366_chipsets[idx];
+ printk(KERN_INFO DRV_NAME ": %s chipset detected\n", info->chip_name);
+
+ d = hpt366_chipsets[min_t(u8, idx, 1)];
- d.name = info->chip_name;
d.udma_mask = info->udma_mask;
/* fixup ->dma_ops for HPT370/HPT370A */
if (info == &hpt370 || info == &hpt370a)
d.dma_ops = &hpt370_dma_ops;
- pci_set_drvdata(dev, (void *)info);
-
if (info == &hpt36x || info == &hpt374)
dev2 = pci_get_slot(dev->bus, dev->devfn + 1);
- if (dev2) {
- int ret;
+ dyn_info = kzalloc(sizeof(*dyn_info) * (dev2 ? 2 : 1), GFP_KERNEL);
+ if (dyn_info == NULL) {
+ printk(KERN_ERR "%s %s: out of memory!\n",
+ d.name, pci_name(dev));
+ pci_dev_put(dev2);
+ return -ENOMEM;
+ }
+
+ /*
+ * Copy everything from a static "template" structure
+ * to just allocated per-chip hpt_info structure.
+ */
+ memcpy(dyn_info, info, sizeof(*dyn_info));
- pci_set_drvdata(dev2, (void *)info);
+ if (dev2) {
+ memcpy(dyn_info + 1, info, sizeof(*dyn_info));
if (info == &hpt374)
hpt374_init(dev, dev2);
@@ -1608,13 +1579,30 @@ static int __devinit hpt366_init_one(struct pci_dev *dev, const struct pci_devic
d.host_flags &= ~IDE_HFLAG_NON_BOOTABLE;
}
- ret = ide_setup_pci_devices(dev, dev2, &d);
- if (ret < 0)
+ ret = ide_pci_init_two(dev, dev2, &d, dyn_info);
+ if (ret < 0) {
pci_dev_put(dev2);
+ kfree(dyn_info);
+ }
return ret;
}
- return ide_setup_pci_device(dev, &d);
+ ret = ide_pci_init_one(dev, &d, dyn_info);
+ if (ret < 0)
+ kfree(dyn_info);
+
+ return ret;
+}
+
+static void __devexit hpt366_remove(struct pci_dev *dev)
+{
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct ide_info *info = host->host_priv;
+ struct pci_dev *dev2 = host->dev[1] ? to_pci_dev(host->dev[1]) : NULL;
+
+ ide_pci_remove(dev);
+ pci_dev_put(dev2);
+ kfree(info);
}
static const struct pci_device_id hpt366_pci_tbl[] __devinitconst = {
@@ -1632,6 +1620,7 @@ static struct pci_driver driver = {
.name = "HPT366_IDE",
.id_table = hpt366_pci_tbl,
.probe = hpt366_init_one,
+ .remove = hpt366_remove,
};
static int __init hpt366_ide_init(void)
@@ -1639,7 +1628,13 @@ static int __init hpt366_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit hpt366_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(hpt366_ide_init);
+module_exit(hpt366_ide_exit);
MODULE_AUTHOR("Andre Hedrick");
MODULE_DESCRIPTION("PCI driver module for Highpoint HPT366 IDE");
diff --git a/drivers/ide/pci/it8213.c b/drivers/ide/pci/it8213.c
index 2b71bdf74e7..6eba8f18826 100644
--- a/drivers/ide/pci/it8213.c
+++ b/drivers/ide/pci/it8213.c
@@ -14,6 +14,8 @@
#include <linux/ide.h>
#include <linux/init.h>
+#define DRV_NAME "it8213"
+
/**
* it8213_set_pio_mode - set host controller for PIO mode
* @drive: drive
@@ -155,23 +157,17 @@ static const struct ide_port_ops it8213_port_ops = {
.cable_detect = it8213_cable_detect,
};
-#define DECLARE_ITE_DEV(name_str) \
- { \
- .name = name_str, \
- .enablebits = { {0x41, 0x80, 0x80} }, \
- .port_ops = &it8213_port_ops, \
- .host_flags = IDE_HFLAG_SINGLE, \
- .pio_mask = ATA_PIO4, \
- .swdma_mask = ATA_SWDMA2_ONLY, \
- .mwdma_mask = ATA_MWDMA12_ONLY, \
- .udma_mask = ATA_UDMA6, \
- }
-
-static const struct ide_port_info it8213_chipsets[] __devinitdata = {
- /* 0 */ DECLARE_ITE_DEV("IT8213"),
+static const struct ide_port_info it8213_chipset __devinitdata = {
+ .name = DRV_NAME,
+ .enablebits = { {0x41, 0x80, 0x80} },
+ .port_ops = &it8213_port_ops,
+ .host_flags = IDE_HFLAG_SINGLE,
+ .pio_mask = ATA_PIO4,
+ .swdma_mask = ATA_SWDMA2_ONLY,
+ .mwdma_mask = ATA_MWDMA12_ONLY,
+ .udma_mask = ATA_UDMA6,
};
-
/**
* it8213_init_one - pci layer discovery entry
* @dev: PCI device
@@ -184,7 +180,7 @@ static const struct ide_port_info it8213_chipsets[] __devinitdata = {
static int __devinit it8213_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
- return ide_setup_pci_device(dev, &it8213_chipsets[id->driver_data]);
+ return ide_pci_init_one(dev, &it8213_chipset, NULL);
}
static const struct pci_device_id it8213_pci_tbl[] = {
@@ -198,6 +194,7 @@ static struct pci_driver driver = {
.name = "ITE8213_IDE",
.id_table = it8213_pci_tbl,
.probe = it8213_init_one,
+ .remove = ide_pci_remove,
};
static int __init it8213_ide_init(void)
@@ -205,7 +202,13 @@ static int __init it8213_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit it8213_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(it8213_ide_init);
+module_exit(it8213_ide_exit);
MODULE_AUTHOR("Jack Lee, Alan Cox");
MODULE_DESCRIPTION("PCI driver module for the ITE 8213");
diff --git a/drivers/ide/pci/it821x.c b/drivers/ide/pci/it821x.c
index cbf64720299..e16a1d113a2 100644
--- a/drivers/ide/pci/it821x.c
+++ b/drivers/ide/pci/it821x.c
@@ -67,6 +67,8 @@
#include <linux/ide.h>
#include <linux/init.h>
+#define DRV_NAME "it821x"
+
struct it821x_dev
{
unsigned int smart:1, /* Are we in smart raid mode */
@@ -534,8 +536,9 @@ static struct ide_dma_ops it821x_pass_through_dma_ops = {
static void __devinit init_hwif_it821x(ide_hwif_t *hwif)
{
struct pci_dev *dev = to_pci_dev(hwif->dev);
- struct it821x_dev **itdevs = (struct it821x_dev **)pci_get_drvdata(dev);
- struct it821x_dev *idev = itdevs[hwif->channel];
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct it821x_dev *itdevs = host->host_priv;
+ struct it821x_dev *idev = itdevs + hwif->channel;
u8 conf;
ide_set_hwifdata(hwif, idev);
@@ -568,7 +571,8 @@ static void __devinit init_hwif_it821x(ide_hwif_t *hwif)
idev->timing10 = 1;
hwif->host_flags |= IDE_HFLAG_NO_ATAPI_DMA;
if (idev->smart == 0)
- printk(KERN_WARNING "it821x: Revision 0x10, workarounds activated.\n");
+ printk(KERN_WARNING DRV_NAME " %s: revision 0x10, "
+ "workarounds activated\n", pci_name(dev));
}
if (idev->smart == 0) {
@@ -601,18 +605,20 @@ static void __devinit it8212_disable_raid(struct pci_dev *dev)
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20);
}
-static unsigned int __devinit init_chipset_it821x(struct pci_dev *dev, const char *name)
+static unsigned int __devinit init_chipset_it821x(struct pci_dev *dev)
{
u8 conf;
static char *mode[2] = { "pass through", "smart" };
/* Force the card into bypass mode if so requested */
if (it8212_noraid) {
- printk(KERN_INFO "it8212: forcing bypass mode.\n");
+ printk(KERN_INFO DRV_NAME " %s: forcing bypass mode\n",
+ pci_name(dev));
it8212_disable_raid(dev);
}
pci_read_config_byte(dev, 0x50, &conf);
- printk(KERN_INFO "it821x: controller in %s mode.\n", mode[conf & 1]);
+ printk(KERN_INFO DRV_NAME " %s: controller in %s mode\n",
+ pci_name(dev), mode[conf & 1]);
return 0;
}
@@ -624,17 +630,12 @@ static const struct ide_port_ops it821x_port_ops = {
.cable_detect = it821x_cable_detect,
};
-#define DECLARE_ITE_DEV(name_str) \
- { \
- .name = name_str, \
- .init_chipset = init_chipset_it821x, \
- .init_hwif = init_hwif_it821x, \
- .port_ops = &it821x_port_ops, \
- .pio_mask = ATA_PIO4, \
- }
-
-static const struct ide_port_info it821x_chipsets[] __devinitdata = {
- /* 0 */ DECLARE_ITE_DEV("IT8212"),
+static const struct ide_port_info it821x_chipset __devinitdata = {
+ .name = DRV_NAME,
+ .init_chipset = init_chipset_it821x,
+ .init_hwif = init_hwif_it821x,
+ .port_ops = &it821x_port_ops,
+ .pio_mask = ATA_PIO4,
};
/**
@@ -648,23 +649,29 @@ static const struct ide_port_info it821x_chipsets[] __devinitdata = {
static int __devinit it821x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
- struct it821x_dev *itdevs[2] = { NULL, NULL} , *itdev;
- unsigned int i;
-
- for (i = 0; i < 2; i++) {
- itdev = kzalloc(sizeof(*itdev), GFP_KERNEL);
- if (itdev == NULL) {
- kfree(itdevs[0]);
- printk(KERN_ERR "it821x: out of memory\n");
- return -ENOMEM;
- }
+ struct it821x_dev *itdevs;
+ int rc;
- itdevs[i] = itdev;
+ itdevs = kzalloc(2 * sizeof(*itdevs), GFP_KERNEL);
+ if (itdevs == NULL) {
+ printk(KERN_ERR DRV_NAME " %s: out of memory\n", pci_name(dev));
+ return -ENOMEM;
}
- pci_set_drvdata(dev, itdevs);
+ rc = ide_pci_init_one(dev, &it821x_chipset, itdevs);
+ if (rc)
+ kfree(itdevs);
- return ide_setup_pci_device(dev, &it821x_chipsets[id->driver_data]);
+ return rc;
+}
+
+static void __devexit it821x_remove(struct pci_dev *dev)
+{
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct it821x_dev *itdevs = host->host_priv;
+
+ ide_pci_remove(dev);
+ kfree(itdevs);
}
static const struct pci_device_id it821x_pci_tbl[] = {
@@ -679,6 +686,7 @@ static struct pci_driver driver = {
.name = "ITE821x IDE",
.id_table = it821x_pci_tbl,
.probe = it821x_init_one,
+ .remove = it821x_remove,
};
static int __init it821x_ide_init(void)
@@ -686,7 +694,13 @@ static int __init it821x_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit it821x_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(it821x_ide_init);
+module_exit(it821x_ide_exit);
module_param_named(noraid, it8212_noraid, int, S_IRUGO);
MODULE_PARM_DESC(noraid, "Force card into bypass mode");
diff --git a/drivers/ide/pci/jmicron.c b/drivers/ide/pci/jmicron.c
index 96ef7394f28..545b6e172d9 100644
--- a/drivers/ide/pci/jmicron.c
+++ b/drivers/ide/pci/jmicron.c
@@ -12,6 +12,8 @@
#include <linux/ide.h>
#include <linux/init.h>
+#define DRV_NAME "jmicron"
+
typedef enum {
PORT_PATA0 = 0,
PORT_PATA1 = 1,
@@ -102,7 +104,7 @@ static const struct ide_port_ops jmicron_port_ops = {
};
static const struct ide_port_info jmicron_chipset __devinitdata = {
- .name = "JMB",
+ .name = DRV_NAME,
.enablebits = { { 0x40, 0x01, 0x01 }, { 0x40, 0x10, 0x10 } },
.port_ops = &jmicron_port_ops,
.pio_mask = ATA_PIO5,
@@ -121,7 +123,7 @@ static const struct ide_port_info jmicron_chipset __devinitdata = {
static int __devinit jmicron_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
- return ide_setup_pci_device(dev, &jmicron_chipset);
+ return ide_pci_init_one(dev, &jmicron_chipset, NULL);
}
/* All JMB PATA controllers have and will continue to have the same
@@ -152,6 +154,7 @@ static struct pci_driver driver = {
.name = "JMicron IDE",
.id_table = jmicron_pci_tbl,
.probe = jmicron_init_one,
+ .remove = ide_pci_remove,
};
static int __init jmicron_ide_init(void)
@@ -159,7 +162,13 @@ static int __init jmicron_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit jmicron_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(jmicron_ide_init);
+module_exit(jmicron_ide_exit);
MODULE_AUTHOR("Alan Cox");
MODULE_DESCRIPTION("PCI driver module for the JMicron in legacy modes");
diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c
index 5cd2b32ff0e..ffefcd15196 100644
--- a/drivers/ide/pci/ns87415.c
+++ b/drivers/ide/pci/ns87415.c
@@ -19,6 +19,8 @@
#include <asm/io.h>
+#define DRV_NAME "ns87415"
+
#ifdef CONFIG_SUPERIO
/* SUPERIO 87560 is a PoS chip that NatSem denies exists.
* Unfortunately, it's built-in on all Astro-based PA-RISC workstations
@@ -305,7 +307,7 @@ static const struct ide_dma_ops ns87415_dma_ops = {
};
static const struct ide_port_info ns87415_chipset __devinitdata = {
- .name = "NS87415",
+ .name = DRV_NAME,
.init_hwif = init_hwif_ns87415,
.port_ops = &ns87415_port_ops,
.dma_ops = &ns87415_dma_ops,
@@ -324,7 +326,7 @@ static int __devinit ns87415_init_one(struct pci_dev *dev, const struct pci_devi
d.tp_ops = &superio_tp_ops;
}
#endif
- return ide_setup_pci_device(dev, &d);
+ return ide_pci_init_one(dev, &d, NULL);
}
static const struct pci_device_id ns87415_pci_tbl[] = {
@@ -337,6 +339,7 @@ static struct pci_driver driver = {
.name = "NS87415_IDE",
.id_table = ns87415_pci_tbl,
.probe = ns87415_init_one,
+ .remove = ide_pci_remove,
};
static int __init ns87415_ide_init(void)
@@ -344,7 +347,13 @@ static int __init ns87415_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit ns87415_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(ns87415_ide_init);
+module_exit(ns87415_ide_exit);
MODULE_AUTHOR("Mark Lord, Eddie Dost, Andre Hedrick");
MODULE_DESCRIPTION("PCI driver module for NS87415 IDE");
diff --git a/drivers/ide/pci/opti621.c b/drivers/ide/pci/opti621.c
index 725c80508d9..e28e672ddaf 100644
--- a/drivers/ide/pci/opti621.c
+++ b/drivers/ide/pci/opti621.c
@@ -90,6 +90,8 @@
#include <asm/io.h>
+#define DRV_NAME "opti621"
+
#define READ_REG 0 /* index of Read cycle timing register */
#define WRITE_REG 1 /* index of Write cycle timing register */
#define CNTRL_REG 3 /* index of Control register */
@@ -200,7 +202,7 @@ static const struct ide_port_ops opti621_port_ops = {
};
static const struct ide_port_info opti621_chipset __devinitdata = {
- .name = "OPTI621/X",
+ .name = DRV_NAME,
.enablebits = { {0x45, 0x80, 0x00}, {0x40, 0x08, 0x00} },
.port_ops = &opti621_port_ops,
.host_flags = IDE_HFLAG_NO_DMA,
@@ -209,7 +211,7 @@ static const struct ide_port_info opti621_chipset __devinitdata = {
static int __devinit opti621_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
- return ide_setup_pci_device(dev, &opti621_chipset);
+ return ide_pci_init_one(dev, &opti621_chipset, NULL);
}
static const struct pci_device_id opti621_pci_tbl[] = {
@@ -223,6 +225,7 @@ static struct pci_driver driver = {
.name = "Opti621_IDE",
.id_table = opti621_pci_tbl,
.probe = opti621_init_one,
+ .remove = ide_pci_remove,
};
static int __init opti621_ide_init(void)
@@ -230,7 +233,13 @@ static int __init opti621_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit opti621_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(opti621_ide_init);
+module_exit(opti621_ide_exit);
MODULE_AUTHOR("Jaromir Koutek, Jan Harkes, Mark Lord");
MODULE_DESCRIPTION("PCI driver module for Opti621 IDE");
diff --git a/drivers/ide/pci/pdc202xx_new.c b/drivers/ide/pci/pdc202xx_new.c
index 070df8ab3b2..998615fa285 100644
--- a/drivers/ide/pci/pdc202xx_new.c
+++ b/drivers/ide/pci/pdc202xx_new.c
@@ -31,6 +31,8 @@
#include <asm/pci-bridge.h>
#endif
+#define DRV_NAME "pdc202xx_new"
+
#undef DEBUG
#ifdef DEBUG
@@ -324,8 +326,9 @@ static void __devinit apple_kiwi_init(struct pci_dev *pdev)
}
#endif /* CONFIG_PPC_PMAC */
-static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev, const char *name)
+static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev)
{
+ const char *name = DRV_NAME;
unsigned long dma_base = pci_resource_start(dev, 4);
unsigned long sec_dma_base = dma_base + 0x08;
long pll_input, pll_output, ratio;
@@ -358,12 +361,13 @@ static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev, const cha
* registers setting.
*/
pll_input = detect_pll_input_clock(dma_base);
- printk("%s: PLL input clock is %ld kHz\n", name, pll_input / 1000);
+ printk(KERN_INFO "%s %s: PLL input clock is %ld kHz\n",
+ name, pci_name(dev), pll_input / 1000);
/* Sanity check */
if (unlikely(pll_input < 5000000L || pll_input > 70000000L)) {
- printk(KERN_ERR "%s: Bad PLL input clock %ld Hz, giving up!\n",
- name, pll_input);
+ printk(KERN_ERR "%s %s: Bad PLL input clock %ld Hz, giving up!"
+ "\n", name, pci_name(dev), pll_input);
goto out;
}
@@ -399,7 +403,8 @@ static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev, const cha
r = 0x00;
} else {
/* Invalid ratio */
- printk(KERN_ERR "%s: Bad ratio %ld, giving up!\n", name, ratio);
+ printk(KERN_ERR "%s %s: Bad ratio %ld, giving up!\n",
+ name, pci_name(dev), ratio);
goto out;
}
@@ -409,7 +414,8 @@ static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev, const cha
if (unlikely(f < 0 || f > 127)) {
/* Invalid F */
- printk(KERN_ERR "%s: F[%d] invalid!\n", name, f);
+ printk(KERN_ERR "%s %s: F[%d] invalid!\n",
+ name, pci_name(dev), f);
goto out;
}
@@ -455,8 +461,8 @@ static struct pci_dev * __devinit pdc20270_get_dev2(struct pci_dev *dev)
if (dev2->irq != dev->irq) {
dev2->irq = dev->irq;
- printk(KERN_INFO "PDC20270: PCI config space "
- "interrupt fixed\n");
+ printk(KERN_INFO DRV_NAME " %s: PCI config space "
+ "interrupt fixed\n", pci_name(dev));
}
return dev2;
@@ -473,9 +479,9 @@ static const struct ide_port_ops pdcnew_port_ops = {
.cable_detect = pdcnew_cable_detect,
};
-#define DECLARE_PDCNEW_DEV(name_str, udma) \
+#define DECLARE_PDCNEW_DEV(udma) \
{ \
- .name = name_str, \
+ .name = DRV_NAME, \
.init_chipset = init_chipset_pdcnew, \
.port_ops = &pdcnew_port_ops, \
.host_flags = IDE_HFLAG_POST_SET_MODE | \
@@ -487,13 +493,8 @@ static const struct ide_port_ops pdcnew_port_ops = {
}
static const struct ide_port_info pdcnew_chipsets[] __devinitdata = {
- /* 0 */ DECLARE_PDCNEW_DEV("PDC20268", ATA_UDMA5),
- /* 1 */ DECLARE_PDCNEW_DEV("PDC20269", ATA_UDMA6),
- /* 2 */ DECLARE_PDCNEW_DEV("PDC20270", ATA_UDMA5),
- /* 3 */ DECLARE_PDCNEW_DEV("PDC20271", ATA_UDMA6),
- /* 4 */ DECLARE_PDCNEW_DEV("PDC20275", ATA_UDMA6),
- /* 5 */ DECLARE_PDCNEW_DEV("PDC20276", ATA_UDMA6),
- /* 6 */ DECLARE_PDCNEW_DEV("PDC20277", ATA_UDMA6),
+ /* 0: PDC202{68,70} */ DECLARE_PDCNEW_DEV(ATA_UDMA5),
+ /* 1: PDC202{69,71,75,76,77} */ DECLARE_PDCNEW_DEV(ATA_UDMA6),
};
/**
@@ -507,13 +508,10 @@ static const struct ide_port_info pdcnew_chipsets[] __devinitdata = {
static int __devinit pdc202new_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
- const struct ide_port_info *d;
+ const struct ide_port_info *d = &pdcnew_chipsets[id->driver_data];
struct pci_dev *bridge = dev->bus->self;
- u8 idx = id->driver_data;
-
- d = &pdcnew_chipsets[idx];
- if (idx == 2 && bridge &&
+ if (dev->device == PCI_DEVICE_ID_PROMISE_20270 && bridge &&
bridge->vendor == PCI_VENDOR_ID_DEC &&
bridge->device == PCI_DEVICE_ID_DEC_21150) {
struct pci_dev *dev2;
@@ -524,33 +522,42 @@ static int __devinit pdc202new_init_one(struct pci_dev *dev, const struct pci_de
dev2 = pdc20270_get_dev2(dev);
if (dev2) {
- int ret = ide_setup_pci_devices(dev, dev2, d);
+ int ret = ide_pci_init_two(dev, dev2, d, NULL);
if (ret < 0)
pci_dev_put(dev2);
return ret;
}
}
- if (idx == 5 && bridge &&
+ if (dev->device == PCI_DEVICE_ID_PROMISE_20276 && bridge &&
bridge->vendor == PCI_VENDOR_ID_INTEL &&
(bridge->device == PCI_DEVICE_ID_INTEL_I960 ||
bridge->device == PCI_DEVICE_ID_INTEL_I960RM)) {
- printk(KERN_INFO "PDC20276: attached to I2O RAID controller, "
- "skipping\n");
+ printk(KERN_INFO DRV_NAME " %s: attached to I2O RAID controller,"
+ " skipping\n", pci_name(dev));
return -ENODEV;
}
- return ide_setup_pci_device(dev, d);
+ return ide_pci_init_one(dev, d, NULL);
+}
+
+static void __devexit pdc202new_remove(struct pci_dev *dev)
+{
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct pci_dev *dev2 = host->dev[1] ? to_pci_dev(host->dev[1]) : NULL;
+
+ ide_pci_remove(dev);
+ pci_dev_put(dev2);
}
static const struct pci_device_id pdc202new_pci_tbl[] = {
{ PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20268), 0 },
{ PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20269), 1 },
- { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20270), 2 },
- { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20271), 3 },
- { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20275), 4 },
- { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20276), 5 },
- { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20277), 6 },
+ { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20270), 0 },
+ { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20271), 1 },
+ { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20275), 1 },
+ { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20276), 1 },
+ { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20277), 1 },
{ 0, },
};
MODULE_DEVICE_TABLE(pci, pdc202new_pci_tbl);
@@ -559,6 +566,7 @@ static struct pci_driver driver = {
.name = "Promise_IDE",
.id_table = pdc202new_pci_tbl,
.probe = pdc202new_init_one,
+ .remove = pdc202new_remove,
};
static int __init pdc202new_ide_init(void)
@@ -566,7 +574,13 @@ static int __init pdc202new_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit pdc202new_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(pdc202new_ide_init);
+module_exit(pdc202new_ide_exit);
MODULE_AUTHOR("Andre Hedrick, Frank Tiernan");
MODULE_DESCRIPTION("PCI driver module for Promise PDC20268 and higher");
diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c
index e54dc653b8c..6ff2def58da 100644
--- a/drivers/ide/pci/pdc202xx_old.c
+++ b/drivers/ide/pci/pdc202xx_old.c
@@ -20,6 +20,8 @@
#include <asm/io.h>
+#define DRV_NAME "pdc202xx_old"
+
#define PDC202XX_DEBUG_DRIVE_INFO 0
static const char *pdc_quirk_drives[] = {
@@ -263,8 +265,7 @@ static void pdc202xx_dma_timeout(ide_drive_t *drive)
ide_dma_timeout(drive);
}
-static unsigned int __devinit init_chipset_pdc202xx(struct pci_dev *dev,
- const char *name)
+static unsigned int __devinit init_chipset_pdc202xx(struct pci_dev *dev)
{
unsigned long dmabase = pci_resource_start(dev, 4);
u8 udma_speed_flag = 0, primary_mode = 0, secondary_mode = 0;
@@ -304,8 +305,8 @@ static void __devinit pdc202ata4_fixup_irq(struct pci_dev *dev,
if (irq != irq2) {
pci_write_config_byte(dev,
(PCI_INTERRUPT_LINE)|0x80, irq); /* 0xbc */
- printk(KERN_INFO "%s: PCI config space interrupt "
- "mirror fixed\n", name);
+ printk(KERN_INFO "%s %s: PCI config space interrupt "
+ "mirror fixed\n", name, pci_name(dev));
}
}
}
@@ -350,9 +351,9 @@ static const struct ide_dma_ops pdc2026x_dma_ops = {
.dma_timeout = pdc202xx_dma_timeout,
};
-#define DECLARE_PDC2026X_DEV(name_str, udma, extra_flags) \
+#define DECLARE_PDC2026X_DEV(udma, extra_flags) \
{ \
- .name = name_str, \
+ .name = DRV_NAME, \
.init_chipset = init_chipset_pdc202xx, \
.port_ops = &pdc2026x_port_ops, \
.dma_ops = &pdc2026x_dma_ops, \
@@ -363,8 +364,8 @@ static const struct ide_dma_ops pdc2026x_dma_ops = {
}
static const struct ide_port_info pdc202xx_chipsets[] __devinitdata = {
- { /* 0 */
- .name = "PDC20246",
+ { /* 0: PDC20246 */
+ .name = DRV_NAME,
.init_chipset = init_chipset_pdc202xx,
.port_ops = &pdc20246_port_ops,
.dma_ops = &pdc20246_dma_ops,
@@ -374,10 +375,10 @@ static const struct ide_port_info pdc202xx_chipsets[] __devinitdata = {
.udma_mask = ATA_UDMA2,
},
- /* 1 */ DECLARE_PDC2026X_DEV("PDC20262", ATA_UDMA4, 0),
- /* 2 */ DECLARE_PDC2026X_DEV("PDC20263", ATA_UDMA4, 0),
- /* 3 */ DECLARE_PDC2026X_DEV("PDC20265", ATA_UDMA5, IDE_HFLAG_RQSIZE_256),
- /* 4 */ DECLARE_PDC2026X_DEV("PDC20267", ATA_UDMA5, IDE_HFLAG_RQSIZE_256),
+ /* 1: PDC2026{2,3} */
+ DECLARE_PDC2026X_DEV(ATA_UDMA4, 0),
+ /* 2: PDC2026{5,7} */
+ DECLARE_PDC2026X_DEV(ATA_UDMA5, IDE_HFLAG_RQSIZE_256),
};
/**
@@ -396,31 +397,32 @@ static int __devinit pdc202xx_init_one(struct pci_dev *dev, const struct pci_dev
d = &pdc202xx_chipsets[idx];
- if (idx < 3)
+ if (idx < 2)
pdc202ata4_fixup_irq(dev, d->name);
- if (idx == 3) {
+ if (dev->vendor == PCI_DEVICE_ID_PROMISE_20265) {
struct pci_dev *bridge = dev->bus->self;
if (bridge &&
bridge->vendor == PCI_VENDOR_ID_INTEL &&
(bridge->device == PCI_DEVICE_ID_INTEL_I960 ||
bridge->device == PCI_DEVICE_ID_INTEL_I960RM)) {
- printk(KERN_INFO "ide: Skipping Promise PDC20265 "
- "attached to I2O RAID controller\n");
+ printk(KERN_INFO DRV_NAME " %s: skipping Promise "
+ "PDC20265 attached to I2O RAID controller\n",
+ pci_name(dev));
return -ENODEV;
}
}
- return ide_setup_pci_device(dev, d);
+ return ide_pci_init_one(dev, d, NULL);
}
static const struct pci_device_id pdc202xx_pci_tbl[] = {
{ PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20246), 0 },
{ PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20262), 1 },
- { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20263), 2 },
- { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20265), 3 },
- { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20267), 4 },
+ { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20263), 1 },
+ { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20265), 2 },
+ { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20267), 2 },
{ 0, },
};
MODULE_DEVICE_TABLE(pci, pdc202xx_pci_tbl);
@@ -429,6 +431,7 @@ static struct pci_driver driver = {
.name = "Promise_Old_IDE",
.id_table = pdc202xx_pci_tbl,
.probe = pdc202xx_init_one,
+ .remove = ide_pci_remove,
};
static int __init pdc202xx_ide_init(void)
@@ -436,7 +439,13 @@ static int __init pdc202xx_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit pdc202xx_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(pdc202xx_ide_init);
+module_exit(pdc202xx_ide_exit);
MODULE_AUTHOR("Andre Hedrick, Frank Tiernan");
MODULE_DESCRIPTION("PCI driver module for older Promise IDE");
diff --git a/drivers/ide/pci/piix.c b/drivers/ide/pci/piix.c
index 0ce41b4ddda..7fc3022dcf6 100644
--- a/drivers/ide/pci/piix.c
+++ b/drivers/ide/pci/piix.c
@@ -54,6 +54,8 @@
#include <asm/io.h>
+#define DRV_NAME "piix"
+
static int no_piix_dma;
/**
@@ -198,13 +200,12 @@ static void piix_set_dma_mode(ide_drive_t *drive, const u8 speed)
/**
* init_chipset_ich - set up the ICH chipset
* @dev: PCI device to set up
- * @name: Name of the device
*
* Initialize the PCI device as required. For the ICH this turns
* out to be nice and simple.
*/
-static unsigned int __devinit init_chipset_ich(struct pci_dev *dev, const char *name)
+static unsigned int __devinit init_chipset_ich(struct pci_dev *dev)
{
u32 extra = 0;
@@ -314,9 +315,9 @@ static const struct ide_port_ops piix_port_ops = {
#define IDE_HFLAGS_PIIX 0
#endif
-#define DECLARE_PIIX_DEV(name_str, udma) \
+#define DECLARE_PIIX_DEV(udma) \
{ \
- .name = name_str, \
+ .name = DRV_NAME, \
.init_hwif = init_hwif_piix, \
.enablebits = {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, \
.port_ops = &piix_port_ops, \
@@ -327,9 +328,9 @@ static const struct ide_port_ops piix_port_ops = {
.udma_mask = udma, \
}
-#define DECLARE_ICH_DEV(name_str, udma) \
+#define DECLARE_ICH_DEV(udma) \
{ \
- .name = name_str, \
+ .name = DRV_NAME, \
.init_chipset = init_chipset_ich, \
.init_hwif = init_hwif_ich, \
.enablebits = {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, \
@@ -342,45 +343,31 @@ static const struct ide_port_ops piix_port_ops = {
}
static const struct ide_port_info piix_pci_info[] __devinitdata = {
- /* 0 */ DECLARE_PIIX_DEV("PIIXa", 0x00), /* no udma */
- /* 1 */ DECLARE_PIIX_DEV("PIIXb", 0x00), /* no udma */
-
- /* 2 */
+ /* 0: MPIIX */
{ /*
* MPIIX actually has only a single IDE channel mapped to
* the primary or secondary ports depending on the value
* of the bit 14 of the IDETIM register at offset 0x6c
*/
- .name = "MPIIX",
+ .name = DRV_NAME,
.enablebits = {{0x6d,0xc0,0x80}, {0x6d,0xc0,0xc0}},
.host_flags = IDE_HFLAG_ISA_PORTS | IDE_HFLAG_NO_DMA |
IDE_HFLAGS_PIIX,
.pio_mask = ATA_PIO4,
/* This is a painful system best to let it self tune for now */
},
-
- /* 3 */ DECLARE_PIIX_DEV("PIIX3", 0x00), /* no udma */
- /* 4 */ DECLARE_PIIX_DEV("PIIX4", ATA_UDMA2),
- /* 5 */ DECLARE_ICH_DEV("ICH0", ATA_UDMA2),
- /* 6 */ DECLARE_PIIX_DEV("PIIX4", ATA_UDMA2),
- /* 7 */ DECLARE_ICH_DEV("ICH", ATA_UDMA4),
- /* 8 */ DECLARE_PIIX_DEV("PIIX4", ATA_UDMA4),
- /* 9 */ DECLARE_PIIX_DEV("PIIX4", ATA_UDMA2),
- /* 10 */ DECLARE_ICH_DEV("ICH2", ATA_UDMA5),
- /* 11 */ DECLARE_ICH_DEV("ICH2M", ATA_UDMA5),
- /* 12 */ DECLARE_ICH_DEV("ICH3M", ATA_UDMA5),
- /* 13 */ DECLARE_ICH_DEV("ICH3", ATA_UDMA5),
- /* 14 */ DECLARE_ICH_DEV("ICH4", ATA_UDMA5),
- /* 15 */ DECLARE_ICH_DEV("ICH5", ATA_UDMA5),
- /* 16 */ DECLARE_ICH_DEV("C-ICH", ATA_UDMA5),
- /* 17 */ DECLARE_ICH_DEV("ICH4", ATA_UDMA5),
- /* 18 */ DECLARE_ICH_DEV("ICH5-SATA", ATA_UDMA5),
- /* 19 */ DECLARE_ICH_DEV("ICH5", ATA_UDMA5),
- /* 20 */ DECLARE_ICH_DEV("ICH6", ATA_UDMA5),
- /* 21 */ DECLARE_ICH_DEV("ICH7", ATA_UDMA5),
- /* 22 */ DECLARE_ICH_DEV("ICH4", ATA_UDMA5),
- /* 23 */ DECLARE_ICH_DEV("ESB2", ATA_UDMA5),
- /* 24 */ DECLARE_ICH_DEV("ICH8M", ATA_UDMA5),
+ /* 1: PIIXa/PIIXb/PIIX3 */
+ DECLARE_PIIX_DEV(0x00), /* no udma */
+ /* 2: PIIX4 */
+ DECLARE_PIIX_DEV(ATA_UDMA2),
+ /* 3: ICH0 */
+ DECLARE_ICH_DEV(ATA_UDMA2),
+ /* 4: ICH */
+ DECLARE_ICH_DEV(ATA_UDMA4),
+ /* 5: PIIX4 */
+ DECLARE_PIIX_DEV(ATA_UDMA4),
+ /* 6: ICH[2-7]/ICH[2-3]M/C-ICH/ICH5-SATA/ESB2/ICH8M */
+ DECLARE_ICH_DEV(ATA_UDMA5),
};
/**
@@ -394,7 +381,7 @@ static const struct ide_port_info piix_pci_info[] __devinitdata = {
static int __devinit piix_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
- return ide_setup_pci_device(dev, &piix_pci_info[id->driver_data]);
+ return ide_pci_init_one(dev, &piix_pci_info[id->driver_data], NULL);
}
/**
@@ -421,39 +408,39 @@ static void __devinit piix_check_450nx(void)
no_piix_dma = 2;
}
if(no_piix_dma)
- printk(KERN_WARNING "piix: 450NX errata present, disabling IDE DMA.\n");
+ printk(KERN_WARNING DRV_NAME ": 450NX errata present, disabling IDE DMA.\n");
if(no_piix_dma == 2)
- printk(KERN_WARNING "piix: A BIOS update may resolve this.\n");
+ printk(KERN_WARNING DRV_NAME ": A BIOS update may resolve this.\n");
}
static const struct pci_device_id piix_pci_tbl[] = {
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371FB_0), 0 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371FB_1), 1 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371MX), 2 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371SB_1), 3 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371AB), 4 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801AB_1), 5 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82443MX_1), 6 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801AA_1), 7 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82372FB_1), 8 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82451NX), 9 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801BA_9), 10 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801BA_8), 11 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801CA_10), 12 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801CA_11), 13 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801DB_11), 14 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801EB_11), 15 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801E_11), 16 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801DB_10), 17 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371FB_0), 1 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371FB_1), 1 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371MX), 0 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371SB_1), 1 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371AB), 2 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801AB_1), 3 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82443MX_1), 2 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801AA_1), 4 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82372FB_1), 5 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82451NX), 2 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801BA_9), 6 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801BA_8), 6 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801CA_10), 6 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801CA_11), 6 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801DB_11), 6 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801EB_11), 6 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801E_11), 6 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801DB_10), 6 },
#ifdef CONFIG_BLK_DEV_IDE_SATA
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801EB_1), 18 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801EB_1), 6 },
#endif
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ESB_2), 19 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH6_19), 20 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH7_21), 21 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801DB_1), 22 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ESB2_18), 23 },
- { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH8_6), 24 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ESB_2), 6 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH6_19), 6 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH7_21), 6 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801DB_1), 6 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ESB2_18), 6 },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH8_6), 6 },
{ 0, },
};
MODULE_DEVICE_TABLE(pci, piix_pci_tbl);
@@ -462,6 +449,7 @@ static struct pci_driver driver = {
.name = "PIIX_IDE",
.id_table = piix_pci_tbl,
.probe = piix_init_one,
+ .remove = ide_pci_remove,
};
static int __init piix_ide_init(void)
@@ -470,7 +458,13 @@ static int __init piix_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit piix_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(piix_ide_init);
+module_exit(piix_ide_exit);
MODULE_AUTHOR("Andre Hedrick, Andrzej Krzysztofowicz");
MODULE_DESCRIPTION("PCI driver module for Intel PIIX IDE");
diff --git a/drivers/ide/pci/rz1000.c b/drivers/ide/pci/rz1000.c
index 532154adba2..8d11ee838a2 100644
--- a/drivers/ide/pci/rz1000.c
+++ b/drivers/ide/pci/rz1000.c
@@ -21,6 +21,8 @@
#include <linux/ide.h>
#include <linux/init.h>
+#define DRV_NAME "rz1000"
+
static void __devinit init_hwif_rz1000 (ide_hwif_t *hwif)
{
struct pci_dev *dev = to_pci_dev(hwif->dev);
@@ -40,7 +42,7 @@ static void __devinit init_hwif_rz1000 (ide_hwif_t *hwif)
}
static const struct ide_port_info rz1000_chipset __devinitdata = {
- .name = "RZ100x",
+ .name = DRV_NAME,
.init_hwif = init_hwif_rz1000,
.chipset = ide_rz1000,
.host_flags = IDE_HFLAG_NO_DMA,
@@ -48,7 +50,7 @@ static const struct ide_port_info rz1000_chipset __devinitdata = {
static int __devinit rz1000_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
- return ide_setup_pci_device(dev, &rz1000_chipset);
+ return ide_pci_init_one(dev, &rz1000_chipset, NULL);
}
static const struct pci_device_id rz1000_pci_tbl[] = {
@@ -62,6 +64,7 @@ static struct pci_driver driver = {
.name = "RZ1000_IDE",
.id_table = rz1000_pci_tbl,
.probe = rz1000_init_one,
+ .remove = ide_pci_remove,
};
static int __init rz1000_ide_init(void)
@@ -69,7 +72,13 @@ static int __init rz1000_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit rz1000_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(rz1000_ide_init);
+module_exit(rz1000_ide_exit);
MODULE_AUTHOR("Andre Hedrick");
MODULE_DESCRIPTION("PCI driver module for RZ1000 IDE");
diff --git a/drivers/ide/pci/sc1200.c b/drivers/ide/pci/sc1200.c
index 14c787b5d95..8efaed16fea 100644
--- a/drivers/ide/pci/sc1200.c
+++ b/drivers/ide/pci/sc1200.c
@@ -22,6 +22,8 @@
#include <asm/io.h>
+#define DRV_NAME "sc1200"
+
#define SC1200_REV_A 0x00
#define SC1200_REV_B1 0x01
#define SC1200_REV_B3 0x02
@@ -234,21 +236,11 @@ static int sc1200_suspend (struct pci_dev *dev, pm_message_t state)
* we only save state when going from full power to less
*/
if (state.event == PM_EVENT_ON) {
- struct sc1200_saved_state *ss;
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct sc1200_saved_state *ss = host->host_priv;
unsigned int r;
/*
- * allocate a permanent save area, if not already allocated
- */
- ss = (struct sc1200_saved_state *)pci_get_drvdata(dev);
- if (ss == NULL) {
- ss = kmalloc(sizeof(*ss), GFP_KERNEL);
- if (ss == NULL)
- return -ENOMEM;
- pci_set_drvdata(dev, ss);
- }
-
- /*
* save timing registers
* (this may be unnecessary if BIOS also does it)
*/
@@ -263,7 +255,8 @@ static int sc1200_suspend (struct pci_dev *dev, pm_message_t state)
static int sc1200_resume (struct pci_dev *dev)
{
- struct sc1200_saved_state *ss;
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct sc1200_saved_state *ss = host->host_priv;
unsigned int r;
int i;
@@ -271,16 +264,12 @@ static int sc1200_resume (struct pci_dev *dev)
if (i)
return i;
- ss = (struct sc1200_saved_state *)pci_get_drvdata(dev);
-
/*
* restore timing registers
* (this may be unnecessary if BIOS also does it)
*/
- if (ss) {
- for (r = 0; r < 8; r++)
- pci_write_config_dword(dev, 0x40 + r * 4, ss->regs[r]);
- }
+ for (r = 0; r < 8; r++)
+ pci_write_config_dword(dev, 0x40 + r * 4, ss->regs[r]);
return 0;
}
@@ -304,7 +293,7 @@ static const struct ide_dma_ops sc1200_dma_ops = {
};
static const struct ide_port_info sc1200_chipset __devinitdata = {
- .name = "SC1200",
+ .name = DRV_NAME,
.port_ops = &sc1200_port_ops,
.dma_ops = &sc1200_dma_ops,
.host_flags = IDE_HFLAG_SERIALIZE |
@@ -317,7 +306,19 @@ static const struct ide_port_info sc1200_chipset __devinitdata = {
static int __devinit sc1200_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
- return ide_setup_pci_device(dev, &sc1200_chipset);
+ struct sc1200_saved_state *ss = NULL;
+ int rc;
+
+#ifdef CONFIG_PM
+ ss = kmalloc(sizeof(*ss), GFP_KERNEL);
+ if (ss == NULL)
+ return -ENOMEM;
+#endif
+ rc = ide_pci_init_one(dev, &sc1200_chipset, ss);
+ if (rc)
+ kfree(ss);
+
+ return rc;
}
static const struct pci_device_id sc1200_pci_tbl[] = {
@@ -330,6 +331,7 @@ static struct pci_driver driver = {
.name = "SC1200_IDE",
.id_table = sc1200_pci_tbl,
.probe = sc1200_init_one,
+ .remove = ide_pci_remove,
#ifdef CONFIG_PM
.suspend = sc1200_suspend,
.resume = sc1200_resume,
@@ -341,7 +343,13 @@ static int __init sc1200_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit sc1200_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(sc1200_ide_init);
+module_exit(sc1200_ide_exit);
MODULE_AUTHOR("Mark Lord");
MODULE_DESCRIPTION("PCI driver module for NS SC1200 IDE");
diff --git a/drivers/ide/pci/serverworks.c b/drivers/ide/pci/serverworks.c
index 127ccb45e26..d173f293772 100644
--- a/drivers/ide/pci/serverworks.c
+++ b/drivers/ide/pci/serverworks.c
@@ -38,6 +38,8 @@
#include <asm/io.h>
+#define DRV_NAME "serverworks"
+
#define SVWKS_CSB5_REVISION_NEW 0x92 /* min PCI_REVISION_ID for UDMA5 (A2.0) */
#define SVWKS_CSB6_REVISION 0xa0 /* min PCI_REVISION_ID for UDMA4 (A1.0) */
@@ -172,7 +174,7 @@ static void svwks_set_dma_mode(ide_drive_t *drive, const u8 speed)
pci_write_config_byte(dev, 0x54, ultra_enable);
}
-static unsigned int __devinit init_chipset_svwks (struct pci_dev *dev, const char *name)
+static unsigned int __devinit init_chipset_svwks(struct pci_dev *dev)
{
unsigned int reg;
u8 btr;
@@ -188,7 +190,8 @@ static unsigned int __devinit init_chipset_svwks (struct pci_dev *dev, const cha
pci_read_config_dword(isa_dev, 0x64, &reg);
reg &= ~0x00002000; /* disable 600ns interrupt mask */
if(!(reg & 0x00004000))
- printk(KERN_DEBUG "%s: UDMA not BIOS enabled.\n", name);
+ printk(KERN_DEBUG DRV_NAME " %s: UDMA not BIOS "
+ "enabled.\n", pci_name(dev));
reg |= 0x00004000; /* enable UDMA/33 support */
pci_write_config_dword(isa_dev, 0x64, reg);
}
@@ -352,40 +355,44 @@ static const struct ide_port_ops svwks_port_ops = {
#define IDE_HFLAGS_SVWKS IDE_HFLAG_LEGACY_IRQS
static const struct ide_port_info serverworks_chipsets[] __devinitdata = {
- { /* 0 */
- .name = "SvrWks OSB4",
+ { /* 0: OSB4 */
+ .name = DRV_NAME,
.init_chipset = init_chipset_svwks,
.port_ops = &osb4_port_ops,
.host_flags = IDE_HFLAGS_SVWKS,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = 0x00, /* UDMA is problematic on OSB4 */
- },{ /* 1 */
- .name = "SvrWks CSB5",
+ },
+ { /* 1: CSB5 */
+ .name = DRV_NAME,
.init_chipset = init_chipset_svwks,
.port_ops = &svwks_port_ops,
.host_flags = IDE_HFLAGS_SVWKS,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA5,
- },{ /* 2 */
- .name = "SvrWks CSB6",
+ },
+ { /* 2: CSB6 */
+ .name = DRV_NAME,
.init_chipset = init_chipset_svwks,
.port_ops = &svwks_port_ops,
.host_flags = IDE_HFLAGS_SVWKS,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA5,
- },{ /* 3 */
- .name = "SvrWks CSB6",
+ },
+ { /* 3: CSB6-2 */
+ .name = DRV_NAME,
.init_chipset = init_chipset_svwks,
.port_ops = &svwks_port_ops,
.host_flags = IDE_HFLAGS_SVWKS | IDE_HFLAG_SINGLE,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA5,
- },{ /* 4 */
- .name = "SvrWks HT1000",
+ },
+ { /* 4: HT1000 */
+ .name = DRV_NAME,
.init_chipset = init_chipset_svwks,
.port_ops = &svwks_port_ops,
.host_flags = IDE_HFLAGS_SVWKS | IDE_HFLAG_SINGLE,
@@ -422,7 +429,7 @@ static int __devinit svwks_init_one(struct pci_dev *dev, const struct pci_device
d.host_flags &= ~IDE_HFLAG_SINGLE;
}
- return ide_setup_pci_device(dev, &d);
+ return ide_pci_init_one(dev, &d, NULL);
}
static const struct pci_device_id svwks_pci_tbl[] = {
@@ -439,6 +446,7 @@ static struct pci_driver driver = {
.name = "Serverworks_IDE",
.id_table = svwks_pci_tbl,
.probe = svwks_init_one,
+ .remove = ide_pci_remove,
};
static int __init svwks_ide_init(void)
@@ -446,7 +454,13 @@ static int __init svwks_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit svwks_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(svwks_ide_init);
+module_exit(svwks_ide_exit);
MODULE_AUTHOR("Michael Aubry. Andrzej Krzysztofowicz, Andre Hedrick");
MODULE_DESCRIPTION("PCI driver module for Serverworks OSB4/CSB5/CSB6 IDE");
diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c
index 5965a35d94a..b8ad9ad6cf0 100644
--- a/drivers/ide/pci/siimage.c
+++ b/drivers/ide/pci/siimage.c
@@ -44,6 +44,8 @@
#include <linux/init.h>
#include <linux/io.h>
+#define DRV_NAME "siimage"
+
/**
* pdev_is_sata - check if device is SATA
* @pdev: PCI device to check
@@ -127,9 +129,10 @@ static inline unsigned long siimage_seldev(ide_drive_t *drive, int r)
static u8 sil_ioread8(struct pci_dev *dev, unsigned long addr)
{
+ struct ide_host *host = pci_get_drvdata(dev);
u8 tmp = 0;
- if (pci_get_drvdata(dev))
+ if (host->host_priv)
tmp = readb((void __iomem *)addr);
else
pci_read_config_byte(dev, addr, &tmp);
@@ -139,9 +142,10 @@ static u8 sil_ioread8(struct pci_dev *dev, unsigned long addr)
static u16 sil_ioread16(struct pci_dev *dev, unsigned long addr)
{
+ struct ide_host *host = pci_get_drvdata(dev);
u16 tmp = 0;
- if (pci_get_drvdata(dev))
+ if (host->host_priv)
tmp = readw((void __iomem *)addr);
else
pci_read_config_word(dev, addr, &tmp);
@@ -151,7 +155,9 @@ static u16 sil_ioread16(struct pci_dev *dev, unsigned long addr)
static void sil_iowrite8(struct pci_dev *dev, u8 val, unsigned long addr)
{
- if (pci_get_drvdata(dev))
+ struct ide_host *host = pci_get_drvdata(dev);
+
+ if (host->host_priv)
writeb(val, (void __iomem *)addr);
else
pci_write_config_byte(dev, addr, val);
@@ -159,7 +165,9 @@ static void sil_iowrite8(struct pci_dev *dev, u8 val, unsigned long addr)
static void sil_iowrite16(struct pci_dev *dev, u16 val, unsigned long addr)
{
- if (pci_get_drvdata(dev))
+ struct ide_host *host = pci_get_drvdata(dev);
+
+ if (host->host_priv)
writew(val, (void __iomem *)addr);
else
pci_write_config_word(dev, addr, val);
@@ -167,7 +175,9 @@ static void sil_iowrite16(struct pci_dev *dev, u16 val, unsigned long addr)
static void sil_iowrite32(struct pci_dev *dev, u32 val, unsigned long addr)
{
- if (pci_get_drvdata(dev))
+ struct ide_host *host = pci_get_drvdata(dev);
+
+ if (host->host_priv)
writel(val, (void __iomem *)addr);
else
pci_write_config_dword(dev, addr, val);
@@ -445,66 +455,24 @@ static void sil_sata_pre_reset(ide_drive_t *drive)
}
/**
- * setup_mmio_siimage - switch controller into MMIO mode
- * @dev: PCI device we are configuring
- * @name: device name
- *
- * Attempt to put the device into MMIO mode. There are some slight
- * complications here with certain systems where the MMIO BAR isn't
- * mapped, so we have to be sure that we can fall back to I/O.
- */
-
-static unsigned int setup_mmio_siimage(struct pci_dev *dev, const char *name)
-{
- resource_size_t bar5 = pci_resource_start(dev, 5);
- unsigned long barsize = pci_resource_len(dev, 5);
- void __iomem *ioaddr;
-
- /*
- * Drop back to PIO if we can't map the MMIO. Some systems
- * seem to get terminally confused in the PCI spaces.
- */
- if (!request_mem_region(bar5, barsize, name)) {
- printk(KERN_WARNING "siimage: IDE controller MMIO ports not "
- "available.\n");
- return 0;
- }
-
- ioaddr = ioremap(bar5, barsize);
- if (ioaddr == NULL) {
- release_mem_region(bar5, barsize);
- return 0;
- }
-
- pci_set_master(dev);
- pci_set_drvdata(dev, (void *) ioaddr);
-
- return 1;
-}
-
-/**
* init_chipset_siimage - set up an SI device
* @dev: PCI device
- * @name: device name
*
* Perform the initial PCI set up for this device. Attempt to switch
* to 133 MHz clocking if the system isn't already set up to do it.
*/
-static unsigned int __devinit init_chipset_siimage(struct pci_dev *dev,
- const char *name)
+static unsigned int __devinit init_chipset_siimage(struct pci_dev *dev)
{
+ struct ide_host *host = pci_get_drvdata(dev);
+ void __iomem *ioaddr = host->host_priv;
unsigned long base, scsc_addr;
- void __iomem *ioaddr = NULL;
- u8 rev = dev->revision, tmp, BA5_EN;
+ u8 rev = dev->revision, tmp;
pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, rev ? 1 : 255);
- pci_read_config_byte(dev, 0x8A, &BA5_EN);
-
- if ((BA5_EN & 0x01) || pci_resource_start(dev, 5))
- if (setup_mmio_siimage(dev, name))
- ioaddr = pci_get_drvdata(dev);
+ if (ioaddr)
+ pci_set_master(dev);
base = (unsigned long)ioaddr;
@@ -571,7 +539,8 @@ static unsigned int __devinit init_chipset_siimage(struct pci_dev *dev,
{ "== 100", "== 133", "== 2X PCI", "DISABLED!" };
tmp >>= 4;
- printk(KERN_INFO "%s: BASE CLOCK %s\n", name, clk_str[tmp & 3]);
+ printk(KERN_INFO DRV_NAME " %s: BASE CLOCK %s\n",
+ pci_name(dev), clk_str[tmp & 3]);
}
return 0;
@@ -592,7 +561,8 @@ static unsigned int __devinit init_chipset_siimage(struct pci_dev *dev,
static void __devinit init_mmio_iops_siimage(ide_hwif_t *hwif)
{
struct pci_dev *dev = to_pci_dev(hwif->dev);
- void *addr = pci_get_drvdata(dev);
+ struct ide_host *host = pci_get_drvdata(dev);
+ void *addr = host->host_priv;
u8 ch = hwif->channel;
struct ide_io_ports *io_ports = &hwif->io_ports;
unsigned long base;
@@ -691,16 +661,15 @@ static void __devinit sil_quirkproc(ide_drive_t *drive)
static void __devinit init_iops_siimage(ide_hwif_t *hwif)
{
struct pci_dev *dev = to_pci_dev(hwif->dev);
+ struct ide_host *host = pci_get_drvdata(dev);
hwif->hwif_data = NULL;
/* Pessimal until we finish probing */
hwif->rqsize = 15;
- if (pci_get_drvdata(dev) == NULL)
- return;
-
- init_mmio_iops_siimage(hwif);
+ if (host->host_priv)
+ init_mmio_iops_siimage(hwif);
}
/**
@@ -748,9 +717,9 @@ static const struct ide_dma_ops sil_dma_ops = {
.dma_lost_irq = ide_dma_lost_irq,
};
-#define DECLARE_SII_DEV(name_str, p_ops) \
+#define DECLARE_SII_DEV(p_ops) \
{ \
- .name = name_str, \
+ .name = DRV_NAME, \
.init_chipset = init_chipset_siimage, \
.init_iops = init_iops_siimage, \
.port_ops = p_ops, \
@@ -761,9 +730,8 @@ static const struct ide_dma_ops sil_dma_ops = {
}
static const struct ide_port_info siimage_chipsets[] __devinitdata = {
- /* 0 */ DECLARE_SII_DEV("SiI680", &sil_pata_port_ops),
- /* 1 */ DECLARE_SII_DEV("SiI3112 Serial ATA", &sil_sata_port_ops),
- /* 2 */ DECLARE_SII_DEV("Adaptec AAR-1210SA", &sil_sata_port_ops)
+ /* 0: SiI680 */ DECLARE_SII_DEV(&sil_pata_port_ops),
+ /* 1: SiI3112 */ DECLARE_SII_DEV(&sil_sata_port_ops)
};
/**
@@ -778,8 +746,13 @@ static const struct ide_port_info siimage_chipsets[] __devinitdata = {
static int __devinit siimage_init_one(struct pci_dev *dev,
const struct pci_device_id *id)
{
+ void __iomem *ioaddr = NULL;
+ resource_size_t bar5 = pci_resource_start(dev, 5);
+ unsigned long barsize = pci_resource_len(dev, 5);
+ int rc;
struct ide_port_info d;
u8 idx = id->driver_data;
+ u8 BA5_EN;
d = siimage_chipsets[idx];
@@ -787,7 +760,7 @@ static int __devinit siimage_init_one(struct pci_dev *dev,
static int first = 1;
if (first) {
- printk(KERN_INFO "siimage: For full SATA support you "
+ printk(KERN_INFO DRV_NAME ": For full SATA support you "
"should use the libata sata_sil module.\n");
first = 0;
}
@@ -795,14 +768,61 @@ static int __devinit siimage_init_one(struct pci_dev *dev,
d.host_flags |= IDE_HFLAG_NO_ATAPI_DMA;
}
- return ide_setup_pci_device(dev, &d);
+ rc = pci_enable_device(dev);
+ if (rc)
+ return rc;
+
+ pci_read_config_byte(dev, 0x8A, &BA5_EN);
+ if ((BA5_EN & 0x01) || bar5) {
+ /*
+ * Drop back to PIO if we can't map the MMIO. Some systems
+ * seem to get terminally confused in the PCI spaces.
+ */
+ if (!request_mem_region(bar5, barsize, d.name)) {
+ printk(KERN_WARNING DRV_NAME " %s: MMIO ports not "
+ "available\n", pci_name(dev));
+ } else {
+ ioaddr = ioremap(bar5, barsize);
+ if (ioaddr == NULL)
+ release_mem_region(bar5, barsize);
+ }
+ }
+
+ rc = ide_pci_init_one(dev, &d, ioaddr);
+ if (rc) {
+ if (ioaddr) {
+ iounmap(ioaddr);
+ release_mem_region(bar5, barsize);
+ }
+ pci_disable_device(dev);
+ }
+
+ return rc;
+}
+
+static void __devexit siimage_remove(struct pci_dev *dev)
+{
+ struct ide_host *host = pci_get_drvdata(dev);
+ void __iomem *ioaddr = host->host_priv;
+
+ ide_pci_remove(dev);
+
+ if (ioaddr) {
+ resource_size_t bar5 = pci_resource_start(dev, 5);
+ unsigned long barsize = pci_resource_len(dev, 5);
+
+ iounmap(ioaddr);
+ release_mem_region(bar5, barsize);
+ }
+
+ pci_disable_device(dev);
}
static const struct pci_device_id siimage_pci_tbl[] = {
{ PCI_VDEVICE(CMD, PCI_DEVICE_ID_SII_680), 0 },
#ifdef CONFIG_BLK_DEV_IDE_SATA
{ PCI_VDEVICE(CMD, PCI_DEVICE_ID_SII_3112), 1 },
- { PCI_VDEVICE(CMD, PCI_DEVICE_ID_SII_1210SA), 2 },
+ { PCI_VDEVICE(CMD, PCI_DEVICE_ID_SII_1210SA), 1 },
#endif
{ 0, },
};
@@ -812,6 +832,7 @@ static struct pci_driver driver = {
.name = "SiI_IDE",
.id_table = siimage_pci_tbl,
.probe = siimage_init_one,
+ .remove = siimage_remove,
};
static int __init siimage_ide_init(void)
@@ -819,7 +840,13 @@ static int __init siimage_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit siimage_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(siimage_ide_init);
+module_exit(siimage_ide_exit);
MODULE_AUTHOR("Andre Hedrick, Alan Cox");
MODULE_DESCRIPTION("PCI driver module for SiI IDE");
diff --git a/drivers/ide/pci/sis5513.c b/drivers/ide/pci/sis5513.c
index 2389945ca95..cc95f90b53b 100644
--- a/drivers/ide/pci/sis5513.c
+++ b/drivers/ide/pci/sis5513.c
@@ -52,6 +52,8 @@
#include <linux/init.h>
#include <linux/ide.h>
+#define DRV_NAME "sis5513"
+
/* registers layout and init values are chipset family dependant */
#define ATA_16 0x01
@@ -380,8 +382,9 @@ static int __devinit sis_find_family(struct pci_dev *dev)
}
pci_dev_put(host);
- printk(KERN_INFO "SIS5513: %s %s controller\n",
- SiSHostChipInfo[i].name, chipset_capability[chipset_family]);
+ printk(KERN_INFO DRV_NAME " %s: %s %s controller\n",
+ pci_name(dev), SiSHostChipInfo[i].name,
+ chipset_capability[chipset_family]);
}
if (!chipset_family) { /* Belongs to pci-quirks */
@@ -396,7 +399,8 @@ static int __devinit sis_find_family(struct pci_dev *dev)
pci_write_config_dword(dev, 0x54, idemisc);
if (trueid == 0x5518) {
- printk(KERN_INFO "SIS5513: SiS 962/963 MuTIOL IDE UDMA133 controller\n");
+ printk(KERN_INFO DRV_NAME " %s: SiS 962/963 MuTIOL IDE UDMA133 controller\n",
+ pci_name(dev));
chipset_family = ATA_133;
/* Check for 5513 compability mapping
@@ -405,7 +409,8 @@ static int __devinit sis_find_family(struct pci_dev *dev)
*/
if ((idemisc & 0x40000000) == 0) {
pci_write_config_dword(dev, 0x54, idemisc | 0x40000000);
- printk(KERN_INFO "SIS5513: Switching to 5513 register mapping\n");
+ printk(KERN_INFO DRV_NAME " %s: Switching to 5513 register mapping\n",
+ pci_name(dev));
}
}
}
@@ -429,10 +434,12 @@ static int __devinit sis_find_family(struct pci_dev *dev)
pci_dev_put(lpc_bridge);
if (lpc_bridge->revision == 0x10 && (prefctl & 0x80)) {
- printk(KERN_INFO "SIS5513: SiS 961B MuTIOL IDE UDMA133 controller\n");
+ printk(KERN_INFO DRV_NAME " %s: SiS 961B MuTIOL IDE UDMA133 controller\n",
+ pci_name(dev));
chipset_family = ATA_133a;
} else {
- printk(KERN_INFO "SIS5513: SiS 961 MuTIOL IDE UDMA100 controller\n");
+ printk(KERN_INFO DRV_NAME " %s: SiS 961 MuTIOL IDE UDMA100 controller\n",
+ pci_name(dev));
chipset_family = ATA_100;
}
}
@@ -441,8 +448,7 @@ static int __devinit sis_find_family(struct pci_dev *dev)
return chipset_family;
}
-static unsigned int __devinit init_chipset_sis5513(struct pci_dev *dev,
- const char *name)
+static unsigned int __devinit init_chipset_sis5513(struct pci_dev *dev)
{
/* Make general config ops here
1/ tell IDE channels to operate in Compatibility mode only
@@ -555,7 +561,7 @@ static const struct ide_port_ops sis_ata133_port_ops = {
};
static const struct ide_port_info sis5513_chipset __devinitdata = {
- .name = "SIS5513",
+ .name = DRV_NAME,
.init_chipset = init_chipset_sis5513,
.enablebits = { {0x4a, 0x02, 0x02}, {0x4a, 0x04, 0x04} },
.host_flags = IDE_HFLAG_LEGACY_IRQS | IDE_HFLAG_NO_AUTODMA,
@@ -583,7 +589,13 @@ static int __devinit sis5513_init_one(struct pci_dev *dev, const struct pci_devi
d.udma_mask = udma_rates[chipset_family];
- return ide_setup_pci_device(dev, &d);
+ return ide_pci_init_one(dev, &d, NULL);
+}
+
+static void __devexit sis5513_remove(struct pci_dev *dev)
+{
+ ide_pci_remove(dev);
+ pci_disable_device(dev);
}
static const struct pci_device_id sis5513_pci_tbl[] = {
@@ -598,6 +610,7 @@ static struct pci_driver driver = {
.name = "SIS_IDE",
.id_table = sis5513_pci_tbl,
.probe = sis5513_init_one,
+ .remove = sis5513_remove,
};
static int __init sis5513_ide_init(void)
@@ -605,7 +618,13 @@ static int __init sis5513_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit sis5513_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(sis5513_ide_init);
+module_exit(sis5513_ide_exit);
MODULE_AUTHOR("Lionel Bouton, L C Chang, Andre Hedrick, Vojtech Pavlik");
MODULE_DESCRIPTION("PCI driver module for SIS IDE");
diff --git a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c
index f82a6502c1b..73905bcc08f 100644
--- a/drivers/ide/pci/sl82c105.c
+++ b/drivers/ide/pci/sl82c105.c
@@ -23,6 +23,8 @@
#include <asm/io.h>
+#define DRV_NAME "sl82c105"
+
#undef DEBUG
#ifdef DEBUG
@@ -270,7 +272,7 @@ static u8 sl82c105_bridge_revision(struct pci_dev *dev)
* channel 0 here at least, but channel 1 has to be enabled by
* firmware or arch code. We still set both to 16 bits mode.
*/
-static unsigned int __devinit init_chipset_sl82c105(struct pci_dev *dev, const char *msg)
+static unsigned int __devinit init_chipset_sl82c105(struct pci_dev *dev)
{
u32 val;
@@ -301,7 +303,7 @@ static const struct ide_dma_ops sl82c105_dma_ops = {
};
static const struct ide_port_info sl82c105_chipset __devinitdata = {
- .name = "W82C105",
+ .name = DRV_NAME,
.init_chipset = init_chipset_sl82c105,
.enablebits = {{0x40,0x01,0x01}, {0x40,0x10,0x10}},
.port_ops = &sl82c105_port_ops,
@@ -328,14 +330,14 @@ static int __devinit sl82c105_init_one(struct pci_dev *dev, const struct pci_dev
* Never ever EVER under any circumstances enable
* DMA when the bridge is this old.
*/
- printk(KERN_INFO "W82C105_IDE: Winbond W83C553 bridge "
+ printk(KERN_INFO DRV_NAME ": Winbond W83C553 bridge "
"revision %d, BM-DMA disabled\n", rev);
d.dma_ops = NULL;
d.mwdma_mask = 0;
d.host_flags &= ~IDE_HFLAG_SERIALIZE_DMA;
}
- return ide_setup_pci_device(dev, &d);
+ return ide_pci_init_one(dev, &d, NULL);
}
static const struct pci_device_id sl82c105_pci_tbl[] = {
@@ -348,6 +350,7 @@ static struct pci_driver driver = {
.name = "W82C105_IDE",
.id_table = sl82c105_pci_tbl,
.probe = sl82c105_init_one,
+ .remove = ide_pci_remove,
};
static int __init sl82c105_ide_init(void)
@@ -355,7 +358,13 @@ static int __init sl82c105_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit sl82c105_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(sl82c105_ide_init);
+module_exit(sl82c105_ide_exit);
MODULE_DESCRIPTION("PCI driver module for W82C105 IDE");
MODULE_LICENSE("GPL");
diff --git a/drivers/ide/pci/slc90e66.c b/drivers/ide/pci/slc90e66.c
index dae6e2c94d8..13d1fa491f2 100644
--- a/drivers/ide/pci/slc90e66.c
+++ b/drivers/ide/pci/slc90e66.c
@@ -15,6 +15,8 @@
#include <linux/ide.h>
#include <linux/init.h>
+#define DRV_NAME "slc90e66"
+
static DEFINE_SPINLOCK(slc90e66_lock);
static void slc90e66_set_pio_mode(ide_drive_t *drive, const u8 pio)
@@ -132,7 +134,7 @@ static const struct ide_port_ops slc90e66_port_ops = {
};
static const struct ide_port_info slc90e66_chipset __devinitdata = {
- .name = "SLC90E66",
+ .name = DRV_NAME,
.enablebits = { {0x41, 0x80, 0x80}, {0x43, 0x80, 0x80} },
.port_ops = &slc90e66_port_ops,
.host_flags = IDE_HFLAG_LEGACY_IRQS,
@@ -144,7 +146,7 @@ static const struct ide_port_info slc90e66_chipset __devinitdata = {
static int __devinit slc90e66_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
- return ide_setup_pci_device(dev, &slc90e66_chipset);
+ return ide_pci_init_one(dev, &slc90e66_chipset, NULL);
}
static const struct pci_device_id slc90e66_pci_tbl[] = {
@@ -157,6 +159,7 @@ static struct pci_driver driver = {
.name = "SLC90e66_IDE",
.id_table = slc90e66_pci_tbl,
.probe = slc90e66_init_one,
+ .remove = ide_pci_remove,
};
static int __init slc90e66_ide_init(void)
@@ -164,7 +167,13 @@ static int __init slc90e66_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit slc90e66_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(slc90e66_ide_init);
+module_exit(slc90e66_ide_exit);
MODULE_AUTHOR("Andre Hedrick");
MODULE_DESCRIPTION("PCI driver module for SLC90E66 IDE");
diff --git a/drivers/ide/pci/tc86c001.c b/drivers/ide/pci/tc86c001.c
index 477e1979010..b1cb8a9ce5a 100644
--- a/drivers/ide/pci/tc86c001.c
+++ b/drivers/ide/pci/tc86c001.c
@@ -11,6 +11,8 @@
#include <linux/pci.h>
#include <linux/ide.h>
+#define DRV_NAME "tc86c001"
+
static void tc86c001_set_mode(ide_drive_t *drive, const u8 speed)
{
ide_hwif_t *hwif = HWIF(drive);
@@ -173,16 +175,6 @@ static void __devinit init_hwif_tc86c001(ide_hwif_t *hwif)
hwif->rqsize = 0xffff;
}
-static unsigned int __devinit init_chipset_tc86c001(struct pci_dev *dev,
- const char *name)
-{
- int err = pci_request_region(dev, 5, name);
-
- if (err)
- printk(KERN_ERR "%s: system control regs already in use", name);
- return err;
-}
-
static const struct ide_port_ops tc86c001_port_ops = {
.set_pio_mode = tc86c001_set_pio_mode,
.set_dma_mode = tc86c001_set_mode,
@@ -201,8 +193,7 @@ static const struct ide_dma_ops tc86c001_dma_ops = {
};
static const struct ide_port_info tc86c001_chipset __devinitdata = {
- .name = "TC86C001",
- .init_chipset = init_chipset_tc86c001,
+ .name = DRV_NAME,
.init_hwif = init_hwif_tc86c001,
.port_ops = &tc86c001_port_ops,
.dma_ops = &tc86c001_dma_ops,
@@ -215,7 +206,37 @@ static const struct ide_port_info tc86c001_chipset __devinitdata = {
static int __devinit tc86c001_init_one(struct pci_dev *dev,
const struct pci_device_id *id)
{
- return ide_setup_pci_device(dev, &tc86c001_chipset);
+ int rc;
+
+ rc = pci_enable_device(dev);
+ if (rc)
+ goto out;
+
+ rc = pci_request_region(dev, 5, DRV_NAME);
+ if (rc) {
+ printk(KERN_ERR DRV_NAME ": system control regs already in use");
+ goto out_disable;
+ }
+
+ rc = ide_pci_init_one(dev, &tc86c001_chipset, NULL);
+ if (rc)
+ goto out_release;
+
+ goto out;
+
+out_release:
+ pci_release_region(dev, 5);
+out_disable:
+ pci_disable_device(dev);
+out:
+ return rc;
+}
+
+static void __devexit tc86c001_remove(struct pci_dev *dev)
+{
+ ide_pci_remove(dev);
+ pci_release_region(dev, 5);
+ pci_disable_device(dev);
}
static const struct pci_device_id tc86c001_pci_tbl[] = {
@@ -227,14 +248,22 @@ MODULE_DEVICE_TABLE(pci, tc86c001_pci_tbl);
static struct pci_driver driver = {
.name = "TC86C001",
.id_table = tc86c001_pci_tbl,
- .probe = tc86c001_init_one
+ .probe = tc86c001_init_one,
+ .remove = tc86c001_remove,
};
static int __init tc86c001_ide_init(void)
{
return ide_pci_register_driver(&driver);
}
+
+static void __exit tc86c001_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(tc86c001_ide_init);
+module_exit(tc86c001_ide_exit);
MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>");
MODULE_DESCRIPTION("PCI driver module for TC86C001 IDE");
diff --git a/drivers/ide/pci/triflex.c b/drivers/ide/pci/triflex.c
index db65a558d4e..b77ec35151b 100644
--- a/drivers/ide/pci/triflex.c
+++ b/drivers/ide/pci/triflex.c
@@ -33,6 +33,8 @@
#include <linux/ide.h>
#include <linux/init.h>
+#define DRV_NAME "triflex"
+
static void triflex_set_mode(ide_drive_t *drive, const u8 speed)
{
ide_hwif_t *hwif = HWIF(drive);
@@ -93,7 +95,7 @@ static const struct ide_port_ops triflex_port_ops = {
};
static const struct ide_port_info triflex_device __devinitdata = {
- .name = "TRIFLEX",
+ .name = DRV_NAME,
.enablebits = {{0x80, 0x01, 0x01}, {0x80, 0x02, 0x02}},
.port_ops = &triflex_port_ops,
.pio_mask = ATA_PIO4,
@@ -104,7 +106,7 @@ static const struct ide_port_info triflex_device __devinitdata = {
static int __devinit triflex_init_one(struct pci_dev *dev,
const struct pci_device_id *id)
{
- return ide_setup_pci_device(dev, &triflex_device);
+ return ide_pci_init_one(dev, &triflex_device, NULL);
}
static const struct pci_device_id triflex_pci_tbl[] = {
@@ -117,6 +119,7 @@ static struct pci_driver driver = {
.name = "TRIFLEX_IDE",
.id_table = triflex_pci_tbl,
.probe = triflex_init_one,
+ .remove = ide_pci_remove,
};
static int __init triflex_ide_init(void)
@@ -124,7 +127,13 @@ static int __init triflex_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit triflex_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(triflex_ide_init);
+module_exit(triflex_ide_exit);
MODULE_AUTHOR("Torben Mathiasen");
MODULE_DESCRIPTION("PCI driver module for Compaq Triflex IDE");
diff --git a/drivers/ide/pci/trm290.c b/drivers/ide/pci/trm290.c
index a8a3138682e..fd28b49977f 100644
--- a/drivers/ide/pci/trm290.c
+++ b/drivers/ide/pci/trm290.c
@@ -141,6 +141,8 @@
#include <asm/io.h>
+#define DRV_NAME "trm290"
+
static void trm290_prepare_drive (ide_drive_t *drive, unsigned int use_dma)
{
ide_hwif_t *hwif = HWIF(drive);
@@ -245,10 +247,10 @@ static void __devinit init_hwif_trm290(ide_hwif_t *hwif)
u8 reg = 0;
if ((dev->class & 5) && cfg_base)
- printk(KERN_INFO "TRM290: chip");
+ printk(KERN_INFO DRV_NAME " %s: chip", pci_name(dev));
else {
cfg_base = 0x3df0;
- printk(KERN_INFO "TRM290: using default");
+ printk(KERN_INFO DRV_NAME " %s: using default", pci_name(dev));
}
printk(KERN_CONT " config base at 0x%04x\n", cfg_base);
hwif->config_data = cfg_base;
@@ -325,7 +327,7 @@ static struct ide_dma_ops trm290_dma_ops = {
};
static const struct ide_port_info trm290_chipset __devinitdata = {
- .name = "TRM290",
+ .name = DRV_NAME,
.init_hwif = init_hwif_trm290,
.chipset = ide_trm290,
.port_ops = &trm290_port_ops,
@@ -340,7 +342,7 @@ static const struct ide_port_info trm290_chipset __devinitdata = {
static int __devinit trm290_init_one(struct pci_dev *dev, const struct pci_device_id *id)
{
- return ide_setup_pci_device(dev, &trm290_chipset);
+ return ide_pci_init_one(dev, &trm290_chipset, NULL);
}
static const struct pci_device_id trm290_pci_tbl[] = {
@@ -353,6 +355,7 @@ static struct pci_driver driver = {
.name = "TRM290_IDE",
.id_table = trm290_pci_tbl,
.probe = trm290_init_one,
+ .remove = ide_pci_remove,
};
static int __init trm290_ide_init(void)
@@ -360,7 +363,13 @@ static int __init trm290_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit trm290_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(trm290_ide_init);
+module_exit(trm290_ide_exit);
MODULE_AUTHOR("Mark Lord");
MODULE_DESCRIPTION("PCI driver module for Tekram TRM290 IDE");
diff --git a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c
index 09dc4803ef9..454d2bf62dc 100644
--- a/drivers/ide/pci/via82cxxx.c
+++ b/drivers/ide/pci/via82cxxx.c
@@ -35,6 +35,8 @@
#include <asm/processor.h>
#endif
+#define DRV_NAME "via82cxxx"
+
#define VIA_IDE_ENABLE 0x40
#define VIA_IDE_CONFIG 0x41
#define VIA_FIFO_CONFIG 0x43
@@ -113,7 +115,8 @@ struct via82cxxx_dev
static void via_set_speed(ide_hwif_t *hwif, u8 dn, struct ide_timing *timing)
{
struct pci_dev *dev = to_pci_dev(hwif->dev);
- struct via82cxxx_dev *vdev = pci_get_drvdata(dev);
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct via82cxxx_dev *vdev = host->host_priv;
u8 t;
if (~vdev->via_config->flags & VIA_BAD_AST) {
@@ -153,7 +156,8 @@ static void via_set_drive(ide_drive_t *drive, const u8 speed)
ide_hwif_t *hwif = drive->hwif;
ide_drive_t *peer = hwif->drives + (~drive->dn & 1);
struct pci_dev *dev = to_pci_dev(hwif->dev);
- struct via82cxxx_dev *vdev = pci_get_drvdata(dev);
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct via82cxxx_dev *vdev = host->host_priv;
struct ide_timing t, p;
unsigned int T, UT;
@@ -258,37 +262,19 @@ static void __devinit via_cable_detect(struct via82cxxx_dev *vdev, u32 u)
/**
* init_chipset_via82cxxx - initialization handler
* @dev: PCI device
- * @name: Name of interface
*
* The initialization callback. Here we determine the IDE chip type
* and initialize its drive independent registers.
*/
-static unsigned int __devinit init_chipset_via82cxxx(struct pci_dev *dev, const char *name)
+static unsigned int __devinit init_chipset_via82cxxx(struct pci_dev *dev)
{
- struct pci_dev *isa = NULL;
- struct via82cxxx_dev *vdev;
- struct via_isa_bridge *via_config;
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct via82cxxx_dev *vdev = host->host_priv;
+ struct via_isa_bridge *via_config = vdev->via_config;
u8 t, v;
u32 u;
- vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
- if (!vdev) {
- printk(KERN_ERR "VP_IDE: out of memory :(\n");
- return -ENOMEM;
- }
- pci_set_drvdata(dev, vdev);
-
- /*
- * Find the ISA bridge to see how good the IDE is.
- */
- vdev->via_config = via_config = via_config_find(&isa);
-
- /* We checked this earlier so if it fails here deeep badness
- is involved */
-
- BUG_ON(!via_config->id);
-
/*
* Detect cable and configure Clk66
*/
@@ -334,39 +320,6 @@ static unsigned int __devinit init_chipset_via82cxxx(struct pci_dev *dev, const
pci_write_config_byte(dev, VIA_FIFO_CONFIG, t);
- /*
- * Determine system bus clock.
- */
-
- via_clock = (ide_pci_clk ? ide_pci_clk : 33) * 1000;
-
- switch (via_clock) {
- case 33000: via_clock = 33333; break;
- case 37000: via_clock = 37500; break;
- case 41000: via_clock = 41666; break;
- }
-
- if (via_clock < 20000 || via_clock > 50000) {
- printk(KERN_WARNING "VP_IDE: User given PCI clock speed "
- "impossible (%d), using 33 MHz instead.\n", via_clock);
- printk(KERN_WARNING "VP_IDE: Use ide0=ata66 if you want "
- "to assume 80-wire cable.\n");
- via_clock = 33333;
- }
-
- /*
- * Print the boot message.
- */
-
- printk(KERN_INFO "VP_IDE: VIA %s (rev %02x) IDE %sDMA%s "
- "controller on pci%s\n",
- via_config->name, isa->revision,
- via_config->udma_mask ? "U" : "MW",
- via_dma[via_config->udma_mask ?
- (fls(via_config->udma_mask) - 1) : 0],
- pci_name(dev));
-
- pci_dev_put(isa);
return 0;
}
@@ -402,7 +355,8 @@ static int via_cable_override(struct pci_dev *pdev)
static u8 __devinit via82cxxx_cable_detect(ide_hwif_t *hwif)
{
struct pci_dev *pdev = to_pci_dev(hwif->dev);
- struct via82cxxx_dev *vdev = pci_get_drvdata(pdev);
+ struct ide_host *host = pci_get_drvdata(pdev);
+ struct via82cxxx_dev *vdev = host->host_priv;
if (via_cable_override(pdev))
return ATA_CBL_PATA40_SHORT;
@@ -420,7 +374,7 @@ static const struct ide_port_ops via_port_ops = {
};
static const struct ide_port_info via82cxxx_chipset __devinitdata = {
- .name = "VP_IDE",
+ .name = DRV_NAME,
.init_chipset = init_chipset_via82cxxx,
.enablebits = { { 0x40, 0x02, 0x02 }, { 0x40, 0x01, 0x01 } },
.port_ops = &via_port_ops,
@@ -436,6 +390,8 @@ static int __devinit via_init_one(struct pci_dev *dev, const struct pci_device_i
{
struct pci_dev *isa = NULL;
struct via_isa_bridge *via_config;
+ struct via82cxxx_dev *vdev;
+ int rc;
u8 idx = id->driver_data;
struct ide_port_info d;
@@ -445,12 +401,42 @@ static int __devinit via_init_one(struct pci_dev *dev, const struct pci_device_i
* Find the ISA bridge and check we know what it is.
*/
via_config = via_config_find(&isa);
- pci_dev_put(isa);
if (!via_config->id) {
- printk(KERN_WARNING "VP_IDE: Unknown VIA SouthBridge, disabling DMA.\n");
+ printk(KERN_WARNING DRV_NAME " %s: unknown chipset, skipping\n",
+ pci_name(dev));
return -ENODEV;
}
+ /*
+ * Print the boot message.
+ */
+ printk(KERN_INFO DRV_NAME " %s: VIA %s (rev %02x) IDE %sDMA%s\n",
+ pci_name(dev), via_config->name, isa->revision,
+ via_config->udma_mask ? "U" : "MW",
+ via_dma[via_config->udma_mask ?
+ (fls(via_config->udma_mask) - 1) : 0]);
+
+ pci_dev_put(isa);
+
+ /*
+ * Determine system bus clock.
+ */
+ via_clock = (ide_pci_clk ? ide_pci_clk : 33) * 1000;
+
+ switch (via_clock) {
+ case 33000: via_clock = 33333; break;
+ case 37000: via_clock = 37500; break;
+ case 41000: via_clock = 41666; break;
+ }
+
+ if (via_clock < 20000 || via_clock > 50000) {
+ printk(KERN_WARNING DRV_NAME ": User given PCI clock speed "
+ "impossible (%d), using 33 MHz instead.\n", via_clock);
+ printk(KERN_WARNING DRV_NAME ": Use ide0=ata66 if you want "
+ "to assume 80-wire cable.\n");
+ via_clock = 33333;
+ }
+
if (idx == 0)
d.host_flags |= IDE_HFLAG_NO_AUTODMA;
else
@@ -466,7 +452,29 @@ static int __devinit via_init_one(struct pci_dev *dev, const struct pci_device_i
d.udma_mask = via_config->udma_mask;
- return ide_setup_pci_device(dev, &d);
+ vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
+ if (!vdev) {
+ printk(KERN_ERR DRV_NAME " %s: out of memory :(\n",
+ pci_name(dev));
+ return -ENOMEM;
+ }
+
+ vdev->via_config = via_config;
+
+ rc = ide_pci_init_one(dev, &d, vdev);
+ if (rc)
+ kfree(vdev);
+
+ return rc;
+}
+
+static void __devexit via_remove(struct pci_dev *dev)
+{
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct via82cxxx_dev *vdev = host->host_priv;
+
+ ide_pci_remove(dev);
+ kfree(vdev);
}
static const struct pci_device_id via_pci_tbl[] = {
@@ -483,6 +491,7 @@ static struct pci_driver driver = {
.name = "VIA_IDE",
.id_table = via_pci_tbl,
.probe = via_init_one,
+ .remove = via_remove,
};
static int __init via_ide_init(void)
@@ -490,7 +499,13 @@ static int __init via_ide_init(void)
return ide_pci_register_driver(&driver);
}
+static void __exit via_ide_exit(void)
+{
+ pci_unregister_driver(&driver);
+}
+
module_init(via_ide_init);
+module_exit(via_ide_exit);
MODULE_AUTHOR("Vojtech Pavlik, Michel Aubry, Jeff Garzik, Andre Hedrick");
MODULE_DESCRIPTION("PCI driver module for VIA IDE");
diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c
index b15cad58dc8..a8e9e8a69a5 100644
--- a/drivers/ide/setup-pci.c
+++ b/drivers/ide/setup-pci.c
@@ -39,17 +39,18 @@ static int ide_setup_pci_baseregs(struct pci_dev *dev, const char *name)
if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) ||
(progif & 5) != 5) {
if ((progif & 0xa) != 0xa) {
- printk(KERN_INFO "%s: device not capable of full "
- "native PCI mode\n", name);
+ printk(KERN_INFO "%s %s: device not capable of full "
+ "native PCI mode\n", name, pci_name(dev));
return -EOPNOTSUPP;
}
- printk("%s: placing both ports into native PCI mode\n", name);
+ printk(KERN_INFO "%s %s: placing both ports into native PCI "
+ "mode\n", name, pci_name(dev));
(void) pci_write_config_byte(dev, PCI_CLASS_PROG, progif|5);
if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) ||
(progif & 5) != 5) {
- printk(KERN_ERR "%s: rewrite of PROGIF failed, wanted "
- "0x%04x, got 0x%04x\n",
- name, progif|5, progif);
+ printk(KERN_ERR "%s %s: rewrite of PROGIF failed, "
+ "wanted 0x%04x, got 0x%04x\n",
+ name, pci_name(dev), progif | 5, progif);
return -EOPNOTSUPP;
}
}
@@ -57,14 +58,14 @@ static int ide_setup_pci_baseregs(struct pci_dev *dev, const char *name)
}
#ifdef CONFIG_BLK_DEV_IDEDMA_PCI
-static void ide_pci_clear_simplex(unsigned long dma_base, const char *name)
+static int ide_pci_clear_simplex(unsigned long dma_base, const char *name)
{
u8 dma_stat = inb(dma_base + 2);
outb(dma_stat & 0x60, dma_base + 2);
dma_stat = inb(dma_base + 2);
- if (dma_stat & 0x80)
- printk(KERN_INFO "%s: simplex device: DMA forced\n", name);
+
+ return (dma_stat & 0x80) ? 1 : 0;
}
/**
@@ -91,7 +92,8 @@ unsigned long ide_pci_dma_base(ide_hwif_t *hwif, const struct ide_port_info *d)
dma_base = pci_resource_start(dev, baridx);
if (dma_base == 0) {
- printk(KERN_ERR "%s: DMA base is invalid\n", d->name);
+ printk(KERN_ERR "%s %s: DMA base is invalid\n",
+ d->name, pci_name(dev));
return 0;
}
}
@@ -105,13 +107,16 @@ EXPORT_SYMBOL_GPL(ide_pci_dma_base);
int ide_pci_check_simplex(ide_hwif_t *hwif, const struct ide_port_info *d)
{
+ struct pci_dev *dev = to_pci_dev(hwif->dev);
u8 dma_stat;
if (d->host_flags & (IDE_HFLAG_MMIO | IDE_HFLAG_CS5520))
goto out;
if (d->host_flags & IDE_HFLAG_CLEAR_SIMPLEX) {
- ide_pci_clear_simplex(hwif->dma_base, d->name);
+ if (ide_pci_clear_simplex(hwif->dma_base, d->name))
+ printk(KERN_INFO "%s %s: simplex device: DMA forced\n",
+ d->name, pci_name(dev));
goto out;
}
@@ -127,7 +132,8 @@ int ide_pci_check_simplex(ide_hwif_t *hwif, const struct ide_port_info *d)
*/
dma_stat = hwif->tp_ops->read_sff_dma_status(hwif);
if ((dma_stat & 0x80) && hwif->mate && hwif->mate->dma_base) {
- printk(KERN_INFO "%s: simplex device: DMA disabled\n", d->name);
+ printk(KERN_INFO "%s %s: simplex device: DMA disabled\n",
+ d->name, pci_name(dev));
return -1;
}
out:
@@ -149,8 +155,8 @@ int ide_pci_set_master(struct pci_dev *dev, const char *name)
if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd) ||
(pcicmd & PCI_COMMAND_MASTER) == 0) {
- printk(KERN_ERR "%s: error updating PCICMD on %s\n",
- name, pci_name(dev));
+ printk(KERN_ERR "%s %s: error updating PCICMD\n",
+ name, pci_name(dev));
return -EIO;
}
}
@@ -162,9 +168,9 @@ EXPORT_SYMBOL_GPL(ide_pci_set_master);
void ide_setup_pci_noise(struct pci_dev *dev, const struct ide_port_info *d)
{
- printk(KERN_INFO "%s: IDE controller (0x%04x:0x%04x rev 0x%02x) at "
- " PCI slot %s\n", d->name, dev->vendor, dev->device,
- dev->revision, pci_name(dev));
+ printk(KERN_INFO "%s %s: IDE controller (0x%04x:0x%04x rev 0x%02x)\n",
+ d->name, pci_name(dev),
+ dev->vendor, dev->device, dev->revision);
}
EXPORT_SYMBOL_GPL(ide_setup_pci_noise);
@@ -189,11 +195,12 @@ static int ide_pci_enable(struct pci_dev *dev, const struct ide_port_info *d)
if (pci_enable_device(dev)) {
ret = pci_enable_device_io(dev);
if (ret < 0) {
- printk(KERN_WARNING "%s: (ide_setup_pci_device:) "
- "Could not enable device.\n", d->name);
+ printk(KERN_WARNING "%s %s: couldn't enable device\n",
+ d->name, pci_name(dev));
goto out;
}
- printk(KERN_WARNING "%s: BIOS configuration fixed.\n", d->name);
+ printk(KERN_WARNING "%s %s: BIOS configuration fixed\n",
+ d->name, pci_name(dev));
}
/*
@@ -203,7 +210,8 @@ static int ide_pci_enable(struct pci_dev *dev, const struct ide_port_info *d)
*/
ret = pci_set_dma_mask(dev, DMA_32BIT_MASK);
if (ret < 0) {
- printk(KERN_ERR "%s: can't set dma mask\n", d->name);
+ printk(KERN_ERR "%s %s: can't set DMA mask\n",
+ d->name, pci_name(dev));
goto out;
}
@@ -221,7 +229,8 @@ static int ide_pci_enable(struct pci_dev *dev, const struct ide_port_info *d)
ret = pci_request_selected_regions(dev, bars, d->name);
if (ret < 0)
- printk(KERN_ERR "%s: can't reserve resources\n", d->name);
+ printk(KERN_ERR "%s %s: can't reserve resources\n",
+ d->name, pci_name(dev));
out:
return ret;
}
@@ -247,15 +256,18 @@ static int ide_pci_configure(struct pci_dev *dev, const struct ide_port_info *d)
*/
if (ide_setup_pci_baseregs(dev, d->name) ||
pci_write_config_word(dev, PCI_COMMAND, pcicmd | PCI_COMMAND_IO)) {
- printk(KERN_INFO "%s: device disabled (BIOS)\n", d->name);
+ printk(KERN_INFO "%s %s: device disabled (BIOS)\n",
+ d->name, pci_name(dev));
return -ENODEV;
}
if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd)) {
- printk(KERN_ERR "%s: error accessing PCI regs\n", d->name);
+ printk(KERN_ERR "%s %s: error accessing PCI regs\n",
+ d->name, pci_name(dev));
return -EIO;
}
if (!(pcicmd & PCI_COMMAND_IO)) {
- printk(KERN_ERR "%s: unable to enable IDE controller\n", d->name);
+ printk(KERN_ERR "%s %s: unable to enable IDE controller\n",
+ d->name, pci_name(dev));
return -ENXIO;
}
return 0;
@@ -311,8 +323,9 @@ static int ide_hw_configure(struct pci_dev *dev, const struct ide_port_info *d,
if ((d->host_flags & IDE_HFLAG_ISA_PORTS) == 0) {
if (ide_pci_check_iomem(dev, d, 2 * port) ||
ide_pci_check_iomem(dev, d, 2 * port + 1)) {
- printk(KERN_ERR "%s: I/O baseregs (BIOS) are reported "
- "as MEM for port %d!\n", d->name, port);
+ printk(KERN_ERR "%s %s: I/O baseregs (BIOS) are "
+ "reported as MEM for port %d!\n",
+ d->name, pci_name(dev), port);
return -EINVAL;
}
@@ -325,8 +338,8 @@ static int ide_hw_configure(struct pci_dev *dev, const struct ide_port_info *d,
}
if (!base || !ctl) {
- printk(KERN_ERR "%s: bad PCI BARs for port %d, skipping\n",
- d->name, port);
+ printk(KERN_ERR "%s %s: bad PCI BARs for port %d, skipping\n",
+ d->name, pci_name(dev), port);
return -EINVAL;
}
@@ -393,14 +406,14 @@ int ide_hwif_setup_dma(ide_hwif_t *hwif, const struct ide_port_info *d)
* @dev: PCI device
* @d: IDE port info
* @noisy: verbose flag
- * @config: returned as 1 if we configured the hardware
*
* Set up the PCI and controller side of the IDE interface. This brings
* up the PCI side of the device, checks that the device is enabled
* and enables it if need be
*/
-static int ide_setup_pci_controller(struct pci_dev *dev, const struct ide_port_info *d, int noisy, int *config)
+static int ide_setup_pci_controller(struct pci_dev *dev,
+ const struct ide_port_info *d, int noisy)
{
int ret;
u16 pcicmd;
@@ -414,15 +427,16 @@ static int ide_setup_pci_controller(struct pci_dev *dev, const struct ide_port_i
ret = pci_read_config_word(dev, PCI_COMMAND, &pcicmd);
if (ret < 0) {
- printk(KERN_ERR "%s: error accessing PCI regs\n", d->name);
+ printk(KERN_ERR "%s %s: error accessing PCI regs\n",
+ d->name, pci_name(dev));
goto out;
}
if (!(pcicmd & PCI_COMMAND_IO)) { /* is device disabled? */
ret = ide_pci_configure(dev, d);
if (ret < 0)
goto out;
- *config = 1;
- printk(KERN_INFO "%s: device enabled (Linux)\n", d->name);
+ printk(KERN_INFO "%s %s: device enabled (Linux)\n",
+ d->name, pci_name(dev));
}
out:
@@ -461,7 +475,8 @@ void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d,
if (e->reg && (pci_read_config_byte(dev, e->reg, &tmp) ||
(tmp & e->mask) != e->val)) {
- printk(KERN_INFO "%s: IDE port disabled\n", d->name);
+ printk(KERN_INFO "%s %s: IDE port disabled\n",
+ d->name, pci_name(dev));
continue; /* port not enabled */
}
@@ -487,51 +502,35 @@ static int do_ide_setup_pci_device(struct pci_dev *dev,
const struct ide_port_info *d,
u8 noisy)
{
- int tried_config = 0;
int pciirq, ret;
- ret = ide_setup_pci_controller(dev, d, noisy, &tried_config);
- if (ret < 0)
- goto out;
-
/*
* Can we trust the reported IRQ?
*/
pciirq = dev->irq;
+ /*
+ * This allows offboard ide-pci cards the enable a BIOS,
+ * verify interrupt settings of split-mirror pci-config
+ * space, place chipset into init-mode, and/or preserve
+ * an interrupt if the card is not native ide support.
+ */
+ ret = d->init_chipset ? d->init_chipset(dev) : 0;
+ if (ret < 0)
+ goto out;
+
/* Is it an "IDE storage" device in non-PCI mode? */
if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && (dev->class & 5) != 5) {
if (noisy)
- printk(KERN_INFO "%s: not 100%% native mode: "
- "will probe irqs later\n", d->name);
- /*
- * This allows offboard ide-pci cards the enable a BIOS,
- * verify interrupt settings of split-mirror pci-config
- * space, place chipset into init-mode, and/or preserve
- * an interrupt if the card is not native ide support.
- */
- ret = d->init_chipset ? d->init_chipset(dev, d->name) : 0;
- if (ret < 0)
- goto out;
+ printk(KERN_INFO "%s %s: not 100%% native mode: will "
+ "probe irqs later\n", d->name, pci_name(dev));
pciirq = ret;
- } else if (tried_config) {
- if (noisy)
- printk(KERN_INFO "%s: will probe irqs later\n", d->name);
- pciirq = 0;
- } else if (!pciirq) {
- if (noisy)
- printk(KERN_WARNING "%s: bad irq (%d): will probe later\n",
- d->name, pciirq);
- pciirq = 0;
- } else {
- if (d->init_chipset) {
- ret = d->init_chipset(dev, d->name);
- if (ret < 0)
- goto out;
- }
- if (noisy)
- printk(KERN_INFO "%s: 100%% native mode on irq %d\n",
- d->name, pciirq);
+ } else if (!pciirq && noisy) {
+ printk(KERN_WARNING "%s %s: bad irq (%d): will probe later\n",
+ d->name, pci_name(dev), pciirq);
+ } else if (noisy) {
+ printk(KERN_INFO "%s %s: 100%% native mode on irq %d\n",
+ d->name, pci_name(dev), pciirq);
}
ret = pciirq;
@@ -539,32 +538,77 @@ out:
return ret;
}
-int ide_setup_pci_device(struct pci_dev *dev, const struct ide_port_info *d)
+int ide_pci_init_one(struct pci_dev *dev, const struct ide_port_info *d,
+ void *priv)
{
+ struct ide_host *host;
hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL };
int ret;
- ret = do_ide_setup_pci_device(dev, d, 1);
+ ret = ide_setup_pci_controller(dev, d, 1);
+ if (ret < 0)
+ goto out;
- if (ret >= 0) {
- /* FIXME: silent failure can happen */
- ide_pci_setup_ports(dev, d, ret, &hw[0], &hws[0]);
+ ide_pci_setup_ports(dev, d, 0, &hw[0], &hws[0]);
- ret = ide_host_add(d, hws, NULL);
+ host = ide_host_alloc(d, hws);
+ if (host == NULL) {
+ ret = -ENOMEM;
+ goto out;
}
+ host->dev[0] = &dev->dev;
+
+ host->host_priv = priv;
+
+ pci_set_drvdata(dev, host);
+
+ ret = do_ide_setup_pci_device(dev, d, 1);
+ if (ret < 0)
+ goto out;
+
+ /* fixup IRQ */
+ hw[1].irq = hw[0].irq = ret;
+
+ ret = ide_host_register(host, d, hws);
+ if (ret)
+ ide_host_free(host);
+out:
return ret;
}
-EXPORT_SYMBOL_GPL(ide_setup_pci_device);
+EXPORT_SYMBOL_GPL(ide_pci_init_one);
-int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2,
- const struct ide_port_info *d)
+int ide_pci_init_two(struct pci_dev *dev1, struct pci_dev *dev2,
+ const struct ide_port_info *d, void *priv)
{
struct pci_dev *pdev[] = { dev1, dev2 };
+ struct ide_host *host;
int ret, i;
hw_regs_t hw[4], *hws[] = { NULL, NULL, NULL, NULL };
for (i = 0; i < 2; i++) {
+ ret = ide_setup_pci_controller(pdev[i], d, !i);
+ if (ret < 0)
+ goto out;
+
+ ide_pci_setup_ports(pdev[i], d, 0, &hw[i*2], &hws[i*2]);
+ }
+
+ host = ide_host_alloc(d, hws);
+ if (host == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ host->dev[0] = &dev1->dev;
+ host->dev[1] = &dev2->dev;
+
+ host->host_priv = priv;
+
+ pci_set_drvdata(pdev[0], host);
+ pci_set_drvdata(pdev[1], host);
+
+ for (i = 0; i < 2; i++) {
ret = do_ide_setup_pci_device(pdev[i], d, !i);
/*
@@ -574,12 +618,44 @@ int ide_setup_pci_devices(struct pci_dev *dev1, struct pci_dev *dev2,
if (ret < 0)
goto out;
- /* FIXME: silent failure can happen */
- ide_pci_setup_ports(pdev[i], d, ret, &hw[i*2], &hws[i*2]);
+ /* fixup IRQ */
+ hw[i*2 + 1].irq = hw[i*2].irq = ret;
}
- ret = ide_host_add(d, hws, NULL);
+ ret = ide_host_register(host, d, hws);
+ if (ret)
+ ide_host_free(host);
out:
return ret;
}
-EXPORT_SYMBOL_GPL(ide_setup_pci_devices);
+EXPORT_SYMBOL_GPL(ide_pci_init_two);
+
+void ide_pci_remove(struct pci_dev *dev)
+{
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct pci_dev *dev2 = host->dev[1] ? to_pci_dev(host->dev[1]) : NULL;
+ int bars;
+
+ if (host->host_flags & IDE_HFLAG_SINGLE)
+ bars = (1 << 2) - 1;
+ else
+ bars = (1 << 4) - 1;
+
+ if ((host->host_flags & IDE_HFLAG_NO_DMA) == 0) {
+ if (host->host_flags & IDE_HFLAG_CS5520)
+ bars |= (1 << 2);
+ else
+ bars |= (1 << 4);
+ }
+
+ ide_host_remove(host);
+
+ if (dev2)
+ pci_release_selected_regions(dev2, bars);
+ pci_release_selected_regions(dev, bars);
+
+ if (dev2)
+ pci_disable_device(dev2);
+ pci_disable_device(dev);
+}
+EXPORT_SYMBOL_GPL(ide_pci_remove);
diff --git a/drivers/ieee1394/iso.c b/drivers/ieee1394/iso.c
index 07ca35c98f9..1cf6487b65b 100644
--- a/drivers/ieee1394/iso.c
+++ b/drivers/ieee1394/iso.c
@@ -11,6 +11,7 @@
#include <linux/pci.h>
#include <linux/sched.h>
+#include <linux/mm.h>
#include <linux/slab.h>
#include "hosts.h"
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index ae11d5cc74d..e980ff3335d 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -168,6 +168,12 @@ struct cma_work {
struct rdma_cm_event event;
};
+struct cma_ndev_work {
+ struct work_struct work;
+ struct rdma_id_private *id;
+ struct rdma_cm_event event;
+};
+
union cma_ip_addr {
struct in6_addr ip6;
struct {
@@ -914,7 +920,10 @@ static int cma_ib_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
struct rdma_cm_event event;
int ret = 0;
- if (cma_disable_callback(id_priv, CMA_CONNECT))
+ if ((ib_event->event != IB_CM_TIMEWAIT_EXIT &&
+ cma_disable_callback(id_priv, CMA_CONNECT)) ||
+ (ib_event->event == IB_CM_TIMEWAIT_EXIT &&
+ cma_disable_callback(id_priv, CMA_DISCONNECT)))
return 0;
memset(&event, 0, sizeof event);
@@ -950,6 +959,8 @@ static int cma_ib_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
event.event = RDMA_CM_EVENT_DISCONNECTED;
break;
case IB_CM_TIMEWAIT_EXIT:
+ event.event = RDMA_CM_EVENT_TIMEWAIT_EXIT;
+ break;
case IB_CM_MRA_RECEIVED:
/* ignore event */
goto out;
@@ -1598,6 +1609,30 @@ out:
kfree(work);
}
+static void cma_ndev_work_handler(struct work_struct *_work)
+{
+ struct cma_ndev_work *work = container_of(_work, struct cma_ndev_work, work);
+ struct rdma_id_private *id_priv = work->id;
+ int destroy = 0;
+
+ mutex_lock(&id_priv->handler_mutex);
+ if (id_priv->state == CMA_DESTROYING ||
+ id_priv->state == CMA_DEVICE_REMOVAL)
+ goto out;
+
+ if (id_priv->id.event_handler(&id_priv->id, &work->event)) {
+ cma_exch(id_priv, CMA_DESTROYING);
+ destroy = 1;
+ }
+
+out:
+ mutex_unlock(&id_priv->handler_mutex);
+ cma_deref_id(id_priv);
+ if (destroy)
+ rdma_destroy_id(&id_priv->id);
+ kfree(work);
+}
+
static int cma_resolve_ib_route(struct rdma_id_private *id_priv, int timeout_ms)
{
struct rdma_route *route = &id_priv->id.route;
@@ -2723,6 +2758,65 @@ void rdma_leave_multicast(struct rdma_cm_id *id, struct sockaddr *addr)
}
EXPORT_SYMBOL(rdma_leave_multicast);
+static int cma_netdev_change(struct net_device *ndev, struct rdma_id_private *id_priv)
+{
+ struct rdma_dev_addr *dev_addr;
+ struct cma_ndev_work *work;
+
+ dev_addr = &id_priv->id.route.addr.dev_addr;
+
+ if ((dev_addr->src_dev == ndev) &&
+ memcmp(dev_addr->src_dev_addr, ndev->dev_addr, ndev->addr_len)) {
+ printk(KERN_INFO "RDMA CM addr change for ndev %s used by id %p\n",
+ ndev->name, &id_priv->id);
+ work = kzalloc(sizeof *work, GFP_KERNEL);
+ if (!work)
+ return -ENOMEM;
+
+ INIT_WORK(&work->work, cma_ndev_work_handler);
+ work->id = id_priv;
+ work->event.event = RDMA_CM_EVENT_ADDR_CHANGE;
+ atomic_inc(&id_priv->refcount);
+ queue_work(cma_wq, &work->work);
+ }
+
+ return 0;
+}
+
+static int cma_netdev_callback(struct notifier_block *self, unsigned long event,
+ void *ctx)
+{
+ struct net_device *ndev = (struct net_device *)ctx;
+ struct cma_device *cma_dev;
+ struct rdma_id_private *id_priv;
+ int ret = NOTIFY_DONE;
+
+ if (dev_net(ndev) != &init_net)
+ return NOTIFY_DONE;
+
+ if (event != NETDEV_BONDING_FAILOVER)
+ return NOTIFY_DONE;
+
+ if (!(ndev->flags & IFF_MASTER) || !(ndev->priv_flags & IFF_BONDING))
+ return NOTIFY_DONE;
+
+ mutex_lock(&lock);
+ list_for_each_entry(cma_dev, &dev_list, list)
+ list_for_each_entry(id_priv, &cma_dev->id_list, list) {
+ ret = cma_netdev_change(ndev, id_priv);
+ if (ret)
+ goto out;
+ }
+
+out:
+ mutex_unlock(&lock);
+ return ret;
+}
+
+static struct notifier_block cma_nb = {
+ .notifier_call = cma_netdev_callback
+};
+
static void cma_add_one(struct ib_device *device)
{
struct cma_device *cma_dev;
@@ -2831,6 +2925,7 @@ static int cma_init(void)
ib_sa_register_client(&sa_client);
rdma_addr_register_client(&addr_client);
+ register_netdevice_notifier(&cma_nb);
ret = ib_register_client(&cma_client);
if (ret)
@@ -2838,6 +2933,7 @@ static int cma_init(void)
return 0;
err:
+ unregister_netdevice_notifier(&cma_nb);
rdma_addr_unregister_client(&addr_client);
ib_sa_unregister_client(&sa_client);
destroy_workqueue(cma_wq);
@@ -2847,6 +2943,7 @@ err:
static void cma_cleanup(void)
{
ib_unregister_client(&cma_client);
+ unregister_netdevice_notifier(&cma_nb);
rdma_addr_unregister_client(&addr_client);
ib_sa_unregister_client(&sa_client);
destroy_workqueue(cma_wq);
diff --git a/drivers/infiniband/core/iwcm.c b/drivers/infiniband/core/iwcm.c
index 81c9195b512..8f9509e1ebf 100644
--- a/drivers/infiniband/core/iwcm.c
+++ b/drivers/infiniband/core/iwcm.c
@@ -942,8 +942,7 @@ static int iwcm_init_qp_init_attr(struct iwcm_id_private *cm_id_priv,
case IW_CM_STATE_CONN_RECV:
case IW_CM_STATE_ESTABLISHED:
*qp_attr_mask = IB_QP_STATE | IB_QP_ACCESS_FLAGS;
- qp_attr->qp_access_flags = IB_ACCESS_LOCAL_WRITE |
- IB_ACCESS_REMOTE_WRITE|
+ qp_attr->qp_access_flags = IB_ACCESS_REMOTE_WRITE|
IB_ACCESS_REMOTE_READ;
ret = 0;
break;
diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c
index 1341de793e5..7863a50d56f 100644
--- a/drivers/infiniband/core/sa_query.c
+++ b/drivers/infiniband/core/sa_query.c
@@ -1064,7 +1064,8 @@ static void ib_sa_remove_one(struct ib_device *device)
for (i = 0; i <= sa_dev->end_port - sa_dev->start_port; ++i) {
ib_unregister_mad_agent(sa_dev->port[i].agent);
- kref_put(&sa_dev->port[i].sm_ah->ref, free_sm_ah);
+ if (sa_dev->port[i].sm_ah)
+ kref_put(&sa_dev->port[i].sm_ah->ref, free_sm_ah);
}
kfree(sa_dev);
diff --git a/drivers/infiniband/hw/ehca/ehca_classes.h b/drivers/infiniband/hw/ehca/ehca_classes.h
index 1e9e99a1393..0b0618edd64 100644
--- a/drivers/infiniband/hw/ehca/ehca_classes.h
+++ b/drivers/infiniband/hw/ehca/ehca_classes.h
@@ -194,6 +194,7 @@ struct ehca_qp {
u32 packet_count;
atomic_t nr_events; /* events seen */
wait_queue_head_t wait_completion;
+ int mig_armed;
};
#define IS_SRQ(qp) (qp->ext_type == EQPT_SRQ)
diff --git a/drivers/infiniband/hw/ehca/ehca_hca.c b/drivers/infiniband/hw/ehca/ehca_hca.c
index bc3b37d2070..46288220cfb 100644
--- a/drivers/infiniband/hw/ehca/ehca_hca.c
+++ b/drivers/infiniband/hw/ehca/ehca_hca.c
@@ -114,7 +114,9 @@ int ehca_query_device(struct ib_device *ibdev, struct ib_device_attr *props)
}
props->max_pkeys = 16;
- props->local_ca_ack_delay = min_t(u8, rblock->local_ca_ack_delay, 255);
+ /* Some FW versions say 0 here; insert sensible value in that case */
+ props->local_ca_ack_delay = rblock->local_ca_ack_delay ?
+ min_t(u8, rblock->local_ca_ack_delay, 255) : 12;
props->max_raw_ipv6_qp = limit_uint(rblock->max_raw_ipv6_qp);
props->max_raw_ethy_qp = limit_uint(rblock->max_raw_ethy_qp);
props->max_mcast_grp = limit_uint(rblock->max_mcast_grp);
diff --git a/drivers/infiniband/hw/ehca/ehca_irq.c b/drivers/infiniband/hw/ehca/ehca_irq.c
index 7a64aa9b51b..cb55be04442 100644
--- a/drivers/infiniband/hw/ehca/ehca_irq.c
+++ b/drivers/infiniband/hw/ehca/ehca_irq.c
@@ -178,6 +178,10 @@ static void dispatch_qp_event(struct ehca_shca *shca, struct ehca_qp *qp,
{
struct ib_event event;
+ /* PATH_MIG without the QP ever having been armed is false alarm */
+ if (event_type == IB_EVENT_PATH_MIG && !qp->mig_armed)
+ return;
+
event.device = &shca->ib_device;
event.event = event_type;
diff --git a/drivers/infiniband/hw/ehca/ehca_qp.c b/drivers/infiniband/hw/ehca/ehca_qp.c
index 3f59587338e..ea13efddf17 100644
--- a/drivers/infiniband/hw/ehca/ehca_qp.c
+++ b/drivers/infiniband/hw/ehca/ehca_qp.c
@@ -1460,6 +1460,8 @@ static int internal_modify_qp(struct ib_qp *ibqp,
goto modify_qp_exit2;
}
mqpcb->path_migration_state = attr->path_mig_state + 1;
+ if (attr->path_mig_state == IB_MIG_REARM)
+ my_qp->mig_armed = 1;
update_mask |=
EHCA_BMASK_SET(MQPCB_MASK_PATH_MIGRATION_STATE, 1);
}
diff --git a/drivers/infiniband/hw/ehca/ipz_pt_fn.c b/drivers/infiniband/hw/ehca/ipz_pt_fn.c
index 661f8db6270..c3a32846543 100644
--- a/drivers/infiniband/hw/ehca/ipz_pt_fn.c
+++ b/drivers/infiniband/hw/ehca/ipz_pt_fn.c
@@ -163,6 +163,7 @@ static int alloc_small_queue_page(struct ipz_queue *queue, struct ehca_pd *pd)
out:
ehca_err(pd->ib_pd.device, "failed to allocate small queue page");
+ mutex_unlock(&pd->lock);
return 0;
}
diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c
index 299f20832ab..0b191a4842c 100644
--- a/drivers/infiniband/hw/mlx4/cq.c
+++ b/drivers/infiniband/hw/mlx4/cq.c
@@ -637,6 +637,7 @@ repoll:
case MLX4_OPCODE_SEND_IMM:
wc->wc_flags |= IB_WC_WITH_IMM;
case MLX4_OPCODE_SEND:
+ case MLX4_OPCODE_SEND_INVAL:
wc->opcode = IB_WC_SEND;
break;
case MLX4_OPCODE_RDMA_READ:
@@ -657,6 +658,12 @@ repoll:
case MLX4_OPCODE_LSO:
wc->opcode = IB_WC_LSO;
break;
+ case MLX4_OPCODE_FMR:
+ wc->opcode = IB_WC_FAST_REG_MR;
+ break;
+ case MLX4_OPCODE_LOCAL_INVAL:
+ wc->opcode = IB_WC_LOCAL_INV;
+ break;
}
} else {
wc->byte_len = be32_to_cpu(cqe->byte_cnt);
@@ -667,6 +674,11 @@ repoll:
wc->wc_flags = IB_WC_WITH_IMM;
wc->ex.imm_data = cqe->immed_rss_invalid;
break;
+ case MLX4_RECV_OPCODE_SEND_INVAL:
+ wc->opcode = IB_WC_RECV;
+ wc->wc_flags = IB_WC_WITH_INVALIDATE;
+ wc->ex.invalidate_rkey = be32_to_cpu(cqe->immed_rss_invalid);
+ break;
case MLX4_RECV_OPCODE_SEND:
wc->opcode = IB_WC_RECV;
wc->wc_flags = 0;
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index bcf50648fa1..38d6907ab52 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -104,6 +104,12 @@ static int mlx4_ib_query_device(struct ib_device *ibdev,
props->device_cap_flags |= IB_DEVICE_UD_IP_CSUM;
if (dev->dev->caps.max_gso_sz)
props->device_cap_flags |= IB_DEVICE_UD_TSO;
+ if (dev->dev->caps.bmme_flags & MLX4_BMME_FLAG_RESERVED_LKEY)
+ props->device_cap_flags |= IB_DEVICE_LOCAL_DMA_LKEY;
+ if ((dev->dev->caps.bmme_flags & MLX4_BMME_FLAG_LOCAL_INV) &&
+ (dev->dev->caps.bmme_flags & MLX4_BMME_FLAG_REMOTE_INV) &&
+ (dev->dev->caps.bmme_flags & MLX4_BMME_FLAG_FAST_REG_WR))
+ props->device_cap_flags |= IB_DEVICE_MEM_MGT_EXTENSIONS;
props->vendor_id = be32_to_cpup((__be32 *) (out_mad->data + 36)) &
0xffffff;
@@ -127,6 +133,7 @@ static int mlx4_ib_query_device(struct ib_device *ibdev,
props->max_srq = dev->dev->caps.num_srqs - dev->dev->caps.reserved_srqs;
props->max_srq_wr = dev->dev->caps.max_srq_wqes - 1;
props->max_srq_sge = dev->dev->caps.max_srq_sge;
+ props->max_fast_reg_page_list_len = PAGE_SIZE / sizeof (u64);
props->local_ca_ack_delay = dev->dev->caps.local_ca_ack_delay;
props->atomic_cap = dev->dev->caps.flags & MLX4_DEV_CAP_FLAG_ATOMIC ?
IB_ATOMIC_HCA : IB_ATOMIC_NONE;
@@ -565,6 +572,7 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
strlcpy(ibdev->ib_dev.name, "mlx4_%d", IB_DEVICE_NAME_MAX);
ibdev->ib_dev.owner = THIS_MODULE;
ibdev->ib_dev.node_type = RDMA_NODE_IB_CA;
+ ibdev->ib_dev.local_dma_lkey = dev->caps.reserved_lkey;
ibdev->ib_dev.phys_port_cnt = dev->caps.num_ports;
ibdev->ib_dev.num_comp_vectors = 1;
ibdev->ib_dev.dma_device = &dev->pdev->dev;
@@ -627,6 +635,9 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
ibdev->ib_dev.get_dma_mr = mlx4_ib_get_dma_mr;
ibdev->ib_dev.reg_user_mr = mlx4_ib_reg_user_mr;
ibdev->ib_dev.dereg_mr = mlx4_ib_dereg_mr;
+ ibdev->ib_dev.alloc_fast_reg_mr = mlx4_ib_alloc_fast_reg_mr;
+ ibdev->ib_dev.alloc_fast_reg_page_list = mlx4_ib_alloc_fast_reg_page_list;
+ ibdev->ib_dev.free_fast_reg_page_list = mlx4_ib_free_fast_reg_page_list;
ibdev->ib_dev.attach_mcast = mlx4_ib_mcg_attach;
ibdev->ib_dev.detach_mcast = mlx4_ib_mcg_detach;
ibdev->ib_dev.process_mad = mlx4_ib_process_mad;
diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h
index c4cf5b69eef..d26a91317d4 100644
--- a/drivers/infiniband/hw/mlx4/mlx4_ib.h
+++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h
@@ -83,6 +83,11 @@ struct mlx4_ib_mr {
struct ib_umem *umem;
};
+struct mlx4_ib_fast_reg_page_list {
+ struct ib_fast_reg_page_list ibfrpl;
+ dma_addr_t map;
+};
+
struct mlx4_ib_fmr {
struct ib_fmr ibfmr;
struct mlx4_fmr mfmr;
@@ -199,6 +204,11 @@ static inline struct mlx4_ib_mr *to_mmr(struct ib_mr *ibmr)
return container_of(ibmr, struct mlx4_ib_mr, ibmr);
}
+static inline struct mlx4_ib_fast_reg_page_list *to_mfrpl(struct ib_fast_reg_page_list *ibfrpl)
+{
+ return container_of(ibfrpl, struct mlx4_ib_fast_reg_page_list, ibfrpl);
+}
+
static inline struct mlx4_ib_fmr *to_mfmr(struct ib_fmr *ibfmr)
{
return container_of(ibfmr, struct mlx4_ib_fmr, ibfmr);
@@ -239,6 +249,11 @@ struct ib_mr *mlx4_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
u64 virt_addr, int access_flags,
struct ib_udata *udata);
int mlx4_ib_dereg_mr(struct ib_mr *mr);
+struct ib_mr *mlx4_ib_alloc_fast_reg_mr(struct ib_pd *pd,
+ int max_page_list_len);
+struct ib_fast_reg_page_list *mlx4_ib_alloc_fast_reg_page_list(struct ib_device *ibdev,
+ int page_list_len);
+void mlx4_ib_free_fast_reg_page_list(struct ib_fast_reg_page_list *page_list);
int mlx4_ib_modify_cq(struct ib_cq *cq, u16 cq_count, u16 cq_period);
int mlx4_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata);
diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c
index 68e92485fc7..db2086faa4e 100644
--- a/drivers/infiniband/hw/mlx4/mr.c
+++ b/drivers/infiniband/hw/mlx4/mr.c
@@ -183,6 +183,76 @@ int mlx4_ib_dereg_mr(struct ib_mr *ibmr)
return 0;
}
+struct ib_mr *mlx4_ib_alloc_fast_reg_mr(struct ib_pd *pd,
+ int max_page_list_len)
+{
+ struct mlx4_ib_dev *dev = to_mdev(pd->device);
+ struct mlx4_ib_mr *mr;
+ int err;
+
+ mr = kmalloc(sizeof *mr, GFP_KERNEL);
+ if (!mr)
+ return ERR_PTR(-ENOMEM);
+
+ err = mlx4_mr_alloc(dev->dev, to_mpd(pd)->pdn, 0, 0, 0,
+ max_page_list_len, 0, &mr->mmr);
+ if (err)
+ goto err_free;
+
+ err = mlx4_mr_enable(dev->dev, &mr->mmr);
+ if (err)
+ goto err_mr;
+
+ return &mr->ibmr;
+
+err_mr:
+ mlx4_mr_free(dev->dev, &mr->mmr);
+
+err_free:
+ kfree(mr);
+ return ERR_PTR(err);
+}
+
+struct ib_fast_reg_page_list *mlx4_ib_alloc_fast_reg_page_list(struct ib_device *ibdev,
+ int page_list_len)
+{
+ struct mlx4_ib_dev *dev = to_mdev(ibdev);
+ struct mlx4_ib_fast_reg_page_list *mfrpl;
+ int size = page_list_len * sizeof (u64);
+
+ if (size > PAGE_SIZE)
+ return ERR_PTR(-EINVAL);
+
+ mfrpl = kmalloc(sizeof *mfrpl, GFP_KERNEL);
+ if (!mfrpl)
+ return ERR_PTR(-ENOMEM);
+
+ mfrpl->ibfrpl.page_list = dma_alloc_coherent(&dev->dev->pdev->dev,
+ size, &mfrpl->map,
+ GFP_KERNEL);
+ if (!mfrpl->ibfrpl.page_list)
+ goto err_free;
+
+ WARN_ON(mfrpl->map & 0x3f);
+
+ return &mfrpl->ibfrpl;
+
+err_free:
+ kfree(mfrpl);
+ return ERR_PTR(-ENOMEM);
+}
+
+void mlx4_ib_free_fast_reg_page_list(struct ib_fast_reg_page_list *page_list)
+{
+ struct mlx4_ib_dev *dev = to_mdev(page_list->device);
+ struct mlx4_ib_fast_reg_page_list *mfrpl = to_mfrpl(page_list);
+ int size = page_list->max_page_list_len * sizeof (u64);
+
+ dma_free_coherent(&dev->dev->pdev->dev, size, page_list->page_list,
+ mfrpl->map);
+ kfree(mfrpl);
+}
+
struct ib_fmr *mlx4_ib_fmr_alloc(struct ib_pd *pd, int acc,
struct ib_fmr_attr *fmr_attr)
{
diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c
index 89eb6cbe592..02a99bc4442 100644
--- a/drivers/infiniband/hw/mlx4/qp.c
+++ b/drivers/infiniband/hw/mlx4/qp.c
@@ -78,6 +78,9 @@ static const __be32 mlx4_ib_opcode[] = {
[IB_WR_RDMA_READ] = __constant_cpu_to_be32(MLX4_OPCODE_RDMA_READ),
[IB_WR_ATOMIC_CMP_AND_SWP] = __constant_cpu_to_be32(MLX4_OPCODE_ATOMIC_CS),
[IB_WR_ATOMIC_FETCH_AND_ADD] = __constant_cpu_to_be32(MLX4_OPCODE_ATOMIC_FA),
+ [IB_WR_SEND_WITH_INV] = __constant_cpu_to_be32(MLX4_OPCODE_SEND_INVAL),
+ [IB_WR_LOCAL_INV] = __constant_cpu_to_be32(MLX4_OPCODE_LOCAL_INVAL),
+ [IB_WR_FAST_REG_MR] = __constant_cpu_to_be32(MLX4_OPCODE_FMR),
};
static struct mlx4_ib_sqp *to_msqp(struct mlx4_ib_qp *mqp)
@@ -976,6 +979,10 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
context->pd = cpu_to_be32(to_mpd(ibqp->pd)->pdn);
context->params1 = cpu_to_be32(MLX4_IB_ACK_REQ_FREQ << 28);
+ /* Set "fast registration enabled" for all kernel QPs */
+ if (!qp->ibqp.uobject)
+ context->params1 |= cpu_to_be32(1 << 11);
+
if (attr_mask & IB_QP_RNR_RETRY) {
context->params1 |= cpu_to_be32(attr->rnr_retry << 13);
optpar |= MLX4_QP_OPTPAR_RNR_RETRY;
@@ -1322,6 +1329,38 @@ static int mlx4_wq_overflow(struct mlx4_ib_wq *wq, int nreq, struct ib_cq *ib_cq
return cur + nreq >= wq->max_post;
}
+static __be32 convert_access(int acc)
+{
+ return (acc & IB_ACCESS_REMOTE_ATOMIC ? cpu_to_be32(MLX4_WQE_FMR_PERM_ATOMIC) : 0) |
+ (acc & IB_ACCESS_REMOTE_WRITE ? cpu_to_be32(MLX4_WQE_FMR_PERM_REMOTE_WRITE) : 0) |
+ (acc & IB_ACCESS_REMOTE_READ ? cpu_to_be32(MLX4_WQE_FMR_PERM_REMOTE_READ) : 0) |
+ (acc & IB_ACCESS_LOCAL_WRITE ? cpu_to_be32(MLX4_WQE_FMR_PERM_LOCAL_WRITE) : 0) |
+ cpu_to_be32(MLX4_WQE_FMR_PERM_LOCAL_READ);
+}
+
+static void set_fmr_seg(struct mlx4_wqe_fmr_seg *fseg, struct ib_send_wr *wr)
+{
+ struct mlx4_ib_fast_reg_page_list *mfrpl = to_mfrpl(wr->wr.fast_reg.page_list);
+
+ fseg->flags = convert_access(wr->wr.fast_reg.access_flags);
+ fseg->mem_key = cpu_to_be32(wr->wr.fast_reg.rkey);
+ fseg->buf_list = cpu_to_be64(mfrpl->map);
+ fseg->start_addr = cpu_to_be64(wr->wr.fast_reg.iova_start);
+ fseg->reg_len = cpu_to_be64(wr->wr.fast_reg.length);
+ fseg->offset = 0; /* XXX -- is this just for ZBVA? */
+ fseg->page_size = cpu_to_be32(wr->wr.fast_reg.page_shift);
+ fseg->reserved[0] = 0;
+ fseg->reserved[1] = 0;
+}
+
+static void set_local_inv_seg(struct mlx4_wqe_local_inval_seg *iseg, u32 rkey)
+{
+ iseg->flags = 0;
+ iseg->mem_key = cpu_to_be32(rkey);
+ iseg->guest_id = 0;
+ iseg->pa = 0;
+}
+
static __always_inline void set_raddr_seg(struct mlx4_wqe_raddr_seg *rseg,
u64 remote_addr, u32 rkey)
{
@@ -1395,7 +1434,7 @@ static void __set_data_seg(struct mlx4_wqe_data_seg *dseg, struct ib_sge *sg)
dseg->addr = cpu_to_be64(sg->addr);
}
-static int build_lso_seg(struct mlx4_lso_seg *wqe, struct ib_send_wr *wr,
+static int build_lso_seg(struct mlx4_wqe_lso_seg *wqe, struct ib_send_wr *wr,
struct mlx4_ib_qp *qp, unsigned *lso_seg_len)
{
unsigned halign = ALIGN(sizeof *wqe + wr->wr.ud.hlen, 16);
@@ -1423,6 +1462,21 @@ static int build_lso_seg(struct mlx4_lso_seg *wqe, struct ib_send_wr *wr,
return 0;
}
+static __be32 send_ieth(struct ib_send_wr *wr)
+{
+ switch (wr->opcode) {
+ case IB_WR_SEND_WITH_IMM:
+ case IB_WR_RDMA_WRITE_WITH_IMM:
+ return wr->ex.imm_data;
+
+ case IB_WR_SEND_WITH_INV:
+ return cpu_to_be32(wr->ex.invalidate_rkey);
+
+ default:
+ return 0;
+ }
+}
+
int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
struct ib_send_wr **bad_wr)
{
@@ -1469,11 +1523,7 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
MLX4_WQE_CTRL_TCP_UDP_CSUM) : 0) |
qp->sq_signal_bits;
- if (wr->opcode == IB_WR_SEND_WITH_IMM ||
- wr->opcode == IB_WR_RDMA_WRITE_WITH_IMM)
- ctrl->imm = wr->ex.imm_data;
- else
- ctrl->imm = 0;
+ ctrl->imm = send_ieth(wr);
wqe += sizeof *ctrl;
size = sizeof *ctrl / 16;
@@ -1505,6 +1555,18 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
size += sizeof (struct mlx4_wqe_raddr_seg) / 16;
break;
+ case IB_WR_LOCAL_INV:
+ set_local_inv_seg(wqe, wr->ex.invalidate_rkey);
+ wqe += sizeof (struct mlx4_wqe_local_inval_seg);
+ size += sizeof (struct mlx4_wqe_local_inval_seg) / 16;
+ break;
+
+ case IB_WR_FAST_REG_MR:
+ set_fmr_seg(wqe, wr);
+ wqe += sizeof (struct mlx4_wqe_fmr_seg);
+ size += sizeof (struct mlx4_wqe_fmr_seg) / 16;
+ break;
+
default:
/* No extra segments required for sends */
break;
diff --git a/drivers/infiniband/hw/mthca/mthca_dev.h b/drivers/infiniband/hw/mthca/mthca_dev.h
index ee4d073c889..252590116df 100644
--- a/drivers/infiniband/hw/mthca/mthca_dev.h
+++ b/drivers/infiniband/hw/mthca/mthca_dev.h
@@ -202,6 +202,7 @@ struct mthca_pd_table {
struct mthca_buddy {
unsigned long **bits;
+ int *num_free;
int max_order;
spinlock_t lock;
};
diff --git a/drivers/infiniband/hw/mthca/mthca_mr.c b/drivers/infiniband/hw/mthca/mthca_mr.c
index 8489b1e81c0..882e6b73591 100644
--- a/drivers/infiniband/hw/mthca/mthca_mr.c
+++ b/drivers/infiniband/hw/mthca/mthca_mr.c
@@ -89,23 +89,26 @@ static u32 mthca_buddy_alloc(struct mthca_buddy *buddy, int order)
spin_lock(&buddy->lock);
- for (o = order; o <= buddy->max_order; ++o) {
- m = 1 << (buddy->max_order - o);
- seg = find_first_bit(buddy->bits[o], m);
- if (seg < m)
- goto found;
- }
+ for (o = order; o <= buddy->max_order; ++o)
+ if (buddy->num_free[o]) {
+ m = 1 << (buddy->max_order - o);
+ seg = find_first_bit(buddy->bits[o], m);
+ if (seg < m)
+ goto found;
+ }
spin_unlock(&buddy->lock);
return -1;
found:
clear_bit(seg, buddy->bits[o]);
+ --buddy->num_free[o];
while (o > order) {
--o;
seg <<= 1;
set_bit(seg ^ 1, buddy->bits[o]);
+ ++buddy->num_free[o];
}
spin_unlock(&buddy->lock);
@@ -123,11 +126,13 @@ static void mthca_buddy_free(struct mthca_buddy *buddy, u32 seg, int order)
while (test_bit(seg ^ 1, buddy->bits[order])) {
clear_bit(seg ^ 1, buddy->bits[order]);
+ --buddy->num_free[order];
seg >>= 1;
++order;
}
set_bit(seg, buddy->bits[order]);
+ ++buddy->num_free[order];
spin_unlock(&buddy->lock);
}
@@ -141,7 +146,9 @@ static int mthca_buddy_init(struct mthca_buddy *buddy, int max_order)
buddy->bits = kzalloc((buddy->max_order + 1) * sizeof (long *),
GFP_KERNEL);
- if (!buddy->bits)
+ buddy->num_free = kzalloc((buddy->max_order + 1) * sizeof (int *),
+ GFP_KERNEL);
+ if (!buddy->bits || !buddy->num_free)
goto err_out;
for (i = 0; i <= buddy->max_order; ++i) {
@@ -154,6 +161,7 @@ static int mthca_buddy_init(struct mthca_buddy *buddy, int max_order)
}
set_bit(0, buddy->bits[buddy->max_order]);
+ buddy->num_free[buddy->max_order] = 1;
return 0;
@@ -161,9 +169,10 @@ err_out_free:
for (i = 0; i <= buddy->max_order; ++i)
kfree(buddy->bits[i]);
+err_out:
kfree(buddy->bits);
+ kfree(buddy->num_free);
-err_out:
return -ENOMEM;
}
@@ -175,6 +184,7 @@ static void mthca_buddy_cleanup(struct mthca_buddy *buddy)
kfree(buddy->bits[i]);
kfree(buddy->bits);
+ kfree(buddy->num_free);
}
static u32 mthca_alloc_mtt_range(struct mthca_dev *dev, int order,
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 8be9ea0436e..f51201b17bf 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -548,7 +548,7 @@ static int path_rec_start(struct net_device *dev,
path_rec_completion,
path, &path->query);
if (path->query_id < 0) {
- ipoib_warn(priv, "ib_sa_path_rec_get failed\n");
+ ipoib_warn(priv, "ib_sa_path_rec_get failed: %d\n", path->query_id);
path->query = NULL;
return path->query_id;
}
diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c
index 3a917c1f796..63462ecca14 100644
--- a/drivers/infiniband/ulp/iser/iser_verbs.c
+++ b/drivers/infiniband/ulp/iser/iser_verbs.c
@@ -483,6 +483,7 @@ static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *eve
break;
case RDMA_CM_EVENT_DISCONNECTED:
case RDMA_CM_EVENT_DEVICE_REMOVAL:
+ case RDMA_CM_EVENT_ADDR_CHANGE:
iser_disconnected_handler(cma_id);
break;
default:
diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c
index adbf29f0169..71c1971abf8 100644
--- a/drivers/input/keyboard/hil_kbd.c
+++ b/drivers/input/keyboard/hil_kbd.c
@@ -37,6 +37,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/semaphore.h>
#include <linux/slab.h>
#include <linux/pci_ids.h>
diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c
index 49d8abfe38f..daa9d422033 100644
--- a/drivers/input/misc/hp_sdc_rtc.c
+++ b/drivers/input/misc/hp_sdc_rtc.c
@@ -44,6 +44,7 @@
#include <linux/proc_fs.h>
#include <linux/poll.h>
#include <linux/rtc.h>
+#include <linux/semaphore.h>
MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
MODULE_DESCRIPTION("HP i8042 SDC + MSM-58321 RTC Driver");
diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c
index 7b233a492ad..0d395979b2d 100644
--- a/drivers/input/serio/hp_sdc.c
+++ b/drivers/input/serio/hp_sdc.c
@@ -67,9 +67,9 @@
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/time.h>
+#include <linux/semaphore.h>
#include <linux/slab.h>
#include <linux/hil.h>
-#include <linux/semaphore.h>
#include <asm/io.h>
#include <asm/system.h>
diff --git a/drivers/isdn/gigaset/asyncdata.c b/drivers/isdn/gigaset/asyncdata.c
index 091deb9d1c4..c2bd97d2927 100644
--- a/drivers/isdn/gigaset/asyncdata.c
+++ b/drivers/isdn/gigaset/asyncdata.c
@@ -575,7 +575,8 @@ int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb)
else
skb = iraw_encode(skb, HW_HDR_LEN, 0);
if (!skb) {
- err("unable to allocate memory for encoding!\n");
+ dev_err(bcs->cs->dev,
+ "unable to allocate memory for encoding!\n");
return -ENOMEM;
}
diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c
index 5255b5e20e1..3f11910c7cc 100644
--- a/drivers/isdn/gigaset/bas-gigaset.c
+++ b/drivers/isdn/gigaset/bas-gigaset.c
@@ -1050,10 +1050,9 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx)
}
/* retrieve block of data to send */
- ifd->offset = gigaset_isowbuf_getbytes(ubc->isooutbuf,
- ifd->length);
- if (ifd->offset < 0) {
- if (ifd->offset == -EBUSY) {
+ rc = gigaset_isowbuf_getbytes(ubc->isooutbuf, ifd->length);
+ if (rc < 0) {
+ if (rc == -EBUSY) {
gig_dbg(DEBUG_ISO,
"%s: buffer busy at frame %d",
__func__, nframe);
@@ -1062,11 +1061,12 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx)
} else {
dev_err(ucx->bcs->cs->dev,
"%s: buffer error %d at frame %d\n",
- __func__, ifd->offset, nframe);
- return ifd->offset;
+ __func__, rc, nframe);
+ return rc;
}
break;
}
+ ifd->offset = rc;
ucx->limit = ubc->isooutbuf->nextread;
ifd->status = 0;
ifd->actual_length = 0;
diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c
index 827c32c1679..9d3ce7718e5 100644
--- a/drivers/isdn/gigaset/common.c
+++ b/drivers/isdn/gigaset/common.c
@@ -287,7 +287,7 @@ struct event_t *gigaset_add_event(struct cardstate *cs,
tail = cs->ev_tail;
next = (tail + 1) % MAX_EVENTS;
if (unlikely(next == cs->ev_head))
- err("event queue full");
+ dev_err(cs->dev, "event queue full\n");
else {
event = cs->events + tail;
event->type = type;
diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h
index f365993161f..00375295499 100644
--- a/drivers/isdn/gigaset/gigaset.h
+++ b/drivers/isdn/gigaset/gigaset.h
@@ -106,7 +106,6 @@ enum debuglevel {
#undef err
#undef info
#undef warn
-#undef notice
#define err(format, arg...) printk(KERN_ERR KBUILD_MODNAME ": " \
format "\n" , ## arg)
@@ -114,8 +113,6 @@ enum debuglevel {
format "\n" , ## arg)
#define warn(format, arg...) printk(KERN_WARNING KBUILD_MODNAME ": " \
format "\n" , ## arg)
-#define notice(format, arg...) printk(KERN_NOTICE KBUILD_MODNAME ": " \
- format "\n" , ## arg)
#ifdef CONFIG_GIGASET_DEBUG
diff --git a/drivers/isdn/gigaset/i4l.c b/drivers/isdn/gigaset/i4l.c
index 9e089f06a94..3c127a8cbaf 100644
--- a/drivers/isdn/gigaset/i4l.c
+++ b/drivers/isdn/gigaset/i4l.c
@@ -46,7 +46,8 @@ static int writebuf_from_LL(int driverID, int channel, int ack,
return -ENODEV;
}
if (channel < 0 || channel >= cs->channels) {
- err("%s: invalid channel ID (%d)", __func__, channel);
+ dev_err(cs->dev, "%s: invalid channel ID (%d)\n",
+ __func__, channel);
return -ENODEV;
}
bcs = &cs->bcs[channel];
@@ -58,11 +59,13 @@ static int writebuf_from_LL(int driverID, int channel, int ack,
if (!len) {
if (ack)
- notice("%s: not ACKing empty packet", __func__);
+ dev_notice(cs->dev, "%s: not ACKing empty packet\n",
+ __func__);
return 0;
}
if (len > MAX_BUF_SIZE) {
- err("%s: packet too large (%d bytes)", __func__, len);
+ dev_err(cs->dev, "%s: packet too large (%d bytes)\n",
+ __func__, len);
return -EINVAL;
}
@@ -116,8 +119,7 @@ static int command_from_LL(isdn_ctrl *cntrl)
gigaset_debugdrivers();
if (!cs) {
- warn("LL tried to access unknown device with nr. %d",
- cntrl->driver);
+ err("%s: invalid driver ID (%d)", __func__, cntrl->driver);
return -ENODEV;
}
@@ -126,7 +128,7 @@ static int command_from_LL(isdn_ctrl *cntrl)
gig_dbg(DEBUG_ANY, "ISDN_CMD_IOCTL (driver: %d, arg: %ld)",
cntrl->driver, cntrl->arg);
- warn("ISDN_CMD_IOCTL is not supported.");
+ dev_warn(cs->dev, "ISDN_CMD_IOCTL not supported\n");
return -EINVAL;
case ISDN_CMD_DIAL:
@@ -138,22 +140,23 @@ static int command_from_LL(isdn_ctrl *cntrl)
cntrl->parm.setup.si1, cntrl->parm.setup.si2);
if (cntrl->arg >= cs->channels) {
- err("ISDN_CMD_DIAL: invalid channel (%d)",
- (int) cntrl->arg);
+ dev_err(cs->dev,
+ "ISDN_CMD_DIAL: invalid channel (%d)\n",
+ (int) cntrl->arg);
return -EINVAL;
}
bcs = cs->bcs + cntrl->arg;
if (!gigaset_get_channel(bcs)) {
- err("ISDN_CMD_DIAL: channel not free");
+ dev_err(cs->dev, "ISDN_CMD_DIAL: channel not free\n");
return -EBUSY;
}
sp = kmalloc(sizeof *sp, GFP_ATOMIC);
if (!sp) {
gigaset_free_channel(bcs);
- err("ISDN_CMD_DIAL: out of memory");
+ dev_err(cs->dev, "ISDN_CMD_DIAL: out of memory\n");
return -ENOMEM;
}
*sp = cntrl->parm.setup;
@@ -173,8 +176,9 @@ static int command_from_LL(isdn_ctrl *cntrl)
gig_dbg(DEBUG_ANY, "ISDN_CMD_ACCEPTD");
if (cntrl->arg >= cs->channels) {
- err("ISDN_CMD_ACCEPTD: invalid channel (%d)",
- (int) cntrl->arg);
+ dev_err(cs->dev,
+ "ISDN_CMD_ACCEPTD: invalid channel (%d)\n",
+ (int) cntrl->arg);
return -EINVAL;
}
@@ -196,8 +200,9 @@ static int command_from_LL(isdn_ctrl *cntrl)
(int) cntrl->arg);
if (cntrl->arg >= cs->channels) {
- err("ISDN_CMD_HANGUP: invalid channel (%u)",
- (unsigned) cntrl->arg);
+ dev_err(cs->dev,
+ "ISDN_CMD_HANGUP: invalid channel (%d)\n",
+ (int) cntrl->arg);
return -EINVAL;
}
@@ -224,8 +229,9 @@ static int command_from_LL(isdn_ctrl *cntrl)
cntrl->arg & 0xff, (cntrl->arg >> 8));
if ((cntrl->arg & 0xff) >= cs->channels) {
- err("ISDN_CMD_SETL2: invalid channel (%u)",
- (unsigned) cntrl->arg & 0xff);
+ dev_err(cs->dev,
+ "ISDN_CMD_SETL2: invalid channel (%d)\n",
+ (int) cntrl->arg & 0xff);
return -EINVAL;
}
@@ -244,14 +250,16 @@ static int command_from_LL(isdn_ctrl *cntrl)
cntrl->arg & 0xff, (cntrl->arg >> 8));
if ((cntrl->arg & 0xff) >= cs->channels) {
- err("ISDN_CMD_SETL3: invalid channel (%u)",
- (unsigned) cntrl->arg & 0xff);
+ dev_err(cs->dev,
+ "ISDN_CMD_SETL3: invalid channel (%d)\n",
+ (int) cntrl->arg & 0xff);
return -EINVAL;
}
if (cntrl->arg >> 8 != ISDN_PROTO_L3_TRANS) {
- err("ISDN_CMD_SETL3: invalid protocol %lu",
- cntrl->arg >> 8);
+ dev_err(cs->dev,
+ "ISDN_CMD_SETL3: invalid protocol %lu\n",
+ cntrl->arg >> 8);
return -EINVAL;
}
@@ -262,8 +270,9 @@ static int command_from_LL(isdn_ctrl *cntrl)
case ISDN_CMD_ALERT:
gig_dbg(DEBUG_ANY, "ISDN_CMD_ALERT"); //FIXME
if (cntrl->arg >= cs->channels) {
- err("ISDN_CMD_ALERT: invalid channel (%d)",
- (int) cntrl->arg);
+ dev_err(cs->dev,
+ "ISDN_CMD_ALERT: invalid channel (%d)\n",
+ (int) cntrl->arg);
return -EINVAL;
}
//bcs = cs->bcs + cntrl->arg;
@@ -295,7 +304,8 @@ static int command_from_LL(isdn_ctrl *cntrl)
gig_dbg(DEBUG_ANY, "ISDN_CMD_GETSIL");
break;
default:
- err("unknown command %d from LL", cntrl->command);
+ dev_err(cs->dev, "unknown command %d from LL\n",
+ cntrl->command);
return -EINVAL;
}
diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c
index af195b07c19..521951a898e 100644
--- a/drivers/isdn/gigaset/interface.c
+++ b/drivers/isdn/gigaset/interface.c
@@ -197,7 +197,7 @@ static void if_close(struct tty_struct *tty, struct file *filp)
mutex_lock(&cs->mutex);
if (!cs->open_count)
- warn("%s: device not opened", __func__);
+ dev_warn(cs->dev, "%s: device not opened\n", __func__);
else {
if (!--cs->open_count) {
spin_lock_irqsave(&cs->lock, flags);
@@ -232,7 +232,7 @@ static int if_ioctl(struct tty_struct *tty, struct file *file,
return -ERESTARTSYS; // FIXME -EINTR?
if (!cs->open_count)
- warn("%s: device not opened", __func__);
+ dev_warn(cs->dev, "%s: device not opened\n", __func__);
else {
retval = 0;
switch (cmd) {
@@ -364,9 +364,9 @@ static int if_write(struct tty_struct *tty, const unsigned char *buf, int count)
return -ERESTARTSYS; // FIXME -EINTR?
if (!cs->open_count)
- warn("%s: device not opened", __func__);
+ dev_warn(cs->dev, "%s: device not opened\n", __func__);
else if (cs->mstate != MS_LOCKED) {
- warn("can't write to unlocked device");
+ dev_warn(cs->dev, "can't write to unlocked device\n");
retval = -EBUSY;
} else if (!cs->connected) {
gig_dbg(DEBUG_ANY, "can't write to unplugged device");
@@ -398,9 +398,9 @@ static int if_write_room(struct tty_struct *tty)
return -ERESTARTSYS; // FIXME -EINTR?
if (!cs->open_count)
- warn("%s: device not opened", __func__);
+ dev_warn(cs->dev, "%s: device not opened\n", __func__);
else if (cs->mstate != MS_LOCKED) {
- warn("can't write to unlocked device");
+ dev_warn(cs->dev, "can't write to unlocked device\n");
retval = -EBUSY;
} else if (!cs->connected) {
gig_dbg(DEBUG_ANY, "can't write to unplugged device");
@@ -430,9 +430,9 @@ static int if_chars_in_buffer(struct tty_struct *tty)
return -ERESTARTSYS; // FIXME -EINTR?
if (!cs->open_count)
- warn("%s: device not opened", __func__);
+ dev_warn(cs->dev, "%s: device not opened\n", __func__);
else if (cs->mstate != MS_LOCKED) {
- warn("can't write to unlocked device");
+ dev_warn(cs->dev, "can't write to unlocked device\n");
retval = -EBUSY;
} else if (!cs->connected) {
gig_dbg(DEBUG_ANY, "can't write to unplugged device");
@@ -460,7 +460,7 @@ static void if_throttle(struct tty_struct *tty)
mutex_lock(&cs->mutex);
if (!cs->open_count)
- warn("%s: device not opened", __func__);
+ dev_warn(cs->dev, "%s: device not opened\n", __func__);
else {
//FIXME
}
@@ -483,7 +483,7 @@ static void if_unthrottle(struct tty_struct *tty)
mutex_lock(&cs->mutex);
if (!cs->open_count)
- warn("%s: device not opened", __func__);
+ dev_warn(cs->dev, "%s: device not opened\n", __func__);
else {
//FIXME
}
@@ -510,7 +510,7 @@ static void if_set_termios(struct tty_struct *tty, struct ktermios *old)
mutex_lock(&cs->mutex);
if (!cs->open_count) {
- warn("%s: device not opened", __func__);
+ dev_warn(cs->dev, "%s: device not opened\n", __func__);
goto out;
}
@@ -623,7 +623,8 @@ void gigaset_if_init(struct cardstate *cs)
if (!IS_ERR(cs->tty_dev))
dev_set_drvdata(cs->tty_dev, cs);
else {
- warn("could not register device to the tty subsystem");
+ dev_warn(cs->dev,
+ "could not register device to the tty subsystem\n");
cs->tty_dev = NULL;
}
mutex_unlock(&cs->mutex);
diff --git a/drivers/isdn/gigaset/usb-gigaset.c b/drivers/isdn/gigaset/usb-gigaset.c
index 77d20ab0cd4..4661830a49d 100644
--- a/drivers/isdn/gigaset/usb-gigaset.c
+++ b/drivers/isdn/gigaset/usb-gigaset.c
@@ -498,8 +498,9 @@ static int send_cb(struct cardstate *cs, struct cmdbuf_t *cb)
if (status) {
ucs->busy = 0;
- err("could not submit urb (error %d)\n",
- -status);
+ dev_err(cs->dev,
+ "could not submit urb (error %d)\n",
+ -status);
cb->len = 0; /* skip urb => remove cb+wakeup
in next loop cycle */
}
@@ -670,7 +671,7 @@ static int write_modem(struct cardstate *cs)
spin_unlock_irqrestore(&cs->lock, flags);
if (ret) {
- err("could not submit urb (error %d)\n", -ret);
+ dev_err(cs->dev, "could not submit urb (error %d)\n", -ret);
ucs->busy = 0;
}
diff --git a/drivers/isdn/hisax/st5481.h b/drivers/isdn/hisax/st5481.h
index 2044e7173ab..cff7a635433 100644
--- a/drivers/isdn/hisax/st5481.h
+++ b/drivers/isdn/hisax/st5481.h
@@ -220,7 +220,7 @@ enum {
#define ERR(format, arg...) \
printk(KERN_ERR "%s:%s: " format "\n" , __FILE__, __func__ , ## arg)
-#define WARN(format, arg...) \
+#define WARNING(format, arg...) \
printk(KERN_WARNING "%s:%s: " format "\n" , __FILE__, __func__ , ## arg)
#define INFO(format, arg...) \
@@ -412,7 +412,7 @@ struct st5481_adapter {
({ \
int status; \
if ((status = usb_submit_urb(urb, mem_flags)) < 0) { \
- WARN("usb_submit_urb failed,status=%d", status); \
+ WARNING("usb_submit_urb failed,status=%d", status); \
} \
status; \
})
diff --git a/drivers/isdn/hisax/st5481_b.c b/drivers/isdn/hisax/st5481_b.c
index fa64115cd7c..0074b600a0e 100644
--- a/drivers/isdn/hisax/st5481_b.c
+++ b/drivers/isdn/hisax/st5481_b.c
@@ -180,7 +180,7 @@ static void usb_b_out_complete(struct urb *urb)
DBG(4,"urb killed status %d", urb->status);
return; // Give up
default:
- WARN("urb status %d",urb->status);
+ WARNING("urb status %d",urb->status);
if (b_out->busy == 0) {
st5481_usb_pipe_reset(adapter, (bcs->channel+1)*2 | USB_DIR_OUT, NULL, NULL);
}
@@ -372,6 +372,6 @@ void st5481_b_l2l1(struct hisax_if *ifc, int pr, void *arg)
B_L1L2(bcs, PH_DEACTIVATE | INDICATION, NULL);
break;
default:
- WARN("pr %#x\n", pr);
+ WARNING("pr %#x\n", pr);
}
}
diff --git a/drivers/isdn/hisax/st5481_d.c b/drivers/isdn/hisax/st5481_d.c
index b8c4855cc88..077991c1cd0 100644
--- a/drivers/isdn/hisax/st5481_d.c
+++ b/drivers/isdn/hisax/st5481_d.c
@@ -389,7 +389,7 @@ static void usb_d_out_complete(struct urb *urb)
DBG(1,"urb killed status %d", urb->status);
break;
default:
- WARN("urb status %d",urb->status);
+ WARNING("urb status %d",urb->status);
if (d_out->busy == 0) {
st5481_usb_pipe_reset(adapter, EP_D_OUT | USB_DIR_OUT, fifo_reseted, adapter);
}
@@ -420,7 +420,7 @@ static void dout_start_xmit(struct FsmInst *fsm, int event, void *arg)
isdnhdlc_out_init(&d_out->hdlc_state, 1, 0);
if (test_and_set_bit(buf_nr, &d_out->busy)) {
- WARN("ep %d urb %d busy %#lx", EP_D_OUT, buf_nr, d_out->busy);
+ WARNING("ep %d urb %d busy %#lx", EP_D_OUT, buf_nr, d_out->busy);
return;
}
urb = d_out->urb[buf_nr];
@@ -601,7 +601,7 @@ void st5481_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg)
FsmEvent(&adapter->d_out.fsm, EV_DOUT_START_XMIT, NULL);
break;
default:
- WARN("pr %#x\n", pr);
+ WARNING("pr %#x\n", pr);
break;
}
}
diff --git a/drivers/isdn/hisax/st5481_usb.c b/drivers/isdn/hisax/st5481_usb.c
index 427a8b0520f..ec3c0e50766 100644
--- a/drivers/isdn/hisax/st5481_usb.c
+++ b/drivers/isdn/hisax/st5481_usb.c
@@ -66,7 +66,7 @@ static void usb_ctrl_msg(struct st5481_adapter *adapter,
struct ctrl_msg *ctrl_msg;
if ((w_index = fifo_add(&ctrl->msg_fifo.f)) < 0) {
- WARN("control msg FIFO full");
+ WARNING("control msg FIFO full");
return;
}
ctrl_msg = &ctrl->msg_fifo.data[w_index];
@@ -139,7 +139,7 @@ static void usb_ctrl_complete(struct urb *urb)
DBG(1,"urb killed status %d", urb->status);
return; // Give up
default:
- WARN("urb status %d",urb->status);
+ WARNING("urb status %d",urb->status);
break;
}
}
@@ -198,7 +198,7 @@ static void usb_int_complete(struct urb *urb)
DBG(2, "urb shutting down with status: %d", urb->status);
return;
default:
- WARN("nonzero urb status received: %d", urb->status);
+ WARNING("nonzero urb status received: %d", urb->status);
goto exit;
}
@@ -235,7 +235,7 @@ static void usb_int_complete(struct urb *urb)
exit:
status = usb_submit_urb (urb, GFP_ATOMIC);
if (status)
- WARN("usb_submit_urb failed with result %d", status);
+ WARNING("usb_submit_urb failed with result %d", status);
}
/* ======================================================================
@@ -257,7 +257,7 @@ int st5481_setup_usb(struct st5481_adapter *adapter)
DBG(2,"");
if ((status = usb_reset_configuration (dev)) < 0) {
- WARN("reset_configuration failed,status=%d",status);
+ WARNING("reset_configuration failed,status=%d",status);
return status;
}
@@ -269,7 +269,7 @@ int st5481_setup_usb(struct st5481_adapter *adapter)
// Check if the config is sane
if ( altsetting->desc.bNumEndpoints != 7 ) {
- WARN("expecting 7 got %d endpoints!", altsetting->desc.bNumEndpoints);
+ WARNING("expecting 7 got %d endpoints!", altsetting->desc.bNumEndpoints);
return -EINVAL;
}
@@ -279,7 +279,7 @@ int st5481_setup_usb(struct st5481_adapter *adapter)
// Use alternative setting 3 on interface 0 to have 2B+D
if ((status = usb_set_interface (dev, 0, 3)) < 0) {
- WARN("usb_set_interface failed,status=%d",status);
+ WARNING("usb_set_interface failed,status=%d",status);
return status;
}
@@ -497,7 +497,7 @@ static void usb_in_complete(struct urb *urb)
DBG(1,"urb killed status %d", urb->status);
return; // Give up
default:
- WARN("urb status %d",urb->status);
+ WARNING("urb status %d",urb->status);
break;
}
}
@@ -523,7 +523,7 @@ static void usb_in_complete(struct urb *urb)
DBG(4,"count=%d",status);
DBG_PACKET(0x400, in->rcvbuf, status);
if (!(skb = dev_alloc_skb(status))) {
- WARN("receive out of memory\n");
+ WARNING("receive out of memory\n");
break;
}
memcpy(skb_put(skb, status), in->rcvbuf, status);
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 86a369bc57d..9556262dda5 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -103,6 +103,14 @@ config LEDS_HP6XX
This option enables led support for the handheld
HP Jornada 620/660/680/690.
+config LEDS_PCA9532
+ tristate "LED driver for PCA9532 dimmer"
+ depends on LEDS_CLASS && I2C && INPUT && EXPERIMENTAL
+ help
+ This option enables support for NXP pca9532
+ led controller. It is generally only usefull
+ as a platform driver
+
config LEDS_GPIO
tristate "LED Support for GPIO connected LEDs"
depends on LEDS_CLASS && GENERIC_GPIO
@@ -147,6 +155,14 @@ config LEDS_CLEVO_MAIL
To compile this driver as a module, choose M here: the
module will be called leds-clevo-mail.
+config LEDS_PCA955X
+ tristate "LED Support for PCA955x I2C chips"
+ depends on LEDS_CLASS && I2C
+ help
+ This option enables support for LEDs connected to PCA955x
+ LED driver chips accessed via the I2C bus. Supported
+ devices include PCA9550, PCA9551, PCA9552, and PCA9553.
+
comment "LED Triggers"
config LEDS_TRIGGERS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 973d626f5f4..ff7982b4456 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -16,11 +16,13 @@ obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o
obj-$(CONFIG_LEDS_H1940) += leds-h1940.o
obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o
obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o
+obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o
obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
obj-$(CONFIG_LEDS_CM_X270) += leds-cm-x270.o
obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
obj-$(CONFIG_LEDS_FSG) += leds-fsg.o
+obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o
# LED Triggers
obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index 0f242b3f09b..f910eaffe3a 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -111,16 +111,17 @@ void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
flags);
if (led_cdev->trigger->deactivate)
led_cdev->trigger->deactivate(led_cdev);
+ led_cdev->trigger = NULL;
led_set_brightness(led_cdev, LED_OFF);
}
if (trigger) {
write_lock_irqsave(&trigger->leddev_list_lock, flags);
list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
write_unlock_irqrestore(&trigger->leddev_list_lock, flags);
+ led_cdev->trigger = trigger;
if (trigger->activate)
trigger->activate(led_cdev);
}
- led_cdev->trigger = trigger;
}
EXPORT_SYMBOL_GPL(led_trigger_set);
diff --git a/drivers/leds/leds-atmel-pwm.c b/drivers/leds/leds-atmel-pwm.c
index 28db6c1444e..52297c3ab24 100644
--- a/drivers/leds/leds-atmel-pwm.c
+++ b/drivers/leds/leds-atmel-pwm.c
@@ -37,7 +37,7 @@ static int __init pwmled_probe(struct platform_device *pdev)
{
const struct gpio_led_platform_data *pdata;
struct pwmled *leds;
- unsigned i;
+ int i;
int status;
pdata = pdev->dev.platform_data;
diff --git a/drivers/leds/leds-h1940.c b/drivers/leds/leds-h1940.c
index bcec4223038..73c70502168 100644
--- a/drivers/leds/leds-h1940.c
+++ b/drivers/leds/leds-h1940.c
@@ -23,7 +23,8 @@
/*
* Green led.
*/
-void h1940_greenled_set(struct led_classdev *led_dev, enum led_brightness value)
+static void h1940_greenled_set(struct led_classdev *led_dev,
+ enum led_brightness value)
{
switch (value) {
case LED_HALF:
@@ -52,7 +53,8 @@ static struct led_classdev h1940_greenled = {
/*
* Red led.
*/
-void h1940_redled_set(struct led_classdev *led_dev, enum led_brightness value)
+static void h1940_redled_set(struct led_classdev *led_dev,
+ enum led_brightness value)
{
switch (value) {
case LED_HALF:
@@ -82,7 +84,8 @@ static struct led_classdev h1940_redled = {
* Blue led.
* (it can only be blue flashing led)
*/
-void h1940_blueled_set(struct led_classdev *led_dev, enum led_brightness value)
+static void h1940_blueled_set(struct led_classdev *led_dev,
+ enum led_brightness value)
{
if (value) {
/* flashing Blue */
diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c
new file mode 100644
index 00000000000..4064d4f6b33
--- /dev/null
+++ b/drivers/leds/leds-pca9532.c
@@ -0,0 +1,337 @@
+/*
+ * pca9532.c - 16-bit Led dimmer
+ *
+ * Copyright (C) 2008 Riku Voipio <riku.voipio@movial.fi>
+ *
+ * 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.
+ *
+ * Datasheet: http://www.nxp.com/acrobat/datasheets/PCA9532_3.pdf
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/leds.h>
+#include <linux/input.h>
+#include <linux/mutex.h>
+#include <linux/leds-pca9532.h>
+
+static const unsigned short normal_i2c[] = { /*0x60,*/ I2C_CLIENT_END};
+I2C_CLIENT_INSMOD_1(pca9532);
+
+#define PCA9532_REG_PSC(i) (0x2+(i)*2)
+#define PCA9532_REG_PWM(i) (0x3+(i)*2)
+#define PCA9532_REG_LS0 0x6
+#define LED_REG(led) ((led>>2)+PCA9532_REG_LS0)
+#define LED_NUM(led) (led & 0x3)
+
+#define ldev_to_led(c) container_of(c, struct pca9532_led, ldev)
+
+struct pca9532_data {
+ struct i2c_client *client;
+ struct pca9532_led leds[16];
+ struct mutex update_lock;
+ struct input_dev *idev;
+ u8 pwm[2];
+ u8 psc[2];
+};
+
+static int pca9532_probe(struct i2c_client *client,
+ const struct i2c_device_id *id);
+static int pca9532_remove(struct i2c_client *client);
+
+static const struct i2c_device_id pca9532_id[] = {
+ { "pca9532", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, pca9532_id);
+
+static struct i2c_driver pca9532_driver = {
+ .driver = {
+ .name = "pca9532",
+ },
+ .probe = pca9532_probe,
+ .remove = pca9532_remove,
+ .id_table = pca9532_id,
+};
+
+/* We have two pwm/blinkers, but 16 possible leds to drive. Additionaly,
+ * the clever Thecus people are using one pwm to drive the beeper. So,
+ * as a compromise we average one pwm to the values requested by all
+ * leds that are not ON/OFF.
+ * */
+static int pca9532_setpwm(struct i2c_client *client, int pwm, int blink,
+ enum led_brightness value)
+{
+ int a = 0, b = 0, i = 0;
+ struct pca9532_data *data = i2c_get_clientdata(client);
+ for (i = 0; i < 16; i++) {
+ if (data->leds[i].type == PCA9532_TYPE_LED &&
+ data->leds[i].state == PCA9532_PWM0+pwm) {
+ a++;
+ b += data->leds[i].ldev.brightness;
+ }
+ }
+ if (a == 0) {
+ dev_err(&client->dev,
+ "fear of division by zero %d/%d, wanted %d\n",
+ b, a, value);
+ return -EINVAL;
+ }
+ b = b/a;
+ if (b > 0xFF)
+ return -EINVAL;
+ mutex_lock(&data->update_lock);
+ data->pwm[pwm] = b;
+ i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm),
+ data->pwm[pwm]);
+ data->psc[pwm] = blink;
+ i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm),
+ data->psc[pwm]);
+ mutex_unlock(&data->update_lock);
+ return 0;
+}
+
+/* Set LED routing */
+static void pca9532_setled(struct pca9532_led *led)
+{
+ struct i2c_client *client = led->client;
+ struct pca9532_data *data = i2c_get_clientdata(client);
+ char reg;
+
+ mutex_lock(&data->update_lock);
+ reg = i2c_smbus_read_byte_data(client, LED_REG(led->id));
+ /* zero led bits */
+ reg = reg & ~(0x3<<LED_NUM(led->id)*2);
+ /* set the new value */
+ reg = reg | (led->state << LED_NUM(led->id)*2);
+ i2c_smbus_write_byte_data(client, LED_REG(led->id), reg);
+ mutex_unlock(&data->update_lock);
+}
+
+static void pca9532_set_brightness(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ int err = 0;
+ struct pca9532_led *led = ldev_to_led(led_cdev);
+
+ if (value == LED_OFF)
+ led->state = PCA9532_OFF;
+ else if (value == LED_FULL)
+ led->state = PCA9532_ON;
+ else {
+ led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */
+ err = pca9532_setpwm(led->client, 0, 0, value);
+ if (err)
+ return; /* XXX: led api doesn't allow error code? */
+ }
+ pca9532_setled(led);
+}
+
+static int pca9532_set_blink(struct led_classdev *led_cdev,
+ unsigned long *delay_on, unsigned long *delay_off)
+{
+ struct pca9532_led *led = ldev_to_led(led_cdev);
+ struct i2c_client *client = led->client;
+ int psc;
+
+ if (*delay_on == 0 && *delay_off == 0) {
+ /* led subsystem ask us for a blink rate */
+ *delay_on = 1000;
+ *delay_off = 1000;
+ }
+ if (*delay_on != *delay_off || *delay_on > 1690 || *delay_on < 6)
+ return -EINVAL;
+
+ /* Thecus specific: only use PSC/PWM 0 */
+ psc = (*delay_on * 152-1)/1000;
+ return pca9532_setpwm(client, 0, psc, led_cdev->brightness);
+}
+
+int pca9532_event(struct input_dev *dev, unsigned int type, unsigned int code,
+ int value)
+{
+ struct pca9532_data *data = input_get_drvdata(dev);
+
+ if (type != EV_SND && (code != SND_BELL || code != SND_TONE))
+ return -1;
+
+ /* XXX: allow different kind of beeps with psc/pwm modifications */
+ if (value > 1 && value < 32767)
+ data->pwm[1] = 127;
+ else
+ data->pwm[1] = 0;
+
+ dev_info(&dev->dev, "setting beep to %d \n", data->pwm[1]);
+ mutex_lock(&data->update_lock);
+ i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1),
+ data->pwm[1]);
+ mutex_unlock(&data->update_lock);
+
+ return 0;
+}
+
+static int pca9532_configure(struct i2c_client *client,
+ struct pca9532_data *data, struct pca9532_platform_data *pdata)
+{
+ int i, err = 0;
+
+ for (i = 0; i < 2; i++) {
+ data->pwm[i] = pdata->pwm[i];
+ data->psc[i] = pdata->psc[i];
+ i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(i),
+ data->pwm[i]);
+ i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(i),
+ data->psc[i]);
+ }
+
+ for (i = 0; i < 16; i++) {
+ struct pca9532_led *led = &data->leds[i];
+ struct pca9532_led *pled = &pdata->leds[i];
+ led->client = client;
+ led->id = i;
+ led->type = pled->type;
+ switch (led->type) {
+ case PCA9532_TYPE_NONE:
+ break;
+ case PCA9532_TYPE_LED:
+ led->state = pled->state;
+ led->name = pled->name;
+ led->ldev.name = led->name;
+ led->ldev.brightness = LED_OFF;
+ led->ldev.brightness_set = pca9532_set_brightness;
+ led->ldev.blink_set = pca9532_set_blink;
+ if (led_classdev_register(&client->dev,
+ &led->ldev) < 0) {
+ dev_err(&client->dev,
+ "couldn't register LED %s\n",
+ led->name);
+ goto exit;
+ }
+ pca9532_setled(led);
+ break;
+ case PCA9532_TYPE_N2100_BEEP:
+ BUG_ON(data->idev);
+ led->state = PCA9532_PWM1;
+ pca9532_setled(led);
+ data->idev = input_allocate_device();
+ if (data->idev == NULL) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ data->idev->name = pled->name;
+ data->idev->phys = "i2c/pca9532";
+ data->idev->id.bustype = BUS_HOST;
+ data->idev->id.vendor = 0x001f;
+ data->idev->id.product = 0x0001;
+ data->idev->id.version = 0x0100;
+ data->idev->evbit[0] = BIT_MASK(EV_SND);
+ data->idev->sndbit[0] = BIT_MASK(SND_BELL) |
+ BIT_MASK(SND_TONE);
+ data->idev->event = pca9532_event;
+ input_set_drvdata(data->idev, data);
+ err = input_register_device(data->idev);
+ if (err) {
+ input_free_device(data->idev);
+ data->idev = NULL;
+ goto exit;
+ }
+ break;
+ }
+ }
+ return 0;
+
+exit:
+ if (i > 0)
+ for (i = i - 1; i >= 0; i--)
+ switch (data->leds[i].type) {
+ case PCA9532_TYPE_NONE:
+ break;
+ case PCA9532_TYPE_LED:
+ led_classdev_unregister(&data->leds[i].ldev);
+ break;
+ case PCA9532_TYPE_N2100_BEEP:
+ if (data->idev != NULL) {
+ input_unregister_device(data->idev);
+ input_free_device(data->idev);
+ data->idev = NULL;
+ }
+ break;
+ }
+
+ return err;
+
+}
+
+static int pca9532_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pca9532_data *data = i2c_get_clientdata(client);
+ struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ data = kzalloc(sizeof(struct pca9532_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ dev_info(&client->dev, "setting platform data\n");
+ i2c_set_clientdata(client, data);
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ if (pca9532_pdata == NULL)
+ return -EIO;
+
+ pca9532_configure(client, data, pca9532_pdata);
+ return 0;
+
+}
+
+static int pca9532_remove(struct i2c_client *client)
+{
+ struct pca9532_data *data = i2c_get_clientdata(client);
+ int i;
+ for (i = 0; i < 16; i++)
+ switch (data->leds[i].type) {
+ case PCA9532_TYPE_NONE:
+ break;
+ case PCA9532_TYPE_LED:
+ led_classdev_unregister(&data->leds[i].ldev);
+ break;
+ case PCA9532_TYPE_N2100_BEEP:
+ if (data->idev != NULL) {
+ input_unregister_device(data->idev);
+ input_free_device(data->idev);
+ data->idev = NULL;
+ }
+ break;
+ }
+
+ kfree(data);
+ i2c_set_clientdata(client, NULL);
+ return 0;
+}
+
+static int __init pca9532_init(void)
+{
+ return i2c_add_driver(&pca9532_driver);
+}
+
+static void __exit pca9532_exit(void)
+{
+ i2c_del_driver(&pca9532_driver);
+}
+
+MODULE_AUTHOR("Riku Voipio <riku.voipio@movial.fi>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PCA 9532 LED dimmer");
+
+module_init(pca9532_init);
+module_exit(pca9532_exit);
+
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
new file mode 100644
index 00000000000..146c0697286
--- /dev/null
+++ b/drivers/leds/leds-pca955x.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright 2007-2008 Extreme Engineering Solutions, Inc.
+ *
+ * Author: Nate Case <ncase@xes-inc.com>
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * LED driver for various PCA955x I2C LED drivers
+ *
+ * Supported devices:
+ *
+ * Device Description 7-bit slave address
+ * ------ ----------- -------------------
+ * PCA9550 2-bit driver 0x60 .. 0x61
+ * PCA9551 8-bit driver 0x60 .. 0x67
+ * PCA9552 16-bit driver 0x60 .. 0x67
+ * PCA9553/01 4-bit driver 0x62
+ * PCA9553/02 4-bit driver 0x63
+ *
+ * Philips PCA955x LED driver chips follow a register map as shown below:
+ *
+ * Control Register Description
+ * ---------------- -----------
+ * 0x0 Input register 0
+ * ..
+ * NUM_INPUT_REGS - 1 Last Input register X
+ *
+ * NUM_INPUT_REGS Frequency prescaler 0
+ * NUM_INPUT_REGS + 1 PWM register 0
+ * NUM_INPUT_REGS + 2 Frequency prescaler 1
+ * NUM_INPUT_REGS + 3 PWM register 1
+ *
+ * NUM_INPUT_REGS + 4 LED selector 0
+ * NUM_INPUT_REGS + 4
+ * + NUM_LED_REGS - 1 Last LED selector
+ *
+ * where NUM_INPUT_REGS and NUM_LED_REGS vary depending on how many
+ * bits the chip supports.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/leds.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+
+/* LED select registers determine the source that drives LED outputs */
+#define PCA955X_LS_LED_ON 0x0 /* Output LOW */
+#define PCA955X_LS_LED_OFF 0x1 /* Output HI-Z */
+#define PCA955X_LS_BLINK0 0x2 /* Blink at PWM0 rate */
+#define PCA955X_LS_BLINK1 0x3 /* Blink at PWM1 rate */
+
+enum pca955x_type {
+ pca9550,
+ pca9551,
+ pca9552,
+ pca9553,
+};
+
+struct pca955x_chipdef {
+ int bits;
+ u8 slv_addr; /* 7-bit slave address mask */
+ int slv_addr_shift; /* Number of bits to ignore */
+};
+
+static struct pca955x_chipdef pca955x_chipdefs[] = {
+ [pca9550] = {
+ .bits = 2,
+ .slv_addr = /* 110000x */ 0x60,
+ .slv_addr_shift = 1,
+ },
+ [pca9551] = {
+ .bits = 8,
+ .slv_addr = /* 1100xxx */ 0x60,
+ .slv_addr_shift = 3,
+ },
+ [pca9552] = {
+ .bits = 16,
+ .slv_addr = /* 1100xxx */ 0x60,
+ .slv_addr_shift = 3,
+ },
+ [pca9553] = {
+ .bits = 4,
+ .slv_addr = /* 110001x */ 0x62,
+ .slv_addr_shift = 1,
+ },
+};
+
+static const struct i2c_device_id pca955x_id[] = {
+ { "pca9550", pca9550 },
+ { "pca9551", pca9551 },
+ { "pca9552", pca9552 },
+ { "pca9553", pca9553 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pca955x_id);
+
+struct pca955x_led {
+ struct pca955x_chipdef *chipdef;
+ struct i2c_client *client;
+ struct work_struct work;
+ spinlock_t lock;
+ enum led_brightness brightness;
+ struct led_classdev led_cdev;
+ int led_num; /* 0 .. 15 potentially */
+ char name[32];
+};
+
+/* 8 bits per input register */
+static inline int pca95xx_num_input_regs(int bits)
+{
+ return (bits + 7) / 8;
+}
+
+/* 4 bits per LED selector register */
+static inline int pca95xx_num_led_regs(int bits)
+{
+ return (bits + 3) / 4;
+}
+
+/*
+ * Return an LED selector register value based on an existing one, with
+ * the appropriate 2-bit state value set for the given LED number (0-3).
+ */
+static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state)
+{
+ return (oldval & (~(0x3 << (led_num << 1)))) |
+ ((state & 0x3) << (led_num << 1));
+}
+
+/*
+ * Write to frequency prescaler register, used to program the
+ * period of the PWM output. period = (PSCx + 1) / 38
+ */
+static void pca955x_write_psc(struct i2c_client *client, int n, u8 val)
+{
+ struct pca955x_led *pca955x = i2c_get_clientdata(client);
+
+ i2c_smbus_write_byte_data(client,
+ pca95xx_num_input_regs(pca955x->chipdef->bits) + 2*n,
+ val);
+}
+
+/*
+ * Write to PWM register, which determines the duty cycle of the
+ * output. LED is OFF when the count is less than the value of this
+ * register, and ON when it is greater. If PWMx == 0, LED is always OFF.
+ *
+ * Duty cycle is (256 - PWMx) / 256
+ */
+static void pca955x_write_pwm(struct i2c_client *client, int n, u8 val)
+{
+ struct pca955x_led *pca955x = i2c_get_clientdata(client);
+
+ i2c_smbus_write_byte_data(client,
+ pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + 2*n,
+ val);
+}
+
+/*
+ * Write to LED selector register, which determines the source that
+ * drives the LED output.
+ */
+static void pca955x_write_ls(struct i2c_client *client, int n, u8 val)
+{
+ struct pca955x_led *pca955x = i2c_get_clientdata(client);
+
+ i2c_smbus_write_byte_data(client,
+ pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n,
+ val);
+}
+
+/*
+ * Read the LED selector register, which determines the source that
+ * drives the LED output.
+ */
+static u8 pca955x_read_ls(struct i2c_client *client, int n)
+{
+ struct pca955x_led *pca955x = i2c_get_clientdata(client);
+
+ return (u8) i2c_smbus_read_byte_data(client,
+ pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n);
+}
+
+static void pca955x_led_work(struct work_struct *work)
+{
+ struct pca955x_led *pca955x;
+ u8 ls;
+ int chip_ls; /* which LSx to use (0-3 potentially) */
+ int ls_led; /* which set of bits within LSx to use (0-3) */
+
+ pca955x = container_of(work, struct pca955x_led, work);
+ chip_ls = pca955x->led_num / 4;
+ ls_led = pca955x->led_num % 4;
+
+ ls = pca955x_read_ls(pca955x->client, chip_ls);
+
+ switch (pca955x->brightness) {
+ case LED_FULL:
+ ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_ON);
+ break;
+ case LED_OFF:
+ ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_OFF);
+ break;
+ case LED_HALF:
+ ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK0);
+ break;
+ default:
+ /*
+ * Use PWM1 for all other values. This has the unwanted
+ * side effect of making all LEDs on the chip share the
+ * same brightness level if set to a value other than
+ * OFF, HALF, or FULL. But, this is probably better than
+ * just turning off for all other values.
+ */
+ pca955x_write_pwm(pca955x->client, 1, 255-pca955x->brightness);
+ ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1);
+ break;
+ }
+
+ pca955x_write_ls(pca955x->client, chip_ls, ls);
+}
+
+void pca955x_led_set(struct led_classdev *led_cdev, enum led_brightness value)
+{
+ struct pca955x_led *pca955x;
+
+ pca955x = container_of(led_cdev, struct pca955x_led, led_cdev);
+
+ spin_lock(&pca955x->lock);
+ pca955x->brightness = value;
+
+ /*
+ * Must use workqueue for the actual I/O since I2C operations
+ * can sleep.
+ */
+ schedule_work(&pca955x->work);
+
+ spin_unlock(&pca955x->lock);
+}
+
+static int __devinit pca955x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pca955x_led *pca955x;
+ int i;
+ int err = -ENODEV;
+ struct pca955x_chipdef *chip;
+ struct i2c_adapter *adapter;
+ struct led_platform_data *pdata;
+
+ chip = &pca955x_chipdefs[id->driver_data];
+ adapter = to_i2c_adapter(client->dev.parent);
+ pdata = client->dev.platform_data;
+
+ /* Make sure the slave address / chip type combo given is possible */
+ if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) !=
+ chip->slv_addr) {
+ dev_err(&client->dev, "invalid slave address %02x\n",
+ client->addr);
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "leds-pca955x: Using %s %d-bit LED driver at "
+ "slave address 0x%02x\n",
+ id->name, chip->bits, client->addr);
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
+ return -EIO;
+
+ if (pdata) {
+ if (pdata->num_leds != chip->bits) {
+ dev_err(&client->dev, "board info claims %d LEDs"
+ " on a %d-bit chip\n",
+ pdata->num_leds, chip->bits);
+ return -ENODEV;
+ }
+ }
+
+ for (i = 0; i < chip->bits; i++) {
+ pca955x = kzalloc(sizeof(struct pca955x_led), GFP_KERNEL);
+ if (!pca955x) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ pca955x->chipdef = chip;
+ pca955x->client = client;
+ pca955x->led_num = i;
+ /* Platform data can specify LED names and default triggers */
+ if (pdata) {
+ if (pdata->leds[i].name)
+ snprintf(pca955x->name, 32, "pca955x:%s",
+ pdata->leds[i].name);
+ if (pdata->leds[i].default_trigger)
+ pca955x->led_cdev.default_trigger =
+ pdata->leds[i].default_trigger;
+ } else {
+ snprintf(pca955x->name, 32, "pca955x:%d", i);
+ }
+ spin_lock_init(&pca955x->lock);
+
+ pca955x->led_cdev.name = pca955x->name;
+ pca955x->led_cdev.brightness_set =
+ pca955x_led_set;
+
+ /*
+ * Client data is a pointer to the _first_ pca955x_led
+ * struct
+ */
+ if (i == 0)
+ i2c_set_clientdata(client, pca955x);
+
+ INIT_WORK(&(pca955x->work), pca955x_led_work);
+
+ led_classdev_register(&client->dev, &(pca955x->led_cdev));
+ }
+
+ /* Turn off LEDs */
+ for (i = 0; i < pca95xx_num_led_regs(chip->bits); i++)
+ pca955x_write_ls(client, i, 0x55);
+
+ /* PWM0 is used for half brightness or 50% duty cycle */
+ pca955x_write_pwm(client, 0, 255-LED_HALF);
+
+ /* PWM1 is used for variable brightness, default to OFF */
+ pca955x_write_pwm(client, 1, 0);
+
+ /* Set to fast frequency so we do not see flashing */
+ pca955x_write_psc(client, 0, 0);
+ pca955x_write_psc(client, 1, 0);
+
+ return 0;
+exit:
+ return err;
+}
+
+static int __devexit pca955x_remove(struct i2c_client *client)
+{
+ struct pca955x_led *pca955x = i2c_get_clientdata(client);
+ int leds = pca955x->chipdef->bits;
+ int i;
+
+ for (i = 0; i < leds; i++) {
+ led_classdev_unregister(&(pca955x->led_cdev));
+ cancel_work_sync(&(pca955x->work));
+ kfree(pca955x);
+ pca955x = pca955x + 1;
+ }
+
+ return 0;
+}
+
+static struct i2c_driver pca955x_driver = {
+ .driver = {
+ .name = "leds-pca955x",
+ .owner = THIS_MODULE,
+ },
+ .probe = pca955x_probe,
+ .remove = __devexit_p(pca955x_remove),
+ .id_table = pca955x_id,
+};
+
+static int __init pca955x_leds_init(void)
+{
+ return i2c_add_driver(&pca955x_driver);
+}
+
+static void __exit pca955x_leds_exit(void)
+{
+ i2c_del_driver(&pca955x_driver);
+}
+
+module_init(pca955x_leds_init);
+module_exit(pca955x_leds_exit);
+
+MODULE_AUTHOR("Nate Case <ncase@xes-inc.com>");
+MODULE_DESCRIPTION("PCA955x LED driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c
index 1a8de57289e..37344aaee22 100644
--- a/drivers/lguest/lguest_device.c
+++ b/drivers/lguest/lguest_device.c
@@ -98,16 +98,20 @@ static u32 lg_get_features(struct virtio_device *vdev)
return features;
}
-static void lg_set_features(struct virtio_device *vdev, u32 features)
+static void lg_finalize_features(struct virtio_device *vdev)
{
- unsigned int i;
+ unsigned int i, bits;
struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
/* Second half of bitmap is features we accept. */
u8 *out_features = lg_features(desc) + desc->feature_len;
+ /* Give virtio_ring a chance to accept features. */
+ vring_transport_features(vdev);
+
memset(out_features, 0, desc->feature_len);
- for (i = 0; i < min(desc->feature_len * 8, 32); i++) {
- if (features & (1 << i))
+ bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8;
+ for (i = 0; i < bits; i++) {
+ if (test_bit(i, vdev->features))
out_features[i / 8] |= (1 << (i % 8));
}
}
@@ -297,7 +301,7 @@ static void lg_del_vq(struct virtqueue *vq)
/* The ops structure which hooks everything together. */
static struct virtio_config_ops lguest_config_ops = {
.get_features = lg_get_features,
- .set_features = lg_set_features,
+ .finalize_features = lg_finalize_features,
.get = lg_get,
.set = lg_set,
.get_status = lg_get_status,
diff --git a/drivers/media/video/pvrusb2/pvrusb2-dvb.c b/drivers/media/video/pvrusb2/pvrusb2-dvb.c
index 6ec4bf81fc7..77b3c338506 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-dvb.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-dvb.c
@@ -20,6 +20,7 @@
#include <linux/kthread.h>
#include <linux/freezer.h>
+#include <linux/mm.h>
#include "dvbdev.h"
#include "pvrusb2-debug.h"
#include "pvrusb2-hdw-internal.h"
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/drivers/media/video/pvrusb2/pvrusb2-ioread.c
index 05a1376405e..b4824782d85 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-ioread.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-ioread.c
@@ -22,6 +22,7 @@
#include "pvrusb2-debug.h"
#include <linux/errno.h>
#include <linux/string.h>
+#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>
diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c
index 7388d0cee3d..5646a6a3293 100644
--- a/drivers/media/video/uvc/uvc_queue.c
+++ b/drivers/media/video/uvc/uvc_queue.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/version.h>
+#include <linux/mm.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/usb.h>
diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c
index 0a88c44ace0..b7b05842cf2 100644
--- a/drivers/media/video/videobuf-core.c
+++ b/drivers/media/video/videobuf-core.c
@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
diff --git a/drivers/message/i2o/device.c b/drivers/message/i2o/device.c
index 489d7c5c496..8774c670e66 100644
--- a/drivers/message/i2o/device.c
+++ b/drivers/message/i2o/device.c
@@ -243,29 +243,41 @@ static int i2o_device_add(struct i2o_controller *c, i2o_lct_entry *entry)
/* create user entries for this device */
tmp = i2o_iop_find_device(i2o_dev->iop, i2o_dev->lct_data.user_tid);
- if (tmp && (tmp != i2o_dev))
- sysfs_create_link(&i2o_dev->device.kobj, &tmp->device.kobj,
- "user");
+ if (tmp && (tmp != i2o_dev)) {
+ rc = sysfs_create_link(&i2o_dev->device.kobj,
+ &tmp->device.kobj, "user");
+ if (rc)
+ goto unreg_dev;
+ }
/* create user entries refering to this device */
list_for_each_entry(tmp, &c->devices, list)
if ((tmp->lct_data.user_tid == i2o_dev->lct_data.tid)
- && (tmp != i2o_dev))
- sysfs_create_link(&tmp->device.kobj,
- &i2o_dev->device.kobj, "user");
+ && (tmp != i2o_dev)) {
+ rc = sysfs_create_link(&tmp->device.kobj,
+ &i2o_dev->device.kobj, "user");
+ if (rc)
+ goto rmlink1;
+ }
/* create parent entries for this device */
tmp = i2o_iop_find_device(i2o_dev->iop, i2o_dev->lct_data.parent_tid);
- if (tmp && (tmp != i2o_dev))
- sysfs_create_link(&i2o_dev->device.kobj, &tmp->device.kobj,
- "parent");
+ if (tmp && (tmp != i2o_dev)) {
+ rc = sysfs_create_link(&i2o_dev->device.kobj,
+ &tmp->device.kobj, "parent");
+ if (rc)
+ goto rmlink1;
+ }
/* create parent entries refering to this device */
list_for_each_entry(tmp, &c->devices, list)
if ((tmp->lct_data.parent_tid == i2o_dev->lct_data.tid)
- && (tmp != i2o_dev))
- sysfs_create_link(&tmp->device.kobj,
- &i2o_dev->device.kobj, "parent");
+ && (tmp != i2o_dev)) {
+ rc = sysfs_create_link(&tmp->device.kobj,
+ &i2o_dev->device.kobj, "parent");
+ if (rc)
+ goto rmlink2;
+ }
i2o_driver_notify_device_add_all(i2o_dev);
@@ -273,6 +285,24 @@ static int i2o_device_add(struct i2o_controller *c, i2o_lct_entry *entry)
return 0;
+rmlink2:
+ /* If link creating failed halfway, we loop whole list to cleanup.
+ * And we don't care wrong removing of link, because sysfs_remove_link
+ * will take care of it.
+ */
+ list_for_each_entry(tmp, &c->devices, list) {
+ if (tmp->lct_data.parent_tid == i2o_dev->lct_data.tid)
+ sysfs_remove_link(&tmp->device.kobj, "parent");
+ }
+ sysfs_remove_link(&i2o_dev->device.kobj, "parent");
+rmlink1:
+ list_for_each_entry(tmp, &c->devices, list)
+ if (tmp->lct_data.user_tid == i2o_dev->lct_data.tid)
+ sysfs_remove_link(&tmp->device.kobj, "user");
+ sysfs_remove_link(&i2o_dev->device.kobj, "user");
+unreg_dev:
+ list_del(&i2o_dev->list);
+ device_unregister(&i2o_dev->device);
err:
kfree(i2o_dev);
return rc;
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 9f93c29fed3..883e7ea31de 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -19,6 +19,14 @@ config MFD_SM501
interface. The device may be connected by PCI or local bus with
varying functions enabled.
+config MFD_SM501_GPIO
+ bool "Export GPIO via GPIO layer"
+ depends on MFD_SM501 && HAVE_GPIO_LIB
+ ---help---
+ This option uses the gpio library layer to export the 64 GPIO
+ lines on the SM501. The platform data is used to supply the
+ base number for the first GPIO line to register.
+
config MFD_ASIC3
bool "Support for Compaq ASIC3"
depends on GENERIC_HARDIRQS && HAVE_GPIO_LIB && ARM
@@ -28,7 +36,7 @@ config MFD_ASIC3
config HTC_EGPIO
bool "HTC EGPIO support"
- depends on GENERIC_HARDIRQS && HAVE_GPIO_LIB && ARM
+ depends on GENERIC_HARDIRQS && GPIOLIB && ARM
help
This driver supports the CPLD egpio chip present on
several HTC phones. It provides basic support for input
@@ -44,7 +52,7 @@ config HTC_PASIC3
config MFD_TC6393XB
bool "Support Toshiba TC6393XB"
- depends on HAVE_GPIO_LIB
+ depends on GPIOLIB && ARM
select MFD_CORE
help
Support for Toshiba Mobile IO Controller TC6393XB
diff --git a/drivers/mfd/htc-egpio.c b/drivers/mfd/htc-egpio.c
index 8872cc07751..6be43172dc6 100644
--- a/drivers/mfd/htc-egpio.c
+++ b/drivers/mfd/htc-egpio.c
@@ -318,6 +318,8 @@ static int __init egpio_probe(struct platform_device *pdev)
ei->chip[i].dev = &(pdev->dev);
chip = &(ei->chip[i].chip);
chip->label = "htc-egpio";
+ chip->dev = &pdev->dev;
+ chip->owner = THIS_MODULE;
chip->get = egpio_get;
chip->set = egpio_set;
chip->direction_input = egpio_direction_input;
diff --git a/drivers/mfd/htc-pasic3.c b/drivers/mfd/htc-pasic3.c
index 633cbba072f..91b294dcc13 100644
--- a/drivers/mfd/htc-pasic3.c
+++ b/drivers/mfd/htc-pasic3.c
@@ -238,6 +238,8 @@ static int pasic3_remove(struct platform_device *pdev)
return 0;
}
+MODULE_ALIAS("platform:pasic3");
+
static struct platform_driver pasic3_driver = {
.driver = {
.name = "pasic3",
diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c
index 1eab7cffcea..b5272b5ce3f 100644
--- a/drivers/mfd/mcp-sa11x0.c
+++ b/drivers/mfd/mcp-sa11x0.c
@@ -242,6 +242,8 @@ static int mcp_sa11x0_resume(struct platform_device *dev)
/*
* The driver for the SA11x0 MCP port.
*/
+MODULE_ALIAS("platform:sa11x0-mcp");
+
static struct platform_driver mcp_sa11x0_driver = {
.probe = mcp_sa11x0_probe,
.remove = mcp_sa11x0_remove,
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index d7d88ce053a..0454be4266c 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -36,7 +36,7 @@ static int mfd_add_device(struct platform_device *parent,
if (ret)
goto fail_device;
- memzero(res, sizeof(res));
+ memset(res, 0, sizeof(res));
for (r = 0; r < cell->num_resources; r++) {
res[r].name = cell->resources[r].name;
res[r].flags = cell->resources[r].flags;
diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c
index 2fe64734d8a..7aebad4c06f 100644
--- a/drivers/mfd/sm501.c
+++ b/drivers/mfd/sm501.c
@@ -19,6 +19,7 @@
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
+#include <linux/i2c-gpio.h>
#include <linux/sm501.h>
#include <linux/sm501-regs.h>
@@ -31,10 +32,37 @@ struct sm501_device {
struct platform_device pdev;
};
+struct sm501_gpio;
+
+#ifdef CONFIG_MFD_SM501_GPIO
+#include <linux/gpio.h>
+
+struct sm501_gpio_chip {
+ struct gpio_chip gpio;
+ struct sm501_gpio *ourgpio; /* to get back to parent. */
+ void __iomem *regbase;
+};
+
+struct sm501_gpio {
+ struct sm501_gpio_chip low;
+ struct sm501_gpio_chip high;
+ spinlock_t lock;
+
+ unsigned int registered : 1;
+ void __iomem *regs;
+ struct resource *regs_res;
+};
+#else
+struct sm501_gpio {
+ /* no gpio support, empty definition for sm501_devdata. */
+};
+#endif
+
struct sm501_devdata {
spinlock_t reg_lock;
struct mutex clock_lock;
struct list_head devices;
+ struct sm501_gpio gpio;
struct device *dev;
struct resource *io_res;
@@ -42,6 +70,7 @@ struct sm501_devdata {
struct resource *regs_claim;
struct sm501_platdata *platdata;
+
unsigned int in_suspend;
unsigned long pm_misc;
@@ -52,6 +81,7 @@ struct sm501_devdata {
unsigned int rev;
};
+
#define MHZ (1000 * 1000)
#ifdef DEBUG
@@ -276,58 +306,6 @@ unsigned long sm501_modify_reg(struct device *dev,
EXPORT_SYMBOL_GPL(sm501_modify_reg);
-unsigned long sm501_gpio_get(struct device *dev,
- unsigned long gpio)
-{
- struct sm501_devdata *sm = dev_get_drvdata(dev);
- unsigned long result;
- unsigned long reg;
-
- reg = (gpio > 32) ? SM501_GPIO_DATA_HIGH : SM501_GPIO_DATA_LOW;
- result = readl(sm->regs + reg);
-
- result >>= (gpio & 31);
- return result & 1UL;
-}
-
-EXPORT_SYMBOL_GPL(sm501_gpio_get);
-
-void sm501_gpio_set(struct device *dev,
- unsigned long gpio,
- unsigned int to,
- unsigned int dir)
-{
- struct sm501_devdata *sm = dev_get_drvdata(dev);
-
- unsigned long bit = 1 << (gpio & 31);
- unsigned long base;
- unsigned long save;
- unsigned long val;
-
- base = (gpio > 32) ? SM501_GPIO_DATA_HIGH : SM501_GPIO_DATA_LOW;
- base += SM501_GPIO;
-
- spin_lock_irqsave(&sm->reg_lock, save);
-
- val = readl(sm->regs + base) & ~bit;
- if (to)
- val |= bit;
- writel(val, sm->regs + base);
-
- val = readl(sm->regs + SM501_GPIO_DDR_LOW) & ~bit;
- if (dir)
- val |= bit;
-
- writel(val, sm->regs + SM501_GPIO_DDR_LOW);
- sm501_sync_regs(sm);
-
- spin_unlock_irqrestore(&sm->reg_lock, save);
-
-}
-
-EXPORT_SYMBOL_GPL(sm501_gpio_set);
-
-
/* sm501_unit_power
*
* alters the power active gate to set specific units on or off
@@ -906,6 +884,313 @@ static int sm501_register_display(struct sm501_devdata *sm,
return sm501_register_device(sm, pdev);
}
+#ifdef CONFIG_MFD_SM501_GPIO
+
+static inline struct sm501_gpio_chip *to_sm501_gpio(struct gpio_chip *gc)
+{
+ return container_of(gc, struct sm501_gpio_chip, gpio);
+}
+
+static inline struct sm501_devdata *sm501_gpio_to_dev(struct sm501_gpio *gpio)
+{
+ return container_of(gpio, struct sm501_devdata, gpio);
+}
+
+static int sm501_gpio_get(struct gpio_chip *chip, unsigned offset)
+
+{
+ struct sm501_gpio_chip *smgpio = to_sm501_gpio(chip);
+ unsigned long result;
+
+ result = readl(smgpio->regbase + SM501_GPIO_DATA_LOW);
+ result >>= offset;
+
+ return result & 1UL;
+}
+
+static void sm501_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+
+{
+ struct sm501_gpio_chip *smchip = to_sm501_gpio(chip);
+ struct sm501_gpio *smgpio = smchip->ourgpio;
+ unsigned long bit = 1 << offset;
+ void __iomem *regs = smchip->regbase;
+ unsigned long save;
+ unsigned long val;
+
+ dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n",
+ __func__, chip, offset);
+
+ spin_lock_irqsave(&smgpio->lock, save);
+
+ val = readl(regs + SM501_GPIO_DATA_LOW) & ~bit;
+ if (value)
+ val |= bit;
+ writel(val, regs);
+
+ sm501_sync_regs(sm501_gpio_to_dev(smgpio));
+ spin_unlock_irqrestore(&smgpio->lock, save);
+}
+
+static int sm501_gpio_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct sm501_gpio_chip *smchip = to_sm501_gpio(chip);
+ struct sm501_gpio *smgpio = smchip->ourgpio;
+ void __iomem *regs = smchip->regbase;
+ unsigned long bit = 1 << offset;
+ unsigned long save;
+ unsigned long ddr;
+
+ dev_info(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d)\n",
+ __func__, chip, offset);
+
+ spin_lock_irqsave(&smgpio->lock, save);
+
+ ddr = readl(regs + SM501_GPIO_DDR_LOW);
+ writel(ddr & ~bit, regs + SM501_GPIO_DDR_LOW);
+
+ sm501_sync_regs(sm501_gpio_to_dev(smgpio));
+ spin_unlock_irqrestore(&smgpio->lock, save);
+
+ return 0;
+}
+
+static int sm501_gpio_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct sm501_gpio_chip *smchip = to_sm501_gpio(chip);
+ struct sm501_gpio *smgpio = smchip->ourgpio;
+ unsigned long bit = 1 << offset;
+ void __iomem *regs = smchip->regbase;
+ unsigned long save;
+ unsigned long val;
+ unsigned long ddr;
+
+ dev_dbg(sm501_gpio_to_dev(smgpio)->dev, "%s(%p,%d,%d)\n",
+ __func__, chip, offset, value);
+
+ spin_lock_irqsave(&smgpio->lock, save);
+
+ val = readl(regs + SM501_GPIO_DATA_LOW);
+ if (value)
+ val |= bit;
+ else
+ val &= ~bit;
+ writel(val, regs);
+
+ ddr = readl(regs + SM501_GPIO_DDR_LOW);
+ writel(ddr | bit, regs + SM501_GPIO_DDR_LOW);
+
+ sm501_sync_regs(sm501_gpio_to_dev(smgpio));
+ writel(val, regs + SM501_GPIO_DATA_LOW);
+
+ sm501_sync_regs(sm501_gpio_to_dev(smgpio));
+ spin_unlock_irqrestore(&smgpio->lock, save);
+
+ return 0;
+}
+
+static struct gpio_chip gpio_chip_template = {
+ .ngpio = 32,
+ .direction_input = sm501_gpio_input,
+ .direction_output = sm501_gpio_output,
+ .set = sm501_gpio_set,
+ .get = sm501_gpio_get,
+};
+
+static int __devinit sm501_gpio_register_chip(struct sm501_devdata *sm,
+ struct sm501_gpio *gpio,
+ struct sm501_gpio_chip *chip)
+{
+ struct sm501_platdata *pdata = sm->platdata;
+ struct gpio_chip *gchip = &chip->gpio;
+ int base = pdata->gpio_base;
+
+ chip->gpio = gpio_chip_template;
+
+ if (chip == &gpio->high) {
+ if (base > 0)
+ base += 32;
+ chip->regbase = gpio->regs + SM501_GPIO_DATA_HIGH;
+ gchip->label = "SM501-HIGH";
+ } else {
+ chip->regbase = gpio->regs + SM501_GPIO_DATA_LOW;
+ gchip->label = "SM501-LOW";
+ }
+
+ gchip->base = base;
+ chip->ourgpio = gpio;
+
+ return gpiochip_add(gchip);
+}
+
+static int sm501_register_gpio(struct sm501_devdata *sm)
+{
+ struct sm501_gpio *gpio = &sm->gpio;
+ resource_size_t iobase = sm->io_res->start + SM501_GPIO;
+ int ret;
+ int tmp;
+
+ dev_dbg(sm->dev, "registering gpio block %08llx\n",
+ (unsigned long long)iobase);
+
+ spin_lock_init(&gpio->lock);
+
+ gpio->regs_res = request_mem_region(iobase, 0x20, "sm501-gpio");
+ if (gpio->regs_res == NULL) {
+ dev_err(sm->dev, "gpio: failed to request region\n");
+ return -ENXIO;
+ }
+
+ gpio->regs = ioremap(iobase, 0x20);
+ if (gpio->regs == NULL) {
+ dev_err(sm->dev, "gpio: failed to remap registers\n");
+ ret = -ENXIO;
+ goto err_claimed;
+ }
+
+ /* Register both our chips. */
+
+ ret = sm501_gpio_register_chip(sm, gpio, &gpio->low);
+ if (ret) {
+ dev_err(sm->dev, "failed to add low chip\n");
+ goto err_mapped;
+ }
+
+ ret = sm501_gpio_register_chip(sm, gpio, &gpio->high);
+ if (ret) {
+ dev_err(sm->dev, "failed to add high chip\n");
+ goto err_low_chip;
+ }
+
+ gpio->registered = 1;
+
+ return 0;
+
+ err_low_chip:
+ tmp = gpiochip_remove(&gpio->low.gpio);
+ if (tmp) {
+ dev_err(sm->dev, "cannot remove low chip, cannot tidy up\n");
+ return ret;
+ }
+
+ err_mapped:
+ iounmap(gpio->regs);
+
+ err_claimed:
+ release_resource(gpio->regs_res);
+ kfree(gpio->regs_res);
+
+ return ret;
+}
+
+static void sm501_gpio_remove(struct sm501_devdata *sm)
+{
+ struct sm501_gpio *gpio = &sm->gpio;
+ int ret;
+
+ if (!sm->gpio.registered)
+ return;
+
+ ret = gpiochip_remove(&gpio->low.gpio);
+ if (ret)
+ dev_err(sm->dev, "cannot remove low chip, cannot tidy up\n");
+
+ ret = gpiochip_remove(&gpio->high.gpio);
+ if (ret)
+ dev_err(sm->dev, "cannot remove high chip, cannot tidy up\n");
+
+ iounmap(gpio->regs);
+ release_resource(gpio->regs_res);
+ kfree(gpio->regs_res);
+}
+
+static inline int sm501_gpio_pin2nr(struct sm501_devdata *sm, unsigned int pin)
+{
+ struct sm501_gpio *gpio = &sm->gpio;
+ int base = (pin < 32) ? gpio->low.gpio.base : gpio->high.gpio.base;
+
+ return (pin % 32) + base;
+}
+
+static inline int sm501_gpio_isregistered(struct sm501_devdata *sm)
+{
+ return sm->gpio.registered;
+}
+#else
+static inline int sm501_register_gpio(struct sm501_devdata *sm)
+{
+ return 0;
+}
+
+static inline void sm501_gpio_remove(struct sm501_devdata *sm)
+{
+}
+
+static inline int sm501_gpio_pin2nr(struct sm501_devdata *sm, unsigned int pin)
+{
+ return -1;
+}
+
+static inline int sm501_gpio_isregistered(struct sm501_devdata *sm)
+{
+ return 0;
+}
+#endif
+
+static int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm,
+ struct sm501_platdata_gpio_i2c *iic)
+{
+ struct i2c_gpio_platform_data *icd;
+ struct platform_device *pdev;
+
+ pdev = sm501_create_subdev(sm, "i2c-gpio", 0,
+ sizeof(struct i2c_gpio_platform_data));
+ if (!pdev)
+ return -ENOMEM;
+
+ icd = pdev->dev.platform_data;
+
+ /* We keep the pin_sda and pin_scl fields relative in case the
+ * same platform data is passed to >1 SM501.
+ */
+
+ icd->sda_pin = sm501_gpio_pin2nr(sm, iic->pin_sda);
+ icd->scl_pin = sm501_gpio_pin2nr(sm, iic->pin_scl);
+ icd->timeout = iic->timeout;
+ icd->udelay = iic->udelay;
+
+ /* note, we can't use either of the pin numbers, as the i2c-gpio
+ * driver uses the platform.id field to generate the bus number
+ * to register with the i2c core; The i2c core doesn't have enough
+ * entries to deal with anything we currently use.
+ */
+
+ pdev->id = iic->bus_num;
+
+ dev_info(sm->dev, "registering i2c-%d: sda=%d (%d), scl=%d (%d)\n",
+ iic->bus_num,
+ icd->sda_pin, iic->pin_sda, icd->scl_pin, iic->pin_scl);
+
+ return sm501_register_device(sm, pdev);
+}
+
+static int sm501_register_gpio_i2c(struct sm501_devdata *sm,
+ struct sm501_platdata *pdata)
+{
+ struct sm501_platdata_gpio_i2c *iic = pdata->gpio_i2c;
+ int index;
+ int ret;
+
+ for (index = 0; index < pdata->gpio_i2c_nr; index++, iic++) {
+ ret = sm501_register_gpio_i2c_instance(sm, iic);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
/* sm501_dbg_regs
*
* Debug attribute to attach to parent device to show core registers
@@ -1013,6 +1298,7 @@ static unsigned int sm501_mem_local[] = {
static int sm501_init_dev(struct sm501_devdata *sm)
{
struct sm501_initdata *idata;
+ struct sm501_platdata *pdata;
resource_size_t mem_avail;
unsigned long dramctrl;
unsigned long devid;
@@ -1051,7 +1337,9 @@ static int sm501_init_dev(struct sm501_devdata *sm)
/* check to see if we have some device initialisation */
- idata = sm->platdata ? sm->platdata->init : NULL;
+ pdata = sm->platdata;
+ idata = pdata ? pdata->init : NULL;
+
if (idata) {
sm501_init_regs(sm, idata);
@@ -1059,6 +1347,15 @@ static int sm501_init_dev(struct sm501_devdata *sm)
sm501_register_usbhost(sm, &mem_avail);
if (idata->devices & (SM501_USE_UART0 | SM501_USE_UART1))
sm501_register_uart(sm, idata->devices);
+ if (idata->devices & SM501_USE_GPIO)
+ sm501_register_gpio(sm);
+ }
+
+ if (pdata->gpio_i2c != NULL && pdata->gpio_i2c_nr > 0) {
+ if (!sm501_gpio_isregistered(sm))
+ dev_err(sm->dev, "no gpio available for i2c gpio.\n");
+ else
+ sm501_register_gpio_i2c(sm, pdata);
}
ret = sm501_check_clocks(sm);
@@ -1138,8 +1435,31 @@ static int sm501_plat_probe(struct platform_device *dev)
}
#ifdef CONFIG_PM
+
/* power management support */
+static void sm501_set_power(struct sm501_devdata *sm, int on)
+{
+ struct sm501_platdata *pd = sm->platdata;
+
+ if (pd == NULL)
+ return;
+
+ if (pd->get_power) {
+ if (pd->get_power(sm->dev) == on) {
+ dev_dbg(sm->dev, "is already %d\n", on);
+ return;
+ }
+ }
+
+ if (pd->set_power) {
+ dev_dbg(sm->dev, "setting power to %d\n", on);
+
+ pd->set_power(sm->dev, on);
+ sm501_mdelay(sm, 10);
+ }
+}
+
static int sm501_plat_suspend(struct platform_device *pdev, pm_message_t state)
{
struct sm501_devdata *sm = platform_get_drvdata(pdev);
@@ -1148,6 +1468,12 @@ static int sm501_plat_suspend(struct platform_device *pdev, pm_message_t state)
sm->pm_misc = readl(sm->regs + SM501_MISC_CONTROL);
sm501_dump_regs(sm);
+
+ if (sm->platdata) {
+ if (sm->platdata->flags & SM501_FLAG_SUSPEND_OFF)
+ sm501_set_power(sm, 0);
+ }
+
return 0;
}
@@ -1155,6 +1481,8 @@ static int sm501_plat_resume(struct platform_device *pdev)
{
struct sm501_devdata *sm = platform_get_drvdata(pdev);
+ sm501_set_power(sm, 1);
+
sm501_dump_regs(sm);
sm501_dump_gate(sm);
sm501_dump_clk(sm);
@@ -1229,6 +1557,7 @@ static struct sm501_platdata_fb sm501_fb_pdata = {
static struct sm501_platdata sm501_pci_platdata = {
.init = &sm501_pci_initdata,
.fb = &sm501_fb_pdata,
+ .gpio_base = -1,
};
static int sm501_pci_probe(struct pci_dev *dev,
@@ -1335,6 +1664,8 @@ static void sm501_dev_remove(struct sm501_devdata *sm)
sm501_remove_sub(sm, smdev);
device_remove_file(sm->dev, &dev_attr_dbg_regs);
+
+ sm501_gpio_remove(sm);
}
static void sm501_pci_remove(struct pci_dev *dev)
@@ -1378,6 +1709,8 @@ static struct pci_driver sm501_pci_drv = {
.remove = sm501_pci_remove,
};
+MODULE_ALIAS("platform:sm501");
+
static struct platform_driver sm501_plat_drv = {
.driver = {
.name = "sm501",
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index ce67d973d34..321eb913463 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -77,11 +77,13 @@ config IBM_ASM
for your IBM server.
config PHANTOM
- tristate "Sensable PHANToM"
+ tristate "Sensable PHANToM (PCI)"
depends on PCI
help
Say Y here if you want to build a driver for Sensable PHANToM device.
+ This driver is only for PCI PHANToMs.
+
If you choose to build module, its name will be phantom. If unsure,
say N here.
@@ -212,6 +214,18 @@ config TC1100_WMI
This is a driver for the WMI extensions (wireless and bluetooth power
control) of the HP Compaq TC1100 tablet.
+config HP_WMI
+ tristate "HP WMI extras"
+ depends on ACPI_WMI
+ depends on INPUT
+ depends on RFKILL
+ help
+ Say Y here if you want to support WMI-based hotkeys on HP laptops and
+ to read data from WMI such as docking or ambient light sensor state.
+
+ To compile this driver as a module, choose M here: the module will
+ be called hp-wmi.
+
config MSI_LAPTOP
tristate "MSI Laptop Extras"
depends on X86
@@ -279,6 +293,8 @@ config THINKPAD_ACPI
select INPUT
select NEW_LEDS
select LEDS_CLASS
+ select NET
+ select RFKILL
---help---
This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
support for Fn-Fx key combinations, Bluetooth control, video
@@ -422,6 +438,7 @@ config SGI_XP
config HP_ILO
tristate "Channel interface driver for HP iLO/iLO2 processor"
+ depends on PCI
default n
help
The channel interface driver allows applications to communicate
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 688fe76135e..f5e273420c0 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_ACER_WMI) += acer-wmi.o
obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o
obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o
+obj-$(CONFIG_HP_WMI) += hp-wmi.o
obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
obj-$(CONFIG_LKDTM) += lkdtm.o
obj-$(CONFIG_TIFM_CORE) += tifm_core.o
diff --git a/drivers/misc/atmel_pwm.c b/drivers/misc/atmel_pwm.c
index 5b5a14dab3d..6aa5294dfec 100644
--- a/drivers/misc/atmel_pwm.c
+++ b/drivers/misc/atmel_pwm.c
@@ -211,8 +211,7 @@ int pwm_clk_alloc(unsigned prescale, unsigned div)
if ((mr & 0xffff) == 0) {
mr |= val;
ret = PWM_CPR_CLKA;
- }
- if ((mr & (0xffff << 16)) == 0) {
+ } else if ((mr & (0xffff << 16)) == 0) {
mr |= val << 16;
ret = PWM_CPR_CLKB;
}
diff --git a/drivers/misc/hp-wmi.c b/drivers/misc/hp-wmi.c
new file mode 100644
index 00000000000..1dbcbcb323a
--- /dev/null
+++ b/drivers/misc/hp-wmi.c
@@ -0,0 +1,494 @@
+/*
+ * HP WMI hotkeys
+ *
+ * Copyright (C) 2008 Red Hat <mjg@redhat.com>
+ *
+ * Portions based on wistron_btns.c:
+ * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
+ * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
+ * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <acpi/acpi_drivers.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+#include <linux/rfkill.h>
+#include <linux/string.h>
+
+MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");
+MODULE_DESCRIPTION("HP laptop WMI hotkeys driver");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C");
+MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
+
+#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
+#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"
+
+#define HPWMI_DISPLAY_QUERY 0x1
+#define HPWMI_HDDTEMP_QUERY 0x2
+#define HPWMI_ALS_QUERY 0x3
+#define HPWMI_DOCK_QUERY 0x4
+#define HPWMI_WIRELESS_QUERY 0x5
+
+static int __init hp_wmi_bios_setup(struct platform_device *device);
+static int __exit hp_wmi_bios_remove(struct platform_device *device);
+
+struct bios_args {
+ u32 signature;
+ u32 command;
+ u32 commandtype;
+ u32 datasize;
+ u32 data;
+};
+
+struct bios_return {
+ u32 sigpass;
+ u32 return_code;
+ u32 value;
+};
+
+struct key_entry {
+ char type; /* See KE_* below */
+ u8 code;
+ u16 keycode;
+};
+
+enum { KE_KEY, KE_SW, KE_END };
+
+static struct key_entry hp_wmi_keymap[] = {
+ {KE_SW, 0x01, SW_DOCK},
+ {KE_KEY, 0x02, KEY_BRIGHTNESSUP},
+ {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN},
+ {KE_KEY, 0x04, KEY_HELP},
+ {KE_END, 0}
+};
+
+static struct input_dev *hp_wmi_input_dev;
+static struct platform_device *hp_wmi_platform_dev;
+
+static struct rfkill *wifi_rfkill;
+static struct rfkill *bluetooth_rfkill;
+static struct rfkill *wwan_rfkill;
+
+static struct platform_driver hp_wmi_driver = {
+ .driver = {
+ .name = "hp-wmi",
+ .owner = THIS_MODULE,
+ },
+ .probe = hp_wmi_bios_setup,
+ .remove = hp_wmi_bios_remove,
+};
+
+static int hp_wmi_perform_query(int query, int write, int value)
+{
+ struct bios_return bios_return;
+ acpi_status status;
+ union acpi_object *obj;
+ struct bios_args args = {
+ .signature = 0x55434553,
+ .command = write ? 0x2 : 0x1,
+ .commandtype = query,
+ .datasize = write ? 0x4 : 0,
+ .data = value,
+ };
+ struct acpi_buffer input = { sizeof(struct bios_args), &args };
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+
+ status = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output);
+
+ obj = output.pointer;
+
+ if (!obj || obj->type != ACPI_TYPE_BUFFER)
+ return -EINVAL;
+
+ bios_return = *((struct bios_return *)obj->buffer.pointer);
+ if (bios_return.return_code > 0)
+ return bios_return.return_code * -1;
+ else
+ return bios_return.value;
+}
+
+static int hp_wmi_display_state(void)
+{
+ return hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, 0);
+}
+
+static int hp_wmi_hddtemp_state(void)
+{
+ return hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, 0);
+}
+
+static int hp_wmi_als_state(void)
+{
+ return hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, 0);
+}
+
+static int hp_wmi_dock_state(void)
+{
+ return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0);
+}
+
+static int hp_wmi_wifi_set(void *data, enum rfkill_state state)
+{
+ if (state)
+ return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x101);
+ else
+ return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x100);
+}
+
+static int hp_wmi_bluetooth_set(void *data, enum rfkill_state state)
+{
+ if (state)
+ return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x202);
+ else
+ return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x200);
+}
+
+static int hp_wmi_wwan_set(void *data, enum rfkill_state state)
+{
+ if (state)
+ return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x404);
+ else
+ return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x400);
+}
+
+static int hp_wmi_wifi_state(void)
+{
+ int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
+
+ if (wireless & 0x100)
+ return 1;
+ else
+ return 0;
+}
+
+static int hp_wmi_bluetooth_state(void)
+{
+ int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
+
+ if (wireless & 0x10000)
+ return 1;
+ else
+ return 0;
+}
+
+static int hp_wmi_wwan_state(void)
+{
+ int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
+
+ if (wireless & 0x1000000)
+ return 1;
+ else
+ return 0;
+}
+
+static ssize_t show_display(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int value = hp_wmi_display_state();
+ if (value < 0)
+ return -EINVAL;
+ return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t show_hddtemp(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int value = hp_wmi_hddtemp_state();
+ if (value < 0)
+ return -EINVAL;
+ return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t show_als(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int value = hp_wmi_als_state();
+ if (value < 0)
+ return -EINVAL;
+ return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t show_dock(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int value = hp_wmi_dock_state();
+ if (value < 0)
+ return -EINVAL;
+ return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t set_als(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u32 tmp = simple_strtoul(buf, NULL, 10);
+ hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, tmp);
+ return count;
+}
+
+static DEVICE_ATTR(display, S_IRUGO, show_display, NULL);
+static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL);
+static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als);
+static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
+
+static struct key_entry *hp_wmi_get_entry_by_scancode(int code)
+{
+ struct key_entry *key;
+
+ for (key = hp_wmi_keymap; key->type != KE_END; key++)
+ if (code == key->code)
+ return key;
+
+ return NULL;
+}
+
+static struct key_entry *hp_wmi_get_entry_by_keycode(int keycode)
+{
+ struct key_entry *key;
+
+ for (key = hp_wmi_keymap; key->type != KE_END; key++)
+ if (key->type == KE_KEY && keycode == key->keycode)
+ return key;
+
+ return NULL;
+}
+
+static int hp_wmi_getkeycode(struct input_dev *dev, int scancode, int *keycode)
+{
+ struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode);
+
+ if (key && key->type == KE_KEY) {
+ *keycode = key->keycode;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int hp_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode)
+{
+ struct key_entry *key;
+ int old_keycode;
+
+ if (keycode < 0 || keycode > KEY_MAX)
+ return -EINVAL;
+
+ key = hp_wmi_get_entry_by_scancode(scancode);
+ if (key && key->type == KE_KEY) {
+ old_keycode = key->keycode;
+ key->keycode = keycode;
+ set_bit(keycode, dev->keybit);
+ if (!hp_wmi_get_entry_by_keycode(old_keycode))
+ clear_bit(old_keycode, dev->keybit);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+void hp_wmi_notify(u32 value, void *context)
+{
+ struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
+ static struct key_entry *key;
+ union acpi_object *obj;
+
+ wmi_get_event_data(value, &response);
+
+ obj = (union acpi_object *)response.pointer;
+
+ if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == 8) {
+ int eventcode = *((u8 *) obj->buffer.pointer);
+ key = hp_wmi_get_entry_by_scancode(eventcode);
+ if (key) {
+ switch (key->type) {
+ case KE_KEY:
+ input_report_key(hp_wmi_input_dev,
+ key->keycode, 1);
+ input_sync(hp_wmi_input_dev);
+ input_report_key(hp_wmi_input_dev,
+ key->keycode, 0);
+ input_sync(hp_wmi_input_dev);
+ break;
+ case KE_SW:
+ input_report_switch(hp_wmi_input_dev,
+ key->keycode,
+ hp_wmi_dock_state());
+ input_sync(hp_wmi_input_dev);
+ break;
+ }
+ } else if (eventcode == 0x5) {
+ if (wifi_rfkill)
+ wifi_rfkill->state = hp_wmi_wifi_state();
+ if (bluetooth_rfkill)
+ bluetooth_rfkill->state =
+ hp_wmi_bluetooth_state();
+ if (wwan_rfkill)
+ wwan_rfkill->state = hp_wmi_wwan_state();
+ } else
+ printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n",
+ eventcode);
+ } else
+ printk(KERN_INFO "HP WMI: Unknown response received\n");
+}
+
+static int __init hp_wmi_input_setup(void)
+{
+ struct key_entry *key;
+ int err;
+
+ hp_wmi_input_dev = input_allocate_device();
+
+ hp_wmi_input_dev->name = "HP WMI hotkeys";
+ hp_wmi_input_dev->phys = "wmi/input0";
+ hp_wmi_input_dev->id.bustype = BUS_HOST;
+ hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode;
+ hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode;
+
+ for (key = hp_wmi_keymap; key->type != KE_END; key++) {
+ switch (key->type) {
+ case KE_KEY:
+ set_bit(EV_KEY, hp_wmi_input_dev->evbit);
+ set_bit(key->keycode, hp_wmi_input_dev->keybit);
+ break;
+ case KE_SW:
+ set_bit(EV_SW, hp_wmi_input_dev->evbit);
+ set_bit(key->keycode, hp_wmi_input_dev->swbit);
+ break;
+ }
+ }
+
+ err = input_register_device(hp_wmi_input_dev);
+
+ if (err) {
+ input_free_device(hp_wmi_input_dev);
+ return err;
+ }
+
+ return 0;
+}
+
+static void cleanup_sysfs(struct platform_device *device)
+{
+ device_remove_file(&device->dev, &dev_attr_display);
+ device_remove_file(&device->dev, &dev_attr_hddtemp);
+ device_remove_file(&device->dev, &dev_attr_als);
+ device_remove_file(&device->dev, &dev_attr_dock);
+}
+
+static int __init hp_wmi_bios_setup(struct platform_device *device)
+{
+ int err;
+
+ err = device_create_file(&device->dev, &dev_attr_display);
+ if (err)
+ goto add_sysfs_error;
+ err = device_create_file(&device->dev, &dev_attr_hddtemp);
+ if (err)
+ goto add_sysfs_error;
+ err = device_create_file(&device->dev, &dev_attr_als);
+ if (err)
+ goto add_sysfs_error;
+ err = device_create_file(&device->dev, &dev_attr_dock);
+ if (err)
+ goto add_sysfs_error;
+
+ wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN);
+ wifi_rfkill->name = "hp-wifi";
+ wifi_rfkill->state = hp_wmi_wifi_state();
+ wifi_rfkill->toggle_radio = hp_wmi_wifi_set;
+ wifi_rfkill->user_claim_unsupported = 1;
+
+ bluetooth_rfkill = rfkill_allocate(&device->dev,
+ RFKILL_TYPE_BLUETOOTH);
+ bluetooth_rfkill->name = "hp-bluetooth";
+ bluetooth_rfkill->state = hp_wmi_bluetooth_state();
+ bluetooth_rfkill->toggle_radio = hp_wmi_bluetooth_set;
+ bluetooth_rfkill->user_claim_unsupported = 1;
+
+ wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WIMAX);
+ wwan_rfkill->name = "hp-wwan";
+ wwan_rfkill->state = hp_wmi_wwan_state();
+ wwan_rfkill->toggle_radio = hp_wmi_wwan_set;
+ wwan_rfkill->user_claim_unsupported = 1;
+
+ rfkill_register(wifi_rfkill);
+ rfkill_register(bluetooth_rfkill);
+ rfkill_register(wwan_rfkill);
+
+ return 0;
+add_sysfs_error:
+ cleanup_sysfs(device);
+ return err;
+}
+
+static int __exit hp_wmi_bios_remove(struct platform_device *device)
+{
+ cleanup_sysfs(device);
+
+ rfkill_unregister(wifi_rfkill);
+ rfkill_unregister(bluetooth_rfkill);
+ rfkill_unregister(wwan_rfkill);
+
+ return 0;
+}
+
+static int __init hp_wmi_init(void)
+{
+ int err;
+
+ if (wmi_has_guid(HPWMI_EVENT_GUID)) {
+ err = wmi_install_notify_handler(HPWMI_EVENT_GUID,
+ hp_wmi_notify, NULL);
+ if (!err)
+ hp_wmi_input_setup();
+ }
+
+ if (wmi_has_guid(HPWMI_BIOS_GUID)) {
+ err = platform_driver_register(&hp_wmi_driver);
+ if (err)
+ return 0;
+ hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1);
+ if (!hp_wmi_platform_dev) {
+ platform_driver_unregister(&hp_wmi_driver);
+ return 0;
+ }
+ platform_device_add(hp_wmi_platform_dev);
+ }
+
+ return 0;
+}
+
+static void __exit hp_wmi_exit(void)
+{
+ if (wmi_has_guid(HPWMI_EVENT_GUID)) {
+ wmi_remove_notify_handler(HPWMI_EVENT_GUID);
+ input_unregister_device(hp_wmi_input_dev);
+ }
+ if (hp_wmi_platform_dev) {
+ platform_device_del(hp_wmi_platform_dev);
+ platform_driver_unregister(&hp_wmi_driver);
+ }
+}
+
+module_init(hp_wmi_init);
+module_exit(hp_wmi_exit);
diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c
index 4ce3bdc2f95..daf585689ce 100644
--- a/drivers/misc/phantom.c
+++ b/drivers/misc/phantom.c
@@ -563,6 +563,6 @@ module_init(phantom_init);
module_exit(phantom_exit);
MODULE_AUTHOR("Jiri Slaby <jirislaby@gmail.com>");
-MODULE_DESCRIPTION("Sensable Phantom driver");
+MODULE_DESCRIPTION("Sensable Phantom driver (PCI devices)");
MODULE_LICENSE("GPL");
MODULE_VERSION(PHANTOM_VERSION);
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index b5969298f3d..d3eb7903c34 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -21,7 +21,7 @@
* 02110-1301, USA.
*/
-#define TPACPI_VERSION "0.20"
+#define TPACPI_VERSION "0.21"
#define TPACPI_SYSFS_VERSION 0x020200
/*
@@ -68,6 +68,7 @@
#include <linux/hwmon-sysfs.h>
#include <linux/input.h>
#include <linux/leds.h>
+#include <linux/rfkill.h>
#include <asm/uaccess.h>
#include <linux/dmi.h>
@@ -144,6 +145,12 @@ enum {
#define TPACPI_MAX_ACPI_ARGS 3
+/* rfkill switches */
+enum {
+ TPACPI_RFK_BLUETOOTH_SW_ID = 0,
+ TPACPI_RFK_WWAN_SW_ID,
+};
+
/* Debugging */
#define TPACPI_LOG TPACPI_FILE ": "
#define TPACPI_ERR KERN_ERR TPACPI_LOG
@@ -905,6 +912,43 @@ static int __init tpacpi_check_std_acpi_brightness_support(void)
return 0;
}
+static int __init tpacpi_new_rfkill(const unsigned int id,
+ struct rfkill **rfk,
+ const enum rfkill_type rfktype,
+ const char *name,
+ int (*toggle_radio)(void *, enum rfkill_state),
+ int (*get_state)(void *, enum rfkill_state *))
+{
+ int res;
+ enum rfkill_state initial_state;
+
+ *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype);
+ if (!*rfk) {
+ printk(TPACPI_ERR
+ "failed to allocate memory for rfkill class\n");
+ return -ENOMEM;
+ }
+
+ (*rfk)->name = name;
+ (*rfk)->get_state = get_state;
+ (*rfk)->toggle_radio = toggle_radio;
+
+ if (!get_state(NULL, &initial_state))
+ (*rfk)->state = initial_state;
+
+ res = rfkill_register(*rfk);
+ if (res < 0) {
+ printk(TPACPI_ERR
+ "failed to register %s rfkill switch: %d\n",
+ name, res);
+ rfkill_free(*rfk);
+ *rfk = NULL;
+ return res;
+ }
+
+ return 0;
+}
+
/*************************************************************************
* thinkpad-acpi driver attributes
*/
@@ -1285,21 +1329,6 @@ static int hotkey_status_set(int status)
return 0;
}
-static void tpacpi_input_send_radiosw(void)
-{
- int wlsw;
-
- if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) {
- mutex_lock(&tpacpi_inputdev_send_mutex);
-
- input_report_switch(tpacpi_inputdev,
- SW_RFKILL_ALL, !!wlsw);
- input_sync(tpacpi_inputdev);
-
- mutex_unlock(&tpacpi_inputdev_send_mutex);
- }
-}
-
static void tpacpi_input_send_tabletsw(void)
{
int state;
@@ -1921,6 +1950,30 @@ static struct attribute *hotkey_mask_attributes[] __initdata = {
&dev_attr_hotkey_wakeup_hotunplug_complete.attr,
};
+static void bluetooth_update_rfk(void);
+static void wan_update_rfk(void);
+static void tpacpi_send_radiosw_update(void)
+{
+ int wlsw;
+
+ /* Sync these BEFORE sending any rfkill events */
+ if (tp_features.bluetooth)
+ bluetooth_update_rfk();
+ if (tp_features.wan)
+ wan_update_rfk();
+
+ if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) {
+ mutex_lock(&tpacpi_inputdev_send_mutex);
+
+ input_report_switch(tpacpi_inputdev,
+ SW_RFKILL_ALL, !!wlsw);
+ input_sync(tpacpi_inputdev);
+
+ mutex_unlock(&tpacpi_inputdev_send_mutex);
+ }
+ hotkey_radio_sw_notify_change();
+}
+
static void hotkey_exit(void)
{
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
@@ -2167,9 +2220,10 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
printk(TPACPI_INFO
"radio switch found; radios are %s\n",
enabled(status, 0));
+ }
+ if (tp_features.hotkey_wlsw)
res = add_to_attr_set(hotkey_dev_attributes,
&dev_attr_hotkey_radio_sw.attr);
- }
/* For X41t, X60t, X61t Tablets... */
if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) {
@@ -2287,7 +2341,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
tpacpi_inputdev->close = &hotkey_inputdev_close;
hotkey_poll_setup_safe(1);
- tpacpi_input_send_radiosw();
+ tpacpi_send_radiosw_update();
tpacpi_input_send_tabletsw();
return 0;
@@ -2419,8 +2473,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
case 7:
/* 0x7000-0x7FFF: misc */
if (tp_features.hotkey_wlsw && hkey == 0x7000) {
- tpacpi_input_send_radiosw();
- hotkey_radio_sw_notify_change();
+ tpacpi_send_radiosw_update();
send_acpi_ev = 0;
break;
}
@@ -2463,8 +2516,7 @@ static void hotkey_resume(void)
printk(TPACPI_ERR
"error while trying to read hot key mask "
"from firmware\n");
- tpacpi_input_send_radiosw();
- hotkey_radio_sw_notify_change();
+ tpacpi_send_radiosw_update();
hotkey_tablet_mode_notify_change();
hotkey_wakeup_reason_notify_change();
hotkey_wakeup_hotunplug_complete_notify_change();
@@ -2581,8 +2633,66 @@ enum {
TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */
};
-static int bluetooth_get_radiosw(void);
-static int bluetooth_set_radiosw(int radio_on);
+static struct rfkill *tpacpi_bluetooth_rfkill;
+
+static int bluetooth_get_radiosw(void)
+{
+ int status;
+
+ if (!tp_features.bluetooth)
+ return -ENODEV;
+
+ /* WLSW overrides bluetooth in firmware/hardware, reflect that */
+ if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
+ return RFKILL_STATE_HARD_BLOCKED;
+
+ if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
+ return -EIO;
+
+ return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ?
+ RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
+}
+
+static void bluetooth_update_rfk(void)
+{
+ int status;
+
+ if (!tpacpi_bluetooth_rfkill)
+ return;
+
+ status = bluetooth_get_radiosw();
+ if (status < 0)
+ return;
+ rfkill_force_state(tpacpi_bluetooth_rfkill, status);
+}
+
+static int bluetooth_set_radiosw(int radio_on, int update_rfk)
+{
+ int status;
+
+ if (!tp_features.bluetooth)
+ return -ENODEV;
+
+ /* WLSW overrides bluetooth in firmware/hardware, but there is no
+ * reason to risk weird behaviour. */
+ if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status
+ && radio_on)
+ return -EPERM;
+
+ if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
+ return -EIO;
+ if (radio_on)
+ status |= TP_ACPI_BLUETOOTH_RADIOSSW;
+ else
+ status &= ~TP_ACPI_BLUETOOTH_RADIOSSW;
+ if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
+ return -EIO;
+
+ if (update_rfk)
+ bluetooth_update_rfk();
+
+ return 0;
+}
/* sysfs bluetooth enable ---------------------------------------------- */
static ssize_t bluetooth_enable_show(struct device *dev,
@@ -2595,7 +2705,8 @@ static ssize_t bluetooth_enable_show(struct device *dev,
if (status < 0)
return status;
- return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0);
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
}
static ssize_t bluetooth_enable_store(struct device *dev,
@@ -2608,7 +2719,7 @@ static ssize_t bluetooth_enable_store(struct device *dev,
if (parse_strtoul(buf, 1, &t))
return -EINVAL;
- res = bluetooth_set_radiosw(t);
+ res = bluetooth_set_radiosw(t, 1);
return (res) ? res : count;
}
@@ -2628,6 +2739,31 @@ static const struct attribute_group bluetooth_attr_group = {
.attrs = bluetooth_attributes,
};
+static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state)
+{
+ int bts = bluetooth_get_radiosw();
+
+ if (bts < 0)
+ return bts;
+
+ *state = bts;
+ return 0;
+}
+
+static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state)
+{
+ return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
+}
+
+static void bluetooth_exit(void)
+{
+ if (tpacpi_bluetooth_rfkill)
+ rfkill_unregister(tpacpi_bluetooth_rfkill);
+
+ sysfs_remove_group(&tpacpi_pdev->dev.kobj,
+ &bluetooth_attr_group);
+}
+
static int __init bluetooth_init(struct ibm_init_struct *iibm)
{
int res;
@@ -2646,57 +2782,32 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm)
str_supported(tp_features.bluetooth),
status);
- if (tp_features.bluetooth) {
- if (!(status & TP_ACPI_BLUETOOTH_HWPRESENT)) {
- /* no bluetooth hardware present in system */
- tp_features.bluetooth = 0;
- dbg_printk(TPACPI_DBG_INIT,
- "bluetooth hardware not installed\n");
- } else {
- res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
- &bluetooth_attr_group);
- if (res)
- return res;
- }
+ if (tp_features.bluetooth &&
+ !(status & TP_ACPI_BLUETOOTH_HWPRESENT)) {
+ /* no bluetooth hardware present in system */
+ tp_features.bluetooth = 0;
+ dbg_printk(TPACPI_DBG_INIT,
+ "bluetooth hardware not installed\n");
}
- return (tp_features.bluetooth)? 0 : 1;
-}
-
-static void bluetooth_exit(void)
-{
- sysfs_remove_group(&tpacpi_pdev->dev.kobj,
- &bluetooth_attr_group);
-}
-
-static int bluetooth_get_radiosw(void)
-{
- int status;
-
if (!tp_features.bluetooth)
- return -ENODEV;
-
- if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
- return -EIO;
-
- return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0);
-}
-
-static int bluetooth_set_radiosw(int radio_on)
-{
- int status;
+ return 1;
- if (!tp_features.bluetooth)
- return -ENODEV;
+ res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+ &bluetooth_attr_group);
+ if (res)
+ return res;
- if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
- return -EIO;
- if (radio_on)
- status |= TP_ACPI_BLUETOOTH_RADIOSSW;
- else
- status &= ~TP_ACPI_BLUETOOTH_RADIOSSW;
- if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
- return -EIO;
+ res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID,
+ &tpacpi_bluetooth_rfkill,
+ RFKILL_TYPE_BLUETOOTH,
+ "tpacpi_bluetooth_sw",
+ tpacpi_bluetooth_rfk_set,
+ tpacpi_bluetooth_rfk_get);
+ if (res) {
+ bluetooth_exit();
+ return res;
+ }
return 0;
}
@@ -2711,7 +2822,8 @@ static int bluetooth_read(char *p)
len += sprintf(p + len, "status:\t\tnot supported\n");
else {
len += sprintf(p + len, "status:\t\t%s\n",
- (status)? "enabled" : "disabled");
+ (status == RFKILL_STATE_UNBLOCKED) ?
+ "enabled" : "disabled");
len += sprintf(p + len, "commands:\tenable, disable\n");
}
@@ -2727,9 +2839,9 @@ static int bluetooth_write(char *buf)
while ((cmd = next_cmd(&buf))) {
if (strlencmp(cmd, "enable") == 0) {
- bluetooth_set_radiosw(1);
+ bluetooth_set_radiosw(1, 1);
} else if (strlencmp(cmd, "disable") == 0) {
- bluetooth_set_radiosw(0);
+ bluetooth_set_radiosw(0, 1);
} else
return -EINVAL;
}
@@ -2755,8 +2867,66 @@ enum {
TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */
};
-static int wan_get_radiosw(void);
-static int wan_set_radiosw(int radio_on);
+static struct rfkill *tpacpi_wan_rfkill;
+
+static int wan_get_radiosw(void)
+{
+ int status;
+
+ if (!tp_features.wan)
+ return -ENODEV;
+
+ /* WLSW overrides WWAN in firmware/hardware, reflect that */
+ if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
+ return RFKILL_STATE_HARD_BLOCKED;
+
+ if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
+ return -EIO;
+
+ return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ?
+ RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
+}
+
+static void wan_update_rfk(void)
+{
+ int status;
+
+ if (!tpacpi_wan_rfkill)
+ return;
+
+ status = wan_get_radiosw();
+ if (status < 0)
+ return;
+ rfkill_force_state(tpacpi_wan_rfkill, status);
+}
+
+static int wan_set_radiosw(int radio_on, int update_rfk)
+{
+ int status;
+
+ if (!tp_features.wan)
+ return -ENODEV;
+
+ /* WLSW overrides bluetooth in firmware/hardware, but there is no
+ * reason to risk weird behaviour. */
+ if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status
+ && radio_on)
+ return -EPERM;
+
+ if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
+ return -EIO;
+ if (radio_on)
+ status |= TP_ACPI_WANCARD_RADIOSSW;
+ else
+ status &= ~TP_ACPI_WANCARD_RADIOSSW;
+ if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
+ return -EIO;
+
+ if (update_rfk)
+ wan_update_rfk();
+
+ return 0;
+}
/* sysfs wan enable ---------------------------------------------------- */
static ssize_t wan_enable_show(struct device *dev,
@@ -2769,7 +2939,8 @@ static ssize_t wan_enable_show(struct device *dev,
if (status < 0)
return status;
- return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0);
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
}
static ssize_t wan_enable_store(struct device *dev,
@@ -2782,7 +2953,7 @@ static ssize_t wan_enable_store(struct device *dev,
if (parse_strtoul(buf, 1, &t))
return -EINVAL;
- res = wan_set_radiosw(t);
+ res = wan_set_radiosw(t, 1);
return (res) ? res : count;
}
@@ -2802,6 +2973,31 @@ static const struct attribute_group wan_attr_group = {
.attrs = wan_attributes,
};
+static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state)
+{
+ int wans = wan_get_radiosw();
+
+ if (wans < 0)
+ return wans;
+
+ *state = wans;
+ return 0;
+}
+
+static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state)
+{
+ return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
+}
+
+static void wan_exit(void)
+{
+ if (tpacpi_wan_rfkill)
+ rfkill_unregister(tpacpi_wan_rfkill);
+
+ sysfs_remove_group(&tpacpi_pdev->dev.kobj,
+ &wan_attr_group);
+}
+
static int __init wan_init(struct ibm_init_struct *iibm)
{
int res;
@@ -2818,57 +3014,32 @@ static int __init wan_init(struct ibm_init_struct *iibm)
str_supported(tp_features.wan),
status);
- if (tp_features.wan) {
- if (!(status & TP_ACPI_WANCARD_HWPRESENT)) {
- /* no wan hardware present in system */
- tp_features.wan = 0;
- dbg_printk(TPACPI_DBG_INIT,
- "wan hardware not installed\n");
- } else {
- res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
- &wan_attr_group);
- if (res)
- return res;
- }
+ if (tp_features.wan &&
+ !(status & TP_ACPI_WANCARD_HWPRESENT)) {
+ /* no wan hardware present in system */
+ tp_features.wan = 0;
+ dbg_printk(TPACPI_DBG_INIT,
+ "wan hardware not installed\n");
}
- return (tp_features.wan)? 0 : 1;
-}
-
-static void wan_exit(void)
-{
- sysfs_remove_group(&tpacpi_pdev->dev.kobj,
- &wan_attr_group);
-}
-
-static int wan_get_radiosw(void)
-{
- int status;
-
if (!tp_features.wan)
- return -ENODEV;
-
- if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
- return -EIO;
-
- return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0);
-}
-
-static int wan_set_radiosw(int radio_on)
-{
- int status;
+ return 1;
- if (!tp_features.wan)
- return -ENODEV;
+ res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+ &wan_attr_group);
+ if (res)
+ return res;
- if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
- return -EIO;
- if (radio_on)
- status |= TP_ACPI_WANCARD_RADIOSSW;
- else
- status &= ~TP_ACPI_WANCARD_RADIOSSW;
- if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
- return -EIO;
+ res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID,
+ &tpacpi_wan_rfkill,
+ RFKILL_TYPE_WWAN,
+ "tpacpi_wwan_sw",
+ tpacpi_wan_rfk_set,
+ tpacpi_wan_rfk_get);
+ if (res) {
+ wan_exit();
+ return res;
+ }
return 0;
}
@@ -2883,7 +3054,8 @@ static int wan_read(char *p)
len += sprintf(p + len, "status:\t\tnot supported\n");
else {
len += sprintf(p + len, "status:\t\t%s\n",
- (status)? "enabled" : "disabled");
+ (status == RFKILL_STATE_UNBLOCKED) ?
+ "enabled" : "disabled");
len += sprintf(p + len, "commands:\tenable, disable\n");
}
@@ -2899,9 +3071,9 @@ static int wan_write(char *buf)
while ((cmd = next_cmd(&buf))) {
if (strlencmp(cmd, "enable") == 0) {
- wan_set_radiosw(1);
+ wan_set_radiosw(1, 1);
} else if (strlencmp(cmd, "disable") == 0) {
- wan_set_radiosw(0);
+ wan_set_radiosw(0, 1);
} else
return -EINVAL;
}
@@ -6168,13 +6340,18 @@ err_out:
/* Probing */
-static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp)
+/* returns 0 - probe ok, or < 0 - probe error.
+ * Probe ok doesn't mean thinkpad found.
+ * On error, kfree() cleanup on tp->* is not performed, caller must do it */
+static int __must_check __init get_thinkpad_model_data(
+ struct thinkpad_id_data *tp)
{
const struct dmi_device *dev = NULL;
char ec_fw_string[18];
+ char const *s;
if (!tp)
- return;
+ return -EINVAL;
memset(tp, 0, sizeof(*tp));
@@ -6183,12 +6360,14 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp)
else if (dmi_name_in_vendors("LENOVO"))
tp->vendor = PCI_VENDOR_ID_LENOVO;
else
- return;
+ return 0;
- tp->bios_version_str = kstrdup(dmi_get_system_info(DMI_BIOS_VERSION),
- GFP_KERNEL);
+ s = dmi_get_system_info(DMI_BIOS_VERSION);
+ tp->bios_version_str = kstrdup(s, GFP_KERNEL);
+ if (s && !tp->bios_version_str)
+ return -ENOMEM;
if (!tp->bios_version_str)
- return;
+ return 0;
tp->bios_model = tp->bios_version_str[0]
| (tp->bios_version_str[1] << 8);
@@ -6207,21 +6386,27 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp)
ec_fw_string[strcspn(ec_fw_string, " ]")] = 0;
tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL);
+ if (!tp->ec_version_str)
+ return -ENOMEM;
tp->ec_model = ec_fw_string[0]
| (ec_fw_string[1] << 8);
break;
}
}
- tp->model_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_VERSION),
- GFP_KERNEL);
- if (tp->model_str && strnicmp(tp->model_str, "ThinkPad", 8) != 0) {
- kfree(tp->model_str);
- tp->model_str = NULL;
+ s = dmi_get_system_info(DMI_PRODUCT_VERSION);
+ if (s && !strnicmp(s, "ThinkPad", 8)) {
+ tp->model_str = kstrdup(s, GFP_KERNEL);
+ if (!tp->model_str)
+ return -ENOMEM;
}
- tp->nummodel_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_NAME),
- GFP_KERNEL);
+ s = dmi_get_system_info(DMI_PRODUCT_NAME);
+ tp->nummodel_str = kstrdup(s, GFP_KERNEL);
+ if (s && !tp->nummodel_str)
+ return -ENOMEM;
+
+ return 0;
}
static int __init probe_for_thinkpad(void)
@@ -6484,7 +6669,13 @@ static int __init thinkpad_acpi_module_init(void)
/* Driver-level probe */
- get_thinkpad_model_data(&thinkpad_id);
+ ret = get_thinkpad_model_data(&thinkpad_id);
+ if (ret) {
+ printk(TPACPI_ERR
+ "unable to get DMI data: %d\n", ret);
+ thinkpad_acpi_module_exit();
+ return ret;
+ }
ret = probe_for_thinkpad();
if (ret) {
thinkpad_acpi_module_exit();
diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c
index c42f4b83f68..3fcf92130aa 100644
--- a/drivers/mtd/maps/uclinux.c
+++ b/drivers/mtd/maps/uclinux.c
@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
+#include <linux/mm.h>
#include <linux/major.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 961416ac061..c7630a22831 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -51,14 +51,13 @@
* @name: MTD device name or number string
* @vid_hdr_offs: VID header offset
*/
-struct mtd_dev_param
-{
+struct mtd_dev_param {
char name[MTD_PARAM_LEN_MAX];
int vid_hdr_offs;
};
/* Numbers of elements set in the @mtd_dev_param array */
-static int mtd_devs = 0;
+static int mtd_devs;
/* MTD devices specification parameters */
static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES];
@@ -160,8 +159,7 @@ void ubi_put_device(struct ubi_device *ubi)
}
/**
- * ubi_get_by_major - get UBI device description object by character device
- * major number.
+ * ubi_get_by_major - get UBI device by character device major number.
* @major: major number
*
* This function is similar to 'ubi_get_device()', but it searches the device
@@ -355,15 +353,34 @@ static void kill_volumes(struct ubi_device *ubi)
}
/**
+ * free_user_volumes - free all user volumes.
+ * @ubi: UBI device description object
+ *
+ * Normally the volumes are freed at the release function of the volume device
+ * objects. However, on error paths the volumes have to be freed before the
+ * device objects have been initialized.
+ */
+static void free_user_volumes(struct ubi_device *ubi)
+{
+ int i;
+
+ for (i = 0; i < ubi->vtbl_slots; i++)
+ if (ubi->volumes[i]) {
+ kfree(ubi->volumes[i]->eba_tbl);
+ kfree(ubi->volumes[i]);
+ }
+}
+
+/**
* uif_init - initialize user interfaces for an UBI device.
* @ubi: UBI device description object
*
* This function returns zero in case of success and a negative error code in
- * case of failure.
+ * case of failure. Note, this function destroys all volumes if it failes.
*/
static int uif_init(struct ubi_device *ubi)
{
- int i, err;
+ int i, err, do_free = 0;
dev_t dev;
sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num);
@@ -384,7 +401,7 @@ static int uif_init(struct ubi_device *ubi)
ubi_assert(MINOR(dev) == 0);
cdev_init(&ubi->cdev, &ubi_cdev_operations);
- dbg_msg("%s major is %u", ubi->ubi_name, MAJOR(dev));
+ dbg_gen("%s major is %u", ubi->ubi_name, MAJOR(dev));
ubi->cdev.owner = THIS_MODULE;
err = cdev_add(&ubi->cdev, dev, 1);
@@ -410,10 +427,13 @@ static int uif_init(struct ubi_device *ubi)
out_volumes:
kill_volumes(ubi);
+ do_free = 0;
out_sysfs:
ubi_sysfs_close(ubi);
cdev_del(&ubi->cdev);
out_unreg:
+ if (do_free)
+ free_user_volumes(ubi);
unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1);
ubi_err("cannot initialize UBI %s, error %d", ubi->ubi_name, err);
return err;
@@ -422,6 +442,10 @@ out_unreg:
/**
* uif_close - close user interfaces for an UBI device.
* @ubi: UBI device description object
+ *
+ * Note, since this function un-registers UBI volume device objects (@vol->dev),
+ * the memory allocated voe the volumes is freed as well (in the release
+ * function).
*/
static void uif_close(struct ubi_device *ubi)
{
@@ -432,6 +456,21 @@ static void uif_close(struct ubi_device *ubi)
}
/**
+ * free_internal_volumes - free internal volumes.
+ * @ubi: UBI device description object
+ */
+static void free_internal_volumes(struct ubi_device *ubi)
+{
+ int i;
+
+ for (i = ubi->vtbl_slots;
+ i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) {
+ kfree(ubi->volumes[i]->eba_tbl);
+ kfree(ubi->volumes[i]);
+ }
+}
+
+/**
* attach_by_scanning - attach an MTD device using scanning method.
* @ubi: UBI device descriptor
*
@@ -475,6 +514,7 @@ static int attach_by_scanning(struct ubi_device *ubi)
out_wl:
ubi_wl_close(ubi);
out_vtbl:
+ free_internal_volumes(ubi);
vfree(ubi->vtbl);
out_si:
ubi_scan_destroy_si(si);
@@ -482,7 +522,7 @@ out_si:
}
/**
- * io_init - initialize I/O unit for a given UBI device.
+ * io_init - initialize I/O sub-system for a given UBI device.
* @ubi: UBI device description object
*
* If @ubi->vid_hdr_offset or @ubi->leb_start is zero, default offsets are
@@ -530,7 +570,11 @@ static int io_init(struct ubi_device *ubi)
ubi->min_io_size = ubi->mtd->writesize;
ubi->hdrs_min_io_size = ubi->mtd->writesize >> ubi->mtd->subpage_sft;
- /* Make sure minimal I/O unit is power of 2 */
+ /*
+ * Make sure minimal I/O unit is power of 2. Note, there is no
+ * fundamental reason for this assumption. It is just an optimization
+ * which allows us to avoid costly division operations.
+ */
if (!is_power_of_2(ubi->min_io_size)) {
ubi_err("min. I/O unit (%d) is not power of 2",
ubi->min_io_size);
@@ -581,7 +625,7 @@ static int io_init(struct ubi_device *ubi)
if (ubi->vid_hdr_offset < UBI_EC_HDR_SIZE ||
ubi->leb_start < ubi->vid_hdr_offset + UBI_VID_HDR_SIZE ||
ubi->leb_start > ubi->peb_size - UBI_VID_HDR_SIZE ||
- ubi->leb_start % ubi->min_io_size) {
+ ubi->leb_start & (ubi->min_io_size - 1)) {
ubi_err("bad VID header (%d) or data offsets (%d)",
ubi->vid_hdr_offset, ubi->leb_start);
return -EINVAL;
@@ -646,7 +690,7 @@ static int autoresize(struct ubi_device *ubi, int vol_id)
/*
* Clear the auto-resize flag in the volume in-memory copy of the
- * volume table, and 'ubi_resize_volume()' will propogate this change
+ * volume table, and 'ubi_resize_volume()' will propagate this change
* to the flash.
*/
ubi->vtbl[vol_id].flags &= ~UBI_VTBL_AUTORESIZE_FLG;
@@ -655,7 +699,7 @@ static int autoresize(struct ubi_device *ubi, int vol_id)
struct ubi_vtbl_record vtbl_rec;
/*
- * No avalilable PEBs to re-size the volume, clear the flag on
+ * No available PEBs to re-size the volume, clear the flag on
* flash and exit.
*/
memcpy(&vtbl_rec, &ubi->vtbl[vol_id],
@@ -682,13 +726,13 @@ static int autoresize(struct ubi_device *ubi, int vol_id)
/**
* ubi_attach_mtd_dev - attach an MTD device.
- * @mtd_dev: MTD device description object
+ * @mtd: MTD device description object
* @ubi_num: number to assign to the new UBI device
* @vid_hdr_offset: VID header offset
*
* This function attaches MTD device @mtd_dev to UBI and assign @ubi_num number
* to the newly created UBI device, unless @ubi_num is %UBI_DEV_NUM_AUTO, in
- * which case this function finds a vacant device nubert and assings it
+ * which case this function finds a vacant device number and assigns it
* automatically. Returns the new UBI device number in case of success and a
* negative error code in case of failure.
*
@@ -698,7 +742,7 @@ static int autoresize(struct ubi_device *ubi, int vol_id)
int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
{
struct ubi_device *ubi;
- int i, err;
+ int i, err, do_free = 1;
/*
* Check if we already have the same MTD device attached.
@@ -735,7 +779,8 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
if (!ubi_devices[ubi_num])
break;
if (ubi_num == UBI_MAX_DEVICES) {
- dbg_err("only %d UBI devices may be created", UBI_MAX_DEVICES);
+ dbg_err("only %d UBI devices may be created",
+ UBI_MAX_DEVICES);
return -ENFILE;
}
} else {
@@ -760,6 +805,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
mutex_init(&ubi->buf_mutex);
mutex_init(&ubi->ckvol_mutex);
+ mutex_init(&ubi->mult_mutex);
mutex_init(&ubi->volumes_mutex);
spin_lock_init(&ubi->volumes_lock);
@@ -798,7 +844,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
err = uif_init(ubi);
if (err)
- goto out_detach;
+ goto out_nofree;
ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name);
if (IS_ERR(ubi->bgt_thread)) {
@@ -824,20 +870,22 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
ubi->beb_rsvd_pebs);
ubi_msg("max/mean erase counter: %d/%d", ubi->max_ec, ubi->mean_ec);
- /* Enable the background thread */
- if (!DBG_DISABLE_BGT) {
+ if (!DBG_DISABLE_BGT)
ubi->thread_enabled = 1;
- wake_up_process(ubi->bgt_thread);
- }
+ wake_up_process(ubi->bgt_thread);
ubi_devices[ubi_num] = ubi;
return ubi_num;
out_uif:
uif_close(ubi);
+out_nofree:
+ do_free = 0;
out_detach:
- ubi_eba_close(ubi);
ubi_wl_close(ubi);
+ if (do_free)
+ free_user_volumes(ubi);
+ free_internal_volumes(ubi);
vfree(ubi->vtbl);
out_free:
vfree(ubi->peb_buf1);
@@ -899,8 +947,8 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
kthread_stop(ubi->bgt_thread);
uif_close(ubi);
- ubi_eba_close(ubi);
ubi_wl_close(ubi);
+ free_internal_volumes(ubi);
vfree(ubi->vtbl);
put_mtd_device(ubi->mtd);
vfree(ubi->peb_buf1);
@@ -1044,8 +1092,7 @@ static void __exit ubi_exit(void)
module_exit(ubi_exit);
/**
- * bytes_str_to_int - convert a string representing number of bytes to an
- * integer.
+ * bytes_str_to_int - convert a number of bytes string into an integer.
* @str: the string to convert
*
* This function returns positive resulting integer in case of success and a
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c
index 89193ba9451..03c759b4eeb 100644
--- a/drivers/mtd/ubi/cdev.c
+++ b/drivers/mtd/ubi/cdev.c
@@ -39,9 +39,9 @@
#include <linux/stat.h>
#include <linux/ioctl.h>
#include <linux/capability.h>
+#include <linux/uaccess.h>
#include <linux/smp_lock.h>
#include <mtd/ubi-user.h>
-#include <asm/uaccess.h>
#include <asm/div64.h>
#include "ubi.h"
@@ -116,7 +116,7 @@ static int vol_cdev_open(struct inode *inode, struct file *file)
else
mode = UBI_READONLY;
- dbg_msg("open volume %d, mode %d", vol_id, mode);
+ dbg_gen("open volume %d, mode %d", vol_id, mode);
desc = ubi_open_volume(ubi_num, vol_id, mode);
unlock_kernel();
@@ -132,7 +132,7 @@ static int vol_cdev_release(struct inode *inode, struct file *file)
struct ubi_volume_desc *desc = file->private_data;
struct ubi_volume *vol = desc->vol;
- dbg_msg("release volume %d, mode %d", vol->vol_id, desc->mode);
+ dbg_gen("release volume %d, mode %d", vol->vol_id, desc->mode);
if (vol->updating) {
ubi_warn("update of volume %d not finished, volume is damaged",
@@ -141,7 +141,7 @@ static int vol_cdev_release(struct inode *inode, struct file *file)
vol->updating = 0;
vfree(vol->upd_buf);
} else if (vol->changing_leb) {
- dbg_msg("only %lld of %lld bytes received for atomic LEB change"
+ dbg_gen("only %lld of %lld bytes received for atomic LEB change"
" for volume %d:%d, cancel", vol->upd_received,
vol->upd_bytes, vol->ubi->ubi_num, vol->vol_id);
vol->changing_leb = 0;
@@ -183,7 +183,7 @@ static loff_t vol_cdev_llseek(struct file *file, loff_t offset, int origin)
return -EINVAL;
}
- dbg_msg("seek volume %d, offset %lld, origin %d, new offset %lld",
+ dbg_gen("seek volume %d, offset %lld, origin %d, new offset %lld",
vol->vol_id, offset, origin, new_offset);
file->f_pos = new_offset;
@@ -201,7 +201,7 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count,
void *tbuf;
uint64_t tmp;
- dbg_msg("read %zd bytes from offset %lld of volume %d",
+ dbg_gen("read %zd bytes from offset %lld of volume %d",
count, *offp, vol->vol_id);
if (vol->updating) {
@@ -216,7 +216,7 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count,
return 0;
if (vol->corrupted)
- dbg_msg("read from corrupted volume %d", vol->vol_id);
+ dbg_gen("read from corrupted volume %d", vol->vol_id);
if (*offp + count > vol->used_bytes)
count_save = count = vol->used_bytes - *offp;
@@ -285,7 +285,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf,
char *tbuf;
uint64_t tmp;
- dbg_msg("requested: write %zd bytes to offset %lld of volume %u",
+ dbg_gen("requested: write %zd bytes to offset %lld of volume %u",
count, *offp, vol->vol_id);
if (vol->vol_type == UBI_STATIC_VOLUME)
@@ -295,7 +295,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf,
off = do_div(tmp, vol->usable_leb_size);
lnum = tmp;
- if (off % ubi->min_io_size) {
+ if (off & (ubi->min_io_size - 1)) {
dbg_err("unaligned position");
return -EINVAL;
}
@@ -304,7 +304,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf,
count_save = count = vol->used_bytes - *offp;
/* We can write only in fractions of the minimum I/O unit */
- if (count % ubi->min_io_size) {
+ if (count & (ubi->min_io_size - 1)) {
dbg_err("unaligned write length");
return -EINVAL;
}
@@ -352,7 +352,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf,
}
#else
-#define vol_cdev_direct_write(file, buf, count, offp) -EPERM
+#define vol_cdev_direct_write(file, buf, count, offp) (-EPERM)
#endif /* CONFIG_MTD_UBI_DEBUG_USERSPACE_IO */
static ssize_t vol_cdev_write(struct file *file, const char __user *buf,
@@ -437,7 +437,8 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file,
break;
}
- rsvd_bytes = vol->reserved_pebs * (ubi->leb_size-vol->data_pad);
+ rsvd_bytes = (long long)vol->reserved_pebs *
+ ubi->leb_size-vol->data_pad;
if (bytes < 0 || bytes > rsvd_bytes) {
err = -EINVAL;
break;
@@ -513,7 +514,7 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file,
break;
}
- dbg_msg("erase LEB %d:%d", vol->vol_id, lnum);
+ dbg_gen("erase LEB %d:%d", vol->vol_id, lnum);
err = ubi_eba_unmap_leb(ubi, vol, lnum);
if (err)
break;
@@ -564,7 +565,7 @@ static int verify_mkvol_req(const struct ubi_device *ubi,
if (req->alignment > ubi->leb_size)
goto bad;
- n = req->alignment % ubi->min_io_size;
+ n = req->alignment & (ubi->min_io_size - 1);
if (req->alignment != 1 && n)
goto bad;
@@ -573,6 +574,10 @@ static int verify_mkvol_req(const struct ubi_device *ubi,
goto bad;
}
+ n = strnlen(req->name, req->name_len + 1);
+ if (n != req->name_len)
+ goto bad;
+
return 0;
bad:
@@ -600,6 +605,166 @@ static int verify_rsvol_req(const struct ubi_device *ubi,
return 0;
}
+/**
+ * rename_volumes - rename UBI volumes.
+ * @ubi: UBI device description object
+ * @req: volumes re-name request
+ *
+ * This is a helper function for the volume re-name IOCTL which validates the
+ * the request, opens the volume and calls corresponding volumes management
+ * function. Returns zero in case of success and a negative error code in case
+ * of failure.
+ */
+static int rename_volumes(struct ubi_device *ubi,
+ struct ubi_rnvol_req *req)
+{
+ int i, n, err;
+ struct list_head rename_list;
+ struct ubi_rename_entry *re, *re1;
+
+ if (req->count < 0 || req->count > UBI_MAX_RNVOL)
+ return -EINVAL;
+
+ if (req->count == 0)
+ return 0;
+
+ /* Validate volume IDs and names in the request */
+ for (i = 0; i < req->count; i++) {
+ if (req->ents[i].vol_id < 0 ||
+ req->ents[i].vol_id >= ubi->vtbl_slots)
+ return -EINVAL;
+ if (req->ents[i].name_len < 0)
+ return -EINVAL;
+ if (req->ents[i].name_len > UBI_VOL_NAME_MAX)
+ return -ENAMETOOLONG;
+ req->ents[i].name[req->ents[i].name_len] = '\0';
+ n = strlen(req->ents[i].name);
+ if (n != req->ents[i].name_len)
+ err = -EINVAL;
+ }
+
+ /* Make sure volume IDs and names are unique */
+ for (i = 0; i < req->count - 1; i++) {
+ for (n = i + 1; n < req->count; n++) {
+ if (req->ents[i].vol_id == req->ents[n].vol_id) {
+ dbg_err("duplicated volume id %d",
+ req->ents[i].vol_id);
+ return -EINVAL;
+ }
+ if (!strcmp(req->ents[i].name, req->ents[n].name)) {
+ dbg_err("duplicated volume name \"%s\"",
+ req->ents[i].name);
+ return -EINVAL;
+ }
+ }
+ }
+
+ /* Create the re-name list */
+ INIT_LIST_HEAD(&rename_list);
+ for (i = 0; i < req->count; i++) {
+ int vol_id = req->ents[i].vol_id;
+ int name_len = req->ents[i].name_len;
+ const char *name = req->ents[i].name;
+
+ re = kzalloc(sizeof(struct ubi_rename_entry), GFP_KERNEL);
+ if (!re) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ re->desc = ubi_open_volume(ubi->ubi_num, vol_id, UBI_EXCLUSIVE);
+ if (IS_ERR(re->desc)) {
+ err = PTR_ERR(re->desc);
+ dbg_err("cannot open volume %d, error %d", vol_id, err);
+ kfree(re);
+ goto out_free;
+ }
+
+ /* Skip this re-naming if the name does not really change */
+ if (re->desc->vol->name_len == name_len &&
+ !memcmp(re->desc->vol->name, name, name_len)) {
+ ubi_close_volume(re->desc);
+ kfree(re);
+ continue;
+ }
+
+ re->new_name_len = name_len;
+ memcpy(re->new_name, name, name_len);
+ list_add_tail(&re->list, &rename_list);
+ dbg_msg("will rename volume %d from \"%s\" to \"%s\"",
+ vol_id, re->desc->vol->name, name);
+ }
+
+ if (list_empty(&rename_list))
+ return 0;
+
+ /* Find out the volumes which have to be removed */
+ list_for_each_entry(re, &rename_list, list) {
+ struct ubi_volume_desc *desc;
+ int no_remove_needed = 0;
+
+ /*
+ * Volume @re->vol_id is going to be re-named to
+ * @re->new_name, while its current name is @name. If a volume
+ * with name @re->new_name currently exists, it has to be
+ * removed, unless it is also re-named in the request (@req).
+ */
+ list_for_each_entry(re1, &rename_list, list) {
+ if (re->new_name_len == re1->desc->vol->name_len &&
+ !memcmp(re->new_name, re1->desc->vol->name,
+ re1->desc->vol->name_len)) {
+ no_remove_needed = 1;
+ break;
+ }
+ }
+
+ if (no_remove_needed)
+ continue;
+
+ /*
+ * It seems we need to remove volume with name @re->new_name,
+ * if it exists.
+ */
+ desc = ubi_open_volume_nm(ubi->ubi_num, re->new_name, UBI_EXCLUSIVE);
+ if (IS_ERR(desc)) {
+ err = PTR_ERR(desc);
+ if (err == -ENODEV)
+ /* Re-naming into a non-existing volume name */
+ continue;
+
+ /* The volume exists but busy, or an error occurred */
+ dbg_err("cannot open volume \"%s\", error %d",
+ re->new_name, err);
+ goto out_free;
+ }
+
+ re = kzalloc(sizeof(struct ubi_rename_entry), GFP_KERNEL);
+ if (!re) {
+ err = -ENOMEM;
+ ubi_close_volume(desc);
+ goto out_free;
+ }
+
+ re->remove = 1;
+ re->desc = desc;
+ list_add(&re->list, &rename_list);
+ dbg_msg("will remove volume %d, name \"%s\"",
+ re->desc->vol->vol_id, re->desc->vol->name);
+ }
+
+ mutex_lock(&ubi->volumes_mutex);
+ err = ubi_rename_volumes(ubi, &rename_list);
+ mutex_unlock(&ubi->volumes_mutex);
+
+out_free:
+ list_for_each_entry_safe(re, re1, &rename_list, list) {
+ ubi_close_volume(re->desc);
+ list_del(&re->list);
+ kfree(re);
+ }
+ return err;
+}
+
static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
@@ -621,19 +786,18 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
{
struct ubi_mkvol_req req;
- dbg_msg("create volume");
+ dbg_gen("create volume");
err = copy_from_user(&req, argp, sizeof(struct ubi_mkvol_req));
if (err) {
err = -EFAULT;
break;
}
+ req.name[req.name_len] = '\0';
err = verify_mkvol_req(ubi, &req);
if (err)
break;
- req.name[req.name_len] = '\0';
-
mutex_lock(&ubi->volumes_mutex);
err = ubi_create_volume(ubi, &req);
mutex_unlock(&ubi->volumes_mutex);
@@ -652,7 +816,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
{
int vol_id;
- dbg_msg("remove volume");
+ dbg_gen("remove volume");
err = get_user(vol_id, (__user int32_t *)argp);
if (err) {
err = -EFAULT;
@@ -666,7 +830,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
}
mutex_lock(&ubi->volumes_mutex);
- err = ubi_remove_volume(desc);
+ err = ubi_remove_volume(desc, 0);
mutex_unlock(&ubi->volumes_mutex);
/*
@@ -685,7 +849,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
uint64_t tmp;
struct ubi_rsvol_req req;
- dbg_msg("re-size volume");
+ dbg_gen("re-size volume");
err = copy_from_user(&req, argp, sizeof(struct ubi_rsvol_req));
if (err) {
err = -EFAULT;
@@ -713,6 +877,32 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
break;
}
+ /* Re-name volumes command */
+ case UBI_IOCRNVOL:
+ {
+ struct ubi_rnvol_req *req;
+
+ dbg_msg("re-name volumes");
+ req = kmalloc(sizeof(struct ubi_rnvol_req), GFP_KERNEL);
+ if (!req) {
+ err = -ENOMEM;
+ break;
+ };
+
+ err = copy_from_user(req, argp, sizeof(struct ubi_rnvol_req));
+ if (err) {
+ err = -EFAULT;
+ kfree(req);
+ break;
+ }
+
+ mutex_lock(&ubi->mult_mutex);
+ err = rename_volumes(ubi, req);
+ mutex_unlock(&ubi->mult_mutex);
+ kfree(req);
+ break;
+ }
+
default:
err = -ENOTTY;
break;
@@ -738,7 +928,7 @@ static int ctrl_cdev_ioctl(struct inode *inode, struct file *file,
struct ubi_attach_req req;
struct mtd_info *mtd;
- dbg_msg("attach MTD device");
+ dbg_gen("attach MTD device");
err = copy_from_user(&req, argp, sizeof(struct ubi_attach_req));
if (err) {
err = -EFAULT;
@@ -778,7 +968,7 @@ static int ctrl_cdev_ioctl(struct inode *inode, struct file *file,
{
int ubi_num;
- dbg_msg("dettach MTD device");
+ dbg_gen("dettach MTD device");
err = get_user(ubi_num, (__user int32_t *)argp);
if (err) {
err = -EFAULT;
diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c
index 56956ec2845..c0ed60e8ade 100644
--- a/drivers/mtd/ubi/debug.c
+++ b/drivers/mtd/ubi/debug.c
@@ -24,7 +24,7 @@
* changes.
*/
-#ifdef CONFIG_MTD_UBI_DEBUG_MSG
+#ifdef CONFIG_MTD_UBI_DEBUG
#include "ubi.h"
@@ -34,14 +34,19 @@
*/
void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr)
{
- dbg_msg("erase counter header dump:");
- dbg_msg("magic %#08x", be32_to_cpu(ec_hdr->magic));
- dbg_msg("version %d", (int)ec_hdr->version);
- dbg_msg("ec %llu", (long long)be64_to_cpu(ec_hdr->ec));
- dbg_msg("vid_hdr_offset %d", be32_to_cpu(ec_hdr->vid_hdr_offset));
- dbg_msg("data_offset %d", be32_to_cpu(ec_hdr->data_offset));
- dbg_msg("hdr_crc %#08x", be32_to_cpu(ec_hdr->hdr_crc));
- dbg_msg("erase counter header hexdump:");
+ printk(KERN_DEBUG "Erase counter header dump:\n");
+ printk(KERN_DEBUG "\tmagic %#08x\n",
+ be32_to_cpu(ec_hdr->magic));
+ printk(KERN_DEBUG "\tversion %d\n", (int)ec_hdr->version);
+ printk(KERN_DEBUG "\tec %llu\n",
+ (long long)be64_to_cpu(ec_hdr->ec));
+ printk(KERN_DEBUG "\tvid_hdr_offset %d\n",
+ be32_to_cpu(ec_hdr->vid_hdr_offset));
+ printk(KERN_DEBUG "\tdata_offset %d\n",
+ be32_to_cpu(ec_hdr->data_offset));
+ printk(KERN_DEBUG "\thdr_crc %#08x\n",
+ be32_to_cpu(ec_hdr->hdr_crc));
+ printk(KERN_DEBUG "erase counter header hexdump:\n");
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
ec_hdr, UBI_EC_HDR_SIZE, 1);
}
@@ -52,22 +57,23 @@ void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr)
*/
void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr)
{
- dbg_msg("volume identifier header dump:");
- dbg_msg("magic %08x", be32_to_cpu(vid_hdr->magic));
- dbg_msg("version %d", (int)vid_hdr->version);
- dbg_msg("vol_type %d", (int)vid_hdr->vol_type);
- dbg_msg("copy_flag %d", (int)vid_hdr->copy_flag);
- dbg_msg("compat %d", (int)vid_hdr->compat);
- dbg_msg("vol_id %d", be32_to_cpu(vid_hdr->vol_id));
- dbg_msg("lnum %d", be32_to_cpu(vid_hdr->lnum));
- dbg_msg("leb_ver %u", be32_to_cpu(vid_hdr->leb_ver));
- dbg_msg("data_size %d", be32_to_cpu(vid_hdr->data_size));
- dbg_msg("used_ebs %d", be32_to_cpu(vid_hdr->used_ebs));
- dbg_msg("data_pad %d", be32_to_cpu(vid_hdr->data_pad));
- dbg_msg("sqnum %llu",
+ printk(KERN_DEBUG "Volume identifier header dump:\n");
+ printk(KERN_DEBUG "\tmagic %08x\n", be32_to_cpu(vid_hdr->magic));
+ printk(KERN_DEBUG "\tversion %d\n", (int)vid_hdr->version);
+ printk(KERN_DEBUG "\tvol_type %d\n", (int)vid_hdr->vol_type);
+ printk(KERN_DEBUG "\tcopy_flag %d\n", (int)vid_hdr->copy_flag);
+ printk(KERN_DEBUG "\tcompat %d\n", (int)vid_hdr->compat);
+ printk(KERN_DEBUG "\tvol_id %d\n", be32_to_cpu(vid_hdr->vol_id));
+ printk(KERN_DEBUG "\tlnum %d\n", be32_to_cpu(vid_hdr->lnum));
+ printk(KERN_DEBUG "\tdata_size %d\n", be32_to_cpu(vid_hdr->data_size));
+ printk(KERN_DEBUG "\tused_ebs %d\n", be32_to_cpu(vid_hdr->used_ebs));
+ printk(KERN_DEBUG "\tdata_pad %d\n", be32_to_cpu(vid_hdr->data_pad));
+ printk(KERN_DEBUG "\tsqnum %llu\n",
(unsigned long long)be64_to_cpu(vid_hdr->sqnum));
- dbg_msg("hdr_crc %08x", be32_to_cpu(vid_hdr->hdr_crc));
- dbg_msg("volume identifier header hexdump:");
+ printk(KERN_DEBUG "\thdr_crc %08x\n", be32_to_cpu(vid_hdr->hdr_crc));
+ printk(KERN_DEBUG "Volume identifier header hexdump:\n");
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
+ vid_hdr, UBI_VID_HDR_SIZE, 1);
}
/**
@@ -76,27 +82,27 @@ void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr)
*/
void ubi_dbg_dump_vol_info(const struct ubi_volume *vol)
{
- dbg_msg("volume information dump:");
- dbg_msg("vol_id %d", vol->vol_id);
- dbg_msg("reserved_pebs %d", vol->reserved_pebs);
- dbg_msg("alignment %d", vol->alignment);
- dbg_msg("data_pad %d", vol->data_pad);
- dbg_msg("vol_type %d", vol->vol_type);
- dbg_msg("name_len %d", vol->name_len);
- dbg_msg("usable_leb_size %d", vol->usable_leb_size);
- dbg_msg("used_ebs %d", vol->used_ebs);
- dbg_msg("used_bytes %lld", vol->used_bytes);
- dbg_msg("last_eb_bytes %d", vol->last_eb_bytes);
- dbg_msg("corrupted %d", vol->corrupted);
- dbg_msg("upd_marker %d", vol->upd_marker);
+ printk(KERN_DEBUG "Volume information dump:\n");
+ printk(KERN_DEBUG "\tvol_id %d\n", vol->vol_id);
+ printk(KERN_DEBUG "\treserved_pebs %d\n", vol->reserved_pebs);
+ printk(KERN_DEBUG "\talignment %d\n", vol->alignment);
+ printk(KERN_DEBUG "\tdata_pad %d\n", vol->data_pad);
+ printk(KERN_DEBUG "\tvol_type %d\n", vol->vol_type);
+ printk(KERN_DEBUG "\tname_len %d\n", vol->name_len);
+ printk(KERN_DEBUG "\tusable_leb_size %d\n", vol->usable_leb_size);
+ printk(KERN_DEBUG "\tused_ebs %d\n", vol->used_ebs);
+ printk(KERN_DEBUG "\tused_bytes %lld\n", vol->used_bytes);
+ printk(KERN_DEBUG "\tlast_eb_bytes %d\n", vol->last_eb_bytes);
+ printk(KERN_DEBUG "\tcorrupted %d\n", vol->corrupted);
+ printk(KERN_DEBUG "\tupd_marker %d\n", vol->upd_marker);
if (vol->name_len <= UBI_VOL_NAME_MAX &&
strnlen(vol->name, vol->name_len + 1) == vol->name_len) {
- dbg_msg("name %s", vol->name);
+ printk(KERN_DEBUG "\tname %s\n", vol->name);
} else {
- dbg_msg("the 1st 5 characters of the name: %c%c%c%c%c",
- vol->name[0], vol->name[1], vol->name[2],
- vol->name[3], vol->name[4]);
+ printk(KERN_DEBUG "\t1st 5 characters of name: %c%c%c%c%c\n",
+ vol->name[0], vol->name[1], vol->name[2],
+ vol->name[3], vol->name[4]);
}
}
@@ -109,28 +115,29 @@ void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx)
{
int name_len = be16_to_cpu(r->name_len);
- dbg_msg("volume table record %d dump:", idx);
- dbg_msg("reserved_pebs %d", be32_to_cpu(r->reserved_pebs));
- dbg_msg("alignment %d", be32_to_cpu(r->alignment));
- dbg_msg("data_pad %d", be32_to_cpu(r->data_pad));
- dbg_msg("vol_type %d", (int)r->vol_type);
- dbg_msg("upd_marker %d", (int)r->upd_marker);
- dbg_msg("name_len %d", name_len);
+ printk(KERN_DEBUG "Volume table record %d dump:\n", idx);
+ printk(KERN_DEBUG "\treserved_pebs %d\n",
+ be32_to_cpu(r->reserved_pebs));
+ printk(KERN_DEBUG "\talignment %d\n", be32_to_cpu(r->alignment));
+ printk(KERN_DEBUG "\tdata_pad %d\n", be32_to_cpu(r->data_pad));
+ printk(KERN_DEBUG "\tvol_type %d\n", (int)r->vol_type);
+ printk(KERN_DEBUG "\tupd_marker %d\n", (int)r->upd_marker);
+ printk(KERN_DEBUG "\tname_len %d\n", name_len);
if (r->name[0] == '\0') {
- dbg_msg("name NULL");
+ printk(KERN_DEBUG "\tname NULL\n");
return;
}
if (name_len <= UBI_VOL_NAME_MAX &&
strnlen(&r->name[0], name_len + 1) == name_len) {
- dbg_msg("name %s", &r->name[0]);
+ printk(KERN_DEBUG "\tname %s\n", &r->name[0]);
} else {
- dbg_msg("1st 5 characters of the name: %c%c%c%c%c",
+ printk(KERN_DEBUG "\t1st 5 characters of name: %c%c%c%c%c\n",
r->name[0], r->name[1], r->name[2], r->name[3],
r->name[4]);
}
- dbg_msg("crc %#08x", be32_to_cpu(r->crc));
+ printk(KERN_DEBUG "\tcrc %#08x\n", be32_to_cpu(r->crc));
}
/**
@@ -139,15 +146,15 @@ void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx)
*/
void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv)
{
- dbg_msg("volume scanning information dump:");
- dbg_msg("vol_id %d", sv->vol_id);
- dbg_msg("highest_lnum %d", sv->highest_lnum);
- dbg_msg("leb_count %d", sv->leb_count);
- dbg_msg("compat %d", sv->compat);
- dbg_msg("vol_type %d", sv->vol_type);
- dbg_msg("used_ebs %d", sv->used_ebs);
- dbg_msg("last_data_size %d", sv->last_data_size);
- dbg_msg("data_pad %d", sv->data_pad);
+ printk(KERN_DEBUG "Volume scanning information dump:\n");
+ printk(KERN_DEBUG "\tvol_id %d\n", sv->vol_id);
+ printk(KERN_DEBUG "\thighest_lnum %d\n", sv->highest_lnum);
+ printk(KERN_DEBUG "\tleb_count %d\n", sv->leb_count);
+ printk(KERN_DEBUG "\tcompat %d\n", sv->compat);
+ printk(KERN_DEBUG "\tvol_type %d\n", sv->vol_type);
+ printk(KERN_DEBUG "\tused_ebs %d\n", sv->used_ebs);
+ printk(KERN_DEBUG "\tlast_data_size %d\n", sv->last_data_size);
+ printk(KERN_DEBUG "\tdata_pad %d\n", sv->data_pad);
}
/**
@@ -157,14 +164,13 @@ void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv)
*/
void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type)
{
- dbg_msg("eraseblock scanning information dump:");
- dbg_msg("ec %d", seb->ec);
- dbg_msg("pnum %d", seb->pnum);
+ printk(KERN_DEBUG "eraseblock scanning information dump:\n");
+ printk(KERN_DEBUG "\tec %d\n", seb->ec);
+ printk(KERN_DEBUG "\tpnum %d\n", seb->pnum);
if (type == 0) {
- dbg_msg("lnum %d", seb->lnum);
- dbg_msg("scrub %d", seb->scrub);
- dbg_msg("sqnum %llu", seb->sqnum);
- dbg_msg("leb_ver %u", seb->leb_ver);
+ printk(KERN_DEBUG "\tlnum %d\n", seb->lnum);
+ printk(KERN_DEBUG "\tscrub %d\n", seb->scrub);
+ printk(KERN_DEBUG "\tsqnum %llu\n", seb->sqnum);
}
}
@@ -176,16 +182,16 @@ void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req)
{
char nm[17];
- dbg_msg("volume creation request dump:");
- dbg_msg("vol_id %d", req->vol_id);
- dbg_msg("alignment %d", req->alignment);
- dbg_msg("bytes %lld", (long long)req->bytes);
- dbg_msg("vol_type %d", req->vol_type);
- dbg_msg("name_len %d", req->name_len);
+ printk(KERN_DEBUG "Volume creation request dump:\n");
+ printk(KERN_DEBUG "\tvol_id %d\n", req->vol_id);
+ printk(KERN_DEBUG "\talignment %d\n", req->alignment);
+ printk(KERN_DEBUG "\tbytes %lld\n", (long long)req->bytes);
+ printk(KERN_DEBUG "\tvol_type %d\n", req->vol_type);
+ printk(KERN_DEBUG "\tname_len %d\n", req->name_len);
memcpy(nm, req->name, 16);
nm[16] = 0;
- dbg_msg("the 1st 16 characters of the name: %s", nm);
+ printk(KERN_DEBUG "\t1st 16 characters of name: %s\n", nm);
}
-#endif /* CONFIG_MTD_UBI_DEBUG_MSG */
+#endif /* CONFIG_MTD_UBI_DEBUG */
diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h
index 8ea99d8c9e1..78e914d23ec 100644
--- a/drivers/mtd/ubi/debug.h
+++ b/drivers/mtd/ubi/debug.h
@@ -24,21 +24,16 @@
#ifdef CONFIG_MTD_UBI_DEBUG
#include <linux/random.h>
-#define ubi_assert(expr) BUG_ON(!(expr))
#define dbg_err(fmt, ...) ubi_err(fmt, ##__VA_ARGS__)
-#else
-#define ubi_assert(expr) ({})
-#define dbg_err(fmt, ...) ({})
-#endif
-#ifdef CONFIG_MTD_UBI_DEBUG_DISABLE_BGT
-#define DBG_DISABLE_BGT 1
-#else
-#define DBG_DISABLE_BGT 0
-#endif
+#define ubi_assert(expr) do { \
+ if (unlikely(!(expr))) { \
+ printk(KERN_CRIT "UBI assert failed in %s at %u (pid %d)\n", \
+ __func__, __LINE__, current->pid); \
+ ubi_dbg_dump_stack(); \
+ } \
+} while (0)
-#ifdef CONFIG_MTD_UBI_DEBUG_MSG
-/* Generic debugging message */
#define dbg_msg(fmt, ...) \
printk(KERN_DEBUG "UBI DBG (pid %d): %s: " fmt "\n", \
current->pid, __func__, ##__VA_ARGS__)
@@ -61,36 +56,29 @@ void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv);
void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type);
void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req);
+#ifdef CONFIG_MTD_UBI_DEBUG_MSG
+/* General debugging messages */
+#define dbg_gen(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
#else
-
-#define dbg_msg(fmt, ...) ({})
-#define ubi_dbg_dump_stack() ({})
-#define ubi_dbg_dump_ec_hdr(ec_hdr) ({})
-#define ubi_dbg_dump_vid_hdr(vid_hdr) ({})
-#define ubi_dbg_dump_vol_info(vol) ({})
-#define ubi_dbg_dump_vtbl_record(r, idx) ({})
-#define ubi_dbg_dump_sv(sv) ({})
-#define ubi_dbg_dump_seb(seb, type) ({})
-#define ubi_dbg_dump_mkvol_req(req) ({})
-
-#endif /* CONFIG_MTD_UBI_DEBUG_MSG */
+#define dbg_gen(fmt, ...) ({})
+#endif
#ifdef CONFIG_MTD_UBI_DEBUG_MSG_EBA
-/* Messages from the eraseblock association unit */
+/* Messages from the eraseblock association sub-system */
#define dbg_eba(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
#else
#define dbg_eba(fmt, ...) ({})
#endif
#ifdef CONFIG_MTD_UBI_DEBUG_MSG_WL
-/* Messages from the wear-leveling unit */
+/* Messages from the wear-leveling sub-system */
#define dbg_wl(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
#else
#define dbg_wl(fmt, ...) ({})
#endif
#ifdef CONFIG_MTD_UBI_DEBUG_MSG_IO
-/* Messages from the input/output unit */
+/* Messages from the input/output sub-system */
#define dbg_io(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__)
#else
#define dbg_io(fmt, ...) ({})
@@ -105,6 +93,12 @@ void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req);
#define UBI_IO_DEBUG 0
#endif
+#ifdef CONFIG_MTD_UBI_DEBUG_DISABLE_BGT
+#define DBG_DISABLE_BGT 1
+#else
+#define DBG_DISABLE_BGT 0
+#endif
+
#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS
/**
* ubi_dbg_is_bitflip - if it is time to emulate a bit-flip.
@@ -149,4 +143,30 @@ static inline int ubi_dbg_is_erase_failure(void)
#define ubi_dbg_is_erase_failure() 0
#endif
+#else
+
+#define ubi_assert(expr) ({})
+#define dbg_err(fmt, ...) ({})
+#define dbg_msg(fmt, ...) ({})
+#define dbg_gen(fmt, ...) ({})
+#define dbg_eba(fmt, ...) ({})
+#define dbg_wl(fmt, ...) ({})
+#define dbg_io(fmt, ...) ({})
+#define dbg_bld(fmt, ...) ({})
+#define ubi_dbg_dump_stack() ({})
+#define ubi_dbg_dump_ec_hdr(ec_hdr) ({})
+#define ubi_dbg_dump_vid_hdr(vid_hdr) ({})
+#define ubi_dbg_dump_vol_info(vol) ({})
+#define ubi_dbg_dump_vtbl_record(r, idx) ({})
+#define ubi_dbg_dump_sv(sv) ({})
+#define ubi_dbg_dump_seb(seb, type) ({})
+#define ubi_dbg_dump_mkvol_req(req) ({})
+
+#define UBI_IO_DEBUG 0
+#define DBG_DISABLE_BGT 0
+#define ubi_dbg_is_bitflip() 0
+#define ubi_dbg_is_write_failure() 0
+#define ubi_dbg_is_erase_failure() 0
+
+#endif /* !CONFIG_MTD_UBI_DEBUG */
#endif /* !__UBI_DEBUG_H__ */
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c
index 7ce91ca742b..e04bcf1dff8 100644
--- a/drivers/mtd/ubi/eba.c
+++ b/drivers/mtd/ubi/eba.c
@@ -19,20 +19,20 @@
*/
/*
- * The UBI Eraseblock Association (EBA) unit.
+ * The UBI Eraseblock Association (EBA) sub-system.
*
- * This unit is responsible for I/O to/from logical eraseblock.
+ * This sub-system is responsible for I/O to/from logical eraseblock.
*
* Although in this implementation the EBA table is fully kept and managed in
* RAM, which assumes poor scalability, it might be (partially) maintained on
* flash in future implementations.
*
- * The EBA unit implements per-logical eraseblock locking. Before accessing a
- * logical eraseblock it is locked for reading or writing. The per-logical
- * eraseblock locking is implemented by means of the lock tree. The lock tree
- * is an RB-tree which refers all the currently locked logical eraseblocks. The
- * lock tree elements are &struct ubi_ltree_entry objects. They are indexed by
- * (@vol_id, @lnum) pairs.
+ * The EBA sub-system implements per-logical eraseblock locking. Before
+ * accessing a logical eraseblock it is locked for reading or writing. The
+ * per-logical eraseblock locking is implemented by means of the lock tree. The
+ * lock tree is an RB-tree which refers all the currently locked logical
+ * eraseblocks. The lock tree elements are &struct ubi_ltree_entry objects.
+ * They are indexed by (@vol_id, @lnum) pairs.
*
* EBA also maintains the global sequence counter which is incremented each
* time a logical eraseblock is mapped to a physical eraseblock and it is
@@ -189,9 +189,7 @@ static struct ubi_ltree_entry *ltree_add_entry(struct ubi_device *ubi,
le->users += 1;
spin_unlock(&ubi->ltree_lock);
- if (le_free)
- kfree(le_free);
-
+ kfree(le_free);
return le;
}
@@ -223,22 +221,18 @@ static int leb_read_lock(struct ubi_device *ubi, int vol_id, int lnum)
*/
static void leb_read_unlock(struct ubi_device *ubi, int vol_id, int lnum)
{
- int free = 0;
struct ubi_ltree_entry *le;
spin_lock(&ubi->ltree_lock);
le = ltree_lookup(ubi, vol_id, lnum);
le->users -= 1;
ubi_assert(le->users >= 0);
+ up_read(&le->mutex);
if (le->users == 0) {
rb_erase(&le->rb, &ubi->ltree);
- free = 1;
+ kfree(le);
}
spin_unlock(&ubi->ltree_lock);
-
- up_read(&le->mutex);
- if (free)
- kfree(le);
}
/**
@@ -274,7 +268,6 @@ static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum)
*/
static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum)
{
- int free;
struct ubi_ltree_entry *le;
le = ltree_add_entry(ubi, vol_id, lnum);
@@ -289,12 +282,9 @@ static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum)
ubi_assert(le->users >= 0);
if (le->users == 0) {
rb_erase(&le->rb, &ubi->ltree);
- free = 1;
- } else
- free = 0;
- spin_unlock(&ubi->ltree_lock);
- if (free)
kfree(le);
+ }
+ spin_unlock(&ubi->ltree_lock);
return 1;
}
@@ -307,23 +297,18 @@ static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum)
*/
static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum)
{
- int free;
struct ubi_ltree_entry *le;
spin_lock(&ubi->ltree_lock);
le = ltree_lookup(ubi, vol_id, lnum);
le->users -= 1;
ubi_assert(le->users >= 0);
+ up_write(&le->mutex);
if (le->users == 0) {
rb_erase(&le->rb, &ubi->ltree);
- free = 1;
- } else
- free = 0;
- spin_unlock(&ubi->ltree_lock);
-
- up_write(&le->mutex);
- if (free)
kfree(le);
+ }
+ spin_unlock(&ubi->ltree_lock);
}
/**
@@ -516,9 +501,8 @@ static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum,
struct ubi_vid_hdr *vid_hdr;
vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
- if (!vid_hdr) {
+ if (!vid_hdr)
return -ENOMEM;
- }
mutex_lock(&ubi->buf_mutex);
@@ -752,7 +736,7 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol,
/* If this is the last LEB @len may be unaligned */
len = ALIGN(data_size, ubi->min_io_size);
else
- ubi_assert(len % ubi->min_io_size == 0);
+ ubi_assert(!(len & (ubi->min_io_size - 1)));
vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
if (!vid_hdr)
@@ -919,7 +903,7 @@ retry:
}
if (vol->eba_tbl[lnum] >= 0) {
- err = ubi_wl_put_peb(ubi, vol->eba_tbl[lnum], 1);
+ err = ubi_wl_put_peb(ubi, vol->eba_tbl[lnum], 0);
if (err)
goto out_leb_unlock;
}
@@ -1141,7 +1125,7 @@ out_unlock_leb:
}
/**
- * ubi_eba_init_scan - initialize the EBA unit using scanning information.
+ * ubi_eba_init_scan - initialize the EBA sub-system using scanning information.
* @ubi: UBI device description object
* @si: scanning information
*
@@ -1156,7 +1140,7 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
struct ubi_scan_leb *seb;
struct rb_node *rb;
- dbg_eba("initialize EBA unit");
+ dbg_eba("initialize EBA sub-system");
spin_lock_init(&ubi->ltree_lock);
mutex_init(&ubi->alc_mutex);
@@ -1222,7 +1206,7 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
ubi->rsvd_pebs += ubi->beb_rsvd_pebs;
}
- dbg_eba("EBA unit is initialized");
+ dbg_eba("EBA sub-system is initialized");
return 0;
out_free:
@@ -1233,20 +1217,3 @@ out_free:
}
return err;
}
-
-/**
- * ubi_eba_close - close EBA unit.
- * @ubi: UBI device description object
- */
-void ubi_eba_close(const struct ubi_device *ubi)
-{
- int i, num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT;
-
- dbg_eba("close EBA unit");
-
- for (i = 0; i < num_volumes; i++) {
- if (!ubi->volumes[i])
- continue;
- kfree(ubi->volumes[i]->eba_tbl);
- }
-}
diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c
index e909b390069..605812bb0b1 100644
--- a/drivers/mtd/ubi/gluebi.c
+++ b/drivers/mtd/ubi/gluebi.c
@@ -111,7 +111,7 @@ static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len,
struct ubi_device *ubi;
uint64_t tmp = from;
- dbg_msg("read %zd bytes from offset %lld", len, from);
+ dbg_gen("read %zd bytes from offset %lld", len, from);
if (len < 0 || from < 0 || from + len > mtd->size)
return -EINVAL;
@@ -162,7 +162,7 @@ static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len,
struct ubi_device *ubi;
uint64_t tmp = to;
- dbg_msg("write %zd bytes to offset %lld", len, to);
+ dbg_gen("write %zd bytes to offset %lld", len, to);
if (len < 0 || to < 0 || len + to > mtd->size)
return -EINVAL;
@@ -215,7 +215,7 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr)
struct ubi_volume *vol;
struct ubi_device *ubi;
- dbg_msg("erase %u bytes at offset %u", instr->len, instr->addr);
+ dbg_gen("erase %u bytes at offset %u", instr->len, instr->addr);
if (instr->addr < 0 || instr->addr > mtd->size - mtd->erasesize)
return -EINVAL;
@@ -249,8 +249,8 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr)
if (err)
goto out_err;
- instr->state = MTD_ERASE_DONE;
- mtd_erase_callback(instr);
+ instr->state = MTD_ERASE_DONE;
+ mtd_erase_callback(instr);
return 0;
out_err:
@@ -299,12 +299,12 @@ int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol)
mtd->size = vol->used_bytes;
if (add_mtd_device(mtd)) {
- ubi_err("cannot not add MTD device\n");
+ ubi_err("cannot not add MTD device");
kfree(mtd->name);
return -ENFILE;
}
- dbg_msg("added mtd%d (\"%s\"), size %u, EB size %u",
+ dbg_gen("added mtd%d (\"%s\"), size %u, EB size %u",
mtd->index, mtd->name, mtd->size, mtd->erasesize);
return 0;
}
@@ -322,7 +322,7 @@ int ubi_destroy_gluebi(struct ubi_volume *vol)
int err;
struct mtd_info *mtd = &vol->gluebi_mtd;
- dbg_msg("remove mtd%d", mtd->index);
+ dbg_gen("remove mtd%d", mtd->index);
err = del_mtd_device(mtd);
if (err)
return err;
diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c
index 4ac11df7b04..2fb64be44f1 100644
--- a/drivers/mtd/ubi/io.c
+++ b/drivers/mtd/ubi/io.c
@@ -20,15 +20,15 @@
*/
/*
- * UBI input/output unit.
+ * UBI input/output sub-system.
*
- * This unit provides a uniform way to work with all kinds of the underlying
- * MTD devices. It also implements handy functions for reading and writing UBI
- * headers.
+ * This sub-system provides a uniform way to work with all kinds of the
+ * underlying MTD devices. It also implements handy functions for reading and
+ * writing UBI headers.
*
* We are trying to have a paranoid mindset and not to trust to what we read
- * from the flash media in order to be more secure and robust. So this unit
- * validates every single header it reads from the flash media.
+ * from the flash media in order to be more secure and robust. So this
+ * sub-system validates every single header it reads from the flash media.
*
* Some words about how the eraseblock headers are stored.
*
@@ -79,11 +79,11 @@
* 512-byte chunks, we have to allocate one more buffer and copy our VID header
* to offset 448 of this buffer.
*
- * The I/O unit does the following trick in order to avoid this extra copy.
- * It always allocates a @ubi->vid_hdr_alsize bytes buffer for the VID header
- * and returns a pointer to offset @ubi->vid_hdr_shift of this buffer. When the
- * VID header is being written out, it shifts the VID header pointer back and
- * writes the whole sub-page.
+ * The I/O sub-system does the following trick in order to avoid this extra
+ * copy. It always allocates a @ubi->vid_hdr_alsize bytes buffer for the VID
+ * header and returns a pointer to offset @ubi->vid_hdr_shift of this buffer.
+ * When the VID header is being written out, it shifts the VID header pointer
+ * back and writes the whole sub-page.
*/
#include <linux/crc32.h>
@@ -156,15 +156,19 @@ retry:
/*
* -EUCLEAN is reported if there was a bit-flip which
* was corrected, so this is harmless.
+ *
+ * We do not report about it here unless debugging is
+ * enabled. A corresponding message will be printed
+ * later, when it is has been scrubbed.
*/
- ubi_msg("fixable bit-flip detected at PEB %d", pnum);
+ dbg_msg("fixable bit-flip detected at PEB %d", pnum);
ubi_assert(len == read);
return UBI_IO_BITFLIPS;
}
if (read != len && retries++ < UBI_IO_RETRIES) {
- dbg_io("error %d while reading %d bytes from PEB %d:%d, "
- "read only %zd bytes, retry",
+ dbg_io("error %d while reading %d bytes from PEB %d:%d,"
+ " read only %zd bytes, retry",
err, len, pnum, offset, read);
yield();
goto retry;
@@ -187,7 +191,7 @@ retry:
ubi_assert(len == read);
if (ubi_dbg_is_bitflip()) {
- dbg_msg("bit-flip (emulated)");
+ dbg_gen("bit-flip (emulated)");
err = UBI_IO_BITFLIPS;
}
}
@@ -391,6 +395,7 @@ static int torture_peb(struct ubi_device *ubi, int pnum)
{
int err, i, patt_count;
+ ubi_msg("run torture test for PEB %d", pnum);
patt_count = ARRAY_SIZE(patterns);
ubi_assert(patt_count > 0);
@@ -434,6 +439,7 @@ static int torture_peb(struct ubi_device *ubi, int pnum)
}
err = patt_count;
+ ubi_msg("PEB %d passed torture test, do not mark it a bad", pnum);
out:
mutex_unlock(&ubi->buf_mutex);
@@ -699,8 +705,8 @@ int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum,
if (hdr_crc != crc) {
if (verbose) {
- ubi_warn("bad EC header CRC at PEB %d, calculated %#08x,"
- " read %#08x", pnum, crc, hdr_crc);
+ ubi_warn("bad EC header CRC at PEB %d, calculated "
+ "%#08x, read %#08x", pnum, crc, hdr_crc);
ubi_dbg_dump_ec_hdr(ec_hdr);
}
return UBI_IO_BAD_EC_HDR;
@@ -1095,8 +1101,7 @@ fail:
}
/**
- * paranoid_check_peb_ec_hdr - check that the erase counter header of a
- * physical eraseblock is in-place and is all right.
+ * paranoid_check_peb_ec_hdr - check erase counter header.
* @ubi: UBI device description object
* @pnum: the physical eraseblock number to check
*
@@ -1174,8 +1179,7 @@ fail:
}
/**
- * paranoid_check_peb_vid_hdr - check that the volume identifier header of a
- * physical eraseblock is in-place and is all right.
+ * paranoid_check_peb_vid_hdr - check volume identifier header.
* @ubi: UBI device description object
* @pnum: the physical eraseblock number to check
*
@@ -1256,7 +1260,7 @@ static int paranoid_check_all_ff(struct ubi_device *ubi, int pnum, int offset,
fail:
ubi_err("paranoid check failed for PEB %d", pnum);
- dbg_msg("hex dump of the %d-%d region", offset, offset + len);
+ ubi_msg("hex dump of the %d-%d region", offset, offset + len);
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
ubi->dbg_peb_buf, len, 1);
err = 1;
diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c
index a70d58823f8..5d9bcf109c1 100644
--- a/drivers/mtd/ubi/kapi.c
+++ b/drivers/mtd/ubi/kapi.c
@@ -106,7 +106,7 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode)
struct ubi_device *ubi;
struct ubi_volume *vol;
- dbg_msg("open device %d volume %d, mode %d", ubi_num, vol_id, mode);
+ dbg_gen("open device %d volume %d, mode %d", ubi_num, vol_id, mode);
if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES)
return ERR_PTR(-EINVAL);
@@ -215,7 +215,7 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
struct ubi_device *ubi;
struct ubi_volume_desc *ret;
- dbg_msg("open volume %s, mode %d", name, mode);
+ dbg_gen("open volume %s, mode %d", name, mode);
if (!name)
return ERR_PTR(-EINVAL);
@@ -266,7 +266,7 @@ void ubi_close_volume(struct ubi_volume_desc *desc)
struct ubi_volume *vol = desc->vol;
struct ubi_device *ubi = vol->ubi;
- dbg_msg("close volume %d, mode %d", vol->vol_id, desc->mode);
+ dbg_gen("close volume %d, mode %d", vol->vol_id, desc->mode);
spin_lock(&ubi->volumes_lock);
switch (desc->mode) {
@@ -323,7 +323,7 @@ int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset,
struct ubi_device *ubi = vol->ubi;
int err, vol_id = vol->vol_id;
- dbg_msg("read %d bytes from LEB %d:%d:%d", len, vol_id, lnum, offset);
+ dbg_gen("read %d bytes from LEB %d:%d:%d", len, vol_id, lnum, offset);
if (vol_id < 0 || vol_id >= ubi->vtbl_slots || lnum < 0 ||
lnum >= vol->used_ebs || offset < 0 || len < 0 ||
@@ -388,7 +388,7 @@ int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
struct ubi_device *ubi = vol->ubi;
int vol_id = vol->vol_id;
- dbg_msg("write %d bytes to LEB %d:%d:%d", len, vol_id, lnum, offset);
+ dbg_gen("write %d bytes to LEB %d:%d:%d", len, vol_id, lnum, offset);
if (vol_id < 0 || vol_id >= ubi->vtbl_slots)
return -EINVAL;
@@ -397,8 +397,8 @@ int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
return -EROFS;
if (lnum < 0 || lnum >= vol->reserved_pebs || offset < 0 || len < 0 ||
- offset + len > vol->usable_leb_size || offset % ubi->min_io_size ||
- len % ubi->min_io_size)
+ offset + len > vol->usable_leb_size ||
+ offset & (ubi->min_io_size - 1) || len & (ubi->min_io_size - 1))
return -EINVAL;
if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM &&
@@ -438,7 +438,7 @@ int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
struct ubi_device *ubi = vol->ubi;
int vol_id = vol->vol_id;
- dbg_msg("atomically write %d bytes to LEB %d:%d", len, vol_id, lnum);
+ dbg_gen("atomically write %d bytes to LEB %d:%d", len, vol_id, lnum);
if (vol_id < 0 || vol_id >= ubi->vtbl_slots)
return -EINVAL;
@@ -447,7 +447,7 @@ int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
return -EROFS;
if (lnum < 0 || lnum >= vol->reserved_pebs || len < 0 ||
- len > vol->usable_leb_size || len % ubi->min_io_size)
+ len > vol->usable_leb_size || len & (ubi->min_io_size - 1))
return -EINVAL;
if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM &&
@@ -482,7 +482,7 @@ int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum)
struct ubi_device *ubi = vol->ubi;
int err;
- dbg_msg("erase LEB %d:%d", vol->vol_id, lnum);
+ dbg_gen("erase LEB %d:%d", vol->vol_id, lnum);
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
return -EROFS;
@@ -542,7 +542,7 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum)
struct ubi_volume *vol = desc->vol;
struct ubi_device *ubi = vol->ubi;
- dbg_msg("unmap LEB %d:%d", vol->vol_id, lnum);
+ dbg_gen("unmap LEB %d:%d", vol->vol_id, lnum);
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
return -EROFS;
@@ -579,7 +579,7 @@ int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype)
struct ubi_volume *vol = desc->vol;
struct ubi_device *ubi = vol->ubi;
- dbg_msg("unmap LEB %d:%d", vol->vol_id, lnum);
+ dbg_gen("unmap LEB %d:%d", vol->vol_id, lnum);
if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME)
return -EROFS;
@@ -621,7 +621,7 @@ int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum)
{
struct ubi_volume *vol = desc->vol;
- dbg_msg("test LEB %d:%d", vol->vol_id, lnum);
+ dbg_gen("test LEB %d:%d", vol->vol_id, lnum);
if (lnum < 0 || lnum >= vol->reserved_pebs)
return -EINVAL;
@@ -632,3 +632,27 @@ int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum)
return vol->eba_tbl[lnum] >= 0;
}
EXPORT_SYMBOL_GPL(ubi_is_mapped);
+
+/**
+ * ubi_sync - synchronize UBI device buffers.
+ * @ubi_num: UBI device to synchronize
+ *
+ * The underlying MTD device may cache data in hardware or in software. This
+ * function ensures the caches are flushed. Returns zero in case of success and
+ * a negative error code in case of failure.
+ */
+int ubi_sync(int ubi_num)
+{
+ struct ubi_device *ubi;
+
+ ubi = ubi_get_device(ubi_num);
+ if (!ubi)
+ return -ENODEV;
+
+ if (ubi->mtd->sync)
+ ubi->mtd->sync(ubi->mtd);
+
+ ubi_put_device(ubi);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ubi_sync);
diff --git a/drivers/mtd/ubi/misc.c b/drivers/mtd/ubi/misc.c
index 93e05281201..22ad3140294 100644
--- a/drivers/mtd/ubi/misc.c
+++ b/drivers/mtd/ubi/misc.c
@@ -37,7 +37,7 @@ int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf,
{
int i;
- ubi_assert(length % ubi->min_io_size == 0);
+ ubi_assert(!(length & (ubi->min_io_size - 1)));
for (i = length - 1; i >= 0; i--)
if (((const uint8_t *)buf)[i] != 0xFF)
diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c
index 96d410e106a..967bb4406df 100644
--- a/drivers/mtd/ubi/scan.c
+++ b/drivers/mtd/ubi/scan.c
@@ -19,9 +19,9 @@
*/
/*
- * UBI scanning unit.
+ * UBI scanning sub-system.
*
- * This unit is responsible for scanning the flash media, checking UBI
+ * This sub-system is responsible for scanning the flash media, checking UBI
* headers and providing complete information about the UBI flash image.
*
* The scanning information is represented by a &struct ubi_scan_info' object.
@@ -93,8 +93,7 @@ static int add_to_list(struct ubi_scan_info *si, int pnum, int ec,
}
/**
- * validate_vid_hdr - check that volume identifier header is correct and
- * consistent.
+ * validate_vid_hdr - check volume identifier header.
* @vid_hdr: the volume identifier header to check
* @sv: information about the volume this logical eraseblock belongs to
* @pnum: physical eraseblock number the VID header came from
@@ -103,7 +102,7 @@ static int add_to_list(struct ubi_scan_info *si, int pnum, int ec,
* non-zero if an inconsistency was found and zero if not.
*
* Note, UBI does sanity check of everything it reads from the flash media.
- * Most of the checks are done in the I/O unit. Here we check that the
+ * Most of the checks are done in the I/O sub-system. Here we check that the
* information in the VID header is consistent to the information in other VID
* headers of the same volume.
*/
@@ -247,45 +246,21 @@ static int compare_lebs(struct ubi_device *ubi, const struct ubi_scan_leb *seb,
struct ubi_vid_hdr *vh = NULL;
unsigned long long sqnum2 = be64_to_cpu(vid_hdr->sqnum);
- if (seb->sqnum == 0 && sqnum2 == 0) {
- long long abs, v1 = seb->leb_ver, v2 = be32_to_cpu(vid_hdr->leb_ver);
-
+ if (sqnum2 == seb->sqnum) {
/*
- * UBI constantly increases the logical eraseblock version
- * number and it can overflow. Thus, we have to bear in mind
- * that versions that are close to %0xFFFFFFFF are less then
- * versions that are close to %0.
- *
- * The UBI WL unit guarantees that the number of pending tasks
- * is not greater then %0x7FFFFFFF. So, if the difference
- * between any two versions is greater or equivalent to
- * %0x7FFFFFFF, there was an overflow and the logical
- * eraseblock with lower version is actually newer then the one
- * with higher version.
- *
- * FIXME: but this is anyway obsolete and will be removed at
- * some point.
+ * This must be a really ancient UBI image which has been
+ * created before sequence numbers support has been added. At
+ * that times we used 32-bit LEB versions stored in logical
+ * eraseblocks. That was before UBI got into mainline. We do not
+ * support these images anymore. Well, those images will work
+ * still work, but only if no unclean reboots happened.
*/
- dbg_bld("using old crappy leb_ver stuff");
-
- if (v1 == v2) {
- ubi_err("PEB %d and PEB %d have the same version %lld",
- seb->pnum, pnum, v1);
- return -EINVAL;
- }
-
- abs = v1 - v2;
- if (abs < 0)
- abs = -abs;
+ ubi_err("unsupported on-flash UBI format\n");
+ return -EINVAL;
+ }
- if (abs < 0x7FFFFFFF)
- /* Non-overflow situation */
- second_is_newer = (v2 > v1);
- else
- second_is_newer = (v2 < v1);
- } else
- /* Obviously the LEB with lower sequence counter is older */
- second_is_newer = sqnum2 > seb->sqnum;
+ /* Obviously the LEB with lower sequence counter is older */
+ second_is_newer = !!(sqnum2 > seb->sqnum);
/*
* Now we know which copy is newer. If the copy flag of the PEB with
@@ -293,7 +268,7 @@ static int compare_lebs(struct ubi_device *ubi, const struct ubi_scan_leb *seb,
* check data CRC. For the second PEB we already have the VID header,
* for the first one - we'll need to re-read it from flash.
*
- * FIXME: this may be optimized so that we wouldn't read twice.
+ * Note: this may be optimized so that we wouldn't read twice.
*/
if (second_is_newer) {
@@ -379,8 +354,7 @@ out_free_vidh:
}
/**
- * ubi_scan_add_used - add information about a physical eraseblock to the
- * scanning information.
+ * ubi_scan_add_used - add physical eraseblock to the scanning information.
* @ubi: UBI device description object
* @si: scanning information
* @pnum: the physical eraseblock number
@@ -400,7 +374,6 @@ int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si,
int bitflips)
{
int err, vol_id, lnum;
- uint32_t leb_ver;
unsigned long long sqnum;
struct ubi_scan_volume *sv;
struct ubi_scan_leb *seb;
@@ -409,10 +382,9 @@ int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si,
vol_id = be32_to_cpu(vid_hdr->vol_id);
lnum = be32_to_cpu(vid_hdr->lnum);
sqnum = be64_to_cpu(vid_hdr->sqnum);
- leb_ver = be32_to_cpu(vid_hdr->leb_ver);
- dbg_bld("PEB %d, LEB %d:%d, EC %d, sqnum %llu, ver %u, bitflips %d",
- pnum, vol_id, lnum, ec, sqnum, leb_ver, bitflips);
+ dbg_bld("PEB %d, LEB %d:%d, EC %d, sqnum %llu, bitflips %d",
+ pnum, vol_id, lnum, ec, sqnum, bitflips);
sv = add_volume(si, vol_id, pnum, vid_hdr);
if (IS_ERR(sv) < 0)
@@ -445,25 +417,20 @@ int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si,
*/
dbg_bld("this LEB already exists: PEB %d, sqnum %llu, "
- "LEB ver %u, EC %d", seb->pnum, seb->sqnum,
- seb->leb_ver, seb->ec);
-
- /*
- * Make sure that the logical eraseblocks have different
- * versions. Otherwise the image is bad.
- */
- if (seb->leb_ver == leb_ver && leb_ver != 0) {
- ubi_err("two LEBs with same version %u", leb_ver);
- ubi_dbg_dump_seb(seb, 0);
- ubi_dbg_dump_vid_hdr(vid_hdr);
- return -EINVAL;
- }
+ "EC %d", seb->pnum, seb->sqnum, seb->ec);
/*
* Make sure that the logical eraseblocks have different
* sequence numbers. Otherwise the image is bad.
*
- * FIXME: remove 'sqnum != 0' check when leb_ver is removed.
+ * However, if the sequence number is zero, we assume it must
+ * be an ancient UBI image from the era when UBI did not have
+ * sequence numbers. We still can attach these images, unless
+ * there is a need to distinguish between old and new
+ * eraseblocks, in which case we'll refuse the image in
+ * 'compare_lebs()'. In other words, we attach old clean
+ * images, but refuse attaching old images with duplicated
+ * logical eraseblocks because there was an unclean reboot.
*/
if (seb->sqnum == sqnum && sqnum != 0) {
ubi_err("two LEBs with same sequence number %llu",
@@ -503,7 +470,6 @@ int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si,
seb->pnum = pnum;
seb->scrub = ((cmp_res & 2) || bitflips);
seb->sqnum = sqnum;
- seb->leb_ver = leb_ver;
if (sv->highest_lnum == lnum)
sv->last_data_size =
@@ -540,7 +506,6 @@ int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si,
seb->lnum = lnum;
seb->sqnum = sqnum;
seb->scrub = bitflips;
- seb->leb_ver = leb_ver;
if (sv->highest_lnum <= lnum) {
sv->highest_lnum = lnum;
@@ -554,8 +519,7 @@ int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si,
}
/**
- * ubi_scan_find_sv - find information about a particular volume in the
- * scanning information.
+ * ubi_scan_find_sv - find volume in the scanning information.
* @si: scanning information
* @vol_id: the requested volume ID
*
@@ -584,8 +548,7 @@ struct ubi_scan_volume *ubi_scan_find_sv(const struct ubi_scan_info *si,
}
/**
- * ubi_scan_find_seb - find information about a particular logical
- * eraseblock in the volume scanning information.
+ * ubi_scan_find_seb - find LEB in the volume scanning information.
* @sv: a pointer to the volume scanning information
* @lnum: the requested logical eraseblock
*
@@ -645,9 +608,9 @@ void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv)
*
* This function erases physical eraseblock 'pnum', and writes the erase
* counter header to it. This function should only be used on UBI device
- * initialization stages, when the EBA unit had not been yet initialized. This
- * function returns zero in case of success and a negative error code in case
- * of failure.
+ * initialization stages, when the EBA sub-system had not been yet initialized.
+ * This function returns zero in case of success and a negative error code in
+ * case of failure.
*/
int ubi_scan_erase_peb(struct ubi_device *ubi, const struct ubi_scan_info *si,
int pnum, int ec)
@@ -687,9 +650,10 @@ out_free:
* @si: scanning information
*
* This function returns a free physical eraseblock. It is supposed to be
- * called on the UBI initialization stages when the wear-leveling unit is not
- * initialized yet. This function picks a physical eraseblocks from one of the
- * lists, writes the EC header if it is needed, and removes it from the list.
+ * called on the UBI initialization stages when the wear-leveling sub-system is
+ * not initialized yet. This function picks a physical eraseblocks from one of
+ * the lists, writes the EC header if it is needed, and removes it from the
+ * list.
*
* This function returns scanning physical eraseblock information in case of
* success and an error code in case of failure.
@@ -742,8 +706,7 @@ struct ubi_scan_leb *ubi_scan_get_free_peb(struct ubi_device *ubi,
}
/**
- * process_eb - read UBI headers, check them and add corresponding data
- * to the scanning information.
+ * process_eb - read, check UBI headers, and add them to scanning information.
* @ubi: UBI device description object
* @si: scanning information
* @pnum: the physical eraseblock number
@@ -751,7 +714,8 @@ struct ubi_scan_leb *ubi_scan_get_free_peb(struct ubi_device *ubi,
* This function returns a zero if the physical eraseblock was successfully
* handled and a negative error code in case of failure.
*/
-static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum)
+static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si,
+ int pnum)
{
long long uninitialized_var(ec);
int err, bitflips = 0, vol_id, ec_corr = 0;
@@ -764,8 +728,9 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum
return err;
else if (err) {
/*
- * FIXME: this is actually duty of the I/O unit to initialize
- * this, but MTD does not provide enough information.
+ * FIXME: this is actually duty of the I/O sub-system to
+ * initialize this, but MTD does not provide enough
+ * information.
*/
si->bad_peb_count += 1;
return 0;
@@ -930,7 +895,7 @@ struct ubi_scan_info *ubi_scan(struct ubi_device *ubi)
for (pnum = 0; pnum < ubi->peb_count; pnum++) {
cond_resched();
- dbg_msg("process PEB %d", pnum);
+ dbg_gen("process PEB %d", pnum);
err = process_eb(ubi, si, pnum);
if (err < 0)
goto out_vidh;
@@ -1079,8 +1044,7 @@ void ubi_scan_destroy_si(struct ubi_scan_info *si)
#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
/**
- * paranoid_check_si - check if the scanning information is correct and
- * consistent.
+ * paranoid_check_si - check the scanning information.
* @ubi: UBI device description object
* @si: scanning information
*
@@ -1265,11 +1229,6 @@ static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si)
ubi_err("bad data_pad %d", sv->data_pad);
goto bad_vid_hdr;
}
-
- if (seb->leb_ver != be32_to_cpu(vidh->leb_ver)) {
- ubi_err("bad leb_ver %u", seb->leb_ver);
- goto bad_vid_hdr;
- }
}
if (!last_seb)
@@ -1299,8 +1258,7 @@ static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si)
if (err < 0) {
kfree(buf);
return err;
- }
- else if (err)
+ } else if (err)
buf[pnum] = 1;
}
diff --git a/drivers/mtd/ubi/scan.h b/drivers/mtd/ubi/scan.h
index 966b9b682a4..61df208e2f2 100644
--- a/drivers/mtd/ubi/scan.h
+++ b/drivers/mtd/ubi/scan.h
@@ -34,7 +34,6 @@
* @u: unions RB-tree or @list links
* @u.rb: link in the per-volume RB-tree of &struct ubi_scan_leb objects
* @u.list: link in one of the eraseblock lists
- * @leb_ver: logical eraseblock version (obsolete)
*
* One object of this type is allocated for each physical eraseblock during
* scanning.
@@ -49,7 +48,6 @@ struct ubi_scan_leb {
struct rb_node rb;
struct list_head list;
} u;
- uint32_t leb_ver;
};
/**
@@ -59,16 +57,16 @@ struct ubi_scan_leb {
* @leb_count: number of logical eraseblocks in this volume
* @vol_type: volume type
* @used_ebs: number of used logical eraseblocks in this volume (only for
- * static volumes)
+ * static volumes)
* @last_data_size: amount of data in the last logical eraseblock of this
- * volume (always equivalent to the usable logical eraseblock size in case of
- * dynamic volumes)
+ * volume (always equivalent to the usable logical eraseblock
+ * size in case of dynamic volumes)
* @data_pad: how many bytes at the end of logical eraseblocks of this volume
- * are not used (due to volume alignment)
+ * are not used (due to volume alignment)
* @compat: compatibility flags of this volume
* @rb: link in the volume RB-tree
* @root: root of the RB-tree containing all the eraseblock belonging to this
- * volume (&struct ubi_scan_leb objects)
+ * volume (&struct ubi_scan_leb objects)
*
* One object of this type is allocated for each volume during scanning.
*/
@@ -92,8 +90,8 @@ struct ubi_scan_volume {
* @free: list of free physical eraseblocks
* @erase: list of physical eraseblocks which have to be erased
* @alien: list of physical eraseblocks which should not be used by UBI (e.g.,
+ * those belonging to "preserve"-compatible internal volumes)
* @bad_peb_count: count of bad physical eraseblocks
- * those belonging to "preserve"-compatible internal volumes)
* @vols_found: number of volumes found during scanning
* @highest_vol_id: highest volume ID
* @alien_peb_count: count of physical eraseblocks in the @alien list
@@ -106,8 +104,8 @@ struct ubi_scan_volume {
* @ec_count: a temporary variable used when calculating @mean_ec
*
* This data structure contains the result of scanning and may be used by other
- * UBI units to build final UBI data structures, further error-recovery and so
- * on.
+ * UBI sub-systems to build final UBI data structures, further error-recovery
+ * and so on.
*/
struct ubi_scan_info {
struct rb_root volumes;
@@ -132,8 +130,7 @@ struct ubi_device;
struct ubi_vid_hdr;
/*
- * ubi_scan_move_to_list - move a physical eraseblock from the volume tree to a
- * list.
+ * ubi_scan_move_to_list - move a PEB from the volume tree to a list.
*
* @sv: volume scanning information
* @seb: scanning eraseblock infprmation
diff --git a/drivers/mtd/ubi/ubi-media.h b/drivers/mtd/ubi/ubi-media.h
index c3185d9fd04..2ad94040905 100644
--- a/drivers/mtd/ubi/ubi-media.h
+++ b/drivers/mtd/ubi/ubi-media.h
@@ -98,10 +98,11 @@ enum {
* Compatibility constants used by internal volumes.
*
* @UBI_COMPAT_DELETE: delete this internal volume before anything is written
- * to the flash
+ * to the flash
* @UBI_COMPAT_RO: attach this device in read-only mode
* @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its
- * physical eraseblocks, don't allow the wear-leveling unit to move them
+ * physical eraseblocks, don't allow the wear-leveling
+ * sub-system to move them
* @UBI_COMPAT_REJECT: reject this UBI image
*/
enum {
@@ -123,7 +124,7 @@ enum {
* struct ubi_ec_hdr - UBI erase counter header.
* @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC)
* @version: version of UBI implementation which is supposed to accept this
- * UBI image
+ * UBI image
* @padding1: reserved for future, zeroes
* @ec: the erase counter
* @vid_hdr_offset: where the VID header starts
@@ -159,24 +160,23 @@ struct ubi_ec_hdr {
* struct ubi_vid_hdr - on-flash UBI volume identifier header.
* @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC)
* @version: UBI implementation version which is supposed to accept this UBI
- * image (%UBI_VERSION)
+ * image (%UBI_VERSION)
* @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC)
* @copy_flag: if this logical eraseblock was copied from another physical
- * eraseblock (for wear-leveling reasons)
+ * eraseblock (for wear-leveling reasons)
* @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE,
- * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT)
+ * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT)
* @vol_id: ID of this volume
* @lnum: logical eraseblock number
- * @leb_ver: version of this logical eraseblock (IMPORTANT: obsolete, to be
- * removed, kept only for not breaking older UBI users)
+ * @padding1: reserved for future, zeroes
* @data_size: how many bytes of data this logical eraseblock contains
* @used_ebs: total number of used logical eraseblocks in this volume
* @data_pad: how many bytes at the end of this physical eraseblock are not
- * used
+ * used
* @data_crc: CRC checksum of the data stored in this logical eraseblock
- * @padding1: reserved for future, zeroes
- * @sqnum: sequence number
* @padding2: reserved for future, zeroes
+ * @sqnum: sequence number
+ * @padding3: reserved for future, zeroes
* @hdr_crc: volume identifier header CRC checksum
*
* The @sqnum is the value of the global sequence counter at the time when this
@@ -224,10 +224,6 @@ struct ubi_ec_hdr {
* checksum is correct, this physical eraseblock is selected (P1). Otherwise
* the older one (P) is selected.
*
- * Note, there is an obsolete @leb_ver field which was used instead of @sqnum
- * in the past. But it is not used anymore and we keep it in order to be able
- * to deal with old UBI images. It will be removed at some point.
- *
* There are 2 sorts of volumes in UBI: user volumes and internal volumes.
* Internal volumes are not seen from outside and are used for various internal
* UBI purposes. In this implementation there is only one internal volume - the
@@ -248,9 +244,9 @@ struct ubi_ec_hdr {
* The @data_crc field contains the CRC checksum of the contents of the logical
* eraseblock if this is a static volume. In case of dynamic volumes, it does
* not contain the CRC checksum as a rule. The only exception is when the
- * data of the physical eraseblock was moved by the wear-leveling unit, then
- * the wear-leveling unit calculates the data CRC and stores it in the
- * @data_crc field. And of course, the @copy_flag is %in this case.
+ * data of the physical eraseblock was moved by the wear-leveling sub-system,
+ * then the wear-leveling sub-system calculates the data CRC and stores it in
+ * the @data_crc field. And of course, the @copy_flag is %in this case.
*
* The @data_size field is used only for static volumes because UBI has to know
* how many bytes of data are stored in this eraseblock. For dynamic volumes,
@@ -277,14 +273,14 @@ struct ubi_vid_hdr {
__u8 compat;
__be32 vol_id;
__be32 lnum;
- __be32 leb_ver; /* obsolete, to be removed, don't use */
+ __u8 padding1[4];
__be32 data_size;
__be32 used_ebs;
__be32 data_pad;
__be32 data_crc;
- __u8 padding1[4];
+ __u8 padding2[4];
__be64 sqnum;
- __u8 padding2[12];
+ __u8 padding3[12];
__be32 hdr_crc;
} __attribute__ ((packed));
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index 67dcbd11c15..1c3fa18c26a 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -74,15 +74,15 @@
#define UBI_IO_RETRIES 3
/*
- * Error codes returned by the I/O unit.
+ * Error codes returned by the I/O sub-system.
*
* UBI_IO_PEB_EMPTY: the physical eraseblock is empty, i.e. it contains only
- * 0xFF bytes
+ * %0xFF bytes
* UBI_IO_PEB_FREE: the physical eraseblock is free, i.e. it contains only a
- * valid erase counter header, and the rest are %0xFF bytes
+ * valid erase counter header, and the rest are %0xFF bytes
* UBI_IO_BAD_EC_HDR: the erase counter header is corrupted (bad magic or CRC)
* UBI_IO_BAD_VID_HDR: the volume identifier header is corrupted (bad magic or
- * CRC)
+ * CRC)
* UBI_IO_BITFLIPS: bit-flips were detected and corrected
*/
enum {
@@ -99,9 +99,9 @@ enum {
* @ec: erase counter
* @pnum: physical eraseblock number
*
- * This data structure is used in the WL unit. Each physical eraseblock has a
- * corresponding &struct wl_entry object which may be kept in different
- * RB-trees. See WL unit for details.
+ * This data structure is used in the WL sub-system. Each physical eraseblock
+ * has a corresponding &struct wl_entry object which may be kept in different
+ * RB-trees. See WL sub-system for details.
*/
struct ubi_wl_entry {
struct rb_node rb;
@@ -118,10 +118,10 @@ struct ubi_wl_entry {
* @mutex: read/write mutex to implement read/write access serialization to
* the (@vol_id, @lnum) logical eraseblock
*
- * This data structure is used in the EBA unit to implement per-LEB locking.
- * When a logical eraseblock is being locked - corresponding
+ * This data structure is used in the EBA sub-system to implement per-LEB
+ * locking. When a logical eraseblock is being locked - corresponding
* &struct ubi_ltree_entry object is inserted to the lock tree (@ubi->ltree).
- * See EBA unit for details.
+ * See EBA sub-system for details.
*/
struct ubi_ltree_entry {
struct rb_node rb;
@@ -131,6 +131,27 @@ struct ubi_ltree_entry {
struct rw_semaphore mutex;
};
+/**
+ * struct ubi_rename_entry - volume re-name description data structure.
+ * @new_name_len: new volume name length
+ * @new_name: new volume name
+ * @remove: if not zero, this volume should be removed, not re-named
+ * @desc: descriptor of the volume
+ * @list: links re-name entries into a list
+ *
+ * This data structure is utilized in the multiple volume re-name code. Namely,
+ * UBI first creates a list of &struct ubi_rename_entry objects from the
+ * &struct ubi_rnvol_req request object, and then utilizes this list to do all
+ * the job.
+ */
+struct ubi_rename_entry {
+ int new_name_len;
+ char new_name[UBI_VOL_NAME_MAX + 1];
+ int remove;
+ struct ubi_volume_desc *desc;
+ struct list_head list;
+};
+
struct ubi_volume_desc;
/**
@@ -206,7 +227,7 @@ struct ubi_volume {
int alignment;
int data_pad;
int name_len;
- char name[UBI_VOL_NAME_MAX+1];
+ char name[UBI_VOL_NAME_MAX + 1];
int upd_ebs;
int ch_lnum;
@@ -225,7 +246,7 @@ struct ubi_volume {
#ifdef CONFIG_MTD_UBI_GLUEBI
/*
* Gluebi-related stuff may be compiled out.
- * TODO: this should not be built into UBI but should be a separate
+ * Note: this should not be built into UBI but should be a separate
* ubimtd driver which works on top of UBI and emulates MTD devices.
*/
struct ubi_volume_desc *gluebi_desc;
@@ -235,8 +256,7 @@ struct ubi_volume {
};
/**
- * struct ubi_volume_desc - descriptor of the UBI volume returned when it is
- * opened.
+ * struct ubi_volume_desc - UBI volume descriptor returned when it is opened.
* @vol: reference to the corresponding volume description object
* @mode: open mode (%UBI_READONLY, %UBI_READWRITE, or %UBI_EXCLUSIVE)
*/
@@ -273,7 +293,7 @@ struct ubi_wl_entry;
* @vtbl_size: size of the volume table in bytes
* @vtbl: in-RAM volume table copy
* @volumes_mutex: protects on-flash volume table and serializes volume
- * changes, like creation, deletion, update, resize
+ * changes, like creation, deletion, update, re-size and re-name
*
* @max_ec: current highest erase counter value
* @mean_ec: current mean erase counter value
@@ -293,6 +313,7 @@ struct ubi_wl_entry;
* @move_to, @move_to_put @erase_pending, @wl_scheduled, and @works
* fields
* @move_mutex: serializes eraseblock moves
+ * @work_sem: sycnhronizes the WL worker with use tasks
* @wl_scheduled: non-zero if the wear-leveling was scheduled
* @lookuptbl: a table to quickly find a &struct ubi_wl_entry object for any
* physical eraseblock
@@ -316,11 +337,11 @@ struct ubi_wl_entry;
* @ro_mode: if the UBI device is in read-only mode
* @leb_size: logical eraseblock size
* @leb_start: starting offset of logical eraseblocks within physical
- * eraseblocks
+ * eraseblocks
* @ec_hdr_alsize: size of the EC header aligned to @hdrs_min_io_size
* @vid_hdr_alsize: size of the VID header aligned to @hdrs_min_io_size
* @vid_hdr_offset: starting offset of the volume identifier header (might be
- * unaligned)
+ * unaligned)
* @vid_hdr_aloffset: starting offset of the VID header aligned to
* @hdrs_min_io_size
* @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset
@@ -331,6 +352,8 @@ struct ubi_wl_entry;
* @peb_buf1: a buffer of PEB size used for different purposes
* @peb_buf2: another buffer of PEB size used for different purposes
* @buf_mutex: proptects @peb_buf1 and @peb_buf2
+ * @ckvol_mutex: serializes static volume checking when opening
+ * @mult_mutex: serializes operations on multiple volumes, like re-nameing
* @dbg_peb_buf: buffer of PEB size used for debugging
* @dbg_buf_mutex: proptects @dbg_peb_buf
*/
@@ -356,16 +379,16 @@ struct ubi_device {
struct mutex volumes_mutex;
int max_ec;
- /* TODO: mean_ec is not updated run-time, fix */
+ /* Note, mean_ec is not updated run-time - should be fixed */
int mean_ec;
- /* EBA unit's stuff */
+ /* EBA sub-system's stuff */
unsigned long long global_sqnum;
spinlock_t ltree_lock;
struct rb_root ltree;
struct mutex alc_mutex;
- /* Wear-leveling unit's stuff */
+ /* Wear-leveling sub-system's stuff */
struct rb_root used;
struct rb_root free;
struct rb_root scrub;
@@ -388,7 +411,7 @@ struct ubi_device {
int thread_enabled;
char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2];
- /* I/O unit's stuff */
+ /* I/O sub-system's stuff */
long long flash_size;
int peb_count;
int peb_size;
@@ -411,6 +434,7 @@ struct ubi_device {
void *peb_buf2;
struct mutex buf_mutex;
struct mutex ckvol_mutex;
+ struct mutex mult_mutex;
#ifdef CONFIG_MTD_UBI_DEBUG
void *dbg_peb_buf;
struct mutex dbg_buf_mutex;
@@ -427,12 +451,15 @@ extern struct mutex ubi_devices_mutex;
/* vtbl.c */
int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
struct ubi_vtbl_record *vtbl_rec);
+int ubi_vtbl_rename_volumes(struct ubi_device *ubi,
+ struct list_head *rename_list);
int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si);
/* vmt.c */
int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req);
-int ubi_remove_volume(struct ubi_volume_desc *desc);
+int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl);
int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs);
+int ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list);
int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol);
void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol);
@@ -447,7 +474,8 @@ int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol,
const void __user *buf, int count);
/* misc.c */
-int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, int length);
+int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf,
+ int length);
int ubi_check_volume(struct ubi_device *ubi, int vol_id);
void ubi_calculate_reserved(struct ubi_device *ubi);
@@ -477,7 +505,6 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
struct ubi_vid_hdr *vid_hdr);
int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si);
-void ubi_eba_close(const struct ubi_device *ubi);
/* wl.c */
int ubi_wl_get_peb(struct ubi_device *ubi, int dtype);
diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c
index ddaa1a56cc6..8b89cc18ff0 100644
--- a/drivers/mtd/ubi/upd.c
+++ b/drivers/mtd/ubi/upd.c
@@ -39,7 +39,7 @@
*/
#include <linux/err.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/div64.h>
#include "ubi.h"
@@ -56,11 +56,11 @@ static int set_update_marker(struct ubi_device *ubi, struct ubi_volume *vol)
int err;
struct ubi_vtbl_record vtbl_rec;
- dbg_msg("set update marker for volume %d", vol->vol_id);
+ dbg_gen("set update marker for volume %d", vol->vol_id);
if (vol->upd_marker) {
ubi_assert(ubi->vtbl[vol->vol_id].upd_marker);
- dbg_msg("already set");
+ dbg_gen("already set");
return 0;
}
@@ -92,7 +92,7 @@ static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol,
uint64_t tmp;
struct ubi_vtbl_record vtbl_rec;
- dbg_msg("clear update marker for volume %d", vol->vol_id);
+ dbg_gen("clear update marker for volume %d", vol->vol_id);
memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id],
sizeof(struct ubi_vtbl_record));
@@ -133,7 +133,7 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol,
int i, err;
uint64_t tmp;
- dbg_msg("start update of volume %d, %llu bytes", vol->vol_id, bytes);
+ dbg_gen("start update of volume %d, %llu bytes", vol->vol_id, bytes);
ubi_assert(!vol->updating && !vol->changing_leb);
vol->updating = 1;
@@ -183,7 +183,7 @@ int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
{
ubi_assert(!vol->updating && !vol->changing_leb);
- dbg_msg("start changing LEB %d:%d, %u bytes",
+ dbg_gen("start changing LEB %d:%d, %u bytes",
vol->vol_id, req->lnum, req->bytes);
if (req->bytes == 0)
return ubi_eba_atomic_leb_change(ubi, vol, req->lnum, NULL, 0,
@@ -237,16 +237,17 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
int err;
if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
- len = ALIGN(len, ubi->min_io_size);
- memset(buf + len, 0xFF, len - len);
+ int l = ALIGN(len, ubi->min_io_size);
- len = ubi_calc_data_len(ubi, buf, len);
+ memset(buf + len, 0xFF, l - len);
+ len = ubi_calc_data_len(ubi, buf, l);
if (len == 0) {
- dbg_msg("all %d bytes contain 0xFF - skip", len);
+ dbg_gen("all %d bytes contain 0xFF - skip", len);
return 0;
}
- err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, len, UBI_UNKNOWN);
+ err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, len,
+ UBI_UNKNOWN);
} else {
/*
* When writing static volume, and this is the last logical
@@ -267,6 +268,7 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
/**
* ubi_more_update_data - write more update data.
+ * @ubi: UBI device description object
* @vol: volume description object
* @buf: write data (user-space memory buffer)
* @count: how much bytes to write
@@ -283,7 +285,7 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol,
uint64_t tmp;
int lnum, offs, err = 0, len, to_write = count;
- dbg_msg("write %d of %lld bytes, %lld already passed",
+ dbg_gen("write %d of %lld bytes, %lld already passed",
count, vol->upd_bytes, vol->upd_received);
if (ubi->ro_mode)
@@ -384,6 +386,7 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol,
/**
* ubi_more_leb_change_data - accept more data for atomic LEB change.
+ * @ubi: UBI device description object
* @vol: volume description object
* @buf: write data (user-space memory buffer)
* @count: how much bytes to write
@@ -400,7 +403,7 @@ int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol,
{
int err;
- dbg_msg("write %d of %lld bytes, %lld already passed",
+ dbg_gen("write %d of %lld bytes, %lld already passed",
count, vol->upd_bytes, vol->upd_received);
if (ubi->ro_mode)
@@ -418,7 +421,8 @@ int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol,
if (vol->upd_received == vol->upd_bytes) {
int len = ALIGN((int)vol->upd_bytes, ubi->min_io_size);
- memset(vol->upd_buf + vol->upd_bytes, 0xFF, len - vol->upd_bytes);
+ memset(vol->upd_buf + vol->upd_bytes, 0xFF,
+ len - vol->upd_bytes);
len = ubi_calc_data_len(ubi, vol->upd_buf, len);
err = ubi_eba_atomic_leb_change(ubi, vol, vol->ch_lnum,
vol->upd_buf, len, UBI_UNKNOWN);
diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c
index 5be58d85c63..3531ca9a1e2 100644
--- a/drivers/mtd/ubi/vmt.c
+++ b/drivers/mtd/ubi/vmt.c
@@ -28,9 +28,9 @@
#include "ubi.h"
#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
-static void paranoid_check_volumes(struct ubi_device *ubi);
+static int paranoid_check_volumes(struct ubi_device *ubi);
#else
-#define paranoid_check_volumes(ubi)
+#define paranoid_check_volumes(ubi) 0
#endif
static ssize_t vol_attribute_show(struct device *dev,
@@ -127,6 +127,7 @@ static void vol_release(struct device *dev)
{
struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev);
+ kfree(vol->eba_tbl);
kfree(vol);
}
@@ -201,7 +202,7 @@ static void volume_sysfs_close(struct ubi_volume *vol)
*/
int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
{
- int i, err, vol_id = req->vol_id, dont_free = 0;
+ int i, err, vol_id = req->vol_id, do_free = 1;
struct ubi_volume *vol;
struct ubi_vtbl_record vtbl_rec;
uint64_t bytes;
@@ -217,7 +218,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
spin_lock(&ubi->volumes_lock);
if (vol_id == UBI_VOL_NUM_AUTO) {
/* Find unused volume ID */
- dbg_msg("search for vacant volume ID");
+ dbg_gen("search for vacant volume ID");
for (i = 0; i < ubi->vtbl_slots; i++)
if (!ubi->volumes[i]) {
vol_id = i;
@@ -232,7 +233,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
req->vol_id = vol_id;
}
- dbg_msg("volume ID %d, %llu bytes, type %d, name %s",
+ dbg_gen("volume ID %d, %llu bytes, type %d, name %s",
vol_id, (unsigned long long)req->bytes,
(int)req->vol_type, req->name);
@@ -252,7 +253,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
goto out_unlock;
}
- /* Calculate how many eraseblocks are requested */
+ /* Calculate how many eraseblocks are requested */
vol->usable_leb_size = ubi->leb_size - ubi->leb_size % req->alignment;
bytes = req->bytes;
if (do_div(bytes, vol->usable_leb_size))
@@ -274,7 +275,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
vol->data_pad = ubi->leb_size % vol->alignment;
vol->vol_type = req->vol_type;
vol->name_len = req->name_len;
- memcpy(vol->name, req->name, vol->name_len + 1);
+ memcpy(vol->name, req->name, vol->name_len);
vol->ubi = ubi;
/*
@@ -349,7 +350,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
vtbl_rec.vol_type = UBI_VID_DYNAMIC;
else
vtbl_rec.vol_type = UBI_VID_STATIC;
- memcpy(vtbl_rec.name, vol->name, vol->name_len + 1);
+ memcpy(vtbl_rec.name, vol->name, vol->name_len);
err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
if (err)
@@ -360,19 +361,19 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
ubi->vol_count += 1;
spin_unlock(&ubi->volumes_lock);
- paranoid_check_volumes(ubi);
- return 0;
+ err = paranoid_check_volumes(ubi);
+ return err;
out_sysfs:
/*
- * We have registered our device, we should not free the volume*
+ * We have registered our device, we should not free the volume
* description object in this function in case of an error - it is
* freed by the release function.
*
* Get device reference to prevent the release function from being
* called just after sysfs has been closed.
*/
- dont_free = 1;
+ do_free = 0;
get_device(&vol->dev);
volume_sysfs_close(vol);
out_gluebi:
@@ -382,17 +383,18 @@ out_gluebi:
out_cdev:
cdev_del(&vol->cdev);
out_mapping:
- kfree(vol->eba_tbl);
+ if (do_free)
+ kfree(vol->eba_tbl);
out_acc:
spin_lock(&ubi->volumes_lock);
ubi->rsvd_pebs -= vol->reserved_pebs;
ubi->avail_pebs += vol->reserved_pebs;
out_unlock:
spin_unlock(&ubi->volumes_lock);
- if (dont_free)
- put_device(&vol->dev);
- else
+ if (do_free)
kfree(vol);
+ else
+ put_device(&vol->dev);
ubi_err("cannot create volume %d, error %d", vol_id, err);
return err;
}
@@ -400,19 +402,20 @@ out_unlock:
/**
* ubi_remove_volume - remove volume.
* @desc: volume descriptor
+ * @no_vtbl: do not change volume table if not zero
*
* This function removes volume described by @desc. The volume has to be opened
* in "exclusive" mode. Returns zero in case of success and a negative error
* code in case of failure. The caller has to have the @ubi->volumes_mutex
* locked.
*/
-int ubi_remove_volume(struct ubi_volume_desc *desc)
+int ubi_remove_volume(struct ubi_volume_desc *desc, int no_vtbl)
{
struct ubi_volume *vol = desc->vol;
struct ubi_device *ubi = vol->ubi;
int i, err, vol_id = vol->vol_id, reserved_pebs = vol->reserved_pebs;
- dbg_msg("remove UBI volume %d", vol_id);
+ dbg_gen("remove UBI volume %d", vol_id);
ubi_assert(desc->mode == UBI_EXCLUSIVE);
ubi_assert(vol == ubi->volumes[vol_id]);
@@ -435,9 +438,11 @@ int ubi_remove_volume(struct ubi_volume_desc *desc)
if (err)
goto out_err;
- err = ubi_change_vtbl_record(ubi, vol_id, NULL);
- if (err)
- goto out_err;
+ if (!no_vtbl) {
+ err = ubi_change_vtbl_record(ubi, vol_id, NULL);
+ if (err)
+ goto out_err;
+ }
for (i = 0; i < vol->reserved_pebs; i++) {
err = ubi_eba_unmap_leb(ubi, vol, i);
@@ -445,8 +450,6 @@ int ubi_remove_volume(struct ubi_volume_desc *desc)
goto out_err;
}
- kfree(vol->eba_tbl);
- vol->eba_tbl = NULL;
cdev_del(&vol->cdev);
volume_sysfs_close(vol);
@@ -465,8 +468,9 @@ int ubi_remove_volume(struct ubi_volume_desc *desc)
ubi->vol_count -= 1;
spin_unlock(&ubi->volumes_lock);
- paranoid_check_volumes(ubi);
- return 0;
+ if (!no_vtbl)
+ err = paranoid_check_volumes(ubi);
+ return err;
out_err:
ubi_err("cannot remove volume %d, error %d", vol_id, err);
@@ -497,7 +501,7 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
if (ubi->ro_mode)
return -EROFS;
- dbg_msg("re-size volume %d to from %d to %d PEBs",
+ dbg_gen("re-size volume %d to from %d to %d PEBs",
vol_id, vol->reserved_pebs, reserved_pebs);
if (vol->vol_type == UBI_STATIC_VOLUME &&
@@ -586,8 +590,8 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
(long long)vol->used_ebs * vol->usable_leb_size;
}
- paranoid_check_volumes(ubi);
- return 0;
+ err = paranoid_check_volumes(ubi);
+ return err;
out_acc:
if (pebs > 0) {
@@ -602,6 +606,44 @@ out_free:
}
/**
+ * ubi_rename_volumes - re-name UBI volumes.
+ * @ubi: UBI device description object
+ * @rename_list: list of &struct ubi_rename_entry objects
+ *
+ * This function re-names or removes volumes specified in the re-name list.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+int ubi_rename_volumes(struct ubi_device *ubi, struct list_head *rename_list)
+{
+ int err;
+ struct ubi_rename_entry *re;
+
+ err = ubi_vtbl_rename_volumes(ubi, rename_list);
+ if (err)
+ return err;
+
+ list_for_each_entry(re, rename_list, list) {
+ if (re->remove) {
+ err = ubi_remove_volume(re->desc, 1);
+ if (err)
+ break;
+ } else {
+ struct ubi_volume *vol = re->desc->vol;
+
+ spin_lock(&ubi->volumes_lock);
+ vol->name_len = re->new_name_len;
+ memcpy(vol->name, re->new_name, re->new_name_len + 1);
+ spin_unlock(&ubi->volumes_lock);
+ }
+ }
+
+ if (!err)
+ err = paranoid_check_volumes(ubi);
+ return err;
+}
+
+/**
* ubi_add_volume - add volume.
* @ubi: UBI device description object
* @vol: volume description object
@@ -615,8 +657,7 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol)
int err, vol_id = vol->vol_id;
dev_t dev;
- dbg_msg("add volume %d", vol_id);
- ubi_dbg_dump_vol_info(vol);
+ dbg_gen("add volume %d", vol_id);
/* Register character device for the volume */
cdev_init(&vol->cdev, &ubi_vol_cdev_operations);
@@ -650,8 +691,8 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol)
return err;
}
- paranoid_check_volumes(ubi);
- return 0;
+ err = paranoid_check_volumes(ubi);
+ return err;
out_gluebi:
err = ubi_destroy_gluebi(vol);
@@ -672,7 +713,7 @@ void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol)
{
int err;
- dbg_msg("free volume %d", vol->vol_id);
+ dbg_gen("free volume %d", vol->vol_id);
ubi->volumes[vol->vol_id] = NULL;
err = ubi_destroy_gluebi(vol);
@@ -686,8 +727,10 @@ void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol)
* paranoid_check_volume - check volume information.
* @ubi: UBI device description object
* @vol_id: volume ID
+ *
+ * Returns zero if volume is all right and a a negative error code if not.
*/
-static void paranoid_check_volume(struct ubi_device *ubi, int vol_id)
+static int paranoid_check_volume(struct ubi_device *ubi, int vol_id)
{
int idx = vol_id2idx(ubi, vol_id);
int reserved_pebs, alignment, data_pad, vol_type, name_len, upd_marker;
@@ -705,16 +748,7 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id)
goto fail;
}
spin_unlock(&ubi->volumes_lock);
- return;
- }
-
- if (vol->exclusive) {
- /*
- * The volume may be being created at the moment, do not check
- * it (e.g., it may be in the middle of ubi_create_volume().
- */
- spin_unlock(&ubi->volumes_lock);
- return;
+ return 0;
}
if (vol->reserved_pebs < 0 || vol->alignment < 0 || vol->data_pad < 0 ||
@@ -727,7 +761,7 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id)
goto fail;
}
- n = vol->alignment % ubi->min_io_size;
+ n = vol->alignment & (ubi->min_io_size - 1);
if (vol->alignment != 1 && n) {
ubi_err("alignment is not multiple of min I/O unit");
goto fail;
@@ -824,31 +858,39 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id)
if (alignment != vol->alignment || data_pad != vol->data_pad ||
upd_marker != vol->upd_marker || vol_type != vol->vol_type ||
- name_len!= vol->name_len || strncmp(name, vol->name, name_len)) {
+ name_len != vol->name_len || strncmp(name, vol->name, name_len)) {
ubi_err("volume info is different");
goto fail;
}
spin_unlock(&ubi->volumes_lock);
- return;
+ return 0;
fail:
ubi_err("paranoid check failed for volume %d", vol_id);
- ubi_dbg_dump_vol_info(vol);
+ if (vol)
+ ubi_dbg_dump_vol_info(vol);
ubi_dbg_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id);
spin_unlock(&ubi->volumes_lock);
- BUG();
+ return -EINVAL;
}
/**
* paranoid_check_volumes - check information about all volumes.
* @ubi: UBI device description object
+ *
+ * Returns zero if volumes are all right and a a negative error code if not.
*/
-static void paranoid_check_volumes(struct ubi_device *ubi)
+static int paranoid_check_volumes(struct ubi_device *ubi)
{
- int i;
+ int i, err = 0;
- for (i = 0; i < ubi->vtbl_slots; i++)
- paranoid_check_volume(ubi, i);
+ for (i = 0; i < ubi->vtbl_slots; i++) {
+ err = paranoid_check_volume(ubi, i);
+ if (err)
+ break;
+ }
+
+ return err;
}
#endif
diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c
index af36b12be27..217d0e111b2 100644
--- a/drivers/mtd/ubi/vtbl.c
+++ b/drivers/mtd/ubi/vtbl.c
@@ -115,8 +115,58 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx,
}
/**
- * vtbl_check - check if volume table is not corrupted and contains sensible
- * data.
+ * ubi_vtbl_rename_volumes - rename UBI volumes in the volume table.
+ * @ubi: UBI device description object
+ * @rename_list: list of &struct ubi_rename_entry objects
+ *
+ * This function re-names multiple volumes specified in @req in the volume
+ * table. Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+int ubi_vtbl_rename_volumes(struct ubi_device *ubi,
+ struct list_head *rename_list)
+{
+ int i, err;
+ struct ubi_rename_entry *re;
+ struct ubi_volume *layout_vol;
+
+ list_for_each_entry(re, rename_list, list) {
+ uint32_t crc;
+ struct ubi_volume *vol = re->desc->vol;
+ struct ubi_vtbl_record *vtbl_rec = &ubi->vtbl[vol->vol_id];
+
+ if (re->remove) {
+ memcpy(vtbl_rec, &empty_vtbl_record,
+ sizeof(struct ubi_vtbl_record));
+ continue;
+ }
+
+ vtbl_rec->name_len = cpu_to_be16(re->new_name_len);
+ memcpy(vtbl_rec->name, re->new_name, re->new_name_len);
+ memset(vtbl_rec->name + re->new_name_len, 0,
+ UBI_VOL_NAME_MAX + 1 - re->new_name_len);
+ crc = crc32(UBI_CRC32_INIT, vtbl_rec,
+ UBI_VTBL_RECORD_SIZE_CRC);
+ vtbl_rec->crc = cpu_to_be32(crc);
+ }
+
+ layout_vol = ubi->volumes[vol_id2idx(ubi, UBI_LAYOUT_VOLUME_ID)];
+ for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) {
+ err = ubi_eba_unmap_leb(ubi, layout_vol, i);
+ if (err)
+ return err;
+
+ err = ubi_eba_write_leb(ubi, layout_vol, i, ubi->vtbl, 0,
+ ubi->vtbl_size, UBI_LONGTERM);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * vtbl_check - check if volume table is not corrupted and sensible.
* @ubi: UBI device description object
* @vtbl: volume table
*
@@ -127,7 +177,7 @@ static int vtbl_check(const struct ubi_device *ubi,
const struct ubi_vtbl_record *vtbl)
{
int i, n, reserved_pebs, alignment, data_pad, vol_type, name_len;
- int upd_marker;
+ int upd_marker, err;
uint32_t crc;
const char *name;
@@ -153,7 +203,7 @@ static int vtbl_check(const struct ubi_device *ubi,
if (reserved_pebs == 0) {
if (memcmp(&vtbl[i], &empty_vtbl_record,
UBI_VTBL_RECORD_SIZE)) {
- dbg_err("bad empty record");
+ err = 2;
goto bad;
}
continue;
@@ -161,56 +211,57 @@ static int vtbl_check(const struct ubi_device *ubi,
if (reserved_pebs < 0 || alignment < 0 || data_pad < 0 ||
name_len < 0) {
- dbg_err("negative values");
+ err = 3;
goto bad;
}
if (alignment > ubi->leb_size || alignment == 0) {
- dbg_err("bad alignment");
+ err = 4;
goto bad;
}
- n = alignment % ubi->min_io_size;
+ n = alignment & (ubi->min_io_size - 1);
if (alignment != 1 && n) {
- dbg_err("alignment is not multiple of min I/O unit");
+ err = 5;
goto bad;
}
n = ubi->leb_size % alignment;
if (data_pad != n) {
dbg_err("bad data_pad, has to be %d", n);
+ err = 6;
goto bad;
}
if (vol_type != UBI_VID_DYNAMIC && vol_type != UBI_VID_STATIC) {
- dbg_err("bad vol_type");
+ err = 7;
goto bad;
}
if (upd_marker != 0 && upd_marker != 1) {
- dbg_err("bad upd_marker");
+ err = 8;
goto bad;
}
if (reserved_pebs > ubi->good_peb_count) {
dbg_err("too large reserved_pebs, good PEBs %d",
ubi->good_peb_count);
+ err = 9;
goto bad;
}
if (name_len > UBI_VOL_NAME_MAX) {
- dbg_err("too long volume name, max %d",
- UBI_VOL_NAME_MAX);
+ err = 10;
goto bad;
}
if (name[0] == '\0') {
- dbg_err("NULL volume name");
+ err = 11;
goto bad;
}
if (name_len != strnlen(name, name_len + 1)) {
- dbg_err("bad name_len");
+ err = 12;
goto bad;
}
}
@@ -235,7 +286,7 @@ static int vtbl_check(const struct ubi_device *ubi,
return 0;
bad:
- ubi_err("volume table check failed, record %d", i);
+ ubi_err("volume table check failed: record %d, error %d", i, err);
ubi_dbg_dump_vtbl_record(&vtbl[i], i);
return -EINVAL;
}
@@ -287,7 +338,6 @@ retry:
vid_hdr->data_pad = cpu_to_be32(0);
vid_hdr->lnum = cpu_to_be32(copy);
vid_hdr->sqnum = cpu_to_be64(++si->max_sqnum);
- vid_hdr->leb_ver = cpu_to_be32(old_seb ? old_seb->leb_ver + 1: 0);
/* The EC header is already there, write the VID header */
err = ubi_io_write_vid_hdr(ubi, new_seb->pnum, vid_hdr);
@@ -370,7 +420,7 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi,
* to LEB 0.
*/
- dbg_msg("check layout volume");
+ dbg_gen("check layout volume");
/* Read both LEB 0 and LEB 1 into memory */
ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) {
@@ -384,7 +434,16 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi,
err = ubi_io_read_data(ubi, leb[seb->lnum], seb->pnum, 0,
ubi->vtbl_size);
if (err == UBI_IO_BITFLIPS || err == -EBADMSG)
- /* Scrub the PEB later */
+ /*
+ * Scrub the PEB later. Note, -EBADMSG indicates an
+ * uncorrectable ECC error, but we have our own CRC and
+ * the data will be checked later. If the data is OK,
+ * the PEB will be scrubbed (because we set
+ * seb->scrub). If the data is not OK, the contents of
+ * the PEB will be recovered from the second copy, and
+ * seb->scrub will be cleared in
+ * 'ubi_scan_add_used()'.
+ */
seb->scrub = 1;
else if (err)
goto out_free;
@@ -400,7 +459,8 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi,
if (!leb_corrupted[0]) {
/* LEB 0 is OK */
if (leb[1])
- leb_corrupted[1] = memcmp(leb[0], leb[1], ubi->vtbl_size);
+ leb_corrupted[1] = memcmp(leb[0], leb[1],
+ ubi->vtbl_size);
if (leb_corrupted[1]) {
ubi_warn("volume table copy #2 is corrupted");
err = create_vtbl(ubi, si, 1, leb[0]);
@@ -620,30 +680,32 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
static int check_sv(const struct ubi_volume *vol,
const struct ubi_scan_volume *sv)
{
+ int err;
+
if (sv->highest_lnum >= vol->reserved_pebs) {
- dbg_err("bad highest_lnum");
+ err = 1;
goto bad;
}
if (sv->leb_count > vol->reserved_pebs) {
- dbg_err("bad leb_count");
+ err = 2;
goto bad;
}
if (sv->vol_type != vol->vol_type) {
- dbg_err("bad vol_type");
+ err = 3;
goto bad;
}
if (sv->used_ebs > vol->reserved_pebs) {
- dbg_err("bad used_ebs");
+ err = 4;
goto bad;
}
if (sv->data_pad != vol->data_pad) {
- dbg_err("bad data_pad");
+ err = 5;
goto bad;
}
return 0;
bad:
- ubi_err("bad scanning information");
+ ubi_err("bad scanning information, error %d", err);
ubi_dbg_dump_sv(sv);
ubi_dbg_dump_vol_info(vol);
return -EINVAL;
@@ -672,14 +734,13 @@ static int check_scanning_info(const struct ubi_device *ubi,
return -EINVAL;
}
- if (si->highest_vol_id >= ubi->vtbl_slots + UBI_INT_VOL_COUNT&&
+ if (si->highest_vol_id >= ubi->vtbl_slots + UBI_INT_VOL_COUNT &&
si->highest_vol_id < UBI_INTERNAL_VOL_START) {
ubi_err("too large volume ID %d found by scanning",
si->highest_vol_id);
return -EINVAL;
}
-
for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) {
cond_resched();
@@ -717,8 +778,7 @@ static int check_scanning_info(const struct ubi_device *ubi,
}
/**
- * ubi_read_volume_table - read volume table.
- * information.
+ * ubi_read_volume_table - read the volume table.
* @ubi: UBI device description object
* @si: scanning information
*
@@ -797,11 +857,10 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si)
out_free:
vfree(ubi->vtbl);
- for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++)
- if (ubi->volumes[i]) {
- kfree(ubi->volumes[i]);
- ubi->volumes[i] = NULL;
- }
+ for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) {
+ kfree(ubi->volumes[i]);
+ ubi->volumes[i] = NULL;
+ }
return err;
}
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index a471a491f0a..05d70937b54 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -19,22 +19,22 @@
*/
/*
- * UBI wear-leveling unit.
+ * UBI wear-leveling sub-system.
*
- * This unit is responsible for wear-leveling. It works in terms of physical
- * eraseblocks and erase counters and knows nothing about logical eraseblocks,
- * volumes, etc. From this unit's perspective all physical eraseblocks are of
- * two types - used and free. Used physical eraseblocks are those that were
- * "get" by the 'ubi_wl_get_peb()' function, and free physical eraseblocks are
- * those that were put by the 'ubi_wl_put_peb()' function.
+ * This sub-system is responsible for wear-leveling. It works in terms of
+ * physical* eraseblocks and erase counters and knows nothing about logical
+ * eraseblocks, volumes, etc. From this sub-system's perspective all physical
+ * eraseblocks are of two types - used and free. Used physical eraseblocks are
+ * those that were "get" by the 'ubi_wl_get_peb()' function, and free physical
+ * eraseblocks are those that were put by the 'ubi_wl_put_peb()' function.
*
* Physical eraseblocks returned by 'ubi_wl_get_peb()' have only erase counter
- * header. The rest of the physical eraseblock contains only 0xFF bytes.
+ * header. The rest of the physical eraseblock contains only %0xFF bytes.
*
- * When physical eraseblocks are returned to the WL unit by means of the
+ * When physical eraseblocks are returned to the WL sub-system by means of the
* 'ubi_wl_put_peb()' function, they are scheduled for erasure. The erasure is
* done asynchronously in context of the per-UBI device background thread,
- * which is also managed by the WL unit.
+ * which is also managed by the WL sub-system.
*
* The wear-leveling is ensured by means of moving the contents of used
* physical eraseblocks with low erase counter to free physical eraseblocks
@@ -43,34 +43,36 @@
* The 'ubi_wl_get_peb()' function accepts data type hints which help to pick
* an "optimal" physical eraseblock. For example, when it is known that the
* physical eraseblock will be "put" soon because it contains short-term data,
- * the WL unit may pick a free physical eraseblock with low erase counter, and
- * so forth.
+ * the WL sub-system may pick a free physical eraseblock with low erase
+ * counter, and so forth.
*
- * If the WL unit fails to erase a physical eraseblock, it marks it as bad.
+ * If the WL sub-system fails to erase a physical eraseblock, it marks it as
+ * bad.
*
- * This unit is also responsible for scrubbing. If a bit-flip is detected in a
- * physical eraseblock, it has to be moved. Technically this is the same as
- * moving it for wear-leveling reasons.
+ * This sub-system is also responsible for scrubbing. If a bit-flip is detected
+ * in a physical eraseblock, it has to be moved. Technically this is the same
+ * as moving it for wear-leveling reasons.
*
- * As it was said, for the UBI unit all physical eraseblocks are either "free"
- * or "used". Free eraseblock are kept in the @wl->free RB-tree, while used
- * eraseblocks are kept in a set of different RB-trees: @wl->used,
+ * As it was said, for the UBI sub-system all physical eraseblocks are either
+ * "free" or "used". Free eraseblock are kept in the @wl->free RB-tree, while
+ * used eraseblocks are kept in a set of different RB-trees: @wl->used,
* @wl->prot.pnum, @wl->prot.aec, and @wl->scrub.
*
* Note, in this implementation, we keep a small in-RAM object for each physical
* eraseblock. This is surely not a scalable solution. But it appears to be good
* enough for moderately large flashes and it is simple. In future, one may
- * re-work this unit and make it more scalable.
+ * re-work this sub-system and make it more scalable.
*
- * At the moment this unit does not utilize the sequence number, which was
- * introduced relatively recently. But it would be wise to do this because the
- * sequence number of a logical eraseblock characterizes how old is it. For
+ * At the moment this sub-system does not utilize the sequence number, which
+ * was introduced relatively recently. But it would be wise to do this because
+ * the sequence number of a logical eraseblock characterizes how old is it. For
* example, when we move a PEB with low erase counter, and we need to pick the
* target PEB, we pick a PEB with the highest EC if our PEB is "old" and we
* pick target PEB with an average EC if our PEB is not very "old". This is a
- * room for future re-works of the WL unit.
+ * room for future re-works of the WL sub-system.
*
- * FIXME: looks too complex, should be simplified (later).
+ * Note: the stuff with protection trees looks too complex and is difficult to
+ * understand. Should be fixed.
*/
#include <linux/slab.h>
@@ -92,20 +94,21 @@
/*
* Maximum difference between two erase counters. If this threshold is
- * exceeded, the WL unit starts moving data from used physical eraseblocks with
- * low erase counter to free physical eraseblocks with high erase counter.
+ * exceeded, the WL sub-system starts moving data from used physical
+ * eraseblocks with low erase counter to free physical eraseblocks with high
+ * erase counter.
*/
#define UBI_WL_THRESHOLD CONFIG_MTD_UBI_WL_THRESHOLD
/*
- * When a physical eraseblock is moved, the WL unit has to pick the target
+ * When a physical eraseblock is moved, the WL sub-system has to pick the target
* physical eraseblock to move to. The simplest way would be just to pick the
* one with the highest erase counter. But in certain workloads this could lead
* to an unlimited wear of one or few physical eraseblock. Indeed, imagine a
* situation when the picked physical eraseblock is constantly erased after the
* data is written to it. So, we have a constant which limits the highest erase
- * counter of the free physical eraseblock to pick. Namely, the WL unit does
- * not pick eraseblocks with erase counter greater then the lowest erase
+ * counter of the free physical eraseblock to pick. Namely, the WL sub-system
+ * does not pick eraseblocks with erase counter greater then the lowest erase
* counter plus %WL_FREE_MAX_DIFF.
*/
#define WL_FREE_MAX_DIFF (2*UBI_WL_THRESHOLD)
@@ -123,11 +126,11 @@
* @abs_ec: the absolute erase counter value when the protection ends
* @e: the wear-leveling entry of the physical eraseblock under protection
*
- * When the WL unit returns a physical eraseblock, the physical eraseblock is
- * protected from being moved for some "time". For this reason, the physical
- * eraseblock is not directly moved from the @wl->free tree to the @wl->used
- * tree. There is one more tree in between where this physical eraseblock is
- * temporarily stored (@wl->prot).
+ * When the WL sub-system returns a physical eraseblock, the physical
+ * eraseblock is protected from being moved for some "time". For this reason,
+ * the physical eraseblock is not directly moved from the @wl->free tree to the
+ * @wl->used tree. There is one more tree in between where this physical
+ * eraseblock is temporarily stored (@wl->prot).
*
* All this protection stuff is needed because:
* o we don't want to move physical eraseblocks just after we have given them
@@ -175,7 +178,6 @@ struct ubi_wl_prot_entry {
* @list: a link in the list of pending works
* @func: worker function
* @priv: private data of the worker function
- *
* @e: physical eraseblock to erase
* @torture: if the physical eraseblock has to be tortured
*
@@ -473,52 +475,47 @@ retry:
}
switch (dtype) {
- case UBI_LONGTERM:
- /*
- * For long term data we pick a physical eraseblock
- * with high erase counter. But the highest erase
- * counter we can pick is bounded by the the lowest
- * erase counter plus %WL_FREE_MAX_DIFF.
- */
- e = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF);
- protect = LT_PROTECTION;
- break;
- case UBI_UNKNOWN:
- /*
- * For unknown data we pick a physical eraseblock with
- * medium erase counter. But we by no means can pick a
- * physical eraseblock with erase counter greater or
- * equivalent than the lowest erase counter plus
- * %WL_FREE_MAX_DIFF.
- */
- first = rb_entry(rb_first(&ubi->free),
- struct ubi_wl_entry, rb);
- last = rb_entry(rb_last(&ubi->free),
- struct ubi_wl_entry, rb);
+ case UBI_LONGTERM:
+ /*
+ * For long term data we pick a physical eraseblock with high
+ * erase counter. But the highest erase counter we can pick is
+ * bounded by the the lowest erase counter plus
+ * %WL_FREE_MAX_DIFF.
+ */
+ e = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF);
+ protect = LT_PROTECTION;
+ break;
+ case UBI_UNKNOWN:
+ /*
+ * For unknown data we pick a physical eraseblock with medium
+ * erase counter. But we by no means can pick a physical
+ * eraseblock with erase counter greater or equivalent than the
+ * lowest erase counter plus %WL_FREE_MAX_DIFF.
+ */
+ first = rb_entry(rb_first(&ubi->free), struct ubi_wl_entry, rb);
+ last = rb_entry(rb_last(&ubi->free), struct ubi_wl_entry, rb);
- if (last->ec - first->ec < WL_FREE_MAX_DIFF)
- e = rb_entry(ubi->free.rb_node,
- struct ubi_wl_entry, rb);
- else {
- medium_ec = (first->ec + WL_FREE_MAX_DIFF)/2;
- e = find_wl_entry(&ubi->free, medium_ec);
- }
- protect = U_PROTECTION;
- break;
- case UBI_SHORTTERM:
- /*
- * For short term data we pick a physical eraseblock
- * with the lowest erase counter as we expect it will
- * be erased soon.
- */
- e = rb_entry(rb_first(&ubi->free),
- struct ubi_wl_entry, rb);
- protect = ST_PROTECTION;
- break;
- default:
- protect = 0;
- e = NULL;
- BUG();
+ if (last->ec - first->ec < WL_FREE_MAX_DIFF)
+ e = rb_entry(ubi->free.rb_node,
+ struct ubi_wl_entry, rb);
+ else {
+ medium_ec = (first->ec + WL_FREE_MAX_DIFF)/2;
+ e = find_wl_entry(&ubi->free, medium_ec);
+ }
+ protect = U_PROTECTION;
+ break;
+ case UBI_SHORTTERM:
+ /*
+ * For short term data we pick a physical eraseblock with the
+ * lowest erase counter as we expect it will be erased soon.
+ */
+ e = rb_entry(rb_first(&ubi->free), struct ubi_wl_entry, rb);
+ protect = ST_PROTECTION;
+ break;
+ default:
+ protect = 0;
+ e = NULL;
+ BUG();
}
/*
@@ -582,7 +579,8 @@ found:
* This function returns zero in case of success and a negative error code in
* case of failure.
*/
-static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, int torture)
+static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
+ int torture)
{
int err;
struct ubi_ec_hdr *ec_hdr;
@@ -634,8 +632,7 @@ out_free:
}
/**
- * check_protection_over - check if it is time to stop protecting some
- * physical eraseblocks.
+ * check_protection_over - check if it is time to stop protecting some PEBs.
* @ubi: UBI device description object
*
* This function is called after each erase operation, when the absolute erase
@@ -871,6 +868,10 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
}
ubi_free_vid_hdr(ubi, vid_hdr);
+ if (scrubbing && !protect)
+ ubi_msg("scrubbed PEB %d, data moved to PEB %d",
+ e1->pnum, e2->pnum);
+
spin_lock(&ubi->wl_lock);
if (protect)
prot_tree_add(ubi, e1, pe, protect);
@@ -1054,8 +1055,8 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
spin_unlock(&ubi->wl_lock);
/*
- * One more erase operation has happened, take care about protected
- * physical eraseblocks.
+ * One more erase operation has happened, take care about
+ * protected physical eraseblocks.
*/
check_protection_over(ubi);
@@ -1136,7 +1137,7 @@ out_ro:
}
/**
- * ubi_wl_put_peb - return a physical eraseblock to the wear-leveling unit.
+ * ubi_wl_put_peb - return a PEB to the wear-leveling sub-system.
* @ubi: UBI device description object
* @pnum: physical eraseblock to return
* @torture: if this physical eraseblock has to be tortured
@@ -1175,11 +1176,11 @@ retry:
/*
* User is putting the physical eraseblock which was selected
* as the target the data is moved to. It may happen if the EBA
- * unit already re-mapped the LEB in 'ubi_eba_copy_leb()' but
- * the WL unit has not put the PEB to the "used" tree yet, but
- * it is about to do this. So we just set a flag which will
- * tell the WL worker that the PEB is not needed anymore and
- * should be scheduled for erasure.
+ * sub-system already re-mapped the LEB in 'ubi_eba_copy_leb()'
+ * but the WL sub-system has not put the PEB to the "used" tree
+ * yet, but it is about to do this. So we just set a flag which
+ * will tell the WL worker that the PEB is not needed anymore
+ * and should be scheduled for erasure.
*/
dbg_wl("PEB %d is the target of data moving", pnum);
ubi_assert(!ubi->move_to_put);
@@ -1229,7 +1230,7 @@ int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum)
{
struct ubi_wl_entry *e;
- ubi_msg("schedule PEB %d for scrubbing", pnum);
+ dbg_msg("schedule PEB %d for scrubbing", pnum);
retry:
spin_lock(&ubi->wl_lock);
@@ -1368,7 +1369,7 @@ int ubi_thread(void *u)
int err;
if (kthread_should_stop())
- goto out;
+ break;
if (try_to_freeze())
continue;
@@ -1403,7 +1404,6 @@ int ubi_thread(void *u)
cond_resched();
}
-out:
dbg_wl("background thread \"%s\" is killed", ubi->bgt_name);
return 0;
}
@@ -1426,8 +1426,7 @@ static void cancel_pending(struct ubi_device *ubi)
}
/**
- * ubi_wl_init_scan - initialize the wear-leveling unit using scanning
- * information.
+ * ubi_wl_init_scan - initialize the WL sub-system using scanning information.
* @ubi: UBI device description object
* @si: scanning information
*
@@ -1584,13 +1583,12 @@ static void protection_trees_destroy(struct ubi_device *ubi)
}
/**
- * ubi_wl_close - close the wear-leveling unit.
+ * ubi_wl_close - close the wear-leveling sub-system.
* @ubi: UBI device description object
*/
void ubi_wl_close(struct ubi_device *ubi)
{
- dbg_wl("close the UBI wear-leveling unit");
-
+ dbg_wl("close the WL sub-system");
cancel_pending(ubi);
protection_trees_destroy(ubi);
tree_destroy(&ubi->used);
@@ -1602,8 +1600,7 @@ void ubi_wl_close(struct ubi_device *ubi)
#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID
/**
- * paranoid_check_ec - make sure that the erase counter of a physical eraseblock
- * is correct.
+ * paranoid_check_ec - make sure that the erase counter of a PEB is correct.
* @ubi: UBI device description object
* @pnum: the physical eraseblock number to check
* @ec: the erase counter to check
@@ -1644,13 +1641,12 @@ out_free:
}
/**
- * paranoid_check_in_wl_tree - make sure that a wear-leveling entry is present
- * in a WL RB-tree.
+ * paranoid_check_in_wl_tree - check that wear-leveling entry is in WL RB-tree.
* @e: the wear-leveling entry to check
* @root: the root of the tree
*
- * This function returns zero if @e is in the @root RB-tree and %1 if it
- * is not.
+ * This function returns zero if @e is in the @root RB-tree and %1 if it is
+ * not.
*/
static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e,
struct rb_root *root)
diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c
index 869544b8c05..9c0f56b3c51 100644
--- a/drivers/net/e1000e/netdev.c
+++ b/drivers/net/e1000e/netdev.c
@@ -4067,8 +4067,6 @@ static void e1000_netpoll(struct net_device *netdev)
disable_irq(adapter->pdev->irq);
e1000_intr(adapter->pdev->irq, netdev);
- e1000_clean_tx_irq(adapter);
-
enable_irq(adapter->pdev->irq);
}
#endif
diff --git a/drivers/net/fec.c b/drivers/net/fec.c
index 32a4f17d35f..ecd5c71a7a8 100644
--- a/drivers/net/fec.c
+++ b/drivers/net/fec.c
@@ -2,12 +2,6 @@
* Fast Ethernet Controller (FEC) driver for Motorola MPC8xx.
* Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
*
- * This version of the driver is specific to the FADS implementation,
- * since the board contains control registers external to the processor
- * for the control of the LevelOne LXT970 transceiver. The MPC860T manual
- * describes connections using the internal parallel port I/O, which
- * is basically all of Port D.
- *
* Right now, I am very wasteful with the buffers. I allocate memory
* pages and then divide them into 2K frame buffers. This way I know I
* have buffers large enough to hold one frame within one buffer descriptor.
@@ -49,17 +43,9 @@
#include <asm/pgtable.h>
#include <asm/cacheflush.h>
-#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || \
- defined(CONFIG_M5272) || defined(CONFIG_M528x) || \
- defined(CONFIG_M520x) || defined(CONFIG_M532x)
#include <asm/coldfire.h>
#include <asm/mcfsim.h>
#include "fec.h"
-#else
-#include <asm/8xx_immap.h>
-#include <asm/mpc8xx.h>
-#include "commproc.h"
-#endif
#if defined(CONFIG_FEC2)
#define FEC_MAX_PORTS 2
@@ -67,7 +53,7 @@
#define FEC_MAX_PORTS 1
#endif
-#if defined(CONFIG_FADS) || defined(CONFIG_RPXCLASSIC) || defined(CONFIG_M5272)
+#if defined(CONFIG_M5272)
#define HAVE_mii_link_interrupt
#endif
@@ -1235,14 +1221,9 @@ static phy_info_t const * const phy_info[] = {
/* ------------------------------------------------------------------------- */
#ifdef HAVE_mii_link_interrupt
-#ifdef CONFIG_RPXCLASSIC
-static void
-mii_link_interrupt(void *dev_id);
-#else
static irqreturn_t
mii_link_interrupt(int irq, void * dev_id);
#endif
-#endif
#if defined(CONFIG_M5272)
/*
@@ -1795,24 +1776,6 @@ static void __inline__ fec_request_intrs(struct net_device *dev)
if (request_8xxirq(FEC_INTERRUPT, fec_enet_interrupt, 0, "fec", dev) != 0)
panic("Could not allocate FEC IRQ!");
-
-#ifdef CONFIG_RPXCLASSIC
- /* Make Port C, bit 15 an input that causes interrupts.
- */
- immap->im_ioport.iop_pcpar &= ~0x0001;
- immap->im_ioport.iop_pcdir &= ~0x0001;
- immap->im_ioport.iop_pcso &= ~0x0001;
- immap->im_ioport.iop_pcint |= 0x0001;
- cpm_install_handler(CPMVEC_PIO_PC15, mii_link_interrupt, dev);
-
- /* Make LEDS reflect Link status.
- */
- *((uint *) RPX_CSR_ADDR) &= ~BCSR2_FETHLEDMODE;
-#endif
-#ifdef CONFIG_FADS
- if (request_8xxirq(SIU_IRQ2, mii_link_interrupt, 0, "mii", dev) != 0)
- panic("Could not allocate MII IRQ!");
-#endif
}
static void __inline__ fec_get_mac(struct net_device *dev)
@@ -1821,16 +1784,6 @@ static void __inline__ fec_get_mac(struct net_device *dev)
bd = (bd_t *)__res;
memcpy(dev->dev_addr, bd->bi_enetaddr, ETH_ALEN);
-
-#ifdef CONFIG_RPXCLASSIC
- /* The Embedded Planet boards have only one MAC address in
- * the EEPROM, but can have two Ethernet ports. For the
- * FEC port, we create another address by setting one of
- * the address bits above something that would have (up to
- * now) been allocated.
- */
- dev->dev_adrd[3] |= 0x80;
-#endif
}
static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_private *fep)
@@ -2109,13 +2062,8 @@ mii_discover_phy(uint mii_reg, struct net_device *dev)
/* This interrupt occurs when the PHY detects a link change.
*/
#ifdef HAVE_mii_link_interrupt
-#ifdef CONFIG_RPXCLASSIC
-static void
-mii_link_interrupt(void *dev_id)
-#else
static irqreturn_t
mii_link_interrupt(int irq, void * dev_id)
-#endif
{
struct net_device *dev = dev_id;
struct fec_enet_private *fep = netdev_priv(dev);
diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c
index 00527805e4f..e5a6e2e8454 100644
--- a/drivers/net/ibmveth.c
+++ b/drivers/net/ibmveth.c
@@ -33,6 +33,7 @@
*/
#include <linux/module.h>
+#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/ioport.h>
@@ -52,7 +53,9 @@
#include <asm/hvcall.h>
#include <asm/atomic.h>
#include <asm/vio.h>
+#include <asm/iommu.h>
#include <asm/uaccess.h>
+#include <asm/firmware.h>
#include <linux/seq_file.h>
#include "ibmveth.h"
@@ -94,8 +97,10 @@ static void ibmveth_proc_register_adapter(struct ibmveth_adapter *adapter);
static void ibmveth_proc_unregister_adapter(struct ibmveth_adapter *adapter);
static irqreturn_t ibmveth_interrupt(int irq, void *dev_instance);
static void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter);
+static unsigned long ibmveth_get_desired_dma(struct vio_dev *vdev);
static struct kobj_type ktype_veth_pool;
+
#ifdef CONFIG_PROC_FS
#define IBMVETH_PROC_DIR "ibmveth"
static struct proc_dir_entry *ibmveth_proc_dir;
@@ -226,16 +231,16 @@ static void ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter, struc
u32 i;
u32 count = pool->size - atomic_read(&pool->available);
u32 buffers_added = 0;
+ struct sk_buff *skb;
+ unsigned int free_index, index;
+ u64 correlator;
+ unsigned long lpar_rc;
+ dma_addr_t dma_addr;
mb();
for(i = 0; i < count; ++i) {
- struct sk_buff *skb;
- unsigned int free_index, index;
- u64 correlator;
union ibmveth_buf_desc desc;
- unsigned long lpar_rc;
- dma_addr_t dma_addr;
skb = alloc_skb(pool->buff_size, GFP_ATOMIC);
@@ -255,6 +260,9 @@ static void ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter, struc
dma_addr = dma_map_single(&adapter->vdev->dev, skb->data,
pool->buff_size, DMA_FROM_DEVICE);
+ if (dma_mapping_error(dma_addr))
+ goto failure;
+
pool->free_map[free_index] = IBM_VETH_INVALID_MAP;
pool->dma_addr[index] = dma_addr;
pool->skbuff[index] = skb;
@@ -267,20 +275,9 @@ static void ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter, struc
lpar_rc = h_add_logical_lan_buffer(adapter->vdev->unit_address, desc.desc);
- if(lpar_rc != H_SUCCESS) {
- pool->free_map[free_index] = index;
- pool->skbuff[index] = NULL;
- if (pool->consumer_index == 0)
- pool->consumer_index = pool->size - 1;
- else
- pool->consumer_index--;
- dma_unmap_single(&adapter->vdev->dev,
- pool->dma_addr[index], pool->buff_size,
- DMA_FROM_DEVICE);
- dev_kfree_skb_any(skb);
- adapter->replenish_add_buff_failure++;
- break;
- } else {
+ if (lpar_rc != H_SUCCESS)
+ goto failure;
+ else {
buffers_added++;
adapter->replenish_add_buff_success++;
}
@@ -288,6 +285,24 @@ static void ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter, struc
mb();
atomic_add(buffers_added, &(pool->available));
+ return;
+
+failure:
+ pool->free_map[free_index] = index;
+ pool->skbuff[index] = NULL;
+ if (pool->consumer_index == 0)
+ pool->consumer_index = pool->size - 1;
+ else
+ pool->consumer_index--;
+ if (!dma_mapping_error(dma_addr))
+ dma_unmap_single(&adapter->vdev->dev,
+ pool->dma_addr[index], pool->buff_size,
+ DMA_FROM_DEVICE);
+ dev_kfree_skb_any(skb);
+ adapter->replenish_add_buff_failure++;
+
+ mb();
+ atomic_add(buffers_added, &(pool->available));
}
/* replenish routine */
@@ -297,7 +312,7 @@ static void ibmveth_replenish_task(struct ibmveth_adapter *adapter)
adapter->replenish_task_cycles++;
- for(i = 0; i < IbmVethNumBufferPools; i++)
+ for (i = (IbmVethNumBufferPools - 1); i >= 0; i--)
if(adapter->rx_buff_pool[i].active)
ibmveth_replenish_buffer_pool(adapter,
&adapter->rx_buff_pool[i]);
@@ -472,6 +487,18 @@ static void ibmveth_cleanup(struct ibmveth_adapter *adapter)
if (adapter->rx_buff_pool[i].active)
ibmveth_free_buffer_pool(adapter,
&adapter->rx_buff_pool[i]);
+
+ if (adapter->bounce_buffer != NULL) {
+ if (!dma_mapping_error(adapter->bounce_buffer_dma)) {
+ dma_unmap_single(&adapter->vdev->dev,
+ adapter->bounce_buffer_dma,
+ adapter->netdev->mtu + IBMVETH_BUFF_OH,
+ DMA_BIDIRECTIONAL);
+ adapter->bounce_buffer_dma = DMA_ERROR_CODE;
+ }
+ kfree(adapter->bounce_buffer);
+ adapter->bounce_buffer = NULL;
+ }
}
static int ibmveth_register_logical_lan(struct ibmveth_adapter *adapter,
@@ -607,6 +634,24 @@ static int ibmveth_open(struct net_device *netdev)
return rc;
}
+ adapter->bounce_buffer =
+ kmalloc(netdev->mtu + IBMVETH_BUFF_OH, GFP_KERNEL);
+ if (!adapter->bounce_buffer) {
+ ibmveth_error_printk("unable to allocate bounce buffer\n");
+ ibmveth_cleanup(adapter);
+ napi_disable(&adapter->napi);
+ return -ENOMEM;
+ }
+ adapter->bounce_buffer_dma =
+ dma_map_single(&adapter->vdev->dev, adapter->bounce_buffer,
+ netdev->mtu + IBMVETH_BUFF_OH, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(adapter->bounce_buffer_dma)) {
+ ibmveth_error_printk("unable to map bounce buffer\n");
+ ibmveth_cleanup(adapter);
+ napi_disable(&adapter->napi);
+ return -ENOMEM;
+ }
+
ibmveth_debug_printk("initial replenish cycle\n");
ibmveth_interrupt(netdev->irq, netdev);
@@ -853,10 +898,12 @@ static int ibmveth_start_xmit(struct sk_buff *skb, struct net_device *netdev)
unsigned int tx_packets = 0;
unsigned int tx_send_failed = 0;
unsigned int tx_map_failed = 0;
+ int used_bounce = 0;
+ unsigned long data_dma_addr;
desc.fields.flags_len = IBMVETH_BUF_VALID | skb->len;
- desc.fields.address = dma_map_single(&adapter->vdev->dev, skb->data,
- skb->len, DMA_TO_DEVICE);
+ data_dma_addr = dma_map_single(&adapter->vdev->dev, skb->data,
+ skb->len, DMA_TO_DEVICE);
if (skb->ip_summed == CHECKSUM_PARTIAL &&
ip_hdr(skb)->protocol != IPPROTO_TCP && skb_checksum_help(skb)) {
@@ -875,12 +922,16 @@ static int ibmveth_start_xmit(struct sk_buff *skb, struct net_device *netdev)
buf[1] = 0;
}
- if (dma_mapping_error(desc.fields.address)) {
- ibmveth_error_printk("tx: unable to map xmit buffer\n");
+ if (dma_mapping_error(data_dma_addr)) {
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ ibmveth_error_printk("tx: unable to map xmit buffer\n");
+ skb_copy_from_linear_data(skb, adapter->bounce_buffer,
+ skb->len);
+ desc.fields.address = adapter->bounce_buffer_dma;
tx_map_failed++;
- tx_dropped++;
- goto out;
- }
+ used_bounce = 1;
+ } else
+ desc.fields.address = data_dma_addr;
/* send the frame. Arbitrarily set retrycount to 1024 */
correlator = 0;
@@ -904,8 +955,9 @@ static int ibmveth_start_xmit(struct sk_buff *skb, struct net_device *netdev)
netdev->trans_start = jiffies;
}
- dma_unmap_single(&adapter->vdev->dev, desc.fields.address,
- skb->len, DMA_TO_DEVICE);
+ if (!used_bounce)
+ dma_unmap_single(&adapter->vdev->dev, data_dma_addr,
+ skb->len, DMA_TO_DEVICE);
out: spin_lock_irqsave(&adapter->stats_lock, flags);
netdev->stats.tx_dropped += tx_dropped;
@@ -1053,9 +1105,9 @@ static void ibmveth_set_multicast_list(struct net_device *netdev)
static int ibmveth_change_mtu(struct net_device *dev, int new_mtu)
{
struct ibmveth_adapter *adapter = dev->priv;
+ struct vio_dev *viodev = adapter->vdev;
int new_mtu_oh = new_mtu + IBMVETH_BUFF_OH;
- int reinit = 0;
- int i, rc;
+ int i;
if (new_mtu < IBMVETH_MAX_MTU)
return -EINVAL;
@@ -1067,23 +1119,34 @@ static int ibmveth_change_mtu(struct net_device *dev, int new_mtu)
if (i == IbmVethNumBufferPools)
return -EINVAL;
+ /* Deactivate all the buffer pools so that the next loop can activate
+ only the buffer pools necessary to hold the new MTU */
+ for (i = 0; i < IbmVethNumBufferPools; i++)
+ if (adapter->rx_buff_pool[i].active) {
+ ibmveth_free_buffer_pool(adapter,
+ &adapter->rx_buff_pool[i]);
+ adapter->rx_buff_pool[i].active = 0;
+ }
+
/* Look for an active buffer pool that can hold the new MTU */
for(i = 0; i<IbmVethNumBufferPools; i++) {
- if (!adapter->rx_buff_pool[i].active) {
- adapter->rx_buff_pool[i].active = 1;
- reinit = 1;
- }
+ adapter->rx_buff_pool[i].active = 1;
if (new_mtu_oh < adapter->rx_buff_pool[i].buff_size) {
- if (reinit && netif_running(adapter->netdev)) {
+ if (netif_running(adapter->netdev)) {
adapter->pool_config = 1;
ibmveth_close(adapter->netdev);
adapter->pool_config = 0;
dev->mtu = new_mtu;
- if ((rc = ibmveth_open(adapter->netdev)))
- return rc;
- } else
- dev->mtu = new_mtu;
+ vio_cmo_set_dev_desired(viodev,
+ ibmveth_get_desired_dma
+ (viodev));
+ return ibmveth_open(adapter->netdev);
+ }
+ dev->mtu = new_mtu;
+ vio_cmo_set_dev_desired(viodev,
+ ibmveth_get_desired_dma
+ (viodev));
return 0;
}
}
@@ -1098,6 +1161,46 @@ static void ibmveth_poll_controller(struct net_device *dev)
}
#endif
+/**
+ * ibmveth_get_desired_dma - Calculate IO memory desired by the driver
+ *
+ * @vdev: struct vio_dev for the device whose desired IO mem is to be returned
+ *
+ * Return value:
+ * Number of bytes of IO data the driver will need to perform well.
+ */
+static unsigned long ibmveth_get_desired_dma(struct vio_dev *vdev)
+{
+ struct net_device *netdev = dev_get_drvdata(&vdev->dev);
+ struct ibmveth_adapter *adapter;
+ unsigned long ret;
+ int i;
+ int rxqentries = 1;
+
+ /* netdev inits at probe time along with the structures we need below*/
+ if (netdev == NULL)
+ return IOMMU_PAGE_ALIGN(IBMVETH_IO_ENTITLEMENT_DEFAULT);
+
+ adapter = netdev_priv(netdev);
+
+ ret = IBMVETH_BUFF_LIST_SIZE + IBMVETH_FILT_LIST_SIZE;
+ ret += IOMMU_PAGE_ALIGN(netdev->mtu);
+
+ for (i = 0; i < IbmVethNumBufferPools; i++) {
+ /* add the size of the active receive buffers */
+ if (adapter->rx_buff_pool[i].active)
+ ret +=
+ adapter->rx_buff_pool[i].size *
+ IOMMU_PAGE_ALIGN(adapter->rx_buff_pool[i].
+ buff_size);
+ rxqentries += adapter->rx_buff_pool[i].size;
+ }
+ /* add the size of the receive queue entries */
+ ret += IOMMU_PAGE_ALIGN(rxqentries * sizeof(struct ibmveth_rx_q_entry));
+
+ return ret;
+}
+
static int __devinit ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id)
{
int rc, i;
@@ -1242,6 +1345,8 @@ static int __devexit ibmveth_remove(struct vio_dev *dev)
ibmveth_proc_unregister_adapter(adapter);
free_netdev(netdev);
+ dev_set_drvdata(&dev->dev, NULL);
+
return 0;
}
@@ -1402,14 +1507,15 @@ const char * buf, size_t count)
return -EPERM;
}
- pool->active = 0;
if (netif_running(netdev)) {
adapter->pool_config = 1;
ibmveth_close(netdev);
+ pool->active = 0;
adapter->pool_config = 0;
if ((rc = ibmveth_open(netdev)))
return rc;
}
+ pool->active = 0;
}
} else if (attr == &veth_num_attr) {
if (value <= 0 || value > IBMVETH_MAX_POOL_COUNT)
@@ -1485,6 +1591,7 @@ static struct vio_driver ibmveth_driver = {
.id_table = ibmveth_device_table,
.probe = ibmveth_probe,
.remove = ibmveth_remove,
+ .get_desired_dma = ibmveth_get_desired_dma,
.driver = {
.name = ibmveth_driver_name,
.owner = THIS_MODULE,
diff --git a/drivers/net/ibmveth.h b/drivers/net/ibmveth.h
index 41f61cd1885..d2818694875 100644
--- a/drivers/net/ibmveth.h
+++ b/drivers/net/ibmveth.h
@@ -93,9 +93,12 @@ static inline long h_illan_attributes(unsigned long unit_address,
plpar_hcall_norets(H_CHANGE_LOGICAL_LAN_MAC, ua, mac)
#define IbmVethNumBufferPools 5
+#define IBMVETH_IO_ENTITLEMENT_DEFAULT 4243456 /* MTU of 1500 needs 4.2Mb */
#define IBMVETH_BUFF_OH 22 /* Overhead: 14 ethernet header + 8 opaque handle */
#define IBMVETH_MAX_MTU 68
#define IBMVETH_MAX_POOL_COUNT 4096
+#define IBMVETH_BUFF_LIST_SIZE 4096
+#define IBMVETH_FILT_LIST_SIZE 4096
#define IBMVETH_MAX_BUF_SIZE (1024 * 128)
static int pool_size[] = { 512, 1024 * 2, 1024 * 16, 1024 * 32, 1024 * 64 };
@@ -143,6 +146,8 @@ struct ibmveth_adapter {
struct ibmveth_rx_q rx_queue;
int pool_config;
int rx_csum;
+ void *bounce_buffer;
+ dma_addr_t bounce_buffer_dma;
/* adapter specific stats */
u64 replenish_task_cycles;
diff --git a/drivers/net/mlx4/cmd.c b/drivers/net/mlx4/cmd.c
index 70dff94a8bc..04d5bc69a6f 100644
--- a/drivers/net/mlx4/cmd.c
+++ b/drivers/net/mlx4/cmd.c
@@ -67,6 +67,8 @@ enum {
CMD_STAT_BAD_INDEX = 0x0a,
/* FW image corrupted: */
CMD_STAT_BAD_NVMEM = 0x0b,
+ /* Error in ICM mapping (e.g. not enough auxiliary ICM pages to execute command): */
+ CMD_STAT_ICM_ERROR = 0x0c,
/* Attempt to modify a QP/EE which is not in the presumed state: */
CMD_STAT_BAD_QP_STATE = 0x10,
/* Bad segment parameters (Address/Size): */
@@ -119,6 +121,7 @@ static int mlx4_status_to_errno(u8 status)
[CMD_STAT_BAD_RES_STATE] = -EBADF,
[CMD_STAT_BAD_INDEX] = -EBADF,
[CMD_STAT_BAD_NVMEM] = -EFAULT,
+ [CMD_STAT_ICM_ERROR] = -ENFILE,
[CMD_STAT_BAD_QP_STATE] = -EINVAL,
[CMD_STAT_BAD_SEG_PARAM] = -EFAULT,
[CMD_STAT_REG_BOUND] = -EBUSY,
diff --git a/drivers/net/mlx4/eq.c b/drivers/net/mlx4/eq.c
index e141a1513f0..ea3a09aaa84 100644
--- a/drivers/net/mlx4/eq.c
+++ b/drivers/net/mlx4/eq.c
@@ -33,6 +33,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/mlx4/cmd.h>
diff --git a/drivers/net/mlx4/fw.c b/drivers/net/mlx4/fw.c
index 2b5006b9be6..57278224ba1 100644
--- a/drivers/net/mlx4/fw.c
+++ b/drivers/net/mlx4/fw.c
@@ -46,6 +46,10 @@ enum {
extern void __buggy_use_of_MLX4_GET(void);
extern void __buggy_use_of_MLX4_PUT(void);
+static int enable_qos;
+module_param(enable_qos, bool, 0444);
+MODULE_PARM_DESC(enable_qos, "Enable Quality of Service support in the HCA (default: off)");
+
#define MLX4_GET(dest, source, offset) \
do { \
void *__p = (char *) (source) + (offset); \
@@ -198,7 +202,7 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
#define QUERY_DEV_CAP_C_MPT_ENTRY_SZ_OFFSET 0x8e
#define QUERY_DEV_CAP_MTT_ENTRY_SZ_OFFSET 0x90
#define QUERY_DEV_CAP_D_MPT_ENTRY_SZ_OFFSET 0x92
-#define QUERY_DEV_CAP_BMME_FLAGS_OFFSET 0x97
+#define QUERY_DEV_CAP_BMME_FLAGS_OFFSET 0x94
#define QUERY_DEV_CAP_RSVD_LKEY_OFFSET 0x98
#define QUERY_DEV_CAP_MAX_ICM_SZ_OFFSET 0xa0
@@ -373,12 +377,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
}
}
- if (dev_cap->bmme_flags & 1)
- mlx4_dbg(dev, "Base MM extensions: yes "
- "(flags %d, rsvd L_Key %08x)\n",
- dev_cap->bmme_flags, dev_cap->reserved_lkey);
- else
- mlx4_dbg(dev, "Base MM extensions: no\n");
+ mlx4_dbg(dev, "Base MM extensions: flags %08x, rsvd L_Key %08x\n",
+ dev_cap->bmme_flags, dev_cap->reserved_lkey);
/*
* Each UAR has 4 EQ doorbells; so if a UAR is reserved, then
@@ -737,6 +737,10 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param)
if (dev->caps.flags & MLX4_DEV_CAP_FLAG_IPOIB_CSUM)
*(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1 << 3);
+ /* Enable QoS support if module parameter set */
+ if (enable_qos)
+ *(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1 << 2);
+
/* QPC/EEC/CQC/EQC/RDMARC attributes */
MLX4_PUT(inbox, param->qpc_base, INIT_HCA_QPC_BASE_OFFSET);
diff --git a/drivers/net/mlx4/fw.h b/drivers/net/mlx4/fw.h
index a0e046c149b..fbf0e22be12 100644
--- a/drivers/net/mlx4/fw.h
+++ b/drivers/net/mlx4/fw.h
@@ -98,7 +98,7 @@ struct mlx4_dev_cap {
int cmpt_entry_sz;
int mtt_entry_sz;
int resize_srq;
- u8 bmme_flags;
+ u32 bmme_flags;
u32 reserved_lkey;
u64 max_icm_sz;
int max_gso_sz;
diff --git a/drivers/net/mlx4/main.c b/drivers/net/mlx4/main.c
index d3736013fe9..8e1d24cda1b 100644
--- a/drivers/net/mlx4/main.c
+++ b/drivers/net/mlx4/main.c
@@ -158,6 +158,8 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
dev->caps.max_msg_sz = dev_cap->max_msg_sz;
dev->caps.page_size_cap = ~(u32) (dev_cap->min_page_sz - 1);
dev->caps.flags = dev_cap->flags;
+ dev->caps.bmme_flags = dev_cap->bmme_flags;
+ dev->caps.reserved_lkey = dev_cap->reserved_lkey;
dev->caps.stat_rate_support = dev_cap->stat_rate_support;
dev->caps.max_gso_sz = dev_cap->max_gso_sz;
diff --git a/drivers/net/mlx4/mlx4.h b/drivers/net/mlx4/mlx4.h
index a4023c2dd05..78038499cff 100644
--- a/drivers/net/mlx4/mlx4.h
+++ b/drivers/net/mlx4/mlx4.h
@@ -118,6 +118,7 @@ struct mlx4_bitmap {
struct mlx4_buddy {
unsigned long **bits;
+ unsigned int *num_free;
int max_order;
spinlock_t lock;
};
diff --git a/drivers/net/mlx4/mr.c b/drivers/net/mlx4/mr.c
index 03a9abcce52..a3c04c5f12c 100644
--- a/drivers/net/mlx4/mr.c
+++ b/drivers/net/mlx4/mr.c
@@ -47,7 +47,7 @@ struct mlx4_mpt_entry {
__be32 flags;
__be32 qpn;
__be32 key;
- __be32 pd;
+ __be32 pd_flags;
__be64 start;
__be64 length;
__be32 lkey;
@@ -61,11 +61,15 @@ struct mlx4_mpt_entry {
} __attribute__((packed));
#define MLX4_MPT_FLAG_SW_OWNS (0xfUL << 28)
+#define MLX4_MPT_FLAG_FREE (0x3UL << 28)
#define MLX4_MPT_FLAG_MIO (1 << 17)
#define MLX4_MPT_FLAG_BIND_ENABLE (1 << 15)
#define MLX4_MPT_FLAG_PHYSICAL (1 << 9)
#define MLX4_MPT_FLAG_REGION (1 << 8)
+#define MLX4_MPT_PD_FLAG_FAST_REG (1 << 26)
+#define MLX4_MPT_PD_FLAG_EN_INV (3 << 24)
+
#define MLX4_MTT_FLAG_PRESENT 1
#define MLX4_MPT_STATUS_SW 0xF0
@@ -79,23 +83,26 @@ static u32 mlx4_buddy_alloc(struct mlx4_buddy *buddy, int order)
spin_lock(&buddy->lock);
- for (o = order; o <= buddy->max_order; ++o) {
- m = 1 << (buddy->max_order - o);
- seg = find_first_bit(buddy->bits[o], m);
- if (seg < m)
- goto found;
- }
+ for (o = order; o <= buddy->max_order; ++o)
+ if (buddy->num_free[o]) {
+ m = 1 << (buddy->max_order - o);
+ seg = find_first_bit(buddy->bits[o], m);
+ if (seg < m)
+ goto found;
+ }
spin_unlock(&buddy->lock);
return -1;
found:
clear_bit(seg, buddy->bits[o]);
+ --buddy->num_free[o];
while (o > order) {
--o;
seg <<= 1;
set_bit(seg ^ 1, buddy->bits[o]);
+ ++buddy->num_free[o];
}
spin_unlock(&buddy->lock);
@@ -113,11 +120,13 @@ static void mlx4_buddy_free(struct mlx4_buddy *buddy, u32 seg, int order)
while (test_bit(seg ^ 1, buddy->bits[order])) {
clear_bit(seg ^ 1, buddy->bits[order]);
+ --buddy->num_free[order];
seg >>= 1;
++order;
}
set_bit(seg, buddy->bits[order]);
+ ++buddy->num_free[order];
spin_unlock(&buddy->lock);
}
@@ -131,7 +140,9 @@ static int mlx4_buddy_init(struct mlx4_buddy *buddy, int max_order)
buddy->bits = kzalloc((buddy->max_order + 1) * sizeof (long *),
GFP_KERNEL);
- if (!buddy->bits)
+ buddy->num_free = kzalloc((buddy->max_order + 1) * sizeof (int *),
+ GFP_KERNEL);
+ if (!buddy->bits || !buddy->num_free)
goto err_out;
for (i = 0; i <= buddy->max_order; ++i) {
@@ -143,6 +154,7 @@ static int mlx4_buddy_init(struct mlx4_buddy *buddy, int max_order)
}
set_bit(0, buddy->bits[buddy->max_order]);
+ buddy->num_free[buddy->max_order] = 1;
return 0;
@@ -150,9 +162,10 @@ err_out_free:
for (i = 0; i <= buddy->max_order; ++i)
kfree(buddy->bits[i]);
+err_out:
kfree(buddy->bits);
+ kfree(buddy->num_free);
-err_out:
return -ENOMEM;
}
@@ -164,6 +177,7 @@ static void mlx4_buddy_cleanup(struct mlx4_buddy *buddy)
kfree(buddy->bits[i]);
kfree(buddy->bits);
+ kfree(buddy->num_free);
}
static u32 mlx4_alloc_mtt_range(struct mlx4_dev *dev, int order)
@@ -314,21 +328,30 @@ int mlx4_mr_enable(struct mlx4_dev *dev, struct mlx4_mr *mr)
memset(mpt_entry, 0, sizeof *mpt_entry);
- mpt_entry->flags = cpu_to_be32(MLX4_MPT_FLAG_SW_OWNS |
- MLX4_MPT_FLAG_MIO |
+ mpt_entry->flags = cpu_to_be32(MLX4_MPT_FLAG_MIO |
MLX4_MPT_FLAG_REGION |
mr->access);
mpt_entry->key = cpu_to_be32(key_to_hw_index(mr->key));
- mpt_entry->pd = cpu_to_be32(mr->pd);
+ mpt_entry->pd_flags = cpu_to_be32(mr->pd | MLX4_MPT_PD_FLAG_EN_INV);
mpt_entry->start = cpu_to_be64(mr->iova);
mpt_entry->length = cpu_to_be64(mr->size);
mpt_entry->entity_size = cpu_to_be32(mr->mtt.page_shift);
+
if (mr->mtt.order < 0) {
mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_PHYSICAL);
mpt_entry->mtt_seg = 0;
- } else
+ } else {
mpt_entry->mtt_seg = cpu_to_be64(mlx4_mtt_addr(dev, &mr->mtt));
+ }
+
+ if (mr->mtt.order >= 0 && mr->mtt.page_shift == 0) {
+ /* fast register MR in free state */
+ mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_FREE);
+ mpt_entry->pd_flags |= cpu_to_be32(MLX4_MPT_PD_FLAG_FAST_REG);
+ } else {
+ mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_SW_OWNS);
+ }
err = mlx4_SW2HW_MPT(dev, mailbox,
key_to_hw_index(mr->key) & (dev->caps.num_mpts - 1));
diff --git a/drivers/net/mlx4/pd.c b/drivers/net/mlx4/pd.c
index 3a93c5f0f7a..aa616892d09 100644
--- a/drivers/net/mlx4/pd.c
+++ b/drivers/net/mlx4/pd.c
@@ -91,6 +91,13 @@ EXPORT_SYMBOL_GPL(mlx4_uar_free);
int mlx4_init_uar_table(struct mlx4_dev *dev)
{
+ if (dev->caps.num_uars <= 128) {
+ mlx4_err(dev, "Only %d UAR pages (need more than 128)\n",
+ dev->caps.num_uars);
+ mlx4_err(dev, "Increase firmware log2_uar_bar_megabytes?\n");
+ return -ENODEV;
+ }
+
return mlx4_bitmap_init(&mlx4_priv(dev)->uar_table.bitmap,
dev->caps.num_uars, dev->caps.num_uars - 1,
max(128, dev->caps.reserved_uars));
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
index 711e4a8948e..5257cf464f1 100644
--- a/drivers/net/sky2.c
+++ b/drivers/net/sky2.c
@@ -1829,9 +1829,6 @@ static int sky2_down(struct net_device *dev)
if (netif_msg_ifdown(sky2))
printk(KERN_INFO PFX "%s: disabling interface\n", dev->name);
- /* Stop more packets from being queued */
- netif_stop_queue(dev);
-
/* Disable port IRQ */
imask = sky2_read32(hw, B0_IMSK);
imask &= ~portirq_msk[port];
@@ -1887,8 +1884,6 @@ static int sky2_down(struct net_device *dev)
sky2_phy_power_down(hw, port);
- netif_carrier_off(dev);
-
/* turn off LED's */
sky2_write16(hw, B0_Y2LED, LED_STAT_OFF);
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index c28d7cb2035..0196a0df902 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -19,6 +19,7 @@
//#define DEBUG
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
#include <linux/module.h>
#include <linux/virtio.h>
#include <linux/virtio_net.h>
@@ -54,9 +55,15 @@ struct virtnet_info
struct tasklet_struct tasklet;
bool free_in_tasklet;
+ /* I like... big packets and I cannot lie! */
+ bool big_packets;
+
/* Receive & send queues. */
struct sk_buff_head recv;
struct sk_buff_head send;
+
+ /* Chain pages by the private ptr. */
+ struct page *pages;
};
static inline struct virtio_net_hdr *skb_vnet_hdr(struct sk_buff *skb)
@@ -69,6 +76,23 @@ static inline void vnet_hdr_to_sg(struct scatterlist *sg, struct sk_buff *skb)
sg_init_one(sg, skb_vnet_hdr(skb), sizeof(struct virtio_net_hdr));
}
+static void give_a_page(struct virtnet_info *vi, struct page *page)
+{
+ page->private = (unsigned long)vi->pages;
+ vi->pages = page;
+}
+
+static struct page *get_a_page(struct virtnet_info *vi, gfp_t gfp_mask)
+{
+ struct page *p = vi->pages;
+
+ if (p)
+ vi->pages = (struct page *)p->private;
+ else
+ p = alloc_page(gfp_mask);
+ return p;
+}
+
static void skb_xmit_done(struct virtqueue *svq)
{
struct virtnet_info *vi = svq->vdev->priv;
@@ -88,6 +112,7 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb,
unsigned len)
{
struct virtio_net_hdr *hdr = skb_vnet_hdr(skb);
+ int err;
if (unlikely(len < sizeof(struct virtio_net_hdr) + ETH_HLEN)) {
pr_debug("%s: short packet %i\n", dev->name, len);
@@ -95,10 +120,23 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb,
goto drop;
}
len -= sizeof(struct virtio_net_hdr);
- BUG_ON(len > MAX_PACKET_LEN);
- skb_trim(skb, len);
+ if (len <= MAX_PACKET_LEN) {
+ unsigned int i;
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
+ give_a_page(dev->priv, skb_shinfo(skb)->frags[i].page);
+ skb->data_len = 0;
+ skb_shinfo(skb)->nr_frags = 0;
+ }
+
+ err = pskb_trim(skb, len);
+ if (err) {
+ pr_debug("%s: pskb_trim failed %i %d\n", dev->name, len, err);
+ dev->stats.rx_dropped++;
+ goto drop;
+ }
+ skb->truesize += skb->data_len;
dev->stats.rx_bytes += skb->len;
dev->stats.rx_packets++;
@@ -160,7 +198,7 @@ static void try_fill_recv(struct virtnet_info *vi)
{
struct sk_buff *skb;
struct scatterlist sg[2+MAX_SKB_FRAGS];
- int num, err;
+ int num, err, i;
sg_init_table(sg, 2+MAX_SKB_FRAGS);
for (;;) {
@@ -170,6 +208,24 @@ static void try_fill_recv(struct virtnet_info *vi)
skb_put(skb, MAX_PACKET_LEN);
vnet_hdr_to_sg(sg, skb);
+
+ if (vi->big_packets) {
+ for (i = 0; i < MAX_SKB_FRAGS; i++) {
+ skb_frag_t *f = &skb_shinfo(skb)->frags[i];
+ f->page = get_a_page(vi, GFP_ATOMIC);
+ if (!f->page)
+ break;
+
+ f->page_offset = 0;
+ f->size = PAGE_SIZE;
+
+ skb->data_len += PAGE_SIZE;
+ skb->len += PAGE_SIZE;
+
+ skb_shinfo(skb)->nr_frags++;
+ }
+ }
+
num = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1;
skb_queue_head(&vi->recv, skb);
@@ -335,16 +391,11 @@ again:
free_old_xmit_skbs(vi);
/* If we has a buffer left over from last time, send it now. */
- if (unlikely(vi->last_xmit_skb)) {
- if (xmit_skb(vi, vi->last_xmit_skb) != 0) {
- /* Drop this skb: we only queue one. */
- vi->dev->stats.tx_dropped++;
- kfree_skb(skb);
- skb = NULL;
- goto stop_queue;
- }
- vi->last_xmit_skb = NULL;
- }
+ if (unlikely(vi->last_xmit_skb) &&
+ xmit_skb(vi, vi->last_xmit_skb) != 0)
+ goto stop_queue;
+
+ vi->last_xmit_skb = NULL;
/* Put new one in send queue and do transmit */
if (likely(skb)) {
@@ -370,6 +421,11 @@ stop_queue:
netif_start_queue(dev);
goto again;
}
+ if (skb) {
+ /* Drop this skb: we only queue one. */
+ vi->dev->stats.tx_dropped++;
+ kfree_skb(skb);
+ }
goto done;
}
@@ -408,6 +464,22 @@ static int virtnet_close(struct net_device *dev)
return 0;
}
+static int virtnet_set_tx_csum(struct net_device *dev, u32 data)
+{
+ struct virtnet_info *vi = netdev_priv(dev);
+ struct virtio_device *vdev = vi->vdev;
+
+ if (data && !virtio_has_feature(vdev, VIRTIO_NET_F_CSUM))
+ return -ENOSYS;
+
+ return ethtool_op_set_tx_hw_csum(dev, data);
+}
+
+static struct ethtool_ops virtnet_ethtool_ops = {
+ .set_tx_csum = virtnet_set_tx_csum,
+ .set_sg = ethtool_op_set_sg,
+};
+
static int virtnet_probe(struct virtio_device *vdev)
{
int err;
@@ -427,6 +499,7 @@ static int virtnet_probe(struct virtio_device *vdev)
#ifdef CONFIG_NET_POLL_CONTROLLER
dev->poll_controller = virtnet_netpoll;
#endif
+ SET_ETHTOOL_OPS(dev, &virtnet_ethtool_ops);
SET_NETDEV_DEV(dev, &vdev->dev);
/* Do we support "hardware" checksums? */
@@ -462,11 +535,18 @@ static int virtnet_probe(struct virtio_device *vdev)
vi->dev = dev;
vi->vdev = vdev;
vdev->priv = vi;
+ vi->pages = NULL;
/* If they give us a callback when all buffers are done, we don't need
* the timer. */
vi->free_in_tasklet = virtio_has_feature(vdev,VIRTIO_F_NOTIFY_ON_EMPTY);
+ /* If we can receive ANY GSO packets, we must allocate large ones. */
+ if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO4)
+ || virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO6)
+ || virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ECN))
+ vi->big_packets = true;
+
/* We expect two virtqueues, receive then send. */
vi->rvq = vdev->config->find_vq(vdev, 0, skb_recv_done);
if (IS_ERR(vi->rvq)) {
@@ -541,6 +621,10 @@ static void virtnet_remove(struct virtio_device *vdev)
vdev->config->del_vq(vi->svq);
vdev->config->del_vq(vi->rvq);
unregister_netdev(vi->dev);
+
+ while (vi->pages)
+ __free_pages(get_a_page(vi, GFP_KERNEL), 0);
+
free_netdev(vi->dev);
}
@@ -553,7 +637,9 @@ static unsigned int features[] = {
VIRTIO_NET_F_CSUM, VIRTIO_NET_F_GUEST_CSUM,
VIRTIO_NET_F_GSO, VIRTIO_NET_F_MAC,
VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_HOST_TSO6,
- VIRTIO_NET_F_HOST_ECN, VIRTIO_F_NOTIFY_ON_EMPTY,
+ VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6,
+ VIRTIO_NET_F_GUEST_ECN, /* We don't yet handle UFO input. */
+ VIRTIO_F_NOTIFY_ON_EMPTY,
};
static struct virtio_driver virtio_net = {
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 3a7a11a75fb..1d7ec312934 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -4,7 +4,7 @@ config OF_DEVICE
config OF_GPIO
def_bool y
- depends on OF && PPC_OF && HAVE_GPIO_LIB
+ depends on OF && PPC_OF && GPIOLIB
help
OpenFirmware GPIO accessors
diff --git a/drivers/of/of_i2c.c b/drivers/of/of_i2c.c
index 5c015d310d4..344e1b03dd8 100644
--- a/drivers/of/of_i2c.c
+++ b/drivers/of/of_i2c.c
@@ -91,8 +91,6 @@ void of_register_i2c_devices(struct i2c_adapter *adap,
}
info.irq = irq_of_parse_and_map(node, 0);
- if (info.irq == NO_IRQ)
- info.irq = -1;
if (of_find_i2c_driver(node, &info) < 0) {
irq_dispose_mapping(info.irq);
diff --git a/drivers/parport/parport_ax88796.c b/drivers/parport/parport_ax88796.c
index 4ec220b2eae..6938d2e9f18 100644
--- a/drivers/parport/parport_ax88796.c
+++ b/drivers/parport/parport_ax88796.c
@@ -406,6 +406,8 @@ static int parport_ax88796_resume(struct platform_device *dev)
#define parport_ax88796_resume NULL
#endif
+MODULE_ALIAS("platform:ax88796-pp");
+
static struct platform_driver axdrv = {
.driver = {
.name = "ax88796-pp",
diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c
index f941f609dbf..8bf86ae2333 100644
--- a/drivers/pci/dmar.c
+++ b/drivers/pci/dmar.c
@@ -317,10 +317,8 @@ int __init dmar_table_init(void)
return -ENODEV;
}
- if (list_empty(&dmar_rmrr_units)) {
+ if (list_empty(&dmar_rmrr_units))
printk(KERN_INFO PREFIX "No RMRR found\n");
- return -ENODEV;
- }
return 0;
}
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index d00f0e0d845..e9c356236d2 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1040,7 +1040,7 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
* @dev: PCI device to handle.
* @state: PCI state from which device will issue PME#.
*/
-static bool pci_pme_capable(struct pci_dev *dev, pci_power_t state)
+bool pci_pme_capable(struct pci_dev *dev, pci_power_t state)
{
if (!dev->pm_cap)
return false;
@@ -1123,17 +1123,10 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable)
}
/**
- * pci_prepare_to_sleep - prepare PCI device for system-wide transition into a sleep state
- * @dev: Device to handle.
- *
- * Choose the power state appropriate for the device depending on whether
- * it can wake up the system and/or is power manageable by the platform
- * (PCI_D3hot is the default) and put the device into that state.
*/
-int pci_prepare_to_sleep(struct pci_dev *dev)
+pci_power_t pci_target_state(struct pci_dev *dev)
{
pci_power_t target_state = PCI_D3hot;
- int error;
if (platform_pci_power_manageable(dev)) {
/*
@@ -1160,7 +1153,7 @@ int pci_prepare_to_sleep(struct pci_dev *dev)
* to generate PME#.
*/
if (!dev->pm_cap)
- return -EIO;
+ return PCI_POWER_ERROR;
if (dev->pme_support) {
while (target_state
@@ -1169,6 +1162,25 @@ int pci_prepare_to_sleep(struct pci_dev *dev)
}
}
+ return target_state;
+}
+
+/**
+ * pci_prepare_to_sleep - prepare PCI device for system-wide transition into a sleep state
+ * @dev: Device to handle.
+ *
+ * Choose the power state appropriate for the device depending on whether
+ * it can wake up the system and/or is power manageable by the platform
+ * (PCI_D3hot is the default) and put the device into that state.
+ */
+int pci_prepare_to_sleep(struct pci_dev *dev)
+{
+ pci_power_t target_state = pci_target_state(dev);
+ int error;
+
+ if (target_state == PCI_POWER_ERROR)
+ return -EIO;
+
pci_enable_wake(dev, target_state, true);
error = pci_set_power_state(dev, target_state);
@@ -1918,7 +1930,9 @@ EXPORT_SYMBOL(pci_select_bars);
EXPORT_SYMBOL(pci_set_power_state);
EXPORT_SYMBOL(pci_save_state);
EXPORT_SYMBOL(pci_restore_state);
+EXPORT_SYMBOL(pci_pme_capable);
EXPORT_SYMBOL(pci_enable_wake);
+EXPORT_SYMBOL(pci_target_state);
EXPORT_SYMBOL(pci_prepare_to_sleep);
EXPORT_SYMBOL(pci_back_from_sleep);
EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state);
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 4400dffbd93..e1098c302c4 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -88,7 +88,7 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp
if ((pos & 3) && cnt > 2) {
unsigned short val;
pci_user_read_config_word(dev, pos, &val);
- __put_user(cpu_to_le16(val), (unsigned short __user *) buf);
+ __put_user(cpu_to_le16(val), (__le16 __user *) buf);
buf += 2;
pos += 2;
cnt -= 2;
@@ -97,7 +97,7 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp
while (cnt >= 4) {
unsigned int val;
pci_user_read_config_dword(dev, pos, &val);
- __put_user(cpu_to_le32(val), (unsigned int __user *) buf);
+ __put_user(cpu_to_le32(val), (__le32 __user *) buf);
buf += 4;
pos += 4;
cnt -= 4;
@@ -106,7 +106,7 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp
if (cnt >= 2) {
unsigned short val;
pci_user_read_config_word(dev, pos, &val);
- __put_user(cpu_to_le16(val), (unsigned short __user *) buf);
+ __put_user(cpu_to_le16(val), (__le16 __user *) buf);
buf += 2;
pos += 2;
cnt -= 2;
@@ -156,8 +156,8 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof
}
if ((pos & 3) && cnt > 2) {
- unsigned short val;
- __get_user(val, (unsigned short __user *) buf);
+ __le16 val;
+ __get_user(val, (__le16 __user *) buf);
pci_user_write_config_word(dev, pos, le16_to_cpu(val));
buf += 2;
pos += 2;
@@ -165,8 +165,8 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof
}
while (cnt >= 4) {
- unsigned int val;
- __get_user(val, (unsigned int __user *) buf);
+ __le32 val;
+ __get_user(val, (__le32 __user *) buf);
pci_user_write_config_dword(dev, pos, le32_to_cpu(val));
buf += 4;
pos += 4;
@@ -174,8 +174,8 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof
}
if (cnt >= 2) {
- unsigned short val;
- __get_user(val, (unsigned short __user *) buf);
+ __le16 val;
+ __get_user(val, (__le16 __user *) buf);
pci_user_write_config_word(dev, pos, le16_to_cpu(val));
buf += 2;
pos += 2;
diff --git a/drivers/pcmcia/electra_cf.c b/drivers/pcmcia/electra_cf.c
index c21f9a9c3e3..a34284b1482 100644
--- a/drivers/pcmcia/electra_cf.c
+++ b/drivers/pcmcia/electra_cf.c
@@ -28,6 +28,7 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/of_platform.h>
diff --git a/drivers/power/ds2760_battery.c b/drivers/power/ds2760_battery.c
index 71be36f1870..308ddb201b6 100644
--- a/drivers/power/ds2760_battery.c
+++ b/drivers/power/ds2760_battery.c
@@ -433,6 +433,8 @@ static int ds2760_battery_resume(struct platform_device *pdev)
#endif /* CONFIG_PM */
+MODULE_ALIAS("platform:ds2760-battery");
+
static struct platform_driver ds2760_battery_driver = {
.driver = {
.name = "ds2760-battery",
diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c
index 82810b7bff9..0471ec743ab 100644
--- a/drivers/power/pda_power.c
+++ b/drivers/power/pda_power.c
@@ -362,6 +362,8 @@ static int pda_power_resume(struct platform_device *pdev)
#define pda_power_resume NULL
#endif /* CONFIG_PM */
+MODULE_ALIAS("platform:pda-power");
+
static struct platform_driver pda_power_pdrv = {
.driver = {
.name = "pda-power",
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index fc85bf2e4a9..90ab7382540 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -273,6 +273,25 @@ comment "SPI RTC drivers"
if SPI_MASTER
+config RTC_DRV_M41T94
+ tristate "ST M41T94"
+ help
+ If you say yes here you will get support for the
+ ST M41T94 SPI RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-m41t94.
+
+config RTC_DRV_DS1305
+ tristate "Dallas/Maxim DS1305/DS1306"
+ help
+ Select this driver to get support for the Dallas/Maxim DS1305
+ and DS1306 real time clock chips. These support a trickle
+ charger, alarms, and NVRAM in addition to the clock.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds1305.
+
config RTC_DRV_MAX6902
tristate "Maxim MAX6902"
help
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index b5d9d67df88..18622ef84ca 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o
obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o
+obj-$(CONFIG_RTC_DRV_DS1305) += rtc-ds1305.o
obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o
obj-$(CONFIG_RTC_DRV_DS1374) += rtc-ds1374.o
obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o
@@ -34,6 +35,7 @@ obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o
+obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o
obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o
obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c
index 9c3db934cc2..cd32d05db77 100644
--- a/drivers/rtc/rtc-at91rm9200.c
+++ b/drivers/rtc/rtc-at91rm9200.c
@@ -171,8 +171,10 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
| BIN2BCD(tm.tm_mday) << 24
| AT91_RTC_DATEEN | AT91_RTC_MTHEN);
- if (alrm->enabled)
+ if (alrm->enabled) {
+ at91_sys_write(AT91_RTC_SCCR, AT91_RTC_ALARM);
at91_sys_write(AT91_RTC_IER, AT91_RTC_ALARM);
+ }
pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__,
at91_alarm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour,
@@ -191,28 +193,22 @@ static int at91_rtc_ioctl(struct device *dev, unsigned int cmd,
pr_debug("%s(): cmd=%08x, arg=%08lx.\n", __func__, cmd, arg);
+ /* important: scrub old status before enabling IRQs */
switch (cmd) {
case RTC_AIE_OFF: /* alarm off */
at91_sys_write(AT91_RTC_IDR, AT91_RTC_ALARM);
break;
case RTC_AIE_ON: /* alarm on */
+ at91_sys_write(AT91_RTC_SCCR, AT91_RTC_ALARM);
at91_sys_write(AT91_RTC_IER, AT91_RTC_ALARM);
break;
case RTC_UIE_OFF: /* update off */
- case RTC_PIE_OFF: /* periodic off */
at91_sys_write(AT91_RTC_IDR, AT91_RTC_SECEV);
break;
case RTC_UIE_ON: /* update on */
- case RTC_PIE_ON: /* periodic on */
+ at91_sys_write(AT91_RTC_SCCR, AT91_RTC_SECEV);
at91_sys_write(AT91_RTC_IER, AT91_RTC_SECEV);
break;
- case RTC_IRQP_READ: /* read periodic alarm frequency */
- ret = put_user(AT91_RTC_FREQ, (unsigned long *) arg);
- break;
- case RTC_IRQP_SET: /* set periodic alarm frequency */
- if (arg != AT91_RTC_FREQ)
- ret = -EINVAL;
- break;
default:
ret = -ENOIOCTLCMD;
break;
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index d7bb9bac71d..6ea349aba3b 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -36,25 +36,9 @@
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
-#ifdef CONFIG_HPET_EMULATE_RTC
-#include <asm/hpet.h>
-#endif
-
/* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */
#include <asm-generic/rtc.h>
-#ifndef CONFIG_HPET_EMULATE_RTC
-#define is_hpet_enabled() 0
-#define hpet_set_alarm_time(hrs, min, sec) do { } while (0)
-#define hpet_set_periodic_freq(arg) 0
-#define hpet_mask_rtc_irq_bit(arg) do { } while (0)
-#define hpet_set_rtc_irq_bit(arg) do { } while (0)
-#define hpet_rtc_timer_init() do { } while (0)
-#define hpet_register_irq_handler(h) 0
-#define hpet_unregister_irq_handler(h) do { } while (0)
-extern irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id);
-#endif
-
struct cmos_rtc {
struct rtc_device *rtc;
struct device *dev;
@@ -93,6 +77,72 @@ static inline int is_intr(u8 rtc_intr)
/*----------------------------------------------------------------*/
+/* Much modern x86 hardware has HPETs (10+ MHz timers) which, because
+ * many BIOS programmers don't set up "sane mode" IRQ routing, are mostly
+ * used in a broken "legacy replacement" mode. The breakage includes
+ * HPET #1 hijacking the IRQ for this RTC, and being unavailable for
+ * other (better) use.
+ *
+ * When that broken mode is in use, platform glue provides a partial
+ * emulation of hardware RTC IRQ facilities using HPET #1. We don't
+ * want to use HPET for anything except those IRQs though...
+ */
+#ifdef CONFIG_HPET_EMULATE_RTC
+#include <asm/hpet.h>
+#else
+
+static inline int is_hpet_enabled(void)
+{
+ return 0;
+}
+
+static inline int hpet_mask_rtc_irq_bit(unsigned long mask)
+{
+ return 0;
+}
+
+static inline int hpet_set_rtc_irq_bit(unsigned long mask)
+{
+ return 0;
+}
+
+static inline int
+hpet_set_alarm_time(unsigned char hrs, unsigned char min, unsigned char sec)
+{
+ return 0;
+}
+
+static inline int hpet_set_periodic_freq(unsigned long freq)
+{
+ return 0;
+}
+
+static inline int hpet_rtc_dropped_irq(void)
+{
+ return 0;
+}
+
+static inline int hpet_rtc_timer_init(void)
+{
+ return 0;
+}
+
+extern irq_handler_t hpet_rtc_interrupt;
+
+static inline int hpet_register_irq_handler(irq_handler_t handler)
+{
+ return 0;
+}
+
+static inline int hpet_unregister_irq_handler(irq_handler_t handler)
+{
+ return 0;
+}
+
+#endif
+
+/*----------------------------------------------------------------*/
+
static int cmos_read_time(struct device *dev, struct rtc_time *t)
{
/* REVISIT: if the clock has a "century" register, use
@@ -185,11 +235,56 @@ static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t)
return 0;
}
+static void cmos_checkintr(struct cmos_rtc *cmos, unsigned char rtc_control)
+{
+ unsigned char rtc_intr;
+
+ /* NOTE after changing RTC_xIE bits we always read INTR_FLAGS;
+ * allegedly some older rtcs need that to handle irqs properly
+ */
+ rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
+
+ if (is_hpet_enabled())
+ return;
+
+ rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
+ if (is_intr(rtc_intr))
+ rtc_update_irq(cmos->rtc, 1, rtc_intr);
+}
+
+static void cmos_irq_enable(struct cmos_rtc *cmos, unsigned char mask)
+{
+ unsigned char rtc_control;
+
+ /* flush any pending IRQ status, notably for update irqs,
+ * before we enable new IRQs
+ */
+ rtc_control = CMOS_READ(RTC_CONTROL);
+ cmos_checkintr(cmos, rtc_control);
+
+ rtc_control |= mask;
+ CMOS_WRITE(rtc_control, RTC_CONTROL);
+ hpet_set_rtc_irq_bit(mask);
+
+ cmos_checkintr(cmos, rtc_control);
+}
+
+static void cmos_irq_disable(struct cmos_rtc *cmos, unsigned char mask)
+{
+ unsigned char rtc_control;
+
+ rtc_control = CMOS_READ(RTC_CONTROL);
+ rtc_control &= ~mask;
+ CMOS_WRITE(rtc_control, RTC_CONTROL);
+ hpet_mask_rtc_irq_bit(mask);
+
+ cmos_checkintr(cmos, rtc_control);
+}
+
static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
{
struct cmos_rtc *cmos = dev_get_drvdata(dev);
unsigned char mon, mday, hrs, min, sec;
- unsigned char rtc_control, rtc_intr;
if (!is_valid_irq(cmos->irq))
return -EIO;
@@ -213,17 +308,10 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
sec = t->time.tm_sec;
sec = (sec < 60) ? BIN2BCD(sec) : 0xff;
- hpet_set_alarm_time(t->time.tm_hour, t->time.tm_min, t->time.tm_sec);
spin_lock_irq(&rtc_lock);
/* next rtc irq must not be from previous alarm setting */
- rtc_control = CMOS_READ(RTC_CONTROL);
- rtc_control &= ~RTC_AIE;
- CMOS_WRITE(rtc_control, RTC_CONTROL);
- rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
- rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
- if (is_intr(rtc_intr))
- rtc_update_irq(cmos->rtc, 1, rtc_intr);
+ cmos_irq_disable(cmos, RTC_AIE);
/* update alarm */
CMOS_WRITE(hrs, RTC_HOURS_ALARM);
@@ -237,14 +325,13 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
CMOS_WRITE(mon, cmos->mon_alrm);
}
- if (t->enabled) {
- rtc_control |= RTC_AIE;
- CMOS_WRITE(rtc_control, RTC_CONTROL);
- rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
- rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
- if (is_intr(rtc_intr))
- rtc_update_irq(cmos->rtc, 1, rtc_intr);
- }
+ /* FIXME the HPET alarm glue currently ignores day_alrm
+ * and mon_alrm ...
+ */
+ hpet_set_alarm_time(t->time.tm_hour, t->time.tm_min, t->time.tm_sec);
+
+ if (t->enabled)
+ cmos_irq_enable(cmos, RTC_AIE);
spin_unlock_irq(&rtc_lock);
@@ -267,8 +354,8 @@ static int cmos_irq_set_freq(struct device *dev, int freq)
f = 16 - f;
spin_lock_irqsave(&rtc_lock, flags);
- if (!hpet_set_periodic_freq(freq))
- CMOS_WRITE(RTC_REF_CLCK_32KHZ | f, RTC_FREQ_SELECT);
+ hpet_set_periodic_freq(freq);
+ CMOS_WRITE(RTC_REF_CLCK_32KHZ | f, RTC_FREQ_SELECT);
spin_unlock_irqrestore(&rtc_lock, flags);
return 0;
@@ -277,26 +364,17 @@ static int cmos_irq_set_freq(struct device *dev, int freq)
static int cmos_irq_set_state(struct device *dev, int enabled)
{
struct cmos_rtc *cmos = dev_get_drvdata(dev);
- unsigned char rtc_control, rtc_intr;
unsigned long flags;
if (!is_valid_irq(cmos->irq))
return -ENXIO;
spin_lock_irqsave(&rtc_lock, flags);
- rtc_control = CMOS_READ(RTC_CONTROL);
if (enabled)
- rtc_control |= RTC_PIE;
+ cmos_irq_enable(cmos, RTC_PIE);
else
- rtc_control &= ~RTC_PIE;
-
- CMOS_WRITE(rtc_control, RTC_CONTROL);
-
- rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
- rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
- if (is_intr(rtc_intr))
- rtc_update_irq(cmos->rtc, 1, rtc_intr);
+ cmos_irq_disable(cmos, RTC_PIE);
spin_unlock_irqrestore(&rtc_lock, flags);
return 0;
@@ -308,7 +386,6 @@ static int
cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{
struct cmos_rtc *cmos = dev_get_drvdata(dev);
- unsigned char rtc_control, rtc_intr;
unsigned long flags;
switch (cmd) {
@@ -316,51 +393,29 @@ cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
case RTC_AIE_ON:
case RTC_UIE_OFF:
case RTC_UIE_ON:
- case RTC_PIE_OFF:
- case RTC_PIE_ON:
if (!is_valid_irq(cmos->irq))
return -EINVAL;
break;
+ /* PIE ON/OFF is handled by cmos_irq_set_state() */
default:
return -ENOIOCTLCMD;
}
spin_lock_irqsave(&rtc_lock, flags);
- rtc_control = CMOS_READ(RTC_CONTROL);
switch (cmd) {
case RTC_AIE_OFF: /* alarm off */
- rtc_control &= ~RTC_AIE;
- hpet_mask_rtc_irq_bit(RTC_AIE);
+ cmos_irq_disable(cmos, RTC_AIE);
break;
case RTC_AIE_ON: /* alarm on */
- rtc_control |= RTC_AIE;
- hpet_set_rtc_irq_bit(RTC_AIE);
+ cmos_irq_enable(cmos, RTC_AIE);
break;
case RTC_UIE_OFF: /* update off */
- rtc_control &= ~RTC_UIE;
- hpet_mask_rtc_irq_bit(RTC_UIE);
+ cmos_irq_disable(cmos, RTC_UIE);
break;
case RTC_UIE_ON: /* update on */
- rtc_control |= RTC_UIE;
- hpet_set_rtc_irq_bit(RTC_UIE);
- break;
- case RTC_PIE_OFF: /* periodic off */
- rtc_control &= ~RTC_PIE;
- hpet_mask_rtc_irq_bit(RTC_PIE);
- break;
- case RTC_PIE_ON: /* periodic on */
- rtc_control |= RTC_PIE;
- hpet_set_rtc_irq_bit(RTC_PIE);
+ cmos_irq_enable(cmos, RTC_UIE);
break;
}
- if (!is_hpet_enabled())
- CMOS_WRITE(rtc_control, RTC_CONTROL);
-
- rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
- rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
- if (is_intr(rtc_intr))
- rtc_update_irq(cmos->rtc, 1, rtc_intr);
-
spin_unlock_irqrestore(&rtc_lock, flags);
return 0;
}
@@ -502,27 +557,29 @@ static irqreturn_t cmos_interrupt(int irq, void *p)
u8 rtc_control;
spin_lock(&rtc_lock);
- /*
- * In this case it is HPET RTC interrupt handler
- * calling us, with the interrupt information
- * passed as arg1, instead of irq.
+
+ /* When the HPET interrupt handler calls us, the interrupt
+ * status is passed as arg1 instead of the irq number. But
+ * always clear irq status, even when HPET is in the way.
+ *
+ * Note that HPET and RTC are almost certainly out of phase,
+ * giving different IRQ status ...
*/
+ irqstat = CMOS_READ(RTC_INTR_FLAGS);
+ rtc_control = CMOS_READ(RTC_CONTROL);
if (is_hpet_enabled())
irqstat = (unsigned long)irq & 0xF0;
- else {
- irqstat = CMOS_READ(RTC_INTR_FLAGS);
- rtc_control = CMOS_READ(RTC_CONTROL);
- irqstat &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
- }
+ irqstat &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
/* All Linux RTC alarms should be treated as if they were oneshot.
* Similar code may be needed in system wakeup paths, in case the
* alarm woke the system.
*/
if (irqstat & RTC_AIE) {
- rtc_control = CMOS_READ(RTC_CONTROL);
rtc_control &= ~RTC_AIE;
CMOS_WRITE(rtc_control, RTC_CONTROL);
+ hpet_mask_rtc_irq_bit(RTC_AIE);
+
CMOS_READ(RTC_INTR_FLAGS);
}
spin_unlock(&rtc_lock);
@@ -629,18 +686,13 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
* do something about other clock frequencies.
*/
cmos_rtc.rtc->irq_freq = 1024;
- if (!hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq))
- CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT);
+ hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq);
+ CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT);
+
+ /* disable irqs */
+ cmos_irq_disable(&cmos_rtc, RTC_PIE | RTC_AIE | RTC_UIE);
- /* disable irqs.
- *
- * NOTE after changing RTC_xIE bits we always read INTR_FLAGS;
- * allegedly some older rtcs need that to handle irqs properly
- */
rtc_control = CMOS_READ(RTC_CONTROL);
- rtc_control &= ~(RTC_PIE | RTC_AIE | RTC_UIE);
- CMOS_WRITE(rtc_control, RTC_CONTROL);
- CMOS_READ(RTC_INTR_FLAGS);
spin_unlock_irq(&rtc_lock);
@@ -687,7 +739,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
goto cleanup2;
}
- pr_info("%s: alarms up to one %s%s\n",
+ pr_info("%s: alarms up to one %s%s%s\n",
cmos_rtc.rtc->dev.bus_id,
is_valid_irq(rtc_irq)
? (cmos_rtc.mon_alrm
@@ -695,8 +747,8 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
: (cmos_rtc.day_alrm
? "month" : "day"))
: "no",
- cmos_rtc.century ? ", y3k" : ""
- );
+ cmos_rtc.century ? ", y3k" : "",
+ is_hpet_enabled() ? ", hpet irqs" : "");
return 0;
@@ -713,13 +765,8 @@ cleanup0:
static void cmos_do_shutdown(void)
{
- unsigned char rtc_control;
-
spin_lock_irq(&rtc_lock);
- rtc_control = CMOS_READ(RTC_CONTROL);
- rtc_control &= ~(RTC_PIE|RTC_AIE|RTC_UIE);
- CMOS_WRITE(rtc_control, RTC_CONTROL);
- CMOS_READ(RTC_INTR_FLAGS);
+ cmos_irq_disable(&cmos_rtc, RTC_IRQMASK);
spin_unlock_irq(&rtc_lock);
}
@@ -760,17 +807,17 @@ static int cmos_suspend(struct device *dev, pm_message_t mesg)
spin_lock_irq(&rtc_lock);
cmos->suspend_ctrl = tmp = CMOS_READ(RTC_CONTROL);
if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) {
- unsigned char irqstat;
+ unsigned char mask;
if (do_wake)
- tmp &= ~(RTC_PIE|RTC_UIE);
+ mask = RTC_IRQMASK & ~RTC_AIE;
else
- tmp &= ~(RTC_PIE|RTC_AIE|RTC_UIE);
+ mask = RTC_IRQMASK;
+ tmp &= ~mask;
CMOS_WRITE(tmp, RTC_CONTROL);
- irqstat = CMOS_READ(RTC_INTR_FLAGS);
- irqstat &= (tmp & RTC_IRQMASK) | RTC_IRQF;
- if (is_intr(irqstat))
- rtc_update_irq(cmos->rtc, 1, irqstat);
+ hpet_mask_rtc_irq_bit(mask);
+
+ cmos_checkintr(cmos, tmp);
}
spin_unlock_irq(&rtc_lock);
@@ -796,7 +843,8 @@ static int cmos_resume(struct device *dev)
unsigned char tmp = cmos->suspend_ctrl;
/* re-enable any irqs previously active */
- if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) {
+ if (tmp & RTC_IRQMASK) {
+ unsigned char mask;
if (cmos->enabled_wake) {
if (cmos->wake_off)
@@ -807,18 +855,28 @@ static int cmos_resume(struct device *dev)
}
spin_lock_irq(&rtc_lock);
- CMOS_WRITE(tmp, RTC_CONTROL);
- tmp = CMOS_READ(RTC_INTR_FLAGS);
- tmp &= (cmos->suspend_ctrl & RTC_IRQMASK) | RTC_IRQF;
- if (is_intr(tmp))
- rtc_update_irq(cmos->rtc, 1, tmp);
+ do {
+ CMOS_WRITE(tmp, RTC_CONTROL);
+ hpet_set_rtc_irq_bit(tmp & RTC_IRQMASK);
+
+ mask = CMOS_READ(RTC_INTR_FLAGS);
+ mask &= (tmp & RTC_IRQMASK) | RTC_IRQF;
+ if (!is_hpet_enabled() || !is_intr(mask))
+ break;
+
+ /* force one-shot behavior if HPET blocked
+ * the wake alarm's irq
+ */
+ rtc_update_irq(cmos->rtc, 1, mask);
+ tmp &= ~RTC_AIE;
+ hpet_mask_rtc_irq_bit(RTC_AIE);
+ } while (mask & RTC_AIE);
spin_unlock_irq(&rtc_lock);
}
pr_debug("%s: resume, ctrl %02x\n",
cmos_rtc.rtc->dev.bus_id,
- cmos->suspend_ctrl);
-
+ tmp);
return 0;
}
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c
index 0114a78b7cb..0a870b7e5c3 100644
--- a/drivers/rtc/rtc-dev.c
+++ b/drivers/rtc/rtc-dev.c
@@ -209,7 +209,7 @@ static unsigned int rtc_dev_poll(struct file *file, poll_table *wait)
return (data != 0) ? (POLLIN | POLLRDNORM) : 0;
}
-static int rtc_dev_ioctl(struct inode *inode, struct file *file,
+static long rtc_dev_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
int err = 0;
@@ -219,6 +219,10 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
struct rtc_wkalrm alarm;
void __user *uarg = (void __user *) arg;
+ err = mutex_lock_interruptible(&rtc->ops_lock);
+ if (err)
+ return -EBUSY;
+
/* check that the calling task has appropriate permissions
* for certain ioctls. doing this check here is useful
* to avoid duplicate code in each driver.
@@ -227,26 +231,31 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
case RTC_EPOCH_SET:
case RTC_SET_TIME:
if (!capable(CAP_SYS_TIME))
- return -EACCES;
+ err = -EACCES;
break;
case RTC_IRQP_SET:
if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE))
- return -EACCES;
+ err = -EACCES;
break;
case RTC_PIE_ON:
if (rtc->irq_freq > rtc->max_user_freq &&
!capable(CAP_SYS_RESOURCE))
- return -EACCES;
+ err = -EACCES;
break;
}
+ if (err)
+ goto done;
+
/* try the driver's ioctl interface */
if (ops->ioctl) {
err = ops->ioctl(rtc->dev.parent, cmd, arg);
- if (err != -ENOIOCTLCMD)
+ if (err != -ENOIOCTLCMD) {
+ mutex_unlock(&rtc->ops_lock);
return err;
+ }
}
/* if the driver does not provide the ioctl interface
@@ -265,15 +274,19 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
switch (cmd) {
case RTC_ALM_READ:
+ mutex_unlock(&rtc->ops_lock);
+
err = rtc_read_alarm(rtc, &alarm);
if (err < 0)
return err;
if (copy_to_user(uarg, &alarm.time, sizeof(tm)))
- return -EFAULT;
- break;
+ err = -EFAULT;
+ return err;
case RTC_ALM_SET:
+ mutex_unlock(&rtc->ops_lock);
+
if (copy_from_user(&alarm.time, uarg, sizeof(tm)))
return -EFAULT;
@@ -321,24 +334,26 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
}
}
- err = rtc_set_alarm(rtc, &alarm);
- break;
+ return rtc_set_alarm(rtc, &alarm);
case RTC_RD_TIME:
+ mutex_unlock(&rtc->ops_lock);
+
err = rtc_read_time(rtc, &tm);
if (err < 0)
return err;
if (copy_to_user(uarg, &tm, sizeof(tm)))
- return -EFAULT;
- break;
+ err = -EFAULT;
+ return err;
case RTC_SET_TIME:
+ mutex_unlock(&rtc->ops_lock);
+
if (copy_from_user(&tm, uarg, sizeof(tm)))
return -EFAULT;
- err = rtc_set_time(rtc, &tm);
- break;
+ return rtc_set_time(rtc, &tm);
case RTC_PIE_ON:
err = rtc_irq_set_state(rtc, NULL, 1);
@@ -376,34 +391,37 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
break;
#endif
case RTC_WKALM_SET:
+ mutex_unlock(&rtc->ops_lock);
if (copy_from_user(&alarm, uarg, sizeof(alarm)))
return -EFAULT;
- err = rtc_set_alarm(rtc, &alarm);
- break;
+ return rtc_set_alarm(rtc, &alarm);
case RTC_WKALM_RD:
+ mutex_unlock(&rtc->ops_lock);
err = rtc_read_alarm(rtc, &alarm);
if (err < 0)
return err;
if (copy_to_user(uarg, &alarm, sizeof(alarm)))
- return -EFAULT;
- break;
+ err = -EFAULT;
+ return err;
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
case RTC_UIE_OFF:
clear_uie(rtc);
- return 0;
+ break;
case RTC_UIE_ON:
- return set_uie(rtc);
+ err = set_uie(rtc);
#endif
default:
err = -ENOTTY;
break;
}
+done:
+ mutex_unlock(&rtc->ops_lock);
return err;
}
@@ -432,7 +450,7 @@ static const struct file_operations rtc_dev_fops = {
.llseek = no_llseek,
.read = rtc_dev_read,
.poll = rtc_dev_poll,
- .ioctl = rtc_dev_ioctl,
+ .unlocked_ioctl = rtc_dev_ioctl,
.open = rtc_dev_open,
.release = rtc_dev_release,
.fasync = rtc_dev_fasync,
diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c
new file mode 100644
index 00000000000..b91d02a3ace
--- /dev/null
+++ b/drivers/rtc/rtc-ds1305.c
@@ -0,0 +1,847 @@
+/*
+ * rtc-ds1305.c -- driver for DS1305 and DS1306 SPI RTC chips
+ *
+ * Copyright (C) 2008 David Brownell
+ *
+ * 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/bcd.h>
+#include <linux/rtc.h>
+#include <linux/workqueue.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/ds1305.h>
+
+
+/*
+ * Registers ... mask DS1305_WRITE into register address to write,
+ * otherwise you're reading it. All non-bitmask values are BCD.
+ */
+#define DS1305_WRITE 0x80
+
+
+/* RTC date/time ... the main special cases are that we:
+ * - Need fancy "hours" encoding in 12hour mode
+ * - Don't rely on the "day-of-week" field (or tm_wday)
+ * - Are a 21st-century clock (2000 <= year < 2100)
+ */
+#define DS1305_RTC_LEN 7 /* bytes for RTC regs */
+
+#define DS1305_SEC 0x00 /* register addresses */
+#define DS1305_MIN 0x01
+#define DS1305_HOUR 0x02
+# define DS1305_HR_12 0x40 /* set == 12 hr mode */
+# define DS1305_HR_PM 0x20 /* set == PM (12hr mode) */
+#define DS1305_WDAY 0x03
+#define DS1305_MDAY 0x04
+#define DS1305_MON 0x05
+#define DS1305_YEAR 0x06
+
+
+/* The two alarms have only sec/min/hour/wday fields (ALM_LEN).
+ * DS1305_ALM_DISABLE disables a match field (some combos are bad).
+ *
+ * NOTE that since we don't use WDAY, we limit ourselves to alarms
+ * only one day into the future (vs potentially up to a week).
+ *
+ * NOTE ALSO that while we could generate once-a-second IRQs (UIE), we
+ * don't currently support them. We'd either need to do it only when
+ * no alarm is pending (not the standard model), or to use the second
+ * alarm (implying that this is a DS1305 not DS1306, *and* that either
+ * it's wired up a second IRQ we know, or that INTCN is set)
+ */
+#define DS1305_ALM_LEN 4 /* bytes for ALM regs */
+#define DS1305_ALM_DISABLE 0x80
+
+#define DS1305_ALM0(r) (0x07 + (r)) /* register addresses */
+#define DS1305_ALM1(r) (0x0b + (r))
+
+
+/* three control registers */
+#define DS1305_CONTROL_LEN 3 /* bytes of control regs */
+
+#define DS1305_CONTROL 0x0f /* register addresses */
+# define DS1305_nEOSC 0x80 /* low enables oscillator */
+# define DS1305_WP 0x40 /* write protect */
+# define DS1305_INTCN 0x04 /* clear == only int0 used */
+# define DS1306_1HZ 0x04 /* enable 1Hz output */
+# define DS1305_AEI1 0x02 /* enable ALM1 IRQ */
+# define DS1305_AEI0 0x01 /* enable ALM0 IRQ */
+#define DS1305_STATUS 0x10
+/* status has just AEIx bits, mirrored as IRQFx */
+#define DS1305_TRICKLE 0x11
+/* trickle bits are defined in <linux/spi/ds1305.h> */
+
+/* a bunch of NVRAM */
+#define DS1305_NVRAM_LEN 96 /* bytes of NVRAM */
+
+#define DS1305_NVRAM 0x20 /* register addresses */
+
+
+struct ds1305 {
+ struct spi_device *spi;
+ struct rtc_device *rtc;
+
+ struct work_struct work;
+
+ unsigned long flags;
+#define FLAG_EXITING 0
+
+ bool hr12;
+ u8 ctrl[DS1305_CONTROL_LEN];
+};
+
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Utilities ... tolerate 12-hour AM/PM notation in case of non-Linux
+ * software (like a bootloader) which may require it.
+ */
+
+static unsigned bcd2hour(u8 bcd)
+{
+ if (bcd & DS1305_HR_12) {
+ unsigned hour = 0;
+
+ bcd &= ~DS1305_HR_12;
+ if (bcd & DS1305_HR_PM) {
+ hour = 12;
+ bcd &= ~DS1305_HR_PM;
+ }
+ hour += BCD2BIN(bcd);
+ return hour - 1;
+ }
+ return BCD2BIN(bcd);
+}
+
+static u8 hour2bcd(bool hr12, int hour)
+{
+ if (hr12) {
+ hour++;
+ if (hour <= 12)
+ return DS1305_HR_12 | BIN2BCD(hour);
+ hour -= 12;
+ return DS1305_HR_12 | DS1305_HR_PM | BIN2BCD(hour);
+ }
+ return BIN2BCD(hour);
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Interface to RTC framework
+ */
+
+#ifdef CONFIG_RTC_INTF_DEV
+
+/*
+ * Context: caller holds rtc->ops_lock (to protect ds1305->ctrl)
+ */
+static int ds1305_ioctl(struct device *dev, unsigned cmd, unsigned long arg)
+{
+ struct ds1305 *ds1305 = dev_get_drvdata(dev);
+ u8 buf[2];
+ int status = -ENOIOCTLCMD;
+
+ buf[0] = DS1305_WRITE | DS1305_CONTROL;
+ buf[1] = ds1305->ctrl[0];
+
+ switch (cmd) {
+ case RTC_AIE_OFF:
+ status = 0;
+ if (!(buf[1] & DS1305_AEI0))
+ goto done;
+ buf[1] &= ~DS1305_AEI0;
+ break;
+
+ case RTC_AIE_ON:
+ status = 0;
+ if (ds1305->ctrl[0] & DS1305_AEI0)
+ goto done;
+ buf[1] |= DS1305_AEI0;
+ break;
+ }
+ if (status == 0) {
+ status = spi_write_then_read(ds1305->spi, buf, sizeof buf,
+ NULL, 0);
+ if (status >= 0)
+ ds1305->ctrl[0] = buf[1];
+ }
+
+done:
+ return status;
+}
+
+#else
+#define ds1305_ioctl NULL
+#endif
+
+/*
+ * Get/set of date and time is pretty normal.
+ */
+
+static int ds1305_get_time(struct device *dev, struct rtc_time *time)
+{
+ struct ds1305 *ds1305 = dev_get_drvdata(dev);
+ u8 addr = DS1305_SEC;
+ u8 buf[DS1305_RTC_LEN];
+ int status;
+
+ /* Use write-then-read to get all the date/time registers
+ * since dma from stack is nonportable
+ */
+ status = spi_write_then_read(ds1305->spi, &addr, sizeof addr,
+ buf, sizeof buf);
+ if (status < 0)
+ return status;
+
+ dev_vdbg(dev, "%s: %02x %02x %02x, %02x %02x %02x %02x\n",
+ "read", buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6]);
+
+ /* Decode the registers */
+ time->tm_sec = BCD2BIN(buf[DS1305_SEC]);
+ time->tm_min = BCD2BIN(buf[DS1305_MIN]);
+ time->tm_hour = bcd2hour(buf[DS1305_HOUR]);
+ time->tm_wday = buf[DS1305_WDAY] - 1;
+ time->tm_mday = BCD2BIN(buf[DS1305_MDAY]);
+ time->tm_mon = BCD2BIN(buf[DS1305_MON]) - 1;
+ time->tm_year = BCD2BIN(buf[DS1305_YEAR]) + 100;
+
+ dev_vdbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "read", time->tm_sec, time->tm_min,
+ time->tm_hour, time->tm_mday,
+ time->tm_mon, time->tm_year, time->tm_wday);
+
+ /* Time may not be set */
+ return rtc_valid_tm(time);
+}
+
+static int ds1305_set_time(struct device *dev, struct rtc_time *time)
+{
+ struct ds1305 *ds1305 = dev_get_drvdata(dev);
+ u8 buf[1 + DS1305_RTC_LEN];
+ u8 *bp = buf;
+
+ dev_vdbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "write", time->tm_sec, time->tm_min,
+ time->tm_hour, time->tm_mday,
+ time->tm_mon, time->tm_year, time->tm_wday);
+
+ /* Write registers starting at the first time/date address. */
+ *bp++ = DS1305_WRITE | DS1305_SEC;
+
+ *bp++ = BIN2BCD(time->tm_sec);
+ *bp++ = BIN2BCD(time->tm_min);
+ *bp++ = hour2bcd(ds1305->hr12, time->tm_hour);
+ *bp++ = (time->tm_wday < 7) ? (time->tm_wday + 1) : 1;
+ *bp++ = BIN2BCD(time->tm_mday);
+ *bp++ = BIN2BCD(time->tm_mon + 1);
+ *bp++ = BIN2BCD(time->tm_year - 100);
+
+ dev_dbg(dev, "%s: %02x %02x %02x, %02x %02x %02x %02x\n",
+ "write", buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7]);
+
+ /* use write-then-read since dma from stack is nonportable */
+ return spi_write_then_read(ds1305->spi, buf, sizeof buf,
+ NULL, 0);
+}
+
+/*
+ * Get/set of alarm is a bit funky:
+ *
+ * - First there's the inherent raciness of getting the (partitioned)
+ * status of an alarm that could trigger while we're reading parts
+ * of that status.
+ *
+ * - Second there's its limited range (we could increase it a bit by
+ * relying on WDAY), which means it will easily roll over.
+ *
+ * - Third there's the choice of two alarms and alarm signals.
+ * Here we use ALM0 and expect that nINT0 (open drain) is used;
+ * that's the only real option for DS1306 runtime alarms, and is
+ * natural on DS1305.
+ *
+ * - Fourth, there's also ALM1, and a second interrupt signal:
+ * + On DS1305 ALM1 uses nINT1 (when INTCN=1) else nINT0;
+ * + On DS1306 ALM1 only uses INT1 (an active high pulse)
+ * and it won't work when VCC1 is active.
+ *
+ * So to be most general, we should probably set both alarms to the
+ * same value, letting ALM1 be the wakeup event source on DS1306
+ * and handling several wiring options on DS1305.
+ *
+ * - Fifth, we support the polled mode (as well as possible; why not?)
+ * even when no interrupt line is wired to an IRQ.
+ */
+
+/*
+ * Context: caller holds rtc->ops_lock (to protect ds1305->ctrl)
+ */
+static int ds1305_get_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct ds1305 *ds1305 = dev_get_drvdata(dev);
+ struct spi_device *spi = ds1305->spi;
+ u8 addr;
+ int status;
+ u8 buf[DS1305_ALM_LEN];
+
+ /* Refresh control register cache BEFORE reading ALM0 registers,
+ * since reading alarm registers acks any pending IRQ. That
+ * makes returning "pending" status a bit of a lie, but that bit
+ * of EFI status is at best fragile anyway (given IRQ handlers).
+ */
+ addr = DS1305_CONTROL;
+ status = spi_write_then_read(spi, &addr, sizeof addr,
+ ds1305->ctrl, sizeof ds1305->ctrl);
+ if (status < 0)
+ return status;
+
+ alm->enabled = !!(ds1305->ctrl[0] & DS1305_AEI0);
+ alm->pending = !!(ds1305->ctrl[1] & DS1305_AEI0);
+
+ /* get and check ALM0 registers */
+ addr = DS1305_ALM0(DS1305_SEC);
+ status = spi_write_then_read(spi, &addr, sizeof addr,
+ buf, sizeof buf);
+ if (status < 0)
+ return status;
+
+ dev_vdbg(dev, "%s: %02x %02x %02x %02x\n",
+ "alm0 read", buf[DS1305_SEC], buf[DS1305_MIN],
+ buf[DS1305_HOUR], buf[DS1305_WDAY]);
+
+ if ((DS1305_ALM_DISABLE & buf[DS1305_SEC])
+ || (DS1305_ALM_DISABLE & buf[DS1305_MIN])
+ || (DS1305_ALM_DISABLE & buf[DS1305_HOUR]))
+ return -EIO;
+
+ /* Stuff these values into alm->time and let RTC framework code
+ * fill in the rest ... and also handle rollover to tomorrow when
+ * that's needed.
+ */
+ alm->time.tm_sec = BCD2BIN(buf[DS1305_SEC]);
+ alm->time.tm_min = BCD2BIN(buf[DS1305_MIN]);
+ alm->time.tm_hour = bcd2hour(buf[DS1305_HOUR]);
+ alm->time.tm_mday = -1;
+ alm->time.tm_mon = -1;
+ alm->time.tm_year = -1;
+ /* next three fields are unused by Linux */
+ alm->time.tm_wday = -1;
+ alm->time.tm_mday = -1;
+ alm->time.tm_isdst = -1;
+
+ return 0;
+}
+
+/*
+ * Context: caller holds rtc->ops_lock (to protect ds1305->ctrl)
+ */
+static int ds1305_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct ds1305 *ds1305 = dev_get_drvdata(dev);
+ struct spi_device *spi = ds1305->spi;
+ unsigned long now, later;
+ struct rtc_time tm;
+ int status;
+ u8 buf[1 + DS1305_ALM_LEN];
+
+ /* convert desired alarm to time_t */
+ status = rtc_tm_to_time(&alm->time, &later);
+ if (status < 0)
+ return status;
+
+ /* Read current time as time_t */
+ status = ds1305_get_time(dev, &tm);
+ if (status < 0)
+ return status;
+ status = rtc_tm_to_time(&tm, &now);
+ if (status < 0)
+ return status;
+
+ /* make sure alarm fires within the next 24 hours */
+ if (later <= now)
+ return -EINVAL;
+ if ((later - now) > 24 * 60 * 60)
+ return -EDOM;
+
+ /* disable alarm if needed */
+ if (ds1305->ctrl[0] & DS1305_AEI0) {
+ ds1305->ctrl[0] &= ~DS1305_AEI0;
+
+ buf[0] = DS1305_WRITE | DS1305_CONTROL;
+ buf[1] = ds1305->ctrl[0];
+ status = spi_write_then_read(ds1305->spi, buf, 2, NULL, 0);
+ if (status < 0)
+ return status;
+ }
+
+ /* write alarm */
+ buf[0] = DS1305_WRITE | DS1305_ALM0(DS1305_SEC);
+ buf[1 + DS1305_SEC] = BIN2BCD(alm->time.tm_sec);
+ buf[1 + DS1305_MIN] = BIN2BCD(alm->time.tm_min);
+ buf[1 + DS1305_HOUR] = hour2bcd(ds1305->hr12, alm->time.tm_hour);
+ buf[1 + DS1305_WDAY] = DS1305_ALM_DISABLE;
+
+ dev_dbg(dev, "%s: %02x %02x %02x %02x\n",
+ "alm0 write", buf[1 + DS1305_SEC], buf[1 + DS1305_MIN],
+ buf[1 + DS1305_HOUR], buf[1 + DS1305_WDAY]);
+
+ status = spi_write_then_read(spi, buf, sizeof buf, NULL, 0);
+ if (status < 0)
+ return status;
+
+ /* enable alarm if requested */
+ if (alm->enabled) {
+ ds1305->ctrl[0] |= DS1305_AEI0;
+
+ buf[0] = DS1305_WRITE | DS1305_CONTROL;
+ buf[1] = ds1305->ctrl[0];
+ status = spi_write_then_read(ds1305->spi, buf, 2, NULL, 0);
+ }
+
+ return status;
+}
+
+#ifdef CONFIG_PROC_FS
+
+static int ds1305_proc(struct device *dev, struct seq_file *seq)
+{
+ struct ds1305 *ds1305 = dev_get_drvdata(dev);
+ char *diodes = "no";
+ char *resistors = "";
+
+ /* ctrl[2] is treated as read-only; no locking needed */
+ if ((ds1305->ctrl[2] & 0xf0) == DS1305_TRICKLE_MAGIC) {
+ switch (ds1305->ctrl[2] & 0x0c) {
+ case DS1305_TRICKLE_DS2:
+ diodes = "2 diodes, ";
+ break;
+ case DS1305_TRICKLE_DS1:
+ diodes = "1 diode, ";
+ break;
+ default:
+ goto done;
+ }
+ switch (ds1305->ctrl[2] & 0x03) {
+ case DS1305_TRICKLE_2K:
+ resistors = "2k Ohm";
+ break;
+ case DS1305_TRICKLE_4K:
+ resistors = "4k Ohm";
+ break;
+ case DS1305_TRICKLE_8K:
+ resistors = "8k Ohm";
+ break;
+ default:
+ diodes = "no";
+ break;
+ }
+ }
+
+done:
+ return seq_printf(seq,
+ "trickle_charge\t: %s%s\n",
+ diodes, resistors);
+}
+
+#else
+#define ds1305_proc NULL
+#endif
+
+static const struct rtc_class_ops ds1305_ops = {
+ .ioctl = ds1305_ioctl,
+ .read_time = ds1305_get_time,
+ .set_time = ds1305_set_time,
+ .read_alarm = ds1305_get_alarm,
+ .set_alarm = ds1305_set_alarm,
+ .proc = ds1305_proc,
+};
+
+static void ds1305_work(struct work_struct *work)
+{
+ struct ds1305 *ds1305 = container_of(work, struct ds1305, work);
+ struct mutex *lock = &ds1305->rtc->ops_lock;
+ struct spi_device *spi = ds1305->spi;
+ u8 buf[3];
+ int status;
+
+ /* lock to protect ds1305->ctrl */
+ mutex_lock(lock);
+
+ /* Disable the IRQ, and clear its status ... for now, we "know"
+ * that if more than one alarm is active, they're in sync.
+ * Note that reading ALM data registers also clears IRQ status.
+ */
+ ds1305->ctrl[0] &= ~(DS1305_AEI1 | DS1305_AEI0);
+ ds1305->ctrl[1] = 0;
+
+ buf[0] = DS1305_WRITE | DS1305_CONTROL;
+ buf[1] = ds1305->ctrl[0];
+ buf[2] = 0;
+
+ status = spi_write_then_read(spi, buf, sizeof buf,
+ NULL, 0);
+ if (status < 0)
+ dev_dbg(&spi->dev, "clear irq --> %d\n", status);
+
+ mutex_unlock(lock);
+
+ if (!test_bit(FLAG_EXITING, &ds1305->flags))
+ enable_irq(spi->irq);
+
+ /* rtc_update_irq() requires an IRQ-disabled context */
+ local_irq_disable();
+ rtc_update_irq(ds1305->rtc, 1, RTC_AF | RTC_IRQF);
+ local_irq_enable();
+}
+
+/*
+ * This "real" IRQ handler hands off to a workqueue mostly to allow
+ * mutex locking for ds1305->ctrl ... unlike I2C, we could issue async
+ * I/O requests in IRQ context (to clear the IRQ status).
+ */
+static irqreturn_t ds1305_irq(int irq, void *p)
+{
+ struct ds1305 *ds1305 = p;
+
+ disable_irq(irq);
+ schedule_work(&ds1305->work);
+ return IRQ_HANDLED;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Interface for NVRAM
+ */
+
+static void msg_init(struct spi_message *m, struct spi_transfer *x,
+ u8 *addr, size_t count, char *tx, char *rx)
+{
+ spi_message_init(m);
+ memset(x, 0, 2 * sizeof(*x));
+
+ x->tx_buf = addr;
+ x->len = 1;
+ spi_message_add_tail(x, m);
+
+ x++;
+
+ x->tx_buf = tx;
+ x->rx_buf = rx;
+ x->len = count;
+ spi_message_add_tail(x, m);
+}
+
+static ssize_t
+ds1305_nvram_read(struct kobject *kobj, struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct spi_device *spi;
+ u8 addr;
+ struct spi_message m;
+ struct spi_transfer x[2];
+ int status;
+
+ spi = container_of(kobj, struct spi_device, dev.kobj);
+
+ if (unlikely(off >= DS1305_NVRAM_LEN))
+ return 0;
+ if (count >= DS1305_NVRAM_LEN)
+ count = DS1305_NVRAM_LEN;
+ if ((off + count) > DS1305_NVRAM_LEN)
+ count = DS1305_NVRAM_LEN - off;
+ if (unlikely(!count))
+ return count;
+
+ addr = DS1305_NVRAM + off;
+ msg_init(&m, x, &addr, count, NULL, buf);
+
+ status = spi_sync(spi, &m);
+ if (status < 0)
+ dev_err(&spi->dev, "nvram %s error %d\n", "read", status);
+ return (status < 0) ? status : count;
+}
+
+static ssize_t
+ds1305_nvram_write(struct kobject *kobj, struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct spi_device *spi;
+ u8 addr;
+ struct spi_message m;
+ struct spi_transfer x[2];
+ int status;
+
+ spi = container_of(kobj, struct spi_device, dev.kobj);
+
+ if (unlikely(off >= DS1305_NVRAM_LEN))
+ return -EFBIG;
+ if (count >= DS1305_NVRAM_LEN)
+ count = DS1305_NVRAM_LEN;
+ if ((off + count) > DS1305_NVRAM_LEN)
+ count = DS1305_NVRAM_LEN - off;
+ if (unlikely(!count))
+ return count;
+
+ addr = (DS1305_WRITE | DS1305_NVRAM) + off;
+ msg_init(&m, x, &addr, count, buf, NULL);
+
+ status = spi_sync(spi, &m);
+ if (status < 0)
+ dev_err(&spi->dev, "nvram %s error %d\n", "write", status);
+ return (status < 0) ? status : count;
+}
+
+static struct bin_attribute nvram = {
+ .attr.name = "nvram",
+ .attr.mode = S_IRUGO | S_IWUSR,
+ .attr.owner = THIS_MODULE,
+ .read = ds1305_nvram_read,
+ .write = ds1305_nvram_write,
+ .size = DS1305_NVRAM_LEN,
+};
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Interface to SPI stack
+ */
+
+static int __devinit ds1305_probe(struct spi_device *spi)
+{
+ struct ds1305 *ds1305;
+ struct rtc_device *rtc;
+ int status;
+ u8 addr, value;
+ struct ds1305_platform_data *pdata = spi->dev.platform_data;
+ bool write_ctrl = false;
+
+ /* Sanity check board setup data. This may be hooked up
+ * in 3wire mode, but we don't care. Note that unless
+ * there's an inverter in place, this needs SPI_CS_HIGH!
+ */
+ if ((spi->bits_per_word && spi->bits_per_word != 8)
+ || (spi->max_speed_hz > 2000000)
+ || !(spi->mode & SPI_CPHA))
+ return -EINVAL;
+
+ /* set up driver data */
+ ds1305 = kzalloc(sizeof *ds1305, GFP_KERNEL);
+ if (!ds1305)
+ return -ENOMEM;
+ ds1305->spi = spi;
+ spi_set_drvdata(spi, ds1305);
+
+ /* read and cache control registers */
+ addr = DS1305_CONTROL;
+ status = spi_write_then_read(spi, &addr, sizeof addr,
+ ds1305->ctrl, sizeof ds1305->ctrl);
+ if (status < 0) {
+ dev_dbg(&spi->dev, "can't %s, %d\n",
+ "read", status);
+ goto fail0;
+ }
+
+ dev_dbg(&spi->dev, "ctrl %s: %02x %02x %02x\n",
+ "read", ds1305->ctrl[0],
+ ds1305->ctrl[1], ds1305->ctrl[2]);
+
+ /* Sanity check register values ... partially compensating for the
+ * fact that SPI has no device handshake. A pullup on MISO would
+ * make these tests fail; but not all systems will have one. If
+ * some register is neither 0x00 nor 0xff, a chip is likely there.
+ */
+ if ((ds1305->ctrl[0] & 0x38) != 0 || (ds1305->ctrl[1] & 0xfc) != 0) {
+ dev_dbg(&spi->dev, "RTC chip is not present\n");
+ status = -ENODEV;
+ goto fail0;
+ }
+ if (ds1305->ctrl[2] == 0)
+ dev_dbg(&spi->dev, "chip may not be present\n");
+
+ /* enable writes if needed ... if we were paranoid it would
+ * make sense to enable them only when absolutely necessary.
+ */
+ if (ds1305->ctrl[0] & DS1305_WP) {
+ u8 buf[2];
+
+ ds1305->ctrl[0] &= ~DS1305_WP;
+
+ buf[0] = DS1305_WRITE | DS1305_CONTROL;
+ buf[1] = ds1305->ctrl[0];
+ status = spi_write_then_read(spi, buf, sizeof buf, NULL, 0);
+
+ dev_dbg(&spi->dev, "clear WP --> %d\n", status);
+ if (status < 0)
+ goto fail0;
+ }
+
+ /* on DS1305, maybe start oscillator; like most low power
+ * oscillators, it may take a second to stabilize
+ */
+ if (ds1305->ctrl[0] & DS1305_nEOSC) {
+ ds1305->ctrl[0] &= ~DS1305_nEOSC;
+ write_ctrl = true;
+ dev_warn(&spi->dev, "SET TIME!\n");
+ }
+
+ /* ack any pending IRQs */
+ if (ds1305->ctrl[1]) {
+ ds1305->ctrl[1] = 0;
+ write_ctrl = true;
+ }
+
+ /* this may need one-time (re)init */
+ if (pdata) {
+ /* maybe enable trickle charge */
+ if (((ds1305->ctrl[2] & 0xf0) != DS1305_TRICKLE_MAGIC)) {
+ ds1305->ctrl[2] = DS1305_TRICKLE_MAGIC
+ | pdata->trickle;
+ write_ctrl = true;
+ }
+
+ /* on DS1306, configure 1 Hz signal */
+ if (pdata->is_ds1306) {
+ if (pdata->en_1hz) {
+ if (!(ds1305->ctrl[0] & DS1306_1HZ)) {
+ ds1305->ctrl[0] |= DS1306_1HZ;
+ write_ctrl = true;
+ }
+ } else {
+ if (ds1305->ctrl[0] & DS1306_1HZ) {
+ ds1305->ctrl[0] &= ~DS1306_1HZ;
+ write_ctrl = true;
+ }
+ }
+ }
+ }
+
+ if (write_ctrl) {
+ u8 buf[4];
+
+ buf[0] = DS1305_WRITE | DS1305_CONTROL;
+ buf[1] = ds1305->ctrl[0];
+ buf[2] = ds1305->ctrl[1];
+ buf[3] = ds1305->ctrl[2];
+ status = spi_write_then_read(spi, buf, sizeof buf, NULL, 0);
+ if (status < 0) {
+ dev_dbg(&spi->dev, "can't %s, %d\n",
+ "write", status);
+ goto fail0;
+ }
+
+ dev_dbg(&spi->dev, "ctrl %s: %02x %02x %02x\n",
+ "write", ds1305->ctrl[0],
+ ds1305->ctrl[1], ds1305->ctrl[2]);
+ }
+
+ /* see if non-Linux software set up AM/PM mode */
+ addr = DS1305_HOUR;
+ status = spi_write_then_read(spi, &addr, sizeof addr,
+ &value, sizeof value);
+ if (status < 0) {
+ dev_dbg(&spi->dev, "read HOUR --> %d\n", status);
+ goto fail0;
+ }
+
+ ds1305->hr12 = (DS1305_HR_12 & value) != 0;
+ if (ds1305->hr12)
+ dev_dbg(&spi->dev, "AM/PM\n");
+
+ /* register RTC ... from here on, ds1305->ctrl needs locking */
+ rtc = rtc_device_register("ds1305", &spi->dev,
+ &ds1305_ops, THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ status = PTR_ERR(rtc);
+ dev_dbg(&spi->dev, "register rtc --> %d\n", status);
+ goto fail0;
+ }
+ ds1305->rtc = rtc;
+
+ /* Maybe set up alarm IRQ; be ready to handle it triggering right
+ * away. NOTE that we don't share this. The signal is active low,
+ * and we can't ack it before a SPI message delay. We temporarily
+ * disable the IRQ until it's acked, which lets us work with more
+ * IRQ trigger modes (not all IRQ controllers can do falling edge).
+ */
+ if (spi->irq) {
+ INIT_WORK(&ds1305->work, ds1305_work);
+ status = request_irq(spi->irq, ds1305_irq,
+ 0, dev_name(&rtc->dev), ds1305);
+ if (status < 0) {
+ dev_dbg(&spi->dev, "request_irq %d --> %d\n",
+ spi->irq, status);
+ goto fail1;
+ }
+ }
+
+ /* export NVRAM */
+ status = sysfs_create_bin_file(&spi->dev.kobj, &nvram);
+ if (status < 0) {
+ dev_dbg(&spi->dev, "register nvram --> %d\n", status);
+ goto fail2;
+ }
+
+ return 0;
+
+fail2:
+ free_irq(spi->irq, ds1305);
+fail1:
+ rtc_device_unregister(rtc);
+fail0:
+ kfree(ds1305);
+ return status;
+}
+
+static int __devexit ds1305_remove(struct spi_device *spi)
+{
+ struct ds1305 *ds1305 = spi_get_drvdata(spi);
+
+ sysfs_remove_bin_file(&spi->dev.kobj, &nvram);
+
+ /* carefully shut down irq and workqueue, if present */
+ if (spi->irq) {
+ set_bit(FLAG_EXITING, &ds1305->flags);
+ free_irq(spi->irq, ds1305);
+ flush_scheduled_work();
+ }
+
+ rtc_device_unregister(ds1305->rtc);
+ spi_set_drvdata(spi, NULL);
+ kfree(ds1305);
+ return 0;
+}
+
+static struct spi_driver ds1305_driver = {
+ .driver.name = "rtc-ds1305",
+ .driver.owner = THIS_MODULE,
+ .probe = ds1305_probe,
+ .remove = __devexit_p(ds1305_remove),
+ /* REVISIT add suspend/resume */
+};
+
+static int __init ds1305_init(void)
+{
+ return spi_register_driver(&ds1305_driver);
+}
+module_init(ds1305_init);
+
+static void __exit ds1305_exit(void)
+{
+ spi_unregister_driver(&ds1305_driver);
+}
+module_exit(ds1305_exit);
+
+MODULE_DESCRIPTION("RTC driver for DS1305 and DS1306 chips");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index 0a19c06019b..24bc1689fc7 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -13,21 +13,21 @@
*
*/
-#include <linux/module.h>
+#include <linux/bcd.h>
+#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/string.h>
-#include <linux/i2c.h>
-#include <linux/rtc.h>
-#include <linux/bcd.h>
#ifdef CONFIG_RTC_DRV_M41T80_WDT
-#include <linux/miscdevice.h>
-#include <linux/watchdog.h>
-#include <linux/reboot.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
+#include <linux/miscdevice.h>
+#include <linux/reboot.h>
+#include <linux/watchdog.h>
#endif
#define M41T80_REG_SSEC 0
@@ -631,14 +631,12 @@ static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
return -EFAULT;
if (rv & WDIOS_DISABLECARD) {
- printk(KERN_INFO
- "rtc-m41t80: disable watchdog\n");
+ pr_info("rtc-m41t80: disable watchdog\n");
wdt_disable();
}
if (rv & WDIOS_ENABLECARD) {
- printk(KERN_INFO
- "rtc-m41t80: enable watchdog\n");
+ pr_info("rtc-m41t80: enable watchdog\n");
wdt_ping();
}
diff --git a/drivers/rtc/rtc-m41t94.c b/drivers/rtc/rtc-m41t94.c
new file mode 100644
index 00000000000..9b19499c829
--- /dev/null
+++ b/drivers/rtc/rtc-m41t94.c
@@ -0,0 +1,173 @@
+/*
+ * Driver for ST M41T94 SPI RTC
+ *
+ * Copyright (C) 2008 Kim B. Heino
+ *
+ * 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/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/spi/spi.h>
+#include <linux/bcd.h>
+
+#define M41T94_REG_SECONDS 0x01
+#define M41T94_REG_MINUTES 0x02
+#define M41T94_REG_HOURS 0x03
+#define M41T94_REG_WDAY 0x04
+#define M41T94_REG_DAY 0x05
+#define M41T94_REG_MONTH 0x06
+#define M41T94_REG_YEAR 0x07
+#define M41T94_REG_HT 0x0c
+
+#define M41T94_BIT_HALT 0x40
+#define M41T94_BIT_STOP 0x80
+#define M41T94_BIT_CB 0x40
+#define M41T94_BIT_CEB 0x80
+
+static int m41t94_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ u8 buf[8]; /* write cmd + 7 registers */
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "write", tm->tm_sec, tm->tm_min,
+ tm->tm_hour, tm->tm_mday,
+ tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ buf[0] = 0x80 | M41T94_REG_SECONDS; /* write time + date */
+ buf[M41T94_REG_SECONDS] = BIN2BCD(tm->tm_sec);
+ buf[M41T94_REG_MINUTES] = BIN2BCD(tm->tm_min);
+ buf[M41T94_REG_HOURS] = BIN2BCD(tm->tm_hour);
+ buf[M41T94_REG_WDAY] = BIN2BCD(tm->tm_wday + 1);
+ buf[M41T94_REG_DAY] = BIN2BCD(tm->tm_mday);
+ buf[M41T94_REG_MONTH] = BIN2BCD(tm->tm_mon + 1);
+
+ buf[M41T94_REG_HOURS] |= M41T94_BIT_CEB;
+ if (tm->tm_year >= 100)
+ buf[M41T94_REG_HOURS] |= M41T94_BIT_CB;
+ buf[M41T94_REG_YEAR] = BIN2BCD(tm->tm_year % 100);
+
+ return spi_write(spi, buf, 8);
+}
+
+static int m41t94_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ u8 buf[2];
+ int ret, hour;
+
+ /* clear halt update bit */
+ ret = spi_w8r8(spi, M41T94_REG_HT);
+ if (ret < 0)
+ return ret;
+ if (ret & M41T94_BIT_HALT) {
+ buf[0] = 0x80 | M41T94_REG_HT;
+ buf[1] = ret & ~M41T94_BIT_HALT;
+ spi_write(spi, buf, 2);
+ }
+
+ /* clear stop bit */
+ ret = spi_w8r8(spi, M41T94_REG_SECONDS);
+ if (ret < 0)
+ return ret;
+ if (ret & M41T94_BIT_STOP) {
+ buf[0] = 0x80 | M41T94_REG_SECONDS;
+ buf[1] = ret & ~M41T94_BIT_STOP;
+ spi_write(spi, buf, 2);
+ }
+
+ tm->tm_sec = BCD2BIN(spi_w8r8(spi, M41T94_REG_SECONDS));
+ tm->tm_min = BCD2BIN(spi_w8r8(spi, M41T94_REG_MINUTES));
+ hour = spi_w8r8(spi, M41T94_REG_HOURS);
+ tm->tm_hour = BCD2BIN(hour & 0x3f);
+ tm->tm_wday = BCD2BIN(spi_w8r8(spi, M41T94_REG_WDAY)) - 1;
+ tm->tm_mday = BCD2BIN(spi_w8r8(spi, M41T94_REG_DAY));
+ tm->tm_mon = BCD2BIN(spi_w8r8(spi, M41T94_REG_MONTH)) - 1;
+ tm->tm_year = BCD2BIN(spi_w8r8(spi, M41T94_REG_YEAR));
+ if ((hour & M41T94_BIT_CB) || !(hour & M41T94_BIT_CEB))
+ tm->tm_year += 100;
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "read", tm->tm_sec, tm->tm_min,
+ tm->tm_hour, tm->tm_mday,
+ tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ /* initial clock setting can be undefined */
+ return rtc_valid_tm(tm);
+}
+
+static const struct rtc_class_ops m41t94_rtc_ops = {
+ .read_time = m41t94_read_time,
+ .set_time = m41t94_set_time,
+};
+
+static struct spi_driver m41t94_driver;
+
+static int __devinit m41t94_probe(struct spi_device *spi)
+{
+ struct rtc_device *rtc;
+ int res;
+
+ spi->bits_per_word = 8;
+ spi_setup(spi);
+
+ res = spi_w8r8(spi, M41T94_REG_SECONDS);
+ if (res < 0) {
+ dev_err(&spi->dev, "not found.\n");
+ return res;
+ }
+
+ rtc = rtc_device_register(m41t94_driver.driver.name,
+ &spi->dev, &m41t94_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ dev_set_drvdata(&spi->dev, rtc);
+
+ return 0;
+}
+
+static int __devexit m41t94_remove(struct spi_device *spi)
+{
+ struct rtc_device *rtc = platform_get_drvdata(spi);
+
+ if (rtc)
+ rtc_device_unregister(rtc);
+
+ return 0;
+}
+
+static struct spi_driver m41t94_driver = {
+ .driver = {
+ .name = "rtc-m41t94",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = m41t94_probe,
+ .remove = __devexit_p(m41t94_remove),
+};
+
+static __init int m41t94_init(void)
+{
+ return spi_register_driver(&m41t94_driver);
+}
+
+module_init(m41t94_init);
+
+static __exit void m41t94_exit(void)
+{
+ spi_unregister_driver(&m41t94_driver);
+}
+
+module_exit(m41t94_exit);
+
+MODULE_AUTHOR("Kim B. Heino <Kim.Heino@bluegiga.com>");
+MODULE_DESCRIPTION("Driver for ST M41T94 SPI RTC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
index eb23d8423f4..8876605d4d4 100644
--- a/drivers/rtc/rtc-omap.c
+++ b/drivers/rtc/rtc-omap.c
@@ -92,18 +92,6 @@
#define rtc_write(val, addr) omap_writeb(val, OMAP_RTC_BASE + (addr))
-/* platform_bus isn't hotpluggable, so for static linkage it'd be safe
- * to get rid of probe() and remove() code ... too bad the driver struct
- * remembers probe(), that's about 25% of the runtime footprint!!
- */
-#ifndef MODULE
-#undef __devexit
-#undef __devexit_p
-#define __devexit __exit
-#define __devexit_p __exit_p
-#endif
-
-
/* we rely on the rtc framework to handle locking (rtc->ops_lock),
* so the only other requirement is that register accesses which
* require BUSY to be clear are made with IRQs locally disabled
@@ -324,7 +312,7 @@ static struct rtc_class_ops omap_rtc_ops = {
static int omap_rtc_alarm;
static int omap_rtc_timer;
-static int __devinit omap_rtc_probe(struct platform_device *pdev)
+static int __init omap_rtc_probe(struct platform_device *pdev)
{
struct resource *res, *mem;
struct rtc_device *rtc;
@@ -440,7 +428,7 @@ fail:
return -EIO;
}
-static int __devexit omap_rtc_remove(struct platform_device *pdev)
+static int __exit omap_rtc_remove(struct platform_device *pdev)
{
struct rtc_device *rtc = platform_get_drvdata(pdev);;
@@ -498,8 +486,7 @@ static void omap_rtc_shutdown(struct platform_device *pdev)
MODULE_ALIAS("platform:omap_rtc");
static struct platform_driver omap_rtc_driver = {
- .probe = omap_rtc_probe,
- .remove = __devexit_p(omap_rtc_remove),
+ .remove = __exit_p(omap_rtc_remove),
.suspend = omap_rtc_suspend,
.resume = omap_rtc_resume,
.shutdown = omap_rtc_shutdown,
@@ -511,7 +498,7 @@ static struct platform_driver omap_rtc_driver = {
static int __init rtc_init(void)
{
- return platform_driver_register(&omap_rtc_driver);
+ return platform_driver_probe(&omap_rtc_driver, omap_rtc_probe);
}
module_init(rtc_init);
diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c
index 3d09d8f0b1f..d388c662bf4 100644
--- a/drivers/rtc/rtc-pcf8583.c
+++ b/drivers/rtc/rtc-pcf8583.c
@@ -2,6 +2,7 @@
* drivers/rtc/rtc-pcf8583.c
*
* Copyright (C) 2000 Russell King
+ * Copyright (C) 2008 Wolfram Sang & Juergen Beisert, Pengutronix
*
* 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
@@ -14,7 +15,6 @@
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
-#include <linux/string.h>
#include <linux/rtc.h>
#include <linux/init.h>
#include <linux/errno.h>
@@ -27,7 +27,6 @@ struct rtc_mem {
};
struct pcf8583 {
- struct i2c_client client;
struct rtc_device *rtc;
unsigned char ctrl;
};
@@ -40,10 +39,6 @@ struct pcf8583 {
#define CTRL_ALARM 0x02
#define CTRL_TIMER 0x01
-static const unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END };
-
-/* Module parameters */
-I2C_CLIENT_INSMOD;
static struct i2c_driver pcf8583_driver;
@@ -269,106 +264,60 @@ static const struct rtc_class_ops pcf8583_rtc_ops = {
.set_time = pcf8583_rtc_set_time,
};
-static int pcf8583_probe(struct i2c_adapter *adap, int addr, int kind);
-
-static int pcf8583_attach(struct i2c_adapter *adap)
-{
- return i2c_probe(adap, &addr_data, pcf8583_probe);
-}
-
-static int pcf8583_detach(struct i2c_client *client)
-{
- int err;
- struct pcf8583 *pcf = i2c_get_clientdata(client);
- struct rtc_device *rtc = pcf->rtc;
-
- if (rtc)
- rtc_device_unregister(rtc);
-
- if ((err = i2c_detach_client(client)))
- return err;
-
- kfree(pcf);
- return 0;
-}
-
-static struct i2c_driver pcf8583_driver = {
- .driver = {
- .name = "pcf8583",
- },
- .id = I2C_DRIVERID_PCF8583,
- .attach_adapter = pcf8583_attach,
- .detach_client = pcf8583_detach,
-};
-
-static int pcf8583_probe(struct i2c_adapter *adap, int addr, int kind)
+static int pcf8583_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
- struct pcf8583 *pcf;
- struct i2c_client *client;
- struct rtc_device *rtc;
- unsigned char buf[1], ad[1] = { 0 };
+ struct pcf8583 *pcf8583;
int err;
- struct i2c_msg msgs[2] = {
- {
- .addr = addr,
- .flags = 0,
- .len = 1,
- .buf = ad,
- }, {
- .addr = addr,
- .flags = I2C_M_RD,
- .len = 1,
- .buf = buf,
- }
- };
- if (!i2c_check_functionality(adap, I2C_FUNC_I2C))
- return 0;
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
- pcf = kzalloc(sizeof(*pcf), GFP_KERNEL);
- if (!pcf)
+ pcf8583 = kzalloc(sizeof(struct pcf8583), GFP_KERNEL);
+ if (!pcf8583)
return -ENOMEM;
- client = &pcf->client;
+ pcf8583->rtc = rtc_device_register(pcf8583_driver.driver.name,
+ &client->dev, &pcf8583_rtc_ops, THIS_MODULE);
- client->addr = addr;
- client->adapter = adap;
- client->driver = &pcf8583_driver;
-
- strlcpy(client->name, pcf8583_driver.driver.name, I2C_NAME_SIZE);
-
- if (i2c_transfer(client->adapter, msgs, 2) != 2) {
- err = -EIO;
+ if (IS_ERR(pcf8583->rtc)) {
+ err = PTR_ERR(pcf8583->rtc);
goto exit_kfree;
}
- err = i2c_attach_client(client);
-
- if (err)
- goto exit_kfree;
-
- rtc = rtc_device_register(pcf8583_driver.driver.name, &client->dev,
- &pcf8583_rtc_ops, THIS_MODULE);
+ i2c_set_clientdata(client, pcf8583);
+ return 0;
- if (IS_ERR(rtc)) {
- err = PTR_ERR(rtc);
- goto exit_detach;
- }
+exit_kfree:
+ kfree(pcf8583);
+ return err;
+}
- pcf->rtc = rtc;
- i2c_set_clientdata(client, pcf);
- set_ctrl(client, buf[0]);
+static int __devexit pcf8583_remove(struct i2c_client *client)
+{
+ struct pcf8583 *pcf8583 = i2c_get_clientdata(client);
+ if (pcf8583->rtc)
+ rtc_device_unregister(pcf8583->rtc);
+ kfree(pcf8583);
return 0;
+}
-exit_detach:
- i2c_detach_client(client);
-
-exit_kfree:
- kfree(pcf);
+static const struct i2c_device_id pcf8583_id[] = {
+ { "pcf8583", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcf8583_id);
- return err;
-}
+static struct i2c_driver pcf8583_driver = {
+ .driver = {
+ .name = "pcf8583",
+ .owner = THIS_MODULE,
+ },
+ .probe = pcf8583_probe,
+ .remove = __devexit_p(pcf8583_remove),
+ .id_table = pcf8583_id,
+};
static __init int pcf8583_init(void)
{
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index fed86e507fd..54b1ebb0150 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -36,10 +36,8 @@ static struct resource *s3c_rtc_mem;
static void __iomem *s3c_rtc_base;
static int s3c_rtc_alarmno = NO_IRQ;
static int s3c_rtc_tickno = NO_IRQ;
-static int s3c_rtc_freq = 1;
static DEFINE_SPINLOCK(s3c_rtc_pie_lock);
-static unsigned int tick_count;
/* IRQ Handlers */
@@ -55,7 +53,7 @@ static irqreturn_t s3c_rtc_tickirq(int irq, void *id)
{
struct rtc_device *rdev = id;
- rtc_update_irq(rdev, tick_count++, RTC_PF | RTC_IRQF);
+ rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);
return IRQ_HANDLED;
}
@@ -74,35 +72,37 @@ static void s3c_rtc_setaie(int to)
writeb(tmp, s3c_rtc_base + S3C2410_RTCALM);
}
-static void s3c_rtc_setpie(int to)
+static int s3c_rtc_setpie(struct device *dev, int enabled)
{
unsigned int tmp;
- pr_debug("%s: pie=%d\n", __func__, to);
+ pr_debug("%s: pie=%d\n", __func__, enabled);
spin_lock_irq(&s3c_rtc_pie_lock);
tmp = readb(s3c_rtc_base + S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE;
- if (to)
+ if (enabled)
tmp |= S3C2410_TICNT_ENABLE;
writeb(tmp, s3c_rtc_base + S3C2410_TICNT);
spin_unlock_irq(&s3c_rtc_pie_lock);
+
+ return 0;
}
-static void s3c_rtc_setfreq(int freq)
+static int s3c_rtc_setfreq(struct device *dev, int freq)
{
unsigned int tmp;
spin_lock_irq(&s3c_rtc_pie_lock);
- tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE;
-
- s3c_rtc_freq = freq;
+ tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE;
tmp |= (128 / freq)-1;
writeb(tmp, s3c_rtc_base + S3C2410_TICNT);
spin_unlock_irq(&s3c_rtc_pie_lock);
+
+ return 0;
}
/* Time read/write */
@@ -267,12 +267,7 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
writeb(alrm_en, base + S3C2410_RTCALM);
- if (0) {
- alrm_en = readb(base + S3C2410_RTCALM);
- alrm_en &= ~S3C2410_RTCALM_ALMEN;
- writeb(alrm_en, base + S3C2410_RTCALM);
- disable_irq_wake(s3c_rtc_alarmno);
- }
+ s3c_rtc_setaie(alrm->enabled);
if (alrm->enabled)
enable_irq_wake(s3c_rtc_alarmno);
@@ -282,59 +277,12 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
return 0;
}
-static int s3c_rtc_ioctl(struct device *dev,
- unsigned int cmd, unsigned long arg)
-{
- unsigned int ret = -ENOIOCTLCMD;
-
- switch (cmd) {
- case RTC_AIE_OFF:
- case RTC_AIE_ON:
- s3c_rtc_setaie((cmd == RTC_AIE_ON) ? 1 : 0);
- ret = 0;
- break;
-
- case RTC_PIE_OFF:
- case RTC_PIE_ON:
- tick_count = 0;
- s3c_rtc_setpie((cmd == RTC_PIE_ON) ? 1 : 0);
- ret = 0;
- break;
-
- case RTC_IRQP_READ:
- ret = put_user(s3c_rtc_freq, (unsigned long __user *)arg);
- break;
-
- case RTC_IRQP_SET:
- if (!is_power_of_2(arg)) {
- ret = -EINVAL;
- goto exit;
- }
-
- pr_debug("s3c2410_rtc: setting frequency %ld\n", arg);
-
- s3c_rtc_setfreq(arg);
- ret = 0;
- break;
-
- case RTC_UIE_ON:
- case RTC_UIE_OFF:
- ret = -EINVAL;
- }
-
- exit:
- return ret;
-}
-
static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
{
unsigned int ticnt = readb(s3c_rtc_base + S3C2410_TICNT);
seq_printf(seq, "periodic_IRQ\t: %s\n",
(ticnt & S3C2410_TICNT_ENABLE) ? "yes" : "no" );
-
- seq_printf(seq, "periodic_freq\t: %d\n", s3c_rtc_freq);
-
return 0;
}
@@ -374,7 +322,7 @@ static void s3c_rtc_release(struct device *dev)
/* do not clear AIE here, it may be needed for wake */
- s3c_rtc_setpie(0);
+ s3c_rtc_setpie(dev, 0);
free_irq(s3c_rtc_alarmno, rtc_dev);
free_irq(s3c_rtc_tickno, rtc_dev);
}
@@ -382,11 +330,12 @@ static void s3c_rtc_release(struct device *dev)
static const struct rtc_class_ops s3c_rtcops = {
.open = s3c_rtc_open,
.release = s3c_rtc_release,
- .ioctl = s3c_rtc_ioctl,
.read_time = s3c_rtc_gettime,
.set_time = s3c_rtc_settime,
.read_alarm = s3c_rtc_getalarm,
.set_alarm = s3c_rtc_setalarm,
+ .irq_set_freq = s3c_rtc_setfreq,
+ .irq_set_state = s3c_rtc_setpie,
.proc = s3c_rtc_proc,
};
@@ -430,14 +379,14 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en)
}
}
-static int s3c_rtc_remove(struct platform_device *dev)
+static int __devexit s3c_rtc_remove(struct platform_device *dev)
{
struct rtc_device *rtc = platform_get_drvdata(dev);
platform_set_drvdata(dev, NULL);
rtc_device_unregister(rtc);
- s3c_rtc_setpie(0);
+ s3c_rtc_setpie(&dev->dev, 0);
s3c_rtc_setaie(0);
iounmap(s3c_rtc_base);
@@ -447,7 +396,7 @@ static int s3c_rtc_remove(struct platform_device *dev)
return 0;
}
-static int s3c_rtc_probe(struct platform_device *pdev)
+static int __devinit s3c_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
struct resource *res;
@@ -504,7 +453,7 @@ static int s3c_rtc_probe(struct platform_device *pdev)
pr_debug("s3c2410_rtc: RTCCON=%02x\n",
readb(s3c_rtc_base + S3C2410_RTCCON));
- s3c_rtc_setfreq(s3c_rtc_freq);
+ s3c_rtc_setfreq(&pdev->dev, 1);
/* register RTC and exit */
@@ -560,7 +509,7 @@ static int s3c_rtc_resume(struct platform_device *pdev)
static struct platform_driver s3c2410_rtcdrv = {
.probe = s3c_rtc_probe,
- .remove = s3c_rtc_remove,
+ .remove = __devexit_p(s3c_rtc_remove),
.suspend = s3c_rtc_suspend,
.resume = s3c_rtc_resume,
.driver = {
diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c
index be9c70d0b19..884b635f028 100644
--- a/drivers/rtc/rtc-vr41xx.c
+++ b/drivers/rtc/rtc-vr41xx.c
@@ -1,7 +1,7 @@
/*
* Driver for NEC VR4100 series Real Time Clock unit.
*
- * Copyright (C) 2003-2006 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
+ * Copyright (C) 2003-2008 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
*
* 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
@@ -34,7 +34,7 @@
MODULE_AUTHOR("Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>");
MODULE_DESCRIPTION("NEC VR4100 series RTC driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
/* RTC 1 registers */
#define ETIMELREG 0x00
@@ -82,7 +82,6 @@ static unsigned long epoch = 1970; /* Jan 1 1970 00:00:00 */
static DEFINE_SPINLOCK(rtc_lock);
static char rtc_name[] = "RTC";
-static unsigned long periodic_frequency;
static unsigned long periodic_count;
static unsigned int alarm_enabled;
static int aie_irq = -1;
@@ -207,10 +206,37 @@ static int vr41xx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
return 0;
}
-static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+static int vr41xx_rtc_irq_set_freq(struct device *dev, int freq)
{
unsigned long count;
+ count = RTC_FREQUENCY;
+ do_div(count, freq);
+
+ periodic_count = count;
+
+ spin_lock_irq(&rtc_lock);
+
+ rtc1_write(RTCL1LREG, count);
+ rtc1_write(RTCL1HREG, count >> 16);
+
+ spin_unlock_irq(&rtc_lock);
+
+ return 0;
+}
+
+static int vr41xx_rtc_irq_set_state(struct device *dev, int enabled)
+{
+ if (enabled)
+ enable_irq(pie_irq);
+ else
+ disable_irq(pie_irq);
+
+ return 0;
+}
+
+static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
switch (cmd) {
case RTC_AIE_ON:
spin_lock_irq(&rtc_lock);
@@ -232,33 +258,6 @@ static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long
spin_unlock_irq(&rtc_lock);
break;
- case RTC_PIE_ON:
- enable_irq(pie_irq);
- break;
- case RTC_PIE_OFF:
- disable_irq(pie_irq);
- break;
- case RTC_IRQP_READ:
- return put_user(periodic_frequency, (unsigned long __user *)arg);
- break;
- case RTC_IRQP_SET:
- if (arg > MAX_PERIODIC_RATE)
- return -EINVAL;
-
- periodic_frequency = arg;
-
- count = RTC_FREQUENCY;
- do_div(count, arg);
-
- periodic_count = count;
-
- spin_lock_irq(&rtc_lock);
-
- rtc1_write(RTCL1LREG, count);
- rtc1_write(RTCL1HREG, count >> 16);
-
- spin_unlock_irq(&rtc_lock);
- break;
case RTC_EPOCH_READ:
return put_user(epoch, (unsigned long __user *)arg);
case RTC_EPOCH_SET:
@@ -309,6 +308,8 @@ static const struct rtc_class_ops vr41xx_rtc_ops = {
.set_time = vr41xx_rtc_set_time,
.read_alarm = vr41xx_rtc_read_alarm,
.set_alarm = vr41xx_rtc_set_alarm,
+ .irq_set_freq = vr41xx_rtc_irq_set_freq,
+ .irq_set_state = vr41xx_rtc_irq_set_state,
};
static int __devinit rtc_probe(struct platform_device *pdev)
@@ -346,6 +347,8 @@ static int __devinit rtc_probe(struct platform_device *pdev)
goto err_iounmap_all;
}
+ rtc->max_user_freq = MAX_PERIODIC_RATE;
+
spin_lock_irq(&rtc_lock);
rtc1_write(ECMPLREG, 0);
diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c
index 5ab34340919..79954bd6bfa 100644
--- a/drivers/s390/kvm/kvm_virtio.c
+++ b/drivers/s390/kvm/kvm_virtio.c
@@ -15,6 +15,7 @@
#include <linux/err.h>
#include <linux/virtio.h>
#include <linux/virtio_config.h>
+#include <linux/virtio_console.h>
#include <linux/interrupt.h>
#include <linux/virtio_ring.h>
#include <linux/pfn.h>
@@ -87,16 +88,20 @@ static u32 kvm_get_features(struct virtio_device *vdev)
return features;
}
-static void kvm_set_features(struct virtio_device *vdev, u32 features)
+static void kvm_finalize_features(struct virtio_device *vdev)
{
- unsigned int i;
+ unsigned int i, bits;
struct kvm_device_desc *desc = to_kvmdev(vdev)->desc;
/* Second half of bitmap is features we accept. */
u8 *out_features = kvm_vq_features(desc) + desc->feature_len;
+ /* Give virtio_ring a chance to accept features. */
+ vring_transport_features(vdev);
+
memset(out_features, 0, desc->feature_len);
- for (i = 0; i < min(desc->feature_len * 8, 32); i++) {
- if (features & (1 << i))
+ bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8;
+ for (i = 0; i < bits; i++) {
+ if (test_bit(i, vdev->features))
out_features[i / 8] |= (1 << (i % 8));
}
}
@@ -222,7 +227,7 @@ static void kvm_del_vq(struct virtqueue *vq)
*/
static struct virtio_config_ops kvm_vq_configspace_ops = {
.get_features = kvm_get_features,
- .set_features = kvm_set_features,
+ .finalize_features = kvm_finalize_features,
.get = kvm_get,
.set = kvm_set,
.get_status = kvm_get_status,
@@ -333,6 +338,25 @@ static int __init kvm_devices_init(void)
return 0;
}
+/* code for early console output with virtio_console */
+static __init int early_put_chars(u32 vtermno, const char *buf, int count)
+{
+ char scratch[17];
+ unsigned int len = count;
+
+ if (len > sizeof(scratch) - 1)
+ len = sizeof(scratch) - 1;
+ scratch[len] = '\0';
+ memcpy(scratch, buf, len);
+ kvm_hypercall1(KVM_S390_VIRTIO_NOTIFY, __pa(scratch));
+ return len;
+}
+
+void s390_virtio_console_init(void)
+{
+ virtio_cons_early_init(early_put_chars);
+}
+
/*
* We do this after core stuff, but before the drivers.
*/
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index eb702b96d57..c4a7c06793c 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -3819,6 +3819,20 @@ static int ibmvfc_remove(struct vio_dev *vdev)
return 0;
}
+/**
+ * ibmvfc_get_desired_dma - Calculate DMA resources needed by the driver
+ * @vdev: vio device struct
+ *
+ * Return value:
+ * Number of bytes the driver will need to DMA map at the same time in
+ * order to perform well.
+ */
+static unsigned long ibmvfc_get_desired_dma(struct vio_dev *vdev)
+{
+ unsigned long pool_dma = max_requests * sizeof(union ibmvfc_iu);
+ return pool_dma + ((512 * 1024) * driver_template.cmd_per_lun);
+}
+
static struct vio_device_id ibmvfc_device_table[] __devinitdata = {
{"fcp", "IBM,vfc-client"},
{ "", "" }
@@ -3829,6 +3843,7 @@ static struct vio_driver ibmvfc_driver = {
.id_table = ibmvfc_device_table,
.probe = ibmvfc_probe,
.remove = ibmvfc_remove,
+ .get_desired_dma = ibmvfc_get_desired_dma,
.driver = {
.name = IBMVFC_NAME,
.owner = THIS_MODULE,
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c
index 5d23368a1bc..20000ec79b0 100644
--- a/drivers/scsi/ibmvscsi/ibmvscsi.c
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.c
@@ -72,6 +72,7 @@
#include <linux/delay.h>
#include <asm/firmware.h>
#include <asm/vio.h>
+#include <asm/firmware.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_host.h>
@@ -426,8 +427,10 @@ static int map_sg_data(struct scsi_cmnd *cmd,
SG_ALL * sizeof(struct srp_direct_buf),
&evt_struct->ext_list_token, 0);
if (!evt_struct->ext_list) {
- sdev_printk(KERN_ERR, cmd->device,
- "Can't allocate memory for indirect table\n");
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ sdev_printk(KERN_ERR, cmd->device,
+ "Can't allocate memory "
+ "for indirect table\n");
return 0;
}
}
@@ -743,7 +746,9 @@ static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd,
srp_cmd->lun = ((u64) lun) << 48;
if (!map_data_for_srp_cmd(cmnd, evt_struct, srp_cmd, hostdata->dev)) {
- sdev_printk(KERN_ERR, cmnd->device, "couldn't convert cmd to srp_cmd\n");
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ sdev_printk(KERN_ERR, cmnd->device,
+ "couldn't convert cmd to srp_cmd\n");
free_event_struct(&hostdata->pool, evt_struct);
return SCSI_MLQUEUE_HOST_BUSY;
}
@@ -855,7 +860,10 @@ static void send_mad_adapter_info(struct ibmvscsi_host_data *hostdata)
DMA_BIDIRECTIONAL);
if (dma_mapping_error(req->buffer)) {
- dev_err(hostdata->dev, "Unable to map request_buffer for adapter_info!\n");
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ dev_err(hostdata->dev,
+ "Unable to map request_buffer for "
+ "adapter_info!\n");
free_event_struct(&hostdata->pool, evt_struct);
return;
}
@@ -1400,7 +1408,9 @@ static int ibmvscsi_do_host_config(struct ibmvscsi_host_data *hostdata,
DMA_BIDIRECTIONAL);
if (dma_mapping_error(host_config->buffer)) {
- dev_err(hostdata->dev, "dma_mapping error getting host config\n");
+ if (!firmware_has_feature(FW_FEATURE_CMO))
+ dev_err(hostdata->dev,
+ "dma_mapping error getting host config\n");
free_event_struct(&hostdata->pool, evt_struct);
return -1;
}
@@ -1604,7 +1614,7 @@ static struct scsi_host_template driver_template = {
.eh_host_reset_handler = ibmvscsi_eh_host_reset_handler,
.slave_configure = ibmvscsi_slave_configure,
.change_queue_depth = ibmvscsi_change_queue_depth,
- .cmd_per_lun = 16,
+ .cmd_per_lun = IBMVSCSI_CMDS_PER_LUN_DEFAULT,
.can_queue = IBMVSCSI_MAX_REQUESTS_DEFAULT,
.this_id = -1,
.sg_tablesize = SG_ALL,
@@ -1613,6 +1623,26 @@ static struct scsi_host_template driver_template = {
};
/**
+ * ibmvscsi_get_desired_dma - Calculate IO memory desired by the driver
+ *
+ * @vdev: struct vio_dev for the device whose desired IO mem is to be returned
+ *
+ * Return value:
+ * Number of bytes of IO data the driver will need to perform well.
+ */
+static unsigned long ibmvscsi_get_desired_dma(struct vio_dev *vdev)
+{
+ /* iu_storage data allocated in initialize_event_pool */
+ unsigned long desired_io = max_requests * sizeof(union viosrp_iu);
+
+ /* add io space for sg data */
+ desired_io += (IBMVSCSI_MAX_SECTORS_DEFAULT *
+ IBMVSCSI_CMDS_PER_LUN_DEFAULT);
+
+ return desired_io;
+}
+
+/**
* Called by bus code for each adapter
*/
static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
@@ -1641,7 +1671,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id)
hostdata->host = host;
hostdata->dev = dev;
atomic_set(&hostdata->request_limit, -1);
- hostdata->host->max_sectors = 32 * 8; /* default max I/O 32 pages */
+ hostdata->host->max_sectors = IBMVSCSI_MAX_SECTORS_DEFAULT;
rc = ibmvscsi_ops->init_crq_queue(&hostdata->queue, hostdata, max_requests);
if (rc != 0 && rc != H_RESOURCE) {
@@ -1735,6 +1765,7 @@ static struct vio_driver ibmvscsi_driver = {
.id_table = ibmvscsi_device_table,
.probe = ibmvscsi_probe,
.remove = ibmvscsi_remove,
+ .get_desired_dma = ibmvscsi_get_desired_dma,
.driver = {
.name = "ibmvscsi",
.owner = THIS_MODULE,
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.h b/drivers/scsi/ibmvscsi/ibmvscsi.h
index 46e850e302c..2d4339d5e16 100644
--- a/drivers/scsi/ibmvscsi/ibmvscsi.h
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.h
@@ -45,6 +45,8 @@ struct Scsi_Host;
#define MAX_INDIRECT_BUFS 10
#define IBMVSCSI_MAX_REQUESTS_DEFAULT 100
+#define IBMVSCSI_CMDS_PER_LUN_DEFAULT 16
+#define IBMVSCSI_MAX_SECTORS_DEFAULT 256 /* 32 * 8 = default max I/O 32 pages */
#define IBMVSCSI_MAX_CMDS_PER_LUN 64
/* ------------------------------------------------------------
diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c
index 538552495d4..b40a673985a 100644
--- a/drivers/scsi/ide-scsi.c
+++ b/drivers/scsi/ide-scsi.c
@@ -101,8 +101,13 @@ static struct ide_scsi_obj *ide_scsi_get(struct gendisk *disk)
mutex_lock(&idescsi_ref_mutex);
scsi = ide_scsi_g(disk);
- if (scsi)
+ if (scsi) {
scsi_host_get(scsi->host);
+ if (ide_device_get(scsi->drive)) {
+ scsi_host_put(scsi->host);
+ scsi = NULL;
+ }
+ }
mutex_unlock(&idescsi_ref_mutex);
return scsi;
}
@@ -110,6 +115,7 @@ static struct ide_scsi_obj *ide_scsi_get(struct gendisk *disk)
static void ide_scsi_put(struct ide_scsi_obj *scsi)
{
mutex_lock(&idescsi_ref_mutex);
+ ide_device_put(scsi->drive);
scsi_host_put(scsi->host);
mutex_unlock(&idescsi_ref_mutex);
}
@@ -201,15 +207,15 @@ static int idescsi_check_condition(ide_drive_t *drive,
/* stuff a sense request in front of our current request */
pc = kzalloc(sizeof(struct ide_atapi_pc), GFP_ATOMIC);
- rq = kmalloc(sizeof(struct request), GFP_ATOMIC);
+ rq = blk_get_request(drive->queue, READ, GFP_ATOMIC);
buf = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_ATOMIC);
if (!pc || !rq || !buf) {
kfree(buf);
- kfree(rq);
+ if (rq)
+ blk_put_request(rq);
kfree(pc);
return -ENOMEM;
}
- blk_rq_init(NULL, rq);
rq->special = (char *) pc;
pc->rq = rq;
pc->buf = buf;
@@ -226,6 +232,7 @@ static int idescsi_check_condition(ide_drive_t *drive,
ide_scsi_hex_dump(pc->c, 6);
}
rq->rq_disk = scsi->disk;
+ rq->ref_count++;
memcpy(rq->cmd, pc->c, 12);
ide_do_drive_cmd(drive, rq);
return 0;
@@ -272,7 +279,7 @@ static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs)
SCSI_SENSE_BUFFERSIZE);
kfree(pc->buf);
kfree(pc);
- kfree(rq);
+ blk_put_request(rq);
pc = opc;
rq = pc->rq;
pc->scsi_cmd->result = (CHECK_CONDITION << 1) |
@@ -303,7 +310,7 @@ static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs)
pc->done(pc->scsi_cmd);
spin_unlock_irqrestore(host->host_lock, flags);
kfree(pc);
- kfree(rq);
+ blk_put_request(rq);
scsi->pc = NULL;
return 0;
}
@@ -577,6 +584,7 @@ static int idescsi_queue (struct scsi_cmnd *cmd,
ide_drive_t *drive = scsi->drive;
struct request *rq = NULL;
struct ide_atapi_pc *pc = NULL;
+ int write = cmd->sc_data_direction == DMA_TO_DEVICE;
if (!drive) {
scmd_printk (KERN_ERR, cmd, "drive not present\n");
@@ -584,7 +592,7 @@ static int idescsi_queue (struct scsi_cmnd *cmd,
}
scsi = drive_to_idescsi(drive);
pc = kmalloc(sizeof(struct ide_atapi_pc), GFP_ATOMIC);
- rq = kmalloc(sizeof(struct request), GFP_ATOMIC);
+ rq = blk_get_request(drive->queue, write, GFP_ATOMIC);
if (rq == NULL || pc == NULL) {
printk (KERN_ERR "ide-scsi: %s: out of memory\n", drive->name);
goto abort;
@@ -614,17 +622,18 @@ static int idescsi_queue (struct scsi_cmnd *cmd,
}
}
- blk_rq_init(NULL, rq);
rq->special = (char *) pc;
rq->cmd_type = REQ_TYPE_SPECIAL;
spin_unlock_irq(host->host_lock);
+ rq->ref_count++;
memcpy(rq->cmd, pc->c, 12);
blk_execute_rq_nowait(drive->queue, scsi->disk, rq, 0, NULL);
spin_lock_irq(host->host_lock);
return 0;
abort:
kfree (pc);
- kfree (rq);
+ if (rq)
+ blk_put_request(rq);
cmd->result = DID_ERROR << 16;
done(cmd);
return 0;
@@ -672,7 +681,9 @@ static int idescsi_eh_abort (struct scsi_cmnd *cmd)
if (blk_sense_request(scsi->pc->rq))
kfree(scsi->pc->buf);
- kfree(scsi->pc->rq);
+ /* we need to call blk_put_request twice. */
+ blk_put_request(scsi->pc->rq);
+ blk_put_request(scsi->pc->rq);
kfree(scsi->pc);
scsi->pc = NULL;
@@ -724,7 +735,7 @@ static int idescsi_eh_reset (struct scsi_cmnd *cmd)
kfree(scsi->pc->buf);
kfree(scsi->pc);
scsi->pc = NULL;
- kfree(req);
+ blk_put_request(req);
/* now nuke the drive queue */
while ((req = elv_next_request(drive->queue))) {
diff --git a/drivers/scsi/sun_esp.c b/drivers/scsi/sun_esp.c
index 2c87db98cdf..f9cf7015136 100644
--- a/drivers/scsi/sun_esp.c
+++ b/drivers/scsi/sun_esp.c
@@ -7,6 +7,7 @@
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/module.h>
+#include <linux/mm.h>
#include <linux/init.h>
#include <asm/irq.h>
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index 27f34a9f9cb..a97f1ae11f7 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -1293,7 +1293,18 @@ receive_chars(struct uart_8250_port *up, unsigned int *status)
char flag;
do {
- ch = serial_inp(up, UART_RX);
+ if (likely(lsr & UART_LSR_DR))
+ ch = serial_inp(up, UART_RX);
+ else
+ /*
+ * Intel 82571 has a Serial Over Lan device that will
+ * set UART_LSR_BI without setting UART_LSR_DR when
+ * it receives a break. To avoid reading from the
+ * receive buffer without UART_LSR_DR bit set, we
+ * just force the read character to be 0
+ */
+ ch = 0;
+
flag = TTY_NORMAL;
up->port.icount.rx++;
@@ -1342,7 +1353,7 @@ receive_chars(struct uart_8250_port *up, unsigned int *status)
ignore_char:
lsr = serial_inp(up, UART_LSR);
- } while ((lsr & UART_LSR_DR) && (max_count-- > 0));
+ } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0));
spin_unlock(&up->port.lock);
tty_flip_buffer_push(tty);
spin_lock(&up->port.lock);
@@ -1425,7 +1436,7 @@ serial8250_handle_port(struct uart_8250_port *up)
DEBUG_INTR("status = %x...", status);
- if (status & UART_LSR_DR)
+ if (status & (UART_LSR_DR | UART_LSR_BI))
receive_chars(up, &status);
check_modem_status(up);
if (status & UART_LSR_THRE)
diff --git a/drivers/serial/8250_gsc.c b/drivers/serial/8250_gsc.c
index 4eb7437a404..0416ad3bc12 100644
--- a/drivers/serial/8250_gsc.c
+++ b/drivers/serial/8250_gsc.c
@@ -119,3 +119,5 @@ int __init probe_serial_gsc(void)
}
module_init(probe_serial_gsc);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c
index 1b36087665a..c2f23933155 100644
--- a/drivers/serial/8250_pci.c
+++ b/drivers/serial/8250_pci.c
@@ -767,6 +767,9 @@ pci_default_setup(struct serial_private *priv, struct pciserial_board *board,
#define PCI_SUBDEVICE_ID_POCTAL232 0x0308
#define PCI_SUBDEVICE_ID_POCTAL422 0x0408
+/* Unknown vendors/cards - this should not be in linux/pci_ids.h */
+#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584
+
/*
* Master list of serial port init/setup/exit quirks.
* This does not describe the general nature of the port.
@@ -882,6 +885,15 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
},
{
.vendor = PCI_VENDOR_ID_PLX,
+ .device = PCI_DEVICE_ID_PLX_9050,
+ .subvendor = PCI_VENDOR_ID_PLX,
+ .subdevice = PCI_SUBDEVICE_ID_UNKNOWN_0x1584,
+ .init = pci_plx9050_init,
+ .setup = pci_default_setup,
+ .exit = __devexit_p(pci_plx9050_exit),
+ },
+ {
+ .vendor = PCI_VENDOR_ID_PLX,
.device = PCI_DEVICE_ID_PLX_ROMULUS,
.subvendor = PCI_VENDOR_ID_PLX,
.subdevice = PCI_DEVICE_ID_PLX_ROMULUS,
@@ -2197,6 +2209,11 @@ static struct pci_device_id serial_pci_tbl[] = {
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b2_4_921600 },
+ /* Unknown card - subdevice 0x1584 */
+ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+ PCI_VENDOR_ID_PLX,
+ PCI_SUBDEVICE_ID_UNKNOWN_0x1584, 0, 0,
+ pbn_b0_4_115200 },
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
PCI_SUBVENDOR_ID_KEYSPAN,
PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0,
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 8fc7451c004..3b4a14e355c 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -942,22 +942,6 @@ config SERIAL_IP22_ZILOG_CONSOLE
depends on SERIAL_IP22_ZILOG=y
select SERIAL_CORE_CONSOLE
-config V850E_UART
- bool "NEC V850E on-chip UART support"
- depends on V850E_MA1 || V850E_ME2 || V850E_TEG || V850E2_ANNA || V850E_AS85EP1
- select SERIAL_CORE
- default y
-
-config V850E_UARTB
- bool
- depends on V850E_UART && V850E_ME2
- default y
-
-config V850E_UART_CONSOLE
- bool "Use NEC V850E on-chip UART for console"
- depends on V850E_UART
- select SERIAL_CORE_CONSOLE
-
config SERIAL_SH_SCI
tristate "SuperH SCI(F) serial port support"
depends on SUPERH || H8300
diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c
index 1ff80de177d..a4f86927a74 100644
--- a/drivers/serial/cpm_uart/cpm_uart_core.c
+++ b/drivers/serial/cpm_uart/cpm_uart_core.c
@@ -435,10 +435,13 @@ static void cpm_uart_shutdown(struct uart_port *port)
}
/* Shut them really down and reinit buffer descriptors */
- if (IS_SMC(pinfo))
+ if (IS_SMC(pinfo)) {
+ out_be16(&pinfo->smcup->smc_brkcr, 0);
cpm_line_cr_cmd(pinfo, CPM_CR_STOP_TX);
- else
+ } else {
+ out_be16(&pinfo->sccup->scc_brkcr, 0);
cpm_line_cr_cmd(pinfo, CPM_CR_GRA_STOP_TX);
+ }
cpm_uart_initbd(pinfo);
}
@@ -554,9 +557,11 @@ static void cpm_uart_set_termios(struct uart_port *port,
* enables, because we want to put them back if they were
* present.
*/
- prev_mode = in_be16(&smcp->smc_smcmr);
- out_be16(&smcp->smc_smcmr, smcr_mk_clen(bits) | cval | SMCMR_SM_UART);
- setbits16(&smcp->smc_smcmr, (prev_mode & (SMCMR_REN | SMCMR_TEN)));
+ prev_mode = in_be16(&smcp->smc_smcmr) & (SMCMR_REN | SMCMR_TEN);
+ /* Output in *one* operation, so we don't interrupt RX/TX if they
+ * were already enabled. */
+ out_be16(&smcp->smc_smcmr, smcr_mk_clen(bits) | cval |
+ SMCMR_SM_UART | prev_mode);
} else {
out_be16(&sccp->scc_psmr, (sbits << 12) | scval);
}
@@ -1198,12 +1203,14 @@ static int __init cpm_uart_console_setup(struct console *co, char *options)
udbg_putc = NULL;
#endif
- cpm_line_cr_cmd(pinfo, CPM_CR_STOP_TX);
-
if (IS_SMC(pinfo)) {
+ out_be16(&pinfo->smcup->smc_brkcr, 0);
+ cpm_line_cr_cmd(pinfo, CPM_CR_STOP_TX);
clrbits8(&pinfo->smcp->smc_smcm, SMCM_RX | SMCM_TX);
clrbits16(&pinfo->smcp->smc_smcmr, SMCMR_REN | SMCMR_TEN);
} else {
+ out_be16(&pinfo->sccup->scc_brkcr, 0);
+ cpm_line_cr_cmd(pinfo, CPM_CR_GRA_STOP_TX);
clrbits16(&pinfo->sccp->scc_sccm, UART_SCCM_TX | UART_SCCM_RX);
clrbits32(&pinfo->sccp->scc_gsmrl, SCC_GSMRL_ENR | SCC_GSMRL_ENT);
}
diff --git a/drivers/serial/dz.c b/drivers/serial/dz.c
index a81d2c2ff8a..6042b87797a 100644
--- a/drivers/serial/dz.c
+++ b/drivers/serial/dz.c
@@ -642,6 +642,26 @@ static void dz_set_termios(struct uart_port *uport, struct ktermios *termios,
spin_unlock_irqrestore(&dport->port.lock, flags);
}
+/*
+ * Hack alert!
+ * Required solely so that the initial PROM-based console
+ * works undisturbed in parallel with this one.
+ */
+static void dz_pm(struct uart_port *uport, unsigned int state,
+ unsigned int oldstate)
+{
+ struct dz_port *dport = to_dport(uport);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dport->port.lock, flags);
+ if (state < 3)
+ dz_start_tx(&dport->port);
+ else
+ dz_stop_tx(&dport->port);
+ spin_unlock_irqrestore(&dport->port.lock, flags);
+}
+
+
static const char *dz_type(struct uart_port *uport)
{
return "DZ";
@@ -738,6 +758,7 @@ static struct uart_ops dz_ops = {
.startup = dz_startup,
.shutdown = dz_shutdown,
.set_termios = dz_set_termios,
+ .pm = dz_pm,
.type = dz_type,
.release_port = dz_release_port,
.request_port = dz_request_port,
@@ -861,7 +882,10 @@ static int __init dz_console_setup(struct console *co, char *options)
if (ret)
return ret;
+ spin_lock_init(&dport->port.lock); /* For dz_pm(). */
+
dz_reset(dport);
+ dz_pm(uport, 0, -1);
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
diff --git a/drivers/serial/zs.c b/drivers/serial/zs.c
index bd45b6230fd..9e6a873f820 100644
--- a/drivers/serial/zs.c
+++ b/drivers/serial/zs.c
@@ -787,7 +787,6 @@ static int zs_startup(struct uart_port *uport)
zport->regs[1] &= ~RxINT_MASK;
zport->regs[1] |= RxINT_ALL | TxINT_ENAB | EXT_INT_ENAB;
zport->regs[3] |= RxENABLE;
- zport->regs[5] |= TxENAB;
zport->regs[15] |= BRKIE;
write_zsreg(zport, R1, zport->regs[1]);
write_zsreg(zport, R3, zport->regs[3]);
@@ -814,7 +813,6 @@ static void zs_shutdown(struct uart_port *uport)
spin_lock_irqsave(&scc->zlock, flags);
- zport->regs[5] &= ~TxENAB;
zport->regs[3] &= ~RxENABLE;
write_zsreg(zport, R5, zport->regs[5]);
write_zsreg(zport, R3, zport->regs[3]);
@@ -959,6 +957,23 @@ static void zs_set_termios(struct uart_port *uport, struct ktermios *termios,
spin_unlock_irqrestore(&scc->zlock, flags);
}
+/*
+ * Hack alert!
+ * Required solely so that the initial PROM-based console
+ * works undisturbed in parallel with this one.
+ */
+static void zs_pm(struct uart_port *uport, unsigned int state,
+ unsigned int oldstate)
+{
+ struct zs_port *zport = to_zport(uport);
+
+ if (state < 3)
+ zport->regs[5] |= TxENAB;
+ else
+ zport->regs[5] &= ~TxENAB;
+ write_zsreg(zport, R5, zport->regs[5]);
+}
+
static const char *zs_type(struct uart_port *uport)
{
@@ -1041,6 +1056,7 @@ static struct uart_ops zs_ops = {
.startup = zs_startup,
.shutdown = zs_shutdown,
.set_termios = zs_set_termios,
+ .pm = zs_pm,
.type = zs_type,
.release_port = zs_release_port,
.request_port = zs_request_port,
@@ -1190,6 +1206,7 @@ static int __init zs_console_setup(struct console *co, char *options)
return ret;
zs_reset(zport);
+ zs_pm(uport, 0, -1);
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 66ec5d8808d..2303521b4f0 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -49,25 +49,26 @@ config SPI_MASTER
controller and the protocol drivers for the SPI slave chips
that are connected.
+if SPI_MASTER
+
comment "SPI Master Controller Drivers"
- depends on SPI_MASTER
config SPI_ATMEL
tristate "Atmel SPI Controller"
- depends on (ARCH_AT91 || AVR32) && SPI_MASTER
+ depends on (ARCH_AT91 || AVR32)
help
This selects a driver for the Atmel SPI Controller, present on
many AT32 (AVR32) and AT91 (ARM) chips.
config SPI_BFIN
tristate "SPI controller driver for ADI Blackfin5xx"
- depends on SPI_MASTER && BLACKFIN
+ depends on BLACKFIN
help
This is the SPI controller master driver for Blackfin 5xx processor.
config SPI_AU1550
tristate "Au1550/Au12x0 SPI Controller"
- depends on SPI_MASTER && (SOC_AU1550 || SOC_AU1200) && EXPERIMENTAL
+ depends on (SOC_AU1550 || SOC_AU1200) && EXPERIMENTAL
select SPI_BITBANG
help
If you say yes to this option, support will be included for the
@@ -78,7 +79,6 @@ config SPI_AU1550
config SPI_BITBANG
tristate "Bitbanging SPI master"
- depends on SPI_MASTER && EXPERIMENTAL
help
With a few GPIO pins, your system can bitbang the SPI protocol.
Select this to get SPI support through I/O pins (GPIO, parallel
@@ -92,7 +92,7 @@ config SPI_BITBANG
config SPI_BUTTERFLY
tristate "Parallel port adapter for AVR Butterfly (DEVELOPMENT)"
- depends on SPI_MASTER && PARPORT && EXPERIMENTAL
+ depends on PARPORT
select SPI_BITBANG
help
This uses a custom parallel port cable to connect to an AVR
@@ -102,14 +102,14 @@ config SPI_BUTTERFLY
config SPI_IMX
tristate "Freescale iMX SPI controller"
- depends on SPI_MASTER && ARCH_IMX && EXPERIMENTAL
+ depends on ARCH_IMX && EXPERIMENTAL
help
This enables using the Freescale iMX SPI controller in master
mode.
config SPI_LM70_LLP
tristate "Parallel port adapter for LM70 eval board (DEVELOPMENT)"
- depends on SPI_MASTER && PARPORT && EXPERIMENTAL
+ depends on PARPORT && EXPERIMENTAL
select SPI_BITBANG
help
This driver supports the NS LM70 LLP Evaluation Board,
@@ -118,14 +118,14 @@ config SPI_LM70_LLP
config SPI_MPC52xx_PSC
tristate "Freescale MPC52xx PSC SPI controller"
- depends on SPI_MASTER && PPC_MPC52xx && EXPERIMENTAL
+ depends on PPC_MPC52xx && EXPERIMENTAL
help
This enables using the Freescale MPC52xx Programmable Serial
Controller in master SPI mode.
config SPI_MPC83xx
tristate "Freescale MPC83xx/QUICC Engine SPI controller"
- depends on SPI_MASTER && (PPC_83xx || QUICC_ENGINE) && EXPERIMENTAL
+ depends on (PPC_83xx || QUICC_ENGINE) && EXPERIMENTAL
help
This enables using the Freescale MPC83xx and QUICC Engine SPI
controllers in master mode.
@@ -137,21 +137,21 @@ config SPI_MPC83xx
config SPI_OMAP_UWIRE
tristate "OMAP1 MicroWire"
- depends on SPI_MASTER && ARCH_OMAP1
+ depends on ARCH_OMAP1
select SPI_BITBANG
help
This hooks up to the MicroWire controller on OMAP1 chips.
config SPI_OMAP24XX
tristate "McSPI driver for OMAP24xx/OMAP34xx"
- depends on SPI_MASTER && (ARCH_OMAP24XX || ARCH_OMAP34XX)
+ depends on ARCH_OMAP24XX || ARCH_OMAP34XX
help
SPI master controller for OMAP24xx/OMAP34xx Multichannel SPI
(McSPI) modules.
config SPI_PXA2XX
tristate "PXA2xx SSP SPI master"
- depends on SPI_MASTER && ARCH_PXA && EXPERIMENTAL
+ depends on ARCH_PXA && EXPERIMENTAL
select PXA_SSP
help
This enables using a PXA2xx SSP port as a SPI master controller.
@@ -160,14 +160,14 @@ config SPI_PXA2XX
config SPI_S3C24XX
tristate "Samsung S3C24XX series SPI"
- depends on SPI_MASTER && ARCH_S3C2410 && EXPERIMENTAL
+ depends on ARCH_S3C2410 && EXPERIMENTAL
select SPI_BITBANG
help
SPI driver for Samsung S3C24XX series ARM SoCs
config SPI_S3C24XX_GPIO
tristate "Samsung S3C24XX series SPI by GPIO"
- depends on SPI_MASTER && ARCH_S3C2410 && EXPERIMENTAL
+ depends on ARCH_S3C2410 && EXPERIMENTAL
select SPI_BITBANG
help
SPI driver for Samsung S3C24XX series ARM SoCs using
@@ -177,20 +177,20 @@ config SPI_S3C24XX_GPIO
config SPI_SH_SCI
tristate "SuperH SCI SPI controller"
- depends on SPI_MASTER && SUPERH
+ depends on SUPERH
select SPI_BITBANG
help
SPI driver for SuperH SCI blocks.
config SPI_TXX9
tristate "Toshiba TXx9 SPI controller"
- depends on SPI_MASTER && GENERIC_GPIO && CPU_TX49XX
+ depends on GENERIC_GPIO && CPU_TX49XX
help
SPI driver for Toshiba TXx9 MIPS SoCs
config SPI_XILINX
tristate "Xilinx SPI controller"
- depends on SPI_MASTER && XILINX_VIRTEX && EXPERIMENTAL
+ depends on XILINX_VIRTEX && EXPERIMENTAL
select SPI_BITBANG
help
This exposes the SPI controller IP from the Xilinx EDK.
@@ -207,11 +207,10 @@ config SPI_XILINX
# being probably the most widely used ones.
#
comment "SPI Protocol Masters"
- depends on SPI_MASTER
config SPI_AT25
tristate "SPI EEPROMs from most vendors"
- depends on SPI_MASTER && SYSFS
+ depends on SYSFS
help
Enable this driver to get read/write support to most SPI EEPROMs,
after you configure the board init code to know about each eeprom
@@ -222,7 +221,7 @@ config SPI_AT25
config SPI_SPIDEV
tristate "User mode SPI device driver support"
- depends on SPI_MASTER && EXPERIMENTAL
+ depends on EXPERIMENTAL
help
This supports user mode SPI protocol drivers.
@@ -231,7 +230,7 @@ config SPI_SPIDEV
config SPI_TLE62X0
tristate "Infineon TLE62X0 (for power switching)"
- depends on SPI_MASTER && SYSFS
+ depends on SYSFS
help
SPI driver for Infineon TLE62X0 series line driver chips,
such as the TLE6220, TLE6230 and TLE6240. This provides a
@@ -242,6 +241,8 @@ config SPI_TLE62X0
# Add new SPI protocol masters in alphabetical order above this line
#
+endif # SPI_MASTER
+
# (slave support would go here)
endif # SPI
diff --git a/drivers/spi/au1550_spi.c b/drivers/spi/au1550_spi.c
index 072c4a59533..9149689c79d 100644
--- a/drivers/spi/au1550_spi.c
+++ b/drivers/spi/au1550_spi.c
@@ -26,6 +26,7 @@
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/platform_device.h>
+#include <linux/resource.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/dma-mapping.h>
@@ -81,6 +82,7 @@ struct au1550_spi {
struct spi_master *master;
struct device *dev;
struct au1550_spi_info *pdata;
+ struct resource *ioarea;
};
@@ -96,6 +98,8 @@ static dbdev_tab_t au1550_spi_mem_dbdev =
.dev_intpolarity = 0
};
+static int ddma_memid; /* id to above mem dma device */
+
static void au1550_spi_bits_handlers_set(struct au1550_spi *hw, int bpw);
@@ -480,9 +484,13 @@ static irqreturn_t au1550_spi_dma_irq_callback(struct au1550_spi *hw)
au1xxx_dbdma_reset(hw->dma_tx_ch);
au1550_spi_reset_fifos(hw);
- dev_err(hw->dev,
- "Unexpected SPI error: event=0x%x stat=0x%x!\n",
- evnt, stat);
+ if (evnt == PSC_SPIEVNT_RO)
+ dev_err(hw->dev,
+ "dma transfer: receive FIFO overflow!\n");
+ else
+ dev_err(hw->dev,
+ "dma transfer: unexpected SPI error "
+ "(event=0x%x stat=0x%x)!\n", evnt, stat);
complete(&hw->master_done);
return IRQ_HANDLED;
@@ -592,17 +600,17 @@ static irqreturn_t au1550_spi_pio_irq_callback(struct au1550_spi *hw)
if ((evnt & (PSC_SPIEVNT_MM | PSC_SPIEVNT_RO
| PSC_SPIEVNT_RU | PSC_SPIEVNT_TO
- | PSC_SPIEVNT_TU | PSC_SPIEVNT_SD))
+ | PSC_SPIEVNT_SD))
!= 0) {
- dev_err(hw->dev,
- "Unexpected SPI error: event=0x%x stat=0x%x!\n",
- evnt, stat);
/*
* due to an error we consider transfer as done,
* so mask all events until before next transfer start
*/
au1550_spi_mask_ack_all(hw);
au1550_spi_reset_fifos(hw);
+ dev_err(hw->dev,
+ "pio transfer: unexpected SPI error "
+ "(event=0x%x stat=0x%x)!\n", evnt, stat);
complete(&hw->master_done);
return IRQ_HANDLED;
}
@@ -616,27 +624,50 @@ static irqreturn_t au1550_spi_pio_irq_callback(struct au1550_spi *hw)
stat = hw->regs->psc_spistat;
au_sync();
- if ((stat & PSC_SPISTAT_RE) == 0 && hw->rx_count < hw->len) {
+ /*
+ * Take care to not let the Rx FIFO overflow.
+ *
+ * We only write a byte if we have read one at least. Initially,
+ * the write fifo is full, so we should read from the read fifo
+ * first.
+ * In case we miss a word from the read fifo, we should get a
+ * RO event and should back out.
+ */
+ if (!(stat & PSC_SPISTAT_RE) && hw->rx_count < hw->len) {
hw->rx_word(hw);
- /* ack the receive request event */
- hw->regs->psc_spievent = PSC_SPIEVNT_RR;
- au_sync();
busy = 1;
- }
- if ((stat & PSC_SPISTAT_TF) == 0 && hw->tx_count < hw->len) {
- hw->tx_word(hw);
- /* ack the transmit request event */
- hw->regs->psc_spievent = PSC_SPIEVNT_TR;
- au_sync();
- busy = 1;
+ if (!(stat & PSC_SPISTAT_TF) && hw->tx_count < hw->len)
+ hw->tx_word(hw);
}
} while (busy);
- evnt = hw->regs->psc_spievent;
+ hw->regs->psc_spievent = PSC_SPIEVNT_RR | PSC_SPIEVNT_TR;
au_sync();
- if (hw->rx_count >= hw->len || (evnt & PSC_SPIEVNT_MD) != 0) {
+ /*
+ * Restart the SPI transmission in case of a transmit underflow.
+ * This seems to work despite the notes in the Au1550 data book
+ * of Figure 8-4 with flowchart for SPI master operation:
+ *
+ * """Note 1: An XFR Error Interrupt occurs, unless masked,
+ * for any of the following events: Tx FIFO Underflow,
+ * Rx FIFO Overflow, or Multiple-master Error
+ * Note 2: In case of a Tx Underflow Error, all zeroes are
+ * transmitted."""
+ *
+ * By simply restarting the spi transfer on Tx Underflow Error,
+ * we assume that spi transfer was paused instead of zeroes
+ * transmittion mentioned in the Note 2 of Au1550 data book.
+ */
+ if (evnt & PSC_SPIEVNT_TU) {
+ hw->regs->psc_spievent = PSC_SPIEVNT_TU | PSC_SPIEVNT_MD;
+ au_sync();
+ hw->regs->psc_spipcr = PSC_SPIPCR_MS;
+ au_sync();
+ }
+
+ if (hw->rx_count >= hw->len) {
/* transfer completed successfully */
au1550_spi_mask_ack_all(hw);
complete(&hw->master_done);
@@ -725,6 +756,8 @@ static void __init au1550_spi_setup_psc_as_spi(struct au1550_spi *hw)
stat = hw->regs->psc_spistat;
au_sync();
} while ((stat & PSC_SPISTAT_DR) == 0);
+
+ au1550_spi_reset_fifos(hw);
}
@@ -732,6 +765,7 @@ static int __init au1550_spi_probe(struct platform_device *pdev)
{
struct au1550_spi *hw;
struct spi_master *master;
+ struct resource *r;
int err = 0;
master = spi_alloc_master(&pdev->dev, sizeof(struct au1550_spi));
@@ -753,76 +787,64 @@ static int __init au1550_spi_probe(struct platform_device *pdev)
goto err_no_pdata;
}
- platform_set_drvdata(pdev, hw);
-
- init_completion(&hw->master_done);
-
- hw->bitbang.master = hw->master;
- hw->bitbang.setup_transfer = au1550_spi_setupxfer;
- hw->bitbang.chipselect = au1550_spi_chipsel;
- hw->bitbang.master->setup = au1550_spi_setup;
- hw->bitbang.txrx_bufs = au1550_spi_txrx_bufs;
+ r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "no IRQ\n");
+ err = -ENODEV;
+ goto err_no_iores;
+ }
+ hw->irq = r->start;
+
+ hw->usedma = 0;
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (r) {
+ hw->dma_tx_id = r->start;
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (r) {
+ hw->dma_rx_id = r->start;
+ if (usedma && ddma_memid) {
+ if (pdev->dev.dma_mask == NULL)
+ dev_warn(&pdev->dev, "no dma mask\n");
+ else
+ hw->usedma = 1;
+ }
+ }
+ }
- switch (hw->pdata->bus_num) {
- case 0:
- hw->irq = AU1550_PSC0_INT;
- hw->regs = (volatile psc_spi_t *)PSC0_BASE_ADDR;
- hw->dma_rx_id = DSCR_CMD0_PSC0_RX;
- hw->dma_tx_id = DSCR_CMD0_PSC0_TX;
- break;
- case 1:
- hw->irq = AU1550_PSC1_INT;
- hw->regs = (volatile psc_spi_t *)PSC1_BASE_ADDR;
- hw->dma_rx_id = DSCR_CMD0_PSC1_RX;
- hw->dma_tx_id = DSCR_CMD0_PSC1_TX;
- break;
- case 2:
- hw->irq = AU1550_PSC2_INT;
- hw->regs = (volatile psc_spi_t *)PSC2_BASE_ADDR;
- hw->dma_rx_id = DSCR_CMD0_PSC2_RX;
- hw->dma_tx_id = DSCR_CMD0_PSC2_TX;
- break;
- case 3:
- hw->irq = AU1550_PSC3_INT;
- hw->regs = (volatile psc_spi_t *)PSC3_BASE_ADDR;
- hw->dma_rx_id = DSCR_CMD0_PSC3_RX;
- hw->dma_tx_id = DSCR_CMD0_PSC3_TX;
- break;
- default:
- dev_err(&pdev->dev, "Wrong bus_num of SPI\n");
- err = -ENOENT;
- goto err_no_pdata;
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "no mmio resource\n");
+ err = -ENODEV;
+ goto err_no_iores;
}
- if (request_mem_region((unsigned long)hw->regs, sizeof(psc_spi_t),
- pdev->name) == NULL) {
+ hw->ioarea = request_mem_region(r->start, sizeof(psc_spi_t),
+ pdev->name);
+ if (!hw->ioarea) {
dev_err(&pdev->dev, "Cannot reserve iomem region\n");
err = -ENXIO;
goto err_no_iores;
}
-
- if (usedma) {
- if (pdev->dev.dma_mask == NULL)
- dev_warn(&pdev->dev, "no dma mask\n");
- else
- hw->usedma = 1;
+ hw->regs = (psc_spi_t __iomem *)ioremap(r->start, sizeof(psc_spi_t));
+ if (!hw->regs) {
+ dev_err(&pdev->dev, "cannot ioremap\n");
+ err = -ENXIO;
+ goto err_ioremap;
}
- if (hw->usedma) {
- /*
- * create memory device with 8 bits dev_devwidth
- * needed for proper byte ordering to spi fifo
- */
- int memid = au1xxx_ddma_add_device(&au1550_spi_mem_dbdev);
- if (!memid) {
- dev_err(&pdev->dev,
- "Cannot create dma 8 bit mem device\n");
- err = -ENXIO;
- goto err_dma_add_dev;
- }
+ platform_set_drvdata(pdev, hw);
+
+ init_completion(&hw->master_done);
+
+ hw->bitbang.master = hw->master;
+ hw->bitbang.setup_transfer = au1550_spi_setupxfer;
+ hw->bitbang.chipselect = au1550_spi_chipsel;
+ hw->bitbang.master->setup = au1550_spi_setup;
+ hw->bitbang.txrx_bufs = au1550_spi_txrx_bufs;
- hw->dma_tx_ch = au1xxx_dbdma_chan_alloc(memid,
+ if (hw->usedma) {
+ hw->dma_tx_ch = au1xxx_dbdma_chan_alloc(ddma_memid,
hw->dma_tx_id, NULL, (void *)hw);
if (hw->dma_tx_ch == 0) {
dev_err(&pdev->dev,
@@ -841,7 +863,7 @@ static int __init au1550_spi_probe(struct platform_device *pdev)
hw->dma_rx_ch = au1xxx_dbdma_chan_alloc(hw->dma_rx_id,
- memid, NULL, (void *)hw);
+ ddma_memid, NULL, (void *)hw);
if (hw->dma_rx_ch == 0) {
dev_err(&pdev->dev,
"Cannot allocate rx dma channel\n");
@@ -874,7 +896,7 @@ static int __init au1550_spi_probe(struct platform_device *pdev)
goto err_no_irq;
}
- master->bus_num = hw->pdata->bus_num;
+ master->bus_num = pdev->id;
master->num_chipselect = hw->pdata->num_chipselect;
/*
@@ -924,8 +946,11 @@ err_no_txdma_descr:
au1xxx_dbdma_chan_free(hw->dma_tx_ch);
err_no_txdma:
-err_dma_add_dev:
- release_mem_region((unsigned long)hw->regs, sizeof(psc_spi_t));
+ iounmap((void __iomem *)hw->regs);
+
+err_ioremap:
+ release_resource(hw->ioarea);
+ kfree(hw->ioarea);
err_no_iores:
err_no_pdata:
@@ -944,7 +969,9 @@ static int __exit au1550_spi_remove(struct platform_device *pdev)
spi_bitbang_stop(&hw->bitbang);
free_irq(hw->irq, hw);
- release_mem_region((unsigned long)hw->regs, sizeof(psc_spi_t));
+ iounmap((void __iomem *)hw->regs);
+ release_resource(hw->ioarea);
+ kfree(hw->ioarea);
if (hw->usedma) {
au1550_spi_dma_rxtmp_free(hw);
@@ -971,12 +998,24 @@ static struct platform_driver au1550_spi_drv = {
static int __init au1550_spi_init(void)
{
+ /*
+ * create memory device with 8 bits dev_devwidth
+ * needed for proper byte ordering to spi fifo
+ */
+ if (usedma) {
+ ddma_memid = au1xxx_ddma_add_device(&au1550_spi_mem_dbdev);
+ if (!ddma_memid)
+ printk(KERN_ERR "au1550-spi: cannot add memory"
+ "dbdma device\n");
+ }
return platform_driver_probe(&au1550_spi_drv, au1550_spi_probe);
}
module_init(au1550_spi_init);
static void __exit au1550_spi_exit(void)
{
+ if (usedma && ddma_memid)
+ au1xxx_ddma_del_device(ddma_memid);
platform_driver_unregister(&au1550_spi_drv);
}
module_exit(au1550_spi_exit);
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 1771b2456bf..ecca4a6a6f9 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -218,6 +218,8 @@ struct spi_device *spi_new_device(struct spi_master *master,
if (!spi_master_get(master))
return NULL;
+ WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
+
proxy = kzalloc(sizeof *proxy, GFP_KERNEL);
if (!proxy) {
dev_err(dev, "can't alloc dev for cs%d\n",
@@ -229,7 +231,7 @@ struct spi_device *spi_new_device(struct spi_master *master,
proxy->max_speed_hz = chip->max_speed_hz;
proxy->mode = chip->mode;
proxy->irq = chip->irq;
- proxy->modalias = chip->modalias;
+ strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
snprintf(proxy->dev.bus_id, sizeof proxy->dev.bus_id,
"%s.%u", master->dev.bus_id,
diff --git a/drivers/spi/spi_mpc83xx.c b/drivers/spi/spi_mpc83xx.c
index 6832da6f710..070c6219e2d 100644
--- a/drivers/spi/spi_mpc83xx.c
+++ b/drivers/spi/spi_mpc83xx.c
@@ -266,21 +266,24 @@ int mpc83xx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
cs->hw_mode |= SPMODE_LEN(bits_per_word);
- if ((mpc83xx_spi->spibrg / hz) >= 64) {
- pm = mpc83xx_spi->spibrg / (hz * 64) - 1;
- if (pm > 0x0f) {
- dev_err(&spi->dev, "Requested speed is too "
- "low: %d Hz. Will use %d Hz instead.\n",
- hz, mpc83xx_spi->spibrg / 1024);
- pm = 0x0f;
+ if ((mpc83xx_spi->spibrg / hz) > 64) {
+ pm = mpc83xx_spi->spibrg / (hz * 64);
+ if (pm > 16) {
+ cs->hw_mode |= SPMODE_DIV16;
+ pm /= 16;
+ if (pm > 16) {
+ dev_err(&spi->dev, "Requested speed is too "
+ "low: %d Hz. Will use %d Hz instead.\n",
+ hz, mpc83xx_spi->spibrg / 1024);
+ pm = 16;
+ }
}
- cs->hw_mode |= SPMODE_PM(pm) | SPMODE_DIV16;
- } else {
+ } else
pm = mpc83xx_spi->spibrg / (hz * 4);
- if (pm)
- pm--;
- cs->hw_mode |= SPMODE_PM(pm);
- }
+ if (pm)
+ pm--;
+
+ cs->hw_mode |= SPMODE_PM(pm);
regval = mpc83xx_spi_read_reg(&mpc83xx_spi->base->mode);
if (cs->hw_mode != regval) {
unsigned long flags;
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 2833fd772a2..e5e0cfed5e3 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -228,7 +228,6 @@ static int spidev_message(struct spidev_data *spidev,
* We walk the array of user-provided transfers, using each one
* to initialize a kernel version of the same transfer.
*/
- mutex_lock(&spidev->buf_lock);
buf = spidev->buffer;
total = 0;
for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
@@ -296,14 +295,12 @@ static int spidev_message(struct spidev_data *spidev,
status = total;
done:
- mutex_unlock(&spidev->buf_lock);
kfree(k_xfers);
return status;
}
-static int
-spidev_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg)
+static long
+spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int err = 0;
int retval = 0;
@@ -341,6 +338,14 @@ spidev_ioctl(struct inode *inode, struct file *filp,
if (spi == NULL)
return -ESHUTDOWN;
+ /* use the buffer lock here for triple duty:
+ * - prevent I/O (from us) so calling spi_setup() is safe;
+ * - prevent concurrent SPI_IOC_WR_* from morphing
+ * data fields while SPI_IOC_RD_* reads them;
+ * - SPI_IOC_MESSAGE needs the buffer locked "normally".
+ */
+ mutex_lock(&spidev->buf_lock);
+
switch (cmd) {
/* read requests */
case SPI_IOC_RD_MODE:
@@ -456,6 +461,8 @@ spidev_ioctl(struct inode *inode, struct file *filp,
kfree(ioc);
break;
}
+
+ mutex_unlock(&spidev->buf_lock);
spi_dev_put(spi);
return retval;
}
@@ -533,7 +540,7 @@ static struct file_operations spidev_fops = {
*/
.write = spidev_write,
.read = spidev_read,
- .ioctl = spidev_ioctl,
+ .unlocked_ioctl = spidev_ioctl,
.open = spidev_open,
.release = spidev_release,
};
diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c
index 113a0468ffc..68d6f4988fb 100644
--- a/drivers/spi/xilinx_spi.c
+++ b/drivers/spi/xilinx_spi.c
@@ -353,11 +353,12 @@ static int __init xilinx_spi_probe(struct platform_device *dev)
goto put_master;
}
- xspi->irq = platform_get_irq(dev, 0);
- if (xspi->irq < 0) {
+ ret = platform_get_irq(dev, 0);
+ if (ret < 0) {
ret = -ENXIO;
goto unmap_io;
}
+ xspi->irq = ret;
master->bus_num = pdata->bus_num;
master->num_chipselect = pdata->num_chipselect;
diff --git a/drivers/telephony/ixj.c b/drivers/telephony/ixj.c
index 49cd9793404..ec7aeb502d1 100644
--- a/drivers/telephony/ixj.c
+++ b/drivers/telephony/ixj.c
@@ -6095,15 +6095,15 @@ static int capabilities_check(IXJ *j, struct phone_capability *pcreq)
return retval;
}
-static int ixj_ioctl(struct inode *inode, struct file *file_p, unsigned int cmd, unsigned long arg)
+static long do_ixj_ioctl(struct file *file_p, unsigned int cmd, unsigned long arg)
{
IXJ_TONE ti;
IXJ_FILTER jf;
IXJ_FILTER_RAW jfr;
void __user *argp = (void __user *)arg;
-
- unsigned int raise, mant;
+ struct inode *inode = file_p->f_path.dentry->d_inode;
unsigned int minor = iminor(inode);
+ unsigned int raise, mant;
int board = NUM(inode);
IXJ *j = get_ixj(NUM(inode));
@@ -6661,6 +6661,15 @@ static int ixj_ioctl(struct inode *inode, struct file *file_p, unsigned int cmd,
return retval;
}
+static long ixj_ioctl(struct file *file_p, unsigned int cmd, unsigned long arg)
+{
+ long ret;
+ lock_kernel();
+ ret = do_ixj_ioctl(file_p, cmd, arg);
+ unlock_kernel();
+ return ret;
+}
+
static int ixj_fasync(int fd, struct file *file_p, int mode)
{
IXJ *j = get_ixj(NUM(file_p->f_path.dentry->d_inode));
@@ -6674,7 +6683,7 @@ static const struct file_operations ixj_fops =
.read = ixj_enhanced_read,
.write = ixj_enhanced_write,
.poll = ixj_poll,
- .ioctl = ixj_ioctl,
+ .unlocked_ioctl = ixj_ioctl,
.release = ixj_release,
.fasync = ixj_fasync
};
diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/at91_udc.h
index a973f2a50fb..c65d6229589 100644
--- a/drivers/usb/gadget/at91_udc.h
+++ b/drivers/usb/gadget/at91_udc.h
@@ -171,7 +171,7 @@ struct at91_request {
#endif
#define ERR(stuff...) pr_err("udc: " stuff)
-#define WARN(stuff...) pr_warning("udc: " stuff)
+#define WARNING(stuff...) pr_warning("udc: " stuff)
#define INFO(stuff...) pr_info("udc: " stuff)
#define DBG(stuff...) pr_debug("udc: " stuff)
diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c
index d490d028950..a39a4b940c3 100644
--- a/drivers/usb/gadget/cdc2.c
+++ b/drivers/usb/gadget/cdc2.c
@@ -170,7 +170,7 @@ static int __init cdc_bind(struct usb_composite_dev *cdev)
* but if the controller isn't recognized at all then
* that assumption is a bit more likely to be wrong.
*/
- WARN(cdev, "controller '%s' not recognized; trying %s\n",
+ WARNING(cdev, "controller '%s' not recognized; trying %s\n",
gadget->name,
cdc_config_driver.label);
device_desc.bcdDevice =
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index d7aaaa29b1e..bcac2e68660 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -293,7 +293,7 @@ static int __init eth_bind(struct usb_composite_dev *cdev)
* but if the controller isn't recognized at all then
* that assumption is a bit more likely to be wrong.
*/
- WARN(cdev, "controller '%s' not recognized; trying %s\n",
+ WARNING(cdev, "controller '%s' not recognized; trying %s\n",
gadget->name,
eth_config_driver.label);
device_desc.bcdDevice =
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index 15c24edbb61..ea2c31d1808 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -308,7 +308,7 @@ MODULE_LICENSE("Dual BSD/GPL");
dev_vdbg(&(d)->gadget->dev , fmt , ## args)
#define ERROR(d, fmt, args...) \
dev_err(&(d)->gadget->dev , fmt , ## args)
-#define WARN(d, fmt, args...) \
+#define WARNING(d, fmt, args...) \
dev_warn(&(d)->gadget->dev , fmt , ## args)
#define INFO(d, fmt, args...) \
dev_info(&(d)->gadget->dev , fmt , ## args)
@@ -1091,7 +1091,7 @@ static int ep0_queue(struct fsg_dev *fsg)
if (rc != 0 && rc != -ESHUTDOWN) {
/* We can't do much more than wait for a reset */
- WARN(fsg, "error in submission: %s --> %d\n",
+ WARNING(fsg, "error in submission: %s --> %d\n",
fsg->ep0->name, rc);
}
return rc;
@@ -1227,7 +1227,7 @@ static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh)
/* Save the command for later */
if (fsg->cbbuf_cmnd_size)
- WARN(fsg, "CB[I] overwriting previous command\n");
+ WARNING(fsg, "CB[I] overwriting previous command\n");
fsg->cbbuf_cmnd_size = req->actual;
memcpy(fsg->cbbuf_cmnd, req->buf, fsg->cbbuf_cmnd_size);
@@ -1506,7 +1506,7 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
* submissions if DMA is enabled. */
if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP &&
req->length == 0))
- WARN(fsg, "error in submission: %s --> %d\n",
+ WARNING(fsg, "error in submission: %s --> %d\n",
ep->name, rc);
}
}
@@ -2294,7 +2294,7 @@ static int halt_bulk_in_endpoint(struct fsg_dev *fsg)
VDBG(fsg, "delayed bulk-in endpoint halt\n");
while (rc != 0) {
if (rc != -EAGAIN) {
- WARN(fsg, "usb_ep_set_halt -> %d\n", rc);
+ WARNING(fsg, "usb_ep_set_halt -> %d\n", rc);
rc = 0;
break;
}
@@ -2317,7 +2317,7 @@ static int wedge_bulk_in_endpoint(struct fsg_dev *fsg)
VDBG(fsg, "delayed bulk-in endpoint wedge\n");
while (rc != 0) {
if (rc != -EAGAIN) {
- WARN(fsg, "usb_ep_set_wedge -> %d\n", rc);
+ WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc);
rc = 0;
break;
}
@@ -3755,7 +3755,7 @@ static int __init check_parameters(struct fsg_dev *fsg)
if (gcnum >= 0)
mod_data.release = 0x0300 + gcnum;
else {
- WARN(fsg, "controller '%s' not recognized\n",
+ WARNING(fsg, "controller '%s' not recognized\n",
fsg->gadget->name);
mod_data.release = 0x0399;
}
diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_usb2_udc.c
index 1695382f30f..1cfccf102a2 100644
--- a/drivers/usb/gadget/fsl_usb2_udc.c
+++ b/drivers/usb/gadget/fsl_usb2_udc.c
@@ -1538,7 +1538,7 @@ static void dtd_complete_irq(struct fsl_udc *udc)
/* If the ep is configured */
if (curr_ep->name == NULL) {
- WARN("Invalid EP?");
+ WARNING("Invalid EP?");
continue;
}
diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h
index 98b1483ef6a..6131752a38b 100644
--- a/drivers/usb/gadget/fsl_usb2_udc.h
+++ b/drivers/usb/gadget/fsl_usb2_udc.h
@@ -552,7 +552,7 @@ static void dump_msg(const char *label, const u8 * buf, unsigned int length)
#endif
#define ERR(stuff...) pr_err("udc: " stuff)
-#define WARN(stuff...) pr_warning("udc: " stuff)
+#define WARNING(stuff...) pr_warning("udc: " stuff)
#define INFO(stuff...) pr_info("udc: " stuff)
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c
index 7f4d4828e3a..ea8651e3da1 100644
--- a/drivers/usb/gadget/gmidi.c
+++ b/drivers/usb/gadget/gmidi.c
@@ -138,8 +138,6 @@ static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req);
dev_vdbg(&(d)->gadget->dev , fmt , ## args)
#define ERROR(d, fmt, args...) \
dev_err(&(d)->gadget->dev , fmt , ## args)
-#define WARN(d, fmt, args...) \
- dev_warn(&(d)->gadget->dev , fmt , ## args)
#define INFO(d, fmt, args...) \
dev_info(&(d)->gadget->dev , fmt , ## args)
diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c
index 48f1c63b701..60aa04847b1 100644
--- a/drivers/usb/gadget/goku_udc.c
+++ b/drivers/usb/gadget/goku_udc.c
@@ -1768,7 +1768,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
* usb_gadget_driver_{register,unregister}() must change.
*/
if (the_controller) {
- WARN(dev, "ignoring %s\n", pci_name(pdev));
+ WARNING(dev, "ignoring %s\n", pci_name(pdev));
return -EBUSY;
}
if (!pdev->irq) {
diff --git a/drivers/usb/gadget/goku_udc.h b/drivers/usb/gadget/goku_udc.h
index bc4eb1e0b50..566cb231905 100644
--- a/drivers/usb/gadget/goku_udc.h
+++ b/drivers/usb/gadget/goku_udc.h
@@ -285,7 +285,7 @@ struct goku_udc {
#define ERROR(dev,fmt,args...) \
xprintk(dev , KERN_ERR , fmt , ## args)
-#define WARN(dev,fmt,args...) \
+#define WARNING(dev,fmt,args...) \
xprintk(dev , KERN_WARNING , fmt , ## args)
#define INFO(dev,fmt,args...) \
xprintk(dev , KERN_INFO , fmt , ## args)
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index 04692d59fc1..f4585d3e90d 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -262,8 +262,6 @@ static const char *CHIP;
#define ERROR(dev,fmt,args...) \
xprintk(dev , KERN_ERR , fmt , ## args)
-#define WARN(dev,fmt,args...) \
- xprintk(dev , KERN_WARNING , fmt , ## args)
#define INFO(dev,fmt,args...) \
xprintk(dev , KERN_INFO , fmt , ## args)
diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c
index b67ab677af7..5cfb5ebf388 100644
--- a/drivers/usb/gadget/net2280.c
+++ b/drivers/usb/gadget/net2280.c
@@ -1007,7 +1007,7 @@ static void scan_dma_completions (struct net2280_ep *ep)
* 0122, and 0124; not all cases trigger the warning.
*/
if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) {
- WARN (ep->dev, "%s lost packet sync!\n",
+ WARNING (ep->dev, "%s lost packet sync!\n",
ep->ep.name);
req->req.status = -EOVERFLOW;
} else if ((tmp = readl (&ep->regs->ep_avail)) != 0) {
diff --git a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h
index 1f2af398a9a..81a71dbdc2c 100644
--- a/drivers/usb/gadget/net2280.h
+++ b/drivers/usb/gadget/net2280.h
@@ -272,7 +272,7 @@ static inline void net2280_led_shutdown (struct net2280 *dev)
#define ERROR(dev,fmt,args...) \
xprintk(dev , KERN_ERR , fmt , ## args)
-#define WARN(dev,fmt,args...) \
+#define WARNING(dev,fmt,args...) \
xprintk(dev , KERN_WARNING , fmt , ## args)
#define INFO(dev,fmt,args...) \
xprintk(dev , KERN_INFO , fmt , ## args)
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
index 4b79a8509e8..395bd184448 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/omap_udc.c
@@ -1120,7 +1120,7 @@ static int omap_ep_set_halt(struct usb_ep *_ep, int value)
status = -EINVAL;
else if (value) {
if (ep->udc->ep0_set_config) {
- WARN("error changing config?\n");
+ WARNING("error changing config?\n");
omap_writew(UDC_CLR_CFG, UDC_SYSCON2);
}
omap_writew(UDC_STALL_CMD, UDC_SYSCON2);
@@ -1764,7 +1764,7 @@ do_stall:
u.r.bRequestType, u.r.bRequest, status);
if (udc->ep0_set_config) {
if (udc->ep0_reset_config)
- WARN("error resetting config?\n");
+ WARNING("error resetting config?\n");
else
omap_writew(UDC_CLR_CFG, UDC_SYSCON2);
}
@@ -3076,7 +3076,7 @@ static int omap_udc_suspend(struct platform_device *dev, pm_message_t message)
* which would prevent entry to deep sleep...
*/
if ((devstat & UDC_ATT) != 0 && (devstat & UDC_SUS) == 0) {
- WARN("session active; suspend requires disconnect\n");
+ WARNING("session active; suspend requires disconnect\n");
omap_pullup(&udc->gadget, 0);
}
diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/omap_udc.h
index 8522bbb1227..29edc51b6b2 100644
--- a/drivers/usb/gadget/omap_udc.h
+++ b/drivers/usb/gadget/omap_udc.h
@@ -188,7 +188,7 @@ struct omap_udc {
#endif
#define ERR(stuff...) pr_err("udc: " stuff)
-#define WARN(stuff...) pr_warning("udc: " stuff)
+#define WARNING(stuff...) pr_warning("udc: " stuff)
#define INFO(stuff...) pr_info("udc: " stuff)
#define DBG(stuff...) pr_debug("udc: " stuff)
diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c
index 49cd9e145a9..e0090085b78 100644
--- a/drivers/usb/gadget/printer.c
+++ b/drivers/usb/gadget/printer.c
@@ -179,7 +179,7 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR);
#define ERROR(dev, fmt, args...) \
xprintk(dev, KERN_ERR, fmt, ## args)
-#define WARN(dev, fmt, args...) \
+#define WARNING(dev, fmt, args...) \
xprintk(dev, KERN_WARNING, fmt, ## args)
#define INFO(dev, fmt, args...) \
xprintk(dev, KERN_INFO, fmt, ## args)
diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c
index 8fb0066609b..7e6725d8997 100644
--- a/drivers/usb/gadget/pxa25x_udc.c
+++ b/drivers/usb/gadget/pxa25x_udc.c
@@ -342,7 +342,7 @@ pxa25x_ep_free_request (struct usb_ep *_ep, struct usb_request *_req)
struct pxa25x_request *req;
req = container_of (_req, struct pxa25x_request, req);
- WARN_ON (!list_empty (&req->queue));
+ WARN_ON(!list_empty (&req->queue));
kfree(req);
}
@@ -1556,7 +1556,7 @@ config_change:
* tell us about config change events,
* so later ones may fail...
*/
- WARN("config change %02x fail %d?\n",
+ WARNING("config change %02x fail %d?\n",
u.r.bRequest, i);
return;
/* TODO experiment: if has_cfr,
@@ -2330,7 +2330,7 @@ static int pxa25x_udc_suspend(struct platform_device *dev, pm_message_t state)
unsigned long flags;
if (!udc->mach->gpio_pullup && !udc->mach->udc_command)
- WARN("USB host won't detect disconnect!\n");
+ WARNING("USB host won't detect disconnect!\n");
udc->suspended = 1;
local_irq_save(flags);
diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/pxa25x_udc.h
index 4d11ece7c95..c8a13215e02 100644
--- a/drivers/usb/gadget/pxa25x_udc.h
+++ b/drivers/usb/gadget/pxa25x_udc.h
@@ -259,7 +259,7 @@ dump_state(struct pxa25x_udc *dev)
#define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0)
#define ERR(stuff...) pr_err("udc: " stuff)
-#define WARN(stuff...) pr_warning("udc: " stuff)
+#define WARNING(stuff...) pr_warning("udc: " stuff)
#define INFO(stuff...) pr_info("udc: " stuff)
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 5458f43a866..3791e627190 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -116,7 +116,6 @@ static inline int qlen(struct usb_gadget *gadget)
#undef DBG
#undef VDBG
#undef ERROR
-#undef WARN
#undef INFO
#define xprintk(d, level, fmt, args...) \
@@ -140,8 +139,6 @@ static inline int qlen(struct usb_gadget *gadget)
#define ERROR(dev, fmt, args...) \
xprintk(dev , KERN_ERR , fmt , ## args)
-#define WARN(dev, fmt, args...) \
- xprintk(dev , KERN_WARNING , fmt , ## args)
#define INFO(dev, fmt, args...) \
xprintk(dev , KERN_INFO , fmt , ## args)
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
index 31178e10cbb..ce1ca0ba051 100644
--- a/drivers/usb/host/isp116x-hcd.c
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -882,7 +882,7 @@ static void isp116x_endpoint_disable(struct usb_hcd *hcd,
for (i = 0; i < 100 && !list_empty(&hep->urb_list); i++)
msleep(3);
if (!list_empty(&hep->urb_list))
- WARN("ep %p not empty?\n", ep);
+ WARNING("ep %p not empty?\n", ep);
kfree(ep);
hep->hcpriv = NULL;
diff --git a/drivers/usb/host/isp116x.h b/drivers/usb/host/isp116x.h
index 595b90a9984..aa211bafcff 100644
--- a/drivers/usb/host/isp116x.h
+++ b/drivers/usb/host/isp116x.h
@@ -338,7 +338,7 @@ struct isp116x_ep {
#endif
#define ERR(stuff...) printk(KERN_ERR "116x: " stuff)
-#define WARN(stuff...) printk(KERN_WARNING "116x: " stuff)
+#define WARNING(stuff...) printk(KERN_WARNING "116x: " stuff)
#define INFO(stuff...) printk(KERN_INFO "116x: " stuff)
/* ------------------------------------------------- */
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index 340d72da554..8a74bbb57d0 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -1026,7 +1026,7 @@ sl811h_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
if (!list_empty(&hep->urb_list))
msleep(3);
if (!list_empty(&hep->urb_list))
- WARN("ep %p not empty?\n", ep);
+ WARNING("ep %p not empty?\n", ep);
kfree(ep);
hep->hcpriv = NULL;
diff --git a/drivers/usb/host/sl811.h b/drivers/usb/host/sl811.h
index 7690d98e42a..b6b8c1f233d 100644
--- a/drivers/usb/host/sl811.h
+++ b/drivers/usb/host/sl811.h
@@ -261,6 +261,6 @@ sl811_read_buf(struct sl811 *sl811, int addr, void *buf, size_t count)
#endif
#define ERR(stuff...) printk(KERN_ERR "sl811: " stuff)
-#define WARN(stuff...) printk(KERN_WARNING "sl811: " stuff)
+#define WARNING(stuff...) printk(KERN_WARNING "sl811: " stuff)
#define INFO(stuff...) printk(KERN_INFO "sl811: " stuff)
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index 054dedd2812..b358c4e1cf2 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -81,7 +81,7 @@ static struct usb_device *testdev_to_usbdev (struct usbtest_dev *test)
#define ERROR(tdev, fmt, args...) \
dev_err(&(tdev)->intf->dev , fmt , ## args)
-#define WARN(tdev, fmt, args...) \
+#define WARNING(tdev, fmt, args...) \
dev_warn(&(tdev)->intf->dev , fmt , ## args)
/*-------------------------------------------------------------------------*/
@@ -1946,7 +1946,7 @@ usbtest_probe (struct usb_interface *intf, const struct usb_device_id *id)
status = get_endpoints (dev, intf);
if (status < 0) {
- WARN(dev, "couldn't get endpoints, %d\n",
+ WARNING(dev, "couldn't get endpoints, %d\n",
status);
return status;
}
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 9b887ef64ff..70d135e0cc4 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1658,6 +1658,32 @@ config FB_PM3
similar boards, 3DLabs Permedia3 Create!, Appian Jeronimo 2000
and maybe other boards.
+config FB_CARMINE
+ tristate "Fujitsu carmine frame buffer support"
+ depends on FB && PCI
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ help
+ This is the frame buffer device driver for the Fujitsu Carmine chip.
+ The driver provides two independent frame buffer devices.
+
+choice
+ depends on FB_CARMINE
+ prompt "DRAM timing"
+ default FB_CARMINE_DRAM_EVAL
+
+config FB_CARMINE_DRAM_EVAL
+ bool "Eval board timings"
+ help
+ Use timings which work on the eval card.
+
+config CARMINE_DRAM_CUSTOM
+ bool "Custom board timings"
+ help
+ Use custom board timings.
+endchoice
+
config FB_AU1100
bool "Au1100 LCD Driver"
depends on (FB = y) && MIPS && SOC_AU1100
@@ -1840,6 +1866,16 @@ config FB_W100
If unsure, say N.
+config FB_SH_MOBILE_LCDC
+ tristate "SuperH Mobile LCDC framebuffer support"
+ depends on FB && SUPERH
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ default m
+ ---help---
+ Frame buffer driver for the on-chip SH-Mobile LCD controller.
+
config FB_S3C2410
tristate "S3C2410 LCD framebuffer support"
depends on FB && ARCH_S3C2410
@@ -1951,6 +1987,23 @@ config FB_AM200EPD
This enables support for the Metronome display controller used on
the E-Ink AM-200 EPD devkit.
+config FB_COBALT
+ tristate "Cobalt server LCD frame buffer support"
+ depends on FB && MIPS_COBALT
+
+config FB_SH7760
+ bool "SH7760/SH7763 LCDC support"
+ depends on FB && (CPU_SUBTYPE_SH7760 || CPU_SUBTYPE_SH7763)
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ help
+ Support for the SH7760/SH7763 integrated (D)STN/TFT LCD Controller.
+ Supports display resolutions up to 1024x1024 pixel, grayscale and
+ color operation, with depths ranging from 1 bpp to 8 bpp monochrome
+ and 8, 15 or 16 bpp color; 90 degrees clockwise display rotation for
+ panels <= 320 pixel horizontal resolution.
+
config FB_VIRTUAL
tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)"
depends on FB
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 04bca35403f..0ebc1bfd251 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -106,17 +106,22 @@ obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o
obj-$(CONFIG_FB_MAXINE) += maxinefb.o
obj-$(CONFIG_FB_METRONOME) += metronomefb.o
obj-$(CONFIG_FB_S1D13XXX) += s1d13xxxfb.o
+obj-$(CONFIG_FB_SH7760) += sh7760fb.o
obj-$(CONFIG_FB_IMX) += imxfb.o
obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o
obj-$(CONFIG_FB_FSL_DIU) += fsl-diu-fb.o
+obj-$(CONFIG_FB_COBALT) += cobalt_lcdfb.o
obj-$(CONFIG_FB_PNX4008_DUM) += pnx4008/
obj-$(CONFIG_FB_PNX4008_DUM_RGB) += pnx4008/
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_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o
+obj-$(CONFIG_FB_SH7343VOU) += sh7343_voufb.o
obj-$(CONFIG_FB_OMAP) += omap/
obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o
+obj-$(CONFIG_FB_CARMINE) += carminefb.o
# Platform or fallback drivers go here
obj-$(CONFIG_FB_UVESA) += uvesafb.o
diff --git a/drivers/video/acornfb.c b/drivers/video/acornfb.c
index eedb8285e32..017233d0c48 100644
--- a/drivers/video/acornfb.c
+++ b/drivers/video/acornfb.c
@@ -23,6 +23,7 @@
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/slab.h>
+#include <linux/mm.h>
#include <linux/init.h>
#include <linux/fb.h>
#include <linux/platform_device.h>
diff --git a/drivers/video/amifb.c b/drivers/video/amifb.c
index 45c154ade9c..b8e9a8682f2 100644
--- a/drivers/video/amifb.c
+++ b/drivers/video/amifb.c
@@ -1136,7 +1136,6 @@ static int amifb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg
* Interface to the low level console driver
*/
-int amifb_init(void);
static void amifb_deinit(void);
/*
@@ -2048,13 +2047,16 @@ static void amifb_copyarea(struct fb_info *info,
width = x2 - dx;
height = y2 - dy;
+ if (area->sx + dx < area->dx || area->sy + dy < area->dy)
+ return;
+
/* update sx,sy */
sx = area->sx + (dx - area->dx);
sy = area->sy + (dy - area->dy);
/* the source must be completely inside the virtual screen */
- if (sx < 0 || sy < 0 || (sx + width) > info->var.xres_virtual ||
- (sy + height) > info->var.yres_virtual)
+ if (sx + width > info->var.xres_virtual ||
+ sy + height > info->var.yres_virtual)
return;
if (dy > sy || (dy == sy && dx > sx)) {
@@ -2245,7 +2247,7 @@ static inline void chipfree(void)
* Initialisation
*/
-int __init amifb_init(void)
+static int __init amifb_init(void)
{
int tag, i, err = 0;
u_long chipptr;
@@ -3790,16 +3792,14 @@ static void ami_rebuild_copper(void)
}
}
-
-module_init(amifb_init);
-
-#ifdef MODULE
-MODULE_LICENSE("GPL");
-
-void cleanup_module(void)
+static void __exit amifb_exit(void)
{
unregister_framebuffer(&fb_info);
amifb_deinit();
amifb_video_off();
}
-#endif /* MODULE */
+
+module_init(amifb_init);
+module_exit(amifb_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/atafb.c b/drivers/video/atafb.c
index fa55d356b53..77eb8b34fbf 100644
--- a/drivers/video/atafb.c
+++ b/drivers/video/atafb.c
@@ -2593,13 +2593,16 @@ static void atafb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
width = x2 - dx;
height = y2 - dy;
+ if (area->sx + dx < area->dx || area->sy + dy < area->dy)
+ return;
+
/* update sx,sy */
sx = area->sx + (dx - area->dx);
sy = area->sy + (dy - area->dy);
/* the source must be completely inside the virtual screen */
- if (sx < 0 || sy < 0 || (sx + width) > info->var.xres_virtual ||
- (sy + height) > info->var.yres_virtual)
+ if (sx + width > info->var.xres_virtual ||
+ sy + height > info->var.yres_virtual)
return;
if (dy > sy || (dy == sy && dx > sx)) {
diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
index b004036d408..5b3a15dffb5 100644
--- a/drivers/video/atmel_lcdfb.c
+++ b/drivers/video/atmel_lcdfb.c
@@ -256,6 +256,20 @@ static int atmel_lcdfb_alloc_video_memory(struct atmel_lcdfb_info *sinfo)
return 0;
}
+static const struct fb_videomode *atmel_lcdfb_choose_mode(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct fb_videomode varfbmode;
+ const struct fb_videomode *fbmode = NULL;
+
+ fb_var_to_videomode(&varfbmode, var);
+ fbmode = fb_find_nearest_mode(&varfbmode, &info->modelist);
+ if (fbmode)
+ fb_videomode_to_var(var, fbmode);
+ return fbmode;
+}
+
+
/**
* atmel_lcdfb_check_var - Validates a var passed in.
* @var: frame buffer variable screen structure
@@ -289,6 +303,15 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var,
clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000;
dev_dbg(dev, "%s:\n", __func__);
+
+ if (!(var->pixclock && var->bits_per_pixel)) {
+ /* choose a suitable mode if possible */
+ if (!atmel_lcdfb_choose_mode(var, info)) {
+ dev_err(dev, "needed value not specified\n");
+ return -EINVAL;
+ }
+ }
+
dev_dbg(dev, " resolution: %ux%u\n", var->xres, var->yres);
dev_dbg(dev, " pixclk: %lu KHz\n", PICOS2KHZ(var->pixclock));
dev_dbg(dev, " bpp: %u\n", var->bits_per_pixel);
@@ -299,6 +322,13 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var,
return -EINVAL;
}
+ /* Do not allow to have real resoulution larger than virtual */
+ if (var->xres > var->xres_virtual)
+ var->xres_virtual = var->xres;
+
+ if (var->yres > var->yres_virtual)
+ var->yres_virtual = var->yres;
+
/* Force same alignment for each line */
var->xres = (var->xres + 3) & ~3UL;
var->xres_virtual = (var->xres_virtual + 3) & ~3UL;
@@ -379,6 +409,35 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var,
return 0;
}
+/*
+ * LCD reset sequence
+ */
+static void atmel_lcdfb_reset(struct atmel_lcdfb_info *sinfo)
+{
+ might_sleep();
+
+ /* LCD power off */
+ lcdc_writel(sinfo, ATMEL_LCDC_PWRCON, sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET);
+
+ /* wait for the LCDC core to become idle */
+ while (lcdc_readl(sinfo, ATMEL_LCDC_PWRCON) & ATMEL_LCDC_BUSY)
+ msleep(10);
+
+ /* DMA disable */
+ lcdc_writel(sinfo, ATMEL_LCDC_DMACON, 0);
+
+ /* wait for DMA engine to become idle */
+ while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & ATMEL_LCDC_DMABUSY)
+ msleep(10);
+
+ /* LCD power on */
+ lcdc_writel(sinfo, ATMEL_LCDC_PWRCON,
+ (sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET) | ATMEL_LCDC_PWR);
+
+ /* DMA enable */
+ lcdc_writel(sinfo, ATMEL_LCDC_DMACON, sinfo->default_dmacon);
+}
+
/**
* atmel_lcdfb_set_par - Alters the hardware state.
* @info: frame buffer structure that represents a single frame buffer
@@ -401,6 +460,8 @@ static int atmel_lcdfb_set_par(struct fb_info *info)
unsigned long clk_value_khz;
unsigned long bits_per_line;
+ might_sleep();
+
dev_dbg(info->device, "%s:\n", __func__);
dev_dbg(info->device, " * resolution: %ux%u (%ux%u virtual)\n",
info->var.xres, info->var.yres,
@@ -511,6 +572,8 @@ static int atmel_lcdfb_set_par(struct fb_info *info)
/* Disable all interrupts */
lcdc_writel(sinfo, ATMEL_LCDC_IDR, ~0UL);
+ /* Enable FIFO & DMA errors */
+ lcdc_writel(sinfo, ATMEL_LCDC_IER, ATMEL_LCDC_UFLWI | ATMEL_LCDC_OWRI | ATMEL_LCDC_MERI);
/* ...wait for DMA engine to become idle... */
while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & ATMEL_LCDC_DMABUSY)
@@ -645,10 +708,26 @@ static irqreturn_t atmel_lcdfb_interrupt(int irq, void *dev_id)
u32 status;
status = lcdc_readl(sinfo, ATMEL_LCDC_ISR);
- lcdc_writel(sinfo, ATMEL_LCDC_IDR, status);
+ if (status & ATMEL_LCDC_UFLWI) {
+ dev_warn(info->device, "FIFO underflow %#x\n", status);
+ /* reset DMA and FIFO to avoid screen shifting */
+ schedule_work(&sinfo->task);
+ }
+ lcdc_writel(sinfo, ATMEL_LCDC_ICR, status);
return IRQ_HANDLED;
}
+/*
+ * LCD controller task (to reset the LCD)
+ */
+static void atmel_lcdfb_task(struct work_struct *work)
+{
+ struct atmel_lcdfb_info *sinfo =
+ container_of(work, struct atmel_lcdfb_info, task);
+
+ atmel_lcdfb_reset(sinfo);
+}
+
static int __init atmel_lcdfb_init_fbinfo(struct atmel_lcdfb_info *sinfo)
{
struct fb_info *info = sinfo->info;
@@ -691,6 +770,7 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
struct fb_info *info;
struct atmel_lcdfb_info *sinfo;
struct atmel_lcdfb_info *pdata_sinfo;
+ struct fb_videomode fbmode;
struct resource *regs = NULL;
struct resource *map = NULL;
int ret;
@@ -824,6 +904,10 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
goto unmap_mmio;
}
+ /* Some operations on the LCDC might sleep and
+ * require a preemptible task context */
+ INIT_WORK(&sinfo->task, atmel_lcdfb_task);
+
ret = atmel_lcdfb_init_fbinfo(sinfo);
if (ret < 0) {
dev_err(dev, "init fbinfo failed: %d\n", ret);
@@ -853,6 +937,10 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
goto free_cmap;
}
+ /* add selected videomode to modelist */
+ fb_var_to_videomode(&fbmode, &info->var);
+ fb_add_videomode(&fbmode, &info->modelist);
+
/* Power up the LCDC screen */
if (sinfo->atmel_lcdfb_power_control)
sinfo->atmel_lcdfb_power_control(1);
@@ -866,6 +954,7 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
free_cmap:
fb_dealloc_cmap(&info->cmap);
unregister_irqs:
+ cancel_work_sync(&sinfo->task);
free_irq(sinfo->irq_base, info);
unmap_mmio:
exit_backlight(sinfo);
@@ -903,6 +992,7 @@ static int __exit atmel_lcdfb_remove(struct platform_device *pdev)
if (!sinfo)
return 0;
+ cancel_work_sync(&sinfo->task);
exit_backlight(sinfo);
if (sinfo->atmel_lcdfb_power_control)
sinfo->atmel_lcdfb_power_control(0);
diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c
index 07b6addbb3c..243ea4ab20c 100644
--- a/drivers/video/aty/aty128fb.c
+++ b/drivers/video/aty/aty128fb.c
@@ -1339,10 +1339,8 @@ static int aty128_var_to_pll(u32 period_in_ps, struct aty128_pll *pll,
if (vclk * 12 < c.ppll_min)
vclk = c.ppll_min/12;
- pll->post_divider = -1;
-
/* now, find an acceptable divider */
- for (i = 0; i < sizeof(post_dividers); i++) {
+ for (i = 0; i < ARRAY_SIZE(post_dividers); i++) {
output_freq = post_dividers[i] * vclk;
if (output_freq >= c.ppll_min && output_freq <= c.ppll_max) {
pll->post_divider = post_dividers[i];
@@ -1350,7 +1348,7 @@ static int aty128_var_to_pll(u32 period_in_ps, struct aty128_pll *pll,
}
}
- if (pll->post_divider < 0)
+ if (i == ARRAY_SIZE(post_dividers))
return -EINVAL;
/* calculate feedback divider */
diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c
index bd4ac0bafec..620ba812036 100644
--- a/drivers/video/aty/atyfb_base.c
+++ b/drivers/video/aty/atyfb_base.c
@@ -424,7 +424,6 @@ static struct {
#endif /* CONFIG_FB_ATY_CT */
};
-/* can not fail */
static int __devinit correct_chipset(struct atyfb_par *par)
{
u8 rev;
@@ -437,6 +436,9 @@ static int __devinit correct_chipset(struct atyfb_par *par)
if (par->pci_id == aty_chips[i].pci_id)
break;
+ if (i < 0)
+ return -ENODEV;
+
name = aty_chips[i].name;
par->pll_limits.pll_max = aty_chips[i].pll;
par->pll_limits.mclk = aty_chips[i].mclk;
@@ -2229,6 +2231,7 @@ static int __devinit aty_init(struct fb_info *info)
const char *ramname = NULL, *xtal;
int gtb_memsize, has_var = 0;
struct fb_var_screeninfo var;
+ int ret;
init_waitqueue_head(&par->vblank.wait);
spin_lock_init(&par->int_lock);
@@ -2610,7 +2613,8 @@ static int __devinit aty_init(struct fb_info *info)
var.yres_virtual = var.yres;
}
- if (atyfb_check_var(&var, info)) {
+ ret = atyfb_check_var(&var, info);
+ if (ret) {
PRINTKE("can't set default video mode\n");
goto aty_init_exit;
}
@@ -2621,10 +2625,12 @@ static int __devinit aty_init(struct fb_info *info)
#endif /* CONFIG_FB_ATY_CT */
info->var = var;
- if (fb_alloc_cmap(&info->cmap, 256, 0) < 0)
+ ret = fb_alloc_cmap(&info->cmap, 256, 0);
+ if (ret < 0)
goto aty_init_exit;
- if (register_framebuffer(info) < 0) {
+ ret = register_framebuffer(info);
+ if (ret < 0) {
fb_dealloc_cmap(&info->cmap);
goto aty_init_exit;
}
@@ -2650,7 +2656,7 @@ aty_init_exit:
par->mtrr_aper = -1;
}
#endif
- return -1;
+ return ret;
}
static void aty_resume_chip(struct fb_info *info)
@@ -2709,8 +2715,7 @@ static int atyfb_blank(int blank, struct fb_info *info)
if (par->lock_blank || par->asleep)
return 0;
-#ifdef CONFIG_FB_ATY_BACKLIGHT
-#elif defined(CONFIG_FB_ATY_GENERIC_LCD)
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
if (par->lcd_table && blank > FB_BLANK_NORMAL &&
(aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) {
u32 pm = aty_ld_lcd(POWER_MANAGEMENT, par);
@@ -2739,8 +2744,7 @@ static int atyfb_blank(int blank, struct fb_info *info)
}
aty_st_le32(CRTC_GEN_CNTL, gen_cntl, par);
-#ifdef CONFIG_FB_ATY_BACKLIGHT
-#elif defined(CONFIG_FB_ATY_GENERIC_LCD)
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
if (par->lcd_table && blank <= FB_BLANK_NORMAL &&
(aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) {
u32 pm = aty_ld_lcd(POWER_MANAGEMENT, par);
@@ -3331,7 +3335,7 @@ static int __devinit init_from_bios(struct atyfb_par *par)
PRINTKE("no BIOS frequency table found, use parameters\n");
ret = -ENXIO;
}
- iounmap((void* __iomem )bios_base);
+ iounmap((void __iomem *)bios_base);
return ret;
}
@@ -3418,14 +3422,7 @@ static int __devinit atyfb_pci_probe(struct pci_dev *pdev, const struct pci_devi
struct fb_info *info;
struct resource *rp;
struct atyfb_par *par;
- int i, rc = -ENOMEM;
-
- for (i = ARRAY_SIZE(aty_chips) - 1; i >= 0; i--)
- if (pdev->device == aty_chips[i].pci_id)
- break;
-
- if (i < 0)
- return -ENODEV;
+ int rc = -ENOMEM;
/* Enable device in PCI config */
if (pci_enable_device(pdev)) {
@@ -3456,7 +3453,7 @@ static int __devinit atyfb_pci_probe(struct pci_dev *pdev, const struct pci_devi
par = info->par;
info->fix = atyfb_fix;
info->device = &pdev->dev;
- par->pci_id = aty_chips[i].pci_id;
+ par->pci_id = pdev->device;
par->res_start = res_start;
par->res_size = res_size;
par->irq = pdev->irq;
@@ -3474,7 +3471,8 @@ static int __devinit atyfb_pci_probe(struct pci_dev *pdev, const struct pci_devi
pci_set_drvdata(pdev, info);
/* Init chip & register framebuffer */
- if (aty_init(info))
+ rc = aty_init(info);
+ if (rc)
goto err_release_io;
#ifdef __sparc__
@@ -3655,18 +3653,62 @@ static void __devexit atyfb_pci_remove(struct pci_dev *pdev)
atyfb_remove(info);
}
-/*
- * This driver uses its own matching table. That will be more difficult
- * to fix, so for now, we just match against any ATI ID and let the
- * probe() function find out what's up. That also mean we don't have
- * a module ID table though.
- */
static struct pci_device_id atyfb_pci_tbl[] = {
- { PCI_VENDOR_ID_ATI, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
- PCI_BASE_CLASS_DISPLAY << 16, 0xff0000, 0 },
- { 0, }
+#ifdef CONFIG_FB_ATY_GX
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GX) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64CX) },
+#endif /* CONFIG_FB_ATY_GX */
+
+#ifdef CONFIG_FB_ATY_CT
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64CT) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64ET) },
+
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LT) },
+
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64VT) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GT) },
+
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64VU) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GU) },
+
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LG) },
+
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64VV) },
+
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GV) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GW) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GY) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GZ) },
+
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GB) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GD) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GI) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GP) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GQ) },
+
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LB) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LD) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LI) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LP) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LQ) },
+
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GM) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GN) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GO) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GL) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GR) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GS) },
+
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LM) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LN) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LR) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LS) },
+#endif /* CONFIG_FB_ATY_CT */
+ { }
};
+MODULE_DEVICE_TABLE(pci, atyfb_pci_tbl);
+
static struct pci_driver atyfb_driver = {
.name = "atyfb",
.id_table = atyfb_pci_tbl,
diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c
index 400e9264e45..652273e9f5f 100644
--- a/drivers/video/aty/radeon_base.c
+++ b/drivers/video/aty/radeon_base.c
@@ -2098,15 +2098,7 @@ static void radeon_identify_vram(struct radeonfb_info *rinfo)
static ssize_t radeon_show_one_edid(char *buf, loff_t off, size_t count, const u8 *edid)
{
- if (off > EDID_LENGTH)
- return 0;
-
- if (off + count > EDID_LENGTH)
- count = EDID_LENGTH - off;
-
- memcpy(buf, edid + off, count);
-
- return count;
+ return memory_read_from_buffer(buf, count, &off, edid, EDID_LENGTH);
}
@@ -2161,6 +2153,7 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev,
struct radeonfb_info *rinfo;
int ret;
unsigned char c1, c2;
+ int err = 0;
pr_debug("radeonfb_pci_register BEGIN\n");
@@ -2340,9 +2333,14 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev,
/* Register some sysfs stuff (should be done better) */
if (rinfo->mon1_EDID)
- sysfs_create_bin_file(&rinfo->pdev->dev.kobj, &edid1_attr);
+ err |= sysfs_create_bin_file(&rinfo->pdev->dev.kobj,
+ &edid1_attr);
if (rinfo->mon2_EDID)
- sysfs_create_bin_file(&rinfo->pdev->dev.kobj, &edid2_attr);
+ err |= sysfs_create_bin_file(&rinfo->pdev->dev.kobj,
+ &edid2_attr);
+ if (err)
+ pr_warning("%s() Creating sysfs files failed, continuing\n",
+ __func__);
/* save current mode regs before we switch into the new one
* so we can restore this upon __exit
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 30bf7f2f163..452b770d8cc 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -36,6 +36,30 @@ config LCD_LTV350QV
The LTV350QV panel is present on all ATSTK1000 boards.
+config LCD_ILI9320
+ tristate
+ depends on LCD_CLASS_DEVICE && BACKLIGHT_LCD_SUPPORT
+ default n
+ help
+ If you have a panel based on the ILI9320 controller chip
+ then say y to include a power driver for it.
+
+config LCD_VGG2432A4
+ tristate "VGG2432A4 LCM device support"
+ depends on BACKLIGHT_LCD_SUPPORT && LCD_CLASS_DEVICE && SPI_MASTER
+ select LCD_ILI9320
+ default n
+ help
+ If you have a VGG2432A4 panel based on the ILI9320 controller chip
+ then say y to include a power driver for it.
+
+config LCD_PLATFORM
+ tristate "Platform LCD controls"
+ depends on LCD_CLASS_DEVICE
+ help
+ This driver provides a platform-device registered LCD power
+ control interface.
+
#
# Backlight
#
@@ -63,6 +87,18 @@ config BACKLIGHT_ATMEL_LCDC
If in doubt, it's safe to enable this option; it doesn't kick
in unless the board's description says it's wired that way.
+config BACKLIGHT_ATMEL_PWM
+ tristate "Atmel PWM backlight control"
+ depends on BACKLIGHT_CLASS_DEVICE && ATMEL_PWM
+ default n
+ help
+ Say Y here if you want to use the PWM peripheral in Atmel AT91 and
+ AVR32 devices. This driver will need additional platform data to know
+ which PWM instance to use and how to configure it.
+
+ To compile this driver as a module, choose M here: the module will be
+ called atmel-pwm-bl.
+
config BACKLIGHT_CORGI
tristate "Generic (aka Sharp Corgi) Backlight Driver"
depends on BACKLIGHT_CLASS_DEVICE
@@ -119,3 +155,12 @@ config BACKLIGHT_PWM
help
If you have a LCD backlight adjustable by PWM, say Y to enable
this driver.
+
+config BACKLIGHT_MBP_NVIDIA
+ tristate "MacBook Pro Nvidia Backlight Driver"
+ depends on BACKLIGHT_CLASS_DEVICE && X86
+ default n
+ help
+ If you have an Apple Macbook Pro with Nvidia graphics hardware say Y
+ to enable a driver for its backlight
+
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index b51a7cd1250..b405aace803 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -1,9 +1,13 @@
# Backlight & LCD drivers
obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o
-obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o
+obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o
+obj-$(CONFIG_LCD_ILI9320) += ili9320.o
+obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o
+obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o
obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
+obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o
obj-$(CONFIG_BACKLIGHT_CORGI) += corgi_bl.o
obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o
obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o
@@ -11,3 +15,5 @@ obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o
obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o
obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o
obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o
+obj-$(CONFIG_BACKLIGHT_MBP_NVIDIA) += mbp_nvidia_bl.o
+
diff --git a/drivers/video/backlight/atmel-pwm-bl.c b/drivers/video/backlight/atmel-pwm-bl.c
new file mode 100644
index 00000000000..505c0823a10
--- /dev/null
+++ b/drivers/video/backlight/atmel-pwm-bl.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2008 Atmel Corporation
+ *
+ * Backlight driver using Atmel PWM peripheral.
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/fb.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/backlight.h>
+#include <linux/atmel_pwm.h>
+#include <linux/atmel-pwm-bl.h>
+
+struct atmel_pwm_bl {
+ const struct atmel_pwm_bl_platform_data *pdata;
+ struct backlight_device *bldev;
+ struct platform_device *pdev;
+ struct pwm_channel pwmc;
+ int gpio_on;
+};
+
+static int atmel_pwm_bl_set_intensity(struct backlight_device *bd)
+{
+ struct atmel_pwm_bl *pwmbl = bl_get_data(bd);
+ int intensity = bd->props.brightness;
+ int pwm_duty;
+
+ if (bd->props.power != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
+ intensity = 0;
+
+ if (pwmbl->pdata->pwm_active_low)
+ pwm_duty = pwmbl->pdata->pwm_duty_min + intensity;
+ else
+ pwm_duty = pwmbl->pdata->pwm_duty_max - intensity;
+
+ if (pwm_duty > pwmbl->pdata->pwm_duty_max)
+ pwm_duty = pwmbl->pdata->pwm_duty_max;
+ if (pwm_duty < pwmbl->pdata->pwm_duty_min)
+ pwm_duty = pwmbl->pdata->pwm_duty_min;
+
+ if (!intensity) {
+ if (pwmbl->gpio_on != -1) {
+ gpio_set_value(pwmbl->gpio_on,
+ 0 ^ pwmbl->pdata->on_active_low);
+ }
+ pwm_channel_writel(&pwmbl->pwmc, PWM_CUPD, pwm_duty);
+ pwm_channel_disable(&pwmbl->pwmc);
+ } else {
+ pwm_channel_enable(&pwmbl->pwmc);
+ pwm_channel_writel(&pwmbl->pwmc, PWM_CUPD, pwm_duty);
+ if (pwmbl->gpio_on != -1) {
+ gpio_set_value(pwmbl->gpio_on,
+ 1 ^ pwmbl->pdata->on_active_low);
+ }
+ }
+
+ return 0;
+}
+
+static int atmel_pwm_bl_get_intensity(struct backlight_device *bd)
+{
+ struct atmel_pwm_bl *pwmbl = bl_get_data(bd);
+ u8 intensity;
+
+ if (pwmbl->pdata->pwm_active_low) {
+ intensity = pwm_channel_readl(&pwmbl->pwmc, PWM_CDTY) -
+ pwmbl->pdata->pwm_duty_min;
+ } else {
+ intensity = pwmbl->pdata->pwm_duty_max -
+ pwm_channel_readl(&pwmbl->pwmc, PWM_CDTY);
+ }
+
+ return intensity;
+}
+
+static int atmel_pwm_bl_init_pwm(struct atmel_pwm_bl *pwmbl)
+{
+ unsigned long pwm_rate = pwmbl->pwmc.mck;
+ unsigned long prescale = DIV_ROUND_UP(pwm_rate,
+ (pwmbl->pdata->pwm_frequency *
+ pwmbl->pdata->pwm_compare_max)) - 1;
+
+ /*
+ * Prescale must be power of two and maximum 0xf in size because of
+ * hardware limit. PWM speed will be:
+ * PWM module clock speed / (2 ^ prescale).
+ */
+ prescale = fls(prescale);
+ if (prescale > 0xf)
+ prescale = 0xf;
+
+ pwm_channel_writel(&pwmbl->pwmc, PWM_CMR, prescale);
+ pwm_channel_writel(&pwmbl->pwmc, PWM_CDTY,
+ pwmbl->pdata->pwm_duty_min +
+ pwmbl->bldev->props.brightness);
+ pwm_channel_writel(&pwmbl->pwmc, PWM_CPRD,
+ pwmbl->pdata->pwm_compare_max);
+
+ dev_info(&pwmbl->pdev->dev, "Atmel PWM backlight driver "
+ "(%lu Hz)\n", pwmbl->pwmc.mck /
+ pwmbl->pdata->pwm_compare_max /
+ (1 << prescale));
+
+ return pwm_channel_enable(&pwmbl->pwmc);
+}
+
+static struct backlight_ops atmel_pwm_bl_ops = {
+ .get_brightness = atmel_pwm_bl_get_intensity,
+ .update_status = atmel_pwm_bl_set_intensity,
+};
+
+static int atmel_pwm_bl_probe(struct platform_device *pdev)
+{
+ const struct atmel_pwm_bl_platform_data *pdata;
+ struct backlight_device *bldev;
+ struct atmel_pwm_bl *pwmbl;
+ int retval;
+
+ pwmbl = kzalloc(sizeof(struct atmel_pwm_bl), GFP_KERNEL);
+ if (!pwmbl)
+ return -ENOMEM;
+
+ pwmbl->pdev = pdev;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ retval = -ENODEV;
+ goto err_free_mem;
+ }
+
+ if (pdata->pwm_compare_max < pdata->pwm_duty_max ||
+ pdata->pwm_duty_min > pdata->pwm_duty_max ||
+ pdata->pwm_frequency == 0) {
+ retval = -EINVAL;
+ goto err_free_mem;
+ }
+
+ pwmbl->pdata = pdata;
+ pwmbl->gpio_on = pdata->gpio_on;
+
+ retval = pwm_channel_alloc(pdata->pwm_channel, &pwmbl->pwmc);
+ if (retval)
+ goto err_free_mem;
+
+ if (pwmbl->gpio_on != -1) {
+ retval = gpio_request(pwmbl->gpio_on, "gpio_atmel_pwm_bl");
+ if (retval) {
+ pwmbl->gpio_on = -1;
+ goto err_free_pwm;
+ }
+
+ /* Turn display off by defatult. */
+ retval = gpio_direction_output(pwmbl->gpio_on,
+ 0 ^ pdata->on_active_low);
+ if (retval)
+ goto err_free_gpio;
+ }
+
+ bldev = backlight_device_register("atmel-pwm-bl",
+ &pdev->dev, pwmbl, &atmel_pwm_bl_ops);
+ if (IS_ERR(bldev)) {
+ retval = PTR_ERR(bldev);
+ goto err_free_gpio;
+ }
+
+ pwmbl->bldev = bldev;
+
+ platform_set_drvdata(pdev, pwmbl);
+
+ /* Power up the backlight by default at middle intesity. */
+ bldev->props.power = FB_BLANK_UNBLANK;
+ bldev->props.max_brightness = pdata->pwm_duty_max - pdata->pwm_duty_min;
+ bldev->props.brightness = bldev->props.max_brightness / 2;
+
+ retval = atmel_pwm_bl_init_pwm(pwmbl);
+ if (retval)
+ goto err_free_bl_dev;
+
+ atmel_pwm_bl_set_intensity(bldev);
+
+ return 0;
+
+err_free_bl_dev:
+ platform_set_drvdata(pdev, NULL);
+ backlight_device_unregister(bldev);
+err_free_gpio:
+ if (pwmbl->gpio_on != -1)
+ gpio_free(pwmbl->gpio_on);
+err_free_pwm:
+ pwm_channel_free(&pwmbl->pwmc);
+err_free_mem:
+ kfree(pwmbl);
+ return retval;
+}
+
+static int __exit atmel_pwm_bl_remove(struct platform_device *pdev)
+{
+ struct atmel_pwm_bl *pwmbl = platform_get_drvdata(pdev);
+
+ if (pwmbl->gpio_on != -1) {
+ gpio_set_value(pwmbl->gpio_on, 0);
+ gpio_free(pwmbl->gpio_on);
+ }
+ pwm_channel_disable(&pwmbl->pwmc);
+ pwm_channel_free(&pwmbl->pwmc);
+ backlight_device_unregister(pwmbl->bldev);
+ platform_set_drvdata(pdev, NULL);
+ kfree(pwmbl);
+
+ return 0;
+}
+
+static struct platform_driver atmel_pwm_bl_driver = {
+ .driver = {
+ .name = "atmel-pwm-bl",
+ },
+ /* REVISIT add suspend() and resume() */
+ .remove = __exit_p(atmel_pwm_bl_remove),
+};
+
+static int __init atmel_pwm_bl_init(void)
+{
+ return platform_driver_probe(&atmel_pwm_bl_driver, atmel_pwm_bl_probe);
+}
+module_init(atmel_pwm_bl_init);
+
+static void __exit atmel_pwm_bl_exit(void)
+{
+ platform_driver_unregister(&atmel_pwm_bl_driver);
+}
+module_exit(atmel_pwm_bl_exit);
+
+MODULE_AUTHOR("Hans-Christian egtvedt <hans-christian.egtvedt@atmel.com>");
+MODULE_DESCRIPTION("Atmel PWM backlight driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c
index 39394757679..fab0bc874b5 100644
--- a/drivers/video/backlight/backlight.c
+++ b/drivers/video/backlight/backlight.c
@@ -191,6 +191,7 @@ static struct device_attribute bl_device_attributes[] = {
* backlight_device class.
* @name: the name of the new object(must be the same as the name of the
* respective framebuffer device).
+ * @parent: a pointer to the parent device
* @devdata: an optional pointer to be stored for private driver use. The
* methods may retrieve it by using bl_get_data(bd).
* @ops: the backlight operations structure.
diff --git a/drivers/video/backlight/ili9320.c b/drivers/video/backlight/ili9320.c
new file mode 100644
index 00000000000..ba89b41b639
--- /dev/null
+++ b/drivers/video/backlight/ili9320.c
@@ -0,0 +1,330 @@
+/* drivers/video/backlight/ili9320.c
+ *
+ * ILI9320 LCD controller driver core.
+ *
+ * Copyright 2007 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * 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/err.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/lcd.h>
+#include <linux/module.h>
+
+#include <linux/spi/spi.h>
+
+#include <video/ili9320.h>
+
+#include "ili9320.h"
+
+
+static inline int ili9320_write_spi(struct ili9320 *ili,
+ unsigned int reg,
+ unsigned int value)
+{
+ struct ili9320_spi *spi = &ili->access.spi;
+ unsigned char *addr = spi->buffer_addr;
+ unsigned char *data = spi->buffer_data;
+
+ /* spi message consits of:
+ * first byte: ID and operation
+ */
+
+ addr[0] = spi->id | ILI9320_SPI_INDEX | ILI9320_SPI_WRITE;
+ addr[1] = reg >> 8;
+ addr[2] = reg;
+
+ /* second message is the data to transfer */
+
+ data[0] = spi->id | ILI9320_SPI_DATA | ILI9320_SPI_WRITE;
+ data[1] = value >> 8;
+ data[2] = value;
+
+ return spi_sync(spi->dev, &spi->message);
+}
+
+int ili9320_write(struct ili9320 *ili, unsigned int reg, unsigned int value)
+{
+ dev_dbg(ili->dev, "write: reg=%02x, val=%04x\n", reg, value);
+ return ili->write(ili, reg, value);
+}
+
+EXPORT_SYMBOL_GPL(ili9320_write);
+
+int ili9320_write_regs(struct ili9320 *ili,
+ struct ili9320_reg *values,
+ int nr_values)
+{
+ int index;
+ int ret;
+
+ for (index = 0; index < nr_values; index++, values++) {
+ ret = ili9320_write(ili, values->address, values->value);
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(ili9320_write_regs);
+
+static void ili9320_reset(struct ili9320 *lcd)
+{
+ struct ili9320_platdata *cfg = lcd->platdata;
+
+ cfg->reset(1);
+ mdelay(50);
+
+ cfg->reset(0);
+ mdelay(50);
+
+ cfg->reset(1);
+ mdelay(100);
+}
+
+static inline int ili9320_init_chip(struct ili9320 *lcd)
+{
+ int ret;
+
+ ili9320_reset(lcd);
+
+ ret = lcd->client->init(lcd, lcd->platdata);
+ if (ret != 0) {
+ dev_err(lcd->dev, "failed to initialise display\n");
+ return ret;
+ }
+
+ lcd->initialised = 1;
+ return 0;
+}
+
+static inline int ili9320_power_on(struct ili9320 *lcd)
+{
+ if (!lcd->initialised)
+ ili9320_init_chip(lcd);
+
+ lcd->display1 |= (ILI9320_DISPLAY1_D(3) | ILI9320_DISPLAY1_BASEE);
+ ili9320_write(lcd, ILI9320_DISPLAY1, lcd->display1);
+
+ return 0;
+}
+
+static inline int ili9320_power_off(struct ili9320 *lcd)
+{
+ lcd->display1 &= ~(ILI9320_DISPLAY1_D(3) | ILI9320_DISPLAY1_BASEE);
+ ili9320_write(lcd, ILI9320_DISPLAY1, lcd->display1);
+
+ return 0;
+}
+
+#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL)
+
+static int ili9320_power(struct ili9320 *lcd, int power)
+{
+ int ret = 0;
+
+ dev_dbg(lcd->dev, "power %d => %d\n", lcd->power, power);
+
+ if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
+ ret = ili9320_power_on(lcd);
+ else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
+ ret = ili9320_power_off(lcd);
+
+ if (ret == 0)
+ lcd->power = power;
+ else
+ dev_warn(lcd->dev, "failed to set power mode %d\n", power);
+
+ return ret;
+}
+
+static inline struct ili9320 *to_our_lcd(struct lcd_device *lcd)
+{
+ return lcd_get_data(lcd);
+}
+
+static int ili9320_set_power(struct lcd_device *ld, int power)
+{
+ struct ili9320 *lcd = to_our_lcd(ld);
+
+ return ili9320_power(lcd, power);
+}
+
+static int ili9320_get_power(struct lcd_device *ld)
+{
+ struct ili9320 *lcd = to_our_lcd(ld);
+
+ return lcd->power;
+}
+
+static struct lcd_ops ili9320_ops = {
+ .get_power = ili9320_get_power,
+ .set_power = ili9320_set_power,
+};
+
+static void __devinit ili9320_setup_spi(struct ili9320 *ili,
+ struct spi_device *dev)
+{
+ struct ili9320_spi *spi = &ili->access.spi;
+
+ ili->write = ili9320_write_spi;
+ spi->dev = dev;
+
+ /* fill the two messages we are going to use to send the data
+ * with, the first the address followed by the data. The datasheet
+ * says they should be done as two distinct cycles of the SPI CS line.
+ */
+
+ spi->xfer[0].tx_buf = spi->buffer_addr;
+ spi->xfer[1].tx_buf = spi->buffer_data;
+ spi->xfer[0].len = 3;
+ spi->xfer[1].len = 3;
+ spi->xfer[0].bits_per_word = 8;
+ spi->xfer[1].bits_per_word = 8;
+ spi->xfer[0].cs_change = 1;
+
+ spi_message_init(&spi->message);
+ spi_message_add_tail(&spi->xfer[0], &spi->message);
+ spi_message_add_tail(&spi->xfer[1], &spi->message);
+}
+
+int __devinit ili9320_probe_spi(struct spi_device *spi,
+ struct ili9320_client *client)
+{
+ struct ili9320_platdata *cfg = spi->dev.platform_data;
+ struct device *dev = &spi->dev;
+ struct ili9320 *ili;
+ struct lcd_device *lcd;
+ int ret = 0;
+
+ /* verify we where given some information */
+
+ if (cfg == NULL) {
+ dev_err(dev, "no platform data supplied\n");
+ return -EINVAL;
+ }
+
+ if (cfg->hsize <= 0 || cfg->vsize <= 0 || cfg->reset == NULL) {
+ dev_err(dev, "invalid platform data supplied\n");
+ return -EINVAL;
+ }
+
+ /* allocate and initialse our state */
+
+ ili = kzalloc(sizeof(struct ili9320), GFP_KERNEL);
+ if (ili == NULL) {
+ dev_err(dev, "no memory for device\n");
+ return -ENOMEM;
+ }
+
+ ili->access.spi.id = ILI9320_SPI_IDCODE | ILI9320_SPI_ID(1);
+
+ ili->dev = dev;
+ ili->client = client;
+ ili->power = FB_BLANK_POWERDOWN;
+ ili->platdata = cfg;
+
+ dev_set_drvdata(&spi->dev, ili);
+
+ ili9320_setup_spi(ili, spi);
+
+ lcd = lcd_device_register("ili9320", dev, ili, &ili9320_ops);
+ if (IS_ERR(lcd)) {
+ dev_err(dev, "failed to register lcd device\n");
+ ret = PTR_ERR(lcd);
+ goto err_free;
+ }
+
+ ili->lcd = lcd;
+
+ dev_info(dev, "initialising %s\n", client->name);
+
+ ret = ili9320_power(ili, FB_BLANK_UNBLANK);
+ if (ret != 0) {
+ dev_err(dev, "failed to set lcd power state\n");
+ goto err_unregister;
+ }
+
+ return 0;
+
+ err_unregister:
+ lcd_device_unregister(lcd);
+
+ err_free:
+ kfree(ili);
+
+ return ret;
+}
+
+EXPORT_SYMBOL_GPL(ili9320_probe_spi);
+
+int __devexit ili9320_remove(struct ili9320 *ili)
+{
+ ili9320_power(ili, FB_BLANK_POWERDOWN);
+
+ lcd_device_unregister(ili->lcd);
+ kfree(ili);
+
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(ili9320_remove);
+
+#ifdef CONFIG_PM
+int ili9320_suspend(struct ili9320 *lcd, pm_message_t state)
+{
+ int ret;
+
+ dev_dbg(lcd->dev, "%s: event %d\n", __func__, state.event);
+
+ if (state.event == PM_EVENT_SUSPEND) {
+ ret = ili9320_power(lcd, FB_BLANK_POWERDOWN);
+
+ if (lcd->platdata->suspend == ILI9320_SUSPEND_DEEP) {
+ ili9320_write(lcd, ILI9320_POWER1, lcd->power1 |
+ ILI9320_POWER1_SLP |
+ ILI9320_POWER1_DSTB);
+ lcd->initialised = 0;
+ }
+
+ return ret;
+ }
+
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(ili9320_suspend);
+
+int ili9320_resume(struct ili9320 *lcd)
+{
+ dev_info(lcd->dev, "resuming from power state %d\n", lcd->power);
+
+ if (lcd->platdata->suspend == ILI9320_SUSPEND_DEEP) {
+ ili9320_write(lcd, ILI9320_POWER1, 0x00);
+ }
+
+ return ili9320_power(lcd, FB_BLANK_UNBLANK);
+}
+
+EXPORT_SYMBOL_GPL(ili9320_resume);
+#endif
+
+/* Power down all displays on reboot, poweroff or halt */
+void ili9320_shutdown(struct ili9320 *lcd)
+{
+ ili9320_power(lcd, FB_BLANK_POWERDOWN);
+}
+
+EXPORT_SYMBOL_GPL(ili9320_shutdown);
+
+MODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>");
+MODULE_DESCRIPTION("ILI9320 LCD Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/backlight/ili9320.h b/drivers/video/backlight/ili9320.h
new file mode 100644
index 00000000000..e388eca7cac
--- /dev/null
+++ b/drivers/video/backlight/ili9320.h
@@ -0,0 +1,80 @@
+/* drivers/video/backlight/ili9320.h
+ *
+ * ILI9320 LCD controller driver core.
+ *
+ * Copyright 2007 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * http://armlinux.simtec.co.uk/
+ *
+ * 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.
+*/
+
+/* Holder for register and value pairs. */
+struct ili9320_reg {
+ unsigned short address;
+ unsigned short value;
+};
+
+struct ili9320;
+
+struct ili9320_client {
+ const char *name;
+ int (*init)(struct ili9320 *ili, struct ili9320_platdata *cfg);
+
+};
+/* Device attached via an SPI bus. */
+struct ili9320_spi {
+ struct spi_device *dev;
+ struct spi_message message;
+ struct spi_transfer xfer[2];
+
+ unsigned char id;
+ unsigned char buffer_addr[4];
+ unsigned char buffer_data[4];
+};
+
+/* ILI9320 device state. */
+struct ili9320 {
+ union {
+ struct ili9320_spi spi; /* SPI attachged device. */
+ } access; /* Register access method. */
+
+ struct device *dev;
+ struct lcd_device *lcd; /* LCD device we created. */
+ struct ili9320_client *client;
+ struct ili9320_platdata *platdata;
+
+ int power; /* current power state. */
+ int initialised;
+
+ unsigned short display1;
+ unsigned short power1;
+
+ int (*write)(struct ili9320 *ili, unsigned int reg, unsigned int val);
+};
+
+
+/* ILI9320 register access routines */
+
+extern int ili9320_write(struct ili9320 *ili,
+ unsigned int reg, unsigned int value);
+
+extern int ili9320_write_regs(struct ili9320 *ili,
+ struct ili9320_reg *values,
+ int nr_values);
+
+/* Device probe */
+
+extern int ili9320_probe_spi(struct spi_device *spi,
+ struct ili9320_client *cli);
+
+extern int ili9320_remove(struct ili9320 *lcd);
+extern void ili9320_shutdown(struct ili9320 *lcd);
+
+/* PM */
+
+extern int ili9320_suspend(struct ili9320 *lcd, pm_message_t state);
+extern int ili9320_resume(struct ili9320 *lcd);
diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c
index 299fd318dd4..b15b2b84a6f 100644
--- a/drivers/video/backlight/lcd.c
+++ b/drivers/video/backlight/lcd.c
@@ -33,7 +33,7 @@ static int fb_notifier_callback(struct notifier_block *self,
ld = container_of(self, struct lcd_device, fb_notif);
mutex_lock(&ld->ops_lock);
if (ld->ops)
- if (!ld->ops->check_fb || ld->ops->check_fb(evdata->info))
+ if (!ld->ops->check_fb || ld->ops->check_fb(ld, evdata->info))
ld->ops->set_power(ld, *(int *)evdata->data);
mutex_unlock(&ld->ops_lock);
return 0;
diff --git a/drivers/video/backlight/mbp_nvidia_bl.c b/drivers/video/backlight/mbp_nvidia_bl.c
new file mode 100644
index 00000000000..385cba40ea8
--- /dev/null
+++ b/drivers/video/backlight/mbp_nvidia_bl.c
@@ -0,0 +1,116 @@
+/*
+ * Backlight Driver for Nvidia 8600 in Macbook Pro
+ *
+ * Copyright (c) Red Hat <mjg@redhat.com>
+ * Based on code from Pommed:
+ * Copyright (C) 2006 Nicolas Boichat <nicolas @boichat.ch>
+ * Copyright (C) 2006 Felipe Alfaro Solana <felipe_alfaro @linuxmail.org>
+ * Copyright (C) 2007 Julien BLACHE <jb@jblache.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
+ * published by the Free Software Foundation.
+ *
+ * This driver triggers SMIs which cause the firmware to change the
+ * backlight brightness. This is icky in many ways, but it's impractical to
+ * get at the firmware code in order to figure out what it's actually doing.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/backlight.h>
+#include <linux/err.h>
+#include <linux/dmi.h>
+#include <linux/io.h>
+
+static struct backlight_device *mbp_backlight_device;
+
+static struct dmi_system_id __initdata mbp_device_table[] = {
+ {
+ .ident = "3,1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,1"),
+ },
+ },
+ {
+ .ident = "3,2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro3,2"),
+ },
+ },
+ {
+ .ident = "4,1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro4,1"),
+ },
+ },
+ { }
+};
+
+static int mbp_send_intensity(struct backlight_device *bd)
+{
+ int intensity = bd->props.brightness;
+
+ outb(0x04 | (intensity << 4), 0xb3);
+ outb(0xbf, 0xb2);
+
+ return 0;
+}
+
+static int mbp_get_intensity(struct backlight_device *bd)
+{
+ outb(0x03, 0xb3);
+ outb(0xbf, 0xb2);
+ return inb(0xb3) >> 4;
+}
+
+static struct backlight_ops mbp_ops = {
+ .get_brightness = mbp_get_intensity,
+ .update_status = mbp_send_intensity,
+};
+
+static int __init mbp_init(void)
+{
+ if (!dmi_check_system(mbp_device_table))
+ return -ENODEV;
+
+ if (!request_region(0xb2, 2, "Macbook Pro backlight"))
+ return -ENXIO;
+
+ mbp_backlight_device = backlight_device_register("mbp_backlight",
+ NULL, NULL,
+ &mbp_ops);
+ if (IS_ERR(mbp_backlight_device)) {
+ release_region(0xb2, 2);
+ return PTR_ERR(mbp_backlight_device);
+ }
+
+ mbp_backlight_device->props.max_brightness = 15;
+ mbp_backlight_device->props.brightness =
+ mbp_get_intensity(mbp_backlight_device);
+ backlight_update_status(mbp_backlight_device);
+
+ return 0;
+}
+
+static void __exit mbp_exit(void)
+{
+ backlight_device_unregister(mbp_backlight_device);
+
+ release_region(0xb2, 2);
+}
+
+module_init(mbp_init);
+module_exit(mbp_exit);
+
+MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
+MODULE_DESCRIPTION("Nvidia-based Macbook Pro Backlight Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("svnAppleInc.:pnMacBookPro3,1");
+MODULE_ALIAS("svnAppleInc.:pnMacBookPro3,2");
+MODULE_ALIAS("svnAppleInc.:pnMacBookPro4,1");
diff --git a/drivers/video/backlight/platform_lcd.c b/drivers/video/backlight/platform_lcd.c
new file mode 100644
index 00000000000..72d44dbfce8
--- /dev/null
+++ b/drivers/video/backlight/platform_lcd.c
@@ -0,0 +1,172 @@
+/* drivers/video/backlight/platform_lcd.c
+ *
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * Generic platform-device LCD power control interface.
+ *
+ * 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/platform_device.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/lcd.h>
+
+#include <video/platform_lcd.h>
+
+struct platform_lcd {
+ struct device *us;
+ struct lcd_device *lcd;
+ struct plat_lcd_data *pdata;
+
+ unsigned int power;
+ unsigned int suspended : 1;
+};
+
+static inline struct platform_lcd *to_our_lcd(struct lcd_device *lcd)
+{
+ return lcd_get_data(lcd);
+}
+
+static int platform_lcd_get_power(struct lcd_device *lcd)
+{
+ struct platform_lcd *plcd = to_our_lcd(lcd);
+
+ return plcd->power;
+}
+
+static int platform_lcd_set_power(struct lcd_device *lcd, int power)
+{
+ struct platform_lcd *plcd = to_our_lcd(lcd);
+ int lcd_power = 1;
+
+ if (power == FB_BLANK_POWERDOWN || plcd->suspended)
+ lcd_power = 0;
+
+ plcd->pdata->set_power(plcd->pdata, lcd_power);
+ plcd->power = power;
+
+ return 0;
+}
+
+static int platform_lcd_match(struct lcd_device *lcd, struct fb_info *info)
+{
+ struct platform_lcd *plcd = to_our_lcd(lcd);
+ struct plat_lcd_data *pdata = plcd->pdata;
+
+ if (pdata->match_fb)
+ return pdata->match_fb(pdata, info);
+
+ return plcd->us->parent == info->device;
+}
+
+static struct lcd_ops platform_lcd_ops = {
+ .get_power = platform_lcd_get_power,
+ .set_power = platform_lcd_set_power,
+ .check_fb = platform_lcd_match,
+};
+
+static int __devinit platform_lcd_probe(struct platform_device *pdev)
+{
+ struct plat_lcd_data *pdata;
+ struct platform_lcd *plcd;
+ struct device *dev = &pdev->dev;
+ int err;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(dev, "no platform data supplied\n");
+ return -EINVAL;
+ }
+
+ plcd = kzalloc(sizeof(struct platform_lcd), GFP_KERNEL);
+ if (!plcd) {
+ dev_err(dev, "no memory for state\n");
+ return -ENOMEM;
+ }
+
+ plcd->us = dev;
+ plcd->pdata = pdata;
+ plcd->lcd = lcd_device_register("platform-lcd", dev,
+ plcd, &platform_lcd_ops);
+ if (IS_ERR(plcd->lcd)) {
+ dev_err(dev, "cannot register lcd device\n");
+ err = PTR_ERR(plcd->lcd);
+ goto err_mem;
+ }
+
+ platform_set_drvdata(pdev, plcd);
+ return 0;
+
+ err_mem:
+ kfree(plcd);
+ return err;
+}
+
+static int __devexit platform_lcd_remove(struct platform_device *pdev)
+{
+ struct platform_lcd *plcd = platform_get_drvdata(pdev);
+
+ lcd_device_unregister(plcd->lcd);
+ kfree(plcd);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int platform_lcd_suspend(struct platform_device *pdev, pm_message_t st)
+{
+ struct platform_lcd *plcd = platform_get_drvdata(pdev);
+
+ plcd->suspended = 1;
+ platform_lcd_set_power(plcd->lcd, plcd->power);
+
+ return 0;
+}
+
+static int platform_lcd_resume(struct platform_device *pdev)
+{
+ struct platform_lcd *plcd = platform_get_drvdata(pdev);
+
+ plcd->suspended = 0;
+ platform_lcd_set_power(plcd->lcd, plcd->power);
+
+ return 0;
+}
+#else
+#define platform_lcd_suspend NULL
+#define platform_lcd_resume NULL
+#endif
+
+static struct platform_driver platform_lcd_driver = {
+ .driver = {
+ .name = "platform-lcd",
+ .owner = THIS_MODULE,
+ },
+ .probe = platform_lcd_probe,
+ .remove = __devexit_p(platform_lcd_remove),
+ .suspend = platform_lcd_suspend,
+ .resume = platform_lcd_resume,
+};
+
+static int __init platform_lcd_init(void)
+{
+ return platform_driver_register(&platform_lcd_driver);
+}
+
+static void __exit platform_lcd_cleanup(void)
+{
+ platform_driver_unregister(&platform_lcd_driver);
+}
+
+module_init(platform_lcd_init);
+module_exit(platform_lcd_cleanup);
+
+MODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:platform-lcd");
diff --git a/drivers/video/backlight/vgg2432a4.c b/drivers/video/backlight/vgg2432a4.c
new file mode 100644
index 00000000000..593c7687d54
--- /dev/null
+++ b/drivers/video/backlight/vgg2432a4.c
@@ -0,0 +1,284 @@
+/* drivers/video/backlight/vgg2432a4.c
+ *
+ * VGG2432A4 (ILI9320) LCD controller driver.
+ *
+ * Copyright 2007 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * 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/err.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/lcd.h>
+#include <linux/module.h>
+
+#include <linux/spi/spi.h>
+
+#include <video/ili9320.h>
+
+#include "ili9320.h"
+
+/* Device initialisation sequences */
+
+static struct ili9320_reg vgg_init1[] = {
+ {
+ .address = ILI9320_POWER1,
+ .value = ILI9320_POWER1_AP(0) | ILI9320_POWER1_BT(0),
+ }, {
+ .address = ILI9320_POWER2,
+ .value = (ILI9320_POWER2_VC(7) |
+ ILI9320_POWER2_DC0(0) | ILI9320_POWER2_DC1(0)),
+ }, {
+ .address = ILI9320_POWER3,
+ .value = ILI9320_POWER3_VRH(0),
+ }, {
+ .address = ILI9320_POWER4,
+ .value = ILI9320_POWER4_VREOUT(0),
+ },
+};
+
+static struct ili9320_reg vgg_init2[] = {
+ {
+ .address = ILI9320_POWER1,
+ .value = (ILI9320_POWER1_AP(3) | ILI9320_POWER1_APE |
+ ILI9320_POWER1_BT(7) | ILI9320_POWER1_SAP),
+ }, {
+ .address = ILI9320_POWER2,
+ .value = ILI9320_POWER2_VC(7) | ILI9320_POWER2_DC0(3),
+ }
+};
+
+static struct ili9320_reg vgg_gamma[] = {
+ {
+ .address = ILI9320_GAMMA1,
+ .value = 0x0000,
+ }, {
+ .address = ILI9320_GAMMA2,
+ .value = 0x0505,
+ }, {
+ .address = ILI9320_GAMMA3,
+ .value = 0x0004,
+ }, {
+ .address = ILI9320_GAMMA4,
+ .value = 0x0006,
+ }, {
+ .address = ILI9320_GAMMA5,
+ .value = 0x0707,
+ }, {
+ .address = ILI9320_GAMMA6,
+ .value = 0x0105,
+ }, {
+ .address = ILI9320_GAMMA7,
+ .value = 0x0002,
+ }, {
+ .address = ILI9320_GAMMA8,
+ .value = 0x0707,
+ }, {
+ .address = ILI9320_GAMMA9,
+ .value = 0x0704,
+ }, {
+ .address = ILI9320_GAMMA10,
+ .value = 0x807,
+ }
+
+};
+
+static struct ili9320_reg vgg_init0[] = {
+ [0] = {
+ /* set direction and scan mode gate */
+ .address = ILI9320_DRIVER,
+ .value = ILI9320_DRIVER_SS,
+ }, {
+ .address = ILI9320_DRIVEWAVE,
+ .value = (ILI9320_DRIVEWAVE_MUSTSET |
+ ILI9320_DRIVEWAVE_EOR | ILI9320_DRIVEWAVE_BC),
+ }, {
+ .address = ILI9320_ENTRYMODE,
+ .value = ILI9320_ENTRYMODE_ID(3) | ILI9320_ENTRYMODE_BGR,
+ }, {
+ .address = ILI9320_RESIZING,
+ .value = 0x0,
+ },
+};
+
+
+static int vgg2432a4_lcd_init(struct ili9320 *lcd,
+ struct ili9320_platdata *cfg)
+{
+ unsigned int addr;
+ int ret;
+
+ /* Set VCore before anything else (VGG243237-6UFLWA) */
+ ret = ili9320_write(lcd, 0x00e5, 0x8000);
+ if (ret)
+ goto err_initial;
+
+ /* Start the oscillator up before we can do anything else. */
+ ret = ili9320_write(lcd, ILI9320_OSCILATION, ILI9320_OSCILATION_OSC);
+ if (ret)
+ goto err_initial;
+
+ /* must wait at-lesat 10ms after starting */
+ mdelay(15);
+
+ ret = ili9320_write_regs(lcd, vgg_init0, ARRAY_SIZE(vgg_init0));
+ if (ret != 0)
+ goto err_initial;
+
+ ili9320_write(lcd, ILI9320_DISPLAY2, cfg->display2);
+ ili9320_write(lcd, ILI9320_DISPLAY3, cfg->display3);
+ ili9320_write(lcd, ILI9320_DISPLAY4, cfg->display4);
+
+ ili9320_write(lcd, ILI9320_RGB_IF1, cfg->rgb_if1);
+ ili9320_write(lcd, ILI9320_FRAMEMAKER, 0x0);
+ ili9320_write(lcd, ILI9320_RGB_IF2, ILI9320_RGBIF2_DPL);
+
+ ret = ili9320_write_regs(lcd, vgg_init1, ARRAY_SIZE(vgg_init1));
+ if (ret != 0)
+ goto err_vgg;
+
+ mdelay(300);
+
+ ret = ili9320_write_regs(lcd, vgg_init2, ARRAY_SIZE(vgg_init2));
+ if (ret != 0)
+ goto err_vgg2;
+
+ mdelay(100);
+
+ ili9320_write(lcd, ILI9320_POWER3, 0x13c);
+
+ mdelay(100);
+
+ ili9320_write(lcd, ILI9320_POWER4, 0x1c00);
+ ili9320_write(lcd, ILI9320_POWER7, 0x000e);
+
+ mdelay(100);
+
+ ili9320_write(lcd, ILI9320_GRAM_HORIZ_ADDR, 0x00);
+ ili9320_write(lcd, ILI9320_GRAM_VERT_ADD, 0x00);
+
+ ret = ili9320_write_regs(lcd, vgg_gamma, ARRAY_SIZE(vgg_gamma));
+ if (ret != 0)
+ goto err_vgg3;
+
+ ili9320_write(lcd, ILI9320_HORIZ_START, 0x0);
+ ili9320_write(lcd, ILI9320_HORIZ_END, cfg->hsize - 1);
+ ili9320_write(lcd, ILI9320_VERT_START, 0x0);
+ ili9320_write(lcd, ILI9320_VERT_END, cfg->vsize - 1);
+
+ ili9320_write(lcd, ILI9320_DRIVER2,
+ ILI9320_DRIVER2_NL(((cfg->vsize - 240) / 8) + 0x1D));
+
+ ili9320_write(lcd, ILI9320_BASE_IMAGE, 0x1);
+ ili9320_write(lcd, ILI9320_VERT_SCROLL, 0x00);
+
+ for (addr = ILI9320_PARTIAL1_POSITION; addr <= ILI9320_PARTIAL2_END;
+ addr++) {
+ ili9320_write(lcd, addr, 0x0);
+ }
+
+ ili9320_write(lcd, ILI9320_INTERFACE1, 0x10);
+ ili9320_write(lcd, ILI9320_INTERFACE2, cfg->interface2);
+ ili9320_write(lcd, ILI9320_INTERFACE3, cfg->interface3);
+ ili9320_write(lcd, ILI9320_INTERFACE4, cfg->interface4);
+ ili9320_write(lcd, ILI9320_INTERFACE5, cfg->interface5);
+ ili9320_write(lcd, ILI9320_INTERFACE6, cfg->interface6);
+
+ lcd->display1 = (ILI9320_DISPLAY1_D(3) | ILI9320_DISPLAY1_DTE |
+ ILI9320_DISPLAY1_GON | ILI9320_DISPLAY1_BASEE |
+ 0x40);
+
+ ili9320_write(lcd, ILI9320_DISPLAY1, lcd->display1);
+
+ return 0;
+
+ err_vgg3:
+ err_vgg2:
+ err_vgg:
+ err_initial:
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int vgg2432a4_suspend(struct spi_device *spi, pm_message_t state)
+{
+ return ili9320_suspend(dev_get_drvdata(&spi->dev), state);
+}
+
+static int vgg2432a4_resume(struct spi_device *spi)
+{
+ return ili9320_resume(dev_get_drvdata(&spi->dev));
+}
+#else
+#define vgg2432a4_suspend NULL
+#define vgg2432a4_resume NULL
+#endif
+
+static struct ili9320_client vgg2432a4_client = {
+ .name = "VGG2432A4",
+ .init = vgg2432a4_lcd_init,
+};
+
+/* Device probe */
+
+static int __devinit vgg2432a4_probe(struct spi_device *spi)
+{
+ int ret;
+
+ ret = ili9320_probe_spi(spi, &vgg2432a4_client);
+ if (ret != 0) {
+ dev_err(&spi->dev, "failed to initialise ili9320\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devexit vgg2432a4_remove(struct spi_device *spi)
+{
+ return ili9320_remove(dev_get_drvdata(&spi->dev));
+}
+
+static void vgg2432a4_shutdown(struct spi_device *spi)
+{
+ ili9320_shutdown(dev_get_drvdata(&spi->dev));
+}
+
+static struct spi_driver vgg2432a4_driver = {
+ .driver = {
+ .name = "VGG2432A4",
+ .owner = THIS_MODULE,
+ },
+ .probe = vgg2432a4_probe,
+ .remove = __devexit_p(vgg2432a4_remove),
+ .shutdown = vgg2432a4_shutdown,
+ .suspend = vgg2432a4_suspend,
+ .resume = vgg2432a4_resume,
+};
+
+/* Device driver initialisation */
+
+static int __init vgg2432a4_init(void)
+{
+ return spi_register_driver(&vgg2432a4_driver);
+}
+
+static void __exit vgg2432a4_exit(void)
+{
+ spi_unregister_driver(&vgg2432a4_driver);
+}
+
+module_init(vgg2432a4_init);
+module_exit(vgg2432a4_exit);
+
+MODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>");
+MODULE_DESCRIPTION("VGG2432A4 LCD Driver");
+MODULE_LICENSE("GPL v2");
+
+
diff --git a/drivers/video/bf54x-lq043fb.c b/drivers/video/bf54x-lq043fb.c
index 49834a67a62..940467aed13 100644
--- a/drivers/video/bf54x-lq043fb.c
+++ b/drivers/video/bf54x-lq043fb.c
@@ -478,7 +478,7 @@ static int bfin_lcd_set_contrast(struct lcd_device *dev, int contrast)
return 0;
}
-static int bfin_lcd_check_fb(struct fb_info *fi)
+static int bfin_lcd_check_fb(struct lcd_device *dev, struct fb_info *fi)
{
if (!fi || (fi == &bfin_bf54x_fb))
return 1;
diff --git a/drivers/video/bfin-t350mcqb-fb.c b/drivers/video/bfin-t350mcqb-fb.c
index 135d6dd7e67..7d1b819e501 100644
--- a/drivers/video/bfin-t350mcqb-fb.c
+++ b/drivers/video/bfin-t350mcqb-fb.c
@@ -396,7 +396,7 @@ static int bfin_lcd_set_contrast(struct lcd_device *dev, int contrast)
return 0;
}
-static int bfin_lcd_check_fb(struct fb_info *fi)
+static int bfin_lcd_check_fb(struct lcd_device *dev, struct fb_info *fi)
{
if (!fi || (fi == &bfin_t350mcqb_fb))
return 1;
diff --git a/drivers/video/carminefb.c b/drivers/video/carminefb.c
new file mode 100644
index 00000000000..e15bb447440
--- /dev/null
+++ b/drivers/video/carminefb.c
@@ -0,0 +1,790 @@
+/*
+ * Frame buffer driver for the Carmine GPU.
+ *
+ * The driver configures the GPU as follows
+ * - FB0 is display 0 with unique memory area
+ * - FB1 is display 1 with unique memory area
+ * - both display use 32 bit colors
+ */
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+
+#include "carminefb.h"
+#include "carminefb_regs.h"
+
+#if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN)
+#error "The endianness of the target host has not been defined."
+#endif
+
+/*
+ * The initial video mode can be supplied via two different ways:
+ * - as a string that is passed to fb_find_mode() (module option fb_mode_str)
+ * - as an integer that picks the video mode from carmine_modedb[] (module
+ * option fb_mode)
+ *
+ * If nothing is used than the initial video mode will be the
+ * CARMINEFB_DEFAULT_VIDEO_MODE member of the carmine_modedb[].
+ */
+#define CARMINEFB_DEFAULT_VIDEO_MODE 1
+
+static unsigned int fb_mode = CARMINEFB_DEFAULT_VIDEO_MODE;
+module_param(fb_mode, uint, 444);
+MODULE_PARM_DESC(fb_mode, "Initial video mode as integer.");
+
+static char *fb_mode_str;
+module_param(fb_mode_str, charp, 444);
+MODULE_PARM_DESC(fb_mode_str, "Initial video mode in characters.");
+
+/*
+ * Carminefb displays:
+ * 0b000 None
+ * 0b001 Display 0
+ * 0b010 Display 1
+ */
+static int fb_displays = CARMINE_USE_DISPLAY0 | CARMINE_USE_DISPLAY1;
+module_param(fb_displays, int, 444);
+MODULE_PARM_DESC(fb_displays, "Bit mode, which displays are used");
+
+struct carmine_hw {
+ void __iomem *v_regs;
+ void __iomem *screen_mem;
+ struct fb_info *fb[MAX_DISPLAY];
+};
+
+struct carmine_resolution {
+ u32 htp;
+ u32 hsp;
+ u32 hsw;
+ u32 hdp;
+ u32 vtr;
+ u32 vsp;
+ u32 vsw;
+ u32 vdp;
+ u32 disp_mode;
+};
+
+struct carmine_fb {
+ void __iomem *display_reg;
+ void __iomem *screen_base;
+ u32 smem_offset;
+ u32 cur_mode;
+ u32 new_mode;
+ struct carmine_resolution *res;
+ u32 pseudo_palette[16];
+};
+
+static struct fb_fix_screeninfo carminefb_fix __devinitdata = {
+ .id = "Carmine",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_TRUECOLOR,
+ .accel = FB_ACCEL_NONE,
+};
+
+static const struct fb_videomode carmine_modedb[] = {
+ {
+ .name = "640x480",
+ .xres = 640,
+ .yres = 480,
+ }, {
+ .name = "800x600",
+ .xres = 800,
+ .yres = 600,
+ },
+};
+
+static struct carmine_resolution car_modes[] = {
+ {
+ /* 640x480 */
+ .htp = 800,
+ .hsp = 672,
+ .hsw = 96,
+ .hdp = 640,
+ .vtr = 525,
+ .vsp = 490,
+ .vsw = 2,
+ .vdp = 480,
+ .disp_mode = 0x1400,
+ },
+ {
+ /* 800x600 */
+ .htp = 1060,
+ .hsp = 864,
+ .hsw = 72,
+ .hdp = 800,
+ .vtr = 628,
+ .vsp = 601,
+ .vsw = 2,
+ .vdp = 600,
+ .disp_mode = 0x0d00,
+ }
+};
+
+static int carmine_find_mode(const struct fb_var_screeninfo *var)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(car_modes); i++)
+ if (car_modes[i].hdp == var->xres &&
+ car_modes[i].vdp == var->yres)
+ return i;
+ return -EINVAL;
+}
+
+static void c_set_disp_reg(const struct carmine_fb *par,
+ u32 offset, u32 val)
+{
+ writel(val, par->display_reg + offset);
+}
+
+static u32 c_get_disp_reg(const struct carmine_fb *par,
+ u32 offset)
+{
+ return readl(par->display_reg + offset);
+}
+
+static void c_set_hw_reg(const struct carmine_hw *hw,
+ u32 offset, u32 val)
+{
+ writel(val, hw->v_regs + offset);
+}
+
+static u32 c_get_hw_reg(const struct carmine_hw *hw,
+ u32 offset)
+{
+ return readl(hw->v_regs + offset);
+}
+
+static int carmine_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp, struct fb_info *info)
+{
+ if (regno >= 16)
+ return 1;
+
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+ transp >>= 8;
+
+ ((u32 *)info->pseudo_palette)[regno] = be32_to_cpu(transp << 24 |
+ red << 0 | green << 8 | blue << 16);
+ return 0;
+}
+
+static int carmine_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ int ret;
+
+ ret = carmine_find_mode(var);
+ if (ret < 0)
+ return ret;
+
+ if (var->grayscale || var->rotate || var->nonstd)
+ return -EINVAL;
+
+ var->xres_virtual = var->xres;
+ var->yres_virtual = var->yres;
+
+ var->bits_per_pixel = 32;
+
+#ifdef __BIG_ENDIAN
+ var->transp.offset = 24;
+ var->red.offset = 0;
+ var->green.offset = 8;
+ var->blue.offset = 16;
+#else
+ var->transp.offset = 24;
+ var->red.offset = 16;
+ var->green.offset = 8;
+ var->blue.offset = 0;
+#endif
+
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ var->transp.length = 8;
+
+ var->red.msb_right = 0;
+ var->green.msb_right = 0;
+ var->blue.msb_right = 0;
+ var->transp.msb_right = 0;
+ return 0;
+}
+
+static void carmine_init_display_param(struct carmine_fb *par)
+{
+ u32 width;
+ u32 height;
+ u32 param;
+ u32 window_size;
+ u32 soffset = par->smem_offset;
+
+ c_set_disp_reg(par, CARMINE_DISP_REG_C_TRANS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_MLMR_TRANS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_CURSOR_MODE,
+ CARMINE_CURSOR0_PRIORITY_MASK |
+ CARMINE_CURSOR1_PRIORITY_MASK |
+ CARMINE_CURSOR_CUTZ_MASK);
+
+ /* Set default cursor position */
+ c_set_disp_reg(par, CARMINE_DISP_REG_CUR1_POS, 0 << 16 | 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_CUR2_POS, 0 << 16 | 0);
+
+ /* Set default display mode */
+ c_set_disp_reg(par, CARMINE_DISP_REG_L0_EXT_MODE, CARMINE_WINDOW_MODE |
+ CARMINE_EXT_CMODE_DIRECT24_RGBA);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L1_EXT_MODE,
+ CARMINE_EXT_CMODE_DIRECT24_RGBA);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L2_EXT_MODE, CARMINE_EXTEND_MODE |
+ CARMINE_EXT_CMODE_DIRECT24_RGBA);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L3_EXT_MODE, CARMINE_EXTEND_MODE |
+ CARMINE_EXT_CMODE_DIRECT24_RGBA);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L4_EXT_MODE, CARMINE_EXTEND_MODE |
+ CARMINE_EXT_CMODE_DIRECT24_RGBA);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L5_EXT_MODE, CARMINE_EXTEND_MODE |
+ CARMINE_EXT_CMODE_DIRECT24_RGBA);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L6_EXT_MODE, CARMINE_EXTEND_MODE |
+ CARMINE_EXT_CMODE_DIRECT24_RGBA);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L7_EXT_MODE, CARMINE_EXTEND_MODE |
+ CARMINE_EXT_CMODE_DIRECT24_RGBA);
+
+ /* Set default frame size to layer mode register */
+ width = par->res->hdp * 4 / CARMINE_DISP_WIDTH_UNIT;
+ width = width << CARMINE_DISP_WIDTH_SHIFT;
+
+ height = par->res->vdp - 1;
+ param = width | height;
+
+ c_set_disp_reg(par, CARMINE_DISP_REG_L0_MODE_W_H, param);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIDTH, width);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L2_MODE_W_H, param);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L3_MODE_W_H, param);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L4_MODE_W_H, param);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L5_MODE_W_H, param);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L6_MODE_W_H, param);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L7_MODE_W_H, param);
+
+ /* Set default pos and size */
+ window_size = (par->res->vdp - 1) << CARMINE_DISP_WIN_H_SHIFT;
+ window_size |= par->res->hdp;
+
+ c_set_disp_reg(par, CARMINE_DISP_REG_L0_WIN_POS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L0_WIN_SIZE, window_size);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIN_POS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIN_SIZE, window_size);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L2_WIN_POS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L2_WIN_SIZE, window_size);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L3_WIN_POS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L3_WIN_SIZE, window_size);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L4_WIN_POS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L4_WIN_SIZE, window_size);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L5_WIN_POS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L5_WIN_SIZE, window_size);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L6_WIN_POS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L6_WIN_SIZE, window_size);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L7_WIN_POS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L7_WIN_SIZE, window_size);
+
+ /* Set default origin address */
+ c_set_disp_reg(par, CARMINE_DISP_REG_L0_ORG_ADR, soffset);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L1_ORG_ADR, soffset);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L2_ORG_ADR1, soffset);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L3_ORG_ADR1, soffset);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L4_ORG_ADR1, soffset);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L5_ORG_ADR1, soffset);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L6_ORG_ADR1, soffset);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L7_ORG_ADR1, soffset);
+
+ /* Set default display address */
+ c_set_disp_reg(par, CARMINE_DISP_REG_L0_DISP_ADR, soffset);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L2_DISP_ADR1, soffset);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L3_DISP_ADR1, soffset);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L4_DISP_ADR1, soffset);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L5_DISP_ADR1, soffset);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L6_DISP_ADR0, soffset);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L7_DISP_ADR0, soffset);
+
+ /* Set default display position */
+ c_set_disp_reg(par, CARMINE_DISP_REG_L0_DISP_POS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L2_DISP_POS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L3_DISP_POS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L4_DISP_POS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L5_DISP_POS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L6_DISP_POS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L7_DISP_POS, 0);
+
+ /* Set default blend mode */
+ c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L0, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L1, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L2, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L3, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L4, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L5, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L6, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L7, 0);
+
+ /* default transparency mode */
+ c_set_disp_reg(par, CARMINE_DISP_REG_L0_TRANS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L1_TRANS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L2_TRANS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L3_TRANS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L4_TRANS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L5_TRANS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L6_TRANS, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L7_TRANS, 0);
+
+ /* Set default read skip parameter */
+ c_set_disp_reg(par, CARMINE_DISP_REG_L0RM, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L2RM, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L3RM, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L4RM, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L5RM, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L6RM, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L7RM, 0);
+
+ c_set_disp_reg(par, CARMINE_DISP_REG_L0PX, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L2PX, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L3PX, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L4PX, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L5PX, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L6PX, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L7PX, 0);
+
+ c_set_disp_reg(par, CARMINE_DISP_REG_L0PY, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L2PY, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L3PY, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L4PY, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L5PY, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L6PY, 0);
+ c_set_disp_reg(par, CARMINE_DISP_REG_L7PY, 0);
+}
+
+static void set_display_parameters(struct carmine_fb *par)
+{
+ u32 mode;
+ u32 hdp, vdp, htp, hsp, hsw, vtr, vsp, vsw;
+
+ /*
+ * display timing. Parameters are decreased by one because hardware
+ * spec is 0 to (n - 1)
+ * */
+ hdp = par->res->hdp - 1;
+ vdp = par->res->vdp - 1;
+ htp = par->res->htp - 1;
+ hsp = par->res->hsp - 1;
+ hsw = par->res->hsw - 1;
+ vtr = par->res->vtr - 1;
+ vsp = par->res->vsp - 1;
+ vsw = par->res->vsw - 1;
+
+ c_set_disp_reg(par, CARMINE_DISP_REG_H_TOTAL,
+ htp << CARMINE_DISP_HTP_SHIFT);
+ c_set_disp_reg(par, CARMINE_DISP_REG_H_PERIOD,
+ (hdp << CARMINE_DISP_HDB_SHIFT) | hdp);
+ c_set_disp_reg(par, CARMINE_DISP_REG_V_H_W_H_POS,
+ (vsw << CARMINE_DISP_VSW_SHIFT) |
+ (hsw << CARMINE_DISP_HSW_SHIFT) |
+ (hsp));
+ c_set_disp_reg(par, CARMINE_DISP_REG_V_TOTAL,
+ vtr << CARMINE_DISP_VTR_SHIFT);
+ c_set_disp_reg(par, CARMINE_DISP_REG_V_PERIOD_POS,
+ (vdp << CARMINE_DISP_VDP_SHIFT) | vsp);
+
+ /* clock */
+ mode = c_get_disp_reg(par, CARMINE_DISP_REG_DCM1);
+ mode = (mode & ~CARMINE_DISP_DCM_MASK) |
+ (par->res->disp_mode & CARMINE_DISP_DCM_MASK);
+ /* enable video output and layer 0 */
+ mode |= CARMINE_DEN | CARMINE_L0E;
+ c_set_disp_reg(par, CARMINE_DISP_REG_DCM1, mode);
+}
+
+static int carmine_set_par(struct fb_info *info)
+{
+ struct carmine_fb *par = info->par;
+ int ret;
+
+ ret = carmine_find_mode(&info->var);
+ if (ret < 0)
+ return ret;
+
+ par->new_mode = ret;
+ if (par->cur_mode != par->new_mode) {
+
+ par->cur_mode = par->new_mode;
+ par->res = &car_modes[par->new_mode];
+
+ carmine_init_display_param(par);
+ set_display_parameters(par);
+ }
+
+ info->fix.line_length = info->var.xres * info->var.bits_per_pixel / 8;
+ return 0;
+}
+
+static int init_hardware(struct carmine_hw *hw)
+{
+ u32 flags;
+ u32 loops;
+ u32 ret;
+
+ /* Initalize Carmine */
+ /* Sets internal clock */
+ c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE,
+ CARMINE_DFLT_IP_CLOCK_ENABLE);
+
+ /* Video signal output is turned off */
+ c_set_hw_reg(hw, CARMINE_DISP0_REG + CARMINE_DISP_REG_DCM1, 0);
+ c_set_hw_reg(hw, CARMINE_DISP1_REG + CARMINE_DISP_REG_DCM1, 0);
+
+ /* Software reset */
+ c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_SOFTWARE_RESET, 1);
+ c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_SOFTWARE_RESET, 0);
+
+ /* I/O mode settings */
+ flags = CARMINE_DFLT_IP_DCTL_IO_CONT1 << 16 |
+ CARMINE_DFLT_IP_DCTL_IO_CONT0;
+ c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_IOCONT1_IOCONT0,
+ flags);
+
+ /* DRAM initial sequence */
+ flags = CARMINE_DFLT_IP_DCTL_MODE << 16 | CARMINE_DFLT_IP_DCTL_ADD;
+ c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_MODE_ADD,
+ flags);
+
+ flags = CARMINE_DFLT_IP_DCTL_SET_TIME1 << 16 |
+ CARMINE_DFLT_IP_DCTL_EMODE;
+ c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_SETTIME1_EMODE,
+ flags);
+
+ flags = CARMINE_DFLT_IP_DCTL_REFRESH << 16 |
+ CARMINE_DFLT_IP_DCTL_SET_TIME2;
+ c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_REFRESH_SETTIME2,
+ flags);
+
+ flags = CARMINE_DFLT_IP_DCTL_RESERVE2 << 16 |
+ CARMINE_DFLT_IP_DCTL_FIFO_DEPTH;
+ c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV2_RSV1, flags);
+
+ flags = CARMINE_DFLT_IP_DCTL_DDRIF2 << 16 | CARMINE_DFLT_IP_DCTL_DDRIF1;
+ c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_DDRIF2_DDRIF1,
+ flags);
+
+ flags = CARMINE_DFLT_IP_DCTL_RESERVE0 << 16 |
+ CARMINE_DFLT_IP_DCTL_STATES;
+ c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV0_STATES,
+ flags);
+
+ /* Executes DLL reset */
+ if (CARMINE_DCTL_DLL_RESET) {
+ for (loops = 0; loops < CARMINE_DCTL_INIT_WAIT_LIMIT; loops++) {
+
+ ret = c_get_hw_reg(hw, CARMINE_DCTL_REG +
+ CARMINE_DCTL_REG_RSV0_STATES);
+ ret &= CARMINE_DCTL_REG_STATES_MASK;
+ if (!ret)
+ break;
+
+ mdelay(CARMINE_DCTL_INIT_WAIT_INTERVAL);
+ }
+
+ if (loops >= CARMINE_DCTL_INIT_WAIT_LIMIT) {
+ printk(KERN_ERR "DRAM init failed\n");
+ return -EIO;
+ }
+ }
+
+ flags = CARMINE_DFLT_IP_DCTL_MODE_AFT_RST << 16 |
+ CARMINE_DFLT_IP_DCTL_ADD;
+ c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_MODE_ADD, flags);
+
+ flags = CARMINE_DFLT_IP_DCTL_RESERVE0 << 16 |
+ CARMINE_DFLT_IP_DCTL_STATES_AFT_RST;
+ c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV0_STATES,
+ flags);
+
+ /* Initialize the write back register */
+ c_set_hw_reg(hw, CARMINE_WB_REG + CARMINE_WB_REG_WBM,
+ CARMINE_WB_REG_WBM_DEFAULT);
+
+ /* Initialize the Kottos registers */
+ c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_VRINTM, 0);
+ c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_VRERRM, 0);
+
+ /* Set DC offsets */
+ c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_PX, 0);
+ c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_PY, 0);
+ c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_LX, 0);
+ c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_LY, 0);
+ c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_TX, 0);
+ c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_TY, 0);
+ return 0;
+}
+
+static struct fb_ops carminefb_ops = {
+ .owner = THIS_MODULE,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+
+ .fb_check_var = carmine_check_var,
+ .fb_set_par = carmine_set_par,
+ .fb_setcolreg = carmine_setcolreg,
+};
+
+static int alloc_carmine_fb(void __iomem *regs, void __iomem *smem_base,
+ int smem_offset, struct device *device, struct fb_info **rinfo)
+{
+ int ret;
+ struct fb_info *info;
+ struct carmine_fb *par;
+
+ info = framebuffer_alloc(sizeof *par, device);
+ if (!info)
+ return -ENOMEM;
+
+ par = info->par;
+ par->display_reg = regs;
+ par->smem_offset = smem_offset;
+
+ info->screen_base = smem_base + smem_offset;
+ info->screen_size = CARMINE_DISPLAY_MEM;
+ info->fbops = &carminefb_ops;
+
+ info->fix = carminefb_fix;
+ info->pseudo_palette = par->pseudo_palette;
+ info->flags = FBINFO_DEFAULT;
+
+ ret = fb_alloc_cmap(&info->cmap, 256, 1);
+ if (ret < 0)
+ goto err_free_fb;
+
+ if (fb_mode > ARRAY_SIZE(carmine_modedb))
+ fb_mode = CARMINEFB_DEFAULT_VIDEO_MODE;
+
+ par->cur_mode = par->new_mode = ~0;
+
+ ret = fb_find_mode(&info->var, info, fb_mode_str, carmine_modedb,
+ ARRAY_SIZE(carmine_modedb),
+ &carmine_modedb[fb_mode], 32);
+ if (!ret || ret == 4) {
+ ret = -EINVAL;
+ goto err_dealloc_cmap;
+ }
+
+ fb_videomode_to_modelist(carmine_modedb, ARRAY_SIZE(carmine_modedb),
+ &info->modelist);
+
+ ret = register_framebuffer(info);
+ if (ret < 0)
+ goto err_dealloc_cmap;
+
+ printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
+ info->fix.id);
+
+ *rinfo = info;
+ return 0;
+
+err_dealloc_cmap:
+ fb_dealloc_cmap(&info->cmap);
+err_free_fb:
+ framebuffer_release(info);
+ return ret;
+}
+
+static void cleanup_fb_device(struct fb_info *info)
+{
+ if (info) {
+ unregister_framebuffer(info);
+ fb_dealloc_cmap(&info->cmap);
+ framebuffer_release(info);
+ }
+}
+
+static int __devinit carminefb_probe(struct pci_dev *dev,
+ const struct pci_device_id *ent)
+{
+ struct carmine_hw *hw;
+ struct device *device = &dev->dev;
+ struct fb_info *info;
+ int ret;
+
+ ret = pci_enable_device(dev);
+ if (ret)
+ return ret;
+
+ ret = -ENOMEM;
+ hw = kzalloc(sizeof *hw, GFP_KERNEL);
+ if (!hw)
+ goto err_enable_pci;
+
+ carminefb_fix.mmio_start = pci_resource_start(dev, CARMINE_CONFIG_BAR);
+ carminefb_fix.mmio_len = pci_resource_len(dev, CARMINE_CONFIG_BAR);
+
+ if (!request_mem_region(carminefb_fix.mmio_start,
+ carminefb_fix.mmio_len,
+ "carminefb regbase")) {
+ printk(KERN_ERR "carminefb: Can't reserve regbase.\n");
+ ret = -EBUSY;
+ goto err_free_hw;
+ }
+ hw->v_regs = ioremap_nocache(carminefb_fix.mmio_start,
+ carminefb_fix.mmio_len);
+ if (!hw->v_regs) {
+ printk(KERN_ERR "carminefb: Can't remap %s register.\n",
+ carminefb_fix.id);
+ goto err_free_reg_mmio;
+ }
+
+ carminefb_fix.smem_start = pci_resource_start(dev, CARMINE_MEMORY_BAR);
+ carminefb_fix.smem_len = pci_resource_len(dev, CARMINE_MEMORY_BAR);
+
+ /* The memory area tends to be very large (256 MiB). Remap only what
+ * is required for that largest resolution to avoid remaps at run
+ * time
+ */
+ if (carminefb_fix.smem_len > CARMINE_TOTAL_DIPLAY_MEM)
+ carminefb_fix.smem_len = CARMINE_TOTAL_DIPLAY_MEM;
+
+ else if (carminefb_fix.smem_len < CARMINE_TOTAL_DIPLAY_MEM) {
+ printk(KERN_ERR "carminefb: Memory bar is only %d bytes, %d "
+ "are required.", carminefb_fix.smem_len,
+ CARMINE_TOTAL_DIPLAY_MEM);
+ goto err_free_reg_mmio;
+ }
+
+ if (!request_mem_region(carminefb_fix.smem_start,
+ carminefb_fix.smem_len, "carminefb smem")) {
+ printk(KERN_ERR "carminefb: Can't reserve smem.\n");
+ goto err_unmap_vregs;
+ }
+
+ hw->screen_mem = ioremap_nocache(carminefb_fix.smem_start,
+ carminefb_fix.smem_len);
+ if (!hw->screen_mem) {
+ printk(KERN_ERR "carmine: Can't ioremap smem area.\n");
+ release_mem_region(carminefb_fix.smem_start,
+ carminefb_fix.smem_len);
+ goto err_reg_smem;
+ }
+
+ ret = init_hardware(hw);
+ if (ret)
+ goto err_unmap_screen;
+
+ info = NULL;
+ if (fb_displays & CARMINE_USE_DISPLAY0) {
+ ret = alloc_carmine_fb(hw->v_regs + CARMINE_DISP0_REG,
+ hw->screen_mem, CARMINE_DISPLAY_MEM * 0,
+ device, &info);
+ if (ret)
+ goto err_deinit_hw;
+ }
+
+ hw->fb[0] = info;
+
+ info = NULL;
+ if (fb_displays & CARMINE_USE_DISPLAY1) {
+ ret = alloc_carmine_fb(hw->v_regs + CARMINE_DISP1_REG,
+ hw->screen_mem, CARMINE_DISPLAY_MEM * 1,
+ device, &info);
+ if (ret)
+ goto err_cleanup_fb0;
+ }
+
+ hw->fb[1] = info;
+ info = NULL;
+
+ pci_set_drvdata(dev, hw);
+ return 0;
+
+err_cleanup_fb0:
+ cleanup_fb_device(hw->fb[0]);
+err_deinit_hw:
+ /* disable clock, etc */
+ c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, 0);
+err_unmap_screen:
+ iounmap(hw->screen_mem);
+err_reg_smem:
+ release_mem_region(carminefb_fix.mmio_start, carminefb_fix.mmio_len);
+err_unmap_vregs:
+ iounmap(hw->v_regs);
+err_free_reg_mmio:
+ release_mem_region(carminefb_fix.mmio_start, carminefb_fix.mmio_len);
+err_free_hw:
+ kfree(hw);
+err_enable_pci:
+ pci_disable_device(dev);
+ return ret;
+}
+
+static void __devexit carminefb_remove(struct pci_dev *dev)
+{
+ struct carmine_hw *hw = pci_get_drvdata(dev);
+ struct fb_fix_screeninfo fix;
+ int i;
+
+ /* in case we use only fb1 and not fb1 */
+ if (hw->fb[0])
+ fix = hw->fb[0]->fix;
+ else
+ fix = hw->fb[1]->fix;
+
+ /* deactivate display(s) and switch clocks */
+ c_set_hw_reg(hw, CARMINE_DISP0_REG + CARMINE_DISP_REG_DCM1, 0);
+ c_set_hw_reg(hw, CARMINE_DISP1_REG + CARMINE_DISP_REG_DCM1, 0);
+ c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, 0);
+
+ for (i = 0; i < MAX_DISPLAY; i++)
+ cleanup_fb_device(hw->fb[i]);
+
+ iounmap(hw->screen_mem);
+ release_mem_region(fix.smem_start, fix.smem_len);
+ iounmap(hw->v_regs);
+ release_mem_region(fix.mmio_start, fix.mmio_len);
+
+ pci_set_drvdata(dev, NULL);
+ pci_disable_device(dev);
+ kfree(hw);
+}
+
+#define PCI_VENDOR_ID_FUJITU_LIMITED 0x10cf
+static struct pci_device_id carmine_devices[] __devinitdata = {
+{
+ PCI_DEVICE(PCI_VENDOR_ID_FUJITU_LIMITED, 0x202b)},
+ {0, 0, 0, 0, 0, 0, 0}
+};
+
+MODULE_DEVICE_TABLE(pci, carmine_devices);
+
+static struct pci_driver carmine_pci_driver = {
+ .name = "carminefb",
+ .id_table = carmine_devices,
+ .probe = carminefb_probe,
+ .remove = __devexit_p(carminefb_remove),
+};
+
+static int __init carminefb_init(void)
+{
+ if (!(fb_displays &
+ (CARMINE_USE_DISPLAY0 | CARMINE_USE_DISPLAY1))) {
+ printk(KERN_ERR "If you disable both displays than you don't "
+ "need the driver at all\n");
+ return -EINVAL;
+ }
+ return pci_register_driver(&carmine_pci_driver);
+}
+module_init(carminefb_init);
+
+static void __exit carminefb_cleanup(void)
+{
+ pci_unregister_driver(&carmine_pci_driver);
+}
+module_exit(carminefb_cleanup);
+
+MODULE_AUTHOR("Sebastian Siewior <bigeasy@linutronix.de>");
+MODULE_DESCRIPTION("Framebuffer driver for Fujitsu Carmine based devices");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/carminefb.h b/drivers/video/carminefb.h
new file mode 100644
index 00000000000..05306de0c6b
--- /dev/null
+++ b/drivers/video/carminefb.h
@@ -0,0 +1,64 @@
+#ifndef CARMINE_CARMINE_H
+#define CARMINE_CARMINE_H
+
+#define CARMINE_MEMORY_BAR 2
+#define CARMINE_CONFIG_BAR 3
+
+#define MAX_DISPLAY 2
+#define CARMINE_DISPLAY_MEM (800 * 600 * 4)
+#define CARMINE_TOTAL_DIPLAY_MEM (CARMINE_DISPLAY_MEM * MAX_DISPLAY)
+
+#define CARMINE_USE_DISPLAY0 (1 << 0)
+#define CARMINE_USE_DISPLAY1 (1 << 1)
+
+/*
+ * This values work on the eval card. Custom boards may use different timings,
+ * here an example :)
+ */
+
+/* DRAM initialization values */
+#ifdef CONFIG_FB_CARMINE_DRAM_EVAL
+
+#define CARMINE_DFLT_IP_CLOCK_ENABLE (0x03ff)
+#define CARMINE_DFLT_IP_DCTL_ADD (0x05c3)
+#define CARMINE_DFLT_IP_DCTL_MODE (0x0121)
+#define CARMINE_DFLT_IP_DCTL_EMODE (0x8000)
+#define CARMINE_DFLT_IP_DCTL_SET_TIME1 (0x4749)
+#define CARMINE_DFLT_IP_DCTL_SET_TIME2 (0x2a22)
+#define CARMINE_DFLT_IP_DCTL_REFRESH (0x0042)
+#define CARMINE_DFLT_IP_DCTL_STATES (0x0003)
+#define CARMINE_DFLT_IP_DCTL_RESERVE0 (0x0020)
+#define CARMINE_DFLT_IP_DCTL_FIFO_DEPTH (0x000f)
+#define CARMINE_DFLT_IP_DCTL_RESERVE2 (0x0000)
+#define CARMINE_DFLT_IP_DCTL_DDRIF1 (0x6646)
+#define CARMINE_DFLT_IP_DCTL_DDRIF2 (0x0055)
+#define CARMINE_DFLT_IP_DCTL_MODE_AFT_RST (0x0021)
+#define CARMINE_DFLT_IP_DCTL_STATES_AFT_RST (0x0002)
+#define CARMINE_DFLT_IP_DCTL_IO_CONT0 (0x0555)
+#define CARMINE_DFLT_IP_DCTL_IO_CONT1 (0x0555)
+#define CARMINE_DCTL_DLL_RESET (1)
+#endif
+
+#ifdef CONFIG_CARMINE_DRAM_CUSTOM
+
+#define CARMINE_DFLT_IP_CLOCK_ENABLE (0x03ff)
+#define CARMINE_DFLT_IP_DCTL_ADD (0x03b2)
+#define CARMINE_DFLT_IP_DCTL_MODE (0x0161)
+#define CARMINE_DFLT_IP_DCTL_EMODE (0x8000)
+#define CARMINE_DFLT_IP_DCTL_SET_TIME1 (0x2628)
+#define CARMINE_DFLT_IP_DCTL_SET_TIME2 (0x1a09)
+#define CARMINE_DFLT_IP_DCTL_REFRESH (0x00fe)
+#define CARMINE_DFLT_IP_DCTL_STATES (0x0003)
+#define CARMINE_DFLT_IP_DCTL_RESERVE0 (0x0020)
+#define CARMINE_DFLT_IP_DCTL_FIFO_DEPTH (0x000f)
+#define CARMINE_DFLT_IP_DCTL_RESERVE2 (0x0000)
+#define CARMINE_DFLT_IP_DCTL_DDRIF1 (0x0646)
+#define CARMINE_DFLT_IP_DCTL_DDRIF2 (0x55aa)
+#define CARMINE_DFLT_IP_DCTL_MODE_AFT_RST (0x0061)
+#define CARMINE_DFLT_IP_DCTL_STATES_AFT_RST (0x0002)
+#define CARMINE_DFLT_IP_DCTL_IO_CONT0 (0x0555)
+#define CARMINE_DFLT_IP_DCTL_IO_CONT1 (0x0555)
+#define CARMINE_DCTL_DLL_RESET (1)
+#endif
+
+#endif
diff --git a/drivers/video/carminefb_regs.h b/drivers/video/carminefb_regs.h
new file mode 100644
index 00000000000..045215600b7
--- /dev/null
+++ b/drivers/video/carminefb_regs.h
@@ -0,0 +1,159 @@
+#ifndef _CARMINEFB_REGS_H
+#define _CARMINEFB_REGS_H
+
+#define CARMINE_OVERLAY_EXT_MODE (0x00000002)
+#define CARMINE_GRAPH_REG (0x00000000)
+#define CARMINE_DISP0_REG (0x00100000)
+#define CARMINE_DISP1_REG (0x00140000)
+#define CARMINE_WB_REG (0x00180000)
+#define CARMINE_DCTL_REG (0x00300000)
+#define CARMINE_CTL_REG (0x00400000)
+#define CARMINE_WINDOW_MODE (0x00000001)
+#define CARMINE_EXTEND_MODE (CARMINE_WINDOW_MODE | \
+ CARMINE_OVERLAY_EXT_MODE)
+#define CARMINE_L0E (1 << 16)
+#define CARMINE_L2E (1 << 18)
+#define CARMINE_DEN (1 << 31)
+
+#define CARMINE_EXT_CMODE_DIRECT24_RGBA (0xC0000000)
+#define CARMINE_DCTL_REG_MODE_ADD (0x00)
+#define CARMINE_DCTL_REG_SETTIME1_EMODE (0x04)
+#define CARMINE_DCTL_REG_REFRESH_SETTIME2 (0x08)
+#define CARMINE_DCTL_REG_RSV0_STATES (0x0C)
+#define CARMINE_DCTL_REG_RSV2_RSV1 (0x10)
+#define CARMINE_DCTL_REG_DDRIF2_DDRIF1 (0x14)
+#define CARMINE_DCTL_REG_IOCONT1_IOCONT0 (0x24)
+#define CARMINE_DCTL_REG_STATES_MASK (0x000F)
+#define CARMINE_DCTL_INIT_WAIT_INTERVAL (1)
+#define CARMINE_DCTL_INIT_WAIT_LIMIT (5000)
+#define CARMINE_WB_REG_WBM_DEFAULT (0x0001c020)
+#define CARMINE_DISP_REG_L0RM (0x1880)
+#define CARMINE_DISP_REG_L0PX (0x1884)
+#define CARMINE_DISP_REG_L0PY (0x1888)
+#define CARMINE_DISP_REG_L2RM (0x18A0)
+#define CARMINE_DISP_REG_L2PX (0x18A4)
+#define CARMINE_DISP_REG_L2PY (0x18A8)
+#define CARMINE_DISP_REG_L3RM (0x18B0)
+#define CARMINE_DISP_REG_L3PX (0x18B4)
+#define CARMINE_DISP_REG_L3PY (0x18B8)
+#define CARMINE_DISP_REG_L4RM (0x18C0)
+#define CARMINE_DISP_REG_L4PX (0x18C4)
+#define CARMINE_DISP_REG_L4PY (0x18C8)
+#define CARMINE_DISP_REG_L5RM (0x18D0)
+#define CARMINE_DISP_REG_L5PX (0x18D4)
+#define CARMINE_DISP_REG_L5PY (0x18D8)
+#define CARMINE_DISP_REG_L6RM (0x1924)
+#define CARMINE_DISP_REG_L6PX (0x1928)
+#define CARMINE_DISP_REG_L6PY (0x192C)
+#define CARMINE_DISP_REG_L7RM (0x1964)
+#define CARMINE_DISP_REG_L7PX (0x1968)
+#define CARMINE_DISP_REG_L7PY (0x196C)
+#define CARMINE_WB_REG_WBM (0x0004)
+#define CARMINE_DISP_HTP_SHIFT (16)
+#define CARMINE_DISP_HDB_SHIFT (16)
+#define CARMINE_DISP_HSW_SHIFT (16)
+#define CARMINE_DISP_VSW_SHIFT (24)
+#define CARMINE_DISP_VTR_SHIFT (16)
+#define CARMINE_DISP_VDP_SHIFT (16)
+#define CARMINE_CURSOR_CUTZ_MASK (0x00000100)
+#define CARMINE_CURSOR0_PRIORITY_MASK (0x00010000)
+#define CARMINE_CURSOR1_PRIORITY_MASK (0x00020000)
+#define CARMINE_DISP_WIDTH_SHIFT (16)
+#define CARMINE_DISP_WIN_H_SHIFT (16)
+#define CARMINE_DISP_REG_H_TOTAL (0x0004)
+#define CARMINE_DISP_REG_H_PERIOD (0x0008)
+#define CARMINE_DISP_REG_V_H_W_H_POS (0x000C)
+#define CARMINE_DISP_REG_V_TOTAL (0x0010)
+#define CARMINE_DISP_REG_V_PERIOD_POS (0x0014)
+#define CARMINE_DISP_REG_L0_MODE_W_H (0x0020)
+#define CARMINE_DISP_REG_L0_ORG_ADR (0x0024)
+#define CARMINE_DISP_REG_L0_DISP_ADR (0x0028)
+#define CARMINE_DISP_REG_L0_DISP_POS (0x002C)
+#define CARMINE_DISP_REG_L1_WIDTH (0x0030)
+#define CARMINE_DISP_REG_L1_ORG_ADR (0x0034)
+#define CARMINE_DISP_REG_L2_MODE_W_H (0x0040)
+#define CARMINE_DISP_REG_L2_ORG_ADR1 (0x0044)
+#define CARMINE_DISP_REG_L2_DISP_ADR1 (0x0048)
+#define CARMINE_DISP_REG_L2_DISP_POS (0x0054)
+#define CARMINE_DISP_REG_L3_MODE_W_H (0x0058)
+#define CARMINE_DISP_REG_L3_ORG_ADR1 (0x005C)
+#define CARMINE_DISP_REG_L3_DISP_ADR1 (0x0060)
+#define CARMINE_DISP_REG_L3_DISP_POS (0x006C)
+#define CARMINE_DISP_REG_L4_MODE_W_H (0x0070)
+#define CARMINE_DISP_REG_L4_ORG_ADR1 (0x0074)
+#define CARMINE_DISP_REG_L4_DISP_ADR1 (0x0078)
+#define CARMINE_DISP_REG_L4_DISP_POS (0x0084)
+#define CARMINE_DISP_REG_L5_MODE_W_H (0x0088)
+#define CARMINE_DISP_REG_L5_ORG_ADR1 (0x008C)
+#define CARMINE_DISP_REG_L5_DISP_ADR1 (0x0090)
+#define CARMINE_DISP_REG_L5_DISP_POS (0x009C)
+#define CARMINE_DISP_REG_CURSOR_MODE (0x00A0)
+#define CARMINE_DISP_REG_CUR1_POS (0x00A8)
+#define CARMINE_DISP_REG_CUR2_POS (0x00B0)
+#define CARMINE_DISP_REG_C_TRANS (0x00BC)
+#define CARMINE_DISP_REG_MLMR_TRANS (0x00C0)
+#define CARMINE_DISP_REG_L0_EXT_MODE (0x0110)
+#define CARMINE_DISP_REG_L0_WIN_POS (0x0114)
+#define CARMINE_DISP_REG_L0_WIN_SIZE (0x0118)
+#define CARMINE_DISP_REG_L1_EXT_MODE (0x0120)
+#define CARMINE_DISP_REG_L1_WIN_POS (0x0124)
+#define CARMINE_DISP_REG_L1_WIN_SIZE (0x0128)
+#define CARMINE_DISP_REG_L2_EXT_MODE (0x0130)
+#define CARMINE_DISP_REG_L2_WIN_POS (0x0134)
+#define CARMINE_DISP_REG_L2_WIN_SIZE (0x0138)
+#define CARMINE_DISP_REG_L3_EXT_MODE (0x0140)
+#define CARMINE_DISP_REG_L3_WIN_POS (0x0144)
+#define CARMINE_DISP_REG_L3_WIN_SIZE (0x0148)
+#define CARMINE_DISP_REG_L4_EXT_MODE (0x0150)
+#define CARMINE_DISP_REG_L4_WIN_POS (0x0154)
+#define CARMINE_DISP_REG_L4_WIN_SIZE (0x0158)
+#define CARMINE_DISP_REG_L5_EXT_MODE (0x0160)
+#define CARMINE_DISP_REG_L5_WIN_POS (0x0164)
+#define CARMINE_DISP_REG_L5_WIN_SIZE (0x0168)
+#define CARMINE_DISP_REG_L6_EXT_MODE (0x1918)
+#define CARMINE_DISP_REG_L6_WIN_POS (0x191c)
+#define CARMINE_DISP_REG_L6_WIN_SIZE (0x1920)
+#define CARMINE_DISP_REG_L7_EXT_MODE (0x1958)
+#define CARMINE_DISP_REG_L7_WIN_POS (0x195c)
+#define CARMINE_DISP_REG_L7_WIN_SIZE (0x1960)
+#define CARMINE_DISP_REG_BLEND_MODE_L0 (0x00B4)
+#define CARMINE_DISP_REG_BLEND_MODE_L1 (0x0188)
+#define CARMINE_DISP_REG_BLEND_MODE_L2 (0x018C)
+#define CARMINE_DISP_REG_BLEND_MODE_L3 (0x0190)
+#define CARMINE_DISP_REG_BLEND_MODE_L4 (0x0194)
+#define CARMINE_DISP_REG_BLEND_MODE_L5 (0x0198)
+#define CARMINE_DISP_REG_BLEND_MODE_L6 (0x1990)
+#define CARMINE_DISP_REG_BLEND_MODE_L7 (0x1994)
+#define CARMINE_DISP_REG_L0_TRANS (0x01A0)
+#define CARMINE_DISP_REG_L1_TRANS (0x01A4)
+#define CARMINE_DISP_REG_L2_TRANS (0x01A8)
+#define CARMINE_DISP_REG_L3_TRANS (0x01AC)
+#define CARMINE_DISP_REG_L4_TRANS (0x01B0)
+#define CARMINE_DISP_REG_L5_TRANS (0x01B4)
+#define CARMINE_DISP_REG_L6_TRANS (0x1998)
+#define CARMINE_DISP_REG_L7_TRANS (0x199c)
+#define CARMINE_EXTEND_MODE_MASK (0x00000003)
+#define CARMINE_DISP_DCM_MASK (0x0000FFFF)
+#define CARMINE_DISP_REG_DCM1 (0x0100)
+#define CARMINE_DISP_WIDTH_UNIT (64)
+#define CARMINE_DISP_REG_L6_MODE_W_H (0x1900)
+#define CARMINE_DISP_REG_L6_ORG_ADR1 (0x1904)
+#define CARMINE_DISP_REG_L6_DISP_ADR0 (0x1908)
+#define CARMINE_DISP_REG_L6_DISP_POS (0x1914)
+#define CARMINE_DISP_REG_L7_MODE_W_H (0x1940)
+#define CARMINE_DISP_REG_L7_ORG_ADR1 (0x1944)
+#define CARMINE_DISP_REG_L7_DISP_ADR0 (0x1948)
+#define CARMINE_DISP_REG_L7_DISP_POS (0x1954)
+#define CARMINE_CTL_REG_CLOCK_ENABLE (0x000C)
+#define CARMINE_CTL_REG_SOFTWARE_RESET (0x0010)
+#define CARMINE_CTL_REG_IST_MASK_ALL (0x07FFFFFF)
+#define CARMINE_GRAPH_REG_VRINTM (0x00028064)
+#define CARMINE_GRAPH_REG_VRERRM (0x0002806C)
+#define CARMINE_GRAPH_REG_DC_OFFSET_PX (0x0004005C)
+#define CARMINE_GRAPH_REG_DC_OFFSET_PY (0x00040060)
+#define CARMINE_GRAPH_REG_DC_OFFSET_LX (0x00040064)
+#define CARMINE_GRAPH_REG_DC_OFFSET_LY (0x00040068)
+#define CARMINE_GRAPH_REG_DC_OFFSET_TX (0x0004006C)
+#define CARMINE_GRAPH_REG_DC_OFFSET_TY (0x00040070)
+
+#endif
diff --git a/drivers/video/cobalt_lcdfb.c b/drivers/video/cobalt_lcdfb.c
new file mode 100644
index 00000000000..7bad24ed04e
--- /dev/null
+++ b/drivers/video/cobalt_lcdfb.c
@@ -0,0 +1,371 @@
+/*
+ * Cobalt server LCD frame buffer driver.
+ *
+ * Copyright (C) 2008 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+
+/*
+ * Cursor position address
+ * \X 0 1 2 ... 14 15
+ * Y+----+----+----+---+----+----+
+ * 0|0x00|0x01|0x02|...|0x0e|0x0f|
+ * +----+----+----+---+----+----+
+ * 1|0x40|0x41|0x42|...|0x4e|0x4f|
+ * +----+----+----+---+----+----+
+ */
+#define LCD_DATA_REG_OFFSET 0x10
+#define LCD_XRES_MAX 16
+#define LCD_YRES_MAX 2
+#define LCD_CHARS_MAX 32
+
+#define LCD_CLEAR 0x01
+#define LCD_CURSOR_MOVE_HOME 0x02
+#define LCD_RESET 0x06
+#define LCD_OFF 0x08
+#define LCD_CURSOR_OFF 0x0c
+#define LCD_CURSOR_BLINK_OFF 0x0e
+#define LCD_CURSOR_ON 0x0f
+#define LCD_ON LCD_CURSOR_ON
+#define LCD_CURSOR_MOVE_LEFT 0x10
+#define LCD_CURSOR_MOVE_RIGHT 0x14
+#define LCD_DISPLAY_LEFT 0x18
+#define LCD_DISPLAY_RIGHT 0x1c
+#define LCD_PRERESET 0x3f /* execute 4 times continuously */
+#define LCD_BUSY 0x80
+
+#define LCD_GRAPHIC_MODE 0x40
+#define LCD_TEXT_MODE 0x80
+#define LCD_CUR_POS_MASK 0x7f
+
+#define LCD_CUR_POS(x) ((x) & LCD_CUR_POS_MASK)
+#define LCD_TEXT_POS(x) ((x) | LCD_TEXT_MODE)
+
+static inline void lcd_write_control(struct fb_info *info, u8 control)
+{
+ writel((u32)control << 24, info->screen_base);
+}
+
+static inline u8 lcd_read_control(struct fb_info *info)
+{
+ return readl(info->screen_base) >> 24;
+}
+
+static inline void lcd_write_data(struct fb_info *info, u8 data)
+{
+ writel((u32)data << 24, info->screen_base + LCD_DATA_REG_OFFSET);
+}
+
+static inline u8 lcd_read_data(struct fb_info *info)
+{
+ return readl(info->screen_base + LCD_DATA_REG_OFFSET) >> 24;
+}
+
+static int lcd_busy_wait(struct fb_info *info)
+{
+ u8 val = 0;
+ int timeout = 10, retval = 0;
+
+ do {
+ val = lcd_read_control(info);
+ val &= LCD_BUSY;
+ if (val != LCD_BUSY)
+ break;
+
+ if (msleep_interruptible(1))
+ return -EINTR;
+
+ timeout--;
+ } while (timeout);
+
+ if (val == LCD_BUSY)
+ retval = -EBUSY;
+
+ return retval;
+}
+
+static void lcd_clear(struct fb_info *info)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ udelay(150);
+
+ lcd_write_control(info, LCD_PRERESET);
+ }
+
+ udelay(150);
+
+ lcd_write_control(info, LCD_CLEAR);
+
+ udelay(150);
+
+ lcd_write_control(info, LCD_RESET);
+}
+
+static struct fb_fix_screeninfo cobalt_lcdfb_fix __initdata = {
+ .id = "cobalt-lcd",
+ .type = FB_TYPE_TEXT,
+ .type_aux = FB_AUX_TEXT_MDA,
+ .visual = FB_VISUAL_MONO01,
+ .line_length = LCD_XRES_MAX,
+ .accel = FB_ACCEL_NONE,
+};
+
+static ssize_t cobalt_lcdfb_read(struct fb_info *info, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char src[LCD_CHARS_MAX];
+ unsigned long pos;
+ int len, retval = 0;
+
+ pos = *ppos;
+ if (pos >= LCD_CHARS_MAX || count == 0)
+ return 0;
+
+ if (count > LCD_CHARS_MAX)
+ count = LCD_CHARS_MAX;
+
+ if (pos + count > LCD_CHARS_MAX)
+ count = LCD_CHARS_MAX - pos;
+
+ for (len = 0; len < count; len++) {
+ retval = lcd_busy_wait(info);
+ if (retval < 0)
+ break;
+
+ lcd_write_control(info, LCD_TEXT_POS(pos));
+
+ retval = lcd_busy_wait(info);
+ if (retval < 0)
+ break;
+
+ src[len] = lcd_read_data(info);
+ if (pos == 0x0f)
+ pos = 0x40;
+ else
+ pos++;
+ }
+
+ if (retval < 0 && signal_pending(current))
+ return -ERESTARTSYS;
+
+ if (copy_to_user(buf, src, len))
+ return -EFAULT;
+
+ *ppos += len;
+
+ return len;
+}
+
+static ssize_t cobalt_lcdfb_write(struct fb_info *info, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char dst[LCD_CHARS_MAX];
+ unsigned long pos;
+ int len, retval = 0;
+
+ pos = *ppos;
+ if (pos >= LCD_CHARS_MAX || count == 0)
+ return 0;
+
+ if (count > LCD_CHARS_MAX)
+ count = LCD_CHARS_MAX;
+
+ if (pos + count > LCD_CHARS_MAX)
+ count = LCD_CHARS_MAX - pos;
+
+ if (copy_from_user(dst, buf, count))
+ return -EFAULT;
+
+ for (len = 0; len < count; len++) {
+ retval = lcd_busy_wait(info);
+ if (retval < 0)
+ break;
+
+ lcd_write_control(info, LCD_TEXT_POS(pos));
+
+ retval = lcd_busy_wait(info);
+ if (retval < 0)
+ break;
+
+ lcd_write_data(info, dst[len]);
+ if (pos == 0x0f)
+ pos = 0x40;
+ else
+ pos++;
+ }
+
+ if (retval < 0 && signal_pending(current))
+ return -ERESTARTSYS;
+
+ *ppos += len;
+
+ return len;
+}
+
+static int cobalt_lcdfb_blank(int blank_mode, struct fb_info *info)
+{
+ int retval;
+
+ retval = lcd_busy_wait(info);
+ if (retval < 0)
+ return retval;
+
+ switch (blank_mode) {
+ case FB_BLANK_UNBLANK:
+ lcd_write_control(info, LCD_ON);
+ break;
+ default:
+ lcd_write_control(info, LCD_OFF);
+ break;
+ }
+
+ return 0;
+}
+
+static int cobalt_lcdfb_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+ u32 x, y;
+ int retval;
+
+ switch (cursor->set) {
+ case FB_CUR_SETPOS:
+ x = cursor->image.dx;
+ y = cursor->image.dy;
+ if (x >= LCD_XRES_MAX || y >= LCD_YRES_MAX)
+ return -EINVAL;
+
+ retval = lcd_busy_wait(info);
+ if (retval < 0)
+ return retval;
+
+ lcd_write_control(info,
+ LCD_TEXT_POS(info->fix.line_length * y + x));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ retval = lcd_busy_wait(info);
+ if (retval < 0)
+ return retval;
+
+ if (cursor->enable)
+ lcd_write_control(info, LCD_CURSOR_ON);
+ else
+ lcd_write_control(info, LCD_CURSOR_OFF);
+
+ return 0;
+}
+
+static struct fb_ops cobalt_lcd_fbops = {
+ .owner = THIS_MODULE,
+ .fb_read = cobalt_lcdfb_read,
+ .fb_write = cobalt_lcdfb_write,
+ .fb_blank = cobalt_lcdfb_blank,
+ .fb_cursor = cobalt_lcdfb_cursor,
+};
+
+static int __init cobalt_lcdfb_probe(struct platform_device *dev)
+{
+ struct fb_info *info;
+ struct resource *res;
+ int retval;
+
+ info = framebuffer_alloc(0, &dev->dev);
+ if (!info)
+ return -ENOMEM;
+
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!res) {
+ framebuffer_release(info);
+ return -EBUSY;
+ }
+
+ info->screen_size = res->end - res->start + 1;
+ info->screen_base = ioremap(res->start, info->screen_size);
+ info->fbops = &cobalt_lcd_fbops;
+ info->fix = cobalt_lcdfb_fix;
+ info->fix.smem_start = res->start;
+ info->fix.smem_len = info->screen_size;
+ info->pseudo_palette = NULL;
+ info->par = NULL;
+ info->flags = FBINFO_DEFAULT;
+
+ retval = register_framebuffer(info);
+ if (retval < 0) {
+ iounmap(info->screen_base);
+ framebuffer_release(info);
+ return retval;
+ }
+
+ platform_set_drvdata(dev, info);
+
+ lcd_clear(info);
+
+ printk(KERN_INFO "fb%d: Cobalt server LCD frame buffer device\n",
+ info->node);
+
+ return 0;
+}
+
+static int __devexit cobalt_lcdfb_remove(struct platform_device *dev)
+{
+ struct fb_info *info;
+
+ info = platform_get_drvdata(dev);
+ if (info) {
+ iounmap(info->screen_base);
+ unregister_framebuffer(info);
+ framebuffer_release(info);
+ }
+
+ return 0;
+}
+
+static struct platform_driver cobalt_lcdfb_driver = {
+ .probe = cobalt_lcdfb_probe,
+ .remove = __devexit_p(cobalt_lcdfb_remove),
+ .driver = {
+ .name = "cobalt-lcd",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init cobalt_lcdfb_init(void)
+{
+ return platform_driver_register(&cobalt_lcdfb_driver);
+}
+
+static void __exit cobalt_lcdfb_exit(void)
+{
+ platform_driver_unregister(&cobalt_lcdfb_driver);
+}
+
+module_init(cobalt_lcdfb_init);
+module_exit(cobalt_lcdfb_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Yoichi Yuasa");
+MODULE_DESCRIPTION("Cobalt server LCD frame buffer driver");
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 4be3b46c069..3ccfa76d9b2 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -107,9 +107,7 @@ static struct display fb_display[MAX_NR_CONSOLES];
static signed char con2fb_map[MAX_NR_CONSOLES];
static signed char con2fb_map_boot[MAX_NR_CONSOLES];
-#ifndef MODULE
-static int logo_height;
-#endif
+
static int logo_lines;
/* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO
enums. */
@@ -607,6 +605,7 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
struct fbcon_ops *ops = info->fbcon_par;
int cnt, erase = vc->vc_video_erase_char, step;
unsigned short *save = NULL, *r, *q;
+ int logo_height;
if (info->flags & FBINFO_MODULE) {
logo_shown = FBCON_LOGO_DONTSHOW;
diff --git a/drivers/video/console/fbcon.h b/drivers/video/console/fbcon.h
index 0135e039545..de1b1365279 100644
--- a/drivers/video/console/fbcon.h
+++ b/drivers/video/console/fbcon.h
@@ -92,7 +92,7 @@ struct fbcon_ops {
#define attr_fgcol(fgshift,s) \
(((s) >> (fgshift)) & 0x0f)
#define attr_bgcol(bgshift,s) \
- (((s) >> (bgshift)) & 0x0f)
+ (((s) >> (bgshift)) & 0x07)
/* Monochrome */
#define attr_bold(s) \
@@ -146,10 +146,8 @@ static inline int attr_col_ec(int shift, struct vc_data *vc,
return is_fg ? fg : bg;
}
-#define attr_bgcol_ec(bgshift,vc,info) \
- attr_col_ec(bgshift,vc,info,0);
-#define attr_fgcol_ec(fgshift,vc,info) \
- attr_col_ec(fgshift,vc,info,1);
+#define attr_bgcol_ec(bgshift, vc, info) attr_col_ec(bgshift, vc, info, 0)
+#define attr_fgcol_ec(fgshift, vc, info) attr_col_ec(fgshift, vc, info, 1)
/* Font */
#define REFCOUNT(fd) (((int *)(fd))[-1])
diff --git a/drivers/video/console/mdacon.c b/drivers/video/console/mdacon.c
index 38a296bbdfc..9901064199b 100644
--- a/drivers/video/console/mdacon.c
+++ b/drivers/video/console/mdacon.c
@@ -71,13 +71,15 @@ static char *mda_type_name;
/* console information */
-static int mda_first_vc = 1;
+static int mda_first_vc = 13;
static int mda_last_vc = 16;
static struct vc_data *mda_display_fg = NULL;
module_param(mda_first_vc, int, 0);
+MODULE_PARM_DESC(mda_first_vc, "First virtual console. Default: 13");
module_param(mda_last_vc, int, 0);
+MODULE_PARM_DESC(mda_last_vc, "Last virtual console. Default: 16");
/* MDA register values
*/
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index 1cd5071e536..5d84b343109 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -35,6 +35,7 @@
#include <linux/device.h>
#include <linux/efi.h>
#include <linux/fb.h>
+#include <linux/major.h>
#include <asm/fb.h>
@@ -848,9 +849,8 @@ int
fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var)
{
struct fb_fix_screeninfo *fix = &info->fix;
- int xoffset = var->xoffset;
- int yoffset = var->yoffset;
- int err = 0, yres = info->var.yres;
+ unsigned int yres = info->var.yres;
+ int err = 0;
if (var->yoffset > 0) {
if (var->vmode & FB_VMODE_YWRAP) {
@@ -866,8 +866,8 @@ fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var)
(var->xoffset % fix->xpanstep)))
err = -EINVAL;
- if (err || !info->fbops->fb_pan_display || xoffset < 0 ||
- yoffset < 0 || var->yoffset + yres > info->var.yres_virtual ||
+ if (err || !info->fbops->fb_pan_display ||
+ var->yoffset + yres > info->var.yres_virtual ||
var->xoffset + info->var.xres > info->var.xres_virtual)
return -EINVAL;
diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c
index 052e1805849..6a0aa180c26 100644
--- a/drivers/video/fbmon.c
+++ b/drivers/video/fbmon.c
@@ -879,7 +879,7 @@ int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
if (edid_is_timing_block(block)) {
var->xres = var->xres_virtual = H_ACTIVE;
var->yres = var->yres_virtual = V_ACTIVE;
- var->height = var->width = -1;
+ var->height = var->width = 0;
var->right_margin = H_SYNC_OFFSET;
var->left_margin = (H_ACTIVE + H_BLANKING) -
(H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c
index 09d7e22c6fe..9cd36c223d3 100644
--- a/drivers/video/fsl-diu-fb.c
+++ b/drivers/video/fsl-diu-fb.c
@@ -279,58 +279,42 @@ static struct diu_hw dr = {
static struct diu_pool pool;
-/* To allocate memory for framebuffer. First try __get_free_pages(). If it
- * fails, try rh_alloc. The reason is __get_free_pages() cannot allocate
- * very large memory (more than 4MB). We don't want to allocate all memory
- * in rheap since small memory allocation/deallocation will fragment the
- * rheap and make the furture large allocation fail.
+/**
+ * fsl_diu_alloc - allocate memory for the DIU
+ * @size: number of bytes to allocate
+ * @param: returned physical address of memory
+ *
+ * This function allocates a physically-contiguous block of memory.
*/
-
-static void *fsl_diu_alloc(unsigned long size, phys_addr_t *phys)
+static void *fsl_diu_alloc(size_t size, phys_addr_t *phys)
{
void *virt;
- pr_debug("size=%lu\n", size);
+ pr_debug("size=%zu\n", size);
- virt = (void *)__get_free_pages(GFP_DMA | __GFP_ZERO, get_order(size));
+ virt = alloc_pages_exact(size, GFP_DMA | __GFP_ZERO);
if (virt) {
*phys = virt_to_phys(virt);
- pr_debug("virt %p, phys=%llx\n", virt, (uint64_t) *phys);
- return virt;
- }
- if (!diu_ops.diu_mem) {
- printk(KERN_INFO "%s: no diu_mem."
- " To reserve more memory, put 'diufb=15M' "
- "in the command line\n", __func__);
- return NULL;
- }
-
- virt = (void *)rh_alloc(&diu_ops.diu_rh_info, size, "DIU");
- if (virt) {
- *phys = virt_to_bus(virt);
- memset(virt, 0, size);
+ pr_debug("virt=%p phys=%llx\n", virt,
+ (unsigned long long)*phys);
}
- pr_debug("rh virt=%p phys=%llx\n", virt, (unsigned long long)*phys);
-
return virt;
}
-static void fsl_diu_free(void *p, unsigned long size)
+/**
+ * fsl_diu_free - release DIU memory
+ * @virt: pointer returned by fsl_diu_alloc()
+ * @size: number of bytes allocated by fsl_diu_alloc()
+ *
+ * This function releases memory allocated by fsl_diu_alloc().
+ */
+static void fsl_diu_free(void *virt, size_t size)
{
- pr_debug("p=%p size=%lu\n", p, size);
+ pr_debug("virt=%p size=%zu\n", virt, size);
- if (!p)
- return;
-
- if ((p >= diu_ops.diu_mem) &&
- (p < (diu_ops.diu_mem + diu_ops.diu_size))) {
- pr_debug("rh\n");
- rh_free(&diu_ops.diu_rh_info, (unsigned long) p);
- } else {
- pr_debug("dma\n");
- free_pages((unsigned long)p, get_order(size));
- }
+ if (virt && size)
+ free_pages_exact(virt, size);
}
static int fsl_diu_enable_panel(struct fb_info *info)
diff --git a/drivers/video/geode/lxfb.h b/drivers/video/geode/lxfb.h
index 3b9416f4ee2..6a51448fd3f 100644
--- a/drivers/video/geode/lxfb.h
+++ b/drivers/video/geode/lxfb.h
@@ -51,8 +51,6 @@ static inline unsigned int lx_get_pitch(unsigned int xres, int bpp)
}
void lx_set_mode(struct fb_info *);
-void lx_get_gamma(struct fb_info *, unsigned int *, int);
-void lx_set_gamma(struct fb_info *, unsigned int *, int);
unsigned int lx_framebuffer_size(void);
int lx_blank_display(struct fb_info *, int);
void lx_set_palette_reg(struct fb_info *, unsigned int, unsigned int,
diff --git a/drivers/video/geode/lxfb_ops.c b/drivers/video/geode/lxfb_ops.c
index aaef9165ec9..b1cd49c9935 100644
--- a/drivers/video/geode/lxfb_ops.c
+++ b/drivers/video/geode/lxfb_ops.c
@@ -517,25 +517,25 @@ void lx_set_palette_reg(struct fb_info *info, unsigned regno,
int lx_blank_display(struct fb_info *info, int blank_mode)
{
struct lxfb_par *par = info->par;
- u32 dcfg, fp_pm;
- int blank, hsync, vsync, crt;
+ u32 dcfg, misc, fp_pm;
+ int blank, hsync, vsync;
/* CRT power saving modes. */
switch (blank_mode) {
case FB_BLANK_UNBLANK:
- blank = 0; hsync = 1; vsync = 1; crt = 1;
+ blank = 0; hsync = 1; vsync = 1;
break;
case FB_BLANK_NORMAL:
- blank = 1; hsync = 1; vsync = 1; crt = 1;
+ blank = 1; hsync = 1; vsync = 1;
break;
case FB_BLANK_VSYNC_SUSPEND:
- blank = 1; hsync = 1; vsync = 0; crt = 1;
+ blank = 1; hsync = 1; vsync = 0;
break;
case FB_BLANK_HSYNC_SUSPEND:
- blank = 1; hsync = 0; vsync = 1; crt = 1;
+ blank = 1; hsync = 0; vsync = 1;
break;
case FB_BLANK_POWERDOWN:
- blank = 1; hsync = 0; vsync = 0; crt = 0;
+ blank = 1; hsync = 0; vsync = 0;
break;
default:
return -EINVAL;
@@ -545,15 +545,23 @@ int lx_blank_display(struct fb_info *info, int blank_mode)
dcfg &= ~(VP_DCFG_DAC_BL_EN | VP_DCFG_HSYNC_EN | VP_DCFG_VSYNC_EN |
VP_DCFG_CRT_EN);
if (!blank)
- dcfg |= VP_DCFG_DAC_BL_EN;
+ dcfg |= VP_DCFG_DAC_BL_EN | VP_DCFG_CRT_EN;
if (hsync)
dcfg |= VP_DCFG_HSYNC_EN;
if (vsync)
dcfg |= VP_DCFG_VSYNC_EN;
- if (crt)
- dcfg |= VP_DCFG_CRT_EN;
+
write_vp(par, VP_DCFG, dcfg);
+ misc = read_vp(par, VP_MISC);
+
+ if (vsync && hsync)
+ misc &= ~VP_MISC_DACPWRDN;
+ else
+ misc |= VP_MISC_DACPWRDN;
+
+ write_vp(par, VP_MISC, misc);
+
/* Power on/off flat panel */
if (par->output & OUTPUT_PANEL) {
diff --git a/drivers/video/hgafb.c b/drivers/video/hgafb.c
index c18880d9db1..0129c044f6d 100644
--- a/drivers/video/hgafb.c
+++ b/drivers/video/hgafb.c
@@ -551,7 +551,7 @@ static struct fb_ops hgafb_ops = {
* Initialization
*/
-static int __init hgafb_probe(struct device *device)
+static int __init hgafb_probe(struct platform_device *pdev)
{
struct fb_info *info;
@@ -565,7 +565,7 @@ static int __init hgafb_probe(struct device *device)
printk(KERN_INFO "hgafb: %s with %ldK of memory detected.\n",
hga_type_name, hga_vram_len/1024);
- info = framebuffer_alloc(0, NULL);
+ info = framebuffer_alloc(0, &pdev->dev);
if (!info) {
iounmap(hga_vram);
return -ENOMEM;
@@ -593,13 +593,13 @@ static int __init hgafb_probe(struct device *device)
printk(KERN_INFO "fb%d: %s frame buffer device\n",
info->node, info->fix.id);
- dev_set_drvdata(device, info);
+ platform_set_drvdata(pdev, info);
return 0;
}
-static int hgafb_remove(struct device *device)
+static int hgafb_remove(struct platform_device *pdev)
{
- struct fb_info *info = dev_get_drvdata(device);
+ struct fb_info *info = platform_get_drvdata(pdev);
hga_txt_mode();
hga_clear_screen();
@@ -620,16 +620,15 @@ static int hgafb_remove(struct device *device)
return 0;
}
-static struct device_driver hgafb_driver = {
- .name = "hgafb",
- .bus = &platform_bus_type,
+static struct platform_driver hgafb_driver = {
.probe = hgafb_probe,
.remove = hgafb_remove,
+ .driver = {
+ .name = "hgafb",
+ },
};
-static struct platform_device hgafb_device = {
- .name = "hgafb",
-};
+static struct platform_device *hgafb_device;
static int __init hgafb_init(void)
{
@@ -638,12 +637,15 @@ static int __init hgafb_init(void)
if (fb_get_options("hgafb", NULL))
return -ENODEV;
- ret = driver_register(&hgafb_driver);
+ ret = platform_driver_register(&hgafb_driver);
if (!ret) {
- ret = platform_device_register(&hgafb_device);
- if (ret)
- driver_unregister(&hgafb_driver);
+ hgafb_device = platform_device_register_simple("hgafb", 0, NULL, 0);
+
+ if (IS_ERR(hgafb_device)) {
+ platform_driver_unregister(&hgafb_driver);
+ ret = PTR_ERR(hgafb_device);
+ }
}
return ret;
@@ -651,8 +653,8 @@ static int __init hgafb_init(void)
static void __exit hgafb_exit(void)
{
- platform_device_unregister(&hgafb_device);
- driver_unregister(&hgafb_driver);
+ platform_device_unregister(hgafb_device);
+ platform_driver_unregister(&hgafb_driver);
}
/* -------------------------------------------------------------------------
diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c
index 94e4d3ac1a0..0c5a475c1ca 100644
--- a/drivers/video/imxfb.c
+++ b/drivers/video/imxfb.c
@@ -24,6 +24,7 @@
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/mm.h>
#include <linux/fb.h>
#include <linux/delay.h>
#include <linux/init.h>
diff --git a/drivers/video/neofb.c b/drivers/video/neofb.c
index 5246b0402d7..25172b2a2a9 100644
--- a/drivers/video/neofb.c
+++ b/drivers/video/neofb.c
@@ -201,7 +201,6 @@ static int neoFindMode(int xres, int yres, int depth)
*
* Determine the closest clock frequency to the one requested.
*/
-#define REF_FREQ 0xe517 /* 14.31818 in 20.12 fixed point */
#define MAX_N 127
#define MAX_D 31
#define MAX_F 1
@@ -211,27 +210,24 @@ static void neoCalcVCLK(const struct fb_info *info,
{
int n, d, f;
int n_best = 0, d_best = 0, f_best = 0;
- long f_best_diff = (0x7ffff << 12); /* 20.12 */
- long f_target = (freq << 12) / 1000; /* 20.12 */
+ long f_best_diff = 0x7ffff;
for (f = 0; f <= MAX_F; f++)
- for (n = 0; n <= MAX_N; n++)
- for (d = 0; d <= MAX_D; d++) {
- long f_out; /* 20.12 */
- long f_diff; /* 20.12 */
-
- f_out =
- ((((n + 1) << 12) / ((d +
- 1) *
- (1 << f))) >> 12)
- * REF_FREQ;
- f_diff = abs(f_out - f_target);
- if (f_diff < f_best_diff) {
+ for (d = 0; d <= MAX_D; d++)
+ for (n = 0; n <= MAX_N; n++) {
+ long f_out;
+ long f_diff;
+
+ f_out = ((14318 * (n + 1)) / (d + 1)) >> f;
+ f_diff = abs(f_out - freq);
+ if (f_diff <= f_best_diff) {
f_best_diff = f_diff;
n_best = n;
d_best = d;
f_best = f;
}
+ if (f_out > freq)
+ break;
}
if (info->fix.accel == FB_ACCEL_NEOMAGIC_NM2200 ||
@@ -248,11 +244,11 @@ static void neoCalcVCLK(const struct fb_info *info,
par->VCLK3Denominator = d_best;
#ifdef NEOFB_DEBUG
- printk("neoVCLK: f:%d NumLow=%d NumHi=%d Den=%d Df=%d\n",
- f_target >> 12,
+ printk(KERN_DEBUG "neoVCLK: f:%ld NumLow=%d NumHi=%d Den=%d Df=%ld\n",
+ freq,
par->VCLK3NumeratorLow,
par->VCLK3NumeratorHigh,
- par->VCLK3Denominator, f_best_diff >> 12);
+ par->VCLK3Denominator, f_best_diff);
#endif
}
@@ -263,15 +259,20 @@ static void neoCalcVCLK(const struct fb_info *info,
*/
static int vgaHWInit(const struct fb_var_screeninfo *var,
- const struct fb_info *info,
- struct neofb_par *par, struct xtimings *timings)
+ struct neofb_par *par)
{
+ int hsync_end = var->xres + var->right_margin + var->hsync_len;
+ int htotal = (hsync_end + var->left_margin) >> 3;
+ int vsync_start = var->yres + var->lower_margin;
+ int vsync_end = vsync_start + var->vsync_len;
+ int vtotal = vsync_end + var->upper_margin;
+
par->MiscOutReg = 0x23;
- if (!(timings->sync & FB_SYNC_HOR_HIGH_ACT))
+ if (!(var->sync & FB_SYNC_HOR_HIGH_ACT))
par->MiscOutReg |= 0x40;
- if (!(timings->sync & FB_SYNC_VERT_HIGH_ACT))
+ if (!(var->sync & FB_SYNC_VERT_HIGH_ACT))
par->MiscOutReg |= 0x80;
/*
@@ -286,25 +287,25 @@ static int vgaHWInit(const struct fb_var_screeninfo *var,
/*
* CRTC Controller
*/
- par->CRTC[0] = (timings->HTotal >> 3) - 5;
- par->CRTC[1] = (timings->HDisplay >> 3) - 1;
- par->CRTC[2] = (timings->HDisplay >> 3) - 1;
- par->CRTC[3] = (((timings->HTotal >> 3) - 1) & 0x1F) | 0x80;
- par->CRTC[4] = (timings->HSyncStart >> 3);
- par->CRTC[5] = ((((timings->HTotal >> 3) - 1) & 0x20) << 2)
- | (((timings->HSyncEnd >> 3)) & 0x1F);
- par->CRTC[6] = (timings->VTotal - 2) & 0xFF;
- par->CRTC[7] = (((timings->VTotal - 2) & 0x100) >> 8)
- | (((timings->VDisplay - 1) & 0x100) >> 7)
- | ((timings->VSyncStart & 0x100) >> 6)
- | (((timings->VDisplay - 1) & 0x100) >> 5)
- | 0x10 | (((timings->VTotal - 2) & 0x200) >> 4)
- | (((timings->VDisplay - 1) & 0x200) >> 3)
- | ((timings->VSyncStart & 0x200) >> 2);
+ par->CRTC[0] = htotal - 5;
+ par->CRTC[1] = (var->xres >> 3) - 1;
+ par->CRTC[2] = (var->xres >> 3) - 1;
+ par->CRTC[3] = ((htotal - 1) & 0x1F) | 0x80;
+ par->CRTC[4] = ((var->xres + var->right_margin) >> 3);
+ par->CRTC[5] = (((htotal - 1) & 0x20) << 2)
+ | (((hsync_end >> 3)) & 0x1F);
+ par->CRTC[6] = (vtotal - 2) & 0xFF;
+ par->CRTC[7] = (((vtotal - 2) & 0x100) >> 8)
+ | (((var->yres - 1) & 0x100) >> 7)
+ | ((vsync_start & 0x100) >> 6)
+ | (((var->yres - 1) & 0x100) >> 5)
+ | 0x10 | (((vtotal - 2) & 0x200) >> 4)
+ | (((var->yres - 1) & 0x200) >> 3)
+ | ((vsync_start & 0x200) >> 2);
par->CRTC[8] = 0x00;
- par->CRTC[9] = (((timings->VDisplay - 1) & 0x200) >> 4) | 0x40;
+ par->CRTC[9] = (((var->yres - 1) & 0x200) >> 4) | 0x40;
- if (timings->dblscan)
+ if (var->vmode & FB_VMODE_DOUBLE)
par->CRTC[9] |= 0x80;
par->CRTC[10] = 0x00;
@@ -313,13 +314,13 @@ static int vgaHWInit(const struct fb_var_screeninfo *var,
par->CRTC[13] = 0x00;
par->CRTC[14] = 0x00;
par->CRTC[15] = 0x00;
- par->CRTC[16] = timings->VSyncStart & 0xFF;
- par->CRTC[17] = (timings->VSyncEnd & 0x0F) | 0x20;
- par->CRTC[18] = (timings->VDisplay - 1) & 0xFF;
+ par->CRTC[16] = vsync_start & 0xFF;
+ par->CRTC[17] = (vsync_end & 0x0F) | 0x20;
+ par->CRTC[18] = (var->yres - 1) & 0xFF;
par->CRTC[19] = var->xres_virtual >> 4;
par->CRTC[20] = 0x00;
- par->CRTC[21] = (timings->VDisplay - 1) & 0xFF;
- par->CRTC[22] = (timings->VTotal - 1) & 0xFF;
+ par->CRTC[21] = (var->yres - 1) & 0xFF;
+ par->CRTC[22] = (vtotal - 1) & 0xFF;
par->CRTC[23] = 0xC3;
par->CRTC[24] = 0xFF;
@@ -483,7 +484,8 @@ static inline int neo2200_sync(struct fb_info *info)
{
struct neofb_par *par = info->par;
- while (readl(&par->neo2200->bltStat) & 1);
+ while (readl(&par->neo2200->bltStat) & 1)
+ cpu_relax();
return 0;
}
@@ -591,34 +593,14 @@ static int
neofb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
struct neofb_par *par = info->par;
- unsigned int pixclock = var->pixclock;
- struct xtimings timings;
int memlen, vramlen;
int mode_ok = 0;
DBG("neofb_check_var");
- if (!pixclock)
- pixclock = 10000; /* 10ns = 100MHz */
- timings.pixclock = 1000000000 / pixclock;
- if (timings.pixclock < 1)
- timings.pixclock = 1;
-
- if (timings.pixclock > par->maxClock)
+ if (PICOS2KHZ(var->pixclock) > par->maxClock)
return -EINVAL;
- timings.dblscan = var->vmode & FB_VMODE_DOUBLE;
- timings.interlaced = var->vmode & FB_VMODE_INTERLACED;
- timings.HDisplay = var->xres;
- timings.HSyncStart = timings.HDisplay + var->right_margin;
- timings.HSyncEnd = timings.HSyncStart + var->hsync_len;
- timings.HTotal = timings.HSyncEnd + var->left_margin;
- timings.VDisplay = var->yres;
- timings.VSyncStart = timings.VDisplay + var->lower_margin;
- timings.VSyncEnd = timings.VSyncStart + var->vsync_len;
- timings.VTotal = timings.VSyncEnd + var->upper_margin;
- timings.sync = var->sync;
-
/* Is the mode larger than the LCD panel? */
if (par->internal_display &&
((var->xres > par->NeoPanelWidth) ||
@@ -759,11 +741,11 @@ neofb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
static int neofb_set_par(struct fb_info *info)
{
struct neofb_par *par = info->par;
- struct xtimings timings;
unsigned char temp;
int i, clock_hi = 0;
int lcd_stretch;
int hoffset, voffset;
+ int vsync_start, vtotal;
DBG("neofb_set_par");
@@ -771,28 +753,15 @@ static int neofb_set_par(struct fb_info *info)
vgaHWProtect(1); /* Blank the screen */
- timings.dblscan = info->var.vmode & FB_VMODE_DOUBLE;
- timings.interlaced = info->var.vmode & FB_VMODE_INTERLACED;
- timings.HDisplay = info->var.xres;
- timings.HSyncStart = timings.HDisplay + info->var.right_margin;
- timings.HSyncEnd = timings.HSyncStart + info->var.hsync_len;
- timings.HTotal = timings.HSyncEnd + info->var.left_margin;
- timings.VDisplay = info->var.yres;
- timings.VSyncStart = timings.VDisplay + info->var.lower_margin;
- timings.VSyncEnd = timings.VSyncStart + info->var.vsync_len;
- timings.VTotal = timings.VSyncEnd + info->var.upper_margin;
- timings.sync = info->var.sync;
- timings.pixclock = PICOS2KHZ(info->var.pixclock);
-
- if (timings.pixclock < 1)
- timings.pixclock = 1;
+ vsync_start = info->var.yres + info->var.lower_margin;
+ vtotal = vsync_start + info->var.vsync_len + info->var.upper_margin;
/*
* This will allocate the datastructure and initialize all of the
* generic VGA registers.
*/
- if (vgaHWInit(&info->var, info, par, &timings))
+ if (vgaHWInit(&info->var, par))
return -EINVAL;
/*
@@ -831,10 +800,10 @@ static int neofb_set_par(struct fb_info *info)
par->ExtCRTDispAddr = 0x10;
/* Vertical Extension */
- par->VerticalExt = (((timings.VTotal - 2) & 0x400) >> 10)
- | (((timings.VDisplay - 1) & 0x400) >> 9)
- | (((timings.VSyncStart) & 0x400) >> 8)
- | (((timings.VSyncStart) & 0x400) >> 7);
+ par->VerticalExt = (((vtotal - 2) & 0x400) >> 10)
+ | (((info->var.yres - 1) & 0x400) >> 9)
+ | (((vsync_start) & 0x400) >> 8)
+ | (((vsync_start) & 0x400) >> 7);
/* Fast write bursts on unless disabled. */
if (par->pci_burst)
@@ -995,7 +964,7 @@ static int neofb_set_par(struct fb_info *info)
* Calculate the VCLK that most closely matches the requested dot
* clock.
*/
- neoCalcVCLK(info, par, timings.pixclock);
+ neoCalcVCLK(info, par, PICOS2KHZ(info->var.pixclock));
/* Since we program the clocks ourselves, always use VCLK3. */
par->MiscOutReg |= 0x0C;
@@ -1927,9 +1896,6 @@ static int __devinit neo_init_hw(struct fb_info *info)
int maxClock = 65000;
int CursorMem = 1024;
int CursorOff = 0x100;
- int linearSize = 1024;
- int maxWidth = 1024;
- int maxHeight = 1024;
DBG("neo_init_hw");
@@ -1948,81 +1914,52 @@ static int __devinit neo_init_hw(struct fb_info *info)
case FB_ACCEL_NEOMAGIC_NM2070:
videoRam = 896;
maxClock = 65000;
- CursorMem = 2048;
- CursorOff = 0x100;
- linearSize = 1024;
- maxWidth = 1024;
- maxHeight = 1024;
break;
case FB_ACCEL_NEOMAGIC_NM2090:
case FB_ACCEL_NEOMAGIC_NM2093:
- videoRam = 1152;
- maxClock = 80000;
- CursorMem = 2048;
- CursorOff = 0x100;
- linearSize = 2048;
- maxWidth = 1024;
- maxHeight = 1024;
- break;
case FB_ACCEL_NEOMAGIC_NM2097:
videoRam = 1152;
maxClock = 80000;
- CursorMem = 1024;
- CursorOff = 0x100;
- linearSize = 2048;
- maxWidth = 1024;
- maxHeight = 1024;
break;
case FB_ACCEL_NEOMAGIC_NM2160:
videoRam = 2048;
maxClock = 90000;
- CursorMem = 1024;
- CursorOff = 0x100;
- linearSize = 2048;
- maxWidth = 1024;
- maxHeight = 1024;
break;
case FB_ACCEL_NEOMAGIC_NM2200:
videoRam = 2560;
maxClock = 110000;
- CursorMem = 1024;
- CursorOff = 0x1000;
- linearSize = 4096;
- maxWidth = 1280;
- maxHeight = 1024; /* ???? */
-
- par->neo2200 = (Neo2200 __iomem *) par->mmio_vbase;
break;
case FB_ACCEL_NEOMAGIC_NM2230:
videoRam = 3008;
maxClock = 110000;
- CursorMem = 1024;
- CursorOff = 0x1000;
- linearSize = 4096;
- maxWidth = 1280;
- maxHeight = 1024; /* ???? */
-
- par->neo2200 = (Neo2200 __iomem *) par->mmio_vbase;
break;
case FB_ACCEL_NEOMAGIC_NM2360:
videoRam = 4096;
maxClock = 110000;
- CursorMem = 1024;
- CursorOff = 0x1000;
- linearSize = 4096;
- maxWidth = 1280;
- maxHeight = 1024; /* ???? */
-
- par->neo2200 = (Neo2200 __iomem *) par->mmio_vbase;
break;
case FB_ACCEL_NEOMAGIC_NM2380:
videoRam = 6144;
maxClock = 110000;
+ break;
+ }
+ switch (info->fix.accel) {
+ case FB_ACCEL_NEOMAGIC_NM2070:
+ case FB_ACCEL_NEOMAGIC_NM2090:
+ case FB_ACCEL_NEOMAGIC_NM2093:
+ CursorMem = 2048;
+ CursorOff = 0x100;
+ break;
+ case FB_ACCEL_NEOMAGIC_NM2097:
+ case FB_ACCEL_NEOMAGIC_NM2160:
+ CursorMem = 1024;
+ CursorOff = 0x100;
+ break;
+ case FB_ACCEL_NEOMAGIC_NM2200:
+ case FB_ACCEL_NEOMAGIC_NM2230:
+ case FB_ACCEL_NEOMAGIC_NM2360:
+ case FB_ACCEL_NEOMAGIC_NM2380:
CursorMem = 1024;
CursorOff = 0x1000;
- linearSize = 8192;
- maxWidth = 1280;
- maxHeight = 1024; /* ???? */
par->neo2200 = (Neo2200 __iomem *) par->mmio_vbase;
break;
@@ -2036,7 +1973,7 @@ static int __devinit neo_init_hw(struct fb_info *info)
*/
par->maxClock = maxClock;
par->cursorOff = CursorOff;
- return ((videoRam * 1024));
+ return videoRam * 1024;
}
diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c
index ab32ceb0617..ab77c51fe9d 100644
--- a/drivers/video/omap/dispc.c
+++ b/drivers/video/omap/dispc.c
@@ -20,6 +20,7 @@
*/
#include <linux/kernel.h>
#include <linux/dma-mapping.h>
+#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/clk.h>
#include <linux/io.h>
diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c
index 14d0f7a1114..f85af5c4fa6 100644
--- a/drivers/video/omap/omapfb_main.c
+++ b/drivers/video/omap/omapfb_main.c
@@ -25,6 +25,7 @@
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/platform_device.h>
+#include <linux/mm.h>
#include <linux/uaccess.h>
#include <asm/mach-types.h>
diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c
index bb251436950..2b707a8ce5d 100644
--- a/drivers/video/pxafb.c
+++ b/drivers/video/pxafb.c
@@ -30,6 +30,7 @@
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/mm.h>
#include <linux/fb.h>
#include <linux/delay.h>
#include <linux/init.h>
@@ -40,6 +41,7 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/completion.h>
+#include <linux/mutex.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
@@ -1116,7 +1118,7 @@ static void set_ctrlr_state(struct pxafb_info *fbi, u_int state)
{
u_int old_state;
- down(&fbi->ctrlr_sem);
+ mutex_lock(&fbi->ctrlr_lock);
old_state = fbi->state;
@@ -1204,7 +1206,7 @@ static void set_ctrlr_state(struct pxafb_info *fbi, u_int state)
}
break;
}
- up(&fbi->ctrlr_sem);
+ mutex_unlock(&fbi->ctrlr_lock);
}
/*
@@ -1457,7 +1459,7 @@ static struct pxafb_info * __devinit pxafb_init_fbinfo(struct device *dev)
init_waitqueue_head(&fbi->ctrlr_wait);
INIT_WORK(&fbi->task, pxafb_task);
- init_MUTEX(&fbi->ctrlr_sem);
+ mutex_init(&fbi->ctrlr_lock);
init_completion(&fbi->disable_done);
#ifdef CONFIG_FB_PXA_SMARTPANEL
init_completion(&fbi->command_done);
diff --git a/drivers/video/pxafb.h b/drivers/video/pxafb.h
index 8238dc82642..31541b86f13 100644
--- a/drivers/video/pxafb.h
+++ b/drivers/video/pxafb.h
@@ -106,7 +106,7 @@ struct pxafb_info {
volatile u_char state;
volatile u_char task_state;
- struct semaphore ctrlr_sem;
+ struct mutex ctrlr_lock;
wait_queue_head_t ctrlr_wait;
struct work_struct task;
diff --git a/drivers/video/sa1100fb.c b/drivers/video/sa1100fb.c
index ab2b2110478..78bcdbc3f48 100644
--- a/drivers/video/sa1100fb.c
+++ b/drivers/video/sa1100fb.c
@@ -167,6 +167,7 @@
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/mm.h>
#include <linux/fb.h>
#include <linux/delay.h>
#include <linux/init.h>
@@ -174,6 +175,7 @@
#include <linux/cpufreq.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/mutex.h>
#include <asm/hardware.h>
#include <asm/io.h>
@@ -1107,7 +1109,7 @@ static void set_ctrlr_state(struct sa1100fb_info *fbi, u_int state)
{
u_int old_state;
- down(&fbi->ctrlr_sem);
+ mutex_lock(&fbi->ctrlr_lock);
old_state = fbi->state;
@@ -1192,7 +1194,7 @@ static void set_ctrlr_state(struct sa1100fb_info *fbi, u_int state)
}
break;
}
- up(&fbi->ctrlr_sem);
+ mutex_unlock(&fbi->ctrlr_lock);
}
/*
@@ -1444,7 +1446,7 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(struct device *dev)
init_waitqueue_head(&fbi->ctrlr_wait);
INIT_WORK(&fbi->task, sa1100fb_task);
- init_MUTEX(&fbi->ctrlr_sem);
+ mutex_init(&fbi->ctrlr_lock);
return fbi;
}
diff --git a/drivers/video/sa1100fb.h b/drivers/video/sa1100fb.h
index f465b27ed86..86831db9a04 100644
--- a/drivers/video/sa1100fb.h
+++ b/drivers/video/sa1100fb.h
@@ -100,7 +100,7 @@ struct sa1100fb_info {
volatile u_char state;
volatile u_char task_state;
- struct semaphore ctrlr_sem;
+ struct mutex ctrlr_lock;
wait_queue_head_t ctrlr_wait;
struct work_struct task;
diff --git a/drivers/video/sh7760fb.c b/drivers/video/sh7760fb.c
new file mode 100644
index 00000000000..4d0e28c5790
--- /dev/null
+++ b/drivers/video/sh7760fb.c
@@ -0,0 +1,658 @@
+/*
+ * SH7760/SH7763 LCDC Framebuffer driver.
+ *
+ * (c) 2006-2008 MSC Vertriebsges.m.b.H.,
+ * Manuel Lauss <mano@roarinelk.homelinux.net>
+ * (c) 2008 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
+ *
+ * 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.
+ *
+ * PLEASE HAVE A LOOK AT Documentation/fb/sh7760fb.txt!
+ *
+ * Thanks to Siegfried Schaefer <s.schaefer at schaefer-edv.de>
+ * for his original source and testing!
+ */
+
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/fb.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/sh7760fb.h>
+
+struct sh7760fb_par {
+ void __iomem *base;
+ int irq;
+
+ struct sh7760fb_platdata *pd; /* display information */
+
+ dma_addr_t fbdma; /* physical address */
+
+ int rot; /* rotation enabled? */
+
+ u32 pseudo_palette[16];
+
+ struct platform_device *dev;
+ struct resource *ioarea;
+ struct completion vsync; /* vsync irq event */
+};
+
+static irqreturn_t sh7760fb_irq(int irq, void *data)
+{
+ struct completion *c = data;
+
+ complete(c);
+
+ return IRQ_HANDLED;
+}
+
+static void sh7760fb_wait_vsync(struct fb_info *info)
+{
+ struct sh7760fb_par *par = info->par;
+
+ if (par->pd->novsync)
+ return;
+
+ iowrite16(ioread16(par->base + LDINTR) & ~VINT_CHECK,
+ par->base + LDINTR);
+
+ if (par->irq < 0) {
+ /* poll for vert. retrace: status bit is sticky */
+ while (!(ioread16(par->base + LDINTR) & VINT_CHECK))
+ cpu_relax();
+ } else {
+ /* a "wait_for_irq_event(par->irq)" would be extremely nice */
+ init_completion(&par->vsync);
+ enable_irq(par->irq);
+ wait_for_completion(&par->vsync);
+ disable_irq_nosync(par->irq);
+ }
+}
+
+/* wait_for_lps - wait until power supply has reached a certain state. */
+static int wait_for_lps(struct sh7760fb_par *par, int val)
+{
+ int i = 100;
+ while (--i && ((ioread16(par->base + LDPMMR) & 3) != val))
+ msleep(1);
+
+ if (i <= 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+/* en/disable the LCDC */
+static int sh7760fb_blank(int blank, struct fb_info *info)
+{
+ struct sh7760fb_par *par = info->par;
+ struct sh7760fb_platdata *pd = par->pd;
+ unsigned short cntr = ioread16(par->base + LDCNTR);
+ unsigned short intr = ioread16(par->base + LDINTR);
+ int lps;
+
+ if (blank == FB_BLANK_UNBLANK) {
+ intr |= VINT_START;
+ cntr = LDCNTR_DON2 | LDCNTR_DON;
+ lps = 3;
+ } else {
+ intr &= ~VINT_START;
+ cntr = LDCNTR_DON2;
+ lps = 0;
+ }
+
+ if (pd->blank)
+ pd->blank(blank);
+
+ iowrite16(intr, par->base + LDINTR);
+ iowrite16(cntr, par->base + LDCNTR);
+
+ return wait_for_lps(par, lps);
+}
+
+/* set color registers */
+static int sh7760fb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
+{
+ struct sh7760fb_par *par = info->par;
+ u32 s = cmap->start;
+ u32 l = cmap->len;
+ u16 *r = cmap->red;
+ u16 *g = cmap->green;
+ u16 *b = cmap->blue;
+ u32 col, tmo;
+ int ret;
+
+ ret = 0;
+
+ sh7760fb_wait_vsync(info);
+
+ /* request palette access */
+ iowrite16(LDPALCR_PALEN, par->base + LDPALCR);
+
+ /* poll for access grant */
+ tmo = 100;
+ while (!(ioread16(par->base + LDPALCR) & LDPALCR_PALS) && (--tmo))
+ cpu_relax();
+
+ if (!tmo) {
+ ret = 1;
+ dev_dbg(info->dev, "no palette access!\n");
+ goto out;
+ }
+
+ while (l && (s < 256)) {
+ col = ((*r) & 0xff) << 16;
+ col |= ((*g) & 0xff) << 8;
+ col |= ((*b) & 0xff);
+ col &= SH7760FB_PALETTE_MASK;
+
+ if (s < 16)
+ ((u32 *) (info->pseudo_palette))[s] = s;
+
+ s++;
+ l--;
+ r++;
+ g++;
+ b++;
+ }
+out:
+ iowrite16(0, par->base + LDPALCR);
+ return ret;
+}
+
+static void encode_fix(struct fb_fix_screeninfo *fix, struct fb_info *info,
+ unsigned long stride)
+{
+ memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+ strcpy(fix->id, "sh7760-lcdc");
+
+ fix->smem_start = (unsigned long)info->screen_base;
+ fix->smem_len = info->screen_size;
+
+ fix->line_length = stride;
+}
+
+static int sh7760fb_get_color_info(struct device *dev,
+ u16 lddfr, int *bpp, int *gray)
+{
+ int lbpp, lgray;
+
+ lgray = lbpp = 0;
+
+ switch (lddfr & LDDFR_COLOR_MASK) {
+ case LDDFR_1BPP_MONO:
+ lgray = 1;
+ lbpp = 1;
+ break;
+ case LDDFR_2BPP_MONO:
+ lgray = 1;
+ lbpp = 2;
+ break;
+ case LDDFR_4BPP_MONO:
+ lgray = 1;
+ case LDDFR_4BPP:
+ lbpp = 4;
+ break;
+ case LDDFR_6BPP_MONO:
+ lgray = 1;
+ case LDDFR_8BPP:
+ lbpp = 8;
+ break;
+ case LDDFR_16BPP_RGB555:
+ case LDDFR_16BPP_RGB565:
+ lbpp = 16;
+ lgray = 0;
+ break;
+ default:
+ dev_dbg(dev, "unsupported LDDFR bit depth.\n");
+ return -EINVAL;
+ }
+
+ if (bpp)
+ *bpp = lbpp;
+ if (gray)
+ *gray = lgray;
+
+ return 0;
+}
+
+static int sh7760fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct fb_fix_screeninfo *fix = &info->fix;
+ struct sh7760fb_par *par = info->par;
+ int ret, bpp;
+
+ /* get color info from register value */
+ ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, NULL);
+ if (ret)
+ return ret;
+
+ var->bits_per_pixel = bpp;
+
+ if ((var->grayscale) && (var->bits_per_pixel == 1))
+ fix->visual = FB_VISUAL_MONO10;
+ else if (var->bits_per_pixel >= 15)
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ else
+ fix->visual = FB_VISUAL_PSEUDOCOLOR;
+
+ /* TODO: add some more validation here */
+ return 0;
+}
+
+/*
+ * sh7760fb_set_par - set videomode.
+ *
+ * NOTE: The rotation, grayscale and DSTN codepaths are
+ * totally untested!
+ */
+static int sh7760fb_set_par(struct fb_info *info)
+{
+ struct sh7760fb_par *par = info->par;
+ struct fb_videomode *vm = par->pd->def_mode;
+ unsigned long sbase, dstn_off, ldsarl, stride;
+ unsigned short hsynp, hsynw, htcn, hdcn;
+ unsigned short vsynp, vsynw, vtln, vdln;
+ unsigned short lddfr, ldmtr;
+ int ret, bpp, gray;
+
+ par->rot = par->pd->rotate;
+
+ /* rotate only works with xres <= 320 */
+ if (par->rot && (vm->xres > 320)) {
+ dev_dbg(info->dev, "rotation disabled due to display size\n");
+ par->rot = 0;
+ }
+
+ /* calculate LCDC reg vals from display parameters */
+ hsynp = vm->right_margin + vm->xres;
+ hsynw = vm->hsync_len;
+ htcn = vm->left_margin + hsynp + hsynw;
+ hdcn = vm->xres;
+ vsynp = vm->lower_margin + vm->yres;
+ vsynw = vm->vsync_len;
+ vtln = vm->upper_margin + vsynp + vsynw;
+ vdln = vm->yres;
+
+ /* get color info from register value */
+ ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, &gray);
+ if (ret)
+ return ret;
+
+ dev_dbg(info->dev, "%dx%d %dbpp %s (orientation %s)\n", hdcn,
+ vdln, bpp, gray ? "grayscale" : "color",
+ par->rot ? "rotated" : "normal");
+
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+ lddfr = par->pd->lddfr | (1 << 8);
+#else
+ lddfr = par->pd->lddfr & ~(1 << 8);
+#endif
+
+ ldmtr = par->pd->ldmtr;
+
+ if (!(vm->sync & FB_SYNC_HOR_HIGH_ACT))
+ ldmtr |= LDMTR_CL1POL;
+ if (!(vm->sync & FB_SYNC_VERT_HIGH_ACT))
+ ldmtr |= LDMTR_FLMPOL;
+
+ /* shut down LCDC before changing display parameters */
+ sh7760fb_blank(FB_BLANK_POWERDOWN, info);
+
+ iowrite16(par->pd->ldickr, par->base + LDICKR); /* pixclock */
+ iowrite16(ldmtr, par->base + LDMTR); /* polarities */
+ iowrite16(lddfr, par->base + LDDFR); /* color/depth */
+ iowrite16((par->rot ? 1 << 13 : 0), par->base + LDSMR); /* rotate */
+ iowrite16(par->pd->ldpmmr, par->base + LDPMMR); /* Power Management */
+ iowrite16(par->pd->ldpspr, par->base + LDPSPR); /* Power Supply Ctrl */
+
+ /* display resolution */
+ iowrite16(((htcn >> 3) - 1) | (((hdcn >> 3) - 1) << 8),
+ par->base + LDHCNR);
+ iowrite16(vdln - 1, par->base + LDVDLNR);
+ iowrite16(vtln - 1, par->base + LDVTLNR);
+ /* h/v sync signals */
+ iowrite16((vsynp - 1) | ((vsynw - 1) << 12), par->base + LDVSYNR);
+ iowrite16(((hsynp >> 3) - 1) | (((hsynw >> 3) - 1) << 12),
+ par->base + LDHSYNR);
+ /* AC modulation sig */
+ iowrite16(par->pd->ldaclnr, par->base + LDACLNR);
+
+ stride = (par->rot) ? vtln : hdcn;
+ if (!gray)
+ stride *= (bpp + 7) >> 3;
+ else {
+ if (bpp == 1)
+ stride >>= 3;
+ else if (bpp == 2)
+ stride >>= 2;
+ else if (bpp == 4)
+ stride >>= 1;
+ /* 6 bpp == 8 bpp */
+ }
+
+ /* if rotated, stride must be power of 2 */
+ if (par->rot) {
+ unsigned long bit = 1 << 31;
+ while (bit) {
+ if (stride & bit)
+ break;
+ bit >>= 1;
+ }
+ if (stride & ~bit)
+ stride = bit << 1; /* not P-o-2, round up */
+ }
+ iowrite16(stride, par->base + LDLAOR);
+
+ /* set display mem start address */
+ sbase = (unsigned long)par->fbdma;
+ if (par->rot)
+ sbase += (hdcn - 1) * stride;
+
+ iowrite32(sbase, par->base + LDSARU);
+
+ /*
+ * for DSTN need to set address for lower half.
+ * I (mlau) don't know which address to set it to,
+ * so I guessed at (stride * yres/2).
+ */
+ if (((ldmtr & 0x003f) >= LDMTR_DSTN_MONO_8) &&
+ ((ldmtr & 0x003f) <= LDMTR_DSTN_COLOR_16)) {
+
+ dev_dbg(info->dev, " ***** DSTN untested! *****\n");
+
+ dstn_off = stride;
+ if (par->rot)
+ dstn_off *= hdcn >> 1;
+ else
+ dstn_off *= vdln >> 1;
+
+ ldsarl = sbase + dstn_off;
+ } else
+ ldsarl = 0;
+
+ iowrite32(ldsarl, par->base + LDSARL); /* mem for lower half of DSTN */
+
+ encode_fix(&info->fix, info, stride);
+ sh7760fb_check_var(&info->var, info);
+
+ sh7760fb_blank(FB_BLANK_UNBLANK, info); /* panel on! */
+
+ dev_dbg(info->dev, "hdcn : %6d htcn : %6d\n", hdcn, htcn);
+ dev_dbg(info->dev, "hsynw : %6d hsynp : %6d\n", hsynw, hsynp);
+ dev_dbg(info->dev, "vdln : %6d vtln : %6d\n", vdln, vtln);
+ dev_dbg(info->dev, "vsynw : %6d vsynp : %6d\n", vsynw, vsynp);
+ dev_dbg(info->dev, "clksrc: %6d clkdiv: %6d\n",
+ (par->pd->ldickr >> 12) & 3, par->pd->ldickr & 0x1f);
+ dev_dbg(info->dev, "ldpmmr: 0x%04x ldpspr: 0x%04x\n", par->pd->ldpmmr,
+ par->pd->ldpspr);
+ dev_dbg(info->dev, "ldmtr : 0x%04x lddfr : 0x%04x\n", ldmtr, lddfr);
+ dev_dbg(info->dev, "ldlaor: %ld\n", stride);
+ dev_dbg(info->dev, "ldsaru: 0x%08lx ldsarl: 0x%08lx\n", sbase, ldsarl);
+
+ return 0;
+}
+
+static struct fb_ops sh7760fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_blank = sh7760fb_blank,
+ .fb_check_var = sh7760fb_check_var,
+ .fb_setcmap = sh7760fb_setcmap,
+ .fb_set_par = sh7760fb_set_par,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+static void sh7760fb_free_mem(struct fb_info *info)
+{
+ struct sh7760fb_par *par = info->par;
+
+ if (!info->screen_base)
+ return;
+
+ dma_free_coherent(info->dev, info->screen_size,
+ info->screen_base, par->fbdma);
+
+ par->fbdma = 0;
+ info->screen_base = NULL;
+ info->screen_size = 0;
+}
+
+/* allocate the framebuffer memory. This memory must be in Area3,
+ * (dictated by the DMA engine) and contiguous, at a 512 byte boundary.
+ */
+static int sh7760fb_alloc_mem(struct fb_info *info)
+{
+ struct sh7760fb_par *par = info->par;
+ void *fbmem;
+ unsigned long vram;
+ int ret, bpp;
+
+ if (info->screen_base)
+ return 0;
+
+ /* get color info from register value */
+ ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, NULL);
+ if (ret) {
+ printk(KERN_ERR "colinfo\n");
+ return ret;
+ }
+
+ /* min VRAM: xres_min = 16, yres_min = 1, bpp = 1: 2byte -> 1 page
+ max VRAM: xres_max = 1024, yres_max = 1024, bpp = 16: 2MB */
+
+ vram = info->var.xres * info->var.yres;
+ if (info->var.grayscale) {
+ if (bpp == 1)
+ vram >>= 3;
+ else if (bpp == 2)
+ vram >>= 2;
+ else if (bpp == 4)
+ vram >>= 1;
+ } else if (bpp > 8)
+ vram *= 2;
+ if ((vram < 1) || (vram > 1024 * 2048)) {
+ dev_dbg(info->dev, "too much VRAM required. Check settings\n");
+ return -ENODEV;
+ }
+
+ if (vram < PAGE_SIZE)
+ vram = PAGE_SIZE;
+
+ fbmem = dma_alloc_coherent(info->dev, vram, &par->fbdma, GFP_KERNEL);
+
+ if (!fbmem)
+ return -ENOMEM;
+
+ if ((par->fbdma & SH7760FB_DMA_MASK) != SH7760FB_DMA_MASK) {
+ sh7760fb_free_mem(info);
+ dev_err(info->dev, "kernel gave me memory at 0x%08lx, which is"
+ "unusable for the LCDC\n", (unsigned long)par->fbdma);
+ return -ENOMEM;
+ }
+
+ info->screen_base = fbmem;
+ info->screen_size = vram;
+
+ return 0;
+}
+
+static int __devinit sh7760fb_probe(struct platform_device *pdev)
+{
+ struct fb_info *info;
+ struct resource *res;
+ struct sh7760fb_par *par;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (unlikely(res == NULL)) {
+ dev_err(&pdev->dev, "invalid resource\n");
+ return -EINVAL;
+ }
+
+ info = framebuffer_alloc(sizeof(struct sh7760fb_par), &pdev->dev);
+ if (!info)
+ return -ENOMEM;
+
+ par = info->par;
+ par->dev = pdev;
+
+ par->pd = pdev->dev.platform_data;
+ if (!par->pd) {
+ dev_dbg(info->dev, "no display setup data!\n");
+ ret = -ENODEV;
+ goto out_fb;
+ }
+
+ par->ioarea = request_mem_region(res->start,
+ (res->end - res->start), pdev->name);
+ if (!par->ioarea) {
+ dev_err(&pdev->dev, "mmio area busy\n");
+ ret = -EBUSY;
+ goto out_fb;
+ }
+
+ par->base = ioremap_nocache(res->start, res->end - res->start + 1);
+ if (!par->base) {
+ dev_err(&pdev->dev, "cannot remap\n");
+ ret = -ENODEV;
+ goto out_res;
+ }
+
+ iowrite16(0, par->base + LDINTR); /* disable vsync irq */
+ par->irq = platform_get_irq(pdev, 0);
+ if (par->irq >= 0) {
+ ret = request_irq(par->irq, sh7760fb_irq, 0,
+ "sh7760-lcdc", &par->vsync);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot grab IRQ\n");
+ par->irq = -ENXIO;
+ } else
+ disable_irq_nosync(par->irq);
+ }
+
+ fb_videomode_to_var(&info->var, par->pd->def_mode);
+
+ ret = sh7760fb_alloc_mem(info);
+ if (ret) {
+ dev_dbg(info->dev, "framebuffer memory allocation failed!\n");
+ goto out_unmap;
+ }
+
+ info->pseudo_palette = par->pseudo_palette;
+
+ /* fixup color register bitpositions. These are fixed by hardware */
+ info->var.red.offset = 11;
+ info->var.red.length = 5;
+ info->var.red.msb_right = 0;
+
+ info->var.green.offset = 5;
+ info->var.green.length = 6;
+ info->var.green.msb_right = 0;
+
+ info->var.blue.offset = 0;
+ info->var.blue.length = 5;
+ info->var.blue.msb_right = 0;
+
+ info->var.transp.offset = 0;
+ info->var.transp.length = 0;
+ info->var.transp.msb_right = 0;
+
+ /* set the DON2 bit now, before cmap allocation, as it will randomize
+ * palette memory.
+ */
+ iowrite16(LDCNTR_DON2, par->base + LDCNTR);
+ info->fbops = &sh7760fb_ops;
+
+ ret = fb_alloc_cmap(&info->cmap, 256, 0);
+ if (ret) {
+ dev_dbg(info->dev, "Unable to allocate cmap memory\n");
+ goto out_mem;
+ }
+
+ ret = register_framebuffer(info);
+ if (ret < 0) {
+ dev_dbg(info->dev, "cannot register fb!\n");
+ goto out_cmap;
+ }
+ platform_set_drvdata(pdev, info);
+
+ printk(KERN_INFO "%s: memory at phys 0x%08lx-0x%08lx, size %ld KiB\n",
+ pdev->name,
+ (unsigned long)par->fbdma,
+ (unsigned long)(par->fbdma + info->screen_size - 1),
+ info->screen_size >> 10);
+
+ return 0;
+
+out_cmap:
+ sh7760fb_blank(FB_BLANK_POWERDOWN, info);
+ fb_dealloc_cmap(&info->cmap);
+out_mem:
+ sh7760fb_free_mem(info);
+out_unmap:
+ if (par->irq >= 0)
+ free_irq(par->irq, &par->vsync);
+ iounmap(par->base);
+out_res:
+ release_resource(par->ioarea);
+ kfree(par->ioarea);
+out_fb:
+ framebuffer_release(info);
+ return ret;
+}
+
+static int __devexit sh7760fb_remove(struct platform_device *dev)
+{
+ struct fb_info *info = platform_get_drvdata(dev);
+ struct sh7760fb_par *par = info->par;
+
+ sh7760fb_blank(FB_BLANK_POWERDOWN, info);
+ unregister_framebuffer(info);
+ fb_dealloc_cmap(&info->cmap);
+ sh7760fb_free_mem(info);
+ if (par->irq >= 0)
+ free_irq(par->irq, par);
+ iounmap(par->base);
+ release_resource(par->ioarea);
+ kfree(par->ioarea);
+ framebuffer_release(info);
+ platform_set_drvdata(dev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver sh7760_lcdc_driver = {
+ .driver = {
+ .name = "sh7760-lcdc",
+ .owner = THIS_MODULE,
+ },
+ .probe = sh7760fb_probe,
+ .remove = __devexit_p(sh7760fb_remove),
+};
+
+static int __init sh7760fb_init(void)
+{
+ return platform_driver_register(&sh7760_lcdc_driver);
+}
+
+static void __exit sh7760fb_exit(void)
+{
+ platform_driver_unregister(&sh7760_lcdc_driver);
+}
+
+module_init(sh7760fb_init);
+module_exit(sh7760fb_exit);
+
+MODULE_AUTHOR("Nobuhiro Iwamatsu, Manuel Lauss");
+MODULE_DESCRIPTION("FBdev for SH7760/63 integrated LCD Controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
new file mode 100644
index 00000000000..f6ef6cca73c
--- /dev/null
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -0,0 +1,725 @@
+/*
+ * SuperH Mobile LCDC Framebuffer
+ *
+ * Copyright (c) 2008 Magnus Damm
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <asm/sh_mobile_lcdc.h>
+
+#define PALETTE_NR 16
+
+struct sh_mobile_lcdc_priv;
+struct sh_mobile_lcdc_chan {
+ struct sh_mobile_lcdc_priv *lcdc;
+ unsigned long *reg_offs;
+ unsigned long ldmt1r_value;
+ unsigned long enabled; /* ME and SE in LDCNT2R */
+ struct sh_mobile_lcdc_chan_cfg cfg;
+ u32 pseudo_palette[PALETTE_NR];
+ struct fb_info info;
+ dma_addr_t dma_handle;
+};
+
+struct sh_mobile_lcdc_priv {
+ void __iomem *base;
+ struct clk *clk;
+ unsigned long lddckr;
+ struct sh_mobile_lcdc_chan ch[2];
+};
+
+/* shared registers */
+#define _LDDCKR 0x410
+#define _LDDCKSTPR 0x414
+#define _LDINTR 0x468
+#define _LDSR 0x46c
+#define _LDCNT1R 0x470
+#define _LDCNT2R 0x474
+#define _LDDDSR 0x47c
+#define _LDDWD0R 0x800
+#define _LDDRDR 0x840
+#define _LDDWAR 0x900
+#define _LDDRAR 0x904
+
+/* per-channel registers */
+enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R,
+ LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR };
+
+static unsigned long lcdc_offs_mainlcd[] = {
+ [LDDCKPAT1R] = 0x400,
+ [LDDCKPAT2R] = 0x404,
+ [LDMT1R] = 0x418,
+ [LDMT2R] = 0x41c,
+ [LDMT3R] = 0x420,
+ [LDDFR] = 0x424,
+ [LDSM1R] = 0x428,
+ [LDSA1R] = 0x430,
+ [LDMLSR] = 0x438,
+ [LDHCNR] = 0x448,
+ [LDHSYNR] = 0x44c,
+ [LDVLNR] = 0x450,
+ [LDVSYNR] = 0x454,
+ [LDPMR] = 0x460,
+};
+
+static unsigned long lcdc_offs_sublcd[] = {
+ [LDDCKPAT1R] = 0x408,
+ [LDDCKPAT2R] = 0x40c,
+ [LDMT1R] = 0x600,
+ [LDMT2R] = 0x604,
+ [LDMT3R] = 0x608,
+ [LDDFR] = 0x60c,
+ [LDSM1R] = 0x610,
+ [LDSA1R] = 0x618,
+ [LDMLSR] = 0x620,
+ [LDHCNR] = 0x624,
+ [LDHSYNR] = 0x628,
+ [LDVLNR] = 0x62c,
+ [LDVSYNR] = 0x630,
+ [LDPMR] = 0x63c,
+};
+
+#define START_LCDC 0x00000001
+#define LCDC_RESET 0x00000100
+#define DISPLAY_BEU 0x00000008
+#define LCDC_ENABLE 0x00000001
+
+static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan,
+ int reg_nr, unsigned long data)
+{
+ iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr]);
+}
+
+static unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan,
+ int reg_nr)
+{
+ return ioread32(chan->lcdc->base + chan->reg_offs[reg_nr]);
+}
+
+static void lcdc_write(struct sh_mobile_lcdc_priv *priv,
+ unsigned long reg_offs, unsigned long data)
+{
+ iowrite32(data, priv->base + reg_offs);
+}
+
+static unsigned long lcdc_read(struct sh_mobile_lcdc_priv *priv,
+ unsigned long reg_offs)
+{
+ return ioread32(priv->base + reg_offs);
+}
+
+static void lcdc_wait_bit(struct sh_mobile_lcdc_priv *priv,
+ unsigned long reg_offs,
+ unsigned long mask, unsigned long until)
+{
+ while ((lcdc_read(priv, reg_offs) & mask) != until)
+ cpu_relax();
+}
+
+static int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan)
+{
+ return chan->cfg.chan == LCDC_CHAN_SUBLCD;
+}
+
+static void lcdc_sys_write_index(void *handle, unsigned long data)
+{
+ struct sh_mobile_lcdc_chan *ch = handle;
+
+ lcdc_write(ch->lcdc, _LDDWD0R, data | 0x10000000);
+ lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
+ lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
+}
+
+static void lcdc_sys_write_data(void *handle, unsigned long data)
+{
+ struct sh_mobile_lcdc_chan *ch = handle;
+
+ lcdc_write(ch->lcdc, _LDDWD0R, data | 0x11000000);
+ lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
+ lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
+}
+
+static unsigned long lcdc_sys_read_data(void *handle)
+{
+ struct sh_mobile_lcdc_chan *ch = handle;
+
+ lcdc_write(ch->lcdc, _LDDRDR, 0x01000000);
+ lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
+ lcdc_write(ch->lcdc, _LDDRAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
+ udelay(1);
+
+ return lcdc_read(ch->lcdc, _LDDRDR) & 0xffff;
+}
+
+struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
+ lcdc_sys_write_index,
+ lcdc_sys_write_data,
+ lcdc_sys_read_data,
+};
+
+static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
+ int start)
+{
+ unsigned long tmp = lcdc_read(priv, _LDCNT2R);
+ int k;
+
+ /* start or stop the lcdc */
+ if (start)
+ lcdc_write(priv, _LDCNT2R, tmp | START_LCDC);
+ else
+ lcdc_write(priv, _LDCNT2R, tmp & ~START_LCDC);
+
+ /* wait until power is applied/stopped on all channels */
+ for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
+ if (lcdc_read(priv, _LDCNT2R) & priv->ch[k].enabled)
+ while (1) {
+ tmp = lcdc_read_chan(&priv->ch[k], LDPMR) & 3;
+ if (start && tmp == 3)
+ break;
+ if (!start && tmp == 0)
+ break;
+ cpu_relax();
+ }
+
+ if (!start)
+ lcdc_write(priv, _LDDCKSTPR, 1); /* stop dotclock */
+}
+
+static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
+{
+ struct sh_mobile_lcdc_chan *ch;
+ struct fb_videomode *lcd_cfg;
+ struct sh_mobile_lcdc_board_cfg *board_cfg;
+ unsigned long tmp;
+ int k, m;
+ int ret = 0;
+
+ /* reset */
+ lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET);
+ lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0);
+
+ /* enable LCDC channels */
+ tmp = lcdc_read(priv, _LDCNT2R);
+ tmp |= priv->ch[0].enabled;
+ tmp |= priv->ch[1].enabled;
+ lcdc_write(priv, _LDCNT2R, tmp);
+
+ /* read data from external memory, avoid using the BEU for now */
+ lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~DISPLAY_BEU);
+
+ /* stop the lcdc first */
+ sh_mobile_lcdc_start_stop(priv, 0);
+
+ /* configure clocks */
+ tmp = priv->lddckr;
+ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
+ ch = &priv->ch[k];
+
+ if (!priv->ch[k].enabled)
+ continue;
+
+ m = ch->cfg.clock_divider;
+ if (!m)
+ continue;
+
+ if (m == 1)
+ m = 1 << 6;
+ tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0);
+
+ lcdc_write_chan(ch, LDDCKPAT1R, 0x00000000);
+ lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1);
+ }
+
+ lcdc_write(priv, _LDDCKR, tmp);
+
+ /* start dotclock again */
+ lcdc_write(priv, _LDDCKSTPR, 0);
+ lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);
+
+ /* interrupts are disabled */
+ lcdc_write(priv, _LDINTR, 0);
+
+ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
+ ch = &priv->ch[k];
+ lcd_cfg = &ch->cfg.lcd_cfg;
+
+ 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;
+ 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);
+
+ /* 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,
+ &sh_mobile_lcdc_sys_bus_ops);
+ if (ret)
+ return ret;
+ }
+
+ /* --- display_lcdc_data() --- */
+ lcdc_write(priv, _LDINTR, 0x00000f00);
+
+ /* word and long word swap */
+ lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6);
+
+ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
+ ch = &priv->ch[k];
+
+ if (!priv->ch[k].enabled)
+ continue;
+
+ /* set bpp format in PKF[4:0] */
+ tmp = lcdc_read_chan(ch, LDDFR);
+ tmp &= ~(0x0001001f);
+ tmp |= (priv->ch[k].info.var.bits_per_pixel == 16) ? 3 : 0;
+ lcdc_write_chan(ch, LDDFR, tmp);
+
+ /* point out our frame buffer */
+ lcdc_write_chan(ch, LDSA1R, ch->info.fix.smem_start);
+
+ /* set line size */
+ lcdc_write_chan(ch, LDMLSR, ch->info.fix.line_length);
+
+ /* continuous read mode */
+ lcdc_write_chan(ch, LDSM1R, 0);
+ }
+
+ /* display output */
+ lcdc_write(priv, _LDCNT1R, LCDC_ENABLE);
+
+ /* start the lcdc */
+ sh_mobile_lcdc_start_stop(priv, 1);
+
+ /* tell the board code to enable the panel */
+ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
+ ch = &priv->ch[k];
+ board_cfg = &ch->cfg.board_cfg;
+ if (board_cfg->display_on)
+ board_cfg->display_on(board_cfg->board_data);
+ }
+
+ return 0;
+}
+
+static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
+{
+ struct sh_mobile_lcdc_chan *ch;
+ struct sh_mobile_lcdc_board_cfg *board_cfg;
+ int k;
+
+ /* tell the board code to disable the panel */
+ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
+ ch = &priv->ch[k];
+ board_cfg = &ch->cfg.board_cfg;
+ if (board_cfg->display_off)
+ board_cfg->display_off(board_cfg->board_data);
+ }
+
+ /* stop the lcdc */
+ sh_mobile_lcdc_start_stop(priv, 0);
+}
+
+static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
+{
+ int ifm, miftyp;
+
+ switch (ch->cfg.interface_type) {
+ case RGB8: ifm = 0; miftyp = 0; break;
+ case RGB9: ifm = 0; miftyp = 4; break;
+ case RGB12A: ifm = 0; miftyp = 5; break;
+ case RGB12B: ifm = 0; miftyp = 6; break;
+ case RGB16: ifm = 0; miftyp = 7; break;
+ case RGB18: ifm = 0; miftyp = 10; break;
+ case RGB24: ifm = 0; miftyp = 11; break;
+ case SYS8A: ifm = 1; miftyp = 0; break;
+ case SYS8B: ifm = 1; miftyp = 1; break;
+ case SYS8C: ifm = 1; miftyp = 2; break;
+ case SYS8D: ifm = 1; miftyp = 3; break;
+ case SYS9: ifm = 1; miftyp = 4; break;
+ case SYS12: ifm = 1; miftyp = 5; break;
+ case SYS16A: ifm = 1; miftyp = 7; break;
+ case SYS16B: ifm = 1; miftyp = 8; break;
+ case SYS16C: ifm = 1; miftyp = 9; break;
+ case SYS18: ifm = 1; miftyp = 10; break;
+ case SYS24: ifm = 1; miftyp = 11; break;
+ default: goto bad;
+ }
+
+ /* SUBLCD only supports SYS interface */
+ if (lcdc_chan_is_sublcd(ch)) {
+ if (ifm == 0)
+ goto bad;
+ else
+ ifm = 0;
+ }
+
+ ch->ldmt1r_value = (ifm << 12) | miftyp;
+ return 0;
+ bad:
+ return -EINVAL;
+}
+
+static int sh_mobile_lcdc_setup_clocks(struct device *dev, int clock_source,
+ struct sh_mobile_lcdc_priv *priv)
+{
+ char *str;
+ int icksel;
+
+ switch (clock_source) {
+ case LCDC_CLK_BUS: str = "bus_clk"; icksel = 0; break;
+ case LCDC_CLK_PERIPHERAL: str = "peripheral_clk"; icksel = 1; break;
+ case LCDC_CLK_EXTERNAL: str = NULL; icksel = 2; break;
+ default:
+ return -EINVAL;
+ }
+
+ priv->lddckr = icksel << 16;
+
+ if (str) {
+ priv->clk = clk_get(dev, str);
+ if (IS_ERR(priv->clk)) {
+ dev_err(dev, "cannot get clock %s\n", str);
+ return PTR_ERR(priv->clk);
+ }
+
+ clk_enable(priv->clk);
+ }
+
+ return 0;
+}
+
+static int sh_mobile_lcdc_setcolreg(u_int regno,
+ u_int red, u_int green, u_int blue,
+ u_int transp, struct fb_info *info)
+{
+ u32 *palette = info->pseudo_palette;
+
+ if (regno >= PALETTE_NR)
+ return -EINVAL;
+
+ /* only FB_VISUAL_TRUECOLOR supported */
+
+ red >>= 16 - info->var.red.length;
+ green >>= 16 - info->var.green.length;
+ blue >>= 16 - info->var.blue.length;
+ transp >>= 16 - info->var.transp.length;
+
+ palette[regno] = (red << info->var.red.offset) |
+ (green << info->var.green.offset) |
+ (blue << info->var.blue.offset) |
+ (transp << info->var.transp.offset);
+
+ return 0;
+}
+
+static struct fb_fix_screeninfo sh_mobile_lcdc_fix = {
+ .id = "SH Mobile LCDC",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_TRUECOLOR,
+ .accel = FB_ACCEL_NONE,
+};
+
+static struct fb_ops sh_mobile_lcdc_ops = {
+ .fb_setcolreg = sh_mobile_lcdc_setcolreg,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp)
+{
+ switch (bpp) {
+ case 16: /* PKF[4:0] = 00011 - RGB 565 */
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+
+ case 32: /* PKF[4:0] = 00000 - RGB 888
+ * sh7722 pdf says 00RRGGBB but reality is GGBB00RR
+ * this may be because LDDDSR has word swap enabled..
+ */
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->green.offset = 24;
+ var->green.length = 8;
+ var->blue.offset = 16;
+ var->blue.length = 8;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ var->bits_per_pixel = bpp;
+ var->red.msb_right = 0;
+ var->green.msb_right = 0;
+ var->blue.msb_right = 0;
+ var->transp.msb_right = 0;
+ return 0;
+}
+
+static int sh_mobile_lcdc_remove(struct platform_device *pdev);
+
+static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
+{
+ struct fb_info *info;
+ struct sh_mobile_lcdc_priv *priv;
+ struct sh_mobile_lcdc_info *pdata;
+ struct sh_mobile_lcdc_chan_cfg *cfg;
+ struct resource *res;
+ int error;
+ void *buf;
+ int i, j;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "no platform data defined\n");
+ error = -EINVAL;
+ goto err0;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "cannot find IO resource\n");
+ error = -ENOENT;
+ goto err0;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&pdev->dev, "cannot allocate device data\n");
+ error = -ENOMEM;
+ goto err0;
+ }
+
+ platform_set_drvdata(pdev, priv);
+ pdata = pdev->dev.platform_data;
+
+ j = 0;
+ for (i = 0; i < ARRAY_SIZE(pdata->ch); i++) {
+ priv->ch[j].lcdc = priv;
+ memcpy(&priv->ch[j].cfg, &pdata->ch[i], sizeof(pdata->ch[i]));
+
+ error = sh_mobile_lcdc_check_interface(&priv->ch[i]);
+ if (error) {
+ dev_err(&pdev->dev, "unsupported interface type\n");
+ goto err1;
+ }
+
+ switch (pdata->ch[i].chan) {
+ case LCDC_CHAN_MAINLCD:
+ priv->ch[j].enabled = 1 << 1;
+ priv->ch[j].reg_offs = lcdc_offs_mainlcd;
+ j++;
+ break;
+ case LCDC_CHAN_SUBLCD:
+ priv->ch[j].enabled = 1 << 2;
+ priv->ch[j].reg_offs = lcdc_offs_sublcd;
+ j++;
+ break;
+ }
+ }
+
+ if (!j) {
+ dev_err(&pdev->dev, "no channels defined\n");
+ error = -EINVAL;
+ goto err1;
+ }
+
+ error = sh_mobile_lcdc_setup_clocks(&pdev->dev,
+ pdata->clock_source, priv);
+ if (error) {
+ dev_err(&pdev->dev, "unable to setup clocks\n");
+ goto err1;
+ }
+
+ priv->lddckr = pdata->lddckr;
+ priv->base = ioremap_nocache(res->start, (res->end - res->start) + 1);
+
+ for (i = 0; i < j; i++) {
+ info = &priv->ch[i].info;
+ cfg = &priv->ch[i].cfg;
+
+ info->fbops = &sh_mobile_lcdc_ops;
+ info->var.xres = info->var.xres_virtual = cfg->lcd_cfg.xres;
+ info->var.yres = info->var.yres_virtual = cfg->lcd_cfg.yres;
+ info->var.activate = FB_ACTIVATE_NOW;
+ error = sh_mobile_lcdc_set_bpp(&info->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.smem_len = info->fix.line_length * cfg->lcd_cfg.yres;
+
+ buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len,
+ &priv->ch[i].dma_handle, GFP_KERNEL);
+ if (!buf) {
+ dev_err(&pdev->dev, "unable to allocate buffer\n");
+ error = -ENOMEM;
+ break;
+ }
+
+ info->pseudo_palette = &priv->ch[i].pseudo_palette;
+ info->flags = FBINFO_FLAG_DEFAULT;
+
+ error = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0);
+ if (error < 0) {
+ dev_err(&pdev->dev, "unable to allocate cmap\n");
+ dma_free_coherent(&pdev->dev, info->fix.smem_len,
+ buf, priv->ch[i].dma_handle);
+ break;
+ }
+
+ memset(buf, 0, info->fix.smem_len);
+ info->fix.smem_start = priv->ch[i].dma_handle;
+ info->screen_base = buf;
+ info->device = &pdev->dev;
+ }
+
+ if (error)
+ goto err1;
+
+ error = sh_mobile_lcdc_start(priv);
+ if (error) {
+ dev_err(&pdev->dev, "unable to start hardware\n");
+ goto err1;
+ }
+
+ for (i = 0; i < j; i++) {
+ error = register_framebuffer(&priv->ch[i].info);
+ if (error < 0)
+ goto err1;
+ }
+
+ for (i = 0; i < j; i++) {
+ info = &priv->ch[i].info;
+ dev_info(info->dev,
+ "registered %s/%s as %dx%d %dbpp.\n",
+ pdev->name,
+ (priv->ch[i].cfg.chan == LCDC_CHAN_MAINLCD) ?
+ "mainlcd" : "sublcd",
+ (int) priv->ch[i].cfg.lcd_cfg.xres,
+ (int) priv->ch[i].cfg.lcd_cfg.yres,
+ priv->ch[i].cfg.bpp);
+ }
+
+ return 0;
+ err1:
+ sh_mobile_lcdc_remove(pdev);
+ err0:
+ return error;
+}
+
+static int sh_mobile_lcdc_remove(struct platform_device *pdev)
+{
+ struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
+ struct fb_info *info;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(priv->ch); i++)
+ if (priv->ch[i].info.dev)
+ unregister_framebuffer(&priv->ch[i].info);
+
+ sh_mobile_lcdc_stop(priv);
+
+ for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
+ info = &priv->ch[i].info;
+
+ if (!info->device)
+ continue;
+
+ dma_free_coherent(&pdev->dev, info->fix.smem_len,
+ info->screen_base, priv->ch[i].dma_handle);
+ fb_dealloc_cmap(&info->cmap);
+ }
+
+ if (priv->clk) {
+ clk_disable(priv->clk);
+ clk_put(priv->clk);
+ }
+
+ if (priv->base)
+ iounmap(priv->base);
+
+ kfree(priv);
+ return 0;
+}
+
+static struct platform_driver sh_mobile_lcdc_driver = {
+ .driver = {
+ .name = "sh_mobile_lcdc_fb",
+ .owner = THIS_MODULE,
+ },
+ .probe = sh_mobile_lcdc_probe,
+ .remove = sh_mobile_lcdc_remove,
+};
+
+static int __init sh_mobile_lcdc_init(void)
+{
+ return platform_driver_register(&sh_mobile_lcdc_driver);
+}
+
+static void __exit sh_mobile_lcdc_exit(void)
+{
+ platform_driver_unregister(&sh_mobile_lcdc_driver);
+}
+
+module_init(sh_mobile_lcdc_init);
+module_exit(sh_mobile_lcdc_exit);
+
+MODULE_DESCRIPTION("SuperH Mobile LCDC Framebuffer driver");
+MODULE_AUTHOR("Magnus Damm <damm@opensource.se>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/sis/init.h b/drivers/video/sis/init.h
index f40a680df86..b96005c39c6 100644
--- a/drivers/video/sis/init.h
+++ b/drivers/video/sis/init.h
@@ -73,7 +73,6 @@
#ifdef SIS_CP
#undef SIS_CP
#endif
-#include <linux/version.h>
#include <linux/types.h>
#include <asm/io.h>
#include <linux/fb.h>
diff --git a/drivers/video/sis/init301.h b/drivers/video/sis/init301.h
index 7708e1e1d99..51d99222375 100644
--- a/drivers/video/sis/init301.h
+++ b/drivers/video/sis/init301.h
@@ -67,7 +67,6 @@
#ifdef SIS_CP
#undef SIS_CP
#endif
-#include <linux/version.h>
#include <linux/types.h>
#include <asm/io.h>
#include <linux/fb.h>
diff --git a/drivers/video/sis/initextlfb.c b/drivers/video/sis/initextlfb.c
index 47a33501549..99c04a4855d 100644
--- a/drivers/video/sis/initextlfb.c
+++ b/drivers/video/sis/initextlfb.c
@@ -30,7 +30,6 @@
#include "vgatypes.h"
#include "vstruct.h"
-#include <linux/version.h>
#include <linux/types.h>
#include <linux/fb.h>
diff --git a/drivers/video/sis/osdef.h b/drivers/video/sis/osdef.h
index c1492782cb1..6ff8f988a1a 100644
--- a/drivers/video/sis/osdef.h
+++ b/drivers/video/sis/osdef.h
@@ -87,7 +87,6 @@
/**********************************************************************/
#ifdef SIS_LINUX_KERNEL
-#include <linux/version.h>
#ifdef CONFIG_FB_SIS_300
#define SIS300
diff --git a/drivers/video/sis/sis.h b/drivers/video/sis/sis.h
index a14e8221103..7c5710e3fb5 100644
--- a/drivers/video/sis/sis.h
+++ b/drivers/video/sis/sis.h
@@ -24,8 +24,6 @@
#ifndef _SIS_H_
#define _SIS_H_
-#include <linux/version.h>
-
#include "osdef.h"
#include <video/sisfb.h>
@@ -42,16 +40,6 @@
#define SIS_NEW_CONFIG_COMPAT
#endif /* CONFIG_COMPAT */
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,8)
-#define SIS_IOTYPE1 void __iomem
-#define SIS_IOTYPE2 __iomem
-#define SISINITSTATIC static
-#else
-#define SIS_IOTYPE1 unsigned char
-#define SIS_IOTYPE2
-#define SISINITSTATIC
-#endif
-
#undef SISFBDEBUG
#ifdef SISFBDEBUG
@@ -505,8 +493,8 @@ struct sis_video_info {
unsigned long UMAsize, LFBsize;
- SIS_IOTYPE1 *video_vbase;
- SIS_IOTYPE1 *mmio_vbase;
+ void __iomem *video_vbase;
+ void __iomem *mmio_vbase;
unsigned char *bios_abase;
@@ -533,8 +521,8 @@ struct sis_video_info {
int sisfb_nocrt2rate;
u32 heapstart; /* offset */
- SIS_IOTYPE1 *sisfb_heap_start; /* address */
- SIS_IOTYPE1 *sisfb_heap_end; /* address */
+ void __iomem *sisfb_heap_start; /* address */
+ void __iomem *sisfb_heap_end; /* address */
u32 sisfb_heap_size;
int havenoheap;
@@ -612,7 +600,7 @@ struct sis_video_info {
u8 detectedpdca;
u8 detectedlcda;
- SIS_IOTYPE1 *hwcursor_vbase;
+ void __iomem *hwcursor_vbase;
int chronteltype;
int tvxpos, tvypos;
diff --git a/drivers/video/sis/sis_accel.c b/drivers/video/sis/sis_accel.c
index 7addf91d2fe..ceb434c95c0 100644
--- a/drivers/video/sis/sis_accel.c
+++ b/drivers/video/sis/sis_accel.c
@@ -28,7 +28,6 @@
* for more information and updates)
*/
-#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fb.h>
diff --git a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c
index b9343844cd1..346d6458cf7 100644
--- a/drivers/video/sis/sis_main.c
+++ b/drivers/video/sis/sis_main.c
@@ -33,7 +33,6 @@
*
*/
-#include <linux/version.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
@@ -41,13 +40,7 @@
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
-#include <linux/tty.h>
-#else
#include <linux/screen_info.h>
-#endif
-
#include <linux/slab.h>
#include <linux/fb.h>
#include <linux/selection.h>
@@ -1167,11 +1160,7 @@ sisfb_set_mode(struct sis_video_info *ivideo, int clrscrn)
unsigned short modeno = ivideo->mode_no;
/* >=2.6.12's fbcon clears the screen anyway */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12)
- if(!clrscrn) modeno |= 0x80;
-#else
modeno |= 0x80;
-#endif
outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD);
@@ -1436,11 +1425,8 @@ sisfb_set_par(struct fb_info *info)
if((err = sisfb_do_set_var(&info->var, 1, info)))
return err;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
- sisfb_get_fix(&info->fix, info->currcon, info);
-#else
sisfb_get_fix(&info->fix, -1, info);
-#endif
+
return 0;
}
@@ -1676,14 +1662,8 @@ sisfb_blank(int blank, struct fb_info *info)
/* ----------- FBDev related routines for all series ---------- */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
static int sisfb_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
-#else
-static int sisfb_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg,
- struct fb_info *info)
-#endif
{
struct sis_video_info *ivideo = (struct sis_video_info *)info->par;
struct sis_memreq sismemreq;
@@ -3986,8 +3966,7 @@ sisfb_handle_command(struct sis_video_info *ivideo, struct sisfb_cmd *sisfb_comm
}
#ifndef MODULE
-SISINITSTATIC int __init
-sisfb_setup(char *options)
+static int __init sisfb_setup(char *options)
{
char *this_opt;
@@ -4086,9 +4065,9 @@ sisfb_setup(char *options)
#endif
static int __devinit
-sisfb_check_rom(SIS_IOTYPE1 *rom_base, struct sis_video_info *ivideo)
+sisfb_check_rom(void __iomem *rom_base, struct sis_video_info *ivideo)
{
- SIS_IOTYPE1 *rom;
+ void __iomem *rom;
int romptr;
if((readb(rom_base) != 0x55) || (readb(rom_base + 1) != 0xaa))
@@ -4117,10 +4096,9 @@ static unsigned char * __devinit
sisfb_find_rom(struct pci_dev *pdev)
{
struct sis_video_info *ivideo = pci_get_drvdata(pdev);
- SIS_IOTYPE1 *rom_base;
+ void __iomem *rom_base;
unsigned char *myrombase = NULL;
u32 temp;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
size_t romsize;
/* First, try the official pci ROM functions (except
@@ -4151,7 +4129,6 @@ sisfb_find_rom(struct pci_dev *pdev)
}
if(myrombase) return myrombase;
-#endif
/* Otherwise do it the conventional way. */
@@ -4225,7 +4202,7 @@ sisfb_post_map_vram(struct sis_video_info *ivideo, unsigned int *mapsize,
static int __devinit
sisfb_post_300_buswidth(struct sis_video_info *ivideo)
{
- SIS_IOTYPE1 *FBAddress = ivideo->video_vbase;
+ void __iomem *FBAddress = ivideo->video_vbase;
unsigned short temp;
unsigned char reg;
int i, j;
@@ -4273,7 +4250,7 @@ sisfb_post_300_rwtest(struct sis_video_info *ivideo, int iteration, int buswidth
int PseudoRankCapacity, int PseudoAdrPinCount,
unsigned int mapsize)
{
- SIS_IOTYPE1 *FBAddr = ivideo->video_vbase;
+ void __iomem *FBAddr = ivideo->video_vbase;
unsigned short sr14;
unsigned int k, RankCapacity, PageCapacity, BankNumHigh, BankNumMid;
unsigned int PhysicalAdrOtherPage, PhysicalAdrHigh, PhysicalAdrHalfPage;
@@ -5829,7 +5806,7 @@ sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
ivideo->engineok = 0;
ivideo->sisfb_was_boot_device = 0;
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12))
+
if(pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW) {
if(ivideo->sisvga_enabled)
ivideo->sisfb_was_boot_device = 1;
@@ -5840,7 +5817,6 @@ sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
"as the primary VGA device\n");
}
}
-#endif
ivideo->sisfb_parm_mem = sisfb_parm_mem;
ivideo->sisfb_accel = sisfb_accel;
@@ -6010,7 +5986,7 @@ sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
ivideo->modeprechange = reg & 0x7f;
} else if(ivideo->sisvga_enabled) {
#if defined(__i386__) || defined(__x86_64__)
- unsigned char SIS_IOTYPE2 *tt = ioremap(0x400, 0x100);
+ unsigned char __iomem *tt = ioremap(0x400, 0x100);
if(tt) {
ivideo->modeprechange = readb(tt + 0x49);
iounmap(tt);
@@ -6503,7 +6479,7 @@ static struct pci_driver sisfb_driver = {
.remove = __devexit_p(sisfb_remove)
};
-SISINITSTATIC int __init sisfb_init(void)
+static int __init sisfb_init(void)
{
#ifndef MODULE
char *options = NULL;
diff --git a/drivers/video/sis/sis_main.h b/drivers/video/sis/sis_main.h
index 3e3b7fa05d6..9540e977270 100644
--- a/drivers/video/sis/sis_main.h
+++ b/drivers/video/sis/sis_main.h
@@ -665,11 +665,11 @@ static struct _customttable {
/* Interface used by the world */
#ifndef MODULE
-SISINITSTATIC int sisfb_setup(char *options);
+static int sisfb_setup(char *options);
#endif
/* Interface to the low level console driver */
-SISINITSTATIC int sisfb_init(void);
+static int sisfb_init(void);
/* fbdev routines */
static int sisfb_get_fix(struct fb_fix_screeninfo *fix, int con,
diff --git a/drivers/video/sis/vgatypes.h b/drivers/video/sis/vgatypes.h
index b532fbd2b04..81a22eaabfd 100644
--- a/drivers/video/sis/vgatypes.h
+++ b/drivers/video/sis/vgatypes.h
@@ -53,10 +53,6 @@
#ifndef _VGATYPES_H_
#define _VGATYPES_H_
-#ifdef SIS_LINUX_KERNEL
-#include <linux/version.h>
-#endif
-
#define SISIOMEMTYPE
#ifdef SIS_LINUX_KERNEL
diff --git a/drivers/video/skeletonfb.c b/drivers/video/skeletonfb.c
index 62321458f71..df5336561d1 100644
--- a/drivers/video/skeletonfb.c
+++ b/drivers/video/skeletonfb.c
@@ -675,13 +675,13 @@ static struct fb_ops xxxfb_ops = {
* Initialization
*/
-/* static int __init xxfb_probe (struct device *device) -- for platform devs */
+/* static int __init xxfb_probe (struct platform_device *pdev) -- for platform devs */
static int __devinit xxxfb_probe(struct pci_dev *dev,
const struct pci_device_id *ent)
{
struct fb_info *info;
struct xxx_par *par;
- struct device* device = &dev->dev; /* for pci drivers */
+ struct device *device = &dev->dev; /* or &pdev->dev */
int cmap_len, retval;
/*
@@ -824,18 +824,18 @@ static int __devinit xxxfb_probe(struct pci_dev *dev,
return -EINVAL;
printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
info->fix.id);
- pci_set_drvdata(dev, info); /* or dev_set_drvdata(device, info) */
+ pci_set_drvdata(dev, info); /* or platform_set_drvdata(pdev, info) */
return 0;
}
/*
* Cleanup
*/
-/* static void __devexit xxxfb_remove(struct device *device) */
+/* static void __devexit xxxfb_remove(struct platform_device *pdev) */
static void __devexit xxxfb_remove(struct pci_dev *dev)
{
struct fb_info *info = pci_get_drvdata(dev);
- /* or dev_get_drvdata(device); */
+ /* or platform_get_drvdata(pdev); */
if (info) {
unregister_framebuffer(info);
@@ -961,18 +961,17 @@ static int xxxfb_resume(struct platform_dev *dev)
#define xxxfb_resume NULL
#endif /* CONFIG_PM */
-static struct device_driver xxxfb_driver = {
- .name = "xxxfb",
- .bus = &platform_bus_type,
+static struct platform_device_driver xxxfb_driver = {
.probe = xxxfb_probe,
.remove = xxxfb_remove,
.suspend = xxxfb_suspend, /* optional but recommended */
.resume = xxxfb_resume, /* optional but recommended */
+ .driver = {
+ .name = "xxxfb",
+ },
};
-static struct platform_device xxxfb_device = {
- .name = "xxxfb",
-};
+static struct platform_device *xxxfb_device;
#ifndef MODULE
/*
@@ -1002,12 +1001,16 @@ static int __init xxxfb_init(void)
return -ENODEV;
xxxfb_setup(option);
#endif
- ret = driver_register(&xxxfb_driver);
+ ret = platform_driver_register(&xxxfb_driver);
if (!ret) {
- ret = platform_device_register(&xxxfb_device);
- if (ret)
- driver_unregister(&xxxfb_driver);
+ xxxfb_device = platform_device_register_simple("xxxfb", 0,
+ NULL, 0);
+
+ if (IS_ERR(xxxfb_device)) {
+ platform_driver_unregister(&xxxfb_driver);
+ ret = PTR_ERR(xxxfb_device);
+ }
}
return ret;
@@ -1015,8 +1018,8 @@ static int __init xxxfb_init(void)
static void __exit xxxfb_exit(void)
{
- platform_device_unregister(&xxxfb_device);
- driver_unregister(&xxxfb_driver);
+ platform_device_unregister(xxxfb_device);
+ platform_driver_unregister(&xxxfb_driver);
}
#endif /* CONFIG_PCI */
diff --git a/drivers/video/sm501fb.c b/drivers/video/sm501fb.c
index 15d4a768b1f..f94ae84a58c 100644
--- a/drivers/video/sm501fb.c
+++ b/drivers/video/sm501fb.c
@@ -48,10 +48,15 @@ enum sm501_controller {
HEAD_PANEL = 1,
};
-/* SM501 memory address */
+/* SM501 memory address.
+ *
+ * This structure is used to track memory usage within the SM501 framebuffer
+ * allocation. The sm_addr field is stored as an offset as it is often used
+ * against both the physical and mapped addresses.
+ */
struct sm501_mem {
unsigned long size;
- unsigned long sm_addr;
+ unsigned long sm_addr; /* offset from base of sm501 fb. */
void __iomem *k_addr;
};
@@ -142,31 +147,68 @@ static inline void sm501fb_sync_regs(struct sm501fb_info *info)
static int sm501_alloc_mem(struct sm501fb_info *inf, struct sm501_mem *mem,
unsigned int why, size_t size)
{
- unsigned int ptr = 0;
+ struct sm501fb_par *par;
+ struct fb_info *fbi;
+ unsigned int ptr;
+ unsigned int end;
switch (why) {
case SM501_MEMF_CURSOR:
ptr = inf->fbmem_len - size;
- inf->fbmem_len = ptr;
+ inf->fbmem_len = ptr; /* adjust available memory. */
break;
case SM501_MEMF_PANEL:
ptr = inf->fbmem_len - size;
- if (ptr < inf->fb[0]->fix.smem_len)
+ fbi = inf->fb[HEAD_CRT];
+
+ /* round down, some programs such as directfb do not draw
+ * 0,0 correctly unless the start is aligned to a page start.
+ */
+
+ if (ptr > 0)
+ ptr &= ~(PAGE_SIZE - 1);
+
+ if (fbi && ptr < fbi->fix.smem_len)
+ return -ENOMEM;
+
+ if (ptr < 0)
return -ENOMEM;
break;
case SM501_MEMF_CRT:
ptr = 0;
+
+ /* check to see if we have panel memory allocated
+ * which would put an limit on available memory. */
+
+ fbi = inf->fb[HEAD_PANEL];
+ if (fbi) {
+ par = fbi->par;
+ end = par->screen.k_addr ? par->screen.sm_addr : inf->fbmem_len;
+ } else
+ end = inf->fbmem_len;
+
+ if ((ptr + size) > end)
+ return -ENOMEM;
+
break;
case SM501_MEMF_ACCEL:
- ptr = inf->fb[0]->fix.smem_len;
+ fbi = inf->fb[HEAD_CRT];
+ ptr = fbi ? fbi->fix.smem_len : 0;
+
+ fbi = inf->fb[HEAD_PANEL];
+ if (fbi) {
+ par = fbi->par;
+ end = par->screen.sm_addr;
+ } else
+ end = inf->fbmem_len;
- if ((ptr + size) >
- (inf->fb[1]->fix.smem_start - inf->fbmem_res->start))
+ if ((ptr + size) > end)
return -ENOMEM;
+
break;
default:
@@ -663,15 +705,25 @@ static void sm501fb_panel_power(struct sm501fb_info *fbi, int to)
sm501fb_sync_regs(fbi);
mdelay(10);
+ /* VBIASEN */
+
if (!(pd->flags & SM501FB_FLAG_PANEL_NO_VBIASEN)) {
- control |= SM501_DC_PANEL_CONTROL_BIAS; /* VBIASEN */
+ if (pd->flags & SM501FB_FLAG_PANEL_INV_VBIASEN)
+ control &= ~SM501_DC_PANEL_CONTROL_BIAS;
+ else
+ control |= SM501_DC_PANEL_CONTROL_BIAS;
+
writel(control, ctrl_reg);
sm501fb_sync_regs(fbi);
mdelay(10);
}
if (!(pd->flags & SM501FB_FLAG_PANEL_NO_FPEN)) {
- control |= SM501_DC_PANEL_CONTROL_FPEN;
+ if (pd->flags & SM501FB_FLAG_PANEL_INV_FPEN)
+ control &= ~SM501_DC_PANEL_CONTROL_FPEN;
+ else
+ control |= SM501_DC_PANEL_CONTROL_FPEN;
+
writel(control, ctrl_reg);
sm501fb_sync_regs(fbi);
mdelay(10);
@@ -679,14 +731,22 @@ static void sm501fb_panel_power(struct sm501fb_info *fbi, int to)
} else if (!to && (control & SM501_DC_PANEL_CONTROL_VDD) != 0) {
/* disable panel power */
if (!(pd->flags & SM501FB_FLAG_PANEL_NO_FPEN)) {
- control &= ~SM501_DC_PANEL_CONTROL_FPEN;
+ if (pd->flags & SM501FB_FLAG_PANEL_INV_FPEN)
+ control |= SM501_DC_PANEL_CONTROL_FPEN;
+ else
+ control &= ~SM501_DC_PANEL_CONTROL_FPEN;
+
writel(control, ctrl_reg);
sm501fb_sync_regs(fbi);
mdelay(10);
}
if (!(pd->flags & SM501FB_FLAG_PANEL_NO_VBIASEN)) {
- control &= ~SM501_DC_PANEL_CONTROL_BIAS;
+ if (pd->flags & SM501FB_FLAG_PANEL_INV_VBIASEN)
+ control |= SM501_DC_PANEL_CONTROL_BIAS;
+ else
+ control &= ~SM501_DC_PANEL_CONTROL_BIAS;
+
writel(control, ctrl_reg);
sm501fb_sync_regs(fbi);
mdelay(10);
@@ -1210,39 +1270,6 @@ static struct fb_ops sm501fb_ops_pnl = {
.fb_imageblit = cfb_imageblit,
};
-/* sm501fb_info_alloc
- *
- * creates and initialises an sm501fb_info structure
-*/
-
-static struct sm501fb_info *sm501fb_info_alloc(struct fb_info *fbinfo_crt,
- struct fb_info *fbinfo_pnl)
-{
- struct sm501fb_info *info;
- struct sm501fb_par *par;
-
- info = kzalloc(sizeof(struct sm501fb_info), GFP_KERNEL);
- if (info) {
- /* set the references back */
-
- par = fbinfo_crt->par;
- par->info = info;
- par->head = HEAD_CRT;
- fbinfo_crt->pseudo_palette = &par->pseudo_palette;
-
- par = fbinfo_pnl->par;
- par->info = info;
- par->head = HEAD_PANEL;
- fbinfo_pnl->pseudo_palette = &par->pseudo_palette;
-
- /* store the two fbs into our info */
- info->fb[HEAD_CRT] = fbinfo_crt;
- info->fb[HEAD_PANEL] = fbinfo_pnl;
- }
-
- return info;
-}
-
/* sm501_init_cursor
*
* initialise hw cursor parameters
@@ -1250,10 +1277,16 @@ static struct sm501fb_info *sm501fb_info_alloc(struct fb_info *fbinfo_crt,
static int sm501_init_cursor(struct fb_info *fbi, unsigned int reg_base)
{
- struct sm501fb_par *par = fbi->par;
- struct sm501fb_info *info = par->info;
+ struct sm501fb_par *par;
+ struct sm501fb_info *info;
int ret;
+ if (fbi == NULL)
+ return 0;
+
+ par = fbi->par;
+ info = par->info;
+
par->cursor_regs = info->regs + reg_base;
ret = sm501_alloc_mem(info, &par->cursor, SM501_MEMF_CURSOR, 1024);
@@ -1281,13 +1314,10 @@ static int sm501fb_start(struct sm501fb_info *info,
struct platform_device *pdev)
{
struct resource *res;
- struct device *dev;
+ struct device *dev = &pdev->dev;
int k;
int ret;
- info->dev = dev = &pdev->dev;
- platform_set_drvdata(pdev, info);
-
info->irq = ret = platform_get_irq(pdev, 0);
if (ret < 0) {
/* we currently do not use the IRQ */
@@ -1390,11 +1420,6 @@ static void sm501fb_stop(struct sm501fb_info *info)
kfree(info->regs_res);
}
-static void sm501fb_info_release(struct sm501fb_info *info)
-{
- kfree(info);
-}
-
static int sm501fb_init_fb(struct fb_info *fb,
enum sm501_controller head,
const char *fbname)
@@ -1539,36 +1564,93 @@ static struct sm501_platdata_fb sm501fb_def_pdata = {
static char driver_name_crt[] = "sm501fb-crt";
static char driver_name_pnl[] = "sm501fb-panel";
-static int __init sm501fb_probe(struct platform_device *pdev)
+static int __devinit sm501fb_probe_one(struct sm501fb_info *info,
+ enum sm501_controller head)
{
- struct sm501fb_info *info;
- struct device *dev = &pdev->dev;
- struct fb_info *fbinfo_crt;
- struct fb_info *fbinfo_pnl;
- int ret;
+ unsigned char *name = (head == HEAD_CRT) ? "crt" : "panel";
+ struct sm501_platdata_fbsub *pd;
+ struct sm501fb_par *par;
+ struct fb_info *fbi;
- /* allocate our framebuffers */
+ pd = (head == HEAD_CRT) ? info->pdata->fb_crt : info->pdata->fb_pnl;
+
+ /* Do not initialise if we've not been given any platform data */
+ if (pd == NULL) {
+ dev_info(info->dev, "no data for fb %s (disabled)\n", name);
+ return 0;
+ }
- fbinfo_crt = framebuffer_alloc(sizeof(struct sm501fb_par), dev);
- if (fbinfo_crt == NULL) {
- dev_err(dev, "cannot allocate crt framebuffer\n");
+ fbi = framebuffer_alloc(sizeof(struct sm501fb_par), info->dev);
+ if (fbi == NULL) {
+ dev_err(info->dev, "cannot allocate %s framebuffer\n", name);
return -ENOMEM;
}
- fbinfo_pnl = framebuffer_alloc(sizeof(struct sm501fb_par), dev);
- if (fbinfo_pnl == NULL) {
- dev_err(dev, "cannot allocate panel framebuffer\n");
- ret = -ENOMEM;
- goto fbinfo_crt_alloc_fail;
+ par = fbi->par;
+ par->info = info;
+ par->head = head;
+ fbi->pseudo_palette = &par->pseudo_palette;
+
+ info->fb[head] = fbi;
+
+ return 0;
+}
+
+/* Free up anything allocated by sm501fb_init_fb */
+
+static void sm501_free_init_fb(struct sm501fb_info *info,
+ enum sm501_controller head)
+{
+ struct fb_info *fbi = info->fb[head];
+
+ fb_dealloc_cmap(&fbi->cmap);
+}
+
+static int __devinit sm501fb_start_one(struct sm501fb_info *info,
+ enum sm501_controller head,
+ const char *drvname)
+{
+ struct fb_info *fbi = info->fb[head];
+ int ret;
+
+ if (!fbi)
+ return 0;
+
+ ret = sm501fb_init_fb(info->fb[head], head, drvname);
+ if (ret) {
+ dev_err(info->dev, "cannot initialise fb %s\n", drvname);
+ return ret;
+ }
+
+ ret = register_framebuffer(info->fb[head]);
+ if (ret) {
+ dev_err(info->dev, "failed to register fb %s\n", drvname);
+ sm501_free_init_fb(info, head);
+ return ret;
}
- info = sm501fb_info_alloc(fbinfo_crt, fbinfo_pnl);
- if (info == NULL) {
- dev_err(dev, "cannot allocate par\n");
- ret = -ENOMEM;
- goto sm501fb_alloc_fail;
+ dev_info(info->dev, "fb%d: %s frame buffer\n", fbi->node, fbi->fix.id);
+
+ return 0;
+}
+
+static int __devinit sm501fb_probe(struct platform_device *pdev)
+{
+ struct sm501fb_info *info;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ /* allocate our framebuffers */
+
+ info = kzalloc(sizeof(struct sm501fb_info), GFP_KERNEL);
+ if (!info) {
+ dev_err(dev, "failed to allocate state\n");
+ return -ENOMEM;
}
+ info->dev = dev = &pdev->dev;
+ platform_set_drvdata(pdev, info);
+
if (dev->parent->platform_data) {
struct sm501_platdata *pd = dev->parent->platform_data;
info->pdata = pd->fb;
@@ -1579,90 +1661,88 @@ static int __init sm501fb_probe(struct platform_device *pdev)
info->pdata = &sm501fb_def_pdata;
}
- /* start the framebuffers */
+ /* probe for the presence of each panel */
- ret = sm501fb_start(info, pdev);
- if (ret) {
- dev_err(dev, "cannot initialise SM501\n");
- goto sm501fb_start_fail;
+ ret = sm501fb_probe_one(info, HEAD_CRT);
+ if (ret < 0) {
+ dev_err(dev, "failed to probe CRT\n");
+ goto err_alloc;
}
- /* CRT framebuffer setup */
+ ret = sm501fb_probe_one(info, HEAD_PANEL);
+ if (ret < 0) {
+ dev_err(dev, "failed to probe PANEL\n");
+ goto err_probed_crt;
+ }
- ret = sm501fb_init_fb(fbinfo_crt, HEAD_CRT, driver_name_crt);
- if (ret) {
- dev_err(dev, "cannot initialise CRT fb\n");
- goto sm501fb_start_fail;
+ if (info->fb[HEAD_PANEL] == NULL &&
+ info->fb[HEAD_CRT] == NULL) {
+ dev_err(dev, "no framebuffers found\n");
+ goto err_alloc;
}
- /* Panel framebuffer setup */
+ /* get the resources for both of the framebuffers */
- ret = sm501fb_init_fb(fbinfo_pnl, HEAD_PANEL, driver_name_pnl);
+ ret = sm501fb_start(info, pdev);
if (ret) {
- dev_err(dev, "cannot initialise Panel fb\n");
- goto sm501fb_start_fail;
+ dev_err(dev, "cannot initialise SM501\n");
+ goto err_probed_panel;
}
- /* register framebuffers */
-
- ret = register_framebuffer(fbinfo_crt);
- if (ret < 0) {
- dev_err(dev, "failed to register CRT fb (%d)\n", ret);
- goto register_crt_fail;
+ ret = sm501fb_start_one(info, HEAD_CRT, driver_name_crt);
+ if (ret) {
+ dev_err(dev, "failed to start CRT\n");
+ goto err_started;
}
- ret = register_framebuffer(fbinfo_pnl);
- if (ret < 0) {
- dev_err(dev, "failed to register panel fb (%d)\n", ret);
- goto register_pnl_fail;
+ ret = sm501fb_start_one(info, HEAD_PANEL, driver_name_pnl);
+ if (ret) {
+ dev_err(dev, "failed to start Panel\n");
+ goto err_started_crt;
}
- dev_info(dev, "fb%d: %s frame buffer device\n",
- fbinfo_crt->node, fbinfo_crt->fix.id);
-
- dev_info(dev, "fb%d: %s frame buffer device\n",
- fbinfo_pnl->node, fbinfo_pnl->fix.id);
-
/* create device files */
ret = device_create_file(dev, &dev_attr_crt_src);
if (ret)
- goto crtsrc_fail;
+ goto err_started_panel;
ret = device_create_file(dev, &dev_attr_fbregs_pnl);
if (ret)
- goto fbregs_pnl_fail;
+ goto err_attached_crtsrc_file;
ret = device_create_file(dev, &dev_attr_fbregs_crt);
if (ret)
- goto fbregs_crt_fail;
+ goto err_attached_pnlregs_file;
/* we registered, return ok */
return 0;
- fbregs_crt_fail:
+err_attached_pnlregs_file:
device_remove_file(dev, &dev_attr_fbregs_pnl);
- fbregs_pnl_fail:
+err_attached_crtsrc_file:
device_remove_file(dev, &dev_attr_crt_src);
- crtsrc_fail:
- unregister_framebuffer(fbinfo_pnl);
+err_started_panel:
+ unregister_framebuffer(info->fb[HEAD_PANEL]);
+ sm501_free_init_fb(info, HEAD_PANEL);
- register_pnl_fail:
- unregister_framebuffer(fbinfo_crt);
+err_started_crt:
+ unregister_framebuffer(info->fb[HEAD_CRT]);
+ sm501_free_init_fb(info, HEAD_CRT);
- register_crt_fail:
+err_started:
sm501fb_stop(info);
- sm501fb_start_fail:
- sm501fb_info_release(info);
+err_probed_panel:
+ framebuffer_release(info->fb[HEAD_PANEL]);
- sm501fb_alloc_fail:
- framebuffer_release(fbinfo_pnl);
+err_probed_crt:
+ framebuffer_release(info->fb[HEAD_CRT]);
- fbinfo_crt_alloc_fail:
- framebuffer_release(fbinfo_crt);
+err_alloc:
+ kfree(info);
return ret;
}
@@ -1681,11 +1761,14 @@ static int sm501fb_remove(struct platform_device *pdev)
device_remove_file(&pdev->dev, &dev_attr_fbregs_pnl);
device_remove_file(&pdev->dev, &dev_attr_crt_src);
+ sm501_free_init_fb(info, HEAD_CRT);
+ sm501_free_init_fb(info, HEAD_PANEL);
+
unregister_framebuffer(fbinfo_crt);
unregister_framebuffer(fbinfo_pnl);
sm501fb_stop(info);
- sm501fb_info_release(info);
+ kfree(info);
framebuffer_release(fbinfo_pnl);
framebuffer_release(fbinfo_crt);
diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c
index ea9f19d2559..77aafcfae03 100644
--- a/drivers/video/tdfxfb.c
+++ b/drivers/video/tdfxfb.c
@@ -836,16 +836,12 @@ static int tdfxfb_pan_display(struct fb_var_screeninfo *var,
struct tdfx_par *par = info->par;
u32 addr = var->yoffset * info->fix.line_length;
- if (nopan || var->xoffset || (var->yoffset > var->yres_virtual))
- return -EINVAL;
- if ((var->yoffset + var->yres > var->yres_virtual && nowrap))
+ if (nopan || var->xoffset)
return -EINVAL;
banshee_make_room(par, 1);
tdfx_outl(par, VIDDESKSTART, addr);
- info->var.xoffset = var->xoffset;
- info->var.yoffset = var->yoffset;
return 0;
}
@@ -1426,6 +1422,8 @@ MODULE_LICENSE("GPL");
module_param(hwcursor, int, 0644);
MODULE_PARM_DESC(hwcursor, "Enable hardware cursor "
"(1=enable, 0=disable, default=1)");
+module_param(mode_option, charp, 0);
+MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
#ifdef CONFIG_MTRR
module_param(nomtrr, bool, 0);
MODULE_PARM_DESC(nomtrr, "Disable MTRR support (default: enabled)");
diff --git a/drivers/video/tridentfb.c b/drivers/video/tridentfb.c
index beefab2992c..479b2e79ad6 100644
--- a/drivers/video/tridentfb.c
+++ b/drivers/video/tridentfb.c
@@ -1,5 +1,5 @@
/*
- * Frame buffer driver for Trident Blade and Image series
+ * Frame buffer driver for Trident TGUI, Blade and Image series
*
* Copyright 2001, 2002 - Jani Monoses <jani@iv.ro>
*
@@ -13,7 +13,6 @@
* code, suggestions
* TODO:
* timing value tweaking so it looks good on every monitor in every mode
- * TGUI acceleration
*/
#include <linux/module.h>
@@ -22,25 +21,26 @@
#include <linux/pci.h>
#include <linux/delay.h>
+#include <video/vga.h>
#include <video/trident.h>
-#define VERSION "0.7.8-NEWAPI"
-
struct tridentfb_par {
void __iomem *io_virt; /* iospace virtual memory address */
+ u32 pseudo_pal[16];
+ int chip_id;
+ int flatpanel;
+ void (*init_accel) (struct tridentfb_par *, int, int);
+ void (*wait_engine) (struct tridentfb_par *);
+ void (*fill_rect)
+ (struct tridentfb_par *par, u32, u32, u32, u32, u32, u32);
+ void (*copy_rect)
+ (struct tridentfb_par *par, u32, u32, u32, u32, u32, u32);
+ void (*image_blit)
+ (struct tridentfb_par *par, const char*,
+ u32, u32, u32, u32, u32, u32);
+ unsigned char eng_oper; /* engine operation... */
};
-static unsigned char eng_oper; /* engine operation... */
-static struct fb_ops tridentfb_ops;
-
-static struct tridentfb_par default_par;
-
-/* FIXME:kmalloc these 3 instead */
-static struct fb_info fb_info;
-static u32 pseudo_pal[16];
-
-static struct fb_var_screeninfo default_var;
-
static struct fb_fix_screeninfo tridentfb_fix = {
.id = "Trident",
.type = FB_TYPE_PACKED_PIXELS,
@@ -49,27 +49,22 @@ static struct fb_fix_screeninfo tridentfb_fix = {
.accel = FB_ACCEL_NONE,
};
-static int chip_id;
-
-static int defaultaccel;
-static int displaytype;
-
/* defaults which are normally overriden by user values */
/* video mode */
-static char *mode_option __devinitdata = "640x480";
-static int bpp = 8;
+static char *mode_option __devinitdata = "640x480-8@60";
+static int bpp __devinitdata = 8;
-static int noaccel;
+static int noaccel __devinitdata;
static int center;
static int stretch;
-static int fp;
-static int crt;
+static int fp __devinitdata;
+static int crt __devinitdata;
-static int memsize;
-static int memdiff;
+static int memsize __devinitdata;
+static int memdiff __devinitdata;
static int nativex;
module_param(mode_option, charp, 0);
@@ -84,25 +79,53 @@ module_param(memsize, int, 0);
module_param(memdiff, int, 0);
module_param(nativex, int, 0);
module_param(fp, int, 0);
+MODULE_PARM_DESC(fp, "Define if flatpanel is connected");
module_param(crt, int, 0);
+MODULE_PARM_DESC(crt, "Define if CRT is connected");
+
+static inline int is_oldclock(int id)
+{
+ return (id == TGUI9440) ||
+ (id == TGUI9660) ||
+ (id == CYBER9320);
+}
+
+static inline int is_oldprotect(int id)
+{
+ return is_oldclock(id) ||
+ (id == PROVIDIA9685) ||
+ (id == CYBER9382) ||
+ (id == CYBER9385);
+}
+
+static inline int is_blade(int id)
+{
+ return (id == BLADE3D) ||
+ (id == CYBERBLADEE4) ||
+ (id == CYBERBLADEi7) ||
+ (id == CYBERBLADEi7D) ||
+ (id == CYBERBLADEi1) ||
+ (id == CYBERBLADEi1D) ||
+ (id == CYBERBLADEAi1) ||
+ (id == CYBERBLADEAi1D);
+}
-static int chip3D;
-static int chipcyber;
+static inline int is_xp(int id)
+{
+ return (id == CYBERBLADEXPAi1) ||
+ (id == CYBERBLADEXPm8) ||
+ (id == CYBERBLADEXPm16);
+}
-static int is3Dchip(int id)
+static inline int is3Dchip(int id)
{
- return ((id == BLADE3D) || (id == CYBERBLADEE4) ||
- (id == CYBERBLADEi7) || (id == CYBERBLADEi7D) ||
+ return is_blade(id) || is_xp(id) ||
(id == CYBER9397) || (id == CYBER9397DVD) ||
(id == CYBER9520) || (id == CYBER9525DVD) ||
- (id == IMAGE975) || (id == IMAGE985) ||
- (id == CYBERBLADEi1) || (id == CYBERBLADEi1D) ||
- (id == CYBERBLADEAi1) || (id == CYBERBLADEAi1D) ||
- (id == CYBERBLADEXPm8) || (id == CYBERBLADEXPm16) ||
- (id == CYBERBLADEXPAi1));
+ (id == IMAGE975) || (id == IMAGE985);
}
-static int iscyber(int id)
+static inline int iscyber(int id)
{
switch (id) {
case CYBER9388:
@@ -122,12 +145,7 @@ static int iscyber(int id)
return 1;
case CYBER9320:
- case TGUI9660:
- case IMAGE975:
- case IMAGE985:
- case BLADE3D:
case CYBERBLADEi7: /* VIA MPV4 integrated version */
-
default:
/* case CYBERBLDAEXPm8: Strange */
/* case CYBERBLDAEXPm16: Strange */
@@ -135,147 +153,110 @@ static int iscyber(int id)
}
}
-#define CRT 0x3D0 /* CRTC registers offset for color display */
-
-#ifndef TRIDENT_MMIO
- #define TRIDENT_MMIO 1
-#endif
-
-#if TRIDENT_MMIO
- #define t_outb(val, reg) writeb(val,((struct tridentfb_par *)(fb_info.par))->io_virt + reg)
- #define t_inb(reg) readb(((struct tridentfb_par*)(fb_info.par))->io_virt + reg)
-#else
- #define t_outb(val, reg) outb(val, reg)
- #define t_inb(reg) inb(reg)
-#endif
+static inline void t_outb(struct tridentfb_par *p, u8 val, u16 reg)
+{
+ fb_writeb(val, p->io_virt + reg);
+}
+static inline u8 t_inb(struct tridentfb_par *p, u16 reg)
+{
+ return fb_readb(p->io_virt + reg);
+}
-static struct accel_switch {
- void (*init_accel) (int, int);
- void (*wait_engine) (void);
- void (*fill_rect) (u32, u32, u32, u32, u32, u32);
- void (*copy_rect) (u32, u32, u32, u32, u32, u32);
-} *acc;
+static inline void writemmr(struct tridentfb_par *par, u16 r, u32 v)
+{
+ fb_writel(v, par->io_virt + r);
+}
-#define writemmr(r, v) writel(v, ((struct tridentfb_par *)fb_info.par)->io_virt + r)
-#define readmmr(r) readl(((struct tridentfb_par *)fb_info.par)->io_virt + r)
+static inline u32 readmmr(struct tridentfb_par *par, u16 r)
+{
+ return fb_readl(par->io_virt + r);
+}
/*
* Blade specific acceleration.
*/
#define point(x, y) ((y) << 16 | (x))
-#define STA 0x2120
-#define CMD 0x2144
-#define ROP 0x2148
-#define CLR 0x2160
-#define SR1 0x2100
-#define SR2 0x2104
-#define DR1 0x2108
-#define DR2 0x210C
-
-#define ROP_S 0xCC
-
-static void blade_init_accel(int pitch, int bpp)
+
+static void blade_init_accel(struct tridentfb_par *par, int pitch, int bpp)
{
int v1 = (pitch >> 3) << 20;
- int tmp = 0, v2;
- switch (bpp) {
- case 8:
- tmp = 0;
- break;
- case 15:
- tmp = 5;
- break;
- case 16:
- tmp = 1;
- break;
- case 24:
- case 32:
- tmp = 2;
- break;
- }
- v2 = v1 | (tmp << 29);
- writemmr(0x21C0, v2);
- writemmr(0x21C4, v2);
- writemmr(0x21B8, v2);
- writemmr(0x21BC, v2);
- writemmr(0x21D0, v1);
- writemmr(0x21D4, v1);
- writemmr(0x21C8, v1);
- writemmr(0x21CC, v1);
- writemmr(0x216C, 0);
+ int tmp = bpp == 24 ? 2 : (bpp >> 4);
+ int v2 = v1 | (tmp << 29);
+
+ writemmr(par, 0x21C0, v2);
+ writemmr(par, 0x21C4, v2);
+ writemmr(par, 0x21B8, v2);
+ writemmr(par, 0x21BC, v2);
+ writemmr(par, 0x21D0, v1);
+ writemmr(par, 0x21D4, v1);
+ writemmr(par, 0x21C8, v1);
+ writemmr(par, 0x21CC, v1);
+ writemmr(par, 0x216C, 0);
}
-static void blade_wait_engine(void)
+static void blade_wait_engine(struct tridentfb_par *par)
{
- while (readmmr(STA) & 0xFA800000) ;
+ while (readmmr(par, STATUS) & 0xFA800000)
+ cpu_relax();
}
-static void blade_fill_rect(u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop)
+static void blade_fill_rect(struct tridentfb_par *par,
+ u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop)
{
- writemmr(CLR, c);
- writemmr(ROP, rop ? 0x66 : ROP_S);
- writemmr(CMD, 0x20000000 | 1 << 19 | 1 << 4 | 2 << 2);
+ writemmr(par, COLOR, c);
+ writemmr(par, ROP, rop ? ROP_X : ROP_S);
+ writemmr(par, CMD, 0x20000000 | 1 << 19 | 1 << 4 | 2 << 2);
- writemmr(DR1, point(x, y));
- writemmr(DR2, point(x + w - 1, y + h - 1));
+ writemmr(par, DST1, point(x, y));
+ writemmr(par, DST2, point(x + w - 1, y + h - 1));
}
-static void blade_copy_rect(u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)
+static void blade_image_blit(struct tridentfb_par *par, const char *data,
+ u32 x, u32 y, u32 w, u32 h, u32 c, u32 b)
+{
+ unsigned size = ((w + 31) >> 5) * h;
+
+ writemmr(par, COLOR, c);
+ writemmr(par, BGCOLOR, b);
+ writemmr(par, CMD, 0xa0000000 | 3 << 19);
+
+ writemmr(par, DST1, point(x, y));
+ writemmr(par, DST2, point(x + w - 1, y + h - 1));
+
+ memcpy(par->io_virt + 0x10000, data, 4 * size);
+}
+
+static void blade_copy_rect(struct tridentfb_par *par,
+ u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)
{
- u32 s1, s2, d1, d2;
int direction = 2;
- s1 = point(x1, y1);
- s2 = point(x1 + w - 1, y1 + h - 1);
- d1 = point(x2, y2);
- d2 = point(x2 + w - 1, y2 + h - 1);
+ u32 s1 = point(x1, y1);
+ u32 s2 = point(x1 + w - 1, y1 + h - 1);
+ u32 d1 = point(x2, y2);
+ u32 d2 = point(x2 + w - 1, y2 + h - 1);
if ((y1 > y2) || ((y1 == y2) && (x1 > x2)))
direction = 0;
- writemmr(ROP, ROP_S);
- writemmr(CMD, 0xE0000000 | 1 << 19 | 1 << 4 | 1 << 2 | direction);
+ writemmr(par, ROP, ROP_S);
+ writemmr(par, CMD, 0xE0000000 | 1 << 19 | 1 << 4 | 1 << 2 | direction);
- writemmr(SR1, direction ? s2 : s1);
- writemmr(SR2, direction ? s1 : s2);
- writemmr(DR1, direction ? d2 : d1);
- writemmr(DR2, direction ? d1 : d2);
+ writemmr(par, SRC1, direction ? s2 : s1);
+ writemmr(par, SRC2, direction ? s1 : s2);
+ writemmr(par, DST1, direction ? d2 : d1);
+ writemmr(par, DST2, direction ? d1 : d2);
}
-static struct accel_switch accel_blade = {
- blade_init_accel,
- blade_wait_engine,
- blade_fill_rect,
- blade_copy_rect,
-};
-
/*
* BladeXP specific acceleration functions
*/
-#define ROP_P 0xF0
-#define masked_point(x, y) ((y & 0xffff)<<16|(x & 0xffff))
-
-static void xp_init_accel(int pitch, int bpp)
+static void xp_init_accel(struct tridentfb_par *par, int pitch, int bpp)
{
- int tmp = 0, v1;
- unsigned char x = 0;
-
- switch (bpp) {
- case 8:
- x = 0;
- break;
- case 16:
- x = 1;
- break;
- case 24:
- x = 3;
- break;
- case 32:
- x = 2;
- break;
- }
+ unsigned char x = bpp == 24 ? 3 : (bpp >> 4);
+ int v1 = pitch << (bpp == 24 ? 20 : (18 + x));
switch (pitch << (bpp >> 3)) {
case 8192:
@@ -293,42 +274,21 @@ static void xp_init_accel(int pitch, int bpp)
break;
}
- t_outb(x, 0x2125);
-
- eng_oper = x | 0x40;
-
- switch (bpp) {
- case 8:
- tmp = 18;
- break;
- case 15:
- case 16:
- tmp = 19;
- break;
- case 24:
- case 32:
- tmp = 20;
- break;
- }
+ t_outb(par, x, 0x2125);
- v1 = pitch << tmp;
+ par->eng_oper = x | 0x40;
- writemmr(0x2154, v1);
- writemmr(0x2150, v1);
- t_outb(3, 0x2126);
+ writemmr(par, 0x2154, v1);
+ writemmr(par, 0x2150, v1);
+ t_outb(par, 3, 0x2126);
}
-static void xp_wait_engine(void)
+static void xp_wait_engine(struct tridentfb_par *par)
{
- int busy;
- int count, timeout;
-
- count = 0;
- timeout = 0;
- for (;;) {
- busy = t_inb(STA) & 0x80;
- if (busy != 0x80)
- return;
+ int count = 0;
+ int timeout = 0;
+
+ while (t_inb(par, STATUS) & 0x80) {
count++;
if (count == 10000000) {
/* Timeout */
@@ -336,30 +296,31 @@ static void xp_wait_engine(void)
timeout++;
if (timeout == 8) {
/* Reset engine */
- t_outb(0x00, 0x2120);
+ t_outb(par, 0x00, STATUS);
return;
}
}
+ cpu_relax();
}
}
-static void xp_fill_rect(u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop)
+static void xp_fill_rect(struct tridentfb_par *par,
+ u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop)
{
- writemmr(0x2127, ROP_P);
- writemmr(0x2158, c);
- writemmr(0x2128, 0x4000);
- writemmr(0x2140, masked_point(h, w));
- writemmr(0x2138, masked_point(y, x));
- t_outb(0x01, 0x2124);
- t_outb(eng_oper, 0x2125);
+ writemmr(par, 0x2127, ROP_P);
+ writemmr(par, 0x2158, c);
+ writemmr(par, DRAWFL, 0x4000);
+ writemmr(par, OLDDIM, point(h, w));
+ writemmr(par, OLDDST, point(y, x));
+ t_outb(par, 0x01, OLDCMD);
+ t_outb(par, par->eng_oper, 0x2125);
}
-static void xp_copy_rect(u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)
+static void xp_copy_rect(struct tridentfb_par *par,
+ u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)
{
- int direction;
u32 x1_tmp, x2_tmp, y1_tmp, y2_tmp;
-
- direction = 0x0004;
+ int direction = 0x0004;
if ((x1 < x2) && (y1 == y2)) {
direction |= 0x0200;
@@ -379,103 +340,152 @@ static void xp_copy_rect(u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)
y2_tmp = y2;
}
- writemmr(0x2128, direction);
- t_outb(ROP_S, 0x2127);
- writemmr(0x213C, masked_point(y1_tmp, x1_tmp));
- writemmr(0x2138, masked_point(y2_tmp, x2_tmp));
- writemmr(0x2140, masked_point(h, w));
- t_outb(0x01, 0x2124);
+ writemmr(par, DRAWFL, direction);
+ t_outb(par, ROP_S, 0x2127);
+ writemmr(par, OLDSRC, point(y1_tmp, x1_tmp));
+ writemmr(par, OLDDST, point(y2_tmp, x2_tmp));
+ writemmr(par, OLDDIM, point(h, w));
+ t_outb(par, 0x01, OLDCMD);
}
-static struct accel_switch accel_xp = {
- xp_init_accel,
- xp_wait_engine,
- xp_fill_rect,
- xp_copy_rect,
-};
-
/*
* Image specific acceleration functions
*/
-static void image_init_accel(int pitch, int bpp)
+static void image_init_accel(struct tridentfb_par *par, int pitch, int bpp)
{
- int tmp = 0;
- switch (bpp) {
- case 8:
- tmp = 0;
- break;
- case 15:
- tmp = 5;
- break;
- case 16:
- tmp = 1;
- break;
- case 24:
- case 32:
- tmp = 2;
- break;
- }
- writemmr(0x2120, 0xF0000000);
- writemmr(0x2120, 0x40000000 | tmp);
- writemmr(0x2120, 0x80000000);
- writemmr(0x2144, 0x00000000);
- writemmr(0x2148, 0x00000000);
- writemmr(0x2150, 0x00000000);
- writemmr(0x2154, 0x00000000);
- writemmr(0x2120, 0x60000000 | (pitch << 16) | pitch);
- writemmr(0x216C, 0x00000000);
- writemmr(0x2170, 0x00000000);
- writemmr(0x217C, 0x00000000);
- writemmr(0x2120, 0x10000000);
- writemmr(0x2130, (2047 << 16) | 2047);
+ int tmp = bpp == 24 ? 2: (bpp >> 4);
+
+ writemmr(par, 0x2120, 0xF0000000);
+ writemmr(par, 0x2120, 0x40000000 | tmp);
+ writemmr(par, 0x2120, 0x80000000);
+ writemmr(par, 0x2144, 0x00000000);
+ writemmr(par, 0x2148, 0x00000000);
+ writemmr(par, 0x2150, 0x00000000);
+ writemmr(par, 0x2154, 0x00000000);
+ writemmr(par, 0x2120, 0x60000000 | (pitch << 16) | pitch);
+ writemmr(par, 0x216C, 0x00000000);
+ writemmr(par, 0x2170, 0x00000000);
+ writemmr(par, 0x217C, 0x00000000);
+ writemmr(par, 0x2120, 0x10000000);
+ writemmr(par, 0x2130, (2047 << 16) | 2047);
}
-static void image_wait_engine(void)
+static void image_wait_engine(struct tridentfb_par *par)
{
- while (readmmr(0x2164) & 0xF0000000) ;
+ while (readmmr(par, 0x2164) & 0xF0000000)
+ cpu_relax();
}
-static void image_fill_rect(u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop)
+static void image_fill_rect(struct tridentfb_par *par,
+ u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop)
{
- writemmr(0x2120, 0x80000000);
- writemmr(0x2120, 0x90000000 | ROP_S);
+ writemmr(par, 0x2120, 0x80000000);
+ writemmr(par, 0x2120, 0x90000000 | ROP_S);
- writemmr(0x2144, c);
+ writemmr(par, 0x2144, c);
- writemmr(DR1, point(x, y));
- writemmr(DR2, point(x + w - 1, y + h - 1));
+ writemmr(par, DST1, point(x, y));
+ writemmr(par, DST2, point(x + w - 1, y + h - 1));
- writemmr(0x2124, 0x80000000 | 3 << 22 | 1 << 10 | 1 << 9);
+ writemmr(par, 0x2124, 0x80000000 | 3 << 22 | 1 << 10 | 1 << 9);
}
-static void image_copy_rect(u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)
+static void image_copy_rect(struct tridentfb_par *par,
+ u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)
{
- u32 s1, s2, d1, d2;
- int direction = 2;
- s1 = point(x1, y1);
- s2 = point(x1 + w - 1, y1 + h - 1);
- d1 = point(x2, y2);
- d2 = point(x2 + w - 1, y2 + h - 1);
+ int direction = 0x4;
+ u32 s1 = point(x1, y1);
+ u32 s2 = point(x1 + w - 1, y1 + h - 1);
+ u32 d1 = point(x2, y2);
+ u32 d2 = point(x2 + w - 1, y2 + h - 1);
if ((y1 > y2) || ((y1 == y2) && (x1 > x2)))
direction = 0;
- writemmr(0x2120, 0x80000000);
- writemmr(0x2120, 0x90000000 | ROP_S);
+ writemmr(par, 0x2120, 0x80000000);
+ writemmr(par, 0x2120, 0x90000000 | ROP_S);
- writemmr(SR1, direction ? s2 : s1);
- writemmr(SR2, direction ? s1 : s2);
- writemmr(DR1, direction ? d2 : d1);
- writemmr(DR2, direction ? d1 : d2);
- writemmr(0x2124, 0x80000000 | 1 << 22 | 1 << 10 | 1 << 7 | direction);
+ writemmr(par, SRC1, direction ? s2 : s1);
+ writemmr(par, SRC2, direction ? s1 : s2);
+ writemmr(par, DST1, direction ? d2 : d1);
+ writemmr(par, DST2, direction ? d1 : d2);
+ writemmr(par, 0x2124,
+ 0x80000000 | 1 << 22 | 1 << 10 | 1 << 7 | direction);
}
-static struct accel_switch accel_image = {
- image_init_accel,
- image_wait_engine,
- image_fill_rect,
- image_copy_rect,
-};
+/*
+ * TGUI 9440/96XX acceleration
+ */
+
+static void tgui_init_accel(struct tridentfb_par *par, int pitch, int bpp)
+{
+ unsigned char x = bpp == 24 ? 3 : (bpp >> 4);
+
+ /* disable clipping */
+ writemmr(par, 0x2148, 0);
+ writemmr(par, 0x214C, point(4095, 2047));
+
+ switch ((pitch * bpp) / 8) {
+ case 8192:
+ case 512:
+ x |= 0x00;
+ break;
+ case 1024:
+ x |= 0x04;
+ break;
+ case 2048:
+ x |= 0x08;
+ break;
+ case 4096:
+ x |= 0x0C;
+ break;
+ }
+
+ fb_writew(x, par->io_virt + 0x2122);
+}
+
+static void tgui_fill_rect(struct tridentfb_par *par,
+ u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop)
+{
+ t_outb(par, ROP_P, 0x2127);
+ writemmr(par, OLDCLR, c);
+ writemmr(par, DRAWFL, 0x4020);
+ writemmr(par, OLDDIM, point(w - 1, h - 1));
+ writemmr(par, OLDDST, point(x, y));
+ t_outb(par, 1, OLDCMD);
+}
+
+static void tgui_copy_rect(struct tridentfb_par *par,
+ u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)
+{
+ int flags = 0;
+ u16 x1_tmp, x2_tmp, y1_tmp, y2_tmp;
+
+ if ((x1 < x2) && (y1 == y2)) {
+ flags |= 0x0200;
+ x1_tmp = x1 + w - 1;
+ x2_tmp = x2 + w - 1;
+ } else {
+ x1_tmp = x1;
+ x2_tmp = x2;
+ }
+
+ if (y1 < y2) {
+ flags |= 0x0100;
+ y1_tmp = y1 + h - 1;
+ y2_tmp = y2 + h - 1;
+ } else {
+ y1_tmp = y1;
+ y2_tmp = y2;
+ }
+
+ writemmr(par, DRAWFL, 0x4 | flags);
+ t_outb(par, ROP_S, 0x2127);
+ writemmr(par, OLDSRC, point(x1_tmp, y1_tmp));
+ writemmr(par, OLDDST, point(x2_tmp, y2_tmp));
+ writemmr(par, OLDDIM, point(w - 1, h - 1));
+ t_outb(par, 1, OLDCMD);
+}
/*
* Accel functions called by the upper layers
@@ -484,129 +494,162 @@ static struct accel_switch accel_image = {
static void tridentfb_fillrect(struct fb_info *info,
const struct fb_fillrect *fr)
{
- int bpp = info->var.bits_per_pixel;
- int col = 0;
+ struct tridentfb_par *par = info->par;
+ int col;
- switch (bpp) {
- default:
- case 8:
- col |= fr->color;
+ if (info->flags & FBINFO_HWACCEL_DISABLED) {
+ cfb_fillrect(info, fr);
+ return;
+ }
+ if (info->var.bits_per_pixel == 8) {
+ col = fr->color;
col |= col << 8;
col |= col << 16;
- break;
- case 16:
+ } else
col = ((u32 *)(info->pseudo_palette))[fr->color];
- break;
- case 32:
- col = ((u32 *)(info->pseudo_palette))[fr->color];
- break;
+
+ par->wait_engine(par);
+ par->fill_rect(par, fr->dx, fr->dy, fr->width,
+ fr->height, col, fr->rop);
+}
+
+static void tridentfb_imageblit(struct fb_info *info,
+ const struct fb_image *img)
+{
+ struct tridentfb_par *par = info->par;
+ int col, bgcol;
+
+ if ((info->flags & FBINFO_HWACCEL_DISABLED) || img->depth != 1) {
+ cfb_imageblit(info, img);
+ return;
+ }
+ if (info->var.bits_per_pixel == 8) {
+ col = img->fg_color;
+ col |= col << 8;
+ col |= col << 16;
+ bgcol = img->bg_color;
+ bgcol |= bgcol << 8;
+ bgcol |= bgcol << 16;
+ } else {
+ col = ((u32 *)(info->pseudo_palette))[img->fg_color];
+ bgcol = ((u32 *)(info->pseudo_palette))[img->bg_color];
}
- acc->fill_rect(fr->dx, fr->dy, fr->width, fr->height, col, fr->rop);
- acc->wait_engine();
+ par->wait_engine(par);
+ if (par->image_blit)
+ par->image_blit(par, img->data, img->dx, img->dy,
+ img->width, img->height, col, bgcol);
+ else
+ cfb_imageblit(info, img);
}
+
static void tridentfb_copyarea(struct fb_info *info,
const struct fb_copyarea *ca)
{
- acc->copy_rect(ca->sx, ca->sy, ca->dx, ca->dy, ca->width, ca->height);
- acc->wait_engine();
+ struct tridentfb_par *par = info->par;
+
+ if (info->flags & FBINFO_HWACCEL_DISABLED) {
+ cfb_copyarea(info, ca);
+ return;
+ }
+ par->wait_engine(par);
+ par->copy_rect(par, ca->sx, ca->sy, ca->dx, ca->dy,
+ ca->width, ca->height);
+}
+
+static int tridentfb_sync(struct fb_info *info)
+{
+ struct tridentfb_par *par = info->par;
+
+ if (!(info->flags & FBINFO_HWACCEL_DISABLED))
+ par->wait_engine(par);
+ return 0;
}
-#else /* !CONFIG_FB_TRIDENT_ACCEL */
+#else
#define tridentfb_fillrect cfb_fillrect
#define tridentfb_copyarea cfb_copyarea
+#define tridentfb_imageblit cfb_imageblit
#endif /* CONFIG_FB_TRIDENT_ACCEL */
-
/*
* Hardware access functions
*/
-static inline unsigned char read3X4(int reg)
+static inline unsigned char read3X4(struct tridentfb_par *par, int reg)
{
- struct tridentfb_par *par = (struct tridentfb_par *)fb_info.par;
- writeb(reg, par->io_virt + CRT + 4);
- return readb(par->io_virt + CRT + 5);
+ return vga_mm_rcrt(par->io_virt, reg);
}
-static inline void write3X4(int reg, unsigned char val)
+static inline void write3X4(struct tridentfb_par *par, int reg,
+ unsigned char val)
{
- struct tridentfb_par *par = (struct tridentfb_par *)fb_info.par;
- writeb(reg, par->io_virt + CRT + 4);
- writeb(val, par->io_virt + CRT + 5);
+ vga_mm_wcrt(par->io_virt, reg, val);
}
-static inline unsigned char read3C4(int reg)
+static inline unsigned char read3CE(struct tridentfb_par *par,
+ unsigned char reg)
{
- t_outb(reg, 0x3C4);
- return t_inb(0x3C5);
+ return vga_mm_rgfx(par->io_virt, reg);
}
-static inline void write3C4(int reg, unsigned char val)
+static inline void writeAttr(struct tridentfb_par *par, int reg,
+ unsigned char val)
{
- t_outb(reg, 0x3C4);
- t_outb(val, 0x3C5);
+ fb_readb(par->io_virt + VGA_IS1_RC); /* flip-flop to index */
+ vga_mm_wattr(par->io_virt, reg, val);
}
-static inline unsigned char read3CE(int reg)
+static inline void write3CE(struct tridentfb_par *par, int reg,
+ unsigned char val)
{
- t_outb(reg, 0x3CE);
- return t_inb(0x3CF);
+ vga_mm_wgfx(par->io_virt, reg, val);
}
-static inline void writeAttr(int reg, unsigned char val)
-{
- readb(((struct tridentfb_par *)fb_info.par)->io_virt + CRT + 0x0A); /* flip-flop to index */
- t_outb(reg, 0x3C0);
- t_outb(val, 0x3C0);
-}
-
-static inline void write3CE(int reg, unsigned char val)
-{
- t_outb(reg, 0x3CE);
- t_outb(val, 0x3CF);
-}
-
-static void enable_mmio(void)
+static void enable_mmio(struct tridentfb_par *par)
{
/* Goto New Mode */
- outb(0x0B, 0x3C4);
- inb(0x3C5);
+ vga_io_rseq(0x0B);
/* Unprotect registers */
- outb(NewMode1, 0x3C4);
- outb(0x80, 0x3C5);
+ vga_io_wseq(NewMode1, 0x80);
+ if (!is_oldprotect(par->chip_id))
+ vga_io_wseq(Protection, 0x92);
/* Enable MMIO */
outb(PCIReg, 0x3D4);
outb(inb(0x3D5) | 0x01, 0x3D5);
}
-static void disable_mmio(void)
+static void disable_mmio(struct tridentfb_par *par)
{
/* Goto New Mode */
- t_outb(0x0B, 0x3C4);
- t_inb(0x3C5);
+ vga_mm_rseq(par->io_virt, 0x0B);
/* Unprotect registers */
- t_outb(NewMode1, 0x3C4);
- t_outb(0x80, 0x3C5);
+ vga_mm_wseq(par->io_virt, NewMode1, 0x80);
+ if (!is_oldprotect(par->chip_id))
+ vga_mm_wseq(par->io_virt, Protection, 0x92);
/* Disable MMIO */
- t_outb(PCIReg, 0x3D4);
- t_outb(t_inb(0x3D5) & ~0x01, 0x3D5);
+ t_outb(par, PCIReg, 0x3D4);
+ t_outb(par, t_inb(par, 0x3D5) & ~0x01, 0x3D5);
}
-#define crtc_unlock() write3X4(CRTVSyncEnd, read3X4(CRTVSyncEnd) & 0x7F)
+static inline void crtc_unlock(struct tridentfb_par *par)
+{
+ write3X4(par, VGA_CRTC_V_SYNC_END,
+ read3X4(par, VGA_CRTC_V_SYNC_END) & 0x7F);
+}
/* Return flat panel's maximum x resolution */
-static int __devinit get_nativex(void)
+static int __devinit get_nativex(struct tridentfb_par *par)
{
int x, y, tmp;
if (nativex)
return nativex;
- tmp = (read3CE(VertStretch) >> 4) & 3;
+ tmp = (read3CE(par, VertStretch) >> 4) & 3;
switch (tmp) {
case 0:
@@ -632,77 +675,92 @@ static int __devinit get_nativex(void)
}
/* Set pitch */
-static void set_lwidth(int width)
+static inline void set_lwidth(struct tridentfb_par *par, int width)
{
- write3X4(Offset, width & 0xFF);
- write3X4(AddColReg,
- (read3X4(AddColReg) & 0xCF) | ((width & 0x300) >> 4));
+ write3X4(par, VGA_CRTC_OFFSET, width & 0xFF);
+ write3X4(par, AddColReg,
+ (read3X4(par, AddColReg) & 0xCF) | ((width & 0x300) >> 4));
}
/* For resolutions smaller than FP resolution stretch */
-static void screen_stretch(void)
+static void screen_stretch(struct tridentfb_par *par)
{
- if (chip_id != CYBERBLADEXPAi1)
- write3CE(BiosReg, 0);
+ if (par->chip_id != CYBERBLADEXPAi1)
+ write3CE(par, BiosReg, 0);
else
- write3CE(BiosReg, 8);
- write3CE(VertStretch, (read3CE(VertStretch) & 0x7C) | 1);
- write3CE(HorStretch, (read3CE(HorStretch) & 0x7C) | 1);
+ write3CE(par, BiosReg, 8);
+ write3CE(par, VertStretch, (read3CE(par, VertStretch) & 0x7C) | 1);
+ write3CE(par, HorStretch, (read3CE(par, HorStretch) & 0x7C) | 1);
}
/* For resolutions smaller than FP resolution center */
-static void screen_center(void)
+static inline void screen_center(struct tridentfb_par *par)
{
- write3CE(VertStretch, (read3CE(VertStretch) & 0x7C) | 0x80);
- write3CE(HorStretch, (read3CE(HorStretch) & 0x7C) | 0x80);
+ write3CE(par, VertStretch, (read3CE(par, VertStretch) & 0x7C) | 0x80);
+ write3CE(par, HorStretch, (read3CE(par, HorStretch) & 0x7C) | 0x80);
}
/* Address of first shown pixel in display memory */
-static void set_screen_start(int base)
+static void set_screen_start(struct tridentfb_par *par, int base)
{
- write3X4(StartAddrLow, base & 0xFF);
- write3X4(StartAddrHigh, (base & 0xFF00) >> 8);
- write3X4(CRTCModuleTest,
- (read3X4(CRTCModuleTest) & 0xDF) | ((base & 0x10000) >> 11));
- write3X4(CRTHiOrd,
- (read3X4(CRTHiOrd) & 0xF8) | ((base & 0xE0000) >> 17));
+ u8 tmp;
+ write3X4(par, VGA_CRTC_START_LO, base & 0xFF);
+ write3X4(par, VGA_CRTC_START_HI, (base & 0xFF00) >> 8);
+ tmp = read3X4(par, CRTCModuleTest) & 0xDF;
+ write3X4(par, CRTCModuleTest, tmp | ((base & 0x10000) >> 11));
+ tmp = read3X4(par, CRTHiOrd) & 0xF8;
+ write3X4(par, CRTHiOrd, tmp | ((base & 0xE0000) >> 17));
}
/* Set dotclock frequency */
-static void set_vclk(unsigned long freq)
+static void set_vclk(struct tridentfb_par *par, unsigned long freq)
{
int m, n, k;
- unsigned long f, fi, d, di;
- unsigned char lo = 0, hi = 0;
+ unsigned long fi, d, di;
+ unsigned char best_m = 0, best_n = 0, best_k = 0;
+ unsigned char hi, lo;
+ unsigned char shift = !is_oldclock(par->chip_id) ? 2 : 1;
d = 20000;
- for (k = 2; k >= 0; k--)
- for (m = 0; m < 63; m++)
- for (n = 0; n < 128; n++) {
+ for (k = shift; k >= 0; k--)
+ for (m = 1; m < 32; m++) {
+ n = ((m + 2) << shift) - 8;
+ for (n = (n < 0 ? 0 : n); n < 122; n++) {
fi = ((14318l * (n + 8)) / (m + 2)) >> k;
- if ((di = abs(fi - freq)) < d) {
+ di = abs(fi - freq);
+ if (di < d || (di == d && k == best_k)) {
d = di;
- f = fi;
- lo = n;
- hi = (k << 6) | m;
+ best_n = n;
+ best_m = m;
+ best_k = k;
}
if (fi > freq)
break;
}
- if (chip3D) {
- write3C4(ClockHigh, hi);
- write3C4(ClockLow, lo);
+ }
+
+ if (is_oldclock(par->chip_id)) {
+ lo = best_n | (best_m << 7);
+ hi = (best_m >> 1) | (best_k << 4);
} else {
- outb(lo, 0x43C8);
- outb(hi, 0x43C9);
+ lo = best_n;
+ hi = best_m | (best_k << 6);
+ }
+
+ if (is3Dchip(par->chip_id)) {
+ vga_mm_wseq(par->io_virt, ClockHigh, hi);
+ vga_mm_wseq(par->io_virt, ClockLow, lo);
+ } else {
+ t_outb(par, lo, 0x43C8);
+ t_outb(par, hi, 0x43C9);
}
debug("VCLK = %X %X\n", hi, lo);
}
/* Set number of lines for flat panels*/
-static void set_number_of_lines(int lines)
+static void set_number_of_lines(struct tridentfb_par *par, int lines)
{
- int tmp = read3CE(CyberEnhance) & 0x8F;
+ int tmp = read3CE(par, CyberEnhance) & 0x8F;
if (lines > 1024)
tmp |= 0x50;
else if (lines > 768)
@@ -711,24 +769,24 @@ static void set_number_of_lines(int lines)
tmp |= 0x20;
else if (lines > 480)
tmp |= 0x10;
- write3CE(CyberEnhance, tmp);
+ write3CE(par, CyberEnhance, tmp);
}
/*
* If we see that FP is active we assume we have one.
- * Otherwise we have a CRT display.User can override.
+ * Otherwise we have a CRT display. User can override.
*/
-static unsigned int __devinit get_displaytype(void)
+static int __devinit is_flatpanel(struct tridentfb_par *par)
{
if (fp)
- return DISPLAY_FP;
- if (crt || !chipcyber)
- return DISPLAY_CRT;
- return (read3CE(FPConfig) & 0x10) ? DISPLAY_FP : DISPLAY_CRT;
+ return 1;
+ if (crt || !iscyber(par->chip_id))
+ return 0;
+ return (read3CE(par, FPConfig) & 0x10) ? 1 : 0;
}
/* Try detecting the video memory size */
-static unsigned int __devinit get_memsize(void)
+static unsigned int __devinit get_memsize(struct tridentfb_par *par)
{
unsigned char tmp, tmp2;
unsigned int k;
@@ -737,12 +795,12 @@ static unsigned int __devinit get_memsize(void)
if (memsize)
k = memsize * Kb;
else
- switch (chip_id) {
+ switch (par->chip_id) {
case CYBER9525DVD:
k = 2560 * Kb;
break;
default:
- tmp = read3X4(SPR) & 0x0F;
+ tmp = read3X4(par, SPR) & 0x0F;
switch (tmp) {
case 0x01:
@@ -774,7 +832,7 @@ static unsigned int __devinit get_memsize(void)
break;
case 0x0E: /* XP */
- tmp2 = read3C4(0xC1);
+ tmp2 = vga_mm_rseq(par->io_virt, 0xC1);
switch (tmp2) {
case 0x00:
k = 20 * Mb;
@@ -812,26 +870,67 @@ static unsigned int __devinit get_memsize(void)
static int tridentfb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
+ struct tridentfb_par *par = info->par;
int bpp = var->bits_per_pixel;
+ int line_length;
+ int ramdac = 230000; /* 230MHz for most 3D chips */
debug("enter\n");
/* check color depth */
if (bpp == 24)
bpp = var->bits_per_pixel = 32;
+ if (bpp != 8 && bpp != 16 && bpp != 32)
+ return -EINVAL;
+ if (par->chip_id == TGUI9440 && bpp == 32)
+ return -EINVAL;
/* check whether resolution fits on panel and in memory */
- if (flatpanel && nativex && var->xres > nativex)
+ if (par->flatpanel && nativex && var->xres > nativex)
+ return -EINVAL;
+ /* various resolution checks */
+ var->xres = (var->xres + 7) & ~0x7;
+ if (var->xres > var->xres_virtual)
+ var->xres_virtual = var->xres;
+ if (var->yres > var->yres_virtual)
+ var->yres_virtual = var->yres;
+ if (var->xres_virtual > 4095 || var->yres > 2048)
return -EINVAL;
- if (var->xres * var->yres_virtual * bpp / 8 > info->fix.smem_len)
+ /* prevent from position overflow for acceleration */
+ if (var->yres_virtual > 0xffff)
+ return -EINVAL;
+ line_length = var->xres_virtual * bpp / 8;
+
+ if (!is3Dchip(par->chip_id) &&
+ !(info->flags & FBINFO_HWACCEL_DISABLED)) {
+ /* acceleration requires line length to be power of 2 */
+ if (line_length <= 512)
+ var->xres_virtual = 512 * 8 / bpp;
+ else if (line_length <= 1024)
+ var->xres_virtual = 1024 * 8 / bpp;
+ else if (line_length <= 2048)
+ var->xres_virtual = 2048 * 8 / bpp;
+ else if (line_length <= 4096)
+ var->xres_virtual = 4096 * 8 / bpp;
+ else if (line_length <= 8192)
+ var->xres_virtual = 8192 * 8 / bpp;
+ else
+ return -EINVAL;
+
+ line_length = var->xres_virtual * bpp / 8;
+ }
+
+ /* datasheet specifies how to set panning only up to 4 MB */
+ if (line_length * (var->yres_virtual - var->yres) > (4 << 20))
+ var->yres_virtual = ((4 << 20) / line_length) + var->yres;
+
+ if (line_length * var->yres_virtual > info->fix.smem_len)
return -EINVAL;
switch (bpp) {
case 8:
var->red.offset = 0;
- var->green.offset = 0;
- var->blue.offset = 0;
- var->red.length = 6;
- var->green.length = 6;
- var->blue.length = 6;
+ var->red.length = 8;
+ var->green = var->red;
+ var->blue = var->red;
break;
case 16:
var->red.offset = 11;
@@ -852,6 +951,33 @@ static int tridentfb_check_var(struct fb_var_screeninfo *var,
default:
return -EINVAL;
}
+
+ if (is_xp(par->chip_id))
+ ramdac = 350000;
+
+ switch (par->chip_id) {
+ case TGUI9440:
+ ramdac = (bpp >= 16) ? 45000 : 90000;
+ break;
+ case CYBER9320:
+ case TGUI9660:
+ ramdac = 135000;
+ break;
+ case PROVIDIA9685:
+ case CYBER9388:
+ case CYBER9382:
+ case CYBER9385:
+ ramdac = 170000;
+ break;
+ }
+
+ /* The clock is doubled for 32 bpp */
+ if (bpp == 32)
+ ramdac /= 2;
+
+ if (PICOS2KHZ(var->pixclock) > ramdac)
+ return -EINVAL;
+
debug("exit\n");
return 0;
@@ -862,25 +988,31 @@ static int tridentfb_check_var(struct fb_var_screeninfo *var,
static int tridentfb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
+ struct tridentfb_par *par = info->par;
unsigned int offset;
debug("enter\n");
- offset = (var->xoffset + (var->yoffset * var->xres))
+ offset = (var->xoffset + (var->yoffset * var->xres_virtual))
* var->bits_per_pixel / 32;
- info->var.xoffset = var->xoffset;
- info->var.yoffset = var->yoffset;
- set_screen_start(offset);
+ set_screen_start(par, offset);
debug("exit\n");
return 0;
}
-#define shadowmode_on() write3CE(CyberControl, read3CE(CyberControl) | 0x81)
-#define shadowmode_off() write3CE(CyberControl, read3CE(CyberControl) & 0x7E)
+static inline void shadowmode_on(struct tridentfb_par *par)
+{
+ write3CE(par, CyberControl, read3CE(par, CyberControl) | 0x81);
+}
+
+static inline void shadowmode_off(struct tridentfb_par *par)
+{
+ write3CE(par, CyberControl, read3CE(par, CyberControl) & 0x7E);
+}
/* Set the hardware to the requested video mode */
static int tridentfb_set_par(struct fb_info *info)
{
- struct tridentfb_par *par = (struct tridentfb_par *)(info->par);
+ struct tridentfb_par *par = info->par;
u32 htotal, hdispend, hsyncstart, hsyncend, hblankstart, hblankend;
u32 vtotal, vdispend, vsyncstart, vsyncend, vblankstart, vblankend;
struct fb_var_screeninfo *var = &info->var;
@@ -891,58 +1023,73 @@ static int tridentfb_set_par(struct fb_info *info)
debug("enter\n");
hdispend = var->xres / 8 - 1;
hsyncstart = (var->xres + var->right_margin) / 8;
- hsyncend = var->hsync_len / 8;
- htotal =
- (var->xres + var->left_margin + var->right_margin +
- var->hsync_len) / 8 - 10;
+ hsyncend = (var->xres + var->right_margin + var->hsync_len) / 8;
+ htotal = (var->xres + var->left_margin + var->right_margin +
+ var->hsync_len) / 8 - 5;
hblankstart = hdispend + 1;
- hblankend = htotal + 5;
+ hblankend = htotal + 3;
vdispend = var->yres - 1;
vsyncstart = var->yres + var->lower_margin;
- vsyncend = var->vsync_len;
- vtotal = var->upper_margin + vsyncstart + vsyncend - 2;
- vblankstart = var->yres;
- vblankend = vtotal + 2;
+ vsyncend = vsyncstart + var->vsync_len;
+ vtotal = var->upper_margin + vsyncend - 2;
+ vblankstart = vdispend + 1;
+ vblankend = vtotal;
+
+ if (info->var.vmode & FB_VMODE_INTERLACED) {
+ vtotal /= 2;
+ vdispend /= 2;
+ vsyncstart /= 2;
+ vsyncend /= 2;
+ vblankstart /= 2;
+ vblankend /= 2;
+ }
- crtc_unlock();
- write3CE(CyberControl, 8);
+ enable_mmio(par);
+ crtc_unlock(par);
+ write3CE(par, CyberControl, 8);
+ tmp = 0xEB;
+ if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+ tmp &= ~0x40;
+ if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+ tmp &= ~0x80;
- if (flatpanel && var->xres < nativex) {
+ if (par->flatpanel && var->xres < nativex) {
/*
* on flat panels with native size larger
* than requested resolution decide whether
* we stretch or center
*/
- t_outb(0xEB, 0x3C2);
+ t_outb(par, tmp | 0xC0, VGA_MIS_W);
- shadowmode_on();
+ shadowmode_on(par);
if (center)
- screen_center();
+ screen_center(par);
else if (stretch)
- screen_stretch();
+ screen_stretch(par);
} else {
- t_outb(0x2B, 0x3C2);
- write3CE(CyberControl, 8);
+ t_outb(par, tmp, VGA_MIS_W);
+ write3CE(par, CyberControl, 8);
}
/* vertical timing values */
- write3X4(CRTVTotal, vtotal & 0xFF);
- write3X4(CRTVDispEnd, vdispend & 0xFF);
- write3X4(CRTVSyncStart, vsyncstart & 0xFF);
- write3X4(CRTVSyncEnd, (vsyncend & 0x0F));
- write3X4(CRTVBlankStart, vblankstart & 0xFF);
- write3X4(CRTVBlankEnd, 0 /* p->vblankend & 0xFF */ );
+ write3X4(par, VGA_CRTC_V_TOTAL, vtotal & 0xFF);
+ write3X4(par, VGA_CRTC_V_DISP_END, vdispend & 0xFF);
+ write3X4(par, VGA_CRTC_V_SYNC_START, vsyncstart & 0xFF);
+ write3X4(par, VGA_CRTC_V_SYNC_END, (vsyncend & 0x0F));
+ write3X4(par, VGA_CRTC_V_BLANK_START, vblankstart & 0xFF);
+ write3X4(par, VGA_CRTC_V_BLANK_END, vblankend & 0xFF);
/* horizontal timing values */
- write3X4(CRTHTotal, htotal & 0xFF);
- write3X4(CRTHDispEnd, hdispend & 0xFF);
- write3X4(CRTHSyncStart, hsyncstart & 0xFF);
- write3X4(CRTHSyncEnd, (hsyncend & 0x1F) | ((hblankend & 0x20) << 2));
- write3X4(CRTHBlankStart, hblankstart & 0xFF);
- write3X4(CRTHBlankEnd, 0 /* (p->hblankend & 0x1F) */ );
+ write3X4(par, VGA_CRTC_H_TOTAL, htotal & 0xFF);
+ write3X4(par, VGA_CRTC_H_DISP, hdispend & 0xFF);
+ write3X4(par, VGA_CRTC_H_SYNC_START, hsyncstart & 0xFF);
+ write3X4(par, VGA_CRTC_H_SYNC_END,
+ (hsyncend & 0x1F) | ((hblankend & 0x20) << 2));
+ write3X4(par, VGA_CRTC_H_BLANK_START, hblankstart & 0xFF);
+ write3X4(par, VGA_CRTC_H_BLANK_END, hblankend & 0x1F);
/* higher bits of vertical timing values */
tmp = 0x10;
@@ -954,39 +1101,43 @@ static int tridentfb_set_par(struct fb_info *info)
if (vtotal & 0x200) tmp |= 0x20;
if (vdispend & 0x200) tmp |= 0x40;
if (vsyncstart & 0x200) tmp |= 0x80;
- write3X4(CRTOverflow, tmp);
+ write3X4(par, VGA_CRTC_OVERFLOW, tmp);
- tmp = read3X4(CRTHiOrd) | 0x08; /* line compare bit 10 */
+ tmp = read3X4(par, CRTHiOrd) & 0x07;
+ tmp |= 0x08; /* line compare bit 10 */
if (vtotal & 0x400) tmp |= 0x80;
if (vblankstart & 0x400) tmp |= 0x40;
if (vsyncstart & 0x400) tmp |= 0x20;
if (vdispend & 0x400) tmp |= 0x10;
- write3X4(CRTHiOrd, tmp);
+ write3X4(par, CRTHiOrd, tmp);
- tmp = 0;
- if (htotal & 0x800) tmp |= 0x800 >> 11;
- if (hblankstart & 0x800) tmp |= 0x800 >> 7;
- write3X4(HorizOverflow, tmp);
+ tmp = (htotal >> 8) & 0x01;
+ tmp |= (hdispend >> 7) & 0x02;
+ tmp |= (hsyncstart >> 5) & 0x08;
+ tmp |= (hblankstart >> 4) & 0x10;
+ write3X4(par, HorizOverflow, tmp);
tmp = 0x40;
if (vblankstart & 0x200) tmp |= 0x20;
//FIXME if (info->var.vmode & FB_VMODE_DOUBLE) tmp |= 0x80; /* double scan for 200 line modes */
- write3X4(CRTMaxScanLine, tmp);
+ write3X4(par, VGA_CRTC_MAX_SCAN, tmp);
- write3X4(CRTLineCompare, 0xFF);
- write3X4(CRTPRowScan, 0);
- write3X4(CRTModeControl, 0xC3);
+ write3X4(par, VGA_CRTC_LINE_COMPARE, 0xFF);
+ write3X4(par, VGA_CRTC_PRESET_ROW, 0);
+ write3X4(par, VGA_CRTC_MODE, 0xC3);
- write3X4(LinearAddReg, 0x20); /* enable linear addressing */
+ write3X4(par, LinearAddReg, 0x20); /* enable linear addressing */
tmp = (info->var.vmode & FB_VMODE_INTERLACED) ? 0x84 : 0x80;
- write3X4(CRTCModuleTest, tmp); /* enable access extended memory */
-
- write3X4(GraphEngReg, 0x80); /* enable GE for text acceleration */
+ /* enable access extended memory */
+ write3X4(par, CRTCModuleTest, tmp);
+ tmp = read3CE(par, MiscIntContReg) & ~0x4;
+ if (info->var.vmode & FB_VMODE_INTERLACED)
+ tmp |= 0x4;
+ write3CE(par, MiscIntContReg, tmp);
-#ifdef CONFIG_FB_TRIDENT_ACCEL
- acc->init_accel(info->var.xres, bpp);
-#endif
+ /* enable GE for text acceleration */
+ write3X4(par, GraphEngReg, 0x80);
switch (bpp) {
case 8:
@@ -1003,57 +1154,59 @@ static int tridentfb_set_par(struct fb_info *info)
break;
}
- write3X4(PixelBusReg, tmp);
+ write3X4(par, PixelBusReg, tmp);
- tmp = 0x10;
- if (chipcyber)
+ tmp = read3X4(par, DRAMControl);
+ if (!is_oldprotect(par->chip_id))
+ tmp |= 0x10;
+ if (iscyber(par->chip_id))
tmp |= 0x20;
- write3X4(DRAMControl, tmp); /* both IO, linear enable */
-
- write3X4(InterfaceSel, read3X4(InterfaceSel) | 0x40);
- write3X4(Performance, 0x92);
- write3X4(PCIReg, 0x07); /* MMIO & PCI read and write burst enable */
+ write3X4(par, DRAMControl, tmp); /* both IO, linear enable */
+
+ write3X4(par, InterfaceSel, read3X4(par, InterfaceSel) | 0x40);
+ if (!is_xp(par->chip_id))
+ write3X4(par, Performance, read3X4(par, Performance) | 0x10);
+ /* MMIO & PCI read and write burst enable */
+ if (par->chip_id != TGUI9440 && par->chip_id != IMAGE975)
+ write3X4(par, PCIReg, read3X4(par, PCIReg) | 0x06);
+
+ vga_mm_wseq(par->io_virt, 0, 3);
+ vga_mm_wseq(par->io_virt, 1, 1); /* set char clock 8 dots wide */
+ /* enable 4 maps because needed in chain4 mode */
+ vga_mm_wseq(par->io_virt, 2, 0x0F);
+ vga_mm_wseq(par->io_virt, 3, 0);
+ vga_mm_wseq(par->io_virt, 4, 0x0E); /* memory mode enable bitmaps ?? */
/* convert from picoseconds to kHz */
vclk = PICOS2KHZ(info->var.pixclock);
- if (bpp == 32)
+
+ /* divide clock by 2 if 32bpp chain4 mode display and CPU path */
+ tmp = read3CE(par, MiscExtFunc) & 0xF0;
+ if (bpp == 32 || (par->chip_id == TGUI9440 && bpp == 16)) {
+ tmp |= 8;
vclk *= 2;
- set_vclk(vclk);
-
- write3C4(0, 3);
- write3C4(1, 1); /* set char clock 8 dots wide */
- write3C4(2, 0x0F); /* enable 4 maps because needed in chain4 mode */
- write3C4(3, 0);
- write3C4(4, 0x0E); /* memory mode enable bitmaps ?? */
-
- write3CE(MiscExtFunc, (bpp == 32) ? 0x1A : 0x12); /* divide clock by 2 if 32bpp */
- /* chain4 mode display and CPU path */
- write3CE(0x5, 0x40); /* no CGA compat, allow 256 col */
- write3CE(0x6, 0x05); /* graphics mode */
- write3CE(0x7, 0x0F); /* planes? */
-
- if (chip_id == CYBERBLADEXPAi1) {
- /* This fixes snow-effect in 32 bpp */
- write3X4(CRTHSyncStart, 0x84);
}
+ set_vclk(par, vclk);
+ write3CE(par, MiscExtFunc, tmp | 0x12);
+ write3CE(par, 0x5, 0x40); /* no CGA compat, allow 256 col */
+ write3CE(par, 0x6, 0x05); /* graphics mode */
+ write3CE(par, 0x7, 0x0F); /* planes? */
- writeAttr(0x10, 0x41); /* graphics mode and support 256 color modes */
- writeAttr(0x12, 0x0F); /* planes */
- writeAttr(0x13, 0); /* horizontal pel panning */
+ /* graphics mode and support 256 color modes */
+ writeAttr(par, 0x10, 0x41);
+ writeAttr(par, 0x12, 0x0F); /* planes */
+ writeAttr(par, 0x13, 0); /* horizontal pel panning */
/* colors */
for (tmp = 0; tmp < 0x10; tmp++)
- writeAttr(tmp, tmp);
- readb(par->io_virt + CRT + 0x0A); /* flip-flop to index */
- t_outb(0x20, 0x3C0); /* enable attr */
+ writeAttr(par, tmp, tmp);
+ fb_readb(par->io_virt + VGA_IS1_RC); /* flip-flop to index */
+ t_outb(par, 0x20, VGA_ATT_W); /* enable attr */
switch (bpp) {
case 8:
tmp = 0;
break;
- case 15:
- tmp = 0x10;
- break;
case 16:
tmp = 0x30;
break;
@@ -1063,19 +1216,23 @@ static int tridentfb_set_par(struct fb_info *info)
break;
}
- t_inb(0x3C8);
- t_inb(0x3C6);
- t_inb(0x3C6);
- t_inb(0x3C6);
- t_inb(0x3C6);
- t_outb(tmp, 0x3C6);
- t_inb(0x3C8);
-
- if (flatpanel)
- set_number_of_lines(info->var.yres);
- set_lwidth(info->var.xres * bpp / (4 * 16));
+ t_inb(par, VGA_PEL_IW);
+ t_inb(par, VGA_PEL_MSK);
+ t_inb(par, VGA_PEL_MSK);
+ t_inb(par, VGA_PEL_MSK);
+ t_inb(par, VGA_PEL_MSK);
+ t_outb(par, tmp, VGA_PEL_MSK);
+ t_inb(par, VGA_PEL_IW);
+
+ if (par->flatpanel)
+ set_number_of_lines(par, info->var.yres);
+ info->fix.line_length = info->var.xres_virtual * bpp / 8;
+ set_lwidth(par, info->fix.line_length / 8);
+
+ if (!(info->flags & FBINFO_HWACCEL_DISABLED))
+ par->init_accel(par, info->var.xres_virtual, bpp);
+
info->fix.visual = (bpp == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
- info->fix.line_length = info->var.xres * (bpp >> 3);
info->cmap.len = (bpp == 8) ? 256 : 16;
debug("exit\n");
return 0;
@@ -1087,17 +1244,18 @@ static int tridentfb_setcolreg(unsigned regno, unsigned red, unsigned green,
struct fb_info *info)
{
int bpp = info->var.bits_per_pixel;
+ struct tridentfb_par *par = info->par;
if (regno >= info->cmap.len)
return 1;
if (bpp == 8) {
- t_outb(0xFF, 0x3C6);
- t_outb(regno, 0x3C8);
+ t_outb(par, 0xFF, VGA_PEL_MSK);
+ t_outb(par, regno, VGA_PEL_IW);
- t_outb(red >> 10, 0x3C9);
- t_outb(green >> 10, 0x3C9);
- t_outb(blue >> 10, 0x3C9);
+ t_outb(par, red >> 10, VGA_PEL_D);
+ t_outb(par, green >> 10, VGA_PEL_D);
+ t_outb(par, blue >> 10, VGA_PEL_D);
} else if (regno < 16) {
if (bpp == 16) { /* RGB 565 */
@@ -1108,28 +1266,28 @@ static int tridentfb_setcolreg(unsigned regno, unsigned red, unsigned green,
col |= col << 16;
((u32 *)(info->pseudo_palette))[regno] = col;
} else if (bpp == 32) /* ARGB 8888 */
- ((u32*)info->pseudo_palette)[regno] =
+ ((u32 *)info->pseudo_palette)[regno] =
((transp & 0xFF00) << 16) |
((red & 0xFF00) << 8) |
((green & 0xFF00)) |
((blue & 0xFF00) >> 8);
}
-/* debug("exit\n"); */
return 0;
}
-/* Try blanking the screen.For flat panels it does nothing */
+/* Try blanking the screen. For flat panels it does nothing */
static int tridentfb_blank(int blank_mode, struct fb_info *info)
{
unsigned char PMCont, DPMSCont;
+ struct tridentfb_par *par = info->par;
debug("enter\n");
- if (flatpanel)
+ if (par->flatpanel)
return 0;
- t_outb(0x04, 0x83C8); /* Read DPMS Control */
- PMCont = t_inb(0x83C6) & 0xFC;
- DPMSCont = read3CE(PowerStatus) & 0xFC;
+ t_outb(par, 0x04, 0x83C8); /* Read DPMS Control */
+ PMCont = t_inb(par, 0x83C6) & 0xFC;
+ DPMSCont = read3CE(par, PowerStatus) & 0xFC;
switch (blank_mode) {
case FB_BLANK_UNBLANK:
/* Screen: On, HSync: On, VSync: On */
@@ -1155,9 +1313,9 @@ static int tridentfb_blank(int blank_mode, struct fb_info *info)
break;
}
- write3CE(PowerStatus, DPMSCont);
- t_outb(4, 0x83C8);
- t_outb(PMCont, 0x83C6);
+ write3CE(par, PowerStatus, DPMSCont);
+ t_outb(par, 4, 0x83C8);
+ t_outb(par, PMCont, 0x83C6);
debug("exit\n");
@@ -1174,33 +1332,46 @@ static struct fb_ops tridentfb_ops = {
.fb_set_par = tridentfb_set_par,
.fb_fillrect = tridentfb_fillrect,
.fb_copyarea = tridentfb_copyarea,
- .fb_imageblit = cfb_imageblit,
+ .fb_imageblit = tridentfb_imageblit,
+#ifdef CONFIG_FB_TRIDENT_ACCEL
+ .fb_sync = tridentfb_sync,
+#endif
};
-static int __devinit trident_pci_probe(struct pci_dev * dev,
- const struct pci_device_id * id)
+static int __devinit trident_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
{
int err;
unsigned char revision;
+ struct fb_info *info;
+ struct tridentfb_par *default_par;
+ int chip3D;
+ int chip_id;
err = pci_enable_device(dev);
if (err)
return err;
- chip_id = id->device;
+ info = framebuffer_alloc(sizeof(struct tridentfb_par), &dev->dev);
+ if (!info)
+ return -ENOMEM;
+ default_par = info->par;
- if (chip_id == CYBERBLADEi1)
- output("*** Please do use cyblafb, Cyberblade/i1 support "
- "will soon be removed from tridentfb!\n");
+ chip_id = id->device;
+#ifndef CONFIG_FB_TRIDENT_ACCEL
+ noaccel = 1;
+#endif
/* If PCI id is 0x9660 then further detect chip type */
if (chip_id == TGUI9660) {
- outb(RevisionID, 0x3C4);
- revision = inb(0x3C5);
+ revision = vga_io_rseq(RevisionID);
switch (revision) {
+ case 0x21:
+ chip_id = PROVIDIA9685;
+ break;
case 0x22:
case 0x23:
chip_id = CYBER9397;
@@ -1229,123 +1400,170 @@ static int __devinit trident_pci_probe(struct pci_dev * dev,
}
chip3D = is3Dchip(chip_id);
- chipcyber = iscyber(chip_id);
if (is_xp(chip_id)) {
- acc = &accel_xp;
+ default_par->init_accel = xp_init_accel;
+ default_par->wait_engine = xp_wait_engine;
+ default_par->fill_rect = xp_fill_rect;
+ default_par->copy_rect = xp_copy_rect;
+ tridentfb_fix.accel = FB_ACCEL_TRIDENT_BLADEXP;
} else if (is_blade(chip_id)) {
- acc = &accel_blade;
- } else {
- acc = &accel_image;
+ default_par->init_accel = blade_init_accel;
+ default_par->wait_engine = blade_wait_engine;
+ default_par->fill_rect = blade_fill_rect;
+ default_par->copy_rect = blade_copy_rect;
+ default_par->image_blit = blade_image_blit;
+ tridentfb_fix.accel = FB_ACCEL_TRIDENT_BLADE3D;
+ } else if (chip3D) { /* 3DImage family left */
+ default_par->init_accel = image_init_accel;
+ default_par->wait_engine = image_wait_engine;
+ default_par->fill_rect = image_fill_rect;
+ default_par->copy_rect = image_copy_rect;
+ tridentfb_fix.accel = FB_ACCEL_TRIDENT_3DIMAGE;
+ } else { /* TGUI 9440/96XX family */
+ default_par->init_accel = tgui_init_accel;
+ default_par->wait_engine = xp_wait_engine;
+ default_par->fill_rect = tgui_fill_rect;
+ default_par->copy_rect = tgui_copy_rect;
+ tridentfb_fix.accel = FB_ACCEL_TRIDENT_TGUI;
}
- /* acceleration is on by default for 3D chips */
- defaultaccel = chip3D && !noaccel;
-
- fb_info.par = &default_par;
+ default_par->chip_id = chip_id;
/* setup MMIO region */
tridentfb_fix.mmio_start = pci_resource_start(dev, 1);
- tridentfb_fix.mmio_len = chip3D ? 0x20000 : 0x10000;
+ tridentfb_fix.mmio_len = pci_resource_len(dev, 1);
- if (!request_mem_region(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len, "tridentfb")) {
+ if (!request_mem_region(tridentfb_fix.mmio_start,
+ tridentfb_fix.mmio_len, "tridentfb")) {
debug("request_region failed!\n");
+ framebuffer_release(info);
return -1;
}
- default_par.io_virt = ioremap_nocache(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len);
+ default_par->io_virt = ioremap_nocache(tridentfb_fix.mmio_start,
+ tridentfb_fix.mmio_len);
- if (!default_par.io_virt) {
+ if (!default_par->io_virt) {
debug("ioremap failed\n");
err = -1;
goto out_unmap1;
}
- enable_mmio();
+ enable_mmio(default_par);
/* setup framebuffer memory */
tridentfb_fix.smem_start = pci_resource_start(dev, 0);
- tridentfb_fix.smem_len = get_memsize();
+ tridentfb_fix.smem_len = get_memsize(default_par);
- if (!request_mem_region(tridentfb_fix.smem_start, tridentfb_fix.smem_len, "tridentfb")) {
+ if (!request_mem_region(tridentfb_fix.smem_start,
+ tridentfb_fix.smem_len, "tridentfb")) {
debug("request_mem_region failed!\n");
- disable_mmio();
+ disable_mmio(info->par);
err = -1;
goto out_unmap1;
}
- fb_info.screen_base = ioremap_nocache(tridentfb_fix.smem_start,
- tridentfb_fix.smem_len);
+ info->screen_base = ioremap_nocache(tridentfb_fix.smem_start,
+ tridentfb_fix.smem_len);
- if (!fb_info.screen_base) {
+ if (!info->screen_base) {
debug("ioremap failed\n");
err = -1;
goto out_unmap2;
}
- output("%s board found\n", pci_name(dev));
- displaytype = get_displaytype();
+ default_par->flatpanel = is_flatpanel(default_par);
- if (flatpanel)
- nativex = get_nativex();
+ if (default_par->flatpanel)
+ nativex = get_nativex(default_par);
- fb_info.fix = tridentfb_fix;
- fb_info.fbops = &tridentfb_ops;
+ info->fix = tridentfb_fix;
+ info->fbops = &tridentfb_ops;
+ info->pseudo_palette = default_par->pseudo_pal;
+ info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
+ if (!noaccel && default_par->init_accel) {
+ info->flags &= ~FBINFO_HWACCEL_DISABLED;
+ info->flags |= FBINFO_HWACCEL_COPYAREA;
+ info->flags |= FBINFO_HWACCEL_FILLRECT;
+ } else
+ info->flags |= FBINFO_HWACCEL_DISABLED;
- fb_info.flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
-#ifdef CONFIG_FB_TRIDENT_ACCEL
- fb_info.flags |= FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT;
-#endif
- fb_info.pseudo_palette = pseudo_pal;
+ info->pixmap.addr = kmalloc(4096, GFP_KERNEL);
+ if (!info->pixmap.addr) {
+ err = -ENOMEM;
+ goto out_unmap2;
+ }
+
+ info->pixmap.size = 4096;
+ info->pixmap.buf_align = 4;
+ info->pixmap.scan_align = 1;
+ info->pixmap.access_align = 32;
+ info->pixmap.flags = FB_PIXMAP_SYSTEM;
- if (!fb_find_mode(&default_var, &fb_info,
+ if (default_par->image_blit) {
+ info->flags |= FBINFO_HWACCEL_IMAGEBLIT;
+ info->pixmap.scan_align = 4;
+ }
+
+ if (noaccel) {
+ printk(KERN_DEBUG "disabling acceleration\n");
+ info->flags |= FBINFO_HWACCEL_DISABLED;
+ info->pixmap.scan_align = 1;
+ }
+
+ if (!fb_find_mode(&info->var, info,
mode_option, NULL, 0, NULL, bpp)) {
err = -EINVAL;
goto out_unmap2;
}
- err = fb_alloc_cmap(&fb_info.cmap, 256, 0);
+ err = fb_alloc_cmap(&info->cmap, 256, 0);
if (err < 0)
goto out_unmap2;
- if (defaultaccel && acc)
- default_var.accel_flags |= FB_ACCELF_TEXT;
- else
- default_var.accel_flags &= ~FB_ACCELF_TEXT;
- default_var.activate |= FB_ACTIVATE_NOW;
- fb_info.var = default_var;
- fb_info.device = &dev->dev;
- if (register_framebuffer(&fb_info) < 0) {
- printk(KERN_ERR "tridentfb: could not register Trident framebuffer\n");
- fb_dealloc_cmap(&fb_info.cmap);
+ info->var.activate |= FB_ACTIVATE_NOW;
+ info->device = &dev->dev;
+ if (register_framebuffer(info) < 0) {
+ printk(KERN_ERR "tridentfb: could not register framebuffer\n");
+ fb_dealloc_cmap(&info->cmap);
err = -EINVAL;
goto out_unmap2;
}
output("fb%d: %s frame buffer device %dx%d-%dbpp\n",
- fb_info.node, fb_info.fix.id, default_var.xres,
- default_var.yres, default_var.bits_per_pixel);
+ info->node, info->fix.id, info->var.xres,
+ info->var.yres, info->var.bits_per_pixel);
+
+ pci_set_drvdata(dev, info);
return 0;
out_unmap2:
- if (fb_info.screen_base)
- iounmap(fb_info.screen_base);
+ kfree(info->pixmap.addr);
+ if (info->screen_base)
+ iounmap(info->screen_base);
release_mem_region(tridentfb_fix.smem_start, tridentfb_fix.smem_len);
- disable_mmio();
+ disable_mmio(info->par);
out_unmap1:
- if (default_par.io_virt)
- iounmap(default_par.io_virt);
+ if (default_par->io_virt)
+ iounmap(default_par->io_virt);
release_mem_region(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len);
+ framebuffer_release(info);
return err;
}
static void __devexit trident_pci_remove(struct pci_dev *dev)
{
- struct tridentfb_par *par = (struct tridentfb_par*)fb_info.par;
- unregister_framebuffer(&fb_info);
+ struct fb_info *info = pci_get_drvdata(dev);
+ struct tridentfb_par *par = info->par;
+
+ unregister_framebuffer(info);
iounmap(par->io_virt);
- iounmap(fb_info.screen_base);
+ iounmap(info->screen_base);
release_mem_region(tridentfb_fix.smem_start, tridentfb_fix.smem_len);
release_mem_region(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len);
+ pci_set_drvdata(dev, NULL);
+ kfree(info->pixmap.addr);
+ framebuffer_release(info);
}
/* List of boards that we are trying to support */
@@ -1358,6 +1576,7 @@ static struct pci_device_id trident_devices[] = {
{PCI_VENDOR_ID_TRIDENT, CYBERBLADEAi1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_TRIDENT, CYBERBLADEAi1D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_TRIDENT, CYBERBLADEE4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_TRIDENT, TGUI9440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_TRIDENT, TGUI9660, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_TRIDENT, IMAGE975, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_TRIDENT, IMAGE985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
@@ -1399,9 +1618,9 @@ static int __init tridentfb_setup(char *options)
if (!strncmp(opt, "noaccel", 7))
noaccel = 1;
else if (!strncmp(opt, "fp", 2))
- displaytype = DISPLAY_FP;
+ fp = 1;
else if (!strncmp(opt, "crt", 3))
- displaytype = DISPLAY_CRT;
+ fp = 0;
else if (!strncmp(opt, "bpp=", 4))
bpp = simple_strtoul(opt + 4, NULL, 0);
else if (!strncmp(opt, "center", 6))
@@ -1430,7 +1649,6 @@ static int __init tridentfb_init(void)
return -ENODEV;
tridentfb_setup(option);
#endif
- output("Trident framebuffer %s initializing\n", VERSION);
return pci_register_driver(&tridentfb_pci_driver);
}
diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c
index cdbb56edb6c..50744229c7a 100644
--- a/drivers/video/uvesafb.c
+++ b/drivers/video/uvesafb.c
@@ -2054,8 +2054,8 @@ MODULE_PARM_DESC(maxhf,
module_param(maxvf, ushort, 0);
MODULE_PARM_DESC(maxvf,
"Maximum vertical frequency [Hz], overrides EDID data");
-module_param_named(mode, mode_option, charp, 0);
-MODULE_PARM_DESC(mode,
+module_param(mode_option, charp, 0);
+MODULE_PARM_DESC(mode_option,
"Specify initial video mode as \"<xres>x<yres>[-<bpp>][@<refresh>]\"");
module_param(vbemode, ushort, 0);
MODULE_PARM_DESC(vbemode,
diff --git a/drivers/video/vfb.c b/drivers/video/vfb.c
index 072638a9528..93fe08d6c78 100644
--- a/drivers/video/vfb.c
+++ b/drivers/video/vfb.c
@@ -443,19 +443,29 @@ static int vfb_mmap(struct fb_info *info,
}
#ifndef MODULE
+/*
+ * The virtual framebuffer driver is only enabled if explicitly
+ * requested by passing 'video=vfb:' (or any actual options).
+ */
static int __init vfb_setup(char *options)
{
char *this_opt;
+ vfb_enable = 0;
+
+ if (!options)
+ return 1;
+
vfb_enable = 1;
- if (!options || !*options)
+ if (!*options)
return 1;
while ((this_opt = strsep(&options, ",")) != NULL) {
if (!*this_opt)
continue;
- if (!strncmp(this_opt, "disable", 7))
+ /* Test disable for backwards compatibility */
+ if (!strcmp(this_opt, "disable"))
vfb_enable = 0;
}
return 1;
diff --git a/drivers/video/vga16fb.c b/drivers/video/vga16fb.c
index 9b3c5923365..e31bca8a0cb 100644
--- a/drivers/video/vga16fb.c
+++ b/drivers/video/vga16fb.c
@@ -26,18 +26,6 @@
#include <asm/io.h>
#include <video/vga.h>
-#define GRAPHICS_ADDR_REG VGA_GFX_I /* Graphics address register. */
-#define GRAPHICS_DATA_REG VGA_GFX_D /* Graphics data register. */
-
-#define SET_RESET_INDEX VGA_GFX_SR_VALUE /* Set/Reset Register index. */
-#define ENABLE_SET_RESET_INDEX VGA_GFX_SR_ENABLE /* Enable Set/Reset Register index. */
-#define DATA_ROTATE_INDEX VGA_GFX_DATA_ROTATE /* Data Rotate Register index. */
-#define GRAPHICS_MODE_INDEX VGA_GFX_MODE /* Graphics Mode Register index. */
-#define BIT_MASK_INDEX VGA_GFX_BIT_MASK /* Bit Mask Register index. */
-
-#define dac_reg (VGA_PEL_IW)
-#define dac_val (VGA_PEL_D)
-
#define VGA_FB_PHYS 0xA0000
#define VGA_FB_PHYS_LEN 65536
@@ -108,7 +96,7 @@ static struct fb_fix_screeninfo vga16fb_fix __initdata = {
.visual = FB_VISUAL_PSEUDOCOLOR,
.xpanstep = 8,
.ypanstep = 1,
- .line_length = 640/8,
+ .line_length = 640 / 8,
.accel = FB_ACCEL_NONE
};
@@ -135,23 +123,22 @@ static inline int setmode(int mode)
{
int oldmode;
- vga_io_w(GRAPHICS_ADDR_REG, GRAPHICS_MODE_INDEX);
- oldmode = vga_io_r(GRAPHICS_DATA_REG);
- vga_io_w(GRAPHICS_DATA_REG, mode);
+ oldmode = vga_io_rgfx(VGA_GFX_MODE);
+ vga_io_w(VGA_GFX_D, mode);
return oldmode;
}
/* Select the Bit Mask Register and return its value. */
static inline int selectmask(void)
{
- return vga_io_rgfx(BIT_MASK_INDEX);
+ return vga_io_rgfx(VGA_GFX_BIT_MASK);
}
/* Set the value of the Bit Mask Register. It must already have been
selected with selectmask(). */
static inline void setmask(int mask)
{
- vga_io_w(GRAPHICS_DATA_REG, mask);
+ vga_io_w(VGA_GFX_D, mask);
}
/* Set the Data Rotate Register and return its old value.
@@ -161,9 +148,8 @@ static inline int setop(int op)
{
int oldop;
- vga_io_w(GRAPHICS_ADDR_REG, DATA_ROTATE_INDEX);
- oldop = vga_io_r(GRAPHICS_DATA_REG);
- vga_io_w(GRAPHICS_DATA_REG, op);
+ oldop = vga_io_rgfx(VGA_GFX_DATA_ROTATE);
+ vga_io_w(VGA_GFX_D, op);
return oldop;
}
@@ -173,9 +159,8 @@ static inline int setsr(int sr)
{
int oldsr;
- vga_io_w(GRAPHICS_ADDR_REG, ENABLE_SET_RESET_INDEX);
- oldsr = vga_io_r(GRAPHICS_DATA_REG);
- vga_io_w(GRAPHICS_DATA_REG, sr);
+ oldsr = vga_io_rgfx(VGA_GFX_SR_ENABLE);
+ vga_io_w(VGA_GFX_D, sr);
return oldsr;
}
@@ -184,22 +169,21 @@ static inline int setcolor(int color)
{
int oldcolor;
- vga_io_w(GRAPHICS_ADDR_REG, SET_RESET_INDEX);
- oldcolor = vga_io_r(GRAPHICS_DATA_REG);
- vga_io_w(GRAPHICS_DATA_REG, color);
+ oldcolor = vga_io_rgfx(VGA_GFX_SR_VALUE);
+ vga_io_w(VGA_GFX_D, color);
return oldcolor;
}
/* Return the value in the Graphics Address Register. */
static inline int getindex(void)
{
- return vga_io_r(GRAPHICS_ADDR_REG);
+ return vga_io_r(VGA_GFX_I);
}
/* Set the value in the Graphics Address Register. */
static inline void setindex(int index)
{
- vga_io_w(GRAPHICS_ADDR_REG, index);
+ vga_io_w(VGA_GFX_I, index);
}
static void vga16fb_pan_var(struct fb_info *info,
@@ -672,10 +656,10 @@ static void ega16_setpalette(int regno, unsigned red, unsigned green, unsigned b
static void vga16_setpalette(int regno, unsigned red, unsigned green, unsigned blue)
{
- outb(regno, dac_reg);
- outb(red >> 10, dac_val);
- outb(green >> 10, dac_val);
- outb(blue >> 10, dac_val);
+ outb(regno, VGA_PEL_IW);
+ outb(red >> 10, VGA_PEL_D);
+ outb(green >> 10, VGA_PEL_D);
+ outb(blue >> 10, VGA_PEL_D);
}
static int vga16fb_setcolreg(unsigned regno, unsigned red, unsigned green,
@@ -719,28 +703,15 @@ static int vga16fb_pan_display(struct fb_var_screeninfo *var,
blanking code was originally by Huang shi chao, and modified by
Christoph Rimek (chrimek@toppoint.de) and todd j. derr
(tjd@barefoot.org) for Linux. */
-#define attrib_port VGA_ATC_IW
-#define seq_port_reg VGA_SEQ_I
-#define seq_port_val VGA_SEQ_D
-#define gr_port_reg VGA_GFX_I
-#define gr_port_val VGA_GFX_D
-#define video_misc_rd VGA_MIS_R
-#define video_misc_wr VGA_MIS_W
-#define vga_video_port_reg VGA_CRT_IC
-#define vga_video_port_val VGA_CRT_DC
static void vga_vesa_blank(struct vga16fb_par *par, int mode)
{
- unsigned char SeqCtrlIndex;
- unsigned char CrtCtrlIndex;
+ unsigned char SeqCtrlIndex = vga_io_r(VGA_SEQ_I);
+ unsigned char CrtCtrlIndex = vga_io_r(VGA_CRT_IC);
- //cli();
- SeqCtrlIndex = vga_io_r(seq_port_reg);
- CrtCtrlIndex = vga_io_r(vga_video_port_reg);
-
/* save original values of VGA controller registers */
if(!par->vesa_blanked) {
- par->vga_state.CrtMiscIO = vga_io_r(video_misc_rd);
+ par->vga_state.CrtMiscIO = vga_io_r(VGA_MIS_R);
//sti();
par->vga_state.HorizontalTotal = vga_io_rcrt(0x00); /* HorizontalTotal */
@@ -756,12 +727,11 @@ static void vga_vesa_blank(struct vga16fb_par *par, int mode)
/* assure that video is enabled */
/* "0x20" is VIDEO_ENABLE_bit in register 01 of sequencer */
- //cli();
vga_io_wseq(0x01, par->vga_state.ClockingMode | 0x20);
/* test for vertical retrace in process.... */
if ((par->vga_state.CrtMiscIO & 0x80) == 0x80)
- vga_io_w(video_misc_wr, par->vga_state.CrtMiscIO & 0xef);
+ vga_io_w(VGA_MIS_W, par->vga_state.CrtMiscIO & 0xef);
/*
* Set <End of vertical retrace> to minimum (0) and
@@ -769,12 +739,10 @@ static void vga_vesa_blank(struct vga16fb_par *par, int mode)
* Result: turn off vertical sync (VSync) pulse.
*/
if (mode & FB_BLANK_VSYNC_SUSPEND) {
- outb_p(0x10,vga_video_port_reg); /* StartVertRetrace */
- outb_p(0xff,vga_video_port_val); /* maximum value */
- outb_p(0x11,vga_video_port_reg); /* EndVertRetrace */
- outb_p(0x40,vga_video_port_val); /* minimum (bits 0..3) */
- outb_p(0x07,vga_video_port_reg); /* Overflow */
- outb_p(par->vga_state.Overflow | 0x84,vga_video_port_val); /* bits 9,10 of vert. retrace */
+ vga_io_wcrt(VGA_CRTC_V_SYNC_START, 0xff);
+ vga_io_wcrt(VGA_CRTC_V_SYNC_END, 0x40);
+ /* bits 9,10 of vert. retrace */
+ vga_io_wcrt(VGA_CRTC_OVERFLOW, par->vga_state.Overflow | 0x84);
}
if (mode & FB_BLANK_HSYNC_SUSPEND) {
@@ -783,29 +751,22 @@ static void vga_vesa_blank(struct vga16fb_par *par, int mode)
* <Start of horizontal Retrace> to maximum
* Result: turn off horizontal sync (HSync) pulse.
*/
- outb_p(0x04,vga_video_port_reg); /* StartHorizRetrace */
- outb_p(0xff,vga_video_port_val); /* maximum */
- outb_p(0x05,vga_video_port_reg); /* EndHorizRetrace */
- outb_p(0x00,vga_video_port_val); /* minimum (0) */
+ vga_io_wcrt(VGA_CRTC_H_SYNC_START, 0xff);
+ vga_io_wcrt(VGA_CRTC_H_SYNC_END, 0x00);
}
/* restore both index registers */
- outb_p(SeqCtrlIndex,seq_port_reg);
- outb_p(CrtCtrlIndex,vga_video_port_reg);
- //sti();
+ outb_p(SeqCtrlIndex, VGA_SEQ_I);
+ outb_p(CrtCtrlIndex, VGA_CRT_IC);
}
static void vga_vesa_unblank(struct vga16fb_par *par)
{
- unsigned char SeqCtrlIndex;
- unsigned char CrtCtrlIndex;
+ unsigned char SeqCtrlIndex = vga_io_r(VGA_SEQ_I);
+ unsigned char CrtCtrlIndex = vga_io_r(VGA_CRT_IC);
- //cli();
- SeqCtrlIndex = vga_io_r(seq_port_reg);
- CrtCtrlIndex = vga_io_r(vga_video_port_reg);
-
/* restore original values of VGA controller registers */
- vga_io_w(video_misc_wr, par->vga_state.CrtMiscIO);
+ vga_io_w(VGA_MIS_W, par->vga_state.CrtMiscIO);
/* HorizontalTotal */
vga_io_wcrt(0x00, par->vga_state.HorizontalTotal);
@@ -827,9 +788,8 @@ static void vga_vesa_unblank(struct vga16fb_par *par)
vga_io_wseq(0x01, par->vga_state.ClockingMode);
/* restore index/control registers */
- vga_io_w(seq_port_reg, SeqCtrlIndex);
- vga_io_w(vga_video_port_reg, CrtCtrlIndex);
- //sti();
+ vga_io_w(VGA_SEQ_I, SeqCtrlIndex);
+ vga_io_w(VGA_CRT_IC, CrtCtrlIndex);
}
static void vga_pal_blank(void)
@@ -837,10 +797,10 @@ static void vga_pal_blank(void)
int i;
for (i=0; i<16; i++) {
- outb_p (i, dac_reg) ;
- outb_p (0, dac_val) ;
- outb_p (0, dac_val) ;
- outb_p (0, dac_val) ;
+ outb_p(i, VGA_PEL_IW);
+ outb_p(0, VGA_PEL_D);
+ outb_p(0, VGA_PEL_D);
+ outb_p(0, VGA_PEL_D);
}
}
@@ -1087,12 +1047,15 @@ static void vga16fb_copyarea(struct fb_info *info, const struct fb_copyarea *are
width = x2 - dx;
height = y2 - dy;
+ if (sx + dx < old_dx || sy + dy < old_dy)
+ return;
+
/* update sx1,sy1 */
sx += (dx - old_dx);
sy += (dy - old_dy);
/* the source must be completely inside the virtual screen */
- if (sx < 0 || sy < 0 || (sx + width) > vxres || (sy + height) > vyres)
+ if (sx + width > vxres || sy + height > vyres)
return;
switch (info->fix.type) {
@@ -1482,6 +1445,7 @@ static void __exit vga16fb_exit(void)
platform_driver_unregister(&vga16fb_driver);
}
+MODULE_DESCRIPTION("Legacy VGA framebuffer device driver");
MODULE_LICENSE("GPL");
module_init(vga16fb_init);
module_exit(vga16fb_exit);
diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c
index 7084e7e146c..5b78fd0aff0 100644
--- a/drivers/virtio/virtio.c
+++ b/drivers/virtio/virtio.c
@@ -71,13 +71,6 @@ static int virtio_uevent(struct device *_dv, struct kobj_uevent_env *env)
dev->id.device, dev->id.vendor);
}
-static struct bus_type virtio_bus = {
- .name = "virtio",
- .match = virtio_dev_match,
- .dev_attrs = virtio_dev_attrs,
- .uevent = virtio_uevent,
-};
-
static void add_status(struct virtio_device *dev, unsigned status)
{
dev->config->set_status(dev, dev->config->get_status(dev) | status);
@@ -120,12 +113,16 @@ static int virtio_dev_probe(struct device *_d)
set_bit(f, dev->features);
}
+ /* Transport features always preserved to pass to finalize_features. */
+ for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++)
+ if (device_features & (1 << i))
+ set_bit(i, dev->features);
+
err = drv->probe(dev);
if (err)
add_status(dev, VIRTIO_CONFIG_S_FAILED);
else {
- /* They should never have set feature bits beyond 32 */
- dev->config->set_features(dev, dev->features[0]);
+ dev->config->finalize_features(dev);
add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK);
}
return err;
@@ -147,13 +144,20 @@ static int virtio_dev_remove(struct device *_d)
return 0;
}
+static struct bus_type virtio_bus = {
+ .name = "virtio",
+ .match = virtio_dev_match,
+ .dev_attrs = virtio_dev_attrs,
+ .uevent = virtio_uevent,
+ .probe = virtio_dev_probe,
+ .remove = virtio_dev_remove,
+};
+
int register_virtio_driver(struct virtio_driver *driver)
{
/* Catch this early. */
BUG_ON(driver->feature_table_size && !driver->feature_table);
driver->driver.bus = &virtio_bus;
- driver->driver.probe = virtio_dev_probe;
- driver->driver.remove = virtio_dev_remove;
return driver_register(&driver->driver);
}
EXPORT_SYMBOL_GPL(register_virtio_driver);
diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c
index eae7236310e..c7dc37c7cce 100644
--- a/drivers/virtio/virtio_pci.c
+++ b/drivers/virtio/virtio_pci.c
@@ -94,12 +94,17 @@ static u32 vp_get_features(struct virtio_device *vdev)
return ioread32(vp_dev->ioaddr + VIRTIO_PCI_HOST_FEATURES);
}
-/* virtio config->set_features() implementation */
-static void vp_set_features(struct virtio_device *vdev, u32 features)
+/* virtio config->finalize_features() implementation */
+static void vp_finalize_features(struct virtio_device *vdev)
{
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
- iowrite32(features, vp_dev->ioaddr + VIRTIO_PCI_GUEST_FEATURES);
+ /* Give virtio_ring a chance to accept features. */
+ vring_transport_features(vdev);
+
+ /* We only support 32 feature bits. */
+ BUILD_BUG_ON(ARRAY_SIZE(vdev->features) != 1);
+ iowrite32(vdev->features[0], vp_dev->ioaddr+VIRTIO_PCI_GUEST_FEATURES);
}
/* virtio config->get() implementation */
@@ -297,7 +302,7 @@ static struct virtio_config_ops virtio_pci_config_ops = {
.find_vq = vp_find_vq,
.del_vq = vp_del_vq,
.get_features = vp_get_features,
- .set_features = vp_set_features,
+ .finalize_features = vp_finalize_features,
};
/* the PCI probing function */
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index 72bf8bc0901..6eb5303fed1 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -18,6 +18,7 @@
*/
#include <linux/virtio.h>
#include <linux/virtio_ring.h>
+#include <linux/virtio_config.h>
#include <linux/device.h>
#ifdef DEBUG
@@ -87,8 +88,11 @@ static int vring_add_buf(struct virtqueue *_vq,
if (vq->num_free < out + in) {
pr_debug("Can't add buf len %i - avail = %i\n",
out + in, vq->num_free);
- /* We notify *even if* VRING_USED_F_NO_NOTIFY is set here. */
- vq->notify(&vq->vq);
+ /* FIXME: for historical reasons, we force a notify here if
+ * there are outgoing parts to the buffer. Presumably the
+ * host should service the ring ASAP. */
+ if (out)
+ vq->notify(&vq->vq);
END_USE(vq);
return -ENOSPC;
}
@@ -320,4 +324,19 @@ void vring_del_virtqueue(struct virtqueue *vq)
}
EXPORT_SYMBOL_GPL(vring_del_virtqueue);
+/* Manipulates transport-specific feature bits. */
+void vring_transport_features(struct virtio_device *vdev)
+{
+ unsigned int i;
+
+ for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++) {
+ switch (i) {
+ default:
+ /* We don't understand this bit. */
+ clear_bit(i, vdev->features);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(vring_transport_features);
+
MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index ccb78f66c2b..48399e134c0 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -788,8 +788,6 @@ config WATCHDOG_RIO
machines. The watchdog timeout period is normally one minute but
can be changed with a boot-time parameter.
-# V850 Architecture
-
# XTENSA Architecture
#
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 25b352b664d..edd305a64e6 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -119,8 +119,6 @@ obj-$(CONFIG_SH_WDT) += shwdt.o
# SPARC64 Architecture
-# V850 Architecture
-
# XTENSA Architecture
# Architecture Independant
diff --git a/drivers/zorro/zorro-sysfs.c b/drivers/zorro/zorro-sysfs.c
index 3da712cc770..5290552d2ef 100644
--- a/drivers/zorro/zorro-sysfs.c
+++ b/drivers/zorro/zorro-sysfs.c
@@ -15,7 +15,6 @@
#include <linux/zorro.h>
#include <linux/stat.h>
#include <linux/string.h>
-#include <linux/fs.h>
#include "zorro.h"