- information on EDAC - Error Detection And Correction
eisa.txt
- info on EISA bus support.
-exception.txt
- - how Linux v2.2 handles exceptions without verify_area etc.
fault-injection/
- dir with docs about the fault injection capabilities infrastructure.
fb/
0 -> resumed
(_UDC_ is the name of the USB Device Controller driver)
+
+What: /sys/devices/platform/_UDC_/gadget/gadget-lunX/nofua
+Date: July 2010
+Contact: Andy Shevchenko <andy.shevchenko@gmail.com>
+Description:
+ Show or set the reaction on the FUA (Force Unit Access) bit in
+ the SCSI WRITE(10,12) commands when a gadget in USB Mass
+ Storage mode.
+
+ Possible values are:
+ 1 -> ignore the FUA flag
+ 0 -> obey the FUA flag
alignment constraints (e.g. the alignment constraints about 64-bit
objects).
+3) Supporting multiple types of IOMMUs
+
+ If your architecture needs to support multiple types of IOMMUs, you
+ can use include/linux/asm-generic/dma-mapping-common.h. It's a
+ library to support the DMA API with multiple types of IOMMUs. Lots
+ of architectures (x86, powerpc, sh, alpha, ia64, microblaze and
+ sparc) use it. Choose one to see how it can be used. If you need to
+ support multiple types of IOMMUs in a single system, the example of
+ x86 or powerpc helps.
+
Closing
This document, and the API itself, would not be in its current
be identical to those passed in (and returned by
dma_alloc_noncoherent()).
-int
-dma_is_consistent(struct device *dev, dma_addr_t dma_handle)
-
-Returns true if the device dev is performing consistent DMA on the memory
-area pointed to by the dma_handle.
-
int
dma_get_cache_alignment(void)
PHONY += xmldocs sgmldocs psdocs pdfdocs htmldocs mandocs installmandocs cleandocs xmldoclinks
BOOKS := $(addprefix $(obj)/,$(DOCBOOKS))
-xmldocs: $(BOOKS) xmldoclinks
+xmldocs: $(BOOKS)
sgmldocs: xmldocs
PS := $(patsubst %.xml, %.ps, $(BOOKS))
pdfdocs: $(PDF)
HTML := $(sort $(patsubst %.xml, %.html, $(BOOKS)))
-htmldocs: $(HTML)
+htmldocs: $(HTML) xmldoclinks
$(call build_main_index)
$(call build_images)
) > $(dir $@).$(notdir $@).cmd
endef
-%.xml: %.tmpl FORCE
+%.xml: %.tmpl xmldoclinks FORCE
$(call if_changed_rule,docproc)
###
<title>FIFO Buffer</title>
<sect1><title>kfifo interface</title>
!Iinclude/linux/kfifo.h
-!Ekernel/kfifo.c
</sect1>
</chapter>
and LIRC_SETUP_END. Drivers can also choose to ignore these ioctls.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>LIRC_SET_WIDEBAND_RECEIVER</term>
+ <listitem>
+ <para>Some receivers are equipped with special wide band receiver which is intended
+ to be used to learn output of existing remote.
+ Calling that ioctl with (1) will enable it, and with (0) disable it.
+ This might be useful of receivers that have otherwise narrow band receiver
+ that prevents them to be used with some remotes.
+ Wide band receiver might also be more precise
+ On the other hand its disadvantage it usually reduced range of reception.
+ Note: wide band receiver might be implictly enabled if you enable
+ carrier reports. In that case it will be disabled as soon as you disable
+ carrier reports. Trying to disable wide band receiver while carrier
+ reports are active will do nothing.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</section>
<entry>r<subscript>1</subscript></entry>
<entry>r<subscript>0</subscript></entry>
</row>
+ <row id="V4L2-PIX-FMT-BGR666">
+ <entry><constant>V4L2_PIX_FMT_BGR666</constant></entry>
+ <entry>'BGRH'</entry>
+ <entry></entry>
+ <entry>b<subscript>5</subscript></entry>
+ <entry>b<subscript>4</subscript></entry>
+ <entry>b<subscript>3</subscript></entry>
+ <entry>b<subscript>2</subscript></entry>
+ <entry>b<subscript>1</subscript></entry>
+ <entry>b<subscript>0</subscript></entry>
+ <entry>g<subscript>5</subscript></entry>
+ <entry>g<subscript>4</subscript></entry>
+ <entry></entry>
+ <entry>g<subscript>3</subscript></entry>
+ <entry>g<subscript>2</subscript></entry>
+ <entry>g<subscript>1</subscript></entry>
+ <entry>g<subscript>0</subscript></entry>
+ <entry>r<subscript>5</subscript></entry>
+ <entry>r<subscript>4</subscript></entry>
+ <entry>r<subscript>3</subscript></entry>
+ <entry>r<subscript>2</subscript></entry>
+ <entry></entry>
+ <entry>r<subscript>1</subscript></entry>
+ <entry>r<subscript>0</subscript></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ </row>
<row id="V4L2-PIX-FMT-BGR24">
<entry><constant>V4L2_PIX_FMT_BGR24</constant></entry>
<entry>'BGR3'</entry>
<entry>b<subscript>1</subscript></entry>
<entry>b<subscript>0</subscript></entry>
</row>
+ <row id="V4L2-PIX-FMT-BGR666">
+ <entry><constant>V4L2_PIX_FMT_BGR666</constant></entry>
+ <entry>'BGRH'</entry>
+ <entry></entry>
+ <entry>b<subscript>5</subscript></entry>
+ <entry>b<subscript>4</subscript></entry>
+ <entry>b<subscript>3</subscript></entry>
+ <entry>b<subscript>2</subscript></entry>
+ <entry>b<subscript>1</subscript></entry>
+ <entry>b<subscript>0</subscript></entry>
+ <entry>g<subscript>5</subscript></entry>
+ <entry>g<subscript>4</subscript></entry>
+ <entry></entry>
+ <entry>g<subscript>3</subscript></entry>
+ <entry>g<subscript>2</subscript></entry>
+ <entry>g<subscript>1</subscript></entry>
+ <entry>g<subscript>0</subscript></entry>
+ <entry>r<subscript>5</subscript></entry>
+ <entry>r<subscript>4</subscript></entry>
+ <entry>r<subscript>3</subscript></entry>
+ <entry>r<subscript>2</subscript></entry>
+ <entry></entry>
+ <entry>r<subscript>1</subscript></entry>
+ <entry>r<subscript>0</subscript></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ <entry></entry>
+ </row>
<row><!-- id="V4L2-PIX-FMT-BGR24" -->
<entry><constant>V4L2_PIX_FMT_BGR24</constant></entry>
<entry>'BGR3'</entry>
Who: Rafael J. Wysocki <rjw@sisk.pl>
----------------------------
+
+What: PCI DMA unmap state API
+When: August 2012
+Why: PCI DMA unmap state API (include/linux/pci-dma.h) was replaced
+ with DMA unmap state API (DMA unmap state API can be used for
+ any bus).
+Who: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
+
+----------------------------
+
+What: DMA_xxBIT_MASK macros
+When: Jun 2011
+Why: DMA_xxBIT_MASK macros were replaced with DMA_BIT_MASK() macros.
+Who: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
+
+----------------------------
+
=======================
Squashfs is a compressed read-only filesystem for Linux.
-It uses zlib compression to compress files, inodes and directories.
+It uses zlib/lzo compression to compress files, inodes and directories.
Inodes in the system are very small and all blocks are packed to minimise
data overhead. Block sizes greater than 4K are supported up to a maximum
of 1Mbytes (default block size 128K).
Spinlock-Safe GPIO access
-------------------------
Most GPIO controllers can be accessed with memory read/write instructions.
-That doesn't need to sleep, and can safely be done from inside IRQ handlers.
-(That includes hardirq contexts on RT kernels.)
+Those don't need to sleep, and can safely be done from inside hard
+(nonthreaded) IRQ handlers and similar contexts.
-Use these calls to access such GPIOs:
+Use the following calls to access such GPIOs,
+for which gpio_cansleep() will always return false (see below):
/* GPIO INPUT: return zero or nonzero */
int gpio_get_value(unsigned gpio);
/* GPIO OUTPUT, might sleep */
void gpio_set_value_cansleep(unsigned gpio, int value);
-Other than the fact that these calls might sleep, and will not be ignored
-for GPIOs that can't be accessed from IRQ handlers, these calls act the
-same as the spinlock-safe calls.
+
+Accessing such GPIOs requires a context which may sleep, for example
+a threaded IRQ handler, and those accessors must be used instead of
+spinlock-safe accessors without the cansleep() name suffix.
+
+Other than the fact that these accessors might sleep, and will work
+on GPIOs that can't be accessed from hardIRQ handlers, these calls act
+the same as the spinlock-safe calls.
+
+ ** IN ADDITION ** calls to setup and configure such GPIOs must be made
+from contexts which may sleep, since they may need to access the GPIO
+controller chip too: (These setup calls are usually made from board
+setup or driver probe/teardown code, so this is an easy constraint.)
+
+ gpio_direction_input()
+ gpio_direction_output()
+ gpio_request()
+
+## gpio_request_one()
+## gpio_request_array()
+## gpio_free_array()
+
+ gpio_free()
+ gpio_set_debounce()
+
Claiming and Releasing GPIOs
earlycon= [KNL] Output early console device and options.
uart[8250],io,<addr>[,options]
uart[8250],mmio,<addr>[,options]
+ uart[8250],mmio32,<addr>[,options]
Start an early, polled-mode console on the 8250/16550
UART at the specified I/O port or MMIO address.
+ MMIO inter-register address stride is either 8bit (mmio)
+ or 32bit (mmio32).
The options are the same as for ttyS, above.
earlyprintk= [X86,SH,BLACKFIN]
controller
i8042.nopnp [HW] Don't use ACPIPnP / PnPBIOS to discover KBD/AUX
controllers
- i8042.panicblink=
- [HW] Frequency with which keyboard LEDs should blink
- when kernel panics (default is 0.5 sec)
i8042.reset [HW] Reset the controller during init and cleanup
i8042.unlock [HW] Unlock (ignore) the keylock
+++ /dev/null
- The Wavelan drivers saga
- ------------------------
-
- By Jean Tourrilhes <jt@hpl.hp.com>
-
- The Wavelan is a Radio network adapter designed by
-Lucent. Under this generic name is hidden quite a variety of hardware,
-and many Linux driver to support it.
- The get the full story on Wireless LANs, please consult :
- http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/
-
-"wavelan" driver (old ISA Wavelan)
-----------------
- o Config : Network device -> Wireless LAN -> AT&T WaveLAN
- o Location : .../drivers/net/wireless/wavelan*
- o in-line doc : .../drivers/net/wireless/wavelan.p.h
- o on-line doc :
- http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Wavelan.html
-
- This is the driver for the ISA version of the first generation
-of the Wavelan, now discontinued. The device is 2 Mb/s, composed of a
-Intel 82586 controller and a Lucent Modem, and is NOT 802.11 compliant.
- The driver has been tested with the following hardware :
- o Wavelan ISA 915 MHz (full length ISA card)
- o Wavelan ISA 915 MHz 2.0 (half length ISA card)
- o Wavelan ISA 2.4 GHz (full length ISA card, fixed frequency)
- o Wavelan ISA 2.4 GHz 2.0 (half length ISA card, frequency selectable)
- o Above cards with the optional DES encryption feature
-
-"wavelan_cs" driver (old Pcmcia Wavelan)
--------------------
- o Config : Network device -> PCMCIA network ->
- Pcmcia Wireless LAN -> AT&T/Lucent WaveLAN
- o Location : .../drivers/net/pcmcia/wavelan*
- o in-line doc : .../drivers/net/pcmcia/wavelan_cs.h
- o on-line doc :
- http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Wavelan.html
-
- This is the driver for the PCMCIA version of the first
-generation of the Wavelan, now discontinued. The device is 2 Mb/s,
-composed of a Intel 82593 controller (totally different from the 82586)
-and a Lucent Modem, and NOT 802.11 compatible.
- The driver has been tested with the following hardware :
- o Wavelan Pcmcia 915 MHz 2.0 (Pcmcia card + separate
- modem/antenna block)
- o Wavelan Pcmcia 2.4 GHz 2.0 (Pcmcia card + separate
- modem/antenna block)
-
-"wvlan_cs" driver (Wavelan IEEE, GPL)
------------------
- o Config : Not yet in kernel
- o Location : Pcmcia package 3.1.10+
- o on-line doc :
- http://web.archive.org/web/*/http://www.fasta.fh-dortmund.de/users/andy/wvlan/
-
- This is the driver for the current generation of Wavelan IEEE,
-which is 802.11 compatible. Depending on version, it is 2 Mb/s or 11
-Mb/s, with or without encryption, all implemented in Lucent specific
-DSP (the Hermes).
- This is a GPL full source PCMCIA driver (ISA is just a Pcmcia
-card with ISA-Pcmcia bridge).
-
-"wavelan2_cs" driver (Wavelan IEEE, binary)
---------------------
- o Config : Not yet in kernel
- o Location : ftp://sourceforge.org/pcmcia/contrib/
-
- This driver support exactly the same hardware as the previous
-driver, the main difference is that it is based on a binary library
-and supported by Lucent.
-
- I hope it clears the confusion ;-)
-
- Jean
The pcpumask describes which processors will be used to execute work
submitted to this instance in parallel. The cbcpumask defines which
-processors are allowed to use as the serialization callback processor.
+processors are allowed to be used as the serialization callback processor.
The workqueue wq is where the work will actually be done; it should be
a multithreaded queue, naturally.
Note: Padata maintains two kinds of cpumasks internally. The user supplied
cpumasks, submitted by padata_alloc/padata_alloc_possible and the 'usable'
-cpumasks. The usable cpumasks are always the subset of active cpus in the
-user supplied cpumasks, these are the cpumasks padata actually use. So
-it is legal to supply a cpumask to padata that contains offline cpus.
-Once a offline cpu in the user supplied cpumask comes online, padata
+cpumasks. The usable cpumasks are always a subset of active CPUs in the
+user supplied cpumasks; these are the cpumasks padata actually uses. So
+it is legal to supply a cpumask to padata that contains offline CPUs.
+Once an offline CPU in the user supplied cpumask comes online, padata
is going to use it.
There are functions for enabling and disabling the instance:
These functions are setting or clearing the "PADATA_INIT" flag;
if that flag is not set, other functions will refuse to work.
padata_start returns zero on success (flag set) or -EINVAL if the
-padata cpumask contains no active cpu (flag not set).
+padata cpumask contains no active CPU (flag not set).
padata_stop clears the flag and blocks until the padata instance
is unused.
It's possible to change both cpumasks of a padata instance with
padata_set_cpumasks by specifying the cpumasks for parallel execution (pcpumask)
-and for the serial callback function (cbcpumask). padata_set_cpumask is to
+and for the serial callback function (cbcpumask). padata_set_cpumask is used to
change just one of the cpumasks. Here cpumask_type is one of PADATA_CPU_SERIAL,
PADATA_CPU_PARALLEL and cpumask specifies the new cpumask to use.
-To simply add or remove one cpu from a certain cpumask the functions
-padata_add_cpu/padata_remove_cpu are used. cpu specifies the cpu to add or
+To simply add or remove one CPU from a certain cpumask the functions
+padata_add_cpu/padata_remove_cpu are used. cpu specifies the CPU to add or
remove and mask is one of PADATA_CPU_SERIAL, PADATA_CPU_PARALLEL.
If a user is interested in padata cpumask changes, he can register to
struct notifier_block *nblock);
The padata cpumask change notifier notifies about changes of the usable
-cpumasks, i.e. the subset of active cpus in the user supplied cpumask.
+cpumasks, i.e. the subset of active CPUs in the user supplied cpumask.
Padata calls the notifier chain with:
Here cpumask_change_notifier is registered notifier, notification_mask
is one of PADATA_CPU_SERIAL, PADATA_CPU_PARALLEL and cpumask is a pointer
-to a struct padata_cpumask that contains the new cpumask informations.
+to a struct padata_cpumask that contains the new cpumask information.
Actually submitting work to the padata instance requires the creation of a
padata_priv structure:
};
This structure will almost certainly be embedded within some larger
-structure specific to the work to be done. Most its fields are private to
+structure specific to the work to be done. Most of its fields are private to
padata, but the structure should be zeroed at initialisation time, and the
parallel() and serial() functions should be provided. Those functions will
be called in the process of getting the work done as we will see
reports inverted write-protect state;
- sdhci,1-bit-only : (optional) specifies that a controller can
only handle 1-bit data transfers.
+ - sdhci,auto-cmd12: (optional) specifies that a controller can
+ only handle auto CMD12.
Example:
- "Low Speed" 1.5 Mbit/sec
USB 1.1 only addressed full speed and low speed. High speed devices
-can be used on USB 1.1 systems, but they slow down to USB 1.1 speeds.
+can be used on USB 1.1 systems, but they slow down to USB 1.1 speeds.
USB 1.1 devices may also be used on USB 2.0 systems. When plugged
into an EHCI controller, they are given to a USB 1.1 "companion"
--- /dev/null
+ -*- org -*-
+
+* Overview
+
+The Multifunction Composite Gadget (or g_multi) is a composite gadget
+that makes extensive use of the composite framework to provide
+a... multifunction gadget.
+
+In it's standard configuration it provides a single USB configuration
+with RNDIS[1] (that is Ethernet), USB CDC[2] ACM (that is serial) and
+USB Mass Storage functions.
+
+A CDC ECM (Ethernet) function may be turned on via a Kconfig option
+and RNDIS can be turned off. If they are both enabled the gadget will
+have two configurations -- one with RNDIS and another with CDC ECM[3].
+
+Please not that if you use non-standard configuration (that is enable
+CDC ECM) you may need to change vendor and/or product ID.
+
+* Host drivers
+
+To make use of the gadget one needs to make it work on host side --
+without that there's no hope of achieving anything with the gadget.
+As one might expect, things one need to do very from system to system.
+
+** Linux host drivers
+
+Since the gadget uses standard composite framework and appears as such
+to Linux host it does not need any additional drivers on Linux host
+side. All the functions are handled by respective drivers developed
+for them.
+
+This is also true for two configuration set-up with RNDIS
+configuration being the first one. Linux host will use the second
+configuration with CDC ECM which should work better under Linux.
+
+** Windows host drivers
+
+For the gadget two work under Windows two conditions have to be met:
+
+*** Detecting as composite gadget
+
+First of all, Windows need to detect the gadget as an USB composite
+gadget which on its own have some conditions[4]. If they are met,
+Windows lets USB Generic Parent Driver[5] handle the device which then
+tries to much drivers for each individual interface (sort of, don't
+get into too many details).
+
+The good news is: you do not have to worry about most of the
+conditions!
+
+The only thing to worry is that the gadget has to have a single
+configuration so a dual RNDIS and CDC ECM gadget won't work unless you
+create a proper INF -- and of course, if you do submit it!
+
+*** Installing drivers for each function
+
+The other, trickier thing is making Windows install drivers for each
+individual function.
+
+For mass storage it is trivial since Windows detect it's an interface
+implementing USB Mass Storage class and selects appropriate driver.
+
+Things are harder with RDNIS and CDC ACM.
+
+**** RNDIS
+
+To make Windows select RNDIS drivers for the first function in the
+gadget, one needs to use the [[file:linux.inf]] file provided with this
+document. It "attaches" Window's RNDIS driver to the first interface
+of the gadget.
+
+Please note, that while testing we encountered some issues[6] when
+RNDIS was not the first interface. You do not need to worry abut it
+unless you are trying to develop your own gadget in which case watch
+out for this bug.
+
+**** CDC ACM
+
+Similarly, [[file:linux-cdc-acm.inf]] is provided for CDC ACM.
+
+**** Customising the gadget
+
+If you intend to hack the g_multi gadget be advised that rearranging
+functions will obviously change interface numbers for each of the
+functionality. As an effect provided INFs won't work since they have
+interface numbers hard-coded in them (it's not hard to change those
+though[7]).
+
+This also means, that after experimenting with g_multi and changing
+provided functions one should change gadget's vendor and/or product ID
+so there will be no collision with other customised gadgets or the
+original gadget.
+
+Failing to comply may cause brain damage after wondering for hours why
+things don't work as intended before realising Windows have cached
+some drivers information (changing USB port may sometimes help plus
+you might try using USBDeview[8] to remove the phantom device).
+
+**** INF testing
+
+Provided INF files have been tested on Windows XP SP3, Windows Vista
+and Windows 7, all 32-bit versions. It should work on 64-bit versions
+as well. It most likely won't work on Windows prior to Windows XP
+SP2.
+
+** Other systems
+
+At this moment, drivers for any other systems have not been tested.
+Knowing how MacOS is based on BSD and BSD is an Open Source it is
+believed that it should (read: "I have no idea whether it will") work
+out-of-the-box.
+
+For more exotic systems I have even less to say...
+
+Any testing and drivers *are* *welcome*!
+
+* Authors
+
+This document has been written by Michal Nazarewicz
+([[mailto:mina86@mina86.com]]). INF files have been hacked with
+support of Marek Szyprowski ([[mailto:m.szyprowski@samsung.com]]) and
+Xiaofan Chen ([[mailto:xiaofanc@gmail.com]]) basing on the MS RNDIS
+template[9], Microchip's CDC ACM INF file and David Brownell's
+([[mailto:dbrownell@users.sourceforge.net]]) original INF files.
+
+* Footnotes
+
+[1] Remote Network Driver Interface Specification,
+[[http://msdn.microsoft.com/en-us/library/ee484414.aspx]].
+
+[2] Communications Device Class Abstract Control Model, spec for this
+and other USB classes can be found at
+[[http://www.usb.org/developers/devclass_docs/]].
+
+[3] CDC Ethernet Control Model.
+
+[4] [[http://msdn.microsoft.com/en-us/library/ff537109(v=VS.85).aspx]]
+
+[5] [[http://msdn.microsoft.com/en-us/library/ff539234(v=VS.85).aspx]]
+
+[6] To put it in some other nice words, Windows failed to respond to
+any user input.
+
+[7] You may find [[http://www.cygnal.org/ubb/Forum9/HTML/001050.html]]
+useful.
+
+[8] http://www.nirsoft.net/utils/usb_devices_view.html
+
+[9] [[http://msdn.microsoft.com/en-us/library/ff570620.aspx]]
Installing the Windows Host ACM Driver
--------------------------------------
-To use the Windows ACM driver you must have the files "gserial.inf"
-and "usbser.sys" together in a folder on the Windows machine.
-
-The "gserial.inf" file is given here.
-
--------------------- CUT HERE --------------------
-[Version]
-Signature="$Windows NT$"
-Class=Ports
-ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318}
-Provider=%LINUX%
-DriverVer=08/17/2004,0.0.2.0
-; Copyright (C) 2004 Al Borchers (alborchers@steinerpoint.com)
-
-[Manufacturer]
-%LINUX%=GSerialDeviceList
-
-[GSerialDeviceList]
-%GSERIAL%=GSerialInstall, USB\VID_0525&PID_A4A7
-
-[DestinationDirs]
-DefaultDestDir=10,System32\Drivers
-
-[GSerialInstall]
-CopyFiles=GSerialCopyFiles
-AddReg=GSerialAddReg
-
-[GSerialCopyFiles]
-usbser.sys
-
-[GSerialAddReg]
-HKR,,DevLoader,,*ntkern
-HKR,,NTMPDriver,,usbser.sys
-HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider"
-
-[GSerialInstall.Services]
-AddService = usbser,0x0002,GSerialService
-
-[GSerialService]
-DisplayName = %GSERIAL_DISPLAY_NAME%
-ServiceType = 1 ; SERVICE_KERNEL_DRIVER
-StartType = 3 ; SERVICE_DEMAND_START
-ErrorControl = 1 ; SERVICE_ERROR_NORMAL
-ServiceBinary = %10%\System32\Drivers\usbser.sys
-LoadOrderGroup = Base
-
-[Strings]
-LINUX = "Linux"
-GSERIAL = "Gadget Serial"
-GSERIAL_DISPLAY_NAME = "USB Gadget Serial Driver"
--------------------- CUT HERE --------------------
-
-The "usbser.sys" file comes with various versions of Windows.
-For example, it can be found on Windows XP typically in
-
- C:\WINDOWS\Driver Cache\i386\driver.cab
-
-Or it can be found on the Windows 98SE CD in the "win98" folder
-in the "DRIVER11.CAB" through "DRIVER20.CAB" cab files. You will
-need the DOS "expand" program, the Cygwin "cabextract" program, or
-a similar program to unpack these cab files and extract "usbser.sys".
-
-For example, to extract "usbser.sys" into the current directory
-on Windows XP, open a DOS window and run a command like
-
- expand C:\WINDOWS\Driver~1\i386\driver.cab -F:usbser.sys .
-
-(Thanks to Nishant Kamat for pointing out this DOS command.)
+To use the Windows ACM driver you must have the "linux-cdc-acm.inf"
+file (provided along this document) which supports all recent versions
+of Windows.
When the gadget serial driver is loaded and the USB device connected
to the Windows host with a USB cable, Windows should recognize the
gadget serial device and ask for a driver. Tell Windows to find the
-driver in the folder that contains "gserial.inf" and "usbser.sys".
+driver in the folder that contains the "linux-cdc-acm.inf" file.
For example, on Windows XP, when the gadget serial device is first
plugged in, the "Found New Hardware Wizard" starts up. Select
-"Install from a list or specific location (Advanced)", then on
-the next screen select "Include this location in the search" and
-enter the path or browse to the folder containing "gserial.inf" and
-"usbser.sys". Windows will complain that the Gadget Serial driver
-has not passed Windows Logo testing, but select "Continue anyway"
-and finish the driver installation.
+"Install from a list or specific location (Advanced)", then on the
+next screen select "Include this location in the search" and enter the
+path or browse to the folder containing the "linux-cdc-acm.inf" file.
+Windows will complain that the Gadget Serial driver has not passed
+Windows Logo testing, but select "Continue anyway" and finish the
+driver installation.
On Windows XP, in the "Device Manager" (under "Control Panel",
"System", "Hardware") expand the "Ports (COM & LPT)" entry and you
side and host side systems. Anything you type on the terminal
window on the gadget side should appear in the terminal window on
the host side and vice versa.
-
-
- Bind a driver to that device. Bus frameworks do that using a
device driver's probe() routine.
-
+
- Tell other subsystems to configure the new device. Print
queues may need to be enabled, networks brought up, disk
partitions mounted, and so on. In some cases these will
Current versions of module-init-tools will create a "modules.usbmap" file
which contains the entries from each driver's MODULE_DEVICE_TABLE. Such
files can be used by various user mode policy agents to make sure all the
-right driver modules get loaded, either at boot time or later.
+right driver modules get loaded, either at boot time or later.
See <linux/usb.h> for full information about such table entries; or look
at existing drivers. Each table entry describes one or more criteria to
--- /dev/null
+; Windows USB CDC ACM Setup File
+
+; Based on INF template which was:
+; Copyright (c) 2000 Microsoft Corporation
+; Copyright (c) 2007 Microchip Technology Inc.
+; likely to be covered by the MLPL as found at:
+; <http://msdn.microsoft.com/en-us/cc300389.aspx#MLPL>.
+; For use only on Windows operating systems.
+
+[Version]
+Signature="$Windows NT$"
+Class=Ports
+ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318}
+Provider=%Linux%
+DriverVer=11/15/2007,5.1.2600.0
+
+[Manufacturer]
+%Linux%=DeviceList, NTamd64
+
+[DestinationDirs]
+DefaultDestDir=12
+
+
+;------------------------------------------------------------------------------
+; Windows 2000/XP/Vista-32bit Sections
+;------------------------------------------------------------------------------
+
+[DriverInstall.nt]
+include=mdmcpq.inf
+CopyFiles=DriverCopyFiles.nt
+AddReg=DriverInstall.nt.AddReg
+
+[DriverCopyFiles.nt]
+usbser.sys,,,0x20
+
+[DriverInstall.nt.AddReg]
+HKR,,DevLoader,,*ntkern
+HKR,,NTMPDriver,,USBSER.sys
+HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider"
+
+[DriverInstall.nt.Services]
+AddService=usbser, 0x00000002, DriverService.nt
+
+[DriverService.nt]
+DisplayName=%SERVICE%
+ServiceType=1
+StartType=3
+ErrorControl=1
+ServiceBinary=%12%\USBSER.sys
+
+;------------------------------------------------------------------------------
+; Vista-64bit Sections
+;------------------------------------------------------------------------------
+
+[DriverInstall.NTamd64]
+include=mdmcpq.inf
+CopyFiles=DriverCopyFiles.NTamd64
+AddReg=DriverInstall.NTamd64.AddReg
+
+[DriverCopyFiles.NTamd64]
+USBSER.sys,,,0x20
+
+[DriverInstall.NTamd64.AddReg]
+HKR,,DevLoader,,*ntkern
+HKR,,NTMPDriver,,USBSER.sys
+HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider"
+
+[DriverInstall.NTamd64.Services]
+AddService=usbser, 0x00000002, DriverService.NTamd64
+
+[DriverService.NTamd64]
+DisplayName=%SERVICE%
+ServiceType=1
+StartType=3
+ErrorControl=1
+ServiceBinary=%12%\USBSER.sys
+
+
+;------------------------------------------------------------------------------
+; Vendor and Product ID Definitions
+;------------------------------------------------------------------------------
+; When developing your USB device, the VID and PID used in the PC side
+; application program and the firmware on the microcontroller must match.
+; Modify the below line to use your VID and PID. Use the format as shown
+; below.
+; Note: One INF file can be used for multiple devices with different
+; VID and PIDs. For each supported device, append
+; ",USB\VID_xxxx&PID_yyyy" to the end of the line.
+;------------------------------------------------------------------------------
+[SourceDisksFiles]
+[SourceDisksNames]
+[DeviceList]
+%DESCRIPTION%=DriverInstall, USB\VID_0525&PID_A4A7, USB\VID_0525&PID_A4AB&MI_02
+
+[DeviceList.NTamd64]
+%DESCRIPTION%=DriverInstall, USB\VID_0525&PID_A4A7, USB\VID_0525&PID_A4AB&MI_02
+
+
+;------------------------------------------------------------------------------
+; String Definitions
+;------------------------------------------------------------------------------
+;Modify these strings to customize your device
+;------------------------------------------------------------------------------
+[Strings]
+Linux = "Linux Developer Community"
+DESCRIPTION = "Gadget Serial"
+SERVICE = "USB RS-232 Emulation Driver"
-; MS-Windows driver config matching some basic modes of the
-; Linux-USB Ethernet/RNDIS gadget firmware:
-;
-; - RNDIS plus CDC Ethernet ... this may be familiar as a DOCSIS
-; cable modem profile, and supports most non-Microsoft USB hosts
-;
-; - RNDIS plus CDC Subset ... used by hardware that incapable of
-; full CDC Ethernet support.
-;
-; Microsoft only directly supports RNDIS drivers, and bundled them into XP.
-; The Microsoft "Remote NDIS USB Driver Kit" is currently found at:
-; http://www.microsoft.com/whdc/device/network/ndis/rmndis.mspx
-
+; Based on template INF file found at
+; <http://msdn.microsoft.com/en-us/library/ff570620.aspx>
+; which was:
+; Copyright (c) Microsoft Corporation
+; and released under the MLPL as found at:
+; <http://msdn.microsoft.com/en-us/cc300389.aspx#MLPL>.
+; For use only on Windows operating systems.
[Version]
-Signature = "$CHICAGO$"
+Signature = "$Windows NT$"
Class = Net
ClassGUID = {4d36e972-e325-11ce-bfc1-08002be10318}
Provider = %Linux%
-Compatible = 1
-MillenniumPreferred = .ME
-DriverVer = 03/30/2004,0.0.0.0
-; catalog file would be used by WHQL
-;CatalogFile = Linux.cat
+DriverVer = 06/21/2006,6.0.6000.16384
[Manufacturer]
-%Linux% = LinuxDevices,NT.5.1
+%Linux% = LinuxDevices,NTx86,NTamd64,NTia64
+
+; Decoration for x86 architecture
+[LinuxDevices.NTx86]
+%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_0525&PID_a4ab&MI_00
-[LinuxDevices]
-; NetChip IDs, used by both firmware modes
-%LinuxDevice% = RNDIS, USB\VID_0525&PID_a4a2
+; Decoration for x64 architecture
+[LinuxDevices.NTamd64]
+%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_0525&PID_a4ab&MI_00
-[LinuxDevices.NT.5.1]
-%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2
+; Decoration for ia64 architecture
+[LinuxDevices.NTia64]
+%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_0525&PID_a4ab&MI_00
+;@@@ This is the common setting for setup
[ControlFlags]
ExcludeFromSelect=*
-; Windows 98, Windows 98 Second Edition specific sections --------
-
-[RNDIS]
-DeviceID = usb8023
-MaxInstance = 512
-DriverVer = 03/30/2004,0.0.0.0
-AddReg = RNDIS_AddReg_98, RNDIS_AddReg_Common
-
-[RNDIS_AddReg_98]
-HKR, , DevLoader, 0, *ndis
-HKR, , DeviceVxDs, 0, usb8023.sys
-HKR, NDIS, LogDriverName, 0, "usb8023"
-HKR, NDIS, MajorNdisVersion, 1, 5
-HKR, NDIS, MinorNdisVersion, 1, 0
-HKR, Ndi\Interfaces, DefUpper, 0, "ndis3,ndis4,ndis5"
-HKR, Ndi\Interfaces, DefLower, 0, "ethernet"
-HKR, Ndi\Interfaces, UpperRange, 0, "ndis3,ndis4,ndis5"
-HKR, Ndi\Interfaces, LowerRange, 0, "ethernet"
-HKR, Ndi\Install, ndis3, 0, "RNDIS_Install_98"
-HKR, Ndi\Install, ndis4, 0, "RNDIS_Install_98"
-HKR, Ndi\Install, ndis5, 0, "RNDIS_Install_98"
-HKR, Ndi, DeviceId, 0, "USB\VID_0525&PID_a4a2"
-
-[RNDIS_Install_98]
-CopyFiles=RNDIS_CopyFiles_98
-
-[RNDIS_CopyFiles_98]
-usb8023.sys, usb8023w.sys, , 0
-rndismp.sys, rndismpw.sys, , 0
-
-; Windows Millennium Edition specific sections --------------------
-
-[RNDIS.ME]
-DeviceID = usb8023
-MaxInstance = 512
-DriverVer = 03/30/2004,0.0.0.0
-AddReg = RNDIS_AddReg_ME, RNDIS_AddReg_Common
-Characteristics = 0x84 ; NCF_PHYSICAL + NCF_HAS_UI
-BusType = 15
-
-[RNDIS_AddReg_ME]
-HKR, , DevLoader, 0, *ndis
-HKR, , DeviceVxDs, 0, usb8023.sys
-HKR, NDIS, LogDriverName, 0, "usb8023"
-HKR, NDIS, MajorNdisVersion, 1, 5
-HKR, NDIS, MinorNdisVersion, 1, 0
-HKR, Ndi\Interfaces, DefUpper, 0, "ndis3,ndis4,ndis5"
-HKR, Ndi\Interfaces, DefLower, 0, "ethernet"
-HKR, Ndi\Interfaces, UpperRange, 0, "ndis3,ndis4,ndis5"
-HKR, Ndi\Interfaces, LowerRange, 0, "ethernet"
-HKR, Ndi\Install, ndis3, 0, "RNDIS_Install_ME"
-HKR, Ndi\Install, ndis4, 0, "RNDIS_Install_ME"
-HKR, Ndi\Install, ndis5, 0, "RNDIS_Install_ME"
-HKR, Ndi, DeviceId, 0, "USB\VID_0525&PID_a4a2"
-
-[RNDIS_Install_ME]
-CopyFiles=RNDIS_CopyFiles_ME
-
-[RNDIS_CopyFiles_ME]
-usb8023.sys, usb8023m.sys, , 0
-rndismp.sys, rndismpm.sys, , 0
-
-; Windows 2000 specific sections ---------------------------------
-
-[RNDIS.NT]
-Characteristics = 0x84 ; NCF_PHYSICAL + NCF_HAS_UI
-BusType = 15
-DriverVer = 03/30/2004,0.0.0.0
-AddReg = RNDIS_AddReg_NT, RNDIS_AddReg_Common
-CopyFiles = RNDIS_CopyFiles_NT
-
-[RNDIS.NT.Services]
-AddService = USB_RNDIS, 2, RNDIS_ServiceInst_NT, RNDIS_EventLog
-
-[RNDIS_CopyFiles_NT]
-; no rename of files on Windows 2000, use the 'k' names as is
-usb8023k.sys, , , 0
-rndismpk.sys, , , 0
-
-[RNDIS_ServiceInst_NT]
-DisplayName = %ServiceDisplayName%
-ServiceType = 1
-StartType = 3
-ErrorControl = 1
-ServiceBinary = %12%\usb8023k.sys
-LoadOrderGroup = NDIS
-AddReg = RNDIS_WMI_AddReg_NT
-
-[RNDIS_WMI_AddReg_NT]
-HKR, , MofImagePath, 0x00020000, "System32\drivers\rndismpk.sys"
-
-; Windows XP specific sections -----------------------------------
-
+; DDInstall section
+; References the in-build Netrndis.inf
[RNDIS.NT.5.1]
-Characteristics = 0x84 ; NCF_PHYSICAL + NCF_HAS_UI
-BusType = 15
-DriverVer = 03/30/2004,0.0.0.0
-AddReg = RNDIS_AddReg_NT, RNDIS_AddReg_Common
-; no copyfiles - the files are already in place
-
+Characteristics = 0x84 ; NCF_PHYSICAL + NCF_HAS_UI
+BusType = 15
+; NEVER REMOVE THE FOLLOWING REFERENCE FOR NETRNDIS.INF
+include = netrndis.inf
+needs = Usb_Rndis.ndi
+AddReg = Rndis_AddReg_Vista
+
+; DDInstal.Services section
[RNDIS.NT.5.1.Services]
-AddService = USB_RNDIS, 2, RNDIS_ServiceInst_51, RNDIS_EventLog
-
-[RNDIS_ServiceInst_51]
-DisplayName = %ServiceDisplayName%
-ServiceType = 1
-StartType = 3
-ErrorControl = 1
-ServiceBinary = %12%\usb8023.sys
-LoadOrderGroup = NDIS
-AddReg = RNDIS_WMI_AddReg_51
-
-[RNDIS_WMI_AddReg_51]
-HKR, , MofImagePath, 0x00020000, "System32\drivers\rndismp.sys"
-
-; Windows 2000 and Windows XP common sections --------------------
-
-[RNDIS_AddReg_NT]
-HKR, Ndi, Service, 0, "USB_RNDIS"
-HKR, Ndi\Interfaces, UpperRange, 0, "ndis5"
-HKR, Ndi\Interfaces, LowerRange, 0, "ethernet"
-
-[RNDIS_EventLog]
-AddReg = RNDIS_EventLog_AddReg
-
-[RNDIS_EventLog_AddReg]
-HKR, , EventMessageFile, 0x00020000, "%%SystemRoot%%\System32\netevent.dll"
-HKR, , TypesSupported, 0x00010001, 7
-
-; Common Sections -------------------------------------------------
-
-[RNDIS_AddReg_Common]
-HKR, NDI\params\NetworkAddress, ParamDesc, 0, %NetworkAddress%
-HKR, NDI\params\NetworkAddress, type, 0, "edit"
-HKR, NDI\params\NetworkAddress, LimitText, 0, "12"
-HKR, NDI\params\NetworkAddress, UpperCase, 0, "1"
-HKR, NDI\params\NetworkAddress, default, 0, " "
-HKR, NDI\params\NetworkAddress, optional, 0, "1"
-
-[SourceDisksNames]
-1=%SourceDisk%,,1
-
-[SourceDisksFiles]
-usb8023m.sys=1
-rndismpm.sys=1
-usb8023w.sys=1
-rndismpw.sys=1
-usb8023k.sys=1
-rndismpk.sys=1
-
-[DestinationDirs]
-RNDIS_CopyFiles_98 = 10, system32/drivers
-RNDIS_CopyFiles_ME = 10, system32/drivers
-RNDIS_CopyFiles_NT = 12
+include = netrndis.inf
+needs = Usb_Rndis.ndi.Services
+
+; Optional registry settings. You can modify as needed.
+[RNDIS_AddReg_Vista]
+HKR, NDI\params\VistaProperty, ParamDesc, 0, %Vista_Property%
+HKR, NDI\params\VistaProperty, type, 0, "edit"
+HKR, NDI\params\VistaProperty, LimitText, 0, "12"
+HKR, NDI\params\VistaProperty, UpperCase, 0, "1"
+HKR, NDI\params\VistaProperty, default, 0, " "
+HKR, NDI\params\VistaProperty, optional, 0, "1"
+
+; No sys copyfiles - the sys files are already in-build
+; (part of the operating system).
+; We do not support XP SP1-, 2003 SP1-, ME, 9x.
[Strings]
-ServiceDisplayName = "USB Remote NDIS Network Device Driver"
-NetworkAddress = "Network Address"
Linux = "Linux Developer Community"
LinuxDevice = "Linux USB Ethernet/RNDIS Gadget"
-SourceDisk = "Ethernet/RNDIS Gadget Driver Install Disk"
-
+Vista_Property = "Optional Vista Property"
--- /dev/null
+Introduction
+============
+
+The V4L2 control API seems simple enough, but quickly becomes very hard to
+implement correctly in drivers. But much of the code needed to handle controls
+is actually not driver specific and can be moved to the V4L core framework.
+
+After all, the only part that a driver developer is interested in is:
+
+1) How do I add a control?
+2) How do I set the control's value? (i.e. s_ctrl)
+
+And occasionally:
+
+3) How do I get the control's value? (i.e. g_volatile_ctrl)
+4) How do I validate the user's proposed control value? (i.e. try_ctrl)
+
+All the rest is something that can be done centrally.
+
+The control framework was created in order to implement all the rules of the
+V4L2 specification with respect to controls in a central place. And to make
+life as easy as possible for the driver developer.
+
+Note that the control framework relies on the presence of a struct v4l2_device
+for V4L2 drivers and struct v4l2_subdev for sub-device drivers.
+
+
+Objects in the framework
+========================
+
+There are two main objects:
+
+The v4l2_ctrl object describes the control properties and keeps track of the
+control's value (both the current value and the proposed new value).
+
+v4l2_ctrl_handler is the object that keeps track of controls. It maintains a
+list of v4l2_ctrl objects that it owns and another list of references to
+controls, possibly to controls owned by other handlers.
+
+
+Basic usage for V4L2 and sub-device drivers
+===========================================
+
+1) Prepare the driver:
+
+1.1) Add the handler to your driver's top-level struct:
+
+ struct foo_dev {
+ ...
+ struct v4l2_ctrl_handler ctrl_handler;
+ ...
+ };
+
+ struct foo_dev *foo;
+
+1.2) Initialize the handler:
+
+ v4l2_ctrl_handler_init(&foo->ctrl_handler, nr_of_controls);
+
+ The second argument is a hint telling the function how many controls this
+ handler is expected to handle. It will allocate a hashtable based on this
+ information. It is a hint only.
+
+1.3) Hook the control handler into the driver:
+
+1.3.1) For V4L2 drivers do this:
+
+ struct foo_dev {
+ ...
+ struct v4l2_device v4l2_dev;
+ ...
+ struct v4l2_ctrl_handler ctrl_handler;
+ ...
+ };
+
+ foo->v4l2_dev.ctrl_handler = &foo->ctrl_handler;
+
+ Where foo->v4l2_dev is of type struct v4l2_device.
+
+ Finally, remove all control functions from your v4l2_ioctl_ops:
+ vidioc_queryctrl, vidioc_querymenu, vidioc_g_ctrl, vidioc_s_ctrl,
+ vidioc_g_ext_ctrls, vidioc_try_ext_ctrls and vidioc_s_ext_ctrls.
+ Those are now no longer needed.
+
+1.3.2) For sub-device drivers do this:
+
+ struct foo_dev {
+ ...
+ struct v4l2_subdev sd;
+ ...
+ struct v4l2_ctrl_handler ctrl_handler;
+ ...
+ };
+
+ foo->sd.ctrl_handler = &foo->ctrl_handler;
+
+ Where foo->sd is of type struct v4l2_subdev.
+
+ And set all core control ops in your struct v4l2_subdev_core_ops to these
+ helpers:
+
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+
+ Note: this is a temporary solution only. Once all V4L2 drivers that depend
+ on subdev drivers are converted to the control framework these helpers will
+ no longer be needed.
+
+1.4) Clean up the handler at the end:
+
+ v4l2_ctrl_handler_free(&foo->ctrl_handler);
+
+
+2) Add controls:
+
+You add non-menu controls by calling v4l2_ctrl_new_std:
+
+ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, s32 min, s32 max, u32 step, s32 def);
+
+Menu controls are added by calling v4l2_ctrl_new_std_menu:
+
+ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, s32 max, s32 skip_mask, s32 def);
+
+These functions are typically called right after the v4l2_ctrl_handler_init:
+
+ v4l2_ctrl_handler_init(&foo->ctrl_handler, nr_of_controls);
+ v4l2_ctrl_new_std(&foo->ctrl_handler, &foo_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&foo->ctrl_handler, &foo_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 128);
+ v4l2_ctrl_new_std_menu(&foo->ctrl_handler, &foo_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
+ V4L2_CID_POWER_LINE_FREQUENCY_DISABLED);
+ ...
+ if (foo->ctrl_handler.error) {
+ int err = foo->ctrl_handler.error;
+
+ v4l2_ctrl_handler_free(&foo->ctrl_handler);
+ return err;
+ }
+
+The v4l2_ctrl_new_std function returns the v4l2_ctrl pointer to the new
+control, but if you do not need to access the pointer outside the control ops,
+then there is no need to store it.
+
+The v4l2_ctrl_new_std function will fill in most fields based on the control
+ID except for the min, max, step and default values. These are passed in the
+last four arguments. These values are driver specific while control attributes
+like type, name, flags are all global. The control's current value will be set
+to the default value.
+
+The v4l2_ctrl_new_std_menu function is very similar but it is used for menu
+controls. There is no min argument since that is always 0 for menu controls,
+and instead of a step there is a skip_mask argument: if bit X is 1, then menu
+item X is skipped.
+
+Note that if something fails, the function will return NULL or an error and
+set ctrl_handler->error to the error code. If ctrl_handler->error was already
+set, then it will just return and do nothing. This is also true for
+v4l2_ctrl_handler_init if it cannot allocate the internal data structure.
+
+This makes it easy to init the handler and just add all controls and only check
+the error code at the end. Saves a lot of repetitive error checking.
+
+It is recommended to add controls in ascending control ID order: it will be
+a bit faster that way.
+
+3) Optionally force initial control setup:
+
+ v4l2_ctrl_handler_setup(&foo->ctrl_handler);
+
+This will call s_ctrl for all controls unconditionally. Effectively this
+initializes the hardware to the default control values. It is recommended
+that you do this as this ensures that both the internal data structures and
+the hardware are in sync.
+
+4) Finally: implement the v4l2_ctrl_ops
+
+ static const struct v4l2_ctrl_ops foo_ctrl_ops = {
+ .s_ctrl = foo_s_ctrl,
+ };
+
+Usually all you need is s_ctrl:
+
+ static int foo_s_ctrl(struct v4l2_ctrl *ctrl)
+ {
+ struct foo *state = container_of(ctrl->handler, struct foo, ctrl_handler);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ write_reg(0x123, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ write_reg(0x456, ctrl->val);
+ break;
+ }
+ return 0;
+ }
+
+The control ops are called with the v4l2_ctrl pointer as argument.
+The new control value has already been validated, so all you need to do is
+to actually update the hardware registers.
+
+You're done! And this is sufficient for most of the drivers we have. No need
+to do any validation of control values, or implement QUERYCTRL/QUERYMENU. And
+G/S_CTRL as well as G/TRY/S_EXT_CTRLS are automatically supported.
+
+
+==============================================================================
+
+The remainder of this document deals with more advanced topics and scenarios.
+In practice the basic usage as described above is sufficient for most drivers.
+
+===============================================================================
+
+
+Inheriting Controls
+===================
+
+When a sub-device is registered with a V4L2 driver by calling
+v4l2_device_register_subdev() and the ctrl_handler fields of both v4l2_subdev
+and v4l2_device are set, then the controls of the subdev will become
+automatically available in the V4L2 driver as well. If the subdev driver
+contains controls that already exist in the V4L2 driver, then those will be
+skipped (so a V4L2 driver can always override a subdev control).
+
+What happens here is that v4l2_device_register_subdev() calls
+v4l2_ctrl_add_handler() adding the controls of the subdev to the controls
+of v4l2_device.
+
+
+Accessing Control Values
+========================
+
+The v4l2_ctrl struct contains these two unions:
+
+ /* The current control value. */
+ union {
+ s32 val;
+ s64 val64;
+ char *string;
+ } cur;
+
+ /* The new control value. */
+ union {
+ s32 val;
+ s64 val64;
+ char *string;
+ };
+
+Within the control ops you can freely use these. The val and val64 speak for
+themselves. The string pointers point to character buffers of length
+ctrl->maximum + 1, and are always 0-terminated.
+
+In most cases 'cur' contains the current cached control value. When you create
+a new control this value is made identical to the default value. After calling
+v4l2_ctrl_handler_setup() this value is passed to the hardware. It is generally
+a good idea to call this function.
+
+Whenever a new value is set that new value is automatically cached. This means
+that most drivers do not need to implement the g_volatile_ctrl() op. The
+exception is for controls that return a volatile register such as a signal
+strength read-out that changes continuously. In that case you will need to
+implement g_volatile_ctrl like this:
+
+ static int foo_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+ {
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->cur.val = read_reg(0x123);
+ break;
+ }
+ }
+
+The 'new value' union is not used in g_volatile_ctrl. In general controls
+that need to implement g_volatile_ctrl are read-only controls.
+
+To mark a control as volatile you have to set the is_volatile flag:
+
+ ctrl = v4l2_ctrl_new_std(&sd->ctrl_handler, ...);
+ if (ctrl)
+ ctrl->is_volatile = 1;
+
+For try/s_ctrl the new values (i.e. as passed by the user) are filled in and
+you can modify them in try_ctrl or set them in s_ctrl. The 'cur' union
+contains the current value, which you can use (but not change!) as well.
+
+If s_ctrl returns 0 (OK), then the control framework will copy the new final
+values to the 'cur' union.
+
+While in g_volatile/s/try_ctrl you can access the value of all controls owned
+by the same handler since the handler's lock is held. If you need to access
+the value of controls owned by other handlers, then you have to be very careful
+not to introduce deadlocks.
+
+Outside of the control ops you have to go through to helper functions to get
+or set a single control value safely in your driver:
+
+ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl);
+ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val);
+
+These functions go through the control framework just as VIDIOC_G/S_CTRL ioctls
+do. Don't use these inside the control ops g_volatile/s/try_ctrl, though, that
+will result in a deadlock since these helpers lock the handler as well.
+
+You can also take the handler lock yourself:
+
+ mutex_lock(&state->ctrl_handler.lock);
+ printk(KERN_INFO "String value is '%s'\n", ctrl1->cur.string);
+ printk(KERN_INFO "Integer value is '%s'\n", ctrl2->cur.val);
+ mutex_unlock(&state->ctrl_handler.lock);
+
+
+Menu Controls
+=============
+
+The v4l2_ctrl struct contains this union:
+
+ union {
+ u32 step;
+ u32 menu_skip_mask;
+ };
+
+For menu controls menu_skip_mask is used. What it does is that it allows you
+to easily exclude certain menu items. This is used in the VIDIOC_QUERYMENU
+implementation where you can return -EINVAL if a certain menu item is not
+present. Note that VIDIOC_QUERYCTRL always returns a step value of 1 for
+menu controls.
+
+A good example is the MPEG Audio Layer II Bitrate menu control where the
+menu is a list of standardized possible bitrates. But in practice hardware
+implementations will only support a subset of those. By setting the skip
+mask you can tell the framework which menu items should be skipped. Setting
+it to 0 means that all menu items are supported.
+
+You set this mask either through the v4l2_ctrl_config struct for a custom
+control, or by calling v4l2_ctrl_new_std_menu().
+
+
+Custom Controls
+===============
+
+Driver specific controls can be created using v4l2_ctrl_new_custom():
+
+ static const struct v4l2_ctrl_config ctrl_filter = {
+ .ops = &ctrl_custom_ops,
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
+ .name = "Spatial Filter",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ .max = 15,
+ .step = 1,
+ };
+
+ ctrl = v4l2_ctrl_new_custom(&foo->ctrl_handler, &ctrl_filter, NULL);
+
+The last argument is the priv pointer which can be set to driver-specific
+private data.
+
+The v4l2_ctrl_config struct also has fields to set the is_private and is_volatile
+flags.
+
+If the name field is not set, then the framework will assume this is a standard
+control and will fill in the name, type and flags fields accordingly.
+
+
+Active and Grabbed Controls
+===========================
+
+If you get more complex relationships between controls, then you may have to
+activate and deactivate controls. For example, if the Chroma AGC control is
+on, then the Chroma Gain control is inactive. That is, you may set it, but
+the value will not be used by the hardware as long as the automatic gain
+control is on. Typically user interfaces can disable such input fields.
+
+You can set the 'active' status using v4l2_ctrl_activate(). By default all
+controls are active. Note that the framework does not check for this flag.
+It is meant purely for GUIs. The function is typically called from within
+s_ctrl.
+
+The other flag is the 'grabbed' flag. A grabbed control means that you cannot
+change it because it is in use by some resource. Typical examples are MPEG
+bitrate controls that cannot be changed while capturing is in progress.
+
+If a control is set to 'grabbed' using v4l2_ctrl_grab(), then the framework
+will return -EBUSY if an attempt is made to set this control. The
+v4l2_ctrl_grab() function is typically called from the driver when it
+starts or stops streaming.
+
+
+Control Clusters
+================
+
+By default all controls are independent from the others. But in more
+complex scenarios you can get dependencies from one control to another.
+In that case you need to 'cluster' them:
+
+ struct foo {
+ struct v4l2_ctrl_handler ctrl_handler;
+#define AUDIO_CL_VOLUME (0)
+#define AUDIO_CL_MUTE (1)
+ struct v4l2_ctrl *audio_cluster[2];
+ ...
+ };
+
+ state->audio_cluster[AUDIO_CL_VOLUME] =
+ v4l2_ctrl_new_std(&state->ctrl_handler, ...);
+ state->audio_cluster[AUDIO_CL_MUTE] =
+ v4l2_ctrl_new_std(&state->ctrl_handler, ...);
+ v4l2_ctrl_cluster(ARRAY_SIZE(state->audio_cluster), state->audio_cluster);
+
+From now on whenever one or more of the controls belonging to the same
+cluster is set (or 'gotten', or 'tried'), only the control ops of the first
+control ('volume' in this example) is called. You effectively create a new
+composite control. Similar to how a 'struct' works in C.
+
+So when s_ctrl is called with V4L2_CID_AUDIO_VOLUME as argument, you should set
+all two controls belonging to the audio_cluster:
+
+ static int foo_s_ctrl(struct v4l2_ctrl *ctrl)
+ {
+ struct foo *state = container_of(ctrl->handler, struct foo, ctrl_handler);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME: {
+ struct v4l2_ctrl *mute = ctrl->cluster[AUDIO_CL_MUTE];
+
+ write_reg(0x123, mute->val ? 0 : ctrl->val);
+ break;
+ }
+ case V4L2_CID_CONTRAST:
+ write_reg(0x456, ctrl->val);
+ break;
+ }
+ return 0;
+ }
+
+In the example above the following are equivalent for the VOLUME case:
+
+ ctrl == ctrl->cluster[AUDIO_CL_VOLUME] == state->audio_cluster[AUDIO_CL_VOLUME]
+ ctrl->cluster[AUDIO_CL_MUTE] == state->audio_cluster[AUDIO_CL_MUTE]
+
+Note that controls in a cluster may be NULL. For example, if for some
+reason mute was never added (because the hardware doesn't support that
+particular feature), then mute will be NULL. So in that case we have a
+cluster of 2 controls, of which only 1 is actually instantiated. The
+only restriction is that the first control of the cluster must always be
+present, since that is the 'master' control of the cluster. The master
+control is the one that identifies the cluster and that provides the
+pointer to the v4l2_ctrl_ops struct that is used for that cluster.
+
+Obviously, all controls in the cluster array must be initialized to either
+a valid control or to NULL.
+
+
+VIDIOC_LOG_STATUS Support
+=========================
+
+This ioctl allow you to dump the current status of a driver to the kernel log.
+The v4l2_ctrl_handler_log_status(ctrl_handler, prefix) can be used to dump the
+value of the controls owned by the given handler to the log. You can supply a
+prefix as well. If the prefix didn't end with a space, then ': ' will be added
+for you.
+
+
+Different Handlers for Different Video Nodes
+============================================
+
+Usually the V4L2 driver has just one control handler that is global for
+all video nodes. But you can also specify different control handlers for
+different video nodes. You can do that by manually setting the ctrl_handler
+field of struct video_device.
+
+That is no problem if there are no subdevs involved but if there are, then
+you need to block the automatic merging of subdev controls to the global
+control handler. You do that by simply setting the ctrl_handler field in
+struct v4l2_device to NULL. Now v4l2_device_register_subdev() will no longer
+merge subdev controls.
+
+After each subdev was added, you will then have to call v4l2_ctrl_add_handler
+manually to add the subdev's control handler (sd->ctrl_handler) to the desired
+control handler. This control handler may be specific to the video_device or
+for a subset of video_device's. For example: the radio device nodes only have
+audio controls, while the video and vbi device nodes share the same control
+handler for the audio and video controls.
+
+If you want to have one handler (e.g. for a radio device node) have a subset
+of another handler (e.g. for a video device node), then you should first add
+the controls to the first handler, add the other controls to the second
+handler and finally add the first handler to the second. For example:
+
+ v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_VOLUME, ...);
+ v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
+ v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_BRIGHTNESS, ...);
+ v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_CONTRAST, ...);
+ v4l2_ctrl_add_handler(&video_ctrl_handler, &radio_ctrl_handler);
+
+Or you can add specific controls to a handler:
+
+ volume = v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_AUDIO_VOLUME, ...);
+ v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_BRIGHTNESS, ...);
+ v4l2_ctrl_new_std(&video_ctrl_handler, &ops, V4L2_CID_CONTRAST, ...);
+ v4l2_ctrl_add_ctrl(&radio_ctrl_handler, volume);
+
+What you should not do is make two identical controls for two handlers.
+For example:
+
+ v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
+ v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_AUDIO_MUTE, ...);
+
+This would be bad since muting the radio would not change the video mute
+control. The rule is to have one control for each hardware 'knob' that you
+can twiddle.
+
+
+Finding Controls
+================
+
+Normally you have created the controls yourself and you can store the struct
+v4l2_ctrl pointer into your own struct.
+
+But sometimes you need to find a control from another handler that you do
+not own. For example, if you have to find a volume control from a subdev.
+
+You can do that by calling v4l2_ctrl_find:
+
+ struct v4l2_ctrl *volume;
+
+ volume = v4l2_ctrl_find(sd->ctrl_handler, V4L2_CID_AUDIO_VOLUME);
+
+Since v4l2_ctrl_find will lock the handler you have to be careful where you
+use it. For example, this is not a good idea:
+
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ v4l2_ctrl_new_std(&ctrl_handler, &video_ops, V4L2_CID_BRIGHTNESS, ...);
+ v4l2_ctrl_new_std(&ctrl_handler, &video_ops, V4L2_CID_CONTRAST, ...);
+
+...and in video_ops.s_ctrl:
+
+ case V4L2_CID_BRIGHTNESS:
+ contrast = v4l2_find_ctrl(&ctrl_handler, V4L2_CID_CONTRAST);
+ ...
+
+When s_ctrl is called by the framework the ctrl_handler.lock is already taken, so
+attempting to find another control from the same handler will deadlock.
+
+It is recommended not to use this function from inside the control ops.
+
+
+Inheriting Controls
+===================
+
+When one control handler is added to another using v4l2_ctrl_add_handler, then
+by default all controls from one are merged to the other. But a subdev might
+have low-level controls that make sense for some advanced embedded system, but
+not when it is used in consumer-level hardware. In that case you want to keep
+those low-level controls local to the subdev. You can do this by simply
+setting the 'is_private' flag of the control to 1:
+
+ static const struct v4l2_ctrl_config ctrl_private = {
+ .ops = &ctrl_custom_ops,
+ .id = V4L2_CID_...,
+ .name = "Some Private Control",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .max = 15,
+ .step = 1,
+ .is_private = 1,
+ };
+
+ ctrl = v4l2_ctrl_new_custom(&foo->ctrl_handler, &ctrl_private, NULL);
+
+These controls will now be skipped when v4l2_ctrl_add_handler is called.
+
+
+V4L2_CTRL_TYPE_CTRL_CLASS Controls
+==================================
+
+Controls of this type can be used by GUIs to get the name of the control class.
+A fully featured GUI can make a dialog with multiple tabs with each tab
+containing the controls belonging to a particular control class. The name of
+each tab can be found by querying a special control with ID <control class | 1>.
+
+Drivers do not have to care about this. The framework will automatically add
+a control of this type whenever the first control belonging to a new control
+class is added.
+
+
+Differences from the Spec
+=========================
+
+There are a few places where the framework acts slightly differently from the
+V4L2 Specification. Those differences are described in this section. We will
+have to see whether we need to adjust the spec or not.
+
+1) It is no longer required to have all controls contained in a
+v4l2_ext_control array be from the same control class. The framework will be
+able to handle any type of control in the array. You need to set ctrl_class
+to 0 in order to enable this. If ctrl_class is non-zero, then it will still
+check that all controls belong to that control class.
+
+If you set ctrl_class to 0 and count to 0, then it will only return an error
+if there are no controls at all.
+
+2) Clarified the way error_idx works. For get and set it will be equal to
+count if nothing was done yet. If it is less than count then only the controls
+up to error_idx-1 were successfully applied.
+
+3) When attempting to read a button control the framework will return -EACCES
+instead of -EINVAL as stated in the spec. It seems to make more sense since
+button controls are write-only controls.
+
+4) Attempting to write to a read-only control will return -EACCES instead of
+-EINVAL as the spec says.
+
+5) The spec does not mention what should happen when you try to set/get a
+control class controls. ivtv currently returns -EINVAL (indicating that the
+control ID does not exist) while the framework will return -EACCES, which
+makes more sense.
+
+
+Proposals for Extensions
+========================
+
+Some ideas for future extensions to the spec:
+
+1) Add a V4L2_CTRL_FLAG_HEX to have values shown as hexadecimal instead of
+decimal. Useful for e.g. video_mute_yuv.
+
+2) It is possible to mark in the controls array which controls have been
+successfully written and which failed by for example adding a bit to the
+control ID. Not sure if it is worth the effort, though.
+
+3) Trying to set volatile inactive controls should result in -EACCESS.
+
+4) Add a new flag to mark volatile controls. Any application that wants
+to store the state of the controls can then skip volatile inactive controls.
+Currently it is not possible to detect such controls.
S: Maintained
ARM/CORTINA SYSTEMS GEMINI ARM ARCHITECTURE
-M: Paulius Zaleckas <paulius.zaleckas@gmail.com>
+M: Hans Ulli Kroll <ulli.kroll@googlemail.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
-T: git git://gitorious.org/linux-gemini/mainline.git
-S: Odd Fixes
+T: git git://git.berlios.de/gemini-board
+S: Maintained
F: arch/arm/mach-gemini/
ARM/EBSA110 MACHINE SUPPORT
F: arch/arm/mach-pxa/ezx.c
ARM/FARADAY FA526 PORT
-M: Paulius Zaleckas <paulius.zaleckas@gmail.com>
+M: Hans Ulli Kroll <ulli.kroll@googlemail.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
-S: Odd Fixes
+S: Maintained
+T: git://git.berlios.de/gemini-board
F: arch/arm/mm/*-fa*
ARM/FOOTBRIDGE ARCHITECTURE
F: arch/arm/mach-sa1100/jornada720.c
F: arch/arm/mach-sa1100/include/mach/jornada720.h
+ARM/INCOME PXA270 SUPPORT
+M: Marek Vasut <marek.vasut@gmail.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/mach-pxa/income.c
+F: arch/arm/mach-pxa/include/mach-pxa/income.h
+
ARM/INTEL IOP32X ARM ARCHITECTURE
M: Lennert Buytenhek <kernel@wantstofly.org>
M: Dan Williams <dan.j.williams@intel.com>
M: Paul Mundt <lethal@linux-sh.org>
M: Magnus Damm <magnus.damm@gmail.com>
L: linux-sh@vger.kernel.org
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/lethal/genesis-2.6.git
W: http://oss.renesas.com
+Q: http://patchwork.kernel.org/project/linux-sh/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/lethal/genesis-2.6.git
S: Supported
F: arch/arm/mach-shmobile/
F: drivers/sh/
F: drivers/mmc/host/cb710-mmc.*
F: include/linux/cb710.h
+ENE KB2426 (ENE0100/ENE020XX) INFRARED RECEIVER
+M: Maxim Levitsky <maximlevitsky@gmail.com>
+S: Maintained
+F: drivers/media/IR/ene_ir.c
+F: drivers/media/IR/ene_ir.h
+
EPSON 1355 FRAMEBUFFER DRIVER
M: Christopher Hoover <ch@murgatroid.com>
M: Christopher Hoover <ch@hpl.hp.com>
#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
-#define dma_is_consistent(d, h) (1)
#define dma_cache_sync(dev, va, size, dir) ((void)0)
-#define dma_get_cache_alignment() L1_CACHE_BYTES
#endif /* _ALPHA_DMA_MAPPING_H */
# define TIOCPKT_START 8
# define TIOCPKT_NOSTOP 16
# define TIOCPKT_DOSTOP 32
+# define TIOCPKT_IOCTL 64
#define TIOCNOTTY 0x5422
#define TIOCGSID 0x5429 /* Return the session ID of FD */
#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
+#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */
#define TIOCSERCONFIG 0x5453
#define TIOCSERGWILD 0x5454
#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
-#define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
-#define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
#endif /* _ASM_ALPHA_IOCTLS_H */
#include <asm-generic/scatterlist.h>
-#define ISA_DMA_THRESHOLD (~0UL)
-
#endif /* !(_ALPHA_SCATTERLIST_H) */
#define FLUSHO 0x00800000
#define PENDIN 0x20000000
#define IEXTEN 0x00000400
+#define EXTPROC 0x10000000
/* Values for the ACTION argument to `tcflow'. */
#define TCOOFF 0
select ARCH_HAS_CPUFREQ
select HAVE_CLK
select ARCH_USES_GETTIMEOFFSET
+ select HAVE_S3C2410_I2C
help
Samsung S3C2410X CPU based systems, such as the Simtec Electronics
BAST (<http://www.simtec.co.uk/products/EB110ITX/>), the IPAQ 1940 or
select S3C_DEV_NAND
select USB_ARCH_HAS_OHCI
select SAMSUNG_GPIOLIB_4BIT
+ select HAVE_S3C2410_I2C
+ select HAVE_S3C2410_WATCHDOG
help
Samsung S3C64XX series based systems
select CPU_V6
select GENERIC_GPIO
select HAVE_CLK
+ select HAVE_S3C2410_WATCHDOG
select ARCH_USES_GETTIMEOFFSET
+ select HAVE_S3C2410_I2C
+ select HAVE_S3C_RTC
help
Samsung S5P6440 CPU based systems
select GENERIC_GPIO
select HAVE_CLK
select ARCH_USES_GETTIMEOFFSET
+ select HAVE_S3C2410_WATCHDOG
help
Samsung S5P6442 CPU based systems
select CPU_V7
select ARM_L1_CACHE_SHIFT_6
select ARCH_USES_GETTIMEOFFSET
+ select HAVE_S3C2410_I2C
+ select HAVE_S3C_RTC
+ select HAVE_S3C2410_WATCHDOG
help
Samsung S5PC100 series based systems
select HAVE_CLK
select ARM_L1_CACHE_SHIFT_6
select ARCH_USES_GETTIMEOFFSET
+ select HAVE_S3C2410_I2C
+ select HAVE_S3C_RTC
+ select HAVE_S3C2410_WATCHDOG
help
Samsung S5PV210/S5PC110 series based systems
+config ARCH_S5PV310
+ bool "Samsung S5PV310/S5PC210"
+ select CPU_V7
+ select GENERIC_GPIO
+ select HAVE_CLK
+ select GENERIC_CLOCKEVENTS
+ help
+ Samsung S5PV310 series based systems
+
config ARCH_SHARK
bool "Shark"
select CPU_SA110
source "arch/arm/mach-s5pv210/Kconfig"
+source "arch/arm/mach-s5pv310/Kconfig"
+
source "arch/arm/mach-shmobile/Kconfig"
source "arch/arm/plat-stmp3xxx/Kconfig"
is not correctly implemented in PL310 as clean lines are not
invalidated as a result of these operations. Note that this errata
uses Texas Instrument's secure monitor api.
+
+config ARM_ERRATA_720789
+ bool "ARM errata: TLBIASIDIS and TLBIMVAIS operations can broadcast a faulty ASID"
+ depends on CPU_V7 && SMP
+ help
+ This option enables the workaround for the 720789 Cortex-A9 (prior to
+ r2p0) erratum. A faulty ASID can be sent to the other CPUs for the
+ broadcasted CP15 TLB maintenance operations TLBIASIDIS and TLBIMVAIS.
+ As a consequence of this erratum, some TLB entries which should be
+ invalidated are not, resulting in an incoherency in the system page
+ tables. The workaround changes the TLB flushing routines to invalidate
+ entries regardless of the ASID.
endmenu
source "arch/arm/common/Kconfig"
bool "Symmetric Multi-Processing (EXPERIMENTAL)"
depends on EXPERIMENTAL && (REALVIEW_EB_ARM11MP || REALVIEW_EB_A9MP ||\
MACH_REALVIEW_PB11MP || MACH_REALVIEW_PBX || ARCH_OMAP4 ||\
- ARCH_U8500 || ARCH_VEXPRESS_CA9X4 || ARCH_TEGRA)
+ ARCH_S5PV310 || ARCH_TEGRA || ARCH_U8500 || ARCH_VEXPRESS_CA9X4)
depends on GENERIC_CLOCKEVENTS
select USE_GENERIC_SMP_HELPERS
- select HAVE_ARM_SCU if (ARCH_REALVIEW || ARCH_OMAP4 || ARCH_U8500 || \
- ARCH_VEXPRESS_CA9X4 || ARCH_TEGRA)
+ select HAVE_ARM_SCU if ARCH_REALVIEW || ARCH_OMAP4 || ARCH_S5PV310 ||\
+ ARCH_TEGRA || ARCH_U8500 || ARCH_VEXPRESS_CA9X4
help
This enables support for systems with more than one CPU. If you have
a system with only one CPU, like most personal computers, say N. If
bool "Use local timer interrupts"
depends on SMP && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || \
REALVIEW_EB_A9MP || MACH_REALVIEW_PBX || ARCH_OMAP4 || \
- ARCH_U8500 || ARCH_VEXPRESS_CA9X4 || ARCH_TEGRA)
+ ARCH_S5PV310 || ARCH_TEGRA || ARCH_U8500 || ARCH_VEXPRESS_CA9X4)
default y
- select HAVE_ARM_TWD if (ARCH_REALVIEW || ARCH_VEXPRESS || ARCH_OMAP4 || \\
- ARCH_U8500 || ARCH_TEGRA
+ select HAVE_ARM_TWD if ARCH_REALVIEW || ARCH_OMAP4 || ARCH_S5PV310 || \
+ ARCH_TEGRA || ARCH_U8500 || ARCH_VEXPRESS
help
Enable support for local timers on SMP platforms, rather then the
legacy IPI broadcast method. Local timers allows the system
config HZ
int
- default 200 if ARCH_EBSA110 || ARCH_S3C2410 || ARCH_S5P6440 || ARCH_S5P6442 || ARCH_S5PV210
+ default 200 if ARCH_EBSA110 || ARCH_S3C2410 || ARCH_S5P6440 || \
+ ARCH_S5P6442 || ARCH_S5PV210 || ARCH_S5PV310
default OMAP_32K_TIMER_HZ if ARCH_OMAP && OMAP_32K_TIMER
default AT91_TIMER_HZ if ARCH_AT91
default SHMOBILE_TIMER_HZ if ARCH_SHMOBILE
machine-$(CONFIG_ARCH_S5P6442) := s5p6442
machine-$(CONFIG_ARCH_S5PC100) := s5pc100
machine-$(CONFIG_ARCH_S5PV210) := s5pv210
+machine-$(CONFIG_ARCH_S5PV310) := s5pv310
machine-$(CONFIG_ARCH_SA1100) := sa1100
machine-$(CONFIG_ARCH_SHARK) := shark
machine-$(CONFIG_ARCH_SHMOBILE) := shmobile
OBJS += head-xscale.o
endif
-ifeq ($(CONFIG_PXA_SHARPSL),y)
+ifeq ($(CONFIG_PXA_SHARPSL_DETECT_MACH_ID),y)
OBJS += head-sharpsl.o
endif
.text
adr r0, LC0
- ARM( ldmia r0, {r1, r2, r3, r5, r6, r11, ip, sp})
- THUMB( ldmia r0, {r1, r2, r3, r5, r6, r11, ip} )
- THUMB( ldr sp, [r0, #32] )
+ ldmia r0, {r1, r2, r3, r5, r6, r11, ip}
+ ldr sp, [r0, #28]
#ifdef CONFIG_AUTO_ZRELADDR
@ determine final kernel image address
and r4, pc, #0xf8000000
return 0;
}
-int dma_needs_bounce(struct device *dev, dma_addr_t dma_addr, size_t size)
-{
- dev_dbg(dev, "%s: dma_addr %08x, size %08x\n",
- __func__, dma_addr, size);
- return (dev->bus == &pci_bus_type) &&
- ((dma_addr + size - PHYS_OFFSET) >= SZ_64M);
-}
-
int __init it8152_pci_setup(int nr, struct pci_sys_data *sys)
{
it8152_io.start = IT8152_IO_BASE + 0x12000;
+++ /dev/null
-CONFIG_EXPERIMENTAL=y
-CONFIG_SYSFS_DEPRECATED_V2=y
-CONFIG_BLK_DEV_INITRD=y
-CONFIG_KALLSYMS_ALL=y
-CONFIG_MODULES=y
-CONFIG_MODULE_UNLOAD=y
-# CONFIG_BLK_DEV_BSG is not set
-CONFIG_ARCH_S5PV210=y
-CONFIG_S3C_LOWLEVEL_UART_PORT=1
-CONFIG_MACH_SMDKC110=y
-CONFIG_VMSPLIT_2G=y
-CONFIG_PREEMPT=y
-CONFIG_AEABI=y
-CONFIG_CMDLINE="root=/dev/ram0 rw ramdisk=8192 initrd=0x20800000,8M console=ttySAC1,115200 init=/linuxrc"
-CONFIG_VFP=y
-CONFIG_NEON=y
-CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
-CONFIG_BLK_DEV_LOOP=y
-CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_RAM_SIZE=8192
-# CONFIG_MISC_DEVICES is not set
-CONFIG_SCSI=y
-CONFIG_BLK_DEV_SD=y
-CONFIG_CHR_DEV_SG=y
-CONFIG_INPUT_EVDEV=y
-# CONFIG_INPUT_KEYBOARD is not set
-# CONFIG_INPUT_MOUSE is not set
-CONFIG_INPUT_TOUCHSCREEN=y
-CONFIG_SERIAL_8250=y
-CONFIG_SERIAL_SAMSUNG=y
-CONFIG_SERIAL_SAMSUNG_CONSOLE=y
-CONFIG_HW_RANDOM=y
-# CONFIG_HWMON is not set
-# CONFIG_VGA_CONSOLE is not set
-# CONFIG_HID_SUPPORT is not set
-# CONFIG_USB_SUPPORT is not set
-CONFIG_EXT2_FS=y
-CONFIG_INOTIFY=y
-CONFIG_MSDOS_FS=y
-CONFIG_VFAT_FS=y
-CONFIG_TMPFS=y
-CONFIG_TMPFS_POSIX_ACL=y
-CONFIG_CRAMFS=y
-CONFIG_ROMFS_FS=y
-CONFIG_PARTITION_ADVANCED=y
-CONFIG_BSD_DISKLABEL=y
-CONFIG_SOLARIS_X86_PARTITION=y
-CONFIG_NLS_CODEPAGE_437=y
-CONFIG_NLS_ASCII=y
-CONFIG_NLS_ISO8859_1=y
-CONFIG_MAGIC_SYSRQ=y
-CONFIG_DEBUG_KERNEL=y
-# CONFIG_DEBUG_PREEMPT is not set
-CONFIG_DEBUG_RT_MUTEXES=y
-CONFIG_DEBUG_SPINLOCK=y
-CONFIG_DEBUG_MUTEXES=y
-CONFIG_DEBUG_SPINLOCK_SLEEP=y
-CONFIG_DEBUG_INFO=y
-# CONFIG_RCU_CPU_STALL_DETECTOR is not set
-CONFIG_SYSCTL_SYSCALL_CHECK=y
-CONFIG_DEBUG_USER=y
-CONFIG_DEBUG_ERRORS=y
-CONFIG_DEBUG_LL=y
-CONFIG_EARLY_PRINTK=y
-CONFIG_DEBUG_S3C_UART=1
-CONFIG_CRC_CCITT=y
# CONFIG_BLK_DEV_BSG is not set
CONFIG_ARCH_S5PV210=y
CONFIG_S3C_LOWLEVEL_UART_PORT=1
+CONFIG_S3C_DEV_FB=y
+CONFIG_S5PV210_SETUP_FB_24BPP=y
+CONFIG_MACH_AQUILA=y
+CONFIG_MACH_GONI=y
+CONFIG_MACH_SMDKC110=y
CONFIG_MACH_SMDKV210=y
CONFIG_VMSPLIT_2G=y
CONFIG_PREEMPT=y
* cache before the transfer is done, causing old data to be seen by
* the CPU.
*/
-#define ARCH_KMALLOC_MINALIGN L1_CACHE_BYTES
+#define ARCH_DMA_MINALIGN L1_CACHE_BYTES
/*
* With EABI on ARMv5 and above we must have 64-bit aligned slab pointers.
return 0;
}
-static inline int dma_get_cache_alignment(void)
-{
- return 32;
-}
-
-static inline int dma_is_consistent(struct device *dev, dma_addr_t handle)
-{
- return !!arch_is_coherent();
-}
-
/*
* DMA errors are defined by all-bits-set in the DMA address.
*/
* DMA access and 1 if the buffer needs to be bounced.
*
*/
+#ifdef CONFIG_SA1111
extern int dma_needs_bounce(struct device*, dma_addr_t, size_t);
+#else
+static inline int dma_needs_bounce(struct device *dev, dma_addr_t addr,
+ size_t size)
+{
+ return 0;
+}
+#endif
/*
* The DMA API, implemented by dmabounce.c. See below for descriptions.
#define R_ARM_THM_CALL 10
#define R_ARM_THM_JUMP24 30
+#define R_ARM_THM_MOVW_ABS_NC 47
+#define R_ARM_THM_MOVT_ABS 48
/*
* These are used to set parameters in the core dumps.
/* CoreSight Component Registers */
#define CSCR_CLASS 0xff4
-#define CSCR_PRSR 0x314
-
#define UNLOCK_MAGIC 0xc5acce55
/* ETM control register, "ETM Architecture", 3.3.1 */
ETMCTRL_BRANCH_OUTPUT | \
ETMCTRL_DO_CONTEXTID)
+/* ETM management registers, "ETM Architecture", 3.5.24 */
+#define ETMMR_OSLAR 0x300
+#define ETMMR_OSLSR 0x304
+#define ETMMR_OSSRR 0x308
+#define ETMMR_PDSR 0x314
+
/* ETB registers, "CoreSight Components TRM", 9.3 */
#define ETBR_DEPTH 0x04
#define ETBR_STATUS 0x0c
#define SCOOP_GPWR 0x24
#define SCOOP_GPRR 0x28
-#define SCOOP_GPCR_PA22 ( 1 << 12 )
-#define SCOOP_GPCR_PA21 ( 1 << 11 )
-#define SCOOP_GPCR_PA20 ( 1 << 10 )
-#define SCOOP_GPCR_PA19 ( 1 << 9 )
-#define SCOOP_GPCR_PA18 ( 1 << 8 )
-#define SCOOP_GPCR_PA17 ( 1 << 7 )
-#define SCOOP_GPCR_PA16 ( 1 << 6 )
-#define SCOOP_GPCR_PA15 ( 1 << 5 )
-#define SCOOP_GPCR_PA14 ( 1 << 4 )
-#define SCOOP_GPCR_PA13 ( 1 << 3 )
-#define SCOOP_GPCR_PA12 ( 1 << 2 )
-#define SCOOP_GPCR_PA11 ( 1 << 1 )
+#define SCOOP_CPR_OUT (1 << 7)
+#define SCOOP_CPR_SD_3V (1 << 2)
+#define SCOOP_CPR_CF_XV (1 << 1)
+#define SCOOP_CPR_CF_3V (1 << 0)
+
+#define SCOOP_GPCR_PA22 (1 << 12)
+#define SCOOP_GPCR_PA21 (1 << 11)
+#define SCOOP_GPCR_PA20 (1 << 10)
+#define SCOOP_GPCR_PA19 (1 << 9)
+#define SCOOP_GPCR_PA18 (1 << 8)
+#define SCOOP_GPCR_PA17 (1 << 7)
+#define SCOOP_GPCR_PA16 (1 << 6)
+#define SCOOP_GPCR_PA15 (1 << 5)
+#define SCOOP_GPCR_PA14 (1 << 4)
+#define SCOOP_GPCR_PA13 (1 << 3)
+#define SCOOP_GPCR_PA12 (1 << 2)
+#define SCOOP_GPCR_PA11 (1 << 1)
struct scoop_config {
unsigned short io_out;
#define TCSETSF2 _IOW('T',0x2D, struct termios2)
#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
+#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */
#define TIOCGRS485 0x542E
#define TIOCSRS485 0x542F
#define TIOCPKT_START 8
#define TIOCPKT_NOSTOP 16
#define TIOCPKT_DOSTOP 32
+#define TIOCPKT_IOCTL 64
#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
#define FLUSHO 0010000
#define PENDIN 0040000
#define IEXTEN 0100000
+#define EXTPROC 0200000
/* tcflow() and TCXONC use these */
#define TCOOFF 0
if (tlb_flag(TLB_V6_I_ASID))
asm("mcr p15, 0, %0, c8, c5, 2" : : "r" (asid) : "cc");
if (tlb_flag(TLB_V7_UIS_ASID))
+#ifdef CONFIG_ARM_ERRATA_720789
+ asm("mcr p15, 0, %0, c8, c3, 0" : : "r" (zero) : "cc");
+#else
asm("mcr p15, 0, %0, c8, c3, 2" : : "r" (asid) : "cc");
+#endif
if (tlb_flag(TLB_BTB)) {
/* flush the branch target cache */
if (tlb_flag(TLB_V6_I_PAGE))
asm("mcr p15, 0, %0, c8, c5, 1" : : "r" (uaddr) : "cc");
if (tlb_flag(TLB_V7_UIS_PAGE))
+#ifdef CONFIG_ARM_ERRATA_720789
+ asm("mcr p15, 0, %0, c8, c3, 3" : : "r" (uaddr & PAGE_MASK) : "cc");
+#else
asm("mcr p15, 0, %0, c8, c3, 1" : : "r" (uaddr) : "cc");
+#endif
if (tlb_flag(TLB_BTB)) {
/* flush the branch target cache */
#define CALL(x) .long x
#ifdef CONFIG_FUNCTION_TRACER
+/*
+ * When compiling with -pg, gcc inserts a call to the mcount routine at the
+ * start of every function. In mcount, apart from the function's address (in
+ * lr), we need to get hold of the function's caller's address.
+ *
+ * Older GCCs (pre-4.4) inserted a call to a routine called mcount like this:
+ *
+ * bl mcount
+ *
+ * These versions have the limitation that in order for the mcount routine to
+ * be able to determine the function's caller's address, an APCS-style frame
+ * pointer (which is set up with something like the code below) is required.
+ *
+ * mov ip, sp
+ * push {fp, ip, lr, pc}
+ * sub fp, ip, #4
+ *
+ * With EABI, these frame pointers are not available unless -mapcs-frame is
+ * specified, and if building as Thumb-2, not even then.
+ *
+ * Newer GCCs (4.4+) solve this problem by introducing a new version of mcount,
+ * with call sites like:
+ *
+ * push {lr}
+ * bl __gnu_mcount_nc
+ *
+ * With these compilers, frame pointers are not necessary.
+ *
+ * mcount can be thought of as a function called in the middle of a subroutine
+ * call. As such, it needs to be transparent for both the caller and the
+ * callee: the original lr needs to be restored when leaving mcount, and no
+ * registers should be clobbered. (In the __gnu_mcount_nc implementation, we
+ * clobber the ip register. This is OK because the ARM calling convention
+ * allows it to be clobbered in subroutines and doesn't use it to hold
+ * parameters.)
+ */
#ifdef CONFIG_DYNAMIC_FTRACE
ENTRY(mcount)
- stmdb sp!, {r0-r3, lr}
- mov r0, lr
- sub r0, r0, #MCOUNT_INSN_SIZE
+ stmdb sp!, {r0-r3, lr}
+ mov r0, lr
+ sub r0, r0, #MCOUNT_INSN_SIZE
.globl mcount_call
mcount_call:
- bl ftrace_stub
- ldr lr, [fp, #-4] @ restore lr
- ldmia sp!, {r0-r3, pc}
+ bl ftrace_stub
+ ldr lr, [fp, #-4] @ restore lr
+ ldmia sp!, {r0-r3, pc}
ENTRY(ftrace_caller)
- stmdb sp!, {r0-r3, lr}
- ldr r1, [fp, #-4]
- mov r0, lr
- sub r0, r0, #MCOUNT_INSN_SIZE
+ stmdb sp!, {r0-r3, lr}
+ ldr r1, [fp, #-4]
+ mov r0, lr
+ sub r0, r0, #MCOUNT_INSN_SIZE
.globl ftrace_call
ftrace_call:
- bl ftrace_stub
- ldr lr, [fp, #-4] @ restore lr
- ldmia sp!, {r0-r3, pc}
+ bl ftrace_stub
+ ldr lr, [fp, #-4] @ restore lr
+ ldmia sp!, {r0-r3, pc}
#else
ENTRY(__gnu_mcount_nc)
- stmdb sp!, {r0-r3, lr}
- ldr r0, =ftrace_trace_function
- ldr r2, [r0]
- adr r0, ftrace_stub
- cmp r0, r2
- bne gnu_trace
- ldmia sp!, {r0-r3, ip, lr}
- mov pc, ip
+ stmdb sp!, {r0-r3, lr}
+ ldr r0, =ftrace_trace_function
+ ldr r2, [r0]
+ adr r0, ftrace_stub
+ cmp r0, r2
+ bne gnu_trace
+ ldmia sp!, {r0-r3, ip, lr}
+ mov pc, ip
gnu_trace:
- ldr r1, [sp, #20] @ lr of instrumented routine
- mov r0, lr
- sub r0, r0, #MCOUNT_INSN_SIZE
- mov lr, pc
- mov pc, r2
- ldmia sp!, {r0-r3, ip, lr}
- mov pc, ip
+ ldr r1, [sp, #20] @ lr of instrumented routine
+ mov r0, lr
+ sub r0, r0, #MCOUNT_INSN_SIZE
+ mov lr, pc
+ mov pc, r2
+ ldmia sp!, {r0-r3, ip, lr}
+ mov pc, ip
ENTRY(mcount)
- stmdb sp!, {r0-r3, lr}
- ldr r0, =ftrace_trace_function
- ldr r2, [r0]
- adr r0, ftrace_stub
- cmp r0, r2
- bne trace
- ldr lr, [fp, #-4] @ restore lr
- ldmia sp!, {r0-r3, pc}
+ stmdb sp!, {r0-r3, lr}
+ ldr r0, =ftrace_trace_function
+ ldr r2, [r0]
+ adr r0, ftrace_stub
+ cmp r0, r2
+ bne trace
+ ldr lr, [fp, #-4] @ restore lr
+ ldmia sp!, {r0-r3, pc}
trace:
- ldr r1, [fp, #-4] @ lr of instrumented routine
- mov r0, lr
- sub r0, r0, #MCOUNT_INSN_SIZE
- mov lr, pc
- mov pc, r2
- ldr lr, [fp, #-4] @ restore lr
- ldmia sp!, {r0-r3, pc}
+ ldr r1, [fp, #-4] @ lr of instrumented routine
+ mov r0, lr
+ sub r0, r0, #MCOUNT_INSN_SIZE
+ mov lr, pc
+ mov pc, r2
+ ldr lr, [fp, #-4] @ restore lr
+ ldmia sp!, {r0-r3, pc}
#endif /* CONFIG_DYNAMIC_FTRACE */
.globl ftrace_stub
ftrace_stub:
- mov pc, lr
+ mov pc, lr
#endif /* CONFIG_FUNCTION_TRACER */
t->etm_portsz = 1;
etm_unlock(t);
- ret = etm_readl(t, CSCR_PRSR);
+ (void)etm_readl(t, ETMMR_PDSR);
+ /* dummy first read */
+ (void)etm_readl(&tracer, ETMMR_OSSRR);
t->ncmppairs = etm_readl(t, ETMR_CONFCODE) & 0xf;
etm_writel(t, 0x440, ETMR_CTRL);
unsigned long loc;
Elf32_Sym *sym;
s32 offset;
+#ifdef CONFIG_THUMB2_KERNEL
u32 upper, lower, sign, j1, j2;
+#endif
offset = ELF32_R_SYM(rel->r_info);
if (offset < 0 || offset > (symsec->sh_size / sizeof(Elf32_Sym))) {
(offset & 0x0fff);
break;
+#ifdef CONFIG_THUMB2_KERNEL
case R_ARM_THM_CALL:
case R_ARM_THM_JUMP24:
upper = *(u16 *)loc;
*(u16 *)(loc + 2) = (u16)((lower & 0xd000) |
(j1 << 13) | (j2 << 11) |
((offset >> 1) & 0x07ff));
+ break;
+
+ case R_ARM_THM_MOVW_ABS_NC:
+ case R_ARM_THM_MOVT_ABS:
upper = *(u16 *)loc;
lower = *(u16 *)(loc + 2);
+
+ /*
+ * MOVT/MOVW instructions encoding in Thumb-2:
+ *
+ * i = upper[10]
+ * imm4 = upper[3:0]
+ * imm3 = lower[14:12]
+ * imm8 = lower[7:0]
+ *
+ * imm16 = imm4:i:imm3:imm8
+ */
+ offset = ((upper & 0x000f) << 12) |
+ ((upper & 0x0400) << 1) |
+ ((lower & 0x7000) >> 4) | (lower & 0x00ff);
+ offset = (offset ^ 0x8000) - 0x8000;
+ offset += sym->st_value;
+
+ if (ELF32_R_TYPE(rel->r_info) == R_ARM_THM_MOVT_ABS)
+ offset >>= 16;
+
+ *(u16 *)loc = (u16)((upper & 0xfbf0) |
+ ((offset & 0xf000) >> 12) |
+ ((offset & 0x0800) >> 1));
+ *(u16 *)(loc + 2) = (u16)((lower & 0x8f00) |
+ ((offset & 0x0700) << 4) |
+ (offset & 0x00ff));
break;
+#endif
default:
printk(KERN_ERR "%s: unknown relocation: %u\n",
* SPI devices.
*/
#if defined(CONFIG_MTD_DATAFLASH)
-static struct mtd_partition __initdata cam60_spi_partitions[] = {
+static struct mtd_partition cam60_spi_partitions[] = {
{
.name = "BOOT1",
.offset = 0,
},
};
-static struct flash_platform_data __initdata cam60_spi_flash_platform_data = {
+static struct flash_platform_data cam60_spi_flash_platform_data = {
.name = "spi_flash",
.parts = cam60_spi_partitions,
.nr_parts = ARRAY_SIZE(cam60_spi_partitions)
};
#endif
-static struct spi_board_info cam60_spi_devices[] = {
+static struct spi_board_info cam60_spi_devices[] __initdata = {
#if defined(CONFIG_MTD_DATAFLASH)
{ /* DataFlash chip */
.modalias = "mtd_dataflash",
#define SYSPLD_VIRT_BASE 0xfe000000
#define SYSPLD_BASE SYSPLD_VIRT_BASE
-#ifndef __ASSEMBLER__
-
-#define PCIO_BASE IO_BASE
-
-#endif
-
-
#if defined (CONFIG_ARCH_AUTCPU12)
#define CS89712_VIRT_BASE CLPS7111_VIRT_BASE
menu "Cortina Systems Gemini Implementations"
+config MACH_NAS4220B
+ bool "Raidsonic NAS-4220-B"
+ select GEMINI_MEM_SWAP
+ help
+ Say Y here if you intend to run this kernel on a
+ Raidsonic NAS-4220-B.
+
config MACH_RUT100
bool "Teltonika RUT100"
select GEMINI_MEM_SWAP
Say Y here if you intend to run this kernel on a
Teltonika 3G Router RUT100.
+config MACH_WBD111
+ bool "Wiliboard WBD-111"
+ select GEMINI_MEM_SWAP
+ help
+ Say Y here if you intend to run this kernel on a
+ Wiliboard WBD-111.
+
+config MACH_WBD222
+ bool "Wiliboard WBD-222"
+ select GEMINI_MEM_SWAP
+ help
+ Say Y here if you intend to run this kernel on a
+ Wiliboard WBD-222.
+
endmenu
config GEMINI_MEM_SWAP
obj-y := irq.o mm.o time.o devices.o gpio.o
# Board-specific support
+obj-$(CONFIG_MACH_NAS4220B) += board-nas4220b.o
obj-$(CONFIG_MACH_RUT100) += board-rut1xx.o
+obj-$(CONFIG_MACH_WBD111) += board-wbd111.o
+obj-$(CONFIG_MACH_WBD222) += board-wbd222.o
--- /dev/null
+/*
+ * Support for Raidsonic NAS-4220-B
+ *
+ * Copyright (C) 2009 Janos Laube <janos.dev@gmail.com>
+ *
+ * based on rut1xx.c
+ * Copyright (C) 2008 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/input.h>
+#include <linux/gpio_keys.h>
+#include <linux/mdio-gpio.h>
+#include <linux/io.h>
+
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+
+#include <mach/hardware.h>
+#include <mach/global_reg.h>
+
+#include "common.h"
+
+static struct sys_timer ib4220b_timer = {
+ .init = gemini_timer_init,
+};
+
+static struct gpio_led ib4220b_leds[] = {
+ {
+ .name = "nas4220b:orange:hdd",
+ .default_trigger = "none",
+ .gpio = 60,
+ },
+ {
+ .name = "nas4220b:green:os",
+ .default_trigger = "heartbeat",
+ .gpio = 62,
+ },
+};
+
+static struct gpio_led_platform_data ib4220b_leds_data = {
+ .num_leds = ARRAY_SIZE(ib4220b_leds),
+ .leds = ib4220b_leds,
+};
+
+static struct platform_device ib4220b_led_device = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &ib4220b_leds_data,
+ },
+};
+
+static struct gpio_keys_button ib4220b_keys[] = {
+ {
+ .code = KEY_SETUP,
+ .gpio = 61,
+ .active_low = 1,
+ .desc = "Backup Button",
+ .type = EV_KEY,
+ },
+ {
+ .code = KEY_RESTART,
+ .gpio = 63,
+ .active_low = 1,
+ .desc = "Softreset Button",
+ .type = EV_KEY,
+ },
+};
+
+static struct gpio_keys_platform_data ib4220b_keys_data = {
+ .buttons = ib4220b_keys,
+ .nbuttons = ARRAY_SIZE(ib4220b_keys),
+};
+
+static struct platform_device ib4220b_key_device = {
+ .name = "gpio-keys",
+ .id = -1,
+ .dev = {
+ .platform_data = &ib4220b_keys_data,
+ },
+};
+
+static void __init ib4220b_init(void)
+{
+ gemini_gpio_init();
+ platform_register_uart();
+ platform_register_pflash(SZ_16M, NULL, 0);
+ platform_device_register(&ib4220b_led_device);
+ platform_device_register(&ib4220b_key_device);
+}
+
+MACHINE_START(NAS4220B, "Raidsonic NAS IB-4220-B")
+ .phys_io = 0x7fffc000,
+ .io_pg_offst = ((0xffffc000) >> 18) & 0xfffc,
+ .boot_params = 0x100,
+ .map_io = gemini_map_io,
+ .init_irq = gemini_init_irq,
+ .timer = &ib4220b_timer,
+ .init_machine = ib4220b_init,
+MACHINE_END
--- /dev/null
+/*
+ * Support for Wiliboard WBD-111
+ *
+ * Copyright (C) 2009 Imre Kaloz <kaloz@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/input.h>
+#include <linux/skbuff.h>
+#include <linux/gpio_keys.h>
+#include <linux/mdio-gpio.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+
+
+#include "common.h"
+
+static struct gpio_keys_button wbd111_keys[] = {
+ {
+ .code = KEY_SETUP,
+ .gpio = 5,
+ .active_low = 1,
+ .desc = "reset",
+ .type = EV_KEY,
+ },
+};
+
+static struct gpio_keys_platform_data wbd111_keys_data = {
+ .buttons = wbd111_keys,
+ .nbuttons = ARRAY_SIZE(wbd111_keys),
+};
+
+static struct platform_device wbd111_keys_device = {
+ .name = "gpio-keys",
+ .id = -1,
+ .dev = {
+ .platform_data = &wbd111_keys_data,
+ },
+};
+
+static struct gpio_led wbd111_leds[] = {
+ {
+ .name = "L3red",
+ .gpio = 1,
+ },
+ {
+ .name = "L4green",
+ .gpio = 2,
+ },
+ {
+ .name = "L4red",
+ .gpio = 3,
+ },
+ {
+ .name = "L3green",
+ .gpio = 5,
+ },
+};
+
+static struct gpio_led_platform_data wbd111_leds_data = {
+ .num_leds = ARRAY_SIZE(wbd111_leds),
+ .leds = wbd111_leds,
+};
+
+static struct platform_device wbd111_leds_device = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &wbd111_leds_data,
+ },
+};
+
+static struct sys_timer wbd111_timer = {
+ .init = gemini_timer_init,
+};
+
+#ifdef CONFIG_MTD_PARTITIONS
+static struct mtd_partition wbd111_partitions[] = {
+ {
+ .name = "RedBoot",
+ .offset = 0,
+ .size = 0x020000,
+ .mask_flags = MTD_WRITEABLE,
+ } , {
+ .name = "kernel",
+ .offset = 0x020000,
+ .size = 0x100000,
+ } , {
+ .name = "rootfs",
+ .offset = 0x120000,
+ .size = 0x6a0000,
+ } , {
+ .name = "VCTL",
+ .offset = 0x7c0000,
+ .size = 0x010000,
+ .mask_flags = MTD_WRITEABLE,
+ } , {
+ .name = "cfg",
+ .offset = 0x7d0000,
+ .size = 0x010000,
+ .mask_flags = MTD_WRITEABLE,
+ } , {
+ .name = "FIS",
+ .offset = 0x7e0000,
+ .size = 0x010000,
+ .mask_flags = MTD_WRITEABLE,
+ }
+};
+#define wbd111_num_partitions ARRAY_SIZE(wbd111_partitions)
+#else
+#define wbd111_partitions NULL
+#define wbd111_num_partitions 0
+#endif /* CONFIG_MTD_PARTITIONS */
+
+static void __init wbd111_init(void)
+{
+ gemini_gpio_init();
+ platform_register_uart();
+ platform_register_pflash(SZ_8M, wbd111_partitions,
+ wbd111_num_partitions);
+ platform_device_register(&wbd111_leds_device);
+ platform_device_register(&wbd111_keys_device);
+}
+
+MACHINE_START(WBD111, "Wiliboard WBD-111")
+ .phys_io = 0x7fffc000,
+ .io_pg_offst = ((0xffffc000) >> 18) & 0xfffc,
+ .boot_params = 0x100,
+ .map_io = gemini_map_io,
+ .init_irq = gemini_init_irq,
+ .timer = &wbd111_timer,
+ .init_machine = wbd111_init,
+MACHINE_END
--- /dev/null
+/*
+ * Support for Wiliboard WBD-222
+ *
+ * Copyright (C) 2009 Imre Kaloz <kaloz@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/input.h>
+#include <linux/skbuff.h>
+#include <linux/gpio_keys.h>
+#include <linux/mdio-gpio.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+
+
+#include "common.h"
+
+static struct gpio_keys_button wbd222_keys[] = {
+ {
+ .code = KEY_SETUP,
+ .gpio = 5,
+ .active_low = 1,
+ .desc = "reset",
+ .type = EV_KEY,
+ },
+};
+
+static struct gpio_keys_platform_data wbd222_keys_data = {
+ .buttons = wbd222_keys,
+ .nbuttons = ARRAY_SIZE(wbd222_keys),
+};
+
+static struct platform_device wbd222_keys_device = {
+ .name = "gpio-keys",
+ .id = -1,
+ .dev = {
+ .platform_data = &wbd222_keys_data,
+ },
+};
+
+static struct gpio_led wbd222_leds[] = {
+ {
+ .name = "L3red",
+ .gpio = 1,
+ },
+ {
+ .name = "L4green",
+ .gpio = 2,
+ },
+ {
+ .name = "L4red",
+ .gpio = 3,
+ },
+ {
+ .name = "L3green",
+ .gpio = 5,
+ },
+};
+
+static struct gpio_led_platform_data wbd222_leds_data = {
+ .num_leds = ARRAY_SIZE(wbd222_leds),
+ .leds = wbd222_leds,
+};
+
+static struct platform_device wbd222_leds_device = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &wbd222_leds_data,
+ },
+};
+
+static struct sys_timer wbd222_timer = {
+ .init = gemini_timer_init,
+};
+
+#ifdef CONFIG_MTD_PARTITIONS
+static struct mtd_partition wbd222_partitions[] = {
+ {
+ .name = "RedBoot",
+ .offset = 0,
+ .size = 0x020000,
+ .mask_flags = MTD_WRITEABLE,
+ } , {
+ .name = "kernel",
+ .offset = 0x020000,
+ .size = 0x100000,
+ } , {
+ .name = "rootfs",
+ .offset = 0x120000,
+ .size = 0x6a0000,
+ } , {
+ .name = "VCTL",
+ .offset = 0x7c0000,
+ .size = 0x010000,
+ .mask_flags = MTD_WRITEABLE,
+ } , {
+ .name = "cfg",
+ .offset = 0x7d0000,
+ .size = 0x010000,
+ .mask_flags = MTD_WRITEABLE,
+ } , {
+ .name = "FIS",
+ .offset = 0x7e0000,
+ .size = 0x010000,
+ .mask_flags = MTD_WRITEABLE,
+ }
+};
+#define wbd222_num_partitions ARRAY_SIZE(wbd222_partitions)
+#else
+#define wbd222_partitions NULL
+#define wbd222_num_partitions 0
+#endif /* CONFIG_MTD_PARTITIONS */
+
+static void __init wbd222_init(void)
+{
+ gemini_gpio_init();
+ platform_register_uart();
+ platform_register_pflash(SZ_8M, wbd222_partitions,
+ wbd222_num_partitions);
+ platform_device_register(&wbd222_leds_device);
+ platform_device_register(&wbd222_keys_device);
+}
+
+MACHINE_START(WBD222, "Wiliboard WBD-222")
+ .phys_io = 0x7fffc000,
+ .io_pg_offst = ((0xffffc000) >> 18) & 0xfffc,
+ .boot_params = 0x100,
+ .map_io = gemini_map_io,
+ .init_irq = gemini_init_irq,
+ .timer = &wbd222_timer,
+ .init_machine = wbd222_init,
+MACHINE_END
#define SERIAL_ENABLE_EN (1<<0)
/* General defines to pacify gcc */
-#define PCIO_BASE (0) /* for inb, outb and friends */
-#define PCIO_VIRT PCIO_BASE
#define __ASM_ARCH_HARDWARE_INCMACH_H
#include "boards.h"
mxc_gpio_mode(GPIO_PORTD | 27 | GPIO_GPIO | GPIO_IN);
spi_register_board_info(pca100_spi_board_info,
ARRAY_SIZE(pca100_spi_board_info));
- imx27_add_spi_imx0(&pca100_spi_0_data);
+ imx27_add_spi_imx0(&pca100_spi0_data);
#endif
gpio_request(OTG_PHY_CS_GPIO, "usb-otg-cs");
#define IO_SIZE 0x0B000000 // How much?
#define IO_START INTEGRATOR_HDR_BASE // PA of IO
-#define PCIO_BASE PCI_IO_VADDR
#define PCIMEM_BASE PCI_MEMORY_VADDR
#define pcibios_assign_all_busses() 1
#define __ASM_ARCH_HARDWARE_H
/* PCI IO info */
-#define PCIO_BASE IXP23XX_PCI_IO_VIRT
#define PCIBIOS_MIN_IO 0x00000000
#define PCIBIOS_MIN_MEM 0xe0000000
FSG-3 device. For more information on this platform,
see http://www.nslu2-linux.org/wiki/FSG3/HomePage
+config MACH_ARCOM_VULCAN
+ bool
+ prompt "Arcom/Eurotech Vulcan"
+ select PCI
+ help
+ Say 'Y' here if you want your kernel to support Arcom's
+ Vulcan board.
+
#
# Certain registers and IRQs are only enabled if supporting IXP465 CPUs
#
obj-pci-$(CONFIG_MACH_GATEWAY7001) += gateway7001-pci.o
obj-pci-$(CONFIG_MACH_WG302V2) += wg302v2-pci.o
obj-pci-$(CONFIG_MACH_FSG) += fsg-pci.o
+obj-pci-$(CONFIG_MACH_ARCOM_VULCAN) += vulcan-pci.o
obj-y += common.o
obj-$(CONFIG_MACH_WG302V2) += wg302v2-setup.o
obj-$(CONFIG_MACH_FSG) += fsg-setup.o
obj-$(CONFIG_MACH_GORAMO_MLR) += goramo_mlr.o
+obj-$(CONFIG_MACH_ARCOM_VULCAN) += vulcan-setup.o
obj-$(CONFIG_PCI) += $(obj-pci-$(CONFIG_PCI)) common-pci.o
obj-$(CONFIG_IXP4XX_QMGR) += ixp4xx_qmgr.o
moveq \rx, #0xc8000000
movne \rx, #0xff000000
orrne \rx, \rx, #0x00b00000
+#ifdef __ARMEB__
add \rx,\rx,#3 @ Uart regs are at off set of 3 if
@ byte writes used - Big Endian.
+#endif
.endm
#define UART_SHIFT 2
return (unsigned int)inb(port & PIO_MASK);
else
#ifndef CONFIG_IXP4XX_INDIRECT_PCI
- return (unsigned int)__raw_readb(port);
+ return (unsigned int)__raw_readb(addr);
#else
return (unsigned int)__indirect_readb(addr);
#endif
return (unsigned int)inw(port & PIO_MASK);
else
#ifndef CONFIG_IXP4XX_INDIRECT_PCI
- return le16_to_cpu(__raw_readw((u32)port));
+ return le16_to_cpu((__force __le16)__raw_readw(addr));
#else
return (unsigned int)__indirect_readw(addr);
#endif
outb(value, port & PIO_MASK);
else
#ifndef CONFIG_IXP4XX_INDIRECT_PCI
- __raw_writeb(value, port);
+ __raw_writeb(value, addr);
#else
__indirect_writeb(value, addr);
#endif
--- /dev/null
+/*
+ * arch/arch/mach-ixp4xx/vulcan-pci.c
+ *
+ * Vulcan board-level PCI initialization
+ *
+ * Copyright (C) 2010 Marc Zyngier <maz@misterjones.org>
+ *
+ * based on ixdp425-pci.c:
+ * Copyright (C) 2002 Intel Corporation.
+ * Copyright (C) 2003-2004 MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <asm/mach/pci.h>
+#include <asm/mach-types.h>
+
+/* PCI controller GPIO to IRQ pin mappings */
+#define INTA 2
+#define INTB 3
+
+void __init vulcan_pci_preinit(void)
+{
+#ifndef CONFIG_IXP4XX_INDIRECT_PCI
+ /*
+ * Cardbus bridge wants way more than the SoC can actually offer,
+ * and leaves the whole PCI bus in a mess. Artificially limit it
+ * to 8MB per region. Of course indirect mode doesn't have this
+ * limitation...
+ */
+ pci_cardbus_mem_size = SZ_8M;
+ pr_info("Vulcan PCI: limiting CardBus memory size to %dMB\n",
+ (int)(pci_cardbus_mem_size >> 20));
+#endif
+ set_irq_type(IXP4XX_GPIO_IRQ(INTA), IRQ_TYPE_LEVEL_LOW);
+ set_irq_type(IXP4XX_GPIO_IRQ(INTB), IRQ_TYPE_LEVEL_LOW);
+ ixp4xx_pci_preinit();
+}
+
+static int __init vulcan_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+ if (slot == 1)
+ return IXP4XX_GPIO_IRQ(INTA);
+
+ if (slot == 2)
+ return IXP4XX_GPIO_IRQ(INTB);
+
+ return -1;
+}
+
+struct hw_pci vulcan_pci __initdata = {
+ .nr_controllers = 1,
+ .preinit = vulcan_pci_preinit,
+ .swizzle = pci_std_swizzle,
+ .setup = ixp4xx_setup,
+ .scan = ixp4xx_scan_bus,
+ .map_irq = vulcan_map_irq,
+};
+
+int __init vulcan_pci_init(void)
+{
+ if (machine_is_arcom_vulcan())
+ pci_common_init(&vulcan_pci);
+ return 0;
+}
+
+subsys_initcall(vulcan_pci_init);
--- /dev/null
+/*
+ * arch/arm/mach-ixp4xx/vulcan-setup.c
+ *
+ * Arcom/Eurotech Vulcan board-setup
+ *
+ * Copyright (C) 2010 Marc Zyngier <maz@misterjones.org>
+ *
+ * based on fsg-setup.c:
+ * Copyright (C) 2008 Rod Whitby <rod@whitby.id.au>
+ */
+
+#include <linux/if_ether.h>
+#include <linux/irq.h>
+#include <linux/serial.h>
+#include <linux/serial_8250.h>
+#include <linux/io.h>
+#include <linux/w1-gpio.h>
+#include <linux/mtd/plat-ram.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/flash.h>
+
+static struct flash_platform_data vulcan_flash_data = {
+ .map_name = "cfi_probe",
+ .width = 2,
+};
+
+static struct resource vulcan_flash_resource = {
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device vulcan_flash = {
+ .name = "IXP4XX-Flash",
+ .id = 0,
+ .dev = {
+ .platform_data = &vulcan_flash_data,
+ },
+ .resource = &vulcan_flash_resource,
+ .num_resources = 1,
+};
+
+static struct platdata_mtd_ram vulcan_sram_data = {
+ .mapname = "Vulcan SRAM",
+ .bankwidth = 1,
+};
+
+static struct resource vulcan_sram_resource = {
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device vulcan_sram = {
+ .name = "mtd-ram",
+ .id = 0,
+ .dev = {
+ .platform_data = &vulcan_sram_data,
+ },
+ .resource = &vulcan_sram_resource,
+ .num_resources = 1,
+};
+
+static struct resource vulcan_uart_resources[] = {
+ [0] = {
+ .start = IXP4XX_UART1_BASE_PHYS,
+ .end = IXP4XX_UART1_BASE_PHYS + 0x0fff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IXP4XX_UART2_BASE_PHYS,
+ .end = IXP4XX_UART2_BASE_PHYS + 0x0fff,
+ .flags = IORESOURCE_MEM,
+ },
+ [2] = {
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct plat_serial8250_port vulcan_uart_data[] = {
+ [0] = {
+ .mapbase = IXP4XX_UART1_BASE_PHYS,
+ .membase = (char *)IXP4XX_UART1_BASE_VIRT + REG_OFFSET,
+ .irq = IRQ_IXP4XX_UART1,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
+ .iotype = UPIO_MEM,
+ .regshift = 2,
+ .uartclk = IXP4XX_UART_XTAL,
+ },
+ [1] = {
+ .mapbase = IXP4XX_UART2_BASE_PHYS,
+ .membase = (char *)IXP4XX_UART2_BASE_VIRT + REG_OFFSET,
+ .irq = IRQ_IXP4XX_UART2,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
+ .iotype = UPIO_MEM,
+ .regshift = 2,
+ .uartclk = IXP4XX_UART_XTAL,
+ },
+ [2] = {
+ .irq = IXP4XX_GPIO_IRQ(4),
+ .irqflags = IRQF_TRIGGER_LOW,
+ .flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
+ .iotype = UPIO_MEM,
+ .uartclk = 1843200,
+ },
+ [3] = {
+ .irq = IXP4XX_GPIO_IRQ(4),
+ .irqflags = IRQF_TRIGGER_LOW,
+ .flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
+ .iotype = UPIO_MEM,
+ .uartclk = 1843200,
+ },
+ { }
+};
+
+static struct platform_device vulcan_uart = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = vulcan_uart_data,
+ },
+ .resource = vulcan_uart_resources,
+ .num_resources = ARRAY_SIZE(vulcan_uart_resources),
+};
+
+static struct eth_plat_info vulcan_plat_eth[] = {
+ [0] = {
+ .phy = 0,
+ .rxq = 3,
+ .txreadyq = 20,
+ },
+ [1] = {
+ .phy = 1,
+ .rxq = 4,
+ .txreadyq = 21,
+ },
+};
+
+static struct platform_device vulcan_eth[] = {
+ [0] = {
+ .name = "ixp4xx_eth",
+ .id = IXP4XX_ETH_NPEB,
+ .dev = {
+ .platform_data = &vulcan_plat_eth[0],
+ },
+ },
+ [1] = {
+ .name = "ixp4xx_eth",
+ .id = IXP4XX_ETH_NPEC,
+ .dev = {
+ .platform_data = &vulcan_plat_eth[1],
+ },
+ },
+};
+
+static struct resource vulcan_max6369_resource = {
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device vulcan_max6369 = {
+ .name = "max6369_wdt",
+ .id = -1,
+ .resource = &vulcan_max6369_resource,
+ .num_resources = 1,
+};
+
+static struct w1_gpio_platform_data vulcan_w1_gpio_pdata = {
+ .pin = 14,
+};
+
+static struct platform_device vulcan_w1_gpio = {
+ .name = "w1-gpio",
+ .id = 0,
+ .dev = {
+ .platform_data = &vulcan_w1_gpio_pdata,
+ },
+};
+
+static struct platform_device *vulcan_devices[] __initdata = {
+ &vulcan_uart,
+ &vulcan_flash,
+ &vulcan_sram,
+ &vulcan_max6369,
+ &vulcan_eth[0],
+ &vulcan_eth[1],
+ &vulcan_w1_gpio,
+};
+
+static void __init vulcan_init(void)
+{
+ ixp4xx_sys_init();
+
+ /* Flash is spread over both CS0 and CS1 */
+ vulcan_flash_resource.start = IXP4XX_EXP_BUS_BASE(0);
+ vulcan_flash_resource.end = IXP4XX_EXP_BUS_BASE(0) + SZ_32M - 1;
+ *IXP4XX_EXP_CS0 = IXP4XX_EXP_BUS_CS_EN |
+ IXP4XX_EXP_BUS_STROBE_T(3) |
+ IXP4XX_EXP_BUS_SIZE(0xF) |
+ IXP4XX_EXP_BUS_BYTE_RD16 |
+ IXP4XX_EXP_BUS_WR_EN;
+ *IXP4XX_EXP_CS1 = *IXP4XX_EXP_CS0;
+
+ /* SRAM on CS2, (256kB, 8bit, writable) */
+ vulcan_sram_resource.start = IXP4XX_EXP_BUS_BASE(2);
+ vulcan_sram_resource.end = IXP4XX_EXP_BUS_BASE(2) + SZ_256K - 1;
+ *IXP4XX_EXP_CS2 = IXP4XX_EXP_BUS_CS_EN |
+ IXP4XX_EXP_BUS_STROBE_T(1) |
+ IXP4XX_EXP_BUS_HOLD_T(2) |
+ IXP4XX_EXP_BUS_SIZE(9) |
+ IXP4XX_EXP_BUS_SPLT_EN |
+ IXP4XX_EXP_BUS_WR_EN |
+ IXP4XX_EXP_BUS_BYTE_EN;
+
+ /* XR16L2551 on CS3 (Moto style, 512 bytes, 8bits, writable) */
+ vulcan_uart_resources[2].start = IXP4XX_EXP_BUS_BASE(3);
+ vulcan_uart_resources[2].end = IXP4XX_EXP_BUS_BASE(3) + 16 - 1;
+ vulcan_uart_data[2].mapbase = vulcan_uart_resources[2].start;
+ vulcan_uart_data[3].mapbase = vulcan_uart_data[2].mapbase + 8;
+ *IXP4XX_EXP_CS3 = IXP4XX_EXP_BUS_CS_EN |
+ IXP4XX_EXP_BUS_STROBE_T(3) |
+ IXP4XX_EXP_BUS_CYCLES(IXP4XX_EXP_BUS_CYCLES_MOTOROLA)|
+ IXP4XX_EXP_BUS_WR_EN |
+ IXP4XX_EXP_BUS_BYTE_EN;
+
+ /* GPIOS on CS4 (512 bytes, 8bits, writable) */
+ *IXP4XX_EXP_CS4 = IXP4XX_EXP_BUS_CS_EN |
+ IXP4XX_EXP_BUS_WR_EN |
+ IXP4XX_EXP_BUS_BYTE_EN;
+
+ /* max6369 on CS5 (512 bytes, 8bits, writable) */
+ vulcan_max6369_resource.start = IXP4XX_EXP_BUS_BASE(5);
+ vulcan_max6369_resource.end = IXP4XX_EXP_BUS_BASE(5);
+ *IXP4XX_EXP_CS5 = IXP4XX_EXP_BUS_CS_EN |
+ IXP4XX_EXP_BUS_WR_EN |
+ IXP4XX_EXP_BUS_BYTE_EN;
+
+ platform_add_devices(vulcan_devices, ARRAY_SIZE(vulcan_devices));
+}
+
+MACHINE_START(ARCOM_VULCAN, "Arcom/Eurotech Vulcan")
+ /* Maintainer: Marc Zyngier <maz@misterjones.org> */
+ .phys_io = IXP4XX_PERIPHERAL_BASE_PHYS,
+ .io_pg_offst = ((IXP4XX_PERIPHERAL_BASE_VIRT) >> 18) & 0xfffc,
+ .map_io = ixp4xx_map_io,
+ .init_irq = ixp4xx_init_irq,
+ .timer = &ixp4xx_timer,
+ .boot_params = 0x0100,
+ .init_machine = vulcan_init,
+MACHINE_END
MACHINE_START(ASPENITE, "PXA168-based Aspenite Development Platform")
.phys_io = APB_PHYS_BASE,
- .boot_params = 0x00000100,
.io_pg_offst = (APB_VIRT_BASE >> 18) & 0xfffc,
- .map_io = pxa_map_io,
+ .map_io = mmp_map_io,
.init_irq = pxa168_init_irq,
.timer = &pxa168_timer,
.init_machine = common_init,
MACHINE_START(ZYLONITE2, "PXA168-based Zylonite2 Development Platform")
.phys_io = APB_PHYS_BASE,
- .boot_params = 0x00000100,
.io_pg_offst = (APB_VIRT_BASE >> 18) & 0xfffc,
- .map_io = pxa_map_io,
+ .map_io = mmp_map_io,
.init_irq = pxa168_init_irq,
.timer = &pxa168_timer,
.init_machine = common_init,
MACHINE_START(AVENGERS_LITE, "PXA168 Avengers lite Development Platform")
.phys_io = APB_PHYS_BASE,
- .boot_params = 0x00000100,
.io_pg_offst = (APB_VIRT_BASE >> 18) & 0xfffc,
- .map_io = pxa_map_io,
+ .map_io = mmp_map_io,
.init_irq = pxa168_init_irq,
.timer = &pxa168_timer,
.init_machine = avengers_lite_init,
},
};
-void __init pxa_map_io(void)
+void __init mmp_map_io(void)
{
iotable_init(standard_io_desc, ARRAY_SIZE(standard_io_desc));
}
struct sys_timer;
extern void timer_init(int irq);
-extern void mmp2_clear_pmic_int(void);
-
-extern struct sys_timer pxa168_timer;
-extern struct sys_timer pxa910_timer;
-extern struct sys_timer mmp2_timer;
-extern void __init pxa168_init_irq(void);
-extern void __init pxa910_init_irq(void);
-extern void __init mmp2_init_icu(void);
-extern void __init mmp2_init_irq(void);
extern void __init icu_init_irq(void);
-extern void __init pxa_map_io(void);
+extern void __init mmp_map_io(void);
MACHINE_START(FLINT, "Flint Development Platform")
.phys_io = APB_PHYS_BASE,
- .boot_params = 0x00000100,
.io_pg_offst = (APB_VIRT_BASE >> 18) & 0xfffc,
- .map_io = pxa_map_io,
+ .map_io = mmp_map_io,
.init_irq = mmp2_init_irq,
.timer = &mmp2_timer,
.init_machine = flint_init,
+#ifndef __MACH_DEVICE_H
+#define __MACH_DEVICE_H
+
#include <linux/types.h>
#define MAX_RESOURCE_DMA 2
}
extern int pxa_register_device(struct pxa_device_desc *, void *, size_t);
+#endif /* __MACH_DEVICE_H */
#ifndef __ASM_MACH_MMP2_H
#define __ASM_MACH_MMP2_H
+struct sys_timer;
+
+extern struct sys_timer mmp2_timer;
+extern void __init mmp2_init_icu(void);
+extern void __init mmp2_init_irq(void);
+extern void mmp2_clear_pmic_int(void);
+
#include <linux/i2c.h>
#include <mach/devices.h>
#include <plat/i2c.h>
#ifndef __ASM_MACH_PXA168_H
#define __ASM_MACH_PXA168_H
+struct sys_timer;
+
+extern struct sys_timer pxa168_timer;
+extern void __init pxa168_init_irq(void);
+
#include <linux/i2c.h>
#include <mach/devices.h>
#include <plat/i2c.h>
#ifndef __ASM_MACH_PXA910_H
#define __ASM_MACH_PXA910_H
+struct sys_timer;
+
+extern struct sys_timer pxa910_timer;
+extern void __init pxa910_init_irq(void);
+
#include <linux/i2c.h>
#include <mach/devices.h>
#include <plat/i2c.h>
#include <linux/io.h>
#include <mach/regs-icu.h>
+#include <mach/mmp2.h>
#include "common.h"
MACHINE_START(MARVELL_JASPER, "Jasper Development Platform")
.phys_io = APB_PHYS_BASE,
- .boot_params = 0x00000100,
.io_pg_offst = (APB_VIRT_BASE >> 18) & 0xfffc,
- .map_io = pxa_map_io,
+ .map_io = mmp_map_io,
.init_irq = mmp2_init_irq,
.timer = &mmp2_timer,
.init_machine = jasper_init,
#include <asm/hardware/cache-tauros2.h>
+#include <asm/mach/time.h>
#include <mach/addr-map.h>
#include <mach/regs-apbc.h>
#include <mach/regs-apmu.h>
#include <mach/mfp.h>
#include <mach/gpio.h>
#include <mach/devices.h>
+#include <mach/mmp2.h>
#include "common.h"
#include "clock.h"
}
postcore_initcall(mmp2_init);
+static void __init mmp2_timer_init(void)
+{
+ unsigned long clk_rst;
+
+ __raw_writel(APBC_APBCLK | APBC_RST, APBC_MMP2_TIMERS);
+
+ /*
+ * enable bus/functional clock, enable 6.5MHz (divider 4),
+ * release reset
+ */
+ clk_rst = APBC_APBCLK | APBC_FNCLK | APBC_FNCLKSEL(1);
+ __raw_writel(clk_rst, APBC_MMP2_TIMERS);
+
+ timer_init(IRQ_MMP2_TIMER1);
+}
+
+struct sys_timer mmp2_timer = {
+ .init = mmp2_timer_init,
+};
+
/* on-chip devices */
MMP2_DEVICE(uart1, "pxa2xx-uart", 0, UART1, 0xd4030000, 0x30, 4, 5);
MMP2_DEVICE(uart2, "pxa2xx-uart", 1, UART2, 0xd4017000, 0x30, 20, 21);
MACHINE_START(TAVOREVB, "PXA910 Evaluation Board (aka TavorEVB)")
.phys_io = APB_PHYS_BASE,
- .boot_params = 0x00000100,
.io_pg_offst = (APB_VIRT_BASE >> 18) & 0xfffc,
- .map_io = pxa_map_io,
+ .map_io = mmp_map_io,
.init_irq = pxa910_init_irq,
.timer = &pxa910_timer,
.init_machine = tavorevb_init,
clocksource_register(&cksrc);
clockevents_register_device(&ckevt);
}
-
-static void __init mmp2_timer_init(void)
-{
- unsigned long clk_rst;
-
- __raw_writel(APBC_APBCLK | APBC_RST, APBC_MMP2_TIMERS);
-
- /*
- * enable bus/functional clock, enable 6.5MHz (divider 4),
- * release reset
- */
- clk_rst = APBC_APBCLK | APBC_FNCLK | APBC_FNCLKSEL(1);
- __raw_writel(clk_rst, APBC_MMP2_TIMERS);
-
- timer_init(IRQ_MMP2_TIMER1);
-}
-
-struct sys_timer mmp2_timer = {
- .init = mmp2_timer_init,
-};
-
MACHINE_START(TTC_DKB, "PXA910-based TTC_DKB Development Platform")
.phys_io = APB_PHYS_BASE,
- .boot_params = 0x00000100,
.io_pg_offst = (APB_VIRT_BASE >> 18) & 0xfffc,
- .map_io = pxa_map_io,
+ .map_io = mmp_map_io,
.init_irq = pxa910_init_irq,
.timer = &pxa910_timer,
.init_machine = ttc_dkb_init,
/* USB */
#if defined(CONFIG_USB_ULPI)
usbotg_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops,
- USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT);
+ ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops,
- USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT);
+ ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
mxc_register_device(&mxc_otg_host, &usbotg_pdata);
mxc_register_device(&mxc_usbh2, &usbh2_pdata);
static void lilly1131_usb_init(void)
{
usbotg_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops,
- USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT);
+ ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops,
- USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT);
+ ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
mxc_register_device(&mxc_usbh1, &usbh1_pdata);
mxc_register_device(&mxc_usbh2, &usbh2_pdata);
#if defined(CONFIG_USB_ULPI)
/* USB */
usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops,
- USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT);
+ ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
mxc_register_device(&mxc_usbh2, &usbh2_pdata);
#endif
static int __init moboard_usbh2_init(void)
{
usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops,
- USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT);
+ ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
return mxc_register_device(&mxc_usbh2, &usbh2_pdata);
}
#if defined(CONFIG_USB_ULPI)
if (otg_mode_host) {
otg_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops,
- USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT);
+ ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
mxc_register_device(&mxc_otg_host, &otg_pdata);
}
usbh2_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops,
- USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT);
+ ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
mxc_register_device(&mxc_usbh2, &usbh2_pdata);
#endif
#if defined(CONFIG_USB_ULPI)
if (otg_mode_host) {
otg_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops,
- USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT);
+ ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
mxc_register_device(&mxc_otg_host, &otg_pdata);
}
static int __init smartbot_otg_host_init(void)
{
otg_host_pdata.otg = otg_ulpi_create(&mxc_ulpi_access_ops,
- USB_OTG_DRV_VBUS | USB_OTG_DRV_VBUS_EXT);
+ ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT);
return mxc_register_device(&mxc_otg_host, &otg_host_pdata);
}
#include <linux/input.h>
#include <linux/input/matrix_keypad.h>
#include <linux/gpio_keys.h>
+#include <linux/mmc/card.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
},
};
+static void pandora_wl1251_init_card(struct mmc_card *card)
+{
+ /*
+ * We have TI wl1251 attached to MMC3. Pass this information to
+ * SDIO core because it can't be probed by normal methods.
+ */
+ card->quirks |= MMC_QUIRK_NONSTD_SDIO;
+ card->cccr.wide_bus = 1;
+ card->cis.vendor = 0x104c;
+ card->cis.device = 0x9066;
+ card->cis.blksize = 512;
+ card->cis.max_dtr = 20000000;
+}
+
static struct omap2_hsmmc_info omap3pandora_mmc[] = {
{
.mmc = 1,
.wires = 4,
.gpio_cd = -EINVAL,
.gpio_wp = -EINVAL,
+ .init_card = pandora_wl1251_init_card,
},
{} /* Terminator */
};
mmc->slots[0].gpio_wp = c->gpio_wp;
mmc->slots[0].remux = c->remux;
+ mmc->slots[0].init_card = c->init_card;
if (c->cover_only)
mmc->slots[0].cover = 1;
* published by the Free Software Foundation.
*/
+struct mmc_card;
+
struct omap2_hsmmc_info {
u8 mmc; /* controller 1/2/3 */
u8 wires; /* 1/4/8 wires */
int ocr_mask; /* temporary HACK */
/* Remux (pad configuation) when powering on/off */
void (*remux)(struct device *dev, int slot, int power_on);
+ /* init some special card */
+ void (*init_card)(struct mmc_card *card);
};
#if defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE)
bool "Toradex Colibri PXA270"
select PXA27x
+config MACH_COLIBRI_PXA270_EVALBOARD
+ bool "Toradex Colibri Evaluation Carrier Board support (PXA270)"
+ depends on MACH_COLIBRI
+
+config MACH_COLIBRI_PXA270_INCOME
+ bool "Income s.r.o. PXA270 SBC"
+ depends on MACH_COLIBRI
+ select PXA27x
+ select HAVE_PWM
+ select PXA_HAVE_BOARD_IRQS
+
config MACH_COLIBRI300
bool "Toradex Colibri PXA300/310"
select PXA3xx
bool "PXA based Palm PDAs"
select HAVE_PWM
+config MACH_PALM27X
+ bool
+
config MACH_PALMTE2
bool "Palm Tungsten|E2"
default y
depends on ARCH_PXA_PALM
select PXA27x
select IWMMXT
+ select MACH_PALM27X
help
Say Y here if you intend to run this kernel on a Palm Tungsten|T5
handheld computer.
depends on ARCH_PXA_PALM
select PXA27x
select IWMMXT
+ select MACH_PALM27X
help
Say Y here if you intend to run this kernel on a Palm T|X
handheld computer.
depends on ARCH_PXA_PALM
select PXA27x
select IWMMXT
+ select MACH_PALM27X
help
Say Y here if you intend to run this kernel on Palm Zire 72
handheld computer.
depends on ARCH_PXA_PALM
select PXA27x
select IWMMXT
+ select MACH_PALM27X
help
Say Y here if you intend to run this kernel on a Palm LifeDrive
handheld computer.
SL-C3000 (Spitz), SL-C3100 (Borzoi) or SL-C6000x (Tosa)
handheld computer.
-config SHARPSL_PM
- bool
- select APM_EMULATION
- select SHARPSL_PM_MAX1111
-
-config SHARPSL_PM_MAX1111
- bool
- depends on !CORGI_SSP_DEPRECATED
- select HWMON
- select SENSORS_MAX1111
+config PXA_SHARPSL_DETECT_MACH_ID
+ bool "Detect machine ID at run-time in the decompressor"
+ depends on PXA_SHARPSL
+ help
+ Say Y here if you want the zImage decompressor to detect
+ the Zaurus machine ID at run-time. For latest kexec-based
+ boot loader, this is not necessary.
config MACH_POODLE
bool "Enable Sharp SL-5600 (Poodle) Support"
select PXA25x
select PXA_HAVE_BOARD_IRQS
+config TOSA_BT
+ tristate "Control the state of built-in bluetooth chip on Sharp SL-6000"
+ depends on MACH_TOSA
+ select RFKILL
+ help
+ This is a simple driver that is able to control
+ the state of built in bluetooth chip on tosa.
+
+config TOSA_USE_EXT_KEYCODES
+ bool "Tosa keyboard: use extended keycodes"
+ depends on MACH_TOSA
+ default n
+ help
+ Say Y here to enable the tosa keyboard driver to generate extended
+ (>= 127) keycodes. Be aware, that they can't be correctly interpreted
+ by either console keyboard driver or by Kdrive keybd driver.
+
+ Say Y only if you know, what you are doing!
+
config MACH_ICONTROL
bool "TMT iControl/SafeTCam based on the MXM-8x10 CoM"
select CPU_PXA320
help
Enable common support for Sharp Cxx00 models
-config TOSA_BT
- tristate "Control the state of built-in bluetooth chip on Sharp SL-6000"
- depends on MACH_TOSA
- select RFKILL
- help
- This is a simple driver that is able to control
- the state of built in bluetooth chip on tosa.
-
-config TOSA_USE_EXT_KEYCODES
- bool "Tosa keyboard: use extended keycodes"
- depends on MACH_TOSA
- default n
- help
- Say Y here to enable the tosa keyboard driver to generate extended
- (>= 127) keycodes. Be aware, that they can't be correctly interpreted
- by either console keyboard driver or by Kdrive keybd driver.
-
- Say Y only if you know, what you are doing!
+config SHARPSL_PM
+ bool
+ select APM_EMULATION
+ select SHARPSL_PM_MAX1111
+config SHARPSL_PM_MAX1111
+ bool
+ select HWMON
+ select SENSORS_MAX1111
config PXA_HAVE_BOARD_IRQS
bool
obj-$(CONFIG_ARCH_GUMSTIX) += gumstix.o
obj-$(CONFIG_GUMSTIX_AM200EPD) += am200epd.o
obj-$(CONFIG_GUMSTIX_AM300EPD) += am300epd.o
-obj-$(CONFIG_MACH_INTELMOTE2) += imote2.o
+obj-$(CONFIG_MACH_INTELMOTE2) += stargate2.o
obj-$(CONFIG_MACH_STARGATE2) += stargate2.o
obj-$(CONFIG_MACH_XCEP) += xcep.o
obj-$(CONFIG_MACH_TRIZEPS4) += trizeps4.o
obj-$(CONFIG_MACH_LOGICPD_PXA270) += lpd270.o
obj-$(CONFIG_MACH_PCM027) += pcm027.o
obj-$(CONFIG_MACH_PCM990_BASEBOARD) += pcm990-baseboard.o
-obj-$(CONFIG_MACH_COLIBRI) += colibri-pxa270.o
+obj-$(CONFIG_MACH_COLIBRI) += colibri-pxa270.o
+obj-$(CONFIG_MACH_COLIBRI_PXA270_EVALBOARD) += colibri-pxa270-evalboard.o
+obj-$(CONFIG_MACH_COLIBRI_PXA270_INCOME) += colibri-pxa270-income.o
obj-$(CONFIG_MACH_COLIBRI300) += colibri-pxa3xx.o colibri-pxa300.o
obj-$(CONFIG_MACH_COLIBRI320) += colibri-pxa3xx.o colibri-pxa320.o
obj-$(CONFIG_MACH_VPAC270) += vpac270.o
obj-$(CONFIG_MACH_MP900C) += mp900.o
obj-$(CONFIG_MACH_PALMTE2) += palmte2.o
obj-$(CONFIG_MACH_PALMTC) += palmtc.o
+obj-$(CONFIG_MACH_PALM27X) += palm27x.o
obj-$(CONFIG_MACH_PALMT5) += palmt5.o
obj-$(CONFIG_MACH_PALMTX) += palmtx.o
obj-$(CONFIG_MACH_PALMZ72) += palmz72.o
obj-$(CONFIG_MACH_TOSA) += tosa.o
obj-$(CONFIG_MACH_ICONTROL) += icontrol.o mxm8x10.o
obj-$(CONFIG_ARCH_PXA_ESERIES) += eseries.o
-obj-$(CONFIG_MACH_E330) += e330.o
-obj-$(CONFIG_MACH_E350) += e350.o
-obj-$(CONFIG_MACH_E740) += e740.o
-obj-$(CONFIG_MACH_E750) += e750.o
-obj-$(CONFIG_MACH_E400) += e400.o
-obj-$(CONFIG_MACH_E800) += e800.o
obj-$(CONFIG_MACH_RAUMFELD_RC) += raumfeld.o
obj-$(CONFIG_MACH_RAUMFELD_CONNECTOR) += raumfeld.o
obj-$(CONFIG_MACH_RAUMFELD_SPEAKER) += raumfeld.o
#include <linux/fb.h>
#include <linux/gpio.h>
#include <linux/ioport.h>
+#include <linux/ucb1400.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/types.h>
+#include <linux/i2c/pcf857x.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/physmap.h>
+#include <linux/regulator/max1586.h>
#include <asm/setup.h>
#include <asm/mach-types.h>
#include "generic.h"
#include "devices.h"
+/******************************************************************************
+ * Pin configuration
+ ******************************************************************************/
+static unsigned long balloon3_pin_config[] __initdata = {
+ /* Select BTUART 'COM1/ttyS0' as IO option for pins 42/43/44/45 */
+ GPIO42_BTUART_RXD,
+ GPIO43_BTUART_TXD,
+ GPIO44_BTUART_CTS,
+ GPIO45_BTUART_RTS,
+
+ /* Reset, configured as GPIO wakeup source */
+ GPIO1_GPIO | WAKEUP_ON_EDGE_BOTH,
+
+ /* LEDs */
+ GPIO9_GPIO, /* NAND activity LED */
+ GPIO10_GPIO, /* Heartbeat LED */
+
+ /* AC97 */
+ GPIO28_AC97_BITCLK,
+ GPIO29_AC97_SDATA_IN_0,
+ GPIO30_AC97_SDATA_OUT,
+ GPIO31_AC97_SYNC,
+ GPIO113_AC97_nRESET,
+ GPIO95_GPIO,
+
+ /* MMC */
+ GPIO32_MMC_CLK,
+ GPIO92_MMC_DAT_0,
+ GPIO109_MMC_DAT_1,
+ GPIO110_MMC_DAT_2,
+ GPIO111_MMC_DAT_3,
+ GPIO112_MMC_CMD,
+
+ /* USB Host */
+ GPIO88_USBH1_PWR,
+ GPIO89_USBH1_PEN,
+
+ /* PC Card */
+ GPIO48_nPOE,
+ GPIO49_nPWE,
+ GPIO50_nPIOR,
+ GPIO51_nPIOW,
+ GPIO85_nPCE_1,
+ GPIO54_nPCE_2,
+ GPIO79_PSKTSEL,
+ GPIO55_nPREG,
+ GPIO56_nPWAIT,
+ GPIO57_nIOIS16,
+};
+
+/******************************************************************************
+ * Compatibility: Parameter parsing
+ ******************************************************************************/
static unsigned long balloon3_irq_enabled;
static unsigned long balloon3_features_present =
}
early_param("balloon3_features", parse_balloon3_features);
+/******************************************************************************
+ * NOR Flash
+ ******************************************************************************/
+#if defined(CONFIG_MTD_PHYSMAP) || defined(CONFIG_MTD_PHYSMAP_MODULE)
+static struct mtd_partition balloon3_nor_partitions[] = {
+ {
+ .name = "Flash",
+ .offset = 0x00000000,
+ .size = MTDPART_SIZ_FULL,
+ }
+};
+
+static struct physmap_flash_data balloon3_flash_data[] = {
+ {
+ .width = 2, /* bankwidth in bytes */
+ .parts = balloon3_nor_partitions,
+ .nr_parts = ARRAY_SIZE(balloon3_nor_partitions)
+ }
+};
+
+static struct resource balloon3_flash_resource = {
+ .start = PXA_CS0_PHYS,
+ .end = PXA_CS0_PHYS + SZ_64M - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device balloon3_flash = {
+ .name = "physmap-flash",
+ .id = 0,
+ .resource = &balloon3_flash_resource,
+ .num_resources = 1,
+ .dev = {
+ .platform_data = balloon3_flash_data,
+ },
+};
+static void __init balloon3_nor_init(void)
+{
+ platform_device_register(&balloon3_flash);
+}
+#else
+static inline void balloon3_nor_init(void) {}
+#endif
+
+/******************************************************************************
+ * Audio and Touchscreen
+ ******************************************************************************/
+#if defined(CONFIG_TOUCHSCREEN_UCB1400) || \
+ defined(CONFIG_TOUCHSCREEN_UCB1400_MODULE)
+static struct ucb1400_pdata vpac270_ucb1400_pdata = {
+ .irq = IRQ_GPIO(BALLOON3_GPIO_CODEC_IRQ),
+};
+
+
+static struct platform_device balloon3_ucb1400_device = {
+ .name = "ucb1400_core",
+ .id = -1,
+ .dev = {
+ .platform_data = &vpac270_ucb1400_pdata,
+ },
+};
+
+static void __init balloon3_ts_init(void)
+{
+ if (!balloon3_has(BALLOON3_FEATURE_AUDIO))
+ return;
+
+ pxa_set_ac97_info(NULL);
+ platform_device_register(&balloon3_ucb1400_device);
+}
+#else
+static inline void balloon3_ts_init(void) {}
+#endif
+
+/******************************************************************************
+ * Framebuffer
+ ******************************************************************************/
+#if defined(CONFIG_FB_PXA) || defined(CONFIG_FB_PXA_MODULE)
+static struct pxafb_mode_info balloon3_lcd_modes[] = {
+ {
+ .pixclock = 38000,
+ .xres = 480,
+ .yres = 640,
+ .bpp = 16,
+ .hsync_len = 8,
+ .left_margin = 8,
+ .right_margin = 8,
+ .vsync_len = 2,
+ .upper_margin = 4,
+ .lower_margin = 5,
+ .sync = 0,
+ },
+};
+
+static struct pxafb_mach_info balloon3_lcd_screen = {
+ .modes = balloon3_lcd_modes,
+ .num_modes = ARRAY_SIZE(balloon3_lcd_modes),
+ .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL,
+};
+
+static void balloon3_backlight_power(int on)
+{
+ gpio_set_value(BALLOON3_GPIO_RUN_BACKLIGHT, on);
+}
+
+static void __init balloon3_lcd_init(void)
+{
+ int ret;
+
+ if (!balloon3_has(BALLOON3_FEATURE_TOPPOLY))
+ return;
+
+ ret = gpio_request(BALLOON3_GPIO_RUN_BACKLIGHT, "BKL-ON");
+ if (ret) {
+ pr_err("Requesting BKL-ON GPIO failed!\n");
+ goto err;
+ }
+
+ ret = gpio_direction_output(BALLOON3_GPIO_RUN_BACKLIGHT, 1);
+ if (ret) {
+ pr_err("Setting BKL-ON GPIO direction failed!\n");
+ goto err2;
+ }
+
+ balloon3_lcd_screen.pxafb_backlight_power = balloon3_backlight_power;
+ set_pxa_fb_info(&balloon3_lcd_screen);
+ return;
+
+err2:
+ gpio_free(BALLOON3_GPIO_RUN_BACKLIGHT);
+err:
+ return;
+}
+#else
+static inline void balloon3_lcd_init(void) {}
+#endif
+
+/******************************************************************************
+ * SD/MMC card controller
+ ******************************************************************************/
+#if defined(CONFIG_MMC_PXA) || defined(CONFIG_MMC_PXA_MODULE)
+static struct pxamci_platform_data balloon3_mci_platform_data = {
+ .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34,
+ .gpio_card_detect = -1,
+ .gpio_card_ro = -1,
+ .gpio_power = -1,
+ .detect_delay_ms = 200,
+};
+
+static void __init balloon3_mmc_init(void)
+{
+ pxa_set_mci_info(&balloon3_mci_platform_data);
+}
+#else
+static inline void balloon3_mmc_init(void) {}
+#endif
+
+/******************************************************************************
+ * USB Gadget
+ ******************************************************************************/
+#if defined(CONFIG_USB_GADGET_PXA27X)||defined(CONFIG_USB_GADGET_PXA27X_MODULE)
+static void balloon3_udc_command(int cmd)
+{
+ if (cmd == PXA2XX_UDC_CMD_CONNECT)
+ UP2OCR |= UP2OCR_DPPUE | UP2OCR_DPPUBE;
+ else if (cmd == PXA2XX_UDC_CMD_DISCONNECT)
+ UP2OCR &= ~UP2OCR_DPPUE;
+}
+
+static int balloon3_udc_is_connected(void)
+{
+ return 1;
+}
+
+static struct pxa2xx_udc_mach_info balloon3_udc_info __initdata = {
+ .udc_command = balloon3_udc_command,
+ .udc_is_connected = balloon3_udc_is_connected,
+ .gpio_pullup = -1,
+};
+
+static void __init balloon3_udc_init(void)
+{
+ pxa_set_udc_info(&balloon3_udc_info);
+ platform_device_register(&balloon3_gpio_vbus);
+}
+#else
+static inline void balloon3_udc_init(void) {}
+#endif
+
+/******************************************************************************
+ * IrDA
+ ******************************************************************************/
+#if defined(CONFIG_IRDA) || defined(CONFIG_IRDA_MODULE)
+static struct pxaficp_platform_data balloon3_ficp_platform_data = {
+ .transceiver_cap = IR_FIRMODE | IR_SIRMODE | IR_OFF,
+};
+
+static void __init balloon3_irda_init(void)
+{
+ pxa_set_ficp_info(&balloon3_ficp_platform_data);
+}
+#else
+static inline void balloon3_irda_init(void) {}
+#endif
+
+/******************************************************************************
+ * USB Host
+ ******************************************************************************/
+#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
+static struct pxaohci_platform_data balloon3_ohci_info = {
+ .port_mode = PMM_PERPORT_MODE,
+ .flags = ENABLE_PORT_ALL | POWER_CONTROL_LOW | POWER_SENSE_LOW,
+};
+
+static void __init balloon3_uhc_init(void)
+{
+ if (!balloon3_has(BALLOON3_FEATURE_OHCI))
+ return;
+ pxa_set_ohci_info(&balloon3_ohci_info);
+}
+#else
+static inline void balloon3_uhc_init(void) {}
+#endif
+
+/******************************************************************************
+ * LEDs
+ ******************************************************************************/
+#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE)
+struct gpio_led balloon3_gpio_leds[] = {
+ {
+ .name = "balloon3:green:idle",
+ .default_trigger = "heartbeat",
+ .gpio = BALLOON3_GPIO_LED_IDLE,
+ .active_low = 1,
+ }, {
+ .name = "balloon3:green:nand",
+ .default_trigger = "nand-disk",
+ .gpio = BALLOON3_GPIO_LED_NAND,
+ .active_low = 1,
+ },
+};
+
+static struct gpio_led_platform_data balloon3_gpio_led_info = {
+ .leds = balloon3_gpio_leds,
+ .num_leds = ARRAY_SIZE(balloon3_gpio_leds),
+};
+
+static struct platform_device balloon3_leds = {
+ .name = "leds-gpio",
+ .id = 0,
+ .dev = {
+ .platform_data = &balloon3_gpio_led_info,
+ }
+};
+
+struct gpio_led balloon3_pcf_gpio_leds[] = {
+ {
+ .name = "balloon3:green:led0",
+ .gpio = BALLOON3_PCF_GPIO_LED0,
+ .active_low = 1,
+ }, {
+ .name = "balloon3:green:led1",
+ .gpio = BALLOON3_PCF_GPIO_LED1,
+ .active_low = 1,
+ }, {
+ .name = "balloon3:orange:led2",
+ .gpio = BALLOON3_PCF_GPIO_LED2,
+ .active_low = 1,
+ }, {
+ .name = "balloon3:orange:led3",
+ .gpio = BALLOON3_PCF_GPIO_LED3,
+ .active_low = 1,
+ }, {
+ .name = "balloon3:orange:led4",
+ .gpio = BALLOON3_PCF_GPIO_LED4,
+ .active_low = 1,
+ }, {
+ .name = "balloon3:orange:led5",
+ .gpio = BALLOON3_PCF_GPIO_LED5,
+ .active_low = 1,
+ }, {
+ .name = "balloon3:red:led6",
+ .gpio = BALLOON3_PCF_GPIO_LED6,
+ .active_low = 1,
+ }, {
+ .name = "balloon3:red:led7",
+ .gpio = BALLOON3_PCF_GPIO_LED7,
+ .active_low = 1,
+ },
+};
+
+static struct gpio_led_platform_data balloon3_pcf_gpio_led_info = {
+ .leds = balloon3_pcf_gpio_leds,
+ .num_leds = ARRAY_SIZE(balloon3_pcf_gpio_leds),
+};
+
+static struct platform_device balloon3_pcf_leds = {
+ .name = "leds-gpio",
+ .id = 1,
+ .dev = {
+ .platform_data = &balloon3_pcf_gpio_led_info,
+ }
+};
+
+static void __init balloon3_leds_init(void)
+{
+ platform_device_register(&balloon3_leds);
+ platform_device_register(&balloon3_pcf_leds);
+}
+#else
+static inline void balloon3_leds_init(void) {}
+#endif
+
+/******************************************************************************
+ * FPGA IRQ
+ ******************************************************************************/
static void balloon3_mask_irq(unsigned int irq)
{
int balloon3_irq = (irq - BALLOON3_IRQ(0));
{
unsigned long pending = __raw_readl(BALLOON3_INT_CONTROL_REG) &
balloon3_irq_enabled;
-
do {
/* clear useless edge notification */
if (desc->chip->ack)
"enabled\n", __func__, BALLOON3_AUX_NIRQ);
}
-static unsigned long balloon3_ac97_pin_config[] = {
- GPIO28_AC97_BITCLK,
- GPIO29_AC97_SDATA_IN_0,
- GPIO30_AC97_SDATA_OUT,
- GPIO31_AC97_SYNC,
- GPIO113_AC97_nRESET,
-};
-
-static void balloon3_backlight_power(int on)
-{
- pr_debug("%s: power is %s\n", __func__, on ? "on" : "off");
- gpio_set_value(BALLOON3_GPIO_RUN_BACKLIGHT, on);
-}
-
-static unsigned long balloon3_lcd_pin_config[] = {
- /* LCD - 16bpp Active TFT */
- GPIOxx_LCD_TFT_16BPP,
-
- GPIO99_GPIO, /* Backlight */
+/******************************************************************************
+ * GPIO expander
+ ******************************************************************************/
+#if defined(CONFIG_GPIO_PCF857X) || defined(CONFIG_GPIO_PCF857X_MODULE)
+static struct pcf857x_platform_data balloon3_pcf857x_pdata = {
+ .gpio_base = BALLOON3_PCF_GPIO_BASE,
+ .n_latch = 0,
+ .setup = NULL,
+ .teardown = NULL,
+ .context = NULL,
};
-static struct pxafb_mode_info balloon3_lcd_modes[] = {
+static struct i2c_board_info __initdata balloon3_i2c_devs[] = {
{
- .pixclock = 38000,
- .xres = 480,
- .yres = 640,
- .bpp = 16,
- .hsync_len = 8,
- .left_margin = 8,
- .right_margin = 8,
- .vsync_len = 2,
- .upper_margin = 4,
- .lower_margin = 5,
- .sync = 0,
+ I2C_BOARD_INFO("pcf8574a", 0x38),
+ .platform_data = &balloon3_pcf857x_pdata,
},
};
-static struct pxafb_mach_info balloon3_pxafb_info = {
- .modes = balloon3_lcd_modes,
- .num_modes = ARRAY_SIZE(balloon3_lcd_modes),
- .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL,
- .pxafb_backlight_power = balloon3_backlight_power,
-};
+static void __init balloon3_i2c_init(void)
+{
+ pxa_set_i2c_info(NULL);
+ i2c_register_board_info(0, ARRAY_AND_SIZE(balloon3_i2c_devs));
+}
+#else
+static inline void balloon3_i2c_init(void) {}
+#endif
+
+/******************************************************************************
+ * NAND
+ ******************************************************************************/
+#if defined(CONFIG_MTD_NAND_PLATFORM)||defined(CONFIG_MTD_NAND_PLATFORM_MODULE)
+static uint16_t balloon3_ctl =
+ BALLOON3_NAND_CONTROL_FLCE0 | BALLOON3_NAND_CONTROL_FLCE1 |
+ BALLOON3_NAND_CONTROL_FLCE2 | BALLOON3_NAND_CONTROL_FLCE3 |
+ BALLOON3_NAND_CONTROL_FLWP;
+
+static void balloon3_nand_cmd_ctl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+ struct nand_chip *this = mtd->priv;
-static unsigned long balloon3_mmc_pin_config[] = {
- GPIO32_MMC_CLK,
- GPIO92_MMC_DAT_0,
- GPIO109_MMC_DAT_1,
- GPIO110_MMC_DAT_2,
- GPIO111_MMC_DAT_3,
- GPIO112_MMC_CMD,
-};
+ if (ctrl & NAND_CTRL_CHANGE) {
+ if (ctrl & NAND_CLE)
+ balloon3_ctl |= BALLOON3_NAND_CONTROL_FLCLE;
+ else
+ balloon3_ctl &= ~BALLOON3_NAND_CONTROL_FLCLE;
-static void balloon3_mci_setpower(struct device *dev, unsigned int vdd)
-{
- struct pxamci_platform_data *p_d = dev->platform_data;
-
- if ((1 << vdd) & p_d->ocr_mask) {
- pr_debug("%s: on\n", __func__);
- /* FIXME something to prod here? */
- } else {
- pr_debug("%s: off\n", __func__);
- /* FIXME something to prod here? */
+ if (ctrl & NAND_ALE)
+ balloon3_ctl |= BALLOON3_NAND_CONTROL_FLALE;
+ else
+ balloon3_ctl &= ~BALLOON3_NAND_CONTROL_FLALE;
+
+ __raw_writel(balloon3_ctl, BALLOON3_NAND_CONTROL_REG);
}
+
+ if (cmd != NAND_CMD_NONE)
+ writeb(cmd, this->IO_ADDR_W);
}
-static struct pxamci_platform_data balloon3_mci_platform_data = {
- .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
- .setpower = balloon3_mci_setpower,
-};
+static void balloon3_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+ if (chip < 0 || chip > 3)
+ return;
-static int balloon3_udc_is_connected(void)
+ balloon3_ctl |= BALLOON3_NAND_CONTROL_FLCE0 |
+ BALLOON3_NAND_CONTROL_FLCE1 |
+ BALLOON3_NAND_CONTROL_FLCE2 |
+ BALLOON3_NAND_CONTROL_FLCE3;
+
+ /* Deassert correct nCE line */
+ balloon3_ctl &= ~(BALLOON3_NAND_CONTROL_FLCE0 << chip);
+
+ __raw_writew(balloon3_ctl, BALLOON3_NAND_CONTROL_REG);
+}
+
+static int balloon3_nand_probe(struct platform_device *pdev)
{
- pr_debug("%s: udc connected\n", __func__);
- return 1;
+ void __iomem *temp_map;
+ uint16_t ver;
+ int ret;
+
+ __raw_writew(BALLOON3_NAND_CONTROL2_16BIT, BALLOON3_NAND_CONTROL2_REG);
+
+ ver = __raw_readw(BALLOON3_FPGA_VER);
+ if (ver > 0x0201)
+ pr_warn("The FPGA code, version 0x%04x, is newer than rel-0.3. "
+ "NAND support might be broken in this version!", ver);
+
+ /* Power up the NAND chips */
+ ret = gpio_request(BALLOON3_GPIO_RUN_NAND, "NAND");
+ if (ret)
+ goto err1;
+
+ ret = gpio_direction_output(BALLOON3_GPIO_RUN_NAND, 1);
+ if (ret)
+ goto err2;
+
+ gpio_set_value(BALLOON3_GPIO_RUN_NAND, 1);
+
+ /* Deassert all nCE lines and write protect line */
+ __raw_writel(balloon3_ctl, BALLOON3_NAND_CONTROL_REG);
+ return 0;
+
+err2:
+ gpio_free(BALLOON3_GPIO_RUN_NAND);
+err1:
+ return ret;
}
-static void balloon3_udc_command(int cmd)
+static void balloon3_nand_remove(struct platform_device *pdev)
{
- switch (cmd) {
- case PXA2XX_UDC_CMD_CONNECT:
- UP2OCR |= (UP2OCR_DPPUE + UP2OCR_DPPUBE);
- pr_debug("%s: connect\n", __func__);
- break;
- case PXA2XX_UDC_CMD_DISCONNECT:
- UP2OCR &= ~UP2OCR_DPPUE;
- pr_debug("%s: disconnect\n", __func__);
- break;
- }
+ /* Power down the NAND chips */
+ gpio_set_value(BALLOON3_GPIO_RUN_NAND, 0);
+ gpio_free(BALLOON3_GPIO_RUN_NAND);
}
-static struct pxa2xx_udc_mach_info balloon3_udc_info = {
- .udc_is_connected = balloon3_udc_is_connected,
- .udc_command = balloon3_udc_command,
+static struct mtd_partition balloon3_partition_info[] = {
+ [0] = {
+ .name = "Boot",
+ .offset = 0,
+ .size = SZ_4M,
+ },
+ [1] = {
+ .name = "RootFS",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL
+ },
};
-static struct pxaficp_platform_data balloon3_ficp_platform_data = {
- .transceiver_cap = IR_SIRMODE | IR_FIRMODE | IR_OFF,
-};
+static const char *balloon3_part_probes[] = { "cmdlinepart", NULL };
-static unsigned long balloon3_ohci_pin_config[] = {
- GPIO88_USBH1_PWR,
- GPIO89_USBH1_PEN,
+struct platform_nand_data balloon3_nand_pdata = {
+ .chip = {
+ .nr_chips = 4,
+ .chip_offset = 0,
+ .nr_partitions = ARRAY_SIZE(balloon3_partition_info),
+ .partitions = balloon3_partition_info,
+ .chip_delay = 50,
+ .part_probe_types = balloon3_part_probes,
+ },
+ .ctrl = {
+ .hwcontrol = 0,
+ .dev_ready = 0,
+ .select_chip = balloon3_nand_select_chip,
+ .cmd_ctrl = balloon3_nand_cmd_ctl,
+ .probe = balloon3_nand_probe,
+ .remove = balloon3_nand_remove,
+ },
};
-static struct pxaohci_platform_data balloon3_ohci_platform_data = {
- .port_mode = PMM_PERPORT_MODE,
- .flags = ENABLE_PORT_ALL | POWER_CONTROL_LOW | POWER_SENSE_LOW,
+static struct resource balloon3_nand_resource[] = {
+ [0] = {
+ .start = BALLOON3_NAND_BASE,
+ .end = BALLOON3_NAND_BASE + 0x4,
+ .flags = IORESOURCE_MEM,
+ },
};
-static unsigned long balloon3_pin_config[] __initdata = {
- /* Select BTUART 'COM1/ttyS0' as IO option for pins 42/43/44/45 */
- GPIO42_BTUART_RXD,
- GPIO43_BTUART_TXD,
- GPIO44_BTUART_CTS,
- GPIO45_BTUART_RTS,
-
- /* Wakeup GPIO */
- GPIO1_GPIO | WAKEUP_ON_EDGE_BOTH,
-
- /* NAND & IDLE LED GPIOs */
- GPIO9_GPIO,
- GPIO10_GPIO,
+static struct platform_device balloon3_nand = {
+ .name = "gen_nand",
+ .num_resources = ARRAY_SIZE(balloon3_nand_resource),
+ .resource = balloon3_nand_resource,
+ .id = -1,
+ .dev = {
+ .platform_data = &balloon3_nand_pdata,
+ }
};
-static struct gpio_led balloon3_gpio_leds[] = {
+static void __init balloon3_nand_init(void)
+{
+ platform_device_register(&balloon3_nand);
+}
+#else
+static inline void balloon3_nand_init(void) {}
+#endif
+
+/******************************************************************************
+ * Core power regulator
+ ******************************************************************************/
+#if defined(CONFIG_REGULATOR_MAX1586) || \
+ defined(CONFIG_REGULATOR_MAX1586_MODULE)
+static struct regulator_consumer_supply balloon3_max1587a_consumers[] = {
{
- .name = "balloon3:green:idle",
- .default_trigger = "heartbeat",
- .gpio = BALLOON3_GPIO_LED_IDLE,
- .active_low = 1,
+ .supply = "vcc_core",
+ }
+};
+
+static struct regulator_init_data balloon3_max1587a_v3_info = {
+ .constraints = {
+ .name = "vcc_core range",
+ .min_uV = 900000,
+ .max_uV = 1705000,
+ .always_on = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
},
+ .consumer_supplies = balloon3_max1587a_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(balloon3_max1587a_consumers),
+};
+
+static struct max1586_subdev_data balloon3_max1587a_subdevs[] = {
{
- .name = "balloon3:green:nand",
- .default_trigger = "nand-disk",
- .gpio = BALLOON3_GPIO_LED_NAND,
- .active_low = 1,
- },
+ .name = "vcc_core",
+ .id = MAX1586_V3,
+ .platform_data = &balloon3_max1587a_v3_info,
+ }
};
-static struct gpio_led_platform_data balloon3_gpio_leds_platform_data = {
- .leds = balloon3_gpio_leds,
- .num_leds = ARRAY_SIZE(balloon3_gpio_leds),
+static struct max1586_platform_data balloon3_max1587a_info = {
+ .subdevs = balloon3_max1587a_subdevs,
+ .num_subdevs = ARRAY_SIZE(balloon3_max1587a_subdevs),
+ .v3_gain = MAX1586_GAIN_R24_3k32, /* 730..1550 mV */
};
-static struct platform_device balloon3led_device = {
- .name = "leds-gpio",
- .id = -1,
- .dev = {
- .platform_data = &balloon3_gpio_leds_platform_data,
+static struct i2c_board_info __initdata balloon3_pi2c_board_info[] = {
+ {
+ I2C_BOARD_INFO("max1586", 0x14),
+ .platform_data = &balloon3_max1587a_info,
},
};
-static void __init balloon3_init(void)
+static void __init balloon3_pmic_init(void)
{
- pr_info("Initialising Balloon3\n");
+ pxa27x_set_i2c_power_info(NULL);
+ i2c_register_board_info(1, ARRAY_AND_SIZE(balloon3_pi2c_board_info));
+}
+#else
+static inline void balloon3_pmic_init(void) {}
+#endif
- /* system bus arbiter setting
- * - Core_Park
- * - LCD_wt:DMA_wt:CORE_Wt = 2:3:4
- */
+/******************************************************************************
+ * Machine init
+ ******************************************************************************/
+static void __init balloon3_init(void)
+{
ARB_CNTRL = ARB_CORE_PARK | 0x234;
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(balloon3_pin_config));
+
pxa_set_ffuart_info(NULL);
pxa_set_btuart_info(NULL);
pxa_set_stuart_info(NULL);
- pxa_set_i2c_info(NULL);
- if (balloon3_has(BALLOON3_FEATURE_AUDIO)) {
- pxa2xx_mfp_config(ARRAY_AND_SIZE(balloon3_ac97_pin_config));
- pxa_set_ac97_info(NULL);
- }
-
- if (balloon3_has(BALLOON3_FEATURE_TOPPOLY)) {
- pxa2xx_mfp_config(ARRAY_AND_SIZE(balloon3_lcd_pin_config));
- gpio_request(BALLOON3_GPIO_RUN_BACKLIGHT,
- "LCD Backlight Power");
- gpio_direction_output(BALLOON3_GPIO_RUN_BACKLIGHT, 1);
- set_pxa_fb_info(&balloon3_pxafb_info);
- }
-
- if (balloon3_has(BALLOON3_FEATURE_MMC)) {
- pxa2xx_mfp_config(ARRAY_AND_SIZE(balloon3_mmc_pin_config));
- pxa_set_mci_info(&balloon3_mci_platform_data);
- }
- pxa_set_ficp_info(&balloon3_ficp_platform_data);
- if (balloon3_has(BALLOON3_FEATURE_OHCI)) {
- pxa2xx_mfp_config(ARRAY_AND_SIZE(balloon3_ohci_pin_config));
- pxa_set_ohci_info(&balloon3_ohci_platform_data);
- }
- pxa_set_udc_info(&balloon3_udc_info);
-
- pxa2xx_mfp_config(ARRAY_AND_SIZE(balloon3_pin_config));
-
- platform_device_register(&balloon3led_device);
+ balloon3_i2c_init();
+ balloon3_irda_init();
+ balloon3_lcd_init();
+ balloon3_leds_init();
+ balloon3_mmc_init();
+ balloon3_nand_init();
+ balloon3_nor_init();
+ balloon3_pmic_init();
+ balloon3_ts_init();
+ balloon3_udc_init();
+ balloon3_uhc_init();
}
static struct map_desc balloon3_io_desc[] __initdata = {
{
pxa3xx_set_i2c_power_info(&cm_x300_pwr_i2c_info);
i2c_register_board_info(1, &cm_x300_pmic_info, 1);
+ set_irq_wake(IRQ_WAKEUP0, 1);
}
static void __init cm_x300_init_wi2wi(void)
{
cm_x300_init_mfp();
- pxa_set_ffuart_info(NULL);
pxa_set_btuart_info(NULL);
pxa_set_stuart_info(NULL);
+ if (cpu_is_pxa300())
+ pxa_set_ffuart_info(NULL);
cm_x300_init_da9030();
cm_x300_init_dm9000();
--- /dev/null
+/*
+ * linux/arch/arm/mach-pxa/colibri-pxa270-evalboard.c
+ *
+ * Support for Toradex PXA270 based Colibri Evaluation Carrier Board
+ * Daniel Mack <daniel@caiaq.de>
+ * Marek Vasut <marek.vasut@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/sysdev.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <asm/mach/arch.h>
+
+#include <mach/pxa27x.h>
+#include <mach/colibri.h>
+#include <mach/mmc.h>
+#include <mach/ohci.h>
+#include <mach/pxa27x-udc.h>
+
+#include "generic.h"
+#include "devices.h"
+
+/******************************************************************************
+ * Pin configuration
+ ******************************************************************************/
+static mfp_cfg_t colibri_pxa270_evalboard_pin_config[] __initdata = {
+ /* MMC */
+ GPIO32_MMC_CLK,
+ GPIO92_MMC_DAT_0,
+ GPIO109_MMC_DAT_1,
+ GPIO110_MMC_DAT_2,
+ GPIO111_MMC_DAT_3,
+ GPIO112_MMC_CMD,
+ GPIO0_GPIO, /* SD detect */
+
+ /* FFUART */
+ GPIO39_FFUART_TXD,
+ GPIO34_FFUART_RXD,
+
+ /* UHC */
+ GPIO88_USBH1_PWR,
+ GPIO89_USBH1_PEN,
+ GPIO119_USBH2_PWR,
+ GPIO120_USBH2_PEN,
+};
+
+/******************************************************************************
+ * SD/MMC card controller
+ ******************************************************************************/
+#if defined(CONFIG_MMC_PXA) || defined(CONFIG_MMC_PXA_MODULE)
+static struct pxamci_platform_data colibri_pxa270_mci_platform_data = {
+ .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34,
+ .gpio_power = -1,
+ .gpio_card_detect = GPIO0_COLIBRI_PXA270_SD_DETECT,
+ .gpio_card_ro = -1,
+ .detect_delay_ms = 200,
+};
+
+static void __init colibri_pxa270_mmc_init(void)
+{
+ pxa_set_mci_info(&colibri_pxa270_mci_platform_data);
+}
+#else
+static inline void colibri_pxa270_mmc_init(void) {}
+#endif
+
+/******************************************************************************
+ * USB Host
+ ******************************************************************************/
+#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
+static int colibri_pxa270_ohci_init(struct device *dev)
+{
+ UP2OCR = UP2OCR_HXS | UP2OCR_HXOE | UP2OCR_DPPDE | UP2OCR_DMPDE;
+ return 0;
+}
+
+static struct pxaohci_platform_data colibri_pxa270_ohci_info = {
+ .port_mode = PMM_PERPORT_MODE,
+ .flags = ENABLE_PORT1 | ENABLE_PORT2 |
+ POWER_CONTROL_LOW | POWER_SENSE_LOW,
+ .init = colibri_pxa270_ohci_init,
+};
+
+static void __init colibri_pxa270_uhc_init(void)
+{
+ pxa_set_ohci_info(&colibri_pxa270_ohci_info);
+}
+#else
+static inline void colibri_pxa270_uhc_init(void) {}
+#endif
+
+void __init colibri_pxa270_evalboard_init(void)
+{
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(colibri_pxa270_evalboard_pin_config));
+ pxa_set_ffuart_info(NULL);
+ pxa_set_btuart_info(NULL);
+ pxa_set_stuart_info(NULL);
+
+ colibri_pxa270_mmc_init();
+ colibri_pxa270_uhc_init();
+}
+
--- /dev/null
+/*
+ * linux/arch/arm/mach-pxa/income.c
+ *
+ * Support for Income s.r.o. SH-Dmaster PXA270 SBC
+ *
+ * Copyright (C) 2010
+ * Marek Vasut <marek.vasut@gmail.com>
+ * Pavel Revak <palo@bielyvlk.sk>
+ *
+ * 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/bitops.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/pwm_backlight.h>
+#include <linux/sysdev.h>
+
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+
+#include <mach/hardware.h>
+#include <mach/mmc.h>
+#include <mach/ohci.h>
+#include <mach/pxa27x.h>
+#include <mach/pxa27x-udc.h>
+#include <mach/pxafb.h>
+
+#include <plat/i2c.h>
+
+#include "devices.h"
+#include "generic.h"
+
+#define GPIO114_INCOME_ETH_IRQ (114)
+#define GPIO0_INCOME_SD_DETECT (0)
+#define GPIO0_INCOME_SD_RO (1)
+#define GPIO54_INCOME_LED_A (54)
+#define GPIO55_INCOME_LED_B (55)
+#define GPIO113_INCOME_TS_IRQ (113)
+
+/******************************************************************************
+ * Pin configuration
+ ******************************************************************************/
+static mfp_cfg_t income_pin_config[] __initdata = {
+ /* MMC */
+ GPIO32_MMC_CLK,
+ GPIO92_MMC_DAT_0,
+ GPIO109_MMC_DAT_1,
+ GPIO110_MMC_DAT_2,
+ GPIO111_MMC_DAT_3,
+ GPIO112_MMC_CMD,
+ GPIO0_GPIO, /* SD detect */
+ GPIO1_GPIO, /* SD read-only */
+
+ /* FFUART */
+ GPIO39_FFUART_TXD,
+ GPIO34_FFUART_RXD,
+
+ /* BFUART */
+ GPIO42_BTUART_RXD,
+ GPIO43_BTUART_TXD,
+ GPIO45_BTUART_RTS,
+
+ /* STUART */
+ GPIO46_STUART_RXD,
+ GPIO47_STUART_TXD,
+
+ /* UHC */
+ GPIO88_USBH1_PWR,
+ GPIO89_USBH1_PEN,
+
+ /* LCD */
+ GPIOxx_LCD_TFT_16BPP,
+
+ /* PWM */
+ GPIO16_PWM0_OUT,
+
+ /* I2C */
+ GPIO117_I2C_SCL,
+ GPIO118_I2C_SDA,
+
+ /* LED */
+ GPIO54_GPIO, /* LED A */
+ GPIO55_GPIO, /* LED B */
+};
+
+/******************************************************************************
+ * SD/MMC card controller
+ ******************************************************************************/
+#if defined(CONFIG_MMC_PXA) || defined(CONFIG_MMC_PXA_MODULE)
+static struct pxamci_platform_data income_mci_platform_data = {
+ .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34,
+ .gpio_power = -1,
+ .gpio_card_detect = GPIO0_INCOME_SD_DETECT,
+ .gpio_card_ro = GPIO0_INCOME_SD_RO,
+ .detect_delay_ms = 200,
+};
+
+static void __init income_mmc_init(void)
+{
+ pxa_set_mci_info(&income_mci_platform_data);
+}
+#else
+static inline void income_mmc_init(void) {}
+#endif
+
+/******************************************************************************
+ * USB Host
+ ******************************************************************************/
+#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
+static struct pxaohci_platform_data income_ohci_info = {
+ .port_mode = PMM_PERPORT_MODE,
+ .flags = ENABLE_PORT1 | POWER_CONTROL_LOW | POWER_SENSE_LOW,
+};
+
+static void __init income_uhc_init(void)
+{
+ pxa_set_ohci_info(&income_ohci_info);
+}
+#else
+static inline void income_uhc_init(void) {}
+#endif
+
+/******************************************************************************
+ * LED
+ ******************************************************************************/
+#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE)
+struct gpio_led income_gpio_leds[] = {
+ {
+ .name = "income:green:leda",
+ .default_trigger = "none",
+ .gpio = GPIO54_INCOME_LED_A,
+ .active_low = 1,
+ },
+ {
+ .name = "income:green:ledb",
+ .default_trigger = "none",
+ .gpio = GPIO55_INCOME_LED_B,
+ .active_low = 1,
+ }
+};
+
+static struct gpio_led_platform_data income_gpio_led_info = {
+ .leds = income_gpio_leds,
+ .num_leds = ARRAY_SIZE(income_gpio_leds),
+};
+
+static struct platform_device income_leds = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &income_gpio_led_info,
+ }
+};
+
+static void __init income_led_init(void)
+{
+ platform_device_register(&income_leds);
+}
+#else
+static inline void income_led_init(void) {}
+#endif
+
+/******************************************************************************
+ * I2C
+ ******************************************************************************/
+#if defined(CONFIG_I2C_PXA) || defined(CONFIG_I2C_PXA_MODULE)
+static struct i2c_board_info __initdata income_i2c_devs[] = {
+ {
+ I2C_BOARD_INFO("ds1340", 0x68),
+ }, {
+ I2C_BOARD_INFO("lm75", 0x4f),
+ },
+};
+
+static void __init income_i2c_init(void)
+{
+ pxa_set_i2c_info(NULL);
+ pxa27x_set_i2c_power_info(NULL);
+ i2c_register_board_info(0, ARRAY_AND_SIZE(income_i2c_devs));
+}
+#else
+static inline void income_i2c_init(void) {}
+#endif
+
+/******************************************************************************
+ * Framebuffer
+ ******************************************************************************/
+#if defined(CONFIG_FB_PXA) || defined(CONFIG_FB_PXA_MODULE)
+static struct pxafb_mode_info income_lcd_modes[] = {
+{
+ .pixclock = 144700,
+ .xres = 320,
+ .yres = 240,
+ .bpp = 32,
+ .depth = 18,
+
+ .left_margin = 10,
+ .right_margin = 10,
+ .upper_margin = 7,
+ .lower_margin = 8,
+
+ .hsync_len = 20,
+ .vsync_len = 2,
+
+ .sync = FB_SYNC_VERT_HIGH_ACT,
+},
+};
+
+static struct pxafb_mach_info income_lcd_screen = {
+ .modes = income_lcd_modes,
+ .num_modes = ARRAY_SIZE(income_lcd_modes),
+ .lcd_conn = LCD_COLOR_TFT_18BPP | LCD_PCLK_EDGE_FALL,
+};
+
+static void __init income_lcd_init(void)
+{
+ set_pxa_fb_info(&income_lcd_screen);
+}
+#else
+static inline void income_lcd_init(void) {}
+#endif
+
+/******************************************************************************
+ * Backlight
+ ******************************************************************************/
+#if defined(CONFIG_BACKLIGHT_PWM) || defined(CONFIG_BACKLIGHT_PWM__MODULE)
+static struct platform_pwm_backlight_data income_backlight_data = {
+ .pwm_id = 0,
+ .max_brightness = 0x3ff,
+ .dft_brightness = 0x1ff,
+ .pwm_period_ns = 1000000,
+};
+
+static struct platform_device income_backlight = {
+ .name = "pwm-backlight",
+ .dev = {
+ .parent = &pxa27x_device_pwm0.dev,
+ .platform_data = &income_backlight_data,
+ },
+};
+
+static void __init income_pwm_init(void)
+{
+ platform_device_register(&income_backlight);
+}
+#else
+static inline void income_pwm_init(void) {}
+#endif
+
+void __init colibri_pxa270_income_boardinit(void)
+{
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(income_pin_config));
+ pxa_set_ffuart_info(NULL);
+ pxa_set_btuart_info(NULL);
+ pxa_set_stuart_info(NULL);
+
+ income_mmc_init();
+ income_uhc_init();
+ income_led_init();
+ income_i2c_init();
+ income_lcd_init();
+ income_pwm_init();
+}
+
*
* Support for Toradex PXA270 based Colibri module
* Daniel Mack <daniel@caiaq.de>
+ * Marek Vasut <marek.vasut@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
*/
#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/sysdev.h>
#include <linux/interrupt.h>
-#include <linux/bitops.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
+#include <linux/kernel.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/physmap.h>
-#include <linux/gpio.h>
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-#include <asm/irq.h>
-#include <asm/sizes.h>
+#include <linux/platform_device.h>
+#include <linux/sysdev.h>
+#include <linux/ucb1400.h>
+
#include <asm/mach/arch.h>
-#include <asm/mach/map.h>
-#include <asm/mach/irq.h>
#include <asm/mach/flash.h>
+#include <asm/mach-types.h>
+#include <asm/sizes.h>
-#include <mach/pxa27x.h>
+#include <mach/audio.h>
#include <mach/colibri.h>
+#include <mach/pxa27x.h>
-#include "generic.h"
#include "devices.h"
+#include "generic.h"
-/*
- * GPIO configuration
- */
+/******************************************************************************
+ * Pin configuration
+ ******************************************************************************/
static mfp_cfg_t colibri_pxa270_pin_config[] __initdata = {
+ /* Ethernet */
GPIO78_nCS_2, /* Ethernet CS */
GPIO114_GPIO, /* Ethernet IRQ */
+
+ /* AC97 */
+ GPIO28_AC97_BITCLK,
+ GPIO29_AC97_SDATA_IN_0,
+ GPIO30_AC97_SDATA_OUT,
+ GPIO31_AC97_SYNC,
+ GPIO95_AC97_nRESET,
+ GPIO98_AC97_SYSCLK,
+ GPIO113_GPIO, /* Touchscreen IRQ */
};
-/*
- * NOR flash
- */
+/******************************************************************************
+ * NOR Flash
+ ******************************************************************************/
+#if defined(CONFIG_MTD_PHYSMAP) || defined(CONFIG_MTD_PHYSMAP_MODULE)
static struct mtd_partition colibri_partitions[] = {
{
.name = "Bootloader",
.offset = 0x00000000,
.size = 0x00040000,
- .mask_flags = MTD_WRITEABLE /* force read-only */
+ .mask_flags = MTD_WRITEABLE /* force read-only */
}, {
.name = "Kernel",
.offset = 0x00040000,
.num_resources = 1,
};
-/*
- * DM9000 Ethernet
- */
-#if defined(CONFIG_DM9000)
-static struct resource dm9000_resources[] = {
- [0] = {
- .start = COLIBRI_PXA270_ETH_PHYS,
- .end = COLIBRI_PXA270_ETH_PHYS + 3,
+static void __init colibri_pxa270_nor_init(void)
+{
+ platform_device_register(&colibri_pxa270_flash_device);
+}
+#else
+static inline void colibri_pxa270_nor_init(void) {}
+#endif
+
+/******************************************************************************
+ * Ethernet
+ ******************************************************************************/
+#if defined(CONFIG_DM9000) || defined(CONFIG_DM9000_MODULE)
+static struct resource colibri_pxa270_dm9000_resources[] = {
+ {
+ .start = PXA_CS2_PHYS,
+ .end = PXA_CS2_PHYS + 3,
.flags = IORESOURCE_MEM,
},
- [1] = {
- .start = COLIBRI_PXA270_ETH_PHYS + 4,
- .end = COLIBRI_PXA270_ETH_PHYS + 4 + 500,
+ {
+ .start = PXA_CS2_PHYS + 4,
+ .end = PXA_CS2_PHYS + 4 + 500,
.flags = IORESOURCE_MEM,
},
- [2] = {
- .start = COLIBRI_PXA270_ETH_IRQ,
- .end = COLIBRI_PXA270_ETH_IRQ,
+ {
+ .start = gpio_to_irq(GPIO114_COLIBRI_PXA270_ETH_IRQ),
+ .end = gpio_to_irq(GPIO114_COLIBRI_PXA270_ETH_IRQ),
.flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING,
},
};
-static struct platform_device dm9000_device = {
+static struct platform_device colibri_pxa270_dm9000_device = {
.name = "dm9000",
.id = -1,
- .num_resources = ARRAY_SIZE(dm9000_resources),
- .resource = dm9000_resources,
+ .num_resources = ARRAY_SIZE(colibri_pxa270_dm9000_resources),
+ .resource = colibri_pxa270_dm9000_resources,
};
-#endif /* CONFIG_DM9000 */
-static struct platform_device *colibri_pxa270_devices[] __initdata = {
- &colibri_pxa270_flash_device,
-#if defined(CONFIG_DM9000)
- &dm9000_device,
+static void __init colibri_pxa270_eth_init(void)
+{
+ platform_device_register(&colibri_pxa270_dm9000_device);
+}
+#else
+static inline void colibri_pxa270_eth_init(void) {}
#endif
+
+/******************************************************************************
+ * Audio and Touchscreen
+ ******************************************************************************/
+#if defined(CONFIG_TOUCHSCREEN_UCB1400) || \
+ defined(CONFIG_TOUCHSCREEN_UCB1400_MODULE)
+static pxa2xx_audio_ops_t colibri_pxa270_ac97_pdata = {
+ .reset_gpio = 95,
+};
+
+static struct ucb1400_pdata colibri_pxa270_ucb1400_pdata = {
+ .irq = gpio_to_irq(GPIO113_COLIBRI_PXA270_TS_IRQ),
+};
+
+static struct platform_device colibri_pxa270_ucb1400_device = {
+ .name = "ucb1400_core",
+ .id = -1,
+ .dev = {
+ .platform_data = &colibri_pxa270_ucb1400_pdata,
+ },
};
+static void __init colibri_pxa270_tsc_init(void)
+{
+ pxa_set_ac97_info(&colibri_pxa270_ac97_pdata);
+ platform_device_register(&colibri_pxa270_ucb1400_device);
+}
+#else
+static inline void colibri_pxa270_tsc_init(void) {}
+#endif
+
+static int colibri_pxa270_baseboard;
+core_param(colibri_pxa270_baseboard, colibri_pxa270_baseboard, int, 0444);
+
static void __init colibri_pxa270_init(void)
{
pxa2xx_mfp_config(ARRAY_AND_SIZE(colibri_pxa270_pin_config));
- pxa_set_ffuart_info(NULL);
- pxa_set_btuart_info(NULL);
- pxa_set_stuart_info(NULL);
- platform_add_devices(ARRAY_AND_SIZE(colibri_pxa270_devices));
+
+ colibri_pxa270_nor_init();
+ colibri_pxa270_eth_init();
+ colibri_pxa270_tsc_init();
+
+ switch (colibri_pxa270_baseboard) {
+ case COLIBRI_PXA270_EVALBOARD:
+ colibri_pxa270_evalboard_init();
+ break;
+ case COLIBRI_PXA270_INCOME:
+ colibri_pxa270_income_boardinit();
+ break;
+ default:
+ printk(KERN_ERR "Illegal colibri_pxa270_baseboard type %d\n",
+ colibri_pxa270_baseboard);
+ }
+}
+
+/* The "Income s.r.o. SH-Dmaster PXA270 SBC" board can be booted either
+ * with the INCOME mach type or with COLIBRI and the kernel parameter
+ * "colibri_pxa270_baseboard=1"
+ */
+static void __init colibri_pxa270_income_init(void)
+{
+ colibri_pxa270_baseboard = COLIBRI_PXA270_INCOME;
+ colibri_pxa270_init();
}
MACHINE_START(COLIBRI, "Toradex Colibri PXA270")
.timer = &pxa_timer,
MACHINE_END
+MACHINE_START(INCOME, "Income s.r.o. SH-Dmaster PXA270 SBC")
+ .phys_io = 0x40000000,
+ .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
+ .boot_params = 0xa0000100,
+ .init_machine = colibri_pxa270_income_init,
+ .map_io = pxa_map_io,
+ .init_irq = pxa27x_init_irq,
+ .timer = &pxa_timer,
+MACHINE_END
+
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
+#include <linux/usb/gpio_vbus.h>
#include <asm/mach-types.h>
#include <asm/sizes.h>
#include <mach/pxafb.h>
#include <mach/ohci.h>
#include <mach/audio.h>
+#include <mach/pxa27x-udc.h>
+#include <mach/udc.h>
#include "generic.h"
#include "devices.h"
static inline void colibri_pxa320_init_ohci(void) {}
#endif /* CONFIG_USB_OHCI_HCD || CONFIG_USB_OHCI_HCD_MODULE */
+#if defined(CONFIG_USB_GADGET_PXA27X)||defined(CONFIG_USB_GADGET_PXA27X_MODULE)
+static struct gpio_vbus_mach_info colibri_pxa320_gpio_vbus_info = {
+ .gpio_vbus = mfp_to_gpio(MFP_PIN_GPIO96),
+ .gpio_pullup = -1,
+};
+
+static struct platform_device colibri_pxa320_gpio_vbus = {
+ .name = "gpio-vbus",
+ .id = -1,
+ .dev = {
+ .platform_data = &colibri_pxa320_gpio_vbus_info,
+ },
+};
+
+static void colibri_pxa320_udc_command(int cmd)
+{
+ if (cmd == PXA2XX_UDC_CMD_CONNECT)
+ UP2OCR = UP2OCR_HXOE | UP2OCR_DPPUE;
+ else if (cmd == PXA2XX_UDC_CMD_DISCONNECT)
+ UP2OCR = UP2OCR_HXOE;
+}
+
+static struct pxa2xx_udc_mach_info colibri_pxa320_udc_info __initdata = {
+ .udc_command = colibri_pxa320_udc_command,
+ .gpio_pullup = -1,
+};
+
+static void __init colibri_pxa320_init_udc(void)
+{
+ pxa_set_udc_info(&colibri_pxa320_udc_info);
+ platform_device_register(&colibri_pxa320_gpio_vbus);
+}
+#else
+static inline void colibri_pxa320_init_udc(void) {}
+#endif
+
static mfp_cfg_t colibri_pxa320_mmc_pin_config[] __initdata = {
GPIO22_MMC1_CLK,
GPIO23_MMC1_CMD,
colibri_pxa3xx_init_mmc(ARRAY_AND_SIZE(colibri_pxa320_mmc_pin_config),
mfp_to_gpio(MFP_PIN_GPIO28));
colibri_pxa320_init_uart();
+ colibri_pxa320_init_udc();
}
MACHINE_START(COLIBRI320, "Toradex Colibri PXA320")
#include <mach/udc.h>
#include <mach/pxa2xx_spi.h>
#include <mach/corgi.h>
-#include <mach/sharpsl.h>
+#include <mach/sharpsl_pm.h>
#include <asm/mach/sharpsl_param.h>
#include <asm/hardware/scoop.h>
#include "generic.h"
#include "devices.h"
-#include "sharpsl.h"
static unsigned long corgi_pin_config[] __initdata = {
/* Static Memory I/O */
.num_devs = 1,
};
-EXPORT_SYMBOL(corgiscoop_device);
-
static struct w100_mem_info corgi_fb_mem = {
.ext_cntl = 0x00040003,
.sdram_mode_reg = 0x00650021,
#include <asm/mach-types.h>
#include <mach/hardware.h>
-#include <mach/sharpsl.h>
#include <mach/corgi.h>
#include <mach/pxa2xx-regs.h>
+#include <mach/sharpsl_pm.h>
#include "generic.h"
-#include "sharpsl.h"
#define SHARPSL_CHARGE_ON_VOLT 0x99 /* 2.9V */
#define SHARPSL_CHARGE_ON_TEMP 0xe0 /* 2.9V */
case SHARPSL_STATUS_ACIN:
return ((GPLR(CORGI_GPIO_AC_IN) & GPIO_bit(CORGI_GPIO_AC_IN)) != 0);
case SHARPSL_STATUS_LOCK:
- return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batlock);
+ return gpio_get_value(sharpsl_pm.machinfo->gpio_batlock);
case SHARPSL_STATUS_CHRGFULL:
- return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batfull);
+ return gpio_get_value(sharpsl_pm.machinfo->gpio_batfull);
case SHARPSL_STATUS_FATAL:
- return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_fatal);
+ return gpio_get_value(sharpsl_pm.machinfo->gpio_fatal);
case SHARPSL_ACIN_VOLT:
return sharpsl_pm_pxa_read_max1111(MAX1111_ACIN_VOLT);
case SHARPSL_BATT_TEMP:
.should_wakeup = corgi_should_wakeup,
#if defined(CONFIG_LCD_CORGI)
.backlight_limit = corgi_lcd_limit_intensity,
-#elif defined(CONFIG_BACKLIGHT_CORGI)
- .backlight_limit = corgibl_limit_intensity,
#endif
.charge_on_volt = SHARPSL_CHARGE_ON_VOLT,
.charge_on_temp = SHARPSL_CHARGE_ON_TEMP,
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <asm/pmu.h>
#include <mach/udc.h>
#include <mach/pxafb.h>
#include <mach/mmc.h>
dev_err(&dev->dev, "unable to register device: %d\n", ret);
}
+static struct resource pxa_resource_pmu = {
+ .start = IRQ_PMU,
+ .end = IRQ_PMU,
+ .flags = IORESOURCE_IRQ,
+};
+
+struct platform_device pxa_device_pmu = {
+ .name = "arm-pmu",
+ .id = ARM_PMU_DEVICE_CPU,
+ .resource = &pxa_resource_pmu,
+ .num_resources = 1,
+};
+
static struct resource pxamci_resources[] = {
[0] = {
.start = 0x41100000,
+extern struct platform_device pxa_device_pmu;
extern struct platform_device pxa_device_mci;
extern struct platform_device pxa3xx_device_mci2;
extern struct platform_device pxa3xx_device_mci3;
+++ /dev/null
-/*
- * Hardware definitions for the Toshiba e330 PDAs
- *
- * Copyright (c) 2003 Ian Molton <spyro@f2s.com>
- *
- * This file is licensed under
- * the terms of the GNU General Public License version 2. This program
- * is licensed "as is" without any warranty of any kind, whether express
- * or implied.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/mfd/tc6387xb.h>
-
-#include <asm/setup.h>
-#include <asm/mach/arch.h>
-#include <asm/mach-types.h>
-
-#include <mach/pxa25x.h>
-#include <mach/eseries-gpio.h>
-#include <mach/udc.h>
-
-#include "generic.h"
-#include "eseries.h"
-#include "clock.h"
-
-/* -------------------- e330 tc6387xb parameters -------------------- */
-
-static struct tc6387xb_platform_data e330_tc6387xb_info = {
- .enable = &eseries_tmio_enable,
- .disable = &eseries_tmio_disable,
- .suspend = &eseries_tmio_suspend,
- .resume = &eseries_tmio_resume,
-};
-
-static struct platform_device e330_tc6387xb_device = {
- .name = "tc6387xb",
- .id = -1,
- .dev = {
- .platform_data = &e330_tc6387xb_info,
- },
- .num_resources = 2,
- .resource = eseries_tmio_resources,
-};
-
-/* --------------------------------------------------------------- */
-
-static struct platform_device *devices[] __initdata = {
- &e330_tc6387xb_device,
-};
-
-static void __init e330_init(void)
-{
- pxa_set_ffuart_info(NULL);
- pxa_set_btuart_info(NULL);
- pxa_set_stuart_info(NULL);
- eseries_register_clks();
- eseries_get_tmio_gpios();
- platform_add_devices(devices, ARRAY_SIZE(devices));
- pxa_set_udc_info(&e7xx_udc_mach_info);
-}
-
-MACHINE_START(E330, "Toshiba e330")
- /* Maintainer: Ian Molton (spyro@f2s.com) */
- .phys_io = 0x40000000,
- .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
- .boot_params = 0xa0000100,
- .map_io = pxa_map_io,
- .init_irq = pxa25x_init_irq,
- .fixup = eseries_fixup,
- .init_machine = e330_init,
- .timer = &pxa_timer,
-MACHINE_END
-
+++ /dev/null
-/*
- * Hardware definitions for the Toshiba e350 PDAs
- *
- * Copyright (c) 2003 Ian Molton <spyro@f2s.com>
- *
- * This file is licensed under
- * the terms of the GNU General Public License version 2. This program
- * is licensed "as is" without any warranty of any kind, whether express
- * or implied.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/mfd/t7l66xb.h>
-
-#include <asm/setup.h>
-#include <asm/mach/arch.h>
-#include <asm/mach-types.h>
-
-#include <mach/irqs.h>
-#include <mach/pxa25x.h>
-#include <mach/eseries-gpio.h>
-#include <mach/udc.h>
-
-#include "generic.h"
-#include "eseries.h"
-#include "clock.h"
-
-/* -------------------- e350 t7l66xb parameters -------------------- */
-
-static struct t7l66xb_platform_data e350_t7l66xb_info = {
- .irq_base = IRQ_BOARD_START,
- .enable = &eseries_tmio_enable,
- .suspend = &eseries_tmio_suspend,
- .resume = &eseries_tmio_resume,
-};
-
-static struct platform_device e350_t7l66xb_device = {
- .name = "t7l66xb",
- .id = -1,
- .dev = {
- .platform_data = &e350_t7l66xb_info,
- },
- .num_resources = 2,
- .resource = eseries_tmio_resources,
-};
-
-/* ---------------------------------------------------------- */
-
-static struct platform_device *devices[] __initdata = {
- &e350_t7l66xb_device,
-};
-
-static void __init e350_init(void)
-{
- pxa_set_ffuart_info(NULL);
- pxa_set_btuart_info(NULL);
- pxa_set_stuart_info(NULL);
- eseries_register_clks();
- eseries_get_tmio_gpios();
- platform_add_devices(devices, ARRAY_SIZE(devices));
- pxa_set_udc_info(&e7xx_udc_mach_info);
-}
-
-MACHINE_START(E350, "Toshiba e350")
- /* Maintainer: Ian Molton (spyro@f2s.com) */
- .phys_io = 0x40000000,
- .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
- .boot_params = 0xa0000100,
- .map_io = pxa_map_io,
- .init_irq = pxa25x_init_irq,
- .fixup = eseries_fixup,
- .init_machine = e350_init,
- .timer = &pxa_timer,
-MACHINE_END
-
+++ /dev/null
-/*
- * Hardware definitions for the Toshiba eseries PDAs
- *
- * Copyright (c) 2003 Ian Molton <spyro@f2s.com>
- *
- * This file is licensed under
- * the terms of the GNU General Public License version 2. This program
- * is licensed "as is" without any warranty of any kind, whether express
- * or implied.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/mfd/t7l66xb.h>
-#include <linux/mtd/nand.h>
-#include <linux/mtd/partitions.h>
-
-#include <asm/setup.h>
-#include <asm/mach/arch.h>
-#include <asm/mach-types.h>
-
-#include <mach/pxa25x.h>
-#include <mach/eseries-gpio.h>
-#include <mach/pxafb.h>
-#include <mach/udc.h>
-#include <mach/irqs.h>
-
-#include "generic.h"
-#include "eseries.h"
-#include "clock.h"
-
-/* ------------------------ E400 LCD definitions ------------------------ */
-
-static struct pxafb_mode_info e400_pxafb_mode_info = {
- .pixclock = 140703,
- .xres = 240,
- .yres = 320,
- .bpp = 16,
- .hsync_len = 4,
- .left_margin = 28,
- .right_margin = 8,
- .vsync_len = 3,
- .upper_margin = 5,
- .lower_margin = 6,
- .sync = 0,
-};
-
-static struct pxafb_mach_info e400_pxafb_mach_info = {
- .modes = &e400_pxafb_mode_info,
- .num_modes = 1,
- .lcd_conn = LCD_COLOR_TFT_16BPP,
- .lccr3 = 0,
- .pxafb_backlight_power = NULL,
-};
-
-/* ------------------------ E400 MFP config ----------------------------- */
-
-static unsigned long e400_pin_config[] __initdata = {
- /* Chip selects */
- GPIO15_nCS_1, /* CS1 - Flash */
- GPIO80_nCS_4, /* CS4 - TMIO */
-
- /* Clocks */
- GPIO12_32KHz,
-
- /* BTUART */
- GPIO42_BTUART_RXD,
- GPIO43_BTUART_TXD,
- GPIO44_BTUART_CTS,
-
- /* TMIO controller */
- GPIO19_GPIO, /* t7l66xb #PCLR */
- GPIO45_GPIO, /* t7l66xb #SUSPEND (NOT BTUART!) */
-
- /* wakeup */
- GPIO0_GPIO | WAKEUP_ON_EDGE_RISE,
-};
-
-/* ---------------------------------------------------------------------- */
-
-static struct mtd_partition partition_a = {
- .name = "Internal NAND flash",
- .offset = 0,
- .size = MTDPART_SIZ_FULL,
-};
-
-static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
-
-static struct nand_bbt_descr e400_t7l66xb_nand_bbt = {
- .options = 0,
- .offs = 4,
- .len = 2,
- .pattern = scan_ff_pattern
-};
-
-static struct tmio_nand_data e400_t7l66xb_nand_config = {
- .num_partitions = 1,
- .partition = &partition_a,
- .badblock_pattern = &e400_t7l66xb_nand_bbt,
-};
-
-static struct t7l66xb_platform_data e400_t7l66xb_info = {
- .irq_base = IRQ_BOARD_START,
- .enable = &eseries_tmio_enable,
- .suspend = &eseries_tmio_suspend,
- .resume = &eseries_tmio_resume,
-
- .nand_data = &e400_t7l66xb_nand_config,
-};
-
-static struct platform_device e400_t7l66xb_device = {
- .name = "t7l66xb",
- .id = -1,
- .dev = {
- .platform_data = &e400_t7l66xb_info,
- },
- .num_resources = 2,
- .resource = eseries_tmio_resources,
-};
-
-/* ---------------------------------------------------------- */
-
-static struct platform_device *devices[] __initdata = {
- &e400_t7l66xb_device,
-};
-
-static void __init e400_init(void)
-{
- pxa2xx_mfp_config(ARRAY_AND_SIZE(e400_pin_config));
- pxa_set_ffuart_info(NULL);
- pxa_set_btuart_info(NULL);
- pxa_set_stuart_info(NULL);
- /* Fixme - e400 may have a switched clock */
- eseries_register_clks();
- eseries_get_tmio_gpios();
- set_pxa_fb_info(&e400_pxafb_mach_info);
- platform_add_devices(devices, ARRAY_SIZE(devices));
- pxa_set_udc_info(&e7xx_udc_mach_info);
-}
-
-MACHINE_START(E400, "Toshiba e400")
- /* Maintainer: Ian Molton (spyro@f2s.com) */
- .phys_io = 0x40000000,
- .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
- .boot_params = 0xa0000100,
- .map_io = pxa_map_io,
- .init_irq = pxa25x_init_irq,
- .fixup = eseries_fixup,
- .init_machine = e400_init,
- .timer = &pxa_timer,
-MACHINE_END
-
+++ /dev/null
-/*
- * Hardware definitions for the Toshiba eseries PDAs
- *
- * Copyright (c) 2003 Ian Molton <spyro@f2s.com>
- *
- * This file is licensed under
- * the terms of the GNU General Public License version 2. This program
- * is licensed "as is" without any warranty of any kind, whether express
- * or implied.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/fb.h>
-#include <linux/clk.h>
-#include <linux/mfd/t7l66xb.h>
-
-#include <video/w100fb.h>
-
-#include <asm/setup.h>
-#include <asm/mach/arch.h>
-#include <asm/mach-types.h>
-
-#include <mach/pxa25x.h>
-#include <mach/eseries-gpio.h>
-#include <mach/udc.h>
-#include <mach/irda.h>
-#include <mach/irqs.h>
-#include <mach/audio.h>
-
-#include "generic.h"
-#include "eseries.h"
-#include "clock.h"
-#include "devices.h"
-
-/* ------------------------ e740 video support --------------------------- */
-
-static struct w100_gen_regs e740_lcd_regs = {
- .lcd_format = 0x00008023,
- .lcdd_cntl1 = 0x0f000000,
- .lcdd_cntl2 = 0x0003ffff,
- .genlcd_cntl1 = 0x00ffff03,
- .genlcd_cntl2 = 0x003c0f03,
- .genlcd_cntl3 = 0x000143aa,
-};
-
-static struct w100_mode e740_lcd_mode = {
- .xres = 240,
- .yres = 320,
- .left_margin = 20,
- .right_margin = 28,
- .upper_margin = 9,
- .lower_margin = 8,
- .crtc_ss = 0x80140013,
- .crtc_ls = 0x81150110,
- .crtc_gs = 0x80050005,
- .crtc_vpos_gs = 0x000a0009,
- .crtc_rev = 0x0040010a,
- .crtc_dclk = 0xa906000a,
- .crtc_gclk = 0x80050108,
- .crtc_goe = 0x80050108,
- .pll_freq = 57,
- .pixclk_divider = 4,
- .pixclk_divider_rotated = 4,
- .pixclk_src = CLK_SRC_XTAL,
- .sysclk_divider = 1,
- .sysclk_src = CLK_SRC_PLL,
- .crtc_ps1_active = 0x41060010,
-};
-
-static struct w100_gpio_regs e740_w100_gpio_info = {
- .init_data1 = 0x21002103,
- .gpio_dir1 = 0xffffdeff,
- .gpio_oe1 = 0x03c00643,
- .init_data2 = 0x003f003f,
- .gpio_dir2 = 0xffffffff,
- .gpio_oe2 = 0x000000ff,
-};
-
-static struct w100fb_mach_info e740_fb_info = {
- .modelist = &e740_lcd_mode,
- .num_modes = 1,
- .regs = &e740_lcd_regs,
- .gpio = &e740_w100_gpio_info,
- .xtal_freq = 14318000,
- .xtal_dbl = 1,
-};
-
-static struct resource e740_fb_resources[] = {
- [0] = {
- .start = 0x0c000000,
- .end = 0x0cffffff,
- .flags = IORESOURCE_MEM,
- },
-};
-
-static struct platform_device e740_fb_device = {
- .name = "w100fb",
- .id = -1,
- .dev = {
- .platform_data = &e740_fb_info,
- },
- .num_resources = ARRAY_SIZE(e740_fb_resources),
- .resource = e740_fb_resources,
-};
-
-/* --------------------------- MFP Pin config -------------------------- */
-
-static unsigned long e740_pin_config[] __initdata = {
- /* Chip selects */
- GPIO15_nCS_1, /* CS1 - Flash */
- GPIO79_nCS_3, /* CS3 - IMAGEON */
- GPIO80_nCS_4, /* CS4 - TMIO */
-
- /* Clocks */
- GPIO12_32KHz,
-
- /* BTUART */
- GPIO42_BTUART_RXD,
- GPIO43_BTUART_TXD,
- GPIO44_BTUART_CTS,
-
- /* TMIO controller */
- GPIO19_GPIO, /* t7l66xb #PCLR */
- GPIO45_GPIO, /* t7l66xb #SUSPEND (NOT BTUART!) */
-
- /* UDC */
- GPIO13_GPIO,
- GPIO3_GPIO,
-
- /* IrDA */
- GPIO38_GPIO | MFP_LPM_DRIVE_HIGH,
-
- /* AC97 */
- GPIO28_AC97_BITCLK,
- GPIO29_AC97_SDATA_IN_0,
- GPIO30_AC97_SDATA_OUT,
- GPIO31_AC97_SYNC,
-
- /* Audio power control */
- GPIO16_GPIO, /* AC97 codec AVDD2 supply (analogue power) */
- GPIO40_GPIO, /* Mic amp power */
- GPIO41_GPIO, /* Headphone amp power */
-
- /* PC Card */
- GPIO8_GPIO, /* CD0 */
- GPIO44_GPIO, /* CD1 */
- GPIO11_GPIO, /* IRQ0 */
- GPIO6_GPIO, /* IRQ1 */
- GPIO27_GPIO, /* RST0 */
- GPIO24_GPIO, /* RST1 */
- GPIO20_GPIO, /* PWR0 */
- GPIO23_GPIO, /* PWR1 */
- GPIO48_nPOE,
- GPIO49_nPWE,
- GPIO50_nPIOR,
- GPIO51_nPIOW,
- GPIO52_nPCE_1,
- GPIO53_nPCE_2,
- GPIO54_nPSKTSEL,
- GPIO55_nPREG,
- GPIO56_nPWAIT,
- GPIO57_nIOIS16,
-
- /* wakeup */
- GPIO0_GPIO | WAKEUP_ON_EDGE_RISE,
-};
-
-/* -------------------- e740 t7l66xb parameters -------------------- */
-
-static struct t7l66xb_platform_data e740_t7l66xb_info = {
- .irq_base = IRQ_BOARD_START,
- .enable = &eseries_tmio_enable,
- .suspend = &eseries_tmio_suspend,
- .resume = &eseries_tmio_resume,
-};
-
-static struct platform_device e740_t7l66xb_device = {
- .name = "t7l66xb",
- .id = -1,
- .dev = {
- .platform_data = &e740_t7l66xb_info,
- },
- .num_resources = 2,
- .resource = eseries_tmio_resources,
-};
-
-/* ----------------------------------------------------------------------- */
-
-static struct platform_device *devices[] __initdata = {
- &e740_fb_device,
- &e740_t7l66xb_device,
-};
-
-static void __init e740_init(void)
-{
- pxa2xx_mfp_config(ARRAY_AND_SIZE(e740_pin_config));
- pxa_set_ffuart_info(NULL);
- pxa_set_btuart_info(NULL);
- pxa_set_stuart_info(NULL);
- eseries_register_clks();
- clk_add_alias("CLK_CK48M", e740_t7l66xb_device.name,
- "UDCCLK", &pxa25x_device_udc.dev),
- eseries_get_tmio_gpios();
- platform_add_devices(devices, ARRAY_SIZE(devices));
- pxa_set_udc_info(&e7xx_udc_mach_info);
- pxa_set_ac97_info(NULL);
- pxa_set_ficp_info(&e7xx_ficp_platform_data);
-}
-
-MACHINE_START(E740, "Toshiba e740")
- /* Maintainer: Ian Molton (spyro@f2s.com) */
- .phys_io = 0x40000000,
- .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
- .boot_params = 0xa0000100,
- .map_io = pxa_map_io,
- .init_irq = pxa25x_init_irq,
- .fixup = eseries_fixup,
- .init_machine = e740_init,
- .timer = &pxa_timer,
-MACHINE_END
-
+++ /dev/null
-/*
- * Hardware definitions for the Toshiba eseries PDAs
- *
- * Copyright (c) 2003 Ian Molton <spyro@f2s.com>
- *
- * This file is licensed under
- * the terms of the GNU General Public License version 2. This program
- * is licensed "as is" without any warranty of any kind, whether express
- * or implied.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/fb.h>
-#include <linux/mfd/tc6393xb.h>
-
-#include <video/w100fb.h>
-
-#include <asm/setup.h>
-#include <asm/mach/arch.h>
-#include <asm/mach-types.h>
-
-#include <mach/pxa25x.h>
-#include <mach/eseries-gpio.h>
-#include <mach/udc.h>
-#include <mach/irda.h>
-#include <mach/irqs.h>
-#include <mach/audio.h>
-
-#include "generic.h"
-#include "eseries.h"
-#include "clock.h"
-
-/* ---------------------- E750 LCD definitions -------------------- */
-
-static struct w100_gen_regs e750_lcd_regs = {
- .lcd_format = 0x00008003,
- .lcdd_cntl1 = 0x00000000,
- .lcdd_cntl2 = 0x0003ffff,
- .genlcd_cntl1 = 0x00fff003,
- .genlcd_cntl2 = 0x003c0f03,
- .genlcd_cntl3 = 0x000143aa,
-};
-
-static struct w100_mode e750_lcd_mode = {
- .xres = 240,
- .yres = 320,
- .left_margin = 21,
- .right_margin = 22,
- .upper_margin = 5,
- .lower_margin = 4,
- .crtc_ss = 0x80150014,
- .crtc_ls = 0x8014000d,
- .crtc_gs = 0xc1000005,
- .crtc_vpos_gs = 0x00020147,
- .crtc_rev = 0x0040010a,
- .crtc_dclk = 0xa1700030,
- .crtc_gclk = 0x80cc0015,
- .crtc_goe = 0x80cc0015,
- .crtc_ps1_active = 0x61060017,
- .pll_freq = 57,
- .pixclk_divider = 4,
- .pixclk_divider_rotated = 4,
- .pixclk_src = CLK_SRC_XTAL,
- .sysclk_divider = 1,
- .sysclk_src = CLK_SRC_PLL,
-};
-
-static struct w100_gpio_regs e750_w100_gpio_info = {
- .init_data1 = 0x01192f1b,
- .gpio_dir1 = 0xd5ffdeff,
- .gpio_oe1 = 0x000020bf,
- .init_data2 = 0x010f010f,
- .gpio_dir2 = 0xffffffff,
- .gpio_oe2 = 0x000001cf,
-};
-
-static struct w100fb_mach_info e750_fb_info = {
- .modelist = &e750_lcd_mode,
- .num_modes = 1,
- .regs = &e750_lcd_regs,
- .gpio = &e750_w100_gpio_info,
- .xtal_freq = 14318000,
- .xtal_dbl = 1,
-};
-
-static struct resource e750_fb_resources[] = {
- [0] = {
- .start = 0x0c000000,
- .end = 0x0cffffff,
- .flags = IORESOURCE_MEM,
- },
-};
-
-static struct platform_device e750_fb_device = {
- .name = "w100fb",
- .id = -1,
- .dev = {
- .platform_data = &e750_fb_info,
- },
- .num_resources = ARRAY_SIZE(e750_fb_resources),
- .resource = e750_fb_resources,
-};
-
-/* -------------------- e750 MFP parameters -------------------- */
-
-static unsigned long e750_pin_config[] __initdata = {
- /* Chip selects */
- GPIO15_nCS_1, /* CS1 - Flash */
- GPIO79_nCS_3, /* CS3 - IMAGEON */
- GPIO80_nCS_4, /* CS4 - TMIO */
-
- /* Clocks */
- GPIO11_3_6MHz,
-
- /* BTUART */
- GPIO42_BTUART_RXD,
- GPIO43_BTUART_TXD,
- GPIO44_BTUART_CTS,
-
- /* TMIO controller */
- GPIO19_GPIO, /* t7l66xb #PCLR */
- GPIO45_GPIO, /* t7l66xb #SUSPEND (NOT BTUART!) */
-
- /* UDC */
- GPIO13_GPIO,
- GPIO3_GPIO,
-
- /* IrDA */
- GPIO38_GPIO | MFP_LPM_DRIVE_HIGH,
-
- /* AC97 */
- GPIO28_AC97_BITCLK,
- GPIO29_AC97_SDATA_IN_0,
- GPIO30_AC97_SDATA_OUT,
- GPIO31_AC97_SYNC,
-
- /* Audio power control */
- GPIO4_GPIO, /* Headphone amp power */
- GPIO7_GPIO, /* Speaker amp power */
- GPIO37_GPIO, /* Headphone detect */
-
- /* PC Card */
- GPIO8_GPIO, /* CD0 */
- GPIO44_GPIO, /* CD1 */
- GPIO11_GPIO, /* IRQ0 */
- GPIO6_GPIO, /* IRQ1 */
- GPIO27_GPIO, /* RST0 */
- GPIO24_GPIO, /* RST1 */
- GPIO20_GPIO, /* PWR0 */
- GPIO23_GPIO, /* PWR1 */
- GPIO48_nPOE,
- GPIO49_nPWE,
- GPIO50_nPIOR,
- GPIO51_nPIOW,
- GPIO52_nPCE_1,
- GPIO53_nPCE_2,
- GPIO54_nPSKTSEL,
- GPIO55_nPREG,
- GPIO56_nPWAIT,
- GPIO57_nIOIS16,
-
- /* wakeup */
- GPIO0_GPIO | WAKEUP_ON_EDGE_RISE,
-};
-
-/* ----------------- e750 tc6393xb parameters ------------------ */
-
-static struct tc6393xb_platform_data e750_tc6393xb_info = {
- .irq_base = IRQ_BOARD_START,
- .scr_pll2cr = 0x0cc1,
- .scr_gper = 0,
- .gpio_base = -1,
- .suspend = &eseries_tmio_suspend,
- .resume = &eseries_tmio_resume,
- .enable = &eseries_tmio_enable,
- .disable = &eseries_tmio_disable,
-};
-
-static struct platform_device e750_tc6393xb_device = {
- .name = "tc6393xb",
- .id = -1,
- .dev = {
- .platform_data = &e750_tc6393xb_info,
- },
- .num_resources = 2,
- .resource = eseries_tmio_resources,
-};
-
-/* ------------------------------------------------------------- */
-
-static struct platform_device *devices[] __initdata = {
- &e750_fb_device,
- &e750_tc6393xb_device,
-};
-
-static void __init e750_init(void)
-{
- pxa2xx_mfp_config(ARRAY_AND_SIZE(e750_pin_config));
- pxa_set_ffuart_info(NULL);
- pxa_set_btuart_info(NULL);
- pxa_set_stuart_info(NULL);
- clk_add_alias("CLK_CK3P6MI", e750_tc6393xb_device.name,
- "GPIO11_CLK", NULL),
- eseries_get_tmio_gpios();
- platform_add_devices(devices, ARRAY_SIZE(devices));
- pxa_set_udc_info(&e7xx_udc_mach_info);
- pxa_set_ac97_info(NULL);
- pxa_set_ficp_info(&e7xx_ficp_platform_data);
-}
-
-MACHINE_START(E750, "Toshiba e750")
- /* Maintainer: Ian Molton (spyro@f2s.com) */
- .phys_io = 0x40000000,
- .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
- .boot_params = 0xa0000100,
- .map_io = pxa_map_io,
- .init_irq = pxa25x_init_irq,
- .fixup = eseries_fixup,
- .init_machine = e750_init,
- .timer = &pxa_timer,
-MACHINE_END
-
+++ /dev/null
-/*
- * Hardware definitions for the Toshiba eseries PDAs
- *
- * Copyright (c) 2003 Ian Molton <spyro@f2s.com>
- *
- * This file is licensed under
- * the terms of the GNU General Public License version 2. This program
- * is licensed "as is" without any warranty of any kind, whether express
- * or implied.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/fb.h>
-#include <linux/mfd/tc6393xb.h>
-
-#include <video/w100fb.h>
-
-#include <asm/setup.h>
-#include <asm/mach/arch.h>
-#include <asm/mach-types.h>
-
-#include <mach/pxa25x.h>
-#include <mach/eseries-gpio.h>
-#include <mach/udc.h>
-#include <mach/irqs.h>
-#include <mach/audio.h>
-
-#include "generic.h"
-#include "eseries.h"
-#include "clock.h"
-
-/* ------------------------ e800 LCD definitions ------------------------- */
-
-static unsigned long e800_pin_config[] __initdata = {
- /* AC97 */
- GPIO28_AC97_BITCLK,
- GPIO29_AC97_SDATA_IN_0,
- GPIO30_AC97_SDATA_OUT,
- GPIO31_AC97_SYNC,
-};
-
-static struct w100_gen_regs e800_lcd_regs = {
- .lcd_format = 0x00008003,
- .lcdd_cntl1 = 0x02a00000,
- .lcdd_cntl2 = 0x0003ffff,
- .genlcd_cntl1 = 0x000ff2a3,
- .genlcd_cntl2 = 0x000002a3,
- .genlcd_cntl3 = 0x000102aa,
-};
-
-static struct w100_mode e800_lcd_mode[2] = {
- [0] = {
- .xres = 480,
- .yres = 640,
- .left_margin = 52,
- .right_margin = 148,
- .upper_margin = 2,
- .lower_margin = 6,
- .crtc_ss = 0x80350034,
- .crtc_ls = 0x802b0026,
- .crtc_gs = 0x80160016,
- .crtc_vpos_gs = 0x00020003,
- .crtc_rev = 0x0040001d,
- .crtc_dclk = 0xe0000000,
- .crtc_gclk = 0x82a50049,
- .crtc_goe = 0x80ee001c,
- .crtc_ps1_active = 0x00000000,
- .pll_freq = 128,
- .pixclk_divider = 4,
- .pixclk_divider_rotated = 6,
- .pixclk_src = CLK_SRC_PLL,
- .sysclk_divider = 0,
- .sysclk_src = CLK_SRC_PLL,
- },
- [1] = {
- .xres = 240,
- .yres = 320,
- .left_margin = 15,
- .right_margin = 88,
- .upper_margin = 0,
- .lower_margin = 7,
- .crtc_ss = 0xd010000f,
- .crtc_ls = 0x80070003,
- .crtc_gs = 0x80000000,
- .crtc_vpos_gs = 0x01460147,
- .crtc_rev = 0x00400003,
- .crtc_dclk = 0xa1700030,
- .crtc_gclk = 0x814b0008,
- .crtc_goe = 0x80cc0015,
- .crtc_ps1_active = 0x00000000,
- .pll_freq = 100,
- .pixclk_divider = 6, /* Wince uses 14 which gives a */
- .pixclk_divider_rotated = 6, /* 7MHz Pclk. We use a 14MHz one */
- .pixclk_src = CLK_SRC_PLL,
- .sysclk_divider = 0,
- .sysclk_src = CLK_SRC_PLL,
- }
-};
-
-
-static struct w100_gpio_regs e800_w100_gpio_info = {
- .init_data1 = 0xc13fc019,
- .gpio_dir1 = 0x3e40df7f,
- .gpio_oe1 = 0x003c3000,
- .init_data2 = 0x00000000,
- .gpio_dir2 = 0x00000000,
- .gpio_oe2 = 0x00000000,
-};
-
-static struct w100_mem_info e800_w100_mem_info = {
- .ext_cntl = 0x09640011,
- .sdram_mode_reg = 0x00600021,
- .ext_timing_cntl = 0x10001545,
- .io_cntl = 0x7ddd7333,
- .size = 0x1fffff,
-};
-
-static void e800_tg_change(struct w100fb_par *par)
-{
- unsigned long tmp;
-
- tmp = w100fb_gpio_read(W100_GPIO_PORT_A);
- if (par->mode->xres == 480)
- tmp |= 0x100;
- else
- tmp &= ~0x100;
- w100fb_gpio_write(W100_GPIO_PORT_A, tmp);
-}
-
-static struct w100_tg_info e800_tg_info = {
- .change = e800_tg_change,
-};
-
-static struct w100fb_mach_info e800_fb_info = {
- .modelist = e800_lcd_mode,
- .num_modes = 2,
- .regs = &e800_lcd_regs,
- .gpio = &e800_w100_gpio_info,
- .mem = &e800_w100_mem_info,
- .tg = &e800_tg_info,
- .xtal_freq = 16000000,
-};
-
-static struct resource e800_fb_resources[] = {
- [0] = {
- .start = 0x0c000000,
- .end = 0x0cffffff,
- .flags = IORESOURCE_MEM,
- },
-};
-
-static struct platform_device e800_fb_device = {
- .name = "w100fb",
- .id = -1,
- .dev = {
- .platform_data = &e800_fb_info,
- },
- .num_resources = ARRAY_SIZE(e800_fb_resources),
- .resource = e800_fb_resources,
-};
-
-/* --------------------------- UDC definitions --------------------------- */
-
-static struct pxa2xx_udc_mach_info e800_udc_mach_info = {
- .gpio_vbus = GPIO_E800_USB_DISC,
- .gpio_pullup = GPIO_E800_USB_PULLUP,
- .gpio_pullup_inverted = 1
-};
-
-/* ----------------- e800 tc6393xb parameters ------------------ */
-
-static struct tc6393xb_platform_data e800_tc6393xb_info = {
- .irq_base = IRQ_BOARD_START,
- .scr_pll2cr = 0x0cc1,
- .scr_gper = 0,
- .gpio_base = -1,
- .suspend = &eseries_tmio_suspend,
- .resume = &eseries_tmio_resume,
- .enable = &eseries_tmio_enable,
- .disable = &eseries_tmio_disable,
-};
-
-static struct platform_device e800_tc6393xb_device = {
- .name = "tc6393xb",
- .id = -1,
- .dev = {
- .platform_data = &e800_tc6393xb_info,
- },
- .num_resources = 2,
- .resource = eseries_tmio_resources,
-};
-
-/* ----------------------------------------------------------------------- */
-
-static struct platform_device *devices[] __initdata = {
- &e800_fb_device,
- &e800_tc6393xb_device,
-};
-
-static void __init e800_init(void)
-{
- pxa2xx_mfp_config(ARRAY_AND_SIZE(e800_pin_config));
- pxa_set_ffuart_info(NULL);
- pxa_set_btuart_info(NULL);
- pxa_set_stuart_info(NULL);
- clk_add_alias("CLK_CK3P6MI", e800_tc6393xb_device.name,
- "GPIO11_CLK", NULL),
- eseries_get_tmio_gpios();
- platform_add_devices(devices, ARRAY_SIZE(devices));
- pxa_set_udc_info(&e800_udc_mach_info);
- pxa_set_ac97_info(NULL);
-}
-
-MACHINE_START(E800, "Toshiba e800")
- /* Maintainer: Ian Molton (spyro@f2s.com) */
- .phys_io = 0x40000000,
- .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
- .boot_params = 0xa0000100,
- .map_io = pxa_map_io,
- .init_irq = pxa25x_init_irq,
- .fixup = eseries_fixup,
- .init_machine = e800_init,
- .timer = &pxa_timer,
-MACHINE_END
-
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
+#include <linux/mfd/tc6387xb.h>
+#include <linux/mfd/tc6393xb.h>
+#include <linux/mfd/t7l66xb.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+#include <video/w100fb.h>
#include <asm/setup.h>
#include <asm/mach/arch.h>
#include <mach/pxa25x.h>
#include <mach/eseries-gpio.h>
+#include <mach/audio.h>
+#include <mach/pxafb.h>
#include <mach/udc.h>
#include <mach/irda.h>
+#include "devices.h"
#include "generic.h"
#include "clock.h"
clkdev_add_table(eseries_clkregs, ARRAY_SIZE(eseries_clkregs));
}
+#ifdef CONFIG_MACH_E330
+/* -------------------- e330 tc6387xb parameters -------------------- */
+
+static struct tc6387xb_platform_data e330_tc6387xb_info = {
+ .enable = &eseries_tmio_enable,
+ .disable = &eseries_tmio_disable,
+ .suspend = &eseries_tmio_suspend,
+ .resume = &eseries_tmio_resume,
+};
+
+static struct platform_device e330_tc6387xb_device = {
+ .name = "tc6387xb",
+ .id = -1,
+ .dev = {
+ .platform_data = &e330_tc6387xb_info,
+ },
+ .num_resources = 2,
+ .resource = eseries_tmio_resources,
+};
+
+/* --------------------------------------------------------------- */
+
+static struct platform_device *e330_devices[] __initdata = {
+ &e330_tc6387xb_device,
+};
+
+static void __init e330_init(void)
+{
+ pxa_set_ffuart_info(NULL);
+ pxa_set_btuart_info(NULL);
+ pxa_set_stuart_info(NULL);
+ eseries_register_clks();
+ eseries_get_tmio_gpios();
+ platform_add_devices(ARRAY_AND_SIZE(e330_devices));
+ pxa_set_udc_info(&e7xx_udc_mach_info);
+}
+
+MACHINE_START(E330, "Toshiba e330")
+ /* Maintainer: Ian Molton (spyro@f2s.com) */
+ .phys_io = 0x40000000,
+ .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
+ .boot_params = 0xa0000100,
+ .map_io = pxa_map_io,
+ .init_irq = pxa25x_init_irq,
+ .fixup = eseries_fixup,
+ .init_machine = e330_init,
+ .timer = &pxa_timer,
+MACHINE_END
+#endif
+
+#ifdef CONFIG_MACH_E350
+/* -------------------- e350 t7l66xb parameters -------------------- */
+
+static struct t7l66xb_platform_data e350_t7l66xb_info = {
+ .irq_base = IRQ_BOARD_START,
+ .enable = &eseries_tmio_enable,
+ .suspend = &eseries_tmio_suspend,
+ .resume = &eseries_tmio_resume,
+};
+
+static struct platform_device e350_t7l66xb_device = {
+ .name = "t7l66xb",
+ .id = -1,
+ .dev = {
+ .platform_data = &e350_t7l66xb_info,
+ },
+ .num_resources = 2,
+ .resource = eseries_tmio_resources,
+};
+
+/* ---------------------------------------------------------- */
+
+static struct platform_device *e350_devices[] __initdata = {
+ &e350_t7l66xb_device,
+};
+
+static void __init e350_init(void)
+{
+ pxa_set_ffuart_info(NULL);
+ pxa_set_btuart_info(NULL);
+ pxa_set_stuart_info(NULL);
+ eseries_register_clks();
+ eseries_get_tmio_gpios();
+ platform_add_devices(ARRAY_AND_SIZE(e350_devices));
+ pxa_set_udc_info(&e7xx_udc_mach_info);
+}
+
+MACHINE_START(E350, "Toshiba e350")
+ /* Maintainer: Ian Molton (spyro@f2s.com) */
+ .phys_io = 0x40000000,
+ .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
+ .boot_params = 0xa0000100,
+ .map_io = pxa_map_io,
+ .init_irq = pxa25x_init_irq,
+ .fixup = eseries_fixup,
+ .init_machine = e350_init,
+ .timer = &pxa_timer,
+MACHINE_END
+#endif
+
+#ifdef CONFIG_MACH_E400
+/* ------------------------ E400 LCD definitions ------------------------ */
+
+static struct pxafb_mode_info e400_pxafb_mode_info = {
+ .pixclock = 140703,
+ .xres = 240,
+ .yres = 320,
+ .bpp = 16,
+ .hsync_len = 4,
+ .left_margin = 28,
+ .right_margin = 8,
+ .vsync_len = 3,
+ .upper_margin = 5,
+ .lower_margin = 6,
+ .sync = 0,
+};
+
+static struct pxafb_mach_info e400_pxafb_mach_info = {
+ .modes = &e400_pxafb_mode_info,
+ .num_modes = 1,
+ .lcd_conn = LCD_COLOR_TFT_16BPP,
+ .lccr3 = 0,
+ .pxafb_backlight_power = NULL,
+};
+
+/* ------------------------ E400 MFP config ----------------------------- */
+
+static unsigned long e400_pin_config[] __initdata = {
+ /* Chip selects */
+ GPIO15_nCS_1, /* CS1 - Flash */
+ GPIO80_nCS_4, /* CS4 - TMIO */
+
+ /* Clocks */
+ GPIO12_32KHz,
+
+ /* BTUART */
+ GPIO42_BTUART_RXD,
+ GPIO43_BTUART_TXD,
+ GPIO44_BTUART_CTS,
+
+ /* TMIO controller */
+ GPIO19_GPIO, /* t7l66xb #PCLR */
+ GPIO45_GPIO, /* t7l66xb #SUSPEND (NOT BTUART!) */
+
+ /* wakeup */
+ GPIO0_GPIO | WAKEUP_ON_EDGE_RISE,
+};
+
+/* ---------------------------------------------------------------------- */
+
+static struct mtd_partition partition_a = {
+ .name = "Internal NAND flash",
+ .offset = 0,
+ .size = MTDPART_SIZ_FULL,
+};
+
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr e400_t7l66xb_nand_bbt = {
+ .options = 0,
+ .offs = 4,
+ .len = 2,
+ .pattern = scan_ff_pattern
+};
+
+static struct tmio_nand_data e400_t7l66xb_nand_config = {
+ .num_partitions = 1,
+ .partition = &partition_a,
+ .badblock_pattern = &e400_t7l66xb_nand_bbt,
+};
+
+static struct t7l66xb_platform_data e400_t7l66xb_info = {
+ .irq_base = IRQ_BOARD_START,
+ .enable = &eseries_tmio_enable,
+ .suspend = &eseries_tmio_suspend,
+ .resume = &eseries_tmio_resume,
+
+ .nand_data = &e400_t7l66xb_nand_config,
+};
+
+static struct platform_device e400_t7l66xb_device = {
+ .name = "t7l66xb",
+ .id = -1,
+ .dev = {
+ .platform_data = &e400_t7l66xb_info,
+ },
+ .num_resources = 2,
+ .resource = eseries_tmio_resources,
+};
+
+/* ---------------------------------------------------------- */
+
+static struct platform_device *e400_devices[] __initdata = {
+ &e400_t7l66xb_device,
+};
+
+static void __init e400_init(void)
+{
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(e400_pin_config));
+ pxa_set_ffuart_info(NULL);
+ pxa_set_btuart_info(NULL);
+ pxa_set_stuart_info(NULL);
+ /* Fixme - e400 may have a switched clock */
+ eseries_register_clks();
+ eseries_get_tmio_gpios();
+ set_pxa_fb_info(&e400_pxafb_mach_info);
+ platform_add_devices(ARRAY_AND_SIZE(e400_devices));
+ pxa_set_udc_info(&e7xx_udc_mach_info);
+}
+
+MACHINE_START(E400, "Toshiba e400")
+ /* Maintainer: Ian Molton (spyro@f2s.com) */
+ .phys_io = 0x40000000,
+ .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
+ .boot_params = 0xa0000100,
+ .map_io = pxa_map_io,
+ .init_irq = pxa25x_init_irq,
+ .fixup = eseries_fixup,
+ .init_machine = e400_init,
+ .timer = &pxa_timer,
+MACHINE_END
+#endif
+
+#ifdef CONFIG_MACH_E740
+/* ------------------------ e740 video support --------------------------- */
+
+static struct w100_gen_regs e740_lcd_regs = {
+ .lcd_format = 0x00008023,
+ .lcdd_cntl1 = 0x0f000000,
+ .lcdd_cntl2 = 0x0003ffff,
+ .genlcd_cntl1 = 0x00ffff03,
+ .genlcd_cntl2 = 0x003c0f03,
+ .genlcd_cntl3 = 0x000143aa,
+};
+
+static struct w100_mode e740_lcd_mode = {
+ .xres = 240,
+ .yres = 320,
+ .left_margin = 20,
+ .right_margin = 28,
+ .upper_margin = 9,
+ .lower_margin = 8,
+ .crtc_ss = 0x80140013,
+ .crtc_ls = 0x81150110,
+ .crtc_gs = 0x80050005,
+ .crtc_vpos_gs = 0x000a0009,
+ .crtc_rev = 0x0040010a,
+ .crtc_dclk = 0xa906000a,
+ .crtc_gclk = 0x80050108,
+ .crtc_goe = 0x80050108,
+ .pll_freq = 57,
+ .pixclk_divider = 4,
+ .pixclk_divider_rotated = 4,
+ .pixclk_src = CLK_SRC_XTAL,
+ .sysclk_divider = 1,
+ .sysclk_src = CLK_SRC_PLL,
+ .crtc_ps1_active = 0x41060010,
+};
+
+static struct w100_gpio_regs e740_w100_gpio_info = {
+ .init_data1 = 0x21002103,
+ .gpio_dir1 = 0xffffdeff,
+ .gpio_oe1 = 0x03c00643,
+ .init_data2 = 0x003f003f,
+ .gpio_dir2 = 0xffffffff,
+ .gpio_oe2 = 0x000000ff,
+};
+
+static struct w100fb_mach_info e740_fb_info = {
+ .modelist = &e740_lcd_mode,
+ .num_modes = 1,
+ .regs = &e740_lcd_regs,
+ .gpio = &e740_w100_gpio_info,
+ .xtal_freq = 14318000,
+ .xtal_dbl = 1,
+};
+
+static struct resource e740_fb_resources[] = {
+ [0] = {
+ .start = 0x0c000000,
+ .end = 0x0cffffff,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device e740_fb_device = {
+ .name = "w100fb",
+ .id = -1,
+ .dev = {
+ .platform_data = &e740_fb_info,
+ },
+ .num_resources = ARRAY_SIZE(e740_fb_resources),
+ .resource = e740_fb_resources,
+};
+
+/* --------------------------- MFP Pin config -------------------------- */
+
+static unsigned long e740_pin_config[] __initdata = {
+ /* Chip selects */
+ GPIO15_nCS_1, /* CS1 - Flash */
+ GPIO79_nCS_3, /* CS3 - IMAGEON */
+ GPIO80_nCS_4, /* CS4 - TMIO */
+
+ /* Clocks */
+ GPIO12_32KHz,
+
+ /* BTUART */
+ GPIO42_BTUART_RXD,
+ GPIO43_BTUART_TXD,
+ GPIO44_BTUART_CTS,
+
+ /* TMIO controller */
+ GPIO19_GPIO, /* t7l66xb #PCLR */
+ GPIO45_GPIO, /* t7l66xb #SUSPEND (NOT BTUART!) */
+
+ /* UDC */
+ GPIO13_GPIO,
+ GPIO3_GPIO,
+
+ /* IrDA */
+ GPIO38_GPIO | MFP_LPM_DRIVE_HIGH,
+
+ /* AC97 */
+ GPIO28_AC97_BITCLK,
+ GPIO29_AC97_SDATA_IN_0,
+ GPIO30_AC97_SDATA_OUT,
+ GPIO31_AC97_SYNC,
+
+ /* Audio power control */
+ GPIO16_GPIO, /* AC97 codec AVDD2 supply (analogue power) */
+ GPIO40_GPIO, /* Mic amp power */
+ GPIO41_GPIO, /* Headphone amp power */
+
+ /* PC Card */
+ GPIO8_GPIO, /* CD0 */
+ GPIO44_GPIO, /* CD1 */
+ GPIO11_GPIO, /* IRQ0 */
+ GPIO6_GPIO, /* IRQ1 */
+ GPIO27_GPIO, /* RST0 */
+ GPIO24_GPIO, /* RST1 */
+ GPIO20_GPIO, /* PWR0 */
+ GPIO23_GPIO, /* PWR1 */
+ GPIO48_nPOE,
+ GPIO49_nPWE,
+ GPIO50_nPIOR,
+ GPIO51_nPIOW,
+ GPIO52_nPCE_1,
+ GPIO53_nPCE_2,
+ GPIO54_nPSKTSEL,
+ GPIO55_nPREG,
+ GPIO56_nPWAIT,
+ GPIO57_nIOIS16,
+
+ /* wakeup */
+ GPIO0_GPIO | WAKEUP_ON_EDGE_RISE,
+};
+
+/* -------------------- e740 t7l66xb parameters -------------------- */
+
+static struct t7l66xb_platform_data e740_t7l66xb_info = {
+ .irq_base = IRQ_BOARD_START,
+ .enable = &eseries_tmio_enable,
+ .suspend = &eseries_tmio_suspend,
+ .resume = &eseries_tmio_resume,
+};
+
+static struct platform_device e740_t7l66xb_device = {
+ .name = "t7l66xb",
+ .id = -1,
+ .dev = {
+ .platform_data = &e740_t7l66xb_info,
+ },
+ .num_resources = 2,
+ .resource = eseries_tmio_resources,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static struct platform_device *e740_devices[] __initdata = {
+ &e740_fb_device,
+ &e740_t7l66xb_device,
+};
+
+static void __init e740_init(void)
+{
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(e740_pin_config));
+ pxa_set_ffuart_info(NULL);
+ pxa_set_btuart_info(NULL);
+ pxa_set_stuart_info(NULL);
+ eseries_register_clks();
+ clk_add_alias("CLK_CK48M", e740_t7l66xb_device.name,
+ "UDCCLK", &pxa25x_device_udc.dev),
+ eseries_get_tmio_gpios();
+ platform_add_devices(ARRAY_AND_SIZE(e740_devices));
+ pxa_set_udc_info(&e7xx_udc_mach_info);
+ pxa_set_ac97_info(NULL);
+ pxa_set_ficp_info(&e7xx_ficp_platform_data);
+}
+
+MACHINE_START(E740, "Toshiba e740")
+ /* Maintainer: Ian Molton (spyro@f2s.com) */
+ .phys_io = 0x40000000,
+ .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
+ .boot_params = 0xa0000100,
+ .map_io = pxa_map_io,
+ .init_irq = pxa25x_init_irq,
+ .fixup = eseries_fixup,
+ .init_machine = e740_init,
+ .timer = &pxa_timer,
+MACHINE_END
+#endif
+
+#ifdef CONFIG_MACH_E750
+/* ---------------------- E750 LCD definitions -------------------- */
+
+static struct w100_gen_regs e750_lcd_regs = {
+ .lcd_format = 0x00008003,
+ .lcdd_cntl1 = 0x00000000,
+ .lcdd_cntl2 = 0x0003ffff,
+ .genlcd_cntl1 = 0x00fff003,
+ .genlcd_cntl2 = 0x003c0f03,
+ .genlcd_cntl3 = 0x000143aa,
+};
+
+static struct w100_mode e750_lcd_mode = {
+ .xres = 240,
+ .yres = 320,
+ .left_margin = 21,
+ .right_margin = 22,
+ .upper_margin = 5,
+ .lower_margin = 4,
+ .crtc_ss = 0x80150014,
+ .crtc_ls = 0x8014000d,
+ .crtc_gs = 0xc1000005,
+ .crtc_vpos_gs = 0x00020147,
+ .crtc_rev = 0x0040010a,
+ .crtc_dclk = 0xa1700030,
+ .crtc_gclk = 0x80cc0015,
+ .crtc_goe = 0x80cc0015,
+ .crtc_ps1_active = 0x61060017,
+ .pll_freq = 57,
+ .pixclk_divider = 4,
+ .pixclk_divider_rotated = 4,
+ .pixclk_src = CLK_SRC_XTAL,
+ .sysclk_divider = 1,
+ .sysclk_src = CLK_SRC_PLL,
+};
+
+static struct w100_gpio_regs e750_w100_gpio_info = {
+ .init_data1 = 0x01192f1b,
+ .gpio_dir1 = 0xd5ffdeff,
+ .gpio_oe1 = 0x000020bf,
+ .init_data2 = 0x010f010f,
+ .gpio_dir2 = 0xffffffff,
+ .gpio_oe2 = 0x000001cf,
+};
+
+static struct w100fb_mach_info e750_fb_info = {
+ .modelist = &e750_lcd_mode,
+ .num_modes = 1,
+ .regs = &e750_lcd_regs,
+ .gpio = &e750_w100_gpio_info,
+ .xtal_freq = 14318000,
+ .xtal_dbl = 1,
+};
+
+static struct resource e750_fb_resources[] = {
+ [0] = {
+ .start = 0x0c000000,
+ .end = 0x0cffffff,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device e750_fb_device = {
+ .name = "w100fb",
+ .id = -1,
+ .dev = {
+ .platform_data = &e750_fb_info,
+ },
+ .num_resources = ARRAY_SIZE(e750_fb_resources),
+ .resource = e750_fb_resources,
+};
+
+/* -------------------- e750 MFP parameters -------------------- */
+
+static unsigned long e750_pin_config[] __initdata = {
+ /* Chip selects */
+ GPIO15_nCS_1, /* CS1 - Flash */
+ GPIO79_nCS_3, /* CS3 - IMAGEON */
+ GPIO80_nCS_4, /* CS4 - TMIO */
+
+ /* Clocks */
+ GPIO11_3_6MHz,
+
+ /* BTUART */
+ GPIO42_BTUART_RXD,
+ GPIO43_BTUART_TXD,
+ GPIO44_BTUART_CTS,
+
+ /* TMIO controller */
+ GPIO19_GPIO, /* t7l66xb #PCLR */
+ GPIO45_GPIO, /* t7l66xb #SUSPEND (NOT BTUART!) */
+
+ /* UDC */
+ GPIO13_GPIO,
+ GPIO3_GPIO,
+
+ /* IrDA */
+ GPIO38_GPIO | MFP_LPM_DRIVE_HIGH,
+
+ /* AC97 */
+ GPIO28_AC97_BITCLK,
+ GPIO29_AC97_SDATA_IN_0,
+ GPIO30_AC97_SDATA_OUT,
+ GPIO31_AC97_SYNC,
+
+ /* Audio power control */
+ GPIO4_GPIO, /* Headphone amp power */
+ GPIO7_GPIO, /* Speaker amp power */
+ GPIO37_GPIO, /* Headphone detect */
+
+ /* PC Card */
+ GPIO8_GPIO, /* CD0 */
+ GPIO44_GPIO, /* CD1 */
+ GPIO11_GPIO, /* IRQ0 */
+ GPIO6_GPIO, /* IRQ1 */
+ GPIO27_GPIO, /* RST0 */
+ GPIO24_GPIO, /* RST1 */
+ GPIO20_GPIO, /* PWR0 */
+ GPIO23_GPIO, /* PWR1 */
+ GPIO48_nPOE,
+ GPIO49_nPWE,
+ GPIO50_nPIOR,
+ GPIO51_nPIOW,
+ GPIO52_nPCE_1,
+ GPIO53_nPCE_2,
+ GPIO54_nPSKTSEL,
+ GPIO55_nPREG,
+ GPIO56_nPWAIT,
+ GPIO57_nIOIS16,
+
+ /* wakeup */
+ GPIO0_GPIO | WAKEUP_ON_EDGE_RISE,
+};
+
+/* ----------------- e750 tc6393xb parameters ------------------ */
+
+static struct tc6393xb_platform_data e750_tc6393xb_info = {
+ .irq_base = IRQ_BOARD_START,
+ .scr_pll2cr = 0x0cc1,
+ .scr_gper = 0,
+ .gpio_base = -1,
+ .suspend = &eseries_tmio_suspend,
+ .resume = &eseries_tmio_resume,
+ .enable = &eseries_tmio_enable,
+ .disable = &eseries_tmio_disable,
+};
+
+static struct platform_device e750_tc6393xb_device = {
+ .name = "tc6393xb",
+ .id = -1,
+ .dev = {
+ .platform_data = &e750_tc6393xb_info,
+ },
+ .num_resources = 2,
+ .resource = eseries_tmio_resources,
+};
+
+/* ------------------------------------------------------------- */
+
+static struct platform_device *e750_devices[] __initdata = {
+ &e750_fb_device,
+ &e750_tc6393xb_device,
+};
+
+static void __init e750_init(void)
+{
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(e750_pin_config));
+ pxa_set_ffuart_info(NULL);
+ pxa_set_btuart_info(NULL);
+ pxa_set_stuart_info(NULL);
+ clk_add_alias("CLK_CK3P6MI", e750_tc6393xb_device.name,
+ "GPIO11_CLK", NULL),
+ eseries_get_tmio_gpios();
+ platform_add_devices(ARRAY_AND_SIZE(e750_devices));
+ pxa_set_udc_info(&e7xx_udc_mach_info);
+ pxa_set_ac97_info(NULL);
+ pxa_set_ficp_info(&e7xx_ficp_platform_data);
+}
+
+MACHINE_START(E750, "Toshiba e750")
+ /* Maintainer: Ian Molton (spyro@f2s.com) */
+ .phys_io = 0x40000000,
+ .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
+ .boot_params = 0xa0000100,
+ .map_io = pxa_map_io,
+ .init_irq = pxa25x_init_irq,
+ .fixup = eseries_fixup,
+ .init_machine = e750_init,
+ .timer = &pxa_timer,
+MACHINE_END
+#endif
+
+#ifdef CONFIG_MACH_E800
+/* ------------------------ e800 LCD definitions ------------------------- */
+
+static unsigned long e800_pin_config[] __initdata = {
+ /* AC97 */
+ GPIO28_AC97_BITCLK,
+ GPIO29_AC97_SDATA_IN_0,
+ GPIO30_AC97_SDATA_OUT,
+ GPIO31_AC97_SYNC,
+};
+
+static struct w100_gen_regs e800_lcd_regs = {
+ .lcd_format = 0x00008003,
+ .lcdd_cntl1 = 0x02a00000,
+ .lcdd_cntl2 = 0x0003ffff,
+ .genlcd_cntl1 = 0x000ff2a3,
+ .genlcd_cntl2 = 0x000002a3,
+ .genlcd_cntl3 = 0x000102aa,
+};
+
+static struct w100_mode e800_lcd_mode[2] = {
+ [0] = {
+ .xres = 480,
+ .yres = 640,
+ .left_margin = 52,
+ .right_margin = 148,
+ .upper_margin = 2,
+ .lower_margin = 6,
+ .crtc_ss = 0x80350034,
+ .crtc_ls = 0x802b0026,
+ .crtc_gs = 0x80160016,
+ .crtc_vpos_gs = 0x00020003,
+ .crtc_rev = 0x0040001d,
+ .crtc_dclk = 0xe0000000,
+ .crtc_gclk = 0x82a50049,
+ .crtc_goe = 0x80ee001c,
+ .crtc_ps1_active = 0x00000000,
+ .pll_freq = 128,
+ .pixclk_divider = 4,
+ .pixclk_divider_rotated = 6,
+ .pixclk_src = CLK_SRC_PLL,
+ .sysclk_divider = 0,
+ .sysclk_src = CLK_SRC_PLL,
+ },
+ [1] = {
+ .xres = 240,
+ .yres = 320,
+ .left_margin = 15,
+ .right_margin = 88,
+ .upper_margin = 0,
+ .lower_margin = 7,
+ .crtc_ss = 0xd010000f,
+ .crtc_ls = 0x80070003,
+ .crtc_gs = 0x80000000,
+ .crtc_vpos_gs = 0x01460147,
+ .crtc_rev = 0x00400003,
+ .crtc_dclk = 0xa1700030,
+ .crtc_gclk = 0x814b0008,
+ .crtc_goe = 0x80cc0015,
+ .crtc_ps1_active = 0x00000000,
+ .pll_freq = 100,
+ .pixclk_divider = 6, /* Wince uses 14 which gives a */
+ .pixclk_divider_rotated = 6, /* 7MHz Pclk. We use a 14MHz one */
+ .pixclk_src = CLK_SRC_PLL,
+ .sysclk_divider = 0,
+ .sysclk_src = CLK_SRC_PLL,
+ }
+};
+
+
+static struct w100_gpio_regs e800_w100_gpio_info = {
+ .init_data1 = 0xc13fc019,
+ .gpio_dir1 = 0x3e40df7f,
+ .gpio_oe1 = 0x003c3000,
+ .init_data2 = 0x00000000,
+ .gpio_dir2 = 0x00000000,
+ .gpio_oe2 = 0x00000000,
+};
+
+static struct w100_mem_info e800_w100_mem_info = {
+ .ext_cntl = 0x09640011,
+ .sdram_mode_reg = 0x00600021,
+ .ext_timing_cntl = 0x10001545,
+ .io_cntl = 0x7ddd7333,
+ .size = 0x1fffff,
+};
+
+static void e800_tg_change(struct w100fb_par *par)
+{
+ unsigned long tmp;
+
+ tmp = w100fb_gpio_read(W100_GPIO_PORT_A);
+ if (par->mode->xres == 480)
+ tmp |= 0x100;
+ else
+ tmp &= ~0x100;
+ w100fb_gpio_write(W100_GPIO_PORT_A, tmp);
+}
+
+static struct w100_tg_info e800_tg_info = {
+ .change = e800_tg_change,
+};
+
+static struct w100fb_mach_info e800_fb_info = {
+ .modelist = e800_lcd_mode,
+ .num_modes = 2,
+ .regs = &e800_lcd_regs,
+ .gpio = &e800_w100_gpio_info,
+ .mem = &e800_w100_mem_info,
+ .tg = &e800_tg_info,
+ .xtal_freq = 16000000,
+};
+
+static struct resource e800_fb_resources[] = {
+ [0] = {
+ .start = 0x0c000000,
+ .end = 0x0cffffff,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device e800_fb_device = {
+ .name = "w100fb",
+ .id = -1,
+ .dev = {
+ .platform_data = &e800_fb_info,
+ },
+ .num_resources = ARRAY_SIZE(e800_fb_resources),
+ .resource = e800_fb_resources,
+};
+
+/* --------------------------- UDC definitions --------------------------- */
+
+static struct pxa2xx_udc_mach_info e800_udc_mach_info = {
+ .gpio_vbus = GPIO_E800_USB_DISC,
+ .gpio_pullup = GPIO_E800_USB_PULLUP,
+ .gpio_pullup_inverted = 1
+};
+
+/* ----------------- e800 tc6393xb parameters ------------------ */
+
+static struct tc6393xb_platform_data e800_tc6393xb_info = {
+ .irq_base = IRQ_BOARD_START,
+ .scr_pll2cr = 0x0cc1,
+ .scr_gper = 0,
+ .gpio_base = -1,
+ .suspend = &eseries_tmio_suspend,
+ .resume = &eseries_tmio_resume,
+ .enable = &eseries_tmio_enable,
+ .disable = &eseries_tmio_disable,
+};
+
+static struct platform_device e800_tc6393xb_device = {
+ .name = "tc6393xb",
+ .id = -1,
+ .dev = {
+ .platform_data = &e800_tc6393xb_info,
+ },
+ .num_resources = 2,
+ .resource = eseries_tmio_resources,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static struct platform_device *e800_devices[] __initdata = {
+ &e800_fb_device,
+ &e800_tc6393xb_device,
+};
+
+static void __init e800_init(void)
+{
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(e800_pin_config));
+ pxa_set_ffuart_info(NULL);
+ pxa_set_btuart_info(NULL);
+ pxa_set_stuart_info(NULL);
+ clk_add_alias("CLK_CK3P6MI", e800_tc6393xb_device.name,
+ "GPIO11_CLK", NULL),
+ eseries_get_tmio_gpios();
+ platform_add_devices(ARRAY_AND_SIZE(e800_devices));
+ pxa_set_udc_info(&e800_udc_mach_info);
+ pxa_set_ac97_info(NULL);
+}
+
+MACHINE_START(E800, "Toshiba e800")
+ /* Maintainer: Ian Molton (spyro@f2s.com) */
+ .phys_io = 0x40000000,
+ .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
+ .boot_params = 0xa0000100,
+ .map_io = pxa_map_io,
+ .init_irq = pxa25x_init_irq,
+ .fixup = eseries_fixup,
+ .init_machine = e800_init,
+ .timer = &pxa_timer,
+MACHINE_END
+#endif
+++ /dev/null
-/*
- * linux/arch/arm/mach-pxa/imote2.c
- *
- * Author: Ed C. Epp
- * Created: Nov 05, 2002
- * Copyright: Intel Corp.
- *
- * Modified 2008: Jonathan Cameron
- *
- * The Imote2 is a wireless sensor node platform sold
- * by Crossbow (www.xbow.com).
- */
-
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/machine.h>
-#include <linux/gpio.h>
-#include <linux/leds.h>
-#include <linux/spi/spi.h>
-#include <linux/i2c.h>
-#include <linux/mfd/da903x.h>
-#include <linux/sht15.h>
-
-#include <asm/mach-types.h>
-#include <asm/mach/arch.h>
-#include <asm/mach/map.h>
-#include <asm/mach/flash.h>
-
-#include <mach/pxa27x.h>
-#include <plat/i2c.h>
-#include <mach/udc.h>
-#include <mach/mmc.h>
-#include <mach/pxa2xx_spi.h>
-#include <mach/pxa27x-udc.h>
-
-#include "devices.h"
-#include "generic.h"
-
-static unsigned long imote2_pin_config[] __initdata = {
-
- /* Device Identification for wakeup*/
- GPIO102_GPIO,
-
- /* Button */
- GPIO91_GPIO,
-
- /* DA9030 */
- GPIO1_GPIO,
-
- /* MMC */
- GPIO32_MMC_CLK,
- GPIO112_MMC_CMD,
- GPIO92_MMC_DAT_0,
- GPIO109_MMC_DAT_1,
- GPIO110_MMC_DAT_2,
- GPIO111_MMC_DAT_3,
-
- /* 802.15.4 radio - driver out of mainline */
- GPIO22_GPIO, /* CC_RSTN */
- GPIO114_GPIO, /* CC_FIFO */
- GPIO116_GPIO, /* CC_CCA */
- GPIO0_GPIO, /* CC_FIFOP */
- GPIO16_GPIO, /* CCSFD */
- GPIO115_GPIO, /* Power enable */
-
- /* I2C */
- GPIO117_I2C_SCL,
- GPIO118_I2C_SDA,
-
- /* SSP 3 - 802.15.4 radio */
- GPIO39_GPIO, /* Chip Select */
- GPIO34_SSP3_SCLK,
- GPIO35_SSP3_TXD,
- GPIO41_SSP3_RXD,
-
- /* SSP 2 - to daughter boards */
- GPIO37_GPIO, /* Chip Select */
- GPIO36_SSP2_SCLK,
- GPIO38_SSP2_TXD,
- GPIO11_SSP2_RXD,
-
- /* SSP 1 - to daughter boards */
- GPIO24_GPIO, /* Chip Select */
- GPIO23_SSP1_SCLK,
- GPIO25_SSP1_TXD,
- GPIO26_SSP1_RXD,
-
- /* BTUART Basic Connector*/
- GPIO42_BTUART_RXD,
- GPIO43_BTUART_TXD,
- GPIO44_BTUART_CTS,
- GPIO45_BTUART_RTS,
-
- /* STUART Serial console via debug board*/
- GPIO46_STUART_RXD,
- GPIO47_STUART_TXD,
-
- /* Basic sensor board */
- GPIO96_GPIO, /* accelerometer interrupt */
- GPIO99_GPIO, /* ADC interrupt */
-
- /* SHT15 */
- GPIO100_GPIO,
- GPIO98_GPIO,
-
- /* Connector pins specified as gpios */
- GPIO94_GPIO, /* large basic connector pin 14 */
- GPIO10_GPIO, /* large basic connector pin 23 */
-
- /* LEDS */
- GPIO103_GPIO, /* red led */
- GPIO104_GPIO, /* green led */
- GPIO105_GPIO, /* blue led */
-};
-
-static struct sht15_platform_data platform_data_sht15 = {
- .gpio_data = 100,
- .gpio_sck = 98,
-};
-
-static struct platform_device sht15 = {
- .name = "sht15",
- .id = -1,
- .dev = {
- .platform_data = &platform_data_sht15,
- },
-};
-
-static struct regulator_consumer_supply imote2_sensor_3_con[] = {
- {
- .dev = &sht15.dev,
- .supply = "vcc",
- },
-};
-
-static struct gpio_led imote2_led_pins[] = {
- {
- .name = "imote2:red",
- .gpio = 103,
- .active_low = 1,
- }, {
- .name = "imote2:green",
- .gpio = 104,
- .active_low = 1,
- }, {
- .name = "imote2:blue",
- .gpio = 105,
- .active_low = 1,
- },
-};
-
-static struct gpio_led_platform_data imote2_led_data = {
- .num_leds = ARRAY_SIZE(imote2_led_pins),
- .leds = imote2_led_pins,
-};
-
-static struct platform_device imote2_leds = {
- .name = "leds-gpio",
- .id = -1,
- .dev = {
- .platform_data = &imote2_led_data,
- },
-};
-
-/* Reverse engineered partly from Platformx drivers */
-enum imote2_ldos{
- vcc_vref,
- vcc_cc2420,
- vcc_mica,
- vcc_bt,
- /* The two voltages available to sensor boards */
- vcc_sensor_1_8,
- vcc_sensor_3,
-
- vcc_sram_ext, /* directly connected to the pxa271 */
- vcc_pxa_pll,
- vcc_pxa_usim, /* Reference voltage for certain gpios */
- vcc_pxa_mem,
- vcc_pxa_flash,
- vcc_pxa_core, /*Dc-Dc buck not yet supported */
- vcc_lcd,
- vcc_bb,
- vcc_bbio,
- vcc_io, /* cc2420 802.15.4 radio and pxa vcc_io ?*/
-};
-
-/* The values of the various regulator constraints are obviously dependent
- * on exactly what is wired to each ldo. Unfortunately this information is
- * not generally available. More information has been requested from Xbow
- * but as of yet they haven't been forthcoming.
- *
- * Some of these are clearly Stargate 2 related (no way of plugging
- * in an lcd on the IM2 for example!).
- */
-static struct regulator_init_data imote2_ldo_init_data[] = {
- [vcc_bbio] = {
- .constraints = { /* board default 1.8V */
- .name = "vcc_bbio",
- .min_uV = 1800000,
- .max_uV = 1800000,
- },
- },
- [vcc_bb] = {
- .constraints = { /* board default 2.8V */
- .name = "vcc_bb",
- .min_uV = 2700000,
- .max_uV = 3000000,
- },
- },
- [vcc_pxa_flash] = {
- .constraints = {/* default is 1.8V */
- .name = "vcc_pxa_flash",
- .min_uV = 1800000,
- .max_uV = 1800000,
- },
- },
- [vcc_cc2420] = { /* also vcc_io */
- .constraints = {
- /* board default is 2.8V */
- .name = "vcc_cc2420",
- .min_uV = 2700000,
- .max_uV = 3300000,
- },
- },
- [vcc_vref] = { /* Reference for what? */
- .constraints = { /* default 1.8V */
- .name = "vcc_vref",
- .min_uV = 1800000,
- .max_uV = 1800000,
- },
- },
- [vcc_sram_ext] = {
- .constraints = { /* default 2.8V */
- .name = "vcc_sram_ext",
- .min_uV = 2800000,
- .max_uV = 2800000,
- },
- },
- [vcc_mica] = {
- .constraints = { /* default 2.8V */
- .name = "vcc_mica",
- .min_uV = 2800000,
- .max_uV = 2800000,
- },
- },
- [vcc_bt] = {
- .constraints = { /* default 2.8V */
- .name = "vcc_bt",
- .min_uV = 2800000,
- .max_uV = 2800000,
- },
- },
- [vcc_lcd] = {
- .constraints = { /* default 2.8V */
- .name = "vcc_lcd",
- .min_uV = 2700000,
- .max_uV = 3300000,
- },
- },
- [vcc_io] = { /* Same or higher than everything
- * bar vccbat and vccusb */
- .constraints = { /* default 2.8V */
- .name = "vcc_io",
- .min_uV = 2692000,
- .max_uV = 3300000,
- },
- },
- [vcc_sensor_1_8] = {
- .constraints = { /* default 1.8V */
- .name = "vcc_sensor_1_8",
- .min_uV = 1800000,
- .max_uV = 1800000,
- },
- },
- [vcc_sensor_3] = { /* curiously default 2.8V */
- .constraints = {
- .name = "vcc_sensor_3",
- .min_uV = 2800000,
- .max_uV = 3000000,
- },
- .num_consumer_supplies = ARRAY_SIZE(imote2_sensor_3_con),
- .consumer_supplies = imote2_sensor_3_con,
- },
- [vcc_pxa_pll] = { /* 1.17V - 1.43V, default 1.3V*/
- .constraints = {
- .name = "vcc_pxa_pll",
- .min_uV = 1170000,
- .max_uV = 1430000,
- },
- },
- [vcc_pxa_usim] = {
- .constraints = { /* default 1.8V */
- .name = "vcc_pxa_usim",
- .min_uV = 1710000,
- .max_uV = 2160000,
- },
- },
- [vcc_pxa_mem] = {
- .constraints = { /* default 1.8V */
- .name = "vcc_pxa_mem",
- .min_uV = 1800000,
- .max_uV = 1800000,
- },
- },
-};
-
-static struct da903x_subdev_info imote2_da9030_subdevs[] = {
- {
- .name = "da903x-regulator",
- .id = DA9030_ID_LDO2,
- .platform_data = &imote2_ldo_init_data[vcc_bbio],
- }, {
- .name = "da903x-regulator",
- .id = DA9030_ID_LDO3,
- .platform_data = &imote2_ldo_init_data[vcc_bb],
- }, {
- .name = "da903x-regulator",
- .id = DA9030_ID_LDO4,
- .platform_data = &imote2_ldo_init_data[vcc_pxa_flash],
- }, {
- .name = "da903x-regulator",
- .id = DA9030_ID_LDO5,
- .platform_data = &imote2_ldo_init_data[vcc_cc2420],
- }, {
- .name = "da903x-regulator",
- .id = DA9030_ID_LDO6,
- .platform_data = &imote2_ldo_init_data[vcc_vref],
- }, {
- .name = "da903x-regulator",
- .id = DA9030_ID_LDO7,
- .platform_data = &imote2_ldo_init_data[vcc_sram_ext],
- }, {
- .name = "da903x-regulator",
- .id = DA9030_ID_LDO8,
- .platform_data = &imote2_ldo_init_data[vcc_mica],
- }, {
- .name = "da903x-regulator",
- .id = DA9030_ID_LDO9,
- .platform_data = &imote2_ldo_init_data[vcc_bt],
- }, {
- .name = "da903x-regulator",
- .id = DA9030_ID_LDO10,
- .platform_data = &imote2_ldo_init_data[vcc_sensor_1_8],
- }, {
- .name = "da903x-regulator",
- .id = DA9030_ID_LDO11,
- .platform_data = &imote2_ldo_init_data[vcc_sensor_3],
- }, {
- .name = "da903x-regulator",
- .id = DA9030_ID_LDO12,
- .platform_data = &imote2_ldo_init_data[vcc_lcd],
- }, {
- .name = "da903x-regulator",
- .id = DA9030_ID_LDO15,
- .platform_data = &imote2_ldo_init_data[vcc_pxa_pll],
- }, {
- .name = "da903x-regulator",
- .id = DA9030_ID_LDO17,
- .platform_data = &imote2_ldo_init_data[vcc_pxa_usim],
- }, {
- .name = "da903x-regulator",
- .id = DA9030_ID_LDO18,
- .platform_data = &imote2_ldo_init_data[vcc_io],
- }, {
- .name = "da903x-regulator",
- .id = DA9030_ID_LDO19,
- .platform_data = &imote2_ldo_init_data[vcc_pxa_mem],
- },
-};
-
-static struct da903x_platform_data imote2_da9030_pdata = {
- .num_subdevs = ARRAY_SIZE(imote2_da9030_subdevs),
- .subdevs = imote2_da9030_subdevs,
-};
-
-/* As the the imote2 doesn't currently have a conventional SD slot
- * there is no option to hotplug cards, making all this rather simple
- */
-static int imote2_mci_get_ro(struct device *dev)
-{
- return 0;
-}
-
-/* Rather simple case as hotplugging not possible */
-static struct pxamci_platform_data imote2_mci_platform_data = {
- .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, /* default anyway */
- .get_ro = imote2_mci_get_ro,
- .gpio_card_detect = -1,
- .gpio_card_ro = -1,
- .gpio_power = -1,
-};
-
-static struct mtd_partition imote2flash_partitions[] = {
- {
- .name = "Bootloader",
- .size = 0x00040000,
- .offset = 0,
- .mask_flags = MTD_WRITEABLE,
- }, {
- .name = "Kernel",
- .size = 0x00200000,
- .offset = 0x00040000,
- .mask_flags = 0,
- }, {
- .name = "Filesystem",
- .size = 0x01DC0000,
- .offset = 0x00240000,
- .mask_flags = 0,
- },
-};
-
-static struct resource flash_resources = {
- .start = PXA_CS0_PHYS,
- .end = PXA_CS0_PHYS + SZ_32M - 1,
- .flags = IORESOURCE_MEM,
-};
-
-static struct flash_platform_data imote2_flash_data = {
- .map_name = "cfi_probe",
- .parts = imote2flash_partitions,
- .nr_parts = ARRAY_SIZE(imote2flash_partitions),
- .name = "PXA27xOnChipROM",
- .width = 2,
-};
-
-static struct platform_device imote2_flash_device = {
- .name = "pxa2xx-flash",
- .id = 0,
- .dev = {
- .platform_data = &imote2_flash_data,
- },
- .resource = &flash_resources,
- .num_resources = 1,
-};
-
-/* Some of the drivers here are out of kernel at the moment (parts of IIO)
- * and it may be a while before they are in the mainline.
- */
-static struct i2c_board_info __initdata imote2_i2c_board_info[] = {
- { /* UCAM sensor board */
- .type = "max1239",
- .addr = 0x35,
- }, { /* ITS400 Sensor board only */
- .type = "max1363",
- .addr = 0x34,
- /* Through a nand gate - Also beware, on V2 sensor board the
- * pull up resistors are missing.
- */
- .irq = IRQ_GPIO(99),
- }, { /* ITS400 Sensor board only */
- .type = "tsl2561",
- .addr = 0x49,
- /* Through a nand gate - Also beware, on V2 sensor board the
- * pull up resistors are missing.
- */
- .irq = IRQ_GPIO(99),
- }, { /* ITS400 Sensor board only */
- .type = "tmp175",
- .addr = 0x4A,
- .irq = IRQ_GPIO(96),
- }, { /* IMB400 Multimedia board */
- .type = "wm8940",
- .addr = 0x1A,
- },
-};
-
-static struct i2c_board_info __initdata imote2_pwr_i2c_board_info[] = {
- {
- .type = "da9030",
- .addr = 0x49,
- .platform_data = &imote2_da9030_pdata,
- .irq = gpio_to_irq(1),
- },
-};
-
-static struct pxa2xx_spi_master pxa_ssp_master_0_info = {
- .num_chipselect = 1,
-};
-
-static struct pxa2xx_spi_master pxa_ssp_master_1_info = {
- .num_chipselect = 1,
-};
-
-static struct pxa2xx_spi_master pxa_ssp_master_2_info = {
- .num_chipselect = 1,
-};
-
-static struct pxa2xx_spi_chip staccel_chip_info = {
- .tx_threshold = 8,
- .rx_threshold = 8,
- .dma_burst_size = 8,
- .timeout = 235,
- .gpio_cs = 24,
-};
-
-static struct pxa2xx_spi_chip cc2420_info = {
- .tx_threshold = 8,
- .rx_threshold = 8,
- .dma_burst_size = 8,
- .timeout = 235,
- .gpio_cs = 39,
-};
-
-static struct spi_board_info spi_board_info[] __initdata = {
- { /* Driver in IIO */
- .modalias = "lis3l02dq",
- .max_speed_hz = 8000000,/* 8MHz max spi frequency at 3V */
- .bus_num = 1,
- .chip_select = 0,
- .controller_data = &staccel_chip_info,
- .irq = IRQ_GPIO(96),
- }, { /* Driver out of kernel as it needs considerable rewriting */
- .modalias = "cc2420",
- .max_speed_hz = 6500000,
- .bus_num = 3,
- .chip_select = 0,
- .controller_data = &cc2420_info,
- },
-};
-
-static void im2_udc_command(int cmd)
-{
- switch (cmd) {
- case PXA2XX_UDC_CMD_CONNECT:
- UP2OCR |= UP2OCR_HXOE | UP2OCR_DPPUE | UP2OCR_DPPUBE;
- break;
- case PXA2XX_UDC_CMD_DISCONNECT:
- UP2OCR &= ~(UP2OCR_HXOE | UP2OCR_DPPUE | UP2OCR_DPPUBE);
- break;
- }
-}
-
-static struct pxa2xx_udc_mach_info imote2_udc_info __initdata = {
- .udc_command = im2_udc_command,
-};
-
-static struct platform_device *imote2_devices[] = {
- &imote2_flash_device,
- &imote2_leds,
- &sht15,
-};
-
-static struct i2c_pxa_platform_data i2c_pwr_pdata = {
- .fast_mode = 1,
-};
-
-static struct i2c_pxa_platform_data i2c_pdata = {
- .fast_mode = 1,
-};
-
-static void __init imote2_init(void)
-{
- pxa2xx_mfp_config(ARRAY_AND_SIZE(imote2_pin_config));
-
- pxa_set_ffuart_info(NULL);
- pxa_set_btuart_info(NULL);
- pxa_set_stuart_info(NULL);
-
- platform_add_devices(imote2_devices, ARRAY_SIZE(imote2_devices));
-
- pxa2xx_set_spi_info(1, &pxa_ssp_master_0_info);
- pxa2xx_set_spi_info(2, &pxa_ssp_master_1_info);
- pxa2xx_set_spi_info(3, &pxa_ssp_master_2_info);
-
- spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
-
- i2c_register_board_info(0, imote2_i2c_board_info,
- ARRAY_SIZE(imote2_i2c_board_info));
- i2c_register_board_info(1, imote2_pwr_i2c_board_info,
- ARRAY_SIZE(imote2_pwr_i2c_board_info));
-
- pxa27x_set_i2c_power_info(&i2c_pwr_pdata);
- pxa_set_i2c_info(&i2c_pdata);
-
- pxa_set_mci_info(&imote2_mci_platform_data);
- pxa_set_udc_info(&imote2_udc_info);
-}
-
-MACHINE_START(INTELMOTE2, "IMOTE 2")
- .phys_io = 0x40000000,
- .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
- .map_io = pxa_map_io,
- .init_irq = pxa27x_init_irq,
- .timer = &pxa_timer,
- .init_machine = imote2_init,
- .boot_params = 0xA0000100,
-MACHINE_END
#define BALLOON3_FPGA_VIRT (0xf1000000) /* as per balloon2 */
#define BALLOON3_FPGA_LENGTH 0x01000000
-/* FPGA/CPLD registers */
-#define BALLOON3_PCMCIA0_REG (BALLOON3_FPGA_VIRT + 0x00e00008)
-/* fixme - same for now */
-#define BALLOON3_PCMCIA1_REG (BALLOON3_FPGA_VIRT + 0x00e00008)
-#define BALLOON3_NANDIO_IO_REG (BALLOON3_FPGA_VIRT + 0x00e00000)
+/* FPGA / CPLD registers for CF socket */
+#define BALLOON3_CF_STATUS_REG (BALLOON3_FPGA_VIRT + 0x00e00008)
+#define BALLOON3_CF_CONTROL_REG (BALLOON3_FPGA_VIRT + 0x00e00008)
+/* FPGA / CPLD version register */
+#define BALLOON3_FPGA_VER (BALLOON3_FPGA_VIRT + 0x00e0001c)
+/* FPGA / CPLD registers for NAND flash */
+#define BALLOON3_NAND_BASE (PXA_CS4_PHYS + 0x00e00000)
+#define BALLOON3_NAND_IO_REG (BALLOON3_FPGA_VIRT + 0x00e00000)
+#define BALLOON3_NAND_CONTROL2_REG (BALLOON3_FPGA_VIRT + 0x00e00010)
+#define BALLOON3_NAND_STAT_REG (BALLOON3_FPGA_VIRT + 0x00e00010)
+#define BALLOON3_NAND_CONTROL_REG (BALLOON3_FPGA_VIRT + 0x00e00014)
+
/* fpga/cpld interrupt control register */
#define BALLOON3_INT_CONTROL_REG (BALLOON3_FPGA_VIRT + 0x00e0000C)
-#define BALLOON3_NANDIO_CTL2_REG (BALLOON3_FPGA_VIRT + 0x00e00010)
-#define BALLOON3_NANDIO_CTL_REG (BALLOON3_FPGA_VIRT + 0x00e00014)
#define BALLOON3_VERSION_REG (BALLOON3_FPGA_VIRT + 0x00e0001c)
#define BALLOON3_SAMOSA_ADDR_REG (BALLOON3_FPGA_VIRT + 0x00c00000)
#define BALLOON3_SAMOSA_DATA_REG (BALLOON3_FPGA_VIRT + 0x00c00004)
#define BALLOON3_SAMOSA_STATUS_REG (BALLOON3_FPGA_VIRT + 0x00c0001c)
+/* CF Status Register bits (read-only) bits */
+#define BALLOON3_CF_nIRQ (1 << 0)
+#define BALLOON3_CF_nSTSCHG_BVD1 (1 << 1)
+
+/* CF Control Set Register bits / CF Control Clear Register bits (write-only) */
+#define BALLOON3_CF_RESET (1 << 0)
+#define BALLOON3_CF_ENABLE (1 << 1)
+#define BALLOON3_CF_ADD_ENABLE (1 << 2)
+
+/* CF Interrupt sources */
+#define BALLOON3_BP_CF_NRDY_IRQ BALLOON3_IRQ(0)
+#define BALLOON3_BP_NSTSCHG_IRQ BALLOON3_IRQ(1)
+
+/* NAND Control register */
+#define BALLOON3_NAND_CONTROL_FLWP (1 << 7)
+#define BALLOON3_NAND_CONTROL_FLSE (1 << 6)
+#define BALLOON3_NAND_CONTROL_FLCE3 (1 << 5)
+#define BALLOON3_NAND_CONTROL_FLCE2 (1 << 4)
+#define BALLOON3_NAND_CONTROL_FLCE1 (1 << 3)
+#define BALLOON3_NAND_CONTROL_FLCE0 (1 << 2)
+#define BALLOON3_NAND_CONTROL_FLALE (1 << 1)
+#define BALLOON3_NAND_CONTROL_FLCLE (1 << 0)
+
+/* NAND Status register */
+#define BALLOON3_NAND_STAT_RNB (1 << 0)
+
+/* NAND Control2 register */
+#define BALLOON3_NAND_CONTROL2_16BIT (1 << 0)
+
/* GPIOs for irqs */
#define BALLOON3_GPIO_AUX_NIRQ (94)
#define BALLOON3_GPIO_CODEC_IRQ (95)
#define BALLOON3_GPIO_S0_CD (105)
+/* NAND */
+#define BALLOON3_GPIO_RUN_NAND (102)
+
+/* PCF8574A Leds */
+#define BALLOON3_PCF_GPIO_BASE 160
+#define BALLOON3_PCF_GPIO_LED0 (BALLOON3_PCF_GPIO_BASE + 0)
+#define BALLOON3_PCF_GPIO_LED1 (BALLOON3_PCF_GPIO_BASE + 1)
+#define BALLOON3_PCF_GPIO_LED2 (BALLOON3_PCF_GPIO_BASE + 2)
+#define BALLOON3_PCF_GPIO_LED3 (BALLOON3_PCF_GPIO_BASE + 3)
+#define BALLOON3_PCF_GPIO_LED4 (BALLOON3_PCF_GPIO_BASE + 4)
+#define BALLOON3_PCF_GPIO_LED5 (BALLOON3_PCF_GPIO_BASE + 5)
+#define BALLOON3_PCF_GPIO_LED6 (BALLOON3_PCF_GPIO_BASE + 6)
+#define BALLOON3_PCF_GPIO_LED7 (BALLOON3_PCF_GPIO_BASE + 7)
+
/* FPGA Interrupt Mask/Acknowledge Register */
#define BALLOON3_INT_S0_IRQ (1 << 0) /* PCMCIA 0 IRQ */
#define BALLOON3_INT_S0_STSCHG (1 << 1) /* PCMCIA 0 status changed */
-/* CF Status Register */
-#define BALLOON3_PCMCIA_nIRQ (1 << 0) /* IRQ / ready signal */
-#define BALLOON3_PCMCIA_nSTSCHG_BVD1 (1 << 1)
- /* VDD sense / card status changed */
-
-/* CF control register (write) */
-#define BALLOON3_PCMCIA_RESET (1 << 0) /* Card reset signal */
-#define BALLOON3_PCMCIA_ENABLE (1 << 1)
-#define BALLOON3_PCMCIA_ADD_ENABLE (1 << 2)
-
/* CPLD (and FPGA) interface definitions */
#define CPLD_LCD0_DATA_SET 0x00
#define CPLD_LCD0_DATA_CLR 0x10
/* Balloon3 Interrupts */
#define BALLOON3_IRQ(x) (IRQ_BOARD_START + (x))
-#define BALLOON3_BP_CF_NRDY_IRQ BALLOON3_IRQ(0)
-#define BALLOON3_BP_NSTSCHG_IRQ BALLOON3_IRQ(1)
-
#define BALLOON3_AUX_NIRQ IRQ_GPIO(BALLOON3_GPIO_AUX_NIRQ)
#define BALLOON3_CODEC_IRQ IRQ_GPIO(BALLOON3_GPIO_CODEC_IRQ)
#define BALLOON3_S0_CD_IRQ IRQ_GPIO(BALLOON3_GPIO_S0_CD)
#include <net/ax88796.h>
#include <mach/mfp.h>
+/*
+ * base board glue for PXA270 module
+ */
+
+enum {
+ COLIBRI_PXA270_EVALBOARD = 0,
+ COLIBRI_PXA270_INCOME,
+};
+
+#if defined(CONFIG_MACH_COLIBRI_PXA270_EVALBOARD)
+extern void colibri_pxa270_evalboard_init(void);
+#else
+static inline void colibri_pxa270_evalboard_init(void) {}
+#endif
+
+#if defined(CONFIG_MACH_COLIBRI_PXA270_INCOME)
+extern void colibri_pxa270_income_boardinit(void);
+#else
+static inline void colibri_pxa270_income_boardinit(void) {}
+#endif
+
/*
* common settings for all modules
*/
/* physical memory regions */
#define COLIBRI_SDRAM_BASE 0xa0000000 /* SDRAM region */
-/* definitions for Colibri PXA270 */
-
-#define COLIBRI_PXA270_FLASH_PHYS (PXA_CS0_PHYS) /* Flash region */
-#define COLIBRI_PXA270_ETH_PHYS (PXA_CS2_PHYS) /* Ethernet */
-#define COLIBRI_PXA270_ETH_IRQ_GPIO 114
-#define COLIBRI_PXA270_ETH_IRQ \
- gpio_to_irq(mfp_to_gpio(COLIBRI_PXA270_ETH_IRQ_GPIO))
+/* GPIO definitions for Colibri PXA270 */
+#define GPIO114_COLIBRI_PXA270_ETH_IRQ 114
+#define GPIO0_COLIBRI_PXA270_SD_DETECT 0
+#define GPIO113_COLIBRI_PXA270_TS_IRQ 113
#endif /* _COLIBRI_H_ */
#define CORGI_GPIO_BACKLIGHT_CONT (CORGI_SCOOP_GPIO_BASE + 7)
#define CORGI_GPIO_MIC_BIAS (CORGI_SCOOP_GPIO_BASE + 8)
-/*
- * Shared data structures
- */
-extern struct platform_device corgiscoop_device;
-
#endif /* __ASM_ARCH_CORGI_H */
/*
GPIOn - Input from MAX823 (or equiv), normalizing USB +5V into a clean
-interrupt signal for determining cable presence. On the original gumstix,
-this is GPIO81, and GPIO83 needs to be defined as well. On the gumstix F,
+interrupt signal for determining cable presence. On the gumstix F,
this moves to GPIO17 and GPIO37. */
/* GPIOx - Connects to USB D+ and used as a pull-up after GPIOn
has detected a cable insertion; driven low otherwise. */
-#ifdef CONFIG_ARCH_GUMSTIX_ORIG
-
-#define GPIO_GUMSTIX_USB_GPIOn 81
-#define GPIO_GUMSTIX_USB_GPIOx 83
-
-#else
-
#define GPIO_GUMSTIX_USB_GPIOn 35
#define GPIO_GUMSTIX_USB_GPIOx 41
-#endif
-
/* usb state change */
#define GUMSTIX_USB_INTR_IRQ IRQ_GPIO(GPIO_GUMSTIX_USB_GPIOn)
#ifndef __ASM_ARCH_HARDWARE_H
#define __ASM_ARCH_HARDWARE_H
-/*
- * We requires absolute addresses.
- */
-#define PCIO_BASE 0
-
/*
* Workarounds for at least 2 errata so far require this.
* The mapping is set in mach-pxa/generic.c.
--- /dev/null
+/*
+ * Common functions for Palm LD, T5, TX, Z72
+ *
+ * Copyright (C) 2010
+ * Marek Vasut <marek.vasut@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#ifndef __INCLUDE_MACH_PALM27X__
+#define __INCLUDE_MACH_PALM27X__
+
+#if defined(CONFIG_MMC_PXA) || defined(CONFIG_MMC_PXA_MODULE)
+extern void __init palm27x_mmc_init(int detect, int ro, int power,
+ int power_inverted);
+#else
+static inline void palm27x_mmc_init(int detect, int ro, int power,
+ int power_inverted)
+{}
+#endif
+
+#if defined(CONFIG_SUSPEND)
+extern void __init palm27x_pm_init(unsigned long str_base);
+#else
+static inline void palm27x_pm_init(unsigned long str_base) {}
+#endif
+
+#if defined(CONFIG_FB_PXA) || defined(CONFIG_FB_PXA_MODULE)
+extern struct pxafb_mode_info palm_320x480_lcd_mode;
+extern struct pxafb_mode_info palm_320x320_lcd_mode;
+extern struct pxafb_mode_info palm_320x320_new_lcd_mode;
+extern void __init palm27x_lcd_init(int power,
+ struct pxafb_mode_info *mode);
+#else
+static inline void palm27x_lcd_init(int power, struct pxafb_mode_info *mode) {}
+#endif
+
+#if defined(CONFIG_USB_GADGET_PXA27X) || \
+ defined(CONFIG_USB_GADGET_PXA27X_MODULE)
+extern void __init palm27x_udc_init(int vbus, int pullup,
+ int vbus_inverted);
+#else
+static inline void palm27x_udc_init(int vbus, int pullup, int vbus_inverted) {}
+#endif
+
+#if defined(CONFIG_IRDA) || defined(CONFIG_IRDA_MODULE)
+extern void __init palm27x_irda_init(int pwdn);
+#else
+static inline void palm27x_irda_init(int pwdn) {}
+#endif
+
+#if defined(CONFIG_TOUCHSCREEN_WM97XX) || \
+ defined(CONFIG_TOUCHSCREEN_WM97XX_MODULE)
+extern void __init palm27x_ac97_init(int minv, int maxv, int jack,
+ int reset);
+#else
+static inline void palm27x_ac97_init(int minv, int maxv, int jack, int reset) {}
+#endif
+
+#if defined(CONFIG_BACKLIGHT_PWM) || defined(CONFIG_BACKLIGHT_PWM_MODULE)
+extern void __init palm27x_pwm_init(int bl, int lcd);
+#else
+static inline void palm27x_pwm_init(int bl, int lcd) {}
+#endif
+
+#if defined(CONFIG_PDA_POWER) || defined(CONFIG_PDA_POWER_MODULE)
+extern void __init palm27x_power_init(int ac, int usb);
+#else
+static inline void palm27x_power_init(int ac, int usb) {}
+#endif
+
+#if defined(CONFIG_REGULATOR_MAX1586) || \
+ defined(CONFIG_REGULATOR_MAX1586_MODULE)
+extern void __init palm27x_pmic_init(void);
+#else
+static inline void palm27x_pmic_init(void) {}
+#endif
+
+#endif /* __INCLUDE_MACH_PALM27X__ */
--- /dev/null
+/*
+ * Generic PXA PATA driver
+ *
+ * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __MACH_PATA_PXA_H__
+#define __MACH_PATA_PXA_H__
+
+struct pata_pxa_pdata {
+ /* PXA DMA DREQ<0:2> pin */
+ uint32_t dma_dreq;
+ /* Register shift */
+ uint32_t reg_shift;
+ /* IRQ flags */
+ uint32_t irq_flags;
+};
+
+#endif /* __MACH_PATA_PXA_H__ */
+++ /dev/null
-/*
- * SharpSL SSP Driver
- */
-
-unsigned long corgi_ssp_ads7846_putget(unsigned long);
-unsigned long corgi_ssp_ads7846_get(void);
-void corgi_ssp_ads7846_put(unsigned long data);
-void corgi_ssp_ads7846_lock(void);
-void corgi_ssp_ads7846_unlock(void);
-void corgi_ssp_lcdtg_send (unsigned char adrs, unsigned char data);
-void corgi_ssp_blduty_set(int duty);
-int corgi_ssp_max1111_get(unsigned long data);
-
-/*
- * SharpSL Touchscreen Driver
- */
-
-struct corgits_machinfo {
- unsigned long (*get_hsync_invperiod)(void);
- void (*put_hsync)(void);
- void (*wait_hsync)(void);
-};
-
-
-/*
- * SharpSL Backlight
- */
-extern void corgibl_limit_intensity(int limit);
-extern void corgi_lcd_limit_intensity(int limit);
-
-
-/*
- * SharpSL Battery/PM Driver
- */
-extern void sharpsl_battery_kick(void);
extern struct sharpsl_pm_status sharpsl_pm;
+extern struct battery_thresh sharpsl_battery_levels_acin[];
+extern struct battery_thresh sharpsl_battery_levels_noac[];
#define SHARPSL_LED_ERROR 2
#define SHARPSL_LED_ON 1
void sharpsl_battery_kick(void);
void sharpsl_pm_led(int val);
+/* MAX1111 Channel Definitions */
+#define MAX1111_BATT_VOLT 4u
+#define MAX1111_BATT_TEMP 2u
+#define MAX1111_ACIN_VOLT 6u
+int sharpsl_pm_pxa_read_max1111(int channel);
+
+void corgi_lcd_limit_intensity(int limit);
#endif
/*
* Shared data structures
*/
-extern struct platform_device spitzscoop_device;
-extern struct platform_device spitzscoop2_device;
extern struct platform_device spitzssp_device;
extern struct sharpsl_charger_machinfo spitz_pm_machinfo;
#include <linux/irq.h>
#include <linux/pda_power.h>
#include <linux/power_supply.h>
-#include <linux/wm97xx_batt.h>
+#include <linux/wm97xx.h>
#include <linux/mtd/physmap.h>
#include <linux/usb/gpio_vbus.h>
#include <linux/regulator/max1586.h>
},
};
-static struct wm97xx_batt_info mioa701_battery_data = {
+static struct wm97xx_batt_pdata mioa701_battery_data = {
.batt_aux = WM97XX_AUX_ID1,
.temp_aux = -1,
.charge_gpio = -1,
.batt_name = "mioa701_battery",
};
+static struct wm97xx_pdata mioa701_wm97xx_pdata = {
+ .batt_pdata = &mioa701_battery_data,
+};
+
/*
* Voltage regulation
*/
static pxa2xx_audio_ops_t mioa701_ac97_info = {
.reset_gpio = 95,
+ .codec_pdata = { &mioa701_wm97xx_pdata, },
};
/*
set_pxa_fb_info(&mioa701_pxafb_info);
pxa_set_mci_info(&mioa701_mci_info);
pxa_set_keypad_info(&mioa701_keypad_info);
- wm97xx_bat_set_pdata(&mioa701_battery_data);
pxa_set_udc_info(&mioa701_udc_info);
pxa_set_ac97_info(&mioa701_ac97_info);
pm_power_off = mioa701_poweroff;
--- /dev/null
+/*
+ * Common code for Palm LD, T5, TX, Z72
+ *
+ * Copyright (C) 2010
+ * Marek Vasut <marek.vasut@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/gpio_keys.h>
+#include <linux/input.h>
+#include <linux/pda_power.h>
+#include <linux/pwm_backlight.h>
+#include <linux/gpio.h>
+#include <linux/wm97xx.h>
+#include <linux/power_supply.h>
+#include <linux/usb/gpio_vbus.h>
+#include <linux/regulator/max1586.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <mach/pxa27x.h>
+#include <mach/audio.h>
+#include <mach/mmc.h>
+#include <mach/pxafb.h>
+#include <mach/irda.h>
+#include <mach/udc.h>
+#include <mach/palmasoc.h>
+#include <mach/palm27x.h>
+
+#include <plat/i2c.h>
+
+#include "generic.h"
+#include "devices.h"
+
+/******************************************************************************
+ * SD/MMC card controller
+ ******************************************************************************/
+#if defined(CONFIG_MMC_PXA) || defined(CONFIG_MMC_PXA_MODULE)
+static struct pxamci_platform_data palm27x_mci_platform_data = {
+ .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34,
+ .detect_delay_ms = 200,
+};
+
+void __init palm27x_mmc_init(int detect, int ro, int power,
+ int power_inverted)
+{
+ palm27x_mci_platform_data.gpio_card_detect = detect;
+ palm27x_mci_platform_data.gpio_card_ro = ro;
+ palm27x_mci_platform_data.gpio_power = power;
+ palm27x_mci_platform_data.gpio_power_invert = power_inverted;
+
+ pxa_set_mci_info(&palm27x_mci_platform_data);
+}
+#endif
+
+/******************************************************************************
+ * Power management - standby
+ ******************************************************************************/
+#if defined(CONFIG_SUSPEND)
+void __init palm27x_pm_init(unsigned long str_base)
+{
+ static const unsigned long resume[] = {
+ 0xe3a00101, /* mov r0, #0x40000000 */
+ 0xe380060f, /* orr r0, r0, #0x00f00000 */
+ 0xe590f008, /* ldr pc, [r0, #0x08] */
+ };
+
+ /*
+ * Copy the bootloader.
+ * NOTE: PalmZ72 uses a different wakeup method!
+ */
+ memcpy(phys_to_virt(str_base), resume, sizeof(resume));
+}
+#endif
+
+/******************************************************************************
+ * Framebuffer
+ ******************************************************************************/
+#if defined(CONFIG_FB_PXA) || defined(CONFIG_FB_PXA_MODULE)
+struct pxafb_mode_info palm_320x480_lcd_mode = {
+ .pixclock = 57692,
+ .xres = 320,
+ .yres = 480,
+ .bpp = 16,
+
+ .left_margin = 32,
+ .right_margin = 1,
+ .upper_margin = 7,
+ .lower_margin = 1,
+
+ .hsync_len = 4,
+ .vsync_len = 1,
+};
+
+struct pxafb_mode_info palm_320x320_lcd_mode = {
+ .pixclock = 115384,
+ .xres = 320,
+ .yres = 320,
+ .bpp = 16,
+
+ .left_margin = 27,
+ .right_margin = 7,
+ .upper_margin = 7,
+ .lower_margin = 8,
+
+ .hsync_len = 6,
+ .vsync_len = 1,
+};
+
+struct pxafb_mode_info palm_320x320_new_lcd_mode = {
+ .pixclock = 86538,
+ .xres = 320,
+ .yres = 320,
+ .bpp = 16,
+
+ .left_margin = 20,
+ .right_margin = 8,
+ .upper_margin = 8,
+ .lower_margin = 5,
+
+ .hsync_len = 4,
+ .vsync_len = 1,
+};
+
+static struct pxafb_mach_info palm27x_lcd_screen = {
+ .num_modes = 1,
+ .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL,
+};
+
+static int palm27x_lcd_power;
+static void palm27x_lcd_ctl(int on, struct fb_var_screeninfo *info)
+{
+ gpio_set_value(palm27x_lcd_power, on);
+}
+
+void __init palm27x_lcd_init(int power, struct pxafb_mode_info *mode)
+{
+ palm27x_lcd_screen.modes = mode;
+
+ if (gpio_is_valid(power)) {
+ if (!gpio_request(power, "LCD power")) {
+ pr_err("Palm27x: failed to claim lcd power gpio!\n");
+ return;
+ }
+ if (!gpio_direction_output(power, 1)) {
+ pr_err("Palm27x: lcd power configuration failed!\n");
+ return;
+ }
+ palm27x_lcd_power = power;
+ palm27x_lcd_screen.pxafb_lcd_power = palm27x_lcd_ctl;
+ }
+
+ set_pxa_fb_info(&palm27x_lcd_screen);
+}
+#endif
+
+/******************************************************************************
+ * USB Gadget
+ ******************************************************************************/
+#if defined(CONFIG_USB_GADGET_PXA27X) || \
+ defined(CONFIG_USB_GADGET_PXA27X_MODULE)
+static struct gpio_vbus_mach_info palm27x_udc_info = {
+ .gpio_vbus_inverted = 1,
+};
+
+static struct platform_device palm27x_gpio_vbus = {
+ .name = "gpio-vbus",
+ .id = -1,
+ .dev = {
+ .platform_data = &palm27x_udc_info,
+ },
+};
+
+void __init palm27x_udc_init(int vbus, int pullup, int vbus_inverted)
+{
+ palm27x_udc_info.gpio_vbus = vbus;
+ palm27x_udc_info.gpio_pullup = pullup;
+
+ palm27x_udc_info.gpio_vbus_inverted = vbus_inverted;
+
+ if (!gpio_request(pullup, "USB Pullup")) {
+ gpio_direction_output(pullup,
+ palm27x_udc_info.gpio_vbus_inverted);
+ gpio_free(pullup);
+ } else
+ return;
+
+ platform_device_register(&palm27x_gpio_vbus);
+}
+#endif
+
+/******************************************************************************
+ * IrDA
+ ******************************************************************************/
+#if defined(CONFIG_IRDA) || defined(CONFIG_IRDA_MODULE)
+static struct pxaficp_platform_data palm27x_ficp_platform_data = {
+ .transceiver_cap = IR_SIRMODE | IR_OFF,
+};
+
+void __init palm27x_irda_init(int pwdn)
+{
+ palm27x_ficp_platform_data.gpio_pwdown = pwdn;
+ pxa_set_ficp_info(&palm27x_ficp_platform_data);
+}
+#endif
+
+/******************************************************************************
+ * WM97xx audio, battery
+ ******************************************************************************/
+#if defined(CONFIG_TOUCHSCREEN_WM97XX) || \
+ defined(CONFIG_TOUCHSCREEN_WM97XX_MODULE)
+static struct wm97xx_batt_pdata palm27x_batt_pdata = {
+ .batt_aux = WM97XX_AUX_ID3,
+ .temp_aux = WM97XX_AUX_ID2,
+ .charge_gpio = -1,
+ .batt_mult = 1000,
+ .batt_div = 414,
+ .temp_mult = 1,
+ .temp_div = 1,
+ .batt_tech = POWER_SUPPLY_TECHNOLOGY_LIPO,
+ .batt_name = "main-batt",
+};
+
+static struct wm97xx_pdata palm27x_wm97xx_pdata = {
+ .batt_pdata = &palm27x_batt_pdata,
+};
+
+static pxa2xx_audio_ops_t palm27x_ac97_pdata = {
+ .codec_pdata = { &palm27x_wm97xx_pdata, },
+};
+
+static struct palm27x_asoc_info palm27x_asoc_pdata = {
+ .jack_gpio = -1,
+};
+
+static struct platform_device palm27x_asoc = {
+ .name = "palm27x-asoc",
+ .id = -1,
+ .dev = {
+ .platform_data = &palm27x_asoc_pdata,
+ },
+};
+
+void __init palm27x_ac97_init(int minv, int maxv, int jack, int reset)
+{
+ palm27x_ac97_pdata.reset_gpio = reset;
+ palm27x_asoc_pdata.jack_gpio = jack;
+
+ if (minv < 0 || maxv < 0) {
+ palm27x_ac97_pdata.codec_pdata[0] = NULL;
+ pxa_set_ac97_info(&palm27x_ac97_pdata);
+ } else {
+ palm27x_batt_pdata.min_voltage = minv,
+ palm27x_batt_pdata.max_voltage = maxv,
+
+ pxa_set_ac97_info(&palm27x_ac97_pdata);
+ platform_device_register(&palm27x_asoc);
+ }
+}
+#endif
+
+/******************************************************************************
+ * Backlight
+ ******************************************************************************/
+#if defined(CONFIG_BACKLIGHT_PWM) || defined(CONFIG_BACKLIGHT_PWM_MODULE)
+static int palm_bl_power;
+static int palm_lcd_power;
+
+static int palm27x_backlight_init(struct device *dev)
+{
+ int ret;
+
+ ret = gpio_request(palm_bl_power, "BL POWER");
+ if (ret)
+ goto err;
+ ret = gpio_direction_output(palm_bl_power, 0);
+ if (ret)
+ goto err2;
+
+ if (gpio_is_valid(palm_lcd_power)) {
+ ret = gpio_request(palm_lcd_power, "LCD POWER");
+ if (ret)
+ goto err2;
+ ret = gpio_direction_output(palm_lcd_power, 0);
+ if (ret)
+ goto err3;
+ }
+
+ return 0;
+err3:
+ gpio_free(palm_lcd_power);
+err2:
+ gpio_free(palm_bl_power);
+err:
+ return ret;
+}
+
+static int palm27x_backlight_notify(struct device *dev, int brightness)
+{
+ gpio_set_value(palm_bl_power, brightness);
+ if (gpio_is_valid(palm_lcd_power))
+ gpio_set_value(palm_lcd_power, brightness);
+ return brightness;
+}
+
+static void palm27x_backlight_exit(struct device *dev)
+{
+ gpio_free(palm_bl_power);
+ if (gpio_is_valid(palm_lcd_power))
+ gpio_free(palm_lcd_power);
+}
+
+static struct platform_pwm_backlight_data palm27x_backlight_data = {
+ .pwm_id = 0,
+ .max_brightness = 0xfe,
+ .dft_brightness = 0x7e,
+ .pwm_period_ns = 3500,
+ .init = palm27x_backlight_init,
+ .notify = palm27x_backlight_notify,
+ .exit = palm27x_backlight_exit,
+};
+
+static struct platform_device palm27x_backlight = {
+ .name = "pwm-backlight",
+ .dev = {
+ .parent = &pxa27x_device_pwm0.dev,
+ .platform_data = &palm27x_backlight_data,
+ },
+};
+
+void __init palm27x_pwm_init(int bl, int lcd)
+{
+ palm_bl_power = bl;
+ palm_lcd_power = lcd;
+ platform_device_register(&palm27x_backlight);
+}
+#endif
+
+/******************************************************************************
+ * Power supply
+ ******************************************************************************/
+#if defined(CONFIG_PDA_POWER) || defined(CONFIG_PDA_POWER_MODULE)
+static int palm_ac_state;
+static int palm_usb_state;
+
+static int palm27x_power_supply_init(struct device *dev)
+{
+ int ret;
+
+ ret = gpio_request(palm_ac_state, "AC state");
+ if (ret)
+ goto err1;
+ ret = gpio_direction_input(palm_ac_state);
+ if (ret)
+ goto err2;
+
+ if (gpio_is_valid(palm_usb_state)) {
+ ret = gpio_request(palm_usb_state, "USB state");
+ if (ret)
+ goto err2;
+ ret = gpio_direction_input(palm_usb_state);
+ if (ret)
+ goto err3;
+ }
+
+ return 0;
+err3:
+ gpio_free(palm_usb_state);
+err2:
+ gpio_free(palm_ac_state);
+err1:
+ return ret;
+}
+
+static void palm27x_power_supply_exit(struct device *dev)
+{
+ gpio_free(palm_usb_state);
+ gpio_free(palm_ac_state);
+}
+
+static int palm27x_is_ac_online(void)
+{
+ return gpio_get_value(palm_ac_state);
+}
+
+static int palm27x_is_usb_online(void)
+{
+ return !gpio_get_value(palm_usb_state);
+}
+static char *palm27x_supplicants[] = {
+ "main-battery",
+};
+
+static struct pda_power_pdata palm27x_ps_info = {
+ .init = palm27x_power_supply_init,
+ .exit = palm27x_power_supply_exit,
+ .is_ac_online = palm27x_is_ac_online,
+ .is_usb_online = palm27x_is_usb_online,
+ .supplied_to = palm27x_supplicants,
+ .num_supplicants = ARRAY_SIZE(palm27x_supplicants),
+};
+
+static struct platform_device palm27x_power_supply = {
+ .name = "pda-power",
+ .id = -1,
+ .dev = {
+ .platform_data = &palm27x_ps_info,
+ },
+};
+
+void __init palm27x_power_init(int ac, int usb)
+{
+ palm_ac_state = ac;
+ palm_usb_state = usb;
+ platform_device_register(&palm27x_power_supply);
+}
+#endif
+
+/******************************************************************************
+ * Core power regulator
+ ******************************************************************************/
+#if defined(CONFIG_REGULATOR_MAX1586) || \
+ defined(CONFIG_REGULATOR_MAX1586_MODULE)
+static struct regulator_consumer_supply palm27x_max1587a_consumers[] = {
+ {
+ .supply = "vcc_core",
+ }
+};
+
+static struct regulator_init_data palm27x_max1587a_v3_info = {
+ .constraints = {
+ .name = "vcc_core range",
+ .min_uV = 900000,
+ .max_uV = 1705000,
+ .always_on = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ },
+ .consumer_supplies = palm27x_max1587a_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(palm27x_max1587a_consumers),
+};
+
+static struct max1586_subdev_data palm27x_max1587a_subdevs[] = {
+ {
+ .name = "vcc_core",
+ .id = MAX1586_V3,
+ .platform_data = &palm27x_max1587a_v3_info,
+ }
+};
+
+static struct max1586_platform_data palm27x_max1587a_info = {
+ .subdevs = palm27x_max1587a_subdevs,
+ .num_subdevs = ARRAY_SIZE(palm27x_max1587a_subdevs),
+ .v3_gain = MAX1586_GAIN_R24_3k32, /* 730..1550 mV */
+};
+
+static struct i2c_board_info __initdata palm27x_pi2c_board_info[] = {
+ {
+ I2C_BOARD_INFO("max1586", 0x14),
+ .platform_data = &palm27x_max1587a_info,
+ },
+};
+
+void __init palm27x_pmic_init(void)
+{
+ i2c_register_board_info(1, ARRAY_AND_SIZE(palm27x_pi2c_board_info));
+ pxa27x_set_i2c_power_info(NULL);
+}
+#endif
#include <linux/pda_power.h>
#include <linux/pwm_backlight.h>
#include <linux/gpio.h>
-#include <linux/wm97xx_batt.h>
+#include <linux/wm97xx.h>
#include <linux/power_supply.h>
#include <linux/sysdev.h>
#include <linux/mtd/mtd.h>
#include <mach/irda.h>
#include <mach/pxa27x_keypad.h>
#include <mach/palmasoc.h>
+#include <mach/palm27x.h>
#include "generic.h"
#include "devices.h"
/******************************************************************************
* NOR Flash
******************************************************************************/
+#if defined(CONFIG_MTD_PHYSMAP) || defined(CONFIG_MTD_PHYSMAP_MODULE)
static struct mtd_partition palmld_partitions[] = {
{
.name = "Flash",
},
};
-/******************************************************************************
- * SD/MMC card controller
- ******************************************************************************/
-static struct pxamci_platform_data palmld_mci_platform_data = {
- .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34,
- .gpio_card_detect = GPIO_NR_PALMLD_SD_DETECT_N,
- .gpio_card_ro = GPIO_NR_PALMLD_SD_READONLY,
- .gpio_power = GPIO_NR_PALMLD_SD_POWER,
- .detect_delay_ms = 200,
-};
+static void __init palmld_nor_init(void)
+{
+ platform_device_register(&palmld_flash);
+}
+#else
+static inline void palmld_nor_init(void) {}
+#endif
/******************************************************************************
* GPIO keyboard
******************************************************************************/
+#if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE)
static unsigned int palmld_matrix_keys[] = {
KEY(0, 1, KEY_F2),
KEY(0, 2, KEY_UP),
.debounce_interval = 30,
};
+static void __init palmld_kpc_init(void)
+{
+ pxa_set_keypad_info(&palmld_keypad_platform_data);
+}
+#else
+static inline void palmld_kpc_init(void) {}
+#endif
+
/******************************************************************************
* GPIO keys
******************************************************************************/
+#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
static struct gpio_keys_button palmld_pxa_buttons[] = {
{KEY_F8, GPIO_NR_PALMLD_HOTSYNC_BUTTON_N, 1, "HotSync Button" },
{KEY_F9, GPIO_NR_PALMLD_LOCK_SWITCH, 0, "Lock Switch" },
},
};
-/******************************************************************************
- * Backlight
- ******************************************************************************/
-static int palmld_backlight_init(struct device *dev)
-{
- int ret;
-
- ret = gpio_request(GPIO_NR_PALMLD_BL_POWER, "BL POWER");
- if (ret)
- goto err;
- ret = gpio_direction_output(GPIO_NR_PALMLD_BL_POWER, 0);
- if (ret)
- goto err2;
- ret = gpio_request(GPIO_NR_PALMLD_LCD_POWER, "LCD POWER");
- if (ret)
- goto err2;
- ret = gpio_direction_output(GPIO_NR_PALMLD_LCD_POWER, 0);
- if (ret)
- goto err3;
-
- return 0;
-err3:
- gpio_free(GPIO_NR_PALMLD_LCD_POWER);
-err2:
- gpio_free(GPIO_NR_PALMLD_BL_POWER);
-err:
- return ret;
-}
-
-static int palmld_backlight_notify(struct device *dev, int brightness)
+static void __init palmld_keys_init(void)
{
- gpio_set_value(GPIO_NR_PALMLD_BL_POWER, brightness);
- gpio_set_value(GPIO_NR_PALMLD_LCD_POWER, brightness);
- return brightness;
+ platform_device_register(&palmld_pxa_keys);
}
-
-static void palmld_backlight_exit(struct device *dev)
-{
- gpio_free(GPIO_NR_PALMLD_BL_POWER);
- gpio_free(GPIO_NR_PALMLD_LCD_POWER);
-}
-
-static struct platform_pwm_backlight_data palmld_backlight_data = {
- .pwm_id = 0,
- .max_brightness = PALMLD_MAX_INTENSITY,
- .dft_brightness = PALMLD_MAX_INTENSITY,
- .pwm_period_ns = PALMLD_PERIOD_NS,
- .init = palmld_backlight_init,
- .notify = palmld_backlight_notify,
- .exit = palmld_backlight_exit,
-};
-
-static struct platform_device palmld_backlight = {
- .name = "pwm-backlight",
- .dev = {
- .parent = &pxa27x_device_pwm0.dev,
- .platform_data = &palmld_backlight_data,
- },
-};
-
-/******************************************************************************
- * IrDA
- ******************************************************************************/
-static struct pxaficp_platform_data palmld_ficp_platform_data = {
- .gpio_pwdown = GPIO_NR_PALMLD_IR_DISABLE,
- .transceiver_cap = IR_SIRMODE | IR_OFF,
-};
+#else
+static inline void palmld_keys_init(void) {}
+#endif
/******************************************************************************
* LEDs
******************************************************************************/
+#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE)
struct gpio_led gpio_leds[] = {
{
.name = "palmld:green:led",
}
};
-/******************************************************************************
- * Power supply
- ******************************************************************************/
-static int power_supply_init(struct device *dev)
-{
- int ret;
-
- ret = gpio_request(GPIO_NR_PALMLD_POWER_DETECT, "CABLE_STATE_AC");
- if (ret)
- goto err1;
- ret = gpio_direction_input(GPIO_NR_PALMLD_POWER_DETECT);
- if (ret)
- goto err2;
-
- ret = gpio_request(GPIO_NR_PALMLD_USB_DETECT_N, "CABLE_STATE_USB");
- if (ret)
- goto err2;
- ret = gpio_direction_input(GPIO_NR_PALMLD_USB_DETECT_N);
- if (ret)
- goto err3;
-
- return 0;
-
-err3:
- gpio_free(GPIO_NR_PALMLD_USB_DETECT_N);
-err2:
- gpio_free(GPIO_NR_PALMLD_POWER_DETECT);
-err1:
- return ret;
-}
-
-static int palmld_is_ac_online(void)
+static void __init palmld_leds_init(void)
{
- return gpio_get_value(GPIO_NR_PALMLD_POWER_DETECT);
+ platform_device_register(&palmld_leds);
}
-
-static int palmld_is_usb_online(void)
-{
- return !gpio_get_value(GPIO_NR_PALMLD_USB_DETECT_N);
-}
-
-static void power_supply_exit(struct device *dev)
-{
- gpio_free(GPIO_NR_PALMLD_USB_DETECT_N);
- gpio_free(GPIO_NR_PALMLD_POWER_DETECT);
-}
-
-static char *palmld_supplicants[] = {
- "main-battery",
-};
-
-static struct pda_power_pdata power_supply_info = {
- .init = power_supply_init,
- .is_ac_online = palmld_is_ac_online,
- .is_usb_online = palmld_is_usb_online,
- .exit = power_supply_exit,
- .supplied_to = palmld_supplicants,
- .num_supplicants = ARRAY_SIZE(palmld_supplicants),
-};
-
-static struct platform_device power_supply = {
- .name = "pda-power",
- .id = -1,
- .dev = {
- .platform_data = &power_supply_info,
- },
-};
-
-/******************************************************************************
- * WM97xx battery
- ******************************************************************************/
-static struct wm97xx_batt_info wm97xx_batt_pdata = {
- .batt_aux = WM97XX_AUX_ID3,
- .temp_aux = WM97XX_AUX_ID2,
- .charge_gpio = -1,
- .max_voltage = PALMLD_BAT_MAX_VOLTAGE,
- .min_voltage = PALMLD_BAT_MIN_VOLTAGE,
- .batt_mult = 1000,
- .batt_div = 414,
- .temp_mult = 1,
- .temp_div = 1,
- .batt_tech = POWER_SUPPLY_TECHNOLOGY_LIPO,
- .batt_name = "main-batt",
-};
-
-/******************************************************************************
- * aSoC audio
- ******************************************************************************/
-static struct palm27x_asoc_info palmld_asoc_pdata = {
- .jack_gpio = GPIO_NR_PALMLD_EARPHONE_DETECT,
-};
-
-static pxa2xx_audio_ops_t palmld_ac97_pdata = {
- .reset_gpio = 95,
-};
-
-static struct platform_device palmld_asoc = {
- .name = "palm27x-asoc",
- .id = -1,
- .dev = {
- .platform_data = &palmld_asoc_pdata,
- },
-};
+#else
+static inline void palmld_leds_init(void) {}
+#endif
/******************************************************************************
* HDD
******************************************************************************/
-static struct platform_device palmld_hdd = {
+#if defined(CONFIG_PATA_PALMLD) || defined(CONFIG_PATA_PALMLD_MODULE)
+static struct platform_device palmld_ide_device = {
.name = "pata_palmld",
.id = -1,
};
-/******************************************************************************
- * Framebuffer
- ******************************************************************************/
-static struct pxafb_mode_info palmld_lcd_modes[] = {
-{
- .pixclock = 57692,
- .xres = 320,
- .yres = 480,
- .bpp = 16,
-
- .left_margin = 32,
- .right_margin = 1,
- .upper_margin = 7,
- .lower_margin = 1,
-
- .hsync_len = 4,
- .vsync_len = 1,
-},
-};
-
-static struct pxafb_mach_info palmld_lcd_screen = {
- .modes = palmld_lcd_modes,
- .num_modes = ARRAY_SIZE(palmld_lcd_modes),
- .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL,
-};
-
-/******************************************************************************
- * Power management - standby
- ******************************************************************************/
-static void __init palmld_pm_init(void)
+static void __init palmld_ide_init(void)
{
- static u32 resume[] = {
- 0xe3a00101, /* mov r0, #0x40000000 */
- 0xe380060f, /* orr r0, r0, #0x00f00000 */
- 0xe590f008, /* ldr pc, [r0, #0x08] */
- };
-
- /* copy the bootloader */
- memcpy(phys_to_virt(PALMLD_STR_BASE), resume, sizeof(resume));
+ platform_device_register(&palmld_ide_device);
}
+#else
+static inline void palmld_ide_init(void) {}
+#endif
/******************************************************************************
* Machine init
******************************************************************************/
-static struct platform_device *devices[] __initdata = {
-#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
- &palmld_pxa_keys,
-#endif
- &palmld_backlight,
- &palmld_leds,
- &power_supply,
- &palmld_asoc,
- &palmld_hdd,
- &palmld_flash,
-};
-
static struct map_desc palmld_io_desc[] __initdata = {
{
.virtual = PALMLD_IDE_VIRT,
static void __init palmld_init(void)
{
pxa2xx_mfp_config(ARRAY_AND_SIZE(palmld_pin_config));
-
pxa_set_ffuart_info(NULL);
pxa_set_btuart_info(NULL);
pxa_set_stuart_info(NULL);
- palmld_pm_init();
- set_pxa_fb_info(&palmld_lcd_screen);
- pxa_set_mci_info(&palmld_mci_platform_data);
- pxa_set_ac97_info(&palmld_ac97_pdata);
- pxa_set_ficp_info(&palmld_ficp_platform_data);
- pxa_set_keypad_info(&palmld_keypad_platform_data);
- wm97xx_bat_set_pdata(&wm97xx_batt_pdata);
-
- platform_add_devices(devices, ARRAY_SIZE(devices));
+ palm27x_mmc_init(GPIO_NR_PALMLD_SD_DETECT_N, GPIO_NR_PALMLD_SD_READONLY,
+ GPIO_NR_PALMLD_SD_POWER, 0);
+ palm27x_pm_init(PALMLD_STR_BASE);
+ palm27x_lcd_init(-1, &palm_320x480_lcd_mode);
+ palm27x_irda_init(GPIO_NR_PALMLD_IR_DISABLE);
+ palm27x_ac97_init(PALMLD_BAT_MIN_VOLTAGE, PALMLD_BAT_MAX_VOLTAGE,
+ GPIO_NR_PALMLD_EARPHONE_DETECT, 95);
+ palm27x_pwm_init(GPIO_NR_PALMLD_BL_POWER, GPIO_NR_PALMLD_LCD_POWER);
+ palm27x_power_init(GPIO_NR_PALMLD_POWER_DETECT,
+ GPIO_NR_PALMLD_USB_DETECT_N);
+ palm27x_pmic_init();
+ palmld_kpc_init();
+ palmld_keys_init();
+ palmld_nor_init();
+ palmld_leds_init();
+ palmld_ide_init();
}
MACHINE_START(PALMLD, "Palm LifeDrive")
#include <linux/pda_power.h>
#include <linux/pwm_backlight.h>
#include <linux/gpio.h>
-#include <linux/wm97xx_batt.h>
+#include <linux/wm97xx.h>
#include <linux/power_supply.h>
#include <linux/usb/gpio_vbus.h>
#include <mach/pxa27x_keypad.h>
#include <mach/udc.h>
#include <mach/palmasoc.h>
+#include <mach/palm27x.h>
#include "generic.h"
#include "devices.h"
GPIO107_GPIO, /* earphone detect */
};
-/******************************************************************************
- * SD/MMC card controller
- ******************************************************************************/
-static struct pxamci_platform_data palmt5_mci_platform_data = {
- .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34,
- .gpio_card_detect = GPIO_NR_PALMT5_SD_DETECT_N,
- .gpio_card_ro = GPIO_NR_PALMT5_SD_READONLY,
- .gpio_power = GPIO_NR_PALMT5_SD_POWER,
- .detect_delay_ms = 200,
-};
-
/******************************************************************************
* GPIO keyboard
******************************************************************************/
+#if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE)
static unsigned int palmt5_matrix_keys[] = {
KEY(0, 0, KEY_POWER),
KEY(0, 1, KEY_F1),
.debounce_interval = 30,
};
+static void __init palmt5_kpc_init(void)
+{
+ pxa_set_keypad_info(&palmt5_keypad_platform_data);
+}
+#else
+static inline void palmt5_kpc_init(void) {}
+#endif
+
/******************************************************************************
* GPIO keys
******************************************************************************/
+#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
static struct gpio_keys_button palmt5_pxa_buttons[] = {
{KEY_F8, GPIO_NR_PALMT5_HOTSYNC_BUTTON_N, 1, "HotSync Button" },
};
},
};
-/******************************************************************************
- * Backlight
- ******************************************************************************/
-static int palmt5_backlight_init(struct device *dev)
-{
- int ret;
-
- ret = gpio_request(GPIO_NR_PALMT5_BL_POWER, "BL POWER");
- if (ret)
- goto err;
- ret = gpio_direction_output(GPIO_NR_PALMT5_BL_POWER, 0);
- if (ret)
- goto err2;
- ret = gpio_request(GPIO_NR_PALMT5_LCD_POWER, "LCD POWER");
- if (ret)
- goto err2;
- ret = gpio_direction_output(GPIO_NR_PALMT5_LCD_POWER, 0);
- if (ret)
- goto err3;
-
- return 0;
-err3:
- gpio_free(GPIO_NR_PALMT5_LCD_POWER);
-err2:
- gpio_free(GPIO_NR_PALMT5_BL_POWER);
-err:
- return ret;
-}
-
-static int palmt5_backlight_notify(struct device *dev, int brightness)
-{
- gpio_set_value(GPIO_NR_PALMT5_BL_POWER, brightness);
- gpio_set_value(GPIO_NR_PALMT5_LCD_POWER, brightness);
- return brightness;
-}
-
-static void palmt5_backlight_exit(struct device *dev)
-{
- gpio_free(GPIO_NR_PALMT5_BL_POWER);
- gpio_free(GPIO_NR_PALMT5_LCD_POWER);
-}
-
-static struct platform_pwm_backlight_data palmt5_backlight_data = {
- .pwm_id = 0,
- .max_brightness = PALMT5_MAX_INTENSITY,
- .dft_brightness = PALMT5_MAX_INTENSITY,
- .pwm_period_ns = PALMT5_PERIOD_NS,
- .init = palmt5_backlight_init,
- .notify = palmt5_backlight_notify,
- .exit = palmt5_backlight_exit,
-};
-
-static struct platform_device palmt5_backlight = {
- .name = "pwm-backlight",
- .dev = {
- .parent = &pxa27x_device_pwm0.dev,
- .platform_data = &palmt5_backlight_data,
- },
-};
-
-/******************************************************************************
- * IrDA
- ******************************************************************************/
-static struct pxaficp_platform_data palmt5_ficp_platform_data = {
- .gpio_pwdown = GPIO_NR_PALMT5_IR_DISABLE,
- .transceiver_cap = IR_SIRMODE | IR_OFF,
-};
-
-/******************************************************************************
- * UDC
- ******************************************************************************/
-static struct gpio_vbus_mach_info palmt5_udc_info = {
- .gpio_vbus = GPIO_NR_PALMT5_USB_DETECT_N,
- .gpio_vbus_inverted = 1,
- .gpio_pullup = GPIO_NR_PALMT5_USB_PULLUP,
-};
-
-static struct platform_device palmt5_gpio_vbus = {
- .name = "gpio-vbus",
- .id = -1,
- .dev = {
- .platform_data = &palmt5_udc_info,
- },
-};
-
-/******************************************************************************
- * Power supply
- ******************************************************************************/
-static int power_supply_init(struct device *dev)
-{
- int ret;
-
- ret = gpio_request(GPIO_NR_PALMT5_POWER_DETECT, "CABLE_STATE_AC");
- if (ret)
- goto err1;
- ret = gpio_direction_input(GPIO_NR_PALMT5_POWER_DETECT);
- if (ret)
- goto err2;
-
- return 0;
-err2:
- gpio_free(GPIO_NR_PALMT5_POWER_DETECT);
-err1:
- return ret;
-}
-
-static int palmt5_is_ac_online(void)
-{
- return gpio_get_value(GPIO_NR_PALMT5_POWER_DETECT);
-}
-
-static void power_supply_exit(struct device *dev)
-{
- gpio_free(GPIO_NR_PALMT5_POWER_DETECT);
-}
-
-static char *palmt5_supplicants[] = {
- "main-battery",
-};
-
-static struct pda_power_pdata power_supply_info = {
- .init = power_supply_init,
- .is_ac_online = palmt5_is_ac_online,
- .exit = power_supply_exit,
- .supplied_to = palmt5_supplicants,
- .num_supplicants = ARRAY_SIZE(palmt5_supplicants),
-};
-
-static struct platform_device power_supply = {
- .name = "pda-power",
- .id = -1,
- .dev = {
- .platform_data = &power_supply_info,
- },
-};
-
-/******************************************************************************
- * WM97xx battery
- ******************************************************************************/
-static struct wm97xx_batt_info wm97xx_batt_pdata = {
- .batt_aux = WM97XX_AUX_ID3,
- .temp_aux = WM97XX_AUX_ID2,
- .charge_gpio = -1,
- .max_voltage = PALMT5_BAT_MAX_VOLTAGE,
- .min_voltage = PALMT5_BAT_MIN_VOLTAGE,
- .batt_mult = 1000,
- .batt_div = 414,
- .temp_mult = 1,
- .temp_div = 1,
- .batt_tech = POWER_SUPPLY_TECHNOLOGY_LIPO,
- .batt_name = "main-batt",
-};
-
-/******************************************************************************
- * aSoC audio
- ******************************************************************************/
-static struct palm27x_asoc_info palmt5_asoc_pdata = {
- .jack_gpio = GPIO_NR_PALMT5_EARPHONE_DETECT,
-};
-
-static pxa2xx_audio_ops_t palmt5_ac97_pdata = {
- .reset_gpio = 95,
-};
-
-static struct platform_device palmt5_asoc = {
- .name = "palm27x-asoc",
- .id = -1,
- .dev = {
- .platform_data = &palmt5_asoc_pdata,
- },
-};
-
-/******************************************************************************
- * Framebuffer
- ******************************************************************************/
-static struct pxafb_mode_info palmt5_lcd_modes[] = {
-{
- .pixclock = 57692,
- .xres = 320,
- .yres = 480,
- .bpp = 16,
-
- .left_margin = 32,
- .right_margin = 1,
- .upper_margin = 7,
- .lower_margin = 1,
-
- .hsync_len = 4,
- .vsync_len = 1,
-},
-};
-
-static struct pxafb_mach_info palmt5_lcd_screen = {
- .modes = palmt5_lcd_modes,
- .num_modes = ARRAY_SIZE(palmt5_lcd_modes),
- .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL,
-};
-
-/******************************************************************************
- * Power management - standby
- ******************************************************************************/
-static void __init palmt5_pm_init(void)
+static void __init palmt5_keys_init(void)
{
- static u32 resume[] = {
- 0xe3a00101, /* mov r0, #0x40000000 */
- 0xe380060f, /* orr r0, r0, #0x00f00000 */
- 0xe590f008, /* ldr pc, [r0, #0x08] */
- };
-
- /* copy the bootloader */
- memcpy(phys_to_virt(PALMT5_STR_BASE), resume, sizeof(resume));
+ platform_device_register(&palmt5_pxa_keys);
}
+#else
+static inline void palmt5_keys_init(void) {}
+#endif
/******************************************************************************
* Machine init
******************************************************************************/
-static struct platform_device *devices[] __initdata = {
-#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
- &palmt5_pxa_keys,
-#endif
- &palmt5_backlight,
- &power_supply,
- &palmt5_asoc,
- &palmt5_gpio_vbus,
-};
-
-/* setup udc GPIOs initial state */
-static void __init palmt5_udc_init(void)
-{
- if (!gpio_request(GPIO_NR_PALMT5_USB_PULLUP, "UDC Vbus")) {
- gpio_direction_output(GPIO_NR_PALMT5_USB_PULLUP, 1);
- gpio_free(GPIO_NR_PALMT5_USB_PULLUP);
- }
-}
-
static void __init palmt5_reserve(void)
{
memblock_reserve(0xa0200000, 0x1000);
static void __init palmt5_init(void)
{
pxa2xx_mfp_config(ARRAY_AND_SIZE(palmt5_pin_config));
-
pxa_set_ffuart_info(NULL);
pxa_set_btuart_info(NULL);
pxa_set_stuart_info(NULL);
- palmt5_pm_init();
- set_pxa_fb_info(&palmt5_lcd_screen);
- pxa_set_mci_info(&palmt5_mci_platform_data);
- palmt5_udc_init();
- pxa_set_ac97_info(&palmt5_ac97_pdata);
- pxa_set_ficp_info(&palmt5_ficp_platform_data);
- pxa_set_keypad_info(&palmt5_keypad_platform_data);
- wm97xx_bat_set_pdata(&wm97xx_batt_pdata);
-
- platform_add_devices(devices, ARRAY_SIZE(devices));
+ palm27x_mmc_init(GPIO_NR_PALMT5_SD_DETECT_N, GPIO_NR_PALMT5_SD_READONLY,
+ GPIO_NR_PALMT5_SD_POWER, 0);
+ palm27x_pm_init(PALMT5_STR_BASE);
+ palm27x_lcd_init(-1, &palm_320x480_lcd_mode);
+ palm27x_udc_init(GPIO_NR_PALMT5_USB_DETECT_N,
+ GPIO_NR_PALMT5_USB_PULLUP, 1);
+ palm27x_irda_init(GPIO_NR_PALMT5_IR_DISABLE);
+ palm27x_ac97_init(PALMT5_BAT_MIN_VOLTAGE, PALMT5_BAT_MAX_VOLTAGE,
+ GPIO_NR_PALMT5_EARPHONE_DETECT, 95);
+ palm27x_pwm_init(GPIO_NR_PALMT5_BL_POWER, GPIO_NR_PALMT5_LCD_POWER);
+ palm27x_power_init(GPIO_NR_PALMT5_POWER_DETECT, -1);
+ palm27x_pmic_init();
+ palmt5_kpc_init();
+ palmt5_keys_init();
}
MACHINE_START(PALMT5, "Palm Tungsten|T5")
#include <linux/pda_power.h>
#include <linux/pwm_backlight.h>
#include <linux/gpio.h>
-#include <linux/wm97xx_batt.h>
+#include <linux/wm97xx.h>
#include <linux/power_supply.h>
#include <linux/usb/gpio_vbus.h>
};
/******************************************************************************
- * WM97xx battery
+ * WM97xx audio, battery
******************************************************************************/
-static struct wm97xx_batt_info wm97xx_batt_pdata = {
+static struct wm97xx_batt_pdata palmte2_batt_pdata = {
.batt_aux = WM97XX_AUX_ID3,
.temp_aux = WM97XX_AUX_ID2,
.charge_gpio = -1,
.batt_name = "main-batt",
};
-/******************************************************************************
- * aSoC audio
- ******************************************************************************/
+static struct wm97xx_pdata palmte2_wm97xx_pdata = {
+ .batt_pdata = &palmte2_batt_pdata,
+};
+
+static pxa2xx_audio_ops_t palmte2_ac97_pdata = {
+ .codec_pdata = { &palmte2_wm97xx_pdata, },
+};
+
static struct palm27x_asoc_info palmte2_asoc_pdata = {
.jack_gpio = GPIO_NR_PALMTE2_EARPHONE_DETECT,
};
set_pxa_fb_info(&palmte2_lcd_screen);
pxa_set_mci_info(&palmte2_mci_platform_data);
palmte2_udc_init();
- pxa_set_ac97_info(NULL);
+ pxa_set_ac97_info(&palmte2_ac97_pdata);
pxa_set_ficp_info(&palmte2_ficp_platform_data);
- wm97xx_bat_set_pdata(&wm97xx_batt_pdata);
platform_add_devices(devices, ARRAY_SIZE(devices));
}
#include <linux/pda_power.h>
#include <linux/pwm_backlight.h>
#include <linux/gpio.h>
-#include <linux/wm97xx_batt.h>
#include <linux/power_supply.h>
#include <linux/sysdev.h>
#include <linux/w1-gpio.h>
#include <mach/pxa2xx-regs.h>
#include <mach/palmasoc.h>
#include <mach/camera.h>
+#include <mach/palm27x.h>
#include <sound/pxa2xx-lib.h>
};
#endif /* CONFIG_MACH_CENTRO */
-/******************************************************************************
- * SD/MMC card controller
- ******************************************************************************/
-#ifdef CONFIG_MACH_TREO680
-static struct pxamci_platform_data treo680_mci_platform_data = {
- .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34,
- .gpio_card_detect = GPIO_NR_TREO_SD_DETECT_N,
- .gpio_card_ro = GPIO_NR_TREO680_SD_READONLY,
- .gpio_power = GPIO_NR_TREO680_SD_POWER,
-};
-#endif /* CONFIG_MACH_TREO680 */
-
-#ifdef CONFIG_MACH_CENTRO
-static struct pxamci_platform_data centro_mci_platform_data = {
- .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34,
- .gpio_card_detect = GPIO_NR_TREO_SD_DETECT_N,
- .gpio_card_ro = -1,
- .gpio_power = GPIO_NR_CENTRO_SD_POWER,
- .gpio_power_invert = 1,
-};
-#endif /* CONFIG_MACH_CENTRO */
-
/******************************************************************************
* GPIO keyboard
******************************************************************************/
-#ifdef CONFIG_MACH_TREO680
+#if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE)
static unsigned int treo680_matrix_keys[] = {
KEY(0, 0, KEY_F8), /* Red/Off/Power */
KEY(0, 1, KEY_LEFT),
KEY(7, 5, KEY_I),
};
-static struct pxa27x_keypad_platform_data treo680_keypad_platform_data = {
- .matrix_key_rows = 8,
- .matrix_key_cols = 7,
- .matrix_key_map = treo680_matrix_keys,
- .matrix_key_map_size = ARRAY_SIZE(treo680_matrix_keys),
- .direct_key_map = { KEY_CONNECT },
- .direct_key_num = 1,
-
- .debounce_interval = 30,
-};
-#endif /* CONFIG_MACH_TREO680 */
-
-#ifdef CONFIG_MACH_CENTRO
static unsigned int centro_matrix_keys[] = {
KEY(0, 0, KEY_F9), /* Home */
KEY(0, 1, KEY_LEFT),
KEY(7, 5, KEY_I),
};
-static struct pxa27x_keypad_platform_data centro_keypad_platform_data = {
+static struct pxa27x_keypad_platform_data treo680_keypad_pdata = {
.matrix_key_rows = 8,
.matrix_key_cols = 7,
- .matrix_key_map = centro_matrix_keys,
- .matrix_key_map_size = ARRAY_SIZE(centro_matrix_keys),
+ .matrix_key_map = treo680_matrix_keys,
+ .matrix_key_map_size = ARRAY_SIZE(treo680_matrix_keys),
.direct_key_map = { KEY_CONNECT },
.direct_key_num = 1,
.debounce_interval = 30,
};
-#endif /* CONFIG_MACH_CENTRO */
-/******************************************************************************
- * aSoC audio
- ******************************************************************************/
-
-static pxa2xx_audio_ops_t treo_ac97_pdata = {
- .reset_gpio = 95,
-};
-
-/******************************************************************************
- * Backlight
- ******************************************************************************/
-static int treo_backlight_init(struct device *dev)
+static void __init palmtreo_kpc_init(void)
{
- int ret;
-
- ret = gpio_request(GPIO_NR_TREO_BL_POWER, "BL POWER");
- if (ret)
- goto err;
- ret = gpio_direction_output(GPIO_NR_TREO_BL_POWER, 0);
- if (ret)
- goto err2;
-
- return 0;
+ static struct pxa27x_keypad_platform_data *data = &treo680_keypad_pdata;
-err2:
- gpio_free(GPIO_NR_TREO_BL_POWER);
-err:
- return ret;
-}
-
-static int treo_backlight_notify(struct device *dev, int brightness)
-{
- gpio_set_value(GPIO_NR_TREO_BL_POWER, brightness);
- return TREO_MAX_INTENSITY - brightness;
-};
+ if (machine_is_centro()) {
+ data->matrix_key_map = centro_matrix_keys;
+ data->matrix_key_map_size = ARRAY_SIZE(centro_matrix_keys);
+ }
-static void treo_backlight_exit(struct device *dev)
-{
- gpio_free(GPIO_NR_TREO_BL_POWER);
+ pxa_set_keypad_info(&treo680_keypad_pdata);
}
-
-static struct platform_pwm_backlight_data treo_backlight_data = {
- .pwm_id = 0,
- .max_brightness = TREO_MAX_INTENSITY,
- .dft_brightness = TREO_DEFAULT_INTENSITY,
- .pwm_period_ns = TREO_PERIOD_NS,
- .init = treo_backlight_init,
- .notify = treo_backlight_notify,
- .exit = treo_backlight_exit,
-};
-
-static struct platform_device treo_backlight = {
- .name = "pwm-backlight",
- .dev = {
- .parent = &pxa27x_device_pwm0.dev,
- .platform_data = &treo_backlight_data,
- },
-};
-
-/******************************************************************************
- * IrDA
- ******************************************************************************/
-static struct pxaficp_platform_data treo_ficp_info = {
- .gpio_pwdown = GPIO_NR_TREO_IR_EN,
- .transceiver_cap = IR_SIRMODE | IR_OFF,
-};
-
-/******************************************************************************
- * UDC
- ******************************************************************************/
-static struct pxa2xx_udc_mach_info treo_udc_info __initdata = {
- .gpio_vbus = GPIO_NR_TREO_USB_DETECT,
- .gpio_vbus_inverted = 1,
- .gpio_pullup = GPIO_NR_TREO_USB_PULLUP,
-};
-
+#else
+static inline void palmtreo_kpc_init(void) {}
+#endif
/******************************************************************************
* USB host
******************************************************************************/
-#ifdef CONFIG_MACH_TREO680
+#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
static struct pxaohci_platform_data treo680_ohci_info = {
.port_mode = PMM_PERPORT_MODE,
.flags = ENABLE_PORT1 | ENABLE_PORT3,
.power_budget = 0,
};
-#endif /* CONFIG_MACH_TREO680 */
-/******************************************************************************
- * Power supply
- ******************************************************************************/
-static int power_supply_init(struct device *dev)
+static void __init palmtreo_uhc_init(void)
{
- int ret;
-
- ret = gpio_request(GPIO_NR_TREO_POWER_DETECT, "CABLE_STATE_AC");
- if (ret)
- goto err1;
- ret = gpio_direction_input(GPIO_NR_TREO_POWER_DETECT);
- if (ret)
- goto err2;
-
- return 0;
-
-err2:
- gpio_free(GPIO_NR_TREO_POWER_DETECT);
-err1:
- return ret;
-}
-
-static int treo_is_ac_online(void)
-{
- return gpio_get_value(GPIO_NR_TREO_POWER_DETECT);
+ if (machine_is_treo680())
+ pxa_set_ohci_info(&treo680_ohci_info);
}
-
-static void power_supply_exit(struct device *dev)
-{
- gpio_free(GPIO_NR_TREO_POWER_DETECT);
-}
-
-static char *treo_supplicants[] = {
- "main-battery",
-};
-
-static struct pda_power_pdata power_supply_info = {
- .init = power_supply_init,
- .is_ac_online = treo_is_ac_online,
- .exit = power_supply_exit,
- .supplied_to = treo_supplicants,
- .num_supplicants = ARRAY_SIZE(treo_supplicants),
-};
-
-static struct platform_device power_supply = {
- .name = "pda-power",
- .id = -1,
- .dev = {
- .platform_data = &power_supply_info,
- },
-};
+#else
+static inline void palmtreo_uhc_init(void) {}
+#endif
/******************************************************************************
* Vibra and LEDs
.num_leds = ARRAY_SIZE(treo680_gpio_leds),
};
-static struct platform_device treo680_leds = {
- .name = "leds-gpio",
- .id = -1,
- .dev = {
- .platform_data = &treo680_gpio_led_info,
- }
-};
-#endif /* CONFIG_MACH_TREO680 */
-
-#ifdef CONFIG_MACH_CENTRO
static struct gpio_led centro_gpio_leds[] = {
{
.name = "centro:vibra:vibra",
.num_leds = ARRAY_SIZE(centro_gpio_leds),
};
-static struct platform_device centro_leds = {
+static struct platform_device palmtreo_leds = {
.name = "leds-gpio",
.id = -1,
.dev = {
- .platform_data = ¢ro_gpio_led_info,
+ .platform_data = &treo680_gpio_led_info,
}
};
-#endif /* CONFIG_MACH_CENTRO */
-
-/******************************************************************************
- * Framebuffer
- ******************************************************************************/
-/* TODO: add support for 324x324 */
-static struct pxafb_mode_info treo_lcd_modes[] = {
-{
- .pixclock = 86538,
- .xres = 320,
- .yres = 320,
- .bpp = 16,
-
- .left_margin = 20,
- .right_margin = 8,
- .upper_margin = 8,
- .lower_margin = 5,
-
- .hsync_len = 4,
- .vsync_len = 1,
-},
-};
-static void treo_lcd_power(int on, struct fb_var_screeninfo *info)
+static void __init palmtreo_leds_init(void)
{
- gpio_set_value(GPIO_NR_TREO_BL_POWER, on);
-}
-
-static struct pxafb_mach_info treo_lcd_screen = {
- .modes = treo_lcd_modes,
- .num_modes = ARRAY_SIZE(treo_lcd_modes),
- .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL,
-};
+ if (machine_is_centro())
+ palmtreo_leds.dev.platform_data = ¢ro_gpio_led_info;
-/******************************************************************************
- * Power management - standby
- ******************************************************************************/
-static void __init treo_pm_init(void)
-{
- static u32 resume[] = {
- 0xe3a00101, /* mov r0, #0x40000000 */
- 0xe380060f, /* orr r0, r0, #0x00f00000 */
- 0xe590f008, /* ldr pc, [r0, #0x08] */
- };
-
- /* this is where the bootloader jumps */
- memcpy(phys_to_virt(TREO_STR_BASE), resume, sizeof(resume));
+ platform_device_register(&palmtreo_leds);
}
+#else
+static inline void palmtreo_leds_init(void) {}
+#endif
/******************************************************************************
* Machine init
******************************************************************************/
-static struct platform_device *treo_devices[] __initdata = {
- &treo_backlight,
- &power_supply,
-};
-
-#ifdef CONFIG_MACH_TREO680
-static struct platform_device *treo680_devices[] __initdata = {
- &treo680_leds,
-};
-#endif /* CONFIG_MACH_TREO680 */
-
-#ifdef CONFIG_MACH_CENTRO
-static struct platform_device *centro_devices[] __initdata = {
- ¢ro_leds,
-};
-#endif /* CONFIG_MACH_CENTRO */
-
-/* setup udc GPIOs initial state */
-static void __init treo_udc_init(void)
-{
- if (!gpio_request(GPIO_NR_TREO_USB_PULLUP, "UDC Vbus")) {
- gpio_direction_output(GPIO_NR_TREO_USB_PULLUP, 1);
- gpio_free(GPIO_NR_TREO_USB_PULLUP);
- }
-}
-
-static void __init treo_lcd_power_init(void)
-{
- int ret;
-
- ret = gpio_request(GPIO_NR_TREO_LCD_POWER, "LCD POWER");
- if (ret) {
- pr_err("Treo680: LCD power GPIO request failed!\n");
- return;
- }
-
- ret = gpio_direction_output(GPIO_NR_TREO_LCD_POWER, 0);
- if (ret) {
- pr_err("Treo680: setting LCD power GPIO direction failed!\n");
- gpio_free(GPIO_NR_TREO_LCD_POWER);
- return;
- }
-
- treo_lcd_screen.pxafb_lcd_power = treo_lcd_power;
-}
-
static void __init treo_reserve(void)
{
memblock_reserve(0xa0000000, 0x1000);
memblock_reserve(0xa2000000, 0x1000);
}
-static void __init treo_init(void)
+static void __init palmphone_common_init(void)
{
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(treo_pin_config));
pxa_set_ffuart_info(NULL);
pxa_set_btuart_info(NULL);
pxa_set_stuart_info(NULL);
-
- treo_pm_init();
- pxa2xx_mfp_config(ARRAY_AND_SIZE(treo_pin_config));
- treo_lcd_power_init();
- set_pxa_fb_info(&treo_lcd_screen);
- treo_udc_init();
- pxa_set_udc_info(&treo_udc_info);
- pxa_set_ac97_info(&treo_ac97_pdata);
- pxa_set_ficp_info(&treo_ficp_info);
-
- platform_add_devices(ARRAY_AND_SIZE(treo_devices));
+ palm27x_pm_init(TREO_STR_BASE);
+ palm27x_lcd_init(GPIO_NR_TREO_BL_POWER, &palm_320x320_new_lcd_mode);
+ palm27x_udc_init(GPIO_NR_TREO_USB_DETECT, GPIO_NR_TREO_USB_PULLUP, 1);
+ palm27x_irda_init(GPIO_NR_TREO_IR_EN);
+ palm27x_ac97_init(-1, -1, -1, 95);
+ palm27x_pwm_init(GPIO_NR_TREO_BL_POWER, -1);
+ palm27x_power_init(GPIO_NR_TREO_POWER_DETECT, -1);
+ palm27x_pmic_init();
+ palmtreo_kpc_init();
+ palmtreo_uhc_init();
+ palmtreo_leds_init();
}
-#ifdef CONFIG_MACH_TREO680
static void __init treo680_init(void)
{
- treo_init();
pxa2xx_mfp_config(ARRAY_AND_SIZE(treo680_pin_config));
- pxa_set_mci_info(&treo680_mci_platform_data);
- pxa_set_keypad_info(&treo680_keypad_platform_data);
- pxa_set_ohci_info(&treo680_ohci_info);
+ palmphone_common_init();
+ palm27x_mmc_init(GPIO_NR_TREO_SD_DETECT_N, GPIO_NR_TREO680_SD_READONLY,
+ GPIO_NR_TREO680_SD_POWER, 0);
+}
- platform_add_devices(ARRAY_AND_SIZE(treo680_devices));
+static void __init centro_init(void)
+{
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(centro685_pin_config));
+ palmphone_common_init();
+ palm27x_mmc_init(GPIO_NR_TREO_SD_DETECT_N, -1,
+ GPIO_NR_CENTRO_SD_POWER, 1);
}
MACHINE_START(TREO680, "Palm Treo 680")
.timer = &pxa_timer,
.init_machine = treo680_init,
MACHINE_END
-#endif /* CONFIG_MACH_TREO680 */
-
-#ifdef CONFIG_MACH_CENTRO
-static void __init centro_init(void)
-{
- treo_init();
- pxa2xx_mfp_config(ARRAY_AND_SIZE(centro685_pin_config));
- pxa_set_mci_info(¢ro_mci_platform_data);
-
- pxa_set_keypad_info(¢ro_keypad_platform_data);
-
- platform_add_devices(ARRAY_AND_SIZE(centro_devices));
-}
MACHINE_START(CENTRO, "Palm Centro 685")
.phys_io = TREO_PHYS_IO_START,
.reserve = treo_reserve,
.init_irq = pxa27x_init_irq,
.timer = &pxa_timer,
- .init_machine = centro_init,
+ .init_machine = centro_init,
MACHINE_END
-#endif /* CONFIG_MACH_CENTRO */
#include <linux/pda_power.h>
#include <linux/pwm_backlight.h>
#include <linux/gpio.h>
-#include <linux/wm97xx_batt.h>
+#include <linux/wm97xx.h>
#include <linux/power_supply.h>
#include <linux/usb/gpio_vbus.h>
#include <linux/mtd/nand.h>
#include <mach/pxa27x_keypad.h>
#include <mach/udc.h>
#include <mach/palmasoc.h>
+#include <mach/palm27x.h>
#include "generic.h"
#include "devices.h"
/******************************************************************************
* NOR Flash
******************************************************************************/
+#if defined(CONFIG_MTD_PHYSMAP) || defined(CONFIG_MTD_PHYSMAP_MODULE)
static struct mtd_partition palmtx_partitions[] = {
{
.name = "Flash",
},
};
-/******************************************************************************
- * SD/MMC card controller
- ******************************************************************************/
-static struct pxamci_platform_data palmtx_mci_platform_data = {
- .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34,
- .gpio_card_detect = GPIO_NR_PALMTX_SD_DETECT_N,
- .gpio_card_ro = GPIO_NR_PALMTX_SD_READONLY,
- .gpio_power = GPIO_NR_PALMTX_SD_POWER,
- .detect_delay_ms = 200,
-};
+static void __init palmtx_nor_init(void)
+{
+ platform_device_register(&palmtx_flash);
+}
+#else
+static inline void palmtx_nor_init(void) {}
+#endif
/******************************************************************************
* GPIO keyboard
******************************************************************************/
+#if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE)
static unsigned int palmtx_matrix_keys[] = {
KEY(0, 0, KEY_POWER),
KEY(0, 1, KEY_F1),
.debounce_interval = 30,
};
+static void __init palmtx_kpc_init(void)
+{
+ pxa_set_keypad_info(&palmtx_keypad_platform_data);
+}
+#else
+static inline void palmtx_kpc_init(void) {}
+#endif
+
/******************************************************************************
* GPIO keys
******************************************************************************/
+#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
static struct gpio_keys_button palmtx_pxa_buttons[] = {
{KEY_F8, GPIO_NR_PALMTX_HOTSYNC_BUTTON_N, 1, "HotSync Button" },
};
},
};
-/******************************************************************************
- * Backlight
- ******************************************************************************/
-static int palmtx_backlight_init(struct device *dev)
-{
- int ret;
-
- ret = gpio_request(GPIO_NR_PALMTX_BL_POWER, "BL POWER");
- if (ret)
- goto err;
- ret = gpio_direction_output(GPIO_NR_PALMTX_BL_POWER, 0);
- if (ret)
- goto err2;
- ret = gpio_request(GPIO_NR_PALMTX_LCD_POWER, "LCD POWER");
- if (ret)
- goto err2;
- ret = gpio_direction_output(GPIO_NR_PALMTX_LCD_POWER, 0);
- if (ret)
- goto err3;
-
- return 0;
-err3:
- gpio_free(GPIO_NR_PALMTX_LCD_POWER);
-err2:
- gpio_free(GPIO_NR_PALMTX_BL_POWER);
-err:
- return ret;
-}
-
-static int palmtx_backlight_notify(struct device *dev, int brightness)
-{
- gpio_set_value(GPIO_NR_PALMTX_BL_POWER, brightness);
- gpio_set_value(GPIO_NR_PALMTX_LCD_POWER, brightness);
- return brightness;
-}
-
-static void palmtx_backlight_exit(struct device *dev)
-{
- gpio_free(GPIO_NR_PALMTX_BL_POWER);
- gpio_free(GPIO_NR_PALMTX_LCD_POWER);
-}
-
-static struct platform_pwm_backlight_data palmtx_backlight_data = {
- .pwm_id = 0,
- .max_brightness = PALMTX_MAX_INTENSITY,
- .dft_brightness = PALMTX_MAX_INTENSITY,
- .pwm_period_ns = PALMTX_PERIOD_NS,
- .init = palmtx_backlight_init,
- .notify = palmtx_backlight_notify,
- .exit = palmtx_backlight_exit,
-};
-
-static struct platform_device palmtx_backlight = {
- .name = "pwm-backlight",
- .dev = {
- .parent = &pxa27x_device_pwm0.dev,
- .platform_data = &palmtx_backlight_data,
- },
-};
-
-/******************************************************************************
- * IrDA
- ******************************************************************************/
-static struct pxaficp_platform_data palmtx_ficp_platform_data = {
- .gpio_pwdown = GPIO_NR_PALMTX_IR_DISABLE,
- .transceiver_cap = IR_SIRMODE | IR_OFF,
-};
-
-/******************************************************************************
- * UDC
- ******************************************************************************/
-static struct gpio_vbus_mach_info palmtx_udc_info = {
- .gpio_vbus = GPIO_NR_PALMTX_USB_DETECT_N,
- .gpio_vbus_inverted = 1,
- .gpio_pullup = GPIO_NR_PALMTX_USB_PULLUP,
-};
-
-static struct platform_device palmtx_gpio_vbus = {
- .name = "gpio-vbus",
- .id = -1,
- .dev = {
- .platform_data = &palmtx_udc_info,
- },
-};
-
-/******************************************************************************
- * Power supply
- ******************************************************************************/
-static int power_supply_init(struct device *dev)
-{
- int ret;
-
- ret = gpio_request(GPIO_NR_PALMTX_POWER_DETECT, "CABLE_STATE_AC");
- if (ret)
- goto err1;
- ret = gpio_direction_input(GPIO_NR_PALMTX_POWER_DETECT);
- if (ret)
- goto err2;
-
- return 0;
-
-err2:
- gpio_free(GPIO_NR_PALMTX_POWER_DETECT);
-err1:
- return ret;
-}
-
-static int palmtx_is_ac_online(void)
-{
- return gpio_get_value(GPIO_NR_PALMTX_POWER_DETECT);
-}
-
-static void power_supply_exit(struct device *dev)
+static void __init palmtx_keys_init(void)
{
- gpio_free(GPIO_NR_PALMTX_POWER_DETECT);
+ platform_device_register(&palmtx_pxa_keys);
}
-
-static char *palmtx_supplicants[] = {
- "main-battery",
-};
-
-static struct pda_power_pdata power_supply_info = {
- .init = power_supply_init,
- .is_ac_online = palmtx_is_ac_online,
- .exit = power_supply_exit,
- .supplied_to = palmtx_supplicants,
- .num_supplicants = ARRAY_SIZE(palmtx_supplicants),
-};
-
-static struct platform_device power_supply = {
- .name = "pda-power",
- .id = -1,
- .dev = {
- .platform_data = &power_supply_info,
- },
-};
-
-/******************************************************************************
- * WM97xx battery
- ******************************************************************************/
-static struct wm97xx_batt_info wm97xx_batt_pdata = {
- .batt_aux = WM97XX_AUX_ID3,
- .temp_aux = WM97XX_AUX_ID2,
- .charge_gpio = -1,
- .max_voltage = PALMTX_BAT_MAX_VOLTAGE,
- .min_voltage = PALMTX_BAT_MIN_VOLTAGE,
- .batt_mult = 1000,
- .batt_div = 414,
- .temp_mult = 1,
- .temp_div = 1,
- .batt_tech = POWER_SUPPLY_TECHNOLOGY_LIPO,
- .batt_name = "main-batt",
-};
-
-/******************************************************************************
- * aSoC audio
- ******************************************************************************/
-static struct palm27x_asoc_info palmtx_asoc_pdata = {
- .jack_gpio = GPIO_NR_PALMTX_EARPHONE_DETECT,
-};
-
-static pxa2xx_audio_ops_t palmtx_ac97_pdata = {
- .reset_gpio = 95,
-};
-
-static struct platform_device palmtx_asoc = {
- .name = "palm27x-asoc",
- .id = -1,
- .dev = {
- .platform_data = &palmtx_asoc_pdata,
- },
-};
-
-/******************************************************************************
- * Framebuffer
- ******************************************************************************/
-static struct pxafb_mode_info palmtx_lcd_modes[] = {
-{
- .pixclock = 57692,
- .xres = 320,
- .yres = 480,
- .bpp = 16,
-
- .left_margin = 32,
- .right_margin = 1,
- .upper_margin = 7,
- .lower_margin = 1,
-
- .hsync_len = 4,
- .vsync_len = 1,
-},
-};
-
-static struct pxafb_mach_info palmtx_lcd_screen = {
- .modes = palmtx_lcd_modes,
- .num_modes = ARRAY_SIZE(palmtx_lcd_modes),
- .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL,
-};
+#else
+static inline void palmtx_keys_init(void) {}
+#endif
/******************************************************************************
* NAND Flash
******************************************************************************/
+#if defined(CONFIG_MTD_NAND_GPIO) || defined(CONFIG_MTD_NAND_GPIO_MODULE)
static void palmtx_nand_cmd_ctl(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
}
};
-/******************************************************************************
- * Power management - standby
- ******************************************************************************/
-static void __init palmtx_pm_init(void)
+static void __init palmtx_nand_init(void)
{
- static u32 resume[] = {
- 0xe3a00101, /* mov r0, #0x40000000 */
- 0xe380060f, /* orr r0, r0, #0x00f00000 */
- 0xe590f008, /* ldr pc, [r0, #0x08] */
- };
-
- /* copy the bootloader */
- memcpy(phys_to_virt(PALMTX_STR_BASE), resume, sizeof(resume));
+ platform_device_register(&palmtx_nand);
}
+#else
+static inline void palmtx_nand_init(void) {}
+#endif
/******************************************************************************
* Machine init
******************************************************************************/
-static struct platform_device *devices[] __initdata = {
-#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
- &palmtx_pxa_keys,
-#endif
- &palmtx_backlight,
- &power_supply,
- &palmtx_asoc,
- &palmtx_gpio_vbus,
- &palmtx_flash,
- &palmtx_nand,
-};
-
static struct map_desc palmtx_io_desc[] __initdata = {
{
.virtual = PALMTX_PCMCIA_VIRT,
iotable_init(palmtx_io_desc, ARRAY_SIZE(palmtx_io_desc));
}
-/* setup udc GPIOs initial state */
-static void __init palmtx_udc_init(void)
-{
- if (!gpio_request(GPIO_NR_PALMTX_USB_PULLUP, "UDC Vbus")) {
- gpio_direction_output(GPIO_NR_PALMTX_USB_PULLUP, 1);
- gpio_free(GPIO_NR_PALMTX_USB_PULLUP);
- }
-}
-
-
static void __init palmtx_init(void)
{
pxa2xx_mfp_config(ARRAY_AND_SIZE(palmtx_pin_config));
-
pxa_set_ffuart_info(NULL);
pxa_set_btuart_info(NULL);
pxa_set_stuart_info(NULL);
- palmtx_pm_init();
- set_pxa_fb_info(&palmtx_lcd_screen);
- pxa_set_mci_info(&palmtx_mci_platform_data);
- palmtx_udc_init();
- pxa_set_ac97_info(&palmtx_ac97_pdata);
- pxa_set_ficp_info(&palmtx_ficp_platform_data);
- pxa_set_keypad_info(&palmtx_keypad_platform_data);
- wm97xx_bat_set_pdata(&wm97xx_batt_pdata);
-
- platform_add_devices(devices, ARRAY_SIZE(devices));
+ palm27x_mmc_init(GPIO_NR_PALMTX_SD_DETECT_N, GPIO_NR_PALMTX_SD_READONLY,
+ GPIO_NR_PALMTX_SD_POWER, 0);
+ palm27x_pm_init(PALMTX_STR_BASE);
+ palm27x_lcd_init(-1, &palm_320x480_lcd_mode);
+ palm27x_udc_init(GPIO_NR_PALMTX_USB_DETECT_N,
+ GPIO_NR_PALMTX_USB_PULLUP, 1);
+ palm27x_irda_init(GPIO_NR_PALMTX_IR_DISABLE);
+ palm27x_ac97_init(PALMTX_BAT_MIN_VOLTAGE, PALMTX_BAT_MAX_VOLTAGE,
+ GPIO_NR_PALMTX_EARPHONE_DETECT, 95);
+ palm27x_pwm_init(GPIO_NR_PALMTX_BL_POWER, GPIO_NR_PALMTX_LCD_POWER);
+ palm27x_power_init(GPIO_NR_PALMTX_POWER_DETECT, -1);
+ palm27x_pmic_init();
+ palmtx_kpc_init();
+ palmtx_keys_init();
+ palmtx_nor_init();
+ palmtx_nand_init();
}
MACHINE_START(PALMTX, "Palm T|X")
#include <linux/pda_power.h>
#include <linux/pwm_backlight.h>
#include <linux/gpio.h>
-#include <linux/wm97xx_batt.h>
+#include <linux/wm97xx.h>
#include <linux/power_supply.h>
#include <linux/usb/gpio_vbus.h>
#include <mach/pxa27x_keypad.h>
#include <mach/udc.h>
#include <mach/palmasoc.h>
+#include <mach/palm27x.h>
#include <mach/pm.h>
GPIO27_GPIO, /* WM9712 IRQ */
};
-/******************************************************************************
- * SD/MMC card controller
- ******************************************************************************/
-/* SD_POWER is not actually power, but it is more like chip
- * select, i.e. it is inverted */
-static struct pxamci_platform_data palmz72_mci_platform_data = {
- .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34,
- .gpio_card_detect = GPIO_NR_PALMZ72_SD_DETECT_N,
- .gpio_card_ro = GPIO_NR_PALMZ72_SD_RO,
- .gpio_power = GPIO_NR_PALMZ72_SD_POWER_N,
- .gpio_power_invert = 1,
-};
-
/******************************************************************************
* GPIO keyboard
******************************************************************************/
+#if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE)
static unsigned int palmz72_matrix_keys[] = {
KEY(0, 0, KEY_POWER),
KEY(0, 1, KEY_F1),
.debounce_interval = 30,
};
-/******************************************************************************
- * Backlight
- ******************************************************************************/
-static int palmz72_backlight_init(struct device *dev)
+static void __init palmz72_kpc_init(void)
{
- int ret;
-
- ret = gpio_request(GPIO_NR_PALMZ72_BL_POWER, "BL POWER");
- if (ret)
- goto err;
- ret = gpio_direction_output(GPIO_NR_PALMZ72_BL_POWER, 0);
- if (ret)
- goto err2;
- ret = gpio_request(GPIO_NR_PALMZ72_LCD_POWER, "LCD POWER");
- if (ret)
- goto err2;
- ret = gpio_direction_output(GPIO_NR_PALMZ72_LCD_POWER, 0);
- if (ret)
- goto err3;
-
- return 0;
-err3:
- gpio_free(GPIO_NR_PALMZ72_LCD_POWER);
-err2:
- gpio_free(GPIO_NR_PALMZ72_BL_POWER);
-err:
- return ret;
-}
-
-static int palmz72_backlight_notify(struct device *dev, int brightness)
-{
- gpio_set_value(GPIO_NR_PALMZ72_BL_POWER, brightness);
- gpio_set_value(GPIO_NR_PALMZ72_LCD_POWER, brightness);
- return brightness;
-}
-
-static void palmz72_backlight_exit(struct device *dev)
-{
- gpio_free(GPIO_NR_PALMZ72_BL_POWER);
- gpio_free(GPIO_NR_PALMZ72_LCD_POWER);
+ pxa_set_keypad_info(&palmz72_keypad_platform_data);
}
-
-static struct platform_pwm_backlight_data palmz72_backlight_data = {
- .pwm_id = 0,
- .max_brightness = PALMZ72_MAX_INTENSITY,
- .dft_brightness = PALMZ72_MAX_INTENSITY,
- .pwm_period_ns = PALMZ72_PERIOD_NS,
- .init = palmz72_backlight_init,
- .notify = palmz72_backlight_notify,
- .exit = palmz72_backlight_exit,
-};
-
-static struct platform_device palmz72_backlight = {
- .name = "pwm-backlight",
- .dev = {
- .parent = &pxa27x_device_pwm0.dev,
- .platform_data = &palmz72_backlight_data,
- },
-};
-
-/******************************************************************************
- * IrDA
- ******************************************************************************/
-static struct pxaficp_platform_data palmz72_ficp_platform_data = {
- .gpio_pwdown = GPIO_NR_PALMZ72_IR_DISABLE,
- .transceiver_cap = IR_SIRMODE | IR_OFF,
-};
+#else
+static inline void palmz72_kpc_init(void) {}
+#endif
/******************************************************************************
* LEDs
******************************************************************************/
+#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE)
static struct gpio_led gpio_leds[] = {
{
.name = "palmz72:green:led",
}
};
-/******************************************************************************
- * UDC
- ******************************************************************************/
-static struct gpio_vbus_mach_info palmz72_udc_info = {
- .gpio_vbus = GPIO_NR_PALMZ72_USB_DETECT_N,
- .gpio_pullup = GPIO_NR_PALMZ72_USB_PULLUP,
-};
-
-static struct platform_device palmz72_gpio_vbus = {
- .name = "gpio-vbus",
- .id = -1,
- .dev = {
- .platform_data = &palmz72_udc_info,
- },
-};
-
-/******************************************************************************
- * Power supply
- ******************************************************************************/
-static int power_supply_init(struct device *dev)
-{
- int ret;
-
- ret = gpio_request(GPIO_NR_PALMZ72_POWER_DETECT, "CABLE_STATE_AC");
- if (ret)
- goto err1;
- ret = gpio_direction_input(GPIO_NR_PALMZ72_POWER_DETECT);
- if (ret)
- goto err2;
-
- ret = gpio_request(GPIO_NR_PALMZ72_USB_DETECT_N, "CABLE_STATE_USB");
- if (ret)
- goto err2;
- ret = gpio_direction_input(GPIO_NR_PALMZ72_USB_DETECT_N);
- if (ret)
- goto err3;
-
- return 0;
-err3:
- gpio_free(GPIO_NR_PALMZ72_USB_DETECT_N);
-err2:
- gpio_free(GPIO_NR_PALMZ72_POWER_DETECT);
-err1:
- return ret;
-}
-
-static int palmz72_is_ac_online(void)
-{
- return gpio_get_value(GPIO_NR_PALMZ72_POWER_DETECT);
-}
-
-static int palmz72_is_usb_online(void)
+static void __init palmz72_leds_init(void)
{
- return !gpio_get_value(GPIO_NR_PALMZ72_USB_DETECT_N);
+ platform_device_register(&palmz72_leds);
}
-
-static void power_supply_exit(struct device *dev)
-{
- gpio_free(GPIO_NR_PALMZ72_USB_DETECT_N);
- gpio_free(GPIO_NR_PALMZ72_POWER_DETECT);
-}
-
-static char *palmz72_supplicants[] = {
- "main-battery",
-};
-
-static struct pda_power_pdata power_supply_info = {
- .init = power_supply_init,
- .is_ac_online = palmz72_is_ac_online,
- .is_usb_online = palmz72_is_usb_online,
- .exit = power_supply_exit,
- .supplied_to = palmz72_supplicants,
- .num_supplicants = ARRAY_SIZE(palmz72_supplicants),
-};
-
-static struct platform_device power_supply = {
- .name = "pda-power",
- .id = -1,
- .dev = {
- .platform_data = &power_supply_info,
- },
-};
-
-/******************************************************************************
- * WM97xx battery
- ******************************************************************************/
-static struct wm97xx_batt_info wm97xx_batt_pdata = {
- .batt_aux = WM97XX_AUX_ID3,
- .temp_aux = WM97XX_AUX_ID2,
- .charge_gpio = -1,
- .max_voltage = PALMZ72_BAT_MAX_VOLTAGE,
- .min_voltage = PALMZ72_BAT_MIN_VOLTAGE,
- .batt_mult = 1000,
- .batt_div = 414,
- .temp_mult = 1,
- .temp_div = 1,
- .batt_tech = POWER_SUPPLY_TECHNOLOGY_LIPO,
- .batt_name = "main-batt",
-};
-
-/******************************************************************************
- * aSoC audio
- ******************************************************************************/
-static struct platform_device palmz72_asoc = {
- .name = "palm27x-asoc",
- .id = -1,
-};
-
-/******************************************************************************
- * Framebuffer
- ******************************************************************************/
-static struct pxafb_mode_info palmz72_lcd_modes[] = {
-{
- .pixclock = 115384,
- .xres = 320,
- .yres = 320,
- .bpp = 16,
-
- .left_margin = 27,
- .right_margin = 7,
- .upper_margin = 7,
- .lower_margin = 8,
-
- .hsync_len = 6,
- .vsync_len = 1,
-},
-};
-
-static struct pxafb_mach_info palmz72_lcd_screen = {
- .modes = palmz72_lcd_modes,
- .num_modes = ARRAY_SIZE(palmz72_lcd_modes),
- .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL,
-};
+#else
+static inline void palmz72_leds_init(void) {}
+#endif
#ifdef CONFIG_PM
/******************************************************************************
* Machine init
******************************************************************************/
-static struct platform_device *devices[] __initdata = {
- &palmz72_backlight,
- &palmz72_leds,
- &palmz72_asoc,
- &power_supply,
- &palmz72_gpio_vbus,
-};
-
-/* setup udc GPIOs initial state */
-static void __init palmz72_udc_init(void)
-{
- if (!gpio_request(GPIO_NR_PALMZ72_USB_PULLUP, "USB Pullup")) {
- gpio_direction_output(GPIO_NR_PALMZ72_USB_PULLUP, 0);
- gpio_free(GPIO_NR_PALMZ72_USB_PULLUP);
- }
-}
-
static void __init palmz72_init(void)
{
pxa2xx_mfp_config(ARRAY_AND_SIZE(palmz72_pin_config));
-
pxa_set_ffuart_info(NULL);
pxa_set_btuart_info(NULL);
pxa_set_stuart_info(NULL);
- set_pxa_fb_info(&palmz72_lcd_screen);
- pxa_set_mci_info(&palmz72_mci_platform_data);
- palmz72_udc_init();
- pxa_set_ac97_info(NULL);
- pxa_set_ficp_info(&palmz72_ficp_platform_data);
- pxa_set_keypad_info(&palmz72_keypad_platform_data);
- wm97xx_bat_set_pdata(&wm97xx_batt_pdata);
-
- platform_add_devices(devices, ARRAY_SIZE(devices));
+ palm27x_mmc_init(GPIO_NR_PALMZ72_SD_DETECT_N, GPIO_NR_PALMZ72_SD_RO,
+ GPIO_NR_PALMZ72_SD_POWER_N, 1);
+ palm27x_lcd_init(-1, &palm_320x320_lcd_mode);
+ palm27x_udc_init(GPIO_NR_PALMZ72_USB_DETECT_N,
+ GPIO_NR_PALMZ72_USB_PULLUP, 0);
+ palm27x_irda_init(GPIO_NR_PALMZ72_IR_DISABLE);
+ palm27x_ac97_init(PALMZ72_BAT_MIN_VOLTAGE, PALMZ72_BAT_MAX_VOLTAGE,
+ -1, 113);
+ palm27x_pwm_init(-1, -1);
+ palm27x_power_init(-1, -1);
+ palm27x_pmic_init();
+ palmz72_kpc_init();
+ palmz72_leds_init();
}
MACHINE_START(PALMZ72, "Palm Zire72")
#include <mach/irda.h>
#include <mach/poodle.h>
#include <mach/pxafb.h>
-#include <mach/sharpsl.h>
#include <mach/pxa2xx_spi.h>
#include <plat/i2c.h>
#include "generic.h"
#include "devices.h"
-#include "sharpsl.h"
static unsigned long poodle_pin_config[] __initdata = {
/* I/O */
static struct platform_device *pxa25x_devices[] __initdata = {
&pxa25x_device_udc,
+ &pxa_device_pmu,
&pxa_device_i2s,
&sa1100_device_rtc,
&pxa25x_device_ssp,
static struct platform_device *devices[] __initdata = {
&pxa27x_device_udc,
+ &pxa_device_pmu,
&pxa_device_i2s,
&sa1100_device_rtc,
&pxa_device_rtc,
static unsigned char smcfs_mult[8] = { 6, 0, 8, 0, 0, 16, };
/* crystal frequency to HSIO bus frequency multiplier (HSS) */
-static unsigned char hss_mult[4] = { 8, 12, 16, 0 };
+static unsigned char hss_mult[4] = { 8, 12, 16, 24 };
/*
* Get the clock frequency as reflected by CCSR and the turbo flag.
PECR |= PECR_IE(irq - IRQ_WAKEUP0);
}
+static int pxa_set_ext_wakeup_type(unsigned int irq, unsigned int flow_type)
+{
+ if (flow_type & IRQ_TYPE_EDGE_RISING)
+ PWER |= 1 << (irq - IRQ_WAKEUP0);
+
+ if (flow_type & IRQ_TYPE_EDGE_FALLING)
+ PWER |= 1 << (irq - IRQ_WAKEUP0 + 2);
+
+ return 0;
+}
+
static struct irq_chip pxa_ext_wakeup_chip = {
.name = "WAKEUP",
.ack = pxa_ack_ext_wakeup,
.mask = pxa_mask_ext_wakeup,
.unmask = pxa_unmask_ext_wakeup,
+ .set_type = pxa_set_ext_wakeup_type,
};
static void __init pxa_init_ext_wakeup_irq(set_wake_t fn)
static struct platform_device *devices[] __initdata = {
&pxa27x_device_udc,
+ &pxa_device_pmu,
&pxa_device_i2s,
&sa1100_device_rtc,
&pxa_device_rtc,
static char *raumfeld_power_supplicants[] = { "ds2760-battery.0" };
+static void raumfeld_power_signal_charged(void)
+{
+ struct power_supply *psy =
+ power_supply_get_by_name(raumfeld_power_supplicants[0]);
+
+ if (psy)
+ power_supply_set_battery_charged(psy);
+}
+
+static int raumfeld_power_resume(void)
+{
+ /* check if GPIO_CHARGE_DONE went low while we were sleeping */
+ if (!gpio_get_value(GPIO_CHARGE_DONE))
+ raumfeld_power_signal_charged();
+
+ return 0;
+}
+
static struct pda_power_pdata power_supply_info = {
.init = power_supply_init,
.is_ac_online = raumfeld_is_ac_online,
.is_usb_online = raumfeld_is_usb_online,
.exit = power_supply_exit,
.supplied_to = raumfeld_power_supplicants,
- .num_supplicants = ARRAY_SIZE(raumfeld_power_supplicants)
+ .num_supplicants = ARRAY_SIZE(raumfeld_power_supplicants),
+ .resume = raumfeld_power_resume,
};
static struct resource power_supply_resources[] = {
static irqreturn_t charge_done_irq(int irq, void *dev_id)
{
- struct power_supply *psy;
-
- psy = power_supply_get_by_name("ds2760-battery.0");
-
- if (psy)
- power_supply_set_battery_charged(psy);
-
+ raumfeld_power_signal_charged();
return IRQ_HANDLED;
}
+++ /dev/null
-/*
- * Copyright (c) 2004-2005 Richard Purdie
- *
- * 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 <mach/sharpsl_pm.h>
-
-/*
- * SharpSL Battery/PM Driver
- */
-#define READ_GPIO_BIT(x) (GPLR(x) & GPIO_bit(x))
-
-/* MAX1111 Channel Definitions */
-#define MAX1111_BATT_VOLT 4u
-#define MAX1111_BATT_TEMP 2u
-#define MAX1111_ACIN_VOLT 6u
-
-extern struct battery_thresh sharpsl_battery_levels_acin[];
-extern struct battery_thresh sharpsl_battery_levels_noac[];
-int sharpsl_pm_pxa_read_max1111(int channel);
-
-
#include <mach/pm.h>
#include <mach/pxa2xx-regs.h>
#include <mach/regs-rtc.h>
-#include <mach/sharpsl.h>
#include <mach/sharpsl_pm.h>
-#include "sharpsl.h"
-
/*
* Constants
*/
if (machine_is_tosa())
return 0;
-#ifdef CONFIG_CORGI_SSP_DEPRECATED
- return corgi_ssp_max1111_get((channel << MAXCTRL_SEL_SH) | MAXCTRL_PD0 | MAXCTRL_PD1
- | MAXCTRL_SGL | MAXCTRL_UNI | MAXCTRL_STR);
-#else
extern int max1111_read_channel(int);
/* max1111 accepts channels from 0-3, however,
* it is encoded from 0-7 here in the code.
*/
return max1111_read_channel(channel >> 1);
-#endif
}
static int get_percentage(int voltage)
dev_dbg(sharpsl_pm.dev, "Battery: voltage: %d, status: %d, percentage: %d, time: %ld\n", voltage,
sharpsl_pm.battstat.mainbat_status, sharpsl_pm.battstat.mainbat_percent, jiffies);
-#ifdef CONFIG_BACKLIGHT_CORGI
- /* If battery is low. limit backlight intensity to save power. */
- if ((sharpsl_pm.battstat.ac_status != APM_AC_ONLINE)
- && ((sharpsl_pm.battstat.mainbat_status == APM_BATTERY_STATUS_LOW)
- || (sharpsl_pm.battstat.mainbat_status == APM_BATTERY_STATUS_CRITICAL))) {
- if (!(sharpsl_pm.flags & SHARPSL_BL_LIMIT)) {
- sharpsl_pm.machinfo->backlight_limit(1);
- sharpsl_pm.flags |= SHARPSL_BL_LIMIT;
- }
- } else if (sharpsl_pm.flags & SHARPSL_BL_LIMIT) {
- sharpsl_pm.machinfo->backlight_limit(0);
- sharpsl_pm.flags &= ~SHARPSL_BL_LIMIT;
- }
-#endif
-
/* Suspend if critical battery level */
if ((sharpsl_pm.battstat.ac_status != APM_AC_ONLINE)
&& (sharpsl_pm.battstat.mainbat_status == APM_BATTERY_STATUS_CRITICAL)
mcr p15, 0, r1, c8, c7, 0 @ invalidate I & D TLBs
mcr p15, 0, r1, c7, c7, 0 @ invalidate I & D caches, BTB
-#ifdef CONFIG_XSCALE_CACHE_ERRATA
- bic r9, r9, #0x0004 @ see cpu_xscale_proc_init
-#endif
-
mcr p14, 0, r3, c6, c0, 0 @ clock configuration, turbo mode.
mcr p15, 0, r4, c15, c1, 0 @ CP access reg
mcr p15, 0, r5, c13, c0, 0 @ PID
.text
resume_after_mmu:
-#ifdef CONFIG_XSCALE_CACHE_ERRATA
- bl cpu_xscale_proc_init
-#endif
ldmfd sp!, {r2, r3}
#ifndef CONFIG_IWMMXT
mar acc0, r2, r3
#include <linux/gpio_keys.h>
#include <linux/gpio.h>
#include <linux/leds.h>
-#include <linux/mtd/physmap.h>
#include <linux/i2c.h>
#include <linux/i2c/pca953x.h>
#include <linux/spi/spi.h>
#include <linux/spi/ads7846.h>
#include <linux/spi/corgi_lcd.h>
+#include <linux/mtd/physmap.h>
#include <linux/mtd/sharpsl.h>
#include <linux/input/matrix_keypad.h>
+#include <linux/regulator/machine.h>
#include <asm/setup.h>
#include <asm/mach-types.h>
#include <asm/mach/sharpsl_param.h>
#include <asm/hardware/scoop.h>
-
#include <mach/pxa27x.h>
#include <mach/pxa27x-udc.h>
#include <mach/reset.h>
-#include <plat/i2c.h>
#include <mach/irda.h>
#include <mach/mmc.h>
#include <mach/ohci.h>
#include <mach/pxafb.h>
#include <mach/pxa2xx_spi.h>
#include <mach/spitz.h>
+#include <mach/sharpsl_pm.h>
+
+#include <plat/i2c.h>
#include "generic.h"
#include "devices.h"
-#include "sharpsl.h"
+/******************************************************************************
+ * Pin configuration
+ ******************************************************************************/
static unsigned long spitz_pin_config[] __initdata = {
/* Chip Selects */
GPIO78_nCS_2, /* SCOOP #2 */
GPIO1_GPIO | WAKEUP_ON_EDGE_FALL, /* SPITZ_GPIO_RESET */
};
-/*
- * Spitz SCOOP Device #1
- */
-static struct resource spitz_scoop_resources[] = {
+
+/******************************************************************************
+ * Scoop GPIO expander
+ ******************************************************************************/
+#if defined(CONFIG_SHARP_SCOOP) || defined(CONFIG_SHARP_SCOOP_MODULE)
+/* SCOOP Device #1 */
+static struct resource spitz_scoop_1_resources[] = {
[0] = {
.start = 0x10800000,
.end = 0x10800fff,
},
};
-static struct scoop_config spitz_scoop_setup = {
+static struct scoop_config spitz_scoop_1_setup = {
.io_dir = SPITZ_SCP_IO_DIR,
.io_out = SPITZ_SCP_IO_OUT,
.suspend_clr = SPITZ_SCP_SUS_CLR,
.gpio_base = SPITZ_SCP_GPIO_BASE,
};
-struct platform_device spitzscoop_device = {
+struct platform_device spitz_scoop_1_device = {
.name = "sharp-scoop",
.id = 0,
.dev = {
- .platform_data = &spitz_scoop_setup,
+ .platform_data = &spitz_scoop_1_setup,
},
- .num_resources = ARRAY_SIZE(spitz_scoop_resources),
- .resource = spitz_scoop_resources,
+ .num_resources = ARRAY_SIZE(spitz_scoop_1_resources),
+ .resource = spitz_scoop_1_resources,
};
-/*
- * Spitz SCOOP Device #2
- */
-static struct resource spitz_scoop2_resources[] = {
+/* SCOOP Device #2 */
+static struct resource spitz_scoop_2_resources[] = {
[0] = {
.start = 0x08800040,
.end = 0x08800fff,
},
};
-static struct scoop_config spitz_scoop2_setup = {
+static struct scoop_config spitz_scoop_2_setup = {
.io_dir = SPITZ_SCP2_IO_DIR,
.io_out = SPITZ_SCP2_IO_OUT,
.suspend_clr = SPITZ_SCP2_SUS_CLR,
.gpio_base = SPITZ_SCP2_GPIO_BASE,
};
-struct platform_device spitzscoop2_device = {
+struct platform_device spitz_scoop_2_device = {
.name = "sharp-scoop",
.id = 1,
.dev = {
- .platform_data = &spitz_scoop2_setup,
+ .platform_data = &spitz_scoop_2_setup,
},
- .num_resources = ARRAY_SIZE(spitz_scoop2_resources),
- .resource = spitz_scoop2_resources,
+ .num_resources = ARRAY_SIZE(spitz_scoop_2_resources),
+ .resource = spitz_scoop_2_resources,
};
-#define SPITZ_PWR_SD 0x01
-#define SPITZ_PWR_CF 0x02
+static void __init spitz_scoop_init(void)
+{
+ platform_device_register(&spitz_scoop_1_device);
+
+ /* Akita doesn't have the second SCOOP chip */
+ if (!machine_is_akita())
+ platform_device_register(&spitz_scoop_2_device);
+}
/* Power control is shared with between one of the CF slots and SD */
-static void spitz_card_pwr_ctrl(int device, unsigned short new_cpr)
+static void spitz_card_pwr_ctrl(uint8_t enable, uint8_t new_cpr)
{
- unsigned short cpr = read_scoop_reg(&spitzscoop_device.dev, SCOOP_CPR);
+ unsigned short cpr;
+ unsigned long flags;
- if (new_cpr & 0x0007) {
+ if (new_cpr & 0x7) {
gpio_set_value(SPITZ_GPIO_CF_POWER, 1);
- if (!(cpr & 0x0002) && !(cpr & 0x0004))
- mdelay(5);
- if (device == SPITZ_PWR_CF)
- cpr |= 0x0002;
- if (device == SPITZ_PWR_SD)
- cpr |= 0x0004;
- write_scoop_reg(&spitzscoop_device.dev, SCOOP_CPR, cpr | new_cpr);
- } else {
- if (device == SPITZ_PWR_CF)
- cpr &= ~0x0002;
- if (device == SPITZ_PWR_SD)
- cpr &= ~0x0004;
- if (!(cpr & 0x0002) && !(cpr & 0x0004)) {
- write_scoop_reg(&spitzscoop_device.dev, SCOOP_CPR, 0x0000);
- mdelay(1);
- gpio_set_value(SPITZ_GPIO_CF_POWER, 0);
- } else {
- write_scoop_reg(&spitzscoop_device.dev, SCOOP_CPR, cpr | new_cpr);
- }
+ mdelay(5);
+ }
+
+ local_irq_save(flags);
+
+ cpr = read_scoop_reg(&spitz_scoop_1_device.dev, SCOOP_CPR);
+
+ if (enable & new_cpr)
+ cpr |= new_cpr;
+ else
+ cpr &= ~enable;
+
+ write_scoop_reg(&spitz_scoop_1_device.dev, SCOOP_CPR, cpr);
+
+ local_irq_restore(flags);
+
+ if (!(cpr & 0x7)) {
+ mdelay(1);
+ gpio_set_value(SPITZ_GPIO_CF_POWER, 0);
}
}
-static void spitz_pcmcia_pwr(struct device *scoop, unsigned short cpr, int nr)
+#else
+static inline void spitz_scoop_init(void) {}
+static inline void spitz_card_pwr_ctrl(uint8_t enable, uint8_t new_cpr) {}
+#endif
+
+/******************************************************************************
+ * PCMCIA
+ ******************************************************************************/
+#if defined(CONFIG_PCMCIA_PXA2XX) || defined(CONFIG_PCMCIA_PXA2XX_MODULE)
+static void spitz_pcmcia_pwr(struct device *scoop, uint16_t cpr, int nr)
{
/* Only need to override behaviour for slot 0 */
if (nr == 0)
- spitz_card_pwr_ctrl(SPITZ_PWR_CF, cpr);
+ spitz_card_pwr_ctrl(
+ cpr & (SCOOP_CPR_CF_3V | SCOOP_CPR_CF_XV), cpr);
else
write_scoop_reg(scoop, SCOOP_CPR, cpr);
}
static struct scoop_pcmcia_dev spitz_pcmcia_scoop[] = {
-{
- .dev = &spitzscoop_device.dev,
- .irq = SPITZ_IRQ_GPIO_CF_IRQ,
- .cd_irq = SPITZ_IRQ_GPIO_CF_CD,
- .cd_irq_str = "PCMCIA0 CD",
-},{
- .dev = &spitzscoop2_device.dev,
- .irq = SPITZ_IRQ_GPIO_CF2_IRQ,
- .cd_irq = -1,
-},
+ {
+ .dev = &spitz_scoop_1_device.dev,
+ .irq = SPITZ_IRQ_GPIO_CF_IRQ,
+ .cd_irq = SPITZ_IRQ_GPIO_CF_CD,
+ .cd_irq_str = "PCMCIA0 CD",
+ }, {
+ .dev = &spitz_scoop_2_device.dev,
+ .irq = SPITZ_IRQ_GPIO_CF2_IRQ,
+ .cd_irq = -1,
+ },
};
static struct scoop_pcmcia_config spitz_pcmcia_config = {
- .devs = &spitz_pcmcia_scoop[0],
- .num_devs = 2,
- .power_ctrl = spitz_pcmcia_pwr,
+ .devs = &spitz_pcmcia_scoop[0],
+ .num_devs = 2,
+ .power_ctrl = spitz_pcmcia_pwr,
};
-EXPORT_SYMBOL(spitzscoop_device);
-EXPORT_SYMBOL(spitzscoop2_device);
+static void __init spitz_pcmcia_init(void)
+{
+ /* Akita has only one PCMCIA slot used */
+ if (machine_is_akita())
+ spitz_pcmcia_config.num_devs = 1;
+
+ platform_scoop_config = &spitz_pcmcia_config;
+}
+#else
+static inline void spitz_pcmcia_init(void) {}
+#endif
+
+/******************************************************************************
+ * GPIO keyboard
+ ******************************************************************************/
+#if defined(CONFIG_KEYBOARD_MATRIX) || defined(CONFIG_KEYBOARD_MATRIX_MODULE)
-/*
- * Spitz Keyboard Device
- */
#define SPITZ_KEY_CALENDAR KEY_F1
#define SPITZ_KEY_ADDRESS KEY_F2
#define SPITZ_KEY_FN KEY_F3
#define SPITZ_KEY_OK KEY_F11
#define SPITZ_KEY_MENU KEY_F12
-static const uint32_t spitzkbd_keymap[] = {
+static const uint32_t spitz_keymap[] = {
KEY(0, 0, KEY_LEFTCTRL),
KEY(0, 1, KEY_1),
KEY(0, 2, KEY_3),
KEY(6, 8, KEY_RIGHT),
};
-static const struct matrix_keymap_data spitzkbd_keymap_data = {
- .keymap = spitzkbd_keymap,
- .keymap_size = ARRAY_SIZE(spitzkbd_keymap),
+static const struct matrix_keymap_data spitz_keymap_data = {
+ .keymap = spitz_keymap,
+ .keymap_size = ARRAY_SIZE(spitz_keymap),
};
-static const uint32_t spitzkbd_row_gpios[] =
+static const uint32_t spitz_row_gpios[] =
{ 12, 17, 91, 34, 36, 38, 39 };
-static const uint32_t spitzkbd_col_gpios[] =
+static const uint32_t spitz_col_gpios[] =
{ 88, 23, 24, 25, 26, 27, 52, 103, 107, 108, 114 };
-static struct matrix_keypad_platform_data spitzkbd_pdata = {
- .keymap_data = &spitzkbd_keymap_data,
- .row_gpios = spitzkbd_row_gpios,
- .col_gpios = spitzkbd_col_gpios,
- .num_row_gpios = ARRAY_SIZE(spitzkbd_row_gpios),
- .num_col_gpios = ARRAY_SIZE(spitzkbd_col_gpios),
+static struct matrix_keypad_platform_data spitz_mkp_pdata = {
+ .keymap_data = &spitz_keymap_data,
+ .row_gpios = spitz_row_gpios,
+ .col_gpios = spitz_col_gpios,
+ .num_row_gpios = ARRAY_SIZE(spitz_row_gpios),
+ .num_col_gpios = ARRAY_SIZE(spitz_col_gpios),
.col_scan_delay_us = 10,
.debounce_ms = 10,
.wakeup = 1,
};
-static struct platform_device spitzkbd_device = {
+static struct platform_device spitz_mkp_device = {
.name = "matrix-keypad",
.id = -1,
.dev = {
- .platform_data = &spitzkbd_pdata,
+ .platform_data = &spitz_mkp_pdata,
},
};
+static void __init spitz_mkp_init(void)
+{
+ platform_device_register(&spitz_mkp_device);
+}
+#else
+static inline void spitz_mkp_init(void) {}
+#endif
+/******************************************************************************
+ * GPIO keys
+ ******************************************************************************/
+#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
static struct gpio_keys_button spitz_gpio_keys[] = {
{
.type = EV_PWR,
},
};
+static void __init spitz_keys_init(void)
+{
+ platform_device_register(&spitz_gpio_keys_device);
+}
+#else
+static inline void spitz_keys_init(void) {}
+#endif
-/*
- * Spitz LEDs
- */
+/******************************************************************************
+ * LEDs
+ ******************************************************************************/
+#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE)
static struct gpio_led spitz_gpio_leds[] = {
{
.name = "spitz:amber:charge",
.num_leds = ARRAY_SIZE(spitz_gpio_leds),
};
-static struct platform_device spitzled_device = {
+static struct platform_device spitz_led_device = {
.name = "leds-gpio",
.id = -1,
.dev = {
- .platform_data = &spitz_gpio_leds_info,
+ .platform_data = &spitz_gpio_leds_info,
},
};
-#if defined(CONFIG_SPI_PXA2XX) || defined(CONFIG_SPI_PXA2XX_MODULE)
-static struct pxa2xx_spi_master spitz_spi_info = {
- .num_chipselect = 3,
-};
+static void __init spitz_leds_init(void)
+{
+ platform_device_register(&spitz_led_device);
+}
+#else
+static inline void spitz_leds_init(void) {}
+#endif
-static void spitz_wait_for_hsync(void)
+/******************************************************************************
+ * SSP Devices
+ ******************************************************************************/
+#if defined(CONFIG_SPI_PXA2XX) || defined(CONFIG_SPI_PXA2XX_MODULE)
+static void spitz_ads7846_wait_for_hsync(void)
{
while (gpio_get_value(SPITZ_GPIO_HSYNC))
cpu_relax();
.y_plate_ohms = 486,
.pressure_max = 1024,
.gpio_pendown = SPITZ_GPIO_TP_INT,
- .wait_for_sync = spitz_wait_for_hsync,
+ .wait_for_sync = spitz_ads7846_wait_for_hsync,
};
static struct pxa2xx_spi_chip spitz_ads7846_chip = {
static struct spi_board_info spitz_spi_devices[] = {
{
- .modalias = "ads7846",
- .max_speed_hz = 1200000,
- .bus_num = 2,
- .chip_select = 0,
- .platform_data = &spitz_ads7846_info,
- .controller_data= &spitz_ads7846_chip,
- .irq = gpio_to_irq(SPITZ_GPIO_TP_INT),
+ .modalias = "ads7846",
+ .max_speed_hz = 1200000,
+ .bus_num = 2,
+ .chip_select = 0,
+ .platform_data = &spitz_ads7846_info,
+ .controller_data = &spitz_ads7846_chip,
+ .irq = gpio_to_irq(SPITZ_GPIO_TP_INT),
}, {
- .modalias = "corgi-lcd",
- .max_speed_hz = 50000,
- .bus_num = 2,
- .chip_select = 1,
- .platform_data = &spitz_lcdcon_info,
- .controller_data= &spitz_lcdcon_chip,
+ .modalias = "corgi-lcd",
+ .max_speed_hz = 50000,
+ .bus_num = 2,
+ .chip_select = 1,
+ .platform_data = &spitz_lcdcon_info,
+ .controller_data = &spitz_lcdcon_chip,
}, {
- .modalias = "max1111",
- .max_speed_hz = 450000,
- .bus_num = 2,
- .chip_select = 2,
- .controller_data= &spitz_max1111_chip,
+ .modalias = "max1111",
+ .max_speed_hz = 450000,
+ .bus_num = 2,
+ .chip_select = 2,
+ .controller_data = &spitz_max1111_chip,
},
};
-static void __init spitz_init_spi(void)
+static struct pxa2xx_spi_master spitz_spi_info = {
+ .num_chipselect = 3,
+};
+
+static void __init spitz_spi_init(void)
{
+ struct corgi_lcd_platform_data *lcd_data = &spitz_lcdcon_info;
+
if (machine_is_akita()) {
- spitz_lcdcon_info.gpio_backlight_cont = AKITA_GPIO_BACKLIGHT_CONT;
- spitz_lcdcon_info.gpio_backlight_on = AKITA_GPIO_BACKLIGHT_ON;
+ lcd_data->gpio_backlight_cont = AKITA_GPIO_BACKLIGHT_CONT;
+ lcd_data->gpio_backlight_on = AKITA_GPIO_BACKLIGHT_ON;
}
pxa2xx_set_spi_info(2, &spitz_spi_info);
spi_register_board_info(ARRAY_AND_SIZE(spitz_spi_devices));
}
#else
-static inline void spitz_init_spi(void) {}
+static inline void spitz_spi_init(void) {}
#endif
+/******************************************************************************
+ * SD/MMC card controller
+ ******************************************************************************/
+#if defined(CONFIG_MMC_PXA) || defined(CONFIG_MMC_PXA_MODULE)
/*
- * MMC/SD Device
- *
- * The card detect interrupt isn't debounced so we delay it by 250ms
- * to give the card a chance to fully insert/eject.
+ * NOTE: The card detect interrupt isn't debounced so we delay it by 250ms to
+ * give the card a chance to fully insert/eject.
*/
static void spitz_mci_setpower(struct device *dev, unsigned int vdd)
{
struct pxamci_platform_data* p_d = dev->platform_data;
- if (( 1 << vdd) & p_d->ocr_mask)
- spitz_card_pwr_ctrl(SPITZ_PWR_SD, 0x0004);
+ if ((1 << vdd) & p_d->ocr_mask)
+ spitz_card_pwr_ctrl(SCOOP_CPR_SD_3V, SCOOP_CPR_SD_3V);
else
- spitz_card_pwr_ctrl(SPITZ_PWR_SD, 0x0000);
+ spitz_card_pwr_ctrl(SCOOP_CPR_SD_3V, 0x0);
}
static struct pxamci_platform_data spitz_mci_platform_data = {
.detect_delay_ms = 250,
.ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
- .setpower = spitz_mci_setpower,
+ .setpower = spitz_mci_setpower,
.gpio_card_detect = SPITZ_GPIO_nSD_DETECT,
.gpio_card_ro = SPITZ_GPIO_nSD_WP,
.gpio_power = -1,
};
+static void __init spitz_mmc_init(void)
+{
+ pxa_set_mci_info(&spitz_mci_platform_data);
+}
+#else
+static inline void spitz_mmc_init(void) {}
+#endif
-/*
- * USB Host (OHCI)
- */
+/******************************************************************************
+ * USB Host
+ ******************************************************************************/
+#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
static int spitz_ohci_init(struct device *dev)
{
int err;
if (err)
return err;
- /* Only Port 2 is connected
- * Setup USB Port 2 Output Control Register
- */
+ /* Only Port 2 is connected, setup USB Port 2 Output Control Register */
UP2OCR = UP2OCR_HXS | UP2OCR_HXOE | UP2OCR_DPPDE | UP2OCR_DMPDE;
return gpio_direction_output(SPITZ_GPIO_USB_HOST, 1);
.power_budget = 150,
};
+static void __init spitz_uhc_init(void)
+{
+ pxa_set_ohci_info(&spitz_ohci_platform_data);
+}
+#else
+static inline void spitz_uhc_init(void) {}
+#endif
-/*
- * Irda
- */
-
+/******************************************************************************
+ * IrDA
+ ******************************************************************************/
+#if defined(CONFIG_PXA_FICP) || defined(CONFIG_PXA_FICP_MODULE)
static struct pxaficp_platform_data spitz_ficp_platform_data = {
-/* .gpio_pwdown is set in spitz_init() and akita_init() accordingly */
.transceiver_cap = IR_SIRMODE | IR_OFF,
};
+static void __init spitz_irda_init(void)
+{
+ if (machine_is_akita())
+ spitz_ficp_platform_data.gpio_pwdown = AKITA_GPIO_IR_ON;
+ else
+ spitz_ficp_platform_data.gpio_pwdown = SPITZ_GPIO_IR_ON;
-/*
- * Spitz PXA Framebuffer
- */
+ pxa_set_ficp_info(&spitz_ficp_platform_data);
+}
+#else
+static inline void spitz_irda_init(void) {}
+#endif
+/******************************************************************************
+ * Framebuffer
+ ******************************************************************************/
+#if defined(CONFIG_FB_PXA) || defined(CONFIG_FB_PXA_MODULE)
static struct pxafb_mode_info spitz_pxafb_modes[] = {
-{
- .pixclock = 19231,
- .xres = 480,
- .yres = 640,
- .bpp = 16,
- .hsync_len = 40,
- .left_margin = 46,
- .right_margin = 125,
- .vsync_len = 3,
- .upper_margin = 1,
- .lower_margin = 0,
- .sync = 0,
-},{
- .pixclock = 134617,
- .xres = 240,
- .yres = 320,
- .bpp = 16,
- .hsync_len = 20,
- .left_margin = 20,
- .right_margin = 46,
- .vsync_len = 2,
- .upper_margin = 1,
- .lower_margin = 0,
- .sync = 0,
-},
+ {
+ .pixclock = 19231,
+ .xres = 480,
+ .yres = 640,
+ .bpp = 16,
+ .hsync_len = 40,
+ .left_margin = 46,
+ .right_margin = 125,
+ .vsync_len = 3,
+ .upper_margin = 1,
+ .lower_margin = 0,
+ .sync = 0,
+ }, {
+ .pixclock = 134617,
+ .xres = 240,
+ .yres = 320,
+ .bpp = 16,
+ .hsync_len = 20,
+ .left_margin = 20,
+ .right_margin = 46,
+ .vsync_len = 2,
+ .upper_margin = 1,
+ .lower_margin = 0,
+ .sync = 0,
+ },
};
static struct pxafb_mach_info spitz_pxafb_info = {
- .modes = &spitz_pxafb_modes[0],
- .num_modes = 2,
+ .modes = spitz_pxafb_modes,
+ .num_modes = ARRAY_SIZE(spitz_pxafb_modes),
.fixed_modes = 1,
.lcd_conn = LCD_COLOR_TFT_16BPP | LCD_ALTERNATE_MAPPING,
};
-static struct mtd_partition sharpsl_nand_partitions[] = {
+static void __init spitz_lcd_init(void)
+{
+ set_pxa_fb_info(&spitz_pxafb_info);
+}
+#else
+static inline void spitz_lcd_init(void) {}
+#endif
+
+/******************************************************************************
+ * Framebuffer
+ ******************************************************************************/
+#if defined(CONFIG_MTD_NAND_SHARPSL) || defined(CONFIG_MTD_NAND_SHARPSL_MODULE)
+static struct mtd_partition spitz_nand_partitions[] = {
{
.name = "System Area",
.offset = 0,
.size = 7 * 1024 * 1024,
- },
- {
+ }, {
.name = "Root Filesystem",
.offset = 7 * 1024 * 1024,
- },
- {
+ }, {
.name = "Home Filesystem",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
-static struct nand_bbt_descr sharpsl_bbt = {
- .options = 0,
- .offs = 4,
- .len = 2,
- .pattern = scan_ff_pattern
+static struct nand_bbt_descr spitz_nand_bbt = {
+ .options = 0,
+ .offs = 4,
+ .len = 2,
+ .pattern = scan_ff_pattern
+};
+
+static struct nand_ecclayout akita_oobinfo = {
+ .oobfree = { {0x08, 0x09} },
+ .eccbytes = 24,
+ .eccpos = {
+ 0x05, 0x01, 0x02, 0x03, 0x06, 0x07, 0x15, 0x11,
+ 0x12, 0x13, 0x16, 0x17, 0x25, 0x21, 0x22, 0x23,
+ 0x26, 0x27, 0x35, 0x31, 0x32, 0x33, 0x36, 0x37,
+ },
};
-static struct sharpsl_nand_platform_data sharpsl_nand_platform_data = {
- .badblock_pattern = &sharpsl_bbt,
- .partitions = sharpsl_nand_partitions,
- .nr_partitions = ARRAY_SIZE(sharpsl_nand_partitions),
+static struct sharpsl_nand_platform_data spitz_nand_pdata = {
+ .badblock_pattern = &spitz_nand_bbt,
+ .partitions = spitz_nand_partitions,
+ .nr_partitions = ARRAY_SIZE(spitz_nand_partitions),
};
-static struct resource sharpsl_nand_resources[] = {
+static struct resource spitz_nand_resources[] = {
{
- .start = 0x0C000000,
- .end = 0x0C000FFF,
+ .start = PXA_CS3_PHYS,
+ .end = PXA_CS3_PHYS + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
};
-static struct platform_device sharpsl_nand_device = {
+static struct platform_device spitz_nand_device = {
.name = "sharpsl-nand",
.id = -1,
- .resource = sharpsl_nand_resources,
- .num_resources = ARRAY_SIZE(sharpsl_nand_resources),
- .dev.platform_data = &sharpsl_nand_platform_data,
+ .resource = spitz_nand_resources,
+ .num_resources = ARRAY_SIZE(spitz_nand_resources),
+ .dev = {
+ .platform_data = &spitz_nand_pdata,
+ }
};
+static void __init spitz_nand_init(void)
+{
+ if (machine_is_spitz()) {
+ spitz_nand_partitions[1].size = 5 * 1024 * 1024;
+ } else if (machine_is_akita()) {
+ spitz_nand_partitions[1].size = 58 * 1024 * 1024;
+ spitz_nand_bbt.len = 1;
+ spitz_nand_pdata.ecc_layout = &akita_oobinfo;
+ } else if (machine_is_borzoi()) {
+ spitz_nand_partitions[1].size = 32 * 1024 * 1024;
+ spitz_nand_bbt.len = 1;
+ spitz_nand_pdata.ecc_layout = &akita_oobinfo;
+ }
+
+ platform_device_register(&spitz_nand_device);
+}
+#else
+static inline void spitz_nand_init(void) {}
+#endif
-static struct mtd_partition sharpsl_rom_parts[] = {
+/******************************************************************************
+ * NOR Flash
+ ******************************************************************************/
+#if defined(CONFIG_MTD_PHYSMAP) || defined(CONFIG_MTD_PHYSMAP_MODULE)
+static struct mtd_partition spitz_rom_parts[] = {
{
.name ="Boot PROM Filesystem",
.offset = 0x00140000,
},
};
-static struct physmap_flash_data sharpsl_rom_data = {
+static struct physmap_flash_data spitz_rom_data = {
.width = 2,
- .nr_parts = ARRAY_SIZE(sharpsl_rom_parts),
- .parts = sharpsl_rom_parts,
+ .nr_parts = ARRAY_SIZE(spitz_rom_parts),
+ .parts = spitz_rom_parts,
};
-static struct resource sharpsl_rom_resources[] = {
+static struct resource spitz_rom_resources[] = {
{
- .start = 0x00000000,
- .end = 0x007fffff,
+ .start = PXA_CS0_PHYS,
+ .end = PXA_CS0_PHYS + SZ_8M - 1,
.flags = IORESOURCE_MEM,
},
};
-static struct platform_device sharpsl_rom_device = {
- .name = "physmap-flash",
- .id = -1,
- .resource = sharpsl_rom_resources,
- .num_resources = ARRAY_SIZE(sharpsl_rom_resources),
- .dev.platform_data = &sharpsl_rom_data,
+static struct platform_device spitz_rom_device = {
+ .name = "physmap-flash",
+ .id = -1,
+ .resource = spitz_rom_resources,
+ .num_resources = ARRAY_SIZE(spitz_rom_resources),
+ .dev = {
+ .platform_data = &spitz_rom_data,
+ },
};
-static struct platform_device *devices[] __initdata = {
- &spitzscoop_device,
- &spitzkbd_device,
- &spitz_gpio_keys_device,
- &spitzled_device,
- &sharpsl_nand_device,
- &sharpsl_rom_device,
+static void __init spitz_nor_init(void)
+{
+ platform_device_register(&spitz_rom_device);
+}
+#else
+static inline void spitz_nor_init(void) {}
+#endif
+
+/******************************************************************************
+ * GPIO expander
+ ******************************************************************************/
+#if defined(CONFIG_I2C_PXA) || defined(CONFIG_I2C_PXA_MODULE)
+static struct pca953x_platform_data akita_pca953x_pdata = {
+ .gpio_base = AKITA_IOEXP_GPIO_BASE,
};
+static struct i2c_board_info spitz_i2c_devs[] = {
+ {
+ .type = "wm8750",
+ .addr = 0x1b,
+ }, {
+ .type = "max7310",
+ .addr = 0x18,
+ .platform_data = &akita_pca953x_pdata,
+ },
+};
+
+static struct regulator_consumer_supply isl6271a_consumers[] = {
+ {
+ .supply = "vcc_core",
+ }
+};
+
+static struct regulator_init_data isl6271a_info[] = {
+ {
+ .constraints = {
+ .name = "vcc_core range",
+ .min_uV = 850000,
+ .max_uV = 1600000,
+ .always_on = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ },
+ .consumer_supplies = isl6271a_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(isl6271a_consumers),
+ }
+};
+
+static struct i2c_board_info spitz_pi2c_devs[] = {
+ {
+ .type = "isl6271a",
+ .addr = 0x0c,
+ .platform_data = &isl6271a_info,
+ },
+};
+
+static void __init spitz_i2c_init(void)
+{
+ int size = ARRAY_SIZE(spitz_i2c_devs);
+
+ /* Only Akita has the max7310 chip */
+ if (!machine_is_akita())
+ size--;
+
+ pxa_set_i2c_info(NULL);
+ pxa27x_set_i2c_power_info(NULL);
+ i2c_register_board_info(0, spitz_i2c_devs, size);
+ i2c_register_board_info(1, ARRAY_AND_SIZE(spitz_pi2c_devs));
+}
+#else
+static inline void spitz_i2c_init(void) {}
+#endif
+
+/******************************************************************************
+ * Machine init
+ ******************************************************************************/
static void spitz_poweroff(void)
{
arm_machine_restart('g', NULL);
static void spitz_restart(char mode, const char *cmd)
{
/* Bootloader magic for a reboot */
- if((MSC0 & 0xffff0000) == 0x7ff00000)
+ if ((MSC0 & 0xffff0000) == 0x7ff00000)
MSC0 = (MSC0 & 0xffff) | 0x7ee00000;
spitz_poweroff();
}
-static void __init common_init(void)
+static void __init spitz_init(void)
{
init_gpio_reset(SPITZ_GPIO_ON_RESET, 1, 0);
pm_power_off = spitz_poweroff;
arm_pm_restart = spitz_restart;
- if (machine_is_spitz()) {
- sharpsl_nand_partitions[1].size = 5 * 1024 * 1024;
- } else if (machine_is_akita()) {
- sharpsl_nand_partitions[1].size = 58 * 1024 * 1024;
- } else if (machine_is_borzoi()) {
- sharpsl_nand_partitions[1].size = 32 * 1024 * 1024;
- }
-
PMCR = 0x00;
/* Stop 3.6MHz and drive HIGH to PCMCIA and CS */
pxa_set_btuart_info(NULL);
pxa_set_stuart_info(NULL);
- spitz_init_spi();
-
- platform_add_devices(devices, ARRAY_SIZE(devices));
- pxa_set_mci_info(&spitz_mci_platform_data);
- pxa_set_ohci_info(&spitz_ohci_platform_data);
- pxa_set_ficp_info(&spitz_ficp_platform_data);
- set_pxa_fb_info(&spitz_pxafb_info);
- pxa_set_i2c_info(NULL);
+ spitz_spi_init();
+ spitz_scoop_init();
+ spitz_mkp_init();
+ spitz_keys_init();
+ spitz_leds_init();
+ spitz_mmc_init();
+ spitz_pcmcia_init();
+ spitz_irda_init();
+ spitz_uhc_init();
+ spitz_lcd_init();
+ spitz_nor_init();
+ spitz_nand_init();
+ spitz_i2c_init();
}
-#if defined(CONFIG_MACH_AKITA) || defined(CONFIG_MACH_BORZOI)
-static struct nand_bbt_descr sharpsl_akita_bbt = {
- .options = 0,
- .offs = 4,
- .len = 1,
- .pattern = scan_ff_pattern
-};
-
-static struct nand_ecclayout akita_oobinfo = {
- .eccbytes = 24,
- .eccpos = {
- 0x5, 0x1, 0x2, 0x3, 0x6, 0x7, 0x15, 0x11,
- 0x12, 0x13, 0x16, 0x17, 0x25, 0x21, 0x22, 0x23,
- 0x26, 0x27, 0x35, 0x31, 0x32, 0x33, 0x36, 0x37},
- .oobfree = {{0x08, 0x09}}
-};
-#endif
-
-#if defined(CONFIG_MACH_SPITZ) || defined(CONFIG_MACH_BORZOI)
-static void __init spitz_init(void)
-{
- spitz_ficp_platform_data.gpio_pwdown = SPITZ_GPIO_IR_ON;
-
-#ifdef CONFIG_MACH_BORZOI
- if (machine_is_borzoi()) {
- sharpsl_nand_platform_data.badblock_pattern = &sharpsl_akita_bbt;
- sharpsl_nand_platform_data.ecc_layout = &akita_oobinfo;
- }
-#endif
-
- platform_scoop_config = &spitz_pcmcia_config;
-
- common_init();
-
- platform_device_register(&spitzscoop2_device);
-}
-#endif
-
-#ifdef CONFIG_MACH_AKITA
-/*
- * Akita IO Expander
- */
-static struct pca953x_platform_data akita_ioexp = {
- .gpio_base = AKITA_IOEXP_GPIO_BASE,
-};
-
-static struct i2c_board_info akita_i2c_board_info[] = {
- {
- .type = "max7310",
- .addr = 0x18,
- .platform_data = &akita_ioexp,
- }, {
- .type = "wm8750",
- .addr = 0x1b,
- },
-};
-
-static void __init akita_init(void)
-{
- spitz_ficp_platform_data.gpio_pwdown = AKITA_GPIO_IR_ON;
-
- sharpsl_nand_platform_data.badblock_pattern = &sharpsl_akita_bbt;
- sharpsl_nand_platform_data.ecc_layout = &akita_oobinfo;
-
- /* We just pretend the second element of the array doesn't exist */
- spitz_pcmcia_config.num_devs = 1;
- platform_scoop_config = &spitz_pcmcia_config;
-
- i2c_register_board_info(0, ARRAY_AND_SIZE(akita_i2c_board_info));
-
- common_init();
-}
-#endif
-
-static void __init fixup_spitz(struct machine_desc *desc,
+static void __init spitz_fixup(struct machine_desc *desc,
struct tag *tags, char **cmdline, struct meminfo *mi)
{
sharpsl_save_param();
MACHINE_START(SPITZ, "SHARP Spitz")
.phys_io = 0x40000000,
.io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
- .fixup = fixup_spitz,
+ .fixup = spitz_fixup,
.map_io = pxa_map_io,
.init_irq = pxa27x_init_irq,
.init_machine = spitz_init,
MACHINE_START(BORZOI, "SHARP Borzoi")
.phys_io = 0x40000000,
.io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
- .fixup = fixup_spitz,
+ .fixup = spitz_fixup,
.map_io = pxa_map_io,
.init_irq = pxa27x_init_irq,
.init_machine = spitz_init,
MACHINE_START(AKITA, "SHARP Akita")
.phys_io = 0x40000000,
.io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
- .fixup = fixup_spitz,
+ .fixup = spitz_fixup,
.map_io = pxa_map_io,
.init_irq = pxa27x_init_irq,
- .init_machine = akita_init,
+ .init_machine = spitz_init,
.timer = &pxa_timer,
MACHINE_END
#endif
#include <asm/mach-types.h>
#include <mach/hardware.h>
-#include <mach/sharpsl.h>
#include <mach/spitz.h>
#include <mach/pxa27x.h>
+#include <mach/sharpsl_pm.h>
-#include "sharpsl.h"
#include "generic.h"
#define SHARPSL_CHARGE_ON_VOLT 0x99 /* 2.9V */
case SHARPSL_STATUS_ACIN:
return (((~GPLR(SPITZ_GPIO_AC_IN)) & GPIO_bit(SPITZ_GPIO_AC_IN)) != 0);
case SHARPSL_STATUS_LOCK:
- return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batlock);
+ return gpio_get_value(sharpsl_pm.machinfo->gpio_batlock);
case SHARPSL_STATUS_CHRGFULL:
- return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batfull);
+ return gpio_get_value(sharpsl_pm.machinfo->gpio_batfull);
case SHARPSL_STATUS_FATAL:
- return READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_fatal);
+ return gpio_get_value(sharpsl_pm.machinfo->gpio_fatal);
case SHARPSL_ACIN_VOLT:
return sharpsl_pm_pxa_read_max1111(MAX1111_ACIN_VOLT);
case SHARPSL_BATT_TEMP:
.should_wakeup = spitz_should_wakeup,
#if defined(CONFIG_LCD_CORGI)
.backlight_limit = corgi_lcd_limit_intensity,
-#elif defined(CONFIG_BACKLIGHT_CORGI)
- .backlight_limit = corgibl_limit_intensity,
#endif
.charge_on_volt = SHARPSL_CHARGE_ON_VOLT,
.charge_on_temp = SHARPSL_CHARGE_ON_TEMP,
#include <linux/i2c/at24.h>
#include <linux/smc91x.h>
#include <linux/gpio.h>
+#include <linux/leds.h>
#include <asm/types.h>
#include <asm/setup.h>
#define SG2_GPIO_nSD_DETECT 90
#define SG2_SD_POWER_ENABLE 89
-static unsigned long stargate2_pin_config[] __initdata = {
-
- GPIO15_nCS_1, /* SRAM */
- /* SMC91x */
- GPIO80_nCS_4,
- GPIO40_GPIO, /*cable detect?*/
+static unsigned long sg2_im2_unified_pin_config[] __initdata = {
/* Device Identification for wakeup*/
GPIO102_GPIO,
-
- /* Button */
- GPIO91_GPIO | WAKEUP_ON_LEVEL_HIGH,
-
/* DA9030 */
GPIO1_GPIO,
- /* Compact Flash */
- GPIO79_PSKTSEL,
- GPIO48_nPOE,
- GPIO49_nPWE,
- GPIO50_nPIOR,
- GPIO51_nPIOW,
- GPIO85_nPCE_1,
- GPIO54_nPCE_2,
- GPIO55_nPREG,
- GPIO56_nPWAIT,
- GPIO57_nIOIS16,
- GPIO120_GPIO, /* Buff ctrl */
- GPIO108_GPIO, /* Power ctrl */
- GPIO82_GPIO, /* Reset */
- GPIO53_GPIO, /* SG2_S0_GPIO_DETECT */
-
/* MMC */
GPIO32_MMC_CLK,
GPIO112_MMC_CMD,
GPIO109_MMC_DAT_1,
GPIO110_MMC_DAT_2,
GPIO111_MMC_DAT_3,
- GPIO90_GPIO, /* nSD detect */
- GPIO89_GPIO, /* SD_POWER_ENABLE */
- /* Bluetooth */
- GPIO81_GPIO, /* reset */
-
- /* cc2420 802.15.4 radio */
- GPIO22_GPIO, /* CC_RSTN (out)*/
- GPIO114_GPIO, /* CC_FIFO (in) */
- GPIO116_GPIO, /* CC_CCA (in) */
- GPIO0_GPIO, /* CC_FIFOP (in) */
- GPIO16_GPIO, /* CCSFD (in) */
- GPIO39_GPIO, /* CSn (out) */
+ /* 802.15.4 radio - driver out of mainline */
+ GPIO22_GPIO, /* CC_RSTN */
+ GPIO114_GPIO, /* CC_FIFO */
+ GPIO116_GPIO, /* CC_CCA */
+ GPIO0_GPIO, /* CC_FIFOP */
+ GPIO16_GPIO, /* CCSFD */
+ GPIO115_GPIO, /* Power enable */
/* I2C */
GPIO117_I2C_SCL,
GPIO118_I2C_SDA,
/* SSP 3 - 802.15.4 radio */
- GPIO39_GPIO, /* chip select */
+ GPIO39_GPIO, /* Chip Select */
GPIO34_SSP3_SCLK,
GPIO35_SSP3_TXD,
GPIO41_SSP3_RXD,
- /* SSP 2 */
+ /* SSP 2 to daughter boards */
GPIO11_SSP2_RXD,
GPIO38_SSP2_TXD,
GPIO36_SSP2_SCLK,
GPIO37_GPIO, /* chip select */
- /* SSP 1 */
- GPIO26_SSP1_RXD,
- GPIO25_SSP1_TXD,
+ /* SSP 1 - to daughter boards */
+ GPIO24_GPIO, /* Chip Select */
GPIO23_SSP1_SCLK,
- GPIO24_GPIO, /* chip select */
+ GPIO25_SSP1_TXD,
+ GPIO26_SSP1_RXD,
- /* BTUART */
+ /* BTUART Basic Connector*/
GPIO42_BTUART_RXD,
GPIO43_BTUART_TXD,
GPIO44_BTUART_CTS,
GPIO45_BTUART_RTS,
- /* STUART */
+ /* STUART - IM2 via debug board not sure on SG2*/
GPIO46_STUART_RXD,
GPIO47_STUART_TXD,
GPIO96_GPIO, /* accelerometer interrupt */
GPIO99_GPIO, /* ADC interrupt */
- /* Connector pins specified as gpios */
- GPIO94_GPIO, /* large basic connector pin 14 */
- GPIO10_GPIO, /* large basic connector pin 23 */
-
/* SHT15 */
GPIO100_GPIO,
GPIO98_GPIO,
-};
-/**
- * stargate2_reset_bluetooth() reset the bluecore to ensure consistent state
- **/
-static int stargate2_reset_bluetooth(void)
-{
- int err;
- err = gpio_request(SG2_BT_RESET, "SG2_BT_RESET");
- if (err) {
- printk(KERN_ERR "Could not get gpio for bluetooth reset \n");
- return err;
- }
- gpio_direction_output(SG2_BT_RESET, 1);
- mdelay(5);
- /* now reset it - 5 msec minimum */
- gpio_set_value(SG2_BT_RESET, 0);
- mdelay(10);
- gpio_set_value(SG2_BT_RESET, 1);
- gpio_free(SG2_BT_RESET);
- return 0;
-}
+ /* Basic sensor board */
+ GPIO96_GPIO, /* accelerometer interrupt */
+ GPIO99_GPIO, /* ADC interrupt */
-static struct led_info stargate2_leds[] = {
- {
- .name = "sg2:red",
- .flags = DA9030_LED_RATE_ON,
- }, {
- .name = "sg2:blue",
- .flags = DA9030_LED_RATE_ON,
- }, {
- .name = "sg2:green",
- .flags = DA9030_LED_RATE_ON,
- },
+ /* Connector pins specified as gpios */
+ GPIO94_GPIO, /* large basic connector pin 14 */
+ GPIO10_GPIO, /* large basic connector pin 23 */
};
static struct sht15_platform_data platform_data_sht15 = {
},
};
-static struct da903x_subdev_info stargate2_da9030_subdevs[] = {
+static struct mtd_partition stargate2flash_partitions[] = {
{
- .name = "da903x-led",
- .id = DA9030_ID_LED_2,
- .platform_data = &stargate2_leds[0],
+ .name = "Bootloader",
+ .size = 0x00040000,
+ .offset = 0,
+ .mask_flags = 0,
}, {
- .name = "da903x-led",
- .id = DA9030_ID_LED_3,
- .platform_data = &stargate2_leds[2],
+ .name = "Kernel",
+ .size = 0x00200000,
+ .offset = 0x00040000,
+ .mask_flags = 0
}, {
- .name = "da903x-led",
- .id = DA9030_ID_LED_4,
- .platform_data = &stargate2_leds[1],
+ .name = "Filesystem",
+ .size = 0x01DC0000,
+ .offset = 0x00240000,
+ .mask_flags = 0
+ },
+};
+
+static struct resource flash_resources = {
+ .start = PXA_CS0_PHYS,
+ .end = PXA_CS0_PHYS + SZ_32M - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct flash_platform_data stargate2_flash_data = {
+ .map_name = "cfi_probe",
+ .parts = stargate2flash_partitions,
+ .nr_parts = ARRAY_SIZE(stargate2flash_partitions),
+ .name = "PXA27xOnChipROM",
+ .width = 2,
+};
+
+static struct platform_device stargate2_flash_device = {
+ .name = "pxa2xx-flash",
+ .id = 0,
+ .dev = {
+ .platform_data = &stargate2_flash_data,
+ },
+ .resource = &flash_resources,
+ .num_resources = 1,
+};
+
+static struct pxa2xx_spi_master pxa_ssp_master_0_info = {
+ .num_chipselect = 1,
+};
+
+static struct pxa2xx_spi_master pxa_ssp_master_1_info = {
+ .num_chipselect = 1,
+};
+
+static struct pxa2xx_spi_master pxa_ssp_master_2_info = {
+ .num_chipselect = 1,
+};
+
+/* An upcoming kernel change will scrap SFRM usage so these
+ * drivers have been moved to use gpio's via cs_control */
+static struct pxa2xx_spi_chip staccel_chip_info = {
+ .tx_threshold = 8,
+ .rx_threshold = 8,
+ .dma_burst_size = 8,
+ .timeout = 235,
+ .gpio_cs = 24,
+};
+
+static struct pxa2xx_spi_chip cc2420_info = {
+ .tx_threshold = 8,
+ .rx_threshold = 8,
+ .dma_burst_size = 8,
+ .timeout = 235,
+ .gpio_cs = 39,
+};
+
+static struct spi_board_info spi_board_info[] __initdata = {
+ {
+ .modalias = "lis3l02dq",
+ .max_speed_hz = 8000000,/* 8MHz max spi frequency at 3V */
+ .bus_num = 1,
+ .chip_select = 0,
+ .controller_data = &staccel_chip_info,
+ .irq = IRQ_GPIO(96),
+ }, {
+ .modalias = "cc2420",
+ .max_speed_hz = 6500000,
+ .bus_num = 3,
+ .chip_select = 0,
+ .controller_data = &cc2420_info,
+ },
+};
+
+static void sg2_udc_command(int cmd)
+{
+ switch (cmd) {
+ case PXA2XX_UDC_CMD_CONNECT:
+ UP2OCR |= UP2OCR_HXOE | UP2OCR_DPPUE | UP2OCR_DPPUBE;
+ break;
+ case PXA2XX_UDC_CMD_DISCONNECT:
+ UP2OCR &= ~(UP2OCR_HXOE | UP2OCR_DPPUE | UP2OCR_DPPUBE);
+ break;
+ }
+}
+
+static struct i2c_pxa_platform_data i2c_pwr_pdata = {
+ .fast_mode = 1,
+};
+
+static struct i2c_pxa_platform_data i2c_pdata = {
+ .fast_mode = 1,
+};
+
+static void __init imote2_stargate2_init(void)
+{
+
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(sg2_im2_unified_pin_config));
+
+ pxa_set_ffuart_info(NULL);
+ pxa_set_btuart_info(NULL);
+ pxa_set_stuart_info(NULL);
+
+ pxa2xx_set_spi_info(1, &pxa_ssp_master_0_info);
+ pxa2xx_set_spi_info(2, &pxa_ssp_master_1_info);
+ pxa2xx_set_spi_info(3, &pxa_ssp_master_2_info);
+ spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
+
+
+ pxa27x_set_i2c_power_info(&i2c_pwr_pdata);
+ pxa_set_i2c_info(&i2c_pdata);
+}
+
+#ifdef CONFIG_MACH_INTELMOTE2
+/* As the the imote2 doesn't currently have a conventional SD slot
+ * there is no option to hotplug cards, making all this rather simple
+ */
+static int imote2_mci_get_ro(struct device *dev)
+{
+ return 0;
+}
+
+/* Rather simple case as hotplugging not possible */
+static struct pxamci_platform_data imote2_mci_platform_data = {
+ .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, /* default anyway */
+ .get_ro = imote2_mci_get_ro,
+ .gpio_card_detect = -1,
+ .gpio_card_ro = -1,
+ .gpio_power = -1,
+};
+
+static struct gpio_led imote2_led_pins[] = {
+ {
+ .name = "imote2:red",
+ .gpio = 103,
+ .active_low = 1,
}, {
+ .name = "imote2:green",
+ .gpio = 104,
+ .active_low = 1,
+ }, {
+ .name = "imote2:blue",
+ .gpio = 105,
+ .active_low = 1,
+ },
+};
+
+static struct gpio_led_platform_data imote2_led_data = {
+ .num_leds = ARRAY_SIZE(imote2_led_pins),
+ .leds = imote2_led_pins,
+};
+
+static struct platform_device imote2_leds = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &imote2_led_data,
+ },
+};
+
+static struct da903x_subdev_info imote2_da9030_subdevs[] = {
+ {
.name = "da903x-regulator",
.id = DA9030_ID_LDO2,
.platform_data = &stargate2_ldo_init_data[vcc_bbio],
},
};
-static struct da903x_platform_data stargate2_da9030_pdata = {
- .num_subdevs = ARRAY_SIZE(stargate2_da9030_subdevs),
- .subdevs = stargate2_da9030_subdevs,
+static struct da903x_platform_data imote2_da9030_pdata = {
+ .num_subdevs = ARRAY_SIZE(imote2_da9030_subdevs),
+ .subdevs = imote2_da9030_subdevs,
};
-static struct resource smc91x_resources[] = {
- [0] = {
- .name = "smc91x-regs",
- .start = (PXA_CS4_PHYS + 0x300),
- .end = (PXA_CS4_PHYS + 0xfffff),
- .flags = IORESOURCE_MEM,
+static struct i2c_board_info __initdata imote2_pwr_i2c_board_info[] = {
+ {
+ .type = "da9030",
+ .addr = 0x49,
+ .platform_data = &imote2_da9030_pdata,
+ .irq = gpio_to_irq(1),
},
- [1] = {
- .start = IRQ_GPIO(40),
- .end = IRQ_GPIO(40),
- .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
- }
-};
-
-static struct smc91x_platdata stargate2_smc91x_info = {
- .flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT
- | SMC91X_NOWAIT | SMC91X_USE_DMA,
};
-static struct platform_device smc91x_device = {
- .name = "smc91x",
- .id = -1,
- .num_resources = ARRAY_SIZE(smc91x_resources),
+static struct i2c_board_info __initdata imote2_i2c_board_info[] = {
+ { /* UCAM sensor board */
+ .type = "max1239",
+ .addr = 0x35,
+ }, { /* ITS400 Sensor board only */
+ .type = "max1363",
+ .addr = 0x34,
+ /* Through a nand gate - Also beware, on V2 sensor board the
+ * pull up resistors are missing.
+ */
+ .irq = IRQ_GPIO(99),
+ }, { /* ITS400 Sensor board only */
+ .type = "tsl2561",
+ .addr = 0x49,
+ /* Through a nand gate - Also beware, on V2 sensor board the
+ * pull up resistors are missing.
+ */
+ .irq = IRQ_GPIO(99),
+ }, { /* ITS400 Sensor board only */
+ .type = "tmp175",
+ .addr = 0x4A,
+ .irq = IRQ_GPIO(96),
+ }, { /* IMB400 Multimedia board */
+ .type = "wm8940",
+ .addr = 0x1A,
+ },
+};
+
+static unsigned long imote2_pin_config[] __initdata = {
+
+ /* Button */
+ GPIO91_GPIO,
+
+ /* LEDS */
+ GPIO103_GPIO, /* red led */
+ GPIO104_GPIO, /* green led */
+ GPIO105_GPIO, /* blue led */
+};
+
+static struct pxa2xx_udc_mach_info imote2_udc_info __initdata = {
+ .udc_command = sg2_udc_command,
+};
+
+static struct platform_device *imote2_devices[] = {
+ &stargate2_flash_device,
+ &imote2_leds,
+ &sht15,
+};
+
+static void __init imote2_init(void)
+{
+ pxa2xx_mfp_config(ARRAY_AND_SIZE(imote2_pin_config));
+
+ imote2_stargate2_init();
+
+ platform_add_devices(imote2_devices, ARRAY_SIZE(imote2_devices));
+
+ i2c_register_board_info(0, imote2_i2c_board_info,
+ ARRAY_SIZE(imote2_i2c_board_info));
+ i2c_register_board_info(1, imote2_pwr_i2c_board_info,
+ ARRAY_SIZE(imote2_pwr_i2c_board_info));
+
+ pxa_set_mci_info(&imote2_mci_platform_data);
+ pxa_set_udc_info(&imote2_udc_info);
+}
+#endif
+
+#ifdef CONFIG_MACH_STARGATE2
+
+static unsigned long stargate2_pin_config[] __initdata = {
+
+ GPIO15_nCS_1, /* SRAM */
+ /* SMC91x */
+ GPIO80_nCS_4,
+ GPIO40_GPIO, /*cable detect?*/
+
+ /* Button */
+ GPIO91_GPIO | WAKEUP_ON_LEVEL_HIGH,
+
+ /* Compact Flash */
+ GPIO79_PSKTSEL,
+ GPIO48_nPOE,
+ GPIO49_nPWE,
+ GPIO50_nPIOR,
+ GPIO51_nPIOW,
+ GPIO85_nPCE_1,
+ GPIO54_nPCE_2,
+ GPIO55_nPREG,
+ GPIO56_nPWAIT,
+ GPIO57_nIOIS16,
+ GPIO120_GPIO, /* Buff ctrl */
+ GPIO108_GPIO, /* Power ctrl */
+ GPIO82_GPIO, /* Reset */
+ GPIO53_GPIO, /* SG2_S0_GPIO_DETECT */
+
+ /* MMC not shared with imote2 */
+ GPIO90_GPIO, /* nSD detect */
+ GPIO89_GPIO, /* SD_POWER_ENABLE */
+
+ /* Bluetooth */
+ GPIO81_GPIO, /* reset */
+};
+
+static struct resource smc91x_resources[] = {
+ [0] = {
+ .name = "smc91x-regs",
+ .start = (PXA_CS4_PHYS + 0x300),
+ .end = (PXA_CS4_PHYS + 0xfffff),
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_GPIO(40),
+ .end = IRQ_GPIO(40),
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
+ }
+};
+
+static struct smc91x_platdata stargate2_smc91x_info = {
+ .flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT
+ | SMC91X_NOWAIT | SMC91X_USE_DMA,
+};
+
+static struct platform_device smc91x_device = {
+ .name = "smc91x",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(smc91x_resources),
.resource = smc91x_resources,
.dev = {
.platform_data = &stargate2_smc91x_info,
};
-
/*
* The card detect interrupt isn't debounced so we delay it by 250ms
* to give the card a chance to fully insert / eject.
.exit = stargate2_mci_exit,
};
-static struct mtd_partition stargate2flash_partitions[] = {
- {
- .name = "Bootloader",
- .size = 0x00040000,
- .offset = 0,
- .mask_flags = 0,
- }, {
- .name = "Kernel",
- .size = 0x00200000,
- .offset = 0x00040000,
- .mask_flags = 0
- }, {
- .name = "Filesystem",
- .size = 0x01DC0000,
- .offset = 0x00240000,
- .mask_flags = 0
- },
-};
-
-static struct resource flash_resources = {
- .start = PXA_CS0_PHYS,
- .end = PXA_CS0_PHYS + SZ_32M - 1,
- .flags = IORESOURCE_MEM,
-};
-
-static struct flash_platform_data stargate2_flash_data = {
- .map_name = "cfi_probe",
- .parts = stargate2flash_partitions,
- .nr_parts = ARRAY_SIZE(stargate2flash_partitions),
- .name = "PXA27xOnChipROM",
- .width = 2,
-};
-
-static struct platform_device stargate2_flash_device = {
- .name = "pxa2xx-flash",
- .id = 0,
- .dev = {
- .platform_data = &stargate2_flash_data,
- },
- .resource = &flash_resources,
- .num_resources = 1,
-};
/*
* SRAM - The Stargate 2 has 32MB of SRAM.
.page_size = 4,
};
+/**
+ * stargate2_reset_bluetooth() reset the bluecore to ensure consistent state
+ **/
+static int stargate2_reset_bluetooth(void)
+{
+ int err;
+ err = gpio_request(SG2_BT_RESET, "SG2_BT_RESET");
+ if (err) {
+ printk(KERN_ERR "Could not get gpio for bluetooth reset\n");
+ return err;
+ }
+ gpio_direction_output(SG2_BT_RESET, 1);
+ mdelay(5);
+ /* now reset it - 5 msec minimum */
+ gpio_set_value(SG2_BT_RESET, 0);
+ mdelay(10);
+ gpio_set_value(SG2_BT_RESET, 1);
+ gpio_free(SG2_BT_RESET);
+ return 0;
+}
+
+static struct led_info stargate2_leds[] = {
+ {
+ .name = "sg2:red",
+ .flags = DA9030_LED_RATE_ON,
+ }, {
+ .name = "sg2:blue",
+ .flags = DA9030_LED_RATE_ON,
+ }, {
+ .name = "sg2:green",
+ .flags = DA9030_LED_RATE_ON,
+ },
+};
+
+static struct da903x_subdev_info stargate2_da9030_subdevs[] = {
+ {
+ .name = "da903x-led",
+ .id = DA9030_ID_LED_2,
+ .platform_data = &stargate2_leds[0],
+ }, {
+ .name = "da903x-led",
+ .id = DA9030_ID_LED_3,
+ .platform_data = &stargate2_leds[2],
+ }, {
+ .name = "da903x-led",
+ .id = DA9030_ID_LED_4,
+ .platform_data = &stargate2_leds[1],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO2,
+ .platform_data = &stargate2_ldo_init_data[vcc_bbio],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO3,
+ .platform_data = &stargate2_ldo_init_data[vcc_bb],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO4,
+ .platform_data = &stargate2_ldo_init_data[vcc_pxa_flash],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO5,
+ .platform_data = &stargate2_ldo_init_data[vcc_cc2420],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO6,
+ .platform_data = &stargate2_ldo_init_data[vcc_vref],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO7,
+ .platform_data = &stargate2_ldo_init_data[vcc_sram_ext],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO8,
+ .platform_data = &stargate2_ldo_init_data[vcc_mica],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO9,
+ .platform_data = &stargate2_ldo_init_data[vcc_bt],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO10,
+ .platform_data = &stargate2_ldo_init_data[vcc_sensor_1_8],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO11,
+ .platform_data = &stargate2_ldo_init_data[vcc_sensor_3],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO12,
+ .platform_data = &stargate2_ldo_init_data[vcc_lcd],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO15,
+ .platform_data = &stargate2_ldo_init_data[vcc_pxa_pll],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO17,
+ .platform_data = &stargate2_ldo_init_data[vcc_pxa_usim],
+ }, {
+ .name = "da903x-regulator", /*pxa vcc i/o and cc2420 vcc i/o */
+ .id = DA9030_ID_LDO18,
+ .platform_data = &stargate2_ldo_init_data[vcc_io],
+ }, {
+ .name = "da903x-regulator",
+ .id = DA9030_ID_LDO19,
+ .platform_data = &stargate2_ldo_init_data[vcc_pxa_mem],
+ },
+};
+
+static struct da903x_platform_data stargate2_da9030_pdata = {
+ .num_subdevs = ARRAY_SIZE(stargate2_da9030_subdevs),
+ .subdevs = stargate2_da9030_subdevs,
+};
+
+static struct i2c_board_info __initdata stargate2_pwr_i2c_board_info[] = {
+ {
+ .type = "da9030",
+ .addr = 0x49,
+ .platform_data = &stargate2_da9030_pdata,
+ .irq = gpio_to_irq(1),
+ },
+};
static struct i2c_board_info __initdata stargate2_i2c_board_info[] = {
/* Techically this a pca9500 - but it's compatible with the 8574
},
};
-static struct i2c_board_info __initdata stargate2_pwr_i2c_board_info[] = {
- {
- .type = "da9030",
- .addr = 0x49,
- .platform_data = &stargate2_da9030_pdata,
- .irq = gpio_to_irq(1),
- },
-};
-
-static struct pxa2xx_spi_master pxa_ssp_master_0_info = {
- .num_chipselect = 1,
-};
-
-static struct pxa2xx_spi_master pxa_ssp_master_1_info = {
- .num_chipselect = 1,
-};
-
-static struct pxa2xx_spi_master pxa_ssp_master_2_info = {
- .num_chipselect = 1,
-};
-
-/* An upcoming kernel change will scrap SFRM usage so these
- * drivers have been moved to use gpio's via cs_control */
-static struct pxa2xx_spi_chip staccel_chip_info = {
- .tx_threshold = 8,
- .rx_threshold = 8,
- .dma_burst_size = 8,
- .timeout = 235,
- .gpio_cs = 24,
-};
-
-static struct pxa2xx_spi_chip cc2420_info = {
- .tx_threshold = 8,
- .rx_threshold = 8,
- .dma_burst_size = 8,
- .timeout = 235,
- .gpio_cs = 39,
-};
-
-static struct spi_board_info spi_board_info[] __initdata = {
- {
- .modalias = "lis3l02dq",
- .max_speed_hz = 8000000,/* 8MHz max spi frequency at 3V */
- .bus_num = 1,
- .chip_select = 0,
- .controller_data = &staccel_chip_info,
- .irq = IRQ_GPIO(96),
- }, {
- .modalias = "cc2420",
- .max_speed_hz = 6500000,
- .bus_num = 3,
- .chip_select = 0,
- .controller_data = &cc2420_info,
- },
-};
-
-static void sg2_udc_command(int cmd)
-{
- switch (cmd) {
- case PXA2XX_UDC_CMD_CONNECT:
- UP2OCR |= UP2OCR_HXOE | UP2OCR_DPPUE | UP2OCR_DPPUBE;
- break;
- case PXA2XX_UDC_CMD_DISCONNECT:
- UP2OCR &= ~(UP2OCR_HXOE | UP2OCR_DPPUE | UP2OCR_DPPUBE);
- break;
- }
-}
-
/* Board doesn't support cable detection - so always lie and say
* something is there.
*/
&sht15,
};
-static struct i2c_pxa_platform_data i2c_pwr_pdata = {
- .fast_mode = 1,
-};
-
-static struct i2c_pxa_platform_data i2c_pdata = {
- .fast_mode = 1,
-};
-
static void __init stargate2_init(void)
{
/* This is probably a board specific hack as this must be set
pxa2xx_mfp_config(ARRAY_AND_SIZE(stargate2_pin_config));
- pxa_set_ffuart_info(NULL);
- pxa_set_btuart_info(NULL);
- pxa_set_stuart_info(NULL);
+ imote2_stargate2_init();
platform_add_devices(ARRAY_AND_SIZE(stargate2_devices));
- pxa2xx_set_spi_info(1, &pxa_ssp_master_0_info);
- pxa2xx_set_spi_info(2, &pxa_ssp_master_1_info);
- pxa2xx_set_spi_info(3, &pxa_ssp_master_2_info);
- spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
-
i2c_register_board_info(0, ARRAY_AND_SIZE(stargate2_i2c_board_info));
- i2c_register_board_info(1,
- ARRAY_AND_SIZE(stargate2_pwr_i2c_board_info));
- pxa27x_set_i2c_power_info(&i2c_pwr_pdata);
- pxa_set_i2c_info(&i2c_pdata);
+ i2c_register_board_info(1, stargate2_pwr_i2c_board_info,
+ ARRAY_SIZE(stargate2_pwr_i2c_board_info));
pxa_set_mci_info(&stargate2_mci_platform_data);
stargate2_reset_bluetooth();
}
+#endif
+
+#ifdef CONFIG_MACH_INTELMOTE2
+MACHINE_START(INTELMOTE2, "IMOTE 2")
+ .phys_io = 0x40000000,
+ .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
+ .map_io = pxa_map_io,
+ .init_irq = pxa27x_init_irq,
+ .timer = &pxa_timer,
+ .init_machine = imote2_init,
+ .boot_params = 0xA0000100,
+MACHINE_END
+#endif
+#ifdef CONFIG_MACH_STARGATE2
MACHINE_START(STARGATE2, "Stargate 2")
.phys_io = 0x40000000,
.io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
.init_machine = stargate2_init,
.boot_params = 0xA0000100,
MACHINE_END
+#endif
i2c_register_board_info(0, trizeps4_i2c_devices,
ARRAY_SIZE(trizeps4_i2c_devices));
-#ifdef CONFIG_IDE_PXA_CF
- /* if boot direct from compact flash dont disable power */
- trizeps_conxs_bcr = 0x0009;
-#else
/* this is the reset value */
trizeps_conxs_bcr = 0x00A0;
-#endif
+
BCR_writew(trizeps_conxs_bcr);
board_backlight_power(1);
}
#include <linux/dm9000.h>
#include <linux/ucb1400.h>
#include <linux/ata_platform.h>
+#include <linux/regulator/max1586.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <mach/ohci.h>
#include <mach/pxa27x-udc.h>
#include <mach/udc.h>
+#include <mach/pata_pxa.h>
#include <plat/i2c.h>
static void __init vpac270_rtc_init(void)
{
- pxa_set_i2c_info(NULL);
i2c_register_board_info(0, ARRAY_AND_SIZE(vpac270_i2c_devs));
}
#else
.vsync_len = 2,
.sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
-},
+}, { /* CRT 640x480 */
+ .pixclock = 35000,
+ .xres = 640,
+ .yres = 480,
+ .bpp = 16,
+ .depth = 16,
+
+ .left_margin = 96,
+ .right_margin = 48,
+ .upper_margin = 33,
+ .lower_margin = 10,
+
+ .hsync_len = 48,
+ .vsync_len = 1,
+
+ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+}, { /* CRT 800x600 H=30kHz V=48HZ */
+ .pixclock = 25000,
+ .xres = 800,
+ .yres = 600,
+ .bpp = 16,
+ .depth = 16,
+
+ .left_margin = 50,
+ .right_margin = 1,
+ .upper_margin = 21,
+ .lower_margin = 12,
+
+ .hsync_len = 8,
+ .vsync_len = 1,
+
+ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+}, { /* CRT 1024x768 H=40kHz V=50Hz */
+ .pixclock = 15000,
+ .xres = 1024,
+ .yres = 768,
+ .bpp = 16,
+ .depth = 16,
+
+ .left_margin = 220,
+ .right_margin = 8,
+ .upper_margin = 33,
+ .lower_margin = 2,
+
+ .hsync_len = 48,
+ .vsync_len = 1,
+
+ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+}
};
static struct pxafb_mach_info vpac270_lcd_screen = {
/******************************************************************************
* PATA IDE
******************************************************************************/
-#if defined(CONFIG_PATA_PLATFORM) || defined(CONFIG_PATA_PLATFORM_MODULE)
-static struct pata_platform_info vpac270_pata_pdata = {
- .ioport_shift = 1,
+#if defined(CONFIG_PATA_PXA) || defined(CONFIG_PATA_PXA_MODULE)
+static struct pata_pxa_pdata vpac270_pata_pdata = {
+ .reg_shift = 1,
+ .dma_dreq = 1,
.irq_flags = IRQF_TRIGGER_RISING,
};
.end = PXA_CS3_PHYS + 0x15f,
.flags = IORESOURCE_MEM
},
- [2] = { /* IDE IRQ pin */
+ [2] = { /* DMA Base address */
+ .start = PXA_CS3_PHYS + 0x20,
+ .end = PXA_CS3_PHYS + 0x2f,
+ .flags = IORESOURCE_DMA
+ },
+ [3] = { /* IDE IRQ pin */
.start = gpio_to_irq(GPIO36_VPAC270_IDE_IRQ),
.end = gpio_to_irq(GPIO36_VPAC270_IDE_IRQ),
.flags = IORESOURCE_IRQ
};
static struct platform_device vpac270_ide_device = {
- .name = "pata_platform",
+ .name = "pata_pxa",
.num_resources = ARRAY_SIZE(vpac270_ide_resources),
.resource = vpac270_ide_resources,
.dev = {
.platform_data = &vpac270_pata_pdata,
+ .coherent_dma_mask = 0xffffffff,
}
};
static inline void vpac270_ide_init(void) {}
#endif
+/******************************************************************************
+ * Core power regulator
+ ******************************************************************************/
+#if defined(CONFIG_REGULATOR_MAX1586) || \
+ defined(CONFIG_REGULATOR_MAX1586_MODULE)
+static struct regulator_consumer_supply vpac270_max1587a_consumers[] = {
+ {
+ .supply = "vcc_core",
+ }
+};
+
+static struct regulator_init_data vpac270_max1587a_v3_info = {
+ .constraints = {
+ .name = "vcc_core range",
+ .min_uV = 900000,
+ .max_uV = 1705000,
+ .always_on = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ },
+ .consumer_supplies = vpac270_max1587a_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(vpac270_max1587a_consumers),
+};
+
+static struct max1586_subdev_data vpac270_max1587a_subdevs[] = {
+ {
+ .name = "vcc_core",
+ .id = MAX1586_V3,
+ .platform_data = &vpac270_max1587a_v3_info,
+ }
+};
+
+static struct max1586_platform_data vpac270_max1587a_info = {
+ .subdevs = vpac270_max1587a_subdevs,
+ .num_subdevs = ARRAY_SIZE(vpac270_max1587a_subdevs),
+ .v3_gain = MAX1586_GAIN_R24_3k32, /* 730..1550 mV */
+};
+
+static struct i2c_board_info __initdata vpac270_pi2c_board_info[] = {
+ {
+ I2C_BOARD_INFO("max1586", 0x14),
+ .platform_data = &vpac270_max1587a_info,
+ },
+};
+
+static void __init vpac270_pmic_init(void)
+{
+ i2c_register_board_info(1, ARRAY_AND_SIZE(vpac270_pi2c_board_info));
+}
+#else
+static inline void vpac270_pmic_init(void) {}
+#endif
+
+
/******************************************************************************
* Machine init
******************************************************************************/
pxa_set_ffuart_info(NULL);
pxa_set_btuart_info(NULL);
pxa_set_stuart_info(NULL);
+ pxa_set_i2c_info(NULL);
+ pxa27x_set_i2c_power_info(NULL);
+ vpac270_pmic_init();
vpac270_lcd_init();
vpac270_mmc_init();
vpac270_nor_init();
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/pwm_backlight.h>
+#include <linux/z2_battery.h>
#include <linux/dma-mapping.h>
#include <linux/spi/spi.h>
#include <linux/spi/libertas_spi.h>
#include <linux/gpio.h>
#include <linux/gpio_keys.h>
#include <linux/delay.h>
+#include <linux/regulator/machine.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
}, {
.name = "U-Boot Environment",
.offset = 0x40000,
- .size = 0x60000,
+ .size = 0x20000,
}, {
.name = "Flash",
.offset = 0x60000,
static inline void z2_keys_init(void) {}
#endif
+/******************************************************************************
+ * Battery
+ ******************************************************************************/
+#if defined(CONFIG_I2C_PXA) || defined(CONFIG_I2C_PXA_MODULE)
+static struct z2_battery_info batt_chip_info = {
+ .batt_I2C_bus = 0,
+ .batt_I2C_addr = 0x55,
+ .batt_I2C_reg = 2,
+ .charge_gpio = GPIO0_ZIPITZ2_AC_DETECT,
+ .min_voltage = 2400000,
+ .max_voltage = 3700000,
+ .batt_div = 69,
+ .batt_mult = 1000000,
+ .batt_tech = POWER_SUPPLY_TECHNOLOGY_LION,
+ .batt_name = "Z2",
+};
+
+static struct i2c_board_info __initdata z2_i2c_board_info[] = {
+ {
+ I2C_BOARD_INFO("aer915", 0x55),
+ .platform_data = &batt_chip_info,
+ }, {
+ I2C_BOARD_INFO("wm8750", 0x1b),
+ },
+
+};
+
+static void __init z2_i2c_init(void)
+{
+ pxa_set_i2c_info(NULL);
+ i2c_register_board_info(0, ARRAY_AND_SIZE(z2_i2c_board_info));
+}
+#else
+static inline void z2_i2c_init(void) {}
+#endif
+
/******************************************************************************
* SSP Devices - WiFi and LCD control
******************************************************************************/
static inline void z2_spi_init(void) {}
#endif
+/******************************************************************************
+ * Core power regulator
+ ******************************************************************************/
+#if defined(CONFIG_REGULATOR_TPS65023) || \
+ defined(CONFIG_REGULATOR_TPS65023_MODULE)
+static struct regulator_consumer_supply z2_tps65021_consumers[] = {
+ {
+ .supply = "vcc_core",
+ }
+};
+
+static struct regulator_init_data z2_tps65021_info[] = {
+ {
+ .constraints = {
+ .name = "vcc_core range",
+ .min_uV = 800000,
+ .max_uV = 1600000,
+ .always_on = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ },
+ .consumer_supplies = z2_tps65021_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(z2_tps65021_consumers),
+ }, {
+ .constraints = {
+ .name = "DCDC2",
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .always_on = 1,
+ },
+ }, {
+ .constraints = {
+ .name = "DCDC3",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .always_on = 1,
+ },
+ }, {
+ .constraints = {
+ .name = "LDO1",
+ .min_uV = 1000000,
+ .max_uV = 3150000,
+ .always_on = 1,
+ },
+ }, {
+ .constraints = {
+ .name = "LDO2",
+ .min_uV = 1050000,
+ .max_uV = 3300000,
+ .always_on = 1,
+ },
+ }
+};
+
+static struct i2c_board_info __initdata z2_pi2c_board_info[] = {
+ {
+ I2C_BOARD_INFO("tps65021", 0x48),
+ .platform_data = &z2_tps65021_info,
+ },
+};
+
+static void __init z2_pmic_init(void)
+{
+ pxa27x_set_i2c_power_info(NULL);
+ i2c_register_board_info(1, ARRAY_AND_SIZE(z2_pi2c_board_info));
+}
+#else
+static inline void z2_pmic_init(void) {}
+#endif
+
/******************************************************************************
* Machine init
******************************************************************************/
{
pxa2xx_mfp_config(ARRAY_AND_SIZE(z2_pin_config));
+ pxa_set_ffuart_info(NULL);
+ pxa_set_btuart_info(NULL);
+ pxa_set_stuart_info(NULL);
+
z2_lcd_init();
z2_mmc_init();
z2_mkp_init();
-
- pxa_set_i2c_info(NULL);
-
+ z2_i2c_init();
z2_spi_init();
z2_nor_init();
z2_pwm_init();
z2_leds_init();
z2_keys_init();
+ z2_pmic_init();
}
MACHINE_START(ZIPIT2, "Zipit Z2")
#include <plat/cpu.h>
#include <plat/iic-core.h>
+#include <plat/fb-core.h>
static struct map_desc s3c2416_iodesc[] __initdata = {
IODESC_ENT(WATCHDOG),
s3c_i2c0_setname("s3c2440-i2c");
s3c_i2c1_setname("s3c2440-i2c");
- s3c_device_fb.name = "s3c2443-fb";
+ s3c_fb_setname("s3c2443-fb");
return sysdev_register(&s3c2416_sysdev);
}
static struct pcf50633 *gta02_pcf;
/*
- * This gets called every 1ms when we paniced.
+ * This gets called frequently when we paniced.
*/
-static long gta02_panic_blink(long count)
+static long gta02_panic_blink(int state)
{
long delay = 0;
- static long last_blink;
- static char led;
+ char led;
- /* Fast blink: 200ms period. */
- if (count - last_blink < 100)
- return 0;
-
- led ^= 1;
+ led = (state) ? 1 : 0;
gpio_direction_output(GTA02_GPIO_AUX_LED, led);
- last_blink = count;
-
return delay;
}
static void __init gta02_machine_init(void)
{
- /* Set the panic callback to make AUX LED blink at ~5Hz. */
+ /* Set the panic callback to turn AUX LED on or off. */
panic_blink = gta02_panic_blink;
s3c_pm_init();
#include <plat/s3c2443.h>
#include <plat/devs.h>
#include <plat/cpu.h>
+#include <plat/fb-core.h>
static struct map_desc s3c2443_iodesc[] __initdata = {
IODESC_ENT(WATCHDOG),
s3c24xx_reset_hook = s3c2443_hard_reset;
s3c_device_nand.name = "s3c2412-nand";
+ s3c_fb_setname("s3c2443-fb");
/* change WDT IRQ number */
s3c_device_wdt.resource[1].start = IRQ_S3C2443_WDT;
help
Common setup code for i2c bus 1.
+config S3C64XX_SETUP_IDE
+ bool
+ help
+ Common setup code for S3C64XX IDE.
+
config S3C64XX_SETUP_FB_24BPP
bool
help
Common setup code for S3C64XX with an 24bpp RGB display helper.
+config S3C64XX_SETUP_KEYPAD
+ bool
+ help
+ Common setup code for S3C64XX KEYPAD GPIO configurations
+
config S3C64XX_SETUP_SDHCI_GPIO
bool
help
select S3C_DEV_HSMMC
select S3C_DEV_HSMMC1
select S3C_DEV_I2C1
+ select SAMSUNG_DEV_IDE
select S3C_DEV_FB
+ select S3C_DEV_RTC
select SAMSUNG_DEV_TS
select S3C_DEV_USB_HOST
select S3C_DEV_USB_HSOTG
select S3C_DEV_WDT
+ select SAMSUNG_DEV_KEYPAD
select HAVE_S3C2410_WATCHDOG
select S3C64XX_SETUP_SDHCI
select S3C64XX_SETUP_I2C1
+ select S3C64XX_SETUP_IDE
select S3C64XX_SETUP_FB_24BPP
+ select S3C64XX_SETUP_KEYPAD
help
Machine support for the Samsung SMDK6410
obj-$(CONFIG_S3C64XX_SETUP_I2C0) += setup-i2c0.o
obj-$(CONFIG_S3C64XX_SETUP_I2C1) += setup-i2c1.o
+obj-$(CONFIG_S3C64XX_SETUP_IDE) += setup-ide.o
+obj-$(CONFIG_S3C64XX_SETUP_KEYPAD) += setup-keypad.o
obj-$(CONFIG_S3C64XX_SETUP_SDHCI) += setup-sdhci.o
obj-$(CONFIG_S3C64XX_SETUP_FB_24BPP) += setup-fb-24bpp.o
obj-$(CONFIG_S3C64XX_SETUP_SDHCI_GPIO) += setup-sdhci-gpio.o
.name = "nand",
.id = -1,
.parent = &clk_h,
+ }, {
+ .name = "rtc",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_RTC,
}, {
.name = "adc",
.id = -1,
.ctrlbit = S3C6410_CLKCON_PCLK_IIS2,
}, {
#endif
+ .name = "keypad",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s3c64xx_pclk_ctrl,
+ .ctrlbit = S3C_CLKCON_PCLK_KEYPAD,
+ }, {
.name = "spi",
.id = 0,
.parent = &clk_p,
.parent = &clk_p,
.enable = s3c64xx_pclk_ctrl,
.ctrlbit = S3C_CLKCON_PCLK_UART3,
- }, {
- .name = "rtc",
- .id = -1,
- .parent = &clk_p,
- .enable = s3c64xx_pclk_ctrl,
- .ctrlbit = S3C_CLKCON_PCLK_RTC,
}, {
.name = "watchdog",
.id = -1,
.id = -1,
.parent = &clk_p,
.ctrlbit = S3C_CLKCON_PCLK_AC97,
+ }, {
+ .name = "cfcon",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s3c64xx_hclk_ctrl,
+ .ctrlbit = S3C_CLKCON_HCLK_IHOST,
}
};
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/gpio.h>
#include <mach/irqs.h>
#include <mach/map.h>
#include <mach/dma.h>
-#include <mach/gpio.h>
#include <plat/devs.h>
#include <plat/audio.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/gpio.h>
#include <mach/dma.h>
#include <mach/map.h>
-#include <mach/gpio.h>
#include <mach/gpio-bank-c.h>
#include <mach/spi-clocks.h>
#include <linux/kernel.h>
#include <linux/irq.h>
#include <linux/io.h>
+#include <linux/gpio.h>
#include <mach/map.h>
-#include <mach/gpio.h>
#include <plat/gpio-core.h>
#include <plat/gpio-cfg.h>
#define S3C64XX_PA_USB_HSOTG (0x7C000000)
#define S3C64XX_PA_WATCHDOG (0x7E004000)
#define S3C64XX_PA_RTC (0x7E005000)
+#define S3C64XX_PA_KEYPAD (0x7E00A000)
#define S3C64XX_PA_ADC (0x7E00B000)
#define S3C64XX_PA_SYSCON (0x7E00F000)
#define S3C64XX_PA_AC97 (0x7F001000)
#define S3C64XX_SZ_GPIO SZ_4K
#define S3C64XX_PA_SDRAM (0x50000000)
+
+#define S3C64XX_PA_CFCON (0x70300000)
+
#define S3C64XX_PA_VIC0 (0x71200000)
#define S3C64XX_PA_VIC1 (0x71300000)
#define S3C_PA_WDT S3C64XX_PA_WATCHDOG
#define SAMSUNG_PA_ADC S3C64XX_PA_ADC
+#define SAMSUNG_PA_CFCON S3C64XX_PA_CFCON
+#define SAMSUNG_PA_KEYPAD S3C64XX_PA_KEYPAD
#endif /* __ASM_ARCH_6400_MAP_H */
#define S3C_SCLK_GATE S3C_CLKREG(0x38)
#define S3C_MEM0_GATE S3C_CLKREG(0x3C)
#define S3C6410_CLK_SRC2 S3C_CLKREG(0x10C)
+#define S3C_MEM_SYS_CFG S3C_CLKREG(0x120)
/* CLKDIV0 */
#define S3C6400_CLKDIV0_PCLK_MASK (0xf << 12)
#define S3C6400_CLKSRC_EPLL_MOUT_SHIFT (2)
#define S3C6400_CLKSRC_MFC (1 << 4)
+/* MEM_SYS_CFG */
+#define MEM_SYS_CFG_INDEP_CF 0x4000
+#define MEM_SYS_CFG_EBI_FIX_PRI_CFCON 0x30
+
#endif /* _PLAT_REGS_CLOCK_H */
#include <plat/regs-fb-v4.h>
-/* Palette registers */
-#define WIN2_PAL(_entry) (0x300 + ((_entry) * 2))
-#define WIN3_PAL(_entry) (0x320 + ((_entry) * 2))
-#define WIN4_PAL(_entry) (0x340 + ((_entry) * 2))
-#define WIN0_PAL(_entry) (0x400 + ((_entry) * 4))
-#define WIN1_PAL(_entry) (0x800 + ((_entry) * 4))
-
-static inline unsigned int s3c_fb_pal_reg(unsigned int window, int reg)
-{
- switch (window) {
- case 0: return WIN0_PAL(reg);
- case 1: return WIN1_PAL(reg);
- case 2: return WIN2_PAL(reg);
- case 3: return WIN3_PAL(reg);
- case 4: return WIN4_PAL(reg);
- }
-
- BUG();
-}
-
#endif /* __ASM_ARCH_MACH_REGS_FB_H */
static struct s3c_fb_pd_win anw6410_fb_win0 = {
/* this is to ensure we use win0 */
.win_mode = {
- .pixclock = 41094,
.left_margin = 8,
.right_margin = 13,
.upper_margin = 7,
static struct s3c_fb_pd_win hmt_fb_win0 = {
.win_mode = {
- .pixclock = 41094,
.left_margin = 8,
.right_margin = 13,
.upper_margin = 7,
static struct s3c_fb_pd_win smartq5_fb_win0 = {
.win_mode = {
- .pixclock = 1000000000000ULL /
- ((40+1+216+800)*(10+1+35+480)*80),
.left_margin = 40,
.right_margin = 216,
.upper_margin = 10,
.vsync_len = 1,
.xres = 800,
.yres = 480,
+ .refresh = 80,
},
.max_bpp = 32,
.default_bpp = 16,
static struct s3c_fb_pd_win smartq7_fb_win0 = {
.win_mode = {
- .pixclock = 1000000000000ULL /
- ((3+10+5+800)*(1+3+20+480)*80),
.left_margin = 3,
.right_margin = 5,
.upper_margin = 1,
.vsync_len = 3,
.xres = 800,
.yres = 480,
+ .refresh = 80,
},
.max_bpp = 32,
.default_bpp = 16,
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
+#include <linux/input.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <mach/regs-gpio.h>
#include <mach/regs-sys.h>
#include <mach/regs-srom.h>
+#include <plat/ata.h>
#include <plat/iic.h>
#include <plat/fb.h>
#include <plat/gpio-cfg.h>
#include <plat/cpu.h>
#include <plat/adc.h>
#include <plat/ts.h>
+#include <plat/keypad.h>
#define UCON S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK
#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB
static struct s3c_fb_pd_win smdk6410_fb_win0 = {
/* this is to ensure we use win0 */
.win_mode = {
- .pixclock = 41094,
.left_margin = 8,
.right_margin = 13,
.upper_margin = 7,
};
#endif
+static struct s3c_ide_platdata smdk6410_ide_pdata __initdata = {
+ .setup_gpio = s3c64xx_ide_setup_gpio,
+};
+
+static uint32_t smdk6410_keymap[] __initdata = {
+ /* KEY(row, col, keycode) */
+ KEY(0, 3, KEY_1), KEY(0, 4, KEY_2), KEY(0, 5, KEY_3),
+ KEY(0, 6, KEY_4), KEY(0, 7, KEY_5),
+ KEY(1, 3, KEY_A), KEY(1, 4, KEY_B), KEY(1, 5, KEY_C),
+ KEY(1, 6, KEY_D), KEY(1, 7, KEY_E)
+};
+
+static struct matrix_keymap_data smdk6410_keymap_data __initdata = {
+ .keymap = smdk6410_keymap,
+ .keymap_size = ARRAY_SIZE(smdk6410_keymap),
+};
+
+static struct samsung_keypad_platdata smdk6410_keypad_data __initdata = {
+ .keymap_data = &smdk6410_keymap_data,
+ .rows = 2,
+ .cols = 8,
+};
+
static struct map_desc smdk6410_iodesc[] = {};
static struct platform_device *smdk6410_devices[] __initdata = {
&s3c_device_ohci,
&s3c_device_usb_hsotg,
&s3c64xx_device_iisv4,
+ &samsung_device_keypad,
#ifdef CONFIG_REGULATOR
&smdk6410_b_pwr_5v,
&smdk6410_smsc911x,
&s3c_device_adc,
+ &s3c_device_cfcon,
+ &s3c_device_rtc,
&s3c_device_ts,
&s3c_device_wdt,
};
s3c_i2c1_set_platdata(NULL);
s3c_fb_set_platdata(&smdk6410_lcd_pdata);
+ samsung_keypad_set_platdata(&smdk6410_keypad_data);
+
s3c24xx_ts_set_platdata(&s3c_ts_platform);
/* configure nCS1 width to 16 bits */
i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
+ s3c_ide_set_platdata(&smdk6410_ide_pdata);
+
platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));
}
#include <plat/devs.h>
#include <plat/clock.h>
#include <plat/sdhci.h>
+#include <plat/ata-core.h>
+#include <plat/adc-core.h>
#include <plat/iic-core.h>
-#include <plat/adc.h>
#include <plat/onenand-core.h>
#include <mach/s3c6400.h>
#include <mach/s3c6410.h>
s3c_i2c0_setname("s3c2440-i2c");
s3c_i2c1_setname("s3c2440-i2c");
- s3c_device_adc.name = "s3c64xx-adc";
+ s3c_adc_setname("s3c64xx-adc");
s3c_device_nand.name = "s3c6400-nand";
s3c_onenand_setname("s3c6410-onenand");
s3c64xx_onenand1_setname("s3c6410-onenand");
+ s3c_cfcon_setname("s3c64xx-pata");
}
void __init s3c6410_init_clocks(int xtal)
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fb.h>
+#include <linux/gpio.h>
#include <mach/regs-fb.h>
-#include <mach/gpio.h>
#include <plat/fb.h>
#include <plat/gpio-cfg.h>
#include <linux/kernel.h>
#include <linux/types.h>
+#include <linux/gpio.h>
struct platform_device; /* don't need the contents */
-#include <mach/gpio.h>
#include <mach/gpio-bank-b.h>
#include <plat/iic.h>
#include <plat/gpio-cfg.h>
#include <linux/kernel.h>
#include <linux/types.h>
+#include <linux/gpio.h>
struct platform_device; /* don't need the contents */
-#include <mach/gpio.h>
#include <mach/gpio-bank-b.h>
#include <plat/iic.h>
#include <plat/gpio-cfg.h>
--- /dev/null
+/* linux/arch/arm/mach-s3c64xx/setup-ide.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * S3C64XX setup information for IDE
+ *
+ * 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/gpio.h>
+#include <linux/io.h>
+
+#include <mach/map.h>
+#include <mach/regs-clock.h>
+#include <plat/gpio-cfg.h>
+
+void s3c64xx_ide_setup_gpio(void)
+{
+ u32 reg;
+ u32 gpio = 0;
+
+ reg = readl(S3C_MEM_SYS_CFG) & (~0x3f);
+
+ /* Independent CF interface, CF chip select configuration */
+ writel(reg | MEM_SYS_CFG_INDEP_CF |
+ MEM_SYS_CFG_EBI_FIX_PRI_CFCON, S3C_MEM_SYS_CFG);
+
+ s3c_gpio_cfgpin(S3C64XX_GPB(4), S3C_GPIO_SFN(4));
+
+ /* Set XhiDATA[15:0] pins as CF Data[15:0] */
+ for (gpio = S3C64XX_GPK(0); gpio <= S3C64XX_GPK(15); gpio++)
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(5));
+
+ /* Set XhiADDR[2:0] pins as CF ADDR[2:0] */
+ for (gpio = S3C64XX_GPL(0); gpio <= S3C64XX_GPL(2); gpio++)
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(6));
+
+ /* Set Xhi ctrl pins as CF ctrl pins(IORDY, IOWR, IORD, CE[0:1]) */
+ s3c_gpio_cfgpin(S3C64XX_GPM(5), S3C_GPIO_SFN(1));
+ for (gpio = S3C64XX_GPM(0); gpio <= S3C64XX_GPM(4); gpio++)
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(6));
+}
--- /dev/null
+/* linux/arch/arm/mach-s3c64xx/setup-keypad.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * GPIO configuration for S3C64XX KeyPad device
+ *
+ * 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/gpio.h>
+#include <plat/gpio-cfg.h>
+
+void samsung_keypad_cfg_gpio(unsigned int rows, unsigned int cols)
+{
+ unsigned int gpio;
+ unsigned int end;
+
+ /* Set all the necessary GPK pins to special-function 3: KP_ROW[x] */
+ end = S3C64XX_GPK(8 + rows);
+ for (gpio = S3C64XX_GPK(8); gpio < end; gpio++) {
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(3));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ }
+
+ /* Set all the necessary GPL pins to special-function 3: KP_COL[x] */
+ end = S3C64XX_GPL(0 + cols);
+ for (gpio = S3C64XX_GPL(0); gpio < end; gpio++) {
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(3));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ }
+}
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/gpio.h>
-#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
+#include <plat/sdhci.h>
void s3c64xx_setup_sdhci0_cfg_gpio(struct platform_device *dev, int width)
{
+ struct s3c_sdhci_platdata *pdata = dev->dev.platform_data;
unsigned int gpio;
unsigned int end;
s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
}
- s3c_gpio_setpull(S3C64XX_GPG(6), S3C_GPIO_PULL_UP);
- s3c_gpio_cfgpin(S3C64XX_GPG(6), S3C_GPIO_SFN(2));
+ if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) {
+ s3c_gpio_setpull(S3C64XX_GPG(6), S3C_GPIO_PULL_UP);
+ s3c_gpio_cfgpin(S3C64XX_GPG(6), S3C_GPIO_SFN(2));
+ }
}
void s3c64xx_setup_sdhci1_cfg_gpio(struct platform_device *dev, int width)
{
+ struct s3c_sdhci_platdata *pdata = dev->dev.platform_data;
unsigned int gpio;
unsigned int end;
s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
}
- s3c_gpio_setpull(S3C64XX_GPG(6), S3C_GPIO_PULL_UP);
- s3c_gpio_cfgpin(S3C64XX_GPG(6), S3C_GPIO_SFN(3));
+ if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) {
+ s3c_gpio_setpull(S3C64XX_GPG(6), S3C_GPIO_PULL_UP);
+ s3c_gpio_cfgpin(S3C64XX_GPG(6), S3C_GPIO_SFN(3));
+ }
}
void s3c64xx_setup_sdhci2_cfg_gpio(struct platform_device *dev, int width)
help
Enable S5P6440 CPU support
+config S5P6440_SETUP_I2C1
+ bool
+ help
+ Common setup code for i2c bus 1.
+
config MACH_SMDK6440
bool "SMDK6440"
select CPU_S5P6440
- select SAMSUNG_DEV_TS
- select SAMSUNG_DEV_ADC
+ select S3C_DEV_I2C1
+ select S3C_DEV_RTC
select S3C_DEV_WDT
- select HAVE_S3C2410_WATCHDOG
+ select SAMSUNG_DEV_ADC
+ select SAMSUNG_DEV_TS
+ select S5P6440_SETUP_I2C1
help
Machine support for the Samsung SMDK6440
# device support
obj-y += dev-audio.o
obj-$(CONFIG_S3C64XX_DEV_SPI) += dev-spi.o
+obj-$(CONFIG_S5P6440_SETUP_I2C1) += setup-i2c1.o
#include <plat/devs.h>
#include <plat/clock.h>
#include <plat/s5p6440.h>
+#include <plat/adc-core.h>
static void s5p6440_idle(void)
{
void __init s5p6440_map_io(void)
{
/* initialize any device information early */
- s3c_device_adc.name = "s3c64xx-adc";
+ s3c_adc_setname("s3c64xx-adc");
}
void __init s5p6440_init_clocks(int xtal)
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/gpio.h>
#include <plat/gpio-cfg.h>
#include <plat/audio.h>
-#include <mach/gpio.h>
#include <mach/map.h>
#include <mach/dma.h>
#include <mach/irqs.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/gpio.h>
#include <mach/dma.h>
#include <mach/map.h>
#include <mach/irqs.h>
-#include <mach/gpio.h>
#include <mach/spi-clocks.h>
#include <plat/s3c64xx-spi.h>
#include <linux/kernel.h>
#include <linux/irq.h>
#include <linux/io.h>
+#include <linux/gpio.h>
+
#include <mach/map.h>
-#include <mach/gpio.h>
#include <mach/regs-gpio.h>
+
#include <plat/gpio-core.h>
#include <plat/gpio-cfg.h>
#include <plat/gpio-cfg-helpers.h>
#define IRQ_DISPCON3 S5P_IRQ_VIC1(19)
#define IRQ_FIMGVG S5P_IRQ_VIC1(20)
#define IRQ_EINT_GROUPS S5P_IRQ_VIC1(21)
-#define IRQ_PMUIRQ S5P_IRQ_VIC1(23)
+#define IRQ_PMU S5P_IRQ_VIC1(23)
#define IRQ_HSMMC0 S5P_IRQ_VIC1(24)
#define IRQ_HSMMC1 S5P_IRQ_VIC1(25)
#define IRQ_HSMMC2 IRQ_SPI1 /* shared with SPI1 */
#define S5P_PA_TIMER S5P6440_PA_TIMER
#define S5P6440_PA_RTC (0xEA100000)
-#define S5P_PA_RTC S5P6440_PA_RTC
#define S5P6440_PA_WDT (0xEA200000)
#define S5P_PA_WDT S5P6440_PA_WDT
#define S5P_SZ_UART SZ_256
#define S5P6440_PA_IIC0 (0xEC104000)
+#define S5P6440_PA_IIC1 (0xEC20F000)
#define S5P6440_PA_SPI0 0xEC400000
#define S5P6440_PA_SPI1 0xEC500000
/* compatibiltiy defines. */
#define S3C_PA_UART S5P6440_PA_UART
#define S3C_PA_IIC S5P6440_PA_IIC0
+#define S3C_PA_RTC S5P6440_PA_RTC
+#define S3C_PA_IIC1 S5P6440_PA_IIC1
#define S3C_PA_WDT S5P6440_PA_WDT
#define SAMSUNG_PA_ADC S5P6440_PA_ADC
#ifndef __ASM_ARCH_SYSTEM_H
#define __ASM_ARCH_SYSTEM_H __FILE__
-static void arch_idle(void)
-{
- /* nothing here yet */
-}
+#include <plat/system-reset.h>
-static void arch_reset(char mode, const char *cmd)
+static void arch_idle(void)
{
/* nothing here yet */
}
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/i2c.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <mach/regs-clock.h>
#include <plat/devs.h>
#include <plat/cpu.h>
+#include <plat/iic.h>
#include <plat/pll.h>
#include <plat/adc.h>
#include <plat/ts.h>
-#define S5P6440_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \
+#define SMDK6440_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \
S3C2410_UCON_RXILEVEL | \
S3C2410_UCON_TXIRQMODE | \
S3C2410_UCON_RXIRQMODE | \
S3C2410_UCON_RXFIFO_TOI | \
S3C2443_UCON_RXERR_IRQEN)
-#define S5P6440_ULCON_DEFAULT S3C2410_LCON_CS8
+#define SMDK6440_ULCON_DEFAULT S3C2410_LCON_CS8
-#define S5P6440_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \
+#define SMDK6440_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \
S3C2440_UFCON_TXTRIG16 | \
S3C2410_UFCON_RXTRIG8)
[0] = {
.hwport = 0,
.flags = 0,
- .ucon = S5P6440_UCON_DEFAULT,
- .ulcon = S5P6440_ULCON_DEFAULT,
- .ufcon = S5P6440_UFCON_DEFAULT,
+ .ucon = SMDK6440_UCON_DEFAULT,
+ .ulcon = SMDK6440_ULCON_DEFAULT,
+ .ufcon = SMDK6440_UFCON_DEFAULT,
},
[1] = {
.hwport = 1,
.flags = 0,
- .ucon = S5P6440_UCON_DEFAULT,
- .ulcon = S5P6440_ULCON_DEFAULT,
- .ufcon = S5P6440_UFCON_DEFAULT,
+ .ucon = SMDK6440_UCON_DEFAULT,
+ .ulcon = SMDK6440_ULCON_DEFAULT,
+ .ufcon = SMDK6440_UFCON_DEFAULT,
},
[2] = {
.hwport = 2,
.flags = 0,
- .ucon = S5P6440_UCON_DEFAULT,
- .ulcon = S5P6440_ULCON_DEFAULT,
- .ufcon = S5P6440_UFCON_DEFAULT,
+ .ucon = SMDK6440_UCON_DEFAULT,
+ .ulcon = SMDK6440_ULCON_DEFAULT,
+ .ufcon = SMDK6440_UFCON_DEFAULT,
},
[3] = {
.hwport = 3,
.flags = 0,
- .ucon = S5P6440_UCON_DEFAULT,
- .ulcon = S5P6440_ULCON_DEFAULT,
- .ufcon = S5P6440_UFCON_DEFAULT,
+ .ucon = SMDK6440_UCON_DEFAULT,
+ .ulcon = SMDK6440_ULCON_DEFAULT,
+ .ufcon = SMDK6440_UFCON_DEFAULT,
},
};
static struct platform_device *smdk6440_devices[] __initdata = {
&s5p6440_device_iis,
&s3c_device_adc,
+ &s3c_device_rtc,
+ &s3c_device_i2c0,
+ &s3c_device_i2c1,
&s3c_device_ts,
&s3c_device_wdt,
};
+static struct i2c_board_info smdk6440_i2c_devs0[] __initdata = {
+ { I2C_BOARD_INFO("24c08", 0x50), },
+};
+
+static struct i2c_board_info smdk6440_i2c_devs1[] __initdata = {
+ /* To be populated */
+};
+
static struct s3c2410_ts_mach_info s3c_ts_platform __initdata = {
.delay = 10000,
.presc = 49,
{
s3c24xx_ts_set_platdata(&s3c_ts_platform);
+ /* I2C */
+ s3c_i2c0_set_platdata(NULL);
+ s3c_i2c1_set_platdata(NULL);
+ i2c_register_board_info(0, smdk6440_i2c_devs0,
+ ARRAY_SIZE(smdk6440_i2c_devs0));
+ i2c_register_board_info(1, smdk6440_i2c_devs1,
+ ARRAY_SIZE(smdk6440_i2c_devs1));
+
platform_add_devices(smdk6440_devices, ARRAY_SIZE(smdk6440_devices));
}
struct platform_device; /* don't need the contents */
+#include <linux/gpio.h>
+#include <plat/gpio-cfg.h>
#include <plat/iic.h>
void s3c_i2c0_cfg_gpio(struct platform_device *dev)
{
- /* Will be populated later */
+ s3c_gpio_cfgpin(S5P6440_GPB(5), S3C_GPIO_SFN(2));
+ s3c_gpio_setpull(S5P6440_GPB(5), S3C_GPIO_PULL_UP);
+ s3c_gpio_cfgpin(S5P6440_GPB(6), S3C_GPIO_SFN(2));
+ s3c_gpio_setpull(S5P6440_GPB(6), S3C_GPIO_PULL_UP);
}
--- /dev/null
+/* linux/arch/arm/mach-s5p6440/setup-i2c1.c
+ *
+ * Copyright (c) 2009 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * I2C1 GPIO configuration.
+ *
+ * Based on plat-s3c64xx/setup-i2c0.c
+ *
+ * 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/types.h>
+#include <linux/gpio.h>
+
+struct platform_device; /* don't need the contents */
+
+#include <plat/gpio-cfg.h>
+#include <plat/iic.h>
+
+void s3c_i2c1_cfg_gpio(struct platform_device *dev)
+{
+ s3c_gpio_cfgpin(S5P6440_GPR(9), S3C_GPIO_SFN(6));
+ s3c_gpio_setpull(S5P6440_GPR(9), S3C_GPIO_PULL_UP);
+ s3c_gpio_cfgpin(S5P6440_GPR(10), S3C_GPIO_SFN(6));
+ s3c_gpio_setpull(S5P6440_GPR(10), S3C_GPIO_PULL_UP);
+}
config MACH_SMDK6442
bool "SMDK6442"
select CPU_S5P6442
+ select S3C_DEV_WDT
help
Machine support for Samsung SMDK6442
.parent = &clk_pclkd1,
.enable = s5p6442_clk_ip3_ctrl,
.ctrlbit = (1<<19),
+ }, {
+ .name = "watchdog",
+ .id = -1,
+ .parent = &clk_pclkd1,
+ .enable = s5p6442_clk_ip3_ctrl,
+ .ctrlbit = (1 << 22),
}, {
.name = "timers",
.id = -1,
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/gpio.h>
#include <plat/gpio-cfg.h>
#include <plat/audio.h>
-#include <mach/gpio.h>
#include <mach/map.h>
#include <mach/dma.h>
#include <mach/irqs.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/gpio.h>
#include <mach/dma.h>
#include <mach/map.h>
#include <mach/irqs.h>
-#include <mach/gpio.h>
#include <mach/spi-clocks.h>
#include <plat/s3c64xx-spi.h>
#define IRQ_GPIOINT S5P_IRQ_VIC0(30)
/* VIC1 */
-#define IRQ_nPMUIRQ S5P_IRQ_VIC1(0)
+#define IRQ_PMU S5P_IRQ_VIC1(0)
#define IRQ_ONENAND S5P_IRQ_VIC1(7)
#define IRQ_UART0 S5P_IRQ_VIC1(10)
#define IRQ_UART1 S5P_IRQ_VIC1(11)
#define S5P6442_PA_SYSTIMER (0xEA100000)
+#define S5P6442_PA_WATCHDOG (0xEA200000)
+
#define S5P6442_PA_UART (0xEC000000)
#define S5P_PA_UART0 (S5P6442_PA_UART + 0x0)
#define S5P6442_PA_PCM1 0xF2500000
/* compatibiltiy defines. */
+#define S3C_PA_WDT S5P6442_PA_WATCHDOG
#define S3C_PA_UART S5P6442_PA_UART
#define S3C_PA_IIC S5P6442_PA_IIC0
#ifndef __ASM_ARCH_SYSTEM_H
#define __ASM_ARCH_SYSTEM_H __FILE__
-static void arch_idle(void)
-{
- /* nothing here yet */
-}
+#include <plat/system-reset.h>
-static void arch_reset(char mode, const char *cmd)
+static void arch_idle(void)
{
/* nothing here yet */
}
#include <plat/cpu.h>
/* Following are default values for UCON, ULCON and UFCON UART registers */
-#define S5P6442_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \
+#define SMDK6442_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \
S3C2410_UCON_RXILEVEL | \
S3C2410_UCON_TXIRQMODE | \
S3C2410_UCON_RXIRQMODE | \
S3C2410_UCON_RXFIFO_TOI | \
S3C2443_UCON_RXERR_IRQEN)
-#define S5P6442_ULCON_DEFAULT S3C2410_LCON_CS8
+#define SMDK6442_ULCON_DEFAULT S3C2410_LCON_CS8
-#define S5P6442_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \
+#define SMDK6442_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \
S5PV210_UFCON_TXTRIG4 | \
S5PV210_UFCON_RXTRIG4)
[0] = {
.hwport = 0,
.flags = 0,
- .ucon = S5P6442_UCON_DEFAULT,
- .ulcon = S5P6442_ULCON_DEFAULT,
- .ufcon = S5P6442_UFCON_DEFAULT,
+ .ucon = SMDK6442_UCON_DEFAULT,
+ .ulcon = SMDK6442_ULCON_DEFAULT,
+ .ufcon = SMDK6442_UFCON_DEFAULT,
},
[1] = {
.hwport = 1,
.flags = 0,
- .ucon = S5P6442_UCON_DEFAULT,
- .ulcon = S5P6442_ULCON_DEFAULT,
- .ufcon = S5P6442_UFCON_DEFAULT,
+ .ucon = SMDK6442_UCON_DEFAULT,
+ .ulcon = SMDK6442_ULCON_DEFAULT,
+ .ufcon = SMDK6442_UFCON_DEFAULT,
},
[2] = {
.hwport = 2,
.flags = 0,
- .ucon = S5P6442_UCON_DEFAULT,
- .ulcon = S5P6442_ULCON_DEFAULT,
- .ufcon = S5P6442_UFCON_DEFAULT,
+ .ucon = SMDK6442_UCON_DEFAULT,
+ .ulcon = SMDK6442_ULCON_DEFAULT,
+ .ufcon = SMDK6442_UFCON_DEFAULT,
},
};
static struct platform_device *smdk6442_devices[] __initdata = {
&s5p6442_device_iis0,
+ &s3c_device_wdt,
};
static void __init smdk6442_map_io(void)
help
Common setup code for i2c bus 1.
+config S5PC100_SETUP_IDE
+ bool
+ help
+ Common setup code for S5PC100 IDE GPIO configurations
+
+config S5PC100_SETUP_KEYPAD
+ bool
+ help
+ Common setup code for KEYPAD GPIO configurations.
+
config S5PC100_SETUP_SDHCI
bool
select S5PC100_SETUP_SDHCI_GPIO
bool "SMDKC100"
select CPU_S5PC100
select S3C_DEV_FB
- select S3C_DEV_I2C1
select S3C_DEV_HSMMC
select S3C_DEV_HSMMC1
select S3C_DEV_HSMMC2
+ select S3C_DEV_I2C1
+ select S3C_DEV_RTC
+ select S3C_DEV_WDT
+ select SAMSUNG_DEV_ADC
+ select SAMSUNG_DEV_IDE
+ select SAMSUNG_DEV_KEYPAD
+ select SAMSUNG_DEV_TS
select S5PC100_SETUP_FB_24BPP
select S5PC100_SETUP_I2C1
+ select S5PC100_SETUP_IDE
+ select S5PC100_SETUP_KEYPAD
select S5PC100_SETUP_SDHCI
+ select S5P_DEV_FIMC0
+ select S5P_DEV_FIMC1
+ select S5P_DEV_FIMC2
help
Machine support for the Samsung SMDKC100
obj-$(CONFIG_S5PC100_SETUP_FB_24BPP) += setup-fb-24bpp.o
obj-$(CONFIG_S5PC100_SETUP_I2C1) += setup-i2c1.o
+obj-$(CONFIG_S5PC100_SETUP_IDE) += setup-ide.o
+obj-$(CONFIG_S5PC100_SETUP_KEYPAD) += setup-keypad.o
obj-$(CONFIG_S5PC100_SETUP_SDHCI) += setup-sdhci.o
obj-$(CONFIG_S5PC100_SETUP_SDHCI_GPIO) += setup-sdhci-gpio.o
.enable = s5pc100_d1_5_ctrl,
.ctrlbit = (1 << 7),
}, {
- .name = "keyif",
+ .name = "keypad",
.id = -1,
.parent = &clk_div_d1_bus.clk,
.enable = s5pc100_d1_5_ctrl,
.reg_div = { .reg = S5P_CLK_DIV3, .shift = 24, .size = 4 },
}, {
.clk = {
- .name = "mmc_bus",
+ .name = "sclk_mmc",
.id = 0,
.ctrlbit = (1 << 12),
.enable = s5pc100_sclk1_ctrl,
.reg_div = { .reg = S5P_CLK_DIV3, .shift = 0, .size = 4 },
}, {
.clk = {
- .name = "mmc_bus",
+ .name = "sclk_mmc",
.id = 1,
.ctrlbit = (1 << 13),
.enable = s5pc100_sclk1_ctrl,
.reg_div = { .reg = S5P_CLK_DIV3, .shift = 4, .size = 4 },
}, {
.clk = {
- .name = "mmc_bus",
+ .name = "sclk_mmc",
.id = 2,
.ctrlbit = (1 << 14),
.enable = s5pc100_sclk1_ctrl,
#include <plat/cpu.h>
#include <plat/devs.h>
#include <plat/clock.h>
+#include <plat/ata-core.h>
#include <plat/iic-core.h>
#include <plat/sdhci.h>
+#include <plat/adc-core.h>
#include <plat/onenand-core.h>
+#include <plat/fb-core.h>
#include <plat/s5pc100.h>
s5pc100_default_sdhci1();
s5pc100_default_sdhci2();
+ s3c_adc_setname("s3c64xx-adc");
+
/* the i2c devices are directly compatible with s3c2440 */
s3c_i2c0_setname("s3c2440-i2c");
s3c_i2c1_setname("s3c2440-i2c");
s3c_onenand_setname("s5pc100-onenand");
+ s3c_fb_setname("s5pc100-fb");
+ s3c_cfcon_setname("s5pc100-pata");
}
void __init s5pc100_init_clocks(int xtal)
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/gpio.h>
#include <plat/gpio-cfg.h>
#include <plat/audio.h>
-#include <mach/gpio.h>
#include <mach/map.h>
#include <mach/dma.h>
#include <mach/irqs.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/gpio.h>
#include <mach/dma.h>
#include <mach/map.h>
-#include <mach/gpio.h>
#include <mach/spi-clocks.h>
#include <plat/s3c64xx-spi.h>
#define IRQ_GPIOINT S5P_IRQ_VIC0(30)
/* VIC1: ARM, power, memory, connectivity */
-#define IRQ_CORTEX0 S5P_IRQ_VIC1(0)
+#define IRQ_PMU S5P_IRQ_VIC1(0)
#define IRQ_CORTEX1 S5P_IRQ_VIC1(1)
#define IRQ_CORTEX2 S5P_IRQ_VIC1(2)
#define IRQ_CORTEX3 S5P_IRQ_VIC1(3)
#define IRQ_IEMIEC S5P_IRQ_VIC1(6)
#define IRQ_ONENAND S5P_IRQ_VIC1(7)
#define IRQ_NFC S5P_IRQ_VIC1(8)
-#define IRQ_CFC S5P_IRQ_VIC1(9)
+#define IRQ_CFCON S5P_IRQ_VIC1(9)
#define IRQ_UART0 S5P_IRQ_VIC1(10)
#define IRQ_UART1 S5P_IRQ_VIC1(11)
#define IRQ_UART2 S5P_IRQ_VIC1(12)
#define S5PC100_PA_ONENAND (0xE7100000)
+#define S5PC100_PA_CFCON (0xE7800000)
+
/* DMA */
#define S5PC100_PA_MDMA (0xE8100000)
#define S5PC100_PA_PDMA0 (0xE9000000)
#define S5PC100_PA_SYSTIMER (0xEA100000)
+#define S5PC100_PA_WATCHDOG (0xEA200000)
+#define S5PC100_PA_RTC (0xEA300000)
+
#define S5PC100_PA_UART (0xEC000000)
#define S5P_PA_UART0 (S5PC100_PA_UART + 0x0)
#define S5PC100_PA_FB (0xEE000000)
+#define S5PC100_PA_FIMC0 (0xEE200000)
+#define S5PC100_PA_FIMC1 (0xEE300000)
+#define S5PC100_PA_FIMC2 (0xEE400000)
+
#define S5PC100_PA_I2S0 (0xF2000000)
#define S5PC100_PA_I2S1 (0xF2100000)
#define S5PC100_PA_I2S2 (0xF2200000)
#define S5PC100_PA_PCM0 0xF2400000
#define S5PC100_PA_PCM1 0xF2500000
+#define S5PC100_PA_TSADC (0xF3000000)
+
/* KEYPAD */
#define S5PC100_PA_KEYPAD (0xF3100000)
#define S3C_PA_HSMMC1 S5PC100_PA_HSMMC(1)
#define S3C_PA_HSMMC2 S5PC100_PA_HSMMC(2)
#define S3C_PA_KEYPAD S5PC100_PA_KEYPAD
+#define S3C_PA_WDT S5PC100_PA_WATCHDOG
#define S3C_PA_TSADC S5PC100_PA_TSADC
#define S3C_PA_ONENAND S5PC100_PA_ONENAND
#define S3C_PA_ONENAND_BUF S5PC100_PA_ONENAND_BUF
#define S3C_SZ_ONENAND_BUF S5PC100_SZ_ONENAND_BUF
+#define S3C_PA_RTC S5PC100_PA_RTC
+
+#define SAMSUNG_PA_ADC S5PC100_PA_TSADC
+#define SAMSUNG_PA_CFCON S5PC100_PA_CFCON
+#define SAMSUNG_PA_KEYPAD S5PC100_PA_KEYPAD
+
+#define S5P_PA_FIMC0 S5PC100_PA_FIMC0
+#define S5P_PA_FIMC1 S5PC100_PA_FIMC1
+#define S5P_PA_FIMC2 S5PC100_PA_FIMC2
#endif /* __ASM_ARCH_C100_MAP_H */
#define S5P_CLKDIV1_PCLKD1_SHIFT (16)
#define S5PC100_SWRESET S5PC100_REG_OTHERS(0x000)
+#define S5PC100_MEM_SYS_CFG S5PC100_REG_OTHERS(0x200)
#define S5PC100_SWRESET_RESETVAL 0xc100
+#define MEM_SYS_CFG_EBI_FIX_PRI_CFCON 0x30
+
#endif /* __ASM_ARCH_REGS_CLOCK_H */
#define BLENDCON (0x260)
#define BLENDCON_8BIT_ALPHA (1 << 0)
-/* Per-window palette base addresses (start of palette memory).
- * Each window palette area consists of 256 32-bit entries.
- * START is the first address (entry 0th), END is the address of 255th entry.
- */
-#define WIN0_PAL_BASE (0x2400)
-#define WIN0_PAL_END (0x27fc)
-#define WIN1_PAL_BASE (0x2800)
-#define WIN1_PAL_END (0x2bfc)
-#define WIN2_PAL_BASE (0x2c00)
-#define WIN2_PAL_END (0x2ffc)
-#define WIN3_PAL_BASE (0x3000)
-#define WIN3_PAL_END (0x33fc)
-#define WIN4_PAL_BASE (0x3400)
-#define WIN4_PAL_END (0x37fc)
-
-#define WIN0_PAL(_entry) (WIN0_PAL_BASE + ((_entry) * 4))
-#define WIN1_PAL(_entry) (WIN1_PAL_BASE + ((_entry) * 4))
-#define WIN2_PAL(_entry) (WIN2_PAL_BASE + ((_entry) * 4))
-#define WIN3_PAL(_entry) (WIN3_PAL_BASE + ((_entry) * 4))
-#define WIN4_PAL(_entry) (WIN4_PAL_BASE + ((_entry) * 4))
-
-static inline unsigned int s3c_fb_pal_reg(unsigned int window, int reg)
-{
- switch (window) {
- case 0: return WIN0_PAL(reg);
- case 1: return WIN1_PAL(reg);
- case 2: return WIN2_PAL(reg);
- case 3: return WIN3_PAL(reg);
- case 4: return WIN4_PAL(reg);
- }
-
- BUG();
-}
-
#endif /* __ASM_ARCH_REGS_FB_H */
#ifndef __ASM_ARCH_SYSTEM_H
#define __ASM_ARCH_SYSTEM_H __FILE__
-#include <linux/io.h>
-#include <mach/map.h>
-#include <mach/regs-clock.h>
+#include <plat/system-reset.h>
static void arch_idle(void)
{
/* nothing here yet */
}
-static void arch_reset(char mode, const char *cmd)
-{
- __raw_writel(S5PC100_SWRESET_RESETVAL, S5PC100_SWRESET);
- return;
-}
#endif /* __ASM_ARCH_IRQ_H */
#include <linux/i2c.h>
#include <linux/fb.h>
#include <linux/delay.h>
+#include <linux/input.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <plat/s5pc100.h>
#include <plat/fb.h>
#include <plat/iic.h>
+#include <plat/ata.h>
+#include <plat/adc.h>
+#include <plat/keypad.h>
+#include <plat/ts.h>
/* Following are default values for UCON, ULCON and UFCON UART registers */
-#define S5PC100_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \
+#define SMDKC100_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \
S3C2410_UCON_RXILEVEL | \
S3C2410_UCON_TXIRQMODE | \
S3C2410_UCON_RXIRQMODE | \
S3C2410_UCON_RXFIFO_TOI | \
S3C2443_UCON_RXERR_IRQEN)
-#define S5PC100_ULCON_DEFAULT S3C2410_LCON_CS8
+#define SMDKC100_ULCON_DEFAULT S3C2410_LCON_CS8
-#define S5PC100_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \
+#define SMDKC100_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \
S3C2440_UFCON_RXTRIG8 | \
S3C2440_UFCON_TXTRIG16)
[0] = {
.hwport = 0,
.flags = 0,
- .ucon = S5PC100_UCON_DEFAULT,
- .ulcon = S5PC100_ULCON_DEFAULT,
- .ufcon = S5PC100_UFCON_DEFAULT,
+ .ucon = SMDKC100_UCON_DEFAULT,
+ .ulcon = SMDKC100_ULCON_DEFAULT,
+ .ufcon = SMDKC100_UFCON_DEFAULT,
},
[1] = {
.hwport = 1,
.flags = 0,
- .ucon = S5PC100_UCON_DEFAULT,
- .ulcon = S5PC100_ULCON_DEFAULT,
- .ufcon = S5PC100_UFCON_DEFAULT,
+ .ucon = SMDKC100_UCON_DEFAULT,
+ .ulcon = SMDKC100_ULCON_DEFAULT,
+ .ufcon = SMDKC100_UFCON_DEFAULT,
},
[2] = {
.hwport = 2,
.flags = 0,
- .ucon = S5PC100_UCON_DEFAULT,
- .ulcon = S5PC100_ULCON_DEFAULT,
- .ufcon = S5PC100_UFCON_DEFAULT,
+ .ucon = SMDKC100_UCON_DEFAULT,
+ .ulcon = SMDKC100_ULCON_DEFAULT,
+ .ufcon = SMDKC100_UFCON_DEFAULT,
},
[3] = {
.hwport = 3,
.flags = 0,
- .ucon = S5PC100_UCON_DEFAULT,
- .ulcon = S5PC100_ULCON_DEFAULT,
- .ufcon = S5PC100_UFCON_DEFAULT,
+ .ucon = SMDKC100_UCON_DEFAULT,
+ .ulcon = SMDKC100_ULCON_DEFAULT,
+ .ufcon = SMDKC100_UFCON_DEFAULT,
},
};
static struct s3c_fb_pd_win smdkc100_fb_win0 = {
/* this is to ensure we use win0 */
.win_mode = {
- .pixclock = 1000000000000ULL / ((8+13+3+800)*(7+5+1+480)*80),
.left_margin = 8,
.right_margin = 13,
.upper_margin = 7,
.vsync_len = 1,
.xres = 800,
.yres = 480,
+ .refresh = 80,
},
.max_bpp = 32,
.default_bpp = 16,
.setup_gpio = s5pc100_fb_gpio_setup_24bpp,
};
+static struct s3c_ide_platdata smdkc100_ide_pdata __initdata = {
+ .setup_gpio = s5pc100_ide_setup_gpio,
+};
+
+static uint32_t smdkc100_keymap[] __initdata = {
+ /* KEY(row, col, keycode) */
+ KEY(0, 3, KEY_1), KEY(0, 4, KEY_2), KEY(0, 5, KEY_3),
+ KEY(0, 6, KEY_4), KEY(0, 7, KEY_5),
+ KEY(1, 3, KEY_A), KEY(1, 4, KEY_B), KEY(1, 5, KEY_C),
+ KEY(1, 6, KEY_D), KEY(1, 7, KEY_E)
+};
+
+static struct matrix_keymap_data smdkc100_keymap_data __initdata = {
+ .keymap = smdkc100_keymap,
+ .keymap_size = ARRAY_SIZE(smdkc100_keymap),
+};
+
+static struct samsung_keypad_platdata smdkc100_keypad_data __initdata = {
+ .keymap_data = &smdkc100_keymap_data,
+ .rows = 2,
+ .cols = 8,
+};
+
static struct platform_device *smdkc100_devices[] __initdata = {
+ &s3c_device_adc,
+ &s3c_device_cfcon,
&s3c_device_i2c0,
&s3c_device_i2c1,
&s3c_device_fb,
&s3c_device_hsmmc0,
&s3c_device_hsmmc1,
&s3c_device_hsmmc2,
+ &s3c_device_ts,
+ &s3c_device_wdt,
&smdkc100_lcd_powerdev,
&s5pc100_device_iis0,
+ &samsung_device_keypad,
&s5pc100_device_ac97,
+ &s3c_device_rtc,
+ &s5p_device_fimc0,
+ &s5p_device_fimc1,
+ &s5p_device_fimc2,
+};
+
+static struct s3c2410_ts_mach_info s3c_ts_platform __initdata = {
+ .delay = 10000,
+ .presc = 49,
+ .oversampling_shift = 2,
};
static void __init smdkc100_map_io(void)
static void __init smdkc100_machine_init(void)
{
+ s3c24xx_ts_set_platdata(&s3c_ts_platform);
+
/* I2C */
s3c_i2c0_set_platdata(NULL);
s3c_i2c1_set_platdata(NULL);
i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
s3c_fb_set_platdata(&smdkc100_lcd_pdata);
+ s3c_ide_set_platdata(&smdkc100_ide_pdata);
+
+ samsung_keypad_set_platdata(&smdkc100_keypad_data);
/* LCD init */
gpio_request(S5PC100_GPD(0), "GPD");
--- /dev/null
+/* linux/arch/arm/mach-s5pc100/setup-ide.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * S5PC100 setup information for IDE
+ *
+ * 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/gpio.h>
+#include <linux/io.h>
+
+#include <mach/regs-clock.h>
+#include <plat/gpio-cfg.h>
+
+void s5pc100_ide_setup_gpio(void)
+{
+ u32 reg;
+ u32 gpio = 0;
+
+ /* Independent CF interface, CF chip select configuration */
+ reg = readl(S5PC100_MEM_SYS_CFG) & (~0x3f);
+ writel(reg | MEM_SYS_CFG_EBI_FIX_PRI_CFCON, S5PC100_MEM_SYS_CFG);
+
+ /* CF_Add[0 - 2], CF_IORDY, CF_INTRQ, CF_DMARQ, CF_DMARST, CF_DMACK */
+ for (gpio = S5PC100_GPJ0(0); gpio <= S5PC100_GPJ0(7); gpio++) {
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(4));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ s5p_gpio_set_drvstr(gpio, S5P_GPIO_DRVSTR_LV4);
+ }
+
+ /*CF_Data[0 - 7] */
+ for (gpio = S5PC100_GPJ2(0); gpio <= S5PC100_GPJ2(7); gpio++) {
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(4));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ s5p_gpio_set_drvstr(gpio, S5P_GPIO_DRVSTR_LV4);
+ }
+
+ /* CF_Data[8 - 15] */
+ for (gpio = S5PC100_GPJ3(0); gpio <= S5PC100_GPJ3(7); gpio++) {
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(4));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ s5p_gpio_set_drvstr(gpio, S5P_GPIO_DRVSTR_LV4);
+ }
+
+ /* CF_CS0, CF_CS1, CF_IORD, CF_IOWR */
+ for (gpio = S5PC100_GPJ4(0); gpio <= S5PC100_GPJ4(3); gpio++) {
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(4));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ s5p_gpio_set_drvstr(gpio, S5P_GPIO_DRVSTR_LV4);
+ }
+
+ /* EBI_OE, EBI_WE */
+ for (gpio = S5PC100_GPK0(6); gpio <= S5PC100_GPK0(7); gpio++)
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(0));
+
+ /* CF_OE, CF_WE */
+ for (gpio = S5PC100_GPK1(6); gpio <= S5PC100_GPK1(7); gpio++) {
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ }
+
+ /* CF_CD */
+ s3c_gpio_cfgpin(S5PC100_GPK3(5), S3C_GPIO_SFN(2));
+ s3c_gpio_setpull(S5PC100_GPK3(5), S3C_GPIO_PULL_NONE);
+}
--- /dev/null
+/* linux/arch/arm/mach-s5pc100/setup-keypad.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * GPIO configuration for S5PC100 KeyPad device
+ *
+ * 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/gpio.h>
+#include <plat/gpio-cfg.h>
+
+void samsung_keypad_cfg_gpio(unsigned int rows, unsigned int cols)
+{
+ unsigned int gpio;
+ unsigned int end;
+
+ /* Set all the necessary GPH3 pins to special-function 3: KP_ROW[x] */
+ end = S5PC100_GPH3(rows);
+ for (gpio = S5PC100_GPH3(0); gpio < end; gpio++) {
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(3));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ }
+
+ /* Set all the necessary GPH2 pins to special-function 3: KP_COL[x] */
+ end = S5PC100_GPH2(cols);
+ for (gpio = S5PC100_GPH2(0); gpio < end; gpio++) {
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(3));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ }
+}
#include <plat/gpio-cfg.h>
#include <plat/regs-sdhci.h>
+#include <plat/sdhci.h>
void s5pc100_setup_sdhci0_cfg_gpio(struct platform_device *dev, int width)
{
+ struct s3c_sdhci_platdata *pdata = dev->dev.platform_data;
unsigned int gpio;
unsigned int end;
unsigned int num;
}
}
- s3c_gpio_setpull(S5PC100_GPG1(2), S3C_GPIO_PULL_UP);
- s3c_gpio_cfgpin(S5PC100_GPG1(2), S3C_GPIO_SFN(2));
+ if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) {
+ s3c_gpio_setpull(S5PC100_GPG1(2), S3C_GPIO_PULL_UP);
+ s3c_gpio_cfgpin(S5PC100_GPG1(2), S3C_GPIO_SFN(2));
+ }
}
void s5pc100_setup_sdhci1_cfg_gpio(struct platform_device *dev, int width)
{
+ struct s3c_sdhci_platdata *pdata = dev->dev.platform_data;
unsigned int gpio;
unsigned int end;
s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
}
- s3c_gpio_setpull(S5PC100_GPG2(6), S3C_GPIO_PULL_UP);
- s3c_gpio_cfgpin(S5PC100_GPG2(6), S3C_GPIO_SFN(2));
+ if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) {
+ s3c_gpio_setpull(S5PC100_GPG2(6), S3C_GPIO_PULL_UP);
+ s3c_gpio_cfgpin(S5PC100_GPG2(6), S3C_GPIO_SFN(2));
+ }
}
void s5pc100_setup_sdhci2_cfg_gpio(struct platform_device *dev, int width)
{
+ struct s3c_sdhci_platdata *pdata = dev->dev.platform_data;
unsigned int gpio;
unsigned int end;
s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
}
- s3c_gpio_setpull(S5PC100_GPG3(6), S3C_GPIO_PULL_UP);
- s3c_gpio_cfgpin(S5PC100_GPG3(6), S3C_GPIO_SFN(2));
+ if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) {
+ s3c_gpio_setpull(S5PC100_GPG3(6), S3C_GPIO_PULL_UP);
+ s3c_gpio_cfgpin(S5PC100_GPG3(6), S3C_GPIO_SFN(2));
+ }
}
/* clock sources for the mmc bus clock, order as for the ctrl2[5..4] */
char *s5pc100_hsmmc_clksrcs[4] = {
- [0] = "hsmmc",
- [1] = "hsmmc",
- /* [2] = "mmc_bus", not yet successfully used yet */
- /* [3] = "48m", - note not successfully used yet */
+ [0] = "hsmmc", /* HCLK */
+ /* [1] = "hsmmc", - duplicate HCLK entry */
+ [2] = "sclk_mmc", /* mmc_bus */
+ /* [3] = "48m", - note not successfully used yet */
};
help
Common setup code for i2c bus 2.
+config S5PV210_SETUP_IDE
+ bool
+ help
+ Common setup code for S5PV210 IDE GPIO configurations
+
config S5PV210_SETUP_FB_24BPP
bool
help
Common setup code for S5PV210 with an 24bpp RGB display helper.
+config S5PV210_SETUP_KEYPAD
+ bool
+ help
+ Common setup code for keypad.
+
config S5PV210_SETUP_SDHCI
bool
select S5PV210_SETUP_SDHCI_GPIO
help
Common setup code for SDHCI gpio.
-# machine support
+config S5PC110_DEV_ONENAND
+ bool
+ help
+ Compile in platform device definition for OneNAND1 controller
+
+menu "S5PC110 Machines"
config MACH_AQUILA
- bool "Samsung Aquila"
+ bool "Aquila"
select CPU_S5PV210
select ARCH_SPARSEMEM_ENABLE
- select S5PV210_SETUP_FB_24BPP
select S3C_DEV_FB
+ select S5P_DEV_FIMC0
+ select S5P_DEV_FIMC1
+ select S5P_DEV_FIMC2
+ select S3C_DEV_HSMMC
+ select S3C_DEV_HSMMC1
+ select S3C_DEV_HSMMC2
+ select S5PC110_DEV_ONENAND
+ select S5PV210_SETUP_FB_24BPP
+ select S5PV210_SETUP_SDHCI
help
Machine support for the Samsung Aquila target based on S5PC110 SoC
bool "GONI"
select CPU_S5PV210
select ARCH_SPARSEMEM_ENABLE
+ select S3C_DEV_FB
+ select S5P_DEV_FIMC0
+ select S5P_DEV_FIMC1
+ select S5P_DEV_FIMC2
+ select S3C_DEV_HSMMC
+ select S3C_DEV_HSMMC1
+ select S3C_DEV_HSMMC2
+ select S5PC110_DEV_ONENAND
+ select S5PV210_SETUP_FB_24BPP
+ select S5PV210_SETUP_SDHCI
help
Machine support for Samsung GONI board
S5PC110(MCP) is one of package option of S5PV210
-config S5PC110_DEV_ONENAND
- bool
+config MACH_SMDKC110
+ bool "SMDKC110"
+ select CPU_S5PV210
+ select ARCH_SPARSEMEM_ENABLE
+ select S3C_DEV_I2C1
+ select S3C_DEV_I2C2
+ select S3C_DEV_RTC
+ select S3C_DEV_WDT
+ select SAMSUNG_DEV_IDE
+ select S5PV210_SETUP_I2C1
+ select S5PV210_SETUP_I2C2
+ select S5PV210_SETUP_IDE
help
- Compile in platform device definition for OneNAND1 controller
+ Machine support for Samsung SMDKC110
+ S5PC110(MCP) is one of package option of S5PV210
+
+endmenu
+
+menu "S5PV210 Machines"
config MACH_SMDKV210
bool "SMDKV210"
select CPU_S5PV210
select ARCH_SPARSEMEM_ENABLE
+ select S3C_DEV_HSMMC
+ select S3C_DEV_HSMMC1
+ select S3C_DEV_HSMMC2
+ select S3C_DEV_HSMMC3
+ select S3C_DEV_I2C1
+ select S3C_DEV_I2C2
+ select S3C_DEV_RTC
+ select S3C_DEV_WDT
select SAMSUNG_DEV_ADC
+ select SAMSUNG_DEV_IDE
+ select SAMSUNG_DEV_KEYPAD
select SAMSUNG_DEV_TS
- select S3C_DEV_WDT
- select HAVE_S3C2410_WATCHDOG
+ select S5PV210_SETUP_I2C1
+ select S5PV210_SETUP_I2C2
+ select S5PV210_SETUP_IDE
+ select S5PV210_SETUP_KEYPAD
+ select S5PV210_SETUP_SDHCI
help
Machine support for Samsung SMDKV210
-config MACH_SMDKC110
- bool "SMDKC110"
- select CPU_S5PV210
- select ARCH_SPARSEMEM_ENABLE
- select S3C_DEV_WDT
- select HAVE_S3C2410_WATCHDOG
- help
- Machine support for Samsung SMDKC110
- S5PC110(MCP) is one of package option of S5PV210
+endmenu
endif
obj-$(CONFIG_S5PV210_SETUP_FB_24BPP) += setup-fb-24bpp.o
obj-$(CONFIG_S5PV210_SETUP_I2C1) += setup-i2c1.o
obj-$(CONFIG_S5PV210_SETUP_I2C2) += setup-i2c2.o
+obj-$(CONFIG_S5PV210_SETUP_IDE) += setup-ide.o
+obj-$(CONFIG_S5PV210_SETUP_KEYPAD) += setup-keypad.o
obj-$(CONFIG_S5PV210_SETUP_SDHCI) += setup-sdhci.o
obj-$(CONFIG_S5PV210_SETUP_SDHCI_GPIO) += setup-sdhci-gpio.o
#include <plat/cpu.h>
#include <plat/devs.h>
#include <plat/clock.h>
+#include <plat/fb-core.h>
#include <plat/s5pv210.h>
+#include <plat/adc-core.h>
+#include <plat/ata-core.h>
+#include <plat/fimc-core.h>
#include <plat/iic-core.h>
+#include <plat/keypad-core.h>
#include <plat/sdhci.h>
+#include <plat/reset.h>
/* Initial IO mappings */
local_irq_enable();
}
+static void s5pv210_sw_reset(void)
+{
+ __raw_writel(0x1, S5P_SWRESET);
+}
+
/* s5pv210_map_io
*
* register the standard cpu IO areas
void __init s5pv210_map_io(void)
{
-#ifdef CONFIG_S3C_DEV_ADC
- s3c_device_adc.name = "s3c64xx-adc";
-#endif
-
iotable_init(s5pv210_iodesc, ARRAY_SIZE(s5pv210_iodesc));
/* initialise device information early */
s5pv210_default_sdhci0();
s5pv210_default_sdhci1();
s5pv210_default_sdhci2();
+ s5pv210_default_sdhci3();
+
+ s3c_adc_setname("s3c64xx-adc");
+
+ s3c_cfcon_setname("s5pv210-pata");
+
+ s3c_fimc_setname(0, "s5pv210-fimc");
+ s3c_fimc_setname(1, "s5pv210-fimc");
+ s3c_fimc_setname(2, "s5pv210-fimc");
/* the i2c devices are directly compatible with s3c2440 */
s3c_i2c0_setname("s3c2440-i2c");
s3c_i2c1_setname("s3c2440-i2c");
s3c_i2c2_setname("s3c2440-i2c");
+
+ s3c_fb_setname("s5pv210-fb");
+
+ /* Use s5pv210-keypad instead of samsung-keypad */
+ samsung_keypad_setname("s5pv210-keypad");
}
void __init s5pv210_init_clocks(int xtal)
/* set idle function */
pm_idle = s5pv210_idle;
+ /* set sw_reset function */
+ s5p_reset_hook = s5pv210_sw_reset;
+
return sysdev_register(&s5pv210_sysdev);
}
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/gpio.h>
#include <plat/gpio-cfg.h>
#include <plat/audio.h>
-#include <mach/gpio.h>
#include <mach/map.h>
#include <mach/dma.h>
#include <mach/irqs.h>
},
[1] = {
.start = S5PC110_PA_ONENAND_DMA,
- .end = S5PC110_PA_ONENAND_DMA + SZ_2K - 1,
+ .end = S5PC110_PA_ONENAND_DMA + SZ_8K - 1,
.flags = IORESOURCE_MEM,
},
+ [2] = {
+ .start = IRQ_ONENAND_AUDI,
+ .end = IRQ_ONENAND_AUDI,
+ .flags = IORESOURCE_IRQ,
+ },
};
struct platform_device s5pc110_device_onenand = {
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/gpio.h>
#include <mach/dma.h>
#include <mach/map.h>
#include <mach/irqs.h>
-#include <mach/gpio.h>
#include <mach/spi-clocks.h>
#include <plat/s3c64xx-spi.h>
.ngpio = S5PV210_GPIO_MP03_NR,
.label = "MP03",
},
+ }, {
+ .config = &gpio_cfg_noint,
+ .chip = {
+ .base = S5PV210_MP04(0),
+ .ngpio = S5PV210_GPIO_MP04_NR,
+ .label = "MP04",
+ },
+ }, {
+ .config = &gpio_cfg_noint,
+ .chip = {
+ .base = S5PV210_MP05(0),
+ .ngpio = S5PV210_GPIO_MP05_NR,
+ .label = "MP05",
+ },
}, {
.base = (S5P_VA_GPIO + 0xC00),
.config = &gpio_cfg_noint,
#define S5PV210_GPIO_MP01_NR (8)
#define S5PV210_GPIO_MP02_NR (4)
#define S5PV210_GPIO_MP03_NR (8)
+#define S5PV210_GPIO_MP04_NR (8)
+#define S5PV210_GPIO_MP05_NR (8)
/* GPIO bank numbers */
S5PV210_GPIO_MP01_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_J4),
S5PV210_GPIO_MP02_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_MP01),
S5PV210_GPIO_MP03_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_MP02),
+ S5PV210_GPIO_MP04_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_MP03),
+ S5PV210_GPIO_MP05_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_MP04),
};
/* S5PV210 GPIO number definitions */
#define S5PV210_MP01(_nr) (S5PV210_GPIO_MP01_START + (_nr))
#define S5PV210_MP02(_nr) (S5PV210_GPIO_MP02_START + (_nr))
#define S5PV210_MP03(_nr) (S5PV210_GPIO_MP03_START + (_nr))
+#define S5PV210_MP04(_nr) (S5PV210_GPIO_MP04_START + (_nr))
+#define S5PV210_MP05(_nr) (S5PV210_GPIO_MP05_START + (_nr))
/* the end of the S5PV210 specific gpios */
-#define S5PV210_GPIO_END (S5PV210_MP03(S5PV210_GPIO_MP03_NR) + 1)
+#define S5PV210_GPIO_END (S5PV210_MP05(S5PV210_GPIO_MP05_NR) + 1)
#define S3C_GPIO_END S5PV210_GPIO_END
-/* define the number of gpios we need to the one after the MP03() range */
-#define ARCH_NR_GPIOS (S5PV210_MP03(S5PV210_GPIO_MP03_NR) + \
+/* define the number of gpios we need to the one after the MP05() range */
+#define ARCH_NR_GPIOS (S5PV210_MP05(S5PV210_GPIO_MP05_NR) + \
CONFIG_SAMSUNG_GPIO_EXTRA + 1)
#include <asm-generic/gpio.h>
/* VIC1: ARM, Power, Memory, Connectivity, Storage */
-#define IRQ_CORTEX0 S5P_IRQ_VIC1(0)
+#define IRQ_PMU S5P_IRQ_VIC1(0)
#define IRQ_CORTEX1 S5P_IRQ_VIC1(1)
#define IRQ_CORTEX2 S5P_IRQ_VIC1(2)
#define IRQ_CORTEX3 S5P_IRQ_VIC1(3)
#define IRQ_IEMIEC S5P_IRQ_VIC1(6)
#define IRQ_ONENAND S5P_IRQ_VIC1(7)
#define IRQ_NFC S5P_IRQ_VIC1(8)
-#define IRQ_CFC S5P_IRQ_VIC1(9)
+#define IRQ_CFCON S5P_IRQ_VIC1(9)
#define IRQ_UART0 S5P_IRQ_VIC1(10)
#define IRQ_UART1 S5P_IRQ_VIC1(11)
#define IRQ_UART2 S5P_IRQ_VIC1(12)
#define S5PV210_PA_SPI0 0xE1300000
#define S5PV210_PA_SPI1 0xE1400000
+#define S5PV210_PA_KEYPAD (0xE1600000)
+
#define S5PV210_PA_IIC0 (0xE1800000)
#define S5PV210_PA_IIC1 (0xFAB00000)
#define S5PV210_PA_IIC2 (0xE1A00000)
#define S5PV210_PA_WATCHDOG (0xE2700000)
+#define S5PV210_PA_RTC (0xE2800000)
#define S5PV210_PA_UART (0xE2900000)
#define S5P_PA_UART0 (S5PV210_PA_UART + 0x0)
#define S5PV210_PA_SROMC (0xE8000000)
+#define S5PV210_PA_CFCON (0xE8200000)
+
#define S5PV210_PA_MDMA 0xFA200000
#define S5PV210_PA_PDMA0 0xE0900000
#define S5PV210_PA_PDMA1 0xE0A00000
#define S5PV210_PA_FB (0xF8000000)
+#define S5PV210_PA_FIMC0 (0xFB200000)
+#define S5PV210_PA_FIMC1 (0xFB300000)
+#define S5PV210_PA_FIMC2 (0xFB400000)
+
#define S5PV210_PA_HSMMC(x) (0xEB000000 + ((x) * 0x100000))
#define S5PV210_PA_VIC0 (0xF2000000)
#define S3C_PA_HSMMC0 S5PV210_PA_HSMMC(0)
#define S3C_PA_HSMMC1 S5PV210_PA_HSMMC(1)
#define S3C_PA_HSMMC2 S5PV210_PA_HSMMC(2)
+#define S3C_PA_HSMMC3 S5PV210_PA_HSMMC(3)
#define S3C_PA_IIC S5PV210_PA_IIC0
#define S3C_PA_IIC1 S5PV210_PA_IIC1
#define S3C_PA_IIC2 S5PV210_PA_IIC2
#define S3C_PA_FB S5PV210_PA_FB
+#define S3C_PA_RTC S5PV210_PA_RTC
#define S3C_PA_WDT S5PV210_PA_WATCHDOG
+#define S5P_PA_FIMC0 S5PV210_PA_FIMC0
+#define S5P_PA_FIMC1 S5PV210_PA_FIMC1
+#define S5P_PA_FIMC2 S5PV210_PA_FIMC2
#define SAMSUNG_PA_ADC S5PV210_PA_ADC
+#define SAMSUNG_PA_CFCON S5PV210_PA_CFCON
+#define SAMSUNG_PA_KEYPAD S5PV210_PA_KEYPAD
#endif /* __ASM_ARCH_MAP_H */
#define PHYS_OFFSET UL(0x20000000)
#define CONSISTENT_DMA_SIZE (SZ_8M + SZ_4M + SZ_2M)
-/* Maximum of 256MiB in one bank */
-#define MAX_PHYSMEM_BITS 32
+/*
+ * Sparsemem support
+ * Physical memory can be located from 0x20000000 to 0x7fffffff,
+ * so MAX_PHYSMEM_BITS is 31.
+ */
+
+#define MAX_PHYSMEM_BITS 31
#define SECTION_SIZE_BITS 28
#endif /* __ASM_ARCH_MEMORY_H */
#define S5P_CLKDIV0_PCLK66_SHIFT (28)
#define S5P_CLKDIV0_PCLK66_MASK (0x7 << S5P_CLKDIV0_PCLK66_SHIFT)
+#define S5P_SWRESET S5P_CLKREG(0x2000)
+
/* Registers related to power management */
#define S5P_PWR_CFG S5P_CLKREG(0xC000)
#define S5P_EINT_WAKEUP_MASK S5P_CLKREG(0xC004)
#ifndef __ASM_ARCH_SYSTEM_H
#define __ASM_ARCH_SYSTEM_H __FILE__
-static void arch_idle(void)
-{
- /* nothing here yet */
-}
+#include <plat/system-reset.h>
-static void arch_reset(char mode, const char *cmd)
+static void arch_idle(void)
{
/* nothing here yet */
}
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/fb.h>
+#include <linux/i2c.h>
+#include <linux/i2c-gpio.h>
+#include <linux/mfd/max8998.h>
+#include <linux/gpio_keys.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-fb.h>
+#include <plat/gpio-cfg.h>
#include <plat/regs-serial.h>
#include <plat/s5pv210.h>
#include <plat/devs.h>
#include <plat/cpu.h>
#include <plat/fb.h>
+#include <plat/fimc-core.h>
+#include <plat/sdhci.h>
/* Following are default values for UCON, ULCON and UFCON UART registers */
-#define S5PV210_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \
+#define AQUILA_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \
S3C2410_UCON_RXILEVEL | \
S3C2410_UCON_TXIRQMODE | \
S3C2410_UCON_RXIRQMODE | \
S3C2410_UCON_RXFIFO_TOI | \
S3C2443_UCON_RXERR_IRQEN)
-#define S5PV210_ULCON_DEFAULT S3C2410_LCON_CS8
+#define AQUILA_ULCON_DEFAULT S3C2410_LCON_CS8
-#define S5PV210_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \
- S5PV210_UFCON_TXTRIG4 | \
- S5PV210_UFCON_RXTRIG4)
+#define AQUILA_UFCON_DEFAULT S3C2410_UFCON_FIFOMODE
-static struct s3c2410_uartcfg smdkv210_uartcfgs[] __initdata = {
+static struct s3c2410_uartcfg aquila_uartcfgs[] __initdata = {
[0] = {
.hwport = 0,
.flags = 0,
- .ucon = S5PV210_UCON_DEFAULT,
- .ulcon = S5PV210_ULCON_DEFAULT,
- .ufcon = S5PV210_UFCON_DEFAULT,
+ .ucon = AQUILA_UCON_DEFAULT,
+ .ulcon = AQUILA_ULCON_DEFAULT,
+ /*
+ * Actually UART0 can support 256 bytes fifo, but aquila board
+ * supports 128 bytes fifo because of initial chip bug
+ */
+ .ufcon = AQUILA_UFCON_DEFAULT |
+ S5PV210_UFCON_TXTRIG128 | S5PV210_UFCON_RXTRIG128,
},
[1] = {
.hwport = 1,
.flags = 0,
- .ucon = S5PV210_UCON_DEFAULT,
- .ulcon = S5PV210_ULCON_DEFAULT,
- .ufcon = S5PV210_UFCON_DEFAULT,
+ .ucon = AQUILA_UCON_DEFAULT,
+ .ulcon = AQUILA_ULCON_DEFAULT,
+ .ufcon = AQUILA_UFCON_DEFAULT |
+ S5PV210_UFCON_TXTRIG64 | S5PV210_UFCON_RXTRIG64,
},
[2] = {
.hwport = 2,
.flags = 0,
- .ucon = S5PV210_UCON_DEFAULT,
- .ulcon = S5PV210_ULCON_DEFAULT,
- .ufcon = S5PV210_UFCON_DEFAULT,
+ .ucon = AQUILA_UCON_DEFAULT,
+ .ulcon = AQUILA_ULCON_DEFAULT,
+ .ufcon = AQUILA_UFCON_DEFAULT |
+ S5PV210_UFCON_TXTRIG16 | S5PV210_UFCON_RXTRIG16,
},
[3] = {
.hwport = 3,
.flags = 0,
- .ucon = S5PV210_UCON_DEFAULT,
- .ulcon = S5PV210_ULCON_DEFAULT,
- .ufcon = S5PV210_UFCON_DEFAULT,
+ .ucon = AQUILA_UCON_DEFAULT,
+ .ulcon = AQUILA_ULCON_DEFAULT,
+ .ufcon = AQUILA_UFCON_DEFAULT |
+ S5PV210_UFCON_TXTRIG16 | S5PV210_UFCON_RXTRIG16,
},
};
/* Frame Buffer */
static struct s3c_fb_pd_win aquila_fb_win0 = {
.win_mode = {
- .pixclock = 1000000000000ULL / ((16+16+2+480)*(28+3+2+800)*60),
.left_margin = 16,
.right_margin = 16,
.upper_margin = 3,
static struct s3c_fb_pd_win aquila_fb_win1 = {
.win_mode = {
- .pixclock = 1000000000000ULL / ((16+16+2+480)*(28+3+2+800)*60),
.left_margin = 16,
.right_margin = 16,
.upper_margin = 3,
.setup_gpio = s5pv210_fb_gpio_setup_24bpp,
};
+/* MAX8998 regulators */
+#if defined(CONFIG_REGULATOR_MAX8998) || defined(CONFIG_REGULATOR_MAX8998_MODULE)
+
+static struct regulator_init_data aquila_ldo2_data = {
+ .constraints = {
+ .name = "VALIVE_1.1V",
+ .min_uV = 1100000,
+ .max_uV = 1100000,
+ .apply_uV = 1,
+ .always_on = 1,
+ .state_mem = {
+ .enabled = 1,
+ },
+ },
+};
+
+static struct regulator_init_data aquila_ldo3_data = {
+ .constraints = {
+ .name = "VUSB/MIPI_1.1V",
+ .min_uV = 1100000,
+ .max_uV = 1100000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct regulator_init_data aquila_ldo4_data = {
+ .constraints = {
+ .name = "VDAC_3.3V",
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .apply_uV = 1,
+ },
+};
+
+static struct regulator_init_data aquila_ldo5_data = {
+ .constraints = {
+ .name = "VTF_2.8V",
+ .min_uV = 2800000,
+ .max_uV = 2800000,
+ .apply_uV = 1,
+ },
+};
+
+static struct regulator_init_data aquila_ldo6_data = {
+ .constraints = {
+ .name = "VCC_3.3V",
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .apply_uV = 1,
+ },
+};
+
+static struct regulator_init_data aquila_ldo7_data = {
+ .constraints = {
+ .name = "VCC_3.0V",
+ .min_uV = 3000000,
+ .max_uV = 3000000,
+ .apply_uV = 1,
+ .boot_on = 1,
+ .always_on = 1,
+ },
+};
+
+static struct regulator_init_data aquila_ldo8_data = {
+ .constraints = {
+ .name = "VUSB/VADC_3.3V",
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct regulator_init_data aquila_ldo9_data = {
+ .constraints = {
+ .name = "VCC/VCAM_2.8V",
+ .min_uV = 2800000,
+ .max_uV = 2800000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct regulator_init_data aquila_ldo10_data = {
+ .constraints = {
+ .name = "VPLL_1.1V",
+ .min_uV = 1100000,
+ .max_uV = 1100000,
+ .apply_uV = 1,
+ .boot_on = 1,
+ },
+};
+
+static struct regulator_init_data aquila_ldo11_data = {
+ .constraints = {
+ .name = "CAM_IO_2.8V",
+ .min_uV = 2800000,
+ .max_uV = 2800000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct regulator_init_data aquila_ldo12_data = {
+ .constraints = {
+ .name = "CAM_ISP_1.2V",
+ .min_uV = 1200000,
+ .max_uV = 1200000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct regulator_init_data aquila_ldo13_data = {
+ .constraints = {
+ .name = "CAM_A_2.8V",
+ .min_uV = 2800000,
+ .max_uV = 2800000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct regulator_init_data aquila_ldo14_data = {
+ .constraints = {
+ .name = "CAM_CIF_1.8V",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct regulator_init_data aquila_ldo15_data = {
+ .constraints = {
+ .name = "CAM_AF_3.3V",
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct regulator_init_data aquila_ldo16_data = {
+ .constraints = {
+ .name = "VMIPI_1.8V",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct regulator_init_data aquila_ldo17_data = {
+ .constraints = {
+ .name = "CAM_8M_1.8V",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+/* BUCK */
+static struct regulator_consumer_supply buck1_consumer[] = {
+ { .supply = "vddarm", },
+};
+
+static struct regulator_consumer_supply buck2_consumer[] = {
+ { .supply = "vddint", },
+};
+
+static struct regulator_init_data aquila_buck1_data = {
+ .constraints = {
+ .name = "VARM_1.2V",
+ .min_uV = 1200000,
+ .max_uV = 1200000,
+ .apply_uV = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(buck1_consumer),
+ .consumer_supplies = buck1_consumer,
+};
+
+static struct regulator_init_data aquila_buck2_data = {
+ .constraints = {
+ .name = "VINT_1.2V",
+ .min_uV = 1200000,
+ .max_uV = 1200000,
+ .apply_uV = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(buck2_consumer),
+ .consumer_supplies = buck2_consumer,
+};
+
+static struct regulator_init_data aquila_buck3_data = {
+ .constraints = {
+ .name = "VCC_1.8V",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .apply_uV = 1,
+ .state_mem = {
+ .enabled = 1,
+ },
+ },
+};
+
+static struct regulator_init_data aquila_buck4_data = {
+ .constraints = {
+ .name = "CAM_CORE_1.2V",
+ .min_uV = 1200000,
+ .max_uV = 1200000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct max8998_regulator_data aquila_regulators[] = {
+ { MAX8998_LDO2, &aquila_ldo2_data },
+ { MAX8998_LDO3, &aquila_ldo3_data },
+ { MAX8998_LDO4, &aquila_ldo4_data },
+ { MAX8998_LDO5, &aquila_ldo5_data },
+ { MAX8998_LDO6, &aquila_ldo6_data },
+ { MAX8998_LDO7, &aquila_ldo7_data },
+ { MAX8998_LDO8, &aquila_ldo8_data },
+ { MAX8998_LDO9, &aquila_ldo9_data },
+ { MAX8998_LDO10, &aquila_ldo10_data },
+ { MAX8998_LDO11, &aquila_ldo11_data },
+ { MAX8998_LDO12, &aquila_ldo12_data },
+ { MAX8998_LDO13, &aquila_ldo13_data },
+ { MAX8998_LDO14, &aquila_ldo14_data },
+ { MAX8998_LDO15, &aquila_ldo15_data },
+ { MAX8998_LDO16, &aquila_ldo16_data },
+ { MAX8998_LDO17, &aquila_ldo17_data },
+ { MAX8998_BUCK1, &aquila_buck1_data },
+ { MAX8998_BUCK2, &aquila_buck2_data },
+ { MAX8998_BUCK3, &aquila_buck3_data },
+ { MAX8998_BUCK4, &aquila_buck4_data },
+};
+
+static struct max8998_platform_data aquila_max8998_pdata = {
+ .num_regulators = ARRAY_SIZE(aquila_regulators),
+ .regulators = aquila_regulators,
+};
+#endif
+
+/* GPIO I2C PMIC */
+#define AP_I2C_GPIO_PMIC_BUS_4 4
+static struct i2c_gpio_platform_data aquila_i2c_gpio_pmic_data = {
+ .sda_pin = S5PV210_GPJ4(0), /* XMSMCSN */
+ .scl_pin = S5PV210_GPJ4(3), /* XMSMIRQN */
+};
+
+static struct platform_device aquila_i2c_gpio_pmic = {
+ .name = "i2c-gpio",
+ .id = AP_I2C_GPIO_PMIC_BUS_4,
+ .dev = {
+ .platform_data = &aquila_i2c_gpio_pmic_data,
+ },
+};
+
+static struct i2c_board_info i2c_gpio_pmic_devs[] __initdata = {
+#if defined(CONFIG_REGULATOR_MAX8998) || defined(CONFIG_REGULATOR_MAX8998_MODULE)
+ {
+ /* 0xCC when SRAD = 0 */
+ I2C_BOARD_INFO("max8998", 0xCC >> 1),
+ .platform_data = &aquila_max8998_pdata,
+ },
+#endif
+};
+
+/* PMIC Power button */
+static struct gpio_keys_button aquila_gpio_keys_table[] = {
+ {
+ .code = KEY_POWER,
+ .gpio = S5PV210_GPH2(6),
+ .desc = "gpio-keys: KEY_POWER",
+ .type = EV_KEY,
+ .active_low = 1,
+ .wakeup = 1,
+ .debounce_interval = 1,
+ },
+};
+
+static struct gpio_keys_platform_data aquila_gpio_keys_data = {
+ .buttons = aquila_gpio_keys_table,
+ .nbuttons = ARRAY_SIZE(aquila_gpio_keys_table),
+};
+
+static struct platform_device aquila_device_gpiokeys = {
+ .name = "gpio-keys",
+ .dev = {
+ .platform_data = &aquila_gpio_keys_data,
+ },
+};
+
+static void __init aquila_pmic_init(void)
+{
+ /* AP_PMIC_IRQ: EINT7 */
+ s3c_gpio_cfgpin(S5PV210_GPH0(7), S3C_GPIO_SFN(0xf));
+ s3c_gpio_setpull(S5PV210_GPH0(7), S3C_GPIO_PULL_UP);
+
+ /* nPower: EINT22 */
+ s3c_gpio_cfgpin(S5PV210_GPH2(6), S3C_GPIO_SFN(0xf));
+ s3c_gpio_setpull(S5PV210_GPH2(6), S3C_GPIO_PULL_UP);
+}
+
+/* MoviNAND */
+static struct s3c_sdhci_platdata aquila_hsmmc0_data __initdata = {
+ .max_width = 4,
+ .cd_type = S3C_SDHCI_CD_PERMANENT,
+};
+
+/* Wireless LAN */
+static struct s3c_sdhci_platdata aquila_hsmmc1_data __initdata = {
+ .max_width = 4,
+ .cd_type = S3C_SDHCI_CD_EXTERNAL,
+ /* ext_cd_{init,cleanup} callbacks will be added later */
+};
+
+/* External Flash */
+#define AQUILA_EXT_FLASH_EN S5PV210_MP05(4)
+#define AQUILA_EXT_FLASH_CD S5PV210_GPH3(4)
+static struct s3c_sdhci_platdata aquila_hsmmc2_data __initdata = {
+ .max_width = 4,
+ .cd_type = S3C_SDHCI_CD_GPIO,
+ .ext_cd_gpio = AQUILA_EXT_FLASH_CD,
+ .ext_cd_gpio_invert = 1,
+};
+
+static void aquila_setup_sdhci(void)
+{
+ gpio_request(AQUILA_EXT_FLASH_EN, "FLASH_EN");
+ gpio_direction_output(AQUILA_EXT_FLASH_EN, 1);
+
+ s3c_sdhci0_set_platdata(&aquila_hsmmc0_data);
+ s3c_sdhci1_set_platdata(&aquila_hsmmc1_data);
+ s3c_sdhci2_set_platdata(&aquila_hsmmc2_data);
+};
+
static struct platform_device *aquila_devices[] __initdata = {
+ &aquila_i2c_gpio_pmic,
+ &aquila_device_gpiokeys,
&s3c_device_fb,
+ &s5pc110_device_onenand,
+ &s3c_device_hsmmc0,
+ &s3c_device_hsmmc1,
+ &s3c_device_hsmmc2,
+ &s5p_device_fimc0,
+ &s5p_device_fimc1,
+ &s5p_device_fimc2,
};
static void __init aquila_map_io(void)
{
s5p_init_io(NULL, 0, S5P_VA_CHIPID);
s3c24xx_init_clocks(24000000);
- s3c24xx_init_uarts(smdkv210_uartcfgs, ARRAY_SIZE(smdkv210_uartcfgs));
+ s3c24xx_init_uarts(aquila_uartcfgs, ARRAY_SIZE(aquila_uartcfgs));
}
static void __init aquila_machine_init(void)
{
+ /* PMIC */
+ aquila_pmic_init();
+ i2c_register_board_info(AP_I2C_GPIO_PMIC_BUS_4, i2c_gpio_pmic_devs,
+ ARRAY_SIZE(i2c_gpio_pmic_devs));
+ /* SDHCI */
+ aquila_setup_sdhci();
+
+ s3c_fimc_setname(0, "s5p-fimc");
+ s3c_fimc_setname(1, "s5p-fimc");
+ s3c_fimc_setname(2, "s5p-fimc");
+
/* FB */
s3c_fb_set_platdata(&aquila_lcd_pdata);
#include <linux/types.h>
#include <linux/init.h>
#include <linux/serial_core.h>
+#include <linux/fb.h>
+#include <linux/i2c.h>
+#include <linux/i2c-gpio.h>
+#include <linux/mfd/max8998.h>
+#include <linux/gpio_keys.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
+#include <mach/regs-fb.h>
+#include <plat/gpio-cfg.h>
#include <plat/regs-serial.h>
#include <plat/s5pv210.h>
#include <plat/devs.h>
#include <plat/cpu.h>
+#include <plat/fb.h>
+#include <plat/sdhci.h>
/* Following are default values for UCON, ULCON and UFCON UART registers */
-#define S5PV210_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \
+#define GONI_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \
S3C2410_UCON_RXILEVEL | \
S3C2410_UCON_TXIRQMODE | \
S3C2410_UCON_RXIRQMODE | \
S3C2410_UCON_RXFIFO_TOI | \
S3C2443_UCON_RXERR_IRQEN)
-#define S5PV210_ULCON_DEFAULT S3C2410_LCON_CS8
+#define GONI_ULCON_DEFAULT S3C2410_LCON_CS8
-#define S5PV210_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \
- S5PV210_UFCON_TXTRIG4 | \
- S5PV210_UFCON_RXTRIG4)
+#define GONI_UFCON_DEFAULT S3C2410_UFCON_FIFOMODE
static struct s3c2410_uartcfg goni_uartcfgs[] __initdata = {
[0] = {
.hwport = 0,
.flags = 0,
- .ucon = S5PV210_UCON_DEFAULT,
- .ulcon = S5PV210_ULCON_DEFAULT,
- .ufcon = S5PV210_UFCON_DEFAULT,
+ .ucon = GONI_UCON_DEFAULT,
+ .ulcon = GONI_ULCON_DEFAULT,
+ .ufcon = GONI_UFCON_DEFAULT |
+ S5PV210_UFCON_TXTRIG256 | S5PV210_UFCON_RXTRIG256,
},
[1] = {
.hwport = 1,
.flags = 0,
- .ucon = S5PV210_UCON_DEFAULT,
- .ulcon = S5PV210_ULCON_DEFAULT,
- .ufcon = S5PV210_UFCON_DEFAULT,
+ .ucon = GONI_UCON_DEFAULT,
+ .ulcon = GONI_ULCON_DEFAULT,
+ .ufcon = GONI_UFCON_DEFAULT |
+ S5PV210_UFCON_TXTRIG64 | S5PV210_UFCON_RXTRIG64,
},
[2] = {
.hwport = 2,
.flags = 0,
- .ucon = S5PV210_UCON_DEFAULT,
- .ulcon = S5PV210_ULCON_DEFAULT,
- .ufcon = S5PV210_UFCON_DEFAULT,
+ .ucon = GONI_UCON_DEFAULT,
+ .ulcon = GONI_ULCON_DEFAULT,
+ .ufcon = GONI_UFCON_DEFAULT |
+ S5PV210_UFCON_TXTRIG16 | S5PV210_UFCON_RXTRIG16,
},
[3] = {
.hwport = 3,
.flags = 0,
- .ucon = S5PV210_UCON_DEFAULT,
- .ulcon = S5PV210_ULCON_DEFAULT,
- .ufcon = S5PV210_UFCON_DEFAULT,
+ .ucon = GONI_UCON_DEFAULT,
+ .ulcon = GONI_ULCON_DEFAULT,
+ .ufcon = GONI_UFCON_DEFAULT |
+ S5PV210_UFCON_TXTRIG16 | S5PV210_UFCON_RXTRIG16,
},
};
+/* Frame Buffer */
+static struct s3c_fb_pd_win goni_fb_win0 = {
+ .win_mode = {
+ .pixclock = 1000000000000ULL / ((16+16+2+480)*(28+3+2+800)*55),
+ .left_margin = 16,
+ .right_margin = 16,
+ .upper_margin = 3,
+ .lower_margin = 28,
+ .hsync_len = 2,
+ .vsync_len = 2,
+ .xres = 480,
+ .yres = 800,
+ .refresh = 55,
+ },
+ .max_bpp = 32,
+ .default_bpp = 16,
+};
+
+static struct s3c_fb_platdata goni_lcd_pdata __initdata = {
+ .win[0] = &goni_fb_win0,
+ .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB |
+ VIDCON0_CLKSEL_LCD,
+ .vidcon1 = VIDCON1_INV_VCLK | VIDCON1_INV_VDEN
+ | VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
+ .setup_gpio = s5pv210_fb_gpio_setup_24bpp,
+};
+
+/* MAX8998 regulators */
+#if defined(CONFIG_REGULATOR_MAX8998) || defined(CONFIG_REGULATOR_MAX8998_MODULE)
+
+static struct regulator_init_data goni_ldo2_data = {
+ .constraints = {
+ .name = "VALIVE_1.1V",
+ .min_uV = 1100000,
+ .max_uV = 1100000,
+ .apply_uV = 1,
+ .always_on = 1,
+ .state_mem = {
+ .enabled = 1,
+ },
+ },
+};
+
+static struct regulator_init_data goni_ldo3_data = {
+ .constraints = {
+ .name = "VUSB/MIPI_1.1V",
+ .min_uV = 1100000,
+ .max_uV = 1100000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct regulator_init_data goni_ldo4_data = {
+ .constraints = {
+ .name = "VDAC_3.3V",
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .apply_uV = 1,
+ },
+};
+
+static struct regulator_init_data goni_ldo5_data = {
+ .constraints = {
+ .name = "VTF_2.8V",
+ .min_uV = 2800000,
+ .max_uV = 2800000,
+ .apply_uV = 1,
+ },
+};
+
+static struct regulator_init_data goni_ldo6_data = {
+ .constraints = {
+ .name = "VCC_3.3V",
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .apply_uV = 1,
+ },
+};
+
+static struct regulator_init_data goni_ldo7_data = {
+ .constraints = {
+ .name = "VLCD_1.8V",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct regulator_init_data goni_ldo8_data = {
+ .constraints = {
+ .name = "VUSB/VADC_3.3V",
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct regulator_init_data goni_ldo9_data = {
+ .constraints = {
+ .name = "VCC/VCAM_2.8V",
+ .min_uV = 2800000,
+ .max_uV = 2800000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct regulator_init_data goni_ldo10_data = {
+ .constraints = {
+ .name = "VPLL_1.1V",
+ .min_uV = 1100000,
+ .max_uV = 1100000,
+ .apply_uV = 1,
+ .boot_on = 1,
+ },
+};
+
+static struct regulator_init_data goni_ldo11_data = {
+ .constraints = {
+ .name = "CAM_IO_2.8V",
+ .min_uV = 2800000,
+ .max_uV = 2800000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct regulator_init_data goni_ldo12_data = {
+ .constraints = {
+ .name = "CAM_ISP_1.2V",
+ .min_uV = 1200000,
+ .max_uV = 1200000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct regulator_init_data goni_ldo13_data = {
+ .constraints = {
+ .name = "CAM_A_2.8V",
+ .min_uV = 2800000,
+ .max_uV = 2800000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct regulator_init_data goni_ldo14_data = {
+ .constraints = {
+ .name = "CAM_CIF_1.8V",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct regulator_init_data goni_ldo15_data = {
+ .constraints = {
+ .name = "CAM_AF_3.3V",
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct regulator_init_data goni_ldo16_data = {
+ .constraints = {
+ .name = "VMIPI_1.8V",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct regulator_init_data goni_ldo17_data = {
+ .constraints = {
+ .name = "VCC_3.0V_LCD",
+ .min_uV = 3000000,
+ .max_uV = 3000000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+/* BUCK */
+static struct regulator_consumer_supply buck1_consumer[] = {
+ { .supply = "vddarm", },
+};
+
+static struct regulator_consumer_supply buck2_consumer[] = {
+ { .supply = "vddint", },
+};
+
+static struct regulator_init_data goni_buck1_data = {
+ .constraints = {
+ .name = "VARM_1.2V",
+ .min_uV = 1200000,
+ .max_uV = 1200000,
+ .apply_uV = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(buck1_consumer),
+ .consumer_supplies = buck1_consumer,
+};
+
+static struct regulator_init_data goni_buck2_data = {
+ .constraints = {
+ .name = "VINT_1.2V",
+ .min_uV = 1200000,
+ .max_uV = 1200000,
+ .apply_uV = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(buck2_consumer),
+ .consumer_supplies = buck2_consumer,
+};
+
+static struct regulator_init_data goni_buck3_data = {
+ .constraints = {
+ .name = "VCC_1.8V",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .apply_uV = 1,
+ .state_mem = {
+ .enabled = 1,
+ },
+ },
+};
+
+static struct regulator_init_data goni_buck4_data = {
+ .constraints = {
+ .name = "CAM_CORE_1.2V",
+ .min_uV = 1200000,
+ .max_uV = 1200000,
+ .apply_uV = 1,
+ .always_on = 1,
+ },
+};
+
+static struct max8998_regulator_data goni_regulators[] = {
+ { MAX8998_LDO2, &goni_ldo2_data },
+ { MAX8998_LDO3, &goni_ldo3_data },
+ { MAX8998_LDO4, &goni_ldo4_data },
+ { MAX8998_LDO5, &goni_ldo5_data },
+ { MAX8998_LDO6, &goni_ldo6_data },
+ { MAX8998_LDO7, &goni_ldo7_data },
+ { MAX8998_LDO8, &goni_ldo8_data },
+ { MAX8998_LDO9, &goni_ldo9_data },
+ { MAX8998_LDO10, &goni_ldo10_data },
+ { MAX8998_LDO11, &goni_ldo11_data },
+ { MAX8998_LDO12, &goni_ldo12_data },
+ { MAX8998_LDO13, &goni_ldo13_data },
+ { MAX8998_LDO14, &goni_ldo14_data },
+ { MAX8998_LDO15, &goni_ldo15_data },
+ { MAX8998_LDO16, &goni_ldo16_data },
+ { MAX8998_LDO17, &goni_ldo17_data },
+ { MAX8998_BUCK1, &goni_buck1_data },
+ { MAX8998_BUCK2, &goni_buck2_data },
+ { MAX8998_BUCK3, &goni_buck3_data },
+ { MAX8998_BUCK4, &goni_buck4_data },
+};
+
+static struct max8998_platform_data goni_max8998_pdata = {
+ .num_regulators = ARRAY_SIZE(goni_regulators),
+ .regulators = goni_regulators,
+};
+#endif
+
+/* GPIO I2C PMIC */
+#define AP_I2C_GPIO_PMIC_BUS_4 4
+static struct i2c_gpio_platform_data goni_i2c_gpio_pmic_data = {
+ .sda_pin = S5PV210_GPJ4(0), /* XMSMCSN */
+ .scl_pin = S5PV210_GPJ4(3), /* XMSMIRQN */
+};
+
+static struct platform_device goni_i2c_gpio_pmic = {
+ .name = "i2c-gpio",
+ .id = AP_I2C_GPIO_PMIC_BUS_4,
+ .dev = {
+ .platform_data = &goni_i2c_gpio_pmic_data,
+ },
+};
+
+static struct i2c_board_info i2c_gpio_pmic_devs[] __initdata = {
+#if defined(CONFIG_REGULATOR_MAX8998) || defined(CONFIG_REGULATOR_MAX8998_MODULE)
+ {
+ /* 0xCC when SRAD = 0 */
+ I2C_BOARD_INFO("max8998", 0xCC >> 1),
+ .platform_data = &goni_max8998_pdata,
+ },
+#endif
+};
+
+/* PMIC Power button */
+static struct gpio_keys_button goni_gpio_keys_table[] = {
+ {
+ .code = KEY_POWER,
+ .gpio = S5PV210_GPH2(6),
+ .desc = "gpio-keys: KEY_POWER",
+ .type = EV_KEY,
+ .active_low = 1,
+ .wakeup = 1,
+ .debounce_interval = 1,
+ },
+};
+
+static struct gpio_keys_platform_data goni_gpio_keys_data = {
+ .buttons = goni_gpio_keys_table,
+ .nbuttons = ARRAY_SIZE(goni_gpio_keys_table),
+};
+
+static struct platform_device goni_device_gpiokeys = {
+ .name = "gpio-keys",
+ .dev = {
+ .platform_data = &goni_gpio_keys_data,
+ },
+};
+
+static void __init goni_pmic_init(void)
+{
+ /* AP_PMIC_IRQ: EINT7 */
+ s3c_gpio_cfgpin(S5PV210_GPH0(7), S3C_GPIO_SFN(0xf));
+ s3c_gpio_setpull(S5PV210_GPH0(7), S3C_GPIO_PULL_UP);
+
+ /* nPower: EINT22 */
+ s3c_gpio_cfgpin(S5PV210_GPH2(6), S3C_GPIO_SFN(0xf));
+ s3c_gpio_setpull(S5PV210_GPH2(6), S3C_GPIO_PULL_UP);
+}
+
+/* MoviNAND */
+static struct s3c_sdhci_platdata goni_hsmmc0_data __initdata = {
+ .max_width = 4,
+ .cd_type = S3C_SDHCI_CD_PERMANENT,
+};
+
+/* Wireless LAN */
+static struct s3c_sdhci_platdata goni_hsmmc1_data __initdata = {
+ .max_width = 4,
+ .cd_type = S3C_SDHCI_CD_EXTERNAL,
+ /* ext_cd_{init,cleanup} callbacks will be added later */
+};
+
+/* External Flash */
+#define GONI_EXT_FLASH_EN S5PV210_MP05(4)
+#define GONI_EXT_FLASH_CD S5PV210_GPH3(4)
+static struct s3c_sdhci_platdata goni_hsmmc2_data __initdata = {
+ .max_width = 4,
+ .cd_type = S3C_SDHCI_CD_GPIO,
+ .ext_cd_gpio = GONI_EXT_FLASH_CD,
+ .ext_cd_gpio_invert = 1,
+};
+
+static void goni_setup_sdhci(void)
+{
+ gpio_request(GONI_EXT_FLASH_EN, "FLASH_EN");
+ gpio_direction_output(GONI_EXT_FLASH_EN, 1);
+
+ s3c_sdhci0_set_platdata(&goni_hsmmc0_data);
+ s3c_sdhci1_set_platdata(&goni_hsmmc1_data);
+ s3c_sdhci2_set_platdata(&goni_hsmmc2_data);
+};
+
static struct platform_device *goni_devices[] __initdata = {
+ &s3c_device_fb,
+ &s5pc110_device_onenand,
+ &goni_i2c_gpio_pmic,
+ &goni_device_gpiokeys,
+ &s5p_device_fimc0,
+ &s5p_device_fimc1,
+ &s5p_device_fimc2,
+ &s3c_device_hsmmc0,
+ &s3c_device_hsmmc1,
+ &s3c_device_hsmmc2,
};
static void __init goni_map_io(void)
static void __init goni_machine_init(void)
{
+ /* PMIC */
+ goni_pmic_init();
+ i2c_register_board_info(AP_I2C_GPIO_PMIC_BUS_4, i2c_gpio_pmic_devs,
+ ARRAY_SIZE(i2c_gpio_pmic_devs));
+ /* SDHCI */
+ goni_setup_sdhci();
+
+ /* FB */
+ s3c_fb_set_platdata(&goni_lcd_pdata);
+
platform_add_devices(goni_devices, ARRAY_SIZE(goni_devices));
}
#include <linux/types.h>
#include <linux/init.h>
#include <linux/serial_core.h>
+#include <linux/i2c.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <plat/s5pv210.h>
#include <plat/devs.h>
#include <plat/cpu.h>
+#include <plat/ata.h>
+#include <plat/iic.h>
/* Following are default values for UCON, ULCON and UFCON UART registers */
-#define S5PV210_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \
+#define SMDKC110_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \
S3C2410_UCON_RXILEVEL | \
S3C2410_UCON_TXIRQMODE | \
S3C2410_UCON_RXIRQMODE | \
S3C2410_UCON_RXFIFO_TOI | \
S3C2443_UCON_RXERR_IRQEN)
-#define S5PV210_ULCON_DEFAULT S3C2410_LCON_CS8
+#define SMDKC110_ULCON_DEFAULT S3C2410_LCON_CS8
-#define S5PV210_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \
+#define SMDKC110_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \
S5PV210_UFCON_TXTRIG4 | \
S5PV210_UFCON_RXTRIG4)
[0] = {
.hwport = 0,
.flags = 0,
- .ucon = S5PV210_UCON_DEFAULT,
- .ulcon = S5PV210_ULCON_DEFAULT,
- .ufcon = S5PV210_UFCON_DEFAULT,
+ .ucon = SMDKC110_UCON_DEFAULT,
+ .ulcon = SMDKC110_ULCON_DEFAULT,
+ .ufcon = SMDKC110_UFCON_DEFAULT,
},
[1] = {
.hwport = 1,
.flags = 0,
- .ucon = S5PV210_UCON_DEFAULT,
- .ulcon = S5PV210_ULCON_DEFAULT,
- .ufcon = S5PV210_UFCON_DEFAULT,
+ .ucon = SMDKC110_UCON_DEFAULT,
+ .ulcon = SMDKC110_ULCON_DEFAULT,
+ .ufcon = SMDKC110_UFCON_DEFAULT,
},
[2] = {
.hwport = 2,
.flags = 0,
- .ucon = S5PV210_UCON_DEFAULT,
- .ulcon = S5PV210_ULCON_DEFAULT,
- .ufcon = S5PV210_UFCON_DEFAULT,
+ .ucon = SMDKC110_UCON_DEFAULT,
+ .ulcon = SMDKC110_ULCON_DEFAULT,
+ .ufcon = SMDKC110_UFCON_DEFAULT,
},
[3] = {
.hwport = 3,
.flags = 0,
- .ucon = S5PV210_UCON_DEFAULT,
- .ulcon = S5PV210_ULCON_DEFAULT,
- .ufcon = S5PV210_UFCON_DEFAULT,
+ .ucon = SMDKC110_UCON_DEFAULT,
+ .ulcon = SMDKC110_ULCON_DEFAULT,
+ .ufcon = SMDKC110_UFCON_DEFAULT,
},
};
+static struct s3c_ide_platdata smdkc110_ide_pdata __initdata = {
+ .setup_gpio = s5pv210_ide_setup_gpio,
+};
+
static struct platform_device *smdkc110_devices[] __initdata = {
&s5pv210_device_iis0,
&s5pv210_device_ac97,
+ &s3c_device_cfcon,
+ &s3c_device_i2c0,
+ &s3c_device_i2c1,
+ &s3c_device_i2c2,
+ &s3c_device_rtc,
&s3c_device_wdt,
};
+static struct i2c_board_info smdkc110_i2c_devs0[] __initdata = {
+ { I2C_BOARD_INFO("24c08", 0x50), }, /* Samsung S524AD0XD1 */
+};
+
+static struct i2c_board_info smdkc110_i2c_devs1[] __initdata = {
+ /* To Be Updated */
+};
+
+static struct i2c_board_info smdkc110_i2c_devs2[] __initdata = {
+ /* To Be Updated */
+};
+
static void __init smdkc110_map_io(void)
{
s5p_init_io(NULL, 0, S5P_VA_CHIPID);
static void __init smdkc110_machine_init(void)
{
+ s3c_i2c0_set_platdata(NULL);
+ s3c_i2c1_set_platdata(NULL);
+ s3c_i2c2_set_platdata(NULL);
+ i2c_register_board_info(0, smdkc110_i2c_devs0,
+ ARRAY_SIZE(smdkc110_i2c_devs0));
+ i2c_register_board_info(1, smdkc110_i2c_devs1,
+ ARRAY_SIZE(smdkc110_i2c_devs1));
+ i2c_register_board_info(2, smdkc110_i2c_devs2,
+ ARRAY_SIZE(smdkc110_i2c_devs2));
+
+ s3c_ide_set_platdata(&smdkc110_ide_pdata);
+
platform_add_devices(smdkc110_devices, ARRAY_SIZE(smdkc110_devices));
}
#include <linux/kernel.h>
#include <linux/types.h>
+#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <plat/cpu.h>
#include <plat/adc.h>
#include <plat/ts.h>
+#include <plat/ata.h>
+#include <plat/iic.h>
+#include <plat/keypad.h>
/* Following are default values for UCON, ULCON and UFCON UART registers */
-#define S5PV210_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \
+#define SMDKV210_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \
S3C2410_UCON_RXILEVEL | \
S3C2410_UCON_TXIRQMODE | \
S3C2410_UCON_RXIRQMODE | \
S3C2410_UCON_RXFIFO_TOI | \
S3C2443_UCON_RXERR_IRQEN)
-#define S5PV210_ULCON_DEFAULT S3C2410_LCON_CS8
+#define SMDKV210_ULCON_DEFAULT S3C2410_LCON_CS8
-#define S5PV210_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \
+#define SMDKV210_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \
S5PV210_UFCON_TXTRIG4 | \
S5PV210_UFCON_RXTRIG4)
[0] = {
.hwport = 0,
.flags = 0,
- .ucon = S5PV210_UCON_DEFAULT,
- .ulcon = S5PV210_ULCON_DEFAULT,
- .ufcon = S5PV210_UFCON_DEFAULT,
+ .ucon = SMDKV210_UCON_DEFAULT,
+ .ulcon = SMDKV210_ULCON_DEFAULT,
+ .ufcon = SMDKV210_UFCON_DEFAULT,
},
[1] = {
.hwport = 1,
.flags = 0,
- .ucon = S5PV210_UCON_DEFAULT,
- .ulcon = S5PV210_ULCON_DEFAULT,
- .ufcon = S5PV210_UFCON_DEFAULT,
+ .ucon = SMDKV210_UCON_DEFAULT,
+ .ulcon = SMDKV210_ULCON_DEFAULT,
+ .ufcon = SMDKV210_UFCON_DEFAULT,
},
[2] = {
.hwport = 2,
.flags = 0,
- .ucon = S5PV210_UCON_DEFAULT,
- .ulcon = S5PV210_ULCON_DEFAULT,
- .ufcon = S5PV210_UFCON_DEFAULT,
+ .ucon = SMDKV210_UCON_DEFAULT,
+ .ulcon = SMDKV210_ULCON_DEFAULT,
+ .ufcon = SMDKV210_UFCON_DEFAULT,
},
[3] = {
.hwport = 3,
.flags = 0,
- .ucon = S5PV210_UCON_DEFAULT,
- .ulcon = S5PV210_ULCON_DEFAULT,
- .ufcon = S5PV210_UFCON_DEFAULT,
+ .ucon = SMDKV210_UCON_DEFAULT,
+ .ulcon = SMDKV210_ULCON_DEFAULT,
+ .ufcon = SMDKV210_UFCON_DEFAULT,
},
};
+static struct s3c_ide_platdata smdkv210_ide_pdata __initdata = {
+ .setup_gpio = s5pv210_ide_setup_gpio,
+};
+
+static uint32_t smdkv210_keymap[] __initdata = {
+ /* KEY(row, col, keycode) */
+ KEY(0, 3, KEY_1), KEY(0, 4, KEY_2), KEY(0, 5, KEY_3),
+ KEY(0, 6, KEY_4), KEY(0, 7, KEY_5),
+ KEY(1, 3, KEY_A), KEY(1, 4, KEY_B), KEY(1, 5, KEY_C),
+ KEY(1, 6, KEY_D), KEY(1, 7, KEY_E)
+};
+
+static struct matrix_keymap_data smdkv210_keymap_data __initdata = {
+ .keymap = smdkv210_keymap,
+ .keymap_size = ARRAY_SIZE(smdkv210_keymap),
+};
+
+static struct samsung_keypad_platdata smdkv210_keypad_data __initdata = {
+ .keymap_data = &smdkv210_keymap_data,
+ .rows = 8,
+ .cols = 8,
+};
+
static struct platform_device *smdkv210_devices[] __initdata = {
&s5pv210_device_iis0,
&s5pv210_device_ac97,
&s3c_device_adc,
+ &s3c_device_cfcon,
+ &s3c_device_hsmmc0,
+ &s3c_device_hsmmc1,
+ &s3c_device_hsmmc2,
+ &s3c_device_hsmmc3,
+ &s3c_device_i2c0,
+ &s3c_device_i2c1,
+ &s3c_device_i2c2,
+ &samsung_device_keypad,
+ &s3c_device_rtc,
&s3c_device_ts,
&s3c_device_wdt,
};
+static struct i2c_board_info smdkv210_i2c_devs0[] __initdata = {
+ { I2C_BOARD_INFO("24c08", 0x50), }, /* Samsung S524AD0XD1 */
+};
+
+static struct i2c_board_info smdkv210_i2c_devs1[] __initdata = {
+ /* To Be Updated */
+};
+
+static struct i2c_board_info smdkv210_i2c_devs2[] __initdata = {
+ /* To Be Updated */
+};
+
static struct s3c2410_ts_mach_info s3c_ts_platform __initdata = {
.delay = 10000,
.presc = 49,
static void __init smdkv210_machine_init(void)
{
+ samsung_keypad_set_platdata(&smdkv210_keypad_data);
s3c24xx_ts_set_platdata(&s3c_ts_platform);
+
+ s3c_i2c0_set_platdata(NULL);
+ s3c_i2c1_set_platdata(NULL);
+ s3c_i2c2_set_platdata(NULL);
+ i2c_register_board_info(0, smdkv210_i2c_devs0,
+ ARRAY_SIZE(smdkv210_i2c_devs0));
+ i2c_register_board_info(1, smdkv210_i2c_devs1,
+ ARRAY_SIZE(smdkv210_i2c_devs1));
+ i2c_register_board_info(2, smdkv210_i2c_devs2,
+ ARRAY_SIZE(smdkv210_i2c_devs2));
+
+ s3c_ide_set_platdata(&smdkv210_ide_pdata);
+
platform_add_devices(smdkv210_devices, ARRAY_SIZE(smdkv210_devices));
}
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fb.h>
+#include <linux/gpio.h>
#include <mach/regs-fb.h>
-#include <mach/gpio.h>
#include <mach/map.h>
#include <plat/fb.h>
#include <mach/regs-clock.h>
#include <linux/kernel.h>
#include <linux/types.h>
+#include <linux/gpio.h>
struct platform_device; /* don't need the contents */
-#include <mach/gpio.h>
#include <plat/iic.h>
#include <plat/gpio-cfg.h>
#include <linux/kernel.h>
#include <linux/types.h>
+#include <linux/gpio.h>
struct platform_device; /* don't need the contents */
-#include <mach/gpio.h>
#include <plat/iic.h>
#include <plat/gpio-cfg.h>
#include <linux/kernel.h>
#include <linux/types.h>
+#include <linux/gpio.h>
struct platform_device; /* don't need the contents */
-#include <mach/gpio.h>
#include <plat/iic.h>
#include <plat/gpio-cfg.h>
--- /dev/null
+/* linux/arch/arm/mach-s5pv210/setup-ide.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * S5PV210 setup information for IDE
+ *
+ * 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/gpio.h>
+
+#include <plat/gpio-cfg.h>
+
+void s5pv210_ide_setup_gpio(void)
+{
+ unsigned int gpio = 0;
+
+ for (gpio = S5PV210_GPJ0(0); gpio <= S5PV210_GPJ0(7); gpio++) {
+ /* CF_Add[0 - 2], CF_IORDY, CF_INTRQ, CF_DMARQ, CF_DMARST,
+ CF_DMACK */
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(4));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ s5p_gpio_set_drvstr(gpio, S5P_GPIO_DRVSTR_LV4);
+ }
+
+ for (gpio = S5PV210_GPJ2(0); gpio <= S5PV210_GPJ2(7); gpio++) {
+ /*CF_Data[0 - 7] */
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(4));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ s5p_gpio_set_drvstr(gpio, S5P_GPIO_DRVSTR_LV4);
+ }
+
+ for (gpio = S5PV210_GPJ3(0); gpio <= S5PV210_GPJ3(7); gpio++) {
+ /* CF_Data[8 - 15] */
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(4));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ s5p_gpio_set_drvstr(gpio, S5P_GPIO_DRVSTR_LV4);
+ }
+
+ for (gpio = S5PV210_GPJ4(0); gpio <= S5PV210_GPJ4(3); gpio++) {
+ /* CF_CS0, CF_CS1, CF_IORD, CF_IOWR */
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(4));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ s5p_gpio_set_drvstr(gpio, S5P_GPIO_DRVSTR_LV4);
+ }
+}
--- /dev/null
+/*
+ * linux/arch/arm/mach-s5pv210/setup-keypad.c
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/gpio.h>
+#include <plat/gpio-cfg.h>
+
+void samsung_keypad_cfg_gpio(unsigned int rows, unsigned int cols)
+{
+ unsigned int gpio, end;
+
+ /* Set all the necessary GPH3 pins to special-function 3: KP_ROW[x] */
+ end = S5PV210_GPH3(rows);
+ for (gpio = S5PV210_GPH3(0); gpio < end; gpio++) {
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(3));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ }
+
+ /* Set all the necessary GPH2 pins to special-function 3: KP_COL[x] */
+ end = S5PV210_GPH2(cols);
+ for (gpio = S5PV210_GPH2(0); gpio < end; gpio++) {
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(3));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ }
+}
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/gpio.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
-#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <plat/regs-sdhci.h>
+#include <plat/sdhci.h>
void s5pv210_setup_sdhci0_cfg_gpio(struct platform_device *dev, int width)
{
+ struct s3c_sdhci_platdata *pdata = dev->dev.platform_data;
unsigned int gpio;
/* Set all the necessary GPG0/GPG1 pins to special-function 2 */
break;
}
- s3c_gpio_setpull(S5PV210_GPG0(2), S3C_GPIO_PULL_UP);
- s3c_gpio_cfgpin(S5PV210_GPG0(2), S3C_GPIO_SFN(2));
+ if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) {
+ s3c_gpio_setpull(S5PV210_GPG0(2), S3C_GPIO_PULL_UP);
+ s3c_gpio_cfgpin(S5PV210_GPG0(2), S3C_GPIO_SFN(2));
+ }
}
void s5pv210_setup_sdhci1_cfg_gpio(struct platform_device *dev, int width)
{
+ struct s3c_sdhci_platdata *pdata = dev->dev.platform_data;
unsigned int gpio;
/* Set all the necessary GPG1[0:1] pins to special-function 2 */
s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
}
- s3c_gpio_setpull(S5PV210_GPG1(2), S3C_GPIO_PULL_UP);
- s3c_gpio_cfgpin(S5PV210_GPG1(2), S3C_GPIO_SFN(2));
+ if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) {
+ s3c_gpio_setpull(S5PV210_GPG1(2), S3C_GPIO_PULL_UP);
+ s3c_gpio_cfgpin(S5PV210_GPG1(2), S3C_GPIO_SFN(2));
+ }
}
void s5pv210_setup_sdhci2_cfg_gpio(struct platform_device *dev, int width)
{
+ struct s3c_sdhci_platdata *pdata = dev->dev.platform_data;
unsigned int gpio;
/* Set all the necessary GPG2[0:1] pins to special-function 2 */
break;
}
- s3c_gpio_setpull(S5PV210_GPG2(2), S3C_GPIO_PULL_UP);
- s3c_gpio_cfgpin(S5PV210_GPG2(2), S3C_GPIO_SFN(2));
+ if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) {
+ s3c_gpio_setpull(S5PV210_GPG2(2), S3C_GPIO_PULL_UP);
+ s3c_gpio_cfgpin(S5PV210_GPG2(2), S3C_GPIO_SFN(2));
+ }
+}
+
+void s5pv210_setup_sdhci3_cfg_gpio(struct platform_device *dev, int width)
+{
+ struct s3c_sdhci_platdata *pdata = dev->dev.platform_data;
+ unsigned int gpio;
+
+ /* Set all the necessary GPG3[0:2] pins to special-function 2 */
+ for (gpio = S5PV210_GPG3(0); gpio < S5PV210_GPG3(2); gpio++) {
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ }
+
+ /* Data pin GPG3[3:6] to special-function 2 */
+ for (gpio = S5PV210_GPG3(3); gpio <= S5PV210_GPG3(6); gpio++) {
+ s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2));
+ s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
+ }
+
+ if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) {
+ s3c_gpio_setpull(S5PV210_GPG3(2), S3C_GPIO_PULL_UP);
+ s3c_gpio_cfgpin(S5PV210_GPG3(2), S3C_GPIO_SFN(2));
+ }
}
char *s5pv210_hsmmc_clksrcs[4] = {
[0] = "hsmmc", /* HCLK */
- [1] = "hsmmc", /* HCLK */
+ /* [1] = "hsmmc", - duplicate HCLK entry */
[2] = "sclk_mmc", /* mmc_bus */
- /*[4] = reserved */
+ /* [3] = NULL, - reserved */
};
void s5pv210_setup_sdhci_cfg_card(struct platform_device *dev,
--- /dev/null
+# arch/arm/mach-s5pv310/Kconfig
+#
+# Copyright (c) 2010 Samsung Electronics Co., Ltd.
+# http://www.samsung.com/
+#
+# Licensed under GPLv2
+
+# Configuration options for the S5PV310
+
+if ARCH_S5PV310
+
+config CPU_S5PV310
+ bool
+ select PLAT_S5P
+ help
+ Enable S5PV310 CPU support
+
+config S5PV310_SETUP_I2C1
+ bool
+ help
+ Common setup code for i2c bus 1.
+
+config S5PV310_SETUP_I2C2
+ bool
+ help
+ Common setup code for i2c bus 2.
+
+# machine support
+
+config MACH_SMDKV310
+ bool "SMDKV310"
+ select CPU_S5PV310
+ select ARCH_SPARSEMEM_ENABLE
+ help
+ Machine support for Samsung SMDKV310
+
+config MACH_UNIVERSAL_C210
+ bool "Mobile UNIVERSAL_C210 Board"
+ select CPU_S5PV310
+ select ARCH_SPARSEMEM_ENABLE
+ help
+ Machine support for Samsung Mobile Universal S5PC210 Reference
+ Board. S5PC210(MCP) is one of package option of S5PV310
+
+endif
--- /dev/null
+# arch/arm/mach-s5pv310/Makefile
+#
+# Copyright (c) 2010 Samsung Electronics Co., Ltd.
+# http://www.samsung.com/
+#
+# Licensed under GPLv2
+
+obj-y :=
+obj-m :=
+obj-n :=
+obj- :=
+
+# Core support for S5PV310 system
+
+obj-$(CONFIG_CPU_S5PV310) += cpu.o init.o clock.o irq-combiner.o
+obj-$(CONFIG_CPU_S5PV310) += setup-i2c0.o time.o
+
+obj-$(CONFIG_SMP) += platsmp.o headsmp.o
+obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o
+obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
+
+# machine support
+
+obj-$(CONFIG_MACH_SMDKV310) += mach-smdkv310.o
+obj-$(CONFIG_MACH_UNIVERSAL_C210) += mach-universal_c210.o
+
+# device support
+
+obj-$(CONFIG_S5PV310_SETUP_I2C1) += setup-i2c1.o
+obj-$(CONFIG_S5PV310_SETUP_I2C2) += setup-i2c2.o
--- /dev/null
+ zreladdr-y := 0x40008000
+params_phys-y := 0x40000100
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/clock.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * S5PV310 - Clock support
+ *
+ * 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/err.h>
+#include <linux/io.h>
+
+#include <plat/cpu-freq.h>
+#include <plat/clock.h>
+#include <plat/cpu.h>
+#include <plat/pll.h>
+#include <plat/s5p-clock.h>
+#include <plat/clock-clksrc.h>
+
+#include <mach/map.h>
+#include <mach/regs-clock.h>
+
+static struct clk clk_sclk_hdmi27m = {
+ .name = "sclk_hdmi27m",
+ .id = -1,
+ .rate = 27000000,
+};
+
+/* Core list of CMU_CPU side */
+
+static struct clksrc_clk clk_mout_apll = {
+ .clk = {
+ .name = "mout_apll",
+ .id = -1,
+ },
+ .sources = &clk_src_apll,
+ .reg_src = { .reg = S5P_CLKSRC_CPU, .shift = 0, .size = 1 },
+ .reg_div = { .reg = S5P_CLKDIV_CPU, .shift = 24, .size = 3 },
+};
+
+static struct clksrc_clk clk_mout_epll = {
+ .clk = {
+ .name = "mout_epll",
+ .id = -1,
+ },
+ .sources = &clk_src_epll,
+ .reg_src = { .reg = S5P_CLKSRC_TOP0, .shift = 4, .size = 1 },
+};
+
+static struct clksrc_clk clk_mout_mpll = {
+ .clk = {
+ .name = "mout_mpll",
+ .id = -1,
+ },
+ .sources = &clk_src_mpll,
+ .reg_src = { .reg = S5P_CLKSRC_CPU, .shift = 8, .size = 1 },
+};
+
+static struct clk *clkset_moutcore_list[] = {
+ [0] = &clk_mout_apll.clk,
+ [1] = &clk_mout_mpll.clk,
+};
+
+static struct clksrc_sources clkset_moutcore = {
+ .sources = clkset_moutcore_list,
+ .nr_sources = ARRAY_SIZE(clkset_moutcore_list),
+};
+
+static struct clksrc_clk clk_moutcore = {
+ .clk = {
+ .name = "moutcore",
+ .id = -1,
+ },
+ .sources = &clkset_moutcore,
+ .reg_src = { .reg = S5P_CLKSRC_CPU, .shift = 16, .size = 1 },
+};
+
+static struct clksrc_clk clk_coreclk = {
+ .clk = {
+ .name = "core_clk",
+ .id = -1,
+ .parent = &clk_moutcore.clk,
+ },
+ .reg_div = { .reg = S5P_CLKDIV_CPU, .shift = 0, .size = 3 },
+};
+
+static struct clksrc_clk clk_armclk = {
+ .clk = {
+ .name = "armclk",
+ .id = -1,
+ .parent = &clk_coreclk.clk,
+ },
+};
+
+static struct clksrc_clk clk_aclk_corem0 = {
+ .clk = {
+ .name = "aclk_corem0",
+ .id = -1,
+ .parent = &clk_coreclk.clk,
+ },
+ .reg_div = { .reg = S5P_CLKDIV_CPU, .shift = 4, .size = 3 },
+};
+
+static struct clksrc_clk clk_aclk_cores = {
+ .clk = {
+ .name = "aclk_cores",
+ .id = -1,
+ .parent = &clk_coreclk.clk,
+ },
+ .reg_div = { .reg = S5P_CLKDIV_CPU, .shift = 4, .size = 3 },
+};
+
+static struct clksrc_clk clk_aclk_corem1 = {
+ .clk = {
+ .name = "aclk_corem1",
+ .id = -1,
+ .parent = &clk_coreclk.clk,
+ },
+ .reg_div = { .reg = S5P_CLKDIV_CPU, .shift = 8, .size = 3 },
+};
+
+static struct clksrc_clk clk_periphclk = {
+ .clk = {
+ .name = "periphclk",
+ .id = -1,
+ .parent = &clk_coreclk.clk,
+ },
+ .reg_div = { .reg = S5P_CLKDIV_CPU, .shift = 12, .size = 3 },
+};
+
+static struct clksrc_clk clk_atclk = {
+ .clk = {
+ .name = "atclk",
+ .id = -1,
+ .parent = &clk_moutcore.clk,
+ },
+ .reg_div = { .reg = S5P_CLKDIV_CPU, .shift = 16, .size = 3 },
+};
+
+static struct clksrc_clk clk_pclk_dbg = {
+ .clk = {
+ .name = "pclk_dbg",
+ .id = -1,
+ .parent = &clk_atclk.clk,
+ },
+ .reg_div = { .reg = S5P_CLKDIV_CPU, .shift = 20, .size = 3 },
+};
+
+/* Core list of CMU_CORE side */
+
+static struct clk *clkset_corebus_list[] = {
+ [0] = &clk_mout_mpll.clk,
+ [1] = &clk_mout_apll.clk,
+};
+
+static struct clksrc_sources clkset_mout_corebus = {
+ .sources = clkset_corebus_list,
+ .nr_sources = ARRAY_SIZE(clkset_corebus_list),
+};
+
+static struct clksrc_clk clk_mout_corebus = {
+ .clk = {
+ .name = "mout_corebus",
+ .id = -1,
+ },
+ .sources = &clkset_mout_corebus,
+ .reg_src = { .reg = S5P_CLKSRC_CORE, .shift = 4, .size = 1 },
+};
+
+static struct clksrc_clk clk_sclk_dmc = {
+ .clk = {
+ .name = "sclk_dmc",
+ .id = -1,
+ .parent = &clk_mout_corebus.clk,
+ },
+ .reg_div = { .reg = S5P_CLKDIV_CORE0, .shift = 12, .size = 3 },
+};
+
+static struct clksrc_clk clk_aclk_cored = {
+ .clk = {
+ .name = "aclk_cored",
+ .id = -1,
+ .parent = &clk_sclk_dmc.clk,
+ },
+ .reg_div = { .reg = S5P_CLKDIV_CORE0, .shift = 16, .size = 3 },
+};
+
+static struct clksrc_clk clk_aclk_corep = {
+ .clk = {
+ .name = "aclk_corep",
+ .id = -1,
+ .parent = &clk_aclk_cored.clk,
+ },
+ .reg_div = { .reg = S5P_CLKDIV_CORE0, .shift = 20, .size = 3 },
+};
+
+static struct clksrc_clk clk_aclk_acp = {
+ .clk = {
+ .name = "aclk_acp",
+ .id = -1,
+ .parent = &clk_mout_corebus.clk,
+ },
+ .reg_div = { .reg = S5P_CLKDIV_CORE0, .shift = 0, .size = 3 },
+};
+
+static struct clksrc_clk clk_pclk_acp = {
+ .clk = {
+ .name = "pclk_acp",
+ .id = -1,
+ .parent = &clk_aclk_acp.clk,
+ },
+ .reg_div = { .reg = S5P_CLKDIV_CORE0, .shift = 4, .size = 3 },
+};
+
+/* Core list of CMU_TOP side */
+
+static struct clk *clkset_aclk_top_list[] = {
+ [0] = &clk_mout_mpll.clk,
+ [1] = &clk_mout_apll.clk,
+};
+
+static struct clksrc_sources clkset_aclk_200 = {
+ .sources = clkset_aclk_top_list,
+ .nr_sources = ARRAY_SIZE(clkset_aclk_top_list),
+};
+
+static struct clksrc_clk clk_aclk_200 = {
+ .clk = {
+ .name = "aclk_200",
+ .id = -1,
+ },
+ .sources = &clkset_aclk_200,
+ .reg_src = { .reg = S5P_CLKSRC_TOP0, .shift = 12, .size = 1 },
+ .reg_div = { .reg = S5P_CLKDIV_TOP, .shift = 0, .size = 3 },
+};
+
+static struct clksrc_sources clkset_aclk_100 = {
+ .sources = clkset_aclk_top_list,
+ .nr_sources = ARRAY_SIZE(clkset_aclk_top_list),
+};
+
+static struct clksrc_clk clk_aclk_100 = {
+ .clk = {
+ .name = "aclk_100",
+ .id = -1,
+ },
+ .sources = &clkset_aclk_100,
+ .reg_src = { .reg = S5P_CLKSRC_TOP0, .shift = 16, .size = 1 },
+ .reg_div = { .reg = S5P_CLKDIV_TOP, .shift = 4, .size = 4 },
+};
+
+static struct clksrc_sources clkset_aclk_160 = {
+ .sources = clkset_aclk_top_list,
+ .nr_sources = ARRAY_SIZE(clkset_aclk_top_list),
+};
+
+static struct clksrc_clk clk_aclk_160 = {
+ .clk = {
+ .name = "aclk_160",
+ .id = -1,
+ },
+ .sources = &clkset_aclk_160,
+ .reg_src = { .reg = S5P_CLKSRC_TOP0, .shift = 20, .size = 1 },
+ .reg_div = { .reg = S5P_CLKDIV_TOP, .shift = 8, .size = 3 },
+};
+
+static struct clksrc_sources clkset_aclk_133 = {
+ .sources = clkset_aclk_top_list,
+ .nr_sources = ARRAY_SIZE(clkset_aclk_top_list),
+};
+
+static struct clksrc_clk clk_aclk_133 = {
+ .clk = {
+ .name = "aclk_133",
+ .id = -1,
+ },
+ .sources = &clkset_aclk_133,
+ .reg_src = { .reg = S5P_CLKSRC_TOP0, .shift = 24, .size = 1 },
+ .reg_div = { .reg = S5P_CLKDIV_TOP, .shift = 12, .size = 3 },
+};
+
+static struct clk *clkset_vpllsrc_list[] = {
+ [0] = &clk_fin_vpll,
+ [1] = &clk_sclk_hdmi27m,
+};
+
+static struct clksrc_sources clkset_vpllsrc = {
+ .sources = clkset_vpllsrc_list,
+ .nr_sources = ARRAY_SIZE(clkset_vpllsrc_list),
+};
+
+static struct clksrc_clk clk_vpllsrc = {
+ .clk = {
+ .name = "vpll_src",
+ .id = -1,
+ },
+ .sources = &clkset_vpllsrc,
+ .reg_src = { .reg = S5P_CLKSRC_TOP1, .shift = 0, .size = 1 },
+};
+
+static struct clk *clkset_sclk_vpll_list[] = {
+ [0] = &clk_vpllsrc.clk,
+ [1] = &clk_fout_vpll,
+};
+
+static struct clksrc_sources clkset_sclk_vpll = {
+ .sources = clkset_sclk_vpll_list,
+ .nr_sources = ARRAY_SIZE(clkset_sclk_vpll_list),
+};
+
+static struct clksrc_clk clk_sclk_vpll = {
+ .clk = {
+ .name = "sclk_vpll",
+ .id = -1,
+ },
+ .sources = &clkset_sclk_vpll,
+ .reg_src = { .reg = S5P_CLKSRC_TOP0, .shift = 8, .size = 1 },
+};
+
+static int s5pv310_clk_ip_peril_ctrl(struct clk *clk, int enable)
+{
+ return s5p_gatectrl(S5P_CLKGATE_IP_PERIL, clk, enable);
+}
+
+static struct clk init_clocks_disable[] = {
+ {
+ .name = "timers",
+ .id = -1,
+ .parent = &clk_aclk_100.clk,
+ .enable = s5pv310_clk_ip_peril_ctrl,
+ .ctrlbit = (1<<24),
+ }
+};
+
+static struct clk init_clocks[] = {
+ /* Nothing here yet */
+};
+
+static struct clk *clkset_group_list[] = {
+ [0] = &clk_ext_xtal_mux,
+ [1] = &clk_xusbxti,
+ [2] = &clk_sclk_hdmi27m,
+ [6] = &clk_mout_mpll.clk,
+ [7] = &clk_mout_epll.clk,
+ [8] = &clk_sclk_vpll.clk,
+};
+
+static struct clksrc_sources clkset_group = {
+ .sources = clkset_group_list,
+ .nr_sources = ARRAY_SIZE(clkset_group_list),
+};
+
+static struct clksrc_clk clksrcs[] = {
+ {
+ .clk = {
+ .name = "uclk1",
+ .id = 0,
+ .ctrlbit = (1 << 0),
+ .enable = s5pv310_clk_ip_peril_ctrl,
+ },
+ .sources = &clkset_group,
+ .reg_src = { .reg = S5P_CLKSRC_PERIL0, .shift = 0, .size = 4 },
+ .reg_div = { .reg = S5P_CLKDIV_PERIL0, .shift = 0, .size = 4 },
+ }, {
+ .clk = {
+ .name = "uclk1",
+ .id = 1,
+ .enable = s5pv310_clk_ip_peril_ctrl,
+ .ctrlbit = (1 << 1),
+ },
+ .sources = &clkset_group,
+ .reg_src = { .reg = S5P_CLKSRC_PERIL0, .shift = 4, .size = 4 },
+ .reg_div = { .reg = S5P_CLKDIV_PERIL0, .shift = 4, .size = 4 },
+ }, {
+ .clk = {
+ .name = "uclk1",
+ .id = 2,
+ .enable = s5pv310_clk_ip_peril_ctrl,
+ .ctrlbit = (1 << 2),
+ },
+ .sources = &clkset_group,
+ .reg_src = { .reg = S5P_CLKSRC_PERIL0, .shift = 8, .size = 4 },
+ .reg_div = { .reg = S5P_CLKDIV_PERIL0, .shift = 8, .size = 4 },
+ }, {
+ .clk = {
+ .name = "uclk1",
+ .id = 3,
+ .enable = s5pv310_clk_ip_peril_ctrl,
+ .ctrlbit = (1 << 3),
+ },
+ .sources = &clkset_group,
+ .reg_src = { .reg = S5P_CLKSRC_PERIL0, .shift = 12, .size = 4 },
+ .reg_div = { .reg = S5P_CLKDIV_PERIL0, .shift = 12, .size = 4 },
+ }, {
+ .clk = {
+ .name = "sclk_pwm",
+ .id = -1,
+ .enable = s5pv310_clk_ip_peril_ctrl,
+ .ctrlbit = (1 << 24),
+ },
+ .sources = &clkset_group,
+ .reg_src = { .reg = S5P_CLKSRC_PERIL0, .shift = 24, .size = 4 },
+ .reg_div = { .reg = S5P_CLKDIV_PERIL3, .shift = 0, .size = 4 },
+ },
+};
+
+/* Clock initialization code */
+static struct clksrc_clk *sysclks[] = {
+ &clk_mout_apll,
+ &clk_mout_epll,
+ &clk_mout_mpll,
+ &clk_moutcore,
+ &clk_coreclk,
+ &clk_armclk,
+ &clk_aclk_corem0,
+ &clk_aclk_cores,
+ &clk_aclk_corem1,
+ &clk_periphclk,
+ &clk_atclk,
+ &clk_pclk_dbg,
+ &clk_mout_corebus,
+ &clk_sclk_dmc,
+ &clk_aclk_cored,
+ &clk_aclk_corep,
+ &clk_aclk_acp,
+ &clk_pclk_acp,
+ &clk_vpllsrc,
+ &clk_sclk_vpll,
+ &clk_aclk_200,
+ &clk_aclk_100,
+ &clk_aclk_160,
+ &clk_aclk_133,
+};
+
+void __init_or_cpufreq s5pv310_setup_clocks(void)
+{
+ struct clk *xtal_clk;
+ unsigned long apll;
+ unsigned long mpll;
+ unsigned long epll;
+ unsigned long vpll;
+ unsigned long vpllsrc;
+ unsigned long xtal;
+ unsigned long armclk;
+ unsigned long aclk_corem0;
+ unsigned long aclk_cores;
+ unsigned long aclk_corem1;
+ unsigned long periphclk;
+ unsigned long sclk_dmc;
+ unsigned long aclk_cored;
+ unsigned long aclk_corep;
+ unsigned long aclk_acp;
+ unsigned long pclk_acp;
+ unsigned int ptr;
+
+ printk(KERN_DEBUG "%s: registering clocks\n", __func__);
+
+ xtal_clk = clk_get(NULL, "xtal");
+ BUG_ON(IS_ERR(xtal_clk));
+
+ xtal = clk_get_rate(xtal_clk);
+ clk_put(xtal_clk);
+
+ printk(KERN_DEBUG "%s: xtal is %ld\n", __func__, xtal);
+
+ apll = s5p_get_pll45xx(xtal, __raw_readl(S5P_APLL_CON0), pll_4508);
+ mpll = s5p_get_pll45xx(xtal, __raw_readl(S5P_MPLL_CON0), pll_4508);
+ epll = s5p_get_pll46xx(xtal, __raw_readl(S5P_EPLL_CON0),
+ __raw_readl(S5P_EPLL_CON1), pll_4500);
+
+ vpllsrc = clk_get_rate(&clk_vpllsrc.clk);
+ vpll = s5p_get_pll46xx(vpllsrc, __raw_readl(S5P_VPLL_CON0),
+ __raw_readl(S5P_VPLL_CON1), pll_4502);
+
+ clk_fout_apll.rate = apll;
+ clk_fout_mpll.rate = mpll;
+ clk_fout_epll.rate = epll;
+ clk_fout_vpll.rate = vpll;
+
+ printk(KERN_INFO "S5PV310: PLL settings, A=%ld, M=%ld, E=%ld V=%ld",
+ apll, mpll, epll, vpll);
+
+ armclk = clk_get_rate(&clk_armclk.clk);
+ aclk_corem0 = clk_get_rate(&clk_aclk_corem0.clk);
+ aclk_cores = clk_get_rate(&clk_aclk_cores.clk);
+ aclk_corem1 = clk_get_rate(&clk_aclk_corem1.clk);
+ periphclk = clk_get_rate(&clk_periphclk.clk);
+ sclk_dmc = clk_get_rate(&clk_sclk_dmc.clk);
+ aclk_cored = clk_get_rate(&clk_aclk_cored.clk);
+ aclk_corep = clk_get_rate(&clk_aclk_corep.clk);
+ aclk_acp = clk_get_rate(&clk_aclk_acp.clk);
+ pclk_acp = clk_get_rate(&clk_pclk_acp.clk);
+
+ printk(KERN_INFO "S5PV310: ARMCLK=%ld, COREM0=%ld, CORES=%ld\n"
+ "COREM1=%ld, PERI=%ld, DMC=%ld, CORED=%ld\n"
+ "COREP=%ld, ACLK_ACP=%ld, PCLK_ACP=%ld",
+ armclk, aclk_corem0, aclk_cores, aclk_corem1,
+ periphclk, sclk_dmc, aclk_cored, aclk_corep,
+ aclk_acp, pclk_acp);
+
+ clk_f.rate = armclk;
+ clk_h.rate = sclk_dmc;
+ clk_p.rate = periphclk;
+
+ for (ptr = 0; ptr < ARRAY_SIZE(clksrcs); ptr++)
+ s3c_set_clksrc(&clksrcs[ptr], true);
+}
+
+static struct clk *clks[] __initdata = {
+ /* Nothing here yet */
+};
+
+void __init s5pv310_register_clocks(void)
+{
+ struct clk *clkp;
+ int ret;
+ int ptr;
+
+ ret = s3c24xx_register_clocks(clks, ARRAY_SIZE(clks));
+ if (ret > 0)
+ printk(KERN_ERR "Failed to register %u clocks\n", ret);
+
+ for (ptr = 0; ptr < ARRAY_SIZE(sysclks); ptr++)
+ s3c_register_clksrc(sysclks[ptr], 1);
+
+ s3c_register_clksrc(clksrcs, ARRAY_SIZE(clksrcs));
+ s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks));
+
+ clkp = init_clocks_disable;
+ for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
+ ret = s3c24xx_register_clock(clkp);
+ if (ret < 0) {
+ printk(KERN_ERR "Failed to register clock %s (%d)\n",
+ clkp->name, ret);
+ }
+ (clkp->enable)(clkp, 0);
+ }
+
+ s3c_pwmclk_init();
+}
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/cpu.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/sched.h>
+#include <linux/sysdev.h>
+
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <asm/proc-fns.h>
+
+#include <plat/cpu.h>
+#include <plat/clock.h>
+#include <plat/s5pv310.h>
+
+#include <mach/regs-irq.h>
+
+void __iomem *gic_cpu_base_addr;
+
+extern int combiner_init(unsigned int combiner_nr, void __iomem *base,
+ unsigned int irq_start);
+extern void combiner_cascade_irq(unsigned int combiner_nr, unsigned int irq);
+
+/* Initial IO mappings */
+static struct map_desc s5pv310_iodesc[] __initdata = {
+ {
+ .virtual = (unsigned long)S5P_VA_COREPERI_BASE,
+ .pfn = __phys_to_pfn(S5PV310_PA_COREPERI),
+ .length = SZ_8K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)S5P_VA_COMBINER_BASE,
+ .pfn = __phys_to_pfn(S5PV310_PA_COMBINER),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)S5P_VA_L2CC,
+ .pfn = __phys_to_pfn(S5PV310_PA_L2CC),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ },
+};
+
+static void s5pv310_idle(void)
+{
+ if (!need_resched())
+ cpu_do_idle();
+
+ local_irq_enable();
+}
+
+/* s5pv310_map_io
+ *
+ * register the standard cpu IO areas
+*/
+void __init s5pv310_map_io(void)
+{
+ iotable_init(s5pv310_iodesc, ARRAY_SIZE(s5pv310_iodesc));
+}
+
+void __init s5pv310_init_clocks(int xtal)
+{
+ printk(KERN_DEBUG "%s: initializing clocks\n", __func__);
+
+ s3c24xx_register_baseclocks(xtal);
+ s5p_register_clocks(xtal);
+ s5pv310_register_clocks();
+ s5pv310_setup_clocks();
+}
+
+void __init s5pv310_init_irq(void)
+{
+ int irq;
+
+ gic_cpu_base_addr = S5P_VA_GIC_CPU;
+ gic_dist_init(0, S5P_VA_GIC_DIST, IRQ_LOCALTIMER);
+ gic_cpu_init(0, S5P_VA_GIC_CPU);
+
+ for (irq = 0; irq < MAX_COMBINER_NR; irq++) {
+ combiner_init(irq, (void __iomem *)S5P_VA_COMBINER(irq),
+ COMBINER_IRQ(irq, 0));
+ combiner_cascade_irq(irq, IRQ_SPI(irq));
+ }
+
+ /* The parameters of s5p_init_irq() are for VIC init.
+ * Theses parameters should be NULL and 0 because S5PV310
+ * uses GIC instead of VIC.
+ */
+ s5p_init_irq(NULL, 0);
+}
+
+struct sysdev_class s5pv310_sysclass = {
+ .name = "s5pv310-core",
+};
+
+static struct sys_device s5pv310_sysdev = {
+ .cls = &s5pv310_sysclass,
+};
+
+static int __init s5pv310_core_init(void)
+{
+ return sysdev_class_register(&s5pv310_sysclass);
+}
+
+core_initcall(s5pv310_core_init);
+
+int __init s5pv310_init(void)
+{
+ printk(KERN_INFO "S5PV310: Initializing architecture\n");
+
+ /* set idle function */
+ pm_idle = s5pv310_idle;
+
+ return sysdev_register(&s5pv310_sysdev);
+}
--- /dev/null
+/*
+ * linux/arch/arm/mach-s5pv310/headsmp.S
+ *
+ * Cloned from linux/arch/arm/mach-realview/headsmp.S
+ *
+ * Copyright (c) 2003 ARM Limited
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+ __INIT
+
+/*
+ * s5pv310 specific entry point for secondary CPUs. This provides
+ * a "holding pen" into which all secondary cores are held until we're
+ * ready for them to initialise.
+ */
+ENTRY(s5pv310_secondary_startup)
+ mrc p15, 0, r0, c0, c0, 5
+ and r0, r0, #15
+ adr r4, 1f
+ ldmia r4, {r5, r6}
+ sub r4, r4, r5
+ add r6, r6, r4
+pen: ldr r7, [r6]
+ cmp r7, r0
+ bne pen
+
+ /*
+ * we've been released from the holding pen: secondary_stack
+ * should now contain the SVC stack for this core
+ */
+ b secondary_startup
+
+1: .long .
+ .long pen_release
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/include/mach/debug-macro.S
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Based on arch/arm/mach-s3c6400/include/mach/debug-macro.S
+ *
+ * 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.
+*/
+
+/* pull in the relevant register and map files. */
+
+#include <mach/map.h>
+
+ /* note, for the boot process to work we have to keep the UART
+ * virtual address aligned to an 1MiB boundary for the L1
+ * mapping the head code makes. We keep the UART virtual address
+ * aligned and add in the offset when we load the value here.
+ */
+
+ .macro addruart, rx, tmp
+ mrc p15, 0, \rx, c1, c0
+ tst \rx, #1
+ ldreq \rx, = S3C_PA_UART
+ ldrne \rx, = S3C_VA_UART
+#if CONFIG_DEBUG_S3C_UART != 0
+ add \rx, \rx, #(0x10000 * CONFIG_DEBUG_S3C_UART)
+#endif
+ .endm
+
+#define fifo_full fifo_full_s5pv210
+#define fifo_level fifo_level_s5pv210
+
+#include <plat/debug-macro.S>
--- /dev/null
+/* arch/arm/mach-s5pv310/include/mach/entry-macro.S
+ *
+ * Cloned from arch/arm/mach-realview/include/mach/entry-macro.S
+ *
+ * Low-level IRQ helper macros for S5PV310 platforms
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+*/
+
+#include <mach/hardware.h>
+#include <asm/hardware/gic.h>
+
+ .macro disable_fiq
+ .endm
+
+ .macro get_irqnr_preamble, base, tmp
+ ldr \base, =gic_cpu_base_addr
+ ldr \base, [\base]
+ .endm
+
+ .macro arch_ret_to_user, tmp1, tmp2
+ .endm
+
+ /*
+ * The interrupt numbering scheme is defined in the
+ * interrupt controller spec. To wit:
+ *
+ * Interrupts 0-15 are IPI
+ * 16-28 are reserved
+ * 29-31 are local. We allow 30 to be used for the watchdog.
+ * 32-1020 are global
+ * 1021-1022 are reserved
+ * 1023 is "spurious" (no interrupt)
+ *
+ * For now, we ignore all local interrupts so only return an interrupt if it's
+ * between 30 and 1020. The test_for_ipi routine below will pick up on IPIs.
+ *
+ * A simple read from the controller will tell us the number of the highest
+ * priority enabled interrupt. We then just need to check whether it is in the
+ * valid range for an IRQ (30-1020 inclusive).
+ */
+
+ .macro get_irqnr_and_base, irqnr, irqstat, base, tmp
+
+ ldr \irqstat, [\base, #GIC_CPU_INTACK] /* bits 12-10 = src CPU, 9-0 = int # */
+
+ ldr \tmp, =1021
+
+ bic \irqnr, \irqstat, #0x1c00
+
+ cmp \irqnr, #29
+ cmpcc \irqnr, \irqnr
+ cmpne \irqnr, \tmp
+ cmpcs \irqnr, \irqnr
+ addne \irqnr, \irqnr, #32
+
+ .endm
+
+ /* We assume that irqstat (the raw value of the IRQ acknowledge
+ * register) is preserved from the macro above.
+ * If there is an IPI, we immediately signal end of interrupt on the
+ * controller, since this requires the original irqstat value which
+ * we won't easily be able to recreate later.
+ */
+
+ .macro test_for_ipi, irqnr, irqstat, base, tmp
+ bic \irqnr, \irqstat, #0x1c00
+ cmp \irqnr, #16
+ strcc \irqstat, [\base, #GIC_CPU_EOI]
+ cmpcs \irqnr, \irqnr
+ .endm
+
+ /* As above, this assumes that irqstat and base are preserved.. */
+
+ .macro test_for_ltirq, irqnr, irqstat, base, tmp
+ bic \irqnr, \irqstat, #0x1c00
+ mov \tmp, #0
+ cmp \irqnr, #29
+ moveq \tmp, #1
+ streq \irqstat, [\base, #GIC_CPU_EOI]
+ cmp \tmp, #0
+ .endm
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/include/mach/gpio.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * S5PV310 - GPIO lib support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_GPIO_H
+#define __ASM_ARCH_GPIO_H __FILE__
+
+#define gpio_get_value __gpio_get_value
+#define gpio_set_value __gpio_set_value
+#define gpio_cansleep __gpio_cansleep
+#define gpio_to_irq __gpio_to_irq
+
+/* Practically, GPIO banks upto GPZ are the configurable gpio banks */
+
+/* GPIO bank sizes */
+#define S5PV310_GPIO_A0_NR (8)
+#define S5PV310_GPIO_A1_NR (6)
+#define S5PV310_GPIO_B_NR (8)
+#define S5PV310_GPIO_C0_NR (5)
+#define S5PV310_GPIO_C1_NR (5)
+#define S5PV310_GPIO_D0_NR (4)
+#define S5PV310_GPIO_D1_NR (4)
+#define S5PV310_GPIO_E0_NR (5)
+#define S5PV310_GPIO_E1_NR (8)
+#define S5PV310_GPIO_E2_NR (6)
+#define S5PV310_GPIO_E3_NR (8)
+#define S5PV310_GPIO_E4_NR (8)
+#define S5PV310_GPIO_F0_NR (8)
+#define S5PV310_GPIO_F1_NR (8)
+#define S5PV310_GPIO_F2_NR (8)
+#define S5PV310_GPIO_F3_NR (6)
+#define S5PV310_GPIO_J0_NR (8)
+#define S5PV310_GPIO_J1_NR (5)
+#define S5PV310_GPIO_K0_NR (7)
+#define S5PV310_GPIO_K1_NR (7)
+#define S5PV310_GPIO_K2_NR (7)
+#define S5PV310_GPIO_K3_NR (7)
+#define S5PV310_GPIO_L0_NR (8)
+#define S5PV310_GPIO_L1_NR (3)
+#define S5PV310_GPIO_L2_NR (8)
+#define S5PV310_GPIO_X0_NR (8)
+#define S5PV310_GPIO_X1_NR (8)
+#define S5PV310_GPIO_X2_NR (8)
+#define S5PV310_GPIO_X3_NR (8)
+#define S5PV310_GPIO_Z_NR (7)
+
+/* GPIO bank numbers */
+
+#define S5PV310_GPIO_NEXT(__gpio) \
+ ((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 1)
+
+enum s5p_gpio_number {
+ S5PV310_GPIO_A0_START = 0,
+ S5PV310_GPIO_A1_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_A0),
+ S5PV310_GPIO_B_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_A1),
+ S5PV310_GPIO_C0_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_B),
+ S5PV310_GPIO_C1_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_C0),
+ S5PV310_GPIO_D0_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_C1),
+ S5PV310_GPIO_D1_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_D0),
+ S5PV310_GPIO_E0_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_D1),
+ S5PV310_GPIO_E1_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_E0),
+ S5PV310_GPIO_E2_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_E1),
+ S5PV310_GPIO_E3_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_E2),
+ S5PV310_GPIO_E4_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_E3),
+ S5PV310_GPIO_F0_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_E4),
+ S5PV310_GPIO_F1_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_F0),
+ S5PV310_GPIO_F2_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_F1),
+ S5PV310_GPIO_F3_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_F2),
+ S5PV310_GPIO_J0_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_F3),
+ S5PV310_GPIO_J1_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_J0),
+ S5PV310_GPIO_K0_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_J1),
+ S5PV310_GPIO_K1_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_K0),
+ S5PV310_GPIO_K2_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_K1),
+ S5PV310_GPIO_K3_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_K2),
+ S5PV310_GPIO_L0_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_K3),
+ S5PV310_GPIO_L1_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_L0),
+ S5PV310_GPIO_L2_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_L1),
+ S5PV310_GPIO_X0_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_L2),
+ S5PV310_GPIO_X1_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_X0),
+ S5PV310_GPIO_X2_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_X1),
+ S5PV310_GPIO_X3_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_X2),
+ S5PV310_GPIO_Z_START = S5PV310_GPIO_NEXT(S5PV310_GPIO_X3),
+};
+
+/* S5PV310 GPIO number definitions */
+#define S5PV310_GPA0(_nr) (S5PV310_GPIO_A0_START + (_nr))
+#define S5PV310_GPA1(_nr) (S5PV310_GPIO_A1_START + (_nr))
+#define S5PV310_GPB(_nr) (S5PV310_GPIO_B_START + (_nr))
+#define S5PV310_GPC0(_nr) (S5PV310_GPIO_C0_START + (_nr))
+#define S5PV310_GPC1(_nr) (S5PV310_GPIO_C1_START + (_nr))
+#define S5PV310_GPD0(_nr) (S5PV310_GPIO_D0_START + (_nr))
+#define S5PV310_GPD1(_nr) (S5PV310_GPIO_D1_START + (_nr))
+#define S5PV310_GPE0(_nr) (S5PV310_GPIO_E0_START + (_nr))
+#define S5PV310_GPE1(_nr) (S5PV310_GPIO_E1_START + (_nr))
+#define S5PV310_GPE2(_nr) (S5PV310_GPIO_E2_START + (_nr))
+#define S5PV310_GPE3(_nr) (S5PV310_GPIO_E3_START + (_nr))
+#define S5PV310_GPE4(_nr) (S5PV310_GPIO_E4_START + (_nr))
+#define S5PV310_GPF0(_nr) (S5PV310_GPIO_F0_START + (_nr))
+#define S5PV310_GPF1(_nr) (S5PV310_GPIO_F1_START + (_nr))
+#define S5PV310_GPF2(_nr) (S5PV310_GPIO_F2_START + (_nr))
+#define S5PV310_GPF3(_nr) (S5PV310_GPIO_F3_START + (_nr))
+#define S5PV310_GPJ0(_nr) (S5PV310_GPIO_J0_START + (_nr))
+#define S5PV310_GPJ1(_nr) (S5PV310_GPIO_J1_START + (_nr))
+#define S5PV310_GPK0(_nr) (S5PV310_GPIO_K0_START + (_nr))
+#define S5PV310_GPK1(_nr) (S5PV310_GPIO_K1_START + (_nr))
+#define S5PV310_GPK2(_nr) (S5PV310_GPIO_K2_START + (_nr))
+#define S5PV310_GPK3(_nr) (S5PV310_GPIO_K3_START + (_nr))
+#define S5PV310_GPL0(_nr) (S5PV310_GPIO_L0_START + (_nr))
+#define S5PV310_GPL1(_nr) (S5PV310_GPIO_L1_START + (_nr))
+#define S5PV310_GPL2(_nr) (S5PV310_GPIO_L2_START + (_nr))
+#define S5PV310_GPX0(_nr) (S5PV310_GPIO_X0_START + (_nr))
+#define S5PV310_GPX1(_nr) (S5PV310_GPIO_X1_START + (_nr))
+#define S5PV310_GPX2(_nr) (S5PV310_GPIO_X2_START + (_nr))
+#define S5PV310_GPX3(_nr) (S5PV310_GPIO_X3_START + (_nr))
+#define S5PV310_GPZ(_nr) (S5PV310_GPIO_Z_START + (_nr))
+
+/* the end of the S5PV310 specific gpios */
+#define S5PV310_GPIO_END (S5PV310_GPZ(S5PV310_GPIO_Z_NR) + 1)
+#define S3C_GPIO_END S5PV310_GPIO_END
+
+/* define the number of gpios we need to the one after the GPZ() range */
+#define ARCH_NR_GPIOS (S5PV310_GPZ(S5PV310_GPIO_Z_NR) + \
+ CONFIG_SAMSUNG_GPIO_EXTRA + 1)
+
+#include <asm-generic/gpio.h>
+
+#endif /* __ASM_ARCH_GPIO_H */
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/include/mach/hardware.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * S5PV310 - Hardware support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_HARDWARE_H
+#define __ASM_ARCH_HARDWARE_H __FILE__
+
+/* currently nothing here, placeholder */
+
+#endif /* __ASM_ARCH_HARDWARE_H */
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/include/mach/io.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Copyright 2008-2010 Ben Dooks <ben-linux@fluff.org>
+ *
+ * Based on arch/arm/mach-s5p6442/include/mach/io.h
+ *
+ * Default IO routines for S5PV310
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H __FILE__
+
+/* No current ISA/PCI bus support. */
+#define __io(a) __typesafe_io(a)
+#define __mem_pci(a) (a)
+
+#define IO_SPACE_LIMIT (0xFFFFFFFF)
+
+#endif /* __ASM_ARM_ARCH_IO_H */
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/include/mach/irqs.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * S5PV210 - IRQ definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_IRQS_H
+#define __ASM_ARCH_IRQS_H __FILE__
+
+#include <plat/irqs.h>
+
+/* Private Peripheral Interrupt */
+#define IRQ_PPI(x) S5P_IRQ(x+16)
+
+#define IRQ_LOCALTIMER IRQ_PPI(13)
+
+/* Shared Peripheral Interrupt */
+#define IRQ_SPI(x) S5P_IRQ(x+32)
+
+#define IRQ_EINT0 IRQ_SPI(40)
+#define IRQ_EINT1 IRQ_SPI(41)
+#define IRQ_EINT2 IRQ_SPI(42)
+#define IRQ_EINT3 IRQ_SPI(43)
+#define IRQ_USB_HSOTG IRQ_SPI(44)
+#define IRQ_USB_HOST IRQ_SPI(45)
+#define IRQ_MODEM_IF IRQ_SPI(46)
+#define IRQ_ROTATOR IRQ_SPI(47)
+#define IRQ_JPEG IRQ_SPI(48)
+#define IRQ_2D IRQ_SPI(49)
+#define IRQ_PCIE IRQ_SPI(50)
+#define IRQ_SYSTEM_TIMER IRQ_SPI(51)
+#define IRQ_MFC IRQ_SPI(52)
+#define IRQ_WTD IRQ_SPI(53)
+#define IRQ_AUDIO_SS IRQ_SPI(54)
+#define IRQ_AC97 IRQ_SPI(55)
+#define IRQ_SPDIF IRQ_SPI(56)
+#define IRQ_KEYPAD IRQ_SPI(57)
+#define IRQ_INTFEEDCTRL_SSS IRQ_SPI(58)
+#define IRQ_SLIMBUS IRQ_SPI(59)
+#define IRQ_PMU IRQ_SPI(60)
+#define IRQ_TSI IRQ_SPI(61)
+#define IRQ_SATA IRQ_SPI(62)
+#define IRQ_GPS IRQ_SPI(63)
+
+#define MAX_IRQ_IN_COMBINER 8
+#define COMBINER_GROUP(x) ((x) * MAX_IRQ_IN_COMBINER + IRQ_SPI(64))
+#define COMBINER_IRQ(x, y) (COMBINER_GROUP(x) + y)
+
+#define IRQ_TIMER0_VIC COMBINER_IRQ(22, 0)
+#define IRQ_TIMER1_VIC COMBINER_IRQ(22, 1)
+#define IRQ_TIMER2_VIC COMBINER_IRQ(22, 2)
+#define IRQ_TIMER3_VIC COMBINER_IRQ(22, 3)
+#define IRQ_TIMER4_VIC COMBINER_IRQ(22, 4)
+
+#define IRQ_UART0 COMBINER_IRQ(26, 0)
+#define IRQ_UART1 COMBINER_IRQ(26, 1)
+#define IRQ_UART2 COMBINER_IRQ(26, 2)
+#define IRQ_UART3 COMBINER_IRQ(26, 3)
+#define IRQ_UART4 COMBINER_IRQ(26, 4)
+
+#define IRQ_IIC COMBINER_IRQ(27, 0)
+
+/* Set the default NR_IRQS */
+#define NR_IRQS COMBINER_IRQ(MAX_COMBINER_NR, 0)
+
+#define MAX_COMBINER_NR 39
+
+#endif /* ASM_ARCH_IRQS_H */
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/include/mach/map.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * S5PV310 - Memory map definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_MAP_H
+#define __ASM_ARCH_MAP_H __FILE__
+
+#include <plat/map-base.h>
+
+/*
+ * S5PV310 UART offset is 0x10000 but the older S5P SoCs are 0x400.
+ * So need to define it, and here is to avoid redefinition warning.
+ */
+#define S3C_UART_OFFSET (0x10000)
+
+#include <plat/map-s5p.h>
+
+#define S5PV310_PA_CHIPID (0x10000000)
+#define S5P_PA_CHIPID S5PV310_PA_CHIPID
+
+#define S5PV310_PA_SYSCON (0x10020000)
+#define S5P_PA_SYSCON S5PV310_PA_SYSCON
+
+#define S5PV310_PA_WATCHDOG (0x10060000)
+
+#define S5PV310_PA_COMBINER (0x10448000)
+
+#define S5PV310_PA_COREPERI (0x10500000)
+#define S5PV310_PA_GIC_CPU (0x10500100)
+#define S5PV310_PA_TWD (0x10500600)
+#define S5PV310_PA_GIC_DIST (0x10501000)
+#define S5PV310_PA_L2CC (0x10502000)
+
+#define S5PV310_PA_GPIO (0x11000000)
+#define S5P_PA_GPIO S5PV310_PA_GPIO
+
+#define S5PV310_PA_UART (0x13800000)
+
+#define S5P_PA_UART(x) (S5PV310_PA_UART + ((x) * S3C_UART_OFFSET))
+#define S5P_PA_UART0 S5P_PA_UART(0)
+#define S5P_PA_UART1 S5P_PA_UART(1)
+#define S5P_PA_UART2 S5P_PA_UART(2)
+#define S5P_PA_UART3 S5P_PA_UART(3)
+#define S5P_PA_UART4 S5P_PA_UART(4)
+
+#define S5P_SZ_UART SZ_256
+
+#define S5PV310_PA_IIC0 (0x13860000)
+
+#define S5PV310_PA_TIMER (0x139D0000)
+#define S5P_PA_TIMER S5PV310_PA_TIMER
+
+#define S5PV310_PA_SDRAM (0x40000000)
+#define S5P_PA_SDRAM S5PV310_PA_SDRAM
+
+/* compatibiltiy defines. */
+#define S3C_PA_UART S5PV310_PA_UART
+#define S3C_PA_IIC S5PV310_PA_IIC0
+#define S3C_PA_WDT S5PV310_PA_WATCHDOG
+
+#endif /* __ASM_ARCH_MAP_H */
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/include/mach/memory.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * S5PV310 - Memory definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H __FILE__
+
+#define PHYS_OFFSET UL(0x40000000)
+
+/* Maximum of 256MiB in one bank */
+#define MAX_PHYSMEM_BITS 32
+#define SECTION_SIZE_BITS 28
+
+#endif /* __ASM_ARCH_MEMORY_H */
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/include/mach/pwm-clock.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * Based on arch/arm/mach-s3c64xx/include/mach/pwm-clock.h
+ *
+ * S5PV310 - pwm clock and timer support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_PWMCLK_H
+#define __ASM_ARCH_PWMCLK_H __FILE__
+
+/**
+ * pwm_cfg_src_is_tclk() - return whether the given mux config is a tclk
+ * @tcfg: The timer TCFG1 register bits shifted down to 0.
+ *
+ * Return true if the given configuration from TCFG1 is a TCLK instead
+ * any of the TDIV clocks.
+ */
+static inline int pwm_cfg_src_is_tclk(unsigned long tcfg)
+{
+ return tcfg == S3C64XX_TCFG1_MUX_TCLK;
+}
+
+/**
+ * tcfg_to_divisor() - convert tcfg1 setting to a divisor
+ * @tcfg1: The tcfg1 setting, shifted down.
+ *
+ * Get the divisor value for the given tcfg1 setting. We assume the
+ * caller has already checked to see if this is not a TCLK source.
+ */
+static inline unsigned long tcfg_to_divisor(unsigned long tcfg1)
+{
+ return 1 << tcfg1;
+}
+
+/**
+ * pwm_tdiv_has_div1() - does the tdiv setting have a /1
+ *
+ * Return true if we have a /1 in the tdiv setting.
+ */
+static inline unsigned int pwm_tdiv_has_div1(void)
+{
+ return 1;
+}
+
+/**
+ * pwm_tdiv_div_bits() - calculate TCFG1 divisor value.
+ * @div: The divisor to calculate the bit information for.
+ *
+ * Turn a divisor into the necessary bit field for TCFG1.
+ */
+static inline unsigned long pwm_tdiv_div_bits(unsigned int div)
+{
+ return ilog2(div);
+}
+
+#define S3C_TCFG1_MUX_TCLK S3C64XX_TCFG1_MUX_TCLK
+
+#endif /* __ASM_ARCH_PWMCLK_H */
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/include/mach/regs-clock.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * S5PV310 - Clock register definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_REGS_CLOCK_H
+#define __ASM_ARCH_REGS_CLOCK_H __FILE__
+
+#include <mach/map.h>
+
+#define S5P_CLKREG(x) (S3C_VA_SYS + (x))
+
+#define S5P_INFORM0 S5P_CLKREG(0x800)
+
+#define S5P_EPLL_CON0 S5P_CLKREG(0x1C110)
+#define S5P_EPLL_CON1 S5P_CLKREG(0x1C114)
+#define S5P_VPLL_CON0 S5P_CLKREG(0x1C120)
+#define S5P_VPLL_CON1 S5P_CLKREG(0x1C124)
+
+#define S5P_CLKSRC_TOP0 S5P_CLKREG(0x1C210)
+#define S5P_CLKSRC_TOP1 S5P_CLKREG(0x1C214)
+
+#define S5P_CLKSRC_PERIL0 S5P_CLKREG(0x1C250)
+
+#define S5P_CLKDIV_TOP S5P_CLKREG(0x1C510)
+
+#define S5P_CLKDIV_PERIL0 S5P_CLKREG(0x1C550)
+#define S5P_CLKDIV_PERIL1 S5P_CLKREG(0x1C554)
+#define S5P_CLKDIV_PERIL2 S5P_CLKREG(0x1C558)
+#define S5P_CLKDIV_PERIL3 S5P_CLKREG(0x1C55C)
+#define S5P_CLKDIV_PERIL4 S5P_CLKREG(0x1C560)
+#define S5P_CLKDIV_PERIL5 S5P_CLKREG(0x1C564)
+
+#define S5P_CLKGATE_IP_PERIL S5P_CLKREG(0x1C950)
+
+#define S5P_CLKSRC_CORE S5P_CLKREG(0x20200)
+
+#define S5P_CLKDIV_CORE0 S5P_CLKREG(0x20500)
+
+#define S5P_APLL_LOCK S5P_CLKREG(0x24000)
+#define S5P_MPLL_LOCK S5P_CLKREG(0x24004)
+#define S5P_APLL_CON0 S5P_CLKREG(0x24100)
+#define S5P_APLL_CON1 S5P_CLKREG(0x24104)
+#define S5P_MPLL_CON0 S5P_CLKREG(0x24108)
+#define S5P_MPLL_CON1 S5P_CLKREG(0x2410C)
+
+#define S5P_CLKSRC_CPU S5P_CLKREG(0x24200)
+#define S5P_CLKMUX_STATCPU S5P_CLKREG(0x24400)
+
+#define S5P_CLKDIV_CPU S5P_CLKREG(0x24500)
+#define S5P_CLKDIV_STATCPU S5P_CLKREG(0x24600)
+
+#define S5P_CLKGATE_SCLKCPU S5P_CLKREG(0x24800)
+
+#endif /* __ASM_ARCH_REGS_CLOCK_H */
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/include/mach/regs-irq.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * S5PV310 - IRQ register definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_REGS_IRQ_H
+#define __ASM_ARCH_REGS_IRQ_H __FILE__
+
+#include <asm/hardware/gic.h>
+#include <mach/map.h>
+
+#endif /* __ASM_ARCH_REGS_IRQ_H */
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/include/mach/smp.h
+ *
+ * Cloned from arch/arm/mach-realview/include/mach/smp.h
+*/
+
+#ifndef ASM_ARCH_SMP_H
+#define ASM_ARCH_SMP_H __FILE__
+
+#include <asm/hardware/gic.h>
+
+extern void __iomem *gic_cpu_base_addr;
+
+#define hard_smp_processor_id() \
+ ({ \
+ unsigned int cpunum; \
+ __asm__("mrc p15, 0, %0, c0, c0, 5" \
+ : "=r" (cpunum)); \
+ cpunum &= 0x03; \
+ })
+
+/*
+ * We use IRQ1 as the IPI
+ */
+static inline void smp_cross_call(const struct cpumask *mask)
+{
+ gic_raise_softirq(mask, 1);
+}
+
+#endif
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/include/mach/system.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * S5PV310 - system support header
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_SYSTEM_H
+#define __ASM_ARCH_SYSTEM_H __FILE__
+
+#include <plat/system-reset.h>
+
+static void arch_idle(void)
+{
+ /* nothing here yet */
+}
+#endif /* __ASM_ARCH_SYSTEM_H */
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/include/mach/timex.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Copyright (c) 2003-2010 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * Based on arch/arm/mach-s5p6442/include/mach/timex.h
+ *
+ * S5PV310 - time parameters
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_TIMEX_H
+#define __ASM_ARCH_TIMEX_H __FILE__
+
+/* CLOCK_TICK_RATE needs to be evaluatable by the cpp, so making it
+ * a variable is useless. It seems as long as we make our timers an
+ * exact multiple of HZ, any value that makes a 1->1 correspondence
+ * for the time conversion functions to/from jiffies is acceptable.
+*/
+
+#define CLOCK_TICK_RATE 12000000
+
+#endif /* __ASM_ARCH_TIMEX_H */
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/include/mach/uncompress.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * S5PV310 - uncompress code
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_UNCOMPRESS_H
+#define __ASM_ARCH_UNCOMPRESS_H __FILE__
+
+#include <mach/map.h>
+#include <plat/uncompress.h>
+
+static void arch_detect_cpu(void)
+{
+ /* we do not need to do any cpu detection here at the moment. */
+
+ /*
+ * For preventing FIFO overrun or infinite loop of UART console,
+ * fifo_max should be the minimum fifo size of all of the UART channels
+ */
+ fifo_mask = S5PV210_UFSTAT_TXMASK;
+ fifo_max = 15 << S5PV210_UFSTAT_TXSHIFT;
+}
+#endif /* __ASM_ARCH_UNCOMPRESS_H */
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/include/mach/vmalloc.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Copyright 2010 Ben Dooks <ben-linux@fluff.org>
+ *
+ * Based on arch/arm/mach-s5p6440/include/mach/vmalloc.h
+ *
+ * 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.
+ *
+ * S5PV310 vmalloc definition
+*/
+
+#ifndef __ASM_ARCH_VMALLOC_H
+#define __ASM_ARCH_VMALLOC_H __FILE__
+
+#define VMALLOC_END (0xF0000000)
+
+#endif /* __ASM_ARCH_VMALLOC_H */
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/init.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/serial_core.h>
+
+#include <plat/cpu.h>
+#include <plat/devs.h>
+#include <plat/regs-serial.h>
+
+static struct s3c24xx_uart_clksrc s5pv310_serial_clocks[] = {
+ [0] = {
+ .name = "uclk1",
+ .divisor = 1,
+ .min_baud = 0,
+ .max_baud = 0,
+ },
+};
+
+/* uart registration process */
+void __init s5pv310_common_init_uarts(struct s3c2410_uartcfg *cfg, int no)
+{
+ struct s3c2410_uartcfg *tcfg = cfg;
+ u32 ucnt;
+
+ for (ucnt = 0; ucnt < no; ucnt++, tcfg++) {
+ if (!tcfg->clocks) {
+ tcfg->has_fracval = 1;
+ tcfg->clocks = s5pv310_serial_clocks;
+ tcfg->clocks_size = ARRAY_SIZE(s5pv310_serial_clocks);
+ }
+ }
+
+ s3c24xx_init_uartdevs("s5pv210-uart", s5p_uart_resources, cfg, no);
+}
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/irq-combiner.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Based on arch/arm/common/gic.c
+ *
+ * IRQ COMBINER support
+ *
+ * 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/io.h>
+
+#include <asm/mach/irq.h>
+
+#define COMBINER_ENABLE_SET 0x0
+#define COMBINER_ENABLE_CLEAR 0x4
+#define COMBINER_INT_STATUS 0xC
+
+static DEFINE_SPINLOCK(irq_controller_lock);
+
+struct combiner_chip_data {
+ unsigned int irq_offset;
+ void __iomem *base;
+};
+
+static struct combiner_chip_data combiner_data[MAX_COMBINER_NR];
+
+static inline void __iomem *combiner_base(unsigned int irq)
+{
+ struct combiner_chip_data *combiner_data = get_irq_chip_data(irq);
+ return combiner_data->base;
+}
+
+static void combiner_mask_irq(unsigned int irq)
+{
+ u32 mask = 1 << (irq % 32);
+
+ __raw_writel(mask, combiner_base(irq) + COMBINER_ENABLE_CLEAR);
+}
+
+static void combiner_unmask_irq(unsigned int irq)
+{
+ u32 mask = 1 << (irq % 32);
+
+ __raw_writel(mask, combiner_base(irq) + COMBINER_ENABLE_SET);
+}
+
+static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
+{
+ struct combiner_chip_data *chip_data = get_irq_data(irq);
+ struct irq_chip *chip = get_irq_chip(irq);
+ unsigned int cascade_irq, combiner_irq;
+ unsigned long status;
+
+ /* primary controller ack'ing */
+ chip->ack(irq);
+
+ spin_lock(&irq_controller_lock);
+ status = __raw_readl(chip_data->base + COMBINER_INT_STATUS);
+ spin_unlock(&irq_controller_lock);
+
+ if (status == 0)
+ goto out;
+
+ for (combiner_irq = 0; combiner_irq < 32; combiner_irq++) {
+ if (status & 0x1)
+ break;
+ status >>= 1;
+ }
+
+ cascade_irq = combiner_irq + (chip_data->irq_offset & ~31);
+ if (unlikely(cascade_irq >= NR_IRQS))
+ do_bad_IRQ(cascade_irq, desc);
+ else
+ generic_handle_irq(cascade_irq);
+
+ out:
+ /* primary controller unmasking */
+ chip->unmask(irq);
+}
+
+static struct irq_chip combiner_chip = {
+ .name = "COMBINER",
+ .mask = combiner_mask_irq,
+ .unmask = combiner_unmask_irq,
+};
+
+void __init combiner_cascade_irq(unsigned int combiner_nr, unsigned int irq)
+{
+ if (combiner_nr >= MAX_COMBINER_NR)
+ BUG();
+ if (set_irq_data(irq, &combiner_data[combiner_nr]) != 0)
+ BUG();
+ set_irq_chained_handler(irq, combiner_handle_cascade_irq);
+}
+
+void __init combiner_init(unsigned int combiner_nr, void __iomem *base,
+ unsigned int irq_start)
+{
+ unsigned int i;
+
+ if (combiner_nr >= MAX_COMBINER_NR)
+ BUG();
+
+ combiner_data[combiner_nr].base = base;
+ combiner_data[combiner_nr].irq_offset = irq_start;
+
+ /* Disable all interrupts */
+
+ __raw_writel(0xffffffff, base + COMBINER_ENABLE_CLEAR);
+
+ /* Setup the Linux IRQ subsystem */
+
+ for (i = irq_start; i < combiner_data[combiner_nr].irq_offset
+ + MAX_IRQ_IN_COMBINER; i++) {
+ set_irq_chip(i, &combiner_chip);
+ set_irq_chip_data(i, &combiner_data[combiner_nr]);
+ set_irq_handler(i, handle_level_irq);
+ set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
+ }
+}
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/localtimer.c
+ *
+ * Cloned from linux/arch/arm/mach-realview/localtimer.c
+ *
+ * Copyright (C) 2002 ARM Ltd.
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/clockchips.h>
+
+#include <asm/irq.h>
+#include <asm/localtimer.h>
+
+/*
+ * Setup the local clock events for a CPU.
+ */
+void __cpuinit local_timer_setup(struct clock_event_device *evt)
+{
+ evt->irq = IRQ_LOCALTIMER;
+ twd_timer_setup(evt);
+}
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/mach-smdkv310.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/serial_core.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+#include <asm/hardware/cache-l2x0.h>
+
+#include <plat/regs-serial.h>
+#include <plat/s5pv310.h>
+#include <plat/cpu.h>
+
+#include <mach/map.h>
+
+/* Following are default values for UCON, ULCON and UFCON UART registers */
+#define SMDKV310_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \
+ S3C2410_UCON_RXILEVEL | \
+ S3C2410_UCON_TXIRQMODE | \
+ S3C2410_UCON_RXIRQMODE | \
+ S3C2410_UCON_RXFIFO_TOI | \
+ S3C2443_UCON_RXERR_IRQEN)
+
+#define SMDKV310_ULCON_DEFAULT S3C2410_LCON_CS8
+
+#define SMDKV310_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \
+ S5PV210_UFCON_TXTRIG4 | \
+ S5PV210_UFCON_RXTRIG4)
+
+static struct s3c2410_uartcfg smdkv310_uartcfgs[] __initdata = {
+ [0] = {
+ .hwport = 0,
+ .flags = 0,
+ .ucon = SMDKV310_UCON_DEFAULT,
+ .ulcon = SMDKV310_ULCON_DEFAULT,
+ .ufcon = SMDKV310_UFCON_DEFAULT,
+ },
+ [1] = {
+ .hwport = 1,
+ .flags = 0,
+ .ucon = SMDKV310_UCON_DEFAULT,
+ .ulcon = SMDKV310_ULCON_DEFAULT,
+ .ufcon = SMDKV310_UFCON_DEFAULT,
+ },
+ [2] = {
+ .hwport = 2,
+ .flags = 0,
+ .ucon = SMDKV310_UCON_DEFAULT,
+ .ulcon = SMDKV310_ULCON_DEFAULT,
+ .ufcon = SMDKV310_UFCON_DEFAULT,
+ },
+ [3] = {
+ .hwport = 3,
+ .flags = 0,
+ .ucon = SMDKV310_UCON_DEFAULT,
+ .ulcon = SMDKV310_ULCON_DEFAULT,
+ .ufcon = SMDKV310_UFCON_DEFAULT,
+ },
+};
+
+static void __init smdkv310_map_io(void)
+{
+ s5p_init_io(NULL, 0, S5P_VA_CHIPID);
+ s3c24xx_init_clocks(24000000);
+ s3c24xx_init_uarts(smdkv310_uartcfgs, ARRAY_SIZE(smdkv310_uartcfgs));
+}
+
+static void __init smdkv310_machine_init(void)
+{
+#ifdef CONFIG_CACHE_L2X0
+ l2x0_init(S5P_VA_L2CC, 1 << 28, 0xffffffff);
+#endif
+}
+
+MACHINE_START(SMDKV310, "SMDKV310")
+ /* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */
+ /* Maintainer: Changhwan Youn <chaos.youn@samsung.com> */
+ .phys_io = S3C_PA_UART & 0xfff00000,
+ .io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
+ .boot_params = S5P_PA_SDRAM + 0x100,
+ .init_irq = s5pv310_init_irq,
+ .map_io = smdkv310_map_io,
+ .init_machine = smdkv310_machine_init,
+ .timer = &s5pv310_timer,
+MACHINE_END
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/mach-universal_c210.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/serial_core.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+#include <asm/hardware/cache-l2x0.h>
+
+#include <plat/regs-serial.h>
+#include <plat/s5pv310.h>
+#include <plat/cpu.h>
+
+#include <mach/map.h>
+
+/* Following are default values for UCON, ULCON and UFCON UART registers */
+#define UNIVERSAL_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \
+ S3C2410_UCON_RXILEVEL | \
+ S3C2410_UCON_TXIRQMODE | \
+ S3C2410_UCON_RXIRQMODE | \
+ S3C2410_UCON_RXFIFO_TOI | \
+ S3C2443_UCON_RXERR_IRQEN)
+
+#define UNIVERSAL_ULCON_DEFAULT S3C2410_LCON_CS8
+
+#define UNIVERSAL_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \
+ S5PV210_UFCON_TXTRIG256 | \
+ S5PV210_UFCON_RXTRIG256)
+
+static struct s3c2410_uartcfg universal_uartcfgs[] __initdata = {
+ [0] = {
+ .hwport = 0,
+ .ucon = UNIVERSAL_UCON_DEFAULT,
+ .ulcon = UNIVERSAL_ULCON_DEFAULT,
+ .ufcon = UNIVERSAL_UFCON_DEFAULT,
+ },
+ [1] = {
+ .hwport = 1,
+ .ucon = UNIVERSAL_UCON_DEFAULT,
+ .ulcon = UNIVERSAL_ULCON_DEFAULT,
+ .ufcon = UNIVERSAL_UFCON_DEFAULT,
+ },
+ [2] = {
+ .hwport = 2,
+ .ucon = UNIVERSAL_UCON_DEFAULT,
+ .ulcon = UNIVERSAL_ULCON_DEFAULT,
+ .ufcon = UNIVERSAL_UFCON_DEFAULT,
+ },
+ [3] = {
+ .hwport = 3,
+ .ucon = UNIVERSAL_UCON_DEFAULT,
+ .ulcon = UNIVERSAL_ULCON_DEFAULT,
+ .ufcon = UNIVERSAL_UFCON_DEFAULT,
+ },
+};
+
+static void __init universal_map_io(void)
+{
+ s5p_init_io(NULL, 0, S5P_VA_CHIPID);
+ s3c24xx_init_clocks(24000000);
+ s3c24xx_init_uarts(universal_uartcfgs, ARRAY_SIZE(universal_uartcfgs));
+}
+
+static void __init universal_machine_init(void)
+{
+#ifdef CONFIG_CACHE_L2X0
+ l2x0_init(S5P_VA_L2CC, 1 << 28, 0xffffffff);
+#endif
+}
+
+MACHINE_START(UNIVERSAL_C210, "UNIVERSAL_C210")
+ /* Maintainer: Kyungmin Park <kyungmin.park@samsung.com> */
+ .phys_io = S3C_PA_UART & 0xfff00000,
+ .io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
+ .boot_params = S5P_PA_SDRAM + 0x100,
+ .init_irq = s5pv310_init_irq,
+ .map_io = universal_map_io,
+ .init_machine = universal_machine_init,
+ .timer = &s5pv310_timer,
+MACHINE_END
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/platsmp.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Cloned from linux/arch/arm/mach-vexpress/platsmp.c
+ *
+ * Copyright (C) 2002 ARM Ltd.
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/smp.h>
+#include <linux/io.h>
+
+#include <asm/cacheflush.h>
+#include <asm/localtimer.h>
+#include <asm/smp_scu.h>
+#include <asm/unified.h>
+
+#include <mach/hardware.h>
+#include <mach/regs-clock.h>
+
+extern void s5pv310_secondary_startup(void);
+
+/*
+ * control for which core is the next to come out of the secondary
+ * boot "holding pen"
+ */
+
+volatile int __cpuinitdata pen_release = -1;
+
+static void __iomem *scu_base_addr(void)
+{
+ return (void __iomem *)(S5P_VA_SCU);
+}
+
+static DEFINE_SPINLOCK(boot_lock);
+
+void __cpuinit platform_secondary_init(unsigned int cpu)
+{
+ trace_hardirqs_off();
+
+ /*
+ * if any interrupts are already enabled for the primary
+ * core (e.g. timer irq), then they will not have been enabled
+ * for us: do so
+ */
+ gic_cpu_init(0, gic_cpu_base_addr);
+
+ /*
+ * let the primary processor know we're out of the
+ * pen, then head off into the C entry point
+ */
+ pen_release = -1;
+ smp_wmb();
+
+ /*
+ * Synchronise with the boot thread.
+ */
+ spin_lock(&boot_lock);
+ spin_unlock(&boot_lock);
+}
+
+int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ unsigned long timeout;
+
+ /*
+ * Set synchronisation state between this boot processor
+ * and the secondary one
+ */
+ spin_lock(&boot_lock);
+
+ /*
+ * The secondary processor is waiting to be released from
+ * the holding pen - release it, then wait for it to flag
+ * that it has been released by resetting pen_release.
+ *
+ * Note that "pen_release" is the hardware CPU ID, whereas
+ * "cpu" is Linux's internal ID.
+ */
+ pen_release = cpu;
+ __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
+ outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
+
+ /*
+ * Send the secondary CPU a soft interrupt, thereby causing
+ * the boot monitor to read the system wide flags register,
+ * and branch to the address found there.
+ */
+ smp_cross_call(cpumask_of(cpu));
+
+ timeout = jiffies + (1 * HZ);
+ while (time_before(jiffies, timeout)) {
+ smp_rmb();
+ if (pen_release == -1)
+ break;
+
+ udelay(10);
+ }
+
+ /*
+ * now the secondary core is starting up let it run its
+ * calibrations, then wait for it to finish
+ */
+ spin_unlock(&boot_lock);
+
+ return pen_release != -1 ? -ENOSYS : 0;
+}
+
+/*
+ * Initialise the CPU possible map early - this describes the CPUs
+ * which may be present or become present in the system.
+ */
+
+void __init smp_init_cpus(void)
+{
+ void __iomem *scu_base = scu_base_addr();
+ unsigned int i, ncores;
+
+ ncores = scu_base ? scu_get_core_count(scu_base) : 1;
+
+ /* sanity check */
+ if (ncores == 0) {
+ printk(KERN_ERR
+ "S5PV310: strange CM count of 0? Default to 1\n");
+
+ ncores = 1;
+ }
+
+ if (ncores > NR_CPUS) {
+ printk(KERN_WARNING
+ "S5PV310: no. of cores (%d) greater than configured "
+ "maximum of %d - clipping\n",
+ ncores, NR_CPUS);
+ ncores = NR_CPUS;
+ }
+
+ for (i = 0; i < ncores; i++)
+ set_cpu_possible(i, true);
+}
+
+void __init smp_prepare_cpus(unsigned int max_cpus)
+{
+ unsigned int ncores = num_possible_cpus();
+ unsigned int cpu = smp_processor_id();
+ int i;
+
+ smp_store_cpu_info(cpu);
+
+ /* are we trying to boot more cores than exist? */
+ if (max_cpus > ncores)
+ max_cpus = ncores;
+
+ /*
+ * Initialise the present map, which describes the set of CPUs
+ * actually populated at the present time.
+ */
+ for (i = 0; i < max_cpus; i++)
+ set_cpu_present(i, true);
+
+ /*
+ * Initialise the SCU if there are more than one CPU and let
+ * them know where to start.
+ */
+ if (max_cpus > 1) {
+ /*
+ * Enable the local timer or broadcast device for the
+ * boot CPU, but only if we have more than one CPU.
+ */
+ percpu_timer_setup();
+
+ scu_enable(scu_base_addr());
+
+ /*
+ * Write the address of secondary startup into the
+ * system-wide flags register. The boot monitor waits
+ * until it receives a soft interrupt, and then the
+ * secondary CPU branches to this address.
+ */
+ __raw_writel(BSYM(virt_to_phys(s5pv310_secondary_startup)), S5P_INFORM0);
+ }
+}
--- /dev/null
+/*
+ * linux/arch/arm/mach-s5pv310/setup-i2c0.c
+ *
+ * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * I2C0 GPIO configuration.
+ *
+ * Based on plat-s3c64xx/setup-i2c0.c
+ *
+ * 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.
+*/
+
+struct platform_device; /* don't need the contents */
+
+#include <linux/gpio.h>
+#include <plat/iic.h>
+#include <plat/gpio-cfg.h>
+
+void s3c_i2c0_cfg_gpio(struct platform_device *dev)
+{
+ s3c_gpio_cfgpin(S5PV310_GPD1(0), S3C_GPIO_SFN(2));
+ s3c_gpio_setpull(S5PV310_GPD1(0), S3C_GPIO_PULL_UP);
+ s3c_gpio_cfgpin(S5PV310_GPD1(1), S3C_GPIO_SFN(2));
+ s3c_gpio_setpull(S5PV310_GPD1(1), S3C_GPIO_PULL_UP);
+}
--- /dev/null
+/*
+ * linux/arch/arm/mach-s5pv310/setup-i2c1.c
+ *
+ * Copyright (C) 2010 Samsung Electronics Co., Ltd.
+ *
+ * I2C1 GPIO configuration.
+ *
+ * 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.
+*/
+
+struct platform_device; /* don't need the contents */
+
+#include <linux/gpio.h>
+#include <plat/iic.h>
+#include <plat/gpio-cfg.h>
+
+void s3c_i2c1_cfg_gpio(struct platform_device *dev)
+{
+ s3c_gpio_cfgpin(S5PV310_GPD1(2), S3C_GPIO_SFN(2));
+ s3c_gpio_setpull(S5PV310_GPD1(2), S3C_GPIO_PULL_UP);
+ s3c_gpio_cfgpin(S5PV310_GPD1(3), S3C_GPIO_SFN(2));
+ s3c_gpio_setpull(S5PV310_GPD1(3), S3C_GPIO_PULL_UP);
+}
--- /dev/null
+/*
+ * linux/arch/arm/mach-s5pv310/setup-i2c2.c
+ *
+ * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
+ *
+ * I2C2 GPIO configuration.
+ *
+ * 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.
+*/
+
+struct platform_device; /* don't need the contents */
+
+#include <linux/gpio.h>
+#include <plat/iic.h>
+#include <plat/gpio-cfg.h>
+
+void s3c_i2c2_cfg_gpio(struct platform_device *dev)
+{
+ s3c_gpio_cfgpin(S5PV310_GPA0(6), S3C_GPIO_SFN(3));
+ s3c_gpio_setpull(S5PV310_GPA0(6), S3C_GPIO_PULL_UP);
+ s3c_gpio_cfgpin(S5PV310_GPA0(7), S3C_GPIO_SFN(3));
+ s3c_gpio_setpull(S5PV310_GPA0(7), S3C_GPIO_PULL_UP);
+}
--- /dev/null
+/* linux/arch/arm/mach-s5pv310/time.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * S5PV310 (and compatible) HRT support
+ * PWM 2/4 is used for this feature
+ *
+ * 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/sched.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/platform_device.h>
+
+#include <asm/smp_twd.h>
+
+#include <mach/map.h>
+#include <plat/regs-timer.h>
+#include <asm/mach/time.h>
+
+static unsigned long clock_count_per_tick;
+
+static struct clk *tin2;
+static struct clk *tin4;
+static struct clk *tdiv2;
+static struct clk *tdiv4;
+static struct clk *timerclk;
+
+static void s5pv310_pwm_stop(unsigned int pwm_id)
+{
+ unsigned long tcon;
+
+ tcon = __raw_readl(S3C2410_TCON);
+
+ switch (pwm_id) {
+ case 2:
+ tcon &= ~S3C2410_TCON_T2START;
+ break;
+ case 4:
+ tcon &= ~S3C2410_TCON_T4START;
+ break;
+ default:
+ break;
+ }
+ __raw_writel(tcon, S3C2410_TCON);
+}
+
+static void s5pv310_pwm_init(unsigned int pwm_id, unsigned long tcnt)
+{
+ unsigned long tcon;
+
+ tcon = __raw_readl(S3C2410_TCON);
+
+ /* timers reload after counting zero, so reduce the count by 1 */
+ tcnt--;
+
+ /* ensure timer is stopped... */
+ switch (pwm_id) {
+ case 2:
+ tcon &= ~(0xf<<12);
+ tcon |= S3C2410_TCON_T2MANUALUPD;
+
+ __raw_writel(tcnt, S3C2410_TCNTB(2));
+ __raw_writel(tcnt, S3C2410_TCMPB(2));
+ __raw_writel(tcon, S3C2410_TCON);
+
+ break;
+ case 4:
+ tcon &= ~(7<<20);
+ tcon |= S3C2410_TCON_T4MANUALUPD;
+
+ __raw_writel(tcnt, S3C2410_TCNTB(4));
+ __raw_writel(tcnt, S3C2410_TCMPB(4));
+ __raw_writel(tcon, S3C2410_TCON);
+
+ break;
+ default:
+ break;
+ }
+}
+
+static inline void s5pv310_pwm_start(unsigned int pwm_id, bool periodic)
+{
+ unsigned long tcon;
+
+ tcon = __raw_readl(S3C2410_TCON);
+
+ switch (pwm_id) {
+ case 2:
+ tcon |= S3C2410_TCON_T2START;
+ tcon &= ~S3C2410_TCON_T2MANUALUPD;
+
+ if (periodic)
+ tcon |= S3C2410_TCON_T2RELOAD;
+ else
+ tcon &= ~S3C2410_TCON_T2RELOAD;
+ break;
+ case 4:
+ tcon |= S3C2410_TCON_T4START;
+ tcon &= ~S3C2410_TCON_T4MANUALUPD;
+
+ if (periodic)
+ tcon |= S3C2410_TCON_T4RELOAD;
+ else
+ tcon &= ~S3C2410_TCON_T4RELOAD;
+ break;
+ default:
+ break;
+ }
+ __raw_writel(tcon, S3C2410_TCON);
+}
+
+static int s5pv310_pwm_set_next_event(unsigned long cycles,
+ struct clock_event_device *evt)
+{
+ s5pv310_pwm_init(2, cycles);
+ s5pv310_pwm_start(2, 0);
+ return 0;
+}
+
+static void s5pv310_pwm_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+ s5pv310_pwm_stop(2);
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ s5pv310_pwm_init(2, clock_count_per_tick);
+ s5pv310_pwm_start(2, 1);
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ break;
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_RESUME:
+ break;
+ }
+}
+
+static struct clock_event_device pwm_event_device = {
+ .name = "pwm_timer2",
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .rating = 200,
+ .shift = 32,
+ .set_next_event = s5pv310_pwm_set_next_event,
+ .set_mode = s5pv310_pwm_set_mode,
+};
+
+irqreturn_t s5pv310_clock_event_isr(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = &pwm_event_device;
+
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction s5pv310_clock_event_irq = {
+ .name = "pwm_timer2_irq",
+ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .handler = s5pv310_clock_event_isr,
+};
+
+static void __init s5pv310_clockevent_init(void)
+{
+ unsigned long pclk;
+ unsigned long clock_rate;
+ struct clk *tscaler;
+
+ pclk = clk_get_rate(timerclk);
+
+ /* configure clock tick */
+
+ tscaler = clk_get_parent(tdiv2);
+
+ clk_set_rate(tscaler, pclk / 2);
+ clk_set_rate(tdiv2, pclk / 2);
+ clk_set_parent(tin2, tdiv2);
+
+ clock_rate = clk_get_rate(tin2);
+
+ clock_count_per_tick = clock_rate / HZ;
+
+ pwm_event_device.mult =
+ div_sc(clock_rate, NSEC_PER_SEC, pwm_event_device.shift);
+ pwm_event_device.max_delta_ns =
+ clockevent_delta2ns(-1, &pwm_event_device);
+ pwm_event_device.min_delta_ns =
+ clockevent_delta2ns(1, &pwm_event_device);
+
+ pwm_event_device.cpumask = cpumask_of(0);
+ clockevents_register_device(&pwm_event_device);
+
+ setup_irq(IRQ_TIMER2, &s5pv310_clock_event_irq);
+}
+
+static cycle_t s5pv310_pwm4_read(struct clocksource *cs)
+{
+ return (cycle_t) ~__raw_readl(S3C_TIMERREG(0x40));
+}
+
+struct clocksource pwm_clocksource = {
+ .name = "pwm_timer4",
+ .rating = 250,
+ .read = s5pv310_pwm4_read,
+ .mask = CLOCKSOURCE_MASK(32),
+ .shift = 20,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS ,
+};
+
+static void __init s5pv310_clocksource_init(void)
+{
+ unsigned long pclk;
+ unsigned long clock_rate;
+
+ pclk = clk_get_rate(timerclk);
+
+ clk_set_rate(tdiv4, pclk / 2);
+ clk_set_parent(tin4, tdiv4);
+
+ clock_rate = clk_get_rate(tin4);
+
+ s5pv310_pwm_init(4, ~0);
+ s5pv310_pwm_start(4, 1);
+
+ pwm_clocksource.mult =
+ clocksource_khz2mult(clock_rate/1000, pwm_clocksource.shift);
+
+ if (clocksource_register(&pwm_clocksource))
+ panic("%s: can't register clocksource\n", pwm_clocksource.name);
+}
+
+static void __init s5pv310_timer_resources(void)
+{
+ struct platform_device tmpdev;
+
+ tmpdev.dev.bus = &platform_bus_type;
+
+ timerclk = clk_get(NULL, "timers");
+ if (IS_ERR(timerclk))
+ panic("failed to get timers clock for system timer");
+
+ clk_enable(timerclk);
+
+ tmpdev.id = 2;
+ tin2 = clk_get(&tmpdev.dev, "pwm-tin");
+ if (IS_ERR(tin2))
+ panic("failed to get pwm-tin2 clock for system timer");
+
+ tdiv2 = clk_get(&tmpdev.dev, "pwm-tdiv");
+ if (IS_ERR(tdiv2))
+ panic("failed to get pwm-tdiv2 clock for system timer");
+ clk_enable(tin2);
+
+ tmpdev.id = 4;
+ tin4 = clk_get(&tmpdev.dev, "pwm-tin");
+ if (IS_ERR(tin4))
+ panic("failed to get pwm-tin4 clock for system timer");
+
+ tdiv4 = clk_get(&tmpdev.dev, "pwm-tdiv");
+ if (IS_ERR(tdiv4))
+ panic("failed to get pwm-tdiv4 clock for system timer");
+
+ clk_enable(tin4);
+}
+
+static void __init s5pv310_timer_init(void)
+{
+#ifdef CONFIG_LOCAL_TIMERS
+ twd_base = S5P_VA_TWD;
+#endif
+
+ s5pv310_timer_resources();
+ s5pv310_clockevent_init();
+ s5pv310_clocksource_init();
+}
+
+struct sys_timer s5pv310_timer = {
+ .init = s5pv310_timer_init,
+};
*/
#ifdef CONFIG_SA1111
#define NR_IRQS (IRQ_BOARD_END + 55)
-#elif defined(CONFIG_SHARPSL_LOCOMO)
+#elif defined(CONFIG_SHARP_LOCOMO)
#define NR_IRQS (IRQ_BOARD_START + 4)
#else
#define NR_IRQS (IRQ_BOARD_START)
select CPU_V6
select HAVE_CLK
select COMMON_CLKDEV
+ select SH_CLK_CPG
select GENERIC_CLOCKEVENTS
config ARCH_SH7377
select CPU_V7
select HAVE_CLK
select COMMON_CLKDEV
+ select SH_CLK_CPG
select GENERIC_CLOCKEVENTS
config ARCH_SH7372
select CPU_V7
select HAVE_CLK
select COMMON_CLKDEV
+ select SH_CLK_CPG
select GENERIC_CLOCKEVENTS
comment "SH-Mobile Board Type"
bool "AP4EVB board"
depends on ARCH_SH7372
select ARCH_REQUIRE_GPIOLIB
+ select SH_LCD_MIPI_DSI
+
+choice
+ prompt "AP4EVB LCD panel selection"
+ default AP4EVB_QHD
+ depends on MACH_AP4EVB
+
+config AP4EVB_QHD
+ bool "MIPI-DSI QHD (960x540)"
+
+config AP4EVB_WVGA
+ bool "Parallel WVGA (800x480)"
+
+endchoice
comment "SH-Mobile System Configuration"
help
This enables build of the CMT timer driver.
+config SH_TIMER_TMU
+ bool "TMU timer driver"
+ default y
+ help
+ This enables build of the TMU timer driver.
+
endmenu
+config SH_CLK_CPG
+ bool
+
endif
#
# Common objects
-obj-y := timer.o console.o
+obj-y := timer.o console.o clock.o
# CPU objects
obj-$(CONFIG_ARCH_SH7367) += setup-sh7367.o clock-sh7367.o intc-sh7367.o
-obj-$(CONFIG_ARCH_SH7377) += setup-sh7377.o clock-sh7367.o intc-sh7377.o
-obj-$(CONFIG_ARCH_SH7372) += setup-sh7372.o clock-sh7367.o intc-sh7372.o
+obj-$(CONFIG_ARCH_SH7377) += setup-sh7377.o clock-sh7377.o intc-sh7377.o
+obj-$(CONFIG_ARCH_SH7372) += setup-sh7372.o clock-sh7372.o intc-sh7372.o
# Pinmux setup
pfc-$(CONFIG_ARCH_SH7367) := pfc-sh7367.o
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
+#include <linux/mfd/sh_mobile_sdhi.h>
+#include <linux/mmc/host.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/physmap.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sh_mmcif.h>
+#include <linux/i2c.h>
+#include <linux/i2c/tsc2007.h>
#include <linux/io.h>
#include <linux/smsc911x.h>
+#include <linux/sh_intc.h>
+#include <linux/sh_clk.h>
#include <linux/gpio.h>
#include <linux/input.h>
#include <linux/input/sh_keysc.h>
+#include <linux/usb/r8a66597.h>
+
+#include <sound/sh_fsi.h>
+
+#include <video/sh_mobile_hdmi.h>
+#include <video/sh_mobile_lcdc.h>
+#include <video/sh_mipi_dsi.h>
+
#include <mach/common.h>
+#include <mach/irqs.h>
#include <mach/sh7372.h>
+
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
+#include <asm/mach/time.h>
/*
* Address Interface BusWidth note
*/
/*
- * KEYSC
+ * LCD / IRQ / KEYSC / IrDA
+ *
+ * IRQ = IRQ26 (TS), IRQ27 (VIO), IRQ28 (QHD-TouchScreen)
+ * LCD = 2nd LCDC (WVGA)
*
- * SW43 KEYSC
- * -------------------------
- * ON enable
- * OFF disable
+ * | SW43 |
+ * SW3 | ON | OFF |
+ * -------------+-----------------------+---------------+
+ * ON | KEY / IrDA | LCD |
+ * OFF | KEY / IrDA / IRQ | IRQ |
+ *
+ *
+ * QHD / WVGA display
+ *
+ * You can choice display type on menuconfig.
+ * Then, check above dip-switch.
+ */
+
+/*
+ * USB
+ *
+ * J7 : 1-2 MAX3355E VBUS
+ * 2-3 DC 5.0V
+ *
+ * S39: bit2: off
+ */
+
+/*
+ * FSI/FSMI
+ *
+ * SW41 : ON : SH-Mobile AP4 Audio Mode
+ * : OFF : Bluetooth Audio Mode
+ */
+
+/*
+ * MMC0/SDHI1 (CN7)
+ *
+ * J22 : select card voltage
+ * 1-2 pin : 1.8v
+ * 2-3 pin : 3.3v
+ *
+ * SW1 | SW33
+ * | bit1 | bit2 | bit3 | bit4
+ * ------------+------+------+------+-------
+ * MMC0 OFF | OFF | ON | ON | X
+ * SDHI1 OFF | ON | X | OFF | ON
+ *
+ * voltage lebel
+ * CN7 : 1.8v
+ * CN12: 3.3v
*/
/* MTD */
.end = 0x16000000 - 1,
.flags = IORESOURCE_MEM,
}, {
- .start = 6,
+ .start = evt2irq(0x02c0) /* IRQ6A */,
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL,
},
};
},
};
+/* SH_MMCIF */
+static struct resource sh_mmcif_resources[] = {
+ [0] = {
+ .name = "SH_MMCIF",
+ .start = 0xE6BD0000,
+ .end = 0xE6BD00FF,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ /* MMC ERR */
+ .start = evt2irq(0x1ac0),
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ /* MMC NOR */
+ .start = evt2irq(0x1ae0),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct sh_mmcif_plat_data sh_mmcif_plat = {
+ .sup_pclk = 0,
+ .ocr = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34,
+ .caps = MMC_CAP_4_BIT_DATA |
+ MMC_CAP_8_BIT_DATA |
+ MMC_CAP_NEEDS_POLL,
+};
+
+static struct platform_device sh_mmcif_device = {
+ .name = "sh_mmcif",
+ .id = 0,
+ .dev = {
+ .dma_mask = NULL,
+ .coherent_dma_mask = 0xffffffff,
+ .platform_data = &sh_mmcif_plat,
+ },
+ .num_resources = ARRAY_SIZE(sh_mmcif_resources),
+ .resource = sh_mmcif_resources,
+};
+
+/* SDHI0 */
+static struct sh_mobile_sdhi_info sdhi0_info = {
+ .dma_slave_tx = SHDMA_SLAVE_SDHI0_TX,
+ .dma_slave_rx = SHDMA_SLAVE_SDHI0_RX,
+};
+
+static struct resource sdhi0_resources[] = {
+ [0] = {
+ .name = "SDHI0",
+ .start = 0xe6850000,
+ .end = 0xe68501ff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = evt2irq(0x0e00) /* SDHI0 */,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device sdhi0_device = {
+ .name = "sh_mobile_sdhi",
+ .num_resources = ARRAY_SIZE(sdhi0_resources),
+ .resource = sdhi0_resources,
+ .id = 0,
+ .dev = {
+ .platform_data = &sdhi0_info,
+ },
+};
+
+/* SDHI1 */
+static struct sh_mobile_sdhi_info sdhi1_info = {
+ .dma_slave_tx = SHDMA_SLAVE_SDHI1_TX,
+ .dma_slave_rx = SHDMA_SLAVE_SDHI1_RX,
+ .tmio_ocr_mask = MMC_VDD_165_195,
+};
+
+static struct resource sdhi1_resources[] = {
+ [0] = {
+ .name = "SDHI1",
+ .start = 0xe6860000,
+ .end = 0xe68601ff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = evt2irq(0x0e80),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device sdhi1_device = {
+ .name = "sh_mobile_sdhi",
+ .num_resources = ARRAY_SIZE(sdhi1_resources),
+ .resource = sdhi1_resources,
+ .id = 1,
+ .dev = {
+ .platform_data = &sdhi1_info,
+ },
+};
+
+/* USB1 */
+static void usb1_host_port_power(int port, int power)
+{
+ if (!power) /* only power-on supported for now */
+ return;
+
+ /* set VBOUT/PWEN and EXTLP1 in DVSTCTR */
+ __raw_writew(__raw_readw(0xE68B0008) | 0x600, 0xE68B0008);
+}
+
+static struct r8a66597_platdata usb1_host_data = {
+ .on_chip = 1,
+ .port_power = usb1_host_port_power,
+};
+
+static struct resource usb1_host_resources[] = {
+ [0] = {
+ .name = "USBHS",
+ .start = 0xE68B0000,
+ .end = 0xE68B00E6 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = evt2irq(0x1ce0) /* USB1_USB1I0 */,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device usb1_host_device = {
+ .name = "r8a66597_hcd",
+ .id = 1,
+ .dev = {
+ .dma_mask = NULL, /* not use dma */
+ .coherent_dma_mask = 0xffffffff,
+ .platform_data = &usb1_host_data,
+ },
+ .num_resources = ARRAY_SIZE(usb1_host_resources),
+ .resource = usb1_host_resources,
+};
+
+static struct sh_mobile_lcdc_info lcdc_info = {
+ .ch[0] = {
+ .chan = LCDC_CHAN_MAINLCD,
+ .bpp = 16,
+ }
+};
+
+static struct resource lcdc_resources[] = {
+ [0] = {
+ .name = "LCDC",
+ .start = 0xfe940000, /* P4-only space */
+ .end = 0xfe943fff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = intcs_evt2irq(0x580),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device lcdc_device = {
+ .name = "sh_mobile_lcdc_fb",
+ .num_resources = ARRAY_SIZE(lcdc_resources),
+ .resource = lcdc_resources,
+ .dev = {
+ .platform_data = &lcdc_info,
+ .coherent_dma_mask = ~0,
+ },
+};
+
+/*
+ * QHD display
+ */
+#ifdef CONFIG_AP4EVB_QHD
+
/* KEYSC (Needs SW43 set to ON) */
static struct sh_keysc_info keysc_info = {
.mode = SH_KEYSC_MODE_1,
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = 79,
+ .start = evt2irq(0x0be0), /* KEYSC_KEY */
.flags = IORESOURCE_IRQ,
},
};
},
};
-/* SDHI0 */
-static struct resource sdhi0_resources[] = {
+/* MIPI-DSI */
+static struct resource mipidsi0_resources[] = {
[0] = {
- .name = "SDHI0",
- .start = 0xe6850000,
- .end = 0xe68501ff,
+ .start = 0xffc60000,
+ .end = 0xffc68fff,
.flags = IORESOURCE_MEM,
},
+};
+
+static struct sh_mipi_dsi_info mipidsi0_info = {
+ .data_format = MIPI_RGB888,
+ .lcd_chan = &lcdc_info.ch[0],
+};
+
+static struct platform_device mipidsi0_device = {
+ .name = "sh-mipi-dsi",
+ .num_resources = ARRAY_SIZE(mipidsi0_resources),
+ .resource = mipidsi0_resources,
+ .id = 0,
+ .dev = {
+ .platform_data = &mipidsi0_info,
+ },
+};
+
+/* This function will disappear when we switch to (runtime) PM */
+static int __init ap4evb_init_display_clk(void)
+{
+ struct clk *lcdc_clk;
+ struct clk *dsitx_clk;
+ int ret;
+
+ lcdc_clk = clk_get(&lcdc_device.dev, "sh_mobile_lcdc_fb.0");
+ if (IS_ERR(lcdc_clk))
+ return PTR_ERR(lcdc_clk);
+
+ dsitx_clk = clk_get(&mipidsi0_device.dev, "sh-mipi-dsi.0");
+ if (IS_ERR(dsitx_clk)) {
+ ret = PTR_ERR(dsitx_clk);
+ goto eclkdsitxget;
+ }
+
+ ret = clk_enable(lcdc_clk);
+ if (ret < 0)
+ goto eclklcdcon;
+
+ ret = clk_enable(dsitx_clk);
+ if (ret < 0)
+ goto eclkdsitxon;
+
+ return 0;
+
+eclkdsitxon:
+ clk_disable(lcdc_clk);
+eclklcdcon:
+ clk_put(dsitx_clk);
+eclkdsitxget:
+ clk_put(lcdc_clk);
+
+ return ret;
+}
+device_initcall(ap4evb_init_display_clk);
+
+static struct platform_device *qhd_devices[] __initdata = {
+ &mipidsi0_device,
+ &keysc_device,
+};
+#endif /* CONFIG_AP4EVB_QHD */
+
+/* FSI */
+#define IRQ_FSI evt2irq(0x1840)
+#define FSIACKCR 0xE6150018
+static void fsiackcr_init(struct clk *clk)
+{
+ u32 status = __raw_readl(clk->enable_reg);
+
+ /* use external clock */
+ status &= ~0x000000ff;
+ status |= 0x00000080;
+ __raw_writel(status, clk->enable_reg);
+}
+
+static struct clk_ops fsiackcr_clk_ops = {
+ .init = fsiackcr_init,
+};
+
+static struct clk fsiackcr_clk = {
+ .ops = &fsiackcr_clk_ops,
+ .enable_reg = (void __iomem *)FSIACKCR,
+ .rate = 0, /* unknown */
+};
+
+static struct sh_fsi_platform_info fsi_info = {
+ .porta_flags = SH_FSI_BRS_INV |
+ SH_FSI_OUT_SLAVE_MODE |
+ SH_FSI_IN_SLAVE_MODE |
+ SH_FSI_OFMT(PCM) |
+ SH_FSI_IFMT(PCM),
+};
+
+static struct resource fsi_resources[] = {
+ [0] = {
+ .name = "FSI",
+ .start = 0xFE3C0000,
+ .end = 0xFE3C0400 - 1,
+ .flags = IORESOURCE_MEM,
+ },
[1] = {
- .start = 96,
+ .start = IRQ_FSI,
.flags = IORESOURCE_IRQ,
},
};
-static struct platform_device sdhi0_device = {
- .name = "sh_mobile_sdhi",
- .num_resources = ARRAY_SIZE(sdhi0_resources),
- .resource = sdhi0_resources,
- .id = 0,
+static struct platform_device fsi_device = {
+ .name = "sh_fsi2",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(fsi_resources),
+ .resource = fsi_resources,
+ .dev = {
+ .platform_data = &fsi_info,
+ },
+};
+
+static struct sh_mobile_lcdc_info sh_mobile_lcdc1_info = {
+ .clock_source = LCDC_CLK_EXTERNAL,
+ .ch[0] = {
+ .chan = LCDC_CHAN_MAINLCD,
+ .bpp = 16,
+ .interface_type = RGB24,
+ .clock_divider = 1,
+ .flags = LCDC_FLAGS_DWPOL,
+ .lcd_cfg = {
+ .name = "HDMI",
+ /* So far only 720p is supported */
+ .xres = 1280,
+ .yres = 720,
+ /*
+ * If left and right margins are not multiples of 8,
+ * LDHAJR will be adjusted accordingly by the LCDC
+ * driver. Until we start using EDID, these values
+ * might have to be adjusted for different monitors.
+ */
+ .left_margin = 200,
+ .right_margin = 88,
+ .hsync_len = 48,
+ .upper_margin = 20,
+ .lower_margin = 5,
+ .vsync_len = 5,
+ .pixclock = 13468,
+ .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT,
+ },
+ }
+};
+
+static struct resource lcdc1_resources[] = {
+ [0] = {
+ .name = "LCDC1",
+ .start = 0xfe944000,
+ .end = 0xfe947fff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = intcs_evt2irq(0x17a0),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device lcdc1_device = {
+ .name = "sh_mobile_lcdc_fb",
+ .num_resources = ARRAY_SIZE(lcdc1_resources),
+ .resource = lcdc1_resources,
+ .id = 1,
+ .dev = {
+ .platform_data = &sh_mobile_lcdc1_info,
+ .coherent_dma_mask = ~0,
+ },
+};
+
+static struct sh_mobile_hdmi_info hdmi_info = {
+ .lcd_chan = &sh_mobile_lcdc1_info.ch[0],
+ .lcd_dev = &lcdc1_device.dev,
+};
+
+static struct resource hdmi_resources[] = {
+ [0] = {
+ .name = "HDMI",
+ .start = 0xe6be0000,
+ .end = 0xe6be00ff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ /* There's also an HDMI interrupt on INTCS @ 0x18e0 */
+ .start = evt2irq(0x17e0),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device hdmi_device = {
+ .name = "sh-mobile-hdmi",
+ .num_resources = ARRAY_SIZE(hdmi_resources),
+ .resource = hdmi_resources,
+ .id = -1,
+ .dev = {
+ .platform_data = &hdmi_info,
+ },
};
static struct platform_device *ap4evb_devices[] __initdata = {
&nor_flash_device,
&smc911x_device,
- &keysc_device,
&sdhi0_device,
+ &sdhi1_device,
+ &usb1_host_device,
+ &fsi_device,
+ &sh_mmcif_device,
+ &lcdc1_device,
+ &lcdc_device,
+ &hdmi_device,
+};
+
+static int __init hdmi_init_pm_clock(void)
+{
+ struct clk *hdmi_ick = clk_get(&hdmi_device.dev, "ick");
+ int ret;
+ long rate;
+
+ if (IS_ERR(hdmi_ick)) {
+ ret = PTR_ERR(hdmi_ick);
+ pr_err("Cannot get HDMI ICK: %d\n", ret);
+ goto out;
+ }
+
+ ret = clk_set_parent(&pllc2_clk, &dv_clki_div2_clk);
+ if (ret < 0) {
+ pr_err("Cannot set PLLC2 parent: %d, %d users\n", ret, pllc2_clk.usecount);
+ goto out;
+ }
+
+ pr_debug("PLLC2 initial frequency %lu\n", clk_get_rate(&pllc2_clk));
+
+ rate = clk_round_rate(&pllc2_clk, 594000000);
+ if (rate < 0) {
+ pr_err("Cannot get suitable rate: %ld\n", rate);
+ ret = rate;
+ goto out;
+ }
+
+ ret = clk_set_rate(&pllc2_clk, rate);
+ if (ret < 0) {
+ pr_err("Cannot set rate %ld: %d\n", rate, ret);
+ goto out;
+ }
+
+ pr_debug("PLLC2 set frequency %lu\n", rate);
+
+ ret = clk_set_parent(hdmi_ick, &pllc2_clk);
+ if (ret < 0) {
+ pr_err("Cannot set HDMI parent: %d\n", ret);
+ goto out;
+ }
+
+out:
+ if (!IS_ERR(hdmi_ick))
+ clk_put(hdmi_ick);
+ return ret;
+}
+
+device_initcall(hdmi_init_pm_clock);
+
+/*
+ * FIXME !!
+ *
+ * gpio_no_direction
+ * gpio_pull_up
+ * are quick_hack.
+ *
+ * current gpio frame work doesn't have
+ * the method to control only pull up/down/free.
+ * this function should be replaced by correct gpio function
+ */
+static void __init gpio_no_direction(u32 addr)
+{
+ __raw_writeb(0x00, addr);
+}
+
+static void __init gpio_pull_up(u32 addr)
+{
+ u8 data = __raw_readb(addr);
+
+ data &= 0x0F;
+ data |= 0xC0;
+ __raw_writeb(data, addr);
+}
+
+/* TouchScreen */
+#define IRQ28 evt2irq(0x3380) /* IRQ28A */
+#define IRQ7 evt2irq(0x02e0) /* IRQ7A */
+static int ts_get_pendown_state(void)
+{
+ int val1, val2;
+
+ gpio_free(GPIO_FN_IRQ28_123);
+ gpio_free(GPIO_FN_IRQ7_40);
+
+ gpio_request(GPIO_PORT123, NULL);
+ gpio_request(GPIO_PORT40, NULL);
+
+ gpio_direction_input(GPIO_PORT123);
+ gpio_direction_input(GPIO_PORT40);
+
+ val1 = gpio_get_value(GPIO_PORT123);
+ val2 = gpio_get_value(GPIO_PORT40);
+
+ gpio_request(GPIO_FN_IRQ28_123, NULL); /* for QHD */
+ gpio_request(GPIO_FN_IRQ7_40, NULL); /* for WVGA */
+
+ return val1 ^ val2;
+}
+
+#define PORT40CR 0xE6051028
+#define PORT123CR 0xE605007B
+static int ts_init(void)
+{
+ gpio_request(GPIO_FN_IRQ28_123, NULL); /* for QHD */
+ gpio_request(GPIO_FN_IRQ7_40, NULL); /* for WVGA */
+
+ gpio_pull_up(PORT40CR);
+ gpio_pull_up(PORT123CR);
+
+ return 0;
+}
+
+static struct tsc2007_platform_data tsc2007_info = {
+ .model = 2007,
+ .x_plate_ohms = 180,
+ .get_pendown_state = ts_get_pendown_state,
+ .init_platform_hw = ts_init,
+};
+
+static struct i2c_board_info tsc_device = {
+ I2C_BOARD_INFO("tsc2007", 0x48),
+ .type = "tsc2007",
+ .platform_data = &tsc2007_info,
+ /*.irq is selected on ap4evb_init */
+};
+
+/* I2C */
+static struct i2c_board_info i2c0_devices[] = {
+ {
+ I2C_BOARD_INFO("ak4643", 0x13),
+ },
+};
+
+static struct i2c_board_info i2c1_devices[] = {
+ {
+ I2C_BOARD_INFO("r2025sd", 0x32),
+ },
};
static struct map_desc ap4evb_io_desc[] __initdata = {
{
iotable_init(ap4evb_io_desc, ARRAY_SIZE(ap4evb_io_desc));
- /* setup early devices, clocks and console here as well */
+ /* setup early devices and console here as well */
sh7372_add_early_devices();
- sh7367_clock_init(); /* use g3 clocks for now */
shmobile_setup_console();
}
+#define GPIO_PORT9CR 0xE6051009
+#define GPIO_PORT10CR 0xE605100A
static void __init ap4evb_init(void)
{
+ u32 srcr4;
+ struct clk *clk;
+
sh7372_pinmux_init();
/* enable SCIFA0 */
gpio_export(GPIO_PORT34, 0);
gpio_export(GPIO_PORT35, 0);
+ /* SDHI0 */
+ gpio_request(GPIO_FN_SDHICD0, NULL);
+ gpio_request(GPIO_FN_SDHIWP0, NULL);
+ gpio_request(GPIO_FN_SDHICMD0, NULL);
+ gpio_request(GPIO_FN_SDHICLK0, NULL);
+ gpio_request(GPIO_FN_SDHID0_3, NULL);
+ gpio_request(GPIO_FN_SDHID0_2, NULL);
+ gpio_request(GPIO_FN_SDHID0_1, NULL);
+ gpio_request(GPIO_FN_SDHID0_0, NULL);
+
+ /* SDHI1 */
+ gpio_request(GPIO_FN_SDHICMD1, NULL);
+ gpio_request(GPIO_FN_SDHICLK1, NULL);
+ gpio_request(GPIO_FN_SDHID1_3, NULL);
+ gpio_request(GPIO_FN_SDHID1_2, NULL);
+ gpio_request(GPIO_FN_SDHID1_1, NULL);
+ gpio_request(GPIO_FN_SDHID1_0, NULL);
+
+ /* MMCIF */
+ gpio_request(GPIO_FN_MMCD0_0, NULL);
+ gpio_request(GPIO_FN_MMCD0_1, NULL);
+ gpio_request(GPIO_FN_MMCD0_2, NULL);
+ gpio_request(GPIO_FN_MMCD0_3, NULL);
+ gpio_request(GPIO_FN_MMCD0_4, NULL);
+ gpio_request(GPIO_FN_MMCD0_5, NULL);
+ gpio_request(GPIO_FN_MMCD0_6, NULL);
+ gpio_request(GPIO_FN_MMCD0_7, NULL);
+ gpio_request(GPIO_FN_MMCCMD0, NULL);
+ gpio_request(GPIO_FN_MMCCLK0, NULL);
+
+ /* USB enable */
+ gpio_request(GPIO_FN_VBUS0_1, NULL);
+ gpio_request(GPIO_FN_IDIN_1_18, NULL);
+ gpio_request(GPIO_FN_PWEN_1_115, NULL);
+ gpio_request(GPIO_FN_OVCN_1_114, NULL);
+ gpio_request(GPIO_FN_EXTLP_1, NULL);
+ gpio_request(GPIO_FN_OVCN2_1, NULL);
+
+ /* setup USB phy */
+ __raw_writew(0x8a0a, 0xE6058130); /* USBCR2 */
+
+ /* enable FSI2 */
+ gpio_request(GPIO_FN_FSIAIBT, NULL);
+ gpio_request(GPIO_FN_FSIAILR, NULL);
+ gpio_request(GPIO_FN_FSIAISLD, NULL);
+ gpio_request(GPIO_FN_FSIAOSLD, NULL);
+ gpio_request(GPIO_PORT161, NULL);
+ gpio_direction_output(GPIO_PORT161, 0); /* slave */
+
+ gpio_request(GPIO_PORT9, NULL);
+ gpio_request(GPIO_PORT10, NULL);
+ gpio_no_direction(GPIO_PORT9CR); /* FSIAOBT needs no direction */
+ gpio_no_direction(GPIO_PORT10CR); /* FSIAOLR needs no direction */
+
+ /* set SPU2 clock to 119.6 MHz */
+ clk = clk_get(NULL, "spu_clk");
+ if (!IS_ERR(clk)) {
+ clk_set_rate(clk, clk_round_rate(clk, 119600000));
+ clk_put(clk);
+ }
+
+ /* change parent of FSI A */
+ clk = clk_get(NULL, "fsia_clk");
+ if (!IS_ERR(clk)) {
+ clk_register(&fsiackcr_clk);
+ clk_set_parent(clk, &fsiackcr_clk);
+ clk_put(clk);
+ }
+
+ /*
+ * set irq priority, to avoid sound chopping
+ * when NFS rootfs is used
+ * FSI(3) > SMSC911X(2)
+ */
+ intc_set_priority(IRQ_FSI, 3);
+
+ i2c_register_board_info(0, i2c0_devices,
+ ARRAY_SIZE(i2c0_devices));
+
+ i2c_register_board_info(1, i2c1_devices,
+ ARRAY_SIZE(i2c1_devices));
+
+#ifdef CONFIG_AP4EVB_QHD
+ /*
+ * QHD
+ */
+
/* enable KEYSC */
gpio_request(GPIO_FN_KEYOUT0, NULL);
gpio_request(GPIO_FN_KEYOUT1, NULL);
gpio_request(GPIO_FN_KEYIN3_133, NULL);
gpio_request(GPIO_FN_KEYIN4, NULL);
- /* SDHI0 */
- gpio_request(GPIO_FN_SDHICD0, NULL);
- gpio_request(GPIO_FN_SDHIWP0, NULL);
- gpio_request(GPIO_FN_SDHICMD0, NULL);
- gpio_request(GPIO_FN_SDHICLK0, NULL);
- gpio_request(GPIO_FN_SDHID0_3, NULL);
- gpio_request(GPIO_FN_SDHID0_2, NULL);
- gpio_request(GPIO_FN_SDHID0_1, NULL);
- gpio_request(GPIO_FN_SDHID0_0, NULL);
+ /* enable TouchScreen */
+ set_irq_type(IRQ28, IRQ_TYPE_LEVEL_LOW);
+
+ tsc_device.irq = IRQ28;
+ i2c_register_board_info(1, &tsc_device, 1);
+
+ /* LCDC0 */
+ lcdc_info.clock_source = LCDC_CLK_PERIPHERAL;
+ lcdc_info.ch[0].interface_type = RGB24;
+ lcdc_info.ch[0].clock_divider = 1;
+ lcdc_info.ch[0].flags = LCDC_FLAGS_DWPOL;
+ lcdc_info.ch[0].lcd_cfg.name = "R63302(QHD)";
+ lcdc_info.ch[0].lcd_cfg.xres = 544;
+ lcdc_info.ch[0].lcd_cfg.yres = 961;
+ lcdc_info.ch[0].lcd_cfg.left_margin = 72;
+ lcdc_info.ch[0].lcd_cfg.right_margin = 600;
+ lcdc_info.ch[0].lcd_cfg.hsync_len = 16;
+ lcdc_info.ch[0].lcd_cfg.upper_margin = 8;
+ lcdc_info.ch[0].lcd_cfg.lower_margin = 8;
+ lcdc_info.ch[0].lcd_cfg.vsync_len = 2;
+ lcdc_info.ch[0].lcd_cfg.sync = FB_SYNC_VERT_HIGH_ACT |
+ FB_SYNC_HOR_HIGH_ACT;
+ lcdc_info.ch[0].lcd_size_cfg.width = 44;
+ lcdc_info.ch[0].lcd_size_cfg.height = 79;
+
+ platform_add_devices(qhd_devices, ARRAY_SIZE(qhd_devices));
+
+#else
+ /*
+ * WVGA
+ */
+ gpio_request(GPIO_FN_LCDD17, NULL);
+ gpio_request(GPIO_FN_LCDD16, NULL);
+ gpio_request(GPIO_FN_LCDD15, NULL);
+ gpio_request(GPIO_FN_LCDD14, NULL);
+ gpio_request(GPIO_FN_LCDD13, NULL);
+ gpio_request(GPIO_FN_LCDD12, NULL);
+ gpio_request(GPIO_FN_LCDD11, NULL);
+ gpio_request(GPIO_FN_LCDD10, NULL);
+ gpio_request(GPIO_FN_LCDD9, NULL);
+ gpio_request(GPIO_FN_LCDD8, NULL);
+ gpio_request(GPIO_FN_LCDD7, NULL);
+ gpio_request(GPIO_FN_LCDD6, NULL);
+ gpio_request(GPIO_FN_LCDD5, NULL);
+ gpio_request(GPIO_FN_LCDD4, NULL);
+ gpio_request(GPIO_FN_LCDD3, NULL);
+ gpio_request(GPIO_FN_LCDD2, NULL);
+ gpio_request(GPIO_FN_LCDD1, NULL);
+ gpio_request(GPIO_FN_LCDD0, NULL);
+ gpio_request(GPIO_FN_LCDDISP, NULL);
+ gpio_request(GPIO_FN_LCDDCK, NULL);
+
+ gpio_request(GPIO_PORT189, NULL); /* backlight */
+ gpio_direction_output(GPIO_PORT189, 1);
+
+ gpio_request(GPIO_PORT151, NULL); /* LCDDON */
+ gpio_direction_output(GPIO_PORT151, 1);
+
+ lcdc_info.clock_source = LCDC_CLK_BUS;
+ lcdc_info.ch[0].interface_type = RGB18;
+ lcdc_info.ch[0].clock_divider = 2;
+ lcdc_info.ch[0].flags = 0;
+ lcdc_info.ch[0].lcd_cfg.name = "WVGA Panel";
+ lcdc_info.ch[0].lcd_cfg.xres = 800;
+ lcdc_info.ch[0].lcd_cfg.yres = 480;
+ lcdc_info.ch[0].lcd_cfg.left_margin = 220;
+ lcdc_info.ch[0].lcd_cfg.right_margin = 110;
+ lcdc_info.ch[0].lcd_cfg.hsync_len = 70;
+ lcdc_info.ch[0].lcd_cfg.upper_margin = 20;
+ lcdc_info.ch[0].lcd_cfg.lower_margin = 5;
+ lcdc_info.ch[0].lcd_cfg.vsync_len = 5;
+ lcdc_info.ch[0].lcd_cfg.sync = 0;
+ lcdc_info.ch[0].lcd_size_cfg.width = 152;
+ lcdc_info.ch[0].lcd_size_cfg.height = 91;
+
+ /* enable TouchScreen */
+ set_irq_type(IRQ7, IRQ_TYPE_LEVEL_LOW);
+
+ tsc_device.irq = IRQ7;
+ i2c_register_board_info(0, &tsc_device, 1);
+#endif /* CONFIG_AP4EVB_QHD */
sh7372_add_standard_devices();
+ /* HDMI */
+ gpio_request(GPIO_FN_HDMI_HPD, NULL);
+ gpio_request(GPIO_FN_HDMI_CEC, NULL);
+
+ /* Reset HDMI, must be held at least one EXTALR (32768Hz) period */
+#define SRCR4 0xe61580bc
+ srcr4 = __raw_readl(SRCR4);
+ __raw_writel(srcr4 | (1 << 13), SRCR4);
+ udelay(50);
+ __raw_writel(srcr4 & ~(1 << 13), SRCR4);
+
platform_add_devices(ap4evb_devices, ARRAY_SIZE(ap4evb_devices));
}
+static void __init ap4evb_timer_init(void)
+{
+ sh7372_clock_init();
+ shmobile_timer.init();
+
+ /* External clock source */
+ clk_set_rate(&dv_clki_clk, 27000000);
+}
+
+static struct sys_timer ap4evb_timer = {
+ .init = ap4evb_timer_init,
+};
+
MACHINE_START(AP4EVB, "ap4evb")
.phys_io = 0xe6000000,
.io_pg_offst = ((0xe6000000) >> 18) & 0xfffc,
.map_io = ap4evb_map_io,
.init_irq = sh7372_init_irq,
.init_machine = ap4evb_init,
- .timer = &shmobile_timer,
+ .timer = &ap4evb_timer,
MACHINE_END
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
+#include <asm/mach/time.h>
+
+/*
+ * IrDA
+ *
+ * S67: 5bit : ON power
+ * : 6bit : ON remote control
+ * OFF IrDA
+ */
static struct mtd_partition nor_flash_partitions[] = {
{
};
/* USBHS */
-void usb_host_port_power(int port, int power)
+static void usb_host_port_power(int port, int power)
{
if (!power) /* only power-on supported for now */
return;
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = 65,
+ .start = evt2irq(0xa20), /* USBHS_USHI0 */
.flags = IORESOURCE_IRQ,
},
};
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = 79,
+ .start = evt2irq(0xbe0), /* KEYSC_KEY */
.flags = IORESOURCE_IRQ,
},
};
},
};
+static struct resource irda_resources[] = {
+ [0] = {
+ .start = 0xE6D00000,
+ .end = 0xE6D01FD4 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = evt2irq(0x480), /* IRDA */
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device irda_device = {
+ .name = "sh_irda",
+ .id = -1,
+ .resource = irda_resources,
+ .num_resources = ARRAY_SIZE(irda_resources),
+};
+
static struct platform_device *g3evm_devices[] __initdata = {
&nor_flash_device,
&usb_host_device,
&keysc_device,
&nand_flash_device,
+ &irda_device,
};
static struct map_desc g3evm_io_desc[] __initdata = {
{
iotable_init(g3evm_io_desc, ARRAY_SIZE(g3evm_io_desc));
- /* setup early devices, clocks and console here as well */
+ /* setup early devices and console here as well */
sh7367_add_early_devices();
- sh7367_clock_init();
shmobile_setup_console();
}
gpio_request(GPIO_FN_EXTLP, NULL);
gpio_request(GPIO_FN_IDIN, NULL);
- /* enable clock in SYMSTPCR2 */
- __raw_writel(__raw_readl(0xe6158048) & ~(1 << 22), 0xe6158048);
-
/* setup USB phy */
__raw_writew(0x0300, 0xe605810a); /* USBCR1 */
__raw_writew(0x00e0, 0xe60581c0); /* CPFCH */
/* FOE, FCDE, FSC on dedicated pins */
__raw_writel(__raw_readl(0xe6158048) & ~(1 << 15), 0xe6158048);
+ /* IrDA */
+ gpio_request(GPIO_FN_IRDA_OUT, NULL);
+ gpio_request(GPIO_FN_IRDA_IN, NULL);
+ gpio_request(GPIO_FN_IRDA_FIRSEL, NULL);
+ set_irq_type(evt2irq(0x480), IRQ_TYPE_LEVEL_LOW);
+
sh7367_add_standard_devices();
platform_add_devices(g3evm_devices, ARRAY_SIZE(g3evm_devices));
}
+static void __init g3evm_timer_init(void)
+{
+ sh7367_clock_init();
+ shmobile_timer.init();
+}
+
+static struct sys_timer g3evm_timer = {
+ .init = g3evm_timer_init,
+};
+
MACHINE_START(G3EVM, "g3evm")
.phys_io = 0xe6000000,
.io_pg_offst = ((0xe6000000) >> 18) & 0xfffc,
.map_io = g3evm_map_io,
.init_irq = sh7367_init_irq,
.init_machine = g3evm_init,
- .timer = &shmobile_timer,
+ .timer = &g3evm_timer,
MACHINE_END
#include <linux/io.h>
#include <linux/input.h>
#include <linux/input/sh_keysc.h>
+#include <linux/mfd/sh_mobile_sdhi.h>
#include <linux/gpio.h>
#include <mach/sh7377.h>
#include <mach/common.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
+#include <asm/mach/time.h>
+
+/*
+ * SDHI
+ *
+ * SDHI0 : card detection is possible
+ * SDHI1 : card detection is impossible
+ *
+ * [G4-MAIN-BOARD]
+ * JP74 : short # DBG_2V8A for SDHI0
+ * JP75 : NC # DBG_3V3A for SDHI0
+ * JP76 : NC # DBG_3V3A_SD for SDHI0
+ * JP77 : NC # 3V3A_SDIO for SDHI1
+ * JP78 : short # DBG_2V8A for SDHI1
+ * JP79 : NC # DBG_3V3A for SDHI1
+ * JP80 : NC # DBG_3V3A_SD for SDHI1
+ *
+ * [G4-CORE-BOARD]
+ * S32 : all off # to dissever from G3-CORE_DBG board
+ * S33 : all off # to dissever from G3-CORE_DBG board
+ *
+ * [G3-CORE_DBG-BOARD]
+ * S1 : all off # to dissever from G3-CORE_DBG board
+ * S3 : all off # to dissever from G3-CORE_DBG board
+ * S4 : all off # to dissever from G3-CORE_DBG board
+ */
static struct mtd_partition nor_flash_partitions[] = {
{
};
/* USBHS */
-void usb_host_port_power(int port, int power)
+static void usb_host_port_power(int port, int power)
{
if (!power) /* only power-on supported for now */
return;
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = 65,
- .end = 65,
+ .start = evt2irq(0x0a20), /* USBHS_USHI0 */
.flags = IORESOURCE_IRQ,
},
};
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = 79,
+ .start = evt2irq(0x0be0), /* KEYSC_KEY */
.flags = IORESOURCE_IRQ,
},
};
},
};
+/* SDHI */
+static struct resource sdhi0_resources[] = {
+ [0] = {
+ .name = "SDHI0",
+ .start = 0xe6d50000,
+ .end = 0xe6d501ff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = evt2irq(0x0e00), /* SDHI0 */
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device sdhi0_device = {
+ .name = "sh_mobile_sdhi",
+ .num_resources = ARRAY_SIZE(sdhi0_resources),
+ .resource = sdhi0_resources,
+ .id = 0,
+};
+
+static struct resource sdhi1_resources[] = {
+ [0] = {
+ .name = "SDHI1",
+ .start = 0xe6d60000,
+ .end = 0xe6d601ff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = evt2irq(0x0e80), /* SDHI1 */
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device sdhi1_device = {
+ .name = "sh_mobile_sdhi",
+ .num_resources = ARRAY_SIZE(sdhi1_resources),
+ .resource = sdhi1_resources,
+ .id = 1,
+};
+
static struct platform_device *g4evm_devices[] __initdata = {
&nor_flash_device,
&usb_host_device,
&keysc_device,
+ &sdhi0_device,
+ &sdhi1_device,
};
static struct map_desc g4evm_io_desc[] __initdata = {
{
iotable_init(g4evm_io_desc, ARRAY_SIZE(g4evm_io_desc));
- /* setup early devices, clocks and console here as well */
+ /* setup early devices and console here as well */
sh7377_add_early_devices();
- sh7367_clock_init(); /* use g3 clocks for now */
shmobile_setup_console();
}
+#define GPIO_SDHID0_D0 0xe60520fc
+#define GPIO_SDHID0_D1 0xe60520fd
+#define GPIO_SDHID0_D2 0xe60520fe
+#define GPIO_SDHID0_D3 0xe60520ff
+#define GPIO_SDHICMD0 0xe6052100
+
+#define GPIO_SDHID1_D0 0xe6052103
+#define GPIO_SDHID1_D1 0xe6052104
+#define GPIO_SDHID1_D2 0xe6052105
+#define GPIO_SDHID1_D3 0xe6052106
+#define GPIO_SDHICMD1 0xe6052107
+
+/*
+ * FIXME !!
+ *
+ * gpio_pull_up is quick_hack.
+ *
+ * current gpio frame work doesn't have
+ * the method to control only pull up/down/free.
+ * this function should be replaced by correct gpio function
+ */
+static void __init gpio_pull_up(u32 addr)
+{
+ u8 data = __raw_readb(addr);
+
+ data &= 0x0F;
+ data |= 0xC0;
+ __raw_writeb(data, addr);
+}
+
static void __init g4evm_init(void)
{
sh7377_pinmux_init();
gpio_request(GPIO_FN_EXTLP, NULL);
gpio_request(GPIO_FN_IDIN, NULL);
- /* enable clock in SMSTPCR3 */
- __raw_writel(__raw_readl(0xe615013c) & ~(1 << 22), 0xe615013c);
-
/* setup USB phy */
__raw_writew(0x0200, 0xe605810a); /* USBCR1 */
__raw_writew(0x00e0, 0xe60581c0); /* CPFCH */
gpio_request(GPIO_FN_PORT71_KEYIN5_PU, NULL);
gpio_request(GPIO_FN_PORT72_KEYIN6_PU, NULL);
+ /* SDHI0 */
+ gpio_request(GPIO_FN_SDHICLK0, NULL);
+ gpio_request(GPIO_FN_SDHICD0, NULL);
+ gpio_request(GPIO_FN_SDHID0_0, NULL);
+ gpio_request(GPIO_FN_SDHID0_1, NULL);
+ gpio_request(GPIO_FN_SDHID0_2, NULL);
+ gpio_request(GPIO_FN_SDHID0_3, NULL);
+ gpio_request(GPIO_FN_SDHICMD0, NULL);
+ gpio_request(GPIO_FN_SDHIWP0, NULL);
+ gpio_pull_up(GPIO_SDHID0_D0);
+ gpio_pull_up(GPIO_SDHID0_D1);
+ gpio_pull_up(GPIO_SDHID0_D2);
+ gpio_pull_up(GPIO_SDHID0_D3);
+ gpio_pull_up(GPIO_SDHICMD0);
+
+ /* SDHI1 */
+ gpio_request(GPIO_FN_SDHICLK1, NULL);
+ gpio_request(GPIO_FN_SDHID1_0, NULL);
+ gpio_request(GPIO_FN_SDHID1_1, NULL);
+ gpio_request(GPIO_FN_SDHID1_2, NULL);
+ gpio_request(GPIO_FN_SDHID1_3, NULL);
+ gpio_request(GPIO_FN_SDHICMD1, NULL);
+ gpio_pull_up(GPIO_SDHID1_D0);
+ gpio_pull_up(GPIO_SDHID1_D1);
+ gpio_pull_up(GPIO_SDHID1_D2);
+ gpio_pull_up(GPIO_SDHID1_D3);
+ gpio_pull_up(GPIO_SDHICMD1);
+
sh7377_add_standard_devices();
platform_add_devices(g4evm_devices, ARRAY_SIZE(g4evm_devices));
}
+static void __init g4evm_timer_init(void)
+{
+ sh7377_clock_init();
+ shmobile_timer.init();
+}
+
+static struct sys_timer g4evm_timer = {
+ .init = g4evm_timer_init,
+};
+
MACHINE_START(G4EVM, "g4evm")
.phys_io = 0xe6000000,
.io_pg_offst = ((0xe6000000) >> 18) & 0xfffc,
.map_io = g4evm_map_io,
.init_irq = sh7377_init_irq,
.init_machine = g4evm_init,
- .timer = &shmobile_timer,
+ .timer = &g4evm_timer,
MACHINE_END
/*
- * Preliminary clock framework support for sh7367
+ * SH7367 clock framework support
*
* Copyright (C) 2010 Magnus Damm
*
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/init.h>
-#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/sh_clk.h>
+#include <mach/common.h>
+#include <asm/clkdev.h>
+
+/* SH7367 registers */
+#define RTFRQCR 0xe6150000
+#define SYFRQCR 0xe6150004
+#define CMFRQCR 0xe61500E0
+#define VCLKCR1 0xe6150008
+#define VCLKCR2 0xe615000C
+#define VCLKCR3 0xe615001C
+#define SCLKACR 0xe6150010
+#define SCLKBCR 0xe6150014
+#define SUBUSBCKCR 0xe6158080
+#define SPUCKCR 0xe6150084
+#define MSUCKCR 0xe6150088
+#define MVI3CKCR 0xe6150090
+#define VOUCKCR 0xe6150094
+#define MFCK1CR 0xe6150098
+#define MFCK2CR 0xe615009C
+#define PLLC1CR 0xe6150028
+#define PLLC2CR 0xe615002C
+#define RTMSTPCR0 0xe6158030
+#define RTMSTPCR2 0xe6158038
+#define SYMSTPCR0 0xe6158040
+#define SYMSTPCR2 0xe6158048
+#define CMMSTPCR0 0xe615804c
-struct clk {
- const char *name;
- unsigned long rate;
+/* Fixed 32 KHz root clock from EXTALR pin */
+static struct clk r_clk = {
+ .rate = 32768,
};
-#include <asm/clkdev.h>
+/*
+ * 26MHz default rate for the EXTALB1 root input clock.
+ * If needed, reset this with clk_set_rate() from the platform code.
+ */
+struct clk sh7367_extalb1_clk = {
+ .rate = 26666666,
+};
-int __clk_get(struct clk *clk)
-{
- return 1;
-}
-EXPORT_SYMBOL(__clk_get);
+/*
+ * 48MHz default rate for the EXTAL2 root input clock.
+ * If needed, reset this with clk_set_rate() from the platform code.
+ */
+struct clk sh7367_extal2_clk = {
+ .rate = 48000000,
+};
-void __clk_put(struct clk *clk)
+/* A fixed divide-by-2 block */
+static unsigned long div2_recalc(struct clk *clk)
{
+ return clk->parent->rate / 2;
}
-EXPORT_SYMBOL(__clk_put);
+static struct clk_ops div2_clk_ops = {
+ .recalc = div2_recalc,
+};
+
+/* Divide extalb1 by two */
+static struct clk extalb1_div2_clk = {
+ .ops = &div2_clk_ops,
+ .parent = &sh7367_extalb1_clk,
+};
+
+/* Divide extal2 by two */
+static struct clk extal2_div2_clk = {
+ .ops = &div2_clk_ops,
+ .parent = &sh7367_extal2_clk,
+};
-int clk_enable(struct clk *clk)
+/* PLLC1 */
+static unsigned long pllc1_recalc(struct clk *clk)
{
- return 0;
+ unsigned long mult = 1;
+
+ if (__raw_readl(PLLC1CR) & (1 << 14))
+ mult = (((__raw_readl(RTFRQCR) >> 24) & 0x3f) + 1) * 2;
+
+ return clk->parent->rate * mult;
}
-EXPORT_SYMBOL(clk_enable);
-void clk_disable(struct clk *clk)
+static struct clk_ops pllc1_clk_ops = {
+ .recalc = pllc1_recalc,
+};
+
+static struct clk pllc1_clk = {
+ .ops = &pllc1_clk_ops,
+ .flags = CLK_ENABLE_ON_INIT,
+ .parent = &extalb1_div2_clk,
+};
+
+/* Divide PLLC1 by two */
+static struct clk pllc1_div2_clk = {
+ .ops = &div2_clk_ops,
+ .parent = &pllc1_clk,
+};
+
+/* PLLC2 */
+static unsigned long pllc2_recalc(struct clk *clk)
{
+ unsigned long mult = 1;
+
+ if (__raw_readl(PLLC2CR) & (1 << 31))
+ mult = (((__raw_readl(PLLC2CR) >> 24) & 0x3f) + 1) * 2;
+
+ return clk->parent->rate * mult;
}
-EXPORT_SYMBOL(clk_disable);
-unsigned long clk_get_rate(struct clk *clk)
+static struct clk_ops pllc2_clk_ops = {
+ .recalc = pllc2_recalc,
+};
+
+static struct clk pllc2_clk = {
+ .ops = &pllc2_clk_ops,
+ .flags = CLK_ENABLE_ON_INIT,
+ .parent = &extalb1_div2_clk,
+};
+
+static struct clk *main_clks[] = {
+ &r_clk,
+ &sh7367_extalb1_clk,
+ &sh7367_extal2_clk,
+ &extalb1_div2_clk,
+ &extal2_div2_clk,
+ &pllc1_clk,
+ &pllc1_div2_clk,
+ &pllc2_clk,
+};
+
+static void div4_kick(struct clk *clk)
{
- return clk ? clk->rate : 0;
+ unsigned long value;
+
+ /* set KICK bit in SYFRQCR to update hardware setting */
+ value = __raw_readl(SYFRQCR);
+ value |= (1 << 31);
+ __raw_writel(value, SYFRQCR);
}
-EXPORT_SYMBOL(clk_get_rate);
-/* a static peripheral clock for now - enough to get sh-sci working */
-static struct clk peripheral_clk = {
- .name = "peripheral_clk",
- .rate = 48000000,
+static int divisors[] = { 2, 3, 4, 6, 8, 12, 16, 18,
+ 24, 32, 36, 48, 0, 72, 0, 0 };
+
+static struct clk_div_mult_table div4_div_mult_table = {
+ .divisors = divisors,
+ .nr_divisors = ARRAY_SIZE(divisors),
};
-/* a static rclk for now - enough to get sh_cmt working */
-static struct clk r_clk = {
- .name = "r_clk",
- .rate = 32768,
+static struct clk_div4_table div4_table = {
+ .div_mult_table = &div4_div_mult_table,
+ .kick = div4_kick,
+};
+
+enum { DIV4_I, DIV4_G, DIV4_S, DIV4_B,
+ DIV4_ZX, DIV4_ZT, DIV4_Z, DIV4_ZD, DIV4_HP,
+ DIV4_ZS, DIV4_ZB, DIV4_ZB3, DIV4_CP, DIV4_NR };
+
+#define DIV4(_reg, _bit, _mask, _flags) \
+ SH_CLK_DIV4(&pllc1_clk, _reg, _bit, _mask, _flags)
+
+static struct clk div4_clks[DIV4_NR] = {
+ [DIV4_I] = DIV4(RTFRQCR, 20, 0x6fff, CLK_ENABLE_ON_INIT),
+ [DIV4_G] = DIV4(RTFRQCR, 16, 0x6fff, CLK_ENABLE_ON_INIT),
+ [DIV4_S] = DIV4(RTFRQCR, 12, 0x6fff, CLK_ENABLE_ON_INIT),
+ [DIV4_B] = DIV4(RTFRQCR, 8, 0x6fff, CLK_ENABLE_ON_INIT),
+ [DIV4_ZX] = DIV4(SYFRQCR, 20, 0x6fff, 0),
+ [DIV4_ZT] = DIV4(SYFRQCR, 16, 0x6fff, 0),
+ [DIV4_Z] = DIV4(SYFRQCR, 12, 0x6fff, 0),
+ [DIV4_ZD] = DIV4(SYFRQCR, 8, 0x6fff, 0),
+ [DIV4_HP] = DIV4(SYFRQCR, 4, 0x6fff, 0),
+ [DIV4_ZS] = DIV4(CMFRQCR, 12, 0x6fff, 0),
+ [DIV4_ZB] = DIV4(CMFRQCR, 8, 0x6fff, 0),
+ [DIV4_ZB3] = DIV4(CMFRQCR, 4, 0x6fff, 0),
+ [DIV4_CP] = DIV4(CMFRQCR, 0, 0x6fff, 0),
};
-/* a static usb0 for now - enough to get r8a66597 working */
-static struct clk usb0_clk = {
- .name = "usb0",
+enum { DIV6_SUB, DIV6_SIUA, DIV6_SIUB, DIV6_MSU, DIV6_SPU,
+ DIV6_MVI3, DIV6_MF1, DIV6_MF2,
+ DIV6_VCK1, DIV6_VCK2, DIV6_VCK3, DIV6_VOU,
+ DIV6_NR };
+
+static struct clk div6_clks[DIV6_NR] = {
+ [DIV6_SUB] = SH_CLK_DIV6(&sh7367_extal2_clk, SUBUSBCKCR, 0),
+ [DIV6_SIUA] = SH_CLK_DIV6(&pllc1_div2_clk, SCLKACR, 0),
+ [DIV6_SIUB] = SH_CLK_DIV6(&pllc1_div2_clk, SCLKBCR, 0),
+ [DIV6_MSU] = SH_CLK_DIV6(&pllc1_div2_clk, MSUCKCR, 0),
+ [DIV6_SPU] = SH_CLK_DIV6(&pllc1_div2_clk, SPUCKCR, 0),
+ [DIV6_MVI3] = SH_CLK_DIV6(&pllc1_div2_clk, MVI3CKCR, 0),
+ [DIV6_MF1] = SH_CLK_DIV6(&pllc1_div2_clk, MFCK1CR, 0),
+ [DIV6_MF2] = SH_CLK_DIV6(&pllc1_div2_clk, MFCK2CR, 0),
+ [DIV6_VCK1] = SH_CLK_DIV6(&pllc1_div2_clk, VCLKCR1, 0),
+ [DIV6_VCK2] = SH_CLK_DIV6(&pllc1_div2_clk, VCLKCR2, 0),
+ [DIV6_VCK3] = SH_CLK_DIV6(&pllc1_div2_clk, VCLKCR3, 0),
+ [DIV6_VOU] = SH_CLK_DIV6(&pllc1_div2_clk, VOUCKCR, 0),
};
-/* a static keysc0 clk for now - enough to get sh_keysc working */
-static struct clk keysc0_clk = {
- .name = "keysc0",
+enum { RTMSTP001,
+ RTMSTP231, RTMSTP230, RTMSTP229, RTMSTP228, RTMSTP226,
+ RTMSTP216, RTMSTP206, RTMSTP205, RTMSTP201,
+ SYMSTP023, SYMSTP007, SYMSTP006, SYMSTP004,
+ SYMSTP003, SYMSTP002, SYMSTP001, SYMSTP000,
+ SYMSTP231, SYMSTP229, SYMSTP225, SYMSTP223, SYMSTP222,
+ SYMSTP215, SYMSTP214, SYMSTP213, SYMSTP211,
+ CMMSTP003,
+ MSTP_NR };
+
+#define MSTP(_parent, _reg, _bit, _flags) \
+ SH_CLK_MSTP32(_parent, _reg, _bit, _flags)
+
+static struct clk mstp_clks[MSTP_NR] = {
+ [RTMSTP001] = MSTP(&div6_clks[DIV6_SUB], RTMSTPCR0, 1, 0), /* IIC2 */
+ [RTMSTP231] = MSTP(&div4_clks[DIV4_B], RTMSTPCR2, 31, 0), /* VEU3 */
+ [RTMSTP230] = MSTP(&div4_clks[DIV4_B], RTMSTPCR2, 30, 0), /* VEU2 */
+ [RTMSTP229] = MSTP(&div4_clks[DIV4_B], RTMSTPCR2, 29, 0), /* VEU1 */
+ [RTMSTP228] = MSTP(&div4_clks[DIV4_B], RTMSTPCR2, 28, 0), /* VEU0 */
+ [RTMSTP226] = MSTP(&div4_clks[DIV4_B], RTMSTPCR2, 26, 0), /* VEU2H */
+ [RTMSTP216] = MSTP(&div6_clks[DIV6_SUB], RTMSTPCR2, 16, 0), /* IIC0 */
+ [RTMSTP206] = MSTP(&div4_clks[DIV4_B], RTMSTPCR2, 6, 0), /* JPU */
+ [RTMSTP205] = MSTP(&div6_clks[DIV6_VOU], RTMSTPCR2, 5, 0), /* VOU */
+ [RTMSTP201] = MSTP(&div4_clks[DIV4_B], RTMSTPCR2, 1, 0), /* VPU */
+ [SYMSTP023] = MSTP(&div6_clks[DIV6_SPU], SYMSTPCR0, 23, 0), /* SPU1 */
+ [SYMSTP007] = MSTP(&div6_clks[DIV6_SUB], SYMSTPCR0, 7, 0), /* SCIFA5 */
+ [SYMSTP006] = MSTP(&div6_clks[DIV6_SUB], SYMSTPCR0, 6, 0), /* SCIFB */
+ [SYMSTP004] = MSTP(&div6_clks[DIV6_SUB], SYMSTPCR0, 4, 0), /* SCIFA0 */
+ [SYMSTP003] = MSTP(&div6_clks[DIV6_SUB], SYMSTPCR0, 3, 0), /* SCIFA1 */
+ [SYMSTP002] = MSTP(&div6_clks[DIV6_SUB], SYMSTPCR0, 2, 0), /* SCIFA2 */
+ [SYMSTP001] = MSTP(&div6_clks[DIV6_SUB], SYMSTPCR0, 1, 0), /* SCIFA3 */
+ [SYMSTP000] = MSTP(&div6_clks[DIV6_SUB], SYMSTPCR0, 0, 0), /* SCIFA4 */
+ [SYMSTP231] = MSTP(&div6_clks[DIV6_SUB], SYMSTPCR2, 31, 0), /* SIU */
+ [SYMSTP229] = MSTP(&r_clk, SYMSTPCR2, 29, 0), /* CMT10 */
+ [SYMSTP225] = MSTP(&div6_clks[DIV6_SUB], SYMSTPCR2, 25, 0), /* IRDA */
+ [SYMSTP223] = MSTP(&div6_clks[DIV6_SUB], SYMSTPCR2, 23, 0), /* IIC1 */
+ [SYMSTP222] = MSTP(&div6_clks[DIV6_SUB], SYMSTPCR2, 22, 0), /* USBHS */
+ [SYMSTP215] = MSTP(&div4_clks[DIV4_HP], SYMSTPCR2, 15, 0), /* FLCTL */
+ [SYMSTP214] = MSTP(&div4_clks[DIV4_HP], SYMSTPCR2, 14, 0), /* SDHI0 */
+ [SYMSTP213] = MSTP(&div4_clks[DIV4_HP], SYMSTPCR2, 13, 0), /* SDHI1 */
+ [SYMSTP211] = MSTP(&div4_clks[DIV4_HP], SYMSTPCR2, 11, 0), /* SDHI2 */
+ [CMMSTP003] = MSTP(&r_clk, CMMSTPCR0, 3, 0), /* KEYSC */
};
+#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
+#define CLKDEV_DEV_ID(_id, _clk) { .dev_id = _id, .clk = _clk }
+
static struct clk_lookup lookups[] = {
- {
- .clk = &peripheral_clk,
- }, {
- .clk = &r_clk,
- }, {
- .clk = &usb0_clk,
- }, {
- .clk = &keysc0_clk,
- }
+ /* main clocks */
+ CLKDEV_CON_ID("r_clk", &r_clk),
+ CLKDEV_CON_ID("extalb1", &sh7367_extalb1_clk),
+ CLKDEV_CON_ID("extal2", &sh7367_extal2_clk),
+ CLKDEV_CON_ID("extalb1_div2_clk", &extalb1_div2_clk),
+ CLKDEV_CON_ID("extal2_div2_clk", &extal2_div2_clk),
+ CLKDEV_CON_ID("pllc1_clk", &pllc1_clk),
+ CLKDEV_CON_ID("pllc1_div2_clk", &pllc1_div2_clk),
+ CLKDEV_CON_ID("pllc2_clk", &pllc2_clk),
+
+ /* DIV4 clocks */
+ CLKDEV_CON_ID("i_clk", &div4_clks[DIV4_I]),
+ CLKDEV_CON_ID("g_clk", &div4_clks[DIV4_G]),
+ CLKDEV_CON_ID("b_clk", &div4_clks[DIV4_B]),
+ CLKDEV_CON_ID("zx_clk", &div4_clks[DIV4_ZX]),
+ CLKDEV_CON_ID("zt_clk", &div4_clks[DIV4_ZT]),
+ CLKDEV_CON_ID("z_clk", &div4_clks[DIV4_Z]),
+ CLKDEV_CON_ID("zd_clk", &div4_clks[DIV4_ZD]),
+ CLKDEV_CON_ID("hp_clk", &div4_clks[DIV4_HP]),
+ CLKDEV_CON_ID("zs_clk", &div4_clks[DIV4_ZS]),
+ CLKDEV_CON_ID("zb_clk", &div4_clks[DIV4_ZB]),
+ CLKDEV_CON_ID("zb3_clk", &div4_clks[DIV4_ZB3]),
+ CLKDEV_CON_ID("cp_clk", &div4_clks[DIV4_CP]),
+
+ /* DIV6 clocks */
+ CLKDEV_CON_ID("sub_clk", &div6_clks[DIV6_SUB]),
+ CLKDEV_CON_ID("siua_clk", &div6_clks[DIV6_SIUA]),
+ CLKDEV_CON_ID("siub_clk", &div6_clks[DIV6_SIUB]),
+ CLKDEV_CON_ID("msu_clk", &div6_clks[DIV6_MSU]),
+ CLKDEV_CON_ID("spu_clk", &div6_clks[DIV6_SPU]),
+ CLKDEV_CON_ID("mvi3_clk", &div6_clks[DIV6_MVI3]),
+ CLKDEV_CON_ID("mf1_clk", &div6_clks[DIV6_MF1]),
+ CLKDEV_CON_ID("mf2_clk", &div6_clks[DIV6_MF2]),
+ CLKDEV_CON_ID("vck1_clk", &div6_clks[DIV6_VCK1]),
+ CLKDEV_CON_ID("vck2_clk", &div6_clks[DIV6_VCK2]),
+ CLKDEV_CON_ID("vck3_clk", &div6_clks[DIV6_VCK3]),
+ CLKDEV_CON_ID("vou_clk", &div6_clks[DIV6_VOU]),
+
+ /* MSTP32 clocks */
+ CLKDEV_DEV_ID("i2c-sh_mobile.2", &mstp_clks[RTMSTP001]), /* IIC2 */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.4", &mstp_clks[RTMSTP231]), /* VEU3 */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.3", &mstp_clks[RTMSTP230]), /* VEU2 */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.2", &mstp_clks[RTMSTP229]), /* VEU1 */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.1", &mstp_clks[RTMSTP228]), /* VEU0 */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.5", &mstp_clks[RTMSTP226]), /* VEU2H */
+ CLKDEV_DEV_ID("i2c-sh_mobile.0", &mstp_clks[RTMSTP216]), /* IIC0 */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.6", &mstp_clks[RTMSTP206]), /* JPU */
+ CLKDEV_DEV_ID("sh-vou", &mstp_clks[RTMSTP205]), /* VOU */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.0", &mstp_clks[RTMSTP201]), /* VPU */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.7", &mstp_clks[SYMSTP023]), /* SPU1 */
+ CLKDEV_DEV_ID("sh-sci.5", &mstp_clks[SYMSTP007]), /* SCIFA5 */
+ CLKDEV_DEV_ID("sh-sci.6", &mstp_clks[SYMSTP006]), /* SCIFB */
+ CLKDEV_DEV_ID("sh-sci.0", &mstp_clks[SYMSTP004]), /* SCIFA0 */
+ CLKDEV_DEV_ID("sh-sci.1", &mstp_clks[SYMSTP003]), /* SCIFA1 */
+ CLKDEV_DEV_ID("sh-sci.2", &mstp_clks[SYMSTP002]), /* SCIFA2 */
+ CLKDEV_DEV_ID("sh-sci.3", &mstp_clks[SYMSTP001]), /* SCIFA3 */
+ CLKDEV_DEV_ID("sh-sci.4", &mstp_clks[SYMSTP000]), /* SCIFA4 */
+ CLKDEV_DEV_ID("sh_siu", &mstp_clks[SYMSTP231]), /* SIU */
+ CLKDEV_CON_ID("cmt1", &mstp_clks[SYMSTP229]), /* CMT10 */
+ CLKDEV_DEV_ID("sh_irda", &mstp_clks[SYMSTP225]), /* IRDA */
+ CLKDEV_DEV_ID("i2c-sh_mobile.1", &mstp_clks[SYMSTP223]), /* IIC1 */
+ CLKDEV_DEV_ID("r8a66597_hcd.0", &mstp_clks[SYMSTP222]), /* USBHS */
+ CLKDEV_DEV_ID("r8a66597_udc.0", &mstp_clks[SYMSTP222]), /* USBHS */
+ CLKDEV_DEV_ID("sh_flctl", &mstp_clks[SYMSTP215]), /* FLCTL */
+ CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[SYMSTP214]), /* SDHI0 */
+ CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[SYMSTP213]), /* SDHI1 */
+ CLKDEV_DEV_ID("sh_mobile_sdhi.2", &mstp_clks[SYMSTP211]), /* SDHI2 */
+ CLKDEV_DEV_ID("sh_keysc.0", &mstp_clks[CMMSTP003]), /* KEYSC */
};
void __init sh7367_clock_init(void)
{
- int i;
+ int k, ret = 0;
+
+ for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++)
+ ret = clk_register(main_clks[k]);
+
+ if (!ret)
+ ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table);
+
+ if (!ret)
+ ret = sh_clk_div6_register(div6_clks, DIV6_NR);
+
+ if (!ret)
+ ret = sh_clk_mstp32_register(mstp_clks, MSTP_NR);
+
+ clkdev_add_table(lookups, ARRAY_SIZE(lookups));
- for (i = 0; i < ARRAY_SIZE(lookups); i++) {
- lookups[i].con_id = lookups[i].clk->name;
- clkdev_add(&lookups[i]);
- }
+ if (!ret)
+ clk_init();
+ else
+ panic("failed to setup sh7367 clocks\n");
}
--- /dev/null
+/*
+ * SH7372 clock framework support
+ *
+ * Copyright (C) 2010 Magnus Damm
+ *
+ * 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
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/sh_clk.h>
+#include <mach/common.h>
+#include <asm/clkdev.h>
+
+/* SH7372 registers */
+#define FRQCRA 0xe6150000
+#define FRQCRB 0xe6150004
+#define FRQCRC 0xe61500e0
+#define FRQCRD 0xe61500e4
+#define VCLKCR1 0xe6150008
+#define VCLKCR2 0xe615000c
+#define VCLKCR3 0xe615001c
+#define FMSICKCR 0xe6150010
+#define FMSOCKCR 0xe6150014
+#define FSIACKCR 0xe6150018
+#define FSIBCKCR 0xe6150090
+#define SUBCKCR 0xe6150080
+#define SPUCKCR 0xe6150084
+#define VOUCKCR 0xe6150088
+#define HDMICKCR 0xe6150094
+#define DSITCKCR 0xe6150060
+#define DSI0PCKCR 0xe6150064
+#define DSI1PCKCR 0xe6150098
+#define PLLC01CR 0xe6150028
+#define PLLC2CR 0xe615002c
+#define SMSTPCR0 0xe6150130
+#define SMSTPCR1 0xe6150134
+#define SMSTPCR2 0xe6150138
+#define SMSTPCR3 0xe615013c
+#define SMSTPCR4 0xe6150140
+
+/* Platforms must set frequency on their DV_CLKI pin */
+struct clk dv_clki_clk = {
+};
+
+/* Fixed 32 KHz root clock from EXTALR pin */
+static struct clk r_clk = {
+ .rate = 32768,
+};
+
+/*
+ * 26MHz default rate for the EXTAL1 root input clock.
+ * If needed, reset this with clk_set_rate() from the platform code.
+ */
+struct clk sh7372_extal1_clk = {
+ .rate = 26000000,
+};
+
+/*
+ * 48MHz default rate for the EXTAL2 root input clock.
+ * If needed, reset this with clk_set_rate() from the platform code.
+ */
+struct clk sh7372_extal2_clk = {
+ .rate = 48000000,
+};
+
+/* A fixed divide-by-2 block */
+static unsigned long div2_recalc(struct clk *clk)
+{
+ return clk->parent->rate / 2;
+}
+
+static struct clk_ops div2_clk_ops = {
+ .recalc = div2_recalc,
+};
+
+/* Divide dv_clki by two */
+struct clk dv_clki_div2_clk = {
+ .ops = &div2_clk_ops,
+ .parent = &dv_clki_clk,
+};
+
+/* Divide extal1 by two */
+static struct clk extal1_div2_clk = {
+ .ops = &div2_clk_ops,
+ .parent = &sh7372_extal1_clk,
+};
+
+/* Divide extal2 by two */
+static struct clk extal2_div2_clk = {
+ .ops = &div2_clk_ops,
+ .parent = &sh7372_extal2_clk,
+};
+
+/* Divide extal2 by four */
+static struct clk extal2_div4_clk = {
+ .ops = &div2_clk_ops,
+ .parent = &extal2_div2_clk,
+};
+
+/* PLLC0 and PLLC1 */
+static unsigned long pllc01_recalc(struct clk *clk)
+{
+ unsigned long mult = 1;
+
+ if (__raw_readl(PLLC01CR) & (1 << 14))
+ mult = (((__raw_readl(clk->enable_reg) >> 24) & 0x3f) + 1) * 2;
+
+ return clk->parent->rate * mult;
+}
+
+static struct clk_ops pllc01_clk_ops = {
+ .recalc = pllc01_recalc,
+};
+
+static struct clk pllc0_clk = {
+ .ops = &pllc01_clk_ops,
+ .flags = CLK_ENABLE_ON_INIT,
+ .parent = &extal1_div2_clk,
+ .enable_reg = (void __iomem *)FRQCRC,
+};
+
+static struct clk pllc1_clk = {
+ .ops = &pllc01_clk_ops,
+ .flags = CLK_ENABLE_ON_INIT,
+ .parent = &extal1_div2_clk,
+ .enable_reg = (void __iomem *)FRQCRA,
+};
+
+/* Divide PLLC1 by two */
+static struct clk pllc1_div2_clk = {
+ .ops = &div2_clk_ops,
+ .parent = &pllc1_clk,
+};
+
+/* PLLC2 */
+
+/* Indices are important - they are the actual src selecting values */
+static struct clk *pllc2_parent[] = {
+ [0] = &extal1_div2_clk,
+ [1] = &extal2_div2_clk,
+ [2] = &dv_clki_div2_clk,
+};
+
+/* Only multipliers 20 * 2 to 46 * 2 are valid, last entry for CPUFREQ_TABLE_END */
+static struct cpufreq_frequency_table pllc2_freq_table[29];
+
+static void pllc2_table_rebuild(struct clk *clk)
+{
+ int i;
+
+ /* Initialise PLLC2 frequency table */
+ for (i = 0; i < ARRAY_SIZE(pllc2_freq_table) - 2; i++) {
+ pllc2_freq_table[i].frequency = clk->parent->rate * (i + 20) * 2;
+ pllc2_freq_table[i].index = i;
+ }
+
+ /* This is a special entry - switching PLL off makes it a repeater */
+ pllc2_freq_table[i].frequency = clk->parent->rate;
+ pllc2_freq_table[i].index = i;
+
+ pllc2_freq_table[++i].frequency = CPUFREQ_TABLE_END;
+ pllc2_freq_table[i].index = i;
+}
+
+static unsigned long pllc2_recalc(struct clk *clk)
+{
+ unsigned long mult = 1;
+
+ pllc2_table_rebuild(clk);
+
+ /*
+ * If the PLL is off, mult == 1, clk->rate will be updated in
+ * pllc2_enable().
+ */
+ if (__raw_readl(PLLC2CR) & (1 << 31))
+ mult = (((__raw_readl(PLLC2CR) >> 24) & 0x3f) + 1) * 2;
+
+ return clk->parent->rate * mult;
+}
+
+static long pllc2_round_rate(struct clk *clk, unsigned long rate)
+{
+ return clk_rate_table_round(clk, clk->freq_table, rate);
+}
+
+static int pllc2_enable(struct clk *clk)
+{
+ int i;
+
+ __raw_writel(__raw_readl(PLLC2CR) | 0x80000000, PLLC2CR);
+
+ for (i = 0; i < 100; i++)
+ if (__raw_readl(PLLC2CR) & 0x80000000) {
+ clk->rate = pllc2_recalc(clk);
+ return 0;
+ }
+
+ pr_err("%s(): timeout!\n", __func__);
+
+ return -ETIMEDOUT;
+}
+
+static void pllc2_disable(struct clk *clk)
+{
+ __raw_writel(__raw_readl(PLLC2CR) & ~0x80000000, PLLC2CR);
+}
+
+static int pllc2_set_rate(struct clk *clk,
+ unsigned long rate, int algo_id)
+{
+ unsigned long value;
+ int idx;
+
+ idx = clk_rate_table_find(clk, clk->freq_table, rate);
+ if (idx < 0)
+ return idx;
+
+ if (rate == clk->parent->rate) {
+ pllc2_disable(clk);
+ return 0;
+ }
+
+ value = __raw_readl(PLLC2CR) & ~(0x3f << 24);
+
+ if (value & 0x80000000)
+ pllc2_disable(clk);
+
+ __raw_writel((value & ~0x80000000) | ((idx + 19) << 24), PLLC2CR);
+
+ if (value & 0x80000000)
+ return pllc2_enable(clk);
+
+ return 0;
+}
+
+static int pllc2_set_parent(struct clk *clk, struct clk *parent)
+{
+ u32 value;
+ int ret, i;
+
+ if (!clk->parent_table || !clk->parent_num)
+ return -EINVAL;
+
+ /* Search the parent */
+ for (i = 0; i < clk->parent_num; i++)
+ if (clk->parent_table[i] == parent)
+ break;
+
+ if (i == clk->parent_num)
+ return -ENODEV;
+
+ ret = clk_reparent(clk, parent);
+ if (ret < 0)
+ return ret;
+
+ value = __raw_readl(PLLC2CR) & ~(3 << 6);
+
+ __raw_writel(value | (i << 6), PLLC2CR);
+
+ /* Rebiuld the frequency table */
+ pllc2_table_rebuild(clk);
+
+ return 0;
+}
+
+static struct clk_ops pllc2_clk_ops = {
+ .recalc = pllc2_recalc,
+ .round_rate = pllc2_round_rate,
+ .set_rate = pllc2_set_rate,
+ .enable = pllc2_enable,
+ .disable = pllc2_disable,
+ .set_parent = pllc2_set_parent,
+};
+
+struct clk pllc2_clk = {
+ .ops = &pllc2_clk_ops,
+ .flags = CLK_ENABLE_ON_INIT,
+ .parent = &extal1_div2_clk,
+ .freq_table = pllc2_freq_table,
+ .parent_table = pllc2_parent,
+ .parent_num = ARRAY_SIZE(pllc2_parent),
+};
+
+static struct clk *main_clks[] = {
+ &dv_clki_clk,
+ &r_clk,
+ &sh7372_extal1_clk,
+ &sh7372_extal2_clk,
+ &dv_clki_div2_clk,
+ &extal1_div2_clk,
+ &extal2_div2_clk,
+ &extal2_div4_clk,
+ &pllc0_clk,
+ &pllc1_clk,
+ &pllc1_div2_clk,
+ &pllc2_clk,
+};
+
+static void div4_kick(struct clk *clk)
+{
+ unsigned long value;
+
+ /* set KICK bit in FRQCRB to update hardware setting */
+ value = __raw_readl(FRQCRB);
+ value |= (1 << 31);
+ __raw_writel(value, FRQCRB);
+}
+
+static int divisors[] = { 2, 3, 4, 6, 8, 12, 16, 18,
+ 24, 32, 36, 48, 0, 72, 96, 0 };
+
+static struct clk_div_mult_table div4_div_mult_table = {
+ .divisors = divisors,
+ .nr_divisors = ARRAY_SIZE(divisors),
+};
+
+static struct clk_div4_table div4_table = {
+ .div_mult_table = &div4_div_mult_table,
+ .kick = div4_kick,
+};
+
+enum { DIV4_I, DIV4_ZG, DIV4_B, DIV4_M1, DIV4_CSIR,
+ DIV4_ZTR, DIV4_ZT, DIV4_ZX, DIV4_HP,
+ DIV4_ISPB, DIV4_S, DIV4_ZB, DIV4_ZB3, DIV4_CP,
+ DIV4_DDRP, DIV4_NR };
+
+#define DIV4(_reg, _bit, _mask, _flags) \
+ SH_CLK_DIV4(&pllc1_clk, _reg, _bit, _mask, _flags)
+
+static struct clk div4_clks[DIV4_NR] = {
+ [DIV4_I] = DIV4(FRQCRA, 20, 0x6fff, CLK_ENABLE_ON_INIT),
+ [DIV4_ZG] = DIV4(FRQCRA, 16, 0x6fff, CLK_ENABLE_ON_INIT),
+ [DIV4_B] = DIV4(FRQCRA, 8, 0x6fff, CLK_ENABLE_ON_INIT),
+ [DIV4_M1] = DIV4(FRQCRA, 4, 0x6fff, CLK_ENABLE_ON_INIT),
+ [DIV4_CSIR] = DIV4(FRQCRA, 0, 0x6fff, 0),
+ [DIV4_ZTR] = DIV4(FRQCRB, 20, 0x6fff, 0),
+ [DIV4_ZT] = DIV4(FRQCRB, 16, 0x6fff, 0),
+ [DIV4_ZX] = DIV4(FRQCRB, 12, 0x6fff, 0),
+ [DIV4_HP] = DIV4(FRQCRB, 4, 0x6fff, 0),
+ [DIV4_ISPB] = DIV4(FRQCRC, 20, 0x6fff, 0),
+ [DIV4_S] = DIV4(FRQCRC, 12, 0x6fff, 0),
+ [DIV4_ZB] = DIV4(FRQCRC, 8, 0x6fff, 0),
+ [DIV4_ZB3] = DIV4(FRQCRC, 4, 0x6fff, 0),
+ [DIV4_CP] = DIV4(FRQCRC, 0, 0x6fff, 0),
+ [DIV4_DDRP] = DIV4(FRQCRD, 0, 0x677c, 0),
+};
+
+enum { DIV6_VCK1, DIV6_VCK2, DIV6_VCK3, DIV6_FMSI, DIV6_FMSO,
+ DIV6_FSIA, DIV6_FSIB, DIV6_SUB, DIV6_SPU,
+ DIV6_VOU, DIV6_DSIT, DIV6_DSI0P, DIV6_DSI1P,
+ DIV6_NR };
+
+static struct clk div6_clks[DIV6_NR] = {
+ [DIV6_VCK1] = SH_CLK_DIV6(&pllc1_div2_clk, VCLKCR1, 0),
+ [DIV6_VCK2] = SH_CLK_DIV6(&pllc1_div2_clk, VCLKCR2, 0),
+ [DIV6_VCK3] = SH_CLK_DIV6(&pllc1_div2_clk, VCLKCR3, 0),
+ [DIV6_FMSI] = SH_CLK_DIV6(&pllc1_div2_clk, FMSICKCR, 0),
+ [DIV6_FMSO] = SH_CLK_DIV6(&pllc1_div2_clk, FMSOCKCR, 0),
+ [DIV6_FSIA] = SH_CLK_DIV6(&pllc1_div2_clk, FSIACKCR, 0),
+ [DIV6_FSIB] = SH_CLK_DIV6(&pllc1_div2_clk, FSIBCKCR, 0),
+ [DIV6_SUB] = SH_CLK_DIV6(&sh7372_extal2_clk, SUBCKCR, 0),
+ [DIV6_SPU] = SH_CLK_DIV6(&pllc1_div2_clk, SPUCKCR, 0),
+ [DIV6_VOU] = SH_CLK_DIV6(&pllc1_div2_clk, VOUCKCR, 0),
+ [DIV6_DSIT] = SH_CLK_DIV6(&pllc1_div2_clk, DSITCKCR, 0),
+ [DIV6_DSI0P] = SH_CLK_DIV6(&pllc1_div2_clk, DSI0PCKCR, 0),
+ [DIV6_DSI1P] = SH_CLK_DIV6(&pllc1_div2_clk, DSI1PCKCR, 0),
+};
+
+enum { DIV6_HDMI, DIV6_REPARENT_NR };
+
+/* Indices are important - they are the actual src selecting values */
+static struct clk *hdmi_parent[] = {
+ [0] = &pllc1_div2_clk,
+ [1] = &pllc2_clk,
+ [2] = &dv_clki_clk,
+ [3] = NULL, /* pllc2_div4 not implemented yet */
+};
+
+static struct clk div6_reparent_clks[DIV6_REPARENT_NR] = {
+ [DIV6_HDMI] = SH_CLK_DIV6_EXT(&pllc1_div2_clk, HDMICKCR, 0,
+ hdmi_parent, ARRAY_SIZE(hdmi_parent), 6, 2),
+};
+
+enum { MSTP001,
+ MSTP131, MSTP130,
+ MSTP129, MSTP128,
+ MSTP118, MSTP117, MSTP116,
+ MSTP106, MSTP101, MSTP100,
+ MSTP223,
+ MSTP207, MSTP206, MSTP204, MSTP203, MSTP202, MSTP201, MSTP200,
+ MSTP329, MSTP328, MSTP323, MSTP322, MSTP314, MSTP313, MSTP312,
+ MSTP415, MSTP413, MSTP411, MSTP410, MSTP406, MSTP403,
+ MSTP_NR };
+
+#define MSTP(_parent, _reg, _bit, _flags) \
+ SH_CLK_MSTP32(_parent, _reg, _bit, _flags)
+
+static struct clk mstp_clks[MSTP_NR] = {
+ [MSTP001] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR0, 1, 0), /* IIC2 */
+ [MSTP131] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 31, 0), /* VEU3 */
+ [MSTP130] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 30, 0), /* VEU2 */
+ [MSTP129] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 29, 0), /* VEU1 */
+ [MSTP128] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 28, 0), /* VEU0 */
+ [MSTP118] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 18, 0), /* DSITX */
+ [MSTP117] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 17, 0), /* LCDC1 */
+ [MSTP116] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR1, 16, 0), /* IIC0 */
+ [MSTP106] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 6, 0), /* JPU */
+ [MSTP101] = MSTP(&div4_clks[DIV4_M1], SMSTPCR1, 1, 0), /* VPU */
+ [MSTP100] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 0, 0), /* LCDC0 */
+ [MSTP223] = MSTP(&div6_clks[DIV6_SPU], SMSTPCR2, 23, 0), /* SPU2 */
+ [MSTP207] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 7, 0), /* SCIFA5 */
+ [MSTP206] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 6, 0), /* SCIFB */
+ [MSTP204] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 4, 0), /* SCIFA0 */
+ [MSTP203] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 3, 0), /* SCIFA1 */
+ [MSTP202] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 2, 0), /* SCIFA2 */
+ [MSTP201] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 1, 0), /* SCIFA3 */
+ [MSTP200] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 0, 0), /* SCIFA4 */
+ [MSTP329] = MSTP(&r_clk, SMSTPCR3, 29, 0), /* CMT10 */
+ [MSTP328] = MSTP(&div6_clks[DIV6_SPU], SMSTPCR3, 28, CLK_ENABLE_ON_INIT), /* FSIA */
+ [MSTP323] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR3, 23, 0), /* IIC1 */
+ [MSTP322] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR3, 22, 0), /* USB0 */
+ [MSTP314] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 14, 0), /* SDHI0 */
+ [MSTP313] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 13, 0), /* SDHI1 */
+ [MSTP312] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 12, 0), /* MMC */
+ [MSTP415] = MSTP(&div4_clks[DIV4_HP], SMSTPCR4, 15, 0), /* SDHI2 */
+ [MSTP413] = MSTP(&pllc1_div2_clk, SMSTPCR4, 13, 0), /* HDMI */
+ [MSTP411] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR4, 11, 0), /* IIC3 */
+ [MSTP410] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR4, 10, 0), /* IIC4 */
+ [MSTP406] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR4, 6, 0), /* USB1 */
+ [MSTP403] = MSTP(&r_clk, SMSTPCR4, 3, 0), /* KEYSC */
+};
+
+#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
+#define CLKDEV_DEV_ID(_id, _clk) { .dev_id = _id, .clk = _clk }
+
+static struct clk_lookup lookups[] = {
+ /* main clocks */
+ CLKDEV_CON_ID("dv_clki_div2_clk", &dv_clki_div2_clk),
+ CLKDEV_CON_ID("r_clk", &r_clk),
+ CLKDEV_CON_ID("extal1", &sh7372_extal1_clk),
+ CLKDEV_CON_ID("extal2", &sh7372_extal2_clk),
+ CLKDEV_CON_ID("extal1_div2_clk", &extal1_div2_clk),
+ CLKDEV_CON_ID("extal2_div2_clk", &extal2_div2_clk),
+ CLKDEV_CON_ID("extal2_div4_clk", &extal2_div4_clk),
+ CLKDEV_CON_ID("pllc0_clk", &pllc0_clk),
+ CLKDEV_CON_ID("pllc1_clk", &pllc1_clk),
+ CLKDEV_CON_ID("pllc1_div2_clk", &pllc1_div2_clk),
+ CLKDEV_CON_ID("pllc2_clk", &pllc2_clk),
+
+ /* DIV4 clocks */
+ CLKDEV_CON_ID("i_clk", &div4_clks[DIV4_I]),
+ CLKDEV_CON_ID("zg_clk", &div4_clks[DIV4_ZG]),
+ CLKDEV_CON_ID("b_clk", &div4_clks[DIV4_B]),
+ CLKDEV_CON_ID("m1_clk", &div4_clks[DIV4_M1]),
+ CLKDEV_CON_ID("csir_clk", &div4_clks[DIV4_CSIR]),
+ CLKDEV_CON_ID("ztr_clk", &div4_clks[DIV4_ZTR]),
+ CLKDEV_CON_ID("zt_clk", &div4_clks[DIV4_ZT]),
+ CLKDEV_CON_ID("zx_clk", &div4_clks[DIV4_ZX]),
+ CLKDEV_CON_ID("hp_clk", &div4_clks[DIV4_HP]),
+ CLKDEV_CON_ID("ispb_clk", &div4_clks[DIV4_ISPB]),
+ CLKDEV_CON_ID("s_clk", &div4_clks[DIV4_S]),
+ CLKDEV_CON_ID("zb_clk", &div4_clks[DIV4_ZB]),
+ CLKDEV_CON_ID("zb3_clk", &div4_clks[DIV4_ZB3]),
+ CLKDEV_CON_ID("cp_clk", &div4_clks[DIV4_CP]),
+ CLKDEV_CON_ID("ddrp_clk", &div4_clks[DIV4_DDRP]),
+
+ /* DIV6 clocks */
+ CLKDEV_CON_ID("vck1_clk", &div6_clks[DIV6_VCK1]),
+ CLKDEV_CON_ID("vck2_clk", &div6_clks[DIV6_VCK2]),
+ CLKDEV_CON_ID("vck3_clk", &div6_clks[DIV6_VCK3]),
+ CLKDEV_CON_ID("fmsi_clk", &div6_clks[DIV6_FMSI]),
+ CLKDEV_CON_ID("fmso_clk", &div6_clks[DIV6_FMSO]),
+ CLKDEV_CON_ID("fsia_clk", &div6_clks[DIV6_FSIA]),
+ CLKDEV_CON_ID("fsib_clk", &div6_clks[DIV6_FSIB]),
+ CLKDEV_CON_ID("sub_clk", &div6_clks[DIV6_SUB]),
+ CLKDEV_CON_ID("spu_clk", &div6_clks[DIV6_SPU]),
+ CLKDEV_CON_ID("vou_clk", &div6_clks[DIV6_VOU]),
+ CLKDEV_CON_ID("hdmi_clk", &div6_reparent_clks[DIV6_HDMI]),
+ CLKDEV_CON_ID("dsit_clk", &div6_clks[DIV6_DSIT]),
+ CLKDEV_CON_ID("dsi0p_clk", &div6_clks[DIV6_DSI0P]),
+ CLKDEV_CON_ID("dsi1p_clk", &div6_clks[DIV6_DSI1P]),
+
+ /* MSTP32 clocks */
+ CLKDEV_DEV_ID("i2c-sh_mobile.2", &mstp_clks[MSTP001]), /* IIC2 */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.4", &mstp_clks[MSTP131]), /* VEU3 */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.3", &mstp_clks[MSTP130]), /* VEU2 */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.2", &mstp_clks[MSTP129]), /* VEU1 */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.1", &mstp_clks[MSTP128]), /* VEU0 */
+ CLKDEV_DEV_ID("sh-mipi-dsi.0", &mstp_clks[MSTP118]), /* DSITX */
+ CLKDEV_DEV_ID("sh_mobile_lcdc_fb.1", &mstp_clks[MSTP117]), /* LCDC1 */
+ CLKDEV_DEV_ID("i2c-sh_mobile.0", &mstp_clks[MSTP116]), /* IIC0 */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.5", &mstp_clks[MSTP106]), /* JPU */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.0", &mstp_clks[MSTP101]), /* VPU */
+ CLKDEV_DEV_ID("sh_mobile_lcdc_fb.0", &mstp_clks[MSTP100]), /* LCDC0 */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.6", &mstp_clks[MSTP223]), /* SPU2DSP0 */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.7", &mstp_clks[MSTP223]), /* SPU2DSP1 */
+ CLKDEV_DEV_ID("sh-sci.5", &mstp_clks[MSTP207]), /* SCIFA5 */
+ CLKDEV_DEV_ID("sh-sci.6", &mstp_clks[MSTP206]), /* SCIFB */
+ CLKDEV_DEV_ID("sh-sci.0", &mstp_clks[MSTP204]), /* SCIFA0 */
+ CLKDEV_DEV_ID("sh-sci.1", &mstp_clks[MSTP203]), /* SCIFA1 */
+ CLKDEV_DEV_ID("sh-sci.2", &mstp_clks[MSTP202]), /* SCIFA2 */
+ CLKDEV_DEV_ID("sh-sci.3", &mstp_clks[MSTP201]), /* SCIFA3 */
+ CLKDEV_DEV_ID("sh-sci.4", &mstp_clks[MSTP200]), /* SCIFA4 */
+ CLKDEV_CON_ID("cmt1", &mstp_clks[MSTP329]), /* CMT10 */
+ CLKDEV_DEV_ID("sh_fsi2", &mstp_clks[MSTP328]), /* FSI2 */
+ CLKDEV_DEV_ID("i2c-sh_mobile.1", &mstp_clks[MSTP323]), /* IIC1 */
+ CLKDEV_DEV_ID("r8a66597_hcd.0", &mstp_clks[MSTP323]), /* USB0 */
+ CLKDEV_DEV_ID("r8a66597_udc.0", &mstp_clks[MSTP323]), /* USB0 */
+ CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[MSTP314]), /* SDHI0 */
+ CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[MSTP313]), /* SDHI1 */
+ CLKDEV_DEV_ID("sh_mmcif.0", &mstp_clks[MSTP312]), /* MMC */
+ CLKDEV_DEV_ID("sh_mobile_sdhi.2", &mstp_clks[MSTP415]), /* SDHI2 */
+ CLKDEV_DEV_ID("sh-mobile-hdmi", &mstp_clks[MSTP413]), /* HDMI */
+ CLKDEV_DEV_ID("i2c-sh_mobile.3", &mstp_clks[MSTP411]), /* IIC3 */
+ CLKDEV_DEV_ID("i2c-sh_mobile.4", &mstp_clks[MSTP410]), /* IIC4 */
+ CLKDEV_DEV_ID("r8a66597_hcd.1", &mstp_clks[MSTP406]), /* USB1 */
+ CLKDEV_DEV_ID("r8a66597_udc.1", &mstp_clks[MSTP406]), /* USB1 */
+ CLKDEV_DEV_ID("sh_keysc.0", &mstp_clks[MSTP403]), /* KEYSC */
+ {.con_id = "ick", .dev_id = "sh-mobile-hdmi", .clk = &div6_reparent_clks[DIV6_HDMI]},
+};
+
+void __init sh7372_clock_init(void)
+{
+ int k, ret = 0;
+
+ for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++)
+ ret = clk_register(main_clks[k]);
+
+ if (!ret)
+ ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table);
+
+ if (!ret)
+ ret = sh_clk_div6_register(div6_clks, DIV6_NR);
+
+ if (!ret)
+ ret = sh_clk_div6_reparent_register(div6_reparent_clks, DIV6_NR);
+
+ if (!ret)
+ ret = sh_clk_mstp32_register(mstp_clks, MSTP_NR);
+
+ clkdev_add_table(lookups, ARRAY_SIZE(lookups));
+
+ if (!ret)
+ clk_init();
+ else
+ panic("failed to setup sh7372 clocks\n");
+
+}
--- /dev/null
+/*
+ * SH7377 clock framework support
+ *
+ * Copyright (C) 2010 Magnus Damm
+ *
+ * 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
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/sh_clk.h>
+#include <mach/common.h>
+#include <asm/clkdev.h>
+
+/* SH7377 registers */
+#define RTFRQCR 0xe6150000
+#define SYFRQCR 0xe6150004
+#define CMFRQCR 0xe61500E0
+#define VCLKCR1 0xe6150008
+#define VCLKCR2 0xe615000C
+#define VCLKCR3 0xe615001C
+#define FMSICKCR 0xe6150010
+#define FMSOCKCR 0xe6150014
+#define FSICKCR 0xe6150018
+#define PLLC1CR 0xe6150028
+#define PLLC2CR 0xe615002C
+#define SUBUSBCKCR 0xe6150080
+#define SPUCKCR 0xe6150084
+#define MSUCKCR 0xe6150088
+#define MVI3CKCR 0xe6150090
+#define HDMICKCR 0xe6150094
+#define MFCK1CR 0xe6150098
+#define MFCK2CR 0xe615009C
+#define DSITCKCR 0xe6150060
+#define DSIPCKCR 0xe6150064
+#define SMSTPCR0 0xe6150130
+#define SMSTPCR1 0xe6150134
+#define SMSTPCR2 0xe6150138
+#define SMSTPCR3 0xe615013C
+#define SMSTPCR4 0xe6150140
+
+/* Fixed 32 KHz root clock from EXTALR pin */
+static struct clk r_clk = {
+ .rate = 32768,
+};
+
+/*
+ * 26MHz default rate for the EXTALC1 root input clock.
+ * If needed, reset this with clk_set_rate() from the platform code.
+ */
+struct clk sh7377_extalc1_clk = {
+ .rate = 26666666,
+};
+
+/*
+ * 48MHz default rate for the EXTAL2 root input clock.
+ * If needed, reset this with clk_set_rate() from the platform code.
+ */
+struct clk sh7377_extal2_clk = {
+ .rate = 48000000,
+};
+
+/* A fixed divide-by-2 block */
+static unsigned long div2_recalc(struct clk *clk)
+{
+ return clk->parent->rate / 2;
+}
+
+static struct clk_ops div2_clk_ops = {
+ .recalc = div2_recalc,
+};
+
+/* Divide extalc1 by two */
+static struct clk extalc1_div2_clk = {
+ .ops = &div2_clk_ops,
+ .parent = &sh7377_extalc1_clk,
+};
+
+/* Divide extal2 by two */
+static struct clk extal2_div2_clk = {
+ .ops = &div2_clk_ops,
+ .parent = &sh7377_extal2_clk,
+};
+
+/* Divide extal2 by four */
+static struct clk extal2_div4_clk = {
+ .ops = &div2_clk_ops,
+ .parent = &extal2_div2_clk,
+};
+
+/* PLLC1 */
+static unsigned long pllc1_recalc(struct clk *clk)
+{
+ unsigned long mult = 1;
+
+ if (__raw_readl(PLLC1CR) & (1 << 14))
+ mult = (((__raw_readl(RTFRQCR) >> 24) & 0x3f) + 1) * 2;
+
+ return clk->parent->rate * mult;
+}
+
+static struct clk_ops pllc1_clk_ops = {
+ .recalc = pllc1_recalc,
+};
+
+static struct clk pllc1_clk = {
+ .ops = &pllc1_clk_ops,
+ .flags = CLK_ENABLE_ON_INIT,
+ .parent = &extalc1_div2_clk,
+};
+
+/* Divide PLLC1 by two */
+static struct clk pllc1_div2_clk = {
+ .ops = &div2_clk_ops,
+ .parent = &pllc1_clk,
+};
+
+/* PLLC2 */
+static unsigned long pllc2_recalc(struct clk *clk)
+{
+ unsigned long mult = 1;
+
+ if (__raw_readl(PLLC2CR) & (1 << 31))
+ mult = (((__raw_readl(PLLC2CR) >> 24) & 0x3f) + 1) * 2;
+
+ return clk->parent->rate * mult;
+}
+
+static struct clk_ops pllc2_clk_ops = {
+ .recalc = pllc2_recalc,
+};
+
+static struct clk pllc2_clk = {
+ .ops = &pllc2_clk_ops,
+ .flags = CLK_ENABLE_ON_INIT,
+ .parent = &extalc1_div2_clk,
+};
+
+static struct clk *main_clks[] = {
+ &r_clk,
+ &sh7377_extalc1_clk,
+ &sh7377_extal2_clk,
+ &extalc1_div2_clk,
+ &extal2_div2_clk,
+ &extal2_div4_clk,
+ &pllc1_clk,
+ &pllc1_div2_clk,
+ &pllc2_clk,
+};
+
+static void div4_kick(struct clk *clk)
+{
+ unsigned long value;
+
+ /* set KICK bit in SYFRQCR to update hardware setting */
+ value = __raw_readl(SYFRQCR);
+ value |= (1 << 31);
+ __raw_writel(value, SYFRQCR);
+}
+
+static int divisors[] = { 2, 3, 4, 6, 8, 12, 16, 18,
+ 24, 32, 36, 48, 0, 72, 96, 0 };
+
+static struct clk_div_mult_table div4_div_mult_table = {
+ .divisors = divisors,
+ .nr_divisors = ARRAY_SIZE(divisors),
+};
+
+static struct clk_div4_table div4_table = {
+ .div_mult_table = &div4_div_mult_table,
+ .kick = div4_kick,
+};
+
+enum { DIV4_I, DIV4_ZG, DIV4_B, DIV4_M1, DIV4_CSIR,
+ DIV4_ZTR, DIV4_ZT, DIV4_Z, DIV4_HP,
+ DIV4_ZS, DIV4_ZB, DIV4_ZB3, DIV4_CP, DIV4_NR };
+
+#define DIV4(_reg, _bit, _mask, _flags) \
+ SH_CLK_DIV4(&pllc1_clk, _reg, _bit, _mask, _flags)
+
+static struct clk div4_clks[DIV4_NR] = {
+ [DIV4_I] = DIV4(RTFRQCR, 20, 0x6fff, CLK_ENABLE_ON_INIT),
+ [DIV4_ZG] = DIV4(RTFRQCR, 16, 0x6fff, CLK_ENABLE_ON_INIT),
+ [DIV4_B] = DIV4(RTFRQCR, 8, 0x6fff, CLK_ENABLE_ON_INIT),
+ [DIV4_M1] = DIV4(RTFRQCR, 4, 0x6fff, CLK_ENABLE_ON_INIT),
+ [DIV4_CSIR] = DIV4(RTFRQCR, 0, 0x6fff, 0),
+ [DIV4_ZTR] = DIV4(SYFRQCR, 20, 0x6fff, 0),
+ [DIV4_ZT] = DIV4(SYFRQCR, 16, 0x6fff, 0),
+ [DIV4_Z] = DIV4(SYFRQCR, 12, 0x6fff, 0),
+ [DIV4_HP] = DIV4(SYFRQCR, 4, 0x6fff, 0),
+ [DIV4_ZS] = DIV4(CMFRQCR, 12, 0x6fff, 0),
+ [DIV4_ZB] = DIV4(CMFRQCR, 8, 0x6fff, 0),
+ [DIV4_ZB3] = DIV4(CMFRQCR, 4, 0x6fff, 0),
+ [DIV4_CP] = DIV4(CMFRQCR, 0, 0x6fff, 0),
+};
+
+enum { DIV6_VCK1, DIV6_VCK2, DIV6_VCK3, DIV6_FMSI, DIV6_FMSO,
+ DIV6_FSI, DIV6_SUB, DIV6_SPU, DIV6_MSU, DIV6_MVI3, DIV6_HDMI,
+ DIV6_MF1, DIV6_MF2, DIV6_DSIT, DIV6_DSIP,
+ DIV6_NR };
+
+static struct clk div6_clks[] = {
+ [DIV6_VCK1] = SH_CLK_DIV6(&pllc1_div2_clk, VCLKCR1, 0),
+ [DIV6_VCK2] = SH_CLK_DIV6(&pllc1_div2_clk, VCLKCR2, 0),
+ [DIV6_VCK3] = SH_CLK_DIV6(&pllc1_div2_clk, VCLKCR3, 0),
+ [DIV6_FMSI] = SH_CLK_DIV6(&pllc1_div2_clk, FMSICKCR, 0),
+ [DIV6_FMSO] = SH_CLK_DIV6(&pllc1_div2_clk, FMSOCKCR, 0),
+ [DIV6_FSI] = SH_CLK_DIV6(&pllc1_div2_clk, FSICKCR, 0),
+ [DIV6_SUB] = SH_CLK_DIV6(&sh7377_extal2_clk, SUBUSBCKCR, 0),
+ [DIV6_SPU] = SH_CLK_DIV6(&pllc1_div2_clk, SPUCKCR, 0),
+ [DIV6_MSU] = SH_CLK_DIV6(&pllc1_div2_clk, MSUCKCR, 0),
+ [DIV6_MVI3] = SH_CLK_DIV6(&pllc1_div2_clk, MVI3CKCR, 0),
+ [DIV6_HDMI] = SH_CLK_DIV6(&pllc1_div2_clk, HDMICKCR, 0),
+ [DIV6_MF1] = SH_CLK_DIV6(&pllc1_div2_clk, MFCK1CR, 0),
+ [DIV6_MF2] = SH_CLK_DIV6(&pllc1_div2_clk, MFCK2CR, 0),
+ [DIV6_DSIT] = SH_CLK_DIV6(&pllc1_div2_clk, DSITCKCR, 0),
+ [DIV6_DSIP] = SH_CLK_DIV6(&pllc1_div2_clk, DSIPCKCR, 0),
+};
+
+enum { MSTP001,
+ MSTP131, MSTP130, MSTP129, MSTP128, MSTP116, MSTP106, MSTP101,
+ MSTP223, MSTP207, MSTP206, MSTP204, MSTP203, MSTP202, MSTP201, MSTP200,
+ MSTP331, MSTP329, MSTP325, MSTP323, MSTP322,
+ MSTP315, MSTP314, MSTP313,
+ MSTP403,
+ MSTP_NR };
+
+#define MSTP(_parent, _reg, _bit, _flags) \
+ SH_CLK_MSTP32(_parent, _reg, _bit, _flags)
+
+static struct clk mstp_clks[] = {
+ [MSTP001] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR0, 1, 0), /* IIC2 */
+ [MSTP131] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 31, 0), /* VEU3 */
+ [MSTP130] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 30, 0), /* VEU2 */
+ [MSTP129] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 29, 0), /* VEU1 */
+ [MSTP128] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 28, 0), /* VEU0 */
+ [MSTP116] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR1, 16, 0), /* IIC0 */
+ [MSTP106] = MSTP(&div4_clks[DIV4_B], SMSTPCR1, 6, 0), /* JPU */
+ [MSTP101] = MSTP(&div4_clks[DIV4_M1], SMSTPCR1, 1, 0), /* VPU */
+ [MSTP223] = MSTP(&div6_clks[DIV6_SPU], SMSTPCR2, 23, 0), /* SPU2 */
+ [MSTP207] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 7, 0), /* SCIFA5 */
+ [MSTP206] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 6, 0), /* SCIFB */
+ [MSTP204] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 4, 0), /* SCIFA0 */
+ [MSTP203] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 3, 0), /* SCIFA1 */
+ [MSTP202] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 2, 0), /* SCIFA2 */
+ [MSTP201] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 1, 0), /* SCIFA3 */
+ [MSTP200] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR2, 0, 0), /* SCIFA4 */
+ [MSTP331] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR3, 31, 0), /* SCIFA6 */
+ [MSTP329] = MSTP(&r_clk, SMSTPCR3, 29, 0), /* CMT10 */
+ [MSTP325] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR3, 25, 0), /* IRDA */
+ [MSTP323] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR3, 23, 0), /* IIC1 */
+ [MSTP322] = MSTP(&div6_clks[DIV6_SUB], SMSTPCR3, 22, 0), /* USB0 */
+ [MSTP315] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 15, 0), /* FLCTL */
+ [MSTP314] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 14, 0), /* SDHI0 */
+ [MSTP313] = MSTP(&div4_clks[DIV4_HP], SMSTPCR3, 13, 0), /* SDHI1 */
+ [MSTP403] = MSTP(&r_clk, SMSTPCR4, 3, 0), /* KEYSC */
+};
+
+#define CLKDEV_CON_ID(_id, _clk) { .con_id = _id, .clk = _clk }
+#define CLKDEV_DEV_ID(_id, _clk) { .dev_id = _id, .clk = _clk }
+
+static struct clk_lookup lookups[] = {
+ /* main clocks */
+ CLKDEV_CON_ID("r_clk", &r_clk),
+ CLKDEV_CON_ID("extalc1", &sh7377_extalc1_clk),
+ CLKDEV_CON_ID("extal2", &sh7377_extal2_clk),
+ CLKDEV_CON_ID("extalc1_div2_clk", &extalc1_div2_clk),
+ CLKDEV_CON_ID("extal2_div2_clk", &extal2_div2_clk),
+ CLKDEV_CON_ID("extal2_div4_clk", &extal2_div4_clk),
+ CLKDEV_CON_ID("pllc1_clk", &pllc1_clk),
+ CLKDEV_CON_ID("pllc1_div2_clk", &pllc1_div2_clk),
+ CLKDEV_CON_ID("pllc2_clk", &pllc2_clk),
+
+ /* DIV4 clocks */
+ CLKDEV_CON_ID("i_clk", &div4_clks[DIV4_I]),
+ CLKDEV_CON_ID("zg_clk", &div4_clks[DIV4_ZG]),
+ CLKDEV_CON_ID("b_clk", &div4_clks[DIV4_B]),
+ CLKDEV_CON_ID("m1_clk", &div4_clks[DIV4_M1]),
+ CLKDEV_CON_ID("csir_clk", &div4_clks[DIV4_CSIR]),
+ CLKDEV_CON_ID("ztr_clk", &div4_clks[DIV4_ZTR]),
+ CLKDEV_CON_ID("zt_clk", &div4_clks[DIV4_ZT]),
+ CLKDEV_CON_ID("z_clk", &div4_clks[DIV4_Z]),
+ CLKDEV_CON_ID("hp_clk", &div4_clks[DIV4_HP]),
+ CLKDEV_CON_ID("zs_clk", &div4_clks[DIV4_ZS]),
+ CLKDEV_CON_ID("zb_clk", &div4_clks[DIV4_ZB]),
+ CLKDEV_CON_ID("zb3_clk", &div4_clks[DIV4_ZB3]),
+ CLKDEV_CON_ID("cp_clk", &div4_clks[DIV4_CP]),
+
+ /* DIV6 clocks */
+ CLKDEV_CON_ID("vck1_clk", &div6_clks[DIV6_VCK1]),
+ CLKDEV_CON_ID("vck2_clk", &div6_clks[DIV6_VCK2]),
+ CLKDEV_CON_ID("vck3_clk", &div6_clks[DIV6_VCK3]),
+ CLKDEV_CON_ID("fmsi_clk", &div6_clks[DIV6_FMSI]),
+ CLKDEV_CON_ID("fmso_clk", &div6_clks[DIV6_FMSO]),
+ CLKDEV_CON_ID("fsi_clk", &div6_clks[DIV6_FSI]),
+ CLKDEV_CON_ID("sub_clk", &div6_clks[DIV6_SUB]),
+ CLKDEV_CON_ID("spu_clk", &div6_clks[DIV6_SPU]),
+ CLKDEV_CON_ID("msu_clk", &div6_clks[DIV6_MSU]),
+ CLKDEV_CON_ID("mvi3_clk", &div6_clks[DIV6_MVI3]),
+ CLKDEV_CON_ID("hdmi_clk", &div6_clks[DIV6_HDMI]),
+ CLKDEV_CON_ID("mf1_clk", &div6_clks[DIV6_MF1]),
+ CLKDEV_CON_ID("mf2_clk", &div6_clks[DIV6_MF2]),
+ CLKDEV_CON_ID("dsit_clk", &div6_clks[DIV6_DSIT]),
+ CLKDEV_CON_ID("dsip_clk", &div6_clks[DIV6_DSIP]),
+
+ /* MSTP32 clocks */
+ CLKDEV_DEV_ID("i2c-sh_mobile.2", &mstp_clks[MSTP001]), /* IIC2 */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.4", &mstp_clks[MSTP131]), /* VEU3 */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.3", &mstp_clks[MSTP130]), /* VEU2 */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.2", &mstp_clks[MSTP129]), /* VEU1 */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.1", &mstp_clks[MSTP128]), /* VEU0 */
+ CLKDEV_DEV_ID("i2c-sh_mobile.0", &mstp_clks[MSTP116]), /* IIC0 */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.5", &mstp_clks[MSTP106]), /* JPU */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.0", &mstp_clks[MSTP101]), /* VPU */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.6", &mstp_clks[MSTP223]), /* SPU2DSP0 */
+ CLKDEV_DEV_ID("uio_pdrv_genirq.7", &mstp_clks[MSTP223]), /* SPU2DSP1 */
+ CLKDEV_DEV_ID("sh-sci.5", &mstp_clks[MSTP207]), /* SCIFA5 */
+ CLKDEV_DEV_ID("sh-sci.7", &mstp_clks[MSTP206]), /* SCIFB */
+ CLKDEV_DEV_ID("sh-sci.0", &mstp_clks[MSTP204]), /* SCIFA0 */
+ CLKDEV_DEV_ID("sh-sci.1", &mstp_clks[MSTP203]), /* SCIFA1 */
+ CLKDEV_DEV_ID("sh-sci.2", &mstp_clks[MSTP202]), /* SCIFA2 */
+ CLKDEV_DEV_ID("sh-sci.3", &mstp_clks[MSTP201]), /* SCIFA3 */
+ CLKDEV_DEV_ID("sh-sci.4", &mstp_clks[MSTP200]), /* SCIFA4 */
+ CLKDEV_DEV_ID("sh-sci.6", &mstp_clks[MSTP331]), /* SCIFA6 */
+ CLKDEV_CON_ID("cmt1", &mstp_clks[MSTP329]), /* CMT10 */
+ CLKDEV_DEV_ID("sh_irda", &mstp_clks[MSTP325]), /* IRDA */
+ CLKDEV_DEV_ID("i2c-sh_mobile.1", &mstp_clks[MSTP323]), /* IIC1 */
+ CLKDEV_DEV_ID("r8a66597_hcd.0", &mstp_clks[MSTP322]), /* USBHS */
+ CLKDEV_DEV_ID("r8a66597_udc.0", &mstp_clks[MSTP322]), /* USBHS */
+ CLKDEV_DEV_ID("sh_flctl", &mstp_clks[MSTP315]), /* FLCTL */
+ CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[MSTP314]), /* SDHI0 */
+ CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[MSTP313]), /* SDHI1 */
+ CLKDEV_DEV_ID("sh_keysc.0", &mstp_clks[MSTP403]), /* KEYSC */
+};
+
+void __init sh7377_clock_init(void)
+{
+ int k, ret = 0;
+
+ for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++)
+ ret = clk_register(main_clks[k]);
+
+ if (!ret)
+ ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table);
+
+ if (!ret)
+ ret = sh_clk_div6_register(div6_clks, DIV6_NR);
+
+ if (!ret)
+ ret = sh_clk_mstp32_register(mstp_clks, MSTP_NR);
+
+ clkdev_add_table(lookups, ARRAY_SIZE(lookups));
+
+ if (!ret)
+ clk_init();
+ else
+ panic("failed to setup sh7377 clocks\n");
+}
--- /dev/null
+/*
+ * SH-Mobile Timer
+ *
+ * Copyright (C) 2010 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sh_clk.h>
+
+int __init clk_init(void)
+{
+ /* Kick the child clocks.. */
+ recalculate_root_clocks();
+
+ /* Enable the necessary init clocks */
+ clk_enable_init_clocks();
+
+ return 0;
+}
+
+int __clk_get(struct clk *clk)
+{
+ return 1;
+}
+EXPORT_SYMBOL(__clk_get);
+
+void __clk_put(struct clk *clk)
+{
+}
+EXPORT_SYMBOL(__clk_put);
extern struct sys_timer shmobile_timer;
extern void shmobile_setup_console(void);
+struct clk;
+extern int clk_init(void);
extern void sh7367_init_irq(void);
extern void sh7367_add_early_devices(void);
extern void sh7367_add_standard_devices(void);
extern void sh7367_clock_init(void);
extern void sh7367_pinmux_init(void);
+extern struct clk sh7367_extalb1_clk;
+extern struct clk sh7367_extal2_clk;
extern void sh7377_init_irq(void);
extern void sh7377_add_early_devices(void);
extern void sh7377_add_standard_devices(void);
+extern void sh7377_clock_init(void);
extern void sh7377_pinmux_init(void);
+extern struct clk sh7377_extalc1_clk;
+extern struct clk sh7377_extal2_clk;
extern void sh7372_init_irq(void);
extern void sh7372_add_early_devices(void);
extern void sh7372_add_standard_devices(void);
+extern void sh7372_clock_init(void);
extern void sh7372_pinmux_init(void);
+extern struct clk sh7372_extal1_clk;
+extern struct clk sh7372_extal2_clk;
#endif /* __ARCH_MACH_COMMON_H */
#define NR_IRQS 512
+/* INTCA */
#define evt2irq(evt) (((evt) >> 5) - 16)
#define irq2evt(irq) (((irq) + 16) << 5)
+/* INTCS */
+#define INTCS_VECT_BASE 0x2200
+#define INTCS_VECT(n, vect) INTC_VECT((n), INTCS_VECT_BASE + (vect))
+#define intcs_evt2irq(evt) evt2irq(INTCS_VECT_BASE + (evt))
+
#endif /* __ASM_MACH_IRQS_H */
#define PHYS_OFFSET UL(CONFIG_MEMORY_START)
#define MEM_SIZE UL(CONFIG_MEMORY_SIZE)
+/* DMA memory at 0xf6000000 - 0xffdfffff */
+#define CONSISTENT_DMA_SIZE (158 << 20)
+
#endif /* __ASM_MACH_MEMORY_H */
#ifndef __ASM_SH7372_H__
#define __ASM_SH7372_H__
+#include <linux/sh_clk.h>
+
/*
* Pin Function Controller:
* GPIO_FN_xx - GPIO used to select pin function
GPIO_FN_SDENC_DV_CLKI,
};
+/* DMA slave IDs */
+enum {
+ SHDMA_SLAVE_SCIF0_TX,
+ SHDMA_SLAVE_SCIF0_RX,
+ SHDMA_SLAVE_SCIF1_TX,
+ SHDMA_SLAVE_SCIF1_RX,
+ SHDMA_SLAVE_SCIF2_TX,
+ SHDMA_SLAVE_SCIF2_RX,
+ SHDMA_SLAVE_SCIF3_TX,
+ SHDMA_SLAVE_SCIF3_RX,
+ SHDMA_SLAVE_SCIF4_TX,
+ SHDMA_SLAVE_SCIF4_RX,
+ SHDMA_SLAVE_SCIF5_TX,
+ SHDMA_SLAVE_SCIF5_RX,
+ SHDMA_SLAVE_SCIF6_TX,
+ SHDMA_SLAVE_SCIF6_RX,
+ SHDMA_SLAVE_SDHI0_RX,
+ SHDMA_SLAVE_SDHI0_TX,
+ SHDMA_SLAVE_SDHI1_RX,
+ SHDMA_SLAVE_SDHI1_TX,
+ SHDMA_SLAVE_SDHI2_RX,
+ SHDMA_SLAVE_SDHI2_TX,
+};
+
+extern struct clk dv_clki_clk;
+extern struct clk dv_clki_div2_clk;
+extern struct clk pllc2_clk;
+
#endif /* __ASM_SH7372_H__ */
#ifndef __ASM_MACH_VMALLOC_H
#define __ASM_MACH_VMALLOC_H
-#define VMALLOC_END (PAGE_OFFSET + 0x24000000)
+/* Vmalloc at ... - 0xe5ffffff */
+#define VMALLOC_END 0xe6000000
#endif /* __ASM_MACH_VMALLOC_H */
ETM11, ARM11, USBHS, FLCTL, IIC1
};
-static struct intc_vect intca_vectors[] = {
+static struct intc_vect intca_vectors[] __initdata = {
INTC_VECT(IRQ0A, 0x0200), INTC_VECT(IRQ1A, 0x0220),
INTC_VECT(IRQ2A, 0x0240), INTC_VECT(IRQ3A, 0x0260),
INTC_VECT(IRQ4A, 0x0280), INTC_VECT(IRQ5A, 0x02a0),
INTC_GROUP(IIC1, IIC1_ALI1, IIC1_TACKI1, IIC1_WAITI1, IIC1_DTEI1),
};
-static struct intc_mask_reg intca_mask_registers[] = {
+static struct intc_mask_reg intca_mask_registers[] __initdata = {
{ 0xe6900040, 0xe6900060, 8, /* INTMSK00A / INTMSKCLR00A */
{ IRQ0A, IRQ1A, IRQ2A, IRQ3A, IRQ4A, IRQ5A, IRQ6A, IRQ7A } },
{ 0xe6900044, 0xe6900064, 8, /* INTMSK10A / INTMSKCLR10A */
MISTY, CMT3, RWDT1, RWDT0 } },
};
-static struct intc_prio_reg intca_prio_registers[] = {
+static struct intc_prio_reg intca_prio_registers[] __initdata = {
{ 0xe6900010, 0, 32, 4, /* INTPRI00A */
{ IRQ0A, IRQ1A, IRQ2A, IRQ3A, IRQ4A, IRQ5A, IRQ6A, IRQ7A } },
{ 0xe6900014, 0, 32, 4, /* INTPRI10A */
intca_sense_registers, intca_ack_registers),
};
+enum {
+ UNUSED_INTCS = 0,
+
+ INTCS,
+
+ /* interrupt sources INTCS */
+ VIO2_VEU0, VIO2_VEU1, VIO2_VEU2, VIO2_VEU3,
+ VIO3_VOU,
+ RTDMAC_1_DEI0, RTDMAC_1_DEI1, RTDMAC_1_DEI2, RTDMAC_1_DEI3,
+ VIO1_CEU, VIO1_BEU0, VIO1_BEU1, VIO1_BEU2,
+ VPU,
+ SGX530,
+ _2DDMAC_2DDM0, _2DDMAC_2DDM1, _2DDMAC_2DDM2, _2DDMAC_2DDM3,
+ IIC2_ALI2, IIC2_TACKI2, IIC2_WAITI2, IIC2_DTEI2,
+ IPMMU_IPMMUB, IPMMU_IPMMUS,
+ RTDMAC_2_DEI4, RTDMAC_2_DEI5, RTDMAC_2_DADERR,
+ MSIOF,
+ IIC0_ALI0, IIC0_TACKI0, IIC0_WAITI0, IIC0_DTEI0,
+ TMU_TUNI0, TMU_TUNI1, TMU_TUNI2,
+ CMT,
+ TSIF,
+ IPMMUI,
+ MVI3,
+ ICB,
+ PEP,
+ ASA,
+ BEM,
+ VE2HO,
+ HQE,
+ JPEG,
+ LCDC,
+
+ /* interrupt groups INTCS */
+ _2DDMAC, RTDMAC_1, RTDMAC_2, VEU, BEU, IIC0, IPMMU, IIC2,
+};
+
+static struct intc_vect intcs_vectors[] = {
+ INTCS_VECT(VIO2_VEU0, 0x700), INTCS_VECT(VIO2_VEU1, 0x720),
+ INTCS_VECT(VIO2_VEU2, 0x740), INTCS_VECT(VIO2_VEU3, 0x760),
+ INTCS_VECT(VIO3_VOU, 0x780),
+ INTCS_VECT(RTDMAC_1_DEI0, 0x800), INTCS_VECT(RTDMAC_1_DEI1, 0x820),
+ INTCS_VECT(RTDMAC_1_DEI2, 0x840), INTCS_VECT(RTDMAC_1_DEI3, 0x860),
+ INTCS_VECT(VIO1_CEU, 0x880), INTCS_VECT(VIO1_BEU0, 0x8a0),
+ INTCS_VECT(VIO1_BEU1, 0x8c0), INTCS_VECT(VIO1_BEU2, 0x8e0),
+ INTCS_VECT(VPU, 0x980),
+ INTCS_VECT(SGX530, 0x9e0),
+ INTCS_VECT(_2DDMAC_2DDM0, 0xa00), INTCS_VECT(_2DDMAC_2DDM1, 0xa20),
+ INTCS_VECT(_2DDMAC_2DDM2, 0xa40), INTCS_VECT(_2DDMAC_2DDM3, 0xa60),
+ INTCS_VECT(IIC2_ALI2, 0xa80), INTCS_VECT(IIC2_TACKI2, 0xaa0),
+ INTCS_VECT(IIC2_WAITI2, 0xac0), INTCS_VECT(IIC2_DTEI2, 0xae0),
+ INTCS_VECT(IPMMU_IPMMUB, 0xb20), INTCS_VECT(IPMMU_IPMMUS, 0xb60),
+ INTCS_VECT(RTDMAC_2_DEI4, 0xb80), INTCS_VECT(RTDMAC_2_DEI5, 0xba0),
+ INTCS_VECT(RTDMAC_2_DADERR, 0xbc0),
+ INTCS_VECT(MSIOF, 0xd20),
+ INTCS_VECT(IIC0_ALI0, 0xe00), INTCS_VECT(IIC0_TACKI0, 0xe20),
+ INTCS_VECT(IIC0_WAITI0, 0xe40), INTCS_VECT(IIC0_DTEI0, 0xe60),
+ INTCS_VECT(TMU_TUNI0, 0xe80), INTCS_VECT(TMU_TUNI1, 0xea0),
+ INTCS_VECT(TMU_TUNI2, 0xec0),
+ INTCS_VECT(CMT, 0xf00),
+ INTCS_VECT(TSIF, 0xf20),
+ INTCS_VECT(IPMMUI, 0xf60),
+ INTCS_VECT(MVI3, 0x420),
+ INTCS_VECT(ICB, 0x480),
+ INTCS_VECT(PEP, 0x4a0),
+ INTCS_VECT(ASA, 0x4c0),
+ INTCS_VECT(BEM, 0x4e0),
+ INTCS_VECT(VE2HO, 0x520),
+ INTCS_VECT(HQE, 0x540),
+ INTCS_VECT(JPEG, 0x560),
+ INTCS_VECT(LCDC, 0x580),
+
+ INTC_VECT(INTCS, 0xf80),
+};
+
+static struct intc_group intcs_groups[] __initdata = {
+ INTC_GROUP(_2DDMAC, _2DDMAC_2DDM0, _2DDMAC_2DDM1,
+ _2DDMAC_2DDM2, _2DDMAC_2DDM3),
+ INTC_GROUP(RTDMAC_1, RTDMAC_1_DEI0, RTDMAC_1_DEI1,
+ RTDMAC_1_DEI2, RTDMAC_1_DEI3),
+ INTC_GROUP(RTDMAC_2, RTDMAC_2_DEI4, RTDMAC_2_DEI5, RTDMAC_2_DADERR),
+ INTC_GROUP(VEU, VIO2_VEU0, VIO2_VEU1, VIO2_VEU2, VIO2_VEU3),
+ INTC_GROUP(BEU, VIO1_BEU0, VIO1_BEU1, VIO1_BEU2),
+ INTC_GROUP(IIC0, IIC0_ALI0, IIC0_TACKI0, IIC0_WAITI0, IIC0_DTEI0),
+ INTC_GROUP(IPMMU, IPMMU_IPMMUS, IPMMU_IPMMUB),
+ INTC_GROUP(IIC2, IIC2_ALI2, IIC2_TACKI2, IIC2_WAITI2, IIC2_DTEI2),
+};
+
+static struct intc_mask_reg intcs_mask_registers[] = {
+ { 0xffd20184, 0xffd201c4, 8, /* IMR1SA / IMCR1SA */
+ { VIO1_BEU2, VIO1_BEU1, VIO1_BEU0, VIO1_CEU,
+ VIO2_VEU3, VIO2_VEU2, VIO2_VEU1, VIO2_VEU0 } },
+ { 0xffd20188, 0xffd201c8, 8, /* IMR2SA / IMCR2SA */
+ { VIO3_VOU, 0, VE2HO, VPU,
+ 0, 0, 0, 0 } },
+ { 0xffd2018c, 0xffd201cc, 8, /* IMR3SA / IMCR3SA */
+ { _2DDMAC_2DDM3, _2DDMAC_2DDM2, _2DDMAC_2DDM1, _2DDMAC_2DDM0,
+ BEM, ASA, PEP, ICB } },
+ { 0xffd20190, 0xffd201d0, 8, /* IMR4SA / IMCR4SA */
+ { 0, 0, MVI3, 0,
+ JPEG, HQE, 0, LCDC } },
+ { 0xffd20194, 0xffd201d4, 8, /* IMR5SA / IMCR5SA */
+ { 0, RTDMAC_2_DADERR, RTDMAC_2_DEI5, RTDMAC_2_DEI4,
+ RTDMAC_1_DEI3, RTDMAC_1_DEI2, RTDMAC_1_DEI1, RTDMAC_1_DEI0 } },
+ { 0xffd20198, 0xffd201d8, 8, /* IMR6SA / IMCR6SA */
+ { 0, 0, MSIOF, 0,
+ SGX530, 0, 0, 0 } },
+ { 0xffd2019c, 0xffd201dc, 8, /* IMR7SA / IMCR7SA */
+ { 0, TMU_TUNI2, TMU_TUNI1, TMU_TUNI0,
+ 0, 0, 0, 0 } },
+ { 0xffd201a4, 0xffd201e4, 8, /* IMR9SA / IMCR9SA */
+ { 0, 0, 0, CMT,
+ IIC2_DTEI2, IIC2_WAITI2, IIC2_TACKI2, IIC2_ALI2 } },
+ { 0xffd201a8, 0xffd201e8, 8, /* IMR10SA / IMCR10SA */
+ { IPMMU_IPMMUS, 0, IPMMU_IPMMUB, 0,
+ 0, 0, 0, 0 } },
+ { 0xffd201ac, 0xffd201ec, 8, /* IMR11SA / IMCR11SA */
+ { IIC0_DTEI0, IIC0_WAITI0, IIC0_TACKI0, IIC0_ALI0,
+ 0, 0, IPMMUI, TSIF } },
+ { 0xffd20104, 0, 16, /* INTAMASK */
+ { 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, INTCS } },
+};
+
+/* Priority is needed for INTCA to receive the INTCS interrupt */
+static struct intc_prio_reg intcs_prio_registers[] = {
+ { 0xffd20000, 0, 16, 4, /* IPRAS */ { 0, MVI3, _2DDMAC, ICB } },
+ { 0xffd20004, 0, 16, 4, /* IPRBS */ { JPEG, LCDC, 0, 0 } },
+ { 0xffd20008, 0, 16, 4, /* IPRCS */ { BBIF2, 0, 0, 0 } },
+ { 0xffd20010, 0, 16, 4, /* IPRES */ { RTDMAC_1, VIO1_CEU, 0, VPU } },
+ { 0xffd20014, 0, 16, 4, /* IPRFS */ { 0, RTDMAC_2, 0, CMT } },
+ { 0xffd20018, 0, 16, 4, /* IPRGS */ { TMU_TUNI0, TMU_TUNI1,
+ TMU_TUNI2, 0 } },
+ { 0xffd2001c, 0, 16, 4, /* IPRHS */ { 0, VIO3_VOU, VEU, BEU } },
+ { 0xffd20020, 0, 16, 4, /* IPRIS */ { 0, MSIOF, TSIF, IIC0 } },
+ { 0xffd20024, 0, 16, 4, /* IPRJS */ { 0, SGX530, 0, 0 } },
+ { 0xffd20028, 0, 16, 4, /* IPRKS */ { BEM, ASA, IPMMUI, PEP } },
+ { 0xffd2002c, 0, 16, 4, /* IPRLS */ { IPMMU, 0, VE2HO, HQE } },
+ { 0xffd20030, 0, 16, 4, /* IPRMS */ { IIC2, 0, 0, 0 } },
+};
+
+static struct resource intcs_resources[] __initdata = {
+ [0] = {
+ .start = 0xffd20000,
+ .end = 0xffd2ffff,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct intc_desc intcs_desc __initdata = {
+ .name = "sh7367-intcs",
+ .resource = intcs_resources,
+ .num_resources = ARRAY_SIZE(intcs_resources),
+ .hw = INTC_HW_DESC(intcs_vectors, intcs_groups, intcs_mask_registers,
+ intcs_prio_registers, NULL, NULL),
+};
+
+static void intcs_demux(unsigned int irq, struct irq_desc *desc)
+{
+ void __iomem *reg = (void *)get_irq_data(irq);
+ unsigned int evtcodeas = ioread32(reg);
+
+ generic_handle_irq(intcs_evt2irq(evtcodeas));
+}
+
void __init sh7367_init_irq(void)
{
- /* INTCA */
+ void __iomem *intevtsa = ioremap_nocache(0xffd20100, PAGE_SIZE);
+
register_intc_controller(&intca_desc);
+ register_intc_controller(&intcs_desc);
+
+ /* demux using INTEVTSA */
+ set_irq_data(evt2irq(0xf80), (void *)intevtsa);
+ set_irq_chained_handler(evt2irq(0xf80), intcs_demux);
}
{ 0xe6950034, 0, 16, 4, /* IPRNA3 */ { AP_ARM2, 0, 0, 0 } },
{ 0xe6950038, 0, 16, 4, /* IPROA3 */ { MFIS2, CPORTR2S,
CMT14, CMT15 } },
- { 0xe694003c, 0, 16, 4, /* IPRPA3 */ { 0, 0,
+ { 0xe695003c, 0, 16, 4, /* IPRPA3 */ { 0, 0,
MMC_MMC_ERR, MMC_MMC_NOR } },
- { 0xe6940040, 0, 16, 4, /* IPRQA3 */ { IIC4_ALI4, IIC4_TACKI4,
+ { 0xe6950040, 0, 16, 4, /* IPRQA3 */ { IIC4_ALI4, IIC4_TACKI4,
IIC4_WAITI4, IIC4_DTEI4 } },
- { 0xe6940044, 0, 16, 4, /* IPRRA3 */ { IIC3_ALI3, IIC3_TACKI3,
+ { 0xe6950044, 0, 16, 4, /* IPRRA3 */ { IIC3_ALI3, IIC3_TACKI3,
IIC3_WAITI3, IIC3_DTEI3 } },
- { 0xe6940048, 0, 16, 4, /* IPRSA3 */ { 0/*ERI*/, 0/*RXI*/,
+ { 0xe6950048, 0, 16, 4, /* IPRSA3 */ { 0/*ERI*/, 0/*RXI*/,
0/*TXI*/, 0/*TEI*/} },
- { 0xe694004c, 0, 16, 4, /* IPRTA3 */ { USB0_USB0I1, USB0_USB0I0,
+ { 0xe695004c, 0, 16, 4, /* IPRTA3 */ { USB0_USB0I1, USB0_USB0I0,
USB1_USB1I1, USB1_USB1I0 } },
- { 0xe6940050, 0, 16, 4, /* IPRUA3 */ { USBHSDMAC1_USHDMI, 0, 0, 0 } },
+ { 0xe6950050, 0, 16, 4, /* IPRUA3 */ { USBHSDMAC1_USHDMI, 0, 0, 0 } },
};
static struct intc_sense_reg intca_sense_registers[] __initdata = {
intca_sense_registers, intca_ack_registers),
};
+enum {
+ UNUSED_INTCS = 0,
+
+ INTCS,
+
+ /* interrupt sources INTCS */
+ VEU_VEU0, VEU_VEU1, VEU_VEU2, VEU_VEU3,
+ RTDMAC_1_DEI0, RTDMAC_1_DEI1, RTDMAC_1_DEI2, RTDMAC_1_DEI3,
+ CEU, BEU_BEU0, BEU_BEU1, BEU_BEU2,
+ VPU,
+ TSIF1,
+ _3DG_SGX530,
+ _2DDMAC,
+ IIC2_ALI2, IIC2_TACKI2, IIC2_WAITI2, IIC2_DTEI2,
+ IPMMU_IPMMUR, IPMMU_IPMMUR2,
+ RTDMAC_2_DEI4, RTDMAC_2_DEI5, RTDMAC_2_DADERR,
+ MSIOF,
+ IIC0_ALI0, IIC0_TACKI0, IIC0_WAITI0, IIC0_DTEI0,
+ TMU_TUNI0, TMU_TUNI1, TMU_TUNI2,
+ CMT0,
+ TSIF0,
+ LMB,
+ CTI,
+ ICB,
+ JPU_JPEG,
+ LCDC,
+ LCRC,
+ RTDMAC2_1_DEI0, RTDMAC2_1_DEI1, RTDMAC2_1_DEI2, RTDMAC2_1_DEI3,
+ RTDMAC2_2_DEI4, RTDMAC2_2_DEI5, RTDMAC2_2_DADERR,
+ ISP,
+ LCDC1,
+ CSIRX,
+ DSITX_DSITX0,
+ DSITX_DSITX1,
+ TMU1_TUNI0, TMU1_TUNI1, TMU1_TUNI2,
+ CMT4,
+ DSITX1_DSITX1_0,
+ DSITX1_DSITX1_1,
+ CPORTS2R,
+ JPU6E,
+
+ /* interrupt groups INTCS */
+ RTDMAC_1, RTDMAC_2, VEU, BEU, IIC0, IPMMU, IIC2,
+ RTDMAC2_1, RTDMAC2_2, TMU1, DSITX,
+};
+
+static struct intc_vect intcs_vectors[] = {
+ INTCS_VECT(VEU_VEU0, 0x700), INTCS_VECT(VEU_VEU1, 0x720),
+ INTCS_VECT(VEU_VEU2, 0x740), INTCS_VECT(VEU_VEU3, 0x760),
+ INTCS_VECT(RTDMAC_1_DEI0, 0x800), INTCS_VECT(RTDMAC_1_DEI1, 0x820),
+ INTCS_VECT(RTDMAC_1_DEI2, 0x840), INTCS_VECT(RTDMAC_1_DEI3, 0x860),
+ INTCS_VECT(CEU, 0x880), INTCS_VECT(BEU_BEU0, 0x8a0),
+ INTCS_VECT(BEU_BEU1, 0x8c0), INTCS_VECT(BEU_BEU2, 0x8e0),
+ INTCS_VECT(VPU, 0x980),
+ INTCS_VECT(TSIF1, 0x9a0),
+ INTCS_VECT(_3DG_SGX530, 0x9e0),
+ INTCS_VECT(_2DDMAC, 0xa00),
+ INTCS_VECT(IIC2_ALI2, 0xa80), INTCS_VECT(IIC2_TACKI2, 0xaa0),
+ INTCS_VECT(IIC2_WAITI2, 0xac0), INTCS_VECT(IIC2_DTEI2, 0xae0),
+ INTCS_VECT(IPMMU_IPMMUR, 0xb00), INTCS_VECT(IPMMU_IPMMUR2, 0xb20),
+ INTCS_VECT(RTDMAC_2_DEI4, 0xb80), INTCS_VECT(RTDMAC_2_DEI5, 0xba0),
+ INTCS_VECT(RTDMAC_2_DADERR, 0xbc0),
+ INTCS_VECT(IIC0_ALI0, 0xe00), INTCS_VECT(IIC0_TACKI0, 0xe20),
+ INTCS_VECT(IIC0_WAITI0, 0xe40), INTCS_VECT(IIC0_DTEI0, 0xe60),
+ INTCS_VECT(TMU_TUNI0, 0xe80), INTCS_VECT(TMU_TUNI1, 0xea0),
+ INTCS_VECT(TMU_TUNI2, 0xec0),
+ INTCS_VECT(CMT0, 0xf00),
+ INTCS_VECT(TSIF0, 0xf20),
+ INTCS_VECT(LMB, 0xf60),
+ INTCS_VECT(CTI, 0x400),
+ INTCS_VECT(ICB, 0x480),
+ INTCS_VECT(JPU_JPEG, 0x560),
+ INTCS_VECT(LCDC, 0x580),
+ INTCS_VECT(LCRC, 0x5a0),
+ INTCS_VECT(RTDMAC2_1_DEI0, 0x1300), INTCS_VECT(RTDMAC2_1_DEI1, 0x1320),
+ INTCS_VECT(RTDMAC2_1_DEI2, 0x1340), INTCS_VECT(RTDMAC2_1_DEI3, 0x1360),
+ INTCS_VECT(RTDMAC2_2_DEI4, 0x1380), INTCS_VECT(RTDMAC2_2_DEI5, 0x13a0),
+ INTCS_VECT(RTDMAC2_2_DADERR, 0x13c0),
+ INTCS_VECT(ISP, 0x1720),
+ INTCS_VECT(LCDC1, 0x1780),
+ INTCS_VECT(CSIRX, 0x17a0),
+ INTCS_VECT(DSITX_DSITX0, 0x17c0),
+ INTCS_VECT(DSITX_DSITX1, 0x17e0),
+ INTCS_VECT(TMU1_TUNI0, 0x1900), INTCS_VECT(TMU1_TUNI1, 0x1920),
+ INTCS_VECT(TMU1_TUNI2, 0x1940),
+ INTCS_VECT(CMT4, 0x1980),
+ INTCS_VECT(DSITX1_DSITX1_0, 0x19a0),
+ INTCS_VECT(DSITX1_DSITX1_1, 0x19c0),
+ INTCS_VECT(CPORTS2R, 0x1a20),
+ INTCS_VECT(JPU6E, 0x1a80),
+
+ INTC_VECT(INTCS, 0xf80),
+};
+
+static struct intc_group intcs_groups[] __initdata = {
+ INTC_GROUP(RTDMAC_1, RTDMAC_1_DEI0, RTDMAC_1_DEI1,
+ RTDMAC_1_DEI2, RTDMAC_1_DEI3),
+ INTC_GROUP(RTDMAC_2, RTDMAC_2_DEI4, RTDMAC_2_DEI5, RTDMAC_2_DADERR),
+ INTC_GROUP(VEU, VEU_VEU0, VEU_VEU1, VEU_VEU2, VEU_VEU3),
+ INTC_GROUP(BEU, BEU_BEU0, BEU_BEU1, BEU_BEU2),
+ INTC_GROUP(IIC0, IIC0_ALI0, IIC0_TACKI0, IIC0_WAITI0, IIC0_DTEI0),
+ INTC_GROUP(IPMMU, IPMMU_IPMMUR, IPMMU_IPMMUR2),
+ INTC_GROUP(IIC2, IIC2_ALI2, IIC2_TACKI2, IIC2_WAITI2, IIC2_DTEI2),
+ INTC_GROUP(RTDMAC2_1, RTDMAC2_1_DEI0, RTDMAC2_1_DEI1,
+ RTDMAC2_1_DEI2, RTDMAC2_1_DEI3),
+ INTC_GROUP(RTDMAC2_2, RTDMAC2_2_DEI4,
+ RTDMAC2_2_DEI5, RTDMAC2_2_DADERR),
+ INTC_GROUP(TMU1, TMU1_TUNI2, TMU1_TUNI1, TMU1_TUNI0),
+ INTC_GROUP(DSITX, DSITX_DSITX0, DSITX_DSITX1),
+};
+
+static struct intc_mask_reg intcs_mask_registers[] = {
+ { 0xffd20184, 0xffd201c4, 8, /* IMR1SA / IMCR1SA */
+ { BEU_BEU2, BEU_BEU1, BEU_BEU0, CEU,
+ VEU_VEU3, VEU_VEU2, VEU_VEU1, VEU_VEU0 } },
+ { 0xffd20188, 0xffd201c8, 8, /* IMR2SA / IMCR2SA */
+ { 0, 0, 0, VPU,
+ 0, 0, 0, 0 } },
+ { 0xffd2018c, 0xffd201cc, 8, /* IMR3SA / IMCR3SA */
+ { 0, 0, 0, _2DDMAC,
+ 0, 0, 0, ICB } },
+ { 0xffd20190, 0xffd201d0, 8, /* IMR4SA / IMCR4SA */
+ { 0, 0, 0, CTI,
+ JPU_JPEG, 0, LCRC, LCDC } },
+ { 0xffd20194, 0xffd201d4, 8, /* IMR5SA / IMCR5SA */
+ { 0, RTDMAC_2_DADERR, RTDMAC_2_DEI5, RTDMAC_2_DEI4,
+ RTDMAC_1_DEI3, RTDMAC_1_DEI2, RTDMAC_1_DEI1, RTDMAC_1_DEI0 } },
+ { 0xffd20198, 0xffd201d8, 8, /* IMR6SA / IMCR6SA */
+ { 0, 0, MSIOF, 0,
+ _3DG_SGX530, 0, 0, 0 } },
+ { 0xffd2019c, 0xffd201dc, 8, /* IMR7SA / IMCR7SA */
+ { 0, TMU_TUNI2, TMU_TUNI1, TMU_TUNI0,
+ 0, 0, 0, 0 } },
+ { 0xffd201a4, 0xffd201e4, 8, /* IMR9SA / IMCR9SA */
+ { 0, 0, 0, CMT0,
+ IIC2_DTEI2, IIC2_WAITI2, IIC2_TACKI2, IIC2_ALI2 } },
+ { 0xffd201a8, 0xffd201e8, 8, /* IMR10SA / IMCR10SA */
+ { 0, 0, IPMMU_IPMMUR2, IPMMU_IPMMUR,
+ 0, 0, 0, 0 } },
+ { 0xffd201ac, 0xffd201ec, 8, /* IMR11SA / IMCR11SA */
+ { IIC0_DTEI0, IIC0_WAITI0, IIC0_TACKI0, IIC0_ALI0,
+ 0, TSIF1, LMB, TSIF0 } },
+ { 0xffd50180, 0xffd501c0, 8, /* IMR0SA3 / IMCR0SA3 */
+ { 0, RTDMAC2_2_DADERR, RTDMAC2_2_DEI5, RTDMAC2_2_DEI4,
+ RTDMAC2_1_DEI3, RTDMAC2_1_DEI2, RTDMAC2_1_DEI1, RTDMAC2_1_DEI0 } },
+ { 0xffd50190, 0xffd501d0, 8, /* IMR4SA3 / IMCR4SA3 */
+ { 0, ISP, 0, 0,
+ LCDC1, CSIRX, DSITX_DSITX0, DSITX_DSITX1 } },
+ { 0xffd50198, 0xffd501d8, 8, /* IMR6SA3 / IMCR6SA3 */
+ { 0, TMU1_TUNI2, TMU1_TUNI1, TMU1_TUNI0,
+ CMT4, DSITX1_DSITX1_0, DSITX1_DSITX1_1, 0 } },
+ { 0xffd5019c, 0xffd501dc, 8, /* IMR7SA3 / IMCR7SA3 */
+ { 0, CPORTS2R, 0, 0,
+ JPU6E, 0, 0, 0 } },
+ { 0xffd20104, 0, 16, /* INTAMASK */
+ { 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, INTCS } },
+};
+
+/* Priority is needed for INTCA to receive the INTCS interrupt */
+static struct intc_prio_reg intcs_prio_registers[] = {
+ { 0xffd20000, 0, 16, 4, /* IPRAS */ { CTI, 0, _2DDMAC, ICB } },
+ { 0xffd20004, 0, 16, 4, /* IPRBS */ { JPU_JPEG, LCDC, 0, LCRC } },
+ { 0xffd20010, 0, 16, 4, /* IPRES */ { RTDMAC_1, CEU, 0, VPU } },
+ { 0xffd20014, 0, 16, 4, /* IPRFS */ { 0, RTDMAC_2, 0, CMT0 } },
+ { 0xffd20018, 0, 16, 4, /* IPRGS */ { TMU_TUNI0, TMU_TUNI1,
+ TMU_TUNI2, TSIF1 } },
+ { 0xffd2001c, 0, 16, 4, /* IPRHS */ { 0, 0, VEU, BEU } },
+ { 0xffd20020, 0, 16, 4, /* IPRIS */ { 0, MSIOF, TSIF0, IIC0 } },
+ { 0xffd20024, 0, 16, 4, /* IPRJS */ { 0, _3DG_SGX530, 0, 0 } },
+ { 0xffd20028, 0, 16, 4, /* IPRKS */ { 0, 0, LMB, 0 } },
+ { 0xffd2002c, 0, 16, 4, /* IPRLS */ { IPMMU, 0, 0, 0 } },
+ { 0xffd20030, 0, 16, 4, /* IPRMS */ { IIC2, 0, 0, 0 } },
+ { 0xffd50000, 0, 16, 4, /* IPRAS3 */ { RTDMAC2_1, 0, 0, 0 } },
+ { 0xffd50004, 0, 16, 4, /* IPRBS3 */ { RTDMAC2_2, 0, 0, 0 } },
+ { 0xffd50020, 0, 16, 4, /* IPRIS3 */ { 0, ISP, 0, 0 } },
+ { 0xffd50024, 0, 16, 4, /* IPRJS3 */ { LCDC1, CSIRX, DSITX, 0 } },
+ { 0xffd50030, 0, 16, 4, /* IPRMS3 */ { TMU1, 0, 0, 0 } },
+ { 0xffd50034, 0, 16, 4, /* IPRNS3 */ { CMT4, DSITX1_DSITX1_0,
+ DSITX1_DSITX1_1, 0 } },
+ { 0xffd50038, 0, 16, 4, /* IPROS3 */ { 0, CPORTS2R, 0, 0 } },
+ { 0xffd5003c, 0, 16, 4, /* IPRPS3 */ { JPU6E, 0, 0, 0 } },
+};
+
+static struct resource intcs_resources[] __initdata = {
+ [0] = {
+ .start = 0xffd20000,
+ .end = 0xffd201ff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = 0xffd50000,
+ .end = 0xffd501ff,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct intc_desc intcs_desc __initdata = {
+ .name = "sh7372-intcs",
+ .resource = intcs_resources,
+ .num_resources = ARRAY_SIZE(intcs_resources),
+ .hw = INTC_HW_DESC(intcs_vectors, intcs_groups, intcs_mask_registers,
+ intcs_prio_registers, NULL, NULL),
+};
+
+static void intcs_demux(unsigned int irq, struct irq_desc *desc)
+{
+ void __iomem *reg = (void *)get_irq_data(irq);
+ unsigned int evtcodeas = ioread32(reg);
+
+ generic_handle_irq(intcs_evt2irq(evtcodeas));
+}
+
void __init sh7372_init_irq(void)
{
+ void __iomem *intevtsa = ioremap_nocache(0xffd20100, PAGE_SIZE);
+
register_intc_controller(&intca_desc);
+ register_intc_controller(&intcs_desc);
+
+ /* demux using INTEVTSA */
+ set_irq_data(evt2irq(0xf80), (void *)intevtsa);
+ set_irq_chained_handler(evt2irq(0xf80), intcs_demux);
}
ICUSB, ICUDMC
};
-static struct intc_vect intca_vectors[] = {
+static struct intc_vect intca_vectors[] __initdata = {
INTC_VECT(IRQ0A, 0x0200), INTC_VECT(IRQ1A, 0x0220),
INTC_VECT(IRQ2A, 0x0240), INTC_VECT(IRQ3A, 0x0260),
INTC_VECT(IRQ4A, 0x0280), INTC_VECT(IRQ5A, 0x02a0),
INTC_GROUP(ICUDMC, ICUDMC_ICUDMC1, ICUDMC_ICUDMC2),
};
-static struct intc_mask_reg intca_mask_registers[] = {
+static struct intc_mask_reg intca_mask_registers[] __initdata = {
{ 0xe6900040, 0xe6900060, 8, /* INTMSK00A / INTMSKCLR00A */
{ IRQ0A, IRQ1A, IRQ2A, IRQ3A, IRQ4A, IRQ5A, IRQ6A, IRQ7A } },
{ 0xe6900044, 0xe6900064, 8, /* INTMSK10A / INTMSKCLR10A */
SCIFA6, 0, 0, 0 } },
};
-static struct intc_prio_reg intca_prio_registers[] = {
+static struct intc_prio_reg intca_prio_registers[] __initdata = {
{ 0xe6900010, 0, 32, 4, /* INTPRI00A */
{ IRQ0A, IRQ1A, IRQ2A, IRQ3A, IRQ4A, IRQ5A, IRQ6A, IRQ7A } },
{ 0xe6900014, 0, 32, 4, /* INTPRI10A */
intca_sense_registers, intca_ack_registers),
};
+/* this macro ignore entry which is also in INTCA */
+#define __IGNORE(a...)
+#define __IGNORE0(a...) 0
+
+enum {
+ UNUSED_INTCS = 0,
+
+ INTCS,
+
+ /* interrupt sources INTCS */
+ VEU_VEU0, VEU_VEU1, VEU_VEU2, VEU_VEU3,
+ RTDMAC1_1_DEI0, RTDMAC1_1_DEI1, RTDMAC1_1_DEI2, RTDMAC1_1_DEI3,
+ CEU,
+ BEU_BEU0, BEU_BEU1, BEU_BEU2,
+ __IGNORE(MFI)
+ __IGNORE(BBIF2)
+ VPU,
+ TSIF1,
+ __IGNORE(SGX540)
+ _2DDMAC,
+ IIC2_ALI2, IIC2_TACKI2, IIC2_WAITI2, IIC2_DTEI2,
+ IPMMU_IPMMUR, IPMMU_IPMMUR2,
+ RTDMAC1_2_DEI4, RTDMAC1_2_DEI5, RTDMAC1_2_DADERR,
+ __IGNORE(KEYSC)
+ __IGNORE(TTI20)
+ __IGNORE(MSIOF)
+ IIC0_ALI0, IIC0_TACKI0, IIC0_WAITI0, IIC0_DTEI0,
+ TMU_TUNI0, TMU_TUNI1, TMU_TUNI2,
+ CMT0,
+ TSIF0,
+ __IGNORE(CMT2)
+ LMB,
+ __IGNORE(MSUG)
+ __IGNORE(MSU_MSU, MSU_MSU2)
+ __IGNORE(CTI)
+ MVI3,
+ __IGNORE(RWDT0)
+ __IGNORE(RWDT1)
+ ICB,
+ PEP,
+ ASA,
+ __IGNORE(_2DG)
+ HQE,
+ JPU,
+ LCDC0,
+ __IGNORE(LCRC)
+ RTDMAC2_1_DEI0, RTDMAC2_1_DEI1, RTDMAC2_1_DEI2, RTDMAC2_1_DEI3,
+ RTDMAC2_2_DEI4, RTDMAC2_2_DEI5, RTDMAC2_2_DADERR,
+ FRC,
+ LCDC1,
+ CSIRX,
+ DSITX_DSITX0, DSITX_DSITX1,
+ __IGNORE(SPU2_SPU0, SPU2_SPU1)
+ __IGNORE(FSI)
+ __IGNORE(FMSI)
+ __IGNORE(SCUV)
+ TMU1_TUNI10, TMU1_TUNI11, TMU1_TUNI12,
+ TSIF2,
+ CMT4,
+ __IGNORE(MFIS2)
+ CPORTS2R,
+
+ /* interrupt groups INTCS */
+ RTDMAC1_1, RTDMAC1_2, VEU, BEU, IIC0, __IGNORE(MSU) IPMMU,
+ IIC2, RTDMAC2_1, RTDMAC2_2, DSITX, __IGNORE(SPU2) TMU1,
+};
+
+#define INTCS_INTVECT 0x0F80
+static struct intc_vect intcs_vectors[] __initdata = {
+ INTCS_VECT(VEU_VEU0, 0x0700), INTCS_VECT(VEU_VEU1, 0x0720),
+ INTCS_VECT(VEU_VEU2, 0x0740), INTCS_VECT(VEU_VEU3, 0x0760),
+ INTCS_VECT(RTDMAC1_1_DEI0, 0x0800), INTCS_VECT(RTDMAC1_1_DEI1, 0x0820),
+ INTCS_VECT(RTDMAC1_1_DEI2, 0x0840), INTCS_VECT(RTDMAC1_1_DEI3, 0x0860),
+ INTCS_VECT(CEU, 0x0880),
+ INTCS_VECT(BEU_BEU0, 0x08A0),
+ INTCS_VECT(BEU_BEU1, 0x08C0),
+ INTCS_VECT(BEU_BEU2, 0x08E0),
+ __IGNORE(INTCS_VECT(MFI, 0x0900))
+ __IGNORE(INTCS_VECT(BBIF2, 0x0960))
+ INTCS_VECT(VPU, 0x0980),
+ INTCS_VECT(TSIF1, 0x09A0),
+ __IGNORE(INTCS_VECT(SGX540, 0x09E0))
+ INTCS_VECT(_2DDMAC, 0x0A00),
+ INTCS_VECT(IIC2_ALI2, 0x0A80), INTCS_VECT(IIC2_TACKI2, 0x0AA0),
+ INTCS_VECT(IIC2_WAITI2, 0x0AC0), INTCS_VECT(IIC2_DTEI2, 0x0AE0),
+ INTCS_VECT(IPMMU_IPMMUR, 0x0B00), INTCS_VECT(IPMMU_IPMMUR2, 0x0B20),
+ INTCS_VECT(RTDMAC1_2_DEI4, 0x0B80),
+ INTCS_VECT(RTDMAC1_2_DEI5, 0x0BA0),
+ INTCS_VECT(RTDMAC1_2_DADERR, 0x0BC0),
+ __IGNORE(INTCS_VECT(KEYSC 0x0BE0))
+ __IGNORE(INTCS_VECT(TTI20, 0x0C80))
+ __IGNORE(INTCS_VECT(MSIOF, 0x0D20))
+ INTCS_VECT(IIC0_ALI0, 0x0E00), INTCS_VECT(IIC0_TACKI0, 0x0E20),
+ INTCS_VECT(IIC0_WAITI0, 0x0E40), INTCS_VECT(IIC0_DTEI0, 0x0E60),
+ INTCS_VECT(TMU_TUNI0, 0x0E80),
+ INTCS_VECT(TMU_TUNI1, 0x0EA0),
+ INTCS_VECT(TMU_TUNI2, 0x0EC0),
+ INTCS_VECT(CMT0, 0x0F00),
+ INTCS_VECT(TSIF0, 0x0F20),
+ __IGNORE(INTCS_VECT(CMT2, 0x0F40))
+ INTCS_VECT(LMB, 0x0F60),
+ __IGNORE(INTCS_VECT(MSUG, 0x0F80))
+ __IGNORE(INTCS_VECT(MSU_MSU, 0x0FA0))
+ __IGNORE(INTCS_VECT(MSU_MSU2, 0x0FC0))
+ __IGNORE(INTCS_VECT(CTI, 0x0400))
+ INTCS_VECT(MVI3, 0x0420),
+ __IGNORE(INTCS_VECT(RWDT0, 0x0440))
+ __IGNORE(INTCS_VECT(RWDT1, 0x0460))
+ INTCS_VECT(ICB, 0x0480),
+ INTCS_VECT(PEP, 0x04A0),
+ INTCS_VECT(ASA, 0x04C0),
+ __IGNORE(INTCS_VECT(_2DG, 0x04E0))
+ INTCS_VECT(HQE, 0x0540),
+ INTCS_VECT(JPU, 0x0560),
+ INTCS_VECT(LCDC0, 0x0580),
+ __IGNORE(INTCS_VECT(LCRC, 0x05A0))
+ INTCS_VECT(RTDMAC2_1_DEI0, 0x1300), INTCS_VECT(RTDMAC2_1_DEI1, 0x1320),
+ INTCS_VECT(RTDMAC2_1_DEI2, 0x1340), INTCS_VECT(RTDMAC2_1_DEI3, 0x1360),
+ INTCS_VECT(RTDMAC2_2_DEI4, 0x1380), INTCS_VECT(RTDMAC2_2_DEI5, 0x13A0),
+ INTCS_VECT(RTDMAC2_2_DADERR, 0x13C0),
+ INTCS_VECT(FRC, 0x1700),
+ INTCS_VECT(LCDC1, 0x1780),
+ INTCS_VECT(CSIRX, 0x17A0),
+ INTCS_VECT(DSITX_DSITX0, 0x17C0), INTCS_VECT(DSITX_DSITX1, 0x17E0),
+ __IGNORE(INTCS_VECT(SPU2_SPU0, 0x1800))
+ __IGNORE(INTCS_VECT(SPU2_SPU1, 0x1820))
+ __IGNORE(INTCS_VECT(FSI, 0x1840))
+ __IGNORE(INTCS_VECT(FMSI, 0x1860))
+ __IGNORE(INTCS_VECT(SCUV, 0x1880))
+ INTCS_VECT(TMU1_TUNI10, 0x1900), INTCS_VECT(TMU1_TUNI11, 0x1920),
+ INTCS_VECT(TMU1_TUNI12, 0x1940),
+ INTCS_VECT(TSIF2, 0x1960),
+ INTCS_VECT(CMT4, 0x1980),
+ __IGNORE(INTCS_VECT(MFIS2, 0x1A00))
+ INTCS_VECT(CPORTS2R, 0x1A20),
+
+ INTC_VECT(INTCS, INTCS_INTVECT),
+};
+
+static struct intc_group intcs_groups[] __initdata = {
+ INTC_GROUP(RTDMAC1_1,
+ RTDMAC1_1_DEI0, RTDMAC1_1_DEI1,
+ RTDMAC1_1_DEI2, RTDMAC1_1_DEI3),
+ INTC_GROUP(RTDMAC1_2,
+ RTDMAC1_2_DEI4, RTDMAC1_2_DEI5, RTDMAC1_2_DADERR),
+ INTC_GROUP(VEU, VEU_VEU0, VEU_VEU1, VEU_VEU2, VEU_VEU3),
+ INTC_GROUP(BEU, BEU_BEU0, BEU_BEU1, BEU_BEU2),
+ INTC_GROUP(IIC0, IIC0_ALI0, IIC0_TACKI0, IIC0_WAITI0, IIC0_DTEI0),
+ __IGNORE(INTC_GROUP(MSU, MSU_MSU, MSU_MSU2))
+ INTC_GROUP(IPMMU, IPMMU_IPMMUR, IPMMU_IPMMUR2),
+ INTC_GROUP(IIC2, IIC2_ALI2, IIC2_TACKI2, IIC2_WAITI2, IIC2_DTEI2),
+ INTC_GROUP(RTDMAC2_1,
+ RTDMAC2_1_DEI0, RTDMAC2_1_DEI1,
+ RTDMAC2_1_DEI2, RTDMAC2_1_DEI3),
+ INTC_GROUP(RTDMAC2_2, RTDMAC2_2_DEI4, RTDMAC2_2_DEI5, RTDMAC2_2_DADERR),
+ INTC_GROUP(DSITX, DSITX_DSITX0, DSITX_DSITX1),
+ __IGNORE(INTC_GROUP(SPU2, SPU2_SPU0, SPU2_SPU1))
+ INTC_GROUP(TMU1, TMU1_TUNI10, TMU1_TUNI11, TMU1_TUNI12),
+};
+
+static struct intc_mask_reg intcs_mask_registers[] __initdata = {
+ { 0xE6940184, 0xE69401C4, 8, /* IMR1AS / IMCR1AS */
+ { BEU_BEU2, BEU_BEU1, BEU_BEU0, CEU,
+ VEU_VEU3, VEU_VEU2, VEU_VEU1, VEU_VEU0 } },
+ { 0xE6940188, 0xE69401C8, 8, /* IMR2AS / IMCR2AS */
+ { 0, 0, 0, VPU,
+ __IGNORE0(BBIF2), 0, 0, __IGNORE0(MFI) } },
+ { 0xE694018C, 0xE69401CC, 8, /* IMR3AS / IMCR3AS */
+ { 0, 0, 0, _2DDMAC,
+ __IGNORE0(_2DG), ASA, PEP, ICB } },
+ { 0xE6940190, 0xE69401D0, 8, /* IMR4AS / IMCR4AS */
+ { 0, 0, MVI3, __IGNORE0(CTI),
+ JPU, HQE, __IGNORE0(LCRC), LCDC0 } },
+ { 0xE6940194, 0xE69401D4, 8, /* IMR5AS / IMCR5AS */
+ { __IGNORE0(KEYSC), RTDMAC1_2_DADERR, RTDMAC1_2_DEI5, RTDMAC1_2_DEI4,
+ RTDMAC1_1_DEI3, RTDMAC1_1_DEI2, RTDMAC1_1_DEI1, RTDMAC1_1_DEI0 } },
+ __IGNORE({ 0xE6940198, 0xE69401D8, 8, /* IMR6AS / IMCR6AS */
+ { 0, 0, MSIOF, 0,
+ SGX540, 0, TTI20, 0 } })
+ { 0xE694019C, 0xE69401DC, 8, /* IMR7AS / IMCR7AS */
+ { 0, TMU_TUNI2, TMU_TUNI1, TMU_TUNI0,
+ 0, 0, 0, 0 } },
+ __IGNORE({ 0xE69401A0, 0xE69401E0, 8, /* IMR8AS / IMCR8AS */
+ { 0, 0, 0, 0,
+ 0, MSU_MSU, MSU_MSU2, MSUG } })
+ { 0xE69401A4, 0xE69401E4, 8, /* IMR9AS / IMCR9AS */
+ { __IGNORE0(RWDT1), __IGNORE0(RWDT0), __IGNORE0(CMT2), CMT0,
+ IIC2_DTEI2, IIC2_WAITI2, IIC2_TACKI2, IIC2_ALI2 } },
+ { 0xE69401A8, 0xE69401E8, 8, /* IMR10AS / IMCR10AS */
+ { 0, 0, IPMMU_IPMMUR, IPMMU_IPMMUR2,
+ 0, 0, 0, 0 } },
+ { 0xE69401AC, 0xE69401EC, 8, /* IMR11AS / IMCR11AS */
+ { IIC0_DTEI0, IIC0_WAITI0, IIC0_TACKI0, IIC0_ALI0,
+ 0, TSIF1, LMB, TSIF0 } },
+ { 0xE6950180, 0xE69501C0, 8, /* IMR0AS3 / IMCR0AS3 */
+ { RTDMAC2_1_DEI0, RTDMAC2_1_DEI1, RTDMAC2_1_DEI2, RTDMAC2_1_DEI3,
+ RTDMAC2_2_DEI4, RTDMAC2_2_DEI5, RTDMAC2_2_DADERR, 0 } },
+ { 0xE6950190, 0xE69501D0, 8, /* IMR4AS3 / IMCR4AS3 */
+ { FRC, 0, 0, 0,
+ LCDC1, CSIRX, DSITX_DSITX0, DSITX_DSITX1 } },
+ __IGNORE({ 0xE6950194, 0xE69501D4, 8, /* IMR5AS3 / IMCR5AS3 */
+ {SPU2_SPU0, SPU2_SPU1, FSI, FMSI,
+ SCUV, 0, 0, 0 } })
+ { 0xE6950198, 0xE69501D8, 8, /* IMR6AS3 / IMCR6AS3 */
+ { TMU1_TUNI10, TMU1_TUNI11, TMU1_TUNI12, TSIF2,
+ CMT4, 0, 0, 0 } },
+ { 0xE695019C, 0xE69501DC, 8, /* IMR7AS3 / IMCR7AS3 */
+ { __IGNORE0(MFIS2), CPORTS2R, 0, 0,
+ 0, 0, 0, 0 } },
+ { 0xFFD20104, 0, 16, /* INTAMASK */
+ { 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, INTCS } }
+};
+
+static struct intc_prio_reg intcs_prio_registers[] __initdata = {
+ /* IPRAS */
+ { 0xFFD20000, 0, 16, 4, { __IGNORE0(CTI), MVI3, _2DDMAC, ICB } },
+ /* IPRBS */
+ { 0xFFD20004, 0, 16, 4, { JPU, LCDC0, 0, __IGNORE0(LCRC) } },
+ /* IPRCS */
+ __IGNORE({ 0xFFD20008, 0, 16, 4, { BBIF2, 0, 0, 0 } })
+ /* IPRES */
+ { 0xFFD20010, 0, 16, 4, { RTDMAC1_1, CEU, __IGNORE0(MFI), VPU } },
+ /* IPRFS */
+ { 0xFFD20014, 0, 16, 4,
+ { __IGNORE0(KEYSC), RTDMAC1_2, __IGNORE0(CMT2), CMT0 } },
+ /* IPRGS */
+ { 0xFFD20018, 0, 16, 4, { TMU_TUNI0, TMU_TUNI1, TMU_TUNI2, TSIF1 } },
+ /* IPRHS */
+ { 0xFFD2001C, 0, 16, 4, { __IGNORE0(TTI20), 0, VEU, BEU } },
+ /* IPRIS */
+ { 0xFFD20020, 0, 16, 4, { 0, __IGNORE0(MSIOF), TSIF0, IIC0 } },
+ /* IPRJS */
+ __IGNORE({ 0xFFD20024, 0, 16, 4, { 0, SGX540, MSUG, MSU } })
+ /* IPRKS */
+ { 0xFFD20028, 0, 16, 4, { __IGNORE0(_2DG), ASA, LMB, PEP } },
+ /* IPRLS */
+ { 0xFFD2002C, 0, 16, 4, { IPMMU, 0, 0, HQE } },
+ /* IPRMS */
+ { 0xFFD20030, 0, 16, 4,
+ { IIC2, 0, __IGNORE0(RWDT1), __IGNORE0(RWDT0) } },
+ /* IPRAS3 */
+ { 0xFFD50000, 0, 16, 4, { RTDMAC2_1, 0, 0, 0 } },
+ /* IPRBS3 */
+ { 0xFFD50004, 0, 16, 4, { RTDMAC2_2, 0, 0, 0 } },
+ /* IPRIS3 */
+ { 0xFFD50020, 0, 16, 4, { FRC, 0, 0, 0 } },
+ /* IPRJS3 */
+ { 0xFFD50024, 0, 16, 4, { LCDC1, CSIRX, DSITX, 0 } },
+ /* IPRKS3 */
+ __IGNORE({ 0xFFD50028, 0, 16, 4, { SPU2, 0, FSI, FMSI } })
+ /* IPRLS3 */
+ __IGNORE({ 0xFFD5002C, 0, 16, 4, { SCUV, 0, 0, 0 } })
+ /* IPRMS3 */
+ { 0xFFD50030, 0, 16, 4, { TMU1, 0, 0, TSIF2 } },
+ /* IPRNS3 */
+ { 0xFFD50034, 0, 16, 4, { CMT4, 0, 0, 0 } },
+ /* IPROS3 */
+ { 0xFFD50038, 0, 16, 4, { __IGNORE0(MFIS2), CPORTS2R, 0, 0 } },
+};
+
+static struct resource intcs_resources[] __initdata = {
+ [0] = {
+ .start = 0xffd20000,
+ .end = 0xffd500ff,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct intc_desc intcs_desc __initdata = {
+ .name = "sh7377-intcs",
+ .resource = intcs_resources,
+ .num_resources = ARRAY_SIZE(intcs_resources),
+ .hw = INTC_HW_DESC(intcs_vectors, intcs_groups,
+ intcs_mask_registers, intcs_prio_registers,
+ NULL, NULL),
+};
+
+static void intcs_demux(unsigned int irq, struct irq_desc *desc)
+{
+ void __iomem *reg = (void *)get_irq_data(irq);
+ unsigned int evtcodeas = ioread32(reg);
+
+ generic_handle_irq(intcs_evt2irq(evtcodeas));
+}
+
+#define INTEVTSA 0xFFD20100
void __init sh7377_init_irq(void)
{
+ void __iomem *intevtsa = ioremap_nocache(INTEVTSA, PAGE_SIZE);
+
register_intc_controller(&intca_desc);
+ register_intc_controller(&intcs_desc);
+
+ /* demux using INTEVTSA */
+ set_irq_data(evt2irq(INTCS_INTVECT), (void *)intevtsa);
+ set_irq_chained_handler(evt2irq(INTCS_INTVECT), intcs_demux);
}
GPIO_FN(LCDD18), GPIO_FN(LCDD19), GPIO_FN(LCDD20),
GPIO_FN(LCDD21), GPIO_FN(LCDD22), GPIO_FN(LCDD23),
+ GPIO_FN(LCDC0_SELECT),
+ GPIO_FN(LCDC1_SELECT),
+
/* IRDA */
GPIO_FN(IRDA_OUT), GPIO_FN(IRDA_IN), GPIO_FN(IRDA_FIRSEL),
GPIO_FN(IROUT_139), GPIO_FN(IROUT_140),
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
+/* SCIFA0 */
static struct plat_sci_port scif0_platform_data = {
.mapbase = 0xe6c40000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 80, 80, 80, 80 },
+ .irqs = { evt2irq(0xc00), evt2irq(0xc00),
+ evt2irq(0xc00), evt2irq(0xc00) },
};
static struct platform_device scif0_device = {
},
};
+/* SCIFA1 */
static struct plat_sci_port scif1_platform_data = {
.mapbase = 0xe6c50000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 81, 81, 81, 81 },
+ .irqs = { evt2irq(0xc20), evt2irq(0xc20),
+ evt2irq(0xc20), evt2irq(0xc20) },
};
static struct platform_device scif1_device = {
},
};
+/* SCIFA2 */
static struct plat_sci_port scif2_platform_data = {
.mapbase = 0xe6c60000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 82, 82, 82, 82 },
+ .irqs = { evt2irq(0xc40), evt2irq(0xc40),
+ evt2irq(0xc40), evt2irq(0xc40) },
};
static struct platform_device scif2_device = {
},
};
+/* SCIFA3 */
static struct plat_sci_port scif3_platform_data = {
.mapbase = 0xe6c70000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 83, 83, 83, 83 },
+ .irqs = { evt2irq(0xc60), evt2irq(0xc60),
+ evt2irq(0xc60), evt2irq(0xc60) },
};
static struct platform_device scif3_device = {
},
};
+/* SCIFA4 */
static struct plat_sci_port scif4_platform_data = {
.mapbase = 0xe6c80000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 89, 89, 89, 89 },
+ .irqs = { evt2irq(0xd20), evt2irq(0xd20),
+ evt2irq(0xd20), evt2irq(0xd20) },
};
static struct platform_device scif4_device = {
},
};
+/* SCIFA5 */
static struct plat_sci_port scif5_platform_data = {
.mapbase = 0xe6cb0000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 90, 90, 90, 90 },
+ .irqs = { evt2irq(0xd40), evt2irq(0xd40),
+ evt2irq(0xd40), evt2irq(0xd40) },
};
static struct platform_device scif5_device = {
},
};
+/* SCIFB */
static struct plat_sci_port scif6_platform_data = {
.mapbase = 0xe6c30000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 91, 91, 91, 91 },
+ .irqs = { evt2irq(0xd60), evt2irq(0xd60),
+ evt2irq(0xd60), evt2irq(0xd60) },
};
static struct platform_device scif6_device = {
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = 72,
+ .start = evt2irq(0xb00), /* CMT1_CMT10 */
.flags = IORESOURCE_IRQ,
},
};
#include <linux/input.h>
#include <linux/io.h>
#include <linux/serial_sci.h>
+#include <linux/sh_dma.h>
#include <linux/sh_intc.h>
#include <linux/sh_timer.h>
#include <mach/hardware.h>
+#include <mach/sh7372.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
+/* SCIFA0 */
static struct plat_sci_port scif0_platform_data = {
.mapbase = 0xe6c40000,
.flags = UPF_BOOT_AUTOCONF,
- .type = PORT_SCIF,
- .irqs = { 80, 80, 80, 80 },
+ .type = PORT_SCIFA,
+ .irqs = { evt2irq(0x0c00), evt2irq(0x0c00),
+ evt2irq(0x0c00), evt2irq(0x0c00) },
};
static struct platform_device scif0_device = {
},
};
+/* SCIFA1 */
static struct plat_sci_port scif1_platform_data = {
.mapbase = 0xe6c50000,
.flags = UPF_BOOT_AUTOCONF,
- .type = PORT_SCIF,
- .irqs = { 81, 81, 81, 81 },
+ .type = PORT_SCIFA,
+ .irqs = { evt2irq(0x0c20), evt2irq(0x0c20),
+ evt2irq(0x0c20), evt2irq(0x0c20) },
};
static struct platform_device scif1_device = {
},
};
+/* SCIFA2 */
static struct plat_sci_port scif2_platform_data = {
.mapbase = 0xe6c60000,
.flags = UPF_BOOT_AUTOCONF,
- .type = PORT_SCIF,
- .irqs = { 82, 82, 82, 82 },
+ .type = PORT_SCIFA,
+ .irqs = { evt2irq(0x0c40), evt2irq(0x0c40),
+ evt2irq(0x0c40), evt2irq(0x0c40) },
};
static struct platform_device scif2_device = {
},
};
+/* SCIFA3 */
static struct plat_sci_port scif3_platform_data = {
.mapbase = 0xe6c70000,
.flags = UPF_BOOT_AUTOCONF,
- .type = PORT_SCIF,
- .irqs = { 83, 83, 83, 83 },
+ .type = PORT_SCIFA,
+ .irqs = { evt2irq(0x0c60), evt2irq(0x0c60),
+ evt2irq(0x0c60), evt2irq(0x0c60) },
};
static struct platform_device scif3_device = {
},
};
+/* SCIFA4 */
static struct plat_sci_port scif4_platform_data = {
.mapbase = 0xe6c80000,
.flags = UPF_BOOT_AUTOCONF,
- .type = PORT_SCIF,
- .irqs = { 89, 89, 89, 89 },
+ .type = PORT_SCIFA,
+ .irqs = { evt2irq(0x0d20), evt2irq(0x0d20),
+ evt2irq(0x0d20), evt2irq(0x0d20) },
};
static struct platform_device scif4_device = {
},
};
+/* SCIFA5 */
static struct plat_sci_port scif5_platform_data = {
.mapbase = 0xe6cb0000,
.flags = UPF_BOOT_AUTOCONF,
- .type = PORT_SCIF,
- .irqs = { 90, 90, 90, 90 },
+ .type = PORT_SCIFA,
+ .irqs = { evt2irq(0x0d40), evt2irq(0x0d40),
+ evt2irq(0x0d40), evt2irq(0x0d40) },
};
static struct platform_device scif5_device = {
},
};
+/* SCIFB */
static struct plat_sci_port scif6_platform_data = {
.mapbase = 0xe6c30000,
.flags = UPF_BOOT_AUTOCONF,
- .type = PORT_SCIF,
- .irqs = { 91, 91, 91, 91 },
+ .type = PORT_SCIFB,
+ .irqs = { evt2irq(0x0d60), evt2irq(0x0d60),
+ evt2irq(0x0d60), evt2irq(0x0d60) },
};
static struct platform_device scif6_device = {
},
};
+/* CMT */
static struct sh_timer_config cmt10_platform_data = {
.name = "CMT10",
.channel_offset = 0x10,
.timer_bit = 0,
- .clk = "r_clk",
+ .clk = "cmt1",
.clockevent_rating = 125,
.clocksource_rating = 125,
};
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = 72,
+ .start = evt2irq(0x0b00), /* CMT1_CMT10 */
.flags = IORESOURCE_IRQ,
},
};
.num_resources = ARRAY_SIZE(cmt10_resources),
};
+/* I2C */
+static struct resource iic0_resources[] = {
+ [0] = {
+ .name = "IIC0",
+ .start = 0xFFF20000,
+ .end = 0xFFF20425 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = intcs_evt2irq(0xe00), /* IIC0_ALI0 */
+ .end = intcs_evt2irq(0xe60), /* IIC0_DTEI0 */
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device iic0_device = {
+ .name = "i2c-sh_mobile",
+ .id = 0, /* "i2c0" clock */
+ .num_resources = ARRAY_SIZE(iic0_resources),
+ .resource = iic0_resources,
+};
+
+static struct resource iic1_resources[] = {
+ [0] = {
+ .name = "IIC1",
+ .start = 0xE6C20000,
+ .end = 0xE6C20425 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = evt2irq(0x780), /* IIC1_ALI1 */
+ .end = evt2irq(0x7e0), /* IIC1_DTEI1 */
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device iic1_device = {
+ .name = "i2c-sh_mobile",
+ .id = 1, /* "i2c1" clock */
+ .num_resources = ARRAY_SIZE(iic1_resources),
+ .resource = iic1_resources,
+};
+
+/* DMA */
+/* Transmit sizes and respective CHCR register values */
+enum {
+ XMIT_SZ_8BIT = 0,
+ XMIT_SZ_16BIT = 1,
+ XMIT_SZ_32BIT = 2,
+ XMIT_SZ_64BIT = 7,
+ XMIT_SZ_128BIT = 3,
+ XMIT_SZ_256BIT = 4,
+ XMIT_SZ_512BIT = 5,
+};
+
+/* log2(size / 8) - used to calculate number of transfers */
+#define TS_SHIFT { \
+ [XMIT_SZ_8BIT] = 0, \
+ [XMIT_SZ_16BIT] = 1, \
+ [XMIT_SZ_32BIT] = 2, \
+ [XMIT_SZ_64BIT] = 3, \
+ [XMIT_SZ_128BIT] = 4, \
+ [XMIT_SZ_256BIT] = 5, \
+ [XMIT_SZ_512BIT] = 6, \
+}
+
+#define TS_INDEX2VAL(i) ((((i) & 3) << 3) | \
+ (((i) & 0xc) << (20 - 2)))
+
+static const struct sh_dmae_slave_config sh7372_dmae_slaves[] = {
+ {
+ .slave_id = SHDMA_SLAVE_SCIF0_TX,
+ .addr = 0xe6c40020,
+ .chcr = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+ .mid_rid = 0x21,
+ }, {
+ .slave_id = SHDMA_SLAVE_SCIF0_RX,
+ .addr = 0xe6c40024,
+ .chcr = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+ .mid_rid = 0x22,
+ }, {
+ .slave_id = SHDMA_SLAVE_SCIF1_TX,
+ .addr = 0xe6c50020,
+ .chcr = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+ .mid_rid = 0x25,
+ }, {
+ .slave_id = SHDMA_SLAVE_SCIF1_RX,
+ .addr = 0xe6c50024,
+ .chcr = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+ .mid_rid = 0x26,
+ }, {
+ .slave_id = SHDMA_SLAVE_SCIF2_TX,
+ .addr = 0xe6c60020,
+ .chcr = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+ .mid_rid = 0x29,
+ }, {
+ .slave_id = SHDMA_SLAVE_SCIF2_RX,
+ .addr = 0xe6c60024,
+ .chcr = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+ .mid_rid = 0x2a,
+ }, {
+ .slave_id = SHDMA_SLAVE_SCIF3_TX,
+ .addr = 0xe6c70020,
+ .chcr = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+ .mid_rid = 0x2d,
+ }, {
+ .slave_id = SHDMA_SLAVE_SCIF3_RX,
+ .addr = 0xe6c70024,
+ .chcr = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+ .mid_rid = 0x2e,
+ }, {
+ .slave_id = SHDMA_SLAVE_SCIF4_TX,
+ .addr = 0xe6c80020,
+ .chcr = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+ .mid_rid = 0x39,
+ }, {
+ .slave_id = SHDMA_SLAVE_SCIF4_RX,
+ .addr = 0xe6c80024,
+ .chcr = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+ .mid_rid = 0x3a,
+ }, {
+ .slave_id = SHDMA_SLAVE_SCIF5_TX,
+ .addr = 0xe6cb0020,
+ .chcr = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+ .mid_rid = 0x35,
+ }, {
+ .slave_id = SHDMA_SLAVE_SCIF5_RX,
+ .addr = 0xe6cb0024,
+ .chcr = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+ .mid_rid = 0x36,
+ }, {
+ .slave_id = SHDMA_SLAVE_SCIF6_TX,
+ .addr = 0xe6c30040,
+ .chcr = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+ .mid_rid = 0x3d,
+ }, {
+ .slave_id = SHDMA_SLAVE_SCIF6_RX,
+ .addr = 0xe6c30060,
+ .chcr = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_8BIT),
+ .mid_rid = 0x3e,
+ }, {
+ .slave_id = SHDMA_SLAVE_SDHI0_TX,
+ .addr = 0xe6850030,
+ .chcr = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_16BIT),
+ .mid_rid = 0xc1,
+ }, {
+ .slave_id = SHDMA_SLAVE_SDHI0_RX,
+ .addr = 0xe6850030,
+ .chcr = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_16BIT),
+ .mid_rid = 0xc2,
+ }, {
+ .slave_id = SHDMA_SLAVE_SDHI1_TX,
+ .addr = 0xe6860030,
+ .chcr = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_16BIT),
+ .mid_rid = 0xc9,
+ }, {
+ .slave_id = SHDMA_SLAVE_SDHI1_RX,
+ .addr = 0xe6860030,
+ .chcr = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_16BIT),
+ .mid_rid = 0xca,
+ }, {
+ .slave_id = SHDMA_SLAVE_SDHI2_TX,
+ .addr = 0xe6870030,
+ .chcr = DM_FIX | SM_INC | 0x800 | TS_INDEX2VAL(XMIT_SZ_16BIT),
+ .mid_rid = 0xcd,
+ }, {
+ .slave_id = SHDMA_SLAVE_SDHI2_RX,
+ .addr = 0xe6870030,
+ .chcr = DM_INC | SM_FIX | 0x800 | TS_INDEX2VAL(XMIT_SZ_16BIT),
+ .mid_rid = 0xce,
+ },
+};
+
+static const struct sh_dmae_channel sh7372_dmae_channels[] = {
+ {
+ .offset = 0,
+ .dmars = 0,
+ .dmars_bit = 0,
+ }, {
+ .offset = 0x10,
+ .dmars = 0,
+ .dmars_bit = 8,
+ }, {
+ .offset = 0x20,
+ .dmars = 4,
+ .dmars_bit = 0,
+ }, {
+ .offset = 0x30,
+ .dmars = 4,
+ .dmars_bit = 8,
+ }, {
+ .offset = 0x50,
+ .dmars = 8,
+ .dmars_bit = 0,
+ }, {
+ .offset = 0x60,
+ .dmars = 8,
+ .dmars_bit = 8,
+ }
+};
+
+static const unsigned int ts_shift[] = TS_SHIFT;
+
+static struct sh_dmae_pdata dma_platform_data = {
+ .slave = sh7372_dmae_slaves,
+ .slave_num = ARRAY_SIZE(sh7372_dmae_slaves),
+ .channel = sh7372_dmae_channels,
+ .channel_num = ARRAY_SIZE(sh7372_dmae_channels),
+ .ts_low_shift = 3,
+ .ts_low_mask = 0x18,
+ .ts_high_shift = (20 - 2), /* 2 bits for shifted low TS */
+ .ts_high_mask = 0x00300000,
+ .ts_shift = ts_shift,
+ .ts_shift_num = ARRAY_SIZE(ts_shift),
+ .dmaor_init = DMAOR_DME,
+};
+
+/* Resource order important! */
+static struct resource sh7372_dmae0_resources[] = {
+ {
+ /* Channel registers and DMAOR */
+ .start = 0xfe008020,
+ .end = 0xfe00808f,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ /* DMARSx */
+ .start = 0xfe009000,
+ .end = 0xfe00900b,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ /* DMA error IRQ */
+ .start = 246,
+ .end = 246,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ /* IRQ for channels 0-5 */
+ .start = 240,
+ .end = 245,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/* Resource order important! */
+static struct resource sh7372_dmae1_resources[] = {
+ {
+ /* Channel registers and DMAOR */
+ .start = 0xfe018020,
+ .end = 0xfe01808f,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ /* DMARSx */
+ .start = 0xfe019000,
+ .end = 0xfe01900b,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ /* DMA error IRQ */
+ .start = 254,
+ .end = 254,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ /* IRQ for channels 0-5 */
+ .start = 248,
+ .end = 253,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/* Resource order important! */
+static struct resource sh7372_dmae2_resources[] = {
+ {
+ /* Channel registers and DMAOR */
+ .start = 0xfe028020,
+ .end = 0xfe02808f,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ /* DMARSx */
+ .start = 0xfe029000,
+ .end = 0xfe02900b,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ /* DMA error IRQ */
+ .start = 262,
+ .end = 262,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ /* IRQ for channels 0-5 */
+ .start = 256,
+ .end = 261,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device dma0_device = {
+ .name = "sh-dma-engine",
+ .id = 0,
+ .resource = sh7372_dmae0_resources,
+ .num_resources = ARRAY_SIZE(sh7372_dmae0_resources),
+ .dev = {
+ .platform_data = &dma_platform_data,
+ },
+};
+
+static struct platform_device dma1_device = {
+ .name = "sh-dma-engine",
+ .id = 1,
+ .resource = sh7372_dmae1_resources,
+ .num_resources = ARRAY_SIZE(sh7372_dmae1_resources),
+ .dev = {
+ .platform_data = &dma_platform_data,
+ },
+};
+
+static struct platform_device dma2_device = {
+ .name = "sh-dma-engine",
+ .id = 2,
+ .resource = sh7372_dmae2_resources,
+ .num_resources = ARRAY_SIZE(sh7372_dmae2_resources),
+ .dev = {
+ .platform_data = &dma_platform_data,
+ },
+};
+
static struct platform_device *sh7372_early_devices[] __initdata = {
&scif0_device,
&scif1_device,
&scif5_device,
&scif6_device,
&cmt10_device,
+ &iic0_device,
+ &iic1_device,
+ &dma0_device,
+ &dma1_device,
+ &dma2_device,
};
void __init sh7372_add_standard_devices(void)
ARRAY_SIZE(sh7372_early_devices));
}
-#define SMSTPCR3 0xe615013c
-#define SMSTPCR3_CMT1 (1 << 29)
-
void __init sh7372_add_early_devices(void)
{
- /* enable clock to CMT1 */
- __raw_writel(__raw_readl(SMSTPCR3) & ~SMSTPCR3_CMT1, SMSTPCR3);
-
early_platform_add_devices(sh7372_early_devices,
ARRAY_SIZE(sh7372_early_devices));
}
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
+/* SCIFA0 */
static struct plat_sci_port scif0_platform_data = {
.mapbase = 0xe6c40000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 80, 80, 80, 80 },
+ .irqs = { evt2irq(0xc00), evt2irq(0xc00),
+ evt2irq(0xc00), evt2irq(0xc00) },
};
static struct platform_device scif0_device = {
},
};
+/* SCIFA1 */
static struct plat_sci_port scif1_platform_data = {
.mapbase = 0xe6c50000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 81, 81, 81, 81 },
+ .irqs = { evt2irq(0xc20), evt2irq(0xc20),
+ evt2irq(0xc20), evt2irq(0xc20) },
};
static struct platform_device scif1_device = {
},
};
+/* SCIFA2 */
static struct plat_sci_port scif2_platform_data = {
.mapbase = 0xe6c60000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 82, 82, 82, 82 },
+ .irqs = { evt2irq(0xc40), evt2irq(0xc40),
+ evt2irq(0xc40), evt2irq(0xc40) },
};
static struct platform_device scif2_device = {
},
};
+/* SCIFA3 */
static struct plat_sci_port scif3_platform_data = {
.mapbase = 0xe6c70000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 83, 83, 83, 83 },
+ .irqs = { evt2irq(0xc60), evt2irq(0xc60),
+ evt2irq(0xc60), evt2irq(0xc60) },
};
static struct platform_device scif3_device = {
},
};
+/* SCIFA4 */
static struct plat_sci_port scif4_platform_data = {
.mapbase = 0xe6c80000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 89, 89, 89, 89 },
+ .irqs = { evt2irq(0xd20), evt2irq(0xd20),
+ evt2irq(0xd20), evt2irq(0xd20) },
};
static struct platform_device scif4_device = {
},
};
+/* SCIFA5 */
static struct plat_sci_port scif5_platform_data = {
.mapbase = 0xe6cb0000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 90, 90, 90, 90 },
+ .irqs = { evt2irq(0xd40), evt2irq(0xd40),
+ evt2irq(0xd40), evt2irq(0xd40) },
};
static struct platform_device scif5_device = {
},
};
+/* SCIFA6 */
static struct plat_sci_port scif6_platform_data = {
.mapbase = 0xe6cc0000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 196, 196, 196, 196 },
+ .irqs = { intcs_evt2irq(0x1a80), intcs_evt2irq(0x1a80),
+ intcs_evt2irq(0x1a80), intcs_evt2irq(0x1a80) },
};
static struct platform_device scif6_device = {
},
};
+/* SCIFB */
static struct plat_sci_port scif7_platform_data = {
.mapbase = 0xe6c30000,
.flags = UPF_BOOT_AUTOCONF,
.type = PORT_SCIF,
- .irqs = { 91, 91, 91, 91 },
+ .irqs = { evt2irq(0xd60), evt2irq(0xd60),
+ evt2irq(0xd60), evt2irq(0xd60) },
};
static struct platform_device scif7_device = {
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = 72,
+ .start = evt2irq(0xb00), /* CMT1_CMT10 */
.flags = IORESOURCE_IRQ,
},
};
.lock = __SPIN_LOCK_UNLOCKED(fast_clk.lock),
};
+/*
+ * The MMCI apb_pclk is hardwired to the same terminal as the
+ * external MCI clock. Thus this will be referenced twice.
+ */
static struct clk mmcsd_clk = {
.name = "MCLK",
.parent = &fast_clk,
.lock = __SPIN_LOCK_UNLOCKED(i2c1_clk.lock),
};
+/*
+ * The SPI apb_pclk is hardwired to the same terminal as the
+ * external SPI clock. Thus this will be referenced twice.
+ */
static struct clk spi_clk = {
.name = "SPI",
.parent = &fast_clk,
};
#ifdef CONFIG_MACH_U300_BS335
-static struct clk uart1_clk = {
- .name = "UART1",
+static struct clk uart1_pclk = {
+ .name = "UART1_PCLK",
.parent = &fast_clk,
- .rate = 13000000,
.hw_ctrld = false,
.reset = true,
.res_reg = U300_SYSCON_VBASE + U300_SYSCON_RFR,
.clk_val = U300_SYSCON_SBCER_UART1_CLK_EN,
.enable = syscon_clk_enable,
.disable = syscon_clk_disable,
+ .lock = __SPIN_LOCK_UNLOCKED(uart1_pclk.lock),
+};
+
+/* This one is hardwired to PLL13 */
+static struct clk uart1_clk = {
+ .name = "UART1_CLK",
+ .rate = 13000000,
+ .hw_ctrld = true,
.lock = __SPIN_LOCK_UNLOCKED(uart1_clk.lock),
};
#endif
.lock = __SPIN_LOCK_UNLOCKED(wdog_clk.lock),
};
-/* This one is hardwired to PLL13 */
-static struct clk uart_clk = {
- .name = "UARTCLK",
+static struct clk uart0_pclk = {
+ .name = "UART0_PCLK",
.parent = &slow_clk,
- .rate = 13000000,
.hw_ctrld = false,
.reset = true,
.res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR,
.clk_val = U300_SYSCON_SBCER_UART_CLK_EN,
.enable = syscon_clk_enable,
.disable = syscon_clk_disable,
- .lock = __SPIN_LOCK_UNLOCKED(uart_clk.lock),
+ .lock = __SPIN_LOCK_UNLOCKED(uart0_pclk.lock),
+};
+
+/* This one is hardwired to PLL13 */
+static struct clk uart0_clk = {
+ .name = "UART0_CLK",
+ .parent = &slow_clk,
+ .rate = 13000000,
+ .hw_ctrld = true,
+ .lock = __SPIN_LOCK_UNLOCKED(uart0_clk.lock),
};
static struct clk keypad_clk = {
.lock = __SPIN_LOCK_UNLOCKED(timer_clk.lock),
};
+/*
+ * There is a binary divider in the hardware that divides
+ * the 13MHz PLL by 13 down to 1 MHz.
+ */
static struct clk app_timer_clk = {
.name = "TIMER_APP",
.parent = &slow_clk,
- .rate = 13000000,
+ .rate = 1000000,
.hw_ctrld = true,
.reset = true,
.res_reg = U300_SYSCON_VBASE + U300_SYSCON_RSR,
};
#endif
-static struct clk dummy_apb_pclk;
-
#define DEF_LOOKUP(devid, clkref) \
{ \
.dev_id = devid, \
.clk = clkref, \
}
+#define DEF_LOOKUP_CON(devid, conid, clkref) \
+ { \
+ .dev_id = devid, \
+ .con_id = conid, \
+ .clk = clkref, \
+ }
+
/*
* Here we only define clocks that are meaningful to
* look up through clockdevice.
*/
static struct clk_lookup lookups[] = {
- {
- .con_id = "apb_pclk",
- .clk = &dummy_apb_pclk,
- },
/* Connected directly to the AMBA bus */
DEF_LOOKUP("amba", &amba_clk),
DEF_LOOKUP("cpu", &cpu_clk),
/* AHB bridge clocks */
DEF_LOOKUP("ahb_subsys", &ahb_subsys_clk),
DEF_LOOKUP("intcon", &intcon_clk),
+ DEF_LOOKUP_CON("intcon", "apb_pclk", &intcon_clk),
DEF_LOOKUP("mspro", &mspro_clk),
DEF_LOOKUP("pl172", &emif_clk),
+ DEF_LOOKUP_CON("pl172", "apb_pclk", &emif_clk),
/* FAST bridge clocks */
DEF_LOOKUP("fast", &fast_clk),
DEF_LOOKUP("mmci", &mmcsd_clk),
+ DEF_LOOKUP_CON("mmci", "apb_pclk", &mmcsd_clk),
/*
* The .0 and .1 identifiers on these comes from the platform device
* .id field and are assigned when the platform devices are registered.
DEF_LOOKUP("stu300.0", &i2c0_clk),
DEF_LOOKUP("stu300.1", &i2c1_clk),
DEF_LOOKUP("pl022", &spi_clk),
+ DEF_LOOKUP_CON("pl022", "apb_pclk", &spi_clk),
#ifdef CONFIG_MACH_U300_BS335
DEF_LOOKUP("uart1", &uart1_clk),
+ DEF_LOOKUP_CON("uart1", "apb_pclk", &uart1_pclk),
#endif
/* SLOW bridge clocks */
DEF_LOOKUP("slow", &slow_clk),
DEF_LOOKUP("coh901327_wdog", &wdog_clk),
- DEF_LOOKUP("uart0", &uart_clk),
+ DEF_LOOKUP("uart0", &uart0_clk),
+ DEF_LOOKUP_CON("uart0", "apb_pclk", &uart0_pclk),
DEF_LOOKUP("apptimer", &app_timer_clk),
DEF_LOOKUP("coh901461-keypad", &keypad_clk),
DEF_LOOKUP("u300-gpio", &gpio_clk),
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
}
-/*
- * These are the clocks for cells registered as primecell drivers
- * on the AMBA bus. These must be on during AMBA device registration
- * since the bus probe will attempt to read magic configuration
- * registers for these devices. If they are deactivated these probes
- * will fail.
- *
- *
- * Please note that on emif, both RAM and NAND is connected in dual
- * RAM phones. On single RAM phones, ram is on semi and NAND on emif.
- *
- */
-void u300_clock_primecells(void)
-{
- clk_enable(&intcon_clk);
- clk_enable(&uart_clk);
-#ifdef CONFIG_MACH_U300_BS335
- clk_enable(&uart1_clk);
-#endif
- clk_enable(&spi_clk);
-
- clk_enable(&mmcsd_clk);
-
-}
-EXPORT_SYMBOL(u300_clock_primecells);
-
-void u300_unclock_primecells(void)
-{
-
- clk_disable(&intcon_clk);
- clk_disable(&uart_clk);
-#ifdef CONFIG_MACH_U300_BS335
- clk_disable(&uart1_clk);
-#endif
- clk_disable(&spi_clk);
- clk_disable(&mmcsd_clk);
-
-}
-EXPORT_SYMBOL(u300_unclock_primecells);
-
-/*
- * The interrupt controller is enabled before the clock API is registered.
- */
-void u300_enable_intcon_clock(void)
-{
- clk_enable(&intcon_clk);
-}
-EXPORT_SYMBOL(u300_enable_intcon_clock);
-
-/*
- * The timer is enabled before the clock API is registered.
- */
-void u300_enable_timer_clock(void)
-{
- clk_enable(&app_timer_clk);
-}
-EXPORT_SYMBOL(u300_enable_timer_clock);
-
#if (defined(CONFIG_DEBUG_FS) && defined(CONFIG_U300_DEBUG))
/*
* The following makes it possible to view the status (especially
&spi_clk,
#ifdef CONFIG_MACH_U300_BS335
&uart1_clk,
+ &uart1_pclk,
#endif
/* SLOW bridge clocks */
&slow_clk,
&wdog_clk,
- &uart_clk,
+ &uart0_clk,
+ &uart0_pclk,
&app_timer_clk,
&keypad_clk,
&gpio_clk,
chars++;
}
cdp[32] = '\0';
- if (clk->get_rate)
+ if (clk->get_rate || clk->rate != 0)
seq_printf(s,
"%s%s\t%s\t%d\t%s\t%lu Hz\n",
&cdp[0],
clk->usecount ? "ON" : "OFF",
clk->usecount,
clk->hw_ctrld ? "YES" : "NO ",
- clk->get_rate(clk));
+ clk_get_rate(clk));
else
seq_printf(s,
"%s%s\t%s\t%d\t%s\t" \
module_init(init_clk_read_debugfs);
#endif
-static int __init u300_clock_init(void)
+int __init u300_clock_init(void)
{
u16 val;
*/
syscon_block_reset_disable(&semi_clk);
syscon_block_reset_disable(&emif_clk);
- semi_clk.enable(&semi_clk);
- emif_clk.enable(&emif_clk);
+ clk_enable(&semi_clk);
+ clk_enable(&emif_clk);
return 0;
}
-/* initialize clocking early to be available later in the boot */
-core_initcall(u300_clock_init);
void (*disable) (struct clk *);
};
-void u300_clock_primecells(void);
-void u300_unclock_primecells(void);
-void u300_enable_intcon_clock(void);
-void u300_enable_timer_clock(void);
+int u300_clock_init(void);
#endif
#include <linux/amba/bus.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/err.h>
#include <mach/coh901318.h>
#include <asm/types.h>
void __init u300_init_irq(void)
{
u32 mask[2] = {0, 0};
+ struct clk *clk;
int i;
+ /* initialize clocking early, we want to clock the INTCON */
+ u300_clock_init();
+
+ /* Clock the interrupt controller */
+ clk = clk_get_sys("intcon", NULL);
+ BUG_ON(IS_ERR(clk));
+ clk_enable(clk);
+
for (i = 0; i < NR_IRQS; i++)
set_bit(i, (unsigned long *) &mask[0]);
- u300_enable_intcon_clock();
vic_init((void __iomem *) U300_INTCON0_VBASE, 0, mask[0], mask[0]);
vic_init((void __iomem *) U300_INTCON1_VBASE, 32, mask[1], mask[1]);
}
u300_spi_init(&pl022_device);
/* Register the AMBA devices in the AMBA bus abstraction layer */
- u300_clock_primecells();
for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
struct amba_device *d = amba_devs[i];
amba_device_register(d, &iomem_resource);
}
- u300_unclock_primecells();
u300_assign_physmem();
mdelay(20);
- mmci_card->mmc_inserted = !!gpio_get_value(U300_GPIO_PIN_MMC_CD);
+ mmci_card->mmc_inserted = !gpio_get_value(U300_GPIO_PIN_MMC_CD);
input_report_switch(mmci_card->mmc_input, KEY_INSERT,
- !mmci_card->mmc_inserted);
+ mmci_card->mmc_inserted);
input_sync(mmci_card->mmc_input);
pr_debug("MMC/SD card was %s\n",
- mmci_card->mmc_inserted ? "removed" : "inserted");
+ mmci_card->mmc_inserted ? "inserted" : "removed");
- enable_irq_on_gpio_pin(U300_GPIO_PIN_MMC_CD, !mmci_card->mmc_inserted);
+ enable_irq_on_gpio_pin(U300_GPIO_PIN_MMC_CD, mmci_card->mmc_inserted);
}
int __devinit mmc_init(struct amba_device *adev)
#include <linux/clocksource.h>
#include <linux/types.h>
#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
#include <mach/hardware.h>
#include <asm/mach/time.h>
#include <asm/mach/irq.h>
-#include "clock.h"
+/* Be able to sleep for atleast 4 seconds (usually more) */
+#define APPTIMER_MIN_RANGE 4
/*
* APP side special timer registers
.name = "GPT1",
.rating = 300, /* Reasonably fast and accurate clock event */
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
- /* 22 calculated using the algorithm in arch/mips/kernel/time.c */
- .shift = 22,
.set_next_event = u300_set_next_event,
.set_mode = u300_set_mode,
};
.rating = 300, /* Reasonably fast and accurate clock source */
.read = u300_get_cycles,
.mask = CLOCKSOURCE_MASK(32), /* 32 bits */
- /* 22 calculated using the algorithm in arch/mips/kernel/time.c */
- .shift = 22,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
*/
static void __init u300_timer_init(void)
{
- u300_enable_timer_clock();
+ struct clk *clk;
+ unsigned long rate;
+
+ /* Clock the interrupt controller */
+ clk = clk_get_sys("apptimer", NULL);
+ BUG_ON(IS_ERR(clk));
+ clk_enable(clk);
+ rate = clk_get_rate(clk);
+
/*
* Disable the "OS" and "DD" timers - these are designed for Symbian!
* Example usage in cnh1601578 cpu subsystem pd_timer_app.c
writel(U300_TIMER_APP_EGPT2_TIMER_ENABLE,
U300_TIMER_APP_VBASE + U300_TIMER_APP_EGPT2);
- /* This is a pure microsecond clock source */
- clocksource_u300_1mhz.mult =
- clocksource_khz2mult(1000, clocksource_u300_1mhz.shift);
+ clocksource_calc_mult_shift(&clocksource_u300_1mhz,
+ rate, APPTIMER_MIN_RANGE);
if (clocksource_register(&clocksource_u300_1mhz))
printk(KERN_ERR "timer: failed to initialize clock "
"source %s\n", clocksource_u300_1mhz.name);
- clockevent_u300_1mhz.mult =
- div_sc(1000000, NSEC_PER_SEC, clockevent_u300_1mhz.shift);
+ clockevents_calc_mult_shift(&clockevent_u300_1mhz,
+ rate, APPTIMER_MIN_RANGE);
/* 32bit counter, so 32bits delta is max */
clockevent_u300_1mhz.max_delta_ns =
clockevent_delta2ns(0xffffffff, &clockevent_u300_1mhz);
#define VERSATILE_PCI_VIRT_BASE (void __iomem *)0xe8000000ul
#define VERSATILE_PCI_CFG_VIRT_BASE (void __iomem *)0xe9000000ul
-#if 0
-#define VERSATILE_PCI_VIRT_MEM_BASE0 0xf4000000
-#define VERSATILE_PCI_VIRT_MEM_BASE1 0xf5000000
-#define VERSATILE_PCI_VIRT_MEM_BASE2 0xf6000000
-
-#define PCIO_BASE VERSATILE_PCI_VIRT_MEM_BASE0
-#define PCIMEM_BASE VERSATILE_PCI_VIRT_MEM_BASE1
-#endif
-
/* CIK guesswork */
#define PCIBIOS_MIN_IO 0x44000000
#define PCIBIOS_MIN_MEM 0x50000000
bool "Enable the L2x0 outer cache controller"
depends on REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || MACH_REALVIEW_PB1176 || \
REALVIEW_EB_A9MP || ARCH_MX35 || ARCH_MX31 || MACH_REALVIEW_PBX || \
- ARCH_NOMADIK || ARCH_OMAP4 || ARCH_U8500 || ARCH_VEXPRESS_CA9X4 || \
- ARCH_TEGRA
+ ARCH_NOMADIK || ARCH_OMAP4 || ARCH_S5PV310 || ARCH_TEGRA || \
+ ARCH_U8500 || ARCH_VEXPRESS_CA9X4
default y
select OUTER_CACHE
select OUTER_CACHE_SYNC
#ifndef __ASM_ARCH_MXC_GPIO_H__
#define __ASM_ARCH_MXC_GPIO_H__
+#include <linux/spinlock.h>
#include <mach/hardware.h>
#include <asm-generic/gpio.h>
/* Call back after enabling / disabling regulators */
void (*after_set_reg)(struct device *dev, int slot,
int power_on, int vdd);
+ /* if we have special card, init it using this callback */
+ void (*init_card)(struct mmc_card *card);
/* return MMC cover switch state, can be NULL if not supported.
*
obj-y := dma.o
-obj-$(CONFIG_ARCH_PXA) += pmu.o
obj-$(CONFIG_GENERIC_GPIO) += gpio.o
obj-$(CONFIG_PXA3xx) += mfp.o
obj-$(CONFIG_ARCH_MMP) += mfp.o
+++ /dev/null
-/*
- * PMU IRQ registration for the PXA xscale PMU families.
- * Copyright (C) 2010 Will Deacon, ARM Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/platform_device.h>
-#include <asm/pmu.h>
-#include <mach/irqs.h>
-
-static struct resource pmu_resource = {
- .start = IRQ_PMU,
- .end = IRQ_PMU,
- .flags = IORESOURCE_IRQ,
-};
-
-static struct platform_device pmu_device = {
- .name = "arm-pmu",
- .id = ARM_PMU_DEVICE_CPU,
- .resource = &pmu_resource,
- .num_resources = 1,
-};
-
-static int __init pxa_pmu_init(void)
-{
- platform_device_register(&pmu_device);
- return 0;
-}
-arch_initcall(pxa_pmu_init);
config PLAT_S5P
bool
- depends on (ARCH_S5P6440 || ARCH_S5P6442 || ARCH_S5PC100 || ARCH_S5PV210)
+ depends on (ARCH_S5P6440 || ARCH_S5P6442 || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_S5PV310)
default y
- select ARM_VIC
+ select ARM_VIC if !ARCH_S5PV310
+ select ARM_GIC if ARCH_S5PV310
select NO_IOPORT
select ARCH_REQUIRE_GPIOLIB
select S3C_GPIO_TRACK
help
Use the external interrupts (other than GPIO interrupts.)
Note: Do not choose this for S5P6440.
+
+config S5P_DEV_FIMC0
+ bool
+ help
+ Compile in platform device definitions for FIMC controller 0
+
+config S5P_DEV_FIMC1
+ bool
+ help
+ Compile in platform device definitions for FIMC controller 1
+
+config S5P_DEV_FIMC2
+ bool
+ help
+ Compile in platform device definitions for FIMC controller 2
# Core files
+obj-y += dev-pmu.o
obj-y += dev-uart.o
obj-y += cpu.o
obj-y += clock.o
obj-y += irq.o
obj-$(CONFIG_S5P_EXT_INT) += irq-eint.o
+# devices
+
+obj-$(CONFIG_S5P_DEV_FIMC0) += dev-fimc0.o
+obj-$(CONFIG_S5P_DEV_FIMC1) += dev-fimc1.o
+obj-$(CONFIG_S5P_DEV_FIMC2) += dev-fimc2.o
#include <plat/s5p6442.h>
#include <plat/s5pc100.h>
#include <plat/s5pv210.h>
+#include <plat/s5pv310.h>
/* table of supported CPUs */
static const char name_s5p6442[] = "S5P6442";
static const char name_s5pc100[] = "S5PC100";
static const char name_s5pv210[] = "S5PV210/S5PC110";
+static const char name_s5pv310[] = "S5PV310";
static struct cpu_table cpu_ids[] __initdata = {
{
.init_uarts = s5pv210_init_uarts,
.init = s5pv210_init,
.name = name_s5pv210,
+ }, {
+ .idcode = 0x43200000,
+ .idmask = 0xfffff000,
+ .map_io = s5pv310_map_io,
+ .init_clocks = s5pv310_init_clocks,
+ .init_uarts = s5pv310_init_uarts,
+ .init = s5pv310_init,
+ .name = name_s5pv310,
},
};
}, {
.virtual = (unsigned long)S3C_VA_UART,
.pfn = __phys_to_pfn(S3C_PA_UART),
- .length = SZ_4K,
+ .length = SZ_512K,
.type = MT_DEVICE,
+#ifdef CONFIG_ARM_VIC
}, {
.virtual = (unsigned long)VA_VIC0,
.pfn = __phys_to_pfn(S5P_PA_VIC0),
.pfn = __phys_to_pfn(S5P_PA_VIC1),
.length = SZ_16K,
.type = MT_DEVICE,
+#endif
}, {
.virtual = (unsigned long)S3C_VA_TIMER,
.pfn = __phys_to_pfn(S5P_PA_TIMER),
.pfn = __phys_to_pfn(S5P_PA_GPIO),
.length = SZ_4K,
.type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)S3C_VA_WATCHDOG,
+ .pfn = __phys_to_pfn(S3C_PA_WDT),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
},
};
--- /dev/null
+/* linux/arch/arm/plat-s5p/dev-fimc0.c
+ *
+ * Copyright (c) 2010 Samsung Electronics
+ *
+ * Base S5P FIMC0 resource and device definitions
+ *
+ * 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/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <mach/map.h>
+
+static struct resource s5p_fimc0_resource[] = {
+ [0] = {
+ .start = S5P_PA_FIMC0,
+ .end = S5P_PA_FIMC0 + SZ_1M - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_FIMC0,
+ .end = IRQ_FIMC0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device s5p_device_fimc0 = {
+ .name = "s5p-fimc",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(s5p_fimc0_resource),
+ .resource = s5p_fimc0_resource,
+};
--- /dev/null
+/* linux/arch/arm/plat-s5p/dev-fimc1.c
+ *
+ * Copyright (c) 2010 Samsung Electronics
+ *
+ * Base S5P FIMC1 resource and device definitions
+ *
+ * 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/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <mach/map.h>
+
+static struct resource s5p_fimc1_resource[] = {
+ [0] = {
+ .start = S5P_PA_FIMC1,
+ .end = S5P_PA_FIMC1 + SZ_1M - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_FIMC1,
+ .end = IRQ_FIMC1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device s5p_device_fimc1 = {
+ .name = "s5p-fimc",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(s5p_fimc1_resource),
+ .resource = s5p_fimc1_resource,
+};
--- /dev/null
+/* linux/arch/arm/plat-s5p/dev-fimc2.c
+ *
+ * Copyright (c) 2010 Samsung Electronics
+ *
+ * Base S5P FIMC2 resource and device definitions
+ *
+ * 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/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <mach/map.h>
+
+static struct resource s5p_fimc2_resource[] = {
+ [0] = {
+ .start = S5P_PA_FIMC2,
+ .end = S5P_PA_FIMC2 + SZ_1M - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_FIMC2,
+ .end = IRQ_FIMC2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device s5p_device_fimc2 = {
+ .name = "s5p-fimc",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(s5p_fimc2_resource),
+ .resource = s5p_fimc2_resource,
+};
--- /dev/null
+/*
+ * linux/arch/arm/plat-s5p/dev-pmu.c
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <asm/pmu.h>
+#include <mach/irqs.h>
+
+static struct resource s5p_pmu_resource = {
+ .start = IRQ_PMU,
+ .end = IRQ_PMU,
+ .flags = IORESOURCE_IRQ,
+};
+
+struct platform_device s5p_device_pmu = {
+ .name = "arm-pmu",
+ .id = ARM_PMU_DEVICE_CPU,
+ .num_resources = 1,
+ .resource = &s5p_pmu_resource,
+};
+
+static int __init s5p_pmu_init(void)
+{
+ platform_device_register(&s5p_device_pmu);
+ return 0;
+}
+arch_initcall(s5p_pmu_init);
#define S5P_VA_SYSTIMER S3C_ADDR(0x01200000)
#define S5P_VA_SROMC S3C_ADDR(0x01100000)
-#define S5P_VA_UART0 (S3C_VA_UART + 0x0)
-#define S5P_VA_UART1 (S3C_VA_UART + 0x400)
-#define S5P_VA_UART2 (S3C_VA_UART + 0x800)
-#define S5P_VA_UART3 (S3C_VA_UART + 0xC00)
+#define S5P_VA_COMBINER_BASE S3C_ADDR(0x00600000)
+#define S5P_VA_COMBINER(x) (S5P_VA_COMBINER_BASE + ((x) >> 2) * 0x10)
+#define S5P_VA_COREPERI_BASE S3C_ADDR(0x00800000)
+#define S5P_VA_COREPERI(x) (S5P_VA_COREPERI_BASE + (x))
+#define S5P_VA_SCU S5P_VA_COREPERI(0x0)
+#define S5P_VA_GIC_CPU S5P_VA_COREPERI(0x100)
+#define S5P_VA_TWD S5P_VA_COREPERI(0x600)
+#define S5P_VA_GIC_DIST S5P_VA_COREPERI(0x1000)
+
+#define S5P_VA_L2CC S3C_ADDR(0x00900000)
+
+#define S5P_VA_UART(x) (S3C_VA_UART + ((x) * S3C_UART_OFFSET))
+#define S5P_VA_UART0 S5P_VA_UART(0)
+#define S5P_VA_UART1 S5P_VA_UART(1)
+#define S5P_VA_UART2 S5P_VA_UART(2)
+#define S5P_VA_UART3 S5P_VA_UART(3)
+
+#ifndef S3C_UART_OFFSET
#define S3C_UART_OFFSET (0x400)
+#endif
#define VA_VIC(x) (S3C_VA_IRQ + ((x) * 0x10000))
#define VA_VIC0 VA_VIC(0)
return (unsigned long)fvco;
}
+#define PLL46XX_KDIV_MASK (0xFFFF)
+#define PLL46XX_MDIV_MASK (0x1FF)
+#define PLL46XX_PDIV_MASK (0x3F)
+#define PLL46XX_SDIV_MASK (0x7)
+#define PLL46XX_MDIV_SHIFT (16)
+#define PLL46XX_PDIV_SHIFT (8)
+#define PLL46XX_SDIV_SHIFT (0)
+
+enum pll46xx_type_t {
+ pll_4600,
+ pll_4650,
+};
+
+static inline unsigned long s5p_get_pll46xx(unsigned long baseclk,
+ u32 pll_con0, u32 pll_con1,
+ enum pll46xx_type_t pll_type)
+{
+ unsigned long result;
+ u32 mdiv, pdiv, sdiv, kdiv;
+ u64 tmp;
+
+ mdiv = (pll_con0 >> PLL46XX_MDIV_SHIFT) & PLL46XX_MDIV_MASK;
+ pdiv = (pll_con0 >> PLL46XX_PDIV_SHIFT) & PLL46XX_PDIV_MASK;
+ sdiv = (pll_con0 >> PLL46XX_SDIV_SHIFT) & PLL46XX_SDIV_MASK;
+ kdiv = pll_con1 & PLL46XX_KDIV_MASK;
+
+ tmp = baseclk;
+
+ if (pll_type == pll_4600) {
+ tmp *= (mdiv << 16) + kdiv;
+ do_div(tmp, (pdiv << sdiv));
+ result = tmp >> 16;
+ } else {
+ tmp *= (mdiv << 10) + kdiv;
+ do_div(tmp, (pdiv << sdiv));
+ result = tmp >> 10;
+ }
+
+ return result;
+}
+
#define PLL90XX_MDIV_MASK (0xFF)
#define PLL90XX_PDIV_MASK (0x3F)
#define PLL90XX_SDIV_MASK (0x7)
--- /dev/null
+/* linux/arch/arm/plat-s5p/include/plat/reset.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_PLAT_S5P_RESET_H
+#define __ASM_PLAT_S5P_RESET_H __FILE__
+
+extern void (*s5p_reset_hook)(void);
+
+#endif /* __ASM_PLAT_S5P_RESET_H */
--- /dev/null
+/* linux/arch/arm/plat-s5p/include/plat/s5pv310.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Header file for s5pv310 cpu support
+ *
+ * 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.
+*/
+
+/* Common init code for S5PV310 related SoCs */
+
+extern void s5pv310_common_init_uarts(struct s3c2410_uartcfg *cfg, int no);
+extern void s5pv310_register_clocks(void);
+extern void s5pv310_setup_clocks(void);
+
+#ifdef CONFIG_CPU_S5PV310
+
+extern int s5pv310_init(void);
+extern void s5pv310_init_irq(void);
+extern void s5pv310_map_io(void);
+extern void s5pv310_init_clocks(int xtal);
+extern struct sys_timer s5pv310_timer;
+
+#define s5pv310_init_uarts s5pv310_common_init_uarts
+
+#else
+#define s5pv310_init_clocks NULL
+#define s5pv310_init_uarts NULL
+#define s5pv310_map_io NULL
+#define s5pv310_init NULL
+#endif
--- /dev/null
+/* linux/arch/arm/plat-s5p/include/plat/system-reset.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Based on arch/arm/mach-s3c2410/include/mach/system-reset.h
+ *
+ * S5P - System define for arch_reset()
+ *
+ * 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 <plat/watchdog-reset.h>
+
+void (*s5p_reset_hook)(void);
+
+static void arch_reset(char mode, const char *cmd)
+{
+ /* SWRESET support in s5p_reset_hook() */
+
+ if (s5p_reset_hook)
+ s5p_reset_hook();
+
+ /* Perform reset using Watchdog reset
+ * if there is no s5p_reset_hook()
+ */
+
+ arch_wdt_reset();
+}
void __init s5p_init_irq(u32 *vic, u32 num_vic)
{
+#ifdef CONFIG_ARM_VIC
int irq;
/* initialize the VICs */
for (irq = 0; irq < num_vic; irq++)
vic_init(VA_VIC(irq), VIC_BASE(irq), vic[irq], 0);
+#endif
s3c_init_vic_timer_irq(IRQ_TIMER0_VIC, IRQ_TIMER0);
s3c_init_vic_timer_irq(IRQ_TIMER1_VIC, IRQ_TIMER1);
help
Compile in platform device definitions for HSMMC channel 2
+config S3C_DEV_HSMMC3
+ bool
+ help
+ Compile in platform device definitions for HSMMC channel 3
+
config S3C_DEV_HWMON
bool
help
help
Compile in platform device definition for ADC controller
+config SAMSUNG_DEV_IDE
+ bool
+ help
+ Compile in platform device definitions for IDE
+
config S3C64XX_DEV_SPI
bool
help
help
Common in platform device definitions for touchscreen device
+config SAMSUNG_DEV_KEYPAD
+ bool
+ help
+ Compile in platform device definitions for keypad
+
# DMA
config S3C_DMA
# Objects we always build independent of SoC choice
obj-y += init.o
-obj-y += time.o
+obj-$(CONFIG_ARCH_USES_GETTIMEOFFSET) += time.o
obj-y += clock.o
obj-y += pwm-clock.o
obj-y += gpio.o
# devices
+obj-y += platformdata.o
+
obj-$(CONFIG_S3C_DEV_HSMMC) += dev-hsmmc.o
obj-$(CONFIG_S3C_DEV_HSMMC1) += dev-hsmmc1.o
obj-$(CONFIG_S3C_DEV_HSMMC2) += dev-hsmmc2.o
+obj-$(CONFIG_S3C_DEV_HSMMC3) += dev-hsmmc3.o
obj-$(CONFIG_S3C_DEV_HWMON) += dev-hwmon.o
obj-y += dev-i2c0.o
obj-$(CONFIG_S3C_DEV_I2C1) += dev-i2c1.o
obj-$(CONFIG_S3C_DEV_RTC) += dev-rtc.o
obj-$(CONFIG_SAMSUNG_DEV_ADC) += dev-adc.o
+obj-$(CONFIG_SAMSUNG_DEV_IDE) += dev-ide.o
obj-$(CONFIG_SAMSUNG_DEV_TS) += dev-ts.o
+obj-$(CONFIG_SAMSUNG_DEV_KEYPAD) += dev-keypad.o
# DMA support
struct s3c_sdhci_platdata *set = &s3c_hsmmc0_def_platdata;
set->max_width = pd->max_width;
+ set->cd_type = pd->cd_type;
+ set->ext_cd_init = pd->ext_cd_init;
+ set->ext_cd_cleanup = pd->ext_cd_cleanup;
+ set->ext_cd_gpio = pd->ext_cd_gpio;
+ set->ext_cd_gpio_invert = pd->ext_cd_gpio_invert;
if (pd->cfg_gpio)
set->cfg_gpio = pd->cfg_gpio;
struct s3c_sdhci_platdata *set = &s3c_hsmmc1_def_platdata;
set->max_width = pd->max_width;
+ set->cd_type = pd->cd_type;
+ set->ext_cd_init = pd->ext_cd_init;
+ set->ext_cd_cleanup = pd->ext_cd_cleanup;
+ set->ext_cd_gpio = pd->ext_cd_gpio;
+ set->ext_cd_gpio_invert = pd->ext_cd_gpio_invert;
if (pd->cfg_gpio)
set->cfg_gpio = pd->cfg_gpio;
struct s3c_sdhci_platdata *set = &s3c_hsmmc2_def_platdata;
set->max_width = pd->max_width;
+ set->cd_type = pd->cd_type;
+ set->ext_cd_init = pd->ext_cd_init;
+ set->ext_cd_cleanup = pd->ext_cd_cleanup;
+ set->ext_cd_gpio = pd->ext_cd_gpio;
+ set->ext_cd_gpio_invert = pd->ext_cd_gpio_invert;
if (pd->cfg_gpio)
set->cfg_gpio = pd->cfg_gpio;
--- /dev/null
+/* linux/arch/arm/plat-samsung/dev-hsmmc3.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Copyright (c) 2008 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * Based on arch/arm/plat-samsung/dev-hsmmc1.c
+ *
+ * Samsung device definition for hsmmc device 3
+ *
+ * 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/platform_device.h>
+#include <linux/mmc/host.h>
+
+#include <mach/map.h>
+#include <plat/sdhci.h>
+#include <plat/devs.h>
+
+#define S3C_SZ_HSMMC (0x1000)
+
+static struct resource s3c_hsmmc3_resource[] = {
+ [0] = {
+ .start = S3C_PA_HSMMC3,
+ .end = S3C_PA_HSMMC3 + S3C_SZ_HSMMC - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_MMC3,
+ .end = IRQ_MMC3,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static u64 s3c_device_hsmmc3_dmamask = 0xffffffffUL;
+
+struct s3c_sdhci_platdata s3c_hsmmc3_def_platdata = {
+ .max_width = 4,
+ .host_caps = (MMC_CAP_4_BIT_DATA |
+ MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED),
+};
+
+struct platform_device s3c_device_hsmmc3 = {
+ .name = "s3c-sdhci",
+ .id = 3,
+ .num_resources = ARRAY_SIZE(s3c_hsmmc3_resource),
+ .resource = s3c_hsmmc3_resource,
+ .dev = {
+ .dma_mask = &s3c_device_hsmmc3_dmamask,
+ .coherent_dma_mask = 0xffffffffUL,
+ .platform_data = &s3c_hsmmc3_def_platdata,
+ },
+};
+
+void s3c_sdhci3_set_platdata(struct s3c_sdhci_platdata *pd)
+{
+ struct s3c_sdhci_platdata *set = &s3c_hsmmc3_def_platdata;
+
+ set->max_width = pd->max_width;
+ set->cd_type = pd->cd_type;
+ set->ext_cd_init = pd->ext_cd_init;
+ set->ext_cd_cleanup = pd->ext_cd_cleanup;
+ set->ext_cd_gpio = pd->ext_cd_gpio;
+ set->ext_cd_gpio_invert = pd->ext_cd_gpio_invert;
+
+ if (pd->cfg_gpio)
+ set->cfg_gpio = pd->cfg_gpio;
+ if (pd->cfg_card)
+ set->cfg_card = pd->cfg_card;
+}
--- /dev/null
+/* linux/arch/arm/plat-samsung/dev-ide.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Samsung CF-ATA device definition.
+ *
+ * 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/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <mach/map.h>
+#include <plat/ata.h>
+#include <plat/devs.h>
+
+static struct resource s3c_cfcon_resource[] = {
+ [0] = {
+ .start = SAMSUNG_PA_CFCON,
+ .end = SAMSUNG_PA_CFCON + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_CFCON,
+ .end = IRQ_CFCON,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device s3c_device_cfcon = {
+ .id = 0,
+ .num_resources = ARRAY_SIZE(s3c_cfcon_resource),
+ .resource = s3c_cfcon_resource,
+};
+
+void s3c_ide_set_platdata(struct s3c_ide_platdata *pdata)
+{
+ s3c_set_platdata(pdata, sizeof(struct s3c_ide_platdata),
+ &s3c_device_cfcon);
+}
--- /dev/null
+/*
+ * linux/arch/arm/plat-samsung/dev-keypad.c
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <mach/irqs.h>
+#include <mach/map.h>
+#include <plat/cpu.h>
+#include <plat/devs.h>
+#include <plat/keypad.h>
+
+static struct resource samsung_keypad_resources[] = {
+ [0] = {
+ .start = SAMSUNG_PA_KEYPAD,
+ .end = SAMSUNG_PA_KEYPAD + 0x20 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_KEYPAD,
+ .end = IRQ_KEYPAD,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device samsung_device_keypad = {
+ .name = "samsung-keypad",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(samsung_keypad_resources),
+ .resource = samsung_keypad_resources,
+};
+
+void __init samsung_keypad_set_platdata(struct samsung_keypad_platdata *pd)
+{
+ struct samsung_keypad_platdata *npd;
+
+ npd = s3c_set_platdata(pd, sizeof(struct samsung_keypad_platdata),
+ &samsung_device_keypad);
+
+ if (!npd->cfg_gpio)
+ npd->cfg_gpio = samsung_keypad_cfg_gpio;
+}
static struct resource s3c_wdt_resource[] = {
[0] = {
.start = S3C_PA_WDT,
- .end = S3C_PA_WDT + SZ_1M - 1,
+ .end = S3C_PA_WDT + SZ_1K,
.flags = IORESOURCE_MEM,
},
[1] = {
#include <linux/kernel.h>
#include <linux/irq.h>
#include <linux/io.h>
-#include <mach/gpio.h>
+#include <linux/gpio.h>
#include <plat/gpio-core.h>
#include <plat/gpio-cfg.h>
#include <plat/gpio-cfg-helpers.h>
--- /dev/null
+/* linux/arch/arm/plat-samsung/include/plat/adc-core.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Samsung ADC Controller core functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_PLAT_ADC_CORE_H
+#define __ASM_PLAT_ADC_CORE_H __FILE__
+
+/* These functions are only for use with the core support code, such as
+ * the cpu specific initialisation code
+ */
+
+/* re-define device name depending on support. */
+static inline void s3c_adc_setname(char *name)
+{
+#ifdef CONFIG_SAMSUNG_DEV_ADC
+ s3c_device_adc.name = name;
+#endif
+}
+
+#endif /* __ASM_PLAT_ADC_CORE_H */
--- /dev/null
+/* linux/arch/arm/plat-samsung/include/plat/ata-core.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Samsung CF-ATA Controller core functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_PLAT_ATA_CORE_H
+#define __ASM_PLAT_ATA_CORE_H __FILE__
+
+/* These functions are only for use with the core support code, such as
+ * the cpu specific initialisation code
+*/
+
+/* re-define device name depending on support. */
+static inline void s3c_cfcon_setname(char *name)
+{
+#ifdef CONFIG_SAMSUNG_DEV_IDE
+ s3c_device_cfcon.name = name;
+#endif
+}
+
+#endif /* __ASM_PLAT_ATA_CORE_H */
--- /dev/null
+/* linux/arch/arm/plat-samsung/include/plat/ata.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Samsung CF-ATA platform_device info
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_PLAT_ATA_H
+#define __ASM_PLAT_ATA_H __FILE__
+
+/**
+ * struct s3c_ide_platdata - S3C IDE driver platform data.
+ * @setup_gpio: Setup the external GPIO pins to the right state for data
+ * transfer in true-ide mode.
+ */
+struct s3c_ide_platdata {
+ void (*setup_gpio)(void);
+};
+
+/*
+ * s3c_ide_set_platdata() - Setup the platform specifc data for IDE driver.
+ * @pdata: Platform data for IDE driver.
+ */
+extern void s3c_ide_set_platdata(struct s3c_ide_platdata *pdata);
+
+/* architecture-specific IDE configuration */
+extern void s3c64xx_ide_setup_gpio(void);
+extern void s5pc100_ide_setup_gpio(void);
+extern void s5pv210_ide_setup_gpio(void);
+
+#endif /*__ASM_PLAT_ATA_H */
extern struct platform_device s3c_device_hsmmc0;
extern struct platform_device s3c_device_hsmmc1;
extern struct platform_device s3c_device_hsmmc2;
+extern struct platform_device s3c_device_hsmmc3;
+extern struct platform_device s3c_device_cfcon;
extern struct platform_device s3c_device_spi0;
extern struct platform_device s3c_device_spi1;
extern struct platform_device s5pc100_device_iis1;
extern struct platform_device s5pc100_device_iis2;
+extern struct platform_device samsung_device_keypad;
+
+extern struct platform_device s5p_device_fimc0;
+extern struct platform_device s5p_device_fimc1;
+extern struct platform_device s5p_device_fimc2;
+
/* s3c2440 specific devices */
#ifdef CONFIG_CPU_S3C2440
extern struct platform_device s3c_device_ac97;
#endif
+
+/**
+ * s3c_set_platdata() - helper for setting platform data
+ * @pd: The default platform data for this device.
+ * @pdsize: The size of the platform data.
+ * @pdev: Pointer to the device to fill in.
+ *
+ * This helper replaces a number of calls that copy and then set the
+ * platform data of the device.
+ */
+extern void *s3c_set_platdata(void *pd, size_t pdsize,
+ struct platform_device *pdev);
--- /dev/null
+/*
+ * arch/arm/plat-samsung/include/plat/fb-core.h
+ *
+ * Copyright 2010 Samsung Electronics Co., Ltd.
+ * Pawel Osciak <p.osciak@samsung.com>
+ *
+ * Samsung framebuffer driver core functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __ASM_PLAT_FB_CORE_H
+#define __ASM_PLAT_FB_CORE_H __FILE__
+
+/*
+ * These functions are only for use with the core support code, such as
+ * the CPU-specific initialization code.
+ */
+
+/* Re-define device name depending on support. */
+static inline void s3c_fb_setname(char *name)
+{
+#ifdef CONFIG_S3C_DEV_FB
+ s3c_device_fb.name = name;
+#endif
+}
+
+#endif /* __ASM_PLAT_FB_CORE_H */
* @setup_gpio: Setup the external GPIO pins to the right state to transfer
* the data from the display system to the connected display
* device.
+ * @default_win: default window layer number to be used for UI layer.
* @vidcon0: The base vidcon0 values to control the panel data format.
* @vidcon1: The base vidcon1 values to control the panel data output.
* @win: The setup data for each hardware window, or NULL for unused.
struct s3c_fb_pd_win *win[S3C_FB_MAX_WIN];
+ u32 default_win;
+
u32 vidcon0;
u32 vidcon1;
};
--- /dev/null
+/*
+ * arch/arm/plat-samsung/include/plat/fimc-core.h
+ *
+ * Copyright 2010 Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * Samsung camera interface driver core functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __ASM_PLAT_FIMC_CORE_H
+#define __ASM_PLAT_FIMC_CORE_H __FILE__
+
+/*
+ * These functions are only for use with the core support code, such as
+ * the CPU-specific initialization code.
+ */
+
+/* Re-define device name to differentiate the subsystem in various SoCs. */
+static inline void s3c_fimc_setname(int id, char *name)
+{
+ switch (id) {
+#ifdef CONFIG_S5P_DEV_FIMC0
+ case 0:
+ s5p_device_fimc0.name = name;
+ break;
+#endif
+#ifdef CONFIG_S5P_DEV_FIMC1
+ case 1:
+ s5p_device_fimc1.name = name;
+ break;
+#endif
+#ifdef CONFIG_S5P_DEV_FIMC2
+ case 2:
+ s5p_device_fimc2.name = name;
+ break;
+#endif
+ }
+}
+
+#endif /* __ASM_PLAT_FIMC_CORE_H */
--- /dev/null
+/*
+ * linux/arch/arm/plat-samsung/include/plat/keypad-core.h
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * Samsung keypad controller core function
+ *
+ * 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.
+ *
+ */
+
+#ifndef __ASM_ARCH_KEYPAD_CORE_H
+#define __ASM_ARCH_KEYPAD_CORE_H
+
+/* These function are only for use with the core support code, such as
+ * the cpu specific initialisation code
+ */
+
+/* re-define device name depending on support. */
+static inline void samsung_keypad_setname(char *name)
+{
+#ifdef CONFIG_SAMSUNG_DEV_KEYPAD
+ samsung_device_keypad.name = name;
+#endif
+}
+
+#endif /* __ASM_ARCH_KEYPAD_CORE_H */
void (*cfg_gpio)(unsigned int rows, unsigned int cols);
};
+/**
+ * samsung_keypad_set_platdata - Set platform data for Samsung Keypad device.
+ * @pd: Platform data to register to device.
+ *
+ * Register the given platform data for use with Samsung Keypad device.
+ * The call will copy the platform data, so the board definitions can
+ * make the structure itself __initdata.
+ */
+extern void samsung_keypad_set_platdata(struct samsung_keypad_platdata *pd);
+
+/* defined by architecture to configure gpio. */
+extern void samsung_keypad_cfg_gpio(unsigned int rows, unsigned int cols);
+
#endif /* __PLAT_SAMSUNG_KEYPAD_H */
--- /dev/null
+/* linux/arch/arm/plat-samsung/include/plat/regs-ata.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Samsung CF-ATA register definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_PLAT_REGS_ATA_H
+#define __ASM_PLAT_REGS_ATA_H __FILE__
+
+#define S3C_CFATA_REG(x) (x)
+
+#define S3C_CFATA_MUX S3C_CFATA_REG(0x0)
+
+#define S3C_ATA_CTRL S3C_CFATA_REG(0x0)
+#define S3C_ATA_STATUS S3C_CFATA_REG(0x4)
+#define S3C_ATA_CMD S3C_CFATA_REG(0x8)
+#define S3C_ATA_SWRST S3C_CFATA_REG(0xc)
+#define S3C_ATA_IRQ S3C_CFATA_REG(0x10)
+#define S3C_ATA_IRQ_MSK S3C_CFATA_REG(0x14)
+#define S3C_ATA_CFG S3C_CFATA_REG(0x18)
+
+#define S3C_ATA_MDMA_TIME S3C_CFATA_REG(0x28)
+#define S3C_ATA_PIO_TIME S3C_CFATA_REG(0x2c)
+#define S3C_ATA_UDMA_TIME S3C_CFATA_REG(0x30)
+#define S3C_ATA_XFR_NUM S3C_CFATA_REG(0x34)
+#define S3C_ATA_XFR_CNT S3C_CFATA_REG(0x38)
+#define S3C_ATA_TBUF_START S3C_CFATA_REG(0x3c)
+#define S3C_ATA_TBUF_SIZE S3C_CFATA_REG(0x40)
+#define S3C_ATA_SBUF_START S3C_CFATA_REG(0x44)
+#define S3C_ATA_SBUF_SIZE S3C_CFATA_REG(0x48)
+#define S3C_ATA_CADR_TBUF S3C_CFATA_REG(0x4c)
+#define S3C_ATA_CADR_SBUF S3C_CFATA_REG(0x50)
+#define S3C_ATA_PIO_DTR S3C_CFATA_REG(0x54)
+#define S3C_ATA_PIO_FED S3C_CFATA_REG(0x58)
+#define S3C_ATA_PIO_SCR S3C_CFATA_REG(0x5c)
+#define S3C_ATA_PIO_LLR S3C_CFATA_REG(0x60)
+#define S3C_ATA_PIO_LMR S3C_CFATA_REG(0x64)
+#define S3C_ATA_PIO_LHR S3C_CFATA_REG(0x68)
+#define S3C_ATA_PIO_DVR S3C_CFATA_REG(0x6c)
+#define S3C_ATA_PIO_CSD S3C_CFATA_REG(0x70)
+#define S3C_ATA_PIO_DAD S3C_CFATA_REG(0x74)
+#define S3C_ATA_PIO_READY S3C_CFATA_REG(0x78)
+#define S3C_ATA_PIO_RDATA S3C_CFATA_REG(0x7c)
+
+#define S3C_CFATA_MUX_TRUEIDE 0x01
+
+#define S3C_ATA_CFG_SWAP 0x40
+#define S3C_ATA_CFG_IORDYEN 0x02
+
+#endif /* __ASM_PLAT_REGS_ATA_H */
/* OSD1 and OSD4 do not have register D */
-#define VIDOSD_A(_win) (0x40 + ((_win) * 16))
-#define VIDOSD_B(_win) (0x44 + ((_win) * 16))
-#define VIDOSD_C(_win) (0x48 + ((_win) * 16))
-#define VIDOSD_D(_win) (0x4C + ((_win) * 16))
-
+#define VIDOSD_BASE (0x40)
#define VIDINTCON0 (0x130)
-#define WxKEYCONy(_win, _con) ((0x140 + ((_win) * 8)) + ((_con) * 4))
-
/* WINCONx */
#define WINCONx_CSCWIDTH_MASK (0x3 << 26)
#define WPALCON_W2PAL_16BPP_A555 (1 << 6)
-/* system specific implementation code for palette sizes, and other
- * information that changes depending on which architecture is being
- * compiled.
-*/
-
-/* return true if window _win has OSD register D */
-#define s3c_fb_has_osd_d(_win) ((_win) != 4 && (_win) != 0)
-
-static inline unsigned int s3c_fb_win_pal_size(unsigned int win)
-{
- if (win < 2)
- return 256;
- if (win < 4)
- return 16;
- if (win == 4)
- return 4;
-
- BUG(); /* shouldn't get here */
-}
-
-static inline int s3c_fb_validate_win_bpp(unsigned int win, unsigned int bpp)
-{
- /* all windows can do 1/2 bpp */
-
- if ((bpp == 25 || bpp == 19) && win == 0)
- return 0; /* win 0 does not have 19 or 25bpp modes */
-
- if (bpp == 4 && win == 4)
- return 0;
-
- if (bpp == 8 && (win >= 3))
- return 0; /* win 3/4 cannot do 8bpp in any mode */
-
- return 1;
-}
-
-static inline int s3c_fb_pal_is16(unsigned int window)
-{
- return window > 1;
-}
-
-struct s3c_fb_palette {
- struct fb_bitfield r;
- struct fb_bitfield g;
- struct fb_bitfield b;
- struct fb_bitfield a;
-};
-
-static inline void s3c_fb_init_palette(unsigned int window,
- struct s3c_fb_palette *palette)
-{
- if (window < 2) {
- /* Windows 0/1 are 8/8/8 or A/8/8/8 */
- palette->r.offset = 16;
- palette->r.length = 8;
- palette->g.offset = 8;
- palette->g.length = 8;
- palette->b.offset = 0;
- palette->b.length = 8;
- } else {
- /* currently we assume RGB 5/6/5 */
- palette->r.offset = 11;
- palette->r.length = 5;
- palette->g.offset = 5;
- palette->g.length = 6;
- palette->b.offset = 0;
- palette->b.length = 5;
- }
-}
-
/* Notes on per-window bpp settings
*
* Value Win0 Win1 Win2 Win3 Win 4
#define VIDCON2_ORGYCbCr (1 << 8)
#define VIDCON2_YUVORDCrCb (1 << 7)
+/* PRTCON (S3C6410, S5PC100)
+ * Might not be present in the S3C6410 documentation,
+ * but tests prove it's there almost for sure; shouldn't hurt in any case.
+ */
+#define PRTCON (0x0c)
+#define PRTCON_PROTECT (1 << 11)
+
/* VIDTCON0 */
#define VIDTCON0_VBPDE_MASK (0xff << 24)
#define WINCONx_BITSWP (1 << 18)
#define WINCONx_BYTSWP (1 << 17)
#define WINCONx_HAWSWP (1 << 16)
+#define WINCONx_WSWP (1 << 15)
#define WINCONx_BURSTLEN_MASK (0x3 << 9)
#define WINCONx_BURSTLEN_SHIFT (9)
#define WINCONx_BURSTLEN_16WORD (0x0 << 9)
#define WINCON1_BPPMODE_25BPP_A1888 (0xd << 2)
#define WINCON1_BPPMODE_28BPP_A4888 (0xd << 2)
+/* S5PV210 */
+#define SHADOWCON (0x34)
+#define SHADOWCON_WINx_PROTECT(_win) (1 << (10 + (_win)))
+/* DMA channels (all windows) */
+#define SHADOWCON_CHx_ENABLE(_win) (1 << (_win))
+/* Local input channels (windows 0-2) */
+#define SHADOWCON_CHx_LOCAL_ENABLE(_win) (1 << (5 + (_win)))
#define VIDOSDxA_TOPLEFT_X_MASK (0x7ff << 11)
#define VIDOSDxA_TOPLEFT_X_SHIFT (11)
#define VIDINTCON0_FRAMESEL0_ACTIVE (0x2 << 15)
#define VIDINTCON0_FRAMESEL0_FRONTPORCH (0x3 << 15)
-#define VIDINTCON0_FRAMESEL1 (1 << 14)
-#define VIDINTCON0_FRAMESEL1_NONE (0x0 << 14)
-#define VIDINTCON0_FRAMESEL1_BACKPORCH (0x1 << 14)
-#define VIDINTCON0_FRAMESEL1_VSYNC (0x2 << 14)
-#define VIDINTCON0_FRAMESEL1_FRONTPORCH (0x3 << 14)
+#define VIDINTCON0_FRAMESEL1 (1 << 13)
+#define VIDINTCON0_FRAMESEL1_MASK (0x3 << 13)
+#define VIDINTCON0_FRAMESEL1_NONE (0x0 << 13)
+#define VIDINTCON0_FRAMESEL1_BACKPORCH (0x1 << 13)
+#define VIDINTCON0_FRAMESEL1_VSYNC (0x2 << 13)
+#define VIDINTCON0_FRAMESEL1_FRONTPORCH (0x3 << 13)
#define VIDINTCON0_INT_FRAME (1 << 12)
#define VIDINTCON0_FIFIOSEL_MASK (0x7f << 5)
#define VIDINTCON1_INT_FIFO (1 << 0)
/* Window colour-key control registers */
+#define WKEYCON (0x140) /* 6410,V210 */
+
+#define WKEYCON0 (0x00)
+#define WKEYCON1 (0x04)
#define WxKEYCON0_KEYBL_EN (1 << 26)
#define WxKEYCON0_KEYEN_F (1 << 25)
#define __ASM_ARCH_REGS_RTC_H __FILE__
#define S3C2410_RTCREG(x) (x)
+#define S3C2410_INTP S3C2410_RTCREG(0x30)
+#define S3C2410_INTP_ALM (1 << 1)
+#define S3C2410_INTP_TIC (1 << 0)
#define S3C2410_RTCCON S3C2410_RTCREG(0x40)
#define S3C2410_RTCCON_RTCEN (1<<0)
unsigned short flags;
upf_t uart_flags; /* default uart flags */
+ unsigned int has_fracval;
+
unsigned long ucon; /* value of ucon for port */
unsigned long ulcon; /* value of ulcon for port */
unsigned long ufcon; /* value of ufcon for port */
#define S3C_DIEPMSK S3C_HSOTG_REG(0x810)
+#define S3C_DIEPMSK_TxFIFOEmpty (1 << 7)
#define S3C_DIEPMSK_INEPNakEffMsk (1 << 6)
#define S3C_DIEPMSK_INTknEPMisMsk (1 << 5)
#define S3C_DIEPMSK_INTknTXFEmpMsk (1 << 4)
#define S3C_DIEPDMA(_a) S3C_HSOTG_REG(0x914 + ((_a) * 0x20))
#define S3C_DOEPDMA(_a) S3C_HSOTG_REG(0xB14 + ((_a) * 0x20))
+#define S3C_DTXFSTS(_a) S3C_HSOTG_REG(0x918 + ((_a) * 0x20))
#define S3C_EPFIFO(_a) S3C_HSOTG_REG(0x1000 + ((_a) * 0x1000))
struct mmc_card;
struct mmc_ios;
+enum cd_types {
+ S3C_SDHCI_CD_INTERNAL, /* use mmc internal CD line */
+ S3C_SDHCI_CD_EXTERNAL, /* use external callback */
+ S3C_SDHCI_CD_GPIO, /* use external gpio pin for CD line */
+ S3C_SDHCI_CD_NONE, /* no CD line, use polling to detect card */
+ S3C_SDHCI_CD_PERMANENT, /* no CD line, card permanently wired to host */
+};
+
/**
* struct s3c_sdhci_platdata() - Platform device data for Samsung SDHCI
* @max_width: The maximum number of data bits supported.
* @host_caps: Standard MMC host capabilities bit field.
+ * @cd_type: Type of Card Detection method (see cd_types enum above)
+ * @ext_cd_init: Initialize external card detect subsystem. Called on
+ * sdhci-s3c driver probe when cd_type == S3C_SDHCI_CD_EXTERNAL.
+ * notify_func argument is a callback to the sdhci-s3c driver
+ * that triggers the card detection event. Callback arguments:
+ * dev is pointer to platform device of the host controller,
+ * state is new state of the card (0 - removed, 1 - inserted).
+ * @ext_cd_cleanup: Cleanup external card detect subsystem. Called on
+ * sdhci-s3c driver remove when cd_type == S3C_SDHCI_CD_EXTERNAL.
+ * notify_func argument is the same callback as for ext_cd_init.
+ * @ext_cd_gpio: gpio pin used for external CD line, valid only if
+ * cd_type == S3C_SDHCI_CD_GPIO
+ * @ext_cd_gpio_invert: invert values for external CD gpio line
* @cfg_gpio: Configure the GPIO for a specific card bit-width
* @cfg_card: Configure the interface for a specific card and speed. This
* is necessary the controllers and/or GPIO blocks require the
struct s3c_sdhci_platdata {
unsigned int max_width;
unsigned int host_caps;
+ enum cd_types cd_type;
char **clocks; /* set of clock sources */
+ int ext_cd_gpio;
+ bool ext_cd_gpio_invert;
+ int (*ext_cd_init)(void (*notify_func)(struct platform_device *,
+ int state));
+ int (*ext_cd_cleanup)(void (*notify_func)(struct platform_device *,
+ int state));
+
void (*cfg_gpio)(struct platform_device *dev, int width);
void (*cfg_card)(struct platform_device *dev,
void __iomem *regbase,
extern void s3c_sdhci0_set_platdata(struct s3c_sdhci_platdata *pd);
extern void s3c_sdhci1_set_platdata(struct s3c_sdhci_platdata *pd);
extern void s3c_sdhci2_set_platdata(struct s3c_sdhci_platdata *pd);
+extern void s3c_sdhci3_set_platdata(struct s3c_sdhci_platdata *pd);
/* Default platform data, exported so that per-cpu initialisation can
* set the correct one when there are more than one cpu type selected.
extern struct s3c_sdhci_platdata s3c_hsmmc0_def_platdata;
extern struct s3c_sdhci_platdata s3c_hsmmc1_def_platdata;
extern struct s3c_sdhci_platdata s3c_hsmmc2_def_platdata;
+extern struct s3c_sdhci_platdata s3c_hsmmc3_def_platdata;
/* Helper function availablity */
extern void s5pv210_setup_sdhci0_cfg_gpio(struct platform_device *, int w);
extern void s5pv210_setup_sdhci1_cfg_gpio(struct platform_device *, int w);
extern void s5pv210_setup_sdhci2_cfg_gpio(struct platform_device *, int w);
+extern void s5pv210_setup_sdhci3_cfg_gpio(struct platform_device *, int w);
-/* S3C6400 SDHCI setup */
+/* S3C64XX SDHCI setup */
#ifdef CONFIG_S3C64XX_SETUP_SDHCI
extern char *s3c64xx_hsmmc_clksrcs[4];
-#ifdef CONFIG_S3C_DEV_HSMMC
extern void s3c6400_setup_sdhci_cfg_card(struct platform_device *dev,
void __iomem *r,
struct mmc_ios *ios,
static inline void s3c6400_default_sdhci0(void)
{
+#ifdef CONFIG_S3C_DEV_HSMMC
s3c_hsmmc0_def_platdata.clocks = s3c64xx_hsmmc_clksrcs;
s3c_hsmmc0_def_platdata.cfg_gpio = s3c64xx_setup_sdhci0_cfg_gpio;
s3c_hsmmc0_def_platdata.cfg_card = s3c6400_setup_sdhci_cfg_card;
+#endif
}
-#else
-static inline void s3c6400_default_sdhci0(void) { }
-#endif /* CONFIG_S3C_DEV_HSMMC */
-
-#ifdef CONFIG_S3C_DEV_HSMMC1
static inline void s3c6400_default_sdhci1(void)
{
+#ifdef CONFIG_S3C_DEV_HSMMC1
s3c_hsmmc1_def_platdata.clocks = s3c64xx_hsmmc_clksrcs;
s3c_hsmmc1_def_platdata.cfg_gpio = s3c64xx_setup_sdhci1_cfg_gpio;
s3c_hsmmc1_def_platdata.cfg_card = s3c6400_setup_sdhci_cfg_card;
+#endif
}
-#else
-static inline void s3c6400_default_sdhci1(void) { }
-#endif /* CONFIG_S3C_DEV_HSMMC1 */
-#ifdef CONFIG_S3C_DEV_HSMMC2
static inline void s3c6400_default_sdhci2(void)
{
+#ifdef CONFIG_S3C_DEV_HSMMC2
s3c_hsmmc2_def_platdata.clocks = s3c64xx_hsmmc_clksrcs;
s3c_hsmmc2_def_platdata.cfg_gpio = s3c64xx_setup_sdhci2_cfg_gpio;
s3c_hsmmc2_def_platdata.cfg_card = s3c6400_setup_sdhci_cfg_card;
+#endif
}
-#else
-static inline void s3c6400_default_sdhci2(void) { }
-#endif /* CONFIG_S3C_DEV_HSMMC2 */
-
-/* S3C6410 SDHCI setup */
extern void s3c6410_setup_sdhci_cfg_card(struct platform_device *dev,
void __iomem *r,
struct mmc_ios *ios,
struct mmc_card *card);
-#ifdef CONFIG_S3C_DEV_HSMMC
static inline void s3c6410_default_sdhci0(void)
{
+#ifdef CONFIG_S3C_DEV_HSMMC
s3c_hsmmc0_def_platdata.clocks = s3c64xx_hsmmc_clksrcs;
s3c_hsmmc0_def_platdata.cfg_gpio = s3c64xx_setup_sdhci0_cfg_gpio;
s3c_hsmmc0_def_platdata.cfg_card = s3c6410_setup_sdhci_cfg_card;
+#endif
}
-#else
-static inline void s3c6410_default_sdhci0(void) { }
-#endif /* CONFIG_S3C_DEV_HSMMC */
-#ifdef CONFIG_S3C_DEV_HSMMC1
static inline void s3c6410_default_sdhci1(void)
{
+#ifdef CONFIG_S3C_DEV_HSMMC1
s3c_hsmmc1_def_platdata.clocks = s3c64xx_hsmmc_clksrcs;
s3c_hsmmc1_def_platdata.cfg_gpio = s3c64xx_setup_sdhci1_cfg_gpio;
s3c_hsmmc1_def_platdata.cfg_card = s3c6410_setup_sdhci_cfg_card;
+#endif
}
-#else
-static inline void s3c6410_default_sdhci1(void) { }
-#endif /* CONFIG_S3C_DEV_HSMMC1 */
-#ifdef CONFIG_S3C_DEV_HSMMC2
static inline void s3c6410_default_sdhci2(void)
{
+#ifdef CONFIG_S3C_DEV_HSMMC2
s3c_hsmmc2_def_platdata.clocks = s3c64xx_hsmmc_clksrcs;
s3c_hsmmc2_def_platdata.cfg_gpio = s3c64xx_setup_sdhci2_cfg_gpio;
s3c_hsmmc2_def_platdata.cfg_card = s3c6410_setup_sdhci_cfg_card;
+#endif
}
-#else
-static inline void s3c6410_default_sdhci2(void) { }
-#endif /* CONFIG_S3C_DEV_HSMMC2 */
#else
static inline void s3c6410_default_sdhci0(void) { }
struct mmc_ios *ios,
struct mmc_card *card);
-#ifdef CONFIG_S3C_DEV_HSMMC
static inline void s5pc100_default_sdhci0(void)
{
+#ifdef CONFIG_S3C_DEV_HSMMC
s3c_hsmmc0_def_platdata.clocks = s5pc100_hsmmc_clksrcs;
s3c_hsmmc0_def_platdata.cfg_gpio = s5pc100_setup_sdhci0_cfg_gpio;
s3c_hsmmc0_def_platdata.cfg_card = s5pc100_setup_sdhci0_cfg_card;
+#endif
}
-#else
-static inline void s5pc100_default_sdhci0(void) { }
-#endif /* CONFIG_S3C_DEV_HSMMC */
-#ifdef CONFIG_S3C_DEV_HSMMC1
static inline void s5pc100_default_sdhci1(void)
{
+#ifdef CONFIG_S3C_DEV_HSMMC1
s3c_hsmmc1_def_platdata.clocks = s5pc100_hsmmc_clksrcs;
s3c_hsmmc1_def_platdata.cfg_gpio = s5pc100_setup_sdhci1_cfg_gpio;
s3c_hsmmc1_def_platdata.cfg_card = s5pc100_setup_sdhci0_cfg_card;
+#endif
}
-#else
-static inline void s5pc100_default_sdhci1(void) { }
-#endif /* CONFIG_S3C_DEV_HSMMC1 */
-#ifdef CONFIG_S3C_DEV_HSMMC2
static inline void s5pc100_default_sdhci2(void)
{
+#ifdef CONFIG_S3C_DEV_HSMMC2
s3c_hsmmc2_def_platdata.clocks = s5pc100_hsmmc_clksrcs;
s3c_hsmmc2_def_platdata.cfg_gpio = s5pc100_setup_sdhci2_cfg_gpio;
s3c_hsmmc2_def_platdata.cfg_card = s5pc100_setup_sdhci0_cfg_card;
+#endif
}
-#else
-static inline void s5pc100_default_sdhci2(void) { }
-#endif /* CONFIG_S3C_DEV_HSMMC1 */
-
#else
static inline void s5pc100_default_sdhci0(void) { }
static inline void s5pc100_default_sdhci1(void) { }
static inline void s5pc100_default_sdhci2(void) { }
+
#endif /* CONFIG_S5PC100_SETUP_SDHCI */
+/* S5PV210 SDHCI setup */
-/* S5PC110 SDHCI setup */
#ifdef CONFIG_S5PV210_SETUP_SDHCI
extern char *s5pv210_hsmmc_clksrcs[4];
struct mmc_ios *ios,
struct mmc_card *card);
-#ifdef CONFIG_S3C_DEV_HSMMC
static inline void s5pv210_default_sdhci0(void)
{
+#ifdef CONFIG_S3C_DEV_HSMMC
s3c_hsmmc0_def_platdata.clocks = s5pv210_hsmmc_clksrcs;
s3c_hsmmc0_def_platdata.cfg_gpio = s5pv210_setup_sdhci0_cfg_gpio;
s3c_hsmmc0_def_platdata.cfg_card = s5pv210_setup_sdhci_cfg_card;
+#endif
}
-#else
-static inline void s5pv210_default_sdhci0(void) { }
-#endif /* CONFIG_S3C_DEV_HSMMC */
-#ifdef CONFIG_S3C_DEV_HSMMC1
static inline void s5pv210_default_sdhci1(void)
{
+#ifdef CONFIG_S3C_DEV_HSMMC1
s3c_hsmmc1_def_platdata.clocks = s5pv210_hsmmc_clksrcs;
s3c_hsmmc1_def_platdata.cfg_gpio = s5pv210_setup_sdhci1_cfg_gpio;
s3c_hsmmc1_def_platdata.cfg_card = s5pv210_setup_sdhci_cfg_card;
+#endif
}
-#else
-static inline void s5pv210_default_sdhci1(void) { }
-#endif /* CONFIG_S3C_DEV_HSMMC1 */
-#ifdef CONFIG_S3C_DEV_HSMMC2
static inline void s5pv210_default_sdhci2(void)
{
+#ifdef CONFIG_S3C_DEV_HSMMC2
s3c_hsmmc2_def_platdata.clocks = s5pv210_hsmmc_clksrcs;
s3c_hsmmc2_def_platdata.cfg_gpio = s5pv210_setup_sdhci2_cfg_gpio;
s3c_hsmmc2_def_platdata.cfg_card = s5pv210_setup_sdhci_cfg_card;
+#endif
+}
+
+static inline void s5pv210_default_sdhci3(void)
+{
+#ifdef CONFIG_S3C_DEV_HSMMC3
+ s3c_hsmmc3_def_platdata.clocks = s5pv210_hsmmc_clksrcs;
+ s3c_hsmmc3_def_platdata.cfg_gpio = s5pv210_setup_sdhci3_cfg_gpio;
+ s3c_hsmmc3_def_platdata.cfg_card = s5pv210_setup_sdhci_cfg_card;
+#endif
}
-#else
-static inline void s5pv210_default_sdhci2(void) { }
-#endif /* CONFIG_S3C_DEV_HSMMC2 */
#else
static inline void s5pv210_default_sdhci0(void) { }
static inline void s5pv210_default_sdhci1(void) { }
static inline void s5pv210_default_sdhci2(void) { }
-#endif /* CONFIG_S5PC100_SETUP_SDHCI */
-
-
+static inline void s5pv210_default_sdhci3(void) { }
+#endif /* CONFIG_S5PV210_SETUP_SDHCI */
#endif /* __PLAT_S3C_SDHCI_H */
--- /dev/null
+/* linux/arch/arm/plat-samsung/platformdata.c
+ *
+ * Copyright 2010 Ben Dooks <ben-linux <at> fluff.org>
+ *
+ * Helper for platform data setting
+ *
+ * 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/string.h>
+#include <linux/platform_device.h>
+
+#include <plat/devs.h>
+
+void __init *s3c_set_platdata(void *pd, size_t pdsize,
+ struct platform_device *pdev)
+{
+ void *npd;
+
+ if (!pd) {
+ /* too early to use dev_name(), may not be registered */
+ printk(KERN_ERR "%s: no platform data supplied\n", pdev->name);
+ return NULL;
+ }
+
+ npd = kmemdup(pd, pdsize, GFP_KERNEL);
+ if (!npd) {
+ printk(KERN_ERR "%s: cannot clone platform data\n", pdev->name);
+ return NULL;
+ }
+
+ pdev->dev.platform_data = npd;
+ return npd;
+}
* cache before the transfer is done, causing old data to be seen by
* the CPU.
*/
-#define ARCH_KMALLOC_MINALIGN L1_CACHE_BYTES
+#define ARCH_DMA_MINALIGN L1_CACHE_BYTES
#ifndef __ASSEMBLER__
struct cache_info {
#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
-static inline int dma_is_consistent(struct device *dev, dma_addr_t dma_addr)
-{
- return 1;
-}
-
-static inline int dma_get_cache_alignment(void)
-{
- return boot_cpu_data.dcache.linesz;
-}
-
#endif /* __ASM_AVR32_DMA_MAPPING_H */
#define TCSETSF2 _IOW('T',0x2D, struct termios2)
#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
+#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */
#define TIOCGRS485 0x542E
#define TIOCSRS485 0x542F
#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
-#define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
-#define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
#define FIOQSIZE 0x5460
/* Used for packet mode */
#define TIOCPKT_START 8
#define TIOCPKT_NOSTOP 16
#define TIOCPKT_DOSTOP 32
+#define TIOCPKT_IOCTL 64
#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
#include <asm-generic/scatterlist.h>
-#define ISA_DMA_THRESHOLD (0xffffffff)
-
#endif /* __ASM_AVR32_SCATTERLIST_H */
#define FLUSHO 0010000
#define PENDIN 0040000
#define IEXTEN 0100000
+#define EXTPROC 0200000
/* tcflow() and TCXONC use these */
#define TCOOFF 0
#define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT)
#define SMP_CACHE_BYTES L1_CACHE_BYTES
-#define ARCH_KMALLOC_MINALIGN L1_CACHE_BYTES
+#define ARCH_DMA_MINALIGN L1_CACHE_BYTES
#ifdef CONFIG_SMP
#define __cacheline_aligned
#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
#define dma_supported(d, m) (1)
-#define dma_get_cache_alignment() (32)
-#define dma_is_consistent(d, h) (1)
static inline int
dma_set_mask(struct device *dev, u64 dma_mask)
#include <asm-generic/scatterlist.h>
-#define ISA_DMA_THRESHOLD (0xffffffff)
-
#endif /* !(_BLACKFIN_SCATTERLIST_H) */
return 0;
}
-static inline int
-dma_get_cache_alignment(void)
-{
- return (1 << INTERNODE_CACHE_SHIFT);
-}
-
-#define dma_is_consistent(d, h) (1)
-
static inline void
dma_cache_sync(struct device *dev, void *vaddr, size_t size,
enum dma_data_direction direction)
#define TCSETSF2 _IOW('T',0x2D, struct termios2)
#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
+#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */
#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */
#define FIOCLEX 0x5451
#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
-#define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
-#define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
#define FIOQSIZE 0x5460
#define TIOCSERSETRS485 0x5461 /* enable rs-485 (deprecated) */
#define TIOCPKT_START 8
#define TIOCPKT_NOSTOP 16
#define TIOCPKT_DOSTOP 32
+#define TIOCPKT_IOCTL 64
#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
#include <asm-generic/scatterlist.h>
-#define ISA_DMA_THRESHOLD (0x1fffffff)
-
#endif /* !(__ASM_CRIS_SCATTERLIST_H) */
#define FLUSHO 0010000
#define PENDIN 0040000
#define IEXTEN 0100000
+#define EXTPROC 0200000
/* tcflow() and TCXONC use these */
#define TCOOFF 0
return 0;
}
-static inline
-int dma_get_cache_alignment(void)
-{
- return 1 << L1_CACHE_SHIFT;
-}
-
-#define dma_is_consistent(d, h) (1)
-
static inline
void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
enum dma_data_direction direction)
#define TCSETSF2 _IOW('T',0x2D, struct termios2)
#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
+#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */
#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */
#define FIOCLEX 0x5451
#define TIOCPKT_START 8
#define TIOCPKT_NOSTOP 16
#define TIOCPKT_DOSTOP 32
+#define TIOCPKT_IOCTL 64
#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
* the slab must be aligned such that load- and store-double instructions don't
* fault if used
*/
-#define ARCH_KMALLOC_MINALIGN L1_CACHE_BYTES
+#define ARCH_DMA_MINALIGN L1_CACHE_BYTES
#define ARCH_SLAB_MINALIGN L1_CACHE_BYTES
/*****************************************************************************/
#include <asm-generic/scatterlist.h>
-#define ISA_DMA_THRESHOLD (0xffffffffUL)
-
#endif /* !_ASM_SCATTERLIST_H */
#define FLUSHO 0010000
#define PENDIN 0040000
#define IEXTEN 0100000
+#define EXTPROC 0200000
/* tcflow() and TCXONC use these */
#define TCSETSF2 _IOW('T',0x2D, struct termios2)
#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
+#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */
#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */
#define FIOCLEX 0x5451
#define TIOCPKT_START 8
#define TIOCPKT_NOSTOP 16
#define TIOCPKT_DOSTOP 32
+#define TIOCPKT_IOCTL 64
#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
#include <asm-generic/scatterlist.h>
-#define ISA_DMA_THRESHOLD (0xffffffff)
-
#endif /* !(_H8300_SCATTERLIST_H) */
#define FLUSHO 0010000
#define PENDIN 0040000
#define IEXTEN 0100000
+#define EXTPROC 0200000
/* tcflow() and TCXONC use these */
return daddr;
}
-extern int dma_get_cache_alignment(void);
-
static inline void
dma_cache_sync (struct device *dev, void *vaddr, size_t size,
enum dma_data_direction dir)
mb();
}
-#define dma_is_consistent(d, h) (1) /* all we do is coherent memory... */
-
#endif /* _ASM_IA64_DMA_MAPPING_H */
#define TCSETSF2 _IOW('T',0x2D, struct termios2)
#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
+#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */
#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */
#define FIOCLEX 0x5451
#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
-#define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
-#define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
#define FIOQSIZE 0x5460
/* Used for packet mode */
#define TIOCPKT_START 8
#define TIOCPKT_NOSTOP 16
#define TIOCPKT_DOSTOP 32
+#define TIOCPKT_IOCTL 64
#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
#define _ASM_IA64_SCATTERLIST_H
#include <asm-generic/scatterlist.h>
-/*
- * It used to be that ISA_DMA_THRESHOLD had something to do with the
- * DMA-limits of ISA-devices. Nowadays, its only remaining use (apart
- * from the aha1542.c driver, which isn't 64-bit clean anyhow) is to
- * tell the block-layer (via BLK_BOUNCE_ISA) what the max. physical
- * address of a page is that is allocated with GFP_DMA. On IA-64,
- * that's 4GB - 1.
- */
-#define ISA_DMA_THRESHOLD 0xffffffff
#define ARCH_HAS_SG_CHAIN
#endif /* _ASM_IA64_SCATTERLIST_H */
#define FLUSHO 0010000
#define PENDIN 0040000
#define IEXTEN 0100000
+#define EXTPROC 0200000
/* tcflow() and TCXONC use these */
#define TCOOFF 0
unsigned long ia64_max_cacheline_size;
-int dma_get_cache_alignment(void)
-{
- return ia64_max_cacheline_size;
-}
-EXPORT_SYMBOL(dma_get_cache_alignment);
-
unsigned long ia64_iobase; /* virtual address for I/O accesses */
EXPORT_SYMBOL(ia64_iobase);
struct io_space io_space[MAX_IO_SPACES];
#define TCSETSF2 _IOW('T',0x2D, struct termios2)
#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
+#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */
#define FIONCLEX 0x5450
#define FIOCLEX 0x5451
#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
-#define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
-#define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
#define FIOQSIZE 0x5460
/* Used for packet mode */
#define TIOCPKT_START 8
#define TIOCPKT_NOSTOP 16
#define TIOCPKT_DOSTOP 32
+#define TIOCPKT_IOCTL 64
#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
#include <asm-generic/scatterlist.h>
-#define ISA_DMA_THRESHOLD (0x1fffffff)
-
#endif /* _ASM_M32R_SCATTERLIST_H */
#define FLUSHO 0010000
#define PENDIN 0040000
#define IEXTEN 0100000
+#define EXTPROC 0200000
/* tcflow() and TCXONC use these */
#define TCOOFF 0
#define L1_CACHE_SHIFT 4
#define L1_CACHE_BYTES (1<< L1_CACHE_SHIFT)
-#define ARCH_KMALLOC_MINALIGN L1_CACHE_BYTES
+#define ARCH_DMA_MINALIGN L1_CACHE_BYTES
#endif
return 0;
}
-static inline int dma_get_cache_alignment(void)
-{
- return 1 << L1_CACHE_SHIFT;
-}
-
-static inline int dma_is_consistent(struct device *dev, dma_addr_t dma_addr)
-{
- return 0;
-}
-
extern void *dma_alloc_coherent(struct device *, size_t,
dma_addr_t *, gfp_t);
extern void dma_free_coherent(struct device *, size_t,
#define TCSETSF2 _IOW('T',0x2D, struct termios2)
#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
+#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */
#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */
#define FIOCLEX 0x5451
#define TIOCPKT_START 8
#define TIOCPKT_NOSTOP 16
#define TIOCPKT_DOSTOP 32
+#define TIOCPKT_IOCTL 64
#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
#include <asm-generic/scatterlist.h>
-/* This is bogus and should go away. */
-#define ISA_DMA_THRESHOLD (0x00ffffff)
-
#endif /* !(_M68K_SCATTERLIST_H) */
#define FLUSHO 0010000
#define PENDIN 0040000
#define IEXTEN 0100000
+#define EXTPROC 0200000
/* tcflow() and TCXONC use these */
.scc_type = MAC_SCC_II,
.nubus_type = MAC_NUBUS,
.floppy_type = MAC_FLOPPY_SWIM_ADDR2,
+ }, {
+ .ident = MAC_MODEL_CCLII,
+ .name = "Color Classic II",
+ .adb_type = MAC_ADB_CUDA,
+ .via_type = MAC_VIA_IIci,
+ .scsi_type = MAC_SCSI_OLD,
+ .scc_type = MAC_SCC_II,
+ .nubus_type = MAC_NUBUS,
+ .floppy_type = MAC_FLOPPY_SWIM_ADDR2,
},
/*
#define cuda_write_pram NULL
#endif
-#if 0 /* def CONFIG_ADB_PMU68K */
+#ifdef CONFIG_ADB_PMU68K
static long pmu_read_time(void)
{
struct adb_request req;
while (!req.complete)
pmu_poll();
- time = (req.reply[0] << 24) | (req.reply[1] << 16)
- | (req.reply[2] << 8) | req.reply[3];
+ time = (req.reply[1] << 24) | (req.reply[2] << 16)
+ | (req.reply[3] << 8) | req.reply[4];
return time - RTC_OFFSET;
}
unsigned char dfc;
GET_DFC(dfc);
- SET_DFC(FC_CONTROL);
- SET_CONTROL_BYTE(AC_LEDS,byte);
+ SET_DFC(FC_CONTROL);
+ SET_CONTROL_BYTE(AC_LEDS, byte);
SET_DFC(dfc);
}
#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
-#define dma_is_consistent(d, h) (1)
static inline void *dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag)
ops->free_coherent(dev, size, cpu_addr, dma_handle);
}
-static inline int dma_get_cache_alignment(void)
-{
- return L1_CACHE_BYTES;
-}
-
static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
enum dma_data_direction direction)
{
#ifndef __ASSEMBLY__
/* MS be sure that SLAB allocates aligned objects */
-#define ARCH_KMALLOC_MINALIGN L1_CACHE_BYTES
+#define ARCH_DMA_MINALIGN L1_CACHE_BYTES
#define ARCH_SLAB_MINALIGN L1_CACHE_BYTES
#include <asm-generic/scatterlist.h>
-
-#define ISA_DMA_THRESHOLD (~0UL)
return 0;
}
-static inline int
-dma_get_cache_alignment(void)
-{
- /* XXX Largest on any MIPS */
- return 128;
-}
-
-extern int dma_is_consistent(struct device *dev, dma_addr_t dma_addr);
-
extern void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
enum dma_data_direction direction);
#define TIOCPKT_START 0x08 /* start output */
#define TIOCPKT_NOSTOP 0x10 /* no more ^S, ^Q */
#define TIOCPKT_DOSTOP 0x20 /* now do ^S ^Q */
-/* #define TIOCPKT_IOCTL 0x40 state change of pty driver */
+#define TIOCPKT_IOCTL 0x40 /* state change of pty driver */
#define TIOCSWINSZ _IOW('t', 103, struct winsize) /* set window size */
#define TIOCGWINSZ _IOR('t', 104, struct winsize) /* get window size */
#define TIOCNOTTY 0x5471 /* void tty association */
#define TCSETSF2 _IOW('T', 0x2D, struct termios2)
#define TIOCGPTN _IOR('T', 0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T', 0x31, int) /* Lock/unlock Pty */
+#define TIOCSIG _IOW('T', 0x36, int) /* Generate signal on Pty slave */
/* I hope the range from 0x5480 on is free ... */
#define TIOCSCTTY 0x5480 /* become controlling tty */
#define TIOCSERSETMULTI 0x5490 /* Set multiport config */
#define TIOCMIWAIT 0x5491 /* wait for a change on serial input line(s) */
#define TIOCGICOUNT 0x5492 /* read serial port inline interrupt counts */
-#define TIOCGHAYESESP 0x5493 /* Get Hayes ESP configuration */
-#define TIOCSHAYESESP 0x5494 /* Set Hayes ESP configuration */
#endif /* __ASM_IOCTLS_H */
* Total overkill for most systems but need as a safe default.
* Set this one if any device in the system might do non-coherent DMA.
*/
-#define ARCH_KMALLOC_MINALIGN 128
+#define ARCH_DMA_MINALIGN 128
#endif
#endif /* __ASM_MACH_GENERIC_KMALLOC_H */
#define __ASM_MACH_IP27_KMALLOC_H
/*
- * All happy, no need to define ARCH_KMALLOC_MINALIGN
+ * All happy, no need to define ARCH_DMA_MINALIGN
*/
#endif /* __ASM_MACH_IP27_KMALLOC_H */
#if defined(CONFIG_CPU_R5000) || defined(CONFIG_CPU_RM7000)
-#define ARCH_KMALLOC_MINALIGN 32
+#define ARCH_DMA_MINALIGN 32
#else
-#define ARCH_KMALLOC_MINALIGN 128
+#define ARCH_DMA_MINALIGN 128
#endif
#endif /* __ASM_MACH_IP32_KMALLOC_H */
#include <asm-generic/scatterlist.h>
-#define ISA_DMA_THRESHOLD (0x00ffffffUL)
-
#endif /* __ASM_SCATTERLIST_H */
#define PENDIN 0040000 /* Retype pending input (state). */
#define TOSTOP 0100000 /* Send SIGTTOU for background output. */
#define ITOSTOP TOSTOP
+#define EXTPROC 0200000 /* External processing on pty */
/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
EXPORT_SYMBOL(dma_supported);
-int dma_is_consistent(struct device *dev, dma_addr_t dma_addr)
-{
- return plat_device_is_coherent(dev);
-}
-
-EXPORT_SYMBOL(dma_is_consistent);
-
void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
enum dma_data_direction direction)
{
#define L1_CACHE_DISPARITY L1_CACHE_NENTRIES * L1_CACHE_BYTES
#endif
-#define ARCH_KMALLOC_MINALIGN L1_CACHE_BYTES
+#define ARCH_DMA_MINALIGN L1_CACHE_BYTES
/* data cache purge registers
* - read from the register to unconditionally purge that cache line
return 0;
}
-static inline
-int dma_get_cache_alignment(void)
-{
- return 1 << L1_CACHE_SHIFT;
-}
-
-#define dma_is_consistent(d) (1)
-
static inline
void dma_cache_sync(void *vaddr, size_t size,
enum dma_data_direction direction)
#define TIOCGPTN _IOR('T', 0x30, unsigned int) /* Get Pty Number
* (of pty-mux device) */
#define TIOCSPTLCK _IOW('T', 0x31, int) /* Lock/unlock Pty */
+#define TIOCSIG _IOW('T', 0x36, int) /* Generate signal on Pty slave */
#define FIONCLEX 0x5450
#define FIOCLEX 0x5451
#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
-#define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
-#define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
#define FIOQSIZE 0x5460
/* Used for packet mode */
#define TIOCPKT_START 8
#define TIOCPKT_NOSTOP 16
#define TIOCPKT_DOSTOP 32
+#define TIOCPKT_IOCTL 64
#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
#include <asm-generic/scatterlist.h>
-#define ISA_DMA_THRESHOLD (0x00ffffff)
-
#endif /* _ASM_SCATTERLIST_H */
#define FLUSHO 0010000
#define PENDIN 0040000
#define IEXTEN 0100000
+#define EXTPROC 0200000
/* tcflow() and TCXONC use these */
#define TCOOFF 0
#define SMP_CACHE_BYTES L1_CACHE_BYTES
+#define ARCH_DMA_MINALIGN L1_CACHE_BYTES
+
#define __read_mostly __attribute__((__section__(".data..read_mostly")))
void parisc_cache_init(void); /* initializes cache-flushing */
return 0;
}
-static inline int
-dma_get_cache_alignment(void)
-{
- return dcache_stride;
-}
-
-static inline int
-dma_is_consistent(struct device *dev, dma_addr_t dma_addr)
-{
- return (hppa_dma_ops->dma_sync_single_for_cpu == NULL);
-}
-
static inline void
dma_cache_sync(struct device *dev, void *vaddr, size_t size,
enum dma_data_direction direction)
#define TCSETSF2 _IOW('T',0x2D, struct termios2)
#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
+#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */
#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */
#define FIOCLEX 0x5451
#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
-#define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
-#define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
#define FIOQSIZE 0x5460 /* Get exact space used by quota */
#define TIOCSTART 0x5461
#define TIOCPKT_START 8
#define TIOCPKT_NOSTOP 16
#define TIOCPKT_DOSTOP 32
+#define TIOCPKT_IOCTL 64
#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
#include <asm/types.h>
#include <asm-generic/scatterlist.h>
-#define ISA_DMA_THRESHOLD (~0UL)
#define sg_virt_addr(sg) ((unsigned long)sg_virt(sg))
#endif /* _ASM_PARISC_SCATTERLIST_H */
#define FLUSHO 0010000
#define PENDIN 0040000
#define IEXTEN 0100000
+#define EXTPROC 0200000
/* tcflow() and TCXONC use these */
#define TCOOFF 0
reg = <0x114000 0x1000>;
interrupts = <48 2>;
interrupt-parent = <&mpic>;
+ voltage-ranges = <3300 3300>;
+ sdhci,auto-cmd12;
};
i2c@118000 {
#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
-#ifdef CONFIG_NOT_COHERENT_CACHE
-#define dma_is_consistent(d, h) (0)
-#else
-#define dma_is_consistent(d, h) (1)
-#endif
-
-static inline int dma_get_cache_alignment(void)
-{
-#ifdef CONFIG_PPC64
- /* no easy way to get cache size on all processors, so return
- * the maximum possible, to be safe */
- return (1 << INTERNODE_CACHE_SHIFT);
-#else
- /*
- * Each processor family will define its own L1_CACHE_SHIFT,
- * L1_CACHE_BYTES wraps to this, so this is always safe.
- */
- return L1_CACHE_BYTES;
-#endif
-}
static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
enum dma_data_direction direction)
# define TIOCPKT_START 8
# define TIOCPKT_NOSTOP 16
# define TIOCPKT_DOSTOP 32
+# define TIOCPKT_IOCTL 64
#define TIOCNOTTY 0x5422
#define TIOCSRS485 0x542f
#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
+#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */
#define TIOCSERCONFIG 0x5453
#define TIOCSERGWILD 0x5454
#define VM_DATA_DEFAULT_FLAGS VM_DATA_DEFAULT_FLAGS32
#ifdef CONFIG_NOT_COHERENT_CACHE
-#define ARCH_KMALLOC_MINALIGN L1_CACHE_BYTES
+#define ARCH_DMA_MINALIGN L1_CACHE_BYTES
#endif
#ifdef CONFIG_PTE_64BIT
#include <asm/dma.h>
#include <asm-generic/scatterlist.h>
-#ifdef __powerpc64__
-#define ISA_DMA_THRESHOLD (~0UL)
-#endif
#define ARCH_HAS_SG_CHAIN
#endif /* _ASM_POWERPC_SCATTERLIST_H */
#define FLUSHO 0x00800000
#define PENDIN 0x20000000
#define IEXTEN 0x00000400
+#define EXTPROC 0x10000000
/* Values for the ACTION argument to `tcflow'. */
#define TCOOFF 0
extern struct ccw_device *ccw_device_probe_console(void);
extern int ccw_device_force_console(void);
+int ccw_device_siosl(struct ccw_device *);
+
// FIXME: these have to go
extern int _ccw_device_get_subchannel_number(struct ccw_device *);
#define TCSETSF2 _IOW('T',0x2D, struct termios2)
#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
+#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */
#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */
#define FIOCLEX 0x5451
#define TIOCPKT_START 8
#define TIOCPKT_NOSTOP 16
#define TIOCPKT_DOSTOP 32
+#define TIOCPKT_IOCTL 64
#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
-#define ISA_DMA_THRESHOLD (~0UL)
-
#include <asm-generic/scatterlist.h>
};
#endif
-#define SD_MC_INIT SD_CPU_INIT
-
#include <asm-generic/topology.h>
#endif /* _ASM_S390_TOPOLOGY_H */
l %r1,.Lstartup
br %r1
-.Linitrd:.long _end + 0x400000 # default address of initrd
+.Linitrd:.long _end # default address of initrd
.Lparm: .long PARMAREA
.Lstartup: .long startup
.Lreset:.byte 0xc3,0xc8,0xc1,0xd5,0xc7,0xc5,0x40,0xd9,0xc4,0xd9,0x40
.notifier_call = cmm_power_event,
};
-static int cmm_init(void)
+static int __init cmm_init(void)
{
int rc = -ENOMEM;
if (!cmm_sysctl_header)
goto out_sysctl;
#ifdef CONFIG_CMM_IUCV
+ /* convert sender to uppercase characters */
+ if (sender) {
+ int len = strlen(sender);
+ while (len--)
+ sender[len] = toupper(sender[len]);
+ }
+
rc = smsg_register_callback(SMSG_PREFIX, cmm_smsg_target);
if (rc < 0)
goto out_smsg;
}
module_init(cmm_init);
-static void cmm_exit(void)
+static void __exit cmm_exit(void)
{
unregister_sysctl_table(cmm_sysctl_header);
#ifdef CONFIG_CMM_IUCV
#ifndef _ASM_SCORE_SCATTERLIST_H
#define _ASM_SCORE_SCATTERLIST_H
-#define ISA_DMA_THRESHOLD (~0UL)
-
#include <asm-generic/scatterlist.h>
#endif /* _ASM_SCORE_SCATTERLIST_H */
#define PORT_DRVCRA 0xA405018A
#define PORT_DRVCRB 0xA405018C
-static void ap320_wvga_power_on(void *board_data)
+static void ap320_wvga_power_on(void *board_data, struct fb_info *info)
{
msleep(100);
-obj-y := setup.o sdram.o
-obj-$(CONFIG_FB_SH_MOBILE_LCDC) += lcd_wqvga.o
+obj-y := setup.o sdram.o
+ifneq ($(CONFIG_FB_SH_MOBILE_LCDC),)
+obj-y += lcd_wqvga.o
+endif
return 0;
}
-void kfr2r09_lcd_on(void *board_data)
+void kfr2r09_lcd_on(void *board_data, struct fb_info *info)
{
kfr2r09_lcd_backlight(1);
}
#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
-#ifdef CONFIG_DMA_COHERENT
-#define dma_is_consistent(d, h) (1)
-#else
-#define dma_is_consistent(d, h) (0)
-#endif
-
-static inline int dma_get_cache_alignment(void)
-{
- /*
- * Each processor family will define its own L1_CACHE_SHIFT,
- * L1_CACHE_BYTES wraps to this, so this is always safe.
- */
- return L1_CACHE_BYTES;
-}
-
static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
{
struct dma_map_ops *ops = get_dma_ops(dev);
# define TIOCPKT_START 8
# define TIOCPKT_NOSTOP 16
# define TIOCPKT_DOSTOP 32
+# define TIOCPKT_IOCTL 64
#define TIOCNOTTY _IO('T', 34) /* 0x5422 */
#define TCSETSF2 _IOW('T', 45, struct termios2)
#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
+#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */
#define TIOCSERCONFIG _IO('T', 83) /* 0x5453 */
#define TIOCSERGWILD _IOR('T', 84, int) /* 0x5454 */
* Some drivers need to perform DMA into kmalloc'ed buffers
* and so we have to increase the kmalloc minalign for this.
*/
-#define ARCH_KMALLOC_MINALIGN L1_CACHE_BYTES
+#define ARCH_DMA_MINALIGN L1_CACHE_BYTES
#ifdef CONFIG_SUPERH64
/*
#ifndef __ASM_SH_SCATTERLIST_H
#define __ASM_SH_SCATTERLIST_H
-#define ISA_DMA_THRESHOLD phys_addr_mask()
-
#include <asm-generic/scatterlist.h>
#endif /* __ASM_SH_SCATTERLIST_H */
#include <video/sh_mobile_lcdc.h>
-#ifdef CONFIG_FB_SH_MOBILE_LCDC
-void kfr2r09_lcd_on(void *board_data);
+#if defined(CONFIG_FB_SH_MOBILE_LCDC) || defined(CONFIG_FB_SH_MOBILE_LCDC_MODULE)
+void kfr2r09_lcd_on(void *board_data, struct fb_info *info);
void kfr2r09_lcd_off(void *board_data);
int kfr2r09_lcd_setup(void *board_data, void *sys_ops_handle,
struct sh_mobile_lcdc_sys_bus_ops *sys_ops);
void kfr2r09_lcd_start(void *board_data, void *sys_ops_handle,
struct sh_mobile_lcdc_sys_bus_ops *sys_ops);
#else
-static inline void kfr2r09_lcd_on(void *board_data) {}
-static inline void kfr2r09_lcd_off(void *board_data) {}
-static inline int kfr2r09_lcd_setup(void *board_data, void *sys_ops_handle,
- struct sh_mobile_lcdc_sys_bus_ops *sys_ops)
+static void kfr2r09_lcd_on(void *board_data) {}
+static void kfr2r09_lcd_off(void *board_data) {}
+static int kfr2r09_lcd_setup(void *board_data, void *sys_ops_handle,
+ struct sh_mobile_lcdc_sys_bus_ops *sys_ops)
{
return -ENODEV;
}
-static inline void kfr2r09_lcd_start(void *board_data, void *sys_ops_handle,
- struct sh_mobile_lcdc_sys_bus_ops *sys_ops)
+static void kfr2r09_lcd_start(void *board_data, void *sys_ops_handle,
+ struct sh_mobile_lcdc_sys_bus_ops *sys_ops)
{
}
#endif
#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
-#define dma_is_consistent(d, h) (1)
extern struct dma_map_ops *dma_ops, pci32_dma_ops;
extern struct bus_type pci_bus_type;
return (dma_addr == DMA_ERROR_CODE);
}
-static inline int dma_get_cache_alignment(void)
-{
- /*
- * no easy way to get cache size on all processors, so return
- * the maximum possible, to be safe
- */
- return (1 << INTERNODE_CACHE_SHIFT);
-}
-
static inline int dma_set_mask(struct device *dev, u64 mask)
{
#ifdef CONFIG_PCI
/* Get minor device of a pty master's FD -- Solaris equiv is ISPTM */
#define TIOCGPTN _IOR('t', 134, unsigned int) /* Get Pty Number */
#define TIOCSPTLCK _IOW('t', 135, int) /* Lock/unlock PTY */
+#define TIOCSIG _IOW('t', 136, int) /* Generate signal on Pty slave */
/* Little f */
#define FIOCLEX _IO('f', 1)
#define TIOCPKT_START 8
#define TIOCPKT_NOSTOP 16
#define TIOCPKT_DOSTOP 32
+#define TIOCPKT_IOCTL 64
#endif /* !(_ASM_SPARC_IOCTLS_H) */
#include <asm-generic/scatterlist.h>
-#define ISA_DMA_THRESHOLD (~0UL)
#define ARCH_HAS_SG_CHAIN
#endif /* !(_SPARC_SCATTERLIST_H) */
#define FLUSHO 0x00002000
#define PENDIN 0x00004000
#define IEXTEN 0x00008000
+#define EXTPROC 0x00010000
/* modem lines */
#define TIOCM_LE 0x001
return 0;
}
-static inline int
-dma_get_cache_alignment(void)
-{
- return L2_CACHE_BYTES;
-}
-
-#define dma_is_consistent(d, h) (1)
-
-
#endif /* _ASM_TILE_DMA_MAPPING_H */
#include "linux/mm.h"
#include "linux/slab.h"
#include "linux/vmalloc.h"
+#include "linux/smp_lock.h"
#include "linux/blkpg.h"
#include "linux/genhd.h"
#include "linux/spinlock.h"
struct ubd *ubd_dev = disk->private_data;
int err = 0;
+ lock_kernel();
if(ubd_dev->count == 0){
err = ubd_open_dev(ubd_dev);
if(err){
if(--ubd_dev->count == 0) ubd_close_dev(ubd_dev);
err = -EROFS;
}*/
- out:
+out:
+ unlock_kernel();
return err;
}
{
struct ubd *ubd_dev = disk->private_data;
+ lock_kernel();
if(--ubd_dev->count == 0)
ubd_close_dev(ubd_dev);
+ unlock_kernel();
return 0;
}
#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
-#define dma_is_consistent(d, h) (1)
static inline int
dma_get_cache_alignment(void)
.quad compat_sys_recvmmsg
.quad sys_fanotify_init
.quad sys32_fanotify_mark
- .quad sys_prlimit64
+ .quad sys_prlimit64 /* 340 */
ia32_syscall_end:
#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
-#define dma_is_consistent(d, h) (1)
extern int dma_supported(struct device *hwdev, u64 mask);
extern int dma_set_mask(struct device *dev, u64 mask);
flush_write_buffers();
}
-static inline int dma_get_cache_alignment(void)
-{
- /* no easy way to get cache size on all x86, so return the
- * maximum possible, to be safe */
- return boot_cpu_data.x86_clflush_size;
-}
-
static inline unsigned long dma_alloc_coherent_mask(struct device *dev,
gfp_t gfp)
{
#include <asm-generic/scatterlist.h>
-#define ISA_DMA_THRESHOLD (0x00ffffff)
#define ARCH_HAS_SG_CHAIN
#endif /* _ASM_X86_SCATTERLIST_H */
.long sys_recvmmsg
.long sys_fanotify_init
.long sys_fanotify_mark
- .long sys_prlimit64
+ .long sys_prlimit64 /* 340 */
# define CACHE_WAY_SIZE ICACHE_WAY_SIZE
#endif
-#define ARCH_KMALLOC_MINALIGN L1_CACHE_BYTES
+#define ARCH_DMA_MINALIGN L1_CACHE_BYTES
#endif /* _XTENSA_CACHE_H */
return 0;
}
-static inline int
-dma_get_cache_alignment(void)
-{
- return L1_CACHE_BYTES;
-}
-
-#define dma_is_consistent(d, h) (1)
-
static inline void
dma_cache_sync(struct device *dev, void *vaddr, size_t size,
enum dma_data_direction direction)
# define TIOCPKT_START 8
# define TIOCPKT_NOSTOP 16
# define TIOCPKT_DOSTOP 32
+# define TIOCPKT_IOCTL 64
#define TIOCNOTTY _IO('T', 34)
#define TCSETSF2 _IOW('T', 45, struct termios2)
#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
+#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */
#define TIOCSERCONFIG _IO('T', 83)
#define TIOCSERGWILD _IOR('T', 84, int)
#include <asm-generic/scatterlist.h>
-#define ISA_DMA_THRESHOLD (~0UL)
-
#endif /* _XTENSA_SCATTERLIST_H */
#define FLUSHO 0010000
#define PENDIN 0040000
#define IEXTEN 0100000
+#define EXTPROC 0200000
/* tcflow() and TCXONC use these */
* blk_queue_ordered - does this queue support ordered writes
* @q: the request queue
* @ordered: one of QUEUE_ORDERED_*
- * @prepare_flush_fn: rq setup helper for cache flush ordered writes
*
* Description:
* For journalled file systems, doing ordered writes on a commit
* feature should call this function and indicate so.
*
**/
-int blk_queue_ordered(struct request_queue *q, unsigned ordered,
- prepare_flush_fn *prepare_flush_fn)
+int blk_queue_ordered(struct request_queue *q, unsigned ordered)
{
- if (!prepare_flush_fn && (ordered & (QUEUE_ORDERED_DO_PREFLUSH |
- QUEUE_ORDERED_DO_POSTFLUSH))) {
- printk(KERN_ERR "%s: prepare_flush_fn required\n", __func__);
- return -EINVAL;
- }
-
if (ordered != QUEUE_ORDERED_NONE &&
ordered != QUEUE_ORDERED_DRAIN &&
ordered != QUEUE_ORDERED_DRAIN_FLUSH &&
q->ordered = ordered;
q->next_ordered = ordered;
- q->prepare_flush_fn = prepare_flush_fn;
return 0;
}
*
* http://thread.gmane.org/gmane.linux.kernel/537473
*/
- if (!blk_fs_request(rq))
+ if (rq->cmd_type != REQ_TYPE_FS)
return QUEUE_ORDSEQ_DRAIN;
if ((rq->cmd_flags & REQ_ORDERED_COLOR) ==
}
blk_rq_init(q, rq);
- rq->cmd_flags = REQ_HARDBARRIER;
- rq->rq_disk = q->bar_rq.rq_disk;
+ rq->cmd_type = REQ_TYPE_FS;
+ rq->cmd_flags = REQ_HARDBARRIER | REQ_FLUSH;
+ rq->rq_disk = q->orig_bar_rq->rq_disk;
rq->end_io = end_io;
- q->prepare_flush_fn(q, rq);
elv_insert(q, rq, ELEVATOR_INSERT_FRONT);
}
/* initialize proxy request and queue it */
blk_rq_init(q, rq);
if (bio_data_dir(q->orig_bar_rq->bio) == WRITE)
- rq->cmd_flags |= REQ_RW;
+ rq->cmd_flags |= REQ_WRITE;
if (q->ordered & QUEUE_ORDERED_DO_FUA)
rq->cmd_flags |= REQ_FUA;
init_request_from_bio(rq, q->orig_bar_rq->bio);
bool blk_do_ordered(struct request_queue *q, struct request **rqp)
{
struct request *rq = *rqp;
- const int is_barrier = blk_fs_request(rq) && blk_barrier_rq(rq);
+ const int is_barrier = rq->cmd_type == REQ_TYPE_FS &&
+ (rq->cmd_flags & REQ_HARDBARRIER);
if (!q->ordseq) {
if (!is_barrier)
*/
/* Special requests are not subject to ordering rules. */
- if (!blk_fs_request(rq) &&
+ if (rq->cmd_type != REQ_TYPE_FS &&
rq != &q->pre_flush_rq && rq != &q->post_flush_rq)
return true;
if (!q)
return -ENXIO;
+ /*
+ * some block devices may not have their queue correctly set up here
+ * (e.g. loop device without a backing file) and so issuing a flush
+ * here will panic. Ensure there is a request function before issuing
+ * the barrier.
+ */
+ if (!q->make_request_fn)
+ return -ENXIO;
+
bio = bio_alloc(gfp_mask, 0);
bio->bi_end_io = bio_end_empty_barrier;
bio->bi_bdev = bdev;
printk(KERN_INFO " bio %p, biotail %p, buffer %p, len %u\n",
rq->bio, rq->biotail, rq->buffer, blk_rq_bytes(rq));
- if (blk_pc_request(rq)) {
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
printk(KERN_INFO " cdb: ");
for (bit = 0; bit < BLK_MAX_CDB; bit++)
printk("%02x ", rq->cmd[bit]);
q->request_fn = rfn;
q->prep_rq_fn = NULL;
+ q->unprep_rq_fn = NULL;
q->unplug_fn = generic_unplug_device;
q->queue_flags = QUEUE_FLAG_DEFAULT;
q->queue_lock = lock;
}
EXPORT_SYMBOL(blk_put_request);
+/**
+ * blk_add_request_payload - add a payload to a request
+ * @rq: request to update
+ * @page: page backing the payload
+ * @len: length of the payload.
+ *
+ * This allows to later add a payload to an already submitted request by
+ * a block driver. The driver needs to take care of freeing the payload
+ * itself.
+ *
+ * Note that this is a quite horrible hack and nothing but handling of
+ * discard requests should ever use it.
+ */
+void blk_add_request_payload(struct request *rq, struct page *page,
+ unsigned int len)
+{
+ struct bio *bio = rq->bio;
+
+ bio->bi_io_vec->bv_page = page;
+ bio->bi_io_vec->bv_offset = 0;
+ bio->bi_io_vec->bv_len = len;
+
+ bio->bi_size = len;
+ bio->bi_vcnt = 1;
+ bio->bi_phys_segments = 1;
+
+ rq->__data_len = rq->resid_len = len;
+ rq->nr_phys_segments = 1;
+ rq->buffer = bio_data(bio);
+}
+EXPORT_SYMBOL_GPL(blk_add_request_payload);
+
void init_request_from_bio(struct request *req, struct bio *bio)
{
req->cpu = bio->bi_comp_cpu;
req->cmd_type = REQ_TYPE_FS;
- /*
- * Inherit FAILFAST from bio (for read-ahead, and explicit
- * FAILFAST). FAILFAST flags are identical for req and bio.
- */
- if (bio_rw_flagged(bio, BIO_RW_AHEAD))
+ req->cmd_flags |= bio->bi_rw & REQ_COMMON_MASK;
+ if (bio->bi_rw & REQ_RAHEAD)
req->cmd_flags |= REQ_FAILFAST_MASK;
- else
- req->cmd_flags |= bio->bi_rw & REQ_FAILFAST_MASK;
-
- if (bio_rw_flagged(bio, BIO_RW_DISCARD))
- req->cmd_flags |= REQ_DISCARD;
- if (bio_rw_flagged(bio, BIO_RW_BARRIER))
- req->cmd_flags |= REQ_HARDBARRIER;
- if (bio_rw_flagged(bio, BIO_RW_SYNCIO))
- req->cmd_flags |= REQ_RW_SYNC;
- if (bio_rw_flagged(bio, BIO_RW_META))
- req->cmd_flags |= REQ_RW_META;
- if (bio_rw_flagged(bio, BIO_RW_NOIDLE))
- req->cmd_flags |= REQ_NOIDLE;
req->errors = 0;
req->__sector = bio->bi_sector;
int el_ret;
unsigned int bytes = bio->bi_size;
const unsigned short prio = bio_prio(bio);
- const bool sync = bio_rw_flagged(bio, BIO_RW_SYNCIO);
- const bool unplug = bio_rw_flagged(bio, BIO_RW_UNPLUG);
+ const bool sync = (bio->bi_rw & REQ_SYNC);
+ const bool unplug = (bio->bi_rw & REQ_UNPLUG);
const unsigned int ff = bio->bi_rw & REQ_FAILFAST_MASK;
int rw_flags;
- if (bio_rw_flagged(bio, BIO_RW_BARRIER) &&
+ if ((bio->bi_rw & REQ_HARDBARRIER) &&
(q->next_ordered == QUEUE_ORDERED_NONE)) {
bio_endio(bio, -EOPNOTSUPP);
return 0;
spin_lock_irq(q->queue_lock);
- if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER)) || elv_queue_empty(q))
+ if (unlikely((bio->bi_rw & REQ_HARDBARRIER)) || elv_queue_empty(q))
goto get_rq;
el_ret = elv_merge(q, &req, bio);
*/
rw_flags = bio_data_dir(bio);
if (sync)
- rw_flags |= REQ_RW_SYNC;
+ rw_flags |= REQ_SYNC;
/*
* Grab a free request. This is might sleep but can not fail.
goto end_io;
}
- if (unlikely(!bio_rw_flagged(bio, BIO_RW_DISCARD) &&
+ if (unlikely(!(bio->bi_rw & REQ_DISCARD) &&
nr_sectors > queue_max_hw_sectors(q))) {
printk(KERN_ERR "bio too big device %s (%u > %u)\n",
bdevname(bio->bi_bdev, b),
if (bio_check_eod(bio, nr_sectors))
goto end_io;
- if (bio_rw_flagged(bio, BIO_RW_DISCARD) &&
- !blk_queue_discard(q)) {
+ if ((bio->bi_rw & REQ_DISCARD) && !blk_queue_discard(q)) {
err = -EOPNOTSUPP;
goto end_io;
}
* If it's a regular read/write or a barrier with data attached,
* go through the normal accounting stuff before submission.
*/
- if (bio_has_data(bio) && !(rw & (1 << BIO_RW_DISCARD))) {
+ if (bio_has_data(bio) && !(rw & REQ_DISCARD)) {
if (rw & WRITE) {
count_vm_events(PGPGOUT, count);
} else {
*/
int blk_rq_check_limits(struct request_queue *q, struct request *rq)
{
+ if (rq->cmd_flags & REQ_DISCARD)
+ return 0;
+
if (blk_rq_sectors(rq) > queue_max_sectors(q) ||
blk_rq_bytes(rq) > queue_max_hw_sectors(q) << 9) {
printk(KERN_ERR "%s: over max size limit.\n", __func__);
* sees this request (possibly after
* requeueing). Notify IO scheduler.
*/
- if (blk_sorted_rq(rq))
+ if (rq->cmd_flags & REQ_SORTED)
elv_activate_rq(q, rq);
/*
* TODO: tj: This is too subtle. It would be better to let
* low level drivers do what they see fit.
*/
- if (blk_fs_request(req))
+ if (req->cmd_type == REQ_TYPE_FS)
req->errors = 0;
- if (error && (blk_fs_request(req) && !(req->cmd_flags & REQ_QUIET))) {
+ if (error && req->cmd_type == REQ_TYPE_FS &&
+ !(req->cmd_flags & REQ_QUIET)) {
printk(KERN_ERR "end_request: I/O error, dev %s, sector %llu\n",
req->rq_disk ? req->rq_disk->disk_name : "?",
(unsigned long long)blk_rq_pos(req));
req->buffer = bio_data(req->bio);
/* update sector only for requests with clear definition of sector */
- if (blk_fs_request(req) || blk_discard_rq(req))
+ if (req->cmd_type == REQ_TYPE_FS || (req->cmd_flags & REQ_DISCARD))
req->__sector += total_bytes >> 9;
/* mixed attributes always follow the first bio */
blk_update_request(rq->next_rq, error, bidi_bytes))
return true;
- add_disk_randomness(rq->rq_disk);
+ if (blk_queue_add_random(rq->q))
+ add_disk_randomness(rq->rq_disk);
return false;
}
+/**
+ * blk_unprep_request - unprepare a request
+ * @req: the request
+ *
+ * This function makes a request ready for complete resubmission (or
+ * completion). It happens only after all error handling is complete,
+ * so represents the appropriate moment to deallocate any resources
+ * that were allocated to the request in the prep_rq_fn. The queue
+ * lock is held when calling this.
+ */
+void blk_unprep_request(struct request *req)
+{
+ struct request_queue *q = req->q;
+
+ req->cmd_flags &= ~REQ_DONTPREP;
+ if (q->unprep_rq_fn)
+ q->unprep_rq_fn(q, req);
+}
+EXPORT_SYMBOL_GPL(blk_unprep_request);
+
/*
* queue lock must be held
*/
BUG_ON(blk_queued_rq(req));
- if (unlikely(laptop_mode) && blk_fs_request(req))
+ if (unlikely(laptop_mode) && req->cmd_type == REQ_TYPE_FS)
laptop_io_completion(&req->q->backing_dev_info);
blk_delete_timer(req);
+ if (req->cmd_flags & REQ_DONTPREP)
+ blk_unprep_request(req);
+
+
blk_account_io_done(req);
if (req->end_io)
struct bio *bio)
{
/* Bit 0 (R/W) is identical in rq->cmd_flags and bio->bi_rw */
- rq->cmd_flags |= bio->bi_rw & REQ_RW;
+ rq->cmd_flags |= bio->bi_rw & REQ_WRITE;
if (bio_has_data(bio)) {
rq->nr_phys_segments = bio_phys_segments(q, bio);
{
dst->cpu = src->cpu;
dst->cmd_flags = (rq_data_dir(src) | REQ_NOMERGE);
+ if (src->cmd_flags & REQ_DISCARD)
+ dst->cmd_flags |= REQ_DISCARD;
dst->cmd_type = src->cmd_type;
dst->__sector = blk_rq_pos(src);
dst->__data_len = blk_rq_bytes(src);
__elv_add_request(q, rq, where, 1);
__generic_unplug_device(q);
/* the queue is stopped so it won't be plugged+unplugged */
- if (blk_pm_resume_request(rq))
+ if (rq->cmd_type == REQ_TYPE_PM_RESUME)
q->request_fn(q);
spin_unlock_irq(q->queue_lock);
}
if (bio->bi_private)
complete(bio->bi_private);
- __free_page(bio_page(bio));
bio_put(bio);
}
struct request_queue *q = bdev_get_queue(bdev);
int type = flags & BLKDEV_IFL_BARRIER ?
DISCARD_BARRIER : DISCARD_NOBARRIER;
+ unsigned int max_discard_sectors;
struct bio *bio;
- struct page *page;
int ret = 0;
if (!q)
if (!blk_queue_discard(q))
return -EOPNOTSUPP;
- while (nr_sects && !ret) {
- unsigned int sector_size = q->limits.logical_block_size;
- unsigned int max_discard_sectors =
- min(q->limits.max_discard_sectors, UINT_MAX >> 9);
+ /*
+ * Ensure that max_discard_sectors is of the proper
+ * granularity
+ */
+ max_discard_sectors = min(q->limits.max_discard_sectors, UINT_MAX >> 9);
+ if (q->limits.discard_granularity) {
+ unsigned int disc_sects = q->limits.discard_granularity >> 9;
+ max_discard_sectors &= ~(disc_sects - 1);
+ }
+
+ while (nr_sects && !ret) {
bio = bio_alloc(gfp_mask, 1);
- if (!bio)
- goto out;
+ if (!bio) {
+ ret = -ENOMEM;
+ break;
+ }
+
bio->bi_sector = sector;
bio->bi_end_io = blkdev_discard_end_io;
bio->bi_bdev = bdev;
if (flags & BLKDEV_IFL_WAIT)
bio->bi_private = &wait;
- /*
- * Add a zeroed one-sector payload as that's what
- * our current implementations need. If we'll ever need
- * more the interface will need revisiting.
- */
- page = alloc_page(gfp_mask | __GFP_ZERO);
- if (!page)
- goto out_free_bio;
- if (bio_add_pc_page(q, bio, page, sector_size, 0) < sector_size)
- goto out_free_page;
-
- /*
- * And override the bio size - the way discard works we
- * touch many more blocks on disk than the actual payload
- * length.
- */
if (nr_sects > max_discard_sectors) {
bio->bi_size = max_discard_sectors << 9;
nr_sects -= max_discard_sectors;
ret = -EIO;
bio_put(bio);
}
+
return ret;
-out_free_page:
- __free_page(page);
-out_free_bio:
- bio_put(bio);
-out:
- return -ENOMEM;
}
EXPORT_SYMBOL(blkdev_issue_discard);
int blkdev_issue_zeroout(struct block_device *bdev, sector_t sector,
sector_t nr_sects, gfp_t gfp_mask, unsigned long flags)
{
- int ret = 0;
+ int ret;
struct bio *bio;
struct bio_batch bb;
unsigned int sz, issued = 0;
return ret;
}
submit:
+ ret = 0;
while (nr_sects != 0) {
bio = bio_alloc(gfp_mask,
min(nr_sects, (sector_t)BIO_MAX_PAGES));
- if (!bio)
+ if (!bio) {
+ ret = -ENOMEM;
break;
+ }
bio->bi_sector = sector;
bio->bi_bdev = bdev;
if (ret < (sz << 9))
break;
}
+ ret = 0;
issued++;
submit_bio(WRITE, bio);
}
return PTR_ERR(bio);
if (rq_data_dir(rq) == WRITE)
- bio->bi_rw |= (1 << BIO_RW);
+ bio->bi_rw |= (1 << REQ_WRITE);
if (do_copy)
rq->cmd_flags |= REQ_COPY_USER;
static unsigned int __blk_recalc_rq_segments(struct request_queue *q,
struct bio *bio)
{
- unsigned int phys_size;
struct bio_vec *bv, *bvprv = NULL;
int cluster, i, high, highprv = 1;
unsigned int seg_size, nr_phys_segs;
fbio = bio;
cluster = test_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags);
seg_size = 0;
- phys_size = nr_phys_segs = 0;
+ nr_phys_segs = 0;
for_each_bio(bio) {
bio_for_each_segment(bv, bio, i) {
/*
}
if (q->dma_drain_size && q->dma_drain_needed(rq)) {
- if (rq->cmd_flags & REQ_RW)
+ if (rq->cmd_flags & REQ_WRITE)
memset(q->dma_drain_buffer, 0, q->dma_drain_size);
sg->page_link &= ~0x02;
{
unsigned short max_sectors;
- if (unlikely(blk_pc_request(req)))
+ if (unlikely(req->cmd_type == REQ_TYPE_BLOCK_PC))
max_sectors = queue_max_hw_sectors(q);
else
max_sectors = queue_max_sectors(q);
{
unsigned short max_sectors;
- if (unlikely(blk_pc_request(req)))
+ if (unlikely(req->cmd_type == REQ_TYPE_BLOCK_PC))
max_sectors = queue_max_hw_sectors(q);
else
max_sectors = queue_max_sectors(q);
}
EXPORT_SYMBOL(blk_queue_prep_rq);
+/**
+ * blk_queue_unprep_rq - set an unprepare_request function for queue
+ * @q: queue
+ * @ufn: unprepare_request function
+ *
+ * It's possible for a queue to register an unprepare_request callback
+ * which is invoked before the request is finally completed. The goal
+ * of the function is to deallocate any data that was allocated in the
+ * prepare_request callback.
+ *
+ */
+void blk_queue_unprep_rq(struct request_queue *q, unprep_rq_fn *ufn)
+{
+ q->unprep_rq_fn = ufn;
+}
+EXPORT_SYMBOL(blk_queue_unprep_rq);
+
/**
* blk_queue_merge_bvec - set a merge_bvec function for queue
* @q: queue
return queue_var_show(max_hw_sectors_kb, (page));
}
-static ssize_t queue_nonrot_show(struct request_queue *q, char *page)
-{
- return queue_var_show(!blk_queue_nonrot(q), page);
+#define QUEUE_SYSFS_BIT_FNS(name, flag, neg) \
+static ssize_t \
+queue_show_##name(struct request_queue *q, char *page) \
+{ \
+ int bit; \
+ bit = test_bit(QUEUE_FLAG_##flag, &q->queue_flags); \
+ return queue_var_show(neg ? !bit : bit, page); \
+} \
+static ssize_t \
+queue_store_##name(struct request_queue *q, const char *page, size_t count) \
+{ \
+ unsigned long val; \
+ ssize_t ret; \
+ ret = queue_var_store(&val, page, count); \
+ if (neg) \
+ val = !val; \
+ \
+ spin_lock_irq(q->queue_lock); \
+ if (val) \
+ queue_flag_set(QUEUE_FLAG_##flag, q); \
+ else \
+ queue_flag_clear(QUEUE_FLAG_##flag, q); \
+ spin_unlock_irq(q->queue_lock); \
+ return ret; \
}
-static ssize_t queue_nonrot_store(struct request_queue *q, const char *page,
- size_t count)
-{
- unsigned long nm;
- ssize_t ret = queue_var_store(&nm, page, count);
-
- spin_lock_irq(q->queue_lock);
- if (nm)
- queue_flag_clear(QUEUE_FLAG_NONROT, q);
- else
- queue_flag_set(QUEUE_FLAG_NONROT, q);
- spin_unlock_irq(q->queue_lock);
-
- return ret;
-}
+QUEUE_SYSFS_BIT_FNS(nonrot, NONROT, 1);
+QUEUE_SYSFS_BIT_FNS(random, ADD_RANDOM, 0);
+QUEUE_SYSFS_BIT_FNS(iostats, IO_STAT, 0);
+#undef QUEUE_SYSFS_BIT_FNS
static ssize_t queue_nomerges_show(struct request_queue *q, char *page)
{
return ret;
}
-static ssize_t queue_iostats_show(struct request_queue *q, char *page)
-{
- return queue_var_show(blk_queue_io_stat(q), page);
-}
-
-static ssize_t queue_iostats_store(struct request_queue *q, const char *page,
- size_t count)
-{
- unsigned long stats;
- ssize_t ret = queue_var_store(&stats, page, count);
-
- spin_lock_irq(q->queue_lock);
- if (stats)
- queue_flag_set(QUEUE_FLAG_IO_STAT, q);
- else
- queue_flag_clear(QUEUE_FLAG_IO_STAT, q);
- spin_unlock_irq(q->queue_lock);
-
- return ret;
-}
-
static struct queue_sysfs_entry queue_requests_entry = {
.attr = {.name = "nr_requests", .mode = S_IRUGO | S_IWUSR },
.show = queue_requests_show,
static struct queue_sysfs_entry queue_nonrot_entry = {
.attr = {.name = "rotational", .mode = S_IRUGO | S_IWUSR },
- .show = queue_nonrot_show,
- .store = queue_nonrot_store,
+ .show = queue_show_nonrot,
+ .store = queue_store_nonrot,
};
static struct queue_sysfs_entry queue_nomerges_entry = {
static struct queue_sysfs_entry queue_iostats_entry = {
.attr = {.name = "iostats", .mode = S_IRUGO | S_IWUSR },
- .show = queue_iostats_show,
- .store = queue_iostats_store,
+ .show = queue_show_iostats,
+ .store = queue_store_iostats,
+};
+
+static struct queue_sysfs_entry queue_random_entry = {
+ .attr = {.name = "add_random", .mode = S_IRUGO | S_IWUSR },
+ .show = queue_show_random,
+ .store = queue_store_random,
};
static struct attribute *default_attrs[] = {
&queue_nomerges_entry.attr,
&queue_rq_affinity_entry.attr,
&queue_iostats_entry.attr,
+ &queue_random_entry.attr,
NULL,
};
*/
static inline int blk_do_io_stat(struct request *rq)
{
- return rq->rq_disk && blk_rq_io_stat(rq) &&
- (blk_fs_request(rq) || blk_discard_rq(rq));
+ return rq->rq_disk &&
+ (rq->cmd_flags & REQ_IO_STAT) &&
+ (rq->cmd_type == REQ_TYPE_FS ||
+ (rq->cmd_flags & REQ_DISCARD));
}
#endif
*/
static inline bool cfq_bio_sync(struct bio *bio)
{
- return bio_data_dir(bio) == READ || bio_rw_flagged(bio, BIO_RW_SYNCIO);
+ return bio_data_dir(bio) == READ || (bio->bi_rw & REQ_SYNC);
}
/*
return rq1;
else if (rq_is_sync(rq2) && !rq_is_sync(rq1))
return rq2;
- if (rq_is_meta(rq1) && !rq_is_meta(rq2))
+ if ((rq1->cmd_flags & REQ_META) && !(rq2->cmd_flags & REQ_META))
return rq1;
- else if (rq_is_meta(rq2) && !rq_is_meta(rq1))
+ else if ((rq2->cmd_flags & REQ_META) &&
+ !(rq1->cmd_flags & REQ_META))
return rq2;
s1 = blk_rq_pos(rq1);
cfqq->cfqd->rq_queued--;
cfq_blkiocg_update_io_remove_stats(&(RQ_CFQG(rq))->blkg,
rq_data_dir(rq), rq_is_sync(rq));
- if (rq_is_meta(rq)) {
+ if (rq->cmd_flags & REQ_META) {
WARN_ON(!cfqq->meta_pending);
cfqq->meta_pending--;
}
* So both queues are sync. Let the new request get disk time if
* it's a metadata request and the current queue is doing regular IO.
*/
- if (rq_is_meta(rq) && !cfqq->meta_pending)
+ if ((rq->cmd_flags & REQ_META) && !cfqq->meta_pending)
return true;
/*
struct cfq_io_context *cic = RQ_CIC(rq);
cfqd->rq_queued++;
- if (rq_is_meta(rq))
+ if (rq->cmd_flags & REQ_META)
cfqq->meta_pending++;
cfq_update_io_thinktime(cfqd, cic);
unsigned long now;
now = jiffies;
- cfq_log_cfqq(cfqd, cfqq, "complete rqnoidle %d", !!rq_noidle(rq));
+ cfq_log_cfqq(cfqd, cfqq, "complete rqnoidle %d",
+ !!(rq->cmd_flags & REQ_NOIDLE));
cfq_update_hw_tag(cfqd);
cfq_slice_expired(cfqd, 1);
else if (sync && cfqq_empty &&
!cfq_close_cooperator(cfqd, cfqq)) {
- cfqd->noidle_tree_requires_idle |= !rq_noidle(rq);
+ cfqd->noidle_tree_requires_idle |=
+ !(rq->cmd_flags & REQ_NOIDLE);
/*
* Idling is enabled for SYNC_WORKLOAD.
* SYNC_NOIDLE_WORKLOAD idles at the end of the tree
- * only if we processed at least one !rq_noidle request
+ * only if we processed at least one !REQ_NOIDLE request
*/
if (cfqd->serving_type == SYNC_WORKLOAD
|| cfqd->noidle_tree_requires_idle
return err;
}
-struct compat_blk_user_trace_setup {
- char name[32];
- u16 act_mask;
- u32 buf_size;
- u32 buf_nr;
- compat_u64 start_lba;
- compat_u64 end_lba;
- u32 pid;
-};
-#define BLKTRACESETUP32 _IOWR(0x12, 115, struct compat_blk_user_trace_setup)
-
-static int compat_blk_trace_setup(struct block_device *bdev, char __user *arg)
-{
- struct blk_user_trace_setup buts;
- struct compat_blk_user_trace_setup cbuts;
- struct request_queue *q;
- char b[BDEVNAME_SIZE];
- int ret;
-
- q = bdev_get_queue(bdev);
- if (!q)
- return -ENXIO;
-
- if (copy_from_user(&cbuts, arg, sizeof(cbuts)))
- return -EFAULT;
-
- bdevname(bdev, b);
-
- buts = (struct blk_user_trace_setup) {
- .act_mask = cbuts.act_mask,
- .buf_size = cbuts.buf_size,
- .buf_nr = cbuts.buf_nr,
- .start_lba = cbuts.start_lba,
- .end_lba = cbuts.end_lba,
- .pid = cbuts.pid,
- };
- memcpy(&buts.name, &cbuts.name, 32);
-
- mutex_lock(&bdev->bd_mutex);
- ret = do_blk_trace_setup(q, b, bdev->bd_dev, bdev, &buts);
- mutex_unlock(&bdev->bd_mutex);
- if (ret)
- return ret;
-
- if (copy_to_user(arg, &buts.name, 32))
- return -EFAULT;
-
- return 0;
-}
-
static int compat_blkdev_driver_ioctl(struct block_device *bdev, fmode_t mode,
unsigned cmd, unsigned long arg)
{
return compat_put_u64(arg, bdev->bd_inode->i_size);
case BLKTRACESETUP32:
- lock_kernel();
- ret = compat_blk_trace_setup(bdev, compat_ptr(arg));
- unlock_kernel();
- return ret;
case BLKTRACESTART: /* compatible */
case BLKTRACESTOP: /* compatible */
case BLKTRACETEARDOWN: /* compatible */
- lock_kernel();
ret = blk_trace_ioctl(bdev, cmd, compat_ptr(arg));
- unlock_kernel();
return ret;
default:
if (disk->fops->compat_ioctl)
/*
* Don't merge file system requests and discard requests
*/
- if (bio_rw_flagged(bio, BIO_RW_DISCARD) !=
- bio_rw_flagged(rq->bio, BIO_RW_DISCARD))
+ if ((bio->bi_rw & REQ_DISCARD) != (rq->bio->bi_rw & REQ_DISCARD))
return 0;
/*
list_for_each_prev(entry, &q->queue_head) {
struct request *pos = list_entry_rq(entry);
- if (blk_discard_rq(rq) != blk_discard_rq(pos))
+ if ((rq->cmd_flags & REQ_DISCARD) !=
+ (pos->cmd_flags & REQ_DISCARD))
break;
if (rq_data_dir(rq) != rq_data_dir(pos))
break;
*/
if (blk_account_rq(rq)) {
q->in_flight[rq_is_sync(rq)]--;
- if (blk_sorted_rq(rq))
+ if (rq->cmd_flags & REQ_SORTED)
elv_deactivate_rq(q, rq);
}
break;
case ELEVATOR_INSERT_SORT:
- BUG_ON(!blk_fs_request(rq) && !blk_discard_rq(rq));
+ BUG_ON(rq->cmd_type != REQ_TYPE_FS &&
+ !(rq->cmd_flags & REQ_DISCARD));
rq->cmd_flags |= REQ_SORTED;
q->nr_sorted++;
if (rq_mergeable(rq)) {
/*
* toggle ordered color
*/
- if (blk_barrier_rq(rq))
+ if (rq->cmd_flags & REQ_HARDBARRIER)
q->ordcolor ^= 1;
/*
* this request is scheduling boundary, update
* end_sector
*/
- if (blk_fs_request(rq) || blk_discard_rq(rq)) {
+ if (rq->cmd_type == REQ_TYPE_FS ||
+ (rq->cmd_flags & REQ_DISCARD)) {
q->end_sector = rq_end_sector(rq);
q->boundary_rq = rq;
}
*/
if (blk_account_rq(rq)) {
q->in_flight[rq_is_sync(rq)]--;
- if (blk_sorted_rq(rq) && e->ops->elevator_completed_req_fn)
+ if ((rq->cmd_flags & REQ_SORTED) &&
+ e->ops->elevator_completed_req_fn)
e->ops->elevator_completed_req_fn(q, rq);
}
unsigned cmd, unsigned long arg)
{
struct gendisk *disk = bdev->bd_disk;
- int ret;
if (disk->fops->ioctl)
return disk->fops->ioctl(bdev, mode, cmd, arg);
- if (disk->fops->locked_ioctl) {
- lock_kernel();
- ret = disk->fops->locked_ioctl(bdev, mode, cmd, arg);
- unlock_kernel();
- return ret;
- }
-
return -ENOTTY;
}
/*
EXPORT_SYMBOL_GPL(__blkdev_driver_ioctl);
/*
- * always keep this in sync with compat_blkdev_ioctl() and
- * compat_blkdev_locked_ioctl()
+ * always keep this in sync with compat_blkdev_ioctl()
*/
int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
unsigned long arg)
if (ret != -EINVAL && ret != -ENOTTY)
return ret;
- lock_kernel();
fsync_bdev(bdev);
invalidate_bdev(bdev);
- unlock_kernel();
return 0;
case BLKROSET:
return -EACCES;
if (get_user(n, (int __user *)(arg)))
return -EFAULT;
- lock_kernel();
set_device_ro(bdev, n);
- unlock_kernel();
return 0;
case BLKDISCARD: {
bd_release(bdev);
return ret;
case BLKPG:
- lock_kernel();
ret = blkpg_ioctl(bdev, (struct blkpg_ioctl_arg __user *) arg);
- unlock_kernel();
break;
case BLKRRPART:
- lock_kernel();
ret = blkdev_reread_part(bdev);
- unlock_kernel();
break;
case BLKGETSIZE:
size = bdev->bd_inode->i_size;
case BLKTRACESTOP:
case BLKTRACESETUP:
case BLKTRACETEARDOWN:
- lock_kernel();
ret = blk_trace_ioctl(bdev, cmd, (char __user *) arg);
- unlock_kernel();
break;
default:
ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
tristate
select ASYNC_CORE
select ASYNC_PQ
+ select ASYNC_XOR
+
+config ASYNC_RAID6_TEST
+ tristate "Self test for hardware accelerated raid6 recovery"
+ depends on ASYNC_RAID6_RECOV
+ select ASYNC_MEMCPY
+ ---help---
+ This is a one-shot self test that permutes through the
+ recovery of all the possible two disk failure scenarios for a
+ N-disk array. Recovery is performed with the asynchronous
+ raid6 recovery routines, and will optionally use an offload
+ engine if one is available.
+
+ If unsure, say N.
config ASYNC_TX_DISABLE_PQ_VAL_DMA
bool
If unsure, say N.
+config PATA_PXA
+ tristate "PXA DMA-capable PATA support"
+ depends on ARCH_PXA
+ help
+ This option enables support for harddrive attached to PXA CPU's bus.
+
+ NOTE: This driver utilizes PXA DMA controller, in case your hardware
+ is not capable of doing MWDMA, use pata_platform instead.
+
+ If unsure, say N.
+
config PATA_WINBOND
tristate "Winbond SL82C105 PATA support"
depends on PCI
obj-$(CONFIG_PATA_SAMSUNG_CF) += pata_samsung_cf.o
obj-$(CONFIG_PATA_WINBOND_VLB) += pata_winbond.o
+obj-$(CONFIG_PATA_PXA) += pata_pxa.o
+
# Should be last but two libata driver
obj-$(CONFIG_PATA_ACPI) += pata_acpi.o
# Should be last but one libata driver
* The special characters ?, [, -, or *, can be matched using a set, eg. [*]
* Behaviour with malformed patterns is undefined, though generally reasonable.
*
- * Example patterns: "SD1?", "SD1[0-5]", "*R0", SD*1?[012]*xx"
+ * Sample patterns: "SD1?", "SD1[0-5]", "*R0", "SD*1?[012]*xx"
*
* This function uses one level of recursion per '*' in pattern.
* Since it calls _nothing_ else, and has _no_ explicit local variables,
*/
static int atapi_drain_needed(struct request *rq)
{
- if (likely(!blk_pc_request(rq)))
+ if (likely(rq->cmd_type != REQ_TYPE_BLOCK_PC))
return 0;
- if (!blk_rq_bytes(rq) || (rq->cmd_flags & REQ_RW))
+ if (!blk_rq_bytes(rq) || (rq->cmd_flags & REQ_WRITE))
return 0;
return atapi_cmd_type(rq->cmd[0]) == ATAPI_MISC;
--- /dev/null
+/*
+ * Generic PXA PATA driver
+ *
+ * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/ata.h>
+#include <linux/libata.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+
+#include <scsi/scsi_host.h>
+
+#include <mach/pxa2xx-regs.h>
+#include <mach/pata_pxa.h>
+#include <mach/dma.h>
+
+#define DRV_NAME "pata_pxa"
+#define DRV_VERSION "0.1"
+
+struct pata_pxa_data {
+ uint32_t dma_channel;
+ struct pxa_dma_desc *dma_desc;
+ dma_addr_t dma_desc_addr;
+ uint32_t dma_desc_id;
+
+ /* DMA IO physical address */
+ uint32_t dma_io_addr;
+ /* PXA DREQ<0:2> pin selector */
+ uint32_t dma_dreq;
+ /* DMA DCSR register value */
+ uint32_t dma_dcsr;
+
+ struct completion dma_done;
+};
+
+/*
+ * Setup the DMA descriptors. The size is transfer capped at 4k per descriptor,
+ * if the transfer is longer, it is split into multiple chained descriptors.
+ */
+static void pxa_load_dmac(struct scatterlist *sg, struct ata_queued_cmd *qc)
+{
+ struct pata_pxa_data *pd = qc->ap->private_data;
+
+ uint32_t cpu_len, seg_len;
+ dma_addr_t cpu_addr;
+
+ cpu_addr = sg_dma_address(sg);
+ cpu_len = sg_dma_len(sg);
+
+ do {
+ seg_len = (cpu_len > 0x1000) ? 0x1000 : cpu_len;
+
+ pd->dma_desc[pd->dma_desc_id].ddadr = pd->dma_desc_addr +
+ ((pd->dma_desc_id + 1) * sizeof(struct pxa_dma_desc));
+
+ pd->dma_desc[pd->dma_desc_id].dcmd = DCMD_BURST32 |
+ DCMD_WIDTH2 | (DCMD_LENGTH & seg_len);
+
+ if (qc->tf.flags & ATA_TFLAG_WRITE) {
+ pd->dma_desc[pd->dma_desc_id].dsadr = cpu_addr;
+ pd->dma_desc[pd->dma_desc_id].dtadr = pd->dma_io_addr;
+ pd->dma_desc[pd->dma_desc_id].dcmd |= DCMD_INCSRCADDR |
+ DCMD_FLOWTRG;
+ } else {
+ pd->dma_desc[pd->dma_desc_id].dsadr = pd->dma_io_addr;
+ pd->dma_desc[pd->dma_desc_id].dtadr = cpu_addr;
+ pd->dma_desc[pd->dma_desc_id].dcmd |= DCMD_INCTRGADDR |
+ DCMD_FLOWSRC;
+ }
+
+ cpu_len -= seg_len;
+ cpu_addr += seg_len;
+ pd->dma_desc_id++;
+
+ } while (cpu_len);
+
+ /* Should not happen */
+ if (seg_len & 0x1f)
+ DALGN |= (1 << pd->dma_dreq);
+}
+
+/*
+ * Prepare taskfile for submission.
+ */
+static void pxa_qc_prep(struct ata_queued_cmd *qc)
+{
+ struct pata_pxa_data *pd = qc->ap->private_data;
+ int si = 0;
+ struct scatterlist *sg;
+
+ if (!(qc->flags & ATA_QCFLAG_DMAMAP))
+ return;
+
+ pd->dma_desc_id = 0;
+
+ DCSR(pd->dma_channel) = 0;
+ DALGN &= ~(1 << pd->dma_dreq);
+
+ for_each_sg(qc->sg, sg, qc->n_elem, si)
+ pxa_load_dmac(sg, qc);
+
+ pd->dma_desc[pd->dma_desc_id - 1].ddadr = DDADR_STOP;
+
+ /* Fire IRQ only at the end of last block */
+ pd->dma_desc[pd->dma_desc_id - 1].dcmd |= DCMD_ENDIRQEN;
+
+ DDADR(pd->dma_channel) = pd->dma_desc_addr;
+ DRCMR(pd->dma_dreq) = DRCMR_MAPVLD | pd->dma_channel;
+
+}
+
+/*
+ * Configure the DMA controller, load the DMA descriptors, but don't start the
+ * DMA controller yet. Only issue the ATA command.
+ */
+static void pxa_bmdma_setup(struct ata_queued_cmd *qc)
+{
+ qc->ap->ops->sff_exec_command(qc->ap, &qc->tf);
+}
+
+/*
+ * Execute the DMA transfer.
+ */
+static void pxa_bmdma_start(struct ata_queued_cmd *qc)
+{
+ struct pata_pxa_data *pd = qc->ap->private_data;
+ init_completion(&pd->dma_done);
+ DCSR(pd->dma_channel) = DCSR_RUN;
+}
+
+/*
+ * Wait until the DMA transfer completes, then stop the DMA controller.
+ */
+static void pxa_bmdma_stop(struct ata_queued_cmd *qc)
+{
+ struct pata_pxa_data *pd = qc->ap->private_data;
+
+ if ((DCSR(pd->dma_channel) & DCSR_RUN) &&
+ wait_for_completion_timeout(&pd->dma_done, HZ))
+ dev_err(qc->ap->dev, "Timeout waiting for DMA completion!");
+
+ DCSR(pd->dma_channel) = 0;
+}
+
+/*
+ * Read DMA status. The bmdma_stop() will take care of properly finishing the
+ * DMA transfer so we always have DMA-complete interrupt here.
+ */
+static unsigned char pxa_bmdma_status(struct ata_port *ap)
+{
+ struct pata_pxa_data *pd = ap->private_data;
+ unsigned char ret = ATA_DMA_INTR;
+
+ if (pd->dma_dcsr & DCSR_BUSERR)
+ ret |= ATA_DMA_ERR;
+
+ return ret;
+}
+
+/*
+ * No IRQ register present so we do nothing.
+ */
+static void pxa_irq_clear(struct ata_port *ap)
+{
+}
+
+/*
+ * Check for ATAPI DMA. ATAPI DMA is unsupported by this driver. It's still
+ * unclear why ATAPI has DMA issues.
+ */
+static int pxa_check_atapi_dma(struct ata_queued_cmd *qc)
+{
+ return -EOPNOTSUPP;
+}
+
+static struct scsi_host_template pxa_ata_sht = {
+ ATA_BMDMA_SHT(DRV_NAME),
+};
+
+static struct ata_port_operations pxa_ata_port_ops = {
+ .inherits = &ata_bmdma_port_ops,
+ .cable_detect = ata_cable_40wire,
+
+ .bmdma_setup = pxa_bmdma_setup,
+ .bmdma_start = pxa_bmdma_start,
+ .bmdma_stop = pxa_bmdma_stop,
+ .bmdma_status = pxa_bmdma_status,
+
+ .check_atapi_dma = pxa_check_atapi_dma,
+
+ .sff_irq_clear = pxa_irq_clear,
+
+ .qc_prep = pxa_qc_prep,
+};
+
+/*
+ * DMA interrupt handler.
+ */
+static void pxa_ata_dma_irq(int dma, void *port)
+{
+ struct ata_port *ap = port;
+ struct pata_pxa_data *pd = ap->private_data;
+
+ pd->dma_dcsr = DCSR(dma);
+ DCSR(dma) = pd->dma_dcsr;
+
+ if (pd->dma_dcsr & DCSR_STOPSTATE)
+ complete(&pd->dma_done);
+}
+
+static int __devinit pxa_ata_probe(struct platform_device *pdev)
+{
+ struct ata_host *host;
+ struct ata_port *ap;
+ struct pata_pxa_data *data;
+ struct resource *cmd_res;
+ struct resource *ctl_res;
+ struct resource *dma_res;
+ struct resource *irq_res;
+ struct pata_pxa_pdata *pdata = pdev->dev.platform_data;
+ int ret = 0;
+
+ /*
+ * Resource validation, three resources are needed:
+ * - CMD port base address
+ * - CTL port base address
+ * - DMA port base address
+ * - IRQ pin
+ */
+ if (pdev->num_resources != 4) {
+ dev_err(&pdev->dev, "invalid number of resources\n");
+ return -EINVAL;
+ }
+
+ /*
+ * CMD port base address
+ */
+ cmd_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (unlikely(cmd_res == NULL))
+ return -EINVAL;
+
+ /*
+ * CTL port base address
+ */
+ ctl_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (unlikely(ctl_res == NULL))
+ return -EINVAL;
+
+ /*
+ * DMA port base address
+ */
+ dma_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (unlikely(dma_res == NULL))
+ return -EINVAL;
+
+ /*
+ * IRQ pin
+ */
+ irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (unlikely(irq_res == NULL))
+ return -EINVAL;
+
+ /*
+ * Allocate the host
+ */
+ host = ata_host_alloc(&pdev->dev, 1);
+ if (!host)
+ return -ENOMEM;
+
+ ap = host->ports[0];
+ ap->ops = &pxa_ata_port_ops;
+ ap->pio_mask = ATA_PIO4;
+ ap->mwdma_mask = ATA_MWDMA2;
+ ap->flags = ATA_FLAG_MMIO;
+
+ ap->ioaddr.cmd_addr = devm_ioremap(&pdev->dev, cmd_res->start,
+ resource_size(cmd_res));
+ ap->ioaddr.ctl_addr = devm_ioremap(&pdev->dev, ctl_res->start,
+ resource_size(ctl_res));
+ ap->ioaddr.bmdma_addr = devm_ioremap(&pdev->dev, dma_res->start,
+ resource_size(dma_res));
+
+ /*
+ * Adjust register offsets
+ */
+ ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr;
+ ap->ioaddr.data_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_DATA << pdata->reg_shift);
+ ap->ioaddr.error_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_ERR << pdata->reg_shift);
+ ap->ioaddr.feature_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_FEATURE << pdata->reg_shift);
+ ap->ioaddr.nsect_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_NSECT << pdata->reg_shift);
+ ap->ioaddr.lbal_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_LBAL << pdata->reg_shift);
+ ap->ioaddr.lbam_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_LBAM << pdata->reg_shift);
+ ap->ioaddr.lbah_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_LBAH << pdata->reg_shift);
+ ap->ioaddr.device_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_DEVICE << pdata->reg_shift);
+ ap->ioaddr.status_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_STATUS << pdata->reg_shift);
+ ap->ioaddr.command_addr = ap->ioaddr.cmd_addr +
+ (ATA_REG_CMD << pdata->reg_shift);
+
+ /*
+ * Allocate and load driver's internal data structure
+ */
+ data = devm_kzalloc(&pdev->dev, sizeof(struct pata_pxa_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ap->private_data = data;
+ data->dma_dreq = pdata->dma_dreq;
+ data->dma_io_addr = dma_res->start;
+
+ /*
+ * Allocate space for the DMA descriptors
+ */
+ data->dma_desc = dmam_alloc_coherent(&pdev->dev, PAGE_SIZE,
+ &data->dma_desc_addr, GFP_KERNEL);
+ if (!data->dma_desc)
+ return -EINVAL;
+
+ /*
+ * Request the DMA channel
+ */
+ data->dma_channel = pxa_request_dma(DRV_NAME, DMA_PRIO_LOW,
+ pxa_ata_dma_irq, ap);
+ if (data->dma_channel < 0)
+ return -EBUSY;
+
+ /*
+ * Stop and clear the DMA channel
+ */
+ DCSR(data->dma_channel) = 0;
+
+ /*
+ * Activate the ATA host
+ */
+ ret = ata_host_activate(host, irq_res->start, ata_sff_interrupt,
+ pdata->irq_flags, &pxa_ata_sht);
+ if (ret)
+ pxa_free_dma(data->dma_channel);
+
+ return ret;
+}
+
+static int __devexit pxa_ata_remove(struct platform_device *pdev)
+{
+ struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ struct pata_pxa_data *data = host->ports[0]->private_data;
+
+ pxa_free_dma(data->dma_channel);
+
+ ata_host_detach(host);
+
+ return 0;
+}
+
+static struct platform_driver pxa_ata_driver = {
+ .probe = pxa_ata_probe,
+ .remove = __devexit_p(pxa_ata_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pxa_ata_init(void)
+{
+ return platform_driver_register(&pxa_ata_driver);
+}
+
+static void __exit pxa_ata_exit(void)
+{
+ platform_driver_unregister(&pxa_ata_driver);
+}
+
+module_init(pxa_ata_init);
+module_exit(pxa_ata_exit);
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("DMA-capable driver for PATA on PXA CPU");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+MODULE_ALIAS("platform:" DRV_NAME);
struct gendisk *disk = bdev->bd_disk;
DAC960_Controller_T *p = disk->queue->queuedata;
int drive_nr = (long)disk->private_data;
+ int ret = -ENXIO;
+ lock_kernel();
if (p->FirmwareType == DAC960_V1_Controller) {
if (p->V1.LogicalDriveInformation[drive_nr].
LogicalDriveState == DAC960_V1_LogicalDrive_Offline)
- return -ENXIO;
+ goto out;
} else {
DAC960_V2_LogicalDeviceInfo_T *i =
p->V2.LogicalDeviceInformation[drive_nr];
if (!i || i->LogicalDeviceState == DAC960_V2_LogicalDevice_Offline)
- return -ENXIO;
+ goto out;
}
check_disk_change(bdev);
if (!get_capacity(p->disks[drive_nr]))
- return -ENXIO;
- return 0;
+ goto out;
+ ret = 0;
+out:
+ unlock_kernel();
+ return ret;
}
static int DAC960_getgeo(struct block_device *bdev, struct hd_geometry *geo)
#include <linux/hdreg.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/smp_lock.h>
#include <linux/amifdreg.h>
#include <linux/amifd.h>
#include <linux/buffer_head.h>
return 0;
}
-static int fd_ioctl(struct block_device *bdev, fmode_t mode,
+static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long param)
{
struct amiga_floppy_struct *p = bdev->bd_disk->private_data;
return 0;
}
+static int fd_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long param)
+{
+ int ret;
+
+ lock_kernel();
+ ret = fd_locked_ioctl(bdev, mode, cmd, param);
+ unlock_kernel();
+
+ return ret;
+}
+
static void fd_probe(int dev)
{
unsigned long code;
int old_dev;
unsigned long flags;
+ lock_kernel();
old_dev = fd_device[drive];
- if (fd_ref[drive] && old_dev != system)
+ if (fd_ref[drive] && old_dev != system) {
+ unlock_kernel();
return -EBUSY;
+ }
if (mode & (FMODE_READ|FMODE_WRITE)) {
check_disk_change(bdev);
fd_deselect (drive);
rel_fdc();
- if (wrprot)
+ if (wrprot) {
+ unlock_kernel();
return -EROFS;
+ }
}
}
printk(KERN_INFO "fd%d: accessing %s-disk with %s-layout\n",drive,
unit[drive].type->name, data_types[system].name);
+ unlock_kernel();
return 0;
}
struct amiga_floppy_struct *p = disk->private_data;
int drive = p - unit;
+ lock_kernel();
if (unit[drive].dirty == 1) {
del_timer (flush_track_timer + drive);
non_int_flush_track (drive);
/* the mod_use counter is handled this way */
floppy_off (drive | 0x40000000);
#endif
+ unlock_kernel();
return 0;
}
.owner = THIS_MODULE,
.open = floppy_open,
.release = floppy_release,
- .locked_ioctl = fd_ioctl,
+ .ioctl = fd_ioctl,
.getgeo = fd_getgeo,
.media_changed = amiga_floppy_change,
};
#include <linux/slab.h>
#include <linux/genhd.h>
#include <linux/netdevice.h>
+#include <linux/smp_lock.h>
#include "aoe.h"
static struct kmem_cache *buf_pool_cache;
struct aoedev *d = bdev->bd_disk->private_data;
ulong flags;
+ lock_kernel();
spin_lock_irqsave(&d->lock, flags);
if (d->flags & DEVFL_UP) {
d->nopen++;
spin_unlock_irqrestore(&d->lock, flags);
+ unlock_kernel();
return 0;
}
spin_unlock_irqrestore(&d->lock, flags);
+ unlock_kernel();
return -ENODEV;
}
BUG();
bio_endio(bio, -ENXIO);
return 0;
- } else if (bio_rw_flagged(bio, BIO_RW_BARRIER)) {
+ } else if (bio->bi_rw & REQ_HARDBARRIER) {
bio_endio(bio, -EOPNOTSUPP);
return 0;
} else if (bio->bi_io_vec == NULL) {
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/blkdev.h>
+#include <linux/smp_lock.h>
#include <asm/atafd.h>
#include <asm/atafdreg.h>
static void finish_fdc_done( int dummy );
static void setup_req_params( int drive );
static void redo_fd_request( void);
-static int fd_ioctl(struct block_device *bdev, fmode_t mode, unsigned int
+static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int
cmd, unsigned long param);
static void fd_probe( int drive );
static int fd_test_drive_present( int drive );
atari_enable_irq( IRQ_MFP_FDC );
}
-static int fd_ioctl(struct block_device *bdev, fmode_t mode,
+static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long param)
{
struct gendisk *disk = bdev->bd_disk;
}
}
+static int fd_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret;
+
+ lock_kernel();
+ ret = fd_locked_ioctl(bdev, mode, cmd, arg);
+ unlock_kernel();
+
+ return ret;
+}
/* Initialize the 'unit' variable for drive 'drive' */
return 0;
}
+static int floppy_unlocked_open(struct block_device *bdev, fmode_t mode)
+{
+ int ret;
+
+ lock_kernel();
+ ret = floppy_open(bdev, mode);
+ unlock_kernel();
+
+ return ret;
+}
static int floppy_release(struct gendisk *disk, fmode_t mode)
{
struct atari_floppy_struct *p = disk->private_data;
+ lock_kernel();
if (p->ref < 0)
p->ref = 0;
else if (!p->ref--) {
printk(KERN_ERR "floppy_release with fd_ref == 0");
p->ref = 0;
}
+ unlock_kernel();
return 0;
}
static const struct block_device_operations floppy_fops = {
.owner = THIS_MODULE,
- .open = floppy_open,
+ .open = floppy_unlocked_open,
.release = floppy_release,
- .locked_ioctl = fd_ioctl,
+ .ioctl = fd_ioctl,
.media_changed = check_floppy_change,
.revalidate_disk= floppy_revalidate,
};
#include <linux/blkdev.h>
#include <linux/bio.h>
#include <linux/highmem.h>
+#include <linux/smp_lock.h>
#include <linux/radix-tree.h>
#include <linux/buffer_head.h> /* invalidate_bh_lrus() */
#include <linux/slab.h>
get_capacity(bdev->bd_disk))
goto out;
- if (unlikely(bio_rw_flagged(bio, BIO_RW_DISCARD))) {
+ if (unlikely(bio->bi_rw & REQ_DISCARD)) {
err = 0;
discard_from_brd(brd, sector, bio->bi_size);
goto out;
* ram device BLKFLSBUF has special semantics, we want to actually
* release and destroy the ramdisk data.
*/
+ lock_kernel();
mutex_lock(&bdev->bd_mutex);
error = -EBUSY;
if (bdev->bd_openers <= 1) {
error = 0;
}
mutex_unlock(&bdev->bd_mutex);
+ unlock_kernel();
return error;
}
static const struct block_device_operations brd_fops = {
.owner = THIS_MODULE,
- .locked_ioctl = brd_ioctl,
+ .ioctl = brd_ioctl,
#ifdef CONFIG_BLK_DEV_XIP
.direct_access = brd_direct_access,
#endif
if (!brd->brd_queue)
goto out_free_dev;
blk_queue_make_request(brd->brd_queue, brd_make_request);
- blk_queue_ordered(brd->brd_queue, QUEUE_ORDERED_TAG, NULL);
+ blk_queue_ordered(brd->brd_queue, QUEUE_ORDERED_TAG);
blk_queue_max_hw_sectors(brd->brd_queue, 1024);
blk_queue_bounce_limit(brd->brd_queue, BLK_BOUNCE_ANY);
#include <linux/kthread.h>
#define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
-#define DRIVER_NAME "HP CISS Driver (v 3.6.20)"
-#define DRIVER_VERSION CCISS_DRIVER_VERSION(3, 6, 20)
+#define DRIVER_NAME "HP CISS Driver (v 3.6.26)"
+#define DRIVER_VERSION CCISS_DRIVER_VERSION(3, 6, 26)
/* Embedded module documentation macros - see modules.h */
MODULE_AUTHOR("Hewlett-Packard Company");
MODULE_DESCRIPTION("Driver for HP Smart Array Controllers");
-MODULE_SUPPORTED_DEVICE("HP SA5i SA5i+ SA532 SA5300 SA5312 SA641 SA642 SA6400"
- " SA6i P600 P800 P400 P400i E200 E200i E500 P700m"
- " Smart Array G2 Series SAS/SATA Controllers");
-MODULE_VERSION("3.6.20");
+MODULE_SUPPORTED_DEVICE("HP Smart Array Controllers");
+MODULE_VERSION("3.6.26");
MODULE_LICENSE("GPL");
static int cciss_allow_hpsa;
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3249},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x324A},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x324B},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3250},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3251},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3252},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3253},
+ {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSE, 0x103C, 0x3254},
{0,}
};
{0x3249103C, "Smart Array P812", &SA5_access},
{0x324A103C, "Smart Array P712m", &SA5_access},
{0x324B103C, "Smart Array P711m", &SA5_access},
+ {0x3250103C, "Smart Array", &SA5_access},
+ {0x3251103C, "Smart Array", &SA5_access},
+ {0x3252103C, "Smart Array", &SA5_access},
+ {0x3253103C, "Smart Array", &SA5_access},
+ {0x3254103C, "Smart Array", &SA5_access},
};
/* How long to wait (in milliseconds) for board to go into simple mode */
static LIST_HEAD(scan_q);
static void do_cciss_request(struct request_queue *q);
-static irqreturn_t do_cciss_intr(int irq, void *dev_id);
+static irqreturn_t do_cciss_intx(int irq, void *dev_id);
+static irqreturn_t do_cciss_msix_intr(int irq, void *dev_id);
static int cciss_open(struct block_device *bdev, fmode_t mode);
+static int cciss_unlocked_open(struct block_device *bdev, fmode_t mode);
static int cciss_release(struct gendisk *disk, fmode_t mode);
+static int do_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long arg);
static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg);
static int cciss_getgeo(struct block_device *bdev, struct hd_geometry *geo);
static int deregister_disk(ctlr_info_t *h, int drv_index,
int clear_all, int via_ioctl);
-static void cciss_read_capacity(int ctlr, int logvol,
+static void cciss_read_capacity(ctlr_info_t *h, int logvol,
sector_t *total_size, unsigned int *block_size);
-static void cciss_read_capacity_16(int ctlr, int logvol,
+static void cciss_read_capacity_16(ctlr_info_t *h, int logvol,
sector_t *total_size, unsigned int *block_size);
-static void cciss_geometry_inquiry(int ctlr, int logvol,
+static void cciss_geometry_inquiry(ctlr_info_t *h, int logvol,
sector_t total_size,
unsigned int block_size, InquiryData_struct *inq_buff,
drive_info_struct *drv);
-static void __devinit cciss_interrupt_mode(ctlr_info_t *, struct pci_dev *,
- __u32);
+static void __devinit cciss_interrupt_mode(ctlr_info_t *);
static void start_io(ctlr_info_t *h);
-static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size,
+static int sendcmd_withirq(ctlr_info_t *h, __u8 cmd, void *buff, size_t size,
__u8 page_code, unsigned char scsi3addr[],
int cmd_type);
static int sendcmd_withirq_core(ctlr_info_t *h, CommandList_struct *c,
int attempt_retry);
static int process_sendcmd_error(ctlr_info_t *h, CommandList_struct *c);
-static void fail_all_cmds(unsigned long ctlr);
static int add_to_scan_list(struct ctlr_info *h);
static int scan_thread(void *data);
static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c);
static void cciss_device_release(struct device *dev);
static void cciss_free_gendisk(ctlr_info_t *h, int drv_index);
static void cciss_free_drive_info(ctlr_info_t *h, int drv_index);
+static inline u32 next_command(ctlr_info_t *h);
+static int __devinit cciss_find_cfg_addrs(struct pci_dev *pdev,
+ void __iomem *vaddr, u32 *cfg_base_addr, u64 *cfg_base_addr_index,
+ u64 *cfg_offset);
+static int __devinit cciss_pci_find_memory_BAR(struct pci_dev *pdev,
+ unsigned long *memory_bar);
+
+
+/* performant mode helper functions */
+static void calc_bucket_map(int *bucket, int num_buckets, int nsgs,
+ int *bucket_map);
+static void cciss_put_controller_into_performant_mode(ctlr_info_t *h);
#ifdef CONFIG_PROC_FS
-static void cciss_procinit(int i);
+static void cciss_procinit(ctlr_info_t *h);
#else
-static void cciss_procinit(int i)
+static void cciss_procinit(ctlr_info_t *h)
{
}
#endif /* CONFIG_PROC_FS */
static const struct block_device_operations cciss_fops = {
.owner = THIS_MODULE,
- .open = cciss_open,
+ .open = cciss_unlocked_open,
.release = cciss_release,
- .locked_ioctl = cciss_ioctl,
+ .ioctl = do_ioctl,
.getgeo = cciss_getgeo,
#ifdef CONFIG_COMPAT
.compat_ioctl = cciss_compat_ioctl,
.revalidate_disk = cciss_revalidate,
};
+/* set_performant_mode: Modify the tag for cciss performant
+ * set bit 0 for pull model, bits 3-1 for block fetch
+ * register number
+ */
+static void set_performant_mode(ctlr_info_t *h, CommandList_struct *c)
+{
+ if (likely(h->transMethod == CFGTBL_Trans_Performant))
+ c->busaddr |= 1 | (h->blockFetchTable[c->Header.SGList] << 1);
+}
+
/*
* Enqueuing and dequeuing functions for cmdlists.
*/
hlist_del_init(&c->list);
}
+static void enqueue_cmd_and_start_io(ctlr_info_t *h,
+ CommandList_struct *c)
+{
+ unsigned long flags;
+ set_performant_mode(h, c);
+ spin_lock_irqsave(&h->lock, flags);
+ addQ(&h->reqQ, c);
+ h->Qdepth++;
+ start_io(h);
+ spin_unlock_irqrestore(&h->lock, flags);
+}
+
static void cciss_free_sg_chain_blocks(SGDescriptor_struct **cmd_sg_list,
int nr_cmds)
{
h->product_name,
(unsigned long)h->board_id,
h->firm_ver[0], h->firm_ver[1], h->firm_ver[2],
- h->firm_ver[3], (unsigned int)h->intr[SIMPLE_MODE_INT],
+ h->firm_ver[3], (unsigned int)h->intr[PERF_MODE_INT],
h->num_luns,
h->Qdepth, h->commands_outstanding,
h->maxQsinceinit, h->max_outstanding, h->maxSG);
#ifdef CONFIG_CISS_SCSI_TAPE
- cciss_seq_tape_report(seq, h->ctlr);
+ cciss_seq_tape_report(seq, h);
#endif /* CONFIG_CISS_SCSI_TAPE */
}
static void *cciss_seq_start(struct seq_file *seq, loff_t *pos)
{
ctlr_info_t *h = seq->private;
- unsigned ctlr = h->ctlr;
unsigned long flags;
/* prevent displaying bogus info during configuration
* or deconfiguration of a logical volume
*/
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
if (h->busy_configuring) {
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
return ERR_PTR(-EBUSY);
}
h->busy_configuring = 1;
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
if (*pos == 0)
cciss_seq_show_header(seq);
struct seq_file *seq = file->private_data;
ctlr_info_t *h = seq->private;
- err = cciss_engage_scsi(h->ctlr);
+ err = cciss_engage_scsi(h);
if (err == 0)
err = length;
} else
.write = cciss_proc_write,
};
-static void __devinit cciss_procinit(int i)
+static void __devinit cciss_procinit(ctlr_info_t *h)
{
struct proc_dir_entry *pde;
proc_cciss = proc_mkdir("driver/cciss", NULL);
if (!proc_cciss)
return;
- pde = proc_create_data(hba[i]->devname, S_IWUSR | S_IRUSR | S_IRGRP |
+ pde = proc_create_data(h->devname, S_IWUSR | S_IRUSR | S_IRGRP |
S_IROTH, proc_cciss,
- &cciss_proc_fops, hba[i]);
+ &cciss_proc_fops, h);
}
#endif /* CONFIG_PROC_FS */
unsigned long flags;
int ret = 0;
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
if (h->busy_configuring)
ret = -EBUSY;
else
memcpy(sn, drv->serial_no, sizeof(sn));
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
if (ret)
return ret;
unsigned long flags;
int ret = 0;
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
if (h->busy_configuring)
ret = -EBUSY;
else
memcpy(vendor, drv->vendor, VENDOR_LEN + 1);
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
if (ret)
return ret;
unsigned long flags;
int ret = 0;
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
if (h->busy_configuring)
ret = -EBUSY;
else
memcpy(model, drv->model, MODEL_LEN + 1);
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
if (ret)
return ret;
unsigned long flags;
int ret = 0;
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
if (h->busy_configuring)
ret = -EBUSY;
else
memcpy(rev, drv->rev, REV_LEN + 1);
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
if (ret)
return ret;
unsigned long flags;
unsigned char lunid[8];
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
if (h->busy_configuring) {
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
return -EBUSY;
}
if (!drv->heads) {
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
return -ENOTTY;
}
memcpy(lunid, drv->LunID, sizeof(lunid));
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
return snprintf(buf, 20, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
lunid[0], lunid[1], lunid[2], lunid[3],
lunid[4], lunid[5], lunid[6], lunid[7]);
int raid;
unsigned long flags;
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
if (h->busy_configuring) {
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
return -EBUSY;
}
raid = drv->raid_level;
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
if (raid < 0 || raid > RAID_UNKNOWN)
raid = RAID_UNKNOWN;
unsigned long flags;
int count;
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
if (h->busy_configuring) {
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
return -EBUSY;
}
count = drv->usage_count;
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
return snprintf(buf, 20, "%d\n", count);
}
static DEVICE_ATTR(usage_count, S_IRUGO, cciss_show_usage_count, NULL);
/*
* For operations that cannot sleep, a command block is allocated at init,
* and managed by cmd_alloc() and cmd_free() using a simple bitmap to track
- * which ones are free or in use. For operations that can wait for kmalloc
- * to possible sleep, this routine can be called with get_from_pool set to 0.
- * cmd_free() MUST be called with a got_from_pool set to 0 if cmd_alloc was.
+ * which ones are free or in use.
*/
-static CommandList_struct *cmd_alloc(ctlr_info_t *h, int get_from_pool)
+static CommandList_struct *cmd_alloc(ctlr_info_t *h)
{
CommandList_struct *c;
int i;
u64bit temp64;
dma_addr_t cmd_dma_handle, err_dma_handle;
- if (!get_from_pool) {
- c = (CommandList_struct *) pci_alloc_consistent(h->pdev,
- sizeof(CommandList_struct), &cmd_dma_handle);
- if (c == NULL)
+ do {
+ i = find_first_zero_bit(h->cmd_pool_bits, h->nr_cmds);
+ if (i == h->nr_cmds)
return NULL;
- memset(c, 0, sizeof(CommandList_struct));
+ } while (test_and_set_bit(i & (BITS_PER_LONG - 1),
+ h->cmd_pool_bits + (i / BITS_PER_LONG)) != 0);
+ c = h->cmd_pool + i;
+ memset(c, 0, sizeof(CommandList_struct));
+ cmd_dma_handle = h->cmd_pool_dhandle + i * sizeof(CommandList_struct);
+ c->err_info = h->errinfo_pool + i;
+ memset(c->err_info, 0, sizeof(ErrorInfo_struct));
+ err_dma_handle = h->errinfo_pool_dhandle
+ + i * sizeof(ErrorInfo_struct);
+ h->nr_allocs++;
- c->cmdindex = -1;
+ c->cmdindex = i;
- c->err_info = (ErrorInfo_struct *)
- pci_alloc_consistent(h->pdev, sizeof(ErrorInfo_struct),
- &err_dma_handle);
+ INIT_HLIST_NODE(&c->list);
+ c->busaddr = (__u32) cmd_dma_handle;
+ temp64.val = (__u64) err_dma_handle;
+ c->ErrDesc.Addr.lower = temp64.val32.lower;
+ c->ErrDesc.Addr.upper = temp64.val32.upper;
+ c->ErrDesc.Len = sizeof(ErrorInfo_struct);
- if (c->err_info == NULL) {
- pci_free_consistent(h->pdev,
- sizeof(CommandList_struct), c, cmd_dma_handle);
- return NULL;
- }
- memset(c->err_info, 0, sizeof(ErrorInfo_struct));
- } else { /* get it out of the controllers pool */
-
- do {
- i = find_first_zero_bit(h->cmd_pool_bits, h->nr_cmds);
- if (i == h->nr_cmds)
- return NULL;
- } while (test_and_set_bit
- (i & (BITS_PER_LONG - 1),
- h->cmd_pool_bits + (i / BITS_PER_LONG)) != 0);
-#ifdef CCISS_DEBUG
- printk(KERN_DEBUG "cciss: using command buffer %d\n", i);
-#endif
- c = h->cmd_pool + i;
- memset(c, 0, sizeof(CommandList_struct));
- cmd_dma_handle = h->cmd_pool_dhandle
- + i * sizeof(CommandList_struct);
- c->err_info = h->errinfo_pool + i;
- memset(c->err_info, 0, sizeof(ErrorInfo_struct));
- err_dma_handle = h->errinfo_pool_dhandle
- + i * sizeof(ErrorInfo_struct);
- h->nr_allocs++;
+ c->ctlr = h->ctlr;
+ return c;
+}
- c->cmdindex = i;
+/* allocate a command using pci_alloc_consistent, used for ioctls,
+ * etc., not for the main i/o path.
+ */
+static CommandList_struct *cmd_special_alloc(ctlr_info_t *h)
+{
+ CommandList_struct *c;
+ u64bit temp64;
+ dma_addr_t cmd_dma_handle, err_dma_handle;
+
+ c = (CommandList_struct *) pci_alloc_consistent(h->pdev,
+ sizeof(CommandList_struct), &cmd_dma_handle);
+ if (c == NULL)
+ return NULL;
+ memset(c, 0, sizeof(CommandList_struct));
+
+ c->cmdindex = -1;
+
+ c->err_info = (ErrorInfo_struct *)
+ pci_alloc_consistent(h->pdev, sizeof(ErrorInfo_struct),
+ &err_dma_handle);
+
+ if (c->err_info == NULL) {
+ pci_free_consistent(h->pdev,
+ sizeof(CommandList_struct), c, cmd_dma_handle);
+ return NULL;
}
+ memset(c->err_info, 0, sizeof(ErrorInfo_struct));
INIT_HLIST_NODE(&c->list);
c->busaddr = (__u32) cmd_dma_handle;
return c;
}
-/*
- * Frees a command block that was previously allocated with cmd_alloc().
- */
-static void cmd_free(ctlr_info_t *h, CommandList_struct *c, int got_from_pool)
+static void cmd_free(ctlr_info_t *h, CommandList_struct *c)
{
int i;
+
+ i = c - h->cmd_pool;
+ clear_bit(i & (BITS_PER_LONG - 1),
+ h->cmd_pool_bits + (i / BITS_PER_LONG));
+ h->nr_frees++;
+}
+
+static void cmd_special_free(ctlr_info_t *h, CommandList_struct *c)
+{
u64bit temp64;
- if (!got_from_pool) {
- temp64.val32.lower = c->ErrDesc.Addr.lower;
- temp64.val32.upper = c->ErrDesc.Addr.upper;
- pci_free_consistent(h->pdev, sizeof(ErrorInfo_struct),
- c->err_info, (dma_addr_t) temp64.val);
- pci_free_consistent(h->pdev, sizeof(CommandList_struct),
- c, (dma_addr_t) c->busaddr);
- } else {
- i = c - h->cmd_pool;
- clear_bit(i & (BITS_PER_LONG - 1),
- h->cmd_pool_bits + (i / BITS_PER_LONG));
- h->nr_frees++;
- }
+ temp64.val32.lower = c->ErrDesc.Addr.lower;
+ temp64.val32.upper = c->ErrDesc.Addr.upper;
+ pci_free_consistent(h->pdev, sizeof(ErrorInfo_struct),
+ c->err_info, (dma_addr_t) temp64.val);
+ pci_free_consistent(h->pdev, sizeof(CommandList_struct),
+ c, (dma_addr_t) c->busaddr);
}
static inline ctlr_info_t *get_host(struct gendisk *disk)
*/
static int cciss_open(struct block_device *bdev, fmode_t mode)
{
- ctlr_info_t *host = get_host(bdev->bd_disk);
+ ctlr_info_t *h = get_host(bdev->bd_disk);
drive_info_struct *drv = get_drv(bdev->bd_disk);
-#ifdef CCISS_DEBUG
- printk(KERN_DEBUG "cciss_open %s\n", bdev->bd_disk->disk_name);
-#endif /* CCISS_DEBUG */
-
+ dev_dbg(&h->pdev->dev, "cciss_open %s\n", bdev->bd_disk->disk_name);
if (drv->busy_configuring)
return -EBUSY;
/*
return -EPERM;
}
drv->usage_count++;
- host->usage_count++;
+ h->usage_count++;
return 0;
}
+static int cciss_unlocked_open(struct block_device *bdev, fmode_t mode)
+{
+ int ret;
+
+ lock_kernel();
+ ret = cciss_open(bdev, mode);
+ unlock_kernel();
+
+ return ret;
+}
+
/*
* Close. Sync first.
*/
static int cciss_release(struct gendisk *disk, fmode_t mode)
{
- ctlr_info_t *host = get_host(disk);
- drive_info_struct *drv = get_drv(disk);
-
-#ifdef CCISS_DEBUG
- printk(KERN_DEBUG "cciss_release %s\n", disk->disk_name);
-#endif /* CCISS_DEBUG */
+ ctlr_info_t *h;
+ drive_info_struct *drv;
+ lock_kernel();
+ h = get_host(disk);
+ drv = get_drv(disk);
+ dev_dbg(&h->pdev->dev, "cciss_release %s\n", disk->disk_name);
drv->usage_count--;
- host->usage_count--;
+ h->usage_count--;
+ unlock_kernel();
return 0;
}
-#ifdef CONFIG_COMPAT
-
static int do_ioctl(struct block_device *bdev, fmode_t mode,
unsigned cmd, unsigned long arg)
{
return ret;
}
+#ifdef CONFIG_COMPAT
+
static int cciss_ioctl32_passthru(struct block_device *bdev, fmode_t mode,
unsigned cmd, unsigned long arg);
static int cciss_ioctl32_big_passthru(struct block_device *bdev, fmode_t mode,
return 0;
}
-static void check_ioctl_unit_attention(ctlr_info_t *host, CommandList_struct *c)
+static void check_ioctl_unit_attention(ctlr_info_t *h, CommandList_struct *c)
{
if (c->err_info->CommandStatus == CMD_TARGET_STATUS &&
c->err_info->ScsiStatus != SAM_STAT_CHECK_CONDITION)
- (void)check_for_unit_attention(host, c);
+ (void)check_for_unit_attention(h, c);
}
/*
* ioctl
unsigned int cmd, unsigned long arg)
{
struct gendisk *disk = bdev->bd_disk;
- ctlr_info_t *host = get_host(disk);
+ ctlr_info_t *h = get_host(disk);
drive_info_struct *drv = get_drv(disk);
- int ctlr = host->ctlr;
void __user *argp = (void __user *)arg;
-#ifdef CCISS_DEBUG
- printk(KERN_DEBUG "cciss_ioctl: Called with cmd=%x %lx\n", cmd, arg);
-#endif /* CCISS_DEBUG */
-
+ dev_dbg(&h->pdev->dev, "cciss_ioctl: Called with cmd=%x %lx\n",
+ cmd, arg);
switch (cmd) {
case CCISS_GETPCIINFO:
{
if (!arg)
return -EINVAL;
- pciinfo.domain = pci_domain_nr(host->pdev->bus);
- pciinfo.bus = host->pdev->bus->number;
- pciinfo.dev_fn = host->pdev->devfn;
- pciinfo.board_id = host->board_id;
+ pciinfo.domain = pci_domain_nr(h->pdev->bus);
+ pciinfo.bus = h->pdev->bus->number;
+ pciinfo.dev_fn = h->pdev->devfn;
+ pciinfo.board_id = h->board_id;
if (copy_to_user
(argp, &pciinfo, sizeof(cciss_pci_info_struct)))
return -EFAULT;
if (!arg)
return -EINVAL;
intinfo.delay =
- readl(&host->cfgtable->HostWrite.CoalIntDelay);
+ readl(&h->cfgtable->HostWrite.CoalIntDelay);
intinfo.count =
- readl(&host->cfgtable->HostWrite.CoalIntCount);
+ readl(&h->cfgtable->HostWrite.CoalIntCount);
if (copy_to_user
(argp, &intinfo, sizeof(cciss_coalint_struct)))
return -EFAULT;
(&intinfo, argp, sizeof(cciss_coalint_struct)))
return -EFAULT;
if ((intinfo.delay == 0) && (intinfo.count == 0))
- {
-// printk("cciss_ioctl: delay and count cannot be 0\n");
return -EINVAL;
- }
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
/* Update the field, and then ring the doorbell */
writel(intinfo.delay,
- &(host->cfgtable->HostWrite.CoalIntDelay));
+ &(h->cfgtable->HostWrite.CoalIntDelay));
writel(intinfo.count,
- &(host->cfgtable->HostWrite.CoalIntCount));
- writel(CFGTBL_ChangeReq, host->vaddr + SA5_DOORBELL);
+ &(h->cfgtable->HostWrite.CoalIntCount));
+ writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL);
for (i = 0; i < MAX_IOCTL_CONFIG_WAIT; i++) {
- if (!(readl(host->vaddr + SA5_DOORBELL)
+ if (!(readl(h->vaddr + SA5_DOORBELL)
& CFGTBL_ChangeReq))
break;
/* delay and try again */
udelay(1000);
}
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
if (i >= MAX_IOCTL_CONFIG_WAIT)
return -EAGAIN;
return 0;
return -EINVAL;
for (i = 0; i < 16; i++)
NodeName[i] =
- readb(&host->cfgtable->ServerName[i]);
+ readb(&h->cfgtable->ServerName[i]);
if (copy_to_user(argp, NodeName, sizeof(NodeName_type)))
return -EFAULT;
return 0;
(NodeName, argp, sizeof(NodeName_type)))
return -EFAULT;
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
/* Update the field, and then ring the doorbell */
for (i = 0; i < 16; i++)
writeb(NodeName[i],
- &host->cfgtable->ServerName[i]);
+ &h->cfgtable->ServerName[i]);
- writel(CFGTBL_ChangeReq, host->vaddr + SA5_DOORBELL);
+ writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL);
for (i = 0; i < MAX_IOCTL_CONFIG_WAIT; i++) {
- if (!(readl(host->vaddr + SA5_DOORBELL)
+ if (!(readl(h->vaddr + SA5_DOORBELL)
& CFGTBL_ChangeReq))
break;
/* delay and try again */
udelay(1000);
}
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
if (i >= MAX_IOCTL_CONFIG_WAIT)
return -EAGAIN;
return 0;
if (!arg)
return -EINVAL;
- heartbeat = readl(&host->cfgtable->HeartBeat);
+ heartbeat = readl(&h->cfgtable->HeartBeat);
if (copy_to_user
(argp, &heartbeat, sizeof(Heartbeat_type)))
return -EFAULT;
if (!arg)
return -EINVAL;
- BusTypes = readl(&host->cfgtable->BusTypes);
+ BusTypes = readl(&h->cfgtable->BusTypes);
if (copy_to_user
(argp, &BusTypes, sizeof(BusTypes_type)))
return -EFAULT;
if (!arg)
return -EINVAL;
- memcpy(firmware, host->firm_ver, 4);
+ memcpy(firmware, h->firm_ver, 4);
if (copy_to_user
(argp, firmware, sizeof(FirmwareVer_type)))
case CCISS_DEREGDISK:
case CCISS_REGNEWD:
case CCISS_REVALIDVOLS:
- return rebuild_lun_table(host, 0, 1);
+ return rebuild_lun_table(h, 0, 1);
case CCISS_GETLUNINFO:{
LogvolInfo_struct luninfo;
CommandList_struct *c;
char *buff = NULL;
u64bit temp64;
- unsigned long flags;
DECLARE_COMPLETION_ONSTACK(wait);
if (!arg)
} else {
memset(buff, 0, iocommand.buf_size);
}
- if ((c = cmd_alloc(host, 0)) == NULL) {
+ c = cmd_special_alloc(h);
+ if (!c) {
kfree(buff);
return -ENOMEM;
}
/* Fill in the scatter gather information */
if (iocommand.buf_size > 0) {
- temp64.val = pci_map_single(host->pdev, buff,
+ temp64.val = pci_map_single(h->pdev, buff,
iocommand.buf_size,
PCI_DMA_BIDIRECTIONAL);
c->SG[0].Addr.lower = temp64.val32.lower;
}
c->waiting = &wait;
- /* Put the request on the tail of the request queue */
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
- addQ(&host->reqQ, c);
- host->Qdepth++;
- start_io(host);
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
-
+ enqueue_cmd_and_start_io(h, c);
wait_for_completion(&wait);
/* unlock the buffers from DMA */
temp64.val32.lower = c->SG[0].Addr.lower;
temp64.val32.upper = c->SG[0].Addr.upper;
- pci_unmap_single(host->pdev, (dma_addr_t) temp64.val,
+ pci_unmap_single(h->pdev, (dma_addr_t) temp64.val,
iocommand.buf_size,
PCI_DMA_BIDIRECTIONAL);
- check_ioctl_unit_attention(host, c);
+ check_ioctl_unit_attention(h, c);
/* Copy the error information out */
iocommand.error_info = *(c->err_info);
if (copy_to_user
(argp, &iocommand, sizeof(IOCTL_Command_struct))) {
kfree(buff);
- cmd_free(host, c, 0);
+ cmd_special_free(h, c);
return -EFAULT;
}
if (copy_to_user
(iocommand.buf, buff, iocommand.buf_size)) {
kfree(buff);
- cmd_free(host, c, 0);
+ cmd_special_free(h, c);
return -EFAULT;
}
}
kfree(buff);
- cmd_free(host, c, 0);
+ cmd_special_free(h, c);
return 0;
}
case CCISS_BIG_PASSTHRU:{
unsigned char **buff = NULL;
int *buff_size = NULL;
u64bit temp64;
- unsigned long flags;
BYTE sg_used = 0;
int status = 0;
int i;
data_ptr += sz;
sg_used++;
}
- if ((c = cmd_alloc(host, 0)) == NULL) {
+ c = cmd_special_alloc(h);
+ if (!c) {
status = -ENOMEM;
goto cleanup1;
}
if (ioc->buf_size > 0) {
for (i = 0; i < sg_used; i++) {
temp64.val =
- pci_map_single(host->pdev, buff[i],
+ pci_map_single(h->pdev, buff[i],
buff_size[i],
PCI_DMA_BIDIRECTIONAL);
c->SG[i].Addr.lower =
}
}
c->waiting = &wait;
- /* Put the request on the tail of the request queue */
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
- addQ(&host->reqQ, c);
- host->Qdepth++;
- start_io(host);
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
+ enqueue_cmd_and_start_io(h, c);
wait_for_completion(&wait);
/* unlock the buffers from DMA */
for (i = 0; i < sg_used; i++) {
temp64.val32.lower = c->SG[i].Addr.lower;
temp64.val32.upper = c->SG[i].Addr.upper;
- pci_unmap_single(host->pdev,
+ pci_unmap_single(h->pdev,
(dma_addr_t) temp64.val, buff_size[i],
PCI_DMA_BIDIRECTIONAL);
}
- check_ioctl_unit_attention(host, c);
+ check_ioctl_unit_attention(h, c);
/* Copy the error information out */
ioc->error_info = *(c->err_info);
if (copy_to_user(argp, ioc, sizeof(*ioc))) {
- cmd_free(host, c, 0);
+ cmd_special_free(h, c);
status = -EFAULT;
goto cleanup1;
}
for (i = 0; i < sg_used; i++) {
if (copy_to_user
(ptr, buff[i], buff_size[i])) {
- cmd_free(host, c, 0);
+ cmd_special_free(h, c);
status = -EFAULT;
goto cleanup1;
}
ptr += buff_size[i];
}
}
- cmd_free(host, c, 0);
+ cmd_special_free(h, c);
status = 0;
cleanup1:
if (buff) {
static void cciss_softirq_done(struct request *rq)
{
- CommandList_struct *cmd = rq->completion_data;
- ctlr_info_t *h = hba[cmd->ctlr];
- SGDescriptor_struct *curr_sg = cmd->SG;
- unsigned long flags;
+ CommandList_struct *c = rq->completion_data;
+ ctlr_info_t *h = hba[c->ctlr];
+ SGDescriptor_struct *curr_sg = c->SG;
u64bit temp64;
+ unsigned long flags;
int i, ddir;
int sg_index = 0;
- if (cmd->Request.Type.Direction == XFER_READ)
+ if (c->Request.Type.Direction == XFER_READ)
ddir = PCI_DMA_FROMDEVICE;
else
ddir = PCI_DMA_TODEVICE;
/* command did not need to be retried */
/* unmap the DMA mapping for all the scatter gather elements */
- for (i = 0; i < cmd->Header.SGList; i++) {
+ for (i = 0; i < c->Header.SGList; i++) {
if (curr_sg[sg_index].Ext == CCISS_SG_CHAIN) {
- cciss_unmap_sg_chain_block(h, cmd);
+ cciss_unmap_sg_chain_block(h, c);
/* Point to the next block */
- curr_sg = h->cmd_sg_list[cmd->cmdindex];
+ curr_sg = h->cmd_sg_list[c->cmdindex];
sg_index = 0;
}
temp64.val32.lower = curr_sg[sg_index].Addr.lower;
++sg_index;
}
-#ifdef CCISS_DEBUG
- printk("Done with %p\n", rq);
-#endif /* CCISS_DEBUG */
+ dev_dbg(&h->pdev->dev, "Done with %p\n", rq);
/* set the residual count for pc requests */
- if (blk_pc_request(rq))
- rq->resid_len = cmd->err_info->ResidualCnt;
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC)
+ rq->resid_len = c->err_info->ResidualCnt;
blk_end_request_all(rq, (rq->errors == 0) ? 0 : -EIO);
spin_lock_irqsave(&h->lock, flags);
- cmd_free(h, cmd, 1);
+ cmd_free(h, c);
cciss_check_queues(h);
spin_unlock_irqrestore(&h->lock, flags);
}
* via the inquiry page 0. Model, vendor, and rev are set to empty strings if
* they cannot be read.
*/
-static void cciss_get_device_descr(int ctlr, int logvol,
+static void cciss_get_device_descr(ctlr_info_t *h, int logvol,
char *vendor, char *model, char *rev)
{
int rc;
if (!inq_buf)
return;
- log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
- rc = sendcmd_withirq(CISS_INQUIRY, ctlr, inq_buf, sizeof(*inq_buf), 0,
+ log_unit_to_scsi3addr(h, scsi3addr, logvol);
+ rc = sendcmd_withirq(h, CISS_INQUIRY, inq_buf, sizeof(*inq_buf), 0,
scsi3addr, TYPE_CMD);
if (rc == IO_OK) {
memcpy(vendor, &inq_buf->data_byte[8], VENDOR_LEN);
* number cannot be had, for whatever reason, 16 bytes of 0xff
* are returned instead.
*/
-static void cciss_get_serial_no(int ctlr, int logvol,
+static void cciss_get_serial_no(ctlr_info_t *h, int logvol,
unsigned char *serial_no, int buflen)
{
#define PAGE_83_INQ_BYTES 64
if (!buf)
return;
memset(serial_no, 0, buflen);
- log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
- rc = sendcmd_withirq(CISS_INQUIRY, ctlr, buf,
+ log_unit_to_scsi3addr(h, scsi3addr, logvol);
+ rc = sendcmd_withirq(h, CISS_INQUIRY, buf,
PAGE_83_INQ_BYTES, 0x83, scsi3addr, TYPE_CMD);
if (rc == IO_OK)
memcpy(serial_no, &buf[8], buflen);
* is also the controller node. Any changes to disk 0 will show up on
* the next reboot.
*/
-static void cciss_update_drive_info(int ctlr, int drv_index, int first_time,
- int via_ioctl)
+static void cciss_update_drive_info(ctlr_info_t *h, int drv_index,
+ int first_time, int via_ioctl)
{
- ctlr_info_t *h = hba[ctlr];
struct gendisk *disk;
InquiryData_struct *inq_buff = NULL;
unsigned int block_size;
/* testing to see if 16-byte CDBs are already being used */
if (h->cciss_read == CCISS_READ_16) {
- cciss_read_capacity_16(h->ctlr, drv_index,
+ cciss_read_capacity_16(h, drv_index,
&total_size, &block_size);
} else {
- cciss_read_capacity(ctlr, drv_index, &total_size, &block_size);
+ cciss_read_capacity(h, drv_index, &total_size, &block_size);
/* if read_capacity returns all F's this volume is >2TB */
/* in size so we switch to 16-byte CDB's for all */
/* read/write ops */
if (total_size == 0xFFFFFFFFULL) {
- cciss_read_capacity_16(ctlr, drv_index,
+ cciss_read_capacity_16(h, drv_index,
&total_size, &block_size);
h->cciss_read = CCISS_READ_16;
h->cciss_write = CCISS_WRITE_16;
}
}
- cciss_geometry_inquiry(ctlr, drv_index, total_size, block_size,
+ cciss_geometry_inquiry(h, drv_index, total_size, block_size,
inq_buff, drvinfo);
drvinfo->block_size = block_size;
drvinfo->nr_blocks = total_size + 1;
- cciss_get_device_descr(ctlr, drv_index, drvinfo->vendor,
+ cciss_get_device_descr(h, drv_index, drvinfo->vendor,
drvinfo->model, drvinfo->rev);
- cciss_get_serial_no(ctlr, drv_index, drvinfo->serial_no,
+ cciss_get_serial_no(h, drv_index, drvinfo->serial_no,
sizeof(drvinfo->serial_no));
/* Save the lunid in case we deregister the disk, below. */
memcpy(drvinfo->LunID, h->drv[drv_index]->LunID,
* (unless it's the first disk (for the controller node).
*/
if (h->drv[drv_index]->raid_level != -1 && drv_index != 0) {
- printk(KERN_WARNING "disk %d has changed.\n", drv_index);
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ dev_warn(&h->pdev->dev, "disk %d has changed.\n", drv_index);
+ spin_lock_irqsave(&h->lock, flags);
h->drv[drv_index]->busy_configuring = 1;
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
/* deregister_disk sets h->drv[drv_index]->queue = NULL
* which keeps the interrupt handler from starting
if (cciss_add_disk(h, disk, drv_index) != 0) {
cciss_free_gendisk(h, drv_index);
cciss_free_drive_info(h, drv_index);
- printk(KERN_WARNING "cciss:%d could not update "
- "disk %d\n", h->ctlr, drv_index);
+ dev_warn(&h->pdev->dev, "could not update disk %d\n",
+ drv_index);
--h->num_luns;
}
}
kfree(drvinfo);
return;
mem_msg:
- printk(KERN_ERR "cciss: out of memory\n");
+ dev_err(&h->pdev->dev, "out of memory\n");
goto freeret;
}
h->gendisk[drv_index] =
alloc_disk(1 << NWD_SHIFT);
if (!h->gendisk[drv_index]) {
- printk(KERN_ERR "cciss%d: could not "
- "allocate a new disk %d\n",
- h->ctlr, drv_index);
+ dev_err(&h->pdev->dev,
+ "could not allocate a new disk %d\n",
+ drv_index);
goto err_free_drive_info;
}
}
cciss_free_gendisk(h, drv_index);
cciss_free_drive_info(h, drv_index);
error:
- printk(KERN_WARNING "cciss%d: could not "
- "add disk 0.\n", h->ctlr);
+ dev_warn(&h->pdev->dev, "could not add disk 0.\n");
return;
}
static int rebuild_lun_table(ctlr_info_t *h, int first_time,
int via_ioctl)
{
- int ctlr = h->ctlr;
int num_luns;
ReportLunData_struct *ld_buff = NULL;
int return_code;
return -EPERM;
/* Set busy_configuring flag for this operation */
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
if (h->busy_configuring) {
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
return -EBUSY;
}
h->busy_configuring = 1;
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
ld_buff = kzalloc(sizeof(ReportLunData_struct), GFP_KERNEL);
if (ld_buff == NULL)
goto mem_msg;
- return_code = sendcmd_withirq(CISS_REPORT_LOG, ctlr, ld_buff,
+ return_code = sendcmd_withirq(h, CISS_REPORT_LOG, ld_buff,
sizeof(ReportLunData_struct),
0, CTLR_LUNID, TYPE_CMD);
if (return_code == IO_OK)
listlength = be32_to_cpu(*(__be32 *) ld_buff->LUNListLength);
else { /* reading number of logical volumes failed */
- printk(KERN_WARNING "cciss: report logical volume"
- " command failed\n");
+ dev_warn(&h->pdev->dev,
+ "report logical volume command failed\n");
listlength = 0;
goto freeret;
}
num_luns = listlength / 8; /* 8 bytes per entry */
if (num_luns > CISS_MAX_LUN) {
num_luns = CISS_MAX_LUN;
- printk(KERN_WARNING "cciss: more luns configured"
+ dev_warn(&h->pdev->dev, "more luns configured"
" on controller than can be handled by"
" this driver.\n");
}
}
if (!drv_found) {
/* Deregister it from the OS, it's gone. */
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
h->drv[i]->busy_configuring = 1;
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
return_code = deregister_disk(h, i, 1, via_ioctl);
if (h->drv[i] != NULL)
h->drv[i]->busy_configuring = 0;
if (drv_index == -1)
goto freeret;
}
- cciss_update_drive_info(ctlr, drv_index, first_time,
- via_ioctl);
+ cciss_update_drive_info(h, drv_index, first_time, via_ioctl);
} /* end for */
freeret:
*/
return -1;
mem_msg:
- printk(KERN_ERR "cciss: out of memory\n");
+ dev_err(&h->pdev->dev, "out of memory\n");
h->busy_configuring = 0;
goto freeret;
}
return 0;
}
-static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff,
+static int fill_cmd(ctlr_info_t *h, CommandList_struct *c, __u8 cmd, void *buff,
size_t size, __u8 page_code, unsigned char *scsi3addr,
int cmd_type)
{
- ctlr_info_t *h = hba[ctlr];
u64bit buff_dma_handle;
int status = IO_OK;
c->Request.Timeout = 0;
break;
default:
- printk(KERN_WARNING
- "cciss%d: Unknown Command 0x%c\n", ctlr, cmd);
+ dev_warn(&h->pdev->dev, "Unknown Command 0x%c\n", cmd);
return IO_ERROR;
}
} else if (cmd_type == TYPE_MSG) {
c->Request.CDB[0] = cmd;
break;
default:
- printk(KERN_WARNING
- "cciss%d: unknown message type %d\n", ctlr, cmd);
+ dev_warn(&h->pdev->dev,
+ "unknown message type %d\n", cmd);
return IO_ERROR;
}
} else {
- printk(KERN_WARNING
- "cciss%d: unknown command type %d\n", ctlr, cmd_type);
+ dev_warn(&h->pdev->dev, "unknown command type %d\n", cmd_type);
return IO_ERROR;
}
/* Fill in the scatter gather information */
default:
if (check_for_unit_attention(h, c))
return IO_NEEDS_RETRY;
- printk(KERN_WARNING "cciss%d: cmd 0x%02x "
+ dev_warn(&h->pdev->dev, "cmd 0x%02x "
"check condition, sense key = 0x%02x\n",
- h->ctlr, c->Request.CDB[0],
- c->err_info->SenseInfo[2]);
+ c->Request.CDB[0], c->err_info->SenseInfo[2]);
}
break;
default:
- printk(KERN_WARNING "cciss%d: cmd 0x%02x"
- "scsi status = 0x%02x\n", h->ctlr,
+ dev_warn(&h->pdev->dev, "cmd 0x%02x"
+ "scsi status = 0x%02x\n",
c->Request.CDB[0], c->err_info->ScsiStatus);
break;
}
/* expected for inquiry and report lun commands */
break;
case CMD_INVALID:
- printk(KERN_WARNING "cciss: cmd 0x%02x is "
+ dev_warn(&h->pdev->dev, "cmd 0x%02x is "
"reported invalid\n", c->Request.CDB[0]);
return_status = IO_ERROR;
break;
case CMD_PROTOCOL_ERR:
- printk(KERN_WARNING "cciss: cmd 0x%02x has "
- "protocol error \n", c->Request.CDB[0]);
+ dev_warn(&h->pdev->dev, "cmd 0x%02x has "
+ "protocol error\n", c->Request.CDB[0]);
return_status = IO_ERROR;
break;
case CMD_HARDWARE_ERR:
- printk(KERN_WARNING "cciss: cmd 0x%02x had "
+ dev_warn(&h->pdev->dev, "cmd 0x%02x had "
" hardware error\n", c->Request.CDB[0]);
return_status = IO_ERROR;
break;
case CMD_CONNECTION_LOST:
- printk(KERN_WARNING "cciss: cmd 0x%02x had "
+ dev_warn(&h->pdev->dev, "cmd 0x%02x had "
"connection lost\n", c->Request.CDB[0]);
return_status = IO_ERROR;
break;
case CMD_ABORTED:
- printk(KERN_WARNING "cciss: cmd 0x%02x was "
+ dev_warn(&h->pdev->dev, "cmd 0x%02x was "
"aborted\n", c->Request.CDB[0]);
return_status = IO_ERROR;
break;
case CMD_ABORT_FAILED:
- printk(KERN_WARNING "cciss: cmd 0x%02x reports "
+ dev_warn(&h->pdev->dev, "cmd 0x%02x reports "
"abort failed\n", c->Request.CDB[0]);
return_status = IO_ERROR;
break;
case CMD_UNSOLICITED_ABORT:
- printk(KERN_WARNING
- "cciss%d: unsolicited abort 0x%02x\n", h->ctlr,
+ dev_warn(&h->pdev->dev, "unsolicited abort 0x%02x\n",
c->Request.CDB[0]);
return_status = IO_NEEDS_RETRY;
break;
default:
- printk(KERN_WARNING "cciss: cmd 0x%02x returned "
+ dev_warn(&h->pdev->dev, "cmd 0x%02x returned "
"unknown status %x\n", c->Request.CDB[0],
c->err_info->CommandStatus);
return_status = IO_ERROR;
{
DECLARE_COMPLETION_ONSTACK(wait);
u64bit buff_dma_handle;
- unsigned long flags;
int return_status = IO_OK;
resend_cmd2:
c->waiting = &wait;
- /* Put the request on the tail of the queue and send it */
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
- addQ(&h->reqQ, c);
- h->Qdepth++;
- start_io(h);
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+ enqueue_cmd_and_start_io(h, c);
wait_for_completion(&wait);
if (return_status == IO_NEEDS_RETRY &&
c->retry_count < MAX_CMD_RETRIES) {
- printk(KERN_WARNING "cciss%d: retrying 0x%02x\n", h->ctlr,
+ dev_warn(&h->pdev->dev, "retrying 0x%02x\n",
c->Request.CDB[0]);
c->retry_count++;
/* erase the old error information */
return return_status;
}
-static int sendcmd_withirq(__u8 cmd, int ctlr, void *buff, size_t size,
+static int sendcmd_withirq(ctlr_info_t *h, __u8 cmd, void *buff, size_t size,
__u8 page_code, unsigned char scsi3addr[],
int cmd_type)
{
- ctlr_info_t *h = hba[ctlr];
CommandList_struct *c;
int return_status;
- c = cmd_alloc(h, 0);
+ c = cmd_special_alloc(h);
if (!c)
return -ENOMEM;
- return_status = fill_cmd(c, cmd, ctlr, buff, size, page_code,
+ return_status = fill_cmd(h, c, cmd, buff, size, page_code,
scsi3addr, cmd_type);
if (return_status == IO_OK)
return_status = sendcmd_withirq_core(h, c, 1);
- cmd_free(h, c, 0);
+ cmd_special_free(h, c);
return return_status;
}
-static void cciss_geometry_inquiry(int ctlr, int logvol,
+static void cciss_geometry_inquiry(ctlr_info_t *h, int logvol,
sector_t total_size,
unsigned int block_size,
InquiryData_struct *inq_buff,
unsigned char scsi3addr[8];
memset(inq_buff, 0, sizeof(InquiryData_struct));
- log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
- return_code = sendcmd_withirq(CISS_INQUIRY, ctlr, inq_buff,
+ log_unit_to_scsi3addr(h, scsi3addr, logvol);
+ return_code = sendcmd_withirq(h, CISS_INQUIRY, inq_buff,
sizeof(*inq_buff), 0xC1, scsi3addr, TYPE_CMD);
if (return_code == IO_OK) {
if (inq_buff->data_byte[8] == 0xFF) {
- printk(KERN_WARNING
- "cciss: reading geometry failed, volume "
+ dev_warn(&h->pdev->dev,
+ "reading geometry failed, volume "
"does not support reading geometry\n");
drv->heads = 255;
drv->sectors = 32; /* Sectors per track */
drv->cylinders = real_size;
}
} else { /* Get geometry failed */
- printk(KERN_WARNING "cciss: reading geometry failed\n");
+ dev_warn(&h->pdev->dev, "reading geometry failed\n");
}
}
static void
-cciss_read_capacity(int ctlr, int logvol, sector_t *total_size,
+cciss_read_capacity(ctlr_info_t *h, int logvol, sector_t *total_size,
unsigned int *block_size)
{
ReadCapdata_struct *buf;
buf = kzalloc(sizeof(ReadCapdata_struct), GFP_KERNEL);
if (!buf) {
- printk(KERN_WARNING "cciss: out of memory\n");
+ dev_warn(&h->pdev->dev, "out of memory\n");
return;
}
- log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
- return_code = sendcmd_withirq(CCISS_READ_CAPACITY, ctlr, buf,
+ log_unit_to_scsi3addr(h, scsi3addr, logvol);
+ return_code = sendcmd_withirq(h, CCISS_READ_CAPACITY, buf,
sizeof(ReadCapdata_struct), 0, scsi3addr, TYPE_CMD);
if (return_code == IO_OK) {
*total_size = be32_to_cpu(*(__be32 *) buf->total_size);
*block_size = be32_to_cpu(*(__be32 *) buf->block_size);
} else { /* read capacity command failed */
- printk(KERN_WARNING "cciss: read capacity failed\n");
+ dev_warn(&h->pdev->dev, "read capacity failed\n");
*total_size = 0;
*block_size = BLOCK_SIZE;
}
kfree(buf);
}
-static void cciss_read_capacity_16(int ctlr, int logvol,
+static void cciss_read_capacity_16(ctlr_info_t *h, int logvol,
sector_t *total_size, unsigned int *block_size)
{
ReadCapdata_struct_16 *buf;
buf = kzalloc(sizeof(ReadCapdata_struct_16), GFP_KERNEL);
if (!buf) {
- printk(KERN_WARNING "cciss: out of memory\n");
+ dev_warn(&h->pdev->dev, "out of memory\n");
return;
}
- log_unit_to_scsi3addr(hba[ctlr], scsi3addr, logvol);
- return_code = sendcmd_withirq(CCISS_READ_CAPACITY_16,
- ctlr, buf, sizeof(ReadCapdata_struct_16),
+ log_unit_to_scsi3addr(h, scsi3addr, logvol);
+ return_code = sendcmd_withirq(h, CCISS_READ_CAPACITY_16,
+ buf, sizeof(ReadCapdata_struct_16),
0, scsi3addr, TYPE_CMD);
if (return_code == IO_OK) {
*total_size = be64_to_cpu(*(__be64 *) buf->total_size);
*block_size = be32_to_cpu(*(__be32 *) buf->block_size);
} else { /* read capacity command failed */
- printk(KERN_WARNING "cciss: read capacity failed\n");
+ dev_warn(&h->pdev->dev, "read capacity failed\n");
*total_size = 0;
*block_size = BLOCK_SIZE;
}
- printk(KERN_INFO " blocks= %llu block_size= %d\n",
+ dev_info(&h->pdev->dev, " blocks= %llu block_size= %d\n",
(unsigned long long)*total_size+1, *block_size);
kfree(buf);
}
inq_buff = kmalloc(sizeof(InquiryData_struct), GFP_KERNEL);
if (inq_buff == NULL) {
- printk(KERN_WARNING "cciss: out of memory\n");
+ dev_warn(&h->pdev->dev, "out of memory\n");
return 1;
}
if (h->cciss_read == CCISS_READ_10) {
- cciss_read_capacity(h->ctlr, logvol,
+ cciss_read_capacity(h, logvol,
&total_size, &block_size);
} else {
- cciss_read_capacity_16(h->ctlr, logvol,
+ cciss_read_capacity_16(h, logvol,
&total_size, &block_size);
}
- cciss_geometry_inquiry(h->ctlr, logvol, total_size, block_size,
+ cciss_geometry_inquiry(h, logvol, total_size, block_size,
inq_buff, drv);
blk_queue_logical_block_size(drv->queue, drv->block_size);
c = hlist_entry(h->reqQ.first, CommandList_struct, list);
/* can't do anything if fifo is full */
if ((h->access.fifo_full(h))) {
- printk(KERN_WARNING "cciss: fifo full\n");
+ dev_warn(&h->pdev->dev, "fifo full\n");
break;
}
}
}
-/* Assumes that CCISS_LOCK(h->ctlr) is held. */
+/* Assumes that h->lock is held. */
/* Zeros out the error record and then resends the command back */
/* to the controller */
static inline void resend_cciss_cmd(ctlr_info_t *h, CommandList_struct *c)
driver_byte = DRIVER_OK;
msg_byte = cmd->err_info->CommandStatus; /* correct? seems too device specific */
- if (blk_pc_request(cmd->rq))
+ if (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC)
host_byte = DID_PASSTHROUGH;
else
host_byte = DID_OK;
host_byte, driver_byte);
if (cmd->err_info->ScsiStatus != SAM_STAT_CHECK_CONDITION) {
- if (!blk_pc_request(cmd->rq))
- printk(KERN_WARNING "cciss: cmd %p "
+ if (cmd->rq->cmd_type != REQ_TYPE_BLOCK_PC)
+ dev_warn(&h->pdev->dev, "cmd %p "
"has SCSI Status 0x%x\n",
cmd, cmd->err_info->ScsiStatus);
return error_value;
/* check the sense key */
sense_key = 0xf & cmd->err_info->SenseInfo[2];
/* no status or recovered error */
- if (((sense_key == 0x0) || (sense_key == 0x1)) && !blk_pc_request(cmd->rq))
+ if (((sense_key == 0x0) || (sense_key == 0x1)) &&
+ (cmd->rq->cmd_type != REQ_TYPE_BLOCK_PC))
error_value = 0;
if (check_for_unit_attention(h, cmd)) {
- *retry_cmd = !blk_pc_request(cmd->rq);
+ *retry_cmd = !(cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC);
return 0;
}
- if (!blk_pc_request(cmd->rq)) { /* Not SG_IO or similar? */
+ /* Not SG_IO or similar? */
+ if (cmd->rq->cmd_type != REQ_TYPE_BLOCK_PC) {
if (error_value != 0)
- printk(KERN_WARNING "cciss: cmd %p has CHECK CONDITION"
+ dev_warn(&h->pdev->dev, "cmd %p has CHECK CONDITION"
" sense key = 0x%x\n", cmd, sense_key);
return error_value;
}
rq->errors = evaluate_target_status(h, cmd, &retry_cmd);
break;
case CMD_DATA_UNDERRUN:
- if (blk_fs_request(cmd->rq)) {
- printk(KERN_WARNING "cciss: cmd %p has"
+ if (cmd->rq->cmd_type == REQ_TYPE_FS) {
+ dev_warn(&h->pdev->dev, "cmd %p has"
" completed with data underrun "
"reported\n", cmd);
cmd->rq->resid_len = cmd->err_info->ResidualCnt;
}
break;
case CMD_DATA_OVERRUN:
- if (blk_fs_request(cmd->rq))
- printk(KERN_WARNING "cciss: cmd %p has"
+ if (cmd->rq->cmd_type == REQ_TYPE_FS)
+ dev_warn(&h->pdev->dev, "cciss: cmd %p has"
" completed with data overrun "
"reported\n", cmd);
break;
case CMD_INVALID:
- printk(KERN_WARNING "cciss: cmd %p is "
+ dev_warn(&h->pdev->dev, "cciss: cmd %p is "
"reported invalid\n", cmd);
rq->errors = make_status_bytes(SAM_STAT_GOOD,
cmd->err_info->CommandStatus, DRIVER_OK,
- blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ERROR);
+ (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ DID_PASSTHROUGH : DID_ERROR);
break;
case CMD_PROTOCOL_ERR:
- printk(KERN_WARNING "cciss: cmd %p has "
- "protocol error \n", cmd);
+ dev_warn(&h->pdev->dev, "cciss: cmd %p has "
+ "protocol error\n", cmd);
rq->errors = make_status_bytes(SAM_STAT_GOOD,
cmd->err_info->CommandStatus, DRIVER_OK,
- blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ERROR);
+ (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ DID_PASSTHROUGH : DID_ERROR);
break;
case CMD_HARDWARE_ERR:
- printk(KERN_WARNING "cciss: cmd %p had "
+ dev_warn(&h->pdev->dev, "cciss: cmd %p had "
" hardware error\n", cmd);
rq->errors = make_status_bytes(SAM_STAT_GOOD,
cmd->err_info->CommandStatus, DRIVER_OK,
- blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ERROR);
+ (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ DID_PASSTHROUGH : DID_ERROR);
break;
case CMD_CONNECTION_LOST:
- printk(KERN_WARNING "cciss: cmd %p had "
+ dev_warn(&h->pdev->dev, "cciss: cmd %p had "
"connection lost\n", cmd);
rq->errors = make_status_bytes(SAM_STAT_GOOD,
cmd->err_info->CommandStatus, DRIVER_OK,
- blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ERROR);
+ (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ DID_PASSTHROUGH : DID_ERROR);
break;
case CMD_ABORTED:
- printk(KERN_WARNING "cciss: cmd %p was "
+ dev_warn(&h->pdev->dev, "cciss: cmd %p was "
"aborted\n", cmd);
rq->errors = make_status_bytes(SAM_STAT_GOOD,
cmd->err_info->CommandStatus, DRIVER_OK,
- blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ABORT);
+ (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ DID_PASSTHROUGH : DID_ABORT);
break;
case CMD_ABORT_FAILED:
- printk(KERN_WARNING "cciss: cmd %p reports "
+ dev_warn(&h->pdev->dev, "cciss: cmd %p reports "
"abort failed\n", cmd);
rq->errors = make_status_bytes(SAM_STAT_GOOD,
cmd->err_info->CommandStatus, DRIVER_OK,
- blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ERROR);
+ (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ DID_PASSTHROUGH : DID_ERROR);
break;
case CMD_UNSOLICITED_ABORT:
- printk(KERN_WARNING "cciss%d: unsolicited "
+ dev_warn(&h->pdev->dev, "cciss%d: unsolicited "
"abort %p\n", h->ctlr, cmd);
if (cmd->retry_count < MAX_CMD_RETRIES) {
retry_cmd = 1;
- printk(KERN_WARNING
- "cciss%d: retrying %p\n", h->ctlr, cmd);
+ dev_warn(&h->pdev->dev, "retrying %p\n", cmd);
cmd->retry_count++;
} else
- printk(KERN_WARNING
- "cciss%d: %p retried too "
- "many times\n", h->ctlr, cmd);
+ dev_warn(&h->pdev->dev,
+ "%p retried too many times\n", cmd);
rq->errors = make_status_bytes(SAM_STAT_GOOD,
cmd->err_info->CommandStatus, DRIVER_OK,
- blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ABORT);
+ (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ DID_PASSTHROUGH : DID_ABORT);
break;
case CMD_TIMEOUT:
- printk(KERN_WARNING "cciss: cmd %p timedout\n", cmd);
+ dev_warn(&h->pdev->dev, "cmd %p timedout\n", cmd);
rq->errors = make_status_bytes(SAM_STAT_GOOD,
cmd->err_info->CommandStatus, DRIVER_OK,
- blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ERROR);
+ (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ DID_PASSTHROUGH : DID_ERROR);
break;
default:
- printk(KERN_WARNING "cciss: cmd %p returned "
+ dev_warn(&h->pdev->dev, "cmd %p returned "
"unknown status %x\n", cmd,
cmd->err_info->CommandStatus);
rq->errors = make_status_bytes(SAM_STAT_GOOD,
cmd->err_info->CommandStatus, DRIVER_OK,
- blk_pc_request(cmd->rq) ? DID_PASSTHROUGH : DID_ERROR);
+ (cmd->rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ DID_PASSTHROUGH : DID_ERROR);
}
after_error_processing:
blk_complete_request(cmd->rq);
}
+static inline u32 cciss_tag_contains_index(u32 tag)
+{
+#define DIRECT_LOOKUP_BIT 0x10
+ return tag & DIRECT_LOOKUP_BIT;
+}
+
+static inline u32 cciss_tag_to_index(u32 tag)
+{
+#define DIRECT_LOOKUP_SHIFT 5
+ return tag >> DIRECT_LOOKUP_SHIFT;
+}
+
+static inline u32 cciss_tag_discard_error_bits(u32 tag)
+{
+#define CCISS_ERROR_BITS 0x03
+ return tag & ~CCISS_ERROR_BITS;
+}
+
+static inline void cciss_mark_tag_indexed(u32 *tag)
+{
+ *tag |= DIRECT_LOOKUP_BIT;
+}
+
+static inline void cciss_set_tag_index(u32 *tag, u32 index)
+{
+ *tag |= (index << DIRECT_LOOKUP_SHIFT);
+}
+
/*
* Get a request and submit it to the controller.
*/
BUG_ON(creq->nr_phys_segments > h->maxsgentries);
- if ((c = cmd_alloc(h, 1)) == NULL)
+ c = cmd_alloc(h);
+ if (!c)
goto full;
blk_start_request(creq);
/* got command from pool, so use the command block index instead */
/* for direct lookups. */
/* The first 2 bits are reserved for controller error reporting. */
- c->Header.Tag.lower = (c->cmdindex << 3);
- c->Header.Tag.lower |= 0x04; /* flag for direct lookup. */
+ cciss_set_tag_index(&c->Header.Tag.lower, c->cmdindex);
+ cciss_mark_tag_indexed(&c->Header.Tag.lower);
memcpy(&c->Header.LUN, drv->LunID, sizeof(drv->LunID));
c->Request.CDBLen = 10; /* 12 byte commands not in FW yet; */
c->Request.Type.Type = TYPE_CMD; /* It is a command. */
c->Request.CDB[0] =
(rq_data_dir(creq) == READ) ? h->cciss_read : h->cciss_write;
start_blk = blk_rq_pos(creq);
-#ifdef CCISS_DEBUG
- printk(KERN_DEBUG "ciss: sector =%d nr_sectors=%d\n",
+ dev_dbg(&h->pdev->dev, "sector =%d nr_sectors=%d\n",
(int)blk_rq_pos(creq), (int)blk_rq_sectors(creq));
-#endif /* CCISS_DEBUG */
-
sg_init_table(tmp_sg, h->maxsgentries);
seg = blk_rq_map_sg(q, creq, tmp_sg);
if (seg > h->maxSG)
h->maxSG = seg;
-#ifdef CCISS_DEBUG
- printk(KERN_DEBUG "cciss: Submitting %ld sectors in %d segments "
+ dev_dbg(&h->pdev->dev, "Submitting %u sectors in %d segments "
"chained[%d]\n",
blk_rq_sectors(creq), seg, chained);
-#endif /* CCISS_DEBUG */
- c->Header.SGList = c->Header.SGTotal = seg + chained;
- if (seg > h->max_cmd_sgentries)
+ c->Header.SGTotal = seg + chained;
+ if (seg <= h->max_cmd_sgentries)
+ c->Header.SGList = c->Header.SGTotal;
+ else
c->Header.SGList = h->max_cmd_sgentries;
+ set_performant_mode(h, c);
- if (likely(blk_fs_request(creq))) {
+ if (likely(creq->cmd_type == REQ_TYPE_FS)) {
if(h->cciss_read == CCISS_READ_10) {
c->Request.CDB[1] = 0;
c->Request.CDB[2] = (start_blk >> 24) & 0xff; /* MSB */
c->Request.CDB[13]= blk_rq_sectors(creq) & 0xff;
c->Request.CDB[14] = c->Request.CDB[15] = 0;
}
- } else if (blk_pc_request(creq)) {
+ } else if (creq->cmd_type == REQ_TYPE_BLOCK_PC) {
c->Request.CDBLen = creq->cmd_len;
memcpy(c->Request.CDB, creq->cmd, BLK_MAX_CDB);
} else {
- printk(KERN_WARNING "cciss%d: bad request type %d\n", h->ctlr, creq->cmd_type);
+ dev_warn(&h->pdev->dev, "bad request type %d\n",
+ creq->cmd_type);
BUG();
}
static inline long interrupt_not_for_us(ctlr_info_t *h)
{
- return (((h->access.intr_pending(h) == 0) ||
- (h->interrupts_enabled == 0)));
+ return ((h->access.intr_pending(h) == 0) ||
+ (h->interrupts_enabled == 0));
}
-static irqreturn_t do_cciss_intr(int irq, void *dev_id)
+static inline int bad_tag(ctlr_info_t *h, u32 tag_index,
+ u32 raw_tag)
{
- ctlr_info_t *h = dev_id;
+ if (unlikely(tag_index >= h->nr_cmds)) {
+ dev_warn(&h->pdev->dev, "bad tag 0x%08x ignored.\n", raw_tag);
+ return 1;
+ }
+ return 0;
+}
+
+static inline void finish_cmd(ctlr_info_t *h, CommandList_struct *c,
+ u32 raw_tag)
+{
+ removeQ(c);
+ if (likely(c->cmd_type == CMD_RWREQ))
+ complete_command(h, c, 0);
+ else if (c->cmd_type == CMD_IOCTL_PEND)
+ complete(c->waiting);
+#ifdef CONFIG_CISS_SCSI_TAPE
+ else if (c->cmd_type == CMD_SCSI)
+ complete_scsi_command(c, 0, raw_tag);
+#endif
+}
+
+static inline u32 next_command(ctlr_info_t *h)
+{
+ u32 a;
+
+ if (unlikely(h->transMethod != CFGTBL_Trans_Performant))
+ return h->access.command_completed(h);
+
+ if ((*(h->reply_pool_head) & 1) == (h->reply_pool_wraparound)) {
+ a = *(h->reply_pool_head); /* Next cmd in ring buffer */
+ (h->reply_pool_head)++;
+ h->commands_outstanding--;
+ } else {
+ a = FIFO_EMPTY;
+ }
+ /* Check for wraparound */
+ if (h->reply_pool_head == (h->reply_pool + h->max_commands)) {
+ h->reply_pool_head = h->reply_pool;
+ h->reply_pool_wraparound ^= 1;
+ }
+ return a;
+}
+
+/* process completion of an indexed ("direct lookup") command */
+static inline u32 process_indexed_cmd(ctlr_info_t *h, u32 raw_tag)
+{
+ u32 tag_index;
CommandList_struct *c;
+
+ tag_index = cciss_tag_to_index(raw_tag);
+ if (bad_tag(h, tag_index, raw_tag))
+ return next_command(h);
+ c = h->cmd_pool + tag_index;
+ finish_cmd(h, c, raw_tag);
+ return next_command(h);
+}
+
+/* process completion of a non-indexed command */
+static inline u32 process_nonindexed_cmd(ctlr_info_t *h, u32 raw_tag)
+{
+ u32 tag;
+ CommandList_struct *c = NULL;
+ struct hlist_node *tmp;
+ __u32 busaddr_masked, tag_masked;
+
+ tag = cciss_tag_discard_error_bits(raw_tag);
+ hlist_for_each_entry(c, tmp, &h->cmpQ, list) {
+ busaddr_masked = cciss_tag_discard_error_bits(c->busaddr);
+ tag_masked = cciss_tag_discard_error_bits(tag);
+ if (busaddr_masked == tag_masked) {
+ finish_cmd(h, c, raw_tag);
+ return next_command(h);
+ }
+ }
+ bad_tag(h, h->nr_cmds + 1, raw_tag);
+ return next_command(h);
+}
+
+static irqreturn_t do_cciss_intx(int irq, void *dev_id)
+{
+ ctlr_info_t *h = dev_id;
unsigned long flags;
- __u32 a, a1, a2;
+ u32 raw_tag;
if (interrupt_not_for_us(h))
return IRQ_NONE;
- /*
- * If there are completed commands in the completion queue,
- * we had better do something about it.
- */
- spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
while (interrupt_pending(h)) {
- while ((a = get_next_completion(h)) != FIFO_EMPTY) {
- a1 = a;
- if ((a & 0x04)) {
- a2 = (a >> 3);
- if (a2 >= h->nr_cmds) {
- printk(KERN_WARNING
- "cciss: controller cciss%d failed, stopping.\n",
- h->ctlr);
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
- fail_all_cmds(h->ctlr);
- return IRQ_HANDLED;
- }
-
- c = h->cmd_pool + a2;
- a = c->busaddr;
-
- } else {
- struct hlist_node *tmp;
-
- a &= ~3;
- c = NULL;
- hlist_for_each_entry(c, tmp, &h->cmpQ, list) {
- if (c->busaddr == a)
- break;
- }
- }
- /*
- * If we've found the command, take it off the
- * completion Q and free it
- */
- if (c && c->busaddr == a) {
- removeQ(c);
- if (c->cmd_type == CMD_RWREQ) {
- complete_command(h, c, 0);
- } else if (c->cmd_type == CMD_IOCTL_PEND) {
- complete(c->waiting);
- }
-# ifdef CONFIG_CISS_SCSI_TAPE
- else if (c->cmd_type == CMD_SCSI)
- complete_scsi_command(c, 0, a1);
-# endif
- continue;
- }
+ raw_tag = get_next_completion(h);
+ while (raw_tag != FIFO_EMPTY) {
+ if (cciss_tag_contains_index(raw_tag))
+ raw_tag = process_indexed_cmd(h, raw_tag);
+ else
+ raw_tag = process_nonindexed_cmd(h, raw_tag);
}
}
+ spin_unlock_irqrestore(&h->lock, flags);
+ return IRQ_HANDLED;
+}
- spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
+/* Add a second interrupt handler for MSI/MSI-X mode. In this mode we never
+ * check the interrupt pending register because it is not set.
+ */
+static irqreturn_t do_cciss_msix_intr(int irq, void *dev_id)
+{
+ ctlr_info_t *h = dev_id;
+ unsigned long flags;
+ u32 raw_tag;
+
+ spin_lock_irqsave(&h->lock, flags);
+ raw_tag = get_next_completion(h);
+ while (raw_tag != FIFO_EMPTY) {
+ if (cciss_tag_contains_index(raw_tag))
+ raw_tag = process_indexed_cmd(h, raw_tag);
+ else
+ raw_tag = process_nonindexed_cmd(h, raw_tag);
+ }
+ spin_unlock_irqrestore(&h->lock, flags);
return IRQ_HANDLED;
}
switch (c->err_info->SenseInfo[12]) {
case STATE_CHANGED:
- printk(KERN_WARNING "cciss%d: a state change "
- "detected, command retried\n", h->ctlr);
+ dev_warn(&h->pdev->dev, "a state change "
+ "detected, command retried\n");
return 1;
break;
case LUN_FAILED:
- printk(KERN_WARNING "cciss%d: LUN failure "
- "detected, action required\n", h->ctlr);
+ dev_warn(&h->pdev->dev, "LUN failure "
+ "detected, action required\n");
return 1;
break;
case REPORT_LUNS_CHANGED:
- printk(KERN_WARNING "cciss%d: report LUN data "
- "changed\n", h->ctlr);
+ dev_warn(&h->pdev->dev, "report LUN data changed\n");
/*
* Here, we could call add_to_scan_list and wake up the scan thread,
* except that it's quite likely that we will get more than one
return 1;
break;
case POWER_OR_RESET:
- printk(KERN_WARNING "cciss%d: a power on "
- "or device reset detected\n", h->ctlr);
+ dev_warn(&h->pdev->dev,
+ "a power on or device reset detected\n");
return 1;
break;
case UNIT_ATTENTION_CLEARED:
- printk(KERN_WARNING "cciss%d: unit attention "
- "cleared by another initiator\n", h->ctlr);
+ dev_warn(&h->pdev->dev,
+ "unit attention cleared by another initiator\n");
return 1;
break;
default:
- printk(KERN_WARNING "cciss%d: unknown "
- "unit attention detected\n", h->ctlr);
- return 1;
+ dev_warn(&h->pdev->dev, "unknown unit attention detected\n");
+ return 1;
}
}
* the io functions.
* This is for debug only.
*/
-#ifdef CCISS_DEBUG
-static void print_cfg_table(CfgTable_struct *tb)
+static void print_cfg_table(ctlr_info_t *h)
{
int i;
char temp_name[17];
+ CfgTable_struct *tb = h->cfgtable;
- printk("Controller Configuration information\n");
- printk("------------------------------------\n");
+ dev_dbg(&h->pdev->dev, "Controller Configuration information\n");
+ dev_dbg(&h->pdev->dev, "------------------------------------\n");
for (i = 0; i < 4; i++)
temp_name[i] = readb(&(tb->Signature[i]));
temp_name[4] = '\0';
- printk(" Signature = %s\n", temp_name);
- printk(" Spec Number = %d\n", readl(&(tb->SpecValence)));
- printk(" Transport methods supported = 0x%x\n",
+ dev_dbg(&h->pdev->dev, " Signature = %s\n", temp_name);
+ dev_dbg(&h->pdev->dev, " Spec Number = %d\n",
+ readl(&(tb->SpecValence)));
+ dev_dbg(&h->pdev->dev, " Transport methods supported = 0x%x\n",
readl(&(tb->TransportSupport)));
- printk(" Transport methods active = 0x%x\n",
+ dev_dbg(&h->pdev->dev, " Transport methods active = 0x%x\n",
readl(&(tb->TransportActive)));
- printk(" Requested transport Method = 0x%x\n",
+ dev_dbg(&h->pdev->dev, " Requested transport Method = 0x%x\n",
readl(&(tb->HostWrite.TransportRequest)));
- printk(" Coalesce Interrupt Delay = 0x%x\n",
+ dev_dbg(&h->pdev->dev, " Coalesce Interrupt Delay = 0x%x\n",
readl(&(tb->HostWrite.CoalIntDelay)));
- printk(" Coalesce Interrupt Count = 0x%x\n",
+ dev_dbg(&h->pdev->dev, " Coalesce Interrupt Count = 0x%x\n",
readl(&(tb->HostWrite.CoalIntCount)));
- printk(" Max outstanding commands = 0x%d\n",
+ dev_dbg(&h->pdev->dev, " Max outstanding commands = 0x%d\n",
readl(&(tb->CmdsOutMax)));
- printk(" Bus Types = 0x%x\n", readl(&(tb->BusTypes)));
+ dev_dbg(&h->pdev->dev, " Bus Types = 0x%x\n",
+ readl(&(tb->BusTypes)));
for (i = 0; i < 16; i++)
temp_name[i] = readb(&(tb->ServerName[i]));
temp_name[16] = '\0';
- printk(" Server Name = %s\n", temp_name);
- printk(" Heartbeat Counter = 0x%x\n\n\n", readl(&(tb->HeartBeat)));
+ dev_dbg(&h->pdev->dev, " Server Name = %s\n", temp_name);
+ dev_dbg(&h->pdev->dev, " Heartbeat Counter = 0x%x\n\n\n",
+ readl(&(tb->HeartBeat)));
}
-#endif /* CCISS_DEBUG */
static int find_PCI_BAR_index(struct pci_dev *pdev, unsigned long pci_bar_addr)
{
offset += 8;
break;
default: /* reserved in PCI 2.2 */
- printk(KERN_WARNING
+ dev_warn(&pdev->dev,
"Base address is invalid\n");
return -1;
break;
return -1;
}
+/* Fill in bucket_map[], given nsgs (the max number of
+ * scatter gather elements supported) and bucket[],
+ * which is an array of 8 integers. The bucket[] array
+ * contains 8 different DMA transfer sizes (in 16
+ * byte increments) which the controller uses to fetch
+ * commands. This function fills in bucket_map[], which
+ * maps a given number of scatter gather elements to one of
+ * the 8 DMA transfer sizes. The point of it is to allow the
+ * controller to only do as much DMA as needed to fetch the
+ * command, with the DMA transfer size encoded in the lower
+ * bits of the command address.
+ */
+static void calc_bucket_map(int bucket[], int num_buckets,
+ int nsgs, int *bucket_map)
+{
+ int i, j, b, size;
+
+ /* even a command with 0 SGs requires 4 blocks */
+#define MINIMUM_TRANSFER_BLOCKS 4
+#define NUM_BUCKETS 8
+ /* Note, bucket_map must have nsgs+1 entries. */
+ for (i = 0; i <= nsgs; i++) {
+ /* Compute size of a command with i SG entries */
+ size = i + MINIMUM_TRANSFER_BLOCKS;
+ b = num_buckets; /* Assume the biggest bucket */
+ /* Find the bucket that is just big enough */
+ for (j = 0; j < 8; j++) {
+ if (bucket[j] >= size) {
+ b = j;
+ break;
+ }
+ }
+ /* for a command with i SG entries, use bucket b. */
+ bucket_map[i] = b;
+ }
+}
+
+static void __devinit cciss_wait_for_mode_change_ack(ctlr_info_t *h)
+{
+ int i;
+
+ /* under certain very rare conditions, this can take awhile.
+ * (e.g.: hot replace a failed 144GB drive in a RAID 5 set right
+ * as we enter this code.) */
+ for (i = 0; i < MAX_CONFIG_WAIT; i++) {
+ if (!(readl(h->vaddr + SA5_DOORBELL) & CFGTBL_ChangeReq))
+ break;
+ msleep(10);
+ }
+}
+
+static __devinit void cciss_enter_performant_mode(ctlr_info_t *h)
+{
+ /* This is a bit complicated. There are 8 registers on
+ * the controller which we write to to tell it 8 different
+ * sizes of commands which there may be. It's a way of
+ * reducing the DMA done to fetch each command. Encoded into
+ * each command's tag are 3 bits which communicate to the controller
+ * which of the eight sizes that command fits within. The size of
+ * each command depends on how many scatter gather entries there are.
+ * Each SG entry requires 16 bytes. The eight registers are programmed
+ * with the number of 16-byte blocks a command of that size requires.
+ * The smallest command possible requires 5 such 16 byte blocks.
+ * the largest command possible requires MAXSGENTRIES + 4 16-byte
+ * blocks. Note, this only extends to the SG entries contained
+ * within the command block, and does not extend to chained blocks
+ * of SG elements. bft[] contains the eight values we write to
+ * the registers. They are not evenly distributed, but have more
+ * sizes for small commands, and fewer sizes for larger commands.
+ */
+ __u32 trans_offset;
+ int bft[8] = { 5, 6, 8, 10, 12, 20, 28, MAXSGENTRIES + 4};
+ /*
+ * 5 = 1 s/g entry or 4k
+ * 6 = 2 s/g entry or 8k
+ * 8 = 4 s/g entry or 16k
+ * 10 = 6 s/g entry or 24k
+ */
+ unsigned long register_value;
+ BUILD_BUG_ON(28 > MAXSGENTRIES + 4);
+
+ h->reply_pool_wraparound = 1; /* spec: init to 1 */
+
+ /* Controller spec: zero out this buffer. */
+ memset(h->reply_pool, 0, h->max_commands * sizeof(__u64));
+ h->reply_pool_head = h->reply_pool;
+
+ trans_offset = readl(&(h->cfgtable->TransMethodOffset));
+ calc_bucket_map(bft, ARRAY_SIZE(bft), h->maxsgentries,
+ h->blockFetchTable);
+ writel(bft[0], &h->transtable->BlockFetch0);
+ writel(bft[1], &h->transtable->BlockFetch1);
+ writel(bft[2], &h->transtable->BlockFetch2);
+ writel(bft[3], &h->transtable->BlockFetch3);
+ writel(bft[4], &h->transtable->BlockFetch4);
+ writel(bft[5], &h->transtable->BlockFetch5);
+ writel(bft[6], &h->transtable->BlockFetch6);
+ writel(bft[7], &h->transtable->BlockFetch7);
+
+ /* size of controller ring buffer */
+ writel(h->max_commands, &h->transtable->RepQSize);
+ writel(1, &h->transtable->RepQCount);
+ writel(0, &h->transtable->RepQCtrAddrLow32);
+ writel(0, &h->transtable->RepQCtrAddrHigh32);
+ writel(h->reply_pool_dhandle, &h->transtable->RepQAddr0Low32);
+ writel(0, &h->transtable->RepQAddr0High32);
+ writel(CFGTBL_Trans_Performant,
+ &(h->cfgtable->HostWrite.TransportRequest));
+
+ writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL);
+ cciss_wait_for_mode_change_ack(h);
+ register_value = readl(&(h->cfgtable->TransportActive));
+ if (!(register_value & CFGTBL_Trans_Performant))
+ dev_warn(&h->pdev->dev, "cciss: unable to get board into"
+ " performant mode\n");
+}
+
+static void __devinit cciss_put_controller_into_performant_mode(ctlr_info_t *h)
+{
+ __u32 trans_support;
+
+ dev_dbg(&h->pdev->dev, "Trying to put board into Performant mode\n");
+ /* Attempt to put controller into performant mode if supported */
+ /* Does board support performant mode? */
+ trans_support = readl(&(h->cfgtable->TransportSupport));
+ if (!(trans_support & PERFORMANT_MODE))
+ return;
+
+ dev_dbg(&h->pdev->dev, "Placing controller into performant mode\n");
+ /* Performant mode demands commands on a 32 byte boundary
+ * pci_alloc_consistent aligns on page boundarys already.
+ * Just need to check if divisible by 32
+ */
+ if ((sizeof(CommandList_struct) % 32) != 0) {
+ dev_warn(&h->pdev->dev, "%s %d %s\n",
+ "cciss info: command size[",
+ (int)sizeof(CommandList_struct),
+ "] not divisible by 32, no performant mode..\n");
+ return;
+ }
+
+ /* Performant mode ring buffer and supporting data structures */
+ h->reply_pool = (__u64 *)pci_alloc_consistent(
+ h->pdev, h->max_commands * sizeof(__u64),
+ &(h->reply_pool_dhandle));
+
+ /* Need a block fetch table for performant mode */
+ h->blockFetchTable = kmalloc(((h->maxsgentries+1) *
+ sizeof(__u32)), GFP_KERNEL);
+
+ if ((h->reply_pool == NULL) || (h->blockFetchTable == NULL))
+ goto clean_up;
+
+ cciss_enter_performant_mode(h);
+
+ /* Change the access methods to the performant access methods */
+ h->access = SA5_performant_access;
+ h->transMethod = CFGTBL_Trans_Performant;
+
+ return;
+clean_up:
+ kfree(h->blockFetchTable);
+ if (h->reply_pool)
+ pci_free_consistent(h->pdev,
+ h->max_commands * sizeof(__u64),
+ h->reply_pool,
+ h->reply_pool_dhandle);
+ return;
+
+} /* cciss_put_controller_into_performant_mode */
+
/* If MSI/MSI-X is supported by the kernel we will try to enable it on
* controllers that are capable. If not, we use IO-APIC mode.
*/
-static void __devinit cciss_interrupt_mode(ctlr_info_t *c,
- struct pci_dev *pdev, __u32 board_id)
+static void __devinit cciss_interrupt_mode(ctlr_info_t *h)
{
#ifdef CONFIG_PCI_MSI
int err;
};
/* Some boards advertise MSI but don't really support it */
- if ((board_id == 0x40700E11) ||
- (board_id == 0x40800E11) ||
- (board_id == 0x40820E11) || (board_id == 0x40830E11))
+ if ((h->board_id == 0x40700E11) || (h->board_id == 0x40800E11) ||
+ (h->board_id == 0x40820E11) || (h->board_id == 0x40830E11))
goto default_int_mode;
- if (pci_find_capability(pdev, PCI_CAP_ID_MSIX)) {
- err = pci_enable_msix(pdev, cciss_msix_entries, 4);
+ if (pci_find_capability(h->pdev, PCI_CAP_ID_MSIX)) {
+ err = pci_enable_msix(h->pdev, cciss_msix_entries, 4);
if (!err) {
- c->intr[0] = cciss_msix_entries[0].vector;
- c->intr[1] = cciss_msix_entries[1].vector;
- c->intr[2] = cciss_msix_entries[2].vector;
- c->intr[3] = cciss_msix_entries[3].vector;
- c->msix_vector = 1;
+ h->intr[0] = cciss_msix_entries[0].vector;
+ h->intr[1] = cciss_msix_entries[1].vector;
+ h->intr[2] = cciss_msix_entries[2].vector;
+ h->intr[3] = cciss_msix_entries[3].vector;
+ h->msix_vector = 1;
return;
}
if (err > 0) {
- printk(KERN_WARNING "cciss: only %d MSI-X vectors "
- "available\n", err);
+ dev_warn(&h->pdev->dev,
+ "only %d MSI-X vectors available\n", err);
goto default_int_mode;
} else {
- printk(KERN_WARNING "cciss: MSI-X init failed %d\n",
- err);
+ dev_warn(&h->pdev->dev,
+ "MSI-X init failed %d\n", err);
goto default_int_mode;
}
}
- if (pci_find_capability(pdev, PCI_CAP_ID_MSI)) {
- if (!pci_enable_msi(pdev)) {
- c->msi_vector = 1;
- } else {
- printk(KERN_WARNING "cciss: MSI init failed\n");
- }
+ if (pci_find_capability(h->pdev, PCI_CAP_ID_MSI)) {
+ if (!pci_enable_msi(h->pdev))
+ h->msi_vector = 1;
+ else
+ dev_warn(&h->pdev->dev, "MSI init failed\n");
}
default_int_mode:
#endif /* CONFIG_PCI_MSI */
/* if we get here we're going to use the default interrupt mode */
- c->intr[SIMPLE_MODE_INT] = pdev->irq;
+ h->intr[PERF_MODE_INT] = h->pdev->irq;
return;
}
-static int __devinit cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
+static int __devinit cciss_lookup_board_id(struct pci_dev *pdev, u32 *board_id)
{
- ushort subsystem_vendor_id, subsystem_device_id, command;
- __u32 board_id, scratchpad = 0;
- __u64 cfg_offset;
- __u32 cfg_base_addr;
- __u64 cfg_base_addr_index;
- int i, prod_index, err;
+ int i;
+ u32 subsystem_vendor_id, subsystem_device_id;
subsystem_vendor_id = pdev->subsystem_vendor;
subsystem_device_id = pdev->subsystem_device;
- board_id = (((__u32) (subsystem_device_id << 16) & 0xffff0000) |
- subsystem_vendor_id);
+ *board_id = ((subsystem_device_id << 16) & 0xffff0000) |
+ subsystem_vendor_id;
for (i = 0; i < ARRAY_SIZE(products); i++) {
/* Stand aside for hpsa driver on request */
if (cciss_allow_hpsa && products[i].board_id == HPSA_BOUNDARY)
return -ENODEV;
- if (board_id == products[i].board_id)
- break;
- }
- prod_index = i;
- if (prod_index == ARRAY_SIZE(products)) {
- dev_warn(&pdev->dev,
- "unrecognized board ID: 0x%08lx, ignoring.\n",
- (unsigned long) board_id);
- return -ENODEV;
+ if (*board_id == products[i].board_id)
+ return i;
}
+ dev_warn(&pdev->dev, "unrecognized board ID: 0x%08x, ignoring.\n",
+ *board_id);
+ return -ENODEV;
+}
- /* check to see if controller has been disabled */
- /* BEFORE trying to enable it */
- (void)pci_read_config_word(pdev, PCI_COMMAND, &command);
- if (!(command & 0x02)) {
- printk(KERN_WARNING
- "cciss: controller appears to be disabled\n");
- return -ENODEV;
- }
+static inline bool cciss_board_disabled(ctlr_info_t *h)
+{
+ u16 command;
- err = pci_enable_device(pdev);
- if (err) {
- printk(KERN_ERR "cciss: Unable to Enable PCI device\n");
- return err;
- }
+ (void) pci_read_config_word(h->pdev, PCI_COMMAND, &command);
+ return ((command & PCI_COMMAND_MEMORY) == 0);
+}
- err = pci_request_regions(pdev, "cciss");
- if (err) {
- printk(KERN_ERR "cciss: Cannot obtain PCI resources, "
- "aborting\n");
- return err;
- }
+static int __devinit cciss_pci_find_memory_BAR(struct pci_dev *pdev,
+ unsigned long *memory_bar)
+{
+ int i;
-#ifdef CCISS_DEBUG
- printk("command = %x\n", command);
- printk("irq = %x\n", pdev->irq);
- printk("board_id = %x\n", board_id);
-#endif /* CCISS_DEBUG */
+ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++)
+ if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) {
+ /* addressing mode bits already removed */
+ *memory_bar = pci_resource_start(pdev, i);
+ dev_dbg(&pdev->dev, "memory BAR = %lx\n",
+ *memory_bar);
+ return 0;
+ }
+ dev_warn(&pdev->dev, "no memory BAR found\n");
+ return -ENODEV;
+}
-/* If the kernel supports MSI/MSI-X we will try to enable that functionality,
- * else we use the IO-APIC interrupt assigned to us by system ROM.
- */
- cciss_interrupt_mode(c, pdev, board_id);
+static int __devinit cciss_wait_for_board_ready(ctlr_info_t *h)
+{
+ int i;
+ u32 scratchpad;
- /* find the memory BAR */
- for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
- if (pci_resource_flags(pdev, i) & IORESOURCE_MEM)
- break;
- }
- if (i == DEVICE_COUNT_RESOURCE) {
- printk(KERN_WARNING "cciss: No memory BAR found\n");
- err = -ENODEV;
- goto err_out_free_res;
+ for (i = 0; i < CCISS_BOARD_READY_ITERATIONS; i++) {
+ scratchpad = readl(h->vaddr + SA5_SCRATCHPAD_OFFSET);
+ if (scratchpad == CCISS_FIRMWARE_READY)
+ return 0;
+ msleep(CCISS_BOARD_READY_POLL_INTERVAL_MSECS);
}
+ dev_warn(&h->pdev->dev, "board not ready, timed out.\n");
+ return -ENODEV;
+}
- c->paddr = pci_resource_start(pdev, i); /* addressing mode bits
- * already removed
- */
+static int __devinit cciss_find_cfg_addrs(struct pci_dev *pdev,
+ void __iomem *vaddr, u32 *cfg_base_addr, u64 *cfg_base_addr_index,
+ u64 *cfg_offset)
+{
+ *cfg_base_addr = readl(vaddr + SA5_CTCFG_OFFSET);
+ *cfg_offset = readl(vaddr + SA5_CTMEM_OFFSET);
+ *cfg_base_addr &= (u32) 0x0000ffff;
+ *cfg_base_addr_index = find_PCI_BAR_index(pdev, *cfg_base_addr);
+ if (*cfg_base_addr_index == -1) {
+ dev_warn(&pdev->dev, "cannot find cfg_base_addr_index, "
+ "*cfg_base_addr = 0x%08x\n", *cfg_base_addr);
+ return -ENODEV;
+ }
+ return 0;
+}
-#ifdef CCISS_DEBUG
- printk("address 0 = %lx\n", c->paddr);
-#endif /* CCISS_DEBUG */
- c->vaddr = remap_pci_mem(c->paddr, 0x250);
+static int __devinit cciss_find_cfgtables(ctlr_info_t *h)
+{
+ u64 cfg_offset;
+ u32 cfg_base_addr;
+ u64 cfg_base_addr_index;
+ u32 trans_offset;
+ int rc;
- /* Wait for the board to become ready. (PCI hotplug needs this.)
- * We poll for up to 120 secs, once per 100ms. */
- for (i = 0; i < 1200; i++) {
- scratchpad = readl(c->vaddr + SA5_SCRATCHPAD_OFFSET);
- if (scratchpad == CCISS_FIRMWARE_READY)
- break;
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(msecs_to_jiffies(100)); /* wait 100ms */
- }
- if (scratchpad != CCISS_FIRMWARE_READY) {
- printk(KERN_WARNING "cciss: Board not ready. Timed out.\n");
- err = -ENODEV;
- goto err_out_free_res;
- }
+ rc = cciss_find_cfg_addrs(h->pdev, h->vaddr, &cfg_base_addr,
+ &cfg_base_addr_index, &cfg_offset);
+ if (rc)
+ return rc;
+ h->cfgtable = remap_pci_mem(pci_resource_start(h->pdev,
+ cfg_base_addr_index) + cfg_offset, sizeof(h->cfgtable));
+ if (!h->cfgtable)
+ return -ENOMEM;
+ /* Find performant mode table. */
+ trans_offset = readl(&h->cfgtable->TransMethodOffset);
+ h->transtable = remap_pci_mem(pci_resource_start(h->pdev,
+ cfg_base_addr_index)+cfg_offset+trans_offset,
+ sizeof(*h->transtable));
+ if (!h->transtable)
+ return -ENOMEM;
+ return 0;
+}
- /* get the address index number */
- cfg_base_addr = readl(c->vaddr + SA5_CTCFG_OFFSET);
- cfg_base_addr &= (__u32) 0x0000ffff;
-#ifdef CCISS_DEBUG
- printk("cfg base address = %x\n", cfg_base_addr);
-#endif /* CCISS_DEBUG */
- cfg_base_addr_index = find_PCI_BAR_index(pdev, cfg_base_addr);
-#ifdef CCISS_DEBUG
- printk("cfg base address index = %llx\n",
- (unsigned long long)cfg_base_addr_index);
-#endif /* CCISS_DEBUG */
- if (cfg_base_addr_index == -1) {
- printk(KERN_WARNING "cciss: Cannot find cfg_base_addr_index\n");
- err = -ENODEV;
- goto err_out_free_res;
+static void __devinit cciss_get_max_perf_mode_cmds(struct ctlr_info *h)
+{
+ h->max_commands = readl(&(h->cfgtable->MaxPerformantModeCommands));
+ if (h->max_commands < 16) {
+ dev_warn(&h->pdev->dev, "Controller reports "
+ "max supported commands of %d, an obvious lie. "
+ "Using 16. Ensure that firmware is up to date.\n",
+ h->max_commands);
+ h->max_commands = 16;
}
+}
- cfg_offset = readl(c->vaddr + SA5_CTMEM_OFFSET);
-#ifdef CCISS_DEBUG
- printk("cfg offset = %llx\n", (unsigned long long)cfg_offset);
-#endif /* CCISS_DEBUG */
- c->cfgtable = remap_pci_mem(pci_resource_start(pdev,
- cfg_base_addr_index) +
- cfg_offset, sizeof(CfgTable_struct));
- c->board_id = board_id;
-
-#ifdef CCISS_DEBUG
- print_cfg_table(c->cfgtable);
-#endif /* CCISS_DEBUG */
-
- /* Some controllers support Zero Memory Raid (ZMR).
- * When configured in ZMR mode the number of supported
- * commands drops to 64. So instead of just setting an
- * arbitrary value we make the driver a little smarter.
- * We read the config table to tell us how many commands
- * are supported on the controller then subtract 4 to
- * leave a little room for ioctl calls.
- */
- c->max_commands = readl(&(c->cfgtable->CmdsOutMax));
- c->maxsgentries = readl(&(c->cfgtable->MaxSGElements));
-
+/* Interrogate the hardware for some limits:
+ * max commands, max SG elements without chaining, and with chaining,
+ * SG chain block size, etc.
+ */
+static void __devinit cciss_find_board_params(ctlr_info_t *h)
+{
+ cciss_get_max_perf_mode_cmds(h);
+ h->nr_cmds = h->max_commands - 4; /* Allow room for some ioctls */
+ h->maxsgentries = readl(&(h->cfgtable->MaxSGElements));
/*
- * Limit native command to 32 s/g elements to save dma'able memory.
+ * Limit in-command s/g elements to 32 save dma'able memory.
* Howvever spec says if 0, use 31
*/
-
- c->max_cmd_sgentries = 31;
- if (c->maxsgentries > 512) {
- c->max_cmd_sgentries = 32;
- c->chainsize = c->maxsgentries - c->max_cmd_sgentries + 1;
- c->maxsgentries -= 1; /* account for chain pointer */
+ h->max_cmd_sgentries = 31;
+ if (h->maxsgentries > 512) {
+ h->max_cmd_sgentries = 32;
+ h->chainsize = h->maxsgentries - h->max_cmd_sgentries + 1;
+ h->maxsgentries--; /* save one for chain pointer */
} else {
- c->maxsgentries = 31; /* Default to traditional value */
- c->chainsize = 0; /* traditional */
+ h->maxsgentries = 31; /* default to traditional values */
+ h->chainsize = 0;
}
+}
- c->product_name = products[prod_index].product_name;
- c->access = *(products[prod_index].access);
- c->nr_cmds = c->max_commands - 4;
- if ((readb(&c->cfgtable->Signature[0]) != 'C') ||
- (readb(&c->cfgtable->Signature[1]) != 'I') ||
- (readb(&c->cfgtable->Signature[2]) != 'S') ||
- (readb(&c->cfgtable->Signature[3]) != 'S')) {
- printk("Does not appear to be a valid CISS config table\n");
- err = -ENODEV;
- goto err_out_free_res;
+static inline bool CISS_signature_present(ctlr_info_t *h)
+{
+ if ((readb(&h->cfgtable->Signature[0]) != 'C') ||
+ (readb(&h->cfgtable->Signature[1]) != 'I') ||
+ (readb(&h->cfgtable->Signature[2]) != 'S') ||
+ (readb(&h->cfgtable->Signature[3]) != 'S')) {
+ dev_warn(&h->pdev->dev, "not a valid CISS config table\n");
+ return false;
}
+ return true;
+}
+
+/* Need to enable prefetch in the SCSI core for 6400 in x86 */
+static inline void cciss_enable_scsi_prefetch(ctlr_info_t *h)
+{
#ifdef CONFIG_X86
- {
- /* Need to enable prefetch in the SCSI core for 6400 in x86 */
- __u32 prefetch;
- prefetch = readl(&(c->cfgtable->SCSI_Prefetch));
- prefetch |= 0x100;
- writel(prefetch, &(c->cfgtable->SCSI_Prefetch));
- }
+ u32 prefetch;
+
+ prefetch = readl(&(h->cfgtable->SCSI_Prefetch));
+ prefetch |= 0x100;
+ writel(prefetch, &(h->cfgtable->SCSI_Prefetch));
#endif
+}
- /* Disabling DMA prefetch and refetch for the P600.
- * An ASIC bug may result in accesses to invalid memory addresses.
- * We've disabled prefetch for some time now. Testing with XEN
- * kernels revealed a bug in the refetch if dom0 resides on a P600.
- */
- if(board_id == 0x3225103C) {
- __u32 dma_prefetch;
- __u32 dma_refetch;
- dma_prefetch = readl(c->vaddr + I2O_DMA1_CFG);
- dma_prefetch |= 0x8000;
- writel(dma_prefetch, c->vaddr + I2O_DMA1_CFG);
- pci_read_config_dword(pdev, PCI_COMMAND_PARITY, &dma_refetch);
- dma_refetch |= 0x1;
- pci_write_config_dword(pdev, PCI_COMMAND_PARITY, dma_refetch);
+/* Disable DMA prefetch for the P600. Otherwise an ASIC bug may result
+ * in a prefetch beyond physical memory.
+ */
+static inline void cciss_p600_dma_prefetch_quirk(ctlr_info_t *h)
+{
+ u32 dma_prefetch;
+ __u32 dma_refetch;
+
+ if (h->board_id != 0x3225103C)
+ return;
+ dma_prefetch = readl(h->vaddr + I2O_DMA1_CFG);
+ dma_prefetch |= 0x8000;
+ writel(dma_prefetch, h->vaddr + I2O_DMA1_CFG);
+ pci_read_config_dword(h->pdev, PCI_COMMAND_PARITY, &dma_refetch);
+ dma_refetch |= 0x1;
+ pci_write_config_dword(h->pdev, PCI_COMMAND_PARITY, dma_refetch);
+}
+
+static int __devinit cciss_pci_init(ctlr_info_t *h)
+{
+ int prod_index, err;
+
+ prod_index = cciss_lookup_board_id(h->pdev, &h->board_id);
+ if (prod_index < 0)
+ return -ENODEV;
+ h->product_name = products[prod_index].product_name;
+ h->access = *(products[prod_index].access);
+
+ if (cciss_board_disabled(h)) {
+ dev_warn(&h->pdev->dev, "controller appears to be disabled\n");
+ return -ENODEV;
+ }
+ err = pci_enable_device(h->pdev);
+ if (err) {
+ dev_warn(&h->pdev->dev, "Unable to Enable PCI device\n");
+ return err;
}
-#ifdef CCISS_DEBUG
- printk("Trying to put board into Simple mode\n");
-#endif /* CCISS_DEBUG */
- c->max_commands = readl(&(c->cfgtable->CmdsOutMax));
- /* Update the field, and then ring the doorbell */
- writel(CFGTBL_Trans_Simple, &(c->cfgtable->HostWrite.TransportRequest));
- writel(CFGTBL_ChangeReq, c->vaddr + SA5_DOORBELL);
+ err = pci_request_regions(h->pdev, "cciss");
+ if (err) {
+ dev_warn(&h->pdev->dev,
+ "Cannot obtain PCI resources, aborting\n");
+ return err;
+ }
- /* under certain very rare conditions, this can take awhile.
- * (e.g.: hot replace a failed 144GB drive in a RAID 5 set right
- * as we enter this code.) */
- for (i = 0; i < MAX_CONFIG_WAIT; i++) {
- if (!(readl(c->vaddr + SA5_DOORBELL) & CFGTBL_ChangeReq))
- break;
- /* delay and try again */
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(msecs_to_jiffies(1));
+ dev_dbg(&h->pdev->dev, "irq = %x\n", h->pdev->irq);
+ dev_dbg(&h->pdev->dev, "board_id = %x\n", h->board_id);
+
+/* If the kernel supports MSI/MSI-X we will try to enable that functionality,
+ * else we use the IO-APIC interrupt assigned to us by system ROM.
+ */
+ cciss_interrupt_mode(h);
+ err = cciss_pci_find_memory_BAR(h->pdev, &h->paddr);
+ if (err)
+ goto err_out_free_res;
+ h->vaddr = remap_pci_mem(h->paddr, 0x250);
+ if (!h->vaddr) {
+ err = -ENOMEM;
+ goto err_out_free_res;
}
+ err = cciss_wait_for_board_ready(h);
+ if (err)
+ goto err_out_free_res;
+ err = cciss_find_cfgtables(h);
+ if (err)
+ goto err_out_free_res;
+ print_cfg_table(h);
+ cciss_find_board_params(h);
-#ifdef CCISS_DEBUG
- printk(KERN_DEBUG "I counter got to %d %x\n", i,
- readl(c->vaddr + SA5_DOORBELL));
-#endif /* CCISS_DEBUG */
-#ifdef CCISS_DEBUG
- print_cfg_table(c->cfgtable);
-#endif /* CCISS_DEBUG */
-
- if (!(readl(&(c->cfgtable->TransportActive)) & CFGTBL_Trans_Simple)) {
- printk(KERN_WARNING "cciss: unable to get board into"
- " simple mode\n");
+ if (!CISS_signature_present(h)) {
err = -ENODEV;
goto err_out_free_res;
}
+ cciss_enable_scsi_prefetch(h);
+ cciss_p600_dma_prefetch_quirk(h);
+ cciss_put_controller_into_performant_mode(h);
return 0;
err_out_free_res:
* Deliberately omit pci_disable_device(): it does something nasty to
* Smart Array controllers that pci_enable_device does not undo
*/
- pci_release_regions(pdev);
+ if (h->transtable)
+ iounmap(h->transtable);
+ if (h->cfgtable)
+ iounmap(h->cfgtable);
+ if (h->vaddr)
+ iounmap(h->vaddr);
+ pci_release_regions(h->pdev);
return err;
}
/* Function to find the first free pointer into our hba[] array
* Returns -1 if no free entries are left.
*/
-static int alloc_cciss_hba(void)
+static int alloc_cciss_hba(struct pci_dev *pdev)
{
int i;
for (i = 0; i < MAX_CTLR; i++) {
if (!hba[i]) {
- ctlr_info_t *p;
+ ctlr_info_t *h;
- p = kzalloc(sizeof(ctlr_info_t), GFP_KERNEL);
- if (!p)
+ h = kzalloc(sizeof(ctlr_info_t), GFP_KERNEL);
+ if (!h)
goto Enomem;
- hba[i] = p;
+ hba[i] = h;
return i;
}
}
- printk(KERN_WARNING "cciss: This driver supports a maximum"
+ dev_warn(&pdev->dev, "This driver supports a maximum"
" of %d controllers.\n", MAX_CTLR);
return -1;
Enomem:
- printk(KERN_ERR "cciss: out of memory.\n");
+ dev_warn(&pdev->dev, "out of memory.\n");
return -1;
}
-static void free_hba(int n)
+static void free_hba(ctlr_info_t *h)
{
- ctlr_info_t *h = hba[n];
int i;
- hba[n] = NULL;
+ hba[h->ctlr] = NULL;
for (i = 0; i < h->highest_lun + 1; i++)
if (h->gendisk[i] != NULL)
put_disk(h->gendisk[i]);
/* we leak the DMA buffer here ... no choice since the controller could
still complete the command. */
if (i == 10) {
- printk(KERN_ERR "cciss: controller message %02x:%02x timed out\n",
+ dev_err(&pdev->dev,
+ "controller message %02x:%02x timed out\n",
opcode, type);
return -ETIMEDOUT;
}
pci_free_consistent(pdev, cmd_sz, cmd, paddr64);
if (tag & 2) {
- printk(KERN_ERR "cciss: controller message %02x:%02x failed\n",
+ dev_err(&pdev->dev, "controller message %02x:%02x failed\n",
opcode, type);
return -EIO;
}
- printk(KERN_INFO "cciss: controller message %02x:%02x succeeded\n",
+ dev_info(&pdev->dev, "controller message %02x:%02x succeeded\n",
opcode, type);
return 0;
}
if (pos) {
pci_read_config_word(pdev, msi_control_reg(pos), &control);
if (control & PCI_MSI_FLAGS_ENABLE) {
- printk(KERN_INFO "cciss: resetting MSI\n");
+ dev_info(&pdev->dev, "resetting MSI\n");
pci_write_config_word(pdev, msi_control_reg(pos), control & ~PCI_MSI_FLAGS_ENABLE);
}
}
if (pos) {
pci_read_config_word(pdev, msi_control_reg(pos), &control);
if (control & PCI_MSIX_FLAGS_ENABLE) {
- printk(KERN_INFO "cciss: resetting MSI-X\n");
+ dev_info(&pdev->dev, "resetting MSI-X\n");
pci_write_config_word(pdev, msi_control_reg(pos), control & ~PCI_MSIX_FLAGS_ENABLE);
}
}
return 0;
}
-/* This does a hard reset of the controller using PCI power management
- * states. */
-static __devinit int cciss_hard_reset_controller(struct pci_dev *pdev)
+static int cciss_controller_hard_reset(struct pci_dev *pdev,
+ void * __iomem vaddr, bool use_doorbell)
{
- u16 pmcsr, saved_config_space[32];
- int i, pos;
+ u16 pmcsr;
+ int pos;
- printk(KERN_INFO "cciss: using PCI PM to reset controller\n");
+ if (use_doorbell) {
+ /* For everything after the P600, the PCI power state method
+ * of resetting the controller doesn't work, so we have this
+ * other way using the doorbell register.
+ */
+ dev_info(&pdev->dev, "using doorbell to reset controller\n");
+ writel(DOORBELL_CTLR_RESET, vaddr + SA5_DOORBELL);
+ msleep(1000);
+ } else { /* Try to do it the PCI power state way */
+
+ /* Quoting from the Open CISS Specification: "The Power
+ * Management Control/Status Register (CSR) controls the power
+ * state of the device. The normal operating state is D0,
+ * CSR=00h. The software off state is D3, CSR=03h. To reset
+ * the controller, place the interface device in D3 then to D0,
+ * this causes a secondary PCI reset which will reset the
+ * controller." */
+
+ pos = pci_find_capability(pdev, PCI_CAP_ID_PM);
+ if (pos == 0) {
+ dev_err(&pdev->dev,
+ "cciss_controller_hard_reset: "
+ "PCI PM not supported\n");
+ return -ENODEV;
+ }
+ dev_info(&pdev->dev, "using PCI PM to reset controller\n");
+ /* enter the D3hot power management state */
+ pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pmcsr);
+ pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
+ pmcsr |= PCI_D3hot;
+ pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
- /* This is very nearly the same thing as
+ msleep(500);
- pci_save_state(pci_dev);
- pci_set_power_state(pci_dev, PCI_D3hot);
- pci_set_power_state(pci_dev, PCI_D0);
- pci_restore_state(pci_dev);
+ /* enter the D0 power management state */
+ pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
+ pmcsr |= PCI_D0;
+ pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
- but we can't use these nice canned kernel routines on
- kexec, because they also check the MSI/MSI-X state in PCI
- configuration space and do the wrong thing when it is
- set/cleared. Also, the pci_save/restore_state functions
- violate the ordering requirements for restoring the
- configuration space from the CCISS document (see the
- comment below). So we roll our own .... */
+ msleep(500);
+ }
+ return 0;
+}
- for (i = 0; i < 32; i++)
- pci_read_config_word(pdev, 2*i, &saved_config_space[i]);
+/* This does a hard reset of the controller using PCI power management
+ * states or using the doorbell register. */
+static __devinit int cciss_kdump_hard_reset_controller(struct pci_dev *pdev)
+{
+ u16 saved_config_space[32];
+ u64 cfg_offset;
+ u32 cfg_base_addr;
+ u64 cfg_base_addr_index;
+ void __iomem *vaddr;
+ unsigned long paddr;
+ u32 misc_fw_support, active_transport;
+ int rc, i;
+ CfgTable_struct __iomem *cfgtable;
+ bool use_doorbell;
+ u32 board_id;
+
+ /* For controllers as old a the p600, this is very nearly
+ * the same thing as
+ *
+ * pci_save_state(pci_dev);
+ * pci_set_power_state(pci_dev, PCI_D3hot);
+ * pci_set_power_state(pci_dev, PCI_D0);
+ * pci_restore_state(pci_dev);
+ *
+ * but we can't use these nice canned kernel routines on
+ * kexec, because they also check the MSI/MSI-X state in PCI
+ * configuration space and do the wrong thing when it is
+ * set/cleared. Also, the pci_save/restore_state functions
+ * violate the ordering requirements for restoring the
+ * configuration space from the CCISS document (see the
+ * comment below). So we roll our own ....
+ *
+ * For controllers newer than the P600, the pci power state
+ * method of resetting doesn't work so we have another way
+ * using the doorbell register.
+ */
- pos = pci_find_capability(pdev, PCI_CAP_ID_PM);
- if (pos == 0) {
- printk(KERN_ERR "cciss_reset_controller: PCI PM not supported\n");
+ /* Exclude 640x boards. These are two pci devices in one slot
+ * which share a battery backed cache module. One controls the
+ * cache, the other accesses the cache through the one that controls
+ * it. If we reset the one controlling the cache, the other will
+ * likely not be happy. Just forbid resetting this conjoined mess.
+ */
+ cciss_lookup_board_id(pdev, &board_id);
+ if (board_id == 0x409C0E11 || board_id == 0x409D0E11) {
+ dev_warn(&pdev->dev, "Cannot reset Smart Array 640x "
+ "due to shared cache module.");
return -ENODEV;
}
- /* Quoting from the Open CISS Specification: "The Power
- * Management Control/Status Register (CSR) controls the power
- * state of the device. The normal operating state is D0,
- * CSR=00h. The software off state is D3, CSR=03h. To reset
- * the controller, place the interface device in D3 then to
- * D0, this causes a secondary PCI reset which will reset the
- * controller." */
+ for (i = 0; i < 32; i++)
+ pci_read_config_word(pdev, 2*i, &saved_config_space[i]);
- /* enter the D3hot power management state */
- pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pmcsr);
- pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
- pmcsr |= PCI_D3hot;
- pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
+ /* find the first memory BAR, so we can find the cfg table */
+ rc = cciss_pci_find_memory_BAR(pdev, &paddr);
+ if (rc)
+ return rc;
+ vaddr = remap_pci_mem(paddr, 0x250);
+ if (!vaddr)
+ return -ENOMEM;
- schedule_timeout_uninterruptible(HZ >> 1);
+ /* find cfgtable in order to check if reset via doorbell is supported */
+ rc = cciss_find_cfg_addrs(pdev, vaddr, &cfg_base_addr,
+ &cfg_base_addr_index, &cfg_offset);
+ if (rc)
+ goto unmap_vaddr;
+ cfgtable = remap_pci_mem(pci_resource_start(pdev,
+ cfg_base_addr_index) + cfg_offset, sizeof(*cfgtable));
+ if (!cfgtable) {
+ rc = -ENOMEM;
+ goto unmap_vaddr;
+ }
- /* enter the D0 power management state */
- pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
- pmcsr |= PCI_D0;
- pci_write_config_word(pdev, pos + PCI_PM_CTRL, pmcsr);
+ /* If reset via doorbell register is supported, use that. */
+ misc_fw_support = readl(&cfgtable->misc_fw_support);
+ use_doorbell = misc_fw_support & MISC_FW_DOORBELL_RESET;
- schedule_timeout_uninterruptible(HZ >> 1);
+ rc = cciss_controller_hard_reset(pdev, vaddr, use_doorbell);
+ if (rc)
+ goto unmap_cfgtable;
/* Restore the PCI configuration space. The Open CISS
* Specification says, "Restore the PCI Configuration
* Registers, offsets 00h through 60h. It is important to
* restore the command register, 16-bits at offset 04h,
* last. Do not restore the configuration status register,
- * 16-bits at offset 06h." Note that the offset is 2*i. */
+ * 16-bits at offset 06h." Note that the offset is 2*i.
+ */
for (i = 0; i < 32; i++) {
if (i == 2 || i == 3)
continue;
wmb();
pci_write_config_word(pdev, 4, saved_config_space[2]);
+ /* Some devices (notably the HP Smart Array 5i Controller)
+ need a little pause here */
+ msleep(CCISS_POST_RESET_PAUSE_MSECS);
+
+ /* Controller should be in simple mode at this point. If it's not,
+ * It means we're on one of those controllers which doesn't support
+ * the doorbell reset method and on which the PCI power management reset
+ * method doesn't work (P800, for example.)
+ * In those cases, don't try to proceed, as it generally doesn't work.
+ */
+ active_transport = readl(&cfgtable->TransportActive);
+ if (active_transport & PERFORMANT_MODE) {
+ dev_warn(&pdev->dev, "Unable to successfully reset controller,"
+ " Ignoring controller.\n");
+ rc = -ENODEV;
+ }
+
+unmap_cfgtable:
+ iounmap(cfgtable);
+
+unmap_vaddr:
+ iounmap(vaddr);
+ return rc;
+}
+
+static __devinit int cciss_init_reset_devices(struct pci_dev *pdev)
+{
+ int rc, i;
+
+ if (!reset_devices)
+ return 0;
+
+ /* Reset the controller with a PCI power-cycle or via doorbell */
+ rc = cciss_kdump_hard_reset_controller(pdev);
+
+ /* -ENOTSUPP here means we cannot reset the controller
+ * but it's already (and still) up and running in
+ * "performant mode". Or, it might be 640x, which can't reset
+ * due to concerns about shared bbwc between 6402/6404 pair.
+ */
+ if (rc == -ENOTSUPP)
+ return 0; /* just try to do the kdump anyhow. */
+ if (rc)
+ return -ENODEV;
+ if (cciss_reset_msi(pdev))
+ return -ENODEV;
+
+ /* Now try to get the controller to respond to a no-op */
+ for (i = 0; i < CCISS_POST_RESET_NOOP_RETRIES; i++) {
+ if (cciss_noop(pdev) == 0)
+ break;
+ else
+ dev_warn(&pdev->dev, "no-op failed%s\n",
+ (i < CCISS_POST_RESET_NOOP_RETRIES - 1 ?
+ "; re-trying" : ""));
+ msleep(CCISS_POST_RESET_NOOP_INTERVAL_MSECS);
+ }
return 0;
}
int rc;
int dac, return_code;
InquiryData_struct *inq_buff;
+ ctlr_info_t *h;
- if (reset_devices) {
- /* Reset the controller with a PCI power-cycle */
- if (cciss_hard_reset_controller(pdev) || cciss_reset_msi(pdev))
- return -ENODEV;
-
- /* Now try to get the controller to respond to a no-op. Some
- devices (notably the HP Smart Array 5i Controller) need
- up to 30 seconds to respond. */
- for (i=0; i<30; i++) {
- if (cciss_noop(pdev) == 0)
- break;
-
- schedule_timeout_uninterruptible(HZ);
- }
- if (i == 30) {
- printk(KERN_ERR "cciss: controller seems dead\n");
- return -EBUSY;
- }
- }
-
- i = alloc_cciss_hba();
+ rc = cciss_init_reset_devices(pdev);
+ if (rc)
+ return rc;
+ i = alloc_cciss_hba(pdev);
if (i < 0)
return -1;
- hba[i]->busy_initializing = 1;
- INIT_HLIST_HEAD(&hba[i]->cmpQ);
- INIT_HLIST_HEAD(&hba[i]->reqQ);
- mutex_init(&hba[i]->busy_shutting_down);
+ h = hba[i];
+ h->pdev = pdev;
+ h->busy_initializing = 1;
+ INIT_HLIST_HEAD(&h->cmpQ);
+ INIT_HLIST_HEAD(&h->reqQ);
+ mutex_init(&h->busy_shutting_down);
- if (cciss_pci_init(hba[i], pdev) != 0)
+ if (cciss_pci_init(h) != 0)
goto clean_no_release_regions;
- sprintf(hba[i]->devname, "cciss%d", i);
- hba[i]->ctlr = i;
- hba[i]->pdev = pdev;
+ sprintf(h->devname, "cciss%d", i);
+ h->ctlr = i;
- init_completion(&hba[i]->scan_wait);
+ init_completion(&h->scan_wait);
- if (cciss_create_hba_sysfs_entry(hba[i]))
+ if (cciss_create_hba_sysfs_entry(h))
goto clean0;
/* configure PCI DMA stuff */
else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32)))
dac = 0;
else {
- printk(KERN_ERR "cciss: no suitable DMA available\n");
+ dev_err(&h->pdev->dev, "no suitable DMA available\n");
goto clean1;
}
* 8 controller support.
*/
if (i < MAX_CTLR_ORIG)
- hba[i]->major = COMPAQ_CISS_MAJOR + i;
- rc = register_blkdev(hba[i]->major, hba[i]->devname);
+ h->major = COMPAQ_CISS_MAJOR + i;
+ rc = register_blkdev(h->major, h->devname);
if (rc == -EBUSY || rc == -EINVAL) {
- printk(KERN_ERR
- "cciss: Unable to get major number %d for %s "
- "on hba %d\n", hba[i]->major, hba[i]->devname, i);
+ dev_err(&h->pdev->dev,
+ "Unable to get major number %d for %s "
+ "on hba %d\n", h->major, h->devname, i);
goto clean1;
} else {
if (i >= MAX_CTLR_ORIG)
- hba[i]->major = rc;
+ h->major = rc;
}
/* make sure the board interrupts are off */
- hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_OFF);
- if (request_irq(hba[i]->intr[SIMPLE_MODE_INT], do_cciss_intr,
- IRQF_DISABLED | IRQF_SHARED, hba[i]->devname, hba[i])) {
- printk(KERN_ERR "cciss: Unable to get irq %d for %s\n",
- hba[i]->intr[SIMPLE_MODE_INT], hba[i]->devname);
- goto clean2;
+ h->access.set_intr_mask(h, CCISS_INTR_OFF);
+ if (h->msi_vector || h->msix_vector) {
+ if (request_irq(h->intr[PERF_MODE_INT],
+ do_cciss_msix_intr,
+ IRQF_DISABLED, h->devname, h)) {
+ dev_err(&h->pdev->dev, "Unable to get irq %d for %s\n",
+ h->intr[PERF_MODE_INT], h->devname);
+ goto clean2;
+ }
+ } else {
+ if (request_irq(h->intr[PERF_MODE_INT], do_cciss_intx,
+ IRQF_DISABLED, h->devname, h)) {
+ dev_err(&h->pdev->dev, "Unable to get irq %d for %s\n",
+ h->intr[PERF_MODE_INT], h->devname);
+ goto clean2;
+ }
}
- printk(KERN_INFO "%s: <0x%x> at PCI %s IRQ %d%s using DAC\n",
- hba[i]->devname, pdev->device, pci_name(pdev),
- hba[i]->intr[SIMPLE_MODE_INT], dac ? "" : " not");
+ dev_info(&h->pdev->dev, "%s: <0x%x> at PCI %s IRQ %d%s using DAC\n",
+ h->devname, pdev->device, pci_name(pdev),
+ h->intr[PERF_MODE_INT], dac ? "" : " not");
- hba[i]->cmd_pool_bits =
- kmalloc(DIV_ROUND_UP(hba[i]->nr_cmds, BITS_PER_LONG)
+ h->cmd_pool_bits =
+ kmalloc(DIV_ROUND_UP(h->nr_cmds, BITS_PER_LONG)
* sizeof(unsigned long), GFP_KERNEL);
- hba[i]->cmd_pool = (CommandList_struct *)
- pci_alloc_consistent(hba[i]->pdev,
- hba[i]->nr_cmds * sizeof(CommandList_struct),
- &(hba[i]->cmd_pool_dhandle));
- hba[i]->errinfo_pool = (ErrorInfo_struct *)
- pci_alloc_consistent(hba[i]->pdev,
- hba[i]->nr_cmds * sizeof(ErrorInfo_struct),
- &(hba[i]->errinfo_pool_dhandle));
- if ((hba[i]->cmd_pool_bits == NULL)
- || (hba[i]->cmd_pool == NULL)
- || (hba[i]->errinfo_pool == NULL)) {
- printk(KERN_ERR "cciss: out of memory");
+ h->cmd_pool = (CommandList_struct *)
+ pci_alloc_consistent(h->pdev,
+ h->nr_cmds * sizeof(CommandList_struct),
+ &(h->cmd_pool_dhandle));
+ h->errinfo_pool = (ErrorInfo_struct *)
+ pci_alloc_consistent(h->pdev,
+ h->nr_cmds * sizeof(ErrorInfo_struct),
+ &(h->errinfo_pool_dhandle));
+ if ((h->cmd_pool_bits == NULL)
+ || (h->cmd_pool == NULL)
+ || (h->errinfo_pool == NULL)) {
+ dev_err(&h->pdev->dev, "out of memory");
goto clean4;
}
/* Need space for temp scatter list */
- hba[i]->scatter_list = kmalloc(hba[i]->max_commands *
+ h->scatter_list = kmalloc(h->max_commands *
sizeof(struct scatterlist *),
GFP_KERNEL);
- for (k = 0; k < hba[i]->nr_cmds; k++) {
- hba[i]->scatter_list[k] = kmalloc(sizeof(struct scatterlist) *
- hba[i]->maxsgentries,
+ for (k = 0; k < h->nr_cmds; k++) {
+ h->scatter_list[k] = kmalloc(sizeof(struct scatterlist) *
+ h->maxsgentries,
GFP_KERNEL);
- if (hba[i]->scatter_list[k] == NULL) {
- printk(KERN_ERR "cciss%d: could not allocate "
- "s/g lists\n", i);
+ if (h->scatter_list[k] == NULL) {
+ dev_err(&h->pdev->dev,
+ "could not allocate s/g lists\n");
goto clean4;
}
}
- hba[i]->cmd_sg_list = cciss_allocate_sg_chain_blocks(hba[i],
- hba[i]->chainsize, hba[i]->nr_cmds);
- if (!hba[i]->cmd_sg_list && hba[i]->chainsize > 0)
+ h->cmd_sg_list = cciss_allocate_sg_chain_blocks(h,
+ h->chainsize, h->nr_cmds);
+ if (!h->cmd_sg_list && h->chainsize > 0)
goto clean4;
- spin_lock_init(&hba[i]->lock);
+ spin_lock_init(&h->lock);
/* Initialize the pdev driver private data.
- have it point to hba[i]. */
- pci_set_drvdata(pdev, hba[i]);
+ have it point to h. */
+ pci_set_drvdata(pdev, h);
/* command and error info recs zeroed out before
they are used */
- memset(hba[i]->cmd_pool_bits, 0,
- DIV_ROUND_UP(hba[i]->nr_cmds, BITS_PER_LONG)
+ memset(h->cmd_pool_bits, 0,
+ DIV_ROUND_UP(h->nr_cmds, BITS_PER_LONG)
* sizeof(unsigned long));
- hba[i]->num_luns = 0;
- hba[i]->highest_lun = -1;
+ h->num_luns = 0;
+ h->highest_lun = -1;
for (j = 0; j < CISS_MAX_LUN; j++) {
- hba[i]->drv[j] = NULL;
- hba[i]->gendisk[j] = NULL;
+ h->drv[j] = NULL;
+ h->gendisk[j] = NULL;
}
- cciss_scsi_setup(i);
+ cciss_scsi_setup(h);
/* Turn the interrupts on so we can service requests */
- hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_ON);
+ h->access.set_intr_mask(h, CCISS_INTR_ON);
/* Get the firmware version */
inq_buff = kzalloc(sizeof(InquiryData_struct), GFP_KERNEL);
if (inq_buff == NULL) {
- printk(KERN_ERR "cciss: out of memory\n");
+ dev_err(&h->pdev->dev, "out of memory\n");
goto clean4;
}
- return_code = sendcmd_withirq(CISS_INQUIRY, i, inq_buff,
+ return_code = sendcmd_withirq(h, CISS_INQUIRY, inq_buff,
sizeof(InquiryData_struct), 0, CTLR_LUNID, TYPE_CMD);
if (return_code == IO_OK) {
- hba[i]->firm_ver[0] = inq_buff->data_byte[32];
- hba[i]->firm_ver[1] = inq_buff->data_byte[33];
- hba[i]->firm_ver[2] = inq_buff->data_byte[34];
- hba[i]->firm_ver[3] = inq_buff->data_byte[35];
+ h->firm_ver[0] = inq_buff->data_byte[32];
+ h->firm_ver[1] = inq_buff->data_byte[33];
+ h->firm_ver[2] = inq_buff->data_byte[34];
+ h->firm_ver[3] = inq_buff->data_byte[35];
} else { /* send command failed */
- printk(KERN_WARNING "cciss: unable to determine firmware"
+ dev_warn(&h->pdev->dev, "unable to determine firmware"
" version of controller\n");
}
kfree(inq_buff);
- cciss_procinit(i);
+ cciss_procinit(h);
- hba[i]->cciss_max_sectors = 8192;
+ h->cciss_max_sectors = 8192;
- rebuild_lun_table(hba[i], 1, 0);
- hba[i]->busy_initializing = 0;
+ rebuild_lun_table(h, 1, 0);
+ h->busy_initializing = 0;
return 1;
clean4:
- kfree(hba[i]->cmd_pool_bits);
+ kfree(h->cmd_pool_bits);
/* Free up sg elements */
- for (k = 0; k < hba[i]->nr_cmds; k++)
- kfree(hba[i]->scatter_list[k]);
- kfree(hba[i]->scatter_list);
- cciss_free_sg_chain_blocks(hba[i]->cmd_sg_list, hba[i]->nr_cmds);
- if (hba[i]->cmd_pool)
- pci_free_consistent(hba[i]->pdev,
- hba[i]->nr_cmds * sizeof(CommandList_struct),
- hba[i]->cmd_pool, hba[i]->cmd_pool_dhandle);
- if (hba[i]->errinfo_pool)
- pci_free_consistent(hba[i]->pdev,
- hba[i]->nr_cmds * sizeof(ErrorInfo_struct),
- hba[i]->errinfo_pool,
- hba[i]->errinfo_pool_dhandle);
- free_irq(hba[i]->intr[SIMPLE_MODE_INT], hba[i]);
+ for (k = 0; k < h->nr_cmds; k++)
+ kfree(h->scatter_list[k]);
+ kfree(h->scatter_list);
+ cciss_free_sg_chain_blocks(h->cmd_sg_list, h->nr_cmds);
+ if (h->cmd_pool)
+ pci_free_consistent(h->pdev,
+ h->nr_cmds * sizeof(CommandList_struct),
+ h->cmd_pool, h->cmd_pool_dhandle);
+ if (h->errinfo_pool)
+ pci_free_consistent(h->pdev,
+ h->nr_cmds * sizeof(ErrorInfo_struct),
+ h->errinfo_pool,
+ h->errinfo_pool_dhandle);
+ free_irq(h->intr[PERF_MODE_INT], h);
clean2:
- unregister_blkdev(hba[i]->major, hba[i]->devname);
+ unregister_blkdev(h->major, h->devname);
clean1:
- cciss_destroy_hba_sysfs_entry(hba[i]);
+ cciss_destroy_hba_sysfs_entry(h);
clean0:
pci_release_regions(pdev);
clean_no_release_regions:
- hba[i]->busy_initializing = 0;
+ h->busy_initializing = 0;
/*
* Deliberately omit pci_disable_device(): it does something nasty to
* Smart Array controllers that pci_enable_device does not undo
*/
pci_set_drvdata(pdev, NULL);
- free_hba(i);
+ free_hba(h);
return -1;
}
h = pci_get_drvdata(pdev);
flush_buf = kzalloc(4, GFP_KERNEL);
if (!flush_buf) {
- printk(KERN_WARNING
- "cciss:%d cache not flushed, out of memory.\n",
- h->ctlr);
+ dev_warn(&h->pdev->dev, "cache not flushed, out of memory.\n");
return;
}
/* write all data in the battery backed cache to disk */
memset(flush_buf, 0, 4);
- return_code = sendcmd_withirq(CCISS_CACHE_FLUSH, h->ctlr, flush_buf,
+ return_code = sendcmd_withirq(h, CCISS_CACHE_FLUSH, flush_buf,
4, 0, CTLR_LUNID, TYPE_CMD);
kfree(flush_buf);
if (return_code != IO_OK)
- printk(KERN_WARNING "cciss%d: Error flushing cache\n",
- h->ctlr);
+ dev_warn(&h->pdev->dev, "Error flushing cache\n");
h->access.set_intr_mask(h, CCISS_INTR_OFF);
- free_irq(h->intr[2], h);
+ free_irq(h->intr[PERF_MODE_INT], h);
}
static void __devexit cciss_remove_one(struct pci_dev *pdev)
{
- ctlr_info_t *tmp_ptr;
+ ctlr_info_t *h;
int i, j;
if (pci_get_drvdata(pdev) == NULL) {
- printk(KERN_ERR "cciss: Unable to remove device \n");
+ dev_err(&pdev->dev, "Unable to remove device\n");
return;
}
- tmp_ptr = pci_get_drvdata(pdev);
- i = tmp_ptr->ctlr;
+ h = pci_get_drvdata(pdev);
+ i = h->ctlr;
if (hba[i] == NULL) {
- printk(KERN_ERR "cciss: device appears to "
- "already be removed \n");
+ dev_err(&pdev->dev, "device appears to already be removed\n");
return;
}
- mutex_lock(&hba[i]->busy_shutting_down);
+ mutex_lock(&h->busy_shutting_down);
- remove_from_scan_list(hba[i]);
- remove_proc_entry(hba[i]->devname, proc_cciss);
- unregister_blkdev(hba[i]->major, hba[i]->devname);
+ remove_from_scan_list(h);
+ remove_proc_entry(h->devname, proc_cciss);
+ unregister_blkdev(h->major, h->devname);
/* remove it from the disk list */
for (j = 0; j < CISS_MAX_LUN; j++) {
- struct gendisk *disk = hba[i]->gendisk[j];
+ struct gendisk *disk = h->gendisk[j];
if (disk) {
struct request_queue *q = disk->queue;
if (disk->flags & GENHD_FL_UP) {
- cciss_destroy_ld_sysfs_entry(hba[i], j, 1);
+ cciss_destroy_ld_sysfs_entry(h, j, 1);
del_gendisk(disk);
}
if (q)
}
#ifdef CONFIG_CISS_SCSI_TAPE
- cciss_unregister_scsi(i); /* unhook from SCSI subsystem */
+ cciss_unregister_scsi(h); /* unhook from SCSI subsystem */
#endif
cciss_shutdown(pdev);
#ifdef CONFIG_PCI_MSI
- if (hba[i]->msix_vector)
- pci_disable_msix(hba[i]->pdev);
- else if (hba[i]->msi_vector)
- pci_disable_msi(hba[i]->pdev);
+ if (h->msix_vector)
+ pci_disable_msix(h->pdev);
+ else if (h->msi_vector)
+ pci_disable_msi(h->pdev);
#endif /* CONFIG_PCI_MSI */
- iounmap(hba[i]->vaddr);
+ iounmap(h->transtable);
+ iounmap(h->cfgtable);
+ iounmap(h->vaddr);
- pci_free_consistent(hba[i]->pdev, hba[i]->nr_cmds * sizeof(CommandList_struct),
- hba[i]->cmd_pool, hba[i]->cmd_pool_dhandle);
- pci_free_consistent(hba[i]->pdev, hba[i]->nr_cmds * sizeof(ErrorInfo_struct),
- hba[i]->errinfo_pool, hba[i]->errinfo_pool_dhandle);
- kfree(hba[i]->cmd_pool_bits);
+ pci_free_consistent(h->pdev, h->nr_cmds * sizeof(CommandList_struct),
+ h->cmd_pool, h->cmd_pool_dhandle);
+ pci_free_consistent(h->pdev, h->nr_cmds * sizeof(ErrorInfo_struct),
+ h->errinfo_pool, h->errinfo_pool_dhandle);
+ kfree(h->cmd_pool_bits);
/* Free up sg elements */
- for (j = 0; j < hba[i]->nr_cmds; j++)
- kfree(hba[i]->scatter_list[j]);
- kfree(hba[i]->scatter_list);
- cciss_free_sg_chain_blocks(hba[i]->cmd_sg_list, hba[i]->nr_cmds);
+ for (j = 0; j < h->nr_cmds; j++)
+ kfree(h->scatter_list[j]);
+ kfree(h->scatter_list);
+ cciss_free_sg_chain_blocks(h->cmd_sg_list, h->nr_cmds);
/*
* Deliberately omit pci_disable_device(): it does something nasty to
* Smart Array controllers that pci_enable_device does not undo
*/
pci_release_regions(pdev);
pci_set_drvdata(pdev, NULL);
- cciss_destroy_hba_sysfs_entry(hba[i]);
- mutex_unlock(&hba[i]->busy_shutting_down);
- free_hba(i);
+ cciss_destroy_hba_sysfs_entry(h);
+ mutex_unlock(&h->busy_shutting_down);
+ free_hba(h);
}
static struct pci_driver cciss_pci_driver = {
* array of them, the size must be a multiple of 8 bytes.
*/
BUILD_BUG_ON(sizeof(CommandList_struct) % COMMANDLIST_ALIGNMENT);
-
printk(KERN_INFO DRIVER_NAME "\n");
err = bus_register(&cciss_bus_type);
/* double check that all controller entrys have been removed */
for (i = 0; i < MAX_CTLR; i++) {
if (hba[i] != NULL) {
- printk(KERN_WARNING "cciss: had to remove"
- " controller %d\n", i);
+ dev_warn(&hba[i]->pdev->dev,
+ "had to remove controller\n");
cciss_remove_one(hba[i]->pdev);
}
}
bus_unregister(&cciss_bus_type);
}
-static void fail_all_cmds(unsigned long ctlr)
-{
- /* If we get here, the board is apparently dead. */
- ctlr_info_t *h = hba[ctlr];
- CommandList_struct *c;
- unsigned long flags;
-
- printk(KERN_WARNING "cciss%d: controller not responding.\n", h->ctlr);
- h->alive = 0; /* the controller apparently died... */
-
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
-
- pci_disable_device(h->pdev); /* Make sure it is really dead. */
-
- /* move everything off the request queue onto the completed queue */
- while (!hlist_empty(&h->reqQ)) {
- c = hlist_entry(h->reqQ.first, CommandList_struct, list);
- removeQ(c);
- h->Qdepth--;
- addQ(&h->cmpQ, c);
- }
-
- /* Now, fail everything on the completed queue with a HW error */
- while (!hlist_empty(&h->cmpQ)) {
- c = hlist_entry(h->cmpQ.first, CommandList_struct, list);
- removeQ(c);
- if (c->cmd_type != CMD_MSG_STALE)
- c->err_info->CommandStatus = CMD_HARDWARE_ERR;
- if (c->cmd_type == CMD_RWREQ) {
- complete_command(h, c, 0);
- } else if (c->cmd_type == CMD_IOCTL_PEND)
- complete(c->waiting);
-#ifdef CONFIG_CISS_SCSI_TAPE
- else if (c->cmd_type == CMD_SCSI)
- complete_scsi_command(c, 0, 0);
-#endif
- }
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
- return;
-}
-
module_init(cciss_init);
module_exit(cciss_cleanup);
void (*submit_command)(ctlr_info_t *h, CommandList_struct *c);
void (*set_intr_mask)(ctlr_info_t *h, unsigned long val);
unsigned long (*fifo_full)(ctlr_info_t *h);
- unsigned long (*intr_pending)(ctlr_info_t *h);
+ bool (*intr_pending)(ctlr_info_t *h);
unsigned long (*command_completed)(ctlr_info_t *h);
};
typedef struct _drive_info_struct
int max_cmd_sgentries;
SGDescriptor_struct **cmd_sg_list;
-# define DOORBELL_INT 0
-# define PERF_MODE_INT 1
+# define PERF_MODE_INT 0
+# define DOORBELL_INT 1
# define SIMPLE_MODE_INT 2
# define MEMQ_MODE_INT 3
unsigned int intr[4];
struct list_head scan_list;
struct completion scan_wait;
struct device dev;
+ /*
+ * Performant mode tables.
+ */
+ u32 trans_support;
+ u32 trans_offset;
+ struct TransTable_struct *transtable;
+ unsigned long transMethod;
+
+ /*
+ * Performant mode completion buffer
+ */
+ u64 *reply_pool;
+ dma_addr_t reply_pool_dhandle;
+ u64 *reply_pool_head;
+ size_t reply_pool_size;
+ unsigned char reply_pool_wraparound;
+ u32 *blockFetchTable;
};
-/* Defining the diffent access_menthods */
-/*
+/* Defining the diffent access_methods
+ *
* Memory mapped FIFO interface (SMART 53xx cards)
*/
#define SA5_DOORBELL 0x20
#define SA5B_INTR_PENDING 0x04
#define FIFO_EMPTY 0xffffffff
#define CCISS_FIRMWARE_READY 0xffff0000 /* value in scratchpad register */
+/* Perf. mode flags */
+#define SA5_PERF_INTR_PENDING 0x04
+#define SA5_PERF_INTR_OFF 0x05
+#define SA5_OUTDB_STATUS_PERF_BIT 0x01
+#define SA5_OUTDB_CLEAR_PERF_BIT 0x01
+#define SA5_OUTDB_CLEAR 0xA0
+#define SA5_OUTDB_CLEAR_PERF_BIT 0x01
+#define SA5_OUTDB_STATUS 0x9C
+
#define CISS_ERROR_BIT 0x02
#define CCISS_INTR_ON 1
#define CCISS_INTR_OFF 0
+
+
+/* CCISS_BOARD_READY_WAIT_SECS is how long to wait for a board
+ * to become ready, in seconds, before giving up on it.
+ * CCISS_BOARD_READY_POLL_INTERVAL_MSECS * is how long to wait
+ * between polling the board to see if it is ready, in
+ * milliseconds. CCISS_BOARD_READY_ITERATIONS is derived
+ * the above.
+ */
+#define CCISS_BOARD_READY_WAIT_SECS (120)
+#define CCISS_BOARD_READY_POLL_INTERVAL_MSECS (100)
+#define CCISS_BOARD_READY_ITERATIONS \
+ ((CCISS_BOARD_READY_WAIT_SECS * 1000) / \
+ CCISS_BOARD_READY_POLL_INTERVAL_MSECS)
+#define CCISS_POST_RESET_PAUSE_MSECS (3000)
+#define CCISS_POST_RESET_NOOP_INTERVAL_MSECS (1000)
+#define CCISS_POST_RESET_NOOP_RETRIES (12)
+
/*
Send the command to the hardware
*/
static void SA5_submit_command( ctlr_info_t *h, CommandList_struct *c)
{
#ifdef CCISS_DEBUG
- printk("Sending %x - down to controller\n", c->busaddr );
-#endif /* CCISS_DEBUG */
+ printk(KERN_WARNING "cciss%d: Sending %08x - down to controller\n",
+ h->ctlr, c->busaddr);
+#endif /* CCISS_DEBUG */
writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
h->commands_outstanding++;
if ( h->commands_outstanding > h->max_outstanding)
h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
}
}
+
+/* Performant mode intr_mask */
+static void SA5_performant_intr_mask(ctlr_info_t *h, unsigned long val)
+{
+ if (val) { /* turn on interrupts */
+ h->interrupts_enabled = 1;
+ writel(0, h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
+ } else {
+ h->interrupts_enabled = 0;
+ writel(SA5_PERF_INTR_OFF,
+ h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
+ }
+}
+
/*
* Returns true if fifo is full.
*
return ( register_value);
}
+
+/* Performant mode command completed */
+static unsigned long SA5_performant_completed(ctlr_info_t *h)
+{
+ unsigned long register_value = FIFO_EMPTY;
+
+ /* flush the controller write of the reply queue by reading
+ * outbound doorbell status register.
+ */
+ register_value = readl(h->vaddr + SA5_OUTDB_STATUS);
+ /* msi auto clears the interrupt pending bit. */
+ if (!(h->msi_vector || h->msix_vector)) {
+ writel(SA5_OUTDB_CLEAR_PERF_BIT, h->vaddr + SA5_OUTDB_CLEAR);
+ /* Do a read in order to flush the write to the controller
+ * (as per spec.)
+ */
+ register_value = readl(h->vaddr + SA5_OUTDB_STATUS);
+ }
+
+ if ((*(h->reply_pool_head) & 1) == (h->reply_pool_wraparound)) {
+ register_value = *(h->reply_pool_head);
+ (h->reply_pool_head)++;
+ h->commands_outstanding--;
+ } else {
+ register_value = FIFO_EMPTY;
+ }
+ /* Check for wraparound */
+ if (h->reply_pool_head == (h->reply_pool + h->max_commands)) {
+ h->reply_pool_head = h->reply_pool;
+ h->reply_pool_wraparound ^= 1;
+ }
+
+ return register_value;
+}
/*
* Returns true if an interrupt is pending..
*/
-static unsigned long SA5_intr_pending(ctlr_info_t *h)
+static bool SA5_intr_pending(ctlr_info_t *h)
{
unsigned long register_value =
readl(h->vaddr + SA5_INTR_STATUS);
/*
* Returns true if an interrupt is pending..
*/
-static unsigned long SA5B_intr_pending(ctlr_info_t *h)
+static bool SA5B_intr_pending(ctlr_info_t *h)
{
unsigned long register_value =
readl(h->vaddr + SA5_INTR_STATUS);
return 0 ;
}
+static bool SA5_performant_intr_pending(ctlr_info_t *h)
+{
+ unsigned long register_value = readl(h->vaddr + SA5_INTR_STATUS);
+
+ if (!register_value)
+ return false;
+
+ if (h->msi_vector || h->msix_vector)
+ return true;
+
+ /* Read outbound doorbell to flush */
+ register_value = readl(h->vaddr + SA5_OUTDB_STATUS);
+ return register_value & SA5_OUTDB_STATUS_PERF_BIT;
+}
static struct access_method SA5_access = {
SA5_submit_command,
SA5_completed,
};
+static struct access_method SA5_performant_access = {
+ SA5_submit_command,
+ SA5_performant_intr_mask,
+ SA5_fifo_full,
+ SA5_performant_intr_pending,
+ SA5_performant_completed,
+};
+
struct board_type {
__u32 board_id;
char *product_name;
int nr_cmds; /* Max cmds this kind of ctlr can handle. */
};
-#define CCISS_LOCK(i) (&hba[i]->lock)
-
#endif /* CCISS_H */
/* Configuration Table */
#define CFGTBL_ChangeReq 0x00000001l
#define CFGTBL_AccCmds 0x00000001l
+#define DOORBELL_CTLR_RESET 0x00000004l
#define CFGTBL_Trans_Simple 0x00000002l
+#define CFGTBL_Trans_Performant 0x00000004l
#define CFGTBL_BusType_Ultra2 0x00000001l
#define CFGTBL_BusType_Ultra3 0x00000002l
* PAD_64 can be adjusted independently as needed for 32-bit
* and 64-bits systems.
*/
-#define COMMANDLIST_ALIGNMENT (8)
+#define COMMANDLIST_ALIGNMENT (32)
#define IS_64_BIT ((sizeof(long) - 4)/4)
#define IS_32_BIT (!IS_64_BIT)
#define PAD_32 (0)
#define PAD_64 (4)
#define PADSIZE (IS_32_BIT * PAD_32 + IS_64_BIT * PAD_64)
+#define DIRECT_LOOKUP_BIT 0x10
+#define DIRECT_LOOKUP_SHIFT 5
+
typedef struct _CommandList_struct {
CommandListHeader_struct Header;
RequestBlock_struct Request;
struct completion *waiting;
int retry_count;
void * scsi_cmd;
- char pad[PADSIZE];
+ char pad[PADSIZE];
} CommandList_struct;
/* Configuration Table Structure */
typedef struct _CfgTable_struct {
BYTE Signature[4];
DWORD SpecValence;
+#define SIMPLE_MODE 0x02
+#define PERFORMANT_MODE 0x04
+#define MEMQ_MODE 0x08
DWORD TransportSupport;
DWORD TransportActive;
HostWrite_struct HostWrite;
DWORD CmdsOutMax;
DWORD BusTypes;
- DWORD Reserved;
+ DWORD TransMethodOffset;
BYTE ServerName[16];
DWORD HeartBeat;
DWORD SCSI_Prefetch;
DWORD MaxLogicalUnits;
DWORD MaxPhysicalDrives;
DWORD MaxPhysicalDrivesPerLogicalUnit;
+ DWORD MaxPerformantModeCommands;
+ u8 reserved[0x78 - 0x58];
+ u32 misc_fw_support; /* offset 0x78 */
+#define MISC_FW_DOORBELL_RESET (0x02)
} CfgTable_struct;
+
+struct TransTable_struct {
+ u32 BlockFetch0;
+ u32 BlockFetch1;
+ u32 BlockFetch2;
+ u32 BlockFetch3;
+ u32 BlockFetch4;
+ u32 BlockFetch5;
+ u32 BlockFetch6;
+ u32 BlockFetch7;
+ u32 RepQSize;
+ u32 RepQCount;
+ u32 RepQCtrAddrLow32;
+ u32 RepQCtrAddrHigh32;
+ u32 RepQAddr0Low32;
+ u32 RepQAddr0High32;
+};
+
#pragma pack()
#endif /* CCISS_CMD_H */
#define CCISS_ABORT_MSG 0x00
#define CCISS_RESET_MSG 0x01
-static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff,
+static int fill_cmd(ctlr_info_t *h, CommandList_struct *c, __u8 cmd, void *buff,
size_t size,
__u8 page_code, unsigned char *scsi3addr,
int cmd_type);
-static CommandList_struct *cmd_alloc(ctlr_info_t *h, int get_from_pool);
-static void cmd_free(ctlr_info_t *h, CommandList_struct *c, int got_from_pool);
+static CommandList_struct *cmd_alloc(ctlr_info_t *h);
+static CommandList_struct *cmd_special_alloc(ctlr_info_t *h);
+static void cmd_free(ctlr_info_t *h, CommandList_struct *c);
+static void cmd_special_free(ctlr_info_t *h, CommandList_struct *c);
static int cciss_scsi_proc_info(
struct Scsi_Host *sh,
#pragma pack(1)
-#define SCSI_PAD_32 0
-#define SCSI_PAD_64 0
+#define SCSI_PAD_32 8
+#define SCSI_PAD_64 8
struct cciss_scsi_cmd_stack_elem_t {
CommandList_struct cmd;
spinlock_t lock; // to protect ccissscsi[ctlr];
};
-#define CPQ_TAPE_LOCK(ctlr, flags) spin_lock_irqsave( \
- &hba[ctlr]->scsi_ctlr->lock, flags);
-#define CPQ_TAPE_UNLOCK(ctlr, flags) spin_unlock_irqrestore( \
- &hba[ctlr]->scsi_ctlr->lock, flags);
+#define CPQ_TAPE_LOCK(h, flags) spin_lock_irqsave( \
+ &h->scsi_ctlr->lock, flags);
+#define CPQ_TAPE_UNLOCK(h, flags) spin_unlock_irqrestore( \
+ &h->scsi_ctlr->lock, flags);
static CommandList_struct *
scsi_cmd_alloc(ctlr_info_t *h)
{
/* assume only one process in here at a time, locking done by caller. */
- /* use CCISS_LOCK(ctlr) */
+ /* use h->lock */
/* might be better to rewrite how we allocate scsi commands in a way that */
/* needs no locking at all. */
}
static void
-scsi_cmd_free(ctlr_info_t *h, CommandList_struct *cmd)
+scsi_cmd_free(ctlr_info_t *h, CommandList_struct *c)
{
/* assume only one process in here at a time, locking done by caller. */
- /* use CCISS_LOCK(ctlr) */
+ /* use h->lock */
/* drop the free memory chunk on top of the stack. */
struct cciss_scsi_adapter_data_t *sa;
stk = &sa->cmd_stack;
stk->top++;
if (stk->top >= CMD_STACK_SIZE) {
- printk("cciss: scsi_cmd_free called too many times.\n");
+ dev_err(&h->pdev->dev,
+ "scsi_cmd_free called too many times.\n");
BUG();
}
- stk->elem[stk->top] = (struct cciss_scsi_cmd_stack_elem_t *) cmd;
+ stk->elem[stk->top] = (struct cciss_scsi_cmd_stack_elem_t *) c;
}
static int
-scsi_cmd_stack_setup(int ctlr, struct cciss_scsi_adapter_data_t *sa)
+scsi_cmd_stack_setup(ctlr_info_t *h, struct cciss_scsi_adapter_data_t *sa)
{
int i;
struct cciss_scsi_cmd_stack_t *stk;
size_t size;
- sa->cmd_sg_list = cciss_allocate_sg_chain_blocks(hba[ctlr],
- hba[ctlr]->chainsize, CMD_STACK_SIZE);
- if (!sa->cmd_sg_list && hba[ctlr]->chainsize > 0)
+ sa->cmd_sg_list = cciss_allocate_sg_chain_blocks(h,
+ h->chainsize, CMD_STACK_SIZE);
+ if (!sa->cmd_sg_list && h->chainsize > 0)
return -ENOMEM;
stk = &sa->cmd_stack;
BUILD_BUG_ON((sizeof(*stk->pool) % COMMANDLIST_ALIGNMENT) != 0);
/* pci_alloc_consistent guarantees 32-bit DMA address will be used */
stk->pool = (struct cciss_scsi_cmd_stack_elem_t *)
- pci_alloc_consistent(hba[ctlr]->pdev, size, &stk->cmd_pool_handle);
+ pci_alloc_consistent(h->pdev, size, &stk->cmd_pool_handle);
if (stk->pool == NULL) {
cciss_free_sg_chain_blocks(sa->cmd_sg_list, CMD_STACK_SIZE);
}
static void
-scsi_cmd_stack_free(int ctlr)
+scsi_cmd_stack_free(ctlr_info_t *h)
{
struct cciss_scsi_adapter_data_t *sa;
struct cciss_scsi_cmd_stack_t *stk;
size_t size;
- sa = hba[ctlr]->scsi_ctlr;
+ sa = h->scsi_ctlr;
stk = &sa->cmd_stack;
if (stk->top != CMD_STACK_SIZE-1) {
- printk( "cciss: %d scsi commands are still outstanding.\n",
+ dev_warn(&h->pdev->dev,
+ "bug: %d scsi commands are still outstanding.\n",
CMD_STACK_SIZE - stk->top);
- // BUG();
- printk("WE HAVE A BUG HERE!!! stk=0x%p\n", stk);
}
size = sizeof(struct cciss_scsi_cmd_stack_elem_t) * CMD_STACK_SIZE;
- pci_free_consistent(hba[ctlr]->pdev, size, stk->pool, stk->cmd_pool_handle);
+ pci_free_consistent(h->pdev, size, stk->pool, stk->cmd_pool_handle);
stk->pool = NULL;
cciss_free_sg_chain_blocks(sa->cmd_sg_list, CMD_STACK_SIZE);
}
#endif
static int
-find_bus_target_lun(int ctlr, int *bus, int *target, int *lun)
+find_bus_target_lun(ctlr_info_t *h, int *bus, int *target, int *lun)
{
/* finds an unused bus, target, lun for a new device */
- /* assumes hba[ctlr]->scsi_ctlr->lock is held */
+ /* assumes h->scsi_ctlr->lock is held */
int i, found=0;
unsigned char target_taken[CCISS_MAX_SCSI_DEVS_PER_HBA];
memset(&target_taken[0], 0, CCISS_MAX_SCSI_DEVS_PER_HBA);
target_taken[SELF_SCSI_ID] = 1;
- for (i=0;i<ccissscsi[ctlr].ndevices;i++)
- target_taken[ccissscsi[ctlr].dev[i].target] = 1;
+ for (i = 0; i < ccissscsi[h->ctlr].ndevices; i++)
+ target_taken[ccissscsi[h->ctlr].dev[i].target] = 1;
- for (i=0;i<CCISS_MAX_SCSI_DEVS_PER_HBA;i++) {
+ for (i = 0; i < CCISS_MAX_SCSI_DEVS_PER_HBA; i++) {
if (!target_taken[i]) {
*bus = 0; *target=i; *lun = 0; found=1;
break;
};
static int
-cciss_scsi_add_entry(int ctlr, int hostno,
+cciss_scsi_add_entry(ctlr_info_t *h, int hostno,
struct cciss_scsi_dev_t *device,
struct scsi2map *added, int *nadded)
{
- /* assumes hba[ctlr]->scsi_ctlr->lock is held */
- int n = ccissscsi[ctlr].ndevices;
+ /* assumes h->scsi_ctlr->lock is held */
+ int n = ccissscsi[h->ctlr].ndevices;
struct cciss_scsi_dev_t *sd;
int i, bus, target, lun;
unsigned char addr1[8], addr2[8];
if (n >= CCISS_MAX_SCSI_DEVS_PER_HBA) {
- printk("cciss%d: Too many devices, "
- "some will be inaccessible.\n", ctlr);
+ dev_warn(&h->pdev->dev, "Too many devices, "
+ "some will be inaccessible.\n");
return -1;
}
memcpy(addr1, device->scsi3addr, 8);
addr1[4] = 0;
for (i = 0; i < n; i++) {
- sd = &ccissscsi[ctlr].dev[i];
+ sd = &ccissscsi[h->ctlr].dev[i];
memcpy(addr2, sd->scsi3addr, 8);
addr2[4] = 0;
/* differ only in byte 4? */
}
}
- sd = &ccissscsi[ctlr].dev[n];
+ sd = &ccissscsi[h->ctlr].dev[n];
if (lun == 0) {
- if (find_bus_target_lun(ctlr,
+ if (find_bus_target_lun(h,
&sd->bus, &sd->target, &sd->lun) != 0)
return -1;
} else {
memcpy(sd->device_id, device->device_id, sizeof(sd->device_id));
sd->devtype = device->devtype;
- ccissscsi[ctlr].ndevices++;
+ ccissscsi[h->ctlr].ndevices++;
/* initially, (before registering with scsi layer) we don't
know our hostno and we don't want to print anything first
time anyway (the scsi layer's inquiries will show that info) */
if (hostno != -1)
- printk("cciss%d: %s device c%db%dt%dl%d added.\n",
- ctlr, scsi_device_type(sd->devtype), hostno,
+ dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d added.\n",
+ scsi_device_type(sd->devtype), hostno,
sd->bus, sd->target, sd->lun);
return 0;
}
static void
-cciss_scsi_remove_entry(int ctlr, int hostno, int entry,
+cciss_scsi_remove_entry(ctlr_info_t *h, int hostno, int entry,
struct scsi2map *removed, int *nremoved)
{
- /* assumes hba[ctlr]->scsi_ctlr->lock is held */
+ /* assumes h->ctlr]->scsi_ctlr->lock is held */
int i;
struct cciss_scsi_dev_t sd;
if (entry < 0 || entry >= CCISS_MAX_SCSI_DEVS_PER_HBA) return;
- sd = ccissscsi[ctlr].dev[entry];
+ sd = ccissscsi[h->ctlr].dev[entry];
removed[*nremoved].bus = sd.bus;
removed[*nremoved].target = sd.target;
removed[*nremoved].lun = sd.lun;
(*nremoved)++;
- for (i=entry;i<ccissscsi[ctlr].ndevices-1;i++)
- ccissscsi[ctlr].dev[i] = ccissscsi[ctlr].dev[i+1];
- ccissscsi[ctlr].ndevices--;
- printk("cciss%d: %s device c%db%dt%dl%d removed.\n",
- ctlr, scsi_device_type(sd.devtype), hostno,
+ for (i = entry; i < ccissscsi[h->ctlr].ndevices-1; i++)
+ ccissscsi[h->ctlr].dev[i] = ccissscsi[h->ctlr].dev[i+1];
+ ccissscsi[h->ctlr].ndevices--;
+ dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d removed.\n",
+ scsi_device_type(sd.devtype), hostno,
sd.bus, sd.target, sd.lun);
}
(a)[1] == (b)[1] && \
(a)[0] == (b)[0])
-static void fixup_botched_add(int ctlr, char *scsi3addr)
+static void fixup_botched_add(ctlr_info_t *h, char *scsi3addr)
{
/* called when scsi_add_device fails in order to re-adjust */
/* ccissscsi[] to match the mid layer's view. */
unsigned long flags;
int i, j;
- CPQ_TAPE_LOCK(ctlr, flags);
- for (i = 0; i < ccissscsi[ctlr].ndevices; i++) {
+ CPQ_TAPE_LOCK(h, flags);
+ for (i = 0; i < ccissscsi[h->ctlr].ndevices; i++) {
if (memcmp(scsi3addr,
- ccissscsi[ctlr].dev[i].scsi3addr, 8) == 0) {
- for (j = i; j < ccissscsi[ctlr].ndevices-1; j++)
- ccissscsi[ctlr].dev[j] =
- ccissscsi[ctlr].dev[j+1];
- ccissscsi[ctlr].ndevices--;
+ ccissscsi[h->ctlr].dev[i].scsi3addr, 8) == 0) {
+ for (j = i; j < ccissscsi[h->ctlr].ndevices-1; j++)
+ ccissscsi[h->ctlr].dev[j] =
+ ccissscsi[h->ctlr].dev[j+1];
+ ccissscsi[h->ctlr].ndevices--;
break;
}
}
- CPQ_TAPE_UNLOCK(ctlr, flags);
+ CPQ_TAPE_UNLOCK(h, flags);
}
static int device_is_the_same(struct cciss_scsi_dev_t *dev1,
}
static int
-adjust_cciss_scsi_table(int ctlr, int hostno,
+adjust_cciss_scsi_table(ctlr_info_t *h, int hostno,
struct cciss_scsi_dev_t sd[], int nsds)
{
/* sd contains scsi3 addresses and devtypes, but
GFP_KERNEL);
if (!added || !removed) {
- printk(KERN_WARNING "cciss%d: Out of memory in "
- "adjust_cciss_scsi_table\n", ctlr);
+ dev_warn(&h->pdev->dev,
+ "Out of memory in adjust_cciss_scsi_table\n");
goto free_and_out;
}
- CPQ_TAPE_LOCK(ctlr, flags);
+ CPQ_TAPE_LOCK(h, flags);
if (hostno != -1) /* if it's not the first time... */
- sh = hba[ctlr]->scsi_ctlr->scsi_host;
+ sh = h->scsi_ctlr->scsi_host;
/* find any devices in ccissscsi[] that are not in
sd[] and remove them from ccissscsi[] */
i = 0;
nremoved = 0;
nadded = 0;
- while(i<ccissscsi[ctlr].ndevices) {
- csd = &ccissscsi[ctlr].dev[i];
+ while (i < ccissscsi[h->ctlr].ndevices) {
+ csd = &ccissscsi[h->ctlr].dev[i];
found=0;
for (j=0;j<nsds;j++) {
if (SCSI3ADDR_EQ(sd[j].scsi3addr,
if (found == 0) { /* device no longer present. */
changes++;
- /* printk("cciss%d: %s device c%db%dt%dl%d removed.\n",
- ctlr, scsi_device_type(csd->devtype), hostno,
- csd->bus, csd->target, csd->lun); */
- cciss_scsi_remove_entry(ctlr, hostno, i,
+ cciss_scsi_remove_entry(h, hostno, i,
removed, &nremoved);
/* remove ^^^, hence i not incremented */
} else if (found == 1) { /* device is different in some way */
changes++;
- printk("cciss%d: device c%db%dt%dl%d has changed.\n",
- ctlr, hostno, csd->bus, csd->target, csd->lun);
- cciss_scsi_remove_entry(ctlr, hostno, i,
+ dev_info(&h->pdev->dev,
+ "device c%db%dt%dl%d has changed.\n",
+ hostno, csd->bus, csd->target, csd->lun);
+ cciss_scsi_remove_entry(h, hostno, i,
removed, &nremoved);
/* remove ^^^, hence i not incremented */
- if (cciss_scsi_add_entry(ctlr, hostno, &sd[j],
+ if (cciss_scsi_add_entry(h, hostno, &sd[j],
added, &nadded) != 0)
/* we just removed one, so add can't fail. */
BUG();
for (i=0;i<nsds;i++) {
found=0;
- for (j=0;j<ccissscsi[ctlr].ndevices;j++) {
- csd = &ccissscsi[ctlr].dev[j];
+ for (j = 0; j < ccissscsi[h->ctlr].ndevices; j++) {
+ csd = &ccissscsi[h->ctlr].dev[j];
if (SCSI3ADDR_EQ(sd[i].scsi3addr,
csd->scsi3addr)) {
if (device_is_the_same(&sd[i], csd))
}
if (!found) {
changes++;
- if (cciss_scsi_add_entry(ctlr, hostno, &sd[i],
+ if (cciss_scsi_add_entry(h, hostno, &sd[i],
added, &nadded) != 0)
break;
} else if (found == 1) {
/* should never happen... */
changes++;
- printk(KERN_WARNING "cciss%d: device "
- "unexpectedly changed\n", ctlr);
+ dev_warn(&h->pdev->dev,
+ "device unexpectedly changed\n");
/* but if it does happen, we just ignore that device */
}
}
- CPQ_TAPE_UNLOCK(ctlr, flags);
+ CPQ_TAPE_UNLOCK(h, flags);
/* Don't notify scsi mid layer of any changes the first time through */
/* (or if there are no changes) scsi_scan_host will do it later the */
/* We don't expect to get here. */
/* future cmds to this device will get selection */
/* timeout as if the device was gone. */
- printk(KERN_WARNING "cciss%d: didn't find "
+ dev_warn(&h->pdev->dev, "didn't find "
"c%db%dt%dl%d\n for removal.",
- ctlr, hostno, removed[i].bus,
+ hostno, removed[i].bus,
removed[i].target, removed[i].lun);
}
}
added[i].target, added[i].lun);
if (rc == 0)
continue;
- printk(KERN_WARNING "cciss%d: scsi_add_device "
+ dev_warn(&h->pdev->dev, "scsi_add_device "
"c%db%dt%dl%d failed, device not added.\n",
- ctlr, hostno,
- added[i].bus, added[i].target, added[i].lun);
+ hostno, added[i].bus, added[i].target, added[i].lun);
/* now we have to remove it from ccissscsi, */
/* since it didn't get added to scsi mid layer */
- fixup_botched_add(ctlr, added[i].scsi3addr);
+ fixup_botched_add(h, added[i].scsi3addr);
}
free_and_out:
}
static int
-lookup_scsi3addr(int ctlr, int bus, int target, int lun, char *scsi3addr)
+lookup_scsi3addr(ctlr_info_t *h, int bus, int target, int lun, char *scsi3addr)
{
int i;
struct cciss_scsi_dev_t *sd;
unsigned long flags;
- CPQ_TAPE_LOCK(ctlr, flags);
- for (i=0;i<ccissscsi[ctlr].ndevices;i++) {
- sd = &ccissscsi[ctlr].dev[i];
+ CPQ_TAPE_LOCK(h, flags);
+ for (i = 0; i < ccissscsi[h->ctlr].ndevices; i++) {
+ sd = &ccissscsi[h->ctlr].dev[i];
if (sd->bus == bus &&
sd->target == target &&
sd->lun == lun) {
memcpy(scsi3addr, &sd->scsi3addr[0], 8);
- CPQ_TAPE_UNLOCK(ctlr, flags);
+ CPQ_TAPE_UNLOCK(h, flags);
return 0;
}
}
- CPQ_TAPE_UNLOCK(ctlr, flags);
+ CPQ_TAPE_UNLOCK(h, flags);
return -1;
}
static void
-cciss_scsi_setup(int cntl_num)
+cciss_scsi_setup(ctlr_info_t *h)
{
struct cciss_scsi_adapter_data_t * shba;
- ccissscsi[cntl_num].ndevices = 0;
+ ccissscsi[h->ctlr].ndevices = 0;
shba = (struct cciss_scsi_adapter_data_t *)
kmalloc(sizeof(*shba), GFP_KERNEL);
if (shba == NULL)
shba->scsi_host = NULL;
spin_lock_init(&shba->lock);
shba->registered = 0;
- if (scsi_cmd_stack_setup(cntl_num, shba) != 0) {
+ if (scsi_cmd_stack_setup(h, shba) != 0) {
kfree(shba);
shba = NULL;
}
- hba[cntl_num]->scsi_ctlr = shba;
+ h->scsi_ctlr = shba;
return;
}
-static void
-complete_scsi_command( CommandList_struct *cp, int timeout, __u32 tag)
+static void complete_scsi_command(CommandList_struct *c, int timeout,
+ __u32 tag)
{
struct scsi_cmnd *cmd;
- ctlr_info_t *ctlr;
+ ctlr_info_t *h;
ErrorInfo_struct *ei;
- ei = cp->err_info;
+ ei = c->err_info;
/* First, see if it was a message rather than a command */
- if (cp->Request.Type.Type == TYPE_MSG) {
- cp->cmd_type = CMD_MSG_DONE;
+ if (c->Request.Type.Type == TYPE_MSG) {
+ c->cmd_type = CMD_MSG_DONE;
return;
}
- cmd = (struct scsi_cmnd *) cp->scsi_cmd;
- ctlr = hba[cp->ctlr];
+ cmd = (struct scsi_cmnd *) c->scsi_cmd;
+ h = hba[c->ctlr];
scsi_dma_unmap(cmd);
- if (cp->Header.SGTotal > ctlr->max_cmd_sgentries)
- cciss_unmap_sg_chain_block(ctlr, cp);
+ if (c->Header.SGTotal > h->max_cmd_sgentries)
+ cciss_unmap_sg_chain_block(h, c);
cmd->result = (DID_OK << 16); /* host byte */
cmd->result |= (COMMAND_COMPLETE << 8); /* msg byte */
{
#if 0
printk(KERN_WARNING "cciss: cmd %p "
- "has SCSI Status = %x\n",
- cp,
- ei->ScsiStatus);
+ "has SCSI Status = %x\n",
+ c, ei->ScsiStatus);
#endif
cmd->result |= (ei->ScsiStatus << 1);
}
case CMD_DATA_UNDERRUN: /* let mid layer handle it. */
break;
case CMD_DATA_OVERRUN:
- printk(KERN_WARNING "cciss: cp %p has"
+ dev_warn(&h->pdev->dev, "%p has"
" completed with data overrun "
- "reported\n", cp);
+ "reported\n", c);
break;
case CMD_INVALID: {
- /* print_bytes(cp, sizeof(*cp), 1, 0);
- print_cmd(cp); */
+ /* print_bytes(c, sizeof(*c), 1, 0);
+ print_cmd(c); */
/* We get CMD_INVALID if you address a non-existent tape drive instead
of a selection timeout (no response). You will see this if you yank
out a tape drive, then try to access it. This is kind of a shame
}
break;
case CMD_PROTOCOL_ERR:
- printk(KERN_WARNING "cciss: cp %p has "
- "protocol error \n", cp);
+ dev_warn(&h->pdev->dev,
+ "%p has protocol error\n", c);
break;
case CMD_HARDWARE_ERR:
cmd->result = DID_ERROR << 16;
- printk(KERN_WARNING "cciss: cp %p had "
- " hardware error\n", cp);
+ dev_warn(&h->pdev->dev,
+ "%p had hardware error\n", c);
break;
case CMD_CONNECTION_LOST:
cmd->result = DID_ERROR << 16;
- printk(KERN_WARNING "cciss: cp %p had "
- "connection lost\n", cp);
+ dev_warn(&h->pdev->dev,
+ "%p had connection lost\n", c);
break;
case CMD_ABORTED:
cmd->result = DID_ABORT << 16;
- printk(KERN_WARNING "cciss: cp %p was "
- "aborted\n", cp);
+ dev_warn(&h->pdev->dev, "%p was aborted\n", c);
break;
case CMD_ABORT_FAILED:
cmd->result = DID_ERROR << 16;
- printk(KERN_WARNING "cciss: cp %p reports "
- "abort failed\n", cp);
+ dev_warn(&h->pdev->dev,
+ "%p reports abort failed\n", c);
break;
case CMD_UNSOLICITED_ABORT:
cmd->result = DID_ABORT << 16;
- printk(KERN_WARNING "cciss: cp %p aborted "
- "do to an unsolicited abort\n", cp);
+ dev_warn(&h->pdev->dev, "%p aborted do to an "
+ "unsolicited abort\n", c);
break;
case CMD_TIMEOUT:
cmd->result = DID_TIME_OUT << 16;
- printk(KERN_WARNING "cciss: cp %p timedout\n",
- cp);
+ dev_warn(&h->pdev->dev, "%p timedout\n", c);
break;
default:
cmd->result = DID_ERROR << 16;
- printk(KERN_WARNING "cciss: cp %p returned "
- "unknown status %x\n", cp,
+ dev_warn(&h->pdev->dev,
+ "%p returned unknown status %x\n", c,
ei->CommandStatus);
}
}
- // printk("c:%p:c%db%dt%dl%d ", cmd, ctlr->ctlr, cmd->channel,
- // cmd->target, cmd->lun);
cmd->scsi_done(cmd);
- scsi_cmd_free(ctlr, cp);
+ scsi_cmd_free(h, c);
}
static int
-cciss_scsi_detect(int ctlr)
+cciss_scsi_detect(ctlr_info_t *h)
{
struct Scsi_Host *sh;
int error;
sh->io_port = 0; // good enough? FIXME,
sh->n_io_port = 0; // I don't think we use these two...
sh->this_id = SELF_SCSI_ID;
- sh->sg_tablesize = hba[ctlr]->maxsgentries;
+ sh->sg_tablesize = h->maxsgentries;
sh->max_cmd_len = MAX_COMMAND_SIZE;
((struct cciss_scsi_adapter_data_t *)
- hba[ctlr]->scsi_ctlr)->scsi_host = sh;
- sh->hostdata[0] = (unsigned long) hba[ctlr];
- sh->irq = hba[ctlr]->intr[SIMPLE_MODE_INT];
+ h->scsi_ctlr)->scsi_host = sh;
+ sh->hostdata[0] = (unsigned long) h;
+ sh->irq = h->intr[SIMPLE_MODE_INT];
sh->unique_id = sh->irq;
- error = scsi_add_host(sh, &hba[ctlr]->pdev->dev);
+ error = scsi_add_host(sh, &h->pdev->dev);
if (error)
goto fail_host_put;
scsi_scan_host(sh);
static void
cciss_unmap_one(struct pci_dev *pdev,
- CommandList_struct *cp,
+ CommandList_struct *c,
size_t buflen,
int data_direction)
{
u64bit addr64;
- addr64.val32.lower = cp->SG[0].Addr.lower;
- addr64.val32.upper = cp->SG[0].Addr.upper;
+ addr64.val32.lower = c->SG[0].Addr.lower;
+ addr64.val32.upper = c->SG[0].Addr.upper;
pci_unmap_single(pdev, (dma_addr_t) addr64.val, buflen, data_direction);
}
static void
cciss_map_one(struct pci_dev *pdev,
- CommandList_struct *cp,
+ CommandList_struct *c,
unsigned char *buf,
size_t buflen,
int data_direction)
__u64 addr64;
addr64 = (__u64) pci_map_single(pdev, buf, buflen, data_direction);
- cp->SG[0].Addr.lower =
+ c->SG[0].Addr.lower =
(__u32) (addr64 & (__u64) 0x00000000FFFFFFFF);
- cp->SG[0].Addr.upper =
+ c->SG[0].Addr.upper =
(__u32) ((addr64 >> 32) & (__u64) 0x00000000FFFFFFFF);
- cp->SG[0].Len = buflen;
- cp->Header.SGList = (__u8) 1; /* no. SGs contig in this cmd */
- cp->Header.SGTotal = (__u16) 1; /* total sgs in this cmd list */
+ c->SG[0].Len = buflen;
+ c->Header.SGList = (__u8) 1; /* no. SGs contig in this cmd */
+ c->Header.SGTotal = (__u16) 1; /* total sgs in this cmd list */
}
static int
-cciss_scsi_do_simple_cmd(ctlr_info_t *c,
- CommandList_struct *cp,
+cciss_scsi_do_simple_cmd(ctlr_info_t *h,
+ CommandList_struct *c,
unsigned char *scsi3addr,
unsigned char *cdb,
unsigned char cdblen,
unsigned char *buf, int bufsize,
int direction)
{
- unsigned long flags;
DECLARE_COMPLETION_ONSTACK(wait);
- cp->cmd_type = CMD_IOCTL_PEND; // treat this like an ioctl
- cp->scsi_cmd = NULL;
- cp->Header.ReplyQueue = 0; // unused in simple mode
- memcpy(&cp->Header.LUN, scsi3addr, sizeof(cp->Header.LUN));
- cp->Header.Tag.lower = cp->busaddr; // Use k. address of cmd as tag
+ c->cmd_type = CMD_IOCTL_PEND; /* treat this like an ioctl */
+ c->scsi_cmd = NULL;
+ c->Header.ReplyQueue = 0; /* unused in simple mode */
+ memcpy(&c->Header.LUN, scsi3addr, sizeof(c->Header.LUN));
+ c->Header.Tag.lower = c->busaddr; /* Use k. address of cmd as tag */
// Fill in the request block...
/* printk("Using scsi3addr 0x%02x%0x2%0x2%0x2%0x2%0x2%0x2%0x2\n",
scsi3addr[0], scsi3addr[1], scsi3addr[2], scsi3addr[3],
scsi3addr[4], scsi3addr[5], scsi3addr[6], scsi3addr[7]); */
- memset(cp->Request.CDB, 0, sizeof(cp->Request.CDB));
- memcpy(cp->Request.CDB, cdb, cdblen);
- cp->Request.Timeout = 0;
- cp->Request.CDBLen = cdblen;
- cp->Request.Type.Type = TYPE_CMD;
- cp->Request.Type.Attribute = ATTR_SIMPLE;
- cp->Request.Type.Direction = direction;
+ memset(c->Request.CDB, 0, sizeof(c->Request.CDB));
+ memcpy(c->Request.CDB, cdb, cdblen);
+ c->Request.Timeout = 0;
+ c->Request.CDBLen = cdblen;
+ c->Request.Type.Type = TYPE_CMD;
+ c->Request.Type.Attribute = ATTR_SIMPLE;
+ c->Request.Type.Direction = direction;
/* Fill in the SG list and do dma mapping */
- cciss_map_one(c->pdev, cp, (unsigned char *) buf,
+ cciss_map_one(h->pdev, c, (unsigned char *) buf,
bufsize, DMA_FROM_DEVICE);
- cp->waiting = &wait;
-
- /* Put the request on the tail of the request queue */
- spin_lock_irqsave(CCISS_LOCK(c->ctlr), flags);
- addQ(&c->reqQ, cp);
- c->Qdepth++;
- start_io(c);
- spin_unlock_irqrestore(CCISS_LOCK(c->ctlr), flags);
-
+ c->waiting = &wait;
+ enqueue_cmd_and_start_io(h, c);
wait_for_completion(&wait);
/* undo the dma mapping */
- cciss_unmap_one(c->pdev, cp, bufsize, DMA_FROM_DEVICE);
+ cciss_unmap_one(h->pdev, c, bufsize, DMA_FROM_DEVICE);
return(0);
}
static void
-cciss_scsi_interpret_error(CommandList_struct *cp)
+cciss_scsi_interpret_error(ctlr_info_t *h, CommandList_struct *c)
{
ErrorInfo_struct *ei;
- ei = cp->err_info;
+ ei = c->err_info;
switch(ei->CommandStatus)
{
case CMD_TARGET_STATUS:
- printk(KERN_WARNING "cciss: cmd %p has "
- "completed with errors\n", cp);
- printk(KERN_WARNING "cciss: cmd %p "
- "has SCSI Status = %x\n",
- cp,
- ei->ScsiStatus);
+ dev_warn(&h->pdev->dev,
+ "cmd %p has completed with errors\n", c);
+ dev_warn(&h->pdev->dev,
+ "cmd %p has SCSI Status = %x\n",
+ c, ei->ScsiStatus);
if (ei->ScsiStatus == 0)
- printk(KERN_WARNING
- "cciss:SCSI status is abnormally zero. "
+ dev_warn(&h->pdev->dev,
+ "SCSI status is abnormally zero. "
"(probably indicates selection timeout "
"reported incorrectly due to a known "
"firmware bug, circa July, 2001.)\n");
break;
case CMD_DATA_UNDERRUN: /* let mid layer handle it. */
- printk("UNDERRUN\n");
+ dev_info(&h->pdev->dev, "UNDERRUN\n");
break;
case CMD_DATA_OVERRUN:
- printk(KERN_WARNING "cciss: cp %p has"
+ dev_warn(&h->pdev->dev, "%p has"
" completed with data overrun "
- "reported\n", cp);
+ "reported\n", c);
break;
case CMD_INVALID: {
/* controller unfortunately reports SCSI passthru's */
/* to non-existent targets as invalid commands. */
- printk(KERN_WARNING "cciss: cp %p is "
- "reported invalid (probably means "
- "target device no longer present)\n",
- cp);
- /* print_bytes((unsigned char *) cp, sizeof(*cp), 1, 0);
- print_cmd(cp); */
+ dev_warn(&h->pdev->dev,
+ "%p is reported invalid (probably means "
+ "target device no longer present)\n", c);
+ /* print_bytes((unsigned char *) c, sizeof(*c), 1, 0);
+ print_cmd(c); */
}
break;
case CMD_PROTOCOL_ERR:
- printk(KERN_WARNING "cciss: cp %p has "
- "protocol error \n", cp);
+ dev_warn(&h->pdev->dev, "%p has protocol error\n", c);
break;
case CMD_HARDWARE_ERR:
/* cmd->result = DID_ERROR << 16; */
- printk(KERN_WARNING "cciss: cp %p had "
- " hardware error\n", cp);
+ dev_warn(&h->pdev->dev, "%p had hardware error\n", c);
break;
case CMD_CONNECTION_LOST:
- printk(KERN_WARNING "cciss: cp %p had "
- "connection lost\n", cp);
+ dev_warn(&h->pdev->dev, "%p had connection lost\n", c);
break;
case CMD_ABORTED:
- printk(KERN_WARNING "cciss: cp %p was "
- "aborted\n", cp);
+ dev_warn(&h->pdev->dev, "%p was aborted\n", c);
break;
case CMD_ABORT_FAILED:
- printk(KERN_WARNING "cciss: cp %p reports "
- "abort failed\n", cp);
+ dev_warn(&h->pdev->dev,
+ "%p reports abort failed\n", c);
break;
case CMD_UNSOLICITED_ABORT:
- printk(KERN_WARNING "cciss: cp %p aborted "
- "do to an unsolicited abort\n", cp);
+ dev_warn(&h->pdev->dev,
+ "%p aborted do to an unsolicited abort\n", c);
break;
case CMD_TIMEOUT:
- printk(KERN_WARNING "cciss: cp %p timedout\n",
- cp);
+ dev_warn(&h->pdev->dev, "%p timedout\n", c);
break;
default:
- printk(KERN_WARNING "cciss: cp %p returned "
- "unknown status %x\n", cp,
- ei->CommandStatus);
+ dev_warn(&h->pdev->dev,
+ "%p returned unknown status %x\n",
+ c, ei->CommandStatus);
}
}
static int
-cciss_scsi_do_inquiry(ctlr_info_t *c, unsigned char *scsi3addr,
+cciss_scsi_do_inquiry(ctlr_info_t *h, unsigned char *scsi3addr,
unsigned char page, unsigned char *buf,
unsigned char bufsize)
{
int rc;
- CommandList_struct *cp;
+ CommandList_struct *c;
char cdb[6];
ErrorInfo_struct *ei;
unsigned long flags;
- spin_lock_irqsave(CCISS_LOCK(c->ctlr), flags);
- cp = scsi_cmd_alloc(c);
- spin_unlock_irqrestore(CCISS_LOCK(c->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
+ c = scsi_cmd_alloc(h);
+ spin_unlock_irqrestore(&h->lock, flags);
- if (cp == NULL) { /* trouble... */
+ if (c == NULL) { /* trouble... */
printk("cmd_alloc returned NULL!\n");
return -1;
}
- ei = cp->err_info;
+ ei = c->err_info;
cdb[0] = CISS_INQUIRY;
cdb[1] = (page != 0);
cdb[3] = 0;
cdb[4] = bufsize;
cdb[5] = 0;
- rc = cciss_scsi_do_simple_cmd(c, cp, scsi3addr, cdb,
+ rc = cciss_scsi_do_simple_cmd(h, c, scsi3addr, cdb,
6, buf, bufsize, XFER_READ);
if (rc != 0) return rc; /* something went wrong */
if (ei->CommandStatus != 0 &&
ei->CommandStatus != CMD_DATA_UNDERRUN) {
- cciss_scsi_interpret_error(cp);
+ cciss_scsi_interpret_error(h, c);
rc = -1;
}
- spin_lock_irqsave(CCISS_LOCK(c->ctlr), flags);
- scsi_cmd_free(c, cp);
- spin_unlock_irqrestore(CCISS_LOCK(c->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
+ scsi_cmd_free(h, c);
+ spin_unlock_irqrestore(&h->lock, flags);
return rc;
}
/* Get the device id from inquiry page 0x83 */
-static int cciss_scsi_get_device_id(ctlr_info_t *c, unsigned char *scsi3addr,
+static int cciss_scsi_get_device_id(ctlr_info_t *h, unsigned char *scsi3addr,
unsigned char *device_id, int buflen)
{
int rc;
buf = kzalloc(64, GFP_KERNEL);
if (!buf)
return -1;
- rc = cciss_scsi_do_inquiry(c, scsi3addr, 0x83, buf, 64);
+ rc = cciss_scsi_do_inquiry(h, scsi3addr, 0x83, buf, 64);
if (rc == 0)
memcpy(device_id, &buf[8], buflen);
kfree(buf);
}
static int
-cciss_scsi_do_report_phys_luns(ctlr_info_t *c,
+cciss_scsi_do_report_phys_luns(ctlr_info_t *h,
ReportLunData_struct *buf, int bufsize)
{
int rc;
- CommandList_struct *cp;
+ CommandList_struct *c;
unsigned char cdb[12];
unsigned char scsi3addr[8];
ErrorInfo_struct *ei;
unsigned long flags;
- spin_lock_irqsave(CCISS_LOCK(c->ctlr), flags);
- cp = scsi_cmd_alloc(c);
- spin_unlock_irqrestore(CCISS_LOCK(c->ctlr), flags);
- if (cp == NULL) { /* trouble... */
+ spin_lock_irqsave(&h->lock, flags);
+ c = scsi_cmd_alloc(h);
+ spin_unlock_irqrestore(&h->lock, flags);
+ if (c == NULL) { /* trouble... */
printk("cmd_alloc returned NULL!\n");
return -1;
}
cdb[10] = 0;
cdb[11] = 0;
- rc = cciss_scsi_do_simple_cmd(c, cp, scsi3addr,
+ rc = cciss_scsi_do_simple_cmd(h, c, scsi3addr,
cdb, 12,
(unsigned char *) buf,
bufsize, XFER_READ);
if (rc != 0) return rc; /* something went wrong */
- ei = cp->err_info;
+ ei = c->err_info;
if (ei->CommandStatus != 0 &&
ei->CommandStatus != CMD_DATA_UNDERRUN) {
- cciss_scsi_interpret_error(cp);
+ cciss_scsi_interpret_error(h, c);
rc = -1;
}
- spin_lock_irqsave(CCISS_LOCK(c->ctlr), flags);
- scsi_cmd_free(c, cp);
- spin_unlock_irqrestore(CCISS_LOCK(c->ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
+ scsi_cmd_free(h, c);
+ spin_unlock_irqrestore(&h->lock, flags);
return rc;
}
static void
-cciss_update_non_disk_devices(int cntl_num, int hostno)
+cciss_update_non_disk_devices(ctlr_info_t *h, int hostno)
{
/* the idea here is we could get notified from /proc
that some devices have changed, so we do a report
ReportLunData_struct *ld_buff;
unsigned char *inq_buff;
unsigned char scsi3addr[8];
- ctlr_info_t *c;
__u32 num_luns=0;
unsigned char *ch;
struct cciss_scsi_dev_t *currentsd, *this_device;
int reportlunsize = sizeof(*ld_buff) + CISS_MAX_PHYS_LUN * 8;
int i;
- c = (ctlr_info_t *) hba[cntl_num];
ld_buff = kzalloc(reportlunsize, GFP_KERNEL);
inq_buff = kmalloc(OBDR_TAPE_INQ_SIZE, GFP_KERNEL);
currentsd = kzalloc(sizeof(*currentsd) *
goto out;
}
this_device = ¤tsd[CCISS_MAX_SCSI_DEVS_PER_HBA];
- if (cciss_scsi_do_report_phys_luns(c, ld_buff, reportlunsize) == 0) {
+ if (cciss_scsi_do_report_phys_luns(h, ld_buff, reportlunsize) == 0) {
ch = &ld_buff->LUNListLength[0];
num_luns = ((ch[0]<<24) | (ch[1]<<16) | (ch[2]<<8) | ch[3]) / 8;
if (num_luns > CISS_MAX_PHYS_LUN) {
memset(inq_buff, 0, OBDR_TAPE_INQ_SIZE);
memcpy(&scsi3addr[0], &ld_buff->LUN[i][0], 8);
- if (cciss_scsi_do_inquiry(hba[cntl_num], scsi3addr, 0, inq_buff,
+ if (cciss_scsi_do_inquiry(h, scsi3addr, 0, inq_buff,
(unsigned char) OBDR_TAPE_INQ_SIZE) != 0)
/* Inquiry failed (msg printed already) */
continue; /* so we will skip this device. */
sizeof(this_device->revision));
memset(this_device->device_id, 0,
sizeof(this_device->device_id));
- cciss_scsi_get_device_id(hba[cntl_num], scsi3addr,
+ cciss_scsi_get_device_id(h, scsi3addr,
this_device->device_id, sizeof(this_device->device_id));
switch (this_device->devtype)
case 0x08: /* medium changer */
if (ncurrent >= CCISS_MAX_SCSI_DEVS_PER_HBA) {
printk(KERN_INFO "cciss%d: %s ignored, "
- "too many devices.\n", cntl_num,
+ "too many devices.\n", h->ctlr,
scsi_device_type(this_device->devtype));
break;
}
}
}
- adjust_cciss_scsi_table(cntl_num, hostno, currentsd, ncurrent);
+ adjust_cciss_scsi_table(h, hostno, currentsd, ncurrent);
out:
kfree(inq_buff);
kfree(ld_buff);
}
static int
-cciss_scsi_user_command(int ctlr, int hostno, char *buffer, int length)
+cciss_scsi_user_command(ctlr_info_t *h, int hostno, char *buffer, int length)
{
int arg_len;
if ((arg_len = is_keyword(buffer, length, "rescan")) != 0)
- cciss_update_non_disk_devices(ctlr, hostno);
+ cciss_update_non_disk_devices(h, hostno);
else
return -EINVAL;
return length;
{
int buflen, datalen;
- ctlr_info_t *ci;
+ ctlr_info_t *h;
int i;
- int cntl_num;
-
- ci = (ctlr_info_t *) sh->hostdata[0];
- if (ci == NULL) /* This really shouldn't ever happen. */
+ h = (ctlr_info_t *) sh->hostdata[0];
+ if (h == NULL) /* This really shouldn't ever happen. */
return -EINVAL;
- cntl_num = ci->ctlr; /* Get our index into the hba[] array */
-
if (func == 0) { /* User is reading from /proc/scsi/ciss*?/?* */
buflen = sprintf(buffer, "cciss%d: SCSI host: %d\n",
- cntl_num, sh->host_no);
+ h->ctlr, sh->host_no);
/* this information is needed by apps to know which cciss
device corresponds to which scsi host number without
this info is for an app to be able to use to know how to
get them back in sync. */
- for (i=0;i<ccissscsi[cntl_num].ndevices;i++) {
- struct cciss_scsi_dev_t *sd = &ccissscsi[cntl_num].dev[i];
+ for (i = 0; i < ccissscsi[h->ctlr].ndevices; i++) {
+ struct cciss_scsi_dev_t *sd =
+ &ccissscsi[h->ctlr].dev[i];
buflen += sprintf(&buffer[buflen], "c%db%dt%dl%d %02d "
"0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
sh->host_no, sd->bus, sd->target, sd->lun,
*start = buffer + offset;
return(datalen);
} else /* User is writing to /proc/scsi/cciss*?/?* ... */
- return cciss_scsi_user_command(cntl_num, sh->host_no,
+ return cciss_scsi_user_command(h, sh->host_no,
buffer, length);
}
/* cciss_scatter_gather takes a struct scsi_cmnd, (cmd), and does the pci
dma mapping and fills in the scatter gather entries of the
- cciss command, cp. */
+ cciss command, c. */
-static void cciss_scatter_gather(ctlr_info_t *h, CommandList_struct *cp,
+static void cciss_scatter_gather(ctlr_info_t *h, CommandList_struct *c,
struct scsi_cmnd *cmd)
{
unsigned int len;
chained = 0;
sg_index = 0;
- curr_sg = cp->SG;
+ curr_sg = c->SG;
request_nsgs = scsi_dma_map(cmd);
if (request_nsgs) {
scsi_for_each_sg(cmd, sg, request_nsgs, i) {
!chained && request_nsgs - i > 1) {
chained = 1;
sg_index = 0;
- curr_sg = sa->cmd_sg_list[cp->cmdindex];
+ curr_sg = sa->cmd_sg_list[c->cmdindex];
}
addr64 = (__u64) sg_dma_address(sg);
len = sg_dma_len(sg);
++sg_index;
}
if (chained)
- cciss_map_sg_chain_block(h, cp,
- sa->cmd_sg_list[cp->cmdindex],
+ cciss_map_sg_chain_block(h, c,
+ sa->cmd_sg_list[c->cmdindex],
(request_nsgs - (h->max_cmd_sgentries - 1)) *
sizeof(SGDescriptor_struct));
}
/* track how many SG entries we are using */
if (request_nsgs > h->maxSG)
h->maxSG = request_nsgs;
- cp->Header.SGTotal = (__u8) request_nsgs + chained;
+ c->Header.SGTotal = (__u8) request_nsgs + chained;
if (request_nsgs > h->max_cmd_sgentries)
- cp->Header.SGList = h->max_cmd_sgentries;
+ c->Header.SGList = h->max_cmd_sgentries;
else
- cp->Header.SGList = cp->Header.SGTotal;
+ c->Header.SGList = c->Header.SGTotal;
return;
}
static int
cciss_scsi_queue_command (struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *))
{
- ctlr_info_t *c;
- int ctlr, rc;
+ ctlr_info_t *h;
+ int rc;
unsigned char scsi3addr[8];
- CommandList_struct *cp;
+ CommandList_struct *c;
unsigned long flags;
// Get the ptr to our adapter structure (hba[i]) out of cmd->host.
// We violate cmd->host privacy here. (Is there another way?)
- c = (ctlr_info_t *) cmd->device->host->hostdata[0];
- ctlr = c->ctlr;
+ h = (ctlr_info_t *) cmd->device->host->hostdata[0];
- rc = lookup_scsi3addr(ctlr, cmd->device->channel, cmd->device->id,
+ rc = lookup_scsi3addr(h, cmd->device->channel, cmd->device->id,
cmd->device->lun, scsi3addr);
if (rc != 0) {
/* the scsi nexus does not match any that we presented... */
return 0;
}
- /* printk("cciss_queue_command, p=%p, cmd=0x%02x, c%db%dt%dl%d\n",
- cmd, cmd->cmnd[0], ctlr, cmd->channel, cmd->target, cmd->lun);*/
- // printk("q:%p:c%db%dt%dl%d ", cmd, ctlr, cmd->channel,
- // cmd->target, cmd->lun);
-
/* Ok, we have a reasonable scsi nexus, so send the cmd down, and
see what the device thinks of it. */
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
- cp = scsi_cmd_alloc(c);
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
- if (cp == NULL) { /* trouble... */
- printk("scsi_cmd_alloc returned NULL!\n");
+ spin_lock_irqsave(&h->lock, flags);
+ c = scsi_cmd_alloc(h);
+ spin_unlock_irqrestore(&h->lock, flags);
+ if (c == NULL) { /* trouble... */
+ dev_warn(&h->pdev->dev, "scsi_cmd_alloc returned NULL!\n");
/* FIXME: next 3 lines are -> BAD! <- */
cmd->result = DID_NO_CONNECT << 16;
done(cmd);
cmd->scsi_done = done; // save this for use by completion code
- // save cp in case we have to abort it
- cmd->host_scribble = (unsigned char *) cp;
+ /* save c in case we have to abort it */
+ cmd->host_scribble = (unsigned char *) c;
- cp->cmd_type = CMD_SCSI;
- cp->scsi_cmd = cmd;
- cp->Header.ReplyQueue = 0; // unused in simple mode
- memcpy(&cp->Header.LUN.LunAddrBytes[0], &scsi3addr[0], 8);
- cp->Header.Tag.lower = cp->busaddr; // Use k. address of cmd as tag
+ c->cmd_type = CMD_SCSI;
+ c->scsi_cmd = cmd;
+ c->Header.ReplyQueue = 0; /* unused in simple mode */
+ memcpy(&c->Header.LUN.LunAddrBytes[0], &scsi3addr[0], 8);
+ c->Header.Tag.lower = c->busaddr; /* Use k. address of cmd as tag */
// Fill in the request block...
- cp->Request.Timeout = 0;
- memset(cp->Request.CDB, 0, sizeof(cp->Request.CDB));
- BUG_ON(cmd->cmd_len > sizeof(cp->Request.CDB));
- cp->Request.CDBLen = cmd->cmd_len;
- memcpy(cp->Request.CDB, cmd->cmnd, cmd->cmd_len);
- cp->Request.Type.Type = TYPE_CMD;
- cp->Request.Type.Attribute = ATTR_SIMPLE;
+ c->Request.Timeout = 0;
+ memset(c->Request.CDB, 0, sizeof(c->Request.CDB));
+ BUG_ON(cmd->cmd_len > sizeof(c->Request.CDB));
+ c->Request.CDBLen = cmd->cmd_len;
+ memcpy(c->Request.CDB, cmd->cmnd, cmd->cmd_len);
+ c->Request.Type.Type = TYPE_CMD;
+ c->Request.Type.Attribute = ATTR_SIMPLE;
switch(cmd->sc_data_direction)
{
- case DMA_TO_DEVICE: cp->Request.Type.Direction = XFER_WRITE; break;
- case DMA_FROM_DEVICE: cp->Request.Type.Direction = XFER_READ; break;
- case DMA_NONE: cp->Request.Type.Direction = XFER_NONE; break;
+ case DMA_TO_DEVICE:
+ c->Request.Type.Direction = XFER_WRITE;
+ break;
+ case DMA_FROM_DEVICE:
+ c->Request.Type.Direction = XFER_READ;
+ break;
+ case DMA_NONE:
+ c->Request.Type.Direction = XFER_NONE;
+ break;
case DMA_BIDIRECTIONAL:
// This can happen if a buggy application does a scsi passthru
// and sets both inlen and outlen to non-zero. ( see
// ../scsi/scsi_ioctl.c:scsi_ioctl_send_command() )
- cp->Request.Type.Direction = XFER_RSVD;
+ c->Request.Type.Direction = XFER_RSVD;
// This is technically wrong, and cciss controllers should
// reject it with CMD_INVALID, which is the most correct
// response, but non-fibre backends appear to let it
break;
default:
- printk("cciss: unknown data direction: %d\n",
+ dev_warn(&h->pdev->dev, "unknown data direction: %d\n",
cmd->sc_data_direction);
BUG();
break;
}
- cciss_scatter_gather(c, cp, cmd);
-
- /* Put the request on the tail of the request queue */
-
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
- addQ(&c->reqQ, cp);
- c->Qdepth++;
- start_io(c);
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
-
+ cciss_scatter_gather(h, c, cmd);
+ enqueue_cmd_and_start_io(h, c);
/* the cmd'll come back via intr handler in complete_scsi_command() */
return 0;
}
-static void
-cciss_unregister_scsi(int ctlr)
+static void cciss_unregister_scsi(ctlr_info_t *h)
{
struct cciss_scsi_adapter_data_t *sa;
struct cciss_scsi_cmd_stack_t *stk;
/* we are being forcibly unloaded, and may not refuse. */
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
- sa = hba[ctlr]->scsi_ctlr;
+ spin_lock_irqsave(&h->lock, flags);
+ sa = h->scsi_ctlr;
stk = &sa->cmd_stack;
/* if we weren't ever actually registered, don't unregister */
if (sa->registered) {
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
+ spin_unlock_irqrestore(&h->lock, flags);
scsi_remove_host(sa->scsi_host);
scsi_host_put(sa->scsi_host);
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
+ spin_lock_irqsave(&h->lock, flags);
}
/* set scsi_host to NULL so our detect routine will
find us on register */
sa->scsi_host = NULL;
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
- scsi_cmd_stack_free(ctlr);
+ spin_unlock_irqrestore(&h->lock, flags);
+ scsi_cmd_stack_free(h);
kfree(sa);
}
-static int
-cciss_engage_scsi(int ctlr)
+static int cciss_engage_scsi(ctlr_info_t *h)
{
struct cciss_scsi_adapter_data_t *sa;
struct cciss_scsi_cmd_stack_t *stk;
unsigned long flags;
- spin_lock_irqsave(CCISS_LOCK(ctlr), flags);
- sa = hba[ctlr]->scsi_ctlr;
+ spin_lock_irqsave(&h->lock, flags);
+ sa = h->scsi_ctlr;
stk = &sa->cmd_stack;
if (sa->registered) {
- printk("cciss%d: SCSI subsystem already engaged.\n", ctlr);
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
+ dev_info(&h->pdev->dev, "SCSI subsystem already engaged.\n");
+ spin_unlock_irqrestore(&h->lock, flags);
return -ENXIO;
}
sa->registered = 1;
- spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
- cciss_update_non_disk_devices(ctlr, -1);
- cciss_scsi_detect(ctlr);
+ spin_unlock_irqrestore(&h->lock, flags);
+ cciss_update_non_disk_devices(h, -1);
+ cciss_scsi_detect(h);
return 0;
}
static void
-cciss_seq_tape_report(struct seq_file *seq, int ctlr)
+cciss_seq_tape_report(struct seq_file *seq, ctlr_info_t *h)
{
unsigned long flags;
- CPQ_TAPE_LOCK(ctlr, flags);
+ CPQ_TAPE_LOCK(h, flags);
seq_printf(seq,
"Sequential access devices: %d\n\n",
- ccissscsi[ctlr].ndevices);
- CPQ_TAPE_UNLOCK(ctlr, flags);
+ ccissscsi[h->ctlr].ndevices);
+ CPQ_TAPE_UNLOCK(h, flags);
}
static int wait_for_device_to_become_ready(ctlr_info_t *h,
int waittime = HZ;
CommandList_struct *c;
- c = cmd_alloc(h, 1);
+ c = cmd_alloc(h);
if (!c) {
- printk(KERN_WARNING "cciss%d: out of memory in "
- "wait_for_device_to_become_ready.\n", h->ctlr);
+ dev_warn(&h->pdev->dev, "out of memory in "
+ "wait_for_device_to_become_ready.\n");
return IO_ERROR;
}
waittime = waittime * 2;
/* Send the Test Unit Ready */
- rc = fill_cmd(c, TEST_UNIT_READY, h->ctlr, NULL, 0, 0,
+ rc = fill_cmd(h, c, TEST_UNIT_READY, NULL, 0, 0,
lunaddr, TYPE_CMD);
if (rc == 0)
rc = sendcmd_withirq_core(h, c, 0);
}
}
retry_tur:
- printk(KERN_WARNING "cciss%d: Waiting %d secs "
+ dev_warn(&h->pdev->dev, "Waiting %d secs "
"for device to become ready.\n",
- h->ctlr, waittime / HZ);
+ waittime / HZ);
rc = 1; /* device not ready. */
}
if (rc)
- printk("cciss%d: giving up on device.\n", h->ctlr);
+ dev_warn(&h->pdev->dev, "giving up on device.\n");
else
- printk(KERN_WARNING "cciss%d: device is ready.\n", h->ctlr);
+ dev_warn(&h->pdev->dev, "device is ready.\n");
- cmd_free(h, c, 1);
+ cmd_free(h, c);
return rc;
}
int rc;
CommandList_struct *cmd_in_trouble;
unsigned char lunaddr[8];
- ctlr_info_t *c;
- int ctlr;
+ ctlr_info_t *h;
/* find the controller to which the command to be aborted was sent */
- c = (ctlr_info_t *) scsicmd->device->host->hostdata[0];
- if (c == NULL) /* paranoia */
+ h = (ctlr_info_t *) scsicmd->device->host->hostdata[0];
+ if (h == NULL) /* paranoia */
return FAILED;
- ctlr = c->ctlr;
- printk(KERN_WARNING "cciss%d: resetting tape drive or medium changer.\n", ctlr);
+ dev_warn(&h->pdev->dev, "resetting tape drive or medium changer.\n");
/* find the command that's giving us trouble */
cmd_in_trouble = (CommandList_struct *) scsicmd->host_scribble;
if (cmd_in_trouble == NULL) /* paranoia */
return FAILED;
memcpy(lunaddr, &cmd_in_trouble->Header.LUN.LunAddrBytes[0], 8);
/* send a reset to the SCSI LUN which the command was sent to */
- rc = sendcmd_withirq(CCISS_RESET_MSG, ctlr, NULL, 0, 0, lunaddr,
+ rc = sendcmd_withirq(h, CCISS_RESET_MSG, NULL, 0, 0, lunaddr,
TYPE_MSG);
- if (rc == 0 && wait_for_device_to_become_ready(c, lunaddr) == 0)
+ if (rc == 0 && wait_for_device_to_become_ready(h, lunaddr) == 0)
return SUCCESS;
- printk(KERN_WARNING "cciss%d: resetting device failed.\n", ctlr);
+ dev_warn(&h->pdev->dev, "resetting device failed.\n");
return FAILED;
}
int rc;
CommandList_struct *cmd_to_abort;
unsigned char lunaddr[8];
- ctlr_info_t *c;
- int ctlr;
+ ctlr_info_t *h;
/* find the controller to which the command to be aborted was sent */
- c = (ctlr_info_t *) scsicmd->device->host->hostdata[0];
- if (c == NULL) /* paranoia */
+ h = (ctlr_info_t *) scsicmd->device->host->hostdata[0];
+ if (h == NULL) /* paranoia */
return FAILED;
- ctlr = c->ctlr;
- printk(KERN_WARNING "cciss%d: aborting tardy SCSI cmd\n", ctlr);
+ dev_warn(&h->pdev->dev, "aborting tardy SCSI cmd\n");
/* find the command to be aborted */
cmd_to_abort = (CommandList_struct *) scsicmd->host_scribble;
if (cmd_to_abort == NULL) /* paranoia */
return FAILED;
memcpy(lunaddr, &cmd_to_abort->Header.LUN.LunAddrBytes[0], 8);
- rc = sendcmd_withirq(CCISS_ABORT_MSG, ctlr, &cmd_to_abort->Header.Tag,
+ rc = sendcmd_withirq(h, CCISS_ABORT_MSG, &cmd_to_abort->Header.Tag,
0, 0, lunaddr, TYPE_MSG);
if (rc == 0)
return SUCCESS;
#include <linux/seq_file.h>
#include <linux/init.h>
#include <linux/hdreg.h>
+#include <linux/smp_lock.h>
#include <linux/spinlock.h>
#include <linux/blkdev.h>
#include <linux/genhd.h>
unsigned int blkcnt,
unsigned int log_unit );
-static int ida_open(struct block_device *bdev, fmode_t mode);
+static int ida_unlocked_open(struct block_device *bdev, fmode_t mode);
static int ida_release(struct gendisk *disk, fmode_t mode);
static int ida_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg);
static int ida_getgeo(struct block_device *bdev, struct hd_geometry *geo);
static const struct block_device_operations ida_fops = {
.owner = THIS_MODULE,
- .open = ida_open,
+ .open = ida_unlocked_open,
.release = ida_release,
- .locked_ioctl = ida_ioctl,
+ .ioctl = ida_ioctl,
.getgeo = ida_getgeo,
.revalidate_disk= ida_revalidate,
};
return 0;
}
+static int ida_unlocked_open(struct block_device *bdev, fmode_t mode)
+{
+ int ret;
+
+ lock_kernel();
+ ret = ida_open(bdev, mode);
+ unlock_kernel();
+
+ return ret;
+}
+
/*
* Close. Sync first.
*/
static int ida_release(struct gendisk *disk, fmode_t mode)
{
- ctlr_info_t *host = get_host(disk);
+ ctlr_info_t *host;
+
+ lock_kernel();
+ host = get_host(disk);
host->usage_count--;
+ unlock_kernel();
+
return 0;
}
* ida_ioctl does some miscellaneous stuff like reporting drive geometry,
* setting readahead and submitting commands from userspace to the controller.
*/
-static int ida_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg)
+static int ida_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg)
{
drv_info_t *drv = get_drv(bdev->bd_disk);
ctlr_info_t *host = get_host(bdev->bd_disk);
return error;
case IDAGETCTLRSIG:
if (!arg) return -EINVAL;
- put_user(host->ctlr_sig, (int __user *)arg);
+ if (put_user(host->ctlr_sig, (int __user *)arg))
+ return -EFAULT;
return 0;
case IDAREVALIDATEVOLS:
if (MINOR(bdev->bd_dev) != 0)
return revalidate_allvol(host);
case IDADRIVERVERSION:
if (!arg) return -EINVAL;
- put_user(DRIVER_VERSION, (unsigned long __user *)arg);
+ if (put_user(DRIVER_VERSION, (unsigned long __user *)arg))
+ return -EFAULT;
return 0;
case IDAGETPCIINFO:
{
}
}
+
+static int ida_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long param)
+{
+ int ret;
+
+ lock_kernel();
+ ret = ida_locked_ioctl(bdev, mode, cmd, param);
+ unlock_kernel();
+
+ return ret;
+}
+
/*
* ida_ctlr_ioctl is for passing commands to the controller from userspace.
* The command block (io) has already been copied to kernel space for us,
/* Pre submit processing */
switch(io->cmd) {
case PASSTHRU_A:
- p = kmalloc(io->sg[0].size, GFP_KERNEL);
- if (!p)
- {
- error = -ENOMEM;
- cmd_free(h, c, 0);
- return(error);
- }
- if (copy_from_user(p, io->sg[0].addr, io->sg[0].size)) {
- kfree(p);
- cmd_free(h, c, 0);
- return -EFAULT;
+ p = memdup_user(io->sg[0].addr, io->sg[0].size);
+ if (IS_ERR(p)) {
+ error = PTR_ERR(p);
+ cmd_free(h, c, 0);
+ return error;
}
c->req.hdr.blk = pci_map_single(h->pci_dev, &(io->c),
sizeof(ida_ioctl_t),
case DIAG_PASS_THRU:
case COLLECT_BUFFER:
case WRITE_FLASH_ROM:
- p = kmalloc(io->sg[0].size, GFP_KERNEL);
- if (!p)
- {
- error = -ENOMEM;
- cmd_free(h, c, 0);
- return(error);
+ p = memdup_user(io->sg[0].addr, io->sg[0].size);
+ if (IS_ERR(p)) {
+ error = PTR_ERR(p);
+ cmd_free(h, c, 0);
+ return error;
}
- if (copy_from_user(p, io->sg[0].addr, io->sg[0].size)) {
- kfree(p);
- cmd_free(h, c, 0);
- return -EFAULT;
- }
c->req.sg[0].size = io->sg[0].size;
c->req.sg[0].addr = pci_map_single(h->pci_dev, p,
c->req.sg[0].size, PCI_DMA_BIDIRECTIONAL);
md_io.error = 0;
if ((rw & WRITE) && !test_bit(MD_NO_BARRIER, &mdev->flags))
- rw |= (1 << BIO_RW_BARRIER);
- rw |= ((1<<BIO_RW_UNPLUG) | (1<<BIO_RW_SYNCIO));
+ rw |= REQ_HARDBARRIER;
+ rw |= REQ_UNPLUG | REQ_SYNC;
retry:
bio = bio_alloc(GFP_NOIO, 1);
/* check for unsupported barrier op.
* would rather check on EOPNOTSUPP, but that is not reliable.
* don't try again for ANY return value != 0 */
- if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER) && !ok)) {
+ if (unlikely((bio->bi_rw & REQ_HARDBARRIER) && !ok)) {
/* Try again with no barrier */
dev_warn(DEV, "Barriers not supported on meta data device - disabling\n");
set_bit(MD_NO_BARRIER, &mdev->flags);
- rw &= ~(1 << BIO_RW_BARRIER);
+ rw &= ~REQ_HARDBARRIER;
bio_put(bio);
goto retry;
}
u32 offset; /* usecs the probe got sent after the reference time point */
} __packed;
-struct delay_probe {
- struct list_head list;
- unsigned int seq_num;
- struct timeval time;
-};
-
/* DCBP: Drbd Compressed Bitmap Packet ... */
static inline enum drbd_bitmap_code
DCBP_get_code(struct p_compressed_bm *p)
unsigned int ko_count;
struct drbd_work resync_work,
unplug_work,
- md_sync_work,
- delay_probe_work;
+ md_sync_work;
struct timer_list resync_timer;
struct timer_list md_sync_timer;
- struct timer_list delay_probe_timer;
/* Used after attach while negotiating new disk state. */
union drbd_state new_state_tmp;
u64 ed_uuid; /* UUID of the exposed data */
struct mutex state_mutex;
char congestion_reason; /* Why we where congested... */
- struct list_head delay_probes; /* protected by peer_seq_lock */
- int data_delay; /* Delay of packets on the data-sock behind meta-sock */
- unsigned int delay_seq; /* To generate sequence numbers of delay probes */
- struct timeval dps_time; /* delay-probes-start-time */
- unsigned int dp_volume_last; /* send_cnt of last delay probe */
- int c_sync_rate; /* current resync rate after delay_probe magic */
};
static inline struct drbd_conf *minor_to_mdev(unsigned int minor)
return ok;
}
-static int drbd_send_delay_probe(struct drbd_conf *mdev, struct drbd_socket *ds)
-{
- struct p_delay_probe dp;
- int offset, ok = 0;
- struct timeval now;
-
- mutex_lock(&ds->mutex);
- if (likely(ds->socket)) {
- do_gettimeofday(&now);
- offset = now.tv_usec - mdev->dps_time.tv_usec +
- (now.tv_sec - mdev->dps_time.tv_sec) * 1000000;
- dp.seq_num = cpu_to_be32(mdev->delay_seq);
- dp.offset = cpu_to_be32(offset);
-
- ok = _drbd_send_cmd(mdev, ds->socket, P_DELAY_PROBE,
- (struct p_header *)&dp, sizeof(dp), 0);
- }
- mutex_unlock(&ds->mutex);
-
- return ok;
-}
-
-static int drbd_send_delay_probes(struct drbd_conf *mdev)
-{
- int ok;
-
- mdev->delay_seq++;
- do_gettimeofday(&mdev->dps_time);
- ok = drbd_send_delay_probe(mdev, &mdev->meta);
- ok = ok && drbd_send_delay_probe(mdev, &mdev->data);
-
- mdev->dp_volume_last = mdev->send_cnt;
- mod_timer(&mdev->delay_probe_timer, jiffies + mdev->sync_conf.dp_interval * HZ / 10);
-
- return ok;
-}
-
/* called on sndtimeo
* returns FALSE if we should retry,
* TRUE if we think connection is dead
return 1;
}
-static void consider_delay_probes(struct drbd_conf *mdev)
-{
- if (mdev->state.conn != C_SYNC_SOURCE || mdev->agreed_pro_version < 93)
- return;
-
- if (mdev->dp_volume_last + mdev->sync_conf.dp_volume * 2 < mdev->send_cnt)
- drbd_send_delay_probes(mdev);
-}
-
-static int w_delay_probes(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
-{
- if (!cancel && mdev->state.conn == C_SYNC_SOURCE)
- drbd_send_delay_probes(mdev);
-
- return 1;
-}
-
-static void delay_probe_timer_fn(unsigned long data)
-{
- struct drbd_conf *mdev = (struct drbd_conf *) data;
-
- if (list_empty(&mdev->delay_probe_work.list))
- drbd_queue_work(&mdev->data.work, &mdev->delay_probe_work);
-}
-
/* Used to send write requests
* R_PRIMARY -> Peer (P_DATA)
*/
/* NOTE: no need to check if barriers supported here as we would
* not pass the test in make_request_common in that case
*/
- if (bio_rw_flagged(req->master_bio, BIO_RW_BARRIER)) {
+ if (req->master_bio->bi_rw & REQ_HARDBARRIER) {
dev_err(DEV, "ASSERT FAILED would have set DP_HARDBARRIER\n");
/* dp_flags |= DP_HARDBARRIER; */
}
- if (bio_rw_flagged(req->master_bio, BIO_RW_SYNCIO))
+ if (req->master_bio->bi_rw & REQ_SYNC)
dp_flags |= DP_RW_SYNC;
/* for now handle SYNCIO and UNPLUG
* as if they still were one and the same flag */
- if (bio_rw_flagged(req->master_bio, BIO_RW_UNPLUG))
+ if (req->master_bio->bi_rw & REQ_UNPLUG)
dp_flags |= DP_RW_SYNC;
if (mdev->state.conn >= C_SYNC_SOURCE &&
mdev->state.conn <= C_PAUSED_SYNC_T)
drbd_put_data_sock(mdev);
- if (ok)
- consider_delay_probes(mdev);
-
return ok;
}
drbd_put_data_sock(mdev);
- if (ok)
- consider_delay_probes(mdev);
-
return ok;
}
unsigned long flags;
int rv = 0;
+ lock_kernel();
spin_lock_irqsave(&mdev->req_lock, flags);
/* to have a stable mdev->state.role
* and no race with updating open_cnt */
if (!rv)
mdev->open_cnt++;
spin_unlock_irqrestore(&mdev->req_lock, flags);
+ unlock_kernel();
return rv;
}
static int drbd_release(struct gendisk *gd, fmode_t mode)
{
struct drbd_conf *mdev = gd->private_data;
+ lock_kernel();
mdev->open_cnt--;
+ unlock_kernel();
return 0;
}
static void drbd_set_defaults(struct drbd_conf *mdev)
{
- mdev->sync_conf.after = DRBD_AFTER_DEF;
- mdev->sync_conf.rate = DRBD_RATE_DEF;
- mdev->sync_conf.al_extents = DRBD_AL_EXTENTS_DEF;
+ /* This way we get a compile error when sync_conf grows,
+ and we forgot to initialize it here */
+ mdev->sync_conf = (struct syncer_conf) {
+ /* .rate = */ DRBD_RATE_DEF,
+ /* .after = */ DRBD_AFTER_DEF,
+ /* .al_extents = */ DRBD_AL_EXTENTS_DEF,
+ /* .verify_alg = */ {}, 0,
+ /* .cpu_mask = */ {}, 0,
+ /* .csums_alg = */ {}, 0,
+ /* .use_rle = */ 0
+ };
+
+ /* Have to use that way, because the layout differs between
+ big endian and little endian */
mdev->state = (union drbd_state) {
{ .role = R_SECONDARY,
.peer = R_UNKNOWN,
INIT_LIST_HEAD(&mdev->unplug_work.list);
INIT_LIST_HEAD(&mdev->md_sync_work.list);
INIT_LIST_HEAD(&mdev->bm_io_work.w.list);
- INIT_LIST_HEAD(&mdev->delay_probes);
- INIT_LIST_HEAD(&mdev->delay_probe_work.list);
mdev->resync_work.cb = w_resync_inactive;
mdev->unplug_work.cb = w_send_write_hint;
mdev->md_sync_work.cb = w_md_sync;
mdev->bm_io_work.w.cb = w_bitmap_io;
- mdev->delay_probe_work.cb = w_delay_probes;
init_timer(&mdev->resync_timer);
init_timer(&mdev->md_sync_timer);
- init_timer(&mdev->delay_probe_timer);
mdev->resync_timer.function = resync_timer_fn;
mdev->resync_timer.data = (unsigned long) mdev;
mdev->md_sync_timer.function = md_sync_timer_fn;
mdev->md_sync_timer.data = (unsigned long) mdev;
- mdev->delay_probe_timer.function = delay_probe_timer_fn;
- mdev->delay_probe_timer.data = (unsigned long) mdev;
-
init_waitqueue_head(&mdev->misc_wait);
init_waitqueue_head(&mdev->state_wait);
sc.rate = DRBD_RATE_DEF;
sc.after = DRBD_AFTER_DEF;
sc.al_extents = DRBD_AL_EXTENTS_DEF;
- sc.dp_volume = DRBD_DP_VOLUME_DEF;
- sc.dp_interval = DRBD_DP_INTERVAL_DEF;
- sc.throttle_th = DRBD_RS_THROTTLE_TH_DEF;
- sc.hold_off_th = DRBD_RS_HOLD_OFF_TH_DEF;
} else
memcpy(&sc, &mdev->sync_conf, sizeof(struct syncer_conf));
seq_printf(seq, "sync'ed:%3u.%u%% ", res / 10, res % 10);
/* if more than 1 GB display in MB */
if (mdev->rs_total > 0x100000L)
- seq_printf(seq, "(%lu/%lu)M",
+ seq_printf(seq, "(%lu/%lu)M\n\t",
(unsigned long) Bit2KB(rs_left >> 10),
(unsigned long) Bit2KB(mdev->rs_total >> 10));
else
- seq_printf(seq, "(%lu/%lu)K",
+ seq_printf(seq, "(%lu/%lu)K\n\t",
(unsigned long) Bit2KB(rs_left),
(unsigned long) Bit2KB(mdev->rs_total));
- if (mdev->state.conn == C_SYNC_TARGET)
- seq_printf(seq, " queue_delay: %d.%d ms\n\t",
- mdev->data_delay / 1000,
- (mdev->data_delay % 1000) / 100);
- else if (mdev->state.conn == C_SYNC_SOURCE)
- seq_printf(seq, " delay_probe: %u\n\t", mdev->delay_seq);
-
/* see drivers/md/md.c
* We do not want to overflow, so the order of operands and
* the * 100 / 100 trick are important. We do a +1 to be
else
seq_printf(seq, " (%ld)", dbdt);
- if (mdev->state.conn == C_SYNC_TARGET) {
- if (mdev->c_sync_rate > 1000)
- seq_printf(seq, " want: %d,%03d",
- mdev->c_sync_rate / 1000, mdev->c_sync_rate % 1000);
- else
- seq_printf(seq, " want: %d", mdev->c_sync_rate);
- }
-
seq_printf(seq, " K/sec\n");
}
bio->bi_sector = sector;
bio->bi_bdev = mdev->ldev->backing_bdev;
/* we special case some flags in the multi-bio case, see below
- * (BIO_RW_UNPLUG, BIO_RW_BARRIER) */
+ * (REQ_UNPLUG, REQ_HARDBARRIER) */
bio->bi_rw = rw;
bio->bi_private = e;
bio->bi_end_io = drbd_endio_sec;
bios = bios->bi_next;
bio->bi_next = NULL;
- /* strip off BIO_RW_UNPLUG unless it is the last bio */
+ /* strip off REQ_UNPLUG unless it is the last bio */
if (bios)
- bio->bi_rw &= ~(1<<BIO_RW_UNPLUG);
+ bio->bi_rw &= ~REQ_UNPLUG;
drbd_generic_make_request(mdev, fault_type, bio);
- /* strip off BIO_RW_BARRIER,
+ /* strip off REQ_HARDBARRIER,
* unless it is the first or last bio */
if (bios && bios->bi_next)
- bios->bi_rw &= ~(1<<BIO_RW_BARRIER);
+ bios->bi_rw &= ~REQ_HARDBARRIER;
} while (bios);
maybe_kick_lo(mdev);
return 0;
}
/**
- * w_e_reissue() - Worker callback; Resubmit a bio, without BIO_RW_BARRIER set
+ * w_e_reissue() - Worker callback; Resubmit a bio, without REQ_HARDBARRIER set
* @mdev: DRBD device.
* @w: work object.
* @cancel: The connection will be closed anyways (unused in this callback)
(and DE_BARRIER_IN_NEXT_EPOCH_ISSUED in the previous Epoch)
so that we can finish that epoch in drbd_may_finish_epoch().
That is necessary if we already have a long chain of Epochs, before
- we realize that BIO_RW_BARRIER is actually not supported */
+ we realize that REQ_HARDBARRIER is actually not supported */
/* As long as the -ENOTSUPP on the barrier is reported immediately
that will never trigger. If it is reported late, we will just
epoch = list_entry(e->epoch->list.prev, struct drbd_epoch, list);
if (epoch == e->epoch) {
set_bit(DE_CONTAINS_A_BARRIER, &e->epoch->flags);
- rw |= (1<<BIO_RW_BARRIER);
+ rw |= REQ_HARDBARRIER;
e->flags |= EE_IS_BARRIER;
} else {
if (atomic_read(&epoch->epoch_size) > 1 ||
!test_bit(DE_CONTAINS_A_BARRIER, &epoch->flags)) {
set_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &epoch->flags);
set_bit(DE_CONTAINS_A_BARRIER, &e->epoch->flags);
- rw |= (1<<BIO_RW_BARRIER);
+ rw |= REQ_HARDBARRIER;
e->flags |= EE_IS_BARRIER;
}
}
dp_flags = be32_to_cpu(p->dp_flags);
if (dp_flags & DP_HARDBARRIER) {
dev_err(DEV, "ASSERT FAILED would have submitted barrier request\n");
- /* rw |= (1<<BIO_RW_BARRIER); */
+ /* rw |= REQ_HARDBARRIER; */
}
if (dp_flags & DP_RW_SYNC)
- rw |= (1<<BIO_RW_SYNCIO) | (1<<BIO_RW_UNPLUG);
+ rw |= REQ_SYNC | REQ_UNPLUG;
if (dp_flags & DP_MAY_SET_IN_SYNC)
e->flags |= EE_MAY_SET_IN_SYNC;
return ok;
}
-static int receive_skip(struct drbd_conf *mdev, struct p_header *h)
+static int receive_skip_(struct drbd_conf *mdev, struct p_header *h, int silent)
{
/* TODO zero copy sink :) */
static char sink[128];
int size, want, r;
- dev_warn(DEV, "skipping unknown optional packet type %d, l: %d!\n",
- h->command, h->length);
+ if (!silent)
+ dev_warn(DEV, "skipping unknown optional packet type %d, l: %d!\n",
+ h->command, h->length);
size = h->length;
while (size > 0) {
return size == 0;
}
-static int receive_UnplugRemote(struct drbd_conf *mdev, struct p_header *h)
-{
- if (mdev->state.disk >= D_INCONSISTENT)
- drbd_kick_lo(mdev);
-
- /* Make sure we've acked all the TCP data associated
- * with the data requests being unplugged */
- drbd_tcp_quickack(mdev->data.socket);
-
- return TRUE;
-}
-
-static void timeval_sub_us(struct timeval* tv, unsigned int us)
+static int receive_skip(struct drbd_conf *mdev, struct p_header *h)
{
- tv->tv_sec -= us / 1000000;
- us = us % 1000000;
- if (tv->tv_usec > us) {
- tv->tv_usec += 1000000;
- tv->tv_sec--;
- }
- tv->tv_usec -= us;
+ return receive_skip_(mdev, h, 0);
}
-static void got_delay_probe(struct drbd_conf *mdev, int from, struct p_delay_probe *p)
+static int receive_skip_silent(struct drbd_conf *mdev, struct p_header *h)
{
- struct delay_probe *dp;
- struct list_head *le;
- struct timeval now;
- int seq_num;
- int offset;
- int data_delay;
-
- seq_num = be32_to_cpu(p->seq_num);
- offset = be32_to_cpu(p->offset);
-
- spin_lock(&mdev->peer_seq_lock);
- if (!list_empty(&mdev->delay_probes)) {
- if (from == USE_DATA_SOCKET)
- le = mdev->delay_probes.next;
- else
- le = mdev->delay_probes.prev;
-
- dp = list_entry(le, struct delay_probe, list);
-
- if (dp->seq_num == seq_num) {
- list_del(le);
- spin_unlock(&mdev->peer_seq_lock);
- do_gettimeofday(&now);
- timeval_sub_us(&now, offset);
- data_delay =
- now.tv_usec - dp->time.tv_usec +
- (now.tv_sec - dp->time.tv_sec) * 1000000;
-
- if (data_delay > 0)
- mdev->data_delay = data_delay;
-
- kfree(dp);
- return;
- }
-
- if (dp->seq_num > seq_num) {
- spin_unlock(&mdev->peer_seq_lock);
- dev_warn(DEV, "Previous allocation failure of struct delay_probe?\n");
- return; /* Do not alloca a struct delay_probe.... */
- }
- }
- spin_unlock(&mdev->peer_seq_lock);
-
- dp = kmalloc(sizeof(struct delay_probe), GFP_NOIO);
- if (!dp) {
- dev_warn(DEV, "Failed to allocate a struct delay_probe, do not worry.\n");
- return;
- }
-
- dp->seq_num = seq_num;
- do_gettimeofday(&dp->time);
- timeval_sub_us(&dp->time, offset);
-
- spin_lock(&mdev->peer_seq_lock);
- if (from == USE_DATA_SOCKET)
- list_add(&dp->list, &mdev->delay_probes);
- else
- list_add_tail(&dp->list, &mdev->delay_probes);
- spin_unlock(&mdev->peer_seq_lock);
+ return receive_skip_(mdev, h, 1);
}
-static int receive_delay_probe(struct drbd_conf *mdev, struct p_header *h)
+static int receive_UnplugRemote(struct drbd_conf *mdev, struct p_header *h)
{
- struct p_delay_probe *p = (struct p_delay_probe *)h;
+ if (mdev->state.disk >= D_INCONSISTENT)
+ drbd_kick_lo(mdev);
- ERR_IF(h->length != (sizeof(*p)-sizeof(*h))) return FALSE;
- if (drbd_recv(mdev, h->payload, h->length) != h->length)
- return FALSE;
+ /* Make sure we've acked all the TCP data associated
+ * with the data requests being unplugged */
+ drbd_tcp_quickack(mdev->data.socket);
- got_delay_probe(mdev, USE_DATA_SOCKET, p);
return TRUE;
}
[P_OV_REQUEST] = receive_DataRequest,
[P_OV_REPLY] = receive_DataRequest,
[P_CSUM_RS_REQUEST] = receive_DataRequest,
- [P_DELAY_PROBE] = receive_delay_probe,
+ [P_DELAY_PROBE] = receive_skip_silent,
/* anything missing from this table is in
* the asender_tbl, see get_asender_cmd */
[P_MAX_CMD] = NULL,
return TRUE;
}
-static int got_delay_probe_m(struct drbd_conf *mdev, struct p_header *h)
+static int got_something_to_ignore_m(struct drbd_conf *mdev, struct p_header *h)
{
- struct p_delay_probe *p = (struct p_delay_probe *)h;
-
- got_delay_probe(mdev, USE_META_SOCKET, p);
+ /* IGNORE */
return TRUE;
}
[P_BARRIER_ACK] = { sizeof(struct p_barrier_ack), got_BarrierAck },
[P_STATE_CHG_REPLY] = { sizeof(struct p_req_state_reply), got_RqSReply },
[P_RS_IS_IN_SYNC] = { sizeof(struct p_block_ack), got_IsInSync },
- [P_DELAY_PROBE] = { sizeof(struct p_delay_probe), got_delay_probe_m },
+ [P_DELAY_PROBE] = { sizeof(struct p_delay_probe), got_something_to_ignore_m },
[P_MAX_CMD] = { 0, NULL },
};
if (cmd > P_MAX_CMD || asender_tbl[cmd].process == NULL)
* because of those XXX, this is not yet enabled,
* i.e. in drbd_init_set_defaults we set the NO_BARRIER_SUPP bit.
*/
- if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER) && test_bit(NO_BARRIER_SUPP, &mdev->flags))) {
+ if (unlikely(bio->bi_rw & REQ_HARDBARRIER) && test_bit(NO_BARRIER_SUPP, &mdev->flags)) {
/* dev_warn(DEV, "Rejecting barrier request as underlying device does not support\n"); */
bio_endio(bio, -EOPNOTSUPP);
return 0;
drbd_queue_work(&mdev->data.work, &mdev->resync_work);
}
-static int calc_resync_rate(struct drbd_conf *mdev)
-{
- int d = mdev->data_delay / 1000; /* us -> ms */
- int td = mdev->sync_conf.throttle_th * 100; /* 0.1s -> ms */
- int hd = mdev->sync_conf.hold_off_th * 100; /* 0.1s -> ms */
- int cr = mdev->sync_conf.rate;
-
- return d <= td ? cr :
- d >= hd ? 0 :
- cr + (cr * (td - d) / (hd - td));
-}
-
int w_make_resync_request(struct drbd_conf *mdev,
struct drbd_work *w, int cancel)
{
max_segment_size = mdev->agreed_pro_version < 94 ?
queue_max_segment_size(mdev->rq_queue) : DRBD_MAX_SEGMENT_SIZE;
- mdev->c_sync_rate = calc_resync_rate(mdev);
- number = SLEEP_TIME * mdev->c_sync_rate / ((BM_BLOCK_SIZE / 1024) * HZ);
+ number = SLEEP_TIME * mdev->sync_conf.rate / ((BM_BLOCK_SIZE / 1024) * HZ);
pe = atomic_read(&mdev->rs_pending_cnt);
mutex_lock(&mdev->data.mutex);
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/bio.h>
+#include <linux/smp_lock.h>
#include <linux/string.h>
#include <linux/jiffies.h>
#include <linux/fcntl.h>
static DECLARE_WAIT_QUEUE_HEAD(fdc_wait);
static DECLARE_WAIT_QUEUE_HEAD(command_done);
-#define NO_SIGNAL (!interruptible || !signal_pending(current))
-
/* Errors during formatting are counted here. */
static int format_errors;
static int *errors;
typedef void (*done_f)(int);
-static struct cont_t {
+static const struct cont_t {
void (*interrupt)(void);
/* this is called after the interrupt of the
* main command */
#define NEED_1_RECAL -2
#define NEED_2_RECAL -3
-static int usage_count;
+static atomic_t usage_count = ATOMIC_INIT(0);
/* buffer related variables */
static int buffer_track = -1;
}
/* locks the driver */
-static int _lock_fdc(int drive, bool interruptible, int line)
+static int lock_fdc(int drive, bool interruptible)
{
- if (!usage_count) {
- pr_err("Trying to lock fdc while usage count=0 at line %d\n",
- line);
+ if (WARN(atomic_read(&usage_count) == 0,
+ "Trying to lock fdc while usage count=0\n"))
return -1;
- }
-
- if (test_and_set_bit(0, &fdc_busy)) {
- DECLARE_WAITQUEUE(wait, current);
- add_wait_queue(&fdc_wait, &wait);
-
- for (;;) {
- set_current_state(TASK_INTERRUPTIBLE);
-
- if (!test_and_set_bit(0, &fdc_busy))
- break;
- schedule();
-
- if (!NO_SIGNAL) {
- remove_wait_queue(&fdc_wait, &wait);
- return -EINTR;
- }
- }
+ if (wait_event_interruptible(fdc_wait, !test_and_set_bit(0, &fdc_busy)))
+ return -EINTR;
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&fdc_wait, &wait);
- flush_scheduled_work();
- }
command_status = FD_COMMAND_NONE;
__reschedule_timeout(drive, "lock fdc");
return 0;
}
-#define lock_fdc(drive, interruptible) \
- _lock_fdc(drive, interruptible, __LINE__)
-
/* unlocks the driver */
-static inline void unlock_fdc(void)
+static void unlock_fdc(void)
{
unsigned long flags;
/* Set perpendicular mode as required, based on data rate, if supported.
* 82077 Now tested. 1Mbps data rate only possible with 82077-1.
*/
-static inline void perpendicular_mode(void)
+static void perpendicular_mode(void)
{
unsigned char perp_mode;
wake_up(&command_done);
}
-static struct cont_t wakeup_cont = {
+static const struct cont_t wakeup_cont = {
.interrupt = empty,
.redo = do_wakeup,
.error = empty,
.done = (done_f)empty
};
-static struct cont_t intr_cont = {
+static const struct cont_t intr_cont = {
.interrupt = empty,
.redo = process_fd_request,
.error = empty,
schedule_bh(handler);
- if (command_status < 2 && NO_SIGNAL) {
- DECLARE_WAITQUEUE(wait, current);
-
- add_wait_queue(&command_done, &wait);
- for (;;) {
- set_current_state(interruptible ?
- TASK_INTERRUPTIBLE :
- TASK_UNINTERRUPTIBLE);
-
- if (command_status >= 2 || !NO_SIGNAL)
- break;
-
- is_alive(__func__, "");
- schedule();
- }
-
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&command_done, &wait);
- }
+ if (interruptible)
+ wait_event_interruptible(command_done, command_status >= 2);
+ else
+ wait_event(command_done, command_status >= 2);
if (command_status < 2) {
cancel_activity();
debugt(__func__, "queue format request");
}
-static struct cont_t format_cont = {
+static const struct cont_t format_cont = {
.interrupt = format_interrupt,
.redo = redo_format,
.error = bad_flp_intr,
int tracksize;
int ssize;
- if (max_buffer_sectors == 0) {
- pr_info("VFS: Block I/O scheduled on unopened device\n");
+ if (WARN(max_buffer_sectors == 0, "VFS: Block I/O scheduled on unopened device\n"))
return 0;
- }
set_fdc((long)current_req->rq_disk->private_data);
return;
}
-static struct cont_t rw_cont = {
+static const struct cont_t rw_cont = {
.interrupt = rw_interrupt,
.redo = redo_fd_request,
.error = bad_flp_intr,
static void do_fd_request(struct request_queue *q)
{
- if (max_buffer_sectors == 0) {
- pr_info("VFS: %s called on non-open device\n", __func__);
+ if (WARN(max_buffer_sectors == 0,
+ "VFS: %s called on non-open device\n", __func__))
return;
- }
- if (usage_count == 0) {
- pr_info("warning: usage count=0, current_req=%p exiting\n",
- current_req);
- pr_info("sect=%ld type=%x flags=%x\n",
- (long)blk_rq_pos(current_req), current_req->cmd_type,
- current_req->cmd_flags);
+ if (WARN(atomic_read(&usage_count) == 0,
+ "warning: usage count=0, current_req=%p sect=%ld type=%x flags=%x\n",
+ current_req, (long)blk_rq_pos(current_req), current_req->cmd_type,
+ current_req->cmd_flags))
return;
- }
+
if (test_bit(0, &fdc_busy)) {
/* fdc busy, this new request will be treated when the
current one is done */
is_alive(__func__, "");
}
-static struct cont_t poll_cont = {
+static const struct cont_t poll_cont = {
.interrupt = success_and_wakeup,
.redo = floppy_ready,
.error = generic_failure,
pr_info("weird, reset interrupt called\n");
}
-static struct cont_t reset_cont = {
+static const struct cont_t reset_cont = {
.interrupt = reset_intr,
.redo = success_and_wakeup,
.error = generic_failure,
return copy_from_user(address, param, size) ? -EFAULT : 0;
}
-static inline const char *drive_name(int type, int drive)
+static const char *drive_name(int type, int drive)
{
struct floppy_struct *floppy;
generic_done(flag);
}
-static struct cont_t raw_cmd_cont = {
+static const struct cont_t raw_cmd_cont = {
.interrupt = success_and_wakeup,
.redo = floppy_start,
.error = generic_failure,
.done = raw_cmd_done
};
-static inline int raw_cmd_copyout(int cmd, void __user *param,
+static int raw_cmd_copyout(int cmd, void __user *param,
struct floppy_raw_cmd *ptr)
{
int ret;
}
}
-static inline int raw_cmd_copyin(int cmd, void __user *param,
+static int raw_cmd_copyin(int cmd, void __user *param,
struct floppy_raw_cmd **rcmd)
{
struct floppy_raw_cmd *ptr;
return 0;
}
-static inline int set_geometry(unsigned int cmd, struct floppy_struct *g,
+static int set_geometry(unsigned int cmd, struct floppy_struct *g,
int drive, int type, struct block_device *bdev)
{
int cnt;
}
/* handle obsolete ioctl's */
-static int ioctl_table[] = {
+static unsigned int ioctl_table[] = {
FDCLRPRM,
FDSETPRM,
FDDEFPRM,
FDTWADDLE
};
-static inline int normalize_ioctl(int *cmd, int *size)
+static int normalize_ioctl(unsigned int *cmd, int *size)
{
int i;
return 0;
}
-static int fd_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
+static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
unsigned long param)
{
int drive = (long)bdev->bd_disk->private_data;
return 0;
}
+static int fd_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long param)
+{
+ int ret;
+
+ lock_kernel();
+ ret = fd_locked_ioctl(bdev, mode, cmd, param);
+ unlock_kernel();
+
+ return ret;
+}
+
static void __init config_types(void)
{
bool has_drive = false;
{
int drive = (long)disk->private_data;
+ lock_kernel();
mutex_lock(&open_lock);
if (UDRS->fd_ref < 0)
UDRS->fd_ref = 0;
if (!UDRS->fd_ref)
opened_bdev[drive] = NULL;
mutex_unlock(&open_lock);
+ unlock_kernel();
return 0;
}
int res = -EBUSY;
char *tmp;
+ lock_kernel();
mutex_lock(&open_lock);
old_dev = UDRS->fd_device;
if (opened_bdev[drive] && opened_bdev[drive] != bdev)
goto out;
}
mutex_unlock(&open_lock);
+ unlock_kernel();
return 0;
out:
if (UDRS->fd_ref < 0)
opened_bdev[drive] = NULL;
out2:
mutex_unlock(&open_lock);
+ unlock_kernel();
return res;
}
bio.bi_size = size;
bio.bi_bdev = bdev;
bio.bi_sector = 0;
+ bio.bi_flags = BIO_QUIET;
init_completion(&complete);
bio.bi_private = &complete;
bio.bi_end_io = floppy_rb0_complete;
if (test_bit(FD_DISK_CHANGED_BIT, &UDRS->flags) ||
test_bit(FD_VERIFY_BIT, &UDRS->flags) ||
test_bit(drive, &fake_change) || NO_GEOM) {
- if (usage_count == 0) {
- pr_info("VFS: revalidate called on non-open device.\n");
+ if (WARN(atomic_read(&usage_count) == 0,
+ "VFS: revalidate called on non-open device.\n"))
return -EFAULT;
- }
+
lock_fdc(drive, false);
cf = (test_bit(FD_DISK_CHANGED_BIT, &UDRS->flags) ||
test_bit(FD_VERIFY_BIT, &UDRS->flags));
.owner = THIS_MODULE,
.open = floppy_open,
.release = floppy_release,
- .locked_ioctl = fd_ioctl,
+ .ioctl = fd_ioctl,
.getgeo = fd_getgeo,
.media_changed = check_floppy_change,
.revalidate_disk = floppy_revalidate,
return sprintf(buf, "%X\n", UDP->cmos);
}
-DEVICE_ATTR(cmos, S_IRUGO, floppy_cmos_show, NULL);
+static DEVICE_ATTR(cmos, S_IRUGO, floppy_cmos_show, NULL);
static void floppy_device_release(struct device *dev)
{
int i, unit, drive;
int err, dr;
+ set_debugt();
+ interruptjiffies = resultjiffies = jiffies;
+
#if defined(CONFIG_PPC)
if (check_legacy_ioport(FDC1))
return -ENODEV;
platform_device_unregister(&floppy_device[drive]);
out_flush_work:
flush_scheduled_work();
- if (usage_count)
+ if (atomic_read(&usage_count))
floppy_release_irq_and_dma();
out_unreg_region:
blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256);
return err;
}
-static DEFINE_SPINLOCK(floppy_usage_lock);
-
static const struct io_region {
int offset;
int size;
static int floppy_grab_irq_and_dma(void)
{
- unsigned long flags;
-
- spin_lock_irqsave(&floppy_usage_lock, flags);
- if (usage_count++) {
- spin_unlock_irqrestore(&floppy_usage_lock, flags);
+ if (atomic_inc_return(&usage_count) > 1)
return 0;
- }
- spin_unlock_irqrestore(&floppy_usage_lock, flags);
/*
* We might have scheduled a free_irq(), wait it to
if (fd_request_irq()) {
DPRINT("Unable to grab IRQ%d for the floppy driver\n",
FLOPPY_IRQ);
- spin_lock_irqsave(&floppy_usage_lock, flags);
- usage_count--;
- spin_unlock_irqrestore(&floppy_usage_lock, flags);
+ atomic_dec(&usage_count);
return -1;
}
if (fd_request_dma()) {
use_virtual_dma = can_use_virtual_dma = 1;
if (!(can_use_virtual_dma & 1)) {
fd_free_irq();
- spin_lock_irqsave(&floppy_usage_lock, flags);
- usage_count--;
- spin_unlock_irqrestore(&floppy_usage_lock, flags);
+ atomic_dec(&usage_count);
return -1;
}
}
fd_free_dma();
while (--fdc >= 0)
floppy_release_regions(fdc);
- spin_lock_irqsave(&floppy_usage_lock, flags);
- usage_count--;
- spin_unlock_irqrestore(&floppy_usage_lock, flags);
+ atomic_dec(&usage_count);
return -1;
}
#endif
long tmpsize;
unsigned long tmpaddr;
- unsigned long flags;
- spin_lock_irqsave(&floppy_usage_lock, flags);
- if (--usage_count) {
- spin_unlock_irqrestore(&floppy_usage_lock, flags);
+ if (!atomic_dec_and_test(&usage_count))
return;
- }
- spin_unlock_irqrestore(&floppy_usage_lock, flags);
+
if (irqdma_allocated) {
fd_disable_dma();
fd_free_dma();
del_timer_sync(&fd_timer);
blk_cleanup_queue(floppy_queue);
- if (usage_count)
+ if (atomic_read(&usage_count))
floppy_release_irq_and_dma();
/* eject disk, if any */
req_data_dir(req) == READ ? "read" : "writ",
cyl, head, sec, nsect, req->buffer);
#endif
- if (blk_fs_request(req)) {
+ if (req->cmd_type == REQ_TYPE_FS) {
switch (rq_data_dir(req)) {
case READ:
hd_out(disk, nsect, sec, head, cyl, ATA_CMD_PIO_READ,
#include <linux/compat.h>
#include <linux/suspend.h>
#include <linux/freezer.h>
+#include <linux/smp_lock.h>
#include <linux/writeback.h>
#include <linux/buffer_head.h> /* for invalidate_bdev() */
#include <linux/completion.h>
pos = ((loff_t) bio->bi_sector << 9) + lo->lo_offset;
if (bio_rw(bio) == WRITE) {
- bool barrier = bio_rw_flagged(bio, BIO_RW_BARRIER);
+ bool barrier = (bio->bi_rw & REQ_HARDBARRIER);
struct file *file = lo->lo_backing_file;
if (barrier) {
lo->lo_queue->unplug_fn = loop_unplug;
if (!(lo_flags & LO_FLAGS_READ_ONLY) && file->f_op->fsync)
- blk_queue_ordered(lo->lo_queue, QUEUE_ORDERED_DRAIN, NULL);
+ blk_queue_ordered(lo->lo_queue, QUEUE_ORDERED_DRAIN);
set_capacity(lo->lo_disk, size);
bd_set_size(bdev, size << 9);
{
struct loop_device *lo = bdev->bd_disk->private_data;
+ lock_kernel();
mutex_lock(&lo->lo_ctl_mutex);
lo->lo_refcnt++;
mutex_unlock(&lo->lo_ctl_mutex);
+ unlock_kernel();
return 0;
}
struct loop_device *lo = disk->private_data;
int err;
+ lock_kernel();
mutex_lock(&lo->lo_ctl_mutex);
if (--lo->lo_refcnt)
out:
mutex_unlock(&lo->lo_ctl_mutex);
out_unlocked:
+ lock_kernel();
return 0;
}
break;
}
- if (unlikely(!blk_fs_request(host->req))) {
+ if (unlikely(host->req->cmd_type != REQ_TYPE_FS)) {
mg_end_request_cur(host, -EIO);
continue;
}
continue;
}
- if (unlikely(!blk_fs_request(req))) {
+ if (unlikely(req->cmd_type != REQ_TYPE_FS)) {
mg_end_request_cur(host, -EIO);
continue;
}
#include <linux/errno.h>
#include <linux/file.h>
#include <linux/ioctl.h>
+#include <linux/smp_lock.h>
#include <linux/compiler.h>
#include <linux/err.h>
#include <linux/kernel.h>
static void nbd_handle_req(struct nbd_device *lo, struct request *req)
{
- if (!blk_fs_request(req))
+ if (req->cmd_type != REQ_TYPE_FS)
goto error_out;
nbd_cmd(req) = NBD_CMD_READ;
dprintk(DBG_IOCTL, "%s: nbd_ioctl cmd=%s(0x%x) arg=%lu\n",
lo->disk->disk_name, ioctl_cmd_to_ascii(cmd), cmd, arg);
+ lock_kernel();
mutex_lock(&lo->tx_lock);
error = __nbd_ioctl(bdev, lo, cmd, arg);
mutex_unlock(&lo->tx_lock);
+ unlock_kernel();
return error;
}
static const struct block_device_operations nbd_fops =
{
.owner = THIS_MODULE,
- .locked_ioctl = nbd_ioctl,
+ .ioctl = nbd_ioctl,
};
/*
break;
/* filter out block requests we don't understand */
- if (!blk_fs_request(rq) && !blk_barrier_rq(rq)) {
+ if (rq->cmd_type != REQ_TYPE_FS &&
+ !(rq->cmd_flags & REQ_HARDBARRIER)) {
blk_end_request_all(rq, 0);
continue;
}
* driver-specific, etc.
*/
- do_flush = (rq->special == (void *) 0xdeadbeefUL);
+ do_flush = rq->cmd_flags & REQ_FLUSH;
do_write = (rq_data_dir(rq) == WRITE);
if (!do_flush) { /* osd_flush does not use a bio */
}
}
-static void osdblk_prepare_flush(struct request_queue *q, struct request *rq)
-{
- /* add driver-specific marker, to indicate that this request
- * is a flush command
- */
- rq->special = (void *) 0xdeadbeefUL;
-}
-
static void osdblk_free_disk(struct osdblk_device *osdev)
{
struct gendisk *disk = osdev->disk;
blk_queue_stack_limits(q, osd_request_queue(osdev->osd));
blk_queue_prep_rq(q, blk_queue_start_tag);
- blk_queue_ordered(q, QUEUE_ORDERED_DRAIN_FLUSH, osdblk_prepare_flush);
+ blk_queue_ordered(q, QUEUE_ORDERED_DRAIN_FLUSH);
disk->queue = q;
#include <linux/cdrom.h>
#include <linux/spinlock.h>
#include <linux/blkdev.h>
+#include <linux/smp_lock.h>
#include <asm/uaccess.h>
static DEFINE_SPINLOCK(pcd_lock);
static int pcd_block_open(struct block_device *bdev, fmode_t mode)
{
struct pcd_unit *cd = bdev->bd_disk->private_data;
- return cdrom_open(&cd->info, bdev, mode);
+ int ret;
+
+ lock_kernel();
+ ret = cdrom_open(&cd->info, bdev, mode);
+ unlock_kernel();
+
+ return ret;
}
static int pcd_block_release(struct gendisk *disk, fmode_t mode)
{
struct pcd_unit *cd = disk->private_data;
+ lock_kernel();
cdrom_release(&cd->info, mode);
+ unlock_kernel();
return 0;
}
unsigned cmd, unsigned long arg)
{
struct pcd_unit *cd = bdev->bd_disk->private_data;
- return cdrom_ioctl(&cd->info, bdev, mode, cmd, arg);
+ int ret;
+
+ lock_kernel();
+ ret = cdrom_ioctl(&cd->info, bdev, mode, cmd, arg);
+ unlock_kernel();
+
+ return ret;
}
static int pcd_block_media_changed(struct gendisk *disk)
.owner = THIS_MODULE,
.open = pcd_block_open,
.release = pcd_block_release,
- .locked_ioctl = pcd_block_ioctl,
+ .ioctl = pcd_block_ioctl,
.media_changed = pcd_block_media_changed,
};
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/kernel.h>
+#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <linux/workqueue.h>
static enum action do_pd_io_start(void)
{
- if (blk_special_request(pd_req)) {
+ if (pd_req->cmd_type == REQ_TYPE_SPECIAL) {
phase = pd_special;
return pd_special();
}
{
struct pd_unit *disk = bdev->bd_disk->private_data;
+ lock_kernel();
disk->access++;
if (disk->removable) {
pd_special_command(disk, pd_media_check);
pd_special_command(disk, pd_door_lock);
}
+ unlock_kernel();
return 0;
}
switch (cmd) {
case CDROMEJECT:
+ lock_kernel();
if (disk->access == 1)
pd_special_command(disk, pd_eject);
+ unlock_kernel();
return 0;
default:
return -EINVAL;
{
struct pd_unit *disk = p->private_data;
+ lock_kernel();
if (!--disk->access && disk->removable)
pd_special_command(disk, pd_door_unlock);
+ unlock_kernel();
return 0;
}
.owner = THIS_MODULE,
.open = pd_open,
.release = pd_release,
- .locked_ioctl = pd_ioctl,
+ .ioctl = pd_ioctl,
.getgeo = pd_getgeo,
.media_changed = pd_check_media,
.revalidate_disk= pd_revalidate
#include <linux/spinlock.h>
#include <linux/blkdev.h>
#include <linux/blkpg.h>
+#include <linux/smp_lock.h>
#include <asm/uaccess.h>
static DEFINE_SPINLOCK(pf_spin_lock);
.owner = THIS_MODULE,
.open = pf_open,
.release = pf_release,
- .locked_ioctl = pf_ioctl,
+ .ioctl = pf_ioctl,
.getgeo = pf_getgeo,
.media_changed = pf_check_media,
};
static int pf_open(struct block_device *bdev, fmode_t mode)
{
struct pf_unit *pf = bdev->bd_disk->private_data;
+ int ret;
+ lock_kernel();
pf_identify(pf);
+ ret = -ENODEV;
if (pf->media_status == PF_NM)
- return -ENODEV;
+ goto out;
+ ret = -EROFS;
if ((pf->media_status == PF_RO) && (mode & FMODE_WRITE))
- return -EROFS;
+ goto out;
+ ret = 0;
pf->access++;
if (pf->removable)
pf_lock(pf, 1);
-
- return 0;
+out:
+ unlock_kernel();
+ return ret;
}
static int pf_getgeo(struct block_device *bdev, struct hd_geometry *geo)
if (pf->access != 1)
return -EBUSY;
+ lock_kernel();
pf_eject(pf);
+ unlock_kernel();
+
return 0;
}
{
struct pf_unit *pf = disk->private_data;
- if (pf->access <= 0)
+ lock_kernel();
+ if (pf->access <= 0) {
+ unlock_kernel();
return -EINVAL;
+ }
pf->access--;
if (!pf->access && pf->removable)
pf_lock(pf, 0);
+ unlock_kernel();
return 0;
}
#include <linux/seq_file.h>
#include <linux/miscdevice.h>
#include <linux/freezer.h>
+#include <linux/smp_lock.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <scsi/scsi_cmnd.h>
pkt->bio->bi_flags = 1 << BIO_UPTODATE;
pkt->bio->bi_idx = 0;
- BUG_ON(pkt->bio->bi_rw != (1 << BIO_RW));
+ BUG_ON(pkt->bio->bi_rw != REQ_WRITE);
BUG_ON(pkt->bio->bi_vcnt != pkt->frames);
BUG_ON(pkt->bio->bi_size != pkt->frames * CD_FRAMESIZE);
BUG_ON(pkt->bio->bi_end_io != pkt_end_io_packet_write);
VPRINTK(DRIVER_NAME": entering open\n");
+ lock_kernel();
mutex_lock(&ctl_mutex);
pd = pkt_find_dev_from_minor(MINOR(bdev->bd_dev));
if (!pd) {
}
mutex_unlock(&ctl_mutex);
+ unlock_kernel();
return 0;
out_dec:
out:
VPRINTK(DRIVER_NAME": failed open (%d)\n", ret);
mutex_unlock(&ctl_mutex);
+ unlock_kernel();
return ret;
}
struct pktcdvd_device *pd = disk->private_data;
int ret = 0;
+ lock_kernel();
mutex_lock(&ctl_mutex);
pd->refcnt--;
BUG_ON(pd->refcnt < 0);
pkt_release_dev(pd, flush);
}
mutex_unlock(&ctl_mutex);
+ unlock_kernel();
return ret;
}
static int pkt_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg)
{
struct pktcdvd_device *pd = bdev->bd_disk->private_data;
+ int ret;
VPRINTK("pkt_ioctl: cmd %x, dev %d:%d\n", cmd,
MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev));
+ lock_kernel();
switch (cmd) {
case CDROMEJECT:
/*
case CDROM_LAST_WRITTEN:
case CDROM_SEND_PACKET:
case SCSI_IOCTL_SEND_COMMAND:
- return __blkdev_driver_ioctl(pd->bdev, mode, cmd, arg);
+ ret = __blkdev_driver_ioctl(pd->bdev, mode, cmd, arg);
+ break;
default:
VPRINTK(DRIVER_NAME": Unknown ioctl for %s (%x)\n", pd->name, cmd);
- return -ENOTTY;
+ ret = -ENOTTY;
}
+ unlock_kernel();
- return 0;
+ return ret;
}
static int pkt_media_changed(struct gendisk *disk)
.owner = THIS_MODULE,
.open = pkt_open,
.release = pkt_close,
- .locked_ioctl = pkt_ioctl,
+ .ioctl = pkt_ioctl,
.media_changed = pkt_media_changed,
};
dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__);
while ((req = blk_fetch_request(q))) {
- if (blk_fs_request(req)) {
- if (ps3disk_submit_request_sg(dev, req))
- break;
- } else if (req->cmd_type == REQ_TYPE_LINUX_BLOCK &&
- req->cmd[0] == REQ_LB_OP_FLUSH) {
+ if (req->cmd_flags & REQ_FLUSH) {
if (ps3disk_submit_flush_request(dev, req))
break;
+ } else if (req->cmd_type == REQ_TYPE_FS) {
+ if (ps3disk_submit_request_sg(dev, req))
+ break;
} else {
blk_dump_rq_flags(req, DEVICE_NAME " bad request");
__blk_end_request_all(req, -EIO);
return IRQ_HANDLED;
}
- if (req->cmd_type == REQ_TYPE_LINUX_BLOCK &&
- req->cmd[0] == REQ_LB_OP_FLUSH) {
+ if (req->cmd_flags & REQ_FLUSH) {
read = 0;
op = "flush";
} else {
return 0;
}
-static void ps3disk_prepare_flush(struct request_queue *q, struct request *req)
-{
- struct ps3_storage_device *dev = q->queuedata;
-
- dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__);
-
- req->cmd_type = REQ_TYPE_LINUX_BLOCK;
- req->cmd[0] = REQ_LB_OP_FLUSH;
-}
-
static unsigned long ps3disk_mask;
static DEFINE_MUTEX(ps3disk_mask_mutex);
blk_queue_dma_alignment(queue, dev->blk_size-1);
blk_queue_logical_block_size(queue, dev->blk_size);
- blk_queue_ordered(queue, QUEUE_ORDERED_DRAIN_FLUSH,
- ps3disk_prepare_flush);
+ blk_queue_ordered(queue, QUEUE_ORDERED_DRAIN_FLUSH);
blk_queue_max_segments(queue, -1);
blk_queue_max_segment_size(queue, dev->bounce_size);
#include <linux/fd.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
+#include <linux/smp_lock.h>
#include <linux/hdreg.h>
#include <linux/kernel.h>
#include <linux/delay.h>
return err;
}
+static int floppy_unlocked_open(struct block_device *bdev, fmode_t mode)
+{
+ int ret;
+
+ lock_kernel();
+ ret = floppy_open(bdev, mode);
+ unlock_kernel();
+
+ return ret;
+}
+
static int floppy_release(struct gendisk *disk, fmode_t mode)
{
struct floppy_state *fs = disk->private_data;
struct swim __iomem *base = fs->swd->base;
+ lock_kernel();
if (fs->ref_count < 0)
fs->ref_count = 0;
else if (fs->ref_count > 0)
if (fs->ref_count == 0)
swim_motor(base, OFF);
+ unlock_kernel();
return 0;
}
case FDEJECT:
if (fs->ref_count != 1)
return -EBUSY;
+ lock_kernel();
err = floppy_eject(fs);
+ unlock_kernel();
return err;
case FDGETPRM:
static const struct block_device_operations floppy_fops = {
.owner = THIS_MODULE,
- .open = floppy_open,
+ .open = floppy_unlocked_open,
.release = floppy_release,
- .locked_ioctl = floppy_ioctl,
+ .ioctl = floppy_ioctl,
.getgeo = floppy_getgeo,
.media_changed = floppy_check_change,
.revalidate_disk = floppy_revalidate,
#include <linux/ioctl.h>
#include <linux/blkdev.h>
#include <linux/interrupt.h>
+#include <linux/smp_lock.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <asm/io.h>
static struct floppy_struct floppy_type =
{ 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL }; /* 7 1.44MB 3.5" */
-static int floppy_ioctl(struct block_device *bdev, fmode_t mode,
+static int floppy_locked_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long param)
{
struct floppy_state *fs = bdev->bd_disk->private_data;
return -ENOTTY;
}
+static int floppy_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long param)
+{
+ int ret;
+
+ lock_kernel();
+ ret = floppy_locked_ioctl(bdev, mode, cmd, param);
+ unlock_kernel();
+
+ return ret;
+}
+
static int floppy_open(struct block_device *bdev, fmode_t mode)
{
struct floppy_state *fs = bdev->bd_disk->private_data;
return 0;
}
+static int floppy_unlocked_open(struct block_device *bdev, fmode_t mode)
+{
+ int ret;
+
+ lock_kernel();
+ ret = floppy_open(bdev, mode);
+ unlock_kernel();
+
+ return ret;
+}
+
static int floppy_release(struct gendisk *disk, fmode_t mode)
{
struct floppy_state *fs = disk->private_data;
struct swim3 __iomem *sw = fs->swim3;
+ lock_kernel();
if (fs->ref_count > 0 && --fs->ref_count == 0) {
swim3_action(fs, MOTOR_OFF);
out_8(&sw->control_bic, 0xff);
swim3_select(fs, RELAX);
}
+ unlock_kernel();
return 0;
}
}
static const struct block_device_operations floppy_fops = {
- .open = floppy_open,
+ .open = floppy_unlocked_open,
.release = floppy_release,
- .locked_ioctl = floppy_ioctl,
+ .ioctl = floppy_ioctl,
.media_changed = floppy_check_change,
.revalidate_disk= floppy_revalidate,
};
#include <linux/timer.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
+#include <linux/smp_lock.h>
#include <scsi/scsi.h>
#define DRV_NAME "ub"
return 0;
}
- if (lun->changed && !blk_pc_request(rq)) {
+ if (lun->changed && rq->cmd_type != REQ_TYPE_BLOCK_PC) {
blk_start_request(rq);
ub_end_rq(rq, SAM_STAT_CHECK_CONDITION);
return 0;
}
urq->nsg = n_elem;
- if (blk_pc_request(rq)) {
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
ub_cmd_build_packet(sc, lun, cmd, urq);
} else {
ub_cmd_build_block(sc, lun, cmd, urq);
rq = urq->rq;
if (cmd->error == 0) {
- if (blk_pc_request(rq)) {
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
if (cmd->act_len >= rq->resid_len)
rq->resid_len = 0;
else
}
}
} else {
- if (blk_pc_request(rq)) {
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
/* UB_SENSE_SIZE is smaller than SCSI_SENSE_BUFFERSIZE */
memcpy(rq->sense, sc->top_sense, UB_SENSE_SIZE);
rq->sense_len = UB_SENSE_SIZE;
return rc;
}
+static int ub_bd_unlocked_open(struct block_device *bdev, fmode_t mode)
+{
+ int ret;
+
+ lock_kernel();
+ ret = ub_bd_open(bdev, mode);
+ unlock_kernel();
+
+ return ret;
+}
+
+
/*
*/
static int ub_bd_release(struct gendisk *disk, fmode_t mode)
struct ub_lun *lun = disk->private_data;
struct ub_dev *sc = lun->udev;
+ lock_kernel();
ub_put(sc);
+ unlock_kernel();
+
return 0;
}
{
struct gendisk *disk = bdev->bd_disk;
void __user *usermem = (void __user *) arg;
+ int ret;
+
+ lock_kernel();
+ ret = scsi_cmd_ioctl(disk->queue, disk, mode, cmd, usermem);
+ unlock_kernel();
- return scsi_cmd_ioctl(disk->queue, disk, mode, cmd, usermem);
+ return ret;
}
/*
static const struct block_device_operations ub_bd_fops = {
.owner = THIS_MODULE,
- .open = ub_bd_open,
+ .open = ub_bd_unlocked_open,
.release = ub_bd_release,
- .locked_ioctl = ub_bd_ioctl,
+ .ioctl = ub_bd_ioctl,
.media_changed = ub_bd_media_changed,
.revalidate_disk = ub_bd_revalidate,
};
le32_to_cpu(desc->local_addr)>>9,
le32_to_cpu(desc->transfer_size));
dump_dmastat(card, control);
- } else if (test_bit(BIO_RW, &bio->bi_rw) &&
+ } else if ((bio->bi_rw & REQ_WRITE) &&
le32_to_cpu(desc->local_addr) >> 9 ==
card->init_size) {
card->init_size += le32_to_cpu(desc->transfer_size) >> 9;
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/string.h>
+#include <linux/smp_lock.h>
#include <linux/dma-mapping.h>
#include <linux/completion.h>
#include <linux/device.h>
return 0;
}
+static int viodasd_unlocked_open(struct block_device *bdev, fmode_t mode)
+{
+ int ret;
+
+ lock_kernel();
+ ret = viodasd_open(bdev, mode);
+ unlock_kernel();
+
+ return ret;
+}
+
+
/*
* External release entry point.
*/
struct viodasd_device *d = disk->private_data;
HvLpEvent_Rc hvrc;
+ lock_kernel();
/* Send the event to OS/400. We DON'T expect a response */
hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
HvLpEvent_Type_VirtualIo,
0, 0, 0);
if (hvrc != 0)
pr_warning("HV close call failed %d\n", (int)hvrc);
+
+ unlock_kernel();
+
return 0;
}
*/
static const struct block_device_operations viodasd_fops = {
.owner = THIS_MODULE,
- .open = viodasd_open,
+ .open = viodasd_unlocked_open,
.release = viodasd_release,
.getgeo = viodasd_getgeo,
};
if (req == NULL)
return;
/* check that request contains a valid command */
- if (!blk_fs_request(req)) {
+ if (req->cmd_type != REQ_TYPE_FS) {
viodasd_end_request(req, -EIO, blk_rq_sectors(req));
continue;
}
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
+#include <linux/smp_lock.h>
#include <linux/hdreg.h>
#include <linux/virtio.h>
#include <linux/virtio_blk.h>
break;
}
- if (blk_pc_request(vbr->req)) {
+ switch (vbr->req->cmd_type) {
+ case REQ_TYPE_BLOCK_PC:
vbr->req->resid_len = vbr->in_hdr.residual;
vbr->req->sense_len = vbr->in_hdr.sense_len;
vbr->req->errors = vbr->in_hdr.errors;
- }
- if (blk_special_request(vbr->req))
+ break;
+ case REQ_TYPE_SPECIAL:
vbr->req->errors = (error != 0);
+ break;
+ default:
+ break;
+ }
__blk_end_request_all(vbr->req, error);
list_del(&vbr->list);
return false;
vbr->req = req;
- switch (req->cmd_type) {
- case REQ_TYPE_FS:
- vbr->out_hdr.type = 0;
- vbr->out_hdr.sector = blk_rq_pos(vbr->req);
- vbr->out_hdr.ioprio = req_get_ioprio(vbr->req);
- break;
- case REQ_TYPE_BLOCK_PC:
- vbr->out_hdr.type = VIRTIO_BLK_T_SCSI_CMD;
- vbr->out_hdr.sector = 0;
- vbr->out_hdr.ioprio = req_get_ioprio(vbr->req);
- break;
- case REQ_TYPE_SPECIAL:
- vbr->out_hdr.type = VIRTIO_BLK_T_GET_ID;
+
+ if (req->cmd_flags & REQ_FLUSH) {
+ vbr->out_hdr.type = VIRTIO_BLK_T_FLUSH;
vbr->out_hdr.sector = 0;
vbr->out_hdr.ioprio = req_get_ioprio(vbr->req);
- break;
- case REQ_TYPE_LINUX_BLOCK:
- if (req->cmd[0] == REQ_LB_OP_FLUSH) {
- vbr->out_hdr.type = VIRTIO_BLK_T_FLUSH;
+ } else {
+ switch (req->cmd_type) {
+ case REQ_TYPE_FS:
+ vbr->out_hdr.type = 0;
+ vbr->out_hdr.sector = blk_rq_pos(vbr->req);
+ vbr->out_hdr.ioprio = req_get_ioprio(vbr->req);
+ break;
+ case REQ_TYPE_BLOCK_PC:
+ vbr->out_hdr.type = VIRTIO_BLK_T_SCSI_CMD;
vbr->out_hdr.sector = 0;
vbr->out_hdr.ioprio = req_get_ioprio(vbr->req);
break;
+ case REQ_TYPE_SPECIAL:
+ vbr->out_hdr.type = VIRTIO_BLK_T_GET_ID;
+ vbr->out_hdr.sector = 0;
+ vbr->out_hdr.ioprio = req_get_ioprio(vbr->req);
+ break;
+ default:
+ /* We don't put anything else in the queue. */
+ BUG();
}
- /*FALLTHRU*/
- default:
- /* We don't put anything else in the queue. */
- BUG();
}
- if (blk_barrier_rq(vbr->req))
+ if (vbr->req->cmd_flags & REQ_HARDBARRIER)
vbr->out_hdr.type |= VIRTIO_BLK_T_BARRIER;
sg_set_buf(&vblk->sg[out++], &vbr->out_hdr, sizeof(vbr->out_hdr));
* block, and before the normal inhdr we put the sense data and the
* inhdr with additional status information before the normal inhdr.
*/
- if (blk_pc_request(vbr->req))
+ if (vbr->req->cmd_type == REQ_TYPE_BLOCK_PC)
sg_set_buf(&vblk->sg[out++], vbr->req->cmd, vbr->req->cmd_len);
num = blk_rq_map_sg(q, vbr->req, vblk->sg + out);
- if (blk_pc_request(vbr->req)) {
+ if (vbr->req->cmd_type == REQ_TYPE_BLOCK_PC) {
sg_set_buf(&vblk->sg[num + out + in++], vbr->req->sense, 96);
sg_set_buf(&vblk->sg[num + out + in++], &vbr->in_hdr,
sizeof(vbr->in_hdr));
virtqueue_kick(vblk->vq);
}
-static void virtblk_prepare_flush(struct request_queue *q, struct request *req)
-{
- req->cmd_type = REQ_TYPE_LINUX_BLOCK;
- req->cmd[0] = REQ_LB_OP_FLUSH;
-}
-
/* return id (s/n) string for *disk to *id_str
*/
static int virtblk_get_id(struct gendisk *disk, char *id_str)
return blk_execute_rq(vblk->disk->queue, vblk->disk, req, false);
}
-static int virtblk_ioctl(struct block_device *bdev, fmode_t mode,
+static int virtblk_locked_ioctl(struct block_device *bdev, fmode_t mode,
unsigned cmd, unsigned long data)
{
struct gendisk *disk = bdev->bd_disk;
(void __user *)data);
}
+static int virtblk_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long param)
+{
+ int ret;
+
+ lock_kernel();
+ ret = virtblk_locked_ioctl(bdev, mode, cmd, param);
+ unlock_kernel();
+
+ return ret;
+}
+
/* We provide getgeo only to please some old bootloader/partitioning tools */
static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo)
{
}
static const struct block_device_operations virtblk_fops = {
- .locked_ioctl = virtblk_ioctl,
+ .ioctl = virtblk_ioctl,
.owner = THIS_MODULE,
.getgeo = virtblk_getgeo,
};
* flushing a volatile write cache on the host. Use that
* to implement write barrier support.
*/
- blk_queue_ordered(q, QUEUE_ORDERED_DRAIN_FLUSH,
- virtblk_prepare_flush);
+ blk_queue_ordered(q, QUEUE_ORDERED_DRAIN_FLUSH);
} else if (virtio_has_feature(vdev, VIRTIO_BLK_F_BARRIER)) {
/*
* If the BARRIER feature is supported the host expects us
* never re-orders outstanding I/O. This feature is not
* useful for real life scenarious and deprecated.
*/
- blk_queue_ordered(q, QUEUE_ORDERED_TAG, NULL);
+ blk_queue_ordered(q, QUEUE_ORDERED_TAG);
} else {
/*
* If the FLUSH feature is not supported we must assume that
* caching. We still need to drain the queue to provider
* proper barrier semantics.
*/
- blk_queue_ordered(q, QUEUE_ORDERED_DRAIN, NULL);
+ blk_queue_ordered(q, QUEUE_ORDERED_DRAIN);
}
/* If disk is read-only in the host, the guest should obey */
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/blkdev.h>
+#include <linux/smp_lock.h>
#include <linux/blkpg.h>
#include <linux/delay.h>
#include <linux/io.h>
static const struct block_device_operations xd_fops = {
.owner = THIS_MODULE,
- .locked_ioctl = xd_ioctl,
+ .ioctl = xd_ioctl,
.getgeo = xd_getgeo,
};
static DECLARE_WAIT_QUEUE_HEAD(xd_wait_int);
int res = -EIO;
int retry;
- if (!blk_fs_request(req))
+ if (req->cmd_type != REQ_TYPE_FS)
goto done;
if (block + count > get_capacity(req->rq_disk))
goto done;
}
/* xd_ioctl: handle device ioctl's */
-static int xd_ioctl(struct block_device *bdev, fmode_t mode, u_int cmd, u_long arg)
+static int xd_locked_ioctl(struct block_device *bdev, fmode_t mode, u_int cmd, u_long arg)
{
switch (cmd) {
case HDIO_SET_DMA:
}
}
+static int xd_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long param)
+{
+ int ret;
+
+ lock_kernel();
+ ret = xd_locked_ioctl(bdev, mode, cmd, param);
+ unlock_kernel();
+
+ return ret;
+}
+
/* xd_readwrite: handle a read/write request */
static int xd_readwrite (u_char operation,XD_INFO *p,char *buffer,u_int block,u_int count)
{
#include <linux/cdrom.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/smp_lock.h>
#include <linux/scatterlist.h>
#include <xen/xen.h>
*/
struct blkfront_info
{
+ struct mutex mutex;
struct xenbus_device *xbdev;
struct gendisk *gd;
int vdevice;
unsigned long shadow_free;
int feature_barrier;
int is_ready;
-
- /**
- * The number of people holding this device open. We won't allow a
- * hot-unplug unless this is 0.
- */
- int users;
};
static DEFINE_SPINLOCK(blkif_io_lock);
+static unsigned int nr_minors;
+static unsigned long *minors;
+static DEFINE_SPINLOCK(minor_lock);
+
#define MAXIMUM_OUTSTANDING_BLOCK_REQS \
(BLKIF_MAX_SEGMENTS_PER_REQUEST * BLK_RING_SIZE)
#define GRANT_INVALID_REF 0
info->shadow_free = id;
}
+static int xlbd_reserve_minors(unsigned int minor, unsigned int nr)
+{
+ unsigned int end = minor + nr;
+ int rc;
+
+ if (end > nr_minors) {
+ unsigned long *bitmap, *old;
+
+ bitmap = kzalloc(BITS_TO_LONGS(end) * sizeof(*bitmap),
+ GFP_KERNEL);
+ if (bitmap == NULL)
+ return -ENOMEM;
+
+ spin_lock(&minor_lock);
+ if (end > nr_minors) {
+ old = minors;
+ memcpy(bitmap, minors,
+ BITS_TO_LONGS(nr_minors) * sizeof(*bitmap));
+ minors = bitmap;
+ nr_minors = BITS_TO_LONGS(end) * BITS_PER_LONG;
+ } else
+ old = bitmap;
+ spin_unlock(&minor_lock);
+ kfree(old);
+ }
+
+ spin_lock(&minor_lock);
+ if (find_next_bit(minors, end, minor) >= end) {
+ for (; minor < end; ++minor)
+ __set_bit(minor, minors);
+ rc = 0;
+ } else
+ rc = -EBUSY;
+ spin_unlock(&minor_lock);
+
+ return rc;
+}
+
+static void xlbd_release_minors(unsigned int minor, unsigned int nr)
+{
+ unsigned int end = minor + nr;
+
+ BUG_ON(end > nr_minors);
+ spin_lock(&minor_lock);
+ for (; minor < end; ++minor)
+ __clear_bit(minor, minors);
+ spin_unlock(&minor_lock);
+}
+
static void blkif_restart_queue_callback(void *arg)
{
struct blkfront_info *info = (struct blkfront_info *)arg;
ring_req->operation = rq_data_dir(req) ?
BLKIF_OP_WRITE : BLKIF_OP_READ;
- if (blk_barrier_rq(req))
+ if (req->cmd_flags & REQ_HARDBARRIER)
ring_req->operation = BLKIF_OP_WRITE_BARRIER;
ring_req->nr_segments = blk_rq_map_sg(req->q, req, info->sg);
blk_start_request(req);
- if (!blk_fs_request(req)) {
+ if (req->cmd_type != REQ_TYPE_FS) {
__blk_end_request_all(req, -EIO);
continue;
}
static int xlvbd_barrier(struct blkfront_info *info)
{
int err;
+ const char *barrier;
- err = blk_queue_ordered(info->rq,
- info->feature_barrier ? QUEUE_ORDERED_DRAIN : QUEUE_ORDERED_NONE,
- NULL);
+ switch (info->feature_barrier) {
+ case QUEUE_ORDERED_DRAIN: barrier = "enabled (drain)"; break;
+ case QUEUE_ORDERED_TAG: barrier = "enabled (tag)"; break;
+ case QUEUE_ORDERED_NONE: barrier = "disabled"; break;
+ default: return -EINVAL;
+ }
+
+ err = blk_queue_ordered(info->rq, info->feature_barrier);
if (err)
return err;
printk(KERN_INFO "blkfront: %s: barriers %s\n",
- info->gd->disk_name,
- info->feature_barrier ? "enabled" : "disabled");
+ info->gd->disk_name, barrier);
return 0;
}
if ((minor % nr_parts) == 0)
nr_minors = nr_parts;
+ err = xlbd_reserve_minors(minor, nr_minors);
+ if (err)
+ goto out;
+ err = -ENODEV;
+
gd = alloc_disk(nr_minors);
if (gd == NULL)
- goto out;
+ goto release;
offset = minor / nr_parts;
if (xlvbd_init_blk_queue(gd, sector_size)) {
del_gendisk(gd);
- goto out;
+ goto release;
}
info->rq = gd->queue;
info->gd = gd;
- if (info->feature_barrier)
- xlvbd_barrier(info);
+ xlvbd_barrier(info);
if (vdisk_info & VDISK_READONLY)
set_disk_ro(gd, 1);
return 0;
+ release:
+ xlbd_release_minors(minor, nr_minors);
out:
return err;
}
+static void xlvbd_release_gendisk(struct blkfront_info *info)
+{
+ unsigned int minor, nr_minors;
+ unsigned long flags;
+
+ if (info->rq == NULL)
+ return;
+
+ spin_lock_irqsave(&blkif_io_lock, flags);
+
+ /* No more blkif_request(). */
+ blk_stop_queue(info->rq);
+
+ /* No more gnttab callback work. */
+ gnttab_cancel_free_callback(&info->callback);
+ spin_unlock_irqrestore(&blkif_io_lock, flags);
+
+ /* Flush gnttab callback work. Must be done with no locks held. */
+ flush_scheduled_work();
+
+ del_gendisk(info->gd);
+
+ minor = info->gd->first_minor;
+ nr_minors = info->gd->minors;
+ xlbd_release_minors(minor, nr_minors);
+
+ blk_cleanup_queue(info->rq);
+ info->rq = NULL;
+
+ put_disk(info->gd);
+ info->gd = NULL;
+}
+
static void kick_pending_request_queues(struct blkfront_info *info)
{
if (!RING_FULL(&info->ring)) {
printk(KERN_WARNING "blkfront: %s: write barrier op failed\n",
info->gd->disk_name);
error = -EOPNOTSUPP;
- info->feature_barrier = 0;
+ info->feature_barrier = QUEUE_ORDERED_NONE;
xlvbd_barrier(info);
}
/* fall through */
/* Common code used when first setting up, and when resuming. */
-static int talk_to_backend(struct xenbus_device *dev,
+static int talk_to_blkback(struct xenbus_device *dev,
struct blkfront_info *info)
{
const char *message = NULL;
return err;
}
-
/**
* Entry point to this code when a new device is created. Allocate the basic
* structures and the ring buffer for communication with the backend, and
return -ENOMEM;
}
+ mutex_init(&info->mutex);
info->xbdev = dev;
info->vdevice = vdevice;
info->connected = BLKIF_STATE_DISCONNECTED;
info->handle = simple_strtoul(strrchr(dev->nodename, '/')+1, NULL, 0);
dev_set_drvdata(&dev->dev, info);
- err = talk_to_backend(dev, info);
+ err = talk_to_blkback(dev, info);
if (err) {
kfree(info);
dev_set_drvdata(&dev->dev, NULL);
blkif_free(info, info->connected == BLKIF_STATE_CONNECTED);
- err = talk_to_backend(dev, info);
+ err = talk_to_blkback(dev, info);
if (info->connected == BLKIF_STATE_SUSPENDED && !err)
err = blkif_recover(info);
return err;
}
+static void
+blkfront_closing(struct blkfront_info *info)
+{
+ struct xenbus_device *xbdev = info->xbdev;
+ struct block_device *bdev = NULL;
+
+ mutex_lock(&info->mutex);
+
+ if (xbdev->state == XenbusStateClosing) {
+ mutex_unlock(&info->mutex);
+ return;
+ }
+
+ if (info->gd)
+ bdev = bdget_disk(info->gd, 0);
+
+ mutex_unlock(&info->mutex);
+
+ if (!bdev) {
+ xenbus_frontend_closed(xbdev);
+ return;
+ }
+
+ mutex_lock(&bdev->bd_mutex);
+
+ if (bdev->bd_openers) {
+ xenbus_dev_error(xbdev, -EBUSY,
+ "Device in use; refusing to close");
+ xenbus_switch_state(xbdev, XenbusStateClosing);
+ } else {
+ xlvbd_release_gendisk(info);
+ xenbus_frontend_closed(xbdev);
+ }
+
+ mutex_unlock(&bdev->bd_mutex);
+ bdput(bdev);
+}
/*
* Invoked when the backend is finally 'ready' (and has told produced
unsigned long sector_size;
unsigned int binfo;
int err;
-
- if ((info->connected == BLKIF_STATE_CONNECTED) ||
- (info->connected == BLKIF_STATE_SUSPENDED) )
+ int barrier;
+
+ switch (info->connected) {
+ case BLKIF_STATE_CONNECTED:
+ /*
+ * Potentially, the back-end may be signalling
+ * a capacity change; update the capacity.
+ */
+ err = xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+ "sectors", "%Lu", §ors);
+ if (XENBUS_EXIST_ERR(err))
+ return;
+ printk(KERN_INFO "Setting capacity to %Lu\n",
+ sectors);
+ set_capacity(info->gd, sectors);
+ revalidate_disk(info->gd);
+
+ /* fall through */
+ case BLKIF_STATE_SUSPENDED:
return;
+ default:
+ break;
+ }
+
dev_dbg(&info->xbdev->dev, "%s:%s.\n",
__func__, info->xbdev->otherend);
}
err = xenbus_gather(XBT_NIL, info->xbdev->otherend,
- "feature-barrier", "%lu", &info->feature_barrier,
+ "feature-barrier", "%lu", &barrier,
NULL);
+
+ /*
+ * If there's no "feature-barrier" defined, then it means
+ * we're dealing with a very old backend which writes
+ * synchronously; draining will do what needs to get done.
+ *
+ * If there are barriers, then we can do full queued writes
+ * with tagged barriers.
+ *
+ * If barriers are not supported, then there's no much we can
+ * do, so just set ordering to NONE.
+ */
if (err)
- info->feature_barrier = 0;
+ info->feature_barrier = QUEUE_ORDERED_DRAIN;
+ else if (barrier)
+ info->feature_barrier = QUEUE_ORDERED_TAG;
+ else
+ info->feature_barrier = QUEUE_ORDERED_NONE;
err = xlvbd_alloc_gendisk(sectors, info, binfo, sector_size);
if (err) {
info->is_ready = 1;
}
-/**
- * Handle the change of state of the backend to Closing. We must delete our
- * device-layer structures now, to ensure that writes are flushed through to
- * the backend. Once is this done, we can switch to Closed in
- * acknowledgement.
- */
-static void blkfront_closing(struct xenbus_device *dev)
-{
- struct blkfront_info *info = dev_get_drvdata(&dev->dev);
- unsigned long flags;
-
- dev_dbg(&dev->dev, "blkfront_closing: %s removed\n", dev->nodename);
-
- if (info->rq == NULL)
- goto out;
-
- spin_lock_irqsave(&blkif_io_lock, flags);
-
- /* No more blkif_request(). */
- blk_stop_queue(info->rq);
-
- /* No more gnttab callback work. */
- gnttab_cancel_free_callback(&info->callback);
- spin_unlock_irqrestore(&blkif_io_lock, flags);
-
- /* Flush gnttab callback work. Must be done with no locks held. */
- flush_scheduled_work();
-
- blk_cleanup_queue(info->rq);
- info->rq = NULL;
-
- del_gendisk(info->gd);
-
- out:
- xenbus_frontend_closed(dev);
-}
-
/**
* Callback received when the backend's state changes.
*/
-static void backend_changed(struct xenbus_device *dev,
+static void blkback_changed(struct xenbus_device *dev,
enum xenbus_state backend_state)
{
struct blkfront_info *info = dev_get_drvdata(&dev->dev);
- struct block_device *bd;
- dev_dbg(&dev->dev, "blkfront:backend_changed.\n");
+ dev_dbg(&dev->dev, "blkfront:blkback_changed to state %d.\n", backend_state);
switch (backend_state) {
case XenbusStateInitialising:
break;
case XenbusStateClosing:
- if (info->gd == NULL) {
- xenbus_frontend_closed(dev);
- break;
- }
- bd = bdget_disk(info->gd, 0);
- if (bd == NULL)
- xenbus_dev_fatal(dev, -ENODEV, "bdget failed");
-
- mutex_lock(&bd->bd_mutex);
- if (info->users > 0)
- xenbus_dev_error(dev, -EBUSY,
- "Device in use; refusing to close");
- else
- blkfront_closing(dev);
- mutex_unlock(&bd->bd_mutex);
- bdput(bd);
+ blkfront_closing(info);
break;
}
}
-static int blkfront_remove(struct xenbus_device *dev)
+static int blkfront_remove(struct xenbus_device *xbdev)
{
- struct blkfront_info *info = dev_get_drvdata(&dev->dev);
+ struct blkfront_info *info = dev_get_drvdata(&xbdev->dev);
+ struct block_device *bdev = NULL;
+ struct gendisk *disk;
- dev_dbg(&dev->dev, "blkfront_remove: %s removed\n", dev->nodename);
+ dev_dbg(&xbdev->dev, "%s removed", xbdev->nodename);
blkif_free(info, 0);
- kfree(info);
+ mutex_lock(&info->mutex);
+
+ disk = info->gd;
+ if (disk)
+ bdev = bdget_disk(disk, 0);
+
+ info->xbdev = NULL;
+ mutex_unlock(&info->mutex);
+
+ if (!bdev) {
+ kfree(info);
+ return 0;
+ }
+
+ /*
+ * The xbdev was removed before we reached the Closed
+ * state. See if it's safe to remove the disk. If the bdev
+ * isn't closed yet, we let release take care of it.
+ */
+
+ mutex_lock(&bdev->bd_mutex);
+ info = disk->private_data;
+
+ dev_warn(disk_to_dev(disk),
+ "%s was hot-unplugged, %d stale handles\n",
+ xbdev->nodename, bdev->bd_openers);
+
+ if (info && !bdev->bd_openers) {
+ xlvbd_release_gendisk(info);
+ disk->private_data = NULL;
+ kfree(info);
+ }
+
+ mutex_unlock(&bdev->bd_mutex);
+ bdput(bdev);
return 0;
}
{
struct blkfront_info *info = dev_get_drvdata(&dev->dev);
- return info->is_ready;
+ return info->is_ready && info->xbdev;
}
static int blkif_open(struct block_device *bdev, fmode_t mode)
{
- struct blkfront_info *info = bdev->bd_disk->private_data;
- info->users++;
- return 0;
+ struct gendisk *disk = bdev->bd_disk;
+ struct blkfront_info *info;
+ int err = 0;
+
+ lock_kernel();
+
+ info = disk->private_data;
+ if (!info) {
+ /* xbdev gone */
+ err = -ERESTARTSYS;
+ goto out;
+ }
+
+ mutex_lock(&info->mutex);
+
+ if (!info->gd)
+ /* xbdev is closed */
+ err = -ERESTARTSYS;
+
+ mutex_unlock(&info->mutex);
+
+out:
+ unlock_kernel();
+ return err;
}
static int blkif_release(struct gendisk *disk, fmode_t mode)
{
struct blkfront_info *info = disk->private_data;
- info->users--;
- if (info->users == 0) {
- /* Check whether we have been instructed to close. We will
- have ignored this request initially, as the device was
- still mounted. */
- struct xenbus_device *dev = info->xbdev;
- enum xenbus_state state = xenbus_read_driver_state(dev->otherend);
-
- if (state == XenbusStateClosing && info->is_ready)
- blkfront_closing(dev);
+ struct block_device *bdev;
+ struct xenbus_device *xbdev;
+
+ lock_kernel();
+
+ bdev = bdget_disk(disk, 0);
+ bdput(bdev);
+
+ if (bdev->bd_openers)
+ goto out;
+
+ /*
+ * Check if we have been instructed to close. We will have
+ * deferred this request, because the bdev was still open.
+ */
+
+ mutex_lock(&info->mutex);
+ xbdev = info->xbdev;
+
+ if (xbdev && xbdev->state == XenbusStateClosing) {
+ /* pending switch to state closed */
+ dev_info(disk_to_dev(bdev->bd_disk), "releasing disk\n");
+ xlvbd_release_gendisk(info);
+ xenbus_frontend_closed(info->xbdev);
+ }
+
+ mutex_unlock(&info->mutex);
+
+ if (!xbdev) {
+ /* sudden device removal */
+ dev_info(disk_to_dev(bdev->bd_disk), "releasing disk\n");
+ xlvbd_release_gendisk(info);
+ disk->private_data = NULL;
+ kfree(info);
}
+
+out:
+ unlock_kernel();
return 0;
}
.open = blkif_open,
.release = blkif_release,
.getgeo = blkif_getgeo,
- .locked_ioctl = blkif_ioctl,
+ .ioctl = blkif_ioctl,
};
.probe = blkfront_probe,
.remove = blkfront_remove,
.resume = blkfront_resume,
- .otherend_changed = backend_changed,
+ .otherend_changed = blkback_changed,
.is_ready = blkfront_is_ready,
};
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
+#include <linux/smp_lock.h>
#include <linux/ata.h>
#include <linux/hdreg.h>
#include <linux/platform_device.h>
struct request *req;
while ((req = blk_peek_request(q)) != NULL) {
- if (blk_fs_request(req))
+ if (req->cmd_type == REQ_TYPE_FS)
break;
blk_start_request(req);
__blk_end_request_all(req, -EIO);
dev_dbg(ace->dev, "ace_open() users=%i\n", ace->users + 1);
+ lock_kernel();
spin_lock_irqsave(&ace->lock, flags);
ace->users++;
spin_unlock_irqrestore(&ace->lock, flags);
check_disk_change(bdev);
+ unlock_kernel();
+
return 0;
}
dev_dbg(ace->dev, "ace_release() users=%i\n", ace->users - 1);
+ lock_kernel();
spin_lock_irqsave(&ace->lock, flags);
ace->users--;
if (ace->users == 0) {
ace_out(ace, ACE_CTRL, val & ~ACE_CTRL_LOCKREQ);
}
spin_unlock_irqrestore(&ace->lock, flags);
+ unlock_kernel();
return 0;
}
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/bitops.h>
+#include <linux/smp_lock.h>
#include <linux/slab.h>
#include <asm/setup.h>
device = MINOR(bdev->bd_dev);
+ lock_kernel();
if ( current_device != -1 && current_device != device )
{
rc = -EBUSY;
set_capacity(z2ram_gendisk, z2ram_size >> 9);
}
+ unlock_kernel();
return 0;
err_out_kfree:
kfree(z2ram_map);
err_out:
+ unlock_kernel();
return rc;
}
static int
z2_release(struct gendisk *disk, fmode_t mode)
{
- if ( current_device == -1 )
- return 0;
-
+ lock_kernel();
+ if ( current_device == -1 ) {
+ unlock_kernel();
+ return 0;
+ }
+ unlock_kernel();
/*
* FIXME: unmap memory
*/
-------------------------------------------------------------------------*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define REVISION "Revision: 3.20"
#define VERSION "Id: cdrom.c 3.20 2003/12/17"
static const char *mrw_address_space[] = { "DMA", "GAA" };
#if (ERRLOGMASK!=CD_NOTHING)
-#define cdinfo(type, fmt, args...) \
- if ((ERRLOGMASK & type) || debug==1 ) \
- printk(KERN_INFO "cdrom: " fmt, ## args)
+#define cdinfo(type, fmt, args...) \
+do { \
+ if ((ERRLOGMASK & type) || debug == 1) \
+ pr_info(fmt, ##args); \
+} while (0)
#else
-#define cdinfo(type, fmt, args...)
+#define cdinfo(type, fmt, args...) \
+do { \
+ if (0 && (ERRLOGMASK & type) || debug == 1) \
+ pr_info(fmt, ##args); \
+} while (0)
#endif
/* These are used to simplify getting data in from and back to user land */
if (cdo->open == NULL || cdo->release == NULL)
return -EINVAL;
if (!banner_printed) {
- printk(KERN_INFO "Uniform CD-ROM driver " REVISION "\n");
+ pr_info("Uniform CD-ROM driver " REVISION "\n");
banner_printed = 1;
cdrom_sysctl_register();
}
unsigned char buffer[12];
int ret;
- printk(KERN_INFO "cdrom: %sstarting format\n", cont ? "Re" : "");
+ pr_info("%sstarting format\n", cont ? "Re" : "");
/*
* FmtData bit set (bit 4), format type is 1
ret = cdi->ops->generic_packet(cdi, &cgc);
if (ret)
- printk(KERN_INFO "cdrom: bgformat failed\n");
+ pr_info("bgformat failed\n");
return ret;
}
ret = 0;
if (di.mrw_status == CDM_MRW_BGFORMAT_ACTIVE) {
- printk(KERN_INFO "cdrom: issuing MRW back ground "
- "format suspend\n");
+ pr_info("issuing MRW background format suspend\n");
ret = cdrom_mrw_bgformat_susp(cdi, 0);
}
if ((ret = cdrom_mode_select(cdi, &cgc)))
return ret;
- printk(KERN_INFO "cdrom: %s: mrw address space %s selected\n", cdi->name, mrw_address_space[space]);
+ pr_info("%s: mrw address space %s selected\n",
+ cdi->name, mrw_address_space[space]);
return 0;
}
* always reset to DMA lba space on open
*/
if (cdrom_mrw_set_lba_space(cdi, MRW_LBA_DMA)) {
- printk(KERN_ERR "cdrom: failed setting lba address space\n");
+ pr_err("failed setting lba address space\n");
return 1;
}
* 3 - MRW formatting complete
*/
ret = 0;
- printk(KERN_INFO "cdrom open: mrw_status '%s'\n",
- mrw_format_status[di.mrw_status]);
+ pr_info("open: mrw_status '%s'\n", mrw_format_status[di.mrw_status]);
if (!di.mrw_status)
ret = 1;
else if (di.mrw_status == CDM_MRW_BGFORMAT_INACTIVE &&
return;
}
- printk(KERN_INFO "cdrom: %s: dirty DVD+RW media, \"finalizing\"\n",
- cdi->name);
+ pr_info("%s: dirty DVD+RW media, \"finalizing\"\n", cdi->name);
init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
cgc.cmd[0] = GPCMD_FLUSH_CACHE;
* frame dma, so drop to single frame dma if we need to
*/
if (cdi->cdda_method == CDDA_BPC_FULL && nframes > 1) {
- printk("cdrom: dropping to single frame dma\n");
+ pr_info("dropping to single frame dma\n");
cdi->cdda_method = CDDA_BPC_SINGLE;
goto retry;
}
if (cdi->last_sense != 0x04 && cdi->last_sense != 0x0b)
return ret;
- printk("cdrom: dropping to old style cdda (sense=%x)\n", cdi->last_sense);
+ pr_info("dropping to old style cdda (sense=%x)\n", cdi->last_sense);
cdi->cdda_method = CDDA_OLD;
return cdrom_read_cdda_old(cdi, ubuf, lba, nframes);
}
"\t%d", CDROM_CAN(val) != 0);
break;
default:
- printk(KERN_INFO "cdrom: invalid option%d\n", option);
+ pr_info("invalid option%d\n", option);
return 1;
}
if (!ret)
mutex_unlock(&cdrom_mutex);
return proc_dostring(ctl, write, buffer, lenp, ppos);
done:
- printk(KERN_INFO "cdrom: info buffer too small\n");
+ pr_info("info buffer too small\n");
goto doit;
}
static void __exit cdrom_exit(void)
{
- printk(KERN_INFO "Uniform CD-ROM driver unloaded\n");
+ pr_info("Uniform CD-ROM driver unloaded\n");
cdrom_sysctl_unregister();
}
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/blkdev.h>
#include <linux/interrupt.h>
#include <linux/device.h>
+#include <linux/smp_lock.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
tocuse = 0;
err = gdrom_readtoc_cmd(gd.toc, 0);
if (err) {
- printk(KERN_INFO "GDROM: Could not get CD "
- "table of contents\n");
+ pr_info("Could not get CD table of contents\n");
return -ENXIO;
}
}
} while (track >= fentry);
if ((track > 100) || (track < get_entry_track(gd.toc->first))) {
- printk(KERN_INFO "GDROM: No data on the last "
- "session of the CD\n");
+ pr_info("No data on the last session of the CD\n");
gdrom_getsense(NULL);
return -ENXIO;
}
goto cleanup_sense;
insw(GDROM_DATA_REG, &sense, sense_command->buflen/2);
if (sense[1] & 40) {
- printk(KERN_INFO "GDROM: Drive not ready - command aborted\n");
+ pr_info("Drive not ready - command aborted\n");
goto cleanup_sense;
}
sense_key = sense[1] & 0x0F;
if (sense_key < ARRAY_SIZE(sense_texts))
- printk(KERN_INFO "GDROM: %s\n", sense_texts[sense_key].text);
+ pr_info("%s\n", sense_texts[sense_key].text);
else
- printk(KERN_ERR "GDROM: Unknown sense key: %d\n", sense_key);
+ pr_err("Unknown sense key: %d\n", sense_key);
if (bufstring) /* return addional sense data */
memcpy(bufstring, &sense[4], 2);
if (sense_key < 2)
static int gdrom_bdops_open(struct block_device *bdev, fmode_t mode)
{
- return cdrom_open(gd.cd_info, bdev, mode);
+ int ret;
+ lock_kernel();
+ ret = cdrom_open(gd.cd_info, bdev, mode);
+ unlock_kernel();
+ return ret;
}
static int gdrom_bdops_release(struct gendisk *disk, fmode_t mode)
{
+ lock_kernel();
cdrom_release(gd.cd_info, mode);
+ unlock_kernel();
return 0;
}
static int gdrom_bdops_ioctl(struct block_device *bdev, fmode_t mode,
unsigned cmd, unsigned long arg)
{
- return cdrom_ioctl(gd.cd_info, bdev, mode, cmd, arg);
+ int ret;
+
+ lock_kernel();
+ ret = cdrom_ioctl(gd.cd_info, bdev, mode, cmd, arg);
+ unlock_kernel();
+
+ return ret;
}
static const struct block_device_operations gdrom_bdops = {
.open = gdrom_bdops_open,
.release = gdrom_bdops_release,
.media_changed = gdrom_bdops_mediachanged,
- .locked_ioctl = gdrom_bdops_ioctl,
+ .ioctl = gdrom_bdops_ioctl,
};
static irqreturn_t gdrom_command_interrupt(int irq, void *dev_id)
struct request *req;
while ((req = blk_fetch_request(rq)) != NULL) {
- if (!blk_fs_request(req)) {
- printk(KERN_DEBUG "GDROM: Non-fs request ignored\n");
+ if (req->cmd_type != REQ_TYPE_FS) {
+ printk(KERN_DEBUG "gdrom: Non-fs request ignored\n");
__blk_end_request_all(req, -EIO);
continue;
}
if (rq_data_dir(req) != READ) {
- printk(KERN_NOTICE "GDROM: Read only device -");
- printk(" write request ignored\n");
+ pr_notice("Read only device - write request ignored\n");
__blk_end_request_all(req, -EIO);
continue;
}
firmw_ver = kstrndup(id->firmver, 16, GFP_KERNEL);
if (!firmw_ver)
goto free_manuf_name;
- printk(KERN_INFO "GDROM: %s from %s with firmware %s\n",
+ pr_info("%s from %s with firmware %s\n",
model_name, manuf_name, firmw_ver);
err = 0;
kfree(firmw_ver);
int err;
/* Start the device */
if (gdrom_execute_diagnostic() != 1) {
- printk(KERN_WARNING "GDROM: ATA Probe for GDROM failed.\n");
+ pr_warning("ATA Probe for GDROM failed\n");
return -ENODEV;
}
/* Print out firmware ID */
gdrom_major = register_blkdev(0, GDROM_DEV_NAME);
if (gdrom_major <= 0)
return gdrom_major;
- printk(KERN_INFO "GDROM: Registered with major number %d\n",
+ pr_info("Registered with major number %d\n",
gdrom_major);
/* Specify basic properties of drive */
gd.cd_info = kzalloc(sizeof(struct cdrom_device_info), GFP_KERNEL);
unregister_blkdev(gdrom_major, GDROM_DEV_NAME);
gdrom_major = 0;
probe_fail_no_mem:
- printk(KERN_WARNING "GDROM: Probe failed - error is 0x%X\n", err);
+ pr_warning("Probe failed - error is 0x%X\n", err);
return err;
}
* the OS/400 partition.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/major.h>
#include <linux/blkdev.h>
#include <linux/cdrom.h>
#include <linux/module.h>
#include <linux/completion.h>
#include <linux/proc_fs.h>
+#include <linux/smp_lock.h>
#include <linux/seq_file.h>
#include <linux/scatterlist.h>
#define VIOCD_VERS "1.06"
-#define VIOCD_KERN_WARNING KERN_WARNING "viocd: "
-#define VIOCD_KERN_INFO KERN_INFO "viocd: "
-
/*
* Should probably make this a module parameter....sigh
*/
static int viocd_blk_open(struct block_device *bdev, fmode_t mode)
{
struct disk_info *di = bdev->bd_disk->private_data;
- return cdrom_open(&di->viocd_info, bdev, mode);
+ int ret;
+
+ lock_kernel();
+ ret = cdrom_open(&di->viocd_info, bdev, mode);
+ unlock_kernel();
+
+ return ret;
}
static int viocd_blk_release(struct gendisk *disk, fmode_t mode)
{
struct disk_info *di = disk->private_data;
+ lock_kernel();
cdrom_release(&di->viocd_info, mode);
+ unlock_kernel();
return 0;
}
unsigned cmd, unsigned long arg)
{
struct disk_info *di = bdev->bd_disk->private_data;
- return cdrom_ioctl(&di->viocd_info, bdev, mode, cmd, arg);
+ int ret;
+
+ lock_kernel();
+ ret = cdrom_ioctl(&di->viocd_info, bdev, mode, cmd, arg);
+ unlock_kernel();
+
+ return ret;
}
static int viocd_blk_media_changed(struct gendisk *disk)
.owner = THIS_MODULE,
.open = viocd_blk_open,
.release = viocd_blk_release,
- .locked_ioctl = viocd_blk_ioctl,
+ .ioctl = viocd_blk_ioctl,
.media_changed = viocd_blk_media_changed,
};
(u64)&we, VIOVERSION << 16, ((u64)device_no << 48),
0, 0, 0);
if (hvrc != 0) {
- printk(VIOCD_KERN_WARNING
- "bad rc on HvCallEvent_signalLpEventFast %d\n",
- (int)hvrc);
+ pr_warning("bad rc on HvCallEvent_signalLpEventFast %d\n",
+ (int)hvrc);
return -EIO;
}
if (we.rc) {
const struct vio_error_entry *err =
vio_lookup_rc(viocd_err_table, we.sub_result);
- printk(VIOCD_KERN_WARNING "bad rc %d:0x%04X on open: %s\n",
- we.rc, we.sub_result, err->msg);
+ pr_warning("bad rc %d:0x%04X on open: %s\n",
+ we.rc, we.sub_result, err->msg);
return -err->errno;
}
viopath_targetinst(viopath_hostLp), 0,
VIOVERSION << 16, ((u64)device_no << 48), 0, 0, 0);
if (hvrc != 0)
- printk(VIOCD_KERN_WARNING
- "bad rc on HvCallEvent_signalLpEventFast %d\n",
- (int)hvrc);
+ pr_warning("bad rc on HvCallEvent_signalLpEventFast %d\n",
+ (int)hvrc);
}
/* Send a read or write request to OS/400 */
sg_init_table(&sg, 1);
if (blk_rq_map_sg(req->q, req, &sg) == 0) {
- printk(VIOCD_KERN_WARNING
- "error setting up scatter/gather list\n");
+ pr_warning("error setting up scatter/gather list\n");
return -1;
}
if (dma_map_sg(diskinfo->dev, &sg, 1, direction) == 0) {
- printk(VIOCD_KERN_WARNING "error allocating sg tce\n");
+ pr_warning("error allocating sg tce\n");
return -1;
}
dmaaddr = sg_dma_address(&sg);
((u64)DEVICE_NR(diskinfo) << 48) | dmaaddr,
(u64)blk_rq_pos(req) * 512, len, 0);
if (hvrc != HvLpEvent_Rc_Good) {
- printk(VIOCD_KERN_WARNING "hv error on op %d\n", (int)hvrc);
+ pr_warning("hv error on op %d\n", (int)hvrc);
return -1;
}
struct request *req;
while ((rwreq == 0) && ((req = blk_fetch_request(q)) != NULL)) {
- if (!blk_fs_request(req))
+ if (req->cmd_type != REQ_TYPE_FS)
__blk_end_request_all(req, -EIO);
else if (send_request(req) < 0) {
- printk(VIOCD_KERN_WARNING
- "unable to send message to OS/400!");
+ pr_warning("unable to send message to OS/400!\n");
__blk_end_request_all(req, -EIO);
} else
rwreq++;
(u64)&we, VIOVERSION << 16, ((u64)device_no << 48),
0, 0, 0);
if (hvrc != 0) {
- printk(VIOCD_KERN_WARNING "bad rc on HvCallEvent_signalLpEventFast %d\n",
- (int)hvrc);
+ pr_warning("bad rc on HvCallEvent_signalLpEventFast %d\n",
+ (int)hvrc);
return -EIO;
}
if (we.rc) {
const struct vio_error_entry *err =
vio_lookup_rc(viocd_err_table, we.sub_result);
- printk(VIOCD_KERN_WARNING
- "bad rc %d:0x%04X on check_change: %s; Assuming no change\n",
- we.rc, we.sub_result, err->msg);
+ pr_warning("bad rc %d:0x%04X on check_change: %s; Assuming no change\n",
+ we.rc, we.sub_result, err->msg);
return 0;
}
(u64)&we, VIOVERSION << 16,
(device_no << 48) | (flags << 32), 0, 0, 0);
if (hvrc != 0) {
- printk(VIOCD_KERN_WARNING "bad rc on HvCallEvent_signalLpEventFast %d\n",
- (int)hvrc);
+ pr_warning("bad rc on HvCallEvent_signalLpEventFast %d\n",
+ (int)hvrc);
return -EIO;
}
return;
/* First, we should NEVER get an int here...only acks */
if (hvlpevent_is_int(event)) {
- printk(VIOCD_KERN_WARNING
- "Yikes! got an int in viocd event handler!\n");
+ pr_warning("Yikes! got an int in viocd event handler!\n");
if (hvlpevent_need_ack(event)) {
event->xRc = HvLpEvent_Rc_InvalidSubtype;
HvCallEvent_ackLpEvent(event);
const struct vio_error_entry *err =
vio_lookup_rc(viocd_err_table,
bevent->sub_result);
- printk(VIOCD_KERN_WARNING "request %p failed "
- "with rc %d:0x%04X: %s\n",
- req, event->xRc,
- bevent->sub_result, err->msg);
+ pr_warning("request %p failed with rc %d:0x%04X: %s\n",
+ req, event->xRc,
+ bevent->sub_result, err->msg);
__blk_end_request_all(req, -EIO);
} else
__blk_end_request_all(req, 0);
break;
default:
- printk(VIOCD_KERN_WARNING
- "message with invalid subtype %0x04X!\n",
- event->xSubtype & VIOMINOR_SUBTYPE_MASK);
+ pr_warning("message with invalid subtype %0x04X!\n",
+ event->xSubtype & VIOMINOR_SUBTYPE_MASK);
if (hvlpevent_need_ack(event)) {
event->xRc = HvLpEvent_Rc_InvalidSubtype;
HvCallEvent_ackLpEvent(event);
sprintf(c->name, VIOCD_DEVICE "%c", 'a' + deviceno);
if (register_cdrom(c) != 0) {
- printk(VIOCD_KERN_WARNING "Cannot register viocd CD-ROM %s!\n",
- c->name);
+ pr_warning("Cannot register viocd CD-ROM %s!\n", c->name);
goto out;
}
- printk(VIOCD_KERN_INFO "cd %s is iSeries resource %10.10s "
- "type %4.4s, model %3.3s\n",
- c->name, d->rsrcname, d->type, d->model);
+ pr_info("cd %s is iSeries resource %10.10s type %4.4s, model %3.3s\n",
+ c->name, d->rsrcname, d->type, d->model);
q = blk_init_queue(do_viocd_request, &viocd_reqlock);
if (q == NULL) {
- printk(VIOCD_KERN_WARNING "Cannot allocate queue for %s!\n",
- c->name);
+ pr_warning("Cannot allocate queue for %s!\n", c->name);
goto out_unregister_cdrom;
}
gendisk = alloc_disk(1);
if (gendisk == NULL) {
- printk(VIOCD_KERN_WARNING "Cannot create gendisk for %s!\n",
- c->name);
+ pr_warning("Cannot create gendisk for %s!\n", c->name);
goto out_cleanup_queue;
}
gendisk->major = VIOCD_MAJOR;
return -ENODEV;
}
- printk(VIOCD_KERN_INFO "vers " VIOCD_VERS ", hosting partition %d\n",
- viopath_hostLp);
+ pr_info("vers " VIOCD_VERS ", hosting partition %d\n", viopath_hostLp);
if (register_blkdev(VIOCD_MAJOR, VIOCD_DEVICE) != 0) {
- printk(VIOCD_KERN_WARNING "Unable to get major %d for %s\n",
- VIOCD_MAJOR, VIOCD_DEVICE);
+ pr_warning("Unable to get major %d for %s\n",
+ VIOCD_MAJOR, VIOCD_DEVICE);
return -EIO;
}
ret = viopath_open(viopath_hostLp, viomajorsubtype_cdio,
MAX_CD_REQ + 2);
if (ret) {
- printk(VIOCD_KERN_WARNING
- "error opening path to host partition %d\n",
- viopath_hostLp);
+ pr_warning("error opening path to host partition %d\n",
+ viopath_hostLp);
goto out_unregister;
}
obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o tty_port.o
+obj-y += tty_mutex.o
obj-$(CONFIG_LEGACY_PTYS) += pty.o
obj-$(CONFIG_UNIX98_PTYS) += pty.o
obj-y += misc.o
if (!retinfo)
return -EFAULT;
memset(&tmp, 0, sizeof(tmp));
- lock_kernel();
+ tty_lock();
tmp.type = state->type;
tmp.line = state->line;
tmp.port = state->port;
tmp.close_delay = state->close_delay;
tmp.closing_wait = state->closing_wait;
tmp.custom_divisor = state->custom_divisor;
- unlock_kernel();
+ tty_unlock();
if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
return -EFAULT;
return 0;
if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
return -EFAULT;
- lock_kernel();
+ tty_lock();
state = info->state;
old_state = *state;
change_irq = new_serial.irq != state->irq;
change_port = (new_serial.port != state->port);
if(change_irq || change_port || (new_serial.xmit_fifo_size != state->xmit_fifo_size)) {
- unlock_kernel();
+ tty_unlock();
return -EINVAL;
}
}
if (new_serial.baud_base < 9600) {
- unlock_kernel();
+ tty_unlock();
return -EINVAL;
}
}
} else
retval = startup(info);
- unlock_kernel();
+ tty_unlock();
return retval;
}
{
struct async_struct * info = tty->driver_data;
unsigned long orig_jiffies, char_time;
+ int tty_was_locked = tty_locked();
int lsr;
if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent"))
orig_jiffies = jiffies;
- lock_kernel();
+ /*
+ * tty_wait_until_sent is called from lots of places,
+ * with or without the BTM.
+ */
+ if (!tty_was_locked)
+ tty_lock();
/*
* Set the check interval to be 1/5 of the estimated time to
* send a single character, and make it at least 1. The check
break;
}
__set_current_state(TASK_RUNNING);
- unlock_kernel();
+ if (!tty_was_locked)
+ tty_unlock();
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
#endif
printk("block_til_ready blocking: ttys%d, count = %d\n",
info->line, state->count);
#endif
+ tty_unlock();
schedule();
+ tty_lock();
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(&info->open_wait, &wait);
static int briq_panel_open(struct inode *ino, struct file *filep)
{
- lock_kernel();
+ tty_lock();
/* enforce single access, vfd_is_open is protected by BKL */
if (vfd_is_open) {
- unlock_kernel();
+ tty_unlock();
return -EBUSY;
}
vfd_is_open = 1;
- unlock_kernel();
+ tty_unlock();
return 0;
}
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
-#include <linux/smp_lock.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
* If the port is the middle of closing, bail out now
*/
if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) {
- wait_event_interruptible(info->port.close_wait,
+ wait_event_interruptible_tty(info->port.close_wait,
!(info->port.flags & ASYNC_CLOSING));
return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS;
}
return; /* Just in case.... */
orig_jiffies = jiffies;
- lock_kernel();
/*
* Set the check interval to be 1/5 of the estimated time to
* send a single character, and make it at least 1. The check
}
/* Run one more char cycle */
msleep_interruptible(jiffies_to_msecs(char_time * 5));
- unlock_kernel();
#ifdef CY_DEBUG_WAIT_UNTIL_SENT
printk(KERN_DEBUG "Clean (jiff=%lu)...done\n", jiffies);
#endif
int char_count;
__u32 tx_put, tx_get, tx_bufsize;
- lock_kernel();
tx_get = readl(&buf_ctrl->tx_get);
tx_put = readl(&buf_ctrl->tx_put);
tx_bufsize = readl(&buf_ctrl->tx_bufsize);
printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n",
info->line, info->xmit_cnt + char_count);
#endif
- unlock_kernel();
return info->xmit_cnt + char_count;
}
#endif /* Z_EXT_CHARS_IN_BUFFER */
struct serial_struct __user *new_info)
{
struct serial_struct new_serial;
+ int ret;
if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
return -EFAULT;
+ mutex_lock(&info->port.mutex);
if (!capable(CAP_SYS_ADMIN)) {
if (new_serial.close_delay != info->port.close_delay ||
new_serial.baud_base != info->baud ||
(new_serial.flags & ASYNC_FLAGS &
~ASYNC_USR_MASK) !=
(info->port.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK))
+ {
+ mutex_unlock(&info->port.mutex);
return -EPERM;
+ }
info->port.flags = (info->port.flags & ~ASYNC_USR_MASK) |
(new_serial.flags & ASYNC_USR_MASK);
info->baud = new_serial.baud_base;
check_and_exit:
if (info->port.flags & ASYNC_INITIALIZED) {
cy_set_line_char(info, tty);
- return 0;
+ ret = 0;
} else {
- return cy_startup(info, tty);
+ ret = cy_startup(info, tty);
}
+ mutex_unlock(&info->port.mutex);
+ return ret;
} /* set_serial_info */
/*
card = info->card;
- lock_kernel();
if (!cy_is_Z(card)) {
unsigned long flags;
int channel = info->line - card->first_line;
((lstatus & C_RS_CTS) ? TIOCM_CTS : 0);
}
end:
- unlock_kernel();
return result;
} /* cy_tiomget */
printk(KERN_DEBUG "cyc:cy_ioctl ttyC%d, cmd = %x arg = %lx\n",
info->line, cmd, arg);
#endif
- lock_kernel();
switch (cmd) {
case CYGETMON:
default:
ret_val = -ENOIOCTLCMD;
}
- unlock_kernel();
#ifdef CY_DEBUG_OTHER
printk(KERN_DEBUG "cyc:cy_ioctl done\n");
#include <linux/ctype.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
-#include <linux/smp_lock.h>
+#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/uaccess.h>
break;
case DIGI_SETAW:
case DIGI_SETAF:
- lock_kernel();
if (cmd == DIGI_SETAW) {
/* Setup an event to indicate when the transmit
buffer empties */
if (tty->ldisc->ops->flush_buffer)
tty->ldisc->ops->flush_buffer(tty);
}
- unlock_kernel();
/* Fall Thru */
case DIGI_SETA:
if (copy_from_user(&ch->digiext, argp, sizeof(digi_t)))
if ( tty_hung_up_p(pFile) || ( pCh->flags & ASYNC_CLOSING )) {
if ( pCh->flags & ASYNC_CLOSING ) {
+ tty_unlock();
schedule();
+ tty_lock();
}
if ( tty_hung_up_p(pFile) ) {
set_current_state( TASK_RUNNING );
rc = (( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS);
break;
}
+ tty_unlock();
schedule();
+ tty_lock();
}
set_current_state( TASK_RUNNING );
remove_wait_queue(&pCh->open_wait, &wait);
info->irq_setup = std_irq_setup;
info->slave_addr = ipmb;
- if (!add_smi(info))
+ if (!add_smi(info)) {
if (try_smi_init(info))
cleanup_one_si(info);
+ } else {
+ kfree(info);
+ }
} else {
/* remove */
struct smi_info *e, *tmp_e;
info->irq_setup = std_irq_setup;
info->slave_addr = slave_addrs[i];
- if (!add_smi(info))
+ if (!add_smi(info)) {
if (try_smi_init(info))
cleanup_one_si(info);
+ } else {
+ kfree(info);
+ }
}
}
/*
* Defined at
- * http://h21007.www2.hp.com/dspp/files/unprotected/devresource/
- * Docs/TechPapers/IA64/hpspmi.pdf
+ * http://h21007.www2.hp.com/portal/download/files
+ * /unprot/hpspmi.pdf
*/
struct SPMITable {
s8 Signature[4];
static __devinit int try_init_spmi(struct SPMITable *spmi)
{
struct smi_info *info;
- u8 addr_space;
if (spmi->IPMIlegacy != 1) {
printk(KERN_INFO PFX "Bad SPMI legacy %d\n", spmi->IPMIlegacy);
return -ENODEV;
}
- if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
- addr_space = IPMI_MEM_ADDR_SPACE;
- else
- addr_space = IPMI_IO_ADDR_SPACE;
-
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
printk(KERN_ERR PFX "Could not allocate SI data (3)\n");
}
info->io.addr_data = spmi->addr.address;
- add_smi(info);
+ pr_info("ipmi_si: SPMI: %s %#lx regsize %d spacing %d irq %d\n",
+ (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
+ info->io.addr_data, info->io.regsize, info->io.regspacing,
+ info->irq);
+
+ if (add_smi(info))
+ kfree(info);
return 0;
}
res, info->io.regsize, info->io.regspacing,
info->irq);
- return add_smi(info);
+ if (add_smi(info))
+ goto err_free;
+
+ return 0;
err_free:
kfree(info);
if (info->irq)
info->irq_setup = std_irq_setup;
- add_smi(info);
+ pr_info("ipmi_si: SMBIOS: %s %#lx regsize %d spacing %d irq %d\n",
+ (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
+ info->io.addr_data, info->io.regsize, info->io.regspacing,
+ info->irq);
+
+ if (add_smi(info))
+ kfree(info);
}
static void __devinit dmi_find_bmc(void)
&pdev->resource[0], info->io.regsize, info->io.regspacing,
info->irq);
- return add_smi(info);
+ if (add_smi(info))
+ kfree(info);
+
+ return 0;
}
static void __devexit ipmi_pci_remove(struct pci_dev *pdev)
dev_set_drvdata(&dev->dev, info);
- return add_smi(info);
+ if (add_smi(info)) {
+ kfree(info);
+ return -EBUSY;
+ }
+
+ return 0;
}
static int __devexit ipmi_of_remove(struct of_device *dev)
info->io.addr_data);
} else
cleanup_one_si(info);
+ } else {
+ kfree(info);
}
}
}
si_to_str[new_smi->si_type]);
mutex_lock(&smi_infos_lock);
if (!is_new_interface(new_smi)) {
- printk(KERN_CONT PFX "duplicate interface\n");
+ printk(KERN_CONT " duplicate interface\n");
rv = -EBUSY;
goto out_err;
}
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/serial.h>
-#include <linux/smp_lock.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
static int isicom_open(struct tty_struct *tty, struct file *filp)
{
struct isi_port *port;
- struct isi_board *card;
struct tty_port *tport;
tport = isicom_find_port(tty);
if (copy_from_user(&newinfo, info, sizeof(newinfo)))
return -EFAULT;
- lock_kernel();
-
+ mutex_lock(&port->port.mutex);
reconfig_port = ((port->port.flags & ASYNC_SPD_MASK) !=
(newinfo.flags & ASYNC_SPD_MASK));
(newinfo.closing_wait != port->port.closing_wait) ||
((newinfo.flags & ~ASYNC_USR_MASK) !=
(port->port.flags & ~ASYNC_USR_MASK))) {
- unlock_kernel();
+ mutex_unlock(&port->port.mutex);
return -EPERM;
}
port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
isicom_config_port(tty);
spin_unlock_irqrestore(&port->card->card_lock, flags);
}
- unlock_kernel();
+ mutex_unlock(&port->port.mutex);
return 0;
}
{
struct serial_struct out_info;
- lock_kernel();
+ mutex_lock(&port->port.mutex);
memset(&out_info, 0, sizeof(out_info));
/* out_info.type = ? */
out_info.line = port - isi_ports;
/* out_info.baud_base = ? */
out_info.close_delay = port->port.close_delay;
out_info.closing_wait = port->port.closing_wait;
- unlock_kernel();
+ mutex_unlock(&port->port.mutex);
if (copy_to_user(info, &out_info, sizeof(out_info)))
return -EFAULT;
return 0;
* the board has been detected, and whether it is actually running a slave
* or not.
*/
-#define BST_FOUND 0x1
-#define BST_STARTED 0x2
-#define BST_PROBED 0x4
+#define BST_FOUND 0
+#define BST_STARTED 1
+#define BST_PROBED 2
/*
* Define the set of port state flags. These are marked for internal
brdp = stli_brds[brdnr];
if (brdp == NULL)
return -ENODEV;
- if ((brdp->state & BST_STARTED) == 0)
+ if (!test_bit(BST_STARTED, &brdp->state))
return -ENODEV;
portnr = MINOR2PORT(minordev);
if (portnr > brdp->nrports)
* order of opens and closes may not be preserved across shared
* memory, so we must wait until it is complete.
*/
- wait_event_interruptible(portp->raw_wait,
+ wait_event_interruptible_tty(portp->raw_wait,
!test_bit(ST_CLOSING, &portp->state));
if (signal_pending(current)) {
return -ERESTARTSYS;
set_bit(ST_OPENING, &portp->state);
spin_unlock_irqrestore(&brd_lock, flags);
- wait_event_interruptible(portp->raw_wait,
+ wait_event_interruptible_tty(portp->raw_wait,
!test_bit(ST_OPENING, &portp->state));
if (signal_pending(current))
rc = -ERESTARTSYS;
* occurs on this port.
*/
if (wait) {
- wait_event_interruptible(portp->raw_wait,
+ wait_event_interruptible_tty(portp->raw_wait,
!test_bit(ST_CLOSING, &portp->state));
if (signal_pending(current)) {
return -ERESTARTSYS;
* to come back.
*/
rc = 0;
- wait_event_interruptible(portp->raw_wait,
+ wait_event_interruptible_tty(portp->raw_wait,
!test_bit(ST_CLOSING, &portp->state));
if (signal_pending(current))
rc = -ERESTARTSYS;
static int stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback)
{
+ /*
+ * no need for wait_event_tty because clearing ST_CMDING cannot block
+ * on BTM
+ */
wait_event_interruptible(portp->raw_wait,
!test_bit(ST_CMDING, &portp->state));
if (signal_pending(current))
rc = stli_portcmdstats(NULL, portp);
uart = "UNKNOWN";
- if (brdp->state & BST_STARTED) {
+ if (test_bit(BST_STARTED, &brdp->state)) {
switch (stli_comstats.hwid) {
case 0: uart = "2681"; break;
case 1: uart = "SC26198"; break;
}
seq_printf(m, "%d: uart:%s ", portnr, uart);
- if ((brdp->state & BST_STARTED) && (rc >= 0)) {
+ if (test_bit(BST_STARTED, &brdp->state) && rc >= 0) {
char sep;
seq_printf(m, "tx:%d rx:%d", (int) stli_comstats.txtotal,
brdp = stli_brds[brdnr];
if (brdp == NULL)
continue;
- if ((brdp->state & BST_STARTED) == 0)
+ if (!test_bit(BST_STARTED, &brdp->state))
continue;
spin_lock(&brd_lock);
}
- brdp->state |= BST_FOUND;
+ set_bit(BST_FOUND, &brdp->state);
return 0;
err_unmap:
iounmap(brdp->membase);
brdp->panels[0] = brdp->nrports;
- brdp->state |= BST_FOUND;
+ set_bit(BST_FOUND, &brdp->state);
return 0;
err_unmap:
iounmap(brdp->membase);
spin_unlock_irqrestore(&brd_lock, flags);
if (rc == 0)
- brdp->state |= BST_STARTED;
+ set_bit(BST_STARTED, &brdp->state);
if (! stli_timeron) {
stli_timeron++;
if (retval)
goto err_null;
- brdp->state |= BST_PROBED;
+ set_bit(BST_PROBED, &brdp->state);
pci_set_drvdata(pdev, brdp);
EBRDENABLE(brdp);
brdp = stli_brds[i];
if (brdp == NULL)
continue;
- if (brdp->state & BST_FOUND) {
+ if (test_bit(BST_FOUND, &brdp->state)) {
EBRDENABLE(brdp);
brdp->enable = NULL;
brdp->disable = NULL;
return -ENODEV;
memset(&stli_brdstats, 0, sizeof(combrd_t));
+
stli_brdstats.brd = brdp->brdnr;
stli_brdstats.type = brdp->brdtype;
stli_brdstats.hwid = 0;
if (brdp == NULL)
return -ENODEV;
- if (brdp->state & BST_STARTED) {
+ mutex_lock(&portp->port.mutex);
+ if (test_bit(BST_STARTED, &brdp->state)) {
if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS,
- &stli_cdkstats, sizeof(asystats_t), 1)) < 0)
+ &stli_cdkstats, sizeof(asystats_t), 1)) < 0) {
+ mutex_unlock(&portp->port.mutex);
return rc;
+ }
} else {
memset(&stli_cdkstats, 0, sizeof(asystats_t));
}
stli_comstats.modem = stli_cdkstats.dcdcnt;
stli_comstats.hwid = stli_cdkstats.hwid;
stli_comstats.signals = stli_mktiocm(stli_cdkstats.signals);
+ mutex_unlock(&portp->port.mutex);
return 0;
}
if (!brdp)
return -ENODEV;
- if (brdp->state & BST_STARTED) {
- if ((rc = stli_cmdwait(brdp, portp, A_CLEARSTATS, NULL, 0, 0)) < 0)
+ mutex_lock(&portp->port.mutex);
+
+ if (test_bit(BST_STARTED, &brdp->state)) {
+ if ((rc = stli_cmdwait(brdp, portp, A_CLEARSTATS, NULL, 0, 0)) < 0) {
+ mutex_unlock(&portp->port.mutex);
return rc;
+ }
}
memset(&stli_comstats, 0, sizeof(comstats_t));
stli_comstats.brd = portp->brdnr;
stli_comstats.panel = portp->panelnr;
stli_comstats.port = portp->portnr;
+ mutex_unlock(&portp->port.mutex);
if (copy_to_user(cp, &stli_comstats, sizeof(comstats_t)))
return -EFAULT;
done = 0;
rc = 0;
- lock_kernel();
-
switch (cmd) {
case COM_GETPORTSTATS:
rc = stli_getportstats(NULL, NULL, argp);
done++;
break;
}
- unlock_kernel();
-
if (done)
return rc;
if (brdp->state == 0)
return -ENODEV;
- lock_kernel();
-
switch (cmd) {
case STL_BINTR:
EBRDINTR(brdp);
rc = stli_startbrd(brdp);
break;
case STL_BSTOP:
- brdp->state &= ~BST_STARTED;
+ clear_bit(BST_STARTED, &brdp->state);
break;
case STL_BRESET:
- brdp->state &= ~BST_STARTED;
+ clear_bit(BST_STARTED, &brdp->state);
EBRDRESET(brdp);
if (stli_shared == 0) {
if (brdp->reenable != NULL)
rc = -ENOIOCTLCMD;
break;
}
- unlock_kernel();
return rc;
}
unsigned int j;
for (j = 0; (j < stli_nrbrds); j++) {
- if ((brdp = stli_brds[j]) == NULL || (brdp->state & BST_PROBED))
+ if ((brdp = stli_brds[j]) == NULL ||
+ test_bit(BST_PROBED, &brdp->state))
continue;
stli_cleanup_ports(brdp);
*/
static void put_queue(struct vc_data *vc, int ch)
{
- struct tty_struct *tty = vc->vc_tty;
+ struct tty_struct *tty = vc->port.tty;
if (tty) {
tty_insert_flip_char(tty, ch, 0);
static void puts_queue(struct vc_data *vc, char *cp)
{
- struct tty_struct *tty = vc->vc_tty;
+ struct tty_struct *tty = vc->port.tty;
if (!tty)
return;
static void fn_hold(struct vc_data *vc)
{
- struct tty_struct *tty = vc->vc_tty;
+ struct tty_struct *tty = vc->port.tty;
if (rep || !tty)
return;
static void fn_send_intr(struct vc_data *vc)
{
- struct tty_struct *tty = vc->vc_tty;
+ struct tty_struct *tty = vc->port.tty;
if (!tty)
return;
struct keyboard_notifier_param param = { .vc = vc, .value = keycode, .down = down };
int rc;
- tty = vc->vc_tty;
+ tty = vc->port.tty;
if (tty && (!tty->driver_data)) {
/* No driver data? Strange. Okay we fix it then. */
port->mon_data.up_txcnt += (cnt - port->xmit_cnt);
port->icount.tx += (cnt - port->xmit_cnt);
- if (port->xmit_cnt < WAKEUP_CHARS && tty)
+ if (port->xmit_cnt < WAKEUP_CHARS)
tty_wakeup(tty);
if (port->xmit_cnt <= 0) {
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
-#include <linux/timer.h>
#include <linux/ctype.h>
#include <linux/mm.h>
#include <linux/string.h>
else
len = gsm_dlci_data_output_framed(gsm, dlci);
if (len < 0)
- return;
+ break;
/* DLCI empty - try the next */
if (len == 0)
i++;
return -EFAULT;
}
- lock_kernel();
+ tty_lock();
for (;;) {
if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
- unlock_kernel();
+ tty_unlock();
return -EIO;
}
n_hdlc = tty2n_hdlc (tty);
if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC ||
tty != n_hdlc->tty) {
- unlock_kernel();
+ tty_unlock();
return 0;
}
/* no data */
if (file->f_flags & O_NONBLOCK) {
- unlock_kernel();
+ tty_unlock();
return -EAGAIN;
}
interruptible_sleep_on (&tty->read_wait);
if (signal_pending(current)) {
- unlock_kernel();
+ tty_unlock();
return -EINTR;
}
}
kfree(rbuf);
else
n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,rbuf);
- unlock_kernel();
+ tty_unlock();
return ret;
} /* end of n_hdlc_tty_read() */
count = maxframe;
}
- lock_kernel();
+ tty_lock();
add_wait_queue(&tty->write_wait, &wait);
set_current_state(TASK_INTERRUPTIBLE);
n_hdlc_buf_put(&n_hdlc->tx_buf_list,tbuf);
n_hdlc_send_frames(n_hdlc,tty);
}
- unlock_kernel();
+ tty_unlock();
return error;
} /* end of n_hdlc_tty_write() */
TRACE_L("read()");
- lock_kernel();
+ tty_lock();
pClient = findClient(pInfo, task_pid(current));
if (pClient) {
goto unlock;
}
/* block until there is a message: */
- wait_event_interruptible(pInfo->read_wait,
+ wait_event_interruptible_tty(pInfo->read_wait,
(pMsg = remove_msg(pInfo, pClient)));
}
}
ret = -EPERM;
unlock:
- unlock_kernel();
+ tty_unlock();
return ret;
}
pHeader->locks = 0;
pHeader->owner = NULL;
- lock_kernel();
+ tty_lock();
pClient = findClient(pInfo, task_pid(current));
if (pClient) {
add_tx_queue(pInfo, pHeader);
trigger_transmit(pInfo);
- unlock_kernel();
+ tty_unlock();
return 0;
}
if (I_IUCLC(tty) && L_IEXTEN(tty))
c = tolower(c);
+ if (L_EXTPROC(tty)) {
+ put_tty_queue(c, tty);
+ return;
+ }
+
if (tty->stopped && !tty->flow_stopped && I_IXON(tty) &&
I_IXANY(tty) && c != START_CHAR(tty) && c != STOP_CHAR(tty) &&
c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && c != SUSP_CHAR(tty)) {
n_tty_set_room(tty);
- if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) {
+ if ((!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) ||
+ L_EXTPROC(tty)) {
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
if (waitqueue_active(&tty->read_wait))
wake_up_interruptible(&tty->read_wait);
static inline int input_available_p(struct tty_struct *tty, int amt)
{
tty_flush_to_ldisc(tty);
- if (tty->icanon) {
+ if (tty->icanon && !L_EXTPROC(tty)) {
if (tty->canon_data)
return 1;
} else if (tty->read_cnt >= (amt ? amt : 1))
spin_lock_irqsave(&tty->read_lock, flags);
tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
tty->read_cnt -= n;
+ /* Turn single EOF into zero-length read */
+ if (L_EXTPROC(tty) && tty->icanon && n == 1) {
+ if (!tty->read_cnt && (*b)[n-1] == EOF_CHAR(tty))
+ n--;
+ }
spin_unlock_irqrestore(&tty->read_lock, flags);
*b += n;
*nr -= n;
nr--;
}
- if (tty->icanon) {
+ if (tty->icanon && !L_EXTPROC(tty)) {
/* N.B. avoid overrun if nr == 0 */
while (nr && tty->read_cnt) {
int eol;
ret = tty_init_termios(tty);
if (ret == 0) {
tty_driver_kref_get(driver);
+ tty->count++;
+ tty->driver_data = port;
driver->ttys[tty->index] = tty;
}
return ret;
static int ntty_open(struct tty_struct *tty, struct file *filp)
{
- struct port *port = get_port_by_tty(tty);
+ struct port *port = tty->driver_data;
return tty_port_open(&port->port, tty, filp);
}
if (dc) {
mutex_lock(&port->tty_sem);
if (port->port.count)
- room = port->fifo_ul.size -
- kfifo_len(&port->fifo_ul);
+ room = kfifo_avail(&port->fifo_ul);
mutex_unlock(&port->tty_sem);
}
return room;
if (tty->driver == ptm_driver)
devpts_pty_kill(tty->link);
#endif
+ tty_unlock();
tty_vhangup(tty->link);
+ tty_lock();
}
}
return 0;
}
+/* Send a signal to the slave */
+static int pty_signal(struct tty_struct *tty, int sig)
+{
+ unsigned long flags;
+ struct pid *pgrp;
+
+ if (tty->link) {
+ spin_lock_irqsave(&tty->link->ctrl_lock, flags);
+ pgrp = get_pid(tty->link->pgrp);
+ spin_unlock_irqrestore(&tty->link->ctrl_lock, flags);
+
+ kill_pgrp(pgrp, sig, 1);
+ put_pid(pgrp);
+ }
+ return 0;
+}
+
static void pty_flush_buffer(struct tty_struct *tty)
{
struct tty_struct *to = tty->link;
switch (cmd) {
case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */
return pty_set_lock(tty, (int __user *) arg);
+ case TIOCSIG: /* Send signal to other side of pty */
+ return pty_signal(tty, (int) arg);
}
return -ENOIOCTLCMD;
}
return pty_set_lock(tty, (int __user *)arg);
case TIOCGPTN: /* Get PT Number */
return put_user(tty->index, (unsigned int __user *)arg);
+ case TIOCSIG: /* Send signal to other side of pty */
+ return pty_signal(tty, (int) arg);
}
return -ENOIOCTLCMD;
* allocated_ptys_lock handles the list of free pty numbers
*/
-static int __ptmx_open(struct inode *inode, struct file *filp)
+static int ptmx_open(struct inode *inode, struct file *filp)
{
struct tty_struct *tty;
int retval;
nonseekable_open(inode, filp);
/* find a device that is not in use. */
+ tty_lock();
index = devpts_new_index(inode);
+ tty_unlock();
if (index < 0)
return index;
mutex_lock(&tty_mutex);
+ tty_lock();
tty = tty_init_dev(ptm_driver, index, 1);
mutex_unlock(&tty_mutex);
goto out1;
retval = ptm_driver->ops->open(tty, filp);
- if (!retval)
- return 0;
+ if (retval)
+ goto out2;
out1:
+ tty_unlock();
+ return retval;
+out2:
+ tty_unlock();
tty_release(inode, filp);
return retval;
out:
devpts_kill_index(inode, index);
+ tty_unlock();
return retval;
}
-static int ptmx_open(struct inode *inode, struct file *filp)
-{
- int ret;
-
- lock_kernel();
- ret = __ptmx_open(inode, filp);
- unlock_kernel();
- return ret;
-}
-
static struct file_operations ptmx_fops;
static void __init unix98_pty_init(void)
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/tty_flip.h>
-#include <linux/smp_lock.h>
#include <linux/spinlock.h>
#include <linux/device.h>
if (copy_from_user(&tmp, newinfo, sizeof(tmp)))
return -EFAULT;
+ mutex_lock(&port->port.mutex);
change_speed = ((port->port.flags & ASYNC_SPD_MASK) !=
(tmp.flags & ASYNC_SPD_MASK));
if ((tmp.close_delay != port->port.close_delay) ||
(tmp.closing_wait != port->port.closing_wait) ||
((tmp.flags & ~ASYNC_USR_MASK) !=
- (port->port.flags & ~ASYNC_USR_MASK)))
+ (port->port.flags & ~ASYNC_USR_MASK))) {
+ mutex_unlock(&port->port.mutex);
return -EPERM;
+ }
port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
(tmp.flags & ASYNC_USR_MASK));
} else {
rc_change_speed(tty, bp, port);
spin_unlock_irqrestore(&riscom_lock, flags);
}
+ mutex_unlock(&port->port.mutex);
return 0;
}
memset(&tmp, 0, sizeof(tmp));
tmp.type = PORT_CIRRUS;
tmp.line = port - rc_port;
+
+ mutex_lock(&port->port.mutex);
tmp.port = bp->base;
tmp.irq = bp->irq;
tmp.flags = port->port.flags;
tmp.baud_base = (RC_OSCFREQ + CD180_TPC/2) / CD180_TPC;
tmp.close_delay = port->port.close_delay * HZ/100;
tmp.closing_wait = port->port.closing_wait * HZ/100;
+ mutex_unlock(&port->port.mutex);
tmp.xmit_fifo_size = CD180_NFIFO;
return copy_to_user(retinfo, &tmp, sizeof(tmp)) ? -EFAULT : 0;
}
switch (cmd) {
case TIOCGSERIAL:
- lock_kernel();
retval = rc_get_serial_info(port, argp);
- unlock_kernel();
break;
case TIOCSSERIAL:
- lock_kernel();
retval = rc_set_serial_info(tty, port, argp);
- unlock_kernel();
break;
default:
retval = -ENOIOCTLCMD;
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
-#include <linux/smp_lock.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
if (tty_port_close_start(port, tty, filp) == 0)
return;
+ mutex_lock(&port->mutex);
cp = &info->channel;
/*
* Before we drop DTR, make sure the UART transmitter
info->xmit_buf = NULL;
}
}
+ spin_lock_irq(&port->lock);
info->port.flags &= ~(ASYNC_INITIALIZED | ASYNC_CLOSING | ASYNC_NORMAL_ACTIVE);
tty->closing = 0;
+ spin_unlock_irq(&port->lock);
+ mutex_unlock(&port->mutex);
tty_port_tty_set(port, NULL);
+
wake_up_interruptible(&port->close_wait);
complete_all(&info->close_wait);
atomic_dec(&rp_num_ports_open);
if (!retinfo)
return -EFAULT;
memset(&tmp, 0, sizeof (tmp));
+ mutex_lock(&info->port.mutex);
tmp.line = info->line;
tmp.flags = info->flags;
tmp.close_delay = info->port.close_delay;
tmp.closing_wait = info->port.closing_wait;
tmp.port = rcktpt_io_addr[(info->line >> 5) & 3];
+ mutex_unlock(&info->port.mutex);
if (copy_to_user(retinfo, &tmp, sizeof (*retinfo)))
return -EFAULT;
if (copy_from_user(&new_serial, new_info, sizeof (new_serial)))
return -EFAULT;
+ mutex_lock(&info->port.mutex);
if (!capable(CAP_SYS_ADMIN))
{
- if ((new_serial.flags & ~ROCKET_USR_MASK) != (info->flags & ~ROCKET_USR_MASK))
+ if ((new_serial.flags & ~ROCKET_USR_MASK) != (info->flags & ~ROCKET_USR_MASK)) {
+ mutex_unlock(&info->port.mutex);
return -EPERM;
+ }
info->flags = ((info->flags & ~ROCKET_USR_MASK) | (new_serial.flags & ROCKET_USR_MASK));
configure_r_port(tty, info, NULL);
return 0;
tty->alt_speed = 230400;
if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP)
tty->alt_speed = 460800;
+ mutex_unlock(&info->port.mutex);
configure_r_port(tty, info, NULL);
return 0;
if (cmd != RCKP_GET_PORTS && rocket_paranoia_check(info, "rp_ioctl"))
return -ENXIO;
- lock_kernel();
-
switch (cmd) {
case RCKP_GET_STRUCT:
if (copy_to_user(argp, info, sizeof (struct r_port)))
default:
ret = -ENOIOCTLCMD;
}
- unlock_kernel();
return ret;
}
jiffies);
printk(KERN_INFO "cps=%d...\n", info->cps);
#endif
- lock_kernel();
while (1) {
txcnt = sGetTxCnt(cp);
if (!txcnt) {
break;
}
__set_current_state(TASK_RUNNING);
- unlock_kernel();
#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
printk(KERN_INFO "txcnt = %d (jiff=%lu)...done\n", txcnt, jiffies);
#endif
{
CHANNEL_t *cp;
struct r_port *info = tty->driver_data;
+ unsigned long flags;
if (rocket_paranoia_check(info, "rp_hangup"))
return;
printk(KERN_INFO "rp_hangup of ttyR%d...\n", info->line);
#endif
rp_flush_buffer(tty);
- if (info->port.flags & ASYNC_CLOSING)
+ spin_lock_irqsave(&info->port.lock, flags);
+ if (info->port.flags & ASYNC_CLOSING) {
+ spin_unlock_irqrestore(&info->port.lock, flags);
return;
+ }
if (info->port.count)
atomic_dec(&rp_num_ports_open);
clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]);
+ spin_unlock_irqrestore(&info->port.lock, flags);
tty_port_hangup(&info->port);
sDisCTSFlowCtl(cp);
sDisTxSoftFlowCtl(cp);
sClrTxXOFF(cp);
- info->port.flags &= ~ASYNC_INITIALIZED;
+ clear_bit(ASYNCB_INITIALIZED, &info->port.flags);
wake_up_interruptible(&info->port.open_wait);
}
#include <linux/selection.h>
#include <linux/tiocl.h>
#include <linux/console.h>
+#include <linux/smp_lock.h>
/* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
#define isspace(c) ((c) == ' ')
struct tty_ldisc *ld;
DECLARE_WAITQUEUE(wait, current);
+ /* always called with BTM from vt_ioctl */
+ WARN_ON(!tty_locked());
+
acquire_console_sem();
poke_blanked_console();
release_console_sem();
- ld = tty_ldisc_ref_wait(tty);
-
+ ld = tty_ldisc_ref(tty);
+ if (!ld) {
+ tty_unlock();
+ ld = tty_ldisc_ref_wait(tty);
+ tty_lock();
+ }
+
add_wait_queue(&vc->paste_wait, &wait);
while (sel_buffer && sel_buffer_lth > pasted) {
set_current_state(TASK_INTERRUPTIBLE);
printk("cy_ioctl %s, cmd = %x arg = %lx\n", tty->name, cmd, arg); /* */
#endif
- lock_kernel();
+ tty_lock();
switch (cmd) {
case CYGETMON:
default:
ret_val = -ENOIOCTLCMD;
}
- unlock_kernel();
+ tty_unlock();
#ifdef SERIAL_DEBUG_OTHER
printk("cy_ioctl done\n");
tty->name, info->count);
/**/
#endif
- schedule();
+ tty_unlock();
+ schedule();
+ tty_lock();
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(&info->open_wait, &wait);
retval = -ERESTARTSYS;
break;
}
+ tty_unlock();
schedule();
+ tty_lock();
}
set_current_state(TASK_RUNNING);
return -EFAULT;
}
- lock_kernel();
-
+ mutex_lock(&port->port.mutex);
change_speed = ((port->port.flags & ASYNC_SPD_MASK) !=
(tmp.flags & ASYNC_SPD_MASK));
change_speed |= (tmp.custom_divisor != port->custom_divisor);
((tmp.flags & ~ASYNC_USR_MASK) !=
(port->port.flags & ~ASYNC_USR_MASK))) {
func_exit();
- unlock_kernel();
+ mutex_unlock(&port->port.mutex);
return -EPERM;
}
port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
sx_change_speed(bp, port);
func_exit();
- unlock_kernel();
+ mutex_unlock(&port->port.mutex);
return 0;
}
func_enter();
memset(&tmp, 0, sizeof(tmp));
- lock_kernel();
+ mutex_lock(&port->port.mutex);
tmp.type = PORT_CIRRUS;
tmp.line = port - sx_port;
tmp.port = bp->base;
tmp.closing_wait = port->port.closing_wait * HZ/100;
tmp.custom_divisor = port->custom_divisor;
tmp.xmit_fifo_size = CD186x_NFIFO;
- unlock_kernel();
+ mutex_unlock(&port->port.mutex);
if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
func_exit();
return -EFAULT;
timeout = HZ;
tend = jiffies + timeout;
- lock_kernel();
while (stl_datastate(portp)) {
if (signal_pending(current))
break;
if (time_after_eq(jiffies, tend))
break;
}
- unlock_kernel();
}
/*****************************************************************************/
pr_debug("stl_getserial(portp=%p,sp=%p)\n", portp, sp);
memset(&sio, 0, sizeof(struct serial_struct));
+
+ mutex_lock(&portp->port.mutex);
sio.line = portp->portnr;
sio.port = portp->ioaddr;
sio.flags = portp->port.flags;
brdp = stl_brds[portp->brdnr];
if (brdp != NULL)
sio.irq = brdp->irq;
+ mutex_unlock(&portp->port.mutex);
return copy_to_user(sp, &sio, sizeof(struct serial_struct)) ? -EFAULT : 0;
}
if (copy_from_user(&sio, sp, sizeof(struct serial_struct)))
return -EFAULT;
+ mutex_lock(&portp->port.mutex);
if (!capable(CAP_SYS_ADMIN)) {
if ((sio.baud_base != portp->baud_base) ||
(sio.close_delay != portp->close_delay) ||
((sio.flags & ~ASYNC_USR_MASK) !=
- (portp->port.flags & ~ASYNC_USR_MASK)))
+ (portp->port.flags & ~ASYNC_USR_MASK))) {
+ mutex_unlock(&portp->port.mutex);
return -EPERM;
+ }
}
portp->port.flags = (portp->port.flags & ~ASYNC_USR_MASK) |
portp->close_delay = sio.close_delay;
portp->closing_wait = sio.closing_wait;
portp->custom_divisor = sio.custom_divisor;
+ mutex_unlock(&portp->port.mutex);
stl_setport(portp, tty->termios);
return 0;
}
rc = 0;
- lock_kernel();
-
switch (cmd) {
case TIOCGSERIAL:
rc = stl_getserial(portp, argp);
rc = -ENOIOCTLCMD;
break;
}
- unlock_kernel();
return rc;
}
return -ENODEV;
}
+ mutex_lock(&portp->port.mutex);
portp->stats.state = portp->istate;
portp->stats.flags = portp->port.flags;
portp->stats.hwid = portp->hwid;
(STL_TXBUFSIZE - (tail - head));
portp->stats.signals = (unsigned long) stl_getsignals(portp);
+ mutex_unlock(&portp->port.mutex);
return copy_to_user(cp, &portp->stats,
sizeof(comstats_t)) ? -EFAULT : 0;
return -ENODEV;
}
+ mutex_lock(&portp->port.mutex);
memset(&portp->stats, 0, sizeof(comstats_t));
portp->stats.brd = portp->brdnr;
portp->stats.panel = portp->panelnr;
portp->stats.port = portp->portnr;
+ mutex_unlock(&portp->port.mutex);
return copy_to_user(cp, &portp->stats,
sizeof(comstats_t)) ? -EFAULT : 0;
}
return -ENODEV;
rc = 0;
- lock_kernel();
switch (cmd) {
case COM_GETPORTSTATS:
rc = stl_getportstats(NULL, NULL, argp);
rc = -ENOIOCTLCMD;
break;
}
- unlock_kernel();
return rc;
}
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
- lock_kernel();
+ tty_lock();
sx_dprintk(SX_DEBUG_FIRMWARE, "IOCTL %x: %lx\n", cmd, arg);
break;
}
out:
- unlock_kernel();
+ tty_unlock();
func_exit();
return rc;
}
int rv;
func_enter();
- lock_kernel();
+ tty_lock();
if (flag)
rv = sx_send_command(port, HS_START, -1, HS_IDLE_BREAK);
if (rv != 1)
printk(KERN_ERR "sx: couldn't send break (%x).\n",
read_sx_byte(port->board, CHAN_OFFSET(port, hi_hstat)));
- unlock_kernel();
+ tty_unlock();
func_exit();
return 0;
}
/* func_enter2(); */
rc = 0;
- lock_kernel();
+ tty_lock();
switch (cmd) {
case TIOCGSERIAL:
rc = gs_getserial(&port->gs, argp);
rc = -ENOIOCTLCMD;
break;
}
- unlock_kernel();
+ tty_unlock();
/* func_exit(); */
return rc;
#include <linux/mm.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/vmalloc.h>
if (!user_icount) {
memset(&info->icount, 0, sizeof(info->icount));
} else {
+ mutex_lock(&info->port.mutex);
COPY_TO_USER(err, user_icount, &info->icount, sizeof(struct mgsl_icount));
+ mutex_unlock(&info->port.mutex);
if (err)
return -EFAULT;
}
printk("%s(%d):mgsl_get_params(%s)\n",
__FILE__,__LINE__, info->device_name);
+ mutex_lock(&info->port.mutex);
COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS));
+ mutex_unlock(&info->port.mutex);
if (err) {
if ( debug_level >= DEBUG_LEVEL_INFO )
printk( "%s(%d):mgsl_get_params(%s) user buffer copy failed\n",
return -EFAULT;
}
+ mutex_lock(&info->port.mutex);
spin_lock_irqsave(&info->irq_spinlock,flags);
memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS));
spin_unlock_irqrestore(&info->irq_spinlock,flags);
mgsl_change_params(info);
+ mutex_unlock(&info->port.mutex);
return 0;
unsigned int cmd, unsigned long arg)
{
struct mgsl_struct * info = tty->driver_data;
- int ret;
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgsl_ioctl %s cmd=%08X\n", __FILE__,__LINE__,
return -EIO;
}
- lock_kernel();
- ret = mgsl_ioctl_common(info, cmd, arg);
- unlock_kernel();
- return ret;
+ return mgsl_ioctl_common(info, cmd, arg);
}
static int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigned long arg)
if (tty_port_close_start(&info->port, tty, filp) == 0)
goto cleanup;
-
+
+ mutex_lock(&info->port.mutex);
if (info->port.flags & ASYNC_INITIALIZED)
mgsl_wait_until_sent(tty, info->timeout);
mgsl_flush_buffer(tty);
tty_ldisc_flush(tty);
shutdown(info);
+ mutex_unlock(&info->port.mutex);
tty_port_close_end(&info->port, tty);
info->port.tty = NULL;
* Note: use tight timings here to satisfy the NIST-PCTS.
*/
- lock_kernel();
if ( info->params.data_rate ) {
char_time = info->timeout/(32 * 5);
if (!char_time)
break;
}
}
- unlock_kernel();
exit:
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):block_til_ready blocking on %s count=%d\n",
__FILE__,__LINE__, tty->driver->name, port->count );
+ tty_unlock();
schedule();
+ tty_lock();
}
set_current_state(TASK_RUNNING);
#define DBGBH(fmt) if (debug_level >= DEBUG_LEVEL_BH) printk fmt
#define DBGISR(fmt) if (debug_level >= DEBUG_LEVEL_ISR) printk fmt
#define DBGDATA(info, buf, size, label) if (debug_level >= DEBUG_LEVEL_DATA) trace_block((info), (buf), (size), (label))
-//#define DBGTBUF(info) dump_tbufs(info)
-//#define DBGRBUF(info) dump_rbufs(info)
+/*#define DBGTBUF(info) dump_tbufs(info)*/
+/*#define DBGRBUF(info) dump_rbufs(info)*/
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
#include <linux/netdevice.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
goto cleanup;
}
+ mutex_lock(&info->port.mutex);
info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
spin_lock_irqsave(&info->netlock, flags);
if (info->netcount) {
retval = -EBUSY;
spin_unlock_irqrestore(&info->netlock, flags);
+ mutex_unlock(&info->port.mutex);
goto cleanup;
}
info->port.count++;
if (retval < 0)
goto cleanup;
}
-
+ mutex_unlock(&info->port.mutex);
retval = block_til_ready(tty, filp, info);
if (retval) {
DBGINFO(("%s block_til_ready rc=%d\n", info->device_name, retval));
if (tty_port_close_start(&info->port, tty, filp) == 0)
goto cleanup;
+ mutex_lock(&info->port.mutex);
if (info->port.flags & ASYNC_INITIALIZED)
wait_until_sent(tty, info->timeout);
flush_buffer(tty);
tty_ldisc_flush(tty);
shutdown(info);
+ mutex_unlock(&info->port.mutex);
tty_port_close_end(&info->port, tty);
info->port.tty = NULL;
static void hangup(struct tty_struct *tty)
{
struct slgt_info *info = tty->driver_data;
+ unsigned long flags;
if (sanity_check(info, tty->name, "hangup"))
return;
DBGINFO(("%s hangup\n", info->device_name));
flush_buffer(tty);
+
+ mutex_lock(&info->port.mutex);
shutdown(info);
+ spin_lock_irqsave(&info->port.lock, flags);
info->port.count = 0;
info->port.flags &= ~ASYNC_NORMAL_ACTIVE;
info->port.tty = NULL;
+ spin_unlock_irqrestore(&info->port.lock, flags);
+ mutex_unlock(&info->port.mutex);
wake_up_interruptible(&info->port.open_wait);
}
* Note: use tight timings here to satisfy the NIST-PCTS.
*/
- lock_kernel();
-
if (info->params.data_rate) {
char_time = info->timeout/(32 * 5);
if (!char_time)
if (timeout && time_after(jiffies, orig_jiffies + timeout))
break;
}
- unlock_kernel();
-
exit:
DBGINFO(("%s wait_until_sent exit\n", info->device_name));
}
return -EIO;
}
- lock_kernel();
-
+ switch (cmd) {
+ case MGSL_IOCWAITEVENT:
+ return wait_mgsl_event(info, argp);
+ case TIOCMIWAIT:
+ return modem_input_wait(info,(int)arg);
+ case TIOCGICOUNT:
+ spin_lock_irqsave(&info->lock,flags);
+ cnow = info->icount;
+ spin_unlock_irqrestore(&info->lock,flags);
+ p_cuser = argp;
+ if (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) ||
+ put_user(cnow.rx, &p_cuser->rx) ||
+ put_user(cnow.tx, &p_cuser->tx) ||
+ put_user(cnow.frame, &p_cuser->frame) ||
+ put_user(cnow.overrun, &p_cuser->overrun) ||
+ put_user(cnow.parity, &p_cuser->parity) ||
+ put_user(cnow.brk, &p_cuser->brk) ||
+ put_user(cnow.buf_overrun, &p_cuser->buf_overrun))
+ return -EFAULT;
+ return 0;
+ case MGSL_IOCSGPIO:
+ return set_gpio(info, argp);
+ case MGSL_IOCGGPIO:
+ return get_gpio(info, argp);
+ case MGSL_IOCWAITGPIO:
+ return wait_gpio(info, argp);
+ }
+ mutex_lock(&info->port.mutex);
switch (cmd) {
case MGSL_IOCGPARAMS:
ret = get_params(info, argp);
case MGSL_IOCGSTATS:
ret = get_stats(info, argp);
break;
- case MGSL_IOCWAITEVENT:
- ret = wait_mgsl_event(info, argp);
- break;
- case TIOCMIWAIT:
- ret = modem_input_wait(info,(int)arg);
- break;
case MGSL_IOCGIF:
ret = get_interface(info, argp);
break;
case MGSL_IOCSIF:
ret = set_interface(info,(int)arg);
break;
- case MGSL_IOCSGPIO:
- ret = set_gpio(info, argp);
- break;
- case MGSL_IOCGGPIO:
- ret = get_gpio(info, argp);
- break;
- case MGSL_IOCWAITGPIO:
- ret = wait_gpio(info, argp);
- break;
- case TIOCGICOUNT:
- spin_lock_irqsave(&info->lock,flags);
- cnow = info->icount;
- spin_unlock_irqrestore(&info->lock,flags);
- p_cuser = argp;
- if (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) ||
- put_user(cnow.rx, &p_cuser->rx) ||
- put_user(cnow.tx, &p_cuser->tx) ||
- put_user(cnow.frame, &p_cuser->frame) ||
- put_user(cnow.overrun, &p_cuser->overrun) ||
- put_user(cnow.parity, &p_cuser->parity) ||
- put_user(cnow.brk, &p_cuser->brk) ||
- put_user(cnow.buf_overrun, &p_cuser->buf_overrun))
- ret = -EFAULT;
- ret = 0;
- break;
default:
ret = -ENOIOCTLCMD;
}
- unlock_kernel();
+ mutex_unlock(&info->port.mutex);
return ret;
}
}
DBGINFO(("%s block_til_ready wait\n", tty->driver->name));
+ tty_unlock();
schedule();
+ tty_lock();
}
set_current_state(TASK_RUNNING);
#include <linux/mm.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
#include <linux/netdevice.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
if (tty_port_close_start(&info->port, tty, filp) == 0)
goto cleanup;
-
+
+ mutex_lock(&info->port.mutex);
if (info->port.flags & ASYNC_INITIALIZED)
wait_until_sent(tty, info->timeout);
flush_buffer(tty);
tty_ldisc_flush(tty);
shutdown(info);
+ mutex_unlock(&info->port.mutex);
tty_port_close_end(&info->port, tty);
info->port.tty = NULL;
static void hangup(struct tty_struct *tty)
{
SLMP_INFO *info = tty->driver_data;
+ unsigned long flags;
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):%s hangup()\n",
if (sanity_check(info, tty->name, "hangup"))
return;
+ mutex_lock(&info->port.mutex);
flush_buffer(tty);
shutdown(info);
+ spin_lock_irqsave(&info->port.lock, flags);
info->port.count = 0;
info->port.flags &= ~ASYNC_NORMAL_ACTIVE;
info->port.tty = NULL;
+ spin_unlock_irqrestore(&info->port.lock, flags);
+ mutex_unlock(&info->port.mutex);
wake_up_interruptible(&info->port.open_wait);
}
if (sanity_check(info, tty->name, "wait_until_sent"))
return;
- lock_kernel();
-
- if (!(info->port.flags & ASYNC_INITIALIZED))
+ if (!test_bit(ASYNCB_INITIALIZED, &info->port.flags))
goto exit;
orig_jiffies = jiffies;
break;
}
} else {
- //TODO: determine if there is something similar to USC16C32
- // TXSTATUS_ALL_SENT status
+ /*
+ * TODO: determine if there is something similar to USC16C32
+ * TXSTATUS_ALL_SENT status
+ */
while ( info->tx_active && info->tx_enabled) {
msleep_interruptible(jiffies_to_msecs(char_time));
if (signal_pending(current))
}
exit:
- unlock_kernel();
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):%s wait_until_sent() exit\n",
__FILE__,__LINE__, info->device_name );
if (sanity_check(info, tty->name, "write_room"))
return 0;
- lock_kernel();
if (info->params.mode == MGSL_MODE_HDLC) {
ret = (info->tx_active) ? 0 : HDLC_MAX_FRAME_SIZE;
} else {
if (ret < 0)
ret = 0;
}
- unlock_kernel();
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):%s write_room()=%d\n",
*
* Return Value: 0 if success, otherwise error code
*/
-static int do_ioctl(struct tty_struct *tty, struct file *file,
+static int ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
SLMP_INFO *info = tty->driver_data;
return 0;
}
-static int ioctl(struct tty_struct *tty, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- int ret;
- lock_kernel();
- ret = do_ioctl(tty, file, cmd, arg);
- unlock_kernel();
- return ret;
-}
-
/*
* /proc fs routines....
*/
if (!user_icount) {
memset(&info->icount, 0, sizeof(info->icount));
} else {
+ mutex_lock(&info->port.mutex);
COPY_TO_USER(err, user_icount, &info->icount, sizeof(struct mgsl_icount));
+ mutex_unlock(&info->port.mutex);
if (err)
return -EFAULT;
}
printk("%s(%d):%s get_params()\n",
__FILE__,__LINE__, info->device_name);
+ mutex_lock(&info->port.mutex);
COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS));
+ mutex_unlock(&info->port.mutex);
if (err) {
if ( debug_level >= DEBUG_LEVEL_INFO )
printk( "%s(%d):%s get_params() user buffer copy failed\n",
return -EFAULT;
}
+ mutex_lock(&info->port.mutex);
spin_lock_irqsave(&info->lock,flags);
memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS));
spin_unlock_irqrestore(&info->lock,flags);
change_params(info);
+ mutex_unlock(&info->port.mutex);
return 0;
}
printk("%s(%d):%s block_til_ready() count=%d\n",
__FILE__,__LINE__, tty->driver->name, port->count );
+ tty_unlock();
schedule();
+ tty_lock();
}
set_current_state(TASK_RUNNING);
#else
#define tty_compat_ioctl NULL
#endif
+static int __tty_fasync(int fd, struct file *filp, int on);
static int tty_fasync(int fd, struct file *filp, int on);
static void release_tty(struct tty_struct *tty, int idx);
static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty);
EXPORT_SYMBOL_GPL(tty_wakeup);
/**
- * do_tty_hangup - actual handler for hangup events
+ * __tty_hangup - actual handler for hangup events
* @work: tty device
*
* This can be called by the "eventd" kernel thread. That is process
* remains intact.
*
* Locking:
- * BKL
+ * BTM
* redirect lock for undoing redirection
* file list lock for manipulating list of ttys
* tty_ldisc_lock from called functions
* tasklist_lock to walk task list for hangup event
* ->siglock to protect ->signal/->sighand
*/
-static void do_tty_hangup(struct work_struct *work)
+void __tty_hangup(struct tty_struct *tty)
{
- struct tty_struct *tty =
- container_of(work, struct tty_struct, hangup_work);
struct file *cons_filp = NULL;
struct file *filp, *f = NULL;
struct task_struct *p;
}
spin_unlock(&redirect_lock);
- /* inuse_filps is protected by the single kernel lock */
- lock_kernel();
- check_tty_count(tty, "do_tty_hangup");
+ tty_lock();
+
+ /* inuse_filps is protected by the single tty lock,
+ this really needs to change if we want to flush the
+ workqueue with the lock held */
+ check_tty_count(tty, "tty_hangup");
file_list_lock();
/* This breaks for file handles being sent over AF_UNIX sockets ? */
if (filp->f_op->write != tty_write)
continue;
closecount++;
- tty_fasync(-1, filp, 0); /* can't block */
+ __tty_fasync(-1, filp, 0); /* can't block */
filp->f_op = &hung_up_tty_fops;
}
file_list_unlock();
*/
set_bit(TTY_HUPPED, &tty->flags);
tty_ldisc_enable(tty);
- unlock_kernel();
+
+ tty_unlock();
+
if (f)
fput(f);
}
+static void do_tty_hangup(struct work_struct *work)
+{
+ struct tty_struct *tty =
+ container_of(work, struct tty_struct, hangup_work);
+
+ __tty_hangup(tty);
+}
+
/**
* tty_hangup - trigger a hangup event
* @tty: tty to hangup
printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf));
#endif
- do_tty_hangup(&tty->hangup_work);
+ __tty_hangup(tty);
}
EXPORT_SYMBOL(tty_vhangup);
+
/**
* tty_vhangup_self - process vhangup for own ctty
*
* exiting; it is 0 if called by the ioctl TIOCNOTTY.
*
* Locking:
- * BKL is taken for hysterical raisins
+ * BTM is taken for hysterical raisins, and held when
+ * called from no_tty().
* tty_mutex is taken to protect tty
* ->siglock is taken to protect ->signal/->sighand
* tasklist_lock is taken to walk process list for sessions
tty = get_current_tty();
if (tty) {
tty_pgrp = get_pid(tty->pgrp);
- lock_kernel();
- if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
- tty_vhangup(tty);
- unlock_kernel();
+ if (on_exit) {
+ if (tty->driver->type != TTY_DRIVER_TYPE_PTY)
+ tty_vhangup(tty);
+ }
tty_kref_put(tty);
} else if (on_exit) {
struct pid *old_pgrp;
void no_tty(void)
{
struct task_struct *tsk = current;
- lock_kernel();
+ tty_lock();
disassociate_ctty(0);
- unlock_kernel();
+ tty_unlock();
proc_clear_tty(tsk);
}
struct inode *inode;
struct tty_ldisc *ld;
- tty = (struct tty_struct *)file->private_data;
+ tty = file->private_data;
inode = file->f_path.dentry->d_inode;
if (tty_paranoia_check(tty, inode, "tty_read"))
return -EIO;
* We don't put it into the syslog queue right now maybe in the future if
* really needed.
*
- * We must still hold the BKL and test the CLOSING flag for the moment.
+ * We must still hold the BTM and test the CLOSING flag for the moment.
*/
void tty_write_message(struct tty_struct *tty, char *msg)
{
if (tty) {
mutex_lock(&tty->atomic_write_lock);
- lock_kernel();
+ tty_lock();
if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) {
- unlock_kernel();
+ tty_unlock();
tty->ops->write(tty, msg, strlen(msg));
} else
- unlock_kernel();
+ tty_unlock();
tty_write_unlock(tty);
}
return;
ssize_t ret;
struct tty_ldisc *ld;
- tty = (struct tty_struct *)file->private_data;
+ tty = file->private_data;
if (tty_paranoia_check(tty, inode, "tty_write"))
return -EIO;
if (!tty || !tty->ops->write ||
int ret;
if (driver->ops->install) {
- lock_kernel();
ret = driver->ops->install(driver, tty);
- unlock_kernel();
return ret;
}
if (tty_init_termios(tty) == 0) {
- lock_kernel();
tty_driver_kref_get(driver);
tty->count++;
driver->ttys[idx] = tty;
- unlock_kernel();
return 0;
}
return -ENOMEM;
struct tty_struct *tty;
int retval;
- lock_kernel();
/* Check if pty master is being opened multiple times */
if (driver->subtype == PTY_TYPE_MASTER &&
(driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) {
- unlock_kernel();
return ERR_PTR(-EIO);
}
- unlock_kernel();
/*
* First time open is complex, especially for PTY devices.
if (printk_ratelimit())
printk(KERN_INFO "tty_init_dev: ldisc open failed, "
"clearing slot %d\n", idx);
- lock_kernel();
release_tty(tty, idx);
- unlock_kernel();
return ERR_PTR(retval);
}
int idx;
char buf[64];
- tty = (struct tty_struct *)filp->private_data;
+ tty = filp->private_data;
if (tty_paranoia_check(tty, inode, "tty_release_dev"))
return 0;
- lock_kernel();
+ tty_lock();
check_tty_count(tty, "tty_release_dev");
- tty_fasync(-1, filp, 0);
+ __tty_fasync(-1, filp, 0);
idx = tty->index;
pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
if (idx < 0 || idx >= tty->driver->num) {
printk(KERN_DEBUG "tty_release_dev: bad idx when trying to "
"free (%s)\n", tty->name);
- unlock_kernel();
+ tty_unlock();
return 0;
}
if (!devpts) {
if (tty != tty->driver->ttys[idx]) {
- unlock_kernel();
+ tty_unlock();
printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty "
"for (%s)\n", idx, tty->name);
return 0;
}
if (tty->termios != tty->driver->termios[idx]) {
- unlock_kernel();
+ tty_unlock();
printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios "
"for (%s)\n",
idx, tty->name);
if (tty->driver->other &&
!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
if (o_tty != tty->driver->other->ttys[idx]) {
- unlock_kernel();
+ tty_unlock();
printk(KERN_DEBUG "tty_release_dev: other->table[%d] "
"not o_tty for (%s)\n",
idx, tty->name);
return 0 ;
}
if (o_tty->termios != tty->driver->other->termios[idx]) {
- unlock_kernel();
+ tty_unlock();
printk(KERN_DEBUG "tty_release_dev: other->termios[%d] "
"not o_termios for (%s)\n",
idx, tty->name);
return 0;
}
if (o_tty->link != tty) {
- unlock_kernel();
+ tty_unlock();
printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n");
return 0;
}
if (tty->ops->close)
tty->ops->close(tty, filp);
- unlock_kernel();
+ tty_unlock();
/*
* Sanity check: if tty->count is going to zero, there shouldn't be
* any waiters on tty->read_wait or tty->write_wait. We test the
opens on /dev/tty */
mutex_lock(&tty_mutex);
- lock_kernel();
+ tty_lock();
tty_closing = tty->count <= 1;
o_tty_closing = o_tty &&
(o_tty->count <= (pty_master ? 1 : 0));
printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue "
"active!\n", tty_name(tty, buf));
- unlock_kernel();
+ tty_unlock();
mutex_unlock(&tty_mutex);
schedule();
}
/* check whether both sides are closing ... */
if (!tty_closing || (o_tty && !o_tty_closing)) {
- unlock_kernel();
+ tty_unlock();
return 0;
}
/* Make this pty number available for reallocation */
if (devpts)
devpts_kill_index(inode, idx);
- unlock_kernel();
+ tty_unlock();
return 0;
}
retval = 0;
mutex_lock(&tty_mutex);
- lock_kernel();
+ tty_lock();
if (device == MKDEV(TTYAUX_MAJOR, 0)) {
tty = get_current_tty();
if (!tty) {
- unlock_kernel();
+ tty_unlock();
mutex_unlock(&tty_mutex);
return -ENXIO;
}
goto got_driver;
}
}
- unlock_kernel();
+ tty_unlock();
mutex_unlock(&tty_mutex);
return -ENODEV;
}
driver = get_tty_driver(device, &index);
if (!driver) {
- unlock_kernel();
+ tty_unlock();
mutex_unlock(&tty_mutex);
return -ENODEV;
}
tty = tty_driver_lookup_tty(driver, inode, index);
if (IS_ERR(tty)) {
- unlock_kernel();
+ tty_unlock();
mutex_unlock(&tty_mutex);
return PTR_ERR(tty);
}
mutex_unlock(&tty_mutex);
tty_driver_kref_put(driver);
if (IS_ERR(tty)) {
- unlock_kernel();
+ tty_unlock();
return PTR_ERR(tty);
}
printk(KERN_DEBUG "error %d in opening %s...", retval,
tty->name);
#endif
+ tty_unlock(); /* need to call tty_release without BTM */
tty_release(inode, filp);
- if (retval != -ERESTARTSYS) {
- unlock_kernel();
+ if (retval != -ERESTARTSYS)
return retval;
- }
- if (signal_pending(current)) {
- unlock_kernel();
+
+ if (signal_pending(current))
return retval;
- }
+
schedule();
/*
* Need to reset f_op in case a hangup happened.
*/
+ tty_lock();
if (filp->f_op == &hung_up_tty_fops)
filp->f_op = &tty_fops;
- unlock_kernel();
+ tty_unlock();
goto retry_open;
}
- unlock_kernel();
+ tty_unlock();
mutex_lock(&tty_mutex);
- lock_kernel();
+ tty_lock();
spin_lock_irq(¤t->sighand->siglock);
if (!noctty &&
current->signal->leader &&
tty->session == NULL)
__proc_set_tty(current, tty);
spin_unlock_irq(¤t->sighand->siglock);
- unlock_kernel();
+ tty_unlock();
mutex_unlock(&tty_mutex);
return 0;
}
struct tty_ldisc *ld;
int ret = 0;
- tty = (struct tty_struct *)filp->private_data;
+ tty = filp->private_data;
if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_poll"))
return 0;
return ret;
}
-static int tty_fasync(int fd, struct file *filp, int on)
+static int __tty_fasync(int fd, struct file *filp, int on)
{
struct tty_struct *tty;
unsigned long flags;
int retval = 0;
- lock_kernel();
- tty = (struct tty_struct *)filp->private_data;
+ tty = filp->private_data;
if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_fasync"))
goto out;
}
retval = 0;
out:
- unlock_kernel();
+ return retval;
+}
+
+static int tty_fasync(int fd, struct file *filp, int on)
+{
+ int retval;
+ tty_lock();
+ retval = __tty_fasync(fd, filp, on);
+ tty_unlock();
return retval;
}
struct tty_ldisc *ld;
struct inode *inode = file->f_dentry->d_inode;
- tty = (struct tty_struct *)file->private_data;
+ tty = file->private_data;
if (tty_paranoia_check(tty, inode, "tty_ioctl"))
return -EINVAL;
/* See if packet mode change of state. */
if (tty->link && tty->link->packet) {
+ int extproc = (old_termios.c_lflag & EXTPROC) |
+ (tty->termios->c_lflag & EXTPROC);
int old_flow = ((old_termios.c_iflag & IXON) &&
(old_termios.c_cc[VSTOP] == '\023') &&
(old_termios.c_cc[VSTART] == '\021'));
int new_flow = (I_IXON(tty) &&
STOP_CHAR(tty) == '\023' &&
START_CHAR(tty) == '\021');
- if (old_flow != new_flow) {
+ if ((old_flow != new_flow) || extproc) {
spin_lock_irqsave(&tty->ctrl_lock, flags);
- tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
- if (new_flow)
- tty->ctrl_status |= TIOCPKT_DOSTOP;
- else
- tty->ctrl_status |= TIOCPKT_NOSTOP;
+ if (old_flow != new_flow) {
+ tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
+ if (new_flow)
+ tty->ctrl_status |= TIOCPKT_DOSTOP;
+ else
+ tty->ctrl_status |= TIOCPKT_NOSTOP;
+ }
+ if (extproc)
+ tty->ctrl_status |= TIOCPKT_IOCTL;
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
wake_up_interruptible(&tty->link->read_wait);
}
*
* A helper opening method. Also a convenient debugging and check
* point.
+ *
+ * Locking: always called with BTM already held.
*/
static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags));
if (ld->ops->open) {
int ret;
- /* BKL here locks verus a hangup event */
- lock_kernel();
+ /* BTM here locks versus a hangup event */
+ WARN_ON(!tty_locked());
ret = ld->ops->open(tty);
- unlock_kernel();
return ret;
}
return 0;
if (IS_ERR(new_ldisc))
return PTR_ERR(new_ldisc);
- lock_kernel();
+ tty_lock();
/*
* We need to look at the tty locking here for pty/tty pairs
* when both sides try to change in parallel.
*/
if (tty->ldisc->ops->num == ldisc) {
- unlock_kernel();
+ tty_unlock();
tty_ldisc_put(new_ldisc);
return 0;
}
- unlock_kernel();
+ tty_unlock();
/*
* Problem: What do we do if this blocks ?
* We could deadlock here
tty_wait_until_sent(tty, 0);
+ tty_lock();
mutex_lock(&tty->ldisc_mutex);
/*
while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) {
mutex_unlock(&tty->ldisc_mutex);
+ tty_unlock();
wait_event(tty_ldisc_wait,
test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0);
+ tty_lock();
mutex_lock(&tty->ldisc_mutex);
}
- lock_kernel();
-
set_bit(TTY_LDISC_CHANGING, &tty->flags);
/*
o_ldisc = tty->ldisc;
- unlock_kernel();
+ tty_unlock();
/*
* Make sure we don't change while someone holds a
* reference to the line discipline. The TTY_LDISC bit
flush_scheduled_work();
+ tty_lock();
mutex_lock(&tty->ldisc_mutex);
- lock_kernel();
if (test_bit(TTY_HUPPED, &tty->flags)) {
/* We were raced by the hangup method. It will have stomped
the ldisc data and closed the ldisc down */
clear_bit(TTY_LDISC_CHANGING, &tty->flags);
mutex_unlock(&tty->ldisc_mutex);
tty_ldisc_put(new_ldisc);
- unlock_kernel();
+ tty_unlock();
return -EIO;
}
if (o_work)
schedule_delayed_work(&o_tty->buf.work, 1);
mutex_unlock(&tty->ldisc_mutex);
- unlock_kernel();
+ tty_unlock();
return retval;
}
* Avoid racing set_ldisc or tty_ldisc_release
*/
mutex_lock(&tty->ldisc_mutex);
- tty_ldisc_halt(tty);
+
+ /*
+ * this is like tty_ldisc_halt, but we need to give up
+ * the BTM before calling cancel_delayed_work_sync,
+ * which may need to wait for another function taking the BTM
+ */
+ clear_bit(TTY_LDISC, &tty->flags);
+ tty_unlock();
+ cancel_delayed_work_sync(&tty->buf.work);
+ mutex_unlock(&tty->ldisc_mutex);
+
+ tty_lock();
+ mutex_lock(&tty->ldisc_mutex);
+
/* At this point we have a closed ldisc and we want to
reopen it. We could defer this to the next open but
it means auditing a lot of other paths so this is
* race with the set_ldisc code path.
*/
+ tty_unlock();
tty_ldisc_halt(tty);
flush_scheduled_work();
+ tty_lock();
mutex_lock(&tty->ldisc_mutex);
/*
--- /dev/null
+/*
+ * drivers/char/tty_lock.c
+ */
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include <linux/semaphore.h>
+#include <linux/sched.h>
+
+/*
+ * The 'big tty mutex'
+ *
+ * This mutex is taken and released by tty_lock() and tty_unlock(),
+ * replacing the older big kernel lock.
+ * It can no longer be taken recursively, and does not get
+ * released implicitly while sleeping.
+ *
+ * Don't use in new code.
+ */
+static DEFINE_MUTEX(big_tty_mutex);
+struct task_struct *__big_tty_mutex_owner;
+EXPORT_SYMBOL_GPL(__big_tty_mutex_owner);
+
+/*
+ * Getting the big tty mutex.
+ */
+void __lockfunc tty_lock(void)
+{
+ struct task_struct *task = current;
+
+ WARN_ON(__big_tty_mutex_owner == task);
+
+ mutex_lock(&big_tty_mutex);
+ __big_tty_mutex_owner = task;
+}
+EXPORT_SYMBOL(tty_lock);
+
+void __lockfunc tty_unlock(void)
+{
+ struct task_struct *task = current;
+
+ WARN_ON(__big_tty_mutex_owner != task);
+ __big_tty_mutex_owner = NULL;
+
+ mutex_unlock(&big_tty_mutex);
+}
+EXPORT_SYMBOL(tty_unlock);
/* block if port is in the process of being closed */
if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
- wait_event_interruptible(port->close_wait,
+ wait_event_interruptible_tty(port->close_wait,
!(port->flags & ASYNC_CLOSING));
if (port->flags & ASYNC_HUP_NOTIFY)
return -EAGAIN;
retval = -ERESTARTSYS;
break;
}
+ tty_unlock();
schedule();
+ tty_lock();
}
finish_wait(&port->open_wait, &wait);
unsigned int currcons = iminor(inode) & 127;
int ret = 0;
- lock_kernel();
+ tty_lock();
if(currcons && !vc_cons_allocated(currcons-1))
ret = -ENXIO;
- unlock_kernel();
+ tty_unlock();
return ret;
}
#include <asm/system.h>
#include <linux/uaccess.h>
#include <linux/kdb.h>
+#include <linux/ctype.h>
#define MAX_NR_CON_DRIVER 16
return p;
}
+/* Called from the keyboard irq path.. */
static inline void scrolldelta(int lines)
{
+ /* FIXME */
+ /* scrolldelta needs some kind of consistency lock, but the BKL was
+ and still is not protecting versus the scheduled back end */
scrollback_delta += lines;
schedule_console_callback();
}
update_attr(vc);
clear_buffer_attributes(vc);
}
- if (update && vc->vc_mode != KD_GRAPHICS)
+
+ /* Forcibly update if we're panicing */
+ if ((update && vc->vc_mode != KD_GRAPHICS) ||
+ vt_force_oops_output(vc))
do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2);
}
set_cursor(vc);
vc->vc_hi_font_mask = 0;
vc->vc_complement_mask = 0;
vc->vc_can_do_color = 0;
+ vc->vc_panic_force_write = false;
vc->vc_sw->con_init(vc, init);
if (!vc->vc_complement_mask)
vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
if (!vc)
return -ENOMEM;
vc_cons[currcons].d = vc;
+ tty_port_init(&vc->port);
INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
visual_init(vc, currcons, 1);
if (!*vc->vc_uni_pagedir_loc)
* Resize a virtual console as seen from the console end of things. We
* use the common vc_do_resize methods to update the structures. The
* caller must hold the console sem to protect console internals and
- * vc->vc_tty
+ * vc->port.tty
*/
int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows)
{
- return vc_do_resize(vc->vc_tty, vc, cols, rows);
+ return vc_do_resize(vc->port.tty, vc, cols, rows);
}
/**
vc->vc_state = ESnormal;
return;
case ESpalette:
- if ( (c>='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) {
- vc->vc_par[vc->vc_npar++] = (c > '9' ? (c & 0xDF) - 'A' + 10 : c - '0');
+ if (isxdigit(c)) {
+ vc->vc_par[vc->vc_npar++] = hex_to_bin(c);
if (vc->vc_npar == 7) {
int i = vc->vc_par[0] * 3, j = 1;
vc->vc_palette[i] = 16 * vc->vc_par[j++];
goto quit;
}
- if (vc->vc_mode != KD_TEXT)
+ if (vc->vc_mode != KD_TEXT && !vt_force_oops_output(vc))
goto quit;
/* undraw cursor first */
return -EFAULT;
ret = 0;
- lock_kernel();
-
switch (type)
{
case TIOCL_SETSEL:
ret = -EINVAL;
break;
}
- unlock_kernel();
return ret;
}
struct vc_data *vc = vc_cons[currcons].d;
/* Still being freed */
- if (vc->vc_tty) {
+ if (vc->port.tty) {
release_console_sem();
return -ERESTARTSYS;
}
tty->driver_data = vc;
- vc->vc_tty = tty;
+ vc->port.tty = tty;
if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
tty->winsize.ws_row = vc_cons[currcons].d->vc_rows;
struct vc_data *vc = tty->driver_data;
BUG_ON(vc == NULL);
acquire_console_sem();
- vc->vc_tty = NULL;
+ vc->port.tty = NULL;
release_console_sem();
tty_shutdown(tty);
}
for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
vc_cons[currcons].d = vc = kzalloc(sizeof(struct vc_data), GFP_NOWAIT);
INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK);
+ tty_port_init(&vc->port);
visual_init(vc, currcons, 1);
vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT);
vc_init(vc, vc->vc_rows, vc->vc_cols,
return;
}
vc = vc_cons[fg_console].d;
- if (vc->vc_mode != KD_TEXT)
+ /* Try to unblank in oops case too */
+ if (vc->vc_mode != KD_TEXT && !vt_force_oops_output(vc))
return; /* but leave console_blanked != 0 */
if (blankinterval) {
}
console_blanked = 0;
- if (vc->vc_sw->con_blank(vc, 0, leaving_gfx))
+ if (vc->vc_sw->con_blank(vc, 0, leaving_gfx) || vt_force_oops_output(vc))
/* Low-level driver cannot restore -> do it ourselves */
update_screen(vc);
if (console_blank_hook)
list_add(&vw->list, &vt_events);
spin_unlock_irqrestore(&vt_event_lock, flags);
/* Wait for it to pass */
- wait_event_interruptible(vt_event_waitqueue, vw->done);
+ wait_event_interruptible_tty(vt_event_waitqueue, vw->done);
/* Dequeue it */
spin_lock_irqsave(&vt_event_lock, flags);
list_del(&vw->list);
console = vc->vc_num;
- lock_kernel();
+ tty_lock();
if (!vc_cons_allocated(console)) { /* impossible? */
ret = -ENOIOCTLCMD;
ret = -ENOIOCTLCMD;
}
out:
- unlock_kernel();
+ tty_unlock();
return ret;
eperm:
ret = -EPERM;
acquire_console_sem();
vc = vc_con->d;
if (vc) {
- tty = vc->vc_tty;
+ tty = vc->port.tty;
/*
* SAK should also work in all raw modes and reset
* them properly.
console = vc->vc_num;
- lock_kernel();
+ tty_lock();
if (!vc_cons_allocated(console)) { /* impossible? */
ret = -ENOIOCTLCMD;
goto fallback;
}
out:
- unlock_kernel();
+ tty_unlock();
return ret;
fallback:
- unlock_kernel();
+ tty_unlock();
return vt_ioctl(tty, file, cmd, arg);
}
return -EIO;
}
release_console_sem();
+ tty_lock();
if (vt_waitactive(vt + 1)) {
pr_debug("Suspend: Can't switch VCs.");
+ tty_unlock();
return -EINTR;
}
+ tty_unlock();
return prev;
}
struct buffer_desc {
u32 phys_next;
+#ifdef __ARMEB__
u16 buf_len;
u16 pkt_len;
+#else
+ u16 pkt_len;
+ u16 buf_len;
+#endif
u32 phys_addr;
u32 __reserved[4];
struct buffer_desc *next;
};
struct crypt_ctl {
+#ifdef __ARMEB__
u8 mode; /* NPE_OP_* operation mode */
u8 init_len;
u16 reserved;
+#else
+ u16 reserved;
+ u8 init_len;
+ u8 mode; /* NPE_OP_* operation mode */
+#endif
u8 iv[MAX_IVLEN]; /* IV for CBC mode or CTR IV for CTR mode */
u32 icv_rev_aes; /* icv or rev aes */
u32 src_buf;
u32 dst_buf;
+#ifdef __ARMEB__
u16 auth_offs; /* Authentication start offset */
u16 auth_len; /* Authentication data length */
u16 crypt_offs; /* Cryption start offset */
u16 crypt_len; /* Cryption data length */
+#else
+ u16 auth_len; /* Authentication data length */
+ u16 auth_offs; /* Authentication start offset */
+ u16 crypt_len; /* Cryption data length */
+ u16 crypt_offs; /* Cryption start offset */
+#endif
u32 aadAddr; /* Additional Auth Data Addr for CCM mode */
u32 crypto_ctx; /* NPE Crypto Param structure address */
/* write cfg word to cryptinfo */
cfgword = algo->cfgword | ( authsize << 6); /* (authsize/4) << 8 */
+#ifndef __ARMEB__
+ cfgword ^= 0xAA000000; /* change the "byte swap" flags */
+#endif
*(u32*)cinfo = cpu_to_be32(cfgword);
cinfo += sizeof(cfgword);
config SH_DMAE
tristate "Renesas SuperH DMAC support"
- depends on SUPERH && SH_DMA
+ depends on (SUPERH && SH_DMA) || (ARM && ARCH_SHMOBILE)
depends on !SH_DMA_API
select DMA_ENGINE
help
return ret;
}
-#if defined(CONFIG_CPU_SH4)
+#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
static irqreturn_t sh_dmae_err(int irq, void *data)
{
struct sh_dmae_device *shdev = (struct sh_dmae_device *)data;
/* Default transfer size of 32 bytes requires 32-byte alignment */
shdev->common.copy_align = LOG2_DEFAULT_XFER_SIZE;
-#if defined(CONFIG_CPU_SH4)
+#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
chanirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
if (!chanirq_res)
#else
chanirq_res = errirq_res;
-#endif /* CONFIG_CPU_SH4 */
+#endif /* CONFIG_CPU_SH4 || CONFIG_ARCH_SHMOBILE */
if (chanirq_res->start == chanirq_res->end &&
!platform_get_resource(pdev, IORESOURCE_IRQ, 1)) {
chan_probe_err:
sh_dmae_chan_remove(shdev);
eirqres:
-#if defined(CONFIG_CPU_SH4)
+#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
free_irq(errirq, shdev);
eirq_err:
#endif
/* wake up device */
rc = pci_enable_device(pdev);
- if (rc == -EIO)
+ if (rc)
return rc;
/* now probe and enable the device */
/* wake up device */
rc = pci_enable_device(pdev);
- if (rc == -EIO)
+ if (rc)
return rc;
/* now probe and enable the device */
#endif
static u32 orig_l2_err_disable;
-#ifdef CONFIG_MPC85xx
+#ifdef CONFIG_FSL_SOC_BOOKE
static u32 orig_hid1[2];
#endif
{ .compatible = "fsl,mpc8555-l2-cache-controller", },
{ .compatible = "fsl,mpc8560-l2-cache-controller", },
{ .compatible = "fsl,mpc8568-l2-cache-controller", },
+ { .compatible = "fsl,mpc8569-l2-cache-controller", },
{ .compatible = "fsl,mpc8572-l2-cache-controller", },
+ { .compatible = "fsl,p1020-l2-cache-controller", },
+ { .compatible = "fsl,p1021-l2-cache-controller", },
{ .compatible = "fsl,p2020-l2-cache-controller", },
{},
};
{ .compatible = "fsl,mpc8569-memory-controller", },
{ .compatible = "fsl,mpc8572-memory-controller", },
{ .compatible = "fsl,mpc8349-memory-controller", },
+ { .compatible = "fsl,p1020-memory-controller", },
+ { .compatible = "fsl,p1021-memory-controller", },
{ .compatible = "fsl,p2020-memory-controller", },
+ { .compatible = "fsl,p4080-memory-controller", },
{},
};
MODULE_DEVICE_TABLE(of, mpc85xx_mc_err_of_match);
},
};
-#ifdef CONFIG_MPC85xx
+#ifdef CONFIG_FSL_SOC_BOOKE
static void __init mpc85xx_mc_clear_rfxe(void *data)
{
orig_hid1[smp_processor_id()] = mfspr(SPRN_HID1);
printk(KERN_WARNING EDAC_MOD_STR "PCI fails to register\n");
#endif
-#ifdef CONFIG_MPC85xx
+#ifdef CONFIG_FSL_SOC_BOOKE
/*
* need to clear HID1[RFXE] to disable machine check int
* so we can catch it
module_init(mpc85xx_mc_init);
-#ifdef CONFIG_MPC85xx
+#ifdef CONFIG_FSL_SOC_BOOKE
static void __exit mpc85xx_mc_restore_hid1(void *data)
{
mtspr(SPRN_HID1, orig_hid1[smp_processor_id()]);
static void __exit mpc85xx_mc_exit(void)
{
-#ifdef CONFIG_MPC85xx
+#ifdef CONFIG_FSL_SOC_BOOKE
on_each_cpu(mpc85xx_mc_restore_hid1, NULL, 0);
#endif
#ifdef CONFIG_PCI
This driver provides an in-kernel interface to those GPIOs using
platform-neutral GPIO calls.
+config GPIO_SX150X
+ bool "Semtech SX150x I2C GPIO expander"
+ depends on I2C=y
+ default n
+ help
+ Say yes here to provide support for Semtech SX150-series I2C
+ GPIO expanders. Compatible models include:
+
+ 8 bits: sx1508q
+ 16 bits: sx1509q
+
config GPIO_TC35892
bool "TC35892 GPIOs"
depends on MFD_TC35892
obj-$(CONFIG_GPIO_SCH) += sch_gpio.o
obj-$(CONFIG_GPIO_RDC321X) += rdc321x-gpio.o
obj-$(CONFIG_GPIO_JANZ_TTL) += janz-ttl.o
+obj-$(CONFIG_GPIO_SX150X) += sx150x.o
#define FLAG_TRIG_RISE 6 /* trigger on rising edge */
#define FLAG_ACTIVE_LOW 7 /* sysfs value has active low */
-#define PDESC_ID_SHIFT 16 /* add new flags before this one */
+#define ID_SHIFT 16 /* add new flags before this one */
-#define GPIO_FLAGS_MASK ((1 << PDESC_ID_SHIFT) - 1)
+#define GPIO_FLAGS_MASK ((1 << ID_SHIFT) - 1)
#define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))
#ifdef CONFIG_DEBUG_FS
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
#ifdef CONFIG_GPIO_SYSFS
-struct poll_desc {
- struct work_struct work;
- struct sysfs_dirent *value_sd;
-};
-
-static struct idr pdesc_idr;
+static DEFINE_IDR(dirent_idr);
#endif
static inline void desc_set_label(struct gpio_desc *d, const char *label)
static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
{
- struct work_struct *work = priv;
+ struct sysfs_dirent *value_sd = priv;
- schedule_work(work);
+ sysfs_notify_dirent(value_sd);
return IRQ_HANDLED;
}
-static void gpio_notify_sysfs(struct work_struct *work)
-{
- struct poll_desc *pdesc;
-
- pdesc = container_of(work, struct poll_desc, work);
- sysfs_notify_dirent(pdesc->value_sd);
-}
-
static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
unsigned long gpio_flags)
{
- struct poll_desc *pdesc;
+ struct sysfs_dirent *value_sd;
unsigned long irq_flags;
int ret, irq, id;
if (irq < 0)
return -EIO;
- id = desc->flags >> PDESC_ID_SHIFT;
- pdesc = idr_find(&pdesc_idr, id);
- if (pdesc) {
- free_irq(irq, &pdesc->work);
- cancel_work_sync(&pdesc->work);
- }
+ id = desc->flags >> ID_SHIFT;
+ value_sd = idr_find(&dirent_idr, id);
+ if (value_sd)
+ free_irq(irq, value_sd);
desc->flags &= ~GPIO_TRIGGER_MASK;
if (!gpio_flags) {
ret = 0;
- goto free_sd;
+ goto free_id;
}
irq_flags = IRQF_SHARED;
irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
- if (!pdesc) {
- pdesc = kmalloc(sizeof(*pdesc), GFP_KERNEL);
- if (!pdesc) {
- ret = -ENOMEM;
+ if (!value_sd) {
+ value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
+ if (!value_sd) {
+ ret = -ENODEV;
goto err_out;
}
do {
ret = -ENOMEM;
- if (idr_pre_get(&pdesc_idr, GFP_KERNEL))
- ret = idr_get_new_above(&pdesc_idr,
- pdesc, 1, &id);
+ if (idr_pre_get(&dirent_idr, GFP_KERNEL))
+ ret = idr_get_new_above(&dirent_idr, value_sd,
+ 1, &id);
} while (ret == -EAGAIN);
if (ret)
- goto free_mem;
+ goto free_sd;
desc->flags &= GPIO_FLAGS_MASK;
- desc->flags |= (unsigned long)id << PDESC_ID_SHIFT;
+ desc->flags |= (unsigned long)id << ID_SHIFT;
- if (desc->flags >> PDESC_ID_SHIFT != id) {
+ if (desc->flags >> ID_SHIFT != id) {
ret = -ERANGE;
goto free_id;
}
-
- pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
- if (!pdesc->value_sd) {
- ret = -ENODEV;
- goto free_id;
- }
- INIT_WORK(&pdesc->work, gpio_notify_sysfs);
}
- ret = request_irq(irq, gpio_sysfs_irq, irq_flags,
- "gpiolib", &pdesc->work);
- if (ret)
- goto free_sd;
+ ret = request_any_context_irq(irq, gpio_sysfs_irq, irq_flags,
+ "gpiolib", value_sd);
+ if (ret < 0)
+ goto free_id;
desc->flags |= gpio_flags;
return 0;
-free_sd:
- if (pdesc)
- sysfs_put(pdesc->value_sd);
free_id:
- idr_remove(&pdesc_idr, id);
+ idr_remove(&dirent_idr, id);
desc->flags &= GPIO_FLAGS_MASK;
-free_mem:
- kfree(pdesc);
+free_sd:
+ if (value_sd)
+ sysfs_put(value_sd);
err_out:
return ret;
}
unsigned long flags;
unsigned gpio;
- idr_init(&pdesc_idr);
-
status = class_register(&gpio_class);
if (status < 0)
return status;
if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) {
if (chip->free) {
spin_unlock_irqrestore(&gpio_lock, flags);
- might_sleep_if(extra_checks && chip->can_sleep);
+ might_sleep_if(chip->can_sleep);
chip->free(chip, gpio - chip->base);
spin_lock_irqsave(&gpio_lock, flags);
}
spin_unlock_irqrestore(&gpio_lock, flags);
- might_sleep_if(extra_checks && chip->can_sleep);
+ might_sleep_if(chip->can_sleep);
if (status) {
status = chip->request(chip, gpio);
spin_unlock_irqrestore(&gpio_lock, flags);
- might_sleep_if(extra_checks && chip->can_sleep);
+ might_sleep_if(chip->can_sleep);
if (status) {
status = chip->request(chip, gpio);
spin_unlock_irqrestore(&gpio_lock, flags);
- might_sleep_if(extra_checks && chip->can_sleep);
+ might_sleep_if(chip->can_sleep);
return chip->set_debounce(chip, gpio, debounce);
struct gpio_chip *chip;
chip = gpio_to_chip(gpio);
- WARN_ON(extra_checks && chip->can_sleep);
+ WARN_ON(chip->can_sleep);
return chip->get ? chip->get(chip, gpio - chip->base) : 0;
}
EXPORT_SYMBOL_GPL(__gpio_get_value);
struct gpio_chip *chip;
chip = gpio_to_chip(gpio);
- WARN_ON(extra_checks && chip->can_sleep);
+ WARN_ON(chip->can_sleep);
chip->set(chip, gpio - chip->base, value);
}
EXPORT_SYMBOL_GPL(__gpio_set_value);
{
struct max7301 *ts = container_of(chip, struct max7301, chip);
u8 *config;
- u8 offset_bits;
+ u8 offset_bits, pin_config;
int ret;
/* First 4 pins are unused in the controller */
config = &ts->port_config[offset >> 2];
+ if (ts->input_pullup_active & BIT(offset))
+ pin_config = PIN_CONFIG_IN_PULLUP;
+ else
+ pin_config = PIN_CONFIG_IN_WO_PULLUP;
+
mutex_lock(&ts->lock);
- /* Standard GPIO API doesn't support pull-ups, has to be extended.
- * Hard-coding no pollup for now. */
*config = (*config & ~(PIN_CONFIG_MASK << offset_bits))
- | (PIN_CONFIG_IN_WO_PULLUP << offset_bits);
+ | (pin_config << offset_bits);
ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config);
/* Power up the chip and disable IRQ output */
ts->write(dev, 0x04, 0x01);
+ ts->input_pullup_active = pdata->input_pullup_active;
ts->chip.label = dev->driver->name;
ts->chip.direction_input = max7301_direction_input;
ts->chip.owner = THIS_MODULE;
/*
- * tristate all pins in hardware and cache the
+ * initialize pullups according to platform data and cache the
* register values for later use.
*/
for (i = 1; i < 8; i++) {
int j;
- /* 0xAA means input with internal pullup disabled */
- ts->write(dev, 0x08 + i, 0xAA);
+ /*
+ * initialize port_config with "0xAA", which means
+ * input with internal pullup disabled. This is needed
+ * to avoid writing zeros (in the inner for loop),
+ * which is not allowed according to the datasheet.
+ */
ts->port_config[i] = 0xAA;
for (j = 0; j < 4; j++) {
int offset = (i - 1) * 4 + j;
pdata = client->dev.platform_data;
if (!pdata) {
dev_dbg(&client->dev, "no platform data\n");
- return -EINVAL;
}
/* Allocate, initialize, and register this gpio_chip. */
mutex_init(&gpio->lock);
- gpio->chip.base = pdata->gpio_base;
+ gpio->chip.base = pdata ? pdata->gpio_base : -1;
gpio->chip.can_sleep = 1;
gpio->chip.dev = &client->dev;
gpio->chip.owner = THIS_MODULE;
* to zero, our software copy of the "latch" then matches the chip's
* all-ones reset state. Otherwise it flags pins to be driven low.
*/
- gpio->out = ~pdata->n_latch;
+ gpio->out = pdata ? ~pdata->n_latch : ~0;
status = gpiochip_add(&gpio->chip);
if (status < 0)
/* Let platform code set up the GPIOs and their users.
* Now is the first time anyone could use them.
*/
- if (pdata->setup) {
+ if (pdata && pdata->setup) {
status = pdata->setup(client,
gpio->chip.base, gpio->chip.ngpio,
pdata->context);
struct pcf857x *gpio = i2c_get_clientdata(client);
int status = 0;
- if (pdata->teardown) {
+ if (pdata && pdata->teardown) {
status = pdata->teardown(client,
gpio->chip.base, gpio->chip.ngpio,
pdata->context);
--- /dev/null
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/i2c/sx150x.h>
+
+struct sx150x_device_data {
+ u8 reg_pullup;
+ u8 reg_pulldn;
+ u8 reg_drain;
+ u8 reg_polarity;
+ u8 reg_dir;
+ u8 reg_data;
+ u8 reg_irq_mask;
+ u8 reg_irq_src;
+ u8 reg_sense;
+ u8 reg_clock;
+ u8 reg_misc;
+ u8 reg_reset;
+ u8 ngpios;
+};
+
+struct sx150x_chip {
+ struct gpio_chip gpio_chip;
+ struct i2c_client *client;
+ const struct sx150x_device_data *dev_cfg;
+ int irq_summary;
+ int irq_base;
+ u32 irq_sense;
+ unsigned long irq_set_type_pending;
+ struct irq_chip irq_chip;
+ struct mutex lock;
+};
+
+static const struct sx150x_device_data sx150x_devices[] = {
+ [0] = { /* sx1508q */
+ .reg_pullup = 0x03,
+ .reg_pulldn = 0x04,
+ .reg_drain = 0x05,
+ .reg_polarity = 0x06,
+ .reg_dir = 0x07,
+ .reg_data = 0x08,
+ .reg_irq_mask = 0x09,
+ .reg_irq_src = 0x0c,
+ .reg_sense = 0x0b,
+ .reg_clock = 0x0f,
+ .reg_misc = 0x10,
+ .reg_reset = 0x7d,
+ .ngpios = 8
+ },
+ [1] = { /* sx1509q */
+ .reg_pullup = 0x07,
+ .reg_pulldn = 0x09,
+ .reg_drain = 0x0b,
+ .reg_polarity = 0x0d,
+ .reg_dir = 0x0f,
+ .reg_data = 0x11,
+ .reg_irq_mask = 0x13,
+ .reg_irq_src = 0x19,
+ .reg_sense = 0x17,
+ .reg_clock = 0x1e,
+ .reg_misc = 0x1f,
+ .reg_reset = 0x7d,
+ .ngpios = 16
+ },
+};
+
+static const struct i2c_device_id sx150x_id[] = {
+ {"sx1508q", 0},
+ {"sx1509q", 1},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, sx150x_id);
+
+static s32 sx150x_i2c_write(struct i2c_client *client, u8 reg, u8 val)
+{
+ s32 err = i2c_smbus_write_byte_data(client, reg, val);
+
+ if (err < 0)
+ dev_warn(&client->dev,
+ "i2c write fail: can't write %02x to %02x: %d\n",
+ val, reg, err);
+ return err;
+}
+
+static s32 sx150x_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+ s32 err = i2c_smbus_read_byte_data(client, reg);
+
+ if (err >= 0)
+ *val = err;
+ else
+ dev_warn(&client->dev,
+ "i2c read fail: can't read from %02x: %d\n",
+ reg, err);
+ return err;
+}
+
+static inline bool offset_is_oscio(struct sx150x_chip *chip, unsigned offset)
+{
+ return (chip->dev_cfg->ngpios == offset);
+}
+
+/*
+ * These utility functions solve the common problem of locating and setting
+ * configuration bits. Configuration bits are grouped into registers
+ * whose indexes increase downwards. For example, with eight-bit registers,
+ * sixteen gpios would have their config bits grouped in the following order:
+ * REGISTER N-1 [ f e d c b a 9 8 ]
+ * N [ 7 6 5 4 3 2 1 0 ]
+ *
+ * For multi-bit configurations, the pattern gets wider:
+ * REGISTER N-3 [ f f e e d d c c ]
+ * N-2 [ b b a a 9 9 8 8 ]
+ * N-1 [ 7 7 6 6 5 5 4 4 ]
+ * N [ 3 3 2 2 1 1 0 0 ]
+ *
+ * Given the address of the starting register 'N', the index of the gpio
+ * whose configuration we seek to change, and the width in bits of that
+ * configuration, these functions allow us to locate the correct
+ * register and mask the correct bits.
+ */
+static inline void sx150x_find_cfg(u8 offset, u8 width,
+ u8 *reg, u8 *mask, u8 *shift)
+{
+ *reg -= offset * width / 8;
+ *mask = (1 << width) - 1;
+ *shift = (offset * width) % 8;
+ *mask <<= *shift;
+}
+
+static s32 sx150x_write_cfg(struct sx150x_chip *chip,
+ u8 offset, u8 width, u8 reg, u8 val)
+{
+ u8 mask;
+ u8 data;
+ u8 shift;
+ s32 err;
+
+ sx150x_find_cfg(offset, width, ®, &mask, &shift);
+ err = sx150x_i2c_read(chip->client, reg, &data);
+ if (err < 0)
+ return err;
+
+ data &= ~mask;
+ data |= (val << shift) & mask;
+ return sx150x_i2c_write(chip->client, reg, data);
+}
+
+static int sx150x_get_io(struct sx150x_chip *chip, unsigned offset)
+{
+ u8 reg = chip->dev_cfg->reg_data;
+ u8 mask;
+ u8 data;
+ u8 shift;
+ s32 err;
+
+ sx150x_find_cfg(offset, 1, ®, &mask, &shift);
+ err = sx150x_i2c_read(chip->client, reg, &data);
+ if (err >= 0)
+ err = (data & mask) != 0 ? 1 : 0;
+
+ return err;
+}
+
+static void sx150x_set_oscio(struct sx150x_chip *chip, int val)
+{
+ sx150x_i2c_write(chip->client,
+ chip->dev_cfg->reg_clock,
+ (val ? 0x1f : 0x10));
+}
+
+static void sx150x_set_io(struct sx150x_chip *chip, unsigned offset, int val)
+{
+ sx150x_write_cfg(chip,
+ offset,
+ 1,
+ chip->dev_cfg->reg_data,
+ (val ? 1 : 0));
+}
+
+static int sx150x_io_input(struct sx150x_chip *chip, unsigned offset)
+{
+ return sx150x_write_cfg(chip,
+ offset,
+ 1,
+ chip->dev_cfg->reg_dir,
+ 1);
+}
+
+static int sx150x_io_output(struct sx150x_chip *chip, unsigned offset, int val)
+{
+ int err;
+
+ err = sx150x_write_cfg(chip,
+ offset,
+ 1,
+ chip->dev_cfg->reg_data,
+ (val ? 1 : 0));
+ if (err >= 0)
+ err = sx150x_write_cfg(chip,
+ offset,
+ 1,
+ chip->dev_cfg->reg_dir,
+ 0);
+ return err;
+}
+
+static int sx150x_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ struct sx150x_chip *chip;
+ int status = -EINVAL;
+
+ chip = container_of(gc, struct sx150x_chip, gpio_chip);
+
+ if (!offset_is_oscio(chip, offset)) {
+ mutex_lock(&chip->lock);
+ status = sx150x_get_io(chip, offset);
+ mutex_unlock(&chip->lock);
+ }
+
+ return status;
+}
+
+static void sx150x_gpio_set(struct gpio_chip *gc, unsigned offset, int val)
+{
+ struct sx150x_chip *chip;
+
+ chip = container_of(gc, struct sx150x_chip, gpio_chip);
+
+ mutex_lock(&chip->lock);
+ if (offset_is_oscio(chip, offset))
+ sx150x_set_oscio(chip, val);
+ else
+ sx150x_set_io(chip, offset, val);
+ mutex_unlock(&chip->lock);
+}
+
+static int sx150x_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+ struct sx150x_chip *chip;
+ int status = -EINVAL;
+
+ chip = container_of(gc, struct sx150x_chip, gpio_chip);
+
+ if (!offset_is_oscio(chip, offset)) {
+ mutex_lock(&chip->lock);
+ status = sx150x_io_input(chip, offset);
+ mutex_unlock(&chip->lock);
+ }
+ return status;
+}
+
+static int sx150x_gpio_direction_output(struct gpio_chip *gc,
+ unsigned offset,
+ int val)
+{
+ struct sx150x_chip *chip;
+ int status = 0;
+
+ chip = container_of(gc, struct sx150x_chip, gpio_chip);
+
+ if (!offset_is_oscio(chip, offset)) {
+ mutex_lock(&chip->lock);
+ status = sx150x_io_output(chip, offset, val);
+ mutex_unlock(&chip->lock);
+ }
+ return status;
+}
+
+static int sx150x_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+ struct sx150x_chip *chip;
+
+ chip = container_of(gc, struct sx150x_chip, gpio_chip);
+
+ if (offset >= chip->dev_cfg->ngpios)
+ return -EINVAL;
+
+ if (chip->irq_base < 0)
+ return -EINVAL;
+
+ return chip->irq_base + offset;
+}
+
+static void sx150x_irq_mask(unsigned int irq)
+{
+ struct irq_chip *ic = get_irq_chip(irq);
+ struct sx150x_chip *chip;
+ unsigned n;
+
+ chip = container_of(ic, struct sx150x_chip, irq_chip);
+ n = irq - chip->irq_base;
+
+ sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 1);
+ sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, 0);
+}
+
+static void sx150x_irq_unmask(unsigned int irq)
+{
+ struct irq_chip *ic = get_irq_chip(irq);
+ struct sx150x_chip *chip;
+ unsigned n;
+
+ chip = container_of(ic, struct sx150x_chip, irq_chip);
+ n = irq - chip->irq_base;
+
+ sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 0);
+ sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense,
+ chip->irq_sense >> (n * 2));
+}
+
+static int sx150x_irq_set_type(unsigned int irq, unsigned int flow_type)
+{
+ struct irq_chip *ic = get_irq_chip(irq);
+ struct sx150x_chip *chip;
+ unsigned n, val = 0;
+
+ if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
+ return -EINVAL;
+
+ chip = container_of(ic, struct sx150x_chip, irq_chip);
+ n = irq - chip->irq_base;
+
+ if (flow_type & IRQ_TYPE_EDGE_RISING)
+ val |= 0x1;
+ if (flow_type & IRQ_TYPE_EDGE_FALLING)
+ val |= 0x2;
+
+ chip->irq_sense &= ~(3UL << (n * 2));
+ chip->irq_sense |= val << (n * 2);
+ chip->irq_set_type_pending |= BIT(n);
+ return 0;
+}
+
+static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id)
+{
+ struct sx150x_chip *chip = (struct sx150x_chip *)dev_id;
+ unsigned nhandled = 0;
+ unsigned sub_irq;
+ unsigned n;
+ s32 err;
+ u8 val;
+ int i;
+
+ for (i = (chip->dev_cfg->ngpios / 8) - 1; i >= 0; --i) {
+ err = sx150x_i2c_read(chip->client,
+ chip->dev_cfg->reg_irq_src - i,
+ &val);
+ if (err < 0)
+ continue;
+
+ sx150x_i2c_write(chip->client,
+ chip->dev_cfg->reg_irq_src - i,
+ val);
+ for (n = 0; n < 8; ++n) {
+ if (val & (1 << n)) {
+ sub_irq = chip->irq_base + (i * 8) + n;
+ handle_nested_irq(sub_irq);
+ ++nhandled;
+ }
+ }
+ }
+
+ return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
+}
+
+static void sx150x_irq_bus_lock(unsigned int irq)
+{
+ struct irq_chip *ic = get_irq_chip(irq);
+ struct sx150x_chip *chip;
+
+ chip = container_of(ic, struct sx150x_chip, irq_chip);
+
+ mutex_lock(&chip->lock);
+}
+
+static void sx150x_irq_bus_sync_unlock(unsigned int irq)
+{
+ struct irq_chip *ic = get_irq_chip(irq);
+ struct sx150x_chip *chip;
+ unsigned n;
+
+ chip = container_of(ic, struct sx150x_chip, irq_chip);
+
+ while (chip->irq_set_type_pending) {
+ n = __ffs(chip->irq_set_type_pending);
+ chip->irq_set_type_pending &= ~BIT(n);
+ if (!(irq_to_desc(n + chip->irq_base)->status & IRQ_MASKED))
+ sx150x_write_cfg(chip, n, 2,
+ chip->dev_cfg->reg_sense,
+ chip->irq_sense >> (n * 2));
+ }
+
+ mutex_unlock(&chip->lock);
+}
+
+static void sx150x_init_chip(struct sx150x_chip *chip,
+ struct i2c_client *client,
+ kernel_ulong_t driver_data,
+ struct sx150x_platform_data *pdata)
+{
+ mutex_init(&chip->lock);
+
+ chip->client = client;
+ chip->dev_cfg = &sx150x_devices[driver_data];
+ chip->gpio_chip.label = client->name;
+ chip->gpio_chip.direction_input = sx150x_gpio_direction_input;
+ chip->gpio_chip.direction_output = sx150x_gpio_direction_output;
+ chip->gpio_chip.get = sx150x_gpio_get;
+ chip->gpio_chip.set = sx150x_gpio_set;
+ chip->gpio_chip.to_irq = sx150x_gpio_to_irq;
+ chip->gpio_chip.base = pdata->gpio_base;
+ chip->gpio_chip.can_sleep = 1;
+ chip->gpio_chip.ngpio = chip->dev_cfg->ngpios;
+ if (pdata->oscio_is_gpo)
+ ++chip->gpio_chip.ngpio;
+
+ chip->irq_chip.name = client->name;
+ chip->irq_chip.mask = sx150x_irq_mask;
+ chip->irq_chip.unmask = sx150x_irq_unmask;
+ chip->irq_chip.set_type = sx150x_irq_set_type;
+ chip->irq_chip.bus_lock = sx150x_irq_bus_lock;
+ chip->irq_chip.bus_sync_unlock = sx150x_irq_bus_sync_unlock;
+ chip->irq_summary = -1;
+ chip->irq_base = -1;
+ chip->irq_sense = 0;
+ chip->irq_set_type_pending = 0;
+}
+
+static int sx150x_init_io(struct sx150x_chip *chip, u8 base, u16 cfg)
+{
+ int err = 0;
+ unsigned n;
+
+ for (n = 0; err >= 0 && n < (chip->dev_cfg->ngpios / 8); ++n)
+ err = sx150x_i2c_write(chip->client, base - n, cfg >> (n * 8));
+ return err;
+}
+
+static int sx150x_init_hw(struct sx150x_chip *chip,
+ struct sx150x_platform_data *pdata)
+{
+ int err = 0;
+
+ err = i2c_smbus_write_word_data(chip->client,
+ chip->dev_cfg->reg_reset,
+ 0x3412);
+ if (err < 0)
+ return err;
+
+ err = sx150x_i2c_write(chip->client,
+ chip->dev_cfg->reg_misc,
+ 0x01);
+ if (err < 0)
+ return err;
+
+ err = sx150x_init_io(chip, chip->dev_cfg->reg_pullup,
+ pdata->io_pullup_ena);
+ if (err < 0)
+ return err;
+
+ err = sx150x_init_io(chip, chip->dev_cfg->reg_pulldn,
+ pdata->io_pulldn_ena);
+ if (err < 0)
+ return err;
+
+ err = sx150x_init_io(chip, chip->dev_cfg->reg_drain,
+ pdata->io_open_drain_ena);
+ if (err < 0)
+ return err;
+
+ err = sx150x_init_io(chip, chip->dev_cfg->reg_polarity,
+ pdata->io_polarity);
+ if (err < 0)
+ return err;
+
+ if (pdata->oscio_is_gpo)
+ sx150x_set_oscio(chip, 0);
+
+ return err;
+}
+
+static int sx150x_install_irq_chip(struct sx150x_chip *chip,
+ int irq_summary,
+ int irq_base)
+{
+ int err;
+ unsigned n;
+ unsigned irq;
+
+ chip->irq_summary = irq_summary;
+ chip->irq_base = irq_base;
+
+ for (n = 0; n < chip->dev_cfg->ngpios; ++n) {
+ irq = irq_base + n;
+ set_irq_chip_and_handler(irq, &chip->irq_chip, handle_edge_irq);
+ set_irq_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ set_irq_noprobe(irq);
+#endif
+ }
+
+ err = request_threaded_irq(irq_summary,
+ NULL,
+ sx150x_irq_thread_fn,
+ IRQF_SHARED | IRQF_TRIGGER_FALLING,
+ chip->irq_chip.name,
+ chip);
+ if (err < 0) {
+ chip->irq_summary = -1;
+ chip->irq_base = -1;
+ }
+
+ return err;
+}
+
+static void sx150x_remove_irq_chip(struct sx150x_chip *chip)
+{
+ unsigned n;
+ unsigned irq;
+
+ free_irq(chip->irq_summary, chip);
+
+ for (n = 0; n < chip->dev_cfg->ngpios; ++n) {
+ irq = chip->irq_base + n;
+ set_irq_handler(irq, NULL);
+ set_irq_chip(irq, NULL);
+ }
+}
+
+static int __devinit sx150x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ static const u32 i2c_funcs = I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WRITE_WORD_DATA;
+ struct sx150x_platform_data *pdata;
+ struct sx150x_chip *chip;
+ int rc;
+
+ pdata = client->dev.platform_data;
+ if (!pdata)
+ return -EINVAL;
+
+ if (!i2c_check_functionality(client->adapter, i2c_funcs))
+ return -ENOSYS;
+
+ chip = kzalloc(sizeof(struct sx150x_chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ sx150x_init_chip(chip, client, id->driver_data, pdata);
+ rc = sx150x_init_hw(chip, pdata);
+ if (rc < 0)
+ goto probe_fail_pre_gpiochip_add;
+
+ rc = gpiochip_add(&chip->gpio_chip);
+ if (rc < 0)
+ goto probe_fail_pre_gpiochip_add;
+
+ if (pdata->irq_summary >= 0) {
+ rc = sx150x_install_irq_chip(chip,
+ pdata->irq_summary,
+ pdata->irq_base);
+ if (rc < 0)
+ goto probe_fail_post_gpiochip_add;
+ }
+
+ i2c_set_clientdata(client, chip);
+
+ return 0;
+probe_fail_post_gpiochip_add:
+ WARN_ON(gpiochip_remove(&chip->gpio_chip) < 0);
+probe_fail_pre_gpiochip_add:
+ kfree(chip);
+ return rc;
+}
+
+static int __devexit sx150x_remove(struct i2c_client *client)
+{
+ struct sx150x_chip *chip;
+ int rc;
+
+ chip = i2c_get_clientdata(client);
+ rc = gpiochip_remove(&chip->gpio_chip);
+ if (rc < 0)
+ return rc;
+
+ if (chip->irq_summary >= 0)
+ sx150x_remove_irq_chip(chip);
+
+ kfree(chip);
+
+ return 0;
+}
+
+static struct i2c_driver sx150x_driver = {
+ .driver = {
+ .name = "sx150x",
+ .owner = THIS_MODULE
+ },
+ .probe = sx150x_probe,
+ .remove = __devexit_p(sx150x_remove),
+ .id_table = sx150x_id,
+};
+
+static int __init sx150x_init(void)
+{
+ return i2c_add_driver(&sx150x_driver);
+}
+subsys_initcall(sx150x_init);
+
+static void __exit sx150x_exit(void)
+{
+ return i2c_del_driver(&sx150x_driver);
+}
+module_exit(sx150x_exit);
+
+MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>");
+MODULE_DESCRIPTION("Driver for Semtech SX150X I2C GPIO Expanders");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:sx150x");
strcpy(info->fix.id, "inteldrmfb");
- info->flags = FBINFO_DEFAULT;
+ info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
info->fbops = &intelfb_ops;
/* setup aperture base/size for vesafb takeover */
info->fix.smem_start = dev->mode_config.fb_base + obj_priv->gtt_offset;
info->fix.smem_len = size;
- info->flags = FBINFO_DEFAULT;
-
info->screen_base = ioremap_wc(dev->agp->base + obj_priv->gtt_offset,
size);
if (!info->screen_base) {
info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
FBINFO_HWACCEL_FILLRECT |
FBINFO_HWACCEL_IMAGEBLIT;
+ info->flags |= FBINFO_CAN_FORCE_OUTPUT;
info->fbops = &nouveau_fbcon_ops;
info->fix.smem_start = dev->mode_config.fb_base + nvbo->bo.offset -
dev_priv->vm_vram_base;
drm_fb_helper_fill_fix(info, fb->pitch, fb->depth);
- info->flags = FBINFO_DEFAULT;
+ info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
info->fbops = &radeonfb_ops;
tmp = radeon_bo_gpu_offset(rbo) - rdev->mc.vram_start;
input_report_key(input, BTN_RIGHT, 0);
input_report_key(input, BTN_MIDDLE, 0);
input_report_abs(input, ABS_DISTANCE,
- input->absmax[ABS_DISTANCE]);
+ input_abs_get_max(input, ABS_DISTANCE));
} else {
input_report_key(input, BTN_TOUCH, 0);
input_report_key(input, BTN_STYLUS, 0);
/* Basics */
input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL);
- input->absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) |
- BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE);
- input->relbit[0] |= BIT(REL_WHEEL);
- set_bit(BTN_TOOL_PEN, input->keybit);
- set_bit(BTN_TOUCH, input->keybit);
- set_bit(BTN_STYLUS, input->keybit);
- set_bit(BTN_STYLUS2, input->keybit);
- set_bit(BTN_LEFT, input->keybit);
- set_bit(BTN_RIGHT, input->keybit);
- set_bit(BTN_MIDDLE, input->keybit);
+
+ __set_bit(REL_WHEEL, input->relbit);
+
+ __set_bit(BTN_TOOL_PEN, input->keybit);
+ __set_bit(BTN_TOUCH, input->keybit);
+ __set_bit(BTN_STYLUS, input->keybit);
+ __set_bit(BTN_STYLUS2, input->keybit);
+ __set_bit(BTN_LEFT, input->keybit);
+ __set_bit(BTN_RIGHT, input->keybit);
+ __set_bit(BTN_MIDDLE, input->keybit);
/* Pad */
input->evbit[0] |= BIT(EV_MSC);
- input->mscbit[0] |= BIT(MSC_SERIAL);
- set_bit(BTN_0, input->keybit);
- set_bit(BTN_1, input->keybit);
- set_bit(BTN_TOOL_FINGER, input->keybit);
- /* Distance, rubber and mouse */
- input->absbit[0] |= BIT(ABS_DISTANCE);
- set_bit(BTN_TOOL_RUBBER, input->keybit);
- set_bit(BTN_TOOL_MOUSE, input->keybit);
+ __set_bit(MSC_SERIAL, input->mscbit);
- input->absmax[ABS_PRESSURE] = 511;
- input->absmax[ABS_DISTANCE] = 32;
+ __set_bit(BTN_0, input->keybit);
+ __set_bit(BTN_1, input->keybit);
+ __set_bit(BTN_TOOL_FINGER, input->keybit);
- input->absmax[ABS_X] = 16704;
- input->absmax[ABS_Y] = 12064;
- input->absfuzz[ABS_X] = 4;
- input->absfuzz[ABS_Y] = 4;
+ /* Distance, rubber and mouse */
+ __set_bit(BTN_TOOL_RUBBER, input->keybit);
+ __set_bit(BTN_TOOL_MOUSE, input->keybit);
+
+ input_set_abs_params(input, ABS_X, 0, 16704, 4, 0);
+ input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0);
+ input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0);
+ input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0);
return 0;
+
err_free:
kfree(wdata);
return ret;
switch (action) {
case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
case CPU_DOWN_FAILED:
- case CPU_DOWN_FAILED_FROZEN:
coretemp_device_add(cpu);
break;
case CPU_DOWN_PREPARE:
- case CPU_DOWN_PREPARE_FROZEN:
coretemp_device_remove(cpu);
break;
}
is necessary for systems where the PXA may be a target on the
I2C bus.
+config HAVE_S3C2410_I2C
+ bool
+ help
+ This will include I2C support for Samsung SoCs. If you want to
+ include I2C support for any machine, kindly select this in the
+ respective Kconfig file.
+
config I2C_S3C2410
tristate "S3C2410 I2C Driver"
- depends on ARCH_S3C2410 || ARCH_S3C64XX
+ depends on HAVE_S3C2410_I2C
help
Say Y here to include support for I2C controller in the
- Samsung S3C2410 based System-on-Chip devices.
+ Samsung SoCs.
config I2C_S6000
tristate "S6000 I2C support"
config I2C_SH_MOBILE
tristate "SuperH Mobile I2C Controller"
- depends on SUPERH
+ depends on SUPERH || ARCH_SHMOBILE
help
If you say yes to this option, support will be included for the
built-in I2C interface on the Renesas SH-Mobile processor.
struct i2c_adapter adap;
struct clk *clk;
+ u_int8_t icic;
u_int8_t iccl;
u_int8_t icch;
+ u_int8_t flags;
spinlock_t lock;
wait_queue_head_t wait;
int sr;
};
+#define IIC_FLAG_HAS_ICIC67 (1 << 0)
+
#define NORMAL_SPEED 100000 /* FAST_SPEED 400000 */
/* Register offsets */
-#define ICDR(pd) (pd->reg + 0x00)
-#define ICCR(pd) (pd->reg + 0x04)
-#define ICSR(pd) (pd->reg + 0x08)
-#define ICIC(pd) (pd->reg + 0x0c)
-#define ICCL(pd) (pd->reg + 0x10)
-#define ICCH(pd) (pd->reg + 0x14)
+#define ICDR 0x00
+#define ICCR 0x04
+#define ICSR 0x08
+#define ICIC 0x0c
+#define ICCL 0x10
+#define ICCH 0x14
/* Register bits */
#define ICCR_ICE 0x80
#define ICSR_WAIT 0x02
#define ICSR_DTE 0x01
+#define ICIC_ICCLB8 0x80
+#define ICIC_ICCHB8 0x40
#define ICIC_ALE 0x08
#define ICIC_TACKE 0x04
#define ICIC_WAITE 0x02
#define ICIC_DTEE 0x01
+static void iic_wr(struct sh_mobile_i2c_data *pd, int offs, unsigned char data)
+{
+ if (offs == ICIC)
+ data |= pd->icic;
+
+ iowrite8(data, pd->reg + offs);
+}
+
+static unsigned char iic_rd(struct sh_mobile_i2c_data *pd, int offs)
+{
+ return ioread8(pd->reg + offs);
+}
+
+static void iic_set_clr(struct sh_mobile_i2c_data *pd, int offs,
+ unsigned char set, unsigned char clr)
+{
+ iic_wr(pd, offs, (iic_rd(pd, offs) | set) & ~clr);
+}
+
static void activate_ch(struct sh_mobile_i2c_data *pd)
{
unsigned long i2c_clk;
else
pd->iccl = (u_int8_t)(num/denom);
+ /* one more bit of ICCL in ICIC */
+ if (pd->flags & IIC_FLAG_HAS_ICIC67) {
+ if ((num/denom) > 0xff)
+ pd->icic |= ICIC_ICCLB8;
+ else
+ pd->icic &= ~ICIC_ICCLB8;
+ }
+
/* Calculate the value for icch. From the data sheet:
icch = (p clock / transfer rate) * (H / (L + H)) */
num = i2c_clk * 4;
else
pd->icch = (u_int8_t)(num/denom);
+ /* one more bit of ICCH in ICIC */
+ if (pd->flags & IIC_FLAG_HAS_ICIC67) {
+ if ((num/denom) > 0xff)
+ pd->icic |= ICIC_ICCHB8;
+ else
+ pd->icic &= ~ICIC_ICCHB8;
+ }
+
/* Enable channel and configure rx ack */
- iowrite8(ioread8(ICCR(pd)) | ICCR_ICE, ICCR(pd));
+ iic_set_clr(pd, ICCR, ICCR_ICE, 0);
/* Mask all interrupts */
- iowrite8(0, ICIC(pd));
+ iic_wr(pd, ICIC, 0);
/* Set the clock */
- iowrite8(pd->iccl, ICCL(pd));
- iowrite8(pd->icch, ICCH(pd));
+ iic_wr(pd, ICCL, pd->iccl);
+ iic_wr(pd, ICCH, pd->icch);
}
static void deactivate_ch(struct sh_mobile_i2c_data *pd)
{
/* Clear/disable interrupts */
- iowrite8(0, ICSR(pd));
- iowrite8(0, ICIC(pd));
+ iic_wr(pd, ICSR, 0);
+ iic_wr(pd, ICIC, 0);
/* Disable channel */
- iowrite8(ioread8(ICCR(pd)) & ~ICCR_ICE, ICCR(pd));
+ iic_set_clr(pd, ICCR, 0, ICCR_ICE);
/* Disable clock and mark device as idle */
clk_disable(pd->clk);
switch (op) {
case OP_START: /* issue start and trigger DTE interrupt */
- iowrite8(0x94, ICCR(pd));
+ iic_wr(pd, ICCR, 0x94);
break;
case OP_TX_FIRST: /* disable DTE interrupt and write data */
- iowrite8(ICIC_WAITE | ICIC_ALE | ICIC_TACKE, ICIC(pd));
- iowrite8(data, ICDR(pd));
+ iic_wr(pd, ICIC, ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
+ iic_wr(pd, ICDR, data);
break;
case OP_TX: /* write data */
- iowrite8(data, ICDR(pd));
+ iic_wr(pd, ICDR, data);
break;
case OP_TX_STOP: /* write data and issue a stop afterwards */
- iowrite8(data, ICDR(pd));
- iowrite8(0x90, ICCR(pd));
+ iic_wr(pd, ICDR, data);
+ iic_wr(pd, ICCR, 0x90);
break;
case OP_TX_TO_RX: /* select read mode */
- iowrite8(0x81, ICCR(pd));
+ iic_wr(pd, ICCR, 0x81);
break;
case OP_RX: /* just read data */
- ret = ioread8(ICDR(pd));
+ ret = iic_rd(pd, ICDR);
break;
case OP_RX_STOP: /* enable DTE interrupt, issue stop */
- iowrite8(ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE,
- ICIC(pd));
- iowrite8(0xc0, ICCR(pd));
+ iic_wr(pd, ICIC,
+ ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
+ iic_wr(pd, ICCR, 0xc0);
break;
case OP_RX_STOP_DATA: /* enable DTE interrupt, read data, issue stop */
- iowrite8(ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE,
- ICIC(pd));
- ret = ioread8(ICDR(pd));
- iowrite8(0xc0, ICCR(pd));
+ iic_wr(pd, ICIC,
+ ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
+ ret = iic_rd(pd, ICDR);
+ iic_wr(pd, ICCR, 0xc0);
break;
}
unsigned char sr;
int wakeup;
- sr = ioread8(ICSR(pd));
+ sr = iic_rd(pd, ICSR);
pd->sr |= sr; /* remember state */
dev_dbg(pd->dev, "i2c_isr 0x%02x 0x%02x %s %d %d!\n", sr, pd->sr,
if (sr & (ICSR_AL | ICSR_TACK)) {
/* don't interrupt transaction - continue to issue stop */
- iowrite8(sr & ~(ICSR_AL | ICSR_TACK), ICSR(pd));
+ iic_wr(pd, ICSR, sr & ~(ICSR_AL | ICSR_TACK));
wakeup = 0;
} else if (pd->msg->flags & I2C_M_RD)
wakeup = sh_mobile_i2c_isr_rx(pd);
wakeup = sh_mobile_i2c_isr_tx(pd);
if (sr & ICSR_WAIT) /* TODO: add delay here to support slow acks */
- iowrite8(sr & ~ICSR_WAIT, ICSR(pd));
+ iic_wr(pd, ICSR, sr & ~ICSR_WAIT);
if (wakeup) {
pd->sr |= SW_DONE;
}
/* Initialize channel registers */
- iowrite8(ioread8(ICCR(pd)) & ~ICCR_ICE, ICCR(pd));
+ iic_set_clr(pd, ICCR, 0, ICCR_ICE);
/* Enable channel and configure rx ack */
- iowrite8(ioread8(ICCR(pd)) | ICCR_ICE, ICCR(pd));
+ iic_set_clr(pd, ICCR, ICCR_ICE, 0);
/* Set the clock */
- iowrite8(pd->iccl, ICCL(pd));
- iowrite8(pd->icch, ICCH(pd));
+ iic_wr(pd, ICCL, pd->iccl);
+ iic_wr(pd, ICCH, pd->icch);
pd->msg = usr_msg;
pd->pos = -1;
pd->sr = 0;
/* Enable all interrupts to begin with */
- iowrite8(ICIC_WAITE | ICIC_ALE | ICIC_TACKE | ICIC_DTEE, ICIC(pd));
+ iic_wr(pd, ICIC, ICIC_DTEE | ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
return 0;
}
retry_count = 1000;
again:
- val = ioread8(ICSR(pd));
+ val = iic_rd(pd, ICSR);
dev_dbg(pd->dev, "val 0x%02x pd->sr 0x%02x\n", val, pd->sr);
goto err_irq;
}
+ /* The IIC blocks on SH-Mobile ARM processors
+ * come with two new bits in ICIC.
+ */
+ if (size > 0x17)
+ pd->flags |= IIC_FLAG_HAS_ICIC67;
+
/* Enable Runtime PM for this device.
*
* Also tell the Runtime PM core to ignore children
BUG_ON(sense_len > sizeof(*sense));
- if (blk_sense_request(rq) || drive->sense_rq_armed)
+ if (rq->cmd_type == REQ_TYPE_SENSE || drive->sense_rq_armed)
return;
memset(sense, 0, sizeof(*sense));
int ide_cd_get_xferlen(struct request *rq)
{
- if (blk_fs_request(rq))
+ switch (rq->cmd_type) {
+ case REQ_TYPE_FS:
return 32768;
- else if (blk_sense_request(rq) || blk_pc_request(rq) ||
- rq->cmd_type == REQ_TYPE_ATA_PC)
+ case REQ_TYPE_SENSE:
+ case REQ_TYPE_BLOCK_PC:
+ case REQ_TYPE_ATA_PC:
return blk_rq_bytes(rq);
- else
+ default:
return 0;
+ }
}
EXPORT_SYMBOL_GPL(ide_cd_get_xferlen);
if (uptodate == 0)
drive->failed_pc = NULL;
- if (blk_special_request(rq)) {
+ if (rq->cmd_type == REQ_TYPE_SPECIAL) {
rq->errors = 0;
error = 0;
} else {
- if (blk_fs_request(rq) == 0 && uptodate <= 0) {
+ if (rq->cmd_type != REQ_TYPE_FS && uptodate <= 0) {
if (rq->errors == 0)
rq->errors = -EIO;
}
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/seq_file.h>
+#include <linux/smp_lock.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
if (!sense->valid)
break;
if (failed_command == NULL ||
- !blk_fs_request(failed_command))
+ failed_command->cmd_type != REQ_TYPE_FS)
break;
sector = (sense->information[0] << 24) |
(sense->information[1] << 16) |
"stat 0x%x",
rq->cmd[0], rq->cmd_type, err, stat);
- if (blk_sense_request(rq)) {
+ if (rq->cmd_type == REQ_TYPE_SENSE) {
/*
* We got an error trying to get sense info from the drive
* (probably while trying to recover from a former error).
}
/* if we have an error, pass CHECK_CONDITION as the SCSI status byte */
- if (blk_pc_request(rq) && !rq->errors)
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC && !rq->errors)
rq->errors = SAM_STAT_CHECK_CONDITION;
if (blk_noretry_request(rq))
switch (sense_key) {
case NOT_READY:
- if (blk_fs_request(rq) && rq_data_dir(rq) == WRITE) {
+ if (rq->cmd_type == REQ_TYPE_FS && rq_data_dir(rq) == WRITE) {
if (ide_cd_breathe(drive, rq))
return 1;
} else {
cdrom_saw_media_change(drive);
- if (blk_fs_request(rq) && !blk_rq_quiet(rq))
+ if (rq->cmd_type == REQ_TYPE_FS &&
+ !(rq->cmd_flags & REQ_QUIET))
printk(KERN_ERR PFX "%s: tray open\n",
drive->name);
}
case UNIT_ATTENTION:
cdrom_saw_media_change(drive);
- if (blk_fs_request(rq) == 0)
+ if (rq->cmd_type != REQ_TYPE_FS)
return 0;
/*
* No point in retrying after an illegal request or data
* protect error.
*/
- if (!blk_rq_quiet(rq))
+ if (!(rq->cmd_flags & REQ_QUIET))
ide_dump_status(drive, "command error", stat);
do_end_request = 1;
break;
* No point in re-trying a zillion times on a bad sector.
* If we got here the error is not correctable.
*/
- if (!blk_rq_quiet(rq))
+ if (!(rq->cmd_flags & REQ_QUIET))
ide_dump_status(drive, "media error "
"(bad sector)", stat);
do_end_request = 1;
break;
case BLANK_CHECK:
/* disk appears blank? */
- if (!blk_rq_quiet(rq))
+ if (!(rq->cmd_flags & REQ_QUIET))
ide_dump_status(drive, "media error (blank)",
stat);
do_end_request = 1;
break;
default:
- if (blk_fs_request(rq) == 0)
+ if (rq->cmd_type != REQ_TYPE_FS)
break;
if (err & ~ATA_ABORTED) {
/* go to the default handler for other errors */
do_end_request = 1;
}
- if (blk_fs_request(rq) == 0) {
+ if (rq->cmd_type != REQ_TYPE_FS) {
rq->cmd_flags |= REQ_FAILED;
do_end_request = 1;
}
ide_expiry_t *expiry = NULL;
int dma_error = 0, dma, thislen, uptodate = 0;
int write = (rq_data_dir(rq) == WRITE) ? 1 : 0, rc = 0;
- int sense = blk_sense_request(rq);
+ int sense = (rq->cmd_type == REQ_TYPE_SENSE);
unsigned int timeout;
u16 len;
u8 ireason, stat;
ide_read_bcount_and_ireason(drive, &len, &ireason);
- thislen = blk_fs_request(rq) ? len : cmd->nleft;
+ thislen = (rq->cmd_type == REQ_TYPE_FS) ? len : cmd->nleft;
if (thislen > len)
thislen = len;
/* If DRQ is clear, the command has completed. */
if ((stat & ATA_DRQ) == 0) {
- if (blk_fs_request(rq)) {
+ if (rq->cmd_type == REQ_TYPE_FS) {
/*
* If we're not done reading/writing, complain.
* Otherwise, complete the command normally.
rq->cmd_flags |= REQ_FAILED;
uptodate = 0;
}
- } else if (!blk_pc_request(rq)) {
+ } else if (rq->cmd_type != REQ_TYPE_BLOCK_PC) {
ide_cd_request_sense_fixup(drive, cmd);
uptodate = cmd->nleft ? 0 : 1;
/* pad, if necessary */
if (len > 0) {
- if (blk_fs_request(rq) == 0 || write == 0)
+ if (rq->cmd_type != REQ_TYPE_FS || write == 0)
ide_pad_transfer(drive, write, len);
else {
printk(KERN_ERR PFX "%s: confused, missing data\n",
}
}
- if (blk_pc_request(rq)) {
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
timeout = rq->timeout;
} else {
timeout = ATAPI_WAIT_PC;
- if (!blk_fs_request(rq))
+ if (rq->cmd_type != REQ_TYPE_FS)
expiry = ide_cd_expiry;
}
return ide_started;
out_end:
- if (blk_pc_request(rq) && rc == 0) {
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC && rc == 0) {
rq->resid_len = 0;
blk_end_request_all(rq, 0);
hwif->rq = NULL;
if (sense && uptodate)
ide_cd_complete_failed_rq(drive, rq);
- if (blk_fs_request(rq)) {
+ if (rq->cmd_type == REQ_TYPE_FS) {
if (cmd->nleft == 0)
uptodate = 1;
} else {
return ide_stopped;
/* make sure it's fully ended */
- if (blk_fs_request(rq) == 0) {
+ if (rq->cmd_type != REQ_TYPE_FS) {
rq->resid_len -= cmd->nbytes - cmd->nleft;
if (uptodate == 0 && (cmd->tf_flags & IDE_TFLAG_WRITE))
rq->resid_len += cmd->last_xfer_len;
ide_debug_log(IDE_DBG_PC, "rq->cmd[0]: 0x%x, rq->cmd_type: 0x%x",
rq->cmd[0], rq->cmd_type);
- if (blk_pc_request(rq))
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC)
rq->cmd_flags |= REQ_QUIET;
else
rq->cmd_flags &= ~REQ_FAILED;
if (drive->debug_mask & IDE_DBG_RQ)
blk_dump_rq_flags(rq, "ide_cd_do_request");
- if (blk_fs_request(rq)) {
+ switch (rq->cmd_type) {
+ case REQ_TYPE_FS:
if (cdrom_start_rw(drive, rq) == ide_stopped)
goto out_end;
- } else if (blk_sense_request(rq) || blk_pc_request(rq) ||
- rq->cmd_type == REQ_TYPE_ATA_PC) {
+ break;
+ case REQ_TYPE_SENSE:
+ case REQ_TYPE_BLOCK_PC:
+ case REQ_TYPE_ATA_PC:
if (!rq->timeout)
rq->timeout = ATAPI_WAIT_PC;
cdrom_do_block_pc(drive, rq);
- } else if (blk_special_request(rq)) {
+ break;
+ case REQ_TYPE_SPECIAL:
/* right now this can only be a reset... */
uptodate = 1;
goto out_end;
- } else
+ default:
BUG();
+ }
/* prepare sense request for this command */
ide_prep_sense(drive, rq);
cmd.rq = rq;
- if (blk_fs_request(rq) || blk_rq_bytes(rq)) {
+ if (rq->cmd_type == REQ_TYPE_FS || blk_rq_bytes(rq)) {
ide_init_sg_cmd(&cmd, blk_rq_bytes(rq));
ide_map_sg(drive, &cmd);
}
static int ide_cdrom_prep_fn(struct request_queue *q, struct request *rq)
{
- if (blk_fs_request(rq))
+ if (rq->cmd_type == REQ_TYPE_FS)
return ide_cdrom_prep_fs(q, rq);
- else if (blk_pc_request(rq))
+ else if (rq->cmd_type == REQ_TYPE_BLOCK_PC)
return ide_cdrom_prep_pc(rq);
return 0;
static int idecd_open(struct block_device *bdev, fmode_t mode)
{
- struct cdrom_info *info = ide_cd_get(bdev->bd_disk);
- int rc = -ENOMEM;
+ struct cdrom_info *info;
+ int rc = -ENXIO;
+ lock_kernel();
+ info = ide_cd_get(bdev->bd_disk);
if (!info)
- return -ENXIO;
+ goto out;
rc = cdrom_open(&info->devinfo, bdev, mode);
-
if (rc < 0)
ide_cd_put(info);
-
+out:
+ unlock_kernel();
return rc;
}
{
struct cdrom_info *info = ide_drv_g(disk, cdrom_info);
+ lock_kernel();
cdrom_release(&info->devinfo, mode);
ide_cd_put(info);
+ unlock_kernel();
return 0;
}
return 0;
}
-static int idecd_ioctl(struct block_device *bdev, fmode_t mode,
+static int idecd_locked_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg)
{
struct cdrom_info *info = ide_drv_g(bdev->bd_disk, cdrom_info);
return err;
}
+static int idecd_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret;
+
+ lock_kernel();
+ ret = idecd_locked_ioctl(bdev, mode, cmd, arg);
+ unlock_kernel();
+
+ return ret;
+}
+
+
static int idecd_media_changed(struct gendisk *disk)
{
struct cdrom_info *info = ide_drv_g(disk, cdrom_info);
.owner = THIS_MODULE,
.open = idecd_open,
.release = idecd_release,
- .locked_ioctl = idecd_ioctl,
+ .ioctl = idecd_ioctl,
.media_changed = idecd_media_changed,
.revalidate_disk = idecd_revalidate_disk
};
touch it at all. */
if (cgc->data_direction == CGC_DATA_WRITE)
- flags |= REQ_RW;
+ flags |= REQ_WRITE;
if (cgc->sense)
memset(cgc->sense, 0, sizeof(struct request_sense));
ide_hwif_t *hwif = drive->hwif;
BUG_ON(drive->dev_flags & IDE_DFLAG_BLOCKED);
- BUG_ON(!blk_fs_request(rq));
+ BUG_ON(rq->cmd_type != REQ_TYPE_FS);
ledtrig_ide_activity();
drive->dev_flags |= IDE_DFLAG_NOHPA; /* disable HPA on resume */
}
-static void idedisk_prepare_flush(struct request_queue *q, struct request *rq)
+static int idedisk_prep_fn(struct request_queue *q, struct request *rq)
{
ide_drive_t *drive = q->queuedata;
- struct ide_cmd *cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC);
+ struct ide_cmd *cmd;
+
+ if (!(rq->cmd_flags & REQ_FLUSH))
+ return BLKPREP_OK;
+
+ cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC);
/* FIXME: map struct ide_taskfile on rq->cmd[] */
BUG_ON(cmd == NULL);
rq->cmd_type = REQ_TYPE_ATA_TASKFILE;
rq->special = cmd;
cmd->rq = rq;
+
+ return BLKPREP_OK;
}
ide_devset_get(multcount, mult_count);
{
u16 *id = drive->id;
unsigned ordered = QUEUE_ORDERED_NONE;
- prepare_flush_fn *prep_fn = NULL;
if (drive->dev_flags & IDE_DFLAG_WCACHE) {
unsigned long long capacity;
if (barrier) {
ordered = QUEUE_ORDERED_DRAIN_FLUSH;
- prep_fn = idedisk_prepare_flush;
+ blk_queue_prep_rq(drive->queue, idedisk_prep_fn);
}
} else
ordered = QUEUE_ORDERED_DRAIN;
- blk_queue_ordered(drive->queue, ordered, prep_fn);
+ blk_queue_ordered(drive->queue, ordered);
}
ide_devset_get_flag(wcache, IDE_DFLAG_WCACHE);
#include <linux/kernel.h>
#include <linux/ide.h>
#include <linux/hdreg.h>
+#include <linux/smp_lock.h>
#include "ide-disk.h"
{
int err;
+ lock_kernel();
err = ide_setting_ioctl(drive, bdev, cmd, arg, ide_disk_ioctl_settings);
if (err != -EOPNOTSUPP)
- return err;
+ goto out;
- return generic_ide_ioctl(drive, bdev, cmd, arg);
+ err = generic_ide_ioctl(drive, bdev, cmd, arg);
+out:
+ unlock_kernel();
+ return err;
}
return ide_stopped;
/* retry only "normal" I/O: */
- if (!blk_fs_request(rq)) {
+ if (rq->cmd_type != REQ_TYPE_FS) {
if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
struct ide_cmd *cmd = rq->special;
{
struct request *rq = drive->hwif->rq;
- if (rq && blk_special_request(rq) && rq->cmd[0] == REQ_DRIVE_RESET) {
+ if (rq && rq->cmd_type == REQ_TYPE_SPECIAL &&
+ rq->cmd[0] == REQ_DRIVE_RESET) {
if (err <= 0 && rq->errors == 0)
rq->errors = -EIO;
ide_complete_rq(drive, err ? err : 0, blk_rq_bytes(rq));
drive->failed_pc = NULL;
if (pc->c[0] == GPCMD_READ_10 || pc->c[0] == GPCMD_WRITE_10 ||
- (rq && blk_pc_request(rq)))
+ (rq && rq->cmd_type == REQ_TYPE_BLOCK_PC))
uptodate = 1; /* FIXME */
else if (pc->c[0] == GPCMD_REQUEST_SENSE) {
"Aborting request!\n");
}
- if (blk_special_request(rq))
+ if (rq->cmd_type == REQ_TYPE_SPECIAL)
rq->errors = uptodate ? 0 : IDE_DRV_ERROR_GENERAL;
return uptodate;
memcpy(rq->cmd, pc->c, 12);
pc->rq = rq;
- if (rq->cmd_flags & REQ_RW)
+ if (rq->cmd_flags & REQ_WRITE)
pc->flags |= PC_FLAG_WRITING;
pc->flags |= PC_FLAG_DMA_OK;
} else
printk(KERN_ERR PFX "%s: I/O error\n", drive->name);
- if (blk_special_request(rq)) {
+ if (rq->cmd_type == REQ_TYPE_SPECIAL) {
rq->errors = 0;
ide_complete_rq(drive, 0, blk_rq_bytes(rq));
return ide_stopped;
} else
goto out_end;
}
- if (blk_fs_request(rq)) {
+
+ switch (rq->cmd_type) {
+ case REQ_TYPE_FS:
if (((long)blk_rq_pos(rq) % floppy->bs_factor) ||
(blk_rq_sectors(rq) % floppy->bs_factor)) {
printk(KERN_ERR PFX "%s: unsupported r/w rq size\n",
}
pc = &floppy->queued_pc;
idefloppy_create_rw_cmd(drive, pc, rq, (unsigned long)block);
- } else if (blk_special_request(rq) || blk_sense_request(rq)) {
+ break;
+ case REQ_TYPE_SPECIAL:
+ case REQ_TYPE_SENSE:
pc = (struct ide_atapi_pc *)rq->special;
- } else if (blk_pc_request(rq)) {
+ break;
+ case REQ_TYPE_BLOCK_PC:
pc = &floppy->queued_pc;
idefloppy_blockpc_cmd(floppy, pc, rq);
- } else
+ break;
+ default:
BUG();
+ }
ide_prep_sense(drive, rq);
cmd.rq = rq;
- if (blk_fs_request(rq) || blk_rq_bytes(rq)) {
+ if (rq->cmd_type == REQ_TYPE_FS || blk_rq_bytes(rq)) {
ide_init_sg_cmd(&cmd, blk_rq_bytes(rq));
ide_map_sg(drive, &cmd);
}
return ide_floppy_issue_pc(drive, &cmd, pc);
out_end:
drive->failed_pc = NULL;
- if (blk_fs_request(rq) == 0 && rq->errors == 0)
+ if (rq->cmd_type != REQ_TYPE_FS && rq->errors == 0)
rq->errors = -EIO;
ide_complete_rq(drive, -EIO, blk_rq_bytes(rq));
return ide_stopped;
#include <linux/kernel.h>
#include <linux/ide.h>
#include <linux/cdrom.h>
+#include <linux/smp_lock.h>
#include <asm/unaligned.h>
void __user *argp = (void __user *)arg;
int err;
- if (cmd == CDROMEJECT || cmd == CDROM_LOCKDOOR)
- return ide_floppy_lockdoor(drive, &pc, arg, cmd);
+ lock_kernel();
+ if (cmd == CDROMEJECT || cmd == CDROM_LOCKDOOR) {
+ err = ide_floppy_lockdoor(drive, &pc, arg, cmd);
+ goto out;
+ }
err = ide_floppy_format_ioctl(drive, &pc, mode, cmd, argp);
if (err != -ENOTTY)
- return err;
+ goto out;
/*
* skip SCSI_IOCTL_SEND_COMMAND (deprecated)
if (err == -ENOTTY)
err = generic_ide_ioctl(drive, bdev, cmd, arg);
+out:
+ unlock_kernel();
return err;
}
+#include <linux/smp_lock.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
return ret;
}
+static int ide_gd_unlocked_open(struct block_device *bdev, fmode_t mode)
+{
+ int ret;
+
+ lock_kernel();
+ ret = ide_gd_open(bdev, mode);
+ unlock_kernel();
+
+ return ret;
+}
+
+
static int ide_gd_release(struct gendisk *disk, fmode_t mode)
{
struct ide_disk_obj *idkp = ide_drv_g(disk, ide_disk_obj);
ide_debug_log(IDE_DBG_FUNC, "enter");
+ lock_kernel();
if (idkp->openers == 1)
drive->disk_ops->flush(drive);
idkp->openers--;
ide_disk_put(idkp);
+ unlock_kernel();
return 0;
}
static const struct block_device_operations ide_gd_ops = {
.owner = THIS_MODULE,
- .open = ide_gd_open,
+ .open = ide_gd_unlocked_open,
.release = ide_gd_release,
- .locked_ioctl = ide_gd_ioctl,
+ .ioctl = ide_gd_ioctl,
.getgeo = ide_gd_getgeo,
.media_changed = ide_gd_media_changed,
.unlock_native_capacity = ide_gd_unlock_native_capacity,
void ide_kill_rq(ide_drive_t *drive, struct request *rq)
{
- u8 drv_req = blk_special_request(rq) && rq->rq_disk;
+ u8 drv_req = (rq->cmd_type == REQ_TYPE_SPECIAL) && rq->rq_disk;
u8 media = drive->media;
drive->failed_pc = NULL;
} else {
if (media == ide_tape)
rq->errors = IDE_DRV_ERROR_GENERAL;
- else if (blk_fs_request(rq) == 0 && rq->errors == 0)
+ else if (rq->cmd_type != REQ_TYPE_FS && rq->errors == 0)
rq->errors = -EIO;
}
{
ide_startstop_t startstop;
- BUG_ON(!blk_rq_started(rq));
+ BUG_ON(!(rq->cmd_flags & REQ_STARTED));
#ifdef DEBUG
printk("%s: start_request: current=0x%08lx\n",
pm->pm_step == IDE_PM_COMPLETED)
ide_complete_pm_rq(drive, rq);
return startstop;
- } else if (!rq->rq_disk && blk_special_request(rq))
+ } else if (!rq->rq_disk && rq->cmd_type == REQ_TYPE_SPECIAL)
/*
* TODO: Once all ULDs have been modified to
* check for specific op codes rather than
#ifdef DEBUG_PM
printk("%s: completing PM request, %s\n", drive->name,
- blk_pm_suspend_request(rq) ? "suspend" : "resume");
+ (rq->cmd_type == REQ_TYPE_PM_SUSPEND) ? "suspend" : "resume");
#endif
spin_lock_irqsave(q->queue_lock, flags);
- if (blk_pm_suspend_request(rq))
+ if (rq->cmd_type == REQ_TYPE_PM_SUSPEND)
blk_stop_queue(q);
else
drive->dev_flags &= ~IDE_DFLAG_BLOCKED;
{
struct request_pm_state *pm = rq->special;
- if (blk_pm_suspend_request(rq) &&
+ if (rq->cmd_type == REQ_TYPE_PM_SUSPEND &&
pm->pm_step == IDE_PM_START_SUSPEND)
/* Mark drive blocked when starting the suspend sequence. */
drive->dev_flags |= IDE_DFLAG_BLOCKED;
- else if (blk_pm_resume_request(rq) &&
+ else if (rq->cmd_type == REQ_TYPE_PM_RESUME &&
pm->pm_step == IDE_PM_START_RESUME) {
/*
* The first thing we do on wakeup is to wait for BSY bit to
#include <linux/errno.h>
#include <linux/genhd.h>
#include <linux/seq_file.h>
+#include <linux/smp_lock.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/ide.h>
rq->cmd[0], (unsigned long long)blk_rq_pos(rq),
blk_rq_sectors(rq));
- BUG_ON(!(blk_special_request(rq) || blk_sense_request(rq)));
+ BUG_ON(!(rq->cmd_type == REQ_TYPE_SPECIAL ||
+ rq->cmd_type == REQ_TYPE_SENSE));
/* Retry a failed packet command */
if (drive->failed_pc && drive->pc->c[0] == REQUEST_SENSE) {
static int idetape_open(struct block_device *bdev, fmode_t mode)
{
- struct ide_tape_obj *tape = ide_tape_get(bdev->bd_disk, false, 0);
+ struct ide_tape_obj *tape;
+
+ lock_kernel();
+ tape = ide_tape_get(bdev->bd_disk, false, 0);
+ unlock_kernel();
if (!tape)
return -ENXIO;
{
struct ide_tape_obj *tape = ide_drv_g(disk, ide_tape_obj);
+ lock_kernel();
ide_tape_put(tape);
+ unlock_kernel();
+
return 0;
}
{
struct ide_tape_obj *tape = ide_drv_g(bdev->bd_disk, ide_tape_obj);
ide_drive_t *drive = tape->drive;
- int err = generic_ide_ioctl(drive, bdev, cmd, arg);
+ int err;
+
+ lock_kernel();
+ err = generic_ide_ioctl(drive, bdev, cmd, arg);
if (err == -EINVAL)
err = idetape_blkdev_ioctl(drive, cmd, arg);
+ unlock_kernel();
+
return err;
}
.owner = THIS_MODULE,
.open = idetape_open,
.release = idetape_release,
- .locked_ioctl = idetape_ioctl,
+ .ioctl = idetape_ioctl,
};
static int ide_tape_probe(ide_drive_t *drive)
rdev->cqshift = PAGE_SHIFT - ilog2(rdev->lldi.ucq_density);
rdev->cqmask = rdev->lldi.ucq_density - 1;
PDBG("%s dev %s stag start 0x%0x size 0x%0x num stags %d "
- "pbl start 0x%0x size 0x%0x rq start 0x%0x size 0x%0x\n",
+ "pbl start 0x%0x size 0x%0x rq start 0x%0x size 0x%0x "
+ "qp qid start %u size %u cq qid start %u size %u\n",
__func__, pci_name(rdev->lldi.pdev), rdev->lldi.vr->stag.start,
rdev->lldi.vr->stag.size, c4iw_num_stags(rdev),
rdev->lldi.vr->pbl.start,
rdev->lldi.vr->pbl.size, rdev->lldi.vr->rq.start,
- rdev->lldi.vr->rq.size);
+ rdev->lldi.vr->rq.size,
+ rdev->lldi.vr->qp.start,
+ rdev->lldi.vr->qp.size,
+ rdev->lldi.vr->cq.start,
+ rdev->lldi.vr->cq.size);
PDBG("udb len 0x%x udb base %p db_reg %p gts_reg %p qpshift %lu "
"qpmask 0x%x cqshift %lu cqmask 0x%x\n",
(unsigned)pci_resource_len(rdev->lldi.pdev, 2),
spin_lock_init(&rdev->resource.qid_fifo_lock);
- if (kfifo_alloc(&rdev->resource.qid_fifo, T4_MAX_QIDS * sizeof(u32),
- GFP_KERNEL))
+ if (kfifo_alloc(&rdev->resource.qid_fifo, rdev->lldi.vr->qp.size *
+ sizeof(u32), GFP_KERNEL))
return -ENOMEM;
- for (i = T4_QID_BASE; i < T4_QID_BASE + T4_MAX_QIDS; i++)
+ for (i = rdev->lldi.vr->qp.start;
+ i < rdev->lldi.vr->qp.start + rdev->lldi.vr->qp.size; i++)
if (!(i & rdev->qpmask))
kfifo_in(&rdev->resource.qid_fifo,
(unsigned char *) &i, sizeof(u32));
#include "t4_msg.h"
#include "t4fw_ri_api.h"
-#define T4_QID_BASE 1024
-#define T4_MAX_QIDS 256
#define T4_MAX_NUM_QP (1<<16)
#define T4_MAX_NUM_CQ (1<<15)
#define T4_MAX_NUM_PD (1<<15)
}
#define OLD_KEY_MAX 0x1ff
-static int handle_eviocgbit(struct input_dev *dev, unsigned int cmd, void __user *p, int compat_mode)
+static int handle_eviocgbit(struct input_dev *dev,
+ unsigned int type, unsigned int size,
+ void __user *p, int compat_mode)
{
static unsigned long keymax_warn_time;
unsigned long *bits;
int len;
- switch (_IOC_NR(cmd) & EV_MAX) {
+ switch (type) {
case 0: bits = dev->evbit; len = EV_MAX; break;
case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
* EVIOCGBIT(EV_KEY, KEY_MAX) and not realize that 'len'
* should be in bytes, not in bits.
*/
- if ((_IOC_NR(cmd) & EV_MAX) == EV_KEY && _IOC_SIZE(cmd) == OLD_KEY_MAX) {
+ if (type == EV_KEY && size == OLD_KEY_MAX) {
len = OLD_KEY_MAX;
if (printk_timed_ratelimit(&keymax_warn_time, 10 * 1000))
printk(KERN_WARNING
BITS_TO_LONGS(OLD_KEY_MAX) * sizeof(long));
}
- return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode);
+ return bits_to_user(bits, len, size, p, compat_mode);
}
#undef OLD_KEY_MAX
struct ff_effect effect;
int __user *ip = (int __user *)p;
unsigned int i, t, u, v;
+ unsigned int size;
int error;
+ /* First we check for fixed-length commands */
switch (cmd) {
case EVIOCGVERSION:
return evdev_grab(evdev, client);
else
return evdev_ungrab(evdev, client);
+ }
- default:
-
- if (_IOC_TYPE(cmd) != 'E')
- return -EINVAL;
-
- if (_IOC_DIR(cmd) == _IOC_READ) {
+ size = _IOC_SIZE(cmd);
- if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0)))
- return handle_eviocgbit(dev, cmd, p, compat_mode);
+ /* Now check variable-length commands */
+#define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0)))
- return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd),
- p, compat_mode);
+ switch (EVIOC_MASK_SIZE(cmd)) {
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0)))
- return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd),
- p, compat_mode);
+ case EVIOCGKEY(0):
+ return bits_to_user(dev->key, KEY_MAX, size, p, compat_mode);
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0)))
- return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd),
- p, compat_mode);
+ case EVIOCGLED(0):
+ return bits_to_user(dev->led, LED_MAX, size, p, compat_mode);
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
- return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd),
- p, compat_mode);
+ case EVIOCGSND(0):
+ return bits_to_user(dev->snd, SND_MAX, size, p, compat_mode);
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0)))
- return str_to_user(dev->name, _IOC_SIZE(cmd), p);
+ case EVIOCGSW(0):
+ return bits_to_user(dev->sw, SW_MAX, size, p, compat_mode);
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0)))
- return str_to_user(dev->phys, _IOC_SIZE(cmd), p);
+ case EVIOCGNAME(0):
+ return str_to_user(dev->name, size, p);
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0)))
- return str_to_user(dev->uniq, _IOC_SIZE(cmd), p);
+ case EVIOCGPHYS(0):
+ return str_to_user(dev->phys, size, p);
- if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
+ case EVIOCGUNIQ(0):
+ return str_to_user(dev->uniq, size, p);
- t = _IOC_NR(cmd) & ABS_MAX;
+ case EVIOC_MASK_SIZE(EVIOCSFF):
+ if (input_ff_effect_from_user(p, size, &effect))
+ return -EFAULT;
- abs.value = dev->abs[t];
- abs.minimum = dev->absmin[t];
- abs.maximum = dev->absmax[t];
- abs.fuzz = dev->absfuzz[t];
- abs.flat = dev->absflat[t];
- abs.resolution = dev->absres[t];
+ error = input_ff_upload(dev, &effect, file);
- if (copy_to_user(p, &abs, min_t(size_t,
- _IOC_SIZE(cmd),
- sizeof(struct input_absinfo))))
- return -EFAULT;
+ if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
+ return -EFAULT;
- return 0;
- }
+ return error;
+ }
- }
+ /* Multi-number variable-length handlers */
+ if (_IOC_TYPE(cmd) != 'E')
+ return -EINVAL;
- if (_IOC_DIR(cmd) == _IOC_WRITE) {
+ if (_IOC_DIR(cmd) == _IOC_READ) {
- if (_IOC_NR(cmd) == _IOC_NR(EVIOCSFF)) {
+ if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0)))
+ return handle_eviocgbit(dev,
+ _IOC_NR(cmd) & EV_MAX, size,
+ p, compat_mode);
- if (input_ff_effect_from_user(p, _IOC_SIZE(cmd), &effect))
- return -EFAULT;
+ if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
- error = input_ff_upload(dev, &effect, file);
+ t = _IOC_NR(cmd) & ABS_MAX;
+ abs = dev->absinfo[t];
- if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
- return -EFAULT;
+ if (copy_to_user(p, &abs, min_t(size_t,
+ size, sizeof(struct input_absinfo))))
+ return -EFAULT;
- return error;
- }
+ return 0;
+ }
+ }
- if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
+ if (_IOC_DIR(cmd) == _IOC_READ) {
- t = _IOC_NR(cmd) & ABS_MAX;
+ if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
- if (copy_from_user(&abs, p, min_t(size_t,
- _IOC_SIZE(cmd),
- sizeof(struct input_absinfo))))
- return -EFAULT;
+ t = _IOC_NR(cmd) & ABS_MAX;
- /* We can't change number of reserved MT slots */
- if (t == ABS_MT_SLOT)
- return -EINVAL;
+ if (copy_from_user(&abs, p, min_t(size_t,
+ size, sizeof(struct input_absinfo))))
+ return -EFAULT;
- /*
- * Take event lock to ensure that we are not
- * changing device parameters in the middle
- * of event.
- */
- spin_lock_irq(&dev->event_lock);
+ if (size < sizeof(struct input_absinfo))
+ abs.resolution = 0;
- dev->abs[t] = abs.value;
- dev->absmin[t] = abs.minimum;
- dev->absmax[t] = abs.maximum;
- dev->absfuzz[t] = abs.fuzz;
- dev->absflat[t] = abs.flat;
- dev->absres[t] = _IOC_SIZE(cmd) < sizeof(struct input_absinfo) ?
- 0 : abs.resolution;
+ /* We can't change number of reserved MT slots */
+ if (t == ABS_MT_SLOT)
+ return -EINVAL;
- spin_unlock_irq(&dev->event_lock);
+ /*
+ * Take event lock to ensure that we are not
+ * changing device parameters in the middle
+ * of event.
+ */
+ spin_lock_irq(&dev->event_lock);
+ dev->absinfo[t] = abs;
+ spin_unlock_irq(&dev->event_lock);
- return 0;
- }
+ return 0;
}
}
+
return -EINVAL;
}
is_mt_event = code >= ABS_MT_FIRST && code <= ABS_MT_LAST;
if (!is_mt_event) {
- pold = &dev->abs[code];
+ pold = &dev->absinfo[code].value;
} else if (dev->mt) {
struct input_mt_slot *mtslot = &dev->mt[dev->slot];
pold = &mtslot->abs[code - ABS_MT_FIRST];
if (pold) {
*pval = input_defuzz_abs_event(*pval, *pold,
- dev->absfuzz[code]);
+ dev->absinfo[code].fuzz);
if (*pold == *pval)
return INPUT_IGNORE_EVENT;
}
/* Flush pending "slot" event */
- if (is_mt_event && dev->slot != dev->abs[ABS_MT_SLOT]) {
- dev->abs[ABS_MT_SLOT] = dev->slot;
+ if (is_mt_event && dev->slot != input_abs_get_val(dev, ABS_MT_SLOT)) {
+ input_abs_set_val(dev, ABS_MT_SLOT, dev->slot);
input_pass_event(dev, EV_ABS, ABS_MT_SLOT, dev->slot);
}
}
EXPORT_SYMBOL(input_inject_event);
+/**
+ * input_alloc_absinfo - allocates array of input_absinfo structs
+ * @dev: the input device emitting absolute events
+ *
+ * If the absinfo struct the caller asked for is already allocated, this
+ * functions will not do anything.
+ */
+void input_alloc_absinfo(struct input_dev *dev)
+{
+ if (!dev->absinfo)
+ dev->absinfo = kcalloc(ABS_CNT, sizeof(struct input_absinfo),
+ GFP_KERNEL);
+
+ WARN(!dev->absinfo, "%s(): kcalloc() failed?\n", __func__);
+}
+EXPORT_SYMBOL(input_alloc_absinfo);
+
+void input_set_abs_params(struct input_dev *dev, unsigned int axis,
+ int min, int max, int fuzz, int flat)
+{
+ struct input_absinfo *absinfo;
+
+ input_alloc_absinfo(dev);
+ if (!dev->absinfo)
+ return;
+
+ absinfo = &dev->absinfo[axis];
+ absinfo->minimum = min;
+ absinfo->maximum = max;
+ absinfo->fuzz = fuzz;
+ absinfo->flat = flat;
+
+ dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis);
+}
+EXPORT_SYMBOL(input_set_abs_params);
+
+
/**
* input_grab_device - grabs device for exclusive use
* @handle: input handle that wants to own the device
input_ff_destroy(dev);
input_mt_destroy_slots(dev);
+ kfree(dev->absinfo);
kfree(dev);
module_put(THIS_MODULE);
{
struct input_dev *dev = joydev->handle.dev;
size_t len;
- int i, j;
+ int i;
const char *name;
/* Process fixed-sized commands. */
case JSIOCSCORR:
if (copy_from_user(joydev->corr, argp,
sizeof(joydev->corr[0]) * joydev->nabs))
- return -EFAULT;
+ return -EFAULT;
for (i = 0; i < joydev->nabs; i++) {
- j = joydev->abspam[i];
- joydev->abs[i] = joydev_correct(dev->abs[j],
- &joydev->corr[i]);
+ int val = input_abs_get_val(dev, joydev->abspam[i]);
+ joydev->abs[i] = joydev_correct(val, &joydev->corr[i]);
}
return 0;
for (i = 0; i < joydev->nabs; i++) {
j = joydev->abspam[i];
- if (dev->absmax[j] == dev->absmin[j]) {
+ if (input_abs_get_max(dev, j) == input_abs_get_min(dev, j)) {
joydev->corr[i].type = JS_CORR_NONE;
- joydev->abs[i] = dev->abs[j];
+ joydev->abs[i] = input_abs_get_val(dev, j);
continue;
}
joydev->corr[i].type = JS_CORR_BROKEN;
- joydev->corr[i].prec = dev->absfuzz[j];
- joydev->corr[i].coef[0] =
- (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j];
- joydev->corr[i].coef[1] =
- (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j];
+ joydev->corr[i].prec = input_abs_get_fuzz(dev, j);
+
+ t = (input_abs_get_max(dev, j) + input_abs_get_min(dev, j)) / 2;
+ joydev->corr[i].coef[0] = t - input_abs_get_flat(dev, j);
+ joydev->corr[i].coef[1] = t + input_abs_get_flat(dev, j);
- t = (dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j];
+ t = (input_abs_get_max(dev, j) - input_abs_get_min(dev, j)) / 2
+ - 2 * input_abs_get_flat(dev, j);
if (t) {
joydev->corr[i].coef[2] = (1 << 29) / t;
joydev->corr[i].coef[3] = (1 << 29) / t;
- joydev->abs[i] = joydev_correct(dev->abs[j],
- joydev->corr + i);
+ joydev->abs[i] =
+ joydev_correct(input_abs_get_val(dev, j),
+ joydev->corr + i);
}
}
for (i = 0; i < 4; i++) {
if (i < 2)
- input_set_abs_params(input_dev, axes[i], 48, input_dev->abs[axes[i]] * 2 - 48, 0, 8);
+ input_set_abs_params(input_dev, axes[i],
+ 48, input_abs_get_val(input_dev, axes[i]) * 2 - 48, 0, 8);
else
input_set_abs_params(input_dev, axes[i], 2, 253, 0, 0);
input_set_abs_params(input_dev, ABS_HAT0X + i, -1, 1, 0, 0);
for (i = 0; i < adi->axes10 + adi->axes8 + (adi->hats + (adi->pad != -1)) * 2; i++) {
t = adi->abs[i];
- x = adi->dev->abs[t];
+ x = input_abs_get_val(adi->dev, t);
if (t == ABS_THROTTLE || t == ABS_RUDDER || adi->id == ADI_ID_WGPE)
x = i < adi->axes10 ? 512 : 128;
amijoy_dev[i]->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
for (j = 0; j < 2; j++) {
- amijoy_dev[i]->absmin[ABS_X + j] = -1;
- amijoy_dev[i]->absmax[ABS_X + j] = 1;
+ XXinput_set_abs_params(amijoy_dev[i], ABS_X + j,
+ -1, 1, 0, 0);
}
err = input_register_device(amijoy_dev[i]);
for (i = 0; i < gf2k_axes[gf2k->id]; i++)
set_bit(gf2k_abs[i], input_dev->absbit);
- for (i = 0; i < gf2k_hats[gf2k->id]; i++) {
- set_bit(ABS_HAT0X + i, input_dev->absbit);
- input_dev->absmin[ABS_HAT0X + i] = -1;
- input_dev->absmax[ABS_HAT0X + i] = 1;
- }
+ for (i = 0; i < gf2k_hats[gf2k->id]; i++)
+ input_set_abs_params(input_dev, ABS_HAT0X + i, -1, 1, 0, 0);
for (i = 0; i < gf2k_joys[gf2k->id]; i++)
set_bit(gf2k_btn_joy[i], input_dev->keybit);
gf2k_read(gf2k, data);
for (i = 0; i < gf2k_axes[gf2k->id]; i++) {
- input_dev->absmax[gf2k_abs[i]] = (i < 2) ? input_dev->abs[gf2k_abs[i]] * 2 - 32 :
- input_dev->abs[gf2k_abs[0]] + input_dev->abs[gf2k_abs[1]] - 32;
- input_dev->absmin[gf2k_abs[i]] = 32;
- input_dev->absfuzz[gf2k_abs[i]] = 8;
- input_dev->absflat[gf2k_abs[i]] = (i < 2) ? 24 : 0;
+ int max = i < 2 ?
+ input_abs_get_val(input_dev, gf2k_abs[i]) * 2 :
+ input_abs_get_val(input_dev, gf2k_abs[0]) +
+ input_abs_get_val(input_dev, gf2k_abs[1]);
+ int flat = i < 2 ? 24 : 0;
+
+ input_set_abs_params(input_dev, gf2k_abs[i],
+ 32, max - 32, 8, flat);
}
err = input_register_device(gf2k->dev);
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
for (i = 0; (t = interact_type[interact->type].abs[i]) >= 0; i++) {
- set_bit(t, input_dev->absbit);
- if (i < interact_type[interact->type].b8) {
- input_dev->absmin[t] = 0;
- input_dev->absmax[t] = 255;
- } else {
- input_dev->absmin[t] = -1;
- input_dev->absmax[t] = 1;
- }
+ if (i < interact_type[interact->type].b8)
+ input_set_abs_params(input_dev, t, 0, 255, 0, 0);
+ else
+ input_set_abs_params(input_dev, t, -1, 1, 0, 0);
}
for (i = 0; (t = interact_type[interact->type].btn[i]) >= 0; i++)
- set_bit(t, input_dev->keybit);
+ __set_bit(t, input_dev->keybit);
err = input_register_device(interact->dev);
if (err)
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
for (j = 0; (bits = sw_bit[sw->type][j]); j++) {
+ int min, max, fuzz, flat;
+
code = sw_abs[sw->type][j];
- set_bit(code, input_dev->absbit);
- input_dev->absmax[code] = (1 << bits) - 1;
- input_dev->absmin[code] = (bits == 1) ? -1 : 0;
- input_dev->absfuzz[code] = ((bits >> 1) >= 2) ? (1 << ((bits >> 1) - 2)) : 0;
- if (code != ABS_THROTTLE)
- input_dev->absflat[code] = (bits >= 5) ? (1 << (bits - 5)) : 0;
+ min = bits == 1 ? -1 : 0;
+ max = (1 << bits) - 1;
+ fuzz = (bits >> 1) >= 2 ? 1 << ((bits >> 1) - 2) : 0;
+ flat = code == ABS_THROTTLE || bits < 5 ?
+ 0 : 1 << (bits - 5);
+
+ input_set_abs_params(input_dev, code,
+ min, max, fuzz, flat);
}
for (j = 0; (code = sw_btn[sw->type][j]); j++)
- set_bit(code, input_dev->keybit);
+ __set_bit(code, input_dev->keybit);
dbg("%s%s [%d-bit id %d data %d]\n", sw->name, comment, m, l, k);
{ 0x0e6f, 0x0005, "Eclipse wireless Controller", 0, XTYPE_XBOX },
{ 0x0e6f, 0x0006, "Edge wireless Controller", 0, XTYPE_XBOX },
{ 0x0e6f, 0x0006, "Pelican 'TSZ' Wired Xbox 360 Controller", 0, XTYPE_XBOX360 },
+ { 0x0e6f, 0x0201, "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller", 0, XTYPE_XBOX360 },
{ 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", 0, XTYPE_XBOX },
{ 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX },
{ 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX },
struct input_dev *input;
struct mutex disable_lock;
unsigned int n_buttons;
+ int (*enable)(struct device *dev);
+ void (*disable)(struct device *dev);
struct gpio_button_data data[0];
};
return error;
}
+static int gpio_keys_open(struct input_dev *input)
+{
+ struct gpio_keys_drvdata *ddata = input_get_drvdata(input);
+
+ return ddata->enable ? ddata->enable(input->dev.parent) : 0;
+}
+
+static void gpio_keys_close(struct input_dev *input)
+{
+ struct gpio_keys_drvdata *ddata = input_get_drvdata(input);
+
+ if (ddata->disable)
+ ddata->disable(input->dev.parent);
+}
+
static int __devinit gpio_keys_probe(struct platform_device *pdev)
{
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
ddata->input = input;
ddata->n_buttons = pdata->nbuttons;
+ ddata->enable = pdata->enable;
+ ddata->disable = pdata->disable;
mutex_init(&ddata->disable_lock);
platform_set_drvdata(pdev, ddata);
+ input_set_drvdata(input, ddata);
input->name = pdev->name;
input->phys = "gpio-keys/input0";
input->dev.parent = &pdev->dev;
+ input->open = gpio_keys_open;
+ input->close = gpio_keys_close;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
if (absdev) {
val = lo + (hi << 8);
#ifdef TABLET_AUTOADJUST
- if (val < dev->absmin[ABS_X + i])
- dev->absmin[ABS_X + i] = val;
- if (val > dev->absmax[ABS_X + i])
- dev->absmax[ABS_X + i] = val;
+ if (val < input_abs_min(dev, ABS_X + i))
+ input_abs_set_min(dev, ABS_X + i, val);
+ if (val > input_abs_max(dev, ABS_X + i))
+ XXinput_abs_set_max(dev, ABS_X + i, val);
#endif
- if (i%3) val = dev->absmax[ABS_X + i] - val;
+ if (i % 3)
+ val = input_abs_max(dev, ABS_X + i) - val;
input_report_abs(dev, ABS_X + i, val);
} else {
- val = (int) (((int8_t)lo) | ((int8_t)hi << 8));
+ val = (int) (((int8_t) lo) | ((int8_t) hi << 8));
if (i % 3)
val *= -1;
input_report_rel(dev, REL_X + i, val);
#ifdef TABLET_AUTOADJUST
for (i = 0; i < ABS_MAX; i++) {
- int diff = input_dev->absmax[ABS_X + i] / 10;
- input_dev->absmin[ABS_X + i] += diff;
- input_dev->absmax[ABS_X + i] -= diff;
+ int diff = input_abs_max(input_dev, ABS_X + i) / 10;
+ input_abs_set_min(input_dev, ABS_X + i,
+ input_abs_min(input_dev, ABS_X + i) + diff)
+ XXinput_abs_set_max(input_dev, ABS_X + i,
+ input_abs_max(input_dev, ABS_X + i) - diff)
}
#endif
pdata = &ac->pdata;
ac->input = input_dev;
- ac->disabled = true;
ac->dev = dev;
ac->irq = irq;
ac->bops = bops;
if (!test_bit(cnt, dev->absbit))
continue;
- if ((dev->absmax[cnt] <= dev->absmin[cnt])) {
+ if (input_abs_get_max(dev, cnt) <= input_abs_get_min(dev, cnt)) {
printk(KERN_DEBUG
"%s: invalid abs[%02x] min:%d max:%d\n",
UINPUT_NAME, cnt,
- dev->absmin[cnt], dev->absmax[cnt]);
+ input_abs_get_min(dev, cnt),
+ input_abs_get_max(dev, cnt));
retval = -EINVAL;
break;
}
- if (dev->absflat[cnt] > (dev->absmax[cnt] - dev->absmin[cnt])) {
+ if (input_abs_get_flat(dev, cnt) >
+ input_abs_get_max(dev, cnt) - input_abs_get_min(dev, cnt)) {
printk(KERN_DEBUG
- "%s: absflat[%02x] out of range: %d "
+ "%s: abs_flat #%02x out of range: %d "
"(min:%d/max:%d)\n",
- UINPUT_NAME, cnt, dev->absflat[cnt],
- dev->absmin[cnt], dev->absmax[cnt]);
+ UINPUT_NAME, cnt,
+ input_abs_get_flat(dev, cnt),
+ input_abs_get_min(dev, cnt),
+ input_abs_get_max(dev, cnt));
retval = -EINVAL;
break;
}
struct uinput_user_dev *user_dev;
struct input_dev *dev;
char *name;
- int size;
+ int i, size;
int retval;
if (count != sizeof(struct uinput_user_dev))
dev->id.product = user_dev->id.product;
dev->id.version = user_dev->id.version;
- size = sizeof(int) * ABS_CNT;
- memcpy(dev->absmax, user_dev->absmax, size);
- memcpy(dev->absmin, user_dev->absmin, size);
- memcpy(dev->absfuzz, user_dev->absfuzz, size);
- memcpy(dev->absflat, user_dev->absflat, size);
+ for (i = 0; i < ABS_CNT; i++) {
+ input_abs_set_max(dev, i, user_dev->absmax[i]);
+ input_abs_set_min(dev, i, user_dev->absmin[i]);
+ input_abs_set_fuzz(dev, i, user_dev->absfuzz[i]);
+ input_abs_set_flat(dev, i, user_dev->absflat[i]);
+ }
/* check if absmin/absmax/absfuzz/absflat are filled as
* told in Documentation/input/input-programming.txt */
struct elantech_data *etd = psmouse->private;
unsigned char *packet = psmouse->packet;
int fingers;
- static int old_fingers;
if (etd->fw_version < 0x020000) {
/*
}
if (etd->jumpy_cursor) {
- /* Discard packets that are likely to have bogus coordinates */
- if (fingers > old_fingers) {
+ if (fingers != 1) {
+ etd->single_finger_reports = 0;
+ } else if (etd->single_finger_reports < 2) {
+ /* Discard first 2 reports of one finger, bogus */
+ etd->single_finger_reports++;
elantech_debug("discarding packet\n");
- goto discard_packet_v1;
+ return;
}
}
}
input_sync(dev);
-
- discard_packet_v1:
- old_fingers = fingers;
}
/*
input_report_key(dev, BTN_TOUCH, fingers != 0);
switch (fingers) {
+ case 3:
+ /*
+ * Same as one finger, except report of more than 3 fingers:
+ * byte 3: n4 . w1 w0 . . . .
+ */
+ if (packet[3] & 0x80)
+ fingers = 4;
+ /* pass through... */
case 1:
/*
* byte 1: . . . . . x10 x9 x8
input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
+ input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4);
input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
break;
case 2:
+ __set_bit(BTN_TOOL_QUADTAP, dev->keybit);
input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0);
input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0);
input_set_abs_params(dev, ABS_HAT0X, ETP_2FT_XMIN, ETP_2FT_XMAX, 0, 0);
etd->capabilities = param[0];
/*
- * This firmware seems to suffer from misreporting coordinates when
+ * This firmware suffers from misreporting coordinates when
* a touch action starts causing the mouse cursor or scrolled page
* to jump. Enable a workaround.
*/
- if (etd->fw_version == 0x020022) {
- pr_info("firmware version 2.0.34 detected, enabling jumpy cursor workaround\n");
- etd->jumpy_cursor = 1;
+ if (etd->fw_version == 0x020022 || etd->fw_version == 0x020600) {
+ pr_info("firmware version 2.0.34/2.6.0 detected, enabling jumpy cursor workaround\n");
+ etd->jumpy_cursor = true;
}
if (elantech_set_absolute_mode(psmouse)) {
unsigned char reg_26;
unsigned char debug;
unsigned char capabilities;
- unsigned char paritycheck;
- unsigned char jumpy_cursor;
+ bool paritycheck;
+ bool jumpy_cursor;
unsigned char hw_version;
- unsigned int fw_version;
+ unsigned int fw_version;
+ unsigned int single_finger_reports;
unsigned char parity[256];
};
pc110pad_dev->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y);
pc110pad_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
- pc110pad_dev->absmax[ABS_X] = 0x1ff;
- pc110pad_dev->absmax[ABS_Y] = 0x0ff;
+ input_abs_set_max(pc110pad_dev, ABS_X, 0x1ff);
+ input_abs_set_max(pc110pad_dev, ABS_Y, 0x0ff);
pc110pad_dev->open = pc110pad_open;
pc110pad_dev->close = pc110pad_close;
__clear_bit(REL_X, dev->relbit);
__clear_bit(REL_Y, dev->relbit);
- dev->absres[ABS_X] = priv->x_res;
- dev->absres[ABS_Y] = priv->y_res;
+ input_abs_set_res(dev, ABS_X, priv->x_res);
+ input_abs_set_res(dev, ABS_Y, priv->y_res);
if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
/* Clickpads report only left button */
#include <linux/random.h>
#include <linux/major.h>
#include <linux/device.h>
+#include <linux/kernel.h>
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
#include <linux/miscdevice.h>
#endif
switch (code) {
case ABS_X:
+
fx(0) = value;
if (mousedev->touch && mousedev->pkt_count >= 2) {
- size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
+ size = input_abs_get_min(dev, ABS_X) -
+ input_abs_get_max(dev, ABS_X);
if (size == 0)
size = 256 * 2;
+
tmp = ((value - fx(2)) * 256 * FRACTION_DENOM) / size;
tmp += mousedev->frac_dx;
mousedev->packet.dx = tmp / FRACTION_DENOM;
case ABS_Y:
fy(0) = value;
if (mousedev->touch && mousedev->pkt_count >= 2) {
- /* use X size to keep the same scale */
- size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
+ /* use X size for ABS_Y to keep the same scale */
+ size = input_abs_get_min(dev, ABS_X) -
+ input_abs_get_max(dev, ABS_X);
if (size == 0)
size = 256 * 2;
+
tmp = -((value - fy(2)) * 256 * FRACTION_DENOM) / size;
tmp += mousedev->frac_dy;
mousedev->packet.dy = tmp / FRACTION_DENOM;
static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev,
unsigned int code, int value)
{
- int size;
+ int min, max, size;
switch (code) {
case ABS_X:
- size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
+ min = input_abs_get_min(dev, ABS_X);
+ max = input_abs_get_max(dev, ABS_X);
+
+ size = max - min;
if (size == 0)
size = xres ? : 1;
- if (value > dev->absmax[ABS_X])
- value = dev->absmax[ABS_X];
- if (value < dev->absmin[ABS_X])
- value = dev->absmin[ABS_X];
- mousedev->packet.x =
- ((value - dev->absmin[ABS_X]) * xres) / size;
+
+ clamp(value, min, max);
+
+ mousedev->packet.x = ((value - min) * xres) / size;
mousedev->packet.abs_event = 1;
break;
case ABS_Y:
- size = dev->absmax[ABS_Y] - dev->absmin[ABS_Y];
+ min = input_abs_get_min(dev, ABS_Y);
+ max = input_abs_get_max(dev, ABS_Y);
+
+ size = max - min;
if (size == 0)
size = yres ? : 1;
- if (value > dev->absmax[ABS_Y])
- value = dev->absmax[ABS_Y];
- if (value < dev->absmin[ABS_Y])
- value = dev->absmin[ABS_Y];
- mousedev->packet.y = yres -
- ((value - dev->absmin[ABS_Y]) * yres) / size;
+
+ clamp(value, min, max);
+
+ mousedev->packet.y = yres - ((value - min) * yres) / size;
mousedev->packet.abs_event = 1;
break;
}
module_param_named(noloop, i8042_noloop, bool, 0);
MODULE_PARM_DESC(noloop, "Disable the AUX Loopback command while probing for the AUX port");
-static unsigned int i8042_blink_frequency = 500;
-module_param_named(panicblink, i8042_blink_frequency, uint, 0600);
-MODULE_PARM_DESC(panicblink, "Frequency with which keyboard LEDs should blink when kernel panics");
-
#ifdef CONFIG_X86
static bool i8042_dritek;
module_param_named(dritek, i8042_dritek, bool, 0);
/*
- * i8042_panic_blink() will flash the keyboard LEDs and is called when
- * kernel panics. Flashing LEDs is useful for users running X who may
+ * i8042_panic_blink() will turn the keyboard LEDs on or off and is called
+ * when kernel panics. Flashing LEDs is useful for users running X who may
* not see the console and will help distingushing panics from "real"
* lockups.
*
#define DELAY do { mdelay(1); if (++delay > 10) return delay; } while(0)
-static long i8042_panic_blink(long count)
+static long i8042_panic_blink(int state)
{
long delay = 0;
- static long last_blink;
- static char led;
-
- /*
- * We expect frequency to be about 1/2s. KDB uses about 1s.
- * Make sure they are different.
- */
- if (!i8042_blink_frequency)
- return 0;
- if (count - last_blink < i8042_blink_frequency)
- return 0;
+ char led;
- led ^= 0x01 | 0x04;
+ led = (state) ? 0x01 | 0x04 : 0;
while (i8042_read_status() & I8042_STR_IBF)
DELAY;
dbg("%02x -> i8042 (panic blink)", 0xed);
dbg("%02x -> i8042 (panic blink)", led);
i8042_write_data(led);
DELAY;
- last_blink = count;
return delay;
}
/* Query getXextension */
if ((ret = aiptek_query(aiptek, 0x01, 0x00)) < 0)
return ret;
- aiptek->inputdev->absmin[ABS_X] = 0;
- aiptek->inputdev->absmax[ABS_X] = ret - 1;
+ input_set_abs_params(aiptek->inputdev, ABS_X, 0, ret - 1, 0, 0);
/* Query getYextension */
if ((ret = aiptek_query(aiptek, 0x01, 0x01)) < 0)
return ret;
- aiptek->inputdev->absmin[ABS_Y] = 0;
- aiptek->inputdev->absmax[ABS_Y] = ret - 1;
+ input_set_abs_params(aiptek->inputdev, ABS_Y, 0, ret - 1, 0, 0);
/* Query getPressureLevels */
if ((ret = aiptek_query(aiptek, 0x08, 0x00)) < 0)
return ret;
- aiptek->inputdev->absmin[ABS_PRESSURE] = 0;
- aiptek->inputdev->absmax[ABS_PRESSURE] = ret - 1;
+ input_set_abs_params(aiptek->inputdev, ABS_PRESSURE, 0, ret - 1, 0, 0);
/* Depending on whether we are in absolute or relative mode, we will
* do a switchToTablet(absolute) or switchToMouse(relative) command.
struct aiptek *aiptek = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%dx%d\n",
- aiptek->inputdev->absmax[ABS_X] + 1,
- aiptek->inputdev->absmax[ABS_Y] + 1);
+ input_abs_get_max(aiptek->inputdev, ABS_X) + 1,
+ input_abs_get_max(aiptek->inputdev, ABS_Y) + 1);
}
/* These structs define the sysfs files, param #1 is the name of the
for (i = 0; i < ARRAY_SIZE(speeds); ++i) {
aiptek->curSetting.programmableDelay = speeds[i];
(void)aiptek_program_tablet(aiptek);
- if (aiptek->inputdev->absmax[ABS_X] > 0) {
+ if (input_abs_get_max(aiptek->inputdev, ABS_X) > 0) {
dev_info(&intf->dev,
"Aiptek using %d ms programming speed\n",
aiptek->curSetting.programmableDelay);
* protocol.
*/
if (wacom->last_finger != finger) {
- if (x == input->abs[ABS_X])
+ if (x == input_abs_get_val(input, ABS_X))
x++;
- if (y == input->abs[ABS_Y])
+ if (y == input_abs_get_val(input, ABS_Y))
y++;
}
unsigned char len, unsigned char *value)
{
struct i2c_client *client = tsc->client;
- unsigned int ret;
+ int ret;
unsigned char i2c_data[6];
BUG_ON(len > 5);
ret = i2c_master_send(client, i2c_data, len + 1);
if (ret != 1) {
dev_err(&client->dev, "i2c write data cmd failed\n");
- return ret;
+ return ret ? ret : -EIO;
}
return 0;
unsigned char *data, unsigned char len, unsigned char cmd)
{
struct i2c_client *client = tsc->client;
- unsigned int ret;
+ int ret;
struct i2c_msg msg[2] = {
/* first write slave position to i2c devices */
{ client->addr, 0, 1, &cmd },
config MD_RAID456
tristate "RAID-4/RAID-5/RAID-6 mode"
depends on BLK_DEV_MD
- select MD_RAID6_PQ
+ select RAID6_PQ
select ASYNC_MEMCPY
select ASYNC_XOR
select ASYNC_PQ
If unsure, say N.
-config MD_RAID6_PQ
- tristate
-
-config ASYNC_RAID6_TEST
- tristate "Self test for hardware accelerated raid6 recovery"
- depends on MD_RAID6_PQ
- select ASYNC_RAID6_RECOV
- ---help---
- This is a one-shot self test that permutes through the
- recovery of all the possible two disk failure scenarios for a
- N-disk array. Recovery is performed with the asynchronous
- raid6 recovery routines, and will optionally use an offload
- engine if one is available.
-
- If unsure, say N.
-
config MD_MULTIPATH
tristate "Multipath I/O support"
depends on BLK_DEV_MD
+= dm-log-userspace-base.o dm-log-userspace-transfer.o
md-mod-y += md.o bitmap.o
raid456-y += raid5.o
-raid6_pq-y += raid6algos.o raid6recov.o raid6tables.o \
- raid6int1.o raid6int2.o raid6int4.o \
- raid6int8.o raid6int16.o raid6int32.o \
- raid6altivec1.o raid6altivec2.o raid6altivec4.o \
- raid6altivec8.o \
- raid6mmx.o raid6sse1.o raid6sse2.o
-hostprogs-y += mktables
# Note: link order is important. All raid personalities
# and must come before md.o, as they each initialise
obj-$(CONFIG_MD_RAID0) += raid0.o
obj-$(CONFIG_MD_RAID1) += raid1.o
obj-$(CONFIG_MD_RAID10) += raid10.o
-obj-$(CONFIG_MD_RAID6_PQ) += raid6_pq.o
obj-$(CONFIG_MD_RAID456) += raid456.o
obj-$(CONFIG_MD_MULTIPATH) += multipath.o
obj-$(CONFIG_MD_FAULTY) += faulty.o
obj-$(CONFIG_DM_LOG_USERSPACE) += dm-log-userspace.o
obj-$(CONFIG_DM_ZERO) += dm-zero.o
-quiet_cmd_unroll = UNROLL $@
- cmd_unroll = $(AWK) -f$(srctree)/$(src)/unroll.awk -vN=$(UNROLL) \
- < $< > $@ || ( rm -f $@ && exit 1 )
-
-ifeq ($(CONFIG_ALTIVEC),y)
-altivec_flags := -maltivec -mabi=altivec
-endif
-
ifeq ($(CONFIG_DM_UEVENT),y)
dm-mod-objs += dm-uevent.o
endif
-
-targets += raid6int1.c
-$(obj)/raid6int1.c: UNROLL := 1
-$(obj)/raid6int1.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-targets += raid6int2.c
-$(obj)/raid6int2.c: UNROLL := 2
-$(obj)/raid6int2.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-targets += raid6int4.c
-$(obj)/raid6int4.c: UNROLL := 4
-$(obj)/raid6int4.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-targets += raid6int8.c
-$(obj)/raid6int8.c: UNROLL := 8
-$(obj)/raid6int8.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-targets += raid6int16.c
-$(obj)/raid6int16.c: UNROLL := 16
-$(obj)/raid6int16.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-targets += raid6int32.c
-$(obj)/raid6int32.c: UNROLL := 32
-$(obj)/raid6int32.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-CFLAGS_raid6altivec1.o += $(altivec_flags)
-targets += raid6altivec1.c
-$(obj)/raid6altivec1.c: UNROLL := 1
-$(obj)/raid6altivec1.c: $(src)/raid6altivec.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-CFLAGS_raid6altivec2.o += $(altivec_flags)
-targets += raid6altivec2.c
-$(obj)/raid6altivec2.c: UNROLL := 2
-$(obj)/raid6altivec2.c: $(src)/raid6altivec.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-CFLAGS_raid6altivec4.o += $(altivec_flags)
-targets += raid6altivec4.c
-$(obj)/raid6altivec4.c: UNROLL := 4
-$(obj)/raid6altivec4.c: $(src)/raid6altivec.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-CFLAGS_raid6altivec8.o += $(altivec_flags)
-targets += raid6altivec8.c
-$(obj)/raid6altivec8.c: UNROLL := 8
-$(obj)/raid6altivec8.c: $(src)/raid6altivec.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-quiet_cmd_mktable = TABLE $@
- cmd_mktable = $(obj)/mktables > $@ || ( rm -f $@ && exit 1 )
-
-targets += raid6tables.c
-$(obj)/raid6tables.c: $(obj)/mktables FORCE
- $(call if_changed,mktable)
* Still to do:
*
* flush after percent set rather than just time based. (maybe both).
- * wait if count gets too high, wake when it drops to half.
*/
#include <linux/blkdev.h>
#include "md.h"
#include "bitmap.h"
+#include <linux/dm-dirty-log.h>
/* debug macros */
#define DEBUG 0
#define INJECT_FATAL_FAULT_3 0 /* undef */
#endif
-//#define DPRINTK PRINTK /* set this NULL to avoid verbose debug output */
-#define DPRINTK(x...) do { } while(0)
-
#ifndef PRINTK
# if DEBUG > 0
# define PRINTK(x...) printk(KERN_DEBUG x)
# endif
#endif
-static inline char * bmname(struct bitmap *bitmap)
+static inline char *bmname(struct bitmap *bitmap)
{
return bitmap->mddev ? mdname(bitmap->mddev) : "mdX";
}
-
/*
* just a placeholder - calls kmalloc for bitmap pages
*/
#ifdef INJECT_FAULTS_1
page = NULL;
#else
- page = kmalloc(PAGE_SIZE, GFP_NOIO);
+ page = kzalloc(PAGE_SIZE, GFP_NOIO);
#endif
if (!page)
printk("%s: bitmap_alloc_page FAILED\n", bmname(bitmap));
* if we find our page, we increment the page's refcount so that it stays
* allocated while we're using it
*/
-static int bitmap_checkpage(struct bitmap *bitmap, unsigned long page, int create)
+static int bitmap_checkpage(struct bitmap *bitmap,
+ unsigned long page, int create)
__releases(bitmap->lock)
__acquires(bitmap->lock)
{
return -EINVAL;
}
-
if (bitmap->bp[page].hijacked) /* it's hijacked, don't try to alloc */
return 0;
if (!create)
return -ENOENT;
- spin_unlock_irq(&bitmap->lock);
-
/* this page has not been allocated yet */
- if ((mappage = bitmap_alloc_page(bitmap)) == NULL) {
+ spin_unlock_irq(&bitmap->lock);
+ mappage = bitmap_alloc_page(bitmap);
+ spin_lock_irq(&bitmap->lock);
+
+ if (mappage == NULL) {
PRINTK("%s: bitmap map page allocation failed, hijacking\n",
bmname(bitmap));
/* failed - set the hijacked flag so that we can use the
* pointer as a counter */
- spin_lock_irq(&bitmap->lock);
if (!bitmap->bp[page].map)
bitmap->bp[page].hijacked = 1;
- goto out;
- }
-
- /* got a page */
-
- spin_lock_irq(&bitmap->lock);
-
- /* recheck the page */
-
- if (bitmap->bp[page].map || bitmap->bp[page].hijacked) {
+ } else if (bitmap->bp[page].map ||
+ bitmap->bp[page].hijacked) {
/* somebody beat us to getting the page */
bitmap_free_page(bitmap, mappage);
return 0;
- }
+ } else {
- /* no page was in place and we have one, so install it */
+ /* no page was in place and we have one, so install it */
- memset(mappage, 0, PAGE_SIZE);
- bitmap->bp[page].map = mappage;
- bitmap->missing_pages--;
-out:
+ bitmap->bp[page].map = mappage;
+ bitmap->missing_pages--;
+ }
return 0;
}
-
/* if page is completely empty, put it back on the free list, or dealloc it */
/* if page was hijacked, unmark the flag so it might get alloced next time */
/* Note: lock should be held when calling this */
if (bitmap->bp[page].hijacked) { /* page was hijacked, undo this now */
bitmap->bp[page].hijacked = 0;
bitmap->bp[page].map = NULL;
- return;
+ } else {
+ /* normal case, free the page */
+ ptr = bitmap->bp[page].map;
+ bitmap->bp[page].map = NULL;
+ bitmap->missing_pages++;
+ bitmap_free_page(bitmap, ptr);
}
-
- /* normal case, free the page */
-
-#if 0
-/* actually ... let's not. We will probably need the page again exactly when
- * memory is tight and we are flusing to disk
- */
- return;
-#else
- ptr = bitmap->bp[page].map;
- bitmap->bp[page].map = NULL;
- bitmap->missing_pages++;
- bitmap_free_page(bitmap, ptr);
- return;
-#endif
}
-
/*
* bitmap file handling - read and write the bitmap file and its superblock
*/
mdk_rdev_t *rdev;
sector_t target;
+ int did_alloc = 0;
- if (!page)
+ if (!page) {
page = alloc_page(GFP_KERNEL);
- if (!page)
- return ERR_PTR(-ENOMEM);
+ if (!page)
+ return ERR_PTR(-ENOMEM);
+ did_alloc = 1;
+ }
list_for_each_entry(rdev, &mddev->disks, same_set) {
if (! test_bit(In_sync, &rdev->flags)
return page;
}
}
+ if (did_alloc)
+ put_page(page);
return ERR_PTR(-EIO);
}
mddev_t *mddev = bitmap->mddev;
while ((rdev = next_active_rdev(rdev, mddev)) != NULL) {
- int size = PAGE_SIZE;
- loff_t offset = mddev->bitmap_info.offset;
- if (page->index == bitmap->file_pages-1)
- size = roundup(bitmap->last_page_size,
- bdev_logical_block_size(rdev->bdev));
- /* Just make sure we aren't corrupting data or
- * metadata
- */
- if (mddev->external) {
- /* Bitmap could be anywhere. */
- if (rdev->sb_start + offset + (page->index *(PAGE_SIZE/512)) >
- rdev->data_offset &&
- rdev->sb_start + offset <
- rdev->data_offset + mddev->dev_sectors +
- (PAGE_SIZE/512))
- goto bad_alignment;
- } else if (offset < 0) {
- /* DATA BITMAP METADATA */
- if (offset
- + (long)(page->index * (PAGE_SIZE/512))
- + size/512 > 0)
- /* bitmap runs in to metadata */
- goto bad_alignment;
- if (rdev->data_offset + mddev->dev_sectors
- > rdev->sb_start + offset)
- /* data runs in to bitmap */
- goto bad_alignment;
- } else if (rdev->sb_start < rdev->data_offset) {
- /* METADATA BITMAP DATA */
- if (rdev->sb_start
- + offset
- + page->index*(PAGE_SIZE/512) + size/512
- > rdev->data_offset)
- /* bitmap runs in to data */
- goto bad_alignment;
- } else {
- /* DATA METADATA BITMAP - no problems */
- }
- md_super_write(mddev, rdev,
- rdev->sb_start + offset
- + page->index * (PAGE_SIZE/512),
- size,
- page);
+ int size = PAGE_SIZE;
+ loff_t offset = mddev->bitmap_info.offset;
+ if (page->index == bitmap->file_pages-1)
+ size = roundup(bitmap->last_page_size,
+ bdev_logical_block_size(rdev->bdev));
+ /* Just make sure we aren't corrupting data or
+ * metadata
+ */
+ if (mddev->external) {
+ /* Bitmap could be anywhere. */
+ if (rdev->sb_start + offset + (page->index
+ * (PAGE_SIZE/512))
+ > rdev->data_offset
+ &&
+ rdev->sb_start + offset
+ < (rdev->data_offset + mddev->dev_sectors
+ + (PAGE_SIZE/512)))
+ goto bad_alignment;
+ } else if (offset < 0) {
+ /* DATA BITMAP METADATA */
+ if (offset
+ + (long)(page->index * (PAGE_SIZE/512))
+ + size/512 > 0)
+ /* bitmap runs in to metadata */
+ goto bad_alignment;
+ if (rdev->data_offset + mddev->dev_sectors
+ > rdev->sb_start + offset)
+ /* data runs in to bitmap */
+ goto bad_alignment;
+ } else if (rdev->sb_start < rdev->data_offset) {
+ /* METADATA BITMAP DATA */
+ if (rdev->sb_start
+ + offset
+ + page->index*(PAGE_SIZE/512) + size/512
+ > rdev->data_offset)
+ /* bitmap runs in to data */
+ goto bad_alignment;
+ } else {
+ /* DATA METADATA BITMAP - no problems */
+ }
+ md_super_write(mddev, rdev,
+ rdev->sb_start + offset
+ + page->index * (PAGE_SIZE/512),
+ size,
+ page);
}
if (wait)
bh = bh->b_this_page;
}
- if (wait) {
+ if (wait)
wait_event(bitmap->write_wait,
atomic_read(&bitmap->pending_writes)==0);
- }
}
if (bitmap->flags & BITMAP_WRITE_ERROR)
bitmap_file_kick(bitmap);
struct buffer_head *bh;
sector_t block;
- PRINTK("read bitmap file (%dB @ %Lu)\n", (int)PAGE_SIZE,
+ PRINTK("read bitmap file (%dB @ %llu)\n", (int)PAGE_SIZE,
(unsigned long long)index << PAGE_SHIFT);
page = alloc_page(GFP_KERNEL);
}
out:
if (IS_ERR(page))
- printk(KERN_ALERT "md: bitmap read error: (%dB @ %Lu): %ld\n",
+ printk(KERN_ALERT "md: bitmap read error: (%dB @ %llu): %ld\n",
(int)PAGE_SIZE,
(unsigned long long)index << PAGE_SHIFT,
PTR_ERR(page));
sb = kmap_atomic(bitmap->sb_page, KM_USER0);
old = le32_to_cpu(sb->state) & bits;
switch (op) {
- case MASK_SET: sb->state |= cpu_to_le32(bits);
- break;
- case MASK_UNSET: sb->state &= cpu_to_le32(~bits);
- break;
- default: BUG();
+ case MASK_SET:
+ sb->state |= cpu_to_le32(bits);
+ break;
+ case MASK_UNSET:
+ sb->state &= cpu_to_le32(~bits);
+ break;
+ default:
+ BUG();
}
kunmap_atomic(sb, KM_USER0);
return old;
static inline struct page *filemap_get_page(struct bitmap *bitmap,
unsigned long chunk)
{
- if (file_page_index(bitmap, chunk) >= bitmap->file_pages) return NULL;
+ if (bitmap->filemap == NULL)
+ return NULL;
+ if (file_page_index(bitmap, chunk) >= bitmap->file_pages)
+ return NULL;
return bitmap->filemap[file_page_index(bitmap, chunk)
- file_page_index(bitmap, 0)];
}
-
static void bitmap_file_unmap(struct bitmap *bitmap)
{
struct page **map, *sb_page;
}
}
-
/*
* bitmap_file_kick - if an error occurs while manipulating the bitmap file
* then it is no longer reliable, so we stop using it and we mark the file
ptr = d_path(&bitmap->file->f_path, path,
PAGE_SIZE);
-
printk(KERN_ALERT
"%s: kicking failed bitmap file %s from array!\n",
bmname(bitmap), IS_ERR(ptr) ? "" : ptr);
}
enum bitmap_page_attr {
- BITMAP_PAGE_DIRTY = 0, // there are set bits that need to be synced
- BITMAP_PAGE_CLEAN = 1, // there are bits that might need to be cleared
- BITMAP_PAGE_NEEDWRITE=2, // there are cleared bits that need to be synced
+ BITMAP_PAGE_DIRTY = 0, /* there are set bits that need to be synced */
+ BITMAP_PAGE_CLEAN = 1, /* there are bits that might need to be cleared */
+ BITMAP_PAGE_NEEDWRITE = 2, /* there are cleared bits that need to be synced */
};
static inline void set_page_attr(struct bitmap *bitmap, struct page *page,
enum bitmap_page_attr attr)
{
- __set_bit((page->index<<2) + attr, bitmap->filemap_attr);
+ if (page)
+ __set_bit((page->index<<2) + attr, bitmap->filemap_attr);
+ else
+ __set_bit(attr, &bitmap->logattrs);
}
static inline void clear_page_attr(struct bitmap *bitmap, struct page *page,
enum bitmap_page_attr attr)
{
- __clear_bit((page->index<<2) + attr, bitmap->filemap_attr);
+ if (page)
+ __clear_bit((page->index<<2) + attr, bitmap->filemap_attr);
+ else
+ __clear_bit(attr, &bitmap->logattrs);
}
static inline unsigned long test_page_attr(struct bitmap *bitmap, struct page *page,
enum bitmap_page_attr attr)
{
- return test_bit((page->index<<2) + attr, bitmap->filemap_attr);
+ if (page)
+ return test_bit((page->index<<2) + attr, bitmap->filemap_attr);
+ else
+ return test_bit(attr, &bitmap->logattrs);
}
/*
static void bitmap_file_set_bit(struct bitmap *bitmap, sector_t block)
{
unsigned long bit;
- struct page *page;
+ struct page *page = NULL;
void *kaddr;
unsigned long chunk = block >> CHUNK_BLOCK_SHIFT(bitmap);
if (!bitmap->filemap) {
- return;
- }
-
- page = filemap_get_page(bitmap, chunk);
- if (!page) return;
- bit = file_page_offset(bitmap, chunk);
+ struct dm_dirty_log *log = bitmap->mddev->bitmap_info.log;
+ if (log)
+ log->type->mark_region(log, chunk);
+ } else {
- /* set the bit */
- kaddr = kmap_atomic(page, KM_USER0);
- if (bitmap->flags & BITMAP_HOSTENDIAN)
- set_bit(bit, kaddr);
- else
- ext2_set_bit(bit, kaddr);
- kunmap_atomic(kaddr, KM_USER0);
- PRINTK("set file bit %lu page %lu\n", bit, page->index);
+ page = filemap_get_page(bitmap, chunk);
+ if (!page)
+ return;
+ bit = file_page_offset(bitmap, chunk);
+ /* set the bit */
+ kaddr = kmap_atomic(page, KM_USER0);
+ if (bitmap->flags & BITMAP_HOSTENDIAN)
+ set_bit(bit, kaddr);
+ else
+ ext2_set_bit(bit, kaddr);
+ kunmap_atomic(kaddr, KM_USER0);
+ PRINTK("set file bit %lu page %lu\n", bit, page->index);
+ }
/* record page number so it gets flushed to disk when unplug occurs */
set_page_attr(bitmap, page, BITMAP_PAGE_DIRTY);
-
}
/* this gets called when the md device is ready to unplug its underlying
if (!bitmap)
return;
+ if (!bitmap->filemap) {
+ /* Must be using a dirty_log */
+ struct dm_dirty_log *log = bitmap->mddev->bitmap_info.log;
+ dirty = test_and_clear_bit(BITMAP_PAGE_DIRTY, &bitmap->logattrs);
+ need_write = test_and_clear_bit(BITMAP_PAGE_NEEDWRITE, &bitmap->logattrs);
+ if (dirty || need_write)
+ if (log->type->flush(log))
+ bitmap->flags |= BITMAP_WRITE_ERROR;
+ goto out;
+ }
/* look at each page to see if there are any set bits that need to be
* flushed out to disk */
wait = 1;
spin_unlock_irqrestore(&bitmap->lock, flags);
- if (dirty | need_write)
+ if (dirty || need_write)
write_page(bitmap, page, 0);
}
if (wait) { /* if any writes were performed, we need to wait on them */
else
md_super_wait(bitmap->mddev);
}
+out:
if (bitmap->flags & BITMAP_WRITE_ERROR)
bitmap_file_kick(bitmap);
}
+EXPORT_SYMBOL(bitmap_unplug);
static void bitmap_set_memory_bits(struct bitmap *bitmap, sector_t offset, int needed);
/* * bitmap_init_from_disk -- called at bitmap_create time to initialize
printk(KERN_INFO "%s: bitmap file is out of date, doing full "
"recovery\n", bmname(bitmap));
- bytes = (chunks + 7) / 8;
+ bytes = DIV_ROUND_UP(bitmap->chunks, 8);
if (!bitmap->mddev->bitmap_info.external)
bytes += sizeof(bitmap_super_t);
-
- num_pages = (bytes + PAGE_SIZE - 1) / PAGE_SIZE;
+ num_pages = DIV_ROUND_UP(bytes, PAGE_SIZE);
if (file && i_size_read(file->f_mapping->host) < bytes) {
printk(KERN_INFO "%s: bitmap file too short %lu < %lu\n",
/* We need 4 bits per page, rounded up to a multiple of sizeof(unsigned long) */
bitmap->filemap_attr = kzalloc(
- roundup( DIV_ROUND_UP(num_pages*4, 8), sizeof(unsigned long)),
+ roundup(DIV_ROUND_UP(num_pages*4, 8), sizeof(unsigned long)),
GFP_KERNEL);
if (!bitmap->filemap_attr)
goto err;
if (outofdate) {
/*
* if bitmap is out of date, dirty the
- * whole page and write it out
+ * whole page and write it out
*/
paddr = kmap_atomic(page, KM_USER0);
memset(paddr + offset, 0xff,
}
}
- /* everything went OK */
+ /* everything went OK */
ret = 0;
bitmap_mask_state(bitmap, BITMAP_STALE, MASK_UNSET);
*/
int i;
- for (i=0; i < bitmap->file_pages; i++)
+ for (i = 0; i < bitmap->file_pages; i++)
set_page_attr(bitmap, bitmap->filemap[i],
BITMAP_PAGE_NEEDWRITE);
}
-
static void bitmap_count_page(struct bitmap *bitmap, sector_t offset, int inc)
{
sector_t chunk = offset >> CHUNK_BLOCK_SHIFT(bitmap);
unsigned long page = chunk >> PAGE_COUNTER_SHIFT;
bitmap->bp[page].count += inc;
-/*
- if (page == 0) printk("count page 0, offset %llu: %d gives %d\n",
- (unsigned long long)offset, inc, bitmap->bp[page].count);
-*/
bitmap_checkfree(bitmap, page);
}
static bitmap_counter_t *bitmap_get_counter(struct bitmap *bitmap,
struct page *page = NULL, *lastpage = NULL;
int blocks;
void *paddr;
+ struct dm_dirty_log *log = mddev->bitmap_info.log;
/* Use a mutex to guard daemon_work against
* bitmap_destroy.
spin_lock_irqsave(&bitmap->lock, flags);
for (j = 0; j < bitmap->chunks; j++) {
bitmap_counter_t *bmc;
- if (!bitmap->filemap)
- /* error or shutdown */
- break;
-
- page = filemap_get_page(bitmap, j);
+ if (!bitmap->filemap) {
+ if (!log)
+ /* error or shutdown */
+ break;
+ } else
+ page = filemap_get_page(bitmap, j);
if (page != lastpage) {
/* skip this page unless it's marked as needing cleaning */
(sector_t)j << CHUNK_BLOCK_SHIFT(bitmap),
&blocks, 0);
if (bmc) {
-/*
- if (j < 100) printk("bitmap: j=%lu, *bmc = 0x%x\n", j, *bmc);
-*/
if (*bmc)
bitmap->allclean = 0;
if (*bmc == 2) {
- *bmc=1; /* maybe clear the bit next time */
+ *bmc = 1; /* maybe clear the bit next time */
set_page_attr(bitmap, page, BITMAP_PAGE_CLEAN);
} else if (*bmc == 1 && !bitmap->need_sync) {
/* we can clear the bit */
-1);
/* clear the bit */
- paddr = kmap_atomic(page, KM_USER0);
- if (bitmap->flags & BITMAP_HOSTENDIAN)
- clear_bit(file_page_offset(bitmap, j),
- paddr);
- else
- ext2_clear_bit(file_page_offset(bitmap, j),
- paddr);
- kunmap_atomic(paddr, KM_USER0);
+ if (page) {
+ paddr = kmap_atomic(page, KM_USER0);
+ if (bitmap->flags & BITMAP_HOSTENDIAN)
+ clear_bit(file_page_offset(bitmap, j),
+ paddr);
+ else
+ ext2_clear_bit(file_page_offset(bitmap, j),
+ paddr);
+ kunmap_atomic(paddr, KM_USER0);
+ } else
+ log->type->clear_region(log, j);
}
} else
j |= PAGE_COUNTER_MASK;
spin_unlock_irqrestore(&bitmap->lock, flags);
/* now sync the final page */
- if (lastpage != NULL) {
+ if (lastpage != NULL || log != NULL) {
spin_lock_irqsave(&bitmap->lock, flags);
if (test_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE)) {
clear_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
spin_unlock_irqrestore(&bitmap->lock, flags);
- write_page(bitmap, lastpage, 0);
+ if (lastpage)
+ write_page(bitmap, lastpage, 0);
+ else
+ if (log->type->flush(log))
+ bitmap->flags |= BITMAP_WRITE_ERROR;
} else {
set_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
spin_unlock_irqrestore(&bitmap->lock, flags);
done:
if (bitmap->allclean == 0)
- bitmap->mddev->thread->timeout =
+ bitmap->mddev->thread->timeout =
bitmap->mddev->bitmap_info.daemon_sleep;
mutex_unlock(&mddev->bitmap_info.mutex);
}
unsigned long page = chunk >> PAGE_COUNTER_SHIFT;
unsigned long pageoff = (chunk & PAGE_COUNTER_MASK) << COUNTER_BYTE_SHIFT;
sector_t csize;
+ int err;
- if (bitmap_checkpage(bitmap, page, create) < 0) {
+ err = bitmap_checkpage(bitmap, page, create);
+
+ if (bitmap->bp[page].hijacked ||
+ bitmap->bp[page].map == NULL)
+ csize = ((sector_t)1) << (CHUNK_BLOCK_SHIFT(bitmap) +
+ PAGE_COUNTER_SHIFT - 1);
+ else
csize = ((sector_t)1) << (CHUNK_BLOCK_SHIFT(bitmap));
- *blocks = csize - (offset & (csize- 1));
+ *blocks = csize - (offset & (csize - 1));
+
+ if (err < 0)
return NULL;
- }
+
/* now locked ... */
if (bitmap->bp[page].hijacked) { /* hijacked pointer */
/* should we use the first or second counter field
* of the hijacked pointer? */
int hi = (pageoff > PAGE_COUNTER_MASK);
- csize = ((sector_t)1) << (CHUNK_BLOCK_SHIFT(bitmap) +
- PAGE_COUNTER_SHIFT - 1);
- *blocks = csize - (offset & (csize- 1));
return &((bitmap_counter_t *)
&bitmap->bp[page].map)[hi];
- } else { /* page is allocated */
- csize = ((sector_t)1) << (CHUNK_BLOCK_SHIFT(bitmap));
- *blocks = csize - (offset & (csize- 1));
+ } else /* page is allocated */
return (bitmap_counter_t *)
&(bitmap->bp[page].map[pageoff]);
- }
}
int bitmap_startwrite(struct bitmap *bitmap, sector_t offset, unsigned long sectors, int behind)
{
- if (!bitmap) return 0;
+ if (!bitmap)
+ return 0;
if (behind) {
int bw;
prepare_to_wait(&bitmap->overflow_wait, &__wait,
TASK_UNINTERRUPTIBLE);
spin_unlock_irq(&bitmap->lock);
- blk_unplug(bitmap->mddev->queue);
+ md_unplug(bitmap->mddev);
schedule();
finish_wait(&bitmap->overflow_wait, &__wait);
continue;
}
- switch(*bmc) {
+ switch (*bmc) {
case 0:
bitmap_file_set_bit(bitmap, offset);
- bitmap_count_page(bitmap,offset, 1);
- blk_plug_device_unlocked(bitmap->mddev->queue);
+ bitmap_count_page(bitmap, offset, 1);
/* fall through */
case 1:
*bmc = 2;
offset += blocks;
if (sectors > blocks)
sectors -= blocks;
- else sectors = 0;
+ else
+ sectors = 0;
}
bitmap->allclean = 0;
return 0;
}
+EXPORT_SYMBOL(bitmap_startwrite);
void bitmap_endwrite(struct bitmap *bitmap, sector_t offset, unsigned long sectors,
int success, int behind)
{
- if (!bitmap) return;
+ if (!bitmap)
+ return;
if (behind) {
if (atomic_dec_and_test(&bitmap->behind_writes))
wake_up(&bitmap->behind_wait);
bitmap->events_cleared < bitmap->mddev->events) {
bitmap->events_cleared = bitmap->mddev->events;
bitmap->need_sync = 1;
- sysfs_notify_dirent(bitmap->sysfs_can_clear);
+ sysfs_notify_dirent_safe(bitmap->sysfs_can_clear);
}
if (!success && ! (*bmc & NEEDED_MASK))
wake_up(&bitmap->overflow_wait);
(*bmc)--;
- if (*bmc <= 2) {
+ if (*bmc <= 2)
set_page_attr(bitmap,
- filemap_get_page(bitmap, offset >> CHUNK_BLOCK_SHIFT(bitmap)),
+ filemap_get_page(
+ bitmap,
+ offset >> CHUNK_BLOCK_SHIFT(bitmap)),
BITMAP_PAGE_CLEAN);
- }
+
spin_unlock_irqrestore(&bitmap->lock, flags);
offset += blocks;
if (sectors > blocks)
sectors -= blocks;
- else sectors = 0;
+ else
+ sectors = 0;
}
}
+EXPORT_SYMBOL(bitmap_endwrite);
static int __bitmap_start_sync(struct bitmap *bitmap, sector_t offset, int *blocks,
int degraded)
}
return rv;
}
+EXPORT_SYMBOL(bitmap_start_sync);
void bitmap_end_sync(struct bitmap *bitmap, sector_t offset, int *blocks, int aborted)
{
bitmap_counter_t *bmc;
unsigned long flags;
-/*
- if (offset == 0) printk("bitmap_end_sync 0 (%d)\n", aborted);
-*/ if (bitmap == NULL) {
+
+ if (bitmap == NULL) {
*blocks = 1024;
return;
}
if (bmc == NULL)
goto unlock;
/* locked */
-/*
- if (offset == 0) printk("bitmap_end sync found 0x%x, blocks %d\n", *bmc, *blocks);
-*/
if (RESYNC(*bmc)) {
*bmc &= ~RESYNC_MASK;
if (!NEEDED(*bmc) && aborted)
*bmc |= NEEDED_MASK;
else {
- if (*bmc <= 2) {
+ if (*bmc <= 2)
set_page_attr(bitmap,
filemap_get_page(bitmap, offset >> CHUNK_BLOCK_SHIFT(bitmap)),
BITMAP_PAGE_CLEAN);
- }
}
}
unlock:
spin_unlock_irqrestore(&bitmap->lock, flags);
bitmap->allclean = 0;
}
+EXPORT_SYMBOL(bitmap_end_sync);
void bitmap_close_sync(struct bitmap *bitmap)
{
sector += blocks;
}
}
+EXPORT_SYMBOL(bitmap_close_sync);
void bitmap_cond_end_sync(struct bitmap *bitmap, sector_t sector)
{
atomic_read(&bitmap->mddev->recovery_active) == 0);
bitmap->mddev->curr_resync_completed = bitmap->mddev->curr_resync;
- set_bit(MD_CHANGE_CLEAN, &bitmap->mddev->flags);
+ if (bitmap->mddev->persistent)
+ set_bit(MD_CHANGE_CLEAN, &bitmap->mddev->flags);
sector &= ~((1ULL << CHUNK_BLOCK_SHIFT(bitmap)) - 1);
s = 0;
while (s < sector && s < bitmap->mddev->resync_max_sectors) {
bitmap->last_end_sync = jiffies;
sysfs_notify(&bitmap->mddev->kobj, NULL, "sync_completed");
}
+EXPORT_SYMBOL(bitmap_cond_end_sync);
static void bitmap_set_memory_bits(struct bitmap *bitmap, sector_t offset, int needed)
{
spin_unlock_irq(&bitmap->lock);
return;
}
- if (! *bmc) {
+ if (!*bmc) {
struct page *page;
- *bmc = 1 | (needed?NEEDED_MASK:0);
+ *bmc = 1 | (needed ? NEEDED_MASK : 0);
bitmap_count_page(bitmap, offset, 1);
page = filemap_get_page(bitmap, offset >> CHUNK_BLOCK_SHIFT(bitmap));
set_page_attr(bitmap, page, BITMAP_PAGE_CLEAN);
unsigned long pages;
struct file *file = mddev->bitmap_info.file;
int err;
- sector_t start;
- struct sysfs_dirent *bm;
+ struct sysfs_dirent *bm = NULL;
BUILD_BUG_ON(sizeof(bitmap_super_t) != 256);
- if (!file && !mddev->bitmap_info.offset) /* bitmap disabled, nothing to do */
+ if (!file
+ && !mddev->bitmap_info.offset
+ && !mddev->bitmap_info.log) /* bitmap disabled, nothing to do */
return 0;
BUG_ON(file && mddev->bitmap_info.offset);
+ BUG_ON(mddev->bitmap_info.offset && mddev->bitmap_info.log);
bitmap = kzalloc(sizeof(*bitmap), GFP_KERNEL);
if (!bitmap)
bitmap->mddev = mddev;
- bm = sysfs_get_dirent(mddev->kobj.sd, NULL, "bitmap");
+ if (mddev->kobj.sd)
+ bm = sysfs_get_dirent(mddev->kobj.sd, NULL, "bitmap");
if (bm) {
bitmap->sysfs_can_clear = sysfs_get_dirent(bm, NULL, "can_clear");
sysfs_put(bm);
bitmap->chunkshift = ffz(~mddev->bitmap_info.chunksize);
/* now that chunksize and chunkshift are set, we can use these macros */
- chunks = (blocks + CHUNK_BLOCK_RATIO(bitmap) - 1) >>
+ chunks = (blocks + CHUNK_BLOCK_RATIO(bitmap) - 1) >>
CHUNK_BLOCK_SHIFT(bitmap);
- pages = (chunks + PAGE_COUNTER_RATIO - 1) / PAGE_COUNTER_RATIO;
+ pages = (chunks + PAGE_COUNTER_RATIO - 1) / PAGE_COUNTER_RATIO;
BUG_ON(!pages);
if (!bitmap->bp)
goto error;
- /* now that we have some pages available, initialize the in-memory
- * bitmap from the on-disk bitmap */
- start = 0;
- if (mddev->degraded == 0
- || bitmap->events_cleared == mddev->events)
- /* no need to keep dirty bits to optimise a re-add of a missing device */
- start = mddev->recovery_cp;
- err = bitmap_init_from_disk(bitmap, start);
-
- if (err)
- goto error;
-
printk(KERN_INFO "created bitmap (%lu pages) for device %s\n",
pages, bmname(bitmap));
mddev->bitmap = bitmap;
- mddev->thread->timeout = mddev->bitmap_info.daemon_sleep;
- md_wakeup_thread(mddev->thread);
-
- bitmap_update_sb(bitmap);
return (bitmap->flags & BITMAP_WRITE_ERROR) ? -EIO : 0;
return err;
}
+int bitmap_load(mddev_t *mddev)
+{
+ int err = 0;
+ sector_t sector = 0;
+ struct bitmap *bitmap = mddev->bitmap;
+
+ if (!bitmap)
+ goto out;
+
+ /* Clear out old bitmap info first: Either there is none, or we
+ * are resuming after someone else has possibly changed things,
+ * so we should forget old cached info.
+ * All chunks should be clean, but some might need_sync.
+ */
+ while (sector < mddev->resync_max_sectors) {
+ int blocks;
+ bitmap_start_sync(bitmap, sector, &blocks, 0);
+ sector += blocks;
+ }
+ bitmap_close_sync(bitmap);
+
+ if (mddev->bitmap_info.log) {
+ unsigned long i;
+ struct dm_dirty_log *log = mddev->bitmap_info.log;
+ for (i = 0; i < bitmap->chunks; i++)
+ if (!log->type->in_sync(log, i, 1))
+ bitmap_set_memory_bits(bitmap,
+ (sector_t)i << CHUNK_BLOCK_SHIFT(bitmap),
+ 1);
+ } else {
+ sector_t start = 0;
+ if (mddev->degraded == 0
+ || bitmap->events_cleared == mddev->events)
+ /* no need to keep dirty bits to optimise a
+ * re-add of a missing device */
+ start = mddev->recovery_cp;
+
+ err = bitmap_init_from_disk(bitmap, start);
+ }
+ if (err)
+ goto out;
+
+ mddev->thread->timeout = mddev->bitmap_info.daemon_sleep;
+ md_wakeup_thread(mddev->thread);
+
+ bitmap_update_sb(bitmap);
+
+ if (bitmap->flags & BITMAP_WRITE_ERROR)
+ err = -EIO;
+out:
+ return err;
+}
+EXPORT_SYMBOL_GPL(bitmap_load);
+
static ssize_t
location_show(mddev_t *mddev, char *page)
{
ssize_t len;
- if (mddev->bitmap_info.file) {
+ if (mddev->bitmap_info.file)
len = sprintf(page, "file");
- } else if (mddev->bitmap_info.offset) {
+ else if (mddev->bitmap_info.offset)
len = sprintf(page, "%+lld", (long long)mddev->bitmap_info.offset);
- } else
+ else
len = sprintf(page, "none");
len += sprintf(page+len, "\n");
return len;
ssize_t len;
unsigned long secs = mddev->bitmap_info.daemon_sleep / HZ;
unsigned long jifs = mddev->bitmap_info.daemon_sleep % HZ;
-
+
len = sprintf(page, "%lu", secs);
if (jifs)
len += sprintf(page+len, ".%03u", jiffies_to_msecs(jifs));
.attrs = md_bitmap_attrs,
};
-
-/* the bitmap API -- for raid personalities */
-EXPORT_SYMBOL(bitmap_startwrite);
-EXPORT_SYMBOL(bitmap_endwrite);
-EXPORT_SYMBOL(bitmap_start_sync);
-EXPORT_SYMBOL(bitmap_end_sync);
-EXPORT_SYMBOL(bitmap_unplug);
-EXPORT_SYMBOL(bitmap_close_sync);
-EXPORT_SYMBOL(bitmap_cond_end_sync);
unsigned long file_pages; /* number of pages in the file */
int last_page_size; /* bytes in the last page */
+ unsigned long logattrs; /* used when filemap_attr doesn't exist
+ * because we are working with a dirty_log
+ */
+
unsigned long flags;
int allclean;
wait_queue_head_t behind_wait;
struct sysfs_dirent *sysfs_can_clear;
+
};
/* the bitmap API */
/* these are used only by md/bitmap */
int bitmap_create(mddev_t *mddev);
+int bitmap_load(mddev_t *mddev);
void bitmap_flush(mddev_t *mddev);
void bitmap_destroy(mddev_t *mddev);
BUG_ON(num_regions > DM_IO_MAX_REGIONS);
if (sync)
- rw |= (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG);
+ rw |= REQ_SYNC | REQ_UNPLUG;
/*
* For multiple regions we need to be careful to rewind
*/
for (i = 0; i < num_regions; i++) {
*dp = old_pages;
- if (where[i].count || (rw & (1 << BIO_RW_BARRIER)))
+ if (where[i].count || (rw & REQ_HARDBARRIER))
do_region(rw, i, where + i, dp, io);
}
}
set_current_state(TASK_RUNNING);
- if (io->eopnotsupp_bits && (rw & (1 << BIO_RW_BARRIER))) {
- rw &= ~(1 << BIO_RW_BARRIER);
+ if (io->eopnotsupp_bits && (rw & REQ_HARDBARRIER)) {
+ rw &= ~REQ_HARDBARRIER;
goto retry;
}
* New collapsed (a)synchronous interface.
*
* If the IO is asynchronous (i.e. it has notify.fn), you must either unplug
- * the queue with blk_unplug() some time later or set the BIO_RW_SYNC bit in
- * io_req->bi_rw. If you fail to do one of these, the IO will be submitted to
+ * the queue with blk_unplug() some time later or set REQ_SYNC in
+io_req->bi_rw. If you fail to do one of these, the IO will be submitted to
* the disk after q->unplug_delay, which defaults to 3ms in blk-settings.c.
*/
int dm_io(struct dm_io_request *io_req, unsigned num_regions,
{
int r;
struct dm_io_request io_req = {
- .bi_rw = job->rw | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG),
+ .bi_rw = job->rw | REQ_SYNC | REQ_UNPLUG,
.mem.type = DM_IO_PAGE_LIST,
.mem.ptr.pl = job->pages,
.mem.offset = job->offset,
if (error == -EOPNOTSUPP)
goto out;
- if ((error == -EWOULDBLOCK) && bio_rw_flagged(bio, BIO_RW_AHEAD))
+ if ((error == -EWOULDBLOCK) && (bio->bi_rw & REQ_RAHEAD))
goto out;
if (unlikely(error)) {
if (!error)
return 0; /* I/O complete */
- if ((error == -EWOULDBLOCK) && bio_rw_flagged(bio, BIO_RW_AHEAD))
+ if ((error == -EWOULDBLOCK) && (bio->bi_rw & REQ_RAHEAD))
return error;
if (error == -EOPNOTSUPP)
#include <linux/blkpg.h>
#include <linux/bio.h>
#include <linux/buffer_head.h>
+#include <linux/smp_lock.h>
#include <linux/mempool.h>
#include <linux/slab.h>
#include <linux/idr.h>
{
struct mapped_device *md;
+ lock_kernel();
spin_lock(&_minor_lock);
md = bdev->bd_disk->private_data;
out:
spin_unlock(&_minor_lock);
+ unlock_kernel();
return md ? 0 : -ENXIO;
}
static int dm_blk_close(struct gendisk *disk, fmode_t mode)
{
struct mapped_device *md = disk->private_data;
+
+ lock_kernel();
atomic_dec(&md->open_count);
dm_put(md);
+ unlock_kernel();
+
return 0;
}
*/
spin_lock_irqsave(&md->deferred_lock, flags);
if (__noflush_suspending(md)) {
- if (!bio_rw_flagged(io->bio, BIO_RW_BARRIER))
+ if (!(io->bio->bi_rw & REQ_HARDBARRIER))
bio_list_add_head(&md->deferred,
io->bio);
} else
io_error = io->error;
bio = io->bio;
- if (bio_rw_flagged(bio, BIO_RW_BARRIER)) {
+ if (bio->bi_rw & REQ_HARDBARRIER) {
/*
* There can be just one barrier request so we use
* a per-device variable for error reporting.
{
int rw = rq_data_dir(clone);
int run_queue = 1;
- bool is_barrier = blk_barrier_rq(clone);
+ bool is_barrier = clone->cmd_flags & REQ_HARDBARRIER;
struct dm_rq_target_io *tio = clone->end_io_data;
struct mapped_device *md = tio->md;
struct request *rq = tio->orig;
- if (blk_pc_request(rq) && !is_barrier) {
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC && !is_barrier) {
rq->errors = clone->errors;
rq->resid_len = clone->resid_len;
struct request_queue *q = rq->q;
unsigned long flags;
- if (unlikely(blk_barrier_rq(clone))) {
+ if (unlikely(clone->cmd_flags & REQ_HARDBARRIER)) {
/*
* Barrier clones share an original request.
* Leave it to dm_end_request(), which handles this special
struct dm_rq_target_io *tio = clone->end_io_data;
struct request *rq = tio->orig;
- if (unlikely(blk_barrier_rq(clone))) {
+ if (unlikely(clone->cmd_flags & REQ_HARDBARRIER)) {
/*
* Barrier clones share an original request. So can't use
* softirq_done with the original.
struct dm_rq_target_io *tio = clone->end_io_data;
struct request *rq = tio->orig;
- if (unlikely(blk_barrier_rq(clone))) {
+ if (unlikely(clone->cmd_flags & REQ_HARDBARRIER)) {
/*
* Barrier clones share an original request.
* Leave it to dm_end_request(), which handles this special
clone->bi_sector = sector;
clone->bi_bdev = bio->bi_bdev;
- clone->bi_rw = bio->bi_rw & ~(1 << BIO_RW_BARRIER);
+ clone->bi_rw = bio->bi_rw & ~REQ_HARDBARRIER;
clone->bi_vcnt = 1;
clone->bi_size = to_bytes(len);
clone->bi_io_vec->bv_offset = offset;
clone = bio_alloc_bioset(GFP_NOIO, bio->bi_max_vecs, bs);
__bio_clone(clone, bio);
- clone->bi_rw &= ~(1 << BIO_RW_BARRIER);
+ clone->bi_rw &= ~REQ_HARDBARRIER;
clone->bi_destructor = dm_bio_destructor;
clone->bi_sector = sector;
clone->bi_idx = idx;
ci.map = dm_get_live_table(md);
if (unlikely(!ci.map)) {
- if (!bio_rw_flagged(bio, BIO_RW_BARRIER))
+ if (!(bio->bi_rw & REQ_HARDBARRIER))
bio_io_error(bio);
else
if (!md->barrier_error)
* we have to queue this io for later.
*/
if (unlikely(test_bit(DMF_QUEUE_IO_TO_THREAD, &md->flags)) ||
- unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
+ unlikely(bio->bi_rw & REQ_HARDBARRIER)) {
up_read(&md->io_lock);
if (unlikely(test_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags)) &&
return _dm_request(q, bio);
}
-/*
- * Mark this request as flush request, so that dm_request_fn() can
- * recognize.
- */
-static void dm_rq_prepare_flush(struct request_queue *q, struct request *rq)
-{
- rq->cmd_type = REQ_TYPE_LINUX_BLOCK;
- rq->cmd[0] = REQ_LB_OP_FLUSH;
-}
-
static bool dm_rq_is_flush_request(struct request *rq)
{
- if (rq->cmd_type == REQ_TYPE_LINUX_BLOCK &&
- rq->cmd[0] == REQ_LB_OP_FLUSH)
+ if (rq->cmd_flags & REQ_FLUSH)
return true;
else
return false;
blk_queue_softirq_done(md->queue, dm_softirq_done);
blk_queue_prep_rq(md->queue, dm_prep_fn);
blk_queue_lld_busy(md->queue, dm_lld_busy);
- blk_queue_ordered(md->queue, QUEUE_ORDERED_DRAIN_FLUSH,
- dm_rq_prepare_flush);
+ blk_queue_ordered(md->queue, QUEUE_ORDERED_DRAIN_FLUSH);
md->disk = alloc_disk(1);
if (!md->disk)
if (dm_request_based(md))
generic_make_request(c);
else {
- if (bio_rw_flagged(c, BIO_RW_BARRIER))
+ if (c->bi_rw & REQ_HARDBARRIER)
process_barrier(md, c);
else
__split_and_process_bio(md, c);
dev_info_t *tmp_dev;
sector_t start_sector;
- if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
+ if (unlikely(bio->bi_rw & REQ_HARDBARRIER)) {
md_barrier_request(mddev, bio);
return 0;
}
#include <linux/blkdev.h>
#include <linux/sysctl.h>
#include <linux/seq_file.h>
+#include <linux/smp_lock.h>
#include <linux/buffer_head.h> /* for invalidate_bdev */
#include <linux/poll.h>
#include <linux/ctype.h>
* Once ->stop is called and completes, the module will be completely
* unused.
*/
-static void mddev_suspend(mddev_t *mddev)
+void mddev_suspend(mddev_t *mddev)
{
BUG_ON(mddev->suspended);
mddev->suspended = 1;
wait_event(mddev->sb_wait, atomic_read(&mddev->active_io) == 0);
mddev->pers->quiesce(mddev, 1);
}
+EXPORT_SYMBOL_GPL(mddev_suspend);
-static void mddev_resume(mddev_t *mddev)
+void mddev_resume(mddev_t *mddev)
{
mddev->suspended = 0;
wake_up(&mddev->sb_wait);
mddev->pers->quiesce(mddev, 0);
}
+EXPORT_SYMBOL_GPL(mddev_resume);
int mddev_congested(mddev_t *mddev, int bits)
{
/* an empty barrier - all done */
bio_endio(bio, 0);
else {
- bio->bi_rw &= ~(1<<BIO_RW_BARRIER);
+ bio->bi_rw &= ~REQ_HARDBARRIER;
if (mddev->pers->make_request(mddev, bio))
generic_make_request(bio);
mddev->barrier = POST_REQUEST_BARRIER;
}
EXPORT_SYMBOL(md_barrier_request);
+/* Support for plugging.
+ * This mirrors the plugging support in request_queue, but does not
+ * require having a whole queue
+ */
+static void plugger_work(struct work_struct *work)
+{
+ struct plug_handle *plug =
+ container_of(work, struct plug_handle, unplug_work);
+ plug->unplug_fn(plug);
+}
+static void plugger_timeout(unsigned long data)
+{
+ struct plug_handle *plug = (void *)data;
+ kblockd_schedule_work(NULL, &plug->unplug_work);
+}
+void plugger_init(struct plug_handle *plug,
+ void (*unplug_fn)(struct plug_handle *))
+{
+ plug->unplug_flag = 0;
+ plug->unplug_fn = unplug_fn;
+ init_timer(&plug->unplug_timer);
+ plug->unplug_timer.function = plugger_timeout;
+ plug->unplug_timer.data = (unsigned long)plug;
+ INIT_WORK(&plug->unplug_work, plugger_work);
+}
+EXPORT_SYMBOL_GPL(plugger_init);
+
+void plugger_set_plug(struct plug_handle *plug)
+{
+ if (!test_and_set_bit(PLUGGED_FLAG, &plug->unplug_flag))
+ mod_timer(&plug->unplug_timer, jiffies + msecs_to_jiffies(3)+1);
+}
+EXPORT_SYMBOL_GPL(plugger_set_plug);
+
+int plugger_remove_plug(struct plug_handle *plug)
+{
+ if (test_and_clear_bit(PLUGGED_FLAG, &plug->unplug_flag)) {
+ del_timer(&plug->unplug_timer);
+ return 1;
+ } else
+ return 0;
+}
+EXPORT_SYMBOL_GPL(plugger_remove_plug);
+
+
static inline mddev_t *mddev_get(mddev_t *mddev)
{
atomic_inc(&mddev->active);
spin_unlock(&all_mddevs_lock);
}
-static void mddev_init(mddev_t *mddev)
+void mddev_init(mddev_t *mddev)
{
mutex_init(&mddev->open_mutex);
mutex_init(&mddev->reconfig_mutex);
mddev->resync_max = MaxSector;
mddev->level = LEVEL_NONE;
}
+EXPORT_SYMBOL_GPL(mddev_init);
static mddev_t * mddev_find(dev_t unit)
{
* an access to the files will try to take reconfig_mutex
* while holding the file unremovable, which leads to
* a deadlock.
- * So hold open_mutex instead - we are allowed to take
- * it while holding reconfig_mutex, and md_run can
- * use it to wait for the remove to complete.
+ * So hold set sysfs_active while the remove in happeing,
+ * and anything else which might set ->to_remove or my
+ * otherwise change the sysfs namespace will fail with
+ * -EBUSY if sysfs_active is still set.
+ * We set sysfs_active under reconfig_mutex and elsewhere
+ * test it under the same mutex to ensure its correct value
+ * is seen.
*/
struct attribute_group *to_remove = mddev->to_remove;
mddev->to_remove = NULL;
- mutex_lock(&mddev->open_mutex);
+ mddev->sysfs_active = 1;
mutex_unlock(&mddev->reconfig_mutex);
- if (to_remove != &md_redundancy_group)
- sysfs_remove_group(&mddev->kobj, to_remove);
- if (mddev->pers == NULL ||
- mddev->pers->sync_request == NULL) {
- sysfs_remove_group(&mddev->kobj, &md_redundancy_group);
- if (mddev->sysfs_action)
- sysfs_put(mddev->sysfs_action);
- mddev->sysfs_action = NULL;
+ if (mddev->kobj.sd) {
+ if (to_remove != &md_redundancy_group)
+ sysfs_remove_group(&mddev->kobj, to_remove);
+ if (mddev->pers == NULL ||
+ mddev->pers->sync_request == NULL) {
+ sysfs_remove_group(&mddev->kobj, &md_redundancy_group);
+ if (mddev->sysfs_action)
+ sysfs_put(mddev->sysfs_action);
+ mddev->sysfs_action = NULL;
+ }
}
- mutex_unlock(&mddev->open_mutex);
+ mddev->sysfs_active = 0;
} else
mutex_unlock(&mddev->reconfig_mutex);
* if zero is reached.
* If an error occurred, call md_error
*
- * As we might need to resubmit the request if BIO_RW_BARRIER
+ * As we might need to resubmit the request if REQ_HARDBARRIER
* causes ENOTSUPP, we allocate a spare bio...
*/
struct bio *bio = bio_alloc(GFP_NOIO, 1);
- int rw = (1<<BIO_RW) | (1<<BIO_RW_SYNCIO) | (1<<BIO_RW_UNPLUG);
+ int rw = REQ_WRITE | REQ_SYNC | REQ_UNPLUG;
bio->bi_bdev = rdev->bdev;
bio->bi_sector = sector;
atomic_inc(&mddev->pending_writes);
if (!test_bit(BarriersNotsupp, &rdev->flags)) {
struct bio *rbio;
- rw |= (1<<BIO_RW_BARRIER);
+ rw |= REQ_HARDBARRIER;
rbio = bio_clone(bio, GFP_NOIO);
rbio->bi_private = bio;
rbio->bi_end_io = super_written_barrier;
struct completion event;
int ret;
- rw |= (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG);
+ rw |= REQ_SYNC | REQ_UNPLUG;
bio->bi_bdev = bdev;
bio->bi_sector = sector;
goto fail;
ko = &part_to_dev(rdev->bdev->bd_part)->kobj;
- if ((err = sysfs_create_link(&rdev->kobj, ko, "block"))) {
- kobject_del(&rdev->kobj);
- goto fail;
- }
- rdev->sysfs_state = sysfs_get_dirent(rdev->kobj.sd, NULL, "state");
+ if (sysfs_create_link(&rdev->kobj, ko, "block"))
+ /* failure here is OK */;
+ rdev->sysfs_state = sysfs_get_dirent_safe(rdev->kobj.sd, "state");
list_add_rcu(&rdev->same_set, &mddev->disks);
bd_claim_by_disk(rdev->bdev, rdev->bdev->bd_holder, mddev->gendisk);
set_bit(In_sync, &rdev->flags);
err = 0;
}
- if (!err && rdev->sysfs_state)
- sysfs_notify_dirent(rdev->sysfs_state);
+ if (!err)
+ sysfs_notify_dirent_safe(rdev->sysfs_state);
return err ? err : len;
}
static struct rdev_sysfs_entry rdev_state =
rdev->raid_disk = -1;
return err;
} else
- sysfs_notify_dirent(rdev->sysfs_state);
+ sysfs_notify_dirent_safe(rdev->sysfs_state);
sprintf(nm, "rd%d", rdev->raid_disk);
if (sysfs_create_link(&rdev->mddev->kobj, &rdev->kobj, nm))
- printk(KERN_WARNING
- "md: cannot register "
- "%s for %s\n",
- nm, mdname(rdev->mddev));
-
+ /* failure here is OK */;
/* don't wakeup anyone, leave that to userspace. */
} else {
if (slot >= rdev->mddev->raid_disks)
clear_bit(Faulty, &rdev->flags);
clear_bit(WriteMostly, &rdev->flags);
set_bit(In_sync, &rdev->flags);
- sysfs_notify_dirent(rdev->sysfs_state);
+ sysfs_notify_dirent_safe(rdev->sysfs_state);
}
return len;
}
.default_attrs = rdev_default_attrs,
};
+void md_rdev_init(mdk_rdev_t *rdev)
+{
+ rdev->desc_nr = -1;
+ rdev->saved_raid_disk = -1;
+ rdev->raid_disk = -1;
+ rdev->flags = 0;
+ rdev->data_offset = 0;
+ rdev->sb_events = 0;
+ rdev->last_read_error.tv_sec = 0;
+ rdev->last_read_error.tv_nsec = 0;
+ atomic_set(&rdev->nr_pending, 0);
+ atomic_set(&rdev->read_errors, 0);
+ atomic_set(&rdev->corrected_errors, 0);
+
+ INIT_LIST_HEAD(&rdev->same_set);
+ init_waitqueue_head(&rdev->blocked_wait);
+}
+EXPORT_SYMBOL_GPL(md_rdev_init);
/*
* Import a device. If 'super_format' >= 0, then sanity check the superblock
*
return ERR_PTR(-ENOMEM);
}
+ md_rdev_init(rdev);
if ((err = alloc_disk_sb(rdev)))
goto abort_free;
kobject_init(&rdev->kobj, &rdev_ktype);
- rdev->desc_nr = -1;
- rdev->saved_raid_disk = -1;
- rdev->raid_disk = -1;
- rdev->flags = 0;
- rdev->data_offset = 0;
- rdev->sb_events = 0;
- rdev->last_read_error.tv_sec = 0;
- rdev->last_read_error.tv_nsec = 0;
- atomic_set(&rdev->nr_pending, 0);
- atomic_set(&rdev->read_errors, 0);
- atomic_set(&rdev->corrected_errors, 0);
-
size = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS;
if (!size) {
printk(KERN_WARNING
}
}
- INIT_LIST_HEAD(&rdev->same_set);
- init_waitqueue_head(&rdev->blocked_wait);
-
return rdev;
abort_free:
* - new personality will access other array.
*/
- if (mddev->sync_thread || mddev->reshape_position != MaxSector)
+ if (mddev->sync_thread ||
+ mddev->reshape_position != MaxSector ||
+ mddev->sysfs_active)
return -EBUSY;
if (!mddev->pers->quiesce) {
if (err)
return err;
else {
- sysfs_notify_dirent(mddev->sysfs_state);
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
return len;
}
}
}
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
md_wakeup_thread(mddev->thread);
- sysfs_notify_dirent(mddev->sysfs_action);
+ sysfs_notify_dirent_safe(mddev->sysfs_action);
return len;
}
disk->disk_name);
error = 0;
}
- if (sysfs_create_group(&mddev->kobj, &md_bitmap_group))
+ if (mddev->kobj.sd &&
+ sysfs_create_group(&mddev->kobj, &md_bitmap_group))
printk(KERN_DEBUG "pointless warning\n");
abort:
mutex_unlock(&disks_mutex);
- if (!error) {
+ if (!error && mddev->kobj.sd) {
kobject_uevent(&mddev->kobj, KOBJ_ADD);
- mddev->sysfs_state = sysfs_get_dirent(mddev->kobj.sd, NULL, "array_state");
+ mddev->sysfs_state = sysfs_get_dirent_safe(mddev->kobj.sd, "array_state");
}
mddev_put(mddev);
return error;
if (!atomic_read(&mddev->writes_pending)) {
mddev->safemode = 1;
if (mddev->external)
- sysfs_notify_dirent(mddev->sysfs_state);
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
}
md_wakeup_thread(mddev->thread);
}
static int start_dirty_degraded;
-static int md_run(mddev_t *mddev)
+int md_run(mddev_t *mddev)
{
int err;
mdk_rdev_t *rdev;
if (mddev->pers)
return -EBUSY;
-
- /* These two calls synchronise us with the
- * sysfs_remove_group calls in mddev_unlock,
- * so they must have completed.
- */
- mutex_lock(&mddev->open_mutex);
- mutex_unlock(&mddev->open_mutex);
+ /* Cannot run until previous stop completes properly */
+ if (mddev->sysfs_active)
+ return -EBUSY;
/*
* Analyze all RAID superblock(s)
return -EINVAL;
}
}
- sysfs_notify_dirent(rdev->sysfs_state);
+ sysfs_notify_dirent_safe(rdev->sysfs_state);
}
spin_lock(&pers_lock);
return err;
}
if (mddev->pers->sync_request) {
- if (sysfs_create_group(&mddev->kobj, &md_redundancy_group))
+ if (mddev->kobj.sd &&
+ sysfs_create_group(&mddev->kobj, &md_redundancy_group))
printk(KERN_WARNING
"md: cannot register extra attributes for %s\n",
mdname(mddev));
- mddev->sysfs_action = sysfs_get_dirent(mddev->kobj.sd, NULL, "sync_action");
+ mddev->sysfs_action = sysfs_get_dirent_safe(mddev->kobj.sd, "sync_action");
} else if (mddev->ro == 2) /* auto-readonly not meaningful */
mddev->ro = 0;
char nm[20];
sprintf(nm, "rd%d", rdev->raid_disk);
if (sysfs_create_link(&mddev->kobj, &rdev->kobj, nm))
- printk("md: cannot register %s for %s\n",
- nm, mdname(mddev));
+ /* failure here is OK */;
}
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
md_wakeup_thread(mddev->sync_thread); /* possibly kick off a reshape */
md_new_event(mddev);
- sysfs_notify_dirent(mddev->sysfs_state);
- if (mddev->sysfs_action)
- sysfs_notify_dirent(mddev->sysfs_action);
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
+ sysfs_notify_dirent_safe(mddev->sysfs_action);
sysfs_notify(&mddev->kobj, NULL, "degraded");
return 0;
}
+EXPORT_SYMBOL_GPL(md_run);
static int do_md_run(mddev_t *mddev)
{
err = md_run(mddev);
if (err)
goto out;
-
+ err = bitmap_load(mddev);
+ if (err) {
+ bitmap_destroy(mddev);
+ goto out;
+ }
set_capacity(mddev->gendisk, mddev->array_sectors);
revalidate_disk(mddev->gendisk);
kobject_uevent(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE);
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
md_wakeup_thread(mddev->thread);
md_wakeup_thread(mddev->sync_thread);
- sysfs_notify_dirent(mddev->sysfs_state);
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
return 0;
}
mddev->bitmap_info.chunksize = 0;
mddev->bitmap_info.daemon_sleep = 0;
mddev->bitmap_info.max_write_behind = 0;
+ mddev->plug = NULL;
}
-static void md_stop_writes(mddev_t *mddev)
+void md_stop_writes(mddev_t *mddev)
{
if (mddev->sync_thread) {
set_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
md_update_sb(mddev, 1);
}
}
+EXPORT_SYMBOL_GPL(md_stop_writes);
-static void md_stop(mddev_t *mddev)
+void md_stop(mddev_t *mddev)
{
- md_stop_writes(mddev);
-
mddev->pers->stop(mddev);
if (mddev->pers->sync_request && mddev->to_remove == NULL)
mddev->to_remove = &md_redundancy_group;
mddev->pers = NULL;
clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
}
+EXPORT_SYMBOL_GPL(md_stop);
static int md_set_readonly(mddev_t *mddev, int is_open)
{
mddev->ro = 1;
set_disk_ro(mddev->gendisk, 1);
clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
- sysfs_notify_dirent(mddev->sysfs_state);
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
err = 0;
}
out:
*/
static int do_md_stop(mddev_t * mddev, int mode, int is_open)
{
- int err = 0;
struct gendisk *disk = mddev->gendisk;
mdk_rdev_t *rdev;
mutex_lock(&mddev->open_mutex);
- if (atomic_read(&mddev->openers) > is_open) {
+ if (atomic_read(&mddev->openers) > is_open ||
+ mddev->sysfs_active) {
printk("md: %s still in use.\n",mdname(mddev));
- err = -EBUSY;
- } else if (mddev->pers) {
+ mutex_unlock(&mddev->open_mutex);
+ return -EBUSY;
+ }
+ if (mddev->pers) {
if (mddev->ro)
set_disk_ro(disk, 0);
+ md_stop_writes(mddev);
md_stop(mddev);
mddev->queue->merge_bvec_fn = NULL;
mddev->queue->unplug_fn = NULL;
mddev->queue->backing_dev_info.congested_fn = NULL;
/* tell userspace to handle 'inactive' */
- sysfs_notify_dirent(mddev->sysfs_state);
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
list_for_each_entry(rdev, &mddev->disks, same_set)
if (rdev->raid_disk >= 0) {
}
set_capacity(disk, 0);
+ mutex_unlock(&mddev->open_mutex);
revalidate_disk(disk);
if (mddev->ro)
mddev->ro = 0;
-
- err = 0;
- }
- mutex_unlock(&mddev->open_mutex);
- if (err)
- return err;
+ } else
+ mutex_unlock(&mddev->open_mutex);
/*
* Free resources if final stop
*/
if (mode == 0) {
-
printk(KERN_INFO "md: %s stopped.\n", mdname(mddev));
bitmap_destroy(mddev);
kobject_uevent(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE);
if (mddev->hold_active == UNTIL_STOP)
mddev->hold_active = 0;
-
}
- err = 0;
blk_integrity_unregister(disk);
md_new_event(mddev);
- sysfs_notify_dirent(mddev->sysfs_state);
- return err;
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
+ return 0;
}
#ifndef MODULE
if (err)
export_rdev(rdev);
else
- sysfs_notify_dirent(rdev->sysfs_state);
+ sysfs_notify_dirent_safe(rdev->sysfs_state);
md_update_sb(mddev, 1);
if (mddev->degraded)
err = 0;
if (mddev->pers) {
mddev->pers->quiesce(mddev, 1);
- if (fd >= 0)
+ if (fd >= 0) {
err = bitmap_create(mddev);
+ if (!err)
+ err = bitmap_load(mddev);
+ }
if (fd < 0 || err) {
bitmap_destroy(mddev);
fd = -1; /* make sure to put the file */
mddev->bitmap_info.default_offset;
mddev->pers->quiesce(mddev, 1);
rv = bitmap_create(mddev);
+ if (!rv)
+ rv = bitmap_load(mddev);
if (rv)
bitmap_destroy(mddev);
mddev->pers->quiesce(mddev, 0);
if (_IOC_TYPE(cmd) == MD_MAJOR && mddev->ro && mddev->pers) {
if (mddev->ro == 2) {
mddev->ro = 0;
- sysfs_notify_dirent(mddev->sysfs_state);
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
md_wakeup_thread(mddev->thread);
} else {
mddev_t *mddev = mddev_find(bdev->bd_dev);
int err;
+ lock_kernel();
if (mddev->gendisk != bdev->bd_disk) {
/* we are racing with mddev_put which is discarding this
* bd_disk.
/* Wait until bdev->bd_disk is definitely gone */
flush_scheduled_work();
/* Then retry the open from the top */
+ unlock_kernel();
return -ERESTARTSYS;
}
BUG_ON(mddev != bdev->bd_disk->private_data);
check_disk_size_change(mddev->gendisk, bdev);
out:
+ unlock_kernel();
return err;
}
mddev_t *mddev = disk->private_data;
BUG_ON(!mddev);
+ lock_kernel();
atomic_dec(&mddev->openers);
mddev_put(mddev);
+ unlock_kernel();
return 0;
}
mddev->pers->error_handler(mddev,rdev);
if (mddev->degraded)
set_bit(MD_RECOVERY_RECOVER, &mddev->recovery);
- sysfs_notify_dirent(rdev->sysfs_state);
+ sysfs_notify_dirent_safe(rdev->sysfs_state);
set_bit(MD_RECOVERY_INTR, &mddev->recovery);
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
md_wakeup_thread(mddev->thread);
+ if (mddev->event_work.func)
+ schedule_work(&mddev->event_work);
md_new_event_inintr(mddev);
}
spin_unlock_irq(&mddev->write_lock);
}
if (did_change)
- sysfs_notify_dirent(mddev->sysfs_state);
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
wait_event(mddev->sb_wait,
!test_bit(MD_CHANGE_CLEAN, &mddev->flags) &&
!test_bit(MD_CHANGE_PENDING, &mddev->flags));
mddev->safemode = 1;
spin_unlock_irq(&mddev->write_lock);
md_update_sb(mddev, 0);
- sysfs_notify_dirent(mddev->sysfs_state);
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
} else
spin_unlock_irq(&mddev->write_lock);
}
EXPORT_SYMBOL_GPL(md_allow_write);
+void md_unplug(mddev_t *mddev)
+{
+ if (mddev->queue)
+ blk_unplug(mddev->queue);
+ if (mddev->plug)
+ mddev->plug->unplug_fn(mddev->plug);
+}
+
#define SYNC_MARKS 10
#define SYNC_MARK_STEP (3*HZ)
void md_do_sync(mddev_t *mddev)
>= mddev->resync_max - mddev->curr_resync_completed
)) {
/* time to update curr_resync_completed */
- blk_unplug(mddev->queue);
+ md_unplug(mddev);
wait_event(mddev->recovery_wait,
atomic_read(&mddev->recovery_active) == 0);
mddev->curr_resync_completed =
mddev->curr_resync;
- set_bit(MD_CHANGE_CLEAN, &mddev->flags);
+ if (mddev->persistent)
+ set_bit(MD_CHANGE_CLEAN, &mddev->flags);
sysfs_notify(&mddev->kobj, NULL, "sync_completed");
}
* about not overloading the IO subsystem. (things like an
* e2fsck being done on the RAID array should execute fast)
*/
- blk_unplug(mddev->queue);
+ md_unplug(mddev);
cond_resched();
currspeed = ((unsigned long)(io_sectors-mddev->resync_mark_cnt))/2
* this also signals 'finished resyncing' to md_stop
*/
out:
- blk_unplug(mddev->queue);
+ md_unplug(mddev);
wait_event(mddev->recovery_wait, !atomic_read(&mddev->recovery_active));
sprintf(nm, "rd%d", rdev->raid_disk);
if (sysfs_create_link(&mddev->kobj,
&rdev->kobj, nm))
- printk(KERN_WARNING
- "md: cannot register "
- "%s for %s\n",
- nm, mdname(mddev));
+ /* failure here is OK */;
spares++;
md_new_event(mddev);
set_bit(MD_CHANGE_DEVS, &mddev->flags);
mddev->safemode = 0;
spin_unlock_irq(&mddev->write_lock);
if (did_change)
- sysfs_notify_dirent(mddev->sysfs_state);
+ sysfs_notify_dirent_safe(mddev->sysfs_state);
}
if (mddev->flags)
mddev->recovery = 0;
/* flag recovery needed just to double check */
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
- sysfs_notify_dirent(mddev->sysfs_action);
+ sysfs_notify_dirent_safe(mddev->sysfs_action);
md_new_event(mddev);
goto unlock;
}
mddev->recovery = 0;
} else
md_wakeup_thread(mddev->sync_thread);
- sysfs_notify_dirent(mddev->sysfs_action);
+ sysfs_notify_dirent_safe(mddev->sysfs_action);
md_new_event(mddev);
}
unlock:
if (test_and_clear_bit(MD_RECOVERY_RECOVER,
&mddev->recovery))
if (mddev->sysfs_action)
- sysfs_notify_dirent(mddev->sysfs_action);
+ sysfs_notify_dirent_safe(mddev->sysfs_action);
}
mddev_unlock(mddev);
}
void md_wait_for_blocked_rdev(mdk_rdev_t *rdev, mddev_t *mddev)
{
- sysfs_notify_dirent(rdev->sysfs_state);
+ sysfs_notify_dirent_safe(rdev->sysfs_state);
wait_event_timeout(rdev->blocked_wait,
!test_bit(Blocked, &rdev->flags),
msecs_to_jiffies(5000));
typedef struct mddev_s mddev_t;
typedef struct mdk_rdev_s mdk_rdev_t;
+/* generic plugging support - like that provided with request_queue,
+ * but does not require a request_queue
+ */
+struct plug_handle {
+ void (*unplug_fn)(struct plug_handle *);
+ struct timer_list unplug_timer;
+ struct work_struct unplug_work;
+ unsigned long unplug_flag;
+};
+#define PLUGGED_FLAG 1
+void plugger_init(struct plug_handle *plug,
+ void (*unplug_fn)(struct plug_handle *));
+void plugger_set_plug(struct plug_handle *plug);
+int plugger_remove_plug(struct plug_handle *plug);
+static inline void plugger_flush(struct plug_handle *plug)
+{
+ del_timer_sync(&plug->unplug_timer);
+ cancel_work_sync(&plug->unplug_work);
+}
+
/*
* MD's 'extended' device
*/
#define Faulty 1 /* device is known to have a fault */
#define In_sync 2 /* device is in_sync with rest of array */
#define WriteMostly 4 /* Avoid reading if at all possible */
-#define BarriersNotsupp 5 /* BIO_RW_BARRIER is not supported */
+#define BarriersNotsupp 5 /* REQ_HARDBARRIER is not supported */
#define AllReserved 6 /* If whole device is reserved for
* one array */
#define AutoDetected 7 /* added by auto-detect */
int suspended;
atomic_t active_io;
int ro;
+ int sysfs_active; /* set when sysfs deletes
+ * are happening, so run/
+ * takeover/stop are not safe
+ */
struct gendisk *gendisk;
* fails. Only supported
*/
struct bio *biolist; /* bios that need to be retried
- * because BIO_RW_BARRIER is not supported
+ * because REQ_HARDBARRIER is not supported
*/
atomic_t recovery_active; /* blocks scheduled, but not written */
* hot-adding a bitmap. It should
* eventually be settable by sysfs.
*/
+ /* When md is serving under dm, it might use a
+ * dirty_log to store the bits.
+ */
+ struct dm_dirty_log *log;
+
struct mutex mutex;
unsigned long chunksize;
- unsigned long daemon_sleep; /* how many seconds between updates? */
+ unsigned long daemon_sleep; /* how many jiffies between updates? */
unsigned long max_write_behind; /* write-behind mode */
int external;
} bitmap_info;
struct list_head all_mddevs;
struct attribute_group *to_remove;
+ struct plug_handle *plug; /* if used by personality */
+
/* Generic barrier handling.
* If there is a pending barrier request, all other
* writes are blocked while the devices are flushed.
struct bio *barrier;
atomic_t flush_pending;
struct work_struct barrier_work;
+ struct work_struct event_work; /* used by dm to report failure event */
};
};
extern struct attribute_group md_bitmap_group;
+static inline struct sysfs_dirent *sysfs_get_dirent_safe(struct sysfs_dirent *sd, char *name)
+{
+ if (sd)
+ return sysfs_get_dirent(sd, NULL, name);
+ return sd;
+}
+static inline void sysfs_notify_dirent_safe(struct sysfs_dirent *sd)
+{
+ if (sd)
+ sysfs_notify_dirent(sd);
+}
+
static inline char * mdname (mddev_t * mddev)
{
return mddev->gendisk ? mddev->gendisk->disk_name : "mdX";
extern void md_integrity_add_rdev(mdk_rdev_t *rdev, mddev_t *mddev);
extern int strict_strtoul_scaled(const char *cp, unsigned long *res, int scale);
extern void restore_bitmap_write_access(struct file *file);
+extern void md_unplug(mddev_t *mddev);
+
+extern void mddev_init(mddev_t *mddev);
+extern int md_run(mddev_t *mddev);
+extern void md_stop(mddev_t *mddev);
+extern void md_stop_writes(mddev_t *mddev);
+extern void md_rdev_init(mdk_rdev_t *rdev);
+extern void mddev_suspend(mddev_t *mddev);
+extern void mddev_resume(mddev_t *mddev);
#endif /* _MD_MD_H */
+++ /dev/null
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved
- *
- * This file is part of the Linux kernel, and is made available under
- * the terms of the GNU General Public License version 2 or (at your
- * option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * mktables.c
- *
- * Make RAID-6 tables. This is a host user space program to be run at
- * compile time.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <inttypes.h>
-#include <stdlib.h>
-#include <time.h>
-
-static uint8_t gfmul(uint8_t a, uint8_t b)
-{
- uint8_t v = 0;
-
- while (b) {
- if (b & 1)
- v ^= a;
- a = (a << 1) ^ (a & 0x80 ? 0x1d : 0);
- b >>= 1;
- }
-
- return v;
-}
-
-static uint8_t gfpow(uint8_t a, int b)
-{
- uint8_t v = 1;
-
- b %= 255;
- if (b < 0)
- b += 255;
-
- while (b) {
- if (b & 1)
- v = gfmul(v, a);
- a = gfmul(a, a);
- b >>= 1;
- }
-
- return v;
-}
-
-int main(int argc, char *argv[])
-{
- int i, j, k;
- uint8_t v;
- uint8_t exptbl[256], invtbl[256];
-
- printf("#include <linux/raid/pq.h>\n");
-
- /* Compute multiplication table */
- printf("\nconst u8 __attribute__((aligned(256)))\n"
- "raid6_gfmul[256][256] =\n"
- "{\n");
- for (i = 0; i < 256; i++) {
- printf("\t{\n");
- for (j = 0; j < 256; j += 8) {
- printf("\t\t");
- for (k = 0; k < 8; k++)
- printf("0x%02x,%c", gfmul(i, j + k),
- (k == 7) ? '\n' : ' ');
- }
- printf("\t},\n");
- }
- printf("};\n");
- printf("#ifdef __KERNEL__\n");
- printf("EXPORT_SYMBOL(raid6_gfmul);\n");
- printf("#endif\n");
-
- /* Compute power-of-2 table (exponent) */
- v = 1;
- printf("\nconst u8 __attribute__((aligned(256)))\n"
- "raid6_gfexp[256] =\n" "{\n");
- for (i = 0; i < 256; i += 8) {
- printf("\t");
- for (j = 0; j < 8; j++) {
- exptbl[i + j] = v;
- printf("0x%02x,%c", v, (j == 7) ? '\n' : ' ');
- v = gfmul(v, 2);
- if (v == 1)
- v = 0; /* For entry 255, not a real entry */
- }
- }
- printf("};\n");
- printf("#ifdef __KERNEL__\n");
- printf("EXPORT_SYMBOL(raid6_gfexp);\n");
- printf("#endif\n");
-
- /* Compute inverse table x^-1 == x^254 */
- printf("\nconst u8 __attribute__((aligned(256)))\n"
- "raid6_gfinv[256] =\n" "{\n");
- for (i = 0; i < 256; i += 8) {
- printf("\t");
- for (j = 0; j < 8; j++) {
- invtbl[i + j] = v = gfpow(i + j, 254);
- printf("0x%02x,%c", v, (j == 7) ? '\n' : ' ');
- }
- }
- printf("};\n");
- printf("#ifdef __KERNEL__\n");
- printf("EXPORT_SYMBOL(raid6_gfinv);\n");
- printf("#endif\n");
-
- /* Compute inv(2^x + 1) (exponent-xor-inverse) table */
- printf("\nconst u8 __attribute__((aligned(256)))\n"
- "raid6_gfexi[256] =\n" "{\n");
- for (i = 0; i < 256; i += 8) {
- printf("\t");
- for (j = 0; j < 8; j++)
- printf("0x%02x,%c", invtbl[exptbl[i + j] ^ 1],
- (j == 7) ? '\n' : ' ');
- }
- printf("};\n");
- printf("#ifdef __KERNEL__\n");
- printf("EXPORT_SYMBOL(raid6_gfexi);\n");
- printf("#endif\n");
-
- return 0;
-}
if (uptodate)
multipath_end_bh_io(mp_bh, 0);
- else if (!bio_rw_flagged(bio, BIO_RW_AHEAD)) {
+ else if (!(bio->bi_rw & REQ_RAHEAD)) {
/*
* oops, IO error:
*/
struct multipath_bh * mp_bh;
struct multipath_info *multipath;
- if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
+ if (unlikely(bio->bi_rw & REQ_HARDBARRIER)) {
md_barrier_request(mddev, bio);
return 0;
}
mp_bh->bio = *bio;
mp_bh->bio.bi_sector += multipath->rdev->data_offset;
mp_bh->bio.bi_bdev = multipath->rdev->bdev;
- mp_bh->bio.bi_rw |= (1 << BIO_RW_FAILFAST_TRANSPORT);
+ mp_bh->bio.bi_rw |= REQ_FAILFAST_TRANSPORT;
mp_bh->bio.bi_end_io = multipath_end_request;
mp_bh->bio.bi_private = mp_bh;
generic_make_request(&mp_bh->bio);
*bio = *(mp_bh->master_bio);
bio->bi_sector += conf->multipaths[mp_bh->path].rdev->data_offset;
bio->bi_bdev = conf->multipaths[mp_bh->path].rdev->bdev;
- bio->bi_rw |= (1 << BIO_RW_FAILFAST_TRANSPORT);
+ bio->bi_rw |= REQ_FAILFAST_TRANSPORT;
bio->bi_end_io = multipath_end_request;
bio->bi_private = mp_bh;
generic_make_request(bio);
struct strip_zone *zone;
mdk_rdev_t *tmp_dev;
- if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
+ if (unlikely(bio->bi_rw & REQ_HARDBARRIER)) {
md_barrier_request(mddev, bio);
return 0;
}
struct bio_list bl;
struct page **behind_pages = NULL;
const int rw = bio_data_dir(bio);
- const bool do_sync = bio_rw_flagged(bio, BIO_RW_SYNCIO);
+ const bool do_sync = (bio->bi_rw & REQ_SYNC);
bool do_barriers;
mdk_rdev_t *blocked_rdev;
finish_wait(&conf->wait_barrier, &w);
}
if (unlikely(!mddev->barriers_work &&
- bio_rw_flagged(bio, BIO_RW_BARRIER))) {
+ (bio->bi_rw & REQ_HARDBARRIER))) {
if (rw == WRITE)
md_write_end(mddev);
bio_endio(bio, -EOPNOTSUPP);
read_bio->bi_sector = r1_bio->sector + mirror->rdev->data_offset;
read_bio->bi_bdev = mirror->rdev->bdev;
read_bio->bi_end_io = raid1_end_read_request;
- read_bio->bi_rw = READ | (do_sync << BIO_RW_SYNCIO);
+ read_bio->bi_rw = READ | do_sync;
read_bio->bi_private = r1_bio;
generic_make_request(read_bio);
atomic_set(&r1_bio->remaining, 0);
atomic_set(&r1_bio->behind_remaining, 0);
- do_barriers = bio_rw_flagged(bio, BIO_RW_BARRIER);
+ do_barriers = bio->bi_rw & REQ_HARDBARRIER;
if (do_barriers)
set_bit(R1BIO_Barrier, &r1_bio->state);
mbio->bi_sector = r1_bio->sector + conf->mirrors[i].rdev->data_offset;
mbio->bi_bdev = conf->mirrors[i].rdev->bdev;
mbio->bi_end_io = raid1_end_write_request;
- mbio->bi_rw = WRITE | (do_barriers << BIO_RW_BARRIER) |
- (do_sync << BIO_RW_SYNCIO);
+ mbio->bi_rw = WRITE | do_barriers | do_sync;
mbio->bi_private = r1_bio;
if (behind_pages) {
sync_request_write(mddev, r1_bio);
unplug = 1;
} else if (test_bit(R1BIO_BarrierRetry, &r1_bio->state)) {
- /* some requests in the r1bio were BIO_RW_BARRIER
+ /* some requests in the r1bio were REQ_HARDBARRIER
* requests which failed with -EOPNOTSUPP. Hohumm..
* Better resubmit without the barrier.
* We know which devices to resubmit for, because
* We already have a nr_pending reference on these rdevs.
*/
int i;
- const bool do_sync = bio_rw_flagged(r1_bio->master_bio, BIO_RW_SYNCIO);
+ const bool do_sync = (r1_bio->master_bio->bi_rw & REQ_SYNC);
clear_bit(R1BIO_BarrierRetry, &r1_bio->state);
clear_bit(R1BIO_Barrier, &r1_bio->state);
for (i=0; i < conf->raid_disks; i++)
conf->mirrors[i].rdev->data_offset;
bio->bi_bdev = conf->mirrors[i].rdev->bdev;
bio->bi_end_io = raid1_end_write_request;
- bio->bi_rw = WRITE |
- (do_sync << BIO_RW_SYNCIO);
+ bio->bi_rw = WRITE | do_sync;
bio->bi_private = r1_bio;
r1_bio->bios[i] = bio;
generic_make_request(bio);
(unsigned long long)r1_bio->sector);
raid_end_bio_io(r1_bio);
} else {
- const bool do_sync = bio_rw_flagged(r1_bio->master_bio, BIO_RW_SYNCIO);
+ const bool do_sync = r1_bio->master_bio->bi_rw & REQ_SYNC;
r1_bio->bios[r1_bio->read_disk] =
mddev->ro ? IO_BLOCKED : NULL;
r1_bio->read_disk = disk;
bio->bi_sector = r1_bio->sector + rdev->data_offset;
bio->bi_bdev = rdev->bdev;
bio->bi_end_io = raid1_end_read_request;
- bio->bi_rw = READ | (do_sync << BIO_RW_SYNCIO);
+ bio->bi_rw = READ | do_sync;
bio->bi_private = r1_bio;
unplug = 1;
generic_make_request(bio);
int i;
int chunk_sects = conf->chunk_mask + 1;
const int rw = bio_data_dir(bio);
- const bool do_sync = bio_rw_flagged(bio, BIO_RW_SYNCIO);
+ const bool do_sync = (bio->bi_rw & REQ_SYNC);
struct bio_list bl;
unsigned long flags;
mdk_rdev_t *blocked_rdev;
- if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
+ if (unlikely(bio->bi_rw & REQ_HARDBARRIER)) {
md_barrier_request(mddev, bio);
return 0;
}
*/
bp = bio_split(bio,
chunk_sects - (bio->bi_sector & (chunk_sects - 1)) );
+
+ /* Each of these 'make_request' calls will call 'wait_barrier'.
+ * If the first succeeds but the second blocks due to the resync
+ * thread raising the barrier, we will deadlock because the
+ * IO to the underlying device will be queued in generic_make_request
+ * and will never complete, so will never reduce nr_pending.
+ * So increment nr_waiting here so no new raise_barriers will
+ * succeed, and so the second wait_barrier cannot block.
+ */
+ spin_lock_irq(&conf->resync_lock);
+ conf->nr_waiting++;
+ spin_unlock_irq(&conf->resync_lock);
+
if (make_request(mddev, &bp->bio1))
generic_make_request(&bp->bio1);
if (make_request(mddev, &bp->bio2))
generic_make_request(&bp->bio2);
+ spin_lock_irq(&conf->resync_lock);
+ conf->nr_waiting--;
+ wake_up(&conf->wait_barrier);
+ spin_unlock_irq(&conf->resync_lock);
+
bio_pair_release(bp);
return 0;
bad_map:
mirror->rdev->data_offset;
read_bio->bi_bdev = mirror->rdev->bdev;
read_bio->bi_end_io = raid10_end_read_request;
- read_bio->bi_rw = READ | (do_sync << BIO_RW_SYNCIO);
+ read_bio->bi_rw = READ | do_sync;
read_bio->bi_private = r10_bio;
generic_make_request(read_bio);
conf->mirrors[d].rdev->data_offset;
mbio->bi_bdev = conf->mirrors[d].rdev->bdev;
mbio->bi_end_io = raid10_end_write_request;
- mbio->bi_rw = WRITE | (do_sync << BIO_RW_SYNCIO);
+ mbio->bi_rw = WRITE | do_sync;
mbio->bi_private = r10_bio;
atomic_inc(&r10_bio->remaining);
raid_end_bio_io(r10_bio);
bio_put(bio);
} else {
- const bool do_sync = bio_rw_flagged(r10_bio->master_bio, BIO_RW_SYNCIO);
+ const bool do_sync = (r10_bio->master_bio->bi_rw & REQ_SYNC);
bio_put(bio);
rdev = conf->mirrors[mirror].rdev;
if (printk_ratelimit())
bio->bi_sector = r10_bio->devs[r10_bio->read_slot].addr
+ rdev->data_offset;
bio->bi_bdev = rdev->bdev;
- bio->bi_rw = READ | (do_sync << BIO_RW_SYNCIO);
+ bio->bi_rw = READ | do_sync;
bio->bi_private = r10_bio;
bio->bi_end_io = raid10_end_read_request;
unplug = 1;
if (test_bit(STRIPE_HANDLE, &sh->state)) {
if (test_bit(STRIPE_DELAYED, &sh->state)) {
list_add_tail(&sh->lru, &conf->delayed_list);
- blk_plug_device(conf->mddev->queue);
+ plugger_set_plug(&conf->plug);
} else if (test_bit(STRIPE_BIT_DELAY, &sh->state) &&
sh->bm_seq - conf->seq_write > 0) {
list_add_tail(&sh->lru, &conf->bitmap_list);
- blk_plug_device(conf->mddev->queue);
+ plugger_set_plug(&conf->plug);
} else {
clear_bit(STRIPE_BIT_DELAY, &sh->state);
list_add_tail(&sh->lru, &conf->handle_list);
}
static void unplug_slaves(mddev_t *mddev);
-static void raid5_unplug_device(struct request_queue *q);
static struct stripe_head *
get_active_stripe(raid5_conf_t *conf, sector_t sector,
< (conf->max_nr_stripes *3/4)
|| !conf->inactive_blocked),
conf->device_lock,
- raid5_unplug_device(conf->mddev->queue)
+ md_raid5_unplug_device(conf)
);
conf->inactive_blocked = 0;
} else
struct kmem_cache *sc;
int devs = max(conf->raid_disks, conf->previous_raid_disks);
- sprintf(conf->cache_name[0],
- "raid%d-%s", conf->level, mdname(conf->mddev));
- sprintf(conf->cache_name[1],
- "raid%d-%s-alt", conf->level, mdname(conf->mddev));
+ if (conf->mddev->gendisk)
+ sprintf(conf->cache_name[0],
+ "raid%d-%s", conf->level, mdname(conf->mddev));
+ else
+ sprintf(conf->cache_name[0],
+ "raid%d-%p", conf->level, conf->mddev);
+ sprintf(conf->cache_name[1], "%s-alt", conf->cache_name[0]);
+
conf->active_name = 0;
sc = kmem_cache_create(conf->cache_name[conf->active_name],
sizeof(struct stripe_head)+(devs-1)*sizeof(struct r5dev),
list_add_tail(&sh->lru, &conf->hold_list);
}
} else
- blk_plug_device(conf->mddev->queue);
+ plugger_set_plug(&conf->plug);
}
static void activate_bit_delay(raid5_conf_t *conf)
rcu_read_unlock();
}
-static void raid5_unplug_device(struct request_queue *q)
+void md_raid5_unplug_device(raid5_conf_t *conf)
{
- mddev_t *mddev = q->queuedata;
- raid5_conf_t *conf = mddev->private;
unsigned long flags;
spin_lock_irqsave(&conf->device_lock, flags);
- if (blk_remove_plug(q)) {
+ if (plugger_remove_plug(&conf->plug)) {
conf->seq_flush++;
raid5_activate_delayed(conf);
}
- md_wakeup_thread(mddev->thread);
+ md_wakeup_thread(conf->mddev->thread);
spin_unlock_irqrestore(&conf->device_lock, flags);
- unplug_slaves(mddev);
+ unplug_slaves(conf->mddev);
}
+EXPORT_SYMBOL_GPL(md_raid5_unplug_device);
-static int raid5_congested(void *data, int bits)
+static void raid5_unplug(struct plug_handle *plug)
+{
+ raid5_conf_t *conf = container_of(plug, raid5_conf_t, plug);
+ md_raid5_unplug_device(conf);
+}
+
+static void raid5_unplug_queue(struct request_queue *q)
+{
+ mddev_t *mddev = q->queuedata;
+ md_raid5_unplug_device(mddev->private);
+}
+
+int md_raid5_congested(mddev_t *mddev, int bits)
{
- mddev_t *mddev = data;
raid5_conf_t *conf = mddev->private;
/* No difference between reads and writes. Just check
* how busy the stripe_cache is
*/
- if (mddev_congested(mddev, bits))
- return 1;
if (conf->inactive_blocked)
return 1;
if (conf->quiesce)
return 0;
}
+EXPORT_SYMBOL_GPL(md_raid5_congested);
+
+static int raid5_congested(void *data, int bits)
+{
+ mddev_t *mddev = data;
+
+ return mddev_congested(mddev, bits) ||
+ md_raid5_congested(mddev, bits);
+}
/* We want read requests to align with chunks where possible,
* but write requests don't need to.
const int rw = bio_data_dir(bi);
int remaining;
- if (unlikely(bio_rw_flagged(bi, BIO_RW_BARRIER))) {
+ if (unlikely(bi->bi_rw & REQ_HARDBARRIER)) {
/* Drain all pending writes. We only really need
* to ensure they have been submitted, but this is
* easier.
* add failed due to overlap. Flush everything
* and wait a while
*/
- raid5_unplug_device(mddev->queue);
+ md_raid5_unplug_device(conf);
release_stripe(sh);
schedule();
goto retry;
return 0;
}
-static ssize_t
-raid5_store_stripe_cache_size(mddev_t *mddev, const char *page, size_t len)
+int
+raid5_set_cache_size(mddev_t *mddev, int size)
{
raid5_conf_t *conf = mddev->private;
- unsigned long new;
int err;
- if (len >= PAGE_SIZE)
+ if (size <= 16 || size > 32768)
return -EINVAL;
- if (!conf)
- return -ENODEV;
-
- if (strict_strtoul(page, 10, &new))
- return -EINVAL;
- if (new <= 16 || new > 32768)
- return -EINVAL;
- while (new < conf->max_nr_stripes) {
+ while (size < conf->max_nr_stripes) {
if (drop_one_stripe(conf))
conf->max_nr_stripes--;
else
err = md_allow_write(mddev);
if (err)
return err;
- while (new > conf->max_nr_stripes) {
+ while (size > conf->max_nr_stripes) {
if (grow_one_stripe(conf))
conf->max_nr_stripes++;
else break;
}
+ return 0;
+}
+EXPORT_SYMBOL(raid5_set_cache_size);
+
+static ssize_t
+raid5_store_stripe_cache_size(mddev_t *mddev, const char *page, size_t len)
+{
+ raid5_conf_t *conf = mddev->private;
+ unsigned long new;
+ int err;
+
+ if (len >= PAGE_SIZE)
+ return -EINVAL;
+ if (!conf)
+ return -ENODEV;
+
+ if (strict_strtoul(page, 10, &new))
+ return -EINVAL;
+ err = raid5_set_cache_size(mddev, new);
+ if (err)
+ return err;
return len;
}
static int run(mddev_t *mddev)
{
raid5_conf_t *conf;
- int working_disks = 0, chunk_size;
+ int working_disks = 0;
int dirty_parity_disks = 0;
mdk_rdev_t *rdev;
sector_t reshape_offset = 0;
"reshape");
}
- /* read-ahead size must cover two whole stripes, which is
- * 2 * (datadisks) * chunksize where 'n' is the number of raid devices
- */
- {
- int data_disks = conf->previous_raid_disks - conf->max_degraded;
- int stripe = data_disks *
- ((mddev->chunk_sectors << 9) / PAGE_SIZE);
- if (mddev->queue->backing_dev_info.ra_pages < 2 * stripe)
- mddev->queue->backing_dev_info.ra_pages = 2 * stripe;
- }
/* Ok, everything is just fine now */
if (mddev->to_remove == &raid5_attrs_group)
mddev->to_remove = NULL;
- else if (sysfs_create_group(&mddev->kobj, &raid5_attrs_group))
+ else if (mddev->kobj.sd &&
+ sysfs_create_group(&mddev->kobj, &raid5_attrs_group))
printk(KERN_WARNING
- "md/raid:%s: failed to create sysfs attributes.\n",
+ "raid5: failed to create sysfs attributes for %s\n",
mdname(mddev));
+ md_set_array_sectors(mddev, raid5_size(mddev, 0, 0));
- mddev->queue->queue_lock = &conf->device_lock;
+ plugger_init(&conf->plug, raid5_unplug);
+ mddev->plug = &conf->plug;
+ if (mddev->queue) {
+ int chunk_size;
+ /* read-ahead size must cover two whole stripes, which
+ * is 2 * (datadisks) * chunksize where 'n' is the
+ * number of raid devices
+ */
+ int data_disks = conf->previous_raid_disks - conf->max_degraded;
+ int stripe = data_disks *
+ ((mddev->chunk_sectors << 9) / PAGE_SIZE);
+ if (mddev->queue->backing_dev_info.ra_pages < 2 * stripe)
+ mddev->queue->backing_dev_info.ra_pages = 2 * stripe;
- mddev->queue->unplug_fn = raid5_unplug_device;
- mddev->queue->backing_dev_info.congested_data = mddev;
- mddev->queue->backing_dev_info.congested_fn = raid5_congested;
+ blk_queue_merge_bvec(mddev->queue, raid5_mergeable_bvec);
- md_set_array_sectors(mddev, raid5_size(mddev, 0, 0));
+ mddev->queue->backing_dev_info.congested_data = mddev;
+ mddev->queue->backing_dev_info.congested_fn = raid5_congested;
+ mddev->queue->queue_lock = &conf->device_lock;
+ mddev->queue->unplug_fn = raid5_unplug_queue;
- blk_queue_merge_bvec(mddev->queue, raid5_mergeable_bvec);
- chunk_size = mddev->chunk_sectors << 9;
- blk_queue_io_min(mddev->queue, chunk_size);
- blk_queue_io_opt(mddev->queue, chunk_size *
- (conf->raid_disks - conf->max_degraded));
+ chunk_size = mddev->chunk_sectors << 9;
+ blk_queue_io_min(mddev->queue, chunk_size);
+ blk_queue_io_opt(mddev->queue, chunk_size *
+ (conf->raid_disks - conf->max_degraded));
- list_for_each_entry(rdev, &mddev->disks, same_set)
- disk_stack_limits(mddev->gendisk, rdev->bdev,
- rdev->data_offset << 9);
+ list_for_each_entry(rdev, &mddev->disks, same_set)
+ disk_stack_limits(mddev->gendisk, rdev->bdev,
+ rdev->data_offset << 9);
+ }
return 0;
abort:
md_unregister_thread(mddev->thread);
mddev->thread = NULL;
- mddev->queue->backing_dev_info.congested_fn = NULL;
- blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
+ if (mddev->queue)
+ mddev->queue->backing_dev_info.congested_fn = NULL;
+ plugger_flush(&conf->plug); /* the unplug fn references 'conf'*/
free_conf(conf);
mddev->private = NULL;
mddev->to_remove = &raid5_attrs_group;
sprintf(nm, "rd%d", rdev->raid_disk);
if (sysfs_create_link(&mddev->kobj,
&rdev->kobj, nm))
- printk(KERN_WARNING
- "md/raid:%s: failed to create "
- " link %s\n",
- mdname(mddev), nm);
+ /* Failure here is OK */;
} else
break;
}
/* read-ahead size must cover two whole stripes, which is
* 2 * (datadisks) * chunksize where 'n' is the number of raid devices
*/
- {
+ if (conf->mddev->queue) {
int data_disks = conf->raid_disks - conf->max_degraded;
int stripe = data_disks * ((conf->chunk_sectors << 9)
/ PAGE_SIZE);
* two caches.
*/
int active_name;
- char cache_name[2][20];
+ char cache_name[2][32];
struct kmem_cache *slab_cache; /* for allocating stripes */
int seq_flush, seq_write;
* (fresh device added).
* Cleared when a sync completes.
*/
+
+ struct plug_handle plug;
+
/* per cpu variables */
struct raid5_percpu {
struct page *spare_page; /* Used when checking P/Q in raid6 */
{
return layout >= 8 && layout <= 10;
}
+
+extern int md_raid5_congested(mddev_t *mddev, int bits);
+extern void md_raid5_unplug_device(raid5_conf_t *conf);
+extern int raid5_set_cache_size(mddev_t *mddev, int size);
#endif
+++ /dev/null
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002 H. Peter Anvin - All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- * Boston MA 02111-1307, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * raid6algos.c
- *
- * Algorithm list and algorithm selection for RAID-6
- */
-
-#include <linux/raid/pq.h>
-#include <linux/gfp.h>
-#ifndef __KERNEL__
-#include <sys/mman.h>
-#include <stdio.h>
-#else
-#if !RAID6_USE_EMPTY_ZERO_PAGE
-/* In .bss so it's zeroed */
-const char raid6_empty_zero_page[PAGE_SIZE] __attribute__((aligned(256)));
-EXPORT_SYMBOL(raid6_empty_zero_page);
-#endif
-#endif
-
-struct raid6_calls raid6_call;
-EXPORT_SYMBOL_GPL(raid6_call);
-
-const struct raid6_calls * const raid6_algos[] = {
- &raid6_intx1,
- &raid6_intx2,
- &raid6_intx4,
- &raid6_intx8,
-#if defined(__ia64__)
- &raid6_intx16,
- &raid6_intx32,
-#endif
-#if defined(__i386__) && !defined(__arch_um__)
- &raid6_mmxx1,
- &raid6_mmxx2,
- &raid6_sse1x1,
- &raid6_sse1x2,
- &raid6_sse2x1,
- &raid6_sse2x2,
-#endif
-#if defined(__x86_64__) && !defined(__arch_um__)
- &raid6_sse2x1,
- &raid6_sse2x2,
- &raid6_sse2x4,
-#endif
-#ifdef CONFIG_ALTIVEC
- &raid6_altivec1,
- &raid6_altivec2,
- &raid6_altivec4,
- &raid6_altivec8,
-#endif
- NULL
-};
-
-#ifdef __KERNEL__
-#define RAID6_TIME_JIFFIES_LG2 4
-#else
-/* Need more time to be stable in userspace */
-#define RAID6_TIME_JIFFIES_LG2 9
-#define time_before(x, y) ((x) < (y))
-#endif
-
-/* Try to pick the best algorithm */
-/* This code uses the gfmul table as convenient data set to abuse */
-
-int __init raid6_select_algo(void)
-{
- const struct raid6_calls * const * algo;
- const struct raid6_calls * best;
- char *syndromes;
- void *dptrs[(65536/PAGE_SIZE)+2];
- int i, disks;
- unsigned long perf, bestperf;
- int bestprefer;
- unsigned long j0, j1;
-
- disks = (65536/PAGE_SIZE)+2;
- for ( i = 0 ; i < disks-2 ; i++ ) {
- dptrs[i] = ((char *)raid6_gfmul) + PAGE_SIZE*i;
- }
-
- /* Normal code - use a 2-page allocation to avoid D$ conflict */
- syndromes = (void *) __get_free_pages(GFP_KERNEL, 1);
-
- if ( !syndromes ) {
- printk("raid6: Yikes! No memory available.\n");
- return -ENOMEM;
- }
-
- dptrs[disks-2] = syndromes;
- dptrs[disks-1] = syndromes + PAGE_SIZE;
-
- bestperf = 0; bestprefer = 0; best = NULL;
-
- for ( algo = raid6_algos ; *algo ; algo++ ) {
- if ( !(*algo)->valid || (*algo)->valid() ) {
- perf = 0;
-
- preempt_disable();
- j0 = jiffies;
- while ( (j1 = jiffies) == j0 )
- cpu_relax();
- while (time_before(jiffies,
- j1 + (1<<RAID6_TIME_JIFFIES_LG2))) {
- (*algo)->gen_syndrome(disks, PAGE_SIZE, dptrs);
- perf++;
- }
- preempt_enable();
-
- if ( (*algo)->prefer > bestprefer ||
- ((*algo)->prefer == bestprefer &&
- perf > bestperf) ) {
- best = *algo;
- bestprefer = best->prefer;
- bestperf = perf;
- }
- printk("raid6: %-8s %5ld MB/s\n", (*algo)->name,
- (perf*HZ) >> (20-16+RAID6_TIME_JIFFIES_LG2));
- }
- }
-
- if (best) {
- printk("raid6: using algorithm %s (%ld MB/s)\n",
- best->name,
- (bestperf*HZ) >> (20-16+RAID6_TIME_JIFFIES_LG2));
- raid6_call = *best;
- } else
- printk("raid6: Yikes! No algorithm found!\n");
-
- free_pages((unsigned long)syndromes, 1);
-
- return best ? 0 : -EINVAL;
-}
-
-static void raid6_exit(void)
-{
- do { } while (0);
-}
-
-subsys_initcall(raid6_select_algo);
-module_exit(raid6_exit);
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("RAID6 Q-syndrome calculations");
+++ /dev/null
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002-2004 H. Peter Anvin - All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- * Boston MA 02111-1307, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * raid6altivec$#.c
- *
- * $#-way unrolled portable integer math RAID-6 instruction set
- *
- * This file is postprocessed using unroll.awk
- *
- * <benh> hpa: in process,
- * you can just "steal" the vec unit with enable_kernel_altivec() (but
- * bracked this with preempt_disable/enable or in a lock)
- */
-
-#include <linux/raid/pq.h>
-
-#ifdef CONFIG_ALTIVEC
-
-#include <altivec.h>
-#ifdef __KERNEL__
-# include <asm/system.h>
-# include <asm/cputable.h>
-#endif
-
-/*
- * This is the C data type to use. We use a vector of
- * signed char so vec_cmpgt() will generate the right
- * instruction.
- */
-
-typedef vector signed char unative_t;
-
-#define NBYTES(x) ((vector signed char) {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x})
-#define NSIZE sizeof(unative_t)
-
-/*
- * The SHLBYTE() operation shifts each byte left by 1, *not*
- * rolling over into the next byte
- */
-static inline __attribute_const__ unative_t SHLBYTE(unative_t v)
-{
- return vec_add(v,v);
-}
-
-/*
- * The MASK() operation returns 0xFF in any byte for which the high
- * bit is 1, 0x00 for any byte for which the high bit is 0.
- */
-static inline __attribute_const__ unative_t MASK(unative_t v)
-{
- unative_t zv = NBYTES(0);
-
- /* vec_cmpgt returns a vector bool char; thus the need for the cast */
- return (unative_t)vec_cmpgt(zv, v);
-}
-
-
-/* This is noinline to make damned sure that gcc doesn't move any of the
- Altivec code around the enable/disable code */
-static void noinline
-raid6_altivec$#_gen_syndrome_real(int disks, size_t bytes, void **ptrs)
-{
- u8 **dptr = (u8 **)ptrs;
- u8 *p, *q;
- int d, z, z0;
-
- unative_t wd$$, wq$$, wp$$, w1$$, w2$$;
- unative_t x1d = NBYTES(0x1d);
-
- z0 = disks - 3; /* Highest data disk */
- p = dptr[z0+1]; /* XOR parity */
- q = dptr[z0+2]; /* RS syndrome */
-
- for ( d = 0 ; d < bytes ; d += NSIZE*$# ) {
- wq$$ = wp$$ = *(unative_t *)&dptr[z0][d+$$*NSIZE];
- for ( z = z0-1 ; z >= 0 ; z-- ) {
- wd$$ = *(unative_t *)&dptr[z][d+$$*NSIZE];
- wp$$ = vec_xor(wp$$, wd$$);
- w2$$ = MASK(wq$$);
- w1$$ = SHLBYTE(wq$$);
- w2$$ = vec_and(w2$$, x1d);
- w1$$ = vec_xor(w1$$, w2$$);
- wq$$ = vec_xor(w1$$, wd$$);
- }
- *(unative_t *)&p[d+NSIZE*$$] = wp$$;
- *(unative_t *)&q[d+NSIZE*$$] = wq$$;
- }
-}
-
-static void raid6_altivec$#_gen_syndrome(int disks, size_t bytes, void **ptrs)
-{
- preempt_disable();
- enable_kernel_altivec();
-
- raid6_altivec$#_gen_syndrome_real(disks, bytes, ptrs);
-
- preempt_enable();
-}
-
-int raid6_have_altivec(void);
-#if $# == 1
-int raid6_have_altivec(void)
-{
- /* This assumes either all CPUs have Altivec or none does */
-# ifdef __KERNEL__
- return cpu_has_feature(CPU_FTR_ALTIVEC);
-# else
- return 1;
-# endif
-}
-#endif
-
-const struct raid6_calls raid6_altivec$# = {
- raid6_altivec$#_gen_syndrome,
- raid6_have_altivec,
- "altivecx$#",
- 0
-};
-
-#endif /* CONFIG_ALTIVEC */
+++ /dev/null
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002-2004 H. Peter Anvin - All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- * Boston MA 02111-1307, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * raid6int$#.c
- *
- * $#-way unrolled portable integer math RAID-6 instruction set
- *
- * This file is postprocessed using unroll.awk
- */
-
-#include <linux/raid/pq.h>
-
-/*
- * This is the C data type to use
- */
-
-/* Change this from BITS_PER_LONG if there is something better... */
-#if BITS_PER_LONG == 64
-# define NBYTES(x) ((x) * 0x0101010101010101UL)
-# define NSIZE 8
-# define NSHIFT 3
-# define NSTRING "64"
-typedef u64 unative_t;
-#else
-# define NBYTES(x) ((x) * 0x01010101U)
-# define NSIZE 4
-# define NSHIFT 2
-# define NSTRING "32"
-typedef u32 unative_t;
-#endif
-
-
-
-/*
- * IA-64 wants insane amounts of unrolling. On other architectures that
- * is just a waste of space.
- */
-#if ($# <= 8) || defined(__ia64__)
-
-
-/*
- * These sub-operations are separate inlines since they can sometimes be
- * specially optimized using architecture-specific hacks.
- */
-
-/*
- * The SHLBYTE() operation shifts each byte left by 1, *not*
- * rolling over into the next byte
- */
-static inline __attribute_const__ unative_t SHLBYTE(unative_t v)
-{
- unative_t vv;
-
- vv = (v << 1) & NBYTES(0xfe);
- return vv;
-}
-
-/*
- * The MASK() operation returns 0xFF in any byte for which the high
- * bit is 1, 0x00 for any byte for which the high bit is 0.
- */
-static inline __attribute_const__ unative_t MASK(unative_t v)
-{
- unative_t vv;
-
- vv = v & NBYTES(0x80);
- vv = (vv << 1) - (vv >> 7); /* Overflow on the top bit is OK */
- return vv;
-}
-
-
-static void raid6_int$#_gen_syndrome(int disks, size_t bytes, void **ptrs)
-{
- u8 **dptr = (u8 **)ptrs;
- u8 *p, *q;
- int d, z, z0;
-
- unative_t wd$$, wq$$, wp$$, w1$$, w2$$;
-
- z0 = disks - 3; /* Highest data disk */
- p = dptr[z0+1]; /* XOR parity */
- q = dptr[z0+2]; /* RS syndrome */
-
- for ( d = 0 ; d < bytes ; d += NSIZE*$# ) {
- wq$$ = wp$$ = *(unative_t *)&dptr[z0][d+$$*NSIZE];
- for ( z = z0-1 ; z >= 0 ; z-- ) {
- wd$$ = *(unative_t *)&dptr[z][d+$$*NSIZE];
- wp$$ ^= wd$$;
- w2$$ = MASK(wq$$);
- w1$$ = SHLBYTE(wq$$);
- w2$$ &= NBYTES(0x1d);
- w1$$ ^= w2$$;
- wq$$ = w1$$ ^ wd$$;
- }
- *(unative_t *)&p[d+NSIZE*$$] = wp$$;
- *(unative_t *)&q[d+NSIZE*$$] = wq$$;
- }
-}
-
-const struct raid6_calls raid6_intx$# = {
- raid6_int$#_gen_syndrome,
- NULL, /* always valid */
- "int" NSTRING "x$#",
- 0
-};
-
-#endif
+++ /dev/null
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002 H. Peter Anvin - All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- * Boston MA 02111-1307, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * raid6mmx.c
- *
- * MMX implementation of RAID-6 syndrome functions
- */
-
-#if defined(__i386__) && !defined(__arch_um__)
-
-#include <linux/raid/pq.h>
-#include "raid6x86.h"
-
-/* Shared with raid6sse1.c */
-const struct raid6_mmx_constants {
- u64 x1d;
-} raid6_mmx_constants = {
- 0x1d1d1d1d1d1d1d1dULL,
-};
-
-static int raid6_have_mmx(void)
-{
- /* Not really "boot_cpu" but "all_cpus" */
- return boot_cpu_has(X86_FEATURE_MMX);
-}
-
-/*
- * Plain MMX implementation
- */
-static void raid6_mmx1_gen_syndrome(int disks, size_t bytes, void **ptrs)
-{
- u8 **dptr = (u8 **)ptrs;
- u8 *p, *q;
- int d, z, z0;
-
- z0 = disks - 3; /* Highest data disk */
- p = dptr[z0+1]; /* XOR parity */
- q = dptr[z0+2]; /* RS syndrome */
-
- kernel_fpu_begin();
-
- asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d));
- asm volatile("pxor %mm5,%mm5"); /* Zero temp */
-
- for ( d = 0 ; d < bytes ; d += 8 ) {
- asm volatile("movq %0,%%mm2" : : "m" (dptr[z0][d])); /* P[0] */
- asm volatile("movq %mm2,%mm4"); /* Q[0] */
- for ( z = z0-1 ; z >= 0 ; z-- ) {
- asm volatile("movq %0,%%mm6" : : "m" (dptr[z][d]));
- asm volatile("pcmpgtb %mm4,%mm5");
- asm volatile("paddb %mm4,%mm4");
- asm volatile("pand %mm0,%mm5");
- asm volatile("pxor %mm5,%mm4");
- asm volatile("pxor %mm5,%mm5");
- asm volatile("pxor %mm6,%mm2");
- asm volatile("pxor %mm6,%mm4");
- }
- asm volatile("movq %%mm2,%0" : "=m" (p[d]));
- asm volatile("pxor %mm2,%mm2");
- asm volatile("movq %%mm4,%0" : "=m" (q[d]));
- asm volatile("pxor %mm4,%mm4");
- }
-
- kernel_fpu_end();
-}
-
-const struct raid6_calls raid6_mmxx1 = {
- raid6_mmx1_gen_syndrome,
- raid6_have_mmx,
- "mmxx1",
- 0
-};
-
-/*
- * Unrolled-by-2 MMX implementation
- */
-static void raid6_mmx2_gen_syndrome(int disks, size_t bytes, void **ptrs)
-{
- u8 **dptr = (u8 **)ptrs;
- u8 *p, *q;
- int d, z, z0;
-
- z0 = disks - 3; /* Highest data disk */
- p = dptr[z0+1]; /* XOR parity */
- q = dptr[z0+2]; /* RS syndrome */
-
- kernel_fpu_begin();
-
- asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d));
- asm volatile("pxor %mm5,%mm5"); /* Zero temp */
- asm volatile("pxor %mm7,%mm7"); /* Zero temp */
-
- for ( d = 0 ; d < bytes ; d += 16 ) {
- asm volatile("movq %0,%%mm2" : : "m" (dptr[z0][d])); /* P[0] */
- asm volatile("movq %0,%%mm3" : : "m" (dptr[z0][d+8]));
- asm volatile("movq %mm2,%mm4"); /* Q[0] */
- asm volatile("movq %mm3,%mm6"); /* Q[1] */
- for ( z = z0-1 ; z >= 0 ; z-- ) {
- asm volatile("pcmpgtb %mm4,%mm5");
- asm volatile("pcmpgtb %mm6,%mm7");
- asm volatile("paddb %mm4,%mm4");
- asm volatile("paddb %mm6,%mm6");
- asm volatile("pand %mm0,%mm5");
- asm volatile("pand %mm0,%mm7");
- asm volatile("pxor %mm5,%mm4");
- asm volatile("pxor %mm7,%mm6");
- asm volatile("movq %0,%%mm5" : : "m" (dptr[z][d]));
- asm volatile("movq %0,%%mm7" : : "m" (dptr[z][d+8]));
- asm volatile("pxor %mm5,%mm2");
- asm volatile("pxor %mm7,%mm3");
- asm volatile("pxor %mm5,%mm4");
- asm volatile("pxor %mm7,%mm6");
- asm volatile("pxor %mm5,%mm5");
- asm volatile("pxor %mm7,%mm7");
- }
- asm volatile("movq %%mm2,%0" : "=m" (p[d]));
- asm volatile("movq %%mm3,%0" : "=m" (p[d+8]));
- asm volatile("movq %%mm4,%0" : "=m" (q[d]));
- asm volatile("movq %%mm6,%0" : "=m" (q[d+8]));
- }
-
- kernel_fpu_end();
-}
-
-const struct raid6_calls raid6_mmxx2 = {
- raid6_mmx2_gen_syndrome,
- raid6_have_mmx,
- "mmxx2",
- 0
-};
-
-#endif
+++ /dev/null
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002 H. Peter Anvin - All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- * Boston MA 02111-1307, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * raid6recov.c
- *
- * RAID-6 data recovery in dual failure mode. In single failure mode,
- * use the RAID-5 algorithm (or, in the case of Q failure, just reconstruct
- * the syndrome.)
- */
-
-#include <linux/raid/pq.h>
-
-/* Recover two failed data blocks. */
-void raid6_2data_recov(int disks, size_t bytes, int faila, int failb,
- void **ptrs)
-{
- u8 *p, *q, *dp, *dq;
- u8 px, qx, db;
- const u8 *pbmul; /* P multiplier table for B data */
- const u8 *qmul; /* Q multiplier table (for both) */
-
- p = (u8 *)ptrs[disks-2];
- q = (u8 *)ptrs[disks-1];
-
- /* Compute syndrome with zero for the missing data pages
- Use the dead data pages as temporary storage for
- delta p and delta q */
- dp = (u8 *)ptrs[faila];
- ptrs[faila] = (void *)raid6_empty_zero_page;
- ptrs[disks-2] = dp;
- dq = (u8 *)ptrs[failb];
- ptrs[failb] = (void *)raid6_empty_zero_page;
- ptrs[disks-1] = dq;
-
- raid6_call.gen_syndrome(disks, bytes, ptrs);
-
- /* Restore pointer table */
- ptrs[faila] = dp;
- ptrs[failb] = dq;
- ptrs[disks-2] = p;
- ptrs[disks-1] = q;
-
- /* Now, pick the proper data tables */
- pbmul = raid6_gfmul[raid6_gfexi[failb-faila]];
- qmul = raid6_gfmul[raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]]];
-
- /* Now do it... */
- while ( bytes-- ) {
- px = *p ^ *dp;
- qx = qmul[*q ^ *dq];
- *dq++ = db = pbmul[px] ^ qx; /* Reconstructed B */
- *dp++ = db ^ px; /* Reconstructed A */
- p++; q++;
- }
-}
-EXPORT_SYMBOL_GPL(raid6_2data_recov);
-
-/* Recover failure of one data block plus the P block */
-void raid6_datap_recov(int disks, size_t bytes, int faila, void **ptrs)
-{
- u8 *p, *q, *dq;
- const u8 *qmul; /* Q multiplier table */
-
- p = (u8 *)ptrs[disks-2];
- q = (u8 *)ptrs[disks-1];
-
- /* Compute syndrome with zero for the missing data page
- Use the dead data page as temporary storage for delta q */
- dq = (u8 *)ptrs[faila];
- ptrs[faila] = (void *)raid6_empty_zero_page;
- ptrs[disks-1] = dq;
-
- raid6_call.gen_syndrome(disks, bytes, ptrs);
-
- /* Restore pointer table */
- ptrs[faila] = dq;
- ptrs[disks-1] = q;
-
- /* Now, pick the proper data tables */
- qmul = raid6_gfmul[raid6_gfinv[raid6_gfexp[faila]]];
-
- /* Now do it... */
- while ( bytes-- ) {
- *p++ ^= *dq = qmul[*q ^ *dq];
- q++; dq++;
- }
-}
-EXPORT_SYMBOL_GPL(raid6_datap_recov);
-
-#ifndef __KERNEL__
-/* Testing only */
-
-/* Recover two failed blocks. */
-void raid6_dual_recov(int disks, size_t bytes, int faila, int failb, void **ptrs)
-{
- if ( faila > failb ) {
- int tmp = faila;
- faila = failb;
- failb = tmp;
- }
-
- if ( failb == disks-1 ) {
- if ( faila == disks-2 ) {
- /* P+Q failure. Just rebuild the syndrome. */
- raid6_call.gen_syndrome(disks, bytes, ptrs);
- } else {
- /* data+Q failure. Reconstruct data from P,
- then rebuild syndrome. */
- /* NOT IMPLEMENTED - equivalent to RAID-5 */
- }
- } else {
- if ( failb == disks-2 ) {
- /* data+P failure. */
- raid6_datap_recov(disks, bytes, faila, ptrs);
- } else {
- /* data+data failure. */
- raid6_2data_recov(disks, bytes, faila, failb, ptrs);
- }
- }
-}
-
-#endif
+++ /dev/null
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002 H. Peter Anvin - All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- * Boston MA 02111-1307, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * raid6sse1.c
- *
- * SSE-1/MMXEXT implementation of RAID-6 syndrome functions
- *
- * This is really an MMX implementation, but it requires SSE-1 or
- * AMD MMXEXT for prefetch support and a few other features. The
- * support for nontemporal memory accesses is enough to make this
- * worthwhile as a separate implementation.
- */
-
-#if defined(__i386__) && !defined(__arch_um__)
-
-#include <linux/raid/pq.h>
-#include "raid6x86.h"
-
-/* Defined in raid6mmx.c */
-extern const struct raid6_mmx_constants {
- u64 x1d;
-} raid6_mmx_constants;
-
-static int raid6_have_sse1_or_mmxext(void)
-{
- /* Not really boot_cpu but "all_cpus" */
- return boot_cpu_has(X86_FEATURE_MMX) &&
- (boot_cpu_has(X86_FEATURE_XMM) ||
- boot_cpu_has(X86_FEATURE_MMXEXT));
-}
-
-/*
- * Plain SSE1 implementation
- */
-static void raid6_sse11_gen_syndrome(int disks, size_t bytes, void **ptrs)
-{
- u8 **dptr = (u8 **)ptrs;
- u8 *p, *q;
- int d, z, z0;
-
- z0 = disks - 3; /* Highest data disk */
- p = dptr[z0+1]; /* XOR parity */
- q = dptr[z0+2]; /* RS syndrome */
-
- kernel_fpu_begin();
-
- asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d));
- asm volatile("pxor %mm5,%mm5"); /* Zero temp */
-
- for ( d = 0 ; d < bytes ; d += 8 ) {
- asm volatile("prefetchnta %0" : : "m" (dptr[z0][d]));
- asm volatile("movq %0,%%mm2" : : "m" (dptr[z0][d])); /* P[0] */
- asm volatile("prefetchnta %0" : : "m" (dptr[z0-1][d]));
- asm volatile("movq %mm2,%mm4"); /* Q[0] */
- asm volatile("movq %0,%%mm6" : : "m" (dptr[z0-1][d]));
- for ( z = z0-2 ; z >= 0 ; z-- ) {
- asm volatile("prefetchnta %0" : : "m" (dptr[z][d]));
- asm volatile("pcmpgtb %mm4,%mm5");
- asm volatile("paddb %mm4,%mm4");
- asm volatile("pand %mm0,%mm5");
- asm volatile("pxor %mm5,%mm4");
- asm volatile("pxor %mm5,%mm5");
- asm volatile("pxor %mm6,%mm2");
- asm volatile("pxor %mm6,%mm4");
- asm volatile("movq %0,%%mm6" : : "m" (dptr[z][d]));
- }
- asm volatile("pcmpgtb %mm4,%mm5");
- asm volatile("paddb %mm4,%mm4");
- asm volatile("pand %mm0,%mm5");
- asm volatile("pxor %mm5,%mm4");
- asm volatile("pxor %mm5,%mm5");
- asm volatile("pxor %mm6,%mm2");
- asm volatile("pxor %mm6,%mm4");
-
- asm volatile("movntq %%mm2,%0" : "=m" (p[d]));
- asm volatile("movntq %%mm4,%0" : "=m" (q[d]));
- }
-
- asm volatile("sfence" : : : "memory");
- kernel_fpu_end();
-}
-
-const struct raid6_calls raid6_sse1x1 = {
- raid6_sse11_gen_syndrome,
- raid6_have_sse1_or_mmxext,
- "sse1x1",
- 1 /* Has cache hints */
-};
-
-/*
- * Unrolled-by-2 SSE1 implementation
- */
-static void raid6_sse12_gen_syndrome(int disks, size_t bytes, void **ptrs)
-{
- u8 **dptr = (u8 **)ptrs;
- u8 *p, *q;
- int d, z, z0;
-
- z0 = disks - 3; /* Highest data disk */
- p = dptr[z0+1]; /* XOR parity */
- q = dptr[z0+2]; /* RS syndrome */
-
- kernel_fpu_begin();
-
- asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d));
- asm volatile("pxor %mm5,%mm5"); /* Zero temp */
- asm volatile("pxor %mm7,%mm7"); /* Zero temp */
-
- /* We uniformly assume a single prefetch covers at least 16 bytes */
- for ( d = 0 ; d < bytes ; d += 16 ) {
- asm volatile("prefetchnta %0" : : "m" (dptr[z0][d]));
- asm volatile("movq %0,%%mm2" : : "m" (dptr[z0][d])); /* P[0] */
- asm volatile("movq %0,%%mm3" : : "m" (dptr[z0][d+8])); /* P[1] */
- asm volatile("movq %mm2,%mm4"); /* Q[0] */
- asm volatile("movq %mm3,%mm6"); /* Q[1] */
- for ( z = z0-1 ; z >= 0 ; z-- ) {
- asm volatile("prefetchnta %0" : : "m" (dptr[z][d]));
- asm volatile("pcmpgtb %mm4,%mm5");
- asm volatile("pcmpgtb %mm6,%mm7");
- asm volatile("paddb %mm4,%mm4");
- asm volatile("paddb %mm6,%mm6");
- asm volatile("pand %mm0,%mm5");
- asm volatile("pand %mm0,%mm7");
- asm volatile("pxor %mm5,%mm4");
- asm volatile("pxor %mm7,%mm6");
- asm volatile("movq %0,%%mm5" : : "m" (dptr[z][d]));
- asm volatile("movq %0,%%mm7" : : "m" (dptr[z][d+8]));
- asm volatile("pxor %mm5,%mm2");
- asm volatile("pxor %mm7,%mm3");
- asm volatile("pxor %mm5,%mm4");
- asm volatile("pxor %mm7,%mm6");
- asm volatile("pxor %mm5,%mm5");
- asm volatile("pxor %mm7,%mm7");
- }
- asm volatile("movntq %%mm2,%0" : "=m" (p[d]));
- asm volatile("movntq %%mm3,%0" : "=m" (p[d+8]));
- asm volatile("movntq %%mm4,%0" : "=m" (q[d]));
- asm volatile("movntq %%mm6,%0" : "=m" (q[d+8]));
- }
-
- asm volatile("sfence" : :: "memory");
- kernel_fpu_end();
-}
-
-const struct raid6_calls raid6_sse1x2 = {
- raid6_sse12_gen_syndrome,
- raid6_have_sse1_or_mmxext,
- "sse1x2",
- 1 /* Has cache hints */
-};
-
-#endif
+++ /dev/null
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002 H. Peter Anvin - All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- * Boston MA 02111-1307, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * raid6sse2.c
- *
- * SSE-2 implementation of RAID-6 syndrome functions
- *
- */
-
-#if (defined(__i386__) || defined(__x86_64__)) && !defined(__arch_um__)
-
-#include <linux/raid/pq.h>
-#include "raid6x86.h"
-
-static const struct raid6_sse_constants {
- u64 x1d[2];
-} raid6_sse_constants __attribute__((aligned(16))) = {
- { 0x1d1d1d1d1d1d1d1dULL, 0x1d1d1d1d1d1d1d1dULL },
-};
-
-static int raid6_have_sse2(void)
-{
- /* Not really boot_cpu but "all_cpus" */
- return boot_cpu_has(X86_FEATURE_MMX) &&
- boot_cpu_has(X86_FEATURE_FXSR) &&
- boot_cpu_has(X86_FEATURE_XMM) &&
- boot_cpu_has(X86_FEATURE_XMM2);
-}
-
-/*
- * Plain SSE2 implementation
- */
-static void raid6_sse21_gen_syndrome(int disks, size_t bytes, void **ptrs)
-{
- u8 **dptr = (u8 **)ptrs;
- u8 *p, *q;
- int d, z, z0;
-
- z0 = disks - 3; /* Highest data disk */
- p = dptr[z0+1]; /* XOR parity */
- q = dptr[z0+2]; /* RS syndrome */
-
- kernel_fpu_begin();
-
- asm volatile("movdqa %0,%%xmm0" : : "m" (raid6_sse_constants.x1d[0]));
- asm volatile("pxor %xmm5,%xmm5"); /* Zero temp */
-
- for ( d = 0 ; d < bytes ; d += 16 ) {
- asm volatile("prefetchnta %0" : : "m" (dptr[z0][d]));
- asm volatile("movdqa %0,%%xmm2" : : "m" (dptr[z0][d])); /* P[0] */
- asm volatile("prefetchnta %0" : : "m" (dptr[z0-1][d]));
- asm volatile("movdqa %xmm2,%xmm4"); /* Q[0] */
- asm volatile("movdqa %0,%%xmm6" : : "m" (dptr[z0-1][d]));
- for ( z = z0-2 ; z >= 0 ; z-- ) {
- asm volatile("prefetchnta %0" : : "m" (dptr[z][d]));
- asm volatile("pcmpgtb %xmm4,%xmm5");
- asm volatile("paddb %xmm4,%xmm4");
- asm volatile("pand %xmm0,%xmm5");
- asm volatile("pxor %xmm5,%xmm4");
- asm volatile("pxor %xmm5,%xmm5");
- asm volatile("pxor %xmm6,%xmm2");
- asm volatile("pxor %xmm6,%xmm4");
- asm volatile("movdqa %0,%%xmm6" : : "m" (dptr[z][d]));
- }
- asm volatile("pcmpgtb %xmm4,%xmm5");
- asm volatile("paddb %xmm4,%xmm4");
- asm volatile("pand %xmm0,%xmm5");
- asm volatile("pxor %xmm5,%xmm4");
- asm volatile("pxor %xmm5,%xmm5");
- asm volatile("pxor %xmm6,%xmm2");
- asm volatile("pxor %xmm6,%xmm4");
-
- asm volatile("movntdq %%xmm2,%0" : "=m" (p[d]));
- asm volatile("pxor %xmm2,%xmm2");
- asm volatile("movntdq %%xmm4,%0" : "=m" (q[d]));
- asm volatile("pxor %xmm4,%xmm4");
- }
-
- asm volatile("sfence" : : : "memory");
- kernel_fpu_end();
-}
-
-const struct raid6_calls raid6_sse2x1 = {
- raid6_sse21_gen_syndrome,
- raid6_have_sse2,
- "sse2x1",
- 1 /* Has cache hints */
-};
-
-/*
- * Unrolled-by-2 SSE2 implementation
- */
-static void raid6_sse22_gen_syndrome(int disks, size_t bytes, void **ptrs)
-{
- u8 **dptr = (u8 **)ptrs;
- u8 *p, *q;
- int d, z, z0;
-
- z0 = disks - 3; /* Highest data disk */
- p = dptr[z0+1]; /* XOR parity */
- q = dptr[z0+2]; /* RS syndrome */
-
- kernel_fpu_begin();
-
- asm volatile("movdqa %0,%%xmm0" : : "m" (raid6_sse_constants.x1d[0]));
- asm volatile("pxor %xmm5,%xmm5"); /* Zero temp */
- asm volatile("pxor %xmm7,%xmm7"); /* Zero temp */
-
- /* We uniformly assume a single prefetch covers at least 32 bytes */
- for ( d = 0 ; d < bytes ; d += 32 ) {
- asm volatile("prefetchnta %0" : : "m" (dptr[z0][d]));
- asm volatile("movdqa %0,%%xmm2" : : "m" (dptr[z0][d])); /* P[0] */
- asm volatile("movdqa %0,%%xmm3" : : "m" (dptr[z0][d+16])); /* P[1] */
- asm volatile("movdqa %xmm2,%xmm4"); /* Q[0] */
- asm volatile("movdqa %xmm3,%xmm6"); /* Q[1] */
- for ( z = z0-1 ; z >= 0 ; z-- ) {
- asm volatile("prefetchnta %0" : : "m" (dptr[z][d]));
- asm volatile("pcmpgtb %xmm4,%xmm5");
- asm volatile("pcmpgtb %xmm6,%xmm7");
- asm volatile("paddb %xmm4,%xmm4");
- asm volatile("paddb %xmm6,%xmm6");
- asm volatile("pand %xmm0,%xmm5");
- asm volatile("pand %xmm0,%xmm7");
- asm volatile("pxor %xmm5,%xmm4");
- asm volatile("pxor %xmm7,%xmm6");
- asm volatile("movdqa %0,%%xmm5" : : "m" (dptr[z][d]));
- asm volatile("movdqa %0,%%xmm7" : : "m" (dptr[z][d+16]));
- asm volatile("pxor %xmm5,%xmm2");
- asm volatile("pxor %xmm7,%xmm3");
- asm volatile("pxor %xmm5,%xmm4");
- asm volatile("pxor %xmm7,%xmm6");
- asm volatile("pxor %xmm5,%xmm5");
- asm volatile("pxor %xmm7,%xmm7");
- }
- asm volatile("movntdq %%xmm2,%0" : "=m" (p[d]));
- asm volatile("movntdq %%xmm3,%0" : "=m" (p[d+16]));
- asm volatile("movntdq %%xmm4,%0" : "=m" (q[d]));
- asm volatile("movntdq %%xmm6,%0" : "=m" (q[d+16]));
- }
-
- asm volatile("sfence" : : : "memory");
- kernel_fpu_end();
-}
-
-const struct raid6_calls raid6_sse2x2 = {
- raid6_sse22_gen_syndrome,
- raid6_have_sse2,
- "sse2x2",
- 1 /* Has cache hints */
-};
-
-#endif
-
-#if defined(__x86_64__) && !defined(__arch_um__)
-
-/*
- * Unrolled-by-4 SSE2 implementation
- */
-static void raid6_sse24_gen_syndrome(int disks, size_t bytes, void **ptrs)
-{
- u8 **dptr = (u8 **)ptrs;
- u8 *p, *q;
- int d, z, z0;
-
- z0 = disks - 3; /* Highest data disk */
- p = dptr[z0+1]; /* XOR parity */
- q = dptr[z0+2]; /* RS syndrome */
-
- kernel_fpu_begin();
-
- asm volatile("movdqa %0,%%xmm0" :: "m" (raid6_sse_constants.x1d[0]));
- asm volatile("pxor %xmm2,%xmm2"); /* P[0] */
- asm volatile("pxor %xmm3,%xmm3"); /* P[1] */
- asm volatile("pxor %xmm4,%xmm4"); /* Q[0] */
- asm volatile("pxor %xmm5,%xmm5"); /* Zero temp */
- asm volatile("pxor %xmm6,%xmm6"); /* Q[1] */
- asm volatile("pxor %xmm7,%xmm7"); /* Zero temp */
- asm volatile("pxor %xmm10,%xmm10"); /* P[2] */
- asm volatile("pxor %xmm11,%xmm11"); /* P[3] */
- asm volatile("pxor %xmm12,%xmm12"); /* Q[2] */
- asm volatile("pxor %xmm13,%xmm13"); /* Zero temp */
- asm volatile("pxor %xmm14,%xmm14"); /* Q[3] */
- asm volatile("pxor %xmm15,%xmm15"); /* Zero temp */
-
- for ( d = 0 ; d < bytes ; d += 64 ) {
- for ( z = z0 ; z >= 0 ; z-- ) {
- /* The second prefetch seems to improve performance... */
- asm volatile("prefetchnta %0" :: "m" (dptr[z][d]));
- asm volatile("prefetchnta %0" :: "m" (dptr[z][d+32]));
- asm volatile("pcmpgtb %xmm4,%xmm5");
- asm volatile("pcmpgtb %xmm6,%xmm7");
- asm volatile("pcmpgtb %xmm12,%xmm13");
- asm volatile("pcmpgtb %xmm14,%xmm15");
- asm volatile("paddb %xmm4,%xmm4");
- asm volatile("paddb %xmm6,%xmm6");
- asm volatile("paddb %xmm12,%xmm12");
- asm volatile("paddb %xmm14,%xmm14");
- asm volatile("pand %xmm0,%xmm5");
- asm volatile("pand %xmm0,%xmm7");
- asm volatile("pand %xmm0,%xmm13");
- asm volatile("pand %xmm0,%xmm15");
- asm volatile("pxor %xmm5,%xmm4");
- asm volatile("pxor %xmm7,%xmm6");
- asm volatile("pxor %xmm13,%xmm12");
- asm volatile("pxor %xmm15,%xmm14");
- asm volatile("movdqa %0,%%xmm5" :: "m" (dptr[z][d]));
- asm volatile("movdqa %0,%%xmm7" :: "m" (dptr[z][d+16]));
- asm volatile("movdqa %0,%%xmm13" :: "m" (dptr[z][d+32]));
- asm volatile("movdqa %0,%%xmm15" :: "m" (dptr[z][d+48]));
- asm volatile("pxor %xmm5,%xmm2");
- asm volatile("pxor %xmm7,%xmm3");
- asm volatile("pxor %xmm13,%xmm10");
- asm volatile("pxor %xmm15,%xmm11");
- asm volatile("pxor %xmm5,%xmm4");
- asm volatile("pxor %xmm7,%xmm6");
- asm volatile("pxor %xmm13,%xmm12");
- asm volatile("pxor %xmm15,%xmm14");
- asm volatile("pxor %xmm5,%xmm5");
- asm volatile("pxor %xmm7,%xmm7");
- asm volatile("pxor %xmm13,%xmm13");
- asm volatile("pxor %xmm15,%xmm15");
- }
- asm volatile("movntdq %%xmm2,%0" : "=m" (p[d]));
- asm volatile("pxor %xmm2,%xmm2");
- asm volatile("movntdq %%xmm3,%0" : "=m" (p[d+16]));
- asm volatile("pxor %xmm3,%xmm3");
- asm volatile("movntdq %%xmm10,%0" : "=m" (p[d+32]));
- asm volatile("pxor %xmm10,%xmm10");
- asm volatile("movntdq %%xmm11,%0" : "=m" (p[d+48]));
- asm volatile("pxor %xmm11,%xmm11");
- asm volatile("movntdq %%xmm4,%0" : "=m" (q[d]));
- asm volatile("pxor %xmm4,%xmm4");
- asm volatile("movntdq %%xmm6,%0" : "=m" (q[d+16]));
- asm volatile("pxor %xmm6,%xmm6");
- asm volatile("movntdq %%xmm12,%0" : "=m" (q[d+32]));
- asm volatile("pxor %xmm12,%xmm12");
- asm volatile("movntdq %%xmm14,%0" : "=m" (q[d+48]));
- asm volatile("pxor %xmm14,%xmm14");
- }
-
- asm volatile("sfence" : : : "memory");
- kernel_fpu_end();
-}
-
-const struct raid6_calls raid6_sse2x4 = {
- raid6_sse24_gen_syndrome,
- raid6_have_sse2,
- "sse2x4",
- 1 /* Has cache hints */
-};
-
-#endif
+++ /dev/null
-#
-# This is a simple Makefile to test some of the RAID-6 code
-# from userspace.
-#
-
-CC = gcc
-OPTFLAGS = -O2 # Adjust as desired
-CFLAGS = -I.. -I ../../../include -g $(OPTFLAGS)
-LD = ld
-AWK = awk
-AR = ar
-RANLIB = ranlib
-
-.c.o:
- $(CC) $(CFLAGS) -c -o $@ $<
-
-%.c: ../%.c
- cp -f $< $@
-
-%.uc: ../%.uc
- cp -f $< $@
-
-all: raid6.a raid6test
-
-raid6.a: raid6int1.o raid6int2.o raid6int4.o raid6int8.o raid6int16.o \
- raid6int32.o \
- raid6mmx.o raid6sse1.o raid6sse2.o \
- raid6altivec1.o raid6altivec2.o raid6altivec4.o raid6altivec8.o \
- raid6recov.o raid6algos.o \
- raid6tables.o
- rm -f $@
- $(AR) cq $@ $^
- $(RANLIB) $@
-
-raid6test: test.c raid6.a
- $(CC) $(CFLAGS) -o raid6test $^
-
-raid6altivec1.c: raid6altivec.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=1 < raid6altivec.uc > $@
-
-raid6altivec2.c: raid6altivec.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=2 < raid6altivec.uc > $@
-
-raid6altivec4.c: raid6altivec.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=4 < raid6altivec.uc > $@
-
-raid6altivec8.c: raid6altivec.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=8 < raid6altivec.uc > $@
-
-raid6int1.c: raid6int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=1 < raid6int.uc > $@
-
-raid6int2.c: raid6int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=2 < raid6int.uc > $@
-
-raid6int4.c: raid6int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=4 < raid6int.uc > $@
-
-raid6int8.c: raid6int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=8 < raid6int.uc > $@
-
-raid6int16.c: raid6int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=16 < raid6int.uc > $@
-
-raid6int32.c: raid6int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=32 < raid6int.uc > $@
-
-raid6tables.c: mktables
- ./mktables > raid6tables.c
-
-clean:
- rm -f *.o *.a mktables mktables.c raid6int.uc raid6*.c raid6test
-
-spotless: clean
- rm -f *~
+++ /dev/null
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved
- *
- * This file is part of the Linux kernel, and is made available under
- * the terms of the GNU General Public License version 2 or (at your
- * option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * raid6test.c
- *
- * Test RAID-6 recovery with various algorithms
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <linux/raid/pq.h>
-
-#define NDISKS 16 /* Including P and Q */
-
-const char raid6_empty_zero_page[PAGE_SIZE] __attribute__((aligned(256)));
-struct raid6_calls raid6_call;
-
-char *dataptrs[NDISKS];
-char data[NDISKS][PAGE_SIZE];
-char recovi[PAGE_SIZE], recovj[PAGE_SIZE];
-
-static void makedata(void)
-{
- int i, j;
-
- for (i = 0; i < NDISKS; i++) {
- for (j = 0; j < PAGE_SIZE; j++)
- data[i][j] = rand();
-
- dataptrs[i] = data[i];
- }
-}
-
-static char disk_type(int d)
-{
- switch (d) {
- case NDISKS-2:
- return 'P';
- case NDISKS-1:
- return 'Q';
- default:
- return 'D';
- }
-}
-
-static int test_disks(int i, int j)
-{
- int erra, errb;
-
- memset(recovi, 0xf0, PAGE_SIZE);
- memset(recovj, 0xba, PAGE_SIZE);
-
- dataptrs[i] = recovi;
- dataptrs[j] = recovj;
-
- raid6_dual_recov(NDISKS, PAGE_SIZE, i, j, (void **)&dataptrs);
-
- erra = memcmp(data[i], recovi, PAGE_SIZE);
- errb = memcmp(data[j], recovj, PAGE_SIZE);
-
- if (i < NDISKS-2 && j == NDISKS-1) {
- /* We don't implement the DQ failure scenario, since it's
- equivalent to a RAID-5 failure (XOR, then recompute Q) */
- erra = errb = 0;
- } else {
- printf("algo=%-8s faila=%3d(%c) failb=%3d(%c) %s\n",
- raid6_call.name,
- i, disk_type(i),
- j, disk_type(j),
- (!erra && !errb) ? "OK" :
- !erra ? "ERRB" :
- !errb ? "ERRA" : "ERRAB");
- }
-
- dataptrs[i] = data[i];
- dataptrs[j] = data[j];
-
- return erra || errb;
-}
-
-int main(int argc, char *argv[])
-{
- const struct raid6_calls *const *algo;
- int i, j;
- int err = 0;
-
- makedata();
-
- for (algo = raid6_algos; *algo; algo++) {
- if (!(*algo)->valid || (*algo)->valid()) {
- raid6_call = **algo;
-
- /* Nuke syndromes */
- memset(data[NDISKS-2], 0xee, 2*PAGE_SIZE);
-
- /* Generate assumed good syndrome */
- raid6_call.gen_syndrome(NDISKS, PAGE_SIZE,
- (void **)&dataptrs);
-
- for (i = 0; i < NDISKS-1; i++)
- for (j = i+1; j < NDISKS; j++)
- err += test_disks(i, j);
- }
- printf("\n");
- }
-
- printf("\n");
- /* Pick the best algorithm test */
- raid6_select_algo();
-
- if (err)
- printf("\n*** ERRORS FOUND ***\n");
-
- return err;
-}
+++ /dev/null
-/* ----------------------------------------------------------------------- *
- *
- * Copyright 2002-2004 H. Peter Anvin - All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
- * Boston MA 02111-1307, USA; either version 2 of the License, or
- * (at your option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * raid6x86.h
- *
- * Definitions common to x86 and x86-64 RAID-6 code only
- */
-
-#ifndef LINUX_RAID_RAID6X86_H
-#define LINUX_RAID_RAID6X86_H
-
-#if (defined(__i386__) || defined(__x86_64__)) && !defined(__arch_um__)
-
-#ifdef __KERNEL__ /* Real code */
-
-#include <asm/i387.h>
-
-#else /* Dummy code for user space testing */
-
-static inline void kernel_fpu_begin(void)
-{
-}
-
-static inline void kernel_fpu_end(void)
-{
-}
-
-#define X86_FEATURE_MMX (0*32+23) /* Multimedia Extensions */
-#define X86_FEATURE_FXSR (0*32+24) /* FXSAVE and FXRSTOR instructions
- * (fast save and restore) */
-#define X86_FEATURE_XMM (0*32+25) /* Streaming SIMD Extensions */
-#define X86_FEATURE_XMM2 (0*32+26) /* Streaming SIMD Extensions-2 */
-#define X86_FEATURE_MMXEXT (1*32+22) /* AMD MMX extensions */
-
-/* Should work well enough on modern CPUs for testing */
-static inline int boot_cpu_has(int flag)
-{
- u32 eax = (flag >> 5) ? 0x80000001 : 1;
- u32 edx;
-
- asm volatile("cpuid"
- : "+a" (eax), "=d" (edx)
- : : "ecx", "ebx");
-
- return (edx >> (flag & 31)) & 1;
-}
-
-#endif /* ndef __KERNEL__ */
-
-#endif
-#endif
+++ /dev/null
-
-# This filter requires one command line option of form -vN=n
-# where n must be a decimal number.
-#
-# Repeat each input line containing $$ n times, replacing $$ with 0...n-1.
-# Replace each $# with n, and each $* with a single $.
-
-BEGIN {
- n = N + 0
-}
-{
- if (/\$\$/) { rep = n } else { rep = 1 }
- for (i = 0; i < rep; ++i) {
- tmp = $0
- gsub(/\$\$/, i, tmp)
- gsub(/\$\#/, n, tmp)
- gsub(/\$\*/, "$", tmp)
- print tmp
- }
-}
-config IR_CORE
- tristate
+menuconfig IR_CORE
+ tristate "Infrared remote controller adapters"
depends on INPUT
default INPUT
+if IR_CORE
+
config VIDEO_IR
tristate
depends on IR_CORE
Enable this option to build the Linux Infrared Remote
Control (LIRC) core device interface driver. The LIRC
interface passes raw IR to and from userspace, where the
- LIRC daemon handles protocol decoding for IR reception ann
+ LIRC daemon handles protocol decoding for IR reception and
encoding for IR transmitting (aka "blasting").
source "drivers/media/IR/keymaps/Kconfig"
To compile this driver as a module, choose M here: the
module will be called mceusb.
+
+config IR_ENE
+ tristate "ENE eHome Receiver/Transciever (pnp id: ENE0100/ENE02xxx)"
+ depends on PNP
+ depends on IR_CORE
+ ---help---
+ Say Y here to enable support for integrated infrared receiver
+ /transciever made by ENE.
+
+ You can see if you have it by looking at lspnp output.
+ Output should include ENE0100 ENE0200 or something similiar.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ene_ir.
+
+config IR_STREAMZAP
+ tristate "Streamzap PC Remote IR Receiver"
+ depends on USB_ARCH_HAS_HCD
+ depends on IR_CORE
+ select USB
+ ---help---
+ Say Y here if you want to use a Streamzap PC Remote
+ Infrared Receiver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called streamzap.
+
+endif #IR_CORE
# stand-alone IR receivers/transmitters
obj-$(CONFIG_IR_IMON) += imon.o
obj-$(CONFIG_IR_MCEUSB) += mceusb.o
+obj-$(CONFIG_IR_ENE) += ene_ir.o
+obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
--- /dev/null
+/*
+ * driver for ENE KB3926 B/C/D CIR (pnp id: ENE0XXX)
+ *
+ * Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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/pnp.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <media/ir-core.h>
+#include <media/ir-common.h>
+#include "ene_ir.h"
+
+
+static int sample_period = -1;
+static int enable_idle = 1;
+static int input = 1;
+static int debug;
+static int txsim;
+
+static int ene_irq_status(struct ene_device *dev);
+
+/* read a hardware register */
+static u8 ene_hw_read_reg(struct ene_device *dev, u16 reg)
+{
+ u8 retval;
+ outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
+ outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
+ retval = inb(dev->hw_io + ENE_IO);
+
+ ene_dbg_verbose("reg %04x == %02x", reg, retval);
+ return retval;
+}
+
+/* write a hardware register */
+static void ene_hw_write_reg(struct ene_device *dev, u16 reg, u8 value)
+{
+ outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
+ outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
+ outb(value, dev->hw_io + ENE_IO);
+
+ ene_dbg_verbose("reg %04x <- %02x", reg, value);
+}
+
+/* change specific bits in hardware register */
+static void ene_hw_write_reg_mask(struct ene_device *dev,
+ u16 reg, u8 value, u8 mask)
+{
+ u8 regvalue;
+
+ outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
+ outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
+
+ regvalue = inb(dev->hw_io + ENE_IO) & ~mask;
+ regvalue |= (value & mask);
+ outb(regvalue, dev->hw_io + ENE_IO);
+
+ ene_dbg_verbose("reg %04x <- %02x (mask=%02x)", reg, value, mask);
+}
+
+/* detect hardware features */
+static int ene_hw_detect(struct ene_device *dev)
+{
+ u8 chip_major, chip_minor;
+ u8 hw_revision, old_ver;
+ u8 tmp;
+ u8 fw_capabilities;
+ int pll_freq;
+
+ tmp = ene_hw_read_reg(dev, ENE_HW_UNK);
+ ene_hw_write_reg(dev, ENE_HW_UNK, tmp & ~ENE_HW_UNK_CLR);
+
+ chip_major = ene_hw_read_reg(dev, ENE_HW_VER_MAJOR);
+ chip_minor = ene_hw_read_reg(dev, ENE_HW_VER_MINOR);
+
+ ene_hw_write_reg(dev, ENE_HW_UNK, tmp);
+ hw_revision = ene_hw_read_reg(dev, ENE_HW_VERSION);
+ old_ver = ene_hw_read_reg(dev, ENE_HW_VER_OLD);
+
+ pll_freq = (ene_hw_read_reg(dev, ENE_PLLFRH) << 4) +
+ (ene_hw_read_reg(dev, ENE_PLLFRL) >> 4);
+
+ if (pll_freq != 1000)
+ dev->rx_period_adjust = 4;
+ else
+ dev->rx_period_adjust = 2;
+
+
+ ene_printk(KERN_NOTICE, "PLL freq = %d\n", pll_freq);
+
+ if (hw_revision == 0xFF) {
+
+ ene_printk(KERN_WARNING, "device seems to be disabled\n");
+ ene_printk(KERN_WARNING,
+ "send a mail to lirc-list@lists.sourceforge.net\n");
+ ene_printk(KERN_WARNING, "please attach output of acpidump\n");
+ return -ENODEV;
+ }
+
+ if (chip_major == 0x33) {
+ ene_printk(KERN_WARNING, "chips 0x33xx aren't supported\n");
+ return -ENODEV;
+ }
+
+ if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) {
+ dev->hw_revision = ENE_HW_C;
+ } else if (old_ver == 0x24 && hw_revision == 0xC0) {
+ dev->hw_revision = ENE_HW_B;
+ ene_printk(KERN_NOTICE, "KB3926B detected\n");
+ } else {
+ dev->hw_revision = ENE_HW_D;
+ ene_printk(KERN_WARNING,
+ "unknown ENE chip detected, assuming KB3926D\n");
+ ene_printk(KERN_WARNING,
+ "driver support might be not complete");
+
+ }
+
+ ene_printk(KERN_DEBUG,
+ "chip is 0x%02x%02x - kbver = 0x%02x, rev = 0x%02x\n",
+ chip_major, chip_minor, old_ver, hw_revision);
+
+ /* detect features hardware supports */
+ if (dev->hw_revision < ENE_HW_C)
+ return 0;
+
+ fw_capabilities = ene_hw_read_reg(dev, ENE_FW2);
+ ene_dbg("Firmware capabilities: %02x", fw_capabilities);
+
+ dev->hw_gpio40_learning = fw_capabilities & ENE_FW2_GP40_AS_LEARN;
+ dev->hw_learning_and_tx_capable = fw_capabilities & ENE_FW2_LEARNING;
+
+ dev->hw_fan_as_normal_input = dev->hw_learning_and_tx_capable &&
+ (fw_capabilities & ENE_FW2_FAN_AS_NRML_IN);
+
+ ene_printk(KERN_NOTICE, "hardware features:\n");
+ ene_printk(KERN_NOTICE,
+ "learning and transmit %s, gpio40_learn %s, fan_in %s\n",
+ dev->hw_learning_and_tx_capable ? "on" : "off",
+ dev->hw_gpio40_learning ? "on" : "off",
+ dev->hw_fan_as_normal_input ? "on" : "off");
+
+ if (dev->hw_learning_and_tx_capable) {
+ ene_printk(KERN_WARNING,
+ "Device supports transmitting, but that support is\n");
+ ene_printk(KERN_WARNING,
+ "lightly tested. Please test it and mail\n");
+ ene_printk(KERN_WARNING,
+ "lirc-list@lists.sourceforge.net\n");
+ }
+ return 0;
+}
+
+/* this enables/disables IR input via gpio40*/
+static void ene_enable_gpio40_receive(struct ene_device *dev, int enable)
+{
+ ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, enable ?
+ 0 : ENE_CIR_CONF2_GPIO40DIS,
+ ENE_CIR_CONF2_GPIO40DIS);
+}
+
+/* this enables/disables IR via standard input */
+static void ene_enable_normal_receive(struct ene_device *dev, int enable)
+{
+ ene_hw_write_reg(dev, ENE_CIR_CONF1, enable ? ENE_CIR_CONF1_RX_ON : 0);
+}
+
+/* this enables/disables IR input via unused fan tachtometer input */
+static void ene_enable_fan_receive(struct ene_device *dev, int enable)
+{
+ if (!enable)
+ ene_hw_write_reg(dev, ENE_FAN_AS_IN1, 0);
+ else {
+ ene_hw_write_reg(dev, ENE_FAN_AS_IN1, ENE_FAN_AS_IN1_EN);
+ ene_hw_write_reg(dev, ENE_FAN_AS_IN2, ENE_FAN_AS_IN2_EN);
+ }
+ dev->rx_fan_input_inuse = enable;
+}
+
+
+/* Sense current received carrier */
+static int ene_rx_sense_carrier(struct ene_device *dev)
+{
+ int period = ene_hw_read_reg(dev, ENE_RX_CARRIER);
+ int carrier;
+ ene_dbg("RX: hardware carrier period = %02x", period);
+
+ if (!(period & ENE_RX_CARRIER_VALID))
+ return 0;
+
+ period &= ~ENE_RX_CARRIER_VALID;
+
+ if (!period)
+ return 0;
+
+ carrier = 2000000 / period;
+ ene_dbg("RX: sensed carrier = %d Hz", carrier);
+ return carrier;
+}
+
+/* determine which input to use*/
+static void ene_rx_set_inputs(struct ene_device *dev)
+{
+ int learning_mode = dev->learning_enabled;
+
+ ene_dbg("RX: setup receiver, learning mode = %d", learning_mode);
+
+ ene_enable_normal_receive(dev, 1);
+
+ /* old hardware doesn't support learning mode for sure */
+ if (dev->hw_revision <= ENE_HW_B)
+ return;
+
+ /* receiver not learning capable, still set gpio40 correctly */
+ if (!dev->hw_learning_and_tx_capable) {
+ ene_enable_gpio40_receive(dev, !dev->hw_gpio40_learning);
+ return;
+ }
+
+ /* enable learning mode */
+ if (learning_mode) {
+ ene_enable_gpio40_receive(dev, dev->hw_gpio40_learning);
+
+ /* fan input is not used for learning */
+ if (dev->hw_fan_as_normal_input)
+ ene_enable_fan_receive(dev, 0);
+
+ /* disable learning mode */
+ } else {
+ if (dev->hw_fan_as_normal_input) {
+ ene_enable_fan_receive(dev, 1);
+ ene_enable_normal_receive(dev, 0);
+ } else
+ ene_enable_gpio40_receive(dev,
+ !dev->hw_gpio40_learning);
+ }
+
+ /* set few additional settings for this mode */
+ ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, learning_mode ?
+ ENE_CIR_CONF1_LEARN1 : 0, ENE_CIR_CONF1_LEARN1);
+
+ ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, learning_mode ?
+ ENE_CIR_CONF2_LEARN2 : 0, ENE_CIR_CONF2_LEARN2);
+
+ if (dev->rx_fan_input_inuse) {
+ dev->props->rx_resolution = ENE_SAMPLE_PERIOD_FAN * 1000;
+
+ dev->props->timeout =
+ ENE_FAN_VALUE_MASK * ENE_SAMPLE_PERIOD_FAN * 1000;
+ } else {
+ dev->props->rx_resolution = sample_period * 1000;
+ dev->props->timeout = ENE_MAXGAP * 1000;
+ }
+}
+
+/* Enable the device for receive */
+static void ene_rx_enable(struct ene_device *dev)
+{
+ u8 reg_value;
+
+ if (dev->hw_revision < ENE_HW_C) {
+ ene_hw_write_reg(dev, ENEB_IRQ, dev->irq << 1);
+ ene_hw_write_reg(dev, ENEB_IRQ_UNK1, 0x01);
+ } else {
+ reg_value = ene_hw_read_reg(dev, ENEC_IRQ) & 0xF0;
+ reg_value |= ENEC_IRQ_UNK_EN;
+ reg_value &= ~ENEC_IRQ_STATUS;
+ reg_value |= (dev->irq & ENEC_IRQ_MASK);
+ ene_hw_write_reg(dev, ENEC_IRQ, reg_value);
+ ene_hw_write_reg(dev, ENE_TX_UNK1, 0x63);
+ }
+
+ ene_hw_write_reg(dev, ENE_CIR_CONF2, 0x00);
+ ene_rx_set_inputs(dev);
+
+ /* set sampling period */
+ ene_hw_write_reg(dev, ENE_CIR_SAMPLE_PERIOD, sample_period);
+
+ /* ack any pending irqs - just in case */
+ ene_irq_status(dev);
+
+ /* enable firmware bits */
+ ene_hw_write_reg_mask(dev, ENE_FW1,
+ ENE_FW1_ENABLE | ENE_FW1_IRQ,
+ ENE_FW1_ENABLE | ENE_FW1_IRQ);
+
+ /* enter idle mode */
+ ir_raw_event_set_idle(dev->idev, 1);
+ ir_raw_event_reset(dev->idev);
+
+}
+
+/* Disable the device receiver */
+static void ene_rx_disable(struct ene_device *dev)
+{
+ /* disable inputs */
+ ene_enable_normal_receive(dev, 0);
+
+ if (dev->hw_fan_as_normal_input)
+ ene_enable_fan_receive(dev, 0);
+
+ /* disable hardware IRQ and firmware flag */
+ ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_ENABLE | ENE_FW1_IRQ);
+
+ ir_raw_event_set_idle(dev->idev, 1);
+ ir_raw_event_reset(dev->idev);
+}
+
+
+/* prepare transmission */
+static void ene_tx_prepare(struct ene_device *dev)
+{
+ u8 conf1;
+
+ conf1 = ene_hw_read_reg(dev, ENE_CIR_CONF1);
+ dev->saved_conf1 = conf1;
+
+ if (dev->hw_revision == ENE_HW_C)
+ conf1 &= ~ENE_CIR_CONF1_TX_CLEAR;
+
+ /* Enable TX engine */
+ conf1 |= ENE_CIR_CONF1_TX_ON;
+
+ /* Set carrier */
+ if (dev->tx_period) {
+
+ /* NOTE: duty cycle handling is just a guess, it might
+ not be aviable. Default values were tested */
+ int tx_period_in500ns = dev->tx_period * 2;
+
+ int tx_pulse_width_in_500ns =
+ tx_period_in500ns / (100 / dev->tx_duty_cycle);
+
+ if (!tx_pulse_width_in_500ns)
+ tx_pulse_width_in_500ns = 1;
+
+ ene_dbg("TX: pulse distance = %d * 500 ns", tx_period_in500ns);
+ ene_dbg("TX: pulse width = %d * 500 ns",
+ tx_pulse_width_in_500ns);
+
+ ene_hw_write_reg(dev, ENE_TX_PERIOD, ENE_TX_PERIOD_UNKBIT |
+ tx_period_in500ns);
+
+ ene_hw_write_reg(dev, ENE_TX_PERIOD_PULSE,
+ tx_pulse_width_in_500ns);
+
+ conf1 |= ENE_CIR_CONF1_TX_CARR;
+ } else
+ conf1 &= ~ENE_CIR_CONF1_TX_CARR;
+
+ ene_hw_write_reg(dev, ENE_CIR_CONF1, conf1);
+
+}
+
+/* end transmission */
+static void ene_tx_complete(struct ene_device *dev)
+{
+ ene_hw_write_reg(dev, ENE_CIR_CONF1, dev->saved_conf1);
+ dev->tx_buffer = NULL;
+}
+
+/* set transmit mask */
+static void ene_tx_hw_set_transmiter_mask(struct ene_device *dev)
+{
+ u8 txport1 = ene_hw_read_reg(dev, ENE_TX_PORT1) & ~ENE_TX_PORT1_EN;
+ u8 txport2 = ene_hw_read_reg(dev, ENE_TX_PORT2) & ~ENE_TX_PORT2_EN;
+
+ if (dev->transmitter_mask & 0x01)
+ txport1 |= ENE_TX_PORT1_EN;
+
+ if (dev->transmitter_mask & 0x02)
+ txport2 |= ENE_TX_PORT2_EN;
+
+ ene_hw_write_reg(dev, ENE_TX_PORT1, txport1);
+ ene_hw_write_reg(dev, ENE_TX_PORT2, txport2);
+}
+
+/* TX one sample - must be called with dev->hw_lock*/
+static void ene_tx_sample(struct ene_device *dev)
+{
+ u8 raw_tx;
+ u32 sample;
+
+ if (!dev->tx_buffer) {
+ ene_dbg("TX: attempt to transmit NULL buffer");
+ return;
+ }
+
+ /* Grab next TX sample */
+ if (!dev->tx_sample) {
+again:
+ if (dev->tx_pos == dev->tx_len + 1) {
+ if (!dev->tx_done) {
+ ene_dbg("TX: no more data to send");
+ dev->tx_done = 1;
+ goto exit;
+ } else {
+ ene_dbg("TX: last sample sent by hardware");
+ ene_tx_complete(dev);
+ complete(&dev->tx_complete);
+ return;
+ }
+ }
+
+ sample = dev->tx_buffer[dev->tx_pos++];
+ dev->tx_sample_pulse = !dev->tx_sample_pulse;
+
+ ene_dbg("TX: sample %8d (%s)", sample, dev->tx_sample_pulse ?
+ "pulse" : "space");
+
+ dev->tx_sample = DIV_ROUND_CLOSEST(sample, ENE_TX_SMPL_PERIOD);
+
+ /* guard against too short samples */
+ if (!dev->tx_sample)
+ goto again;
+ }
+
+ raw_tx = min(dev->tx_sample , (unsigned int)ENE_TX_SMLP_MASK);
+ dev->tx_sample -= raw_tx;
+
+ if (dev->tx_sample_pulse)
+ raw_tx |= ENE_TX_PULSE_MASK;
+
+ ene_hw_write_reg(dev, ENE_TX_INPUT1 + dev->tx_reg, raw_tx);
+ dev->tx_reg = !dev->tx_reg;
+exit:
+ /* simulate TX done interrupt */
+ if (txsim)
+ mod_timer(&dev->tx_sim_timer, jiffies + HZ / 500);
+}
+
+/* timer to simulate tx done interrupt */
+static void ene_tx_irqsim(unsigned long data)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ ene_tx_sample(dev);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+
+/* read irq status and ack it */
+static int ene_irq_status(struct ene_device *dev)
+{
+ u8 irq_status;
+ u8 fw_flags1, fw_flags2;
+ int cur_rx_pointer;
+ int retval = 0;
+
+ fw_flags2 = ene_hw_read_reg(dev, ENE_FW2);
+ cur_rx_pointer = !!(fw_flags2 & ENE_FW2_BUF_HIGH);
+
+ if (dev->hw_revision < ENE_HW_C) {
+ irq_status = ene_hw_read_reg(dev, ENEB_IRQ_STATUS);
+
+ if (!(irq_status & ENEB_IRQ_STATUS_IR))
+ return 0;
+
+ ene_hw_write_reg(dev, ENEB_IRQ_STATUS,
+ irq_status & ~ENEB_IRQ_STATUS_IR);
+ dev->rx_pointer = cur_rx_pointer;
+ return ENE_IRQ_RX;
+ }
+
+ irq_status = ene_hw_read_reg(dev, ENEC_IRQ);
+
+ if (!(irq_status & ENEC_IRQ_STATUS))
+ return 0;
+
+ /* original driver does that twice - a workaround ? */
+ ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
+ ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
+
+ /* clear unknown flag in F8F9 */
+ if (fw_flags2 & ENE_FW2_IRQ_CLR)
+ ene_hw_write_reg(dev, ENE_FW2, fw_flags2 & ~ENE_FW2_IRQ_CLR);
+
+ /* check if this is a TX interrupt */
+ fw_flags1 = ene_hw_read_reg(dev, ENE_FW1);
+ if (fw_flags1 & ENE_FW1_TXIRQ) {
+ ene_hw_write_reg(dev, ENE_FW1, fw_flags1 & ~ENE_FW1_TXIRQ);
+ retval |= ENE_IRQ_TX;
+ }
+
+ /* Check if this is RX interrupt */
+ if (dev->rx_pointer != cur_rx_pointer) {
+ retval |= ENE_IRQ_RX;
+ dev->rx_pointer = cur_rx_pointer;
+
+ } else if (!(retval & ENE_IRQ_TX)) {
+ ene_dbg("RX: interrupt without change in RX pointer(%d)",
+ dev->rx_pointer);
+ retval |= ENE_IRQ_RX;
+ }
+
+ if ((retval & ENE_IRQ_RX) && (retval & ENE_IRQ_TX))
+ ene_dbg("both RX and TX interrupt at same time");
+
+ return retval;
+}
+
+/* interrupt handler */
+static irqreturn_t ene_isr(int irq, void *data)
+{
+ u16 hw_value;
+ int i, hw_sample;
+ int pulse;
+ int irq_status;
+ unsigned long flags;
+ int carrier = 0;
+ irqreturn_t retval = IRQ_NONE;
+ struct ene_device *dev = (struct ene_device *)data;
+ struct ir_raw_event ev;
+
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ irq_status = ene_irq_status(dev);
+
+ if (!irq_status)
+ goto unlock;
+
+ retval = IRQ_HANDLED;
+
+ if (irq_status & ENE_IRQ_TX) {
+
+ if (!dev->hw_learning_and_tx_capable) {
+ ene_dbg("TX interrupt on unsupported device!");
+ goto unlock;
+ }
+ ene_tx_sample(dev);
+ }
+
+ if (!(irq_status & ENE_IRQ_RX))
+ goto unlock;
+
+
+ if (dev->carrier_detect_enabled || debug)
+ carrier = ene_rx_sense_carrier(dev);
+#if 0
+ /* TODO */
+ if (dev->carrier_detect_enabled && carrier)
+ ir_raw_event_report_frequency(dev->idev, carrier);
+#endif
+
+ for (i = 0; i < ENE_SAMPLES_SIZE; i++) {
+ hw_value = ene_hw_read_reg(dev,
+ ENE_SAMPLE_BUFFER + dev->rx_pointer * 4 + i);
+
+ if (dev->rx_fan_input_inuse) {
+ /* read high part of the sample */
+ hw_value |= ene_hw_read_reg(dev,
+ ENE_SAMPLE_BUFFER_FAN +
+ dev->rx_pointer * 4 + i) << 8;
+ pulse = hw_value & ENE_FAN_SMPL_PULS_MSK;
+
+ /* clear space bit, and other unused bits */
+ hw_value &= ENE_FAN_VALUE_MASK;
+ hw_sample = hw_value * ENE_SAMPLE_PERIOD_FAN;
+
+ } else {
+ pulse = !(hw_value & ENE_SAMPLE_SPC_MASK);
+ hw_value &= ENE_SAMPLE_VALUE_MASK;
+ hw_sample = hw_value * sample_period;
+
+ if (dev->rx_period_adjust) {
+ hw_sample *= (100 - dev->rx_period_adjust);
+ hw_sample /= 100;
+ }
+ }
+ /* no more data */
+ if (!(hw_value))
+ break;
+
+ ene_dbg("RX: %d (%s)", hw_sample, pulse ? "pulse" : "space");
+
+
+ ev.duration = hw_sample * 1000;
+ ev.pulse = pulse;
+ ir_raw_event_store_with_filter(dev->idev, &ev);
+ }
+
+ ir_raw_event_handle(dev->idev);
+unlock:
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+ return retval;
+}
+
+/* Initialize default settings */
+static void ene_setup_settings(struct ene_device *dev)
+{
+ dev->tx_period = 32;
+ dev->tx_duty_cycle = 25; /*%*/
+ dev->transmitter_mask = 3;
+
+ /* Force learning mode if (input == 2), otherwise
+ let user set it with LIRC_SET_REC_CARRIER */
+ dev->learning_enabled =
+ (input == 2 && dev->hw_learning_and_tx_capable);
+
+ dev->rx_pointer = -1;
+
+}
+
+/* outside interface: called on first open*/
+static int ene_open(void *data)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ dev->in_use = 1;
+ ene_setup_settings(dev);
+ ene_rx_enable(dev);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+ return 0;
+}
+
+/* outside interface: called on device close*/
+static void ene_close(void *data)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+ spin_lock_irqsave(&dev->hw_lock, flags);
+
+ ene_rx_disable(dev);
+ dev->in_use = 0;
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+/* outside interface: set transmitter mask */
+static int ene_set_tx_mask(void *data, u32 tx_mask)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+ ene_dbg("TX: attempt to set transmitter mask %02x", tx_mask);
+
+ /* invalid txmask */
+ if (!tx_mask || tx_mask & ~0x3) {
+ ene_dbg("TX: invalid mask");
+ /* return count of transmitters */
+ return 2;
+ }
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ dev->transmitter_mask = tx_mask;
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+ return 0;
+}
+
+/* outside interface : set tx carrier */
+static int ene_set_tx_carrier(void *data, u32 carrier)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+ u32 period = 1000000 / carrier; /* (1 / freq) (* # usec in 1 sec) */
+
+ ene_dbg("TX: attempt to set tx carrier to %d kHz", carrier);
+
+ if (period && (period > ENE_TX_PERIOD_MAX ||
+ period < ENE_TX_PERIOD_MIN)) {
+
+ ene_dbg("TX: out of range %d-%d carrier, "
+ "falling back to 32 kHz",
+ 1000 / ENE_TX_PERIOD_MIN,
+ 1000 / ENE_TX_PERIOD_MAX);
+
+ period = 32; /* this is just a coincidence!!! */
+ }
+ ene_dbg("TX: set carrier to %d kHz", carrier);
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ dev->tx_period = period;
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+ return 0;
+}
+
+
+/* outside interface: enable learning mode */
+static int ene_set_learning_mode(void *data, int enable)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+ if (enable == dev->learning_enabled)
+ return 0;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ dev->learning_enabled = enable;
+ ene_rx_set_inputs(dev);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+ return 0;
+}
+
+/* outside interface: set rec carrier */
+static int ene_set_rec_carrier(void *data, u32 min, u32 max)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ ene_set_learning_mode(dev,
+ max > ENE_NORMAL_RX_HI || min < ENE_NORMAL_RX_LOW);
+ return 0;
+}
+
+/* outside interface: enable or disable idle mode */
+static void ene_rx_set_idle(void *data, int idle)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ ene_dbg("%sabling idle mode", idle ? "en" : "dis");
+
+ ene_hw_write_reg_mask(dev, ENE_CIR_SAMPLE_PERIOD,
+ (enable_idle && idle) ? 0 : ENE_CIR_SAMPLE_OVERFLOW,
+ ENE_CIR_SAMPLE_OVERFLOW);
+}
+
+
+/* outside interface: transmit */
+static int ene_transmit(void *data, int *buf, u32 n)
+{
+ struct ene_device *dev = (struct ene_device *)data;
+ unsigned long flags;
+
+ dev->tx_buffer = buf;
+ dev->tx_len = n / sizeof(int);
+ dev->tx_pos = 0;
+ dev->tx_reg = 0;
+ dev->tx_done = 0;
+ dev->tx_sample = 0;
+ dev->tx_sample_pulse = 0;
+
+ ene_dbg("TX: %d samples", dev->tx_len);
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+
+ ene_tx_hw_set_transmiter_mask(dev);
+ ene_tx_prepare(dev);
+
+ /* Transmit first two samples */
+ ene_tx_sample(dev);
+ ene_tx_sample(dev);
+
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+
+ if (wait_for_completion_timeout(&dev->tx_complete, 2 * HZ) == 0) {
+ ene_dbg("TX: timeout");
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ ene_tx_complete(dev);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+ } else
+ ene_dbg("TX: done");
+ return n;
+}
+
+
+/* probe entry */
+static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
+{
+ int error = -ENOMEM;
+ struct ir_dev_props *ir_props;
+ struct input_dev *input_dev;
+ struct ene_device *dev;
+
+ /* allocate memory */
+ input_dev = input_allocate_device();
+ ir_props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL);
+ dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL);
+
+ if (!input_dev || !ir_props || !dev)
+ goto error;
+
+ /* validate resources */
+ error = -ENODEV;
+
+ if (!pnp_port_valid(pnp_dev, 0) ||
+ pnp_port_len(pnp_dev, 0) < ENE_MAX_IO)
+ goto error;
+
+ if (!pnp_irq_valid(pnp_dev, 0))
+ goto error;
+
+ dev->hw_io = pnp_port_start(pnp_dev, 0);
+ dev->irq = pnp_irq(pnp_dev, 0);
+ spin_lock_init(&dev->hw_lock);
+
+ /* claim the resources */
+ error = -EBUSY;
+ if (!request_region(dev->hw_io, ENE_MAX_IO, ENE_DRIVER_NAME))
+ goto error;
+
+ if (request_irq(dev->irq, ene_isr,
+ IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev))
+ goto error;
+
+ pnp_set_drvdata(pnp_dev, dev);
+ dev->pnp_dev = pnp_dev;
+
+ /* detect hardware version and features */
+ error = ene_hw_detect(dev);
+ if (error)
+ goto error;
+
+ ene_setup_settings(dev);
+
+ if (!dev->hw_learning_and_tx_capable && txsim) {
+ dev->hw_learning_and_tx_capable = 1;
+ setup_timer(&dev->tx_sim_timer, ene_tx_irqsim,
+ (long unsigned int)dev);
+ ene_printk(KERN_WARNING,
+ "Simulation of TX activated\n");
+ }
+
+ ir_props->driver_type = RC_DRIVER_IR_RAW;
+ ir_props->allowed_protos = IR_TYPE_ALL;
+ ir_props->priv = dev;
+ ir_props->open = ene_open;
+ ir_props->close = ene_close;
+ ir_props->min_timeout = ENE_MINGAP * 1000;
+ ir_props->max_timeout = ENE_MAXGAP * 1000;
+ ir_props->timeout = ENE_MAXGAP * 1000;
+
+ if (dev->hw_revision == ENE_HW_B)
+ ir_props->s_idle = ene_rx_set_idle;
+
+
+ dev->props = ir_props;
+ dev->idev = input_dev;
+
+ /* don't allow too short/long sample periods */
+ if (sample_period < 5 || sample_period > 0x7F)
+ sample_period = -1;
+
+ /* choose default sample period */
+ if (sample_period == -1) {
+
+ sample_period = 50;
+
+ /* on revB, hardware idle mode eats first sample
+ if we set too low sample period */
+ if (dev->hw_revision == ENE_HW_B && enable_idle)
+ sample_period = 75;
+ }
+
+ ir_props->rx_resolution = sample_period * 1000;
+
+ if (dev->hw_learning_and_tx_capable) {
+
+ ir_props->s_learning_mode = ene_set_learning_mode;
+
+ if (input == 0)
+ ir_props->s_rx_carrier_range = ene_set_rec_carrier;
+
+ init_completion(&dev->tx_complete);
+ ir_props->tx_ir = ene_transmit;
+ ir_props->s_tx_mask = ene_set_tx_mask;
+ ir_props->s_tx_carrier = ene_set_tx_carrier;
+ ir_props->tx_resolution = ENE_TX_SMPL_PERIOD * 1000;
+ /* ir_props->s_carrier_report = ene_set_carrier_report; */
+ }
+
+
+ device_set_wakeup_capable(&pnp_dev->dev, 1);
+ device_set_wakeup_enable(&pnp_dev->dev, 1);
+
+ if (dev->hw_learning_and_tx_capable)
+ input_dev->name = "ENE eHome Infrared Remote Transceiver";
+ else
+ input_dev->name = "ENE eHome Infrared Remote Receiver";
+
+
+ error = -ENODEV;
+ if (ir_input_register(input_dev, RC_MAP_RC6_MCE, ir_props,
+ ENE_DRIVER_NAME))
+ goto error;
+
+
+ ene_printk(KERN_NOTICE, "driver has been succesfully loaded\n");
+ return 0;
+error:
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+ if (dev->hw_io)
+ release_region(dev->hw_io, ENE_MAX_IO);
+
+ input_free_device(input_dev);
+ kfree(ir_props);
+ kfree(dev);
+ return error;
+}
+
+/* main unload function */
+static void ene_remove(struct pnp_dev *pnp_dev)
+{
+ struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->hw_lock, flags);
+ ene_rx_disable(dev);
+ spin_unlock_irqrestore(&dev->hw_lock, flags);
+
+ free_irq(dev->irq, dev);
+ release_region(dev->hw_io, ENE_MAX_IO);
+ ir_input_unregister(dev->idev);
+ kfree(dev->props);
+ kfree(dev);
+}
+
+/* enable wake on IR (wakes on specific button on original remote) */
+static void ene_enable_wake(struct ene_device *dev, int enable)
+{
+ enable = enable && device_may_wakeup(&dev->pnp_dev->dev);
+
+ ene_dbg("wake on IR %s", enable ? "enabled" : "disabled");
+
+ ene_hw_write_reg_mask(dev, ENE_FW1, enable ?
+ ENE_FW1_WAKE : 0, ENE_FW1_WAKE);
+}
+
+#ifdef CONFIG_PM
+static int ene_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
+{
+ struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+ ene_enable_wake(dev, 1);
+ return 0;
+}
+
+static int ene_resume(struct pnp_dev *pnp_dev)
+{
+ struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+ if (dev->in_use)
+ ene_rx_enable(dev);
+
+ ene_enable_wake(dev, 0);
+ return 0;
+}
+#endif
+
+static void ene_shutdown(struct pnp_dev *pnp_dev)
+{
+ struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+ ene_enable_wake(dev, 1);
+}
+
+static const struct pnp_device_id ene_ids[] = {
+ {.id = "ENE0100",},
+ {.id = "ENE0200",},
+ {.id = "ENE0201",},
+ {.id = "ENE0202",},
+ {},
+};
+
+static struct pnp_driver ene_driver = {
+ .name = ENE_DRIVER_NAME,
+ .id_table = ene_ids,
+ .flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
+
+ .probe = ene_probe,
+ .remove = __devexit_p(ene_remove),
+#ifdef CONFIG_PM
+ .suspend = ene_suspend,
+ .resume = ene_resume,
+#endif
+ .shutdown = ene_shutdown,
+};
+
+static int __init ene_init(void)
+{
+ return pnp_register_driver(&ene_driver);
+}
+
+static void ene_exit(void)
+{
+ pnp_unregister_driver(&ene_driver);
+}
+
+module_param(sample_period, int, S_IRUGO);
+MODULE_PARM_DESC(sample_period, "Hardware sample period (50 us default)");
+
+module_param(enable_idle, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(enable_idle,
+ "Enables turning off signal sampling after long inactivity time; "
+ "if disabled might help detecting input signal (default: enabled)"
+ " (KB3926B only)");
+
+module_param(input, bool, S_IRUGO);
+MODULE_PARM_DESC(input, "select which input to use "
+ "0 - auto, 1 - standard, 2 - wideband(KB3926C+)");
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debug (debug=2 verbose debug output)");
+
+module_param(txsim, bool, S_IRUGO);
+MODULE_PARM_DESC(txsim,
+ "Simulate TX features on unsupported hardware (dangerous)");
+
+MODULE_DEVICE_TABLE(pnp, ene_ids);
+MODULE_DESCRIPTION
+ ("Infrared input driver for KB3926B/KB3926C/KB3926D "
+ "(aka ENE0100/ENE0200/ENE0201) CIR port");
+
+MODULE_AUTHOR("Maxim Levitsky");
+MODULE_LICENSE("GPL");
+
+module_init(ene_init);
+module_exit(ene_exit);
--- /dev/null
+/*
+ * driver for ENE KB3926 B/C/D CIR (also known as ENE0XXX)
+ *
+ * Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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/spinlock.h>
+
+
+/* hardware address */
+#define ENE_STATUS 0 /* hardware status - unused */
+#define ENE_ADDR_HI 1 /* hi byte of register address */
+#define ENE_ADDR_LO 2 /* low byte of register address */
+#define ENE_IO 3 /* read/write window */
+#define ENE_MAX_IO 4
+
+/* 8 bytes of samples, divided in 2 halfs*/
+#define ENE_SAMPLE_BUFFER 0xF8F0 /* regular sample buffer */
+#define ENE_SAMPLE_SPC_MASK 0x80 /* sample is space */
+#define ENE_SAMPLE_VALUE_MASK 0x7F
+#define ENE_SAMPLE_OVERFLOW 0x7F
+#define ENE_SAMPLES_SIZE 4
+
+/* fan input sample buffer */
+#define ENE_SAMPLE_BUFFER_FAN 0xF8FB /* this buffer holds high byte of */
+ /* each sample of normal buffer */
+#define ENE_FAN_SMPL_PULS_MSK 0x8000 /* this bit of combined sample */
+ /* if set, says that sample is pulse */
+#define ENE_FAN_VALUE_MASK 0x0FFF /* mask for valid bits of the value */
+
+/* first firmware register */
+#define ENE_FW1 0xF8F8
+#define ENE_FW1_ENABLE 0x01 /* enable fw processing */
+#define ENE_FW1_TXIRQ 0x02 /* TX interrupt pending */
+#define ENE_FW1_WAKE 0x40 /* enable wake from S3 */
+#define ENE_FW1_IRQ 0x80 /* enable interrupt */
+
+/* second firmware register */
+#define ENE_FW2 0xF8F9
+#define ENE_FW2_BUF_HIGH 0x01 /* which half of the buffer to read */
+#define ENE_FW2_IRQ_CLR 0x04 /* clear this on IRQ */
+#define ENE_FW2_GP40_AS_LEARN 0x08 /* normal input is used as */
+ /* learning input */
+#define ENE_FW2_FAN_AS_NRML_IN 0x40 /* fan is used as normal input */
+#define ENE_FW2_LEARNING 0x80 /* hardware supports learning and TX */
+
+/* transmitter ports */
+#define ENE_TX_PORT2 0xFC01 /* this enables one or both */
+#define ENE_TX_PORT2_EN 0x20 /* TX ports */
+#define ENE_TX_PORT1 0xFC08
+#define ENE_TX_PORT1_EN 0x02
+
+/* IRQ registers block (for revision B) */
+#define ENEB_IRQ 0xFD09 /* IRQ number */
+#define ENEB_IRQ_UNK1 0xFD17 /* unknown setting = 1 */
+#define ENEB_IRQ_STATUS 0xFD80 /* irq status */
+#define ENEB_IRQ_STATUS_IR 0x20 /* IR irq */
+
+/* fan as input settings - only if learning capable */
+#define ENE_FAN_AS_IN1 0xFE30 /* fan init reg 1 */
+#define ENE_FAN_AS_IN1_EN 0xCD
+#define ENE_FAN_AS_IN2 0xFE31 /* fan init reg 2 */
+#define ENE_FAN_AS_IN2_EN 0x03
+#define ENE_SAMPLE_PERIOD_FAN 61 /* fan input has fixed sample period */
+
+/* IRQ registers block (for revision C,D) */
+#define ENEC_IRQ 0xFE9B /* new irq settings register */
+#define ENEC_IRQ_MASK 0x0F /* irq number mask */
+#define ENEC_IRQ_UNK_EN 0x10 /* always enabled */
+#define ENEC_IRQ_STATUS 0x20 /* irq status and ACK */
+
+/* CIR block settings */
+#define ENE_CIR_CONF1 0xFEC0
+#define ENE_CIR_CONF1_TX_CLEAR 0x01 /* clear that on revC */
+ /* while transmitting */
+#define ENE_CIR_CONF1_RX_ON 0x07 /* normal receiver enabled */
+#define ENE_CIR_CONF1_LEARN1 0x08 /* enabled on learning mode */
+#define ENE_CIR_CONF1_TX_ON 0x30 /* enabled on transmit */
+#define ENE_CIR_CONF1_TX_CARR 0x80 /* send TX carrier or not */
+
+#define ENE_CIR_CONF2 0xFEC1 /* unknown setting = 0 */
+#define ENE_CIR_CONF2_LEARN2 0x10 /* set on enable learning */
+#define ENE_CIR_CONF2_GPIO40DIS 0x20 /* disable input via gpio40 */
+
+#define ENE_CIR_SAMPLE_PERIOD 0xFEC8 /* sample period in us */
+#define ENE_CIR_SAMPLE_OVERFLOW 0x80 /* interrupt on overflows if set */
+
+
+/* Two byte tx buffer */
+#define ENE_TX_INPUT1 0xFEC9
+#define ENE_TX_INPUT2 0xFECA
+#define ENE_TX_PULSE_MASK 0x80 /* Transmitted sample is pulse */
+#define ENE_TX_SMLP_MASK 0x7F
+#define ENE_TX_SMPL_PERIOD 50 /* transmit sample period - fixed */
+
+
+/* Unknown TX setting - TX sample period ??? */
+#define ENE_TX_UNK1 0xFECB /* set to 0x63 */
+
+/* Current received carrier period */
+#define ENE_RX_CARRIER 0xFECC /* RX period (500 ns) */
+#define ENE_RX_CARRIER_VALID 0x80 /* Register content valid */
+
+
+/* TX period (1/carrier) */
+#define ENE_TX_PERIOD 0xFECE /* TX period (500 ns) */
+#define ENE_TX_PERIOD_UNKBIT 0x80 /* This bit set on transmit*/
+#define ENE_TX_PERIOD_PULSE 0xFECF /* TX pulse period (500 ns)*/
+
+/* Hardware versions */
+#define ENE_HW_VERSION 0xFF00 /* hardware revision */
+#define ENE_PLLFRH 0xFF16
+#define ENE_PLLFRL 0xFF17
+
+#define ENE_HW_UNK 0xFF1D
+#define ENE_HW_UNK_CLR 0x04
+#define ENE_HW_VER_MAJOR 0xFF1E /* chip version */
+#define ENE_HW_VER_MINOR 0xFF1F
+#define ENE_HW_VER_OLD 0xFD00
+
+/* Normal/Learning carrier ranges - only valid if we have learning input*/
+/* TODO: test */
+#define ENE_NORMAL_RX_LOW 34
+#define ENE_NORMAL_RX_HI 38
+
+/* Tx carrier range */
+/* Hardware might be able to do more, but this range is enough for
+ all purposes */
+#define ENE_TX_PERIOD_MAX 32 /* corresponds to 29.4 kHz */
+#define ENE_TX_PERIOD_MIN 16 /* corrsponds to 62.5 kHz */
+
+
+
+/* Minimal and maximal gaps */
+
+/* Normal case:
+ Minimal gap is 0x7F * sample period
+ Maximum gap depends on hardware.
+ For KB3926B, it is unlimited, for newer models its around
+ 250000, after which HW stops sending samples, and that is
+ not possible to change */
+
+/* Fan case:
+ Both minimal and maximal gaps are same, and equal to 0xFFF * 0x61
+ And there is nothing to change this setting
+*/
+
+#define ENE_MAXGAP 250000
+#define ENE_MINGAP (127 * sample_period)
+
+/******************************************************************************/
+
+#define ENE_DRIVER_NAME "ene_ir"
+
+#define ENE_IRQ_RX 1
+#define ENE_IRQ_TX 2
+
+#define ENE_HW_B 1 /* 3926B */
+#define ENE_HW_C 2 /* 3926C */
+#define ENE_HW_D 3 /* 3926D */
+
+#define ene_printk(level, text, ...) \
+ printk(level ENE_DRIVER_NAME ": " text, ## __VA_ARGS__)
+
+#define ene_dbg(text, ...) \
+ if (debug) \
+ printk(KERN_DEBUG \
+ ENE_DRIVER_NAME ": " text "\n" , ## __VA_ARGS__)
+
+#define ene_dbg_verbose(text, ...) \
+ if (debug > 1) \
+ printk(KERN_DEBUG \
+ ENE_DRIVER_NAME ": " text "\n" , ## __VA_ARGS__)
+
+
+struct ene_device {
+ struct pnp_dev *pnp_dev;
+ struct input_dev *idev;
+ struct ir_dev_props *props;
+ int in_use;
+
+ /* hw IO settings */
+ unsigned long hw_io;
+ int irq;
+ spinlock_t hw_lock;
+
+ /* HW features */
+ int hw_revision; /* hardware revision */
+ bool hw_learning_and_tx_capable; /* learning capable */
+ bool hw_gpio40_learning; /* gpio40 is learning */
+ bool hw_fan_as_normal_input; /* fan input is used as */
+ /* regular input */
+ /* HW state*/
+ int rx_pointer; /* hw pointer to rx buffer */
+ bool rx_fan_input_inuse; /* is fan input in use for rx*/
+ int tx_reg; /* current reg used for TX */
+ u8 saved_conf1; /* saved FEC0 reg */
+
+ /* TX sample handling */
+ unsigned int tx_sample; /* current sample for TX */
+ bool tx_sample_pulse; /* current sample is pulse */
+
+ /* TX buffer */
+ int *tx_buffer; /* input samples buffer*/
+ int tx_pos; /* position in that bufer */
+ int tx_len; /* current len of tx buffer */
+ int tx_done; /* done transmitting */
+ /* one more sample pending*/
+ struct completion tx_complete; /* TX completion */
+ struct timer_list tx_sim_timer;
+
+ /* TX settings */
+ int tx_period;
+ int tx_duty_cycle;
+ int transmitter_mask;
+
+ /* RX settings */
+ bool learning_enabled; /* learning input enabled */
+ bool carrier_detect_enabled; /* carrier detect enabled */
+ int rx_period_adjust;
+};
struct imon_context {
struct device *dev;
struct ir_dev_props *props;
- struct ir_input_dev *ir;
/* Newer devices have two interfaces */
struct usb_device *usbdev_intf0;
struct usb_device *usbdev_intf1;
{
struct input_dev *idev;
struct ir_dev_props *props;
- struct ir_input_dev *ir;
int ret, i;
idev = input_allocate_device();
goto props_alloc_failed;
}
- ir = kzalloc(sizeof(struct ir_input_dev), GFP_KERNEL);
- if (!ir) {
- dev_err(ictx->dev, "remote ir input dev allocation failed\n");
- goto ir_dev_alloc_failed;
- }
-
snprintf(ictx->name_idev, sizeof(ictx->name_idev),
"iMON Remote (%04x:%04x)", ictx->vendor, ictx->product);
idev->name = ictx->name_idev;
props->change_protocol = imon_ir_change_protocol;
ictx->props = props;
- ictx->ir = ir;
- memcpy(&ir->dev, ictx->dev, sizeof(struct device));
-
usb_to_input_id(ictx->usbdev_intf0, &idev->id);
idev->dev.parent = ictx->dev;
- input_set_drvdata(idev, ir);
-
ret = ir_input_register(idev, RC_MAP_IMON_PAD, props, MOD_NAME);
if (ret < 0) {
dev_err(ictx->dev, "remote input dev register failed\n");
return idev;
idev_register_failed:
- kfree(ir);
-ir_dev_alloc_failed:
kfree(props);
props_alloc_failed:
input_free_device(idev);
urb_submit_failed:
ir_input_unregister(ictx->idev);
- input_free_device(ictx->idev);
idev_setup_failed:
find_endpoint_failed:
mutex_unlock(&ictx->lock);
return ictx;
urb_submit_failed:
- if (ictx->touch) {
+ if (ictx->touch)
input_unregister_device(ictx->touch);
- input_free_device(ictx->touch);
- }
touch_setup_failed:
find_endpoint_failed:
mutex_unlock(&ictx->lock);
struct ir_raw_event_ctrl {
struct list_head list; /* to keep track of raw clients */
- struct work_struct rx_work; /* for the rx decoding workqueue */
+ struct task_struct *thread;
struct kfifo kfifo; /* fifo for the pulse/space durations */
ktime_t last_event; /* when last event occurred */
enum raw_event_type last_type; /* last event type */
/* raw decoder state follows */
struct ir_raw_event prev_ev;
+ struct ir_raw_event this_ev;
struct nec_dec {
int state;
unsigned count;
u32 bits;
+ bool is_nec_x;
+ bool necx_repeat;
} nec;
struct rc5_dec {
int state;
struct lirc_codec {
struct ir_input_dev *ir_dev;
struct lirc_driver *drv;
- int lircdata;
+ int carrier_low;
} lirc;
};
ev->duration -= duration;
}
-#define TO_US(duration) (((duration) + 500) / 1000)
+#define TO_US(duration) DIV_ROUND_CLOSEST((duration), 1000)
#define TO_STR(is_pulse) ((is_pulse) ? "pulse" : "space")
#define IS_RESET(ev) (ev.duration == 0)
-
/*
* Routines from ir-sysfs.c - Meant to be called only internally inside
* ir-core
void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler);
void ir_raw_init(void);
-
+int ir_rcmap_init(void);
+void ir_rcmap_cleanup(void);
/*
* Decoder initialization code
*
STATE_BIT_SPACE,
STATE_TRAILER_PULSE,
STATE_TRAILER_SPACE,
+ STATE_CHECK_REPEAT,
};
/**
IR_dprintk(2, "JVC decode started at state %d (%uus %s)\n",
data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+again:
switch (data->state) {
case STATE_INACTIVE:
}
data->count = 0;
- data->state = STATE_BIT_PULSE;
+ data->state = STATE_CHECK_REPEAT;
return 0;
+
+ case STATE_CHECK_REPEAT:
+ if (!ev.pulse)
+ break;
+
+ if (eq_margin(ev.duration, JVC_HEADER_PULSE, JVC_UNIT / 2))
+ data->state = STATE_INACTIVE;
+ else
+ data->state = STATE_BIT_PULSE;
+ goto again;
}
out:
spin_lock_irqsave(&ir->keylock, flags);
+ input_event(dev, EV_MSC, MSC_SCAN, ir->last_scancode);
+
if (!ir->keypressed)
goto out;
spin_lock_irqsave(&ir->keylock, flags);
+ input_event(dev, EV_MSC, MSC_SCAN, scancode);
+
/* Repeat event? */
if (ir->keypressed &&
ir->last_scancode == scancode &&
ir->last_toggle = toggle;
ir->last_keycode = keycode;
+
if (keycode == KEY_RESERVED)
goto out;
+
/* Register a keypress */
ir->keypressed = true;
IR_dprintk(1, "%s: key down event, key 0x%04x, scancode 0x%04x\n",
*/
int __ir_input_register(struct input_dev *input_dev,
const struct ir_scancode_table *rc_tab,
- const struct ir_dev_props *props,
+ struct ir_dev_props *props,
const char *driver_name)
{
struct ir_input_dev *ir_dev;
set_bit(EV_KEY, input_dev->evbit);
set_bit(EV_REP, input_dev->evbit);
+ set_bit(EV_MSC, input_dev->evbit);
+ set_bit(MSC_SCAN, input_dev->mscbit);
if (ir_setkeytable(input_dev, &ir_dev->rc_tab, rc_tab)) {
rc = -ENOMEM;
IR_dprintk(1, "Registered input device on %s for %s remote%s.\n",
driver_name, rc_tab->name,
- ir_dev->props->driver_type == RC_DRIVER_IR_RAW ? " in raw mode" : "");
+ (ir_dev->props && ir_dev->props->driver_type == RC_DRIVER_IR_RAW) ?
+ " in raw mode" : "");
return 0;
static int ir_lirc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
{
struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+ int sample;
if (!(ir_dev->raw->enabled_protocols & IR_TYPE_LIRC))
return 0;
if (!ir_dev->raw->lirc.drv || !ir_dev->raw->lirc.drv->rbuf)
return -EINVAL;
+ if (IS_RESET(ev))
+ return 0;
+
IR_dprintk(2, "LIRC data transfer started (%uus %s)\n",
TO_US(ev.duration), TO_STR(ev.pulse));
- ir_dev->raw->lirc.lircdata += ev.duration / 1000;
+ sample = ev.duration / 1000;
if (ev.pulse)
- ir_dev->raw->lirc.lircdata |= PULSE_BIT;
+ sample |= PULSE_BIT;
lirc_buffer_write(ir_dev->raw->lirc.drv->rbuf,
- (unsigned char *) &ir_dev->raw->lirc.lircdata);
+ (unsigned char *) &sample);
wake_up(&ir_dev->raw->lirc.drv->rbuf->wait_poll);
- ir_dev->raw->lirc.lircdata = 0;
return 0;
}
return ret;
}
-static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
+ unsigned long __user arg)
{
struct lirc_codec *lirc;
struct ir_input_dev *ir_dev;
int ret = 0;
void *drv_data;
- unsigned long val;
+ unsigned long val = 0;
lirc = lirc_get_pdata(filep);
if (!lirc)
drv_data = ir_dev->props->priv;
- switch (cmd) {
- case LIRC_SET_TRANSMITTER_MASK:
+ if (_IOC_DIR(cmd) & _IOC_WRITE) {
ret = get_user(val, (unsigned long *)arg);
if (ret)
return ret;
+ }
+
+ switch (cmd) {
- if (ir_dev->props && ir_dev->props->s_tx_mask)
+ /* legacy support */
+ case LIRC_GET_SEND_MODE:
+ val = LIRC_CAN_SEND_PULSE & LIRC_CAN_SEND_MASK;
+ break;
+
+ case LIRC_SET_SEND_MODE:
+ if (val != (LIRC_MODE_PULSE & LIRC_CAN_SEND_MASK))
+ return -EINVAL;
+ break;
+
+ /* TX settings */
+ case LIRC_SET_TRANSMITTER_MASK:
+ if (ir_dev->props->s_tx_mask)
ret = ir_dev->props->s_tx_mask(drv_data, (u32)val);
else
return -EINVAL;
break;
case LIRC_SET_SEND_CARRIER:
- ret = get_user(val, (unsigned long *)arg);
- if (ret)
- return ret;
-
- if (ir_dev->props && ir_dev->props->s_tx_carrier)
+ if (ir_dev->props->s_tx_carrier)
ir_dev->props->s_tx_carrier(drv_data, (u32)val);
else
return -EINVAL;
break;
- case LIRC_GET_SEND_MODE:
- val = LIRC_CAN_SEND_PULSE & LIRC_CAN_SEND_MASK;
- ret = put_user(val, (unsigned long *)arg);
+ case LIRC_SET_SEND_DUTY_CYCLE:
+ if (!ir_dev->props->s_tx_duty_cycle)
+ return -ENOSYS;
+
+ if (val <= 0 || val >= 100)
+ return -EINVAL;
+
+ ir_dev->props->s_tx_duty_cycle(ir_dev->props->priv, val);
break;
- case LIRC_SET_SEND_MODE:
- ret = get_user(val, (unsigned long *)arg);
- if (ret)
- return ret;
+ /* RX settings */
+ case LIRC_SET_REC_CARRIER:
+ if (ir_dev->props->s_rx_carrier_range)
+ ret = ir_dev->props->s_rx_carrier_range(
+ ir_dev->props->priv,
+ ir_dev->raw->lirc.carrier_low, val);
+ else
+ return -ENOSYS;
- if (val != (LIRC_MODE_PULSE & LIRC_CAN_SEND_MASK))
+ if (!ret)
+ ir_dev->raw->lirc.carrier_low = 0;
+ break;
+
+ case LIRC_SET_REC_CARRIER_RANGE:
+ if (val >= 0)
+ ir_dev->raw->lirc.carrier_low = val;
+ break;
+
+
+ case LIRC_GET_REC_RESOLUTION:
+ val = ir_dev->props->rx_resolution;
+ break;
+
+ case LIRC_SET_WIDEBAND_RECEIVER:
+ if (ir_dev->props->s_learning_mode)
+ return ir_dev->props->s_learning_mode(
+ ir_dev->props->priv, !!val);
+ else
+ return -ENOSYS;
+
+ /* Generic timeout support */
+ case LIRC_GET_MIN_TIMEOUT:
+ if (!ir_dev->props->max_timeout)
+ return -ENOSYS;
+ val = ir_dev->props->min_timeout / 1000;
+ break;
+
+ case LIRC_GET_MAX_TIMEOUT:
+ if (!ir_dev->props->max_timeout)
+ return -ENOSYS;
+ val = ir_dev->props->max_timeout / 1000;
+ break;
+
+ case LIRC_SET_REC_TIMEOUT:
+ if (val < ir_dev->props->min_timeout ||
+ val > ir_dev->props->max_timeout)
return -EINVAL;
+ ir_dev->props->timeout = val * 1000;
break;
default:
return lirc_dev_fop_ioctl(filep, cmd, arg);
}
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ ret = put_user(val, (unsigned long *)arg);
+
return ret;
}
features = LIRC_CAN_REC_MODE2;
if (ir_dev->props->tx_ir) {
+
features |= LIRC_CAN_SEND_PULSE;
if (ir_dev->props->s_tx_mask)
features |= LIRC_CAN_SET_TRANSMITTER_MASK;
if (ir_dev->props->s_tx_carrier)
features |= LIRC_CAN_SET_SEND_CARRIER;
+
+ if (ir_dev->props->s_tx_duty_cycle)
+ features |= LIRC_CAN_SET_REC_DUTY_CYCLE;
}
+ if (ir_dev->props->s_rx_carrier_range)
+ features |= LIRC_CAN_SET_REC_CARRIER |
+ LIRC_CAN_SET_REC_CARRIER_RANGE;
+
+ if (ir_dev->props->s_learning_mode)
+ features |= LIRC_CAN_USE_WIDEBAND_RECEIVER;
+
+ if (ir_dev->props->max_timeout)
+ features |= LIRC_CAN_SET_REC_TIMEOUT;
+
+
snprintf(drv->name, sizeof(drv->name), "ir-lirc-codec (%s)",
ir_dev->driver_name);
drv->minor = -1;
ir_dev->raw->lirc.drv = drv;
ir_dev->raw->lirc.ir_dev = ir_dev;
- ir_dev->raw->lirc.lircdata = PULSE_MASK;
-
return 0;
lirc_register_failed:
#define NEC_HEADER_PULSE (16 * NEC_UNIT)
#define NECX_HEADER_PULSE (8 * NEC_UNIT) /* Less common NEC variant */
#define NEC_HEADER_SPACE (8 * NEC_UNIT)
-#define NEC_REPEAT_SPACE (8 * NEC_UNIT)
+#define NEC_REPEAT_SPACE (4 * NEC_UNIT)
#define NEC_BIT_PULSE (1 * NEC_UNIT)
#define NEC_BIT_0_SPACE (1 * NEC_UNIT)
#define NEC_BIT_1_SPACE (3 * NEC_UNIT)
#define NEC_TRAILER_PULSE (1 * NEC_UNIT)
#define NEC_TRAILER_SPACE (10 * NEC_UNIT) /* even longer in reality */
+#define NECX_REPEAT_BITS 1
enum nec_state {
STATE_INACTIVE,
if (!ev.pulse)
break;
- if (!eq_margin(ev.duration, NEC_HEADER_PULSE, NEC_UNIT / 2) &&
- !eq_margin(ev.duration, NECX_HEADER_PULSE, NEC_UNIT / 2))
+ if (eq_margin(ev.duration, NEC_HEADER_PULSE, NEC_UNIT / 2)) {
+ data->is_nec_x = false;
+ data->necx_repeat = false;
+ } else if (eq_margin(ev.duration, NECX_HEADER_PULSE, NEC_UNIT / 2))
+ data->is_nec_x = true;
+ else
break;
data->count = 0;
if (ev.pulse)
break;
+ if (data->necx_repeat && data->count == NECX_REPEAT_BITS &&
+ geq_margin(ev.duration,
+ NEC_TRAILER_SPACE, NEC_UNIT / 2)) {
+ IR_dprintk(1, "Repeat last key\n");
+ ir_repeat(input_dev);
+ data->state = STATE_INACTIVE;
+ return 0;
+
+ } else if (data->count > NECX_REPEAT_BITS)
+ data->necx_repeat = false;
+
data->bits <<= 1;
if (eq_margin(ev.duration, NEC_BIT_1_SPACE, NEC_UNIT / 2))
data->bits |= 1;
IR_dprintk(1, "NEC scancode 0x%04x\n", scancode);
}
+ if (data->is_nec_x)
+ data->necx_repeat = true;
+
ir_keydown(input_dev, scancode, 0);
data->state = STATE_INACTIVE;
return 0;
* GNU General Public License for more details.
*/
-#include <linux/workqueue.h>
-#include <linux/spinlock.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
#include <linux/sched.h>
+#include <linux/freezer.h>
#include "ir-core-priv.h"
/* Define the max number of pulse/space transitions to buffer */
static LIST_HEAD(ir_raw_client_list);
/* Used to handle IR raw handler extensions */
-static DEFINE_SPINLOCK(ir_raw_handler_lock);
+static DEFINE_MUTEX(ir_raw_handler_lock);
static LIST_HEAD(ir_raw_handler_list);
static u64 available_protocols;
static struct work_struct wq_load;
#endif
-static void ir_raw_event_work(struct work_struct *work)
+static int ir_raw_event_thread(void *data)
{
struct ir_raw_event ev;
struct ir_raw_handler *handler;
- struct ir_raw_event_ctrl *raw =
- container_of(work, struct ir_raw_event_ctrl, rx_work);
-
- while (kfifo_out(&raw->kfifo, &ev, sizeof(ev)) == sizeof(ev)) {
- spin_lock(&ir_raw_handler_lock);
- list_for_each_entry(handler, &ir_raw_handler_list, list)
- handler->decode(raw->input_dev, ev);
- spin_unlock(&ir_raw_handler_lock);
- raw->prev_ev = ev;
+ struct ir_raw_event_ctrl *raw = (struct ir_raw_event_ctrl *)data;
+
+ while (!kthread_should_stop()) {
+ try_to_freeze();
+
+ mutex_lock(&ir_raw_handler_lock);
+
+ while (kfifo_out(&raw->kfifo, &ev, sizeof(ev)) == sizeof(ev)) {
+ list_for_each_entry(handler, &ir_raw_handler_list, list)
+ handler->decode(raw->input_dev, ev);
+ raw->prev_ev = ev;
+ }
+
+ mutex_unlock(&ir_raw_handler_lock);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
}
+
+ return 0;
}
/**
if (!ir->raw)
return -EINVAL;
+ IR_dprintk(2, "sample: (05%dus %s)\n",
+ TO_US(ev->duration), TO_STR(ev->pulse));
+
if (kfifo_in(&ir->raw->kfifo, ev, sizeof(*ev)) != sizeof(*ev))
return -ENOMEM;
}
EXPORT_SYMBOL_GPL(ir_raw_event_store_edge);
+/**
+ * ir_raw_event_store_with_filter() - pass next pulse/space to decoders with some processing
+ * @input_dev: the struct input_dev device descriptor
+ * @type: the type of the event that has occurred
+ *
+ * This routine (which may be called from an interrupt context) works
+ * in similiar manner to ir_raw_event_store_edge.
+ * This routine is intended for devices with limited internal buffer
+ * It automerges samples of same type, and handles timeouts
+ */
+int ir_raw_event_store_with_filter(struct input_dev *input_dev,
+ struct ir_raw_event *ev)
+{
+ struct ir_input_dev *ir = input_get_drvdata(input_dev);
+ struct ir_raw_event_ctrl *raw = ir->raw;
+
+ if (!raw || !ir->props)
+ return -EINVAL;
+
+ /* Ignore spaces in idle mode */
+ if (ir->idle && !ev->pulse)
+ return 0;
+ else if (ir->idle)
+ ir_raw_event_set_idle(input_dev, 0);
+
+ if (!raw->this_ev.duration) {
+ raw->this_ev = *ev;
+ } else if (ev->pulse == raw->this_ev.pulse) {
+ raw->this_ev.duration += ev->duration;
+ } else {
+ ir_raw_event_store(input_dev, &raw->this_ev);
+ raw->this_ev = *ev;
+ }
+
+ /* Enter idle mode if nessesary */
+ if (!ev->pulse && ir->props->timeout &&
+ raw->this_ev.duration >= ir->props->timeout)
+ ir_raw_event_set_idle(input_dev, 1);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ir_raw_event_store_with_filter);
+
+void ir_raw_event_set_idle(struct input_dev *input_dev, int idle)
+{
+ struct ir_input_dev *ir = input_get_drvdata(input_dev);
+ struct ir_raw_event_ctrl *raw = ir->raw;
+ ktime_t now;
+ u64 delta;
+
+ if (!ir->props)
+ return;
+
+ if (!ir->raw)
+ goto out;
+
+ if (idle) {
+ IR_dprintk(2, "enter idle mode\n");
+ raw->last_event = ktime_get();
+ } else {
+ IR_dprintk(2, "exit idle mode\n");
+
+ now = ktime_get();
+ delta = ktime_to_ns(ktime_sub(now, ir->raw->last_event));
+
+ WARN_ON(raw->this_ev.pulse);
+
+ raw->this_ev.duration =
+ min(raw->this_ev.duration + delta,
+ (u64)IR_MAX_DURATION);
+
+ ir_raw_event_store(input_dev, &raw->this_ev);
+
+ if (raw->this_ev.duration == IR_MAX_DURATION)
+ ir_raw_event_reset(input_dev);
+
+ raw->this_ev.duration = 0;
+ }
+out:
+ if (ir->props->s_idle)
+ ir->props->s_idle(ir->props->priv, idle);
+ ir->idle = idle;
+}
+EXPORT_SYMBOL_GPL(ir_raw_event_set_idle);
+
/**
* ir_raw_event_handle() - schedules the decoding of stored ir data
* @input_dev: the struct input_dev device descriptor
if (!ir->raw)
return;
- schedule_work(&ir->raw->rx_work);
+ wake_up_process(ir->raw->thread);
}
EXPORT_SYMBOL_GPL(ir_raw_event_handle);
ir_raw_get_allowed_protocols()
{
u64 protocols;
- spin_lock(&ir_raw_handler_lock);
+ mutex_lock(&ir_raw_handler_lock);
protocols = available_protocols;
- spin_unlock(&ir_raw_handler_lock);
+ mutex_unlock(&ir_raw_handler_lock);
return protocols;
}
return -ENOMEM;
ir->raw->input_dev = input_dev;
- INIT_WORK(&ir->raw->rx_work, ir_raw_event_work);
+
ir->raw->enabled_protocols = ~0;
rc = kfifo_alloc(&ir->raw->kfifo, sizeof(s64) * MAX_IR_EVENT_SIZE,
GFP_KERNEL);
return rc;
}
- spin_lock(&ir_raw_handler_lock);
+ ir->raw->thread = kthread_run(ir_raw_event_thread, ir->raw,
+ "rc%u", (unsigned int)ir->devno);
+
+ if (IS_ERR(ir->raw->thread)) {
+ kfree(ir->raw);
+ ir->raw = NULL;
+ return PTR_ERR(ir->raw->thread);
+ }
+
+ mutex_lock(&ir_raw_handler_lock);
list_add_tail(&ir->raw->list, &ir_raw_client_list);
list_for_each_entry(handler, &ir_raw_handler_list, list)
if (handler->raw_register)
handler->raw_register(ir->raw->input_dev);
- spin_unlock(&ir_raw_handler_lock);
+ mutex_unlock(&ir_raw_handler_lock);
return 0;
}
if (!ir->raw)
return;
- cancel_work_sync(&ir->raw->rx_work);
+ kthread_stop(ir->raw->thread);
- spin_lock(&ir_raw_handler_lock);
+ mutex_lock(&ir_raw_handler_lock);
list_del(&ir->raw->list);
list_for_each_entry(handler, &ir_raw_handler_list, list)
if (handler->raw_unregister)
handler->raw_unregister(ir->raw->input_dev);
- spin_unlock(&ir_raw_handler_lock);
+ mutex_unlock(&ir_raw_handler_lock);
kfifo_free(&ir->raw->kfifo);
kfree(ir->raw);
{
struct ir_raw_event_ctrl *raw;
- spin_lock(&ir_raw_handler_lock);
+ mutex_lock(&ir_raw_handler_lock);
list_add_tail(&ir_raw_handler->list, &ir_raw_handler_list);
if (ir_raw_handler->raw_register)
list_for_each_entry(raw, &ir_raw_client_list, list)
ir_raw_handler->raw_register(raw->input_dev);
available_protocols |= ir_raw_handler->protocols;
- spin_unlock(&ir_raw_handler_lock);
+ mutex_unlock(&ir_raw_handler_lock);
return 0;
}
{
struct ir_raw_event_ctrl *raw;
- spin_lock(&ir_raw_handler_lock);
+ mutex_lock(&ir_raw_handler_lock);
list_del(&ir_raw_handler->list);
if (ir_raw_handler->raw_unregister)
list_for_each_entry(raw, &ir_raw_client_list, list)
ir_raw_handler->raw_unregister(raw->input_dev);
available_protocols &= ~ir_raw_handler->protocols;
- spin_unlock(&ir_raw_handler_lock);
+ mutex_unlock(&ir_raw_handler_lock);
}
EXPORT_SYMBOL(ir_raw_handler_unregister);
/* Initialize/load the decoders/keymap code that will be used */
ir_raw_init();
+ ir_rcmap_init();
return 0;
}
static void __exit ir_core_exit(void)
{
class_unregister(&ir_input_class);
+ ir_rcmap_cleanup();
}
module_init(ir_core_init);
rc-dm1105-nec.o \
rc-dntv-live-dvb-t.o \
rc-dntv-live-dvbt-pro.o \
- rc-empty.o \
rc-em-terratec.o \
rc-encore-enltv2.o \
rc-encore-enltv.o \
rc-purpletv.o \
rc-pv951.o \
rc-rc5-hauppauge-new.o \
+ rc-rc5-streamzap.o \
rc-rc5-tv.o \
rc-rc6-mce.o \
rc-real-audio-220-32-keys.o \
+++ /dev/null
-/* empty.h - Keytable for empty Remote Controller
- *
- * keymap imported from ir-keymaps.c
- *
- * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <media/rc-map.h>
-
-/* empty keytable, can be used as placeholder for not-yet created keytables */
-
-static struct ir_scancode empty[] = {
- { 0x2a, KEY_COFFEE },
-};
-
-static struct rc_keymap empty_map = {
- .map = {
- .scan = empty,
- .size = ARRAY_SIZE(empty),
- .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */
- .name = RC_MAP_EMPTY,
- }
-};
-
-static int __init init_rc_map_empty(void)
-{
- return ir_register_map(&empty_map);
-}
-
-static void __exit exit_rc_map_empty(void)
-{
- ir_unregister_map(&empty_map);
-}
-
-module_init(init_rc_map_empty)
-module_exit(exit_rc_map_empty)
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
--- /dev/null
+/* rc-rc5-streamzap.c - Keytable for Streamzap PC Remote, for use
+ * with the Streamzap PC Remote IR Receiver.
+ *
+ * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+
+static struct ir_scancode rc5_streamzap[] = {
+/*
+ * FIXME: The Streamzap remote isn't actually true RC-5, it has an extra
+ * bit in it, which presently throws the in-kernel RC-5 decoder for a loop.
+ * We either have to enhance the decoder to support it, add a new decoder,
+ * or just rely on lirc userspace decoding.
+ */
+ { 0x00, KEY_NUMERIC_0 },
+ { 0x01, KEY_NUMERIC_1 },
+ { 0x02, KEY_NUMERIC_2 },
+ { 0x03, KEY_NUMERIC_3 },
+ { 0x04, KEY_NUMERIC_4 },
+ { 0x05, KEY_NUMERIC_5 },
+ { 0x06, KEY_NUMERIC_6 },
+ { 0x07, KEY_NUMERIC_7 },
+ { 0x08, KEY_NUMERIC_8 },
+ { 0x0a, KEY_POWER },
+ { 0x0b, KEY_MUTE },
+ { 0x0c, KEY_CHANNELUP },
+ { 0x0d, KEY_VOLUMEUP },
+ { 0x0e, KEY_CHANNELDOWN },
+ { 0x0f, KEY_VOLUMEDOWN },
+ { 0x10, KEY_UP },
+ { 0x11, KEY_LEFT },
+ { 0x12, KEY_OK },
+ { 0x13, KEY_RIGHT },
+ { 0x14, KEY_DOWN },
+ { 0x15, KEY_MENU },
+ { 0x16, KEY_EXIT },
+ { 0x17, KEY_PLAY },
+ { 0x18, KEY_PAUSE },
+ { 0x19, KEY_STOP },
+ { 0x1a, KEY_BACK },
+ { 0x1b, KEY_FORWARD },
+ { 0x1c, KEY_RECORD },
+ { 0x1d, KEY_REWIND },
+ { 0x1e, KEY_FASTFORWARD },
+ { 0x20, KEY_RED },
+ { 0x21, KEY_GREEN },
+ { 0x22, KEY_YELLOW },
+ { 0x23, KEY_BLUE },
+
+};
+
+static struct rc_keymap rc5_streamzap_map = {
+ .map = {
+ .scan = rc5_streamzap,
+ .size = ARRAY_SIZE(rc5_streamzap),
+ .ir_type = IR_TYPE_RC5,
+ .name = RC_MAP_RC5_STREAMZAP,
+ }
+};
+
+static int __init init_rc_map_rc5_streamzap(void)
+{
+ return ir_register_map(&rc5_streamzap_map);
+}
+
+static void __exit exit_rc_map_rc5_streamzap(void)
+{
+ ir_unregister_map(&rc5_streamzap_map);
+}
+
+module_init(init_rc_map_rc5_streamzap)
+module_exit(exit_rc_map_rc5_streamzap)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
{ 0x800f045a, KEY_SUBTITLE }, /* Caption/Teletext */
{ 0x800f044d, KEY_TITLE },
+ { 0x800f044e, KEY_PRINT }, /* Print - HP OEM version of remote */
+
{ 0x800f040c, KEY_POWER },
{ 0x800f040d, KEY_PROG1 }, /* Windows MCE button */
/* data structure for each usb transceiver */
struct mceusb_dev {
/* ir-core bits */
- struct ir_input_dev *irdev;
struct ir_dev_props *props;
struct ir_raw_event rawir;
}
}
-static void usb_async_callback(struct urb *urb, struct pt_regs *regs)
+static void mce_async_callback(struct urb *urb, struct pt_regs *regs)
{
struct mceusb_dev *ir;
int len;
/* outbound data */
usb_fill_int_urb(async_urb, ir->usbdev,
usb_sndintpipe(ir->usbdev, ep->bEndpointAddress),
- async_buf, size, (usb_complete_t) usb_async_callback,
+ async_buf, size, (usb_complete_t)mce_async_callback,
ir, ep->bInterval);
memcpy(async_buf, data, size);
if (ir->send_flags == RECV_FLAG_IN_PROGRESS) {
ir->send_flags = SEND_FLAG_COMPLETE;
- dev_dbg(&ir->irdev->dev, "setup answer received %d bytes\n",
+ dev_dbg(ir->dev, "setup answer received %d bytes\n",
buf_len);
}
{
struct input_dev *idev;
struct ir_dev_props *props;
- struct ir_input_dev *irdev;
struct device *dev = ir->dev;
int ret = -ENODEV;
goto props_alloc_failed;
}
- irdev = kzalloc(sizeof(struct ir_input_dev), GFP_KERNEL);
- if (!irdev) {
- dev_err(dev, "remote ir input dev allocation failed\n");
- goto ir_dev_alloc_failed;
- }
-
snprintf(ir->name, sizeof(ir->name), "Media Center Ed. eHome "
"Infrared Remote Transceiver (%04x:%04x)",
le16_to_cpu(ir->usbdev->descriptor.idVendor),
props->tx_ir = mceusb_tx_ir;
ir->props = props;
- ir->irdev = irdev;
-
- input_set_drvdata(idev, irdev);
ret = ir_input_register(idev, RC_MAP_RC6_MCE, props, DRIVER_NAME);
if (ret < 0) {
return idev;
irdev_failed:
- kfree(irdev);
-ir_dev_alloc_failed:
kfree(props);
props_alloc_failed:
input_free_device(idev);
struct usb_endpoint_descriptor *ep = NULL;
struct usb_endpoint_descriptor *ep_in = NULL;
struct usb_endpoint_descriptor *ep_out = NULL;
- struct usb_host_config *config;
struct mceusb_dev *ir = NULL;
int pipe, maxp, i;
char buf[63], name[128] = "";
dev_dbg(&intf->dev, ": %s called\n", __func__);
- config = dev->actconfig;
idesc = intf->cur_altsetting;
is_gen3 = usb_match_id(intf, gen3_list) ? 1 : 0;
}
EXPORT_SYMBOL_GPL(ir_unregister_map);
+
+static struct ir_scancode empty[] = {
+ { 0x2a, KEY_COFFEE },
+};
+
+static struct rc_keymap empty_map = {
+ .map = {
+ .scan = empty,
+ .size = ARRAY_SIZE(empty),
+ .ir_type = IR_TYPE_UNKNOWN, /* Legacy IR type */
+ .name = RC_MAP_EMPTY,
+ }
+};
+
+int ir_rcmap_init(void)
+{
+ return ir_register_map(&empty_map);
+}
+
+void ir_rcmap_cleanup(void)
+{
+ ir_unregister_map(&empty_map);
+}
--- /dev/null
+/*
+ * Streamzap Remote Control driver
+ *
+ * Copyright (c) 2005 Christoph Bartelmus <lirc@bartelmus.de>
+ * Copyright (c) 2010 Jarod Wilson <jarod@wilsonet.com>
+ *
+ * This driver was based on the work of Greg Wickham and Adrian
+ * Dewhurst. It was substantially rewritten to support correct signal
+ * gaps and now maintains a delay buffer, which is used to present
+ * consistent timing behaviour to user space applications. Without the
+ * delay buffer an ugly hack would be required in lircd, which can
+ * cause sluggish signal decoding in certain situations.
+ *
+ * Ported to in-kernel ir-core interface by Jarod Wilson
+ *
+ * This driver is based on the USB skeleton driver packaged with the
+ * kernel; copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/input.h>
+#include <media/ir-core.h>
+
+#define DRIVER_VERSION "1.60"
+#define DRIVER_NAME "streamzap"
+#define DRIVER_DESC "Streamzap Remote Control driver"
+
+#ifdef CONFIG_USB_DEBUG
+static int debug = 1;
+#else
+static int debug;
+#endif
+
+#define USB_STREAMZAP_VENDOR_ID 0x0e9c
+#define USB_STREAMZAP_PRODUCT_ID 0x0000
+
+/* table of devices that work with this driver */
+static struct usb_device_id streamzap_table[] = {
+ /* Streamzap Remote Control */
+ { USB_DEVICE(USB_STREAMZAP_VENDOR_ID, USB_STREAMZAP_PRODUCT_ID) },
+ /* Terminating entry */
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, streamzap_table);
+
+#define STREAMZAP_PULSE_MASK 0xf0
+#define STREAMZAP_SPACE_MASK 0x0f
+#define STREAMZAP_TIMEOUT 0xff
+#define STREAMZAP_RESOLUTION 256
+
+/* number of samples buffered */
+#define SZ_BUF_LEN 128
+
+enum StreamzapDecoderState {
+ PulseSpace,
+ FullPulse,
+ FullSpace,
+ IgnorePulse
+};
+
+/* structure to hold our device specific stuff */
+struct streamzap_ir {
+
+ /* ir-core */
+ struct ir_dev_props *props;
+ struct ir_raw_event rawir;
+
+ /* core device info */
+ struct device *dev;
+ struct input_dev *idev;
+
+ /* usb */
+ struct usb_device *usbdev;
+ struct usb_interface *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct urb *urb_in;
+
+ /* buffer & dma */
+ unsigned char *buf_in;
+ dma_addr_t dma_in;
+ unsigned int buf_in_len;
+
+ /* timer used to support delay buffering */
+ struct timer_list delay_timer;
+ bool timer_running;
+ spinlock_t timer_lock;
+ struct timer_list flush_timer;
+ bool flush;
+
+ /* delay buffer */
+ struct kfifo fifo;
+ bool fifo_initialized;
+
+ /* track what state we're in */
+ enum StreamzapDecoderState decoder_state;
+ /* tracks whether we are currently receiving some signal */
+ bool idle;
+ /* sum of signal lengths received since signal start */
+ unsigned long sum;
+ /* start time of signal; necessary for gap tracking */
+ struct timeval signal_last;
+ struct timeval signal_start;
+ /* bool timeout_enabled; */
+
+ char name[128];
+ char phys[64];
+};
+
+
+/* local function prototypes */
+static int streamzap_probe(struct usb_interface *interface,
+ const struct usb_device_id *id);
+static void streamzap_disconnect(struct usb_interface *interface);
+static void streamzap_callback(struct urb *urb);
+static int streamzap_suspend(struct usb_interface *intf, pm_message_t message);
+static int streamzap_resume(struct usb_interface *intf);
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver streamzap_driver = {
+ .name = DRIVER_NAME,
+ .probe = streamzap_probe,
+ .disconnect = streamzap_disconnect,
+ .suspend = streamzap_suspend,
+ .resume = streamzap_resume,
+ .id_table = streamzap_table,
+};
+
+static void streamzap_stop_timer(struct streamzap_ir *sz)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sz->timer_lock, flags);
+ if (sz->timer_running) {
+ sz->timer_running = false;
+ spin_unlock_irqrestore(&sz->timer_lock, flags);
+ del_timer_sync(&sz->delay_timer);
+ } else {
+ spin_unlock_irqrestore(&sz->timer_lock, flags);
+ }
+}
+
+static void streamzap_flush_timeout(unsigned long arg)
+{
+ struct streamzap_ir *sz = (struct streamzap_ir *)arg;
+
+ dev_info(sz->dev, "%s: callback firing\n", __func__);
+
+ /* finally start accepting data */
+ sz->flush = false;
+}
+
+static void streamzap_delay_timeout(unsigned long arg)
+{
+ struct streamzap_ir *sz = (struct streamzap_ir *)arg;
+ struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
+ unsigned long flags;
+ int len, ret;
+ static unsigned long delay;
+ bool wake = false;
+
+ /* deliver data every 10 ms */
+ delay = msecs_to_jiffies(10);
+
+ spin_lock_irqsave(&sz->timer_lock, flags);
+
+ if (kfifo_len(&sz->fifo) > 0) {
+ ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
+ if (ret != sizeof(rawir))
+ dev_err(sz->dev, "Problem w/kfifo_out...\n");
+ ir_raw_event_store(sz->idev, &rawir);
+ wake = true;
+ }
+
+ len = kfifo_len(&sz->fifo);
+ if (len > 0) {
+ while ((len < SZ_BUF_LEN / 2) &&
+ (len < SZ_BUF_LEN * sizeof(int))) {
+ ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
+ if (ret != sizeof(rawir))
+ dev_err(sz->dev, "Problem w/kfifo_out...\n");
+ ir_raw_event_store(sz->idev, &rawir);
+ wake = true;
+ len = kfifo_len(&sz->fifo);
+ }
+ if (sz->timer_running)
+ mod_timer(&sz->delay_timer, jiffies + delay);
+
+ } else {
+ sz->timer_running = false;
+ }
+
+ if (wake)
+ ir_raw_event_handle(sz->idev);
+
+ spin_unlock_irqrestore(&sz->timer_lock, flags);
+}
+
+static void streamzap_flush_delay_buffer(struct streamzap_ir *sz)
+{
+ struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
+ bool wake = false;
+ int ret;
+
+ while (kfifo_len(&sz->fifo) > 0) {
+ ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
+ if (ret != sizeof(rawir))
+ dev_err(sz->dev, "Problem w/kfifo_out...\n");
+ ir_raw_event_store(sz->idev, &rawir);
+ wake = true;
+ }
+
+ if (wake)
+ ir_raw_event_handle(sz->idev);
+}
+
+static void sz_push(struct streamzap_ir *sz)
+{
+ struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&sz->timer_lock, flags);
+ if (kfifo_len(&sz->fifo) >= sizeof(int) * SZ_BUF_LEN) {
+ ret = kfifo_out(&sz->fifo, &rawir, sizeof(rawir));
+ if (ret != sizeof(rawir))
+ dev_err(sz->dev, "Problem w/kfifo_out...\n");
+ ir_raw_event_store(sz->idev, &rawir);
+ }
+
+ kfifo_in(&sz->fifo, &sz->rawir, sizeof(rawir));
+
+ if (!sz->timer_running) {
+ sz->delay_timer.expires = jiffies + (HZ / 10);
+ add_timer(&sz->delay_timer);
+ sz->timer_running = true;
+ }
+
+ spin_unlock_irqrestore(&sz->timer_lock, flags);
+}
+
+static void sz_push_full_pulse(struct streamzap_ir *sz,
+ unsigned char value)
+{
+ if (sz->idle) {
+ long deltv;
+
+ sz->signal_last = sz->signal_start;
+ do_gettimeofday(&sz->signal_start);
+
+ deltv = sz->signal_start.tv_sec - sz->signal_last.tv_sec;
+ sz->rawir.pulse = false;
+ if (deltv > 15) {
+ /* really long time */
+ sz->rawir.duration = IR_MAX_DURATION;
+ } else {
+ sz->rawir.duration = (int)(deltv * 1000000 +
+ sz->signal_start.tv_usec -
+ sz->signal_last.tv_usec);
+ sz->rawir.duration -= sz->sum;
+ sz->rawir.duration *= 1000;
+ sz->rawir.duration &= IR_MAX_DURATION;
+ }
+ dev_dbg(sz->dev, "ls %u\n", sz->rawir.duration);
+ sz_push(sz);
+
+ sz->idle = 0;
+ sz->sum = 0;
+ }
+
+ sz->rawir.pulse = true;
+ sz->rawir.duration = ((int) value) * STREAMZAP_RESOLUTION;
+ sz->rawir.duration += STREAMZAP_RESOLUTION / 2;
+ sz->sum += sz->rawir.duration;
+ sz->rawir.duration *= 1000;
+ sz->rawir.duration &= IR_MAX_DURATION;
+ dev_dbg(sz->dev, "p %u\n", sz->rawir.duration);
+ sz_push(sz);
+}
+
+static void sz_push_half_pulse(struct streamzap_ir *sz,
+ unsigned char value)
+{
+ sz_push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK) >> 4);
+}
+
+static void sz_push_full_space(struct streamzap_ir *sz,
+ unsigned char value)
+{
+ sz->rawir.pulse = false;
+ sz->rawir.duration = ((int) value) * STREAMZAP_RESOLUTION;
+ sz->rawir.duration += STREAMZAP_RESOLUTION / 2;
+ sz->sum += sz->rawir.duration;
+ sz->rawir.duration *= 1000;
+ dev_dbg(sz->dev, "s %u\n", sz->rawir.duration);
+ sz_push(sz);
+}
+
+static void sz_push_half_space(struct streamzap_ir *sz,
+ unsigned long value)
+{
+ sz_push_full_space(sz, value & STREAMZAP_SPACE_MASK);
+}
+
+/**
+ * streamzap_callback - usb IRQ handler callback
+ *
+ * This procedure is invoked on reception of data from
+ * the usb remote.
+ */
+static void streamzap_callback(struct urb *urb)
+{
+ struct streamzap_ir *sz;
+ unsigned int i;
+ int len;
+ #if 0
+ static int timeout = (((STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION) &
+ IR_MAX_DURATION) | 0x03000000);
+ #endif
+
+ if (!urb)
+ return;
+
+ sz = urb->context;
+ len = urb->actual_length;
+
+ switch (urb->status) {
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /*
+ * this urb is terminated, clean up.
+ * sz might already be invalid at this point
+ */
+ dev_err(sz->dev, "urb terminated, status: %d\n", urb->status);
+ return;
+ default:
+ break;
+ }
+
+ dev_dbg(sz->dev, "%s: received urb, len %d\n", __func__, len);
+ if (!sz->flush) {
+ for (i = 0; i < urb->actual_length; i++) {
+ dev_dbg(sz->dev, "%d: %x\n", i,
+ (unsigned char)sz->buf_in[i]);
+ switch (sz->decoder_state) {
+ case PulseSpace:
+ if ((sz->buf_in[i] & STREAMZAP_PULSE_MASK) ==
+ STREAMZAP_PULSE_MASK) {
+ sz->decoder_state = FullPulse;
+ continue;
+ } else if ((sz->buf_in[i] & STREAMZAP_SPACE_MASK)
+ == STREAMZAP_SPACE_MASK) {
+ sz_push_half_pulse(sz, sz->buf_in[i]);
+ sz->decoder_state = FullSpace;
+ continue;
+ } else {
+ sz_push_half_pulse(sz, sz->buf_in[i]);
+ sz_push_half_space(sz, sz->buf_in[i]);
+ }
+ break;
+ case FullPulse:
+ sz_push_full_pulse(sz, sz->buf_in[i]);
+ sz->decoder_state = IgnorePulse;
+ break;
+ case FullSpace:
+ if (sz->buf_in[i] == STREAMZAP_TIMEOUT) {
+ sz->idle = 1;
+ streamzap_stop_timer(sz);
+ #if 0
+ if (sz->timeout_enabled) {
+ sz->rawir.pulse = false;
+ sz->rawir.duration = timeout;
+ sz->rawir.duration *= 1000;
+ sz_push(sz);
+ }
+ #endif
+ streamzap_flush_delay_buffer(sz);
+ } else
+ sz_push_full_space(sz, sz->buf_in[i]);
+ sz->decoder_state = PulseSpace;
+ break;
+ case IgnorePulse:
+ if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK) ==
+ STREAMZAP_SPACE_MASK) {
+ sz->decoder_state = FullSpace;
+ continue;
+ }
+ sz_push_half_space(sz, sz->buf_in[i]);
+ sz->decoder_state = PulseSpace;
+ break;
+ }
+ }
+ }
+
+ usb_submit_urb(urb, GFP_ATOMIC);
+
+ return;
+}
+
+static struct input_dev *streamzap_init_input_dev(struct streamzap_ir *sz)
+{
+ struct input_dev *idev;
+ struct ir_dev_props *props;
+ struct device *dev = sz->dev;
+ int ret;
+
+ idev = input_allocate_device();
+ if (!idev) {
+ dev_err(dev, "remote input dev allocation failed\n");
+ goto idev_alloc_failed;
+ }
+
+ props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL);
+ if (!props) {
+ dev_err(dev, "remote ir dev props allocation failed\n");
+ goto props_alloc_failed;
+ }
+
+ snprintf(sz->name, sizeof(sz->name), "Streamzap PC Remote Infrared "
+ "Receiver (%04x:%04x)",
+ le16_to_cpu(sz->usbdev->descriptor.idVendor),
+ le16_to_cpu(sz->usbdev->descriptor.idProduct));
+
+ idev->name = sz->name;
+ usb_make_path(sz->usbdev, sz->phys, sizeof(sz->phys));
+ strlcat(sz->phys, "/input0", sizeof(sz->phys));
+ idev->phys = sz->phys;
+
+ props->priv = sz;
+ props->driver_type = RC_DRIVER_IR_RAW;
+ /* FIXME: not sure about supported protocols, check on this */
+ props->allowed_protos = IR_TYPE_RC5 | IR_TYPE_RC6;
+
+ sz->props = props;
+
+ ret = ir_input_register(idev, RC_MAP_RC5_STREAMZAP, props, DRIVER_NAME);
+ if (ret < 0) {
+ dev_err(dev, "remote input device register failed\n");
+ goto irdev_failed;
+ }
+
+ return idev;
+
+irdev_failed:
+ kfree(props);
+props_alloc_failed:
+ input_free_device(idev);
+idev_alloc_failed:
+ return NULL;
+}
+
+static int streamzap_delay_buf_init(struct streamzap_ir *sz)
+{
+ int ret;
+
+ ret = kfifo_alloc(&sz->fifo, sizeof(int) * SZ_BUF_LEN,
+ GFP_KERNEL);
+ if (ret == 0)
+ sz->fifo_initialized = 1;
+
+ return ret;
+}
+
+static void streamzap_start_flush_timer(struct streamzap_ir *sz)
+{
+ sz->flush_timer.expires = jiffies + HZ;
+ sz->flush = true;
+ add_timer(&sz->flush_timer);
+
+ sz->urb_in->dev = sz->usbdev;
+ if (usb_submit_urb(sz->urb_in, GFP_ATOMIC))
+ dev_err(sz->dev, "urb submit failed\n");
+}
+
+/**
+ * streamzap_probe
+ *
+ * Called by usb-core to associated with a candidate device
+ * On any failure the return value is the ERROR
+ * On success return 0
+ */
+static int __devinit streamzap_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+ struct usb_host_interface *iface_host;
+ struct streamzap_ir *sz = NULL;
+ char buf[63], name[128] = "";
+ int retval = -ENOMEM;
+ int pipe, maxp;
+
+ /* Allocate space for device driver specific data */
+ sz = kzalloc(sizeof(struct streamzap_ir), GFP_KERNEL);
+ if (!sz)
+ return -ENOMEM;
+
+ sz->usbdev = usbdev;
+ sz->interface = intf;
+
+ /* Check to ensure endpoint information matches requirements */
+ iface_host = intf->cur_altsetting;
+
+ if (iface_host->desc.bNumEndpoints != 1) {
+ dev_err(&intf->dev, "%s: Unexpected desc.bNumEndpoints (%d)\n",
+ __func__, iface_host->desc.bNumEndpoints);
+ retval = -ENODEV;
+ goto free_sz;
+ }
+
+ sz->endpoint = &(iface_host->endpoint[0].desc);
+ if ((sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ != USB_DIR_IN) {
+ dev_err(&intf->dev, "%s: endpoint doesn't match input device "
+ "02%02x\n", __func__, sz->endpoint->bEndpointAddress);
+ retval = -ENODEV;
+ goto free_sz;
+ }
+
+ if ((sz->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ != USB_ENDPOINT_XFER_INT) {
+ dev_err(&intf->dev, "%s: endpoint attributes don't match xfer "
+ "02%02x\n", __func__, sz->endpoint->bmAttributes);
+ retval = -ENODEV;
+ goto free_sz;
+ }
+
+ pipe = usb_rcvintpipe(usbdev, sz->endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(usbdev, pipe, usb_pipeout(pipe));
+
+ if (maxp == 0) {
+ dev_err(&intf->dev, "%s: endpoint Max Packet Size is 0!?!\n",
+ __func__);
+ retval = -ENODEV;
+ goto free_sz;
+ }
+
+ /* Allocate the USB buffer and IRQ URB */
+ sz->buf_in = usb_alloc_coherent(usbdev, maxp, GFP_ATOMIC, &sz->dma_in);
+ if (!sz->buf_in)
+ goto free_sz;
+
+ sz->urb_in = usb_alloc_urb(0, GFP_KERNEL);
+ if (!sz->urb_in)
+ goto free_buf_in;
+
+ sz->dev = &intf->dev;
+ sz->buf_in_len = maxp;
+
+ if (usbdev->descriptor.iManufacturer
+ && usb_string(usbdev, usbdev->descriptor.iManufacturer,
+ buf, sizeof(buf)) > 0)
+ strlcpy(name, buf, sizeof(name));
+
+ if (usbdev->descriptor.iProduct
+ && usb_string(usbdev, usbdev->descriptor.iProduct,
+ buf, sizeof(buf)) > 0)
+ snprintf(name + strlen(name), sizeof(name) - strlen(name),
+ " %s", buf);
+
+ retval = streamzap_delay_buf_init(sz);
+ if (retval) {
+ dev_err(&intf->dev, "%s: delay buffer init failed\n", __func__);
+ goto free_urb_in;
+ }
+
+ sz->idev = streamzap_init_input_dev(sz);
+ if (!sz->idev)
+ goto input_dev_fail;
+
+ sz->idle = true;
+ sz->decoder_state = PulseSpace;
+ #if 0
+ /* not yet supported, depends on patches from maxim */
+ /* see also: LIRC_GET_REC_RESOLUTION and LIRC_SET_REC_TIMEOUT */
+ sz->timeout_enabled = false;
+ sz->min_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION * 1000;
+ sz->max_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION * 1000;
+ #endif
+
+ init_timer(&sz->delay_timer);
+ sz->delay_timer.function = streamzap_delay_timeout;
+ sz->delay_timer.data = (unsigned long)sz;
+ spin_lock_init(&sz->timer_lock);
+
+ init_timer(&sz->flush_timer);
+ sz->flush_timer.function = streamzap_flush_timeout;
+ sz->flush_timer.data = (unsigned long)sz;
+
+ do_gettimeofday(&sz->signal_start);
+
+ /* Complete final initialisations */
+ usb_fill_int_urb(sz->urb_in, usbdev, pipe, sz->buf_in,
+ maxp, (usb_complete_t)streamzap_callback,
+ sz, sz->endpoint->bInterval);
+ sz->urb_in->transfer_dma = sz->dma_in;
+ sz->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ usb_set_intfdata(intf, sz);
+
+ streamzap_start_flush_timer(sz);
+
+ dev_info(sz->dev, "Registered %s on usb%d:%d\n", name,
+ usbdev->bus->busnum, usbdev->devnum);
+
+ return 0;
+
+input_dev_fail:
+ kfifo_free(&sz->fifo);
+free_urb_in:
+ usb_free_urb(sz->urb_in);
+free_buf_in:
+ usb_free_coherent(usbdev, maxp, sz->buf_in, sz->dma_in);
+free_sz:
+ kfree(sz);
+
+ return retval;
+}
+
+/**
+ * streamzap_disconnect
+ *
+ * Called by the usb core when the device is removed from the system.
+ *
+ * This routine guarantees that the driver will not submit any more urbs
+ * by clearing dev->usbdev. It is also supposed to terminate any currently
+ * active urbs. Unfortunately, usb_bulk_msg(), used in streamzap_read(),
+ * does not provide any way to do this.
+ */
+static void streamzap_disconnect(struct usb_interface *interface)
+{
+ struct streamzap_ir *sz = usb_get_intfdata(interface);
+ struct usb_device *usbdev = interface_to_usbdev(interface);
+
+ usb_set_intfdata(interface, NULL);
+
+ if (!sz)
+ return;
+
+ if (sz->flush) {
+ sz->flush = false;
+ del_timer_sync(&sz->flush_timer);
+ }
+
+ streamzap_stop_timer(sz);
+
+ sz->usbdev = NULL;
+ ir_input_unregister(sz->idev);
+ usb_kill_urb(sz->urb_in);
+ usb_free_urb(sz->urb_in);
+ usb_free_coherent(usbdev, sz->buf_in_len, sz->buf_in, sz->dma_in);
+
+ kfree(sz);
+}
+
+static int streamzap_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct streamzap_ir *sz = usb_get_intfdata(intf);
+
+ if (sz->flush) {
+ sz->flush = false;
+ del_timer_sync(&sz->flush_timer);
+ }
+
+ streamzap_stop_timer(sz);
+
+ usb_kill_urb(sz->urb_in);
+
+ return 0;
+}
+
+static int streamzap_resume(struct usb_interface *intf)
+{
+ struct streamzap_ir *sz = usb_get_intfdata(intf);
+
+ if (sz->fifo_initialized)
+ kfifo_reset(&sz->fifo);
+
+ sz->flush_timer.expires = jiffies + HZ;
+ sz->flush = true;
+ add_timer(&sz->flush_timer);
+
+ if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
+ dev_err(sz->dev, "Error sumbiting urb\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * streamzap_init
+ */
+static int __init streamzap_init(void)
+{
+ int ret;
+
+ /* register this driver with the USB subsystem */
+ ret = usb_register(&streamzap_driver);
+ if (ret < 0)
+ printk(KERN_ERR DRIVER_NAME ": usb register failed, "
+ "result = %d\n", ret);
+
+ return ret;
+}
+
+/**
+ * streamzap_exit
+ */
+static void __exit streamzap_exit(void)
+{
+ usb_deregister(&streamzap_driver);
+}
+
+
+module_init(streamzap_init);
+module_exit(streamzap_exit);
+
+MODULE_AUTHOR("Jarod Wilson <jarod@wilsonet.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
menuconfig MEDIA_TUNER_CUSTOMISE
bool "Customize analog and hybrid tuner modules to build"
depends on MEDIA_TUNER
- default n
+ default y if EMBEDDED
help
This allows the user to deselect tuner drivers unnecessary
for their hardware from the build. Use this option with care
.frequency_min = 137000000,
.frequency_max = 858000000,
.frequency_stepsize = 166667,
- .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO
+ .caps = FE_CAN_FEC_AUTO |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_QAM_16 |
+ FE_CAN_QAM_32 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_128 |
+ FE_CAN_QAM_256 |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO
},
.release = dst_release,
config DVB_FE_CUSTOMISE
bool "Customise the frontend modules to build"
depends on DVB_CORE
- default N
+ default y if EMBEDDED
help
This allows the user to select/deselect frontend drivers for their
hardware from the build.
*/
prepare_to_wait(&coredev->buffer_mng_waitq, &wait, TASK_INTERRUPTIBLE);
-
- if (list_empty(&coredev->buffers))
+ if (list_empty(&coredev->buffers)) {
+ spin_unlock_irqrestore(&coredev->bufferslock, flags);
schedule();
+ spin_lock_irqsave(&coredev->bufferslock, flags);
+ }
finish_wait(&coredev->buffer_mng_waitq, &wait);
config VIDEO_HELPER_CHIPS_AUTO
bool "Autoselect pertinent encoders/decoders and other helper chips"
- default y
+ default y if !EMBEDDED
---help---
Most video cards may require additional modules to encode or
decode audio/video standards. This option will autoselect
and colour models.
config SOC_CAMERA_MT9M111
- tristate "mt9m111 and mt9m112 support"
+ tristate "mt9m111, mt9m112 and mt9m131 support"
depends on SOC_CAMERA && I2C
help
- This driver supports MT9M111 and MT9M112 cameras from Micron
+ This driver supports MT9M111, MT9M112 and MT9M131 cameras from
+ Micron/Aptina
config SOC_CAMERA_MT9T031
tristate "mt9t031 support"
This is a virtual test device for the memory-to-memory driver
framework.
+config VIDEO_SAMSUNG_S5P_FIMC
+ tristate "Samsung S5P FIMC (video postprocessor) driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
+ select VIDEOBUF_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ This is a v4l2 driver for the S5P camera interface
+ (video postprocessor)
+
endif # V4L_MEM2MEM_DRIVERS
omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o
videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
- v4l2-event.o
+ v4l2-event.o v4l2-ctrls.o
# V4L2 core modules
obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o
obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/
obj-$(CONFIG_ARCH_DAVINCI) += davinci/
#include <linux/ioctl.h>
#include <asm/uaccess.h>
#include <linux/i2c.h>
-#include <linux/i2c-id.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-i2c-drv.h>
MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC");
MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On");
+struct cs53l32a_state {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+};
+
+static inline struct cs53l32a_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct cs53l32a_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct cs53l32a_state, hdl)->sd;
+}
+
/* ----------------------------------------------------------------------- */
static int cs53l32a_write(struct v4l2_subdev *sd, u8 reg, u8 value)
return 0;
}
-static int cs53l32a_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int cs53l32a_s_ctrl(struct v4l2_ctrl *ctrl)
{
- if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
- ctrl->value = (cs53l32a_read(sd, 0x03) & 0xc0) != 0;
- return 0;
- }
- if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
- return -EINVAL;
- ctrl->value = (s8)cs53l32a_read(sd, 0x04);
- return 0;
-}
+ struct v4l2_subdev *sd = to_sd(ctrl);
-static int cs53l32a_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
- cs53l32a_write(sd, 0x03, ctrl->value ? 0xf0 : 0x30);
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ cs53l32a_write(sd, 0x03, ctrl->val ? 0xf0 : 0x30);
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ cs53l32a_write(sd, 0x04, (u8)ctrl->val);
+ cs53l32a_write(sd, 0x05, (u8)ctrl->val);
return 0;
}
- if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
- return -EINVAL;
- if (ctrl->value > 12 || ctrl->value < -96)
- return -EINVAL;
- cs53l32a_write(sd, 0x04, (u8) ctrl->value);
- cs53l32a_write(sd, 0x05, (u8) ctrl->value);
- return 0;
+ return -EINVAL;
}
static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
static int cs53l32a_log_status(struct v4l2_subdev *sd)
{
+ struct cs53l32a_state *state = to_state(sd);
u8 v = cs53l32a_read(sd, 0x01);
- u8 m = cs53l32a_read(sd, 0x03);
- s8 vol = cs53l32a_read(sd, 0x04);
- v4l2_info(sd, "Input: %d%s\n", (v >> 4) & 3,
- (m & 0xC0) ? " (muted)" : "");
- v4l2_info(sd, "Volume: %d dB\n", vol);
+ v4l2_info(sd, "Input: %d\n", (v >> 4) & 3);
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
return 0;
}
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops cs53l32a_ctrl_ops = {
+ .s_ctrl = cs53l32a_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops cs53l32a_core_ops = {
.log_status = cs53l32a_log_status,
.g_chip_ident = cs53l32a_g_chip_ident,
- .g_ctrl = cs53l32a_g_ctrl,
- .s_ctrl = cs53l32a_s_ctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
};
static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = {
static int cs53l32a_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct cs53l32a_state *state;
struct v4l2_subdev *sd;
int i;
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
- if (sd == NULL)
+ state = kzalloc(sizeof(struct cs53l32a_state), GFP_KERNEL);
+ if (state == NULL)
return -ENOMEM;
+ sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops);
for (i = 1; i <= 7; i++) {
v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v);
}
+ v4l2_ctrl_handler_init(&state->hdl, 2);
+ v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, -96, 12, 1, 0);
+ v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ sd->ctrl_handler = &state->hdl;
+ if (state->hdl.error) {
+ int err = state->hdl.error;
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return err;
+ }
+
/* Set cs53l32a internal register for Adaptec 2010/2410 setup */
- cs53l32a_write(sd, 0x01, (u8) 0x21);
- cs53l32a_write(sd, 0x02, (u8) 0x29);
- cs53l32a_write(sd, 0x03, (u8) 0x30);
- cs53l32a_write(sd, 0x04, (u8) 0x00);
- cs53l32a_write(sd, 0x05, (u8) 0x00);
- cs53l32a_write(sd, 0x06, (u8) 0x00);
- cs53l32a_write(sd, 0x07, (u8) 0x00);
+ cs53l32a_write(sd, 0x01, 0x21);
+ cs53l32a_write(sd, 0x02, 0x29);
+ cs53l32a_write(sd, 0x03, 0x30);
+ cs53l32a_write(sd, 0x04, 0x00);
+ cs53l32a_write(sd, 0x05, 0x00);
+ cs53l32a_write(sd, 0x06, 0x00);
+ cs53l32a_write(sd, 0x07, 0x00);
/* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */
static int cs53l32a_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct cs53l32a_state *state = to_state(sd);
v4l2_device_unregister_subdev(sd);
- kfree(sd);
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
return 0;
}
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
+/********************** COMMON CODE *********************/
+
+/* definitions for audio properties bits 29-28 */
+#define CX2341X_AUDIO_ENCODING_METHOD_MPEG 0
+#define CX2341X_AUDIO_ENCODING_METHOD_AC3 1
+#define CX2341X_AUDIO_ENCODING_METHOD_LPCM 2
+
+static const char *cx2341x_get_name(u32 id)
+{
+ switch (id) {
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ return "Spatial Filter Mode";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+ return "Spatial Filter";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ return "Spatial Luma Filter Type";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ return "Spatial Chroma Filter Type";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ return "Temporal Filter Mode";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
+ return "Temporal Filter";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ return "Median Filter Type";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+ return "Median Luma Filter Maximum";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
+ return "Median Luma Filter Minimum";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
+ return "Median Chroma Filter Maximum";
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
+ return "Median Chroma Filter Minimum";
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ return "Insert Navigation Packets";
+ }
+ return NULL;
+}
+
+static const char **cx2341x_get_menu(u32 id)
+{
+ static const char *cx2341x_video_spatial_filter_mode_menu[] = {
+ "Manual",
+ "Auto",
+ NULL
+ };
+
+ static const char *cx2341x_video_luma_spatial_filter_type_menu[] = {
+ "Off",
+ "1D Horizontal",
+ "1D Vertical",
+ "2D H/V Separable",
+ "2D Symmetric non-separable",
+ NULL
+ };
+
+ static const char *cx2341x_video_chroma_spatial_filter_type_menu[] = {
+ "Off",
+ "1D Horizontal",
+ NULL
+ };
+
+ static const char *cx2341x_video_temporal_filter_mode_menu[] = {
+ "Manual",
+ "Auto",
+ NULL
+ };
+
+ static const char *cx2341x_video_median_filter_type_menu[] = {
+ "Off",
+ "Horizontal",
+ "Vertical",
+ "Horizontal/Vertical",
+ "Diagonal",
+ NULL
+ };
+
+ switch (id) {
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ return cx2341x_video_spatial_filter_mode_menu;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ return cx2341x_video_luma_spatial_filter_type_menu;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ return cx2341x_video_chroma_spatial_filter_type_menu;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ return cx2341x_video_temporal_filter_mode_menu;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ return cx2341x_video_median_filter_type_menu;
+ }
+ return NULL;
+}
+
+static void cx2341x_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
+ s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags)
+{
+ *name = cx2341x_get_name(id);
+ *flags = 0;
+
+ switch (id) {
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ *type = V4L2_CTRL_TYPE_MENU;
+ *min = 0;
+ *step = 0;
+ break;
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ *type = V4L2_CTRL_TYPE_BOOLEAN;
+ *min = 0;
+ *max = *step = 1;
+ break;
+ default:
+ *type = V4L2_CTRL_TYPE_INTEGER;
+ break;
+ }
+ switch (id) {
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
+ *flags |= V4L2_CTRL_FLAG_UPDATE;
+ break;
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
+ case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
+ *flags |= V4L2_CTRL_FLAG_SLIDER;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ break;
+ }
+}
+
+
+/********************** OLD CODE *********************/
+
/* Must be sorted from low to high control ID! */
const u32 cx2341x_mpeg_ctrls[] = {
V4L2_CID_MPEG_CLASS,
.video_chroma_median_filter_top = 255,
.video_chroma_median_filter_bottom = 0,
};
-
-
/* Map the control ID to the correct field in the cx2341x_mpeg_params
struct. Return -EINVAL if the ID is unknown, else return 0. */
static int cx2341x_get_ctrl(const struct cx2341x_mpeg_params *params,
{
const char *name;
- qctrl->flags = 0;
switch (qctrl->id) {
/* MPEG controls */
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
- name = "Spatial Filter Mode";
- break;
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
- name = "Spatial Filter";
- break;
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
- name = "Spatial Luma Filter Type";
- break;
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
- name = "Spatial Chroma Filter Type";
- break;
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
- name = "Temporal Filter Mode";
- break;
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER:
- name = "Temporal Filter";
- break;
case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
- name = "Median Filter Type";
- break;
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
- name = "Median Luma Filter Maximum";
- break;
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM:
- name = "Median Luma Filter Minimum";
- break;
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP:
- name = "Median Chroma Filter Maximum";
- break;
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM:
- name = "Median Chroma Filter Minimum";
- break;
case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
- name = "Insert Navigation Packets";
- break;
+ cx2341x_ctrl_fill(qctrl->id, &name, &qctrl->type,
+ &min, &max, &step, &def, &qctrl->flags);
+ qctrl->minimum = min;
+ qctrl->maximum = max;
+ qctrl->step = step;
+ qctrl->default_value = def;
+ qctrl->reserved[0] = qctrl->reserved[1] = 0;
+ strlcpy(qctrl->name, name, sizeof(qctrl->name));
+ return 0;
default:
return v4l2_ctrl_query_fill(qctrl, min, max, step, def);
}
- switch (qctrl->id) {
- case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
- case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
- case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
- case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
- case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
- qctrl->type = V4L2_CTRL_TYPE_MENU;
- min = 0;
- step = 1;
- break;
- case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
- qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
- min = 0;
- max = 1;
- step = 1;
- break;
- default:
- qctrl->type = V4L2_CTRL_TYPE_INTEGER;
- break;
- }
- switch (qctrl->id) {
- case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
- case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
- case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
- qctrl->flags |= V4L2_CTRL_FLAG_UPDATE;
- break;
- }
- qctrl->minimum = min;
- qctrl->maximum = max;
- qctrl->step = step;
- qctrl->default_value = def;
- qctrl->reserved[0] = qctrl->reserved[1] = 0;
- snprintf(qctrl->name, sizeof(qctrl->name), name);
- return 0;
}
int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params,
NULL
};
- static const char *cx2341x_video_spatial_filter_mode_menu[] = {
- "Manual",
- "Auto",
- NULL
- };
-
- static const char *cx2341x_video_luma_spatial_filter_type_menu[] = {
- "Off",
- "1D Horizontal",
- "1D Vertical",
- "2D H/V Separable",
- "2D Symmetric non-separable",
- NULL
- };
-
- static const char *cx2341x_video_chroma_spatial_filter_type_menu[] = {
- "Off",
- "1D Horizontal",
- NULL
- };
-
- static const char *cx2341x_video_temporal_filter_mode_menu[] = {
- "Manual",
- "Auto",
- NULL
- };
-
- static const char *cx2341x_video_median_filter_type_menu[] = {
- "Off",
- "Horizontal",
- "Vertical",
- "Horizontal/Vertical",
- "Diagonal",
- NULL
- };
-
switch (id) {
case V4L2_CID_MPEG_STREAM_TYPE:
return (p->capabilities & CX2341X_CAP_HAS_TS) ?
case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
return NULL;
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
- return cx2341x_video_spatial_filter_mode_menu;
case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
- return cx2341x_video_luma_spatial_filter_type_menu;
case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE:
- return cx2341x_video_chroma_spatial_filter_type_menu;
case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE:
- return cx2341x_video_temporal_filter_mode_menu;
case V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE:
- return cx2341x_video_median_filter_type_menu;
+ return cx2341x_get_menu(id);
default:
return v4l2_ctrl_get_menu(id);
}
}
EXPORT_SYMBOL(cx2341x_ctrl_get_menu);
-/* definitions for audio properties bits 29-28 */
-#define CX2341X_AUDIO_ENCODING_METHOD_MPEG 0
-#define CX2341X_AUDIO_ENCODING_METHOD_AC3 1
-#define CX2341X_AUDIO_ENCODING_METHOD_LPCM 2
-
static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params)
{
params->audio_properties =
}
EXPORT_SYMBOL(cx2341x_log_status);
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
+
+/********************** NEW CODE *********************/
+
+static inline struct cx2341x_handler *to_cxhdl(struct v4l2_ctrl *ctrl)
+{
+ return container_of(ctrl->handler, struct cx2341x_handler, hdl);
+}
+
+static int cx2341x_hdl_api(struct cx2341x_handler *hdl,
+ u32 cmd, int args, ...)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ va_list vargs;
+ int i;
+
+ va_start(vargs, args);
+
+ for (i = 0; i < args; i++)
+ data[i] = va_arg(vargs, int);
+ va_end(vargs);
+ return hdl->func(hdl->priv, cmd, args, 0, data);
+}
+
+/* ctrl->handler->lock is held, so it is safe to access cur.val */
+static inline int cx2341x_neq(struct v4l2_ctrl *ctrl)
+{
+ return ctrl && ctrl->val != ctrl->cur.val;
+}
+
+static int cx2341x_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct cx2341x_handler *hdl = to_cxhdl(ctrl);
+ s32 val = ctrl->val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES: {
+ /* video gop cluster */
+ int b = val + 1;
+ int gop = hdl->video_gop_size->val;
+
+ gop = b * ((gop + b - 1) / b);
+
+ /* Max GOP size = 34 */
+ while (gop > 34)
+ gop -= b;
+ hdl->video_gop_size->val = gop;
+ break;
+ }
+
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ /* stream type cluster */
+ hdl->video_encoding->val =
+ (hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_SS ||
+ hdl->stream_type->val == V4L2_MPEG_STREAM_TYPE_MPEG1_VCD) ?
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_1 :
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
+ if (hdl->video_encoding->val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
+ /* MPEG-1 implies CBR */
+ hdl->video_bitrate_mode->val =
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
+ /* peak bitrate shall be >= normal bitrate */
+ if (hdl->video_bitrate_mode->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
+ hdl->video_bitrate_peak->val < hdl->video_bitrate->val)
+ hdl->video_bitrate_peak->val = hdl->video_bitrate->val;
+ break;
+ }
+ return 0;
+}
+
+static int cx2341x_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ static const int mpeg_stream_type[] = {
+ 0, /* MPEG-2 PS */
+ 1, /* MPEG-2 TS */
+ 2, /* MPEG-1 SS */
+ 14, /* DVD */
+ 11, /* VCD */
+ 12, /* SVCD */
+ };
+ struct cx2341x_handler *hdl = to_cxhdl(ctrl);
+ s32 val = ctrl->val;
+ u32 props;
+ int err;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ if (hdl->ops && hdl->ops->s_stream_vbi_fmt)
+ return hdl->ops->s_stream_vbi_fmt(hdl, val);
+ return 0;
+
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ return cx2341x_hdl_api(hdl,
+ CX2341X_ENC_SET_ASPECT_RATIO, 1, val + 1);
+
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_CLOSURE, 1, val);
+
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_AUDIO, 1, val);
+
+ case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
+ return cx2341x_hdl_api(hdl,
+ CX2341X_ENC_SET_FRAME_DROP_RATE, 1, val);
+
+ case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS:
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_MISC, 2, 7, val);
+
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ /* audio properties cluster */
+ props = (hdl->audio_sampling_freq->val << 0) |
+ (hdl->audio_mode->val << 8) |
+ (hdl->audio_mode_extension->val << 10) |
+ (hdl->audio_crc->val << 14);
+ if (hdl->audio_emphasis->val == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17)
+ props |= 3 << 12;
+ else
+ props |= hdl->audio_emphasis->val << 12;
+
+ if (hdl->audio_encoding->val == V4L2_MPEG_AUDIO_ENCODING_AC3) {
+ props |=
+#if 1
+ /* Not sure if this MPEG Layer II setting is required */
+ ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) |
+#endif
+ (hdl->audio_ac3_bitrate->val << 4) |
+ (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28);
+ } else {
+ /* Assuming MPEG Layer II */
+ props |=
+ ((3 - hdl->audio_encoding->val) << 2) |
+ ((1 + hdl->audio_l2_bitrate->val) << 4);
+ }
+ err = cx2341x_hdl_api(hdl,
+ CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, props);
+ if (err)
+ return err;
+
+ hdl->audio_properties = props;
+ if (hdl->audio_ac3_bitrate) {
+ int is_ac3 = hdl->audio_encoding->val ==
+ V4L2_MPEG_AUDIO_ENCODING_AC3;
+
+ v4l2_ctrl_activate(hdl->audio_ac3_bitrate, is_ac3);
+ v4l2_ctrl_activate(hdl->audio_l2_bitrate, !is_ac3);
+ }
+ v4l2_ctrl_activate(hdl->audio_mode_extension,
+ hdl->audio_mode->val == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO);
+ if (cx2341x_neq(hdl->audio_sampling_freq) &&
+ hdl->ops && hdl->ops->s_audio_sampling_freq)
+ return hdl->ops->s_audio_sampling_freq(hdl, hdl->audio_sampling_freq->val);
+ if (cx2341x_neq(hdl->audio_mode) &&
+ hdl->ops && hdl->ops->s_audio_mode)
+ return hdl->ops->s_audio_mode(hdl, hdl->audio_mode->val);
+ return 0;
+
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ /* video gop cluster */
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_GOP_PROPERTIES, 2,
+ hdl->video_gop_size->val,
+ hdl->video_b_frames->val + 1);
+
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ /* stream type cluster */
+ err = cx2341x_hdl_api(hdl,
+ CX2341X_ENC_SET_STREAM_TYPE, 1, mpeg_stream_type[val]);
+ if (err)
+ return err;
+
+ err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_BIT_RATE, 5,
+ hdl->video_bitrate_mode->val,
+ hdl->video_bitrate->val,
+ hdl->video_bitrate_peak->val / 400, 0, 0);
+ if (err)
+ return err;
+
+ v4l2_ctrl_activate(hdl->video_bitrate_mode,
+ hdl->video_encoding->val != V4L2_MPEG_VIDEO_ENCODING_MPEG_1);
+ v4l2_ctrl_activate(hdl->video_bitrate_peak,
+ hdl->video_bitrate_mode->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
+ if (cx2341x_neq(hdl->video_encoding) &&
+ hdl->ops && hdl->ops->s_video_encoding)
+ return hdl->ops->s_video_encoding(hdl, hdl->video_encoding->val);
+ return 0;
+
+ case V4L2_CID_MPEG_VIDEO_MUTE:
+ /* video mute cluster */
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_MUTE_VIDEO, 1,
+ hdl->video_mute->val |
+ (hdl->video_mute_yuv->val << 8));
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: {
+ int active_filter;
+
+ /* video filter mode */
+ err = cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_MODE, 2,
+ hdl->video_spatial_filter_mode->val |
+ (hdl->video_temporal_filter_mode->val << 1),
+ hdl->video_median_filter_type->val);
+ if (err)
+ return err;
+
+ active_filter = hdl->video_spatial_filter_mode->val !=
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO;
+ v4l2_ctrl_activate(hdl->video_spatial_filter, active_filter);
+ v4l2_ctrl_activate(hdl->video_luma_spatial_filter_type, active_filter);
+ v4l2_ctrl_activate(hdl->video_chroma_spatial_filter_type, active_filter);
+ active_filter = hdl->video_temporal_filter_mode->val !=
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO;
+ v4l2_ctrl_activate(hdl->video_temporal_filter, active_filter);
+ active_filter = hdl->video_median_filter_type->val !=
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF;
+ v4l2_ctrl_activate(hdl->video_luma_median_filter_bottom, active_filter);
+ v4l2_ctrl_activate(hdl->video_luma_median_filter_top, active_filter);
+ v4l2_ctrl_activate(hdl->video_chroma_median_filter_bottom, active_filter);
+ v4l2_ctrl_activate(hdl->video_chroma_median_filter_top, active_filter);
+ return 0;
+ }
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE:
+ /* video filter type cluster */
+ return cx2341x_hdl_api(hdl,
+ CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 2,
+ hdl->video_luma_spatial_filter_type->val,
+ hdl->video_chroma_spatial_filter_type->val);
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER:
+ /* video filter cluster */
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2,
+ hdl->video_spatial_filter->val,
+ hdl->video_temporal_filter->val);
+
+ case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP:
+ /* video median cluster */
+ return cx2341x_hdl_api(hdl, CX2341X_ENC_SET_CORING_LEVELS, 4,
+ hdl->video_luma_median_filter_bottom->val,
+ hdl->video_luma_median_filter_top->val,
+ hdl->video_chroma_median_filter_bottom->val,
+ hdl->video_chroma_median_filter_top->val);
+ }
+ return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops cx2341x_ops = {
+ .try_ctrl = cx2341x_try_ctrl,
+ .s_ctrl = cx2341x_s_ctrl,
+};
+
+static struct v4l2_ctrl *cx2341x_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
+ u32 id, s32 min, s32 max, s32 step, s32 def)
+{
+ struct v4l2_ctrl_config cfg;
+
+ cx2341x_ctrl_fill(id, &cfg.name, &cfg.type, &min, &max, &step, &def, &cfg.flags);
+ cfg.ops = &cx2341x_ops;
+ cfg.id = id;
+ cfg.min = min;
+ cfg.max = max;
+ cfg.def = def;
+ if (cfg.type == V4L2_CTRL_TYPE_MENU) {
+ cfg.step = 0;
+ cfg.menu_skip_mask = step;
+ cfg.qmenu = cx2341x_get_menu(id);
+ } else {
+ cfg.step = step;
+ cfg.menu_skip_mask = 0;
+ }
+ return v4l2_ctrl_new_custom(hdl, &cfg, NULL);
+}
+
+static struct v4l2_ctrl *cx2341x_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
+ u32 id, s32 min, s32 max, s32 step, s32 def)
+{
+ return v4l2_ctrl_new_std(hdl, &cx2341x_ops, id, min, max, step, def);
+}
+
+static struct v4l2_ctrl *cx2341x_ctrl_new_menu(struct v4l2_ctrl_handler *hdl,
+ u32 id, s32 max, s32 mask, s32 def)
+{
+ return v4l2_ctrl_new_std_menu(hdl, &cx2341x_ops, id, max, mask, def);
+}
+
+int cx2341x_handler_init(struct cx2341x_handler *cxhdl,
+ unsigned nr_of_controls_hint)
+{
+ struct v4l2_ctrl_handler *hdl = &cxhdl->hdl;
+ u32 caps = cxhdl->capabilities;
+ int has_sliced_vbi = caps & CX2341X_CAP_HAS_SLICED_VBI;
+ int has_ac3 = caps & CX2341X_CAP_HAS_AC3;
+ int has_ts = caps & CX2341X_CAP_HAS_TS;
+
+ cxhdl->width = 720;
+ cxhdl->height = 480;
+
+ v4l2_ctrl_handler_init(hdl, nr_of_controls_hint);
+
+ /* Add controls in ascending control ID order for fastest
+ insertion time. */
+ cxhdl->stream_type = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_STREAM_TYPE,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, has_ts ? 0 : 2,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_PS);
+ cxhdl->stream_vbi_fmt = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_STREAM_VBI_FMT,
+ V4L2_MPEG_STREAM_VBI_FMT_IVTV, has_sliced_vbi ? 0 : 2,
+ V4L2_MPEG_STREAM_VBI_FMT_NONE);
+ cxhdl->audio_sampling_freq = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 0,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000);
+ cxhdl->audio_encoding = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_ENCODING,
+ V4L2_MPEG_AUDIO_ENCODING_AC3, has_ac3 ? ~0x12 : ~0x2,
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2);
+ cxhdl->audio_l2_bitrate = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_L2_BITRATE,
+ V4L2_MPEG_AUDIO_L2_BITRATE_384K, 0x1ff,
+ V4L2_MPEG_AUDIO_L2_BITRATE_224K);
+ cxhdl->audio_mode = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_MODE,
+ V4L2_MPEG_AUDIO_MODE_MONO, 0,
+ V4L2_MPEG_AUDIO_MODE_STEREO);
+ cxhdl->audio_mode_extension = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_MODE_EXTENSION,
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 0,
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4);
+ cxhdl->audio_emphasis = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_EMPHASIS,
+ V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 0,
+ V4L2_MPEG_AUDIO_EMPHASIS_NONE);
+ cxhdl->audio_crc = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_CRC,
+ V4L2_MPEG_AUDIO_CRC_CRC16, 0,
+ V4L2_MPEG_AUDIO_CRC_NONE);
+
+ cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_AUDIO_MUTE, 0, 1, 1, 0);
+ if (has_ac3)
+ cxhdl->audio_ac3_bitrate = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_AUDIO_AC3_BITRATE,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 0x03,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_224K);
+ cxhdl->video_encoding = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_VIDEO_ENCODING,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 0,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2);
+ cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_VIDEO_ASPECT,
+ V4L2_MPEG_VIDEO_ASPECT_221x100, 0,
+ V4L2_MPEG_VIDEO_ASPECT_4x3);
+ cxhdl->video_b_frames = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 33, 1, 2);
+ cxhdl->video_gop_size = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+ 1, 34, 1, cxhdl->is_50hz ? 12 : 15);
+ cx2341x_ctrl_new_std(hdl, V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, 0, 1, 1, 1);
+ cxhdl->video_bitrate_mode = cx2341x_ctrl_new_menu(hdl,
+ V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+ cxhdl->video_bitrate = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_BITRATE,
+ 0, 27000000, 1, 6000000);
+ cxhdl->video_bitrate_peak = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+ 0, 27000000, 1, 8000000);
+ cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, 0, 255, 1, 0);
+ cxhdl->video_mute = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_MUTE, 0, 1, 1, 0);
+ cxhdl->video_mute_yuv = cx2341x_ctrl_new_std(hdl,
+ V4L2_CID_MPEG_VIDEO_MUTE_YUV, 0, 0xffffff, 1, 0x008080);
+
+ /* CX23415/6 specific */
+ cxhdl->video_spatial_filter_mode = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 0,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL);
+ cxhdl->video_spatial_filter = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
+ 0, 15, 1, 0);
+ cxhdl->video_luma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE,
+ 0,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR);
+ cxhdl->video_chroma_spatial_filter_type = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
+ 0,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR);
+ cxhdl->video_temporal_filter_mode = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO,
+ 0,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL);
+ cxhdl->video_temporal_filter = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER,
+ 0, 31, 1, 8);
+ cxhdl->video_median_filter_type = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG,
+ 0,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF);
+ cxhdl->video_luma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM,
+ 0, 255, 1, 0);
+ cxhdl->video_luma_median_filter_top = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP,
+ 0, 255, 1, 255);
+ cxhdl->video_chroma_median_filter_bottom = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM,
+ 0, 255, 1, 0);
+ cxhdl->video_chroma_median_filter_top = cx2341x_ctrl_new_custom(hdl,
+ V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP,
+ 0, 255, 1, 255);
+ cx2341x_ctrl_new_custom(hdl, V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS,
+ 0, 1, 1, 0);
+
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ return err;
+ }
+
+ v4l2_ctrl_cluster(8, &cxhdl->audio_sampling_freq);
+ v4l2_ctrl_cluster(2, &cxhdl->video_b_frames);
+ v4l2_ctrl_cluster(5, &cxhdl->stream_type);
+ v4l2_ctrl_cluster(2, &cxhdl->video_mute);
+ v4l2_ctrl_cluster(3, &cxhdl->video_spatial_filter_mode);
+ v4l2_ctrl_cluster(2, &cxhdl->video_luma_spatial_filter_type);
+ v4l2_ctrl_cluster(2, &cxhdl->video_spatial_filter);
+ v4l2_ctrl_cluster(4, &cxhdl->video_luma_median_filter_top);
+
+ return 0;
+}
+EXPORT_SYMBOL(cx2341x_handler_init);
+
+void cx2341x_handler_set_50hz(struct cx2341x_handler *cxhdl, int is_50hz)
+{
+ cxhdl->is_50hz = is_50hz;
+ cxhdl->video_gop_size->default_value = cxhdl->is_50hz ? 12 : 15;
+}
+EXPORT_SYMBOL(cx2341x_handler_set_50hz);
+
+int cx2341x_handler_setup(struct cx2341x_handler *cxhdl)
+{
+ int h = cxhdl->height;
+ int w = cxhdl->width;
+ int err;
+
+ err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_OUTPUT_PORT, 2, cxhdl->port, 0);
+ if (err)
+ return err;
+ err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_RATE, 1, cxhdl->is_50hz);
+ if (err)
+ return err;
+
+ if (v4l2_ctrl_g_ctrl(cxhdl->video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) {
+ w /= 2;
+ h /= 2;
+ }
+ err = cx2341x_hdl_api(cxhdl, CX2341X_ENC_SET_FRAME_SIZE, 2, h, w);
+ if (err)
+ return err;
+ return v4l2_ctrl_handler_setup(&cxhdl->hdl);
+}
+EXPORT_SYMBOL(cx2341x_handler_setup);
+
+void cx2341x_handler_set_busy(struct cx2341x_handler *cxhdl, int busy)
+{
+ v4l2_ctrl_grab(cxhdl->audio_sampling_freq, busy);
+ v4l2_ctrl_grab(cxhdl->audio_encoding, busy);
+ v4l2_ctrl_grab(cxhdl->audio_l2_bitrate, busy);
+ v4l2_ctrl_grab(cxhdl->audio_ac3_bitrate, busy);
+ v4l2_ctrl_grab(cxhdl->stream_vbi_fmt, busy);
+ v4l2_ctrl_grab(cxhdl->stream_type, busy);
+ v4l2_ctrl_grab(cxhdl->video_bitrate_mode, busy);
+ v4l2_ctrl_grab(cxhdl->video_bitrate, busy);
+ v4l2_ctrl_grab(cxhdl->video_bitrate_peak, busy);
+}
+EXPORT_SYMBOL(cx2341x_handler_set_busy);
select VIDEO_BTCX
select VIDEO_TUNER
select VIDEO_TVEEPROM
- select VIDEO_IR
+ select IR_CORE
select VIDEOBUF_DVB
select VIDEOBUF_DMA_SG
select VIDEO_CX25840
cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \
cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \
- cx23885-ioctl.o cx23885-ir.o cx23885-input.o cx23888-ir.o \
- netup-init.o cimax2.o netup-eeprom.o cx23885-f300.o
+ cx23885-ioctl.o cx23885-ir.o cx23885-av.o cx23885-input.o \
+ cx23888-ir.o netup-init.o cimax2.o netup-eeprom.o \
+ cx23885-f300.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
--- /dev/null
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * AV device support routines - non-input, non-vl42_subdev routines
+ *
+ * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
+ *
+ * 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 "cx23885.h"
+
+void cx23885_av_work_handler(struct work_struct *work)
+{
+ struct cx23885_dev *dev =
+ container_of(work, struct cx23885_dev, cx25840_work);
+ bool handled;
+
+ v4l2_subdev_call(dev->sd_cx25840, core, interrupt_service_routine,
+ PCI_MSK_AV_CORE, &handled);
+ cx23885_irq_enable(dev, PCI_MSK_AV_CORE);
+}
--- /dev/null
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * AV device support routines - non-input, non-vl42_subdev routines
+ *
+ * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
+ *
+ * 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.
+ */
+
+#ifndef _CX23885_AV_H_
+#define _CX23885_AV_H_
+void cx23885_av_work_handler(struct work_struct *work);
+#endif
#include "netup-init.h"
#include "cx23888-ir.h"
+static unsigned int enable_885_ir;
+module_param(enable_885_ir, int, 0644);
+MODULE_PARM_DESC(enable_885_ir,
+ "Enable integrated IR controller for supported\n"
+ "\t\t CX2388[57] boards that are wired for it:\n"
+ "\t\t\tHVR-1250 (reported safe)\n"
+ "\t\t\tTeVii S470 (reported unsafe)\n"
+ "\t\t This can cause an interrupt storm with some cards.\n"
+ "\t\t Default: 0 [Disabled]");
+
/* ------------------------------------------------------------------ */
/* board config info */
case 79101:
/* WinTV-HVR1250 (PCIe, Retail, IR, half height,
ATSC and Basic analog */
+ case 79501:
+ /* WinTV-HVR1250 (PCIe, No IR, half height,
+ ATSC [at least] and Basic analog) */
case 79561:
/* WinTV-HVR1250 (PCIe, OEM, No IR, half height,
ATSC and Basic analog */
int cx23885_ir_init(struct cx23885_dev *dev)
{
+ static struct v4l2_subdev_io_pin_config ir_rxtx_pin_cfg[] = {
+ {
+ .flags = V4L2_SUBDEV_IO_PIN_INPUT,
+ .pin = CX23885_PIN_IR_RX_GPIO19,
+ .function = CX23885_PAD_IR_RX,
+ .value = 0,
+ .strength = CX25840_PIN_DRIVE_MEDIUM,
+ }, {
+ .flags = V4L2_SUBDEV_IO_PIN_OUTPUT,
+ .pin = CX23885_PIN_IR_TX_GPIO20,
+ .function = CX23885_PAD_IR_TX,
+ .value = 0,
+ .strength = CX25840_PIN_DRIVE_MEDIUM,
+ }
+ };
+ const size_t ir_rxtx_pin_cfg_count = ARRAY_SIZE(ir_rxtx_pin_cfg);
+
+ static struct v4l2_subdev_io_pin_config ir_rx_pin_cfg[] = {
+ {
+ .flags = V4L2_SUBDEV_IO_PIN_INPUT,
+ .pin = CX23885_PIN_IR_RX_GPIO19,
+ .function = CX23885_PAD_IR_RX,
+ .value = 0,
+ .strength = CX25840_PIN_DRIVE_MEDIUM,
+ }
+ };
+ const size_t ir_rx_pin_cfg_count = ARRAY_SIZE(ir_rx_pin_cfg);
+
+ struct v4l2_subdev_ir_parameters params;
int ret = 0;
switch (dev->board) {
- case CX23885_BOARD_HAUPPAUGE_HVR1250:
case CX23885_BOARD_HAUPPAUGE_HVR1500:
case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
case CX23885_BOARD_HAUPPAUGE_HVR1800:
if (ret)
break;
dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
- dev->pci_irqmask |= PCI_MSK_IR;
+ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+ ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg);
+ /*
+ * For these boards we need to invert the Tx output via the
+ * IR controller to have the LED off while idle
+ */
+ v4l2_subdev_call(dev->sd_ir, ir, tx_g_parameters, ¶ms);
+ params.enable = false;
+ params.shutdown = false;
+ params.invert_level = true;
+ v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, ¶ms);
+ params.shutdown = true;
+ v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, ¶ms);
+ break;
+ case CX23885_BOARD_TEVII_S470:
+ if (!enable_885_ir)
+ break;
+ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
+ if (dev->sd_ir == NULL) {
+ ret = -ENODEV;
+ break;
+ }
+ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+ ir_rx_pin_cfg_count, ir_rx_pin_cfg);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ if (!enable_885_ir)
+ break;
+ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
+ if (dev->sd_ir == NULL) {
+ ret = -ENODEV;
+ break;
+ }
+ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+ ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg);
break;
case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
request_module("ir-kbd-i2c");
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
- dev->pci_irqmask &= ~PCI_MSK_IR;
- cx_clear(PCI_INT_MSK, PCI_MSK_IR);
+ cx23885_irq_remove(dev, PCI_MSK_IR);
cx23888_ir_remove(dev);
dev->sd_ir = NULL;
break;
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ cx23885_irq_remove(dev, PCI_MSK_AV_CORE);
+ /* sd_ir is a duplicate pointer to the AV Core, just clear it */
+ dev->sd_ir = NULL;
+ break;
}
}
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
- if (dev->sd_ir && (dev->pci_irqmask & PCI_MSK_IR))
- cx_set(PCI_INT_MSK, PCI_MSK_IR);
+ if (dev->sd_ir)
+ cx23885_irq_add_enable(dev, PCI_MSK_IR);
+ break;
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ if (dev->sd_ir)
+ cx23885_irq_add_enable(dev, PCI_MSK_AV_CORE);
break;
}
}
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ if (dev->i2c_bus[0].i2c_rc == 0) {
+ if (eeprom[0x80] != 0x84)
+ hauppauge_eeprom(dev, eeprom+0xc0);
+ else
+ hauppauge_eeprom(dev, eeprom+0x80);
+ }
+ break;
case CX23885_BOARD_HAUPPAUGE_HVR1500:
case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
case CX23885_BOARD_HAUPPAUGE_HVR1400:
* loaded, ensure this happens.
*/
switch (dev->board) {
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ /* Currently only enabled for the integrated IR controller */
+ if (!enable_885_ir)
+ break;
case CX23885_BOARD_HAUPPAUGE_HVR1800:
case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
case CX23885_BOARD_HAUPPAUGE_HVR1700:
dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[2].i2c_adap,
"cx25840", "cx25840", 0x88 >> 1, NULL);
- v4l2_subdev_call(dev->sd_cx25840, core, load_fw);
+ if (dev->sd_cx25840) {
+ dev->sd_cx25840->grp_id = CX23885_HW_AV_CORE;
+ v4l2_subdev_call(dev->sd_cx25840, core, load_fw);
+ }
break;
}
#include "cimax2.h"
#include "cx23888-ir.h"
#include "cx23885-ir.h"
+#include "cx23885-av.h"
#include "cx23885-input.h"
MODULE_DESCRIPTION("Driver for cx23885 based TV cards");
},
};
+void cx23885_irq_add(struct cx23885_dev *dev, u32 mask)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ dev->pci_irqmask |= mask;
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ dev->pci_irqmask |= mask;
+ cx_set(PCI_INT_MSK, mask);
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask)
+{
+ u32 v;
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ v = mask & dev->pci_irqmask;
+ if (v)
+ cx_set(PCI_INT_MSK, v);
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static inline void cx23885_irq_enable_all(struct cx23885_dev *dev)
+{
+ cx23885_irq_enable(dev, 0xffffffff);
+}
+
+void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ cx_clear(PCI_INT_MSK, mask);
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static inline void cx23885_irq_disable_all(struct cx23885_dev *dev)
+{
+ cx23885_irq_disable(dev, 0xffffffff);
+}
+
+void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ dev->pci_irqmask &= ~mask;
+ cx_clear(PCI_INT_MSK, mask);
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static u32 cx23885_irq_get_mask(struct cx23885_dev *dev)
+{
+ u32 v;
+ unsigned long flags;
+ spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+ v = cx_read(PCI_INT_MSK);
+
+ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+ return v;
+}
+
static int cx23885_risc_decode(u32 risc)
{
static char *instr[16] = {
cx_write(UART_CTL, 0);
/* Disable Interrupts */
- cx_write(PCI_INT_MSK, 0);
+ cx23885_irq_disable_all(dev);
cx_write(VID_A_INT_MSK, 0);
cx_write(VID_B_INT_MSK, 0);
cx_write(VID_C_INT_MSK, 0);
{
int i;
+ spin_lock_init(&dev->pci_irqmask_lock);
+
mutex_init(&dev->lock);
mutex_init(&dev->gpio_lock);
dev->pci_bus = dev->pci->bus->number;
dev->pci_slot = PCI_SLOT(dev->pci->devfn);
- dev->pci_irqmask = 0x001f00;
+ cx23885_irq_add(dev, 0x001f00);
if (cx23885_boards[dev->board].cimax > 0)
- dev->pci_irqmask |= 0x01800000; /* for CiMaxes */
+ cx23885_irq_add(dev, 0x01800000); /* for CiMaxes */
/* External Master 1 Bus */
dev->i2c_bus[0].nr = 0;
dprintk(1, "%s() DEV_CNTRL2 0x%08X\n", __func__,
cx_read(DEV_CNTRL2));
dprintk(1, "%s() PCI_INT_MSK 0x%08X\n", __func__,
- cx_read(PCI_INT_MSK));
+ cx23885_irq_get_mask(dev));
dprintk(1, "%s() AUD_INT_INT_MSK 0x%08X\n", __func__,
cx_read(AUDIO_INT_INT_MSK));
dprintk(1, "%s() AUD_INT_DMA_CTL 0x%08X\n", __func__,
dprintk(1, "%s() enabling TS int's and DMA\n", __func__);
cx_set(port->reg_ts_int_msk, port->ts_int_msk_val);
cx_set(port->reg_dma_ctl, port->dma_ctl_val);
- cx_set(PCI_INT_MSK, dev->pci_irqmask | port->pci_irqmask);
+ cx23885_irq_add(dev, port->pci_irqmask);
+ cx23885_irq_enable_all(dev);
break;
default:
BUG();
u32 ts1_status, ts1_mask;
u32 ts2_status, ts2_mask;
int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0;
- bool ir_handled = false;
+ bool subdev_handled;
pci_status = cx_read(PCI_INT_STAT);
- pci_mask = cx_read(PCI_INT_MSK);
+ pci_mask = cx23885_irq_get_mask(dev);
vida_status = cx_read(VID_A_INT_STAT);
vida_mask = cx_read(VID_A_INT_MSK);
ts1_status = cx_read(VID_B_INT_STAT);
PCI_MSK_VID_C | PCI_MSK_VID_B | PCI_MSK_VID_A |
PCI_MSK_AUD_INT | PCI_MSK_AUD_EXT |
PCI_MSK_GPIO0 | PCI_MSK_GPIO1 |
- PCI_MSK_IR)) {
+ PCI_MSK_AV_CORE | PCI_MSK_IR)) {
if (pci_status & PCI_MSK_RISC_RD)
dprintk(7, " (PCI_MSK_RISC_RD 0x%08x)\n",
dprintk(7, " (PCI_MSK_GPIO1 0x%08x)\n",
PCI_MSK_GPIO1);
+ if (pci_status & PCI_MSK_AV_CORE)
+ dprintk(7, " (PCI_MSK_AV_CORE 0x%08x)\n",
+ PCI_MSK_AV_CORE);
+
if (pci_status & PCI_MSK_IR)
dprintk(7, " (PCI_MSK_IR 0x%08x)\n",
PCI_MSK_IR);
handled += cx23885_video_irq(dev, vida_status);
if (pci_status & PCI_MSK_IR) {
- v4l2_subdev_call(dev->sd_ir, ir, interrupt_service_routine,
- pci_status, &ir_handled);
- if (ir_handled)
+ subdev_handled = false;
+ v4l2_subdev_call(dev->sd_ir, core, interrupt_service_routine,
+ pci_status, &subdev_handled);
+ if (subdev_handled)
handled++;
}
+ if ((pci_status & pci_mask) & PCI_MSK_AV_CORE) {
+ cx23885_irq_disable(dev, PCI_MSK_AV_CORE);
+ if (!schedule_work(&dev->cx25840_work))
+ printk(KERN_ERR "%s: failed to set up deferred work for"
+ " AV Core/IR interrupt. Interrupt is disabled"
+ " and won't be re-enabled\n", dev->name);
+ handled++;
+ }
+
if (handled)
cx_write(PCI_INT_STAT, pci_status);
out:
dev = to_cx23885(sd->v4l2_dev);
switch (notification) {
- case V4L2_SUBDEV_IR_RX_NOTIFY: /* Called in an IRQ context */
+ case V4L2_SUBDEV_IR_RX_NOTIFY: /* Possibly called in an IRQ context */
if (sd == dev->sd_ir)
cx23885_ir_rx_v4l2_dev_notify(sd, *(u32 *)arg);
break;
- case V4L2_SUBDEV_IR_TX_NOTIFY: /* Called in an IRQ context */
+ case V4L2_SUBDEV_IR_TX_NOTIFY: /* Possibly called in an IRQ context */
if (sd == dev->sd_ir)
cx23885_ir_tx_v4l2_dev_notify(sd, *(u32 *)arg);
break;
static void cx23885_v4l2_dev_notify_init(struct cx23885_dev *dev)
{
+ INIT_WORK(&dev->cx25840_work, cx23885_av_work_handler);
INIT_WORK(&dev->ir_rx_work, cx23885_ir_rx_work_handler);
INIT_WORK(&dev->ir_tx_work, cx23885_ir_tx_work_handler);
dev->v4l2_dev.notify = cx23885_v4l2_dev_notify;
switch (dev->board) {
case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
- cx_set(PCI_INT_MSK, 0x01800000); /* for NetUP */
+ cx23885_irq_add_enable(dev, 0x01800000); /* for NetUP */
break;
}
if (!i2c_wait_done(i2c_adap))
return -EIO;
if (!i2c_slave_did_ack(i2c_adap))
- return -EIO;
+ return -ENXIO;
dprintk(1, "%s() returns 0\n", __func__);
return 0;
cx_write(bus->reg_wdata, wdata);
cx_write(bus->reg_ctrl, ctrl);
- retval = i2c_wait_done(i2c_adap);
- if (retval < 0)
- goto err;
- if (retval == 0)
+ if (!i2c_wait_done(i2c_adap))
goto eio;
+ if (!i2c_slave_did_ack(i2c_adap)) {
+ retval = -ENXIO;
+ goto err;
+ }
if (i2c_debug) {
printk(" <W %02x %02x", msg->addr << 1, msg->buf[0]);
if (!(ctrl & I2C_NOSTOP))
cx_write(bus->reg_wdata, wdata);
cx_write(bus->reg_ctrl, ctrl);
- retval = i2c_wait_done(i2c_adap);
- if (retval < 0)
- goto err;
- if (retval == 0)
+ if (!i2c_wait_done(i2c_adap))
goto eio;
if (i2c_debug) {
dprintk(1, " %02x", msg->buf[cnt]);
if (!i2c_wait_done(i2c_adap))
return -EIO;
if (!i2c_slave_did_ack(i2c_adap))
- return -EIO;
+ return -ENXIO;
dprintk(1, "%s() returns 0\n", __func__);
cx_write(bus->reg_addr, msg->addr << 25);
cx_write(bus->reg_ctrl, ctrl);
- retval = i2c_wait_done(i2c_adap);
- if (retval < 0)
- goto err;
- if (retval == 0)
+ if (!i2c_wait_done(i2c_adap))
goto eio;
+ if (cnt == 0 && !i2c_slave_did_ack(i2c_adap)) {
+ retval = -ENXIO;
+ goto err;
+ }
msg->buf[cnt] = cx_read(bus->reg_rdata) & 0xff;
if (i2c_debug) {
dprintk(1, " %02x", msg->buf[cnt]);
#define MODULE_NAME "cx23885"
-static void convert_measurement(u32 x, struct ir_raw_event *y)
-{
- if (x == V4L2_SUBDEV_IR_PULSE_RX_SEQ_END) {
- y->pulse = false;
- y->duration = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
- return;
- }
-
- y->pulse = (x & V4L2_SUBDEV_IR_PULSE_LEVEL_MASK) ? true : false;
- y->duration = x & V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
-}
-
static void cx23885_input_process_measurements(struct cx23885_dev *dev,
bool overrun)
{
struct cx23885_kernel_ir *kernel_ir = dev->kernel_ir;
- struct ir_raw_event kernel_ir_event;
- u32 sd_ir_data[64];
ssize_t num;
int count, i;
bool handle = false;
+ struct ir_raw_event ir_core_event[64];
do {
num = 0;
- v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) sd_ir_data,
- sizeof(sd_ir_data), &num);
+ v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ir_core_event,
+ sizeof(ir_core_event), &num);
- count = num / sizeof(u32);
+ count = num / sizeof(struct ir_raw_event);
for (i = 0; i < count; i++) {
- convert_measurement(sd_ir_data[i], &kernel_ir_event);
ir_raw_event_store(kernel_ir->inp_dev,
- &kernel_ir_event);
+ &ir_core_event[i]);
handle = true;
}
} while (num != 0);
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
/*
- * The only board we handle right now. However other boards
+ * The only boards we handle right now. However other boards
* using the CX2388x integrated IR controller should be similar
*/
break;
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
/*
* The IR controller on this board only returns pulse widths.
* Any other mode setting will fail to set up the device.
* mark is received as low logic level;
* falling edges are detected as rising edges; etc.
*/
- params.invert = true;
+ params.invert_level = true;
+ break;
+ case CX23885_BOARD_TEVII_S470:
+ /*
+ * The IR controller on this board only returns pulse widths.
+ * Any other mode setting will fail to set up the device.
+ */
+ params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+ params.enable = true;
+ params.interrupt_enable = true;
+ params.shutdown = false;
+
+ /* Setup for a standard NEC protocol */
+ params.carrier_freq = 37917; /* Hz, 455 kHz/12 for NEC */
+ params.carrier_range_lower = 33000; /* Hz */
+ params.carrier_range_upper = 43000; /* Hz */
+ params.duty_cycle = 33; /* percent, 33 percent for NEC */
+
+ /*
+ * NEC max pulse width: (64/3)/(455 kHz/12) * 16 nec_units
+ * (64/3)/(455 kHz/12) * 16 nec_units * 1.375 = 12378022 ns
+ */
+ params.max_pulse_width = 12378022; /* ns */
+
+ /*
+ * NEC noise filter min width: (64/3)/(455 kHz/12) * 1 nec_unit
+ * (64/3)/(455 kHz/12) * 1 nec_units * 0.625 = 351648 ns
+ */
+ params.noise_filter_min_width = 351648; /* ns */
+
+ params.modulation = false;
+ params.invert_level = true;
break;
}
v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms);
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1850:
case CX23885_BOARD_HAUPPAUGE_HVR1290:
- /* Integrated CX23888 IR controller */
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ /* Integrated CX2388[58] IR controller */
driver_type = RC_DRIVER_IR_RAW;
allowed_protos = IR_TYPE_ALL;
/* The grey Hauppauge RC-5 remote */
rc_map = RC_MAP_RC5_HAUPPAUGE_NEW;
break;
+ case CX23885_BOARD_TEVII_S470:
+ /* Integrated CX23885 IR controller */
+ driver_type = RC_DRIVER_IR_RAW;
+ allowed_protos = IR_TYPE_ALL;
+ /* A guess at the remote */
+ rc_map = RC_MAP_TEVII_NEC;
+ break;
default:
return -ENODEV;
}
}
-/* Called in an IRQ context */
+/* Possibly called in an IRQ context */
void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
{
struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
set_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications);
if (events & V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN)
set_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications);
- schedule_work(&dev->ir_rx_work);
+
+ /*
+ * For the integrated AV core, we are already in a workqueue context.
+ * For the CX23888 integrated IR, we are in an interrupt context.
+ */
+ if (sd == dev->sd_cx25840)
+ cx23885_ir_rx_work_handler(&dev->ir_rx_work);
+ else
+ schedule_work(&dev->ir_rx_work);
}
-/* Called in an IRQ context */
+/* Possibly called in an IRQ context */
void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
{
struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
if (events & V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ)
set_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications);
- schedule_work(&dev->ir_tx_work);
+
+ /*
+ * For the integrated AV core, we are already in a workqueue context.
+ * For the CX23888 integrated IR, we are in an interrupt context.
+ */
+ if (sd == dev->sd_cx25840)
+ cx23885_ir_tx_work_handler(&dev->ir_tx_work);
+ else
+ schedule_work(&dev->ir_tx_work);
}
#define DEV_CNTRL2 0x00040000
#define PCI_MSK_IR (1 << 28)
+#define PCI_MSK_AV_CORE (1 << 27)
#define PCI_MSK_GPIO1 (1 << 24)
#define PCI_MSK_GPIO0 (1 << 23)
#define PCI_MSK_APB_DMA (1 << 12)
q->count = 1;
/* enable irqs */
- cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | 0x01);
+ cx23885_irq_add_enable(dev, 0x01);
cx_set(VID_A_INT_MSK, 0x000022);
/* start dma */
q->count = 1;
/* enable irq */
- cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | 0x01);
+ cx23885_irq_add_enable(dev, 0x01);
cx_set(VID_A_INT_MSK, 0x000011);
/* start dma */
return 0;
}
+static int vidioc_log_status(struct file *file, void *priv)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ printk(KERN_INFO
+ "%s/0: ============ START LOG STATUS ============\n",
+ dev->name);
+ call_all(dev, core, log_status);
+ printk(KERN_INFO
+ "%s/0: ============= END LOG STATUS =============\n",
+ dev->name);
+ return 0;
+}
+
static int vidioc_queryctrl(struct file *file, void *priv,
struct v4l2_queryctrl *qctrl)
{
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
+ .vidioc_log_status = vidioc_log_status,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
void cx23885_video_unregister(struct cx23885_dev *dev)
{
dprintk(1, "%s()\n", __func__);
- cx_clear(PCI_INT_MSK, 1);
+ cx23885_irq_remove(dev, 0x01);
if (dev->video_dev) {
if (video_is_registered(dev->video_dev))
VID_A_DMA_CTL, 0x11, 0x00);
/* Don't enable VBI yet */
- cx_set(PCI_INT_MSK, 1);
+
+ cx23885_irq_add_enable(dev, 0x01);
if (TUNER_ABSENT != dev->tuner_type) {
struct v4l2_subdev *sd = NULL;
u32 __iomem *lmmio;
u8 __iomem *bmmio;
int pci_irqmask;
+ spinlock_t pci_irqmask_lock; /* protects mask reg too */
int hwrevision;
/* This valud is board specific and is used to configure the
unsigned char radio_addr;
unsigned int has_radio;
struct v4l2_subdev *sd_cx25840;
+ struct work_struct cx25840_work;
/* Infrared */
struct v4l2_subdev *sd_ir;
#define call_all(dev, o, f, args...) \
v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
-#define CX23885_HW_888_IR (1 << 0)
+#define CX23885_HW_888_IR (1 << 0)
+#define CX23885_HW_AV_CORE (1 << 1)
#define call_hw(dev, grpid, o, f, args...) \
v4l2_device_call_all(&dev->v4l2_dev, grpid, o, f, ##args)
extern void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask,
int asoutput);
+extern void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask);
/* ----------------------------------------------------------- */
/* cx23885-cards.c */
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
+#include <media/ir-core.h>
#include "cx23885.h"
#define CNTRL_CPL 0x00001000
#define CNTRL_LBM 0x00002000
#define CNTRL_R 0x00004000
+/* CX23888 specific control flag */
+#define CNTRL_IVO 0x00008000
#define CX23888_IR_TXCLK_REG 0x170004
#define TXCLK_TCD 0x0000FFFF
#define CX23888_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */
#define CX23888_IR_REFCLK_FREQ (CX23888_VIDCLK_FREQ / 2)
-#define CX23888_IR_RX_KFIFO_SIZE (512 * sizeof(u32))
-#define CX23888_IR_TX_KFIFO_SIZE (512 * sizeof(u32))
+/*
+ * We use this union internally for convenience, but callers to tx_write
+ * and rx_read will be expecting records of type struct ir_raw_event.
+ * Always ensure the size of this union is dictated by struct ir_raw_event.
+ */
+union cx23888_ir_fifo_rec {
+ u32 hw_fifo_data;
+ struct ir_raw_event ir_core_data;
+};
+
+#define CX23888_IR_RX_KFIFO_SIZE (256 * sizeof(union cx23888_ir_fifo_rec))
+#define CX23888_IR_TX_KFIFO_SIZE (256 * sizeof(union cx23888_ir_fifo_rec))
struct cx23888_ir_state {
struct v4l2_subdev sd;
invert ? CNTRL_CPL : 0);
}
+static inline void control_tx_level_invert(struct cx23885_dev *dev,
+ bool invert)
+{
+ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_IVO,
+ invert ? CNTRL_IVO : 0);
+}
+
/*
* IR Rx & Tx Clock Register helpers
*/
{
u64 pulse_clocks;
- if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
- ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
+ if (ns > IR_MAX_DURATION)
+ ns = IR_MAX_DURATION;
pulse_clocks = ns_to_pulse_clocks(ns);
*divider = pulse_clocks_to_clock_divider(pulse_clocks);
cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider);
{
u64 pulse_clocks;
- if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
- ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
+ if (ns > IR_MAX_DURATION)
+ ns = IR_MAX_DURATION;
pulse_clocks = ns_to_pulse_clocks(ns);
*divider = pulse_clocks_to_clock_divider(pulse_clocks);
cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider);
u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG);
u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG);
- u32 rx_data[FIFO_RX_DEPTH];
- int i, j, k;
+ union cx23888_ir_fifo_rec rx_data[FIFO_RX_DEPTH];
+ unsigned int i, j, k;
u32 events, v;
int tsr, rsr, rto, ror, tse, rse, rte, roe, kror;
for (j = 0;
(v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) {
v = cx23888_ir_read4(dev, CX23888_IR_FIFO_REG);
- rx_data[i++] = v & ~FIFO_RX_NDV;
+ rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV;
+ i++;
}
if (i == 0)
break;
- j = i * sizeof(u32);
+ j = i * sizeof(union cx23888_ir_fifo_rec);
k = kfifo_in_locked(&state->rx_kfifo,
(unsigned char *) rx_data, j,
&state->rx_kfifo_lock);
u16 divider = (u16) atomic_read(&state->rxclk_divider);
unsigned int i, n;
- u32 *p;
- u32 u, v;
+ union cx23888_ir_fifo_rec *p;
+ unsigned u, v;
- n = count / sizeof(u32) * sizeof(u32);
+ n = count / sizeof(union cx23888_ir_fifo_rec)
+ * sizeof(union cx23888_ir_fifo_rec);
if (n == 0) {
*num = 0;
return 0;
n = kfifo_out_locked(&state->rx_kfifo, buf, n, &state->rx_kfifo_lock);
- n /= sizeof(u32);
- *num = n * sizeof(u32);
+ n /= sizeof(union cx23888_ir_fifo_rec);
+ *num = n * sizeof(union cx23888_ir_fifo_rec);
+
+ for (p = (union cx23888_ir_fifo_rec *) buf, i = 0; i < n; p++, i++) {
- for (p = (u32 *) buf, i = 0; i < n; p++, i++) {
- if ((*p & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
- *p = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END;
+ if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
+ /* Assume RTO was because of no IR light input */
+ u = 0;
v4l2_dbg(2, ir_888_debug, sd, "rx read: end of rx\n");
- continue;
+ } else {
+ u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0;
+ if (invert)
+ u = u ? 0 : 1;
}
- u = (*p & FIFO_RXTX_LVL) ? V4L2_SUBDEV_IR_PULSE_LEVEL_MASK : 0;
- if (invert)
- u = u ? 0 : V4L2_SUBDEV_IR_PULSE_LEVEL_MASK;
+ v = (unsigned) pulse_width_count_to_ns(
+ (u16) (p->hw_fifo_data & FIFO_RXTX), divider);
+ if (v > IR_MAX_DURATION)
+ v = IR_MAX_DURATION;
- v = (u32) pulse_width_count_to_ns((u16) (*p & FIFO_RXTX),
- divider);
- if (v >= V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
- v = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS - 1;
-
- *p = u | v;
+ p->ir_core_data.pulse = u;
+ p->ir_core_data.duration = v;
v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns %s\n",
v, u ? "mark" : "space");
o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
- o->bytes_per_data_element = p->bytes_per_data_element = sizeof(u32);
+ o->bytes_per_data_element = p->bytes_per_data_element
+ = sizeof(union cx23888_ir_fifo_rec);
/* Before we tweak the hardware, we have to disable the receiver */
irqenable_rx(dev, 0);
&p->carrier_range_upper);
o->carrier_range_lower = p->carrier_range_lower;
o->carrier_range_upper = p->carrier_range_upper;
+
+ p->max_pulse_width =
+ (u32) pulse_width_count_to_ns(FIFO_RXTX, rxclk_divider);
} else {
p->max_pulse_width =
rxclk_rx_s_max_pulse_width(dev, p->max_pulse_width,
&rxclk_divider);
- o->max_pulse_width = p->max_pulse_width;
}
+ o->max_pulse_width = p->max_pulse_width;
atomic_set(&state->rxclk_divider, rxclk_divider);
p->noise_filter_min_width =
control_rx_s_edge_detection(dev, CNTRL_EDG_BOTH);
- o->invert = p->invert;
- atomic_set(&state->rx_invert, p->invert);
+ o->invert_level = p->invert_level;
+ atomic_set(&state->rx_invert, p->invert_level);
o->interrupt_enable = p->interrupt_enable;
o->enable = p->enable;
o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
- o->bytes_per_data_element = p->bytes_per_data_element = sizeof(u32);
+ o->bytes_per_data_element = p->bytes_per_data_element
+ = sizeof(union cx23888_ir_fifo_rec);
/* Before we tweak the hardware, we have to disable the transmitter */
irqenable_tx(dev, 0);
p->duty_cycle = cduty_tx_s_duty_cycle(dev, p->duty_cycle);
o->duty_cycle = p->duty_cycle;
+
+ p->max_pulse_width =
+ (u32) pulse_width_count_to_ns(FIFO_RXTX, txclk_divider);
} else {
p->max_pulse_width =
txclk_tx_s_max_pulse_width(dev, p->max_pulse_width,
&txclk_divider);
- o->max_pulse_width = p->max_pulse_width;
}
+ o->max_pulse_width = p->max_pulse_width;
atomic_set(&state->txclk_divider, txclk_divider);
p->resolution = clock_divider_to_resolution(txclk_divider);
/* FIXME - make this dependent on resolution for better performance */
control_tx_irq_watermark(dev, TX_FIFO_HALF_EMPTY);
- control_tx_polarity_invert(dev, p->invert);
- o->invert = p->invert;
+ control_tx_polarity_invert(dev, p->invert_carrier_sense);
+ o->invert_carrier_sense = p->invert_carrier_sense;
+
+ control_tx_level_invert(dev, p->invert_level);
+ o->invert_level = p->invert_level;
o->interrupt_enable = p->interrupt_enable;
o->enable = p->enable;
"-%1d/+%1d, %u to %u Hz\n", i, j,
clock_divider_to_freq(rxclk, 16 + j),
clock_divider_to_freq(rxclk, 16 - i));
- } else {
- v4l2_info(sd, "\tMax measurable pulse width: %u us, "
- "%llu ns\n",
- pulse_width_count_to_us(FIFO_RXTX, rxclk),
- pulse_width_count_to_ns(FIFO_RXTX, rxclk));
}
+ v4l2_info(sd, "\tMax measurable pulse width: %u us, %llu ns\n",
+ pulse_width_count_to_us(FIFO_RXTX, rxclk),
+ pulse_width_count_to_ns(FIFO_RXTX, rxclk));
v4l2_info(sd, "\tLow pass filter: %s\n",
filtr ? "enabled" : "disabled");
if (filtr)
cntrl & CNTRL_TFE ? "enabled" : "disabled");
v4l2_info(sd, "\tFIFO interrupt watermark: %s\n",
cntrl & CNTRL_TIC ? "not empty" : "half full or less");
- v4l2_info(sd, "\tSignal polarity: %s\n",
- cntrl & CNTRL_CPL ? "0:mark 1:space" : "0:space 1:mark");
+ v4l2_info(sd, "\tOutput pin level inversion %s\n",
+ cntrl & CNTRL_IVO ? "yes" : "no");
+ v4l2_info(sd, "\tCarrier polarity: %s\n",
+ cntrl & CNTRL_CPL ? "space:burst mark:noburst"
+ : "space:noburst mark:burst");
if (cntrl & CNTRL_MOD) {
v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n",
clock_divider_to_carrier_freq(txclk));
v4l2_info(sd, "\tCarrier duty cycle: %2u/16\n",
cduty + 1);
- } else {
- v4l2_info(sd, "\tMax pulse width: %u us, "
- "%llu ns\n",
- pulse_width_count_to_us(FIFO_RXTX, txclk),
- pulse_width_count_to_ns(FIFO_RXTX, txclk));
}
+ v4l2_info(sd, "\tMax pulse width: %u us, %llu ns\n",
+ pulse_width_count_to_us(FIFO_RXTX, txclk),
+ pulse_width_count_to_ns(FIFO_RXTX, txclk));
v4l2_info(sd, "\tBusy: %s\n",
stats & STATS_TBY ? "yes" : "no");
v4l2_info(sd, "\tFIFO service requested: %s\n",
.g_register = cx23888_ir_g_register,
.s_register = cx23888_ir_s_register,
#endif
+ .interrupt_service_routine = cx23888_ir_irq_handler,
};
static const struct v4l2_subdev_ir_ops cx23888_ir_ir_ops = {
- .interrupt_service_routine = cx23888_ir_irq_handler,
-
.rx_read = cx23888_ir_rx_read,
.rx_g_parameters = cx23888_ir_rx_g_parameters,
.rx_s_parameters = cx23888_ir_rx_s_parameters,
};
static const struct v4l2_subdev_ir_parameters default_rx_params = {
- .bytes_per_data_element = sizeof(u32),
+ .bytes_per_data_element = sizeof(union cx23888_ir_fifo_rec),
.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
.enable = false,
.noise_filter_min_width = 333333, /* ns */
.carrier_range_lower = 35000,
.carrier_range_upper = 37000,
- .invert = false,
+ .invert_level = false,
};
static const struct v4l2_subdev_ir_parameters default_tx_params = {
- .bytes_per_data_element = sizeof(u32),
+ .bytes_per_data_element = sizeof(union cx23888_ir_fifo_rec),
.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
.enable = false,
.modulation = true,
.carrier_freq = 36000, /* 36 kHz - RC-5 carrier */
.duty_cycle = 25, /* 25 % - RC-5 carrier */
- .invert = false,
+ .invert_level = false,
+ .invert_carrier_sense = false,
};
int cx23888_ir_probe(struct cx23885_dev *dev)
cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \
- cx25840-vbi.o
+ cx25840-vbi.o cx25840-ir.o
obj-$(CONFIG_VIDEO_CX25840) += cx25840.o
cx25840_and_or(client, 0x803, ~0x10, 0x10);
}
-static int get_volume(struct i2c_client *client)
-{
- struct cx25840_state *state = to_state(i2c_get_clientdata(client));
- int vol;
-
- if (state->unmute_volume >= 0)
- return state->unmute_volume;
-
- /* Volume runs +18dB to -96dB in 1/2dB steps
- * change to fit the msp3400 -114dB to +12dB range */
-
- /* check PATH1_VOLUME */
- vol = 228 - cx25840_read(client, 0x8d4);
- vol = (vol / 2) + 23;
- return vol << 9;
-}
-
static void set_volume(struct i2c_client *client, int volume)
{
- struct cx25840_state *state = to_state(i2c_get_clientdata(client));
int vol;
- if (state->unmute_volume >= 0) {
- state->unmute_volume = volume;
- return;
- }
-
/* Convert the volume to msp3400 values (0-127) */
vol = volume >> 9;
cx25840_write(client, 0x8d4, 228 - (vol * 2));
}
-static int get_bass(struct i2c_client *client)
-{
- /* bass is 49 steps +12dB to -12dB */
-
- /* check PATH1_EQ_BASS_VOL */
- int bass = cx25840_read(client, 0x8d9) & 0x3f;
- bass = (((48 - bass) * 0xffff) + 47) / 48;
- return bass;
-}
-
-static void set_bass(struct i2c_client *client, int bass)
-{
- /* PATH1_EQ_BASS_VOL */
- cx25840_and_or(client, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff));
-}
-
-static int get_treble(struct i2c_client *client)
-{
- /* treble is 49 steps +12dB to -12dB */
-
- /* check PATH1_EQ_TREBLE_VOL */
- int treble = cx25840_read(client, 0x8db) & 0x3f;
- treble = (((48 - treble) * 0xffff) + 47) / 48;
- return treble;
-}
-
-static void set_treble(struct i2c_client *client, int treble)
-{
- /* PATH1_EQ_TREBLE_VOL */
- cx25840_and_or(client, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff));
-}
-
-static int get_balance(struct i2c_client *client)
-{
- /* balance is 7 bit, 0 to -96dB */
-
- /* check PATH1_BAL_LEVEL */
- int balance = cx25840_read(client, 0x8d5) & 0x7f;
- /* check PATH1_BAL_LEFT */
- if ((cx25840_read(client, 0x8d5) & 0x80) == 0)
- balance = 0x80 - balance;
- else
- balance = 0x80 + balance;
- return balance << 8;
-}
-
static void set_balance(struct i2c_client *client, int balance)
{
int bal = balance >> 8;
}
}
-static int get_mute(struct i2c_client *client)
-{
- struct cx25840_state *state = to_state(i2c_get_clientdata(client));
-
- return state->unmute_volume >= 0;
-}
-
-static void set_mute(struct i2c_client *client, int mute)
-{
- struct cx25840_state *state = to_state(i2c_get_clientdata(client));
-
- if (mute && state->unmute_volume == -1) {
- int vol = get_volume(client);
-
- set_volume(client, 0);
- state->unmute_volume = vol;
- }
- else if (!mute && state->unmute_volume != -1) {
- int vol = state->unmute_volume;
-
- state->unmute_volume = -1;
- set_volume(client, vol);
- }
-}
-
int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
return retval;
}
-int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int cx25840_audio_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct cx25840_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = get_volume(client);
+ if (state->mute->val)
+ set_volume(client, 0);
+ else
+ set_volume(client, state->volume->val);
break;
case V4L2_CID_AUDIO_BASS:
- ctrl->value = get_bass(client);
+ /* PATH1_EQ_BASS_VOL */
+ cx25840_and_or(client, 0x8d9, ~0x3f,
+ 48 - (ctrl->val * 48 / 0xffff));
break;
case V4L2_CID_AUDIO_TREBLE:
- ctrl->value = get_treble(client);
+ /* PATH1_EQ_TREBLE_VOL */
+ cx25840_and_or(client, 0x8db, ~0x3f,
+ 48 - (ctrl->val * 48 / 0xffff));
break;
case V4L2_CID_AUDIO_BALANCE:
- ctrl->value = get_balance(client);
- break;
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = get_mute(client);
+ set_balance(client, ctrl->val);
break;
default:
return -EINVAL;
return 0;
}
-int cx25840_audio_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_VOLUME:
- set_volume(client, ctrl->value);
- break;
- case V4L2_CID_AUDIO_BASS:
- set_bass(client, ctrl->value);
- break;
- case V4L2_CID_AUDIO_TREBLE:
- set_treble(client, ctrl->value);
- break;
- case V4L2_CID_AUDIO_BALANCE:
- set_balance(client, ctrl->value);
- break;
- case V4L2_CID_AUDIO_MUTE:
- set_mute(client, ctrl->value);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
+const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops = {
+ .s_ctrl = cx25840_audio_s_ctrl,
+};
*
* CX23885 support by Steven Toth <stoth@linuxtv.org>.
*
+ * CX2388[578] IRQ handling, IO Pin mux configuration and other small fixes are
+ * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
+ *
* 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
MODULE_AUTHOR("Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford");
MODULE_LICENSE("GPL");
+#define CX25840_VID_INT_STAT_REG 0x410
+#define CX25840_VID_INT_STAT_BITS 0x0000ffff
+#define CX25840_VID_INT_MASK_BITS 0xffff0000
+#define CX25840_VID_INT_MASK_SHFT 16
+#define CX25840_VID_INT_MASK_REG 0x412
+
+#define CX23885_AUD_MC_INT_MASK_REG 0x80c
+#define CX23885_AUD_MC_INT_STAT_BITS 0xffff0000
+#define CX23885_AUD_MC_INT_CTRL_BITS 0x0000ffff
+#define CX23885_AUD_MC_INT_STAT_SHFT 16
+
+#define CX25840_AUD_INT_CTRL_REG 0x812
+#define CX25840_AUD_INT_STAT_REG 0x813
+
+#define CX23885_PIN_CTRL_IRQ_REG 0x123
+#define CX23885_PIN_CTRL_IRQ_IR_STAT 0x40
+#define CX23885_PIN_CTRL_IRQ_AUD_STAT 0x20
+#define CX23885_PIN_CTRL_IRQ_VID_STAT 0x10
+
+#define CX25840_IR_STATS_REG 0x210
+#define CX25840_IR_IRQEN_REG 0x214
+
static int cx25840_debug;
module_param_named(debug,cx25840_debug, int, 0644);
u8 cx25840_read(struct i2c_client * client, u16 addr)
{
- u8 buffer[2];
- buffer[0] = addr >> 8;
- buffer[1] = addr & 0xff;
-
- if (i2c_master_send(client, buffer, 2) < 2)
- return 0;
-
- if (i2c_master_recv(client, buffer, 1) < 1)
+ struct i2c_msg msgs[2];
+ u8 tx_buf[2], rx_buf[1];
+
+ /* Write register address */
+ tx_buf[0] = addr >> 8;
+ tx_buf[1] = addr & 0xff;
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 2;
+ msgs[0].buf = (char *) tx_buf;
+
+ /* Read data from register */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = 1;
+ msgs[1].buf = (char *) rx_buf;
+
+ if (i2c_transfer(client->adapter, msgs, 2) < 2)
return 0;
- return buffer[0];
+ return rx_buf[0];
}
u32 cx25840_read4(struct i2c_client * client, u16 addr)
{
- u8 buffer[4];
- buffer[0] = addr >> 8;
- buffer[1] = addr & 0xff;
-
- if (i2c_master_send(client, buffer, 2) < 2)
- return 0;
-
- if (i2c_master_recv(client, buffer, 4) < 4)
+ struct i2c_msg msgs[2];
+ u8 tx_buf[2], rx_buf[4];
+
+ /* Write register address */
+ tx_buf[0] = addr >> 8;
+ tx_buf[1] = addr & 0xff;
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 2;
+ msgs[0].buf = (char *) tx_buf;
+
+ /* Read data from registers */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = 4;
+ msgs[1].buf = (char *) rx_buf;
+
+ if (i2c_transfer(client->adapter, msgs, 2) < 2)
return 0;
- return (buffer[3] << 24) | (buffer[2] << 16) |
- (buffer[1] << 8) | buffer[0];
+ return (rx_buf[3] << 24) | (rx_buf[2] << 16) | (rx_buf[1] << 8) |
+ rx_buf[0];
}
int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask,
or_value);
}
+int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask,
+ u32 or_value)
+{
+ return cx25840_write4(client, addr,
+ (cx25840_read4(client, addr) & and_mask) |
+ or_value);
+}
+
/* ----------------------------------------------------------------------- */
static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input,
/* ----------------------------------------------------------------------- */
+static int cx23885_s_io_pin_config(struct v4l2_subdev *sd, size_t n,
+ struct v4l2_subdev_io_pin_config *p)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int i;
+ u32 pin_ctrl;
+ u8 gpio_oe, gpio_data, strength;
+
+ pin_ctrl = cx25840_read4(client, 0x120);
+ gpio_oe = cx25840_read(client, 0x160);
+ gpio_data = cx25840_read(client, 0x164);
+
+ for (i = 0; i < n; i++) {
+ strength = p[i].strength;
+ if (strength > CX25840_PIN_DRIVE_FAST)
+ strength = CX25840_PIN_DRIVE_FAST;
+
+ switch (p[i].pin) {
+ case CX23885_PIN_IRQ_N_GPIO16:
+ if (p[i].function != CX23885_PAD_IRQ_N) {
+ /* GPIO16 */
+ pin_ctrl &= ~(0x1 << 25);
+ } else {
+ /* IRQ_N */
+ if (p[i].flags &
+ (V4L2_SUBDEV_IO_PIN_DISABLE |
+ V4L2_SUBDEV_IO_PIN_INPUT)) {
+ pin_ctrl &= ~(0x1 << 25);
+ } else {
+ pin_ctrl |= (0x1 << 25);
+ }
+ if (p[i].flags &
+ V4L2_SUBDEV_IO_PIN_ACTIVE_LOW) {
+ pin_ctrl &= ~(0x1 << 24);
+ } else {
+ pin_ctrl |= (0x1 << 24);
+ }
+ }
+ break;
+ case CX23885_PIN_IR_RX_GPIO19:
+ if (p[i].function != CX23885_PAD_GPIO19) {
+ /* IR_RX */
+ gpio_oe |= (0x1 << 0);
+ pin_ctrl &= ~(0x3 << 18);
+ pin_ctrl |= (strength << 18);
+ } else {
+ /* GPIO19 */
+ gpio_oe &= ~(0x1 << 0);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+ gpio_data &= ~(0x1 << 0);
+ gpio_data |= ((p[i].value & 0x1) << 0);
+ }
+ pin_ctrl &= ~(0x3 << 12);
+ pin_ctrl |= (strength << 12);
+ }
+ break;
+ case CX23885_PIN_IR_TX_GPIO20:
+ if (p[i].function != CX23885_PAD_GPIO20) {
+ /* IR_TX */
+ gpio_oe |= (0x1 << 1);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_DISABLE)
+ pin_ctrl &= ~(0x1 << 10);
+ else
+ pin_ctrl |= (0x1 << 10);
+ pin_ctrl &= ~(0x3 << 18);
+ pin_ctrl |= (strength << 18);
+ } else {
+ /* GPIO20 */
+ gpio_oe &= ~(0x1 << 1);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+ gpio_data &= ~(0x1 << 1);
+ gpio_data |= ((p[i].value & 0x1) << 1);
+ }
+ pin_ctrl &= ~(0x3 << 12);
+ pin_ctrl |= (strength << 12);
+ }
+ break;
+ case CX23885_PIN_I2S_SDAT_GPIO21:
+ if (p[i].function != CX23885_PAD_GPIO21) {
+ /* I2S_SDAT */
+ /* TODO: Input or Output config */
+ gpio_oe |= (0x1 << 2);
+ pin_ctrl &= ~(0x3 << 22);
+ pin_ctrl |= (strength << 22);
+ } else {
+ /* GPIO21 */
+ gpio_oe &= ~(0x1 << 2);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+ gpio_data &= ~(0x1 << 2);
+ gpio_data |= ((p[i].value & 0x1) << 2);
+ }
+ pin_ctrl &= ~(0x3 << 12);
+ pin_ctrl |= (strength << 12);
+ }
+ break;
+ case CX23885_PIN_I2S_WCLK_GPIO22:
+ if (p[i].function != CX23885_PAD_GPIO22) {
+ /* I2S_WCLK */
+ /* TODO: Input or Output config */
+ gpio_oe |= (0x1 << 3);
+ pin_ctrl &= ~(0x3 << 22);
+ pin_ctrl |= (strength << 22);
+ } else {
+ /* GPIO22 */
+ gpio_oe &= ~(0x1 << 3);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+ gpio_data &= ~(0x1 << 3);
+ gpio_data |= ((p[i].value & 0x1) << 3);
+ }
+ pin_ctrl &= ~(0x3 << 12);
+ pin_ctrl |= (strength << 12);
+ }
+ break;
+ case CX23885_PIN_I2S_BCLK_GPIO23:
+ if (p[i].function != CX23885_PAD_GPIO23) {
+ /* I2S_BCLK */
+ /* TODO: Input or Output config */
+ gpio_oe |= (0x1 << 4);
+ pin_ctrl &= ~(0x3 << 22);
+ pin_ctrl |= (strength << 22);
+ } else {
+ /* GPIO23 */
+ gpio_oe &= ~(0x1 << 4);
+ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) {
+ gpio_data &= ~(0x1 << 4);
+ gpio_data |= ((p[i].value & 0x1) << 4);
+ }
+ pin_ctrl &= ~(0x3 << 12);
+ pin_ctrl |= (strength << 12);
+ }
+ break;
+ }
+ }
+
+ cx25840_write(client, 0x164, gpio_data);
+ cx25840_write(client, 0x160, gpio_oe);
+ cx25840_write4(client, 0x120, pin_ctrl);
+ return 0;
+}
+
+static int common_s_io_pin_config(struct v4l2_subdev *sd, size_t n,
+ struct v4l2_subdev_io_pin_config *pincfg)
+{
+ struct cx25840_state *state = to_state(sd);
+
+ if (is_cx2388x(state))
+ return cx23885_s_io_pin_config(sd, n, pincfg);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
static void init_dll1(struct i2c_client *client)
{
/* This is the Hauppauge sequence used to
/* start microcontroller */
cx25840_and_or(client, 0x803, ~0x10, 0x10);
+
+ /* Disable and clear video interrupts - we don't use them */
+ cx25840_write4(client, CX25840_VID_INT_STAT_REG, 0xffffffff);
+
+ /* Disable and clear audio interrupts - we don't use them */
+ cx25840_write(client, CX25840_AUD_INT_CTRL_REG, 0xff);
+ cx25840_write(client, CX25840_AUD_INT_STAT_REG, 0xff);
}
/* ----------------------------------------------------------------------- */
/* ----------------------------------------------------------------------- */
-static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int cx25840_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct cx25840_state *state = to_state(sd);
+ struct v4l2_subdev *sd = to_sd(ctrl);
struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
- case CX25840_CID_ENABLE_PVR150_WORKAROUND:
- state->pvr150_workaround = ctrl->value;
- set_input(client, state->vid_input, state->aud_input);
- break;
-
case V4L2_CID_BRIGHTNESS:
- if (ctrl->value < 0 || ctrl->value > 255) {
- v4l_err(client, "invalid brightness setting %d\n",
- ctrl->value);
- return -ERANGE;
- }
-
- cx25840_write(client, 0x414, ctrl->value - 128);
+ cx25840_write(client, 0x414, ctrl->val - 128);
break;
case V4L2_CID_CONTRAST:
- if (ctrl->value < 0 || ctrl->value > 127) {
- v4l_err(client, "invalid contrast setting %d\n",
- ctrl->value);
- return -ERANGE;
- }
-
- cx25840_write(client, 0x415, ctrl->value << 1);
+ cx25840_write(client, 0x415, ctrl->val << 1);
break;
case V4L2_CID_SATURATION:
- if (ctrl->value < 0 || ctrl->value > 127) {
- v4l_err(client, "invalid saturation setting %d\n",
- ctrl->value);
- return -ERANGE;
- }
-
- cx25840_write(client, 0x420, ctrl->value << 1);
- cx25840_write(client, 0x421, ctrl->value << 1);
+ cx25840_write(client, 0x420, ctrl->val << 1);
+ cx25840_write(client, 0x421, ctrl->val << 1);
break;
case V4L2_CID_HUE:
- if (ctrl->value < -128 || ctrl->value > 127) {
- v4l_err(client, "invalid hue setting %d\n", ctrl->value);
- return -ERANGE;
- }
-
- cx25840_write(client, 0x422, ctrl->value);
+ cx25840_write(client, 0x422, ctrl->val);
break;
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_MUTE:
- if (is_cx2583x(state))
- return -EINVAL;
- return cx25840_audio_s_ctrl(sd, ctrl);
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct cx25840_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- switch (ctrl->id) {
- case CX25840_CID_ENABLE_PVR150_WORKAROUND:
- ctrl->value = state->pvr150_workaround;
- break;
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = (s8)cx25840_read(client, 0x414) + 128;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = cx25840_read(client, 0x415) >> 1;
- break;
- case V4L2_CID_SATURATION:
- ctrl->value = cx25840_read(client, 0x420) >> 1;
- break;
- case V4L2_CID_HUE:
- ctrl->value = (s8)cx25840_read(client, 0x422);
- break;
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_MUTE:
- if (is_cx2583x(state))
- return -EINVAL;
- return cx25840_audio_g_ctrl(sd, ctrl);
default:
return -EINVAL;
}
default: p = "not defined";
}
v4l_info(client, "Detected audio standard: %s\n", p);
- v4l_info(client, "Audio muted: %s\n",
- (state->unmute_volume >= 0) ? "yes" : "no");
v4l_info(client, "Audio microcontroller: %s\n",
(download_ctl & 0x10) ?
((mute_ctl & 0x2) ? "detecting" : "running") : "stopped");
return 0;
}
-static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- struct cx25840_state *state = to_state(sd);
-
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
- default:
- break;
- }
- if (is_cx2583x(state))
- return -EINVAL;
-
- switch (qc->id) {
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 65535,
- 65535 / 100, state->default_volume);
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
- default:
- return -EINVAL;
- }
- return -EINVAL;
-}
-
static int cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct cx25840_state *state = to_state(sd);
log_video_status(client);
if (!is_cx2583x(state))
log_audio_status(client);
+ cx25840_ir_log_status(sd);
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
+ return 0;
+}
+
+static int cx25840_s_config(struct v4l2_subdev *sd, int irq, void *platform_data)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (platform_data) {
+ struct cx25840_platform_data *pdata = platform_data;
+
+ state->pvr150_workaround = pdata->pvr150_workaround;
+ set_input(client, state->vid_input, state->aud_input);
+ }
return 0;
}
+static int cx23885_irq_handler(struct v4l2_subdev *sd, u32 status,
+ bool *handled)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ u8 irq_stat, aud_stat, aud_en, ir_stat, ir_en;
+ u32 vid_stat, aud_mc_stat;
+ bool block_handled;
+ int ret = 0;
+
+ irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG);
+ v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (entry): %s %s %s\n",
+ irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ",
+ irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ",
+ irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " ");
+
+ if ((is_cx23885(state) || is_cx23887(state))) {
+ ir_stat = cx25840_read(c, CX25840_IR_STATS_REG);
+ ir_en = cx25840_read(c, CX25840_IR_IRQEN_REG);
+ v4l_dbg(2, cx25840_debug, c,
+ "AV Core ir IRQ status: %#04x disables: %#04x\n",
+ ir_stat, ir_en);
+ if (irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT) {
+ block_handled = false;
+ ret = cx25840_ir_irq_handler(sd,
+ status, &block_handled);
+ if (block_handled)
+ *handled = true;
+ }
+ }
+
+ aud_stat = cx25840_read(c, CX25840_AUD_INT_STAT_REG);
+ aud_en = cx25840_read(c, CX25840_AUD_INT_CTRL_REG);
+ v4l_dbg(2, cx25840_debug, c,
+ "AV Core audio IRQ status: %#04x disables: %#04x\n",
+ aud_stat, aud_en);
+ aud_mc_stat = cx25840_read4(c, CX23885_AUD_MC_INT_MASK_REG);
+ v4l_dbg(2, cx25840_debug, c,
+ "AV Core audio MC IRQ status: %#06x enables: %#06x\n",
+ aud_mc_stat >> CX23885_AUD_MC_INT_STAT_SHFT,
+ aud_mc_stat & CX23885_AUD_MC_INT_CTRL_BITS);
+ if (irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT) {
+ if (aud_stat) {
+ cx25840_write(c, CX25840_AUD_INT_STAT_REG, aud_stat);
+ *handled = true;
+ }
+ }
+
+ vid_stat = cx25840_read4(c, CX25840_VID_INT_STAT_REG);
+ v4l_dbg(2, cx25840_debug, c,
+ "AV Core video IRQ status: %#06x disables: %#06x\n",
+ vid_stat & CX25840_VID_INT_STAT_BITS,
+ vid_stat >> CX25840_VID_INT_MASK_SHFT);
+ if (irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT) {
+ if (vid_stat & CX25840_VID_INT_STAT_BITS) {
+ cx25840_write4(c, CX25840_VID_INT_STAT_REG, vid_stat);
+ *handled = true;
+ }
+ }
+
+ irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG);
+ v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (exit): %s %s %s\n",
+ irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ",
+ irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ",
+ irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " ");
+
+ return ret;
+}
+
+static int cx25840_irq_handler(struct v4l2_subdev *sd, u32 status,
+ bool *handled)
+{
+ struct cx25840_state *state = to_state(sd);
+
+ *handled = false;
+
+ /* Only support the CX2388[578] AV Core for now */
+ if (is_cx2388x(state))
+ return cx23885_irq_handler(sd, status, handled);
+
+ return -ENODEV;
+}
+
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops cx25840_ctrl_ops = {
+ .s_ctrl = cx25840_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops cx25840_core_ops = {
.log_status = cx25840_log_status,
+ .s_config = cx25840_s_config,
.g_chip_ident = cx25840_g_chip_ident,
- .g_ctrl = cx25840_g_ctrl,
- .s_ctrl = cx25840_s_ctrl,
- .queryctrl = cx25840_queryctrl,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
.s_std = cx25840_s_std,
.reset = cx25840_reset,
.load_fw = cx25840_load_fw,
+ .s_io_pin_config = common_s_io_pin_config,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = cx25840_g_register,
.s_register = cx25840_s_register,
#endif
+ .interrupt_service_routine = cx25840_irq_handler,
};
static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = {
.audio = &cx25840_audio_ops,
.video = &cx25840_video_ops,
.vbi = &cx25840_vbi_ops,
+ .ir = &cx25840_ir_ops,
};
/* ----------------------------------------------------------------------- */
{
struct cx25840_state *state;
struct v4l2_subdev *sd;
+ int default_volume;
u32 id = V4L2_IDENT_NONE;
u16 device_id;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &cx25840_ops);
+
switch (id) {
case V4L2_IDENT_CX23885_AV:
v4l_info(client, "cx23885 A/V decoder found @ 0x%x (%s)\n",
state->audclk_freq = 48000;
state->pvr150_workaround = 0;
state->audmode = V4L2_TUNER_MODE_LANG1;
- state->unmute_volume = -1;
- state->default_volume = 228 - cx25840_read(client, 0x8d4);
- state->default_volume = ((state->default_volume / 2) + 23) << 9;
state->vbi_line_offset = 8;
state->id = id;
state->rev = device_id;
+ v4l2_ctrl_handler_init(&state->hdl, 9);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ if (!is_cx2583x(state)) {
+ default_volume = 228 - cx25840_read(client, 0x8d4);
+ default_volume = ((default_volume / 2) + 23) << 9;
+
+ state->volume = v4l2_ctrl_new_std(&state->hdl,
+ &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME,
+ 0, 65335, 65535 / 100, default_volume);
+ state->mute = v4l2_ctrl_new_std(&state->hdl,
+ &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE,
+ 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
+ V4L2_CID_AUDIO_BALANCE,
+ 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
+ V4L2_CID_AUDIO_BASS,
+ 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops,
+ V4L2_CID_AUDIO_TREBLE,
+ 0, 65535, 65535 / 100, 32768);
+ }
+ sd->ctrl_handler = &state->hdl;
+ if (state->hdl.error) {
+ int err = state->hdl.error;
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return err;
+ }
+ v4l2_ctrl_cluster(2, &state->volume);
+ v4l2_ctrl_handler_setup(&state->hdl);
+ cx25840_ir_probe(sd);
return 0;
}
static int cx25840_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(sd);
+ cx25840_ir_remove(sd);
v4l2_device_unregister_subdev(sd);
- kfree(to_state(sd));
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
return 0;
}
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
#include <linux/i2c.h>
-/* ENABLE_PVR150_WORKAROUND activates a workaround for a hardware bug that is
- present in Hauppauge PVR-150 (and possibly PVR-500) cards that have
- certain NTSC tuners (tveeprom tuner model numbers 85, 99 and 112). The
- audio autodetect fails on some channels for these models and the workaround
- is to select the audio standard explicitly. Many thanks to Hauppauge for
- providing this information. */
-#define CX25840_CID_ENABLE_PVR150_WORKAROUND (V4L2_CID_PRIVATE_BASE+0)
+struct cx25840_ir_state;
struct cx25840_state {
struct i2c_client *c;
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* volume cluster */
+ struct v4l2_ctrl *volume;
+ struct v4l2_ctrl *mute;
+ };
int pvr150_workaround;
int radio;
v4l2_std_id std;
enum cx25840_audio_input aud_input;
u32 audclk_freq;
int audmode;
- int unmute_volume; /* -1 if not muted */
- int default_volume;
int vbi_line_offset;
u32 id;
u32 rev;
int is_initialized;
wait_queue_head_t fw_wait; /* wake up when the fw load is finished */
struct work_struct fw_work; /* work entry for fw load */
+ struct cx25840_ir_state *ir_state;
};
static inline struct cx25840_state *to_state(struct v4l2_subdev *sd)
return container_of(sd, struct cx25840_state, sd);
}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct cx25840_state, hdl)->sd;
+}
+
static inline bool is_cx2583x(struct cx25840_state *state)
{
return state->id == V4L2_IDENT_CX25836 ||
state->id == V4L2_IDENT_CX23888_AV;
}
+static inline bool is_cx23885(struct cx25840_state *state)
+{
+ return state->id == V4L2_IDENT_CX23885_AV;
+}
+
+static inline bool is_cx23887(struct cx25840_state *state)
+{
+ return state->id == V4L2_IDENT_CX23887_AV;
+}
+
+static inline bool is_cx23888(struct cx25840_state *state)
+{
+ return state->id == V4L2_IDENT_CX23888_AV;
+}
+
/* ----------------------------------------------------------------------- */
/* cx25850-core.c */
int cx25840_write(struct i2c_client *client, u16 addr, u8 value);
u8 cx25840_read(struct i2c_client *client, u16 addr);
u32 cx25840_read4(struct i2c_client *client, u16 addr);
int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned mask, u8 value);
+int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask,
+ u32 or_value);
void cx25840_std_setup(struct i2c_client *client);
/* ----------------------------------------------------------------------- */
/* cx25850-audio.c */
void cx25840_audio_set_path(struct i2c_client *client);
int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq);
-int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
-int cx25840_audio_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
+
+extern const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops;
/* ----------------------------------------------------------------------- */
/* cx25850-vbi.c */
int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt);
int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi);
+/* ----------------------------------------------------------------------- */
+/* cx25850-ir.c */
+extern const struct v4l2_subdev_ir_ops cx25840_ir_ops;
+int cx25840_ir_log_status(struct v4l2_subdev *sd);
+int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled);
+int cx25840_ir_probe(struct v4l2_subdev *sd);
+int cx25840_ir_remove(struct v4l2_subdev *sd);
+
#endif
--- /dev/null
+/*
+ * Driver for the Conexant CX2584x Audio/Video decoder chip and related cores
+ *
+ * Integrated Consumer Infrared Controller
+ *
+ * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
+ *
+ * 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/slab.h>
+#include <linux/kfifo.h>
+#include <media/cx25840.h>
+#include <media/ir-core.h>
+
+#include "cx25840-core.h"
+
+static unsigned int ir_debug;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug, "enable integrated IR debug messages");
+
+#define CX25840_IR_REG_BASE 0x200
+
+#define CX25840_IR_CNTRL_REG 0x200
+#define CNTRL_WIN_3_3 0x00000000
+#define CNTRL_WIN_4_3 0x00000001
+#define CNTRL_WIN_3_4 0x00000002
+#define CNTRL_WIN_4_4 0x00000003
+#define CNTRL_WIN 0x00000003
+#define CNTRL_EDG_NONE 0x00000000
+#define CNTRL_EDG_FALL 0x00000004
+#define CNTRL_EDG_RISE 0x00000008
+#define CNTRL_EDG_BOTH 0x0000000C
+#define CNTRL_EDG 0x0000000C
+#define CNTRL_DMD 0x00000010
+#define CNTRL_MOD 0x00000020
+#define CNTRL_RFE 0x00000040
+#define CNTRL_TFE 0x00000080
+#define CNTRL_RXE 0x00000100
+#define CNTRL_TXE 0x00000200
+#define CNTRL_RIC 0x00000400
+#define CNTRL_TIC 0x00000800
+#define CNTRL_CPL 0x00001000
+#define CNTRL_LBM 0x00002000
+#define CNTRL_R 0x00004000
+
+#define CX25840_IR_TXCLK_REG 0x204
+#define TXCLK_TCD 0x0000FFFF
+
+#define CX25840_IR_RXCLK_REG 0x208
+#define RXCLK_RCD 0x0000FFFF
+
+#define CX25840_IR_CDUTY_REG 0x20C
+#define CDUTY_CDC 0x0000000F
+
+#define CX25840_IR_STATS_REG 0x210
+#define STATS_RTO 0x00000001
+#define STATS_ROR 0x00000002
+#define STATS_RBY 0x00000004
+#define STATS_TBY 0x00000008
+#define STATS_RSR 0x00000010
+#define STATS_TSR 0x00000020
+
+#define CX25840_IR_IRQEN_REG 0x214
+#define IRQEN_RTE 0x00000001
+#define IRQEN_ROE 0x00000002
+#define IRQEN_RSE 0x00000010
+#define IRQEN_TSE 0x00000020
+#define IRQEN_MSK 0x00000033
+
+#define CX25840_IR_FILTR_REG 0x218
+#define FILTR_LPF 0x0000FFFF
+
+#define CX25840_IR_FIFO_REG 0x23C
+#define FIFO_RXTX 0x0000FFFF
+#define FIFO_RXTX_LVL 0x00010000
+#define FIFO_RXTX_RTO 0x0001FFFF
+#define FIFO_RX_NDV 0x00020000
+#define FIFO_RX_DEPTH 8
+#define FIFO_TX_DEPTH 8
+
+#define CX25840_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */
+#define CX25840_IR_REFCLK_FREQ (CX25840_VIDCLK_FREQ / 2)
+
+/*
+ * We use this union internally for convenience, but callers to tx_write
+ * and rx_read will be expecting records of type struct ir_raw_event.
+ * Always ensure the size of this union is dictated by struct ir_raw_event.
+ */
+union cx25840_ir_fifo_rec {
+ u32 hw_fifo_data;
+ struct ir_raw_event ir_core_data;
+};
+
+#define CX25840_IR_RX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec))
+#define CX25840_IR_TX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec))
+
+struct cx25840_ir_state {
+ struct i2c_client *c;
+
+ struct v4l2_subdev_ir_parameters rx_params;
+ struct mutex rx_params_lock; /* protects Rx parameter settings cache */
+ atomic_t rxclk_divider;
+ atomic_t rx_invert;
+
+ struct kfifo rx_kfifo;
+ spinlock_t rx_kfifo_lock; /* protect Rx data kfifo */
+
+ struct v4l2_subdev_ir_parameters tx_params;
+ struct mutex tx_params_lock; /* protects Tx parameter settings cache */
+ atomic_t txclk_divider;
+};
+
+static inline struct cx25840_ir_state *to_ir_state(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ return state ? state->ir_state : NULL;
+}
+
+
+/*
+ * Rx and Tx Clock Divider register computations
+ *
+ * Note the largest clock divider value of 0xffff corresponds to:
+ * (0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns
+ * which fits in 21 bits, so we'll use unsigned int for time arguments.
+ */
+static inline u16 count_to_clock_divider(unsigned int d)
+{
+ if (d > RXCLK_RCD + 1)
+ d = RXCLK_RCD;
+ else if (d < 2)
+ d = 1;
+ else
+ d--;
+ return (u16) d;
+}
+
+static inline u16 ns_to_clock_divider(unsigned int ns)
+{
+ return count_to_clock_divider(
+ DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000));
+}
+
+static inline unsigned int clock_divider_to_ns(unsigned int divider)
+{
+ /* Period of the Rx or Tx clock in ns */
+ return DIV_ROUND_CLOSEST((divider + 1) * 1000,
+ CX25840_IR_REFCLK_FREQ / 1000000);
+}
+
+static inline u16 carrier_freq_to_clock_divider(unsigned int freq)
+{
+ return count_to_clock_divider(
+ DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * 16));
+}
+
+static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider)
+{
+ return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, (divider + 1) * 16);
+}
+
+static inline u16 freq_to_clock_divider(unsigned int freq,
+ unsigned int rollovers)
+{
+ return count_to_clock_divider(
+ DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * rollovers));
+}
+
+static inline unsigned int clock_divider_to_freq(unsigned int divider,
+ unsigned int rollovers)
+{
+ return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ,
+ (divider + 1) * rollovers);
+}
+
+/*
+ * Low Pass Filter register calculations
+ *
+ * Note the largest count value of 0xffff corresponds to:
+ * 0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns
+ * which fits in 21 bits, so we'll use unsigned int for time arguments.
+ */
+static inline u16 count_to_lpf_count(unsigned int d)
+{
+ if (d > FILTR_LPF)
+ d = FILTR_LPF;
+ else if (d < 4)
+ d = 0;
+ return (u16) d;
+}
+
+static inline u16 ns_to_lpf_count(unsigned int ns)
+{
+ return count_to_lpf_count(
+ DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000));
+}
+
+static inline unsigned int lpf_count_to_ns(unsigned int count)
+{
+ /* Duration of the Low Pass Filter rejection window in ns */
+ return DIV_ROUND_CLOSEST(count * 1000,
+ CX25840_IR_REFCLK_FREQ / 1000000);
+}
+
+static inline unsigned int lpf_count_to_us(unsigned int count)
+{
+ /* Duration of the Low Pass Filter rejection window in us */
+ return DIV_ROUND_CLOSEST(count, CX25840_IR_REFCLK_FREQ / 1000000);
+}
+
+/*
+ * FIFO register pulse width count compuations
+ */
+static u32 clock_divider_to_resolution(u16 divider)
+{
+ /*
+ * Resolution is the duration of 1 tick of the readable portion of
+ * of the pulse width counter as read from the FIFO. The two lsb's are
+ * not readable, hence the << 2. This function returns ns.
+ */
+ return DIV_ROUND_CLOSEST((1 << 2) * ((u32) divider + 1) * 1000,
+ CX25840_IR_REFCLK_FREQ / 1000000);
+}
+
+static u64 pulse_width_count_to_ns(u16 count, u16 divider)
+{
+ u64 n;
+ u32 rem;
+
+ /*
+ * The 2 lsb's of the pulse width timer count are not readable, hence
+ * the (count << 2) | 0x3
+ */
+ n = (((u64) count << 2) | 0x3) * (divider + 1) * 1000; /* millicycles */
+ rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => ns */
+ if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2)
+ n++;
+ return n;
+}
+
+#if 0
+/* Keep as we will need this for Transmit functionality */
+static u16 ns_to_pulse_width_count(u32 ns, u16 divider)
+{
+ u64 n;
+ u32 d;
+ u32 rem;
+
+ /*
+ * The 2 lsb's of the pulse width timer count are not accessable, hence
+ * the (1 << 2)
+ */
+ n = ((u64) ns) * CX25840_IR_REFCLK_FREQ / 1000000; /* millicycles */
+ d = (1 << 2) * ((u32) divider + 1) * 1000; /* millicycles/count */
+ rem = do_div(n, d);
+ if (rem >= d / 2)
+ n++;
+
+ if (n > FIFO_RXTX)
+ n = FIFO_RXTX;
+ else if (n == 0)
+ n = 1;
+ return (u16) n;
+}
+
+#endif
+static unsigned int pulse_width_count_to_us(u16 count, u16 divider)
+{
+ u64 n;
+ u32 rem;
+
+ /*
+ * The 2 lsb's of the pulse width timer count are not readable, hence
+ * the (count << 2) | 0x3
+ */
+ n = (((u64) count << 2) | 0x3) * (divider + 1); /* cycles */
+ rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => us */
+ if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2)
+ n++;
+ return (unsigned int) n;
+}
+
+/*
+ * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts
+ *
+ * The total pulse clock count is an 18 bit pulse width timer count as the most
+ * significant part and (up to) 16 bit clock divider count as a modulus.
+ * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse
+ * width timer count's least significant bit.
+ */
+static u64 ns_to_pulse_clocks(u32 ns)
+{
+ u64 clocks;
+ u32 rem;
+ clocks = CX25840_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles */
+ rem = do_div(clocks, 1000); /* /1000 = cycles */
+ if (rem >= 1000 / 2)
+ clocks++;
+ return clocks;
+}
+
+static u16 pulse_clocks_to_clock_divider(u64 count)
+{
+ u32 rem;
+
+ rem = do_div(count, (FIFO_RXTX << 2) | 0x3);
+
+ /* net result needs to be rounded down and decremented by 1 */
+ if (count > RXCLK_RCD + 1)
+ count = RXCLK_RCD;
+ else if (count < 2)
+ count = 1;
+ else
+ count--;
+ return (u16) count;
+}
+
+/*
+ * IR Control Register helpers
+ */
+enum tx_fifo_watermark {
+ TX_FIFO_HALF_EMPTY = 0,
+ TX_FIFO_EMPTY = CNTRL_TIC,
+};
+
+enum rx_fifo_watermark {
+ RX_FIFO_HALF_FULL = 0,
+ RX_FIFO_NOT_EMPTY = CNTRL_RIC,
+};
+
+static inline void control_tx_irq_watermark(struct i2c_client *c,
+ enum tx_fifo_watermark level)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_TIC, level);
+}
+
+static inline void control_rx_irq_watermark(struct i2c_client *c,
+ enum rx_fifo_watermark level)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_RIC, level);
+}
+
+static inline void control_tx_enable(struct i2c_client *c, bool enable)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_TXE | CNTRL_TFE),
+ enable ? (CNTRL_TXE | CNTRL_TFE) : 0);
+}
+
+static inline void control_rx_enable(struct i2c_client *c, bool enable)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_RXE | CNTRL_RFE),
+ enable ? (CNTRL_RXE | CNTRL_RFE) : 0);
+}
+
+static inline void control_tx_modulation_enable(struct i2c_client *c,
+ bool enable)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_MOD,
+ enable ? CNTRL_MOD : 0);
+}
+
+static inline void control_rx_demodulation_enable(struct i2c_client *c,
+ bool enable)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_DMD,
+ enable ? CNTRL_DMD : 0);
+}
+
+static inline void control_rx_s_edge_detection(struct i2c_client *c,
+ u32 edge_types)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_EDG_BOTH,
+ edge_types & CNTRL_EDG_BOTH);
+}
+
+static void control_rx_s_carrier_window(struct i2c_client *c,
+ unsigned int carrier,
+ unsigned int *carrier_range_low,
+ unsigned int *carrier_range_high)
+{
+ u32 v;
+ unsigned int c16 = carrier * 16;
+
+ if (*carrier_range_low < DIV_ROUND_CLOSEST(c16, 16 + 3)) {
+ v = CNTRL_WIN_3_4;
+ *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 4);
+ } else {
+ v = CNTRL_WIN_3_3;
+ *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 3);
+ }
+
+ if (*carrier_range_high > DIV_ROUND_CLOSEST(c16, 16 - 3)) {
+ v |= CNTRL_WIN_4_3;
+ *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 4);
+ } else {
+ v |= CNTRL_WIN_3_3;
+ *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 3);
+ }
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_WIN, v);
+}
+
+static inline void control_tx_polarity_invert(struct i2c_client *c,
+ bool invert)
+{
+ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_CPL,
+ invert ? CNTRL_CPL : 0);
+}
+
+/*
+ * IR Rx & Tx Clock Register helpers
+ */
+static unsigned int txclk_tx_s_carrier(struct i2c_client *c,
+ unsigned int freq,
+ u16 *divider)
+{
+ *divider = carrier_freq_to_clock_divider(freq);
+ cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider);
+ return clock_divider_to_carrier_freq(*divider);
+}
+
+static unsigned int rxclk_rx_s_carrier(struct i2c_client *c,
+ unsigned int freq,
+ u16 *divider)
+{
+ *divider = carrier_freq_to_clock_divider(freq);
+ cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider);
+ return clock_divider_to_carrier_freq(*divider);
+}
+
+static u32 txclk_tx_s_max_pulse_width(struct i2c_client *c, u32 ns,
+ u16 *divider)
+{
+ u64 pulse_clocks;
+
+ if (ns > IR_MAX_DURATION)
+ ns = IR_MAX_DURATION;
+ pulse_clocks = ns_to_pulse_clocks(ns);
+ *divider = pulse_clocks_to_clock_divider(pulse_clocks);
+ cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider);
+ return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider);
+}
+
+static u32 rxclk_rx_s_max_pulse_width(struct i2c_client *c, u32 ns,
+ u16 *divider)
+{
+ u64 pulse_clocks;
+
+ if (ns > IR_MAX_DURATION)
+ ns = IR_MAX_DURATION;
+ pulse_clocks = ns_to_pulse_clocks(ns);
+ *divider = pulse_clocks_to_clock_divider(pulse_clocks);
+ cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider);
+ return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider);
+}
+
+/*
+ * IR Tx Carrier Duty Cycle register helpers
+ */
+static unsigned int cduty_tx_s_duty_cycle(struct i2c_client *c,
+ unsigned int duty_cycle)
+{
+ u32 n;
+ n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */
+ if (n != 0)
+ n--;
+ if (n > 15)
+ n = 15;
+ cx25840_write4(c, CX25840_IR_CDUTY_REG, n);
+ return DIV_ROUND_CLOSEST((n + 1) * 100, 16);
+}
+
+/*
+ * IR Filter Register helpers
+ */
+static u32 filter_rx_s_min_width(struct i2c_client *c, u32 min_width_ns)
+{
+ u32 count = ns_to_lpf_count(min_width_ns);
+ cx25840_write4(c, CX25840_IR_FILTR_REG, count);
+ return lpf_count_to_ns(count);
+}
+
+/*
+ * IR IRQ Enable Register helpers
+ */
+static inline void irqenable_rx(struct v4l2_subdev *sd, u32 mask)
+{
+ struct cx25840_state *state = to_state(sd);
+
+ if (is_cx23885(state) || is_cx23887(state))
+ mask ^= IRQEN_MSK;
+ mask &= (IRQEN_RTE | IRQEN_ROE | IRQEN_RSE);
+ cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG,
+ ~(IRQEN_RTE | IRQEN_ROE | IRQEN_RSE), mask);
+}
+
+static inline void irqenable_tx(struct v4l2_subdev *sd, u32 mask)
+{
+ struct cx25840_state *state = to_state(sd);
+
+ if (is_cx23885(state) || is_cx23887(state))
+ mask ^= IRQEN_MSK;
+ mask &= IRQEN_TSE;
+ cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG, ~IRQEN_TSE, mask);
+}
+
+/*
+ * V4L2 Subdevice IR Ops
+ */
+int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c = NULL;
+ unsigned long flags;
+
+ union cx25840_ir_fifo_rec rx_data[FIFO_RX_DEPTH];
+ unsigned int i, j, k;
+ u32 events, v;
+ int tsr, rsr, rto, ror, tse, rse, rte, roe, kror;
+ u32 cntrl, irqen, stats;
+
+ *handled = false;
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ c = ir_state->c;
+
+ /* Only support the IR controller for the CX2388[57] AV Core for now */
+ if (!(is_cx23885(state) || is_cx23887(state)))
+ return -ENODEV;
+
+ cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG);
+ irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG);
+ if (is_cx23885(state) || is_cx23887(state))
+ irqen ^= IRQEN_MSK;
+ stats = cx25840_read4(c, CX25840_IR_STATS_REG);
+
+ tsr = stats & STATS_TSR; /* Tx FIFO Service Request */
+ rsr = stats & STATS_RSR; /* Rx FIFO Service Request */
+ rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */
+ ror = stats & STATS_ROR; /* Rx FIFO Over Run */
+
+ tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */
+ rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */
+ rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */
+ roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */
+
+ v4l2_dbg(2, ir_debug, sd, "IR IRQ Status: %s %s %s %s %s %s\n",
+ tsr ? "tsr" : " ", rsr ? "rsr" : " ",
+ rto ? "rto" : " ", ror ? "ror" : " ",
+ stats & STATS_TBY ? "tby" : " ",
+ stats & STATS_RBY ? "rby" : " ");
+
+ v4l2_dbg(2, ir_debug, sd, "IR IRQ Enables: %s %s %s %s\n",
+ tse ? "tse" : " ", rse ? "rse" : " ",
+ rte ? "rte" : " ", roe ? "roe" : " ");
+
+ /*
+ * Transmitter interrupt service
+ */
+ if (tse && tsr) {
+ /*
+ * TODO:
+ * Check the watermark threshold setting
+ * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo
+ * Push the data to the hardware FIFO.
+ * If there was nothing more to send in the tx_kfifo, disable
+ * the TSR IRQ and notify the v4l2_device.
+ * If there was something in the tx_kfifo, check the tx_kfifo
+ * level and notify the v4l2_device, if it is low.
+ */
+ /* For now, inhibit TSR interrupt until Tx is implemented */
+ irqenable_tx(sd, 0);
+ events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ;
+ v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events);
+ *handled = true;
+ }
+
+ /*
+ * Receiver interrupt service
+ */
+ kror = 0;
+ if ((rse && rsr) || (rte && rto)) {
+ /*
+ * Receive data on RSR to clear the STATS_RSR.
+ * Receive data on RTO, since we may not have yet hit the RSR
+ * watermark when we receive the RTO.
+ */
+ for (i = 0, v = FIFO_RX_NDV;
+ (v & FIFO_RX_NDV) && !kror; i = 0) {
+ for (j = 0;
+ (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) {
+ v = cx25840_read4(c, CX25840_IR_FIFO_REG);
+ rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV;
+ i++;
+ }
+ if (i == 0)
+ break;
+ j = i * sizeof(union cx25840_ir_fifo_rec);
+ k = kfifo_in_locked(&ir_state->rx_kfifo,
+ (unsigned char *) rx_data, j,
+ &ir_state->rx_kfifo_lock);
+ if (k != j)
+ kror++; /* rx_kfifo over run */
+ }
+ *handled = true;
+ }
+
+ events = 0;
+ v = 0;
+ if (kror) {
+ events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN;
+ v4l2_err(sd, "IR receiver software FIFO overrun\n");
+ }
+ if (roe && ror) {
+ /*
+ * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear
+ * the Rx FIFO Over Run status (STATS_ROR)
+ */
+ v |= CNTRL_RFE;
+ events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN;
+ v4l2_err(sd, "IR receiver hardware FIFO overrun\n");
+ }
+ if (rte && rto) {
+ /*
+ * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear
+ * the Rx Pulse Width Timer Time Out (STATS_RTO)
+ */
+ v |= CNTRL_RXE;
+ events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED;
+ }
+ if (v) {
+ /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */
+ cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl & ~v);
+ cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl);
+ *handled = true;
+ }
+ spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags);
+ if (kfifo_len(&ir_state->rx_kfifo) >= CX25840_IR_RX_KFIFO_SIZE / 2)
+ events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ;
+ spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags);
+
+ if (events)
+ v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events);
+ return 0;
+}
+
+/* Receiver */
+static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
+ ssize_t *num)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ bool invert;
+ u16 divider;
+ unsigned int i, n;
+ union cx25840_ir_fifo_rec *p;
+ unsigned u, v;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ invert = (bool) atomic_read(&ir_state->rx_invert);
+ divider = (u16) atomic_read(&ir_state->rxclk_divider);
+
+ n = count / sizeof(union cx25840_ir_fifo_rec)
+ * sizeof(union cx25840_ir_fifo_rec);
+ if (n == 0) {
+ *num = 0;
+ return 0;
+ }
+
+ n = kfifo_out_locked(&ir_state->rx_kfifo, buf, n,
+ &ir_state->rx_kfifo_lock);
+
+ n /= sizeof(union cx25840_ir_fifo_rec);
+ *num = n * sizeof(union cx25840_ir_fifo_rec);
+
+ for (p = (union cx25840_ir_fifo_rec *) buf, i = 0; i < n; p++, i++) {
+
+ if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
+ /* Assume RTO was because of no IR light input */
+ u = 0;
+ v4l2_dbg(2, ir_debug, sd, "rx read: end of rx\n");
+ } else {
+ u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0;
+ if (invert)
+ u = u ? 0 : 1;
+ }
+
+ v = (unsigned) pulse_width_count_to_ns(
+ (u16) (p->hw_fifo_data & FIFO_RXTX), divider);
+ if (v > IR_MAX_DURATION)
+ v = IR_MAX_DURATION;
+
+ p->ir_core_data.pulse = u;
+ p->ir_core_data.duration = v;
+
+ v4l2_dbg(2, ir_debug, sd, "rx read: %10u ns %s\n",
+ v, u ? "mark" : "space");
+ }
+ return 0;
+}
+
+static int cx25840_ir_rx_g_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ mutex_lock(&ir_state->rx_params_lock);
+ memcpy(p, &ir_state->rx_params,
+ sizeof(struct v4l2_subdev_ir_parameters));
+ mutex_unlock(&ir_state->rx_params_lock);
+ return 0;
+}
+
+static int cx25840_ir_rx_shutdown(struct v4l2_subdev *sd)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ c = ir_state->c;
+ mutex_lock(&ir_state->rx_params_lock);
+
+ /* Disable or slow down all IR Rx circuits and counters */
+ irqenable_rx(sd, 0);
+ control_rx_enable(c, false);
+ control_rx_demodulation_enable(c, false);
+ control_rx_s_edge_detection(c, CNTRL_EDG_NONE);
+ filter_rx_s_min_width(c, 0);
+ cx25840_write4(c, CX25840_IR_RXCLK_REG, RXCLK_RCD);
+
+ ir_state->rx_params.shutdown = true;
+
+ mutex_unlock(&ir_state->rx_params_lock);
+ return 0;
+}
+
+static int cx25840_ir_rx_s_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c;
+ struct v4l2_subdev_ir_parameters *o;
+ u16 rxclk_divider;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ if (p->shutdown)
+ return cx25840_ir_rx_shutdown(sd);
+
+ if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH)
+ return -ENOSYS;
+
+ c = ir_state->c;
+ o = &ir_state->rx_params;
+
+ mutex_lock(&ir_state->rx_params_lock);
+
+ o->shutdown = p->shutdown;
+
+ p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+ o->mode = p->mode;
+
+ p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec);
+ o->bytes_per_data_element = p->bytes_per_data_element;
+
+ /* Before we tweak the hardware, we have to disable the receiver */
+ irqenable_rx(sd, 0);
+ control_rx_enable(c, false);
+
+ control_rx_demodulation_enable(c, p->modulation);
+ o->modulation = p->modulation;
+
+ if (p->modulation) {
+ p->carrier_freq = rxclk_rx_s_carrier(c, p->carrier_freq,
+ &rxclk_divider);
+
+ o->carrier_freq = p->carrier_freq;
+
+ p->duty_cycle = 50;
+ o->duty_cycle = p->duty_cycle;
+
+ control_rx_s_carrier_window(c, p->carrier_freq,
+ &p->carrier_range_lower,
+ &p->carrier_range_upper);
+ o->carrier_range_lower = p->carrier_range_lower;
+ o->carrier_range_upper = p->carrier_range_upper;
+
+ p->max_pulse_width =
+ (u32) pulse_width_count_to_ns(FIFO_RXTX, rxclk_divider);
+ } else {
+ p->max_pulse_width =
+ rxclk_rx_s_max_pulse_width(c, p->max_pulse_width,
+ &rxclk_divider);
+ }
+ o->max_pulse_width = p->max_pulse_width;
+ atomic_set(&ir_state->rxclk_divider, rxclk_divider);
+
+ p->noise_filter_min_width =
+ filter_rx_s_min_width(c, p->noise_filter_min_width);
+ o->noise_filter_min_width = p->noise_filter_min_width;
+
+ p->resolution = clock_divider_to_resolution(rxclk_divider);
+ o->resolution = p->resolution;
+
+ /* FIXME - make this dependent on resolution for better performance */
+ control_rx_irq_watermark(c, RX_FIFO_HALF_FULL);
+
+ control_rx_s_edge_detection(c, CNTRL_EDG_BOTH);
+
+ o->invert_level = p->invert_level;
+ atomic_set(&ir_state->rx_invert, p->invert_level);
+
+ o->interrupt_enable = p->interrupt_enable;
+ o->enable = p->enable;
+ if (p->enable) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags);
+ kfifo_reset(&ir_state->rx_kfifo);
+ spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags);
+ if (p->interrupt_enable)
+ irqenable_rx(sd, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE);
+ control_rx_enable(c, p->enable);
+ }
+
+ mutex_unlock(&ir_state->rx_params_lock);
+ return 0;
+}
+
+/* Transmitter */
+static int cx25840_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count,
+ ssize_t *num)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ c = ir_state->c;
+#if 0
+ /*
+ * FIXME - the code below is an incomplete and untested sketch of what
+ * may need to be done. The critical part is to get 4 (or 8) pulses
+ * from the tx_kfifo, or converted from ns to the proper units from the
+ * input, and push them off to the hardware Tx FIFO right away, if the
+ * HW TX fifo needs service. The rest can be pushed to the tx_kfifo in
+ * a less critical timeframe. Also watch out for overruning the
+ * tx_kfifo - don't let it happen and let the caller know not all his
+ * pulses were written.
+ */
+ u32 *ns_pulse = (u32 *) buf;
+ unsigned int n;
+ u32 fifo_pulse[FIFO_TX_DEPTH];
+ u32 mark;
+
+ /* Compute how much we can fit in the tx kfifo */
+ n = CX25840_IR_TX_KFIFO_SIZE - kfifo_len(ir_state->tx_kfifo);
+ n = min(n, (unsigned int) count);
+ n /= sizeof(u32);
+
+ /* FIXME - turn on Tx Fifo service interrupt
+ * check hardware fifo level, and other stuff
+ */
+ for (i = 0; i < n; ) {
+ for (j = 0; j < FIFO_TX_DEPTH / 2 && i < n; j++) {
+ mark = ns_pulse[i] & LEVEL_MASK;
+ fifo_pulse[j] = ns_to_pulse_width_count(
+ ns_pulse[i] &
+ ~LEVEL_MASK,
+ ir_state->txclk_divider);
+ if (mark)
+ fifo_pulse[j] &= FIFO_RXTX_LVL;
+ i++;
+ }
+ kfifo_put(ir_state->tx_kfifo, (u8 *) fifo_pulse,
+ j * sizeof(u32));
+ }
+ *num = n * sizeof(u32);
+#else
+ /* For now enable the Tx FIFO Service interrupt & pretend we did work */
+ irqenable_tx(sd, IRQEN_TSE);
+ *num = count;
+#endif
+ return 0;
+}
+
+static int cx25840_ir_tx_g_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ mutex_lock(&ir_state->tx_params_lock);
+ memcpy(p, &ir_state->tx_params,
+ sizeof(struct v4l2_subdev_ir_parameters));
+ mutex_unlock(&ir_state->tx_params_lock);
+ return 0;
+}
+
+static int cx25840_ir_tx_shutdown(struct v4l2_subdev *sd)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ c = ir_state->c;
+ mutex_lock(&ir_state->tx_params_lock);
+
+ /* Disable or slow down all IR Tx circuits and counters */
+ irqenable_tx(sd, 0);
+ control_tx_enable(c, false);
+ control_tx_modulation_enable(c, false);
+ cx25840_write4(c, CX25840_IR_TXCLK_REG, TXCLK_TCD);
+
+ ir_state->tx_params.shutdown = true;
+
+ mutex_unlock(&ir_state->tx_params_lock);
+ return 0;
+}
+
+static int cx25840_ir_tx_s_parameters(struct v4l2_subdev *sd,
+ struct v4l2_subdev_ir_parameters *p)
+{
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+ struct i2c_client *c;
+ struct v4l2_subdev_ir_parameters *o;
+ u16 txclk_divider;
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ if (p->shutdown)
+ return cx25840_ir_tx_shutdown(sd);
+
+ if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH)
+ return -ENOSYS;
+
+ c = ir_state->c;
+ o = &ir_state->tx_params;
+ mutex_lock(&ir_state->tx_params_lock);
+
+ o->shutdown = p->shutdown;
+
+ p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+ o->mode = p->mode;
+
+ p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec);
+ o->bytes_per_data_element = p->bytes_per_data_element;
+
+ /* Before we tweak the hardware, we have to disable the transmitter */
+ irqenable_tx(sd, 0);
+ control_tx_enable(c, false);
+
+ control_tx_modulation_enable(c, p->modulation);
+ o->modulation = p->modulation;
+
+ if (p->modulation) {
+ p->carrier_freq = txclk_tx_s_carrier(c, p->carrier_freq,
+ &txclk_divider);
+ o->carrier_freq = p->carrier_freq;
+
+ p->duty_cycle = cduty_tx_s_duty_cycle(c, p->duty_cycle);
+ o->duty_cycle = p->duty_cycle;
+
+ p->max_pulse_width =
+ (u32) pulse_width_count_to_ns(FIFO_RXTX, txclk_divider);
+ } else {
+ p->max_pulse_width =
+ txclk_tx_s_max_pulse_width(c, p->max_pulse_width,
+ &txclk_divider);
+ }
+ o->max_pulse_width = p->max_pulse_width;
+ atomic_set(&ir_state->txclk_divider, txclk_divider);
+
+ p->resolution = clock_divider_to_resolution(txclk_divider);
+ o->resolution = p->resolution;
+
+ /* FIXME - make this dependent on resolution for better performance */
+ control_tx_irq_watermark(c, TX_FIFO_HALF_EMPTY);
+
+ control_tx_polarity_invert(c, p->invert_carrier_sense);
+ o->invert_carrier_sense = p->invert_carrier_sense;
+
+ /*
+ * FIXME: we don't have hardware help for IO pin level inversion
+ * here like we have on the CX23888.
+ * Act on this with some mix of logical inversion of data levels,
+ * carrier polarity, and carrier duty cycle.
+ */
+ o->invert_level = p->invert_level;
+
+ o->interrupt_enable = p->interrupt_enable;
+ o->enable = p->enable;
+ if (p->enable) {
+ /* reset tx_fifo here */
+ if (p->interrupt_enable)
+ irqenable_tx(sd, IRQEN_TSE);
+ control_tx_enable(c, p->enable);
+ }
+
+ mutex_unlock(&ir_state->tx_params_lock);
+ return 0;
+}
+
+
+/*
+ * V4L2 Subdevice Core Ops support
+ */
+int cx25840_ir_log_status(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *c = state->c;
+ char *s;
+ int i, j;
+ u32 cntrl, txclk, rxclk, cduty, stats, irqen, filtr;
+
+ /* The CX23888 chip doesn't have an IR controller on the A/V core */
+ if (is_cx23888(state))
+ return 0;
+
+ cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG);
+ txclk = cx25840_read4(c, CX25840_IR_TXCLK_REG) & TXCLK_TCD;
+ rxclk = cx25840_read4(c, CX25840_IR_RXCLK_REG) & RXCLK_RCD;
+ cduty = cx25840_read4(c, CX25840_IR_CDUTY_REG) & CDUTY_CDC;
+ stats = cx25840_read4(c, CX25840_IR_STATS_REG);
+ irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG);
+ if (is_cx23885(state) || is_cx23887(state))
+ irqen ^= IRQEN_MSK;
+ filtr = cx25840_read4(c, CX25840_IR_FILTR_REG) & FILTR_LPF;
+
+ v4l2_info(sd, "IR Receiver:\n");
+ v4l2_info(sd, "\tEnabled: %s\n",
+ cntrl & CNTRL_RXE ? "yes" : "no");
+ v4l2_info(sd, "\tDemodulation from a carrier: %s\n",
+ cntrl & CNTRL_DMD ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO: %s\n",
+ cntrl & CNTRL_RFE ? "enabled" : "disabled");
+ switch (cntrl & CNTRL_EDG) {
+ case CNTRL_EDG_NONE:
+ s = "disabled";
+ break;
+ case CNTRL_EDG_FALL:
+ s = "falling edge";
+ break;
+ case CNTRL_EDG_RISE:
+ s = "rising edge";
+ break;
+ case CNTRL_EDG_BOTH:
+ s = "rising & falling edges";
+ break;
+ default:
+ s = "??? edge";
+ break;
+ }
+ v4l2_info(sd, "\tPulse timers' start/stop trigger: %s\n", s);
+ v4l2_info(sd, "\tFIFO data on pulse timer overflow: %s\n",
+ cntrl & CNTRL_R ? "not loaded" : "overflow marker");
+ v4l2_info(sd, "\tFIFO interrupt watermark: %s\n",
+ cntrl & CNTRL_RIC ? "not empty" : "half full or greater");
+ v4l2_info(sd, "\tLoopback mode: %s\n",
+ cntrl & CNTRL_LBM ? "loopback active" : "normal receive");
+ if (cntrl & CNTRL_DMD) {
+ v4l2_info(sd, "\tExpected carrier (16 clocks): %u Hz\n",
+ clock_divider_to_carrier_freq(rxclk));
+ switch (cntrl & CNTRL_WIN) {
+ case CNTRL_WIN_3_3:
+ i = 3;
+ j = 3;
+ break;
+ case CNTRL_WIN_4_3:
+ i = 4;
+ j = 3;
+ break;
+ case CNTRL_WIN_3_4:
+ i = 3;
+ j = 4;
+ break;
+ case CNTRL_WIN_4_4:
+ i = 4;
+ j = 4;
+ break;
+ default:
+ i = 0;
+ j = 0;
+ break;
+ }
+ v4l2_info(sd, "\tNext carrier edge window: 16 clocks "
+ "-%1d/+%1d, %u to %u Hz\n", i, j,
+ clock_divider_to_freq(rxclk, 16 + j),
+ clock_divider_to_freq(rxclk, 16 - i));
+ }
+ v4l2_info(sd, "\tMax measurable pulse width: %u us, %llu ns\n",
+ pulse_width_count_to_us(FIFO_RXTX, rxclk),
+ pulse_width_count_to_ns(FIFO_RXTX, rxclk));
+ v4l2_info(sd, "\tLow pass filter: %s\n",
+ filtr ? "enabled" : "disabled");
+ if (filtr)
+ v4l2_info(sd, "\tMin acceptable pulse width (LPF): %u us, "
+ "%u ns\n",
+ lpf_count_to_us(filtr),
+ lpf_count_to_ns(filtr));
+ v4l2_info(sd, "\tPulse width timer timed-out: %s\n",
+ stats & STATS_RTO ? "yes" : "no");
+ v4l2_info(sd, "\tPulse width timer time-out intr: %s\n",
+ irqen & IRQEN_RTE ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO overrun: %s\n",
+ stats & STATS_ROR ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO overrun interrupt: %s\n",
+ irqen & IRQEN_ROE ? "enabled" : "disabled");
+ v4l2_info(sd, "\tBusy: %s\n",
+ stats & STATS_RBY ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service requested: %s\n",
+ stats & STATS_RSR ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service request interrupt: %s\n",
+ irqen & IRQEN_RSE ? "enabled" : "disabled");
+
+ v4l2_info(sd, "IR Transmitter:\n");
+ v4l2_info(sd, "\tEnabled: %s\n",
+ cntrl & CNTRL_TXE ? "yes" : "no");
+ v4l2_info(sd, "\tModulation onto a carrier: %s\n",
+ cntrl & CNTRL_MOD ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO: %s\n",
+ cntrl & CNTRL_TFE ? "enabled" : "disabled");
+ v4l2_info(sd, "\tFIFO interrupt watermark: %s\n",
+ cntrl & CNTRL_TIC ? "not empty" : "half full or less");
+ v4l2_info(sd, "\tCarrier polarity: %s\n",
+ cntrl & CNTRL_CPL ? "space:burst mark:noburst"
+ : "space:noburst mark:burst");
+ if (cntrl & CNTRL_MOD) {
+ v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n",
+ clock_divider_to_carrier_freq(txclk));
+ v4l2_info(sd, "\tCarrier duty cycle: %2u/16\n",
+ cduty + 1);
+ }
+ v4l2_info(sd, "\tMax pulse width: %u us, %llu ns\n",
+ pulse_width_count_to_us(FIFO_RXTX, txclk),
+ pulse_width_count_to_ns(FIFO_RXTX, txclk));
+ v4l2_info(sd, "\tBusy: %s\n",
+ stats & STATS_TBY ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service requested: %s\n",
+ stats & STATS_TSR ? "yes" : "no");
+ v4l2_info(sd, "\tFIFO service request interrupt: %s\n",
+ irqen & IRQEN_TSE ? "enabled" : "disabled");
+
+ return 0;
+}
+
+
+const struct v4l2_subdev_ir_ops cx25840_ir_ops = {
+ .rx_read = cx25840_ir_rx_read,
+ .rx_g_parameters = cx25840_ir_rx_g_parameters,
+ .rx_s_parameters = cx25840_ir_rx_s_parameters,
+
+ .tx_write = cx25840_ir_tx_write,
+ .tx_g_parameters = cx25840_ir_tx_g_parameters,
+ .tx_s_parameters = cx25840_ir_tx_s_parameters,
+};
+
+
+static const struct v4l2_subdev_ir_parameters default_rx_params = {
+ .bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec),
+ .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
+
+ .enable = false,
+ .interrupt_enable = false,
+ .shutdown = true,
+
+ .modulation = true,
+ .carrier_freq = 36000, /* 36 kHz - RC-5, and RC-6 carrier */
+
+ /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */
+ /* RC-6: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */
+ .noise_filter_min_width = 333333, /* ns */
+ .carrier_range_lower = 35000,
+ .carrier_range_upper = 37000,
+ .invert_level = false,
+};
+
+static const struct v4l2_subdev_ir_parameters default_tx_params = {
+ .bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec),
+ .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
+
+ .enable = false,
+ .interrupt_enable = false,
+ .shutdown = true,
+
+ .modulation = true,
+ .carrier_freq = 36000, /* 36 kHz - RC-5 carrier */
+ .duty_cycle = 25, /* 25 % - RC-5 carrier */
+ .invert_level = false,
+ .invert_carrier_sense = false,
+};
+
+int cx25840_ir_probe(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct cx25840_ir_state *ir_state;
+ struct v4l2_subdev_ir_parameters default_params;
+
+ /* Only init the IR controller for the CX2388[57] AV Core for now */
+ if (!(is_cx23885(state) || is_cx23887(state)))
+ return 0;
+
+ ir_state = kzalloc(sizeof(struct cx25840_ir_state), GFP_KERNEL);
+ if (ir_state == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&ir_state->rx_kfifo_lock);
+ if (kfifo_alloc(&ir_state->rx_kfifo,
+ CX25840_IR_RX_KFIFO_SIZE, GFP_KERNEL)) {
+ kfree(ir_state);
+ return -ENOMEM;
+ }
+
+ ir_state->c = state->c;
+ state->ir_state = ir_state;
+
+ /* Ensure no interrupts arrive yet */
+ if (is_cx23885(state) || is_cx23887(state))
+ cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, IRQEN_MSK);
+ else
+ cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, 0);
+
+ mutex_init(&ir_state->rx_params_lock);
+ memcpy(&default_params, &default_rx_params,
+ sizeof(struct v4l2_subdev_ir_parameters));
+ v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params);
+
+ mutex_init(&ir_state->tx_params_lock);
+ memcpy(&default_params, &default_tx_params,
+ sizeof(struct v4l2_subdev_ir_parameters));
+ v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params);
+
+ return 0;
+}
+
+int cx25840_ir_remove(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct cx25840_ir_state *ir_state = to_ir_state(sd);
+
+ if (ir_state == NULL)
+ return -ENODEV;
+
+ cx25840_ir_rx_shutdown(sd);
+ cx25840_ir_tx_shutdown(sd);
+
+ kfifo_free(&ir_state->rx_kfifo);
+ kfree(ir_state);
+ state->ir_state = NULL;
+ return 0;
+}
MODULE_DESCRIPTION("GSPCA USB Camera Driver");
MODULE_LICENSE("GPL");
-#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 9, 0)
+#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 10, 0)
#ifdef GSPCA_DEBUG
int gspca_debug = D_ERR | D_PROBE;
frame->v4l2_buf.sequence = ++gspca_dev->sequence;
gspca_dev->image = frame->data;
gspca_dev->image_len = 0;
- } else if (gspca_dev->last_packet_type == DISCARD_PACKET) {
- if (packet_type == LAST_PACKET)
- gspca_dev->last_packet_type = packet_type;
- return;
+ } else {
+ switch (gspca_dev->last_packet_type) {
+ case DISCARD_PACKET:
+ if (packet_type == LAST_PACKET)
+ gspca_dev->last_packet_type = packet_type;
+ return;
+ case LAST_PACKET:
+ return;
+ }
}
/* append the packet to the frame buffer */
gspca_dev->frsz);
packet_type = DISCARD_PACKET;
} else {
+/* !! image is NULL only when last pkt is LAST or DISCARD
+ if (gspca_dev->image == NULL) {
+ err("gspca_frame_add() image == NULL");
+ return;
+ }
+ */
memcpy(gspca_dev->image + gspca_dev->image_len,
data, len);
gspca_dev->image_len += len;
#define BRIDGE_SN9C110 2
#define BRIDGE_SN9C120 3
u8 sensor; /* Type of image sensor chip */
-enum {
+ u8 i2c_addr;
+
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+enum sensors {
SENSOR_ADCM1700,
SENSOR_GC0307,
SENSOR_HV7131R,
SENSOR_PO2030N,
SENSOR_SOI768,
SENSOR_SP80708,
-} sensors;
- u8 i2c_addr;
-
- u8 jpeg_hdr[JPEG_HDR_SZ];
};
/* V4L2 controls supported by the driver */
#define MODULE_NAME "sq930x"
#include "gspca.h"
-#include "jpeg.h"
MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>\n"
"Gerard Klaver <gerard at gkall dot hobby dot nl\n"
MODULE_DESCRIPTION("GSPCA/SQ930x USB Camera Driver");
MODULE_LICENSE("GPL");
-#define BULK_TRANSFER_LEN 5128
-
/* Structure to hold all of our device specific stuff */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
u16 expo;
u8 gain;
- u8 quality; /* webcam quality 0..3 */
-#define QUALITY_DEF 1
-
- u8 gpio[2];
-
- u8 eof_len;
u8 do_ctrl;
-
+ u8 gpio[2];
u8 sensor;
-enum {
+ u8 type;
+#define Generic 0
+#define Creative_live_motion 1
+};
+enum sensors {
SENSOR_ICX098BQ,
SENSOR_LZ24BP,
SENSOR_MI0360,
- SENSOR_MT9V111,
+ SENSOR_MT9V111, /* = MI360SOC */
SENSOR_OV7660,
SENSOR_OV9630,
-} sensors;
- u8 type;
-#define Generic 0
-#define Creative_live_motion 1
-
- u8 jpeg_hdr[JPEG_HDR_SZ];
};
static int sd_setexpo(struct gspca_dev *gspca_dev, __s32 val);
.minimum = 0x0001,
.maximum = 0x0fff,
.step = 1,
-#define EXPO_DEF 0x027d
+#define EXPO_DEF 0x0356
.default_value = EXPO_DEF,
},
.set = sd_setexpo,
.minimum = 0x01,
.maximum = 0xff,
.step = 1,
-#define GAIN_DEF 0x61
+#define GAIN_DEF 0x8d
.default_value = GAIN_DEF,
},
.set = sd_setgain,
};
static struct v4l2_pix_format vga_mode[] = {
- {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 160,
- .sizeimage = 160 * 120 * 5 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 0},
- {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ {320, 240, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
.bytesperline = 320,
- .sizeimage = 320 * 240 * 4 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 1},
- {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+ {640, 480, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
.bytesperline = 640,
- .sizeimage = 640 * 480 * 3 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 2},
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
};
-/* JPEG quality indexed by webcam quality */
-#define QUAL_0 90
-#define QUAL_1 85
-#define QUAL_2 75
-#define QUAL_3 70
-static const u8 quality_tb[4] = { QUAL_0, QUAL_1, QUAL_2, QUAL_3 };
-
/* sq930x registers */
#define SQ930_CTRL_UCBUS_IO 0x0001
#define SQ930_CTRL_I2C_IO 0x0002
{0x01, 0x0001}, /* select IFP/SOC registers */
{0x06, 0x300c}, /* operating mode control */
{0x08, 0xcc00}, /* output format control (RGB) */
- {0x01, 0x0004}, /* select core registers */
+ {0x01, 0x0004}, /* select sensor core registers */
};
static const struct i2c_write_cmd mt9v111_init_1[] = {
{0x03, 0x01e5}, /* window height */
{0x62, 0x0405},
};
static const struct i2c_write_cmd mt9v111_init_4[] = {
- {0x05, 0x00ce}, /* horizontal blanking */
+/* {0x05, 0x00ce}, */
+ {0x05, 0x005d}, /* horizontal blanking */
};
static const struct ucbus_write_cmd ov7660_start_0[] = {
{0xf334, 0x3e}, {0xf335, 0xf8}, {0xf33f, 0x03}
};
+/* start parameters indexed by [sensor][mode] */
static const struct cap_s {
u8 cc_sizeid;
u8 cc_bytes[32];
-} capconfig[4][3] = {
+} capconfig[4][2] = {
[SENSOR_ICX098BQ] = {
- {0, /* JPEG, 160x120 */
+ {2, /* Bayer 320x240 */
+ {0x05, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
+ 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ {4, /* Bayer 640x480 */
{0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x02, 0x8b, 0x00, 0x8b, 0x00, 0x41, 0x01, 0x41,
- 0x01, 0x41, 0x01, 0x05, 0x40, 0x01, 0xf0, 0x00} },
- {2, /* JPEG, 320x240 */
- {0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
- 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x02, 0xdf, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x3f,
- 0x01, 0x00, 0x00, 0x05, 0x40, 0x01, 0xf0, 0x00} },
- {4, /* JPEG, 640x480 */
- {0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xf0,
- 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x07, 0xe1, 0x01, 0xe1, 0x01, 0x3f, 0x01, 0x3f,
- 0x01, 0x3f, 0x01, 0x05, 0x80, 0x02, 0xe0, 0x01} },
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
},
[SENSOR_LZ24BP] = {
- {0, /* JPEG, 160x120 */
- {0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
- 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x02, 0x8b, 0x00, 0x8b, 0x00, 0x41, 0x01, 0x41,
- 0x01, 0x41, 0x01, 0x05, 0x40, 0x01, 0xf0, 0x00} },
- {2, /* JPEG, 320x240 */
+ {2, /* Bayer 320x240 */
+ {0x05, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
+ 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ {4, /* Bayer 640x480 */
{0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x02, 0xdf, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x3f,
- 0x01, 0x00, 0x00, 0x05, 0x40, 0x01, 0xf0, 0x00} },
- {4, /* JPEG, 640x480 */
- {0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xf0,
- 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x07, 0xe1, 0x01, 0xe1, 0x01, 0x3f, 0x01, 0x3f,
- 0x01, 0x3f, 0x01, 0x05, 0x80, 0x02, 0xe0, 0x01} },
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
},
[SENSOR_MI0360] = {
- {0, /* JPEG, 160x120 */
- {0x05, 0x3d, 0x20, 0x0b, 0x00, 0xbd, 0x02, 0x0b,
- 0x02, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x02, 0x01, 0x01, 0x01, 0x01, 0x9f, 0x00, 0x9f,
- 0x00, 0x9f, 0x01, 0x05, 0xa0, 0x00, 0x80, 0x00} },
- {2, /* JPEG, 320x240 */
+ {2, /* Bayer 320x240 */
+ {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
+ 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ {4, /* Bayer 640x480 */
{0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
-/*fixme 03 e3 */
0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x02, 0xdf, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x3f,
- 0x01, 0x00, 0x00, 0x05, 0x40, 0x01, 0xf0, 0x00} },
- {4, /* JPEG, 640x480 */
- {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe3,
- 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x07, 0xe1, 0x01, 0xe1, 0x01, 0x3f, 0x01, 0x3f,
- 0x01, 0x3f, 0x01, 0x05, 0x80, 0x02, 0xe0, 0x01} },
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
},
[SENSOR_MT9V111] = {
- {0, /* JPEG, 160x120 */
- {0x05, 0x3d, 0x20, 0x0b, 0x00, 0xbd, 0x02, 0x0b,
- 0x02, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x02, 0x01, 0x01, 0x01, 0x01, 0x9f, 0x00, 0x9f,
- 0x00, 0x9f, 0x01, 0x05, 0xa0, 0x00, 0x80, 0x00} },
- {2, /* JPEG, 320x240 */
- {0x01, 0x02, 0x20, 0x03, 0x20, 0x82, 0x02, 0xe3,
- 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x02, 0xdf, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x3f,
- 0x01, 0x00, 0x00, 0x05, 0x40, 0x01, 0xf0, 0x00} },
- {4, /* JPEG, 640x480 */
- {0x01, 0x02, 0x20, 0x03, 0x20, 0x82, 0x02, 0xe3,
+ {2, /* Bayer 320x240 */
+ {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
+ 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ {4, /* Bayer 640x480 */
+ {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x07, 0xe1, 0x01, 0xe1, 0x01, 0x3f, 0x01, 0x3f,
- 0x01, 0x3f, 0x01, 0x05, 0x80, 0x02, 0xe0, 0x01} },
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
},
};
buf[i++] = 0x35; /* reg = global gain */
buf[i++] = 0x00; /* val H */
buf[i++] = sensor->i2c_dum;
- buf[i++] = sd->gain; /* val L */
+ buf[i++] = 0x80 + sd->gain / 2; /* val L */
buf[i++] = 0x00;
buf[i++] = 0x00;
buf[i++] = 0x00;
cam->nmodes = ARRAY_SIZE(vga_mode);
cam->bulk = 1;
- cam->bulk_size = BULK_TRANSFER_LEN;
-/* cam->bulk_nurbs = 2; fixme: if no setexpo sync */
- sd->quality = QUALITY_DEF;
sd->gain = GAIN_DEF;
sd->expo = EXPO_DEF;
if (sd->sensor == SENSOR_MI0360) {
/* no sensor probe for icam tracer */
- if (gspca_dev->usb_buf[5] == 0xf6) { /* if CMOS */
+ if (gspca_dev->usb_buf[5] == 0xf6) /* if CMOS */
sd->sensor = SENSOR_ICX098BQ;
- gspca_dev->cam.cam_mode = &vga_mode[1];
- gspca_dev->cam.nmodes = 1; /* only 320x240 */
- } else {
+ else
cmos_probe(gspca_dev);
- }
}
PDEBUG(D_PROBE, "Sensor %s", sensor_tb[sd->sensor].name);
return gspca_dev->usb_err;
}
-/* special function to create the quantization tables of the JPEG header */
-static void sd_jpeg_set_qual(u8 *jpeg_hdr,
- int quality)
-{
- int i, sc1, sc2;
-
- quality = quality_tb[quality]; /* convert to JPEG quality */
-/*
- * approximative qualities for Y and U/V:
- * quant = 0:94%/91% 1:91%/87% 2:82%/73% 3:69%/56%
- * should have:
- * quant = 0:94%/91% 1:91%/87.5% 2:81.5%/72% 3:69%/54.5%
- */
- sc1 = 200 - quality * 2;
- quality = quality * 7 / 5 - 40; /* UV quality */
- sc2 = 200 - quality * 2;
- for (i = 0; i < 64; i++) {
- jpeg_hdr[JPEG_QT0_OFFSET + i] =
- (jpeg_head[JPEG_QT0_OFFSET + i] * sc1 + 50) / 100;
- jpeg_hdr[JPEG_QT1_OFFSET + i] =
- (jpeg_head[JPEG_QT1_OFFSET + i] * sc2 + 50) / 100;
- }
-}
-
/* send the start/stop commands to the webcam */
static void send_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
const struct cap_s *cap;
- int mode, quality;
+ int mode;
mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
cap = &capconfig[sd->sensor][mode];
- quality = sd->quality;
- reg_wb(gspca_dev, (quality << 12)
- | 0x0a00 /* 900 for Bayer */
- | SQ930_CTRL_CAP_START,
- 0x0500 /* a00 for Bayer */
- | cap->cc_sizeid,
+ reg_wb(gspca_dev, 0x0900 | SQ930_CTRL_CAP_START,
+ 0x0a00 | cap->cc_sizeid,
cap->cc_bytes, 32);
-};
+}
+
static void send_stop(struct gspca_dev *gspca_dev)
{
reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0);
-};
+}
/* function called at start time before URB creation */
static int sd_isoc_init(struct gspca_dev *gspca_dev)
gspca_dev->cam.bulk_nurbs = 1; /* there must be one URB only */
sd->do_ctrl = 0;
+ gspca_dev->cam.bulk_size = gspca_dev->width * gspca_dev->height + 8;
return 0;
}
struct sd *sd = (struct sd *) gspca_dev;
int mode;
- /* initialize the JPEG header */
- jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
- 0x21); /* JPEG 422 */
- sd_jpeg_set_qual(sd->jpeg_hdr, sd->quality);
-
bridge_init(sd);
global_init(sd, 0);
msleep(100);
ARRAY_SIZE(lz24bp_start_2),
6);
mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
- lz24bp_ppl(sd, mode == 2 ? 0x0564 : 0x0310);
+ lz24bp_ppl(sd, mode == 1 ? 0x0564 : 0x0310);
msleep(10);
break;
case SENSOR_MI0360:
/* 1st start */
send_start(gspca_dev);
msleep(60);
- reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0x0000);
+ send_stop(gspca_dev);
i2c_write(sd,
mi0360_start_4, ARRAY_SIZE(mi0360_start_4));
ARRAY_SIZE(mt9v111_init_2));
ucbus_write(gspca_dev, mt9v111_start_1,
ARRAY_SIZE(mt9v111_start_1),
- 8);
+ 5);
i2c_write(sd, mt9v111_init_3,
ARRAY_SIZE(mt9v111_init_3));
i2c_write(sd, mt9v111_init_4,
out:
msleep(1000);
- sd->eof_len = 0; /* init packet scan */
-
if (sd->sensor == SENSOR_MT9V111)
gpio_set(sd, SQ930_GPIO_DFL_LED, SQ930_GPIO_DFL_LED);
msleep(100);
}
-/* move a packet adding 0x00 after 0xff */
-static void add_packet(struct gspca_dev *gspca_dev,
- u8 *data,
- int len)
-{
- int i;
-
- i = 0;
- do {
- if (data[i] == 0xff) {
- gspca_frame_add(gspca_dev, INTER_PACKET,
- data, i + 1);
- len -= i;
- data += i;
- *data = 0x00;
- i = 0;
- }
- } while (++i < len);
- gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
-}
-
-/* end a frame and start a new one */
-static void eof_sof(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- static const u8 ffd9[] = {0xff, 0xd9};
-
- /* if control set, stop bulk transfer */
- if (sd->do_ctrl
- && gspca_dev->last_packet_type == INTER_PACKET)
- gspca_dev->cam.bulk_nurbs = 0;
- gspca_frame_add(gspca_dev, LAST_PACKET,
- ffd9, 2);
- gspca_frame_add(gspca_dev, FIRST_PACKET,
- sd->jpeg_hdr, JPEG_HDR_SZ);
-}
-
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
u8 *data, /* isoc packet */
int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
- u8 *p;
- int l;
-
- len -= 8; /* ignore last 8 bytes (00 00 55 aa 55 aa 00 00) */
-
- /*
- * the end/start of frame is indicated by
- * 0x00 * 16 - 0xab * 8
- * aligned on 8 bytes boundary
- */
- if (sd->eof_len != 0) { /* if 'abababab' in previous pkt */
- if (*((u32 *) data) == 0xabababab) {
- /*fixme: should remove previous 0000ababab*/
- eof_sof(gspca_dev);
- data += 4;
- len -= 4;
- }
- sd->eof_len = 0;
- }
- p = data;
- l = len;
- for (;;) {
- if (*((u32 *) p) == 0xabababab) {
- if (l < 8) { /* (may be 4 only) */
- sd->eof_len = 1;
- break;
- }
- if (*((u32 *) p + 1) == 0xabababab) {
- add_packet(gspca_dev, data, p - data - 16);
- /* remove previous zeros */
- eof_sof(gspca_dev);
- p += 8;
- l -= 8;
- if (l <= 0)
- return;
- len = l;
- data = p;
- continue;
- }
- }
- p += 4;
- l -= 4;
- if (l <= 0)
- break;
- }
- add_packet(gspca_dev, data, len);
+
+ if (sd->do_ctrl)
+ gspca_dev->cam.bulk_nurbs = 0;
+ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len - 8);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
}
static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
return 0;
}
-static int sd_set_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int quality;
-
- if (jcomp->quality >= (QUAL_0 + QUAL_1) / 2)
- quality = 0;
- else if (jcomp->quality >= (QUAL_1 + QUAL_2) / 2)
- quality = 1;
- else if (jcomp->quality >= (QUAL_2 + QUAL_3) / 2)
- quality = 2;
- else
- quality = 3;
-
- if (quality != sd->quality) {
- sd->quality = quality;
- if (gspca_dev->streaming) {
- send_stop(gspca_dev);
- sd_jpeg_set_qual(sd->jpeg_hdr, sd->quality);
- msleep(70);
- send_start(gspca_dev);
- }
- }
- return gspca_dev->usb_err;
-}
-
-static int sd_get_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- memset(jcomp, 0, sizeof *jcomp);
- jcomp->quality = quality_tb[sd->quality];
- jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
- | V4L2_JPEG_MARKER_DQT;
- return 0;
-}
-
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
.dq_callback = sd_dq_callback,
- .get_jcomp = sd_get_jcomp,
- .set_jcomp = sd_set_jcomp,
};
/* Table of supported USB devices */
u8 effect;
u8 sensor;
-enum {
+};
+enum sensors {
SENSOR_OM6802,
SENSOR_OTHER,
SENSOR_TAS5130A,
SENSOR_LT168G, /* must verify if this is the actual model */
-} sensors;
};
/* V4L2 controls supported by the driver */
u8 vflip;
u8 lightfreq;
s8 sharpness;
+ u16 exposure;
+ u8 gain;
+ u8 autogain;
+ u8 backlight;
u8 image_offset;
static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setbacklight(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbacklight(struct gspca_dev *gspca_dev, __s32 *val);
static const struct ctrl sd_ctrls[] = {
#define BRIGHTNESS_IDX 0
.set = sd_setsharpness,
.get = sd_getsharpness,
},
+#define GAIN_IDX 7
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 78,
+ .step = 1,
+#define GAIN_DEF 0
+ .default_value = GAIN_DEF,
+ },
+ .set = sd_setgain,
+ .get = sd_getgain,
+ },
+#define EXPOSURE_IDX 8
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+#define EXPOSURE_DEF 450
+ .minimum = 0,
+ .maximum = 4095,
+ .step = 1,
+ .default_value = EXPOSURE_DEF,
+ },
+ .set = sd_setexposure,
+ .get = sd_getexposure,
+ },
+#define AUTOGAIN_IDX 9
+ {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Automatic Gain and Exposure",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define AUTOGAIN_DEF 1
+ .default_value = AUTOGAIN_DEF,
+ },
+ .set = sd_setautogain,
+ .get = sd_getautogain,
+ },
+#define BACKLIGHT_IDX 10
+ {
+ {
+ .id = V4L2_CID_BACKLIGHT_COMPENSATION,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Backlight Compensation",
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+#define BACKLIGHT_DEF 15
+ .default_value = BACKLIGHT_DEF,
+ },
+ .set = sd_setbacklight,
+ .get = sd_getbacklight,
+ },
};
/* table of the disabled controls */
/* SENSOR_HV7131R 0 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
| (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
- | (1 << SHARPNESS_IDX),
+ | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_MI0360 1 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
| (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
- | (1 << SHARPNESS_IDX),
+ | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_MI1310_SOC 2 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
+ | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_MI1320 3 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
+ | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_MI1320_SOC 4 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
+ | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_OV7660 5 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
+ | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_OV7670 6 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << SHARPNESS_IDX),
+ | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_PO1200 7 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
- | (1 << LIGHTFREQ_IDX),
+ | (1 << LIGHTFREQ_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_PO3130NC 8 */
(1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
| (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
- | (1 << SHARPNESS_IDX),
+ | (1 << SHARPNESS_IDX)
+ | (1 << GAIN_IDX) | (1 << EXPOSURE_IDX)
+ | (1 << AUTOGAIN_IDX) | (1 << BACKLIGHT_IDX),
/* SENSOR_POxxxx 9 */
(1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX),
};
{0x00, 0x1e, 0xc6, 0xaa},
{0x00, 0x00, 0x40, 0xdd},
{0x00, 0x1d, 0x05, 0xaa},
-
+ {}
+};
+static const u8 poxxxx_gamma[][4] = {
{0x00, 0xd6, 0x22, 0xaa}, /* gamma 0 */
{0x00, 0x73, 0x00, 0xaa},
{0x00, 0x74, 0x0a, 0xaa},
{0x00, 0x7c, 0xba, 0xaa},
{0x00, 0x7d, 0xd4, 0xaa},
{0x00, 0x7e, 0xea, 0xaa},
-
- {0x00, 0xaa, 0xff, 0xaa}, /* back light comp */
- {0x00, 0xc4, 0x03, 0xaa},
- {0x00, 0xc5, 0x19, 0xaa},
- {0x00, 0xc6, 0x03, 0xaa},
- {0x00, 0xc7, 0x91, 0xaa},
- {0x00, 0xc8, 0x01, 0xaa},
- {0x00, 0xc9, 0xdd, 0xaa},
- {0x00, 0xca, 0x02, 0xaa},
- {0x00, 0xcb, 0x37, 0xaa},
-
-/* read d1 */
- {0x00, 0xd1, 0x3c, 0xaa},
+ {}
+};
+static const u8 poxxxx_init_start_3[][4] = {
{0x00, 0xb8, 0x28, 0xaa},
{0x00, 0xb9, 0x1e, 0xaa},
{0x00, 0xb6, 0x14, 0xaa},
{0x00, 0xb3, 0x08, 0xaa},
{0x00, 0xb4, 0x0b, 0xaa},
{0x00, 0xb5, 0x0d, 0xaa},
- {0x00, 0x59, 0x7e, 0xaa}, /* sharpness */
- {0x00, 0x16, 0x00, 0xaa}, /* white balance */
- {0x00, 0x18, 0x00, 0xaa},
{}
};
static const u8 poxxxx_init_end_2[][4] = {
/* this function is called at probe time */
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->bridge = id->driver_info >> 8;
+ sd->flags = id->driver_info & 0xff;
+
+ if (id->idVendor == 0x046d &&
+ (id->idProduct == 0x0892 || id->idProduct == 0x0896))
+ sd->sensor = SENSOR_POxxxx; /* no probe */
+
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->colors = COLOR_DEF;
+ sd->hflip = HFLIP_DEF;
+ sd->vflip = VFLIP_DEF;
+ sd->lightfreq = FREQ_DEF;
+ sd->sharpness = SHARPNESS_DEF;
+ sd->gain = GAIN_DEF;
+ sd->exposure = EXPOSURE_DEF;
+ sd->autogain = AUTOGAIN_DEF;
+ sd->backlight = BACKLIGHT_DEF;
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
128, /* POxxxx 9 */
};
- cam = &gspca_dev->cam;
- sd->bridge = id->driver_info >> 8;
- sd->flags = id->driver_info & 0xff;
- if (id->idVendor == 0x046d &&
- (id->idProduct == 0x0892 || id->idProduct == 0x0896))
- sensor = SENSOR_POxxxx;
- else
+ if (sd->sensor != SENSOR_POxxxx)
sensor = vc032x_probe_sensor(gspca_dev);
+ else
+ sensor = sd->sensor;
+
switch (sensor) {
case -1:
PDEBUG(D_PROBE, "Unknown sensor...");
}
sd->sensor = sensor;
+ cam = &gspca_dev->cam;
if (sd->bridge == BRIDGE_VC0321) {
cam->cam_mode = vc0321_mode;
cam->nmodes = ARRAY_SIZE(vc0321_mode);
}
}
cam->npkt = npkt[sd->sensor];
-
- sd->brightness = BRIGHTNESS_DEF;
- sd->contrast = CONTRAST_DEF;
- sd->colors = COLOR_DEF;
- sd->hflip = HFLIP_DEF;
- sd->vflip = VFLIP_DEF;
- sd->lightfreq = FREQ_DEF;
- sd->sharpness = SHARPNESS_DEF;
-
gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
if (sd->sensor == SENSOR_OV7670)
sd->flags |= FL_HFLIP | FL_VFLIP;
- return 0;
-}
-
-/* this function is called at probe and resume time */
-static int sd_init(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
if (sd->bridge == BRIDGE_VC0321) {
reg_r(gspca_dev, 0x8a, 0, 3);
reg_w(gspca_dev, 0x87, 0x00, 0x0f0f);
if (gspca_dev->usb_buf[0] != 0) {
reg_w(gspca_dev, 0xa0, 0x26, 0xb300);
reg_w(gspca_dev, 0xa0, 0x04, 0xb300);
- reg_w(gspca_dev, 0xa0, 0x00, 0xb300);
}
+ reg_w(gspca_dev, 0xa0, 0x00, 0xb300);
}
}
return gspca_dev->usb_err;
break;
}
}
+static void setgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (gspca_dev->ctrl_dis & (1 << GAIN_IDX))
+ return;
+ i2c_write(gspca_dev, 0x15, &sd->gain, 1);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data;
+
+ if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX))
+ return;
+ data = sd->exposure >> 8;
+ i2c_write(gspca_dev, 0x1a, &data, 1);
+ data = sd->exposure;
+ i2c_write(gspca_dev, 0x1b, &data, 1);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const u8 data[2] = {0x28, 0x3c};
+
+ if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX))
+ return;
+ i2c_write(gspca_dev, 0xd1, &data[sd->autogain], 1);
+}
+
+static void setgamma(struct gspca_dev *gspca_dev)
+{
+/*fixme:to do */
+ usb_exchange(gspca_dev, poxxxx_gamma);
+}
+
+static void setbacklight(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 v;
+ u8 data;
+
+ data = (sd->backlight << 4) | 0x0f;
+ i2c_write(gspca_dev, 0xaa, &data, 1);
+ v = 613 + 12 * sd->backlight;
+ data = v >> 8;
+ i2c_write(gspca_dev, 0xc4, &data, 1);
+ data = v;
+ i2c_write(gspca_dev, 0xc5, &data, 1);
+ v = 1093 - 12 * sd->backlight;
+ data = v >> 8;
+ i2c_write(gspca_dev, 0xc6, &data, 1);
+ data = v;
+ i2c_write(gspca_dev, 0xc7, &data, 1);
+ v = 342 + 9 * sd->backlight;
+ data = v >> 8;
+ i2c_write(gspca_dev, 0xc8, &data, 1);
+ data = v;
+ i2c_write(gspca_dev, 0xc9, &data, 1);
+ v = 702 - 9 * sd->backlight;
+ data = v >> 8;
+ i2c_write(gspca_dev, 0xca, &data, 1);
+ data = v;
+ i2c_write(gspca_dev, 0xcb, &data, 1);
+}
+
+static void setwb(struct gspca_dev *gspca_dev)
+{
+/*fixme:to do - valid when reg d1 = 0x1c - (reg16 + reg15 = 0xa3)*/
+ static const u8 data[2] = {0x00, 0x00};
+
+ i2c_write(gspca_dev, 0x16, &data[0], 1);
+ i2c_write(gspca_dev, 0x18, &data[1], 1);
+}
static int sd_start(struct gspca_dev *gspca_dev)
{
default:
/* case SENSOR_POxxxx: */
usb_exchange(gspca_dev, poxxxx_init_common);
+ setgamma(gspca_dev);
+ setbacklight(gspca_dev);
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
+ setcolors(gspca_dev);
+ setsharpness(gspca_dev);
+ setautogain(gspca_dev);
+ setexposure(gspca_dev);
+ setgain(gspca_dev);
+ usb_exchange(gspca_dev, poxxxx_init_start_3);
if (mode)
init = poxxxx_initQVGA;
else
break;
}
msleep(100);
- setsharpness(gspca_dev);
sethvflip(gspca_dev);
setlightfreq(gspca_dev);
}
reg_w(gspca_dev, 0xa0, 0x0000, 0xbfff);
break;
case SENSOR_POxxxx:
- setcolors(gspca_dev);
- setbrightness(gspca_dev);
- setcontrast(gspca_dev);
-
- /* led on */
- msleep(80);
- reg_w(gspca_dev, 0x89, 0xffff, 0xfdff);
usb_exchange(gspca_dev, poxxxx_init_end_2);
+ setwb(gspca_dev);
+ msleep(80); /* led on */
+ reg_w(gspca_dev, 0x89, 0xffff, 0xfdff);
break;
}
return gspca_dev->usb_err;
return 0;
}
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->gain = val;
+ if (gspca_dev->streaming)
+ setgain(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->gain;
+ return 0;
+}
+
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->exposure = val;
+ if (gspca_dev->streaming)
+ setexposure(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->exposure;
+ return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->autogain = val;
+ if (gspca_dev->streaming)
+ setautogain(gspca_dev);
+
+ return gspca_dev->usb_err;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->autogain;
+ return 0;
+}
+
+static int sd_setbacklight(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->backlight = val;
+ if (gspca_dev->streaming)
+ setbacklight(gspca_dev);
+
+ return gspca_dev->usb_err;
+}
+
+static int sd_getbacklight(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->backlight;
+ return 0;
+}
+
static int sd_querymenu(struct gspca_dev *gspca_dev,
struct v4l2_querymenu *menu)
{
#define MODULE_NAME "zc3xx"
+#ifdef CONFIG_INPUT
#include <linux/input.h>
+#endif
#include "gspca.h"
#include "jpeg.h"
#define QUALITY_MAX 80
#define QUALITY_DEF 70
+ u8 bridge;
u8 sensor; /* Type of image sensor chip */
-/* !! values used in different tables */
-#define SENSOR_ADCM2700 0
-#define SENSOR_CS2102 1
-#define SENSOR_CS2102K 2
-#define SENSOR_GC0305 3
-#define SENSOR_HDCS2020b 4
-#define SENSOR_HV7131B 5
-#define SENSOR_HV7131C 6
-#define SENSOR_ICM105A 7
-#define SENSOR_MC501CB 8
-#define SENSOR_MI0360SOC 9
-#define SENSOR_OV7620 10
-/*#define SENSOR_OV7648 10 - same values */
-#define SENSOR_OV7630C 11
-#define SENSOR_PAS106 12
-#define SENSOR_PAS202B 13
-#define SENSOR_PB0330 14 /* (MI0360) */
-#define SENSOR_PO2030 15
-#define SENSOR_TAS5130CK 16
-#define SENSOR_TAS5130CXX 17
-#define SENSOR_TAS5130C_VF0250 18
-#define SENSOR_MAX 19
- unsigned short chip_revision;
+ u16 chip_revision;
u8 jpeg_hdr[JPEG_HDR_SZ];
};
+enum bridges {
+ BRIDGE_ZC301,
+ BRIDGE_ZC303,
+};
+enum sensors {
+ SENSOR_ADCM2700,
+ SENSOR_CS2102,
+ SENSOR_CS2102K,
+ SENSOR_GC0305,
+ SENSOR_HDCS2020b,
+ SENSOR_HV7131B,
+ SENSOR_HV7131R,
+ SENSOR_ICM105A,
+ SENSOR_MC501CB,
+ SENSOR_MT9V111_1, /* (mi360soc) zc301 */
+ SENSOR_MT9V111_3, /* (mi360soc) zc303 */
+ SENSOR_OV7620, /* OV7648 - same values */
+ SENSOR_OV7630C,
+ SENSOR_PAS106,
+ SENSOR_PAS202B,
+ SENSOR_PB0330,
+ SENSOR_PO2030,
+ SENSOR_TAS5130C,
+ SENSOR_TAS5130C_VF0250,
+ SENSOR_MAX
+};
/* V4L2 controls supported by the driver */
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
{}
};
+/* from lPEPI264v.inf (hv7131b!) */
static const struct usb_action hv7131r_InitialScale[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
{0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
{0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
{0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
{0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
{0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
{0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xdd, 0x00, 0x0200},
{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xaa, 0x01, 0x000c},
{0xaa, 0x11, 0x0000},
{0xaa, 0x15, 0x00e8},
{0xaa, 0x16, 0x0002},
{0xaa, 0x17, 0x0088},
-
+ {0xaa, 0x30, 0x000b},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x89, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET},
{0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
{0xa0, 0x00, 0x01ad},
{0xa0, 0xc0, 0x019b},
{0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa1, 0x01, 0x0002},
- {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
- {0xa1, 0x01, 0x0091},
- {0xa1, 0x01, 0x0095},
- {0xa1, 0x01, 0x0096},
-
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
-
- {0xa0, 0x60, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf0, ZC3XX_R10B_RGB01},
- {0xa0, 0xf0, ZC3XX_R10C_RGB02},
- {0xa0, 0xf0, ZC3XX_R10D_RGB10},
- {0xa0, 0x60, ZC3XX_R10E_RGB11},
- {0xa0, 0xf0, ZC3XX_R10F_RGB12},
- {0xa0, 0xf0, ZC3XX_R110_RGB20},
- {0xa0, 0xf0, ZC3XX_R111_RGB21},
- {0xa0, 0x60, ZC3XX_R112_RGB22},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x25, 0x0007},
- {0xaa, 0x26, 0x0053},
- {0xaa, 0x27, 0x0000},
-
- {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 2f */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 9b */
- {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 80 */
- {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x13, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa1, 0x01, 0x001d},
- {0xa1, 0x01, 0x001e},
- {0xa1, 0x01, 0x001f},
- {0xa1, 0x01, 0x0020},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-
static const struct usb_action hv7131r_Initial[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
-
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* diff */
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},
{0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
{0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
-
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
-
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
{0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
{0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 1e0 */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
{0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
{0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
{0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
- {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
{0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
{0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
{0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xdd, 0x00, 0x0200},
{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xaa, 0x01, 0x000c},
{0xaa, 0x11, 0x0000},
{0xaa, 0x13, 0x0000},
{0xaa, 0x14, 0x0001},
- {0xaa, 0x15, 0x00e8},
+ {0xaa, 0x15, 0x00e6},
{0xaa, 0x16, 0x0002},
- {0xaa, 0x17, 0x0088},
-
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00 */
-
+ {0xaa, 0x17, 0x0086},
+ {0xaa, 0x30, 0x000b},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x89, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET},
{0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
{0xa0, 0x00, 0x01ad},
{0xa0, 0xc0, 0x019b},
{0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa1, 0x01, 0x0002},
- {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
- /* read the i2c chips ident */
- {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
- {0xa1, 0x01, 0x0091},
- {0xa1, 0x01, 0x0095},
- {0xa1, 0x01, 0x0096},
-
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
-
- {0xa0, 0x60, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf0, ZC3XX_R10B_RGB01},
- {0xa0, 0xf0, ZC3XX_R10C_RGB02},
- {0xa0, 0xf0, ZC3XX_R10D_RGB10},
- {0xa0, 0x60, ZC3XX_R10E_RGB11},
- {0xa0, 0xf0, ZC3XX_R10F_RGB12},
- {0xa0, 0xf0, ZC3XX_R110_RGB20},
- {0xa0, 0xf0, ZC3XX_R111_RGB21},
- {0xa0, 0x60, ZC3XX_R112_RGB22},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action hv7131r_50HZ[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x25, 0x0007},
- {0xaa, 0x26, 0x0053},
- {0xaa, 0x27, 0x0000},
-
- {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 2f */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 9b */
- {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 80 */
-
+ {0xa0, 0x06, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x68, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action hv7131r_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x0c, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xd1, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x40, ZC3XX_R192_EXPOSURELIMITLOW},
{0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
{0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID},
{0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW},
-
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
{0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
{0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x13, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa1, 0x01, 0x001d},
- {0xa1, 0x01, 0x001e},
- {0xa1, 0x01, 0x001f},
- {0xa1, 0x01, 0x0020},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action hv7131r_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x06, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x1a, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action hv7131r_60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x0c, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action hv7131r_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action hv7131r_NoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x04, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0xb0, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
{}
};
{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
{0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,01,cc */
/* {0xa0, 0x44, ZC3XX_R002_CLOCKSELECT}, * 00,02,44,cc
- - if mode1 (320x240) */
+ * if mode1 (320x240) */
/* ?? was
{0xa0, 0x00, 0x0039}, * 00,00,00,dd *
{0xa1, 0x01, 0x0037}, */
{0xa0, 0xf8, ZC3XX_R110_RGB20},
{0xa0, 0xf8, ZC3XX_R111_RGB21},
{0xa0, 0x50, ZC3XX_R112_RGB22},
-/* 0x03, */
{0xa1, 0x01, 0x0008},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
{0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
{0xaa, 0x0e, 0x0002},
{0xaa, 0x14, 0x0081},
-/* Other registors */
+/* Other registers */
{0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
/* Frame retreiving */
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
/* Sharpness */
{0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
{0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
-/* Other registors */
+/* Other registers */
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
/* Auto exposure and white balance */
{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
{0xaa, 0x0e, 0x0002},
{0xaa, 0x14, 0x0081},
-/* Other registors */
+/* Other registers */
{0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
/* Frame retreiving */
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
/* Sharpness */
{0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
{0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
-/* Other registors */
+/* Other registers */
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
/* Auto exposure and white balance */
{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
{}
};
-/* mi0360soc and pb0330 from vm30x.inf for 0ac8:301b and 0ac8:303b 07/02/13 */
-static const struct usb_action mi0360soc_Initial[] = { /* 640x480 */
+/* mt9v111 (mi0360soc) and pb0330 from vm30x.inf 0ac8:301b 07/02/13 */
+static const struct usb_action mt9v111_1_Initial[] = { /* 640x480 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
{0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
{0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
{0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, /*jfm: was 03*/
-/* {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, */
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
{0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
{0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
{0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
{0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
{0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xaa, 0x01, 0x0001},
{0xaa, 0x06, 0x0000},
{0xaa, 0x08, 0x0483},
{0xaa, 0x03, 0x01e5}, /*jfm: was 01e7*/
{0xaa, 0x04, 0x0285}, /*jfm: was 0287*/
{0xaa, 0x07, 0x3002},
- {0xaa, 0x20, 0x5100}, /*jfm: was 1100*/
- {0xaa, 0x35, 0x507f}, /*jfm: was 0050*/
+ {0xaa, 0x20, 0x5100},
+ {0xaa, 0x35, 0x507f},
{0xaa, 0x30, 0x0005},
{0xaa, 0x31, 0x0000},
{0xaa, 0x58, 0x0078},
{0xaa, 0x62, 0x0411},
- {0xaa, 0x2b, 0x0028},
+ {0xaa, 0x2b, 0x007f},
{0xaa, 0x2c, 0x007f}, /*jfm: was 0030*/
{0xaa, 0x2d, 0x007f}, /*jfm: was 0030*/
{0xaa, 0x2e, 0x007f}, /*jfm: was 0030*/
{0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /*jfm: was 37*/
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
{0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, /* jfm: was 78 */
+ {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
{0xa0, 0x61, ZC3XX_R116_RGAIN},
{0xa0, 0x65, ZC3XX_R118_BGAIN},
{}
};
-static const struct usb_action mi0360soc_InitialScale[] = { /* 320x240 */
+static const struct usb_action mt9v111_1_InitialScale[] = { /* 320x240 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
{0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
{0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
{0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, /*jfm: was 03*/
-/* {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, */
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
{0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
{0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
{0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
{0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
{0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xaa, 0x01, 0x0001},
{0xaa, 0x06, 0x0000},
{0xaa, 0x08, 0x0483},
{0xaa, 0x03, 0x01e7},
{0xaa, 0x04, 0x0287},
{0xaa, 0x07, 0x3002},
- {0xaa, 0x20, 0x5100}, /*jfm: was 1100*/
+ {0xaa, 0x20, 0x5100},
{0xaa, 0x35, 0x007f}, /*jfm: was 0050*/
{0xaa, 0x30, 0x0005},
{0xaa, 0x31, 0x0000},
{0xaa, 0x2d, 0x007f}, /*jfm: was 30*/
{0xaa, 0x2e, 0x007f}, /*jfm: was 28*/
{0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /*jfm: was 37*/
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
{0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, /*jfm: was 78*/
+ {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
{0xa0, 0x61, ZC3XX_R116_RGAIN},
{0xa0, 0x65, ZC3XX_R118_BGAIN},
{}
};
-static const struct usb_action mi360soc_AE50HZ[] = {
+static const struct usb_action mt9v111_1_AE50HZ[] = {
{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xbb, 0x00, 0x0562},
{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-static const struct usb_action mi360soc_AE50HZScale[] = {
+static const struct usb_action mt9v111_1_AE50HZScale[] = {
{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xbb, 0x00, 0x0509},
{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-static const struct usb_action mi360soc_AE60HZ[] = {
+static const struct usb_action mt9v111_1_AE60HZ[] = {
{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xbb, 0x00, 0x053d},
- {0xbb, 0x01, 0x096e},
+ {0xaa, 0x05, 0x003d},
+ {0xaa, 0x09, 0x016e},
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
{0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW},
{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-static const struct usb_action mi360soc_AE60HZScale[] = {
+static const struct usb_action mt9v111_1_AE60HZScale[] = {
{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xbb, 0x00, 0x0509},
{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-static const struct usb_action mi360soc_AENoFliker[] = {
+static const struct usb_action mt9v111_1_AENoFliker[] = {
{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xbb, 0x00, 0x0509},
{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-static const struct usb_action mi360soc_AENoFlikerScale[] = {
+static const struct usb_action mt9v111_1_AENoFlikerScale[] = {
{0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
{0xbb, 0x00, 0x0534},
{0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-
-static const struct usb_action pb0330_Initial[] = { /* 640x480 */
+/* from usbvm303.inf 0ac8:303b 07/03/25 (3 - tas5130c) */
+static const struct usb_action mt9v111_3_Initial[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
{0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
{0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
{0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
{0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
{0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
{0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
{0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
{0xdd, 0x00, 0x0200},
{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xaa, 0x01, 0x0006},
- {0xaa, 0x02, 0x0011},
- {0xaa, 0x03, 0x01e5}, /*jfm: was 1e7*/
- {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/
- {0xaa, 0x06, 0x0003},
- {0xaa, 0x07, 0x3002},
- {0xaa, 0x20, 0x1100},
- {0xaa, 0x2f, 0xf7b0},
+ {0xaa, 0x01, 0x0001}, /* select IFP/SOC registers */
+ {0xaa, 0x06, 0x0000}, /* operating mode control */
+ {0xaa, 0x08, 0x0483}, /* output format control */
+ /* H red first, V red or blue first,
+ * raw Bayer, auto flicker */
+ {0xaa, 0x01, 0x0004}, /* select sensor core registers */
+ {0xaa, 0x08, 0x0006}, /* row start */
+ {0xaa, 0x02, 0x0011}, /* column start */
+ {0xaa, 0x03, 0x01e5}, /* window height - 1 */
+ {0xaa, 0x04, 0x0285}, /* window width - 1 */
+ {0xaa, 0x07, 0x3002}, /* output control */
+ {0xaa, 0x20, 0x1100}, /* read mode: bits 8 & 12 (?) */
+ {0xaa, 0x35, 0x007f}, /* global gain */
{0xaa, 0x30, 0x0005},
{0xaa, 0x31, 0x0000},
- {0xaa, 0x34, 0x0100},
- {0xaa, 0x35, 0x0060},
- {0xaa, 0x3d, 0x068f},
- {0xaa, 0x40, 0x01e0},
{0xaa, 0x58, 0x0078},
{0xaa, 0x62, 0x0411},
+ {0xaa, 0x2b, 0x007f}, /* green1 gain */
+ {0xaa, 0x2c, 0x007f}, /* blue gain */
+ {0xaa, 0x2d, 0x007f}, /* red gain */
+ {0xaa, 0x2e, 0x007f}, /* green2 gain */
{0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
{0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x09, 0x01ad}, /*jfm: was 00 */
- {0xa0, 0x15, 0x01ae},
+ {0xa0, 0x00, 0x01ad},
{0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
{0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
{0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/
+ {0xa0, 0x80, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x61, ZC3XX_R116_RGAIN},
+ {0xa0, 0x65, ZC3XX_R118_BGAIN},
{}
};
-static const struct usb_action pb0330_InitialScale[] = { /* 320x240 */
+static const struct usb_action mt9v111_3_InitialScale[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
{0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
{0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
{0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
{0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
{0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
{0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
{0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
{0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
{0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
{0xdd, 0x00, 0x0200},
{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xaa, 0x01, 0x0006},
+ {0xaa, 0x01, 0x0001},
+ {0xaa, 0x06, 0x0000},
+ {0xaa, 0x08, 0x0483},
+ {0xaa, 0x01, 0x0004},
+ {0xaa, 0x08, 0x0006},
{0xaa, 0x02, 0x0011},
{0xaa, 0x03, 0x01e7},
{0xaa, 0x04, 0x0287},
- {0xaa, 0x06, 0x0003},
{0xaa, 0x07, 0x3002},
{0xaa, 0x20, 0x1100},
- {0xaa, 0x2f, 0xf7b0},
+ {0xaa, 0x35, 0x007f},
{0xaa, 0x30, 0x0005},
{0xaa, 0x31, 0x0000},
- {0xaa, 0x34, 0x0100},
- {0xaa, 0x35, 0x0060},
- {0xaa, 0x3d, 0x068f},
- {0xaa, 0x40, 0x01e0},
{0xaa, 0x58, 0x0078},
{0xaa, 0x62, 0x0411},
+ {0xaa, 0x2b, 0x007f},
+ {0xaa, 0x2c, 0x007f},
+ {0xaa, 0x2d, 0x007f},
+ {0xaa, 0x2e, 0x007f},
{0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
{0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
{0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
{0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
{0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x15, 0x01ae},
+ {0xa0, 0x00, 0x01ad},
{0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
{0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
{0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
{0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
{0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/
+ {0xa0, 0x80, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x61, ZC3XX_R116_RGAIN},
+ {0xa0, 0x65, ZC3XX_R118_BGAIN},
{}
};
-static const struct usb_action pb0330_50HZ[] = {
+static const struct usb_action mt9v111_3_AE50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xbb, 0x00, 0x055c},
- {0xbb, 0x01, 0x09aa},
- {0xbb, 0x00, 0x1001},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xaa, 0x05, 0x0009}, /* horizontal blanking */
+ {0xaa, 0x09, 0x01ce}, /* shutter width */
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xc4, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
{0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
{0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x5c, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
{0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-static const struct usb_action pb0330_50HZScale[] = {
+static const struct usb_action mt9v111_3_AE50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xbb, 0x00, 0x0566},
- {0xbb, 0x02, 0x09b2},
- {0xbb, 0x00, 0x1002},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xaa, 0x05, 0x0009},
+ {0xaa, 0x09, 0x01ce},
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x8c, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
{0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
{0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
{0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
{0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-static const struct usb_action pb0330_60HZ[] = {
+static const struct usb_action mt9v111_3_AE60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xbb, 0x00, 0x0535},
- {0xbb, 0x01, 0x0974},
- {0xbb, 0x00, 0x1001},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xaa, 0x05, 0x0009},
+ {0xaa, 0x09, 0x0083},
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW},
{0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
{0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x50, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xd0, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
{}
};
-static const struct usb_action pb0330_60HZScale[] = {
+static const struct usb_action mt9v111_3_AE60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xbb, 0x00, 0x0535},
- {0xbb, 0x02, 0x096c},
- {0xbb, 0x00, 0x1002},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xaa, 0x05, 0x0009},
+ {0xaa, 0x09, 0x0083},
{0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
{0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_3_AENoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x0034},
+ {0xaa, 0x09, 0x0260},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_3_AENoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x0034},
+ {0xaa, 0x09, 0x0260},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+
+static const struct usb_action pb0330_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x0006},
+ {0xaa, 0x02, 0x0011},
+ {0xaa, 0x03, 0x01e5}, /*jfm: was 1e7*/
+ {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/
+ {0xaa, 0x06, 0x0003},
+ {0xaa, 0x07, 0x3002},
+ {0xaa, 0x20, 0x1100},
+ {0xaa, 0x2f, 0xf7b0},
+ {0xaa, 0x30, 0x0005},
+ {0xaa, 0x31, 0x0000},
+ {0xaa, 0x34, 0x0100},
+ {0xaa, 0x35, 0x0060},
+ {0xaa, 0x3d, 0x068f},
+ {0xaa, 0x40, 0x01e0},
+ {0xaa, 0x58, 0x0078},
+ {0xaa, 0x62, 0x0411},
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x09, 0x01ad}, /*jfm: was 00 */
+ {0xa0, 0x15, 0x01ae},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/
+ {}
+};
+static const struct usb_action pb0330_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x0006},
+ {0xaa, 0x02, 0x0011},
+ {0xaa, 0x03, 0x01e7},
+ {0xaa, 0x04, 0x0287},
+ {0xaa, 0x06, 0x0003},
+ {0xaa, 0x07, 0x3002},
+ {0xaa, 0x20, 0x1100},
+ {0xaa, 0x2f, 0xf7b0},
+ {0xaa, 0x30, 0x0005},
+ {0xaa, 0x31, 0x0000},
+ {0xaa, 0x34, 0x0100},
+ {0xaa, 0x35, 0x0060},
+ {0xaa, 0x3d, 0x068f},
+ {0xaa, 0x40, 0x01e0},
+ {0xaa, 0x58, 0x0078},
+ {0xaa, 0x62, 0x0411},
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x09, 0x01ad},
+ {0xa0, 0x15, 0x01ae},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/
+ {}
+};
+static const struct usb_action pb0330_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x055c},
+ {0xbb, 0x01, 0x09aa},
+ {0xbb, 0x00, 0x1001},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xc4, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x5c, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action pb0330_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0566},
+ {0xbb, 0x02, 0x09b2},
+ {0xbb, 0x00, 0x1002},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x8c, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action pb0330_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0535},
+ {0xbb, 0x01, 0x0974},
+ {0xbb, 0x00, 0x1001},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x50, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xd0, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action pb0330_60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0535},
+ {0xbb, 0x02, 0x096c},
+ {0xbb, 0x00, 0x1002},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW},
{0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
{0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
{0xa0, 0x7c, ZC3XX_R197_ANTIFLICKERLOW},
{0xaa, 0x58, 0x0002}, /* 00,58,02,aa */
{0xaa, 0x66, 0x00c0}, /* 00,66,c0,aa */
{0xaa, 0x67, 0x0044}, /* 00,67,44,aa */
- {0xaa, 0x6b, 0x00a0}, /* 00,6b,a0,aa */
- {0xaa, 0x6c, 0x0054}, /* 00,6c,54,aa */
- {0xaa, 0xd6, 0x0007}, /* 00,d6,07,aa */
- {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,f7,cc */
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
- {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
- {0xa0, 0x7a, ZC3XX_R116_RGAIN}, /* 01,16,7a,cc */
- {0xa0, 0x4a, ZC3XX_R118_BGAIN}, /* 01,18,4a,cc */
- {}
-};
-
-static const struct usb_action po2030_50HZ[] = {
- {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
- {0xaa, 0x1a, 0x0001}, /* 00,1a,01,aa */
- {0xaa, 0x1b, 0x000a}, /* 00,1b,0a,aa */
- {0xaa, 0x1c, 0x00b0}, /* 00,1c,b0,aa */
- {0xa0, 0x05, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,05,cc */
- {0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,35,cc */
- {0xa0, 0x70, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,70,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x85, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,85,cc */
- {0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,58,cc */
- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
- {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,22,cc */
- {0xa0, 0x88, ZC3XX_R18D_YTARGET}, /* 01,8d,88,cc */
- {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc */
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
- {}
-};
-
-static const struct usb_action po2030_60HZ[] = {
- {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
- {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
- {0xaa, 0x1b, 0x00de}, /* 00,1b,de,aa */
- {0xaa, 0x1c, 0x0040}, /* 00,1c,40,aa */
- {0xa0, 0x08, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,08,cc */
- {0xa0, 0xae, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,ae,cc */
- {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x6f, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,6f,cc */
- {0xa0, 0x20, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,20,cc */
- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
- {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,22,cc */
- {0xa0, 0x88, ZC3XX_R18D_YTARGET}, /* 01,8d,88,cc */
- /* win: 01,8d,80 */
- {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc */
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
- {}
-};
-
-static const struct usb_action po2030_NoFliker[] = {
- {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
- {0xaa, 0x8d, 0x000d}, /* 00,8d,0d,aa */
- {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
- {0xaa, 0x1b, 0x0002}, /* 00,1b,02,aa */
- {0xaa, 0x1c, 0x0078}, /* 00,1c,78,aa */
- {0xaa, 0x46, 0x0000}, /* 00,46,00,aa */
- {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */
- {}
-};
-
-/* TEST */
-static const struct usb_action tas5130cK_InitialScale[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x01, 0x003b},
- {0xa0, 0x0e, 0x003a},
- {0xa0, 0x01, 0x0038},
- {0xa0, 0x0b, 0x0039},
- {0xa0, 0x00, 0x0038},
- {0xa0, 0x0b, 0x0039},
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x83, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x06, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x02, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0xE7, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x04, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x87, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x02, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x07, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x30, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x51, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x35, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7F, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x30, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x05, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x31, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x58, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x78, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x62, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x2B, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x2c, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x2D, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x2e, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
- {0xa0, 0x61, ZC3XX_R116_RGAIN},
- {0xa0, 0x65, ZC3XX_R118_BGAIN},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x15, 0x01ae},
- {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf1, ZC3XX_R10B_RGB01},
- {0xa0, 0x03, ZC3XX_R10C_RGB02},
- {0xa0, 0xfe, ZC3XX_R10D_RGB10},
- {0xa0, 0x51, ZC3XX_R10E_RGB11},
- {0xa0, 0xf1, ZC3XX_R10F_RGB12},
- {0xa0, 0xec, ZC3XX_R110_RGB20},
- {0xa0, 0x03, ZC3XX_R111_RGB21},
- {0xa0, 0x51, ZC3XX_R112_RGB22},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
- {0xa0, 0x38, ZC3XX_R120_GAMMA00}, /* gamma > 5 */
- {0xa0, 0x51, ZC3XX_R121_GAMMA01},
- {0xa0, 0x6e, ZC3XX_R122_GAMMA02},
- {0xa0, 0x8c, ZC3XX_R123_GAMMA03},
- {0xa0, 0xa2, ZC3XX_R124_GAMMA04},
- {0xa0, 0xb6, ZC3XX_R125_GAMMA05},
- {0xa0, 0xc8, ZC3XX_R126_GAMMA06},
- {0xa0, 0xd6, ZC3XX_R127_GAMMA07},
- {0xa0, 0xe2, ZC3XX_R128_GAMMA08},
- {0xa0, 0xed, ZC3XX_R129_GAMMA09},
- {0xa0, 0xf5, ZC3XX_R12A_GAMMA0A},
- {0xa0, 0xfc, ZC3XX_R12B_GAMMA0B},
- {0xa0, 0xff, ZC3XX_R12C_GAMMA0C},
- {0xa0, 0xff, ZC3XX_R12D_GAMMA0D},
- {0xa0, 0xff, ZC3XX_R12E_GAMMA0E},
- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
- {0xa0, 0x12, ZC3XX_R130_GAMMA10},
- {0xa0, 0x1b, ZC3XX_R131_GAMMA11},
- {0xa0, 0x1d, ZC3XX_R132_GAMMA12},
- {0xa0, 0x1a, ZC3XX_R133_GAMMA13},
- {0xa0, 0x15, ZC3XX_R134_GAMMA14},
- {0xa0, 0x12, ZC3XX_R135_GAMMA15},
- {0xa0, 0x0f, ZC3XX_R136_GAMMA16},
- {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
- {0xa0, 0x05, ZC3XX_R13B_GAMMA1B},
- {0xa0, 0x00, ZC3XX_R13C_GAMMA1C},
- {0xa0, 0x00, ZC3XX_R13D_GAMMA1D},
- {0xa0, 0x00, ZC3XX_R13E_GAMMA1E},
- {0xa0, 0x01, ZC3XX_R13F_GAMMA1F},
- {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf1, ZC3XX_R10B_RGB01},
- {0xa0, 0x03, ZC3XX_R10C_RGB02},
- {0xa0, 0xfe, ZC3XX_R10D_RGB10},
- {0xa0, 0x51, ZC3XX_R10E_RGB11},
- {0xa0, 0xf1, ZC3XX_R10F_RGB12},
- {0xa0, 0xec, ZC3XX_R110_RGB20},
- {0xa0, 0x03, ZC3XX_R111_RGB21},
- {0xa0, 0x51, ZC3XX_R112_RGB22},
- {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x09, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x34, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x15, 0x01ae},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xaa, 0x6b, 0x00a0}, /* 00,6b,a0,aa */
+ {0xaa, 0x6c, 0x0054}, /* 00,6c,54,aa */
+ {0xaa, 0xd6, 0x0007}, /* 00,d6,07,aa */
+ {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,f7,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x7a, ZC3XX_R116_RGAIN}, /* 01,16,7a,cc */
+ {0xa0, 0x4a, ZC3XX_R118_BGAIN}, /* 01,18,4a,cc */
{}
};
-static const struct usb_action tas5130cK_Initial[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x01, 0x003b},
- {0xa0, 0x0e, 0x003a},
- {0xa0, 0x01, 0x0038},
- {0xa0, 0x0b, 0x0039},
- {0xa0, 0x00, 0x0038},
- {0xa0, 0x0b, 0x0039},
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x83, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x06, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x02, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0xe5, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x04, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x85, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x02, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x07, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x30, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x51, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x35, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7F, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x50, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x30, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x05, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x31, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x58, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x78, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x62, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x2B, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x2C, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7F, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x2D, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x2e, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
- {0xa0, 0x61, ZC3XX_R116_RGAIN},
- {0xa0, 0x65, ZC3XX_R118_BGAIN},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x15, 0x01ae},
- {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf1, ZC3XX_R10B_RGB01},
- {0xa0, 0x03, ZC3XX_R10C_RGB02},
- {0xa0, 0xfe, ZC3XX_R10D_RGB10},
- {0xa0, 0x51, ZC3XX_R10E_RGB11},
- {0xa0, 0xf1, ZC3XX_R10F_RGB12},
- {0xa0, 0xec, ZC3XX_R110_RGB20},
- {0xa0, 0x03, ZC3XX_R111_RGB21},
- {0xa0, 0x51, ZC3XX_R112_RGB22},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
- {0xa0, 0x38, ZC3XX_R120_GAMMA00}, /* gamma > 5 */
- {0xa0, 0x51, ZC3XX_R121_GAMMA01},
- {0xa0, 0x6e, ZC3XX_R122_GAMMA02},
- {0xa0, 0x8c, ZC3XX_R123_GAMMA03},
- {0xa0, 0xa2, ZC3XX_R124_GAMMA04},
- {0xa0, 0xb6, ZC3XX_R125_GAMMA05},
- {0xa0, 0xc8, ZC3XX_R126_GAMMA06},
- {0xa0, 0xd6, ZC3XX_R127_GAMMA07},
- {0xa0, 0xe2, ZC3XX_R128_GAMMA08},
- {0xa0, 0xed, ZC3XX_R129_GAMMA09},
- {0xa0, 0xf5, ZC3XX_R12A_GAMMA0A},
- {0xa0, 0xfc, ZC3XX_R12B_GAMMA0B},
- {0xa0, 0xff, ZC3XX_R12C_GAMMA0C},
- {0xa0, 0xff, ZC3XX_R12D_GAMMA0D},
- {0xa0, 0xff, ZC3XX_R12E_GAMMA0E},
- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
- {0xa0, 0x12, ZC3XX_R130_GAMMA10},
- {0xa0, 0x1b, ZC3XX_R131_GAMMA11},
- {0xa0, 0x1d, ZC3XX_R132_GAMMA12},
- {0xa0, 0x1a, ZC3XX_R133_GAMMA13},
- {0xa0, 0x15, ZC3XX_R134_GAMMA14},
- {0xa0, 0x12, ZC3XX_R135_GAMMA15},
- {0xa0, 0x0f, ZC3XX_R136_GAMMA16},
- {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
- {0xa0, 0x05, ZC3XX_R13B_GAMMA1B},
- {0xa0, 0x00, ZC3XX_R13C_GAMMA1C},
- {0xa0, 0x00, ZC3XX_R13D_GAMMA1D},
- {0xa0, 0x00, ZC3XX_R13E_GAMMA1E},
- {0xa0, 0x01, ZC3XX_R13F_GAMMA1F},
- {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf1, ZC3XX_R10B_RGB01},
- {0xa0, 0x03, ZC3XX_R10C_RGB02},
- {0xa0, 0xfe, ZC3XX_R10D_RGB10},
- {0xa0, 0x51, ZC3XX_R10E_RGB11},
- {0xa0, 0xf1, ZC3XX_R10F_RGB12},
- {0xa0, 0xec, ZC3XX_R110_RGB20},
- {0xa0, 0x03, ZC3XX_R111_RGB21},
- {0xa0, 0x51, ZC3XX_R112_RGB22},
- {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x62, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0xaa, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x9b, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x15, 0x01ae},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x30, 0x0007},
- {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x00, 0x0007},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+static const struct usb_action po2030_50HZ[] = {
+ {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
+ {0xaa, 0x1a, 0x0001}, /* 00,1a,01,aa */
+ {0xaa, 0x1b, 0x000a}, /* 00,1b,0a,aa */
+ {0xaa, 0x1c, 0x00b0}, /* 00,1c,b0,aa */
+ {0xa0, 0x05, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,05,cc */
+ {0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,35,cc */
+ {0xa0, 0x70, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,70,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x85, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,85,cc */
+ {0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,58,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+ {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,22,cc */
+ {0xa0, 0x88, ZC3XX_R18D_YTARGET}, /* 01,8d,88,cc */
+ {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
+ {}
+};
+
+static const struct usb_action po2030_60HZ[] = {
+ {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
+ {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
+ {0xaa, 0x1b, 0x00de}, /* 00,1b,de,aa */
+ {0xaa, 0x1c, 0x0040}, /* 00,1c,40,aa */
+ {0xa0, 0x08, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,08,cc */
+ {0xa0, 0xae, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,ae,cc */
+ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x6f, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,6f,cc */
+ {0xa0, 0x20, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,20,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+ {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,22,cc */
+ {0xa0, 0x88, ZC3XX_R18D_YTARGET}, /* 01,8d,88,cc */
+ /* win: 01,8d,80 */
+ {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
+ {}
+};
+
+static const struct usb_action po2030_NoFliker[] = {
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+ {0xaa, 0x8d, 0x000d}, /* 00,8d,0d,aa */
+ {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
+ {0xaa, 0x1b, 0x0002}, /* 00,1b,02,aa */
+ {0xaa, 0x1c, 0x0078}, /* 00,1c,78,aa */
+ {0xaa, 0x46, 0x0000}, /* 00,46,00,aa */
+ {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */
{}
};
-static const struct usb_action tas5130cxx_InitialScale[] = { /* 320x240 */
+static const struct usb_action tas5130c_InitialScale[] = { /* 320x240 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x50, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
{0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
{}
};
-static const struct usb_action tas5130cxx_Initial[] = { /* 640x480 */
+static const struct usb_action tas5130c_Initial[] = { /* 640x480 */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x40, ZC3XX_R002_CLOCKSELECT},
{0xa0, 0x00, ZC3XX_R008_CLOCKSETTING},
{0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
{}
};
-static const struct usb_action tas5130cxx_50HZ[] = {
+static const struct usb_action tas5130c_50HZ[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
{0xaa, 0xa4, 0x0063}, /* 00,a4,63,aa */
{0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
{}
};
-static const struct usb_action tas5130cxx_50HZScale[] = {
+static const struct usb_action tas5130c_50HZScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
{0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */
{0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
{}
};
-static const struct usb_action tas5130cxx_60HZ[] = {
+static const struct usb_action tas5130c_60HZ[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
{0xaa, 0xa4, 0x0036}, /* 00,a4,36,aa */
{0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
{}
};
-static const struct usb_action tas5130cxx_60HZScale[] = {
+static const struct usb_action tas5130c_60HZScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
{0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */
{0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
{}
};
-static const struct usb_action tas5130cxx_NoFliker[] = {
+static const struct usb_action tas5130c_NoFliker[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
{0xaa, 0xa4, 0x0040}, /* 00,a4,40,aa */
{}
};
-static const struct usb_action tas5130cxx_NoFlikerScale[] = {
+static const struct usb_action tas5130c_NoFlikerScale[] = {
{0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
{0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
{0xaa, 0xa4, 0x0090}, /* 00,a4,90,aa */
static u8 reg_r_i(struct gspca_dev *gspca_dev,
u16 index)
{
- usb_control_msg(gspca_dev->dev,
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return 0;
+ ret = usb_control_msg(gspca_dev->dev,
usb_rcvctrlpipe(gspca_dev->dev, 0),
0xa1,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0x01, /* value */
index, gspca_dev->usb_buf, 1,
500);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "reg_r_i err %d", ret);
+ gspca_dev->usb_err = ret;
+ return 0;
+ }
return gspca_dev->usb_buf[0];
}
return ret;
}
-static void reg_w_i(struct usb_device *dev,
+static void reg_w_i(struct gspca_dev *gspca_dev,
u8 value,
u16 index)
{
- usb_control_msg(dev,
- usb_sndctrlpipe(dev, 0),
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
0xa0,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value, index, NULL, 0,
500);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "reg_w_i err %d", ret);
+ gspca_dev->usb_err = ret;
+ }
}
-static void reg_w(struct usb_device *dev,
+static void reg_w(struct gspca_dev *gspca_dev,
u8 value,
u16 index)
{
PDEBUG(D_USBO, "reg w [%04x] = %02x", index, value);
- reg_w_i(dev, value, index);
+ reg_w_i(gspca_dev, value, index);
}
static u16 i2c_read(struct gspca_dev *gspca_dev,
u8 retbyte;
u16 retval;
- reg_w_i(gspca_dev->dev, reg, 0x0092);
- reg_w_i(gspca_dev->dev, 0x02, 0x0090); /* <- read command */
+ if (gspca_dev->usb_err < 0)
+ return 0;
+ reg_w_i(gspca_dev, reg, 0x0092);
+ reg_w_i(gspca_dev, 0x02, 0x0090); /* <- read command */
msleep(20);
retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */
if (retbyte != 0x00)
{
u8 retbyte;
- reg_w_i(gspca_dev->dev, reg, 0x92);
- reg_w_i(gspca_dev->dev, valL, 0x93);
- reg_w_i(gspca_dev->dev, valH, 0x94);
- reg_w_i(gspca_dev->dev, 0x01, 0x90); /* <- write command */
+ if (gspca_dev->usb_err < 0)
+ return 0;
+ reg_w_i(gspca_dev, reg, 0x92);
+ reg_w_i(gspca_dev, valL, 0x93);
+ reg_w_i(gspca_dev, valH, 0x94);
+ reg_w_i(gspca_dev, 0x01, 0x90); /* <- write command */
msleep(1);
retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */
if (retbyte != 0x00)
while (action->req) {
switch (action->req) {
case 0xa0: /* write register */
- reg_w(gspca_dev->dev, action->val, action->idx);
+ reg_w(gspca_dev, action->val, action->idx);
break;
case 0xa1: /* read status */
reg_r(gspca_dev, action->idx);
static const u8 vf0250_matrix[9] =
{0x7b, 0xea, 0xea, 0xea, 0x7b, 0xea, 0xea, 0xea, 0x7b};
static const u8 *matrix_tb[SENSOR_MAX] = {
- adcm2700_matrix, /* SENSOR_ADCM2700 0 */
- ov7620_matrix, /* SENSOR_CS2102 1 */
- NULL, /* SENSOR_CS2102K 2 */
- gc0305_matrix, /* SENSOR_GC0305 3 */
- NULL, /* SENSOR_HDCS2020b 4 */
- NULL, /* SENSOR_HV7131B 5 */
- NULL, /* SENSOR_HV7131C 6 */
- NULL, /* SENSOR_ICM105A 7 */
- NULL, /* SENSOR_MC501CB 8 */
- gc0305_matrix, /* SENSOR_MI0360SOC 9 */
- ov7620_matrix, /* SENSOR_OV7620 10 */
- NULL, /* SENSOR_OV7630C 11 */
- NULL, /* SENSOR_PAS106 12 */
- pas202b_matrix, /* SENSOR_PAS202B 13 */
- gc0305_matrix, /* SENSOR_PB0330 14 */
- po2030_matrix, /* SENSOR_PO2030 15 */
- NULL, /* SENSOR_TAS5130CK 16 */
- tas5130c_matrix, /* SENSOR_TAS5130CXX 17 */
- vf0250_matrix, /* SENSOR_TAS5130C_VF0250 18 */
+ [SENSOR_ADCM2700] = adcm2700_matrix,
+ [SENSOR_CS2102] = ov7620_matrix,
+ [SENSOR_CS2102K] = NULL,
+ [SENSOR_GC0305] = gc0305_matrix,
+ [SENSOR_HDCS2020b] = NULL,
+ [SENSOR_HV7131B] = NULL,
+ [SENSOR_HV7131R] = NULL,
+ [SENSOR_ICM105A] = po2030_matrix,
+ [SENSOR_MC501CB] = NULL,
+ [SENSOR_MT9V111_1] = gc0305_matrix,
+ [SENSOR_MT9V111_3] = gc0305_matrix,
+ [SENSOR_OV7620] = ov7620_matrix,
+ [SENSOR_OV7630C] = NULL,
+ [SENSOR_PAS106] = NULL,
+ [SENSOR_PAS202B] = pas202b_matrix,
+ [SENSOR_PB0330] = gc0305_matrix,
+ [SENSOR_PO2030] = po2030_matrix,
+ [SENSOR_TAS5130C] = tas5130c_matrix,
+ [SENSOR_TAS5130C_VF0250] = vf0250_matrix,
};
matrix = matrix_tb[sd->sensor];
if (matrix == NULL)
return; /* matrix already loaded */
for (i = 0; i < ARRAY_SIZE(ov7620_matrix); i++)
- reg_w(gspca_dev->dev, matrix[i], 0x010a + i);
+ reg_w(gspca_dev, matrix[i], 0x010a + i);
}
static void setsharpness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- struct usb_device *dev = gspca_dev->dev;
int sharpness;
static const u8 sharpness_tb[][2] = {
{0x02, 0x03},
};
sharpness = sd->sharpness;
- reg_w(dev, sharpness_tb[sharpness][0], 0x01c6);
+ reg_w(gspca_dev, sharpness_tb[sharpness][0], 0x01c6);
reg_r(gspca_dev, 0x01c8);
reg_r(gspca_dev, 0x01c9);
reg_r(gspca_dev, 0x01ca);
- reg_w(dev, sharpness_tb[sharpness][1], 0x01cb);
+ reg_w(gspca_dev, sharpness_tb[sharpness][1], 0x01cb);
}
static void setcontrast(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- struct usb_device *dev = gspca_dev->dev;
const u8 *Tgamma;
int g, i, brightness, contrast, adj, gp1, gp2;
u8 gr[16];
g = 0xff;
else if (g < 0)
g = 0;
- reg_w(dev, g, 0x0120 + i); /* gamma */
+ reg_w(gspca_dev, g, 0x0120 + i); /* gamma */
if (contrast > 0)
adj--;
else if (contrast < 0)
}
gr[15] = (0xff - gp2) / 2;
for (i = 0; i < 16; i++)
- reg_w(dev, gr[i], 0x0130 + i); /* gradient */
+ reg_w(gspca_dev, gr[i], 0x0130 + i); /* gradient */
}
static void setquality(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- struct usb_device *dev = gspca_dev->dev;
u8 frxt;
switch (sd->sensor) {
return;
}
/*fixme: is it really 0008 0007 0018 for all other sensors? */
- reg_w(dev, QUANT_VAL, 0x0008);
+ reg_w(gspca_dev, QUANT_VAL, 0x0008);
frxt = 0x30;
- reg_w(dev, frxt, 0x0007);
+ reg_w(gspca_dev, frxt, 0x0007);
#if QUANT_VAL == 0 || QUANT_VAL == 1 || QUANT_VAL == 2
frxt = 0xff;
#elif QUANT_VAL == 3
#else
frxt = 0x20;
#endif
- reg_w(dev, frxt, 0x0018);
+ reg_w(gspca_dev, frxt, 0x0018);
}
/* Matches the sensor's internal frame rate to the lighting frequency.
* 50Hz, for European and Asian lighting (default)
* 60Hz, for American lighting
* 0 = No Fliker (for outdoore usage)
- * Returns: 0 for success
*/
-static int setlightfreq(struct gspca_dev *gspca_dev)
+static void setlightfreq(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int i, mode;
const struct usb_action *zc3_freq;
static const struct usb_action *freq_tb[SENSOR_MAX][6] = {
-/* SENSOR_ADCM2700 0 */
+ [SENSOR_ADCM2700] =
{adcm2700_NoFliker, adcm2700_NoFliker,
adcm2700_50HZ, adcm2700_50HZ,
adcm2700_60HZ, adcm2700_60HZ},
-/* SENSOR_CS2102 1 */
+ [SENSOR_CS2102] =
{cs2102_NoFliker, cs2102_NoFlikerScale,
cs2102_50HZ, cs2102_50HZScale,
cs2102_60HZ, cs2102_60HZScale},
-/* SENSOR_CS2102K 2 */
+ [SENSOR_CS2102K] =
{cs2102_NoFliker, cs2102_NoFlikerScale,
NULL, NULL, /* currently disabled */
NULL, NULL},
-/* SENSOR_GC0305 3 */
+ [SENSOR_GC0305] =
{gc0305_NoFliker, gc0305_NoFliker,
gc0305_50HZ, gc0305_50HZ,
gc0305_60HZ, gc0305_60HZ},
-/* SENSOR_HDCS2020b 4 */
+ [SENSOR_HDCS2020b] =
{hdcs2020b_NoFliker, hdcs2020b_NoFliker,
hdcs2020b_50HZ, hdcs2020b_50HZ,
hdcs2020b_60HZ, hdcs2020b_60HZ},
-/* SENSOR_HV7131B 5 */
+ [SENSOR_HV7131B] =
{hv7131b_NoFliker, hv7131b_NoFlikerScale,
hv7131b_50HZ, hv7131b_50HZScale,
hv7131b_60HZ, hv7131b_60HZScale},
-/* SENSOR_HV7131C 6 */
- {NULL, NULL,
- NULL, NULL,
- NULL, NULL},
-/* SENSOR_ICM105A 7 */
+ [SENSOR_HV7131R] =
+ {hv7131r_NoFliker, hv7131r_NoFlikerScale,
+ hv7131r_50HZ, hv7131r_50HZScale,
+ hv7131r_60HZ, hv7131r_60HZScale},
+ [SENSOR_ICM105A] =
{icm105a_NoFliker, icm105a_NoFlikerScale,
icm105a_50HZ, icm105a_50HZScale,
icm105a_60HZ, icm105a_60HZScale},
-/* SENSOR_MC501CB 8 */
+ [SENSOR_MC501CB] =
{mc501cb_NoFliker, mc501cb_NoFlikerScale,
mc501cb_50HZ, mc501cb_50HZScale,
mc501cb_60HZ, mc501cb_60HZScale},
-/* SENSOR_MI0360SOC 9 */
- {mi360soc_AENoFliker, mi360soc_AENoFlikerScale,
- mi360soc_AE50HZ, mi360soc_AE50HZScale,
- mi360soc_AE60HZ, mi360soc_AE60HZScale},
-/* SENSOR_OV7620 10 */
+ [SENSOR_MT9V111_1] =
+ {mt9v111_1_AENoFliker, mt9v111_1_AENoFlikerScale,
+ mt9v111_1_AE50HZ, mt9v111_1_AE50HZScale,
+ mt9v111_1_AE60HZ, mt9v111_1_AE60HZScale},
+ [SENSOR_MT9V111_3] =
+ {mt9v111_3_AENoFliker, mt9v111_3_AENoFlikerScale,
+ mt9v111_3_AE50HZ, mt9v111_3_AE50HZScale,
+ mt9v111_3_AE60HZ, mt9v111_3_AE60HZScale},
+ [SENSOR_OV7620] =
{ov7620_NoFliker, ov7620_NoFliker,
ov7620_50HZ, ov7620_50HZ,
ov7620_60HZ, ov7620_60HZ},
-/* SENSOR_OV7630C 11 */
+ [SENSOR_OV7630C] =
{NULL, NULL,
NULL, NULL,
NULL, NULL},
-/* SENSOR_PAS106 12 */
+ [SENSOR_PAS106] =
{pas106b_NoFliker, pas106b_NoFliker,
pas106b_50HZ, pas106b_50HZ,
pas106b_60HZ, pas106b_60HZ},
-/* SENSOR_PAS202B 13 */
+ [SENSOR_PAS202B] =
{pas202b_NoFliker, pas202b_NoFlikerScale,
pas202b_50HZ, pas202b_50HZScale,
pas202b_60HZ, pas202b_60HZScale},
-/* SENSOR_PB0330 14 */
+ [SENSOR_PB0330] =
{pb0330_NoFliker, pb0330_NoFlikerScale,
pb0330_50HZ, pb0330_50HZScale,
pb0330_60HZ, pb0330_60HZScale},
-/* SENSOR_PO2030 15 */
+ [SENSOR_PO2030] =
{po2030_NoFliker, po2030_NoFliker,
po2030_50HZ, po2030_50HZ,
po2030_60HZ, po2030_60HZ},
-/* SENSOR_TAS5130CK 16 */
- {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale,
- tas5130cxx_50HZ, tas5130cxx_50HZScale,
- tas5130cxx_60HZ, tas5130cxx_60HZScale},
-/* SENSOR_TAS5130CXX 17 */
- {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale,
- tas5130cxx_50HZ, tas5130cxx_50HZScale,
- tas5130cxx_60HZ, tas5130cxx_60HZScale},
-/* SENSOR_TAS5130C_VF0250 18 */
+ [SENSOR_TAS5130C] =
+ {tas5130c_NoFliker, tas5130c_NoFlikerScale,
+ tas5130c_50HZ, tas5130c_50HZScale,
+ tas5130c_60HZ, tas5130c_60HZScale},
+ [SENSOR_TAS5130C_VF0250] =
{tas5130c_vf0250_NoFliker, tas5130c_vf0250_NoFlikerScale,
tas5130c_vf0250_50HZ, tas5130c_vf0250_50HZScale,
tas5130c_vf0250_60HZ, tas5130c_vf0250_60HZScale},
if (mode)
i++; /* 320x240 */
zc3_freq = freq_tb[sd->sensor][i];
- if (zc3_freq != NULL) {
- usb_exchange(gspca_dev, zc3_freq);
- switch (sd->sensor) {
- case SENSOR_GC0305:
- if (mode /* if 320x240 */
- && sd->lightfreq == 1) /* and 50Hz */
- reg_w(gspca_dev->dev, 0x85, 0x018d);
- /* win: 0x80, 0x018d */
- break;
- case SENSOR_OV7620:
- if (!mode) { /* if 640x480 */
- if (sd->lightfreq != 0) /* and 50 or 60 Hz */
- reg_w(gspca_dev->dev, 0x40, 0x0002);
- else
- reg_w(gspca_dev->dev, 0x44, 0x0002);
- }
- break;
- case SENSOR_PAS202B:
- reg_w(gspca_dev->dev, 0x00, 0x01a7);
- break;
+ if (zc3_freq == NULL)
+ return;
+ usb_exchange(gspca_dev, zc3_freq);
+ switch (sd->sensor) {
+ case SENSOR_GC0305:
+ if (mode /* if 320x240 */
+ && sd->lightfreq == 1) /* and 50Hz */
+ reg_w(gspca_dev, 0x85, 0x018d);
+ /* win: 0x80, 0x018d */
+ break;
+ case SENSOR_OV7620:
+ if (!mode) { /* if 640x480 */
+ if (sd->lightfreq != 0) /* and 50 or 60 Hz */
+ reg_w(gspca_dev, 0x40, 0x0002);
+ else
+ reg_w(gspca_dev, 0x44, 0x0002);
}
+ break;
+ case SENSOR_PAS202B:
+ reg_w(gspca_dev, 0x00, 0x01a7);
+ break;
}
- return 0;
}
static void setautogain(struct gspca_dev *gspca_dev)
autoval = 0x42;
else
autoval = 0x02;
- reg_w(gspca_dev->dev, autoval, 0x0180);
+ reg_w(gspca_dev, autoval, 0x0180);
}
-static void send_unknown(struct usb_device *dev, int sensor)
+static void send_unknown(struct gspca_dev *gspca_dev, int sensor)
{
- reg_w(dev, 0x01, 0x0000); /* led off */
+ reg_w(gspca_dev, 0x01, 0x0000); /* led off */
switch (sensor) {
case SENSOR_PAS106:
- reg_w(dev, 0x03, 0x003a);
- reg_w(dev, 0x0c, 0x003b);
- reg_w(dev, 0x08, 0x0038);
+ reg_w(gspca_dev, 0x03, 0x003a);
+ reg_w(gspca_dev, 0x0c, 0x003b);
+ reg_w(gspca_dev, 0x08, 0x0038);
break;
case SENSOR_ADCM2700:
case SENSOR_GC0305:
case SENSOR_OV7620:
- case SENSOR_MI0360SOC:
+ case SENSOR_MT9V111_1:
+ case SENSOR_MT9V111_3:
case SENSOR_PB0330:
case SENSOR_PO2030:
- reg_w(dev, 0x0d, 0x003a);
- reg_w(dev, 0x02, 0x003b);
- reg_w(dev, 0x00, 0x0038);
+ reg_w(gspca_dev, 0x0d, 0x003a);
+ reg_w(gspca_dev, 0x02, 0x003b);
+ reg_w(gspca_dev, 0x00, 0x0038);
break;
case SENSOR_PAS202B:
- reg_w(dev, 0x03, 0x003b);
- reg_w(dev, 0x0c, 0x003a);
- reg_w(dev, 0x0b, 0x0039);
- reg_w(dev, 0x0b, 0x0038);
+ reg_w(gspca_dev, 0x03, 0x003b);
+ reg_w(gspca_dev, 0x0c, 0x003a);
+ reg_w(gspca_dev, 0x0b, 0x0039);
+ reg_w(gspca_dev, 0x0b, 0x0038);
break;
}
}
/* start probe 2 wires */
-static void start_2wr_probe(struct usb_device *dev, int sensor)
+static void start_2wr_probe(struct gspca_dev *gspca_dev, int sensor)
{
- reg_w(dev, 0x01, 0x0000);
- reg_w(dev, sensor, 0x0010);
- reg_w(dev, 0x01, 0x0001);
- reg_w(dev, 0x03, 0x0012);
- reg_w(dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, sensor, 0x0010);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
/* msleep(2); */
}
{
u16 checkword;
- start_2wr_probe(gspca_dev->dev, 0x0f); /* PAS106 */
- reg_w(gspca_dev->dev, 0x08, 0x008d);
+ start_2wr_probe(gspca_dev, 0x0f); /* PAS106 */
+ reg_w(gspca_dev, 0x08, 0x008d);
msleep(150);
checkword = ((i2c_read(gspca_dev, 0x00) & 0x0f) << 4)
| ((i2c_read(gspca_dev, 0x01) & 0xf0) >> 4);
PDEBUG(D_PROBE, "probe sif 0x%04x", checkword);
if (checkword == 0x0007) {
- send_unknown(gspca_dev->dev, SENSOR_PAS106);
+ send_unknown(gspca_dev, SENSOR_PAS106);
return 0x0f; /* PAS106 */
}
return -1;
static int vga_2wr_probe(struct gspca_dev *gspca_dev)
{
- struct usb_device *dev = gspca_dev->dev;
u16 retword;
- start_2wr_probe(dev, 0x00); /* HV7131B */
+ start_2wr_probe(gspca_dev, 0x00); /* HV7131B */
i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
retword = i2c_read(gspca_dev, 0x01);
if (retword != 0)
return 0x00; /* HV7131B */
- start_2wr_probe(dev, 0x04); /* CS2102 */
+ start_2wr_probe(gspca_dev, 0x04); /* CS2102 */
i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
retword = i2c_read(gspca_dev, 0x01);
if (retword != 0)
return 0x04; /* CS2102 */
- start_2wr_probe(dev, 0x06); /* OmniVision */
- reg_w(dev, 0x08, 0x008d);
+ start_2wr_probe(gspca_dev, 0x06); /* OmniVision */
+ reg_w(gspca_dev, 0x08, 0x008d);
i2c_write(gspca_dev, 0x11, 0xaa, 0x00);
retword = i2c_read(gspca_dev, 0x11);
if (retword != 0) {
goto ov_check;
}
- start_2wr_probe(dev, 0x08); /* HDCS2020 */
+ start_2wr_probe(gspca_dev, 0x08); /* HDCS2020 */
i2c_write(gspca_dev, 0x1c, 0x00, 0x00);
i2c_write(gspca_dev, 0x15, 0xaa, 0x00);
retword = i2c_read(gspca_dev, 0x15);
if (retword != 0)
return 0x08; /* HDCS2020 */
- start_2wr_probe(dev, 0x0a); /* PB0330 */
+ start_2wr_probe(gspca_dev, 0x0a); /* PB0330 */
i2c_write(gspca_dev, 0x07, 0xaa, 0xaa);
retword = i2c_read(gspca_dev, 0x07);
if (retword != 0)
if (retword != 0)
return 0x0a; /* PB0330 ?? */
- start_2wr_probe(dev, 0x0c); /* ICM105A */
+ start_2wr_probe(gspca_dev, 0x0c); /* ICM105A */
i2c_write(gspca_dev, 0x01, 0x11, 0x00);
retword = i2c_read(gspca_dev, 0x01);
if (retword != 0)
return 0x0c; /* ICM105A */
- start_2wr_probe(dev, 0x0e); /* PAS202BCB */
- reg_w(dev, 0x08, 0x008d);
+ start_2wr_probe(gspca_dev, 0x0e); /* PAS202BCB */
+ reg_w(gspca_dev, 0x08, 0x008d);
i2c_write(gspca_dev, 0x03, 0xaa, 0x00);
msleep(50);
retword = i2c_read(gspca_dev, 0x03);
if (retword != 0) {
- send_unknown(dev, SENSOR_PAS202B);
+ send_unknown(gspca_dev, SENSOR_PAS202B);
return 0x0e; /* PAS202BCB */
}
- start_2wr_probe(dev, 0x02); /* TAS5130C */
+ start_2wr_probe(gspca_dev, 0x02); /* TAS5130C */
i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
retword = i2c_read(gspca_dev, 0x01);
if (retword != 0)
reg_r(gspca_dev, 0x0010); /* ?? */
reg_r(gspca_dev, 0x0010);
- reg_w(dev, 0x01, 0x0000);
- reg_w(dev, 0x01, 0x0001);
- reg_w(dev, 0x06, 0x0010); /* OmniVision */
- reg_w(dev, 0xa1, 0x008b);
- reg_w(dev, 0x08, 0x008d);
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0x06, 0x0010); /* OmniVision */
+ reg_w(gspca_dev, 0xa1, 0x008b);
+ reg_w(gspca_dev, 0x08, 0x008d);
msleep(500);
- reg_w(dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
i2c_write(gspca_dev, 0x12, 0x80, 0x00); /* sensor reset */
retword = i2c_read(gspca_dev, 0x0a) << 8;
retword |= i2c_read(gspca_dev, 0x0b);
PDEBUG(D_PROBE, "probe 2wr ov vga 0x%04x", retword);
switch (retword) {
case 0x7631: /* OV7630C */
- reg_w(dev, 0x06, 0x0010);
+ reg_w(gspca_dev, 0x06, 0x0010);
break;
case 0x7620: /* OV7620 */
case 0x7648: /* OV7648 */
};
static const struct sensor_by_chipset_revision chipset_revision_sensor[] = {
{0xc000, 0x12}, /* TAS5130C */
- {0xc001, 0x13}, /* MI0360SOC */
+ {0xc001, 0x13}, /* MT9V111 */
{0xe001, 0x13},
{0x8001, 0x13},
{0x8000, 0x14}, /* CS2102K */
- {0x8400, 0x15}, /* TAS5130K */
+ {0x8400, 0x15}, /* MT9V111 */
{0xe400, 0x15},
};
static int vga_3wr_probe(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- struct usb_device *dev = gspca_dev->dev;
int i;
u8 retbyte;
u16 retword;
/*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/
- reg_w(dev, 0x02, 0x0010);
+ reg_w(gspca_dev, 0x02, 0x0010);
reg_r(gspca_dev, 0x0010);
- reg_w(dev, 0x01, 0x0000);
- reg_w(dev, 0x00, 0x0010);
- reg_w(dev, 0x01, 0x0001);
- reg_w(dev, 0x91, 0x008b);
- reg_w(dev, 0x03, 0x0012);
- reg_w(dev, 0x01, 0x0012);
- reg_w(dev, 0x05, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, 0x00, 0x0010);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0x91, 0x008b);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x05, 0x0012);
retword = i2c_read(gspca_dev, 0x14);
if (retword != 0)
return 0x11; /* HV7131R */
if (retword != 0)
return 0x11; /* HV7131R */
- reg_w(dev, 0x02, 0x0010);
+ reg_w(gspca_dev, 0x02, 0x0010);
retword = reg_r(gspca_dev, 0x000b) << 8;
retword |= reg_r(gspca_dev, 0x000a);
PDEBUG(D_PROBE, "probe 3wr vga 1 0x%04x", retword);
reg_r(gspca_dev, 0x0010);
- /* value 0x4001 is meaningless */
- if (retword != 0x4001) {
- if ((retword & 0xff00) == 0x6400)
- return 0x02; /* TAS5130C */
- for (i = 0; i < ARRAY_SIZE(chipset_revision_sensor); i++) {
- if (chipset_revision_sensor[i].revision == retword) {
- sd->chip_revision = retword;
- send_unknown(dev, SENSOR_PB0330);
- return chipset_revision_sensor[i]
- .internal_sensor_id;
- }
+ if ((retword & 0xff00) == 0x6400)
+ return 0x02; /* TAS5130C */
+ for (i = 0; i < ARRAY_SIZE(chipset_revision_sensor); i++) {
+ if (chipset_revision_sensor[i].revision == retword) {
+ sd->chip_revision = retword;
+ send_unknown(gspca_dev, SENSOR_PB0330);
+ return chipset_revision_sensor[i]
+ .internal_sensor_id;
}
}
- reg_w(dev, 0x01, 0x0000); /* check PB0330 */
- reg_w(dev, 0x01, 0x0001);
- reg_w(dev, 0xdd, 0x008b);
- reg_w(dev, 0x0a, 0x0010);
- reg_w(dev, 0x03, 0x0012);
- reg_w(dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0000); /* check PB0330 */
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0xdd, 0x008b);
+ reg_w(gspca_dev, 0x0a, 0x0010);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
retword = i2c_read(gspca_dev, 0x00);
if (retword != 0) {
- PDEBUG(D_PROBE, "probe 3wr vga type 0a ?");
+ PDEBUG(D_PROBE, "probe 3wr vga type 0a");
return 0x0a; /* PB0330 */
}
- reg_w(dev, 0x01, 0x0000);
- reg_w(dev, 0x01, 0x0001);
- reg_w(dev, 0x98, 0x008b);
- reg_w(dev, 0x01, 0x0010);
- reg_w(dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0x98, 0x008b);
+ reg_w(gspca_dev, 0x01, 0x0010);
+ reg_w(gspca_dev, 0x03, 0x0012);
msleep(2);
- reg_w(dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
retword = i2c_read(gspca_dev, 0x00);
if (retword != 0) {
PDEBUG(D_PROBE, "probe 3wr vga type %02x", retword);
if (retword == 0x0011) /* VF0250 */
return 0x0250;
if (retword == 0x0029) /* gc0305 */
- send_unknown(dev, SENSOR_GC0305);
+ send_unknown(gspca_dev, SENSOR_GC0305);
return retword;
}
- reg_w(dev, 0x01, 0x0000); /* check OmniVision */
- reg_w(dev, 0x01, 0x0001);
- reg_w(dev, 0xa1, 0x008b);
- reg_w(dev, 0x08, 0x008d);
- reg_w(dev, 0x06, 0x0010);
- reg_w(dev, 0x01, 0x0012);
- reg_w(dev, 0x05, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0000); /* check OmniVision */
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0xa1, 0x008b);
+ reg_w(gspca_dev, 0x08, 0x008d);
+ reg_w(gspca_dev, 0x06, 0x0010);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x05, 0x0012);
if (i2c_read(gspca_dev, 0x1c) == 0x007f /* OV7610 - manufacturer ID */
&& i2c_read(gspca_dev, 0x1d) == 0x00a2) {
- send_unknown(dev, SENSOR_OV7620);
+ send_unknown(gspca_dev, SENSOR_OV7620);
return 0x06; /* OmniVision confirm ? */
}
- reg_w(dev, 0x01, 0x0000);
- reg_w(dev, 0x00, 0x0002);
- reg_w(dev, 0x01, 0x0010);
- reg_w(dev, 0x01, 0x0001);
- reg_w(dev, 0xee, 0x008b);
- reg_w(dev, 0x03, 0x0012);
- reg_w(dev, 0x01, 0x0012);
- reg_w(dev, 0x05, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, 0x00, 0x0002);
+ reg_w(gspca_dev, 0x01, 0x0010);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0xee, 0x008b);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x05, 0x0012);
retword = i2c_read(gspca_dev, 0x00) << 8; /* ID 0 */
retword |= i2c_read(gspca_dev, 0x01); /* ID 1 */
PDEBUG(D_PROBE, "probe 3wr vga 2 0x%04x", retword);
if (retword == 0x2030) {
retbyte = i2c_read(gspca_dev, 0x02); /* revision number */
PDEBUG(D_PROBE, "sensor PO2030 rev 0x%02x", retbyte);
- send_unknown(dev, SENSOR_PO2030);
+ send_unknown(gspca_dev, SENSOR_PO2030);
return retword;
}
- reg_w(dev, 0x01, 0x0000);
- reg_w(dev, 0x0a, 0x0010);
- reg_w(dev, 0xd3, 0x008b);
- reg_w(dev, 0x01, 0x0001);
- reg_w(dev, 0x03, 0x0012);
- reg_w(dev, 0x01, 0x0012);
- reg_w(dev, 0x05, 0x0012);
- reg_w(dev, 0xd3, 0x008b);
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, 0x0a, 0x0010);
+ reg_w(gspca_dev, 0xd3, 0x008b);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x05, 0x0012);
+ reg_w(gspca_dev, 0xd3, 0x008b);
retword = i2c_read(gspca_dev, 0x01);
if (retword != 0) {
PDEBUG(D_PROBE, "probe 3wr vga type 0a ? ret: %04x", retword);
/* this function is called at probe time */
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (id->idProduct == 0x301b)
+ sd->bridge = BRIDGE_ZC301;
+ else
+ sd->bridge = BRIDGE_ZC303;
+
+ /* define some sensors from the vendor/product */
+ sd->sensor = id->driver_info;
+
+ sd->sharpness = SHARPNESS_DEF;
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->autogain = AUTOGAIN_DEF;
+ sd->lightfreq = FREQ_DEF;
+ sd->quality = QUALITY_DEF;
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
int sensor;
static const u8 gamma[SENSOR_MAX] = {
- 4, /* SENSOR_ADCM2700 0 */
- 4, /* SENSOR_CS2102 1 */
- 5, /* SENSOR_CS2102K 2 */
- 4, /* SENSOR_GC0305 3 */
- 4, /* SENSOR_HDCS2020b 4 */
- 4, /* SENSOR_HV7131B 5 */
- 4, /* SENSOR_HV7131C 6 */
- 4, /* SENSOR_ICM105A 7 */
- 4, /* SENSOR_MC501CB 8 */
- 4, /* SENSOR_MI0360SOC 9 */
- 3, /* SENSOR_OV7620 10 */
- 4, /* SENSOR_OV7630C 11 */
- 4, /* SENSOR_PAS106 12 */
- 4, /* SENSOR_PAS202B 13 */
- 4, /* SENSOR_PB0330 14 */
- 4, /* SENSOR_PO2030 15 */
- 4, /* SENSOR_TAS5130CK 16 */
- 3, /* SENSOR_TAS5130CXX 17 */
- 3, /* SENSOR_TAS5130C_VF0250 18 */
+ [SENSOR_ADCM2700] = 4,
+ [SENSOR_CS2102] = 4,
+ [SENSOR_CS2102K] = 5,
+ [SENSOR_GC0305] = 4,
+ [SENSOR_HDCS2020b] = 4,
+ [SENSOR_HV7131B] = 4,
+ [SENSOR_HV7131R] = 4,
+ [SENSOR_ICM105A] = 4,
+ [SENSOR_MC501CB] = 4,
+ [SENSOR_MT9V111_1] = 4,
+ [SENSOR_MT9V111_3] = 4,
+ [SENSOR_OV7620] = 3,
+ [SENSOR_OV7630C] = 4,
+ [SENSOR_PAS106] = 4,
+ [SENSOR_PAS202B] = 4,
+ [SENSOR_PB0330] = 4,
+ [SENSOR_PO2030] = 4,
+ [SENSOR_TAS5130C] = 3,
+ [SENSOR_TAS5130C_VF0250] = 3,
};
static const u8 mode_tb[SENSOR_MAX] = {
- 2, /* SENSOR_ADCM2700 0 */
- 1, /* SENSOR_CS2102 1 */
- 1, /* SENSOR_CS2102K 2 */
- 1, /* SENSOR_GC0305 3 */
- 1, /* SENSOR_HDCS2020b 4 */
- 1, /* SENSOR_HV7131B 5 */
- 1, /* SENSOR_HV7131C 6 */
- 1, /* SENSOR_ICM105A 7 */
- 2, /* SENSOR_MC501CB 8 */
- 1, /* SENSOR_MI0360SOC 9 */
- 2, /* SENSOR_OV7620 10 */
- 1, /* SENSOR_OV7630C 11 */
- 0, /* SENSOR_PAS106 12 */
- 1, /* SENSOR_PAS202B 13 */
- 1, /* SENSOR_PB0330 14 */
- 1, /* SENSOR_PO2030 15 */
- 1, /* SENSOR_TAS5130CK 16 */
- 1, /* SENSOR_TAS5130CXX 17 */
- 1, /* SENSOR_TAS5130C_VF0250 18 */
+ [SENSOR_ADCM2700] = 2,
+ [SENSOR_CS2102] = 1,
+ [SENSOR_CS2102K] = 1,
+ [SENSOR_GC0305] = 1,
+ [SENSOR_HDCS2020b] = 1,
+ [SENSOR_HV7131B] = 1,
+ [SENSOR_HV7131R] = 1,
+ [SENSOR_ICM105A] = 1,
+ [SENSOR_MC501CB] = 2,
+ [SENSOR_MT9V111_1] = 1,
+ [SENSOR_MT9V111_3] = 1,
+ [SENSOR_OV7620] = 2,
+ [SENSOR_OV7630C] = 1,
+ [SENSOR_PAS106] = 0,
+ [SENSOR_PAS202B] = 1,
+ [SENSOR_PB0330] = 1,
+ [SENSOR_PO2030] = 1,
+ [SENSOR_TAS5130C] = 1,
+ [SENSOR_TAS5130C_VF0250] = 1,
};
- /* define some sensors from the vendor/product */
- sd->sharpness = SHARPNESS_DEF;
- sd->sensor = id->driver_info;
sensor = zcxx_probeSensor(gspca_dev);
if (sensor >= 0)
PDEBUG(D_PROBE, "probe sensor -> %04x", sensor);
break;
default:
PDEBUG(D_PROBE,
- "Sensor UNKNOWN_0 force Tas5130");
- sd->sensor = SENSOR_TAS5130CXX;
+ "Unknown sensor - set to TAS5130C");
+ sd->sensor = SENSOR_TAS5130C;
}
break;
case 0:
break;
default:
/* case 2: * hv7131r */
- PDEBUG(D_PROBE, "Find Sensor HV7131R(c)");
- sd->sensor = SENSOR_HV7131C;
+ PDEBUG(D_PROBE, "Find Sensor HV7131R");
+ sd->sensor = SENSOR_HV7131R;
break;
}
break;
case 0x02:
PDEBUG(D_PROBE, "Sensor TAS5130C");
- sd->sensor = SENSOR_TAS5130CXX;
+ sd->sensor = SENSOR_TAS5130C;
break;
case 0x04:
PDEBUG(D_PROBE, "Find Sensor CS2102");
case 0x10:
case 0x12:
PDEBUG(D_PROBE, "Find Sensor TAS5130C");
- sd->sensor = SENSOR_TAS5130CXX;
+ sd->sensor = SENSOR_TAS5130C;
break;
case 0x11:
- PDEBUG(D_PROBE, "Find Sensor HV7131R(c)");
- sd->sensor = SENSOR_HV7131C;
+ PDEBUG(D_PROBE, "Find Sensor HV7131R");
+ sd->sensor = SENSOR_HV7131R;
break;
case 0x13:
+ case 0x15:
PDEBUG(D_PROBE,
- "Find Sensor MI0360SOC. Chip revision %x",
+ "Sensor MT9V111. Chip revision %04x",
sd->chip_revision);
- sd->sensor = SENSOR_MI0360SOC;
+ sd->sensor = sd->bridge == BRIDGE_ZC301
+ ? SENSOR_MT9V111_1
+ : SENSOR_MT9V111_3;
break;
case 0x14:
PDEBUG(D_PROBE,
sd->chip_revision);
sd->sensor = SENSOR_CS2102K;
break;
- case 0x15:
- PDEBUG(D_PROBE,
- "Find Sensor TAS5130CK?. Chip revision %x",
- sd->chip_revision);
- sd->sensor = SENSOR_TAS5130CK;
- break;
case 0x16:
PDEBUG(D_PROBE, "Find Sensor ADCM2700");
sd->sensor = SENSOR_ADCM2700;
}
if (sensor < 0x20) {
if (sensor == -1 || sensor == 0x10 || sensor == 0x12)
- reg_w(gspca_dev->dev, 0x02, 0x0010);
+ reg_w(gspca_dev, 0x02, 0x0010);
reg_r(gspca_dev, 0x0010);
}
cam = &gspca_dev->cam;
-/*fixme:test*/
- gspca_dev->nbalt--;
switch (mode_tb[sd->sensor]) {
case 0:
cam->cam_mode = sif_mode;
cam->nmodes = ARRAY_SIZE(broken_vga_mode);
break;
}
- sd->brightness = BRIGHTNESS_DEF;
- sd->contrast = CONTRAST_DEF;
sd->gamma = gamma[sd->sensor];
- sd->autogain = AUTOGAIN_DEF;
- sd->lightfreq = FREQ_DEF;
- sd->quality = QUALITY_DEF;
switch (sd->sensor) {
- case SENSOR_HV7131B:
- case SENSOR_HV7131C:
case SENSOR_OV7630C:
gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX);
break;
}
- return 0;
-}
-
-/* this function is called at probe and resume time */
-static int sd_init(struct gspca_dev *gspca_dev)
-{
/* switch off the led */
- reg_w(gspca_dev->dev, 0x01, 0x0000);
- return 0;
+ reg_w(gspca_dev, 0x01, 0x0000);
+ return gspca_dev->usb_err;
}
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- struct usb_device *dev = gspca_dev->dev;
int mode;
static const struct usb_action *init_tb[SENSOR_MAX][2] = {
- {adcm2700_Initial, adcm2700_InitialScale}, /* 0 */
- {cs2102_Initial, cs2102_InitialScale}, /* 1 */
- {cs2102K_Initial, cs2102K_InitialScale}, /* 2 */
- {gc0305_Initial, gc0305_InitialScale}, /* 3 */
- {hdcs2020b_Initial, hdcs2020b_InitialScale}, /* 4 */
- {hv7131b_Initial, hv7131b_InitialScale}, /* 5 */
- {hv7131r_Initial, hv7131r_InitialScale}, /* 6 */
- {icm105a_Initial, icm105a_InitialScale}, /* 7 */
- {mc501cb_Initial, mc501cb_InitialScale}, /* 8 */
- {mi0360soc_Initial, mi0360soc_InitialScale}, /* 9 */
- {ov7620_Initial, ov7620_InitialScale}, /* 10 */
- {ov7630c_Initial, ov7630c_InitialScale}, /* 11 */
- {pas106b_Initial, pas106b_InitialScale}, /* 12 */
- {pas202b_Initial, pas202b_InitialScale}, /* 13 */
- {pb0330_Initial, pb0330_InitialScale}, /* 14 */
- {po2030_Initial, po2030_InitialScale}, /* 15 */
- {tas5130cK_Initial, tas5130cK_InitialScale}, /* 16 */
- {tas5130cxx_Initial, tas5130cxx_InitialScale}, /* 17 */
+ [SENSOR_ADCM2700] =
+ {adcm2700_Initial, adcm2700_InitialScale},
+ [SENSOR_CS2102] =
+ {cs2102_Initial, cs2102_InitialScale},
+ [SENSOR_CS2102K] =
+ {cs2102K_Initial, cs2102K_InitialScale},
+ [SENSOR_GC0305] =
+ {gc0305_Initial, gc0305_InitialScale},
+ [SENSOR_HDCS2020b] =
+ {hdcs2020b_Initial, hdcs2020b_InitialScale},
+ [SENSOR_HV7131B] =
+ {hv7131b_Initial, hv7131b_InitialScale},
+ [SENSOR_HV7131R] =
+ {hv7131r_Initial, hv7131r_InitialScale},
+ [SENSOR_ICM105A] =
+ {icm105a_Initial, icm105a_InitialScale},
+ [SENSOR_MC501CB] =
+ {mc501cb_Initial, mc501cb_InitialScale},
+ [SENSOR_MT9V111_1] =
+ {mt9v111_1_Initial, mt9v111_1_InitialScale},
+ [SENSOR_MT9V111_3] =
+ {mt9v111_3_Initial, mt9v111_3_InitialScale},
+ [SENSOR_OV7620] =
+ {ov7620_Initial, ov7620_InitialScale},
+ [SENSOR_OV7630C] =
+ {ov7630c_Initial, ov7630c_InitialScale},
+ [SENSOR_PAS106] =
+ {pas106b_Initial, pas106b_InitialScale},
+ [SENSOR_PAS202B] =
+ {pas202b_Initial, pas202b_InitialScale},
+ [SENSOR_PB0330] =
+ {pb0330_Initial, pb0330_InitialScale},
+ [SENSOR_PO2030] =
+ {po2030_Initial, po2030_InitialScale},
+ [SENSOR_TAS5130C] =
+ {tas5130c_Initial, tas5130c_InitialScale},
+ [SENSOR_TAS5130C_VF0250] =
{tas5130c_vf0250_Initial, tas5130c_vf0250_InitialScale},
- /* 18 */
};
/* create the JPEG header */
mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
switch (sd->sensor) {
- case SENSOR_HV7131C:
+ case SENSOR_HV7131R:
zcxx_probeSensor(gspca_dev);
break;
case SENSOR_PAS106:
case SENSOR_GC0305:
case SENSOR_OV7620:
case SENSOR_PO2030:
- case SENSOR_TAS5130CXX:
+ case SENSOR_TAS5130C:
case SENSOR_TAS5130C_VF0250:
/* msleep(100); * ?? */
reg_r(gspca_dev, 0x0002); /* --> 0x40 */
- reg_w(dev, 0x09, 0x01ad); /* (from win traces) */
- reg_w(dev, 0x15, 0x01ae);
- if (sd->sensor == SENSOR_TAS5130CXX)
+ reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */
+ reg_w(gspca_dev, 0x15, 0x01ae);
+ if (sd->sensor == SENSOR_TAS5130C)
break;
- reg_w(dev, 0x0d, 0x003a);
- reg_w(dev, 0x02, 0x003b);
- reg_w(dev, 0x00, 0x0038);
+ reg_w(gspca_dev, 0x0d, 0x003a);
+ reg_w(gspca_dev, 0x02, 0x003b);
+ reg_w(gspca_dev, 0x00, 0x0038);
break;
case SENSOR_PAS202B:
- reg_w(dev, 0x03, 0x003b);
- reg_w(dev, 0x0c, 0x003a);
- reg_w(dev, 0x0b, 0x0039);
+ reg_w(gspca_dev, 0x03, 0x003b);
+ reg_w(gspca_dev, 0x0c, 0x003a);
+ reg_w(gspca_dev, 0x0b, 0x0039);
break;
}
case SENSOR_ADCM2700:
case SENSOR_OV7620:
reg_r(gspca_dev, 0x0008);
- reg_w(dev, 0x00, 0x0008);
+ reg_w(gspca_dev, 0x00, 0x0008);
break;
case SENSOR_PAS202B:
case SENSOR_GC0305:
- case SENSOR_TAS5130CXX:
+ case SENSOR_TAS5130C:
reg_r(gspca_dev, 0x0008);
/* fall thru */
case SENSOR_PO2030:
- reg_w(dev, 0x03, 0x0008);
+ reg_w(gspca_dev, 0x03, 0x0008);
break;
}
setsharpness(gspca_dev);
case SENSOR_CS2102K: /* gamma set in xxx_Initial */
case SENSOR_HDCS2020b:
case SENSOR_OV7630C:
- case SENSOR_TAS5130CK:
break;
default:
setcontrast(gspca_dev);
case SENSOR_OV7620:
case SENSOR_PAS202B:
reg_r(gspca_dev, 0x0180); /* from win */
- reg_w(dev, 0x00, 0x0180);
+ reg_w(gspca_dev, 0x00, 0x0180);
break;
default:
setquality(gspca_dev);
switch (sd->sensor) {
case SENSOR_ADCM2700:
- reg_w(dev, 0x09, 0x01ad); /* (from win traces) */
- reg_w(dev, 0x15, 0x01ae);
- reg_w(dev, 0x02, 0x0180);
+ reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */
+ reg_w(gspca_dev, 0x15, 0x01ae);
+ reg_w(gspca_dev, 0x02, 0x0180);
/* ms-win + */
- reg_w(dev, 0x40, 0x0117);
+ reg_w(gspca_dev, 0x40, 0x0117);
break;
case SENSOR_GC0305:
- case SENSOR_TAS5130CXX:
- reg_w(dev, 0x09, 0x01ad); /* (from win traces) */
- reg_w(dev, 0x15, 0x01ae);
+ case SENSOR_TAS5130C:
+ reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */
+ reg_w(gspca_dev, 0x15, 0x01ae);
/* fall thru */
case SENSOR_PAS202B:
case SENSOR_PO2030:
-/* reg_w(dev, 0x40, ZC3XX_R117_GGAIN); * (from win traces) */
+/* reg_w(gspca_dev, 0x40, ZC3XX_R117_GGAIN); * (from win traces) */
reg_r(gspca_dev, 0x0180);
break;
case SENSOR_OV7620:
- reg_w(dev, 0x09, 0x01ad);
- reg_w(dev, 0x15, 0x01ae);
+ reg_w(gspca_dev, 0x09, 0x01ad);
+ reg_w(gspca_dev, 0x15, 0x01ae);
i2c_read(gspca_dev, 0x13); /*fixme: returns 0xa3 */
i2c_write(gspca_dev, 0x13, 0xa3, 0x00);
/*fixme: returned value to send? */
- reg_w(dev, 0x40, 0x0117);
+ reg_w(gspca_dev, 0x40, 0x0117);
reg_r(gspca_dev, 0x0180);
break;
}
switch (sd->sensor) {
case SENSOR_PO2030:
msleep(50);
- reg_w(dev, 0x00, 0x0007); /* (from win traces) */
- reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING);
+ reg_w(gspca_dev, 0x00, 0x0007); /* (from win traces) */
+ reg_w(gspca_dev, 0x02, ZC3XX_R008_CLOCKSETTING);
break;
}
- return 0;
+ return gspca_dev->usb_err;
}
/* called on streamoff with alt 0 and on disconnect */
if (!gspca_dev->present)
return;
- send_unknown(gspca_dev->dev, sd->sensor);
+ send_unknown(gspca_dev, sd->sensor);
}
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
sd->brightness = val;
if (gspca_dev->streaming)
setcontrast(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
sd->contrast = val;
if (gspca_dev->streaming)
setcontrast(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
sd->autogain = val;
if (gspca_dev->streaming)
setautogain(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
sd->gamma = val;
if (gspca_dev->streaming)
setcontrast(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val)
sd->lightfreq = val;
if (gspca_dev->streaming)
setlightfreq(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
sd->sharpness = val;
if (gspca_dev->streaming)
setsharpness(gspca_dev);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
sd->quality = jcomp->quality;
if (gspca_dev->streaming)
jpeg_set_qual(sd->jpeg_hdr, sd->quality);
- return 0;
+ return gspca_dev->usb_err;
}
static int sd_get_jcomp(struct gspca_dev *gspca_dev,
{USB_DEVICE(0x10fd, 0x8050)},
{} /* end of entry */
};
-#undef DVNAME
MODULE_DEVICE_TABLE(usb, device_table);
/* -- device connect -- */
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/slab.h>
#include "ivtv-driver.h"
-#include "ivtv-cards.h"
#include "ivtv-ioctl.h"
-#include "ivtv-routing.h"
-#include "ivtv-i2c.h"
-#include "ivtv-mailbox.h"
#include "ivtv-controls.h"
-/* Must be sorted from low to high control ID! */
-static const u32 user_ctrls[] = {
- V4L2_CID_USER_CLASS,
- V4L2_CID_BRIGHTNESS,
- V4L2_CID_CONTRAST,
- V4L2_CID_SATURATION,
- V4L2_CID_HUE,
- V4L2_CID_AUDIO_VOLUME,
- V4L2_CID_AUDIO_BALANCE,
- V4L2_CID_AUDIO_BASS,
- V4L2_CID_AUDIO_TREBLE,
- V4L2_CID_AUDIO_MUTE,
- V4L2_CID_AUDIO_LOUDNESS,
- 0
-};
-
-static const u32 *ctrl_classes[] = {
- user_ctrls,
- cx2341x_mpeg_ctrls,
- NULL
-};
-
-
-int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl)
-{
- struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
- const char *name;
-
- qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
- if (qctrl->id == 0)
- return -EINVAL;
-
- switch (qctrl->id) {
- /* Standard V4L2 controls */
- case V4L2_CID_USER_CLASS:
- return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0);
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_HUE:
- case V4L2_CID_SATURATION:
- case V4L2_CID_CONTRAST:
- if (v4l2_subdev_call(itv->sd_video, core, queryctrl, qctrl))
- qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
- return 0;
-
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_MUTE:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_AUDIO_LOUDNESS:
- if (v4l2_subdev_call(itv->sd_audio, core, queryctrl, qctrl))
- qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
- return 0;
-
- default:
- if (cx2341x_ctrl_query(&itv->params, qctrl))
- qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
- return 0;
- }
- strncpy(qctrl->name, name, sizeof(qctrl->name) - 1);
- qctrl->name[sizeof(qctrl->name) - 1] = 0;
- return 0;
-}
-
-int ivtv_querymenu(struct file *file, void *fh, struct v4l2_querymenu *qmenu)
-{
- struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
- struct v4l2_queryctrl qctrl;
-
- qctrl.id = qmenu->id;
- ivtv_queryctrl(file, fh, &qctrl);
- return v4l2_ctrl_query_menu(qmenu, &qctrl,
- cx2341x_ctrl_get_menu(&itv->params, qmenu->id));
-}
-
-static int ivtv_try_ctrl(struct file *file, void *fh,
- struct v4l2_ext_control *vctrl)
-{
- struct v4l2_queryctrl qctrl;
- const char **menu_items = NULL;
- int err;
-
- qctrl.id = vctrl->id;
- err = ivtv_queryctrl(file, fh, &qctrl);
- if (err)
- return err;
- if (qctrl.type == V4L2_CTRL_TYPE_MENU)
- menu_items = v4l2_ctrl_get_menu(qctrl.id);
- return v4l2_ctrl_check(vctrl, &qctrl, menu_items);
-}
-
-static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
-{
- switch (vctrl->id) {
- /* Standard V4L2 controls */
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_HUE:
- case V4L2_CID_SATURATION:
- case V4L2_CID_CONTRAST:
- return v4l2_subdev_call(itv->sd_video, core, s_ctrl, vctrl);
-
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_MUTE:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_AUDIO_LOUDNESS:
- return v4l2_subdev_call(itv->sd_audio, core, s_ctrl, vctrl);
-
- default:
- IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
- return -EINVAL;
- }
- return 0;
-}
-
-static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
+static int ivtv_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt)
{
- switch (vctrl->id) {
- /* Standard V4L2 controls */
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_HUE:
- case V4L2_CID_SATURATION:
- case V4L2_CID_CONTRAST:
- return v4l2_subdev_call(itv->sd_video, core, g_ctrl, vctrl);
-
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_MUTE:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_AUDIO_LOUDNESS:
- return v4l2_subdev_call(itv->sd_audio, core, g_ctrl, vctrl);
- default:
- IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
- return -EINVAL;
- }
- return 0;
-}
-
-static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fmt)
-{
- if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE))
- return -EINVAL;
- if (atomic_read(&itv->capturing) > 0)
- return -EBUSY;
+ struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
/* First try to allocate sliced VBI buffers if needed. */
if (fmt && itv->vbi.sliced_mpeg_data[0] == NULL) {
return 0;
}
-int ivtv_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
+static int ivtv_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val)
{
- struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
- struct v4l2_control ctrl;
-
- if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
- int i;
- int err = 0;
-
- for (i = 0; i < c->count; i++) {
- ctrl.id = c->controls[i].id;
- ctrl.value = c->controls[i].value;
- err = ivtv_g_ctrl(itv, &ctrl);
- c->controls[i].value = ctrl.value;
- if (err) {
- c->error_idx = i;
- break;
- }
- }
- return err;
- }
- if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
- return cx2341x_ext_ctrls(&itv->params, 0, c, VIDIOC_G_EXT_CTRLS);
- return -EINVAL;
+ struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
+ int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
+ struct v4l2_mbus_framefmt fmt;
+
+ /* fix videodecoder resolution */
+ fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1);
+ fmt.height = cxhdl->height;
+ fmt.code = V4L2_MBUS_FMT_FIXED;
+ v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &fmt);
+ return 0;
}
-int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
+static int ivtv_s_audio_sampling_freq(struct cx2341x_handler *cxhdl, u32 idx)
{
- struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
- struct v4l2_control ctrl;
-
- if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
- int i;
- int err = 0;
-
- for (i = 0; i < c->count; i++) {
- ctrl.id = c->controls[i].id;
- ctrl.value = c->controls[i].value;
- err = ivtv_s_ctrl(itv, &ctrl);
- c->controls[i].value = ctrl.value;
- if (err) {
- c->error_idx = i;
- break;
- }
- }
- return err;
- }
- if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
- static u32 freqs[3] = { 44100, 48000, 32000 };
- struct cx2341x_mpeg_params p = itv->params;
- int err = cx2341x_ext_ctrls(&p, atomic_read(&itv->capturing), c, VIDIOC_S_EXT_CTRLS);
- unsigned idx;
-
- if (err)
- return err;
+ static const u32 freqs[3] = { 44100, 48000, 32000 };
+ struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
- if (p.video_encoding != itv->params.video_encoding) {
- int is_mpeg1 = p.video_encoding ==
- V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
- struct v4l2_mbus_framefmt fmt;
-
- /* fix videodecoder resolution */
- fmt.width = itv->params.width / (is_mpeg1 ? 2 : 1);
- fmt.height = itv->params.height;
- fmt.code = V4L2_MBUS_FMT_FIXED;
- v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &fmt);
- }
- err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p);
- if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt)
- err = ivtv_setup_vbi_fmt(itv, p.stream_vbi_fmt);
- itv->params = p;
- itv->dualwatch_stereo_mode = p.audio_properties & 0x0300;
- idx = p.audio_properties & 0x03;
- /* The audio clock of the digitizer must match the codec sample
- rate otherwise you get some very strange effects. */
- if (idx < ARRAY_SIZE(freqs))
- ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]);
- return err;
- }
- return -EINVAL;
+ /* The audio clock of the digitizer must match the codec sample
+ rate otherwise you get some very strange effects. */
+ if (idx < ARRAY_SIZE(freqs))
+ ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]);
+ return 0;
}
-int ivtv_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
+static int ivtv_s_audio_mode(struct cx2341x_handler *cxhdl, u32 val)
{
- struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+ struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
- if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
- int i;
- int err = 0;
-
- for (i = 0; i < c->count; i++) {
- err = ivtv_try_ctrl(file, fh, &c->controls[i]);
- if (err) {
- c->error_idx = i;
- break;
- }
- }
- return err;
- }
- if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
- return cx2341x_ext_ctrls(&itv->params, atomic_read(&itv->capturing), c, VIDIOC_TRY_EXT_CTRLS);
- return -EINVAL;
+ itv->dualwatch_stereo_mode = val;
+ return 0;
}
+
+struct cx2341x_handler_ops ivtv_cxhdl_ops = {
+ .s_audio_mode = ivtv_s_audio_mode,
+ .s_audio_sampling_freq = ivtv_s_audio_sampling_freq,
+ .s_video_encoding = ivtv_s_video_encoding,
+ .s_stream_vbi_fmt = ivtv_s_stream_vbi_fmt,
+};
#ifndef IVTV_CONTROLS_H
#define IVTV_CONTROLS_H
-int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a);
-int ivtv_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
-int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
-int ivtv_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
-int ivtv_querymenu(struct file *file, void *fh, struct v4l2_querymenu *a);
+extern struct cx2341x_handler_ops ivtv_cxhdl_ops;
#endif
#include "ivtv-cards.h"
#include "ivtv-vbi.h"
#include "ivtv-routing.h"
+#include "ivtv-controls.h"
#include "ivtv-gpio.h"
#include <media/tveeprom.h>
itv->open_id = 1;
/* Initial settings */
- cx2341x_fill_defaults(&itv->params);
- itv->params.port = CX2341X_PORT_MEMORY;
- itv->params.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
+ itv->cxhdl.port = CX2341X_PORT_MEMORY;
+ itv->cxhdl.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
init_waitqueue_head(&itv->eos_waitq);
init_waitqueue_head(&itv->event_waitq);
init_waitqueue_head(&itv->vsync_waitq);
retval = -ENOMEM;
goto err;
}
+ retval = cx2341x_handler_init(&itv->cxhdl, 50);
+ if (retval)
+ goto err;
+ itv->v4l2_dev.ctrl_handler = &itv->cxhdl.hdl;
+ itv->cxhdl.ops = &ivtv_cxhdl_ops;
+ itv->cxhdl.priv = itv;
+ itv->cxhdl.func = ivtv_api_func;
IVTV_DEBUG_INFO("base addr: 0x%08x\n", itv->base_addr);
itv->yuv_info.v4l2_src_w = itv->yuv_info.osd_full_w;
itv->yuv_info.v4l2_src_h = itv->yuv_info.osd_full_h;
- itv->params.video_gop_size = itv->is_60hz ? 15 : 12;
+ cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz);
itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG] = 0x08000;
itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM] = 0x01200;
IVTV_DEBUG_INFO("Getting firmware version..\n");
ivtv_firmware_versions(itv);
- if (itv->card->hw_all & IVTV_HW_CX25840) {
- struct v4l2_control ctrl;
-
+ if (itv->card->hw_all & IVTV_HW_CX25840)
v4l2_subdev_call(itv->sd_video, core, load_fw);
- /* CX25840_CID_ENABLE_PVR150_WORKAROUND */
- ctrl.id = V4L2_CID_PRIVATE_BASE;
- ctrl.value = itv->pvr150_workaround;
- v4l2_subdev_call(itv->sd_video, core, s_ctrl, &ctrl);
- }
vf.tuner = 0;
vf.type = V4L2_TUNER_ANALOG_TV;
/* For cards with video out, this call needs interrupts enabled */
ivtv_s_std(NULL, &fh, &itv->tuner_std);
+ /* Setup initial controls */
+ cx2341x_handler_setup(&itv->cxhdl);
return 0;
}
#include <linux/dvb/audio.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fh.h>
#include <media/tuner.h>
struct ivtv_options options; /* user options */
struct v4l2_device v4l2_dev;
+ struct cx2341x_handler cxhdl;
+ struct v4l2_ctrl_handler hdl_gpio;
struct v4l2_subdev sd_gpio; /* GPIO sub-device */
u16 instance;
v4l2_std_id std_out; /* current TV output standard */
u8 audio_stereo_mode; /* decoder setting how to handle stereo MPEG audio */
u8 audio_bilingual_mode; /* decoder setting how to handle bilingual MPEG audio */
- struct cx2341x_mpeg_params params; /* current encoder parameters */
/* Locking */
static void ivtv_dualwatch(struct ivtv *itv)
{
struct v4l2_tuner vt;
- u32 new_bitmap;
u32 new_stereo_mode;
- const u32 stereo_mask = 0x0300;
- const u32 dual = 0x0200;
+ const u32 dual = 0x02;
- new_stereo_mode = itv->params.audio_properties & stereo_mask;
+ new_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode);
memset(&vt, 0, sizeof(vt));
ivtv_call_all(itv, tuner, g_tuner, &vt);
if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
if (new_stereo_mode == itv->dualwatch_stereo_mode)
return;
- new_bitmap = new_stereo_mode | (itv->params.audio_properties & ~stereo_mask);
-
- IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n",
- itv->dualwatch_stereo_mode, new_stereo_mode, new_bitmap);
-
- if (ivtv_vapi(itv, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new_bitmap) == 0) {
- itv->dualwatch_stereo_mode = new_stereo_mode;
- return;
- }
- IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
+ IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x.\n",
+ itv->dualwatch_stereo_mode, new_stereo_mode);
+ if (v4l2_ctrl_s_ctrl(itv->cxhdl.audio_mode, new_stereo_mode))
+ IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
}
static void ivtv_update_pgm_info(struct ivtv *itv)
if (atomic_read(&itv->capturing) > 0) {
/* Undo video mute */
ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
- itv->params.video_mute | (itv->params.video_mute_yuv << 8));
+ v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute) |
+ (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8));
}
/* Done! Unmute and continue. */
ivtv_unmute(itv);
volatile u8 __iomem *mem_offset;
data[0] = 0;
- data[1] = itv->params.width; /* YUV source width */
- data[2] = itv->params.height;
- data[3] = itv->params.audio_properties; /* Audio settings to use,
+ data[1] = itv->cxhdl.width; /* YUV source width */
+ data[2] = itv->cxhdl.height;
+ data[3] = itv->cxhdl.audio_properties; /* Audio settings to use,
bitmap. see docs. */
if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) {
IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n");
#include "ivtv-gpio.h"
#include "tuner-xc2028.h"
#include <media/tuner.h>
+#include <media/v4l2-ctrls.h>
/*
* GPIO assignment of Yuan MPG600/MPG160
return container_of(sd, struct ivtv, sd_gpio);
}
-static struct v4l2_queryctrl gpio_ctrl_mute = {
- .id = V4L2_CID_AUDIO_MUTE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Mute",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- .flags = 0,
-};
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct ivtv, hdl_gpio)->sd_gpio;
+}
static int subdev_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
{
return 0;
}
-static int subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int subdev_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd = to_sd(ctrl);
struct ivtv *itv = sd_to_ivtv(sd);
u16 mask, data;
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- mask = itv->card->gpio_audio_mute.mask;
- data = itv->card->gpio_audio_mute.mute;
- ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data;
- return 0;
-}
-
-static int subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct ivtv *itv = sd_to_ivtv(sd);
- u16 mask, data;
-
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- mask = itv->card->gpio_audio_mute.mask;
- data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0;
- if (mask)
- write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
- return 0;
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ mask = itv->card->gpio_audio_mute.mask;
+ data = ctrl->val ? itv->card->gpio_audio_mute.mute : 0;
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) |
+ (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+ }
+ return -EINVAL;
}
-static int subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- if (qc->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- *qc = gpio_ctrl_mute;
- return 0;
-}
static int subdev_log_status(struct v4l2_subdev *sd)
{
IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n",
read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT),
read_reg(IVTV_REG_GPIO_IN));
+ v4l2_ctrl_handler_log_status(&itv->hdl_gpio, sd->name);
return 0;
}
return 0;
}
+static const struct v4l2_ctrl_ops gpio_ctrl_ops = {
+ .s_ctrl = subdev_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops subdev_core_ops = {
.log_status = subdev_log_status,
- .g_ctrl = subdev_g_ctrl,
- .s_ctrl = subdev_s_ctrl,
- .queryctrl = subdev_queryctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
};
static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = {
v4l2_subdev_init(&itv->sd_gpio, &subdev_ops);
snprintf(itv->sd_gpio.name, sizeof(itv->sd_gpio.name), "%s-gpio", itv->v4l2_dev.name);
itv->sd_gpio.grp_id = IVTV_HW_GPIO;
+ v4l2_ctrl_handler_init(&itv->hdl_gpio, 1);
+ v4l2_ctrl_new_std(&itv->hdl_gpio, &gpio_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ if (itv->hdl_gpio.error)
+ return itv->hdl_gpio.error;
+ itv->sd_gpio.ctrl_handler = &itv->hdl_gpio;
+ v4l2_ctrl_handler_setup(&itv->hdl_gpio);
return v4l2_device_register_subdev(&itv->v4l2_dev, &itv->sd_gpio);
}
#include "ivtv-cards.h"
#include "ivtv-gpio.h"
#include "ivtv-i2c.h"
+#include <media/cx25840.h>
/* i2c implementation for cx23415/6 chip, ivtv project.
* Author: Kevin Thayer (nufan_wfk at yahoo.com)
if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) {
sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
adap, mod, type, 0, I2C_ADDRS(hw_addrs[idx]));
+ } else if (hw == IVTV_HW_CX25840) {
+ struct cx25840_platform_data pdata;
+
+ pdata.pvr150_workaround = itv->pvr150_workaround;
+ sd = v4l2_i2c_new_subdev_cfg(&itv->v4l2_dev,
+ adap, mod, type, 0, &pdata, hw_addrs[idx], NULL);
} else {
sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
adap, mod, type, hw_addrs[idx], NULL);
data[0] |= (speed > 1000 || speed < -1500) ? 0x40000000 : 0;
data[1] = (speed < 0);
data[2] = speed < 0 ? 3 : 7;
- data[3] = itv->params.video_b_frames;
+ data[3] = v4l2_ctrl_g_ctrl(itv->cxhdl.video_b_frames);
data[4] = (speed == 1500 || speed == 500) ? itv->speed_mute_audio : 0;
data[5] = 0;
data[6] = 0;
struct ivtv *itv = id->itv;
struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
- pixfmt->width = itv->params.width;
- pixfmt->height = itv->params.height;
+ pixfmt->width = itv->cxhdl.width;
+ pixfmt->height = itv->cxhdl.height;
pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
pixfmt->field = V4L2_FIELD_INTERLACED;
pixfmt->priv = 0;
{
struct ivtv_open_id *id = fh;
struct ivtv *itv = id->itv;
- struct cx2341x_mpeg_params *p = &itv->params;
struct v4l2_mbus_framefmt mbus_fmt;
int ret = ivtv_try_fmt_vid_cap(file, fh, fmt);
int w = fmt->fmt.pix.width;
if (ret)
return ret;
- if (p->width == w && p->height == h)
+ if (itv->cxhdl.width == w && itv->cxhdl.height == h)
return 0;
if (atomic_read(&itv->capturing) > 0)
return -EBUSY;
- p->width = w;
- p->height = h;
- if (p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
+ itv->cxhdl.width = w;
+ itv->cxhdl.height = h;
+ if (v4l2_ctrl_g_ctrl(itv->cxhdl.video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
fmt->fmt.pix.width /= 2;
mbus_fmt.width = fmt->fmt.pix.width;
mbus_fmt.height = h;
itv->std = *std;
itv->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
- itv->params.is_50hz = itv->is_50hz = !itv->is_60hz;
- itv->params.width = 720;
- itv->params.height = itv->is_50hz ? 576 : 480;
+ itv->is_50hz = !itv->is_60hz;
+ cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz);
+ itv->cxhdl.width = 720;
+ itv->cxhdl.height = itv->is_50hz ? 576 : 480;
itv->vbi.count = itv->is_50hz ? 18 : 12;
itv->vbi.start[0] = itv->is_50hz ? 6 : 10;
itv->vbi.start[1] = itv->is_50hz ? 318 : 273;
ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz);
itv->main_rect.left = itv->main_rect.top = 0;
itv->main_rect.width = 720;
- itv->main_rect.height = itv->params.height;
+ itv->main_rect.height = itv->cxhdl.height;
ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
720, itv->main_rect.height, 0, 0);
yi->main_rect = itv->main_rect;
}
IVTV_INFO("Tuner: %s\n",
test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV");
- cx2341x_log_status(&itv->params, itv->v4l2_dev.name);
+ v4l2_ctrl_handler_log_status(&itv->cxhdl.hdl, itv->v4l2_dev.name);
IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags);
for (i = 0; i < IVTV_MAX_STREAMS; i++) {
struct ivtv_stream *s = &itv->streams[i];
.vidioc_s_register = ivtv_s_register,
#endif
.vidioc_default = ivtv_default,
- .vidioc_queryctrl = ivtv_queryctrl,
- .vidioc_querymenu = ivtv_querymenu,
- .vidioc_g_ext_ctrls = ivtv_g_ext_ctrls,
- .vidioc_s_ext_ctrls = ivtv_s_ext_ctrls,
- .vidioc_try_ext_ctrls = ivtv_try_ext_ctrls,
.vidioc_subscribe_event = ivtv_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
s->vdev->num = num;
s->vdev->v4l2_dev = &itv->v4l2_dev;
+ s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler;
s->vdev->fops = ivtv_stream_info[type].fops;
s->vdev->release = video_device_release;
s->vdev->tvnorms = V4L2_STD_ALL;
{
u32 data[CX2341X_MBOX_MAX_DATA];
struct ivtv *itv = s->itv;
- struct cx2341x_mpeg_params *p = &itv->params;
int captype = 0, subtype = 0;
int enable_passthrough = 0;
}
itv->mpg_data_received = itv->vbi_data_inserted = 0;
itv->dualwatch_jiffies = jiffies;
- itv->dualwatch_stereo_mode = p->audio_properties & 0x0300;
+ itv->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode);
itv->search_pack_header = 0;
break;
itv->pgm_info_offset, itv->pgm_info_num);
/* Setup API for Stream */
- cx2341x_update(itv, ivtv_api_func, NULL, p);
+ cx2341x_handler_setup(&itv->cxhdl);
/* mute if capturing radio */
if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags))
ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
- 1 | (p->video_mute_yuv << 8));
+ 1 | (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8));
}
/* Vsync Setup */
clear_bit(IVTV_F_I_EOS, &itv->i_flags);
+ cx2341x_handler_set_busy(&itv->cxhdl, 1);
+
/* Initialize Digitizer for Capture */
/* Avoid tinny audio problem - ensure audio clocks are going */
v4l2_subdev_call(itv->sd_audio, audio, s_stream, 1);
{
u32 data[CX2341X_MBOX_MAX_DATA];
struct ivtv *itv = s->itv;
- struct cx2341x_mpeg_params *p = &itv->params;
int datatype;
u16 width;
u16 height;
IVTV_DEBUG_INFO("Setting some initial decoder settings\n");
- width = p->width;
- height = p->height;
+ width = itv->cxhdl.width;
+ height = itv->cxhdl.height;
/* set audio mode to left/stereo for dual/stereo mode. */
ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
break;
}
if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype,
- width, height, p->audio_properties)) {
+ width, height, itv->cxhdl.audio_properties)) {
IVTV_DEBUG_WARN("Couldn't initialize decoder source\n");
}
return 0;
}
+ cx2341x_handler_set_busy(&itv->cxhdl, 0);
+
/* Set the following Interrupt mask bits for capture */
ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
del_timer(&itv->dma_timer);
/* Setup capture if not already done */
if (atomic_read(&itv->capturing) == 0) {
- cx2341x_update(itv, ivtv_api_func, NULL, &itv->params);
+ cx2341x_handler_setup(&itv->cxhdl);
+ cx2341x_handler_set_busy(&itv->cxhdl, 1);
}
/* Start Passthrough Mode */
clear_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
itv->output_mode = OUT_NONE;
+ if (atomic_read(&itv->capturing) == 0)
+ cx2341x_handler_set_busy(&itv->cxhdl, 0);
return 0;
}
msp_write_dem(client, 0x40, state->i2s_mode);
}
-void msp_set_audio(struct i2c_client *client)
-{
- struct msp_state *state = to_state(i2c_get_clientdata(client));
- int bal = 0, bass, treble, loudness;
- int val = 0;
- int reallymuted = state->muted | state->scan_in_progress;
-
- if (!reallymuted)
- val = (state->volume * 0x7f / 65535) << 8;
-
- v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n",
- state->muted ? "on" : "off",
- state->scan_in_progress ? "yes" : "no",
- state->volume);
-
- msp_write_dsp(client, 0x0000, val);
- msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1));
- if (state->has_scart2_out_volume)
- msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1));
- if (state->has_headphones)
- msp_write_dsp(client, 0x0006, val);
- if (!state->has_sound_processing)
- return;
-
- if (val)
- bal = (u8)((state->balance / 256) - 128);
- bass = ((state->bass - 32768) * 0x60 / 65535) << 8;
- treble = ((state->treble - 32768) * 0x60 / 65535) << 8;
- loudness = state->loudness ? ((5 * 4) << 8) : 0;
-
- v4l_dbg(1, msp_debug, client, "balance=%d bass=%d treble=%d loudness=%d\n",
- state->balance, state->bass, state->treble, state->loudness);
-
- msp_write_dsp(client, 0x0001, bal << 8);
- msp_write_dsp(client, 0x0002, bass);
- msp_write_dsp(client, 0x0003, treble);
- msp_write_dsp(client, 0x0004, loudness);
- if (!state->has_headphones)
- return;
- msp_write_dsp(client, 0x0030, bal << 8);
- msp_write_dsp(client, 0x0031, bass);
- msp_write_dsp(client, 0x0032, treble);
- msp_write_dsp(client, 0x0033, loudness);
-}
-
/* ------------------------------------------------------------------------ */
static void msp_wake_thread(struct i2c_client *client)
/* ------------------------------------------------------------------------ */
-static int msp_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int msp_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct msp_state *state = to_state(sd);
+ struct msp_state *state = ctrl_to_state(ctrl);
+ struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
+ int val = ctrl->val;
switch (ctrl->id) {
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = state->volume;
- break;
-
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = state->muted;
- break;
-
- case V4L2_CID_AUDIO_BALANCE:
- if (!state->has_sound_processing)
- return -EINVAL;
- ctrl->value = state->balance;
- break;
-
- case V4L2_CID_AUDIO_BASS:
- if (!state->has_sound_processing)
- return -EINVAL;
- ctrl->value = state->bass;
+ case V4L2_CID_AUDIO_VOLUME: {
+ /* audio volume cluster */
+ int reallymuted = state->muted->val | state->scan_in_progress;
+
+ if (!reallymuted)
+ val = (val * 0x7f / 65535) << 8;
+
+ v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n",
+ state->muted->val ? "on" : "off",
+ state->scan_in_progress ? "yes" : "no",
+ state->volume->val);
+
+ msp_write_dsp(client, 0x0000, val);
+ msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1));
+ if (state->has_scart2_out_volume)
+ msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1));
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0006, val);
break;
-
- case V4L2_CID_AUDIO_TREBLE:
- if (!state->has_sound_processing)
- return -EINVAL;
- ctrl->value = state->treble;
- break;
-
- case V4L2_CID_AUDIO_LOUDNESS:
- if (!state->has_sound_processing)
- return -EINVAL;
- ctrl->value = state->loudness;
- break;
-
- default:
- return -EINVAL;
}
- return 0;
-}
-
-static int msp_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct msp_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_VOLUME:
- state->volume = ctrl->value;
- if (state->volume == 0)
- state->balance = 32768;
- break;
-
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value < 0 || ctrl->value >= 2)
- return -ERANGE;
- state->muted = ctrl->value;
- break;
case V4L2_CID_AUDIO_BASS:
- if (!state->has_sound_processing)
- return -EINVAL;
- state->bass = ctrl->value;
+ val = ((val - 32768) * 0x60 / 65535) << 8;
+ msp_write_dsp(client, 0x0002, val);
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0031, val);
break;
case V4L2_CID_AUDIO_TREBLE:
- if (!state->has_sound_processing)
- return -EINVAL;
- state->treble = ctrl->value;
+ val = ((val - 32768) * 0x60 / 65535) << 8;
+ msp_write_dsp(client, 0x0003, val);
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0032, val);
break;
case V4L2_CID_AUDIO_LOUDNESS:
- if (!state->has_sound_processing)
- return -EINVAL;
- state->loudness = ctrl->value;
+ val = val ? ((5 * 4) << 8) : 0;
+ msp_write_dsp(client, 0x0004, val);
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0033, val);
break;
case V4L2_CID_AUDIO_BALANCE:
- if (!state->has_sound_processing)
- return -EINVAL;
- state->balance = ctrl->value;
+ val = (u8)((val / 256) - 128);
+ msp_write_dsp(client, 0x0001, val << 8);
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0030, val << 8);
break;
default:
return -EINVAL;
}
- msp_set_audio(client);
return 0;
}
+void msp_update_volume(struct msp_state *state)
+{
+ v4l2_ctrl_s_ctrl(state->volume, v4l2_ctrl_g_ctrl(state->volume));
+}
+
/* --- v4l2 ioctls --- */
static int msp_s_radio(struct v4l2_subdev *sd)
{
msp3400c_set_mode(client, MSP_MODE_FM_RADIO);
msp3400c_set_carrier(client, MSP_CARRIER(10.7),
MSP_CARRIER(10.7));
- msp_set_audio(client);
+ msp_update_volume(state);
break;
case OPMODE_AUTODETECT:
case OPMODE_AUTOSELECT:
return 0;
}
-static int msp_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- struct msp_state *state = to_state(sd);
-
- switch (qc->id) {
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880);
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
- default:
- break;
- }
- if (!state->has_sound_processing)
- return -EINVAL;
- switch (qc->id) {
- case V4L2_CID_AUDIO_LOUDNESS:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
- default:
- return -EINVAL;
- }
- return 0;
-}
-
static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
{
struct msp_state *state = to_state(sd);
struct msp_state *state = to_state(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
const char *p;
+ char prefix[V4L2_SUBDEV_NAME_SIZE + 20];
if (state->opmode == OPMODE_AUTOSELECT)
msp_detect_stereo(client);
v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n",
client->name, state->rev1, state->rev2);
- v4l_info(client, "Audio: volume %d%s\n",
- state->volume, state->muted ? " (muted)" : "");
- if (state->has_sound_processing) {
- v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n",
- state->balance, state->bass,
- state->treble,
- state->loudness ? "on" : "off");
- }
+ snprintf(prefix, sizeof(prefix), "%s: Audio: ", sd->name);
+ v4l2_ctrl_handler_log_status(&state->hdl, prefix);
switch (state->mode) {
case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break;
case MSP_MODE_FM_RADIO: p = "FM Radio"; break;
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops msp_ctrl_ops = {
+ .s_ctrl = msp_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops msp_core_ops = {
.log_status = msp_log_status,
.g_chip_ident = msp_g_chip_ident,
- .g_ctrl = msp_g_ctrl,
- .s_ctrl = msp_s_ctrl,
- .queryctrl = msp_queryctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
.s_std = msp_s_std,
};
{
struct msp_state *state;
struct v4l2_subdev *sd;
+ struct v4l2_ctrl_handler *hdl;
int (*thread_func)(void *data) = NULL;
int msp_hard;
int msp_family;
state->v4l2_std = V4L2_STD_NTSC;
state->audmode = V4L2_TUNER_MODE_STEREO;
- state->volume = 58880; /* 0db gain */
- state->balance = 32768; /* 0db gain */
- state->bass = 32768;
- state->treble = 32768;
- state->loudness = 0;
state->input = -1;
- state->muted = 0;
state->i2s_mode = 0;
init_waitqueue_head(&state->wq);
/* These are the reset input/output positions */
return -ENODEV;
}
- msp_set_audio(client);
-
msp_family = ((state->rev1 >> 4) & 0x0f) + 3;
msp_product = (state->rev2 >> 8) & 0xff;
msp_prod_hi = msp_product / 10;
state->opmode = OPMODE_MANUAL;
}
+ hdl = &state->hdl;
+ v4l2_ctrl_handler_init(hdl, 6);
+ if (state->has_sound_processing) {
+ v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_BASS, 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_TREBLE, 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 0);
+ }
+ state->volume = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 58880);
+ v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768);
+ state->muted = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ kfree(state);
+ return err;
+ }
+
+ v4l2_ctrl_cluster(2, &state->volume);
+ v4l2_ctrl_handler_setup(hdl);
+
/* hello world :-) */
v4l_info(client, "MSP%d4%02d%c-%c%d found @ 0x%x (%s)\n",
msp_family, msp_product,
}
msp_reset(client);
+ v4l2_ctrl_handler_free(&state->hdl);
kfree(state);
return 0;
}
#include <media/msp3400.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
/* ---------------------------------------------------------------------- */
struct msp_state {
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
int rev1, rev2;
int ident;
u8 has_nicam;
int audmode;
int rxsubchans;
- int volume, muted;
- int balance, loudness;
- int bass, treble;
+ struct {
+ /* volume cluster */
+ struct v4l2_ctrl *volume;
+ struct v4l2_ctrl *muted;
+ };
+
int scan_in_progress;
/* thread */
return container_of(sd, struct msp_state, sd);
}
+static inline struct msp_state *ctrl_to_state(struct v4l2_ctrl *ctrl)
+{
+ return container_of(ctrl->handler, struct msp_state, hdl);
+}
+
/* msp3400-driver.c */
int msp_write_dem(struct i2c_client *client, int addr, int val);
int msp_write_dsp(struct i2c_client *client, int addr, int val);
int msp_read_dsp(struct i2c_client *client, int addr);
int msp_reset(struct i2c_client *client);
void msp_set_scart(struct i2c_client *client, int in, int out);
-void msp_set_audio(struct i2c_client *client);
+void msp_update_volume(struct msp_state *state);
int msp_sleep(struct msp_state *state, int timeout);
/* msp3400-kthreads.c */
v4l_dbg(1, msp_debug, client,
"thread: no carrier scan\n");
state->scan_in_progress = 0;
- msp_set_audio(client);
+ msp_update_volume(state);
continue;
}
/* mute audio */
state->scan_in_progress = 1;
- msp_set_audio(client);
+ msp_update_volume(state);
msp3400c_set_mode(client, MSP_MODE_AM_DETECT);
val1 = val2 = 0;
/* unmute */
state->scan_in_progress = 0;
msp3400c_set_audmode(client);
- msp_set_audio(client);
+ msp_update_volume(state);
if (msp_debug)
msp3400c_print_mode(client);
v4l_dbg(1, msp_debug, client,
"thread: no carrier scan\n");
state->scan_in_progress = 0;
- msp_set_audio(client);
+ msp_update_volume(state);
continue;
}
/* mute audio */
state->scan_in_progress = 1;
- msp_set_audio(client);
+ msp_update_volume(state);
/* start autodetect. Note: autodetect is not supported for
NTSC-M and radio, hence we force the standard in those
/* unmute */
msp3400c_set_audmode(client);
state->scan_in_progress = 0;
- msp_set_audio(client);
+ msp_update_volume(state);
/* monitor tv audio mode, the first time don't wait
so long to get a quick stereo/bilingual result */
v4l_dbg(1, msp_debug, client,
"thread: no carrier scan\n");
state->scan_in_progress = 0;
- msp_set_audio(client);
+ msp_update_volume(state);
continue;
}
}
/* unmute: dispatch sound to scart output, set scart volume */
- msp_set_audio(client);
+ msp_update_volume(state);
/* restore ACB */
if (msp_write_dsp(client, 0x13, state->acb))
/*
- * Driver for MT9M111/MT9M112 CMOS Image Sensor from Micron
+ * Driver for MT9M111/MT9M112/MT9M131 CMOS Image Sensor from Micron/Aptina
*
* Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
*
#include <media/soc_camera.h>
/*
- * mt9m111 and mt9m112 i2c address is 0x5d or 0x48 (depending on SAddr pin)
+ * MT9M111, MT9M112 and MT9M131:
+ * i2c address is 0x48 or 0x5d (depending on SADDR pin)
* The platform has to define i2c_board_info and call i2c_register_board_info()
*/
-/* mt9m111: Sensor register addresses */
+/*
+ * Sensor core register addresses (0x000..0x0ff)
+ */
#define MT9M111_CHIP_VERSION 0x000
#define MT9M111_ROW_START 0x001
#define MT9M111_COLUMN_START 0x002
#define MT9M111_CTXT_CTRL_LED_FLASH_EN (1 << 2)
#define MT9M111_CTXT_CTRL_VBLANK_SEL_B (1 << 1)
#define MT9M111_CTXT_CTRL_HBLANK_SEL_B (1 << 0)
+
/*
- * mt9m111: Colorpipe register addresses (0x100..0x1ff)
+ * Colorpipe register addresses (0x100..0x1ff)
*/
#define MT9M111_OPER_MODE_CTRL 0x106
#define MT9M111_OUTPUT_FORMAT_CTRL 0x108
#define MT9M111_OUTFMT_SWAP_YCbCr_C_Y (1 << 1)
#define MT9M111_OUTFMT_SWAP_RGB_EVEN (1 << 1)
#define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr (1 << 0)
+
/*
- * mt9m111: Camera control register addresses (0x200..0x2ff not implemented)
+ * Camera control register addresses (0x200..0x2ff not implemented)
*/
#define reg_read(reg) mt9m111_reg_read(client, MT9M111_##reg)
struct mt9m111 {
struct v4l2_subdev subdev;
- int model; /* V4L2_IDENT_MT9M11x* codes from v4l2-chip-ident.h */
+ int model; /* V4L2_IDENT_MT9M111 or V4L2_IDENT_MT9M112 code
+ * from v4l2-chip-ident.h */
enum mt9m111_context context;
struct v4l2_rect rect;
const struct mt9m111_datafmt *fmt;
if (!ret)
ret = mt9m111_set_autoexposure(client, mt9m111->autoexposure);
if (ret)
- dev_err(&client->dev, "mt9m11x init failed: %d\n", ret);
+ dev_err(&client->dev, "mt9m111 init failed: %d\n", ret);
return ret;
}
mt9m111->swap_rgb_even_odd = 1;
mt9m111->swap_rgb_red_blue = 1;
- ret = mt9m111_init(client);
- if (ret)
- goto ei2c;
-
data = reg_read(CHIP_VERSION);
switch (data) {
- case 0x143a: /* MT9M111 */
+ case 0x143a: /* MT9M111 or MT9M131 */
mt9m111->model = V4L2_IDENT_MT9M111;
+ dev_info(&client->dev,
+ "Detected a MT9M111/MT9M131 chip ID %x\n", data);
break;
case 0x148c: /* MT9M112 */
mt9m111->model = V4L2_IDENT_MT9M112;
+ dev_info(&client->dev, "Detected a MT9M112 chip ID %x\n", data);
break;
default:
ret = -ENODEV;
dev_err(&client->dev,
- "No MT9M11x chip detected, register read %x\n", data);
+ "No MT9M111/MT9M112/MT9M131 chip detected register read %x\n",
+ data);
goto ei2c;
}
- dev_info(&client->dev, "Detected a MT9M11x chip ID %x\n", data);
+ ret = mt9m111_init(client);
ei2c:
return ret;
int ret;
if (!icd) {
- dev_err(&client->dev, "MT9M11x: missing soc-camera data!\n");
+ dev_err(&client->dev, "mt9m111: soc-camera data missing!\n");
return -EINVAL;
}
icl = to_soc_camera_link(icd);
if (!icl) {
- dev_err(&client->dev, "MT9M11x driver needs platform data\n");
+ dev_err(&client->dev, "mt9m111: driver needs platform data\n");
return -EINVAL;
}
module_init(mt9m111_mod_init);
module_exit(mt9m111_mod_exit);
-MODULE_DESCRIPTION("Micron MT9M111/MT9M112 Camera driver");
+MODULE_DESCRIPTION("Micron/Aptina MT9M111/MT9M112/MT9M131 Camera driver");
MODULE_AUTHOR("Robert Jarzmik");
MODULE_LICENSE("GPL");
if (ret < 0)
return ret;
+ if (common_flags & SOCAM_PCLK_SAMPLE_RISING)
+ csicr1 |= CSICR1_REDGE;
if (common_flags & SOCAM_PCLK_SAMPLE_FALLING)
csicr1 |= CSICR1_INV_PCLK;
if (common_flags & SOCAM_VSYNC_ACTIVE_HIGH)
buf = list_entry(pcdev->capture.next,
struct mx2_buffer, vb.queue);
- buf->bufnum = bufnum;
+ buf->bufnum = !bufnum;
list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
u32 *num_ptr)
{
u32 result = 0;
- u32 val;
- int ch;
int radix = 10;
if ((count >= 2) && (buf[0] == '0') &&
((buf[1] == 'x') || (buf[1] == 'X'))) {
}
while (count--) {
- ch = *buf++;
- if ((ch >= '0') && (ch <= '9')) {
- val = ch - '0';
- } else if ((ch >= 'a') && (ch <= 'f')) {
- val = ch - 'a' + 10;
- } else if ((ch >= 'A') && (ch <= 'F')) {
- val = ch - 'A' + 10;
- } else {
+ int val = hex_to_bin(*buf++);
+ if (val < 0 || val >= radix)
return -EINVAL;
- }
- if (val >= radix) return -EINVAL;
result *= radix;
result += val;
}
--- /dev/null
+
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) := s5p-fimc.o
+s5p-fimc-y := fimc-core.o fimc-reg.o
--- /dev/null
+/*
+ * S5P camera interface (video postprocessor) driver
+ *
+ * Copyright (c) 2010 Samsung Electronics
+ *
+ * Sylwester Nawrocki, <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation, either version 2 of the License,
+ * or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf-dma-contig.h>
+
+#include "fimc-core.h"
+
+static char *fimc_clock_name[NUM_FIMC_CLOCKS] = { "sclk_fimc", "fimc" };
+
+static struct fimc_fmt fimc_formats[] = {
+ {
+ .name = "RGB565",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .depth = 16,
+ .color = S5P_FIMC_RGB565,
+ .buff_cnt = 1,
+ .planes_cnt = 1
+ }, {
+ .name = "BGR666",
+ .fourcc = V4L2_PIX_FMT_BGR666,
+ .depth = 32,
+ .color = S5P_FIMC_RGB666,
+ .buff_cnt = 1,
+ .planes_cnt = 1
+ }, {
+ .name = "XRGB-8-8-8-8, 24 bpp",
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .depth = 32,
+ .color = S5P_FIMC_RGB888,
+ .buff_cnt = 1,
+ .planes_cnt = 1
+ }, {
+ .name = "YUV 4:2:2 packed, YCbYCr",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .color = S5P_FIMC_YCBYCR422,
+ .buff_cnt = 1,
+ .planes_cnt = 1
+ }, {
+ .name = "YUV 4:2:2 packed, CbYCrY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ .color = S5P_FIMC_CBYCRY422,
+ .buff_cnt = 1,
+ .planes_cnt = 1
+ }, {
+ .name = "YUV 4:2:2 packed, CrYCbY",
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .depth = 16,
+ .color = S5P_FIMC_CRYCBY422,
+ .buff_cnt = 1,
+ .planes_cnt = 1
+ }, {
+ .name = "YUV 4:2:2 packed, YCrYCb",
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .depth = 16,
+ .color = S5P_FIMC_YCRYCB422,
+ .buff_cnt = 1,
+ .planes_cnt = 1
+ }, {
+ .name = "YUV 4:2:2 planar, Y/Cb/Cr",
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .depth = 12,
+ .color = S5P_FIMC_YCBCR422,
+ .buff_cnt = 1,
+ .planes_cnt = 3
+ }, {
+ .name = "YUV 4:2:2 planar, Y/CbCr",
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .depth = 16,
+ .color = S5P_FIMC_YCBCR422,
+ .buff_cnt = 1,
+ .planes_cnt = 2
+ }, {
+ .name = "YUV 4:2:2 planar, Y/CrCb",
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .depth = 16,
+ .color = S5P_FIMC_RGB565,
+ .buff_cnt = 1,
+ .planes_cnt = 2
+ }, {
+ .name = "YUV 4:2:0 planar, YCbCr",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .depth = 12,
+ .color = S5P_FIMC_YCBCR420,
+ .buff_cnt = 1,
+ .planes_cnt = 3
+ }, {
+ .name = "YUV 4:2:0 planar, Y/CbCr",
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .depth = 12,
+ .color = S5P_FIMC_YCBCR420,
+ .buff_cnt = 1,
+ .planes_cnt = 2
+ }
+ };
+
+static struct v4l2_queryctrl fimc_ctrls[] = {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Horizontal flip",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_ROTATE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Rotation (CCW)",
+ .minimum = 0,
+ .maximum = 270,
+ .step = 90,
+ .default_value = 0,
+ },
+};
+
+
+static struct v4l2_queryctrl *get_ctrl(int id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fimc_ctrls); ++i)
+ if (id == fimc_ctrls[i].id)
+ return &fimc_ctrls[i];
+ return NULL;
+}
+
+static int fimc_check_scaler_ratio(struct v4l2_rect *r, struct fimc_frame *f)
+{
+ if (r->width > f->width) {
+ if (f->width > (r->width * SCALER_MAX_HRATIO))
+ return -EINVAL;
+ } else {
+ if ((f->width * SCALER_MAX_HRATIO) < r->width)
+ return -EINVAL;
+ }
+
+ if (r->height > f->height) {
+ if (f->height > (r->height * SCALER_MAX_VRATIO))
+ return -EINVAL;
+ } else {
+ if ((f->height * SCALER_MAX_VRATIO) < r->height)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift)
+{
+ if (src >= tar * 64) {
+ return -EINVAL;
+ } else if (src >= tar * 32) {
+ *ratio = 32;
+ *shift = 5;
+ } else if (src >= tar * 16) {
+ *ratio = 16;
+ *shift = 4;
+ } else if (src >= tar * 8) {
+ *ratio = 8;
+ *shift = 3;
+ } else if (src >= tar * 4) {
+ *ratio = 4;
+ *shift = 2;
+ } else if (src >= tar * 2) {
+ *ratio = 2;
+ *shift = 1;
+ } else {
+ *ratio = 1;
+ *shift = 0;
+ }
+
+ return 0;
+}
+
+static int fimc_set_scaler_info(struct fimc_ctx *ctx)
+{
+ struct fimc_scaler *sc = &ctx->scaler;
+ struct fimc_frame *s_frame = &ctx->s_frame;
+ struct fimc_frame *d_frame = &ctx->d_frame;
+ int tx, ty, sx, sy;
+ int ret;
+
+ tx = d_frame->width;
+ ty = d_frame->height;
+ if (tx <= 0 || ty <= 0) {
+ v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
+ "invalid target size: %d x %d", tx, ty);
+ return -EINVAL;
+ }
+
+ sx = s_frame->width;
+ sy = s_frame->height;
+ if (sx <= 0 || sy <= 0) {
+ err("invalid source size: %d x %d", sx, sy);
+ return -EINVAL;
+ }
+
+ sc->real_width = sx;
+ sc->real_height = sy;
+ dbg("sx= %d, sy= %d, tx= %d, ty= %d", sx, sy, tx, ty);
+
+ ret = fimc_get_scaler_factor(sx, tx, &sc->pre_hratio, &sc->hfactor);
+ if (ret)
+ return ret;
+
+ ret = fimc_get_scaler_factor(sy, ty, &sc->pre_vratio, &sc->vfactor);
+ if (ret)
+ return ret;
+
+ sc->pre_dst_width = sx / sc->pre_hratio;
+ sc->pre_dst_height = sy / sc->pre_vratio;
+
+ sc->main_hratio = (sx << 8) / (tx << sc->hfactor);
+ sc->main_vratio = (sy << 8) / (ty << sc->vfactor);
+
+ sc->scaleup_h = (tx >= sx) ? 1 : 0;
+ sc->scaleup_v = (ty >= sy) ? 1 : 0;
+
+ /* check to see if input and output size/format differ */
+ if (s_frame->fmt->color == d_frame->fmt->color
+ && s_frame->width == d_frame->width
+ && s_frame->height == d_frame->height)
+ sc->copy_mode = 1;
+ else
+ sc->copy_mode = 0;
+
+ return 0;
+}
+
+
+static irqreturn_t fimc_isr(int irq, void *priv)
+{
+ struct fimc_vid_buffer *src_buf, *dst_buf;
+ struct fimc_dev *fimc = (struct fimc_dev *)priv;
+ struct fimc_ctx *ctx;
+
+ BUG_ON(!fimc);
+ fimc_hw_clear_irq(fimc);
+
+ spin_lock(&fimc->slock);
+
+ if (test_and_clear_bit(ST_M2M_PEND, &fimc->state)) {
+ ctx = v4l2_m2m_get_curr_priv(fimc->m2m.m2m_dev);
+ if (!ctx || !ctx->m2m_ctx)
+ goto isr_unlock;
+ src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+ if (src_buf && dst_buf) {
+ spin_lock(&fimc->irqlock);
+ src_buf->vb.state = dst_buf->vb.state = VIDEOBUF_DONE;
+ wake_up(&src_buf->vb.done);
+ wake_up(&dst_buf->vb.done);
+ spin_unlock(&fimc->irqlock);
+ v4l2_m2m_job_finish(fimc->m2m.m2m_dev, ctx->m2m_ctx);
+ }
+ }
+
+isr_unlock:
+ spin_unlock(&fimc->slock);
+ return IRQ_HANDLED;
+}
+
+/* The color format (planes_cnt, buff_cnt) must be already configured. */
+static int fimc_prepare_addr(struct fimc_ctx *ctx,
+ struct fimc_vid_buffer *buf, enum v4l2_buf_type type)
+{
+ struct fimc_frame *frame;
+ struct fimc_addr *paddr;
+ u32 pix_size;
+ int ret = 0;
+
+ frame = ctx_m2m_get_frame(ctx, type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+ paddr = &frame->paddr;
+
+ if (!buf)
+ return -EINVAL;
+
+ pix_size = frame->width * frame->height;
+
+ dbg("buff_cnt= %d, planes_cnt= %d, frame->size= %d, pix_size= %d",
+ frame->fmt->buff_cnt, frame->fmt->planes_cnt,
+ frame->size, pix_size);
+
+ if (frame->fmt->buff_cnt == 1) {
+ paddr->y = videobuf_to_dma_contig(&buf->vb);
+ switch (frame->fmt->planes_cnt) {
+ case 1:
+ paddr->cb = 0;
+ paddr->cr = 0;
+ break;
+ case 2:
+ /* decompose Y into Y/Cb */
+ paddr->cb = (u32)(paddr->y + pix_size);
+ paddr->cr = 0;
+ break;
+ case 3:
+ paddr->cb = (u32)(paddr->y + pix_size);
+ /* decompose Y into Y/Cb/Cr */
+ if (S5P_FIMC_YCBCR420 == frame->fmt->color)
+ paddr->cr = (u32)(paddr->cb
+ + (pix_size >> 2));
+ else /* 422 */
+ paddr->cr = (u32)(paddr->cb
+ + (pix_size >> 1));
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ dbg("PHYS_ADDR: type= %d, y= 0x%X cb= 0x%X cr= 0x%X ret= %d",
+ type, paddr->y, paddr->cb, paddr->cr, ret);
+
+ return ret;
+}
+
+/* Set order for 1 and 2 plane YCBCR 4:2:2 formats. */
+static void fimc_set_yuv_order(struct fimc_ctx *ctx)
+{
+ /* The one only mode supported in SoC. */
+ ctx->in_order_2p = S5P_FIMC_LSB_CRCB;
+ ctx->out_order_2p = S5P_FIMC_LSB_CRCB;
+
+ /* Set order for 1 plane input formats. */
+ switch (ctx->s_frame.fmt->color) {
+ case S5P_FIMC_YCRYCB422:
+ ctx->in_order_1p = S5P_FIMC_IN_YCRYCB;
+ break;
+ case S5P_FIMC_CBYCRY422:
+ ctx->in_order_1p = S5P_FIMC_IN_CBYCRY;
+ break;
+ case S5P_FIMC_CRYCBY422:
+ ctx->in_order_1p = S5P_FIMC_IN_CRYCBY;
+ break;
+ case S5P_FIMC_YCBYCR422:
+ default:
+ ctx->in_order_1p = S5P_FIMC_IN_YCBYCR;
+ break;
+ }
+ dbg("ctx->in_order_1p= %d", ctx->in_order_1p);
+
+ switch (ctx->d_frame.fmt->color) {
+ case S5P_FIMC_YCRYCB422:
+ ctx->out_order_1p = S5P_FIMC_OUT_YCRYCB;
+ break;
+ case S5P_FIMC_CBYCRY422:
+ ctx->out_order_1p = S5P_FIMC_OUT_CBYCRY;
+ break;
+ case S5P_FIMC_CRYCBY422:
+ ctx->out_order_1p = S5P_FIMC_OUT_CRYCBY;
+ break;
+ case S5P_FIMC_YCBYCR422:
+ default:
+ ctx->out_order_1p = S5P_FIMC_OUT_YCBYCR;
+ break;
+ }
+ dbg("ctx->out_order_1p= %d", ctx->out_order_1p);
+}
+
+/**
+ * fimc_prepare_config - check dimensions, operation and color mode
+ * and pre-calculate offset and the scaling coefficients.
+ *
+ * @ctx: hardware context information
+ * @flags: flags indicating which parameters to check/update
+ *
+ * Return: 0 if dimensions are valid or non zero otherwise.
+ */
+static int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags)
+{
+ struct fimc_frame *s_frame, *d_frame;
+ struct fimc_vid_buffer *buf = NULL;
+ struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
+ int ret = 0;
+
+ s_frame = &ctx->s_frame;
+ d_frame = &ctx->d_frame;
+
+ if (flags & FIMC_PARAMS) {
+ if ((ctx->out_path == FIMC_DMA) &&
+ (ctx->rotation == 90 || ctx->rotation == 270)) {
+ swap(d_frame->f_width, d_frame->f_height);
+ swap(d_frame->width, d_frame->height);
+ }
+
+ /* Prepare the output offset ratios for scaler. */
+ d_frame->dma_offset.y_h = d_frame->offs_h;
+ if (!variant->pix_hoff)
+ d_frame->dma_offset.y_h *= (d_frame->fmt->depth >> 3);
+
+ d_frame->dma_offset.y_v = d_frame->offs_v;
+
+ d_frame->dma_offset.cb_h = d_frame->offs_h;
+ d_frame->dma_offset.cb_v = d_frame->offs_v;
+
+ d_frame->dma_offset.cr_h = d_frame->offs_h;
+ d_frame->dma_offset.cr_v = d_frame->offs_v;
+
+ if (!variant->pix_hoff && d_frame->fmt->planes_cnt == 3) {
+ d_frame->dma_offset.cb_h >>= 1;
+ d_frame->dma_offset.cb_v >>= 1;
+ d_frame->dma_offset.cr_h >>= 1;
+ d_frame->dma_offset.cr_v >>= 1;
+ }
+
+ dbg("out offset: color= %d, y_h= %d, y_v= %d",
+ d_frame->fmt->color,
+ d_frame->dma_offset.y_h, d_frame->dma_offset.y_v);
+
+ /* Prepare the input offset ratios for scaler. */
+ s_frame->dma_offset.y_h = s_frame->offs_h;
+ if (!variant->pix_hoff)
+ s_frame->dma_offset.y_h *= (s_frame->fmt->depth >> 3);
+ s_frame->dma_offset.y_v = s_frame->offs_v;
+
+ s_frame->dma_offset.cb_h = s_frame->offs_h;
+ s_frame->dma_offset.cb_v = s_frame->offs_v;
+
+ s_frame->dma_offset.cr_h = s_frame->offs_h;
+ s_frame->dma_offset.cr_v = s_frame->offs_v;
+
+ if (!variant->pix_hoff && s_frame->fmt->planes_cnt == 3) {
+ s_frame->dma_offset.cb_h >>= 1;
+ s_frame->dma_offset.cb_v >>= 1;
+ s_frame->dma_offset.cr_h >>= 1;
+ s_frame->dma_offset.cr_v >>= 1;
+ }
+
+ dbg("in offset: color= %d, y_h= %d, y_v= %d",
+ s_frame->fmt->color, s_frame->dma_offset.y_h,
+ s_frame->dma_offset.y_v);
+
+ fimc_set_yuv_order(ctx);
+
+ /* Check against the scaler ratio. */
+ if (s_frame->height > (SCALER_MAX_VRATIO * d_frame->height) ||
+ s_frame->width > (SCALER_MAX_HRATIO * d_frame->width)) {
+ err("out of scaler range");
+ return -EINVAL;
+ }
+ }
+
+ /* Input DMA mode is not allowed when the scaler is disabled. */
+ ctx->scaler.enabled = 1;
+
+ if (flags & FIMC_SRC_ADDR) {
+ buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ ret = fimc_prepare_addr(ctx, buf,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ if (ret)
+ return ret;
+ }
+
+ if (flags & FIMC_DST_ADDR) {
+ buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ ret = fimc_prepare_addr(ctx, buf,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ }
+
+ return ret;
+}
+
+static void fimc_dma_run(void *priv)
+{
+ struct fimc_ctx *ctx = priv;
+ struct fimc_dev *fimc;
+ unsigned long flags;
+ u32 ret;
+
+ if (WARN(!ctx, "null hardware context"))
+ return;
+
+ fimc = ctx->fimc_dev;
+
+ spin_lock_irqsave(&ctx->slock, flags);
+ set_bit(ST_M2M_PEND, &fimc->state);
+
+ ctx->state |= (FIMC_SRC_ADDR | FIMC_DST_ADDR);
+ ret = fimc_prepare_config(ctx, ctx->state);
+ if (ret) {
+ err("general configuration error");
+ goto dma_unlock;
+ }
+
+ if (fimc->m2m.ctx != ctx)
+ ctx->state |= FIMC_PARAMS;
+
+ fimc_hw_set_input_addr(fimc, &ctx->s_frame.paddr);
+
+ if (ctx->state & FIMC_PARAMS) {
+ fimc_hw_set_input_path(ctx);
+ fimc_hw_set_in_dma(ctx);
+ if (fimc_set_scaler_info(ctx)) {
+ err("scaler configuration error");
+ goto dma_unlock;
+ }
+ fimc_hw_set_prescaler(ctx);
+ fimc_hw_set_scaler(ctx);
+ fimc_hw_set_target_format(ctx);
+ fimc_hw_set_rotation(ctx);
+ fimc_hw_set_effect(ctx);
+ }
+
+ fimc_hw_set_output_path(ctx);
+ if (ctx->state & (FIMC_DST_ADDR | FIMC_PARAMS))
+ fimc_hw_set_output_addr(fimc, &ctx->d_frame.paddr);
+
+ if (ctx->state & FIMC_PARAMS)
+ fimc_hw_set_out_dma(ctx);
+
+ if (ctx->scaler.enabled)
+ fimc_hw_start_scaler(fimc);
+ fimc_hw_en_capture(ctx);
+
+ ctx->state = 0;
+ fimc_hw_start_in_dma(fimc);
+
+ fimc->m2m.ctx = ctx;
+
+dma_unlock:
+ spin_unlock_irqrestore(&ctx->slock, flags);
+}
+
+static void fimc_job_abort(void *priv)
+{
+ /* Nothing done in job_abort. */
+}
+
+static void fimc_buf_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ videobuf_dma_contig_free(vq, vb);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int fimc_buf_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct fimc_ctx *ctx = vq->priv_data;
+ struct fimc_frame *frame;
+
+ frame = ctx_m2m_get_frame(ctx, vq->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ *size = (frame->width * frame->height * frame->fmt->depth) >> 3;
+ if (0 == *count)
+ *count = 1;
+ return 0;
+}
+
+static int fimc_buf_prepare(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct fimc_ctx *ctx = vq->priv_data;
+ struct v4l2_device *v4l2_dev = &ctx->fimc_dev->m2m.v4l2_dev;
+ struct fimc_frame *frame;
+ int ret;
+
+ frame = ctx_m2m_get_frame(ctx, vq->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ if (vb->baddr) {
+ if (vb->bsize < frame->size) {
+ v4l2_err(v4l2_dev,
+ "User-provided buffer too small (%d < %d)\n",
+ vb->bsize, frame->size);
+ WARN_ON(1);
+ return -EINVAL;
+ }
+ } else if (vb->state != VIDEOBUF_NEEDS_INIT
+ && vb->bsize < frame->size) {
+ return -EINVAL;
+ }
+
+ vb->width = frame->width;
+ vb->height = frame->height;
+ vb->bytesperline = (frame->width * frame->fmt->depth) >> 3;
+ vb->size = frame->size;
+ vb->field = field;
+
+ if (VIDEOBUF_NEEDS_INIT == vb->state) {
+ ret = videobuf_iolock(vq, vb, NULL);
+ if (ret) {
+ v4l2_err(v4l2_dev, "Iolock failed\n");
+ fimc_buf_release(vq, vb);
+ return ret;
+ }
+ }
+ vb->state = VIDEOBUF_PREPARED;
+
+ return 0;
+}
+
+static void fimc_buf_queue(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct fimc_ctx *ctx = vq->priv_data;
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vq, vb);
+}
+
+static struct videobuf_queue_ops fimc_qops = {
+ .buf_setup = fimc_buf_setup,
+ .buf_prepare = fimc_buf_prepare,
+ .buf_queue = fimc_buf_queue,
+ .buf_release = fimc_buf_release,
+};
+
+static int fimc_m2m_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct fimc_ctx *ctx = file->private_data;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+
+ strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
+ strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
+ cap->bus_info[0] = 0;
+ cap->version = KERNEL_VERSION(1, 0, 0);
+ cap->capabilities = V4L2_CAP_STREAMING |
+ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT;
+
+ return 0;
+}
+
+static int fimc_m2m_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct fimc_fmt *fmt;
+
+ if (f->index >= ARRAY_SIZE(fimc_formats))
+ return -EINVAL;
+
+ fmt = &fimc_formats[f->index];
+ strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+ f->pixelformat = fmt->fourcc;
+ return 0;
+}
+
+static int fimc_m2m_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct fimc_ctx *ctx = priv;
+ struct fimc_frame *frame;
+
+ frame = ctx_m2m_get_frame(ctx, f->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ f->fmt.pix.width = frame->width;
+ f->fmt.pix.height = frame->height;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.pixelformat = frame->fmt->fourcc;
+
+ return 0;
+}
+
+static struct fimc_fmt *find_format(struct v4l2_format *f)
+{
+ struct fimc_fmt *fmt;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(fimc_formats); ++i) {
+ fmt = &fimc_formats[i];
+ if (fmt->fourcc == f->fmt.pix.pixelformat)
+ break;
+ }
+ if (i == ARRAY_SIZE(fimc_formats))
+ return NULL;
+
+ return fmt;
+}
+
+static int fimc_m2m_try_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct fimc_fmt *fmt;
+ u32 max_width, max_height, mod_x, mod_y;
+ struct fimc_ctx *ctx = priv;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct samsung_fimc_variant *variant = fimc->variant;
+
+ fmt = find_format(f);
+ if (!fmt) {
+ v4l2_err(&fimc->m2m.v4l2_dev,
+ "Fourcc format (0x%X) invalid.\n", pix->pixelformat);
+ return -EINVAL;
+ }
+
+ if (pix->field == V4L2_FIELD_ANY)
+ pix->field = V4L2_FIELD_NONE;
+ else if (V4L2_FIELD_NONE != pix->field)
+ return -EINVAL;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ max_width = variant->scaler_dis_w;
+ max_height = variant->scaler_dis_w;
+ mod_x = variant->min_inp_pixsize;
+ mod_y = variant->min_inp_pixsize;
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ max_width = variant->out_rot_dis_w;
+ max_height = variant->out_rot_dis_w;
+ mod_x = variant->min_out_pixsize;
+ mod_y = variant->min_out_pixsize;
+ } else {
+ err("Wrong stream type (%d)", f->type);
+ return -EINVAL;
+ }
+
+ dbg("max_w= %d, max_h= %d", max_width, max_height);
+
+ if (pix->height > max_height)
+ pix->height = max_height;
+ if (pix->width > max_width)
+ pix->width = max_width;
+
+ if (tiled_fmt(fmt)) {
+ mod_x = 64; /* 64x32 tile */
+ mod_y = 32;
+ }
+
+ dbg("mod_x= 0x%X, mod_y= 0x%X", mod_x, mod_y);
+
+ pix->width = (pix->width == 0) ? mod_x : ALIGN(pix->width, mod_x);
+ pix->height = (pix->height == 0) ? mod_y : ALIGN(pix->height, mod_y);
+
+ if (pix->bytesperline == 0 ||
+ pix->bytesperline * 8 / fmt->depth > pix->width)
+ pix->bytesperline = (pix->width * fmt->depth) >> 3;
+
+ if (pix->sizeimage == 0)
+ pix->sizeimage = pix->height * pix->bytesperline;
+
+ dbg("pix->bytesperline= %d, fmt->depth= %d",
+ pix->bytesperline, fmt->depth);
+
+ return 0;
+}
+
+
+static int fimc_m2m_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct fimc_ctx *ctx = priv;
+ struct v4l2_device *v4l2_dev = &ctx->fimc_dev->m2m.v4l2_dev;
+ struct videobuf_queue *src_vq, *dst_vq;
+ struct fimc_frame *frame;
+ struct v4l2_pix_format *pix;
+ unsigned long flags;
+ int ret = 0;
+
+ BUG_ON(!ctx);
+
+ ret = fimc_m2m_try_fmt(file, priv, f);
+ if (ret)
+ return ret;
+
+ mutex_lock(&ctx->fimc_dev->lock);
+
+ src_vq = v4l2_m2m_get_src_vq(ctx->m2m_ctx);
+ dst_vq = v4l2_m2m_get_dst_vq(ctx->m2m_ctx);
+
+ mutex_lock(&src_vq->vb_lock);
+ mutex_lock(&dst_vq->vb_lock);
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ if (videobuf_queue_is_busy(src_vq)) {
+ v4l2_err(v4l2_dev, "%s queue busy\n", __func__);
+ ret = -EBUSY;
+ goto s_fmt_out;
+ }
+ frame = &ctx->s_frame;
+ spin_lock_irqsave(&ctx->slock, flags);
+ ctx->state |= FIMC_SRC_FMT;
+ spin_unlock_irqrestore(&ctx->slock, flags);
+
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (videobuf_queue_is_busy(dst_vq)) {
+ v4l2_err(v4l2_dev, "%s queue busy\n", __func__);
+ ret = -EBUSY;
+ goto s_fmt_out;
+ }
+ frame = &ctx->d_frame;
+ spin_lock_irqsave(&ctx->slock, flags);
+ ctx->state |= FIMC_DST_FMT;
+ spin_unlock_irqrestore(&ctx->slock, flags);
+ } else {
+ v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
+ "Wrong buffer/video queue type (%d)\n", f->type);
+ return -EINVAL;
+ }
+
+ pix = &f->fmt.pix;
+ frame->fmt = find_format(f);
+ if (!frame->fmt) {
+ ret = -EINVAL;
+ goto s_fmt_out;
+ }
+
+ frame->f_width = pix->bytesperline * 8 / frame->fmt->depth;
+ frame->f_height = pix->sizeimage/pix->bytesperline;
+ frame->width = pix->width;
+ frame->height = pix->height;
+ frame->o_width = pix->width;
+ frame->o_height = pix->height;
+ frame->offs_h = 0;
+ frame->offs_v = 0;
+ frame->size = (pix->width * pix->height * frame->fmt->depth) >> 3;
+ src_vq->field = dst_vq->field = pix->field;
+ spin_lock_irqsave(&ctx->slock, flags);
+ ctx->state |= FIMC_PARAMS;
+ spin_unlock_irqrestore(&ctx->slock, flags);
+
+ dbg("f_width= %d, f_height= %d", frame->f_width, frame->f_height);
+
+s_fmt_out:
+ mutex_unlock(&dst_vq->vb_lock);
+ mutex_unlock(&src_vq->vb_lock);
+ mutex_unlock(&ctx->fimc_dev->lock);
+ return ret;
+}
+
+static int fimc_m2m_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct fimc_ctx *ctx = priv;
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int fimc_m2m_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_ctx *ctx = priv;
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int fimc_m2m_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_ctx *ctx = priv;
+
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int fimc_m2m_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_ctx *ctx = priv;
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int fimc_m2m_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct fimc_ctx *ctx = priv;
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int fimc_m2m_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct fimc_ctx *ctx = priv;
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+int fimc_m2m_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct v4l2_queryctrl *c;
+ c = get_ctrl(qc->id);
+ if (!c)
+ return -EINVAL;
+ *qc = *c;
+ return 0;
+}
+
+int fimc_m2m_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct fimc_ctx *ctx = priv;
+
+ switch (ctrl->id) {
+ case V4L2_CID_HFLIP:
+ ctrl->value = (FLIP_X_AXIS & ctx->flip) ? 1 : 0;
+ break;
+ case V4L2_CID_VFLIP:
+ ctrl->value = (FLIP_Y_AXIS & ctx->flip) ? 1 : 0;
+ break;
+ case V4L2_CID_ROTATE:
+ ctrl->value = ctx->rotation;
+ break;
+ default:
+ v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, "Invalid control\n");
+ return -EINVAL;
+ }
+ dbg("ctrl->value= %d", ctrl->value);
+ return 0;
+}
+
+static int check_ctrl_val(struct fimc_ctx *ctx,
+ struct v4l2_control *ctrl)
+{
+ struct v4l2_queryctrl *c;
+ c = get_ctrl(ctrl->id);
+ if (!c)
+ return -EINVAL;
+
+ if (ctrl->value < c->minimum || ctrl->value > c->maximum
+ || (c->step != 0 && ctrl->value % c->step != 0)) {
+ v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
+ "Invalid control value\n");
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+int fimc_m2m_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct fimc_ctx *ctx = priv;
+ struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
+ unsigned long flags;
+ int ret = 0;
+
+ ret = check_ctrl_val(ctx, ctrl);
+ if (ret)
+ return ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_HFLIP:
+ if (ctx->rotation != 0)
+ return 0;
+ if (ctrl->value)
+ ctx->flip |= FLIP_X_AXIS;
+ else
+ ctx->flip &= ~FLIP_X_AXIS;
+ break;
+
+ case V4L2_CID_VFLIP:
+ if (ctx->rotation != 0)
+ return 0;
+ if (ctrl->value)
+ ctx->flip |= FLIP_Y_AXIS;
+ else
+ ctx->flip &= ~FLIP_Y_AXIS;
+ break;
+
+ case V4L2_CID_ROTATE:
+ if (ctrl->value == 90 || ctrl->value == 270) {
+ if (ctx->out_path == FIMC_LCDFIFO &&
+ !variant->has_inp_rot) {
+ return -EINVAL;
+ } else if (ctx->in_path == FIMC_DMA &&
+ !variant->has_out_rot) {
+ return -EINVAL;
+ }
+ }
+ ctx->rotation = ctrl->value;
+ if (ctrl->value == 180)
+ ctx->flip = FLIP_XY_AXIS;
+ break;
+
+ default:
+ v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev, "Invalid control\n");
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&ctx->slock, flags);
+ ctx->state |= FIMC_PARAMS;
+ spin_unlock_irqrestore(&ctx->slock, flags);
+ return 0;
+}
+
+
+static int fimc_m2m_cropcap(struct file *file, void *fh,
+ struct v4l2_cropcap *cr)
+{
+ struct fimc_frame *frame;
+ struct fimc_ctx *ctx = fh;
+
+ frame = ctx_m2m_get_frame(ctx, cr->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ cr->bounds.left = 0;
+ cr->bounds.top = 0;
+ cr->bounds.width = frame->f_width;
+ cr->bounds.height = frame->f_height;
+ cr->defrect.left = frame->offs_h;
+ cr->defrect.top = frame->offs_v;
+ cr->defrect.width = frame->o_width;
+ cr->defrect.height = frame->o_height;
+ return 0;
+}
+
+static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
+{
+ struct fimc_frame *frame;
+ struct fimc_ctx *ctx = file->private_data;
+
+ frame = ctx_m2m_get_frame(ctx, cr->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ cr->c.left = frame->offs_h;
+ cr->c.top = frame->offs_v;
+ cr->c.width = frame->width;
+ cr->c.height = frame->height;
+
+ return 0;
+}
+
+static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
+{
+ struct fimc_ctx *ctx = file->private_data;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ unsigned long flags;
+ struct fimc_frame *f;
+ u32 min_size;
+ int ret = 0;
+
+ if (cr->c.top < 0 || cr->c.left < 0) {
+ v4l2_err(&fimc->m2m.v4l2_dev,
+ "doesn't support negative values for top & left\n");
+ return -EINVAL;
+ }
+
+ if (cr->c.width <= 0 || cr->c.height <= 0) {
+ v4l2_err(&fimc->m2m.v4l2_dev,
+ "crop width and height must be greater than 0\n");
+ return -EINVAL;
+ }
+
+ f = ctx_m2m_get_frame(ctx, cr->type);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ /* Adjust to required pixel boundary. */
+ min_size = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ?
+ fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize;
+
+ cr->c.width = round_down(cr->c.width, min_size);
+ cr->c.height = round_down(cr->c.height, min_size);
+ cr->c.left = round_down(cr->c.left + 1, min_size);
+ cr->c.top = round_down(cr->c.top + 1, min_size);
+
+ if ((cr->c.left + cr->c.width > f->o_width)
+ || (cr->c.top + cr->c.height > f->o_height)) {
+ v4l2_err(&fimc->m2m.v4l2_dev, "Error in S_CROP params\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&ctx->slock, flags);
+ if ((ctx->state & FIMC_SRC_FMT) && (ctx->state & FIMC_DST_FMT)) {
+ /* Check for the pixel scaling ratio when cropping input img. */
+ if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ ret = fimc_check_scaler_ratio(&cr->c, &ctx->d_frame);
+ else if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ ret = fimc_check_scaler_ratio(&cr->c, &ctx->s_frame);
+
+ if (ret) {
+ spin_unlock_irqrestore(&ctx->slock, flags);
+ v4l2_err(&fimc->m2m.v4l2_dev, "Out of scaler range");
+ return -EINVAL;
+ }
+ }
+ ctx->state |= FIMC_PARAMS;
+ spin_unlock_irqrestore(&ctx->slock, flags);
+
+ f->offs_h = cr->c.left;
+ f->offs_v = cr->c.top;
+ f->width = cr->c.width;
+ f->height = cr->c.height;
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
+ .vidioc_querycap = fimc_m2m_querycap,
+
+ .vidioc_enum_fmt_vid_cap = fimc_m2m_enum_fmt,
+ .vidioc_enum_fmt_vid_out = fimc_m2m_enum_fmt,
+
+ .vidioc_g_fmt_vid_cap = fimc_m2m_g_fmt,
+ .vidioc_g_fmt_vid_out = fimc_m2m_g_fmt,
+
+ .vidioc_try_fmt_vid_cap = fimc_m2m_try_fmt,
+ .vidioc_try_fmt_vid_out = fimc_m2m_try_fmt,
+
+ .vidioc_s_fmt_vid_cap = fimc_m2m_s_fmt,
+ .vidioc_s_fmt_vid_out = fimc_m2m_s_fmt,
+
+ .vidioc_reqbufs = fimc_m2m_reqbufs,
+ .vidioc_querybuf = fimc_m2m_querybuf,
+
+ .vidioc_qbuf = fimc_m2m_qbuf,
+ .vidioc_dqbuf = fimc_m2m_dqbuf,
+
+ .vidioc_streamon = fimc_m2m_streamon,
+ .vidioc_streamoff = fimc_m2m_streamoff,
+
+ .vidioc_queryctrl = fimc_m2m_queryctrl,
+ .vidioc_g_ctrl = fimc_m2m_g_ctrl,
+ .vidioc_s_ctrl = fimc_m2m_s_ctrl,
+
+ .vidioc_g_crop = fimc_m2m_g_crop,
+ .vidioc_s_crop = fimc_m2m_s_crop,
+ .vidioc_cropcap = fimc_m2m_cropcap
+
+};
+
+static void queue_init(void *priv, struct videobuf_queue *vq,
+ enum v4l2_buf_type type)
+{
+ struct fimc_ctx *ctx = priv;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+
+ videobuf_queue_dma_contig_init(vq, &fimc_qops,
+ fimc->m2m.v4l2_dev.dev,
+ &fimc->irqlock, type, V4L2_FIELD_NONE,
+ sizeof(struct fimc_vid_buffer), priv);
+}
+
+static int fimc_m2m_open(struct file *file)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_ctx *ctx = NULL;
+ int err = 0;
+
+ mutex_lock(&fimc->lock);
+ fimc->m2m.refcnt++;
+ set_bit(ST_OUTDMA_RUN, &fimc->state);
+ mutex_unlock(&fimc->lock);
+
+
+ ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ file->private_data = ctx;
+ ctx->fimc_dev = fimc;
+ /* default format */
+ ctx->s_frame.fmt = &fimc_formats[0];
+ ctx->d_frame.fmt = &fimc_formats[0];
+ /* per user process device context initialization */
+ ctx->state = 0;
+ ctx->flags = 0;
+ ctx->effect.type = S5P_FIMC_EFFECT_ORIGINAL;
+ ctx->in_path = FIMC_DMA;
+ ctx->out_path = FIMC_DMA;
+ spin_lock_init(&ctx->slock);
+
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(ctx, fimc->m2m.m2m_dev, queue_init);
+ if (IS_ERR(ctx->m2m_ctx)) {
+ err = PTR_ERR(ctx->m2m_ctx);
+ kfree(ctx);
+ }
+ return err;
+}
+
+static int fimc_m2m_release(struct file *file)
+{
+ struct fimc_ctx *ctx = file->private_data;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ kfree(ctx);
+ mutex_lock(&fimc->lock);
+ if (--fimc->m2m.refcnt <= 0)
+ clear_bit(ST_OUTDMA_RUN, &fimc->state);
+ mutex_unlock(&fimc->lock);
+ return 0;
+}
+
+static unsigned int fimc_m2m_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct fimc_ctx *ctx = file->private_data;
+ return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+}
+
+
+static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct fimc_ctx *ctx = file->private_data;
+ return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+}
+
+static const struct v4l2_file_operations fimc_m2m_fops = {
+ .owner = THIS_MODULE,
+ .open = fimc_m2m_open,
+ .release = fimc_m2m_release,
+ .poll = fimc_m2m_poll,
+ .ioctl = video_ioctl2,
+ .mmap = fimc_m2m_mmap,
+};
+
+static struct v4l2_m2m_ops m2m_ops = {
+ .device_run = fimc_dma_run,
+ .job_abort = fimc_job_abort,
+};
+
+
+static int fimc_register_m2m_device(struct fimc_dev *fimc)
+{
+ struct video_device *vfd;
+ struct platform_device *pdev;
+ struct v4l2_device *v4l2_dev;
+ int ret = 0;
+
+ if (!fimc)
+ return -ENODEV;
+
+ pdev = fimc->pdev;
+ v4l2_dev = &fimc->m2m.v4l2_dev;
+
+ /* set name if it is empty */
+ if (!v4l2_dev->name[0])
+ snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
+ "%s.m2m", dev_name(&pdev->dev));
+
+ ret = v4l2_device_register(&pdev->dev, v4l2_dev);
+ if (ret)
+ return ret;;
+
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(v4l2_dev, "Failed to allocate video device\n");
+ goto err_m2m_r1;
+ }
+
+ vfd->fops = &fimc_m2m_fops;
+ vfd->ioctl_ops = &fimc_m2m_ioctl_ops;
+ vfd->minor = -1;
+ vfd->release = video_device_release;
+
+ snprintf(vfd->name, sizeof(vfd->name), "%s:m2m", dev_name(&pdev->dev));
+
+ video_set_drvdata(vfd, fimc);
+ platform_set_drvdata(pdev, fimc);
+
+ fimc->m2m.vfd = vfd;
+ fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops);
+ if (IS_ERR(fimc->m2m.m2m_dev)) {
+ v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n");
+ ret = PTR_ERR(fimc->m2m.m2m_dev);
+ goto err_m2m_r2;
+ }
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+ if (ret) {
+ v4l2_err(v4l2_dev,
+ "%s(): failed to register video device\n", __func__);
+ goto err_m2m_r3;
+ }
+ v4l2_info(v4l2_dev,
+ "FIMC m2m driver registered as /dev/video%d\n", vfd->num);
+
+ return 0;
+
+err_m2m_r3:
+ v4l2_m2m_release(fimc->m2m.m2m_dev);
+err_m2m_r2:
+ video_device_release(fimc->m2m.vfd);
+err_m2m_r1:
+ v4l2_device_unregister(v4l2_dev);
+
+ return ret;
+}
+
+static void fimc_unregister_m2m_device(struct fimc_dev *fimc)
+{
+ if (fimc) {
+ v4l2_m2m_release(fimc->m2m.m2m_dev);
+ video_unregister_device(fimc->m2m.vfd);
+ video_device_release(fimc->m2m.vfd);
+ v4l2_device_unregister(&fimc->m2m.v4l2_dev);
+ }
+}
+
+static void fimc_clk_release(struct fimc_dev *fimc)
+{
+ int i;
+ for (i = 0; i < NUM_FIMC_CLOCKS; i++) {
+ if (fimc->clock[i]) {
+ clk_disable(fimc->clock[i]);
+ clk_put(fimc->clock[i]);
+ }
+ }
+}
+
+static int fimc_clk_get(struct fimc_dev *fimc)
+{
+ int i;
+ for (i = 0; i < NUM_FIMC_CLOCKS; i++) {
+ fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clock_name[i]);
+ if (IS_ERR(fimc->clock[i])) {
+ dev_err(&fimc->pdev->dev,
+ "failed to get fimc clock: %s\n",
+ fimc_clock_name[i]);
+ return -ENXIO;
+ }
+ clk_enable(fimc->clock[i]);
+ }
+ return 0;
+}
+
+static int fimc_probe(struct platform_device *pdev)
+{
+ struct fimc_dev *fimc;
+ struct resource *res;
+ struct samsung_fimc_driverdata *drv_data;
+ int ret = 0;
+
+ dev_dbg(&pdev->dev, "%s():\n", __func__);
+
+ drv_data = (struct samsung_fimc_driverdata *)
+ platform_get_device_id(pdev)->driver_data;
+
+ if (pdev->id >= drv_data->devs_cnt) {
+ dev_err(&pdev->dev, "Invalid platform device id: %d\n",
+ pdev->id);
+ return -EINVAL;
+ }
+
+ fimc = kzalloc(sizeof(struct fimc_dev), GFP_KERNEL);
+ if (!fimc)
+ return -ENOMEM;
+
+ fimc->id = pdev->id;
+ fimc->variant = drv_data->variant[fimc->id];
+ fimc->pdev = pdev;
+ fimc->state = ST_IDLE;
+
+ spin_lock_init(&fimc->irqlock);
+ spin_lock_init(&fimc->slock);
+
+ mutex_init(&fimc->lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to find the registers\n");
+ ret = -ENOENT;
+ goto err_info;
+ }
+
+ fimc->regs_res = request_mem_region(res->start, resource_size(res),
+ dev_name(&pdev->dev));
+ if (!fimc->regs_res) {
+ dev_err(&pdev->dev, "failed to obtain register region\n");
+ ret = -ENOENT;
+ goto err_info;
+ }
+
+ fimc->regs = ioremap(res->start, resource_size(res));
+ if (!fimc->regs) {
+ dev_err(&pdev->dev, "failed to map registers\n");
+ ret = -ENXIO;
+ goto err_req_region;
+ }
+
+ ret = fimc_clk_get(fimc);
+ if (ret)
+ goto err_regs_unmap;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to get IRQ resource\n");
+ ret = -ENXIO;
+ goto err_clk;
+ }
+ fimc->irq = res->start;
+
+ fimc_hw_reset(fimc);
+
+ ret = request_irq(fimc->irq, fimc_isr, 0, pdev->name, fimc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to install irq (%d)\n", ret);
+ goto err_clk;
+ }
+
+ fimc->work_queue = create_workqueue(dev_name(&fimc->pdev->dev));
+ if (!fimc->work_queue)
+ goto err_irq;
+
+ ret = fimc_register_m2m_device(fimc);
+ if (ret)
+ goto err_wq;
+
+ fimc_hw_en_lastirq(fimc, true);
+
+ dev_dbg(&pdev->dev, "%s(): fimc-%d registered successfully\n",
+ __func__, fimc->id);
+
+ return 0;
+
+err_wq:
+ destroy_workqueue(fimc->work_queue);
+err_irq:
+ free_irq(fimc->irq, fimc);
+err_clk:
+ fimc_clk_release(fimc);
+err_regs_unmap:
+ iounmap(fimc->regs);
+err_req_region:
+ release_resource(fimc->regs_res);
+ kfree(fimc->regs_res);
+err_info:
+ kfree(fimc);
+ dev_err(&pdev->dev, "failed to install\n");
+ return ret;
+}
+
+static int __devexit fimc_remove(struct platform_device *pdev)
+{
+ struct fimc_dev *fimc =
+ (struct fimc_dev *)platform_get_drvdata(pdev);
+
+ v4l2_info(&fimc->m2m.v4l2_dev, "Removing %s\n", pdev->name);
+
+ free_irq(fimc->irq, fimc);
+
+ fimc_hw_reset(fimc);
+
+ fimc_unregister_m2m_device(fimc);
+ fimc_clk_release(fimc);
+ iounmap(fimc->regs);
+ release_resource(fimc->regs_res);
+ kfree(fimc->regs_res);
+ kfree(fimc);
+ return 0;
+}
+
+static struct samsung_fimc_variant fimc01_variant_s5p = {
+ .has_inp_rot = 1,
+ .has_out_rot = 1,
+ .min_inp_pixsize = 16,
+ .min_out_pixsize = 16,
+
+ .scaler_en_w = 3264,
+ .scaler_dis_w = 8192,
+ .in_rot_en_h = 1920,
+ .in_rot_dis_w = 8192,
+ .out_rot_en_w = 1920,
+ .out_rot_dis_w = 4224,
+};
+
+static struct samsung_fimc_variant fimc2_variant_s5p = {
+ .min_inp_pixsize = 16,
+ .min_out_pixsize = 16,
+
+ .scaler_en_w = 4224,
+ .scaler_dis_w = 8192,
+ .in_rot_en_h = 1920,
+ .in_rot_dis_w = 8192,
+ .out_rot_en_w = 1920,
+ .out_rot_dis_w = 4224,
+};
+
+static struct samsung_fimc_variant fimc01_variant_s5pv210 = {
+ .has_inp_rot = 1,
+ .has_out_rot = 1,
+ .min_inp_pixsize = 16,
+ .min_out_pixsize = 32,
+
+ .scaler_en_w = 4224,
+ .scaler_dis_w = 8192,
+ .in_rot_en_h = 1920,
+ .in_rot_dis_w = 8192,
+ .out_rot_en_w = 1920,
+ .out_rot_dis_w = 4224,
+};
+
+static struct samsung_fimc_variant fimc2_variant_s5pv210 = {
+ .min_inp_pixsize = 16,
+ .min_out_pixsize = 32,
+
+ .scaler_en_w = 1920,
+ .scaler_dis_w = 8192,
+ .in_rot_en_h = 1280,
+ .in_rot_dis_w = 8192,
+ .out_rot_en_w = 1280,
+ .out_rot_dis_w = 1920,
+};
+
+static struct samsung_fimc_driverdata fimc_drvdata_s5p = {
+ .variant = {
+ [0] = &fimc01_variant_s5p,
+ [1] = &fimc01_variant_s5p,
+ [2] = &fimc2_variant_s5p,
+ },
+ .devs_cnt = 3
+};
+
+static struct samsung_fimc_driverdata fimc_drvdata_s5pv210 = {
+ .variant = {
+ [0] = &fimc01_variant_s5pv210,
+ [1] = &fimc01_variant_s5pv210,
+ [2] = &fimc2_variant_s5pv210,
+ },
+ .devs_cnt = 3
+};
+
+static struct platform_device_id fimc_driver_ids[] = {
+ {
+ .name = "s5p-fimc",
+ .driver_data = (unsigned long)&fimc_drvdata_s5p,
+ }, {
+ .name = "s5pv210-fimc",
+ .driver_data = (unsigned long)&fimc_drvdata_s5pv210,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, fimc_driver_ids);
+
+static struct platform_driver fimc_driver = {
+ .probe = fimc_probe,
+ .remove = __devexit_p(fimc_remove),
+ .id_table = fimc_driver_ids,
+ .driver = {
+ .name = MODULE_NAME,
+ .owner = THIS_MODULE,
+ }
+};
+
+static char banner[] __initdata = KERN_INFO
+ "S5PC Camera Interface V4L2 Driver, (c) 2010 Samsung Electronics\n";
+
+static int __init fimc_init(void)
+{
+ u32 ret;
+ printk(banner);
+
+ ret = platform_driver_register(&fimc_driver);
+ if (ret) {
+ printk(KERN_ERR "FIMC platform driver register failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+static void __exit fimc_exit(void)
+{
+ platform_driver_unregister(&fimc_driver);
+}
+
+module_init(fimc_init);
+module_exit(fimc_exit);
+
+MODULE_AUTHOR("Sylwester Nawrocki, s.nawrocki@samsung.com");
+MODULE_DESCRIPTION("S3C/S5P FIMC (video postprocessor) driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Copyright (c) 2010 Samsung Electronics
+ *
+ * Sylwester Nawrocki, <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef FIMC_CORE_H_
+#define FIMC_CORE_H_
+
+#include <linux/types.h>
+#include <media/videobuf-core.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mem2mem.h>
+#include <linux/videodev2.h>
+#include "regs-fimc.h"
+
+#define err(fmt, args...) \
+ printk(KERN_ERR "%s:%d: " fmt "\n", __func__, __LINE__, ##args)
+
+#ifdef DEBUG
+#define dbg(fmt, args...) \
+ printk(KERN_DEBUG "%s:%d: " fmt "\n", __func__, __LINE__, ##args)
+#else
+#define dbg(fmt, args...)
+#endif
+
+#define NUM_FIMC_CLOCKS 2
+#define MODULE_NAME "s5p-fimc"
+#define FIMC_MAX_DEVS 3
+#define FIMC_MAX_OUT_BUFS 4
+#define SCALER_MAX_HRATIO 64
+#define SCALER_MAX_VRATIO 64
+
+enum {
+ ST_IDLE,
+ ST_OUTDMA_RUN,
+ ST_M2M_PEND,
+};
+
+#define fimc_m2m_active(dev) test_bit(ST_OUTDMA_RUN, &(dev)->state)
+#define fimc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state)
+
+enum fimc_datapath {
+ FIMC_ITU_CAM_A,
+ FIMC_ITU_CAM_B,
+ FIMC_MIPI_CAM,
+ FIMC_DMA,
+ FIMC_LCDFIFO,
+ FIMC_WRITEBACK
+};
+
+enum fimc_color_fmt {
+ S5P_FIMC_RGB565,
+ S5P_FIMC_RGB666,
+ S5P_FIMC_RGB888,
+ S5P_FIMC_YCBCR420,
+ S5P_FIMC_YCBCR422,
+ S5P_FIMC_YCBYCR422,
+ S5P_FIMC_YCRYCB422,
+ S5P_FIMC_CBYCRY422,
+ S5P_FIMC_CRYCBY422,
+ S5P_FIMC_RGB30_LOCAL,
+ S5P_FIMC_YCBCR444_LOCAL,
+ S5P_FIMC_MAX_COLOR = S5P_FIMC_YCBCR444_LOCAL,
+ S5P_FIMC_COLOR_MASK = 0x0F,
+};
+
+/* Y/Cb/Cr components order at DMA output for 1 plane YCbCr 4:2:2 formats. */
+#define S5P_FIMC_OUT_CRYCBY S5P_CIOCTRL_ORDER422_CRYCBY
+#define S5P_FIMC_OUT_CBYCRY S5P_CIOCTRL_ORDER422_YCRYCB
+#define S5P_FIMC_OUT_YCRYCB S5P_CIOCTRL_ORDER422_CBYCRY
+#define S5P_FIMC_OUT_YCBYCR S5P_CIOCTRL_ORDER422_YCBYCR
+
+/* Input Y/Cb/Cr components order for 1 plane YCbCr 4:2:2 color formats. */
+#define S5P_FIMC_IN_CRYCBY S5P_MSCTRL_ORDER422_CRYCBY
+#define S5P_FIMC_IN_CBYCRY S5P_MSCTRL_ORDER422_YCRYCB
+#define S5P_FIMC_IN_YCRYCB S5P_MSCTRL_ORDER422_CBYCRY
+#define S5P_FIMC_IN_YCBYCR S5P_MSCTRL_ORDER422_YCBYCR
+
+/* Cb/Cr chrominance components order for 2 plane Y/CbCr 4:2:2 formats. */
+#define S5P_FIMC_LSB_CRCB S5P_CIOCTRL_ORDER422_2P_LSB_CRCB
+
+/* The embedded image effect selection */
+#define S5P_FIMC_EFFECT_ORIGINAL S5P_CIIMGEFF_FIN_BYPASS
+#define S5P_FIMC_EFFECT_ARBITRARY S5P_CIIMGEFF_FIN_ARBITRARY
+#define S5P_FIMC_EFFECT_NEGATIVE S5P_CIIMGEFF_FIN_NEGATIVE
+#define S5P_FIMC_EFFECT_ARTFREEZE S5P_CIIMGEFF_FIN_ARTFREEZE
+#define S5P_FIMC_EFFECT_EMBOSSING S5P_CIIMGEFF_FIN_EMBOSSING
+#define S5P_FIMC_EFFECT_SIKHOUETTE S5P_CIIMGEFF_FIN_SILHOUETTE
+
+/* The hardware context state. */
+#define FIMC_PARAMS (1 << 0)
+#define FIMC_SRC_ADDR (1 << 1)
+#define FIMC_DST_ADDR (1 << 2)
+#define FIMC_SRC_FMT (1 << 3)
+#define FIMC_DST_FMT (1 << 4)
+
+/* Image conversion flags */
+#define FIMC_IN_DMA_ACCESS_TILED (1 << 0)
+#define FIMC_IN_DMA_ACCESS_LINEAR (0 << 0)
+#define FIMC_OUT_DMA_ACCESS_TILED (1 << 1)
+#define FIMC_OUT_DMA_ACCESS_LINEAR (0 << 1)
+#define FIMC_SCAN_MODE_PROGRESSIVE (0 << 2)
+#define FIMC_SCAN_MODE_INTERLACED (1 << 2)
+/* YCbCr data dynamic range for RGB-YUV color conversion. Y/Cb/Cr: (0 ~ 255) */
+#define FIMC_COLOR_RANGE_WIDE (0 << 3)
+/* Y (16 ~ 235), Cb/Cr (16 ~ 240) */
+#define FIMC_COLOR_RANGE_NARROW (1 << 3)
+
+#define FLIP_NONE 0
+#define FLIP_X_AXIS 1
+#define FLIP_Y_AXIS 2
+#define FLIP_XY_AXIS (FLIP_X_AXIS | FLIP_Y_AXIS)
+
+/**
+ * struct fimc_fmt - the driver's internal color format data
+ * @name: format description
+ * @fourcc: the fourcc code for this format
+ * @color: the corresponding fimc_color_fmt
+ * @depth: number of bits per pixel
+ * @buff_cnt: number of physically non-contiguous data planes
+ * @planes_cnt: number of physically contiguous data planes
+ */
+struct fimc_fmt {
+ char *name;
+ u32 fourcc;
+ u32 color;
+ u32 depth;
+ u16 buff_cnt;
+ u16 planes_cnt;
+};
+
+/**
+ * struct fimc_dma_offset - pixel offset information for DMA
+ * @y_h: y value horizontal offset
+ * @y_v: y value vertical offset
+ * @cb_h: cb value horizontal offset
+ * @cb_v: cb value vertical offset
+ * @cr_h: cr value horizontal offset
+ * @cr_v: cr value vertical offset
+ */
+struct fimc_dma_offset {
+ int y_h;
+ int y_v;
+ int cb_h;
+ int cb_v;
+ int cr_h;
+ int cr_v;
+};
+
+/**
+ * struct fimc_effect - the configuration data for the "Arbitrary" image effect
+ * @type: effect type
+ * @pat_cb: cr value when type is "arbitrary"
+ * @pat_cr: cr value when type is "arbitrary"
+ */
+struct fimc_effect {
+ u32 type;
+ u8 pat_cb;
+ u8 pat_cr;
+};
+
+/**
+ * struct fimc_scaler - the configuration data for FIMC inetrnal scaler
+ *
+ * @enabled: the flag set when the scaler is used
+ * @hfactor: horizontal shift factor
+ * @vfactor: vertical shift factor
+ * @pre_hratio: horizontal ratio of the prescaler
+ * @pre_vratio: vertical ratio of the prescaler
+ * @pre_dst_width: the prescaler's destination width
+ * @pre_dst_height: the prescaler's destination height
+ * @scaleup_h: flag indicating scaling up horizontally
+ * @scaleup_v: flag indicating scaling up vertically
+ * @main_hratio: the main scaler's horizontal ratio
+ * @main_vratio: the main scaler's vertical ratio
+ * @real_width: source width - offset
+ * @real_height: source height - offset
+ * @copy_mode: flag set if one-to-one mode is used, i.e. no scaling
+ * and color format conversion
+ */
+struct fimc_scaler {
+ u32 enabled;
+ u32 hfactor;
+ u32 vfactor;
+ u32 pre_hratio;
+ u32 pre_vratio;
+ u32 pre_dst_width;
+ u32 pre_dst_height;
+ u32 scaleup_h;
+ u32 scaleup_v;
+ u32 main_hratio;
+ u32 main_vratio;
+ u32 real_width;
+ u32 real_height;
+ u32 copy_mode;
+};
+
+/**
+ * struct fimc_addr - the FIMC physical address set for DMA
+ *
+ * @y: luminance plane physical address
+ * @cb: Cb plane physical address
+ * @cr: Cr plane physical address
+ */
+struct fimc_addr {
+ u32 y;
+ u32 cb;
+ u32 cr;
+};
+
+/**
+ * struct fimc_vid_buffer - the driver's video buffer
+ * @vb: v4l videobuf buffer
+ */
+struct fimc_vid_buffer {
+ struct videobuf_buffer vb;
+};
+
+/**
+ * struct fimc_frame - input/output frame format properties
+ *
+ * @f_width: image full width (virtual screen size)
+ * @f_height: image full height (virtual screen size)
+ * @o_width: original image width as set by S_FMT
+ * @o_height: original image height as set by S_FMT
+ * @offs_h: image horizontal pixel offset
+ * @offs_v: image vertical pixel offset
+ * @width: image pixel width
+ * @height: image pixel weight
+ * @paddr: image frame buffer physical addresses
+ * @buf_cnt: number of buffers depending on a color format
+ * @size: image size in bytes
+ * @color: color format
+ * @dma_offset: DMA offset in bytes
+ */
+struct fimc_frame {
+ u32 f_width;
+ u32 f_height;
+ u32 o_width;
+ u32 o_height;
+ u32 offs_h;
+ u32 offs_v;
+ u32 width;
+ u32 height;
+ u32 size;
+ struct fimc_addr paddr;
+ struct fimc_dma_offset dma_offset;
+ struct fimc_fmt *fmt;
+};
+
+/**
+ * struct fimc_m2m_device - v4l2 memory-to-memory device data
+ * @vfd: the video device node for v4l2 m2m mode
+ * @v4l2_dev: v4l2 device for m2m mode
+ * @m2m_dev: v4l2 memory-to-memory device data
+ * @ctx: hardware context data
+ * @refcnt: the reference counter
+ */
+struct fimc_m2m_device {
+ struct video_device *vfd;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct fimc_ctx *ctx;
+ int refcnt;
+};
+
+/**
+ * struct samsung_fimc_variant - camera interface variant information
+ *
+ * @pix_hoff: indicate whether horizontal offset is in pixels or in bytes
+ * @has_inp_rot: set if has input rotator
+ * @has_out_rot: set if has output rotator
+ * @min_inp_pixsize: minimum input pixel size
+ * @min_out_pixsize: minimum output pixel size
+ * @scaler_en_w: maximum input pixel width when the scaler is enabled
+ * @scaler_dis_w: maximum input pixel width when the scaler is disabled
+ * @in_rot_en_h: maximum input width when the input rotator is used
+ * @in_rot_dis_w: maximum input width when the input rotator is used
+ * @out_rot_en_w: maximum output width for the output rotator enabled
+ * @out_rot_dis_w: maximum output width for the output rotator enabled
+ */
+struct samsung_fimc_variant {
+ unsigned int pix_hoff:1;
+ unsigned int has_inp_rot:1;
+ unsigned int has_out_rot:1;
+
+ u16 min_inp_pixsize;
+ u16 min_out_pixsize;
+ u16 scaler_en_w;
+ u16 scaler_dis_w;
+ u16 in_rot_en_h;
+ u16 in_rot_dis_w;
+ u16 out_rot_en_w;
+ u16 out_rot_dis_w;
+};
+
+/**
+ * struct samsung_fimc_driverdata - per-device type driver data for init time.
+ *
+ * @variant: the variant information for this driver.
+ * @dev_cnt: number of fimc sub-devices available in SoC
+ */
+struct samsung_fimc_driverdata {
+ struct samsung_fimc_variant *variant[FIMC_MAX_DEVS];
+ int devs_cnt;
+};
+
+struct fimc_ctx;
+
+/**
+ * struct fimc_subdev - abstraction for a FIMC entity
+ *
+ * @slock: the spinlock protecting this data structure
+ * @lock: the mutex protecting this data structure
+ * @pdev: pointer to the FIMC platform device
+ * @id: FIMC device index (0..2)
+ * @clock[]: the clocks required for FIMC operation
+ * @regs: the mapped hardware registers
+ * @regs_res: the resource claimed for IO registers
+ * @irq: interrupt number of the FIMC subdevice
+ * @irqlock: spinlock protecting videbuffer queue
+ * @m2m: memory-to-memory V4L2 device information
+ * @state: the FIMC device state flags
+ */
+struct fimc_dev {
+ spinlock_t slock;
+ struct mutex lock;
+ struct platform_device *pdev;
+ struct samsung_fimc_variant *variant;
+ int id;
+ struct clk *clock[NUM_FIMC_CLOCKS];
+ void __iomem *regs;
+ struct resource *regs_res;
+ int irq;
+ spinlock_t irqlock;
+ struct workqueue_struct *work_queue;
+ struct fimc_m2m_device m2m;
+ unsigned long state;
+};
+
+/**
+ * fimc_ctx - the device context data
+ *
+ * @lock: mutex protecting this data structure
+ * @s_frame: source frame properties
+ * @d_frame: destination frame properties
+ * @out_order_1p: output 1-plane YCBCR order
+ * @out_order_2p: output 2-plane YCBCR order
+ * @in_order_1p input 1-plane YCBCR order
+ * @in_order_2p: input 2-plane YCBCR order
+ * @in_path: input mode (DMA or camera)
+ * @out_path: output mode (DMA or FIFO)
+ * @scaler: image scaler properties
+ * @effect: image effect
+ * @rotation: image clockwise rotation in degrees
+ * @flip: image flip mode
+ * @flags: an additional flags for image conversion
+ * @state: flags to keep track of user configuration
+ * @fimc_dev: the FIMC device this context applies to
+ * @m2m_ctx: memory-to-memory device context
+ */
+struct fimc_ctx {
+ spinlock_t slock;
+ struct fimc_frame s_frame;
+ struct fimc_frame d_frame;
+ u32 out_order_1p;
+ u32 out_order_2p;
+ u32 in_order_1p;
+ u32 in_order_2p;
+ enum fimc_datapath in_path;
+ enum fimc_datapath out_path;
+ struct fimc_scaler scaler;
+ struct fimc_effect effect;
+ int rotation;
+ u32 flip;
+ u32 flags;
+ u32 state;
+ struct fimc_dev *fimc_dev;
+ struct v4l2_m2m_ctx *m2m_ctx;
+};
+
+
+static inline int tiled_fmt(struct fimc_fmt *fmt)
+{
+ return 0;
+}
+
+static inline void fimc_hw_clear_irq(struct fimc_dev *dev)
+{
+ u32 cfg = readl(dev->regs + S5P_CIGCTRL);
+ cfg |= S5P_CIGCTRL_IRQ_CLR;
+ writel(cfg, dev->regs + S5P_CIGCTRL);
+}
+
+static inline void fimc_hw_start_scaler(struct fimc_dev *dev)
+{
+ u32 cfg = readl(dev->regs + S5P_CISCCTRL);
+ cfg |= S5P_CISCCTRL_SCALERSTART;
+ writel(cfg, dev->regs + S5P_CISCCTRL);
+}
+
+static inline void fimc_hw_stop_scaler(struct fimc_dev *dev)
+{
+ u32 cfg = readl(dev->regs + S5P_CISCCTRL);
+ cfg &= ~S5P_CISCCTRL_SCALERSTART;
+ writel(cfg, dev->regs + S5P_CISCCTRL);
+}
+
+static inline void fimc_hw_dis_capture(struct fimc_dev *dev)
+{
+ u32 cfg = readl(dev->regs + S5P_CIIMGCPT);
+ cfg &= ~(S5P_CIIMGCPT_IMGCPTEN | S5P_CIIMGCPT_IMGCPTEN_SC);
+ writel(cfg, dev->regs + S5P_CIIMGCPT);
+}
+
+static inline void fimc_hw_start_in_dma(struct fimc_dev *dev)
+{
+ u32 cfg = readl(dev->regs + S5P_MSCTRL);
+ cfg |= S5P_MSCTRL_ENVID;
+ writel(cfg, dev->regs + S5P_MSCTRL);
+}
+
+static inline void fimc_hw_stop_in_dma(struct fimc_dev *dev)
+{
+ u32 cfg = readl(dev->regs + S5P_MSCTRL);
+ cfg &= ~S5P_MSCTRL_ENVID;
+ writel(cfg, dev->regs + S5P_MSCTRL);
+}
+
+static inline struct fimc_frame *ctx_m2m_get_frame(struct fimc_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ struct fimc_frame *frame;
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT == type) {
+ frame = &ctx->s_frame;
+ } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE == type) {
+ frame = &ctx->d_frame;
+ } else {
+ v4l2_err(&ctx->fimc_dev->m2m.v4l2_dev,
+ "Wrong buffer/video queue type (%d)\n", type);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return frame;
+}
+
+/* -----------------------------------------------------*/
+/* fimc-reg.c */
+void fimc_hw_reset(struct fimc_dev *dev);
+void fimc_hw_set_rotation(struct fimc_ctx *ctx);
+void fimc_hw_set_target_format(struct fimc_ctx *ctx);
+void fimc_hw_set_out_dma(struct fimc_ctx *ctx);
+void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable);
+void fimc_hw_en_irq(struct fimc_dev *dev, int enable);
+void fimc_hw_set_prescaler(struct fimc_ctx *ctx);
+void fimc_hw_set_scaler(struct fimc_ctx *ctx);
+void fimc_hw_en_capture(struct fimc_ctx *ctx);
+void fimc_hw_set_effect(struct fimc_ctx *ctx);
+void fimc_hw_set_in_dma(struct fimc_ctx *ctx);
+void fimc_hw_set_input_path(struct fimc_ctx *ctx);
+void fimc_hw_set_output_path(struct fimc_ctx *ctx);
+void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *paddr);
+void fimc_hw_set_output_addr(struct fimc_dev *dev, struct fimc_addr *paddr);
+
+#endif /* FIMC_CORE_H_ */
--- /dev/null
+/*
+ * Register interface file for Samsung Camera Interface (FIMC) driver
+ *
+ * Copyright (c) 2010 Samsung Electronics
+ *
+ * Sylwester Nawrocki, s.nawrocki@samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <mach/map.h>
+
+#include "fimc-core.h"
+
+
+void fimc_hw_reset(struct fimc_dev *dev)
+{
+ u32 cfg;
+
+ cfg = readl(dev->regs + S5P_CISRCFMT);
+ cfg |= S5P_CISRCFMT_ITU601_8BIT;
+ writel(cfg, dev->regs + S5P_CISRCFMT);
+
+ /* Software reset. */
+ cfg = readl(dev->regs + S5P_CIGCTRL);
+ cfg |= (S5P_CIGCTRL_SWRST | S5P_CIGCTRL_IRQ_LEVEL);
+ writel(cfg, dev->regs + S5P_CIGCTRL);
+ msleep(1);
+
+ cfg = readl(dev->regs + S5P_CIGCTRL);
+ cfg &= ~S5P_CIGCTRL_SWRST;
+ writel(cfg, dev->regs + S5P_CIGCTRL);
+
+}
+
+void fimc_hw_set_rotation(struct fimc_ctx *ctx)
+{
+ u32 cfg, flip;
+ struct fimc_dev *dev = ctx->fimc_dev;
+
+ cfg = readl(dev->regs + S5P_CITRGFMT);
+ cfg &= ~(S5P_CITRGFMT_INROT90 | S5P_CITRGFMT_OUTROT90);
+
+ flip = readl(dev->regs + S5P_MSCTRL);
+ flip &= ~S5P_MSCTRL_FLIP_MASK;
+
+ /*
+ * The input and output rotator cannot work simultaneously.
+ * Use the output rotator in output DMA mode or the input rotator
+ * in direct fifo output mode.
+ */
+ if (ctx->rotation == 90 || ctx->rotation == 270) {
+ if (ctx->out_path == FIMC_LCDFIFO) {
+ cfg |= S5P_CITRGFMT_INROT90;
+ if (ctx->rotation == 270)
+ flip |= S5P_MSCTRL_FLIP_180;
+ } else {
+ cfg |= S5P_CITRGFMT_OUTROT90;
+ if (ctx->rotation == 270)
+ cfg |= S5P_CITRGFMT_FLIP_180;
+ }
+ } else if (ctx->rotation == 180) {
+ if (ctx->out_path == FIMC_LCDFIFO)
+ flip |= S5P_MSCTRL_FLIP_180;
+ else
+ cfg |= S5P_CITRGFMT_FLIP_180;
+ }
+ if (ctx->rotation == 180 || ctx->rotation == 270)
+ writel(flip, dev->regs + S5P_MSCTRL);
+ writel(cfg, dev->regs + S5P_CITRGFMT);
+}
+
+static u32 fimc_hw_get_in_flip(u32 ctx_flip)
+{
+ u32 flip = S5P_MSCTRL_FLIP_NORMAL;
+
+ switch (ctx_flip) {
+ case FLIP_X_AXIS:
+ flip = S5P_MSCTRL_FLIP_X_MIRROR;
+ break;
+ case FLIP_Y_AXIS:
+ flip = S5P_MSCTRL_FLIP_Y_MIRROR;
+ break;
+ case FLIP_XY_AXIS:
+ flip = S5P_MSCTRL_FLIP_180;
+ break;
+ }
+
+ return flip;
+}
+
+static u32 fimc_hw_get_target_flip(u32 ctx_flip)
+{
+ u32 flip = S5P_CITRGFMT_FLIP_NORMAL;
+
+ switch (ctx_flip) {
+ case FLIP_X_AXIS:
+ flip = S5P_CITRGFMT_FLIP_X_MIRROR;
+ break;
+ case FLIP_Y_AXIS:
+ flip = S5P_CITRGFMT_FLIP_Y_MIRROR;
+ break;
+ case FLIP_XY_AXIS:
+ flip = S5P_CITRGFMT_FLIP_180;
+ break;
+ case FLIP_NONE:
+ break;
+
+ }
+ return flip;
+}
+
+void fimc_hw_set_target_format(struct fimc_ctx *ctx)
+{
+ u32 cfg;
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_frame *frame = &ctx->d_frame;
+
+ dbg("w= %d, h= %d color: %d", frame->width,
+ frame->height, frame->fmt->color);
+
+ cfg = readl(dev->regs + S5P_CITRGFMT);
+ cfg &= ~(S5P_CITRGFMT_FMT_MASK | S5P_CITRGFMT_HSIZE_MASK |
+ S5P_CITRGFMT_VSIZE_MASK);
+
+ switch (frame->fmt->color) {
+ case S5P_FIMC_RGB565:
+ case S5P_FIMC_RGB666:
+ case S5P_FIMC_RGB888:
+ cfg |= S5P_CITRGFMT_RGB;
+ break;
+ case S5P_FIMC_YCBCR420:
+ cfg |= S5P_CITRGFMT_YCBCR420;
+ break;
+ case S5P_FIMC_YCBYCR422:
+ case S5P_FIMC_YCRYCB422:
+ case S5P_FIMC_CBYCRY422:
+ case S5P_FIMC_CRYCBY422:
+ if (frame->fmt->planes_cnt == 1)
+ cfg |= S5P_CITRGFMT_YCBCR422_1P;
+ else
+ cfg |= S5P_CITRGFMT_YCBCR422;
+ break;
+ default:
+ break;
+ }
+
+ cfg |= S5P_CITRGFMT_HSIZE(frame->width);
+ cfg |= S5P_CITRGFMT_VSIZE(frame->height);
+
+ if (ctx->rotation == 0) {
+ cfg &= ~S5P_CITRGFMT_FLIP_MASK;
+ cfg |= fimc_hw_get_target_flip(ctx->flip);
+ }
+ writel(cfg, dev->regs + S5P_CITRGFMT);
+
+ cfg = readl(dev->regs + S5P_CITAREA) & ~S5P_CITAREA_MASK;
+ cfg |= (frame->width * frame->height);
+ writel(cfg, dev->regs + S5P_CITAREA);
+}
+
+static void fimc_hw_set_out_dma_size(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_frame *frame = &ctx->d_frame;
+ u32 cfg = 0;
+
+ if (ctx->rotation == 90 || ctx->rotation == 270) {
+ cfg |= S5P_ORIG_SIZE_HOR(frame->f_height);
+ cfg |= S5P_ORIG_SIZE_VER(frame->f_width);
+ } else {
+ cfg |= S5P_ORIG_SIZE_HOR(frame->f_width);
+ cfg |= S5P_ORIG_SIZE_VER(frame->f_height);
+ }
+ writel(cfg, dev->regs + S5P_ORGOSIZE);
+}
+
+void fimc_hw_set_out_dma(struct fimc_ctx *ctx)
+{
+ u32 cfg;
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_frame *frame = &ctx->d_frame;
+ struct fimc_dma_offset *offset = &frame->dma_offset;
+
+ /* Set the input dma offsets. */
+ cfg = 0;
+ cfg |= S5P_CIO_OFFS_HOR(offset->y_h);
+ cfg |= S5P_CIO_OFFS_VER(offset->y_v);
+ writel(cfg, dev->regs + S5P_CIOYOFF);
+
+ cfg = 0;
+ cfg |= S5P_CIO_OFFS_HOR(offset->cb_h);
+ cfg |= S5P_CIO_OFFS_VER(offset->cb_v);
+ writel(cfg, dev->regs + S5P_CIOCBOFF);
+
+ cfg = 0;
+ cfg |= S5P_CIO_OFFS_HOR(offset->cr_h);
+ cfg |= S5P_CIO_OFFS_VER(offset->cr_v);
+ writel(cfg, dev->regs + S5P_CIOCROFF);
+
+ fimc_hw_set_out_dma_size(ctx);
+
+ /* Configure chroma components order. */
+ cfg = readl(dev->regs + S5P_CIOCTRL);
+
+ cfg &= ~(S5P_CIOCTRL_ORDER2P_MASK | S5P_CIOCTRL_ORDER422_MASK |
+ S5P_CIOCTRL_YCBCR_PLANE_MASK);
+
+ if (frame->fmt->planes_cnt == 1)
+ cfg |= ctx->out_order_1p;
+ else if (frame->fmt->planes_cnt == 2)
+ cfg |= ctx->out_order_2p | S5P_CIOCTRL_YCBCR_2PLANE;
+ else if (frame->fmt->planes_cnt == 3)
+ cfg |= S5P_CIOCTRL_YCBCR_3PLANE;
+
+ writel(cfg, dev->regs + S5P_CIOCTRL);
+}
+
+static void fimc_hw_en_autoload(struct fimc_dev *dev, int enable)
+{
+ u32 cfg = readl(dev->regs + S5P_ORGISIZE);
+ if (enable)
+ cfg |= S5P_CIREAL_ISIZE_AUTOLOAD_EN;
+ else
+ cfg &= ~S5P_CIREAL_ISIZE_AUTOLOAD_EN;
+ writel(cfg, dev->regs + S5P_ORGISIZE);
+}
+
+void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable)
+{
+ unsigned long flags;
+ u32 cfg;
+
+ spin_lock_irqsave(&dev->slock, flags);
+
+ cfg = readl(dev->regs + S5P_CIOCTRL);
+ if (enable)
+ cfg |= S5P_CIOCTRL_LASTIRQ_ENABLE;
+ else
+ cfg &= ~S5P_CIOCTRL_LASTIRQ_ENABLE;
+ writel(cfg, dev->regs + S5P_CIOCTRL);
+
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+void fimc_hw_set_prescaler(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_scaler *sc = &ctx->scaler;
+ u32 cfg = 0, shfactor;
+
+ shfactor = 10 - (sc->hfactor + sc->vfactor);
+
+ cfg |= S5P_CISCPRERATIO_SHFACTOR(shfactor);
+ cfg |= S5P_CISCPRERATIO_HOR(sc->pre_hratio);
+ cfg |= S5P_CISCPRERATIO_VER(sc->pre_vratio);
+ writel(cfg, dev->regs + S5P_CISCPRERATIO);
+
+ cfg = 0;
+ cfg |= S5P_CISCPREDST_WIDTH(sc->pre_dst_width);
+ cfg |= S5P_CISCPREDST_HEIGHT(sc->pre_dst_height);
+ writel(cfg, dev->regs + S5P_CISCPREDST);
+}
+
+void fimc_hw_set_scaler(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_scaler *sc = &ctx->scaler;
+ struct fimc_frame *src_frame = &ctx->s_frame;
+ struct fimc_frame *dst_frame = &ctx->d_frame;
+ u32 cfg = 0;
+
+ if (!(ctx->flags & FIMC_COLOR_RANGE_NARROW))
+ cfg |= (S5P_CISCCTRL_CSCR2Y_WIDE | S5P_CISCCTRL_CSCY2R_WIDE);
+
+ if (!sc->enabled)
+ cfg |= S5P_CISCCTRL_SCALERBYPASS;
+
+ if (sc->scaleup_h)
+ cfg |= S5P_CISCCTRL_SCALEUP_H;
+
+ if (sc->scaleup_v)
+ cfg |= S5P_CISCCTRL_SCALEUP_V;
+
+ if (sc->copy_mode)
+ cfg |= S5P_CISCCTRL_ONE2ONE;
+
+
+ if (ctx->in_path == FIMC_DMA) {
+ if (src_frame->fmt->color == S5P_FIMC_RGB565)
+ cfg |= S5P_CISCCTRL_INRGB_FMT_RGB565;
+ else if (src_frame->fmt->color == S5P_FIMC_RGB666)
+ cfg |= S5P_CISCCTRL_INRGB_FMT_RGB666;
+ else if (src_frame->fmt->color == S5P_FIMC_RGB888)
+ cfg |= S5P_CISCCTRL_INRGB_FMT_RGB888;
+ }
+
+ if (ctx->out_path == FIMC_DMA) {
+ if (dst_frame->fmt->color == S5P_FIMC_RGB565)
+ cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB565;
+ else if (dst_frame->fmt->color == S5P_FIMC_RGB666)
+ cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB666;
+ else if (dst_frame->fmt->color == S5P_FIMC_RGB888)
+ cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888;
+ } else {
+ cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888;
+
+ if (ctx->flags & FIMC_SCAN_MODE_INTERLACED)
+ cfg |= S5P_CISCCTRL_INTERLACE;
+ }
+
+ dbg("main_hratio= 0x%X main_vratio= 0x%X",
+ sc->main_hratio, sc->main_vratio);
+
+ cfg |= S5P_CISCCTRL_SC_HORRATIO(sc->main_hratio);
+ cfg |= S5P_CISCCTRL_SC_VERRATIO(sc->main_vratio);
+
+ writel(cfg, dev->regs + S5P_CISCCTRL);
+}
+
+void fimc_hw_en_capture(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ u32 cfg;
+
+ cfg = readl(dev->regs + S5P_CIIMGCPT);
+ /* One shot mode for output DMA or freerun for FIFO. */
+ if (ctx->out_path == FIMC_DMA)
+ cfg |= S5P_CIIMGCPT_CPT_FREN_ENABLE;
+ else
+ cfg &= ~S5P_CIIMGCPT_CPT_FREN_ENABLE;
+
+ if (ctx->scaler.enabled)
+ cfg |= S5P_CIIMGCPT_IMGCPTEN_SC;
+
+ writel(cfg | S5P_CIIMGCPT_IMGCPTEN, dev->regs + S5P_CIIMGCPT);
+}
+
+void fimc_hw_set_effect(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_effect *effect = &ctx->effect;
+ u32 cfg = (S5P_CIIMGEFF_IE_ENABLE | S5P_CIIMGEFF_IE_SC_AFTER);
+
+ cfg |= effect->type;
+
+ if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) {
+ cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb);
+ cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr);
+ }
+
+ writel(cfg, dev->regs + S5P_CIIMGEFF);
+}
+
+static void fimc_hw_set_in_dma_size(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_frame *frame = &ctx->s_frame;
+ u32 cfg_o = 0;
+ u32 cfg_r = 0;
+
+ if (FIMC_LCDFIFO == ctx->out_path)
+ cfg_r |= S5P_CIREAL_ISIZE_AUTOLOAD_EN;
+
+ cfg_o |= S5P_ORIG_SIZE_HOR(frame->f_width);
+ cfg_o |= S5P_ORIG_SIZE_VER(frame->f_height);
+ cfg_r |= S5P_CIREAL_ISIZE_WIDTH(frame->width);
+ cfg_r |= S5P_CIREAL_ISIZE_HEIGHT(frame->height);
+
+ writel(cfg_o, dev->regs + S5P_ORGISIZE);
+ writel(cfg_r, dev->regs + S5P_CIREAL_ISIZE);
+}
+
+void fimc_hw_set_in_dma(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+ struct fimc_frame *frame = &ctx->s_frame;
+ struct fimc_dma_offset *offset = &frame->dma_offset;
+ u32 cfg = 0;
+
+ /* Set the pixel offsets. */
+ cfg |= S5P_CIO_OFFS_HOR(offset->y_h);
+ cfg |= S5P_CIO_OFFS_VER(offset->y_v);
+ writel(cfg, dev->regs + S5P_CIIYOFF);
+
+ cfg = 0;
+ cfg |= S5P_CIO_OFFS_HOR(offset->cb_h);
+ cfg |= S5P_CIO_OFFS_VER(offset->cb_v);
+ writel(cfg, dev->regs + S5P_CIICBOFF);
+
+ cfg = 0;
+ cfg |= S5P_CIO_OFFS_HOR(offset->cr_h);
+ cfg |= S5P_CIO_OFFS_VER(offset->cr_v);
+ writel(cfg, dev->regs + S5P_CIICROFF);
+
+ /* Input original and real size. */
+ fimc_hw_set_in_dma_size(ctx);
+
+ /* Autoload is used currently only in FIFO mode. */
+ fimc_hw_en_autoload(dev, ctx->out_path == FIMC_LCDFIFO);
+
+ /* Set the input DMA to process single frame only. */
+ cfg = readl(dev->regs + S5P_MSCTRL);
+ cfg &= ~(S5P_MSCTRL_FLIP_MASK
+ | S5P_MSCTRL_INFORMAT_MASK
+ | S5P_MSCTRL_IN_BURST_COUNT_MASK
+ | S5P_MSCTRL_INPUT_MASK
+ | S5P_MSCTRL_C_INT_IN_MASK
+ | S5P_MSCTRL_2P_IN_ORDER_MASK);
+
+ cfg |= (S5P_MSCTRL_FRAME_COUNT(1) | S5P_MSCTRL_INPUT_MEMORY);
+
+ switch (frame->fmt->color) {
+ case S5P_FIMC_RGB565:
+ case S5P_FIMC_RGB666:
+ case S5P_FIMC_RGB888:
+ cfg |= S5P_MSCTRL_INFORMAT_RGB;
+ break;
+ case S5P_FIMC_YCBCR420:
+ cfg |= S5P_MSCTRL_INFORMAT_YCBCR420;
+
+ if (frame->fmt->planes_cnt == 2)
+ cfg |= ctx->in_order_2p | S5P_MSCTRL_C_INT_IN_2PLANE;
+ else
+ cfg |= S5P_MSCTRL_C_INT_IN_3PLANE;
+
+ break;
+ case S5P_FIMC_YCBYCR422:
+ case S5P_FIMC_YCRYCB422:
+ case S5P_FIMC_CBYCRY422:
+ case S5P_FIMC_CRYCBY422:
+ if (frame->fmt->planes_cnt == 1) {
+ cfg |= ctx->in_order_1p
+ | S5P_MSCTRL_INFORMAT_YCBCR422_1P;
+ } else {
+ cfg |= S5P_MSCTRL_INFORMAT_YCBCR422;
+
+ if (frame->fmt->planes_cnt == 2)
+ cfg |= ctx->in_order_2p
+ | S5P_MSCTRL_C_INT_IN_2PLANE;
+ else
+ cfg |= S5P_MSCTRL_C_INT_IN_3PLANE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Input DMA flip mode (and rotation).
+ * Do not allow simultaneous rotation and flipping.
+ */
+ if (!ctx->rotation && ctx->out_path == FIMC_LCDFIFO)
+ cfg |= fimc_hw_get_in_flip(ctx->flip);
+
+ writel(cfg, dev->regs + S5P_MSCTRL);
+
+ /* Input/output DMA linear/tiled mode. */
+ cfg = readl(dev->regs + S5P_CIDMAPARAM);
+ cfg &= ~S5P_CIDMAPARAM_TILE_MASK;
+
+ if (tiled_fmt(ctx->s_frame.fmt))
+ cfg |= S5P_CIDMAPARAM_R_64X32;
+
+ if (tiled_fmt(ctx->d_frame.fmt))
+ cfg |= S5P_CIDMAPARAM_W_64X32;
+
+ writel(cfg, dev->regs + S5P_CIDMAPARAM);
+}
+
+
+void fimc_hw_set_input_path(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+
+ u32 cfg = readl(dev->regs + S5P_MSCTRL);
+ cfg &= ~S5P_MSCTRL_INPUT_MASK;
+
+ if (ctx->in_path == FIMC_DMA)
+ cfg |= S5P_MSCTRL_INPUT_MEMORY;
+ else
+ cfg |= S5P_MSCTRL_INPUT_EXTCAM;
+
+ writel(cfg, dev->regs + S5P_MSCTRL);
+}
+
+void fimc_hw_set_output_path(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *dev = ctx->fimc_dev;
+
+ u32 cfg = readl(dev->regs + S5P_CISCCTRL);
+ cfg &= ~S5P_CISCCTRL_LCDPATHEN_FIFO;
+ if (ctx->out_path == FIMC_LCDFIFO)
+ cfg |= S5P_CISCCTRL_LCDPATHEN_FIFO;
+ writel(cfg, dev->regs + S5P_CISCCTRL);
+}
+
+void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *paddr)
+{
+ u32 cfg = 0;
+
+ cfg = readl(dev->regs + S5P_CIREAL_ISIZE);
+ cfg |= S5P_CIREAL_ISIZE_ADDR_CH_DIS;
+ writel(cfg, dev->regs + S5P_CIREAL_ISIZE);
+
+ writel(paddr->y, dev->regs + S5P_CIIYSA0);
+ writel(paddr->cb, dev->regs + S5P_CIICBSA0);
+ writel(paddr->cr, dev->regs + S5P_CIICRSA0);
+
+ cfg &= ~S5P_CIREAL_ISIZE_ADDR_CH_DIS;
+ writel(cfg, dev->regs + S5P_CIREAL_ISIZE);
+}
+
+void fimc_hw_set_output_addr(struct fimc_dev *dev, struct fimc_addr *paddr)
+{
+ int i;
+ /* Set all the output register sets to point to single video buffer. */
+ for (i = 0; i < FIMC_MAX_OUT_BUFS; i++) {
+ writel(paddr->y, dev->regs + S5P_CIOYSA(i));
+ writel(paddr->cb, dev->regs + S5P_CIOCBSA(i));
+ writel(paddr->cr, dev->regs + S5P_CIOCRSA(i));
+ }
+}
--- /dev/null
+/*
+ * Register definition file for Samsung Camera Interface (FIMC) driver
+ *
+ * Copyright (c) 2010 Samsung Electronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef REGS_FIMC_H_
+#define REGS_FIMC_H_
+
+#define S5P_CIOYSA(__x) (0x18 + (__x) * 4)
+#define S5P_CIOCBSA(__x) (0x28 + (__x) * 4)
+#define S5P_CIOCRSA(__x) (0x38 + (__x) * 4)
+
+/* Input source format */
+#define S5P_CISRCFMT 0x00
+#define S5P_CISRCFMT_ITU601_8BIT (1 << 31)
+#define S5P_CISRCFMT_ITU601_16BIT (1 << 29)
+#define S5P_CISRCFMT_ORDER422_YCBYCR (0 << 14)
+#define S5P_CISRCFMT_ORDER422_YCRYCB (1 << 14)
+#define S5P_CISRCFMT_ORDER422_CBYCRY (2 << 14)
+#define S5P_CISRCFMT_ORDER422_CRYCBY (3 << 14)
+#define S5P_CISRCFMT_HSIZE(x) ((x) << 16)
+#define S5P_CISRCFMT_VSIZE(x) ((x) << 0)
+
+/* Window offset */
+#define S5P_CIWDOFST 0x04
+#define S5P_CIWDOFST_WINOFSEN (1 << 31)
+#define S5P_CIWDOFST_CLROVFIY (1 << 30)
+#define S5P_CIWDOFST_CLROVRLB (1 << 29)
+#define S5P_CIWDOFST_WINHOROFST_MASK (0x7ff << 16)
+#define S5P_CIWDOFST_CLROVFICB (1 << 15)
+#define S5P_CIWDOFST_CLROVFICR (1 << 14)
+#define S5P_CIWDOFST_WINHOROFST(x) ((x) << 16)
+#define S5P_CIWDOFST_WINVEROFST(x) ((x) << 0)
+#define S5P_CIWDOFST_WINVEROFST_MASK (0xfff << 0)
+
+/* Global control */
+#define S5P_CIGCTRL 0x08
+#define S5P_CIGCTRL_SWRST (1 << 31)
+#define S5P_CIGCTRL_CAMRST_A (1 << 30)
+#define S5P_CIGCTRL_SELCAM_ITU_A (1 << 29)
+#define S5P_CIGCTRL_SELCAM_ITU_MASK (1 << 29)
+#define S5P_CIGCTRL_TESTPAT_NORMAL (0 << 27)
+#define S5P_CIGCTRL_TESTPAT_COLOR_BAR (1 << 27)
+#define S5P_CIGCTRL_TESTPAT_HOR_INC (2 << 27)
+#define S5P_CIGCTRL_TESTPAT_VER_INC (3 << 27)
+#define S5P_CIGCTRL_TESTPAT_MASK (3 << 27)
+#define S5P_CIGCTRL_TESTPAT_SHIFT (27)
+#define S5P_CIGCTRL_INVPOLPCLK (1 << 26)
+#define S5P_CIGCTRL_INVPOLVSYNC (1 << 25)
+#define S5P_CIGCTRL_INVPOLHREF (1 << 24)
+#define S5P_CIGCTRL_IRQ_OVFEN (1 << 22)
+#define S5P_CIGCTRL_HREF_MASK (1 << 21)
+#define S5P_CIGCTRL_IRQ_LEVEL (1 << 20)
+#define S5P_CIGCTRL_IRQ_CLR (1 << 19)
+#define S5P_CIGCTRL_IRQ_ENABLE (1 << 16)
+#define S5P_CIGCTRL_SHDW_DISABLE (1 << 12)
+#define S5P_CIGCTRL_SELCAM_MIPI_A (1 << 7)
+#define S5P_CIGCTRL_CAMIF_SELWB (1 << 6)
+#define S5P_CIGCTRL_INVPOLHSYNC (1 << 4)
+#define S5P_CIGCTRL_SELCAM_MIPI (1 << 3)
+#define S5P_CIGCTRL_INTERLACE (1 << 0)
+
+/* Window offset 2 */
+#define S5P_CIWDOFST2 0x14
+#define S5P_CIWDOFST2_HOROFF_MASK (0xfff << 16)
+#define S5P_CIWDOFST2_VEROFF_MASK (0xfff << 0)
+#define S5P_CIWDOFST2_HOROFF(x) ((x) << 16)
+#define S5P_CIWDOFST2_VEROFF(x) ((x) << 0)
+
+/* Output DMA Y plane start address */
+#define S5P_CIOYSA1 0x18
+#define S5P_CIOYSA2 0x1c
+#define S5P_CIOYSA3 0x20
+#define S5P_CIOYSA4 0x24
+
+/* Output DMA Cb plane start address */
+#define S5P_CIOCBSA1 0x28
+#define S5P_CIOCBSA2 0x2c
+#define S5P_CIOCBSA3 0x30
+#define S5P_CIOCBSA4 0x34
+
+/* Output DMA Cr plane start address */
+#define S5P_CIOCRSA1 0x38
+#define S5P_CIOCRSA2 0x3c
+#define S5P_CIOCRSA3 0x40
+#define S5P_CIOCRSA4 0x44
+
+/* Target image format */
+#define S5P_CITRGFMT 0x48
+#define S5P_CITRGFMT_INROT90 (1 << 31)
+#define S5P_CITRGFMT_YCBCR420 (0 << 29)
+#define S5P_CITRGFMT_YCBCR422 (1 << 29)
+#define S5P_CITRGFMT_YCBCR422_1P (2 << 29)
+#define S5P_CITRGFMT_RGB (3 << 29)
+#define S5P_CITRGFMT_FMT_MASK (3 << 29)
+#define S5P_CITRGFMT_HSIZE_MASK (0xfff << 16)
+#define S5P_CITRGFMT_FLIP_SHIFT (14)
+#define S5P_CITRGFMT_FLIP_NORMAL (0 << 14)
+#define S5P_CITRGFMT_FLIP_X_MIRROR (1 << 14)
+#define S5P_CITRGFMT_FLIP_Y_MIRROR (2 << 14)
+#define S5P_CITRGFMT_FLIP_180 (3 << 14)
+#define S5P_CITRGFMT_FLIP_MASK (3 << 14)
+#define S5P_CITRGFMT_OUTROT90 (1 << 13)
+#define S5P_CITRGFMT_VSIZE_MASK (0xfff << 0)
+#define S5P_CITRGFMT_HSIZE(x) ((x) << 16)
+#define S5P_CITRGFMT_VSIZE(x) ((x) << 0)
+
+/* Output DMA control */
+#define S5P_CIOCTRL 0x4c
+#define S5P_CIOCTRL_ORDER422_MASK (3 << 0)
+#define S5P_CIOCTRL_ORDER422_CRYCBY (0 << 0)
+#define S5P_CIOCTRL_ORDER422_YCRYCB (1 << 0)
+#define S5P_CIOCTRL_ORDER422_CBYCRY (2 << 0)
+#define S5P_CIOCTRL_ORDER422_YCBYCR (3 << 0)
+#define S5P_CIOCTRL_LASTIRQ_ENABLE (1 << 2)
+#define S5P_CIOCTRL_YCBCR_3PLANE (0 << 3)
+#define S5P_CIOCTRL_YCBCR_2PLANE (1 << 3)
+#define S5P_CIOCTRL_YCBCR_PLANE_MASK (1 << 3)
+#define S5P_CIOCTRL_ORDER2P_SHIFT (24)
+#define S5P_CIOCTRL_ORDER2P_MASK (3 << 24)
+#define S5P_CIOCTRL_ORDER422_2P_LSB_CRCB (0 << 24)
+
+/* Pre-scaler control 1 */
+#define S5P_CISCPRERATIO 0x50
+#define S5P_CISCPRERATIO_SHFACTOR(x) ((x) << 28)
+#define S5P_CISCPRERATIO_HOR(x) ((x) << 16)
+#define S5P_CISCPRERATIO_VER(x) ((x) << 0)
+
+#define S5P_CISCPREDST 0x54
+#define S5P_CISCPREDST_WIDTH(x) ((x) << 16)
+#define S5P_CISCPREDST_HEIGHT(x) ((x) << 0)
+
+/* Main scaler control */
+#define S5P_CISCCTRL 0x58
+#define S5P_CISCCTRL_SCALERBYPASS (1 << 31)
+#define S5P_CISCCTRL_SCALEUP_H (1 << 30)
+#define S5P_CISCCTRL_SCALEUP_V (1 << 29)
+#define S5P_CISCCTRL_CSCR2Y_WIDE (1 << 28)
+#define S5P_CISCCTRL_CSCY2R_WIDE (1 << 27)
+#define S5P_CISCCTRL_LCDPATHEN_FIFO (1 << 26)
+#define S5P_CISCCTRL_INTERLACE (1 << 25)
+#define S5P_CISCCTRL_SCALERSTART (1 << 15)
+#define S5P_CISCCTRL_INRGB_FMT_RGB565 (0 << 13)
+#define S5P_CISCCTRL_INRGB_FMT_RGB666 (1 << 13)
+#define S5P_CISCCTRL_INRGB_FMT_RGB888 (2 << 13)
+#define S5P_CISCCTRL_INRGB_FMT_MASK (3 << 13)
+#define S5P_CISCCTRL_OUTRGB_FMT_RGB565 (0 << 11)
+#define S5P_CISCCTRL_OUTRGB_FMT_RGB666 (1 << 11)
+#define S5P_CISCCTRL_OUTRGB_FMT_RGB888 (2 << 11)
+#define S5P_CISCCTRL_OUTRGB_FMT_MASK (3 << 11)
+#define S5P_CISCCTRL_RGB_EXT (1 << 10)
+#define S5P_CISCCTRL_ONE2ONE (1 << 9)
+#define S5P_CISCCTRL_SC_HORRATIO(x) ((x) << 16)
+#define S5P_CISCCTRL_SC_VERRATIO(x) ((x) << 0)
+
+/* Target area */
+#define S5P_CITAREA 0x5c
+#define S5P_CITAREA_MASK 0x0fffffff
+
+/* General status */
+#define S5P_CISTATUS 0x64
+#define S5P_CISTATUS_OVFIY (1 << 31)
+#define S5P_CISTATUS_OVFICB (1 << 30)
+#define S5P_CISTATUS_OVFICR (1 << 29)
+#define S5P_CISTATUS_VSYNC (1 << 28)
+#define S5P_CISTATUS_WINOFF_EN (1 << 25)
+#define S5P_CISTATUS_IMGCPT_EN (1 << 22)
+#define S5P_CISTATUS_IMGCPT_SCEN (1 << 21)
+#define S5P_CISTATUS_VSYNC_A (1 << 20)
+#define S5P_CISTATUS_VSYNC_B (1 << 19)
+#define S5P_CISTATUS_OVRLB (1 << 18)
+#define S5P_CISTATUS_FRAME_END (1 << 17)
+#define S5P_CISTATUS_LASTCAPT_END (1 << 16)
+#define S5P_CISTATUS_VVALID_A (1 << 15)
+#define S5P_CISTATUS_VVALID_B (1 << 14)
+
+/* Image capture control */
+#define S5P_CIIMGCPT 0xc0
+#define S5P_CIIMGCPT_IMGCPTEN (1 << 31)
+#define S5P_CIIMGCPT_IMGCPTEN_SC (1 << 30)
+#define S5P_CIIMGCPT_CPT_FREN_ENABLE (1 << 25)
+#define S5P_CIIMGCPT_CPT_FRMOD_CNT (1 << 18)
+
+/* Frame capture sequence */
+#define S5P_CICPTSEQ 0xc4
+
+/* Image effect */
+#define S5P_CIIMGEFF 0xd0
+#define S5P_CIIMGEFF_IE_DISABLE (0 << 30)
+#define S5P_CIIMGEFF_IE_ENABLE (1 << 30)
+#define S5P_CIIMGEFF_IE_SC_BEFORE (0 << 29)
+#define S5P_CIIMGEFF_IE_SC_AFTER (1 << 29)
+#define S5P_CIIMGEFF_FIN_BYPASS (0 << 26)
+#define S5P_CIIMGEFF_FIN_ARBITRARY (1 << 26)
+#define S5P_CIIMGEFF_FIN_NEGATIVE (2 << 26)
+#define S5P_CIIMGEFF_FIN_ARTFREEZE (3 << 26)
+#define S5P_CIIMGEFF_FIN_EMBOSSING (4 << 26)
+#define S5P_CIIMGEFF_FIN_SILHOUETTE (5 << 26)
+#define S5P_CIIMGEFF_FIN_MASK (7 << 26)
+#define S5P_CIIMGEFF_PAT_CBCR_MASK ((0xff < 13) | (0xff < 0))
+#define S5P_CIIMGEFF_PAT_CB(x) ((x) << 13)
+#define S5P_CIIMGEFF_PAT_CR(x) ((x) << 0)
+
+/* Input DMA Y/Cb/Cr plane start address 0 */
+#define S5P_CIIYSA0 0xd4
+#define S5P_CIICBSA0 0xd8
+#define S5P_CIICRSA0 0xdc
+
+/* Real input DMA image size */
+#define S5P_CIREAL_ISIZE 0xf8
+#define S5P_CIREAL_ISIZE_AUTOLOAD_EN (1 << 31)
+#define S5P_CIREAL_ISIZE_ADDR_CH_DIS (1 << 30)
+#define S5P_CIREAL_ISIZE_HEIGHT(x) ((x) << 16)
+#define S5P_CIREAL_ISIZE_WIDTH(x) ((x) << 0)
+
+
+/* Input DMA control */
+#define S5P_MSCTRL 0xfc
+#define S5P_MSCTRL_IN_BURST_COUNT_MASK (3 << 24)
+#define S5P_MSCTRL_2P_IN_ORDER_MASK (3 << 16)
+#define S5P_MSCTRL_2P_IN_ORDER_SHIFT 16
+#define S5P_MSCTRL_C_INT_IN_3PLANE (0 << 15)
+#define S5P_MSCTRL_C_INT_IN_2PLANE (1 << 15)
+#define S5P_MSCTRL_C_INT_IN_MASK (1 << 15)
+#define S5P_MSCTRL_FLIP_SHIFT 13
+#define S5P_MSCTRL_FLIP_MASK (3 << 13)
+#define S5P_MSCTRL_FLIP_NORMAL (0 << 13)
+#define S5P_MSCTRL_FLIP_X_MIRROR (1 << 13)
+#define S5P_MSCTRL_FLIP_Y_MIRROR (2 << 13)
+#define S5P_MSCTRL_FLIP_180 (3 << 13)
+#define S5P_MSCTRL_ORDER422_SHIFT 4
+#define S5P_MSCTRL_ORDER422_CRYCBY (0 << 4)
+#define S5P_MSCTRL_ORDER422_YCRYCB (1 << 4)
+#define S5P_MSCTRL_ORDER422_CBYCRY (2 << 4)
+#define S5P_MSCTRL_ORDER422_YCBYCR (3 << 4)
+#define S5P_MSCTRL_ORDER422_MASK (3 << 4)
+#define S5P_MSCTRL_INPUT_EXTCAM (0 << 3)
+#define S5P_MSCTRL_INPUT_MEMORY (1 << 3)
+#define S5P_MSCTRL_INPUT_MASK (1 << 3)
+#define S5P_MSCTRL_INFORMAT_YCBCR420 (0 << 1)
+#define S5P_MSCTRL_INFORMAT_YCBCR422 (1 << 1)
+#define S5P_MSCTRL_INFORMAT_YCBCR422_1P (2 << 1)
+#define S5P_MSCTRL_INFORMAT_RGB (3 << 1)
+#define S5P_MSCTRL_INFORMAT_MASK (3 << 1)
+#define S5P_MSCTRL_ENVID (1 << 0)
+#define S5P_MSCTRL_FRAME_COUNT(x) ((x) << 24)
+
+/* Input DMA Y/Cb/Cr plane start address 1 */
+#define S5P_CIIYSA1 0x144
+#define S5P_CIICBSA1 0x148
+#define S5P_CIICRSA1 0x14c
+
+/* Output DMA Y/Cb/Cr offset */
+#define S5P_CIOYOFF 0x168
+#define S5P_CIOCBOFF 0x16c
+#define S5P_CIOCROFF 0x170
+
+/* Input DMA Y/Cb/Cr offset */
+#define S5P_CIIYOFF 0x174
+#define S5P_CIICBOFF 0x178
+#define S5P_CIICROFF 0x17c
+
+#define S5P_CIO_OFFS_VER(x) ((x) << 16)
+#define S5P_CIO_OFFS_HOR(x) ((x) << 0)
+
+/* Input DMA original image size */
+#define S5P_ORGISIZE 0x180
+
+/* Output DMA original image size */
+#define S5P_ORGOSIZE 0x184
+
+#define S5P_ORIG_SIZE_VER(x) ((x) << 16)
+#define S5P_ORIG_SIZE_HOR(x) ((x) << 0)
+
+/* Real output DMA image size (extension register) */
+#define S5P_CIEXTEN 0x188
+
+#define S5P_CIDMAPARAM 0x18c
+#define S5P_CIDMAPARAM_R_LINEAR (0 << 29)
+#define S5P_CIDMAPARAM_R_64X32 (3 << 29)
+#define S5P_CIDMAPARAM_W_LINEAR (0 << 13)
+#define S5P_CIDMAPARAM_W_64X32 (3 << 13)
+#define S5P_CIDMAPARAM_TILE_MASK ((3 << 29) | (3 << 13))
+
+/* MIPI CSI image format */
+#define S5P_CSIIMGFMT 0x194
+
+#endif /* REGS_FIMC_H_ */
#include <linux/i2c.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
#include <media/saa7115.h>
struct saa711x_state {
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+
+ struct {
+ /* chroma gain control cluster */
+ struct v4l2_ctrl *agc;
+ struct v4l2_ctrl *gain;
+ };
+
v4l2_std_id std;
int input;
int output;
int enable;
int radio;
- int bright;
- int contrast;
- int hue;
- int sat;
- int chroma_agc;
int width;
int height;
u32 ident;
return container_of(sd, struct saa711x_state, sd);
}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct saa711x_state, hdl)->sd;
+}
+
/* ----------------------------------------------------------------------- */
static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value)
return 0;
}
-static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int saa711x_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd = to_sd(ctrl);
struct saa711x_state *state = to_state(sd);
- u8 val;
switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- if (ctrl->value < 0 || ctrl->value > 255) {
- v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value);
- return -ERANGE;
- }
-
- state->bright = ctrl->value;
- saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, state->bright);
- break;
-
- case V4L2_CID_CONTRAST:
- if (ctrl->value < 0 || ctrl->value > 127) {
- v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value);
- return -ERANGE;
- }
-
- state->contrast = ctrl->value;
- saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, state->contrast);
- break;
-
- case V4L2_CID_SATURATION:
- if (ctrl->value < 0 || ctrl->value > 127) {
- v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value);
- return -ERANGE;
- }
-
- state->sat = ctrl->value;
- saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, state->sat);
- break;
-
- case V4L2_CID_HUE:
- if (ctrl->value < -128 || ctrl->value > 127) {
- v4l2_err(sd, "invalid hue setting %d\n", ctrl->value);
- return -ERANGE;
- }
-
- state->hue = ctrl->value;
- saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, state->hue);
- break;
case V4L2_CID_CHROMA_AGC:
- val = saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL);
- state->chroma_agc = ctrl->value;
- if (ctrl->value)
- val &= 0x7f;
- else
- val |= 0x80;
- saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, val);
+ /* chroma gain cluster */
+ if (state->agc->cur.val)
+ state->gain->cur.val =
+ saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f;
break;
- case V4L2_CID_CHROMA_GAIN:
- /* Chroma gain cannot be set when AGC is enabled */
- if (state->chroma_agc == 1)
- return -EINVAL;
- saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, ctrl->value | 0x80);
- break;
- default:
- return -EINVAL;
}
-
return 0;
}
-static int saa711x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int saa711x_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd = to_sd(ctrl);
struct saa711x_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- ctrl->value = state->bright;
+ saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, ctrl->val);
break;
+
case V4L2_CID_CONTRAST:
- ctrl->value = state->contrast;
+ saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, ctrl->val);
break;
+
case V4L2_CID_SATURATION:
- ctrl->value = state->sat;
+ saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, ctrl->val);
break;
+
case V4L2_CID_HUE:
- ctrl->value = state->hue;
+ saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, ctrl->val);
break;
+
case V4L2_CID_CHROMA_AGC:
- ctrl->value = state->chroma_agc;
- break;
- case V4L2_CID_CHROMA_GAIN:
- ctrl->value = saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f;
+ /* chroma gain cluster */
+ if (state->agc->val)
+ saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val);
+ else
+ saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val | 0x80);
+ v4l2_ctrl_activate(state->gain, !state->agc->val);
break;
+
default:
return -EINVAL;
}
return 0;
}
-static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
- case V4L2_CID_CHROMA_AGC:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- case V4L2_CID_CHROMA_GAIN:
- return v4l2_ctrl_query_fill(qc, 0, 127, 1, 48);
- default:
- return -EINVAL;
- }
-}
-
static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct saa711x_state *state = to_state(sd);
break;
}
v4l2_info(sd, "Width, Height: %d, %d\n", state->width, state->height);
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
return 0;
}
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops saa711x_ctrl_ops = {
+ .s_ctrl = saa711x_s_ctrl,
+ .g_volatile_ctrl = saa711x_g_volatile_ctrl,
+};
+
static const struct v4l2_subdev_core_ops saa711x_core_ops = {
.log_status = saa711x_log_status,
.g_chip_ident = saa711x_g_chip_ident,
- .g_ctrl = saa711x_g_ctrl,
- .s_ctrl = saa711x_s_ctrl,
- .queryctrl = saa711x_queryctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
.s_std = saa711x_s_std,
.reset = saa711x_reset,
.s_gpio = saa711x_s_gpio,
{
struct saa711x_state *state;
struct v4l2_subdev *sd;
- int i;
- char name[17];
+ struct v4l2_ctrl_handler *hdl;
+ int i;
+ char name[17];
char chip_id;
int autodetect = !id || id->driver_data == 1;
return -ENOMEM;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &saa711x_ops);
+
+ hdl = &state->hdl;
+ v4l2_ctrl_handler_init(hdl, 6);
+ /* add in ascending ID order */
+ v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ state->agc = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_CHROMA_AGC, 0, 1, 1, 1);
+ state->gain = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_CHROMA_GAIN, 0, 127, 1, 40);
+ state->gain->is_volatile = 1;
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ kfree(state);
+ return err;
+ }
+ state->agc->flags |= V4L2_CTRL_FLAG_UPDATE;
+ v4l2_ctrl_cluster(2, &state->agc);
+
state->input = -1;
state->output = SAA7115_IPORT_ON;
state->enable = 1;
state->radio = 0;
- state->bright = 128;
- state->contrast = 64;
- state->hue = 0;
- state->sat = 64;
- state->chroma_agc = 1;
switch (chip_id) {
case '1':
state->ident = V4L2_IDENT_SAA7111;
if (state->ident > V4L2_IDENT_SAA7111A)
saa711x_writeregs(sd, saa7115_init_misc);
saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
+ v4l2_ctrl_handler_setup(hdl);
v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n",
saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC),
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
kfree(to_state(sd));
return 0;
}
#include <linux/videodev2.h>
#include <linux/i2c.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-i2c-drv.h>
MODULE_DESCRIPTION("Philips SAA717x audio/video decoder driver");
struct saa717x_state {
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
v4l2_std_id std;
int input;
int enable;
int radio;
- int bright;
- int contrast;
- int hue;
- int sat;
int playback;
int audio;
int tuner_audio_mode;
return container_of(sd, struct saa717x_state, sd);
}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct saa717x_state, hdl)->sd;
+}
+
/* ----------------------------------------------------------------------- */
/* for audio mode */
saa717x_write(sd, 0x470, reg_set_audio_template[audio_mode][1]);
}
-/* write regs to video output level (bright,contrast,hue,sat) */
-static void set_video_output_level_regs(struct v4l2_subdev *sd,
- struct saa717x_state *decoder)
-{
- /* brightness ffh (bright) - 80h (ITU level) - 00h (dark) */
- saa717x_write(sd, 0x10a, decoder->bright);
-
- /* contrast 7fh (max: 1.984) - 44h (ITU) - 40h (1.0) -
- 0h (luminance off) 40: i2c dump
- c0h (-1.0 inverse chrominance)
- 80h (-2.0 inverse chrominance) */
- saa717x_write(sd, 0x10b, decoder->contrast);
-
- /* saturation? 7fh(max)-40h(ITU)-0h(color off)
- c0h (-1.0 inverse chrominance)
- 80h (-2.0 inverse chrominance) */
- saa717x_write(sd, 0x10c, decoder->sat);
-
- /* color hue (phase) control
- 7fh (+178.6) - 0h (0 normal) - 80h (-180.0) */
- saa717x_write(sd, 0x10d, decoder->hue);
-}
-
/* write regs to set audio volume, bass and treble */
static int set_audio_regs(struct v4l2_subdev *sd,
struct saa717x_state *decoder)
saa717x_write(sd, 0x480, val);
- /* bass and treble; go to another function */
/* set bass and treble */
- val = decoder->audio_main_bass | (decoder->audio_main_treble << 8);
+ val = decoder->audio_main_bass & 0x1f;
+ val |= (decoder->audio_main_treble & 0x1f) << 5;
saa717x_write(sd, 0x488, val);
return 0;
}
saa717x_write(sd, 0x71 + task_shift, yscale >> 8);
}
-static int saa717x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct saa717x_state *state = to_state(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- if (ctrl->value < 0 || ctrl->value > 255) {
- v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value);
- return -ERANGE;
- }
-
- state->bright = ctrl->value;
- v4l2_dbg(1, debug, sd, "bright:%d\n", state->bright);
- saa717x_write(sd, 0x10a, state->bright);
- break;
-
- case V4L2_CID_CONTRAST:
- if (ctrl->value < 0 || ctrl->value > 127) {
- v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value);
- return -ERANGE;
- }
-
- state->contrast = ctrl->value;
- v4l2_dbg(1, debug, sd, "contrast:%d\n", state->contrast);
- saa717x_write(sd, 0x10b, state->contrast);
- break;
-
- case V4L2_CID_SATURATION:
- if (ctrl->value < 0 || ctrl->value > 127) {
- v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value);
- return -ERANGE;
- }
-
- state->sat = ctrl->value;
- v4l2_dbg(1, debug, sd, "sat:%d\n", state->sat);
- saa717x_write(sd, 0x10c, state->sat);
- break;
-
- case V4L2_CID_HUE:
- if (ctrl->value < -128 || ctrl->value > 127) {
- v4l2_err(sd, "invalid hue setting %d\n", ctrl->value);
- return -ERANGE;
- }
-
- state->hue = ctrl->value;
- v4l2_dbg(1, debug, sd, "hue:%d\n", state->hue);
- saa717x_write(sd, 0x10d, state->hue);
- break;
-
- case V4L2_CID_AUDIO_MUTE:
- state->audio_main_mute = ctrl->value;
- set_audio_regs(sd, state);
- break;
-
- case V4L2_CID_AUDIO_VOLUME:
- state->audio_main_volume = ctrl->value;
- set_audio_regs(sd, state);
- break;
-
- case V4L2_CID_AUDIO_BALANCE:
- state->audio_main_balance = ctrl->value;
- set_audio_regs(sd, state);
- break;
-
- case V4L2_CID_AUDIO_TREBLE:
- state->audio_main_treble = ctrl->value;
- set_audio_regs(sd, state);
- break;
-
- case V4L2_CID_AUDIO_BASS:
- state->audio_main_bass = ctrl->value;
- set_audio_regs(sd, state);
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int saa717x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int saa717x_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd = to_sd(ctrl);
struct saa717x_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- ctrl->value = state->bright;
- break;
+ saa717x_write(sd, 0x10a, ctrl->val);
+ return 0;
case V4L2_CID_CONTRAST:
- ctrl->value = state->contrast;
- break;
+ saa717x_write(sd, 0x10b, ctrl->val);
+ return 0;
case V4L2_CID_SATURATION:
- ctrl->value = state->sat;
- break;
+ saa717x_write(sd, 0x10c, ctrl->val);
+ return 0;
case V4L2_CID_HUE:
- ctrl->value = state->hue;
- break;
+ saa717x_write(sd, 0x10d, ctrl->val);
+ return 0;
case V4L2_CID_AUDIO_MUTE:
- ctrl->value = state->audio_main_mute;
+ state->audio_main_mute = ctrl->val;
break;
case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = state->audio_main_volume;
+ state->audio_main_volume = ctrl->val;
break;
case V4L2_CID_AUDIO_BALANCE:
- ctrl->value = state->audio_main_balance;
+ state->audio_main_balance = ctrl->val;
break;
case V4L2_CID_AUDIO_TREBLE:
- ctrl->value = state->audio_main_treble;
+ state->audio_main_treble = ctrl->val;
break;
case V4L2_CID_AUDIO_BASS:
- ctrl->value = state->audio_main_bass;
+ state->audio_main_bass = ctrl->val;
break;
default:
- return -EINVAL;
+ return 0;
}
-
+ set_audio_regs(sd, state);
return 0;
}
-static struct v4l2_queryctrl saa717x_qctrl[] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 128,
- .flags = 0,
- }, {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 64,
- .flags = 0,
- }, {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 64,
- .flags = 0,
- }, {
- .id = V4L2_CID_HUE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Hue",
- .minimum = -128,
- .maximum = 127,
- .step = 1,
- .default_value = 0,
- .flags = 0,
- }, {
- .id = V4L2_CID_AUDIO_VOLUME,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Volume",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535 / 100,
- .default_value = 58880,
- .flags = 0,
- }, {
- .id = V4L2_CID_AUDIO_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Balance",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535 / 100,
- .default_value = 32768,
- .flags = 0,
- }, {
- .id = V4L2_CID_AUDIO_MUTE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Mute",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- .flags = 0,
- }, {
- .id = V4L2_CID_AUDIO_BASS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Bass",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535 / 100,
- .default_value = 32768,
- }, {
- .id = V4L2_CID_AUDIO_TREBLE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Treble",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535 / 100,
- .default_value = 32768,
- },
-};
-
static int saa717x_s_video_routing(struct v4l2_subdev *sd,
u32 input, u32 output, u32 config)
{
return 0;
}
-static int saa717x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(saa717x_qctrl); i++)
- if (qc->id && qc->id == saa717x_qctrl[i].id) {
- memcpy(qc, &saa717x_qctrl[i], sizeof(*qc));
- return 0;
- }
- return -EINVAL;
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
{
return 0;
}
+static int saa717x_log_status(struct v4l2_subdev *sd)
+{
+ struct saa717x_state *state = to_state(sd);
+
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
+ return 0;
+}
+
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops saa717x_ctrl_ops = {
+ .s_ctrl = saa717x_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops saa717x_core_ops = {
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = saa717x_g_register,
.s_register = saa717x_s_register,
#endif
- .queryctrl = saa717x_queryctrl,
- .g_ctrl = saa717x_g_ctrl,
- .s_ctrl = saa717x_s_ctrl,
.s_std = saa717x_s_std,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+ .log_status = saa717x_log_status,
};
static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = {
const struct i2c_device_id *did)
{
struct saa717x_state *decoder;
+ struct v4l2_ctrl_handler *hdl;
struct v4l2_subdev *sd;
u8 id = 0;
char *p = "";
p = "saa7171";
v4l2_info(sd, "%s found @ 0x%x (%s)\n", p,
client->addr << 1, client->adapter->name);
+
+ hdl = &decoder->hdl;
+ v4l2_ctrl_handler_init(hdl, 9);
+ /* add in ascending ID order */
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 68);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 42000);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_AUDIO_BASS, -16, 15, 1, 0);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_AUDIO_TREBLE, -16, 15, 1, 0);
+ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ kfree(decoder);
+ return err;
+ }
+
decoder->std = V4L2_STD_NTSC;
decoder->input = -1;
decoder->enable = 1;
- /* tune these parameters */
- decoder->bright = 0x80;
- decoder->contrast = 0x44;
- decoder->sat = 0x40;
- decoder->hue = 0x00;
-
/* FIXME!! */
decoder->playback = 0; /* initially capture mode used */
decoder->audio = 1; /* DECODER_AUDIO_48_KHZ */
/* set volume, bass and treble */
decoder->audio_main_vol_l = 6;
decoder->audio_main_vol_r = 6;
- decoder->audio_main_bass = 0;
- decoder->audio_main_treble = 0;
- decoder->audio_main_mute = 0;
- decoder->audio_main_balance = 32768;
- /* normalize (24 to -40 (not -84) -> 65535 to 0) */
- decoder->audio_main_volume =
- (decoder->audio_main_vol_r + 41) * 65535 / (24 - (-40));
v4l2_dbg(1, debug, sd, "writing init values\n");
/* FIXME!! */
saa717x_write_regs(sd, reg_init_initialize);
- set_video_output_level_regs(sd, decoder);
- /* set bass,treble to 0db 20041101 K.Ohta */
- decoder->audio_main_bass = 0;
- decoder->audio_main_treble = 0;
- set_audio_regs(sd, decoder);
+
+ v4l2_ctrl_handler_setup(hdl);
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(2*HZ);
struct v4l2_subdev *sd = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
kfree(to_state(sd));
return 0;
}
ret = ici->ops->get_crop(icd, ¤t_crop);
/* Prohibit window size change with initialised buffers */
- if (icf->vb_vidq.bufs[0] && !ret &&
- (a->c.width != current_crop.c.width ||
- a->c.height != current_crop.c.height)) {
+ if (ret < 0) {
+ dev_err(&icd->dev,
+ "S_CROP denied: getting current crop failed\n");
+ } else if (icf->vb_vidq.bufs[0] &&
+ (a->c.width != current_crop.c.width ||
+ a->c.height != current_crop.c.height)) {
dev_err(&icd->dev,
"S_CROP denied: queue initialised and sizes differ\n");
ret = -EBUSY;
/* Register parameters for 480P */
static const struct i2c_reg_value tvp7002_parms_480P[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x35, TVP7002_WRITE },
- { TVP7002_HPLL_FDBK_DIV_LSBS, 0x0a, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0xa0, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0x02, TVP7002_WRITE },
{ TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x91, TVP7002_WRITE },
/* Register parameters for 1080I60 */
static const struct i2c_reg_value tvp7002_parms_1080I60[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE },
- { TVP7002_HPLL_FDBK_DIV_LSBS, 0x08, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE },
{ TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE },
/* Register parameters for 1080P60 */
static const struct i2c_reg_value tvp7002_parms_1080P60[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE },
- { TVP7002_HPLL_FDBK_DIV_LSBS, 0x08, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0xE0, TVP7002_WRITE },
{ TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE },
/* Register parameters for 720P60 */
static const struct i2c_reg_value tvp7002_parms_720P60[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE },
- { TVP7002_HPLL_FDBK_DIV_LSBS, 0x02, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE },
{ TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE },
/* Register parameters for 720P50 */
static const struct i2c_reg_value tvp7002_parms_720P50[] = {
{ TVP7002_HPLL_FDBK_DIV_MSBS, 0x7b, TVP7002_WRITE },
- { TVP7002_HPLL_FDBK_DIV_LSBS, 0x0c, TVP7002_WRITE },
+ { TVP7002_HPLL_FDBK_DIV_LSBS, 0xc0, TVP7002_WRITE },
{ TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE },
{ TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE },
{ TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE },
};
unsigned short digit;
int ix, iy;
+ int value;
if ((uvd == NULL) || (frame == NULL))
return;
- if (ch >= '0' && ch <= '9')
- ch -= '0';
- else if (ch >= 'A' && ch <= 'F')
- ch = 10 + (ch - 'A');
- else if (ch >= 'a' && ch <= 'f')
- ch = 10 + (ch - 'a');
- else
+ value = hex_to_bin(ch);
+ if (value < 0)
return;
- digit = digits[ch];
+ digit = digits[value];
for (iy=0; iy < 5; iy++) {
for (ix=0; ix < 3; ix++) {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Miricle 307K */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x17dc,
+ .idProduct = 0x0202,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
/* Lenovo Thinkpad SL400/SL500 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
*
*/
-void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
+void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
+ int drop_corrupted)
{
mutex_init(&queue->mutex);
spin_lock_init(&queue->irqlock);
INIT_LIST_HEAD(&queue->mainqueue);
INIT_LIST_HEAD(&queue->irqqueue);
+ queue->flags = drop_corrupted ? UVC_QUEUE_DROP_CORRUPTED : 0;
queue->type = type;
}
uvc_queue_cancel(queue, 0);
INIT_LIST_HEAD(&queue->mainqueue);
- for (i = 0; i < queue->count; ++i)
+ for (i = 0; i < queue->count; ++i) {
+ queue->buffer[i].error = 0;
queue->buffer[i].state = UVC_BUF_STATE_IDLE;
+ }
queue->flags &= ~UVC_QUEUE_STREAMING;
}
struct uvc_buffer *nextbuf;
unsigned long flags;
- if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) &&
- buf->buf.length != buf->buf.bytesused) {
+ if ((queue->flags & UVC_QUEUE_DROP_CORRUPTED) && buf->error) {
+ buf->error = 0;
buf->state = UVC_BUF_STATE_QUEUED;
buf->buf.bytesused = 0;
return buf;
spin_lock_irqsave(&queue->irqlock, flags);
list_del(&buf->queue);
+ buf->error = 0;
buf->state = UVC_BUF_STATE_DONE;
if (!list_empty(&queue->irqqueue))
nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
if (urb->iso_frame_desc[i].status < 0) {
uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
"lost (%d).\n", urb->iso_frame_desc[i].status);
+ /* Mark the buffer as faulty. */
+ if (buf != NULL)
+ buf->error = 1;
continue;
}
uvc_video_decode_end(stream, buf, mem,
urb->iso_frame_desc[i].actual_length);
- if (buf->state == UVC_BUF_STATE_READY)
+ if (buf->state == UVC_BUF_STATE_READY) {
+ if (buf->buf.length != buf->buf.bytesused &&
+ !(stream->cur_format->flags &
+ UVC_FMT_FLAG_COMPRESSED))
+ buf->error = 1;
+
buf = uvc_queue_next_buffer(&stream->queue, buf);
+ }
}
}
atomic_set(&stream->active, 0);
/* Initialize the video buffers queue. */
- uvc_queue_init(&stream->queue, stream->type);
+ uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param);
/* Alternate setting 0 should be the default, yet the XBox Live Vision
* Cam (and possibly other devices) crash or otherwise misbehave if
return 0;
}
- if ((stream->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) ||
- uvc_no_drop_param)
- stream->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE;
- else
- stream->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE;
-
ret = uvc_queue_enable(&stream->queue, 1);
if (ret < 0)
return ret;
struct list_head queue;
wait_queue_head_t wait;
enum uvc_buffer_state state;
+ unsigned int error;
};
#define UVC_QUEUE_STREAMING (1 << 0)
#define UVC_QUEUE_DISCONNECTED (1 << 1)
-#define UVC_QUEUE_DROP_INCOMPLETE (1 << 2)
+#define UVC_QUEUE_DROP_CORRUPTED (1 << 2)
struct uvc_video_queue {
enum v4l2_buf_type type;
/* Video buffers queue management. */
extern void uvc_queue_init(struct uvc_video_queue *queue,
- enum v4l2_buf_type type);
+ enum v4l2_buf_type type, int drop_corrupted);
extern int uvc_alloc_buffers(struct uvc_video_queue *queue,
unsigned int nbuffers, unsigned int buflength);
extern int uvc_free_buffers(struct uvc_video_queue *queue);
#define __OLD_VIDIOC_ /* To allow fixing old calls*/
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-chip-ident.h>
#include <linux/videodev2.h>
}
EXPORT_SYMBOL(v4l2_ctrl_check);
-/* Returns NULL or a character pointer array containing the menu for
- the given control ID. The pointer array ends with a NULL pointer.
- An empty string signifies a menu entry that is invalid. This allows
- drivers to disable certain options if it is not supported. */
-const char **v4l2_ctrl_get_menu(u32 id)
-{
- static const char *mpeg_audio_sampling_freq[] = {
- "44.1 kHz",
- "48 kHz",
- "32 kHz",
- NULL
- };
- static const char *mpeg_audio_encoding[] = {
- "MPEG-1/2 Layer I",
- "MPEG-1/2 Layer II",
- "MPEG-1/2 Layer III",
- "MPEG-2/4 AAC",
- "AC-3",
- NULL
- };
- static const char *mpeg_audio_l1_bitrate[] = {
- "32 kbps",
- "64 kbps",
- "96 kbps",
- "128 kbps",
- "160 kbps",
- "192 kbps",
- "224 kbps",
- "256 kbps",
- "288 kbps",
- "320 kbps",
- "352 kbps",
- "384 kbps",
- "416 kbps",
- "448 kbps",
- NULL
- };
- static const char *mpeg_audio_l2_bitrate[] = {
- "32 kbps",
- "48 kbps",
- "56 kbps",
- "64 kbps",
- "80 kbps",
- "96 kbps",
- "112 kbps",
- "128 kbps",
- "160 kbps",
- "192 kbps",
- "224 kbps",
- "256 kbps",
- "320 kbps",
- "384 kbps",
- NULL
- };
- static const char *mpeg_audio_l3_bitrate[] = {
- "32 kbps",
- "40 kbps",
- "48 kbps",
- "56 kbps",
- "64 kbps",
- "80 kbps",
- "96 kbps",
- "112 kbps",
- "128 kbps",
- "160 kbps",
- "192 kbps",
- "224 kbps",
- "256 kbps",
- "320 kbps",
- NULL
- };
- static const char *mpeg_audio_ac3_bitrate[] = {
- "32 kbps",
- "40 kbps",
- "48 kbps",
- "56 kbps",
- "64 kbps",
- "80 kbps",
- "96 kbps",
- "112 kbps",
- "128 kbps",
- "160 kbps",
- "192 kbps",
- "224 kbps",
- "256 kbps",
- "320 kbps",
- "384 kbps",
- "448 kbps",
- "512 kbps",
- "576 kbps",
- "640 kbps",
- NULL
- };
- static const char *mpeg_audio_mode[] = {
- "Stereo",
- "Joint Stereo",
- "Dual",
- "Mono",
- NULL
- };
- static const char *mpeg_audio_mode_extension[] = {
- "Bound 4",
- "Bound 8",
- "Bound 12",
- "Bound 16",
- NULL
- };
- static const char *mpeg_audio_emphasis[] = {
- "No Emphasis",
- "50/15 us",
- "CCITT J17",
- NULL
- };
- static const char *mpeg_audio_crc[] = {
- "No CRC",
- "16-bit CRC",
- NULL
- };
- static const char *mpeg_video_encoding[] = {
- "MPEG-1",
- "MPEG-2",
- "MPEG-4 AVC",
- NULL
- };
- static const char *mpeg_video_aspect[] = {
- "1x1",
- "4x3",
- "16x9",
- "2.21x1",
- NULL
- };
- static const char *mpeg_video_bitrate_mode[] = {
- "Variable Bitrate",
- "Constant Bitrate",
- NULL
- };
- static const char *mpeg_stream_type[] = {
- "MPEG-2 Program Stream",
- "MPEG-2 Transport Stream",
- "MPEG-1 System Stream",
- "MPEG-2 DVD-compatible Stream",
- "MPEG-1 VCD-compatible Stream",
- "MPEG-2 SVCD-compatible Stream",
- NULL
- };
- static const char *mpeg_stream_vbi_fmt[] = {
- "No VBI",
- "Private packet, IVTV format",
- NULL
- };
- static const char *camera_power_line_frequency[] = {
- "Disabled",
- "50 Hz",
- "60 Hz",
- NULL
- };
- static const char *camera_exposure_auto[] = {
- "Auto Mode",
- "Manual Mode",
- "Shutter Priority Mode",
- "Aperture Priority Mode",
- NULL
- };
- static const char *colorfx[] = {
- "None",
- "Black & White",
- "Sepia",
- "Negative",
- "Emboss",
- "Sketch",
- "Sky blue",
- "Grass green",
- "Skin whiten",
- "Vivid",
- NULL
- };
- static const char *tune_preemphasis[] = {
- "No preemphasis",
- "50 useconds",
- "75 useconds",
- NULL,
- };
-
- switch (id) {
- case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
- return mpeg_audio_sampling_freq;
- case V4L2_CID_MPEG_AUDIO_ENCODING:
- return mpeg_audio_encoding;
- case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
- return mpeg_audio_l1_bitrate;
- case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
- return mpeg_audio_l2_bitrate;
- case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
- return mpeg_audio_l3_bitrate;
- case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
- return mpeg_audio_ac3_bitrate;
- case V4L2_CID_MPEG_AUDIO_MODE:
- return mpeg_audio_mode;
- case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
- return mpeg_audio_mode_extension;
- case V4L2_CID_MPEG_AUDIO_EMPHASIS:
- return mpeg_audio_emphasis;
- case V4L2_CID_MPEG_AUDIO_CRC:
- return mpeg_audio_crc;
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- return mpeg_video_encoding;
- case V4L2_CID_MPEG_VIDEO_ASPECT:
- return mpeg_video_aspect;
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
- return mpeg_video_bitrate_mode;
- case V4L2_CID_MPEG_STREAM_TYPE:
- return mpeg_stream_type;
- case V4L2_CID_MPEG_STREAM_VBI_FMT:
- return mpeg_stream_vbi_fmt;
- case V4L2_CID_POWER_LINE_FREQUENCY:
- return camera_power_line_frequency;
- case V4L2_CID_EXPOSURE_AUTO:
- return camera_exposure_auto;
- case V4L2_CID_COLORFX:
- return colorfx;
- case V4L2_CID_TUNE_PREEMPHASIS:
- return tune_preemphasis;
- default:
- return NULL;
- }
-}
-EXPORT_SYMBOL(v4l2_ctrl_get_menu);
-
-/* Return the control name. */
-const char *v4l2_ctrl_get_name(u32 id)
-{
- switch (id) {
- /* USER controls */
- case V4L2_CID_USER_CLASS: return "User Controls";
- case V4L2_CID_BRIGHTNESS: return "Brightness";
- case V4L2_CID_CONTRAST: return "Contrast";
- case V4L2_CID_SATURATION: return "Saturation";
- case V4L2_CID_HUE: return "Hue";
- case V4L2_CID_AUDIO_VOLUME: return "Volume";
- case V4L2_CID_AUDIO_BALANCE: return "Balance";
- case V4L2_CID_AUDIO_BASS: return "Bass";
- case V4L2_CID_AUDIO_TREBLE: return "Treble";
- case V4L2_CID_AUDIO_MUTE: return "Mute";
- case V4L2_CID_AUDIO_LOUDNESS: return "Loudness";
- case V4L2_CID_BLACK_LEVEL: return "Black Level";
- case V4L2_CID_AUTO_WHITE_BALANCE: return "White Balance, Automatic";
- case V4L2_CID_DO_WHITE_BALANCE: return "Do White Balance";
- case V4L2_CID_RED_BALANCE: return "Red Balance";
- case V4L2_CID_BLUE_BALANCE: return "Blue Balance";
- case V4L2_CID_GAMMA: return "Gamma";
- case V4L2_CID_EXPOSURE: return "Exposure";
- case V4L2_CID_AUTOGAIN: return "Gain, Automatic";
- case V4L2_CID_GAIN: return "Gain";
- case V4L2_CID_HFLIP: return "Horizontal Flip";
- case V4L2_CID_VFLIP: return "Vertical Flip";
- case V4L2_CID_HCENTER: return "Horizontal Center";
- case V4L2_CID_VCENTER: return "Vertical Center";
- case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency";
- case V4L2_CID_HUE_AUTO: return "Hue, Automatic";
- case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature";
- case V4L2_CID_SHARPNESS: return "Sharpness";
- case V4L2_CID_BACKLIGHT_COMPENSATION: return "Backlight Compensation";
- case V4L2_CID_CHROMA_AGC: return "Chroma AGC";
- case V4L2_CID_CHROMA_GAIN: return "Chroma Gain";
- case V4L2_CID_COLOR_KILLER: return "Color Killer";
- case V4L2_CID_COLORFX: return "Color Effects";
- case V4L2_CID_AUTOBRIGHTNESS: return "Brightness, Automatic";
- case V4L2_CID_BAND_STOP_FILTER: return "Band-Stop Filter";
- case V4L2_CID_ROTATE: return "Rotate";
- case V4L2_CID_BG_COLOR: return "Background Color";
-
- /* MPEG controls */
- case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls";
- case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return "Audio Sampling Frequency";
- case V4L2_CID_MPEG_AUDIO_ENCODING: return "Audio Encoding";
- case V4L2_CID_MPEG_AUDIO_L1_BITRATE: return "Audio Layer I Bitrate";
- case V4L2_CID_MPEG_AUDIO_L2_BITRATE: return "Audio Layer II Bitrate";
- case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return "Audio Layer III Bitrate";
- case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: return "Audio AAC Bitrate";
- case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return "Audio AC-3 Bitrate";
- case V4L2_CID_MPEG_AUDIO_MODE: return "Audio Stereo Mode";
- case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: return "Audio Stereo Mode Extension";
- case V4L2_CID_MPEG_AUDIO_EMPHASIS: return "Audio Emphasis";
- case V4L2_CID_MPEG_AUDIO_CRC: return "Audio CRC";
- case V4L2_CID_MPEG_AUDIO_MUTE: return "Audio Mute";
- case V4L2_CID_MPEG_VIDEO_ENCODING: return "Video Encoding";
- case V4L2_CID_MPEG_VIDEO_ASPECT: return "Video Aspect";
- case V4L2_CID_MPEG_VIDEO_B_FRAMES: return "Video B Frames";
- case V4L2_CID_MPEG_VIDEO_GOP_SIZE: return "Video GOP Size";
- case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: return "Video GOP Closure";
- case V4L2_CID_MPEG_VIDEO_PULLDOWN: return "Video Pulldown";
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: return "Video Bitrate Mode";
- case V4L2_CID_MPEG_VIDEO_BITRATE: return "Video Bitrate";
- case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: return "Video Peak Bitrate";
- case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation";
- case V4L2_CID_MPEG_VIDEO_MUTE: return "Video Mute";
- case V4L2_CID_MPEG_VIDEO_MUTE_YUV: return "Video Mute YUV";
- case V4L2_CID_MPEG_STREAM_TYPE: return "Stream Type";
- case V4L2_CID_MPEG_STREAM_PID_PMT: return "Stream PMT Program ID";
- case V4L2_CID_MPEG_STREAM_PID_AUDIO: return "Stream Audio Program ID";
- case V4L2_CID_MPEG_STREAM_PID_VIDEO: return "Stream Video Program ID";
- case V4L2_CID_MPEG_STREAM_PID_PCR: return "Stream PCR Program ID";
- case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: return "Stream PES Audio ID";
- case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID";
- case V4L2_CID_MPEG_STREAM_VBI_FMT: return "Stream VBI Format";
-
- /* CAMERA controls */
- case V4L2_CID_CAMERA_CLASS: return "Camera Controls";
- case V4L2_CID_EXPOSURE_AUTO: return "Auto Exposure";
- case V4L2_CID_EXPOSURE_ABSOLUTE: return "Exposure Time, Absolute";
- case V4L2_CID_EXPOSURE_AUTO_PRIORITY: return "Exposure, Dynamic Framerate";
- case V4L2_CID_PAN_RELATIVE: return "Pan, Relative";
- case V4L2_CID_TILT_RELATIVE: return "Tilt, Relative";
- case V4L2_CID_PAN_RESET: return "Pan, Reset";
- case V4L2_CID_TILT_RESET: return "Tilt, Reset";
- case V4L2_CID_PAN_ABSOLUTE: return "Pan, Absolute";
- case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute";
- case V4L2_CID_FOCUS_ABSOLUTE: return "Focus, Absolute";
- case V4L2_CID_FOCUS_RELATIVE: return "Focus, Relative";
- case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic";
- case V4L2_CID_IRIS_ABSOLUTE: return "Iris, Absolute";
- case V4L2_CID_IRIS_RELATIVE: return "Iris, Relative";
- case V4L2_CID_ZOOM_ABSOLUTE: return "Zoom, Absolute";
- case V4L2_CID_ZOOM_RELATIVE: return "Zoom, Relative";
- case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous";
- case V4L2_CID_PRIVACY: return "Privacy";
-
- /* FM Radio Modulator control */
- case V4L2_CID_FM_TX_CLASS: return "FM Radio Modulator Controls";
- case V4L2_CID_RDS_TX_DEVIATION: return "RDS Signal Deviation";
- case V4L2_CID_RDS_TX_PI: return "RDS Program ID";
- case V4L2_CID_RDS_TX_PTY: return "RDS Program Type";
- case V4L2_CID_RDS_TX_PS_NAME: return "RDS PS Name";
- case V4L2_CID_RDS_TX_RADIO_TEXT: return "RDS Radio Text";
- case V4L2_CID_AUDIO_LIMITER_ENABLED: return "Audio Limiter Feature Enabled";
- case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time";
- case V4L2_CID_AUDIO_LIMITER_DEVIATION: return "Audio Limiter Deviation";
- case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Feature Enabled";
- case V4L2_CID_AUDIO_COMPRESSION_GAIN: return "Audio Compression Gain";
- case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: return "Audio Compression Threshold";
- case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: return "Audio Compression Attack Time";
- case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: return "Audio Compression Release Time";
- case V4L2_CID_PILOT_TONE_ENABLED: return "Pilot Tone Feature Enabled";
- case V4L2_CID_PILOT_TONE_DEVIATION: return "Pilot Tone Deviation";
- case V4L2_CID_PILOT_TONE_FREQUENCY: return "Pilot Tone Frequency";
- case V4L2_CID_TUNE_PREEMPHASIS: return "Pre-emphasis settings";
- case V4L2_CID_TUNE_POWER_LEVEL: return "Tune Power Level";
- case V4L2_CID_TUNE_ANTENNA_CAPACITOR: return "Tune Antenna Capacitor";
-
- default:
- return NULL;
- }
-}
-EXPORT_SYMBOL(v4l2_ctrl_get_name);
-
/* Fill in a struct v4l2_queryctrl */
int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def)
{
- const char *name = v4l2_ctrl_get_name(qctrl->id);
+ const char *name;
+
+ v4l2_ctrl_fill(qctrl->id, &name, &qctrl->type,
+ &min, &max, &step, &def, &qctrl->flags);
- qctrl->flags = 0;
if (name == NULL)
return -EINVAL;
- switch (qctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- case V4L2_CID_AUDIO_LOUDNESS:
- case V4L2_CID_AUTO_WHITE_BALANCE:
- case V4L2_CID_AUTOGAIN:
- case V4L2_CID_HFLIP:
- case V4L2_CID_VFLIP:
- case V4L2_CID_HUE_AUTO:
- case V4L2_CID_CHROMA_AGC:
- case V4L2_CID_COLOR_KILLER:
- case V4L2_CID_MPEG_AUDIO_MUTE:
- case V4L2_CID_MPEG_VIDEO_MUTE:
- case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
- case V4L2_CID_MPEG_VIDEO_PULLDOWN:
- case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
- case V4L2_CID_FOCUS_AUTO:
- case V4L2_CID_PRIVACY:
- case V4L2_CID_AUDIO_LIMITER_ENABLED:
- case V4L2_CID_AUDIO_COMPRESSION_ENABLED:
- case V4L2_CID_PILOT_TONE_ENABLED:
- qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
- min = 0;
- max = step = 1;
- break;
- case V4L2_CID_PAN_RESET:
- case V4L2_CID_TILT_RESET:
- qctrl->type = V4L2_CTRL_TYPE_BUTTON;
- qctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
- min = max = step = def = 0;
- break;
- case V4L2_CID_POWER_LINE_FREQUENCY:
- case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
- case V4L2_CID_MPEG_AUDIO_ENCODING:
- case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
- case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
- case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
- case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
- case V4L2_CID_MPEG_AUDIO_MODE:
- case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
- case V4L2_CID_MPEG_AUDIO_EMPHASIS:
- case V4L2_CID_MPEG_AUDIO_CRC:
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- case V4L2_CID_MPEG_VIDEO_ASPECT:
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
- case V4L2_CID_MPEG_STREAM_TYPE:
- case V4L2_CID_MPEG_STREAM_VBI_FMT:
- case V4L2_CID_EXPOSURE_AUTO:
- case V4L2_CID_COLORFX:
- case V4L2_CID_TUNE_PREEMPHASIS:
- qctrl->type = V4L2_CTRL_TYPE_MENU;
- step = 1;
- break;
- case V4L2_CID_RDS_TX_PS_NAME:
- case V4L2_CID_RDS_TX_RADIO_TEXT:
- qctrl->type = V4L2_CTRL_TYPE_STRING;
- break;
- case V4L2_CID_USER_CLASS:
- case V4L2_CID_CAMERA_CLASS:
- case V4L2_CID_MPEG_CLASS:
- case V4L2_CID_FM_TX_CLASS:
- qctrl->type = V4L2_CTRL_TYPE_CTRL_CLASS;
- qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
- min = max = step = def = 0;
- break;
- case V4L2_CID_BG_COLOR:
- qctrl->type = V4L2_CTRL_TYPE_INTEGER;
- step = 1;
- min = 0;
- /* Max is calculated as RGB888 that is 2^24 */
- max = 0xFFFFFF;
- break;
- default:
- qctrl->type = V4L2_CTRL_TYPE_INTEGER;
- break;
- }
- switch (qctrl->id) {
- case V4L2_CID_MPEG_AUDIO_ENCODING:
- case V4L2_CID_MPEG_AUDIO_MODE:
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
- case V4L2_CID_MPEG_VIDEO_B_FRAMES:
- case V4L2_CID_MPEG_STREAM_TYPE:
- qctrl->flags |= V4L2_CTRL_FLAG_UPDATE;
- break;
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- case V4L2_CID_HUE:
- case V4L2_CID_RED_BALANCE:
- case V4L2_CID_BLUE_BALANCE:
- case V4L2_CID_GAMMA:
- case V4L2_CID_SHARPNESS:
- case V4L2_CID_CHROMA_GAIN:
- case V4L2_CID_RDS_TX_DEVIATION:
- case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME:
- case V4L2_CID_AUDIO_LIMITER_DEVIATION:
- case V4L2_CID_AUDIO_COMPRESSION_GAIN:
- case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD:
- case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME:
- case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME:
- case V4L2_CID_PILOT_TONE_DEVIATION:
- case V4L2_CID_PILOT_TONE_FREQUENCY:
- case V4L2_CID_TUNE_POWER_LEVEL:
- case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
- qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
- break;
- case V4L2_CID_PAN_RELATIVE:
- case V4L2_CID_TILT_RELATIVE:
- case V4L2_CID_FOCUS_RELATIVE:
- case V4L2_CID_IRIS_RELATIVE:
- case V4L2_CID_ZOOM_RELATIVE:
- qctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
- break;
- }
qctrl->minimum = min;
qctrl->maximum = max;
qctrl->step = step;
--- /dev/null
+/*
+ V4L2 controls framework implementation.
+
+ Copyright (C) 2010 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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/ctype.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+
+/* Internal temporary helper struct, one for each v4l2_ext_control */
+struct ctrl_helper {
+ /* The control corresponding to the v4l2_ext_control ID field. */
+ struct v4l2_ctrl *ctrl;
+ /* Used internally to mark whether this control was already
+ processed. */
+ bool handled;
+};
+
+/* Returns NULL or a character pointer array containing the menu for
+ the given control ID. The pointer array ends with a NULL pointer.
+ An empty string signifies a menu entry that is invalid. This allows
+ drivers to disable certain options if it is not supported. */
+const char **v4l2_ctrl_get_menu(u32 id)
+{
+ static const char *mpeg_audio_sampling_freq[] = {
+ "44.1 kHz",
+ "48 kHz",
+ "32 kHz",
+ NULL
+ };
+ static const char *mpeg_audio_encoding[] = {
+ "MPEG-1/2 Layer I",
+ "MPEG-1/2 Layer II",
+ "MPEG-1/2 Layer III",
+ "MPEG-2/4 AAC",
+ "AC-3",
+ NULL
+ };
+ static const char *mpeg_audio_l1_bitrate[] = {
+ "32 kbps",
+ "64 kbps",
+ "96 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "288 kbps",
+ "320 kbps",
+ "352 kbps",
+ "384 kbps",
+ "416 kbps",
+ "448 kbps",
+ NULL
+ };
+ static const char *mpeg_audio_l2_bitrate[] = {
+ "32 kbps",
+ "48 kbps",
+ "56 kbps",
+ "64 kbps",
+ "80 kbps",
+ "96 kbps",
+ "112 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "320 kbps",
+ "384 kbps",
+ NULL
+ };
+ static const char *mpeg_audio_l3_bitrate[] = {
+ "32 kbps",
+ "40 kbps",
+ "48 kbps",
+ "56 kbps",
+ "64 kbps",
+ "80 kbps",
+ "96 kbps",
+ "112 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "320 kbps",
+ NULL
+ };
+ static const char *mpeg_audio_ac3_bitrate[] = {
+ "32 kbps",
+ "40 kbps",
+ "48 kbps",
+ "56 kbps",
+ "64 kbps",
+ "80 kbps",
+ "96 kbps",
+ "112 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "320 kbps",
+ "384 kbps",
+ "448 kbps",
+ "512 kbps",
+ "576 kbps",
+ "640 kbps",
+ NULL
+ };
+ static const char *mpeg_audio_mode[] = {
+ "Stereo",
+ "Joint Stereo",
+ "Dual",
+ "Mono",
+ NULL
+ };
+ static const char *mpeg_audio_mode_extension[] = {
+ "Bound 4",
+ "Bound 8",
+ "Bound 12",
+ "Bound 16",
+ NULL
+ };
+ static const char *mpeg_audio_emphasis[] = {
+ "No Emphasis",
+ "50/15 us",
+ "CCITT J17",
+ NULL
+ };
+ static const char *mpeg_audio_crc[] = {
+ "No CRC",
+ "16-bit CRC",
+ NULL
+ };
+ static const char *mpeg_video_encoding[] = {
+ "MPEG-1",
+ "MPEG-2",
+ "MPEG-4 AVC",
+ NULL
+ };
+ static const char *mpeg_video_aspect[] = {
+ "1x1",
+ "4x3",
+ "16x9",
+ "2.21x1",
+ NULL
+ };
+ static const char *mpeg_video_bitrate_mode[] = {
+ "Variable Bitrate",
+ "Constant Bitrate",
+ NULL
+ };
+ static const char *mpeg_stream_type[] = {
+ "MPEG-2 Program Stream",
+ "MPEG-2 Transport Stream",
+ "MPEG-1 System Stream",
+ "MPEG-2 DVD-compatible Stream",
+ "MPEG-1 VCD-compatible Stream",
+ "MPEG-2 SVCD-compatible Stream",
+ NULL
+ };
+ static const char *mpeg_stream_vbi_fmt[] = {
+ "No VBI",
+ "Private packet, IVTV format",
+ NULL
+ };
+ static const char *camera_power_line_frequency[] = {
+ "Disabled",
+ "50 Hz",
+ "60 Hz",
+ NULL
+ };
+ static const char *camera_exposure_auto[] = {
+ "Auto Mode",
+ "Manual Mode",
+ "Shutter Priority Mode",
+ "Aperture Priority Mode",
+ NULL
+ };
+ static const char *colorfx[] = {
+ "None",
+ "Black & White",
+ "Sepia",
+ "Negative",
+ "Emboss",
+ "Sketch",
+ "Sky blue",
+ "Grass green",
+ "Skin whiten",
+ "Vivid",
+ NULL
+ };
+ static const char *tune_preemphasis[] = {
+ "No preemphasis",
+ "50 useconds",
+ "75 useconds",
+ NULL,
+ };
+
+ switch (id) {
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ return mpeg_audio_sampling_freq;
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ return mpeg_audio_encoding;
+ case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
+ return mpeg_audio_l1_bitrate;
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ return mpeg_audio_l2_bitrate;
+ case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
+ return mpeg_audio_l3_bitrate;
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ return mpeg_audio_ac3_bitrate;
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ return mpeg_audio_mode;
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+ return mpeg_audio_mode_extension;
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+ return mpeg_audio_emphasis;
+ case V4L2_CID_MPEG_AUDIO_CRC:
+ return mpeg_audio_crc;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ return mpeg_video_encoding;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ return mpeg_video_aspect;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ return mpeg_video_bitrate_mode;
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ return mpeg_stream_type;
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ return mpeg_stream_vbi_fmt;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ return camera_power_line_frequency;
+ case V4L2_CID_EXPOSURE_AUTO:
+ return camera_exposure_auto;
+ case V4L2_CID_COLORFX:
+ return colorfx;
+ case V4L2_CID_TUNE_PREEMPHASIS:
+ return tune_preemphasis;
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_get_menu);
+
+/* Return the control name. */
+const char *v4l2_ctrl_get_name(u32 id)
+{
+ switch (id) {
+ /* USER controls */
+ /* Keep the order of the 'case's the same as in videodev2.h! */
+ case V4L2_CID_USER_CLASS: return "User Controls";
+ case V4L2_CID_BRIGHTNESS: return "Brightness";
+ case V4L2_CID_CONTRAST: return "Contrast";
+ case V4L2_CID_SATURATION: return "Saturation";
+ case V4L2_CID_HUE: return "Hue";
+ case V4L2_CID_AUDIO_VOLUME: return "Volume";
+ case V4L2_CID_AUDIO_BALANCE: return "Balance";
+ case V4L2_CID_AUDIO_BASS: return "Bass";
+ case V4L2_CID_AUDIO_TREBLE: return "Treble";
+ case V4L2_CID_AUDIO_MUTE: return "Mute";
+ case V4L2_CID_AUDIO_LOUDNESS: return "Loudness";
+ case V4L2_CID_BLACK_LEVEL: return "Black Level";
+ case V4L2_CID_AUTO_WHITE_BALANCE: return "White Balance, Automatic";
+ case V4L2_CID_DO_WHITE_BALANCE: return "Do White Balance";
+ case V4L2_CID_RED_BALANCE: return "Red Balance";
+ case V4L2_CID_BLUE_BALANCE: return "Blue Balance";
+ case V4L2_CID_GAMMA: return "Gamma";
+ case V4L2_CID_EXPOSURE: return "Exposure";
+ case V4L2_CID_AUTOGAIN: return "Gain, Automatic";
+ case V4L2_CID_GAIN: return "Gain";
+ case V4L2_CID_HFLIP: return "Horizontal Flip";
+ case V4L2_CID_VFLIP: return "Vertical Flip";
+ case V4L2_CID_HCENTER: return "Horizontal Center";
+ case V4L2_CID_VCENTER: return "Vertical Center";
+ case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency";
+ case V4L2_CID_HUE_AUTO: return "Hue, Automatic";
+ case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature";
+ case V4L2_CID_SHARPNESS: return "Sharpness";
+ case V4L2_CID_BACKLIGHT_COMPENSATION: return "Backlight Compensation";
+ case V4L2_CID_CHROMA_AGC: return "Chroma AGC";
+ case V4L2_CID_COLOR_KILLER: return "Color Killer";
+ case V4L2_CID_COLORFX: return "Color Effects";
+ case V4L2_CID_AUTOBRIGHTNESS: return "Brightness, Automatic";
+ case V4L2_CID_BAND_STOP_FILTER: return "Band-Stop Filter";
+ case V4L2_CID_ROTATE: return "Rotate";
+ case V4L2_CID_BG_COLOR: return "Background Color";
+ case V4L2_CID_CHROMA_GAIN: return "Chroma Gain";
+
+ /* MPEG controls */
+ /* Keep the order of the 'case's the same as in videodev2.h! */
+ case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls";
+ case V4L2_CID_MPEG_STREAM_TYPE: return "Stream Type";
+ case V4L2_CID_MPEG_STREAM_PID_PMT: return "Stream PMT Program ID";
+ case V4L2_CID_MPEG_STREAM_PID_AUDIO: return "Stream Audio Program ID";
+ case V4L2_CID_MPEG_STREAM_PID_VIDEO: return "Stream Video Program ID";
+ case V4L2_CID_MPEG_STREAM_PID_PCR: return "Stream PCR Program ID";
+ case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: return "Stream PES Audio ID";
+ case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID";
+ case V4L2_CID_MPEG_STREAM_VBI_FMT: return "Stream VBI Format";
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return "Audio Sampling Frequency";
+ case V4L2_CID_MPEG_AUDIO_ENCODING: return "Audio Encoding";
+ case V4L2_CID_MPEG_AUDIO_L1_BITRATE: return "Audio Layer I Bitrate";
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE: return "Audio Layer II Bitrate";
+ case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return "Audio Layer III Bitrate";
+ case V4L2_CID_MPEG_AUDIO_MODE: return "Audio Stereo Mode";
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: return "Audio Stereo Mode Extension";
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS: return "Audio Emphasis";
+ case V4L2_CID_MPEG_AUDIO_CRC: return "Audio CRC";
+ case V4L2_CID_MPEG_AUDIO_MUTE: return "Audio Mute";
+ case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: return "Audio AAC Bitrate";
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return "Audio AC-3 Bitrate";
+ case V4L2_CID_MPEG_VIDEO_ENCODING: return "Video Encoding";
+ case V4L2_CID_MPEG_VIDEO_ASPECT: return "Video Aspect";
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES: return "Video B Frames";
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE: return "Video GOP Size";
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: return "Video GOP Closure";
+ case V4L2_CID_MPEG_VIDEO_PULLDOWN: return "Video Pulldown";
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: return "Video Bitrate Mode";
+ case V4L2_CID_MPEG_VIDEO_BITRATE: return "Video Bitrate";
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: return "Video Peak Bitrate";
+ case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation";
+ case V4L2_CID_MPEG_VIDEO_MUTE: return "Video Mute";
+ case V4L2_CID_MPEG_VIDEO_MUTE_YUV: return "Video Mute YUV";
+
+ /* CAMERA controls */
+ /* Keep the order of the 'case's the same as in videodev2.h! */
+ case V4L2_CID_CAMERA_CLASS: return "Camera Controls";
+ case V4L2_CID_EXPOSURE_AUTO: return "Auto Exposure";
+ case V4L2_CID_EXPOSURE_ABSOLUTE: return "Exposure Time, Absolute";
+ case V4L2_CID_EXPOSURE_AUTO_PRIORITY: return "Exposure, Dynamic Framerate";
+ case V4L2_CID_PAN_RELATIVE: return "Pan, Relative";
+ case V4L2_CID_TILT_RELATIVE: return "Tilt, Relative";
+ case V4L2_CID_PAN_RESET: return "Pan, Reset";
+ case V4L2_CID_TILT_RESET: return "Tilt, Reset";
+ case V4L2_CID_PAN_ABSOLUTE: return "Pan, Absolute";
+ case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute";
+ case V4L2_CID_FOCUS_ABSOLUTE: return "Focus, Absolute";
+ case V4L2_CID_FOCUS_RELATIVE: return "Focus, Relative";
+ case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic";
+ case V4L2_CID_ZOOM_ABSOLUTE: return "Zoom, Absolute";
+ case V4L2_CID_ZOOM_RELATIVE: return "Zoom, Relative";
+ case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous";
+ case V4L2_CID_PRIVACY: return "Privacy";
+ case V4L2_CID_IRIS_ABSOLUTE: return "Iris, Absolute";
+ case V4L2_CID_IRIS_RELATIVE: return "Iris, Relative";
+
+ /* FM Radio Modulator control */
+ /* Keep the order of the 'case's the same as in videodev2.h! */
+ case V4L2_CID_FM_TX_CLASS: return "FM Radio Modulator Controls";
+ case V4L2_CID_RDS_TX_DEVIATION: return "RDS Signal Deviation";
+ case V4L2_CID_RDS_TX_PI: return "RDS Program ID";
+ case V4L2_CID_RDS_TX_PTY: return "RDS Program Type";
+ case V4L2_CID_RDS_TX_PS_NAME: return "RDS PS Name";
+ case V4L2_CID_RDS_TX_RADIO_TEXT: return "RDS Radio Text";
+ case V4L2_CID_AUDIO_LIMITER_ENABLED: return "Audio Limiter Feature Enabled";
+ case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time";
+ case V4L2_CID_AUDIO_LIMITER_DEVIATION: return "Audio Limiter Deviation";
+ case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Feature Enabled";
+ case V4L2_CID_AUDIO_COMPRESSION_GAIN: return "Audio Compression Gain";
+ case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: return "Audio Compression Threshold";
+ case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: return "Audio Compression Attack Time";
+ case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: return "Audio Compression Release Time";
+ case V4L2_CID_PILOT_TONE_ENABLED: return "Pilot Tone Feature Enabled";
+ case V4L2_CID_PILOT_TONE_DEVIATION: return "Pilot Tone Deviation";
+ case V4L2_CID_PILOT_TONE_FREQUENCY: return "Pilot Tone Frequency";
+ case V4L2_CID_TUNE_PREEMPHASIS: return "Pre-emphasis settings";
+ case V4L2_CID_TUNE_POWER_LEVEL: return "Tune Power Level";
+ case V4L2_CID_TUNE_ANTENNA_CAPACITOR: return "Tune Antenna Capacitor";
+
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_get_name);
+
+void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
+ s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags)
+{
+ *name = v4l2_ctrl_get_name(id);
+ *flags = 0;
+
+ switch (id) {
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_LOUDNESS:
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ case V4L2_CID_AUTOGAIN:
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HUE_AUTO:
+ case V4L2_CID_CHROMA_AGC:
+ case V4L2_CID_COLOR_KILLER:
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ case V4L2_CID_MPEG_VIDEO_MUTE:
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ case V4L2_CID_MPEG_VIDEO_PULLDOWN:
+ case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
+ case V4L2_CID_FOCUS_AUTO:
+ case V4L2_CID_PRIVACY:
+ case V4L2_CID_AUDIO_LIMITER_ENABLED:
+ case V4L2_CID_AUDIO_COMPRESSION_ENABLED:
+ case V4L2_CID_PILOT_TONE_ENABLED:
+ *type = V4L2_CTRL_TYPE_BOOLEAN;
+ *min = 0;
+ *max = *step = 1;
+ break;
+ case V4L2_CID_PAN_RESET:
+ case V4L2_CID_TILT_RESET:
+ *type = V4L2_CTRL_TYPE_BUTTON;
+ *flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+ *min = *max = *step = *def = 0;
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+ case V4L2_CID_MPEG_AUDIO_CRC:
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ case V4L2_CID_EXPOSURE_AUTO:
+ case V4L2_CID_COLORFX:
+ case V4L2_CID_TUNE_PREEMPHASIS:
+ *type = V4L2_CTRL_TYPE_MENU;
+ break;
+ case V4L2_CID_RDS_TX_PS_NAME:
+ case V4L2_CID_RDS_TX_RADIO_TEXT:
+ *type = V4L2_CTRL_TYPE_STRING;
+ break;
+ case V4L2_CID_USER_CLASS:
+ case V4L2_CID_CAMERA_CLASS:
+ case V4L2_CID_MPEG_CLASS:
+ case V4L2_CID_FM_TX_CLASS:
+ *type = V4L2_CTRL_TYPE_CTRL_CLASS;
+ /* You can neither read not write these */
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY;
+ *min = *max = *step = *def = 0;
+ break;
+ case V4L2_CID_BG_COLOR:
+ *type = V4L2_CTRL_TYPE_INTEGER;
+ *step = 1;
+ *min = 0;
+ /* Max is calculated as RGB888 that is 2^24 */
+ *max = 0xFFFFFF;
+ break;
+ default:
+ *type = V4L2_CTRL_TYPE_INTEGER;
+ break;
+ }
+ switch (id) {
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ *flags |= V4L2_CTRL_FLAG_UPDATE;
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_BRIGHTNESS:
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_HUE:
+ case V4L2_CID_RED_BALANCE:
+ case V4L2_CID_BLUE_BALANCE:
+ case V4L2_CID_GAMMA:
+ case V4L2_CID_SHARPNESS:
+ case V4L2_CID_CHROMA_GAIN:
+ case V4L2_CID_RDS_TX_DEVIATION:
+ case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME:
+ case V4L2_CID_AUDIO_LIMITER_DEVIATION:
+ case V4L2_CID_AUDIO_COMPRESSION_GAIN:
+ case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD:
+ case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME:
+ case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME:
+ case V4L2_CID_PILOT_TONE_DEVIATION:
+ case V4L2_CID_PILOT_TONE_FREQUENCY:
+ case V4L2_CID_TUNE_POWER_LEVEL:
+ case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
+ *flags |= V4L2_CTRL_FLAG_SLIDER;
+ break;
+ case V4L2_CID_PAN_RELATIVE:
+ case V4L2_CID_TILT_RELATIVE:
+ case V4L2_CID_FOCUS_RELATIVE:
+ case V4L2_CID_IRIS_RELATIVE:
+ case V4L2_CID_ZOOM_RELATIVE:
+ *flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+ break;
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_fill);
+
+/* Helper function to determine whether the control type is compatible with
+ VIDIOC_G/S_CTRL. */
+static bool type_is_int(const struct v4l2_ctrl *ctrl)
+{
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER64:
+ case V4L2_CTRL_TYPE_STRING:
+ /* Nope, these need v4l2_ext_control */
+ return false;
+ default:
+ return true;
+ }
+}
+
+/* Helper function: copy the current control value back to the caller */
+static int cur_to_user(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl)
+{
+ u32 len;
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_STRING:
+ len = strlen(ctrl->cur.string);
+ if (c->size < len + 1) {
+ c->size = len + 1;
+ return -ENOSPC;
+ }
+ return copy_to_user(c->string, ctrl->cur.string,
+ len + 1) ? -EFAULT : 0;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ c->value64 = ctrl->cur.val64;
+ break;
+ default:
+ c->value = ctrl->cur.val;
+ break;
+ }
+ return 0;
+}
+
+/* Helper function: copy the caller-provider value as the new control value */
+static int user_to_new(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl)
+{
+ int ret;
+ u32 size;
+
+ ctrl->has_new = 1;
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER64:
+ ctrl->val64 = c->value64;
+ break;
+ case V4L2_CTRL_TYPE_STRING:
+ size = c->size;
+ if (size == 0)
+ return -ERANGE;
+ if (size > ctrl->maximum + 1)
+ size = ctrl->maximum + 1;
+ ret = copy_from_user(ctrl->string, c->string, size);
+ if (!ret) {
+ char last = ctrl->string[size - 1];
+
+ ctrl->string[size - 1] = 0;
+ /* If the string was longer than ctrl->maximum,
+ then return an error. */
+ if (strlen(ctrl->string) == ctrl->maximum && last)
+ return -ERANGE;
+ }
+ return ret ? -EFAULT : 0;
+ default:
+ ctrl->val = c->value;
+ break;
+ }
+ return 0;
+}
+
+/* Helper function: copy the new control value back to the caller */
+static int new_to_user(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl)
+{
+ u32 len;
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_STRING:
+ len = strlen(ctrl->string);
+ if (c->size < len + 1) {
+ c->size = ctrl->maximum + 1;
+ return -ENOSPC;
+ }
+ return copy_to_user(c->string, ctrl->string,
+ len + 1) ? -EFAULT : 0;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ c->value64 = ctrl->val64;
+ break;
+ default:
+ c->value = ctrl->val;
+ break;
+ }
+ return 0;
+}
+
+/* Copy the new value to the current value. */
+static void new_to_cur(struct v4l2_ctrl *ctrl)
+{
+ if (ctrl == NULL)
+ return;
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_STRING:
+ /* strings are always 0-terminated */
+ strcpy(ctrl->cur.string, ctrl->string);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ ctrl->cur.val64 = ctrl->val64;
+ break;
+ default:
+ ctrl->cur.val = ctrl->val;
+ break;
+ }
+}
+
+/* Copy the current value to the new value */
+static void cur_to_new(struct v4l2_ctrl *ctrl)
+{
+ if (ctrl == NULL)
+ return;
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_STRING:
+ /* strings are always 0-terminated */
+ strcpy(ctrl->string, ctrl->cur.string);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ ctrl->val64 = ctrl->cur.val64;
+ break;
+ default:
+ ctrl->val = ctrl->cur.val;
+ break;
+ }
+}
+
+/* Return non-zero if one or more of the controls in the cluster has a new
+ value that differs from the current value. */
+static int cluster_changed(struct v4l2_ctrl *master)
+{
+ int diff = 0;
+ int i;
+
+ for (i = 0; !diff && i < master->ncontrols; i++) {
+ struct v4l2_ctrl *ctrl = master->cluster[i];
+
+ if (ctrl == NULL)
+ continue;
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_BUTTON:
+ /* Button controls are always 'different' */
+ return 1;
+ case V4L2_CTRL_TYPE_STRING:
+ /* strings are always 0-terminated */
+ diff = strcmp(ctrl->string, ctrl->cur.string);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ diff = ctrl->val64 != ctrl->cur.val64;
+ break;
+ default:
+ diff = ctrl->val != ctrl->cur.val;
+ break;
+ }
+ }
+ return diff;
+}
+
+/* Validate a new control */
+static int validate_new(struct v4l2_ctrl *ctrl)
+{
+ s32 val = ctrl->val;
+ char *s = ctrl->string;
+ u32 offset;
+ size_t len;
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER:
+ /* Round towards the closest legal value */
+ val += ctrl->step / 2;
+ if (val < ctrl->minimum)
+ val = ctrl->minimum;
+ if (val > ctrl->maximum)
+ val = ctrl->maximum;
+ offset = val - ctrl->minimum;
+ offset = ctrl->step * (offset / ctrl->step);
+ val = ctrl->minimum + offset;
+ ctrl->val = val;
+ return 0;
+
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ ctrl->val = !!ctrl->val;
+ return 0;
+
+ case V4L2_CTRL_TYPE_MENU:
+ if (val < ctrl->minimum || val > ctrl->maximum)
+ return -ERANGE;
+ if (ctrl->qmenu[val][0] == '\0' ||
+ (ctrl->menu_skip_mask & (1 << val)))
+ return -EINVAL;
+ return 0;
+
+ case V4L2_CTRL_TYPE_BUTTON:
+ case V4L2_CTRL_TYPE_CTRL_CLASS:
+ ctrl->val64 = 0;
+ return 0;
+
+ case V4L2_CTRL_TYPE_INTEGER64:
+ return 0;
+
+ case V4L2_CTRL_TYPE_STRING:
+ len = strlen(s);
+ if (len < ctrl->minimum)
+ return -ERANGE;
+ if ((len - ctrl->minimum) % ctrl->step)
+ return -ERANGE;
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static inline u32 node2id(struct list_head *node)
+{
+ return list_entry(node, struct v4l2_ctrl_ref, node)->ctrl->id;
+}
+
+/* Set the handler's error code if it wasn't set earlier already */
+static inline int handler_set_err(struct v4l2_ctrl_handler *hdl, int err)
+{
+ if (hdl->error == 0)
+ hdl->error = err;
+ return err;
+}
+
+/* Initialize the handler */
+int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl,
+ unsigned nr_of_controls_hint)
+{
+ mutex_init(&hdl->lock);
+ INIT_LIST_HEAD(&hdl->ctrls);
+ INIT_LIST_HEAD(&hdl->ctrl_refs);
+ hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
+ hdl->buckets = kzalloc(sizeof(hdl->buckets[0]) * hdl->nr_of_buckets,
+ GFP_KERNEL);
+ hdl->error = hdl->buckets ? 0 : -ENOMEM;
+ return hdl->error;
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_init);
+
+/* Free all controls and control refs */
+void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
+{
+ struct v4l2_ctrl_ref *ref, *next_ref;
+ struct v4l2_ctrl *ctrl, *next_ctrl;
+
+ if (hdl == NULL || hdl->buckets == NULL)
+ return;
+
+ mutex_lock(&hdl->lock);
+ /* Free all nodes */
+ list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) {
+ list_del(&ref->node);
+ kfree(ref);
+ }
+ /* Free all controls owned by the handler */
+ list_for_each_entry_safe(ctrl, next_ctrl, &hdl->ctrls, node) {
+ list_del(&ctrl->node);
+ kfree(ctrl);
+ }
+ kfree(hdl->buckets);
+ hdl->buckets = NULL;
+ hdl->cached = NULL;
+ hdl->error = 0;
+ mutex_unlock(&hdl->lock);
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_free);
+
+/* For backwards compatibility: V4L2_CID_PRIVATE_BASE should no longer
+ be used except in G_CTRL, S_CTRL, QUERYCTRL and QUERYMENU when dealing
+ with applications that do not use the NEXT_CTRL flag.
+
+ We just find the n-th private user control. It's O(N), but that should not
+ be an issue in this particular case. */
+static struct v4l2_ctrl_ref *find_private_ref(
+ struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl_ref *ref;
+
+ id -= V4L2_CID_PRIVATE_BASE;
+ list_for_each_entry(ref, &hdl->ctrl_refs, node) {
+ /* Search for private user controls that are compatible with
+ VIDIOC_G/S_CTRL. */
+ if (V4L2_CTRL_ID2CLASS(ref->ctrl->id) == V4L2_CTRL_CLASS_USER &&
+ V4L2_CTRL_DRIVER_PRIV(ref->ctrl->id)) {
+ if (!type_is_int(ref->ctrl))
+ continue;
+ if (id == 0)
+ return ref;
+ id--;
+ }
+ }
+ return NULL;
+}
+
+/* Find a control with the given ID. */
+static struct v4l2_ctrl_ref *find_ref(struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl_ref *ref;
+ int bucket;
+
+ id &= V4L2_CTRL_ID_MASK;
+
+ /* Old-style private controls need special handling */
+ if (id >= V4L2_CID_PRIVATE_BASE)
+ return find_private_ref(hdl, id);
+ bucket = id % hdl->nr_of_buckets;
+
+ /* Simple optimization: cache the last control found */
+ if (hdl->cached && hdl->cached->ctrl->id == id)
+ return hdl->cached;
+
+ /* Not in cache, search the hash */
+ ref = hdl->buckets ? hdl->buckets[bucket] : NULL;
+ while (ref && ref->ctrl->id != id)
+ ref = ref->next;
+
+ if (ref)
+ hdl->cached = ref; /* cache it! */
+ return ref;
+}
+
+/* Find a control with the given ID. Take the handler's lock first. */
+static struct v4l2_ctrl_ref *find_ref_lock(
+ struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl_ref *ref = NULL;
+
+ if (hdl) {
+ mutex_lock(&hdl->lock);
+ ref = find_ref(hdl, id);
+ mutex_unlock(&hdl->lock);
+ }
+ return ref;
+}
+
+/* Find a control with the given ID. */
+struct v4l2_ctrl *v4l2_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id);
+
+ return ref ? ref->ctrl : NULL;
+}
+EXPORT_SYMBOL(v4l2_ctrl_find);
+
+/* Allocate a new v4l2_ctrl_ref and hook it into the handler. */
+static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_ctrl_ref *ref;
+ struct v4l2_ctrl_ref *new_ref;
+ u32 id = ctrl->id;
+ u32 class_ctrl = V4L2_CTRL_ID2CLASS(id) | 1;
+ int bucket = id % hdl->nr_of_buckets; /* which bucket to use */
+
+ /* Automatically add the control class if it is not yet present. */
+ if (id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL)
+ if (!v4l2_ctrl_new_std(hdl, NULL, class_ctrl, 0, 0, 0, 0))
+ return hdl->error;
+
+ if (hdl->error)
+ return hdl->error;
+
+ new_ref = kzalloc(sizeof(*new_ref), GFP_KERNEL);
+ if (!new_ref)
+ return handler_set_err(hdl, -ENOMEM);
+ new_ref->ctrl = ctrl;
+ if (ctrl->handler == hdl) {
+ /* By default each control starts in a cluster of its own.
+ new_ref->ctrl is basically a cluster array with one
+ element, so that's perfect to use as the cluster pointer.
+ But only do this for the handler that owns the control. */
+ ctrl->cluster = &new_ref->ctrl;
+ ctrl->ncontrols = 1;
+ }
+
+ INIT_LIST_HEAD(&new_ref->node);
+
+ mutex_lock(&hdl->lock);
+
+ /* Add immediately at the end of the list if the list is empty, or if
+ the last element in the list has a lower ID.
+ This ensures that when elements are added in ascending order the
+ insertion is an O(1) operation. */
+ if (list_empty(&hdl->ctrl_refs) || id > node2id(hdl->ctrl_refs.prev)) {
+ list_add_tail(&new_ref->node, &hdl->ctrl_refs);
+ goto insert_in_hash;
+ }
+
+ /* Find insert position in sorted list */
+ list_for_each_entry(ref, &hdl->ctrl_refs, node) {
+ if (ref->ctrl->id < id)
+ continue;
+ /* Don't add duplicates */
+ if (ref->ctrl->id == id) {
+ kfree(new_ref);
+ goto unlock;
+ }
+ list_add(&new_ref->node, ref->node.prev);
+ break;
+ }
+
+insert_in_hash:
+ /* Insert the control node in the hash */
+ new_ref->next = hdl->buckets[bucket];
+ hdl->buckets[bucket] = new_ref;
+
+unlock:
+ mutex_unlock(&hdl->lock);
+ return 0;
+}
+
+/* Add a new control */
+static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, const char *name, enum v4l2_ctrl_type type,
+ s32 min, s32 max, u32 step, s32 def,
+ u32 flags, const char **qmenu, void *priv)
+{
+ struct v4l2_ctrl *ctrl;
+ unsigned sz_extra = 0;
+
+ if (hdl->error)
+ return NULL;
+
+ /* Sanity checks */
+ if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE ||
+ def < min || def > max || max < min ||
+ (type == V4L2_CTRL_TYPE_INTEGER && step == 0) ||
+ (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) ||
+ (type == V4L2_CTRL_TYPE_STRING && max == 0)) {
+ handler_set_err(hdl, -ERANGE);
+ return NULL;
+ }
+
+ if (type == V4L2_CTRL_TYPE_BUTTON)
+ flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+ else if (type == V4L2_CTRL_TYPE_CTRL_CLASS)
+ flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ else if (type == V4L2_CTRL_TYPE_STRING)
+ sz_extra += 2 * (max + 1);
+
+ ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
+ if (ctrl == NULL) {
+ handler_set_err(hdl, -ENOMEM);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&ctrl->node);
+ ctrl->handler = hdl;
+ ctrl->ops = ops;
+ ctrl->id = id;
+ ctrl->name = name;
+ ctrl->type = type;
+ ctrl->flags = flags;
+ ctrl->minimum = min;
+ ctrl->maximum = max;
+ ctrl->step = step;
+ ctrl->qmenu = qmenu;
+ ctrl->priv = priv;
+ ctrl->cur.val = ctrl->val = ctrl->default_value = def;
+
+ if (ctrl->type == V4L2_CTRL_TYPE_STRING) {
+ ctrl->cur.string = (char *)&ctrl[1] + sz_extra - (max + 1);
+ ctrl->string = (char *)&ctrl[1] + sz_extra - 2 * (max + 1);
+ if (ctrl->minimum)
+ memset(ctrl->cur.string, ' ', ctrl->minimum);
+ }
+ if (handler_new_ref(hdl, ctrl)) {
+ kfree(ctrl);
+ return NULL;
+ }
+ mutex_lock(&hdl->lock);
+ list_add_tail(&ctrl->node, &hdl->ctrls);
+ mutex_unlock(&hdl->lock);
+ return ctrl;
+}
+
+struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_config *cfg, void *priv)
+{
+ bool is_menu;
+ struct v4l2_ctrl *ctrl;
+ const char *name = cfg->name;
+ const char **qmenu = cfg->qmenu;
+ enum v4l2_ctrl_type type = cfg->type;
+ u32 flags = cfg->flags;
+ s32 min = cfg->min;
+ s32 max = cfg->max;
+ u32 step = cfg->step;
+ s32 def = cfg->def;
+
+ if (name == NULL)
+ v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step,
+ &def, &flags);
+
+ is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU);
+ if (is_menu)
+ WARN_ON(step);
+ else
+ WARN_ON(cfg->menu_skip_mask);
+ if (is_menu && qmenu == NULL)
+ qmenu = v4l2_ctrl_get_menu(cfg->id);
+
+ ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name,
+ type, min, max,
+ is_menu ? cfg->menu_skip_mask : step,
+ def, flags, qmenu, priv);
+ if (ctrl) {
+ ctrl->is_private = cfg->is_private;
+ ctrl->is_volatile = cfg->is_volatile;
+ }
+ return ctrl;
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_custom);
+
+/* Helper function for standard non-menu controls */
+struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, s32 min, s32 max, u32 step, s32 def)
+{
+ const char *name;
+ enum v4l2_ctrl_type type;
+ u32 flags;
+
+ v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
+ if (type == V4L2_CTRL_TYPE_MENU) {
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
+ return v4l2_ctrl_new(hdl, ops, id, name, type,
+ min, max, step, def, flags, NULL, NULL);
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_std);
+
+/* Helper function for standard menu controls */
+struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, s32 max, s32 mask, s32 def)
+{
+ const char **qmenu = v4l2_ctrl_get_menu(id);
+ const char *name;
+ enum v4l2_ctrl_type type;
+ s32 min;
+ s32 step;
+ u32 flags;
+
+ v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
+ if (type != V4L2_CTRL_TYPE_MENU) {
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
+ return v4l2_ctrl_new(hdl, ops, id, name, type,
+ 0, max, mask, def, flags, qmenu, NULL);
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
+
+/* Add a control from another handler to this handler */
+struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ctrl *ctrl)
+{
+ if (hdl == NULL || hdl->error)
+ return NULL;
+ if (ctrl == NULL) {
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
+ if (ctrl->handler == hdl)
+ return ctrl;
+ return handler_new_ref(hdl, ctrl) ? NULL : ctrl;
+}
+EXPORT_SYMBOL(v4l2_ctrl_add_ctrl);
+
+/* Add the controls from another handler to our own. */
+int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ctrl_handler *add)
+{
+ struct v4l2_ctrl *ctrl;
+ int ret = 0;
+
+ /* Do nothing if either handler is NULL or if they are the same */
+ if (!hdl || !add || hdl == add)
+ return 0;
+ if (hdl->error)
+ return hdl->error;
+ mutex_lock(&add->lock);
+ list_for_each_entry(ctrl, &add->ctrls, node) {
+ /* Skip handler-private controls. */
+ if (ctrl->is_private)
+ continue;
+ ret = handler_new_ref(hdl, ctrl);
+ if (ret)
+ break;
+ }
+ mutex_unlock(&add->lock);
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_ctrl_add_handler);
+
+/* Cluster controls */
+void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls)
+{
+ int i;
+
+ /* The first control is the master control and it must not be NULL */
+ BUG_ON(controls[0] == NULL);
+
+ for (i = 0; i < ncontrols; i++) {
+ if (controls[i]) {
+ controls[i]->cluster = controls;
+ controls[i]->ncontrols = ncontrols;
+ }
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_cluster);
+
+/* Activate/deactivate a control. */
+void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active)
+{
+ if (ctrl == NULL)
+ return;
+
+ if (!active)
+ /* set V4L2_CTRL_FLAG_INACTIVE */
+ set_bit(4, &ctrl->flags);
+ else
+ /* clear V4L2_CTRL_FLAG_INACTIVE */
+ clear_bit(4, &ctrl->flags);
+}
+EXPORT_SYMBOL(v4l2_ctrl_activate);
+
+/* Grab/ungrab a control.
+ Typically used when streaming starts and you want to grab controls,
+ preventing the user from changing them.
+
+ Just call this and the framework will block any attempts to change
+ these controls. */
+void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed)
+{
+ if (ctrl == NULL)
+ return;
+
+ if (grabbed)
+ /* set V4L2_CTRL_FLAG_GRABBED */
+ set_bit(1, &ctrl->flags);
+ else
+ /* clear V4L2_CTRL_FLAG_GRABBED */
+ clear_bit(1, &ctrl->flags);
+}
+EXPORT_SYMBOL(v4l2_ctrl_grab);
+
+/* Log the control name and value */
+static void log_ctrl(const struct v4l2_ctrl *ctrl,
+ const char *prefix, const char *colon)
+{
+ int fl_inact = ctrl->flags & V4L2_CTRL_FLAG_INACTIVE;
+ int fl_grabbed = ctrl->flags & V4L2_CTRL_FLAG_GRABBED;
+
+ if (ctrl->flags & (V4L2_CTRL_FLAG_DISABLED | V4L2_CTRL_FLAG_WRITE_ONLY))
+ return;
+ if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
+ return;
+
+ printk(KERN_INFO "%s%s%s: ", prefix, colon, ctrl->name);
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER:
+ printk(KERN_CONT "%d", ctrl->cur.val);
+ break;
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ printk(KERN_CONT "%s", ctrl->cur.val ? "true" : "false");
+ break;
+ case V4L2_CTRL_TYPE_MENU:
+ printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ printk(KERN_CONT "%lld", ctrl->cur.val64);
+ break;
+ case V4L2_CTRL_TYPE_STRING:
+ printk(KERN_CONT "%s", ctrl->cur.string);
+ break;
+ default:
+ printk(KERN_CONT "unknown type %d", ctrl->type);
+ break;
+ }
+ if (fl_inact && fl_grabbed)
+ printk(KERN_CONT " (inactive, grabbed)\n");
+ else if (fl_inact)
+ printk(KERN_CONT " (inactive)\n");
+ else if (fl_grabbed)
+ printk(KERN_CONT " (grabbed)\n");
+ else
+ printk(KERN_CONT "\n");
+}
+
+/* Log all controls owned by the handler */
+void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl,
+ const char *prefix)
+{
+ struct v4l2_ctrl *ctrl;
+ const char *colon = "";
+ int len;
+
+ if (hdl == NULL)
+ return;
+ if (prefix == NULL)
+ prefix = "";
+ len = strlen(prefix);
+ if (len && prefix[len - 1] != ' ')
+ colon = ": ";
+ mutex_lock(&hdl->lock);
+ list_for_each_entry(ctrl, &hdl->ctrls, node)
+ if (!(ctrl->flags & V4L2_CTRL_FLAG_DISABLED))
+ log_ctrl(ctrl, prefix, colon);
+ mutex_unlock(&hdl->lock);
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_log_status);
+
+/* Call s_ctrl for all controls owned by the handler */
+int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
+{
+ struct v4l2_ctrl *ctrl;
+ int ret = 0;
+
+ if (hdl == NULL)
+ return 0;
+ mutex_lock(&hdl->lock);
+ list_for_each_entry(ctrl, &hdl->ctrls, node)
+ ctrl->done = false;
+
+ list_for_each_entry(ctrl, &hdl->ctrls, node) {
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+ int i;
+
+ /* Skip if this control was already handled by a cluster. */
+ if (ctrl->done)
+ continue;
+
+ for (i = 0; i < master->ncontrols; i++)
+ cur_to_new(master->cluster[i]);
+
+ /* Skip button controls and read-only controls. */
+ if (ctrl->type == V4L2_CTRL_TYPE_BUTTON ||
+ (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY))
+ continue;
+ ret = master->ops->s_ctrl(master);
+ if (ret)
+ break;
+ for (i = 0; i < master->ncontrols; i++)
+ if (master->cluster[i])
+ master->cluster[i]->done = true;
+ }
+ mutex_unlock(&hdl->lock);
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_setup);
+
+/* Implement VIDIOC_QUERYCTRL */
+int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
+{
+ u32 id = qc->id & V4L2_CTRL_ID_MASK;
+ struct v4l2_ctrl_ref *ref;
+ struct v4l2_ctrl *ctrl;
+
+ if (hdl == NULL)
+ return -EINVAL;
+
+ mutex_lock(&hdl->lock);
+
+ /* Try to find it */
+ ref = find_ref(hdl, id);
+
+ if ((qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) && !list_empty(&hdl->ctrl_refs)) {
+ /* Find the next control with ID > qc->id */
+
+ /* Did we reach the end of the control list? */
+ if (id >= node2id(hdl->ctrl_refs.prev)) {
+ ref = NULL; /* Yes, so there is no next control */
+ } else if (ref) {
+ /* We found a control with the given ID, so just get
+ the next one in the list. */
+ ref = list_entry(ref->node.next, typeof(*ref), node);
+ } else {
+ /* No control with the given ID exists, so start
+ searching for the next largest ID. We know there
+ is one, otherwise the first 'if' above would have
+ been true. */
+ list_for_each_entry(ref, &hdl->ctrl_refs, node)
+ if (id < ref->ctrl->id)
+ break;
+ }
+ }
+ mutex_unlock(&hdl->lock);
+ if (!ref)
+ return -EINVAL;
+
+ ctrl = ref->ctrl;
+ memset(qc, 0, sizeof(*qc));
+ qc->id = ctrl->id;
+ strlcpy(qc->name, ctrl->name, sizeof(qc->name));
+ qc->minimum = ctrl->minimum;
+ qc->maximum = ctrl->maximum;
+ qc->default_value = ctrl->default_value;
+ if (qc->type == V4L2_CTRL_TYPE_MENU)
+ qc->step = 1;
+ else
+ qc->step = ctrl->step;
+ qc->flags = ctrl->flags;
+ qc->type = ctrl->type;
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_queryctrl);
+
+int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ return v4l2_queryctrl(sd->ctrl_handler, qc);
+}
+EXPORT_SYMBOL(v4l2_subdev_queryctrl);
+
+/* Implement VIDIOC_QUERYMENU */
+int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm)
+{
+ struct v4l2_ctrl *ctrl;
+ u32 i = qm->index;
+
+ ctrl = v4l2_ctrl_find(hdl, qm->id);
+ if (!ctrl)
+ return -EINVAL;
+
+ qm->reserved = 0;
+ /* Sanity checks */
+ if (ctrl->qmenu == NULL ||
+ i < ctrl->minimum || i > ctrl->maximum)
+ return -EINVAL;
+ /* Use mask to see if this menu item should be skipped */
+ if (ctrl->menu_skip_mask & (1 << i))
+ return -EINVAL;
+ /* Empty menu items should also be skipped */
+ if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0')
+ return -EINVAL;
+ strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name));
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_querymenu);
+
+int v4l2_subdev_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qm)
+{
+ return v4l2_querymenu(sd->ctrl_handler, qm);
+}
+EXPORT_SYMBOL(v4l2_subdev_querymenu);
+
+
+
+/* Some general notes on the atomic requirements of VIDIOC_G/TRY/S_EXT_CTRLS:
+
+ It is not a fully atomic operation, just best-effort only. After all, if
+ multiple controls have to be set through multiple i2c writes (for example)
+ then some initial writes may succeed while others fail. Thus leaving the
+ system in an inconsistent state. The question is how much effort you are
+ willing to spend on trying to make something atomic that really isn't.
+
+ From the point of view of an application the main requirement is that
+ when you call VIDIOC_S_EXT_CTRLS and some values are invalid then an
+ error should be returned without actually affecting any controls.
+
+ If all the values are correct, then it is acceptable to just give up
+ in case of low-level errors.
+
+ It is important though that the application can tell when only a partial
+ configuration was done. The way we do that is through the error_idx field
+ of struct v4l2_ext_controls: if that is equal to the count field then no
+ controls were affected. Otherwise all controls before that index were
+ successful in performing their 'get' or 'set' operation, the control at
+ the given index failed, and you don't know what happened with the controls
+ after the failed one. Since if they were part of a control cluster they
+ could have been successfully processed (if a cluster member was encountered
+ at index < error_idx), they could have failed (if a cluster member was at
+ error_idx), or they may not have been processed yet (if the first cluster
+ member appeared after error_idx).
+
+ It is all fairly theoretical, though. In practice all you can do is to
+ bail out. If error_idx == count, then it is an application bug. If
+ error_idx < count then it is only an application bug if the error code was
+ EBUSY. That usually means that something started streaming just when you
+ tried to set the controls. In all other cases it is a driver/hardware
+ problem and all you can do is to retry or bail out.
+
+ Note that these rules do not apply to VIDIOC_TRY_EXT_CTRLS: since that
+ never modifies controls the error_idx is just set to whatever control
+ has an invalid value.
+ */
+
+/* Prepare for the extended g/s/try functions.
+ Find the controls in the control array and do some basic checks. */
+static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ext_controls *cs,
+ struct ctrl_helper *helpers,
+ bool try)
+{
+ u32 i;
+
+ for (i = 0; i < cs->count; i++) {
+ struct v4l2_ext_control *c = &cs->controls[i];
+ struct v4l2_ctrl *ctrl;
+ u32 id = c->id & V4L2_CTRL_ID_MASK;
+
+ if (try)
+ cs->error_idx = i;
+
+ if (cs->ctrl_class && V4L2_CTRL_ID2CLASS(id) != cs->ctrl_class)
+ return -EINVAL;
+
+ /* Old-style private controls are not allowed for
+ extended controls */
+ if (id >= V4L2_CID_PRIVATE_BASE)
+ return -EINVAL;
+ ctrl = v4l2_ctrl_find(hdl, id);
+ if (ctrl == NULL)
+ return -EINVAL;
+ if (ctrl->flags & V4L2_CTRL_FLAG_DISABLED)
+ return -EINVAL;
+
+ helpers[i].ctrl = ctrl;
+ helpers[i].handled = false;
+ }
+ return 0;
+}
+
+typedef int (*cluster_func)(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl);
+
+/* Walk over all controls in v4l2_ext_controls belonging to the same cluster
+ and call the provided function. */
+static int cluster_walk(unsigned from,
+ struct v4l2_ext_controls *cs,
+ struct ctrl_helper *helpers,
+ cluster_func f)
+{
+ struct v4l2_ctrl **cluster = helpers[from].ctrl->cluster;
+ int ret = 0;
+ int i;
+
+ /* Find any controls from the same cluster and call the function */
+ for (i = from; !ret && i < cs->count; i++) {
+ struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+
+ if (!helpers[i].handled && ctrl->cluster == cluster)
+ ret = f(&cs->controls[i], ctrl);
+ }
+ return ret;
+}
+
+static void cluster_done(unsigned from,
+ struct v4l2_ext_controls *cs,
+ struct ctrl_helper *helpers)
+{
+ struct v4l2_ctrl **cluster = helpers[from].ctrl->cluster;
+ int i;
+
+ /* Find any controls from the same cluster and mark them as handled */
+ for (i = from; i < cs->count; i++)
+ if (helpers[i].ctrl->cluster == cluster)
+ helpers[i].handled = true;
+}
+
+/* Handles the corner case where cs->count == 0. It checks whether the
+ specified control class exists. If that class ID is 0, then it checks
+ whether there are any controls at all. */
+static int class_check(struct v4l2_ctrl_handler *hdl, u32 ctrl_class)
+{
+ if (ctrl_class == 0)
+ return list_empty(&hdl->ctrl_refs) ? -EINVAL : 0;
+ return find_ref_lock(hdl, ctrl_class | 1) ? 0 : -EINVAL;
+}
+
+
+
+/* Get extended controls. Allocates the helpers array if needed. */
+int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+{
+ struct ctrl_helper helper[4];
+ struct ctrl_helper *helpers = helper;
+ int ret;
+ int i;
+
+ cs->error_idx = cs->count;
+ cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
+
+ if (hdl == NULL)
+ return -EINVAL;
+
+ if (cs->count == 0)
+ return class_check(hdl, cs->ctrl_class);
+
+ if (cs->count > ARRAY_SIZE(helper)) {
+ helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL);
+ if (helpers == NULL)
+ return -ENOMEM;
+ }
+
+ ret = prepare_ext_ctrls(hdl, cs, helpers, false);
+
+ for (i = 0; !ret && i < cs->count; i++)
+ if (helpers[i].ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
+ ret = -EACCES;
+
+ for (i = 0; !ret && i < cs->count; i++) {
+ struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+
+ if (helpers[i].handled)
+ continue;
+
+ cs->error_idx = i;
+
+ v4l2_ctrl_lock(master);
+ /* g_volatile_ctrl will update the current control values */
+ if (ctrl->is_volatile && master->ops->g_volatile_ctrl)
+ ret = master->ops->g_volatile_ctrl(master);
+ /* If OK, then copy the current control values to the caller */
+ if (!ret)
+ ret = cluster_walk(i, cs, helpers, cur_to_user);
+ v4l2_ctrl_unlock(master);
+ cluster_done(i, cs, helpers);
+ }
+
+ if (cs->count > ARRAY_SIZE(helper))
+ kfree(helpers);
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_g_ext_ctrls);
+
+int v4l2_subdev_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+ return v4l2_g_ext_ctrls(sd->ctrl_handler, cs);
+}
+EXPORT_SYMBOL(v4l2_subdev_g_ext_ctrls);
+
+/* Helper function to get a single control */
+static int get_ctrl(struct v4l2_ctrl *ctrl, s32 *val)
+{
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+ int ret = 0;
+
+ if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
+ return -EACCES;
+
+ v4l2_ctrl_lock(master);
+ /* g_volatile_ctrl will update the current control values */
+ if (ctrl->is_volatile && master->ops->g_volatile_ctrl)
+ ret = master->ops->g_volatile_ctrl(master);
+ *val = ctrl->cur.val;
+ v4l2_ctrl_unlock(master);
+ return ret;
+}
+
+int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control)
+{
+ struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
+
+ if (ctrl == NULL || !type_is_int(ctrl))
+ return -EINVAL;
+ return get_ctrl(ctrl, &control->value);
+}
+EXPORT_SYMBOL(v4l2_g_ctrl);
+
+int v4l2_subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
+{
+ return v4l2_g_ctrl(sd->ctrl_handler, control);
+}
+EXPORT_SYMBOL(v4l2_subdev_g_ctrl);
+
+s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl)
+{
+ s32 val = 0;
+
+ /* It's a driver bug if this happens. */
+ WARN_ON(!type_is_int(ctrl));
+ get_ctrl(ctrl, &val);
+ return val;
+}
+EXPORT_SYMBOL(v4l2_ctrl_g_ctrl);
+
+
+/* Core function that calls try/s_ctrl and ensures that the new value is
+ copied to the current value on a set.
+ Must be called with ctrl->handler->lock held. */
+static int try_or_set_control_cluster(struct v4l2_ctrl *master, bool set)
+{
+ bool try = !set;
+ int ret = 0;
+ int i;
+
+ /* Go through the cluster and either validate the new value or
+ (if no new value was set), copy the current value to the new
+ value, ensuring a consistent view for the control ops when
+ called. */
+ for (i = 0; !ret && i < master->ncontrols; i++) {
+ struct v4l2_ctrl *ctrl = master->cluster[i];
+
+ if (ctrl == NULL)
+ continue;
+
+ if (ctrl->has_new) {
+ /* Double check this: it may have changed since the
+ last check in try_or_set_ext_ctrls(). */
+ if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
+ return -EBUSY;
+
+ /* Validate if required */
+ if (!set)
+ ret = validate_new(ctrl);
+ continue;
+ }
+ /* No new value was set, so copy the current and force
+ a call to try_ctrl later, since the values for the cluster
+ may now have changed and the end result might be invalid. */
+ try = true;
+ cur_to_new(ctrl);
+ }
+
+ /* For larger clusters you have to call try_ctrl again to
+ verify that the controls are still valid after the
+ 'cur_to_new' above. */
+ if (!ret && master->ops->try_ctrl && try)
+ ret = master->ops->try_ctrl(master);
+
+ /* Don't set if there is no change */
+ if (!ret && set && cluster_changed(master)) {
+ ret = master->ops->s_ctrl(master);
+ /* If OK, then make the new values permanent. */
+ if (!ret)
+ for (i = 0; i < master->ncontrols; i++)
+ new_to_cur(master->cluster[i]);
+ }
+ return ret;
+}
+
+/* Try or set controls. */
+static int try_or_set_ext_ctrls(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ext_controls *cs,
+ struct ctrl_helper *helpers,
+ bool set)
+{
+ unsigned i, j;
+ int ret = 0;
+
+ cs->error_idx = cs->count;
+ for (i = 0; i < cs->count; i++) {
+ struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+
+ if (!set)
+ cs->error_idx = i;
+
+ if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
+ return -EACCES;
+ /* This test is also done in try_set_control_cluster() which
+ is called in atomic context, so that has the final say,
+ but it makes sense to do an up-front check as well. Once
+ an error occurs in try_set_control_cluster() some other
+ controls may have been set already and we want to do a
+ best-effort to avoid that. */
+ if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
+ return -EBUSY;
+ }
+
+ for (i = 0; !ret && i < cs->count; i++) {
+ struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+
+ cs->error_idx = i;
+
+ if (helpers[i].handled)
+ continue;
+
+ v4l2_ctrl_lock(ctrl);
+
+ /* Reset the 'has_new' flags of the cluster */
+ for (j = 0; j < master->ncontrols; j++)
+ if (master->cluster[j])
+ master->cluster[j]->has_new = 0;
+
+ /* Copy the new caller-supplied control values.
+ user_to_new() sets 'has_new' to 1. */
+ ret = cluster_walk(i, cs, helpers, user_to_new);
+
+ if (!ret)
+ ret = try_or_set_control_cluster(master, set);
+
+ /* Copy the new values back to userspace. */
+ if (!ret)
+ ret = cluster_walk(i, cs, helpers, new_to_user);
+
+ v4l2_ctrl_unlock(ctrl);
+ cluster_done(i, cs, helpers);
+ }
+ return ret;
+}
+
+/* Try or try-and-set controls */
+static int try_set_ext_ctrls(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ext_controls *cs,
+ bool set)
+{
+ struct ctrl_helper helper[4];
+ struct ctrl_helper *helpers = helper;
+ int ret;
+ int i;
+
+ cs->error_idx = cs->count;
+ cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
+
+ if (hdl == NULL)
+ return -EINVAL;
+
+ if (cs->count == 0)
+ return class_check(hdl, cs->ctrl_class);
+
+ if (cs->count > ARRAY_SIZE(helper)) {
+ helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL);
+ if (!helpers)
+ return -ENOMEM;
+ }
+ ret = prepare_ext_ctrls(hdl, cs, helpers, !set);
+ if (ret)
+ goto free;
+
+ /* First 'try' all controls and abort on error */
+ ret = try_or_set_ext_ctrls(hdl, cs, helpers, false);
+ /* If this is a 'set' operation and the initial 'try' failed,
+ then set error_idx to count to tell the application that no
+ controls changed value yet. */
+ if (set)
+ cs->error_idx = cs->count;
+ if (!ret && set) {
+ /* Reset 'handled' state */
+ for (i = 0; i < cs->count; i++)
+ helpers[i].handled = false;
+ ret = try_or_set_ext_ctrls(hdl, cs, helpers, true);
+ }
+
+free:
+ if (cs->count > ARRAY_SIZE(helper))
+ kfree(helpers);
+ return ret;
+}
+
+int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+{
+ return try_set_ext_ctrls(hdl, cs, false);
+}
+EXPORT_SYMBOL(v4l2_try_ext_ctrls);
+
+int v4l2_s_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+{
+ return try_set_ext_ctrls(hdl, cs, true);
+}
+EXPORT_SYMBOL(v4l2_s_ext_ctrls);
+
+int v4l2_subdev_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+ return try_set_ext_ctrls(sd->ctrl_handler, cs, false);
+}
+EXPORT_SYMBOL(v4l2_subdev_try_ext_ctrls);
+
+int v4l2_subdev_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+ return try_set_ext_ctrls(sd->ctrl_handler, cs, true);
+}
+EXPORT_SYMBOL(v4l2_subdev_s_ext_ctrls);
+
+/* Helper function for VIDIOC_S_CTRL compatibility */
+static int set_ctrl(struct v4l2_ctrl *ctrl, s32 *val)
+{
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+ int ret;
+ int i;
+
+ v4l2_ctrl_lock(ctrl);
+
+ /* Reset the 'has_new' flags of the cluster */
+ for (i = 0; i < master->ncontrols; i++)
+ if (master->cluster[i])
+ master->cluster[i]->has_new = 0;
+
+ ctrl->val = *val;
+ ctrl->has_new = 1;
+ ret = try_or_set_control_cluster(master, false);
+ if (!ret)
+ ret = try_or_set_control_cluster(master, true);
+ *val = ctrl->cur.val;
+ v4l2_ctrl_unlock(ctrl);
+ return ret;
+}
+
+int v4l2_s_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control)
+{
+ struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
+
+ if (ctrl == NULL || !type_is_int(ctrl))
+ return -EINVAL;
+
+ return set_ctrl(ctrl, &control->value);
+}
+EXPORT_SYMBOL(v4l2_s_ctrl);
+
+int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
+{
+ return v4l2_s_ctrl(sd->ctrl_handler, control);
+}
+EXPORT_SYMBOL(v4l2_subdev_s_ctrl);
+
+int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
+{
+ /* It's a driver bug if this happens. */
+ WARN_ON(!type_is_int(ctrl));
+ return set_ctrl(ctrl, &val);
+}
+EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/slab.h>
+#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <asm/system.h>
return vdev->fops->poll(filp, poll);
}
-static int v4l2_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg)
+static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct video_device *vdev = video_devdata(filp);
+ int ret;
- if (!vdev->fops->ioctl)
- return -ENOTTY;
/* Allow ioctl to continue even if the device was unregistered.
Things like dequeueing buffers might still be useful. */
- return vdev->fops->ioctl(filp, cmd, arg);
-}
-
-static long v4l2_unlocked_ioctl(struct file *filp,
- unsigned int cmd, unsigned long arg)
-{
- struct video_device *vdev = video_devdata(filp);
+ if (vdev->fops->unlocked_ioctl) {
+ ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
+ } else if (vdev->fops->ioctl) {
+ /* TODO: convert all drivers to unlocked_ioctl */
+ lock_kernel();
+ ret = vdev->fops->ioctl(filp, cmd, arg);
+ unlock_kernel();
+ } else
+ ret = -ENOTTY;
- if (!vdev->fops->unlocked_ioctl)
- return -ENOTTY;
- /* Allow ioctl to continue even if the device was unregistered.
- Things like dequeueing buffers might still be useful. */
- return vdev->fops->unlocked_ioctl(filp, cmd, arg);
+ return ret;
}
#ifdef CONFIG_MMU
return ret;
}
-static const struct file_operations v4l2_unlocked_fops = {
- .owner = THIS_MODULE,
- .read = v4l2_read,
- .write = v4l2_write,
- .open = v4l2_open,
- .get_unmapped_area = v4l2_get_unmapped_area,
- .mmap = v4l2_mmap,
- .unlocked_ioctl = v4l2_unlocked_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = v4l2_compat_ioctl32,
-#endif
- .release = v4l2_release,
- .poll = v4l2_poll,
- .llseek = no_llseek,
-};
-
static const struct file_operations v4l2_fops = {
.owner = THIS_MODULE,
.read = v4l2_read,
.open = v4l2_open,
.get_unmapped_area = v4l2_get_unmapped_area,
.mmap = v4l2_mmap,
- .ioctl = v4l2_ioctl,
+ .unlocked_ioctl = v4l2_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l2_compat_ioctl32,
#endif
vdev->vfl_type = type;
vdev->cdev = NULL;
- if (vdev->v4l2_dev && vdev->v4l2_dev->dev)
- vdev->parent = vdev->v4l2_dev->dev;
+ if (vdev->v4l2_dev) {
+ if (vdev->v4l2_dev->dev)
+ vdev->parent = vdev->v4l2_dev->dev;
+ if (vdev->ctrl_handler == NULL)
+ vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
+ }
/* Part 2: find a free minor, device node number and device index. */
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
ret = -ENOMEM;
goto cleanup;
}
- if (vdev->fops->unlocked_ioctl)
- vdev->cdev->ops = &v4l2_unlocked_fops;
- else
- vdev->cdev->ops = &v4l2_fops;
+ vdev->cdev->ops = &v4l2_fops;
vdev->cdev->owner = vdev->fops->owner;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
if (ret < 0) {
#endif
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
{
int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
struct v4l2_subdev *sd)
{
+ int err;
+
/* Check for valid input */
if (v4l2_dev == NULL || sd == NULL || !sd->name[0])
return -EINVAL;
WARN_ON(sd->v4l2_dev != NULL);
if (!try_module_get(sd->owner))
return -ENODEV;
+ /* This just returns 0 if either of the two args is NULL */
+ err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler);
+ if (err)
+ return err;
sd->v4l2_dev = v4l2_dev;
spin_lock(&v4l2_dev->lock);
list_add_tail(&sd->list, &v4l2_dev->subdevs);
#endif
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
#include <media/v4l2-chip-ident.h>
{
struct v4l2_queryctrl *p = arg;
- if (!ops->vidioc_queryctrl)
+ if (vfd->ctrl_handler)
+ ret = v4l2_queryctrl(vfd->ctrl_handler, p);
+ else if (ops->vidioc_queryctrl)
+ ret = ops->vidioc_queryctrl(file, fh, p);
+ else
break;
- ret = ops->vidioc_queryctrl(file, fh, p);
if (!ret)
dbgarg(cmd, "id=0x%x, type=%d, name=%s, min/max=%d/%d, "
"step=%d, default=%d, flags=0x%08x\n",
{
struct v4l2_control *p = arg;
- if (ops->vidioc_g_ctrl)
+ if (vfd->ctrl_handler)
+ ret = v4l2_g_ctrl(vfd->ctrl_handler, p);
+ else if (ops->vidioc_g_ctrl)
ret = ops->vidioc_g_ctrl(file, fh, p);
else if (ops->vidioc_g_ext_ctrls) {
struct v4l2_ext_controls ctrls;
struct v4l2_ext_controls ctrls;
struct v4l2_ext_control ctrl;
- if (!ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls)
+ if (!vfd->ctrl_handler &&
+ !ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls)
break;
dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
+ if (vfd->ctrl_handler) {
+ ret = v4l2_s_ctrl(vfd->ctrl_handler, p);
+ break;
+ }
if (ops->vidioc_s_ctrl) {
ret = ops->vidioc_s_ctrl(file, fh, p);
break;
struct v4l2_ext_controls *p = arg;
p->error_idx = p->count;
- if (!ops->vidioc_g_ext_ctrls)
- break;
- if (check_ext_ctrls(p, 0))
+ if (vfd->ctrl_handler)
+ ret = v4l2_g_ext_ctrls(vfd->ctrl_handler, p);
+ else if (ops->vidioc_g_ext_ctrls && check_ext_ctrls(p, 0))
ret = ops->vidioc_g_ext_ctrls(file, fh, p);
+ else
+ break;
v4l_print_ext_ctrls(cmd, vfd, p, !ret);
break;
}
struct v4l2_ext_controls *p = arg;
p->error_idx = p->count;
- if (!ops->vidioc_s_ext_ctrls)
+ if (!vfd->ctrl_handler && !ops->vidioc_s_ext_ctrls)
break;
v4l_print_ext_ctrls(cmd, vfd, p, 1);
- if (check_ext_ctrls(p, 0))
+ if (vfd->ctrl_handler)
+ ret = v4l2_s_ext_ctrls(vfd->ctrl_handler, p);
+ else if (check_ext_ctrls(p, 0))
ret = ops->vidioc_s_ext_ctrls(file, fh, p);
break;
}
struct v4l2_ext_controls *p = arg;
p->error_idx = p->count;
- if (!ops->vidioc_try_ext_ctrls)
+ if (!vfd->ctrl_handler && !ops->vidioc_try_ext_ctrls)
break;
v4l_print_ext_ctrls(cmd, vfd, p, 1);
- if (check_ext_ctrls(p, 0))
+ if (vfd->ctrl_handler)
+ ret = v4l2_try_ext_ctrls(vfd->ctrl_handler, p);
+ else if (check_ext_ctrls(p, 0))
ret = ops->vidioc_try_ext_ctrls(file, fh, p);
break;
}
{
struct v4l2_querymenu *p = arg;
- if (!ops->vidioc_querymenu)
+ if (vfd->ctrl_handler)
+ ret = v4l2_querymenu(vfd->ctrl_handler, p);
+ else if (ops->vidioc_querymenu)
+ ret = ops->vidioc_querymenu(file, fh, p);
+ else
break;
- ret = ops->vidioc_querymenu(file, fh, p);
if (!ret)
dbgarg(cmd, "id=0x%x, index=%d, name=%s\n",
p->id, p->index, p->name);
#include <linux/ioctl.h>
#include <asm/uaccess.h>
#include <linux/i2c.h>
-#include <linux/i2c-id.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
+#include <media/v4l2-ctrls.h>
MODULE_DESCRIPTION("wm8739 driver");
MODULE_AUTHOR("T. Adachi, Hans Verkuil");
struct wm8739_state {
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* audio cluster */
+ struct v4l2_ctrl *volume;
+ struct v4l2_ctrl *mute;
+ struct v4l2_ctrl *balance;
+ };
u32 clock_freq;
- u8 muted;
- u16 volume;
- u16 balance;
- u8 vol_l; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
- u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
};
static inline struct wm8739_state *to_state(struct v4l2_subdev *sd)
return container_of(sd, struct wm8739_state, sd);
}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct wm8739_state, hdl)->sd;
+}
+
/* ------------------------------------------------------------------------ */
static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val)
return -1;
}
-/* write regs to set audio volume etc */
-static void wm8739_set_audio(struct v4l2_subdev *sd)
-{
- struct wm8739_state *state = to_state(sd);
- u16 mute = state->muted ? 0x80 : 0;
-
- /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB
- * Default setting: 0x17 = 0 dB
- */
- wm8739_write(sd, R0, (state->vol_l & 0x1f) | mute);
- wm8739_write(sd, R1, (state->vol_r & 0x1f) | mute);
-}
-
-static int wm8739_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct wm8739_state *state = to_state(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = state->muted;
- break;
-
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = state->volume;
- break;
-
- case V4L2_CID_AUDIO_BALANCE:
- ctrl->value = state->balance;
- break;
-
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int wm8739_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int wm8739_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd = to_sd(ctrl);
struct wm8739_state *state = to_state(sd);
unsigned int work_l, work_r;
+ u8 vol_l; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
+ u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
+ u16 mute;
switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- state->muted = ctrl->value;
- break;
-
case V4L2_CID_AUDIO_VOLUME:
- state->volume = ctrl->value;
- break;
-
- case V4L2_CID_AUDIO_BALANCE:
- state->balance = ctrl->value;
break;
default:
}
/* normalize ( 65535 to 0 -> 31 to 0 (12dB to -34.5dB) ) */
- work_l = (min(65536 - state->balance, 32768) * state->volume) / 32768;
- work_r = (min(state->balance, (u16)32768) * state->volume) / 32768;
+ work_l = (min(65536 - state->balance->val, 32768) * state->volume->val) / 32768;
+ work_r = (min(state->balance->val, 32768) * state->volume->val) / 32768;
- state->vol_l = (long)work_l * 31 / 65535;
- state->vol_r = (long)work_r * 31 / 65535;
+ vol_l = (long)work_l * 31 / 65535;
+ vol_r = (long)work_r * 31 / 65535;
/* set audio volume etc. */
- wm8739_set_audio(sd);
+ mute = state->mute->val ? 0x80 : 0;
+
+ /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB
+ * Default setting: 0x17 = 0 dB
+ */
+ wm8739_write(sd, R0, (vol_l & 0x1f) | mute);
+ wm8739_write(sd, R1, (vol_r & 0x1f) | mute);
return 0;
}
/* ------------------------------------------------------------------------ */
-static struct v4l2_queryctrl wm8739_qctrl[] = {
- {
- .id = V4L2_CID_AUDIO_VOLUME,
- .name = "Volume",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535/100,
- .default_value = 58880,
- .flags = 0,
- .type = V4L2_CTRL_TYPE_INTEGER,
- }, {
- .id = V4L2_CID_AUDIO_MUTE,
- .name = "Mute",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- .flags = 0,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- }, {
- .id = V4L2_CID_AUDIO_BALANCE,
- .name = "Balance",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535/100,
- .default_value = 32768,
- .flags = 0,
- .type = V4L2_CTRL_TYPE_INTEGER,
- }
-};
-
-/* ------------------------------------------------------------------------ */
-
static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq)
{
struct wm8739_state *state = to_state(sd);
return 0;
}
-static int wm8739_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++)
- if (qc->id && qc->id == wm8739_qctrl[i].id) {
- memcpy(qc, &wm8739_qctrl[i], sizeof(*qc));
- return 0;
- }
- return -EINVAL;
-}
-
static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct wm8739_state *state = to_state(sd);
v4l2_info(sd, "Frequency: %u Hz\n", state->clock_freq);
- v4l2_info(sd, "Volume L: %02x%s\n", state->vol_l & 0x1f,
- state->muted ? " (muted)" : "");
- v4l2_info(sd, "Volume R: %02x%s\n", state->vol_r & 0x1f,
- state->muted ? " (muted)" : "");
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
return 0;
}
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops wm8739_ctrl_ops = {
+ .s_ctrl = wm8739_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops wm8739_core_ops = {
.log_status = wm8739_log_status,
.g_chip_ident = wm8739_g_chip_ident,
- .queryctrl = wm8739_queryctrl,
- .g_ctrl = wm8739_g_ctrl,
- .s_ctrl = wm8739_s_ctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
};
static const struct v4l2_subdev_audio_ops wm8739_audio_ops = {
v4l_info(client, "chip found @ 0x%x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kmalloc(sizeof(struct wm8739_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct wm8739_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &wm8739_ops);
- state->vol_l = 0x17; /* 0dB */
- state->vol_r = 0x17; /* 0dB */
- state->muted = 0;
- state->balance = 32768;
- /* normalize (12dB(31) to -34.5dB(0) [0dB(23)] -> 65535 to 0) */
- state->volume = ((long)state->vol_l + 1) * 65535 / 31;
+ v4l2_ctrl_handler_init(&state->hdl, 2);
+ state->volume = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 50736);
+ state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ state->balance = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops,
+ V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768);
+ sd->ctrl_handler = &state->hdl;
+ if (state->hdl.error) {
+ int err = state->hdl.error;
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return err;
+ }
+ v4l2_ctrl_cluster(3, &state->volume);
+
state->clock_freq = 48000;
/* Initialize wm8739 */
/* activate */
wm8739_write(sd, R9, 0x001);
/* set volume/mute */
- wm8739_set_audio(sd);
+ v4l2_ctrl_handler_setup(&state->hdl);
return 0;
}
static int wm8739_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct wm8739_state *state = to_state(sd);
v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&state->hdl);
kfree(to_state(sd));
return 0;
}
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-i2c-drv.h>
MODULE_DESCRIPTION("wm8775 driver");
struct wm8775_state {
struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *mute;
u8 input; /* Last selected input (0-0xf) */
- u8 muted;
};
static inline struct wm8775_state *to_state(struct v4l2_subdev *sd)
return container_of(sd, struct wm8775_state, sd);
}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct wm8775_state, hdl)->sd;
+}
+
static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
return -EINVAL;
}
state->input = input;
- if (state->muted)
+ if (!v4l2_ctrl_g_ctrl(state->mute))
return 0;
wm8775_write(sd, R21, 0x0c0);
wm8775_write(sd, R14, 0x1d4);
return 0;
}
-static int wm8775_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ struct v4l2_subdev *sd = to_sd(ctrl);
struct wm8775_state *state = to_state(sd);
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- ctrl->value = state->muted;
- return 0;
-}
-
-static int wm8775_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct wm8775_state *state = to_state(sd);
-
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- state->muted = ctrl->value;
- wm8775_write(sd, R21, 0x0c0);
- wm8775_write(sd, R14, 0x1d4);
- wm8775_write(sd, R15, 0x1d4);
- if (!state->muted)
- wm8775_write(sd, R21, 0x100 + state->input);
- return 0;
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ wm8775_write(sd, R21, 0x0c0);
+ wm8775_write(sd, R14, 0x1d4);
+ wm8775_write(sd, R15, 0x1d4);
+ if (!ctrl->val)
+ wm8775_write(sd, R21, 0x100 + state->input);
+ return 0;
+ }
+ return -EINVAL;
}
static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
{
struct wm8775_state *state = to_state(sd);
- v4l2_info(sd, "Input: %d%s\n", state->input,
- state->muted ? " (muted)" : "");
+ v4l2_info(sd, "Input: %d\n", state->input);
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
return 0;
}
/* ----------------------------------------------------------------------- */
+static const struct v4l2_ctrl_ops wm8775_ctrl_ops = {
+ .s_ctrl = wm8775_s_ctrl,
+};
+
static const struct v4l2_subdev_core_ops wm8775_core_ops = {
.log_status = wm8775_log_status,
.g_chip_ident = wm8775_g_chip_ident,
- .g_ctrl = wm8775_g_ctrl,
- .s_ctrl = wm8775_s_ctrl,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
};
static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = {
v4l_info(client, "chip found @ 0x%02x (%s)\n",
client->addr << 1, client->adapter->name);
- state = kmalloc(sizeof(struct wm8775_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct wm8775_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &wm8775_ops);
state->input = 2;
- state->muted = 0;
+
+ v4l2_ctrl_handler_init(&state->hdl, 1);
+ state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ sd->ctrl_handler = &state->hdl;
+ if (state->hdl.error) {
+ int err = state->hdl.error;
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return err;
+ }
/* Initialize wm8775 */
static int wm8775_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct wm8775_state *state = to_state(sd);
v4l2_device_unregister_subdev(sd);
- kfree(to_state(sd));
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
return 0;
}
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/smp_lock.h>
#include <linux/memstick.h>
#define DRIVER_NAME "mspro_block"
struct mspro_block_data *msb = disk->private_data;
int rc = -ENXIO;
+ lock_kernel();
mutex_lock(&mspro_block_disk_lock);
if (msb && msb->card) {
}
mutex_unlock(&mspro_block_disk_lock);
+ unlock_kernel();
return rc;
}
static int mspro_block_bd_release(struct gendisk *disk, fmode_t mode)
{
- return mspro_block_disk_release(disk);
+ int ret;
+ lock_kernel();
+ ret = mspro_block_disk_release(disk);
+ unlock_kernel();
+ return ret;
}
static int mspro_block_bd_getgeo(struct block_device *bdev,
static int mspro_block_prepare_req(struct request_queue *q, struct request *req)
{
- if (!blk_fs_request(req) && !blk_pc_request(req)) {
+ if (req->cmd_type != REQ_TYPE_FS &&
+ req->cmd_type != REQ_TYPE_BLOCK_PC) {
blk_dump_rq_flags(req, "MSPro unsupported request");
return BLKPREP_KILL;
}
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/init.h>
+#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/pci.h>
static int mpt_host_page_alloc(MPT_ADAPTER *ioc, pIOCInit_t ioc_init);
#ifdef CONFIG_PROC_FS
-static int procmpt_summary_read(char *buf, char **start, off_t offset,
- int request, int *eof, void *data);
-static int procmpt_version_read(char *buf, char **start, off_t offset,
- int request, int *eof, void *data);
-static int procmpt_iocinfo_read(char *buf, char **start, off_t offset,
- int request, int *eof, void *data);
+static const struct file_operations mpt_summary_proc_fops;
+static const struct file_operations mpt_version_proc_fops;
+static const struct file_operations mpt_iocinfo_proc_fops;
#endif
static void mpt_get_fw_exp_ver(char *buf, MPT_ADAPTER *ioc);
u8 pcixcmd;
static int mpt_ids = 0;
#ifdef CONFIG_PROC_FS
- struct proc_dir_entry *dent, *ent;
+ struct proc_dir_entry *dent;
#endif
ioc = kzalloc(sizeof(MPT_ADAPTER), GFP_ATOMIC);
*/
dent = proc_mkdir(ioc->name, mpt_proc_root_dir);
if (dent) {
- ent = create_proc_entry("info", S_IFREG|S_IRUGO, dent);
- if (ent) {
- ent->read_proc = procmpt_iocinfo_read;
- ent->data = ioc;
- }
- ent = create_proc_entry("summary", S_IFREG|S_IRUGO, dent);
- if (ent) {
- ent->read_proc = procmpt_summary_read;
- ent->data = ioc;
- }
+ proc_create_data("info", S_IRUGO, dent, &mpt_iocinfo_proc_fops, ioc);
+ proc_create_data("summary", S_IRUGO, dent, &mpt_summary_proc_fops, ioc);
}
#endif
static int
procmpt_create(void)
{
- struct proc_dir_entry *ent;
-
mpt_proc_root_dir = proc_mkdir(MPT_PROCFS_MPTBASEDIR, NULL);
if (mpt_proc_root_dir == NULL)
return -ENOTDIR;
- ent = create_proc_entry("summary", S_IFREG|S_IRUGO, mpt_proc_root_dir);
- if (ent)
- ent->read_proc = procmpt_summary_read;
-
- ent = create_proc_entry("version", S_IFREG|S_IRUGO, mpt_proc_root_dir);
- if (ent)
- ent->read_proc = procmpt_version_read;
-
+ proc_create("summary", S_IRUGO, mpt_proc_root_dir, &mpt_summary_proc_fops);
+ proc_create("version", S_IRUGO, mpt_proc_root_dir, &mpt_version_proc_fops);
return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
- * procmpt_summary_read - Handle read request of a summary file
- * @buf: Pointer to area to write information
- * @start: Pointer to start pointer
- * @offset: Offset to start writing
- * @request: Amount of read data requested
- * @eof: Pointer to EOF integer
- * @data: Pointer
- *
* Handles read request from /proc/mpt/summary or /proc/mpt/iocN/summary.
- * Returns number of characters written to process performing the read.
*/
-static int
-procmpt_summary_read(char *buf, char **start, off_t offset, int request, int *eof, void *data)
-{
- MPT_ADAPTER *ioc;
- char *out = buf;
- int len;
+static void seq_mpt_print_ioc_summary(MPT_ADAPTER *ioc, struct seq_file *m, int showlan);
- if (data) {
- int more = 0;
-
- ioc = data;
- mpt_print_ioc_summary(ioc, out, &more, 0, 1);
+static int mpt_summary_proc_show(struct seq_file *m, void *v)
+{
+ MPT_ADAPTER *ioc = m->private;
- out += more;
+ if (ioc) {
+ seq_mpt_print_ioc_summary(ioc, m, 1);
} else {
list_for_each_entry(ioc, &ioc_list, list) {
- int more = 0;
-
- mpt_print_ioc_summary(ioc, out, &more, 0, 1);
-
- out += more;
- if ((out-buf) >= request)
- break;
+ seq_mpt_print_ioc_summary(ioc, m, 1);
}
}
- len = out - buf;
+ return 0;
+}
- MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len);
+static int mpt_summary_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mpt_summary_proc_show, PDE(inode)->data);
}
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * procmpt_version_read - Handle read request from /proc/mpt/version.
- * @buf: Pointer to area to write information
- * @start: Pointer to start pointer
- * @offset: Offset to start writing
- * @request: Amount of read data requested
- * @eof: Pointer to EOF integer
- * @data: Pointer
- *
- * Returns number of characters written to process performing the read.
- */
-static int
-procmpt_version_read(char *buf, char **start, off_t offset, int request, int *eof, void *data)
+static const struct file_operations mpt_summary_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = mpt_summary_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int mpt_version_proc_show(struct seq_file *m, void *v)
{
u8 cb_idx;
int scsi, fc, sas, lan, ctl, targ, dmp;
char *drvname;
- int len;
- len = sprintf(buf, "%s-%s\n", "mptlinux", MPT_LINUX_VERSION_COMMON);
- len += sprintf(buf+len, " Fusion MPT base driver\n");
+ seq_printf(m, "%s-%s\n", "mptlinux", MPT_LINUX_VERSION_COMMON);
+ seq_printf(m, " Fusion MPT base driver\n");
scsi = fc = sas = lan = ctl = targ = dmp = 0;
for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) {
}
if (drvname)
- len += sprintf(buf+len, " Fusion MPT %s driver\n", drvname);
+ seq_printf(m, " Fusion MPT %s driver\n", drvname);
}
}
- MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len);
+ return 0;
}
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * procmpt_iocinfo_read - Handle read request from /proc/mpt/iocN/info.
- * @buf: Pointer to area to write information
- * @start: Pointer to start pointer
- * @offset: Offset to start writing
- * @request: Amount of read data requested
- * @eof: Pointer to EOF integer
- * @data: Pointer
- *
- * Returns number of characters written to process performing the read.
- */
-static int
-procmpt_iocinfo_read(char *buf, char **start, off_t offset, int request, int *eof, void *data)
+static int mpt_version_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mpt_version_proc_show, NULL);
+}
+
+static const struct file_operations mpt_version_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = mpt_version_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int mpt_iocinfo_proc_show(struct seq_file *m, void *v)
{
- MPT_ADAPTER *ioc = data;
- int len;
+ MPT_ADAPTER *ioc = m->private;
char expVer[32];
int sz;
int p;
mpt_get_fw_exp_ver(expVer, ioc);
- len = sprintf(buf, "%s:", ioc->name);
+ seq_printf(m, "%s:", ioc->name);
if (ioc->facts.Flags & MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT)
- len += sprintf(buf+len, " (f/w download boot flag set)");
+ seq_printf(m, " (f/w download boot flag set)");
// if (ioc->facts.IOCExceptions & MPI_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL)
-// len += sprintf(buf+len, " CONFIG_CHECKSUM_FAIL!");
+// seq_printf(m, " CONFIG_CHECKSUM_FAIL!");
- len += sprintf(buf+len, "\n ProductID = 0x%04x (%s)\n",
+ seq_printf(m, "\n ProductID = 0x%04x (%s)\n",
ioc->facts.ProductID,
ioc->prod_name);
- len += sprintf(buf+len, " FWVersion = 0x%08x%s", ioc->facts.FWVersion.Word, expVer);
+ seq_printf(m, " FWVersion = 0x%08x%s", ioc->facts.FWVersion.Word, expVer);
if (ioc->facts.FWImageSize)
- len += sprintf(buf+len, " (fw_size=%d)", ioc->facts.FWImageSize);
- len += sprintf(buf+len, "\n MsgVersion = 0x%04x\n", ioc->facts.MsgVersion);
- len += sprintf(buf+len, " FirstWhoInit = 0x%02x\n", ioc->FirstWhoInit);
- len += sprintf(buf+len, " EventState = 0x%02x\n", ioc->facts.EventState);
+ seq_printf(m, " (fw_size=%d)", ioc->facts.FWImageSize);
+ seq_printf(m, "\n MsgVersion = 0x%04x\n", ioc->facts.MsgVersion);
+ seq_printf(m, " FirstWhoInit = 0x%02x\n", ioc->FirstWhoInit);
+ seq_printf(m, " EventState = 0x%02x\n", ioc->facts.EventState);
- len += sprintf(buf+len, " CurrentHostMfaHighAddr = 0x%08x\n",
+ seq_printf(m, " CurrentHostMfaHighAddr = 0x%08x\n",
ioc->facts.CurrentHostMfaHighAddr);
- len += sprintf(buf+len, " CurrentSenseBufferHighAddr = 0x%08x\n",
+ seq_printf(m, " CurrentSenseBufferHighAddr = 0x%08x\n",
ioc->facts.CurrentSenseBufferHighAddr);
- len += sprintf(buf+len, " MaxChainDepth = 0x%02x frames\n", ioc->facts.MaxChainDepth);
- len += sprintf(buf+len, " MinBlockSize = 0x%02x bytes\n", 4*ioc->facts.BlockSize);
+ seq_printf(m, " MaxChainDepth = 0x%02x frames\n", ioc->facts.MaxChainDepth);
+ seq_printf(m, " MinBlockSize = 0x%02x bytes\n", 4*ioc->facts.BlockSize);
- len += sprintf(buf+len, " RequestFrames @ 0x%p (Dma @ 0x%p)\n",
+ seq_printf(m, " RequestFrames @ 0x%p (Dma @ 0x%p)\n",
(void *)ioc->req_frames, (void *)(ulong)ioc->req_frames_dma);
/*
* Rounding UP to nearest 4-kB boundary here...
*/
sz = (ioc->req_sz * ioc->req_depth) + 128;
sz = ((sz + 0x1000UL - 1UL) / 0x1000) * 0x1000;
- len += sprintf(buf+len, " {CurReqSz=%d} x {CurReqDepth=%d} = %d bytes ^= 0x%x\n",
+ seq_printf(m, " {CurReqSz=%d} x {CurReqDepth=%d} = %d bytes ^= 0x%x\n",
ioc->req_sz, ioc->req_depth, ioc->req_sz*ioc->req_depth, sz);
- len += sprintf(buf+len, " {MaxReqSz=%d} {MaxReqDepth=%d}\n",
+ seq_printf(m, " {MaxReqSz=%d} {MaxReqDepth=%d}\n",
4*ioc->facts.RequestFrameSize,
ioc->facts.GlobalCredits);
- len += sprintf(buf+len, " Frames @ 0x%p (Dma @ 0x%p)\n",
+ seq_printf(m, " Frames @ 0x%p (Dma @ 0x%p)\n",
(void *)ioc->alloc, (void *)(ulong)ioc->alloc_dma);
sz = (ioc->reply_sz * ioc->reply_depth) + 128;
- len += sprintf(buf+len, " {CurRepSz=%d} x {CurRepDepth=%d} = %d bytes ^= 0x%x\n",
+ seq_printf(m, " {CurRepSz=%d} x {CurRepDepth=%d} = %d bytes ^= 0x%x\n",
ioc->reply_sz, ioc->reply_depth, ioc->reply_sz*ioc->reply_depth, sz);
- len += sprintf(buf+len, " {MaxRepSz=%d} {MaxRepDepth=%d}\n",
+ seq_printf(m, " {MaxRepSz=%d} {MaxRepDepth=%d}\n",
ioc->facts.CurReplyFrameSize,
ioc->facts.ReplyQueueDepth);
- len += sprintf(buf+len, " MaxDevices = %d\n",
+ seq_printf(m, " MaxDevices = %d\n",
(ioc->facts.MaxDevices==0) ? 255 : ioc->facts.MaxDevices);
- len += sprintf(buf+len, " MaxBuses = %d\n", ioc->facts.MaxBuses);
+ seq_printf(m, " MaxBuses = %d\n", ioc->facts.MaxBuses);
/* per-port info */
for (p=0; p < ioc->facts.NumberOfPorts; p++) {
- len += sprintf(buf+len, " PortNumber = %d (of %d)\n",
+ seq_printf(m, " PortNumber = %d (of %d)\n",
p+1,
ioc->facts.NumberOfPorts);
if (ioc->bus_type == FC) {
if (ioc->pfacts[p].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN) {
u8 *a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow;
- len += sprintf(buf+len, " LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
+ seq_printf(m, " LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
a[5], a[4], a[3], a[2], a[1], a[0]);
}
- len += sprintf(buf+len, " WWN = %08X%08X:%08X%08X\n",
+ seq_printf(m, " WWN = %08X%08X:%08X%08X\n",
ioc->fc_port_page0[p].WWNN.High,
ioc->fc_port_page0[p].WWNN.Low,
ioc->fc_port_page0[p].WWPN.High,
}
}
- MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len);
+ return 0;
}
+static int mpt_iocinfo_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mpt_iocinfo_proc_show, PDE(inode)->data);
+}
+
+static const struct file_operations mpt_iocinfo_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = mpt_iocinfo_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
#endif /* CONFIG_PROC_FS } */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
*size = y;
}
+
+static void seq_mpt_print_ioc_summary(MPT_ADAPTER *ioc, struct seq_file *m, int showlan)
+{
+ char expVer[32];
+
+ mpt_get_fw_exp_ver(expVer, ioc);
+
+ /*
+ * Shorter summary of attached ioc's...
+ */
+ seq_printf(m, "%s: %s, %s%08xh%s, Ports=%d, MaxQ=%d",
+ ioc->name,
+ ioc->prod_name,
+ MPT_FW_REV_MAGIC_ID_STRING, /* "FwRev=" or somesuch */
+ ioc->facts.FWVersion.Word,
+ expVer,
+ ioc->facts.NumberOfPorts,
+ ioc->req_depth);
+
+ if (showlan && (ioc->pfacts[0].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN)) {
+ u8 *a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow;
+ seq_printf(m, ", LanAddr=%02X:%02X:%02X:%02X:%02X:%02X",
+ a[5], a[4], a[3], a[2], a[1], a[0]);
+ }
+
+ seq_printf(m, ", IRQ=%d", ioc->pci_irq);
+
+ if (!ioc->active)
+ seq_printf(m, " (disabled)");
+
+ seq_putc(m, '\n');
+}
+
/**
* mpt_set_taskmgmt_in_progress_flag - set flags associated with task management
* @ioc: Pointer to MPT_ADAPTER structure
* mpt_SoftResetHandler - Issues a less expensive reset
* @ioc: Pointer to MPT_ADAPTER structure
* @sleepFlag: Indicates if sleep or schedule must be called.
-
*
* Returns 0 for SUCCESS or -1 if FAILED.
*
* mpt_Soft_Hard_ResetHandler - Try less expensive reset
* @ioc: Pointer to MPT_ADAPTER structure
* @sleepFlag: Indicates if sleep or schedule must be called.
-
*
* Returns 0 for SUCCESS or -1 if FAILED.
* Try for softreset first, only if it fails go for expensive
#define MPT_TARGET_FLAGS_RAID_COMPONENT 0x40
#define MPT_TARGET_FLAGS_LED_ON 0x80
-/*
- * /proc/mpt interface
- */
-typedef struct {
- const char *name;
- mode_t mode;
- int pad;
- read_proc_t *read_proc;
- write_proc_t *write_proc;
-} mpt_proc_entry_t;
-
-#define MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len) \
-do { \
- len -= offset; \
- if (len < request) { \
- *eof = 1; \
- if (len <= 0) \
- return 0; \
- } else \
- len = request; \
- *start = buf + offset; \
- return len; \
-} while (0)
-
-
/*
* IOCTL structure and associated defines
*/
dev = &c->pdev->dev;
if (i2o_dma_realloc(dev, &c->dlct,
- le32_to_cpu(sb->expected_lct_size)))
+ le32_to_cpu(sb->expected_lct_size))) {
+ mutex_unlock(&c->lct_lock);
return -ENOMEM;
+ }
msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
- if (IS_ERR(msg))
+ if (IS_ERR(msg)) {
+ mutex_unlock(&c->lct_lock);
return PTR_ERR(msg);
+ }
msg->u.head[0] = cpu_to_le32(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_6);
msg->u.head[1] = cpu_to_le32(I2O_CMD_LCT_NOTIFY << 24 | HOST_TID << 12 |
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2o.h>
+#include <linux/smp_lock.h>
#include <linux/mempool.h>
if (!dev->i2o_dev)
return -ENODEV;
+ lock_kernel();
if (dev->power > 0x1f)
i2o_block_device_power(dev, 0x02);
i2o_block_device_lock(dev->i2o_dev, -1);
osm_debug("Ready.\n");
+ unlock_kernel();
return 0;
};
if (!dev->i2o_dev)
return 0;
+ lock_kernel();
i2o_block_device_flush(dev->i2o_dev);
i2o_block_device_unlock(dev->i2o_dev, -1);
operation = 0x24;
i2o_block_device_power(dev, operation);
+ unlock_kernel();
return 0;
}
{
struct gendisk *disk = bdev->bd_disk;
struct i2o_block_device *dev = disk->private_data;
+ int ret = -ENOTTY;
/* Anyone capable of this syscall can do *real bad* things */
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
+ lock_kernel();
switch (cmd) {
case BLKI2OGRSTRAT:
- return put_user(dev->rcache, (int __user *)arg);
+ ret = put_user(dev->rcache, (int __user *)arg);
+ break;
case BLKI2OGWSTRAT:
- return put_user(dev->wcache, (int __user *)arg);
+ ret = put_user(dev->wcache, (int __user *)arg);
+ break;
case BLKI2OSRSTRAT:
+ ret = -EINVAL;
if (arg < 0 || arg > CACHE_SMARTFETCH)
- return -EINVAL;
+ break;
dev->rcache = arg;
+ ret = 0;
break;
case BLKI2OSWSTRAT:
+ ret = -EINVAL;
if (arg != 0
&& (arg < CACHE_WRITETHROUGH || arg > CACHE_SMARTBACK))
- return -EINVAL;
+ break;
dev->wcache = arg;
+ ret = 0;
break;
}
- return -ENOTTY;
+ unlock_kernel();
+
+ return ret;
};
/**
{
struct i2o_block_device *dev = req->rq_disk->private_data;
struct i2o_controller *c;
- u32 tid = dev->i2o_dev->lct_data.tid;
+ u32 tid;
struct i2o_message *msg;
u32 *mptr;
struct i2o_block_request *ireq = req->special;
goto exit;
}
+ tid = dev->i2o_dev->lct_data.tid;
c = dev->i2o_dev->iop;
msg = i2o_msg_get(c);
if (!req)
break;
- if (blk_fs_request(req)) {
+ if (req->cmd_type == REQ_TYPE_FS) {
struct i2o_block_delayed_request *dreq;
struct i2o_block_request *ireq = req->special;
unsigned int queue_depth;
.owner = THIS_MODULE,
.open = i2o_block_open,
.release = i2o_block_release,
- .locked_ioctl = i2o_block_ioctl,
+ .ioctl = i2o_block_ioctl,
+ .compat_ioctl = i2o_block_ioctl,
.getgeo = i2o_block_getgeo,
.media_changed = i2o_block_media_changed
};
len = 8 + ((hrt->entry_len * hrt->num_entries) << 2);
- /* We did a get user...so assuming mem is ok...is this bad? */
- put_user(len, kcmd.reslen);
- if (len > reslen)
+ if (put_user(len, kcmd.reslen))
+ ret = -EFAULT;
+ else if (len > reslen)
ret = -ENOBUFS;
- if (copy_to_user(kcmd.resbuf, (void *)hrt, len))
+ else if (copy_to_user(kcmd.resbuf, (void *)hrt, len))
ret = -EFAULT;
return ret;
lct = (i2o_lct *) c->lct;
len = (unsigned int)lct->table_size << 2;
- put_user(len, kcmd.reslen);
- if (len > reslen)
+ if (put_user(len, kcmd.reslen))
+ ret = -EFAULT;
+ else if (len > reslen)
ret = -ENOBUFS;
else if (copy_to_user(kcmd.resbuf, lct, len))
ret = -EFAULT;
return -EAGAIN;
}
- put_user(len, kcmd.reslen);
- if (len > reslen)
+ if (put_user(len, kcmd.reslen))
+ ret = -EFAULT;
+ else if (len > reslen)
ret = -ENOBUFS;
else if (copy_to_user(kcmd.resbuf, res, len))
ret = -EFAULT;
* Do the incoming paperwork
*/
i2o_dev = SCpnt->device->hostdata;
- c = i2o_dev->iop;
SCpnt->scsi_done = done;
done(SCpnt);
goto exit;
}
-
+ c = i2o_dev->iop;
tid = i2o_dev->lct_data.tid;
osm_debug("qcmd: Tid = %03x\n", tid);
*/
void cs5535_mfgpt_free_timer(struct cs5535_mfgpt_timer *timer)
{
+ unsigned long flags;
+ uint16_t val;
+
+ /* timer can be made available again only if never set up */
+ val = cs5535_mfgpt_read(timer, MFGPT_REG_SETUP);
+ if (!(val & MFGPT_SETUP_SETUP)) {
+ spin_lock_irqsave(&timer->chip->lock, flags);
+ __set_bit(timer->nr, timer->chip->avail);
+ spin_unlock_irqrestore(&timer->chip->lock, flags);
+ }
+
kfree(timer);
}
EXPORT_SYMBOL_GPL(cs5535_mfgpt_free_timer);
#include <linux/kdev_t.h>
#include <linux/blkdev.h>
#include <linux/mutex.h>
+#include <linux/smp_lock.h>
#include <linux/scatterlist.h>
#include <linux/string_helpers.h>
struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk);
int ret = -ENXIO;
+ lock_kernel();
if (md) {
if (md->usage == 2)
check_disk_change(bdev);
ret = -EROFS;
}
}
+ unlock_kernel();
return ret;
}
{
struct mmc_blk_data *md = disk->private_data;
+ lock_kernel();
mmc_blk_put(md);
+ unlock_kernel();
return 0;
}
/*
* We only like normal block requests.
*/
- if (!blk_fs_request(req)) {
+ if (req->cmd_type != REQ_TYPE_FS) {
blk_dump_rq_flags(req, "MMC bad request");
return BLKPREP_KILL;
}
mq->req = NULL;
blk_queue_prep_rq(mq->queue, mmc_prep_request);
- blk_queue_ordered(mq->queue, QUEUE_ORDERED_DRAIN, NULL);
+ blk_queue_ordered(mq->queue, QUEUE_ORDERED_DRAIN);
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
#ifdef CONFIG_MMC_BLOCK_BOUNCE
return sprintf(buf, "SD\n");
case MMC_TYPE_SDIO:
return sprintf(buf, "SDIO\n");
+ case MMC_TYPE_SD_COMBO:
+ return sprintf(buf, "SDcombo\n");
default:
return -EFAULT;
}
case MMC_TYPE_SDIO:
type = "SDIO";
break;
+ case MMC_TYPE_SD_COMBO:
+ type = "SDcombo";
+ break;
default:
type = NULL;
}
case MMC_TYPE_SDIO:
type = "SDIO";
break;
+ case MMC_TYPE_SD_COMBO:
+ type = "SD-combo";
+ if (mmc_card_blockaddr(card))
+ type = "SDHC-combo";
default:
type = "?";
break;
container_of(work, struct mmc_host, detect.work);
u32 ocr;
int err;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (host->rescan_disable) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ return;
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
mmc_bus_get(host);
*/
err = mmc_send_io_op_cond(host, 0, &ocr);
if (!err) {
- if (mmc_attach_sdio(host, ocr))
- mmc_power_off(host);
+ if (mmc_attach_sdio(host, ocr)) {
+ mmc_claim_host(host);
+ /* try SDMEM (but not MMC) even if SDIO is broken */
+ if (mmc_send_app_op_cond(host, 0, &ocr))
+ goto out_fail;
+
+ if (mmc_attach_sd(host, ocr))
+ mmc_power_off(host);
+ }
goto out;
}
goto out;
}
+out_fail:
mmc_release_host(host);
mmc_power_off(host);
if (host->bus_ops && !host->bus_dead) {
if (host->bus_ops->suspend)
err = host->bus_ops->suspend(host);
- if (err == -ENOSYS || !host->bus_ops->resume) {
- /*
- * We simply "remove" the card in this case.
- * It will be redetected on resume.
- */
- if (host->bus_ops->remove)
- host->bus_ops->remove(host);
- mmc_claim_host(host);
- mmc_detach_bus(host);
- mmc_release_host(host);
- host->pm_flags = 0;
- err = 0;
- }
}
mmc_bus_put(host);
printk(KERN_WARNING "%s: error %d during resume "
"(card was removed?)\n",
mmc_hostname(host), err);
- if (host->bus_ops->remove)
- host->bus_ops->remove(host);
- mmc_claim_host(host);
- mmc_detach_bus(host);
- mmc_release_host(host);
- /* no need to bother upper layers */
err = 0;
}
}
mmc_bus_put(host);
- /*
- * We add a slight delay here so that resume can progress
- * in parallel.
- */
- mmc_detect_change(host, 1);
-
return err;
}
-
EXPORT_SYMBOL(mmc_resume_host);
+/* Do the card removal on suspend if card is assumed removeable
+ * Do that in pm notifier while userspace isn't yet frozen, so we will be able
+ to sync the card.
+*/
+int mmc_pm_notify(struct notifier_block *notify_block,
+ unsigned long mode, void *unused)
+{
+ struct mmc_host *host = container_of(
+ notify_block, struct mmc_host, pm_notify);
+ unsigned long flags;
+
+
+ switch (mode) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->rescan_disable = 1;
+ spin_unlock_irqrestore(&host->lock, flags);
+ cancel_delayed_work_sync(&host->detect);
+
+ if (!host->bus_ops || host->bus_ops->suspend)
+ break;
+
+ mmc_claim_host(host);
+
+ if (host->bus_ops->remove)
+ host->bus_ops->remove(host);
+
+ mmc_detach_bus(host);
+ mmc_release_host(host);
+ host->pm_flags = 0;
+ break;
+
+ case PM_POST_SUSPEND:
+ case PM_POST_HIBERNATION:
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->rescan_disable = 0;
+ spin_unlock_irqrestore(&host->lock, flags);
+ mmc_detect_change(host, 0);
+
+ }
+
+ return 0;
+}
#endif
static int __init mmc_init(void)
#include <linux/pagemap.h>
#include <linux/leds.h>
#include <linux/slab.h>
+#include <linux/suspend.h>
#include <linux/mmc/host.h>
init_waitqueue_head(&host->wq);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable);
+ host->pm_notify.notifier_call = mmc_pm_notify;
/*
* By default, hosts do not support SGIO or large requests.
#endif
mmc_start_host(host);
+ register_pm_notifier(&host->pm_notify);
return 0;
}
*/
void mmc_remove_host(struct mmc_host *host)
{
+ unregister_pm_notifier(&host->pm_notify);
mmc_stop_host(host);
#ifdef CONFIG_DEBUG_FS
static int mmc_decode_csd(struct mmc_card *card)
{
struct mmc_csd *csd = &card->csd;
- unsigned int e, m, csd_struct;
+ unsigned int e, m;
u32 *resp = card->raw_csd;
/*
* We only understand CSD structure v1.1 and v1.2.
* v1.2 has extra information in bits 15, 11 and 10.
+ * We also support eMMC v4.4 & v4.41.
*/
- csd_struct = UNSTUFF_BITS(resp, 126, 2);
- if (csd_struct != 1 && csd_struct != 2) {
+ csd->structure = UNSTUFF_BITS(resp, 126, 2);
+ if (csd->structure == 0) {
printk(KERN_ERR "%s: unrecognised CSD structure version %d\n",
- mmc_hostname(card->host), csd_struct);
+ mmc_hostname(card->host), csd->structure);
return -EINVAL;
}
goto out;
}
+ /* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */
+ if (card->csd.structure == 3) {
+ int ext_csd_struct = ext_csd[EXT_CSD_STRUCTURE];
+ if (ext_csd_struct > 2) {
+ printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
+ "version %d\n", mmc_hostname(card->host),
+ ext_csd_struct);
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
if (card->ext_csd.rev > 5) {
- printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
- "version %d\n", mmc_hostname(card->host),
- card->ext_csd.rev);
+ printk(KERN_ERR "%s: unrecognised EXT_CSD revision %d\n",
+ mmc_hostname(card->host), card->ext_csd.rev);
err = -EINVAL;
goto out;
}
ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
ext_csd[EXT_CSD_SEC_CNT + 2] << 16 |
ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
- if (card->ext_csd.sectors)
+
+ /* Cards with density > 2GiB are sector addressed */
+ if (card->ext_csd.sectors > (2u * 1024 * 1024 * 1024) / 512)
mmc_card_set_blockaddr(card);
}
/*
* Given the decoded CSD structure, decode the raw CID to our CID structure.
*/
-static void mmc_decode_cid(struct mmc_card *card)
+void mmc_decode_cid(struct mmc_card *card)
{
u32 *resp = card->raw_cid;
/*
* Test if the card supports high-speed mode and, if so, switch to it.
*/
-static int mmc_switch_hs(struct mmc_card *card)
+int mmc_sd_switch_hs(struct mmc_card *card)
{
int err;
u8 *status;
printk(KERN_WARNING "%s: Problem switching card "
"into high-speed mode!\n",
mmc_hostname(card->host));
+ err = 0;
} else {
- mmc_card_set_highspeed(card);
- mmc_set_timing(card->host, MMC_TIMING_SD_HS);
+ err = 1;
}
out:
NULL,
};
-static struct device_type sd_type = {
+struct device_type sd_type = {
.groups = sd_attr_groups,
};
/*
- * Handle the detection and initialisation of a card.
- *
- * In the case of a resume, "oldcard" will contain the card
- * we're trying to reinitialise.
+ * Fetch CID from card.
*/
-static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
- struct mmc_card *oldcard)
+int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
{
- struct mmc_card *card;
int err;
- u32 cid[4];
- unsigned int max_dtr;
-
- BUG_ON(!host);
- WARN_ON(!host->claimed);
/*
* Since we're changing the OCR value, we seem to
err = mmc_send_app_op_cond(host, ocr, NULL);
if (err)
- goto err;
+ return err;
- /*
- * Fetch CID from card.
- */
if (mmc_host_is_spi(host))
err = mmc_send_cid(host, cid);
else
err = mmc_all_send_cid(host, cid);
+
+ return err;
+}
+
+int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card)
+{
+ int err;
+
+ /*
+ * Fetch CSD from card.
+ */
+ err = mmc_send_csd(card, card->raw_csd);
if (err)
- goto err;
+ return err;
- if (oldcard) {
- if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
- err = -ENOENT;
- goto err;
+ err = mmc_decode_csd(card);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
+ bool reinit)
+{
+ int err;
+
+ if (!reinit) {
+ /*
+ * Fetch SCR from card.
+ */
+ err = mmc_app_send_scr(card, card->raw_scr);
+ if (err)
+ return err;
+
+ err = mmc_decode_scr(card);
+ if (err)
+ return err;
+
+ /*
+ * Fetch switch information from card.
+ */
+ err = mmc_read_switch(card);
+ if (err)
+ return err;
+ }
+
+ /*
+ * For SPI, enable CRC as appropriate.
+ * This CRC enable is located AFTER the reading of the
+ * card registers because some SDHC cards are not able
+ * to provide valid CRCs for non-512-byte blocks.
+ */
+ if (mmc_host_is_spi(host)) {
+ err = mmc_spi_set_crc(host, use_spi_crc);
+ if (err)
+ return err;
+ }
+
+ /*
+ * Check if read-only switch is active.
+ */
+ if (!reinit) {
+ int ro = -1;
+
+ if (host->ops->get_ro)
+ ro = host->ops->get_ro(host);
+
+ if (ro < 0) {
+ printk(KERN_WARNING "%s: host does not "
+ "support reading read-only "
+ "switch. assuming write-enable.\n",
+ mmc_hostname(host));
+ } else if (ro > 0) {
+ mmc_card_set_readonly(card);
}
+ }
+
+ return 0;
+}
+
+unsigned mmc_sd_get_max_clock(struct mmc_card *card)
+{
+ unsigned max_dtr = (unsigned int)-1;
+
+ if (mmc_card_highspeed(card)) {
+ if (max_dtr > card->sw_caps.hs_max_dtr)
+ max_dtr = card->sw_caps.hs_max_dtr;
+ } else if (max_dtr > card->csd.max_dtr) {
+ max_dtr = card->csd.max_dtr;
+ }
+
+ return max_dtr;
+}
+
+void mmc_sd_go_highspeed(struct mmc_card *card)
+{
+ mmc_card_set_highspeed(card);
+ mmc_set_timing(card->host, MMC_TIMING_SD_HS);
+}
+
+/*
+ * Handle the detection and initialisation of a card.
+ *
+ * In the case of a resume, "oldcard" will contain the card
+ * we're trying to reinitialise.
+ */
+static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
+ struct mmc_card *oldcard)
+{
+ struct mmc_card *card;
+ int err;
+ u32 cid[4];
+
+ BUG_ON(!host);
+ WARN_ON(!host->claimed);
+
+ err = mmc_sd_get_cid(host, ocr, cid);
+ if (err)
+ return err;
+
+ if (oldcard) {
+ if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
+ return -ENOENT;
card = oldcard;
} else {
* Allocate card structure.
*/
card = mmc_alloc_card(host, &sd_type);
- if (IS_ERR(card)) {
- err = PTR_ERR(card);
- goto err;
- }
+ if (IS_ERR(card))
+ return PTR_ERR(card);
card->type = MMC_TYPE_SD;
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
if (!mmc_host_is_spi(host)) {
err = mmc_send_relative_addr(host, &card->rca);
if (err)
- goto free_card;
+ return err;
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
}
if (!oldcard) {
- /*
- * Fetch CSD from card.
- */
- err = mmc_send_csd(card, card->raw_csd);
+ err = mmc_sd_get_csd(host, card);
if (err)
- goto free_card;
-
- err = mmc_decode_csd(card);
- if (err)
- goto free_card;
+ return err;
mmc_decode_cid(card);
}
if (!mmc_host_is_spi(host)) {
err = mmc_select_card(card);
if (err)
- goto free_card;
+ return err;
}
- if (!oldcard) {
- /*
- * Fetch SCR from card.
- */
- err = mmc_app_send_scr(card, card->raw_scr);
- if (err)
- goto free_card;
-
- err = mmc_decode_scr(card);
- if (err < 0)
- goto free_card;
-
- /*
- * Fetch switch information from card.
- */
- err = mmc_read_switch(card);
- if (err)
- goto free_card;
- }
-
- /*
- * For SPI, enable CRC as appropriate.
- * This CRC enable is located AFTER the reading of the
- * card registers because some SDHC cards are not able
- * to provide valid CRCs for non-512-byte blocks.
- */
- if (mmc_host_is_spi(host)) {
- err = mmc_spi_set_crc(host, use_spi_crc);
- if (err)
- goto free_card;
- }
+ err = mmc_sd_setup_card(host, card, oldcard != NULL);
+ if (err)
+ goto free_card;
/*
* Attempt to change to high-speed (if supported)
*/
- err = mmc_switch_hs(card);
- if (err)
+ err = mmc_sd_switch_hs(card);
+ if (err > 0)
+ mmc_sd_go_highspeed(card);
+ else if (err)
goto free_card;
/*
- * Compute bus speed.
+ * Set bus speed.
*/
- max_dtr = (unsigned int)-1;
-
- if (mmc_card_highspeed(card)) {
- if (max_dtr > card->sw_caps.hs_max_dtr)
- max_dtr = card->sw_caps.hs_max_dtr;
- } else if (max_dtr > card->csd.max_dtr) {
- max_dtr = card->csd.max_dtr;
- }
-
- mmc_set_clock(host, max_dtr);
+ mmc_set_clock(host, mmc_sd_get_max_clock(card));
/*
* Switch to wider bus (if supported).
mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
}
- /*
- * Check if read-only switch is active.
- */
- if (!oldcard) {
- if (!host->ops->get_ro || host->ops->get_ro(host) < 0) {
- printk(KERN_WARNING "%s: host does not "
- "support reading read-only "
- "switch. assuming write-enable.\n",
- mmc_hostname(host));
- } else {
- if (host->ops->get_ro(host) > 0)
- mmc_card_set_readonly(card);
- }
- }
-
- if (!oldcard)
- host->card = card;
-
+ host->card = card;
return 0;
free_card:
if (!oldcard)
mmc_remove_card(card);
-err:
return err;
}
--- /dev/null
+#ifndef _MMC_CORE_SD_H
+#define _MMC_CORE_SD_H
+
+#include <linux/mmc/card.h>
+
+extern struct device_type sd_type;
+
+int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid);
+int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card);
+void mmc_decode_cid(struct mmc_card *card);
+int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
+ bool reinit);
+unsigned mmc_sd_get_max_clock(struct mmc_card *card);
+int mmc_sd_switch_hs(struct mmc_card *card);
+void mmc_sd_go_highspeed(struct mmc_card *card);
+
+#endif
#include "core.h"
#include "bus.h"
+#include "sd.h"
#include "sdio_bus.h"
#include "mmc_ops.h"
#include "sd_ops.h"
func->num = fn;
- ret = sdio_read_fbr(func);
- if (ret)
- goto fail;
+ if (!(card->quirks & MMC_QUIRK_NONSTD_SDIO)) {
+ ret = sdio_read_fbr(func);
+ if (ret)
+ goto fail;
- ret = sdio_read_func_cis(func);
- if (ret)
- goto fail;
+ ret = sdio_read_func_cis(func);
+ if (ret)
+ goto fail;
+ } else {
+ func->vendor = func->card->cis.vendor;
+ func->device = func->card->cis.device;
+ func->max_blksize = func->card->cis.blksize;
+ }
card->sdio_func[fn - 1] = func;
if (ret)
return ret;
- mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
-
- return 0;
+ return 1;
}
/*
return 0;
}
+
+static int sdio_enable_4bit_bus(struct mmc_card *card)
+{
+ int err;
+
+ if (card->type == MMC_TYPE_SDIO)
+ return sdio_enable_wide(card);
+
+ if ((card->host->caps & MMC_CAP_4_BIT_DATA) &&
+ (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
+ err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
+ if (err)
+ return err;
+ } else
+ return 0;
+
+ err = sdio_enable_wide(card);
+ if (err <= 0)
+ mmc_app_set_bus_width(card, MMC_BUS_WIDTH_1);
+
+ return err;
+}
+
+
/*
* Test if the card supports high-speed mode and, if so, switch to it.
*/
-static int sdio_enable_hs(struct mmc_card *card)
+static int mmc_sdio_switch_hs(struct mmc_card *card, int enable)
{
int ret;
u8 speed;
if (ret)
return ret;
- speed |= SDIO_SPEED_EHS;
+ if (enable)
+ speed |= SDIO_SPEED_EHS;
+ else
+ speed &= ~SDIO_SPEED_EHS;
ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL);
if (ret)
return ret;
- mmc_card_set_highspeed(card);
- mmc_set_timing(card->host, MMC_TIMING_SD_HS);
+ return 1;
+}
- return 0;
+/*
+ * Enable SDIO/combo card's high-speed mode. Return 0/1 if [not]supported.
+ */
+static int sdio_enable_hs(struct mmc_card *card)
+{
+ int ret;
+
+ ret = mmc_sdio_switch_hs(card, true);
+ if (ret <= 0 || card->type == MMC_TYPE_SDIO)
+ return ret;
+
+ ret = mmc_sd_switch_hs(card);
+ if (ret <= 0)
+ mmc_sdio_switch_hs(card, false);
+
+ return ret;
+}
+
+static unsigned mmc_sdio_get_max_clock(struct mmc_card *card)
+{
+ unsigned max_dtr;
+
+ if (mmc_card_highspeed(card)) {
+ /*
+ * The SDIO specification doesn't mention how
+ * the CIS transfer speed register relates to
+ * high-speed, but it seems that 50 MHz is
+ * mandatory.
+ */
+ max_dtr = 50000000;
+ } else {
+ max_dtr = card->cis.max_dtr;
+ }
+
+ if (card->type == MMC_TYPE_SD_COMBO)
+ max_dtr = min(max_dtr, mmc_sd_get_max_clock(card));
+
+ return max_dtr;
}
/*
goto err;
}
- card->type = MMC_TYPE_SDIO;
+ err = mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid);
+
+ if (!err) {
+ card->type = MMC_TYPE_SD_COMBO;
+
+ if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
+ memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) {
+ mmc_remove_card(card);
+ return -ENOENT;
+ }
+ } else {
+ card->type = MMC_TYPE_SDIO;
+
+ if (oldcard && oldcard->type != MMC_TYPE_SDIO) {
+ mmc_remove_card(card);
+ return -ENOENT;
+ }
+ }
/*
* Call the optional HC's init_card function to handle quirks.
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
}
+ /*
+ * Read CSD, before selecting the card
+ */
+ if (!oldcard && card->type == MMC_TYPE_SD_COMBO) {
+ err = mmc_sd_get_csd(host, card);
+ if (err)
+ return err;
+
+ mmc_decode_cid(card);
+ }
+
/*
* Select card, as all following commands rely on that.
*/
goto remove;
}
+ if (card->quirks & MMC_QUIRK_NONSTD_SDIO) {
+ /*
+ * This is non-standard SDIO device, meaning it doesn't
+ * have any CIA (Common I/O area) registers present.
+ * It's host's responsibility to fill cccr and cis
+ * structures in init_card().
+ */
+ mmc_set_clock(host, card->cis.max_dtr);
+
+ if (card->cccr.high_speed) {
+ mmc_card_set_highspeed(card);
+ mmc_set_timing(card->host, MMC_TIMING_SD_HS);
+ }
+
+ goto finish;
+ }
+
/*
* Read the common registers.
*/
int same = (card->cis.vendor == oldcard->cis.vendor &&
card->cis.device == oldcard->cis.device);
mmc_remove_card(card);
- if (!same) {
- err = -ENOENT;
- goto err;
- }
+ if (!same)
+ return -ENOENT;
+
card = oldcard;
return 0;
}
+ if (card->type == MMC_TYPE_SD_COMBO) {
+ err = mmc_sd_setup_card(host, card, oldcard != NULL);
+ /* handle as SDIO-only card if memory init failed */
+ if (err) {
+ mmc_go_idle(host);
+ if (mmc_host_is_spi(host))
+ /* should not fail, as it worked previously */
+ mmc_spi_set_crc(host, use_spi_crc);
+ card->type = MMC_TYPE_SDIO;
+ } else
+ card->dev.type = &sd_type;
+ }
+
+ /*
+ * If needed, disconnect card detection pull-up resistor.
+ */
+ err = sdio_disable_cd(card);
+ if (err)
+ goto remove;
+
/*
* Switch to high-speed (if supported).
*/
err = sdio_enable_hs(card);
- if (err)
+ if (err > 0)
+ mmc_sd_go_highspeed(card);
+ else if (err)
goto remove;
/*
* Change to the card's maximum speed.
*/
- if (mmc_card_highspeed(card)) {
- /*
- * The SDIO specification doesn't mention how
- * the CIS transfer speed register relates to
- * high-speed, but it seems that 50 MHz is
- * mandatory.
- */
- mmc_set_clock(host, 50000000);
- } else {
- mmc_set_clock(host, card->cis.max_dtr);
- }
+ mmc_set_clock(host, mmc_sdio_get_max_clock(card));
/*
* Switch to wider bus (if supported).
*/
- err = sdio_enable_wide(card);
- if (err)
+ err = sdio_enable_4bit_bus(card);
+ if (err > 0)
+ mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
+ else if (err)
goto remove;
+finish:
if (!oldcard)
host->card = card;
return 0;
mmc_claim_host(host);
err = mmc_sdio_init_card(host, host->ocr, host->card,
(host->pm_flags & MMC_PM_KEEP_POWER));
- if (!err)
+ if (!err) {
/* We may have switched to 1-bit mode during suspend. */
- err = sdio_enable_wide(host->card);
+ err = sdio_enable_4bit_bus(host->card);
+ if (err > 0) {
+ mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
+ err = 0;
+ }
+ }
if (!err && host->sdio_irqs)
mmc_signal_sdio_irq(host);
mmc_release_host(host);
funcs = (ocr & 0x70000000) >> 28;
card->sdio_funcs = 0;
- /*
- * If needed, disconnect card detection pull-up resistor.
- */
- err = sdio_disable_cd(card);
- if (err)
- goto remove;
-
/*
* Initialize (but don't add) all present functions.
*/
If unsure, say N.
+config MMC_SDHCI_CNS3XXX
+ bool "SDHCI support on the Cavium Networks CNS3xxx SoC"
+ depends on ARCH_CNS3XXX
+ depends on MMC_SDHCI_PLTFM
+ help
+ This selects the SDHCI support for CNS3xxx System-on-Chip devices.
+
+ If unsure, say N.
+
config MMC_SDHCI_S3C
tristate "SDHCI support on Samsung S3C SoC"
depends on MMC_SDHCI && (PLAT_S3C24XX || PLAT_S3C64XX)
obj-$(CONFIG_MMC_MXC) += mxcmmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
-obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o
obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
+obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-platform.o
+sdhci-platform-y := sdhci-pltfm.o
+sdhci-platform-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o
+
obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o
sdhci-of-y := sdhci-of-core.o
sdhci-of-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
return 0;
}
-#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ
-static void
-do_resume_work(struct work_struct *work)
-{
- struct msmsdcc_host *host =
- container_of(work, struct msmsdcc_host, resume_task);
- struct mmc_host *mmc = host->mmc;
-
- if (mmc) {
- mmc_resume_host(mmc);
- if (host->stat_irq)
- enable_irq(host->stat_irq);
- }
-}
-#endif
-
static int
msmsdcc_probe(struct platform_device *pdev)
{
host->dmares = dmares;
spin_lock_init(&host->lock);
-#ifdef CONFIG_MMC_EMBEDDED_SDIO
- if (plat->embedded_sdio)
- mmc_set_embedded_sdio_data(mmc,
- &plat->embedded_sdio->cis,
- &plat->embedded_sdio->cccr,
- plat->embedded_sdio->funcs,
- plat->embedded_sdio->num_funcs);
-#endif
-
/*
* Setup DMA
*/
int cmdpoll;
struct msmsdcc_stats stats;
-#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ
- struct work_struct resume_task;
-#endif
-
/* Command parameters */
unsigned int cmd_timeout;
unsigned int cmd_pio_irqmask;
struct mmc_data *data = host->mrq->data;
int dma_ch, req_in_progress;
- if (ch_status & OMAP2_DMA_MISALIGNED_ERR_IRQ)
- dev_dbg(mmc_dev(host->mmc), "MISALIGNED_ADRS_ERR\n");
+ if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
+ dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n",
+ ch_status);
+ return;
+ }
spin_lock(&host->irq_lock);
if (host->dma_ch < 0) {
return mmc_slot(host).get_ro(host->dev, 0);
}
+static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card)
+{
+ struct omap_hsmmc_host *host = mmc_priv(mmc);
+
+ if (mmc_slot(host).init_card)
+ mmc_slot(host).init_card(card);
+}
+
static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
{
u32 hctl, capa, value;
.set_ios = omap_hsmmc_set_ios,
.get_cd = omap_hsmmc_get_cd,
.get_ro = omap_hsmmc_get_ro,
+ .init_card = omap_hsmmc_init_card,
/* NYET -- enable_sdio_irq */
};
.set_ios = omap_hsmmc_set_ios,
.get_cd = omap_hsmmc_get_cd,
.get_ro = omap_hsmmc_get_ro,
+ .init_card = omap_hsmmc_init_card,
/* NYET -- enable_sdio_irq */
};
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
MMC_CAP_WAIT_WHILE_BUSY;
- if (mmc_slot(host).wires >= 8)
+ switch (mmc_slot(host).wires) {
+ case 8:
mmc->caps |= MMC_CAP_8_BIT_DATA;
- else if (mmc_slot(host).wires >= 4)
+ /* Fall through */
+ case 4:
mmc->caps |= MMC_CAP_4_BIT_DATA;
+ break;
+ case 1:
+ /* Nothing to crib here */
+ case 0:
+ /* Assuming nothing was given by board, Core use's 1-Bit */
+ break;
+ default:
+ /* Completely unexpected.. Core goes with 1-Bit Width */
+ dev_crit(mmc_dev(host->mmc), "Invalid width %d\n used!"
+ "using 1 instead\n", mmc_slot(host).wires);
+ }
if (mmc_slot(host).nonremovable)
mmc->caps |= MMC_CAP_NONREMOVABLE;
--- /dev/null
+/*
+ * SDHCI support for CNS3xxx SoC
+ *
+ * Copyright 2008 Cavium Networks
+ * Copyright 2010 MontaVista Software, LLC.
+ *
+ * Authors: Scott Shu
+ * Anton Vorontsov <avorontsov@mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mmc/host.h>
+#include <linux/sdhci-pltfm.h>
+#include <mach/cns3xxx.h>
+#include "sdhci.h"
+#include "sdhci-pltfm.h"
+
+static unsigned int sdhci_cns3xxx_get_max_clk(struct sdhci_host *host)
+{
+ return 150000000;
+}
+
+static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ struct device *dev = mmc_dev(host->mmc);
+ int div = 1;
+ u16 clk;
+ unsigned long timeout;
+
+ if (clock == host->clock)
+ return;
+
+ sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
+
+ if (clock == 0)
+ goto out;
+
+ while (host->max_clk / div > clock) {
+ /*
+ * On CNS3xxx divider grows linearly up to 4, and then
+ * exponentially up to 256.
+ */
+ if (div < 4)
+ div += 1;
+ else if (div < 256)
+ div *= 2;
+ else
+ break;
+ }
+
+ dev_dbg(dev, "desired SD clock: %d, actual: %d\n",
+ clock, host->max_clk / div);
+
+ /* Divide by 3 is special. */
+ if (div != 3)
+ div >>= 1;
+
+ clk = div << SDHCI_DIVIDER_SHIFT;
+ clk |= SDHCI_CLOCK_INT_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ timeout = 20;
+ while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
+ & SDHCI_CLOCK_INT_STABLE)) {
+ if (timeout == 0) {
+ dev_warn(dev, "clock is unstable");
+ break;
+ }
+ timeout--;
+ mdelay(1);
+ }
+
+ clk |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+out:
+ host->clock = clock;
+}
+
+static struct sdhci_ops sdhci_cns3xxx_ops = {
+ .get_max_clock = sdhci_cns3xxx_get_max_clk,
+ .set_clock = sdhci_cns3xxx_set_clock,
+};
+
+struct sdhci_pltfm_data sdhci_cns3xxx_pdata = {
+ .ops = &sdhci_cns3xxx_ops,
+ .quirks = SDHCI_QUIRK_BROKEN_DMA |
+ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
+ SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
+ SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
+ SDHCI_QUIRK_NONSTANDARD_CLOCK,
+};
host->ops = &sdhci_of_data->ops;
}
+ if (of_get_property(np, "sdhci,auto-cmd12", NULL))
+ host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
+
+
if (of_get_property(np, "sdhci,1-bit-only", NULL))
host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
+#include <linux/device.h>
#include <linux/mmc/host.h>
if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG ||
chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SONY)
chip->quirks |= SDHCI_QUIRK_NO_CARD_NO_RESET;
+ return 0;
+}
+
+static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot)
+{
+ slot->host->caps =
+ ((0x21 << SDHCI_TIMEOUT_CLK_SHIFT)
+ & SDHCI_TIMEOUT_CLK_MASK) |
+
+ ((0x21 << SDHCI_CLOCK_BASE_SHIFT)
+ & SDHCI_CLOCK_BASE_MASK) |
+ SDHCI_TIMEOUT_CLK_UNIT |
+ SDHCI_CAN_VDD_330 |
+ SDHCI_CAN_DO_SDMA;
+ return 0;
+}
+
+static int ricoh_mmc_resume(struct sdhci_pci_chip *chip)
+{
+ /* Apply a delay to allow controller to settle */
+ /* Otherwise it becomes confused if card state changed
+ during suspend */
+ msleep(500);
return 0;
}
SDHCI_QUIRK_CLOCK_BEFORE_RESET,
};
+static const struct sdhci_pci_fixes sdhci_ricoh_mmc = {
+ .probe_slot = ricoh_mmc_probe_slot,
+ .resume = ricoh_mmc_resume,
+ .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR |
+ SDHCI_QUIRK_CLOCK_BEFORE_RESET |
+ SDHCI_QUIRK_NO_CARD_NO_RESET |
+ SDHCI_QUIRK_MISSING_CAPS
+};
+
static const struct sdhci_pci_fixes sdhci_ene_712 = {
.quirks = SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_BROKEN_DMA,
.driver_data = (kernel_ulong_t)&sdhci_ricoh,
},
+ {
+ .vendor = PCI_VENDOR_ID_RICOH,
+ .device = 0x843,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc,
+ },
+
+ {
+ .vendor = PCI_VENDOR_ID_RICOH,
+ .device = 0xe822,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc,
+ },
+
{
.vendor = PCI_VENDOR_ID_ENE,
.device = PCI_DEVICE_ID_ENE_CB712_SD,
#include <linux/delay.h>
#include <linux/highmem.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/mmc/host.h>
#include <linux/sdhci-pltfm.h>
#include "sdhci.h"
+#include "sdhci-pltfm.h"
/*****************************************************************************\
* *
static int __devinit sdhci_pltfm_probe(struct platform_device *pdev)
{
struct sdhci_pltfm_data *pdata = pdev->dev.platform_data;
+ const struct platform_device_id *platid = platform_get_device_id(pdev);
struct sdhci_host *host;
struct resource *iomem;
int ret;
+ if (!pdata && platid && platid->driver_data)
+ pdata = (void *)platid->driver_data;
+
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!iomem) {
ret = -ENOMEM;
return 0;
}
+static const struct platform_device_id sdhci_pltfm_ids[] = {
+ { "sdhci", },
+#ifdef CONFIG_MMC_SDHCI_CNS3XXX
+ { "sdhci-cns3xxx", (kernel_ulong_t)&sdhci_cns3xxx_pdata },
+#endif
+ { },
+};
+MODULE_DEVICE_TABLE(platform, sdhci_pltfm_ids);
+
static struct platform_driver sdhci_pltfm_driver = {
.driver = {
.name = "sdhci",
},
.probe = sdhci_pltfm_probe,
.remove = __devexit_p(sdhci_pltfm_remove),
+ .id_table = sdhci_pltfm_ids,
};
/*****************************************************************************\
MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver");
MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:sdhci");
--- /dev/null
+/*
+ * Copyright 2010 MontaVista Software, LLC.
+ *
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _DRIVERS_MMC_SDHCI_PLTFM_H
+#define _DRIVERS_MMC_SDHCI_PLTFM_H
+
+#include <linux/sdhci-pltfm.h>
+
+extern struct sdhci_pltfm_data sdhci_cns3xxx_pdata;
+
+#endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/gpio.h>
#include <linux/mmc/host.h>
struct resource *ioarea;
struct s3c_sdhci_platdata *pdata;
unsigned int cur_clk;
+ int ext_cd_irq;
+ int ext_cd_gpio;
struct clk *clk_io;
struct clk *clk_bus[MAX_BUS_CLK];
return max;
}
-static unsigned int sdhci_s3c_get_timeout_clk(struct sdhci_host *host)
-{
- return sdhci_s3c_get_max_clk(host) / 1000000;
-}
-
/**
* sdhci_s3c_consider_clock - consider one the bus clocks for current setting
* @ourhost: Our SDHCI instance.
ourhost->cur_clk = best_src;
host->max_clk = clk_get_rate(clk);
- host->timeout_clk = sdhci_s3c_get_timeout_clk(host);
ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
}
}
+/**
+ * sdhci_s3c_get_min_clock - callback to get minimal supported clock value
+ * @host: The SDHCI host being queried
+ *
+ * To init mmc host properly a minimal clock value is needed. For high system
+ * bus clock's values the standard formula gives values out of allowed range.
+ * The clock still can be set to lower values, if clock source other then
+ * system bus is selected.
+*/
+static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host)
+{
+ struct sdhci_s3c *ourhost = to_s3c(host);
+ unsigned int delta, min = UINT_MAX;
+ int src;
+
+ for (src = 0; src < MAX_BUS_CLK; src++) {
+ delta = sdhci_s3c_consider_clock(ourhost, src, 0);
+ if (delta == UINT_MAX)
+ continue;
+ /* delta is a negative value in this case */
+ if (-delta < min)
+ min = -delta;
+ }
+ return min;
+}
+
static struct sdhci_ops sdhci_s3c_ops = {
.get_max_clock = sdhci_s3c_get_max_clk,
- .get_timeout_clock = sdhci_s3c_get_timeout_clk,
.set_clock = sdhci_s3c_set_clock,
+ .get_min_clock = sdhci_s3c_get_min_clock,
};
+static void sdhci_s3c_notify_change(struct platform_device *dev, int state)
+{
+ struct sdhci_host *host = platform_get_drvdata(dev);
+ if (host) {
+ mutex_lock(&host->lock);
+ if (state) {
+ dev_dbg(&dev->dev, "card inserted.\n");
+ host->flags &= ~SDHCI_DEVICE_DEAD;
+ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ } else {
+ dev_dbg(&dev->dev, "card removed.\n");
+ host->flags |= SDHCI_DEVICE_DEAD;
+ host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ }
+ sdhci_card_detect(host);
+ mutex_unlock(&host->lock);
+ }
+}
+
+static irqreturn_t sdhci_s3c_gpio_card_detect_thread(int irq, void *dev_id)
+{
+ struct sdhci_s3c *sc = dev_id;
+ int status = gpio_get_value(sc->ext_cd_gpio);
+ if (sc->pdata->ext_cd_gpio_invert)
+ status = !status;
+ sdhci_s3c_notify_change(sc->pdev, status);
+ return IRQ_HANDLED;
+}
+
+static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc)
+{
+ struct s3c_sdhci_platdata *pdata = sc->pdata;
+ struct device *dev = &sc->pdev->dev;
+
+ if (gpio_request(pdata->ext_cd_gpio, "SDHCI EXT CD") == 0) {
+ sc->ext_cd_gpio = pdata->ext_cd_gpio;
+ sc->ext_cd_irq = gpio_to_irq(pdata->ext_cd_gpio);
+ if (sc->ext_cd_irq &&
+ request_threaded_irq(sc->ext_cd_irq, NULL,
+ sdhci_s3c_gpio_card_detect_thread,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ dev_name(dev), sc) == 0) {
+ int status = gpio_get_value(sc->ext_cd_gpio);
+ if (pdata->ext_cd_gpio_invert)
+ status = !status;
+ sdhci_s3c_notify_change(sc->pdev, status);
+ } else {
+ dev_warn(dev, "cannot request irq for card detect\n");
+ sc->ext_cd_irq = 0;
+ }
+ } else {
+ dev_err(dev, "cannot request gpio for card detect\n");
+ }
+}
+
static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
{
struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data;
sc->host = host;
sc->pdev = pdev;
sc->pdata = pdata;
+ sc->ext_cd_gpio = -1; /* invalid gpio number */
platform_set_drvdata(pdev, host);
/* Setup quirks for the controller */
host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;
+ host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
#ifndef CONFIG_MMC_SDHCI_S3C_DMA
* SDHCI block, or a missing configuration that needs to be set. */
host->quirks |= SDHCI_QUIRK_NO_BUSY_IRQ;
+ if (pdata->cd_type == S3C_SDHCI_CD_NONE ||
+ pdata->cd_type == S3C_SDHCI_CD_PERMANENT)
+ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+
+ if (pdata->cd_type == S3C_SDHCI_CD_PERMANENT)
+ host->mmc->caps = MMC_CAP_NONREMOVABLE;
+
host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR |
SDHCI_QUIRK_32BIT_DMA_SIZE);
+ /* HSMMC on Samsung SoCs uses SDCLK as timeout clock */
+ host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK;
+
ret = sdhci_add_host(host);
if (ret) {
dev_err(dev, "sdhci_add_host() failed\n");
goto err_add_host;
}
+ /* The following two methods of card detection might call
+ sdhci_s3c_notify_change() immediately, so they can be called
+ only after sdhci_add_host(). Setup errors are ignored. */
+ if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_init)
+ pdata->ext_cd_init(&sdhci_s3c_notify_change);
+ if (pdata->cd_type == S3C_SDHCI_CD_GPIO &&
+ gpio_is_valid(pdata->ext_cd_gpio))
+ sdhci_s3c_setup_card_detect_gpio(sc);
+
return 0;
err_add_host:
static int __devexit sdhci_s3c_remove(struct platform_device *pdev)
{
+ struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data;
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_s3c *sc = sdhci_priv(host);
int ptr;
+ if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_cleanup)
+ pdata->ext_cd_cleanup(&sdhci_s3c_notify_change);
+
+ if (sc->ext_cd_irq)
+ free_irq(sc->ext_cd_irq, sc);
+
+ if (gpio_is_valid(sc->ext_cd_gpio))
+ gpio_free(sc->ext_cd_gpio);
+
sdhci_remove_host(host, 1);
for (ptr = 0; ptr < 3; ptr++) {
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/scatterlist.h>
+#include <linux/regulator/consumer.h>
#include <linux/leds.h>
WARN_ON(!host->data);
mode = SDHCI_TRNS_BLK_CNT_EN;
- if (data->blocks > 1)
- mode |= SDHCI_TRNS_MULTI;
+ if (data->blocks > 1) {
+ if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
+ mode |= SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12;
+ else
+ mode |= SDHCI_TRNS_MULTI;
+ }
if (data->flags & MMC_DATA_READ)
mode |= SDHCI_TRNS_READ;
if (host->flags & SDHCI_REQ_USE_DMA)
#ifndef SDHCI_USE_LEDS_CLASS
sdhci_activate_led(host);
#endif
+ if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) {
+ if (mrq->stop) {
+ mrq->data->stop = NULL;
+ mrq->stop = NULL;
+ }
+ }
host->mrq = mrq;
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+ if (ios->bus_width == MMC_BUS_WIDTH_8)
+ ctrl |= SDHCI_CTRL_8BITBUS;
+ else
+ ctrl &= ~SDHCI_CTRL_8BITBUS;
+
if (ios->bus_width == MMC_BUS_WIDTH_4)
ctrl |= SDHCI_CTRL_4BITBUS;
else
free_irq(host->irq, host);
- return 0;
+ if (host->vmmc)
+ ret = regulator_disable(host->vmmc);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(sdhci_suspend_host);
{
int ret;
+ if (host->vmmc) {
+ int ret = regulator_enable(host->vmmc);
+ if (ret)
+ return ret;
+ }
+
+
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
if (host->ops->enable_dma)
host->ops->enable_dma(host);
host->version);
}
- caps = sdhci_readl(host, SDHCI_CAPABILITIES);
+ caps = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
+ sdhci_readl(host, SDHCI_CAPABILITIES);
if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
host->flags |= SDHCI_USE_SDMA;
* Set host parameters.
*/
mmc->ops = &sdhci_ops;
- if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK &&
- host->ops->set_clock && host->ops->get_min_clock)
+ if (host->ops->get_min_clock)
mmc->f_min = host->ops->get_min_clock(host);
else
mmc->f_min = host->max_clk / 256;
mmc->f_max = host->max_clk;
- mmc->caps = MMC_CAP_SDIO_IRQ;
+ mmc->caps |= MMC_CAP_SDIO_IRQ;
if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
mmc->caps |= MMC_CAP_4_BIT_DATA;
if (ret)
goto untasklet;
+ host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
+ if (IS_ERR(host->vmmc)) {
+ printk(KERN_INFO "%s: no vmmc regulator found\n", mmc_hostname(mmc));
+ host->vmmc = NULL;
+ } else {
+ regulator_enable(host->vmmc);
+ }
+
sdhci_init(host, 0);
#ifdef CONFIG_MMC_DEBUG
tasklet_kill(&host->card_tasklet);
tasklet_kill(&host->finish_tasklet);
+ if (host->vmmc) {
+ regulator_disable(host->vmmc);
+ regulator_put(host->vmmc);
+ }
+
kfree(host->adma_desc);
kfree(host->align_buffer);
#define SDHCI_CTRL_ADMA1 0x08
#define SDHCI_CTRL_ADMA32 0x10
#define SDHCI_CTRL_ADMA64 0x18
+#define SDHCI_CTRL_8BITBUS 0x20
#define SDHCI_POWER_CONTROL 0x29
#define SDHCI_POWER_ON 0x01
#define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1<<25)
/* Controller cannot support End Attribute in NOP ADMA descriptor */
#define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1<<26)
+/* Controller is missing device caps. Use caps provided by host */
+#define SDHCI_QUIRK_MISSING_CAPS (1<<27)
+/* Controller uses Auto CMD12 command to stop the transfer */
+#define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1<<28)
int irq; /* Device IRQ */
void __iomem * ioaddr; /* Mapped address */
const struct sdhci_ops *ops; /* Low level hw interface */
+ struct regulator *vmmc; /* Power regulator */
+
/* Internal data */
struct mmc_host *mmc; /* MMC structure */
u64 dma_mask; /* custom DMA mask */
struct timer_list timer; /* Timer for timeouts */
+ unsigned int caps; /* Alternative capabilities */
+
unsigned long private[0] ____cacheline_aligned;
};
return (void *)host->private;
}
+extern void sdhci_card_detect(struct sdhci_host *host);
extern int sdhci_add_host(struct sdhci_host *host);
extern void sdhci_remove_host(struct sdhci_host *host, int dead);
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
#include <linux/hdreg.h>
#include <linux/init.h>
#include <linux/mutex.h>
buf = req->buffer;
- if (!blk_fs_request(req))
+ if (req->cmd_type != REQ_TYPE_FS)
return -EIO;
if (blk_rq_pos(req) + blk_rq_cur_sectors(req) >
get_capacity(req->rq_disk))
return -EIO;
- if (blk_discard_rq(req))
+ if (req->cmd_flags & REQ_DISCARD)
return tr->discard(dev, block, nsect);
switch(rq_data_dir(req)) {
int ret;
if (!dev)
- return -ERESTARTSYS;
+ return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/
+ lock_kernel();
mutex_lock(&dev->lock);
if (!dev->mtd) {
unlock:
mutex_unlock(&dev->lock);
blktrans_dev_put(dev);
+ unlock_kernel();
return ret;
}
if (!dev)
return ret;
+ lock_kernel();
mutex_lock(&dev->lock);
/* Release one reference, we sure its not the last one here*/
unlock:
mutex_unlock(&dev->lock);
blktrans_dev_put(dev);
+ unlock_kernel();
return ret;
}
if (!dev)
return ret;
+ lock_kernel();
mutex_lock(&dev->lock);
if (!dev->mtd)
}
unlock:
mutex_unlock(&dev->lock);
+ unlock_kernel();
blktrans_dev_put(dev);
return ret;
}
.owner = THIS_MODULE,
.open = blktrans_open,
.release = blktrans_release,
- .locked_ioctl = blktrans_ioctl,
+ .ioctl = blktrans_ioctl,
.getgeo = blktrans_getgeo,
};
}
/**
- * __nand_unlock - [REPLACABLE] unlocks specified locked blockes
+ * __nand_unlock - [REPLACEABLE] unlocks specified locked blocks
*
- * @param mtd - mtd info
- * @param ofs - offset to start unlock from
- * @param len - length to unlock
- * @invert - when = 0, unlock the range of blocks within the lower and
+ * @mtd: mtd info
+ * @ofs: offset to start unlock from
+ * @len: length to unlock
+ * @invert: when = 0, unlock the range of blocks within the lower and
* upper boundary address
- * whne = 1, unlock the range of blocks outside the boundaries
+ * when = 1, unlock the range of blocks outside the boundaries
* of the lower and upper boundary address
*
- * @return - unlock status
+ * return - unlock status
*/
static int __nand_unlock(struct mtd_info *mtd, loff_t ofs,
uint64_t len, int invert)
}
/**
- * nand_unlock - [REPLACABLE] unlocks specified locked blockes
+ * nand_unlock - [REPLACEABLE] unlocks specified locked blocks
*
- * @param mtd - mtd info
- * @param ofs - offset to start unlock from
- * @param len - length to unlock
+ * @mtd: mtd info
+ * @ofs: offset to start unlock from
+ * @len: length to unlock
*
- * @return - unlock status
+ * return - unlock status
*/
int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
}
/**
- * nand_lock - [REPLACABLE] locks all blockes present in the device
+ * nand_lock - [REPLACEABLE] locks all blocks present in the device
*
- * @param mtd - mtd info
- * @param ofs - offset to start unlock from
- * @param len - length to unlock
+ * @mtd: mtd info
+ * @ofs: offset to start unlock from
+ * @len: length to unlock
*
- * @return - lock status
+ * return - lock status
*
- * This feature is not support in many NAND parts. 'Micron' NAND parts
- * do have this feature, but it allows only to lock all blocks not for
+ * This feature is not supported in many NAND parts. 'Micron' NAND parts
+ * do have this feature, but it allows only to lock all blocks, not for
* specified range for block.
*
* Implementing 'lock' feature by making use of 'unlock', for now.
* nand_fill_oob - [Internal] Transfer client buffer to oob
* @chip: nand chip structure
* @oob: oob data buffer
+ * @len: oob data write length
* @ops: oob ops structure
*/
static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
static spinlock_t mdio_lock;
static struct eth_regs __iomem *mdio_regs; /* mdio command and status only */
-struct mii_bus *mdio_bus;
+static struct mii_bus *mdio_bus;
static int ports_open;
static struct port *npe_port_tab[MAX_NPES];
static struct dma_pool *dma_pool;
struct net_device *ndev;
struct sh_irda_self *self;
struct resource *res;
- char clk_name[8];
int irq;
int err = -ENOMEM;
if (err)
goto err_mem_2;
- snprintf(clk_name, sizeof(clk_name), "irda%d", pdev->id);
- self->clk = clk_get(&pdev->dev, clk_name);
+ self->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(self->clk)) {
- dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+ dev_err(&pdev->dev, "cannot get irda clock\n");
goto err_mem_3;
}
msg.cmd = PORT_CONFIG_WRITE;
msg.hss_port = port->id;
msg.index = HSS_CONFIG_TX_PCR;
- msg.data32 = PCR_FRM_SYNC_OUTPUT_RISING | PCR_MSB_ENDIAN |
+ msg.data32 = PCR_FRM_PULSE_DISABLED | PCR_MSB_ENDIAN |
PCR_TX_DATA_ENABLE | PCR_SOF_NO_FBIT;
if (port->clock_type == CLOCK_INT)
msg.data32 |= PCR_SYNC_CLK_DIR_OUTPUT;
*
*/
+#include <linux/sched.h>
+#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/ieee80211.h>
#include <net/cfg80211.h>
dev_dbg(&dev->dev,
"PCI parallel port detected: I/O at %#lx(%#lx), IRQ %d\n",
io_lo, io_hi, irq);
- irq = PARPORT_IRQ_NONE;
}
port = parport_pc_probe_port (io_lo, io_hi, irq,
PARPORT_DMA_NONE, &dev->dev, IRQF_SHARED);
depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \
|| MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \
|| ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2 \
- || MACH_VPAC270)
+ || MACH_VPAC270 || MACH_BALLOON3)
select PCMCIA_SOC_COMMON
help
Say Y here to include support for the PXA2xx PCMCIA controller
pxa2xx-obj-$(CONFIG_MACH_E740) += pxa2xx_e740.o
pxa2xx-obj-$(CONFIG_MACH_STARGATE2) += pxa2xx_stargate2.o
pxa2xx-obj-$(CONFIG_MACH_VPAC270) += pxa2xx_vpac270.o
+pxa2xx-obj-$(CONFIG_MACH_BALLOON3) += pxa2xx_balloon3.o
obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_base.o $(pxa2xx-obj-y)
--- /dev/null
+/*
+ * linux/drivers/pcmcia/pxa2xx_balloon3.c
+ *
+ * Balloon3 PCMCIA specific routines.
+ *
+ * Author: Nick Bane
+ * Created: June, 2006
+ * Copyright: Toby Churchill Ltd
+ * Derived from pxa2xx_mainstone.c, by Nico Pitre
+ *
+ * Various modification by Marek Vasut <marek.vasut@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <mach/balloon3.h>
+
+#include "soc_common.h"
+
+/*
+ * These are a list of interrupt sources that provokes a polled
+ * check of status
+ */
+static struct pcmcia_irqs irqs[] = {
+ { 0, BALLOON3_S0_CD_IRQ, "PCMCIA0 CD" },
+ { 0, BALLOON3_BP_NSTSCHG_IRQ, "PCMCIA0 STSCHG" },
+};
+
+static int balloon3_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+ uint16_t ver;
+ int ret;
+ static void __iomem *fpga_ver;
+
+ ver = __raw_readw(BALLOON3_FPGA_VER);
+ if (ver > 0x0201)
+ pr_warn("The FPGA code, version 0x%04x, is newer than rel-0.3. "
+ "PCMCIA/CF support might be broken in this version!",
+ ver);
+
+ skt->socket.pci_irq = BALLOON3_BP_CF_NRDY_IRQ;
+ return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void balloon3_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+ soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static unsigned long balloon3_pcmcia_status[2] = {
+ BALLOON3_CF_nSTSCHG_BVD1,
+ BALLOON3_CF_nSTSCHG_BVD1
+};
+
+static void balloon3_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
+ struct pcmcia_state *state)
+{
+ uint16_t status;
+ int flip;
+
+ /* This actually reads the STATUS register */
+ status = __raw_readw(BALLOON3_CF_STATUS_REG);
+ flip = (status ^ balloon3_pcmcia_status[skt->nr])
+ & BALLOON3_CF_nSTSCHG_BVD1;
+ /*
+ * Workaround for STSCHG which can't be deasserted:
+ * We therefore disable/enable corresponding IRQs
+ * as needed to avoid IRQ locks.
+ */
+ if (flip) {
+ balloon3_pcmcia_status[skt->nr] = status;
+ if (status & BALLOON3_CF_nSTSCHG_BVD1)
+ enable_irq(BALLOON3_BP_NSTSCHG_IRQ);
+ else
+ disable_irq(BALLOON3_BP_NSTSCHG_IRQ);
+ }
+
+ state->detect = !gpio_get_value(BALLOON3_GPIO_S0_CD);
+ state->ready = !!(status & BALLOON3_CF_nIRQ);
+ state->bvd1 = !!(status & BALLOON3_CF_nSTSCHG_BVD1);
+ state->bvd2 = 0; /* not available */
+ state->vs_3v = 1; /* Always true its a CF card */
+ state->vs_Xv = 0; /* not available */
+ state->wrprot = 0; /* not available */
+}
+
+static int balloon3_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
+ const socket_state_t *state)
+{
+ __raw_writew((state->flags & SS_RESET) ? BALLOON3_CF_RESET : 0,
+ BALLOON3_CF_CONTROL_REG);
+ return 0;
+}
+
+static void balloon3_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+}
+
+static void balloon3_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+}
+
+static struct pcmcia_low_level balloon3_pcmcia_ops = {
+ .owner = THIS_MODULE,
+ .hw_init = balloon3_pcmcia_hw_init,
+ .hw_shutdown = balloon3_pcmcia_hw_shutdown,
+ .socket_state = balloon3_pcmcia_socket_state,
+ .configure_socket = balloon3_pcmcia_configure_socket,
+ .socket_init = balloon3_pcmcia_socket_init,
+ .socket_suspend = balloon3_pcmcia_socket_suspend,
+ .first = 0,
+ .nr = 1,
+};
+
+static struct platform_device *balloon3_pcmcia_device;
+
+static int __init balloon3_pcmcia_init(void)
+{
+ int ret;
+
+ balloon3_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
+ if (!balloon3_pcmcia_device)
+ return -ENOMEM;
+
+ ret = platform_device_add_data(balloon3_pcmcia_device,
+ &balloon3_pcmcia_ops, sizeof(balloon3_pcmcia_ops));
+
+ if (!ret)
+ ret = platform_device_add(balloon3_pcmcia_device);
+
+ if (ret)
+ platform_device_put(balloon3_pcmcia_device);
+
+ return ret;
+}
+
+static void __exit balloon3_pcmcia_exit(void)
+{
+ platform_device_unregister(balloon3_pcmcia_device);
+}
+
+module_init(balloon3_pcmcia_init);
+module_exit(balloon3_pcmcia_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nick Bane <nick@cecomputing.co.uk>");
+MODULE_ALIAS("platform:pxa2xx-pcmcia");
+MODULE_DESCRIPTION("Balloon3 board CF/PCMCIA driver");
.attr = {
.name = "eeprom",
.mode = S_IRUGO,
- .owner = THIS_MODULE,
},
.size = 0,
.read = olpc_bat_eeprom_read,
static struct work_struct bat_work;
static struct mutex work_lock;
static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
-static struct wm97xx_batt_info *gpdata;
static enum power_supply_property *prop;
static unsigned long wm97xx_read_bat(struct power_supply *bat_ps)
struct wm97xx_pdata *wmdata = dev->dev.platform_data;
struct wm97xx_batt_pdata *pdata;
- if (gpdata) {
- dev_err(&dev->dev, "Do not pass platform_data through "
- "wm97xx_bat_set_pdata!\n");
- return -EINVAL;
- }
-
if (!wmdata) {
dev_err(&dev->dev, "No platform data supplied\n");
return -EINVAL;
platform_driver_unregister(&wm97xx_bat_driver);
}
-/* The interface is deprecated, as well as linux/wm97xx_batt.h */
-void wm97xx_bat_set_pdata(struct wm97xx_batt_info *data);
-
-void wm97xx_bat_set_pdata(struct wm97xx_batt_info *data)
-{
- gpdata = data;
-}
-EXPORT_SYMBOL_GPL(wm97xx_bat_set_pdata);
-
module_init(wm97xx_bat_init);
module_exit(wm97xx_bat_exit);
This driver can also be built as a module. If so, the module
will be called rtc-ds1672.
+config RTC_DRV_DS3232
+ tristate "Dallas/Maxim DS3232"
+ depends on RTC_CLASS && I2C
+ help
+ If you say yes here you get support for Dallas Semiconductor
+ DS3232 real-time clock chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds3232.
+
config RTC_DRV_MAX6900
tristate "Maxim MAX6900"
help
This driver can also be built as a module. If so, the module
will be called rtc-isl1208.
+config RTC_DRV_ISL12022
+ tristate "Intersil ISL12022"
+ help
+ If you say yes here you get support for the
+ Intersil ISL12022 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-isl12022.
+
config RTC_DRV_X1205
tristate "Xicor/Intersil X1205"
help
This driver can also be built as a module. If so, the module
will be called rtc-msm6242.
+config RTC_DRV_IMXDI
+ tristate "Freescale IMX DryIce Real Time Clock"
+ depends on ARCH_MX25
+ depends on RTC_CLASS
+ help
+ Support for Freescale IMX DryIce RTC
+
+ This driver can also be built as a module, if so, the module
+ will be called "rtc-imxdi".
+
config RTC_MXC
tristate "Freescale MXC Real Time Clock"
depends on ARCH_MXC
DA8xx/OMAP-L13x chips. This driver can also be built as a
module called rtc-omap.
+config HAVE_S3C_RTC
+ bool
+ help
+ This will include RTC support for Samsung SoCs. If
+ you want to include RTC support for any machine, kindly
+ select this in the respective mach-XXXX/Kconfig file.
+
config RTC_DRV_S3C
tristate "Samsung S3C series SoC RTC"
- depends on ARCH_S3C2410 || ARCH_S3C64XX
+ depends on ARCH_S3C2410 || ARCH_S3C64XX || HAVE_S3C_RTC
help
RTC (Realtime Clock) driver for the clock inbuilt into the
Samsung S3C24XX series of SoCs. This can provide periodic
obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
+obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o
obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
+obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
+obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o
obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o
obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o
obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o
#include <linux/acpi.h>
-#ifdef CONFIG_PM
static u32 rtc_handler(void *context)
{
acpi_clear_event(ACPI_EVENT_RTC);
{
acpi_disable_event(ACPI_EVENT_RTC, 0);
}
-#else
-#define rtc_wake_setup() do{}while(0)
-#define rtc_wake_on NULL
-#define rtc_wake_off NULL
-#endif
/* Every ACPI platform has a mc146818 compatible "cmos rtc". Here we find
* its device node and pass extra config data. This helps its driver use
--- /dev/null
+/*
+ * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over I2C
+ *
+ * Copyright (C) 2009-2010 Freescale Semiconductor.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+/*
+ * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
+ * recommened in .../Documentation/i2c/writing-clients section
+ * "Sending and receiving", using SMBus level communication is preferred.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+
+#define DS3232_REG_SECONDS 0x00
+#define DS3232_REG_MINUTES 0x01
+#define DS3232_REG_HOURS 0x02
+#define DS3232_REG_AMPM 0x02
+#define DS3232_REG_DAY 0x03
+#define DS3232_REG_DATE 0x04
+#define DS3232_REG_MONTH 0x05
+#define DS3232_REG_CENTURY 0x05
+#define DS3232_REG_YEAR 0x06
+#define DS3232_REG_ALARM1 0x07 /* Alarm 1 BASE */
+#define DS3232_REG_ALARM2 0x0B /* Alarm 2 BASE */
+#define DS3232_REG_CR 0x0E /* Control register */
+# define DS3232_REG_CR_nEOSC 0x80
+# define DS3232_REG_CR_INTCN 0x04
+# define DS3232_REG_CR_A2IE 0x02
+# define DS3232_REG_CR_A1IE 0x01
+
+#define DS3232_REG_SR 0x0F /* control/status register */
+# define DS3232_REG_SR_OSF 0x80
+# define DS3232_REG_SR_BSY 0x04
+# define DS3232_REG_SR_A2F 0x02
+# define DS3232_REG_SR_A1F 0x01
+
+struct ds3232 {
+ struct i2c_client *client;
+ struct rtc_device *rtc;
+ struct work_struct work;
+
+ /* The mutex protects alarm operations, and prevents a race
+ * between the enable_irq() in the workqueue and the free_irq()
+ * in the remove function.
+ */
+ struct mutex mutex;
+ int exiting;
+};
+
+static struct i2c_driver ds3232_driver;
+
+static int ds3232_check_rtc_status(struct i2c_client *client)
+{
+ int ret = 0;
+ int control, stat;
+
+ stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+ if (stat < 0)
+ return stat;
+
+ if (stat & DS3232_REG_SR_OSF)
+ dev_warn(&client->dev,
+ "oscillator discontinuity flagged, "
+ "time unreliable\n");
+
+ stat &= ~(DS3232_REG_SR_OSF | DS3232_REG_SR_A1F | DS3232_REG_SR_A2F);
+
+ ret = i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
+ if (ret < 0)
+ return ret;
+
+ /* If the alarm is pending, clear it before requesting
+ * the interrupt, so an interrupt event isn't reported
+ * before everything is initialized.
+ */
+
+ control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+ if (control < 0)
+ return control;
+
+ control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE);
+ control |= DS3232_REG_CR_INTCN;
+
+ return i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+}
+
+static int ds3232_read_time(struct device *dev, struct rtc_time *time)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret;
+ u8 buf[7];
+ unsigned int year, month, day, hour, minute, second;
+ unsigned int week, twelve_hr, am_pm;
+ unsigned int century, add_century = 0;
+
+ ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_SECONDS, 7, buf);
+
+ if (ret < 0)
+ return ret;
+ if (ret < 7)
+ return -EIO;
+
+ second = buf[0];
+ minute = buf[1];
+ hour = buf[2];
+ week = buf[3];
+ day = buf[4];
+ month = buf[5];
+ year = buf[6];
+
+ /* Extract additional information for AM/PM and century */
+
+ twelve_hr = hour & 0x40;
+ am_pm = hour & 0x20;
+ century = month & 0x80;
+
+ /* Write to rtc_time structure */
+
+ time->tm_sec = bcd2bin(second);
+ time->tm_min = bcd2bin(minute);
+ if (twelve_hr) {
+ /* Convert to 24 hr */
+ if (am_pm)
+ time->tm_hour = bcd2bin(hour & 0x1F) + 12;
+ else
+ time->tm_hour = bcd2bin(hour & 0x1F);
+ } else {
+ time->tm_hour = bcd2bin(hour);
+ }
+
+ time->tm_wday = bcd2bin(week);
+ time->tm_mday = bcd2bin(day);
+ time->tm_mon = bcd2bin(month & 0x7F);
+ if (century)
+ add_century = 100;
+
+ time->tm_year = bcd2bin(year) + add_century;
+
+ return rtc_valid_tm(time);
+}
+
+static int ds3232_set_time(struct device *dev, struct rtc_time *time)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u8 buf[7];
+
+ /* Extract time from rtc_time and load into ds3232*/
+
+ buf[0] = bin2bcd(time->tm_sec);
+ buf[1] = bin2bcd(time->tm_min);
+ buf[2] = bin2bcd(time->tm_hour);
+ buf[3] = bin2bcd(time->tm_wday); /* Day of the week */
+ buf[4] = bin2bcd(time->tm_mday); /* Date */
+ buf[5] = bin2bcd(time->tm_mon);
+ if (time->tm_year >= 100) {
+ buf[5] |= 0x80;
+ buf[6] = bin2bcd(time->tm_year - 100);
+ } else {
+ buf[6] = bin2bcd(time->tm_year);
+ }
+
+ return i2c_smbus_write_i2c_block_data(client,
+ DS3232_REG_SECONDS, 7, buf);
+}
+
+static irqreturn_t ds3232_irq(int irq, void *dev_id)
+{
+ struct i2c_client *client = dev_id;
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+
+ disable_irq_nosync(irq);
+ schedule_work(&ds3232->work);
+ return IRQ_HANDLED;
+}
+
+static void ds3232_work(struct work_struct *work)
+{
+ struct ds3232 *ds3232 = container_of(work, struct ds3232, work);
+ struct i2c_client *client = ds3232->client;
+ int stat, control;
+
+ mutex_lock(&ds3232->mutex);
+
+ stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+ if (stat < 0)
+ goto unlock;
+
+ if (stat & DS3232_REG_SR_A1F) {
+ control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+ if (control < 0)
+ goto out;
+ /* disable alarm1 interrupt */
+ control &= ~(DS3232_REG_CR_A1IE);
+ i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+
+ /* clear the alarm pend flag */
+ stat &= ~DS3232_REG_SR_A1F;
+ i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
+
+ rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF);
+ }
+
+out:
+ if (!ds3232->exiting)
+ enable_irq(client->irq);
+unlock:
+ mutex_unlock(&ds3232->mutex);
+}
+
+static const struct rtc_class_ops ds3232_rtc_ops = {
+ .read_time = ds3232_read_time,
+ .set_time = ds3232_set_time,
+};
+
+static int __devinit ds3232_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ds3232 *ds3232;
+ int ret;
+
+ ds3232 = kzalloc(sizeof(struct ds3232), GFP_KERNEL);
+ if (!ds3232)
+ return -ENOMEM;
+
+ ds3232->client = client;
+ i2c_set_clientdata(client, ds3232);
+
+ INIT_WORK(&ds3232->work, ds3232_work);
+ mutex_init(&ds3232->mutex);
+
+ ret = ds3232_check_rtc_status(client);
+ if (ret)
+ goto out_free;
+
+ ds3232->rtc = rtc_device_register(client->name, &client->dev,
+ &ds3232_rtc_ops, THIS_MODULE);
+ if (IS_ERR(ds3232->rtc)) {
+ ret = PTR_ERR(ds3232->rtc);
+ dev_err(&client->dev, "unable to register the class device\n");
+ goto out_irq;
+ }
+
+ if (client->irq >= 0) {
+ ret = request_irq(client->irq, ds3232_irq, 0,
+ "ds3232", client);
+ if (ret) {
+ dev_err(&client->dev, "unable to request IRQ\n");
+ goto out_free;
+ }
+ }
+
+ return 0;
+
+out_irq:
+ if (client->irq >= 0)
+ free_irq(client->irq, client);
+
+out_free:
+ i2c_set_clientdata(client, NULL);
+ kfree(ds3232);
+ return ret;
+}
+
+static int __devexit ds3232_remove(struct i2c_client *client)
+{
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+
+ if (client->irq >= 0) {
+ mutex_lock(&ds3232->mutex);
+ ds3232->exiting = 1;
+ mutex_unlock(&ds3232->mutex);
+
+ free_irq(client->irq, client);
+ flush_scheduled_work();
+ }
+
+ rtc_device_unregister(ds3232->rtc);
+ i2c_set_clientdata(client, NULL);
+ kfree(ds3232);
+ return 0;
+}
+
+static const struct i2c_device_id ds3232_id[] = {
+ { "ds3232", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ds3232_id);
+
+static struct i2c_driver ds3232_driver = {
+ .driver = {
+ .name = "rtc-ds3232",
+ .owner = THIS_MODULE,
+ },
+ .probe = ds3232_probe,
+ .remove = __devexit_p(ds3232_remove),
+ .id_table = ds3232_id,
+};
+
+static int __init ds3232_init(void)
+{
+ return i2c_add_driver(&ds3232_driver);
+}
+
+static void __exit ds3232_exit(void)
+{
+ i2c_del_driver(&ds3232_driver);
+}
+
+module_init(ds3232_init);
+module_exit(ds3232_exit);
+
+MODULE_AUTHOR("Srikanth Srinivasan <srikanth.srinivasan@freescale.com>");
+MODULE_DESCRIPTION("Maxim/Dallas DS3232 RTC Driver");
+MODULE_LICENSE("GPL");
struct i2c_msg msg[4];
struct i2c_client *client;
struct rtc_device *rtc;
+ int alarm_valid;
int data_valid;
- int alarm;
};
static const struct i2c_device_id fm3130_id[] = {
{ "fm3130", 0 },
dev_dbg(dev, "invalid mode %d\n", mode);
break;
}
- /* Checking for alarm */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) {
- fm3130->alarm = 1;
- fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF;
- }
+
i2c_smbus_write_byte_data(fm3130->client,
FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL]);
}
struct fm3130 *fm3130 = dev_get_drvdata(dev);
int tmp;
struct rtc_time *tm = &alrm->time;
+
+ if (!fm3130->alarm_valid) {
+ /*
+ * We have invalid alarm in RTC, probably due to battery faults
+ * or other problems. Return EIO for now, it will allow us to
+ * set alarm value later instead of error during probing which
+ * disables device
+ */
+ return -EIO;
+ }
+
/* read the RTC alarm registers all at once */
tmp = i2c_transfer(to_i2c_adapter(fm3130->client->dev.parent),
&fm3130->msg[2], 2);
fm3130->regs[FM3130_ALARM_DATE],
fm3130->regs[FM3130_ALARM_MONTHS]);
-
tm->tm_sec = bcd2bin(fm3130->regs[FM3130_ALARM_SECONDS] & 0x7F);
tm->tm_min = bcd2bin(fm3130->regs[FM3130_ALARM_MINUTES] & 0x7F);
tm->tm_hour = bcd2bin(fm3130->regs[FM3130_ALARM_HOURS] & 0x3F);
tm->tm_mday = bcd2bin(fm3130->regs[FM3130_ALARM_DATE] & 0x3F);
tm->tm_mon = bcd2bin(fm3130->regs[FM3130_ALARM_MONTHS] & 0x1F);
+
if (tm->tm_mon > 0)
tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */
+
dev_dbg(dev, "%s secs=%d, mins=%d, "
"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
"read alarm", tm->tm_sec, tm->tm_min,
tm->tm_hour, tm->tm_mday,
tm->tm_mon, tm->tm_year, tm->tm_wday);
+ /* check if alarm enabled */
+ fm3130->regs[FM3130_RTC_CONTROL] =
+ i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL);
+
+ if ((fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AEN) &&
+ (~fm3130->regs[FM3130_RTC_CONTROL] &
+ FM3130_RTC_CONTROL_BIT_CAL)) {
+ alrm->enabled = 1;
+ }
+
return 0;
}
tm->tm_hour, tm->tm_mday,
tm->tm_mon, tm->tm_year, tm->tm_wday);
- if (tm->tm_sec != -1)
- fm3130->regs[FM3130_ALARM_SECONDS] =
- bin2bcd(tm->tm_sec) | 0x80;
+ fm3130->regs[FM3130_ALARM_SECONDS] =
+ (tm->tm_sec != -1) ? bin2bcd(tm->tm_sec) : 0x80;
- if (tm->tm_min != -1)
- fm3130->regs[FM3130_ALARM_MINUTES] =
- bin2bcd(tm->tm_min) | 0x80;
+ fm3130->regs[FM3130_ALARM_MINUTES] =
+ (tm->tm_min != -1) ? bin2bcd(tm->tm_min) : 0x80;
- if (tm->tm_hour != -1)
- fm3130->regs[FM3130_ALARM_HOURS] =
- bin2bcd(tm->tm_hour) | 0x80;
+ fm3130->regs[FM3130_ALARM_HOURS] =
+ (tm->tm_hour != -1) ? bin2bcd(tm->tm_hour) : 0x80;
- if (tm->tm_mday != -1)
- fm3130->regs[FM3130_ALARM_DATE] =
- bin2bcd(tm->tm_mday) | 0x80;
+ fm3130->regs[FM3130_ALARM_DATE] =
+ (tm->tm_mday != -1) ? bin2bcd(tm->tm_mday) : 0x80;
- if (tm->tm_mon != -1)
- fm3130->regs[FM3130_ALARM_MONTHS] =
- bin2bcd(tm->tm_mon + 1) | 0x80;
+ fm3130->regs[FM3130_ALARM_MONTHS] =
+ (tm->tm_mon != -1) ? bin2bcd(tm->tm_mon + 1) : 0x80;
dev_dbg(dev, "alarm write %02x %02x %02x %02x %02x\n",
fm3130->regs[FM3130_ALARM_SECONDS],
}
fm3130->regs[FM3130_RTC_CONTROL] =
i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL);
- /* Checking for alarm */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) {
- fm3130->alarm = 1;
- fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF;
- }
+
+ /* enable or disable alarm */
if (alrm->enabled) {
i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL,
(fm3130->regs[FM3130_RTC_CONTROL] &
} else {
i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL,
fm3130->regs[FM3130_RTC_CONTROL] &
- ~(FM3130_RTC_CONTROL_BIT_AEN));
+ ~(FM3130_RTC_CONTROL_BIT_CAL) &
+ ~(FM3130_RTC_CONTROL_BIT_AEN));
}
+
+ /* We assume here that data is valid once written */
+ if (!fm3130->alarm_valid)
+ fm3130->alarm_valid = 1;
+
return 0;
}
+static int fm3130_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct fm3130 *fm3130 = dev_get_drvdata(dev);
+ int ret = 0;
+
+ fm3130->regs[FM3130_RTC_CONTROL] =
+ i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL);
+
+ dev_dbg(dev, "alarm_irq_enable: enable=%d, FM3130_RTC_CONTROL=%02x\n",
+ enabled, fm3130->regs[FM3130_RTC_CONTROL]);
+
+ switch (enabled) {
+ case 0: /* alarm off */
+ ret = i2c_smbus_write_byte_data(fm3130->client,
+ FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_CAL) &
+ ~(FM3130_RTC_CONTROL_BIT_AEN));
+ break;
+ case 1: /* alarm on */
+ ret = i2c_smbus_write_byte_data(fm3130->client,
+ FM3130_RTC_CONTROL, (fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_CAL)) |
+ FM3130_RTC_CONTROL_BIT_AEN);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
static const struct rtc_class_ops fm3130_rtc_ops = {
.read_time = fm3130_get_time,
.set_time = fm3130_set_time,
.read_alarm = fm3130_read_alarm,
.set_alarm = fm3130_set_alarm,
+ .alarm_irq_enable = fm3130_alarm_irq_enable,
};
static struct i2c_driver fm3130_driver;
fm3130->msg[3].len = FM3130_ALARM_REGS;
fm3130->msg[3].buf = &fm3130->regs[FM3130_ALARM_SECONDS];
+ fm3130->alarm_valid = 0;
fm3130->data_valid = 0;
tmp = i2c_transfer(adapter, fm3130->msg, 4);
fm3130->regs[FM3130_CAL_CONTROL] =
i2c_smbus_read_byte_data(client, FM3130_CAL_CONTROL);
- /* Checking for alarm */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) {
- fm3130->alarm = 1;
- fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF;
- }
-
/* Disabling calibration mode */
if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_CAL) {
i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
fm3130->regs[FM3130_CAL_CONTROL] &
~(FM3130_CAL_CONTROL_BIT_nOSCEN));
- /* oscillator fault? clear flag, and warn */
- if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB)
+ /* low battery? clear flag, and warn */
+ if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB) {
+ i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
+ fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_LB));
dev_warn(&client->dev, "Low battery!\n");
+ }
- /* oscillator fault? clear flag, and warn */
+ /* check if Power On Reset bit is set */
if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_POR) {
i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
fm3130->regs[FM3130_RTC_CONTROL] &
~FM3130_RTC_CONTROL_BIT_POR);
- dev_warn(&client->dev, "SET TIME!\n");
+ dev_dbg(&client->dev, "POR bit is set\n");
}
/* ACS is controlled by alarm */
i2c_smbus_write_byte_data(client, FM3130_ALARM_WP_CONTROL, 0x80);
- /* TODO */
- /* TODO need to sanity check alarm */
- tmp = fm3130->regs[FM3130_RTC_SECONDS];
- tmp = bcd2bin(tmp & 0x7f);
- if (tmp > 60)
- goto exit_bad;
+ /* alarm registers sanity check */
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f);
+ if (tmp > 59)
+ goto bad_alarm;
+
tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f);
- if (tmp > 60)
- goto exit_bad;
+ if (tmp > 59)
+ goto bad_alarm;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f);
+ if (tmp > 23)
+ goto bad_alarm;
tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f);
if (tmp == 0 || tmp > 31)
- goto exit_bad;
+ goto bad_alarm;
tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f);
if (tmp == 0 || tmp > 12)
- goto exit_bad;
+ goto bad_alarm;
- tmp = fm3130->regs[FM3130_RTC_HOURS];
+ fm3130->alarm_valid = 1;
+
+bad_alarm:
+
+ /* clock registers sanity chek */
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f);
+ if (tmp > 59)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f);
+ if (tmp > 59)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f);
+ if (tmp > 23)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_DAY] & 0x7);
+ if (tmp == 0 || tmp > 7)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f);
+ if (tmp == 0 || tmp > 31)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f);
+ if (tmp == 0 || tmp > 12)
+ goto bad_clock;
fm3130->data_valid = 1;
-exit_bad:
- if (!fm3130->data_valid)
+bad_clock:
+
+ if (!fm3130->data_valid || !fm3130->alarm_valid)
dev_dbg(&client->dev,
"%s: %02x %02x %02x %02x %02x %02x %02x %02x"
"%02x %02x %02x %02x %02x %02x %02x\n",
--- /dev/null
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2010 Orex Computed Radiography
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/* based on rtc-mc13892.c */
+
+/*
+ * This driver uses the 47-bit 32 kHz counter in the Freescale DryIce block
+ * to implement a Linux RTC. Times and alarms are truncated to seconds.
+ * Since the RTC framework performs API locking via rtc->ops_lock the
+ * only simultaneous accesses we need to deal with is updating DryIce
+ * registers while servicing an alarm.
+ *
+ * Note that reading the DSR (DryIce Status Register) automatically clears
+ * the WCF (Write Complete Flag). All DryIce writes are synchronized to the
+ * LP (Low Power) domain and set the WCF upon completion. Writes to the
+ * DIER (DryIce Interrupt Enable Register) are the only exception. These
+ * occur at normal bus speeds and do not set WCF. Periodic interrupts are
+ * not supported by the hardware.
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/workqueue.h>
+
+/* DryIce Register Definitions */
+
+#define DTCMR 0x00 /* Time Counter MSB Reg */
+#define DTCLR 0x04 /* Time Counter LSB Reg */
+
+#define DCAMR 0x08 /* Clock Alarm MSB Reg */
+#define DCALR 0x0c /* Clock Alarm LSB Reg */
+#define DCAMR_UNSET 0xFFFFFFFF /* doomsday - 1 sec */
+
+#define DCR 0x10 /* Control Reg */
+#define DCR_TCE (1 << 3) /* Time Counter Enable */
+
+#define DSR 0x14 /* Status Reg */
+#define DSR_WBF (1 << 10) /* Write Busy Flag */
+#define DSR_WNF (1 << 9) /* Write Next Flag */
+#define DSR_WCF (1 << 8) /* Write Complete Flag */
+#define DSR_WEF (1 << 7) /* Write Error Flag */
+#define DSR_CAF (1 << 4) /* Clock Alarm Flag */
+#define DSR_NVF (1 << 1) /* Non-Valid Flag */
+#define DSR_SVF (1 << 0) /* Security Violation Flag */
+
+#define DIER 0x18 /* Interrupt Enable Reg */
+#define DIER_WNIE (1 << 9) /* Write Next Interrupt Enable */
+#define DIER_WCIE (1 << 8) /* Write Complete Interrupt Enable */
+#define DIER_WEIE (1 << 7) /* Write Error Interrupt Enable */
+#define DIER_CAIE (1 << 4) /* Clock Alarm Interrupt Enable */
+
+/**
+ * struct imxdi_dev - private imxdi rtc data
+ * @pdev: pionter to platform dev
+ * @rtc: pointer to rtc struct
+ * @ioaddr: IO registers pointer
+ * @irq: dryice normal interrupt
+ * @clk: input reference clock
+ * @dsr: copy of the DSR register
+ * @irq_lock: interrupt enable register (DIER) lock
+ * @write_wait: registers write complete queue
+ * @write_mutex: serialize registers write
+ * @work: schedule alarm work
+ */
+struct imxdi_dev {
+ struct platform_device *pdev;
+ struct rtc_device *rtc;
+ void __iomem *ioaddr;
+ int irq;
+ struct clk *clk;
+ u32 dsr;
+ spinlock_t irq_lock;
+ wait_queue_head_t write_wait;
+ struct mutex write_mutex;
+ struct work_struct work;
+};
+
+/*
+ * enable a dryice interrupt
+ */
+static void di_int_enable(struct imxdi_dev *imxdi, u32 intr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&imxdi->irq_lock, flags);
+ __raw_writel(__raw_readl(imxdi->ioaddr + DIER) | intr,
+ imxdi->ioaddr + DIER);
+ spin_unlock_irqrestore(&imxdi->irq_lock, flags);
+}
+
+/*
+ * disable a dryice interrupt
+ */
+static void di_int_disable(struct imxdi_dev *imxdi, u32 intr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&imxdi->irq_lock, flags);
+ __raw_writel(__raw_readl(imxdi->ioaddr + DIER) & ~intr,
+ imxdi->ioaddr + DIER);
+ spin_unlock_irqrestore(&imxdi->irq_lock, flags);
+}
+
+/*
+ * This function attempts to clear the dryice write-error flag.
+ *
+ * A dryice write error is similar to a bus fault and should not occur in
+ * normal operation. Clearing the flag requires another write, so the root
+ * cause of the problem may need to be fixed before the flag can be cleared.
+ */
+static void clear_write_error(struct imxdi_dev *imxdi)
+{
+ int cnt;
+
+ dev_warn(&imxdi->pdev->dev, "WARNING: Register write error!\n");
+
+ /* clear the write error flag */
+ __raw_writel(DSR_WEF, imxdi->ioaddr + DSR);
+
+ /* wait for it to take effect */
+ for (cnt = 0; cnt < 1000; cnt++) {
+ if ((__raw_readl(imxdi->ioaddr + DSR) & DSR_WEF) == 0)
+ return;
+ udelay(10);
+ }
+ dev_err(&imxdi->pdev->dev,
+ "ERROR: Cannot clear write-error flag!\n");
+}
+
+/*
+ * Write a dryice register and wait until it completes.
+ *
+ * This function uses interrupts to determine when the
+ * write has completed.
+ */
+static int di_write_wait(struct imxdi_dev *imxdi, u32 val, int reg)
+{
+ int ret;
+ int rc = 0;
+
+ /* serialize register writes */
+ mutex_lock(&imxdi->write_mutex);
+
+ /* enable the write-complete interrupt */
+ di_int_enable(imxdi, DIER_WCIE);
+
+ imxdi->dsr = 0;
+
+ /* do the register write */
+ __raw_writel(val, imxdi->ioaddr + reg);
+
+ /* wait for the write to finish */
+ ret = wait_event_interruptible_timeout(imxdi->write_wait,
+ imxdi->dsr & (DSR_WCF | DSR_WEF), msecs_to_jiffies(1));
+ if (ret < 0) {
+ rc = ret;
+ goto out;
+ } else if (ret == 0) {
+ dev_warn(&imxdi->pdev->dev,
+ "Write-wait timeout "
+ "val = 0x%08x reg = 0x%08x\n", val, reg);
+ }
+
+ /* check for write error */
+ if (imxdi->dsr & DSR_WEF) {
+ clear_write_error(imxdi);
+ rc = -EIO;
+ }
+
+out:
+ mutex_unlock(&imxdi->write_mutex);
+
+ return rc;
+}
+
+/*
+ * read the seconds portion of the current time from the dryice time counter
+ */
+static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ unsigned long now;
+
+ now = __raw_readl(imxdi->ioaddr + DTCMR);
+ rtc_time_to_tm(now, tm);
+
+ return 0;
+}
+
+/*
+ * set the seconds portion of dryice time counter and clear the
+ * fractional part.
+ */
+static int dryice_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ int rc;
+
+ /* zero the fractional part first */
+ rc = di_write_wait(imxdi, 0, DTCLR);
+ if (rc == 0)
+ rc = di_write_wait(imxdi, secs, DTCMR);
+
+ return rc;
+}
+
+static int dryice_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+
+ if (enabled)
+ di_int_enable(imxdi, DIER_CAIE);
+ else
+ di_int_disable(imxdi, DIER_CAIE);
+
+ return 0;
+}
+
+/*
+ * read the seconds portion of the alarm register.
+ * the fractional part of the alarm register is always zero.
+ */
+static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ u32 dcamr;
+
+ dcamr = __raw_readl(imxdi->ioaddr + DCAMR);
+ rtc_time_to_tm(dcamr, &alarm->time);
+
+ /* alarm is enabled if the interrupt is enabled */
+ alarm->enabled = (__raw_readl(imxdi->ioaddr + DIER) & DIER_CAIE) != 0;
+
+ /* don't allow the DSR read to mess up DSR_WCF */
+ mutex_lock(&imxdi->write_mutex);
+
+ /* alarm is pending if the alarm flag is set */
+ alarm->pending = (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) != 0;
+
+ mutex_unlock(&imxdi->write_mutex);
+
+ return 0;
+}
+
+/*
+ * set the seconds portion of dryice alarm register
+ */
+static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ unsigned long now;
+ unsigned long alarm_time;
+ int rc;
+
+ rc = rtc_tm_to_time(&alarm->time, &alarm_time);
+ if (rc)
+ return rc;
+
+ /* don't allow setting alarm in the past */
+ now = __raw_readl(imxdi->ioaddr + DTCMR);
+ if (alarm_time < now)
+ return -EINVAL;
+
+ /* write the new alarm time */
+ rc = di_write_wait(imxdi, (u32)alarm_time, DCAMR);
+ if (rc)
+ return rc;
+
+ if (alarm->enabled)
+ di_int_enable(imxdi, DIER_CAIE); /* enable alarm intr */
+ else
+ di_int_disable(imxdi, DIER_CAIE); /* disable alarm intr */
+
+ return 0;
+}
+
+static struct rtc_class_ops dryice_rtc_ops = {
+ .read_time = dryice_rtc_read_time,
+ .set_mmss = dryice_rtc_set_mmss,
+ .alarm_irq_enable = dryice_rtc_alarm_irq_enable,
+ .read_alarm = dryice_rtc_read_alarm,
+ .set_alarm = dryice_rtc_set_alarm,
+};
+
+/*
+ * dryice "normal" interrupt handler
+ */
+static irqreturn_t dryice_norm_irq(int irq, void *dev_id)
+{
+ struct imxdi_dev *imxdi = dev_id;
+ u32 dsr, dier;
+ irqreturn_t rc = IRQ_NONE;
+
+ dier = __raw_readl(imxdi->ioaddr + DIER);
+
+ /* handle write complete and write error cases */
+ if ((dier & DIER_WCIE)) {
+ /*If the write wait queue is empty then there is no pending
+ operations. It means the interrupt is for DryIce -Security.
+ IRQ must be returned as none.*/
+ if (list_empty_careful(&imxdi->write_wait.task_list))
+ return rc;
+
+ /* DSR_WCF clears itself on DSR read */
+ dsr = __raw_readl(imxdi->ioaddr + DSR);
+ if ((dsr & (DSR_WCF | DSR_WEF))) {
+ /* mask the interrupt */
+ di_int_disable(imxdi, DIER_WCIE);
+
+ /* save the dsr value for the wait queue */
+ imxdi->dsr |= dsr;
+
+ wake_up_interruptible(&imxdi->write_wait);
+ rc = IRQ_HANDLED;
+ }
+ }
+
+ /* handle the alarm case */
+ if ((dier & DIER_CAIE)) {
+ /* DSR_WCF clears itself on DSR read */
+ dsr = __raw_readl(imxdi->ioaddr + DSR);
+ if (dsr & DSR_CAF) {
+ /* mask the interrupt */
+ di_int_disable(imxdi, DIER_CAIE);
+
+ /* finish alarm in user context */
+ schedule_work(&imxdi->work);
+ rc = IRQ_HANDLED;
+ }
+ }
+ return rc;
+}
+
+/*
+ * post the alarm event from user context so it can sleep
+ * on the write completion.
+ */
+static void dryice_work(struct work_struct *work)
+{
+ struct imxdi_dev *imxdi = container_of(work,
+ struct imxdi_dev, work);
+
+ /* dismiss the interrupt (ignore error) */
+ di_write_wait(imxdi, DSR_CAF, DSR);
+
+ /* pass the alarm event to the rtc framework. */
+ rtc_update_irq(imxdi->rtc, 1, RTC_AF | RTC_IRQF);
+}
+
+/*
+ * probe for dryice rtc device
+ */
+static int dryice_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct imxdi_dev *imxdi;
+ int rc;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ imxdi = devm_kzalloc(&pdev->dev, sizeof(*imxdi), GFP_KERNEL);
+ if (!imxdi)
+ return -ENOMEM;
+
+ imxdi->pdev = pdev;
+
+ if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
+ pdev->name))
+ return -EBUSY;
+
+ imxdi->ioaddr = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (imxdi->ioaddr == NULL)
+ return -ENOMEM;
+
+ imxdi->irq = platform_get_irq(pdev, 0);
+ if (imxdi->irq < 0)
+ return imxdi->irq;
+
+ init_waitqueue_head(&imxdi->write_wait);
+
+ INIT_WORK(&imxdi->work, dryice_work);
+
+ mutex_init(&imxdi->write_mutex);
+
+ imxdi->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(imxdi->clk))
+ return PTR_ERR(imxdi->clk);
+ clk_enable(imxdi->clk);
+
+ /*
+ * Initialize dryice hardware
+ */
+
+ /* mask all interrupts */
+ __raw_writel(0, imxdi->ioaddr + DIER);
+
+ rc = devm_request_irq(&pdev->dev, imxdi->irq, dryice_norm_irq,
+ IRQF_SHARED, pdev->name, imxdi);
+ if (rc) {
+ dev_warn(&pdev->dev, "interrupt not available.\n");
+ goto err;
+ }
+
+ /* put dryice into valid state */
+ if (__raw_readl(imxdi->ioaddr + DSR) & DSR_NVF) {
+ rc = di_write_wait(imxdi, DSR_NVF | DSR_SVF, DSR);
+ if (rc)
+ goto err;
+ }
+
+ /* initialize alarm */
+ rc = di_write_wait(imxdi, DCAMR_UNSET, DCAMR);
+ if (rc)
+ goto err;
+ rc = di_write_wait(imxdi, 0, DCALR);
+ if (rc)
+ goto err;
+
+ /* clear alarm flag */
+ if (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) {
+ rc = di_write_wait(imxdi, DSR_CAF, DSR);
+ if (rc)
+ goto err;
+ }
+
+ /* the timer won't count if it has never been written to */
+ if (__raw_readl(imxdi->ioaddr + DTCMR) == 0) {
+ rc = di_write_wait(imxdi, 0, DTCMR);
+ if (rc)
+ goto err;
+ }
+
+ /* start keeping time */
+ if (!(__raw_readl(imxdi->ioaddr + DCR) & DCR_TCE)) {
+ rc = di_write_wait(imxdi,
+ __raw_readl(imxdi->ioaddr + DCR) | DCR_TCE,
+ DCR);
+ if (rc)
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, imxdi);
+ imxdi->rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &dryice_rtc_ops, THIS_MODULE);
+ if (IS_ERR(imxdi->rtc)) {
+ rc = PTR_ERR(imxdi->rtc);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ clk_disable(imxdi->clk);
+ clk_put(imxdi->clk);
+
+ return rc;
+}
+
+static int __devexit dryice_rtc_remove(struct platform_device *pdev)
+{
+ struct imxdi_dev *imxdi = platform_get_drvdata(pdev);
+
+ flush_work(&imxdi->work);
+
+ /* mask all interrupts */
+ __raw_writel(0, imxdi->ioaddr + DIER);
+
+ rtc_device_unregister(imxdi->rtc);
+
+ clk_disable(imxdi->clk);
+ clk_put(imxdi->clk);
+
+ return 0;
+}
+
+static struct platform_driver dryice_rtc_driver = {
+ .driver = {
+ .name = "imxdi_rtc",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(dryice_rtc_remove),
+};
+
+static int __init dryice_rtc_init(void)
+{
+ return platform_driver_probe(&dryice_rtc_driver, dryice_rtc_probe);
+}
+
+static void __exit dryice_rtc_exit(void)
+{
+ platform_driver_unregister(&dryice_rtc_driver);
+}
+
+module_init(dryice_rtc_init);
+module_exit(dryice_rtc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
+MODULE_DESCRIPTION("IMX DryIce Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * An I2C driver for the Intersil ISL 12022
+ *
+ * Author: Roman Fietze <roman.fietze@telemotive.de>
+ *
+ * Based on the Philips PCF8563 RTC
+ * by Alessandro Zummo <a.zummo@towertech.it>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+
+#define DRV_VERSION "0.1"
+
+/* ISL register offsets */
+#define ISL12022_REG_SC 0x00
+#define ISL12022_REG_MN 0x01
+#define ISL12022_REG_HR 0x02
+#define ISL12022_REG_DT 0x03
+#define ISL12022_REG_MO 0x04
+#define ISL12022_REG_YR 0x05
+#define ISL12022_REG_DW 0x06
+
+#define ISL12022_REG_SR 0x07
+#define ISL12022_REG_INT 0x08
+
+/* ISL register bits */
+#define ISL12022_HR_MIL (1 << 7) /* military or 24 hour time */
+
+#define ISL12022_SR_LBAT85 (1 << 2)
+#define ISL12022_SR_LBAT75 (1 << 1)
+
+#define ISL12022_INT_WRTC (1 << 6)
+
+
+static struct i2c_driver isl12022_driver;
+
+struct isl12022 {
+ struct rtc_device *rtc;
+
+ bool write_enabled; /* true if write enable is set */
+};
+
+
+static int isl12022_read_regs(struct i2c_client *client, uint8_t reg,
+ uint8_t *data, size_t n)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = data
+ }, /* setup read ptr */
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = n,
+ .buf = data
+ }
+ };
+
+ int ret;
+
+ data[0] = reg;
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs)) {
+ dev_err(&client->dev, "%s: read error, ret=%d\n",
+ __func__, ret);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+static int isl12022_write_reg(struct i2c_client *client,
+ uint8_t reg, uint8_t val)
+{
+ uint8_t data[2] = { reg, val };
+ int err;
+
+ err = i2c_master_send(client, data, sizeof(data));
+ if (err != sizeof(data)) {
+ dev_err(&client->dev,
+ "%s: err=%d addr=%02x, data=%02x\n",
+ __func__, err, data[0], data[1]);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+/*
+ * In the routines that deal directly with the isl12022 hardware, we use
+ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
+ */
+static int isl12022_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ uint8_t buf[ISL12022_REG_INT + 1];
+ int ret;
+
+ ret = isl12022_read_regs(client, ISL12022_REG_SC, buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ if (buf[ISL12022_REG_SR] & (ISL12022_SR_LBAT85 | ISL12022_SR_LBAT75)) {
+ dev_warn(&client->dev,
+ "voltage dropped below %u%%, "
+ "date and time is not reliable.\n",
+ buf[ISL12022_REG_SR] & ISL12022_SR_LBAT85 ? 85 : 75);
+ }
+
+ dev_dbg(&client->dev,
+ "%s: raw data is sec=%02x, min=%02x, hr=%02x, "
+ "mday=%02x, mon=%02x, year=%02x, wday=%02x, "
+ "sr=%02x, int=%02x",
+ __func__,
+ buf[ISL12022_REG_SC],
+ buf[ISL12022_REG_MN],
+ buf[ISL12022_REG_HR],
+ buf[ISL12022_REG_DT],
+ buf[ISL12022_REG_MO],
+ buf[ISL12022_REG_YR],
+ buf[ISL12022_REG_DW],
+ buf[ISL12022_REG_SR],
+ buf[ISL12022_REG_INT]);
+
+ tm->tm_sec = bcd2bin(buf[ISL12022_REG_SC] & 0x7F);
+ tm->tm_min = bcd2bin(buf[ISL12022_REG_MN] & 0x7F);
+ tm->tm_hour = bcd2bin(buf[ISL12022_REG_HR] & 0x3F);
+ tm->tm_mday = bcd2bin(buf[ISL12022_REG_DT] & 0x3F);
+ tm->tm_wday = buf[ISL12022_REG_DW] & 0x07;
+ tm->tm_mon = bcd2bin(buf[ISL12022_REG_MO] & 0x1F) - 1;
+ tm->tm_year = bcd2bin(buf[ISL12022_REG_YR]) + 100;
+
+ dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ /* The clock can give out invalid datetime, but we cannot return
+ * -EINVAL otherwise hwclock will refuse to set the time on bootup. */
+ if (rtc_valid_tm(tm) < 0)
+ dev_err(&client->dev, "retrieved date and time is invalid.\n");
+
+ return 0;
+}
+
+static int isl12022_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ struct isl12022 *isl12022 = i2c_get_clientdata(client);
+ size_t i;
+ int ret;
+ uint8_t buf[ISL12022_REG_DW + 1];
+
+ dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ if (!isl12022->write_enabled) {
+
+ ret = isl12022_read_regs(client, ISL12022_REG_INT, buf, 1);
+ if (ret)
+ return ret;
+
+ /* Check if WRTC (write rtc enable) is set factory default is
+ * 0 (not set) */
+ if (!(buf[0] & ISL12022_INT_WRTC)) {
+ dev_info(&client->dev,
+ "init write enable and 24 hour format\n");
+
+ /* Set the write enable bit. */
+ ret = isl12022_write_reg(client,
+ ISL12022_REG_INT,
+ buf[0] | ISL12022_INT_WRTC);
+ if (ret)
+ return ret;
+
+ /* Write to any RTC register to start RTC, we use the
+ * HR register, setting the MIL bit to use the 24 hour
+ * format. */
+ ret = isl12022_read_regs(client, ISL12022_REG_HR,
+ buf, 1);
+ if (ret)
+ return ret;
+
+ ret = isl12022_write_reg(client,
+ ISL12022_REG_HR,
+ buf[0] | ISL12022_HR_MIL);
+ if (ret)
+ return ret;
+ }
+
+ isl12022->write_enabled = 1;
+ }
+
+ /* hours, minutes and seconds */
+ buf[ISL12022_REG_SC] = bin2bcd(tm->tm_sec);
+ buf[ISL12022_REG_MN] = bin2bcd(tm->tm_min);
+ buf[ISL12022_REG_HR] = bin2bcd(tm->tm_hour) | ISL12022_HR_MIL;
+
+ buf[ISL12022_REG_DT] = bin2bcd(tm->tm_mday);
+
+ /* month, 1 - 12 */
+ buf[ISL12022_REG_MO] = bin2bcd(tm->tm_mon + 1);
+
+ /* year and century */
+ buf[ISL12022_REG_YR] = bin2bcd(tm->tm_year % 100);
+
+ buf[ISL12022_REG_DW] = tm->tm_wday & 0x07;
+
+ /* write register's data */
+ for (i = 0; i < ARRAY_SIZE(buf); i++) {
+ ret = isl12022_write_reg(client, ISL12022_REG_SC + i,
+ buf[ISL12022_REG_SC + i]);
+ if (ret)
+ return -EIO;
+ };
+
+ return 0;
+}
+
+static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return isl12022_get_datetime(to_i2c_client(dev), tm);
+}
+
+static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return isl12022_set_datetime(to_i2c_client(dev), tm);
+}
+
+static const struct rtc_class_ops isl12022_rtc_ops = {
+ .read_time = isl12022_rtc_read_time,
+ .set_time = isl12022_rtc_set_time,
+};
+
+static int isl12022_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct isl12022 *isl12022;
+
+ int ret = 0;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ isl12022 = kzalloc(sizeof(struct isl12022), GFP_KERNEL);
+ if (!isl12022)
+ return -ENOMEM;
+
+ dev_dbg(&client->dev, "chip found, driver version " DRV_VERSION "\n");
+
+ i2c_set_clientdata(client, isl12022);
+
+ isl12022->rtc = rtc_device_register(isl12022_driver.driver.name,
+ &client->dev,
+ &isl12022_rtc_ops,
+ THIS_MODULE);
+
+ if (IS_ERR(isl12022->rtc)) {
+ ret = PTR_ERR(isl12022->rtc);
+ goto exit_kfree;
+ }
+
+ return 0;
+
+exit_kfree:
+ kfree(isl12022);
+
+ return ret;
+}
+
+static int isl12022_remove(struct i2c_client *client)
+{
+ struct isl12022 *isl12022 = i2c_get_clientdata(client);
+
+ rtc_device_unregister(isl12022->rtc);
+ kfree(isl12022);
+
+ return 0;
+}
+
+static const struct i2c_device_id isl12022_id[] = {
+ { "isl12022", 0 },
+ { "rtc8564", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, isl12022_id);
+
+static struct i2c_driver isl12022_driver = {
+ .driver = {
+ .name = "rtc-isl12022",
+ },
+ .probe = isl12022_probe,
+ .remove = isl12022_remove,
+ .id_table = isl12022_id,
+};
+
+static int __init isl12022_init(void)
+{
+ return i2c_add_driver(&isl12022_driver);
+}
+
+static void __exit isl12022_exit(void)
+{
+ i2c_del_driver(&isl12022_driver);
+}
+
+module_init(isl12022_init);
+module_exit(isl12022_exit);
+
+MODULE_AUTHOR("roman.fietze@telemotive.de");
+MODULE_DESCRIPTION("ISL 12022 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
/* assume 20YY not 19YY, and ignore the Century Bit */
tm->tm_year = bcd2bin(buf[M41T80_REG_YEAR]) + 100;
- return 0;
+ return rtc_valid_tm(tm);
}
/* Sets the given date and time to the real time clock. */
t->time.tm_isdst = -1;
t->enabled = !!(reg[M41T80_REG_ALARM_MON] & M41T80_ALMON_AFE);
t->pending = !!(reg[M41T80_REG_FLAGS] & M41T80_FLAGS_AF);
- return 0;
+ return rtc_valid_tm(t);
}
static struct rtc_class_ops m41t80_rtc_ops = {
dev_dbg(dev, "RTC read time %04d-%02d-%02d %02d/%02d/%02d\n",
tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
- return 0;
+ return rtc_valid_tm(tm);
}
static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm)
dev_dbg(dev, "RTC read alarm time %04d-%02d-%02d %02d/%02d/%02d\n",
tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
- return 0;
+ return rtc_valid_tm(tm);
}
/*
free_irq(m48t59->irq, &pdev->dev);
if (m48t59->ioaddr)
iounmap(m48t59->ioaddr);
- if (m48t59)
kfree(m48t59);
return ret;
}
if (ops->readbyte(M48T86_REG_HOUR) & 0x80)
tm->tm_hour += 12;
- return 0;
+ return rtc_valid_tm(tm);
}
static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm)
bcd2bin(regs[MAX6900_REG_CENTURY]) * 100 - 1900;
tm->tm_wday = bcd2bin(regs[MAX6900_REG_DW]);
- return 0;
+ return rtc_valid_tm(tm);
}
static int max6900_i2c_clear_write_protect(struct i2c_client *client)
void __iomem *ioaddr;
int irq;
struct clk *clk;
- unsigned int irqen;
- int alrm_sec;
- int alrm_min;
- int alrm_hour;
- int alrm_mday;
- struct timespec mxc_rtc_delta;
struct rtc_time g_rtc_alarm;
};
static int *check_rtc_access_enable(struct nuc900_rtc *nuc900_rtc)
{
- unsigned int i;
+ unsigned int timeout = 0x1000;
__raw_writel(INIRRESET, nuc900_rtc->rtc_reg + REG_RTC_INIR);
mdelay(10);
__raw_writel(AERPOWERON, nuc900_rtc->rtc_reg + REG_RTC_AER);
- for (i = 0; i < 1000; i++) {
- if (__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB)
- return 0;
- }
+ while (!(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB)
+ && timeout--)
+ mdelay(1);
- if ((__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB) == 0x0)
- return ERR_PTR(-ENODEV);
+ if (!timeout)
+ return ERR_PTR(-EPERM);
- return ERR_PTR(-EPERM);
+ return 0;
}
-static void nuc900_rtc_bcd2bin(unsigned int timereg,
+static int nuc900_rtc_bcd2bin(unsigned int timereg,
unsigned int calreg, struct rtc_time *tm)
{
tm->tm_mday = bcd2bin(calreg >> 0);
tm->tm_min = bcd2bin(timereg >> 8);
tm->tm_hour = bcd2bin(timereg >> 16);
- rtc_valid_tm(tm);
+ return rtc_valid_tm(tm);
}
-static void nuc900_rtc_bin2bcd(struct rtc_time *settm,
+static void nuc900_rtc_bin2bcd(struct device *dev, struct rtc_time *settm,
struct nuc900_bcd_time *gettm)
{
gettm->bcd_mday = bin2bcd(settm->tm_mday) << 0;
gettm->bcd_mon = bin2bcd(settm->tm_mon) << 8;
- gettm->bcd_year = bin2bcd(settm->tm_year - 100) << 16;
+
+ if (settm->tm_year < 100) {
+ dev_warn(dev, "The year will be between 1970-1999, right?\n");
+ gettm->bcd_year = bin2bcd(settm->tm_year) << 16;
+ } else {
+ gettm->bcd_year = bin2bcd(settm->tm_year - 100) << 16;
+ }
gettm->bcd_sec = bin2bcd(settm->tm_sec) << 0;
gettm->bcd_min = bin2bcd(settm->tm_min) << 8;
timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TLR);
clrval = __raw_readl(rtc->rtc_reg + REG_RTC_CLR);
- nuc900_rtc_bcd2bin(timeval, clrval, tm);
-
- return 0;
+ return nuc900_rtc_bcd2bin(timeval, clrval, tm);
}
static int nuc900_rtc_set_time(struct device *dev, struct rtc_time *tm)
unsigned long val;
int *err;
- nuc900_rtc_bin2bcd(tm, &gettm);
+ nuc900_rtc_bin2bcd(dev, tm, &gettm);
err = check_rtc_access_enable(rtc);
if (IS_ERR(err))
timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TAR);
carval = __raw_readl(rtc->rtc_reg + REG_RTC_CAR);
- nuc900_rtc_bcd2bin(timeval, carval, &alrm->time);
-
- return 0;
+ return nuc900_rtc_bcd2bin(timeval, carval, &alrm->time);
}
static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
unsigned long val;
int *err;
- nuc900_rtc_bin2bcd(&alrm->time, &tm);
+ nuc900_rtc_bin2bcd(dev, &alrm->time, &tm);
err = check_rtc_access_enable(rtc);
if (IS_ERR(err))
goto fail2;
}
- nuc900_rtc->irq_num = platform_get_irq(pdev, 0);
- if (request_irq(nuc900_rtc->irq_num, nuc900_rtc_interrupt,
- IRQF_DISABLED, "nuc900rtc", nuc900_rtc)) {
- dev_err(&pdev->dev, "NUC900 RTC request irq failed\n");
- err = -EBUSY;
- goto fail3;
- }
+ platform_set_drvdata(pdev, nuc900_rtc);
nuc900_rtc->rtcdev = rtc_device_register(pdev->name, &pdev->dev,
&nuc900_rtc_ops, THIS_MODULE);
if (IS_ERR(nuc900_rtc->rtcdev)) {
dev_err(&pdev->dev, "rtc device register faild\n");
err = PTR_ERR(nuc900_rtc->rtcdev);
- goto fail4;
+ goto fail3;
}
- platform_set_drvdata(pdev, nuc900_rtc);
__raw_writel(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_TSSR) | MODE24,
nuc900_rtc->rtc_reg + REG_RTC_TSSR);
+ nuc900_rtc->irq_num = platform_get_irq(pdev, 0);
+ if (request_irq(nuc900_rtc->irq_num, nuc900_rtc_interrupt,
+ IRQF_DISABLED, "nuc900rtc", nuc900_rtc)) {
+ dev_err(&pdev->dev, "NUC900 RTC request irq failed\n");
+ err = -EBUSY;
+ goto fail4;
+ }
+
return 0;
-fail4: free_irq(nuc900_rtc->irq_num, nuc900_rtc);
+fail4: rtc_device_unregister(nuc900_rtc->rtcdev);
fail3: iounmap(nuc900_rtc->rtc_reg);
fail2: release_mem_region(res->start, resource_size(res));
fail1: kfree(nuc900_rtc);
struct nuc900_rtc *nuc900_rtc = platform_get_drvdata(pdev);
struct resource *res;
- rtc_device_unregister(nuc900_rtc->rtcdev);
free_irq(nuc900_rtc->irq_num, nuc900_rtc);
+ rtc_device_unregister(nuc900_rtc->rtcdev);
iounmap(nuc900_rtc->rtc_reg);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
return 0;
}
-struct pcf8563_limit
-{
- unsigned char reg;
- unsigned char mask;
- unsigned char min;
- unsigned char max;
-};
-
static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
return pcf8563_get_datetime(to_i2c_client(dev), tm);
#include <linux/io.h>
#include <linux/bcd.h>
#include <linux/delay.h>
-#include <linux/version.h>
#include <linux/slab.h>
/*
int irq_Alrm;
struct rtc_device *rtc;
spinlock_t lock; /* Protects this structure */
- struct rtc_time rtc_alarm;
};
static u32 ryxr_calc(struct rtc_time *tm)
return 0;
}
-static int pxa_rtc_ioctl(struct device *dev, unsigned int cmd,
- unsigned long arg)
+static int pxa_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
- int ret = 0;
spin_lock_irq(&pxa_rtc->lock);
- switch (cmd) {
- case RTC_AIE_OFF:
- rtsr_clear_bits(pxa_rtc, RTSR_RDALE1);
- break;
- case RTC_AIE_ON:
+
+ if (enabled)
rtsr_set_bits(pxa_rtc, RTSR_RDALE1);
- break;
- case RTC_UIE_OFF:
- rtsr_clear_bits(pxa_rtc, RTSR_HZE);
- break;
- case RTC_UIE_ON:
+ else
+ rtsr_clear_bits(pxa_rtc, RTSR_RDALE1);
+
+ spin_unlock_irq(&pxa_rtc->lock);
+ return 0;
+}
+
+static int pxa_update_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
+
+ spin_lock_irq(&pxa_rtc->lock);
+
+ if (enabled)
rtsr_set_bits(pxa_rtc, RTSR_HZE);
- break;
- default:
- ret = -ENOIOCTLCMD;
- }
+ else
+ rtsr_clear_bits(pxa_rtc, RTSR_HZE);
spin_unlock_irq(&pxa_rtc->lock);
- return ret;
+ return 0;
}
static int pxa_rtc_read_time(struct device *dev, struct rtc_time *tm)
static const struct rtc_class_ops pxa_rtc_ops = {
.open = pxa_rtc_open,
.release = pxa_rtc_release,
- .ioctl = pxa_rtc_ioctl,
.read_time = pxa_rtc_read_time,
.set_time = pxa_rtc_set_time,
.read_alarm = pxa_rtc_read_alarm,
.set_alarm = pxa_rtc_set_alarm,
+ .alarm_irq_enable = pxa_alarm_irq_enable,
+ .update_irq_enable = pxa_update_irq_enable,
.proc = pxa_rtc_proc,
.irq_set_state = pxa_periodic_irq_set_state,
.irq_set_freq = pxa_periodic_irq_set_freq,
struct rp5c01_priv {
u32 __iomem *regs;
struct rtc_device *rtc;
+ spinlock_t lock; /* against concurrent RTC/NVRAM access */
+ struct bin_attribute nvram_attr;
};
static inline unsigned int rp5c01_read(struct rp5c01_priv *priv,
{
struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ spin_lock_irq(&priv->lock);
rp5c01_lock(priv);
tm->tm_sec = rp5c01_read(priv, RP5C01_10_SECOND) * 10 +
tm->tm_year += 100;
rp5c01_unlock(priv);
+ spin_unlock_irq(&priv->lock);
return rtc_valid_tm(tm);
}
{
struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ spin_lock_irq(&priv->lock);
rp5c01_lock(priv);
rp5c01_write(priv, tm->tm_sec / 10, RP5C01_10_SECOND);
rp5c01_write(priv, tm->tm_year % 10, RP5C01_1_YEAR);
rp5c01_unlock(priv);
+ spin_unlock_irq(&priv->lock);
return 0;
}
.set_time = rp5c01_set_time,
};
+
+/*
+ * The NVRAM is organized as 2 blocks of 13 nibbles of 4 bits.
+ * We provide access to them like AmigaOS does: the high nibble of each 8-bit
+ * byte is stored in BLOCK10, the low nibble in BLOCK11.
+ */
+
+static ssize_t rp5c01_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ ssize_t count;
+
+ spin_lock_irq(&priv->lock);
+
+ for (count = 0; size > 0 && pos < RP5C01_MODE; count++, size--) {
+ u8 data;
+
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK10,
+ RP5C01_MODE);
+ data = rp5c01_read(priv, pos) << 4;
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK11,
+ RP5C01_MODE);
+ data |= rp5c01_read(priv, pos++);
+ rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01,
+ RP5C01_MODE);
+ *buf++ = data;
+ }
+
+ spin_unlock_irq(&priv->lock);
+ return count;
+}
+
+static ssize_t rp5c01_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ ssize_t count;
+
+ spin_lock_irq(&priv->lock);
+
+ for (count = 0; size > 0 && pos < RP5C01_MODE; count++, size--) {
+ u8 data = *buf++;
+
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK10,
+ RP5C01_MODE);
+ rp5c01_write(priv, data >> 4, pos);
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK11,
+ RP5C01_MODE);
+ rp5c01_write(priv, data & 0xf, pos++);
+ rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01,
+ RP5C01_MODE);
+ }
+
+ spin_unlock_irq(&priv->lock);
+ return count;
+}
+
static int __init rp5c01_rtc_probe(struct platform_device *dev)
{
struct resource *res;
goto out_free_priv;
}
+ sysfs_bin_attr_init(&priv->nvram_attr);
+ priv->nvram_attr.attr.name = "nvram";
+ priv->nvram_attr.attr.mode = S_IRUGO | S_IWUSR;
+ priv->nvram_attr.read = rp5c01_nvram_read;
+ priv->nvram_attr.write = rp5c01_nvram_write;
+ priv->nvram_attr.size = RP5C01_MODE;
+
+ spin_lock_init(&priv->lock);
+
rtc = rtc_device_register("rtc-rp5c01", &dev->dev, &rp5c01_rtc_ops,
THIS_MODULE);
if (IS_ERR(rtc)) {
priv->rtc = rtc;
platform_set_drvdata(dev, priv);
+
+ error = sysfs_create_bin_file(&dev->dev.kobj, &priv->nvram_attr);
+ if (error)
+ goto out_unregister;
+
return 0;
+out_unregister:
+ rtc_device_unregister(rtc);
out_unmap:
iounmap(priv->regs);
out_free_priv:
{
struct rp5c01_priv *priv = platform_get_drvdata(dev);
+ sysfs_remove_bin_file(&dev->dev.kobj, &priv->nvram_attr);
rtc_device_unregister(priv->rtc);
iounmap(priv->regs);
kfree(priv);
/* drivers/rtc/rtc-s3c.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
*
* Copyright (c) 2004,2006 Simtec Electronics
* Ben Dooks, <ben@simtec.co.uk>
static struct resource *s3c_rtc_mem;
+static struct clk *rtc_clk;
static void __iomem *s3c_rtc_base;
static int s3c_rtc_alarmno = NO_IRQ;
static int s3c_rtc_tickno = NO_IRQ;
struct rtc_device *rdev = id;
rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);
+
+ if (s3c_rtc_cpu_type == TYPE_S3C64XX)
+ writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP);
+
return IRQ_HANDLED;
}
struct rtc_device *rdev = id;
rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);
+
+ if (s3c_rtc_cpu_type == TYPE_S3C64XX)
+ writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP);
+
return IRQ_HANDLED;
}
if (enabled)
tmp |= S3C64XX_RTCCON_TICEN;
- writeb(tmp, s3c_rtc_base + S3C2410_RTCCON);
+ writew(tmp, s3c_rtc_base + S3C2410_RTCCON);
} else {
tmp = readb(s3c_rtc_base + S3C2410_TICNT);
tmp &= ~S3C2410_TICNT_ENABLE;
tmp |= (rtc_dev->max_user_freq / freq)-1;
- writeb(tmp, s3c_rtc_base + S3C2410_TICNT);
+ writel(tmp, s3c_rtc_base + S3C2410_TICNT);
spin_unlock_irq(&s3c_rtc_pie_lock);
return 0;
s3c_rtc_setpie(&dev->dev, 0);
s3c_rtc_setaie(0);
+ clk_disable(rtc_clk);
+ clk_put(rtc_clk);
+ rtc_clk = NULL;
+
iounmap(s3c_rtc_base);
release_resource(s3c_rtc_mem);
kfree(s3c_rtc_mem);
{
struct rtc_device *rtc;
struct resource *res;
+ unsigned int tmp, i;
int ret;
pr_debug("%s: probe=%p\n", __func__, pdev);
goto err_nomap;
}
+ rtc_clk = clk_get(&pdev->dev, "rtc");
+ if (IS_ERR(rtc_clk)) {
+ dev_err(&pdev->dev, "failed to find rtc clock source\n");
+ ret = PTR_ERR(rtc_clk);
+ rtc_clk = NULL;
+ goto err_clk;
+ }
+
+ clk_enable(rtc_clk);
+
/* check to see if everything is setup correctly */
s3c_rtc_enable(pdev, 1);
s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data;
+ /* Check RTC Time */
+
+ for (i = S3C2410_RTCSEC; i <= S3C2410_RTCYEAR; i += 0x4) {
+ tmp = readb(s3c_rtc_base + i);
+
+ if ((tmp & 0xf) > 0x9 || ((tmp >> 4) & 0xf) > 0x9)
+ writeb(0, s3c_rtc_base + i);
+ }
+
if (s3c_rtc_cpu_type == TYPE_S3C64XX)
rtc->max_user_freq = 32768;
else
err_nortc:
s3c_rtc_enable(pdev, 0);
+ clk_disable(rtc_clk);
+ clk_put(rtc_clk);
+
+ err_clk:
iounmap(s3c_rtc_base);
err_nomap:
#include <linux/hdreg.h>
#include <linux/async.h>
#include <linux/mutex.h>
+#include <linux/smp_lock.h>
#include <asm/ccwdev.h>
#include <asm/ebcdic.h>
*/
blk_queue_max_segment_size(block->request_queue, PAGE_SIZE);
blk_queue_segment_boundary(block->request_queue, PAGE_SIZE - 1);
- blk_queue_ordered(block->request_queue, QUEUE_ORDERED_DRAIN, NULL);
+ blk_queue_ordered(block->request_queue, QUEUE_ORDERED_DRAIN);
}
/*
if (!block)
return -ENODEV;
+ lock_kernel();
base = block->base;
atomic_inc(&block->open_count);
if (test_bit(DASD_FLAG_OFFLINE, &base->flags)) {
goto out;
}
+ unlock_kernel();
return 0;
out:
module_put(base->discipline->owner);
unlock:
atomic_dec(&block->open_count);
+ unlock_kernel();
return rc;
}
{
struct dasd_block *block = disk->private_data;
+ lock_kernel();
atomic_dec(&block->open_count);
module_put(block->base->discipline->owner);
+ unlock_kernel();
return 0;
}
static DEVICE_ATTR(eer_enabled, 0644, dasd_eer_show, dasd_eer_store);
+/*
+ * expiration time for default requests
+ */
+static ssize_t
+dasd_expires_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct dasd_device *device;
+ int len;
+
+ device = dasd_device_from_cdev(to_ccwdev(dev));
+ if (IS_ERR(device))
+ return -ENODEV;
+ len = snprintf(buf, PAGE_SIZE, "%lu\n", device->default_expires);
+ dasd_put_device(device);
+ return len;
+}
+
+static ssize_t
+dasd_expires_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct dasd_device *device;
+ unsigned long val;
+
+ device = dasd_device_from_cdev(to_ccwdev(dev));
+ if (IS_ERR(device))
+ return -ENODEV;
+
+ if ((strict_strtoul(buf, 10, &val) != 0) ||
+ (val > DASD_EXPIRES_MAX) || val == 0) {
+ dasd_put_device(device);
+ return -EINVAL;
+ }
+
+ if (val)
+ device->default_expires = val;
+
+ dasd_put_device(device);
+ return count;
+}
+
+static DEVICE_ATTR(expires, 0644, dasd_expires_show, dasd_expires_store);
+
static struct attribute * dasd_attrs[] = {
&dev_attr_readonly.attr,
&dev_attr_discipline.attr,
&dev_attr_eer_enabled.attr,
&dev_attr_erplog.attr,
&dev_attr_failfast.attr,
+ &dev_attr_expires.attr,
NULL,
};
sizeof(struct dasd_diag_req)) / \
sizeof(struct dasd_diag_bio)) / 2)
#define DIAG_MAX_RETRIES 32
-#define DIAG_TIMEOUT 50 * HZ
+#define DIAG_TIMEOUT 50
static struct dasd_discipline dasd_diag_discipline;
goto out;
}
+ device->default_expires = DIAG_TIMEOUT;
+
/* Figure out position of label block */
switch (private->rdc_data.vdev_class) {
case DEV_CLASS_FBA:
cqr->startdev = memdev;
cqr->memdev = memdev;
cqr->block = block;
- cqr->expires = DIAG_TIMEOUT;
+ cqr->expires = memdev->default_expires * HZ;
cqr->status = DASD_CQR_FILLED;
return cqr;
}
#define INIT_CQR_UNFORMATTED 1
#define INIT_CQR_ERROR 2
+/* emergency request for reserve/release */
+static struct {
+ struct dasd_ccw_req cqr;
+ struct ccw1 ccw;
+ char data[32];
+} *dasd_reserve_req;
+static DEFINE_MUTEX(dasd_reserve_mutex);
+
/* initial attempt at a probe function. this can be simplified once
* the other detection code is gone */
struct dasd_eckd_private *private;
struct dasd_block *block;
struct dasd_uid temp_uid;
- int is_known, rc;
+ int is_known, rc, i;
int readonly;
+ unsigned long value;
if (!ccw_device_is_pathgroup(device->cdev)) {
dev_warn(&device->cdev->dev,
if (rc)
goto out_err1;
+ /* set default timeout */
+ device->default_expires = DASD_EXPIRES;
+ if (private->gneq) {
+ value = 1;
+ for (i = 0; i < private->gneq->timeout.value; i++)
+ value = 10 * value;
+ value = value * private->gneq->timeout.number;
+ /* do not accept useless values */
+ if (value != 0 && value <= DASD_EXPIRES_MAX)
+ device->default_expires = value;
+ }
+
/* Generate device unique id */
rc = dasd_eckd_generate_uid(device);
if (rc)
cqr->startdev = startdev;
cqr->memdev = startdev;
cqr->block = block;
- cqr->expires = 5 * 60 * HZ; /* 5 minutes */
+ cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */
cqr->lpm = private->path_data.ppm;
cqr->retries = 256;
cqr->buildclk = get_clock();
cqr->startdev = startdev;
cqr->memdev = startdev;
cqr->block = block;
- cqr->expires = 5 * 60 * HZ; /* 5 minutes */
+ cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */
cqr->lpm = private->path_data.ppm;
cqr->retries = 256;
cqr->buildclk = get_clock();
cqr->startdev = startdev;
cqr->memdev = startdev;
cqr->block = block;
- cqr->expires = 5 * 60 * HZ; /* 5 minutes */
+ cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */
cqr->lpm = private->path_data.ppm;
cqr->retries = 256;
cqr->buildclk = get_clock();
struct dasd_ccw_req *cqr;
int rc;
struct ccw1 *ccw;
+ int useglobal;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
+ useglobal = 0;
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device);
if (IS_ERR(cqr)) {
- DBF_DEV_EVENT(DBF_WARNING, device, "%s",
- "Could not allocate initialization request");
- return PTR_ERR(cqr);
+ mutex_lock(&dasd_reserve_mutex);
+ useglobal = 1;
+ cqr = &dasd_reserve_req->cqr;
+ memset(cqr, 0, sizeof(*cqr));
+ memset(&dasd_reserve_req->ccw, 0,
+ sizeof(dasd_reserve_req->ccw));
+ cqr->cpaddr = &dasd_reserve_req->ccw;
+ cqr->data = &dasd_reserve_req->data;
+ cqr->magic = DASD_ECKD_MAGIC;
}
ccw = cqr->cpaddr;
ccw->cmd_code = DASD_ECKD_CCW_RELEASE;
rc = dasd_sleep_on_immediatly(cqr);
- dasd_sfree_request(cqr, cqr->memdev);
+ if (useglobal)
+ mutex_unlock(&dasd_reserve_mutex);
+ else
+ dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
struct dasd_ccw_req *cqr;
int rc;
struct ccw1 *ccw;
+ int useglobal;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
+ useglobal = 0;
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device);
if (IS_ERR(cqr)) {
- DBF_DEV_EVENT(DBF_WARNING, device, "%s",
- "Could not allocate initialization request");
- return PTR_ERR(cqr);
+ mutex_lock(&dasd_reserve_mutex);
+ useglobal = 1;
+ cqr = &dasd_reserve_req->cqr;
+ memset(cqr, 0, sizeof(*cqr));
+ memset(&dasd_reserve_req->ccw, 0,
+ sizeof(dasd_reserve_req->ccw));
+ cqr->cpaddr = &dasd_reserve_req->ccw;
+ cqr->data = &dasd_reserve_req->data;
+ cqr->magic = DASD_ECKD_MAGIC;
}
ccw = cqr->cpaddr;
ccw->cmd_code = DASD_ECKD_CCW_RESERVE;
rc = dasd_sleep_on_immediatly(cqr);
- dasd_sfree_request(cqr, cqr->memdev);
+ if (useglobal)
+ mutex_unlock(&dasd_reserve_mutex);
+ else
+ dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
struct dasd_ccw_req *cqr;
int rc;
struct ccw1 *ccw;
+ int useglobal;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
+ useglobal = 0;
cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device);
if (IS_ERR(cqr)) {
- DBF_DEV_EVENT(DBF_WARNING, device, "%s",
- "Could not allocate initialization request");
- return PTR_ERR(cqr);
+ mutex_lock(&dasd_reserve_mutex);
+ useglobal = 1;
+ cqr = &dasd_reserve_req->cqr;
+ memset(cqr, 0, sizeof(*cqr));
+ memset(&dasd_reserve_req->ccw, 0,
+ sizeof(dasd_reserve_req->ccw));
+ cqr->cpaddr = &dasd_reserve_req->ccw;
+ cqr->data = &dasd_reserve_req->data;
+ cqr->magic = DASD_ECKD_MAGIC;
}
ccw = cqr->cpaddr;
ccw->cmd_code = DASD_ECKD_CCW_SLCK;
rc = dasd_sleep_on_immediatly(cqr);
- dasd_sfree_request(cqr, cqr->memdev);
+ if (useglobal)
+ mutex_unlock(&dasd_reserve_mutex);
+ else
+ dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
int ret;
ASCEBC(dasd_eckd_discipline.ebcname, 4);
+ dasd_reserve_req = kmalloc(sizeof(*dasd_reserve_req),
+ GFP_KERNEL | GFP_DMA);
+ if (!dasd_reserve_req)
+ return -ENOMEM;
ret = ccw_driver_register(&dasd_eckd_driver);
if (!ret)
wait_for_device_probe();
-
+ else
+ kfree(dasd_reserve_req);
return ret;
}
dasd_eckd_cleanup(void)
{
ccw_driver_unregister(&dasd_eckd_driver);
+ kfree(dasd_reserve_req);
}
module_init(dasd_eckd_init);
__u8 identifier:2;
__u8 reserved:6;
} __attribute__ ((packed)) flags;
- __u8 reserved[7];
+ __u8 reserved[5];
+ struct {
+ __u8 value:2;
+ __u8 number:6;
+ } __attribute__ ((packed)) timeout;
+ __u8 reserved3;
__u16 subsystemID;
__u8 reserved2[22];
} __attribute__ ((packed));
return rc;
}
+ device->default_expires = DASD_EXPIRES;
+
readonly = dasd_device_is_ro(device);
if (readonly)
set_bit(DASD_FLAG_DEVICE_RO, &device->flags);
cqr->startdev = memdev;
cqr->memdev = memdev;
cqr->block = block;
- cqr->expires = 5 * 60 * HZ; /* 5 minutes */
+ cqr->expires = memdev->default_expires * HZ; /* default 5 minutes */
cqr->retries = 32;
cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED;
/* ... and how */
unsigned long starttime; /* jiffies time of request start */
- int expires; /* expiration period in jiffies */
+ unsigned long expires; /* expiration period in jiffies */
char lpm; /* logical path mask */
void *data; /* pointer to data area */
#define DASD_CQR_CLEARED 0x84 /* request was cleared */
#define DASD_CQR_SUCCESS 0x85 /* request was successful */
+/* default expiration time*/
+#define DASD_EXPIRES 300
+#define DASD_EXPIRES_MAX 40000000
/* per dasd_ccw_req flags */
#define DASD_CQR_FLAGS_USE_ERP 0 /* use ERP for this request */
/* hook for alias management */
struct list_head alias_list;
+
+ /* default expiration time in s */
+ unsigned long default_expires;
};
struct dasd_block {
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
+#include <linux/smp_lock.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
struct dcssblk_dev_info *dev_info;
int rc;
+ lock_kernel();
dev_info = bdev->bd_disk->private_data;
if (NULL == dev_info) {
rc = -ENODEV;
bdev->bd_block_size = 4096;
rc = 0;
out:
+ unlock_kernel();
return rc;
}
struct segment_info *entry;
int rc;
+ lock_kernel();
if (!dev_info) {
rc = -ENODEV;
goto out;
up_write(&dcssblk_devices_sem);
rc = 0;
out:
+ unlock_kernel();
return rc;
}
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/blkdev.h>
+#include <linux/smp_lock.h>
#include <linux/interrupt.h>
#include <linux/buffer_head.h>
#include <linux/kernel.h>
struct tape_device * device;
int rc;
+ lock_kernel();
device = tape_get_device(disk->private_data);
if (device->required_tapemarks) {
* is called.
*/
tape_state_set(device, TS_BLKUSE);
+ unlock_kernel();
return 0;
release:
tape_release(device);
put_device:
tape_put_device(device);
+ unlock_kernel();
return rc;
}
tapeblock_release(struct gendisk *disk, fmode_t mode)
{
struct tape_device *device = disk->private_data;
-
+
+ lock_kernel();
tape_state_set(device, TS_IN_USE);
tape_release(device);
tape_put_device(device);
+ unlock_kernel();
return 0;
}
{
struct ccw_request *req = &cdev->private->req;
+ if (!req->singlepath) {
+ req->mask = 0;
+ goto out;
+ }
req->retries = req->maxretries;
req->mask = lpm_adjust(req->mask >>= 1, req->lpm);
-
+out:
return req->mask;
}
{
struct ccw_request *req = &cdev->private->req;
- /* Try all paths twice to counter link flapping. */
- req->mask = 0x8080;
+ if (req->singlepath) {
+ /* Try all paths twice to counter link flapping. */
+ req->mask = 0x8080;
+ } else
+ req->mask = req->lpm;
+
req->retries = req->maxretries;
req->mask = lpm_adjust(req->mask, req->lpm);
req->drc = 0;
/* Ask the driver what to do */
if (cdev->drv && cdev->drv->uc_handler) {
todo = cdev->drv->uc_handler(cdev, lcirb);
+ CIO_TRACE_EVENT(2, "uc_response");
+ CIO_HEX_EVENT(2, &todo, sizeof(todo));
switch (todo) {
case UC_TODO_RETRY:
return IO_STATUS_ERROR;
#include "chsc.h"
static void *sei_page;
+static DEFINE_SPINLOCK(siosl_lock);
static DEFINE_SPINLOCK(sda_lock);
/**
case 0x0007:
case 0x0008:
case 0x000a:
+ case 0x0104:
return -EINVAL;
case 0x0004:
return -EOPNOTSUPP;
return (rr->response.code == 0x0001) ? 0 : -EIO;
}
+static struct {
+ struct chsc_header request;
+ u32 word1;
+ struct subchannel_id sid;
+ u32 word3;
+ struct chsc_header response;
+ u32 word[11];
+} __attribute__ ((packed)) siosl_area __attribute__ ((__aligned__(PAGE_SIZE)));
+
+int chsc_siosl(struct subchannel_id schid)
+{
+ unsigned long flags;
+ int ccode;
+ int rc;
+
+ spin_lock_irqsave(&siosl_lock, flags);
+ memset(&siosl_area, 0, sizeof(siosl_area));
+ siosl_area.request.length = 0x0010;
+ siosl_area.request.code = 0x0046;
+ siosl_area.word1 = 0x80000000;
+ siosl_area.sid = schid;
+
+ ccode = chsc(&siosl_area);
+ if (ccode > 0) {
+ if (ccode == 3)
+ rc = -ENODEV;
+ else
+ rc = -EBUSY;
+ CIO_MSG_EVENT(2, "chsc: chsc failed for 0.%x.%04x (ccode=%d)\n",
+ schid.ssid, schid.sch_no, ccode);
+ goto out;
+ }
+ rc = chsc_error_from_response(siosl_area.response.code);
+ if (rc)
+ CIO_MSG_EVENT(2, "chsc: siosl failed for 0.%x.%04x (rc=%04x)\n",
+ schid.ssid, schid.sch_no,
+ siosl_area.response.code);
+ else
+ CIO_MSG_EVENT(4, "chsc: siosl succeeded for 0.%x.%04x\n",
+ schid.ssid, schid.sch_no);
+out:
+ spin_unlock_irqrestore(&siosl_lock, flags);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(chsc_siosl);
int chsc_error_from_response(int response);
+int chsc_siosl(struct subchannel_id schid);
+
#endif
#include "ioasm.h"
#include "io_sch.h"
#include "blacklist.h"
+#include "chsc.h"
static struct timer_list recovery_timer;
static DEFINE_SPINLOCK(recovery_lock);
spin_lock_irq(cdev->ccwlock);
ccw_device_sched_todo(cdev, CDEV_TODO_UNREG_EVAL);
spin_unlock_irq(cdev->ccwlock);
- } else if (cdev->online && cdev->drv && cdev->drv->set_offline)
+ return 0;
+ }
+ if (cdev->drv && cdev->drv->set_offline)
return ccw_device_set_offline(cdev);
- return 0;
+ return -EINVAL;
}
static int online_store_recog_and_online(struct ccw_device *cdev)
return -EAGAIN;
}
if (cdev->drv && cdev->drv->set_online)
- ccw_device_set_online(cdev);
- return 0;
+ return ccw_device_set_online(cdev);
+ return -EINVAL;
}
static int online_store_handle_online(struct ccw_device *cdev, int force)
}
}
+static ssize_t
+initiate_logging(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct subchannel *sch = to_subchannel(dev);
+ int rc;
+
+ rc = chsc_siosl(sch->schid);
+ if (rc < 0) {
+ pr_warning("Logging for subchannel 0.%x.%04x failed with "
+ "errno=%d\n",
+ sch->schid.ssid, sch->schid.sch_no, rc);
+ return rc;
+ }
+ pr_notice("Logging for subchannel 0.%x.%04x was triggered\n",
+ sch->schid.ssid, sch->schid.sch_no);
+ return count;
+}
+
static DEVICE_ATTR(chpids, 0444, chpids_show, NULL);
static DEVICE_ATTR(pimpampom, 0444, pimpampom_show, NULL);
static DEVICE_ATTR(devtype, 0444, devtype_show, NULL);
static DEVICE_ATTR(modalias, 0444, modalias_show, NULL);
static DEVICE_ATTR(online, 0644, online_show, online_store);
static DEVICE_ATTR(availability, 0444, available_show, NULL);
+static DEVICE_ATTR(logging, 0200, NULL, initiate_logging);
static struct attribute *io_subchannel_attrs[] = {
&dev_attr_chpids.attr,
&dev_attr_pimpampom.attr,
+ &dev_attr_logging.attr,
NULL,
};
}
}
+/**
+ * ccw_device_siosl() - initiate logging
+ * @cdev: ccw device
+ *
+ * This function is used to invoke model-dependent logging within the channel
+ * subsystem.
+ */
+int ccw_device_siosl(struct ccw_device *cdev)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+
+ return chsc_siosl(sch->schid);
+}
+EXPORT_SYMBOL_GPL(ccw_device_siosl);
+
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(ccw_device_set_online);
EXPORT_SYMBOL(ccw_device_set_offline);
req->timeout = PGID_TIMEOUT;
req->maxretries = PGID_RETRIES;
req->lpm = 0x80;
+ req->singlepath = 1;
req->callback = spid_callback;
spid_do(cdev);
}
req->timeout = PGID_TIMEOUT;
req->maxretries = PGID_RETRIES;
req->lpm = 0x80;
+ req->singlepath = 1;
if (cdev->private->flags.pgroup) {
CIO_TRACE_EVENT(4, "snid");
CIO_HEX_EVENT(4, devid, sizeof(*devid));
req->timeout = PGID_TIMEOUT;
req->maxretries = PGID_RETRIES;
req->lpm = sch->schib.pmcw.pam & sch->opm;
+ req->singlepath = 1;
req->callback = disband_callback;
fn = SPID_FUNC_DISBAND;
if (cdev->private->flags.mpath)
* @filter: optional callback to adjust request status based on IRB data
* @callback: final callback
* @data: user-defined pointer passed to all callbacks
+ * @singlepath: if set, use only one path from @lpm per start I/O
+ * @cancel: non-zero if request was cancelled
+ * @done: non-zero if request was finished
* @mask: current path mask
* @retries: current number of retries
* @drc: delayed return code
- * @cancel: non-zero if request was cancelled
- * @done: non-zero if request was finished
*/
struct ccw_request {
struct ccw1 *cp;
enum io_status);
void (*callback)(struct ccw_device *, void *, int);
void *data;
+ unsigned int singlepath:1;
/* These fields are used internally. */
+ unsigned int cancel:1;
+ unsigned int done:1;
u16 mask;
u16 retries;
int drc;
- int cancel:1;
- int done:1;
} __attribute__((packed));
/*
goto fail_put_driver;
}
+ /* convert sender to uppercase characters */
+ if (sender) {
+ int len = strlen(sender);
+ while (len--)
+ sender[len] = toupper(sender[len]);
+ }
+
/* register with the smsgiucv device driver */
rc = smsg_register_callback(SMSG_PREFIX, smsg_app_callback);
if (rc) {
hostdata->msgin = memory + MSGIN_OFFSET;
hostdata->msgout = memory + MSGOUT_OFFSET;
hostdata->status = memory + STATUS_OFFSET;
- /* all of these offsets are L1_CACHE_BYTES separated. It is fatal
- * if this isn't sufficient separation to avoid dma flushing issues */
- BUG_ON(!dma_is_consistent(hostdata->dev, pScript) && L1_CACHE_BYTES < dma_get_cache_alignment());
hostdata->slots = (struct NCR_700_command_slot *)(memory + SLOTS_OFFSET);
hostdata->dev = dev;
config SCSI_PMCRAID
tristate "PMC SIERRA Linux MaxRAID adapter support"
- depends on PCI && SCSI
+ depends on PCI && SCSI && NET
---help---
This driver supports the PMC SIERRA MaxRAID adapters.
#endif
/* KLL May need eop and parity in 53c400 */
if (hostdata->flags & FLAG_NCR53C400)
- NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_PAR_CHECK | MR_ENABLE_PAR_INTR | MR_ENABLE_EOP_INTR | MR_DMA_MODE | MR_MONITOR_BSY);
+ NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE |
+ MR_ENABLE_PAR_CHECK | MR_ENABLE_PAR_INTR |
+ MR_ENABLE_EOP_INTR | MR_MONITOR_BSY);
else
NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE);
#endif /* def REAL_DMA */
pci_free_consistent(dev->pdev, sizeof(struct POSTSTATUS),
post, paddr);
if (likely((buffer[0] == '0') && ((buffer[1] == 'x') || (buffer[1] == 'X')))) {
- ret = (buffer[2] <= '9') ? (buffer[2] - '0') : (buffer[2] - 'A' + 10);
- ret <<= 4;
- ret += (buffer[3] <= '9') ? (buffer[3] - '0') : (buffer[3] - 'A' + 10);
+ ret = (hex_to_bin(buffer[2]) << 4) +
+ hex_to_bin(buffer[3]);
}
pci_free_consistent(dev->pdev, 512, buffer, baddr);
return ret;
#define SCSI_BUF_PA(address) isa_virt_to_bus(address)
#define SCSI_SG_PA(sgent) (isa_page_to_bus(sg_page((sgent))) + (sgent)->offset)
-static void BAD_SG_DMA(Scsi_Cmnd * SCpnt,
- struct scatterlist *sgp,
- int nseg,
- int badseg)
-{
- printk(KERN_CRIT "sgpnt[%d:%d] page %p/0x%llx length %u\n",
- badseg, nseg, sg_virt(sgp),
- (unsigned long long)SCSI_SG_PA(sgp),
- sgp->length);
-
- /*
- * Not safe to continue.
- */
- panic("Buffer at physical address > 16Mb used for aha1542");
-}
-
#include<linux/stat.h>
#ifdef DEBUG
}
scsi_for_each_sg(SCpnt, sg, sg_count, i) {
any2scsi(cptr[i].dataptr, SCSI_SG_PA(sg));
- if (SCSI_SG_PA(sg) + sg->length - 1 > ISA_DMA_THRESHOLD)
- BAD_SG_DMA(SCpnt, scsi_sglist(SCpnt), sg_count, i);
any2scsi(cptr[i].datalen, sg->length);
};
any2scsi(ccb[mbo].datalen, sg_count * sizeof(struct chain));
release_region(bases[indx], 4);
continue;
}
- /* For now we do this - until kmalloc is more intelligent
- we are resigned to stupid hacks like this */
- if (SCSI_BUF_PA(shpnt) >= ISA_DMA_THRESHOLD) {
- printk(KERN_ERR "Invalid address for shpnt with 1542.\n");
- goto unregister;
- }
if (!aha1542_test_port(bases[indx], shpnt))
goto unregister;
-
base_io = bases[indx];
/* Set the Bus on/off-times as not to ruin floppy performance */
{
int i;
struct asd_sas_phy **sas_phys =
- kmalloc(ASD_MAX_PHYS * sizeof(struct asd_sas_phy), GFP_KERNEL);
+ kcalloc(ASD_MAX_PHYS, sizeof(*sas_phys), GFP_KERNEL);
struct asd_sas_port **sas_ports =
- kmalloc(ASD_MAX_PHYS * sizeof(struct asd_sas_port), GFP_KERNEL);
+ kcalloc(ASD_MAX_PHYS, sizeof(*sas_ports), GFP_KERNEL);
if (!sas_phys || !sas_ports) {
kfree(sas_phys);
break;
case BFI_FCPORT_I2H_DISABLE_RSP:
- if (fcport->msgtag == i2hmsg.penable_rsp->msgtag)
+ if (fcport->msgtag == i2hmsg.pdisable_rsp->msgtag)
bfa_sm_send_event(fcport, BFA_FCPORT_SM_FWRSP);
break;
struct bfa_tskim_s *tskim;
struct bfad_itnim_s *itnim;
struct bfa_itnim_s *bfa_itnim;
- DECLARE_WAIT_QUEUE_HEAD(wq);
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
int rc = SUCCESS;
unsigned long flags;
enum bfi_tskim_status task_status;
struct bfad_itnim_s *itnim;
unsigned long flags;
u32 i, rc, err_cnt = 0;
- DECLARE_WAIT_QUEUE_HEAD(wq);
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
enum bfi_tskim_status task_status;
spin_lock_irqsave(&bfad->bfad_lock, flags);
#ifndef __FCPPROTO_H__
#define __FCPPROTO_H__
+#include <linux/bitops.h>
#include <protocol/scsi.h>
#pragma pack(1)
/*
* Task management flags field - only one bit shall be set
*/
-#ifndef BIT
-#define BIT(_x) (1 << (_x))
-#endif
enum fcp_tm_cmnd{
FCP_TM_ABORT_TASK_SET = BIT(1),
FCP_TM_CLEAR_TASK_SET = BIT(2),
config SCSI_BNX2_ISCSI
tristate "Broadcom NetXtreme II iSCSI support"
+ depends on NET
+ depends on PCI
select SCSI_ISCSI_ATTRS
select NETDEVICES
select NETDEV_1000
select CNIC
- depends on PCI
---help---
This driver supports iSCSI offload for the Broadcom NetXtreme II
devices.
};
// module_param_string_array(vendor_labels, NULL, 0444);
-#define dprintk(fmt, arg...) if (debug) \
- printk(KERN_DEBUG "%s: " fmt, ch->name , ## arg)
-#define vprintk(fmt, arg...) if (verbose) \
- printk(KERN_INFO "%s: " fmt, ch->name , ## arg)
+#define DPRINTK(fmt, arg...) \
+do { \
+ if (debug) \
+ printk(KERN_DEBUG "%s: " fmt, ch->name, ##arg); \
+} while (0)
+#define VPRINTK(level, fmt, arg...) \
+do { \
+ if (verbose) \
+ printk(level "%s: " fmt, ch->name, ##arg); \
+} while (0)
/* ------------------------------------------------------------------- */
retry:
errno = 0;
if (debug) {
- dprintk("command: ");
+ DPRINTK("command: ");
__scsi_print_command(cmd);
}
buflength, &sshdr, timeout * HZ,
MAX_RETRIES, NULL);
- dprintk("result: 0x%x\n",result);
+ DPRINTK("result: 0x%x\n",result);
if (driver_byte(result) & DRIVER_SENSE) {
if (debug)
scsi_print_sense_hdr(ch->name, &sshdr);
cmd[9] = 255;
if (0 == (result = ch_do_scsi(ch, cmd, buffer, 256, DMA_FROM_DEVICE))) {
if (((buffer[16] << 8) | buffer[17]) != elem) {
- dprintk("asked for element 0x%02x, got 0x%02x\n",
+ DPRINTK("asked for element 0x%02x, got 0x%02x\n",
elem,(buffer[16] << 8) | buffer[17]);
kfree(buffer);
return -EIO;
} else {
if (ch->voltags) {
ch->voltags = 0;
- vprintk("device has no volume tag support\n");
+ VPRINTK(KERN_INFO, "device has no volume tag support\n");
goto retry;
}
- dprintk("READ ELEMENT STATUS for element 0x%x failed\n",elem);
+ DPRINTK("READ ELEMENT STATUS for element 0x%x failed\n",elem);
}
kfree(buffer);
return result;
int err;
u_char cmd[6];
- vprintk("INITIALIZE ELEMENT STATUS, may take some time ...\n");
+ VPRINTK(KERN_INFO, "INITIALIZE ELEMENT STATUS, may take some time ...\n");
memset(cmd,0,sizeof(cmd));
cmd[0] = INITIALIZE_ELEMENT_STATUS;
cmd[1] = ch->device->lun << 5;
err = ch_do_scsi(ch, cmd, NULL, 0, DMA_NONE);
- vprintk("... finished\n");
+ VPRINTK(KERN_INFO, "... finished\n");
return err;
}
(buffer[buffer[3]+18] << 8) | buffer[buffer[3]+19];
ch->counts[CHET_DT] =
(buffer[buffer[3]+20] << 8) | buffer[buffer[3]+21];
- vprintk("type #1 (mt): 0x%x+%d [medium transport]\n",
+ VPRINTK(KERN_INFO, "type #1 (mt): 0x%x+%d [medium transport]\n",
ch->firsts[CHET_MT],
ch->counts[CHET_MT]);
- vprintk("type #2 (st): 0x%x+%d [storage]\n",
+ VPRINTK(KERN_INFO, "type #2 (st): 0x%x+%d [storage]\n",
ch->firsts[CHET_ST],
ch->counts[CHET_ST]);
- vprintk("type #3 (ie): 0x%x+%d [import/export]\n",
+ VPRINTK(KERN_INFO, "type #3 (ie): 0x%x+%d [import/export]\n",
ch->firsts[CHET_IE],
ch->counts[CHET_IE]);
- vprintk("type #4 (dt): 0x%x+%d [data transfer]\n",
+ VPRINTK(KERN_INFO, "type #4 (dt): 0x%x+%d [data transfer]\n",
ch->firsts[CHET_DT],
ch->counts[CHET_DT]);
} else {
- vprintk("reading element address assigment page failed!\n");
+ VPRINTK(KERN_INFO, "reading element address assigment page failed!\n");
}
/* vendor specific element types */
continue;
ch->firsts[CHET_V1+i] = vendor_firsts[i];
ch->counts[CHET_V1+i] = vendor_counts[i];
- vprintk("type #%d (v%d): 0x%x+%d [%s, vendor specific]\n",
+ VPRINTK(KERN_INFO, "type #%d (v%d): 0x%x+%d [%s, vendor specific]\n",
i+5,i+1,vendor_firsts[i],vendor_counts[i],
vendor_labels[i]);
}
/* look up the devices of the data transfer elements */
- ch->dt = kmalloc(ch->counts[CHET_DT]*sizeof(struct scsi_device),
+ ch->dt = kcalloc(ch->counts[CHET_DT], sizeof(*ch->dt),
GFP_KERNEL);
if (!ch->dt) {
if (elem < CH_DT_MAX && -1 != dt_id[elem]) {
id = dt_id[elem];
lun = dt_lun[elem];
- vprintk("dt 0x%x: [insmod option] ",
+ VPRINTK(KERN_INFO, "dt 0x%x: [insmod option] ",
elem+ch->firsts[CHET_DT]);
} else if (0 != ch_read_element_status
(ch,elem+ch->firsts[CHET_DT],data)) {
- vprintk("dt 0x%x: READ ELEMENT STATUS failed\n",
+ VPRINTK(KERN_INFO, "dt 0x%x: READ ELEMENT STATUS failed\n",
elem+ch->firsts[CHET_DT]);
} else {
- vprintk("dt 0x%x: ",elem+ch->firsts[CHET_DT]);
+ VPRINTK(KERN_INFO, "dt 0x%x: ",elem+ch->firsts[CHET_DT]);
if (data[6] & 0x80) {
- if (verbose)
- printk("not this SCSI bus\n");
+ VPRINTK(KERN_CONT, "not this SCSI bus\n");
ch->dt[elem] = NULL;
} else if (0 == (data[6] & 0x30)) {
- if (verbose)
- printk("ID/LUN unknown\n");
+ VPRINTK(KERN_CONT, "ID/LUN unknown\n");
ch->dt[elem] = NULL;
} else {
id = ch->device->id;
}
}
if (-1 != id) {
- if (verbose)
- printk("ID %i, LUN %i, ",id,lun);
+ VPRINTK(KERN_CONT, "ID %i, LUN %i, ",id,lun);
ch->dt[elem] =
scsi_device_lookup(ch->device->host,
ch->device->channel,
id,lun);
if (!ch->dt[elem]) {
/* should not happen */
- if (verbose)
- printk("Huh? device not found!\n");
+ VPRINTK(KERN_CONT, "Huh? device not found!\n");
} else {
- if (verbose)
- printk("name: %8.8s %16.16s %4.4s\n",
- ch->dt[elem]->vendor,
- ch->dt[elem]->model,
- ch->dt[elem]->rev);
+ VPRINTK(KERN_CONT, "name: %8.8s %16.16s %4.4s\n",
+ ch->dt[elem]->vendor,
+ ch->dt[elem]->model,
+ ch->dt[elem]->rev);
}
}
}
{
u_char cmd[10];
- dprintk("position: 0x%x\n",elem);
+ DPRINTK("position: 0x%x\n",elem);
if (0 == trans)
trans = ch->firsts[CHET_MT];
memset(cmd,0,sizeof(cmd));
{
u_char cmd[12];
- dprintk("move: 0x%x => 0x%x\n",src,dest);
+ DPRINTK("move: 0x%x => 0x%x\n",src,dest);
if (0 == trans)
trans = ch->firsts[CHET_MT];
memset(cmd,0,sizeof(cmd));
{
u_char cmd[12];
- dprintk("exchange: 0x%x => 0x%x => 0x%x\n",
+ DPRINTK("exchange: 0x%x => 0x%x => 0x%x\n",
src,dest1,dest2);
if (0 == trans)
trans = ch->firsts[CHET_MT];
if (!buffer)
return -ENOMEM;
- dprintk("%s %s voltag: 0x%x => \"%s\"\n",
+ DPRINTK("%s %s voltag: 0x%x => \"%s\"\n",
clear ? "clear" : "set",
alternate ? "alternate" : "primary",
elem, tag);
}
put_user(data[2], dest+i);
if (data[2] & CESTATUS_EXCEPT)
- vprintk("element 0x%x: asc=0x%x, ascq=0x%x\n",
+ VPRINTK(KERN_INFO, "element 0x%x: asc=0x%x, ascq=0x%x\n",
ch->firsts[type]+i,
(int)data[4],(int)data[5]);
retval = ch_read_element_status
return -EFAULT;
if (0 != ch_checkrange(ch, pos.cp_type, pos.cp_unit)) {
- dprintk("CHIOPOSITION: invalid parameter\n");
+ DPRINTK("CHIOPOSITION: invalid parameter\n");
return -EBADSLT;
}
mutex_lock(&ch->lock);
if (0 != ch_checkrange(ch, mv.cm_fromtype, mv.cm_fromunit) ||
0 != ch_checkrange(ch, mv.cm_totype, mv.cm_tounit )) {
- dprintk("CHIOMOVE: invalid parameter\n");
+ DPRINTK("CHIOMOVE: invalid parameter\n");
return -EBADSLT;
}
if (0 != ch_checkrange(ch, mv.ce_srctype, mv.ce_srcunit ) ||
0 != ch_checkrange(ch, mv.ce_fdsttype, mv.ce_fdstunit) ||
0 != ch_checkrange(ch, mv.ce_sdsttype, mv.ce_sdstunit)) {
- dprintk("CHIOEXCHANGE: invalid parameter\n");
+ DPRINTK("CHIOEXCHANGE: invalid parameter\n");
return -EBADSLT;
}
}
} else if (ch->voltags) {
ch->voltags = 0;
- vprintk("device has no volume tag support\n");
+ VPRINTK(KERN_INFO, "device has no volume tag support\n");
goto voltag_retry;
}
kfree(buffer);
return -EFAULT;
if (0 != ch_checkrange(ch, csv.csv_type, csv.csv_unit)) {
- dprintk("CHIOSVOLTAG: invalid parameter\n");
+ DPRINTK("CHIOSVOLTAG: invalid parameter\n");
return -EBADSLT;
}
elem = ch->firsts[csv.csv_type] + csv.csv_unit;
u32 tag_mask = 1;
u8 tag_number = 0;
while (tag_mask & dcb->tag_mask
- && tag_number <= dcb->max_command) {
+ && tag_number < dcb->max_command) {
tag_mask = tag_mask << 1;
tag_number++;
}
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/workqueue.h>
+#include <linux/bitops.h>
#include <scsi/libfc.h>
#include <scsi/libfcoe.h>
#include "fnic_io.h"
/*
* Tag bits used for special requests.
*/
-#define BIT(nr) (1UL << (nr))
#define FNIC_TAG_ABORT BIT(30) /* tag bit indicating abort */
#define FNIC_TAG_DEV_RST BIT(29) /* indicates device reset */
#define FNIC_TAG_MASK (BIT(24) - 1) /* mask for lookup */
int __init generic_NCR5380_detect(struct scsi_host_template * tpnt)
{
static int current_override = 0;
- int count, i;
+ int count;
unsigned int *ports;
+#ifndef SCSI_G_NCR5380_MEM
+ int i;
unsigned long region_size = 16;
+#endif
static unsigned int __initdata ncr_53c400a_ports[] = {
0x280, 0x290, 0x300, 0x310, 0x330, 0x340, 0x348, 0x350, 0
};
};
int flags = 0;
struct Scsi_Host *instance;
-#ifdef CONFIG_SCSI_G_NCR5380_MEM
+#ifdef SCSI_G_NCR5380_MEM
unsigned long base;
void __iomem *iomem;
#endif
overrides[0].board = BOARD_NCR53C400A;
else if (dtc_3181e != NCR_NOT_SET)
overrides[0].board = BOARD_DTC3181E;
-
+#ifndef SCSI_G_NCR5380_MEM
if (!current_override && isapnp_present()) {
struct pnp_dev *dev = NULL;
count = 0;
while ((dev = pnp_find_dev(NULL, ISAPNP_VENDOR('D', 'T', 'C'), ISAPNP_FUNCTION(0x436e), dev))) {
if (count >= NO_OVERRIDES)
break;
- if (pnp_device_attach(dev) < 0) {
- printk(KERN_ERR "dtc436e probe: attach failed\n");
+ if (pnp_device_attach(dev) < 0)
continue;
- }
if (pnp_activate_dev(dev) < 0) {
printk(KERN_ERR "dtc436e probe: activate failed\n");
pnp_device_detach(dev);
count++;
}
}
-
+#endif
tpnt->proc_name = "g_NCR5380";
for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
break;
}
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
if (ports) {
/* wakeup sequence for the NCR53C400A and DTC3181E */
#endif
instance = scsi_register(tpnt, sizeof(struct NCR5380_hostdata));
if (instance == NULL) {
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
release_region(overrides[current_override].NCR5380_map_name, region_size);
#else
iounmap(iomem);
}
instance->NCR5380_instance_name = overrides[current_override].NCR5380_map_name;
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
instance->n_io_port = region_size;
#else
- ((struct NCR5380_hostdata *)instance->hostdata).iomem = iomem;
+ ((struct NCR5380_hostdata *)instance->hostdata)->iomem = iomem;
#endif
NCR5380_init(instance, flags);
free_irq(instance->irq, instance);
NCR5380_exit(instance);
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
release_region(instance->NCR5380_instance_name, instance->n_io_port);
#else
- iounmap(((struct NCR5380_hostdata *)instance->hostdata).iomem);
+ iounmap(((struct NCR5380_hostdata *)instance->hostdata)->iomem);
release_mem_region(instance->NCR5380_instance_name, NCR5380_region_size);
#endif
}
while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY);
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
{
int i;
for (i = 0; i < 128; i++)
dst[start + i] = NCR5380_read(C400_HOST_BUFFER);
}
#else
- /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ /* implies SCSI_G_NCR5380_MEM */
memcpy_fromio(dst + start, iomem + NCR53C400_host_buffer, 128);
#endif
start += 128;
// FIXME - no timeout
}
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
{
int i;
for (i = 0; i < 128; i++)
dst[start + i] = NCR5380_read(C400_HOST_BUFFER);
}
#else
- /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ /* implies SCSI_G_NCR5380_MEM */
memcpy_fromio(dst + start, iomem + NCR53C400_host_buffer, 128);
#endif
start += 128;
}
while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
; // FIXME - timeout
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
{
for (i = 0; i < 128; i++)
NCR5380_write(C400_HOST_BUFFER, src[start + i]);
}
#else
- /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ /* implies SCSI_G_NCR5380_MEM */
memcpy_toio(iomem + NCR53C400_host_buffer, src + start, 128);
#endif
start += 128;
while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
; // FIXME - no timeout
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
{
for (i = 0; i < 128; i++)
NCR5380_write(C400_HOST_BUFFER, src[start + i]);
}
#else
- /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ /* implies SCSI_G_NCR5380_MEM */
memcpy_toio(iomem + NCR53C400_host_buffer, src + start, 128);
#endif
start += 128;
module_param(dtc_3181e, int, 0);
MODULE_LICENSE("GPL");
-
+#ifndef SCSI_G_NCR5380_MEM
static struct isapnp_device_id id_table[] __devinitdata = {
{
ISAPNP_ANY_ID, ISAPNP_ANY_ID,
};
MODULE_DEVICE_TABLE(isapnp, id_table);
-
+#endif
__setup("ncr5380=", do_NCR5380_setup);
__setup("ncr53c400=", do_NCR53C400_setup);
#define __STRVAL(x) #x
#define STRVAL(x) __STRVAL(x)
-#ifndef CONFIG_SCSI_G_NCR5380_MEM
+#ifndef SCSI_G_NCR5380_MEM
#define NCR5380_map_config port
#define NCR5380_map_type int
NCR5380_map_name = (NCR5380_map_type)((instance)->NCR5380_instance_name)
#else
-/* therefore CONFIG_SCSI_G_NCR5380_MEM */
+/* therefore SCSI_G_NCR5380_MEM */
#define NCR5380_map_config memory
#define NCR5380_map_type unsigned long
register void __iomem *iomem
#define NCR5380_setup(instance) \
- iomem = (((struct NCR5380_hostdata *)(instance)->hostdata).iomem)
+ iomem = (((struct NCR5380_hostdata *)(instance)->hostdata)->iomem)
#endif
error = scsi_add_host(shp, NULL);
if (error)
- goto out_free_coal_stat;
+ goto out_free_ccb_phys;
list_add_tail(&ha->list, &gdth_instances);
gdth_timer_init();
}
cmnd->result = cblk->tastat | (cblk->hastat << 16);
- WARN_ON(cmnd == NULL);
i91u_unmap_scb(host->pci_dev, cmnd);
cmnd->scsi_done(cmnd); /* Notify system DONE */
initio_release_scb(host, cblk); /* Release SCB for current channel */
#include <linux/interrupt.h>
#include <linux/aer.h>
#include <linux/gfp.h>
+#include <linux/kernel.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
/* Validate and store the new name */
for (i=0, j=0; i < 16; i++) {
- if ((*buf >= 'a') && (*buf <= 'f'))
- j = ((j << 4) | ((*buf++ -'a') + 10));
- else if ((*buf >= 'A') && (*buf <= 'F'))
- j = ((j << 4) | ((*buf++ -'A') + 10));
- else if ((*buf >= '0') && (*buf <= '9'))
- j = ((j << 4) | (*buf++ -'0'));
+ int value;
+
+ value = hex_to_bin(*buf++);
+ if (value >= 0)
+ j = (j << 4) | value;
else
return -EINVAL;
if (i % 2) {
/* Validate and store the new name */
for (i=0, j=0; i < 16; i++) {
- if ((*buf >= 'a') && (*buf <= 'f'))
- j = ((j << 4) | ((*buf++ -'a') + 10));
- else if ((*buf >= 'A') && (*buf <= 'F'))
- j = ((j << 4) | ((*buf++ -'A') + 10));
- else if ((*buf >= '0') && (*buf <= '9'))
- j = ((j << 4) | (*buf++ -'0'));
+ int value;
+
+ value = hex_to_bin(*buf++);
+ if (value >= 0)
+ j = (j << 4) | value;
else
return -EINVAL;
if (i % 2) {
return PTR_ERR(bio);
}
- bio->bi_rw &= ~(1 << BIO_RW);
+ bio->bi_rw &= ~REQ_WRITE;
or->in.bio = bio;
or->in.total_bytes = bio->bi_size;
return 0;
{
_osd_req_encode_common(or, OSD_ACT_WRITE, obj, offset, len);
WARN_ON(or->out.bio || or->out.total_bytes);
- WARN_ON(0 == bio_rw_flagged(bio, BIO_RW));
+ WARN_ON(0 == (bio->bi_rw & REQ_WRITE));
or->out.bio = bio;
or->out.total_bytes = len;
}
if (IS_ERR(bio))
return PTR_ERR(bio);
- bio->bi_rw |= (1 << BIO_RW); /* FIXME: bio_set_dir() */
+ bio->bi_rw |= REQ_WRITE; /* FIXME: bio_set_dir() */
osd_req_write(or, obj, offset, bio, len);
return 0;
}
{
_osd_req_encode_common(or, OSD_ACT_READ, obj, offset, len);
WARN_ON(or->in.bio || or->in.total_bytes);
- WARN_ON(1 == bio_rw_flagged(bio, BIO_RW));
+ WARN_ON(1 == (bio->bi_rw & REQ_WRITE));
or->in.bio = bio;
or->in.total_bytes = len;
}
}
/* find a free minor number */
- for (i=0; os_scsi_tapes[i] && i<osst_max_dev; i++);
+ for (i = 0; i < osst_max_dev && os_scsi_tapes[i]; i++)
+ ;
if(i >= osst_max_dev) panic ("Scsi_devices corrupt (osst)");
dev_num = i;
module_param(ql2xmaxqueues, int, S_IRUGO|S_IRUSR);
MODULE_PARM_DESC(ql2xmaxqueues,
"Enables MQ settings "
- "Default is 1 for single queue. Set it to number \
- of queues in MQ mode.");
+ "Default is 1 for single queue. Set it to number "
+ "of queues in MQ mode.");
int ql2xmultique_tag;
module_param(ql2xmultique_tag, int, S_IRUGO|S_IRUSR);
"changed. The Linux SCSI layer does not "
"automatically adjust these parameters.\n");
- if (blk_barrier_rq(scmd->request))
+ if (scmd->request->cmd_flags & REQ_HARDBARRIER)
/*
* barrier requests should always retry on UA
* otherwise block will get a spurious error
case DID_OK:
break;
case DID_BUS_BUSY:
- return blk_failfast_transport(scmd->request);
+ return (scmd->request->cmd_flags & REQ_FAILFAST_TRANSPORT);
case DID_PARITY:
- return blk_failfast_dev(scmd->request);
+ return (scmd->request->cmd_flags & REQ_FAILFAST_DEV);
case DID_ERROR:
if (msg_byte(scmd->result) == COMMAND_COMPLETE &&
status_byte(scmd->result) == RESERVATION_CONFLICT)
return 0;
/* fall through */
case DID_SOFT_ERROR:
- return blk_failfast_driver(scmd->request);
+ return (scmd->request->cmd_flags & REQ_FAILFAST_DRIVER);
}
switch (status_byte(scmd->result)) {
* assume caller has checked sense and determinted
* the check condition was retryable.
*/
- return blk_failfast_dev(scmd->request);
+ if (scmd->request->cmd_flags & REQ_FAILFAST_DEV ||
+ scmd->request->cmd_type == REQ_TYPE_BLOCK_PC)
+ return 1;
}
return 0;
{
struct scsi_cmnd *cmd = req->special;
- req->cmd_flags &= ~REQ_DONTPREP;
+ blk_unprep_request(req);
req->special = NULL;
scsi_put_command(cmd);
sense_deferred = scsi_sense_is_deferred(&sshdr);
}
- if (blk_pc_request(req)) { /* SG_IO ioctl from block level */
+ if (req->cmd_type == REQ_TYPE_BLOCK_PC) { /* SG_IO ioctl from block level */
req->errors = result;
if (result) {
if (sense_valid && req->sense) {
}
}
- BUG_ON(blk_bidi_rq(req)); /* bidi not support for !blk_pc_request yet */
+ /* no bidi support for !REQ_TYPE_BLOCK_PC yet */
+ BUG_ON(blk_bidi_rq(req));
/*
* Next deal with any sectors which we were able to correctly
err_exit:
scsi_release_buffers(cmd);
- if (error == BLKPREP_KILL)
- scsi_put_command(cmd);
- else /* BLKPREP_DEFER */
- scsi_unprep_request(cmd->request);
-
+ scsi_put_command(cmd);
+ cmd->request->special = NULL;
return error;
}
EXPORT_SYMBOL(scsi_init_io);
blk_start_request(req);
- if (unlikely(cmd == NULL)) {
- printk(KERN_CRIT "impossible request in %s.\n",
- __func__);
- BUG();
- }
-
sdev = cmd->device;
starget = scsi_target(sdev);
shost = sdev->host;
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/kernel.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h>
/* Validate and store the new name */
for (i=0, j=0; i < 16; i++) {
- if ((*ns >= 'a') && (*ns <= 'f'))
- j = ((j << 4) | ((*ns++ -'a') + 10));
- else if ((*ns >= 'A') && (*ns <= 'F'))
- j = ((j << 4) | ((*ns++ -'A') + 10));
- else if ((*ns >= '0') && (*ns <= '9'))
- j = ((j << 4) | (*ns++ -'0'));
+ int value;
+
+ value = hex_to_bin(*ns++);
+ if (value >= 0)
+ j = (j << 4) | value;
else
return -EINVAL;
if (i % 2) {
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/delay.h>
+#include <linux/smp_lock.h>
#include <linux/mutex.h>
#include <linux/string_helpers.h>
#include <linux/async.h>
* object after last put) */
static DEFINE_MUTEX(sd_ref_mutex);
-struct kmem_cache *sd_cdb_cache;
-mempool_t *sd_cdb_pool;
+static struct kmem_cache *sd_cdb_cache;
+static mempool_t *sd_cdb_pool;
static const char *sd_cache_types[] = {
"write through", "none", "write back",
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(sd_cache_types); i++) {
- const int len = strlen(sd_cache_types[i]);
+ len = strlen(sd_cache_types[i]);
if (strncmp(sd_cache_types[i], buf, len) == 0 &&
buf[len] == '\n') {
ct = i;
}
/**
- * sd_prepare_discard - unmap blocks on thinly provisioned device
+ * scsi_setup_discard_cmnd - unmap blocks on thinly provisioned device
+ * @sdp: scsi device to operate one
* @rq: Request to prepare
*
* Will issue either UNMAP or WRITE SAME(16) depending on preference
* indicated by target device.
**/
-static int sd_prepare_discard(struct request *rq)
+static int scsi_setup_discard_cmnd(struct scsi_device *sdp, struct request *rq)
{
struct scsi_disk *sdkp = scsi_disk(rq->rq_disk);
struct bio *bio = rq->bio;
sector_t sector = bio->bi_sector;
- unsigned int num = bio_sectors(bio);
+ unsigned int nr_sectors = bio_sectors(bio);
+ unsigned int len;
+ int ret;
+ struct page *page;
if (sdkp->device->sector_size == 4096) {
sector >>= 3;
- num >>= 3;
+ nr_sectors >>= 3;
}
- rq->cmd_type = REQ_TYPE_BLOCK_PC;
rq->timeout = SD_TIMEOUT;
memset(rq->cmd, 0, rq->cmd_len);
+ page = alloc_page(GFP_ATOMIC | __GFP_ZERO);
+ if (!page)
+ return BLKPREP_DEFER;
+
if (sdkp->unmap) {
- char *buf = kmap_atomic(bio_page(bio), KM_USER0);
+ char *buf = page_address(page);
+ rq->cmd_len = 10;
rq->cmd[0] = UNMAP;
rq->cmd[8] = 24;
- rq->cmd_len = 10;
-
- /* Ensure that data length matches payload */
- rq->__data_len = bio->bi_size = bio->bi_io_vec->bv_len = 24;
put_unaligned_be16(6 + 16, &buf[0]);
put_unaligned_be16(16, &buf[2]);
put_unaligned_be64(sector, &buf[8]);
- put_unaligned_be32(num, &buf[16]);
+ put_unaligned_be32(nr_sectors, &buf[16]);
- kunmap_atomic(buf, KM_USER0);
+ len = 24;
} else {
+ rq->cmd_len = 16;
rq->cmd[0] = WRITE_SAME_16;
rq->cmd[1] = 0x8; /* UNMAP */
put_unaligned_be64(sector, &rq->cmd[2]);
- put_unaligned_be32(num, &rq->cmd[10]);
- rq->cmd_len = 16;
+ put_unaligned_be32(nr_sectors, &rq->cmd[10]);
+
+ len = sdkp->device->sector_size;
+ }
+
+ blk_add_request_payload(rq, page, len);
+ ret = scsi_setup_blk_pc_cmnd(sdp, rq);
+ rq->buffer = page_address(page);
+ if (ret != BLKPREP_OK) {
+ __free_page(page);
+ rq->buffer = NULL;
}
+ return ret;
+}
- return BLKPREP_OK;
+static int scsi_setup_flush_cmnd(struct scsi_device *sdp, struct request *rq)
+{
+ rq->timeout = SD_TIMEOUT;
+ rq->retries = SD_MAX_RETRIES;
+ rq->cmd[0] = SYNCHRONIZE_CACHE;
+ rq->cmd_len = 10;
+
+ return scsi_setup_blk_pc_cmnd(sdp, rq);
+}
+
+static void sd_unprep_fn(struct request_queue *q, struct request *rq)
+{
+ if (rq->cmd_flags & REQ_DISCARD) {
+ free_page((unsigned long)rq->buffer);
+ rq->buffer = NULL;
+ }
}
/**
* Discard request come in as REQ_TYPE_FS but we turn them into
* block PC requests to make life easier.
*/
- if (blk_discard_rq(rq))
- ret = sd_prepare_discard(rq);
-
- if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
+ if (rq->cmd_flags & REQ_DISCARD) {
+ ret = scsi_setup_discard_cmnd(sdp, rq);
+ goto out;
+ } else if (rq->cmd_flags & REQ_FLUSH) {
+ ret = scsi_setup_flush_cmnd(sdp, rq);
+ goto out;
+ } else if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
ret = scsi_setup_blk_pc_cmnd(sdp, rq);
goto out;
} else if (rq->cmd_type != REQ_TYPE_FS) {
SCpnt->cmnd[0] = VARIABLE_LENGTH_CMD;
SCpnt->cmnd[7] = 0x18;
SCpnt->cmnd[9] = (rq_data_dir(rq) == READ) ? READ_32 : WRITE_32;
- SCpnt->cmnd[10] = protect | (blk_fua_rq(rq) ? 0x8 : 0);
+ SCpnt->cmnd[10] = protect | ((rq->cmd_flags & REQ_FUA) ? 0x8 : 0);
/* LBA */
SCpnt->cmnd[12] = sizeof(block) > 4 ? (unsigned char) (block >> 56) & 0xff : 0;
SCpnt->cmnd[31] = (unsigned char) this_count & 0xff;
} else if (block > 0xffffffff) {
SCpnt->cmnd[0] += READ_16 - READ_6;
- SCpnt->cmnd[1] = protect | (blk_fua_rq(rq) ? 0x8 : 0);
+ SCpnt->cmnd[1] = protect | ((rq->cmd_flags & REQ_FUA) ? 0x8 : 0);
SCpnt->cmnd[2] = sizeof(block) > 4 ? (unsigned char) (block >> 56) & 0xff : 0;
SCpnt->cmnd[3] = sizeof(block) > 4 ? (unsigned char) (block >> 48) & 0xff : 0;
SCpnt->cmnd[4] = sizeof(block) > 4 ? (unsigned char) (block >> 40) & 0xff : 0;
this_count = 0xffff;
SCpnt->cmnd[0] += READ_10 - READ_6;
- SCpnt->cmnd[1] = protect | (blk_fua_rq(rq) ? 0x8 : 0);
+ SCpnt->cmnd[1] = protect | ((rq->cmd_flags & REQ_FUA) ? 0x8 : 0);
SCpnt->cmnd[2] = (unsigned char) (block >> 24) & 0xff;
SCpnt->cmnd[3] = (unsigned char) (block >> 16) & 0xff;
SCpnt->cmnd[4] = (unsigned char) (block >> 8) & 0xff;
SCpnt->cmnd[7] = (unsigned char) (this_count >> 8) & 0xff;
SCpnt->cmnd[8] = (unsigned char) this_count & 0xff;
} else {
- if (unlikely(blk_fua_rq(rq))) {
+ if (unlikely(rq->cmd_flags & REQ_FUA)) {
/*
* This happens only if this drive failed
* 10byte rw command with ILLEGAL_REQUEST
* or from within the kernel (e.g. as a result of a mount(1) ).
* In the latter case @inode and @filp carry an abridged amount
* of information as noted above.
+ *
+ * Locking: called with bdev->bd_mutex held.
**/
static int sd_open(struct block_device *bdev, fmode_t mode)
{
if (!scsi_device_online(sdev))
goto error_out;
- if (!sdkp->openers++ && sdev->removable) {
+ if ((atomic_inc_return(&sdkp->openers) == 1) && sdev->removable) {
if (scsi_block_when_processing_errors(sdev))
scsi_set_medium_removal(sdev, SCSI_REMOVAL_PREVENT);
}
*
* Note: may block (uninterruptible) if error recovery is underway
* on this disk.
+ *
+ * Locking: called with bdev->bd_mutex held.
**/
static int sd_release(struct gendisk *disk, fmode_t mode)
{
SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_release\n"));
- if (!--sdkp->openers && sdev->removable) {
+ if (atomic_dec_return(&sdkp->openers) && sdev->removable) {
if (scsi_block_when_processing_errors(sdev))
scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW);
}
error = scsi_nonblockable_ioctl(sdp, cmd, p,
(mode & FMODE_NDELAY) != 0);
if (!scsi_block_when_processing_errors(sdp) || !error)
- return error;
+ goto out;
/*
* Send SCSI addressing ioctls directly to mid level, send other
switch (cmd) {
case SCSI_IOCTL_GET_IDLUN:
case SCSI_IOCTL_GET_BUS_NUMBER:
- return scsi_ioctl(sdp, cmd, p);
+ error = scsi_ioctl(sdp, cmd, p);
+ break;
default:
error = scsi_cmd_ioctl(disk->queue, disk, mode, cmd, p);
if (error != -ENOTTY)
- return error;
+ break;
+ error = scsi_ioctl(sdp, cmd, p);
+ break;
}
- return scsi_ioctl(sdp, cmd, p);
+out:
+ return error;
}
static void set_media_not_present(struct scsi_disk *sdkp)
return 0;
}
-static void sd_prepare_flush(struct request_queue *q, struct request *rq)
-{
- rq->cmd_type = REQ_TYPE_BLOCK_PC;
- rq->timeout = SD_TIMEOUT;
- rq->retries = SD_MAX_RETRIES;
- rq->cmd[0] = SYNCHRONIZE_CACHE;
- rq->cmd_len = 10;
-}
-
static void sd_rescan(struct device *dev)
{
struct scsi_disk *sdkp = scsi_disk_get_from_dev(dev);
.owner = THIS_MODULE,
.open = sd_open,
.release = sd_release,
- .locked_ioctl = sd_ioctl,
+ .ioctl = sd_ioctl,
.getgeo = sd_getgeo,
#ifdef CONFIG_COMPAT
.compat_ioctl = sd_compat_ioctl,
u64 bad_lba;
int info_valid;
- if (!blk_fs_request(scmd->request))
+ if (scmd->request->cmd_type != REQ_TYPE_FS)
return 0;
info_valid = scsi_get_sense_info_fld(scmd->sense_buffer,
int sense_valid = 0;
int sense_deferred = 0;
+ if (SCpnt->request->cmd_flags & REQ_DISCARD) {
+ if (!result)
+ scsi_set_resid(SCpnt, 0);
+ return good_bytes;
+ }
+
if (result) {
sense_valid = scsi_command_normalize_sense(SCpnt, &sshdr);
if (sense_valid)
/*
* Determine whether disk supports Data Integrity Field.
*/
-void sd_read_protection_type(struct scsi_disk *sdkp, unsigned char *buffer)
+static void sd_read_protection_type(struct scsi_disk *sdkp, unsigned char *buffer)
{
struct scsi_device *sdp = sdkp->device;
u8 type;
* The ATO bit indicates whether the DIF application tag is available
* for use by the operating system.
*/
-void sd_read_app_tag_own(struct scsi_disk *sdkp, unsigned char *buffer)
+static void sd_read_app_tag_own(struct scsi_disk *sdkp, unsigned char *buffer)
{
int res, offset;
struct scsi_device *sdp = sdkp->device;
else
ordered = QUEUE_ORDERED_DRAIN;
- blk_queue_ordered(sdkp->disk->queue, ordered, sd_prepare_flush);
+ blk_queue_ordered(sdkp->disk->queue, ordered);
set_capacity(disk, sdkp->capacity);
kfree(buffer);
sd_revalidate_disk(gd);
blk_queue_prep_rq(sdp->request_queue, sd_prep_fn);
+ blk_queue_unprep_rq(sdp->request_queue, sd_unprep_fn);
gd->driverfs_dev = &sdp->sdev_gendev;
gd->flags = GENHD_FL_EXT_DEVT;
struct scsi_device *sdp = to_scsi_device(dev);
struct scsi_disk *sdkp;
struct gendisk *gd;
- u32 index;
+ int index;
int error;
error = -ENODEV;
sdkp->driver = &sd_template;
sdkp->disk = gd;
sdkp->index = index;
- sdkp->openers = 0;
+ atomic_set(&sdkp->openers, 0);
sdkp->previous_state = 1;
if (!sdp->request_queue->rq_timeout) {
async_synchronize_full();
blk_queue_prep_rq(sdkp->device->request_queue, scsi_prep_fn);
+ blk_queue_unprep_rq(sdkp->device->request_queue, NULL);
device_del(&sdkp->dev);
del_gendisk(sdkp->disk);
sd_shutdown(dev);
struct scsi_device *device;
struct device dev;
struct gendisk *disk;
- unsigned int openers; /* protected by BKL for now, yuck */
+ atomic_t openers;
sector_t capacity; /* size in 512-byte sectors */
u32 index;
unsigned short hw_sector_size;
int len, size = sizeof(struct sg_iovec) * iov_count;
struct iovec *iov;
- iov = kmalloc(size, GFP_ATOMIC);
- if (!iov)
- return -ENOMEM;
-
- if (copy_from_user(iov, hp->dxferp, size)) {
- kfree(iov);
- return -EFAULT;
- }
+ iov = memdup_user(hp->dxferp, size);
+ if (IS_ERR(iov))
+ return PTR_ERR(iov);
len = iov_length(iov, iov_count);
if (hp->dxfer_len < len) {
#include <linux/init.h>
#include <linux/blkdev.h>
#include <linux/mutex.h>
+#include <linux/smp_lock.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
static int sr_block_open(struct block_device *bdev, fmode_t mode)
{
- struct scsi_cd *cd = scsi_cd_get(bdev->bd_disk);
+ struct scsi_cd *cd;
int ret = -ENXIO;
+ lock_kernel();
+ cd = scsi_cd_get(bdev->bd_disk);
if (cd) {
ret = cdrom_open(&cd->cdi, bdev, mode);
if (ret)
scsi_cd_put(cd);
}
+ unlock_kernel();
return ret;
}
static int sr_block_release(struct gendisk *disk, fmode_t mode)
{
struct scsi_cd *cd = scsi_cd(disk);
+ lock_kernel();
cdrom_release(&cd->cdi, mode);
scsi_cd_put(cd);
+ unlock_kernel();
return 0;
}
void __user *argp = (void __user *)arg;
int ret;
+ lock_kernel();
+
/*
* Send SCSI addressing ioctls directly to mid level, send other
* ioctls to cdrom/block level.
switch (cmd) {
case SCSI_IOCTL_GET_IDLUN:
case SCSI_IOCTL_GET_BUS_NUMBER:
- return scsi_ioctl(sdev, cmd, argp);
+ ret = scsi_ioctl(sdev, cmd, argp);
+ goto out;
}
ret = cdrom_ioctl(&cd->cdi, bdev, mode, cmd, arg);
if (ret != -ENOSYS)
- return ret;
+ goto out;
/*
* ENODEV means that we didn't recognise the ioctl, or that we
ret = scsi_nonblockable_ioctl(sdev, cmd, argp,
(mode & FMODE_NDELAY) != 0);
if (ret != -ENODEV)
- return ret;
- return scsi_ioctl(sdev, cmd, argp);
+ goto out;
+ ret = scsi_ioctl(sdev, cmd, argp);
+
+out:
+ unlock_kernel();
+ return ret;
}
static int sr_block_media_changed(struct gendisk *disk)
.owner = THIS_MODULE,
.open = sr_block_open,
.release = sr_block_release,
- .locked_ioctl = sr_block_ioctl,
+ .ioctl = sr_block_ioctl,
.media_changed = sr_block_media_changed,
/*
* No compat_ioctl for now because sr_block_ioctl never
if((count > SUN3_DMA_MINSIZE) && (sun3_dma_setup_done
!= cmd))
{
- if(blk_fs_request(cmd->request)) {
+ if (cmd->request->cmd_type == REQ_TYPE_FS) {
sun3scsi_dma_setup(d, count,
rq_data_dir(cmd->request));
sun3_dma_setup_done = cmd;
struct scsi_cmnd *cmd,
int write_flag)
{
- if(blk_fs_request(cmd->request))
+ if (cmd->request->cmd_type == REQ_TYPE_FS)
return wanted;
else
return 0;
struct scsi_cmnd *cmd,
int write_flag)
{
- if(blk_fs_request(cmd->request))
+ if (cmd->request->cmd_type == REQ_TYPE_FS)
return wanted;
else
return 0;
struct ktermios *old)
{
unsigned long flags;
- unsigned int baud, quot, h_lcr;
+ unsigned int baud, quot, h_lcr, b;
/*
* We don't support modem control lines.
*/
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
quot = uart_get_divisor(port, baud);
-
- if (port->state && port->state->port.tty) {
- struct tty_struct *tty = port->state->port.tty;
- unsigned int b = port->uartclk / (16 * quot);
- tty_encode_baud_rate(tty, b, b);
- }
+ b = port->uartclk / (16 * quot);
+ tty_termios_encode_baud_rate(termios, b, b);
switch (termios->c_cflag & CSIZE) {
case CS5:
#define M68K_CLOCK (16667000) /* FIXME: 16MHz is likely wrong */
-#ifdef CONFIG_CONSOLE
-extern wait_queue_head_t keypress_wait;
-#endif
-
struct tty_driver *serial_driver;
/* number of characters left in xmit buffer before we ask for more */
* Setup for console. Argument comes from the boot command line.
*/
-#if defined(CONFIG_M68EZ328ADS) || defined(CONFIG_ALMA_ANS) || defined(CONFIG_DRAGONIXVZ)
-#define CONSOLE_BAUD_RATE 115200
-#define DEFAULT_CBAUD B115200
-#else
- /* (es) */
- /* note: this is messy, but it works, again, perhaps defined somewhere else?*/
- #ifdef CONFIG_M68VZ328
- #define CONSOLE_BAUD_RATE 19200
- #define DEFAULT_CBAUD B19200
- #endif
- /* (/es) */
+/* note: this is messy, but it works, again, perhaps defined somewhere else?*/
+#ifdef CONFIG_M68VZ328
+#define CONSOLE_BAUD_RATE 19200
+#define DEFAULT_CBAUD B19200
#endif
+
#ifndef CONSOLE_BAUD_RATE
#define CONSOLE_BAUD_RATE 9600
#define DEFAULT_CBAUD B9600
return;
#endif /* CONFIG_MAGIC_SYSRQ */
}
- /* It is a 'keyboard interrupt' ;-) */
-#ifdef CONFIG_CONSOLE
- wake_up(&keypress_wait);
-#endif
}
if(!tty)
retval = -ERESTARTSYS;
break;
}
+ tty_unlock();
schedule();
+ tty_lock();
}
current->state = TASK_RUNNING;
remove_wait_queue(&info->open_wait, &wait);
printk("jiff=%lu...", jiffies);
#endif
- lock_kernel();
/* We go through the loop at least once because we can't tell
* exactly when the last character exits the shifter. There can
* be at least two characters waiting to be sent after the buffers
bdp--;
} while (bdp->status & BD_SC_READY);
current->state = TASK_RUNNING;
- unlock_kernel();
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
#endif
printk("block_til_ready blocking: ttys%d, count = %d\n",
info->line, state->count);
#endif
+ tty_unlock();
schedule();
+ tty_lock();
}
current->state = TASK_RUNNING;
remove_wait_queue(&info->open_wait, &wait);
.fifo_size = 128,
.tx_loadsz = 128,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
- .flags = UART_CAP_FIFO,
+ .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
},
[PORT_16654] = {
.name = "ST16654",
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00,
.flags = UART_CAP_FIFO | UART_CAP_AFE,
},
+ [PORT_U6_16550A] = {
+ .name = "U6_16550A",
+ .fifo_size = 64,
+ .tx_loadsz = 64,
+ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+ .flags = UART_CAP_FIFO | UART_CAP_AFE,
+ },
};
#if defined(CONFIG_MIPS_ALCHEMY)
DEBUG_AUTOCONF("Couldn't force IER_UUE to 0 ");
}
serial_outp(up, UART_IER, iersave);
+
+ /*
+ * We distinguish between 16550A and U6 16550A by counting
+ * how many bytes are in the FIFO.
+ */
+ if (up->port.type == PORT_16550A && size_fifo(up) == 64) {
+ up->port.type = PORT_U6_16550A;
+ up->capabilities |= UART_CAP_AFE;
+ }
}
/*
return quot;
}
-static void
-serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
- struct ktermios *old)
+void
+serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
unsigned char cval, fcr = 0;
if (tty_termios_baud_rate(termios))
tty_termios_encode_baud_rate(termios, baud, baud);
}
+EXPORT_SYMBOL(serial8250_do_set_termios);
static void
-serial8250_set_ldisc(struct uart_port *port)
+serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
{
- int line = port->line;
-
- if (line >= port->state->port.tty->driver->num)
- return;
+ if (port->set_termios)
+ port->set_termios(port, termios, old);
+ else
+ serial8250_do_set_termios(port, termios, old);
+}
- if (port->state->port.tty->ldisc->ops->num == N_PPS) {
+static void
+serial8250_set_ldisc(struct uart_port *port, int new)
+{
+ if (new == N_PPS) {
port->flags |= UPF_HARDPPS_CD;
serial8250_enable_ms(port);
} else
port.type = p->type;
port.serial_in = p->serial_in;
port.serial_out = p->serial_out;
+ port.set_termios = p->set_termios;
port.dev = &dev->dev;
port.irqflags |= irqflag;
ret = serial8250_register_port(&port);
uart->port.serial_in = port->serial_in;
if (port->serial_out)
uart->port.serial_out = port->serial_out;
+ /* Possibly override set_termios call */
+ if (port->set_termios)
+ uart->port.set_termios = port->set_termios;
ret = uart_add_one_port(&serial8250_reg, &uart->port);
if (ret == 0)
* The user can specify the device directly, e.g.,
* earlycon=uart8250,io,0x3f8,9600n8
* earlycon=uart8250,mmio,0xff5e0000,115200n8
+ * earlycon=uart8250,mmio32,0xff5e0000,115200n8
* or
* console=uart8250,io,0x3f8,9600n8
* console=uart8250,mmio,0xff5e0000,115200n8
+ * console=uart8250,mmio32,0xff5e0000,115200n8
*/
#include <linux/tty.h>
static unsigned int __init serial_in(struct uart_port *port, int offset)
{
- if (port->iotype == UPIO_MEM)
+ switch (port->iotype) {
+ case UPIO_MEM:
return readb(port->membase + offset);
- else
+ case UPIO_MEM32:
+ return readl(port->membase + (offset << 2));
+ case UPIO_PORT:
return inb(port->iobase + offset);
+ default:
+ return 0;
+ }
}
static void __init serial_out(struct uart_port *port, int offset, int value)
{
- if (port->iotype == UPIO_MEM)
+ switch (port->iotype) {
+ case UPIO_MEM:
writeb(value, port->membase + offset);
- else
+ break;
+ case UPIO_MEM32:
+ writel(value, port->membase + (offset << 2));
+ break;
+ case UPIO_PORT:
outb(value, port->iobase + offset);
+ break;
+ }
}
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
char *options)
{
struct uart_port *port = &device->port;
- int mmio, length;
+ int mmio, mmio32, length;
if (!options)
return -ENODEV;
port->uartclk = BASE_BAUD * 16;
- if (!strncmp(options, "mmio,", 5)) {
- port->iotype = UPIO_MEM;
- port->mapbase = simple_strtoul(options + 5, &options, 0);
+
+ mmio = !strncmp(options, "mmio,", 5);
+ mmio32 = !strncmp(options, "mmio32,", 7);
+ if (mmio || mmio32) {
+ port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32);
+ port->mapbase = simple_strtoul(options + (mmio ? 5 : 7),
+ &options, 0);
+ if (mmio32)
+ port->regshift = 2;
#ifdef CONFIG_FIX_EARLYCON_MEM
set_fixmap_nocache(FIX_EARLYCON_MEM_BASE,
port->mapbase & PAGE_MASK);
if (!port->membase) {
printk(KERN_ERR "%s: Couldn't ioremap 0x%llx\n",
__func__,
- (unsigned long long)port->mapbase);
+ (unsigned long long) port->mapbase);
return -ENOMEM;
}
#endif
- mmio = 1;
} else if (!strncmp(options, "io,", 3)) {
port->iotype = UPIO_PORT;
port->iobase = simple_strtoul(options + 3, &options, 0);
device->baud);
}
- printk(KERN_INFO "Early serial console at %s 0x%llx (options '%s')\n",
- mmio ? "MMIO" : "I/O port",
- mmio ? (unsigned long long) port->mapbase
- : (unsigned long long) port->iobase,
- device->options);
+ if (mmio || mmio32)
+ printk(KERN_INFO
+ "Early serial console at MMIO%s 0x%llu (options '%s')\n",
+ mmio32 ? "32" : "",
+ (unsigned long long)port->mapbase,
+ device->options);
+ else
+ printk(KERN_INFO
+ "Early serial console at I/O port 0x%lu (options '%s')\n",
+ port->iobase,
+ device->options);
+
return 0;
}
#define PCI_DEVICE_ID_TITAN_800E 0xA014
#define PCI_DEVICE_ID_TITAN_200EI 0xA016
#define PCI_DEVICE_ID_TITAN_200EISI 0xA017
+#define PCI_DEVICE_ID_OXSEMI_16PCI958 0x9538
/* Unknown vendors/cards - this should not be in linux/pci_ids.h */
#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584
pbn_b2_4_921600,
pbn_b2_8_921600,
+ pbn_b2_8_1152000,
+
pbn_b2_bt_1_115200,
pbn_b2_bt_2_115200,
pbn_b2_bt_4_115200,
.uart_offset = 8,
},
+ [pbn_b2_8_1152000] = {
+ .flags = FL_BASE2,
+ .num_ports = 8,
+ .base_baud = 1152000,
+ .uart_offset = 8,
+ },
+
[pbn_b2_bt_1_115200] = {
.flags = FL_BASE2|FL_BASE_BARS,
.num_ports = 1,
{ PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_bt_2_921600 },
+ { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI958,
+ PCI_ANY_ID , PCI_ANY_ID, 0, 0,
+ pbn_b2_8_1152000 },
/*
* Oxford Semiconductor Inc. Tornado PCI express device range.
config SERIAL_S5PV210
tristate "Samsung S5PV210 Serial port support"
- depends on SERIAL_SAMSUNG && (CPU_S5PV210 || CPU_S5P6442)
- select SERIAL_SAMSUNG_UARTS_4 if CPU_S5PV210
+ depends on SERIAL_SAMSUNG && (CPU_S5PV210 || CPU_S5P6442 || CPU_S5PV310)
+ select SERIAL_SAMSUNG_UARTS_4 if (CPU_S5PV210 || CPU_S5PV310)
default y
help
Serial port support for Samsung's S5P Family of SoC's
+
config SERIAL_MAX3100
tristate "MAX3100 support"
depends on SPI
help
MAX3100 chip support
+config SERIAL_MAX3107
+ tristate "MAX3107 support"
+ depends on SPI
+ select SERIAL_CORE
+ help
+ MAX3107 chip support
+
+config SERIAL_MAX3107_AAVA
+ tristate "MAX3107 AAVA platform support"
+ depends on X86_MRST && SERIAL_MAX3107 && GPIOLIB
+ select SERIAL_CORE
+ help
+ Support for the MAX3107 chip configuration found on the AAVA
+ platform. Includes the extra initialisation and GPIO support
+ neded for this device.
+
config SERIAL_DZ
bool "DECstation DZ serial driver"
depends on MACH_DECSTATION && 32BIT
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
+config SERIAL_MRST_MAX3110
+ tristate "SPI UART driver for Max3110"
+ depends on SPI_DW_PCI
+ select SERIAL_CORE
+ select SERIAL_CORE_CONSOLE
+ help
+ This is the UART protocol driver for the MAX3110 device on
+ the Intel Moorestown platform. On other systems use the max3100
+ driver.
+
+config MRST_MAX3110_IRQ
+ boolean "Enable GPIO IRQ for Max3110 over Moorestown"
+ default n
+ depends on SERIAL_MRST_MAX3110 && GPIO_LANGWELL
+ help
+ This has to be enabled after Moorestown GPIO driver is loaded
+
+config SERIAL_MFD_HSU
+ tristate "Medfield High Speed UART support"
+ depends on PCI
+ select SERIAL_CORE
+
+config SERIAL_MFD_HSU_CONSOLE
+ boolean "Medfile HSU serial console support"
+ depends on SERIAL_MFD_HSU=y
+ select SERIAL_CORE_CONSOLE
+
config SERIAL_BFIN
tristate "Blackfin serial port support"
depends on BLACKFIN
obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o
obj-$(CONFIG_SERIAL_S5PV210) += s5pv210.o
obj-$(CONFIG_SERIAL_MAX3100) += max3100.o
+obj-$(CONFIG_SERIAL_MAX3107) += max3107.o
+obj-$(CONFIG_SERIAL_MAX3107_AAVA) += max3107-aava.o
obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o
obj-$(CONFIG_SERIAL_MUX) += mux.o
obj-$(CONFIG_SERIAL_68328) += 68328serial.o
obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
+obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o
+obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o
static void altera_uart_console_putc(struct uart_port *port, const char c)
{
while (!(readl(port->membase + ALTERA_UART_STATUS_REG) &
- ALTERA_UART_STATUS_TRDY_MSK))
+ ALTERA_UART_STATUS_TRDY_MSK))
cpu_relax();
writel(c, port->membase + ALTERA_UART_TXDATA_REG);
if (rs485conf->flags & SER_RS485_ENABLED) {
dev_dbg(port->dev, "Setting UART to RS485\n");
atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
- UART_PUT_TTGR(port, rs485conf->delay_rts_before_send);
+ if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND)
+ UART_PUT_TTGR(port, rs485conf->delay_rts_after_send);
mode |= ATMEL_US_USMODE_RS485;
} else {
dev_dbg(port->dev, "Setting UART to RS232\n");
if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
dev_dbg(port->dev, "Setting UART to RS485\n");
- UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send);
+ if (atmel_port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
+ UART_PUT_TTGR(port,
+ atmel_port->rs485.delay_rts_after_send);
mode |= ATMEL_US_USMODE_RS485;
} else {
dev_dbg(port->dev, "Setting UART to RS232\n");
if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
dev_dbg(port->dev, "Setting UART to RS485\n");
- UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_before_send);
+ if (atmel_port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
+ UART_PUT_TTGR(port,
+ atmel_port->rs485.delay_rts_after_send);
mode |= ATMEL_US_USMODE_RS485;
} else {
dev_dbg(port->dev, "Setting UART to RS232\n");
* Enable the IrDA function if tty->ldisc.num is N_IRDA.
* In other cases, disable IrDA function.
*/
-static void bfin_serial_set_ldisc(struct uart_port *port)
+static void bfin_serial_set_ldisc(struct uart_port *port, int ld)
{
int line = port->line;
unsigned short val;
- if (line >= port->state->port.tty->driver->num)
- return;
-
- switch (port->state->port.tty->termios->c_line) {
+ switch (ld) {
case N_IRDA:
val = UART_GET_GCTL(&bfin_serial_ports[line]);
val |= (IREN | RPOLC);
* Check R_DMA_CHx_STATUS bit 0-6=number of available bytes in FIFO
* R_DMA_CHx_HWSW bit 31-16=nbr of bytes left in DMA buffer (0=64k)
*/
- lock_kernel();
orig_jiffies = jiffies;
while (info->xmit.head != info->xmit.tail || /* More in send queue */
(*info->ostatusadr & 0x007f) || /* more in FIFO */
curr_time_usec - info->last_tx_active_usec;
}
set_current_state(TASK_RUNNING);
- unlock_kernel();
}
/*
*/
if (tty_hung_up_p(filp) ||
(info->flags & ASYNC_CLOSING)) {
- wait_event_interruptible(info->close_wait,
+ wait_event_interruptible_tty(info->close_wait,
!(info->flags & ASYNC_CLOSING));
#ifdef SERIAL_DO_RESTART
if (info->flags & ASYNC_HUP_NOTIFY)
printk("block_til_ready blocking: ttyS%d, count = %d\n",
info->line, info->count);
#endif
+ tty_unlock();
schedule();
+ tty_lock();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&info->open_wait, &wait);
*/
if (tty_hung_up_p(filp) ||
(info->flags & ASYNC_CLOSING)) {
- wait_event_interruptible(info->close_wait,
+ wait_event_interruptible_tty(info->close_wait,
!(info->flags & ASYNC_CLOSING));
#ifdef SERIAL_DO_RESTART
return ((info->flags & ASYNC_HUP_NOTIFY) ?
INIT_WORK(&info->work, do_softint);
if (info->enabled) {
- printk(KERN_INFO "%s%d at 0x%x is a builtin UART with DMA\n",
- serial_driver->name, info->line, (unsigned int)info->ioport);
+ printk(KERN_INFO "%s%d at %p is a builtin UART with DMA\n",
+ serial_driver->name, info->line, info->ioport);
}
}
#ifdef CONFIG_ETRAX_FAST_TIMER
rational_best_approximation(16 * div * baud, sport->port.uartclk,
1 << 16, 1 << 16, &num, &denom);
- if (port->state && port->state->port.tty) {
- tdiv64 = sport->port.uartclk;
- tdiv64 *= num;
- do_div(tdiv64, denom * 16 * div);
- tty_encode_baud_rate(sport->port.state->port.tty,
+ tdiv64 = sport->port.uartclk;
+ tdiv64 *= num;
+ do_div(tdiv64, denom * 16 * div);
+ tty_termios_encode_baud_rate(termios,
(speed_t)tdiv64, (speed_t)tdiv64);
- }
num -= 1;
denom -= 1;
struct ktermios *new_termios, struct ktermios *old_termios)
{
struct ioc3_port *port = get_ioc3_port(the_port);
- unsigned int cflag;
+ unsigned int cflag, iflag;
int baud;
int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8;
struct uart_state *state = the_port->state;
cflag = new_termios->c_cflag;
+ iflag = new_termios->c_iflag;
switch (cflag & CSIZE) {
case CS5:
state->port.tty->low_latency = 1;
- if (I_IGNPAR(state->port.tty))
+ if (iflag & IGNPAR)
the_port->ignore_status_mask &= ~(N_PARITY_ERROR
| N_FRAMING_ERROR);
- if (I_IGNBRK(state->port.tty)) {
+ if (iflag & IGNBRK) {
the_port->ignore_status_mask &= ~N_BREAK;
- if (I_IGNPAR(state->port.tty))
+ if (iflag & IGNPAR)
the_port->ignore_status_mask &= ~N_OVERRUN_ERROR;
}
if (!(cflag & CREAD)) {
{
struct ioc4_port *port = get_ioc4_port(the_port, 0);
int baud, bits;
- unsigned cflag;
+ unsigned cflag, iflag;
int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8;
struct uart_state *state = the_port->state;
cflag = new_termios->c_cflag;
+ iflag = new_termios->c_iflag;
switch (cflag & CSIZE) {
case CS5:
state->port.tty->low_latency = 1;
- if (I_IGNPAR(state->port.tty))
+ if (iflag & IGNPAR)
the_port->ignore_status_mask &= ~(N_PARITY_ERROR
| N_FRAMING_ERROR);
- if (I_IGNBRK(state->port.tty)) {
+ if (iflag & IGNBRK) {
the_port->ignore_status_mask &= ~N_BREAK;
- if (I_IGNPAR(state->port.tty))
+ if (iflag & IGNPAR)
the_port->ignore_status_mask &= ~N_OVERRUN_ERROR;
}
if (!(cflag & CREAD)) {
int baud = 0;
unsigned cflag;
u32 param_new, param_mask, parity = 0;
- struct tty_struct *tty = s->port.state->port.tty;
dev_dbg(&s->spi->dev, "%s\n", __func__);
- if (!tty)
- return;
cflag = termios->c_cflag;
param_new = 0;
param_mask = 0;
- baud = tty_get_baud_rate(tty);
+ baud = tty_termios_baud_rate(termios);
param_new = s->conf & MAX3100_BAUD;
switch (baud) {
case 300:
default:
baud = s->baud;
}
- tty_encode_baud_rate(tty, baud, baud);
+ tty_termios_encode_baud_rate(termios, baud, baud);
s->baud = baud;
param_mask |= MAX3100_BAUD;
--- /dev/null
+/*
+ * max3107.c - spi uart protocol driver for Maxim 3107
+ * Based on max3100.c
+ * by Christian Pellegrin <chripell@evolware.org>
+ * and max3110.c
+ * by Feng Tang <feng.tang@intel.com>
+ *
+ * Copyright (C) Aavamobile 2009
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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/delay.h>
+#include <linux/device.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/spi/spi.h>
+#include <linux/freezer.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/sfi.h>
+#include <asm/mrst.h>
+#include "max3107.h"
+
+/* GPIO direction to input function */
+static int max3107_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+ struct max3107_port *s = container_of(chip, struct max3107_port, chip);
+ u16 buf[1]; /* Buffer for SPI transfer */
+
+ if (offset >= MAX3107_GPIO_COUNT) {
+ dev_err(&s->spi->dev, "Invalid GPIO\n");
+ return -EINVAL;
+ }
+
+ /* Read current GPIO configuration register */
+ buf[0] = MAX3107_GPIOCFG_REG;
+ /* Perform SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) {
+ dev_err(&s->spi->dev, "SPI transfer GPIO read failed\n");
+ return -EIO;
+ }
+ buf[0] &= MAX3107_SPI_RX_DATA_MASK;
+
+ /* Set GPIO to input */
+ buf[0] &= ~(0x0001 << offset);
+
+ /* Write new GPIO configuration register value */
+ buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG);
+ /* Perform SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, NULL, 2)) {
+ dev_err(&s->spi->dev, "SPI transfer GPIO write failed\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+/* GPIO direction to output function */
+static int max3107_gpio_direction_out(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct max3107_port *s = container_of(chip, struct max3107_port, chip);
+ u16 buf[2]; /* Buffer for SPI transfers */
+
+ if (offset >= MAX3107_GPIO_COUNT) {
+ dev_err(&s->spi->dev, "Invalid GPIO\n");
+ return -EINVAL;
+ }
+
+ /* Read current GPIO configuration and data registers */
+ buf[0] = MAX3107_GPIOCFG_REG;
+ buf[1] = MAX3107_GPIODATA_REG;
+ /* Perform SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) {
+ dev_err(&s->spi->dev, "SPI transfer gpio failed\n");
+ return -EIO;
+ }
+ buf[0] &= MAX3107_SPI_RX_DATA_MASK;
+ buf[1] &= MAX3107_SPI_RX_DATA_MASK;
+
+ /* Set GPIO to output */
+ buf[0] |= (0x0001 << offset);
+ /* Set value */
+ if (value)
+ buf[1] |= (0x0001 << offset);
+ else
+ buf[1] &= ~(0x0001 << offset);
+
+ /* Write new GPIO configuration and data register values */
+ buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG);
+ buf[1] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG);
+ /* Perform SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, NULL, 4)) {
+ dev_err(&s->spi->dev,
+ "SPI transfer for GPIO conf data w failed\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+/* GPIO value query function */
+static int max3107_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct max3107_port *s = container_of(chip, struct max3107_port, chip);
+ u16 buf[1]; /* Buffer for SPI transfer */
+
+ if (offset >= MAX3107_GPIO_COUNT) {
+ dev_err(&s->spi->dev, "Invalid GPIO\n");
+ return -EINVAL;
+ }
+
+ /* Read current GPIO data register */
+ buf[0] = MAX3107_GPIODATA_REG;
+ /* Perform SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) {
+ dev_err(&s->spi->dev, "SPI transfer GPIO data r failed\n");
+ return -EIO;
+ }
+ buf[0] &= MAX3107_SPI_RX_DATA_MASK;
+
+ /* Return value */
+ return buf[0] & (0x0001 << offset);
+}
+
+/* GPIO value set function */
+static void max3107_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct max3107_port *s = container_of(chip, struct max3107_port, chip);
+ u16 buf[2]; /* Buffer for SPI transfers */
+
+ if (offset >= MAX3107_GPIO_COUNT) {
+ dev_err(&s->spi->dev, "Invalid GPIO\n");
+ return;
+ }
+
+ /* Read current GPIO configuration registers*/
+ buf[0] = MAX3107_GPIODATA_REG;
+ buf[1] = MAX3107_GPIOCFG_REG;
+ /* Perform SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) {
+ dev_err(&s->spi->dev,
+ "SPI transfer for GPIO data and config read failed\n");
+ return;
+ }
+ buf[0] &= MAX3107_SPI_RX_DATA_MASK;
+ buf[1] &= MAX3107_SPI_RX_DATA_MASK;
+
+ if (!(buf[1] & (0x0001 << offset))) {
+ /* Configured as input, can't set value */
+ dev_warn(&s->spi->dev,
+ "Trying to set value for input GPIO\n");
+ return;
+ }
+
+ /* Set value */
+ if (value)
+ buf[0] |= (0x0001 << offset);
+ else
+ buf[0] &= ~(0x0001 << offset);
+
+ /* Write new GPIO data register value */
+ buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG);
+ /* Perform SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, NULL, 2))
+ dev_err(&s->spi->dev, "SPI transfer GPIO data w failed\n");
+}
+
+/* GPIO chip data */
+static struct gpio_chip max3107_gpio_chip = {
+ .owner = THIS_MODULE,
+ .direction_input = max3107_gpio_direction_in,
+ .direction_output = max3107_gpio_direction_out,
+ .get = max3107_gpio_get,
+ .set = max3107_gpio_set,
+ .can_sleep = 1,
+ .base = MAX3107_GPIO_BASE,
+ .ngpio = MAX3107_GPIO_COUNT,
+};
+
+/**
+ * max3107_aava_reset - reset on AAVA systems
+ * @spi: The SPI device we are probing
+ *
+ * Reset the device ready for probing.
+ */
+
+static int max3107_aava_reset(struct spi_device *spi)
+{
+ /* Reset the chip */
+ if (gpio_request(MAX3107_RESET_GPIO, "max3107")) {
+ pr_err("Requesting RESET GPIO failed\n");
+ return -EIO;
+ }
+ if (gpio_direction_output(MAX3107_RESET_GPIO, 0)) {
+ pr_err("Setting RESET GPIO to 0 failed\n");
+ gpio_free(MAX3107_RESET_GPIO);
+ return -EIO;
+ }
+ msleep(MAX3107_RESET_DELAY);
+ if (gpio_direction_output(MAX3107_RESET_GPIO, 1)) {
+ pr_err("Setting RESET GPIO to 1 failed\n");
+ gpio_free(MAX3107_RESET_GPIO);
+ return -EIO;
+ }
+ gpio_free(MAX3107_RESET_GPIO);
+ msleep(MAX3107_WAKEUP_DELAY);
+ return 0;
+}
+
+static int max3107_aava_configure(struct max3107_port *s)
+{
+ int retval;
+
+ /* Initialize GPIO chip data */
+ s->chip = max3107_gpio_chip;
+ s->chip.label = s->spi->modalias;
+ s->chip.dev = &s->spi->dev;
+
+ /* Add GPIO chip */
+ retval = gpiochip_add(&s->chip);
+ if (retval) {
+ dev_err(&s->spi->dev, "Adding GPIO chip failed\n");
+ return retval;
+ }
+
+ /* Temporary fix for EV2 boot problems, set modem reset to 0 */
+ max3107_gpio_direction_out(&s->chip, 3, 0);
+ return 0;
+}
+
+#if 0
+/* This will get enabled once we have the board stuff merged for this
+ specific case */
+
+static const struct baud_table brg13_ext[] = {
+ { 300, MAX3107_BRG13_B300 },
+ { 600, MAX3107_BRG13_B600 },
+ { 1200, MAX3107_BRG13_B1200 },
+ { 2400, MAX3107_BRG13_B2400 },
+ { 4800, MAX3107_BRG13_B4800 },
+ { 9600, MAX3107_BRG13_B9600 },
+ { 19200, MAX3107_BRG13_B19200 },
+ { 57600, MAX3107_BRG13_B57600 },
+ { 115200, MAX3107_BRG13_B115200 },
+ { 230400, MAX3107_BRG13_B230400 },
+ { 460800, MAX3107_BRG13_B460800 },
+ { 921600, MAX3107_BRG13_B921600 },
+ { 0, 0 }
+};
+
+static void max3107_aava_init(struct max3107_port *s)
+{
+ /*override for AAVA SC specific*/
+ if (mrst_platform_id() == MRST_PLATFORM_AAVA_SC) {
+ if (get_koski_build_id() <= KOSKI_EV2)
+ if (s->ext_clk) {
+ s->brg_cfg = MAX3107_BRG13_B9600;
+ s->baud_tbl = (struct baud_table *)brg13_ext;
+ }
+ }
+}
+#endif
+
+static int __devexit max3107_aava_remove(struct spi_device *spi)
+{
+ struct max3107_port *s = dev_get_drvdata(&spi->dev);
+
+ /* Remove GPIO chip */
+ if (gpiochip_remove(&s->chip))
+ dev_warn(&spi->dev, "Removing GPIO chip failed\n");
+
+ /* Then do the default remove */
+ return max3107_remove(spi);
+}
+
+/* Platform data */
+static struct max3107_plat aava_plat_data = {
+ .loopback = 0,
+ .ext_clk = 1,
+/* .init = max3107_aava_init, */
+ .configure = max3107_aava_configure,
+ .hw_suspend = max3107_hw_susp,
+ .polled_mode = 0,
+ .poll_time = 0,
+};
+
+
+static int __devinit max3107_probe_aava(struct spi_device *spi)
+{
+ int err = max3107_aava_reset(spi);
+ if (err < 0)
+ return err;
+ return max3107_probe(spi, &aava_plat_data);
+}
+
+/* Spi driver data */
+static struct spi_driver max3107_driver = {
+ .driver = {
+ .name = "aava-max3107",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = max3107_probe_aava,
+ .remove = __devexit_p(max3107_aava_remove),
+ .suspend = max3107_suspend,
+ .resume = max3107_resume,
+};
+
+/* Driver init function */
+static int __init max3107_init(void)
+{
+ return spi_register_driver(&max3107_driver);
+}
+
+/* Driver exit function */
+static void __exit max3107_exit(void)
+{
+ spi_unregister_driver(&max3107_driver);
+}
+
+module_init(max3107_init);
+module_exit(max3107_exit);
+
+MODULE_DESCRIPTION("MAX3107 driver");
+MODULE_AUTHOR("Aavamobile");
+MODULE_ALIAS("aava-max3107-spi");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * max3107.c - spi uart protocol driver for Maxim 3107
+ * Based on max3100.c
+ * by Christian Pellegrin <chripell@evolware.org>
+ * and max3110.c
+ * by Feng Tang <feng.tang@intel.com>
+ *
+ * Copyright (C) Aavamobile 2009
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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/delay.h>
+#include <linux/device.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/freezer.h>
+#include "max3107.h"
+
+static const struct baud_table brg26_ext[] = {
+ { 300, MAX3107_BRG26_B300 },
+ { 600, MAX3107_BRG26_B600 },
+ { 1200, MAX3107_BRG26_B1200 },
+ { 2400, MAX3107_BRG26_B2400 },
+ { 4800, MAX3107_BRG26_B4800 },
+ { 9600, MAX3107_BRG26_B9600 },
+ { 19200, MAX3107_BRG26_B19200 },
+ { 57600, MAX3107_BRG26_B57600 },
+ { 115200, MAX3107_BRG26_B115200 },
+ { 230400, MAX3107_BRG26_B230400 },
+ { 460800, MAX3107_BRG26_B460800 },
+ { 921600, MAX3107_BRG26_B921600 },
+ { 0, 0 }
+};
+
+static const struct baud_table brg13_int[] = {
+ { 300, MAX3107_BRG13_IB300 },
+ { 600, MAX3107_BRG13_IB600 },
+ { 1200, MAX3107_BRG13_IB1200 },
+ { 2400, MAX3107_BRG13_IB2400 },
+ { 4800, MAX3107_BRG13_IB4800 },
+ { 9600, MAX3107_BRG13_IB9600 },
+ { 19200, MAX3107_BRG13_IB19200 },
+ { 57600, MAX3107_BRG13_IB57600 },
+ { 115200, MAX3107_BRG13_IB115200 },
+ { 230400, MAX3107_BRG13_IB230400 },
+ { 460800, MAX3107_BRG13_IB460800 },
+ { 921600, MAX3107_BRG13_IB921600 },
+ { 0, 0 }
+};
+
+static u32 get_new_brg(int baud, struct max3107_port *s)
+{
+ int i;
+ const struct baud_table *baud_tbl = s->baud_tbl;
+
+ for (i = 0; i < 13; i++) {
+ if (baud == baud_tbl[i].baud)
+ return baud_tbl[i].new_brg;
+ }
+
+ return 0;
+}
+
+/* Perform SPI transfer for write/read of device register(s) */
+int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len)
+{
+ struct spi_message spi_msg;
+ struct spi_transfer spi_xfer;
+
+ /* Initialize SPI ,message */
+ spi_message_init(&spi_msg);
+
+ /* Initialize SPI transfer */
+ memset(&spi_xfer, 0, sizeof spi_xfer);
+ spi_xfer.len = len;
+ spi_xfer.tx_buf = tx;
+ spi_xfer.rx_buf = rx;
+ spi_xfer.speed_hz = MAX3107_SPI_SPEED;
+
+ /* Add SPI transfer to SPI message */
+ spi_message_add_tail(&spi_xfer, &spi_msg);
+
+#ifdef DBG_TRACE_SPI_DATA
+ {
+ int i;
+ pr_info("tx len %d:\n", spi_xfer.len);
+ for (i = 0 ; i < spi_xfer.len && i < 32 ; i++)
+ pr_info(" %x", ((u8 *)spi_xfer.tx_buf)[i]);
+ pr_info("\n");
+ }
+#endif
+
+ /* Perform synchronous SPI transfer */
+ if (spi_sync(s->spi, &spi_msg)) {
+ dev_err(&s->spi->dev, "spi_sync failure\n");
+ return -EIO;
+ }
+
+#ifdef DBG_TRACE_SPI_DATA
+ if (spi_xfer.rx_buf) {
+ int i;
+ pr_info("rx len %d:\n", spi_xfer.len);
+ for (i = 0 ; i < spi_xfer.len && i < 32 ; i++)
+ pr_info(" %x", ((u8 *)spi_xfer.rx_buf)[i]);
+ pr_info("\n");
+ }
+#endif
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max3107_rw);
+
+/* Puts received data to circular buffer */
+static void put_data_to_circ_buf(struct max3107_port *s, unsigned char *data,
+ int len)
+{
+ struct uart_port *port = &s->port;
+ struct tty_struct *tty;
+
+ if (!port->state)
+ return;
+
+ tty = port->state->port.tty;
+ if (!tty)
+ return;
+
+ /* Insert received data */
+ tty_insert_flip_string(tty, data, len);
+ /* Update RX counter */
+ port->icount.rx += len;
+}
+
+/* Handle data receiving */
+static void max3107_handlerx(struct max3107_port *s, u16 rxlvl)
+{
+ int i;
+ int j;
+ int len; /* SPI transfer buffer length */
+ u16 *buf;
+ u8 *valid_str;
+
+ if (!s->rx_enabled)
+ /* RX is disabled */
+ return;
+
+ if (rxlvl == 0) {
+ /* RX fifo is empty */
+ return;
+ } else if (rxlvl >= MAX3107_RX_FIFO_SIZE) {
+ dev_warn(&s->spi->dev, "Possible RX FIFO overrun %d\n", rxlvl);
+ /* Ensure sanity of RX level */
+ rxlvl = MAX3107_RX_FIFO_SIZE;
+ }
+ if ((s->rxbuf == 0) || (s->rxstr == 0)) {
+ dev_warn(&s->spi->dev, "Rx buffer/str isn't ready\n");
+ return;
+ }
+ buf = s->rxbuf;
+ valid_str = s->rxstr;
+ while (rxlvl) {
+ pr_debug("rxlvl %d\n", rxlvl);
+ /* Clear buffer */
+ memset(buf, 0, sizeof(u16) * (MAX3107_RX_FIFO_SIZE + 2));
+ len = 0;
+ if (s->irqen_reg & MAX3107_IRQ_RXFIFO_BIT) {
+ /* First disable RX FIFO interrupt */
+ pr_debug("Disabling RX INT\n");
+ buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG);
+ s->irqen_reg &= ~MAX3107_IRQ_RXFIFO_BIT;
+ buf[0] |= s->irqen_reg;
+ len++;
+ }
+ /* Just increase the length by amount of words in FIFO since
+ * buffer was zeroed and SPI transfer of 0x0000 means reading
+ * from RX FIFO
+ */
+ len += rxlvl;
+ /* Append RX level query */
+ buf[len] = MAX3107_RXFIFOLVL_REG;
+ len++;
+
+ /* Perform the SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, (u8 *)buf, len * 2)) {
+ dev_err(&s->spi->dev, "SPI transfer for RX h failed\n");
+ return;
+ }
+
+ /* Skip RX FIFO interrupt disabling word if it was added */
+ j = ((len - 1) - rxlvl);
+ /* Read received words */
+ for (i = 0; i < rxlvl; i++, j++)
+ valid_str[i] = (u8)buf[j];
+ put_data_to_circ_buf(s, valid_str, rxlvl);
+ /* Get new RX level */
+ rxlvl = (buf[len - 1] & MAX3107_SPI_RX_DATA_MASK);
+ }
+
+ if (s->rx_enabled) {
+ /* RX still enabled, re-enable RX FIFO interrupt */
+ pr_debug("Enabling RX INT\n");
+ buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG);
+ s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT;
+ buf[0] |= s->irqen_reg;
+ if (max3107_rw(s, (u8 *)buf, NULL, 2))
+ dev_err(&s->spi->dev, "RX FIFO INT enabling failed\n");
+ }
+
+ /* Push the received data to receivers */
+ if (s->port.state->port.tty)
+ tty_flip_buffer_push(s->port.state->port.tty);
+}
+
+
+/* Handle data sending */
+static void max3107_handletx(struct max3107_port *s)
+{
+ struct circ_buf *xmit = &s->port.state->xmit;
+ int i;
+ unsigned long flags;
+ int len; /* SPI transfer buffer length */
+ u16 *buf;
+
+ if (!s->tx_fifo_empty)
+ /* Don't send more data before previous data is sent */
+ return;
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port))
+ /* No data to send or TX is stopped */
+ return;
+
+ if (!s->txbuf) {
+ dev_warn(&s->spi->dev, "Txbuf isn't ready\n");
+ return;
+ }
+ buf = s->txbuf;
+ /* Get length of data pending in circular buffer */
+ len = uart_circ_chars_pending(xmit);
+ if (len) {
+ /* Limit to size of TX FIFO */
+ if (len > MAX3107_TX_FIFO_SIZE)
+ len = MAX3107_TX_FIFO_SIZE;
+
+ pr_debug("txlen %d\n", len);
+
+ /* Update TX counter */
+ s->port.icount.tx += len;
+
+ /* TX FIFO will no longer be empty */
+ s->tx_fifo_empty = 0;
+
+ i = 0;
+ if (s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT) {
+ /* First disable TX empty interrupt */
+ pr_debug("Disabling TE INT\n");
+ buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG);
+ s->irqen_reg &= ~MAX3107_IRQ_TXEMPTY_BIT;
+ buf[i] |= s->irqen_reg;
+ i++;
+ len++;
+ }
+ /* Add data to send */
+ spin_lock_irqsave(&s->port.lock, flags);
+ for ( ; i < len ; i++) {
+ buf[i] = (MAX3107_WRITE_BIT | MAX3107_THR_REG);
+ buf[i] |= ((u16)xmit->buf[xmit->tail] &
+ MAX3107_SPI_TX_DATA_MASK);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ }
+ spin_unlock_irqrestore(&s->port.lock, flags);
+ if (!(s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT)) {
+ /* Enable TX empty interrupt */
+ pr_debug("Enabling TE INT\n");
+ buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG);
+ s->irqen_reg |= MAX3107_IRQ_TXEMPTY_BIT;
+ buf[i] |= s->irqen_reg;
+ i++;
+ len++;
+ }
+ if (!s->tx_enabled) {
+ /* Enable TX */
+ pr_debug("Enable TX\n");
+ buf[i] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG);
+ spin_lock_irqsave(&s->data_lock, flags);
+ s->mode1_reg &= ~MAX3107_MODE1_TXDIS_BIT;
+ buf[i] |= s->mode1_reg;
+ spin_unlock_irqrestore(&s->data_lock, flags);
+ s->tx_enabled = 1;
+ i++;
+ len++;
+ }
+
+ /* Perform the SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, NULL, len*2)) {
+ dev_err(&s->spi->dev,
+ "SPI transfer TX handling failed\n");
+ return;
+ }
+ }
+
+ /* Indicate wake up if circular buffer is getting low on data */
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&s->port);
+
+}
+
+/* Handle interrupts
+ * Also reads and returns current RX FIFO level
+ */
+static u16 handle_interrupt(struct max3107_port *s)
+{
+ u16 buf[4]; /* Buffer for SPI transfers */
+ u8 irq_status;
+ u16 rx_level;
+ unsigned long flags;
+
+ /* Read IRQ status register */
+ buf[0] = MAX3107_IRQSTS_REG;
+ /* Read status IRQ status register */
+ buf[1] = MAX3107_STS_IRQSTS_REG;
+ /* Read LSR IRQ status register */
+ buf[2] = MAX3107_LSR_IRQSTS_REG;
+ /* Query RX level */
+ buf[3] = MAX3107_RXFIFOLVL_REG;
+
+ if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 8)) {
+ dev_err(&s->spi->dev,
+ "SPI transfer for INTR handling failed\n");
+ return 0;
+ }
+
+ irq_status = (u8)buf[0];
+ pr_debug("IRQSTS %x\n", irq_status);
+ rx_level = (buf[3] & MAX3107_SPI_RX_DATA_MASK);
+
+ if (irq_status & MAX3107_IRQ_LSR_BIT) {
+ /* LSR interrupt */
+ if (buf[2] & MAX3107_LSR_RXTO_BIT)
+ /* RX timeout interrupt,
+ * handled by normal RX handling
+ */
+ pr_debug("RX TO INT\n");
+ }
+
+ if (irq_status & MAX3107_IRQ_TXEMPTY_BIT) {
+ /* Tx empty interrupt,
+ * disable TX and set tx_fifo_empty flag
+ */
+ pr_debug("TE INT, disabling TX\n");
+ buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG);
+ spin_lock_irqsave(&s->data_lock, flags);
+ s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT;
+ buf[0] |= s->mode1_reg;
+ spin_unlock_irqrestore(&s->data_lock, flags);
+ if (max3107_rw(s, (u8 *)buf, NULL, 2))
+ dev_err(&s->spi->dev, "SPI transfer TX dis failed\n");
+ s->tx_enabled = 0;
+ s->tx_fifo_empty = 1;
+ }
+
+ if (irq_status & MAX3107_IRQ_RXFIFO_BIT)
+ /* RX FIFO interrupt,
+ * handled by normal RX handling
+ */
+ pr_debug("RFIFO INT\n");
+
+ /* Return RX level */
+ return rx_level;
+}
+
+/* Trigger work thread*/
+static void max3107_dowork(struct max3107_port *s)
+{
+ if (!work_pending(&s->work) && !freezing(current) && !s->suspended)
+ queue_work(s->workqueue, &s->work);
+ else
+ dev_warn(&s->spi->dev, "interrup isn't serviced normally!\n");
+}
+
+/* Work thread */
+static void max3107_work(struct work_struct *w)
+{
+ struct max3107_port *s = container_of(w, struct max3107_port, work);
+ u16 rxlvl = 0;
+ int len; /* SPI transfer buffer length */
+ u16 buf[5]; /* Buffer for SPI transfers */
+ unsigned long flags;
+
+ /* Start by reading current RX FIFO level */
+ buf[0] = MAX3107_RXFIFOLVL_REG;
+ if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) {
+ dev_err(&s->spi->dev, "SPI transfer RX lev failed\n");
+ rxlvl = 0;
+ } else {
+ rxlvl = (buf[0] & MAX3107_SPI_RX_DATA_MASK);
+ }
+
+ do {
+ pr_debug("rxlvl %d\n", rxlvl);
+
+ /* Handle RX */
+ max3107_handlerx(s, rxlvl);
+ rxlvl = 0;
+
+ if (s->handle_irq) {
+ /* Handle pending interrupts
+ * We also get new RX FIFO level since new data may
+ * have been received while pushing received data to
+ * receivers
+ */
+ s->handle_irq = 0;
+ rxlvl = handle_interrupt(s);
+ }
+
+ /* Handle TX */
+ max3107_handletx(s);
+
+ /* Handle configuration changes */
+ len = 0;
+ spin_lock_irqsave(&s->data_lock, flags);
+ if (s->mode1_commit) {
+ pr_debug("mode1_commit\n");
+ buf[len] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG);
+ buf[len++] |= s->mode1_reg;
+ s->mode1_commit = 0;
+ }
+ if (s->lcr_commit) {
+ pr_debug("lcr_commit\n");
+ buf[len] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG);
+ buf[len++] |= s->lcr_reg;
+ s->lcr_commit = 0;
+ }
+ if (s->brg_commit) {
+ pr_debug("brg_commit\n");
+ buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG);
+ buf[len++] |= ((s->brg_cfg >> 16) &
+ MAX3107_SPI_TX_DATA_MASK);
+ buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG);
+ buf[len++] |= ((s->brg_cfg >> 8) &
+ MAX3107_SPI_TX_DATA_MASK);
+ buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG);
+ buf[len++] |= ((s->brg_cfg) & 0xff);
+ s->brg_commit = 0;
+ }
+ spin_unlock_irqrestore(&s->data_lock, flags);
+
+ if (len > 0) {
+ if (max3107_rw(s, (u8 *)buf, NULL, len * 2))
+ dev_err(&s->spi->dev,
+ "SPI transfer config failed\n");
+ }
+
+ /* Reloop if interrupt handling indicated data in RX FIFO */
+ } while (rxlvl);
+
+}
+
+/* Set sleep mode */
+static void max3107_set_sleep(struct max3107_port *s, int mode)
+{
+ u16 buf[1]; /* Buffer for SPI transfer */
+ unsigned long flags;
+ pr_debug("enter, mode %d\n", mode);
+
+ buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG);
+ spin_lock_irqsave(&s->data_lock, flags);
+ switch (mode) {
+ case MAX3107_DISABLE_FORCED_SLEEP:
+ s->mode1_reg &= ~MAX3107_MODE1_FORCESLEEP_BIT;
+ break;
+ case MAX3107_ENABLE_FORCED_SLEEP:
+ s->mode1_reg |= MAX3107_MODE1_FORCESLEEP_BIT;
+ break;
+ case MAX3107_DISABLE_AUTOSLEEP:
+ s->mode1_reg &= ~MAX3107_MODE1_AUTOSLEEP_BIT;
+ break;
+ case MAX3107_ENABLE_AUTOSLEEP:
+ s->mode1_reg |= MAX3107_MODE1_AUTOSLEEP_BIT;
+ break;
+ default:
+ spin_unlock_irqrestore(&s->data_lock, flags);
+ dev_warn(&s->spi->dev, "invalid sleep mode\n");
+ return;
+ }
+ buf[0] |= s->mode1_reg;
+ spin_unlock_irqrestore(&s->data_lock, flags);
+
+ if (max3107_rw(s, (u8 *)buf, NULL, 2))
+ dev_err(&s->spi->dev, "SPI transfer sleep mode failed\n");
+
+ if (mode == MAX3107_DISABLE_AUTOSLEEP ||
+ mode == MAX3107_DISABLE_FORCED_SLEEP)
+ msleep(MAX3107_WAKEUP_DELAY);
+}
+
+/* Perform full register initialization */
+static void max3107_register_init(struct max3107_port *s)
+{
+ u16 buf[11]; /* Buffer for SPI transfers */
+
+ /* 1. Configure baud rate, 9600 as default */
+ s->baud = 9600;
+ /* the below is default*/
+ if (s->ext_clk) {
+ s->brg_cfg = MAX3107_BRG26_B9600;
+ s->baud_tbl = (struct baud_table *)brg26_ext;
+ } else {
+ s->brg_cfg = MAX3107_BRG13_IB9600;
+ s->baud_tbl = (struct baud_table *)brg13_int;
+ }
+
+ if (s->pdata->init)
+ s->pdata->init(s);
+
+ buf[0] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG)
+ | ((s->brg_cfg >> 16) & MAX3107_SPI_TX_DATA_MASK);
+ buf[1] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG)
+ | ((s->brg_cfg >> 8) & MAX3107_SPI_TX_DATA_MASK);
+ buf[2] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG)
+ | ((s->brg_cfg) & 0xff);
+
+ /* 2. Configure LCR register, 8N1 mode by default */
+ s->lcr_reg = MAX3107_LCR_WORD_LEN_8;
+ buf[3] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG)
+ | s->lcr_reg;
+
+ /* 3. Configure MODE 1 register */
+ s->mode1_reg = 0;
+ /* Enable IRQ pin */
+ s->mode1_reg |= MAX3107_MODE1_IRQSEL_BIT;
+ /* Disable TX */
+ s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT;
+ s->tx_enabled = 0;
+ /* RX is enabled */
+ s->rx_enabled = 1;
+ buf[4] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG)
+ | s->mode1_reg;
+
+ /* 4. Configure MODE 2 register */
+ buf[5] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG);
+ if (s->loopback) {
+ /* Enable loopback */
+ buf[5] |= MAX3107_MODE2_LOOPBACK_BIT;
+ }
+ /* Reset FIFOs */
+ buf[5] |= MAX3107_MODE2_FIFORST_BIT;
+ s->tx_fifo_empty = 1;
+
+ /* 5. Configure FIFO trigger level register */
+ buf[6] = (MAX3107_WRITE_BIT | MAX3107_FIFOTRIGLVL_REG);
+ /* RX FIFO trigger for 16 words, TX FIFO trigger not used */
+ buf[6] |= (MAX3107_FIFOTRIGLVL_RX(16) | MAX3107_FIFOTRIGLVL_TX(0));
+
+ /* 6. Configure flow control levels */
+ buf[7] = (MAX3107_WRITE_BIT | MAX3107_FLOWLVL_REG);
+ /* Flow control halt level 96, resume level 48 */
+ buf[7] |= (MAX3107_FLOWLVL_RES(48) | MAX3107_FLOWLVL_HALT(96));
+
+ /* 7. Configure flow control */
+ buf[8] = (MAX3107_WRITE_BIT | MAX3107_FLOWCTRL_REG);
+ /* Enable auto CTS and auto RTS flow control */
+ buf[8] |= (MAX3107_FLOWCTRL_AUTOCTS_BIT | MAX3107_FLOWCTRL_AUTORTS_BIT);
+
+ /* 8. Configure RX timeout register */
+ buf[9] = (MAX3107_WRITE_BIT | MAX3107_RXTO_REG);
+ /* Timeout after 48 character intervals */
+ buf[9] |= 0x0030;
+
+ /* 9. Configure LSR interrupt enable register */
+ buf[10] = (MAX3107_WRITE_BIT | MAX3107_LSR_IRQEN_REG);
+ /* Enable RX timeout interrupt */
+ buf[10] |= MAX3107_LSR_RXTO_BIT;
+
+ /* Perform SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, NULL, 22))
+ dev_err(&s->spi->dev, "SPI transfer for init failed\n");
+
+ /* 10. Clear IRQ status register by reading it */
+ buf[0] = MAX3107_IRQSTS_REG;
+
+ /* 11. Configure interrupt enable register */
+ /* Enable LSR interrupt */
+ s->irqen_reg = MAX3107_IRQ_LSR_BIT;
+ /* Enable RX FIFO interrupt */
+ s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT;
+ buf[1] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG)
+ | s->irqen_reg;
+
+ /* 12. Clear FIFO reset that was set in step 6 */
+ buf[2] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG);
+ if (s->loopback) {
+ /* Keep loopback enabled */
+ buf[2] |= MAX3107_MODE2_LOOPBACK_BIT;
+ }
+
+ /* Perform SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 6))
+ dev_err(&s->spi->dev, "SPI transfer for init failed\n");
+
+}
+
+/* IRQ handler */
+static irqreturn_t max3107_irq(int irqno, void *dev_id)
+{
+ struct max3107_port *s = dev_id;
+
+ if (irqno != s->spi->irq) {
+ /* Unexpected IRQ */
+ return IRQ_NONE;
+ }
+
+ /* Indicate irq */
+ s->handle_irq = 1;
+
+ /* Trigger work thread */
+ max3107_dowork(s);
+
+ return IRQ_HANDLED;
+}
+
+/* HW suspension function
+ *
+ * Currently autosleep is used to decrease current consumption, alternative
+ * approach would be to set the chip to reset mode if UART is not being
+ * used but that would mess the GPIOs
+ *
+ */
+void max3107_hw_susp(struct max3107_port *s, int suspend)
+{
+ pr_debug("enter, suspend %d\n", suspend);
+
+ if (suspend) {
+ /* Suspend requested,
+ * enable autosleep to decrease current consumption
+ */
+ s->suspended = 1;
+ max3107_set_sleep(s, MAX3107_ENABLE_AUTOSLEEP);
+ } else {
+ /* Resume requested,
+ * disable autosleep
+ */
+ s->suspended = 0;
+ max3107_set_sleep(s, MAX3107_DISABLE_AUTOSLEEP);
+ }
+}
+EXPORT_SYMBOL_GPL(max3107_hw_susp);
+
+/* Modem status IRQ enabling */
+static void max3107_enable_ms(struct uart_port *port)
+{
+ /* Modem status not supported */
+}
+
+/* Data send function */
+static void max3107_start_tx(struct uart_port *port)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+ /* Trigger work thread for sending data */
+ max3107_dowork(s);
+}
+
+/* Function for checking that there is no pending transfers */
+static unsigned int max3107_tx_empty(struct uart_port *port)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+ pr_debug("returning %d\n",
+ (s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit)));
+ return s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit);
+}
+
+/* Function for stopping RX */
+static void max3107_stop_rx(struct uart_port *port)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+ unsigned long flags;
+
+ /* Set RX disabled in MODE 1 register */
+ spin_lock_irqsave(&s->data_lock, flags);
+ s->mode1_reg |= MAX3107_MODE1_RXDIS_BIT;
+ s->mode1_commit = 1;
+ spin_unlock_irqrestore(&s->data_lock, flags);
+ /* Set RX disabled */
+ s->rx_enabled = 0;
+ /* Trigger work thread for doing the actual configuration change */
+ max3107_dowork(s);
+}
+
+/* Function for returning control pin states */
+static unsigned int max3107_get_mctrl(struct uart_port *port)
+{
+ /* DCD and DSR are not wired and CTS/RTS is handled automatically
+ * so just indicate DSR and CAR asserted
+ */
+ return TIOCM_DSR | TIOCM_CAR;
+}
+
+/* Function for setting control pin states */
+static void max3107_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ /* DCD and DSR are not wired and CTS/RTS is hadnled automatically
+ * so do nothing
+ */
+}
+
+/* Function for configuring UART parameters */
+static void max3107_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+ struct tty_struct *tty;
+ int baud;
+ u16 new_lcr = 0;
+ u32 new_brg = 0;
+ unsigned long flags;
+
+ if (!port->state)
+ return;
+
+ tty = port->state->port.tty;
+ if (!tty)
+ return;
+
+ /* Get new LCR register values */
+ /* Word size */
+ if ((termios->c_cflag & CSIZE) == CS7)
+ new_lcr |= MAX3107_LCR_WORD_LEN_7;
+ else
+ new_lcr |= MAX3107_LCR_WORD_LEN_8;
+
+ /* Parity */
+ if (termios->c_cflag & PARENB) {
+ new_lcr |= MAX3107_LCR_PARITY_BIT;
+ if (!(termios->c_cflag & PARODD))
+ new_lcr |= MAX3107_LCR_EVENPARITY_BIT;
+ }
+
+ /* Stop bits */
+ if (termios->c_cflag & CSTOPB) {
+ /* 2 stop bits */
+ new_lcr |= MAX3107_LCR_STOPLEN_BIT;
+ }
+
+ /* Mask termios capabilities we don't support */
+ termios->c_cflag &= ~CMSPAR;
+
+ /* Set status ignore mask */
+ s->port.ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ s->port.ignore_status_mask |= MAX3107_ALL_ERRORS;
+
+ /* Set low latency to immediately handle pushed data */
+ s->port.state->port.tty->low_latency = 1;
+
+ /* Get new baud rate generator configuration */
+ baud = tty_get_baud_rate(tty);
+
+ spin_lock_irqsave(&s->data_lock, flags);
+ new_brg = get_new_brg(baud, s);
+ /* if can't find the corrent config, use previous */
+ if (!new_brg) {
+ baud = s->baud;
+ new_brg = s->brg_cfg;
+ }
+ spin_unlock_irqrestore(&s->data_lock, flags);
+ tty_termios_encode_baud_rate(termios, baud, baud);
+ s->baud = baud;
+
+ /* Update timeout according to new baud rate */
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ spin_lock_irqsave(&s->data_lock, flags);
+ if (s->lcr_reg != new_lcr) {
+ s->lcr_reg = new_lcr;
+ s->lcr_commit = 1;
+ }
+ if (s->brg_cfg != new_brg) {
+ s->brg_cfg = new_brg;
+ s->brg_commit = 1;
+ }
+ spin_unlock_irqrestore(&s->data_lock, flags);
+
+ /* Trigger work thread for doing the actual configuration change */
+ max3107_dowork(s);
+}
+
+/* Port shutdown function */
+static void max3107_shutdown(struct uart_port *port)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+ if (s->suspended && s->pdata->hw_suspend)
+ s->pdata->hw_suspend(s, 0);
+
+ /* Free the interrupt */
+ free_irq(s->spi->irq, s);
+
+ if (s->workqueue) {
+ /* Flush and destroy work queue */
+ flush_workqueue(s->workqueue);
+ destroy_workqueue(s->workqueue);
+ s->workqueue = NULL;
+ }
+
+ /* Suspend HW */
+ if (s->pdata->hw_suspend)
+ s->pdata->hw_suspend(s, 1);
+}
+
+/* Port startup function */
+static int max3107_startup(struct uart_port *port)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+ /* Initialize work queue */
+ s->workqueue = create_freezeable_workqueue("max3107");
+ if (!s->workqueue) {
+ dev_err(&s->spi->dev, "Workqueue creation failed\n");
+ return -EBUSY;
+ }
+ INIT_WORK(&s->work, max3107_work);
+
+ /* Setup IRQ */
+ if (request_irq(s->spi->irq, max3107_irq, IRQF_TRIGGER_FALLING,
+ "max3107", s)) {
+ dev_err(&s->spi->dev, "IRQ reguest failed\n");
+ destroy_workqueue(s->workqueue);
+ s->workqueue = NULL;
+ return -EBUSY;
+ }
+
+ /* Resume HW */
+ if (s->pdata->hw_suspend)
+ s->pdata->hw_suspend(s, 0);
+
+ /* Init registers */
+ max3107_register_init(s);
+
+ return 0;
+}
+
+/* Port type function */
+static const char *max3107_type(struct uart_port *port)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+ return s->spi->modalias;
+}
+
+/* Port release function */
+static void max3107_release_port(struct uart_port *port)
+{
+ /* Do nothing */
+}
+
+/* Port request function */
+static int max3107_request_port(struct uart_port *port)
+{
+ /* Do nothing */
+ return 0;
+}
+
+/* Port config function */
+static void max3107_config_port(struct uart_port *port, int flags)
+{
+ struct max3107_port *s = container_of(port, struct max3107_port, port);
+ s->port.type = PORT_MAX3107;
+}
+
+/* Port verify function */
+static int max3107_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
+{
+ if (ser->type == PORT_UNKNOWN || ser->type == PORT_MAX3107)
+ return 0;
+
+ return -EINVAL;
+}
+
+/* Port stop TX function */
+static void max3107_stop_tx(struct uart_port *port)
+{
+ /* Do nothing */
+}
+
+/* Port break control function */
+static void max3107_break_ctl(struct uart_port *port, int break_state)
+{
+ /* We don't support break control, do nothing */
+}
+
+
+/* Port functions */
+static struct uart_ops max3107_ops = {
+ .tx_empty = max3107_tx_empty,
+ .set_mctrl = max3107_set_mctrl,
+ .get_mctrl = max3107_get_mctrl,
+ .stop_tx = max3107_stop_tx,
+ .start_tx = max3107_start_tx,
+ .stop_rx = max3107_stop_rx,
+ .enable_ms = max3107_enable_ms,
+ .break_ctl = max3107_break_ctl,
+ .startup = max3107_startup,
+ .shutdown = max3107_shutdown,
+ .set_termios = max3107_set_termios,
+ .type = max3107_type,
+ .release_port = max3107_release_port,
+ .request_port = max3107_request_port,
+ .config_port = max3107_config_port,
+ .verify_port = max3107_verify_port,
+};
+
+/* UART driver data */
+static struct uart_driver max3107_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = "ttyMAX",
+ .dev_name = "ttyMAX",
+ .nr = 1,
+};
+
+static int driver_registered = 0;
+
+
+
+/* 'Generic' platform data */
+static struct max3107_plat generic_plat_data = {
+ .loopback = 0,
+ .ext_clk = 1,
+ .hw_suspend = max3107_hw_susp,
+ .polled_mode = 0,
+ .poll_time = 0,
+};
+
+
+/*******************************************************************/
+
+/**
+ * max3107_probe - SPI bus probe entry point
+ * @spi: the spi device
+ *
+ * SPI wants us to probe this device and if appropriate claim it.
+ * Perform any platform specific requirements and then initialise
+ * the device.
+ */
+
+int max3107_probe(struct spi_device *spi, struct max3107_plat *pdata)
+{
+ struct max3107_port *s;
+ u16 buf[2]; /* Buffer for SPI transfers */
+ int retval;
+
+ pr_info("enter max3107 probe\n");
+
+ /* Allocate port structure */
+ s = kzalloc(sizeof(*s), GFP_KERNEL);
+ if (!s) {
+ pr_err("Allocating port structure failed\n");
+ return -ENOMEM;
+ }
+
+ s->pdata = pdata;
+
+ /* SPI Rx buffer
+ * +2 for RX FIFO interrupt
+ * disabling and RX level query
+ */
+ s->rxbuf = kzalloc(sizeof(u16) * (MAX3107_RX_FIFO_SIZE+2), GFP_KERNEL);
+ if (!s->rxbuf) {
+ pr_err("Allocating RX buffer failed\n");
+ return -ENOMEM;
+ }
+ s->rxstr = kzalloc(sizeof(u8) * MAX3107_RX_FIFO_SIZE, GFP_KERNEL);
+ if (!s->rxstr) {
+ pr_err("Allocating RX buffer failed\n");
+ return -ENOMEM;
+ }
+ /* SPI Tx buffer
+ * SPI transfer buffer
+ * +3 for TX FIFO empty
+ * interrupt disabling and
+ * enabling and TX enabling
+ */
+ s->txbuf = kzalloc(sizeof(u16) * MAX3107_TX_FIFO_SIZE + 3, GFP_KERNEL);
+ if (!s->txbuf) {
+ pr_err("Allocating TX buffer failed\n");
+ return -ENOMEM;
+ }
+ /* Initialize shared data lock */
+ spin_lock_init(&s->data_lock);
+
+ /* SPI intializations */
+ dev_set_drvdata(&spi->dev, s);
+ spi->mode = SPI_MODE_0;
+ spi->dev.platform_data = pdata;
+ spi->bits_per_word = 16;
+ s->ext_clk = pdata->ext_clk;
+ s->loopback = pdata->loopback;
+ spi_setup(spi);
+ s->spi = spi;
+
+ /* Check REV ID to ensure we are talking to what we expect */
+ buf[0] = MAX3107_REVID_REG;
+ if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) {
+ dev_err(&s->spi->dev, "SPI transfer for REVID read failed\n");
+ return -EIO;
+ }
+ if ((buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID1 &&
+ (buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID2) {
+ dev_err(&s->spi->dev, "REVID %x does not match\n",
+ (buf[0] & MAX3107_SPI_RX_DATA_MASK));
+ return -ENODEV;
+ }
+
+ /* Disable all interrupts */
+ buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG | 0x0000);
+ buf[0] |= 0x0000;
+
+ /* Configure clock source */
+ buf[1] = (MAX3107_WRITE_BIT | MAX3107_CLKSRC_REG);
+ if (s->ext_clk) {
+ /* External clock */
+ buf[1] |= MAX3107_CLKSRC_EXTCLK_BIT;
+ }
+
+ /* PLL bypass ON */
+ buf[1] |= MAX3107_CLKSRC_PLLBYP_BIT;
+
+ /* Perform SPI transfer */
+ if (max3107_rw(s, (u8 *)buf, NULL, 4)) {
+ dev_err(&s->spi->dev, "SPI transfer for init failed\n");
+ return -EIO;
+ }
+
+ /* Register UART driver */
+ if (!driver_registered) {
+ retval = uart_register_driver(&max3107_uart_driver);
+ if (retval) {
+ dev_err(&s->spi->dev, "Registering UART driver failed\n");
+ return retval;
+ }
+ driver_registered = 1;
+ }
+
+ /* Initialize UART port data */
+ s->port.fifosize = 128;
+ s->port.ops = &max3107_ops;
+ s->port.line = 0;
+ s->port.dev = &spi->dev;
+ s->port.uartclk = 9600;
+ s->port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
+ s->port.irq = s->spi->irq;
+ s->port.type = PORT_MAX3107;
+
+ /* Add UART port */
+ retval = uart_add_one_port(&max3107_uart_driver, &s->port);
+ if (retval < 0) {
+ dev_err(&s->spi->dev, "Adding UART port failed\n");
+ return retval;
+ }
+
+ if (pdata->configure) {
+ retval = pdata->configure(s);
+ if (retval < 0)
+ return retval;
+ }
+
+ /* Go to suspend mode */
+ if (pdata->hw_suspend)
+ pdata->hw_suspend(s, 1);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max3107_probe);
+
+/* Driver remove function */
+int max3107_remove(struct spi_device *spi)
+{
+ struct max3107_port *s = dev_get_drvdata(&spi->dev);
+
+ pr_info("enter max3107 remove\n");
+
+ /* Remove port */
+ if (uart_remove_one_port(&max3107_uart_driver, &s->port))
+ dev_warn(&s->spi->dev, "Removing UART port failed\n");
+
+
+ /* Free TxRx buffer */
+ kfree(s->rxbuf);
+ kfree(s->rxstr);
+ kfree(s->txbuf);
+
+ /* Free port structure */
+ kfree(s);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max3107_remove);
+
+/* Driver suspend function */
+int max3107_suspend(struct spi_device *spi, pm_message_t state)
+{
+#ifdef CONFIG_PM
+ struct max3107_port *s = dev_get_drvdata(&spi->dev);
+
+ pr_debug("enter suspend\n");
+
+ /* Suspend UART port */
+ uart_suspend_port(&max3107_uart_driver, &s->port);
+
+ /* Go to suspend mode */
+ if (s->pdata->hw_suspend)
+ s->pdata->hw_suspend(s, 1);
+#endif /* CONFIG_PM */
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max3107_suspend);
+
+/* Driver resume function */
+int max3107_resume(struct spi_device *spi)
+{
+#ifdef CONFIG_PM
+ struct max3107_port *s = dev_get_drvdata(&spi->dev);
+
+ pr_debug("enter resume\n");
+
+ /* Resume from suspend */
+ if (s->pdata->hw_suspend)
+ s->pdata->hw_suspend(s, 0);
+
+ /* Resume UART port */
+ uart_resume_port(&max3107_uart_driver, &s->port);
+#endif /* CONFIG_PM */
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max3107_resume);
+
+static int max3107_probe_generic(struct spi_device *spi)
+{
+ return max3107_probe(spi, &generic_plat_data);
+}
+
+/* Spi driver data */
+static struct spi_driver max3107_driver = {
+ .driver = {
+ .name = "max3107",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = max3107_probe_generic,
+ .remove = __devexit_p(max3107_remove),
+ .suspend = max3107_suspend,
+ .resume = max3107_resume,
+};
+
+/* Driver init function */
+static int __init max3107_init(void)
+{
+ pr_info("enter max3107 init\n");
+ return spi_register_driver(&max3107_driver);
+}
+
+/* Driver exit function */
+static void __exit max3107_exit(void)
+{
+ pr_info("enter max3107 exit\n");
+ /* Unregister UART driver */
+ if (driver_registered)
+ uart_unregister_driver(&max3107_uart_driver);
+ spi_unregister_driver(&max3107_driver);
+}
+
+module_init(max3107_init);
+module_exit(max3107_exit);
+
+MODULE_DESCRIPTION("MAX3107 driver");
+MODULE_AUTHOR("Aavamobile");
+MODULE_ALIAS("max3107-spi");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * max3107.h - spi uart protocol driver header for Maxim 3107
+ *
+ * Copyright (C) Aavamobile 2009
+ * Based on serial_max3100.h by Christian Pellegrin
+ *
+ * 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.
+ */
+
+#ifndef _MAX3107_H
+#define _MAX3107_H
+
+/* Serial error status definitions */
+#define MAX3107_PARITY_ERROR 1
+#define MAX3107_FRAME_ERROR 2
+#define MAX3107_OVERRUN_ERROR 4
+#define MAX3107_ALL_ERRORS (MAX3107_PARITY_ERROR | \
+ MAX3107_FRAME_ERROR | \
+ MAX3107_OVERRUN_ERROR)
+
+/* GPIO definitions */
+#define MAX3107_GPIO_BASE 88
+#define MAX3107_GPIO_COUNT 4
+
+
+/* GPIO connected to chip's reset pin */
+#define MAX3107_RESET_GPIO 87
+
+
+/* Chip reset delay */
+#define MAX3107_RESET_DELAY 10
+
+/* Chip wakeup delay */
+#define MAX3107_WAKEUP_DELAY 50
+
+
+/* Sleep mode definitions */
+#define MAX3107_DISABLE_FORCED_SLEEP 0
+#define MAX3107_ENABLE_FORCED_SLEEP 1
+#define MAX3107_DISABLE_AUTOSLEEP 2
+#define MAX3107_ENABLE_AUTOSLEEP 3
+
+
+/* Definitions for register access with SPI transfers
+ *
+ * SPI transfer format:
+ *
+ * Master to slave bits xzzzzzzzyyyyyyyy
+ * Slave to master bits aaaaaaaabbbbbbbb
+ *
+ * where:
+ * x = 0 for reads, 1 for writes
+ * z = register address
+ * y = new register value if write, 0 if read
+ * a = unspecified
+ * b = register value if read, unspecified if write
+ */
+
+/* SPI speed */
+#define MAX3107_SPI_SPEED (3125000 * 2)
+
+/* Write bit */
+#define MAX3107_WRITE_BIT (1 << 15)
+
+/* SPI TX data mask */
+#define MAX3107_SPI_RX_DATA_MASK (0x00ff)
+
+/* SPI RX data mask */
+#define MAX3107_SPI_TX_DATA_MASK (0x00ff)
+
+/* Register access masks */
+#define MAX3107_RHR_REG (0x0000) /* RX FIFO */
+#define MAX3107_THR_REG (0x0000) /* TX FIFO */
+#define MAX3107_IRQEN_REG (0x0100) /* IRQ enable */
+#define MAX3107_IRQSTS_REG (0x0200) /* IRQ status */
+#define MAX3107_LSR_IRQEN_REG (0x0300) /* LSR IRQ enable */
+#define MAX3107_LSR_IRQSTS_REG (0x0400) /* LSR IRQ status */
+#define MAX3107_SPCHR_IRQEN_REG (0x0500) /* Special char IRQ enable */
+#define MAX3107_SPCHR_IRQSTS_REG (0x0600) /* Special char IRQ status */
+#define MAX3107_STS_IRQEN_REG (0x0700) /* Status IRQ enable */
+#define MAX3107_STS_IRQSTS_REG (0x0800) /* Status IRQ status */
+#define MAX3107_MODE1_REG (0x0900) /* MODE1 */
+#define MAX3107_MODE2_REG (0x0a00) /* MODE2 */
+#define MAX3107_LCR_REG (0x0b00) /* LCR */
+#define MAX3107_RXTO_REG (0x0c00) /* RX timeout */
+#define MAX3107_HDPIXDELAY_REG (0x0d00) /* Auto transceiver delays */
+#define MAX3107_IRDA_REG (0x0e00) /* IRDA settings */
+#define MAX3107_FLOWLVL_REG (0x0f00) /* Flow control levels */
+#define MAX3107_FIFOTRIGLVL_REG (0x1000) /* FIFO IRQ trigger levels */
+#define MAX3107_TXFIFOLVL_REG (0x1100) /* TX FIFO level */
+#define MAX3107_RXFIFOLVL_REG (0x1200) /* RX FIFO level */
+#define MAX3107_FLOWCTRL_REG (0x1300) /* Flow control */
+#define MAX3107_XON1_REG (0x1400) /* XON1 character */
+#define MAX3107_XON2_REG (0x1500) /* XON2 character */
+#define MAX3107_XOFF1_REG (0x1600) /* XOFF1 character */
+#define MAX3107_XOFF2_REG (0x1700) /* XOFF2 character */
+#define MAX3107_GPIOCFG_REG (0x1800) /* GPIO config */
+#define MAX3107_GPIODATA_REG (0x1900) /* GPIO data */
+#define MAX3107_PLLCFG_REG (0x1a00) /* PLL config */
+#define MAX3107_BRGCFG_REG (0x1b00) /* Baud rate generator conf */
+#define MAX3107_BRGDIVLSB_REG (0x1c00) /* Baud rate divisor LSB */
+#define MAX3107_BRGDIVMSB_REG (0x1d00) /* Baud rate divisor MSB */
+#define MAX3107_CLKSRC_REG (0x1e00) /* Clock source */
+#define MAX3107_REVID_REG (0x1f00) /* Revision identification */
+
+/* IRQ register bits */
+#define MAX3107_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */
+#define MAX3107_IRQ_SPCHR_BIT (1 << 1) /* Special char interrupt */
+#define MAX3107_IRQ_STS_BIT (1 << 2) /* Status interrupt */
+#define MAX3107_IRQ_RXFIFO_BIT (1 << 3) /* RX FIFO interrupt */
+#define MAX3107_IRQ_TXFIFO_BIT (1 << 4) /* TX FIFO interrupt */
+#define MAX3107_IRQ_TXEMPTY_BIT (1 << 5) /* TX FIFO empty interrupt */
+#define MAX3107_IRQ_RXEMPTY_BIT (1 << 6) /* RX FIFO empty interrupt */
+#define MAX3107_IRQ_CTS_BIT (1 << 7) /* CTS interrupt */
+
+/* LSR register bits */
+#define MAX3107_LSR_RXTO_BIT (1 << 0) /* RX timeout */
+#define MAX3107_LSR_RXOVR_BIT (1 << 1) /* RX overrun */
+#define MAX3107_LSR_RXPAR_BIT (1 << 2) /* RX parity error */
+#define MAX3107_LSR_FRERR_BIT (1 << 3) /* Frame error */
+#define MAX3107_LSR_RXBRK_BIT (1 << 4) /* RX break */
+#define MAX3107_LSR_RXNOISE_BIT (1 << 5) /* RX noise */
+#define MAX3107_LSR_UNDEF6_BIT (1 << 6) /* Undefined/not used */
+#define MAX3107_LSR_CTS_BIT (1 << 7) /* CTS pin state */
+
+/* Special character register bits */
+#define MAX3107_SPCHR_XON1_BIT (1 << 0) /* XON1 character */
+#define MAX3107_SPCHR_XON2_BIT (1 << 1) /* XON2 character */
+#define MAX3107_SPCHR_XOFF1_BIT (1 << 2) /* XOFF1 character */
+#define MAX3107_SPCHR_XOFF2_BIT (1 << 3) /* XOFF2 character */
+#define MAX3107_SPCHR_BREAK_BIT (1 << 4) /* RX break */
+#define MAX3107_SPCHR_MULTIDROP_BIT (1 << 5) /* 9-bit multidrop addr char */
+#define MAX3107_SPCHR_UNDEF6_BIT (1 << 6) /* Undefined/not used */
+#define MAX3107_SPCHR_UNDEF7_BIT (1 << 7) /* Undefined/not used */
+
+/* Status register bits */
+#define MAX3107_STS_GPIO0_BIT (1 << 0) /* GPIO 0 interrupt */
+#define MAX3107_STS_GPIO1_BIT (1 << 1) /* GPIO 1 interrupt */
+#define MAX3107_STS_GPIO2_BIT (1 << 2) /* GPIO 2 interrupt */
+#define MAX3107_STS_GPIO3_BIT (1 << 3) /* GPIO 3 interrupt */
+#define MAX3107_STS_UNDEF4_BIT (1 << 4) /* Undefined/not used */
+#define MAX3107_STS_CLKREADY_BIT (1 << 5) /* Clock ready */
+#define MAX3107_STS_SLEEP_BIT (1 << 6) /* Sleep interrupt */
+#define MAX3107_STS_UNDEF7_BIT (1 << 7) /* Undefined/not used */
+
+/* MODE1 register bits */
+#define MAX3107_MODE1_RXDIS_BIT (1 << 0) /* RX disable */
+#define MAX3107_MODE1_TXDIS_BIT (1 << 1) /* TX disable */
+#define MAX3107_MODE1_TXHIZ_BIT (1 << 2) /* TX pin three-state */
+#define MAX3107_MODE1_RTSHIZ_BIT (1 << 3) /* RTS pin three-state */
+#define MAX3107_MODE1_TRNSCVCTRL_BIT (1 << 4) /* Transceiver ctrl enable */
+#define MAX3107_MODE1_FORCESLEEP_BIT (1 << 5) /* Force sleep mode */
+#define MAX3107_MODE1_AUTOSLEEP_BIT (1 << 6) /* Auto sleep enable */
+#define MAX3107_MODE1_IRQSEL_BIT (1 << 7) /* IRQ pin enable */
+
+/* MODE2 register bits */
+#define MAX3107_MODE2_RST_BIT (1 << 0) /* Chip reset */
+#define MAX3107_MODE2_FIFORST_BIT (1 << 1) /* FIFO reset */
+#define MAX3107_MODE2_RXTRIGINV_BIT (1 << 2) /* RX FIFO INT invert */
+#define MAX3107_MODE2_RXEMPTINV_BIT (1 << 3) /* RX FIFO empty INT invert */
+#define MAX3107_MODE2_SPCHR_BIT (1 << 4) /* Special chr detect enable */
+#define MAX3107_MODE2_LOOPBACK_BIT (1 << 5) /* Internal loopback enable */
+#define MAX3107_MODE2_MULTIDROP_BIT (1 << 6) /* 9-bit multidrop enable */
+#define MAX3107_MODE2_ECHOSUPR_BIT (1 << 7) /* ECHO suppression enable */
+
+/* LCR register bits */
+#define MAX3107_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */
+#define MAX3107_LCR_LENGTH1_BIT (1 << 1) /* Word length bit 1
+ *
+ * Word length bits table:
+ * 00 -> 5 bit words
+ * 01 -> 6 bit words
+ * 10 -> 7 bit words
+ * 11 -> 8 bit words
+ */
+#define MAX3107_LCR_STOPLEN_BIT (1 << 2) /* STOP length bit
+ *
+ * STOP length bit table:
+ * 0 -> 1 stop bit
+ * 1 -> 1-1.5 stop bits if
+ * word length is 5,
+ * 2 stop bits otherwise
+ */
+#define MAX3107_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */
+#define MAX3107_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */
+#define MAX3107_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */
+#define MAX3107_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */
+#define MAX3107_LCR_RTS_BIT (1 << 7) /* RTS pin control */
+#define MAX3107_LCR_WORD_LEN_5 (0x0000)
+#define MAX3107_LCR_WORD_LEN_6 (0x0001)
+#define MAX3107_LCR_WORD_LEN_7 (0x0002)
+#define MAX3107_LCR_WORD_LEN_8 (0x0003)
+
+
+/* IRDA register bits */
+#define MAX3107_IRDA_IRDAEN_BIT (1 << 0) /* IRDA mode enable */
+#define MAX3107_IRDA_SIR_BIT (1 << 1) /* SIR mode enable */
+#define MAX3107_IRDA_SHORTIR_BIT (1 << 2) /* Short SIR mode enable */
+#define MAX3107_IRDA_MIR_BIT (1 << 3) /* MIR mode enable */
+#define MAX3107_IRDA_RXINV_BIT (1 << 4) /* RX logic inversion enable */
+#define MAX3107_IRDA_TXINV_BIT (1 << 5) /* TX logic inversion enable */
+#define MAX3107_IRDA_UNDEF6_BIT (1 << 6) /* Undefined/not used */
+#define MAX3107_IRDA_UNDEF7_BIT (1 << 7) /* Undefined/not used */
+
+/* Flow control trigger level register masks */
+#define MAX3107_FLOWLVL_HALT_MASK (0x000f) /* Flow control halt level */
+#define MAX3107_FLOWLVL_RES_MASK (0x00f0) /* Flow control resume level */
+#define MAX3107_FLOWLVL_HALT(words) ((words/8) & 0x000f)
+#define MAX3107_FLOWLVL_RES(words) (((words/8) & 0x000f) << 4)
+
+/* FIFO interrupt trigger level register masks */
+#define MAX3107_FIFOTRIGLVL_TX_MASK (0x000f) /* TX FIFO trigger level */
+#define MAX3107_FIFOTRIGLVL_RX_MASK (0x00f0) /* RX FIFO trigger level */
+#define MAX3107_FIFOTRIGLVL_TX(words) ((words/8) & 0x000f)
+#define MAX3107_FIFOTRIGLVL_RX(words) (((words/8) & 0x000f) << 4)
+
+/* Flow control register bits */
+#define MAX3107_FLOWCTRL_AUTORTS_BIT (1 << 0) /* Auto RTS flow ctrl enable */
+#define MAX3107_FLOWCTRL_AUTOCTS_BIT (1 << 1) /* Auto CTS flow ctrl enable */
+#define MAX3107_FLOWCTRL_GPIADDR_BIT (1 << 2) /* Enables that GPIO inputs
+ * are used in conjunction with
+ * XOFF2 for definition of
+ * special character */
+#define MAX3107_FLOWCTRL_SWFLOWEN_BIT (1 << 3) /* Auto SW flow ctrl enable */
+#define MAX3107_FLOWCTRL_SWFLOW0_BIT (1 << 4) /* SWFLOW bit 0 */
+#define MAX3107_FLOWCTRL_SWFLOW1_BIT (1 << 5) /* SWFLOW bit 1
+ *
+ * SWFLOW bits 1 & 0 table:
+ * 00 -> no transmitter flow
+ * control
+ * 01 -> receiver compares
+ * XON2 and XOFF2
+ * and controls
+ * transmitter
+ * 10 -> receiver compares
+ * XON1 and XOFF1
+ * and controls
+ * transmitter
+ * 11 -> receiver compares
+ * XON1, XON2, XOFF1 and
+ * XOFF2 and controls
+ * transmitter
+ */
+#define MAX3107_FLOWCTRL_SWFLOW2_BIT (1 << 6) /* SWFLOW bit 2 */
+#define MAX3107_FLOWCTRL_SWFLOW3_BIT (1 << 7) /* SWFLOW bit 3
+ *
+ * SWFLOW bits 3 & 2 table:
+ * 00 -> no received flow
+ * control
+ * 01 -> transmitter generates
+ * XON2 and XOFF2
+ * 10 -> transmitter generates
+ * XON1 and XOFF1
+ * 11 -> transmitter generates
+ * XON1, XON2, XOFF1 and
+ * XOFF2
+ */
+
+/* GPIO configuration register bits */
+#define MAX3107_GPIOCFG_GP0OUT_BIT (1 << 0) /* GPIO 0 output enable */
+#define MAX3107_GPIOCFG_GP1OUT_BIT (1 << 1) /* GPIO 1 output enable */
+#define MAX3107_GPIOCFG_GP2OUT_BIT (1 << 2) /* GPIO 2 output enable */
+#define MAX3107_GPIOCFG_GP3OUT_BIT (1 << 3) /* GPIO 3 output enable */
+#define MAX3107_GPIOCFG_GP0OD_BIT (1 << 4) /* GPIO 0 open-drain enable */
+#define MAX3107_GPIOCFG_GP1OD_BIT (1 << 5) /* GPIO 1 open-drain enable */
+#define MAX3107_GPIOCFG_GP2OD_BIT (1 << 6) /* GPIO 2 open-drain enable */
+#define MAX3107_GPIOCFG_GP3OD_BIT (1 << 7) /* GPIO 3 open-drain enable */
+
+/* GPIO DATA register bits */
+#define MAX3107_GPIODATA_GP0OUT_BIT (1 << 0) /* GPIO 0 output value */
+#define MAX3107_GPIODATA_GP1OUT_BIT (1 << 1) /* GPIO 1 output value */
+#define MAX3107_GPIODATA_GP2OUT_BIT (1 << 2) /* GPIO 2 output value */
+#define MAX3107_GPIODATA_GP3OUT_BIT (1 << 3) /* GPIO 3 output value */
+#define MAX3107_GPIODATA_GP0IN_BIT (1 << 4) /* GPIO 0 input value */
+#define MAX3107_GPIODATA_GP1IN_BIT (1 << 5) /* GPIO 1 input value */
+#define MAX3107_GPIODATA_GP2IN_BIT (1 << 6) /* GPIO 2 input value */
+#define MAX3107_GPIODATA_GP3IN_BIT (1 << 7) /* GPIO 3 input value */
+
+/* PLL configuration register masks */
+#define MAX3107_PLLCFG_PREDIV_MASK (0x003f) /* PLL predivision value */
+#define MAX3107_PLLCFG_PLLFACTOR_MASK (0x00c0) /* PLL multiplication factor */
+
+/* Baud rate generator configuration register masks and bits */
+#define MAX3107_BRGCFG_FRACT_MASK (0x000f) /* Fractional portion of
+ * Baud rate generator divisor
+ */
+#define MAX3107_BRGCFG_2XMODE_BIT (1 << 4) /* Double baud rate */
+#define MAX3107_BRGCFG_4XMODE_BIT (1 << 5) /* Quadruple baud rate */
+#define MAX3107_BRGCFG_UNDEF6_BIT (1 << 6) /* Undefined/not used */
+#define MAX3107_BRGCFG_UNDEF7_BIT (1 << 7) /* Undefined/not used */
+
+/* Clock source register bits */
+#define MAX3107_CLKSRC_INTOSC_BIT (1 << 0) /* Internal osc enable */
+#define MAX3107_CLKSRC_CRYST_BIT (1 << 1) /* Crystal osc enable */
+#define MAX3107_CLKSRC_PLL_BIT (1 << 2) /* PLL enable */
+#define MAX3107_CLKSRC_PLLBYP_BIT (1 << 3) /* PLL bypass */
+#define MAX3107_CLKSRC_EXTCLK_BIT (1 << 4) /* External clock enable */
+#define MAX3107_CLKSRC_UNDEF5_BIT (1 << 5) /* Undefined/not used */
+#define MAX3107_CLKSRC_UNDEF6_BIT (1 << 6) /* Undefined/not used */
+#define MAX3107_CLKSRC_CLK2RTS_BIT (1 << 7) /* Baud clk to RTS pin */
+
+
+/* HW definitions */
+#define MAX3107_RX_FIFO_SIZE 128
+#define MAX3107_TX_FIFO_SIZE 128
+#define MAX3107_REVID1 0x00a0
+#define MAX3107_REVID2 0x00a1
+
+
+/* Baud rate generator configuration values for external clock 13MHz */
+#define MAX3107_BRG13_B300 (0x0A9400 | 0x05)
+#define MAX3107_BRG13_B600 (0x054A00 | 0x03)
+#define MAX3107_BRG13_B1200 (0x02A500 | 0x01)
+#define MAX3107_BRG13_B2400 (0x015200 | 0x09)
+#define MAX3107_BRG13_B4800 (0x00A900 | 0x04)
+#define MAX3107_BRG13_B9600 (0x005400 | 0x0A)
+#define MAX3107_BRG13_B19200 (0x002A00 | 0x05)
+#define MAX3107_BRG13_B38400 (0x001500 | 0x03)
+#define MAX3107_BRG13_B57600 (0x000E00 | 0x02)
+#define MAX3107_BRG13_B115200 (0x000700 | 0x01)
+#define MAX3107_BRG13_B230400 (0x000300 | 0x08)
+#define MAX3107_BRG13_B460800 (0x000100 | 0x0c)
+#define MAX3107_BRG13_B921600 (0x000100 | 0x1c)
+
+/* Baud rate generator configuration values for external clock 26MHz */
+#define MAX3107_BRG26_B300 (0x152800 | 0x0A)
+#define MAX3107_BRG26_B600 (0x0A9400 | 0x05)
+#define MAX3107_BRG26_B1200 (0x054A00 | 0x03)
+#define MAX3107_BRG26_B2400 (0x02A500 | 0x01)
+#define MAX3107_BRG26_B4800 (0x015200 | 0x09)
+#define MAX3107_BRG26_B9600 (0x00A900 | 0x04)
+#define MAX3107_BRG26_B19200 (0x005400 | 0x0A)
+#define MAX3107_BRG26_B38400 (0x002A00 | 0x05)
+#define MAX3107_BRG26_B57600 (0x001C00 | 0x03)
+#define MAX3107_BRG26_B115200 (0x000E00 | 0x02)
+#define MAX3107_BRG26_B230400 (0x000700 | 0x01)
+#define MAX3107_BRG26_B460800 (0x000300 | 0x08)
+#define MAX3107_BRG26_B921600 (0x000100 | 0x0C)
+
+/* Baud rate generator configuration values for internal clock */
+#define MAX3107_BRG13_IB300 (0x008000 | 0x00)
+#define MAX3107_BRG13_IB600 (0x004000 | 0x00)
+#define MAX3107_BRG13_IB1200 (0x002000 | 0x00)
+#define MAX3107_BRG13_IB2400 (0x001000 | 0x00)
+#define MAX3107_BRG13_IB4800 (0x000800 | 0x00)
+#define MAX3107_BRG13_IB9600 (0x000400 | 0x00)
+#define MAX3107_BRG13_IB19200 (0x000200 | 0x00)
+#define MAX3107_BRG13_IB38400 (0x000100 | 0x00)
+#define MAX3107_BRG13_IB57600 (0x000000 | 0x0B)
+#define MAX3107_BRG13_IB115200 (0x000000 | 0x05)
+#define MAX3107_BRG13_IB230400 (0x000000 | 0x03)
+#define MAX3107_BRG13_IB460800 (0x000000 | 0x00)
+#define MAX3107_BRG13_IB921600 (0x000000 | 0x00)
+
+
+struct baud_table {
+ int baud;
+ u32 new_brg;
+};
+
+struct max3107_port {
+ /* UART port structure */
+ struct uart_port port;
+
+ /* SPI device structure */
+ struct spi_device *spi;
+
+#if defined(CONFIG_GPIOLIB)
+ /* GPIO chip stucture */
+ struct gpio_chip chip;
+#endif
+
+ /* Workqueue that does all the magic */
+ struct workqueue_struct *workqueue;
+ struct work_struct work;
+
+ /* Lock for shared data */
+ spinlock_t data_lock;
+
+ /* Device configuration */
+ int ext_clk; /* 1 if external clock used */
+ int loopback; /* Current loopback mode state */
+ int baud; /* Current baud rate */
+
+ /* State flags */
+ int suspended; /* Indicates suspend mode */
+ int tx_fifo_empty; /* Flag for TX FIFO state */
+ int rx_enabled; /* Flag for receiver state */
+ int tx_enabled; /* Flag for transmitter state */
+
+ u16 irqen_reg; /* Current IRQ enable register value */
+ /* Shared data */
+ u16 mode1_reg; /* Current mode1 register value*/
+ int mode1_commit; /* Flag for setting new mode1 register value */
+ u16 lcr_reg; /* Current LCR register value */
+ int lcr_commit; /* Flag for setting new LCR register value */
+ u32 brg_cfg; /* Current Baud rate generator config */
+ int brg_commit; /* Flag for setting new baud rate generator
+ * config
+ */
+ struct baud_table *baud_tbl;
+ int handle_irq; /* Indicates that IRQ should be handled */
+
+ /* Rx buffer and str*/
+ u16 *rxbuf;
+ u8 *rxstr;
+ /* Tx buffer*/
+ u16 *txbuf;
+
+ struct max3107_plat *pdata; /* Platform data */
+};
+
+/* Platform data structure */
+struct max3107_plat {
+ /* Loopback mode enable */
+ int loopback;
+ /* External clock enable */
+ int ext_clk;
+ /* Called during the register initialisation */
+ void (*init)(struct max3107_port *s);
+ /* Called when the port is found and configured */
+ int (*configure)(struct max3107_port *s);
+ /* HW suspend function */
+ void (*hw_suspend) (struct max3107_port *s, int suspend);
+ /* Polling mode enable */
+ int polled_mode;
+ /* Polling period if polling mode enabled */
+ int poll_time;
+};
+
+extern int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len);
+extern void max3107_hw_susp(struct max3107_port *s, int suspend);
+extern int max3107_probe(struct spi_device *spi, struct max3107_plat *pdata);
+extern int max3107_remove(struct spi_device *spi);
+extern int max3107_suspend(struct spi_device *spi, pm_message_t state);
+extern int max3107_resume(struct spi_device *spi);
+
+#endif /* _LINUX_SERIAL_MAX3107_H */
static unsigned int mcf_get_mctrl(struct uart_port *port)
{
struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
- unsigned long flags;
unsigned int sigs;
- spin_lock_irqsave(&port->lock, flags);
sigs = (readb(port->membase + MCFUART_UIPR) & MCFUART_UIPR_CTS) ?
0 : TIOCM_CTS;
sigs |= (pp->sigs & TIOCM_RTS);
sigs |= (mcf_getppdcd(port->line) ? TIOCM_CD : 0);
sigs |= (mcf_getppdtr(port->line) ? TIOCM_DTR : 0);
- spin_unlock_irqrestore(&port->lock, flags);
+
return sigs;
}
static void mcf_set_mctrl(struct uart_port *port, unsigned int sigs)
{
struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
- unsigned long flags;
- spin_lock_irqsave(&port->lock, flags);
pp->sigs = sigs;
mcf_setppdtr(port->line, (sigs & TIOCM_DTR));
if (sigs & TIOCM_RTS)
writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP1);
else
writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP0);
- spin_unlock_irqrestore(&port->lock, flags);
}
/****************************************************************************/
static void mcf_start_tx(struct uart_port *port)
{
struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
- unsigned long flags;
- spin_lock_irqsave(&port->lock, flags);
pp->imr |= MCFUART_UIR_TXREADY;
writeb(pp->imr, port->membase + MCFUART_UIMR);
- spin_unlock_irqrestore(&port->lock, flags);
}
/****************************************************************************/
static void mcf_stop_tx(struct uart_port *port)
{
struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
- unsigned long flags;
- spin_lock_irqsave(&port->lock, flags);
pp->imr &= ~MCFUART_UIR_TXREADY;
writeb(pp->imr, port->membase + MCFUART_UIMR);
- spin_unlock_irqrestore(&port->lock, flags);
}
/****************************************************************************/
static void mcf_stop_rx(struct uart_port *port)
{
struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
- unsigned long flags;
- spin_lock_irqsave(&port->lock, flags);
pp->imr &= ~MCFUART_UIR_RXREADY;
writeb(pp->imr, port->membase + MCFUART_UIMR);
- spin_unlock_irqrestore(&port->lock, flags);
}
/****************************************************************************/
struct uart_port *port = data;
struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
unsigned int isr;
+ irqreturn_t ret = IRQ_NONE;
isr = readb(port->membase + MCFUART_UISR) & pp->imr;
- if (isr & MCFUART_UIR_RXREADY)
+
+ spin_lock(&port->lock);
+ if (isr & MCFUART_UIR_RXREADY) {
mcf_rx_chars(pp);
- if (isr & MCFUART_UIR_TXREADY)
+ ret = IRQ_HANDLED;
+ }
+ if (isr & MCFUART_UIR_TXREADY) {
mcf_tx_chars(pp);
- return IRQ_HANDLED;
+ ret = IRQ_HANDLED;
+ }
+ spin_unlock(&port->lock);
+
+ return ret;
}
/****************************************************************************/
--- /dev/null
+/*
+ * mfd.c: driver for High Speed UART device of Intel Medfield platform
+ *
+ * Refer pxa.c, 8250.c and some other drivers in drivers/serial/
+ *
+ * (C) Copyright 2010 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+/* Notes:
+ * 1. DMA channel allocation: 0/1 channel are assigned to port 0,
+ * 2/3 chan to port 1, 4/5 chan to port 3. Even number chans
+ * are used for RX, odd chans for TX
+ *
+ * 2. In A0 stepping, UART will not support TX half empty flag
+ *
+ * 3. The RI/DSR/DCD/DTR are not pinned out, DCD & DSR are always
+ * asserted, only when the HW is reset the DDCD and DDSR will
+ * be triggered
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/serial_reg.h>
+#include <linux/circ_buf.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial_mfd.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+
+#define MFD_HSU_A0_STEPPING 1
+
+#define HSU_DMA_BUF_SIZE 2048
+
+#define chan_readl(chan, offset) readl(chan->reg + offset)
+#define chan_writel(chan, offset, val) writel(val, chan->reg + offset)
+
+#define mfd_readl(obj, offset) readl(obj->reg + offset)
+#define mfd_writel(obj, offset, val) writel(val, obj->reg + offset)
+
+#define HSU_DMA_TIMEOUT_CHECK_FREQ (HZ/10)
+
+struct hsu_dma_buffer {
+ u8 *buf;
+ dma_addr_t dma_addr;
+ u32 dma_size;
+ u32 ofs;
+};
+
+struct hsu_dma_chan {
+ u32 id;
+ enum dma_data_direction dirt;
+ struct uart_hsu_port *uport;
+ void __iomem *reg;
+ struct timer_list rx_timer; /* only needed by RX channel */
+};
+
+struct uart_hsu_port {
+ struct uart_port port;
+ unsigned char ier;
+ unsigned char lcr;
+ unsigned char mcr;
+ unsigned int lsr_break_flag;
+ char name[12];
+ int index;
+ struct device *dev;
+
+ struct hsu_dma_chan *txc;
+ struct hsu_dma_chan *rxc;
+ struct hsu_dma_buffer txbuf;
+ struct hsu_dma_buffer rxbuf;
+ int use_dma; /* flag for DMA/PIO */
+ int running;
+ int dma_tx_on;
+};
+
+/* Top level data structure of HSU */
+struct hsu_port {
+ void __iomem *reg;
+ unsigned long paddr;
+ unsigned long iolen;
+ u32 irq;
+
+ struct uart_hsu_port port[3];
+ struct hsu_dma_chan chans[10];
+
+ struct dentry *debugfs;
+};
+
+static inline unsigned int serial_in(struct uart_hsu_port *up, int offset)
+{
+ unsigned int val;
+
+ if (offset > UART_MSR) {
+ offset <<= 2;
+ val = readl(up->port.membase + offset);
+ } else
+ val = (unsigned int)readb(up->port.membase + offset);
+
+ return val;
+}
+
+static inline void serial_out(struct uart_hsu_port *up, int offset, int value)
+{
+ if (offset > UART_MSR) {
+ offset <<= 2;
+ writel(value, up->port.membase + offset);
+ } else {
+ unsigned char val = value & 0xff;
+ writeb(val, up->port.membase + offset);
+ }
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+#define HSU_REGS_BUFSIZE 1024
+
+static int hsu_show_regs_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t port_show_regs(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct uart_hsu_port *up = file->private_data;
+ char *buf;
+ u32 len = 0;
+ ssize_t ret;
+
+ buf = kzalloc(HSU_REGS_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return 0;
+
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "MFD HSU port[%d] regs:\n", up->index);
+
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "=================================\n");
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "IER: \t\t0x%08x\n", serial_in(up, UART_IER));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "IIR: \t\t0x%08x\n", serial_in(up, UART_IIR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "LCR: \t\t0x%08x\n", serial_in(up, UART_LCR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "MCR: \t\t0x%08x\n", serial_in(up, UART_MCR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "LSR: \t\t0x%08x\n", serial_in(up, UART_LSR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "MSR: \t\t0x%08x\n", serial_in(up, UART_MSR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "FOR: \t\t0x%08x\n", serial_in(up, UART_FOR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "PS: \t\t0x%08x\n", serial_in(up, UART_PS));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "MUL: \t\t0x%08x\n", serial_in(up, UART_MUL));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "DIV: \t\t0x%08x\n", serial_in(up, UART_DIV));
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t dma_show_regs(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hsu_dma_chan *chan = file->private_data;
+ char *buf;
+ u32 len = 0;
+ ssize_t ret;
+
+ buf = kzalloc(HSU_REGS_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return 0;
+
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "MFD HSU DMA channel [%d] regs:\n", chan->id);
+
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "=================================\n");
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "CR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_CR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "DCR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_DCR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "BSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_BSR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "MOTSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_MOTSR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D0SAR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D0TSR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D1SAR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D1TSR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D2SAR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D2TSR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D3SAR));
+ len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+ "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D3TSR));
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ kfree(buf);
+ return ret;
+}
+
+static const struct file_operations port_regs_ops = {
+ .owner = THIS_MODULE,
+ .open = hsu_show_regs_open,
+ .read = port_show_regs,
+};
+
+static const struct file_operations dma_regs_ops = {
+ .owner = THIS_MODULE,
+ .open = hsu_show_regs_open,
+ .read = dma_show_regs,
+};
+
+static int hsu_debugfs_init(struct hsu_port *hsu)
+{
+ int i;
+ char name[32];
+
+ hsu->debugfs = debugfs_create_dir("hsu", NULL);
+ if (!hsu->debugfs)
+ return -ENOMEM;
+
+ for (i = 0; i < 3; i++) {
+ snprintf(name, sizeof(name), "port_%d_regs", i);
+ debugfs_create_file(name, S_IFREG | S_IRUGO,
+ hsu->debugfs, (void *)(&hsu->port[i]), &port_regs_ops);
+ }
+
+ for (i = 0; i < 6; i++) {
+ snprintf(name, sizeof(name), "dma_chan_%d_regs", i);
+ debugfs_create_file(name, S_IFREG | S_IRUGO,
+ hsu->debugfs, (void *)&hsu->chans[i], &dma_regs_ops);
+ }
+
+ return 0;
+}
+
+static void hsu_debugfs_remove(struct hsu_port *hsu)
+{
+ if (hsu->debugfs)
+ debugfs_remove_recursive(hsu->debugfs);
+}
+
+#else
+static inline int hsu_debugfs_init(struct hsu_port *hsu)
+{
+ return 0;
+}
+
+static inline void hsu_debugfs_remove(struct hsu_port *hsu)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static void serial_hsu_enable_ms(struct uart_port *port)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+
+ up->ier |= UART_IER_MSI;
+ serial_out(up, UART_IER, up->ier);
+}
+
+void hsu_dma_tx(struct uart_hsu_port *up)
+{
+ struct circ_buf *xmit = &up->port.state->xmit;
+ struct hsu_dma_buffer *dbuf = &up->txbuf;
+ int count;
+
+ /* test_and_set_bit may be better, but anyway it's in lock protected mode */
+ if (up->dma_tx_on)
+ return;
+
+ /* Update the circ buf info */
+ xmit->tail += dbuf->ofs;
+ xmit->tail &= UART_XMIT_SIZE - 1;
+
+ up->port.icount.tx += dbuf->ofs;
+ dbuf->ofs = 0;
+
+ /* Disable the channel */
+ chan_writel(up->txc, HSU_CH_CR, 0x0);
+
+ if (!uart_circ_empty(xmit) && !uart_tx_stopped(&up->port)) {
+ dma_sync_single_for_device(up->port.dev,
+ dbuf->dma_addr,
+ dbuf->dma_size,
+ DMA_TO_DEVICE);
+
+ count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ dbuf->ofs = count;
+
+ /* Reprogram the channel */
+ chan_writel(up->txc, HSU_CH_D0SAR, dbuf->dma_addr + xmit->tail);
+ chan_writel(up->txc, HSU_CH_D0TSR, count);
+
+ /* Reenable the channel */
+ chan_writel(up->txc, HSU_CH_DCR, 0x1
+ | (0x1 << 8)
+ | (0x1 << 16)
+ | (0x1 << 24));
+ up->dma_tx_on = 1;
+ chan_writel(up->txc, HSU_CH_CR, 0x1);
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&up->port);
+}
+
+/* The buffer is already cache coherent */
+void hsu_dma_start_rx_chan(struct hsu_dma_chan *rxc, struct hsu_dma_buffer *dbuf)
+{
+ dbuf->ofs = 0;
+
+ chan_writel(rxc, HSU_CH_BSR, 32);
+ chan_writel(rxc, HSU_CH_MOTSR, 4);
+
+ chan_writel(rxc, HSU_CH_D0SAR, dbuf->dma_addr);
+ chan_writel(rxc, HSU_CH_D0TSR, dbuf->dma_size);
+ chan_writel(rxc, HSU_CH_DCR, 0x1 | (0x1 << 8)
+ | (0x1 << 16)
+ | (0x1 << 24) /* timeout bit, see HSU Errata 1 */
+ );
+ chan_writel(rxc, HSU_CH_CR, 0x3);
+
+ mod_timer(&rxc->rx_timer, jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ);
+}
+
+/* Protected by spin_lock_irqsave(port->lock) */
+static void serial_hsu_start_tx(struct uart_port *port)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+
+ if (up->use_dma) {
+ hsu_dma_tx(up);
+ } else if (!(up->ier & UART_IER_THRI)) {
+ up->ier |= UART_IER_THRI;
+ serial_out(up, UART_IER, up->ier);
+ }
+}
+
+static void serial_hsu_stop_tx(struct uart_port *port)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ struct hsu_dma_chan *txc = up->txc;
+
+ if (up->use_dma)
+ chan_writel(txc, HSU_CH_CR, 0x0);
+ else if (up->ier & UART_IER_THRI) {
+ up->ier &= ~UART_IER_THRI;
+ serial_out(up, UART_IER, up->ier);
+ }
+}
+
+/* This is always called in spinlock protected mode, so
+ * modify timeout timer is safe here */
+void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts)
+{
+ struct hsu_dma_buffer *dbuf = &up->rxbuf;
+ struct hsu_dma_chan *chan = up->rxc;
+ struct uart_port *port = &up->port;
+ struct tty_struct *tty = port->state->port.tty;
+ int count;
+
+ if (!tty)
+ return;
+
+ /*
+ * First need to know how many is already transferred,
+ * then check if its a timeout DMA irq, and return
+ * the trail bytes out, push them up and reenable the
+ * channel
+ */
+
+ /* Timeout IRQ, need wait some time, see Errata 2 */
+ if (int_sts & 0xf00)
+ udelay(2);
+
+ /* Stop the channel */
+ chan_writel(chan, HSU_CH_CR, 0x0);
+
+ count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr;
+ if (!count) {
+ /* Restart the channel before we leave */
+ chan_writel(chan, HSU_CH_CR, 0x3);
+ return;
+ }
+ del_timer(&chan->rx_timer);
+
+ dma_sync_single_for_cpu(port->dev, dbuf->dma_addr,
+ dbuf->dma_size, DMA_FROM_DEVICE);
+
+ /*
+ * Head will only wrap around when we recycle
+ * the DMA buffer, and when that happens, we
+ * explicitly set tail to 0. So head will
+ * always be greater than tail.
+ */
+ tty_insert_flip_string(tty, dbuf->buf, count);
+ port->icount.rx += count;
+
+ dma_sync_single_for_device(up->port.dev, dbuf->dma_addr,
+ dbuf->dma_size, DMA_FROM_DEVICE);
+
+ /* Reprogram the channel */
+ chan_writel(chan, HSU_CH_D0SAR, dbuf->dma_addr);
+ chan_writel(chan, HSU_CH_D0TSR, dbuf->dma_size);
+ chan_writel(chan, HSU_CH_DCR, 0x1
+ | (0x1 << 8)
+ | (0x1 << 16)
+ | (0x1 << 24) /* timeout bit, see HSU Errata 1 */
+ );
+ tty_flip_buffer_push(tty);
+
+ chan_writel(chan, HSU_CH_CR, 0x3);
+ chan->rx_timer.expires = jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ;
+ add_timer(&chan->rx_timer);
+
+}
+
+static void serial_hsu_stop_rx(struct uart_port *port)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ struct hsu_dma_chan *chan = up->rxc;
+
+ if (up->use_dma)
+ chan_writel(chan, HSU_CH_CR, 0x2);
+ else {
+ up->ier &= ~UART_IER_RLSI;
+ up->port.read_status_mask &= ~UART_LSR_DR;
+ serial_out(up, UART_IER, up->ier);
+ }
+}
+
+static inline void receive_chars(struct uart_hsu_port *up, int *status)
+{
+ struct tty_struct *tty = up->port.state->port.tty;
+ unsigned int ch, flag;
+ unsigned int max_count = 256;
+
+ if (!tty)
+ return;
+
+ do {
+ ch = serial_in(up, UART_RX);
+ flag = TTY_NORMAL;
+ up->port.icount.rx++;
+
+ if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
+ UART_LSR_FE | UART_LSR_OE))) {
+
+ dev_warn(up->dev, "We really rush into ERR/BI case"
+ "status = 0x%02x", *status);
+ /* For statistics only */
+ if (*status & UART_LSR_BI) {
+ *status &= ~(UART_LSR_FE | UART_LSR_PE);
+ up->port.icount.brk++;
+ /*
+ * We do the SysRQ and SAK checking
+ * here because otherwise the break
+ * may get masked by ignore_status_mask
+ * or read_status_mask.
+ */
+ if (uart_handle_break(&up->port))
+ goto ignore_char;
+ } else if (*status & UART_LSR_PE)
+ up->port.icount.parity++;
+ else if (*status & UART_LSR_FE)
+ up->port.icount.frame++;
+ if (*status & UART_LSR_OE)
+ up->port.icount.overrun++;
+
+ /* Mask off conditions which should be ignored. */
+ *status &= up->port.read_status_mask;
+
+#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE
+ if (up->port.cons &&
+ up->port.cons->index == up->port.line) {
+ /* Recover the break flag from console xmit */
+ *status |= up->lsr_break_flag;
+ up->lsr_break_flag = 0;
+ }
+#endif
+ if (*status & UART_LSR_BI) {
+ flag = TTY_BREAK;
+ } else if (*status & UART_LSR_PE)
+ flag = TTY_PARITY;
+ else if (*status & UART_LSR_FE)
+ flag = TTY_FRAME;
+ }
+
+ if (uart_handle_sysrq_char(&up->port, ch))
+ goto ignore_char;
+
+ uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag);
+ ignore_char:
+ *status = serial_in(up, UART_LSR);
+ } while ((*status & UART_LSR_DR) && max_count--);
+ tty_flip_buffer_push(tty);
+}
+
+static void transmit_chars(struct uart_hsu_port *up)
+{
+ struct circ_buf *xmit = &up->port.state->xmit;
+ int count;
+
+ if (up->port.x_char) {
+ serial_out(up, UART_TX, up->port.x_char);
+ up->port.icount.tx++;
+ up->port.x_char = 0;
+ return;
+ }
+ if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
+ serial_hsu_stop_tx(&up->port);
+ return;
+ }
+
+#ifndef MFD_HSU_A0_STEPPING
+ count = up->port.fifosize / 2;
+#else
+ /*
+ * A0 only supports fully empty IRQ, and the first char written
+ * into it won't clear the EMPT bit, so we may need be cautious
+ * by useing a shorter buffer
+ */
+ count = up->port.fifosize - 4;
+#endif
+ do {
+ serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+
+ up->port.icount.tx++;
+ if (uart_circ_empty(xmit))
+ break;
+ } while (--count > 0);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&up->port);
+
+ if (uart_circ_empty(xmit))
+ serial_hsu_stop_tx(&up->port);
+}
+
+static inline void check_modem_status(struct uart_hsu_port *up)
+{
+ int status;
+
+ status = serial_in(up, UART_MSR);
+
+ if ((status & UART_MSR_ANY_DELTA) == 0)
+ return;
+
+ if (status & UART_MSR_TERI)
+ up->port.icount.rng++;
+ if (status & UART_MSR_DDSR)
+ up->port.icount.dsr++;
+ /* We may only get DDCD when HW init and reset */
+ if (status & UART_MSR_DDCD)
+ uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
+ /* Will start/stop_tx accordingly */
+ if (status & UART_MSR_DCTS)
+ uart_handle_cts_change(&up->port, status & UART_MSR_CTS);
+
+ wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+}
+
+/*
+ * This handles the interrupt from one port.
+ */
+static irqreturn_t port_irq(int irq, void *dev_id)
+{
+ struct uart_hsu_port *up = dev_id;
+ unsigned int iir, lsr;
+ unsigned long flags;
+
+ if (unlikely(!up->running))
+ return IRQ_NONE;
+
+ spin_lock_irqsave(&up->port.lock, flags);
+ if (up->use_dma) {
+ lsr = serial_in(up, UART_LSR);
+ if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE |
+ UART_LSR_FE | UART_LSR_OE)))
+ dev_warn(up->dev,
+ "Got lsr irq while using DMA, lsr = 0x%2x\n",
+ lsr);
+ check_modem_status(up);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ iir = serial_in(up, UART_IIR);
+ if (iir & UART_IIR_NO_INT) {
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ return IRQ_NONE;
+ }
+
+ lsr = serial_in(up, UART_LSR);
+ if (lsr & UART_LSR_DR)
+ receive_chars(up, &lsr);
+ check_modem_status(up);
+
+ /* lsr will be renewed during the receive_chars */
+ if (lsr & UART_LSR_THRE)
+ transmit_chars(up);
+
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ return IRQ_HANDLED;
+}
+
+static inline void dma_chan_irq(struct hsu_dma_chan *chan)
+{
+ struct uart_hsu_port *up = chan->uport;
+ unsigned long flags;
+ u32 int_sts;
+
+ spin_lock_irqsave(&up->port.lock, flags);
+
+ if (!up->use_dma || !up->running)
+ goto exit;
+
+ /*
+ * No matter what situation, need read clear the IRQ status
+ * There is a bug, see Errata 5, HSD 2900918
+ */
+ int_sts = chan_readl(chan, HSU_CH_SR);
+
+ /* Rx channel */
+ if (chan->dirt == DMA_FROM_DEVICE)
+ hsu_dma_rx(up, int_sts);
+
+ /* Tx channel */
+ if (chan->dirt == DMA_TO_DEVICE) {
+ chan_writel(chan, HSU_CH_CR, 0x0);
+ up->dma_tx_on = 0;
+ hsu_dma_tx(up);
+ }
+
+exit:
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ return;
+}
+
+static irqreturn_t dma_irq(int irq, void *dev_id)
+{
+ struct hsu_port *hsu = dev_id;
+ u32 int_sts, i;
+
+ int_sts = mfd_readl(hsu, HSU_GBL_DMAISR);
+
+ /* Currently we only have 6 channels may be used */
+ for (i = 0; i < 6; i++) {
+ if (int_sts & 0x1)
+ dma_chan_irq(&hsu->chans[i]);
+ int_sts >>= 1;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static unsigned int serial_hsu_tx_empty(struct uart_port *port)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ unsigned long flags;
+ unsigned int ret;
+
+ spin_lock_irqsave(&up->port.lock, flags);
+ ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+ spin_unlock_irqrestore(&up->port.lock, flags);
+
+ return ret;
+}
+
+static unsigned int serial_hsu_get_mctrl(struct uart_port *port)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ unsigned char status;
+ unsigned int ret;
+
+ status = serial_in(up, UART_MSR);
+
+ ret = 0;
+ if (status & UART_MSR_DCD)
+ ret |= TIOCM_CAR;
+ if (status & UART_MSR_RI)
+ ret |= TIOCM_RNG;
+ if (status & UART_MSR_DSR)
+ ret |= TIOCM_DSR;
+ if (status & UART_MSR_CTS)
+ ret |= TIOCM_CTS;
+ return ret;
+}
+
+static void serial_hsu_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ unsigned char mcr = 0;
+
+ if (mctrl & TIOCM_RTS)
+ mcr |= UART_MCR_RTS;
+ if (mctrl & TIOCM_DTR)
+ mcr |= UART_MCR_DTR;
+ if (mctrl & TIOCM_OUT1)
+ mcr |= UART_MCR_OUT1;
+ if (mctrl & TIOCM_OUT2)
+ mcr |= UART_MCR_OUT2;
+ if (mctrl & TIOCM_LOOP)
+ mcr |= UART_MCR_LOOP;
+
+ mcr |= up->mcr;
+
+ serial_out(up, UART_MCR, mcr);
+}
+
+static void serial_hsu_break_ctl(struct uart_port *port, int break_state)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ unsigned long flags;
+
+ spin_lock_irqsave(&up->port.lock, flags);
+ if (break_state == -1)
+ up->lcr |= UART_LCR_SBC;
+ else
+ up->lcr &= ~UART_LCR_SBC;
+ serial_out(up, UART_LCR, up->lcr);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+/*
+ * What special to do:
+ * 1. chose the 64B fifo mode
+ * 2. make sure not to select half empty mode for A0 stepping
+ * 3. start dma or pio depends on configuration
+ * 4. we only allocate dma memory when needed
+ */
+static int serial_hsu_startup(struct uart_port *port)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ unsigned long flags;
+
+ /*
+ * Clear the FIFO buffers and disable them.
+ * (they will be reenabled in set_termios())
+ */
+ serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+ serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ serial_out(up, UART_FCR, 0);
+
+ /* Clear the interrupt registers. */
+ (void) serial_in(up, UART_LSR);
+ (void) serial_in(up, UART_RX);
+ (void) serial_in(up, UART_IIR);
+ (void) serial_in(up, UART_MSR);
+
+ /* Now, initialize the UART, default is 8n1 */
+ serial_out(up, UART_LCR, UART_LCR_WLEN8);
+
+ spin_lock_irqsave(&up->port.lock, flags);
+
+ up->port.mctrl |= TIOCM_OUT2;
+ serial_hsu_set_mctrl(&up->port, up->port.mctrl);
+
+ /*
+ * Finally, enable interrupts. Note: Modem status interrupts
+ * are set via set_termios(), which will be occurring imminently
+ * anyway, so we don't enable them here.
+ */
+ if (!up->use_dma)
+ up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE;
+ else
+ up->ier = 0;
+ serial_out(up, UART_IER, up->ier);
+
+ spin_unlock_irqrestore(&up->port.lock, flags);
+
+ /* DMA init */
+ if (up->use_dma) {
+ struct hsu_dma_buffer *dbuf;
+ struct circ_buf *xmit = &port->state->xmit;
+
+ up->dma_tx_on = 0;
+
+ /* First allocate the RX buffer */
+ dbuf = &up->rxbuf;
+ dbuf->buf = kzalloc(HSU_DMA_BUF_SIZE, GFP_KERNEL);
+ if (!dbuf->buf) {
+ up->use_dma = 0;
+ goto exit;
+ }
+ dbuf->dma_addr = dma_map_single(port->dev,
+ dbuf->buf,
+ HSU_DMA_BUF_SIZE,
+ DMA_FROM_DEVICE);
+ dbuf->dma_size = HSU_DMA_BUF_SIZE;
+
+ /* Start the RX channel right now */
+ hsu_dma_start_rx_chan(up->rxc, dbuf);
+
+ /* Next init the TX DMA */
+ dbuf = &up->txbuf;
+ dbuf->buf = xmit->buf;
+ dbuf->dma_addr = dma_map_single(port->dev,
+ dbuf->buf,
+ UART_XMIT_SIZE,
+ DMA_TO_DEVICE);
+ dbuf->dma_size = UART_XMIT_SIZE;
+
+ /* This should not be changed all around */
+ chan_writel(up->txc, HSU_CH_BSR, 32);
+ chan_writel(up->txc, HSU_CH_MOTSR, 4);
+ dbuf->ofs = 0;
+ }
+
+exit:
+ /* And clear the interrupt registers again for luck. */
+ (void) serial_in(up, UART_LSR);
+ (void) serial_in(up, UART_RX);
+ (void) serial_in(up, UART_IIR);
+ (void) serial_in(up, UART_MSR);
+
+ up->running = 1;
+ return 0;
+}
+
+static void serial_hsu_shutdown(struct uart_port *port)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ unsigned long flags;
+
+ del_timer_sync(&up->rxc->rx_timer);
+
+ /* Disable interrupts from this port */
+ up->ier = 0;
+ serial_out(up, UART_IER, 0);
+ up->running = 0;
+
+ spin_lock_irqsave(&up->port.lock, flags);
+ up->port.mctrl &= ~TIOCM_OUT2;
+ serial_hsu_set_mctrl(&up->port, up->port.mctrl);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+
+ /* Disable break condition and FIFOs */
+ serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC);
+ serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+ UART_FCR_CLEAR_RCVR |
+ UART_FCR_CLEAR_XMIT);
+ serial_out(up, UART_FCR, 0);
+}
+
+static void
+serial_hsu_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ struct tty_struct *tty = port->state->port.tty;
+ unsigned char cval, fcr = 0;
+ unsigned long flags;
+ unsigned int baud, quot;
+ u32 mul = 0x3600;
+ u32 ps = 0x10;
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ cval = UART_LCR_WLEN5;
+ break;
+ case CS6:
+ cval = UART_LCR_WLEN6;
+ break;
+ case CS7:
+ cval = UART_LCR_WLEN7;
+ break;
+ default:
+ case CS8:
+ cval = UART_LCR_WLEN8;
+ break;
+ }
+
+ /* CMSPAR isn't supported by this driver */
+ if (tty)
+ tty->termios->c_cflag &= ~CMSPAR;
+
+ if (termios->c_cflag & CSTOPB)
+ cval |= UART_LCR_STOP;
+ if (termios->c_cflag & PARENB)
+ cval |= UART_LCR_PARITY;
+ if (!(termios->c_cflag & PARODD))
+ cval |= UART_LCR_EPAR;
+
+ /*
+ * For those basic low baud rate we can get the direct
+ * scalar from 2746800, like 115200 = 2746800/24, for those
+ * higher baud rate, we have to handle them case by case,
+ * but DIV reg is never touched as its default value 0x3d09
+ */
+ baud = uart_get_baud_rate(port, termios, old, 0, 4000000);
+ quot = uart_get_divisor(port, baud);
+
+ switch (baud) {
+ case 3500000:
+ mul = 0x3345;
+ ps = 0xC;
+ quot = 1;
+ break;
+ case 2500000:
+ mul = 0x2710;
+ ps = 0x10;
+ quot = 1;
+ break;
+ case 18432000:
+ mul = 0x2400;
+ ps = 0x10;
+ quot = 1;
+ break;
+ case 1500000:
+ mul = 0x1D4C;
+ ps = 0xc;
+ quot = 1;
+ break;
+ default:
+ ;
+ }
+
+ if ((up->port.uartclk / quot) < (2400 * 16))
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_1B;
+ else if ((up->port.uartclk / quot) < (230400 * 16))
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_16B;
+ else
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_32B;
+
+ fcr |= UART_FCR_HSU_64B_FIFO;
+#ifdef MFD_HSU_A0_STEPPING
+ /* A0 doesn't support half empty IRQ */
+ fcr |= UART_FCR_FULL_EMPT_TXI;
+#endif
+
+ /*
+ * Ok, we're now changing the port state. Do it with
+ * interrupts disabled.
+ */
+ spin_lock_irqsave(&up->port.lock, flags);
+
+ /* Update the per-port timeout */
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+ if (termios->c_iflag & INPCK)
+ up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+ if (termios->c_iflag & (BRKINT | PARMRK))
+ up->port.read_status_mask |= UART_LSR_BI;
+
+ /* Characters to ignore */
+ up->port.ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+ if (termios->c_iflag & IGNBRK) {
+ up->port.ignore_status_mask |= UART_LSR_BI;
+ /*
+ * If we're ignoring parity and break indicators,
+ * ignore overruns too (for real raw support).
+ */
+ if (termios->c_iflag & IGNPAR)
+ up->port.ignore_status_mask |= UART_LSR_OE;
+ }
+
+ /* Ignore all characters if CREAD is not set */
+ if ((termios->c_cflag & CREAD) == 0)
+ up->port.ignore_status_mask |= UART_LSR_DR;
+
+ /*
+ * CTS flow control flag and modem status interrupts, disable
+ * MSI by default
+ */
+ up->ier &= ~UART_IER_MSI;
+ if (UART_ENABLE_MS(&up->port, termios->c_cflag))
+ up->ier |= UART_IER_MSI;
+
+ serial_out(up, UART_IER, up->ier);
+
+ if (termios->c_cflag & CRTSCTS)
+ up->mcr |= UART_MCR_AFE | UART_MCR_RTS;
+ else
+ up->mcr &= ~UART_MCR_AFE;
+
+ serial_out(up, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
+ serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */
+ serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */
+ serial_out(up, UART_LCR, cval); /* reset DLAB */
+ serial_out(up, UART_MUL, mul); /* set MUL */
+ serial_out(up, UART_PS, ps); /* set PS */
+ up->lcr = cval; /* Save LCR */
+ serial_hsu_set_mctrl(&up->port, up->port.mctrl);
+ serial_out(up, UART_FCR, fcr);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void
+serial_hsu_pm(struct uart_port *port, unsigned int state,
+ unsigned int oldstate)
+{
+}
+
+static void serial_hsu_release_port(struct uart_port *port)
+{
+}
+
+static int serial_hsu_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+static void serial_hsu_config_port(struct uart_port *port, int flags)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ up->port.type = PORT_MFD;
+}
+
+static int
+serial_hsu_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ /* We don't want the core code to modify any port params */
+ return -EINVAL;
+}
+
+static const char *
+serial_hsu_type(struct uart_port *port)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+ return up->name;
+}
+
+/* Mainly for uart console use */
+static struct uart_hsu_port *serial_hsu_ports[3];
+static struct uart_driver serial_hsu_reg;
+
+#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE
+
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+/* Wait for transmitter & holding register to empty */
+static inline void wait_for_xmitr(struct uart_hsu_port *up)
+{
+ unsigned int status, tmout = 1000;
+
+ /* Wait up to 1ms for the character to be sent. */
+ do {
+ status = serial_in(up, UART_LSR);
+
+ if (status & UART_LSR_BI)
+ up->lsr_break_flag = UART_LSR_BI;
+
+ if (--tmout == 0)
+ break;
+ udelay(1);
+ } while (!(status & BOTH_EMPTY));
+
+ /* Wait up to 1s for flow control if necessary */
+ if (up->port.flags & UPF_CONS_FLOW) {
+ tmout = 1000000;
+ while (--tmout &&
+ ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
+ udelay(1);
+ }
+}
+
+static void serial_hsu_console_putchar(struct uart_port *port, int ch)
+{
+ struct uart_hsu_port *up =
+ container_of(port, struct uart_hsu_port, port);
+
+ wait_for_xmitr(up);
+ serial_out(up, UART_TX, ch);
+}
+
+/*
+ * Print a string to the serial port trying not to disturb
+ * any possible real use of the port...
+ *
+ * The console_lock must be held when we get here.
+ */
+static void
+serial_hsu_console_write(struct console *co, const char *s, unsigned int count)
+{
+ struct uart_hsu_port *up = serial_hsu_ports[co->index];
+ unsigned long flags;
+ unsigned int ier;
+ int locked = 1;
+
+ local_irq_save(flags);
+ if (up->port.sysrq)
+ locked = 0;
+ else if (oops_in_progress) {
+ locked = spin_trylock(&up->port.lock);
+ } else
+ spin_lock(&up->port.lock);
+
+ /* First save the IER then disable the interrupts */
+ ier = serial_in(up, UART_IER);
+ serial_out(up, UART_IER, 0);
+
+ uart_console_write(&up->port, s, count, serial_hsu_console_putchar);
+
+ /*
+ * Finally, wait for transmitter to become empty
+ * and restore the IER
+ */
+ wait_for_xmitr(up);
+ serial_out(up, UART_IER, ier);
+
+ if (locked)
+ spin_unlock(&up->port.lock);
+ local_irq_restore(flags);
+}
+
+static struct console serial_hsu_console;
+
+static int __init
+serial_hsu_console_setup(struct console *co, char *options)
+{
+ struct uart_hsu_port *up;
+ int baud = 115200;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+ int ret;
+
+ if (co->index == -1 || co->index >= serial_hsu_reg.nr)
+ co->index = 0;
+ up = serial_hsu_ports[co->index];
+ if (!up)
+ return -ENODEV;
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ ret = uart_set_options(&up->port, co, baud, parity, bits, flow);
+
+ return ret;
+}
+
+static struct console serial_hsu_console = {
+ .name = "ttyMFD",
+ .write = serial_hsu_console_write,
+ .device = uart_console_device,
+ .setup = serial_hsu_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = 2,
+ .data = &serial_hsu_reg,
+};
+#endif
+
+struct uart_ops serial_hsu_pops = {
+ .tx_empty = serial_hsu_tx_empty,
+ .set_mctrl = serial_hsu_set_mctrl,
+ .get_mctrl = serial_hsu_get_mctrl,
+ .stop_tx = serial_hsu_stop_tx,
+ .start_tx = serial_hsu_start_tx,
+ .stop_rx = serial_hsu_stop_rx,
+ .enable_ms = serial_hsu_enable_ms,
+ .break_ctl = serial_hsu_break_ctl,
+ .startup = serial_hsu_startup,
+ .shutdown = serial_hsu_shutdown,
+ .set_termios = serial_hsu_set_termios,
+ .pm = serial_hsu_pm,
+ .type = serial_hsu_type,
+ .release_port = serial_hsu_release_port,
+ .request_port = serial_hsu_request_port,
+ .config_port = serial_hsu_config_port,
+ .verify_port = serial_hsu_verify_port,
+};
+
+static struct uart_driver serial_hsu_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = "MFD serial",
+ .dev_name = "ttyMFD",
+ .major = TTY_MAJOR,
+ .minor = 128,
+ .nr = 3,
+};
+
+#ifdef CONFIG_PM
+static int serial_hsu_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ void *priv = pci_get_drvdata(pdev);
+ struct uart_hsu_port *up;
+
+ /* Make sure this is not the internal dma controller */
+ if (priv && (pdev->device != 0x081E)) {
+ up = priv;
+ uart_suspend_port(&serial_hsu_reg, &up->port);
+ }
+
+ pci_save_state(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+ return 0;
+}
+
+static int serial_hsu_resume(struct pci_dev *pdev)
+{
+ void *priv = pci_get_drvdata(pdev);
+ struct uart_hsu_port *up;
+ int ret;
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ dev_warn(&pdev->dev,
+ "HSU: can't re-enable device, try to continue\n");
+
+ if (priv && (pdev->device != 0x081E)) {
+ up = priv;
+ uart_resume_port(&serial_hsu_reg, &up->port);
+ }
+ return 0;
+}
+#else
+#define serial_hsu_suspend NULL
+#define serial_hsu_resume NULL
+#endif
+
+/* temp global pointer before we settle down on using one or four PCI dev */
+static struct hsu_port *phsu;
+
+static int serial_hsu_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct uart_hsu_port *uport;
+ int index, ret;
+
+ printk(KERN_INFO "HSU: found PCI Serial controller(ID: %04x:%04x)\n",
+ pdev->vendor, pdev->device);
+
+ switch (pdev->device) {
+ case 0x081B:
+ index = 0;
+ break;
+ case 0x081C:
+ index = 1;
+ break;
+ case 0x081D:
+ index = 2;
+ break;
+ case 0x081E:
+ /* internal DMA controller */
+ index = 3;
+ break;
+ default:
+ dev_err(&pdev->dev, "HSU: out of index!");
+ return -ENODEV;
+ }
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ if (index == 3) {
+ /* DMA controller */
+ ret = request_irq(pdev->irq, dma_irq, 0, "hsu_dma", phsu);
+ if (ret) {
+ dev_err(&pdev->dev, "can not get IRQ\n");
+ goto err_disable;
+ }
+ pci_set_drvdata(pdev, phsu);
+ } else {
+ /* UART port 0~2 */
+ uport = &phsu->port[index];
+ uport->port.irq = pdev->irq;
+ uport->port.dev = &pdev->dev;
+ uport->dev = &pdev->dev;
+
+ ret = request_irq(pdev->irq, port_irq, 0, uport->name, uport);
+ if (ret) {
+ dev_err(&pdev->dev, "can not get IRQ\n");
+ goto err_disable;
+ }
+ uart_add_one_port(&serial_hsu_reg, &uport->port);
+
+#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE
+ if (index == 2) {
+ register_console(&serial_hsu_console);
+ uport->port.cons = &serial_hsu_console;
+ }
+#endif
+ pci_set_drvdata(pdev, uport);
+ }
+
+ return 0;
+
+err_disable:
+ pci_disable_device(pdev);
+ return ret;
+}
+
+static void hsu_dma_rx_timeout(unsigned long data)
+{
+ struct hsu_dma_chan *chan = (void *)data;
+ struct uart_hsu_port *up = chan->uport;
+ struct hsu_dma_buffer *dbuf = &up->rxbuf;
+ int count = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&up->port.lock, flags);
+
+ count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr;
+
+ if (!count) {
+ mod_timer(&chan->rx_timer, jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ);
+ goto exit;
+ }
+
+ hsu_dma_rx(up, 0);
+exit:
+ spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void hsu_global_init(void)
+{
+ struct hsu_port *hsu;
+ struct uart_hsu_port *uport;
+ struct hsu_dma_chan *dchan;
+ int i, ret;
+
+ hsu = kzalloc(sizeof(struct hsu_port), GFP_KERNEL);
+ if (!hsu)
+ return;
+
+ /* Get basic io resource and map it */
+ hsu->paddr = 0xffa28000;
+ hsu->iolen = 0x1000;
+
+ if (!(request_mem_region(hsu->paddr, hsu->iolen, "HSU global")))
+ pr_warning("HSU: error in request mem region\n");
+
+ hsu->reg = ioremap_nocache((unsigned long)hsu->paddr, hsu->iolen);
+ if (!hsu->reg) {
+ pr_err("HSU: error in ioremap\n");
+ ret = -ENOMEM;
+ goto err_free_region;
+ }
+
+ /* Initialise the 3 UART ports */
+ uport = hsu->port;
+ for (i = 0; i < 3; i++) {
+ uport->port.type = PORT_MFD;
+ uport->port.iotype = UPIO_MEM;
+ uport->port.mapbase = (resource_size_t)hsu->paddr
+ + HSU_PORT_REG_OFFSET
+ + i * HSU_PORT_REG_LENGTH;
+ uport->port.membase = hsu->reg + HSU_PORT_REG_OFFSET
+ + i * HSU_PORT_REG_LENGTH;
+
+ sprintf(uport->name, "hsu_port%d", i);
+ uport->port.fifosize = 64;
+ uport->port.ops = &serial_hsu_pops;
+ uport->port.line = i;
+ uport->port.flags = UPF_IOREMAP;
+ /* set the scalable maxim support rate to 2746800 bps */
+ uport->port.uartclk = 115200 * 24 * 16;
+
+ uport->running = 0;
+ uport->txc = &hsu->chans[i * 2];
+ uport->rxc = &hsu->chans[i * 2 + 1];
+
+ serial_hsu_ports[i] = uport;
+ uport->index = i;
+ uport++;
+ }
+
+ /* Initialise 6 dma channels */
+ dchan = hsu->chans;
+ for (i = 0; i < 6; i++) {
+ dchan->id = i;
+ dchan->dirt = (i & 0x1) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ dchan->uport = &hsu->port[i/2];
+ dchan->reg = hsu->reg + HSU_DMA_CHANS_REG_OFFSET +
+ i * HSU_DMA_CHANS_REG_LENGTH;
+
+ /* Work around for RX */
+ if (dchan->dirt == DMA_FROM_DEVICE) {
+ init_timer(&dchan->rx_timer);
+ dchan->rx_timer.function = hsu_dma_rx_timeout;
+ dchan->rx_timer.data = (unsigned long)dchan;
+ }
+ dchan++;
+ }
+
+ phsu = hsu;
+
+ hsu_debugfs_init(hsu);
+ return;
+
+err_free_region:
+ release_mem_region(hsu->paddr, hsu->iolen);
+ kfree(hsu);
+ return;
+}
+
+static void serial_hsu_remove(struct pci_dev *pdev)
+{
+ struct hsu_port *hsu;
+ int i;
+
+ hsu = pci_get_drvdata(pdev);
+ if (!hsu)
+ return;
+
+ for (i = 0; i < 3; i++)
+ uart_remove_one_port(&serial_hsu_reg, &hsu->port[i].port);
+
+ pci_set_drvdata(pdev, NULL);
+ free_irq(hsu->irq, hsu);
+ pci_disable_device(pdev);
+}
+
+/* First 3 are UART ports, and the 4th is the DMA */
+static const struct pci_device_id pci_ids[] __devinitdata = {
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081B) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081C) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081D) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081E) },
+ {},
+};
+
+static struct pci_driver hsu_pci_driver = {
+ .name = "HSU serial",
+ .id_table = pci_ids,
+ .probe = serial_hsu_probe,
+ .remove = __devexit_p(serial_hsu_remove),
+ .suspend = serial_hsu_suspend,
+ .resume = serial_hsu_resume,
+};
+
+static int __init hsu_pci_init(void)
+{
+ int ret;
+
+ hsu_global_init();
+
+ ret = uart_register_driver(&serial_hsu_reg);
+ if (ret)
+ return ret;
+
+ return pci_register_driver(&hsu_pci_driver);
+}
+
+static void __exit hsu_pci_exit(void)
+{
+ pci_unregister_driver(&hsu_pci_driver);
+ uart_unregister_driver(&serial_hsu_reg);
+
+ hsu_debugfs_remove(phsu);
+
+ kfree(phsu);
+}
+
+module_init(hsu_pci_init);
+module_exit(hsu_pci_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:medfield-hsu");
--- /dev/null
+/*
+ * max3110.c - spi uart protocol driver for Maxim 3110 on Moorestown
+ *
+ * Copyright (C) Intel 2008 Feng Tang <feng.tang@intel.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,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * Note:
+ * 1. From Max3110 spec, the Rx FIFO has 8 words, while the Tx FIFO only has
+ * 1 word. If SPI master controller doesn't support sclk frequency change,
+ * then the char need be sent out one by one with some delay
+ *
+ * 2. Currently only RX availabe interrrupt is used, no need for waiting TXE
+ * interrupt for a low speed UART device
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <asm/atomic.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/dw_spi.h>
+
+#include "mrst_max3110.h"
+
+#define PR_FMT "mrst_max3110: "
+
+#define UART_TX_NEEDED 1
+#define CON_TX_NEEDED 2
+#define BIT_IRQ_PENDING 3
+
+struct uart_max3110 {
+ struct uart_port port;
+ struct spi_device *spi;
+ char *name;
+
+ wait_queue_head_t wq;
+ struct task_struct *main_thread;
+ struct task_struct *read_thread;
+ struct mutex thread_mutex;;
+
+ u32 baud;
+ u16 cur_conf;
+ u8 clock;
+ u8 parity, word_7bits;
+
+ unsigned long uart_flags;
+
+ /* console related */
+ struct circ_buf con_xmit;
+
+ /* irq related */
+ u16 irq;
+};
+
+/* global data structure, may need be removed */
+struct uart_max3110 *pmax;
+static inline void receive_char(struct uart_max3110 *max, u8 ch);
+static void receive_chars(struct uart_max3110 *max,
+ unsigned char *str, int len);
+static int max3110_read_multi(struct uart_max3110 *max, int len, u8 *buf);
+static void max3110_console_receive(struct uart_max3110 *max);
+
+int max3110_write_then_read(struct uart_max3110 *max,
+ const u8 *txbuf, u8 *rxbuf, unsigned len, int always_fast)
+{
+ struct spi_device *spi = max->spi;
+ struct spi_message message;
+ struct spi_transfer x;
+ int ret;
+
+ if (!txbuf || !rxbuf)
+ return -EINVAL;
+
+ spi_message_init(&message);
+ memset(&x, 0, sizeof x);
+ x.len = len;
+ x.tx_buf = txbuf;
+ x.rx_buf = rxbuf;
+ spi_message_add_tail(&x, &message);
+
+ if (always_fast)
+ x.speed_hz = 3125000;
+ else if (max->baud)
+ x.speed_hz = max->baud;
+
+ /* Do the i/o */
+ ret = spi_sync(spi, &message);
+ return ret;
+}
+
+/* Write a u16 to the device, and return one u16 read back */
+int max3110_out(struct uart_max3110 *max, const u16 out)
+{
+ u16 tmp;
+ int ret;
+
+ ret = max3110_write_then_read(max, (u8 *)&out, (u8 *)&tmp, 2, 1);
+ if (ret)
+ return ret;
+
+ /* If some valid data is read back */
+ if (tmp & MAX3110_READ_DATA_AVAILABLE)
+ receive_char(max, (tmp & 0xff));
+
+ return ret;
+}
+
+#define MAX_READ_LEN 20
+/*
+ * This is usually used to read data from SPIC RX FIFO, which doesn't
+ * need any delay like flushing character out. It returns how many
+ * valide bytes are read back
+ */
+static int max3110_read_multi(struct uart_max3110 *max, int len, u8 *buf)
+{
+ u16 out[MAX_READ_LEN], in[MAX_READ_LEN];
+ u8 *pbuf, valid_str[MAX_READ_LEN];
+ int i, j, bytelen;
+
+ if (len > MAX_READ_LEN) {
+ pr_err(PR_FMT "read len %d is too large\n", len);
+ return 0;
+ }
+
+ bytelen = len * 2;
+ memset(out, 0, bytelen);
+ memset(in, 0, bytelen);
+
+ if (max3110_write_then_read(max, (u8 *)out, (u8 *)in, bytelen, 1))
+ return 0;
+
+ /* If caller don't provide a buffer, then handle received char */
+ pbuf = buf ? buf : valid_str;
+
+ for (i = 0, j = 0; i < len; i++) {
+ if (in[i] & MAX3110_READ_DATA_AVAILABLE)
+ pbuf[j++] = (u8)(in[i] & 0xff);
+ }
+
+ if (j && (pbuf == valid_str))
+ receive_chars(max, valid_str, j);
+
+ return j;
+}
+
+static void serial_m3110_con_putchar(struct uart_port *port, int ch)
+{
+ struct uart_max3110 *max =
+ container_of(port, struct uart_max3110, port);
+ struct circ_buf *xmit = &max->con_xmit;
+
+ if (uart_circ_chars_free(xmit)) {
+ xmit->buf[xmit->head] = (char)ch;
+ xmit->head = (xmit->head + 1) & (PAGE_SIZE - 1);
+ }
+
+
+ if (!test_and_set_bit(CON_TX_NEEDED, &max->uart_flags))
+ wake_up_process(max->main_thread);
+}
+
+/*
+ * Print a string to the serial port trying not to disturb
+ * any possible real use of the port...
+ *
+ * The console_lock must be held when we get here.
+ */
+static void serial_m3110_con_write(struct console *co,
+ const char *s, unsigned int count)
+{
+ if (!pmax)
+ return;
+
+ uart_console_write(&pmax->port, s, count, serial_m3110_con_putchar);
+}
+
+static int __init
+serial_m3110_con_setup(struct console *co, char *options)
+{
+ struct uart_max3110 *max = pmax;
+ int baud = 115200;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ pr_info(PR_FMT "setting up console\n");
+
+ if (!max) {
+ pr_err(PR_FMT "pmax is NULL, return");
+ return -ENODEV;
+ }
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ return uart_set_options(&max->port, co, baud, parity, bits, flow);
+}
+
+static struct tty_driver *serial_m3110_con_device(struct console *co,
+ int *index)
+{
+ struct uart_driver *p = co->data;
+ *index = co->index;
+ return p->tty_driver;
+}
+
+static struct uart_driver serial_m3110_reg;
+static struct console serial_m3110_console = {
+ .name = "ttyS",
+ .write = serial_m3110_con_write,
+ .device = serial_m3110_con_device,
+ .setup = serial_m3110_con_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &serial_m3110_reg,
+};
+
+#define MRST_CONSOLE (&serial_m3110_console)
+
+static unsigned int serial_m3110_tx_empty(struct uart_port *port)
+{
+ return 1;
+}
+
+static void serial_m3110_stop_tx(struct uart_port *port)
+{
+ return;
+}
+
+/* stop_rx will be called in spin_lock env */
+static void serial_m3110_stop_rx(struct uart_port *port)
+{
+ return;
+}
+
+#define WORDS_PER_XFER 128
+static inline void send_circ_buf(struct uart_max3110 *max,
+ struct circ_buf *xmit)
+{
+ int len, left = 0;
+ u16 obuf[WORDS_PER_XFER], ibuf[WORDS_PER_XFER];
+ u8 valid_str[WORDS_PER_XFER];
+ int i, j;
+
+ while (!uart_circ_empty(xmit)) {
+ left = uart_circ_chars_pending(xmit);
+ while (left) {
+ len = (left >= WORDS_PER_XFER) ? WORDS_PER_XFER : left;
+
+ memset(obuf, 0, len * 2);
+ memset(ibuf, 0, len * 2);
+ for (i = 0; i < len; i++) {
+ obuf[i] = (u8)xmit->buf[xmit->tail] | WD_TAG;
+ xmit->tail = (xmit->tail + 1) &
+ (UART_XMIT_SIZE - 1);
+ }
+ max3110_write_then_read(max, (u8 *)obuf,
+ (u8 *)ibuf, len * 2, 0);
+
+ for (i = 0, j = 0; i < len; i++) {
+ if (ibuf[i] & MAX3110_READ_DATA_AVAILABLE)
+ valid_str[j++] = (u8)(ibuf[i] & 0xff);
+ }
+
+ if (j)
+ receive_chars(max, valid_str, j);
+
+ max->port.icount.tx += len;
+ left -= len;
+ }
+ }
+}
+
+static void transmit_char(struct uart_max3110 *max)
+{
+ struct uart_port *port = &max->port;
+ struct circ_buf *xmit = &port->state->xmit;
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+ return;
+
+ send_circ_buf(max, xmit);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ if (uart_circ_empty(xmit))
+ serial_m3110_stop_tx(port);
+}
+
+/* This will be called by uart_write() and tty_write, can't
+ * go to sleep */
+static void serial_m3110_start_tx(struct uart_port *port)
+{
+ struct uart_max3110 *max =
+ container_of(port, struct uart_max3110, port);
+
+ if (!test_and_set_bit(UART_TX_NEEDED, &max->uart_flags))
+ wake_up_process(max->main_thread);
+}
+
+static void receive_chars(struct uart_max3110 *max, unsigned char *str, int len)
+{
+ struct uart_port *port = &max->port;
+ struct tty_struct *tty;
+ int usable;
+
+ /* If uart is not opened, just return */
+ if (!port->state)
+ return;
+
+ tty = port->state->port.tty;
+ if (!tty)
+ return; /* receive some char before the tty is opened */
+
+ while (len) {
+ usable = tty_buffer_request_room(tty, len);
+ if (usable) {
+ tty_insert_flip_string(tty, str, usable);
+ str += usable;
+ port->icount.rx += usable;
+ tty_flip_buffer_push(tty);
+ }
+ len -= usable;
+ }
+}
+
+static inline void receive_char(struct uart_max3110 *max, u8 ch)
+{
+ receive_chars(max, &ch, 1);
+}
+
+static void max3110_console_receive(struct uart_max3110 *max)
+{
+ int loop = 1, num, total = 0;
+ u8 recv_buf[512], *pbuf;
+
+ pbuf = recv_buf;
+ do {
+ num = max3110_read_multi(max, 8, pbuf);
+
+ if (num) {
+ loop = 10;
+ pbuf += num;
+ total += num;
+
+ if (total >= 500) {
+ receive_chars(max, recv_buf, total);
+ pbuf = recv_buf;
+ total = 0;
+ }
+ }
+ } while (--loop);
+
+ if (total)
+ receive_chars(max, recv_buf, total);
+}
+
+static int max3110_main_thread(void *_max)
+{
+ struct uart_max3110 *max = _max;
+ wait_queue_head_t *wq = &max->wq;
+ int ret = 0;
+ struct circ_buf *xmit = &max->con_xmit;
+
+ init_waitqueue_head(wq);
+ pr_info(PR_FMT "start main thread\n");
+
+ do {
+ wait_event_interruptible(*wq, max->uart_flags || kthread_should_stop());
+
+ mutex_lock(&max->thread_mutex);
+
+ if (test_and_clear_bit(BIT_IRQ_PENDING, &max->uart_flags))
+ max3110_console_receive(max);
+
+ /* first handle console output */
+ if (test_and_clear_bit(CON_TX_NEEDED, &max->uart_flags))
+ send_circ_buf(max, xmit);
+
+ /* handle uart output */
+ if (test_and_clear_bit(UART_TX_NEEDED, &max->uart_flags))
+ transmit_char(max);
+
+ mutex_unlock(&max->thread_mutex);
+
+ } while (!kthread_should_stop());
+
+ return ret;
+}
+
+#ifdef CONFIG_MRST_MAX3110_IRQ
+static irqreturn_t serial_m3110_irq(int irq, void *dev_id)
+{
+ struct uart_max3110 *max = dev_id;
+
+ /* max3110's irq is a falling edge, not level triggered,
+ * so no need to disable the irq */
+ if (!test_and_set_bit(BIT_IRQ_PENDING, &max->uart_flags))
+ wake_up_process(max->main_thread);
+
+ return IRQ_HANDLED;
+}
+#else
+/* if don't use RX IRQ, then need a thread to polling read */
+static int max3110_read_thread(void *_max)
+{
+ struct uart_max3110 *max = _max;
+
+ pr_info(PR_FMT "start read thread\n");
+ do {
+ mutex_lock(&max->thread_mutex);
+ max3110_console_receive(max);
+ mutex_unlock(&max->thread_mutex);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ / 20);
+ } while (!kthread_should_stop());
+
+ return 0;
+}
+#endif
+
+static int serial_m3110_startup(struct uart_port *port)
+{
+ struct uart_max3110 *max =
+ container_of(port, struct uart_max3110, port);
+ u16 config = 0;
+ int ret = 0;
+
+ if (port->line != 0)
+ pr_err(PR_FMT "uart port startup failed\n");
+
+ /* firstly disable all IRQ and config it to 115200, 8n1 */
+ config = WC_TAG | WC_FIFO_ENABLE
+ | WC_1_STOPBITS
+ | WC_8BIT_WORD
+ | WC_BAUD_DR2;
+ ret = max3110_out(max, config);
+
+ /* as we use thread to handle tx/rx, need set low latency */
+ port->state->port.tty->low_latency = 1;
+
+#ifdef CONFIG_MRST_MAX3110_IRQ
+ ret = request_irq(max->irq, serial_m3110_irq,
+ IRQ_TYPE_EDGE_FALLING, "max3110", max);
+ if (ret)
+ return ret;
+
+ /* enable RX IRQ only */
+ config |= WC_RXA_IRQ_ENABLE;
+ max3110_out(max, config);
+#else
+ /* if IRQ is disabled, start a read thread for input data */
+ max->read_thread =
+ kthread_run(max3110_read_thread, max, "max3110_read");
+#endif
+
+ max->cur_conf = config;
+ return 0;
+}
+
+static void serial_m3110_shutdown(struct uart_port *port)
+{
+ struct uart_max3110 *max =
+ container_of(port, struct uart_max3110, port);
+ u16 config;
+
+ if (max->read_thread) {
+ kthread_stop(max->read_thread);
+ max->read_thread = NULL;
+ }
+
+#ifdef CONFIG_MRST_MAX3110_IRQ
+ free_irq(max->irq, max);
+#endif
+
+ /* Disable interrupts from this port */
+ config = WC_TAG | WC_SW_SHDI;
+ max3110_out(max, config);
+}
+
+static void serial_m3110_release_port(struct uart_port *port)
+{
+}
+
+static int serial_m3110_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+static void serial_m3110_config_port(struct uart_port *port, int flags)
+{
+ /* give it fake type */
+ port->type = PORT_PXA;
+}
+
+static int
+serial_m3110_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ /* we don't want the core code to modify any port params */
+ return -EINVAL;
+}
+
+
+static const char *serial_m3110_type(struct uart_port *port)
+{
+ struct uart_max3110 *max =
+ container_of(port, struct uart_max3110, port);
+ return max->name;
+}
+
+static void
+serial_m3110_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct uart_max3110 *max =
+ container_of(port, struct uart_max3110, port);
+ unsigned char cval;
+ unsigned int baud, parity = 0;
+ int clk_div = -1;
+ u16 new_conf = max->cur_conf;
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS7:
+ cval = UART_LCR_WLEN7;
+ new_conf |= WC_7BIT_WORD;
+ break;
+ default:
+ case CS8:
+ cval = UART_LCR_WLEN8;
+ new_conf |= WC_8BIT_WORD;
+ break;
+ }
+
+ baud = uart_get_baud_rate(port, termios, old, 0, 230400);
+
+ /* first calc the div for 1.8MHZ clock case */
+ switch (baud) {
+ case 300:
+ clk_div = WC_BAUD_DR384;
+ break;
+ case 600:
+ clk_div = WC_BAUD_DR192;
+ break;
+ case 1200:
+ clk_div = WC_BAUD_DR96;
+ break;
+ case 2400:
+ clk_div = WC_BAUD_DR48;
+ break;
+ case 4800:
+ clk_div = WC_BAUD_DR24;
+ break;
+ case 9600:
+ clk_div = WC_BAUD_DR12;
+ break;
+ case 19200:
+ clk_div = WC_BAUD_DR6;
+ break;
+ case 38400:
+ clk_div = WC_BAUD_DR3;
+ break;
+ case 57600:
+ clk_div = WC_BAUD_DR2;
+ break;
+ case 115200:
+ clk_div = WC_BAUD_DR1;
+ break;
+ case 230400:
+ if (max->clock & MAX3110_HIGH_CLK)
+ break;
+ default:
+ /* pick the previous baud rate */
+ baud = max->baud;
+ clk_div = max->cur_conf & WC_BAUD_DIV_MASK;
+ tty_termios_encode_baud_rate(termios, baud, baud);
+ }
+
+ if (max->clock & MAX3110_HIGH_CLK) {
+ clk_div += 1;
+ /* high clk version max3110 doesn't support B300 */
+ if (baud == 300)
+ baud = 600;
+ if (baud == 230400)
+ clk_div = WC_BAUD_DR1;
+ tty_termios_encode_baud_rate(termios, baud, baud);
+ }
+
+ new_conf = (new_conf & ~WC_BAUD_DIV_MASK) | clk_div;
+ if (termios->c_cflag & CSTOPB)
+ new_conf |= WC_2_STOPBITS;
+ else
+ new_conf &= ~WC_2_STOPBITS;
+
+ if (termios->c_cflag & PARENB) {
+ new_conf |= WC_PARITY_ENABLE;
+ parity |= UART_LCR_PARITY;
+ } else
+ new_conf &= ~WC_PARITY_ENABLE;
+
+ if (!(termios->c_cflag & PARODD))
+ parity |= UART_LCR_EPAR;
+ max->parity = parity;
+
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ new_conf |= WC_TAG;
+ if (new_conf != max->cur_conf) {
+ max3110_out(max, new_conf);
+ max->cur_conf = new_conf;
+ max->baud = baud;
+ }
+}
+
+/* don't handle hw handshaking */
+static unsigned int serial_m3110_get_mctrl(struct uart_port *port)
+{
+ return TIOCM_DSR | TIOCM_CAR | TIOCM_DSR;
+}
+
+static void serial_m3110_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static void serial_m3110_break_ctl(struct uart_port *port, int break_state)
+{
+}
+
+static void serial_m3110_pm(struct uart_port *port, unsigned int state,
+ unsigned int oldstate)
+{
+}
+
+static void serial_m3110_enable_ms(struct uart_port *port)
+{
+}
+
+struct uart_ops serial_m3110_ops = {
+ .tx_empty = serial_m3110_tx_empty,
+ .set_mctrl = serial_m3110_set_mctrl,
+ .get_mctrl = serial_m3110_get_mctrl,
+ .stop_tx = serial_m3110_stop_tx,
+ .start_tx = serial_m3110_start_tx,
+ .stop_rx = serial_m3110_stop_rx,
+ .enable_ms = serial_m3110_enable_ms,
+ .break_ctl = serial_m3110_break_ctl,
+ .startup = serial_m3110_startup,
+ .shutdown = serial_m3110_shutdown,
+ .set_termios = serial_m3110_set_termios, /* must have */
+ .pm = serial_m3110_pm,
+ .type = serial_m3110_type,
+ .release_port = serial_m3110_release_port,
+ .request_port = serial_m3110_request_port,
+ .config_port = serial_m3110_config_port,
+ .verify_port = serial_m3110_verify_port,
+};
+
+static struct uart_driver serial_m3110_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = "MRST serial",
+ .dev_name = "ttyS",
+ .major = TTY_MAJOR,
+ .minor = 64,
+ .nr = 1,
+ .cons = MRST_CONSOLE,
+};
+
+static int serial_m3110_suspend(struct spi_device *spi, pm_message_t state)
+{
+ return 0;
+}
+
+static int serial_m3110_resume(struct spi_device *spi)
+{
+ return 0;
+}
+
+static struct dw_spi_chip spi0_uart = {
+ .poll_mode = 1,
+ .enable_dma = 0,
+ .type = SPI_FRF_SPI,
+};
+
+static int serial_m3110_probe(struct spi_device *spi)
+{
+ struct uart_max3110 *max;
+ int ret;
+ unsigned char *buffer;
+ u16 res;
+ max = kzalloc(sizeof(*max), GFP_KERNEL);
+ if (!max)
+ return -ENOMEM;
+
+ /* set spi info */
+ spi->mode = SPI_MODE_0;
+ spi->bits_per_word = 16;
+ max->clock = MAX3110_HIGH_CLK;
+ spi->controller_data = &spi0_uart;
+
+ spi_setup(spi);
+
+ max->port.type = PORT_PXA; /* need apply for a max3110 type */
+ max->port.fifosize = 2; /* only have 16b buffer */
+ max->port.ops = &serial_m3110_ops;
+ max->port.line = 0;
+ max->port.dev = &spi->dev;
+ max->port.uartclk = 115200;
+
+ max->spi = spi;
+ max->name = spi->modalias; /* use spi name as the name */
+ max->irq = (u16)spi->irq;
+
+ mutex_init(&max->thread_mutex);
+
+ max->word_7bits = 0;
+ max->parity = 0;
+ max->baud = 0;
+
+ max->cur_conf = 0;
+ max->uart_flags = 0;
+
+ /* Check if reading configuration register returns something sane */
+
+ res = RC_TAG;
+ ret = max3110_write_then_read(max, (u8 *)&res, (u8 *)&res, 2, 0);
+ if (ret < 0 || res == 0 || res == 0xffff) {
+ printk(KERN_ERR "MAX3111 deemed not present (conf reg %04x)",
+ res);
+ ret = -ENODEV;
+ goto err_get_page;
+ }
+ buffer = (unsigned char *)__get_free_page(GFP_KERNEL);
+ if (!buffer) {
+ ret = -ENOMEM;
+ goto err_get_page;
+ }
+ max->con_xmit.buf = (unsigned char *)buffer;
+ max->con_xmit.head = max->con_xmit.tail = 0;
+
+ max->main_thread = kthread_run(max3110_main_thread,
+ max, "max3110_main");
+ if (IS_ERR(max->main_thread)) {
+ ret = PTR_ERR(max->main_thread);
+ goto err_kthread;
+ }
+
+ pmax = max;
+ /* give membase a psudo value to pass serial_core's check */
+ max->port.membase = (void *)0xff110000;
+ uart_add_one_port(&serial_m3110_reg, &max->port);
+
+ return 0;
+
+err_kthread:
+ free_page((unsigned long)buffer);
+err_get_page:
+ pmax = NULL;
+ kfree(max);
+ return ret;
+}
+
+static int max3110_remove(struct spi_device *dev)
+{
+ struct uart_max3110 *max = pmax;
+
+ if (!pmax)
+ return 0;
+
+ pmax = NULL;
+ uart_remove_one_port(&serial_m3110_reg, &max->port);
+
+ free_page((unsigned long)max->con_xmit.buf);
+
+ if (max->main_thread)
+ kthread_stop(max->main_thread);
+
+ kfree(max);
+ return 0;
+}
+
+static struct spi_driver uart_max3110_driver = {
+ .driver = {
+ .name = "spi_max3111",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = serial_m3110_probe,
+ .remove = __devexit_p(max3110_remove),
+ .suspend = serial_m3110_suspend,
+ .resume = serial_m3110_resume,
+};
+
+
+int __init serial_m3110_init(void)
+{
+ int ret = 0;
+
+ ret = uart_register_driver(&serial_m3110_reg);
+ if (ret)
+ return ret;
+
+ ret = spi_register_driver(&uart_max3110_driver);
+ if (ret)
+ uart_unregister_driver(&serial_m3110_reg);
+
+ return ret;
+}
+
+void __exit serial_m3110_exit(void)
+{
+ spi_unregister_driver(&uart_max3110_driver);
+ uart_unregister_driver(&serial_m3110_reg);
+}
+
+module_init(serial_m3110_init);
+module_exit(serial_m3110_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("max3110-uart");
--- /dev/null
+#ifndef _MRST_MAX3110_H
+#define _MRST_MAX3110_H
+
+#define MAX3110_HIGH_CLK 0x1 /* 3.6864 MHZ */
+#define MAX3110_LOW_CLK 0x0 /* 1.8432 MHZ */
+
+/* status bits for all 4 MAX3110 operate modes */
+#define MAX3110_READ_DATA_AVAILABLE (1 << 15)
+#define MAX3110_WRITE_BUF_EMPTY (1 << 14)
+
+#define WC_TAG (3 << 14)
+#define RC_TAG (1 << 14)
+#define WD_TAG (2 << 14)
+#define RD_TAG (0 << 14)
+
+/* bits def for write configuration */
+#define WC_FIFO_ENABLE_MASK (1 << 13)
+#define WC_FIFO_ENABLE (0 << 13)
+
+#define WC_SW_SHDI (1 << 12)
+
+#define WC_IRQ_MASK (0xF << 8)
+#define WC_TXE_IRQ_ENABLE (1 << 11) /* TX empty irq */
+#define WC_RXA_IRQ_ENABLE (1 << 10) /* RX availabe irq */
+#define WC_PAR_HIGH_IRQ_ENABLE (1 << 9)
+#define WC_REC_ACT_IRQ_ENABLE (1 << 8)
+
+#define WC_IRDA_ENABLE (1 << 7)
+
+#define WC_STOPBITS_MASK (1 << 6)
+#define WC_2_STOPBITS (1 << 6)
+#define WC_1_STOPBITS (0 << 6)
+
+#define WC_PARITY_ENABLE_MASK (1 << 5)
+#define WC_PARITY_ENABLE (1 << 5)
+
+#define WC_WORDLEN_MASK (1 << 4)
+#define WC_7BIT_WORD (1 << 4)
+#define WC_8BIT_WORD (0 << 4)
+
+#define WC_BAUD_DIV_MASK (0xF)
+#define WC_BAUD_DR1 (0x0)
+#define WC_BAUD_DR2 (0x1)
+#define WC_BAUD_DR4 (0x2)
+#define WC_BAUD_DR8 (0x3)
+#define WC_BAUD_DR16 (0x4)
+#define WC_BAUD_DR32 (0x5)
+#define WC_BAUD_DR64 (0x6)
+#define WC_BAUD_DR128 (0x7)
+#define WC_BAUD_DR3 (0x8)
+#define WC_BAUD_DR6 (0x9)
+#define WC_BAUD_DR12 (0xA)
+#define WC_BAUD_DR24 (0xB)
+#define WC_BAUD_DR48 (0xC)
+#define WC_BAUD_DR96 (0xD)
+#define WC_BAUD_DR192 (0xE)
+#define WC_BAUD_DR384 (0xF)
+
+#endif
static int s5pv210_serial_setsource(struct uart_port *port,
struct s3c24xx_uart_clksrc *clk)
{
+ struct s3c2410_uartcfg *cfg = port->dev->platform_data;
unsigned long ucon = rd_regl(port, S3C2410_UCON);
+ if ((cfg->clocks_size) == 1)
+ return 0;
+
if (strcmp(clk->name, "pclk") == 0)
ucon &= ~S5PV210_UCON_CLKMASK;
else if (strcmp(clk->name, "uclk1") == 0)
static int s5pv210_serial_getsource(struct uart_port *port,
struct s3c24xx_uart_clksrc *clk)
{
+ struct s3c2410_uartcfg *cfg = port->dev->platform_data;
u32 ucon = rd_regl(port, S3C2410_UCON);
clk->divisor = 1;
+ if ((cfg->clocks_size) == 1)
+ return 0;
+
switch (ucon & S5PV210_UCON_CLKMASK) {
case S5PV210_UCON_PCLK:
clk->name = "pclk";
if (ourport->info->has_divslot) {
unsigned int div = ourport->baudclk_rate / baud;
- udivslot = udivslot_table[div & 15];
- dbg("udivslot = %04x (div %d)\n", udivslot, div & 15);
+ if (cfg->has_fracval) {
+ udivslot = (div & 15);
+ dbg("fracval = %04x\n", udivslot);
+ } else {
+ udivslot = udivslot_table[div & 15];
+ dbg("udivslot = %04x (div %d)\n", udivslot, div & 15);
+ }
}
switch (termios->c_cflag & CSIZE) {
#define uart_console(port) (0)
#endif
-static void uart_change_speed(struct uart_state *state,
+static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
struct ktermios *old_termios);
-static void uart_wait_until_sent(struct tty_struct *tty, int timeout);
+static void __uart_wait_until_sent(struct uart_port *port, int timeout);
static void uart_change_pm(struct uart_state *state, int pm_state);
/*
* Startup the port. This will be called once per open. All calls
* will be serialised by the per-port mutex.
*/
-static int uart_startup(struct uart_state *state, int init_hw)
+static int uart_startup(struct tty_struct *tty, struct uart_state *state, int init_hw)
{
struct uart_port *uport = state->uart_port;
struct tty_port *port = &state->port;
* once we have successfully opened the port. Also set
* up the tty->alt_speed kludge
*/
- set_bit(TTY_IO_ERROR, &port->tty->flags);
+ set_bit(TTY_IO_ERROR, &tty->flags);
if (uport->type == PORT_UNKNOWN)
return 0;
/*
* Initialise the hardware port settings.
*/
- uart_change_speed(state, NULL);
+ uart_change_speed(tty, state, NULL);
/*
* Setup the RTS and DTR signals once the
* port is open and ready to respond.
*/
- if (port->tty->termios->c_cflag & CBAUD)
+ if (tty->termios->c_cflag & CBAUD)
uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
}
if (port->flags & ASYNC_CTS_FLOW) {
spin_lock_irq(&uport->lock);
if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS))
- port->tty->hw_stopped = 1;
+ tty->hw_stopped = 1;
spin_unlock_irq(&uport->lock);
}
set_bit(ASYNCB_INITIALIZED, &port->flags);
- clear_bit(TTY_IO_ERROR, &port->tty->flags);
+ clear_bit(TTY_IO_ERROR, &tty->flags);
}
if (retval && capable(CAP_SYS_ADMIN))
* DTR is dropped if the hangup on close termio flag is on. Calls to
* uart_shutdown are serialised by the per-port semaphore.
*/
-static void uart_shutdown(struct uart_state *state)
+static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
{
struct uart_port *uport = state->uart_port;
struct tty_port *port = &state->port;
- struct tty_struct *tty = port->tty;
/*
* Set the TTY IO error marker
EXPORT_SYMBOL(uart_get_divisor);
/* FIXME: Consistent locking policy */
-static void
-uart_change_speed(struct uart_state *state, struct ktermios *old_termios)
+static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
+ struct ktermios *old_termios)
{
struct tty_port *port = &state->port;
- struct tty_struct *tty = port->tty;
struct uart_port *uport = state->uart_port;
struct ktermios *termios;
uport->ops->set_termios(uport, termios, old_termios);
}
-static inline int
-__uart_put_char(struct uart_port *port, struct circ_buf *circ, unsigned char c)
+static inline int __uart_put_char(struct uart_port *port,
+ struct circ_buf *circ, unsigned char c)
{
unsigned long flags;
int ret = 0;
uart_start(tty);
}
-static int
-uart_write(struct tty_struct *tty, const unsigned char *buf, int count)
+static int uart_write(struct tty_struct *tty,
+ const unsigned char *buf, int count)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port;
return 0;
}
-static int uart_set_info(struct uart_state *state,
+static int uart_set_info(struct tty_struct *tty, struct uart_state *state,
struct serial_struct __user *newinfo)
{
struct serial_struct new_serial;
* We need to shutdown the serial port at the old
* port/type/irq combination.
*/
- uart_shutdown(state);
+ uart_shutdown(tty, state);
}
if (change_port) {
"is deprecated.\n", current->comm,
tty_name(port->tty, buf));
}
- uart_change_speed(state, NULL);
+ uart_change_speed(tty, state, NULL);
}
} else
- retval = uart_startup(state, 1);
+ retval = uart_startup(tty, state, 1);
exit:
mutex_unlock(&port->mutex);
return retval;
}
-
-/*
- * uart_get_lsr_info - get line status register info.
- * Note: uart_ioctl protects us against hangups.
+/**
+ * uart_get_lsr_info - get line status register info
+ * @tty: tty associated with the UART
+ * @state: UART being queried
+ * @value: returned modem value
+ *
+ * Note: uart_ioctl protects us against hangups.
*/
-static int uart_get_lsr_info(struct uart_state *state,
- unsigned int __user *value)
+static int uart_get_lsr_info(struct tty_struct *tty,
+ struct uart_state *state, unsigned int __user *value)
{
struct uart_port *uport = state->uart_port;
- struct tty_port *port = &state->port;
unsigned int result;
result = uport->ops->tx_empty(uport);
*/
if (uport->x_char ||
((uart_circ_chars_pending(&state->xmit) > 0) &&
- !port->tty->stopped && !port->tty->hw_stopped))
+ !tty->stopped && !tty->hw_stopped))
result &= ~TIOCSER_TEMT;
return put_user(result, value);
return 0;
}
-static int uart_do_autoconfig(struct uart_state *state)
+static int uart_do_autoconfig(struct tty_struct *tty,struct uart_state *state)
{
struct uart_port *uport = state->uart_port;
struct tty_port *port = &state->port;
ret = -EBUSY;
if (tty_port_users(port) == 1) {
- uart_shutdown(state);
+ uart_shutdown(tty, state);
/*
* If we already have a port type configured,
*/
uport->ops->config_port(uport, flags);
- ret = uart_startup(state, 1);
+ ret = uart_startup(tty, state, 1);
}
mutex_unlock(&port->mutex);
return ret;
break;
case TIOCSSERIAL:
- ret = uart_set_info(state, uarg);
+ ret = uart_set_info(tty, state, uarg);
break;
case TIOCSERCONFIG:
- ret = uart_do_autoconfig(state);
+ ret = uart_do_autoconfig(tty, state);
break;
case TIOCSERGWILD: /* obsolete */
*/
switch (cmd) {
case TIOCSERGETLSR: /* Get line status register */
- ret = uart_get_lsr_info(state, uarg);
+ ret = uart_get_lsr_info(tty, state, uarg);
break;
default: {
struct uart_port *uport = state->uart_port;
if (uport->ops->set_ldisc)
- uport->ops->set_ldisc(uport);
+ uport->ops->set_ldisc(uport, tty->termios->c_line);
}
static void uart_set_termios(struct tty_struct *tty,
return;
}
- uart_change_speed(state, old_termios);
+ uart_change_speed(tty, state, old_termios);
/* Handle transition to B0 status */
if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
struct uart_state *state = tty->driver_data;
struct tty_port *port;
struct uart_port *uport;
+ unsigned long flags;
- BUG_ON(!kernel_locked());
+ BUG_ON(!tty_locked());
if (!state)
return;
pr_debug("uart_close(%d) called\n", uport->line);
mutex_lock(&port->mutex);
+ spin_lock_irqsave(&port->lock, flags);
- if (tty_hung_up_p(filp))
+ if (tty_hung_up_p(filp)) {
+ spin_unlock_irqrestore(&port->lock, flags);
goto done;
+ }
if ((tty->count == 1) && (port->count != 1)) {
/*
tty->name, port->count);
port->count = 0;
}
- if (port->count)
+ if (port->count) {
+ spin_unlock_irqrestore(&port->lock, flags);
goto done;
+ }
/*
* Now we wait for the transmit buffer to clear; and we notify
* setting tty->closing.
*/
tty->closing = 1;
+ spin_unlock_irqrestore(&port->lock, flags);
- if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
- tty_wait_until_sent(tty, msecs_to_jiffies(port->closing_wait));
+ if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) {
+ /*
+ * hack: open-coded tty_wait_until_sent to avoid
+ * recursive tty_lock
+ */
+ long timeout = msecs_to_jiffies(port->closing_wait);
+ if (wait_event_interruptible_timeout(tty->write_wait,
+ !tty_chars_in_buffer(tty), timeout) >= 0)
+ __uart_wait_until_sent(uport, timeout);
+ }
/*
* At this point, we stop accepting input. To do this, we
* has completely drained; this is especially
* important if there is a transmit FIFO!
*/
- uart_wait_until_sent(tty, uport->timeout);
+ __uart_wait_until_sent(uport, uport->timeout);
}
- uart_shutdown(state);
+ uart_shutdown(tty, state);
uart_flush_buffer(tty);
tty_ldisc_flush(tty);
- tty->closing = 0;
tty_port_tty_set(port, NULL);
+ spin_lock_irqsave(&port->lock, flags);
+ tty->closing = 0;
if (port->blocked_open) {
+ spin_unlock_irqrestore(&port->lock, flags);
if (port->close_delay)
msleep_interruptible(port->close_delay);
+ spin_lock_irqsave(&port->lock, flags);
} else if (!uart_console(uport)) {
+ spin_unlock_irqrestore(&port->lock, flags);
uart_change_pm(state, 3);
+ spin_lock_irqsave(&port->lock, flags);
}
/*
* Wake up anyone trying to open this port.
*/
clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
+ spin_unlock_irqrestore(&port->lock, flags);
wake_up_interruptible(&port->open_wait);
done:
mutex_unlock(&port->mutex);
}
-static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
+static void __uart_wait_until_sent(struct uart_port *port, int timeout)
{
- struct uart_state *state = tty->driver_data;
- struct uart_port *port = state->uart_port;
unsigned long char_time, expire;
if (port->type == PORT_UNKNOWN || port->fifosize == 0)
return;
- lock_kernel();
-
/*
* Set the check interval to be 1/5 of the estimated time to
* send a single character, and make it at least 1. The check
break;
}
set_current_state(TASK_RUNNING); /* might not be needed */
- unlock_kernel();
+}
+
+static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+ struct uart_state *state = tty->driver_data;
+ struct uart_port *port = state->uart_port;
+
+ tty_lock();
+ __uart_wait_until_sent(port, timeout);
+ tty_unlock();
}
/*
{
struct uart_state *state = tty->driver_data;
struct tty_port *port = &state->port;
+ unsigned long flags;
- BUG_ON(!kernel_locked());
+ BUG_ON(!tty_locked());
pr_debug("uart_hangup(%d)\n", state->uart_port->line);
mutex_lock(&port->mutex);
if (port->flags & ASYNC_NORMAL_ACTIVE) {
uart_flush_buffer(tty);
- uart_shutdown(state);
+ uart_shutdown(tty, state);
+ spin_lock_irqsave(&port->lock, flags);
port->count = 0;
clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
+ spin_unlock_irqrestore(&port->lock, flags);
tty_port_tty_set(port, NULL);
wake_up_interruptible(&port->open_wait);
wake_up_interruptible(&port->delta_msr_wait);
mutex_unlock(&port->mutex);
}
-/*
- * Copy across the serial console cflag setting into the termios settings
- * for the initial open of the port. This allows continuity between the
- * kernel settings, and the settings init adopts when it opens the port
- * for the first time.
+/**
+ * uart_update_termios - update the terminal hw settings
+ * @tty: tty associated with UART
+ * @state: UART to update
+ *
+ * Copy across the serial console cflag setting into the termios settings
+ * for the initial open of the port. This allows continuity between the
+ * kernel settings, and the settings init adopts when it opens the port
+ * for the first time.
*/
-static void uart_update_termios(struct uart_state *state)
+static void uart_update_termios(struct tty_struct *tty,
+ struct uart_state *state)
{
- struct tty_struct *tty = state->port.tty;
struct uart_port *port = state->uart_port;
if (uart_console(port) && port->cons->cflag) {
/*
* Make termios settings take effect.
*/
- uart_change_speed(state, NULL);
+ uart_change_speed(tty, state, NULL);
/*
* And finally enable the RTS and DTR signals.
}
}
-/*
- * Block the open until the port is ready. We must be called with
- * the per-port semaphore held.
- */
-static int
-uart_block_til_ready(struct file *filp, struct uart_state *state)
+static int uart_carrier_raised(struct tty_port *port)
{
- DECLARE_WAITQUEUE(wait, current);
+ struct uart_state *state = container_of(port, struct uart_state, port);
struct uart_port *uport = state->uart_port;
- struct tty_port *port = &state->port;
- unsigned int mctrl;
-
- port->blocked_open++;
- port->count--;
-
- add_wait_queue(&port->open_wait, &wait);
- while (1) {
- set_current_state(TASK_INTERRUPTIBLE);
-
- /*
- * If we have been hung up, tell userspace/restart open.
- */
- if (tty_hung_up_p(filp) || port->tty == NULL)
- break;
-
- /*
- * If the port has been closed, tell userspace/restart open.
- */
- if (!(port->flags & ASYNC_INITIALIZED))
- break;
+ int mctrl;
+ spin_lock_irq(&uport->lock);
+ uport->ops->enable_ms(uport);
+ mctrl = uport->ops->get_mctrl(uport);
+ spin_unlock_irq(&uport->lock);
+ if (mctrl & TIOCM_CAR)
+ return 1;
+ return 0;
+}
- /*
- * If non-blocking mode is set, or CLOCAL mode is set,
- * we don't want to wait for the modem status lines to
- * indicate that the port is ready.
- *
- * Also, if the port is not enabled/configured, we want
- * to allow the open to succeed here. Note that we will
- * have set TTY_IO_ERROR for a non-existant port.
- */
- if ((filp->f_flags & O_NONBLOCK) ||
- (port->tty->termios->c_cflag & CLOCAL) ||
- (port->tty->flags & (1 << TTY_IO_ERROR)))
- break;
+static void uart_dtr_rts(struct tty_port *port, int onoff)
+{
+ struct uart_state *state = container_of(port, struct uart_state, port);
+ struct uart_port *uport = state->uart_port;
- /*
- * Set DTR to allow modem to know we're waiting. Do
- * not set RTS here - we want to make sure we catch
- * the data from the modem.
- */
- if (port->tty->termios->c_cflag & CBAUD)
- uart_set_mctrl(uport, TIOCM_DTR);
+ if (onoff) {
+ uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
/*
- * and wait for the carrier to indicate that the
- * modem is ready for us.
+ * If this is the first open to succeed,
+ * adjust things to suit.
*/
- spin_lock_irq(&uport->lock);
- uport->ops->enable_ms(uport);
- mctrl = uport->ops->get_mctrl(uport);
- spin_unlock_irq(&uport->lock);
- if (mctrl & TIOCM_CAR)
- break;
-
- mutex_unlock(&port->mutex);
- schedule();
- mutex_lock(&port->mutex);
-
- if (signal_pending(current))
- break;
+ if (!test_and_set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags))
+ uart_update_termios(port->tty, state);
}
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&port->open_wait, &wait);
-
- port->count++;
- port->blocked_open--;
-
- if (signal_pending(current))
- return -ERESTARTSYS;
-
- if (!port->tty || tty_hung_up_p(filp))
- return -EAGAIN;
-
- return 0;
+ else
+ uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
}
static struct uart_state *uart_get(struct uart_driver *drv, int line)
struct tty_port *port;
int retval, line = tty->index;
- BUG_ON(!kernel_locked());
+ BUG_ON(!tty_locked());
pr_debug("uart_open(%d) called\n", line);
/*
/*
* Start up the serial port.
*/
- retval = uart_startup(state, 0);
+ retval = uart_startup(tty, state, 0);
/*
* If we succeeded, wait until the port is ready.
*/
- if (retval == 0)
- retval = uart_block_til_ready(filp, state);
mutex_unlock(&port->mutex);
-
- /*
- * If this is the first open to succeed, adjust things to suit.
- */
- if (retval == 0 && !(port->flags & ASYNC_NORMAL_ACTIVE)) {
- set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
-
- uart_update_termios(state);
- }
+ if (retval == 0)
+ retval = tty_port_block_til_ready(port, tty, filp);
fail:
return retval;
struct tty_port *port = &state->port;
struct device *tty_dev;
struct uart_match match = {uport, drv};
+ struct tty_struct *tty;
mutex_lock(&port->mutex);
+ /* Must be inside the mutex lock until we convert to tty_port */
+ tty = port->tty;
+
tty_dev = device_find_child(uport->dev, &match, serial_match_port);
if (device_may_wakeup(tty_dev)) {
enable_irq_wake(uport->irq);
ops->set_mctrl(uport, 0);
spin_unlock_irq(&uport->lock);
if (console_suspend_enabled || !uart_console(uport)) {
+ /* Protected by port mutex for now */
+ struct tty_struct *tty = port->tty;
ret = ops->startup(uport);
if (ret == 0) {
- uart_change_speed(state, NULL);
+ if (tty)
+ uart_change_speed(tty, state, NULL);
spin_lock_irq(&uport->lock);
ops->set_mctrl(uport, uport->mctrl);
ops->start_tx(uport);
* Clear the "initialized" flag so we won't try
* to call the low level drivers shutdown method.
*/
- uart_shutdown(state);
+ uart_shutdown(tty, state);
}
}
#endif
};
+static const struct tty_port_operations uart_port_ops = {
+ .carrier_raised = uart_carrier_raised,
+ .dtr_rts = uart_dtr_rts,
+};
+
/**
* uart_register_driver - register a driver with the uart core layer
* @drv: low level driver structure
struct tty_port *port = &state->port;
tty_port_init(port);
+ port->ops = &uart_port_ops;
port->close_delay = 500; /* .5 seconds */
port->closing_wait = 30000; /* 30 seconds */
tasklet_init(&state->tlet, uart_tasklet_action,
return sci_in(port, SCFDR) & SCIF2_RFDC_MASK;
}
}
+#elif defined(CONFIG_ARCH_SH7372)
+static int scif_txfill(struct uart_port *port)
+{
+ if (port->type == PORT_SCIFA)
+ return sci_in(port, SCFDR) >> 8;
+ else
+ return sci_in(port, SCTFDR);
+}
+
+static int scif_txroom(struct uart_port *port)
+{
+ return port->fifosize - scif_txfill(port);
+}
+
+static int scif_rxfill(struct uart_port *port)
+{
+ if (port->type == PORT_SCIFA)
+ return sci_in(port, SCFDR) & SCIF_RFDC_MASK;
+ else
+ return sci_in(port, SCRFDR);
+}
#else
static int scif_txfill(struct uart_port *port)
{
u16 ssr = sci_in(port, SCxSR);
/* Disable future Rx interrupts */
- if (port->type == PORT_SCIFA) {
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
disable_irq_nosync(irq);
scr |= 0x4000;
} else {
if (!uart_circ_empty(xmit)) {
schedule_work(&s->work_tx);
- } else if (port->type == PORT_SCIFA) {
+ } else if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
u16 ctrl = sci_in(port, SCSCR);
sci_out(port, SCSCR, ctrl & ~SCI_CTRL_FLAGS_TIE);
}
unsigned short ctrl;
#ifdef CONFIG_SERIAL_SH_SCI_DMA
- if (port->type == PORT_SCIFA) {
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
u16 new, scr = sci_in(port, SCSCR);
if (s->chan_tx)
new = scr | 0x8000;
s->cookie_tx < 0)
schedule_work(&s->work_tx);
#endif
- if (!s->chan_tx || port->type == PORT_SCIFA) {
+ if (!s->chan_tx || port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
/* Set TIE (Transmit Interrupt Enable) bit in SCSCR */
ctrl = sci_in(port, SCSCR);
sci_out(port, SCSCR, ctrl | SCI_CTRL_FLAGS_TIE);
/* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */
ctrl = sci_in(port, SCSCR);
- if (port->type == PORT_SCIFA)
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
ctrl &= ~0x8000;
ctrl &= ~SCI_CTRL_FLAGS_TIE;
sci_out(port, SCSCR, ctrl);
/* Set RIE (Receive Interrupt Enable) bit in SCSCR */
ctrl |= sci_in(port, SCSCR);
- if (port->type == PORT_SCIFA)
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
ctrl &= ~0x4000;
sci_out(port, SCSCR, ctrl);
}
/* Clear RIE (Receive Interrupt Enable) bit in SCSCR */
ctrl = sci_in(port, SCSCR);
- if (port->type == PORT_SCIFA)
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
ctrl &= ~0x4000;
ctrl &= ~(SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE);
sci_out(port, SCSCR, ctrl);
struct uart_port *port = &s->port;
u16 scr = sci_in(port, SCSCR);
- if (port->type == PORT_SCIFA) {
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
scr &= ~0x4000;
enable_irq(s->irqs[1]);
}
return "scif";
case PORT_SCIFA:
return "scifa";
+ case PORT_SCIFB:
+ return "scifb";
}
return NULL;
port->line = index;
switch (p->type) {
+ case PORT_SCIFB:
+ port->fifosize = 256;
+ break;
case PORT_SCIFA:
port->fifosize = 64;
break;
#define CPU_SCIx_FNS(name, sci_offset, sci_size, scif_offset, scif_size)\
static inline unsigned int sci_##name##_in(struct uart_port *port) \
{ \
- if (port->type == PORT_SCIF) { \
+ if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \
SCI_IN(scif_size, scif_offset) \
} else { /* PORT_SCI or PORT_SCIFA */ \
SCI_IN(sci_size, sci_offset); \
} \
static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \
{ \
- if (port->type == PORT_SCIF) { \
+ if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \
SCI_OUT(scif_size, scif_offset, value) \
} else { /* PORT_SCI or PORT_SCIFA */ \
SCI_OUT(sci_size, sci_offset, value); \
defined(CONFIG_CPU_SUBTYPE_SH7720) || \
defined(CONFIG_CPU_SUBTYPE_SH7721) || \
defined(CONFIG_ARCH_SH7367) || \
- defined(CONFIG_ARCH_SH7377) || \
- defined(CONFIG_ARCH_SH7372)
+ defined(CONFIG_ARCH_SH7377)
+#define SCIF_FNS(name, scif_offset, scif_size) \
+ CPU_SCIF_FNS(name, scif_offset, scif_size)
+#elif defined(CONFIG_ARCH_SH7372)
+#define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) \
+ CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size)
#define SCIF_FNS(name, scif_offset, scif_size) \
CPU_SCIF_FNS(name, scif_offset, scif_size)
#else
defined(CONFIG_CPU_SUBTYPE_SH7720) || \
defined(CONFIG_CPU_SUBTYPE_SH7721) || \
defined(CONFIG_ARCH_SH7367) || \
- defined(CONFIG_ARCH_SH7377) || \
- defined(CONFIG_ARCH_SH7372)
+ defined(CONFIG_ARCH_SH7377)
SCIF_FNS(SCSMR, 0x00, 16)
SCIF_FNS(SCBRR, 0x04, 8)
SCIF_FNS(SCxTDR, 0x20, 8)
SCIF_FNS(SCxRDR, 0x24, 8)
SCIF_FNS(SCLSR, 0x00, 0)
+#elif defined(CONFIG_ARCH_SH7372)
+SCIF_FNS(SCSMR, 0x00, 16)
+SCIF_FNS(SCBRR, 0x04, 8)
+SCIF_FNS(SCSCR, 0x08, 16)
+SCIF_FNS(SCTDSR, 0x0c, 16)
+SCIF_FNS(SCFER, 0x10, 16)
+SCIF_FNS(SCxSR, 0x14, 16)
+SCIF_FNS(SCFCR, 0x18, 16)
+SCIF_FNS(SCFDR, 0x1c, 16)
+SCIF_FNS(SCTFDR, 0x38, 16)
+SCIF_FNS(SCRFDR, 0x3c, 16)
+SCIx_FNS(SCxTDR, 0x20, 8, 0x40, 8)
+SCIx_FNS(SCxRDR, 0x24, 8, 0x60, 8)
+SCIF_FNS(SCLSR, 0x00, 0)
#elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\
defined(CONFIG_CPU_SUBTYPE_SH7724)
SCIx_FNS(SCSMR, 0x00, 16, 0x00, 16)
.nr = 1
};
-static int timbuart_probe(struct platform_device *dev)
+static int __devinit timbuart_probe(struct platform_device *dev)
{
int err, irq;
struct timbuart_port *uart;
return err;
}
-static int timbuart_remove(struct platform_device *dev)
+static int __devexit timbuart_remove(struct platform_device *dev)
{
struct timbuart_port *uart = platform_get_drvdata(dev);
.owner = THIS_MODULE,
},
.probe = timbuart_probe,
- .remove = timbuart_remove,
+ .remove = __devexit_p(timbuart_remove),
};
/*--------------------------------------------------------------------------*/
#
# Makefile for the SuperH specific drivers.
#
+obj-y := clk.o intc.o
+
obj-$(CONFIG_SUPERHYWAY) += superhyway/
obj-$(CONFIG_MAPLE) += maple/
+
obj-$(CONFIG_GENERIC_GPIO) += pfc.o
-obj-$(CONFIG_SUPERH) += clk.o
obj-$(CONFIG_SH_CLK_CPG) += clk-cpg.o
-obj-y += intc.o
return clk->freq_table[idx].frequency;
}
+static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct clk_div_mult_table *table = &sh_clk_div6_table;
+ u32 value;
+ int ret, i;
+
+ if (!clk->parent_table || !clk->parent_num)
+ return -EINVAL;
+
+ /* Search the parent */
+ for (i = 0; i < clk->parent_num; i++)
+ if (clk->parent_table[i] == parent)
+ break;
+
+ if (i == clk->parent_num)
+ return -ENODEV;
+
+ ret = clk_reparent(clk, parent);
+ if (ret < 0)
+ return ret;
+
+ value = __raw_readl(clk->enable_reg) &
+ ~(((1 << clk->src_width) - 1) << clk->src_shift);
+
+ __raw_writel(value | (i << clk->src_shift), clk->enable_reg);
+
+ /* Rebuild the frequency table */
+ clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
+ table, &clk->arch_flags);
+
+ return 0;
+}
+
static int sh_clk_div6_set_rate(struct clk *clk,
unsigned long rate, int algo_id)
{
.disable = sh_clk_div6_disable,
};
-int __init sh_clk_div6_register(struct clk *clks, int nr)
+static struct clk_ops sh_clk_div6_reparent_clk_ops = {
+ .recalc = sh_clk_div6_recalc,
+ .round_rate = sh_clk_div_round_rate,
+ .set_rate = sh_clk_div6_set_rate,
+ .enable = sh_clk_div6_enable,
+ .disable = sh_clk_div6_disable,
+ .set_parent = sh_clk_div6_set_parent,
+};
+
+static int __init sh_clk_div6_register_ops(struct clk *clks, int nr,
+ struct clk_ops *ops)
{
struct clk *clkp;
void *freq_table;
for (k = 0; !ret && (k < nr); k++) {
clkp = clks + k;
- clkp->ops = &sh_clk_div6_clk_ops;
+ clkp->ops = ops;
clkp->id = -1;
clkp->freq_table = freq_table + (k * freq_table_size);
clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END;
return ret;
}
+int __init sh_clk_div6_register(struct clk *clks, int nr)
+{
+ return sh_clk_div6_register_ops(clks, nr, &sh_clk_div6_clk_ops);
+}
+
+int __init sh_clk_div6_reparent_register(struct clk *clks, int nr)
+{
+ return sh_clk_div6_register_ops(clks, nr,
+ &sh_clk_div6_reparent_clk_ops);
+}
+
static unsigned long sh_clk_div4_recalc(struct clk *clk)
{
struct clk_div4_table *d4t = clk->priv;
void easycap_complete(struct urb *);
int easycap_open(struct inode *, struct file *);
int easycap_release(struct inode *, struct file *);
-int easycap_ioctl(struct inode *, struct file *, \
- unsigned int, unsigned long);
+long easycap_ioctl(struct file *, unsigned int, unsigned long);
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#if defined(EASYCAP_IS_VIDEODEV_CLIENT)
int easycap_open_noinode(struct file *);
int easycap_release_noinode(struct file *);
-long easycap_ioctl_noinode(struct file *, \
- unsigned int, unsigned long);
int videodev_release(struct video_device *);
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
ssize_t easysnd_read(struct file *, char __user *, size_t, loff_t *);
int easysnd_open(struct inode *, struct file *);
int easysnd_release(struct inode *, struct file *);
-int easysnd_ioctl(struct inode *, struct file *, \
- unsigned int, unsigned long);
+long easysnd_ioctl(struct file *, unsigned int, unsigned long);
unsigned int easysnd_poll(struct file *, poll_table *);
void easysnd_delete(struct kref *);
int submit_audio_urbs(struct easycap *);
*/
/*****************************************************************************/
+#include <linux/smp_lock.h>
#include "easycap.h"
#include "easycap_debug.h"
#include "easycap_standard.h"
SAY("WARNING: failed to adjust mute: control not found\n");
return -ENOENT;
}
-/****************************************************************************/
-/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
-#if defined(EASYCAP_IS_VIDEODEV_CLIENT)
-long
-easycap_ioctl_noinode(struct file *file, unsigned int cmd, unsigned long arg)\
- {
- return easycap_ioctl((struct inode *)NULL, file, cmd, arg);
-}
-#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
-/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+
/*--------------------------------------------------------------------------*/
-int easycap_ioctl(struct inode *inode, struct file *file, \
- unsigned int cmd, unsigned long arg)
+static int easycap_ioctl_bkl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
{
static struct easycap *peasycap;
static struct usb_device *p;
}
return 0;
}
-/****************************************************************************/
-/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
-#if defined(EASYCAP_IS_VIDEODEV_CLIENT)
-long
-easysnd_ioctl_noinode(struct file *file, unsigned int cmd, unsigned long arg)
+
+long easycap_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- return easysnd_ioctl((struct inode *)NULL, file, cmd, arg);
+ struct inode *inode = file->f_dentry->d_inode;
+ long ret;
+
+ lock_kernel();
+ ret = easycap_ioctl_bkl(inode, file, cmd, arg);
+ unlock_kernel();
+
+ return ret;
}
-#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
-/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+
/*--------------------------------------------------------------------------*/
-int easysnd_ioctl(struct inode *inode, struct file *file, \
- unsigned int cmd, unsigned long arg)
+static int easysnd_ioctl_bkl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
{
struct easycap *peasycap;
struct usb_device *p;
}
return 0;
}
+
+long easysnd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ long ret;
+
+ lock_kernel();
+ ret = easysnd_ioctl_bkl(inode, file, cmd, arg);
+ unlock_kernel();
+
+ return ret;
+}
+
/*****************************************************************************/
int explain_ioctl(__u32 wot)
{
*/
/*---------------------------------------------------------------------------*/
const struct file_operations easycap_fops = {
-.owner = THIS_MODULE,
-.open = easycap_open,
-.release = easycap_release,
-.ioctl = easycap_ioctl,
-.poll = easycap_poll,
-.mmap = easycap_mmap,
-.llseek = no_llseek,
+ .owner = THIS_MODULE,
+ .open = easycap_open,
+ .release = easycap_release,
+ .unlocked_ioctl = easycap_ioctl,
+ .poll = easycap_poll,
+ .mmap = easycap_mmap,
+ .llseek = no_llseek,
};
struct vm_operations_struct easycap_vm_ops = {
.open = easycap_vma_open,
#if defined(EASYCAP_IS_VIDEODEV_CLIENT)
#if defined(EASYCAP_NEEDS_V4L2_FOPS)
const struct v4l2_file_operations v4l2_fops = {
-.owner = THIS_MODULE,
-.open = easycap_open_noinode,
-.release = easycap_release_noinode,
-.ioctl = easycap_ioctl_noinode,
-.poll = easycap_poll,
-.mmap = easycap_mmap,
+ .owner = THIS_MODULE,
+ .open = easycap_open_noinode,
+ .release = easycap_release_noinode,
+ .unlocked_ioctl = easycap_ioctl,
+ .poll = easycap_poll,
+ .mmap = easycap_mmap,
};
#endif /*EASYCAP_NEEDS_V4L2_FOPS*/
int video_device_many /*=0*/;
*/
/*--------------------------------------------------------------------------*/
const struct file_operations easysnd_fops = {
-.owner = THIS_MODULE,
-.open = easysnd_open,
-.release = easysnd_release,
-.ioctl = easysnd_ioctl,
-.read = easysnd_read,
-.llseek = no_llseek,
+ .owner = THIS_MODULE,
+ .open = easysnd_open,
+ .release = easysnd_release,
+ .unlocked_ioctl = easysnd_ioctl,
+ .read = easysnd_read,
+ .llseek = no_llseek,
};
struct usb_class_driver easysnd_class = {
.name = "usb/easysnd%d",
#include <linux/major.h>
#include <linux/delay.h>
#include <linux/hdreg.h>
+#include <linux/smp_lock.h>
#include <linux/slab.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
blkvsc_req->cmnd[0] = READ_16;
}
- blkvsc_req->cmnd[1] |= blk_fua_rq(blkvsc_req->req) ? 0x8 : 0;
+ blkvsc_req->cmnd[1] |=
+ (blkvsc_req->req->cmd_flags & REQ_FUA) ? 0x8 : 0;
*(unsigned long long *)&blkvsc_req->cmnd[2] =
cpu_to_be64(blkvsc_req->sector_start);
blkvsc_req->cmnd[0] = READ_10;
}
- blkvsc_req->cmnd[1] |= blk_fua_rq(blkvsc_req->req) ? 0x8 : 0;
+ blkvsc_req->cmnd[1] |=
+ (blkvsc_req->req->cmd_flags & REQ_FUA) ? 0x8 : 0;
*(unsigned int *)&blkvsc_req->cmnd[2] =
cpu_to_be32(blkvsc_req->sector_start);
DPRINT_DBG(BLKVSC_DRV, "- req %p\n", req);
blkdev = req->rq_disk->private_data;
- if (blkdev->shutting_down || !blk_fs_request(req) ||
+ if (blkdev->shutting_down || req->cmd_type != REQ_TYPE_FS ||
blkdev->media_not_present) {
__blk_end_request_cur(req, 0);
continue;
DPRINT_DBG(BLKVSC_DRV, "- users %d disk %s\n", blkdev->users,
blkdev->gd->disk_name);
+ lock_kernel();
spin_lock(&blkdev->lock);
if (!blkdev->users && blkdev->device_type == DVD_TYPE) {
blkdev->users++;
spin_unlock(&blkdev->lock);
+ unlock_kernel();
return 0;
}
DPRINT_DBG(BLKVSC_DRV, "- users %d disk %s\n", blkdev->users,
blkdev->gd->disk_name);
+ lock_kernel();
spin_lock(&blkdev->lock);
if (blkdev->users == 1) {
spin_unlock(&blkdev->lock);
blkdev->users--;
spin_unlock(&blkdev->lock);
+ unlock_kernel();
return 0;
}
#
menuconfig LIRC_STAGING
bool "Linux Infrared Remote Control IR receiver/transmitter drivers"
+ depends on LIRC
help
Say Y here, and all supported Linux Infrared Remote Control IR and
RF receiver and transmitter drivers will be displayed. When paired
config LIRC_BT829
tristate "BT829 based hardware"
- depends on LIRC_STAGING
+ depends on LIRC_STAGING && PCI
help
Driver for the IR interface on BT829-based hardware
-config LIRC_ENE0100
- tristate "ENE KB3924/ENE0100 CIR Port Reciever"
- depends on LIRC_STAGING
- help
- This is a driver for CIR port handled by ENE KB3924 embedded
- controller found on some notebooks.
- It appears on PNP list as ENE0100.
-
config LIRC_I2C
tristate "I2C Based IR Receivers"
- depends on LIRC_STAGING
+ depends on LIRC_STAGING && I2C
help
Driver for I2C-based IR receivers, such as those commonly
found onboard Hauppauge PVR-150/250/350 video capture cards
config LIRC_IMON
tristate "Legacy SoundGraph iMON Receiver and Display"
- depends on LIRC_STAGING
+ depends on LIRC_STAGING && USB
help
Driver for the original SoundGraph iMON IR Receiver and Display
config LIRC_IT87
tristate "ITE IT87XX CIR Port Receiver"
- depends on LIRC_STAGING
+ depends on LIRC_STAGING && PNP
help
Driver for the ITE IT87xx IR Receiver
config LIRC_PARALLEL
tristate "Homebrew Parallel Port Receiver"
- depends on LIRC_STAGING && !SMP
+ depends on LIRC_STAGING && PARPORT && !SMP
help
Driver for Homebrew Parallel Port Receivers
config LIRC_SASEM
tristate "Sasem USB IR Remote"
- depends on LIRC_STAGING
+ depends on LIRC_STAGING && USB
help
Driver for the Sasem OnAir Remocon-V or Dign HV5 HTPC IR/VFD Module
help
Driver for the SIR IrDA port
-config LIRC_STREAMZAP
- tristate "Streamzap PC Receiver"
- depends on LIRC_STAGING
- help
- Driver for the Streamzap PC Receiver
-
config LIRC_TTUSBIR
tristate "Technotrend USB IR Receiver"
depends on LIRC_STAGING && USB
config LIRC_ZILOG
tristate "Zilog/Hauppauge IR Transmitter"
- depends on LIRC_STAGING
+ depends on LIRC_STAGING && I2C
help
Driver for the Zilog/Hauppauge IR Transmitter, found on
PVR-150/500, HVR-1200/1250/1700/1800, HD-PVR and other cards
# Each configuration option enables a list of files.
obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o
-obj-$(CONFIG_LIRC_ENE0100) += lirc_ene0100.o
obj-$(CONFIG_LIRC_I2C) += lirc_i2c.o
obj-$(CONFIG_LIRC_IGORPLUGUSB) += lirc_igorplugusb.o
obj-$(CONFIG_LIRC_IMON) += lirc_imon.o
obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o
obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o
obj-$(CONFIG_LIRC_SIR) += lirc_sir.o
-obj-$(CONFIG_LIRC_STREAMZAP) += lirc_streamzap.o
obj-$(CONFIG_LIRC_TTUSBIR) += lirc_ttusbir.o
obj-$(CONFIG_LIRC_ZILOG) += lirc_zilog.o
+++ /dev/null
-/*
- * driver for ENE KB3926 B/C/D CIR (also known as ENE0100)
- *
- * Copyright (C) 2009 Maxim Levitsky <maximlevitsky@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 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/pnp.h>
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/sched.h>
-#include "lirc_ene0100.h"
-
-static int sample_period = 75;
-static int enable_idle = 1;
-static int enable_learning;
-
-static void ene_set_idle(struct ene_device *dev, int idle);
-static void ene_set_inputs(struct ene_device *dev, int enable);
-
-/* read a hardware register */
-static u8 ene_hw_read_reg(struct ene_device *dev, u16 reg)
-{
- outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
- outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
- return inb(dev->hw_io + ENE_IO);
-}
-
-/* write a hardware register */
-static void ene_hw_write_reg(struct ene_device *dev, u16 reg, u8 value)
-{
- outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
- outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
- outb(value, dev->hw_io + ENE_IO);
-}
-
-/* change specific bits in hardware register */
-static void ene_hw_write_reg_mask(struct ene_device *dev,
- u16 reg, u8 value, u8 mask)
-{
- u8 regvalue;
-
- outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
- outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
-
- regvalue = inb(dev->hw_io + ENE_IO) & ~mask;
- regvalue |= (value & mask);
- outb(regvalue, dev->hw_io + ENE_IO);
-}
-
-/* read irq status and ack it */
-static int ene_hw_irq_status(struct ene_device *dev, int *buffer_pointer)
-{
- u8 irq_status;
- u8 fw_flags1, fw_flags2;
-
- fw_flags2 = ene_hw_read_reg(dev, ENE_FW2);
-
- if (buffer_pointer)
- *buffer_pointer = 4 * (fw_flags2 & ENE_FW2_BUF_HIGH);
-
- if (dev->hw_revision < ENE_HW_C) {
- irq_status = ene_hw_read_reg(dev, ENEB_IRQ_STATUS);
-
- if (!(irq_status & ENEB_IRQ_STATUS_IR))
- return 0;
- ene_hw_write_reg(dev, ENEB_IRQ_STATUS,
- irq_status & ~ENEB_IRQ_STATUS_IR);
-
- /* rev B support only recieving */
- return ENE_IRQ_RX;
- }
-
- irq_status = ene_hw_read_reg(dev, ENEC_IRQ);
-
- if (!(irq_status & ENEC_IRQ_STATUS))
- return 0;
-
- /* original driver does that twice - a workaround ? */
- ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
- ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
-
- /* clear unknown flag in F8F9 */
- if (fw_flags2 & ENE_FW2_IRQ_CLR)
- ene_hw_write_reg(dev, ENE_FW2, fw_flags2 & ~ENE_FW2_IRQ_CLR);
-
- /* check if this is a TX interrupt */
- fw_flags1 = ene_hw_read_reg(dev, ENE_FW1);
-
- if (fw_flags1 & ENE_FW1_TXIRQ) {
- ene_hw_write_reg(dev, ENE_FW1, fw_flags1 & ~ENE_FW1_TXIRQ);
- return ENE_IRQ_TX;
- } else
- return ENE_IRQ_RX;
-}
-
-static int ene_hw_detect(struct ene_device *dev)
-{
- u8 chip_major, chip_minor;
- u8 hw_revision, old_ver;
- u8 tmp;
- u8 fw_capabilities;
-
- tmp = ene_hw_read_reg(dev, ENE_HW_UNK);
- ene_hw_write_reg(dev, ENE_HW_UNK, tmp & ~ENE_HW_UNK_CLR);
-
- chip_major = ene_hw_read_reg(dev, ENE_HW_VER_MAJOR);
- chip_minor = ene_hw_read_reg(dev, ENE_HW_VER_MINOR);
-
- ene_hw_write_reg(dev, ENE_HW_UNK, tmp);
- hw_revision = ene_hw_read_reg(dev, ENE_HW_VERSION);
- old_ver = ene_hw_read_reg(dev, ENE_HW_VER_OLD);
-
- if (hw_revision == 0xFF) {
-
- ene_printk(KERN_WARNING, "device seems to be disabled\n");
- ene_printk(KERN_WARNING,
- "send a mail to lirc-list@lists.sourceforge.net\n");
- ene_printk(KERN_WARNING, "please attach output of acpidump\n");
-
- return -ENODEV;
- }
-
- if (chip_major == 0x33) {
- ene_printk(KERN_WARNING, "chips 0x33xx aren't supported yet\n");
- return -ENODEV;
- }
-
- if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) {
- dev->hw_revision = ENE_HW_C;
- ene_printk(KERN_WARNING,
- "KB3926C detected, driver support is not complete!\n");
-
- } else if (old_ver == 0x24 && hw_revision == 0xC0) {
- dev->hw_revision = ENE_HW_B;
- ene_printk(KERN_NOTICE, "KB3926B detected\n");
- } else {
- dev->hw_revision = ENE_HW_D;
- ene_printk(KERN_WARNING,
- "unknown ENE chip detected, assuming KB3926D\n");
- ene_printk(KERN_WARNING, "driver support incomplete");
-
- }
-
- ene_printk(KERN_DEBUG, "chip is 0x%02x%02x - 0x%02x, 0x%02x\n",
- chip_major, chip_minor, old_ver, hw_revision);
-
-
- /* detect features hardware supports */
-
- if (dev->hw_revision < ENE_HW_C)
- return 0;
-
- fw_capabilities = ene_hw_read_reg(dev, ENE_FW2);
-
- dev->hw_gpio40_learning = fw_capabilities & ENE_FW2_GP40_AS_LEARN;
- dev->hw_learning_and_tx_capable = fw_capabilities & ENE_FW2_LEARNING;
-
- dev->hw_fan_as_normal_input = dev->hw_learning_and_tx_capable &&
- fw_capabilities & ENE_FW2_FAN_AS_NRML_IN;
-
- ene_printk(KERN_NOTICE, "hardware features:\n");
- ene_printk(KERN_NOTICE,
- "learning and tx %s, gpio40_learn %s, fan_in %s\n",
- dev->hw_learning_and_tx_capable ? "on" : "off",
- dev->hw_gpio40_learning ? "on" : "off",
- dev->hw_fan_as_normal_input ? "on" : "off");
-
- if (!dev->hw_learning_and_tx_capable && enable_learning)
- enable_learning = 0;
-
- if (dev->hw_learning_and_tx_capable) {
- ene_printk(KERN_WARNING,
- "Device supports transmitting, but the driver doesn't\n");
- ene_printk(KERN_WARNING,
- "due to lack of hardware to test against.\n");
- ene_printk(KERN_WARNING,
- "Send a mail to: lirc-list@lists.sourceforge.net\n");
- }
- return 0;
-}
-
-/* hardware initialization */
-static int ene_hw_init(void *data)
-{
- u8 reg_value;
- struct ene_device *dev = (struct ene_device *)data;
- dev->in_use = 1;
-
- if (dev->hw_revision < ENE_HW_C) {
- ene_hw_write_reg(dev, ENEB_IRQ, dev->irq << 1);
- ene_hw_write_reg(dev, ENEB_IRQ_UNK1, 0x01);
- } else {
- reg_value = ene_hw_read_reg(dev, ENEC_IRQ) & 0xF0;
- reg_value |= ENEC_IRQ_UNK_EN;
- reg_value &= ~ENEC_IRQ_STATUS;
- reg_value |= (dev->irq & ENEC_IRQ_MASK);
- ene_hw_write_reg(dev, ENEC_IRQ, reg_value);
- ene_hw_write_reg(dev, ENE_TX_UNK1, 0x63);
- }
-
- ene_hw_write_reg(dev, ENE_CIR_CONF2, 0x00);
- ene_set_inputs(dev, enable_learning);
-
- /* set sampling period */
- ene_hw_write_reg(dev, ENE_CIR_SAMPLE_PERIOD, sample_period);
-
- /* ack any pending irqs - just in case */
- ene_hw_irq_status(dev, NULL);
-
- /* enter idle mode */
- ene_set_idle(dev, 1);
-
- /* enable firmware bits */
- ene_hw_write_reg_mask(dev, ENE_FW1,
- ENE_FW1_ENABLE | ENE_FW1_IRQ,
- ENE_FW1_ENABLE | ENE_FW1_IRQ);
- /* clear stats */
- dev->sample = 0;
- return 0;
-}
-
-/* this enables gpio40 signal, used if connected to wide band input*/
-static void ene_enable_gpio40(struct ene_device *dev, int enable)
-{
- ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, enable ?
- 0 : ENE_CIR_CONF2_GPIO40DIS,
- ENE_CIR_CONF2_GPIO40DIS);
-}
-
-/* this enables the classic sampler */
-static void ene_enable_normal_recieve(struct ene_device *dev, int enable)
-{
- ene_hw_write_reg(dev, ENE_CIR_CONF1, enable ? ENE_CIR_CONF1_ADC_ON : 0);
-}
-
-/* this enables recieve via fan input */
-static void ene_enable_fan_recieve(struct ene_device *dev, int enable)
-{
- if (!enable)
- ene_hw_write_reg(dev, ENE_FAN_AS_IN1, 0);
- else {
- ene_hw_write_reg(dev, ENE_FAN_AS_IN1, ENE_FAN_AS_IN1_EN);
- ene_hw_write_reg(dev, ENE_FAN_AS_IN2, ENE_FAN_AS_IN2_EN);
- }
- dev->fan_input_inuse = enable;
-}
-
-/* determine which input to use*/
-static void ene_set_inputs(struct ene_device *dev, int learning_enable)
-{
- ene_enable_normal_recieve(dev, 1);
-
- /* old hardware doesn't support learning mode for sure */
- if (dev->hw_revision <= ENE_HW_B)
- return;
-
- /* reciever not learning capable, still set gpio40 correctly */
- if (!dev->hw_learning_and_tx_capable) {
- ene_enable_gpio40(dev, !dev->hw_gpio40_learning);
- return;
- }
-
- /* enable learning mode */
- if (learning_enable) {
- ene_enable_gpio40(dev, dev->hw_gpio40_learning);
-
- /* fan input is not used for learning */
- if (dev->hw_fan_as_normal_input)
- ene_enable_fan_recieve(dev, 0);
-
- /* disable learning mode */
- } else {
- if (dev->hw_fan_as_normal_input) {
- ene_enable_fan_recieve(dev, 1);
- ene_enable_normal_recieve(dev, 0);
- } else
- ene_enable_gpio40(dev, !dev->hw_gpio40_learning);
- }
-
- /* set few additional settings for this mode */
- ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, learning_enable ?
- ENE_CIR_CONF1_LEARN1 : 0, ENE_CIR_CONF1_LEARN1);
-
- ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, learning_enable ?
- ENE_CIR_CONF2_LEARN2 : 0, ENE_CIR_CONF2_LEARN2);
-}
-
-/* deinitialization */
-static void ene_hw_deinit(void *data)
-{
- struct ene_device *dev = (struct ene_device *)data;
-
- /* disable samplers */
- ene_enable_normal_recieve(dev, 0);
-
- if (dev->hw_fan_as_normal_input)
- ene_enable_fan_recieve(dev, 0);
-
- /* disable hardware IRQ and firmware flag */
- ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_ENABLE | ENE_FW1_IRQ);
-
- ene_set_idle(dev, 1);
- dev->in_use = 0;
-}
-
-/* sends current sample to userspace */
-static void send_sample(struct ene_device *dev)
-{
- int value = abs(dev->sample) & PULSE_MASK;
-
- if (dev->sample > 0)
- value |= PULSE_BIT;
-
- if (!lirc_buffer_full(dev->lirc_driver->rbuf)) {
- lirc_buffer_write(dev->lirc_driver->rbuf, (void *)&value);
- wake_up(&dev->lirc_driver->rbuf->wait_poll);
- }
- dev->sample = 0;
-}
-
-/* this updates current sample */
-static void update_sample(struct ene_device *dev, int sample)
-{
- if (!dev->sample)
- dev->sample = sample;
- else if (same_sign(dev->sample, sample))
- dev->sample += sample;
- else {
- send_sample(dev);
- dev->sample = sample;
- }
-}
-
-/* enable or disable idle mode */
-static void ene_set_idle(struct ene_device *dev, int idle)
-{
- struct timeval now;
- int disable = idle && enable_idle && (dev->hw_revision < ENE_HW_C);
-
- ene_hw_write_reg_mask(dev, ENE_CIR_SAMPLE_PERIOD,
- disable ? 0 : ENE_CIR_SAMPLE_OVERFLOW,
- ENE_CIR_SAMPLE_OVERFLOW);
- dev->idle = idle;
-
- /* remember when we have entered the idle mode */
- if (idle) {
- do_gettimeofday(&dev->gap_start);
- return;
- }
-
- /* send the gap between keypresses now */
- do_gettimeofday(&now);
-
- if (now.tv_sec - dev->gap_start.tv_sec > 16)
- dev->sample = space(PULSE_MASK);
- else
- dev->sample = dev->sample +
- space(1000000ull * (now.tv_sec - dev->gap_start.tv_sec))
- + space(now.tv_usec - dev->gap_start.tv_usec);
-
- if (abs(dev->sample) > PULSE_MASK)
- dev->sample = space(PULSE_MASK);
- send_sample(dev);
-}
-
-/* interrupt handler */
-static irqreturn_t ene_hw_irq(int irq, void *data)
-{
- u16 hw_value;
- int i, hw_sample;
- int space;
- int buffer_pointer;
- int irq_status;
-
- struct ene_device *dev = (struct ene_device *)data;
- irq_status = ene_hw_irq_status(dev, &buffer_pointer);
-
- if (!irq_status)
- return IRQ_NONE;
-
- /* TODO: only RX for now */
- if (irq_status == ENE_IRQ_TX)
- return IRQ_HANDLED;
-
- for (i = 0; i < ENE_SAMPLES_SIZE; i++) {
-
- hw_value = ene_hw_read_reg(dev,
- ENE_SAMPLE_BUFFER + buffer_pointer + i);
-
- if (dev->fan_input_inuse) {
- /* read high part of the sample */
- hw_value |= ene_hw_read_reg(dev,
- ENE_SAMPLE_BUFFER_FAN + buffer_pointer + i) << 8;
-
- /* test for _space_ bit */
- space = !(hw_value & ENE_FAN_SMPL_PULS_MSK);
-
- /* clear space bit, and other unused bits */
- hw_value &= ENE_FAN_VALUE_MASK;
- hw_sample = hw_value * ENE_SAMPLE_PERIOD_FAN;
-
- } else {
- space = hw_value & ENE_SAMPLE_SPC_MASK;
- hw_value &= ENE_SAMPLE_VALUE_MASK;
- hw_sample = hw_value * sample_period;
- }
-
- /* no more data */
- if (!(hw_value))
- break;
-
- if (space)
- hw_sample *= -1;
-
- /* overflow sample recieved, handle it */
-
- if (!dev->fan_input_inuse && hw_value == ENE_SAMPLE_OVERFLOW) {
-
- if (dev->idle)
- continue;
-
- if (dev->sample > 0 || abs(dev->sample) <= ENE_MAXGAP)
- update_sample(dev, hw_sample);
- else
- ene_set_idle(dev, 1);
-
- continue;
- }
-
- /* normal first sample recieved */
- if (!dev->fan_input_inuse && dev->idle) {
- ene_set_idle(dev, 0);
-
- /* discard first recieved value, its random
- since its the time signal was off before
- first pulse if idle mode is enabled, HW
- does that for us */
-
- if (!enable_idle)
- continue;
- }
- update_sample(dev, hw_sample);
- send_sample(dev);
- }
- return IRQ_HANDLED;
-}
-
-static int ene_probe(struct pnp_dev *pnp_dev,
- const struct pnp_device_id *dev_id)
-{
- struct ene_device *dev;
- struct lirc_driver *lirc_driver;
- int error = -ENOMEM;
-
- dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL);
-
- if (!dev)
- goto err1;
-
- dev->pnp_dev = pnp_dev;
- pnp_set_drvdata(pnp_dev, dev);
-
-
- /* prepare lirc interface */
- error = -ENOMEM;
- lirc_driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
-
- if (!lirc_driver)
- goto err2;
-
- dev->lirc_driver = lirc_driver;
-
- strcpy(lirc_driver->name, ENE_DRIVER_NAME);
- lirc_driver->minor = -1;
- lirc_driver->code_length = sizeof(int) * 8;
- lirc_driver->features = LIRC_CAN_REC_MODE2;
- lirc_driver->data = dev;
- lirc_driver->set_use_inc = ene_hw_init;
- lirc_driver->set_use_dec = ene_hw_deinit;
- lirc_driver->dev = &pnp_dev->dev;
- lirc_driver->owner = THIS_MODULE;
-
- lirc_driver->rbuf = kzalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
-
- if (!lirc_driver->rbuf)
- goto err3;
-
- if (lirc_buffer_init(lirc_driver->rbuf, sizeof(int), sizeof(int) * 256))
- goto err4;
-
- error = -ENODEV;
- if (lirc_register_driver(lirc_driver))
- goto err5;
-
- /* validate resources */
- if (!pnp_port_valid(pnp_dev, 0) ||
- pnp_port_len(pnp_dev, 0) < ENE_MAX_IO)
- goto err6;
-
- if (!pnp_irq_valid(pnp_dev, 0))
- goto err6;
-
- dev->hw_io = pnp_port_start(pnp_dev, 0);
- dev->irq = pnp_irq(pnp_dev, 0);
-
- /* claim the resources */
- error = -EBUSY;
- if (!request_region(dev->hw_io, ENE_MAX_IO, ENE_DRIVER_NAME))
- goto err6;
-
- if (request_irq(dev->irq, ene_hw_irq,
- IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev))
- goto err7;
-
- /* detect hardware version and features */
- error = ene_hw_detect(dev);
- if (error)
- goto err8;
-
- ene_printk(KERN_NOTICE, "driver has been succesfully loaded\n");
- return 0;
-
-err8:
- free_irq(dev->irq, dev);
-err7:
- release_region(dev->hw_io, ENE_MAX_IO);
-err6:
- lirc_unregister_driver(lirc_driver->minor);
-err5:
- lirc_buffer_free(lirc_driver->rbuf);
-err4:
- kfree(lirc_driver->rbuf);
-err3:
- kfree(lirc_driver);
-err2:
- kfree(dev);
-err1:
- return error;
-}
-
-static void ene_remove(struct pnp_dev *pnp_dev)
-{
- struct ene_device *dev = pnp_get_drvdata(pnp_dev);
- ene_hw_deinit(dev);
- free_irq(dev->irq, dev);
- release_region(dev->hw_io, ENE_MAX_IO);
- lirc_unregister_driver(dev->lirc_driver->minor);
- lirc_buffer_free(dev->lirc_driver->rbuf);
- kfree(dev->lirc_driver);
- kfree(dev);
-}
-
-#ifdef CONFIG_PM
-
-/* TODO: make 'wake on IR' configurable and add .shutdown */
-/* currently impossible due to lack of kernel support */
-
-static int ene_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
-{
- struct ene_device *dev = pnp_get_drvdata(pnp_dev);
- ene_hw_write_reg_mask(dev, ENE_FW1, ENE_FW1_WAKE, ENE_FW1_WAKE);
- return 0;
-}
-
-static int ene_resume(struct pnp_dev *pnp_dev)
-{
- struct ene_device *dev = pnp_get_drvdata(pnp_dev);
- if (dev->in_use)
- ene_hw_init(dev);
-
- ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_WAKE);
- return 0;
-}
-
-#endif
-
-static const struct pnp_device_id ene_ids[] = {
- {.id = "ENE0100",},
- {},
-};
-
-static struct pnp_driver ene_driver = {
- .name = ENE_DRIVER_NAME,
- .id_table = ene_ids,
- .flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
-
- .probe = ene_probe,
- .remove = __devexit_p(ene_remove),
-
-#ifdef CONFIG_PM
- .suspend = ene_suspend,
- .resume = ene_resume,
-#endif
-};
-
-static int __init ene_init(void)
-{
- if (sample_period < 5) {
- ene_printk(KERN_ERR, "sample period must be at\n");
- ene_printk(KERN_ERR, "least 5 us, (at least 30 recommended)\n");
- return -EINVAL;
- }
- return pnp_register_driver(&ene_driver);
-}
-
-static void ene_exit(void)
-{
- pnp_unregister_driver(&ene_driver);
-}
-
-module_param(sample_period, int, S_IRUGO);
-MODULE_PARM_DESC(sample_period, "Hardware sample period (75 us default)");
-
-module_param(enable_idle, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(enable_idle,
- "Enables turning off signal sampling after long inactivity time; "
- "if disabled might help detecting input signal (default: enabled)");
-
-module_param(enable_learning, bool, S_IRUGO);
-MODULE_PARM_DESC(enable_learning, "Use wide band (learning) reciever");
-
-MODULE_DEVICE_TABLE(pnp, ene_ids);
-MODULE_DESCRIPTION
- ("LIRC driver for KB3926B/KB3926C/KB3926D (aka ENE0100) CIR port");
-MODULE_AUTHOR("Maxim Levitsky");
-MODULE_LICENSE("GPL");
-
-module_init(ene_init);
-module_exit(ene_exit);
static DEFINE_SPINLOCK(hardware_lock);
static DEFINE_SPINLOCK(dev_lock);
+static bool device_open;
static int rx_buf[RBUF_LEN];
unsigned int rx_tail, rx_head;
static int lirc_open(struct inode *inode, struct file *file)
{
spin_lock(&dev_lock);
- if (module_refcount(THIS_MODULE)) {
+ if (device_open) {
spin_unlock(&dev_lock);
return -EBUSY;
}
+ device_open = true;
spin_unlock(&dev_lock);
return 0;
}
static int lirc_close(struct inode *inode, struct file *file)
{
+ spin_lock(&dev_lock);
+ device_open = false;
+ spin_unlock(&dev_lock);
return 0;
}
};
-#ifdef MODULE
static int init_chrdev(void)
{
driver.minor = lirc_register_driver(&driver);
{
lirc_unregister_driver(driver.minor);
}
-#endif
/* SECTION: Hardware */
unsigned int level, newlevel;
unsigned int timeout;
- if (!module_refcount(THIS_MODULE))
+ if (!is_open)
return;
if (!is_claimed)
static int lirc_open(struct inode *node, struct file *filep)
{
- if (module_refcount(THIS_MODULE) || !lirc_claim())
+ if (is_open || !lirc_claim())
return -EBUSY;
parport_enable_irq(pport);
+++ /dev/null
-/*
- * Streamzap Remote Control driver
- *
- * Copyright (c) 2005 Christoph Bartelmus <lirc@bartelmus.de>
- *
- * This driver was based on the work of Greg Wickham and Adrian
- * Dewhurst. It was substantially rewritten to support correct signal
- * gaps and now maintains a delay buffer, which is used to present
- * consistent timing behaviour to user space applications. Without the
- * delay buffer an ugly hack would be required in lircd, which can
- * cause sluggish signal decoding in certain situations.
- *
- * This driver is based on the USB skeleton driver packaged with the
- * kernel; copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/smp_lock.h>
-#include <linux/completion.h>
-#include <linux/uaccess.h>
-#include <linux/usb.h>
-
-#include <media/lirc.h>
-#include <media/lirc_dev.h>
-
-#define DRIVER_VERSION "1.28"
-#define DRIVER_NAME "lirc_streamzap"
-#define DRIVER_DESC "Streamzap Remote Control driver"
-
-static int debug;
-
-#define USB_STREAMZAP_VENDOR_ID 0x0e9c
-#define USB_STREAMZAP_PRODUCT_ID 0x0000
-
-/* Use our own dbg macro */
-#define dprintk(fmt, args...) \
- do { \
- if (debug) \
- printk(KERN_DEBUG DRIVER_NAME "[%d]: " \
- fmt "\n", ## args); \
- } while (0)
-
-/* table of devices that work with this driver */
-static struct usb_device_id streamzap_table[] = {
- /* Streamzap Remote Control */
- { USB_DEVICE(USB_STREAMZAP_VENDOR_ID, USB_STREAMZAP_PRODUCT_ID) },
- /* Terminating entry */
- { }
-};
-
-MODULE_DEVICE_TABLE(usb, streamzap_table);
-
-#define STREAMZAP_PULSE_MASK 0xf0
-#define STREAMZAP_SPACE_MASK 0x0f
-#define STREAMZAP_TIMEOUT 0xff
-#define STREAMZAP_RESOLUTION 256
-
-/* number of samples buffered */
-#define STREAMZAP_BUF_LEN 128
-
-enum StreamzapDecoderState {
- PulseSpace,
- FullPulse,
- FullSpace,
- IgnorePulse
-};
-
-/* Structure to hold all of our device specific stuff
- *
- * some remarks regarding locking:
- * theoretically this struct can be accessed from three threads:
- *
- * - from lirc_dev through set_use_inc/set_use_dec
- *
- * - from the USB layer throuh probe/disconnect/irq
- *
- * Careful placement of lirc_register_driver/lirc_unregister_driver
- * calls will prevent conflicts. lirc_dev makes sure that
- * set_use_inc/set_use_dec are not being executed and will not be
- * called after lirc_unregister_driver returns.
- *
- * - by the timer callback
- *
- * The timer is only running when the device is connected and the
- * LIRC device is open. Making sure the timer is deleted by
- * set_use_dec will make conflicts impossible.
- */
-struct usb_streamzap {
-
- /* usb */
- /* save off the usb device pointer */
- struct usb_device *udev;
- /* the interface for this device */
- struct usb_interface *interface;
-
- /* buffer & dma */
- unsigned char *buf_in;
- dma_addr_t dma_in;
- unsigned int buf_in_len;
-
- struct usb_endpoint_descriptor *endpoint;
-
- /* IRQ */
- struct urb *urb_in;
-
- /* lirc */
- struct lirc_driver *driver;
- struct lirc_buffer *delay_buf;
-
- /* timer used to support delay buffering */
- struct timer_list delay_timer;
- int timer_running;
- spinlock_t timer_lock;
-
- /* tracks whether we are currently receiving some signal */
- int idle;
- /* sum of signal lengths received since signal start */
- unsigned long sum;
- /* start time of signal; necessary for gap tracking */
- struct timeval signal_last;
- struct timeval signal_start;
- enum StreamzapDecoderState decoder_state;
- struct timer_list flush_timer;
- int flush;
- int in_use;
- int timeout_enabled;
-};
-
-
-/* local function prototypes */
-static int streamzap_probe(struct usb_interface *interface,
- const struct usb_device_id *id);
-static void streamzap_disconnect(struct usb_interface *interface);
-static void usb_streamzap_irq(struct urb *urb);
-static int streamzap_use_inc(void *data);
-static void streamzap_use_dec(void *data);
-static long streamzap_ioctl(struct file *filep, unsigned int cmd,
- unsigned long arg);
-static int streamzap_suspend(struct usb_interface *intf, pm_message_t message);
-static int streamzap_resume(struct usb_interface *intf);
-
-/* usb specific object needed to register this driver with the usb subsystem */
-
-static struct usb_driver streamzap_driver = {
- .name = DRIVER_NAME,
- .probe = streamzap_probe,
- .disconnect = streamzap_disconnect,
- .suspend = streamzap_suspend,
- .resume = streamzap_resume,
- .id_table = streamzap_table,
-};
-
-static void stop_timer(struct usb_streamzap *sz)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&sz->timer_lock, flags);
- if (sz->timer_running) {
- sz->timer_running = 0;
- spin_unlock_irqrestore(&sz->timer_lock, flags);
- del_timer_sync(&sz->delay_timer);
- } else {
- spin_unlock_irqrestore(&sz->timer_lock, flags);
- }
-}
-
-static void flush_timeout(unsigned long arg)
-{
- struct usb_streamzap *sz = (struct usb_streamzap *) arg;
-
- /* finally start accepting data */
- sz->flush = 0;
-}
-static void delay_timeout(unsigned long arg)
-{
- unsigned long flags;
- /* deliver data every 10 ms */
- static unsigned long timer_inc =
- (10000/(1000000/HZ)) == 0 ? 1 : (10000/(1000000/HZ));
- struct usb_streamzap *sz = (struct usb_streamzap *) arg;
- int data;
-
- spin_lock_irqsave(&sz->timer_lock, flags);
-
- if (!lirc_buffer_empty(sz->delay_buf) &&
- !lirc_buffer_full(sz->driver->rbuf)) {
- lirc_buffer_read(sz->delay_buf, (unsigned char *) &data);
- lirc_buffer_write(sz->driver->rbuf, (unsigned char *) &data);
- }
- if (!lirc_buffer_empty(sz->delay_buf)) {
- while (lirc_buffer_available(sz->delay_buf) <
- STREAMZAP_BUF_LEN / 2 &&
- !lirc_buffer_full(sz->driver->rbuf)) {
- lirc_buffer_read(sz->delay_buf,
- (unsigned char *) &data);
- lirc_buffer_write(sz->driver->rbuf,
- (unsigned char *) &data);
- }
- if (sz->timer_running) {
- sz->delay_timer.expires = jiffies + timer_inc;
- add_timer(&sz->delay_timer);
- }
- } else {
- sz->timer_running = 0;
- }
-
- if (!lirc_buffer_empty(sz->driver->rbuf))
- wake_up(&sz->driver->rbuf->wait_poll);
-
- spin_unlock_irqrestore(&sz->timer_lock, flags);
-}
-
-static void flush_delay_buffer(struct usb_streamzap *sz)
-{
- int data;
- int empty = 1;
-
- while (!lirc_buffer_empty(sz->delay_buf)) {
- empty = 0;
- lirc_buffer_read(sz->delay_buf, (unsigned char *) &data);
- if (!lirc_buffer_full(sz->driver->rbuf)) {
- lirc_buffer_write(sz->driver->rbuf,
- (unsigned char *) &data);
- } else {
- dprintk("buffer overflow", sz->driver->minor);
- }
- }
- if (!empty)
- wake_up(&sz->driver->rbuf->wait_poll);
-}
-
-static void push(struct usb_streamzap *sz, unsigned char *data)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&sz->timer_lock, flags);
- if (lirc_buffer_full(sz->delay_buf)) {
- int read_data;
-
- lirc_buffer_read(sz->delay_buf,
- (unsigned char *) &read_data);
- if (!lirc_buffer_full(sz->driver->rbuf)) {
- lirc_buffer_write(sz->driver->rbuf,
- (unsigned char *) &read_data);
- } else {
- dprintk("buffer overflow", sz->driver->minor);
- }
- }
-
- lirc_buffer_write(sz->delay_buf, data);
-
- if (!sz->timer_running) {
- sz->delay_timer.expires = jiffies + HZ/10;
- add_timer(&sz->delay_timer);
- sz->timer_running = 1;
- }
-
- spin_unlock_irqrestore(&sz->timer_lock, flags);
-}
-
-static void push_full_pulse(struct usb_streamzap *sz,
- unsigned char value)
-{
- int pulse;
-
- if (sz->idle) {
- long deltv;
- int tmp;
-
- sz->signal_last = sz->signal_start;
- do_gettimeofday(&sz->signal_start);
-
- deltv = sz->signal_start.tv_sec-sz->signal_last.tv_sec;
- if (deltv > 15) {
- /* really long time */
- tmp = LIRC_SPACE(LIRC_VALUE_MASK);
- } else {
- tmp = (int) (deltv*1000000+
- sz->signal_start.tv_usec -
- sz->signal_last.tv_usec);
- tmp -= sz->sum;
- tmp = LIRC_SPACE(tmp);
- }
- dprintk("ls %u", sz->driver->minor, tmp);
- push(sz, (char *)&tmp);
-
- sz->idle = 0;
- sz->sum = 0;
- }
-
- pulse = ((int) value) * STREAMZAP_RESOLUTION;
- pulse += STREAMZAP_RESOLUTION / 2;
- sz->sum += pulse;
- pulse = LIRC_PULSE(pulse);
-
- dprintk("p %u", sz->driver->minor, pulse & PULSE_MASK);
- push(sz, (char *)&pulse);
-}
-
-static void push_half_pulse(struct usb_streamzap *sz,
- unsigned char value)
-{
- push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK)>>4);
-}
-
-static void push_full_space(struct usb_streamzap *sz,
- unsigned char value)
-{
- int space;
-
- space = ((int) value)*STREAMZAP_RESOLUTION;
- space += STREAMZAP_RESOLUTION/2;
- sz->sum += space;
- space = LIRC_SPACE(space);
- dprintk("s %u", sz->driver->minor, space);
- push(sz, (char *)&space);
-}
-
-static void push_half_space(struct usb_streamzap *sz,
- unsigned char value)
-{
- push_full_space(sz, value & STREAMZAP_SPACE_MASK);
-}
-
-/**
- * usb_streamzap_irq - IRQ handler
- *
- * This procedure is invoked on reception of data from
- * the usb remote.
- */
-static void usb_streamzap_irq(struct urb *urb)
-{
- struct usb_streamzap *sz;
- int len;
- unsigned int i = 0;
-
- if (!urb)
- return;
-
- sz = urb->context;
- len = urb->actual_length;
-
- switch (urb->status) {
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /*
- * this urb is terminated, clean up.
- * sz might already be invalid at this point
- */
- dprintk("urb status: %d", -1, urb->status);
- return;
- default:
- break;
- }
-
- dprintk("received %d", sz->driver->minor, urb->actual_length);
- if (!sz->flush) {
- for (i = 0; i < urb->actual_length; i++) {
- dprintk("%d: %x", sz->driver->minor,
- i, (unsigned char) sz->buf_in[i]);
- switch (sz->decoder_state) {
- case PulseSpace:
- if ((sz->buf_in[i]&STREAMZAP_PULSE_MASK) ==
- STREAMZAP_PULSE_MASK) {
- sz->decoder_state = FullPulse;
- continue;
- } else if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK)
- == STREAMZAP_SPACE_MASK) {
- push_half_pulse(sz, sz->buf_in[i]);
- sz->decoder_state = FullSpace;
- continue;
- } else {
- push_half_pulse(sz, sz->buf_in[i]);
- push_half_space(sz, sz->buf_in[i]);
- }
- break;
- case FullPulse:
- push_full_pulse(sz, sz->buf_in[i]);
- sz->decoder_state = IgnorePulse;
- break;
- case FullSpace:
- if (sz->buf_in[i] == STREAMZAP_TIMEOUT) {
- sz->idle = 1;
- stop_timer(sz);
- if (sz->timeout_enabled) {
- int timeout =
- LIRC_TIMEOUT
- (STREAMZAP_TIMEOUT *
- STREAMZAP_RESOLUTION);
- push(sz, (char *)&timeout);
- }
- flush_delay_buffer(sz);
- } else
- push_full_space(sz, sz->buf_in[i]);
- sz->decoder_state = PulseSpace;
- break;
- case IgnorePulse:
- if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK) ==
- STREAMZAP_SPACE_MASK) {
- sz->decoder_state = FullSpace;
- continue;
- }
- push_half_space(sz, sz->buf_in[i]);
- sz->decoder_state = PulseSpace;
- break;
- }
- }
- }
-
- usb_submit_urb(urb, GFP_ATOMIC);
-
- return;
-}
-
-static const struct file_operations streamzap_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = streamzap_ioctl,
- .read = lirc_dev_fop_read,
- .write = lirc_dev_fop_write,
- .poll = lirc_dev_fop_poll,
- .open = lirc_dev_fop_open,
- .release = lirc_dev_fop_close,
-};
-
-
-/**
- * streamzap_probe
- *
- * Called by usb-core to associated with a candidate device
- * On any failure the return value is the ERROR
- * On success return 0
- */
-static int streamzap_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
-{
- struct usb_device *udev = interface_to_usbdev(interface);
- struct usb_host_interface *iface_host;
- struct usb_streamzap *sz;
- struct lirc_driver *driver;
- struct lirc_buffer *lirc_buf;
- struct lirc_buffer *delay_buf;
- char buf[63], name[128] = "";
- int retval = -ENOMEM;
- int minor = 0;
-
- /* Allocate space for device driver specific data */
- sz = kzalloc(sizeof(struct usb_streamzap), GFP_KERNEL);
- if (sz == NULL)
- return -ENOMEM;
-
- sz->udev = udev;
- sz->interface = interface;
-
- /* Check to ensure endpoint information matches requirements */
- iface_host = interface->cur_altsetting;
-
- if (iface_host->desc.bNumEndpoints != 1) {
- err("%s: Unexpected desc.bNumEndpoints (%d)", __func__,
- iface_host->desc.bNumEndpoints);
- retval = -ENODEV;
- goto free_sz;
- }
-
- sz->endpoint = &(iface_host->endpoint[0].desc);
- if ((sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
- != USB_DIR_IN) {
- err("%s: endpoint doesn't match input device 02%02x",
- __func__, sz->endpoint->bEndpointAddress);
- retval = -ENODEV;
- goto free_sz;
- }
-
- if ((sz->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- != USB_ENDPOINT_XFER_INT) {
- err("%s: endpoint attributes don't match xfer 02%02x",
- __func__, sz->endpoint->bmAttributes);
- retval = -ENODEV;
- goto free_sz;
- }
-
- if (sz->endpoint->wMaxPacketSize == 0) {
- err("%s: endpoint message size==0? ", __func__);
- retval = -ENODEV;
- goto free_sz;
- }
-
- /* Allocate the USB buffer and IRQ URB */
-
- sz->buf_in_len = sz->endpoint->wMaxPacketSize;
- sz->buf_in = usb_alloc_coherent(sz->udev, sz->buf_in_len,
- GFP_ATOMIC, &sz->dma_in);
- if (sz->buf_in == NULL)
- goto free_sz;
-
- sz->urb_in = usb_alloc_urb(0, GFP_KERNEL);
- if (sz->urb_in == NULL)
- goto free_sz;
-
- /* Connect this device to the LIRC sub-system */
- driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
- if (!driver)
- goto free_sz;
-
- lirc_buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
- if (!lirc_buf)
- goto free_driver;
- if (lirc_buffer_init(lirc_buf, sizeof(int), STREAMZAP_BUF_LEN))
- goto kfree_lirc_buf;
-
- delay_buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
- if (!delay_buf)
- goto free_lirc_buf;
- if (lirc_buffer_init(delay_buf, sizeof(int), STREAMZAP_BUF_LEN))
- goto kfree_delay_buf;
-
- sz->driver = driver;
- strcpy(sz->driver->name, DRIVER_NAME);
- sz->driver->minor = -1;
- sz->driver->sample_rate = 0;
- sz->driver->code_length = sizeof(int) * 8;
- sz->driver->features = LIRC_CAN_REC_MODE2 |
- LIRC_CAN_GET_REC_RESOLUTION |
- LIRC_CAN_SET_REC_TIMEOUT;
- sz->driver->data = sz;
- sz->driver->min_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION;
- sz->driver->max_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION;
- sz->driver->rbuf = lirc_buf;
- sz->delay_buf = delay_buf;
- sz->driver->set_use_inc = &streamzap_use_inc;
- sz->driver->set_use_dec = &streamzap_use_dec;
- sz->driver->fops = &streamzap_fops;
- sz->driver->dev = &interface->dev;
- sz->driver->owner = THIS_MODULE;
-
- sz->idle = 1;
- sz->decoder_state = PulseSpace;
- init_timer(&sz->delay_timer);
- sz->delay_timer.function = delay_timeout;
- sz->delay_timer.data = (unsigned long) sz;
- sz->timer_running = 0;
- spin_lock_init(&sz->timer_lock);
-
- init_timer(&sz->flush_timer);
- sz->flush_timer.function = flush_timeout;
- sz->flush_timer.data = (unsigned long) sz;
- /* Complete final initialisations */
-
- usb_fill_int_urb(sz->urb_in, udev,
- usb_rcvintpipe(udev, sz->endpoint->bEndpointAddress),
- sz->buf_in, sz->buf_in_len, usb_streamzap_irq, sz,
- sz->endpoint->bInterval);
- sz->urb_in->transfer_dma = sz->dma_in;
- sz->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-
- if (udev->descriptor.iManufacturer
- && usb_string(udev, udev->descriptor.iManufacturer,
- buf, sizeof(buf)) > 0)
- strlcpy(name, buf, sizeof(name));
-
- if (udev->descriptor.iProduct
- && usb_string(udev, udev->descriptor.iProduct,
- buf, sizeof(buf)) > 0)
- snprintf(name + strlen(name), sizeof(name) - strlen(name),
- " %s", buf);
-
- minor = lirc_register_driver(driver);
-
- if (minor < 0)
- goto free_delay_buf;
-
- sz->driver->minor = minor;
-
- usb_set_intfdata(interface, sz);
-
- printk(KERN_INFO DRIVER_NAME "[%d]: %s on usb%d:%d attached\n",
- sz->driver->minor, name,
- udev->bus->busnum, sz->udev->devnum);
-
- return 0;
-
-free_delay_buf:
- lirc_buffer_free(sz->delay_buf);
-kfree_delay_buf:
- kfree(delay_buf);
-free_lirc_buf:
- lirc_buffer_free(sz->driver->rbuf);
-kfree_lirc_buf:
- kfree(lirc_buf);
-free_driver:
- kfree(driver);
-free_sz:
- if (retval == -ENOMEM)
- err("Out of memory");
-
- if (sz) {
- usb_free_urb(sz->urb_in);
- usb_free_coherent(udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
- kfree(sz);
- }
-
- return retval;
-}
-
-static int streamzap_use_inc(void *data)
-{
- struct usb_streamzap *sz = data;
-
- if (!sz) {
- dprintk("%s called with no context", -1, __func__);
- return -EINVAL;
- }
- dprintk("set use inc", sz->driver->minor);
-
- lirc_buffer_clear(sz->driver->rbuf);
- lirc_buffer_clear(sz->delay_buf);
-
- sz->flush_timer.expires = jiffies + HZ;
- sz->flush = 1;
- add_timer(&sz->flush_timer);
-
- sz->urb_in->dev = sz->udev;
- if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
- dprintk("open result = -EIO error submitting urb",
- sz->driver->minor);
- return -EIO;
- }
- sz->in_use++;
-
- return 0;
-}
-
-static void streamzap_use_dec(void *data)
-{
- struct usb_streamzap *sz = data;
-
- if (!sz) {
- dprintk("%s called with no context", -1, __func__);
- return;
- }
- dprintk("set use dec", sz->driver->minor);
-
- if (sz->flush) {
- sz->flush = 0;
- del_timer_sync(&sz->flush_timer);
- }
-
- usb_kill_urb(sz->urb_in);
-
- stop_timer(sz);
-
- sz->in_use--;
-}
-
-static long streamzap_ioctl(struct file *filep, unsigned int cmd,
- unsigned long arg)
-{
- int result = 0;
- int val;
- struct usb_streamzap *sz = lirc_get_pdata(filep);
-
- switch (cmd) {
- case LIRC_GET_REC_RESOLUTION:
- result = put_user(STREAMZAP_RESOLUTION, (unsigned int *) arg);
- break;
- case LIRC_SET_REC_TIMEOUT:
- result = get_user(val, (int *)arg);
- if (result == 0) {
- if (val == STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION)
- sz->timeout_enabled = 1;
- else if (val == 0)
- sz->timeout_enabled = 0;
- else
- result = -EINVAL;
- }
- break;
- default:
- return lirc_dev_fop_ioctl(filep, cmd, arg);
- }
- return result;
-}
-
-/**
- * streamzap_disconnect
- *
- * Called by the usb core when the device is removed from the system.
- *
- * This routine guarantees that the driver will not submit any more urbs
- * by clearing dev->udev. It is also supposed to terminate any currently
- * active urbs. Unfortunately, usb_bulk_msg(), used in streamzap_read(),
- * does not provide any way to do this.
- */
-static void streamzap_disconnect(struct usb_interface *interface)
-{
- struct usb_streamzap *sz;
- int errnum;
- int minor;
-
- sz = usb_get_intfdata(interface);
-
- /* unregister from the LIRC sub-system */
-
- errnum = lirc_unregister_driver(sz->driver->minor);
- if (errnum != 0)
- dprintk("error in lirc_unregister: (returned %d)",
- sz->driver->minor, errnum);
-
- lirc_buffer_free(sz->delay_buf);
- lirc_buffer_free(sz->driver->rbuf);
-
- /* unregister from the USB sub-system */
-
- usb_free_urb(sz->urb_in);
-
- usb_free_coherent(sz->udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
-
- minor = sz->driver->minor;
- kfree(sz->driver->rbuf);
- kfree(sz->driver);
- kfree(sz->delay_buf);
- kfree(sz);
-
- printk(KERN_INFO DRIVER_NAME "[%d]: disconnected\n", minor);
-}
-
-static int streamzap_suspend(struct usb_interface *intf, pm_message_t message)
-{
- struct usb_streamzap *sz = usb_get_intfdata(intf);
-
- printk(KERN_INFO DRIVER_NAME "[%d]: suspend\n", sz->driver->minor);
- if (sz->in_use) {
- if (sz->flush) {
- sz->flush = 0;
- del_timer_sync(&sz->flush_timer);
- }
-
- stop_timer(sz);
-
- usb_kill_urb(sz->urb_in);
- }
- return 0;
-}
-
-static int streamzap_resume(struct usb_interface *intf)
-{
- struct usb_streamzap *sz = usb_get_intfdata(intf);
-
- lirc_buffer_clear(sz->driver->rbuf);
- lirc_buffer_clear(sz->delay_buf);
-
- if (sz->in_use) {
- sz->flush_timer.expires = jiffies + HZ;
- sz->flush = 1;
- add_timer(&sz->flush_timer);
-
- sz->urb_in->dev = sz->udev;
- if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
- dprintk("open result = -EIO error submitting urb",
- sz->driver->minor);
- return -EIO;
- }
- }
- return 0;
-}
-
-/**
- * usb_streamzap_init
- */
-static int __init usb_streamzap_init(void)
-{
- int result;
-
- /* register this driver with the USB subsystem */
- result = usb_register(&streamzap_driver);
-
- if (result) {
- err("usb_register failed. Error number %d",
- result);
- return result;
- }
-
- printk(KERN_INFO DRIVER_NAME " " DRIVER_VERSION " registered\n");
- return 0;
-}
-
-/**
- * usb_streamzap_exit
- */
-static void __exit usb_streamzap_exit(void)
-{
- usb_deregister(&streamzap_driver);
-}
-
-
-module_init(usb_streamzap_init);
-module_exit(usb_streamzap_exit);
-
-MODULE_AUTHOR("Christoph Bartelmus, Greg Wickham, Adrian Dewhurst");
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Enable debugging messages");
vhci = hcd_to_vhci(hcd);
spin_lock_irqsave(&vhci->lock, flags);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
usbip_dbg_vhci_rh("hw accessible flag in on?\n");
goto done;
}
u32 prev_port_status[VHCI_NPORTS];
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ if (!HCD_HW_ACCESSIBLE(hcd))
return -ETIMEDOUT;
/*
dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__);
spin_lock_irq(&vhci->lock);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
rc = -ESHUTDOWN;
} else {
/* vhci->rh_state = DUMMY_RH_RUNNING;
obj-$(CONFIG_USB_SERIAL) += serial/
obj-$(CONFIG_USB) += misc/
-obj-y += early/
+obj-$(CONFIG_EARLY_PRINTK_DBGP) += early/
obj-$(CONFIG_USB_ATM) += atm/
obj-$(CONFIG_USB_SPEEDTOUCH) += atm/
}
static int cxacru_start_wait_urb(struct urb *urb, struct completion *done,
- int* actual_length)
+ int *actual_length)
{
struct timer_list timer;
put_unaligned(cpu_to_le32(addr), (__le32 *)(buf + offb));
offb += 4;
addr += l;
- if(l)
+ if (l)
memcpy(buf + offb, data + offd, l);
if (l < stride)
memset(buf + offb + l, 0, stride - l);
}
offb = 0;
}
- } while(offd < size);
+ } while (offd < size);
dbg("sent fw %#x", fw);
ret = 0;
if (instance->modem_type->boot_rom_patch) {
val = cpu_to_le32(BR_ADDR);
ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, BR_STACK_ADDR, (u8 *) &val, 4);
- }
- else {
+ } else {
ret = cxacru_fw(usb_dev, FW_GOTO_MEM, 0x0, 0x0, FW_ADDR, NULL, 0);
}
if (ret) {
}
static int cxacru_find_firmware(struct cxacru_data *instance,
- char* phase, const struct firmware **fw_p)
+ char *phase, const struct firmware **fw_p)
{
struct usbatm_data *usbatm = instance->usbatm;
struct device *dev = &usbatm->usb_intf->dev;
.id_table = speedtch_usb_ids
};
-static void speedtch_release_interfaces(struct usb_device *usb_dev, int num_interfaces) {
+static void speedtch_release_interfaces(struct usb_device *usb_dev,
+ int num_interfaces)
+{
struct usb_interface *cur_intf;
int i;
- for(i = 0; i < num_interfaces; i++)
+ for (i = 0; i < num_interfaces; i++)
if ((cur_intf = usb_ifnum_to_if(usb_dev, i))) {
usb_set_intfdata(cur_intf, NULL);
usb_driver_release_interface(&speedtch_usb_driver, cur_intf);
/* claim all interfaces */
- for (i=0; i < num_interfaces; i++) {
+ for (i = 0; i < num_interfaces; i++) {
cur_intf = usb_ifnum_to_if(usb_dev, i);
if ((i != ifnum) && cur_intf) {
use_isoc = 0; /* fall back to bulk if endpoint not found */
- for (i=0; i<desc->desc.bNumEndpoints; i++) {
+ for (i = 0; i < desc->desc.bNumEndpoints; i++) {
const struct usb_endpoint_descriptor *endpoint_desc = &desc->endpoint[i].desc;
if ((endpoint_desc->bEndpointAddress == target_address)) {
#include <linux/mutex.h>
#include <linux/freezer.h>
#include <linux/slab.h>
+#include <linux/kernel.h>
#include <asm/unaligned.h>
/* Retrieve the device End System Identifier (MAC) */
-#define htoi(x) (isdigit(x) ? x-'0' : toupper(x)-'A'+10)
static int uea_getesi(struct uea_softc *sc, u_char * esi)
{
unsigned char mac_str[2 * ETH_ALEN + 1];
return 1;
for (i = 0; i < ETH_ALEN; i++)
- esi[i] = htoi(mac_str[2 * i]) * 16 + htoi(mac_str[2 * i + 1]);
+ esi[i] = hex_to_bin(mac_str[2 * i]) * 16 +
+ hex_to_bin(mac_str[2 * i + 1]);
return 0;
}
#ifdef VERBOSE_DEBUG
static int usbatm_print_packet(const unsigned char *data, int len);
-#define PACKETDEBUG(arg...) usbatm_print_packet (arg)
-#define vdbg(arg...) dbg (arg)
+#define PACKETDEBUG(arg...) usbatm_print_packet(arg)
+#define vdbg(arg...) dbg(arg)
#else
#define PACKETDEBUG(arg...)
#define vdbg(arg...)
if (unlikely(status) &&
(!(channel->usbatm->flags & UDSL_IGNORE_EILSEQ) ||
- status != -EILSEQ ))
- {
+ status != -EILSEQ)) {
if (status == -ESHUTDOWN)
return;
ptr += data_len;
__skb_pull(skb, data_len);
- if(!left)
+ if (!left)
continue;
memset(ptr, 0, left);
trailer[2] = ctrl->len >> 8;
trailer[3] = ctrl->len;
- ctrl->crc = ~ crc32_be(ctrl->crc, ptr, left - 4);
+ ctrl->crc = ~crc32_be(ctrl->crc, ptr, left - 4);
trailer[4] = ctrl->crc >> 24;
trailer[5] = ctrl->crc >> 16;
target[3] |= 0x2; /* adjust PTI */
ctrl->len = 0; /* tag this skb finished */
- }
- else
+ } else
ctrl->crc = crc32_be(ctrl->crc, ptr, left);
}
instance->tx_channel.endpoint = usb_sndbulkpipe(usb_dev, driver->bulk_out);
/* tx buffer size must be a positive multiple of the stride */
- instance->tx_channel.buf_size = max (instance->tx_channel.stride,
+ instance->tx_channel.buf_size = max(instance->tx_channel.stride,
snd_buf_bytes - (snd_buf_bytes % instance->tx_channel.stride));
/* rx buffer size must be a positive multiple of the endpoint maxpacket */
goto fail_unbind;
}
- num_packets = max (1U, (rcv_buf_bytes + maxpacket / 2) / maxpacket); /* round */
+ num_packets = max(1U, (rcv_buf_bytes + maxpacket / 2) / maxpacket); /* round */
if (num_packets * maxpacket > UDSL_MAX_BUF_SIZE)
num_packets--;
usb_free_urb(instance->urbs[i]);
}
- kfree (instance);
+ kfree(instance);
return error;
}
for (i = 0; i < len;) {
buffer[0] = '\0';
sprintf(buffer, "%.3d :", i);
- for (j = 0; (j < 16) && (i < len); j++, i++) {
+ for (j = 0; (j < 16) && (i < len); j++, i++)
sprintf(buffer, "%s %2.2x", buffer, data[i]);
- }
dbg("%s", buffer);
}
return i;
dev_warn(&(instance)->usb_intf->dev, \
"failed assertion '%s' at line %d", \
__stringify(x), __LINE__); \
- } while(0)
+ } while (0)
#endif
#define usb_err(instance, format, arg...) \
dev_warn(&(instance)->usb_intf->dev , format , ## arg)
#ifdef DEBUG
#define usb_dbg(instance, format, arg...) \
- dev_printk(KERN_DEBUG , &(instance)->usb_intf->dev , format , ## arg)
+ dev_printk(KERN_DEBUG , &(instance)->usb_intf->dev , format , ## arg)
#else
#define usb_dbg(instance, format, arg...) \
do {} while (0)
/*
* Assuming all methods exist and succeed, they are called in this order:
*
-* bind, heavy_init, atm_start, ..., atm_stop, unbind
+* bind, heavy_init, atm_start, ..., atm_stop, unbind
*/
struct usbatm_driver {
const char *driver_name;
/* init device ... can sleep, or cause probe() failure */
- int (*bind) (struct usbatm_data *, struct usb_interface *,
+ int (*bind) (struct usbatm_data *, struct usb_interface *,
const struct usb_device_id *id);
/* additional device initialization that is too slow to be done in probe() */
- int (*heavy_init) (struct usbatm_data *, struct usb_interface *);
+ int (*heavy_init) (struct usbatm_data *, struct usb_interface *);
/* cleanup device ... can sleep, but can't fail */
- void (*unbind) (struct usbatm_data *, struct usb_interface *);
+ void (*unbind) (struct usbatm_data *, struct usb_interface *);
/* init ATM device ... can sleep, or cause ATM initialization failure */
int (*atm_start) (struct usbatm_data *, struct atm_dev *);
/* cleanup ATM device ... can sleep, but can't fail */
void (*atm_stop) (struct usbatm_data *, struct atm_dev *);
- int bulk_in; /* bulk rx endpoint */
- int isoc_in; /* isochronous rx endpoint */
- int bulk_out; /* bulk tx endpoint */
+ int bulk_in; /* bulk rx endpoint */
+ int isoc_in; /* isochronous rx endpoint */
+ int bulk_out; /* bulk tx endpoint */
unsigned rx_padding;
unsigned tx_padding;
struct usbatm_data {
/******************
* public fields *
- ******************/
+ ******************/
/* mini driver */
struct usbatm_driver *driver;
/********************************
* private fields - do not use *
- ********************************/
+ ********************************/
struct kref refcount;
struct mutex serialize;
static struct usb_device_id xusbatm_usb_ids[XUSBATM_DRIVERS_MAX + 1];
static struct usb_driver xusbatm_usb_driver;
-static struct usb_interface *xusbatm_find_intf (struct usb_device *usb_dev, int altsetting, u8 ep)
+static struct usb_interface *xusbatm_find_intf(struct usb_device *usb_dev, int altsetting, u8 ep)
{
struct usb_host_interface *alt;
struct usb_interface *intf;
int i, j;
- for(i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++)
+ for (i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++)
if ((intf = usb_dev->actconfig->interface[i]) && (alt = usb_altnum_to_altsetting(intf, altsetting)))
for (j = 0; j < alt->desc.bNumEndpoints; j++)
if (alt->endpoint[j].desc.bEndpointAddress == ep)
return NULL;
}
-static int xusbatm_capture_intf (struct usbatm_data *usbatm, struct usb_device *usb_dev,
+static int xusbatm_capture_intf(struct usbatm_data *usbatm, struct usb_device *usb_dev,
struct usb_interface *intf, int altsetting, int claim)
{
int ifnum = intf->altsetting->desc.bInterfaceNumber;
return 0;
}
-static void xusbatm_release_intf (struct usb_device *usb_dev, struct usb_interface *intf, int claimed)
+static void xusbatm_release_intf(struct usb_device *usb_dev, struct usb_interface *intf, int claimed)
{
if (claimed) {
usb_set_intfdata(intf, NULL);
usb_dbg(usbatm, "%s entered\n", __func__);
- for(i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) {
+ for (i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) {
struct usb_interface *cur_intf = usb_dev->actconfig->interface[i];
if (cur_intf && (usb_get_intfdata(cur_intf) == usbatm)) {
if (unlikely(hcd->state == HC_STATE_HALT))
return;
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ if (!HCD_HW_ACCESSIBLE(hcd))
return;
/* Handle Start of frame events */
{
hcd->uses_new_polling = 1;
hcd->state = HC_STATE_RUNNING;
- hcd->poll_rh = 1;
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
return 0;
}
static int acm_tty_chars_in_buffer(struct tty_struct *tty);
-static void acm_port_down(struct acm *acm, int drain)
+static void acm_port_down(struct acm *acm)
{
int i, nr = acm->rx_buflimit;
mutex_lock(&open_mutex);
if (acm->dev) {
usb_autopm_get_interface(acm->control);
acm_set_control(acm, acm->ctrlout = 0);
- /* try letting the last writes drain naturally */
- if (drain) {
- wait_event_interruptible_timeout(acm->drain_wait,
- (ACM_NW == acm_wb_is_avail(acm)) || !acm->dev,
- ACM_CLOSE_TIMEOUT * HZ);
- }
usb_kill_urb(acm->ctrlurb);
for (i = 0; i < ACM_NW; i++)
usb_kill_urb(acm->wb[i].urb);
{
struct acm *acm = tty->driver_data;
tty_port_hangup(&acm->port);
- acm_port_down(acm, 0);
+ acm_port_down(acm);
}
static void acm_tty_close(struct tty_struct *tty, struct file *filp)
mutex_unlock(&open_mutex);
return;
}
- acm_port_down(acm, 0);
+ acm_port_down(acm);
tty_port_close_end(&acm->port, tty);
tty_port_tty_set(&acm->port, NULL);
}
* ->lock locks what interrupt accesses.
*/
struct usblp {
- struct usb_device *dev; /* USB device */
+ struct usb_device *dev; /* USB device */
struct mutex wmut;
struct mutex mut;
spinlock_t lock; /* locks rcomplete, wcomplete */
};
#ifdef DEBUG
-static void usblp_dump(struct usblp *usblp) {
+static void usblp_dump(struct usblp *usblp)
+{
int p;
dbg("usblp=0x%p", usblp);
{ 0x03f0, 0x0304, USBLP_QUIRK_BIDIR }, /* HP DeskJet 810C/812C */
{ 0x03f0, 0x0404, USBLP_QUIRK_BIDIR }, /* HP DeskJet 830C */
{ 0x03f0, 0x0504, USBLP_QUIRK_BIDIR }, /* HP DeskJet 885C */
- { 0x03f0, 0x0604, USBLP_QUIRK_BIDIR }, /* HP DeskJet 840C */
- { 0x03f0, 0x0804, USBLP_QUIRK_BIDIR }, /* HP DeskJet 816C */
+ { 0x03f0, 0x0604, USBLP_QUIRK_BIDIR }, /* HP DeskJet 840C */
+ { 0x03f0, 0x0804, USBLP_QUIRK_BIDIR }, /* HP DeskJet 816C */
{ 0x03f0, 0x1104, USBLP_QUIRK_BIDIR }, /* HP Deskjet 959C */
{ 0x0409, 0xefbe, USBLP_QUIRK_BIDIR }, /* NEC Picty900 (HP OEM) */
{ 0x0409, 0xbef4, USBLP_QUIRK_BIDIR }, /* NEC Picty760 (HP OEM) */
/* High byte has the interface index.
Low byte has the alternate setting.
*/
- if ((request == USBLP_REQ_GET_ID) && (type == USB_TYPE_CLASS)) {
- index = (usblp->ifnum<<8)|usblp->protocol[usblp->current_protocol].alt_setting;
- }
+ if ((request == USBLP_REQ_GET_ID) && (type == USB_TYPE_CLASS))
+ index = (usblp->ifnum<<8)|usblp->protocol[usblp->current_protocol].alt_setting;
retval = usb_control_msg(usblp->dev,
dir ? usb_rcvctrlpipe(usblp->dev, 0) : usb_sndctrlpipe(usblp->dev, 0),
return newerr;
}
-static int handle_bidir (struct usblp *usblp)
+static int handle_bidir(struct usblp *usblp)
{
if (usblp->bidir && usblp->used) {
if (usblp_submit_read(usblp) < 0)
if (minor < 0)
return -ENODEV;
- mutex_lock (&usblp_mutex);
+ mutex_lock(&usblp_mutex);
retval = -ENODEV;
intf = usb_find_interface(&usblp_driver, minor);
- if (!intf) {
+ if (!intf)
goto out;
- }
- usblp = usb_get_intfdata (intf);
+ usblp = usb_get_intfdata(intf);
if (!usblp || !usblp->dev || !usblp->present)
goto out;
retval = -EIO;
}
out:
- mutex_unlock (&usblp_mutex);
+ mutex_unlock(&usblp_mutex);
return retval;
}
-static void usblp_cleanup (struct usblp *usblp)
+static void usblp_cleanup(struct usblp *usblp)
{
printk(KERN_INFO "usblp%d: removed\n", usblp->minor);
kfree(usblp->readbuf);
- kfree (usblp->device_id_string);
- kfree (usblp->statusbuf);
- kfree (usblp);
+ kfree(usblp->device_id_string);
+ kfree(usblp->statusbuf);
+ kfree(usblp);
}
static void usblp_unlink_urbs(struct usblp *usblp)
usblp->flags &= ~LP_ABORT;
- mutex_lock (&usblp_mutex);
+ mutex_lock(&usblp_mutex);
usblp->used = 0;
if (usblp->present) {
usblp_unlink_urbs(usblp);
usb_autopm_put_interface(usblp->intf);
- } else /* finish cleanup from disconnect */
- usblp_cleanup (usblp);
- mutex_unlock (&usblp_mutex);
+ } else /* finish cleanup from disconnect */
+ usblp_cleanup(usblp);
+ mutex_unlock(&usblp_mutex);
return 0;
}
int twoints[2];
int retval = 0;
- mutex_lock (&usblp->mut);
+ mutex_lock(&usblp->mut);
if (!usblp->present) {
retval = -ENODEV;
goto done;
}
dbg("usblp_ioctl: cmd=0x%x (%c nr=%d len=%d dir=%d)", cmd, _IOC_TYPE(cmd),
- _IOC_NR(cmd), _IOC_SIZE(cmd), _IOC_DIR(cmd) );
+ _IOC_NR(cmd), _IOC_SIZE(cmd), _IOC_DIR(cmd));
if (_IOC_TYPE(cmd) == 'P') /* new-style ioctl number */
switch (_IOC_NR(cmd)) {
- case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */
- if (_IOC_DIR(cmd) != _IOC_READ) {
- retval = -EINVAL;
- goto done;
- }
+ case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */
+ if (_IOC_DIR(cmd) != _IOC_READ) {
+ retval = -EINVAL;
+ goto done;
+ }
- length = usblp_cache_device_id_string(usblp);
- if (length < 0) {
- retval = length;
- goto done;
- }
- if (length > _IOC_SIZE(cmd))
- length = _IOC_SIZE(cmd); /* truncate */
-
- if (copy_to_user((void __user *) arg,
- usblp->device_id_string,
- (unsigned long) length)) {
- retval = -EFAULT;
- goto done;
- }
+ length = usblp_cache_device_id_string(usblp);
+ if (length < 0) {
+ retval = length;
+ goto done;
+ }
+ if (length > _IOC_SIZE(cmd))
+ length = _IOC_SIZE(cmd); /* truncate */
+
+ if (copy_to_user((void __user *) arg,
+ usblp->device_id_string,
+ (unsigned long) length)) {
+ retval = -EFAULT;
+ goto done;
+ }
- break;
+ break;
- case IOCNR_GET_PROTOCOLS:
- if (_IOC_DIR(cmd) != _IOC_READ ||
- _IOC_SIZE(cmd) < sizeof(twoints)) {
- retval = -EINVAL;
- goto done;
- }
+ case IOCNR_GET_PROTOCOLS:
+ if (_IOC_DIR(cmd) != _IOC_READ ||
+ _IOC_SIZE(cmd) < sizeof(twoints)) {
+ retval = -EINVAL;
+ goto done;
+ }
- twoints[0] = usblp->current_protocol;
- twoints[1] = 0;
- for (i = USBLP_FIRST_PROTOCOL;
- i <= USBLP_LAST_PROTOCOL; i++) {
- if (usblp->protocol[i].alt_setting >= 0)
- twoints[1] |= (1<<i);
- }
+ twoints[0] = usblp->current_protocol;
+ twoints[1] = 0;
+ for (i = USBLP_FIRST_PROTOCOL;
+ i <= USBLP_LAST_PROTOCOL; i++) {
+ if (usblp->protocol[i].alt_setting >= 0)
+ twoints[1] |= (1<<i);
+ }
- if (copy_to_user((void __user *)arg,
- (unsigned char *)twoints,
- sizeof(twoints))) {
- retval = -EFAULT;
- goto done;
- }
+ if (copy_to_user((void __user *)arg,
+ (unsigned char *)twoints,
+ sizeof(twoints))) {
+ retval = -EFAULT;
+ goto done;
+ }
- break;
+ break;
- case IOCNR_SET_PROTOCOL:
- if (_IOC_DIR(cmd) != _IOC_WRITE) {
- retval = -EINVAL;
- goto done;
- }
+ case IOCNR_SET_PROTOCOL:
+ if (_IOC_DIR(cmd) != _IOC_WRITE) {
+ retval = -EINVAL;
+ goto done;
+ }
#ifdef DEBUG
- if (arg == -10) {
- usblp_dump(usblp);
- break;
- }
+ if (arg == -10) {
+ usblp_dump(usblp);
+ break;
+ }
#endif
- usblp_unlink_urbs(usblp);
- retval = usblp_set_protocol(usblp, arg);
- if (retval < 0) {
- usblp_set_protocol(usblp,
- usblp->current_protocol);
- }
- break;
+ usblp_unlink_urbs(usblp);
+ retval = usblp_set_protocol(usblp, arg);
+ if (retval < 0) {
+ usblp_set_protocol(usblp,
+ usblp->current_protocol);
+ }
+ break;
- case IOCNR_HP_SET_CHANNEL:
- if (_IOC_DIR(cmd) != _IOC_WRITE ||
- le16_to_cpu(usblp->dev->descriptor.idVendor) != 0x03F0 ||
- usblp->quirks & USBLP_QUIRK_BIDIR) {
- retval = -EINVAL;
- goto done;
- }
+ case IOCNR_HP_SET_CHANNEL:
+ if (_IOC_DIR(cmd) != _IOC_WRITE ||
+ le16_to_cpu(usblp->dev->descriptor.idVendor) != 0x03F0 ||
+ usblp->quirks & USBLP_QUIRK_BIDIR) {
+ retval = -EINVAL;
+ goto done;
+ }
- err = usblp_hp_channel_change_request(usblp,
- arg, &newChannel);
- if (err < 0) {
- dev_err(&usblp->dev->dev,
- "usblp%d: error = %d setting "
- "HP channel\n",
- usblp->minor, err);
- retval = -EIO;
- goto done;
- }
+ err = usblp_hp_channel_change_request(usblp,
+ arg, &newChannel);
+ if (err < 0) {
+ dev_err(&usblp->dev->dev,
+ "usblp%d: error = %d setting "
+ "HP channel\n",
+ usblp->minor, err);
+ retval = -EIO;
+ goto done;
+ }
- dbg("usblp%d requested/got HP channel %ld/%d",
- usblp->minor, arg, newChannel);
- break;
+ dbg("usblp%d requested/got HP channel %ld/%d",
+ usblp->minor, arg, newChannel);
+ break;
- case IOCNR_GET_BUS_ADDRESS:
- if (_IOC_DIR(cmd) != _IOC_READ ||
- _IOC_SIZE(cmd) < sizeof(twoints)) {
- retval = -EINVAL;
- goto done;
- }
+ case IOCNR_GET_BUS_ADDRESS:
+ if (_IOC_DIR(cmd) != _IOC_READ ||
+ _IOC_SIZE(cmd) < sizeof(twoints)) {
+ retval = -EINVAL;
+ goto done;
+ }
- twoints[0] = usblp->dev->bus->busnum;
- twoints[1] = usblp->dev->devnum;
- if (copy_to_user((void __user *)arg,
- (unsigned char *)twoints,
- sizeof(twoints))) {
- retval = -EFAULT;
- goto done;
- }
+ twoints[0] = usblp->dev->bus->busnum;
+ twoints[1] = usblp->dev->devnum;
+ if (copy_to_user((void __user *)arg,
+ (unsigned char *)twoints,
+ sizeof(twoints))) {
+ retval = -EFAULT;
+ goto done;
+ }
- dbg("usblp%d is bus=%d, device=%d",
- usblp->minor, twoints[0], twoints[1]);
- break;
+ dbg("usblp%d is bus=%d, device=%d",
+ usblp->minor, twoints[0], twoints[1]);
+ break;
- case IOCNR_GET_VID_PID:
- if (_IOC_DIR(cmd) != _IOC_READ ||
- _IOC_SIZE(cmd) < sizeof(twoints)) {
- retval = -EINVAL;
- goto done;
- }
+ case IOCNR_GET_VID_PID:
+ if (_IOC_DIR(cmd) != _IOC_READ ||
+ _IOC_SIZE(cmd) < sizeof(twoints)) {
+ retval = -EINVAL;
+ goto done;
+ }
- twoints[0] = le16_to_cpu(usblp->dev->descriptor.idVendor);
- twoints[1] = le16_to_cpu(usblp->dev->descriptor.idProduct);
- if (copy_to_user((void __user *)arg,
- (unsigned char *)twoints,
- sizeof(twoints))) {
- retval = -EFAULT;
- goto done;
- }
+ twoints[0] = le16_to_cpu(usblp->dev->descriptor.idVendor);
+ twoints[1] = le16_to_cpu(usblp->dev->descriptor.idProduct);
+ if (copy_to_user((void __user *)arg,
+ (unsigned char *)twoints,
+ sizeof(twoints))) {
+ retval = -EFAULT;
+ goto done;
+ }
- dbg("usblp%d is VID=0x%4.4X, PID=0x%4.4X",
- usblp->minor, twoints[0], twoints[1]);
- break;
+ dbg("usblp%d is VID=0x%4.4X, PID=0x%4.4X",
+ usblp->minor, twoints[0], twoints[1]);
+ break;
- case IOCNR_SOFT_RESET:
- if (_IOC_DIR(cmd) != _IOC_NONE) {
- retval = -EINVAL;
- goto done;
- }
- retval = usblp_reset(usblp);
- break;
- default:
- retval = -ENOTTY;
+ case IOCNR_SOFT_RESET:
+ if (_IOC_DIR(cmd) != _IOC_NONE) {
+ retval = -EINVAL;
+ goto done;
+ }
+ retval = usblp_reset(usblp);
+ break;
+ default:
+ retval = -ENOTTY;
}
else /* old-style ioctl value */
switch (cmd) {
- case LPGETSTATUS:
- if ((retval = usblp_read_status(usblp, usblp->statusbuf))) {
- if (printk_ratelimit())
- printk(KERN_ERR "usblp%d:"
- "failed reading printer status (%d)\n",
- usblp->minor, retval);
- retval = -EIO;
- goto done;
- }
- status = *usblp->statusbuf;
- if (copy_to_user ((void __user *)arg, &status, sizeof(int)))
- retval = -EFAULT;
- break;
+ case LPGETSTATUS:
+ if ((retval = usblp_read_status(usblp, usblp->statusbuf))) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "usblp%d:"
+ "failed reading printer status (%d)\n",
+ usblp->minor, retval);
+ retval = -EIO;
+ goto done;
+ }
+ status = *usblp->statusbuf;
+ if (copy_to_user((void __user *)arg, &status, sizeof(int)))
+ retval = -EFAULT;
+ break;
- case LPABORT:
- if (arg)
- usblp->flags |= LP_ABORT;
- else
- usblp->flags &= ~LP_ABORT;
- break;
+ case LPABORT:
+ if (arg)
+ usblp->flags |= LP_ABORT;
+ else
+ usblp->flags &= ~LP_ABORT;
+ break;
- default:
- retval = -ENOTTY;
+ default:
+ retval = -ENOTTY;
}
done:
- mutex_unlock (&usblp->mut);
+ mutex_unlock(&usblp->mut);
return retval;
}
}
done:
- mutex_unlock (&usblp->mut);
+ mutex_unlock(&usblp->mut);
return count;
}
* while you are sending print data, and you don't try to query the
* printer status every couple of milliseconds, you will probably be OK.
*/
-static unsigned int usblp_quirks (__u16 vendor, __u16 product)
+static unsigned int usblp_quirks(__u16 vendor, __u16 product)
{
int i;
if (vendor == quirk_printers[i].vendorId &&
product == quirk_printers[i].productId)
return quirk_printers[i].quirks;
- }
+ }
return 0;
}
static ssize_t usblp_show_ieee1284_id(struct device *dev, struct device_attribute *attr, char *buf)
{
struct usb_interface *intf = to_usb_interface(dev);
- struct usblp *usblp = usb_get_intfdata (intf);
+ struct usblp *usblp = usb_get_intfdata(intf);
if (usblp->device_id_string[0] == 0 &&
usblp->device_id_string[1] == 0)
static int usblp_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
- struct usb_device *dev = interface_to_usbdev (intf);
+ struct usb_device *dev = interface_to_usbdev(intf);
struct usblp *usblp;
int protocol;
int retval;
}
usblp->dev = dev;
mutex_init(&usblp->wmut);
- mutex_init (&usblp->mut);
+ mutex_init(&usblp->mut);
spin_lock_init(&usblp->lock);
init_waitqueue_head(&usblp->rwait);
init_waitqueue_head(&usblp->wwait);
usblp_check_status(usblp, 0);
#endif
- usb_set_intfdata (intf, usblp);
+ usb_set_intfdata(intf, usblp);
usblp->present = 1;
return 0;
abort_intfdata:
- usb_set_intfdata (intf, NULL);
+ usb_set_intfdata(intf, NULL);
device_remove_file(&intf->dev, &dev_attr_ieee1284_id);
abort:
kfree(usblp->readbuf);
static void usblp_disconnect(struct usb_interface *intf)
{
- struct usblp *usblp = usb_get_intfdata (intf);
+ struct usblp *usblp = usb_get_intfdata(intf);
usb_deregister_dev(intf, &usblp_class);
if (!usblp || !usblp->dev) {
dev_err(&intf->dev, "bogus disconnect\n");
- BUG ();
+ BUG();
}
device_remove_file(&intf->dev, &dev_attr_ieee1284_id);
- mutex_lock (&usblp_mutex);
- mutex_lock (&usblp->mut);
+ mutex_lock(&usblp_mutex);
+ mutex_lock(&usblp->mut);
usblp->present = 0;
wake_up(&usblp->wwait);
wake_up(&usblp->rwait);
- usb_set_intfdata (intf, NULL);
+ usb_set_intfdata(intf, NULL);
usblp_unlink_urbs(usblp);
- mutex_unlock (&usblp->mut);
+ mutex_unlock(&usblp->mut);
if (!usblp->used)
- usblp_cleanup (usblp);
- mutex_unlock (&usblp_mutex);
+ usblp_cleanup(usblp);
+ mutex_unlock(&usblp_mutex);
}
static int usblp_suspend(struct usb_interface *intf, pm_message_t message)
{
- struct usblp *usblp = usb_get_intfdata (intf);
+ struct usblp *usblp = usb_get_intfdata(intf);
usblp_unlink_urbs(usblp);
#if 0 /* XXX Do we want this? What if someone is reading, should we fail? */
static int usblp_resume(struct usb_interface *intf)
{
- struct usblp *usblp = usb_get_intfdata (intf);
+ struct usblp *usblp = usb_get_intfdata(intf);
int r;
- r = handle_bidir (usblp);
+ r = handle_bidir(usblp);
return r;
}
{ } /* Terminating entry */
};
-MODULE_DEVICE_TABLE (usb, usblp_ids);
+MODULE_DEVICE_TABLE(usb, usblp_ids);
static struct usb_driver usblp_driver = {
.name = "usblp",
module_init(usblp_init);
module_exit(usblp_exit);
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
module_param(proto_bias, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(proto_bias, "Favourite protocol number");
MODULE_LICENSE("GPL");
default:
if (intf->dev.driver)
driver = to_usb_driver(intf->dev.driver);
- if (driver == NULL || driver->ioctl == NULL) {
+ if (driver == NULL || driver->unlocked_ioctl == NULL) {
retval = -ENOTTY;
} else {
- /* keep API that guarantees BKL */
- lock_kernel();
- retval = driver->ioctl(intf, ctl->ioctl_code, buf);
- unlock_kernel();
+ retval = driver->unlocked_ioctl(intf, ctl->ioctl_code, buf);
if (retval == -ENOIOCTLCMD)
retval = -ENOTTY;
}
}
/* Prevent the parent from suspending immediately after */
- else if (udev->parent) {
+ else if (udev->parent)
udev->parent->last_busy = jiffies;
- }
}
/* Runtime suspend for a USB interface doesn't mean anything. */
return 0;
}
-static struct dev_pm_ops usb_bus_pm_ops = {
+static const struct dev_pm_ops usb_bus_pm_ops = {
.runtime_suspend = usb_runtime_suspend,
.runtime_resume = usb_runtime_resume,
.runtime_idle = usb_runtime_idle,
};
-#else
-
-#define usb_bus_pm_ops (*(struct dev_pm_ops *) NULL)
-
#endif /* CONFIG_USB_SUSPEND */
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
+#ifdef CONFIG_USB_SUSPEND
.pm = &usb_bus_pm_ops,
+#endif
};
switch (usb_endpoint_type(ep->desc)) {
case USB_ENDPOINT_XFER_CONTROL:
- if (ep->udev->speed == USB_SPEED_HIGH) /* uframes per NAK */
+ if (ep->udev->speed == USB_SPEED_HIGH)
+ /* uframes per NAK */
interval = ep->desc->bInterval;
break;
+
case USB_ENDPOINT_XFER_ISOC:
interval = 1 << (ep->desc->bInterval - 1);
break;
+
case USB_ENDPOINT_XFER_BULK:
- if (ep->udev->speed == USB_SPEED_HIGH && !in) /* uframes per NAK */
+ if (ep->udev->speed == USB_SPEED_HIGH && !in)
+ /* uframes per NAK */
interval = ep->desc->bInterval;
break;
+
case USB_ENDPOINT_XFER_INT:
if (ep->udev->speed == USB_SPEED_HIGH)
interval = 1 << (ep->desc->bInterval - 1);
/* When the first config's first interface is one of Microsoft's
* pet nonstandard Ethernet-over-USB protocols, ignore it unless
* this kernel has enabled the necessary host side driver.
+ * But: Don't ignore it if it's the only config.
*/
- if (i == 0 && desc && (is_rndis(desc) || is_activesync(desc))) {
+ if (i == 0 && num_configs > 1 && desc &&
+ (is_rndis(desc) || is_activesync(desc))) {
#if !defined(CONFIG_USB_NET_RNDIS_HOST) && !defined(CONFIG_USB_NET_RNDIS_HOST_MODULE)
continue;
#else
* vice versa.
*/
companion = NULL;
- for (;;) {
- companion = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, companion);
- if (!companion)
- break;
+ for_each_pci_dev(companion) {
if (companion->bus != pdev->bus ||
PCI_SLOT(companion->devfn) != slot)
continue;
if (retval != 0)
goto err4;
set_hs_companion(dev, hcd);
+
+ if (pci_dev_run_wake(dev))
+ pm_runtime_put_noidle(&dev->dev);
return retval;
err4:
if (!hcd)
return;
+ if (pci_dev_run_wake(dev))
+ pm_runtime_get_noresume(&dev->dev);
+
+ /* Fake an interrupt request in order to give the driver a chance
+ * to test whether the controller hardware has been removed (e.g.,
+ * cardbus physical eject).
+ */
+ local_irq_disable();
+ usb_hcd_irq(0, hcd);
+ local_irq_enable();
+
usb_remove_hcd(hcd);
if (hcd->driver->flags & HCD_MEMORY) {
iounmap(hcd->regs);
if (!hcd)
return;
- if (hcd->driver->shutdown)
+ if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) &&
+ hcd->driver->shutdown)
hcd->driver->shutdown(hcd);
}
EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown);
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM_OPS
+
+#ifdef CONFIG_PPC_PMAC
+static void powermac_set_asic(struct pci_dev *pci_dev, int enable)
+{
+ /* Enanble or disable ASIC clocks for USB */
+ if (machine_is(powermac)) {
+ struct device_node *of_node;
+
+ of_node = pci_device_to_OF_node(pci_dev);
+ if (of_node)
+ pmac_call_feature(PMAC_FTR_USB_ENABLE,
+ of_node, 0, enable);
+ }
+}
+
+#else
+
+static inline void powermac_set_asic(struct pci_dev *pci_dev, int enable)
+{}
+
+#endif /* CONFIG_PPC_PMAC */
static int check_root_hub_suspended(struct device *dev)
{
return 0;
}
-static int hcd_pci_suspend(struct device *dev)
+static int suspend_common(struct device *dev, bool do_wakeup)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
if (retval)
return retval;
- /* We might already be suspended (runtime PM -- not yet written) */
- if (pci_dev->current_state != PCI_D0)
- return retval;
-
if (hcd->driver->pci_suspend) {
- retval = hcd->driver->pci_suspend(hcd);
+ /* Optimization: Don't suspend if a root-hub wakeup is
+ * pending and it would cause the HCD to wake up anyway.
+ */
+ if (do_wakeup && HCD_WAKEUP_PENDING(hcd))
+ return -EBUSY;
+ retval = hcd->driver->pci_suspend(hcd, do_wakeup);
suspend_report_result(hcd->driver->pci_suspend, retval);
+
+ /* Check again in case wakeup raced with pci_suspend */
+ if (retval == 0 && do_wakeup && HCD_WAKEUP_PENDING(hcd)) {
+ if (hcd->driver->pci_resume)
+ hcd->driver->pci_resume(hcd, false);
+ retval = -EBUSY;
+ }
if (retval)
return retval;
}
return retval;
}
+static int resume_common(struct device *dev, int event)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
+ int retval;
+
+ if (hcd->state != HC_STATE_SUSPENDED) {
+ dev_dbg(dev, "can't resume, not suspended!\n");
+ return 0;
+ }
+
+ retval = pci_enable_device(pci_dev);
+ if (retval < 0) {
+ dev_err(dev, "can't re-enable after resume, %d!\n", retval);
+ return retval;
+ }
+
+ pci_set_master(pci_dev);
+
+ clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+
+ if (hcd->driver->pci_resume) {
+ if (event != PM_EVENT_AUTO_RESUME)
+ wait_for_companions(pci_dev, hcd);
+
+ retval = hcd->driver->pci_resume(hcd,
+ event == PM_EVENT_RESTORE);
+ if (retval) {
+ dev_err(dev, "PCI post-resume error %d!\n", retval);
+ usb_hc_died(hcd);
+ }
+ }
+ return retval;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int hcd_pci_suspend(struct device *dev)
+{
+ return suspend_common(dev, device_may_wakeup(dev));
+}
+
static int hcd_pci_suspend_noirq(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
return retval;
}
-#ifdef CONFIG_PPC_PMAC
- /* Disable ASIC clocks for USB */
- if (machine_is(powermac)) {
- struct device_node *of_node;
-
- of_node = pci_device_to_OF_node(pci_dev);
- if (of_node)
- pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
- }
-#endif
+ powermac_set_asic(pci_dev, 0);
return retval;
}
{
struct pci_dev *pci_dev = to_pci_dev(dev);
-#ifdef CONFIG_PPC_PMAC
- /* Reenable ASIC clocks for USB */
- if (machine_is(powermac)) {
- struct device_node *of_node;
-
- of_node = pci_device_to_OF_node(pci_dev);
- if (of_node)
- pmac_call_feature(PMAC_FTR_USB_ENABLE,
- of_node, 0, 1);
- }
-#endif
+ powermac_set_asic(pci_dev, 1);
/* Go back to D0 and disable remote wakeup */
pci_back_from_sleep(pci_dev);
return 0;
}
-static int resume_common(struct device *dev, bool hibernated)
+static int hcd_pci_resume(struct device *dev)
{
- struct pci_dev *pci_dev = to_pci_dev(dev);
- struct usb_hcd *hcd = pci_get_drvdata(pci_dev);
- int retval;
+ return resume_common(dev, PM_EVENT_RESUME);
+}
- if (hcd->state != HC_STATE_SUSPENDED) {
- dev_dbg(dev, "can't resume, not suspended!\n");
- return 0;
- }
+static int hcd_pci_restore(struct device *dev)
+{
+ return resume_common(dev, PM_EVENT_RESTORE);
+}
- retval = pci_enable_device(pci_dev);
- if (retval < 0) {
- dev_err(dev, "can't re-enable after resume, %d!\n", retval);
- return retval;
- }
+#else
- pci_set_master(pci_dev);
+#define hcd_pci_suspend NULL
+#define hcd_pci_suspend_noirq NULL
+#define hcd_pci_resume_noirq NULL
+#define hcd_pci_resume NULL
+#define hcd_pci_restore NULL
- clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+#endif /* CONFIG_PM_SLEEP */
- if (hcd->driver->pci_resume) {
- /* This call should be made only during system resume,
- * not during runtime resume.
- */
- wait_for_companions(pci_dev, hcd);
+#ifdef CONFIG_PM_RUNTIME
- retval = hcd->driver->pci_resume(hcd, hibernated);
- if (retval) {
- dev_err(dev, "PCI post-resume error %d!\n", retval);
- usb_hc_died(hcd);
- }
- }
+static int hcd_pci_runtime_suspend(struct device *dev)
+{
+ int retval;
+
+ retval = suspend_common(dev, true);
+ if (retval == 0)
+ powermac_set_asic(to_pci_dev(dev), 0);
+ dev_dbg(dev, "hcd_pci_runtime_suspend: %d\n", retval);
return retval;
}
-static int hcd_pci_resume(struct device *dev)
+static int hcd_pci_runtime_resume(struct device *dev)
{
- return resume_common(dev, false);
-}
+ int retval;
-static int hcd_pci_restore(struct device *dev)
-{
- return resume_common(dev, true);
+ powermac_set_asic(to_pci_dev(dev), 1);
+ retval = resume_common(dev, PM_EVENT_AUTO_RESUME);
+ dev_dbg(dev, "hcd_pci_runtime_resume: %d\n", retval);
+ return retval;
}
+#else
+
+#define hcd_pci_runtime_suspend NULL
+#define hcd_pci_runtime_resume NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
const struct dev_pm_ops usb_hcd_pci_pm_ops = {
.suspend = hcd_pci_suspend,
.suspend_noirq = hcd_pci_suspend_noirq,
.poweroff_noirq = hcd_pci_suspend_noirq,
.restore_noirq = hcd_pci_resume_noirq,
.restore = hcd_pci_restore,
+ .runtime_suspend = hcd_pci_runtime_suspend,
+ .runtime_resume = hcd_pci_runtime_resume,
};
EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops);
-#endif /* CONFIG_PM_SLEEP */
+#endif /* CONFIG_PM_OPS */
unsigned long flags;
char buffer[6]; /* Any root hubs with > 31 ports? */
- if (unlikely(!hcd->rh_registered))
+ if (unlikely(!hcd->rh_pollable))
return;
if (!hcd->uses_new_polling && !hcd->status_urb)
return;
spin_lock_irqsave(&hcd_root_hub_lock, flags);
urb = hcd->status_urb;
if (urb) {
- hcd->poll_pending = 0;
+ clear_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
hcd->status_urb = NULL;
urb->actual_length = length;
memcpy(urb->transfer_buffer, buffer, length);
spin_lock(&hcd_root_hub_lock);
} else {
length = 0;
- hcd->poll_pending = 1;
+ set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
}
spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
}
* exceed that limit if HZ is 100. The math is more clunky than
* maybe expected, this is to make sure that all timers for USB devices
* fire at the same time to give the CPU a break inbetween */
- if (hcd->uses_new_polling ? hcd->poll_rh :
+ if (hcd->uses_new_polling ? HCD_POLL_RH(hcd) :
(length == 0 && hcd->status_urb != NULL))
mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
}
mod_timer(&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
/* If a status change has already occurred, report it ASAP */
- else if (hcd->poll_pending)
+ else if (HCD_POLL_PENDING(hcd))
mod_timer(&hcd->rh_timer, jiffies);
retval = 0;
done:
* finish unlinking the initial failed usb_set_address()
* or device descriptor fetch.
*/
- if (!test_bit(HCD_FLAG_SAW_IRQ, &hcd->flags) &&
- !is_root_hub(urb->dev)) {
+ if (!HCD_SAW_IRQ(hcd) && !is_root_hub(urb->dev)) {
dev_warn(hcd->self.controller, "Unlink after no-IRQ? "
"Controller is probably using the wrong IRQ.\n");
set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
{
unsigned char *vaddr;
+ if (*vaddr_handle == NULL) {
+ WARN_ON_ONCE(1);
+ return -EFAULT;
+ }
+
vaddr = hcd_buffer_alloc(bus, size + sizeof(vaddr),
mem_flags, dma_handle);
if (!vaddr)
dev_dbg(&rhdev->dev, "usb %s%s\n",
(msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume");
+ clear_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
if (!hcd->driver->bus_resume)
return -ENOENT;
if (hcd->state == HC_STATE_RUNNING)
unsigned long flags;
spin_lock_irqsave (&hcd_root_hub_lock, flags);
- if (hcd->rh_registered)
+ if (hcd->rh_registered) {
+ set_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
queue_work(pm_wq, &hcd->wakeup_work);
+ }
spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
}
EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);
*/
local_irq_save(flags);
- if (unlikely(hcd->state == HC_STATE_HALT ||
- !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) {
+ if (unlikely(hcd->state == HC_STATE_HALT || !HCD_HW_ACCESSIBLE(hcd))) {
rc = IRQ_NONE;
} else if (hcd->driver->irq(hcd) == IRQ_NONE) {
rc = IRQ_NONE;
local_irq_restore(flags);
return rc;
}
+EXPORT_SYMBOL_GPL(usb_hcd_irq);
/*-------------------------------------------------------------------------*/
spin_lock_irqsave (&hcd_root_hub_lock, flags);
if (hcd->rh_registered) {
- hcd->poll_rh = 0;
+ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
/* make khubd clean up old urbs and devices */
usb_set_device_state (hcd->self.root_hub,
retval = -ENOMEM;
goto err_allocate_root_hub;
}
+ hcd->self.root_hub = rhdev;
switch (hcd->driver->flags & HCD_MASK) {
case HCD_USB11:
rhdev->speed = USB_SPEED_SUPER;
break;
default:
- goto err_allocate_root_hub;
+ goto err_set_rh_speed;
}
- hcd->self.root_hub = rhdev;
/* wakeup flag init defaults to "everything works" for root hubs,
* but drivers can override it in reset() if needed, along with
dev_err(hcd->self.controller, "can't setup\n");
goto err_hcd_driver_setup;
}
+ hcd->rh_pollable = 1;
/* NOTE: root hub and controller capabilities may not be the same */
if (device_can_wakeup(hcd->self.controller)
retval);
goto error_create_attr_group;
}
- if (hcd->uses_new_polling && hcd->poll_rh)
+ if (hcd->uses_new_polling && HCD_POLL_RH(hcd))
usb_hcd_poll_rh_status(hcd);
return retval;
error_create_attr_group:
+ if (HC_IS_RUNNING(hcd->state))
+ hcd->state = HC_STATE_QUIESCING;
+ spin_lock_irq(&hcd_root_hub_lock);
+ hcd->rh_registered = 0;
+ spin_unlock_irq(&hcd_root_hub_lock);
+
+#ifdef CONFIG_USB_SUSPEND
+ cancel_work_sync(&hcd->wakeup_work);
+#endif
mutex_lock(&usb_bus_list_lock);
- usb_disconnect(&hcd->self.root_hub);
+ usb_disconnect(&rhdev); /* Sets rhdev to NULL */
mutex_unlock(&usb_bus_list_lock);
err_register_root_hub:
+ hcd->rh_pollable = 0;
+ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ del_timer_sync(&hcd->rh_timer);
hcd->driver->stop(hcd);
+ hcd->state = HC_STATE_HALT;
+ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ del_timer_sync(&hcd->rh_timer);
err_hcd_driver_start:
if (hcd->irq >= 0)
free_irq(irqnum, hcd);
err_request_irq:
err_hcd_driver_setup:
- hcd->self.root_hub = NULL;
- usb_put_dev(rhdev);
+err_set_rh_speed:
+ usb_put_dev(hcd->self.root_hub);
err_allocate_root_hub:
usb_deregister_bus(&hcd->self);
err_register_bus:
*/
void usb_remove_hcd(struct usb_hcd *hcd)
{
+ struct usb_device *rhdev = hcd->self.root_hub;
+
dev_info(hcd->self.controller, "remove, state %x\n", hcd->state);
+ usb_get_dev(rhdev);
+ sysfs_remove_group(&rhdev->dev.kobj, &usb_bus_attr_group);
+
if (HC_IS_RUNNING (hcd->state))
hcd->state = HC_STATE_QUIESCING;
cancel_work_sync(&hcd->wakeup_work);
#endif
- sysfs_remove_group(&hcd->self.root_hub->dev.kobj, &usb_bus_attr_group);
mutex_lock(&usb_bus_list_lock);
- usb_disconnect(&hcd->self.root_hub);
+ usb_disconnect(&rhdev); /* Sets rhdev to NULL */
mutex_unlock(&usb_bus_list_lock);
+ /* Prevent any more root-hub status calls from the timer.
+ * The HCD might still restart the timer (if a port status change
+ * interrupt occurs), but usb_hcd_poll_rh_status() won't invoke
+ * the hub_status_data() callback.
+ */
+ hcd->rh_pollable = 0;
+ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ del_timer_sync(&hcd->rh_timer);
+
hcd->driver->stop(hcd);
hcd->state = HC_STATE_HALT;
- hcd->poll_rh = 0;
+ /* In case the HCD restarted the timer, stop it again. */
+ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
del_timer_sync(&hcd->rh_timer);
if (hcd->irq >= 0)
free_irq(hcd->irq, hcd);
+
+ usb_put_dev(hcd->self.root_hub);
usb_deregister_bus(&hcd->self);
hcd_buffer_destroy(hcd);
}
#include <linux/usb.h>
#include <linux/usbdevice_fs.h>
#include <linux/usb/hcd.h>
+#include <linux/usb/quirks.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/freezer.h>
return -ENODEV;
}
+/* No BKL needed */
static int
hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
{
pm_runtime_set_active(&udev->dev);
pm_runtime_enable(&udev->dev);
- usb_detect_quirks(udev);
err = usb_enumerate_device(udev); /* Read descriptors */
if (err < 0)
goto fail;
}
retval = 0;
-
+ /* notify HCD that we have a device connected and addressed */
+ if (hcd->driver->update_device)
+ hcd->driver->update_device(hcd, udev);
fail:
if (retval) {
hub_port_disable(hub, port1, 0);
if (status < 0)
goto loop;
+ usb_detect_quirks(udev);
+ if (udev->quirks & USB_QUIRK_DELAY_INIT)
+ msleep(1000);
+
/* consecutive bus-powered hubs aren't reliable; they can
* violate the voltage drop budget. if the new child has
* a "powered" LED, users should notice we didn't enable it
.reset_resume = hub_reset_resume,
.pre_reset = hub_pre_reset,
.post_reset = hub_post_reset,
- .ioctl = hub_ioctl,
+ .unlocked_ioctl = hub_ioctl,
.id_table = hub_id_table,
.supports_autosuspend = 1,
};
return -EINVAL;
}
- lock_kernel();
-
if (usbfs_mount && usbfs_mount->mnt_sb)
update_sb(usbfs_mount->mnt_sb);
- unlock_kernel();
-
return 0;
}
/* Creative SB Audigy 2 NX */
{ USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Logitech Harmony 700-series */
+ { USB_DEVICE(0x046d, 0xc122), .driver_info = USB_QUIRK_DELAY_INIT },
+
/* Philips PSC805 audio device */
{ USB_DEVICE(0x0471, 0x0155), .driver_info = USB_QUIRK_RESET_RESUME },
}
EXPORT_SYMBOL_GPL(usb_anchor_urb);
+/* Callers must hold anchor->lock */
+static void __usb_unanchor_urb(struct urb *urb, struct usb_anchor *anchor)
+{
+ urb->anchor = NULL;
+ list_del(&urb->anchor_list);
+ usb_put_urb(urb);
+ if (list_empty(&anchor->urb_list))
+ wake_up(&anchor->wait);
+}
+
/**
* usb_unanchor_urb - unanchors an URB
* @urb: pointer to the urb to anchor
return;
spin_lock_irqsave(&anchor->lock, flags);
- if (unlikely(anchor != urb->anchor)) {
- /* we've lost the race to another thread */
- spin_unlock_irqrestore(&anchor->lock, flags);
- return;
- }
- urb->anchor = NULL;
- list_del(&urb->anchor_list);
+ /*
+ * At this point, we could be competing with another thread which
+ * has the same intention. To protect the urb from being unanchored
+ * twice, only the winner of the race gets the job.
+ */
+ if (likely(anchor == urb->anchor))
+ __usb_unanchor_urb(urb, anchor);
spin_unlock_irqrestore(&anchor->lock, flags);
- usb_put_urb(urb);
- if (list_empty(&anchor->urb_list))
- wake_up(&anchor->wait);
}
EXPORT_SYMBOL_GPL(usb_unanchor_urb);
void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
{
struct urb *victim;
- unsigned long flags;
- spin_lock_irqsave(&anchor->lock, flags);
- while (!list_empty(&anchor->urb_list)) {
- victim = list_entry(anchor->urb_list.prev, struct urb,
- anchor_list);
- usb_get_urb(victim);
- spin_unlock_irqrestore(&anchor->lock, flags);
- /* this will unanchor the URB */
+ while ((victim = usb_get_from_anchor(anchor)) != NULL) {
usb_unlink_urb(victim);
usb_put_urb(victim);
- spin_lock_irqsave(&anchor->lock, flags);
}
- spin_unlock_irqrestore(&anchor->lock, flags);
}
EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
victim = list_entry(anchor->urb_list.next, struct urb,
anchor_list);
usb_get_urb(victim);
- spin_unlock_irqrestore(&anchor->lock, flags);
- usb_unanchor_urb(victim);
+ __usb_unanchor_urb(victim, anchor);
} else {
- spin_unlock_irqrestore(&anchor->lock, flags);
victim = NULL;
}
+ spin_unlock_irqrestore(&anchor->lock, flags);
return victim;
}
while (!list_empty(&anchor->urb_list)) {
victim = list_entry(anchor->urb_list.prev, struct urb,
anchor_list);
- usb_get_urb(victim);
- spin_unlock_irqrestore(&anchor->lock, flags);
- /* this may free the URB */
- usb_unanchor_urb(victim);
- usb_put_urb(victim);
- spin_lock_irqsave(&anchor->lock, flags);
+ __usb_unanchor_urb(victim, anchor);
}
spin_unlock_irqrestore(&anchor->lock, flags);
}
.restore = usb_dev_restore,
};
-#else
-
-#define usb_device_pm_ops (*(struct dev_pm_ops *) NULL)
-
#endif /* CONFIG_PM */
.release = usb_release_dev,
.uevent = usb_dev_uevent,
.devnode = usb_devnode,
+#ifdef CONFIG_PM
.pm = &usb_device_pm_ops,
+#endif
};
config USB_FUNCTIONFS
tristate "Function Filesystem (EXPERIMENTAL)"
depends on EXPERIMENTAL
+ select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
help
The Function Filesystem (FunctioFS) lets one create USB
composite functions in user space in the same way as GadgetFS
implemented in kernel space (for instance Ethernet, serial or
mass storage) and other are implemented in user space.
+ If you say "y" or "m" here you will be able what kind of
+ configurations the gadget will provide.
+
Say "y" to link the driver statically, or "m" to build
a dynamically linked module called "g_ffs".
config USB_FUNCTIONFS_ETH
- bool "Include CDC ECM (Ethernet) function"
+ bool "Include configuration with CDC ECM (Ethernet)"
depends on USB_FUNCTIONFS && NET
help
- Include an CDC ECM (Ethernet) funcion in the CDC ECM (Funcion)
- Filesystem. If you also say "y" to the RNDIS query below the
- gadget will have two configurations.
+ Include a configuration with CDC ECM funcion (Ethernet) and the
+ Funcion Filesystem.
config USB_FUNCTIONFS_RNDIS
- bool "Include RNDIS (Ethernet) function"
+ bool "Include configuration with RNDIS (Ethernet)"
depends on USB_FUNCTIONFS && NET
help
- Include an RNDIS (Ethernet) funcion in the Funcion Filesystem.
- If you also say "y" to the CDC ECM query above the gadget will
- have two configurations.
+ Include a configuration with RNDIS funcion (Ethernet) and the Filesystem.
config USB_FUNCTIONFS_GENERIC
bool "Include 'pure' configuration"
- depends on USB_FUNCTIONFS && (USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
+ depends on USB_FUNCTIONFS
help
- Include a configuration with FunctionFS and no Ethernet
- configuration.
+ Include a configuration with the Function Filesystem alone with
+ no Ethernet interface.
config USB_FILE_STORAGE
tristate "File-backed Storage Gadget"
config USB_G_MULTI
tristate "Multifunction Composite Gadget (EXPERIMENTAL)"
depends on BLOCK && NET
+ select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS
help
The Multifunction Composite Gadget provides Ethernet (RNDIS
and/or CDC Ethernet), mass storage and ACM serial link
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_hid".
+config USB_G_DBGP
+ tristate "EHCI Debug Device Gadget"
+ help
+ This gadget emulates an EHCI Debug device. This is useful when you want
+ to interact with an EHCI Debug Port.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_dbgp".
+
+if USB_G_DBGP
+choice
+ prompt "EHCI Debug Device mode"
+ default USB_G_DBGP_SERIAL
+
+config USB_G_DBGP_PRINTK
+ depends on USB_G_DBGP
+ bool "printk"
+ help
+ Directly printk() received data. No interaction.
+
+config USB_G_DBGP_SERIAL
+ depends on USB_G_DBGP
+ bool "serial"
+ help
+ Userland can interact using /dev/ttyGSxxx.
+endchoice
+endif
+
# put drivers that need isochronous transfer support (for audio
# or video class gadget drivers), or specific hardware, here.
config USB_G_WEBCAM
g_cdc-objs := cdc2.o
g_multi-objs := multi.o
g_hid-objs := hid.o
+g_dbgp-objs := dbgp.o
g_nokia-objs := nokia.o
g_webcam-objs := webcam.o
obj-$(CONFIG_USB_ETH) += g_ether.o
obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o
-obj-$(CONFIG_USB_ETH_FUNCTIONFS) += g_eth_ffs.o
obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o
obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o
obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o
obj-$(CONFIG_USB_G_HID) += g_hid.o
+obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o
obj-$(CONFIG_USB_G_MULTI) += g_multi.o
obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
/*-------------------------------------------------------------------------*/
-static int __init audio_do_config(struct usb_configuration *c)
+static int __ref audio_do_config(struct usb_configuration *c)
{
/* FIXME alloc iConfiguration string, set it in c->strings */
/*-------------------------------------------------------------------------*/
-static int __init audio_bind(struct usb_composite_dev *cdev)
+static int __ref audio_bind(struct usb_composite_dev *cdev)
{
int gcnum;
int status;
/*
* We _always_ have both CDC ECM and CDC ACM functions.
*/
-static int __init cdc_do_config(struct usb_configuration *c)
+static int __ref cdc_do_config(struct usb_configuration *c)
{
int status;
/*-------------------------------------------------------------------------*/
-static int __init cdc_bind(struct usb_composite_dev *cdev)
+static int __ref cdc_bind(struct usb_composite_dev *cdev)
{
int gcnum;
struct usb_gadget *gadget = cdev->gadget;
* string IDs. Drivers for functions, configurations, or gadgets will
* then store that ID in the appropriate descriptors and string table.
*
- * All string identifier should be allocated using this routine, to
- * ensure that for example different functions don't wrongly assign
- * different meanings to the same identifier.
+ * All string identifier should be allocated using this,
+ * @usb_string_ids_tab() or @usb_string_ids_n() routine, to ensure
+ * that for example different functions don't wrongly assign different
+ * meanings to the same identifier.
*/
int usb_string_id(struct usb_composite_dev *cdev)
{
if (cdev->next_string_id < 254) {
- /* string id 0 is reserved */
+ /* string id 0 is reserved by USB spec for list of
+ * supported languages */
+ /* 255 reserved as well? -- mina86 */
cdev->next_string_id++;
return cdev->next_string_id;
}
return -ENODEV;
}
+/**
+ * usb_string_ids() - allocate unused string IDs in batch
+ * @cdev: the device whose string descriptor IDs are being allocated
+ * @str: an array of usb_string objects to assign numbers to
+ * Context: single threaded during gadget setup
+ *
+ * @usb_string_ids() is called from bind() callbacks to allocate
+ * string IDs. Drivers for functions, configurations, or gadgets will
+ * then copy IDs from the string table to the appropriate descriptors
+ * and string table for other languages.
+ *
+ * All string identifier should be allocated using this,
+ * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for
+ * example different functions don't wrongly assign different meanings
+ * to the same identifier.
+ */
+int usb_string_ids_tab(struct usb_composite_dev *cdev, struct usb_string *str)
+{
+ int next = cdev->next_string_id;
+
+ for (; str->s; ++str) {
+ if (unlikely(next >= 254))
+ return -ENODEV;
+ str->id = ++next;
+ }
+
+ cdev->next_string_id = next;
+
+ return 0;
+}
+
+/**
+ * usb_string_ids_n() - allocate unused string IDs in batch
+ * @cdev: the device whose string descriptor IDs are being allocated
+ * @n: number of string IDs to allocate
+ * Context: single threaded during gadget setup
+ *
+ * Returns the first requested ID. This ID and next @n-1 IDs are now
+ * valid IDs. At least providind that @n is non zore because if it
+ * is, returns last requested ID which is now very useful information.
+ *
+ * @usb_string_ids_n() is called from bind() callbacks to allocate
+ * string IDs. Drivers for functions, configurations, or gadgets will
+ * then store that ID in the appropriate descriptors and string table.
+ *
+ * All string identifier should be allocated using this,
+ * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for
+ * example different functions don't wrongly assign different meanings
+ * to the same identifier.
+ */
+int usb_string_ids_n(struct usb_composite_dev *c, unsigned n)
+{
+ unsigned next = c->next_string_id;
+ if (unlikely(n > 254 || (unsigned)next + n > 254))
+ return -ENODEV;
+ c->next_string_id += n;
+ return next + 1;
+}
+
+
/*-------------------------------------------------------------------------*/
static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
spin_lock_irqsave(&cdev->lock, flags);
if (cdev->config)
reset_config(cdev);
+ if (composite->disconnect)
+ composite->disconnect(cdev);
spin_unlock_irqrestore(&cdev->lock, flags);
}
--- /dev/null
+/*
+ * dbgp.c -- EHCI Debug Port device gadget
+ *
+ * Copyright (C) 2010 Stephane Duverger
+ *
+ * Released under the GPLv2.
+ *
+ */
+
+/* verbose messages */
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+/* See comments in "zero.c" */
+#include "epautoconf.c"
+
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+#include "u_serial.c"
+#endif
+
+#define DRIVER_VENDOR_ID 0x0525 /* NetChip */
+#define DRIVER_PRODUCT_ID 0xc0de /* undefined */
+
+#define USB_DEBUG_MAX_PACKET_SIZE 8
+#define DBGP_REQ_EP0_LEN 128
+#define DBGP_REQ_LEN 512
+
+static struct dbgp {
+ struct usb_gadget *gadget;
+ struct usb_request *req;
+ struct usb_ep *i_ep;
+ struct usb_ep *o_ep;
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+ struct gserial *serial;
+#endif
+} dbgp;
+
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_VENDOR_SPEC,
+ .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_ID),
+ .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_ID),
+ .bNumConfigurations = 1,
+};
+
+static struct usb_debug_descriptor dbg_desc = {
+ .bLength = sizeof dbg_desc,
+ .bDescriptorType = USB_DT_DEBUG,
+};
+
+static struct usb_endpoint_descriptor i_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .bEndpointAddress = USB_DIR_IN,
+};
+
+static struct usb_endpoint_descriptor o_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .bEndpointAddress = USB_DIR_OUT,
+};
+
+#ifdef CONFIG_USB_G_DBGP_PRINTK
+static int dbgp_consume(char *buf, unsigned len)
+{
+ char c;
+
+ if (!len)
+ return 0;
+
+ c = buf[len-1];
+ if (c != 0)
+ buf[len-1] = 0;
+
+ printk(KERN_NOTICE "%s%c", buf, c);
+ return 0;
+}
+
+static void __disable_ep(struct usb_ep *ep)
+{
+ if (ep && ep->driver_data == dbgp.gadget) {
+ usb_ep_disable(ep);
+ ep->driver_data = NULL;
+ }
+}
+
+static void dbgp_disable_ep(void)
+{
+ __disable_ep(dbgp.i_ep);
+ __disable_ep(dbgp.o_ep);
+}
+
+static void dbgp_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ int stp;
+ int err = 0;
+ int status = req->status;
+
+ if (ep == dbgp.i_ep) {
+ stp = 1;
+ goto fail;
+ }
+
+ if (status != 0) {
+ stp = 2;
+ goto release_req;
+ }
+
+ dbgp_consume(req->buf, req->actual);
+
+ req->length = DBGP_REQ_LEN;
+ err = usb_ep_queue(ep, req, GFP_ATOMIC);
+ if (err < 0) {
+ stp = 3;
+ goto release_req;
+ }
+
+ return;
+
+release_req:
+ kfree(req->buf);
+ usb_ep_free_request(dbgp.o_ep, req);
+ dbgp_disable_ep();
+fail:
+ dev_dbg(&dbgp.gadget->dev,
+ "complete: failure (%d:%d) ==> %d\n", stp, err, status);
+}
+
+static int dbgp_enable_ep_req(struct usb_ep *ep)
+{
+ int err, stp;
+ struct usb_request *req;
+
+ req = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!req) {
+ err = -ENOMEM;
+ stp = 1;
+ goto fail_1;
+ }
+
+ req->buf = kmalloc(DBGP_REQ_LEN, GFP_KERNEL);
+ if (!req->buf) {
+ err = -ENOMEM;
+ stp = 2;
+ goto fail_2;
+ }
+
+ req->complete = dbgp_complete;
+ req->length = DBGP_REQ_LEN;
+ err = usb_ep_queue(ep, req, GFP_ATOMIC);
+ if (err < 0) {
+ stp = 3;
+ goto fail_3;
+ }
+
+ return 0;
+
+fail_3:
+ kfree(req->buf);
+fail_2:
+ usb_ep_free_request(dbgp.o_ep, req);
+fail_1:
+ dev_dbg(&dbgp.gadget->dev,
+ "enable ep req: failure (%d:%d)\n", stp, err);
+ return err;
+}
+
+static int __enable_ep(struct usb_ep *ep, struct usb_endpoint_descriptor *desc)
+{
+ int err = usb_ep_enable(ep, desc);
+ ep->driver_data = dbgp.gadget;
+ return err;
+}
+
+static int dbgp_enable_ep(void)
+{
+ int err, stp;
+
+ err = __enable_ep(dbgp.i_ep, &i_desc);
+ if (err < 0) {
+ stp = 1;
+ goto fail_1;
+ }
+
+ err = __enable_ep(dbgp.o_ep, &o_desc);
+ if (err < 0) {
+ stp = 2;
+ goto fail_2;
+ }
+
+ err = dbgp_enable_ep_req(dbgp.o_ep);
+ if (err < 0) {
+ stp = 3;
+ goto fail_3;
+ }
+
+ return 0;
+
+fail_3:
+ __disable_ep(dbgp.o_ep);
+fail_2:
+ __disable_ep(dbgp.i_ep);
+fail_1:
+ dev_dbg(&dbgp.gadget->dev, "enable ep: failure (%d:%d)\n", stp, err);
+ return err;
+}
+#endif
+
+static void dbgp_disconnect(struct usb_gadget *gadget)
+{
+#ifdef CONFIG_USB_G_DBGP_PRINTK
+ dbgp_disable_ep();
+#else
+ gserial_disconnect(dbgp.serial);
+#endif
+}
+
+static void dbgp_unbind(struct usb_gadget *gadget)
+{
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+ kfree(dbgp.serial);
+#endif
+ if (dbgp.req) {
+ kfree(dbgp.req->buf);
+ usb_ep_free_request(gadget->ep0, dbgp.req);
+ }
+
+ gadget->ep0->driver_data = NULL;
+}
+
+static int __init dbgp_configure_endpoints(struct usb_gadget *gadget)
+{
+ int stp;
+
+ usb_ep_autoconfig_reset(gadget);
+
+ dbgp.i_ep = usb_ep_autoconfig(gadget, &i_desc);
+ if (!dbgp.i_ep) {
+ stp = 1;
+ goto fail_1;
+ }
+
+ dbgp.i_ep->driver_data = gadget;
+ i_desc.wMaxPacketSize =
+ __constant_cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE);
+
+ dbgp.o_ep = usb_ep_autoconfig(gadget, &o_desc);
+ if (!dbgp.o_ep) {
+ dbgp.i_ep->driver_data = NULL;
+ stp = 2;
+ goto fail_2;
+ }
+
+ dbgp.o_ep->driver_data = gadget;
+ o_desc.wMaxPacketSize =
+ __constant_cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE);
+
+ dbg_desc.bDebugInEndpoint = i_desc.bEndpointAddress & 0x7f;
+ dbg_desc.bDebugOutEndpoint = o_desc.bEndpointAddress & 0x7f;
+
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+ dbgp.serial->in = dbgp.i_ep;
+ dbgp.serial->out = dbgp.o_ep;
+
+ dbgp.serial->in_desc = &i_desc;
+ dbgp.serial->out_desc = &o_desc;
+
+ if (gserial_setup(gadget, 1) < 0) {
+ stp = 3;
+ goto fail_3;
+ }
+
+ return 0;
+
+fail_3:
+ dbgp.o_ep->driver_data = NULL;
+#else
+ return 0;
+#endif
+fail_2:
+ dbgp.i_ep->driver_data = NULL;
+fail_1:
+ dev_dbg(&dbgp.gadget->dev, "ep config: failure (%d)\n", stp);
+ return -ENODEV;
+}
+
+static int __init dbgp_bind(struct usb_gadget *gadget)
+{
+ int err, stp;
+
+ dbgp.gadget = gadget;
+
+ dbgp.req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+ if (!dbgp.req) {
+ err = -ENOMEM;
+ stp = 1;
+ goto fail;
+ }
+
+ dbgp.req->buf = kmalloc(DBGP_REQ_EP0_LEN, GFP_KERNEL);
+ if (!dbgp.req->buf) {
+ err = -ENOMEM;
+ stp = 2;
+ goto fail;
+ }
+
+ dbgp.req->length = DBGP_REQ_EP0_LEN;
+ gadget->ep0->driver_data = gadget;
+
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+ dbgp.serial = kzalloc(sizeof(struct gserial), GFP_KERNEL);
+ if (!dbgp.serial) {
+ stp = 3;
+ err = -ENOMEM;
+ goto fail;
+ }
+#endif
+ err = dbgp_configure_endpoints(gadget);
+ if (err < 0) {
+ stp = 4;
+ goto fail;
+ }
+
+ dev_dbg(&dbgp.gadget->dev, "bind: success\n");
+ return 0;
+
+fail:
+ dev_dbg(&gadget->dev, "bind: failure (%d:%d)\n", stp, err);
+ dbgp_unbind(gadget);
+ return err;
+}
+
+static void dbgp_setup_complete(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ dev_dbg(&dbgp.gadget->dev, "setup complete: %d, %d/%d\n",
+ req->status, req->actual, req->length);
+}
+
+static int dbgp_setup(struct usb_gadget *gadget,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_request *req = dbgp.req;
+ u8 request = ctrl->bRequest;
+ u16 value = le16_to_cpu(ctrl->wValue);
+ u16 length = le16_to_cpu(ctrl->wLength);
+ int err = 0;
+ void *data;
+ u16 len;
+
+ gadget->ep0->driver_data = gadget;
+
+ if (request == USB_REQ_GET_DESCRIPTOR) {
+ switch (value>>8) {
+ case USB_DT_DEVICE:
+ dev_dbg(&dbgp.gadget->dev, "setup: desc device\n");
+ len = sizeof device_desc;
+ data = &device_desc;
+ break;
+ case USB_DT_DEBUG:
+ dev_dbg(&dbgp.gadget->dev, "setup: desc debug\n");
+ len = sizeof dbg_desc;
+ data = &dbg_desc;
+ break;
+ default:
+ goto fail;
+ }
+ } else if (request == USB_REQ_SET_FEATURE &&
+ value == USB_DEVICE_DEBUG_MODE) {
+ len = 0;
+ data = NULL;
+ dev_dbg(&dbgp.gadget->dev, "setup: feat debug\n");
+#ifdef CONFIG_USB_G_DBGP_PRINTK
+ err = dbgp_enable_ep();
+#else
+ err = gserial_connect(dbgp.serial, 0);
+#endif
+ if (err < 0)
+ goto fail;
+ } else
+ goto fail;
+
+ if (len >= 0) {
+ req->length = min(length, len);
+ req->zero = len < req->length;
+ if (data && req->length)
+ memcpy(req->buf, data, req->length);
+
+ req->complete = dbgp_setup_complete;
+ return usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+ }
+
+fail:
+ dev_dbg(&dbgp.gadget->dev,
+ "setup: failure req %x v %x\n", request, value);
+ return err;
+}
+
+static struct usb_gadget_driver dbgp_driver = {
+ .function = "dbgp",
+ .speed = USB_SPEED_HIGH,
+ .bind = dbgp_bind,
+ .unbind = dbgp_unbind,
+ .setup = dbgp_setup,
+ .disconnect = dbgp_disconnect,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "dbgp"
+ },
+};
+
+static int __init dbgp_init(void)
+{
+ return usb_gadget_register_driver(&dbgp_driver);
+}
+
+static void __exit dbgp_exit(void)
+{
+ usb_gadget_unregister_driver(&dbgp_driver);
+#ifdef CONFIG_USB_G_DBGP_SERIAL
+ gserial_cleanup();
+#endif
+}
+
+MODULE_AUTHOR("Stephane Duverger");
+MODULE_LICENSE("GPL");
+module_init(dbgp_init);
+module_exit(dbgp_exit);
dum = hcd_to_dummy (hcd);
spin_lock_irqsave (&dum->lock, flags);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ if (!HCD_HW_ACCESSIBLE(hcd))
goto done;
if (dum->resuming && time_after_eq (jiffies, dum->re_timeout)) {
int retval = 0;
unsigned long flags;
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ if (!HCD_HW_ACCESSIBLE(hcd))
return -ETIMEDOUT;
dum = hcd_to_dummy (hcd);
dev_dbg (&hcd->self.root_hub->dev, "%s\n", __func__);
spin_lock_irq (&dum->lock);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
rc = -ESHUTDOWN;
} else {
dum->rh_state = DUMMY_RH_RUNNING;
* the first one present. That's to make Microsoft's drivers happy,
* and to follow DOCSIS 1.0 (cable modem standard).
*/
-static int __init rndis_do_config(struct usb_configuration *c)
+static int __ref rndis_do_config(struct usb_configuration *c)
{
/* FIXME alloc iConfiguration string, set it in c->strings */
/*
* We _always_ have an ECM, CDC Subset, or EEM configuration.
*/
-static int __init eth_do_config(struct usb_configuration *c)
+static int __ref eth_do_config(struct usb_configuration *c)
{
/* FIXME alloc iConfiguration string, set it in c->strings */
/*-------------------------------------------------------------------------*/
-static int __init eth_bind(struct usb_composite_dev *cdev)
+static int __ref eth_bind(struct usb_composite_dev *cdev)
{
int gcnum;
struct usb_gadget *gadget = cdev->gadget;
struct ffs_function *func = ffs->func;
ret = func ? ffs_func_revmap_intf(func, value) : -ENODEV;
} else if (gadget->ops->ioctl) {
- lock_kernel();
ret = gadget->ops->ioctl(gadget, code, value);
- unlock_kernel();
} else {
ret = -ENOTTY;
}
static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
{
- unsigned i, count;
+ struct usb_gadget_strings **lang;
+ int first_id;
ENTER();
|| test_and_set_bit(FFS_FL_BOUND, &ffs->flags)))
return -EBADFD;
- ffs_data_get(ffs);
+ first_id = usb_string_ids_n(cdev, ffs->strings_count);
+ if (unlikely(first_id < 0))
+ return first_id;
ffs->ep0req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
if (unlikely(!ffs->ep0req))
ffs->ep0req->complete = ffs_ep0_complete;
ffs->ep0req->context = ffs;
- /* Get strings identifiers */
- for (count = ffs->strings_count, i = 0; i < count; ++i) {
- struct usb_gadget_strings **lang;
-
- int id = usb_string_id(cdev);
- if (unlikely(id < 0)) {
- usb_ep_free_request(cdev->gadget->ep0, ffs->ep0req);
- ffs->ep0req = NULL;
- return id;
- }
-
- lang = ffs->stringtabs;
- do {
- (*lang)->strings[i].id = id;
- ++lang;
- } while (*lang);
+ lang = ffs->stringtabs;
+ for (lang = ffs->stringtabs; *lang; ++lang) {
+ struct usb_string *str = (*lang)->strings;
+ int id = first_id;
+ for (; str->s; ++id, ++str)
+ str->id = id;
}
ffs->gadget = cdev->gadget;
+ ffs_data_get(ffs);
return 0;
}
}
-static int functionfs_add(struct usb_composite_dev *cdev,
- struct usb_configuration *c,
- struct ffs_data *ffs)
+static int functionfs_bind_config(struct usb_composite_dev *cdev,
+ struct usb_configuration *c,
+ struct ffs_data *ffs)
{
struct ffs_function *func;
int ret;
static ssize_t f_hidg_read(struct file *file, char __user *buffer,
size_t count, loff_t *ptr)
{
- struct f_hidg *hidg = (struct f_hidg *)file->private_data;
+ struct f_hidg *hidg = file->private_data;
char *tmp_buff = NULL;
unsigned long flags;
static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
size_t count, loff_t *offp)
{
- struct f_hidg *hidg = (struct f_hidg *)file->private_data;
+ struct f_hidg *hidg = file->private_data;
ssize_t status = -ENOMEM;
if (!access_ok(VERIFY_READ, buffer, count))
static unsigned int f_hidg_poll(struct file *file, poll_table *wait)
{
- struct f_hidg *hidg = (struct f_hidg *)file->private_data;
+ struct f_hidg *hidg = file->private_data;
unsigned int ret = 0;
poll_wait(file, &hidg->read_queue, wait);
/*-------------------------------------------------------------------------*/
-static int __init loopback_bind_config(struct usb_configuration *c)
+static int __ref loopback_bind_config(struct usb_configuration *c)
{
struct f_loopback *loop;
int status;
return status;
}
-static struct usb_configuration loopback_driver = {
+static struct usb_configuration loopback_driver = {
.label = "loopback",
.strings = loopback_strings,
.bind = loopback_bind_config,
/*-------------------------------------------------------------------------*/
struct fsg_dev;
+struct fsg_common;
+
+/* FSF callback functions */
+struct fsg_operations {
+ /* Callback function to call when thread exits. If no
+ * callback is set or it returns value lower then zero MSF
+ * will force eject all LUNs it operates on (including those
+ * marked as non-removable or with prevent_medium_removal flag
+ * set). */
+ int (*thread_exits)(struct fsg_common *common);
+
+ /* Called prior to ejection. Negative return means error,
+ * zero means to continue with ejection, positive means not to
+ * eject. */
+ int (*pre_eject)(struct fsg_common *common,
+ struct fsg_lun *lun, int num);
+ /* Called after ejection. Negative return means error, zero
+ * or positive is just a success. */
+ int (*post_eject)(struct fsg_common *common,
+ struct fsg_lun *lun, int num);
+};
/* Data shared by all the FSG instances. */
struct usb_ep *ep0; /* Copy of gadget->ep0 */
struct usb_request *ep0req; /* Copy of cdev->req */
unsigned int ep0_req_tag;
- const char *ep0req_name;
struct fsg_buffhd *next_buffhd_to_fill;
struct fsg_buffhd *next_buffhd_to_drain;
struct completion thread_notifier;
struct task_struct *thread_task;
- /* Callback function to call when thread exits. */
- int (*thread_exits)(struct fsg_common *common);
+ /* Callback functions. */
+ const struct fsg_operations *ops;
/* Gadget's private data. */
void *private_data;
const char *lun_name_format;
const char *thread_name;
- /* Callback function to call when thread exits. If no
- * callback is set or it returns value lower then zero MSF
- * will force eject all LUNs it operates on (including those
- * marked as non-removable or with prevent_medium_removal flag
- * set). */
- int (*thread_exits)(struct fsg_common *common);
+ /* Callback functions. */
+ const struct fsg_operations *ops;
/* Gadget's private data. */
void *private_data;
if (common->fsg)
return 1;
ERROR(common, "common->fsg is NULL in %s at %u\n", func, line);
+ WARN_ON(1);
return 0;
}
/* Respond with data/status */
req->length = min((u16)1, w_length);
- fsg->common->ep0req_name =
- ctrl->bRequestType & USB_DIR_IN ? "ep0-in" : "ep0-out";
return ep0_queue(fsg->common);
}
} else if (!curlun->removable) {
curlun->sense_data = SS_INVALID_COMMAND;
return -EINVAL;
- }
-
- loej = common->cmnd[4] & 0x02;
- start = common->cmnd[4] & 0x01;
-
- /* eject code from file_storage.c:do_start_stop() */
-
- if ((common->cmnd[1] & ~0x01) != 0 || /* Mask away Immed */
- (common->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */
+ } else if ((common->cmnd[1] & ~0x01) != 0 || /* Mask away Immed */
+ (common->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
return -EINVAL;
}
- if (!start) {
- /* Are we allowed to unload the media? */
- if (curlun->prevent_medium_removal) {
- LDBG(curlun, "unload attempt prevented\n");
- curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED;
- return -EINVAL;
- }
- if (loej) { /* Simulate an unload/eject */
- up_read(&common->filesem);
- down_write(&common->filesem);
- fsg_lun_close(curlun);
- up_write(&common->filesem);
- down_read(&common->filesem);
- }
- } else {
+ loej = common->cmnd[4] & 0x02;
+ start = common->cmnd[4] & 0x01;
- /* Our emulation doesn't support mounting; the medium is
- * available for use as soon as it is loaded. */
+ /* Our emulation doesn't support mounting; the medium is
+ * available for use as soon as it is loaded. */
+ if (start) {
if (!fsg_lun_is_open(curlun)) {
curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
return -EINVAL;
}
+ return 0;
}
- return 0;
+
+ /* Are we allowed to unload the media? */
+ if (curlun->prevent_medium_removal) {
+ LDBG(curlun, "unload attempt prevented\n");
+ curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED;
+ return -EINVAL;
+ }
+
+ if (!loej)
+ return 0;
+
+ /* Simulate an unload/eject */
+ if (common->ops && common->ops->pre_eject) {
+ int r = common->ops->pre_eject(common, curlun,
+ curlun - common->luns);
+ if (unlikely(r < 0))
+ return r;
+ else if (r)
+ return 0;
+ }
+
+ up_read(&common->filesem);
+ down_write(&common->filesem);
+ fsg_lun_close(curlun);
+ up_write(&common->filesem);
+ down_read(&common->filesem);
+
+ return common->ops && common->ops->post_eject
+ ? min(0, common->ops->post_eject(common, curlun,
+ curlun - common->luns))
+ : 0;
}
common->thread_task = NULL;
spin_unlock_irq(&common->lock);
- if (!common->thread_exits || common->thread_exits(common) < 0) {
+ if (!common->ops || !common->ops->thread_exits
+ || common->ops->thread_exits(common) < 0) {
struct fsg_lun *curlun = common->luns;
unsigned i = common->nluns;
common->free_storage_on_release = 0;
}
+ common->ops = cfg->ops;
common->private_data = cfg->private_data;
common->gadget = gadget;
/* Tell the thread to start working */
- common->thread_exits = cfg->thread_exits;
common->thread_task =
kthread_create(fsg_main_thread, common,
OR(cfg->thread_name, "file-storage"));
NULL,
};
-static int fsg_add(struct usb_composite_dev *cdev,
- struct usb_configuration *c,
- struct fsg_common *common)
+static int fsg_bind_config(struct usb_composite_dev *cdev,
+ struct usb_configuration *c,
+ struct fsg_common *common)
{
struct fsg_dev *fsg;
int rc;
return rc;
}
+static inline int __deprecated __maybe_unused
+fsg_add(struct usb_composite_dev *cdev,
+ struct usb_configuration *c,
+ struct fsg_common *common)
+{
+ return fsg_bind_config(cdev, c, common);
+}
/************************* Module parameters *************************/
cfg->product_name = 0;
cfg->release = 0xffff;
- cfg->thread_exits = 0;
- cfg->private_data = 0;
+ cfg->ops = NULL;
+ cfg->private_data = NULL;
/* Finalise */
cfg->can_stall = params->stall;
/*-------------------------------------------------------------------------*/
-static int __init sourcesink_bind_config(struct usb_configuration *c)
+static int __ref sourcesink_bind_config(struct usb_configuration *c)
{
struct f_sourcesink *ss;
int status;
* following protocols: RBC (0x01), ATAPI or SFF-8020i (0x02), QIC-157 (0c03),
* UFI (0x04), SFF-8070i (0x05), and transparent SCSI (0x06), selected by
* the optional "protocol" module parameter. In addition, the default
- * Vendor ID, Product ID, and release number can be overridden.
+ * Vendor ID, Product ID, release number and serial number can be overridden.
*
* There is support for multiple logical units (LUNs), each of which has
* its own backing file. The number of LUNs can be set using the optional
* removable Default false, boolean for removable media
* luns=N Default N = number of filenames, number of
* LUNs to support
+ * nofua=b[,b...] Default false, booleans for ignore FUA flag
+ * in SCSI WRITE(10,12) commands
* stall Default determined according to the type of
* USB device controller (usually true),
* boolean to permit the driver to halt
* vendor=0xVVVV Default 0x0525 (NetChip), USB Vendor ID
* product=0xPPPP Default 0xa4a5 (FSG), USB Product ID
* release=0xRRRR Override the USB release number (bcdDevice)
+ * serial=HHHH... Override serial number (string of hex chars)
* buflen=N Default N=16384, buffer size used (will be
* rounded down to a multiple of
* PAGE_CACHE_SIZE)
*
* If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "ro",
- * "removable", "luns", "stall", and "cdrom" options are available; default
- * values are used for everything else.
+ * "removable", "luns", "nofua", "stall", and "cdrom" options are available;
+ * default values are used for everything else.
*
* The pathnames of the backing files and the ro settings are available in
- * the attribute files "file" and "ro" in the lun<n> subdirectory of the
- * gadget's sysfs directory. If the "removable" option is set, writing to
+ * the attribute files "file", "nofua", and "ro" in the lun<n> subdirectory of
+ * the gadget's sysfs directory. If the "removable" option is set, writing to
* these files will simulate ejecting/loading the medium (writing an empty
* line means eject) and adjusting a write-enable tab. Changes to the ro
* setting are not allowed when the medium is loaded or if CD-ROM emulation
#define DRIVER_DESC "File-backed Storage Gadget"
#define DRIVER_NAME "g_file_storage"
+/* DRIVER_VERSION must be at least 6 characters long, as it is used
+ * to generate a fallback serial number. */
#define DRIVER_VERSION "20 November 2008"
static char fsg_string_manufacturer[64];
static struct {
char *file[FSG_MAX_LUNS];
int ro[FSG_MAX_LUNS];
+ int nofua[FSG_MAX_LUNS];
unsigned int num_filenames;
unsigned int num_ros;
+ unsigned int num_nofuas;
unsigned int nluns;
int removable;
unsigned short vendor;
unsigned short product;
unsigned short release;
+ char *serial;
unsigned int buflen;
int transport_type;
module_param_array_named(ro, mod_data.ro, bool, &mod_data.num_ros, S_IRUGO);
MODULE_PARM_DESC(ro, "true to force read-only");
+module_param_array_named(nofua, mod_data.nofua, bool, &mod_data.num_nofuas,
+ S_IRUGO);
+MODULE_PARM_DESC(nofua, "true to ignore SCSI WRITE(10,12) FUA bit");
+
module_param_named(luns, mod_data.nluns, uint, S_IRUGO);
MODULE_PARM_DESC(luns, "number of LUNs");
module_param_named(cdrom, mod_data.cdrom, bool, S_IRUGO);
MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk");
+module_param_named(serial, mod_data.serial, charp, S_IRUGO);
+MODULE_PARM_DESC(serial, "USB serial number");
/* In the non-TEST version, only the module parameters listed above
* are available. */
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
return -EINVAL;
}
- if (fsg->cmnd[1] & 0x08) { // FUA
+ /* FUA */
+ if (!curlun->nofua && (fsg->cmnd[1] & 0x08)) {
spin_lock(&curlun->filp->f_lock);
curlun->filp->f_flags |= O_DSYNC;
spin_unlock(&curlun->filp->f_lock);
/* The write permissions and store_xxx pointers are set in fsg_bind() */
static DEVICE_ATTR(ro, 0444, fsg_show_ro, NULL);
+static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, NULL);
static DEVICE_ATTR(file, 0444, fsg_show_file, NULL);
{
int prot;
int gcnum;
+ int i;
/* Store the default values */
mod_data.transport_type = USB_PR_BULK;
ERROR(fsg, "invalid buflen\n");
return -ETOOSMALL;
}
+
#endif /* CONFIG_USB_FILE_STORAGE_TEST */
+ /* Serial string handling.
+ * On a real device, the serial string would be loaded
+ * from permanent storage. */
+ if (mod_data.serial) {
+ const char *ch;
+ unsigned len = 0;
+
+ /* Sanity check :
+ * The CB[I] specification limits the serial string to
+ * 12 uppercase hexadecimal characters.
+ * BBB need at least 12 uppercase hexadecimal characters,
+ * with a maximum of 126. */
+ for (ch = mod_data.serial; *ch; ++ch) {
+ ++len;
+ if ((*ch < '0' || *ch > '9') &&
+ (*ch < 'A' || *ch > 'F')) { /* not uppercase hex */
+ WARNING(fsg,
+ "Invalid serial string character: %c; "
+ "Failing back to default\n",
+ *ch);
+ goto fill_serial;
+ }
+ }
+ if (len > 126 ||
+ (mod_data.transport_type == USB_PR_BULK && len < 12) ||
+ (mod_data.transport_type != USB_PR_BULK && len > 12)) {
+ WARNING(fsg,
+ "Invalid serial string length; "
+ "Failing back to default\n");
+ goto fill_serial;
+ }
+ fsg_strings[FSG_STRING_SERIAL - 1].s = mod_data.serial;
+ } else {
+ WARNING(fsg,
+ "Userspace failed to provide serial number; "
+ "Failing back to default\n");
+fill_serial:
+ /* Serial number not specified or invalid, make our own.
+ * We just encode it from the driver version string,
+ * 12 characters to comply with both CB[I] and BBB spec.
+ * Warning : Two devices running the same kernel will have
+ * the same fallback serial number. */
+ for (i = 0; i < 12; i += 2) {
+ unsigned char c = DRIVER_VERSION[i / 2];
+
+ if (!c)
+ break;
+ sprintf(&fsg_string_serial[i], "%02X", c);
+ }
+ }
+
return 0;
}
-static int __init fsg_bind(struct usb_gadget *gadget)
+static int __ref fsg_bind(struct usb_gadget *gadget)
{
struct fsg_dev *fsg = the_fsg;
int rc;
}
}
+ /* Only for removable media? */
+ dev_attr_nofua.attr.mode = 0644;
+ dev_attr_nofua.store = fsg_store_nofua;
+
/* Find out how many LUNs there should be */
i = mod_data.nluns;
if (i == 0)
curlun->ro = mod_data.cdrom || mod_data.ro[i];
curlun->initially_ro = curlun->ro;
curlun->removable = mod_data.removable;
+ curlun->nofua = mod_data.nofua[i];
curlun->dev.release = lun_release;
curlun->dev.parent = &gadget->dev;
curlun->dev.driver = &fsg_driver.driver;
}
if ((rc = device_create_file(&curlun->dev,
&dev_attr_ro)) != 0 ||
+ (rc = device_create_file(&curlun->dev,
+ &dev_attr_nofua)) != 0 ||
(rc = device_create_file(&curlun->dev,
&dev_attr_file)) != 0) {
device_unregister(&curlun->dev);
init_utsname()->sysname, init_utsname()->release,
gadget->name);
- /* On a real device, serial[] would be loaded from permanent
- * storage. We just encode it from the driver version string. */
- for (i = 0; i < sizeof fsg_string_serial - 2; i += 2) {
- unsigned char c = DRIVER_VERSION[i / 2];
-
- if (!c)
- break;
- sprintf(&fsg_string_serial[i], "%02X", c);
- }
-
fsg->thread_task = kthread_create(fsg_main_thread, fsg,
"file-storage-gadget");
if (IS_ERR(fsg->thread_task)) {
if (IS_ERR(p))
p = NULL;
}
- LINFO(curlun, "ro=%d, file: %s\n",
- curlun->ro, (p ? p : "(error)"));
+ LINFO(curlun, "ro=%d, nofua=%d, file: %s\n",
+ curlun->ro, curlun->nofua, (p ? p : "(error)"));
}
}
kfree(pathbuf);
# include "u_ether.c"
static u8 gfs_hostaddr[ETH_ALEN];
-#else
-# if !defined CONFIG_USB_FUNCTIONFS_GENERIC
-# define CONFIG_USB_FUNCTIONFS_GENERIC
+# ifdef CONFIG_USB_FUNCTIONFS_ETH
+static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
# endif
+#else
# define gether_cleanup() do { } while (0)
# define gether_setup(gadget, hostaddr) ((int)0)
+# define gfs_hostaddr NULL
#endif
#include "f_fs.c"
enum {
GFS_STRING_MANUFACTURER_IDX,
GFS_STRING_PRODUCT_IDX,
-#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
- GFS_STRING_RNDIS_CONFIG_IDX,
-#endif
-#ifdef CONFIG_USB_FUNCTIONFS_ETH
- GFS_STRING_ECM_CONFIG_IDX,
-#endif
-#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
- GFS_STRING_GENERIC_CONFIG_IDX,
-#endif
+ GFS_STRING_FIRST_CONFIG_IDX,
};
static char gfs_manufacturer[50];
[GFS_STRING_MANUFACTURER_IDX].s = gfs_manufacturer,
[GFS_STRING_PRODUCT_IDX].s = gfs_driver_desc,
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
- [GFS_STRING_RNDIS_CONFIG_IDX].s = "FunctionFS + RNDIS",
+ { .s = "FunctionFS + RNDIS" },
#endif
#ifdef CONFIG_USB_FUNCTIONFS_ETH
- [GFS_STRING_ECM_CONFIG_IDX].s = "FunctionFS + ECM",
+ { .s = "FunctionFS + ECM" },
#endif
#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
- [GFS_STRING_GENERIC_CONFIG_IDX].s = "FunctionFS",
+ { .s = "FunctionFS" },
#endif
{ } /* end of list */
};
};
+
+struct gfs_configuration {
+ struct usb_configuration c;
+ int (*eth)(struct usb_configuration *c, u8 *ethaddr);
+} gfs_configurations[] = {
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
-static int gfs_do_rndis_config(struct usb_configuration *c);
-
-static struct usb_configuration gfs_rndis_config_driver = {
- .label = "FunctionFS + RNDIS",
- .bind = gfs_do_rndis_config,
- .bConfigurationValue = 1,
- /* .iConfiguration = DYNAMIC */
- .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
-};
-# define gfs_add_rndis_config(cdev) \
- usb_add_config(cdev, &gfs_rndis_config_driver)
-#else
-# define gfs_add_rndis_config(cdev) 0
+ {
+ .eth = rndis_bind_config,
+ },
#endif
-
#ifdef CONFIG_USB_FUNCTIONFS_ETH
-static int gfs_do_ecm_config(struct usb_configuration *c);
-
-static struct usb_configuration gfs_ecm_config_driver = {
- .label = "FunctionFS + ECM",
- .bind = gfs_do_ecm_config,
- .bConfigurationValue = 1,
- /* .iConfiguration = DYNAMIC */
- .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
-};
-# define gfs_add_ecm_config(cdev) \
- usb_add_config(cdev, &gfs_ecm_config_driver)
-#else
-# define gfs_add_ecm_config(cdev) 0
+ {
+ .eth = eth_bind_config,
+ },
#endif
-
#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
-static int gfs_do_generic_config(struct usb_configuration *c);
-
-static struct usb_configuration gfs_generic_config_driver = {
- .label = "FunctionFS",
- .bind = gfs_do_generic_config,
- .bConfigurationValue = 2,
- /* .iConfiguration = DYNAMIC */
- .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
-};
-# define gfs_add_generic_config(cdev) \
- usb_add_config(cdev, &gfs_generic_config_driver)
-#else
-# define gfs_add_generic_config(cdev) 0
+ {
+ },
#endif
+};
static int gfs_bind(struct usb_composite_dev *cdev);
static int gfs_unbind(struct usb_composite_dev *cdev);
+static int gfs_do_config(struct usb_configuration *c);
static struct usb_composite_driver gfs_driver = {
.name = gfs_short_name,
static int gfs_bind(struct usb_composite_dev *cdev)
{
- int ret;
+ int ret, i;
ENTER();
snprintf(gfs_manufacturer, sizeof gfs_manufacturer, "%s %s with %s",
init_utsname()->sysname, init_utsname()->release,
cdev->gadget->name);
- ret = usb_string_id(cdev);
- if (unlikely(ret < 0))
- goto error;
- gfs_strings[GFS_STRING_MANUFACTURER_IDX].id = ret;
- gfs_dev_desc.iManufacturer = ret;
-
- ret = usb_string_id(cdev);
- if (unlikely(ret < 0))
- goto error;
- gfs_strings[GFS_STRING_PRODUCT_IDX].id = ret;
- gfs_dev_desc.iProduct = ret;
-
-#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
- ret = usb_string_id(cdev);
- if (unlikely(ret < 0))
- goto error;
- gfs_strings[GFS_STRING_RNDIS_CONFIG_IDX].id = ret;
- gfs_rndis_config_driver.iConfiguration = ret;
-#endif
-#ifdef CONFIG_USB_FUNCTIONFS_ETH
- ret = usb_string_id(cdev);
+ ret = usb_string_ids_tab(cdev, gfs_strings);
if (unlikely(ret < 0))
goto error;
- gfs_strings[GFS_STRING_ECM_CONFIG_IDX].id = ret;
- gfs_ecm_config_driver.iConfiguration = ret;
-#endif
-#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
- ret = usb_string_id(cdev);
- if (unlikely(ret < 0))
- goto error;
- gfs_strings[GFS_STRING_GENERIC_CONFIG_IDX].id = ret;
- gfs_generic_config_driver.iConfiguration = ret;
-#endif
+ gfs_dev_desc.iManufacturer = gfs_strings[GFS_STRING_MANUFACTURER_IDX].id;
+ gfs_dev_desc.iProduct = gfs_strings[GFS_STRING_PRODUCT_IDX].id;
ret = functionfs_bind(gfs_ffs_data, cdev);
if (unlikely(ret < 0))
goto error;
- ret = gfs_add_rndis_config(cdev);
- if (unlikely(ret < 0))
- goto error_unbind;
+ for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) {
+ struct gfs_configuration *c = gfs_configurations + i;
- ret = gfs_add_ecm_config(cdev);
- if (unlikely(ret < 0))
- goto error_unbind;
+ ret = GFS_STRING_FIRST_CONFIG_IDX + i;
+ c->c.label = gfs_strings[ret].s;
+ c->c.iConfiguration = gfs_strings[ret].id;
+ c->c.bind = gfs_do_config;
+ c->c.bConfigurationValue = 1 + i;
+ c->c.bmAttributes = USB_CONFIG_ATT_SELFPOWER;
- ret = gfs_add_generic_config(cdev);
- if (unlikely(ret < 0))
- goto error_unbind;
+ ret = usb_add_config(cdev, &c->c);
+ if (unlikely(ret < 0))
+ goto error_unbind;
+ }
return 0;
}
-static int __gfs_do_config(struct usb_configuration *c,
- int (*eth)(struct usb_configuration *c, u8 *ethaddr),
- u8 *ethaddr)
+static int gfs_do_config(struct usb_configuration *c)
{
+ struct gfs_configuration *gc =
+ container_of(c, struct gfs_configuration, c);
int ret;
if (WARN_ON(!gfs_ffs_data))
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
- if (eth) {
- ret = eth(c, ethaddr);
+ if (gc->eth) {
+ ret = gc->eth(c, gfs_hostaddr);
if (unlikely(ret < 0))
return ret;
}
- ret = functionfs_add(c->cdev, c, gfs_ffs_data);
+ ret = functionfs_bind_config(c->cdev, c, gfs_ffs_data);
if (unlikely(ret < 0))
return ret;
return 0;
}
-#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
-static int gfs_do_rndis_config(struct usb_configuration *c)
-{
- ENTER();
-
- return __gfs_do_config(c, rndis_bind_config, gfs_hostaddr);
-}
-#endif
#ifdef CONFIG_USB_FUNCTIONFS_ETH
-static int gfs_do_ecm_config(struct usb_configuration *c)
-{
- ENTER();
-
- return __gfs_do_config(c,
- can_support_ecm(c->cdev->gadget)
- ? ecm_bind_config : geth_bind_config,
- gfs_hostaddr);
-}
-#endif
-
-#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
-static int gfs_do_generic_config(struct usb_configuration *c)
+static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
{
- ENTER();
-
- return __gfs_do_config(c, NULL, NULL);
+ return can_support_ecm(c->cdev->gadget)
+ ? ecm_bind_config(c, ethaddr)
+ : geth_bind_config(c, ethaddr);
}
#endif
/*
* Creates an output endpoint, and initializes output ports.
*/
-static int __init gmidi_bind(struct usb_gadget *gadget)
+static int __ref gmidi_bind(struct usb_gadget *gadget)
{
struct gmidi_device *dev;
struct usb_ep *in_ep, *out_ep;
/****************************** Configurations ******************************/
-static int __init do_config(struct usb_configuration *c)
+static int __ref do_config(struct usb_configuration *c)
{
struct hidg_func_node *e;
int func = 0, status = 0;
/****************************** Gadget Bind ******************************/
-static int __init hid_bind(struct usb_composite_dev *cdev)
+static int __ref hid_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
struct list_head *tmp;
struct usb_gadget *gadget = dev->gadget;
long ret = -ENOTTY;
- if (gadget->ops->ioctl) {
- lock_kernel();
+ if (gadget->ops->ioctl)
ret = gadget->ops->ioctl (gadget, code, value);
- unlock_kernel();
- }
+
return ret;
}
buf += 4;
length -= 4;
- kbuf = kmalloc (length, GFP_KERNEL);
- if (!kbuf)
- return -ENOMEM;
- if (copy_from_user (kbuf, buf, length)) {
- kfree (kbuf);
- return -EFAULT;
- }
+ kbuf = memdup_user(buf, length);
+ if (IS_ERR(kbuf))
+ return PTR_ERR(kbuf);
spin_lock_irq (&dev->lock);
value = -EINVAL;
VDBG(dev, "req->mapped = 0\n");
}
- DBG(dev, "%s queue req %p, len %u, buf %p, dma 0x%08x\n",
- _ep->name,
- _req, _req->length, _req->buf, _req->dma);
+ DBG(dev, "%s queue req %p, len %u, buf %p, dma 0x%08llx\n",
+ _ep->name,
+ _req, _req->length, _req->buf, (unsigned long long)_req->dma);
_req->status = -EINPROGRESS;
_req->actual = 0;
return 0;
}
-static int __init msg_do_config(struct usb_configuration *c)
+static int __ref msg_do_config(struct usb_configuration *c)
{
- struct fsg_common *common;
+ static const struct fsg_operations ops = {
+ .thread_exits = msg_thread_exits,
+ };
+ static struct fsg_common common;
+
+ struct fsg_common *retp;
struct fsg_config config;
int ret;
}
fsg_config_from_params(&config, &mod_data);
- config.thread_exits = msg_thread_exits;
- common = fsg_common_init(0, c->cdev, &config);
- if (IS_ERR(common))
- return PTR_ERR(common);
+ config.ops = &ops;
+
+ retp = fsg_common_init(&common, c->cdev, &config);
+ if (IS_ERR(retp))
+ return PTR_ERR(retp);
- ret = fsg_add(c->cdev, c, common);
- fsg_common_put(common);
+ ret = fsg_bind_config(c->cdev, c, &common);
+ fsg_common_put(&common);
return ret;
}
/****************************** Gadget Bind ******************************/
-static int __init msg_bind(struct usb_composite_dev *cdev)
+static int __ref msg_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
int status;
#include <linux/kernel.h>
#include <linux/utsname.h>
+#include <linux/module.h>
#if defined USB_ETH_RNDIS
#define DRIVER_DESC "Multifunction Composite Gadget"
-#define DRIVER_VERSION "2009/07/21"
-/*-------------------------------------------------------------------------*/
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Michal Nazarewicz");
+MODULE_LICENSE("GPL");
-#define MULTI_VENDOR_NUM 0x0525 /* XXX NetChip */
-#define MULTI_PRODUCT_NUM 0xa4ab /* XXX */
-/*-------------------------------------------------------------------------*/
+/***************************** All the files... *****************************/
/*
* kbuild is not very cooperative with respect to linking separately
#include "config.c"
#include "epautoconf.c"
+#include "f_mass_storage.c"
+
#include "u_serial.c"
#include "f_acm.c"
#endif
#include "u_ether.c"
-#undef DBG /* u_ether.c has broken idea about macros */
-#undef VDBG /* so clean up after it */
-#undef ERROR
-#undef INFO
-#include "f_mass_storage.c"
-/*-------------------------------------------------------------------------*/
+
+/***************************** Device Descriptor ****************************/
+
+#define MULTI_VENDOR_NUM 0x0525 /* XXX NetChip */
+#define MULTI_PRODUCT_NUM 0xa4ab /* XXX */
+
+
+enum {
+ __MULTI_NO_CONFIG,
+#ifdef CONFIG_USB_G_MULTI_RNDIS
+ MULTI_RNDIS_CONFIG_NUM,
+#endif
+#ifdef CONFIG_USB_G_MULTI_CDC
+ MULTI_CDC_CONFIG_NUM,
+#endif
+};
+
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bcdUSB = cpu_to_le16(0x0200),
- /* .bDeviceClass = USB_CLASS_COMM, */
- /* .bDeviceSubClass = 0, */
- /* .bDeviceProtocol = 0, */
- .bDeviceClass = 0xEF,
+ .bDeviceClass = USB_CLASS_MISC /* 0xEF */,
.bDeviceSubClass = 2,
.bDeviceProtocol = 1,
- /* .bMaxPacketSize0 = f(hardware) */
/* Vendor and product id can be overridden by module parameters. */
.idVendor = cpu_to_le16(MULTI_VENDOR_NUM),
.idProduct = cpu_to_le16(MULTI_PRODUCT_NUM),
- /* .bcdDevice = f(hardware) */
- /* .iManufacturer = DYNAMIC */
- /* .iProduct = DYNAMIC */
- /* NO SERIAL NUMBER */
- .bNumConfigurations = 1,
};
-static struct usb_otg_descriptor otg_descriptor = {
- .bLength = sizeof otg_descriptor,
- .bDescriptorType = USB_DT_OTG,
-
- /* REVISIT SRP-only hardware is possible, although
- * it would not be called "OTG" ...
- */
- .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
-};
static const struct usb_descriptor_header *otg_desc[] = {
- (struct usb_descriptor_header *) &otg_descriptor,
+ (struct usb_descriptor_header *) &(struct usb_otg_descriptor){
+ .bLength = sizeof(struct usb_otg_descriptor),
+ .bDescriptorType = USB_DT_OTG,
+
+ /*
+ * REVISIT SRP-only hardware is possible, although
+ * it would not be called "OTG" ...
+ */
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+ },
NULL,
};
-/* string IDs are assigned dynamically */
-
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
+enum {
+ MULTI_STRING_MANUFACTURER_IDX,
+ MULTI_STRING_PRODUCT_IDX,
+#ifdef CONFIG_USB_G_MULTI_RNDIS
+ MULTI_STRING_RNDIS_CONFIG_IDX,
+#endif
+#ifdef CONFIG_USB_G_MULTI_CDC
+ MULTI_STRING_CDC_CONFIG_IDX,
+#endif
+};
static char manufacturer[50];
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer,
- [STRING_PRODUCT_IDX].s = DRIVER_DESC,
+ [MULTI_STRING_MANUFACTURER_IDX].s = manufacturer,
+ [MULTI_STRING_PRODUCT_IDX].s = DRIVER_DESC,
+#ifdef CONFIG_USB_G_MULTI_RNDIS
+ [MULTI_STRING_RNDIS_CONFIG_IDX].s = "Multifunction with RNDIS",
+#endif
+#ifdef CONFIG_USB_G_MULTI_CDC
+ [MULTI_STRING_CDC_CONFIG_IDX].s = "Multifunction with CDC ECM",
+#endif
{ } /* end of list */
};
-static struct usb_gadget_strings stringtab_dev = {
- .language = 0x0409, /* en-us */
- .strings = strings_dev,
-};
-
static struct usb_gadget_strings *dev_strings[] = {
- &stringtab_dev,
+ &(struct usb_gadget_strings){
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+ },
NULL,
};
-static u8 hostaddr[ETH_ALEN];
/****************************** Configurations ******************************/
-static struct fsg_module_parameters mod_data = {
- .stall = 1
-};
-FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
+static struct fsg_module_parameters fsg_mod_data = { .stall = 1 };
+FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
+
+static struct fsg_common fsg_common;
+
+static u8 hostaddr[ETH_ALEN];
-static struct fsg_common *fsg_common;
+/********** RNDIS **********/
#ifdef USB_ETH_RNDIS
-static int __init rndis_do_config(struct usb_configuration *c)
+static __ref int rndis_do_config(struct usb_configuration *c)
{
int ret;
if (ret < 0)
return ret;
- ret = fsg_add(c->cdev, c, fsg_common);
+ ret = fsg_bind_config(c->cdev, c, &fsg_common);
if (ret < 0)
return ret;
return 0;
}
-static struct usb_configuration rndis_config_driver = {
- .label = "Multifunction Composite (RNDIS + MS + ACM)",
- .bind = rndis_do_config,
- .bConfigurationValue = 2,
- /* .iConfiguration = DYNAMIC */
- .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
-};
+static int rndis_config_register(struct usb_composite_dev *cdev)
+{
+ static struct usb_configuration config = {
+ .bind = rndis_do_config,
+ .bConfigurationValue = MULTI_RNDIS_CONFIG_NUM,
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+ };
+
+ config.label = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].s;
+ config.iConfiguration = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].id;
+
+ return usb_add_config(cdev, &config);
+}
+
+#else
+
+static int rndis_config_register(struct usb_composite_dev *cdev)
+{
+ return 0;
+}
#endif
+
+/********** CDC ECM **********/
+
#ifdef CONFIG_USB_G_MULTI_CDC
-static int __init cdc_do_config(struct usb_configuration *c)
+static __ref int cdc_do_config(struct usb_configuration *c)
{
int ret;
if (ret < 0)
return ret;
- ret = fsg_add(c->cdev, c, fsg_common);
+ ret = fsg_bind_config(c->cdev, c, &fsg_common);
if (ret < 0)
return ret;
return 0;
}
-static struct usb_configuration cdc_config_driver = {
- .label = "Multifunction Composite (CDC + MS + ACM)",
- .bind = cdc_do_config,
- .bConfigurationValue = 1,
- /* .iConfiguration = DYNAMIC */
- .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
-};
+static int cdc_config_register(struct usb_composite_dev *cdev)
+{
+ static struct usb_configuration config = {
+ .bind = cdc_do_config,
+ .bConfigurationValue = MULTI_CDC_CONFIG_NUM,
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+ };
+
+ config.label = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].s;
+ config.iConfiguration = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].id;
+
+ return usb_add_config(cdev, &config);
+}
+
+#else
+
+static int cdc_config_register(struct usb_composite_dev *cdev)
+{
+ return 0;
+}
#endif
/****************************** Gadget Bind ******************************/
-static int __init multi_bind(struct usb_composite_dev *cdev)
+static int __ref multi_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
int status, gcnum;
goto fail0;
/* set up mass storage function */
- fsg_common = fsg_common_from_params(0, cdev, &mod_data);
- if (IS_ERR(fsg_common)) {
- status = PTR_ERR(fsg_common);
- goto fail1;
+ {
+ void *retp;
+ retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data);
+ if (IS_ERR(retp)) {
+ status = PTR_ERR(retp);
+ goto fail1;
+ }
}
-
+ /* set bcdDevice */
gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0)
+ if (gcnum >= 0) {
device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- else {
- /* We assume that can_support_ecm() tells the truth;
- * but if the controller isn't recognized at all then
- * that assumption is a bit more likely to be wrong.
- */
- WARNING(cdev, "controller '%s' not recognized\n",
- gadget->name);
+ } else {
+ WARNING(cdev, "controller '%s' not recognized\n", gadget->name);
device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099);
}
-
- /* Allocate string descriptor numbers ... note that string
- * contents can be overridden by the composite_dev glue.
- */
-
- /* device descriptor strings: manufacturer, product */
+ /* allocate string descriptor numbers */
snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
init_utsname()->sysname, init_utsname()->release,
gadget->name);
- status = usb_string_id(cdev);
- if (status < 0)
- goto fail2;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
- device_desc.iManufacturer = status;
- status = usb_string_id(cdev);
- if (status < 0)
+ status = usb_string_ids_tab(cdev, strings_dev);
+ if (unlikely(status < 0))
goto fail2;
- strings_dev[STRING_PRODUCT_IDX].id = status;
- device_desc.iProduct = status;
-#ifdef USB_ETH_RNDIS
- /* register our first configuration */
- status = usb_add_config(cdev, &rndis_config_driver);
- if (status < 0)
+ device_desc.iManufacturer =
+ strings_dev[MULTI_STRING_MANUFACTURER_IDX].id;
+ device_desc.iProduct =
+ strings_dev[MULTI_STRING_PRODUCT_IDX].id;
+
+ /* register configurations */
+ status = rndis_config_register(cdev);
+ if (unlikely(status < 0))
goto fail2;
-#endif
-#ifdef CONFIG_USB_G_MULTI_CDC
- /* register our second configuration */
- status = usb_add_config(cdev, &cdc_config_driver);
- if (status < 0)
+ status = cdc_config_register(cdev);
+ if (unlikely(status < 0))
goto fail2;
-#endif
- dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n");
- fsg_common_put(fsg_common);
+ /* we're done */
+ dev_info(&gadget->dev, DRIVER_DESC "\n");
+ fsg_common_put(&fsg_common);
return 0;
+
+ /* error recovery */
fail2:
- fsg_common_put(fsg_common);
+ fsg_common_put(&fsg_common);
fail1:
gserial_cleanup();
fail0:
.unbind = __exit_p(multi_unbind),
};
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_AUTHOR("Michal Nazarewicz");
-MODULE_LICENSE("GPL");
-static int __init g_multi_init(void)
+static int __init multi_init(void)
{
return usb_composite_register(&multi_driver);
}
-module_init(g_multi_init);
+module_init(multi_init);
-static void __exit g_multi_cleanup(void)
+static void __exit multi_exit(void)
{
usb_composite_unregister(&multi_driver);
}
-module_exit(g_multi_cleanup);
+module_exit(multi_exit);
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
+#include <linux/mutex.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/timer.h>
#define DRIVER_DESC "Printer Gadget"
#define DRIVER_VERSION "2007 OCT 06"
+static DEFINE_MUTEX(printer_mutex);
static const char shortname [] = "printer";
static const char driver_desc [] = DRIVER_DESC;
unsigned long flags;
int ret = -EBUSY;
- lock_kernel();
+ mutex_lock(&printer_mutex);
dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev);
spin_lock_irqsave(&dev->lock, flags);
spin_unlock_irqrestore(&dev->lock, flags);
DBG(dev, "printer_open returned %x\n", ret);
- unlock_kernel();
+ mutex_unlock(&printer_mutex);
return ret;
}
set_gadget_data(gadget, NULL);
}
-static int __init
+static int __ref
printer_bind(struct usb_gadget *gadget)
{
struct printer_dev *dev;
* published by the Free Software Foundation.
*/
+#define DEBUG
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/clk.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <plat/regs-usb-hsotg.h>
#include <mach/regs-sys.h>
#include <plat/udc-hs.h>
+#include <plat/cpu.h>
#define DMA_ADDR_INVALID (~((dma_addr_t)0))
* For periodic IN endpoints, we have fifo_size and fifo_load to try
* and keep track of the amount of data in the periodic FIFO for each
* of these as we don't have a status register that tells us how much
- * is in each of them.
+ * is in each of them. (note, this may actually be useless information
+ * as in shared-fifo mode periodic in acts like a single-frame packet
+ * buffer than a fifo)
*/
struct s3c_hsotg_ep {
struct usb_ep ep;
* @regs: The memory area mapped for accessing registers.
* @regs_res: The resource that was allocated when claiming register space.
* @irq: The IRQ number we are using
+ * @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos.
* @debug_root: root directrory for debugfs.
* @debug_file: main status file for debugfs.
* @debug_fifo: FIFO status file for debugfs.
void __iomem *regs;
struct resource *regs_res;
int irq;
+ struct clk *clk;
+
+ unsigned int dedicated_fifos:1;
struct dentry *debug_root;
struct dentry *debug_file;
hsotg->regs + S3C_GNPTXFSIZ);
*/
- /* set FIFO sizes to 2048/0x1C0 */
+ /* set FIFO sizes to 2048/1024 */
writel(2048, hsotg->regs + S3C_GRXFSIZ);
writel(S3C_GNPTXFSIZ_NPTxFStAddr(2048) |
- S3C_GNPTXFSIZ_NPTxFDep(0x1C0),
+ S3C_GNPTXFSIZ_NPTxFDep(1024),
hsotg->regs + S3C_GNPTXFSIZ);
/* arange all the rest of the TX FIFOs, as some versions of this
if (to_write == 0)
return 0;
- if (periodic) {
+ if (periodic && !hsotg->dedicated_fifos) {
u32 epsize = readl(hsotg->regs + S3C_DIEPTSIZ(hs_ep->index));
int size_left;
int size_done;
size_left = S3C_DxEPTSIZ_XferSize_GET(epsize);
+ /* if shared fifo, we cannot write anything until the
+ * previous data has been completely sent.
+ */
+ if (hs_ep->fifo_load != 0) {
+ s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_PTxFEmp);
+ return -ENOSPC;
+ }
+
dev_dbg(hsotg->dev, "%s: left=%d, load=%d, fifo=%d, size %d\n",
__func__, size_left,
hs_ep->size_loaded, hs_ep->fifo_load, hs_ep->fifo_size);
s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_PTxFEmp);
return -ENOSPC;
}
+ } else if (hsotg->dedicated_fifos && hs_ep->index != 0) {
+ can_write = readl(hsotg->regs + S3C_DTXFSTS(hs_ep->index));
+
+ can_write &= 0xffff;
+ can_write *= 4;
} else {
if (S3C_GNPTXSTS_NPTxQSpcAvail_GET(gnptxsts) == 0) {
dev_dbg(hsotg->dev,
}
can_write = S3C_GNPTXSTS_NPTxFSpcAvail_GET(gnptxsts);
+ can_write *= 4; /* fifo size is in 32bit quantities. */
}
dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, mps %d\n",
if (can_write > 512)
can_write = 512;
+ /* limit the write to one max-packet size worth of data, but allow
+ * the transfer to return that it did not run out of fifo space
+ * doing it. */
+ if (to_write > hs_ep->ep.maxpacket) {
+ to_write = hs_ep->ep.maxpacket;
+
+ s3c_hsotg_en_gsint(hsotg,
+ periodic ? S3C_GINTSTS_PTxFEmp :
+ S3C_GINTSTS_NPTxFEmp);
+ }
+
/* see if we can write data */
if (to_write > can_write) {
maxsize = S3C_DxEPTSIZ_XferSize_LIMIT + 1;
maxpkt = S3C_DxEPTSIZ_PktCnt_LIMIT + 1;
} else {
+ maxsize = 64+64;
if (hs_ep->dir_in) {
- /* maxsize = S3C_DIEPTSIZ0_XferSize_LIMIT + 1; */
- maxsize = 64+64+1;
maxpkt = S3C_DIEPTSIZ0_PktCnt_LIMIT + 1;
} else {
- maxsize = 0x3f;
maxpkt = 2;
}
}
read_ptr = hs_req->req.actual;
max_req = hs_req->req.length - read_ptr;
+ dev_dbg(hsotg->dev, "%s: read %d/%d, done %d/%d\n",
+ __func__, to_read, max_req, read_ptr, hs_req->req.length);
+
if (to_read > max_req) {
/* more data appeared than we where willing
* to deal with in this request.
WARN_ON_ONCE(1);
}
- dev_dbg(hsotg->dev, "%s: read %d/%d, done %d/%d\n",
- __func__, to_read, max_req, read_ptr, hs_req->req.length);
-
hs_ep->total_data += to_read;
hs_req->req.actual += to_read;
to_read = DIV_ROUND_UP(to_read, 4);
static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg,
int epnum, bool was_setup)
{
+ u32 epsize = readl(hsotg->regs + S3C_DOEPTSIZ(epnum));
struct s3c_hsotg_ep *hs_ep = &hsotg->eps[epnum];
struct s3c_hsotg_req *hs_req = hs_ep->req;
struct usb_request *req = &hs_req->req;
+ unsigned size_left = S3C_DxEPTSIZ_XferSize_GET(epsize);
int result = 0;
if (!hs_req) {
}
if (using_dma(hsotg)) {
- u32 epsize = readl(hsotg->regs + S3C_DOEPTSIZ(epnum));
unsigned size_done;
- unsigned size_left;
/* Calculate the size of the transfer by checking how much
* is left in the endpoint size register and then working it
* so may overshoot/undershoot the transfer.
*/
- size_left = S3C_DxEPTSIZ_XferSize_GET(epsize);
-
size_done = hs_ep->size_loaded - size_left;
size_done += hs_ep->last_load;
req->actual = size_done;
}
+ /* if there is more request to do, schedule new transfer */
+ if (req->actual < req->length && size_left == 0) {
+ s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
+ return;
+ }
+
if (req->actual < req->length && req->short_not_ok) {
dev_dbg(hsotg->dev, "%s: got %d/%d (short not ok) => error\n",
__func__, req->actual, req->length);
if (dir_in) {
s3c_hsotg_complete_in(hsotg, hs_ep);
- if (idx == 0)
+ if (idx == 0 && !hs_ep->req)
s3c_hsotg_enqueue_setup(hsotg);
} else if (using_dma(hsotg)) {
/* We're using DMA, we need to fire an OutDone here
__func__, idx);
clear |= S3C_DIEPMSK_INTknEPMisMsk;
}
+
+ /* FIFO has space or is empty (see GAHBCFG) */
+ if (hsotg->dedicated_fifos &&
+ ints & S3C_DIEPMSK_TxFIFOEmpty) {
+ dev_dbg(hsotg->dev, "%s: ep%d: TxFIFOEmpty\n",
+ __func__, idx);
+ s3c_hsotg_trytx(hsotg, hs_ep);
+ clear |= S3C_DIEPMSK_TxFIFOEmpty;
+ }
}
writel(clear, hsotg->regs + epint_reg);
kill_all_requests(hsotg, &hsotg->eps[0], -ECONNRESET, true);
/* it seems after a reset we can end up with a situation
- * where the TXFIFO still has data in it... try flushing
- * it to remove anything that may still be in it.
+ * where the TXFIFO still has data in it... the docs
+ * suggest resetting all the fifos, so use the init_fifo
+ * code to relayout and flush the fifos.
*/
- if (1) {
- writel(S3C_GRSTCTL_TxFNum(0) | S3C_GRSTCTL_TxFFlsh,
- hsotg->regs + S3C_GRSTCTL);
-
- dev_info(hsotg->dev, "GNPTXSTS=%08x\n",
- readl(hsotg->regs + S3C_GNPTXSTS));
- }
+ s3c_hsotg_init_fifo(hsotg);
s3c_hsotg_enqueue_setup(hsotg);
break;
}
+ /* if the hardware has dedicated fifos, we must give each IN EP
+ * a unique tx-fifo even if it is non-periodic.
+ */
+ if (dir_in && hsotg->dedicated_fifos)
+ epctrl |= S3C_DxEPCTL_TxFNum(index);
+
/* for non control endpoints, set PID to D0 */
if (index)
epctrl |= S3C_DxEPCTL_SetD0PID;
writel(S3C_DIEPMSK_TimeOUTMsk | S3C_DIEPMSK_AHBErrMsk |
S3C_DIEPMSK_INTknEPMisMsk |
- S3C_DIEPMSK_EPDisbldMsk | S3C_DIEPMSK_XferComplMsk,
+ S3C_DIEPMSK_EPDisbldMsk | S3C_DIEPMSK_XferComplMsk |
+ ((hsotg->dedicated_fifos) ? S3C_DIEPMSK_TxFIFOEmpty : 0),
hsotg->regs + S3C_DIEPMSK);
/* don't need XferCompl, we get that from RXFIFO in slave mode. In
*/
ptxfifo = readl(hsotg->regs + S3C_DPTXFSIZn(epnum));
- hs_ep->fifo_size = S3C_DPTXFSIZn_DPTxFSize_GET(ptxfifo);
+ hs_ep->fifo_size = S3C_DPTXFSIZn_DPTxFSize_GET(ptxfifo) * 4;
/* if we're using dma, we need to set the next-endpoint pointer
* to be something valid.
*/
static void s3c_hsotg_otgreset(struct s3c_hsotg *hsotg)
{
- u32 osc;
+ struct clk *xusbxti;
+ u32 pwr, osc;
- writel(0, S3C_PHYPWR);
+ pwr = readl(S3C_PHYPWR);
+ pwr &= ~0x19;
+ writel(pwr, S3C_PHYPWR);
mdelay(1);
osc = hsotg->plat->is_osc ? S3C_PHYCLK_EXT_OSC : 0;
+ xusbxti = clk_get(hsotg->dev, "xusbxti");
+ if (xusbxti && !IS_ERR(xusbxti)) {
+ switch (clk_get_rate(xusbxti)) {
+ case 12*MHZ:
+ osc |= S3C_PHYCLK_CLKSEL_12M;
+ break;
+ case 24*MHZ:
+ osc |= S3C_PHYCLK_CLKSEL_24M;
+ break;
+ default:
+ case 48*MHZ:
+ /* default reference clock */
+ break;
+ }
+ clk_put(xusbxti);
+ }
+
writel(osc | 0x10, S3C_PHYCLK);
/* issue a full set of resets to the otg and core */
static void s3c_hsotg_init(struct s3c_hsotg *hsotg)
{
+ u32 cfg4;
+
/* unmask subset of endpoint interrupts */
writel(S3C_DIEPMSK_TimeOUTMsk | S3C_DIEPMSK_AHBErrMsk |
writel(using_dma(hsotg) ? S3C_GAHBCFG_DMAEn : 0x0,
hsotg->regs + S3C_GAHBCFG);
+
+ /* check hardware configuration */
+
+ cfg4 = readl(hsotg->regs + 0x50);
+ hsotg->dedicated_fifos = (cfg4 >> 25) & 1;
+
+ dev_info(hsotg->dev, "%s fifos\n",
+ hsotg->dedicated_fifos ? "dedicated" : "shared");
}
static void s3c_hsotg_dump(struct s3c_hsotg *hsotg)
hsotg->dev = dev;
hsotg->plat = plat;
+ hsotg->clk = clk_get(&pdev->dev, "otg");
+ if (IS_ERR(hsotg->clk)) {
+ dev_err(dev, "cannot get otg clock\n");
+ ret = -EINVAL;
+ goto err_mem;
+ }
+
platform_set_drvdata(pdev, hsotg);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "cannot find register resource 0\n");
ret = -EINVAL;
- goto err_mem;
+ goto err_clk;
}
hsotg->regs_res = request_mem_region(res->start, resource_size(res),
if (!hsotg->regs_res) {
dev_err(dev, "cannot reserve registers\n");
ret = -ENOENT;
- goto err_mem;
+ goto err_clk;
}
hsotg->regs = ioremap(res->start, resource_size(res));
/* reset the system */
+ clk_enable(hsotg->clk);
+
s3c_hsotg_gate(pdev, true);
s3c_hsotg_otgreset(hsotg);
err_regs_res:
release_resource(hsotg->regs_res);
kfree(hsotg->regs_res);
-
+err_clk:
+ clk_put(hsotg->clk);
err_mem:
kfree(hsotg);
return ret;
s3c_hsotg_gate(pdev, false);
+ clk_disable(hsotg->clk);
+ clk_put(hsotg->clk);
+
kfree(hsotg);
return 0;
}
/*-------------------------------------------------------------------------*/
-static int __init serial_bind_config(struct usb_configuration *c)
+static int __ref serial_bind_config(struct usb_configuration *c)
{
unsigned i;
int status = 0;
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
};
-static int __init gs_bind(struct usb_composite_dev *cdev)
+static int __ref gs_bind(struct usb_composite_dev *cdev)
{
int gcnum;
struct usb_gadget *gadget = cdev->gadget;
#include <asm/unaligned.h>
-/* Thanks to NetChip Technologies for donating this product ID.
+/*
+ * Thanks to NetChip Technologies for donating this product ID.
*
* DO NOT REUSE THESE IDs with any other driver!! Ever!!
- * Instead: allocate your own, using normal USB-IF procedures. */
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
#define FSG_VENDOR_ID 0x0525 /* NetChip */
#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */
#define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args)
#define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args)
-/* Keep those macros in sync with thos in
- * include/linux/ubs/composite.h or else GCC will complain. If they
+/*
+ * Keep those macros in sync with those in
+ * include/linux/usb/composite.h or else GCC will complain. If they
* are identical (the same names of arguments, white spaces in the
* same places) GCC will allow redefinition otherwise (even if some
- * white space is removed or added) warning will be issued. No
- * checking if those symbols is defined is performed because warning
- * is desired when those macros were defined by someone else to mean
- * something else. */
+ * white space is removed or added) warning will be issued.
+ *
+ * Those macros are needed here because File Storage Gadget does not
+ * include the composite.h header. For composite gadgets those macros
+ * are redundant since composite.h is included any way.
+ *
+ * One could check whether those macros are already defined (which
+ * would indicate composite.h had been included) or not (which would
+ * indicate we were in FSG) but this is not done because a warning is
+ * desired if definitions here differ from the ones in composite.h.
+ *
+ * We want the definitions to match and be the same in File Storage
+ * Gadget as well as Mass Storage Function (and so composite gadgets
+ * using MSF). If someone changes them in composite.h it will produce
+ * a warning in this file when building MSF.
+ */
#define DBG(d, fmt, args...) dev_dbg(&(d)->gadget->dev , fmt , ## args)
#define VDBG(d, fmt, args...) dev_vdbg(&(d)->gadget->dev , fmt , ## args)
#define ERROR(d, fmt, args...) dev_err(&(d)->gadget->dev , fmt , ## args)
unsigned int prevent_medium_removal:1;
unsigned int registered:1;
unsigned int info_valid:1;
+ unsigned int nofua:1;
u32 sense_data;
u32 sense_data_info;
enum fsg_buffer_state state;
struct fsg_buffhd *next;
- /* The NetChip 2280 is faster, and handles some protocol faults
+ /*
+ * The NetChip 2280 is faster, and handles some protocol faults
* better, if we don't submit any short bulk-out read requests.
- * So we will record the intended request length here. */
+ * So we will record the intended request length here.
+ */
unsigned int bulk_out_intended_length;
struct usb_request *inreq;
.iInterface = FSG_STRING_INTERFACE,
};
-/* Three full-speed endpoint descriptors: bulk-in, bulk-out,
- * and interrupt-in. */
+/*
+ * Three full-speed endpoint descriptors: bulk-in, bulk-out, and
+ * interrupt-in.
+ */
static struct usb_endpoint_descriptor
fsg_fs_bulk_in_desc = {
*
* That means alternate endpoint descriptors (bigger packets)
* and a "device qualifier" ... plus more construction options
- * for the config descriptor.
+ * for the configuration descriptor.
*/
static struct usb_endpoint_descriptor
fsg_hs_bulk_in_desc = {
/*-------------------------------------------------------------------------*/
-/* If the next two routines are called while the gadget is registered,
- * the caller must own fsg->filesem for writing. */
+/*
+ * If the next two routines are called while the gadget is registered,
+ * the caller must own fsg->filesem for writing.
+ */
static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
{
goto out;
}
- /* If we can't read the file, it's no good.
- * If we can't write the file, use it read-only. */
+ /*
+ * If we can't read the file, it's no good.
+ * If we can't write the file, use it read-only.
+ */
if (!filp->f_op || !(filp->f_op->read || filp->f_op->aio_read)) {
LINFO(curlun, "file not readable: %s\n", filename);
goto out;
/*-------------------------------------------------------------------------*/
-/* Sync the file data, don't bother with the metadata.
- * This code was copied from fs/buffer.c:sys_fdatasync(). */
+/*
+ * Sync the file data, don't bother with the metadata.
+ * This code was copied from fs/buffer.c:sys_fdatasync().
+ */
static int fsg_lun_fsync_sub(struct fsg_lun *curlun)
{
struct file *filp = curlun->filp;
: curlun->initially_ro);
}
+static ssize_t fsg_show_nofua(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+
+ return sprintf(buf, "%u\n", curlun->nofua);
+}
+
static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr,
char *buf)
{
ssize_t rc = count;
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
struct rw_semaphore *filesem = dev_get_drvdata(dev);
- int i;
+ unsigned long ro;
- if (sscanf(buf, "%d", &i) != 1)
+ if (strict_strtoul(buf, 2, &ro))
return -EINVAL;
- /* Allow the write-enable status to change only while the backing file
- * is closed. */
+ /*
+ * Allow the write-enable status to change only while the
+ * backing file is closed.
+ */
down_read(filesem);
if (fsg_lun_is_open(curlun)) {
LDBG(curlun, "read-only status change prevented\n");
rc = -EBUSY;
} else {
- curlun->ro = !!i;
- curlun->initially_ro = !!i;
+ curlun->ro = ro;
+ curlun->initially_ro = ro;
LDBG(curlun, "read-only status set to %d\n", curlun->ro);
}
up_read(filesem);
return rc;
}
+static ssize_t fsg_store_nofua(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+ unsigned long nofua;
+
+ if (strict_strtoul(buf, 2, &nofua))
+ return -EINVAL;
+
+ /* Sync data when switching from async mode to sync */
+ if (!nofua && curlun->nofua)
+ fsg_lun_fsync_sub(curlun);
+
+ curlun->nofua = nofua;
+
+ return count;
+}
+
static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
module_param(host_addr, charp, S_IRUGO);
MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
-
-static u8 __init nibble(unsigned char c)
-{
- if (isdigit(c))
- return c - '0';
- c = toupper(c);
- if (isxdigit(c))
- return 10 + c - 'A';
- return 0;
-}
-
static int get_ether_addr(const char *str, u8 *dev_addr)
{
if (str) {
if ((*str == '.') || (*str == ':'))
str++;
- num = nibble(*str++) << 4;
- num |= (nibble(*str++));
+ num = hex_to_bin(*str++) << 4;
+ num |= hex_to_bin(*str++);
dev_addr [i] = num;
}
if (is_valid_ether_addr(dev_addr))
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
+#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/delay.h>
* USB configuration
*/
-static int __init
+static int __ref
webcam_config_bind(struct usb_configuration *c)
{
return uvc_bind_config(c, uvc_control_cls, uvc_fs_streaming_cls,
return 0;
}
-static int __init
+static int __ref
webcam_bind(struct usb_composite_dev *cdev)
{
int ret;
/*-------------------------------------------------------------------------*/
-static int __init zero_bind(struct usb_composite_dev *cdev)
+static int __ref zero_bind(struct usb_composite_dev *cdev)
{
int gcnum;
struct usb_gadget *gadget = cdev->gadget;
from ARC, and has since changed hands a few times.
config USB_EHCI_TT_NEWSCHED
- bool "Improved Transaction Translator scheduling (EXPERIMENTAL)"
- depends on USB_EHCI_HCD && EXPERIMENTAL
+ bool "Improved Transaction Translator scheduling"
+ depends on USB_EHCI_HCD
+ default y
---help---
This changes the periodic scheduling code to fill more of the low
and full speed bandwidth available from the Transaction Translator
If you have multiple periodic low/fullspeed devices connected to a
highspeed USB hub which is connected to a highspeed USB Host
Controller, and some of those devices will not work correctly
- (possibly due to "ENOSPC" or "-28" errors), say Y.
+ (possibly due to "ENOSPC" or "-28" errors), say Y. Conversely, if
+ you have only one such device and it doesn't work, you could try
+ saying N.
- If unsure, say N.
+ If unsure, say Y.
config USB_EHCI_BIG_ENDIAN_MMIO
bool
* the root hub is either suspended or stopped.
*/
spin_lock_irqsave(&ehci->lock, flags);
- ehci_prepare_ports_for_controller_suspend(ehci);
+ ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev));
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
(void)ehci_readl(ehci, &ehci->regs->intr_enable);
HCC_64BIT_ADDR(params) ? " 64 bit addr" : "");
} else {
ehci_dbg (ehci,
- "%s hcc_params %04x thresh %d uframes %s%s%s\n",
+ "%s hcc_params %04x thresh %d uframes %s%s%s%s%s%s%s\n",
label,
params,
HCC_ISOC_THRES(params),
HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024",
HCC_CANPARK(params) ? " park" : "",
- HCC_64BIT_ADDR(params) ? " 64 bit addr" : "");
+ HCC_64BIT_ADDR(params) ? " 64 bit addr" : "",
+ HCC_LPM(params) ? " LPM" : "",
+ HCC_PER_PORT_CHANGE_EVENT(params) ? " ppce" : "",
+ HCC_HW_PREFETCH(params) ? " hw prefetch" : "",
+ HCC_32FRAME_PERIODIC_LIST(params) ?
+ " 32 peridic list" : "");
}
}
#else
dbg_status_buf (char *buf, unsigned len, const char *label, u32 status)
{
return scnprintf (buf, len,
- "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s",
+ "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s%s",
label, label [0] ? " " : "", status,
+ (status & STS_PPCE_MASK) ? " PPCE" : "",
(status & STS_ASS) ? " Async" : "",
(status & STS_PSS) ? " Periodic" : "",
(status & STS_RECL) ? " Recl" : "",
dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable)
{
return scnprintf (buf, len,
- "%s%sintrenable %02x%s%s%s%s%s%s",
+ "%s%sintrenable %02x%s%s%s%s%s%s%s",
label, label [0] ? " " : "", enable,
+ (enable & STS_PPCE_MASK) ? " PPCE" : "",
(enable & STS_IAA) ? " IAA" : "",
(enable & STS_FATAL) ? " FATAL" : "",
(enable & STS_FLR) ? " FLR" : "",
dbg_command_buf (char *buf, unsigned len, const char *label, u32 command)
{
return scnprintf (buf, len,
- "%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s",
+ "%s%scommand %07x %s%s%s%s%s%s=%d ithresh=%d%s%s%s%s "
+ "period=%s%s %s",
label, label [0] ? " " : "", command,
- (command & CMD_PARK) ? "park" : "(park)",
+ (command & CMD_HIRD) ? " HIRD" : "",
+ (command & CMD_PPCEE) ? " PPCEE" : "",
+ (command & CMD_FSP) ? " FSP" : "",
+ (command & CMD_ASPE) ? " ASPE" : "",
+ (command & CMD_PSPE) ? " PSPE" : "",
+ (command & CMD_PARK) ? " park" : "(park)",
CMD_PARK_CNT (command),
(command >> 16) & 0x3f,
(command & CMD_LRESET) ? " LReset" : "",
}
return scnprintf (buf, len,
- "%s%sport %d status %06x%s%s sig=%s%s%s%s%s%s%s%s%s%s",
+ "%s%sport:%d status %06x %d %s%s%s%s%s%s "
+ "sig=%s%s%s%s%s%s%s%s%s%s%s",
label, label [0] ? " " : "", port, status,
+ status>>25,/*device address */
+ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ACK ?
+ " ACK" : "",
+ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_NYET ?
+ " NYET" : "",
+ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_STALL ?
+ " STALL" : "",
+ (status & PORT_SSTS)>>23 == PORTSC_SUSPEND_STS_ERR ?
+ " ERR" : "",
(status & PORT_POWER) ? " POWER" : "",
(status & PORT_OWNER) ? " OWNER" : "",
sig,
+ (status & PORT_LPM) ? " LPM" : "",
(status & PORT_RESET) ? " RESET" : "",
(status & PORT_SUSPEND) ? " SUSPEND" : "",
(status & PORT_RESUME) ? " RESUME" : "",
static int debug_periodic_open(struct inode *, struct file *);
static int debug_registers_open(struct inode *, struct file *);
static int debug_async_open(struct inode *, struct file *);
+static int debug_lpm_open(struct inode *, struct file *);
+static ssize_t debug_lpm_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos);
+static ssize_t debug_lpm_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos);
+static int debug_lpm_close(struct inode *inode, struct file *file);
+
static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*);
static int debug_close(struct inode *, struct file *);
.read = debug_output,
.release = debug_close,
};
+static const struct file_operations debug_lpm_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_lpm_open,
+ .read = debug_lpm_read,
+ .write = debug_lpm_write,
+ .release = debug_lpm_close,
+};
static struct dentry *ehci_debug_root;
spin_lock_irqsave (&ehci->lock, flags);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
size = scnprintf (next, size,
"bus %s, device %s\n"
"%s\n"
return file->private_data ? 0 : -ENOMEM;
}
+static int debug_lpm_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static int debug_lpm_close(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static ssize_t debug_lpm_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ /* TODO: show lpm stats */
+ return 0;
+}
+
+static ssize_t debug_lpm_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ char buf[50];
+ size_t len;
+ u32 temp;
+ unsigned long port;
+ u32 __iomem *portsc ;
+ u32 params;
+
+ hcd = bus_to_hcd(file->private_data);
+ ehci = hcd_to_ehci(hcd);
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+ buf[len] = '\0';
+ if (len > 0 && buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+
+ if (strncmp(buf, "enable", 5) == 0) {
+ if (strict_strtoul(buf + 7, 10, &port))
+ return -EINVAL;
+ params = ehci_readl(ehci, &ehci->caps->hcs_params);
+ if (port > HCS_N_PORTS(params)) {
+ ehci_dbg(ehci, "ERR: LPM on bad port %lu\n", port);
+ return -ENODEV;
+ }
+ portsc = &ehci->regs->port_status[port-1];
+ temp = ehci_readl(ehci, portsc);
+ if (!(temp & PORT_DEV_ADDR)) {
+ ehci_dbg(ehci, "LPM: no device attached\n");
+ return -ENODEV;
+ }
+ temp |= PORT_LPM;
+ ehci_writel(ehci, temp, portsc);
+ printk(KERN_INFO "force enable LPM for port %lu\n", port);
+ } else if (strncmp(buf, "hird=", 5) == 0) {
+ unsigned long hird;
+ if (strict_strtoul(buf + 5, 16, &hird))
+ return -EINVAL;
+ printk(KERN_INFO "setting hird %s %lu\n", buf + 6, hird);
+ temp = ehci_readl(ehci, &ehci->regs->command);
+ temp &= ~CMD_HIRD;
+ temp |= hird << 24;
+ ehci_writel(ehci, temp, &ehci->regs->command);
+ } else if (strncmp(buf, "disable", 7) == 0) {
+ if (strict_strtoul(buf + 8, 10, &port))
+ return -EINVAL;
+ params = ehci_readl(ehci, &ehci->caps->hcs_params);
+ if (port > HCS_N_PORTS(params)) {
+ ehci_dbg(ehci, "ERR: LPM off bad port %lu\n", port);
+ return -ENODEV;
+ }
+ portsc = &ehci->regs->port_status[port-1];
+ temp = ehci_readl(ehci, portsc);
+ if (!(temp & PORT_DEV_ADDR)) {
+ ehci_dbg(ehci, "ERR: no device attached\n");
+ return -ENODEV;
+ }
+ temp &= ~PORT_LPM;
+ ehci_writel(ehci, temp, portsc);
+ printk(KERN_INFO "disabled LPM for port %lu\n", port);
+ } else
+ return -EOPNOTSUPP;
+ return count;
+}
+
static inline void create_debug_files (struct ehci_hcd *ehci)
{
struct usb_bus *bus = &ehci_to_hcd(ehci)->self;
ehci->debug_dir = debugfs_create_dir(bus->bus_name, ehci_debug_root);
if (!ehci->debug_dir)
- goto dir_error;
-
- ehci->debug_async = debugfs_create_file("async", S_IRUGO,
- ehci->debug_dir, bus,
- &debug_async_fops);
- if (!ehci->debug_async)
- goto async_error;
-
- ehci->debug_periodic = debugfs_create_file("periodic", S_IRUGO,
- ehci->debug_dir, bus,
- &debug_periodic_fops);
- if (!ehci->debug_periodic)
- goto periodic_error;
-
- ehci->debug_registers = debugfs_create_file("registers", S_IRUGO,
- ehci->debug_dir, bus,
- &debug_registers_fops);
- if (!ehci->debug_registers)
- goto registers_error;
+ return;
+
+ if (!debugfs_create_file("async", S_IRUGO, ehci->debug_dir, bus,
+ &debug_async_fops))
+ goto file_error;
+
+ if (!debugfs_create_file("periodic", S_IRUGO, ehci->debug_dir, bus,
+ &debug_periodic_fops))
+ goto file_error;
+
+ if (!debugfs_create_file("registers", S_IRUGO, ehci->debug_dir, bus,
+ &debug_registers_fops))
+ goto file_error;
+
+ if (!debugfs_create_file("lpm", S_IRUGO|S_IWUGO, ehci->debug_dir, bus,
+ &debug_lpm_fops))
+ goto file_error;
+
return;
-registers_error:
- debugfs_remove(ehci->debug_periodic);
-periodic_error:
- debugfs_remove(ehci->debug_async);
-async_error:
- debugfs_remove(ehci->debug_dir);
-dir_error:
- ehci->debug_periodic = NULL;
- ehci->debug_async = NULL;
- ehci->debug_dir = NULL;
+file_error:
+ debugfs_remove_recursive(ehci->debug_dir);
}
static inline void remove_debug_files (struct ehci_hcd *ehci)
{
- debugfs_remove(ehci->debug_registers);
- debugfs_remove(ehci->debug_periodic);
- debugfs_remove(ehci->debug_async);
- debugfs_remove(ehci->debug_dir);
+ debugfs_remove_recursive(ehci->debug_dir);
}
#endif /* STUB_DEBUG_FILES */
struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd);
void __iomem *non_ehci = hcd->regs;
- ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd));
+ ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd),
+ device_may_wakeup(dev));
if (!fsl_deep_sleep())
return 0;
#include <linux/dma-mapping.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
+#include <linux/uaccess.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#define EHCI_TUNE_RL_TT 0
#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */
#define EHCI_TUNE_MULT_TT 1
-#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */
+/*
+ * Some drivers think it's safe to schedule isochronous transfers more than
+ * 256 ms into the future (partly as a result of an old bug in the scheduling
+ * code). In an attempt to avoid trouble, we will use a minimum scheduling
+ * length of 512 frames instead of 256.
+ */
+#define EHCI_TUNE_FLS 1 /* (medium) 512-frame schedule */
#define EHCI_IAA_MSECS 10 /* arbitrary */
#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
module_param (ignore_oc, bool, S_IRUGO);
MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications");
+/* for link power management(LPM) feature */
+static unsigned int hird;
+module_param(hird, int, S_IRUGO);
+MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us\n");
+
#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
/*-------------------------------------------------------------------------*/
static void ehci_work(struct ehci_hcd *ehci);
#include "ehci-hub.c"
+#include "ehci-lpm.c"
#include "ehci-mem.c"
#include "ehci-q.c"
#include "ehci-sched.c"
if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
log2_irq_thresh = 0;
temp = 1 << (16 + log2_irq_thresh);
+ if (HCC_PER_PORT_CHANGE_EVENT(hcc_params)) {
+ ehci->has_ppcd = 1;
+ ehci_dbg(ehci, "enable per-port change event\n");
+ temp |= CMD_PPCEE;
+ }
if (HCC_CANPARK(hcc_params)) {
/* HW default park == 3, on hardware that supports it (like
* NVidia and ALI silicon), maximizes throughput on the async
default: BUG();
}
}
+ if (HCC_LPM(hcc_params)) {
+ /* support link power management EHCI 1.1 addendum */
+ ehci_dbg(ehci, "support lpm\n");
+ ehci->has_lpm = 1;
+ if (hird > 0xf) {
+ ehci_dbg(ehci, "hird %d invalid, use default 0",
+ hird);
+ hird = 0;
+ }
+ temp |= hird << 24;
+ }
ehci->command = temp;
/* Accept arbitrarily long scatter-gather lists */
- hcd->self.sg_tablesize = ~0;
+ if (!(hcd->driver->flags & HCD_LOCAL_MEM))
+ hcd->self.sg_tablesize = ~0;
return 0;
}
u32 hcc_params;
hcd->uses_new_polling = 1;
- hcd->poll_rh = 0;
/* EHCI spec section 4.1 */
if ((retval = ehci_reset(ehci)) != 0) {
/* remote wakeup [4.3.1] */
if (status & STS_PCD) {
unsigned i = HCS_N_PORTS (ehci->hcs_params);
+ u32 ppcd = 0;
/* kick root hub later */
pcd_status = status;
if (!(cmd & CMD_RUN))
usb_hcd_resume_root_hub(hcd);
+ /* get per-port change detect bits */
+ if (ehci->has_ppcd)
+ ppcd = status >> 16;
+
while (i--) {
- int pstatus = ehci_readl(ehci,
- &ehci->regs->port_status [i]);
+ int pstatus;
+
+ /* leverage per-port change bits feature */
+ if (ehci->has_ppcd && !(ppcd & (1 << i)))
+ continue;
+ pstatus = ehci_readl(ehci,
+ &ehci->regs->port_status[i]);
if (pstatus & PORT_OWNER)
continue;
}
static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
- bool suspending)
+ bool suspending, bool do_wakeup)
{
int port;
u32 temp;
* when the controller is suspended or resumed. In all other
* cases they don't need to be changed.
*/
- if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup ||
- device_may_wakeup(ehci_to_hcd(ehci)->self.controller))
+ if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup || do_wakeup)
return;
/* clear phy low-power mode before changing wakeup flags */
ehci_writel(ehci, temp | HOSTPC_PHCD, hostpc_reg);
}
}
+
+ /* Does the root hub have a port wakeup pending? */
+ if (!suspending && (ehci_readl(ehci, &ehci->regs->status) & STS_PCD))
+ usb_hcd_resume_root_hub(ehci_to_hcd(ehci));
}
static int ehci_bus_suspend (struct usb_hcd *hcd)
if (time_before (jiffies, ehci->next_statechange))
msleep(5);
spin_lock_irq (&ehci->lock);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
spin_unlock_irq(&ehci->lock);
return -ESHUTDOWN;
}
u32 mask;
int ports, i, retval = 1;
unsigned long flags;
+ u32 ppcd = 0;
/* if !USB_SUSPEND, root hub timers won't get shut down ... */
if (!HC_IS_RUNNING(hcd->state))
/* port N changes (bit N)? */
spin_lock_irqsave (&ehci->lock, flags);
+
+ /* get per-port change detect bits */
+ if (ehci->has_ppcd)
+ ppcd = ehci_readl(ehci, &ehci->regs->status) >> 16;
+
for (i = 0; i < ports; i++) {
+ /* leverage per-port change bits feature */
+ if (ehci->has_ppcd && !(ppcd & (1 << i)))
+ continue;
temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
/*
status_reg);
break;
case USB_PORT_FEAT_C_CONNECTION:
+ if (ehci->has_lpm) {
+ /* clear PORTSC bits on disconnect */
+ temp &= ~PORT_LPM;
+ temp &= ~PORT_DEV_ADDR;
+ }
ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_CSC,
status_reg);
break;
--- /dev/null
+/* ehci-lpm.c EHCI HCD LPM support code
+ * Copyright (c) 2008 - 2010, Intel Corporation.
+ * Author: Jacob Pan <jacob.jun.pan@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* this file is part of ehci-hcd.c */
+static int ehci_lpm_set_da(struct ehci_hcd *ehci, int dev_addr, int port_num)
+{
+ u32 __iomem portsc;
+
+ ehci_dbg(ehci, "set dev address %d for port %d\n", dev_addr, port_num);
+ if (port_num > HCS_N_PORTS(ehci->hcs_params)) {
+ ehci_dbg(ehci, "invalid port number %d\n", port_num);
+ return -ENODEV;
+ }
+ portsc = ehci_readl(ehci, &ehci->regs->port_status[port_num-1]);
+ portsc &= ~PORT_DEV_ADDR;
+ portsc |= dev_addr<<25;
+ ehci_writel(ehci, portsc, &ehci->regs->port_status[port_num-1]);
+ return 0;
+}
+
+/*
+ * this function is used to check if the device support LPM
+ * if yes, mark the PORTSC register with PORT_LPM bit
+ */
+static int ehci_lpm_check(struct ehci_hcd *ehci, int port)
+{
+ u32 __iomem *portsc ;
+ u32 val32;
+ int retval;
+
+ portsc = &ehci->regs->port_status[port-1];
+ val32 = ehci_readl(ehci, portsc);
+ if (!(val32 & PORT_DEV_ADDR)) {
+ ehci_dbg(ehci, "LPM: no device attached\n");
+ return -ENODEV;
+ }
+ val32 |= PORT_LPM;
+ ehci_writel(ehci, val32, portsc);
+ msleep(5);
+ val32 |= PORT_SUSPEND;
+ ehci_dbg(ehci, "Sending LPM 0x%08x to port %d\n", val32, port);
+ ehci_writel(ehci, val32, portsc);
+ /* wait for ACK */
+ msleep(10);
+ retval = handshake(ehci, &ehci->regs->port_status[port-1], PORT_SSTS,
+ PORTSC_SUSPEND_STS_ACK, 125);
+ dbg_port(ehci, "LPM", port, val32);
+ if (retval != -ETIMEDOUT) {
+ ehci_dbg(ehci, "LPM: device ACK for LPM\n");
+ val32 |= PORT_LPM;
+ /*
+ * now device should be in L1 sleep, let's wake up the device
+ * so that we can complete enumeration.
+ */
+ ehci_writel(ehci, val32, portsc);
+ msleep(10);
+ val32 |= PORT_RESUME;
+ ehci_writel(ehci, val32, portsc);
+ } else {
+ ehci_dbg(ehci, "LPM: device does not ACK, disable LPM %d\n",
+ retval);
+ val32 &= ~PORT_LPM;
+ retval = -ETIMEDOUT;
+ ehci_writel(ehci, val32, portsc);
+ }
+
+ return retval;
+}
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/usb/ulpi.h>
#include <plat/usb.h>
/*
/*-------------------------------------------------------------------------*/
+static void omap_ehci_soft_phy_reset(struct ehci_hcd_omap *omap, u8 port)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+ unsigned reg = 0;
+
+ reg = ULPI_FUNC_CTRL_RESET
+ /* FUNCTION_CTRL_SET register */
+ | (ULPI_SET(ULPI_FUNC_CTRL) << EHCI_INSNREG05_ULPI_REGADD_SHIFT)
+ /* Write */
+ | (2 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT)
+ /* PORTn */
+ | ((port + 1) << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT)
+ /* start ULPI access*/
+ | (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT);
+
+ ehci_omap_writel(omap->ehci_base, EHCI_INSNREG05_ULPI, reg);
+
+ /* Wait for ULPI access completion */
+ while ((ehci_omap_readl(omap->ehci_base, EHCI_INSNREG05_ULPI)
+ & (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT))) {
+ cpu_relax();
+
+ if (time_after(jiffies, timeout)) {
+ dev_dbg(omap->dev, "phy reset operation timed out\n");
+ break;
+ }
+ }
+}
+
/* omap_start_ehc
* - Start the TI USBHOST controller
*/
gpio_set_value(omap->reset_gpio_port[1], 1);
}
+ /* Soft reset the PHY using PHY reset command over ULPI */
+ if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY)
+ omap_ehci_soft_phy_reset(omap, 0);
+ if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY)
+ omap_ehci_soft_phy_reset(omap, 1);
+
return 0;
err_sys_status:
break;
case PCI_VENDOR_ID_INTEL:
ehci->need_io_watchdog = 0;
+ ehci->fs_i_thresh = 1;
if (pdev->device == 0x27cc) {
ehci->broken_periodic = 1;
ehci_info(ehci, "using broken periodic workaround\n");
* Also they depend on separate root hub suspend/resume.
*/
-static int ehci_pci_suspend(struct usb_hcd *hcd)
+static int ehci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
unsigned long flags;
* the root hub is either suspended or stopped.
*/
spin_lock_irqsave (&ehci->lock, flags);
- ehci_prepare_ports_for_controller_suspend(ehci);
+ ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup);
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
(void)ehci_readl(ehci, &ehci->regs->intr_enable);
}
#endif
+static int ehci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int rc = 0;
+
+ if (!udev->parent) /* udev is root hub itself, impossible */
+ rc = -1;
+ /* we only support lpm device connected to root hub yet */
+ if (ehci->has_lpm && !udev->parent->parent) {
+ rc = ehci_lpm_set_da(ehci, udev->devnum, udev->portnum);
+ if (!rc)
+ rc = ehci_lpm_check(ehci, udev->portnum);
+ }
+ return rc;
+}
+
static const struct hc_driver ehci_pci_hc_driver = {
.description = hcd_name,
.product_desc = "EHCI Host Controller",
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
+ /*
+ * call back when device connected and addressed
+ */
+ .update_device = ehci_update_device,
+
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
};
#endif
spin_lock_irqsave (&ehci->lock, flags);
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
- &ehci_to_hcd(ehci)->flags))) {
+ if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) {
rc = -ESHUTDOWN;
goto done;
}
spin_lock_irqsave (&ehci->lock, flags);
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
- &ehci_to_hcd(ehci)->flags))) {
+ if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) {
status = -ESHUTDOWN;
goto done_not_linked;
}
if (stream->ep)
stream->ep->hcpriv = NULL;
- if (stream->rescheduled) {
- ehci_info (ehci, "ep%d%s-iso rescheduled "
- "%lu times in %lu seconds\n",
- stream->bEndpointAddress, is_in ? "in" : "out",
- stream->rescheduled,
- ((jiffies - stream->start)/HZ)
- );
- }
-
kfree(stream);
}
}
struct ehci_iso_stream *stream
)
{
- u32 now, next, start, period;
+ u32 now, next, start, period, span;
int status;
unsigned mod = ehci->periodic_size << 3;
struct ehci_iso_sched *sched = urb->hcpriv;
- struct pci_dev *pdev;
- if (sched->span > (mod - SCHEDULE_SLOP)) {
- ehci_dbg (ehci, "iso request %p too long\n", urb);
- status = -EFBIG;
- goto fail;
+ period = urb->interval;
+ span = sched->span;
+ if (!stream->highspeed) {
+ period <<= 3;
+ span <<= 3;
}
- if ((stream->depth + sched->span) > mod) {
- ehci_dbg (ehci, "request %p would overflow (%d+%d>%d)\n",
- urb, stream->depth, sched->span, mod);
+ if (span > mod - SCHEDULE_SLOP) {
+ ehci_dbg (ehci, "iso request %p too long\n", urb);
status = -EFBIG;
goto fail;
}
- period = urb->interval;
- if (!stream->highspeed)
- period <<= 3;
-
- now = ehci_readl(ehci, &ehci->regs->frame_index) % mod;
+ now = ehci_readl(ehci, &ehci->regs->frame_index) & (mod - 1);
/* Typical case: reuse current schedule, stream is still active.
* Hopefully there are no gaps from the host falling behind
* slot in the schedule, implicitly assuming URB_ISO_ASAP.
*/
if (likely (!list_empty (&stream->td_list))) {
- pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller);
- start = stream->next_uframe;
+ u32 excess;
/* For high speed devices, allow scheduling within the
- * isochronous scheduling threshold. For full speed devices,
- * don't. (Work around for Intel ICH9 bug.)
+ * isochronous scheduling threshold. For full speed devices
+ * and Intel PCI-based controllers, don't (work around for
+ * Intel ICH9 bug).
*/
- if (!stream->highspeed &&
- pdev->vendor == PCI_VENDOR_ID_INTEL)
+ if (!stream->highspeed && ehci->fs_i_thresh)
next = now + ehci->i_thresh;
else
next = now;
- /* Fell behind (by up to twice the slop amount)? */
- if (((start - next) & (mod - 1)) >=
- mod - 2 * SCHEDULE_SLOP)
- start += period * DIV_ROUND_UP(
- (next - start) & (mod - 1),
- period);
-
- /* Tried to schedule too far into the future? */
- if (unlikely(((start - now) & (mod - 1)) + sched->span
- >= mod - 2 * SCHEDULE_SLOP)) {
+ /* Fell behind (by up to twice the slop amount)?
+ * We decide based on the time of the last currently-scheduled
+ * slot, not the time of the next available slot.
+ */
+ excess = (stream->next_uframe - period - next) & (mod - 1);
+ if (excess >= mod - 2 * SCHEDULE_SLOP)
+ start = next + excess - mod + period *
+ DIV_ROUND_UP(mod - excess, period);
+ else
+ start = next + excess + period;
+ if (start - now >= mod) {
+ ehci_dbg(ehci, "request %p would overflow (%d+%d >= %d)\n",
+ urb, start - now - period, period,
+ mod);
status = -EFBIG;
goto fail;
}
- stream->next_uframe = start;
- goto ready;
}
/* need to schedule; when's the next (u)frame we could start?
* can also help high bandwidth if the dma and irq loads don't
* jump until after the queue is primed.
*/
- start = SCHEDULE_SLOP + (now & ~0x07);
- start %= mod;
- stream->next_uframe = start;
-
- /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */
-
- /* find a uframe slot with enough bandwidth */
- for (; start < (stream->next_uframe + period); start++) {
- int enough_space;
-
- /* check schedule: enough space? */
- if (stream->highspeed)
- enough_space = itd_slot_ok (ehci, mod, start,
- stream->usecs, period);
- else {
- if ((start % 8) >= 6)
- continue;
- enough_space = sitd_slot_ok (ehci, mod, stream,
- start, sched, period);
+ else {
+ start = SCHEDULE_SLOP + (now & ~0x07);
+
+ /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */
+
+ /* find a uframe slot with enough bandwidth */
+ next = start + period;
+ for (; start < next; start++) {
+
+ /* check schedule: enough space? */
+ if (stream->highspeed) {
+ if (itd_slot_ok(ehci, mod, start,
+ stream->usecs, period))
+ break;
+ } else {
+ if ((start % 8) >= 6)
+ continue;
+ if (sitd_slot_ok(ehci, mod, stream,
+ start, sched, period))
+ break;
+ }
}
- /* schedule it here if there's enough bandwidth */
- if (enough_space) {
- stream->next_uframe = start % mod;
- goto ready;
+ /* no room in the schedule */
+ if (start == next) {
+ ehci_dbg(ehci, "iso resched full %p (now %d max %d)\n",
+ urb, now, now + mod);
+ status = -ENOSPC;
+ goto fail;
}
}
- /* no room in the schedule */
- ehci_dbg (ehci, "iso %ssched full %p (now %d max %d)\n",
- list_empty (&stream->td_list) ? "" : "re",
- urb, now, now + mod);
- status = -ENOSPC;
+ /* Tried to schedule too far into the future? */
+ if (unlikely(start - now + span - period
+ >= mod - 2 * SCHEDULE_SLOP)) {
+ ehci_dbg(ehci, "request %p would overflow (%d+%d >= %d)\n",
+ urb, start - now, span - period,
+ mod - 2 * SCHEDULE_SLOP);
+ status = -EFBIG;
+ goto fail;
+ }
-fail:
- iso_sched_free (stream, sched);
- urb->hcpriv = NULL;
- return status;
+ stream->next_uframe = start & (mod - 1);
-ready:
/* report high speed start in uframes; full speed, in frames */
urb->start_frame = stream->next_uframe;
if (!stream->highspeed)
urb->start_frame >>= 3;
return 0;
+
+ fail:
+ iso_sched_free(stream, sched);
+ urb->hcpriv = NULL;
+ return status;
}
/*-------------------------------------------------------------------------*/
struct ehci_iso_sched *iso_sched = urb->hcpriv;
struct ehci_itd *itd;
- next_uframe = stream->next_uframe % mod;
+ next_uframe = stream->next_uframe & (mod - 1);
if (unlikely (list_empty(&stream->td_list))) {
ehci_to_hcd(ehci)->self.bandwidth_allocated
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
urb->interval,
next_uframe >> 3, next_uframe & 0x7);
- stream->start = jiffies;
}
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
itd_patch(ehci, itd, iso_sched, packet, uframe);
next_uframe += stream->interval;
- stream->depth += stream->interval;
- next_uframe %= mod;
+ next_uframe &= mod - 1;
packet++;
/* link completed itds into the schedule */
if (((next_uframe >> 3) != frame)
|| packet == urb->number_of_packets) {
- itd_link (ehci, frame % ehci->periodic_size, itd);
+ itd_link(ehci, frame & (ehci->periodic_size - 1), itd);
itd = NULL;
}
}
t = hc32_to_cpup(ehci, &itd->hw_transaction [uframe]);
itd->hw_transaction [uframe] = 0;
- stream->depth -= stream->interval;
/* report transfer status */
if (unlikely (t & ISO_ERRS)) {
/* schedule ... need to lock */
spin_lock_irqsave (&ehci->lock, flags);
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
- &ehci_to_hcd(ehci)->flags))) {
+ if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) {
status = -ESHUTDOWN;
goto done_not_linked;
}
"sched devp %s ep%d%s-iso [%d] %dms/%04x\n",
urb->dev->devpath, stream->bEndpointAddress & 0x0f,
(stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
- (next_uframe >> 3) % ehci->periodic_size,
+ (next_uframe >> 3) & (ehci->periodic_size - 1),
stream->interval, hc32_to_cpu(ehci, stream->splits));
- stream->start = jiffies;
}
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
sitd->urb = urb;
sitd_patch(ehci, stream, sitd, sched, packet);
- sitd_link (ehci, (next_uframe >> 3) % ehci->periodic_size,
+ sitd_link(ehci, (next_uframe >> 3) & (ehci->periodic_size - 1),
sitd);
next_uframe += stream->interval << 3;
- stream->depth += stream->interval << 3;
}
- stream->next_uframe = next_uframe % mod;
+ stream->next_uframe = next_uframe & (mod - 1);
/* don't need that schedule data any more */
iso_sched_free (stream, sched);
desc->actual_length = desc->length - SITD_LENGTH(t);
urb->actual_length += desc->actual_length;
}
- stream->depth -= stream->interval << 3;
/* handle completion now? */
if ((urb_index + 1) != urb->number_of_packets)
/* schedule ... need to lock */
spin_lock_irqsave (&ehci->lock, flags);
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
- &ehci_to_hcd(ehci)->flags))) {
+ if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) {
status = -ESHUTDOWN;
goto done_not_linked;
}
now_uframe = ehci->next_uframe;
if (HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) {
clock = ehci_readl(ehci, &ehci->regs->frame_index);
- clock_frame = (clock >> 3) % ehci->periodic_size;
+ clock_frame = (clock >> 3) & (ehci->periodic_size - 1);
} else {
clock = now_uframe + mod - 1;
clock_frame = -1;
free_cached_lists(ehci);
ehci->clock_frame = clock_frame;
}
- clock %= mod;
+ clock &= mod - 1;
clock_frame = clock >> 3;
for (;;) {
* frame is current.
*/
if (((frame == clock_frame) ||
- (((frame + 1) % ehci->periodic_size)
+ (((frame + 1) & (ehci->periodic_size - 1))
== clock_frame))
&& live
&& (q.sitd->hw_results &
|| ehci->periodic_sched == 0)
break;
ehci->next_uframe = now_uframe;
- now = ehci_readl(ehci, &ehci->regs->frame_index) % mod;
+ now = ehci_readl(ehci, &ehci->regs->frame_index) &
+ (mod - 1);
if (now_uframe == now)
break;
}
} else {
now_uframe++;
- now_uframe %= mod;
+ now_uframe &= mod - 1;
}
}
}
unsigned has_amcc_usb23:1;
unsigned need_io_watchdog:1;
unsigned broken_periodic:1;
+ unsigned fs_i_thresh:1; /* Intel iso scheduling */
/* required for usb32 quirk */
#define OHCI_CTRL_HCFS (3 << 6)
#define OHCI_HCCTRL_LEN 0x4
__hc32 *ohci_hcctrl_reg;
unsigned has_hostpc:1;
-
+ unsigned has_lpm:1; /* support link power management */
+ unsigned has_ppcd:1; /* support per-port change bits */
u8 sbrn; /* packed release number */
/* irq statistics */
/* debug files */
#ifdef DEBUG
struct dentry *debug_dir;
- struct dentry *debug_async;
- struct dentry *debug_periodic;
- struct dentry *debug_registers;
#endif
};
u32 refcount;
u8 bEndpointAddress;
u8 highspeed;
- u16 depth; /* depth in uframes */
struct list_head td_list; /* queued itds/sitds */
struct list_head free_list; /* list of unused itds/sitds */
struct usb_device *udev;
struct usb_host_endpoint *ep;
/* output of (re)scheduling */
- unsigned long start; /* jiffies */
- unsigned long rescheduled;
int next_uframe;
__hc32 splits;
/* Prepare the PORTSC wakeup flags during controller suspend/resume */
-#define ehci_prepare_ports_for_controller_suspend(ehci) \
- ehci_adjust_port_wakeup_flags(ehci, true);
+#define ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup) \
+ ehci_adjust_port_wakeup_flags(ehci, true, do_wakeup);
-#define ehci_prepare_ports_for_controller_resume(ehci) \
- ehci_adjust_port_wakeup_flags(ehci, false);
+#define ehci_prepare_ports_for_controller_resume(ehci) \
+ ehci_adjust_port_wakeup_flags(ehci, false, false);
/*-------------------------------------------------------------------------*/
goto error_set_cluster_id;
usb_hcd->uses_new_polling = 1;
- usb_hcd->poll_rh = 1;
+ set_bit(HCD_FLAG_POLL_RH, &usb_hcd->flags);
usb_hcd->state = HC_STATE_RUNNING;
result = 0;
out:
goto error_alloc;
}
usb_hcd->wireless = 1;
- usb_hcd->flags |= HCD_FLAG_SAW_IRQ;
+ set_bit(HCD_FLAG_SAW_IRQ, &usb_hcd->flags);
wusbhc = usb_hcd_to_wusbhc(usb_hcd);
hwahc = container_of(wusbhc, struct hwahc, wusbhc);
hwahc_init(hwahc);
return -ETIMEDOUT;
}
spin_unlock_irq(&imx21->lock);
- schedule_timeout(1);
+ schedule_timeout_uninterruptible(1);
spin_lock_irq(&imx21->lock);
}
spin_unlock_irqrestore(&imx21->lock, flags);
/*
* Platform specific compile time options
*/
-#if defined(CONFIG_ARCH_KARO)
-#include <asm/arch/hardware.h>
-#include <asm/arch/pxa-regs.h>
-#include <asm/arch/karo.h>
-
-#define USE_32BIT 1
-
-
-/* These options are mutually eclusive */
-#define USE_PLATFORM_DELAY 1
-#define USE_NDELAY 0
-/*
- * MAX_ROOT_PORTS: Number of downstream ports
- *
- * The chip has two USB ports, one of which can be configured as
- * an USB device port, so the value of this constant is implementation
- * specific.
- */
-#define MAX_ROOT_PORTS 2
-#define DUMMY_DELAY_ACCESS do {} while (0)
-
-/* insert platform specific definitions for other machines here */
-#elif defined(CONFIG_BLACKFIN)
+#if defined(CONFIG_BLACKFIN)
#include <linux/io.h>
#define USE_32BIT 0
u32 chipid;
hcd->uses_new_polling = 1;
- hcd->poll_rh = 0;
hcd->state = HC_STATE_RUNNING;
isp1760_enable_interrupts(hcd);
epnum = urb->ep->desc.bEndpointAddress;
spin_lock_irqsave(&priv->lock, flags);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &priv_to_hcd(priv)->flags)) {
+ if (!HCD_HW_ACCESSIBLE(priv_to_hcd(priv))) {
rc = -ESHUTDOWN;
goto done;
}
hcd->product_desc,
hcd_name);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
size -= scnprintf (next, size,
"SUSPENDED (no register access)\n");
goto done;
next += temp;
temp = scnprintf (next, size, "hub poll timer %s\n",
- ohci_to_hcd(ohci)->poll_rh ? "ON" : "off");
+ HCD_POLL_RH(ohci_to_hcd(ohci)) ? "ON" : "off");
size -= temp;
next += temp;
spin_lock_irqsave (&ohci->lock, flags);
/* don't submit to a dead HC */
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
retval = -ENODEV;
goto fail;
}
}
/* use rhsc irqs after khubd is fully initialized */
- hcd->poll_rh = 1;
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
hcd->uses_new_polling = 1;
/* start controller operations */
else if (ints & OHCI_INTR_RD) {
ohci_vdbg(ohci, "resume detect\n");
ohci_writel(ohci, OHCI_INTR_RD, ®s->intrstatus);
- hcd->poll_rh = 1;
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
if (ohci->autostop) {
spin_lock (&ohci->lock);
ohci_rh_resume (ohci);
spin_lock_irq (&ohci->lock);
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
+ if (unlikely(!HCD_HW_ACCESSIBLE(hcd)))
rc = -ESHUTDOWN;
else
rc = ohci_rh_suspend (ohci, 0);
spin_lock_irq (&ohci->lock);
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
+ if (unlikely(!HCD_HW_ACCESSIBLE(hcd)))
rc = -ESHUTDOWN;
else
rc = ohci_rh_resume (ohci);
ohci_readl(ohci, &ohci->regs->intrenable);
msleep(20);
}
+
+ /* Does the root hub have a port wakeup pending? */
+ if (ohci_readl(ohci, &ohci->regs->intrstatus) &
+ (OHCI_INTR_RD | OHCI_INTR_RHSC))
+ usb_hcd_resume_root_hub(hcd);
}
/* Carry out polling-, autostop-, and autoresume-related state changes */
int poll_rh = 1;
int rhsc_enable;
- /* Some broken controllers never turn off RHCS in the interrupt
+ /* Some broken controllers never turn off RHSC in the interrupt
* status register. For their sake we won't re-enable RHSC
* interrupts if the interrupt bit is already active.
*/
unsigned long flags;
spin_lock_irqsave (&ohci->lock, flags);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ if (!HCD_HW_ACCESSIBLE(hcd))
goto done;
/* undocumented erratum seen on at least rev D */
}
}
- hcd->poll_rh = ohci_root_hub_state_changes(ohci, changed,
- any_connected, rhsc_status);
+ if (ohci_root_hub_state_changes(ohci, changed,
+ any_connected, rhsc_status))
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ else
+ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+
done:
spin_unlock_irqrestore (&ohci->lock, flags);
u32 temp;
int retval = 0;
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
+ if (unlikely(!HCD_HW_ACCESSIBLE(hcd)))
return -ESHUTDOWN;
switch (typeReq) {
#ifdef CONFIG_PM
-static int ohci_pci_suspend(struct usb_hcd *hcd)
+static int ohci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
unsigned long flags;
{
struct usb_hcd *hcd = ssb_get_drvdata(dev);
+ if (hcd->driver->shutdown)
+ hcd->driver->shutdown(hcd);
usb_remove_hcd(hcd);
iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
ssb_device_disable(dev, 0);
}
int err = -ENOMEM;
u32 tmp, flags = 0;
- if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV)
- flags |= SSB_OHCI_TMSLOW_HOSTMODE;
+ if (dma_set_mask(dev->dma_dev, DMA_BIT_MASK(32)) ||
+ dma_set_coherent_mask(dev->dma_dev, DMA_BIT_MASK(32)))
+ return -EOPNOTSUPP;
- ssb_device_enable(dev, flags);
+ if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV) {
+ /* Put the device into host-mode. */
+ flags |= SSB_OHCI_TMSLOW_HOSTMODE;
+ ssb_device_enable(dev, flags);
+ } else if (dev->id.coreid == SSB_DEV_USB20_HOST) {
+ /*
+ * USB 2.0 special considerations:
+ *
+ * In addition to the standard SSB reset sequence, the Host
+ * Control Register must be programmed to bring the USB core
+ * and various phy components out of reset.
+ */
+ ssb_device_enable(dev, 0);
+ ssb_write32(dev, 0x200, 0x7ff);
+
+ /* Change Flush control reg */
+ tmp = ssb_read32(dev, 0x400);
+ tmp &= ~8;
+ ssb_write32(dev, 0x400, tmp);
+ tmp = ssb_read32(dev, 0x400);
+
+ /* Change Shim control reg */
+ tmp = ssb_read32(dev, 0x304);
+ tmp &= ~0x100;
+ ssb_write32(dev, 0x304, tmp);
+ tmp = ssb_read32(dev, 0x304);
+
+ udelay(1);
+
+ /* Work around for 5354 failures */
+ if (dev->id.revision == 2 && dev->bus->chip_id == 0x5354) {
+ /* Change syn01 reg */
+ tmp = 0x00fe00fe;
+ ssb_write32(dev, 0x894, tmp);
+
+ /* Change syn03 reg */
+ tmp = ssb_read32(dev, 0x89c);
+ tmp |= 0x1;
+ ssb_write32(dev, 0x89c, tmp);
+ }
+ } else
+ ssb_device_enable(dev, 0);
hcd = usb_create_hcd(&ssb_ohci_hc_driver, dev->dev,
dev_name(dev->dev));
static const struct ssb_device_id ssb_ohci_table[] = {
SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV),
SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV),
+ SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV),
SSB_DEVTABLE_END
};
MODULE_DEVICE_TABLE(ssb, ssb_ohci_table);
#endif
spin_lock_irqsave(&oxu->lock, flags);
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
- &oxu_to_hcd(oxu)->flags))) {
+ if (unlikely(!HCD_HW_ACCESSIBLE(oxu_to_hcd(oxu)))) {
rc = -ESHUTDOWN;
goto done;
}
spin_lock_irqsave(&oxu->lock, flags);
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE,
- &oxu_to_hcd(oxu)->flags))) {
+ if (unlikely(!HCD_HW_ACCESSIBLE(oxu_to_hcd(oxu)))) {
status = -ESHUTDOWN;
goto done;
}
u32 temp, hcc_params;
hcd->uses_new_polling = 1;
- hcd->poll_rh = 0;
/* EHCI spec section 4.1 */
retval = ehci_reset(oxu);
#endif
/* avoid all allocations within spinlocks */
- if (!hep->hcpriv)
+ if (!hep->hcpriv) {
ep = kzalloc(sizeof *ep, mem_flags);
+ if (ep == NULL)
+ return -ENOMEM;
+ }
spin_lock_irqsave(&sl811->lock, flags);
#include "uhci-hcd.h"
-#define uhci_debug_operations (* (const struct file_operations *) NULL)
static struct dentry *uhci_debugfs_root;
#ifdef DEBUG
{
struct uhci_hcd *uhci = inode->i_private;
struct uhci_debug *up;
- int ret = -ENOMEM;
unsigned long flags;
- lock_kernel();
up = kmalloc(sizeof(*up), GFP_KERNEL);
if (!up)
- goto out;
+ return -ENOMEM;
up->data = kmalloc(MAX_OUTPUT, GFP_KERNEL);
if (!up->data) {
kfree(up);
- goto out;
+ return -ENOMEM;
}
up->size = 0;
file->private_data = up;
- ret = 0;
-out:
- unlock_kernel();
- return ret;
+ return 0;
}
static loff_t uhci_debug_lseek(struct file *file, loff_t off, int whence)
struct uhci_debug *up;
loff_t new = -1;
- lock_kernel();
up = file->private_data;
+ /* XXX: atomic 64bit seek access, but that needs to be fixed in the VFS */
switch (whence) {
case 0:
new = off;
new = file->f_pos + off;
break;
}
- if (new < 0 || new > up->size) {
- unlock_kernel();
+
+ if (new < 0 || new > up->size)
return -EINVAL;
- }
- unlock_kernel();
+
return (file->f_pos = new);
}
return 0;
}
-#undef uhci_debug_operations
static const struct file_operations uhci_debug_operations = {
.owner = THIS_MODULE,
.open = uhci_debug_open,
.read = uhci_debug_read,
.release = uhci_debug_release,
};
+#define UHCI_DEBUG_OPS
#endif /* CONFIG_DEBUG_FS */
uhci->rh_state = UHCI_RH_RESET;
uhci->is_stopped = UHCI_IS_STOPPED;
uhci_to_hcd(uhci)->state = HC_STATE_HALT;
- uhci_to_hcd(uhci)->poll_rh = 0;
+ clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
uhci->dead = 0; /* Full reset resurrects the controller */
}
*/
static void configure_hc(struct uhci_hcd *uhci)
{
+ struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci));
+
/* Set the frame length to the default: 1 ms exactly */
outb(USBSOF_DEFAULT, uhci->io_addr + USBSOF);
mb();
/* Enable PIRQ */
- pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
- USBLEGSUP_DEFAULT);
+ pci_write_config_word(pdev, USBLEGSUP, USBLEGSUP_DEFAULT);
+
+ /* Disable platform-specific non-PME# wakeup */
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL)
+ pci_write_config_byte(pdev, USBRES_INTEL, 0);
}
/* If interrupts don't work and remote wakeup is enabled then
* the suspended root hub needs to be polled.
*/
- uhci_to_hcd(uhci)->poll_rh = (!int_enable && wakeup_enable);
+ if (!int_enable && wakeup_enable)
+ set_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
+ else
+ clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
uhci_scan_schedule(uhci);
uhci_fsbr_off(uhci);
uhci->io_addr + USBINTR);
mb();
uhci->rh_state = UHCI_RH_RUNNING;
- uhci_to_hcd(uhci)->poll_rh = 1;
+ set_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
}
static void wakeup_rh(struct uhci_hcd *uhci)
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int retval = -EBUSY;
int i;
- struct dentry *dentry;
+ struct dentry __maybe_unused *dentry;
hcd->uses_new_polling = 1;
INIT_LIST_HEAD(&uhci->idle_qh_list);
init_waitqueue_head(&uhci->waitqh);
- if (DEBUG_CONFIGURED) {
- dentry = debugfs_create_file(hcd->self.bus_name,
- S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root,
- uhci, &uhci_debug_operations);
- if (!dentry) {
- dev_err(uhci_dev(uhci), "couldn't create uhci "
- "debugfs entry\n");
- retval = -ENOMEM;
- goto err_create_debug_entry;
- }
- uhci->dentry = dentry;
+#ifdef UHCI_DEBUG_OPS
+ dentry = debugfs_create_file(hcd->self.bus_name,
+ S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root,
+ uhci, &uhci_debug_operations);
+ if (!dentry) {
+ dev_err(uhci_dev(uhci), "couldn't create uhci debugfs entry\n");
+ return -ENOMEM;
}
+ uhci->dentry = dentry;
+#endif
uhci->frame = dma_alloc_coherent(uhci_dev(uhci),
UHCI_NUMFRAMES * sizeof(*uhci->frame),
configure_hc(uhci);
uhci->is_initialized = 1;
+ spin_lock_irq(&uhci->lock);
start_rh(uhci);
+ spin_unlock_irq(&uhci->lock);
return 0;
/*
err_alloc_frame:
debugfs_remove(uhci->dentry);
-err_create_debug_entry:
return retval;
}
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
spin_lock_irq(&uhci->lock);
- if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) && !uhci->dead)
+ if (HCD_HW_ACCESSIBLE(hcd) && !uhci->dead)
uhci_hc_died(uhci);
uhci_scan_schedule(uhci);
spin_unlock_irq(&uhci->lock);
int rc = 0;
spin_lock_irq(&uhci->lock);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ if (!HCD_HW_ACCESSIBLE(hcd))
rc = -ESHUTDOWN;
else if (uhci->dead)
; /* Dead controllers tell no tales */
int rc = 0;
spin_lock_irq(&uhci->lock);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))
+ if (!HCD_HW_ACCESSIBLE(hcd))
rc = -ESHUTDOWN;
else if (!uhci->dead)
wakeup_rh(uhci);
return rc;
}
-static int uhci_pci_suspend(struct usb_hcd *hcd)
+static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci));
int rc = 0;
dev_dbg(uhci_dev(uhci), "%s\n", __func__);
spin_lock_irq(&uhci->lock);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead)
+ if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
goto done_okay; /* Already suspended or dead */
if (uhci->rh_state > UHCI_RH_SUSPENDED) {
/* All PCI host controllers are required to disable IRQ generation
* at the source, so we must turn off PIRQ.
*/
- pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0);
- mb();
- hcd->poll_rh = 0;
-
- /* FIXME: Enable non-PME# remote wakeup? */
+ pci_write_config_word(pdev, USBLEGSUP, 0);
+ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+
+ /* Enable platform-specific non-PME# wakeup */
+ if (do_wakeup) {
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL)
+ pci_write_config_byte(pdev, USBRES_INTEL,
+ USBPORT1EN | USBPORT2EN);
+ }
done_okay:
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
* even if the controller was dead.
*/
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
- mb();
spin_lock_irq(&uhci->lock);
if (hibernated)
uhci_hc_died(uhci);
- /* FIXME: Disable non-PME# remote wakeup? */
-
/* The firmware or a boot kernel may have changed the controller
* settings during a system wakeup. Check it and reconfigure
* to avoid problems.
/* If the controller was dead before, it's back alive now */
configure_hc(uhci);
- if (uhci->rh_state == UHCI_RH_RESET) {
-
- /* The controller had to be reset */
+ /* Tell the core if the controller had to be reset */
+ if (uhci->rh_state == UHCI_RH_RESET)
usb_root_hub_lost_power(hcd->self.root_hub);
- suspend_rh(uhci, UHCI_RH_SUSPENDED);
- }
spin_unlock_irq(&uhci->lock);
/* If interrupts don't work and remote wakeup is enabled then
* the suspended root hub needs to be polled.
*/
- if (!uhci->RD_enable && hcd->self.root_hub->do_remote_wakeup) {
- hcd->poll_rh = 1;
- usb_hcd_poll_rh_status(hcd);
- }
+ if (!uhci->RD_enable && hcd->self.root_hub->do_remote_wakeup)
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+
+ /* Does the root hub have a port wakeup pending? */
+ usb_hcd_poll_rh_status(hcd);
return 0;
}
#endif
#define USBPORTSC_RES3 0x4000 /* reserved, write zeroes */
#define USBPORTSC_RES4 0x8000 /* reserved, write zeroes */
-/* Legacy support register */
+/* PCI legacy support register */
#define USBLEGSUP 0xc0
#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */
#define USBLEGSUP_RWC 0x8f00 /* the R/WC bits */
#define USBLEGSUP_RO 0x5040 /* R/O and reserved bits */
+/* PCI Intel-specific resume-enable register */
+#define USBRES_INTEL 0xc4
+#define USBPORT1EN 0x01
+#define USBPORT2EN 0x02
+
#define UHCI_PTR_BITS cpu_to_le32(0x000F)
#define UHCI_PTR_TERM cpu_to_le32(0x0001)
#define UHCI_PTR_QH cpu_to_le32(0x0002)
spin_lock_irqsave(&uhci->lock, flags);
uhci_scan_schedule(uhci);
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead)
+ if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
goto done;
uhci_check_ports(uhci);
case UHCI_RH_SUSPENDING:
case UHCI_RH_SUSPENDED:
/* if port change, ask to be resumed */
- if (status)
+ if (status || uhci->resuming_ports)
usb_hcd_resume_root_hub(hcd);
break;
u16 wPortChange, wPortStatus;
unsigned long flags;
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead)
+ if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
return -ETIMEDOUT;
spin_lock_irqsave(&uhci->lock, flags);
qh->unlink_frame = uhci->frame_number;
/* Force an interrupt so we know when the QH is fully unlinked */
- if (list_empty(&uhci->skel_unlink_qh->node))
+ if (list_empty(&uhci->skel_unlink_qh->node) || uhci->is_stopped)
uhci_set_next_interrupt(uhci);
/* Move the QH from its old list to the end of the unlinking list */
qh->advance_jiffies = jiffies;
goto done;
}
- ret = 0;
+ ret = uhci->is_stopped;
}
/* The queue hasn't advanced; check for timeout */
whc_write_wusbcmd(whc, WUSBCMD_RUN, WUSBCMD_RUN);
usb_hcd->uses_new_polling = 1;
- usb_hcd->poll_rh = 1;
+ set_bit(HCD_FLAG_POLL_RH, &usb_hcd->flags);
usb_hcd->state = HC_STATE_RUNNING;
out:
|| (prev_end & (WHCI_PAGE_SIZE-1))
|| (dma_addr & (WHCI_PAGE_SIZE-1))
|| std->len + WHCI_PAGE_SIZE > QTD_MAX_XFER_SIZE) {
- if (std->len % qset->max_packet != 0)
+ if (std && std->len % qset->max_packet != 0)
return -EINVAL;
std = qset_new_std(whc, qset, urb, mem_flags);
if (std == NULL) {
return ep->stream_info->stream_rings[stream_id];
}
-struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
- unsigned int slot_id, unsigned int ep_index,
- unsigned int stream_id)
-{
- struct xhci_virt_ep *ep;
-
- ep = &xhci->devs[slot_id]->eps[ep_index];
- /* Common case: no streams */
- if (!(ep->ep_state & EP_HAS_STREAMS))
- return ep->ring;
-
- if (stream_id == 0) {
- xhci_warn(xhci,
- "WARN: Slot ID %u, ep index %u has streams, "
- "but URB has no stream ID.\n",
- slot_id, ep_index);
- return NULL;
- }
-
- if (stream_id < ep->stream_info->num_streams)
- return ep->stream_info->stream_rings[stream_id];
-
- xhci_warn(xhci,
- "WARN: Slot ID %u, ep index %u has "
- "stream IDs 1 to %u allocated, "
- "but stream ID %u is requested.\n",
- slot_id, ep_index,
- ep->stream_info->num_streams - 1,
- stream_id);
- return NULL;
-}
-
-/* Get the right ring for the given URB.
- * If the endpoint supports streams, boundary check the URB's stream ID.
- * If the endpoint doesn't support streams, return the singular endpoint ring.
- */
-struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
- struct urb *urb)
-{
- return xhci_triad_to_transfer_ring(xhci, urb->dev->slot_id,
- xhci_get_endpoint_index(&urb->ep->desc), urb->stream_id);
-}
-
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
static int xhci_test_radix_tree(struct xhci_hcd *xhci,
unsigned int num_streams,
ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index);
/* Set up the endpoint ring */
- virt_dev->eps[ep_index].new_ring =
- xhci_ring_alloc(xhci, 1, true, mem_flags);
+ /*
+ * Isochronous endpoint ring needs bigger size because one isoc URB
+ * carries multiple packets and it will insert multiple tds to the
+ * ring.
+ * This should be replaced with dynamic ring resizing in the future.
+ */
+ if (usb_endpoint_xfer_isoc(&ep->desc))
+ virt_dev->eps[ep_index].new_ring =
+ xhci_ring_alloc(xhci, 8, true, mem_flags);
+ else
+ virt_dev->eps[ep_index].new_ring =
+ xhci_ring_alloc(xhci, 1, true, mem_flags);
if (!virt_dev->eps[ep_index].new_ring) {
/* Attempt to use the ring cache */
if (virt_dev->num_rings_cached == 0)
virt_dev->num_rings_cached--;
xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring);
}
+ virt_dev->eps[ep_index].skip = false;
ep_ring = virt_dev->eps[ep_index].new_ring;
ep_ctx->deq = ep_ring->first_seg->dma | ep_ring->cycle_state;
return command;
}
+void xhci_urb_free_priv(struct xhci_hcd *xhci, struct urb_priv *urb_priv)
+{
+ int last;
+
+ if (!urb_priv)
+ return;
+
+ last = urb_priv->length - 1;
+ if (last >= 0) {
+ int i;
+ for (i = 0; i <= last; i++)
+ kfree(urb_priv->td[i]);
+ }
+ kfree(urb_priv);
+}
+
void xhci_free_command(struct xhci_hcd *xhci,
struct xhci_command *command)
{
unsigned int num_tests;
int i, ret;
- num_tests = sizeof(simple_test_vector) / sizeof(simple_test_vector[0]);
+ num_tests = ARRAY_SIZE(simple_test_vector);
for (i = 0; i < num_tests; i++) {
ret = xhci_test_trb_in_td(xhci,
xhci->event_ring->first_seg,
return ret;
}
- num_tests = sizeof(complex_test_vector) / sizeof(complex_test_vector[0]);
+ num_tests = ARRAY_SIZE(complex_test_vector);
for (i = 0; i < num_tests; i++) {
ret = xhci_test_trb_in_td(xhci,
complex_test_vector[i].input_seg,
return 0;
}
+static void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
+{
+ u64 temp;
+ dma_addr_t deq;
+
+ deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg,
+ xhci->event_ring->dequeue);
+ if (deq == 0 && !in_interrupt())
+ xhci_warn(xhci, "WARN something wrong with SW event ring "
+ "dequeue ptr.\n");
+ /* Update HC event ring dequeue pointer */
+ temp = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
+ temp &= ERST_PTR_MASK;
+ /* Don't clear the EHB bit (which is RW1C) because
+ * there might be more events to service.
+ */
+ temp &= ~ERST_EHB;
+ xhci_dbg(xhci, "// Write event ring dequeue pointer, "
+ "preserving EHB bit\n");
+ xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp,
+ &xhci->ir_set->erst_dequeue);
+}
+
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
int retval;
+ u32 temp;
hcd->self.sg_tablesize = TRBS_PER_SEGMENT - 2;
return retval;
xhci_dbg(xhci, "Reset complete\n");
+ temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params);
+ if (HCC_64BIT_ADDR(temp)) {
+ xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n");
+ dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64));
+ } else {
+ dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32));
+ }
+
xhci_dbg(xhci, "Calling HCD init\n");
/* Initialize HCD and host controller data structures. */
retval = xhci_init(hcd);
return 1;
}
-void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
-{
- u64 temp;
- dma_addr_t deq;
-
- deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg,
- xhci->event_ring->dequeue);
- if (deq == 0 && !in_interrupt())
- xhci_warn(xhci, "WARN something wrong with SW event ring "
- "dequeue ptr.\n");
- /* Update HC event ring dequeue pointer */
- temp = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
- temp &= ERST_PTR_MASK;
- /* Don't clear the EHB bit (which is RW1C) because
- * there might be more events to service.
- */
- temp &= ~ERST_EHB;
- xhci_dbg(xhci, "// Write event ring dequeue pointer, preserving EHB bit\n");
- xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp,
- &xhci->ir_set->erst_dequeue);
-}
-
/* Ring the host controller doorbell after placing a command on the ring */
void xhci_ring_cmd_db(struct xhci_hcd *xhci)
{
field = xhci_readl(xhci, db_addr) & DB_MASK;
field |= EPI_TO_DB(ep_index) | STREAM_ID_TO_DB(stream_id);
xhci_writel(xhci, field, db_addr);
- /* Flush PCI posted writes - FIXME Matthew Wilcox says this
- * isn't time-critical and we shouldn't make the CPU wait for
- * the flush.
- */
- xhci_readl(xhci, db_addr);
}
}
return cur_seg;
}
+
+static struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
+ unsigned int slot_id, unsigned int ep_index,
+ unsigned int stream_id)
+{
+ struct xhci_virt_ep *ep;
+
+ ep = &xhci->devs[slot_id]->eps[ep_index];
+ /* Common case: no streams */
+ if (!(ep->ep_state & EP_HAS_STREAMS))
+ return ep->ring;
+
+ if (stream_id == 0) {
+ xhci_warn(xhci,
+ "WARN: Slot ID %u, ep index %u has streams, "
+ "but URB has no stream ID.\n",
+ slot_id, ep_index);
+ return NULL;
+ }
+
+ if (stream_id < ep->stream_info->num_streams)
+ return ep->stream_info->stream_rings[stream_id];
+
+ xhci_warn(xhci,
+ "WARN: Slot ID %u, ep index %u has "
+ "stream IDs 1 to %u allocated, "
+ "but stream ID %u is requested.\n",
+ slot_id, ep_index,
+ ep->stream_info->num_streams - 1,
+ stream_id);
+ return NULL;
+}
+
+/* Get the right ring for the given URB.
+ * If the endpoint supports streams, boundary check the URB's stream ID.
+ * If the endpoint doesn't support streams, return the singular endpoint ring.
+ */
+static struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
+ struct urb *urb)
+{
+ return xhci_triad_to_transfer_ring(xhci, urb->dev->slot_id,
+ xhci_get_endpoint_index(&urb->ep->desc), urb->stream_id);
+}
+
/*
* Move the xHC's endpoint ring dequeue pointer past cur_td.
* Record the new state of the xHC's endpoint ring dequeue segment,
struct xhci_td *cur_td, int status, char *adjective)
{
struct usb_hcd *hcd = xhci_to_hcd(xhci);
+ struct urb *urb;
+ struct urb_priv *urb_priv;
- cur_td->urb->hcpriv = NULL;
- usb_hcd_unlink_urb_from_ep(hcd, cur_td->urb);
- xhci_dbg(xhci, "Giveback %s URB %p\n", adjective, cur_td->urb);
+ urb = cur_td->urb;
+ urb_priv = urb->hcpriv;
+ urb_priv->td_cnt++;
- spin_unlock(&xhci->lock);
- usb_hcd_giveback_urb(hcd, cur_td->urb, status);
- kfree(cur_td);
- spin_lock(&xhci->lock);
- xhci_dbg(xhci, "%s URB given back\n", adjective);
+ /* Only giveback urb when this is the last td in urb */
+ if (urb_priv->td_cnt == urb_priv->length) {
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ xhci_dbg(xhci, "Giveback %s URB %p\n", adjective, urb);
+
+ spin_unlock(&xhci->lock);
+ usb_hcd_giveback_urb(hcd, urb, status);
+ xhci_urb_free_priv(xhci, urb_priv);
+ spin_lock(&xhci->lock);
+ xhci_dbg(xhci, "%s URB given back\n", adjective);
+ }
}
/*
/* Update event ring dequeue pointer before dropping the lock */
inc_deq(xhci, xhci->event_ring, true);
- xhci_set_hc_event_deq(xhci);
spin_unlock(&xhci->lock);
/* Pass this up to the core */
}
/*
- * If this function returns an error condition, it means it got a Transfer
- * event with a corrupted Slot ID, Endpoint ID, or TRB DMA address.
- * At this point, the host controller is probably hosed and should be reset.
+ * Finish the td processing, remove the td from td list;
+ * Return 1 if the urb can be given back.
*/
-static int handle_tx_event(struct xhci_hcd *xhci,
- struct xhci_transfer_event *event)
+static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
+ union xhci_trb *event_trb, struct xhci_transfer_event *event,
+ struct xhci_virt_ep *ep, int *status, bool skip)
{
struct xhci_virt_device *xdev;
- struct xhci_virt_ep *ep;
struct xhci_ring *ep_ring;
unsigned int slot_id;
int ep_index;
- struct xhci_td *td = NULL;
- dma_addr_t event_dma;
- struct xhci_segment *event_seg;
- union xhci_trb *event_trb;
struct urb *urb = NULL;
- int status = -EINPROGRESS;
struct xhci_ep_ctx *ep_ctx;
+ int ret = 0;
+ struct urb_priv *urb_priv;
u32 trb_comp_code;
- xhci_dbg(xhci, "In %s\n", __func__);
slot_id = TRB_TO_SLOT_ID(event->flags);
xdev = xhci->devs[slot_id];
- if (!xdev) {
- xhci_err(xhci, "ERROR Transfer event pointed to bad slot\n");
- return -ENODEV;
- }
-
- /* Endpoint ID is 1 based, our index is zero based */
ep_index = TRB_TO_EP_ID(event->flags) - 1;
- xhci_dbg(xhci, "%s - ep index = %d\n", __func__, ep_index);
- ep = &xdev->eps[ep_index];
ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
- if (!ep_ring || (ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_DISABLED) {
- xhci_err(xhci, "ERROR Transfer event for disabled endpoint "
- "or incorrect stream ring\n");
- return -ENODEV;
- }
-
- event_dma = event->buffer;
- /* This TRB should be in the TD at the head of this ring's TD list */
- xhci_dbg(xhci, "%s - checking for list empty\n", __func__);
- if (list_empty(&ep_ring->td_list)) {
- xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n",
- TRB_TO_SLOT_ID(event->flags), ep_index);
- xhci_dbg(xhci, "Event TRB with TRB type ID %u\n",
- (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10);
- xhci_print_trb_offsets(xhci, (union xhci_trb *) event);
- urb = NULL;
- goto cleanup;
- }
- xhci_dbg(xhci, "%s - getting list entry\n", __func__);
- td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list);
-
- /* Is this a TRB in the currently executing TD? */
- xhci_dbg(xhci, "%s - looking for TD\n", __func__);
- event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue,
- td->last_trb, event_dma);
- xhci_dbg(xhci, "%s - found event_seg = %p\n", __func__, event_seg);
- if (!event_seg) {
- /* HC is busted, give up! */
- xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not part of current TD\n");
- return -ESHUTDOWN;
- }
- event_trb = &event_seg->trbs[(event_dma - event_seg->dma) / sizeof(*event_trb)];
- xhci_dbg(xhci, "Event TRB with TRB type ID %u\n",
- (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10);
- xhci_dbg(xhci, "Offset 0x00 (buffer lo) = 0x%x\n",
- lower_32_bits(event->buffer));
- xhci_dbg(xhci, "Offset 0x04 (buffer hi) = 0x%x\n",
- upper_32_bits(event->buffer));
- xhci_dbg(xhci, "Offset 0x08 (transfer length) = 0x%x\n",
- (unsigned int) event->transfer_len);
- xhci_dbg(xhci, "Offset 0x0C (flags) = 0x%x\n",
- (unsigned int) event->flags);
-
- /* Look for common error cases */
trb_comp_code = GET_COMP_CODE(event->transfer_len);
- switch (trb_comp_code) {
- /* Skip codes that require special handling depending on
- * transfer type
- */
- case COMP_SUCCESS:
- case COMP_SHORT_TX:
- break;
- case COMP_STOP:
- xhci_dbg(xhci, "Stopped on Transfer TRB\n");
- break;
- case COMP_STOP_INVAL:
- xhci_dbg(xhci, "Stopped on No-op or Link TRB\n");
- break;
- case COMP_STALL:
- xhci_warn(xhci, "WARN: Stalled endpoint\n");
- ep->ep_state |= EP_HALTED;
- status = -EPIPE;
- break;
- case COMP_TRB_ERR:
- xhci_warn(xhci, "WARN: TRB error on endpoint\n");
- status = -EILSEQ;
- break;
- case COMP_SPLIT_ERR:
- case COMP_TX_ERR:
- xhci_warn(xhci, "WARN: transfer error on endpoint\n");
- status = -EPROTO;
- break;
- case COMP_BABBLE:
- xhci_warn(xhci, "WARN: babble error on endpoint\n");
- status = -EOVERFLOW;
- break;
- case COMP_DB_ERR:
- xhci_warn(xhci, "WARN: HC couldn't access mem fast enough\n");
- status = -ENOSR;
- break;
- default:
- if (xhci_is_vendor_info_code(xhci, trb_comp_code)) {
- status = 0;
- break;
- }
- xhci_warn(xhci, "ERROR Unknown event condition, HC probably busted\n");
- urb = NULL;
- goto cleanup;
- }
- /* Now update the urb's actual_length and give back to the core */
- /* Was this a control transfer? */
- if (usb_endpoint_xfer_control(&td->urb->ep->desc)) {
- xhci_debug_trb(xhci, xhci->event_ring->dequeue);
- switch (trb_comp_code) {
- case COMP_SUCCESS:
- if (event_trb == ep_ring->dequeue) {
- xhci_warn(xhci, "WARN: Success on ctrl setup TRB without IOC set??\n");
- status = -ESHUTDOWN;
- } else if (event_trb != td->last_trb) {
- xhci_warn(xhci, "WARN: Success on ctrl data TRB without IOC set??\n");
- status = -ESHUTDOWN;
- } else {
- xhci_dbg(xhci, "Successful control transfer!\n");
- status = 0;
- }
- break;
- case COMP_SHORT_TX:
- xhci_warn(xhci, "WARN: short transfer on control ep\n");
- if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
- status = -EREMOTEIO;
- else
- status = 0;
- break;
-
- default:
- if (!xhci_requires_manual_halt_cleanup(xhci,
- ep_ctx, trb_comp_code))
- break;
- xhci_dbg(xhci, "TRB error code %u, "
- "halted endpoint index = %u\n",
- trb_comp_code, ep_index);
- /* else fall through */
- case COMP_STALL:
- /* Did we transfer part of the data (middle) phase? */
- if (event_trb != ep_ring->dequeue &&
- event_trb != td->last_trb)
- td->urb->actual_length =
- td->urb->transfer_buffer_length
- - TRB_LEN(event->transfer_len);
- else
- td->urb->actual_length = 0;
- xhci_cleanup_halted_endpoint(xhci,
- slot_id, ep_index, 0, td, event_trb);
- goto td_cleanup;
- }
- /*
- * Did we transfer any data, despite the errors that might have
- * happened? I.e. did we get past the setup stage?
- */
- if (event_trb != ep_ring->dequeue) {
- /* The event was for the status stage */
- if (event_trb == td->last_trb) {
- if (td->urb->actual_length != 0) {
- /* Don't overwrite a previously set error code */
- if ((status == -EINPROGRESS ||
- status == 0) &&
- (td->urb->transfer_flags
- & URB_SHORT_NOT_OK))
- /* Did we already see a short data stage? */
- status = -EREMOTEIO;
- } else {
- td->urb->actual_length =
- td->urb->transfer_buffer_length;
- }
- } else {
- /* Maybe the event was for the data stage? */
- if (trb_comp_code != COMP_STOP_INVAL) {
- /* We didn't stop on a link TRB in the middle */
- td->urb->actual_length =
- td->urb->transfer_buffer_length -
- TRB_LEN(event->transfer_len);
- xhci_dbg(xhci, "Waiting for status stage event\n");
- urb = NULL;
- goto cleanup;
- }
- }
- }
- } else {
- switch (trb_comp_code) {
- case COMP_SUCCESS:
- /* Double check that the HW transferred everything. */
- if (event_trb != td->last_trb) {
- xhci_warn(xhci, "WARN Successful completion "
- "on short TX\n");
- if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
- status = -EREMOTEIO;
- else
- status = 0;
- } else {
- if (usb_endpoint_xfer_bulk(&td->urb->ep->desc))
- xhci_dbg(xhci, "Successful bulk "
- "transfer!\n");
- else
- xhci_dbg(xhci, "Successful interrupt "
- "transfer!\n");
- status = 0;
- }
- break;
- case COMP_SHORT_TX:
- if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
- status = -EREMOTEIO;
- else
- status = 0;
- break;
- default:
- /* Others already handled above */
- break;
- }
- dev_dbg(&td->urb->dev->dev,
- "ep %#x - asked for %d bytes, "
- "%d bytes untransferred\n",
- td->urb->ep->desc.bEndpointAddress,
- td->urb->transfer_buffer_length,
- TRB_LEN(event->transfer_len));
- /* Fast path - was this the last TRB in the TD for this URB? */
- if (event_trb == td->last_trb) {
- if (TRB_LEN(event->transfer_len) != 0) {
- td->urb->actual_length =
- td->urb->transfer_buffer_length -
- TRB_LEN(event->transfer_len);
- if (td->urb->transfer_buffer_length <
- td->urb->actual_length) {
- xhci_warn(xhci, "HC gave bad length "
- "of %d bytes left\n",
- TRB_LEN(event->transfer_len));
- td->urb->actual_length = 0;
- if (td->urb->transfer_flags &
- URB_SHORT_NOT_OK)
- status = -EREMOTEIO;
- else
- status = 0;
- }
- /* Don't overwrite a previously set error code */
- if (status == -EINPROGRESS) {
- if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
- status = -EREMOTEIO;
- else
- status = 0;
- }
- } else {
- td->urb->actual_length = td->urb->transfer_buffer_length;
- /* Ignore a short packet completion if the
- * untransferred length was zero.
- */
- if (status == -EREMOTEIO)
- status = 0;
- }
- } else {
- /* Slow path - walk the list, starting from the dequeue
- * pointer, to get the actual length transferred.
- */
- union xhci_trb *cur_trb;
- struct xhci_segment *cur_seg;
+ if (skip)
+ goto td_cleanup;
- td->urb->actual_length = 0;
- for (cur_trb = ep_ring->dequeue, cur_seg = ep_ring->deq_seg;
- cur_trb != event_trb;
- next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) {
- if ((cur_trb->generic.field[3] &
- TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) &&
- (cur_trb->generic.field[3] &
- TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK))
- td->urb->actual_length +=
- TRB_LEN(cur_trb->generic.field[2]);
- }
- /* If the ring didn't stop on a Link or No-op TRB, add
- * in the actual bytes transferred from the Normal TRB
- */
- if (trb_comp_code != COMP_STOP_INVAL)
- td->urb->actual_length +=
- TRB_LEN(cur_trb->generic.field[2]) -
- TRB_LEN(event->transfer_len);
- }
- }
if (trb_comp_code == COMP_STOP_INVAL ||
trb_comp_code == COMP_STOP) {
/* The Endpoint Stop Command completion will take care of any
*/
ep->stopped_td = td;
ep->stopped_trb = event_trb;
+ return 0;
} else {
if (trb_comp_code == COMP_STALL) {
/* The transfer is completed from the driver's
* xHCI hardware manually.
*/
xhci_cleanup_halted_endpoint(xhci,
- slot_id, ep_index, ep_ring->stream_id, td, event_trb);
+ slot_id, ep_index, ep_ring->stream_id,
+ td, event_trb);
} else {
/* Update ring dequeue pointer */
while (ep_ring->dequeue != td->last_trb)
td_cleanup:
/* Clean up the endpoint's TD list */
urb = td->urb;
+ urb_priv = urb->hcpriv;
+
/* Do one last check of the actual transfer length.
* If the host controller said we transferred more data than
* the buffer length, urb->actual_length will be a very big
urb->actual_length);
urb->actual_length = 0;
if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
- status = -EREMOTEIO;
+ *status = -EREMOTEIO;
else
- status = 0;
+ *status = 0;
}
list_del(&td->td_list);
/* Was this TD slated to be cancelled but completed anyway? */
if (!list_empty(&td->cancelled_td_list))
list_del(&td->cancelled_td_list);
- /* Leave the TD around for the reset endpoint function to use
- * (but only if it's not a control endpoint, since we already
- * queued the Set TR dequeue pointer command for stalled
- * control endpoints).
- */
- if (usb_endpoint_xfer_control(&urb->ep->desc) ||
- (trb_comp_code != COMP_STALL &&
- trb_comp_code != COMP_BABBLE)) {
- kfree(td);
- }
- urb->hcpriv = NULL;
+ urb_priv->td_cnt++;
+ /* Giveback the urb when all the tds are completed */
+ if (urb_priv->td_cnt == urb_priv->length)
+ ret = 1;
}
-cleanup:
- inc_deq(xhci, xhci->event_ring, true);
- xhci_set_hc_event_deq(xhci);
- /* FIXME for multi-TD URBs (who have buffers bigger than 64MB) */
- if (urb) {
- usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb);
- xhci_dbg(xhci, "Giveback URB %p, len = %d, status = %d\n",
- urb, urb->actual_length, status);
- spin_unlock(&xhci->lock);
- usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, status);
- spin_lock(&xhci->lock);
- }
- return 0;
+ return ret;
}
/*
- * This function handles all OS-owned events on the event ring. It may drop
- * xhci->lock between event processing (e.g. to pass up port status changes).
+ * Process control tds, update urb status and actual_length.
*/
-void xhci_handle_event(struct xhci_hcd *xhci)
+static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
+ union xhci_trb *event_trb, struct xhci_transfer_event *event,
+ struct xhci_virt_ep *ep, int *status)
{
- union xhci_trb *event;
- int update_ptrs = 1;
- int ret;
-
- xhci_dbg(xhci, "In %s\n", __func__);
- if (!xhci->event_ring || !xhci->event_ring->dequeue) {
- xhci->error_bitmask |= 1 << 1;
- return;
- }
+ struct xhci_virt_device *xdev;
+ struct xhci_ring *ep_ring;
+ unsigned int slot_id;
+ int ep_index;
+ struct xhci_ep_ctx *ep_ctx;
+ u32 trb_comp_code;
- event = xhci->event_ring->dequeue;
- /* Does the HC or OS own the TRB? */
- if ((event->event_cmd.flags & TRB_CYCLE) !=
- xhci->event_ring->cycle_state) {
- xhci->error_bitmask |= 1 << 2;
- return;
- }
- xhci_dbg(xhci, "%s - OS owns TRB\n", __func__);
+ slot_id = TRB_TO_SLOT_ID(event->flags);
+ xdev = xhci->devs[slot_id];
+ ep_index = TRB_TO_EP_ID(event->flags) - 1;
+ ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
+ ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
+ trb_comp_code = GET_COMP_CODE(event->transfer_len);
- /* FIXME: Handle more event types. */
- switch ((event->event_cmd.flags & TRB_TYPE_BITMASK)) {
- case TRB_TYPE(TRB_COMPLETION):
- xhci_dbg(xhci, "%s - calling handle_cmd_completion\n", __func__);
- handle_cmd_completion(xhci, &event->event_cmd);
- xhci_dbg(xhci, "%s - returned from handle_cmd_completion\n", __func__);
- break;
- case TRB_TYPE(TRB_PORT_STATUS):
- xhci_dbg(xhci, "%s - calling handle_port_status\n", __func__);
- handle_port_status(xhci, event);
- xhci_dbg(xhci, "%s - returned from handle_port_status\n", __func__);
- update_ptrs = 0;
+ xhci_debug_trb(xhci, xhci->event_ring->dequeue);
+ switch (trb_comp_code) {
+ case COMP_SUCCESS:
+ if (event_trb == ep_ring->dequeue) {
+ xhci_warn(xhci, "WARN: Success on ctrl setup TRB "
+ "without IOC set??\n");
+ *status = -ESHUTDOWN;
+ } else if (event_trb != td->last_trb) {
+ xhci_warn(xhci, "WARN: Success on ctrl data TRB "
+ "without IOC set??\n");
+ *status = -ESHUTDOWN;
+ } else {
+ xhci_dbg(xhci, "Successful control transfer!\n");
+ *status = 0;
+ }
break;
- case TRB_TYPE(TRB_TRANSFER):
- xhci_dbg(xhci, "%s - calling handle_tx_event\n", __func__);
- ret = handle_tx_event(xhci, &event->trans_event);
- xhci_dbg(xhci, "%s - returned from handle_tx_event\n", __func__);
- if (ret < 0)
- xhci->error_bitmask |= 1 << 9;
+ case COMP_SHORT_TX:
+ xhci_warn(xhci, "WARN: short transfer on control ep\n");
+ if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+ *status = -EREMOTEIO;
else
- update_ptrs = 0;
+ *status = 0;
break;
default:
- if ((event->event_cmd.flags & TRB_TYPE_BITMASK) >= TRB_TYPE(48))
- handle_vendor_event(xhci, event);
+ if (!xhci_requires_manual_halt_cleanup(xhci,
+ ep_ctx, trb_comp_code))
+ break;
+ xhci_dbg(xhci, "TRB error code %u, "
+ "halted endpoint index = %u\n",
+ trb_comp_code, ep_index);
+ /* else fall through */
+ case COMP_STALL:
+ /* Did we transfer part of the data (middle) phase? */
+ if (event_trb != ep_ring->dequeue &&
+ event_trb != td->last_trb)
+ td->urb->actual_length =
+ td->urb->transfer_buffer_length
+ - TRB_LEN(event->transfer_len);
else
- xhci->error_bitmask |= 1 << 3;
+ td->urb->actual_length = 0;
+
+ xhci_cleanup_halted_endpoint(xhci,
+ slot_id, ep_index, 0, td, event_trb);
+ return finish_td(xhci, td, event_trb, event, ep, status, true);
}
- /* Any of the above functions may drop and re-acquire the lock, so check
- * to make sure a watchdog timer didn't mark the host as non-responsive.
+ /*
+ * Did we transfer any data, despite the errors that might have
+ * happened? I.e. did we get past the setup stage?
*/
- if (xhci->xhc_state & XHCI_STATE_DYING) {
- xhci_dbg(xhci, "xHCI host dying, returning from "
- "event handler.\n");
- return;
+ if (event_trb != ep_ring->dequeue) {
+ /* The event was for the status stage */
+ if (event_trb == td->last_trb) {
+ if (td->urb->actual_length != 0) {
+ /* Don't overwrite a previously set error code
+ */
+ if ((*status == -EINPROGRESS || *status == 0) &&
+ (td->urb->transfer_flags
+ & URB_SHORT_NOT_OK))
+ /* Did we already see a short data
+ * stage? */
+ *status = -EREMOTEIO;
+ } else {
+ td->urb->actual_length =
+ td->urb->transfer_buffer_length;
+ }
+ } else {
+ /* Maybe the event was for the data stage? */
+ if (trb_comp_code != COMP_STOP_INVAL) {
+ /* We didn't stop on a link TRB in the middle */
+ td->urb->actual_length =
+ td->urb->transfer_buffer_length -
+ TRB_LEN(event->transfer_len);
+ xhci_dbg(xhci, "Waiting for status "
+ "stage event\n");
+ return 0;
+ }
+ }
}
- if (update_ptrs) {
- /* Update SW and HC event ring dequeue pointer */
- inc_deq(xhci, xhci->event_ring, true);
- xhci_set_hc_event_deq(xhci);
+ return finish_td(xhci, td, event_trb, event, ep, status, false);
+}
+
+/*
+ * Process isochronous tds, update urb packet status and actual_length.
+ */
+static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
+ union xhci_trb *event_trb, struct xhci_transfer_event *event,
+ struct xhci_virt_ep *ep, int *status)
+{
+ struct xhci_ring *ep_ring;
+ struct urb_priv *urb_priv;
+ int idx;
+ int len = 0;
+ int skip_td = 0;
+ union xhci_trb *cur_trb;
+ struct xhci_segment *cur_seg;
+ u32 trb_comp_code;
+
+ ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
+ trb_comp_code = GET_COMP_CODE(event->transfer_len);
+ urb_priv = td->urb->hcpriv;
+ idx = urb_priv->td_cnt;
+
+ if (ep->skip) {
+ /* The transfer is partly done */
+ *status = -EXDEV;
+ td->urb->iso_frame_desc[idx].status = -EXDEV;
+ } else {
+ /* handle completion code */
+ switch (trb_comp_code) {
+ case COMP_SUCCESS:
+ td->urb->iso_frame_desc[idx].status = 0;
+ xhci_dbg(xhci, "Successful isoc transfer!\n");
+ break;
+ case COMP_SHORT_TX:
+ if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+ td->urb->iso_frame_desc[idx].status =
+ -EREMOTEIO;
+ else
+ td->urb->iso_frame_desc[idx].status = 0;
+ break;
+ case COMP_BW_OVER:
+ td->urb->iso_frame_desc[idx].status = -ECOMM;
+ skip_td = 1;
+ break;
+ case COMP_BUFF_OVER:
+ case COMP_BABBLE:
+ td->urb->iso_frame_desc[idx].status = -EOVERFLOW;
+ skip_td = 1;
+ break;
+ case COMP_STALL:
+ td->urb->iso_frame_desc[idx].status = -EPROTO;
+ skip_td = 1;
+ break;
+ case COMP_STOP:
+ case COMP_STOP_INVAL:
+ break;
+ default:
+ td->urb->iso_frame_desc[idx].status = -1;
+ break;
+ }
+ }
+
+ /* calc actual length */
+ if (ep->skip) {
+ td->urb->iso_frame_desc[idx].actual_length = 0;
+ return finish_td(xhci, td, event_trb, event, ep, status, true);
+ }
+
+ if (trb_comp_code == COMP_SUCCESS || skip_td == 1) {
+ td->urb->iso_frame_desc[idx].actual_length =
+ td->urb->iso_frame_desc[idx].length;
+ td->urb->actual_length +=
+ td->urb->iso_frame_desc[idx].length;
+ } else {
+ for (cur_trb = ep_ring->dequeue,
+ cur_seg = ep_ring->deq_seg; cur_trb != event_trb;
+ next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) {
+ if ((cur_trb->generic.field[3] &
+ TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) &&
+ (cur_trb->generic.field[3] &
+ TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK))
+ len +=
+ TRB_LEN(cur_trb->generic.field[2]);
+ }
+ len += TRB_LEN(cur_trb->generic.field[2]) -
+ TRB_LEN(event->transfer_len);
+
+ if (trb_comp_code != COMP_STOP_INVAL) {
+ td->urb->iso_frame_desc[idx].actual_length = len;
+ td->urb->actual_length += len;
+ }
+ }
+
+ if ((idx == urb_priv->length - 1) && *status == -EINPROGRESS)
+ *status = 0;
+
+ return finish_td(xhci, td, event_trb, event, ep, status, false);
+}
+
+/*
+ * Process bulk and interrupt tds, update urb status and actual_length.
+ */
+static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
+ union xhci_trb *event_trb, struct xhci_transfer_event *event,
+ struct xhci_virt_ep *ep, int *status)
+{
+ struct xhci_ring *ep_ring;
+ union xhci_trb *cur_trb;
+ struct xhci_segment *cur_seg;
+ u32 trb_comp_code;
+
+ ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
+ trb_comp_code = GET_COMP_CODE(event->transfer_len);
+
+ switch (trb_comp_code) {
+ case COMP_SUCCESS:
+ /* Double check that the HW transferred everything. */
+ if (event_trb != td->last_trb) {
+ xhci_warn(xhci, "WARN Successful completion "
+ "on short TX\n");
+ if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+ *status = -EREMOTEIO;
+ else
+ *status = 0;
+ } else {
+ if (usb_endpoint_xfer_bulk(&td->urb->ep->desc))
+ xhci_dbg(xhci, "Successful bulk "
+ "transfer!\n");
+ else
+ xhci_dbg(xhci, "Successful interrupt "
+ "transfer!\n");
+ *status = 0;
+ }
+ break;
+ case COMP_SHORT_TX:
+ if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+ *status = -EREMOTEIO;
+ else
+ *status = 0;
+ break;
+ default:
+ /* Others already handled above */
+ break;
+ }
+ dev_dbg(&td->urb->dev->dev,
+ "ep %#x - asked for %d bytes, "
+ "%d bytes untransferred\n",
+ td->urb->ep->desc.bEndpointAddress,
+ td->urb->transfer_buffer_length,
+ TRB_LEN(event->transfer_len));
+ /* Fast path - was this the last TRB in the TD for this URB? */
+ if (event_trb == td->last_trb) {
+ if (TRB_LEN(event->transfer_len) != 0) {
+ td->urb->actual_length =
+ td->urb->transfer_buffer_length -
+ TRB_LEN(event->transfer_len);
+ if (td->urb->transfer_buffer_length <
+ td->urb->actual_length) {
+ xhci_warn(xhci, "HC gave bad length "
+ "of %d bytes left\n",
+ TRB_LEN(event->transfer_len));
+ td->urb->actual_length = 0;
+ if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+ *status = -EREMOTEIO;
+ else
+ *status = 0;
+ }
+ /* Don't overwrite a previously set error code */
+ if (*status == -EINPROGRESS) {
+ if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
+ *status = -EREMOTEIO;
+ else
+ *status = 0;
+ }
+ } else {
+ td->urb->actual_length =
+ td->urb->transfer_buffer_length;
+ /* Ignore a short packet completion if the
+ * untransferred length was zero.
+ */
+ if (*status == -EREMOTEIO)
+ *status = 0;
+ }
+ } else {
+ /* Slow path - walk the list, starting from the dequeue
+ * pointer, to get the actual length transferred.
+ */
+ td->urb->actual_length = 0;
+ for (cur_trb = ep_ring->dequeue, cur_seg = ep_ring->deq_seg;
+ cur_trb != event_trb;
+ next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) {
+ if ((cur_trb->generic.field[3] &
+ TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) &&
+ (cur_trb->generic.field[3] &
+ TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK))
+ td->urb->actual_length +=
+ TRB_LEN(cur_trb->generic.field[2]);
+ }
+ /* If the ring didn't stop on a Link or No-op TRB, add
+ * in the actual bytes transferred from the Normal TRB
+ */
+ if (trb_comp_code != COMP_STOP_INVAL)
+ td->urb->actual_length +=
+ TRB_LEN(cur_trb->generic.field[2]) -
+ TRB_LEN(event->transfer_len);
+ }
+
+ return finish_td(xhci, td, event_trb, event, ep, status, false);
+}
+
+/*
+ * If this function returns an error condition, it means it got a Transfer
+ * event with a corrupted Slot ID, Endpoint ID, or TRB DMA address.
+ * At this point, the host controller is probably hosed and should be reset.
+ */
+static int handle_tx_event(struct xhci_hcd *xhci,
+ struct xhci_transfer_event *event)
+{
+ struct xhci_virt_device *xdev;
+ struct xhci_virt_ep *ep;
+ struct xhci_ring *ep_ring;
+ unsigned int slot_id;
+ int ep_index;
+ struct xhci_td *td = NULL;
+ dma_addr_t event_dma;
+ struct xhci_segment *event_seg;
+ union xhci_trb *event_trb;
+ struct urb *urb = NULL;
+ int status = -EINPROGRESS;
+ struct urb_priv *urb_priv;
+ struct xhci_ep_ctx *ep_ctx;
+ u32 trb_comp_code;
+ int ret = 0;
+
+ slot_id = TRB_TO_SLOT_ID(event->flags);
+ xdev = xhci->devs[slot_id];
+ if (!xdev) {
+ xhci_err(xhci, "ERROR Transfer event pointed to bad slot\n");
+ return -ENODEV;
+ }
+
+ /* Endpoint ID is 1 based, our index is zero based */
+ ep_index = TRB_TO_EP_ID(event->flags) - 1;
+ xhci_dbg(xhci, "%s - ep index = %d\n", __func__, ep_index);
+ ep = &xdev->eps[ep_index];
+ ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
+ ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
+ if (!ep_ring ||
+ (ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_DISABLED) {
+ xhci_err(xhci, "ERROR Transfer event for disabled endpoint "
+ "or incorrect stream ring\n");
+ return -ENODEV;
}
+
+ event_dma = event->buffer;
+ trb_comp_code = GET_COMP_CODE(event->transfer_len);
+ /* Look for common error cases */
+ switch (trb_comp_code) {
+ /* Skip codes that require special handling depending on
+ * transfer type
+ */
+ case COMP_SUCCESS:
+ case COMP_SHORT_TX:
+ break;
+ case COMP_STOP:
+ xhci_dbg(xhci, "Stopped on Transfer TRB\n");
+ break;
+ case COMP_STOP_INVAL:
+ xhci_dbg(xhci, "Stopped on No-op or Link TRB\n");
+ break;
+ case COMP_STALL:
+ xhci_warn(xhci, "WARN: Stalled endpoint\n");
+ ep->ep_state |= EP_HALTED;
+ status = -EPIPE;
+ break;
+ case COMP_TRB_ERR:
+ xhci_warn(xhci, "WARN: TRB error on endpoint\n");
+ status = -EILSEQ;
+ break;
+ case COMP_SPLIT_ERR:
+ case COMP_TX_ERR:
+ xhci_warn(xhci, "WARN: transfer error on endpoint\n");
+ status = -EPROTO;
+ break;
+ case COMP_BABBLE:
+ xhci_warn(xhci, "WARN: babble error on endpoint\n");
+ status = -EOVERFLOW;
+ break;
+ case COMP_DB_ERR:
+ xhci_warn(xhci, "WARN: HC couldn't access mem fast enough\n");
+ status = -ENOSR;
+ break;
+ case COMP_BW_OVER:
+ xhci_warn(xhci, "WARN: bandwidth overrun event on endpoint\n");
+ break;
+ case COMP_BUFF_OVER:
+ xhci_warn(xhci, "WARN: buffer overrun event on endpoint\n");
+ break;
+ case COMP_UNDERRUN:
+ /*
+ * When the Isoch ring is empty, the xHC will generate
+ * a Ring Overrun Event for IN Isoch endpoint or Ring
+ * Underrun Event for OUT Isoch endpoint.
+ */
+ xhci_dbg(xhci, "underrun event on endpoint\n");
+ if (!list_empty(&ep_ring->td_list))
+ xhci_dbg(xhci, "Underrun Event for slot %d ep %d "
+ "still with TDs queued?\n",
+ TRB_TO_SLOT_ID(event->flags), ep_index);
+ goto cleanup;
+ case COMP_OVERRUN:
+ xhci_dbg(xhci, "overrun event on endpoint\n");
+ if (!list_empty(&ep_ring->td_list))
+ xhci_dbg(xhci, "Overrun Event for slot %d ep %d "
+ "still with TDs queued?\n",
+ TRB_TO_SLOT_ID(event->flags), ep_index);
+ goto cleanup;
+ case COMP_MISSED_INT:
+ /*
+ * When encounter missed service error, one or more isoc tds
+ * may be missed by xHC.
+ * Set skip flag of the ep_ring; Complete the missed tds as
+ * short transfer when process the ep_ring next time.
+ */
+ ep->skip = true;
+ xhci_dbg(xhci, "Miss service interval error, set skip flag\n");
+ goto cleanup;
+ default:
+ if (xhci_is_vendor_info_code(xhci, trb_comp_code)) {
+ status = 0;
+ break;
+ }
+ xhci_warn(xhci, "ERROR Unknown event condition, HC probably "
+ "busted\n");
+ goto cleanup;
+ }
+
+ do {
+ /* This TRB should be in the TD at the head of this ring's
+ * TD list.
+ */
+ if (list_empty(&ep_ring->td_list)) {
+ xhci_warn(xhci, "WARN Event TRB for slot %d ep %d "
+ "with no TDs queued?\n",
+ TRB_TO_SLOT_ID(event->flags), ep_index);
+ xhci_dbg(xhci, "Event TRB with TRB type ID %u\n",
+ (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10);
+ xhci_print_trb_offsets(xhci, (union xhci_trb *) event);
+ if (ep->skip) {
+ ep->skip = false;
+ xhci_dbg(xhci, "td_list is empty while skip "
+ "flag set. Clear skip flag.\n");
+ }
+ ret = 0;
+ goto cleanup;
+ }
+
+ td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list);
+ /* Is this a TRB in the currently executing TD? */
+ event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue,
+ td->last_trb, event_dma);
+ if (event_seg && ep->skip) {
+ xhci_dbg(xhci, "Found td. Clear skip flag.\n");
+ ep->skip = false;
+ }
+ if (!event_seg &&
+ (!ep->skip || !usb_endpoint_xfer_isoc(&td->urb->ep->desc))) {
+ /* HC is busted, give up! */
+ xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not "
+ "part of current TD\n");
+ return -ESHUTDOWN;
+ }
+
+ if (event_seg) {
+ event_trb = &event_seg->trbs[(event_dma -
+ event_seg->dma) / sizeof(*event_trb)];
+ /*
+ * No-op TRB should not trigger interrupts.
+ * If event_trb is a no-op TRB, it means the
+ * corresponding TD has been cancelled. Just ignore
+ * the TD.
+ */
+ if ((event_trb->generic.field[3] & TRB_TYPE_BITMASK)
+ == TRB_TYPE(TRB_TR_NOOP)) {
+ xhci_dbg(xhci, "event_trb is a no-op TRB. "
+ "Skip it\n");
+ goto cleanup;
+ }
+ }
+
+ /* Now update the urb's actual_length and give back to
+ * the core
+ */
+ if (usb_endpoint_xfer_control(&td->urb->ep->desc))
+ ret = process_ctrl_td(xhci, td, event_trb, event, ep,
+ &status);
+ else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc))
+ ret = process_isoc_td(xhci, td, event_trb, event, ep,
+ &status);
+ else
+ ret = process_bulk_intr_td(xhci, td, event_trb, event,
+ ep, &status);
+
+cleanup:
+ /*
+ * Do not update event ring dequeue pointer if ep->skip is set.
+ * Will roll back to continue process missed tds.
+ */
+ if (trb_comp_code == COMP_MISSED_INT || !ep->skip) {
+ inc_deq(xhci, xhci->event_ring, true);
+ }
+
+ if (ret) {
+ urb = td->urb;
+ urb_priv = urb->hcpriv;
+ /* Leave the TD around for the reset endpoint function
+ * to use(but only if it's not a control endpoint,
+ * since we already queued the Set TR dequeue pointer
+ * command for stalled control endpoints).
+ */
+ if (usb_endpoint_xfer_control(&urb->ep->desc) ||
+ (trb_comp_code != COMP_STALL &&
+ trb_comp_code != COMP_BABBLE))
+ xhci_urb_free_priv(xhci, urb_priv);
+
+ usb_hcd_unlink_urb_from_ep(xhci_to_hcd(xhci), urb);
+ xhci_dbg(xhci, "Giveback URB %p, len = %d, "
+ "status = %d\n",
+ urb, urb->actual_length, status);
+ spin_unlock(&xhci->lock);
+ usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, status);
+ spin_lock(&xhci->lock);
+ }
+
+ /*
+ * If ep->skip is set, it means there are missed tds on the
+ * endpoint ring need to take care of.
+ * Process them as short transfer until reach the td pointed by
+ * the event.
+ */
+ } while (ep->skip && trb_comp_code != COMP_MISSED_INT);
+
+ return 0;
+}
+
+/*
+ * This function handles all OS-owned events on the event ring. It may drop
+ * xhci->lock between event processing (e.g. to pass up port status changes).
+ */
+static void xhci_handle_event(struct xhci_hcd *xhci)
+{
+ union xhci_trb *event;
+ int update_ptrs = 1;
+ int ret;
+
+ xhci_dbg(xhci, "In %s\n", __func__);
+ if (!xhci->event_ring || !xhci->event_ring->dequeue) {
+ xhci->error_bitmask |= 1 << 1;
+ return;
+ }
+
+ event = xhci->event_ring->dequeue;
+ /* Does the HC or OS own the TRB? */
+ if ((event->event_cmd.flags & TRB_CYCLE) !=
+ xhci->event_ring->cycle_state) {
+ xhci->error_bitmask |= 1 << 2;
+ return;
+ }
+ xhci_dbg(xhci, "%s - OS owns TRB\n", __func__);
+
+ /* FIXME: Handle more event types. */
+ switch ((event->event_cmd.flags & TRB_TYPE_BITMASK)) {
+ case TRB_TYPE(TRB_COMPLETION):
+ xhci_dbg(xhci, "%s - calling handle_cmd_completion\n", __func__);
+ handle_cmd_completion(xhci, &event->event_cmd);
+ xhci_dbg(xhci, "%s - returned from handle_cmd_completion\n", __func__);
+ break;
+ case TRB_TYPE(TRB_PORT_STATUS):
+ xhci_dbg(xhci, "%s - calling handle_port_status\n", __func__);
+ handle_port_status(xhci, event);
+ xhci_dbg(xhci, "%s - returned from handle_port_status\n", __func__);
+ update_ptrs = 0;
+ break;
+ case TRB_TYPE(TRB_TRANSFER):
+ xhci_dbg(xhci, "%s - calling handle_tx_event\n", __func__);
+ ret = handle_tx_event(xhci, &event->trans_event);
+ xhci_dbg(xhci, "%s - returned from handle_tx_event\n", __func__);
+ if (ret < 0)
+ xhci->error_bitmask |= 1 << 9;
+ else
+ update_ptrs = 0;
+ break;
+ default:
+ if ((event->event_cmd.flags & TRB_TYPE_BITMASK) >= TRB_TYPE(48))
+ handle_vendor_event(xhci, event);
+ else
+ xhci->error_bitmask |= 1 << 3;
+ }
+ /* Any of the above functions may drop and re-acquire the lock, so check
+ * to make sure a watchdog timer didn't mark the host as non-responsive.
+ */
+ if (xhci->xhc_state & XHCI_STATE_DYING) {
+ xhci_dbg(xhci, "xHCI host dying, returning from "
+ "event handler.\n");
+ return;
+ }
+
+ if (update_ptrs)
+ /* Update SW event ring dequeue pointer */
+ inc_deq(xhci, xhci->event_ring, true);
+
/* Are there more items on the event ring? */
xhci_handle_event(xhci);
}
+/*
+ * xHCI spec says we can get an interrupt, and if the HC has an error condition,
+ * we might get bad data out of the event ring. Section 4.10.2.7 has a list of
+ * indicators of an event TRB error, but we check the status *first* to be safe.
+ */
+irqreturn_t xhci_irq(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ u32 status;
+ union xhci_trb *trb;
+ u64 temp_64;
+ union xhci_trb *event_ring_deq;
+ dma_addr_t deq;
+
+ spin_lock(&xhci->lock);
+ trb = xhci->event_ring->dequeue;
+ /* Check if the xHC generated the interrupt, or the irq is shared */
+ status = xhci_readl(xhci, &xhci->op_regs->status);
+ if (status == 0xffffffff)
+ goto hw_died;
+
+ if (!(status & STS_EINT)) {
+ spin_unlock(&xhci->lock);
+ xhci_warn(xhci, "Spurious interrupt.\n");
+ return IRQ_NONE;
+ }
+ xhci_dbg(xhci, "op reg status = %08x\n", status);
+ xhci_dbg(xhci, "Event ring dequeue ptr:\n");
+ xhci_dbg(xhci, "@%llx %08x %08x %08x %08x\n",
+ (unsigned long long)
+ xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, trb),
+ lower_32_bits(trb->link.segment_ptr),
+ upper_32_bits(trb->link.segment_ptr),
+ (unsigned int) trb->link.intr_target,
+ (unsigned int) trb->link.control);
+
+ if (status & STS_FATAL) {
+ xhci_warn(xhci, "WARNING: Host System Error\n");
+ xhci_halt(xhci);
+hw_died:
+ xhci_to_hcd(xhci)->state = HC_STATE_HALT;
+ spin_unlock(&xhci->lock);
+ return -ESHUTDOWN;
+ }
+
+ /*
+ * Clear the op reg interrupt status first,
+ * so we can receive interrupts from other MSI-X interrupters.
+ * Write 1 to clear the interrupt status.
+ */
+ status |= STS_EINT;
+ xhci_writel(xhci, status, &xhci->op_regs->status);
+ /* FIXME when MSI-X is supported and there are multiple vectors */
+ /* Clear the MSI-X event interrupt status */
+
+ if (hcd->irq != -1) {
+ u32 irq_pending;
+ /* Acknowledge the PCI interrupt */
+ irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending);
+ irq_pending |= 0x3;
+ xhci_writel(xhci, irq_pending, &xhci->ir_set->irq_pending);
+ }
+
+ if (xhci->xhc_state & XHCI_STATE_DYING) {
+ xhci_dbg(xhci, "xHCI dying, ignoring interrupt. "
+ "Shouldn't IRQs be disabled?\n");
+ /* Clear the event handler busy flag (RW1C);
+ * the event ring should be empty.
+ */
+ temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
+ xhci_write_64(xhci, temp_64 | ERST_EHB,
+ &xhci->ir_set->erst_dequeue);
+ spin_unlock(&xhci->lock);
+
+ return IRQ_HANDLED;
+ }
+
+ event_ring_deq = xhci->event_ring->dequeue;
+ /* FIXME this should be a delayed service routine
+ * that clears the EHB.
+ */
+ xhci_handle_event(xhci);
+
+ temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
+ /* If necessary, update the HW's version of the event ring deq ptr. */
+ if (event_ring_deq != xhci->event_ring->dequeue) {
+ deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg,
+ xhci->event_ring->dequeue);
+ if (deq == 0)
+ xhci_warn(xhci, "WARN something wrong with SW event "
+ "ring dequeue ptr.\n");
+ /* Update HC event ring dequeue pointer */
+ temp_64 &= ERST_PTR_MASK;
+ temp_64 |= ((u64) deq & (u64) ~ERST_PTR_MASK);
+ }
+
+ /* Clear the event handler busy flag (RW1C); event ring is empty. */
+ temp_64 |= ERST_EHB;
+ xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue);
+
+ spin_unlock(&xhci->lock);
+
+ return IRQ_HANDLED;
+}
+
+irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd)
+{
+ irqreturn_t ret;
+
+ set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+
+ ret = xhci_irq(hcd);
+
+ return ret;
+}
+
/**** Endpoint Ring Operations ****/
/*
unsigned int stream_id,
unsigned int num_trbs,
struct urb *urb,
- struct xhci_td **td,
+ unsigned int td_index,
gfp_t mem_flags)
{
int ret;
+ struct urb_priv *urb_priv;
+ struct xhci_td *td;
struct xhci_ring *ep_ring;
struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
num_trbs, mem_flags);
if (ret)
return ret;
- *td = kzalloc(sizeof(struct xhci_td), mem_flags);
- if (!*td)
- return -ENOMEM;
- INIT_LIST_HEAD(&(*td)->td_list);
- INIT_LIST_HEAD(&(*td)->cancelled_td_list);
- ret = usb_hcd_link_urb_to_ep(xhci_to_hcd(xhci), urb);
- if (unlikely(ret)) {
- kfree(*td);
- return ret;
+ urb_priv = urb->hcpriv;
+ td = urb_priv->td[td_index];
+
+ INIT_LIST_HEAD(&td->td_list);
+ INIT_LIST_HEAD(&td->cancelled_td_list);
+
+ if (td_index == 0) {
+ ret = usb_hcd_link_urb_to_ep(xhci_to_hcd(xhci), urb);
+ if (unlikely(ret)) {
+ xhci_urb_free_priv(xhci, urb_priv);
+ urb->hcpriv = NULL;
+ return ret;
+ }
}
- (*td)->urb = urb;
- urb->hcpriv = (void *) (*td);
+ td->urb = urb;
/* Add this TD to the tail of the endpoint ring's TD list */
- list_add_tail(&(*td)->td_list, &ep_ring->td_list);
- (*td)->start_seg = ep_ring->enq_seg;
- (*td)->first_trb = ep_ring->enqueue;
+ list_add_tail(&td->td_list, &ep_ring->td_list);
+ td->start_seg = ep_ring->enq_seg;
+ td->first_trb = ep_ring->enqueue;
+
+ urb_priv->td[td_index] = td;
return 0;
}
{
struct xhci_ring *ep_ring;
unsigned int num_trbs;
+ struct urb_priv *urb_priv;
struct xhci_td *td;
struct scatterlist *sg;
int num_sgs;
trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id],
ep_index, urb->stream_id,
- num_trbs, urb, &td, mem_flags);
+ num_trbs, urb, 0, mem_flags);
if (trb_buff_len < 0)
return trb_buff_len;
+
+ urb_priv = urb->hcpriv;
+ td = urb_priv->td[0];
+
/*
* Don't give the first TRB to the hardware (by toggling the cycle bit)
* until we've finished creating all the other TRBs. The ring's cycle
struct urb *urb, int slot_id, unsigned int ep_index)
{
struct xhci_ring *ep_ring;
+ struct urb_priv *urb_priv;
struct xhci_td *td;
int num_trbs;
struct xhci_generic_trb *start_trb;
ret = prepare_transfer(xhci, xhci->devs[slot_id],
ep_index, urb->stream_id,
- num_trbs, urb, &td, mem_flags);
+ num_trbs, urb, 0, mem_flags);
if (ret < 0)
return ret;
+ urb_priv = urb->hcpriv;
+ td = urb_priv->td[0];
+
/*
* Don't give the first TRB to the hardware (by toggling the cycle bit)
* until we've finished creating all the other TRBs. The ring's cycle
struct xhci_generic_trb *start_trb;
int start_cycle;
u32 field, length_field;
+ struct urb_priv *urb_priv;
struct xhci_td *td;
ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
num_trbs++;
ret = prepare_transfer(xhci, xhci->devs[slot_id],
ep_index, urb->stream_id,
- num_trbs, urb, &td, mem_flags);
+ num_trbs, urb, 0, mem_flags);
if (ret < 0)
return ret;
+ urb_priv = urb->hcpriv;
+ td = urb_priv->td[0];
+
/*
* Don't give the first TRB to the hardware (by toggling the cycle bit)
* until we've finished creating all the other TRBs. The ring's cycle
return 0;
}
+static int count_isoc_trbs_needed(struct xhci_hcd *xhci,
+ struct urb *urb, int i)
+{
+ int num_trbs = 0;
+ u64 addr, td_len, running_total;
+
+ addr = (u64) (urb->transfer_dma + urb->iso_frame_desc[i].offset);
+ td_len = urb->iso_frame_desc[i].length;
+
+ running_total = TRB_MAX_BUFF_SIZE -
+ (addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1));
+ if (running_total != 0)
+ num_trbs++;
+
+ while (running_total < td_len) {
+ num_trbs++;
+ running_total += TRB_MAX_BUFF_SIZE;
+ }
+
+ return num_trbs;
+}
+
+/* This is for isoc transfer */
+static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
+ struct urb *urb, int slot_id, unsigned int ep_index)
+{
+ struct xhci_ring *ep_ring;
+ struct urb_priv *urb_priv;
+ struct xhci_td *td;
+ int num_tds, trbs_per_td;
+ struct xhci_generic_trb *start_trb;
+ bool first_trb;
+ int start_cycle;
+ u32 field, length_field;
+ int running_total, trb_buff_len, td_len, td_remain_len, ret;
+ u64 start_addr, addr;
+ int i, j;
+
+ ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
+
+ num_tds = urb->number_of_packets;
+ if (num_tds < 1) {
+ xhci_dbg(xhci, "Isoc URB with zero packets?\n");
+ return -EINVAL;
+ }
+
+ if (!in_interrupt())
+ dev_dbg(&urb->dev->dev, "ep %#x - urb len = %#x (%d),"
+ " addr = %#llx, num_tds = %d\n",
+ urb->ep->desc.bEndpointAddress,
+ urb->transfer_buffer_length,
+ urb->transfer_buffer_length,
+ (unsigned long long)urb->transfer_dma,
+ num_tds);
+
+ start_addr = (u64) urb->transfer_dma;
+ start_trb = &ep_ring->enqueue->generic;
+ start_cycle = ep_ring->cycle_state;
+
+ /* Queue the first TRB, even if it's zero-length */
+ for (i = 0; i < num_tds; i++) {
+ first_trb = true;
+
+ running_total = 0;
+ addr = start_addr + urb->iso_frame_desc[i].offset;
+ td_len = urb->iso_frame_desc[i].length;
+ td_remain_len = td_len;
+
+ trbs_per_td = count_isoc_trbs_needed(xhci, urb, i);
+
+ ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index,
+ urb->stream_id, trbs_per_td, urb, i, mem_flags);
+ if (ret < 0)
+ return ret;
+
+ urb_priv = urb->hcpriv;
+ td = urb_priv->td[i];
+
+ for (j = 0; j < trbs_per_td; j++) {
+ u32 remainder = 0;
+ field = 0;
+
+ if (first_trb) {
+ /* Queue the isoc TRB */
+ field |= TRB_TYPE(TRB_ISOC);
+ /* Assume URB_ISO_ASAP is set */
+ field |= TRB_SIA;
+ if (i > 0)
+ field |= ep_ring->cycle_state;
+ first_trb = false;
+ } else {
+ /* Queue other normal TRBs */
+ field |= TRB_TYPE(TRB_NORMAL);
+ field |= ep_ring->cycle_state;
+ }
+
+ /* Chain all the TRBs together; clear the chain bit in
+ * the last TRB to indicate it's the last TRB in the
+ * chain.
+ */
+ if (j < trbs_per_td - 1) {
+ field |= TRB_CHAIN;
+ } else {
+ td->last_trb = ep_ring->enqueue;
+ field |= TRB_IOC;
+ }
+
+ /* Calculate TRB length */
+ trb_buff_len = TRB_MAX_BUFF_SIZE -
+ (addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1));
+ if (trb_buff_len > td_remain_len)
+ trb_buff_len = td_remain_len;
+
+ remainder = xhci_td_remainder(td_len - running_total);
+ length_field = TRB_LEN(trb_buff_len) |
+ remainder |
+ TRB_INTR_TARGET(0);
+ queue_trb(xhci, ep_ring, false, false,
+ lower_32_bits(addr),
+ upper_32_bits(addr),
+ length_field,
+ /* We always want to know if the TRB was short,
+ * or we won't get an event when it completes.
+ * (Unless we use event data TRBs, which are a
+ * waste of space and HC resources.)
+ */
+ field | TRB_ISP);
+ running_total += trb_buff_len;
+
+ addr += trb_buff_len;
+ td_remain_len -= trb_buff_len;
+ }
+
+ /* Check TD length */
+ if (running_total != td_len) {
+ xhci_err(xhci, "ISOC TD length unmatch\n");
+ return -EINVAL;
+ }
+ }
+
+ wmb();
+ start_trb->field[3] |= start_cycle;
+
+ ring_ep_doorbell(xhci, slot_id, ep_index, urb->stream_id);
+ return 0;
+}
+
+/*
+ * Check transfer ring to guarantee there is enough room for the urb.
+ * Update ISO URB start_frame and interval.
+ * Update interval as xhci_queue_intr_tx does. Just use xhci frame_index to
+ * update the urb->start_frame by now.
+ * Always assume URB_ISO_ASAP set, and NEVER use urb->start_frame as input.
+ */
+int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
+ struct urb *urb, int slot_id, unsigned int ep_index)
+{
+ struct xhci_virt_device *xdev;
+ struct xhci_ring *ep_ring;
+ struct xhci_ep_ctx *ep_ctx;
+ int start_frame;
+ int xhci_interval;
+ int ep_interval;
+ int num_tds, num_trbs, i;
+ int ret;
+
+ xdev = xhci->devs[slot_id];
+ ep_ring = xdev->eps[ep_index].ring;
+ ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
+
+ num_trbs = 0;
+ num_tds = urb->number_of_packets;
+ for (i = 0; i < num_tds; i++)
+ num_trbs += count_isoc_trbs_needed(xhci, urb, i);
+
+ /* Check the ring to guarantee there is enough room for the whole urb.
+ * Do not insert any td of the urb to the ring if the check failed.
+ */
+ ret = prepare_ring(xhci, ep_ring, ep_ctx->ep_info & EP_STATE_MASK,
+ num_trbs, mem_flags);
+ if (ret)
+ return ret;
+
+ start_frame = xhci_readl(xhci, &xhci->run_regs->microframe_index);
+ start_frame &= 0x3fff;
+
+ urb->start_frame = start_frame;
+ if (urb->dev->speed == USB_SPEED_LOW ||
+ urb->dev->speed == USB_SPEED_FULL)
+ urb->start_frame >>= 3;
+
+ xhci_interval = EP_INTERVAL_TO_UFRAMES(ep_ctx->ep_info);
+ ep_interval = urb->interval;
+ /* Convert to microframes */
+ if (urb->dev->speed == USB_SPEED_LOW ||
+ urb->dev->speed == USB_SPEED_FULL)
+ ep_interval *= 8;
+ /* FIXME change this to a warning and a suggestion to use the new API
+ * to set the polling interval (once the API is added).
+ */
+ if (xhci_interval != ep_interval) {
+ if (!printk_ratelimit())
+ dev_dbg(&urb->dev->dev, "Driver uses different interval"
+ " (%d microframe%s) than xHCI "
+ "(%d microframe%s)\n",
+ ep_interval,
+ ep_interval == 1 ? "" : "s",
+ xhci_interval,
+ xhci_interval == 1 ? "" : "s");
+ urb->interval = xhci_interval;
+ /* Convert back to frames for LS/FS devices */
+ if (urb->dev->speed == USB_SPEED_LOW ||
+ urb->dev->speed == USB_SPEED_FULL)
+ urb->interval /= 8;
+ }
+ return xhci_queue_isoc_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index);
+}
+
/**** Command Ring Operations ****/
/* Generic function for queueing a command TRB on the command ring.
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <linux/pci.h>
#include <linux/irq.h>
#include <linux/log2.h>
#include <linux/module.h>
return handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000);
}
+/*
+ * Free IRQs
+ * free all IRQs request
+ */
+static void xhci_free_irq(struct xhci_hcd *xhci)
+{
+ int i;
+ struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
-#if 0
-/* Set up MSI-X table for entry 0 (may claim other entries later) */
-static int xhci_setup_msix(struct xhci_hcd *xhci)
+ /* return if using legacy interrupt */
+ if (xhci_to_hcd(xhci)->irq >= 0)
+ return;
+
+ if (xhci->msix_entries) {
+ for (i = 0; i < xhci->msix_count; i++)
+ if (xhci->msix_entries[i].vector)
+ free_irq(xhci->msix_entries[i].vector,
+ xhci_to_hcd(xhci));
+ } else if (pdev->irq >= 0)
+ free_irq(pdev->irq, xhci_to_hcd(xhci));
+
+ return;
+}
+
+/*
+ * Set up MSI
+ */
+static int xhci_setup_msi(struct xhci_hcd *xhci)
{
int ret;
+ struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
+
+ ret = pci_enable_msi(pdev);
+ if (ret) {
+ xhci_err(xhci, "failed to allocate MSI entry\n");
+ return ret;
+ }
+
+ ret = request_irq(pdev->irq, (irq_handler_t)xhci_msi_irq,
+ 0, "xhci_hcd", xhci_to_hcd(xhci));
+ if (ret) {
+ xhci_err(xhci, "disable MSI interrupt\n");
+ pci_disable_msi(pdev);
+ }
+
+ return ret;
+}
+
+/*
+ * Set up MSI-X
+ */
+static int xhci_setup_msix(struct xhci_hcd *xhci)
+{
+ int i, ret = 0;
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
- xhci->msix_count = 0;
- /* XXX: did I do this right? ixgbe does kcalloc for more than one */
- xhci->msix_entries = kmalloc(sizeof(struct msix_entry), GFP_KERNEL);
+ /*
+ * calculate number of msi-x vectors supported.
+ * - HCS_MAX_INTRS: the max number of interrupts the host can handle,
+ * with max number of interrupters based on the xhci HCSPARAMS1.
+ * - num_online_cpus: maximum msi-x vectors per CPUs core.
+ * Add additional 1 vector to ensure always available interrupt.
+ */
+ xhci->msix_count = min(num_online_cpus() + 1,
+ HCS_MAX_INTRS(xhci->hcs_params1));
+
+ xhci->msix_entries =
+ kmalloc((sizeof(struct msix_entry))*xhci->msix_count,
+ GFP_KERNEL);
if (!xhci->msix_entries) {
xhci_err(xhci, "Failed to allocate MSI-X entries\n");
return -ENOMEM;
}
- xhci->msix_entries[0].entry = 0;
+
+ for (i = 0; i < xhci->msix_count; i++) {
+ xhci->msix_entries[i].entry = i;
+ xhci->msix_entries[i].vector = 0;
+ }
ret = pci_enable_msix(pdev, xhci->msix_entries, xhci->msix_count);
if (ret) {
goto free_entries;
}
- /*
- * Pass the xhci pointer value as the request_irq "cookie".
- * If more irqs are added, this will need to be unique for each one.
- */
- ret = request_irq(xhci->msix_entries[0].vector, &xhci_irq, 0,
- "xHCI", xhci_to_hcd(xhci));
- if (ret) {
- xhci_err(xhci, "Failed to allocate MSI-X interrupt\n");
- goto disable_msix;
+ for (i = 0; i < xhci->msix_count; i++) {
+ ret = request_irq(xhci->msix_entries[i].vector,
+ (irq_handler_t)xhci_msi_irq,
+ 0, "xhci_hcd", xhci_to_hcd(xhci));
+ if (ret)
+ goto disable_msix;
}
- xhci_dbg(xhci, "Finished setting up MSI-X\n");
- return 0;
+
+ return ret;
disable_msix:
+ xhci_err(xhci, "disable MSI-X interrupt\n");
+ xhci_free_irq(xhci);
pci_disable_msix(pdev);
free_entries:
kfree(xhci->msix_entries);
return ret;
}
-/* XXX: code duplication; can xhci_setup_msix call this? */
/* Free any IRQs and disable MSI-X */
static void xhci_cleanup_msix(struct xhci_hcd *xhci)
{
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
- if (!xhci->msix_entries)
- return;
- free_irq(xhci->msix_entries[0].vector, xhci);
- pci_disable_msix(pdev);
- kfree(xhci->msix_entries);
- xhci->msix_entries = NULL;
- xhci_dbg(xhci, "Finished cleaning up MSI-X\n");
+ xhci_free_irq(xhci);
+
+ if (xhci->msix_entries) {
+ pci_disable_msix(pdev);
+ kfree(xhci->msix_entries);
+ xhci->msix_entries = NULL;
+ } else {
+ pci_disable_msi(pdev);
+ }
+
+ return;
}
-#endif
/*
* Initialize memory for HCD and xHC (one-time init).
return retval;
}
-/*
- * Called in interrupt context when there might be work
- * queued on the event ring
- *
- * xhci->lock must be held by caller.
- */
-static void xhci_work(struct xhci_hcd *xhci)
-{
- u32 temp;
- u64 temp_64;
-
- /*
- * Clear the op reg interrupt status first,
- * so we can receive interrupts from other MSI-X interrupters.
- * Write 1 to clear the interrupt status.
- */
- temp = xhci_readl(xhci, &xhci->op_regs->status);
- temp |= STS_EINT;
- xhci_writel(xhci, temp, &xhci->op_regs->status);
- /* FIXME when MSI-X is supported and there are multiple vectors */
- /* Clear the MSI-X event interrupt status */
-
- /* Acknowledge the interrupt */
- temp = xhci_readl(xhci, &xhci->ir_set->irq_pending);
- temp |= 0x3;
- xhci_writel(xhci, temp, &xhci->ir_set->irq_pending);
- /* Flush posted writes */
- xhci_readl(xhci, &xhci->ir_set->irq_pending);
-
- if (xhci->xhc_state & XHCI_STATE_DYING)
- xhci_dbg(xhci, "xHCI dying, ignoring interrupt. "
- "Shouldn't IRQs be disabled?\n");
- else
- /* FIXME this should be a delayed service routine
- * that clears the EHB.
- */
- xhci_handle_event(xhci);
-
- /* Clear the event handler busy flag (RW1C); the event ring should be empty. */
- temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
- xhci_write_64(xhci, temp_64 | ERST_EHB, &xhci->ir_set->erst_dequeue);
- /* Flush posted writes -- FIXME is this necessary? */
- xhci_readl(xhci, &xhci->ir_set->irq_pending);
-}
-
/*-------------------------------------------------------------------------*/
-/*
- * xHCI spec says we can get an interrupt, and if the HC has an error condition,
- * we might get bad data out of the event ring. Section 4.10.2.7 has a list of
- * indicators of an event TRB error, but we check the status *first* to be safe.
- */
-irqreturn_t xhci_irq(struct usb_hcd *hcd)
-{
- struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- u32 temp, temp2;
- union xhci_trb *trb;
-
- spin_lock(&xhci->lock);
- trb = xhci->event_ring->dequeue;
- /* Check if the xHC generated the interrupt, or the irq is shared */
- temp = xhci_readl(xhci, &xhci->op_regs->status);
- temp2 = xhci_readl(xhci, &xhci->ir_set->irq_pending);
- if (temp == 0xffffffff && temp2 == 0xffffffff)
- goto hw_died;
-
- if (!(temp & STS_EINT) && !ER_IRQ_PENDING(temp2)) {
- spin_unlock(&xhci->lock);
- return IRQ_NONE;
- }
- xhci_dbg(xhci, "op reg status = %08x\n", temp);
- xhci_dbg(xhci, "ir set irq_pending = %08x\n", temp2);
- xhci_dbg(xhci, "Event ring dequeue ptr:\n");
- xhci_dbg(xhci, "@%llx %08x %08x %08x %08x\n",
- (unsigned long long)xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, trb),
- lower_32_bits(trb->link.segment_ptr),
- upper_32_bits(trb->link.segment_ptr),
- (unsigned int) trb->link.intr_target,
- (unsigned int) trb->link.control);
-
- if (temp & STS_FATAL) {
- xhci_warn(xhci, "WARNING: Host System Error\n");
- xhci_halt(xhci);
-hw_died:
- xhci_to_hcd(xhci)->state = HC_STATE_HALT;
- spin_unlock(&xhci->lock);
- return -ESHUTDOWN;
- }
-
- xhci_work(xhci);
- spin_unlock(&xhci->lock);
-
- return IRQ_HANDLED;
-}
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
void xhci_event_ring_work(unsigned long arg)
{
u32 temp;
u64 temp_64;
+ u32 ret;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
void (*doorbell)(struct xhci_hcd *) = NULL;
hcd->uses_new_polling = 1;
- hcd->poll_rh = 0;
xhci_dbg(xhci, "xhci_run\n");
-#if 0 /* FIXME: MSI not setup yet */
- /* Do this at the very last minute */
+ /* unregister the legacy interrupt */
+ if (hcd->irq)
+ free_irq(hcd->irq, hcd);
+ hcd->irq = -1;
+
ret = xhci_setup_msix(xhci);
- if (!ret)
- return ret;
+ if (ret)
+ /* fall back to msi*/
+ ret = xhci_setup_msi(xhci);
+
+ if (ret) {
+ /* fall back to legacy interrupt*/
+ ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED,
+ hcd->irq_descr, hcd);
+ if (ret) {
+ xhci_err(xhci, "request interrupt %d failed\n",
+ pdev->irq);
+ return ret;
+ }
+ hcd->irq = pdev->irq;
+ }
- return -ENOSYS;
-#endif
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
init_timer(&xhci->event_ring_timer);
xhci->event_ring_timer.data = (unsigned long) xhci;
return -ENODEV;
}
- xhci_dbg(xhci, "// @%p = 0x%x\n", &xhci->op_regs->command, temp);
if (doorbell)
(*doorbell)(xhci);
if (xhci->quirks & XHCI_NEC_HOST)
spin_lock_irq(&xhci->lock);
xhci_halt(xhci);
xhci_reset(xhci);
+ xhci_cleanup_msix(xhci);
spin_unlock_irq(&xhci->lock);
-#if 0 /* No MSI yet */
- xhci_cleanup_msix(xhci);
-#endif
#ifdef CONFIG_USB_XHCI_HCD_DEBUGGING
/* Tell the event ring poll function not to reschedule */
xhci->zombie = 1;
spin_lock_irq(&xhci->lock);
xhci_halt(xhci);
- spin_unlock_irq(&xhci->lock);
-
-#if 0
xhci_cleanup_msix(xhci);
-#endif
+ spin_unlock_irq(&xhci->lock);
xhci_dbg(xhci, "xhci_shutdown completed - status = %x\n",
xhci_readl(xhci, &xhci->op_regs->status));
unsigned long flags;
int ret = 0;
unsigned int slot_id, ep_index;
-
+ struct urb_priv *urb_priv;
+ int size, i;
if (!urb || xhci_check_args(hcd, urb->dev, urb->ep, true, __func__) <= 0)
return -EINVAL;
ret = -EINVAL;
goto exit;
}
- if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
+ if (!HCD_HW_ACCESSIBLE(hcd)) {
if (!in_interrupt())
xhci_dbg(xhci, "urb submitted during PCI suspend\n");
ret = -ESHUTDOWN;
goto exit;
}
+
+ if (usb_endpoint_xfer_isoc(&urb->ep->desc))
+ size = urb->number_of_packets;
+ else
+ size = 1;
+
+ urb_priv = kzalloc(sizeof(struct urb_priv) +
+ size * sizeof(struct xhci_td *), mem_flags);
+ if (!urb_priv)
+ return -ENOMEM;
+
+ for (i = 0; i < size; i++) {
+ urb_priv->td[i] = kzalloc(sizeof(struct xhci_td), mem_flags);
+ if (!urb_priv->td[i]) {
+ urb_priv->length = i;
+ xhci_urb_free_priv(xhci, urb_priv);
+ return -ENOMEM;
+ }
+ }
+
+ urb_priv->length = size;
+ urb_priv->td_cnt = 0;
+ urb->hcpriv = urb_priv;
+
if (usb_endpoint_xfer_control(&urb->ep->desc)) {
/* Check to see if the max packet size for the default control
* endpoint changed during FS device enumeration
slot_id, ep_index);
spin_unlock_irqrestore(&xhci->lock, flags);
} else {
- ret = -EINVAL;
+ spin_lock_irqsave(&xhci->lock, flags);
+ if (xhci->xhc_state & XHCI_STATE_DYING)
+ goto dying;
+ ret = xhci_queue_isoc_tx_prepare(xhci, GFP_ATOMIC, urb,
+ slot_id, ep_index);
+ spin_unlock_irqrestore(&xhci->lock, flags);
}
exit:
return ret;
dying:
+ xhci_urb_free_priv(xhci, urb_priv);
+ urb->hcpriv = NULL;
xhci_dbg(xhci, "Ep 0x%x: URB %p submitted for "
"non-responsive xHCI host.\n",
urb->ep->desc.bEndpointAddress, urb);
return -ESHUTDOWN;
}
+/* Get the right ring for the given URB.
+ * If the endpoint supports streams, boundary check the URB's stream ID.
+ * If the endpoint doesn't support streams, return the singular endpoint ring.
+ */
+static struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
+ struct urb *urb)
+{
+ unsigned int slot_id;
+ unsigned int ep_index;
+ unsigned int stream_id;
+ struct xhci_virt_ep *ep;
+
+ slot_id = urb->dev->slot_id;
+ ep_index = xhci_get_endpoint_index(&urb->ep->desc);
+ stream_id = urb->stream_id;
+ ep = &xhci->devs[slot_id]->eps[ep_index];
+ /* Common case: no streams */
+ if (!(ep->ep_state & EP_HAS_STREAMS))
+ return ep->ring;
+
+ if (stream_id == 0) {
+ xhci_warn(xhci,
+ "WARN: Slot ID %u, ep index %u has streams, "
+ "but URB has no stream ID.\n",
+ slot_id, ep_index);
+ return NULL;
+ }
+
+ if (stream_id < ep->stream_info->num_streams)
+ return ep->stream_info->stream_rings[stream_id];
+
+ xhci_warn(xhci,
+ "WARN: Slot ID %u, ep index %u has "
+ "stream IDs 1 to %u allocated, "
+ "but stream ID %u is requested.\n",
+ slot_id, ep_index,
+ ep->stream_info->num_streams - 1,
+ stream_id);
+ return NULL;
+}
+
/*
* Remove the URB's TD from the endpoint ring. This may cause the HC to stop
* USB transfers, potentially stopping in the middle of a TRB buffer. The HC
int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
{
unsigned long flags;
- int ret;
+ int ret, i;
u32 temp;
struct xhci_hcd *xhci;
+ struct urb_priv *urb_priv;
struct xhci_td *td;
unsigned int ep_index;
struct xhci_ring *ep_ring;
temp = xhci_readl(xhci, &xhci->op_regs->status);
if (temp == 0xffffffff) {
xhci_dbg(xhci, "HW died, freeing TD.\n");
- td = (struct xhci_td *) urb->hcpriv;
+ urb_priv = urb->hcpriv;
usb_hcd_unlink_urb_from_ep(hcd, urb);
spin_unlock_irqrestore(&xhci->lock, flags);
usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, -ESHUTDOWN);
- kfree(td);
+ xhci_urb_free_priv(xhci, urb_priv);
return ret;
}
if (xhci->xhc_state & XHCI_STATE_DYING) {
xhci_dbg(xhci, "Endpoint ring:\n");
xhci_debug_ring(xhci, ep_ring);
- td = (struct xhci_td *) urb->hcpriv;
- list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list);
+ urb_priv = urb->hcpriv;
+
+ for (i = urb_priv->td_cnt; i < urb_priv->length; i++) {
+ td = urb_priv->td[i];
+ list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list);
+ }
+
/* Queue a stop endpoint command, but only if this is
* the first cancellation to be handled.
*/
struct timer_list stop_cmd_timer;
int stop_cmds_pending;
struct xhci_hcd *xhci;
+ /*
+ * Sometimes the xHC can not process isochronous endpoint ring quickly
+ * enough, and it will miss some isoc tds on the ring and generate
+ * a Missed Service Error Event.
+ * Set skip flag when receive a Missed Service Error Event and
+ * process the missed tds on the endpoint ring.
+ */
+ bool skip;
};
struct xhci_virt_device {
/* Control transfer TRB specific fields */
#define TRB_DIR_IN (1<<16)
+/* Isochronous TRB specific fields */
+#define TRB_SIA (1<<31)
+
struct xhci_generic_trb {
u32 field[4];
};
dma_addr_t *sp_dma_buffers;
};
+struct urb_priv {
+ int length;
+ int td_cnt;
+ struct xhci_td *td[0];
+};
+
/*
* Each segment table entry is 4*32bits long. 1K seems like an ok size:
* (1K bytes * 8bytes/bit) / (4*32 bits) = 64 segment entries in the table,
int page_size;
/* Valid values are 12 to 20, inclusive */
int page_shift;
- /* only one MSI vector for now, but might need more later */
+ /* msi-x vectors */
int msix_count;
struct msix_entry *msix_entries;
/* data structures */
struct xhci_ring *xhci_dma_to_transfer_ring(
struct xhci_virt_ep *ep,
u64 address);
-struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
- struct urb *urb);
-struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
- unsigned int slot_id, unsigned int ep_index,
- unsigned int stream_id);
struct xhci_ring *xhci_stream_id_to_ring(
struct xhci_virt_device *dev,
unsigned int ep_index,
struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
bool allocate_in_ctx, bool allocate_completion,
gfp_t mem_flags);
+void xhci_urb_free_priv(struct xhci_hcd *xhci, struct urb_priv *urb_priv);
void xhci_free_command(struct xhci_hcd *xhci,
struct xhci_command *command);
void xhci_shutdown(struct usb_hcd *hcd);
int xhci_get_frame(struct usb_hcd *hcd);
irqreturn_t xhci_irq(struct usb_hcd *hcd);
+irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd);
int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev);
void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev);
int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code);
void xhci_ring_cmd_db(struct xhci_hcd *xhci);
void *xhci_setup_one_noop(struct xhci_hcd *xhci);
-void xhci_handle_event(struct xhci_hcd *xhci);
-void xhci_set_hc_event_deq(struct xhci_hcd *xhci);
int xhci_queue_slot_control(struct xhci_hcd *xhci, u32 trb_type, u32 slot_id);
int xhci_queue_address_device(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
u32 slot_id);
int slot_id, unsigned int ep_index);
int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb,
int slot_id, unsigned int ep_index);
+int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
+ struct urb *urb, int slot_id, unsigned int ep_index);
int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
u32 slot_id, bool command_must_succeed);
int xhci_queue_evaluate_context(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
static int ftdi_elan_release(struct inode *inode, struct file *file)
{
- struct usb_ftdi *ftdi = (struct usb_ftdi *)file->private_data;
+ struct usb_ftdi *ftdi = file->private_data;
if (ftdi == NULL)
return -ENODEV;
up(&ftdi->sw_lock); /* decrement the count on our device */
int bytes_read = 0;
int retry_on_empty = 10;
int retry_on_timeout = 5;
- struct usb_ftdi *ftdi = (struct usb_ftdi *)file->private_data;
+ struct usb_ftdi *ftdi = file->private_data;
if (ftdi->disconnected > 0) {
return -ENODEV;
}
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/sched.h>
-#include <linux/smp_lock.h>
+#include <linux/mutex.h>
#include <linux/poll.h>
#include <linux/usb/iowarrior.h>
MODULE_LICENSE("GPL");
/* Module parameters */
+static DEFINE_MUTEX(iowarrior_mutex);
static int debug = 0;
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "debug=1 enables debugging messages");
int read_idx;
int offset;
- dev = (struct iowarrior *)file->private_data;
+ dev = file->private_data;
/* verify that the device wasn't unplugged */
if (dev == NULL || !dev->present)
char *buf = NULL; /* for IOW24 and IOW56 we need a buffer */
struct urb *int_out_urb = NULL;
- dev = (struct iowarrior *)file->private_data;
+ dev = file->private_data;
mutex_lock(&dev->mutex);
/* verify that the device wasn't unplugged */
int retval;
int io_res; /* checks for bytes read/written and copy_to/from_user results */
- dev = (struct iowarrior *)file->private_data;
+ dev = file->private_data;
if (dev == NULL) {
return -ENODEV;
}
return -ENOMEM;
/* lock this object */
- lock_kernel();
+ mutex_lock(&iowarrior_mutex);
mutex_lock(&dev->mutex);
/* verify that the device wasn't unplugged */
error_out:
/* unlock the device */
mutex_unlock(&dev->mutex);
- unlock_kernel();
+ mutex_unlock(&iowarrior_mutex);
kfree(buffer);
return retval;
}
dbg("%s", __func__);
- lock_kernel();
+ mutex_lock(&iowarrior_mutex);
subminor = iminor(inode);
interface = usb_find_interface(&iowarrior_driver, subminor);
if (!interface) {
- unlock_kernel();
+ mutex_unlock(&iowarrior_mutex);
err("%s - error, can't find device for minor %d", __func__,
subminor);
return -ENODEV;
dev = usb_get_intfdata(interface);
if (!dev) {
mutex_unlock(&iowarrior_open_disc_lock);
- unlock_kernel();
+ mutex_unlock(&iowarrior_mutex);
return -ENODEV;
}
out:
mutex_unlock(&dev->mutex);
- unlock_kernel();
+ mutex_unlock(&iowarrior_mutex);
return retval;
}
struct iowarrior *dev;
int retval = 0;
- dev = (struct iowarrior *)file->private_data;
+ dev = file->private_data;
if (dev == NULL) {
return -ENODEV;
}
dbg(2, "%s: enter", __func__);
- dev = (struct lego_usb_tower *)file->private_data;
+ dev = file->private_data;
if (dev == NULL) {
dbg(1, "%s: object is NULL", __func__);
dbg(2, "%s: enter, count = %Zd", __func__, count);
- dev = (struct lego_usb_tower *)file->private_data;
+ dev = file->private_data;
/* lock this object */
if (mutex_lock_interruptible(&dev->lock)) {
dbg(2, "%s: enter, count = %Zd", __func__, count);
- dev = (struct lego_usb_tower *)file->private_data;
+ dev = file->private_data;
/* lock this object */
if (mutex_lock_interruptible(&dev->lock)) {
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/sched.h>
-#include <linux/smp_lock.h>
+#include <linux/mutex.h>
#include <linux/errno.h>
#include <linux/random.h>
#include <linux/poll.h>
struct mutex lock; /* general race avoidance */
};
+static DEFINE_MUTEX(rio500_mutex);
static struct rio_usb_data rio_instance;
static int open_rio(struct inode *inode, struct file *file)
struct rio_usb_data *rio = &rio_instance;
/* against disconnect() */
- lock_kernel();
+ mutex_lock(&rio500_mutex);
mutex_lock(&(rio->lock));
if (rio->isopen || !rio->present) {
mutex_unlock(&(rio->lock));
- unlock_kernel();
+ mutex_unlock(&rio500_mutex);
return -EBUSY;
}
rio->isopen = 1;
mutex_unlock(&(rio->lock));
dev_info(&rio->rio_dev->dev, "Rio opened.\n");
- unlock_kernel();
+ mutex_unlock(&rio500_mutex);
return 0;
}
struct rio_usb_data *rio = usb_get_intfdata (intf);
usb_set_intfdata (intf, NULL);
- lock_kernel();
+ mutex_lock(&rio500_mutex);
if (rio) {
usb_deregister_dev(intf, &usb_rio_class);
/* better let it finish - the release will do whats needed */
rio->rio_dev = NULL;
mutex_unlock(&(rio->lock));
- unlock_kernel();
+ mutex_unlock(&rio500_mutex);
return;
}
kfree(rio->ibuf);
rio->present = 0;
mutex_unlock(&(rio->lock));
}
- unlock_kernel();
+ mutex_unlock(&rio500_mutex);
}
static const struct usb_device_id rio_table[] = {
{
struct sisusb_usb_data *sisusb;
- if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
+ if (!(sisusb = file->private_data))
return -ENODEV;
mutex_lock(&sisusb->lock);
u16 buf16;
u32 buf32, address;
- if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
+ if (!(sisusb = file->private_data))
return -ENODEV;
mutex_lock(&sisusb->lock);
u16 buf16;
u32 buf32, address;
- if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
+ if (!(sisusb = file->private_data))
return -ENODEV;
mutex_lock(&sisusb->lock);
struct sisusb_usb_data *sisusb;
loff_t ret;
- if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
+ if (!(sisusb = file->private_data))
return -ENODEV;
mutex_lock(&sisusb->lock);
long retval = 0;
u32 __user *argp = (u32 __user *)arg;
- if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
+ if (!(sisusb = file->private_data))
return -ENODEV;
mutex_lock(&sisusb->lock);
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>
#define IOCTL_GET_DRV_VERSION 2
+static DEFINE_MUTEX(lcd_mutex);
static const struct usb_device_id id_table[] = {
{ .idVendor = 0x10D2, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, },
{ },
struct usb_interface *interface;
int subminor, r;
- lock_kernel();
+ mutex_lock(&lcd_mutex);
subminor = iminor(inode);
interface = usb_find_interface(&lcd_driver, subminor);
if (!interface) {
- unlock_kernel();
+ mutex_unlock(&lcd_mutex);
err ("USBLCD: %s - error, can't find device for minor %d",
__func__, subminor);
return -ENODEV;
dev = usb_get_intfdata(interface);
if (!dev) {
mutex_unlock(&open_disc_mutex);
- unlock_kernel();
+ mutex_unlock(&lcd_mutex);
return -ENODEV;
}
r = usb_autopm_get_interface(interface);
if (r < 0) {
kref_put(&dev->kref, lcd_delete);
- unlock_kernel();
+ mutex_unlock(&lcd_mutex);
return r;
}
/* save our object in the file's private structure */
file->private_data = dev;
- unlock_kernel();
+ mutex_unlock(&lcd_mutex);
return 0;
}
{
struct usb_lcd *dev;
- dev = (struct usb_lcd *)file->private_data;
+ dev = file->private_data;
if (dev == NULL)
return -ENODEV;
int retval = 0;
int bytes_read;
- dev = (struct usb_lcd *)file->private_data;
+ dev = file->private_data;
/* do a blocking bulk read to get data from the device */
retval = usb_bulk_msg(dev->udev,
u16 bcdDevice;
char buf[30];
- dev = (struct usb_lcd *)file->private_data;
+ dev = file->private_data;
if (dev == NULL)
return -ENODEV;
switch (cmd) {
case IOCTL_GET_HARD_VERSION:
- lock_kernel();
+ mutex_lock(&lcd_mutex);
bcdDevice = le16_to_cpu((dev->udev)->descriptor.bcdDevice);
sprintf(buf,"%1d%1d.%1d%1d",
(bcdDevice & 0xF000)>>12,
(bcdDevice & 0xF00)>>8,
(bcdDevice & 0xF0)>>4,
(bcdDevice & 0xF));
- unlock_kernel();
+ mutex_unlock(&lcd_mutex);
if (copy_to_user((void __user *)arg,buf,strlen(buf))!=0)
return -EFAULT;
break;
struct urb *urb = NULL;
char *buf = NULL;
- dev = (struct usb_lcd *)file->private_data;
+ dev = file->private_data;
/* verify that we actually have some data to write */
if (count == 0)
iso_out = e;
}
}
- if ((in && out) || (iso_in && iso_out))
+ if ((in && out) || iso_in || iso_out)
goto found;
}
return -EINVAL;
dev->in_iso_pipe = usb_rcvisocpipe (udev,
iso_in->desc.bEndpointAddress
& USB_ENDPOINT_NUMBER_MASK);
+ }
+
+ if (iso_out) {
dev->iso_out = &iso_out->desc;
dev->out_iso_pipe = usb_sndisocpipe (udev,
iso_out->desc.bEndpointAddress
break;
}
}
- simple_free_urb (urb);
ctx->pending--;
if (ctx->pending == 0) {
}
simple_free_urb (urbs [i]);
+ urbs[i] = NULL;
context.pending--;
context.submit_error = 1;
break;
wait_for_completion (&context.done);
+ for (i = 0; i < param->sglen; i++) {
+ if (urbs[i])
+ simple_free_urb(urbs[i]);
+ }
/*
* Isochronous transfers are expected to fail sometimes. As an
* arbitrary limit, we will report an error if any submissions
* off just killing the userspace task and waiting for it to exit.
*/
+/* No BKL needed */
static int
usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf)
{
.name = "usbtest",
.id_table = id_table,
.probe = usbtest_probe,
- .ioctl = usbtest_ioctl,
+ .unlocked_ioctl = usbtest_ioctl,
.disconnect = usbtest_disconnect,
.suspend = usbtest_suspend,
.resume = usbtest_resume,
size_t size;
int rc;
- lock_kernel();
mutex_lock(&mon_lock);
if ((mbus = mon_bus_lookup(iminor(inode))) == NULL) {
mutex_unlock(&mon_lock);
- unlock_kernel();
return -ENODEV;
}
if (mbus != &mon_bus0 && mbus->u_bus == NULL) {
printk(KERN_ERR TAG ": consistency error on open\n");
mutex_unlock(&mon_lock);
- unlock_kernel();
return -ENODEV;
}
file->private_data = rp;
mutex_unlock(&mon_lock);
- unlock_kernel();
return 0;
err_allocbuff:
kfree(rp);
err_alloc:
mutex_unlock(&mon_lock);
- unlock_kernel();
return rc;
}
/*
*/
-static int mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+static long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct mon_reader_bin *rp = file->private_data;
// struct mon_bus* mbus = rp->r.m_bus;
mutex_lock(&rp->fetch_lock);
spin_lock_irqsave(&rp->b_lock, flags);
- mon_free_buff(rp->b_vec, size/CHUNK_SIZE);
+ mon_free_buff(rp->b_vec, rp->b_size/CHUNK_SIZE);
kfree(rp->b_vec);
rp->b_vec = vec;
rp->b_size = size;
return ret;
}
-static long mon_bin_unlocked_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- int ret;
-
- lock_kernel();
- ret = mon_bin_ioctl(file, cmd, arg);
- unlock_kernel();
-
- return ret;
-}
-
-
#ifdef CONFIG_COMPAT
static long mon_bin_compat_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
.read = mon_bin_read,
/* .write = mon_text_write, */
.poll = mon_bin_poll,
- .unlocked_ioctl = mon_bin_unlocked_ioctl,
+ .unlocked_ioctl = mon_bin_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = mon_bin_compat_ioctl,
#endif
#ifdef CONFIG_USB_MUSB_HDRC_HCD
if (int_usb & MUSB_INTR_CONNECT) {
struct usb_hcd *hcd = musb_to_hcd(musb);
- void __iomem *mbase = musb->mregs;
handled = IRQ_HANDLED;
musb->is_active = 1;
if (is_peripheral_active(musb)) {
/* REVISIT HNP; just force disconnect */
}
- musb_writew(mbase, MUSB_INTRTXE, musb->epmask);
- musb_writew(mbase, MUSB_INTRRXE, musb->epmask & 0xfffe);
- musb_writeb(mbase, MUSB_INTRUSBE, 0xf7);
+ musb_writew(musb->mregs, MUSB_INTRTXE, musb->epmask);
+ musb_writew(musb->mregs, MUSB_INTRRXE, musb->epmask & 0xfffe);
+ musb_writeb(musb->mregs, MUSB_INTRUSBE, 0xf7);
#endif
musb->port1_status &= ~(USB_PORT_STAT_LOW_SPEED
|USB_PORT_STAT_HIGH_SPEED
{ "LS_EOF1", 0x7E, 8 },
{ "SOFT_RST", 0x7F, 8 },
{ "DMA_CNTLch0", 0x204, 16 },
- { "DMA_ADDRch0", 0x208, 16 },
- { "DMA_COUNTch0", 0x20C, 16 },
+ { "DMA_ADDRch0", 0x208, 32 },
+ { "DMA_COUNTch0", 0x20C, 32 },
{ "DMA_CNTLch1", 0x214, 16 },
- { "DMA_ADDRch1", 0x218, 16 },
- { "DMA_COUNTch1", 0x21C, 16 },
+ { "DMA_ADDRch1", 0x218, 32 },
+ { "DMA_COUNTch1", 0x21C, 32 },
{ "DMA_CNTLch2", 0x224, 16 },
- { "DMA_ADDRch2", 0x228, 16 },
- { "DMA_COUNTch2", 0x22C, 16 },
+ { "DMA_ADDRch2", 0x228, 32 },
+ { "DMA_COUNTch2", 0x22C, 32 },
{ "DMA_CNTLch3", 0x234, 16 },
- { "DMA_ADDRch3", 0x238, 16 },
- { "DMA_COUNTch3", 0x23C, 16 },
+ { "DMA_ADDRch3", 0x238, 32 },
+ { "DMA_COUNTch3", 0x23C, 32 },
{ "DMA_CNTLch4", 0x244, 16 },
- { "DMA_ADDRch4", 0x248, 16 },
- { "DMA_COUNTch4", 0x24C, 16 },
+ { "DMA_ADDRch4", 0x248, 32 },
+ { "DMA_COUNTch4", 0x24C, 32 },
{ "DMA_CNTLch5", 0x254, 16 },
- { "DMA_ADDRch5", 0x258, 16 },
- { "DMA_COUNTch5", 0x25C, 16 },
+ { "DMA_ADDRch5", 0x258, 32 },
+ { "DMA_COUNTch5", 0x25C, 32 },
{ "DMA_CNTLch6", 0x264, 16 },
- { "DMA_ADDRch6", 0x268, 16 },
- { "DMA_COUNTch6", 0x26C, 16 },
+ { "DMA_ADDRch6", 0x268, 32 },
+ { "DMA_COUNTch6", 0x26C, 32 },
{ "DMA_CNTLch7", 0x274, 16 },
- { "DMA_ADDRch7", 0x278, 16 },
- { "DMA_COUNTch7", 0x27C, 16 },
+ { "DMA_ADDRch7", 0x278, 32 },
+ { "DMA_COUNTch7", 0x27C, 32 },
{ } /* Terminating Entry */
};
musb->g.a_alt_hnp_support = 1;
break;
#endif
+ case USB_DEVICE_DEBUG_MODE:
+ handled = 0;
+ break;
stall:
default:
handled = -EINVAL;
spin_lock_irqsave(&musb->lock, flags);
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) {
+ if (unlikely(!HCD_HW_ACCESSIBLE(hcd))) {
spin_unlock_irqrestore(&musb->lock, flags);
return -ESHUTDOWN;
}
musb_channel->max_packet_sz = packet_sz;
channel->status = MUSB_DMA_STATUS_BUSY;
- if ((mode == 1) && (len >= packet_sz))
- configure_channel(channel, packet_sz, 1, dma_addr, len);
- else
- configure_channel(channel, packet_sz, 0, dma_addr, len);
+ configure_channel(channel, packet_sz, mode, dma_addr, len);
return true;
}
#include <linux/clk.h>
#include <linux/io.h>
-#include <plat/mux.h>
-
#include "musb_core.h"
#include "omap2430.h"
u32 l;
struct omap_musb_board_data *data = board_data;
-#if defined(CONFIG_ARCH_OMAP2430)
- omap_cfg_reg(AE5_2430_USB0HS_STP);
-#endif
-
/* We require some kind of external transceiver, hooked
* up through ULPI. TWL4030-family PMICs include one,
* which needs a driver, drivers aren't always needed.
Enable this to support ULPI connected USB OTG transceivers which
are likely found on embedded boards.
- The only chip currently supported is NXP's ISP1504
-
config TWL4030_USB
tristate "TWL4030 USB Transceiver Driver"
depends on TWL4030_CORE && REGULATOR_TWL4030
#define ULPI_ID(vendor, product) (((vendor) << 16) | (product))
-#define TR_FLAG(flags, a, b) (((flags) & a) ? b : 0)
-
/* ULPI hardcoded IDs, used for probing */
static unsigned int ulpi_ids[] = {
ULPI_ID(0x04cc, 0x1504), /* NXP ISP1504 */
+ ULPI_ID(0x0424, 0x0006), /* SMSC USB3319 */
};
-static int ulpi_set_flags(struct otg_transceiver *otg)
+static int ulpi_set_otg_flags(struct otg_transceiver *otg)
{
- unsigned int flags = 0;
+ unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN |
+ ULPI_OTG_CTRL_DM_PULLDOWN;
- if (otg->flags & USB_OTG_PULLUP_ID)
+ if (otg->flags & ULPI_OTG_ID_PULLUP)
flags |= ULPI_OTG_CTRL_ID_PULLUP;
- if (otg->flags & USB_OTG_PULLDOWN_DM)
- flags |= ULPI_OTG_CTRL_DM_PULLDOWN;
+ /*
+ * ULPI Specification rev.1.1 default
+ * for Dp/DmPulldown is enabled.
+ */
+ if (otg->flags & ULPI_OTG_DP_PULLDOWN_DIS)
+ flags &= ~ULPI_OTG_CTRL_DP_PULLDOWN;
- if (otg->flags & USB_OTG_PULLDOWN_DP)
- flags |= ULPI_OTG_CTRL_DP_PULLDOWN;
+ if (otg->flags & ULPI_OTG_DM_PULLDOWN_DIS)
+ flags &= ~ULPI_OTG_CTRL_DM_PULLDOWN;
- if (otg->flags & USB_OTG_EXT_VBUS_INDICATOR)
+ if (otg->flags & ULPI_OTG_EXTVBUSIND)
flags |= ULPI_OTG_CTRL_EXTVBUSIND;
- return otg_io_write(otg, flags, ULPI_SET(ULPI_OTG_CTRL));
+ return otg_io_write(otg, flags, ULPI_OTG_CTRL);
+}
+
+static int ulpi_set_fc_flags(struct otg_transceiver *otg)
+{
+ unsigned int flags = 0;
+
+ /*
+ * ULPI Specification rev.1.1 default
+ * for XcvrSelect is Full Speed.
+ */
+ if (otg->flags & ULPI_FC_HS)
+ flags |= ULPI_FUNC_CTRL_HIGH_SPEED;
+ else if (otg->flags & ULPI_FC_LS)
+ flags |= ULPI_FUNC_CTRL_LOW_SPEED;
+ else if (otg->flags & ULPI_FC_FS4LS)
+ flags |= ULPI_FUNC_CTRL_FS4LS;
+ else
+ flags |= ULPI_FUNC_CTRL_FULL_SPEED;
+
+ if (otg->flags & ULPI_FC_TERMSEL)
+ flags |= ULPI_FUNC_CTRL_TERMSELECT;
+
+ /*
+ * ULPI Specification rev.1.1 default
+ * for OpMode is Normal Operation.
+ */
+ if (otg->flags & ULPI_FC_OP_NODRV)
+ flags |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
+ else if (otg->flags & ULPI_FC_OP_DIS_NRZI)
+ flags |= ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI;
+ else if (otg->flags & ULPI_FC_OP_NSYNC_NEOP)
+ flags |= ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP;
+ else
+ flags |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
+
+ /*
+ * ULPI Specification rev.1.1 default
+ * for SuspendM is Powered.
+ */
+ flags |= ULPI_FUNC_CTRL_SUSPENDM;
+
+ return otg_io_write(otg, flags, ULPI_FUNC_CTRL);
+}
+
+static int ulpi_set_ic_flags(struct otg_transceiver *otg)
+{
+ unsigned int flags = 0;
+
+ if (otg->flags & ULPI_IC_AUTORESUME)
+ flags |= ULPI_IFC_CTRL_AUTORESUME;
+
+ if (otg->flags & ULPI_IC_EXTVBUS_INDINV)
+ flags |= ULPI_IFC_CTRL_EXTERNAL_VBUS;
+
+ if (otg->flags & ULPI_IC_IND_PASSTHRU)
+ flags |= ULPI_IFC_CTRL_PASSTHRU;
+
+ if (otg->flags & ULPI_IC_PROTECT_DIS)
+ flags |= ULPI_IFC_CTRL_PROTECT_IFC_DISABLE;
+
+ return otg_io_write(otg, flags, ULPI_IFC_CTRL);
+}
+
+static int ulpi_set_flags(struct otg_transceiver *otg)
+{
+ int ret;
+
+ ret = ulpi_set_otg_flags(otg);
+ if (ret)
+ return ret;
+
+ ret = ulpi_set_ic_flags(otg);
+ if (ret)
+ return ret;
+
+ return ulpi_set_fc_flags(otg);
}
static int ulpi_init(struct otg_transceiver *otg)
return -ENODEV;
}
+static int ulpi_set_host(struct otg_transceiver *otg, struct usb_bus *host)
+{
+ unsigned int flags = otg_io_read(otg, ULPI_IFC_CTRL);
+
+ if (!host) {
+ otg->host = NULL;
+ return 0;
+ }
+
+ otg->host = host;
+
+ flags &= ~(ULPI_IFC_CTRL_6_PIN_SERIAL_MODE |
+ ULPI_IFC_CTRL_3_PIN_SERIAL_MODE |
+ ULPI_IFC_CTRL_CARKITMODE);
+
+ if (otg->flags & ULPI_IC_6PIN_SERIAL)
+ flags |= ULPI_IFC_CTRL_6_PIN_SERIAL_MODE;
+ else if (otg->flags & ULPI_IC_3PIN_SERIAL)
+ flags |= ULPI_IFC_CTRL_3_PIN_SERIAL_MODE;
+ else if (otg->flags & ULPI_IC_CARKIT)
+ flags |= ULPI_IFC_CTRL_CARKITMODE;
+
+ return otg_io_write(otg, flags, ULPI_IFC_CTRL);
+}
+
static int ulpi_set_vbus(struct otg_transceiver *otg, bool on)
{
unsigned int flags = otg_io_read(otg, ULPI_OTG_CTRL);
flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT);
if (on) {
- if (otg->flags & USB_OTG_DRV_VBUS)
+ if (otg->flags & ULPI_OTG_DRVVBUS)
flags |= ULPI_OTG_CTRL_DRVVBUS;
- if (otg->flags & USB_OTG_DRV_VBUS_EXT)
+ if (otg->flags & ULPI_OTG_DRVVBUS_EXT)
flags |= ULPI_OTG_CTRL_DRVVBUS_EXT;
}
- return otg_io_write(otg, flags, ULPI_SET(ULPI_OTG_CTRL));
+ return otg_io_write(otg, flags, ULPI_OTG_CTRL);
}
struct otg_transceiver *
otg->flags = flags;
otg->io_ops = ops;
otg->init = ulpi_init;
+ otg->set_host = ulpi_set_host;
otg->set_vbus = ulpi_set_vbus;
return otg;
To compile this driver as a module, choose M here: the
module will be called zio.
+config USB_SERIAL_SSU100
+ tristate "USB Quatech SSU-100 Single Port Serial Driver"
+ help
+ Say Y here if you want to use the Quatech SSU-100 single
+ port usb to serial adapter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ssu100.
+
config USB_SERIAL_DEBUG
tristate "USB Debugging Device"
help
obj-$(CONFIG_USB_SERIAL_SIEMENS_MPI) += siemens_mpi.o
obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o
obj-$(CONFIG_USB_SERIAL_SPCP8X5) += spcp8x5.o
+obj-$(CONFIG_USB_SERIAL_SSU100) += ssu100.o
obj-$(CONFIG_USB_SERIAL_SYMBOL) += symbolserial.o
obj-$(CONFIG_USB_SERIAL_WWAN) += usb_wwan.o
obj-$(CONFIG_USB_SERIAL_TI) += ti_usb_3410_5052.o
{ USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */
{ USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */
{ USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */
+ { USB_DEVICE(0x16DC, 0x0010) }, /* W-IE-NE-R Plein & Baus GmbH PL512 Power Supply */
+ { USB_DEVICE(0x16DC, 0x0011) }, /* W-IE-NE-R Plein & Baus GmbH RCM Remote Control for MARATON Power Supply */
+ { USB_DEVICE(0x16DC, 0x0012) }, /* W-IE-NE-R Plein & Baus GmbH MPOD Multi Channel Power Supply */
+ { USB_DEVICE(0x16DC, 0x0015) }, /* W-IE-NE-R Plein & Baus GmbH CML Control, Monitoring and Data Logger */
{ } /* Terminating Entry */
};
static void digi_wakeup_write(struct usb_serial_port *port)
{
struct tty_struct *tty = tty_port_tty_get(&port->port);
- tty_wakeup(tty);
- tty_kref_put(tty);
+ if (tty) {
+ tty_wakeup(tty);
+ tty_kref_put(tty);
+ }
}
priv->dp_throttle_restart = 1;
/* receive data */
- if (opcode == DIGI_CMD_RECEIVE_DATA) {
+ if (tty && opcode == DIGI_CMD_RECEIVE_DATA) {
/* get flag from port_status */
flag = 0;
return -1;
tty = tty_port_tty_get(&port->port);
+
rts = 0;
- rts = tty->termios->c_cflag & CRTSCTS;
+ if (tty)
+ rts = tty->termios->c_cflag & CRTSCTS;
- if (opcode == DIGI_CMD_READ_INPUT_SIGNALS) {
+ if (tty && opcode == DIGI_CMD_READ_INPUT_SIGNALS) {
spin_lock(&priv->dp_port_lock);
/* convert from digi flags to termiox flags */
if (val & DIGI_READ_INPUT_SIGNALS_CTS) {
{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_5_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_6_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_7_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_USINT_CAT_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_USINT_WKEY_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_USINT_RS232_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ACTZWAVE_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_IRTRANS_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_IPLUS_PID) },
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SH4_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE(FTDI_VID, SEGWAY_RMP200_PID) },
{ }, /* Optional parameter entry */
{ } /* Terminating entry */
};
#define FTDI_NXTCAM_PID 0xABB8 /* NXTCam for Mindstorms NXT */
+/* US Interface Navigator (http://www.usinterface.com/) */
+#define FTDI_USINT_CAT_PID 0xb810 /* Navigator CAT and 2nd PTT lines */
+#define FTDI_USINT_WKEY_PID 0xb811 /* Navigator WKEY and FSK lines */
+#define FTDI_USINT_RS232_PID 0xb812 /* Navigator RS232 and CONFIG lines */
+
/* OOCDlink by Joern Kaipf <joernk@web.de>
* (http://www.joernonline.de/dw/doku.php?id=start&idx=projects:oocdlink) */
#define FTDI_OOCDLINK_PID 0xbaf8 /* Amontec JTAGkey */
#define XVERVE_SIGNALYZER_SH2_PID 0xBCA2
#define XVERVE_SIGNALYZER_SH4_PID 0xBCA4
+/*
+ * Segway Robotic Mobility Platform USB interface (using VID 0x0403)
+ * Submitted by John G. Rogers
+ */
+#define SEGWAY_RMP200_PID 0xe729
urb->transfer_buffer_length = count;
usb_serial_debug_data(debug, &port->dev, __func__, count,
urb->transfer_buffer);
+ spin_lock_irqsave(&port->lock, flags);
+ port->tx_bytes += count;
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ clear_bit(i, &port->write_urbs_free);
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result) {
dev_err(&port->dev, "%s - error submitting urb: %d\n",
__func__, result);
+ set_bit(i, &port->write_urbs_free);
+ spin_lock_irqsave(&port->lock, flags);
+ port->tx_bytes -= count;
+ spin_unlock_irqrestore(&port->lock, flags);
+
clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
return result;
}
- clear_bit(i, &port->write_urbs_free);
-
- spin_lock_irqsave(&port->lock, flags);
- port->tx_bytes += count;
- spin_unlock_irqrestore(&port->lock, flags);
/* Try sending off another urb, unless in irq context (in which case
* there will be no free urb). */
kfree(header);
kfree(rom_desc);
kfree(ti_manuf_desc);
- return status;
+ return -EINVAL;
}
/* verify the write -- must do this in order for
kfree(header);
kfree(rom_desc);
kfree(ti_manuf_desc);
- return status;
+ return -EINVAL;
}
kfree(vheader);
{ USB_DEVICE(0x413C, 0x4009) }, /* Dell Axim USB Sync */
{ USB_DEVICE(0x4505, 0x0010) }, /* Smartphone */
{ USB_DEVICE(0x5E04, 0xCE00) }, /* SAGEM Wireless Assistant */
- { USB_DEVICE(0x0BB4, 0x00CF) }, /* HTC smartphone modems */
{ } /* Terminating entry */
};
/*
* Infinity Unlimited USB Phoenix driver
*
+ * Copyright (C) 2010 James Courtier-Dutton (James@superbug.co.uk)
+
* Copyright (C) 2007 Alain Degreffe (eczema@ecze.com)
*
* Original code taken from iuutool (Copyright (C) 2006 Juan Carlos Borrás)
/*
* Version Information
*/
-#define DRIVER_VERSION "v0.11"
+#define DRIVER_VERSION "v0.12"
#define DRIVER_DESC "Infinity USB Unlimited Phoenix driver"
static const struct usb_device_id id_table[] = {
u8 *dbgbuf; /* debug buffer */
u8 len;
int vcc; /* vcc (either 3 or 5 V) */
+ u32 baud;
+ u32 boost;
+ u32 clk;
};
port->number, set, clear);
spin_lock_irqsave(&priv->lock, flags);
- if (set & TIOCM_RTS)
- priv->tiostatus = TIOCM_RTS;
- if (!(set & TIOCM_RTS) && priv->tiostatus == TIOCM_RTS) {
+ if ((set & TIOCM_RTS) && !(priv->tiostatus == TIOCM_RTS)) {
dbg("%s TIOCMSET RESET called !!!", __func__);
priv->reset = 1;
}
+ if (set & TIOCM_RTS)
+ priv->tiostatus = TIOCM_RTS;
+
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
return status;
}
-static int iuu_uart_baud(struct usb_serial_port *port, u32 baud,
+static int iuu_uart_baud(struct usb_serial_port *port, u32 baud_base,
u32 *actual, u8 parity)
{
int status;
+ u32 baud;
u8 *dataout;
u8 DataCount = 0;
u8 T1Frekvens = 0;
u8 T1reload = 0;
unsigned int T1FrekvensHZ = 0;
+ dbg("%s - enter baud_base=%d", __func__, baud_base);
dataout = kmalloc(sizeof(u8) * 5, GFP_KERNEL);
if (!dataout)
return -ENOMEM;
+ /*baud = (((priv->clk / 35) * baud_base) / 100000); */
+ baud = baud_base;
if (baud < 1200 || baud > 230400) {
kfree(dataout);
struct usb_serial_port *port, struct ktermios *old_termios)
{
const u32 supported_mask = CMSPAR|PARENB|PARODD;
-
+ struct iuu_private *priv = usb_get_serial_port_data(port);
unsigned int cflag = tty->termios->c_cflag;
int status;
u32 actual;
u32 parity;
int csize = CS7;
- int baud = 9600; /* Fixed for the moment */
+ int baud;
u32 newval = cflag & supported_mask;
+ /* Just use the ospeed. ispeed should be the same. */
+ baud = tty->termios->c_ospeed;
+
+ dbg("%s - enter c_ospeed or baud=%d", __func__, baud);
+
/* compute the parity parameter */
parity = 0;
if (cflag & CMSPAR) { /* Using mark space */
/* set it */
status = iuu_uart_baud(port,
- (clockmode == 2) ? 16457 : 9600 * boost / 100,
+ baud * priv->boost / 100,
&actual, parity);
/* set the termios value to the real one, so the user now what has
* changed. We support few fields so its easies to copy the old hw
* settings back over and then adjust them
*/
- if (old_termios)
- tty_termios_copy_hw(tty->termios, old_termios);
+ if (old_termios)
+ tty_termios_copy_hw(tty->termios, old_termios);
if (status != 0) /* Set failed - return old bits */
return;
/* Re-encode speed, parity and csize */
static void iuu_init_termios(struct tty_struct *tty)
{
+ dbg("%s - enter", __func__);
*(tty->termios) = tty_std_termios;
tty->termios->c_cflag = CLOCAL | CREAD | CS8 | B9600
| TIOCM_CTS | CSTOPB | PARENB;
struct usb_serial *serial = port->serial;
u8 *buf;
int result;
+ int baud;
u32 actual;
struct iuu_private *priv = usb_get_serial_port_data(port);
- dbg("%s - port %d", __func__, port->number);
+ baud = tty->termios->c_ospeed;
+ tty->termios->c_ispeed = baud;
+ /* Re-encode speed */
+ tty_encode_baud_rate(tty, baud, baud);
+
+ dbg("%s - port %d, baud %d", __func__, port->number, baud);
usb_clear_halt(serial->dev, port->write_urb->pipe);
usb_clear_halt(serial->dev, port->read_urb->pipe);
iuu_uart_on(port);
if (boost < 100)
boost = 100;
+ priv->boost = boost;
+ priv->baud = baud;
switch (clockmode) {
case 2: /* 3.680 Mhz */
+ priv->clk = IUU_CLK_3680000;
iuu_clk(port, IUU_CLK_3680000 * boost / 100);
result =
- iuu_uart_baud(port, 9600 * boost / 100, &actual,
+ iuu_uart_baud(port, baud * boost / 100, &actual,
IUU_PARITY_EVEN);
break;
case 3: /* 6.00 Mhz */
iuu_clk(port, IUU_CLK_6000000 * boost / 100);
+ priv->clk = IUU_CLK_6000000;
+ /* Ratio of 6000000 to 3500000 for baud 9600 */
result =
iuu_uart_baud(port, 16457 * boost / 100, &actual,
IUU_PARITY_EVEN);
break;
default: /* 3.579 Mhz */
iuu_clk(port, IUU_CLK_3579000 * boost / 100);
+ priv->clk = IUU_CLK_3579000;
result =
- iuu_uart_baud(port, 9600 * boost / 100, &actual,
+ iuu_uart_baud(port, baud * boost / 100, &actual,
IUU_PARITY_EVEN);
}
#define HUAWEI_PRODUCT_E143D 0x143D
#define HUAWEI_PRODUCT_E143E 0x143E
#define HUAWEI_PRODUCT_E143F 0x143F
+#define HUAWEI_PRODUCT_K4505 0x1464
+#define HUAWEI_PRODUCT_K3765 0x1465
#define HUAWEI_PRODUCT_E14AC 0x14AC
+#define HUAWEI_PRODUCT_ETS1220 0x1803
#define QUANTA_VENDOR_ID 0x0408
#define QUANTA_PRODUCT_Q101 0xEA02
#define BANDRICH_PRODUCT_1011 0x1011
#define BANDRICH_PRODUCT_1012 0x1012
-#define AMOI_VENDOR_ID 0x1614
-#define AMOI_PRODUCT_9508 0x0800
-
#define QUALCOMM_VENDOR_ID 0x05C6
#define CMOTECH_VENDOR_ID 0x16d8
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143D, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143E, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143F, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ETS1220, 0xff, 0xff, 0xff) },
{ USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E14AC) },
- { USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_9508) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) }, /* Novatel Merlin V640/XV620 */
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V620) }, /* Novatel Merlin V620/S620 */
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V740) }, /* Novatel Merlin EX720/V740/X720 */
serial->interface->cur_altsetting->desc.bInterfaceClass != 0xff)
return -ENODEV;
+ /* Don't bind network interfaces on Huawei K3765 & K4505 */
+ if (serial->dev->descriptor.idVendor == HUAWEI_VENDOR_ID &&
+ (serial->dev->descriptor.idProduct == HUAWEI_PRODUCT_K3765 ||
+ serial->dev->descriptor.idProduct == HUAWEI_PRODUCT_K4505) &&
+ serial->interface->cur_altsetting->desc.bInterfaceNumber == 1)
+ return -ENODEV;
+
data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL);
if (!data)
--- /dev/null
+/*
+ * usb-serial driver for Quatech SSU-100
+ *
+ * based on ftdi_sio.c and the original serqt_usb.c from Quatech
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/uaccess.h>
+
+#define QT_OPEN_CLOSE_CHANNEL 0xca
+#define QT_SET_GET_DEVICE 0xc2
+#define QT_SET_GET_REGISTER 0xc0
+#define QT_GET_SET_PREBUF_TRIG_LVL 0xcc
+#define QT_SET_ATF 0xcd
+#define QT_GET_SET_UART 0xc1
+#define QT_TRANSFER_IN 0xc0
+#define QT_HW_FLOW_CONTROL_MASK 0xc5
+#define QT_SW_FLOW_CONTROL_MASK 0xc6
+
+#define MODEM_CTL_REGISTER 0x04
+#define MODEM_STATUS_REGISTER 0x06
+
+
+#define SERIAL_LSR_OE 0x02
+#define SERIAL_LSR_PE 0x04
+#define SERIAL_LSR_FE 0x08
+#define SERIAL_LSR_BI 0x10
+
+#define SERIAL_LSR_TEMT 0x40
+
+#define SERIAL_MCR_DTR 0x01
+#define SERIAL_MCR_RTS 0x02
+#define SERIAL_MCR_LOOP 0x10
+
+#define SERIAL_MSR_CTS 0x10
+#define SERIAL_MSR_CD 0x80
+#define SERIAL_MSR_RI 0x40
+#define SERIAL_MSR_DSR 0x20
+#define SERIAL_MSR_MASK 0xf0
+
+#define SERIAL_CRTSCTS ((SERIAL_MCR_RTS << 8) | SERIAL_MSR_CTS)
+
+#define SERIAL_8_DATA 0x03
+#define SERIAL_7_DATA 0x02
+#define SERIAL_6_DATA 0x01
+#define SERIAL_5_DATA 0x00
+
+#define SERIAL_ODD_PARITY 0X08
+#define SERIAL_EVEN_PARITY 0X18
+
+#define MAX_BAUD_RATE 460800
+
+#define ATC_DISABLED 0x00
+#define DUPMODE_BITS 0xc0
+#define RR_BITS 0x03
+#define LOOPMODE_BITS 0x41
+#define RS232_MODE 0x00
+#define RTSCTS_TO_CONNECTOR 0x40
+#define CLKS_X4 0x02
+#define FULLPWRBIT 0x00000080
+#define NEXT_BOARD_POWER_BIT 0x00000004
+
+static int debug = 1;
+
+/* Version Information */
+#define DRIVER_VERSION "v0.1"
+#define DRIVER_DESC "Quatech SSU-100 USB to Serial Driver"
+
+#define USB_VENDOR_ID_QUATECH 0x061d /* Quatech VID */
+#define QUATECH_SSU100 0xC020 /* SSU100 */
+
+static const struct usb_device_id id_table[] = {
+ {USB_DEVICE(USB_VENDOR_ID_QUATECH, QUATECH_SSU100)},
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+
+static struct usb_driver ssu100_driver = {
+ .name = "ssu100",
+ .probe = usb_serial_probe,
+ .disconnect = usb_serial_disconnect,
+ .id_table = id_table,
+ .suspend = usb_serial_suspend,
+ .resume = usb_serial_resume,
+ .no_dynamic_id = 1,
+ .supports_autosuspend = 1,
+};
+
+struct ssu100_port_private {
+ u8 shadowLSR;
+ u8 shadowMSR;
+ wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
+ unsigned short max_packet_size;
+};
+
+static void ssu100_release(struct usb_serial *serial)
+{
+ struct ssu100_port_private *priv = usb_get_serial_port_data(*serial->port);
+
+ dbg("%s", __func__);
+ kfree(priv);
+}
+
+static inline int ssu100_control_msg(struct usb_device *dev,
+ u8 request, u16 data, u16 index)
+{
+ return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ request, 0x40, data, index,
+ NULL, 0, 300);
+}
+
+static inline int ssu100_setdevice(struct usb_device *dev, u8 *data)
+{
+ u16 x = ((u16)(data[1] << 8) | (u16)(data[0]));
+
+ return ssu100_control_msg(dev, QT_SET_GET_DEVICE, x, 0);
+}
+
+
+static inline int ssu100_getdevice(struct usb_device *dev, u8 *data)
+{
+ return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ QT_SET_GET_DEVICE, 0xc0, 0, 0,
+ data, 3, 300);
+}
+
+static inline int ssu100_getregister(struct usb_device *dev,
+ unsigned short uart,
+ unsigned short reg,
+ u8 *data)
+{
+ return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ QT_SET_GET_REGISTER, 0xc0, reg,
+ uart, data, sizeof(*data), 300);
+
+}
+
+
+static inline int ssu100_setregister(struct usb_device *dev,
+ unsigned short uart,
+ u16 data)
+{
+ u16 value = (data << 8) | MODEM_CTL_REGISTER;
+
+ return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ QT_SET_GET_REGISTER, 0x40, value, uart,
+ NULL, 0, 300);
+
+}
+
+#define set_mctrl(dev, set) update_mctrl((dev), (set), 0)
+#define clear_mctrl(dev, clear) update_mctrl((dev), 0, (clear))
+
+/* these do not deal with device that have more than 1 port */
+static inline int update_mctrl(struct usb_device *dev, unsigned int set,
+ unsigned int clear)
+{
+ unsigned urb_value;
+ int result;
+
+ if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0) {
+ dbg("%s - DTR|RTS not being set|cleared", __func__);
+ return 0; /* no change */
+ }
+
+ clear &= ~set; /* 'set' takes precedence over 'clear' */
+ urb_value = 0;
+ if (set & TIOCM_DTR)
+ urb_value |= SERIAL_MCR_DTR;
+ if (set & TIOCM_RTS)
+ urb_value |= SERIAL_MCR_RTS;
+
+ result = ssu100_setregister(dev, 0, urb_value);
+ if (result < 0)
+ dbg("%s Error from MODEM_CTRL urb", __func__);
+
+ return result;
+}
+
+static int ssu100_initdevice(struct usb_device *dev)
+{
+ u8 *data;
+ int result = 0;
+
+ dbg("%s", __func__);
+
+ data = kzalloc(3, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ result = ssu100_getdevice(dev, data);
+ if (result < 0) {
+ dbg("%s - get_device failed %i", __func__, result);
+ goto out;
+ }
+
+ data[1] &= ~FULLPWRBIT;
+
+ result = ssu100_setdevice(dev, data);
+ if (result < 0) {
+ dbg("%s - setdevice failed %i", __func__, result);
+ goto out;
+ }
+
+ result = ssu100_control_msg(dev, QT_GET_SET_PREBUF_TRIG_LVL, 128, 0);
+ if (result < 0) {
+ dbg("%s - set prebuffer level failed %i", __func__, result);
+ goto out;
+ }
+
+ result = ssu100_control_msg(dev, QT_SET_ATF, ATC_DISABLED, 0);
+ if (result < 0) {
+ dbg("%s - set ATFprebuffer level failed %i", __func__, result);
+ goto out;
+ }
+
+ result = ssu100_getdevice(dev, data);
+ if (result < 0) {
+ dbg("%s - get_device failed %i", __func__, result);
+ goto out;
+ }
+
+ data[0] &= ~(RR_BITS | DUPMODE_BITS);
+ data[0] |= CLKS_X4;
+ data[1] &= ~(LOOPMODE_BITS);
+ data[1] |= RS232_MODE;
+
+ result = ssu100_setdevice(dev, data);
+ if (result < 0) {
+ dbg("%s - setdevice failed %i", __func__, result);
+ goto out;
+ }
+
+out: kfree(data);
+ return result;
+
+}
+
+
+static void ssu100_set_termios(struct tty_struct *tty,
+ struct usb_serial_port *port,
+ struct ktermios *old_termios)
+{
+ struct usb_device *dev = port->serial->dev;
+ struct ktermios *termios = tty->termios;
+ u16 baud, divisor, remainder;
+ unsigned int cflag = termios->c_cflag;
+ u16 urb_value = 0; /* will hold the new flags */
+ int result;
+
+ dbg("%s", __func__);
+
+ if (cflag & PARENB) {
+ if (cflag & PARODD)
+ urb_value |= SERIAL_ODD_PARITY;
+ else
+ urb_value |= SERIAL_EVEN_PARITY;
+ }
+
+ switch (cflag & CSIZE) {
+ case CS5:
+ urb_value |= SERIAL_5_DATA;
+ break;
+ case CS6:
+ urb_value |= SERIAL_6_DATA;
+ break;
+ case CS7:
+ urb_value |= SERIAL_7_DATA;
+ break;
+ default:
+ case CS8:
+ urb_value |= SERIAL_8_DATA;
+ break;
+ }
+
+ baud = tty_get_baud_rate(tty);
+ if (!baud)
+ baud = 9600;
+
+ dbg("%s - got baud = %d\n", __func__, baud);
+
+
+ divisor = MAX_BAUD_RATE / baud;
+ remainder = MAX_BAUD_RATE % baud;
+ if (((remainder * 2) >= baud) && (baud != 110))
+ divisor++;
+
+ urb_value = urb_value << 8;
+
+ result = ssu100_control_msg(dev, QT_GET_SET_UART, divisor, urb_value);
+ if (result < 0)
+ dbg("%s - set uart failed", __func__);
+
+ if (cflag & CRTSCTS)
+ result = ssu100_control_msg(dev, QT_HW_FLOW_CONTROL_MASK,
+ SERIAL_CRTSCTS, 0);
+ else
+ result = ssu100_control_msg(dev, QT_HW_FLOW_CONTROL_MASK,
+ 0, 0);
+ if (result < 0)
+ dbg("%s - set HW flow control failed", __func__);
+
+ if (I_IXOFF(tty) || I_IXON(tty)) {
+ u16 x = ((u16)(START_CHAR(tty) << 8) | (u16)(STOP_CHAR(tty)));
+
+ result = ssu100_control_msg(dev, QT_SW_FLOW_CONTROL_MASK,
+ x, 0);
+ } else
+ result = ssu100_control_msg(dev, QT_SW_FLOW_CONTROL_MASK,
+ 0, 0);
+
+ if (result < 0)
+ dbg("%s - set SW flow control failed", __func__);
+
+}
+
+
+static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+ struct usb_device *dev = port->serial->dev;
+ struct ssu100_port_private *priv = usb_get_serial_port_data(port);
+ u8 *data;
+ int result;
+
+ dbg("%s - port %d", __func__, port->number);
+
+ data = kzalloc(2, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ QT_OPEN_CLOSE_CHANNEL,
+ QT_TRANSFER_IN, 0x01,
+ 0, data, 2, 300);
+ if (result < 0) {
+ dbg("%s - open failed %i", __func__, result);
+ kfree(data);
+ return result;
+ }
+
+ priv->shadowLSR = data[0] & (SERIAL_LSR_OE | SERIAL_LSR_PE |
+ SERIAL_LSR_FE | SERIAL_LSR_BI);
+
+ priv->shadowMSR = data[1] & (SERIAL_MSR_CTS | SERIAL_MSR_DSR |
+ SERIAL_MSR_RI | SERIAL_MSR_CD);
+
+ kfree(data);
+
+/* set to 9600 */
+ result = ssu100_control_msg(dev, QT_GET_SET_UART, 0x30, 0x0300);
+ if (result < 0)
+ dbg("%s - set uart failed", __func__);
+
+ if (tty)
+ ssu100_set_termios(tty, port, tty->termios);
+
+ return usb_serial_generic_open(tty, port);
+}
+
+static void ssu100_close(struct usb_serial_port *port)
+{
+ dbg("%s", __func__);
+ usb_serial_generic_close(port);
+}
+
+static int get_serial_info(struct usb_serial_port *port,
+ struct serial_struct __user *retinfo)
+{
+ struct serial_struct tmp;
+
+ if (!retinfo)
+ return -EFAULT;
+
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.line = port->serial->minor;
+ tmp.port = 0;
+ tmp.irq = 0;
+ tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
+ tmp.xmit_fifo_size = port->bulk_out_size;
+ tmp.baud_base = 9600;
+ tmp.close_delay = 5*HZ;
+ tmp.closing_wait = 30*HZ;
+
+ if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+ return -EFAULT;
+ return 0;
+}
+
+static int ssu100_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct ssu100_port_private *priv = usb_get_serial_port_data(port);
+
+ dbg("%s cmd 0x%04x", __func__, cmd);
+
+ switch (cmd) {
+ case TIOCGSERIAL:
+ return get_serial_info(port,
+ (struct serial_struct __user *) arg);
+
+ case TIOCMIWAIT:
+ while (priv != NULL) {
+ u8 prevMSR = priv->shadowMSR & SERIAL_MSR_MASK;
+ interruptible_sleep_on(&priv->delta_msr_wait);
+ /* see if a signal did it */
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ else {
+ u8 diff = (priv->shadowMSR & SERIAL_MSR_MASK) ^ prevMSR;
+ if (!diff)
+ return -EIO; /* no change => error */
+
+ /* Return 0 if caller wanted to know about
+ these bits */
+
+ if (((arg & TIOCM_RNG) && (diff & SERIAL_MSR_RI)) ||
+ ((arg & TIOCM_DSR) && (diff & SERIAL_MSR_DSR)) ||
+ ((arg & TIOCM_CD) && (diff & SERIAL_MSR_CD)) ||
+ ((arg & TIOCM_CTS) && (diff & SERIAL_MSR_CTS)))
+ return 0;
+ }
+ }
+ return 0;
+
+ default:
+ break;
+ }
+
+ dbg("%s arg not supported", __func__);
+
+ return -ENOIOCTLCMD;
+}
+
+static void ssu100_set_max_packet_size(struct usb_serial_port *port)
+{
+ struct ssu100_port_private *priv = usb_get_serial_port_data(port);
+ struct usb_serial *serial = port->serial;
+ struct usb_device *udev = serial->dev;
+
+ struct usb_interface *interface = serial->interface;
+ struct usb_endpoint_descriptor *ep_desc = &interface->cur_altsetting->endpoint[1].desc;
+
+ unsigned num_endpoints;
+ int i;
+
+ num_endpoints = interface->cur_altsetting->desc.bNumEndpoints;
+ dev_info(&udev->dev, "Number of endpoints %d\n", num_endpoints);
+
+ for (i = 0; i < num_endpoints; i++) {
+ dev_info(&udev->dev, "Endpoint %d MaxPacketSize %d\n", i+1,
+ interface->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
+ ep_desc = &interface->cur_altsetting->endpoint[i].desc;
+ }
+
+ /* set max packet size based on descriptor */
+ priv->max_packet_size = ep_desc->wMaxPacketSize;
+
+ dev_info(&udev->dev, "Setting MaxPacketSize %d\n", priv->max_packet_size);
+}
+
+static int ssu100_attach(struct usb_serial *serial)
+{
+ struct ssu100_port_private *priv;
+ struct usb_serial_port *port = *serial->port;
+
+ dbg("%s", __func__);
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&port->dev, "%s- kmalloc(%Zd) failed.\n", __func__,
+ sizeof(*priv));
+ return -ENOMEM;
+ }
+
+ init_waitqueue_head(&priv->delta_msr_wait);
+ usb_set_serial_port_data(port, priv);
+
+ ssu100_set_max_packet_size(port);
+
+ return ssu100_initdevice(serial->dev);
+}
+
+static int ssu100_tiocmget(struct tty_struct *tty, struct file *file)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct usb_device *dev = port->serial->dev;
+ u8 *d;
+ int r;
+
+ dbg("%s\n", __func__);
+
+ d = kzalloc(2, GFP_KERNEL);
+ if (!d)
+ return -ENOMEM;
+
+ r = ssu100_getregister(dev, 0, MODEM_CTL_REGISTER, d);
+ if (r < 0)
+ goto mget_out;
+
+ r = ssu100_getregister(dev, 0, MODEM_STATUS_REGISTER, d+1);
+ if (r < 0)
+ goto mget_out;
+
+ r = (d[0] & SERIAL_MCR_DTR ? TIOCM_DTR : 0) |
+ (d[0] & SERIAL_MCR_RTS ? TIOCM_RTS : 0) |
+ (d[1] & SERIAL_MSR_CTS ? TIOCM_CTS : 0) |
+ (d[1] & SERIAL_MSR_CD ? TIOCM_CAR : 0) |
+ (d[1] & SERIAL_MSR_RI ? TIOCM_RI : 0) |
+ (d[1] & SERIAL_MSR_DSR ? TIOCM_DSR : 0);
+
+mget_out:
+ kfree(d);
+ return r;
+}
+
+static int ssu100_tiocmset(struct tty_struct *tty, struct file *file,
+ unsigned int set, unsigned int clear)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ struct usb_device *dev = port->serial->dev;
+
+ dbg("%s\n", __func__);
+ return update_mctrl(dev, set, clear);
+}
+
+static void ssu100_dtr_rts(struct usb_serial_port *port, int on)
+{
+ struct usb_device *dev = port->serial->dev;
+
+ dbg("%s\n", __func__);
+
+ mutex_lock(&port->serial->disc_mutex);
+ if (!port->serial->disconnected) {
+ /* Disable flow control */
+ if (!on &&
+ ssu100_setregister(dev, 0, 0) < 0)
+ dev_err(&port->dev, "error from flowcontrol urb\n");
+ /* drop RTS and DTR */
+ if (on)
+ set_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
+ else
+ clear_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
+ }
+ mutex_unlock(&port->serial->disc_mutex);
+}
+
+static int ssu100_process_packet(struct tty_struct *tty,
+ struct usb_serial_port *port,
+ struct ssu100_port_private *priv,
+ char *packet, int len)
+{
+ int i;
+ char flag;
+ char *ch;
+
+ dbg("%s - port %d", __func__, port->number);
+
+ if (len < 4) {
+ dbg("%s - malformed packet", __func__);
+ return 0;
+ }
+
+ if ((packet[0] == 0x1b) && (packet[1] == 0x1b) &&
+ ((packet[2] == 0x00) || (packet[2] == 0x01))) {
+ if (packet[2] == 0x00)
+ priv->shadowLSR = packet[3] & (SERIAL_LSR_OE |
+ SERIAL_LSR_PE |
+ SERIAL_LSR_FE |
+ SERIAL_LSR_BI);
+
+ if (packet[2] == 0x01) {
+ priv->shadowMSR = packet[3];
+ wake_up_interruptible(&priv->delta_msr_wait);
+ }
+
+ len -= 4;
+ ch = packet + 4;
+ } else
+ ch = packet;
+
+ if (!len)
+ return 0; /* status only */
+
+ if (port->port.console && port->sysrq) {
+ for (i = 0; i < len; i++, ch++) {
+ if (!usb_serial_handle_sysrq_char(tty, port, *ch))
+ tty_insert_flip_char(tty, *ch, flag);
+ }
+ } else
+ tty_insert_flip_string_fixed_flag(tty, ch, flag, len);
+
+ return len;
+}
+
+static void ssu100_process_read_urb(struct urb *urb)
+{
+ struct usb_serial_port *port = urb->context;
+ struct ssu100_port_private *priv = usb_get_serial_port_data(port);
+ char *data = (char *)urb->transfer_buffer;
+ struct tty_struct *tty;
+ int count = 0;
+ int i;
+ int len;
+
+ dbg("%s", __func__);
+
+ tty = tty_port_tty_get(&port->port);
+ if (!tty)
+ return;
+
+ for (i = 0; i < urb->actual_length; i += priv->max_packet_size) {
+ len = min_t(int, urb->actual_length - i, priv->max_packet_size);
+ count += ssu100_process_packet(tty, port, priv, &data[i], len);
+ }
+
+ if (count)
+ tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
+}
+
+
+static struct usb_serial_driver ssu100_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ssu100",
+ },
+ .description = DRIVER_DESC,
+ .id_table = id_table,
+ .usb_driver = &ssu100_driver,
+ .num_ports = 1,
+ .bulk_in_size = 256,
+ .bulk_out_size = 256,
+ .open = ssu100_open,
+ .close = ssu100_close,
+ .attach = ssu100_attach,
+ .release = ssu100_release,
+ .dtr_rts = ssu100_dtr_rts,
+ .process_read_urb = ssu100_process_read_urb,
+ .tiocmget = ssu100_tiocmget,
+ .tiocmset = ssu100_tiocmset,
+ .ioctl = ssu100_ioctl,
+ .set_termios = ssu100_set_termios,
+};
+
+static int __init ssu100_init(void)
+{
+ int retval;
+
+ dbg("%s", __func__);
+
+ /* register with usb-serial */
+ retval = usb_serial_register(&ssu100_device);
+
+ if (retval)
+ goto failed_usb_sio_register;
+
+ retval = usb_register(&ssu100_driver);
+ if (retval)
+ goto failed_usb_register;
+
+ printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+ DRIVER_DESC "\n");
+
+ return 0;
+
+failed_usb_register:
+ usb_serial_deregister(&ssu100_device);
+failed_usb_sio_register:
+ return retval;
+}
+
+static void __exit ssu100_exit(void)
+{
+ usb_deregister(&ssu100_driver);
+ usb_serial_deregister(&ssu100_device);
+}
+
+module_init(ssu100_init);
+module_exit(ssu100_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
return id;
}
+/* Caller must hold table_lock */
static struct usb_serial_driver *search_serial_device(
struct usb_interface *iface)
{
int num_ports = 0;
int max_endpoints;
- lock_kernel(); /* guard against unloading a serial driver module */
+ mutex_lock(&table_lock);
type = search_serial_device(interface);
if (!type) {
- unlock_kernel();
+ mutex_unlock(&table_lock);
dbg("none matched");
return -ENODEV;
}
+ if (!try_module_get(type->driver.owner)) {
+ mutex_unlock(&table_lock);
+ dev_err(&interface->dev, "module get failed, exiting\n");
+ return -EIO;
+ }
+ mutex_unlock(&table_lock);
+
serial = create_serial(dev, interface, type);
if (!serial) {
- unlock_kernel();
dev_err(&interface->dev, "%s - out of memory\n", __func__);
return -ENOMEM;
}
if (type->probe) {
const struct usb_device_id *id;
- if (!try_module_get(type->driver.owner)) {
- unlock_kernel();
- dev_err(&interface->dev,
- "module get failed, exiting\n");
- kfree(serial);
- return -EIO;
- }
-
id = get_iface_id(type, interface);
retval = type->probe(serial, id);
module_put(type->driver.owner);
if (retval) {
- unlock_kernel();
dbg("sub driver rejected device");
kfree(serial);
return retval;
* properly during a later invocation of usb_serial_probe
*/
if (num_bulk_in == 0 || num_bulk_out == 0) {
- unlock_kernel();
dev_info(&interface->dev, "PL-2303 hack: descriptors matched but endpoints did not\n");
kfree(serial);
return -ENODEV;
if (type == &usb_serial_generic_device) {
num_ports = num_bulk_out;
if (num_ports == 0) {
- unlock_kernel();
dev_err(&interface->dev,
"Generic device with no bulk out, not allowed.\n");
kfree(serial);
/* if this device type has a calc_num_ports function, call it */
if (type->calc_num_ports) {
if (!try_module_get(type->driver.owner)) {
- unlock_kernel();
dev_err(&interface->dev,
"module get failed, exiting\n");
kfree(serial);
max_endpoints = max(max_endpoints, num_interrupt_out);
max_endpoints = max(max_endpoints, (int)serial->num_ports);
serial->num_port_pointers = max_endpoints;
- unlock_kernel();
dbg("%s - setting up %d port structures for this device",
__func__, max_endpoints);
dev_set_name(&port->dev, "ttyUSB%d", port->number);
dbg ("%s - registering %s", __func__, dev_name(&port->dev));
port->dev_state = PORT_REGISTERING;
+ device_enable_async_suspend(&port->dev);
+
retval = device_add(&port->dev);
if (retval) {
dev_err(&port->dev, "Error registering port device, "
driver->description = driver->driver.name;
/* Add this device to our list of devices */
+ mutex_lock(&table_lock);
list_add(&driver->driver_list, &usb_serial_driver_list);
retval = usb_serial_bus_register(driver);
printk(KERN_INFO "USB Serial support registered for %s\n",
driver->description);
+ mutex_unlock(&table_lock);
return retval;
}
EXPORT_SYMBOL_GPL(usb_serial_register);
/* must be called with BKL held */
printk(KERN_INFO "USB Serial deregistering driver %s\n",
device->description);
+ mutex_lock(&table_lock);
list_del(&device->driver_list);
usb_serial_bus_deregister(device);
+ mutex_unlock(&table_lock);
}
EXPORT_SYMBOL_GPL(usb_serial_deregister);
/* The firmware will time-out commands after 20 seconds. Some commands
* can legitimately take longer than this, so we use a different
* command that only waits for the interrupt and then sends status,
- * without having to send a new ATAPI command to the device.
+ * without having to send a new ATAPI command to the device.
*
* NOTE: There is some indication that a data transfer after a timeout
* may not work, but that is a condition that should never happen.
/* Find the length we desire to read. */
switch (srb->cmnd[0]) {
- case INQUIRY:
- case REQUEST_SENSE: /* 16 or 18 bytes? spec says 18, lots of devices only have 16 */
- case MODE_SENSE:
- case MODE_SENSE_10:
- length = le16_to_cpu(fst->Count);
- break;
- default:
- length = scsi_bufflen(srb);
+ case INQUIRY:
+ case REQUEST_SENSE: /* 16 or 18 bytes? spec says 18, lots of devices only have 16 */
+ case MODE_SENSE:
+ case MODE_SENSE_10:
+ length = le16_to_cpu(fst->Count);
+ break;
+ default:
+ length = scsi_bufflen(srb);
}
/* verify that this amount is legal */
/* should never hit here -- filtered in usb.c */
US_DEBUGP ("freecom unimplemented direction: %d\n",
us->srb->sc_data_direction);
- // Return fail, SCSI seems to handle this better.
+ /* Return fail, SCSI seems to handle this better. */
return USB_STOR_TRANSPORT_FAILED;
break;
}
offset = 0;
}
offset += sprintf (line+offset, "%08x:", i);
- }
- else if ((i & 7) == 0) {
+ } else if ((i & 7) == 0) {
offset += sprintf (line+offset, " -");
}
offset += sprintf (line+offset, " %02x", buffer[i] & 0xff);
int retStatus = ISD200_GOOD;
struct isd200_info *info;
- info = (struct isd200_info *)
- kzalloc(sizeof(struct isd200_info), GFP_KERNEL);
+ info = kzalloc(sizeof(struct isd200_info), GFP_KERNEL);
if (!info)
retStatus = ISD200_ERROR;
else {
else {
US_DEBUG(usb_stor_show_command(us->srb));
us->proto_handler(us->srb, us);
+ usb_mark_last_busy(us->pusb_dev);
}
/* lock access to the state */
/* Should we unbind if no devices were detected? */
}
+ usb_autopm_put_interface(us->pusb_intf);
complete_and_exit(&us->scanning_done, 0);
}
goto BadDevice;
}
+ usb_autopm_get_interface_no_resume(us->pusb_intf);
wake_up_process(th);
return 0;
.pre_reset = usb_stor_pre_reset,
.post_reset = usb_stor_post_reset,
.id_table = usb_storage_usb_ids,
+ .supports_autosuspend = 1,
.soft_unbind = 1,
};
{
struct usb_skel *dev;
- dev = (struct usb_skel *)file->private_data;
+ dev = file->private_data;
if (dev == NULL)
return -ENODEV;
struct usb_skel *dev;
int res;
- dev = (struct usb_skel *)file->private_data;
+ dev = file->private_data;
if (dev == NULL)
return -ENODEV;
int rv;
bool ongoing_io;
- dev = (struct usb_skel *)file->private_data;
+ dev = file->private_data;
/* if we cannot read at all, return EOF */
if (!dev->bulk_in_urb || !count)
char *buf = NULL;
size_t writesize = min(count, (size_t)MAX_TRANSFER);
- dev = (struct usb_skel *)file->private_data;
+ dev = file->private_data;
/* verify that we actually have some data to write */
if (count == 0)
If unsure, say N.
+config SH_MIPI_DSI
+ tristate
+ depends on (SUPERH || ARCH_SHMOBILE) && HAVE_CLK
+
+config SH_LCD_MIPI_DSI
+ bool
+
config FB_SH_MOBILE_LCDC
tristate "SuperH Mobile LCDC framebuffer support"
depends on FB && (SUPERH || ARCH_SHMOBILE) && HAVE_CLK
select FB_SYS_IMAGEBLIT
select FB_SYS_FOPS
select FB_DEFERRED_IO
+ select SH_MIPI_DSI if SH_LCD_MIPI_DSI
---help---
Frame buffer driver for the on-chip SH-Mobile LCD controller.
+config FB_SH_MOBILE_HDMI
+ tristate "SuperH Mobile HDMI controller support"
+ depends on FB_SH_MOBILE_LCDC
+ select FB_MODE_HELPERS
+ ---help---
+ Driver for the on-chip SH-Mobile HDMI controller.
+
config FB_TMIO
tristate "Toshiba Mobile IO FrameBuffer support"
depends on FB && MFD_CORE
config FB_S3C
tristate "Samsung S3C framebuffer support"
- depends on FB && ARCH_S3C64XX
+ depends on FB && S3C_DEV_FB
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
obj-$(CONFIG_FB_PS3) += ps3fb.o
obj-$(CONFIG_FB_SM501) += sm501fb.o
obj-$(CONFIG_FB_XILINX) += xilinxfb.o
+obj-$(CONFIG_SH_MIPI_DSI) += sh_mipi_dsi.o
+obj-$(CONFIG_FB_SH_MOBILE_HDMI) += sh_mobile_hdmi.o
obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o
obj-$(CONFIG_FB_OMAP) += omap/
obj-y += omap2/
/*
* Accelerated handlers.
*/
-static inline void update_attr(u8 *dst, u8 *src, int attribute,
+static void update_attr(u8 *dst, u8 *src, int attribute,
struct vc_data *vc)
{
int i, offset = (vc->vc_font.height < 10) ? 1 : 2;
struct fbcon_ops *ops = info->fbcon_par;
return (info->state != FBINFO_STATE_RUNNING ||
- vc->vc_mode != KD_TEXT || ops->graphics);
+ vc->vc_mode != KD_TEXT || ops->graphics) &&
+ !vt_force_oops_output(vc);
}
-static inline int get_color(struct vc_data *vc, struct fb_info *info,
+static int get_color(struct vc_data *vc, struct fb_info *info,
u16 c, int is_fg)
{
int depth = fb_get_color_depth(&info->var, &info->fix);
if (p->userfont)
charcnt = FNTCHARCNT(p->fontdata);
+ vc->vc_panic_force_write = !!(info->flags & FBINFO_CAN_FORCE_OUTPUT);
vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
if (charcnt == 256) {
* Rotation 270 degrees
*/
-static inline void ccw_update_attr(u8 *dst, u8 *src, int attribute,
+static void ccw_update_attr(u8 *dst, u8 *src, int attribute,
struct vc_data *vc)
{
int i, j, offset = (vc->vc_font.height < 10) ? 1 : 2;
* Rotation 90 degrees
*/
-static inline void cw_update_attr(u8 *dst, u8 *src, int attribute,
+static void cw_update_attr(u8 *dst, u8 *src, int attribute,
struct vc_data *vc)
{
int i, j, offset = (vc->vc_font.height < 10) ? 1 : 2;
* Rotation 180 degrees
*/
-static inline void ud_update_attr(u8 *dst, u8 *src, int attribute,
+static void ud_update_attr(u8 *dst, u8 *src, int attribute,
struct vc_data *vc)
{
int i, offset = (vc->vc_font.height < 10) ? 1 : 2;
charmap += 4 * cmapsz;
#endif
- unlock_kernel();
spin_lock_irq(&vga_lock);
/* First, the Sequencer */
vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1);
vga_wattr(state->vgabase, VGA_AR_ENABLE_DISPLAY, 0);
}
spin_unlock_irq(&vga_lock);
- lock_kernel();
return 0;
}
#include <video/vga.h>
-static struct fb_var_screeninfo efifb_defined __initdata = {
+static struct fb_var_screeninfo efifb_defined __devinitdata = {
.activate = FB_ACTIVATE_NOW,
.height = -1,
.width = -1,
.vmode = FB_VMODE_NONINTERLACED,
};
-static struct fb_fix_screeninfo efifb_fix __initdata = {
+static struct fb_fix_screeninfo efifb_fix __devinitdata = {
.id = "EFI VGA",
.type = FB_TYPE_PACKED_PIXELS,
.accel = FB_ACCEL_NONE,
int stride;
int width;
int height;
-} dmi_list[] = {
+} dmi_list[] __initdata = {
[M_I17] = { "i17", 0x80010000, 1472 * 4, 1440, 900 },
[M_I20] = { "i20", 0x80010000, 1728 * 4, 1680, 1050 }, /* guess */
[M_I20_SR] = { "imac7", 0x40010000, 1728 * 4, 1680, 1050 },
DMI_MATCH(DMI_PRODUCT_NAME, name) }, \
&dmi_list[enumid] }
-static struct dmi_system_id __initdata dmi_system_table[] = {
+static const struct dmi_system_id dmi_system_table[] __initconst = {
EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac4,1", M_I17),
/* At least one of these two will be right; maybe both? */
EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac5,1", M_I20),
vma->vm_pgoff = off >> PAGE_SHIFT;
/* This is an IO map - tell maydump to skip this VMA */
vma->vm_flags |= VM_IO | VM_RESERVED;
+ vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
fb_pgprotect(file, vma, off);
if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot))
int fb_get_options(char *name, char **option)
{
char *opt, *options = NULL;
- int opt_len, retval = 0;
+ int retval = 0;
int name_len = strlen(name), i;
if (name_len && ofonly && strncmp(name, "offb", 4))
for (i = 0; i < FB_MAX; i++) {
if (video_options[i] == NULL)
continue;
- opt_len = strlen(video_options[i]);
- if (!opt_len)
+ if (!video_options[i][0])
continue;
opt = video_options[i];
if (!strncmp(name, opt, name_len) &&
return 1;
}
-int __init igafb_init(void)
+static int __init igafb_init(void)
{
struct fb_info *info;
struct pci_dev *pdev;
iounmap(info->screen_base);
kfree(par->mmap_map);
kfree(info);
+ return -ENODEV;
}
#ifdef CONFIG_SPARC
return 0;
}
-int __init igafb_setup(char *options)
+static int __init igafb_setup(char *options)
{
char *this_opt;
*/
#define DEBUG_VAR 1
+#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || \
+ (defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) && \
+ defined(CONFIG_FB_IMX_MODULE))
+#define PWMR_BACKLIGHT_AVAILABLE
+#endif
+
#define DRIVER_NAME "imx-fb"
#define LCDC_SSA 0x00
struct imx_fb_videomode *mode;
int num_modes;
+#ifdef PWMR_BACKLIGHT_AVAILABLE
struct backlight_device *bl;
+#endif
void (*lcd_power)(int);
void (*backlight_power)(int);
return 0;
}
-
-
+#ifdef PWMR_BACKLIGHT_AVAILABLE
static int imxfb_bl_get_brightness(struct backlight_device *bl)
{
struct imxfb_info *fbi = bl_get_data(bl);
if (fbi->bl)
backlight_device_unregister(fbi->bl);
}
+#endif
static void imxfb_enable_controller(struct imxfb_info *fbi)
{
fbi->regs + LCDC_SIZE);
writel(fbi->pcr, fbi->regs + LCDC_PCR);
+#ifndef PWMR_BACKLIGHT_AVAILABLE
+ writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
+#endif
writel(fbi->lscr1, fbi->regs + LCDC_LSCR1);
writel(fbi->dmacr, fbi->regs + LCDC_DMACR);
imxfb_enable_controller(fbi);
fbi->pdev = pdev;
+#ifdef PWMR_BACKLIGHT_AVAILABLE
imxfb_init_backlight(fbi);
+#endif
return 0;
imxfb_disable_controller(fbi);
+#ifdef PWMR_BACKLIGHT_AVAILABLE
imxfb_exit_backlight(fbi);
+#endif
unregister_framebuffer(info);
pdata = pdev->dev.platform_data;
static void mddi_handle_rev_data_avail(struct mddi_info *mddi)
{
- union mddi_rev *rev = mddi->rev_data;
uint32_t rev_data_count;
uint32_t rev_crc_err_count;
- int i;
struct reg_read_info *ri;
size_t prev_offset;
uint16_t length;
return 0;
}
-static int __init mddi_probe(struct platform_device *pdev)
+static int __devinit mddi_probe(struct platform_device *pdev)
{
struct msm_mddi_platform_data *pdata = pdev->dev.platform_data;
struct mddi_info *mddi = &mddi_info[pdev->id];
{
int put_needed, ret = 0;
struct file *file;
- unsigned long vstart;
file = fget_light(img->memory_id, &put_needed);
if (file == NULL)
/* linux/drivers/video/s3c-fb.c
*
* Copyright 2008 Openmoko Inc.
- * Copyright 2008 Simtec Electronics
+ * Copyright 2008-2010 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
* http://armlinux.simtec.co.uk/
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * published by the Free Software FoundatIon.
*/
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/fb.h>
#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/interrupt.h>
#include <mach/map.h>
-#include <mach/regs-fb.h>
+#include <plat/regs-fb-v4.h>
#include <plat/fb.h>
/* This driver will export a number of framebuffer interfaces depending
* output timings and as the control for the output power-down state.
*/
-/* note, some of the functions that get called are derived from including
- * <mach/regs-fb.h> as they are specific to the architecture that the code
- * is being built for.
+/* note, the previous use of <mach/regs-fb.h> to get platform specific data
+ * has been replaced by using the platform device name to pick the correct
+ * configuration data for the system.
*/
#ifdef CONFIG_FB_S3C_DEBUG_REGWRITE
__raw_writel(v, r); } while(0)
#endif /* FB_S3C_DEBUG_REGWRITE */
+/* irq_flags bits */
+#define S3C_FB_VSYNC_IRQ_EN 0
+
+#define VSYNC_TIMEOUT_MSEC 50
+
struct s3c_fb;
+#define VALID_BPP(x) (1 << ((x) - 1))
+
+#define OSD_BASE(win, variant) ((variant).osd + ((win) * (variant).osd_stride))
+#define VIDOSD_A(win, variant) (OSD_BASE(win, variant) + 0x00)
+#define VIDOSD_B(win, variant) (OSD_BASE(win, variant) + 0x04)
+#define VIDOSD_C(win, variant) (OSD_BASE(win, variant) + 0x08)
+#define VIDOSD_D(win, variant) (OSD_BASE(win, variant) + 0x0C)
+
+/**
+ * struct s3c_fb_variant - fb variant information
+ * @is_2443: Set if S3C2443/S3C2416 style hardware.
+ * @nr_windows: The number of windows.
+ * @vidtcon: The base for the VIDTCONx registers
+ * @wincon: The base for the WINxCON registers.
+ * @winmap: The base for the WINxMAP registers.
+ * @keycon: The abse for the WxKEYCON registers.
+ * @buf_start: Offset of buffer start registers.
+ * @buf_size: Offset of buffer size registers.
+ * @buf_end: Offset of buffer end registers.
+ * @osd: The base for the OSD registers.
+ * @palette: Address of palette memory, or 0 if none.
+ * @has_prtcon: Set if has PRTCON register.
+ * @has_shadowcon: Set if has SHADOWCON register.
+ */
+struct s3c_fb_variant {
+ unsigned int is_2443:1;
+ unsigned short nr_windows;
+ unsigned short vidtcon;
+ unsigned short wincon;
+ unsigned short winmap;
+ unsigned short keycon;
+ unsigned short buf_start;
+ unsigned short buf_end;
+ unsigned short buf_size;
+ unsigned short osd;
+ unsigned short osd_stride;
+ unsigned short palette[S3C_FB_MAX_WIN];
+
+ unsigned int has_prtcon:1;
+ unsigned int has_shadowcon:1;
+};
+
+/**
+ * struct s3c_fb_win_variant
+ * @has_osd_c: Set if has OSD C register.
+ * @has_osd_d: Set if has OSD D register.
+ * @has_osd_alpha: Set if can change alpha transparency for a window.
+ * @palette_sz: Size of palette in entries.
+ * @palette_16bpp: Set if palette is 16bits wide.
+ * @osd_size_off: If != 0, supports setting up OSD for a window; the appropriate
+ * register is located at the given offset from OSD_BASE.
+ * @valid_bpp: 1 bit per BPP setting to show valid bits-per-pixel.
+ *
+ * valid_bpp bit x is set if (x+1)BPP is supported.
+ */
+struct s3c_fb_win_variant {
+ unsigned int has_osd_c:1;
+ unsigned int has_osd_d:1;
+ unsigned int has_osd_alpha:1;
+ unsigned int palette_16bpp:1;
+ unsigned short osd_size_off;
+ unsigned short palette_sz;
+ u32 valid_bpp;
+};
+
+/**
+ * struct s3c_fb_driverdata - per-device type driver data for init time.
+ * @variant: The variant information for this driver.
+ * @win: The window information for each window.
+ */
+struct s3c_fb_driverdata {
+ struct s3c_fb_variant variant;
+ struct s3c_fb_win_variant *win[S3C_FB_MAX_WIN];
+};
+
+/**
+ * struct s3c_fb_palette - palette information
+ * @r: Red bitfield.
+ * @g: Green bitfield.
+ * @b: Blue bitfield.
+ * @a: Alpha bitfield.
+ */
+struct s3c_fb_palette {
+ struct fb_bitfield r;
+ struct fb_bitfield g;
+ struct fb_bitfield b;
+ struct fb_bitfield a;
+};
+
/**
* struct s3c_fb_win - per window private data for each framebuffer.
* @windata: The platform data supplied for the window configuration.
* @parent: The hardware that this window is part of.
* @fbinfo: Pointer pack to the framebuffer info for this window.
+ * @varint: The variant information for this window.
* @palette_buffer: Buffer/cache to hold palette entries.
* @pseudo_palette: For use in TRUECOLOUR modes for entries 0..15/
* @index: The window number of this window.
struct s3c_fb *parent;
struct fb_info *fbinfo;
struct s3c_fb_palette palette;
+ struct s3c_fb_win_variant variant;
u32 *palette_buffer;
u32 pseudo_palette[16];
unsigned int index;
};
+/**
+ * struct s3c_fb_vsync - vsync information
+ * @wait: a queue for processes waiting for vsync
+ * @count: vsync interrupt count
+ */
+struct s3c_fb_vsync {
+ wait_queue_head_t wait;
+ unsigned int count;
+};
+
/**
* struct s3c_fb - overall hardware state of the hardware
* @dev: The device that we bound to, for printing, etc.
* @regs_res: The resource we claimed for the IO registers.
* @bus_clk: The clk (hclk) feeding our interface and possibly pixclk.
* @regs: The mapped hardware registers.
+ * @variant: Variant information for this hardware.
* @enabled: A bitmask of enabled hardware windows.
* @pdata: The platform configuration data passed with the device.
* @windows: The hardware windows that have been claimed.
+ * @irq_no: IRQ line number
+ * @irq_flags: irq flags
+ * @vsync_info: VSYNC-related information (count, queues...)
*/
struct s3c_fb {
struct device *dev;
struct resource *regs_res;
struct clk *bus_clk;
void __iomem *regs;
+ struct s3c_fb_variant variant;
unsigned char enabled;
struct s3c_fb_platdata *pdata;
struct s3c_fb_win *windows[S3C_FB_MAX_WIN];
+
+ int irq_no;
+ unsigned long irq_flags;
+ struct s3c_fb_vsync vsync_info;
};
/**
- * s3c_fb_win_has_palette() - determine if a mode has a palette
- * @win: The window number being queried.
- * @bpp: The number of bits per pixel to test.
- *
- * Work out if the given window supports palletised data at the specified bpp.
+ * s3c_fb_validate_win_bpp - validate the bits-per-pixel for this mode.
+ * @win: The device window.
+ * @bpp: The bit depth.
*/
-static int s3c_fb_win_has_palette(unsigned int win, unsigned int bpp)
+static bool s3c_fb_validate_win_bpp(struct s3c_fb_win *win, unsigned int bpp)
{
- return s3c_fb_win_pal_size(win) <= (1 << bpp);
+ return win->variant.valid_bpp & VALID_BPP(bpp);
}
/**
var->xres_virtual = max((unsigned int)windata->virtual_x, var->xres);
var->yres_virtual = max((unsigned int)windata->virtual_y, var->yres);
- if (!s3c_fb_validate_win_bpp(win->index, var->bits_per_pixel)) {
+ if (!s3c_fb_validate_win_bpp(win, var->bits_per_pixel)) {
dev_dbg(sfb->dev, "win %d: unsupported bpp %d\n",
win->index, var->bits_per_pixel);
return -EINVAL;
case 2:
case 4:
case 8:
- if (!s3c_fb_win_has_palette(win->index, var->bits_per_pixel)) {
+ if (sfb->variant.palette[win->index] != 0) {
/* non palletised, A:1,R:2,G:3,B:2 mode */
var->red.offset = 4;
var->green.offset = 2;
return ALIGN(pix, pix_per_word);
}
+/**
+ * vidosd_set_size() - set OSD size for a window
+ *
+ * @win: the window to set OSD size for
+ * @size: OSD size register value
+ */
+static void vidosd_set_size(struct s3c_fb_win *win, u32 size)
+{
+ struct s3c_fb *sfb = win->parent;
+
+ /* OSD can be set up if osd_size_off != 0 for this window */
+ if (win->variant.osd_size_off)
+ writel(size, sfb->regs + OSD_BASE(win->index, sfb->variant)
+ + win->variant.osd_size_off);
+}
+
+/**
+ * vidosd_set_alpha() - set alpha transparency for a window
+ *
+ * @win: the window to set OSD size for
+ * @alpha: alpha register value
+ */
+static void vidosd_set_alpha(struct s3c_fb_win *win, u32 alpha)
+{
+ struct s3c_fb *sfb = win->parent;
+
+ if (win->variant.has_osd_alpha)
+ writel(alpha, sfb->regs + VIDOSD_C(win->index, sfb->variant));
+}
+
+/**
+ * shadow_protect_win() - disable updating values from shadow registers at vsync
+ *
+ * @win: window to protect registers for
+ * @protect: 1 to protect (disable updates)
+ */
+static void shadow_protect_win(struct s3c_fb_win *win, bool protect)
+{
+ struct s3c_fb *sfb = win->parent;
+ u32 reg;
+
+ if (protect) {
+ if (sfb->variant.has_prtcon) {
+ writel(PRTCON_PROTECT, sfb->regs + PRTCON);
+ } else if (sfb->variant.has_shadowcon) {
+ reg = readl(sfb->regs + SHADOWCON);
+ writel(reg | SHADOWCON_WINx_PROTECT(win->index),
+ sfb->regs + SHADOWCON);
+ }
+ } else {
+ if (sfb->variant.has_prtcon) {
+ writel(0, sfb->regs + PRTCON);
+ } else if (sfb->variant.has_shadowcon) {
+ reg = readl(sfb->regs + SHADOWCON);
+ writel(reg & ~SHADOWCON_WINx_PROTECT(win->index),
+ sfb->regs + SHADOWCON);
+ }
+ }
+}
+
/**
* s3c_fb_set_par() - framebuffer request to set new framebuffer state.
* @info: The framebuffer to change.
struct s3c_fb_win *win = info->par;
struct s3c_fb *sfb = win->parent;
void __iomem *regs = sfb->regs;
+ void __iomem *buf = regs;
int win_no = win->index;
- u32 osdc_data = 0;
+ u32 alpha = 0;
u32 data;
u32 pagewidth;
int clkdiv;
dev_dbg(sfb->dev, "setting framebuffer parameters\n");
+ shadow_protect_win(win, 1);
+
switch (var->bits_per_pixel) {
case 32:
case 24:
info->fix.visual = FB_VISUAL_TRUECOLOR;
break;
case 8:
- if (s3c_fb_win_has_palette(win_no, 8))
+ if (win->variant.palette_sz >= 256)
info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
else
info->fix.visual = FB_VISUAL_TRUECOLOR;
info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
+ info->fix.xpanstep = info->var.xres_virtual > info->var.xres ? 1 : 0;
+ info->fix.ypanstep = info->var.yres_virtual > info->var.yres ? 1 : 0;
+
/* disable the window whilst we update it */
writel(0, regs + WINCON(win_no));
- /* use window 0 as the basis for the lcd output timings */
+ /* use platform specified window as the basis for the lcd timings */
- if (win_no == 0) {
+ if (win_no == sfb->pdata->default_win) {
clkdiv = s3c_fb_calc_pixclk(sfb, var->pixclock);
data = sfb->pdata->vidcon0;
/* write the timing data to the panel */
+ if (sfb->variant.is_2443)
+ data |= (1 << 5);
+
data |= VIDCON0_ENVID | VIDCON0_ENVID_F;
writel(data, regs + VIDCON0);
VIDTCON0_VFPD(var->lower_margin - 1) |
VIDTCON0_VSPW(var->vsync_len - 1);
- writel(data, regs + VIDTCON0);
+ writel(data, regs + sfb->variant.vidtcon);
data = VIDTCON1_HBPD(var->left_margin - 1) |
VIDTCON1_HFPD(var->right_margin - 1) |
VIDTCON1_HSPW(var->hsync_len - 1);
- writel(data, regs + VIDTCON1);
+ /* VIDTCON1 */
+ writel(data, regs + sfb->variant.vidtcon + 4);
data = VIDTCON2_LINEVAL(var->yres - 1) |
VIDTCON2_HOZVAL(var->xres - 1);
- writel(data, regs + VIDTCON2);
+ writel(data, regs +sfb->variant.vidtcon + 8 );
}
/* write the buffer address */
- writel(info->fix.smem_start, regs + VIDW_BUF_START(win_no));
+ /* start and end registers stride is 8 */
+ buf = regs + win_no * 8;
+
+ writel(info->fix.smem_start, buf + sfb->variant.buf_start);
data = info->fix.smem_start + info->fix.line_length * var->yres;
- writel(data, regs + VIDW_BUF_END(win_no));
+ writel(data, buf + sfb->variant.buf_end);
pagewidth = (var->xres * var->bits_per_pixel) >> 3;
data = VIDW_BUF_SIZE_OFFSET(info->fix.line_length - pagewidth) |
VIDW_BUF_SIZE_PAGEWIDTH(pagewidth);
- writel(data, regs + VIDW_BUF_SIZE(win_no));
+ writel(data, regs + sfb->variant.buf_size + (win_no * 4));
/* write 'OSD' registers to control position of framebuffer */
data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0);
- writel(data, regs + VIDOSD_A(win_no));
+ writel(data, regs + VIDOSD_A(win_no, sfb->variant));
data = VIDOSDxB_BOTRIGHT_X(s3c_fb_align_word(var->bits_per_pixel,
var->xres - 1)) |
VIDOSDxB_BOTRIGHT_Y(var->yres - 1);
- writel(data, regs + VIDOSD_B(win_no));
+ writel(data, regs + VIDOSD_B(win_no, sfb->variant));
data = var->xres * var->yres;
- osdc_data = VIDISD14C_ALPHA1_R(0xf) |
+ alpha = VIDISD14C_ALPHA1_R(0xf) |
VIDISD14C_ALPHA1_G(0xf) |
VIDISD14C_ALPHA1_B(0xf);
- if (s3c_fb_has_osd_d(win_no)) {
- writel(data, regs + VIDOSD_D(win_no));
- writel(osdc_data, regs + VIDOSD_C(win_no));
- } else
- writel(data, regs + VIDOSD_C(win_no));
+ vidosd_set_alpha(win, alpha);
+ vidosd_set_size(win, data);
data = WINCONx_ENWIN;
else
data |= WINCON0_BPPMODE_24BPP_888;
+ data |= WINCONx_WSWP;
data |= WINCONx_BURSTLEN_16WORD;
break;
}
- /* It has no color key control register for window0 */
+ /* Enable the colour keying for the window below this one */
if (win_no > 0) {
u32 keycon0_data = 0, keycon1_data = 0;
+ void __iomem *keycon = regs + sfb->variant.keycon;
keycon0_data = ~(WxKEYCON0_KEYBL_EN |
WxKEYCON0_KEYEN_F |
keycon1_data = WxKEYCON1_COLVAL(0xffffff);
- writel(keycon0_data, regs + WxKEYCONy(win_no-1, 0));
- writel(keycon1_data, regs + WxKEYCONy(win_no-1, 1));
+ keycon += (win_no - 1) * 8;
+
+ writel(keycon0_data, keycon + WKEYCON0);
+ writel(keycon1_data, keycon + WKEYCON1);
+ }
+
+ writel(data, regs + sfb->variant.wincon + (win_no * 4));
+ writel(0x0, regs + sfb->variant.winmap + (win_no * 4));
+
+ /* Enable DMA channel for this window */
+ if (sfb->variant.has_shadowcon) {
+ data = readl(sfb->regs + SHADOWCON);
+ data |= SHADOWCON_CHx_ENABLE(win_no);
+ writel(data, sfb->regs + SHADOWCON);
}
- writel(data, regs + WINCON(win_no));
- writel(0x0, regs + WINxMAP(win_no));
+ shadow_protect_win(win, 0);
return 0;
}
void __iomem *palreg;
u32 palcon;
- palreg = sfb->regs + s3c_fb_pal_reg(win->index, reg);
+ palreg = sfb->regs + sfb->variant.palette[win->index];
dev_dbg(sfb->dev, "%s: win %d, reg %d (%p): %08x\n",
__func__, win->index, reg, palreg, value);
palcon = readl(sfb->regs + WPALCON);
writel(palcon | WPALCON_PAL_UPDATE, sfb->regs + WPALCON);
- if (s3c_fb_pal_is16(win->index))
- writew(value, palreg);
+ if (win->variant.palette_16bpp)
+ writew(value, palreg + (reg * 2));
else
- writel(value, palreg);
+ writel(value, palreg + (reg * 4));
writel(palcon, sfb->regs + WPALCON);
}
break;
case FB_VISUAL_PSEUDOCOLOR:
- if (regno < s3c_fb_win_pal_size(win->index)) {
+ if (regno < win->variant.palette_sz) {
val = chan_to_field(red, &win->palette.r);
val |= chan_to_field(green, &win->palette.g);
val |= chan_to_field(blue, &win->palette.b);
dev_dbg(sfb->dev, "blank mode %d\n", blank_mode);
- wincon = readl(sfb->regs + WINCON(index));
+ wincon = readl(sfb->regs + sfb->variant.wincon + (index * 4));
switch (blank_mode) {
case FB_BLANK_POWERDOWN:
case FB_BLANK_NORMAL:
/* disable the DMA and display 0x0 (black) */
writel(WINxMAP_MAP | WINxMAP_MAP_COLOUR(0x0),
- sfb->regs + WINxMAP(index));
+ sfb->regs + sfb->variant.winmap + (index * 4));
break;
case FB_BLANK_UNBLANK:
- writel(0x0, sfb->regs + WINxMAP(index));
+ writel(0x0, sfb->regs + sfb->variant.winmap + (index * 4));
wincon |= WINCONx_ENWIN;
sfb->enabled |= (1 << index);
break;
return 1;
}
- writel(wincon, sfb->regs + WINCON(index));
+ writel(wincon, sfb->regs + sfb->variant.wincon + (index * 4));
/* Check the enabled state to see if we need to be running the
* main LCD interface, as if there are no active windows then
/* we're stuck with this until we can do something about overriding
* the power control using the blanking event for a single fb.
*/
- if (index == 0)
+ if (index == sfb->pdata->default_win)
s3c_fb_enable(sfb, blank_mode != FB_BLANK_POWERDOWN ? 1 : 0);
return 0;
}
+/**
+ * s3c_fb_pan_display() - Pan the display.
+ *
+ * Note that the offsets can be written to the device at any time, as their
+ * values are latched at each vsync automatically. This also means that only
+ * the last call to this function will have any effect on next vsync, but
+ * there is no need to sleep waiting for it to prevent tearing.
+ *
+ * @var: The screen information to verify.
+ * @info: The framebuffer device.
+ */
+static int s3c_fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct s3c_fb_win *win = info->par;
+ struct s3c_fb *sfb = win->parent;
+ void __iomem *buf = sfb->regs + win->index * 8;
+ unsigned int start_boff, end_boff;
+
+ /* Offset in bytes to the start of the displayed area */
+ start_boff = var->yoffset * info->fix.line_length;
+ /* X offset depends on the current bpp */
+ if (info->var.bits_per_pixel >= 8) {
+ start_boff += var->xoffset * (info->var.bits_per_pixel >> 3);
+ } else {
+ switch (info->var.bits_per_pixel) {
+ case 4:
+ start_boff += var->xoffset >> 1;
+ break;
+ case 2:
+ start_boff += var->xoffset >> 2;
+ break;
+ case 1:
+ start_boff += var->xoffset >> 3;
+ break;
+ default:
+ dev_err(sfb->dev, "invalid bpp\n");
+ return -EINVAL;
+ }
+ }
+ /* Offset in bytes to the end of the displayed area */
+ end_boff = start_boff + var->yres * info->fix.line_length;
+
+ /* Temporarily turn off per-vsync update from shadow registers until
+ * both start and end addresses are updated to prevent corruption */
+ shadow_protect_win(win, 1);
+
+ writel(info->fix.smem_start + start_boff, buf + sfb->variant.buf_start);
+ writel(info->fix.smem_start + end_boff, buf + sfb->variant.buf_end);
+
+ shadow_protect_win(win, 0);
+
+ return 0;
+}
+
+/**
+ * s3c_fb_enable_irq() - enable framebuffer interrupts
+ * @sfb: main hardware state
+ */
+static void s3c_fb_enable_irq(struct s3c_fb *sfb)
+{
+ void __iomem *regs = sfb->regs;
+ u32 irq_ctrl_reg;
+
+ if (!test_and_set_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) {
+ /* IRQ disabled, enable it */
+ irq_ctrl_reg = readl(regs + VIDINTCON0);
+
+ irq_ctrl_reg |= VIDINTCON0_INT_ENABLE;
+ irq_ctrl_reg |= VIDINTCON0_INT_FRAME;
+
+ irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL0_MASK;
+ irq_ctrl_reg |= VIDINTCON0_FRAMESEL0_VSYNC;
+ irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL1_MASK;
+ irq_ctrl_reg |= VIDINTCON0_FRAMESEL1_NONE;
+
+ writel(irq_ctrl_reg, regs + VIDINTCON0);
+ }
+}
+
+/**
+ * s3c_fb_disable_irq() - disable framebuffer interrupts
+ * @sfb: main hardware state
+ */
+static void s3c_fb_disable_irq(struct s3c_fb *sfb)
+{
+ void __iomem *regs = sfb->regs;
+ u32 irq_ctrl_reg;
+
+ if (test_and_clear_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) {
+ /* IRQ enabled, disable it */
+ irq_ctrl_reg = readl(regs + VIDINTCON0);
+
+ irq_ctrl_reg &= ~VIDINTCON0_INT_FRAME;
+ irq_ctrl_reg &= ~VIDINTCON0_INT_ENABLE;
+
+ writel(irq_ctrl_reg, regs + VIDINTCON0);
+ }
+}
+
+static irqreturn_t s3c_fb_irq(int irq, void *dev_id)
+{
+ struct s3c_fb *sfb = dev_id;
+ void __iomem *regs = sfb->regs;
+ u32 irq_sts_reg;
+
+ irq_sts_reg = readl(regs + VIDINTCON1);
+
+ if (irq_sts_reg & VIDINTCON1_INT_FRAME) {
+
+ /* VSYNC interrupt, accept it */
+ writel(VIDINTCON1_INT_FRAME, regs + VIDINTCON1);
+
+ sfb->vsync_info.count++;
+ wake_up_interruptible(&sfb->vsync_info.wait);
+ }
+
+ /* We only support waiting for VSYNC for now, so it's safe
+ * to always disable irqs here.
+ */
+ s3c_fb_disable_irq(sfb);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * s3c_fb_wait_for_vsync() - sleep until next VSYNC interrupt or timeout
+ * @sfb: main hardware state
+ * @crtc: head index.
+ */
+static int s3c_fb_wait_for_vsync(struct s3c_fb *sfb, u32 crtc)
+{
+ unsigned long count;
+ int ret;
+
+ if (crtc != 0)
+ return -ENODEV;
+
+ count = sfb->vsync_info.count;
+ s3c_fb_enable_irq(sfb);
+ ret = wait_event_interruptible_timeout(sfb->vsync_info.wait,
+ count != sfb->vsync_info.count,
+ msecs_to_jiffies(VSYNC_TIMEOUT_MSEC));
+ if (ret == 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int s3c_fb_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ struct s3c_fb_win *win = info->par;
+ struct s3c_fb *sfb = win->parent;
+ int ret;
+ u32 crtc;
+
+ switch (cmd) {
+ case FBIO_WAITFORVSYNC:
+ if (get_user(crtc, (u32 __user *)arg)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = s3c_fb_wait_for_vsync(sfb, crtc);
+ break;
+ default:
+ ret = -ENOTTY;
+ }
+
+ return ret;
+}
+
static struct fb_ops s3c_fb_ops = {
.owner = THIS_MODULE,
.fb_check_var = s3c_fb_check_var,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
+ .fb_pan_display = s3c_fb_pan_display,
+ .fb_ioctl = s3c_fb_ioctl,
};
+/**
+ * s3c_fb_missing_pixclock() - calculates pixel clock
+ * @mode: The video mode to change.
+ *
+ * Calculate the pixel clock when none has been given through platform data.
+ */
+static void __devinit s3c_fb_missing_pixclock(struct fb_videomode *mode)
+{
+ u64 pixclk = 1000000000000ULL;
+ u32 div;
+
+ div = mode->left_margin + mode->hsync_len + mode->right_margin +
+ mode->xres;
+ div *= mode->upper_margin + mode->vsync_len + mode->lower_margin +
+ mode->yres;
+ div *= mode->refresh ? : 60;
+
+ do_div(pixclk, div);
+
+ mode->pixclock = pixclk;
+}
+
/**
* s3c_fb_alloc_memory() - allocate display memory for framebuffer window
* @sfb: The base resources for the hardware.
{
struct fb_info *fbi = win->fbinfo;
- dma_free_writecombine(sfb->dev, PAGE_ALIGN(fbi->fix.smem_len),
+ if (fbi->screen_base)
+ dma_free_writecombine(sfb->dev, PAGE_ALIGN(fbi->fix.smem_len),
fbi->screen_base, fbi->fix.smem_start);
}
*/
static void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win)
{
+ u32 data;
+
if (win->fbinfo) {
+ if (sfb->variant.has_shadowcon) {
+ data = readl(sfb->regs + SHADOWCON);
+ data &= ~SHADOWCON_CHx_ENABLE(win->index);
+ data &= ~SHADOWCON_CHx_LOCAL_ENABLE(win->index);
+ writel(data, sfb->regs + SHADOWCON);
+ }
unregister_framebuffer(win->fbinfo);
- fb_dealloc_cmap(&win->fbinfo->cmap);
+ if (win->fbinfo->cmap.len)
+ fb_dealloc_cmap(&win->fbinfo->cmap);
s3c_fb_free_memory(sfb, win);
framebuffer_release(win->fbinfo);
}
/**
* s3c_fb_probe_win() - register an hardware window
* @sfb: The base resources for the hardware
+ * @variant: The variant information for this window.
* @res: Pointer to where to place the resultant window.
*
* Allocate and do the basic initialisation for one of the hardware's graphics
* windows.
*/
static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
+ struct s3c_fb_win_variant *variant,
struct s3c_fb_win **res)
{
struct fb_var_screeninfo *var;
int palette_size;
int ret;
- dev_dbg(sfb->dev, "probing window %d\n", win_no);
+ dev_dbg(sfb->dev, "probing window %d, variant %p\n", win_no, variant);
+
+ init_waitqueue_head(&sfb->vsync_info.wait);
- palette_size = s3c_fb_win_pal_size(win_no);
+ palette_size = variant->palette_sz * 4;
fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) +
palette_size * sizeof(u32), sfb->dev);
WARN_ON(windata->win_mode.yres == 0);
win = fbinfo->par;
+ *res = win;
var = &fbinfo->var;
+ win->variant = *variant;
win->fbinfo = fbinfo;
win->parent = sfb;
win->windata = windata;
}
/* setup the r/b/g positions for the window's palette */
- s3c_fb_init_palette(win_no, &win->palette);
+ if (win->variant.palette_16bpp) {
+ /* Set RGB 5:6:5 as default */
+ win->palette.r.offset = 11;
+ win->palette.r.length = 5;
+ win->palette.g.offset = 5;
+ win->palette.g.length = 6;
+ win->palette.b.offset = 0;
+ win->palette.b.length = 5;
+
+ } else {
+ /* Set 8bpp or 8bpp and 1bit alpha */
+ win->palette.r.offset = 16;
+ win->palette.r.length = 8;
+ win->palette.g.offset = 8;
+ win->palette.g.length = 8;
+ win->palette.b.offset = 0;
+ win->palette.b.length = 8;
+ }
/* setup the initial video mode from the window */
fb_videomode_to_var(&fbinfo->var, initmode);
/* create initial colour map */
- ret = fb_alloc_cmap(&fbinfo->cmap, s3c_fb_win_pal_size(win_no), 1);
+ ret = fb_alloc_cmap(&fbinfo->cmap, win->variant.palette_sz, 1);
if (ret == 0)
fb_set_cmap(&fbinfo->cmap, fbinfo);
else
return ret;
}
- *res = win;
dev_info(sfb->dev, "window %d: fb %s\n", win_no, fbinfo->fix.id);
return 0;
static void s3c_fb_clear_win(struct s3c_fb *sfb, int win)
{
void __iomem *regs = sfb->regs;
-
- writel(0, regs + WINCON(win));
- writel(0xffffff, regs + WxKEYCONy(win, 0));
- writel(0xffffff, regs + WxKEYCONy(win, 1));
-
- writel(0, regs + VIDOSD_A(win));
- writel(0, regs + VIDOSD_B(win));
- writel(0, regs + VIDOSD_C(win));
+ u32 reg;
+
+ writel(0, regs + sfb->variant.wincon + (win * 4));
+ writel(0, regs + VIDOSD_A(win, sfb->variant));
+ writel(0, regs + VIDOSD_B(win, sfb->variant));
+ writel(0, regs + VIDOSD_C(win, sfb->variant));
+ reg = readl(regs + SHADOWCON);
+ writel(reg & ~SHADOWCON_WINx_PROTECT(win), regs + SHADOWCON);
}
static int __devinit s3c_fb_probe(struct platform_device *pdev)
{
+ struct s3c_fb_driverdata *fbdrv;
struct device *dev = &pdev->dev;
struct s3c_fb_platdata *pd;
struct s3c_fb *sfb;
int win;
int ret = 0;
+ fbdrv = (struct s3c_fb_driverdata *)platform_get_device_id(pdev)->driver_data;
+
+ if (fbdrv->variant.nr_windows > S3C_FB_MAX_WIN) {
+ dev_err(dev, "too many windows, cannot attach\n");
+ return -EINVAL;
+ }
+
pd = pdev->dev.platform_data;
if (!pd) {
dev_err(dev, "no platform data specified\n");
return -ENOMEM;
}
+ dev_dbg(dev, "allocate new framebuffer %p\n", sfb);
+
sfb->dev = dev;
sfb->pdata = pd;
+ sfb->variant = fbdrv->variant;
sfb->bus_clk = clk_get(dev, "lcd");
if (IS_ERR(sfb->bus_clk)) {
goto err_req_region;
}
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dev, "failed to acquire irq resource\n");
+ ret = -ENOENT;
+ goto err_ioremap;
+ }
+ sfb->irq_no = res->start;
+ ret = request_irq(sfb->irq_no, s3c_fb_irq,
+ 0, "s3c_fb", sfb);
+ if (ret) {
+ dev_err(dev, "irq request failed\n");
+ goto err_ioremap;
+ }
+
dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs);
/* setup gpio and output polarity controls */
/* zero all windows before we do anything */
- for (win = 0; win < S3C_FB_MAX_WIN; win++)
+ for (win = 0; win < fbdrv->variant.nr_windows; win++)
s3c_fb_clear_win(sfb, win);
+ /* initialise colour key controls */
+ for (win = 0; win < (fbdrv->variant.nr_windows - 1); win++) {
+ void __iomem *regs = sfb->regs + sfb->variant.keycon;
+
+ regs += (win * 8);
+ writel(0xffffff, regs + WKEYCON0);
+ writel(0xffffff, regs + WKEYCON1);
+ }
+
/* we have the register setup, start allocating framebuffers */
- for (win = 0; win < S3C_FB_MAX_WIN; win++) {
+ for (win = 0; win < fbdrv->variant.nr_windows; win++) {
if (!pd->win[win])
continue;
- ret = s3c_fb_probe_win(sfb, win, &sfb->windows[win]);
+ if (!pd->win[win]->win_mode.pixclock)
+ s3c_fb_missing_pixclock(&pd->win[win]->win_mode);
+
+ ret = s3c_fb_probe_win(sfb, win, fbdrv->win[win],
+ &sfb->windows[win]);
if (ret < 0) {
dev_err(dev, "failed to create window %d\n", win);
for (; win >= 0; win--)
s3c_fb_release_win(sfb, sfb->windows[win]);
- goto err_ioremap;
+ goto err_irq;
}
}
return 0;
+err_irq:
+ free_irq(sfb->irq_no, sfb);
+
err_ioremap:
iounmap(sfb->regs);
if (sfb->windows[win])
s3c_fb_release_win(sfb, sfb->windows[win]);
+ free_irq(sfb->irq_no, sfb);
+
iounmap(sfb->regs);
clk_disable(sfb->bus_clk);
writel(pd->vidcon1, sfb->regs + VIDCON1);
/* zero all windows before we do anything */
- for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++)
+ for (win_no = 0; win_no < sfb->variant.nr_windows; win_no++)
s3c_fb_clear_win(sfb, win_no);
+ for (win_no = 0; win_no < sfb->variant.nr_windows - 1; win_no++) {
+ void __iomem *regs = sfb->regs + sfb->variant.keycon;
+
+ regs += (win_no * 8);
+ writel(0xffffff, regs + WKEYCON0);
+ writel(0xffffff, regs + WKEYCON1);
+ }
+
/* restore framebuffers */
for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) {
win = sfb->windows[win_no];
#define s3c_fb_resume NULL
#endif
+
+#define VALID_BPP124 (VALID_BPP(1) | VALID_BPP(2) | VALID_BPP(4))
+#define VALID_BPP1248 (VALID_BPP124 | VALID_BPP(8))
+
+static struct s3c_fb_win_variant s3c_fb_data_64xx_wins[] = {
+ [0] = {
+ .has_osd_c = 1,
+ .osd_size_off = 0x8,
+ .palette_sz = 256,
+ .valid_bpp = VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(24),
+ },
+ [1] = {
+ .has_osd_c = 1,
+ .has_osd_d = 1,
+ .osd_size_off = 0x12,
+ .has_osd_alpha = 1,
+ .palette_sz = 256,
+ .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) |
+ VALID_BPP(18) | VALID_BPP(19) |
+ VALID_BPP(24) | VALID_BPP(25)),
+ },
+ [2] = {
+ .has_osd_c = 1,
+ .has_osd_d = 1,
+ .osd_size_off = 0x12,
+ .has_osd_alpha = 1,
+ .palette_sz = 16,
+ .palette_16bpp = 1,
+ .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) |
+ VALID_BPP(18) | VALID_BPP(19) |
+ VALID_BPP(24) | VALID_BPP(25)),
+ },
+ [3] = {
+ .has_osd_c = 1,
+ .has_osd_alpha = 1,
+ .palette_sz = 16,
+ .palette_16bpp = 1,
+ .valid_bpp = (VALID_BPP124 | VALID_BPP(16) |
+ VALID_BPP(18) | VALID_BPP(19) |
+ VALID_BPP(24) | VALID_BPP(25)),
+ },
+ [4] = {
+ .has_osd_c = 1,
+ .has_osd_alpha = 1,
+ .palette_sz = 4,
+ .palette_16bpp = 1,
+ .valid_bpp = (VALID_BPP(1) | VALID_BPP(2) |
+ VALID_BPP(16) | VALID_BPP(18) |
+ VALID_BPP(24) | VALID_BPP(25)),
+ },
+};
+
+static struct s3c_fb_driverdata s3c_fb_data_64xx = {
+ .variant = {
+ .nr_windows = 5,
+ .vidtcon = VIDTCON0,
+ .wincon = WINCON(0),
+ .winmap = WINxMAP(0),
+ .keycon = WKEYCON,
+ .osd = VIDOSD_BASE,
+ .osd_stride = 16,
+ .buf_start = VIDW_BUF_START(0),
+ .buf_size = VIDW_BUF_SIZE(0),
+ .buf_end = VIDW_BUF_END(0),
+
+ .palette = {
+ [0] = 0x400,
+ [1] = 0x800,
+ [2] = 0x300,
+ [3] = 0x320,
+ [4] = 0x340,
+ },
+
+ .has_prtcon = 1,
+ },
+ .win[0] = &s3c_fb_data_64xx_wins[0],
+ .win[1] = &s3c_fb_data_64xx_wins[1],
+ .win[2] = &s3c_fb_data_64xx_wins[2],
+ .win[3] = &s3c_fb_data_64xx_wins[3],
+ .win[4] = &s3c_fb_data_64xx_wins[4],
+};
+
+static struct s3c_fb_driverdata s3c_fb_data_s5pc100 = {
+ .variant = {
+ .nr_windows = 5,
+ .vidtcon = VIDTCON0,
+ .wincon = WINCON(0),
+ .winmap = WINxMAP(0),
+ .keycon = WKEYCON,
+ .osd = VIDOSD_BASE,
+ .osd_stride = 16,
+ .buf_start = VIDW_BUF_START(0),
+ .buf_size = VIDW_BUF_SIZE(0),
+ .buf_end = VIDW_BUF_END(0),
+
+ .palette = {
+ [0] = 0x2400,
+ [1] = 0x2800,
+ [2] = 0x2c00,
+ [3] = 0x3000,
+ [4] = 0x3400,
+ },
+
+ .has_prtcon = 1,
+ },
+ .win[0] = &s3c_fb_data_64xx_wins[0],
+ .win[1] = &s3c_fb_data_64xx_wins[1],
+ .win[2] = &s3c_fb_data_64xx_wins[2],
+ .win[3] = &s3c_fb_data_64xx_wins[3],
+ .win[4] = &s3c_fb_data_64xx_wins[4],
+};
+
+static struct s3c_fb_driverdata s3c_fb_data_s5pv210 = {
+ .variant = {
+ .nr_windows = 5,
+ .vidtcon = VIDTCON0,
+ .wincon = WINCON(0),
+ .winmap = WINxMAP(0),
+ .keycon = WKEYCON,
+ .osd = VIDOSD_BASE,
+ .osd_stride = 16,
+ .buf_start = VIDW_BUF_START(0),
+ .buf_size = VIDW_BUF_SIZE(0),
+ .buf_end = VIDW_BUF_END(0),
+
+ .palette = {
+ [0] = 0x2400,
+ [1] = 0x2800,
+ [2] = 0x2c00,
+ [3] = 0x3000,
+ [4] = 0x3400,
+ },
+
+ .has_shadowcon = 1,
+ },
+ .win[0] = &s3c_fb_data_64xx_wins[0],
+ .win[1] = &s3c_fb_data_64xx_wins[1],
+ .win[2] = &s3c_fb_data_64xx_wins[2],
+ .win[3] = &s3c_fb_data_64xx_wins[3],
+ .win[4] = &s3c_fb_data_64xx_wins[4],
+};
+
+/* S3C2443/S3C2416 style hardware */
+static struct s3c_fb_driverdata s3c_fb_data_s3c2443 = {
+ .variant = {
+ .nr_windows = 2,
+ .is_2443 = 1,
+
+ .vidtcon = 0x08,
+ .wincon = 0x14,
+ .winmap = 0xd0,
+ .keycon = 0xb0,
+ .osd = 0x28,
+ .osd_stride = 12,
+ .buf_start = 0x64,
+ .buf_size = 0x94,
+ .buf_end = 0x7c,
+
+ .palette = {
+ [0] = 0x400,
+ [1] = 0x800,
+ },
+ },
+ .win[0] = &(struct s3c_fb_win_variant) {
+ .palette_sz = 256,
+ .valid_bpp = VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(24),
+ },
+ .win[1] = &(struct s3c_fb_win_variant) {
+ .has_osd_c = 1,
+ .has_osd_alpha = 1,
+ .palette_sz = 256,
+ .valid_bpp = (VALID_BPP1248 | VALID_BPP(16) |
+ VALID_BPP(18) | VALID_BPP(19) |
+ VALID_BPP(24) | VALID_BPP(25) |
+ VALID_BPP(28)),
+ },
+};
+
+static struct platform_device_id s3c_fb_driver_ids[] = {
+ {
+ .name = "s3c-fb",
+ .driver_data = (unsigned long)&s3c_fb_data_64xx,
+ }, {
+ .name = "s5pc100-fb",
+ .driver_data = (unsigned long)&s3c_fb_data_s5pc100,
+ }, {
+ .name = "s5pv210-fb",
+ .driver_data = (unsigned long)&s3c_fb_data_s5pv210,
+ }, {
+ .name = "s3c2443-fb",
+ .driver_data = (unsigned long)&s3c_fb_data_s3c2443,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, s3c_fb_driver_ids);
+
static struct platform_driver s3c_fb_driver = {
.probe = s3c_fb_probe,
.remove = __devexit_p(s3c_fb_remove),
.suspend = s3c_fb_suspend,
.resume = s3c_fb_resume,
+ .id_table = s3c_fb_driver_ids,
.driver = {
.name = "s3c-fb",
.owner = THIS_MODULE,
--- /dev/null
+/*
+ * Renesas SH-mobile MIPI DSI support
+ *
+ * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include <video/mipi_display.h>
+#include <video/sh_mipi_dsi.h>
+#include <video/sh_mobile_lcdc.h>
+
+#define CMTSRTCTR 0x80d0
+#define CMTSRTREQ 0x8070
+
+#define DSIINTE 0x0060
+
+/* E.g., sh7372 has 2 MIPI-DSIs - one for each LCDC */
+#define MAX_SH_MIPI_DSI 2
+
+struct sh_mipi {
+ void __iomem *base;
+ struct clk *dsit_clk;
+ struct clk *dsip_clk;
+};
+
+static struct sh_mipi *mipi_dsi[MAX_SH_MIPI_DSI];
+
+/* Protect the above array */
+static DEFINE_MUTEX(array_lock);
+
+static struct sh_mipi *sh_mipi_by_handle(int handle)
+{
+ if (handle >= ARRAY_SIZE(mipi_dsi) || handle < 0)
+ return NULL;
+
+ return mipi_dsi[handle];
+}
+
+static int sh_mipi_send_short(struct sh_mipi *mipi, u8 dsi_cmd,
+ u8 cmd, u8 param)
+{
+ u32 data = (dsi_cmd << 24) | (cmd << 16) | (param << 8);
+ int cnt = 100;
+
+ /* transmit a short packet to LCD panel */
+ iowrite32(1 | data, mipi->base + 0x80d0); /* CMTSRTCTR */
+ iowrite32(1, mipi->base + 0x8070); /* CMTSRTREQ */
+
+ while ((ioread32(mipi->base + 0x8070) & 1) && --cnt)
+ udelay(1);
+
+ return cnt ? 0 : -ETIMEDOUT;
+}
+
+#define LCD_CHAN2MIPI(c) ((c) < LCDC_CHAN_MAINLCD || (c) > LCDC_CHAN_SUBLCD ? \
+ -EINVAL : (c) - 1)
+
+static int sh_mipi_dcs(int handle, u8 cmd)
+{
+ struct sh_mipi *mipi = sh_mipi_by_handle(LCD_CHAN2MIPI(handle));
+ if (!mipi)
+ return -ENODEV;
+ return sh_mipi_send_short(mipi, MIPI_DSI_DCS_SHORT_WRITE, cmd, 0);
+}
+
+static int sh_mipi_dcs_param(int handle, u8 cmd, u8 param)
+{
+ struct sh_mipi *mipi = sh_mipi_by_handle(LCD_CHAN2MIPI(handle));
+ if (!mipi)
+ return -ENODEV;
+ return sh_mipi_send_short(mipi, MIPI_DSI_DCS_SHORT_WRITE_PARAM, cmd,
+ param);
+}
+
+static void sh_mipi_dsi_enable(struct sh_mipi *mipi, bool enable)
+{
+ /*
+ * enable LCDC data tx, transition to LPS after completion of each HS
+ * packet
+ */
+ iowrite32(0x00000002 | enable, mipi->base + 0x8000); /* DTCTR */
+}
+
+static void sh_mipi_shutdown(struct platform_device *pdev)
+{
+ struct sh_mipi *mipi = platform_get_drvdata(pdev);
+
+ sh_mipi_dsi_enable(mipi, false);
+}
+
+static void mipi_display_on(void *arg, struct fb_info *info)
+{
+ struct sh_mipi *mipi = arg;
+
+ sh_mipi_dsi_enable(mipi, true);
+}
+
+static void mipi_display_off(void *arg)
+{
+ struct sh_mipi *mipi = arg;
+
+ sh_mipi_dsi_enable(mipi, false);
+}
+
+static int __init sh_mipi_setup(struct sh_mipi *mipi,
+ struct sh_mipi_dsi_info *pdata)
+{
+ void __iomem *base = mipi->base;
+ struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan;
+ u32 pctype, datatype, pixfmt;
+ u32 linelength;
+ bool yuv;
+
+ /* Select data format */
+ switch (pdata->data_format) {
+ case MIPI_RGB888:
+ pctype = 0;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24;
+ pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
+ linelength = ch->lcd_cfg.xres * 3;
+ yuv = false;
+ break;
+ case MIPI_RGB565:
+ pctype = 1;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16;
+ pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
+ linelength = ch->lcd_cfg.xres * 2;
+ yuv = false;
+ break;
+ case MIPI_RGB666_LP:
+ pctype = 2;
+ datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
+ pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
+ linelength = ch->lcd_cfg.xres * 3;
+ yuv = false;
+ break;
+ case MIPI_RGB666:
+ pctype = 3;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18;
+ pixfmt = MIPI_DCS_PIXEL_FMT_18BIT;
+ linelength = (ch->lcd_cfg.xres * 18 + 7) / 8;
+ yuv = false;
+ break;
+ case MIPI_BGR888:
+ pctype = 8;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24;
+ pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
+ linelength = ch->lcd_cfg.xres * 3;
+ yuv = false;
+ break;
+ case MIPI_BGR565:
+ pctype = 9;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16;
+ pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
+ linelength = ch->lcd_cfg.xres * 2;
+ yuv = false;
+ break;
+ case MIPI_BGR666_LP:
+ pctype = 0xa;
+ datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
+ pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
+ linelength = ch->lcd_cfg.xres * 3;
+ yuv = false;
+ break;
+ case MIPI_BGR666:
+ pctype = 0xb;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18;
+ pixfmt = MIPI_DCS_PIXEL_FMT_18BIT;
+ linelength = (ch->lcd_cfg.xres * 18 + 7) / 8;
+ yuv = false;
+ break;
+ case MIPI_YUYV:
+ pctype = 4;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16;
+ pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
+ linelength = ch->lcd_cfg.xres * 2;
+ yuv = true;
+ break;
+ case MIPI_UYVY:
+ pctype = 5;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16;
+ pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
+ linelength = ch->lcd_cfg.xres * 2;
+ yuv = true;
+ break;
+ case MIPI_YUV420_L:
+ pctype = 6;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12;
+ pixfmt = MIPI_DCS_PIXEL_FMT_12BIT;
+ linelength = (ch->lcd_cfg.xres * 12 + 7) / 8;
+ yuv = true;
+ break;
+ case MIPI_YUV420:
+ pctype = 7;
+ datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12;
+ pixfmt = MIPI_DCS_PIXEL_FMT_12BIT;
+ /* Length of U/V line */
+ linelength = (ch->lcd_cfg.xres + 1) / 2;
+ yuv = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((yuv && ch->interface_type != YUV422) ||
+ (!yuv && ch->interface_type != RGB24))
+ return -EINVAL;
+
+ /* reset DSI link */
+ iowrite32(0x00000001, base); /* SYSCTRL */
+ /* Hold reset for 100 cycles of the slowest of bus, HS byte and LP clock */
+ udelay(50);
+ iowrite32(0x00000000, base); /* SYSCTRL */
+
+ /* setup DSI link */
+
+ /*
+ * Default = ULPS enable |
+ * Contention detection enabled |
+ * EoT packet transmission enable |
+ * CRC check enable |
+ * ECC check enable
+ * additionally enable first two lanes
+ */
+ iowrite32(0x00003703, base + 0x04); /* SYSCONF */
+ /*
+ * T_wakeup = 0x7000
+ * T_hs-trail = 3
+ * T_hs-prepare = 3
+ * T_clk-trail = 3
+ * T_clk-prepare = 2
+ */
+ iowrite32(0x70003332, base + 0x08); /* TIMSET */
+ /* no responses requested */
+ iowrite32(0x00000000, base + 0x18); /* RESREQSET0 */
+ /* request response to packets of type 0x28 */
+ iowrite32(0x00000100, base + 0x1c); /* RESREQSET1 */
+ /* High-speed transmission timeout, default 0xffffffff */
+ iowrite32(0x0fffffff, base + 0x20); /* HSTTOVSET */
+ /* LP reception timeout, default 0xffffffff */
+ iowrite32(0x0fffffff, base + 0x24); /* LPRTOVSET */
+ /* Turn-around timeout, default 0xffffffff */
+ iowrite32(0x0fffffff, base + 0x28); /* TATOVSET */
+ /* Peripheral reset timeout, default 0xffffffff */
+ iowrite32(0x0fffffff, base + 0x2c); /* PRTOVSET */
+ /* Enable timeout counters */
+ iowrite32(0x00000f00, base + 0x30); /* DSICTRL */
+ /* Interrupts not used, disable all */
+ iowrite32(0, base + DSIINTE);
+ /* DSI-Tx bias on */
+ iowrite32(0x00000001, base + 0x70); /* PHYCTRL */
+ udelay(200);
+ /* Deassert resets, power on, set multiplier */
+ iowrite32(0x03070b01, base + 0x70); /* PHYCTRL */
+
+ /* setup l-bridge */
+
+ /*
+ * Enable transmission of all packets,
+ * transmit LPS after each HS packet completion
+ */
+ iowrite32(0x00000006, base + 0x8000); /* DTCTR */
+ /* VSYNC width = 2 (<< 17) */
+ iowrite32(0x00040000 | (pctype << 12) | datatype, base + 0x8020); /* VMCTR1 */
+ /*
+ * Non-burst mode with sync pulses: VSE and HSE are output,
+ * HSA period allowed, no commands in LP
+ */
+ iowrite32(0x00e00000, base + 0x8024); /* VMCTR2 */
+ /*
+ * 0x660 = 1632 bytes per line (RGB24, 544 pixels: see
+ * sh_mobile_lcdc_info.ch[0].lcd_cfg.xres), HSALEN = 1 - default
+ * (unused, since VMCTR2[HSABM] = 0)
+ */
+ iowrite32(1 | (linelength << 16), base + 0x8028); /* VMLEN1 */
+
+ msleep(5);
+
+ /* setup LCD panel */
+
+ /* cf. drivers/video/omap/lcd_mipid.c */
+ sh_mipi_dcs(ch->chan, MIPI_DCS_EXIT_SLEEP_MODE);
+ msleep(120);
+ /*
+ * [7] - Page Address Mode
+ * [6] - Column Address Mode
+ * [5] - Page / Column Address Mode
+ * [4] - Display Device Line Refresh Order
+ * [3] - RGB/BGR Order
+ * [2] - Display Data Latch Data Order
+ * [1] - Flip Horizontal
+ * [0] - Flip Vertical
+ */
+ sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
+ /* cf. set_data_lines() */
+ sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_PIXEL_FORMAT,
+ pixfmt << 4);
+ sh_mipi_dcs(ch->chan, MIPI_DCS_SET_DISPLAY_ON);
+
+ return 0;
+}
+
+static int __init sh_mipi_probe(struct platform_device *pdev)
+{
+ struct sh_mipi *mipi;
+ struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ unsigned long rate, f_current;
+ int idx = pdev->id, ret;
+ char dsip_clk[] = "dsi.p_clk";
+
+ if (!res || idx >= ARRAY_SIZE(mipi_dsi) || !pdata)
+ return -ENODEV;
+
+ mutex_lock(&array_lock);
+ if (idx < 0)
+ for (idx = 0; idx < ARRAY_SIZE(mipi_dsi) && mipi_dsi[idx]; idx++)
+ ;
+
+ if (idx == ARRAY_SIZE(mipi_dsi)) {
+ ret = -EBUSY;
+ goto efindslot;
+ }
+
+ mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
+ if (!mipi) {
+ ret = -ENOMEM;
+ goto ealloc;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+ dev_err(&pdev->dev, "MIPI register region already claimed\n");
+ ret = -EBUSY;
+ goto ereqreg;
+ }
+
+ mipi->base = ioremap(res->start, resource_size(res));
+ if (!mipi->base) {
+ ret = -ENOMEM;
+ goto emap;
+ }
+
+ mipi->dsit_clk = clk_get(&pdev->dev, "dsit_clk");
+ if (IS_ERR(mipi->dsit_clk)) {
+ ret = PTR_ERR(mipi->dsit_clk);
+ goto eclktget;
+ }
+
+ f_current = clk_get_rate(mipi->dsit_clk);
+ /* 80MHz required by the datasheet */
+ rate = clk_round_rate(mipi->dsit_clk, 80000000);
+ if (rate > 0 && rate != f_current)
+ ret = clk_set_rate(mipi->dsit_clk, rate);
+ else
+ ret = rate;
+ if (ret < 0)
+ goto esettrate;
+
+ dev_dbg(&pdev->dev, "DSI-T clk %lu -> %lu\n", f_current, rate);
+
+ sprintf(dsip_clk, "dsi%1.1dp_clk", idx);
+ mipi->dsip_clk = clk_get(&pdev->dev, dsip_clk);
+ if (IS_ERR(mipi->dsip_clk)) {
+ ret = PTR_ERR(mipi->dsip_clk);
+ goto eclkpget;
+ }
+
+ f_current = clk_get_rate(mipi->dsip_clk);
+ /* Between 10 and 50MHz */
+ rate = clk_round_rate(mipi->dsip_clk, 24000000);
+ if (rate > 0 && rate != f_current)
+ ret = clk_set_rate(mipi->dsip_clk, rate);
+ else
+ ret = rate;
+ if (ret < 0)
+ goto esetprate;
+
+ dev_dbg(&pdev->dev, "DSI-P clk %lu -> %lu\n", f_current, rate);
+
+ msleep(10);
+
+ ret = clk_enable(mipi->dsit_clk);
+ if (ret < 0)
+ goto eclkton;
+
+ ret = clk_enable(mipi->dsip_clk);
+ if (ret < 0)
+ goto eclkpon;
+
+ mipi_dsi[idx] = mipi;
+
+ ret = sh_mipi_setup(mipi, pdata);
+ if (ret < 0)
+ goto emipisetup;
+
+ mutex_unlock(&array_lock);
+ platform_set_drvdata(pdev, mipi);
+
+ /* Set up LCDC callbacks */
+ pdata->lcd_chan->board_cfg.board_data = mipi;
+ pdata->lcd_chan->board_cfg.display_on = mipi_display_on;
+ pdata->lcd_chan->board_cfg.display_off = mipi_display_off;
+
+ return 0;
+
+emipisetup:
+ mipi_dsi[idx] = NULL;
+ clk_disable(mipi->dsip_clk);
+eclkpon:
+ clk_disable(mipi->dsit_clk);
+eclkton:
+esetprate:
+ clk_put(mipi->dsip_clk);
+eclkpget:
+esettrate:
+ clk_put(mipi->dsit_clk);
+eclktget:
+ iounmap(mipi->base);
+emap:
+ release_mem_region(res->start, resource_size(res));
+ereqreg:
+ kfree(mipi);
+ealloc:
+efindslot:
+ mutex_unlock(&array_lock);
+
+ return ret;
+}
+
+static int __exit sh_mipi_remove(struct platform_device *pdev)
+{
+ struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct sh_mipi *mipi = platform_get_drvdata(pdev);
+ int i, ret;
+
+ mutex_lock(&array_lock);
+
+ for (i = 0; i < ARRAY_SIZE(mipi_dsi) && mipi_dsi[i] != mipi; i++)
+ ;
+
+ if (i == ARRAY_SIZE(mipi_dsi)) {
+ ret = -EINVAL;
+ } else {
+ ret = 0;
+ mipi_dsi[i] = NULL;
+ }
+
+ mutex_unlock(&array_lock);
+
+ if (ret < 0)
+ return ret;
+
+ pdata->lcd_chan->board_cfg.display_on = NULL;
+ pdata->lcd_chan->board_cfg.display_off = NULL;
+ pdata->lcd_chan->board_cfg.board_data = NULL;
+
+ clk_disable(mipi->dsip_clk);
+ clk_disable(mipi->dsit_clk);
+ clk_put(mipi->dsit_clk);
+ clk_put(mipi->dsip_clk);
+ iounmap(mipi->base);
+ if (res)
+ release_mem_region(res->start, resource_size(res));
+ platform_set_drvdata(pdev, NULL);
+ kfree(mipi);
+
+ return 0;
+}
+
+static struct platform_driver sh_mipi_driver = {
+ .remove = __exit_p(sh_mipi_remove),
+ .shutdown = sh_mipi_shutdown,
+ .driver = {
+ .name = "sh-mipi-dsi",
+ },
+};
+
+static int __init sh_mipi_init(void)
+{
+ return platform_driver_probe(&sh_mipi_driver, sh_mipi_probe);
+}
+module_init(sh_mipi_init);
+
+static void __exit sh_mipi_exit(void)
+{
+ platform_driver_unregister(&sh_mipi_driver);
+}
+module_exit(sh_mipi_exit);
+
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_DESCRIPTION("SuperH / ARM-shmobile MIPI DSI driver");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * SH-Mobile High-Definition Multimedia Interface (HDMI) driver
+ * for SLISHDMI13T and SLIPHDMIT IP cores
+ *
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include <video/sh_mobile_hdmi.h>
+#include <video/sh_mobile_lcdc.h>
+
+#define HDMI_SYSTEM_CTRL 0x00 /* System control */
+#define HDMI_L_R_DATA_SWAP_CTRL_RPKT 0x01 /* L/R data swap control,
+ bits 19..16 of 20-bit N for Audio Clock Regeneration packet */
+#define HDMI_20_BIT_N_FOR_AUDIO_RPKT_15_8 0x02 /* bits 15..8 of 20-bit N for Audio Clock Regeneration packet */
+#define HDMI_20_BIT_N_FOR_AUDIO_RPKT_7_0 0x03 /* bits 7..0 of 20-bit N for Audio Clock Regeneration packet */
+#define HDMI_SPDIF_AUDIO_SAMP_FREQ_CTS 0x04 /* SPDIF audio sampling frequency,
+ bits 19..16 of Internal CTS */
+#define HDMI_INTERNAL_CTS_15_8 0x05 /* bits 15..8 of Internal CTS */
+#define HDMI_INTERNAL_CTS_7_0 0x06 /* bits 7..0 of Internal CTS */
+#define HDMI_EXTERNAL_CTS_19_16 0x07 /* External CTS */
+#define HDMI_EXTERNAL_CTS_15_8 0x08 /* External CTS */
+#define HDMI_EXTERNAL_CTS_7_0 0x09 /* External CTS */
+#define HDMI_AUDIO_SETTING_1 0x0A /* Audio setting.1 */
+#define HDMI_AUDIO_SETTING_2 0x0B /* Audio setting.2 */
+#define HDMI_I2S_AUDIO_SET 0x0C /* I2S audio setting */
+#define HDMI_DSD_AUDIO_SET 0x0D /* DSD audio setting */
+#define HDMI_DEBUG_MONITOR_1 0x0E /* Debug monitor.1 */
+#define HDMI_DEBUG_MONITOR_2 0x0F /* Debug monitor.2 */
+#define HDMI_I2S_INPUT_PIN_SWAP 0x10 /* I2S input pin swap */
+#define HDMI_AUDIO_STATUS_BITS_SETTING_1 0x11 /* Audio status bits setting.1 */
+#define HDMI_AUDIO_STATUS_BITS_SETTING_2 0x12 /* Audio status bits setting.2 */
+#define HDMI_CATEGORY_CODE 0x13 /* Category code */
+#define HDMI_SOURCE_NUM_AUDIO_WORD_LEN 0x14 /* Source number/Audio word length */
+#define HDMI_AUDIO_VIDEO_SETTING_1 0x15 /* Audio/Video setting.1 */
+#define HDMI_VIDEO_SETTING_1 0x16 /* Video setting.1 */
+#define HDMI_DEEP_COLOR_MODES 0x17 /* Deep Color Modes */
+
+/* 12 16- and 10-bit Color space conversion parameters: 0x18..0x2f */
+#define HDMI_COLOR_SPACE_CONVERSION_PARAMETERS 0x18
+
+#define HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS 0x30 /* External video parameter settings */
+#define HDMI_EXTERNAL_H_TOTAL_7_0 0x31 /* External horizontal total (LSB) */
+#define HDMI_EXTERNAL_H_TOTAL_11_8 0x32 /* External horizontal total (MSB) */
+#define HDMI_EXTERNAL_H_BLANK_7_0 0x33 /* External horizontal blank (LSB) */
+#define HDMI_EXTERNAL_H_BLANK_9_8 0x34 /* External horizontal blank (MSB) */
+#define HDMI_EXTERNAL_H_DELAY_7_0 0x35 /* External horizontal delay (LSB) */
+#define HDMI_EXTERNAL_H_DELAY_9_8 0x36 /* External horizontal delay (MSB) */
+#define HDMI_EXTERNAL_H_DURATION_7_0 0x37 /* External horizontal duration (LSB) */
+#define HDMI_EXTERNAL_H_DURATION_9_8 0x38 /* External horizontal duration (MSB) */
+#define HDMI_EXTERNAL_V_TOTAL_7_0 0x39 /* External vertical total (LSB) */
+#define HDMI_EXTERNAL_V_TOTAL_9_8 0x3A /* External vertical total (MSB) */
+#define HDMI_AUDIO_VIDEO_SETTING_2 0x3B /* Audio/Video setting.2 */
+#define HDMI_EXTERNAL_V_BLANK 0x3D /* External vertical blank */
+#define HDMI_EXTERNAL_V_DELAY 0x3E /* External vertical delay */
+#define HDMI_EXTERNAL_V_DURATION 0x3F /* External vertical duration */
+#define HDMI_CTRL_PKT_MANUAL_SEND_CONTROL 0x40 /* Control packet manual send control */
+#define HDMI_CTRL_PKT_AUTO_SEND 0x41 /* Control packet auto send with VSYNC control */
+#define HDMI_AUTO_CHECKSUM_OPTION 0x42 /* Auto checksum option */
+#define HDMI_VIDEO_SETTING_2 0x45 /* Video setting.2 */
+#define HDMI_OUTPUT_OPTION 0x46 /* Output option */
+#define HDMI_SLIPHDMIT_PARAM_OPTION 0x51 /* SLIPHDMIT parameter option */
+#define HDMI_HSYNC_PMENT_AT_EMB_7_0 0x52 /* HSYNC placement at embedded sync (LSB) */
+#define HDMI_HSYNC_PMENT_AT_EMB_15_8 0x53 /* HSYNC placement at embedded sync (MSB) */
+#define HDMI_VSYNC_PMENT_AT_EMB_7_0 0x54 /* VSYNC placement at embedded sync (LSB) */
+#define HDMI_VSYNC_PMENT_AT_EMB_14_8 0x55 /* VSYNC placement at embedded sync (MSB) */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_1 0x56 /* SLIPHDMIT parameter settings.1 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_2 0x57 /* SLIPHDMIT parameter settings.2 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_3 0x58 /* SLIPHDMIT parameter settings.3 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_5 0x59 /* SLIPHDMIT parameter settings.5 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_6 0x5A /* SLIPHDMIT parameter settings.6 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_7 0x5B /* SLIPHDMIT parameter settings.7 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_8 0x5C /* SLIPHDMIT parameter settings.8 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_9 0x5D /* SLIPHDMIT parameter settings.9 */
+#define HDMI_SLIPHDMIT_PARAM_SETTINGS_10 0x5E /* SLIPHDMIT parameter settings.10 */
+#define HDMI_CTRL_PKT_BUF_INDEX 0x5F /* Control packet buffer index */
+#define HDMI_CTRL_PKT_BUF_ACCESS_HB0 0x60 /* Control packet data buffer access window - HB0 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_HB1 0x61 /* Control packet data buffer access window - HB1 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_HB2 0x62 /* Control packet data buffer access window - HB2 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB0 0x63 /* Control packet data buffer access window - PB0 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB1 0x64 /* Control packet data buffer access window - PB1 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB2 0x65 /* Control packet data buffer access window - PB2 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB3 0x66 /* Control packet data buffer access window - PB3 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB4 0x67 /* Control packet data buffer access window - PB4 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB5 0x68 /* Control packet data buffer access window - PB5 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB6 0x69 /* Control packet data buffer access window - PB6 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB7 0x6A /* Control packet data buffer access window - PB7 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB8 0x6B /* Control packet data buffer access window - PB8 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB9 0x6C /* Control packet data buffer access window - PB9 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB10 0x6D /* Control packet data buffer access window - PB10 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB11 0x6E /* Control packet data buffer access window - PB11 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB12 0x6F /* Control packet data buffer access window - PB12 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB13 0x70 /* Control packet data buffer access window - PB13 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB14 0x71 /* Control packet data buffer access window - PB14 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB15 0x72 /* Control packet data buffer access window - PB15 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB16 0x73 /* Control packet data buffer access window - PB16 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB17 0x74 /* Control packet data buffer access window - PB17 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB18 0x75 /* Control packet data buffer access window - PB18 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB19 0x76 /* Control packet data buffer access window - PB19 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB20 0x77 /* Control packet data buffer access window - PB20 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB21 0x78 /* Control packet data buffer access window - PB21 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB22 0x79 /* Control packet data buffer access window - PB22 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB23 0x7A /* Control packet data buffer access window - PB23 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB24 0x7B /* Control packet data buffer access window - PB24 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB25 0x7C /* Control packet data buffer access window - PB25 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB26 0x7D /* Control packet data buffer access window - PB26 */
+#define HDMI_CTRL_PKT_BUF_ACCESS_PB27 0x7E /* Control packet data buffer access window - PB27 */
+#define HDMI_EDID_KSV_FIFO_ACCESS_WINDOW 0x80 /* EDID/KSV FIFO access window */
+#define HDMI_DDC_BUS_ACCESS_FREQ_CTRL_7_0 0x81 /* DDC bus access frequency control (LSB) */
+#define HDMI_DDC_BUS_ACCESS_FREQ_CTRL_15_8 0x82 /* DDC bus access frequency control (MSB) */
+#define HDMI_INTERRUPT_MASK_1 0x92 /* Interrupt mask.1 */
+#define HDMI_INTERRUPT_MASK_2 0x93 /* Interrupt mask.2 */
+#define HDMI_INTERRUPT_STATUS_1 0x94 /* Interrupt status.1 */
+#define HDMI_INTERRUPT_STATUS_2 0x95 /* Interrupt status.2 */
+#define HDMI_INTERRUPT_MASK_3 0x96 /* Interrupt mask.3 */
+#define HDMI_INTERRUPT_MASK_4 0x97 /* Interrupt mask.4 */
+#define HDMI_INTERRUPT_STATUS_3 0x98 /* Interrupt status.3 */
+#define HDMI_INTERRUPT_STATUS_4 0x99 /* Interrupt status.4 */
+#define HDMI_SOFTWARE_HDCP_CONTROL_1 0x9A /* Software HDCP control.1 */
+#define HDMI_FRAME_COUNTER 0x9C /* Frame counter */
+#define HDMI_FRAME_COUNTER_FOR_RI_CHECK 0x9D /* Frame counter for Ri check */
+#define HDMI_HDCP_CONTROL 0xAF /* HDCP control */
+#define HDMI_RI_FRAME_COUNT_REGISTER 0xB2 /* Ri frame count register */
+#define HDMI_DDC_BUS_CONTROL 0xB7 /* DDC bus control */
+#define HDMI_HDCP_STATUS 0xB8 /* HDCP status */
+#define HDMI_SHA0 0xB9 /* sha0 */
+#define HDMI_SHA1 0xBA /* sha1 */
+#define HDMI_SHA2 0xBB /* sha2 */
+#define HDMI_SHA3 0xBC /* sha3 */
+#define HDMI_SHA4 0xBD /* sha4 */
+#define HDMI_BCAPS_READ 0xBE /* BCAPS read / debug */
+#define HDMI_AKSV_BKSV_7_0_MONITOR 0xBF /* AKSV/BKSV[7:0] monitor */
+#define HDMI_AKSV_BKSV_15_8_MONITOR 0xC0 /* AKSV/BKSV[15:8] monitor */
+#define HDMI_AKSV_BKSV_23_16_MONITOR 0xC1 /* AKSV/BKSV[23:16] monitor */
+#define HDMI_AKSV_BKSV_31_24_MONITOR 0xC2 /* AKSV/BKSV[31:24] monitor */
+#define HDMI_AKSV_BKSV_39_32_MONITOR 0xC3 /* AKSV/BKSV[39:32] monitor */
+#define HDMI_EDID_SEGMENT_POINTER 0xC4 /* EDID segment pointer */
+#define HDMI_EDID_WORD_ADDRESS 0xC5 /* EDID word address */
+#define HDMI_EDID_DATA_FIFO_ADDRESS 0xC6 /* EDID data FIFO address */
+#define HDMI_NUM_OF_HDMI_DEVICES 0xC7 /* Number of HDMI devices */
+#define HDMI_HDCP_ERROR_CODE 0xC8 /* HDCP error code */
+#define HDMI_100MS_TIMER_SET 0xC9 /* 100ms timer setting */
+#define HDMI_5SEC_TIMER_SET 0xCA /* 5sec timer setting */
+#define HDMI_RI_READ_COUNT 0xCB /* Ri read count */
+#define HDMI_AN_SEED 0xCC /* An seed */
+#define HDMI_MAX_NUM_OF_RCIVRS_ALLOWED 0xCD /* Maximum number of receivers allowed */
+#define HDMI_HDCP_MEMORY_ACCESS_CONTROL_1 0xCE /* HDCP memory access control.1 */
+#define HDMI_HDCP_MEMORY_ACCESS_CONTROL_2 0xCF /* HDCP memory access control.2 */
+#define HDMI_HDCP_CONTROL_2 0xD0 /* HDCP Control 2 */
+#define HDMI_HDCP_KEY_MEMORY_CONTROL 0xD2 /* HDCP Key Memory Control */
+#define HDMI_COLOR_SPACE_CONV_CONFIG_1 0xD3 /* Color space conversion configuration.1 */
+#define HDMI_VIDEO_SETTING_3 0xD4 /* Video setting.3 */
+#define HDMI_RI_7_0 0xD5 /* Ri[7:0] */
+#define HDMI_RI_15_8 0xD6 /* Ri[15:8] */
+#define HDMI_PJ 0xD7 /* Pj */
+#define HDMI_SHA_RD 0xD8 /* sha_rd */
+#define HDMI_RI_7_0_SAVED 0xD9 /* Ri[7:0] saved */
+#define HDMI_RI_15_8_SAVED 0xDA /* Ri[15:8] saved */
+#define HDMI_PJ_SAVED 0xDB /* Pj saved */
+#define HDMI_NUM_OF_DEVICES 0xDC /* Number of devices */
+#define HDMI_HOT_PLUG_MSENS_STATUS 0xDF /* Hot plug/MSENS status */
+#define HDMI_BCAPS_WRITE 0xE0 /* bcaps */
+#define HDMI_BSTAT_7_0 0xE1 /* bstat[7:0] */
+#define HDMI_BSTAT_15_8 0xE2 /* bstat[15:8] */
+#define HDMI_BKSV_7_0 0xE3 /* bksv[7:0] */
+#define HDMI_BKSV_15_8 0xE4 /* bksv[15:8] */
+#define HDMI_BKSV_23_16 0xE5 /* bksv[23:16] */
+#define HDMI_BKSV_31_24 0xE6 /* bksv[31:24] */
+#define HDMI_BKSV_39_32 0xE7 /* bksv[39:32] */
+#define HDMI_AN_7_0 0xE8 /* An[7:0] */
+#define HDMI_AN_15_8 0xE9 /* An [15:8] */
+#define HDMI_AN_23_16 0xEA /* An [23:16] */
+#define HDMI_AN_31_24 0xEB /* An [31:24] */
+#define HDMI_AN_39_32 0xEC /* An [39:32] */
+#define HDMI_AN_47_40 0xED /* An [47:40] */
+#define HDMI_AN_55_48 0xEE /* An [55:48] */
+#define HDMI_AN_63_56 0xEF /* An [63:56] */
+#define HDMI_PRODUCT_ID 0xF0 /* Product ID */
+#define HDMI_REVISION_ID 0xF1 /* Revision ID */
+#define HDMI_TEST_MODE 0xFE /* Test mode */
+
+enum hotplug_state {
+ HDMI_HOTPLUG_DISCONNECTED,
+ HDMI_HOTPLUG_CONNECTED,
+ HDMI_HOTPLUG_EDID_DONE,
+};
+
+struct sh_hdmi {
+ void __iomem *base;
+ enum hotplug_state hp_state;
+ struct clk *hdmi_clk;
+ struct device *dev;
+ struct fb_info *info;
+ struct delayed_work edid_work;
+ struct fb_var_screeninfo var;
+};
+
+static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg)
+{
+ iowrite8(data, hdmi->base + reg);
+}
+
+static u8 hdmi_read(struct sh_hdmi *hdmi, u8 reg)
+{
+ return ioread8(hdmi->base + reg);
+}
+
+/* External video parameter settings */
+static void hdmi_external_video_param(struct sh_hdmi *hdmi)
+{
+ struct fb_var_screeninfo *var = &hdmi->var;
+ u16 htotal, hblank, hdelay, vtotal, vblank, vdelay, voffset;
+ u8 sync = 0;
+
+ htotal = var->xres + var->right_margin + var->left_margin + var->hsync_len;
+
+ hdelay = var->hsync_len + var->left_margin;
+ hblank = var->right_margin + hdelay;
+
+ /*
+ * Vertical timing looks a bit different in Figure 18,
+ * but let's try the same first by setting offset = 0
+ */
+ vtotal = var->yres + var->upper_margin + var->lower_margin + var->vsync_len;
+
+ vdelay = var->vsync_len + var->upper_margin;
+ vblank = var->lower_margin + vdelay;
+ voffset = min(var->upper_margin / 2, 6U);
+
+ /*
+ * [3]: VSYNC polarity: Positive
+ * [2]: HSYNC polarity: Positive
+ * [1]: Interlace/Progressive: Progressive
+ * [0]: External video settings enable: used.
+ */
+ if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+ sync |= 4;
+ if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+ sync |= 8;
+
+ pr_debug("H: %u, %u, %u, %u; V: %u, %u, %u, %u; sync 0x%x\n",
+ htotal, hblank, hdelay, var->hsync_len,
+ vtotal, vblank, vdelay, var->vsync_len, sync);
+
+ hdmi_write(hdmi, sync | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS);
+
+ hdmi_write(hdmi, htotal, HDMI_EXTERNAL_H_TOTAL_7_0);
+ hdmi_write(hdmi, htotal >> 8, HDMI_EXTERNAL_H_TOTAL_11_8);
+
+ hdmi_write(hdmi, hblank, HDMI_EXTERNAL_H_BLANK_7_0);
+ hdmi_write(hdmi, hblank >> 8, HDMI_EXTERNAL_H_BLANK_9_8);
+
+ hdmi_write(hdmi, hdelay, HDMI_EXTERNAL_H_DELAY_7_0);
+ hdmi_write(hdmi, hdelay >> 8, HDMI_EXTERNAL_H_DELAY_9_8);
+
+ hdmi_write(hdmi, var->hsync_len, HDMI_EXTERNAL_H_DURATION_7_0);
+ hdmi_write(hdmi, var->hsync_len >> 8, HDMI_EXTERNAL_H_DURATION_9_8);
+
+ hdmi_write(hdmi, vtotal, HDMI_EXTERNAL_V_TOTAL_7_0);
+ hdmi_write(hdmi, vtotal >> 8, HDMI_EXTERNAL_V_TOTAL_9_8);
+
+ hdmi_write(hdmi, vblank, HDMI_EXTERNAL_V_BLANK);
+
+ hdmi_write(hdmi, vdelay, HDMI_EXTERNAL_V_DELAY);
+
+ hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION);
+
+ /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for manual mode */
+}
+
+/**
+ * sh_hdmi_video_config()
+ */
+static void sh_hdmi_video_config(struct sh_hdmi *hdmi)
+{
+ /*
+ * [7:4]: Audio sampling frequency: 48kHz
+ * [3:1]: Input video format: RGB and YCbCr 4:4:4 (Y on Green)
+ * [0]: Internal/External DE select: internal
+ */
+ hdmi_write(hdmi, 0x20, HDMI_AUDIO_VIDEO_SETTING_1);
+
+ /*
+ * [7:6]: Video output format: RGB 4:4:4
+ * [5:4]: Input video data width: 8 bit
+ * [3:1]: EAV/SAV location: channel 1
+ * [0]: Video input color space: RGB
+ */
+ hdmi_write(hdmi, 0x34, HDMI_VIDEO_SETTING_1);
+
+ /*
+ * [7:6]: Together with bit [6] of HDMI_AUDIO_VIDEO_SETTING_2, which is
+ * left at 0 by default, this configures 24bpp and sets the Color Depth
+ * (CD) field in the General Control Packet
+ */
+ hdmi_write(hdmi, 0x20, HDMI_DEEP_COLOR_MODES);
+}
+
+/**
+ * sh_hdmi_audio_config()
+ */
+static void sh_hdmi_audio_config(struct sh_hdmi *hdmi)
+{
+ /*
+ * [7:4] L/R data swap control
+ * [3:0] appropriate N[19:16]
+ */
+ hdmi_write(hdmi, 0x00, HDMI_L_R_DATA_SWAP_CTRL_RPKT);
+ /* appropriate N[15:8] */
+ hdmi_write(hdmi, 0x18, HDMI_20_BIT_N_FOR_AUDIO_RPKT_15_8);
+ /* appropriate N[7:0] */
+ hdmi_write(hdmi, 0x00, HDMI_20_BIT_N_FOR_AUDIO_RPKT_7_0);
+
+ /* [7:4] 48 kHz SPDIF not used */
+ hdmi_write(hdmi, 0x20, HDMI_SPDIF_AUDIO_SAMP_FREQ_CTS);
+
+ /*
+ * [6:5] set required down sampling rate if required
+ * [4:3] set required audio source
+ */
+ hdmi_write(hdmi, 0x00, HDMI_AUDIO_SETTING_1);
+
+ /* [3:0] set sending channel number for channel status */
+ hdmi_write(hdmi, 0x40, HDMI_AUDIO_SETTING_2);
+
+ /*
+ * [5:2] set valid I2S source input pin
+ * [1:0] set input I2S source mode
+ */
+ hdmi_write(hdmi, 0x04, HDMI_I2S_AUDIO_SET);
+
+ /* [7:4] set valid DSD source input pin */
+ hdmi_write(hdmi, 0x00, HDMI_DSD_AUDIO_SET);
+
+ /* [7:0] set appropriate I2S input pin swap settings if required */
+ hdmi_write(hdmi, 0x00, HDMI_I2S_INPUT_PIN_SWAP);
+
+ /*
+ * [7] set validity bit for channel status
+ * [3:0] set original sample frequency for channel status
+ */
+ hdmi_write(hdmi, 0x00, HDMI_AUDIO_STATUS_BITS_SETTING_1);
+
+ /*
+ * [7] set value for channel status
+ * [6] set value for channel status
+ * [5] set copyright bit for channel status
+ * [4:2] set additional information for channel status
+ * [1:0] set clock accuracy for channel status
+ */
+ hdmi_write(hdmi, 0x00, HDMI_AUDIO_STATUS_BITS_SETTING_2);
+
+ /* [7:0] set category code for channel status */
+ hdmi_write(hdmi, 0x00, HDMI_CATEGORY_CODE);
+
+ /*
+ * [7:4] set source number for channel status
+ * [3:0] set word length for channel status
+ */
+ hdmi_write(hdmi, 0x00, HDMI_SOURCE_NUM_AUDIO_WORD_LEN);
+
+ /* [7:4] set sample frequency for channel status */
+ hdmi_write(hdmi, 0x20, HDMI_AUDIO_VIDEO_SETTING_1);
+}
+
+/**
+ * sh_hdmi_phy_config()
+ */
+static void sh_hdmi_phy_config(struct sh_hdmi *hdmi)
+{
+ /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */
+ hdmi_write(hdmi, 0x19, HDMI_SLIPHDMIT_PARAM_SETTINGS_1);
+ hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2);
+ hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3);
+ /* PLLA_CONFIG[7:0]: VCO gain, VCO offset, LPF resistance[0] */
+ hdmi_write(hdmi, 0x44, HDMI_SLIPHDMIT_PARAM_SETTINGS_5);
+ hdmi_write(hdmi, 0x32, HDMI_SLIPHDMIT_PARAM_SETTINGS_6);
+ hdmi_write(hdmi, 0x4A, HDMI_SLIPHDMIT_PARAM_SETTINGS_7);
+ hdmi_write(hdmi, 0x0E, HDMI_SLIPHDMIT_PARAM_SETTINGS_8);
+ hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9);
+ hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10);
+}
+
+/**
+ * sh_hdmi_avi_infoframe_setup() - Auxiliary Video Information InfoFrame CONTROL PACKET
+ */
+static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi)
+{
+ /* AVI InfoFrame */
+ hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_INDEX);
+
+ /* Packet Type = 0x82 */
+ hdmi_write(hdmi, 0x82, HDMI_CTRL_PKT_BUF_ACCESS_HB0);
+
+ /* Version = 0x02 */
+ hdmi_write(hdmi, 0x02, HDMI_CTRL_PKT_BUF_ACCESS_HB1);
+
+ /* Length = 13 (0x0D) */
+ hdmi_write(hdmi, 0x0D, HDMI_CTRL_PKT_BUF_ACCESS_HB2);
+
+ /* N. A. Checksum */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0);
+
+ /*
+ * Y = RGB
+ * A0 = No Data
+ * B = Bar Data not valid
+ * S = No Data
+ */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB1);
+
+ /*
+ * C = No Data
+ * M = 16:9 Picture Aspect Ratio
+ * R = Same as picture aspect ratio
+ */
+ hdmi_write(hdmi, 0x28, HDMI_CTRL_PKT_BUF_ACCESS_PB2);
+
+ /*
+ * ITC = No Data
+ * EC = xvYCC601
+ * Q = Default (depends on video format)
+ * SC = No Known non_uniform Scaling
+ */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3);
+
+ /*
+ * VIC = 1280 x 720p: ignored if external config is used
+ * Send 2 for 720 x 480p, 16 for 1080p
+ */
+ hdmi_write(hdmi, 4, HDMI_CTRL_PKT_BUF_ACCESS_PB4);
+
+ /* PR = No Repetition */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB5);
+
+ /* Line Number of End of Top Bar (lower 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB6);
+
+ /* Line Number of End of Top Bar (upper 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB7);
+
+ /* Line Number of Start of Bottom Bar (lower 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB8);
+
+ /* Line Number of Start of Bottom Bar (upper 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB9);
+
+ /* Pixel Number of End of Left Bar (lower 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB10);
+
+ /* Pixel Number of End of Left Bar (upper 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB11);
+
+ /* Pixel Number of Start of Right Bar (lower 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB12);
+
+ /* Pixel Number of Start of Right Bar (upper 8 bits) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB13);
+}
+
+/**
+ * sh_hdmi_audio_infoframe_setup() - Audio InfoFrame of CONTROL PACKET
+ */
+static void sh_hdmi_audio_infoframe_setup(struct sh_hdmi *hdmi)
+{
+ /* Audio InfoFrame */
+ hdmi_write(hdmi, 0x08, HDMI_CTRL_PKT_BUF_INDEX);
+
+ /* Packet Type = 0x84 */
+ hdmi_write(hdmi, 0x84, HDMI_CTRL_PKT_BUF_ACCESS_HB0);
+
+ /* Version Number = 0x01 */
+ hdmi_write(hdmi, 0x01, HDMI_CTRL_PKT_BUF_ACCESS_HB1);
+
+ /* 0 Length = 10 (0x0A) */
+ hdmi_write(hdmi, 0x0A, HDMI_CTRL_PKT_BUF_ACCESS_HB2);
+
+ /* n. a. Checksum */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0);
+
+ /* Audio Channel Count = Refer to Stream Header */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB1);
+
+ /* Refer to Stream Header */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB2);
+
+ /* Format depends on coding type (i.e. CT0...CT3) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3);
+
+ /* Speaker Channel Allocation = Front Right + Front Left */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB4);
+
+ /* Level Shift Value = 0 dB, Down - mix is permitted or no information */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB5);
+
+ /* Reserved (0) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB6);
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB7);
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB8);
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB9);
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB10);
+}
+
+/**
+ * sh_hdmi_gamut_metadata_setup() - Gamut Metadata Packet of CONTROL PACKET
+ */
+static void sh_hdmi_gamut_metadata_setup(struct sh_hdmi *hdmi)
+{
+ int i;
+
+ /* Gamut Metadata Packet */
+ hdmi_write(hdmi, 0x04, HDMI_CTRL_PKT_BUF_INDEX);
+
+ /* Packet Type = 0x0A */
+ hdmi_write(hdmi, 0x0A, HDMI_CTRL_PKT_BUF_ACCESS_HB0);
+ /* Gamut Packet is not used, so default value */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1);
+ /* Gamut Packet is not used, so default value */
+ hdmi_write(hdmi, 0x10, HDMI_CTRL_PKT_BUF_ACCESS_HB2);
+
+ /* GBD bytes 0 through 27 */
+ for (i = 0; i <= 27; i++)
+ /* HDMI_CTRL_PKT_BUF_ACCESS_PB0_63H - PB27_7EH */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i);
+}
+
+/**
+ * sh_hdmi_acp_setup() - Audio Content Protection Packet (ACP)
+ */
+static void sh_hdmi_acp_setup(struct sh_hdmi *hdmi)
+{
+ int i;
+
+ /* Audio Content Protection Packet (ACP) */
+ hdmi_write(hdmi, 0x01, HDMI_CTRL_PKT_BUF_INDEX);
+
+ /* Packet Type = 0x04 */
+ hdmi_write(hdmi, 0x04, HDMI_CTRL_PKT_BUF_ACCESS_HB0);
+ /* ACP_Type */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1);
+ /* Reserved (0) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2);
+
+ /* GBD bytes 0 through 27 */
+ for (i = 0; i <= 27; i++)
+ /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i);
+}
+
+/**
+ * sh_hdmi_isrc1_setup() - ISRC1 Packet
+ */
+static void sh_hdmi_isrc1_setup(struct sh_hdmi *hdmi)
+{
+ int i;
+
+ /* ISRC1 Packet */
+ hdmi_write(hdmi, 0x02, HDMI_CTRL_PKT_BUF_INDEX);
+
+ /* Packet Type = 0x05 */
+ hdmi_write(hdmi, 0x05, HDMI_CTRL_PKT_BUF_ACCESS_HB0);
+ /* ISRC_Cont, ISRC_Valid, Reserved (0), ISRC_Status */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1);
+ /* Reserved (0) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2);
+
+ /* PB0 UPC_EAN_ISRC_0-15 */
+ /* Bytes PB16-PB27 shall be set to a value of 0. */
+ for (i = 0; i <= 27; i++)
+ /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i);
+}
+
+/**
+ * sh_hdmi_isrc2_setup() - ISRC2 Packet
+ */
+static void sh_hdmi_isrc2_setup(struct sh_hdmi *hdmi)
+{
+ int i;
+
+ /* ISRC2 Packet */
+ hdmi_write(hdmi, 0x03, HDMI_CTRL_PKT_BUF_INDEX);
+
+ /* HB0 Packet Type = 0x06 */
+ hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_ACCESS_HB0);
+ /* Reserved (0) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1);
+ /* Reserved (0) */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2);
+
+ /* PB0 UPC_EAN_ISRC_16-31 */
+ /* Bytes PB16-PB27 shall be set to a value of 0. */
+ for (i = 0; i <= 27; i++)
+ /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */
+ hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i);
+}
+
+/**
+ * sh_hdmi_configure() - Initialise HDMI for output
+ */
+static void sh_hdmi_configure(struct sh_hdmi *hdmi)
+{
+ /* Configure video format */
+ sh_hdmi_video_config(hdmi);
+
+ /* Configure audio format */
+ sh_hdmi_audio_config(hdmi);
+
+ /* Configure PHY */
+ sh_hdmi_phy_config(hdmi);
+
+ /* Auxiliary Video Information (AVI) InfoFrame */
+ sh_hdmi_avi_infoframe_setup(hdmi);
+
+ /* Audio InfoFrame */
+ sh_hdmi_audio_infoframe_setup(hdmi);
+
+ /* Gamut Metadata packet */
+ sh_hdmi_gamut_metadata_setup(hdmi);
+
+ /* Audio Content Protection (ACP) Packet */
+ sh_hdmi_acp_setup(hdmi);
+
+ /* ISRC1 Packet */
+ sh_hdmi_isrc1_setup(hdmi);
+
+ /* ISRC2 Packet */
+ sh_hdmi_isrc2_setup(hdmi);
+
+ /*
+ * Control packet auto send with VSYNC control: auto send
+ * General control, Gamut metadata, ISRC, and ACP packets
+ */
+ hdmi_write(hdmi, 0x8E, HDMI_CTRL_PKT_AUTO_SEND);
+
+ /* FIXME */
+ msleep(10);
+
+ /* PS mode b->d, reset PLLA and PLLB */
+ hdmi_write(hdmi, 0x4C, HDMI_SYSTEM_CTRL);
+
+ udelay(10);
+
+ hdmi_write(hdmi, 0x40, HDMI_SYSTEM_CTRL);
+}
+
+static void sh_hdmi_read_edid(struct sh_hdmi *hdmi)
+{
+ struct fb_var_screeninfo *var = &hdmi->var;
+ struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
+ struct fb_videomode *lcd_cfg = &pdata->lcd_chan->lcd_cfg;
+ unsigned long height = var->height, width = var->width;
+ int i;
+ u8 edid[128];
+
+ /* Read EDID */
+ pr_debug("Read back EDID code:");
+ for (i = 0; i < 128; i++) {
+ edid[i] = hdmi_read(hdmi, HDMI_EDID_KSV_FIFO_ACCESS_WINDOW);
+#ifdef DEBUG
+ if ((i % 16) == 0) {
+ printk(KERN_CONT "\n");
+ printk(KERN_DEBUG "%02X | %02X", i, edid[i]);
+ } else {
+ printk(KERN_CONT " %02X", edid[i]);
+ }
+#endif
+ }
+#ifdef DEBUG
+ printk(KERN_CONT "\n");
+#endif
+ fb_parse_edid(edid, var);
+ pr_debug("%u-%u-%u-%u x %u-%u-%u-%u @ %lu kHz monitor detected\n",
+ var->left_margin, var->xres, var->right_margin, var->hsync_len,
+ var->upper_margin, var->yres, var->lower_margin, var->vsync_len,
+ PICOS2KHZ(var->pixclock));
+
+ /* FIXME: Use user-provided configuration instead of EDID */
+ var->width = width;
+ var->xres = lcd_cfg->xres;
+ var->xres_virtual = lcd_cfg->xres;
+ var->left_margin = lcd_cfg->left_margin;
+ var->right_margin = lcd_cfg->right_margin;
+ var->hsync_len = lcd_cfg->hsync_len;
+ var->height = height;
+ var->yres = lcd_cfg->yres;
+ var->yres_virtual = lcd_cfg->yres * 2;
+ var->upper_margin = lcd_cfg->upper_margin;
+ var->lower_margin = lcd_cfg->lower_margin;
+ var->vsync_len = lcd_cfg->vsync_len;
+ var->sync = lcd_cfg->sync;
+ var->pixclock = lcd_cfg->pixclock;
+
+ hdmi_external_video_param(hdmi);
+}
+
+static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id)
+{
+ struct sh_hdmi *hdmi = dev_id;
+ u8 status1, status2, mask1, mask2;
+
+ /* mode_b and PLLA and PLLB reset */
+ hdmi_write(hdmi, 0x2C, HDMI_SYSTEM_CTRL);
+
+ /* How long shall reset be held? */
+ udelay(10);
+
+ /* mode_b and PLLA and PLLB reset release */
+ hdmi_write(hdmi, 0x20, HDMI_SYSTEM_CTRL);
+
+ status1 = hdmi_read(hdmi, HDMI_INTERRUPT_STATUS_1);
+ status2 = hdmi_read(hdmi, HDMI_INTERRUPT_STATUS_2);
+
+ mask1 = hdmi_read(hdmi, HDMI_INTERRUPT_MASK_1);
+ mask2 = hdmi_read(hdmi, HDMI_INTERRUPT_MASK_2);
+
+ /* Correct would be to ack only set bits, but the datasheet requires 0xff */
+ hdmi_write(hdmi, 0xFF, HDMI_INTERRUPT_STATUS_1);
+ hdmi_write(hdmi, 0xFF, HDMI_INTERRUPT_STATUS_2);
+
+ if (printk_ratelimit())
+ pr_debug("IRQ #%d: Status #1: 0x%x & 0x%x, #2: 0x%x & 0x%x\n",
+ irq, status1, mask1, status2, mask2);
+
+ if (!((status1 & mask1) | (status2 & mask2))) {
+ return IRQ_NONE;
+ } else if (status1 & 0xc0) {
+ u8 msens;
+
+ /* Datasheet specifies 10ms... */
+ udelay(500);
+
+ msens = hdmi_read(hdmi, HDMI_HOT_PLUG_MSENS_STATUS);
+ pr_debug("MSENS 0x%x\n", msens);
+ /* Check, if hot plug & MSENS pin status are both high */
+ if ((msens & 0xC0) == 0xC0) {
+ /* Display plug in */
+ hdmi->hp_state = HDMI_HOTPLUG_CONNECTED;
+
+ /* Set EDID word address */
+ hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS);
+ /* Set EDID segment pointer */
+ hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER);
+ /* Enable EDID interrupt */
+ hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1);
+ } else if (!(status1 & 0x80)) {
+ /* Display unplug, beware multiple interrupts */
+ if (hdmi->hp_state != HDMI_HOTPLUG_DISCONNECTED)
+ schedule_delayed_work(&hdmi->edid_work, 0);
+
+ hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED;
+ /* display_off will switch back to mode_a */
+ }
+ } else if (status1 & 2) {
+ /* EDID error interrupt: retry */
+ /* Set EDID word address */
+ hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS);
+ /* Set EDID segment pointer */
+ hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER);
+ } else if (status1 & 4) {
+ /* Disable EDID interrupt */
+ hdmi_write(hdmi, 0xC0, HDMI_INTERRUPT_MASK_1);
+ hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE;
+ schedule_delayed_work(&hdmi->edid_work, msecs_to_jiffies(10));
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void hdmi_display_on(void *arg, struct fb_info *info)
+{
+ struct sh_hdmi *hdmi = arg;
+ struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
+
+ if (info->var.xres != 1280 || info->var.yres != 720) {
+ dev_warn(info->device, "Unsupported framebuffer geometry %ux%u\n",
+ info->var.xres, info->var.yres);
+ return;
+ }
+
+ pr_debug("%s(%p): state %x\n", __func__, pdata->lcd_dev, info->state);
+ /*
+ * FIXME: not a good place to store fb_info. And we cannot nullify it
+ * even on monitor disconnect. What should the lifecycle be?
+ */
+ hdmi->info = info;
+ switch (hdmi->hp_state) {
+ case HDMI_HOTPLUG_EDID_DONE:
+ /* PS mode d->e. All functions are active */
+ hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL);
+ pr_debug("HDMI running\n");
+ break;
+ case HDMI_HOTPLUG_DISCONNECTED:
+ info->state = FBINFO_STATE_SUSPENDED;
+ default:
+ hdmi->var = info->var;
+ }
+}
+
+static void hdmi_display_off(void *arg)
+{
+ struct sh_hdmi *hdmi = arg;
+ struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
+
+ pr_debug("%s(%p)\n", __func__, pdata->lcd_dev);
+ /* PS mode e->a */
+ hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL);
+}
+
+/* Hotplug interrupt occurred, read EDID */
+static void edid_work_fn(struct work_struct *work)
+{
+ struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work);
+ struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
+
+ pr_debug("%s(%p): begin, hotplug status %d\n", __func__,
+ pdata->lcd_dev, hdmi->hp_state);
+
+ if (!pdata->lcd_dev)
+ return;
+
+ if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) {
+ pm_runtime_get_sync(hdmi->dev);
+ /* A device has been plugged in */
+ sh_hdmi_read_edid(hdmi);
+ msleep(10);
+ sh_hdmi_configure(hdmi);
+ /* Switched to another (d) power-save mode */
+ msleep(10);
+
+ if (!hdmi->info)
+ return;
+
+ acquire_console_sem();
+
+ /* HDMI plug in */
+ hdmi->info->var = hdmi->var;
+ if (hdmi->info->state != FBINFO_STATE_RUNNING)
+ fb_set_suspend(hdmi->info, 0);
+ else
+ hdmi_display_on(hdmi, hdmi->info);
+
+ release_console_sem();
+ } else {
+ if (!hdmi->info)
+ return;
+
+ acquire_console_sem();
+
+ /* HDMI disconnect */
+ fb_set_suspend(hdmi->info, 1);
+
+ release_console_sem();
+ pm_runtime_put(hdmi->dev);
+ }
+
+ pr_debug("%s(%p): end\n", __func__, pdata->lcd_dev);
+}
+
+static int __init sh_hdmi_probe(struct platform_device *pdev)
+{
+ struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ int irq = platform_get_irq(pdev, 0), ret;
+ struct sh_hdmi *hdmi;
+ long rate;
+
+ if (!res || !pdata || irq < 0)
+ return -ENODEV;
+
+ hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL);
+ if (!hdmi) {
+ dev_err(&pdev->dev, "Cannot allocate device data\n");
+ return -ENOMEM;
+ }
+
+ hdmi->dev = &pdev->dev;
+
+ hdmi->hdmi_clk = clk_get(&pdev->dev, "ick");
+ if (IS_ERR(hdmi->hdmi_clk)) {
+ ret = PTR_ERR(hdmi->hdmi_clk);
+ dev_err(&pdev->dev, "Unable to get clock: %d\n", ret);
+ goto egetclk;
+ }
+
+ rate = PICOS2KHZ(pdata->lcd_chan->lcd_cfg.pixclock) * 1000;
+
+ rate = clk_round_rate(hdmi->hdmi_clk, rate);
+ if (rate < 0) {
+ ret = rate;
+ dev_err(&pdev->dev, "Cannot get suitable rate: %ld\n", rate);
+ goto erate;
+ }
+
+ ret = clk_set_rate(hdmi->hdmi_clk, rate);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot set rate %ld: %d\n", rate, ret);
+ goto erate;
+ }
+
+ pr_debug("HDMI set frequency %lu\n", rate);
+
+ ret = clk_enable(hdmi->hdmi_clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot enable clock: %d\n", ret);
+ goto eclkenable;
+ }
+
+ dev_info(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate);
+
+ if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) {
+ dev_err(&pdev->dev, "HDMI register region already claimed\n");
+ ret = -EBUSY;
+ goto ereqreg;
+ }
+
+ hdmi->base = ioremap(res->start, resource_size(res));
+ if (!hdmi->base) {
+ dev_err(&pdev->dev, "HDMI register region already claimed\n");
+ ret = -ENOMEM;
+ goto emap;
+ }
+
+ platform_set_drvdata(pdev, hdmi);
+
+#if 1
+ /* Product and revision IDs are 0 in sh-mobile version */
+ dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n",
+ hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID));
+#endif
+
+ /* Set up LCDC callbacks */
+ pdata->lcd_chan->board_cfg.board_data = hdmi;
+ pdata->lcd_chan->board_cfg.display_on = hdmi_display_on;
+ pdata->lcd_chan->board_cfg.display_off = hdmi_display_off;
+
+ INIT_DELAYED_WORK(&hdmi->edid_work, edid_work_fn);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_resume(&pdev->dev);
+
+ ret = request_irq(irq, sh_hdmi_hotplug, 0,
+ dev_name(&pdev->dev), hdmi);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Unable to request irq: %d\n", ret);
+ goto ereqirq;
+ }
+
+ return 0;
+
+ereqirq:
+ pm_runtime_disable(&pdev->dev);
+ iounmap(hdmi->base);
+emap:
+ release_mem_region(res->start, resource_size(res));
+ereqreg:
+ clk_disable(hdmi->hdmi_clk);
+eclkenable:
+erate:
+ clk_put(hdmi->hdmi_clk);
+egetclk:
+ kfree(hdmi);
+
+ return ret;
+}
+
+static int __exit sh_hdmi_remove(struct platform_device *pdev)
+{
+ struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data;
+ struct sh_hdmi *hdmi = platform_get_drvdata(pdev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ int irq = platform_get_irq(pdev, 0);
+
+ pdata->lcd_chan->board_cfg.display_on = NULL;
+ pdata->lcd_chan->board_cfg.display_off = NULL;
+ pdata->lcd_chan->board_cfg.board_data = NULL;
+
+ free_irq(irq, hdmi);
+ pm_runtime_disable(&pdev->dev);
+ cancel_delayed_work_sync(&hdmi->edid_work);
+ clk_disable(hdmi->hdmi_clk);
+ clk_put(hdmi->hdmi_clk);
+ iounmap(hdmi->base);
+ release_mem_region(res->start, resource_size(res));
+ kfree(hdmi);
+
+ return 0;
+}
+
+static struct platform_driver sh_hdmi_driver = {
+ .remove = __exit_p(sh_hdmi_remove),
+ .driver = {
+ .name = "sh-mobile-hdmi",
+ },
+};
+
+static int __init sh_hdmi_init(void)
+{
+ return platform_driver_probe(&sh_hdmi_driver, sh_hdmi_probe);
+}
+module_init(sh_hdmi_init);
+
+static void __exit sh_hdmi_exit(void)
+{
+ platform_driver_unregister(&sh_hdmi_driver);
+}
+module_exit(sh_hdmi_exit);
+
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_DESCRIPTION("SuperH / ARM-shmobile HDMI driver");
+MODULE_LICENSE("GPL v2");
/* per-channel registers */
enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R,
LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR,
+ LDHAJR,
NR_CH_REGS };
static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = {
[LDVLNR] = 0x450,
[LDVSYNR] = 0x454,
[LDPMR] = 0x460,
+ [LDHAJR] = 0x4a0,
};
static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = {
struct clk *dot_clk;
unsigned long lddckr;
struct sh_mobile_lcdc_chan ch[2];
+ struct notifier_block notifier;
unsigned long saved_shared_regs[NR_SHARED_REGS];
int started;
};
lcdc_write(priv, _LDDCKSTPR, 1); /* stop dotclock */
}
+static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
+{
+ struct fb_var_screeninfo *var = &ch->info->var;
+ unsigned long h_total, hsync_pos;
+ u32 tmp;
+
+ tmp = ch->ldmt1r_value;
+ tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28;
+ tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27;
+ tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0;
+ tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0;
+ tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0;
+ tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0;
+ tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0;
+ lcdc_write_chan(ch, LDMT1R, tmp);
+
+ /* setup SYS bus */
+ lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r);
+ lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r);
+
+ /* horizontal configuration */
+ h_total = var->xres + var->hsync_len +
+ var->left_margin + var->right_margin;
+ tmp = h_total / 8; /* HTCN */
+ tmp |= (var->xres / 8) << 16; /* HDCN */
+ lcdc_write_chan(ch, LDHCNR, tmp);
+
+ hsync_pos = var->xres + var->right_margin;
+ tmp = hsync_pos / 8; /* HSYNP */
+ tmp |= (var->hsync_len / 8) << 16; /* HSYNW */
+ lcdc_write_chan(ch, LDHSYNR, tmp);
+
+ /* vertical configuration */
+ tmp = var->yres + var->vsync_len +
+ var->upper_margin + var->lower_margin; /* VTLN */
+ tmp |= var->yres << 16; /* VDLN */
+ lcdc_write_chan(ch, LDVLNR, tmp);
+
+ tmp = var->yres + var->lower_margin; /* VSYNP */
+ tmp |= var->vsync_len << 16; /* VSYNW */
+ lcdc_write_chan(ch, LDVSYNR, tmp);
+
+ /* Adjust horizontal synchronisation for HDMI */
+ tmp = ((var->xres & 7) << 24) |
+ ((h_total & 7) << 16) |
+ ((var->hsync_len & 7) << 8) |
+ hsync_pos;
+ lcdc_write_chan(ch, LDHAJR, tmp);
+}
+
static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
{
struct sh_mobile_lcdc_chan *ch;
if (!ch->enabled)
continue;
- tmp = ch->ldmt1r_value;
- tmp |= (lcd_cfg->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28;
- tmp |= (lcd_cfg->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0;
- lcdc_write_chan(ch, LDMT1R, tmp);
-
- /* setup SYS bus */
- lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r);
- lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r);
-
- /* horizontal configuration */
- tmp = lcd_cfg->xres + lcd_cfg->hsync_len;
- tmp += lcd_cfg->left_margin;
- tmp += lcd_cfg->right_margin;
- tmp /= 8; /* HTCN */
- tmp |= (lcd_cfg->xres / 8) << 16; /* HDCN */
- lcdc_write_chan(ch, LDHCNR, tmp);
-
- tmp = lcd_cfg->xres;
- tmp += lcd_cfg->right_margin;
- tmp /= 8; /* HSYNP */
- tmp |= (lcd_cfg->hsync_len / 8) << 16; /* HSYNW */
- lcdc_write_chan(ch, LDHSYNR, tmp);
+ sh_mobile_lcdc_geometry(ch);
/* power supply */
lcdc_write_chan(ch, LDPMR, 0);
- /* vertical configuration */
- tmp = lcd_cfg->yres + lcd_cfg->vsync_len;
- tmp += lcd_cfg->upper_margin;
- tmp += lcd_cfg->lower_margin; /* VTLN */
- tmp |= lcd_cfg->yres << 16; /* VDLN */
- lcdc_write_chan(ch, LDVLNR, tmp);
-
- tmp = lcd_cfg->yres;
- tmp += lcd_cfg->lower_margin; /* VSYNP */
- tmp |= lcd_cfg->vsync_len << 16; /* VSYNW */
- lcdc_write_chan(ch, LDVSYNR, tmp);
-
board_cfg = &ch->cfg.board_cfg;
if (board_cfg->setup_sys)
ret = board_cfg->setup_sys(board_cfg->board_data, ch,
board_cfg = &ch->cfg.board_cfg;
if (board_cfg->display_on)
- board_cfg->display_on(board_cfg->board_data);
+ board_cfg->display_on(board_cfg->board_data, ch->info);
}
return 0;
.runtime_resume = sh_mobile_lcdc_runtime_resume,
};
+static int sh_mobile_lcdc_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct fb_event *event = data;
+ struct fb_info *info = event->info;
+ struct sh_mobile_lcdc_chan *ch = info->par;
+ struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg;
+ struct fb_var_screeninfo *var;
+
+ if (&ch->lcdc->notifier != nb)
+ return 0;
+
+ dev_dbg(info->dev, "%s(): action = %lu, data = %p\n",
+ __func__, action, event->data);
+
+ switch(action) {
+ case FB_EVENT_SUSPEND:
+ if (board_cfg->display_off)
+ board_cfg->display_off(board_cfg->board_data);
+ pm_runtime_put(info->device);
+ break;
+ case FB_EVENT_RESUME:
+ var = &info->var;
+
+ /* HDMI must be enabled before LCDC configuration */
+ if (board_cfg->display_on)
+ board_cfg->display_on(board_cfg->board_data, ch->info);
+
+ /* Check if the new display is not in our modelist */
+ if (ch->info->modelist.next &&
+ !fb_match_mode(var, &ch->info->modelist)) {
+ struct fb_videomode mode;
+ int ret;
+
+ /* Can we handle this display? */
+ if (var->xres > ch->cfg.lcd_cfg.xres ||
+ var->yres > ch->cfg.lcd_cfg.yres)
+ return -ENOMEM;
+
+ /* Add to the modelist */
+ fb_var_to_videomode(&mode, var);
+ ret = fb_add_videomode(&mode, &ch->info->modelist);
+ if (ret < 0)
+ return ret;
+ }
+
+ pm_runtime_get_sync(info->device);
+
+ sh_mobile_lcdc_geometry(ch);
+
+ break;
+ }
+
+ return 0;
+}
+
static int sh_mobile_lcdc_remove(struct platform_device *pdev);
static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
goto err1;
}
+ priv->base = ioremap_nocache(res->start, resource_size(res));
+ if (!priv->base)
+ goto err1;
+
error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv);
if (error) {
dev_err(&pdev->dev, "unable to setup clocks\n");
goto err1;
}
- priv->base = ioremap_nocache(res->start, (res->end - res->start) + 1);
-
for (i = 0; i < j; i++) {
+ struct fb_var_screeninfo *var;
+ struct fb_videomode *lcd_cfg;
cfg = &priv->ch[i].cfg;
priv->ch[i].info = framebuffer_alloc(0, &pdev->dev);
}
info = priv->ch[i].info;
+ var = &info->var;
+ lcd_cfg = &cfg->lcd_cfg;
info->fbops = &sh_mobile_lcdc_ops;
- info->var.xres = info->var.xres_virtual = cfg->lcd_cfg.xres;
- info->var.yres = cfg->lcd_cfg.yres;
+ var->xres = var->xres_virtual = lcd_cfg->xres;
+ var->yres = lcd_cfg->yres;
/* Default Y virtual resolution is 2x panel size */
- info->var.yres_virtual = info->var.yres * 2;
- info->var.width = cfg->lcd_size_cfg.width;
- info->var.height = cfg->lcd_size_cfg.height;
- info->var.activate = FB_ACTIVATE_NOW;
- error = sh_mobile_lcdc_set_bpp(&info->var, cfg->bpp);
+ var->yres_virtual = var->yres * 2;
+ var->width = cfg->lcd_size_cfg.width;
+ var->height = cfg->lcd_size_cfg.height;
+ var->activate = FB_ACTIVATE_NOW;
+ var->left_margin = lcd_cfg->left_margin;
+ var->right_margin = lcd_cfg->right_margin;
+ var->upper_margin = lcd_cfg->upper_margin;
+ var->lower_margin = lcd_cfg->lower_margin;
+ var->hsync_len = lcd_cfg->hsync_len;
+ var->vsync_len = lcd_cfg->vsync_len;
+ var->sync = lcd_cfg->sync;
+ var->pixclock = lcd_cfg->pixclock;
+
+ error = sh_mobile_lcdc_set_bpp(var, cfg->bpp);
if (error)
break;
info->fix = sh_mobile_lcdc_fix;
- info->fix.line_length = cfg->lcd_cfg.xres * (cfg->bpp / 8);
+ info->fix.line_length = lcd_cfg->xres * (cfg->bpp / 8);
info->fix.smem_len = info->fix.line_length *
- info->var.yres_virtual;
+ var->yres_virtual;
buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len,
&priv->ch[i].dma_handle, GFP_KERNEL);
ch->cfg.bpp);
/* deferred io mode: disable clock to save power */
- if (info->fbdefio)
+ if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED)
sh_mobile_lcdc_clk_off(priv);
}
+ /* Failure ignored */
+ priv->notifier.notifier_call = sh_mobile_lcdc_notify;
+ fb_register_client(&priv->notifier);
+
return 0;
err1:
sh_mobile_lcdc_remove(pdev);
struct fb_info *info;
int i;
+ fb_unregister_client(&priv->notifier);
+
for (i = 0; i < ARRAY_SIZE(priv->ch); i++)
if (priv->ch[i].info && priv->ch[i].info->dev)
unregister_framebuffer(priv->ch[i].info);
# ARM Architecture
+config ARM_SP805_WATCHDOG
+ tristate "ARM SP805 Watchdog"
+ depends on ARM_AMBA
+ help
+ ARM Primecell SP805 Watchdog timer. This will reboot your system when
+ the timeout is reached.
+
config AT91RM9200_WATCHDOG
tristate "AT91RM9200 watchdog"
depends on ARCH_AT91RM9200
Most people will say N.
+config F71808E_WDT
+ tristate "Fintek F71808E and F71882FG Watchdog"
+ depends on X86 && EXPERIMENTAL
+ help
+ This is the driver for the hardware watchdog on the Fintek
+ F71808E and F71882FG Super I/O controllers.
+
+ You can compile this driver directly into the kernel, or use
+ it as a module. The module will be called f71808e_wdt.
+
+
config GEODE_WDT
tristate "AMD Geode CS5535/CS5536 Watchdog"
depends on CS5535_MFGPT
# ALPHA Architecture
# ARM Architecture
+obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o
obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o
obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o
obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o
+obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o
obj-$(CONFIG_GEODE_WDT) += geodewdt.o
obj-$(CONFIG_SC520_WDT) += sc520_wdt.o
obj-$(CONFIG_SBC_FITPC2_WATCHDOG) += sbc_fitpc2_wdt.o
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2006 by Hans Edgington <hans@edgington.nl> *
+ * Copyright (C) 2007-2009 Hans de Goede <hdegoede@redhat.com> *
+ * Copyright (C) 2010 Giel van Schijndel <me@mortis.eu> *
+ * *
+ * 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/err.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+
+#define DRVNAME "f71808e_wdt"
+
+#define SIO_F71808FG_LD_WDT 0x07 /* Watchdog timer logical device */
+#define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */
+#define SIO_LOCK_KEY 0xAA /* Key to diasble Super-I/O */
+
+#define SIO_REG_LDSEL 0x07 /* Logical device select */
+#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
+#define SIO_REG_DEVREV 0x22 /* Device revision */
+#define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */
+#define SIO_REG_ENABLE 0x30 /* Logical device enable */
+#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
+
+#define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */
+#define SIO_F71808_ID 0x0901 /* Chipset ID */
+#define SIO_F71858_ID 0x0507 /* Chipset ID */
+#define SIO_F71862_ID 0x0601 /* Chipset ID */
+#define SIO_F71882_ID 0x0541 /* Chipset ID */
+#define SIO_F71889_ID 0x0723 /* Chipset ID */
+
+#define F71882FG_REG_START 0x01
+
+#define F71808FG_REG_WDO_CONF 0xf0
+#define F71808FG_REG_WDT_CONF 0xf5
+#define F71808FG_REG_WD_TIME 0xf6
+
+#define F71808FG_FLAG_WDOUT_EN 7
+
+#define F71808FG_FLAG_WDTMOUT_STS 5
+#define F71808FG_FLAG_WD_EN 5
+#define F71808FG_FLAG_WD_PULSE 4
+#define F71808FG_FLAG_WD_UNIT 3
+
+/* Default values */
+#define WATCHDOG_TIMEOUT 60 /* 1 minute default timeout */
+#define WATCHDOG_MAX_TIMEOUT (60 * 255)
+#define WATCHDOG_PULSE_WIDTH 125 /* 125 ms, default pulse width for
+ watchdog signal */
+
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+static const int max_timeout = WATCHDOG_MAX_TIMEOUT;
+static int timeout = 60; /* default timeout in seconds */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. 1<= timeout <="
+ __MODULE_STRING(WATCHDOG_MAX_TIMEOUT) " (default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
+
+static unsigned int pulse_width = WATCHDOG_PULSE_WIDTH;
+module_param(pulse_width, uint, 0);
+MODULE_PARM_DESC(pulse_width,
+ "Watchdog signal pulse width. 0(=level), 1 ms, 25 ms, 125 ms or 5000 ms"
+ " (default=" __MODULE_STRING(WATCHDOG_PULSE_WIDTH) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0444);
+MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
+
+static unsigned int start_withtimeout;
+module_param(start_withtimeout, uint, 0);
+MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with"
+ " given initial timeout. Zero (default) disables this feature.");
+
+enum chips { f71808fg, f71858fg, f71862fg, f71882fg, f71889fg };
+
+static const char *f71808e_names[] = {
+ "f71808fg",
+ "f71858fg",
+ "f71862fg",
+ "f71882fg",
+ "f71889fg",
+};
+
+/* Super-I/O Function prototypes */
+static inline int superio_inb(int base, int reg);
+static inline int superio_inw(int base, int reg);
+static inline void superio_outb(int base, int reg, u8 val);
+static inline void superio_set_bit(int base, int reg, int bit);
+static inline void superio_clear_bit(int base, int reg, int bit);
+static inline int superio_enter(int base);
+static inline void superio_select(int base, int ld);
+static inline void superio_exit(int base);
+
+struct watchdog_data {
+ unsigned short sioaddr;
+ enum chips type;
+ unsigned long opened;
+ struct mutex lock;
+ char expect_close;
+ struct watchdog_info ident;
+
+ unsigned short timeout;
+ u8 timer_val; /* content for the wd_time register */
+ char minutes_mode;
+ u8 pulse_val; /* pulse width flag */
+ char pulse_mode; /* enable pulse output mode? */
+ char caused_reboot; /* last reboot was by the watchdog */
+};
+
+static struct watchdog_data watchdog = {
+ .lock = __MUTEX_INITIALIZER(watchdog.lock),
+};
+
+/* Super I/O functions */
+static inline int superio_inb(int base, int reg)
+{
+ outb(reg, base);
+ return inb(base + 1);
+}
+
+static int superio_inw(int base, int reg)
+{
+ int val;
+ val = superio_inb(base, reg) << 8;
+ val |= superio_inb(base, reg + 1);
+ return val;
+}
+
+static inline void superio_outb(int base, int reg, u8 val)
+{
+ outb(reg, base);
+ outb(val, base + 1);
+}
+
+static inline void superio_set_bit(int base, int reg, int bit)
+{
+ unsigned long val = superio_inb(base, reg);
+ __set_bit(bit, &val);
+ superio_outb(base, reg, val);
+}
+
+static inline void superio_clear_bit(int base, int reg, int bit)
+{
+ unsigned long val = superio_inb(base, reg);
+ __clear_bit(bit, &val);
+ superio_outb(base, reg, val);
+}
+
+static inline int superio_enter(int base)
+{
+ /* Don't step on other drivers' I/O space by accident */
+ if (!request_muxed_region(base, 2, DRVNAME)) {
+ printk(KERN_ERR DRVNAME ": I/O address 0x%04x already in use\n",
+ (int)base);
+ return -EBUSY;
+ }
+
+ /* according to the datasheet the key must be send twice! */
+ outb(SIO_UNLOCK_KEY, base);
+ outb(SIO_UNLOCK_KEY, base);
+
+ return 0;
+}
+
+static inline void superio_select(int base, int ld)
+{
+ outb(SIO_REG_LDSEL, base);
+ outb(ld, base + 1);
+}
+
+static inline void superio_exit(int base)
+{
+ outb(SIO_LOCK_KEY, base);
+ release_region(base, 2);
+}
+
+static int watchdog_set_timeout(int timeout)
+{
+ if (timeout <= 0
+ || timeout > max_timeout) {
+ printk(KERN_ERR DRVNAME ": watchdog timeout out of range\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&watchdog.lock);
+
+ watchdog.timeout = timeout;
+ if (timeout > 0xff) {
+ watchdog.timer_val = DIV_ROUND_UP(timeout, 60);
+ watchdog.minutes_mode = true;
+ } else {
+ watchdog.timer_val = timeout;
+ watchdog.minutes_mode = false;
+ }
+
+ mutex_unlock(&watchdog.lock);
+
+ return 0;
+}
+
+static int watchdog_set_pulse_width(unsigned int pw)
+{
+ int err = 0;
+
+ mutex_lock(&watchdog.lock);
+
+ if (pw <= 1) {
+ watchdog.pulse_val = 0;
+ } else if (pw <= 25) {
+ watchdog.pulse_val = 1;
+ } else if (pw <= 125) {
+ watchdog.pulse_val = 2;
+ } else if (pw <= 5000) {
+ watchdog.pulse_val = 3;
+ } else {
+ printk(KERN_ERR DRVNAME ": pulse width out of range\n");
+ err = -EINVAL;
+ goto exit_unlock;
+ }
+
+ watchdog.pulse_mode = pw;
+
+exit_unlock:
+ mutex_unlock(&watchdog.lock);
+ return err;
+}
+
+static int watchdog_keepalive(void)
+{
+ int err = 0;
+
+ mutex_lock(&watchdog.lock);
+ err = superio_enter(watchdog.sioaddr);
+ if (err)
+ goto exit_unlock;
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+
+ if (watchdog.minutes_mode)
+ /* select minutes for timer units */
+ superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_UNIT);
+ else
+ /* select seconds for timer units */
+ superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_UNIT);
+
+ /* Set timer value */
+ superio_outb(watchdog.sioaddr, F71808FG_REG_WD_TIME,
+ watchdog.timer_val);
+
+ superio_exit(watchdog.sioaddr);
+
+exit_unlock:
+ mutex_unlock(&watchdog.lock);
+ return err;
+}
+
+static int watchdog_start(void)
+{
+ /* Make sure we don't die as soon as the watchdog is enabled below */
+ int err = watchdog_keepalive();
+ if (err)
+ return err;
+
+ mutex_lock(&watchdog.lock);
+ err = superio_enter(watchdog.sioaddr);
+ if (err)
+ goto exit_unlock;
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+
+ /* Watchdog pin configuration */
+ switch (watchdog.type) {
+ case f71808fg:
+ /* Set pin 21 to GPIO23/WDTRST#, then to WDTRST# */
+ superio_clear_bit(watchdog.sioaddr, 0x2a, 3);
+ superio_clear_bit(watchdog.sioaddr, 0x2b, 3);
+ break;
+
+ case f71882fg:
+ /* Set pin 56 to WDTRST# */
+ superio_set_bit(watchdog.sioaddr, 0x29, 1);
+ break;
+
+ default:
+ /*
+ * 'default' label to shut up the compiler and catch
+ * programmer errors
+ */
+ err = -ENODEV;
+ goto exit_superio;
+ }
+
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+ superio_set_bit(watchdog.sioaddr, SIO_REG_ENABLE, 0);
+ superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDO_CONF,
+ F71808FG_FLAG_WDOUT_EN);
+
+ superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_EN);
+
+ if (watchdog.pulse_mode) {
+ /* Select "pulse" output mode with given duration */
+ u8 wdt_conf = superio_inb(watchdog.sioaddr,
+ F71808FG_REG_WDT_CONF);
+
+ /* Set WD_PSWIDTH bits (1:0) */
+ wdt_conf = (wdt_conf & 0xfc) | (watchdog.pulse_val & 0x03);
+ /* Set WD_PULSE to "pulse" mode */
+ wdt_conf |= BIT(F71808FG_FLAG_WD_PULSE);
+
+ superio_outb(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
+ wdt_conf);
+ } else {
+ /* Select "level" output mode */
+ superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_PULSE);
+ }
+
+exit_superio:
+ superio_exit(watchdog.sioaddr);
+exit_unlock:
+ mutex_unlock(&watchdog.lock);
+
+ return err;
+}
+
+static int watchdog_stop(void)
+{
+ int err = 0;
+
+ mutex_lock(&watchdog.lock);
+ err = superio_enter(watchdog.sioaddr);
+ if (err)
+ goto exit_unlock;
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+
+ superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_EN);
+
+ superio_exit(watchdog.sioaddr);
+
+exit_unlock:
+ mutex_unlock(&watchdog.lock);
+
+ return err;
+}
+
+static int watchdog_get_status(void)
+{
+ int status = 0;
+
+ mutex_lock(&watchdog.lock);
+ status = (watchdog.caused_reboot) ? WDIOF_CARDRESET : 0;
+ mutex_unlock(&watchdog.lock);
+
+ return status;
+}
+
+static bool watchdog_is_running(void)
+{
+ /*
+ * if we fail to determine the watchdog's status assume it to be
+ * running to be on the safe side
+ */
+ bool is_running = true;
+
+ mutex_lock(&watchdog.lock);
+ if (superio_enter(watchdog.sioaddr))
+ goto exit_unlock;
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+
+ is_running = (superio_inb(watchdog.sioaddr, SIO_REG_ENABLE) & BIT(0))
+ && (superio_inb(watchdog.sioaddr, F71808FG_REG_WDT_CONF)
+ & F71808FG_FLAG_WD_EN);
+
+ superio_exit(watchdog.sioaddr);
+
+exit_unlock:
+ mutex_unlock(&watchdog.lock);
+ return is_running;
+}
+
+/* /dev/watchdog api */
+
+static int watchdog_open(struct inode *inode, struct file *file)
+{
+ int err;
+
+ /* If the watchdog is alive we don't need to start it again */
+ if (test_and_set_bit(0, &watchdog.opened))
+ return -EBUSY;
+
+ err = watchdog_start();
+ if (err) {
+ clear_bit(0, &watchdog.opened);
+ return err;
+ }
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ watchdog.expect_close = 0;
+ return nonseekable_open(inode, file);
+}
+
+static int watchdog_release(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &watchdog.opened);
+
+ if (!watchdog.expect_close) {
+ watchdog_keepalive();
+ printk(KERN_CRIT DRVNAME
+ ": Unexpected close, not stopping watchdog!\n");
+ } else if (!nowayout) {
+ watchdog_stop();
+ }
+ return 0;
+}
+
+/*
+ * watchdog_write:
+ * @file: file handle to the watchdog
+ * @buf: buffer to write
+ * @count: count of bytes
+ * @ppos: pointer to the position to write. No seeks allowed
+ *
+ * A write to a watchdog device is defined as a keepalive signal. Any
+ * write of data will do, as we we don't define content meaning.
+ */
+
+static ssize_t watchdog_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ bool expect_close = false;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ expect_close = (c == 'V');
+ }
+
+ /* Properly order writes across fork()ed processes */
+ mutex_lock(&watchdog.lock);
+ watchdog.expect_close = expect_close;
+ mutex_unlock(&watchdog.lock);
+ }
+
+ /* someone wrote to us, we should restart timer */
+ watchdog_keepalive();
+ }
+ return count;
+}
+
+/*
+ * watchdog_ioctl:
+ * @inode: inode of the device
+ * @file: file handle to the device
+ * @cmd: watchdog command
+ * @arg: argument pointer
+ *
+ * The watchdog API defines a common set of functions for all watchdogs
+ * according to their available features.
+ */
+static long watchdog_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int status;
+ int new_options;
+ int new_timeout;
+ union {
+ struct watchdog_info __user *ident;
+ int __user *i;
+ } uarg;
+
+ uarg.i = (int __user *)arg;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(uarg.ident, &watchdog.ident,
+ sizeof(watchdog.ident)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ status = watchdog_get_status();
+ if (status < 0)
+ return status;
+ return put_user(status, uarg.i);
+
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, uarg.i);
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(new_options, uarg.i))
+ return -EFAULT;
+
+ if (new_options & WDIOS_DISABLECARD)
+ watchdog_stop();
+
+ if (new_options & WDIOS_ENABLECARD)
+ return watchdog_start();
+
+
+ case WDIOC_KEEPALIVE:
+ watchdog_keepalive();
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, uarg.i))
+ return -EFAULT;
+
+ if (watchdog_set_timeout(new_timeout))
+ return -EINVAL;
+
+ watchdog_keepalive();
+ /* Fall */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(watchdog.timeout, uarg.i);
+
+ default:
+ return -ENOTTY;
+
+ }
+}
+
+static int watchdog_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ watchdog_stop();
+ return NOTIFY_DONE;
+}
+
+static const struct file_operations watchdog_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .open = watchdog_open,
+ .release = watchdog_release,
+ .write = watchdog_write,
+ .unlocked_ioctl = watchdog_ioctl,
+};
+
+static struct miscdevice watchdog_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &watchdog_fops,
+};
+
+static struct notifier_block watchdog_notifier = {
+ .notifier_call = watchdog_notify_sys,
+};
+
+static int __init watchdog_init(int sioaddr)
+{
+ int wdt_conf, err = 0;
+
+ /* No need to lock watchdog.lock here because no entry points
+ * into the module have been registered yet.
+ */
+ watchdog.sioaddr = sioaddr;
+ watchdog.ident.options = WDIOC_SETTIMEOUT
+ | WDIOF_MAGICCLOSE
+ | WDIOF_KEEPALIVEPING;
+
+ snprintf(watchdog.ident.identity,
+ sizeof(watchdog.ident.identity), "%s watchdog",
+ f71808e_names[watchdog.type]);
+
+ err = superio_enter(sioaddr);
+ if (err)
+ return err;
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+
+ wdt_conf = superio_inb(sioaddr, F71808FG_REG_WDT_CONF);
+ watchdog.caused_reboot = wdt_conf & F71808FG_FLAG_WDTMOUT_STS;
+
+ superio_exit(sioaddr);
+
+ err = watchdog_set_timeout(timeout);
+ if (err)
+ return err;
+ err = watchdog_set_pulse_width(pulse_width);
+ if (err)
+ return err;
+
+ err = register_reboot_notifier(&watchdog_notifier);
+ if (err)
+ return err;
+
+ err = misc_register(&watchdog_miscdev);
+ if (err) {
+ printk(KERN_ERR DRVNAME
+ ": cannot register miscdev on minor=%d\n",
+ watchdog_miscdev.minor);
+ goto exit_reboot;
+ }
+
+ if (start_withtimeout) {
+ if (start_withtimeout <= 0
+ || start_withtimeout > max_timeout) {
+ printk(KERN_ERR DRVNAME
+ ": starting timeout out of range\n");
+ err = -EINVAL;
+ goto exit_miscdev;
+ }
+
+ err = watchdog_start();
+ if (err) {
+ printk(KERN_ERR DRVNAME
+ ": cannot start watchdog timer\n");
+ goto exit_miscdev;
+ }
+
+ mutex_lock(&watchdog.lock);
+ err = superio_enter(sioaddr);
+ if (err)
+ goto exit_unlock;
+ superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT);
+
+ if (start_withtimeout > 0xff) {
+ /* select minutes for timer units */
+ superio_set_bit(sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_UNIT);
+ superio_outb(sioaddr, F71808FG_REG_WD_TIME,
+ DIV_ROUND_UP(start_withtimeout, 60));
+ } else {
+ /* select seconds for timer units */
+ superio_clear_bit(sioaddr, F71808FG_REG_WDT_CONF,
+ F71808FG_FLAG_WD_UNIT);
+ superio_outb(sioaddr, F71808FG_REG_WD_TIME,
+ start_withtimeout);
+ }
+
+ superio_exit(sioaddr);
+ mutex_unlock(&watchdog.lock);
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ printk(KERN_INFO DRVNAME
+ ": watchdog started with initial timeout of %u sec\n",
+ start_withtimeout);
+ }
+
+ return 0;
+
+exit_unlock:
+ mutex_unlock(&watchdog.lock);
+exit_miscdev:
+ misc_deregister(&watchdog_miscdev);
+exit_reboot:
+ unregister_reboot_notifier(&watchdog_notifier);
+
+ return err;
+}
+
+static int __init f71808e_find(int sioaddr)
+{
+ u16 devid;
+ int err = superio_enter(sioaddr);
+ if (err)
+ return err;
+
+ devid = superio_inw(sioaddr, SIO_REG_MANID);
+ if (devid != SIO_FINTEK_ID) {
+ pr_debug(DRVNAME ": Not a Fintek device\n");
+ err = -ENODEV;
+ goto exit;
+ }
+
+ devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID);
+ switch (devid) {
+ case SIO_F71808_ID:
+ watchdog.type = f71808fg;
+ break;
+ case SIO_F71882_ID:
+ watchdog.type = f71882fg;
+ break;
+ case SIO_F71862_ID:
+ case SIO_F71889_ID:
+ /* These have a watchdog, though it isn't implemented (yet). */
+ err = -ENOSYS;
+ goto exit;
+ case SIO_F71858_ID:
+ /* Confirmed (by datasheet) not to have a watchdog. */
+ err = -ENODEV;
+ goto exit;
+ default:
+ printk(KERN_INFO DRVNAME ": Unrecognized Fintek device: %04x\n",
+ (unsigned int)devid);
+ err = -ENODEV;
+ goto exit;
+ }
+
+ printk(KERN_INFO DRVNAME ": Found %s watchdog chip, revision %d\n",
+ f71808e_names[watchdog.type],
+ (int)superio_inb(sioaddr, SIO_REG_DEVREV));
+exit:
+ superio_exit(sioaddr);
+ return err;
+}
+
+static int __init f71808e_init(void)
+{
+ static const unsigned short addrs[] = { 0x2e, 0x4e };
+ int err = -ENODEV;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(addrs); i++) {
+ err = f71808e_find(addrs[i]);
+ if (err == 0)
+ break;
+ }
+ if (i == ARRAY_SIZE(addrs))
+ return err;
+
+ return watchdog_init(addrs[i]);
+}
+
+static void __exit f71808e_exit(void)
+{
+ if (watchdog_is_running()) {
+ printk(KERN_WARNING DRVNAME
+ ": Watchdog timer still running, stopping it\n");
+ watchdog_stop();
+ }
+ misc_deregister(&watchdog_miscdev);
+ unregister_reboot_notifier(&watchdog_notifier);
+}
+
+MODULE_DESCRIPTION("F71808E Watchdog Driver");
+MODULE_AUTHOR("Giel van Schijndel <me@mortis.eu>");
+MODULE_LICENSE("GPL");
+
+module_init(f71808e_init);
+module_exit(f71808e_exit);
physical_bios_offset);
printk(KERN_DEBUG "hpwdt: CRU Length: 0x%lx\n",
cru_length);
- printk(KERN_DEBUG "hpwdt: CRU Mapped Address: 0x%x\n",
- (unsigned int)&cru_rom_addr);
+ printk(KERN_DEBUG "hpwdt: CRU Mapped Address: %p\n",
+ &cru_rom_addr);
}
iounmap(bios32_map);
return retval;
static int __devexit s3c2410wdt_remove(struct platform_device *dev)
{
- s3c2410wdt_cpufreq_deregister();
-
- release_resource(wdt_mem);
- kfree(wdt_mem);
- wdt_mem = NULL;
+ misc_deregister(&s3c2410wdt_miscdev);
- free_irq(wdt_irq->start, dev);
- wdt_irq = NULL;
+ s3c2410wdt_cpufreq_deregister();
clk_disable(wdt_clock);
clk_put(wdt_clock);
wdt_clock = NULL;
+ free_irq(wdt_irq->start, dev);
+ wdt_irq = NULL;
+
iounmap(wdt_base);
- misc_deregister(&s3c2410wdt_miscdev);
+
+ release_resource(wdt_mem);
+ kfree(wdt_mem);
+ wdt_mem = NULL;
return 0;
}
val = therm_trip ? 0x06 : 0x04;
outb(val, sch311x_wdt_data.runtime_reg + RESGEN);
+ sch311x_wdt_miscdev.parent = dev;
+
err = misc_register(&sch311x_wdt_miscdev);
if (err != 0) {
dev_err(dev, "cannot register miscdev on minor=%d (err=%d)\n",
goto exit_release_region3;
}
- sch311x_wdt_miscdev.parent = dev;
-
dev_info(dev,
"SMSC SCH311x WDT initialized. timeout=%d sec (nowayout=%d)\n",
timeout, nowayout);
--- /dev/null
+/*
+ * drivers/char/watchdog/sp805-wdt.c
+ *
+ * Watchdog driver for ARM SP805 watchdog module
+ *
+ * Copyright (C) 2010 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2 or later. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/device.h>
+#include <linux/resource.h>
+#include <linux/amba/bus.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+
+/* default timeout in seconds */
+#define DEFAULT_TIMEOUT 60
+
+#define MODULE_NAME "sp805-wdt"
+
+/* watchdog register offsets and masks */
+#define WDTLOAD 0x000
+ #define LOAD_MIN 0x00000001
+ #define LOAD_MAX 0xFFFFFFFF
+#define WDTVALUE 0x004
+#define WDTCONTROL 0x008
+ /* control register masks */
+ #define INT_ENABLE (1 << 0)
+ #define RESET_ENABLE (1 << 1)
+#define WDTINTCLR 0x00C
+#define WDTRIS 0x010
+#define WDTMIS 0x014
+ #define INT_MASK (1 << 0)
+#define WDTLOCK 0xC00
+ #define UNLOCK 0x1ACCE551
+ #define LOCK 0x00000001
+
+/**
+ * struct sp805_wdt: sp805 wdt device structure
+ *
+ * lock: spin lock protecting dev structure and io access
+ * base: base address of wdt
+ * clk: clock structure of wdt
+ * dev: amba device structure of wdt
+ * status: current status of wdt
+ * load_val: load value to be set for current timeout
+ * timeout: current programmed timeout
+ */
+struct sp805_wdt {
+ spinlock_t lock;
+ void __iomem *base;
+ struct clk *clk;
+ struct amba_device *adev;
+ unsigned long status;
+ #define WDT_BUSY 0
+ #define WDT_CAN_BE_CLOSED 1
+ unsigned int load_val;
+ unsigned int timeout;
+};
+
+/* local variables */
+static struct sp805_wdt *wdt;
+static int nowayout = WATCHDOG_NOWAYOUT;
+
+/* This routine finds load value that will reset system in required timout */
+static void wdt_setload(unsigned int timeout)
+{
+ u64 load, rate;
+
+ rate = clk_get_rate(wdt->clk);
+
+ /*
+ * sp805 runs counter with given value twice, after the end of first
+ * counter it gives an interrupt and then starts counter again. If
+ * interrupt already occured then it resets the system. This is why
+ * load is half of what should be required.
+ */
+ load = div_u64(rate, 2) * timeout - 1;
+
+ load = (load > LOAD_MAX) ? LOAD_MAX : load;
+ load = (load < LOAD_MIN) ? LOAD_MIN : load;
+
+ spin_lock(&wdt->lock);
+ wdt->load_val = load;
+ /* roundup timeout to closest positive integer value */
+ wdt->timeout = div_u64((load + 1) * 2 + (rate / 2), rate);
+ spin_unlock(&wdt->lock);
+}
+
+/* returns number of seconds left for reset to occur */
+static u32 wdt_timeleft(void)
+{
+ u64 load, rate;
+
+ rate = clk_get_rate(wdt->clk);
+
+ spin_lock(&wdt->lock);
+ load = readl(wdt->base + WDTVALUE);
+
+ /*If the interrupt is inactive then time left is WDTValue + WDTLoad. */
+ if (!(readl(wdt->base + WDTRIS) & INT_MASK))
+ load += wdt->load_val + 1;
+ spin_unlock(&wdt->lock);
+
+ return div_u64(load, rate);
+}
+
+/* enables watchdog timers reset */
+static void wdt_enable(void)
+{
+ spin_lock(&wdt->lock);
+
+ writel(UNLOCK, wdt->base + WDTLOCK);
+ writel(wdt->load_val, wdt->base + WDTLOAD);
+ writel(INT_MASK, wdt->base + WDTINTCLR);
+ writel(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL);
+ writel(LOCK, wdt->base + WDTLOCK);
+
+ spin_unlock(&wdt->lock);
+}
+
+/* disables watchdog timers reset */
+static void wdt_disable(void)
+{
+ spin_lock(&wdt->lock);
+
+ writel(UNLOCK, wdt->base + WDTLOCK);
+ writel(0, wdt->base + WDTCONTROL);
+ writel(0, wdt->base + WDTLOAD);
+ writel(LOCK, wdt->base + WDTLOCK);
+
+ spin_unlock(&wdt->lock);
+}
+
+static ssize_t sp805_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ /* Check for Magic Close character */
+ if (c == 'V') {
+ set_bit(WDT_CAN_BE_CLOSED,
+ &wdt->status);
+ break;
+ }
+ }
+ }
+ wdt_enable();
+ }
+ return len;
+}
+
+static const struct watchdog_info ident = {
+ .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = MODULE_NAME,
+};
+
+static long sp805_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -ENOTTY;
+ unsigned int timeout;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ wdt_enable();
+ ret = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(timeout, (unsigned int *)arg);
+ if (ret)
+ break;
+
+ wdt_setload(timeout);
+
+ wdt_enable();
+ /* Fall through */
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(wdt->timeout, (unsigned int *)arg);
+ break;
+ case WDIOC_GETTIMELEFT:
+ ret = put_user(wdt_timeleft(), (unsigned int *)arg);
+ break;
+ }
+ return ret;
+}
+
+static int sp805_wdt_open(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+
+ if (test_and_set_bit(WDT_BUSY, &wdt->status))
+ return -EBUSY;
+
+ ret = clk_enable(wdt->clk);
+ if (ret) {
+ dev_err(&wdt->adev->dev, "clock enable fail");
+ goto err;
+ }
+
+ wdt_enable();
+
+ /* can not be closed, once enabled */
+ clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
+ return nonseekable_open(inode, file);
+
+err:
+ clear_bit(WDT_BUSY, &wdt->status);
+ return ret;
+}
+
+static int sp805_wdt_release(struct inode *inode, struct file *file)
+{
+ if (!test_bit(WDT_CAN_BE_CLOSED, &wdt->status)) {
+ clear_bit(WDT_BUSY, &wdt->status);
+ dev_warn(&wdt->adev->dev, "Device closed unexpectedly\n");
+ return 0;
+ }
+
+ wdt_disable();
+ clk_disable(wdt->clk);
+ clear_bit(WDT_BUSY, &wdt->status);
+
+ return 0;
+}
+
+static const struct file_operations sp805_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = sp805_wdt_write,
+ .unlocked_ioctl = sp805_wdt_ioctl,
+ .open = sp805_wdt_open,
+ .release = sp805_wdt_release,
+};
+
+static struct miscdevice sp805_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &sp805_wdt_fops,
+};
+
+static int __devinit
+sp805_wdt_probe(struct amba_device *adev, struct amba_id *id)
+{
+ int ret = 0;
+
+ if (!request_mem_region(adev->res.start, resource_size(&adev->res),
+ "sp805_wdt")) {
+ dev_warn(&adev->dev, "Failed to get memory region resource\n");
+ ret = -ENOENT;
+ goto err;
+ }
+
+ wdt = kzalloc(sizeof(*wdt), GFP_KERNEL);
+ if (!wdt) {
+ dev_warn(&adev->dev, "Kzalloc failed\n");
+ ret = -ENOMEM;
+ goto err_kzalloc;
+ }
+
+ wdt->clk = clk_get(&adev->dev, NULL);
+ if (IS_ERR(wdt->clk)) {
+ dev_warn(&adev->dev, "Clock not found\n");
+ ret = PTR_ERR(wdt->clk);
+ goto err_clk_get;
+ }
+
+ wdt->base = ioremap(adev->res.start, resource_size(&adev->res));
+ if (!wdt->base) {
+ ret = -ENOMEM;
+ dev_warn(&adev->dev, "ioremap fail\n");
+ goto err_ioremap;
+ }
+
+ wdt->adev = adev;
+ spin_lock_init(&wdt->lock);
+ wdt_setload(DEFAULT_TIMEOUT);
+
+ ret = misc_register(&sp805_wdt_miscdev);
+ if (ret < 0) {
+ dev_warn(&adev->dev, "cannot register misc device\n");
+ goto err_misc_register;
+ }
+
+ dev_info(&adev->dev, "registration successful\n");
+ return 0;
+
+err_misc_register:
+ iounmap(wdt->base);
+err_ioremap:
+ clk_put(wdt->clk);
+err_clk_get:
+ kfree(wdt);
+ wdt = NULL;
+err_kzalloc:
+ release_mem_region(adev->res.start, resource_size(&adev->res));
+err:
+ dev_err(&adev->dev, "Probe Failed!!!\n");
+ return ret;
+}
+
+static int __devexit sp805_wdt_remove(struct amba_device *adev)
+{
+ misc_deregister(&sp805_wdt_miscdev);
+ iounmap(wdt->base);
+ clk_put(wdt->clk);
+ kfree(wdt);
+ release_mem_region(adev->res.start, resource_size(&adev->res));
+
+ return 0;
+}
+
+static struct amba_id sp805_wdt_ids[] __initdata = {
+ {
+ .id = 0x00141805,
+ .mask = 0x00ffffff,
+ },
+ { 0, 0 },
+};
+
+static struct amba_driver sp805_wdt_driver = {
+ .drv = {
+ .name = MODULE_NAME,
+ },
+ .id_table = sp805_wdt_ids,
+ .probe = sp805_wdt_probe,
+ .remove = __devexit_p(sp805_wdt_remove),
+};
+
+static int __init sp805_wdt_init(void)
+{
+ return amba_driver_register(&sp805_wdt_driver);
+}
+module_init(sp805_wdt_init);
+
+static void __exit sp805_wdt_exit(void)
+{
+ amba_driver_unregister(&sp805_wdt_driver);
+}
+module_exit(sp805_wdt_exit);
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Set to 1 to keep watchdog running after device release");
+
+MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
+MODULE_DESCRIPTION("ARM SP805 Watchdog Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
#define PFX "wdt_pci: "
-/*
- * Until Access I/O gets their application for a PCI vendor ID approved,
- * I don't think that it's appropriate to move these constants into the
- * regular pci_ids.h file. -- JPN 2000/01/18
- */
-
-#ifndef PCI_VENDOR_ID_ACCESSIO
-#define PCI_VENDOR_ID_ACCESSIO 0x494f
-#endif
-#ifndef PCI_DEVICE_ID_WDG_CSM
-#define PCI_DEVICE_ID_WDG_CSM 0x22c0
-#endif
-
/* We can only use 1 card due to the /dev/watchdog restriction */
static int dev_count;
static struct pci_device_id wdtpci_pci_tbl[] = {
{
.vendor = PCI_VENDOR_ID_ACCESSIO,
- .device = PCI_DEVICE_ID_WDG_CSM,
+ .device = PCI_DEVICE_ID_ACCESSIO_WDG_CSM,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
}
EXPORT_SYMBOL_GPL(xenbus_watch_pathfmt);
+static void xenbus_switch_fatal(struct xenbus_device *, int, int,
+ const char *, ...);
-/**
- * xenbus_switch_state
- * @dev: xenbus device
- * @state: new state
- *
- * Advertise in the store a change of the given driver to the given new_state.
- * Return 0 on success, or -errno on error. On error, the device will switch
- * to XenbusStateClosing, and the error will be saved in the store.
- */
-int xenbus_switch_state(struct xenbus_device *dev, enum xenbus_state state)
+static int
+__xenbus_switch_state(struct xenbus_device *dev,
+ enum xenbus_state state, int depth)
{
/* We check whether the state is currently set to the given value, and
if not, then the state is set. We don't want to unconditionally
to it, as the device will be tearing down, and we don't want to
resurrect that directory.
- Note that, because of this cached value of our state, this function
- will not work inside a Xenstore transaction (something it was
- trying to in the past) because dev->state would not get reset if
- the transaction was aborted.
-
+ Note that, because of this cached value of our state, this
+ function will not take a caller's Xenstore transaction
+ (something it was trying to in the past) because dev->state
+ would not get reset if the transaction was aborted.
*/
+ struct xenbus_transaction xbt;
int current_state;
- int err;
+ int err, abort;
if (state == dev->state)
return 0;
- err = xenbus_scanf(XBT_NIL, dev->nodename, "state", "%d",
- ¤t_state);
- if (err != 1)
+again:
+ abort = 1;
+
+ err = xenbus_transaction_start(&xbt);
+ if (err) {
+ xenbus_switch_fatal(dev, depth, err, "starting transaction");
return 0;
+ }
+
+ err = xenbus_scanf(xbt, dev->nodename, "state", "%d", ¤t_state);
+ if (err != 1)
+ goto abort;
- err = xenbus_printf(XBT_NIL, dev->nodename, "state", "%d", state);
+ err = xenbus_printf(xbt, dev->nodename, "state", "%d", state);
if (err) {
- if (state != XenbusStateClosing) /* Avoid looping */
- xenbus_dev_fatal(dev, err, "writing new state");
- return err;
+ xenbus_switch_fatal(dev, depth, err, "writing new state");
+ goto abort;
}
- dev->state = state;
+ abort = 0;
+abort:
+ err = xenbus_transaction_end(xbt, abort);
+ if (err) {
+ if (err == -EAGAIN && !abort)
+ goto again;
+ xenbus_switch_fatal(dev, depth, err, "ending transaction");
+ } else
+ dev->state = state;
return 0;
}
+
+/**
+ * xenbus_switch_state
+ * @dev: xenbus device
+ * @state: new state
+ *
+ * Advertise in the store a change of the given driver to the given new_state.
+ * Return 0 on success, or -errno on error. On error, the device will switch
+ * to XenbusStateClosing, and the error will be saved in the store.
+ */
+int xenbus_switch_state(struct xenbus_device *dev, enum xenbus_state state)
+{
+ return __xenbus_switch_state(dev, state, 0);
+}
+
EXPORT_SYMBOL_GPL(xenbus_switch_state);
int xenbus_frontend_closed(struct xenbus_device *dev)
}
EXPORT_SYMBOL_GPL(xenbus_dev_fatal);
+/**
+ * Equivalent to xenbus_dev_fatal(dev, err, fmt, args), but helps
+ * avoiding recursion within xenbus_switch_state.
+ */
+static void xenbus_switch_fatal(struct xenbus_device *dev, int depth, int err,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ xenbus_va_dev_error(dev, err, fmt, ap);
+ va_end(ap);
+
+ if (!depth)
+ __xenbus_switch_state(dev, XenbusStateClosing, 1);
+}
+
/**
* xenbus_grant_ring
* @dev: xenbus device
proc_bus_zorro_lseek(struct file *file, loff_t off, int whence)
{
loff_t new = -1;
+ struct inode *inode = file->f_path.dentry->d_inode;
- lock_kernel();
+ mutex_lock(&inode->i_mutex);
switch (whence) {
case 0:
new = off;
new = sizeof(struct ConfigDev) + off;
break;
}
- if (new < 0 || new > sizeof(struct ConfigDev)) {
- unlock_kernel();
- return -EINVAL;
- }
- unlock_kernel();
- return (file->f_pos = new);
+ if (new < 0 || new > sizeof(struct ConfigDev))
+ new = -EINVAL;
+ else
+ file->f_pos = new;
+ mutex_unlock(&inode->i_mutex);
+ return new;
}
static ssize_t
cd.cd_BoardAddr = (void *)zorro_resource_start(z);
cd.cd_BoardSize = zorro_resource_len(z);
- if (copy_to_user(buf, &cd, nbytes))
+ if (copy_to_user(buf, (void *)&cd + pos, nbytes))
return -EFAULT;
*ppos += nbytes;
ret = kernel_bind(socket, (struct sockaddr *) &srx, sizeof(srx));
if (ret < 0) {
sock_release(socket);
+ destroy_workqueue(afs_async_calls);
_leave(" = %d [bind]", ret);
return ret;
}
#include <linux/slab.h>
#include <linux/param.h>
#include <linux/time.h>
+#include <linux/compat.h>
#include <linux/smp_lock.h>
#include "autofs_i.h"
static int autofs_root_unlink(struct inode *,struct dentry *);
static int autofs_root_rmdir(struct inode *,struct dentry *);
static int autofs_root_mkdir(struct inode *,struct dentry *,int);
-static int autofs_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long);
+static long autofs_root_ioctl(struct file *,unsigned int,unsigned long);
+static long autofs_root_compat_ioctl(struct file *,unsigned int,unsigned long);
const struct file_operations autofs_root_operations = {
.llseek = generic_file_llseek,
.read = generic_read_dir,
.readdir = autofs_root_readdir,
- .ioctl = autofs_root_ioctl,
+ .unlocked_ioctl = autofs_root_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = autofs_root_compat_ioctl,
+#endif
};
const struct inode_operations autofs_root_inode_operations = {
}
/* Get/set timeout ioctl() operation */
+#ifdef CONFIG_COMPAT
+static inline int autofs_compat_get_set_timeout(struct autofs_sb_info *sbi,
+ unsigned int __user *p)
+{
+ unsigned long ntimeout;
+
+ if (get_user(ntimeout, p) ||
+ put_user(sbi->exp_timeout / HZ, p))
+ return -EFAULT;
+
+ if (ntimeout > UINT_MAX/HZ)
+ sbi->exp_timeout = 0;
+ else
+ sbi->exp_timeout = ntimeout * HZ;
+
+ return 0;
+}
+#endif
+
static inline int autofs_get_set_timeout(struct autofs_sb_info *sbi,
unsigned long __user *p)
{
* ioctl()'s on the root directory is the chief method for the daemon to
* generate kernel reactions
*/
-static int autofs_root_ioctl(struct inode *inode, struct file *filp,
+static int autofs_do_root_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
struct autofs_sb_info *sbi = autofs_sbi(inode->i_sb);
return 0;
case AUTOFS_IOC_PROTOVER: /* Get protocol version */
return autofs_get_protover(argp);
+#ifdef CONFIG_COMPAT
+ case AUTOFS_IOC_SETTIMEOUT32:
+ return autofs_compat_get_set_timeout(sbi, argp);
+#endif
case AUTOFS_IOC_SETTIMEOUT:
return autofs_get_set_timeout(sbi, argp);
case AUTOFS_IOC_EXPIRE:
default:
return -ENOSYS;
}
+
+}
+
+static long autofs_root_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret;
+
+ lock_kernel();
+ ret = autofs_do_root_ioctl(filp->f_path.dentry->d_inode,
+ filp, cmd, arg);
+ unlock_kernel();
+
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long autofs_root_compat_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct inode *inode = filp->f_path.dentry->d_inode;
+ int ret;
+
+ lock_kernel();
+ if (cmd == AUTOFS_IOC_READY || cmd == AUTOFS_IOC_FAIL)
+ ret = autofs_do_root_ioctl(inode, filp, cmd, arg);
+ else
+ ret = autofs_do_root_ioctl(inode, filp, cmd,
+ (unsigned long)compat_ptr(arg));
+ unlock_kernel();
+
+ return ret;
}
+#endif
#include <linux/slab.h>
#include <linux/param.h>
#include <linux/time.h>
+#include <linux/compat.h>
#include <linux/smp_lock.h>
+
#include "autofs_i.h"
static int autofs4_dir_symlink(struct inode *,struct dentry *,const char *);
static int autofs4_dir_rmdir(struct inode *,struct dentry *);
static int autofs4_dir_mkdir(struct inode *,struct dentry *,int);
static long autofs4_root_ioctl(struct file *,unsigned int,unsigned long);
+static long autofs4_root_compat_ioctl(struct file *,unsigned int,unsigned long);
static int autofs4_dir_open(struct inode *inode, struct file *file);
static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *);
static void *autofs4_follow_link(struct dentry *, struct nameidata *);
.readdir = dcache_readdir,
.llseek = dcache_dir_lseek,
.unlocked_ioctl = autofs4_root_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = autofs4_root_compat_ioctl,
+#endif
};
const struct file_operations autofs4_dir_operations = {
}
/* Initialize expiry counter after successful mount */
- if (ino)
- ino->last_used = jiffies;
+ ino->last_used = jiffies;
spin_lock(&sbi->fs_lock);
ino->flags &= ~AUTOFS_INF_PENDING;
}
/* Get/set timeout ioctl() operation */
+#ifdef CONFIG_COMPAT
+static inline int autofs4_compat_get_set_timeout(struct autofs_sb_info *sbi,
+ compat_ulong_t __user *p)
+{
+ int rv;
+ unsigned long ntimeout;
+
+ if ((rv = get_user(ntimeout, p)) ||
+ (rv = put_user(sbi->exp_timeout/HZ, p)))
+ return rv;
+
+ if (ntimeout > UINT_MAX/HZ)
+ sbi->exp_timeout = 0;
+ else
+ sbi->exp_timeout = ntimeout * HZ;
+
+ return 0;
+}
+#endif
+
static inline int autofs4_get_set_timeout(struct autofs_sb_info *sbi,
unsigned long __user *p)
{
return autofs4_get_protosubver(sbi, p);
case AUTOFS_IOC_SETTIMEOUT:
return autofs4_get_set_timeout(sbi, p);
+#ifdef CONFIG_COMPAT
+ case AUTOFS_IOC_SETTIMEOUT32:
+ return autofs4_compat_get_set_timeout(sbi, p);
+#endif
case AUTOFS_IOC_ASKUMOUNT:
return autofs4_ask_umount(filp->f_path.mnt, p);
return ret;
}
+
+#ifdef CONFIG_COMPAT
+static long autofs4_root_compat_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct inode *inode = filp->f_path.dentry->d_inode;
+ int ret;
+
+ lock_kernel();
+ if (cmd == AUTOFS_IOC_READY || cmd == AUTOFS_IOC_FAIL)
+ ret = autofs4_root_ioctl_unlocked(inode, filp, cmd, arg);
+ else
+ ret = autofs4_root_ioctl_unlocked(inode, filp, cmd,
+ (unsigned long)compat_ptr(arg));
+ unlock_kernel();
+
+ return ret;
+}
+#endif
if (!bio)
goto out_bmd;
- bio->bi_rw |= (!write_to_vm << BIO_RW);
+ if (!write_to_vm)
+ bio->bi_rw |= REQ_WRITE;
ret = 0;
* set data direction, and check if mapped pages need bouncing
*/
if (!write_to_vm)
- bio->bi_rw |= (1 << BIO_RW);
+ bio->bi_rw |= REQ_WRITE;
bio->bi_bdev = bdev;
bio->bi_flags |= (1 << BIO_USER_MAPPED);
/*
* hooks: /n/, see "layering violations".
*/
- ret = devcgroup_inode_permission(bdev->bd_inode, perm);
- if (ret != 0) {
- bdput(bdev);
- return ret;
+ if (!for_part) {
+ ret = devcgroup_inode_permission(bdev->bd_inode, perm);
+ if (ret != 0) {
+ bdput(bdev);
+ return ret;
+ }
}
- lock_kernel();
restart:
ret = -ENXIO;
disk = get_gendisk(bdev->bd_dev, &partno);
if (!disk)
- goto out_unlock_kernel;
+ goto out;
mutex_lock_nested(&bdev->bd_mutex, for_part);
if (!bdev->bd_openers) {
if (for_part)
bdev->bd_part_count++;
mutex_unlock(&bdev->bd_mutex);
- unlock_kernel();
return 0;
out_clear:
bdev->bd_contains = NULL;
out_unlock_bdev:
mutex_unlock(&bdev->bd_mutex);
- out_unlock_kernel:
- unlock_kernel();
-
+ out:
if (disk)
module_put(disk->fops->owner);
put_disk(disk);
struct block_device *victim = NULL;
mutex_lock_nested(&bdev->bd_mutex, for_part);
- lock_kernel();
if (for_part)
bdev->bd_part_count--;
victim = bdev->bd_contains;
bdev->bd_contains = NULL;
}
- unlock_kernel();
mutex_unlock(&bdev->bd_mutex);
bdput(bdev);
if (victim)
end_io_wq->work.func = end_workqueue_fn;
end_io_wq->work.flags = 0;
- if (bio->bi_rw & (1 << BIO_RW)) {
+ if (bio->bi_rw & REQ_WRITE) {
if (end_io_wq->metadata)
btrfs_queue_worker(&fs_info->endio_meta_write_workers,
&end_io_wq->work);
atomic_inc(&fs_info->nr_async_submits);
- if (rw & (1 << BIO_RW_SYNCIO))
+ if (rw & REQ_SYNC)
btrfs_set_work_high_prio(&async->work);
btrfs_queue_worker(&fs_info->workers, &async->work);
bio, 1);
BUG_ON(ret);
- if (!(rw & (1 << BIO_RW))) {
+ if (!(rw & REQ_WRITE)) {
/*
* called for a read, do the setup so that checksum validation
* can happen in the async kernel threads
* ram and up to date before trying to verify things. For
* blocksize <= pagesize, it is basically a noop
*/
- if (!(bio->bi_rw & (1 << BIO_RW)) && end_io_wq->metadata &&
+ if (!(bio->bi_rw & REQ_WRITE) && end_io_wq->metadata &&
!bio_ready_for_csum(bio)) {
btrfs_queue_worker(&fs_info->endio_meta_workers,
&end_io_wq->work);
ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
BUG_ON(ret);
- if (!(rw & (1 << BIO_RW))) {
+ if (!(rw & REQ_WRITE)) {
if (bio_flags & EXTENT_BIO_COMPRESSED) {
return btrfs_submit_compressed_read(inode, bio,
mirror_num, bio_flags);
bio->bi_size = 0;
bio_add_page(bio, page, failrec->len, start - page_offset(page));
- if (failed_bio->bi_rw & (1 << BIO_RW))
+ if (failed_bio->bi_rw & REQ_WRITE)
rw = WRITE;
else
rw = READ;
struct bio_vec *bvec = bio->bi_io_vec;
u64 start;
int skip_sum;
- int write = rw & (1 << BIO_RW);
+ int write = rw & REQ_WRITE;
int ret = 0;
skip_sum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM;
BUG_ON(atomic_read(&cur->bi_cnt) == 0);
- if (bio_rw_flagged(cur, BIO_RW_SYNCIO))
+ if (cur->bi_rw & REQ_SYNC)
num_sync_run++;
submit_bio(cur->bi_rw, cur);
int max_errors = 0;
struct btrfs_multi_bio *multi = NULL;
- if (multi_ret && !(rw & (1 << BIO_RW)))
+ if (multi_ret && !(rw & REQ_WRITE))
stripes_allocated = 1;
again:
if (multi_ret) {
mirror_num = 0;
/* if our multi bio struct is too small, back off and try again */
- if (rw & (1 << BIO_RW)) {
+ if (rw & REQ_WRITE) {
if (map->type & (BTRFS_BLOCK_GROUP_RAID1 |
BTRFS_BLOCK_GROUP_DUP)) {
stripes_required = map->num_stripes;
max_errors = 1;
}
}
- if (multi_ret && (rw & (1 << BIO_RW)) &&
+ if (multi_ret && (rw & REQ_WRITE) &&
stripes_allocated < stripes_required) {
stripes_allocated = map->num_stripes;
free_extent_map(em);
num_stripes = 1;
stripe_index = 0;
if (map->type & BTRFS_BLOCK_GROUP_RAID1) {
- if (unplug_page || (rw & (1 << BIO_RW)))
+ if (unplug_page || (rw & REQ_WRITE))
num_stripes = map->num_stripes;
else if (mirror_num)
stripe_index = mirror_num - 1;
}
} else if (map->type & BTRFS_BLOCK_GROUP_DUP) {
- if (rw & (1 << BIO_RW))
+ if (rw & REQ_WRITE)
num_stripes = map->num_stripes;
else if (mirror_num)
stripe_index = mirror_num - 1;
stripe_index = do_div(stripe_nr, factor);
stripe_index *= map->sub_stripes;
- if (unplug_page || (rw & (1 << BIO_RW)))
+ if (unplug_page || (rw & REQ_WRITE))
num_stripes = map->sub_stripes;
else if (mirror_num)
stripe_index += mirror_num - 1;
struct btrfs_pending_bios *pending_bios;
/* don't bother with additional async steps for reads, right now */
- if (!(rw & (1 << BIO_RW))) {
+ if (!(rw & REQ_WRITE)) {
bio_get(bio);
submit_bio(rw, bio);
bio_put(bio);
bio->bi_rw |= rw;
spin_lock(&device->io_lock);
- if (bio_rw_flagged(bio, BIO_RW_SYNCIO))
+ if (bio->bi_rw & REQ_SYNC)
pending_bios = &device->pending_sync_bios;
else
pending_bios = &device->pending_bios;
obj-$(CONFIG_CEPH_FS) += ceph.o
-ceph-objs := super.o inode.o dir.o file.o addr.o ioctl.o \
+ceph-objs := super.o inode.o dir.o file.o locks.o addr.o ioctl.o \
export.o caps.o snap.o xattr.o \
messenger.o msgpool.o buffer.o pagelist.o \
mds_client.o mdsmap.o \
zero_user_segment(page, s, PAGE_CACHE_SIZE);
}
- if (add_to_page_cache_lru(page, mapping, page->index, GFP_NOFS)) {
+ if (add_to_page_cache_lru(page, mapping, page->index,
+ GFP_NOFS)) {
page_cache_release(page);
dout("readpages %p add_to_page_cache failed %p\n",
inode, page);
* page truncation thread, possibly losing some data that
* raced its way in
*/
- if ((issued & CEPH_CAP_FILE_CACHE) == 0)
+ if ((issued & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) == 0)
generic_error_remove_page(inode->i_mapping, page);
unlock_page(page);
dout("%p will write page %p idx %lu\n",
inode, page, page->index);
- writeback_stat = atomic_long_inc_return(&client->writeback_count);
- if (writeback_stat > CONGESTION_ON_THRESH(client->mount_args->congestion_kb)) {
- set_bdi_congested(&client->backing_dev_info, BLK_RW_ASYNC);
+ writeback_stat =
+ atomic_long_inc_return(&client->writeback_count);
+ if (writeback_stat > CONGESTION_ON_THRESH(
+ client->mount_args->congestion_kb)) {
+ set_bdi_congested(&client->backing_dev_info,
+ BLK_RW_ASYNC);
}
set_page_writeback(page);
*pagep = page;
dout("write_begin file %p inode %p page %p %d~%d\n", file,
- inode, page, (int)pos, (int)len);
+ inode, page, (int)pos, (int)len);
r = ceph_update_writeable_page(file, pos, len, page);
} while (r == -EAGAIN);
#include <linux/errno.h>
+int ceph_armor(char *dst, const char *src, const char *end);
+int ceph_unarmor(char *dst, const char *src, const char *end);
+
/*
* base64 encode/decode.
*/
-const char *pem_key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char *pem_key =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static int encode_bits(int c)
{
CEPH_AUTH_CEPHX
};
-int ceph_auth_init_protocol(struct ceph_auth_client *ac, int protocol)
+static int ceph_auth_init_protocol(struct ceph_auth_client *ac, int protocol)
{
switch (protocol) {
case CEPH_AUTH_NONE:
return -ERANGE;
}
-int ceph_build_auth_request(struct ceph_auth_client *ac,
- void *msg_buf, size_t msg_len)
+static int ceph_build_auth_request(struct ceph_auth_client *ac,
+ void *msg_buf, size_t msg_len)
{
struct ceph_mon_request_header *monhdr = msg_buf;
void *p = monhdr + 1;
/*
* get existing (or insert new) ticket handler
*/
-struct ceph_x_ticket_handler *get_ticket_handler(struct ceph_auth_client *ac,
- int service)
+static struct ceph_x_ticket_handler *
+get_ticket_handler(struct ceph_auth_client *ac, int service)
{
struct ceph_x_ticket_handler *th;
struct ceph_x_info *xi = ac->private;
auth->struct_v = 1;
auth->key = 0;
for (u = (u64 *)tmp_enc; u + 1 <= (u64 *)(tmp_enc + ret); u++)
- auth->key ^= *u;
+ auth->key ^= *(__le64 *)u;
dout(" server_challenge %llx client_challenge %llx key %llx\n",
xi->server_challenge, le64_to_cpu(auth->client_challenge),
le64_to_cpu(auth->key));
kfree(b);
}
-int ceph_buffer_alloc(struct ceph_buffer *b, int len, gfp_t gfp)
-{
- b->vec.iov_base = kmalloc(len, gfp | __GFP_NOWARN);
- if (b->vec.iov_base) {
- b->is_vmalloc = false;
- } else {
- b->vec.iov_base = __vmalloc(len, gfp, PAGE_KERNEL);
- b->is_vmalloc = true;
- }
- if (!b->vec.iov_base)
- return -ENOMEM;
- b->alloc_len = len;
- b->vec.iov_len = len;
- return 0;
-}
-
int ceph_decode_buffer(struct ceph_buffer **b, void **p, void *end)
{
size_t len;
return cap_str[i];
}
-/*
- * Cap reservations
- *
- * Maintain a global pool of preallocated struct ceph_caps, referenced
- * by struct ceph_caps_reservations. This ensures that we preallocate
- * memory needed to successfully process an MDS response. (If an MDS
- * sends us cap information and we fail to process it, we will have
- * problems due to the client and MDS being out of sync.)
- *
- * Reservations are 'owned' by a ceph_cap_reservation context.
- */
-static spinlock_t caps_list_lock;
-static struct list_head caps_list; /* unused (reserved or unreserved) */
-static int caps_total_count; /* total caps allocated */
-static int caps_use_count; /* in use */
-static int caps_reserve_count; /* unused, reserved */
-static int caps_avail_count; /* unused, unreserved */
-static int caps_min_count; /* keep at least this many (unreserved) */
-
-void __init ceph_caps_init(void)
+void ceph_caps_init(struct ceph_mds_client *mdsc)
{
- INIT_LIST_HEAD(&caps_list);
- spin_lock_init(&caps_list_lock);
+ INIT_LIST_HEAD(&mdsc->caps_list);
+ spin_lock_init(&mdsc->caps_list_lock);
}
-void ceph_caps_finalize(void)
+void ceph_caps_finalize(struct ceph_mds_client *mdsc)
{
struct ceph_cap *cap;
- spin_lock(&caps_list_lock);
- while (!list_empty(&caps_list)) {
- cap = list_first_entry(&caps_list, struct ceph_cap, caps_item);
+ spin_lock(&mdsc->caps_list_lock);
+ while (!list_empty(&mdsc->caps_list)) {
+ cap = list_first_entry(&mdsc->caps_list,
+ struct ceph_cap, caps_item);
list_del(&cap->caps_item);
kmem_cache_free(ceph_cap_cachep, cap);
}
- caps_total_count = 0;
- caps_avail_count = 0;
- caps_use_count = 0;
- caps_reserve_count = 0;
- caps_min_count = 0;
- spin_unlock(&caps_list_lock);
+ mdsc->caps_total_count = 0;
+ mdsc->caps_avail_count = 0;
+ mdsc->caps_use_count = 0;
+ mdsc->caps_reserve_count = 0;
+ mdsc->caps_min_count = 0;
+ spin_unlock(&mdsc->caps_list_lock);
}
-void ceph_adjust_min_caps(int delta)
+void ceph_adjust_min_caps(struct ceph_mds_client *mdsc, int delta)
{
- spin_lock(&caps_list_lock);
- caps_min_count += delta;
- BUG_ON(caps_min_count < 0);
- spin_unlock(&caps_list_lock);
+ spin_lock(&mdsc->caps_list_lock);
+ mdsc->caps_min_count += delta;
+ BUG_ON(mdsc->caps_min_count < 0);
+ spin_unlock(&mdsc->caps_list_lock);
}
-int ceph_reserve_caps(struct ceph_cap_reservation *ctx, int need)
+int ceph_reserve_caps(struct ceph_mds_client *mdsc,
+ struct ceph_cap_reservation *ctx, int need)
{
int i;
struct ceph_cap *cap;
dout("reserve caps ctx=%p need=%d\n", ctx, need);
/* first reserve any caps that are already allocated */
- spin_lock(&caps_list_lock);
- if (caps_avail_count >= need)
+ spin_lock(&mdsc->caps_list_lock);
+ if (mdsc->caps_avail_count >= need)
have = need;
else
- have = caps_avail_count;
- caps_avail_count -= have;
- caps_reserve_count += have;
- BUG_ON(caps_total_count != caps_use_count + caps_reserve_count +
- caps_avail_count);
- spin_unlock(&caps_list_lock);
+ have = mdsc->caps_avail_count;
+ mdsc->caps_avail_count -= have;
+ mdsc->caps_reserve_count += have;
+ BUG_ON(mdsc->caps_total_count != mdsc->caps_use_count +
+ mdsc->caps_reserve_count +
+ mdsc->caps_avail_count);
+ spin_unlock(&mdsc->caps_list_lock);
for (i = have; i < need; i++) {
cap = kmem_cache_alloc(ceph_cap_cachep, GFP_NOFS);
}
BUG_ON(have + alloc != need);
- spin_lock(&caps_list_lock);
- caps_total_count += alloc;
- caps_reserve_count += alloc;
- list_splice(&newcaps, &caps_list);
+ spin_lock(&mdsc->caps_list_lock);
+ mdsc->caps_total_count += alloc;
+ mdsc->caps_reserve_count += alloc;
+ list_splice(&newcaps, &mdsc->caps_list);
- BUG_ON(caps_total_count != caps_use_count + caps_reserve_count +
- caps_avail_count);
- spin_unlock(&caps_list_lock);
+ BUG_ON(mdsc->caps_total_count != mdsc->caps_use_count +
+ mdsc->caps_reserve_count +
+ mdsc->caps_avail_count);
+ spin_unlock(&mdsc->caps_list_lock);
ctx->count = need;
dout("reserve caps ctx=%p %d = %d used + %d resv + %d avail\n",
- ctx, caps_total_count, caps_use_count, caps_reserve_count,
- caps_avail_count);
+ ctx, mdsc->caps_total_count, mdsc->caps_use_count,
+ mdsc->caps_reserve_count, mdsc->caps_avail_count);
return 0;
out_alloc_count:
return ret;
}
-int ceph_unreserve_caps(struct ceph_cap_reservation *ctx)
+int ceph_unreserve_caps(struct ceph_mds_client *mdsc,
+ struct ceph_cap_reservation *ctx)
{
dout("unreserve caps ctx=%p count=%d\n", ctx, ctx->count);
if (ctx->count) {
- spin_lock(&caps_list_lock);
- BUG_ON(caps_reserve_count < ctx->count);
- caps_reserve_count -= ctx->count;
- caps_avail_count += ctx->count;
+ spin_lock(&mdsc->caps_list_lock);
+ BUG_ON(mdsc->caps_reserve_count < ctx->count);
+ mdsc->caps_reserve_count -= ctx->count;
+ mdsc->caps_avail_count += ctx->count;
ctx->count = 0;
dout("unreserve caps %d = %d used + %d resv + %d avail\n",
- caps_total_count, caps_use_count, caps_reserve_count,
- caps_avail_count);
- BUG_ON(caps_total_count != caps_use_count + caps_reserve_count +
- caps_avail_count);
- spin_unlock(&caps_list_lock);
+ mdsc->caps_total_count, mdsc->caps_use_count,
+ mdsc->caps_reserve_count, mdsc->caps_avail_count);
+ BUG_ON(mdsc->caps_total_count != mdsc->caps_use_count +
+ mdsc->caps_reserve_count +
+ mdsc->caps_avail_count);
+ spin_unlock(&mdsc->caps_list_lock);
}
return 0;
}
-static struct ceph_cap *get_cap(struct ceph_cap_reservation *ctx)
+static struct ceph_cap *get_cap(struct ceph_mds_client *mdsc,
+ struct ceph_cap_reservation *ctx)
{
struct ceph_cap *cap = NULL;
if (!ctx) {
cap = kmem_cache_alloc(ceph_cap_cachep, GFP_NOFS);
if (cap) {
- caps_use_count++;
- caps_total_count++;
+ mdsc->caps_use_count++;
+ mdsc->caps_total_count++;
}
return cap;
}
- spin_lock(&caps_list_lock);
+ spin_lock(&mdsc->caps_list_lock);
dout("get_cap ctx=%p (%d) %d = %d used + %d resv + %d avail\n",
- ctx, ctx->count, caps_total_count, caps_use_count,
- caps_reserve_count, caps_avail_count);
+ ctx, ctx->count, mdsc->caps_total_count, mdsc->caps_use_count,
+ mdsc->caps_reserve_count, mdsc->caps_avail_count);
BUG_ON(!ctx->count);
- BUG_ON(ctx->count > caps_reserve_count);
- BUG_ON(list_empty(&caps_list));
+ BUG_ON(ctx->count > mdsc->caps_reserve_count);
+ BUG_ON(list_empty(&mdsc->caps_list));
ctx->count--;
- caps_reserve_count--;
- caps_use_count++;
+ mdsc->caps_reserve_count--;
+ mdsc->caps_use_count++;
- cap = list_first_entry(&caps_list, struct ceph_cap, caps_item);
+ cap = list_first_entry(&mdsc->caps_list, struct ceph_cap, caps_item);
list_del(&cap->caps_item);
- BUG_ON(caps_total_count != caps_use_count + caps_reserve_count +
- caps_avail_count);
- spin_unlock(&caps_list_lock);
+ BUG_ON(mdsc->caps_total_count != mdsc->caps_use_count +
+ mdsc->caps_reserve_count + mdsc->caps_avail_count);
+ spin_unlock(&mdsc->caps_list_lock);
return cap;
}
-void ceph_put_cap(struct ceph_cap *cap)
+void ceph_put_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap)
{
- spin_lock(&caps_list_lock);
+ spin_lock(&mdsc->caps_list_lock);
dout("put_cap %p %d = %d used + %d resv + %d avail\n",
- cap, caps_total_count, caps_use_count,
- caps_reserve_count, caps_avail_count);
- caps_use_count--;
+ cap, mdsc->caps_total_count, mdsc->caps_use_count,
+ mdsc->caps_reserve_count, mdsc->caps_avail_count);
+ mdsc->caps_use_count--;
/*
* Keep some preallocated caps around (ceph_min_count), to
* avoid lots of free/alloc churn.
*/
- if (caps_avail_count >= caps_reserve_count + caps_min_count) {
- caps_total_count--;
+ if (mdsc->caps_avail_count >= mdsc->caps_reserve_count +
+ mdsc->caps_min_count) {
+ mdsc->caps_total_count--;
kmem_cache_free(ceph_cap_cachep, cap);
} else {
- caps_avail_count++;
- list_add(&cap->caps_item, &caps_list);
+ mdsc->caps_avail_count++;
+ list_add(&cap->caps_item, &mdsc->caps_list);
}
- BUG_ON(caps_total_count != caps_use_count + caps_reserve_count +
- caps_avail_count);
- spin_unlock(&caps_list_lock);
+ BUG_ON(mdsc->caps_total_count != mdsc->caps_use_count +
+ mdsc->caps_reserve_count + mdsc->caps_avail_count);
+ spin_unlock(&mdsc->caps_list_lock);
}
void ceph_reservation_status(struct ceph_client *client,
int *total, int *avail, int *used, int *reserved,
int *min)
{
+ struct ceph_mds_client *mdsc = &client->mdsc;
+
if (total)
- *total = caps_total_count;
+ *total = mdsc->caps_total_count;
if (avail)
- *avail = caps_avail_count;
+ *avail = mdsc->caps_avail_count;
if (used)
- *used = caps_use_count;
+ *used = mdsc->caps_use_count;
if (reserved)
- *reserved = caps_reserve_count;
+ *reserved = mdsc->caps_reserve_count;
if (min)
- *min = caps_min_count;
+ *min = mdsc->caps_min_count;
}
/*
return NULL;
}
+struct ceph_cap *ceph_get_cap_for_mds(struct ceph_inode_info *ci, int mds)
+{
+ struct ceph_cap *cap;
+
+ spin_lock(&ci->vfs_inode.i_lock);
+ cap = __get_cap_for_mds(ci, mds);
+ spin_unlock(&ci->vfs_inode.i_lock);
+ return cap;
+}
+
/*
- * Return id of any MDS with a cap, preferably FILE_WR|WRBUFFER|EXCL, else
- * -1.
+ * Return id of any MDS with a cap, preferably FILE_WR|BUFFER|EXCL, else -1.
*/
-static int __ceph_get_cap_mds(struct ceph_inode_info *ci, u32 *mseq)
+static int __ceph_get_cap_mds(struct ceph_inode_info *ci)
{
struct ceph_cap *cap;
int mds = -1;
struct rb_node *p;
- /* prefer mds with WR|WRBUFFER|EXCL caps */
+ /* prefer mds with WR|BUFFER|EXCL caps */
for (p = rb_first(&ci->i_caps); p; p = rb_next(p)) {
cap = rb_entry(p, struct ceph_cap, ci_node);
mds = cap->mds;
- if (mseq)
- *mseq = cap->mseq;
if (cap->issued & (CEPH_CAP_FILE_WR |
CEPH_CAP_FILE_BUFFER |
CEPH_CAP_FILE_EXCL))
{
int mds;
spin_lock(&inode->i_lock);
- mds = __ceph_get_cap_mds(ceph_inode(inode), NULL);
+ mds = __ceph_get_cap_mds(ceph_inode(inode));
spin_unlock(&inode->i_lock);
return mds;
}
* Each time we receive FILE_CACHE anew, we increment
* i_rdcache_gen.
*/
- if ((issued & CEPH_CAP_FILE_CACHE) &&
- (had & CEPH_CAP_FILE_CACHE) == 0)
+ if ((issued & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) &&
+ (had & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) == 0)
ci->i_rdcache_gen++;
/*
new_cap = NULL;
} else {
spin_unlock(&inode->i_lock);
- new_cap = get_cap(caps_reservation);
+ new_cap = get_cap(mdsc, caps_reservation);
if (new_cap == NULL)
return -ENOMEM;
goto retry;
} else {
pr_err("ceph_add_cap: couldn't find snap realm %llx\n",
realmino);
+ WARN_ON(!realm);
}
}
{
int want = 0;
int mode;
- for (mode = 0; mode < 4; mode++)
+ for (mode = 0; mode < CEPH_FILE_MODE_NUM; mode++)
if (ci->i_nr_by_mode[mode])
want |= ceph_caps_for_mode(mode);
return want;
ci->i_auth_cap = NULL;
if (removed)
- ceph_put_cap(cap);
+ ceph_put_cap(mdsc, cap);
if (!__ceph_is_any_caps(ci) && ci->i_snap_realm) {
struct ceph_snap_realm *realm = ci->i_snap_realm;
*/
void __ceph_flush_snaps(struct ceph_inode_info *ci,
struct ceph_mds_session **psession)
+ __releases(ci->vfs_inode->i_lock)
+ __acquires(ci->vfs_inode->i_lock)
{
struct inode *inode = &ci->vfs_inode;
int mds;
BUG_ON(capsnap->dirty == 0);
/* pick mds, take s_mutex */
- mds = __ceph_get_cap_mds(ci, &mseq);
+ if (ci->i_auth_cap == NULL) {
+ dout("no auth cap (migrating?), doing nothing\n");
+ goto out;
+ }
+ mds = ci->i_auth_cap->session->s_mds;
+ mseq = ci->i_auth_cap->mseq;
+
if (session && session->s_mds != mds) {
dout("oops, wrong session %p mutex\n", session);
mutex_unlock(&session->s_mutex);
}
/*
* if session == NULL, we raced against a cap
- * deletion. retry, and we'll get a better
- * @mds value next time.
+ * deletion or migration. retry, and we'll
+ * get a better @mds value next time.
*/
spin_lock(&inode->i_lock);
goto retry;
list_del_init(&ci->i_snap_flush_item);
spin_unlock(&mdsc->snap_flush_lock);
+out:
if (psession)
*psession = session;
else if (session) {
*/
void ceph_check_caps(struct ceph_inode_info *ci, int flags,
struct ceph_mds_session *session)
- __releases(session->s_mutex)
{
struct ceph_client *client = ceph_inode_to_client(&ci->vfs_inode);
struct ceph_mds_client *mdsc = &client->mdsc;
ci->i_wrbuffer_ref == 0 && /* no dirty pages... */
ci->i_rdcache_gen && /* may have cached pages */
(file_wanted == 0 || /* no open files */
- (revoking & CEPH_CAP_FILE_CACHE)) && /* or revoking cache */
+ (revoking & (CEPH_CAP_FILE_CACHE|
+ CEPH_CAP_FILE_LAZYIO))) && /* or revoking cache */
!tried_invalidate) {
dout("check_caps trying to invalidate on %p\n", inode);
if (try_nonblocking_invalidate(inode) < 0) {
- if (revoking & CEPH_CAP_FILE_CACHE) {
+ if (revoking & (CEPH_CAP_FILE_CACHE|
+ CEPH_CAP_FILE_LAZYIO)) {
dout("check_caps queuing invalidate\n");
queue_invalidate = 1;
ci->i_rdcache_revoking = ci->i_rdcache_gen;
struct ceph_mds_session *session,
struct ceph_cap *cap,
struct ceph_buffer *xattr_buf)
- __releases(inode->i_lock)
- __releases(session->s_mutex)
+ __releases(inode->i_lock)
{
struct ceph_inode_info *ci = ceph_inode(inode);
int mds = session->s_mds;
* will invalidate _after_ writeback.)
*/
if (((cap->issued & ~newcaps) & CEPH_CAP_FILE_CACHE) &&
+ (newcaps & CEPH_CAP_FILE_LAZYIO) == 0 &&
!ci->i_wrbuffer_ref) {
if (try_nonblocking_invalidate(inode) == 0) {
revoked_rdcache = 1;
/* revocation, grant, or no-op? */
if (cap->issued & ~newcaps) {
- dout("revocation: %s -> %s\n", ceph_cap_string(cap->issued),
- ceph_cap_string(newcaps));
- if ((used & ~newcaps) & CEPH_CAP_FILE_BUFFER)
- writeback = 1; /* will delay ack */
- else if (dirty & ~newcaps)
- check_caps = 1; /* initiate writeback in check_caps */
- else if (((used & ~newcaps) & CEPH_CAP_FILE_CACHE) == 0 ||
- revoked_rdcache)
- check_caps = 2; /* send revoke ack in check_caps */
+ int revoking = cap->issued & ~newcaps;
+
+ dout("revocation: %s -> %s (revoking %s)\n",
+ ceph_cap_string(cap->issued),
+ ceph_cap_string(newcaps),
+ ceph_cap_string(revoking));
+ if (revoking & used & CEPH_CAP_FILE_BUFFER)
+ writeback = 1; /* initiate writeback; will delay ack */
+ else if (revoking == CEPH_CAP_FILE_CACHE &&
+ (newcaps & CEPH_CAP_FILE_LAZYIO) == 0 &&
+ queue_invalidate)
+ ; /* do nothing yet, invalidation will be queued */
+ else if (cap == ci->i_auth_cap)
+ check_caps = 1; /* check auth cap only */
+ else
+ check_caps = 2; /* check all caps */
cap->issued = newcaps;
cap->implemented |= newcaps;
} else if (cap->issued == newcaps) {
* caller holds s_mutex
*/
static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex,
- struct ceph_mds_session *session)
+ struct ceph_mds_session *session,
+ int *open_target_sessions)
{
struct ceph_inode_info *ci = ceph_inode(inode);
int mds = session->s_mds;
ci->i_cap_exporting_mds = mds;
ci->i_cap_exporting_mseq = mseq;
ci->i_cap_exporting_issued = cap->issued;
+
+ /*
+ * make sure we have open sessions with all possible
+ * export targets, so that we get the matching IMPORT
+ */
+ *open_target_sessions = 1;
}
__ceph_remove_cap(cap);
}
u64 size, max_size;
u64 tid;
void *snaptrace;
+ size_t snaptrace_len;
+ void *flock;
+ u32 flock_len;
+ int open_target_sessions = 0;
dout("handle_caps from mds%d\n", mds);
if (msg->front.iov_len < sizeof(*h))
goto bad;
h = msg->front.iov_base;
- snaptrace = h + 1;
op = le32_to_cpu(h->op);
vino.ino = le64_to_cpu(h->ino);
vino.snap = CEPH_NOSNAP;
size = le64_to_cpu(h->size);
max_size = le64_to_cpu(h->max_size);
+ snaptrace = h + 1;
+ snaptrace_len = le32_to_cpu(h->snap_trace_len);
+
+ if (le16_to_cpu(msg->hdr.version) >= 2) {
+ void *p, *end;
+
+ p = snaptrace + snaptrace_len;
+ end = msg->front.iov_base + msg->front.iov_len;
+ ceph_decode_32_safe(&p, end, flock_len, bad);
+ flock = p;
+ } else {
+ flock = NULL;
+ flock_len = 0;
+ }
+
mutex_lock(&session->s_mutex);
session->s_seq++;
dout(" mds%d seq %lld cap seq %u\n", session->s_mds, session->s_seq,
* along for the mds (who clearly thinks we still have this
* cap).
*/
- ceph_add_cap_releases(mdsc, session, -1);
+ ceph_add_cap_releases(mdsc, session);
ceph_send_cap_releases(mdsc, session);
goto done;
}
goto done;
case CEPH_CAP_OP_EXPORT:
- handle_cap_export(inode, h, session);
+ handle_cap_export(inode, h, session, &open_target_sessions);
goto done;
case CEPH_CAP_OP_IMPORT:
handle_cap_import(mdsc, inode, h, session,
- snaptrace, le32_to_cpu(h->snap_trace_len));
+ snaptrace, snaptrace_len);
ceph_check_caps(ceph_inode(inode), CHECK_CAPS_NODELAY,
session);
goto done_unlocked;
done_unlocked:
if (inode)
iput(inode);
+ if (open_target_sessions)
+ ceph_mdsc_open_export_target_sessions(mdsc, session);
return;
bad:
-#ifndef _FS_CEPH_FRAG_H
-#define _FS_CEPH_FRAG_H
+#ifndef FS_CEPH_FRAG_H
+#define FS_CEPH_FRAG_H
/*
* "Frags" are a way to describe a subset of a 32-bit number space,
int ceph_flags_to_mode(int flags)
{
+ int mode;
+
#ifdef O_DIRECTORY /* fixme */
if ((flags & O_DIRECTORY) == O_DIRECTORY)
return CEPH_FILE_MODE_PIN;
#endif
+ if ((flags & O_APPEND) == O_APPEND)
+ flags |= O_WRONLY;
+
+ if ((flags & O_ACCMODE) == O_RDWR)
+ mode = CEPH_FILE_MODE_RDWR;
+ else if ((flags & O_ACCMODE) == O_WRONLY)
+ mode = CEPH_FILE_MODE_WR;
+ else
+ mode = CEPH_FILE_MODE_RD;
+
#ifdef O_LAZY
if (flags & O_LAZY)
- return CEPH_FILE_MODE_LAZY;
+ mode |= CEPH_FILE_MODE_LAZY;
#endif
- if ((flags & O_APPEND) == O_APPEND)
- flags |= O_WRONLY;
- flags &= O_ACCMODE;
- if ((flags & O_RDWR) == O_RDWR)
- return CEPH_FILE_MODE_RDWR;
- if ((flags & O_WRONLY) == O_WRONLY)
- return CEPH_FILE_MODE_WR;
- return CEPH_FILE_MODE_RD;
+ return mode;
}
int ceph_caps_for_mode(int mode)
{
- switch (mode) {
- case CEPH_FILE_MODE_PIN:
- return CEPH_CAP_PIN;
- case CEPH_FILE_MODE_RD:
- return CEPH_CAP_PIN | CEPH_CAP_FILE_SHARED |
+ int caps = CEPH_CAP_PIN;
+
+ if (mode & CEPH_FILE_MODE_RD)
+ caps |= CEPH_CAP_FILE_SHARED |
CEPH_CAP_FILE_RD | CEPH_CAP_FILE_CACHE;
- case CEPH_FILE_MODE_RDWR:
- return CEPH_CAP_PIN | CEPH_CAP_FILE_SHARED |
- CEPH_CAP_FILE_EXCL |
- CEPH_CAP_FILE_RD | CEPH_CAP_FILE_CACHE |
- CEPH_CAP_FILE_WR | CEPH_CAP_FILE_BUFFER |
- CEPH_CAP_AUTH_SHARED | CEPH_CAP_AUTH_EXCL |
- CEPH_CAP_XATTR_SHARED | CEPH_CAP_XATTR_EXCL;
- case CEPH_FILE_MODE_WR:
- return CEPH_CAP_PIN | CEPH_CAP_FILE_SHARED |
- CEPH_CAP_FILE_EXCL |
+ if (mode & CEPH_FILE_MODE_WR)
+ caps |= CEPH_CAP_FILE_EXCL |
CEPH_CAP_FILE_WR | CEPH_CAP_FILE_BUFFER |
CEPH_CAP_AUTH_SHARED | CEPH_CAP_AUTH_EXCL |
CEPH_CAP_XATTR_SHARED | CEPH_CAP_XATTR_EXCL;
- }
- return 0;
+ if (mode & CEPH_FILE_MODE_LAZY)
+ caps |= CEPH_CAP_FILE_LAZYIO;
+
+ return caps;
}
* LGPL2
*/
-#ifndef _FS_CEPH_CEPH_FS_H
-#define _FS_CEPH_CEPH_FS_H
+#ifndef CEPH_FS_H
+#define CEPH_FS_H
#include "msgr.h"
#include "rados.h"
-/*
- * Ceph release version
- */
-#define CEPH_VERSION_MAJOR 0
-#define CEPH_VERSION_MINOR 20
-#define CEPH_VERSION_PATCH 0
-
-#define _CEPH_STRINGIFY(x) #x
-#define CEPH_STRINGIFY(x) _CEPH_STRINGIFY(x)
-#define CEPH_MAKE_VERSION(x, y, z) CEPH_STRINGIFY(x) "." CEPH_STRINGIFY(y) \
- "." CEPH_STRINGIFY(z)
-#define CEPH_VERSION CEPH_MAKE_VERSION(CEPH_VERSION_MAJOR, \
- CEPH_VERSION_MINOR, CEPH_VERSION_PATCH)
-
/*
* subprotocol versions. when specific messages types or high-level
* protocols change, bump the affected components. we keep rev
/*
* feature bits
*/
-#define CEPH_FEATURE_UID 1
-#define CEPH_FEATURE_NOSRCADDR 2
-#define CEPH_FEATURE_FLOCK 4
-
-#define CEPH_FEATURE_SUPPORTED_MON CEPH_FEATURE_UID|CEPH_FEATURE_NOSRCADDR
-#define CEPH_FEATURE_REQUIRED_MON CEPH_FEATURE_UID
-#define CEPH_FEATURE_SUPPORTED_MDS CEPH_FEATURE_UID|CEPH_FEATURE_NOSRCADDR|CEPH_FEATURE_FLOCK
-#define CEPH_FEATURE_REQUIRED_MDS CEPH_FEATURE_UID
-#define CEPH_FEATURE_SUPPORTED_OSD CEPH_FEATURE_UID|CEPH_FEATURE_NOSRCADDR
-#define CEPH_FEATURE_REQUIRED_OSD CEPH_FEATURE_UID
-#define CEPH_FEATURE_SUPPORTED_CLIENT CEPH_FEATURE_NOSRCADDR
-#define CEPH_FEATURE_REQUIRED_CLIENT CEPH_FEATURE_NOSRCADDR
+#define CEPH_FEATURE_UID (1<<0)
+#define CEPH_FEATURE_NOSRCADDR (1<<1)
+#define CEPH_FEATURE_MONCLOCKCHECK (1<<2)
+#define CEPH_FEATURE_FLOCK (1<<3)
/*
#define CEPH_CRYPTO_NONE 0x0
#define CEPH_CRYPTO_AES 0x1
+#define CEPH_AES_IV "cephsageyudagreg"
+
/* security/authentication protocols */
#define CEPH_AUTH_UNKNOWN 0x0
#define CEPH_AUTH_NONE 0x1
#define CEPH_LOCK_IDFT 512 /* dir frag tree */
#define CEPH_LOCK_INEST 1024 /* mds internal */
#define CEPH_LOCK_IXATTR 2048
+#define CEPH_LOCK_IFLOCK 4096 /* advisory file locks */
#define CEPH_LOCK_INO 8192 /* immutable inode bits; not a lock */
/* client_session ops */
CEPH_MDS_OP_RMXATTR = 0x01106,
CEPH_MDS_OP_SETLAYOUT = 0x01107,
CEPH_MDS_OP_SETATTR = 0x01108,
+ CEPH_MDS_OP_SETFILELOCK= 0x01109,
+ CEPH_MDS_OP_GETFILELOCK= 0x00110,
CEPH_MDS_OP_MKNOD = 0x01201,
CEPH_MDS_OP_LINK = 0x01202,
struct {
struct ceph_file_layout layout;
} __attribute__ ((packed)) setlayout;
+ struct {
+ __u8 rule; /* currently fcntl or flock */
+ __u8 type; /* shared, exclusive, remove*/
+ __le64 pid; /* process id requesting the lock */
+ __le64 pid_namespace;
+ __le64 start; /* initial location to lock */
+ __le64 length; /* num bytes to lock from start */
+ __u8 wait; /* will caller wait for lock to become available? */
+ } __attribute__ ((packed)) filelock_change;
} __attribute__ ((packed));
#define CEPH_MDS_FLAG_REPLAY 1 /* this is a replayed op */
__le32 dist[];
} __attribute__ ((packed));
+#define CEPH_LOCK_FCNTL 1
+#define CEPH_LOCK_FLOCK 2
+
+#define CEPH_LOCK_SHARED 1
+#define CEPH_LOCK_EXCL 2
+#define CEPH_LOCK_UNLOCK 4
+
+struct ceph_filelock {
+ __le64 start;/* file offset to start lock at */
+ __le64 length; /* num bytes to lock; 0 for all following start */
+ __le64 client; /* which client holds the lock */
+ __le64 pid; /* process id holding the lock on the client */
+ __le64 pid_namespace;
+ __u8 type; /* shared lock, exclusive lock, or unlock */
+} __attribute__ ((packed));
+
+
/* file access modes */
#define CEPH_FILE_MODE_PIN 0
#define CEPH_FILE_MODE_RD 1
#define CEPH_CAP_SAUTH 2
#define CEPH_CAP_SLINK 4
#define CEPH_CAP_SXATTR 6
-#define CEPH_CAP_SFILE 8 /* goes at the end (uses >2 cap bits) */
+#define CEPH_CAP_SFILE 8
+#define CEPH_CAP_SFLOCK 20
-#define CEPH_CAP_BITS 16
+#define CEPH_CAP_BITS 22
/* composed values */
#define CEPH_CAP_AUTH_SHARED (CEPH_CAP_GSHARED << CEPH_CAP_SAUTH)
#define CEPH_CAP_FILE_BUFFER (CEPH_CAP_GBUFFER << CEPH_CAP_SFILE)
#define CEPH_CAP_FILE_WREXTEND (CEPH_CAP_GWREXTEND << CEPH_CAP_SFILE)
#define CEPH_CAP_FILE_LAZYIO (CEPH_CAP_GLAZYIO << CEPH_CAP_SFILE)
+#define CEPH_CAP_FLOCK_SHARED (CEPH_CAP_GSHARED << CEPH_CAP_SFLOCK)
+#define CEPH_CAP_FLOCK_EXCL (CEPH_CAP_GEXCL << CEPH_CAP_SFLOCK)
+
/* cap masks (for getattr) */
#define CEPH_STAT_CAP_INODE CEPH_CAP_PIN
CEPH_CAP_FILE_EXCL)
#define CEPH_CAP_ANY_WR (CEPH_CAP_ANY_EXCL | CEPH_CAP_ANY_FILE_WR)
#define CEPH_CAP_ANY (CEPH_CAP_ANY_RD | CEPH_CAP_ANY_EXCL | \
- CEPH_CAP_ANY_FILE_WR | CEPH_CAP_PIN)
+ CEPH_CAP_ANY_FILE_WR | CEPH_CAP_FILE_LAZYIO | \
+ CEPH_CAP_PIN)
#define CEPH_CAP_LOCKS (CEPH_LOCK_IFILE | CEPH_LOCK_IAUTH | CEPH_LOCK_ILINK | \
CEPH_LOCK_IXATTR)
/* client reconnect */
struct ceph_mds_cap_reconnect {
+ __le64 cap_id;
+ __le32 wanted;
+ __le32 issued;
+ __le64 snaprealm;
+ __le64 pathbase; /* base ino for our path to this ino */
+ __le32 flock_len; /* size of flock state blob, if any */
+} __attribute__ ((packed));
+/* followed by flock blob */
+
+struct ceph_mds_cap_reconnect_v1 {
__le64 cap_id;
__le32 wanted;
__le32 issued;
__le64 snaprealm;
__le64 pathbase; /* base ino for our path to this ino */
} __attribute__ ((packed));
-/* followed by encoded string */
struct ceph_mds_snaprealm_reconnect {
__le64 ino; /* snap realm base */
-#ifndef _FS_CEPH_HASH_H
-#define _FS_CEPH_HASH_H
+#ifndef FS_CEPH_HASH_H
+#define FS_CEPH_HASH_H
#define CEPH_STR_HASH_LINUX 0x1 /* linux dcache hash */
#define CEPH_STR_HASH_RJENKINS 0x2 /* robert jenkins' */
case CEPH_OSD_OP_TRUNCATE: return "truncate";
case CEPH_OSD_OP_ZERO: return "zero";
case CEPH_OSD_OP_WRITEFULL: return "writefull";
+ case CEPH_OSD_OP_ROLLBACK: return "rollback";
case CEPH_OSD_OP_APPEND: return "append";
case CEPH_OSD_OP_STARTSYNC: return "startsync";
case CEPH_MDS_OP_LSSNAP: return "lssnap";
case CEPH_MDS_OP_MKSNAP: return "mksnap";
case CEPH_MDS_OP_RMSNAP: return "rmsnap";
+ case CEPH_MDS_OP_SETFILELOCK: return "setfilelock";
+ case CEPH_MDS_OP_GETFILELOCK: return "getfilelock";
}
return "???";
}
-#ifndef _CRUSH_CRUSH_H
-#define _CRUSH_CRUSH_H
+#ifndef CEPH_CRUSH_CRUSH_H
+#define CEPH_CRUSH_CRUSH_H
#include <linux/types.h>
-#ifndef _CRUSH_HASH_H
-#define _CRUSH_HASH_H
+#ifndef CEPH_CRUSH_HASH_H
+#define CEPH_CRUSH_HASH_H
#define CRUSH_HASH_RJENKINS1 0
-#ifndef _CRUSH_MAPPER_H
-#define _CRUSH_MAPPER_H
+#ifndef CEPH_CRUSH_MAPPER_H
+#define CEPH_CRUSH_MAPPER_H
/*
* CRUSH functions for find rules and then mapping an input to an
return crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC);
}
-const u8 *aes_iv = "cephsageyudagreg";
+static const u8 *aes_iv = (u8 *)CEPH_AES_IV;
-int ceph_aes_encrypt(const void *key, int key_len, void *dst, size_t *dst_len,
- const void *src, size_t src_len)
+static int ceph_aes_encrypt(const void *key, int key_len,
+ void *dst, size_t *dst_len,
+ const void *src, size_t src_len)
{
struct scatterlist sg_in[2], sg_out[1];
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
return 0;
}
-int ceph_aes_encrypt2(const void *key, int key_len, void *dst, size_t *dst_len,
- const void *src1, size_t src1_len,
- const void *src2, size_t src2_len)
+static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
+ size_t *dst_len,
+ const void *src1, size_t src1_len,
+ const void *src2, size_t src2_len)
{
struct scatterlist sg_in[3], sg_out[1];
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
return 0;
}
-int ceph_aes_decrypt(const void *key, int key_len, void *dst, size_t *dst_len,
- const void *src, size_t src_len)
+static int ceph_aes_decrypt(const void *key, int key_len,
+ void *dst, size_t *dst_len,
+ const void *src, size_t src_len)
{
struct scatterlist sg_in[1], sg_out[2];
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
return 0;
}
-int ceph_aes_decrypt2(const void *key, int key_len,
- void *dst1, size_t *dst1_len,
- void *dst2, size_t *dst2_len,
- const void *src, size_t src_len)
+static int ceph_aes_decrypt2(const void *key, int key_len,
+ void *dst1, size_t *dst1_len,
+ void *dst2, size_t *dst2_len,
+ const void *src, size_t src_len)
{
struct scatterlist sg_in[1], sg_out[3];
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
const void *src2, size_t src2_len);
/* armor.c */
-extern int ceph_armor(char *dst, const void *src, const void *end);
-extern int ceph_unarmor(void *dst, const char *src, const char *end);
+extern int ceph_armor(char *dst, const char *src, const char *end);
+extern int ceph_unarmor(char *dst, const char *src, const char *end);
#endif
return 0;
}
-#define DEFINE_SHOW_FUNC(name) \
+#define DEFINE_SHOW_FUNC(name) \
static int name##_open(struct inode *inode, struct file *file) \
{ \
struct seq_file *sf; \
int ret = 0;
char name[80];
- snprintf(name, sizeof(name), FSID_FORMAT ".client%lld",
- PR_FSID(&client->fsid), client->monc.auth->global_id);
+ snprintf(name, sizeof(name), "%pU.client%lld", &client->fsid,
+ client->monc.auth->global_id);
client->debugfs_dir = debugfs_create_dir(name, ceph_debugfs_dir);
if (!client->debugfs_dir)
if (!client->debugfs_caps)
goto out;
- client->debugfs_congestion_kb = debugfs_create_file("writeback_congestion_kb",
- 0600,
- client->debugfs_dir,
- client,
- &congestion_kb_fops);
+ client->debugfs_congestion_kb =
+ debugfs_create_file("writeback_congestion_kb",
+ 0600,
+ client->debugfs_dir,
+ client,
+ &congestion_kb_fops);
if (!client->debugfs_congestion_kb)
goto out;
debugfs_remove(client->debugfs_dir);
}
-#else // CONFIG_DEBUG_FS
+#else /* CONFIG_DEBUG_FS */
int __init ceph_debugfs_init(void)
{
{
}
-#endif // CONFIG_DEBUG_FS
+#endif /* CONFIG_DEBUG_FS */
*/
static inline void ceph_encode_addr(struct ceph_entity_addr *a)
{
- a->in_addr.ss_family = htons(a->in_addr.ss_family);
+ __be16 ss_family = htons(a->in_addr.ss_family);
+ a->in_addr.ss_family = *(__u16 *)&ss_family;
}
static inline void ceph_decode_addr(struct ceph_entity_addr *a)
{
- a->in_addr.ss_family = ntohs(a->in_addr.ss_family);
+ __be16 ss_family = *(__be16 *)&a->in_addr.ss_family;
+ a->in_addr.ss_family = ntohs(ss_family);
WARN_ON(a->in_addr.ss_family == 512);
}
const struct inode_operations ceph_dir_iops;
const struct file_operations ceph_dir_fops;
-struct dentry_operations ceph_dentry_ops;
+const struct dentry_operations ceph_dentry_ops;
/*
* Initialize ceph dentry state.
*/
static int __dcache_readdir(struct file *filp,
void *dirent, filldir_t filldir)
+ __releases(inode->i_lock)
+ __acquires(inode->i_lock)
{
struct inode *inode = filp->f_dentry->d_inode;
struct ceph_file_info *fi = filp->private_data;
.create = ceph_create,
};
-struct dentry_operations ceph_dentry_ops = {
+const struct dentry_operations ceph_dentry_ops = {
.d_revalidate = ceph_d_revalidate,
.d_release = ceph_dentry_release,
};
-struct dentry_operations ceph_snapdir_dentry_ops = {
+const struct dentry_operations ceph_snapdir_dentry_ops = {
.d_revalidate = ceph_snapdir_d_revalidate,
.d_release = ceph_dentry_release,
};
-struct dentry_operations ceph_snap_dentry_ops = {
+const struct dentry_operations ceph_snap_dentry_ops = {
.d_release = ceph_dentry_release,
};
/*
* allocate a vector new pages
*/
-struct page **ceph_alloc_page_vector(int num_pages, gfp_t flags)
+static struct page **ceph_alloc_page_vector(int num_pages, gfp_t flags)
{
struct page **pages;
int i;
* throw out any page cache pages in this range. this
* may block.
*/
- truncate_inode_pages_range(inode->i_mapping, pos,
+ truncate_inode_pages_range(inode->i_mapping, pos,
(pos+len) | (PAGE_CACHE_SIZE-1));
} else {
pages = ceph_alloc_page_vector(num_pages, GFP_NOFS);
unsigned long nr_segs, loff_t pos)
{
struct file *filp = iocb->ki_filp;
+ struct ceph_file_info *fi = filp->private_data;
loff_t *ppos = &iocb->ki_pos;
size_t len = iov->iov_len;
struct inode *inode = filp->f_dentry->d_inode;
struct ceph_inode_info *ci = ceph_inode(inode);
- void *base = iov->iov_base;
+ void __user *base = iov->iov_base;
ssize_t ret;
- int got = 0;
+ int want, got = 0;
int checkeof = 0, read = 0;
dout("aio_read %p %llx.%llx %llu~%u trying to get caps on %p\n",
inode, ceph_vinop(inode), pos, (unsigned)len, inode);
again:
__ceph_do_pending_vmtruncate(inode);
- ret = ceph_get_caps(ci, CEPH_CAP_FILE_RD, CEPH_CAP_FILE_CACHE,
- &got, -1);
+ if (fi->fmode & CEPH_FILE_MODE_LAZY)
+ want = CEPH_CAP_FILE_CACHE | CEPH_CAP_FILE_LAZYIO;
+ else
+ want = CEPH_CAP_FILE_CACHE;
+ ret = ceph_get_caps(ci, CEPH_CAP_FILE_RD, want, &got, -1);
if (ret < 0)
goto out;
dout("aio_read %p %llx.%llx %llu~%u got cap refs on %s\n",
inode, ceph_vinop(inode), pos, (unsigned)len,
ceph_cap_string(got));
- if ((got & CEPH_CAP_FILE_CACHE) == 0 ||
+ if ((got & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) == 0 ||
(iocb->ki_filp->f_flags & O_DIRECT) ||
(inode->i_sb->s_flags & MS_SYNCHRONOUS))
/* hmm, this isn't really async... */
unsigned long nr_segs, loff_t pos)
{
struct file *file = iocb->ki_filp;
+ struct ceph_file_info *fi = file->private_data;
struct inode *inode = file->f_dentry->d_inode;
struct ceph_inode_info *ci = ceph_inode(inode);
struct ceph_osd_client *osdc = &ceph_sb_to_client(inode->i_sb)->osdc;
loff_t endoff = pos + iov->iov_len;
- int got = 0;
+ int want, got = 0;
int ret, err;
if (ceph_snap(inode) != CEPH_NOSNAP)
dout("aio_write %p %llx.%llx %llu~%u getting caps. i_size %llu\n",
inode, ceph_vinop(inode), pos, (unsigned)iov->iov_len,
inode->i_size);
- ret = ceph_get_caps(ci, CEPH_CAP_FILE_WR, CEPH_CAP_FILE_BUFFER,
- &got, endoff);
+ if (fi->fmode & CEPH_FILE_MODE_LAZY)
+ want = CEPH_CAP_FILE_BUFFER | CEPH_CAP_FILE_LAZYIO;
+ else
+ want = CEPH_CAP_FILE_BUFFER;
+ ret = ceph_get_caps(ci, CEPH_CAP_FILE_WR, want, &got, endoff);
if (ret < 0)
goto out;
inode, ceph_vinop(inode), pos, (unsigned)iov->iov_len,
ceph_cap_string(got));
- if ((got & CEPH_CAP_FILE_BUFFER) == 0 ||
+ if ((got & (CEPH_CAP_FILE_BUFFER|CEPH_CAP_FILE_LAZYIO)) == 0 ||
(iocb->ki_filp->f_flags & O_DIRECT) ||
(inode->i_sb->s_flags & MS_SYNCHRONOUS)) {
ret = ceph_sync_write(file, iov->iov_base, iov->iov_len,
.aio_write = ceph_aio_write,
.mmap = ceph_mmap,
.fsync = ceph_fsync,
+ .lock = ceph_lock,
+ .flock = ceph_flock,
.splice_read = generic_file_splice_read,
.splice_write = generic_file_splice_write,
.unlocked_ioctl = ceph_ioctl,
* the file is either opened or mmaped
*/
if ((issued & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_RD|
- CEPH_CAP_FILE_WR|CEPH_CAP_FILE_BUFFER|
- CEPH_CAP_FILE_EXCL)) ||
+ CEPH_CAP_FILE_WR|CEPH_CAP_FILE_BUFFER|
+ CEPH_CAP_FILE_EXCL|
+ CEPH_CAP_FILE_LAZYIO)) ||
mapping_mapped(inode->i_mapping) ||
__ceph_caps_file_wanted(ci)) {
ci->i_truncate_pending++;
return 0;
}
+static long ceph_ioctl_lazyio(struct file *file)
+{
+ struct ceph_file_info *fi = file->private_data;
+ struct inode *inode = file->f_dentry->d_inode;
+ struct ceph_inode_info *ci = ceph_inode(inode);
+
+ if ((fi->fmode & CEPH_FILE_MODE_LAZY) == 0) {
+ spin_lock(&inode->i_lock);
+ ci->i_nr_by_mode[fi->fmode]--;
+ fi->fmode |= CEPH_FILE_MODE_LAZY;
+ ci->i_nr_by_mode[fi->fmode]++;
+ spin_unlock(&inode->i_lock);
+ dout("ioctl_layzio: file %p marked lazy\n", file);
+
+ ceph_check_caps(ci, 0, NULL);
+ } else {
+ dout("ioctl_layzio: file %p already lazy\n", file);
+ }
+ return 0;
+}
+
long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
dout("ioctl file %p cmd %u arg %lu\n", file, cmd, arg);
case CEPH_IOC_GET_DATALOC:
return ceph_ioctl_get_dataloc(file, (void __user *)arg);
+
+ case CEPH_IOC_LAZYIO:
+ return ceph_ioctl_lazyio(file);
}
return -ENOTTY;
}
#define CEPH_IOC_GET_DATALOC _IOWR(CEPH_IOCTL_MAGIC, 3, \
struct ceph_ioctl_dataloc)
+#define CEPH_IOC_LAZYIO _IO(CEPH_IOCTL_MAGIC, 4)
+
#endif
--- /dev/null
+#include "ceph_debug.h"
+
+#include <linux/file.h>
+#include <linux/namei.h>
+
+#include "super.h"
+#include "mds_client.h"
+#include "pagelist.h"
+
+/**
+ * Implement fcntl and flock locking functions.
+ */
+static int ceph_lock_message(u8 lock_type, u16 operation, struct file *file,
+ u64 pid, u64 pid_ns,
+ int cmd, u64 start, u64 length, u8 wait)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ struct ceph_mds_client *mdsc =
+ &ceph_sb_to_client(inode->i_sb)->mdsc;
+ struct ceph_mds_request *req;
+ int err;
+
+ req = ceph_mdsc_create_request(mdsc, operation, USE_AUTH_MDS);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+ req->r_inode = igrab(inode);
+
+ dout("ceph_lock_message: rule: %d, op: %d, pid: %llu, start: %llu, "
+ "length: %llu, wait: %d, type`: %d", (int)lock_type,
+ (int)operation, pid, start, length, wait, cmd);
+
+ req->r_args.filelock_change.rule = lock_type;
+ req->r_args.filelock_change.type = cmd;
+ req->r_args.filelock_change.pid = cpu_to_le64(pid);
+ /* This should be adjusted, but I'm not sure if
+ namespaces actually get id numbers*/
+ req->r_args.filelock_change.pid_namespace =
+ cpu_to_le64((u64)pid_ns);
+ req->r_args.filelock_change.start = cpu_to_le64(start);
+ req->r_args.filelock_change.length = cpu_to_le64(length);
+ req->r_args.filelock_change.wait = wait;
+
+ err = ceph_mdsc_do_request(mdsc, inode, req);
+ ceph_mdsc_put_request(req);
+ dout("ceph_lock_message: rule: %d, op: %d, pid: %llu, start: %llu, "
+ "length: %llu, wait: %d, type`: %d err code %d", (int)lock_type,
+ (int)operation, pid, start, length, wait, cmd, err);
+ return err;
+}
+
+/**
+ * Attempt to set an fcntl lock.
+ * For now, this just goes away to the server. Later it may be more awesome.
+ */
+int ceph_lock(struct file *file, int cmd, struct file_lock *fl)
+{
+ u64 length;
+ u8 lock_cmd;
+ int err;
+ u8 wait = 0;
+ u16 op = CEPH_MDS_OP_SETFILELOCK;
+
+ fl->fl_nspid = get_pid(task_tgid(current));
+ dout("ceph_lock, fl_pid:%d", fl->fl_pid);
+
+ /* set wait bit as appropriate, then make command as Ceph expects it*/
+ if (F_SETLKW == cmd)
+ wait = 1;
+ if (F_GETLK == cmd)
+ op = CEPH_MDS_OP_GETFILELOCK;
+
+ if (F_RDLCK == fl->fl_type)
+ lock_cmd = CEPH_LOCK_SHARED;
+ else if (F_WRLCK == fl->fl_type)
+ lock_cmd = CEPH_LOCK_EXCL;
+ else
+ lock_cmd = CEPH_LOCK_UNLOCK;
+
+ if (LLONG_MAX == fl->fl_end)
+ length = 0;
+ else
+ length = fl->fl_end - fl->fl_start + 1;
+
+ err = ceph_lock_message(CEPH_LOCK_FCNTL, op, file,
+ (u64)fl->fl_pid, (u64)fl->fl_nspid,
+ lock_cmd, fl->fl_start,
+ length, wait);
+ if (!err) {
+ dout("mds locked, locking locally");
+ err = posix_lock_file(file, fl, NULL);
+ if (err && (CEPH_MDS_OP_SETFILELOCK == op)) {
+ /* undo! This should only happen if the kernel detects
+ * local deadlock. */
+ ceph_lock_message(CEPH_LOCK_FCNTL, op, file,
+ (u64)fl->fl_pid, (u64)fl->fl_nspid,
+ CEPH_LOCK_UNLOCK, fl->fl_start,
+ length, 0);
+ dout("got %d on posix_lock_file, undid lock", err);
+ }
+ } else {
+ dout("mds returned error code %d", err);
+ }
+ return err;
+}
+
+int ceph_flock(struct file *file, int cmd, struct file_lock *fl)
+{
+ u64 length;
+ u8 lock_cmd;
+ int err;
+ u8 wait = 1;
+
+ fl->fl_nspid = get_pid(task_tgid(current));
+ dout("ceph_flock, fl_pid:%d", fl->fl_pid);
+
+ /* set wait bit, then clear it out of cmd*/
+ if (cmd & LOCK_NB)
+ wait = 0;
+ cmd = cmd & (LOCK_SH | LOCK_EX | LOCK_UN);
+ /* set command sequence that Ceph wants to see:
+ shared lock, exclusive lock, or unlock */
+ if (LOCK_SH == cmd)
+ lock_cmd = CEPH_LOCK_SHARED;
+ else if (LOCK_EX == cmd)
+ lock_cmd = CEPH_LOCK_EXCL;
+ else
+ lock_cmd = CEPH_LOCK_UNLOCK;
+ /* mds requires start and length rather than start and end */
+ if (LLONG_MAX == fl->fl_end)
+ length = 0;
+ else
+ length = fl->fl_end - fl->fl_start + 1;
+
+ err = ceph_lock_message(CEPH_LOCK_FLOCK, CEPH_MDS_OP_SETFILELOCK,
+ file, (u64)fl->fl_pid, (u64)fl->fl_nspid,
+ lock_cmd, fl->fl_start,
+ length, wait);
+ if (!err) {
+ err = flock_lock_file_wait(file, fl);
+ if (err) {
+ ceph_lock_message(CEPH_LOCK_FLOCK,
+ CEPH_MDS_OP_SETFILELOCK,
+ file, (u64)fl->fl_pid,
+ (u64)fl->fl_nspid,
+ CEPH_LOCK_UNLOCK, fl->fl_start,
+ length, 0);
+ dout("got %d on flock_lock_file_wait, undid lock", err);
+ }
+ } else {
+ dout("mds error code %d", err);
+ }
+ return err;
+}
+
+/**
+ * Must be called with BKL already held. Fills in the passed
+ * counter variables, so you can prepare pagelist metadata before calling
+ * ceph_encode_locks.
+ */
+void ceph_count_locks(struct inode *inode, int *fcntl_count, int *flock_count)
+{
+ struct file_lock *lock;
+
+ *fcntl_count = 0;
+ *flock_count = 0;
+
+ for (lock = inode->i_flock; lock != NULL; lock = lock->fl_next) {
+ if (lock->fl_flags & FL_POSIX)
+ ++(*fcntl_count);
+ else if (lock->fl_flags & FL_FLOCK)
+ ++(*flock_count);
+ }
+ dout("counted %d flock locks and %d fcntl locks",
+ *flock_count, *fcntl_count);
+}
+
+/**
+ * Encode the flock and fcntl locks for the given inode into the pagelist.
+ * Format is: #fcntl locks, sequential fcntl locks, #flock locks,
+ * sequential flock locks.
+ * Must be called with BLK already held, and the lock numbers should have
+ * been gathered under the same lock holding window.
+ */
+int ceph_encode_locks(struct inode *inode, struct ceph_pagelist *pagelist,
+ int num_fcntl_locks, int num_flock_locks)
+{
+ struct file_lock *lock;
+ struct ceph_filelock cephlock;
+ int err = 0;
+
+ dout("encoding %d flock and %d fcntl locks", num_flock_locks,
+ num_fcntl_locks);
+ err = ceph_pagelist_append(pagelist, &num_fcntl_locks, sizeof(u32));
+ if (err)
+ goto fail;
+ for (lock = inode->i_flock; lock != NULL; lock = lock->fl_next) {
+ if (lock->fl_flags & FL_POSIX) {
+ err = lock_to_ceph_filelock(lock, &cephlock);
+ if (err)
+ goto fail;
+ err = ceph_pagelist_append(pagelist, &cephlock,
+ sizeof(struct ceph_filelock));
+ }
+ if (err)
+ goto fail;
+ }
+
+ err = ceph_pagelist_append(pagelist, &num_flock_locks, sizeof(u32));
+ if (err)
+ goto fail;
+ for (lock = inode->i_flock; lock != NULL; lock = lock->fl_next) {
+ if (lock->fl_flags & FL_FLOCK) {
+ err = lock_to_ceph_filelock(lock, &cephlock);
+ if (err)
+ goto fail;
+ err = ceph_pagelist_append(pagelist, &cephlock,
+ sizeof(struct ceph_filelock));
+ }
+ if (err)
+ goto fail;
+ }
+fail:
+ return err;
+}
+
+/*
+ * Given a pointer to a lock, convert it to a ceph filelock
+ */
+int lock_to_ceph_filelock(struct file_lock *lock,
+ struct ceph_filelock *cephlock)
+{
+ int err = 0;
+
+ cephlock->start = cpu_to_le64(lock->fl_start);
+ cephlock->length = cpu_to_le64(lock->fl_end - lock->fl_start + 1);
+ cephlock->client = cpu_to_le64(0);
+ cephlock->pid = cpu_to_le64(lock->fl_pid);
+ cephlock->pid_namespace = cpu_to_le64((u64)lock->fl_nspid);
+
+ switch (lock->fl_type) {
+ case F_RDLCK:
+ cephlock->type = CEPH_LOCK_SHARED;
+ break;
+ case F_WRLCK:
+ cephlock->type = CEPH_LOCK_EXCL;
+ break;
+ case F_UNLCK:
+ cephlock->type = CEPH_LOCK_UNLOCK;
+ break;
+ default:
+ dout("Have unknown lock type %d", lock->fl_type);
+ err = -EINVAL;
+ }
+
+ return err;
+}
#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/sched.h>
+#include <linux/smp_lock.h>
#include "mds_client.h"
#include "mon_client.h"
* are no longer valid.
*/
+struct ceph_reconnect_state {
+ struct ceph_pagelist *pagelist;
+ bool flock;
+};
+
static void __wake_requests(struct ceph_mds_client *mdsc,
struct list_head *head);
kfree(req->r_path1);
kfree(req->r_path2);
put_request_session(req);
- ceph_unreserve_caps(&req->r_caps_reservation);
+ ceph_unreserve_caps(req->r_mdsc, &req->r_caps_reservation);
kfree(req);
}
{
req->r_tid = ++mdsc->last_tid;
if (req->r_num_caps)
- ceph_reserve_caps(&req->r_caps_reservation, req->r_num_caps);
+ ceph_reserve_caps(mdsc, &req->r_caps_reservation,
+ req->r_num_caps);
dout("__register_request %p tid %lld\n", req, req->r_tid);
ceph_mdsc_get_request(req);
__insert_request(mdsc, req);
return 0;
}
+/*
+ * open sessions for any export targets for the given mds
+ *
+ * called under mdsc->mutex
+ */
+static void __open_export_target_sessions(struct ceph_mds_client *mdsc,
+ struct ceph_mds_session *session)
+{
+ struct ceph_mds_info *mi;
+ struct ceph_mds_session *ts;
+ int i, mds = session->s_mds;
+ int target;
+
+ if (mds >= mdsc->mdsmap->m_max_mds)
+ return;
+ mi = &mdsc->mdsmap->m_info[mds];
+ dout("open_export_target_sessions for mds%d (%d targets)\n",
+ session->s_mds, mi->num_export_targets);
+
+ for (i = 0; i < mi->num_export_targets; i++) {
+ target = mi->export_targets[i];
+ ts = __ceph_lookup_mds_session(mdsc, target);
+ if (!ts) {
+ ts = register_session(mdsc, target);
+ if (IS_ERR(ts))
+ return;
+ }
+ if (session->s_state == CEPH_MDS_SESSION_NEW ||
+ session->s_state == CEPH_MDS_SESSION_CLOSING)
+ __open_session(mdsc, session);
+ else
+ dout(" mds%d target mds%d %p is %s\n", session->s_mds,
+ i, ts, session_state_name(ts->s_state));
+ ceph_put_mds_session(ts);
+ }
+}
+
+void ceph_mdsc_open_export_target_sessions(struct ceph_mds_client *mdsc,
+ struct ceph_mds_session *session)
+{
+ mutex_lock(&mdsc->mutex);
+ __open_export_target_sessions(mdsc, session);
+ mutex_unlock(&mdsc->mutex);
+}
+
/*
* session caps
*/
last_inode = NULL;
}
if (old_cap) {
- ceph_put_cap(old_cap);
+ ceph_put_cap(session->s_mdsc, old_cap);
old_cap = NULL;
}
if (last_inode)
iput(last_inode);
if (old_cap)
- ceph_put_cap(old_cap);
+ ceph_put_cap(session->s_mdsc, old_cap);
return ret;
}
* Called under s_mutex.
*/
int ceph_add_cap_releases(struct ceph_mds_client *mdsc,
- struct ceph_mds_session *session,
- int extra)
+ struct ceph_mds_session *session)
{
- struct ceph_msg *msg;
+ struct ceph_msg *msg, *partial = NULL;
struct ceph_mds_cap_release *head;
int err = -ENOMEM;
+ int extra = mdsc->client->mount_args->cap_release_safety;
+ int num;
- if (extra < 0)
- extra = mdsc->client->mount_args->cap_release_safety;
+ dout("add_cap_releases %p mds%d extra %d\n", session, session->s_mds,
+ extra);
spin_lock(&session->s_cap_lock);
struct ceph_msg,
list_head);
head = msg->front.iov_base;
- extra += CEPH_CAPS_PER_RELEASE - le32_to_cpu(head->num);
+ num = le32_to_cpu(head->num);
+ if (num) {
+ dout(" partial %p with (%d/%d)\n", msg, num,
+ (int)CEPH_CAPS_PER_RELEASE);
+ extra += CEPH_CAPS_PER_RELEASE - num;
+ partial = msg;
+ }
}
-
while (session->s_num_cap_releases < session->s_nr_caps + extra) {
spin_unlock(&session->s_cap_lock);
msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPRELEASE, PAGE_CACHE_SIZE,
session->s_num_cap_releases += CEPH_CAPS_PER_RELEASE;
}
- if (!list_empty(&session->s_cap_releases)) {
- msg = list_first_entry(&session->s_cap_releases,
- struct ceph_msg,
- list_head);
- head = msg->front.iov_base;
- if (head->num) {
- dout(" queueing non-full %p (%d)\n", msg,
- le32_to_cpu(head->num));
- list_move_tail(&msg->list_head,
- &session->s_cap_releases_done);
- session->s_num_cap_releases -=
- CEPH_CAPS_PER_RELEASE - le32_to_cpu(head->num);
- }
+ if (partial) {
+ head = partial->front.iov_base;
+ num = le32_to_cpu(head->num);
+ dout(" queueing partial %p with %d/%d\n", partial, num,
+ (int)CEPH_CAPS_PER_RELEASE);
+ list_move_tail(&partial->list_head,
+ &session->s_cap_releases_done);
+ session->s_num_cap_releases -= CEPH_CAPS_PER_RELEASE - num;
}
err = 0;
spin_unlock(&session->s_cap_lock);
return ERR_PTR(-ENOMEM);
mutex_init(&req->r_fill_mutex);
+ req->r_mdsc = mdsc;
req->r_started = jiffies;
req->r_resend_mds = -1;
INIT_LIST_HEAD(&req->r_unsafe_dir_item);
req->r_mds = mds;
req->r_attempts++;
+ if (req->r_inode) {
+ struct ceph_cap *cap =
+ ceph_get_cap_for_mds(ceph_inode(req->r_inode), mds);
+
+ if (cap)
+ req->r_sent_on_mseq = cap->mseq;
+ else
+ req->r_sent_on_mseq = -1;
+ }
dout("prepare_send_request %p tid %lld %s (attempt %d)\n", req,
req->r_tid, ceph_mds_op_name(req->r_op), req->r_attempts);
result = le32_to_cpu(head->result);
/*
- * Tolerate 2 consecutive ESTALEs from the same mds.
- * FIXME: we should be looking at the cap migrate_seq.
+ * Handle an ESTALE
+ * if we're not talking to the authority, send to them
+ * if the authority has changed while we weren't looking,
+ * send to new authority
+ * Otherwise we just have to return an ESTALE
*/
if (result == -ESTALE) {
- req->r_direct_mode = USE_AUTH_MDS;
- req->r_num_stale++;
- if (req->r_num_stale <= 2) {
+ dout("got ESTALE on request %llu", req->r_tid);
+ if (!req->r_inode) {
+ /* do nothing; not an authority problem */
+ } else if (req->r_direct_mode != USE_AUTH_MDS) {
+ dout("not using auth, setting for that now");
+ req->r_direct_mode = USE_AUTH_MDS;
__do_request(mdsc, req);
mutex_unlock(&mdsc->mutex);
goto out;
+ } else {
+ struct ceph_inode_info *ci = ceph_inode(req->r_inode);
+ struct ceph_cap *cap =
+ ceph_get_cap_for_mds(ci, req->r_mds);;
+
+ dout("already using auth");
+ if ((!cap || cap != ci->i_auth_cap) ||
+ (cap->mseq != req->r_sent_on_mseq)) {
+ dout("but cap changed, so resending");
+ __do_request(mdsc, req);
+ mutex_unlock(&mdsc->mutex);
+ goto out;
+ }
}
- } else {
- req->r_num_stale = 0;
+ dout("have to return ESTALE on request %llu", req->r_tid);
}
+
if (head->safe) {
req->r_got_safe = true;
__unregister_request(mdsc, req);
if (err == 0) {
if (result == 0 && rinfo->dir_nr)
ceph_readdir_prepopulate(req, req->r_session);
- ceph_unreserve_caps(&req->r_caps_reservation);
+ ceph_unreserve_caps(mdsc, &req->r_caps_reservation);
}
mutex_unlock(&req->r_fill_mutex);
}
mutex_unlock(&mdsc->mutex);
- ceph_add_cap_releases(mdsc, req->r_session, -1);
+ ceph_add_cap_releases(mdsc, req->r_session);
mutex_unlock(&session->s_mutex);
/* kick calling process */
static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap,
void *arg)
{
- struct ceph_mds_cap_reconnect rec;
+ union {
+ struct ceph_mds_cap_reconnect v2;
+ struct ceph_mds_cap_reconnect_v1 v1;
+ } rec;
+ size_t reclen;
struct ceph_inode_info *ci;
- struct ceph_pagelist *pagelist = arg;
+ struct ceph_reconnect_state *recon_state = arg;
+ struct ceph_pagelist *pagelist = recon_state->pagelist;
char *path;
int pathlen, err;
u64 pathbase;
spin_lock(&inode->i_lock);
cap->seq = 0; /* reset cap seq */
cap->issue_seq = 0; /* and issue_seq */
- rec.cap_id = cpu_to_le64(cap->cap_id);
- rec.pathbase = cpu_to_le64(pathbase);
- rec.wanted = cpu_to_le32(__ceph_caps_wanted(ci));
- rec.issued = cpu_to_le32(cap->issued);
- rec.size = cpu_to_le64(inode->i_size);
- ceph_encode_timespec(&rec.mtime, &inode->i_mtime);
- ceph_encode_timespec(&rec.atime, &inode->i_atime);
- rec.snaprealm = cpu_to_le64(ci->i_snap_realm->ino);
+
+ if (recon_state->flock) {
+ rec.v2.cap_id = cpu_to_le64(cap->cap_id);
+ rec.v2.wanted = cpu_to_le32(__ceph_caps_wanted(ci));
+ rec.v2.issued = cpu_to_le32(cap->issued);
+ rec.v2.snaprealm = cpu_to_le64(ci->i_snap_realm->ino);
+ rec.v2.pathbase = cpu_to_le64(pathbase);
+ rec.v2.flock_len = 0;
+ reclen = sizeof(rec.v2);
+ } else {
+ rec.v1.cap_id = cpu_to_le64(cap->cap_id);
+ rec.v1.wanted = cpu_to_le32(__ceph_caps_wanted(ci));
+ rec.v1.issued = cpu_to_le32(cap->issued);
+ rec.v1.size = cpu_to_le64(inode->i_size);
+ ceph_encode_timespec(&rec.v1.mtime, &inode->i_mtime);
+ ceph_encode_timespec(&rec.v1.atime, &inode->i_atime);
+ rec.v1.snaprealm = cpu_to_le64(ci->i_snap_realm->ino);
+ rec.v1.pathbase = cpu_to_le64(pathbase);
+ reclen = sizeof(rec.v1);
+ }
spin_unlock(&inode->i_lock);
- err = ceph_pagelist_append(pagelist, &rec, sizeof(rec));
+ if (recon_state->flock) {
+ int num_fcntl_locks, num_flock_locks;
+
+ lock_kernel();
+ ceph_count_locks(inode, &num_fcntl_locks, &num_flock_locks);
+ rec.v2.flock_len = (2*sizeof(u32) +
+ (num_fcntl_locks+num_flock_locks) *
+ sizeof(struct ceph_filelock));
+
+ err = ceph_pagelist_append(pagelist, &rec, reclen);
+ if (!err)
+ err = ceph_encode_locks(inode, pagelist,
+ num_fcntl_locks,
+ num_flock_locks);
+ unlock_kernel();
+ }
out:
kfree(path);
int mds = session->s_mds;
int err = -ENOMEM;
struct ceph_pagelist *pagelist;
+ struct ceph_reconnect_state recon_state;
pr_info("mds%d reconnect start\n", mds);
err = ceph_pagelist_encode_32(pagelist, session->s_nr_caps);
if (err)
goto fail;
- err = iterate_session_caps(session, encode_caps_cb, pagelist);
+
+ recon_state.pagelist = pagelist;
+ recon_state.flock = session->s_con.peer_features & CEPH_FEATURE_FLOCK;
+ err = iterate_session_caps(session, encode_caps_cb, &recon_state);
if (err < 0)
goto fail;
}
reply->pagelist = pagelist;
+ if (recon_state.flock)
+ reply->hdr.version = cpu_to_le16(2);
reply->hdr.data_len = cpu_to_le32(pagelist->length);
reply->nr_pages = calc_pages_for(0, pagelist->length);
ceph_con_send(&session->s_con, reply);
oldstate = ceph_mdsmap_get_state(oldmap, i);
newstate = ceph_mdsmap_get_state(newmap, i);
- dout("check_new_map mds%d state %s -> %s (session %s)\n",
+ dout("check_new_map mds%d state %s%s -> %s%s (session %s)\n",
i, ceph_mds_state_name(oldstate),
+ ceph_mdsmap_is_laggy(oldmap, i) ? " (laggy)" : "",
ceph_mds_state_name(newstate),
+ ceph_mdsmap_is_laggy(newmap, i) ? " (laggy)" : "",
session_state_name(s->s_state));
if (memcmp(ceph_mdsmap_get_addr(oldmap, i),
wake_up_session_caps(s, 1);
}
}
+
+ for (i = 0; i < newmap->m_max_mds && i < mdsc->max_sessions; i++) {
+ s = mdsc->sessions[i];
+ if (!s)
+ continue;
+ if (!ceph_mdsmap_is_laggy(newmap, i))
+ continue;
+ if (s->s_state == CEPH_MDS_SESSION_OPEN ||
+ s->s_state == CEPH_MDS_SESSION_HUNG ||
+ s->s_state == CEPH_MDS_SESSION_CLOSING) {
+ dout(" connecting to export targets of laggy mds%d\n",
+ i);
+ __open_export_target_sessions(mdsc, s);
+ }
+ }
}
send_renew_caps(mdsc, s);
else
ceph_con_keepalive(&s->s_con);
- ceph_add_cap_releases(mdsc, s, -1);
+ ceph_add_cap_releases(mdsc, s);
if (s->s_state == CEPH_MDS_SESSION_OPEN ||
s->s_state == CEPH_MDS_SESSION_HUNG)
ceph_send_cap_releases(mdsc, s);
spin_lock_init(&mdsc->dentry_lru_lock);
INIT_LIST_HEAD(&mdsc->dentry_lru);
+ ceph_caps_init(mdsc);
+ ceph_adjust_min_caps(mdsc, client->min_caps);
+
return 0;
}
if (mdsc->mdsmap)
ceph_mdsmap_destroy(mdsc->mdsmap);
kfree(mdsc->sessions);
+ ceph_caps_finalize(mdsc);
}
struct ceph_mds_request {
u64 r_tid; /* transaction id */
struct rb_node r_node;
+ struct ceph_mds_client *r_mdsc;
int r_op; /* mds op code */
int r_mds;
int r_attempts; /* resend attempts */
int r_num_fwd; /* number of forward attempts */
- int r_num_stale;
int r_resend_mds; /* mds to resend to next, if any*/
+ u32 r_sent_on_mseq; /* cap mseq request was sent at*/
struct kref r_kref;
struct list_head r_wait;
spinlock_t cap_dirty_lock; /* protects above items */
wait_queue_head_t cap_flushing_wq;
+ /*
+ * Cap reservations
+ *
+ * Maintain a global pool of preallocated struct ceph_caps, referenced
+ * by struct ceph_caps_reservations. This ensures that we preallocate
+ * memory needed to successfully process an MDS response. (If an MDS
+ * sends us cap information and we fail to process it, we will have
+ * problems due to the client and MDS being out of sync.)
+ *
+ * Reservations are 'owned' by a ceph_cap_reservation context.
+ */
+ spinlock_t caps_list_lock;
+ struct list_head caps_list; /* unused (reserved or
+ unreserved) */
+ int caps_total_count; /* total caps allocated */
+ int caps_use_count; /* in use */
+ int caps_reserve_count; /* unused, reserved */
+ int caps_avail_count; /* unused, unreserved */
+ int caps_min_count; /* keep at least this many
+ (unreserved) */
+
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_file;
#endif
}
extern int ceph_add_cap_releases(struct ceph_mds_client *mdsc,
- struct ceph_mds_session *session,
- int extra);
+ struct ceph_mds_session *session);
extern void ceph_send_cap_releases(struct ceph_mds_client *mdsc,
struct ceph_mds_session *session);
extern void ceph_mdsc_handle_map(struct ceph_mds_client *mdsc,
struct ceph_msg *msg);
+extern void ceph_mdsc_open_export_target_sessions(struct ceph_mds_client *mdsc,
+ struct ceph_mds_session *session);
+
#endif
struct ceph_entity_addr addr;
u32 num_export_targets;
void *pexport_targets = NULL;
+ struct ceph_timespec laggy_since;
ceph_decode_need(p, end, sizeof(u64)*2 + 1 + sizeof(u32), bad);
global_id = ceph_decode_64(p);
state_seq = ceph_decode_64(p);
ceph_decode_copy(p, &addr, sizeof(addr));
ceph_decode_addr(&addr);
- *p += sizeof(struct ceph_timespec);
+ ceph_decode_copy(p, &laggy_since, sizeof(laggy_since));
*p += sizeof(u32);
ceph_decode_32_safe(p, end, namelen, bad);
*p += namelen;
m->m_info[mds].global_id = global_id;
m->m_info[mds].state = state;
m->m_info[mds].addr = addr;
+ m->m_info[mds].laggy =
+ (laggy_since.tv_sec != 0 ||
+ laggy_since.tv_nsec != 0);
m->m_info[mds].num_export_targets = num_export_targets;
if (num_export_targets) {
m->m_info[mds].export_targets =
struct ceph_entity_addr addr;
s32 state;
int num_export_targets;
+ bool laggy;
u32 *export_targets;
};
return m->m_info[w].state;
}
+static inline bool ceph_mdsmap_is_laggy(struct ceph_mdsmap *m, int w)
+{
+ if (w >= 0 && w < m->m_max_mds)
+ return m->m_info[w].laggy;
+ return false;
+}
+
extern int ceph_mdsmap_get_random_mds(struct ceph_mdsmap *m);
extern struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end);
extern void ceph_mdsmap_destroy(struct ceph_mdsmap *m);
destroy_workqueue(ceph_msgr_wq);
}
-void ceph_msgr_flush()
+void ceph_msgr_flush(void)
{
flush_workqueue(ceph_msgr_wq);
}
dout("prepare_write_connect %p cseq=%d gseq=%d proto=%d\n", con,
con->connect_seq, global_seq, proto);
- con->out_connect.features = cpu_to_le64(CEPH_FEATURE_SUPPORTED_CLIENT);
+ con->out_connect.features = cpu_to_le64(CEPH_FEATURE_SUPPORTED);
con->out_connect.host_type = cpu_to_le32(CEPH_ENTITY_TYPE_CLIENT);
con->out_connect.connect_seq = cpu_to_le32(con->connect_seq);
con->out_connect.global_seq = cpu_to_le32(global_seq);
sizeof(con->peer_addr)) != 0 &&
!(addr_is_blank(&con->actual_peer_addr.in_addr) &&
con->actual_peer_addr.nonce == con->peer_addr.nonce)) {
- pr_warning("wrong peer, want %s/%lld, got %s/%lld\n",
+ pr_warning("wrong peer, want %s/%d, got %s/%d\n",
pr_addr(&con->peer_addr.in_addr),
- le64_to_cpu(con->peer_addr.nonce),
+ (int)le32_to_cpu(con->peer_addr.nonce),
pr_addr(&con->actual_peer_addr.in_addr),
- le64_to_cpu(con->actual_peer_addr.nonce));
+ (int)le32_to_cpu(con->actual_peer_addr.nonce));
con->error_msg = "wrong peer at address";
return -1;
}
static int process_connect(struct ceph_connection *con)
{
- u64 sup_feat = CEPH_FEATURE_SUPPORTED_CLIENT;
- u64 req_feat = CEPH_FEATURE_REQUIRED_CLIENT;
+ u64 sup_feat = CEPH_FEATURE_SUPPORTED;
+ u64 req_feat = CEPH_FEATURE_REQUIRED;
u64 server_feat = le64_to_cpu(con->in_reply.features);
dout("process_connect on %p tag %d\n", con, (int)con->in_tag);
static int read_partial_message_section(struct ceph_connection *con,
- struct kvec *section, unsigned int sec_len,
- u32 *crc)
+ struct kvec *section,
+ unsigned int sec_len, u32 *crc)
{
int left;
int ret;
/* middle */
if (m->middle) {
- ret = read_partial_message_section(con, &m->middle->vec, middle_len,
+ ret = read_partial_message_section(con, &m->middle->vec,
+ middle_len,
&con->in_middle_crc);
if (ret <= 0)
return ret;
/*
* in case we faulted due to authentication, invalidate our
* current tickets so that we can get new ones.
- */
+ */
if (con->auth_retry && con->ops->invalidate_authorizer) {
dout("calling invalidate_authorizer()\n");
con->ops->invalidate_authorizer(con);
}
/*
- * statfs
+ * generic requests (e.g., statfs, poolop)
*/
static struct ceph_mon_generic_request *__lookup_generic_req(
struct ceph_mon_client *monc, u64 tid)
return m;
}
+static int do_generic_request(struct ceph_mon_client *monc,
+ struct ceph_mon_generic_request *req)
+{
+ int err;
+
+ /* register request */
+ mutex_lock(&monc->mutex);
+ req->tid = ++monc->last_tid;
+ req->request->hdr.tid = cpu_to_le64(req->tid);
+ __insert_generic_request(monc, req);
+ monc->num_generic_requests++;
+ ceph_con_send(monc->con, ceph_msg_get(req->request));
+ mutex_unlock(&monc->mutex);
+
+ err = wait_for_completion_interruptible(&req->completion);
+
+ mutex_lock(&monc->mutex);
+ rb_erase(&req->node, &monc->generic_request_tree);
+ monc->num_generic_requests--;
+ mutex_unlock(&monc->mutex);
+
+ if (!err)
+ err = req->result;
+ return err;
+}
+
+/*
+ * statfs
+ */
static void handle_statfs_reply(struct ceph_mon_client *monc,
struct ceph_msg *msg)
{
return;
bad:
- pr_err("corrupt generic reply, no tid\n");
+ pr_err("corrupt generic reply, tid %llu\n", tid);
ceph_msg_dump(msg);
}
kref_init(&req->kref);
req->buf = buf;
+ req->buf_len = sizeof(*buf);
init_completion(&req->completion);
err = -ENOMEM;
h->monhdr.session_mon_tid = 0;
h->fsid = monc->monmap->fsid;
- /* register request */
- mutex_lock(&monc->mutex);
- req->tid = ++monc->last_tid;
- req->request->hdr.tid = cpu_to_le64(req->tid);
- __insert_generic_request(monc, req);
- monc->num_generic_requests++;
- mutex_unlock(&monc->mutex);
+ err = do_generic_request(monc, req);
- /* send request and wait */
- ceph_con_send(monc->con, ceph_msg_get(req->request));
- err = wait_for_completion_interruptible(&req->completion);
+out:
+ kref_put(&req->kref, release_generic_request);
+ return err;
+}
+
+/*
+ * pool ops
+ */
+static int get_poolop_reply_buf(const char *src, size_t src_len,
+ char *dst, size_t dst_len)
+{
+ u32 buf_len;
+
+ if (src_len != sizeof(u32) + dst_len)
+ return -EINVAL;
+
+ buf_len = le32_to_cpu(*(u32 *)src);
+ if (buf_len != dst_len)
+ return -EINVAL;
+
+ memcpy(dst, src + sizeof(u32), dst_len);
+ return 0;
+}
+
+static void handle_poolop_reply(struct ceph_mon_client *monc,
+ struct ceph_msg *msg)
+{
+ struct ceph_mon_generic_request *req;
+ struct ceph_mon_poolop_reply *reply = msg->front.iov_base;
+ u64 tid = le64_to_cpu(msg->hdr.tid);
+
+ if (msg->front.iov_len < sizeof(*reply))
+ goto bad;
+ dout("handle_poolop_reply %p tid %llu\n", msg, tid);
mutex_lock(&monc->mutex);
- rb_erase(&req->node, &monc->generic_request_tree);
- monc->num_generic_requests--;
+ req = __lookup_generic_req(monc, tid);
+ if (req) {
+ if (req->buf_len &&
+ get_poolop_reply_buf(msg->front.iov_base + sizeof(*reply),
+ msg->front.iov_len - sizeof(*reply),
+ req->buf, req->buf_len) < 0) {
+ mutex_unlock(&monc->mutex);
+ goto bad;
+ }
+ req->result = le32_to_cpu(reply->reply_code);
+ get_generic_request(req);
+ }
mutex_unlock(&monc->mutex);
+ if (req) {
+ complete(&req->completion);
+ put_generic_request(req);
+ }
+ return;
- if (!err)
- err = req->result;
+bad:
+ pr_err("corrupt generic reply, tid %llu\n", tid);
+ ceph_msg_dump(msg);
+}
+
+/*
+ * Do a synchronous pool op.
+ */
+int ceph_monc_do_poolop(struct ceph_mon_client *monc, u32 op,
+ u32 pool, u64 snapid,
+ char *buf, int len)
+{
+ struct ceph_mon_generic_request *req;
+ struct ceph_mon_poolop *h;
+ int err;
+
+ req = kzalloc(sizeof(*req), GFP_NOFS);
+ if (!req)
+ return -ENOMEM;
+
+ kref_init(&req->kref);
+ req->buf = buf;
+ req->buf_len = len;
+ init_completion(&req->completion);
+
+ err = -ENOMEM;
+ req->request = ceph_msg_new(CEPH_MSG_POOLOP, sizeof(*h), GFP_NOFS);
+ if (!req->request)
+ goto out;
+ req->reply = ceph_msg_new(CEPH_MSG_POOLOP_REPLY, 1024, GFP_NOFS);
+ if (!req->reply)
+ goto out;
+
+ /* fill out request */
+ req->request->hdr.version = cpu_to_le16(2);
+ h = req->request->front.iov_base;
+ h->monhdr.have_version = 0;
+ h->monhdr.session_mon = cpu_to_le16(-1);
+ h->monhdr.session_mon_tid = 0;
+ h->fsid = monc->monmap->fsid;
+ h->pool = cpu_to_le32(pool);
+ h->op = cpu_to_le32(op);
+ h->auid = 0;
+ h->snapid = cpu_to_le64(snapid);
+ h->name_len = 0;
+
+ err = do_generic_request(monc, req);
out:
kref_put(&req->kref, release_generic_request);
return err;
}
+int ceph_monc_create_snapid(struct ceph_mon_client *monc,
+ u32 pool, u64 *snapid)
+{
+ return ceph_monc_do_poolop(monc, POOL_OP_CREATE_UNMANAGED_SNAP,
+ pool, 0, (char *)snapid, sizeof(*snapid));
+
+}
+
+int ceph_monc_delete_snapid(struct ceph_mon_client *monc,
+ u32 pool, u64 snapid)
+{
+ return ceph_monc_do_poolop(monc, POOL_OP_CREATE_UNMANAGED_SNAP,
+ pool, snapid, 0, 0);
+
+}
+
/*
- * Resend pending statfs requests.
+ * Resend pending generic requests.
*/
static void __resend_generic_request(struct ceph_mon_client *monc)
{
handle_statfs_reply(monc, msg);
break;
+ case CEPH_MSG_POOLOP_REPLY:
+ handle_poolop_reply(monc, msg);
+ break;
+
case CEPH_MSG_MON_MAP:
ceph_monc_handle_map(monc, msg);
break;
case CEPH_MSG_MON_SUBSCRIBE_ACK:
m = ceph_msg_get(monc->m_subscribe_ack);
break;
+ case CEPH_MSG_POOLOP_REPLY:
case CEPH_MSG_STATFS_REPLY:
return get_generic_reply(con, hdr, skip);
case CEPH_MSG_AUTH_REPLY:
struct rb_node node;
int result;
void *buf;
+ int buf_len;
struct completion completion;
struct ceph_msg *request; /* original request */
struct ceph_msg *reply; /* and reply */
extern int ceph_monc_validate_auth(struct ceph_mon_client *monc);
+extern int ceph_monc_create_snapid(struct ceph_mon_client *monc,
+ u32 pool, u64 *snapid);
+extern int ceph_monc_delete_snapid(struct ceph_mon_client *monc,
+ u32 pool, u64 snapid);
#endif
-#ifndef __MSGR_H
-#define __MSGR_H
+#ifndef CEPH_MSGR_H
+#define CEPH_MSGR_H
/*
* Data types for message passing layer used by Ceph.
/* it may be a short read due to an object boundary */
req->r_pages = pages;
- num_pages = calc_pages_for(off, *plen);
- req->r_num_pages = num_pages;
dout("readpages final extent is %llu~%llu (%d pages)\n",
off, *plen, req->r_num_pages);
/* it may be a short write due to an object boundary */
req->r_pages = pages;
- req->r_num_pages = calc_pages_for(off, len);
dout("writepages %llu~%llu (%d pages)\n", off, len,
req->r_num_pages);
* authentication
*/
static int get_authorizer(struct ceph_connection *con,
- void **buf, int *len, int *proto,
- void **reply_buf, int *reply_len, int force_new)
+ void **buf, int *len, int *proto,
+ void **reply_buf, int *reply_len, int force_new)
{
struct ceph_osd *o = con->private;
struct ceph_osd_client *osdc = o->o_osdc;
&o->o_authorizer_reply_buf,
&o->o_authorizer_reply_buf_len);
if (ret)
- return ret;
+ return ret;
}
*proto = ac->protocol;
kfree(pi);
}
-void __decode_pool(void **p, struct ceph_pg_pool_info *pi)
+static int __decode_pool(void **p, void *end, struct ceph_pg_pool_info *pi)
{
+ unsigned n, m;
+
ceph_decode_copy(p, &pi->v, sizeof(pi->v));
calc_pg_masks(pi);
- *p += le32_to_cpu(pi->v.num_snaps) * sizeof(u64);
+
+ /* num_snaps * snap_info_t */
+ n = le32_to_cpu(pi->v.num_snaps);
+ while (n--) {
+ ceph_decode_need(p, end, sizeof(u64) + 1 + sizeof(u64) +
+ sizeof(struct ceph_timespec), bad);
+ *p += sizeof(u64) + /* key */
+ 1 + sizeof(u64) + /* u8, snapid */
+ sizeof(struct ceph_timespec);
+ m = ceph_decode_32(p); /* snap name */
+ *p += m;
+ }
+
*p += le32_to_cpu(pi->v.num_removed_snap_intervals) * sizeof(u64) * 2;
+ return 0;
+
+bad:
+ return -EINVAL;
}
static int __decode_pool_names(void **p, void *end, struct ceph_osdmap *map)
kfree(pi);
goto bad;
}
- __decode_pool(p, pi);
+ err = __decode_pool(p, end, pi);
+ if (err < 0)
+ goto bad;
__insert_pg_pool(&map->pg_pools, pi);
}
pi->id = pool;
__insert_pg_pool(&map->pg_pools, pi);
}
- __decode_pool(p, pi);
+ err = __decode_pool(p, end, pi);
+ if (err < 0)
+ goto bad;
}
if (version >= 5 && __decode_pool_names(p, end, map) < 0)
goto bad;
node)->pgid, pgid) <= 0) {
struct ceph_pg_mapping *cur =
rb_entry(rbp, struct ceph_pg_mapping, node);
-
+
rbp = rb_next(rbp);
dout(" removed pg_temp %llx\n", *(u64 *)&cur->pgid);
rb_erase(&cur->node, &map->pg_temp);
ruleno = crush_find_rule(osdmap->crush, pool->v.crush_ruleset,
pool->v.type, pool->v.size);
if (ruleno < 0) {
- pr_err("no crush rule pool %d type %d size %d\n",
- poolid, pool->v.type, pool->v.size);
+ pr_err("no crush rule pool %d ruleset %d type %d size %d\n",
+ poolid, pool->v.crush_ruleset, pool->v.type,
+ pool->v.size);
return NULL;
}
-#ifndef __RADOS_H
-#define __RADOS_H
+#ifndef CEPH_RADOS_H
+#define CEPH_RADOS_H
/*
* Data types for the Ceph distributed object storage layer RADOS
CEPH_OSD_OP_TMAPGET = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_DATA | 12,
CEPH_OSD_OP_CREATE = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 13,
+ CEPH_OSD_OP_ROLLBACK= CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 14,
/** attrs **/
/* read */
return (op & CEPH_OSD_OP_MODE) == CEPH_OSD_OP_MODE_WR;
}
+/*
+ * note that the following tmap stuff is also defined in the ceph librados.h
+ * any modification here needs to be updated there
+ */
#define CEPH_OSD_TMAP_HDR 'h'
#define CEPH_OSD_TMAP_SET 's'
#define CEPH_OSD_TMAP_RM 'r'
CEPH_OSD_FLAG_PARALLELEXEC = 512, /* execute op in parallel */
CEPH_OSD_FLAG_PGOP = 1024, /* pg op, no object */
CEPH_OSD_FLAG_EXEC = 2048, /* op may exec */
+ CEPH_OSD_FLAG_EXEC_PUBLIC = 4096, /* op may exec (public) */
};
enum {
struct {
__le64 cookie, count;
} __attribute__ ((packed)) pgls;
+ struct {
+ __le64 snapid;
+ } __attribute__ ((packed)) snap;
};
__le32 payload_len;
} __attribute__ ((packed));
#include "ceph_debug.h"
#include <linux/backing-dev.h>
+#include <linux/ctype.h>
#include <linux/fs.h>
#include <linux/inet.h>
#include <linux/in6.h>
}
-static int ceph_syncfs(struct super_block *sb, int wait)
+static int ceph_sync_fs(struct super_block *sb, int wait)
{
- dout("sync_fs %d\n", wait);
+ struct ceph_client *client = ceph_sb_to_client(sb);
+
+ if (!wait) {
+ dout("sync_fs (non-blocking)\n");
+ ceph_flush_dirty_caps(&client->mdsc);
+ dout("sync_fs (non-blocking) done\n");
+ return 0;
+ }
+
+ dout("sync_fs (blocking)\n");
ceph_osdc_sync(&ceph_sb_to_client(sb)->osdc);
ceph_mdsc_sync(&ceph_sb_to_client(sb)->mdsc);
- dout("sync_fs %d done\n", wait);
+ dout("sync_fs (blocking) done\n");
return 0;
}
struct ceph_mount_args *args = client->mount_args;
if (args->flags & CEPH_OPT_FSID)
- seq_printf(m, ",fsidmajor=%llu,fsidminor%llu",
- le64_to_cpu(*(__le64 *)&args->fsid.fsid[0]),
- le64_to_cpu(*(__le64 *)&args->fsid.fsid[8]));
+ seq_printf(m, ",fsid=%pU", &args->fsid);
if (args->flags & CEPH_OPT_NOSHARE)
seq_puts(m, ",noshare");
if (args->flags & CEPH_OPT_DIRSTAT)
.alloc_inode = ceph_alloc_inode,
.destroy_inode = ceph_destroy_inode,
.write_inode = ceph_write_inode,
- .sync_fs = ceph_syncfs,
+ .sync_fs = ceph_sync_fs,
.put_super = ceph_put_super,
.show_options = ceph_show_options,
.statfs = ceph_statfs,
* mount options
*/
enum {
- Opt_fsidmajor,
- Opt_fsidminor,
- Opt_monport,
Opt_wsize,
Opt_rsize,
Opt_osdtimeout,
Opt_congestion_kb,
Opt_last_int,
/* int args above */
+ Opt_fsid,
Opt_snapdirname,
Opt_name,
Opt_secret,
};
static match_table_t arg_tokens = {
- {Opt_fsidmajor, "fsidmajor=%ld"},
- {Opt_fsidminor, "fsidminor=%ld"},
- {Opt_monport, "monport=%d"},
{Opt_wsize, "wsize=%d"},
{Opt_rsize, "rsize=%d"},
{Opt_osdtimeout, "osdtimeout=%d"},
{Opt_readdir_max_bytes, "readdir_max_bytes=%d"},
{Opt_congestion_kb, "write_congestion_kb=%d"},
/* int args above */
+ {Opt_fsid, "fsid=%s"},
{Opt_snapdirname, "snapdirname=%s"},
{Opt_name, "name=%s"},
{Opt_secret, "secret=%s"},
{-1, NULL}
};
+static int parse_fsid(const char *str, struct ceph_fsid *fsid)
+{
+ int i = 0;
+ char tmp[3];
+ int err = -EINVAL;
+ int d;
+
+ dout("parse_fsid '%s'\n", str);
+ tmp[2] = 0;
+ while (*str && i < 16) {
+ if (ispunct(*str)) {
+ str++;
+ continue;
+ }
+ if (!isxdigit(str[0]) || !isxdigit(str[1]))
+ break;
+ tmp[0] = str[0];
+ tmp[1] = str[1];
+ if (sscanf(tmp, "%x", &d) < 1)
+ break;
+ fsid->fsid[i] = d & 0xff;
+ i++;
+ str += 2;
+ }
+
+ if (i == 16)
+ err = 0;
+ dout("parse_fsid ret %d got fsid %pU", err, fsid);
+ return err;
+}
static struct ceph_mount_args *parse_mount_args(int flags, char *options,
const char *dev_name,
dout("got token %d\n", token);
}
switch (token) {
- case Opt_fsidmajor:
- *(__le64 *)&args->fsid.fsid[0] = cpu_to_le64(intval);
- break;
- case Opt_fsidminor:
- *(__le64 *)&args->fsid.fsid[8] = cpu_to_le64(intval);
- break;
case Opt_ip:
err = ceph_parse_ips(argstr[0].from,
argstr[0].to,
args->flags |= CEPH_OPT_MYIP;
break;
+ case Opt_fsid:
+ err = parse_fsid(argstr[0].from, &args->fsid);
+ if (err == 0)
+ args->flags |= CEPH_OPT_FSID;
+ break;
case Opt_snapdirname:
kfree(args->snapdir_name);
args->snapdir_name = kstrndup(argstr[0].from,
case Opt_osdkeepalivetimeout:
args->osd_keepalive_timeout = intval;
break;
+ case Opt_osd_idle_ttl:
+ args->osd_idle_ttl = intval;
+ break;
case Opt_mount_timeout:
args->mount_timeout = intval;
break;
/* caps */
client->min_caps = args->max_readdir;
- ceph_adjust_min_caps(client->min_caps);
/* subsystems */
err = ceph_monc_init(&client->monc, client);
ceph_monc_stop(&client->monc);
- ceph_adjust_min_caps(-client->min_caps);
-
ceph_debugfs_client_cleanup(client);
destroy_workqueue(client->wb_wq);
destroy_workqueue(client->pg_inv_wq);
{
if (client->have_fsid) {
if (ceph_fsid_compare(&client->fsid, fsid)) {
- pr_err("bad fsid, had " FSID_FORMAT " got " FSID_FORMAT,
- PR_FSID(&client->fsid), PR_FSID(fsid));
+ pr_err("bad fsid, had %pU got %pU",
+ &client->fsid, fsid);
return -1;
}
} else {
- pr_info("client%lld fsid " FSID_FORMAT "\n",
- client->monc.auth->global_id, PR_FSID(fsid));
+ pr_info("client%lld fsid %pU\n", client->monc.auth->global_id,
+ fsid);
memcpy(&client->fsid, fsid, sizeof(*fsid));
ceph_debugfs_client_init(client);
client->have_fsid = true;
if (ret)
goto out_msgr;
- ceph_caps_init();
-
ret = register_filesystem(&ceph_fs_type);
if (ret)
goto out_icache;
{
dout("exit_ceph\n");
unregister_filesystem(&ceph_fs_type);
- ceph_caps_finalize();
destroy_caches();
ceph_msgr_exit();
ceph_debugfs_cleanup();
#define CEPH_BLOCK_SHIFT 20 /* 1 MB */
#define CEPH_BLOCK (1 << CEPH_BLOCK_SHIFT)
+/*
+ * Supported features
+ */
+#define CEPH_FEATURE_SUPPORTED CEPH_FEATURE_NOSRCADDR | CEPH_FEATURE_FLOCK
+#define CEPH_FEATURE_REQUIRED CEPH_FEATURE_NOSRCADDR
+
/*
* mount options
*/
/* what the mds thinks we want */
extern int __ceph_caps_mds_wanted(struct ceph_inode_info *ci);
-extern void ceph_caps_init(void);
-extern void ceph_caps_finalize(void);
-extern void ceph_adjust_min_caps(int delta);
-extern int ceph_reserve_caps(struct ceph_cap_reservation *ctx, int need);
-extern int ceph_unreserve_caps(struct ceph_cap_reservation *ctx);
+extern void ceph_caps_init(struct ceph_mds_client *mdsc);
+extern void ceph_caps_finalize(struct ceph_mds_client *mdsc);
+extern void ceph_adjust_min_caps(struct ceph_mds_client *mdsc, int delta);
+extern int ceph_reserve_caps(struct ceph_mds_client *mdsc,
+ struct ceph_cap_reservation *ctx, int need);
+extern int ceph_unreserve_caps(struct ceph_mds_client *mdsc,
+ struct ceph_cap_reservation *ctx);
extern void ceph_reservation_status(struct ceph_client *client,
int *total, int *avail, int *used,
int *reserved, int *min);
extern const char *ceph_msg_type_name(int type);
extern int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid);
-#define FSID_FORMAT "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" \
- "%02x%02x%02x%02x%02x%02x"
-#define PR_FSID(f) (f)->fsid[0], (f)->fsid[1], (f)->fsid[2], (f)->fsid[3], \
- (f)->fsid[4], (f)->fsid[5], (f)->fsid[6], (f)->fsid[7], \
- (f)->fsid[8], (f)->fsid[9], (f)->fsid[10], (f)->fsid[11], \
- (f)->fsid[12], (f)->fsid[13], (f)->fsid[14], (f)->fsid[15]
-
/* inode.c */
extern const struct inode_operations ceph_file_iops;
__ceph_remove_cap(cap);
spin_unlock(&inode->i_lock);
}
-extern void ceph_put_cap(struct ceph_cap *cap);
+extern void ceph_put_cap(struct ceph_mds_client *mdsc,
+ struct ceph_cap *cap);
extern void ceph_queue_caps_release(struct inode *inode);
extern int ceph_write_inode(struct inode *inode, struct writeback_control *wbc);
extern int ceph_fsync(struct file *file, int datasync);
extern void ceph_kick_flushing_caps(struct ceph_mds_client *mdsc,
struct ceph_mds_session *session);
+extern struct ceph_cap *ceph_get_cap_for_mds(struct ceph_inode_info *ci,
+ int mds);
extern int ceph_get_cap_mds(struct inode *inode);
extern void ceph_get_cap_refs(struct ceph_inode_info *ci, int caps);
extern void ceph_put_cap_refs(struct ceph_inode_info *ci, int had);
/* dir.c */
extern const struct file_operations ceph_dir_fops;
extern const struct inode_operations ceph_dir_iops;
-extern struct dentry_operations ceph_dentry_ops, ceph_snap_dentry_ops,
+extern const struct dentry_operations ceph_dentry_ops, ceph_snap_dentry_ops,
ceph_snapdir_dentry_ops;
extern int ceph_handle_notrace_create(struct inode *dir, struct dentry *dentry);
extern int ceph_debugfs_client_init(struct ceph_client *client);
extern void ceph_debugfs_client_cleanup(struct ceph_client *client);
+/* locks.c */
+extern int ceph_lock(struct file *file, int cmd, struct file_lock *fl);
+extern int ceph_flock(struct file *file, int cmd, struct file_lock *fl);
+extern void ceph_count_locks(struct inode *inode, int *p_num, int *f_num);
+extern int ceph_encode_locks(struct inode *i, struct ceph_pagelist *p,
+ int p_locks, int f_locks);
+extern int lock_to_ceph_filelock(struct file_lock *fl, struct ceph_filelock *c);
+
static inline struct inode *get_dentry_parent_inode(struct dentry *dentry)
{
if (dentry && dentry->d_parent)
}
static int __build_xattrs(struct inode *inode)
+ __releases(inode->i_lock)
+ __acquires(inode->i_lock)
{
u32 namelen;
u32 numattr = 0;
nbytes = req->uc_outSize; /* don't have more space! */
}
if (copy_from_user(req->uc_data, buf, nbytes)) {
- req->uc_flags |= REQ_ABORT;
+ req->uc_flags |= CODA_REQ_ABORT;
wake_up(&req->uc_sleep);
retval = -EFAULT;
goto out;
retval = -EFAULT;
/* If request was not a signal, enqueue and don't free */
- if (!(req->uc_flags & REQ_ASYNC)) {
- req->uc_flags |= REQ_READ;
+ if (!(req->uc_flags & CODA_REQ_ASYNC)) {
+ req->uc_flags |= CODA_REQ_READ;
list_add_tail(&(req->uc_chain), &vcp->vc_processing);
goto out;
}
list_del(&req->uc_chain);
/* Async requests need to be freed here */
- if (req->uc_flags & REQ_ASYNC) {
+ if (req->uc_flags & CODA_REQ_ASYNC) {
CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr));
kfree(req);
continue;
}
- req->uc_flags |= REQ_ABORT;
+ req->uc_flags |= CODA_REQ_ABORT;
wake_up(&req->uc_sleep);
}
list_for_each_entry_safe(req, tmp, &vcp->vc_processing, uc_chain) {
list_del(&req->uc_chain);
- req->uc_flags |= REQ_ABORT;
+ req->uc_flags |= CODA_REQ_ABORT;
wake_up(&req->uc_sleep);
}
(((r)->uc_opcode != CODA_CLOSE && \
(r)->uc_opcode != CODA_STORE && \
(r)->uc_opcode != CODA_RELEASE) || \
- (r)->uc_flags & REQ_READ))
+ (r)->uc_flags & CODA_REQ_READ))
static inline void coda_waitfor_upcall(struct upc_req *req)
{
set_current_state(TASK_UNINTERRUPTIBLE);
/* got a reply */
- if (req->uc_flags & (REQ_WRITE | REQ_ABORT))
+ if (req->uc_flags & (CODA_REQ_WRITE | CODA_REQ_ABORT))
break;
if (blocked && time_after(jiffies, timeout) &&
coda_waitfor_upcall(req);
/* Op went through, interrupt or not... */
- if (req->uc_flags & REQ_WRITE) {
+ if (req->uc_flags & CODA_REQ_WRITE) {
out = (union outputArgs *)req->uc_data;
/* here we map positive Venus errors to kernel errors */
error = -out->oh.result;
}
error = -EINTR;
- if ((req->uc_flags & REQ_ABORT) || !signal_pending(current)) {
+ if ((req->uc_flags & CODA_REQ_ABORT) || !signal_pending(current)) {
printk(KERN_WARNING "coda: Unexpected interruption.\n");
goto exit;
}
/* Interrupted before venus read it. */
- if (!(req->uc_flags & REQ_READ))
+ if (!(req->uc_flags & CODA_REQ_READ))
goto exit;
/* Venus saw the upcall, make sure we can send interrupt signal */
sig_inputArgs->ih.opcode = CODA_SIGNAL;
sig_inputArgs->ih.unique = req->uc_unique;
- sig_req->uc_flags = REQ_ASYNC;
+ sig_req->uc_flags = CODA_REQ_ASYNC;
sig_req->uc_opcode = sig_inputArgs->ih.opcode;
sig_req->uc_unique = sig_inputArgs->ih.unique;
sig_req->uc_inSize = sizeof(struct coda_in_hdr);
return err;
}
-static int rw_long(unsigned int fd, unsigned int cmd,
- compat_ulong_t __user *argp)
-{
- mm_segment_t old_fs = get_fs();
- int err;
- unsigned long val;
-
- if(get_user(val, argp))
- return -EFAULT;
- set_fs (KERNEL_DS);
- err = sys_ioctl(fd, cmd, (unsigned long)&val);
- set_fs (old_fs);
- if (!err && put_user(val, argp))
- return -EFAULT;
- return err;
-}
-
struct compat_video_event {
int32_t type;
compat_time_t timestamp;
return err;
}
-static int ioc_settimeout(unsigned int fd, unsigned int cmd,
- compat_ulong_t __user *argp)
-{
- return rw_long(fd, AUTOFS_IOC_SETTIMEOUT, argp);
-}
-
/* Bluetooth ioctls */
#define HCIUARTSETPROTO _IOW('U', 200, int)
#define HCIUARTGETPROTO _IOR('U', 201, int)
COMPATIBLE_IOCTL(TIOCGPTN)
COMPATIBLE_IOCTL(TIOCSPTLCK)
COMPATIBLE_IOCTL(TIOCSERGETLSR)
+COMPATIBLE_IOCTL(TIOCSIG)
#ifdef TCGETS2
COMPATIBLE_IOCTL(TCGETS2)
COMPATIBLE_IOCTL(TCSETS2)
COMPATIBLE_IOCTL(SOUND_MIXER_GETLEVELS)
COMPATIBLE_IOCTL(SOUND_MIXER_SETLEVELS)
COMPATIBLE_IOCTL(OSS_GETVERSION)
-/* AUTOFS */
-COMPATIBLE_IOCTL(AUTOFS_IOC_CATATONIC)
-COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER)
-COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE)
-COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI)
-COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER)
-COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT)
/* Raw devices */
COMPATIBLE_IOCTL(RAW_SETBIND)
COMPATIBLE_IOCTL(RAW_GETBIND)
case RAW_GETBIND:
return raw_ioctl(fd, cmd, argp);
#endif
-#define AUTOFS_IOC_SETTIMEOUT32 _IOWR(0x93,0x64,unsigned int)
- case AUTOFS_IOC_SETTIMEOUT32:
- return ioc_settimeout(fd, cmd, argp);
/* One SMB ioctl needs translations. */
#define SMB_IOC_GETMOUNTUID_32 _IOR('u', 1, compat_uid_t)
case SMB_IOC_GETMOUNTUID_32:
case KDSKBMETA:
case KDSKBLED:
case KDSETLED:
- /* AUTOFS */
- case AUTOFS_IOC_READY:
- case AUTOFS_IOC_FAIL:
/* NBD */
case NBD_SET_SOCK:
case NBD_SET_BLKSIZE:
"the persistent file for the dentry with name "
"[%s]; rc = [%d]\n", __func__,
ecryptfs_dentry->d_name.name, rc);
- goto out;
+ goto out_free;
}
}
if ((ecryptfs_inode_to_private(inode)->lower_file->f_flags & O_RDONLY)
rc = -EPERM;
printk(KERN_WARNING "%s: Lower persistent file is RO; eCryptfs "
"file must hence be opened RO\n", __func__);
- goto out;
+ goto out_free;
}
ecryptfs_set_file_lower(
file, ecryptfs_inode_to_private(inode)->lower_file);
return rc;
}
-static int ecryptfs_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg);
+static long
+ecryptfs_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct file *lower_file = NULL;
+ long rc = -ENOTTY;
+
+ if (ecryptfs_file_to_private(file))
+ lower_file = ecryptfs_file_to_lower(file);
+ if (lower_file && lower_file->f_op && lower_file->f_op->unlocked_ioctl)
+ rc = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg);
+ return rc;
+}
+
+#ifdef CONFIG_COMPAT
+static long
+ecryptfs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct file *lower_file = NULL;
+ long rc = -ENOIOCTLCMD;
+
+ if (ecryptfs_file_to_private(file))
+ lower_file = ecryptfs_file_to_lower(file);
+ if (lower_file && lower_file->f_op && lower_file->f_op->compat_ioctl)
+ rc = lower_file->f_op->compat_ioctl(lower_file, cmd, arg);
+ return rc;
+}
+#endif
const struct file_operations ecryptfs_dir_fops = {
.readdir = ecryptfs_readdir,
- .ioctl = ecryptfs_ioctl,
+ .unlocked_ioctl = ecryptfs_unlocked_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = ecryptfs_compat_ioctl,
+#endif
.open = ecryptfs_open,
.flush = ecryptfs_flush,
.release = ecryptfs_release,
.write = do_sync_write,
.aio_write = generic_file_aio_write,
.readdir = ecryptfs_readdir,
- .ioctl = ecryptfs_ioctl,
+ .unlocked_ioctl = ecryptfs_unlocked_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = ecryptfs_compat_ioctl,
+#endif
.mmap = generic_file_mmap,
.open = ecryptfs_open,
.flush = ecryptfs_flush,
.fasync = ecryptfs_fasync,
.splice_read = generic_file_splice_read,
};
-
-static int
-ecryptfs_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- int rc = 0;
- struct file *lower_file = NULL;
-
- if (ecryptfs_file_to_private(file))
- lower_file = ecryptfs_file_to_lower(file);
- if (lower_file && lower_file->f_op && lower_file->f_op->ioctl)
- rc = lower_file->f_op->ioctl(ecryptfs_inode_to_lower(inode),
- lower_file, cmd, arg);
- else
- rc = -ENOTTY;
- return rc;
-}
printk(KERN_ERR "%s: Out of memory whilst attempting "
"to allocate ecryptfs_dentry_info struct\n",
__func__);
- goto out_dput;
+ goto out_put;
}
ecryptfs_set_dentry_lower(ecryptfs_dentry, lower_dentry);
ecryptfs_set_dentry_lower_mnt(ecryptfs_dentry, lower_mnt);
out_free_kmem:
kmem_cache_free(ecryptfs_header_cache_2, page_virt);
goto out;
-out_dput:
+out_put:
dput(lower_dentry);
+ mntput(lower_mnt);
d_drop(ecryptfs_dentry);
out:
return rc;
}
+/**
+ * ecryptfs_new_lower_dentry
+ * @ename: The name of the new dentry.
+ * @lower_dir_dentry: Parent directory of the new dentry.
+ * @nd: nameidata from last lookup.
+ *
+ * Create a new dentry or get it from lower parent dir.
+ */
+static struct dentry *
+ecryptfs_new_lower_dentry(struct qstr *name, struct dentry *lower_dir_dentry,
+ struct nameidata *nd)
+{
+ struct dentry *new_dentry;
+ struct dentry *tmp;
+ struct inode *lower_dir_inode;
+
+ lower_dir_inode = lower_dir_dentry->d_inode;
+
+ tmp = d_alloc(lower_dir_dentry, name);
+ if (!tmp)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_lock(&lower_dir_inode->i_mutex);
+ new_dentry = lower_dir_inode->i_op->lookup(lower_dir_inode, tmp, nd);
+ mutex_unlock(&lower_dir_inode->i_mutex);
+
+ if (!new_dentry)
+ new_dentry = tmp;
+ else
+ dput(tmp);
+
+ return new_dentry;
+}
+
+
+/**
+ * ecryptfs_lookup_one_lower
+ * @ecryptfs_dentry: The eCryptfs dentry that we are looking up
+ * @lower_dir_dentry: lower parent directory
+ *
+ * Get the lower dentry from vfs. If lower dentry does not exist yet,
+ * create it.
+ */
+static struct dentry *
+ecryptfs_lookup_one_lower(struct dentry *ecryptfs_dentry,
+ struct dentry *lower_dir_dentry)
+{
+ struct nameidata nd;
+ struct vfsmount *lower_mnt;
+ struct qstr *name;
+ int err;
+
+ name = &ecryptfs_dentry->d_name;
+ lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(
+ ecryptfs_dentry->d_parent));
+ err = vfs_path_lookup(lower_dir_dentry, lower_mnt, name->name , 0, &nd);
+ mntput(lower_mnt);
+
+ if (!err) {
+ /* we dont need the mount */
+ mntput(nd.path.mnt);
+ return nd.path.dentry;
+ }
+ if (err != -ENOENT)
+ return ERR_PTR(err);
+
+ /* create a new lower dentry */
+ return ecryptfs_new_lower_dentry(name, lower_dir_dentry, &nd);
+}
+
/**
* ecryptfs_lookup
* @ecryptfs_dir_inode: The eCryptfs directory inode
goto out_d_drop;
}
lower_dir_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry->d_parent);
- mutex_lock(&lower_dir_dentry->d_inode->i_mutex);
- lower_dentry = lookup_one_len(ecryptfs_dentry->d_name.name,
- lower_dir_dentry,
- ecryptfs_dentry->d_name.len);
- mutex_unlock(&lower_dir_dentry->d_inode->i_mutex);
+
+ lower_dentry = ecryptfs_lookup_one_lower(ecryptfs_dentry,
+ lower_dir_dentry);
if (IS_ERR(lower_dentry)) {
rc = PTR_ERR(lower_dentry);
- ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_len() returned "
+ ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_lower() returned "
"[%d] on lower_dentry = [%s]\n", __func__, rc,
encrypted_and_encoded_name);
goto out_d_drop;
"filename; rc = [%d]\n", __func__, rc);
goto out_d_drop;
}
- mutex_lock(&lower_dir_dentry->d_inode->i_mutex);
- lower_dentry = lookup_one_len(encrypted_and_encoded_name,
- lower_dir_dentry,
- encrypted_and_encoded_name_size - 1);
- mutex_unlock(&lower_dir_dentry->d_inode->i_mutex);
+ lower_dentry = ecryptfs_lookup_one_lower(ecryptfs_dentry,
+ lower_dir_dentry);
if (IS_ERR(lower_dentry)) {
rc = PTR_ERR(lower_dentry);
- ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_len() returned "
+ ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_lower() returned "
"[%d] on lower_dentry = [%s]\n", __func__, rc,
encrypted_and_encoded_name);
goto out_d_drop;
struct user_namespace *user_ns, struct pid *pid,
u32 seq)
{
- struct ecryptfs_daemon *daemon;
+ struct ecryptfs_daemon *uninitialized_var(daemon);
struct ecryptfs_msg_ctx *msg_ctx;
size_t msg_size;
struct nsproxy *nsproxy;
* along with exofs; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-
-#include <linux/buffer_head.h>
-
#include "exofs.h"
static int exofs_release_file(struct inode *inode, struct file *filp)
return 0;
}
+/* exofs_file_fsync - flush the inode to disk
+ *
+ * Note, in exofs all metadata is written as part of inode, regardless.
+ * The writeout is synchronous
+ */
static int exofs_file_fsync(struct file *filp, int datasync)
{
int ret;
- struct address_space *mapping = filp->f_mapping;
- struct inode *inode = mapping->host;
+ struct inode *inode = filp->f_mapping->host;
+ struct writeback_control wbc = {
+ .sync_mode = WB_SYNC_ALL,
+ .nr_to_write = 0, /* metadata-only; caller takes care of data */
+ };
struct super_block *sb;
- ret = filemap_write_and_wait(mapping);
- if (ret)
- return ret;
+ if (!(inode->i_state & I_DIRTY))
+ return 0;
+ if (datasync && !(inode->i_state & I_DIRTY_DATASYNC))
+ return 0;
- /* sync the inode attributes */
- ret = write_inode_now(inode, 1);
+ ret = sync_inode(inode, &wbc);
/* This is a good place to write the sb */
/* TODO: Sechedule an sb-sync on create */
static int exofs_flush(struct file *file, fl_owner_t id)
{
- exofs_file_fsync(file, 1);
+ int ret = vfs_fsync(file, 0);
/* TODO: Flush the OSD target */
- return 0;
+ return ret;
}
const struct file_operations exofs_file_operations = {
*/
#include <linux/slab.h>
-#include <linux/writeback.h>
-#include <linux/buffer_head.h>
-#include <scsi/scsi_device.h>
#include "exofs.h"
{
EXOFS_DBGMSG("page 0x%lx\n", page->index);
WARN_ON(1);
- return try_to_free_buffers(page);
+ return 0;
}
static void exofs_invalidatepage(struct page *page, unsigned long offset)
{
- EXOFS_DBGMSG("page_has_buffers=>%d\n", page_has_buffers(page));
+ EXOFS_DBGMSG("page 0x%lx offset 0x%lx\n", page->index, offset);
WARN_ON(1);
-
- block_invalidatepage(page, offset);
}
const struct address_space_operations exofs_aops = {
struct _striping_info {
u64 obj_offset;
u64 group_length;
- u64 total_group_length;
- u64 Major;
unsigned dev;
unsigned unit_off;
};
(M * group_depth * stripe_unit);
si->group_length = T - H;
- si->total_group_length = T;
- si->Major = M;
}
static int _add_stripe_unit(struct exofs_io_state *ios, unsigned *cur_pg,
}
static int _prepare_one_group(struct exofs_io_state *ios, u64 length,
- struct _striping_info *si, unsigned first_comp)
+ struct _striping_info *si)
{
unsigned stripe_unit = ios->layout->stripe_unit;
unsigned mirrors_p1 = ios->layout->mirrors_p1;
unsigned devs_in_group = ios->layout->group_width * mirrors_p1;
unsigned dev = si->dev;
unsigned first_dev = dev - (dev % devs_in_group);
- unsigned comp = first_comp + (dev - first_dev);
unsigned max_comp = ios->numdevs ? ios->numdevs - mirrors_p1 : 0;
unsigned cur_pg = ios->pages_consumed;
int ret = 0;
while (length) {
- struct exofs_per_dev_state *per_dev = &ios->per_dev[comp];
+ struct exofs_per_dev_state *per_dev = &ios->per_dev[dev];
unsigned cur_len, page_off = 0;
if (!per_dev->length) {
cur_len = stripe_unit;
}
- if (max_comp < comp)
- max_comp = comp;
-
- dev += mirrors_p1;
- dev = (dev % devs_in_group) + first_dev;
+ if (max_comp < dev)
+ max_comp = dev;
} else {
cur_len = stripe_unit;
}
if (unlikely(ret))
goto out;
- comp += mirrors_p1;
- comp = (comp % devs_in_group) + first_comp;
+ dev += mirrors_p1;
+ dev = (dev % devs_in_group) + first_dev;
length -= cur_len;
}
static int _prepare_for_striping(struct exofs_io_state *ios)
{
u64 length = ios->length;
+ u64 offset = ios->offset;
struct _striping_info si;
- unsigned devs_in_group = ios->layout->group_width *
- ios->layout->mirrors_p1;
- unsigned first_comp = 0;
int ret = 0;
- _calc_stripe_info(ios, ios->offset, &si);
-
if (!ios->pages) {
if (ios->kern_buff) {
struct exofs_per_dev_state *per_dev = &ios->per_dev[0];
+ _calc_stripe_info(ios, ios->offset, &si);
per_dev->offset = si.obj_offset;
per_dev->dev = si.dev;
}
while (length) {
+ _calc_stripe_info(ios, offset, &si);
+
if (length < si.group_length)
si.group_length = length;
- ret = _prepare_one_group(ios, si.group_length, &si, first_comp);
+ ret = _prepare_one_group(ios, si.group_length, &si);
if (unlikely(ret))
goto out;
+ offset += si.group_length;
length -= si.group_length;
-
- si.group_length = si.total_group_length;
- si.unit_off = 0;
- ++si.Major;
- si.obj_offset = si.Major * ios->layout->stripe_unit *
- ios->layout->group_depth;
-
- si.dev = (si.dev - (si.dev % devs_in_group)) + devs_in_group;
- si.dev %= ios->layout->s_numdevs;
-
- first_comp += devs_in_group;
- first_comp %= ios->layout->s_numdevs;
}
out:
} else {
bio = master_dev->bio;
/* FIXME: bio_set_dir() */
- bio->bi_rw |= (1 << BIO_RW);
+ bio->bi_rw |= REQ_WRITE;
}
osd_req_write(or, &ios->obj, per_dev->offset, bio,
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <linux/smp_lock.h>
#include <linux/string.h>
#include <linux/parser.h>
#include <linux/vfs.h>
}
EXPORT_SYMBOL(kill_fasync);
-static int __init fasync_init(void)
+static int __init fcntl_init(void)
{
+ /* please add new bits here to ensure allocation uniqueness */
+ BUILD_BUG_ON(19 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32(
+ O_RDONLY | O_WRONLY | O_RDWR |
+ O_CREAT | O_EXCL | O_NOCTTY |
+ O_TRUNC | O_APPEND | O_NONBLOCK |
+ __O_SYNC | O_DSYNC | FASYNC |
+ O_DIRECT | O_LARGEFILE | O_DIRECTORY |
+ O_NOFOLLOW | O_NOATIME | O_CLOEXEC |
+ FMODE_EXEC
+ ));
+
fasync_cache = kmem_cache_create("fasync_cache",
sizeof(struct fasync_struct), 0, SLAB_PANIC, NULL);
return 0;
}
-module_init(fasync_init)
+module_init(fcntl_init)
*/
static DEFINE_PER_CPU(struct fdtable_defer, fdtable_defer_list);
-static inline void * alloc_fdmem(unsigned int size)
+static inline void *alloc_fdmem(unsigned int size)
{
- if (size <= PAGE_SIZE)
- return kmalloc(size, GFP_KERNEL);
- else
- return vmalloc(size);
+ void *data;
+
+ data = kmalloc(size, GFP_KERNEL|__GFP_NOWARN);
+ if (data != NULL)
+ return data;
+
+ return vmalloc(size);
}
-static inline void free_fdarr(struct fdtable *fdt)
+static void free_fdmem(void *ptr)
{
- if (fdt->max_fds <= (PAGE_SIZE / sizeof(struct file *)))
- kfree(fdt->fd);
- else
- vfree(fdt->fd);
+ is_vmalloc_addr(ptr) ? vfree(ptr) : kfree(ptr);
}
-static inline void free_fdset(struct fdtable *fdt)
+static void __free_fdtable(struct fdtable *fdt)
{
- if (fdt->max_fds <= (PAGE_SIZE * BITS_PER_BYTE / 2))
- kfree(fdt->open_fds);
- else
- vfree(fdt->open_fds);
+ free_fdmem(fdt->fd);
+ free_fdmem(fdt->open_fds);
+ kfree(fdt);
}
static void free_fdtable_work(struct work_struct *work)
spin_unlock_bh(&f->lock);
while(fdt) {
struct fdtable *next = fdt->next;
- vfree(fdt->fd);
- free_fdset(fdt);
- kfree(fdt);
+
+ __free_fdtable(fdt);
fdt = next;
}
}
container_of(fdt, struct files_struct, fdtab));
return;
}
- if (fdt->max_fds <= (PAGE_SIZE / sizeof(struct file *))) {
+ if (!is_vmalloc_addr(fdt->fd) && !is_vmalloc_addr(fdt->open_fds)) {
kfree(fdt->fd);
kfree(fdt->open_fds);
kfree(fdt);
return fdt;
out_arr:
- free_fdarr(fdt);
+ free_fdmem(fdt->fd);
out_fdt:
kfree(fdt);
out:
* caller and alloc_fdtable(). Cheaper to catch it here...
*/
if (unlikely(new_fdt->max_fds <= nr)) {
- free_fdarr(new_fdt);
- free_fdset(new_fdt);
- kfree(new_fdt);
+ __free_fdtable(new_fdt);
return -EMFILE;
}
/*
free_fdtable(cur_fdt);
} else {
/* Somebody else expanded, so undo our attempt */
- free_fdarr(new_fdt);
- free_fdset(new_fdt);
- kfree(new_fdt);
+ __free_fdtable(new_fdt);
}
return 1;
}
while (unlikely(open_files > new_fdt->max_fds)) {
spin_unlock(&oldf->file_lock);
- if (new_fdt != &newf->fdtab) {
- free_fdarr(new_fdt);
- free_fdset(new_fdt);
- kfree(new_fdt);
- }
+ if (new_fdt != &newf->fdtab)
+ __free_fdtable(new_fdt);
new_fdt = alloc_fdtable(open_files - 1);
if (!new_fdt) {
/* beyond sysctl_nr_open; nothing to do */
if (unlikely(new_fdt->max_fds < open_files)) {
- free_fdarr(new_fdt);
- free_fdset(new_fdt);
- kfree(new_fdt);
+ __free_fdtable(new_fdt);
*errorp = -EMFILE;
goto out_release;
}
EXPORT_SYMBOL(fget);
/*
- * Lightweight file lookup - no refcnt increment if fd table isn't shared.
- * You can use this only if it is guranteed that the current task already
- * holds a refcnt to that file. That check has to be done at fget() only
- * and a flag is returned to be passed to the corresponding fput_light().
- * There must not be a cloning between an fget_light/fput_light pair.
+ * Lightweight file lookup - no refcnt increment if fd table isn't shared.
+ *
+ * You can use this instead of fget if you satisfy all of the following
+ * conditions:
+ * 1) You must call fput_light before exiting the syscall and returning control
+ * to userspace (i.e. you cannot remember the returned struct file * after
+ * returning to userspace).
+ * 2) You must not call filp_close on the returned struct file * in between
+ * calls to fget_light and fput_light.
+ * 3) You must not clone the current task in between the calls to fget_light
+ * and fput_light.
+ *
+ * The fput_needed flag returned by fget_light should be passed to the
+ * corresponding fput_light.
*/
struct file *fget_light(unsigned int fd, int *fput_needed)
{
#include <linux/blkdev.h>
#include <linux/backing-dev.h>
#include <linux/buffer_head.h>
+#include <linux/tracepoint.h>
#include "internal.h"
-#define inode_to_bdi(inode) ((inode)->i_mapping->backing_dev_info)
-
-/*
- * We don't actually have pdflush, but this one is exported though /proc...
- */
-int nr_pdflush_threads;
-
/*
* Passed into wb_writeback(), essentially a subset of writeback_control
*/
struct completion *done; /* set if the caller waits */
};
+/*
+ * Include the creation of the trace points after defining the
+ * wb_writeback_work structure so that the definition remains local to this
+ * file.
+ */
+#define CREATE_TRACE_POINTS
+#include <trace/events/writeback.h>
+
+#define inode_to_bdi(inode) ((inode)->i_mapping->backing_dev_info)
+
+/*
+ * We don't actually have pdflush, but this one is exported though /proc...
+ */
+int nr_pdflush_threads;
+
/**
* writeback_in_progress - determine whether there is writeback in progress
* @bdi: the device's backing_dev_info structure.
static void bdi_queue_work(struct backing_dev_info *bdi,
struct wb_writeback_work *work)
{
- spin_lock(&bdi->wb_lock);
- list_add_tail(&work->list, &bdi->work_list);
- spin_unlock(&bdi->wb_lock);
+ trace_writeback_queue(bdi, work);
- /*
- * If the default thread isn't there, make sure we add it. When
- * it gets created and wakes up, we'll run this work.
- */
- if (unlikely(list_empty_careful(&bdi->wb_list)))
+ spin_lock_bh(&bdi->wb_lock);
+ list_add_tail(&work->list, &bdi->work_list);
+ if (bdi->wb.task) {
+ wake_up_process(bdi->wb.task);
+ } else {
+ /*
+ * The bdi thread isn't there, wake up the forker thread which
+ * will create and run it.
+ */
+ trace_writeback_nothread(bdi, work);
wake_up_process(default_backing_dev_info.wb.task);
- else {
- struct bdi_writeback *wb = &bdi->wb;
-
- if (wb->task)
- wake_up_process(wb->task);
}
+ spin_unlock_bh(&bdi->wb_lock);
}
static void
*/
work = kzalloc(sizeof(*work), GFP_ATOMIC);
if (!work) {
- if (bdi->wb.task)
+ if (bdi->wb.task) {
+ trace_writeback_nowork(bdi);
wake_up_process(bdi->wb.task);
+ }
return;
}
wbc.more_io = 0;
wbc.nr_to_write = MAX_WRITEBACK_PAGES;
wbc.pages_skipped = 0;
+
+ trace_wbc_writeback_start(&wbc, wb->bdi);
if (work->sb)
__writeback_inodes_sb(work->sb, wb, &wbc);
else
writeback_inodes_wb(wb, &wbc);
+ trace_wbc_writeback_written(&wbc, wb->bdi);
+
work->nr_pages -= MAX_WRITEBACK_PAGES - wbc.nr_to_write;
wrote += MAX_WRITEBACK_PAGES - wbc.nr_to_write;
if (!list_empty(&wb->b_more_io)) {
inode = list_entry(wb->b_more_io.prev,
struct inode, i_list);
+ trace_wbc_writeback_wait(&wbc, wb->bdi);
inode_wait_for_writeback(inode);
}
spin_unlock(&inode_lock);
* Return the next wb_writeback_work struct that hasn't been processed yet.
*/
static struct wb_writeback_work *
-get_next_work_item(struct backing_dev_info *bdi, struct bdi_writeback *wb)
+get_next_work_item(struct backing_dev_info *bdi)
{
struct wb_writeback_work *work = NULL;
- spin_lock(&bdi->wb_lock);
+ spin_lock_bh(&bdi->wb_lock);
if (!list_empty(&bdi->work_list)) {
work = list_entry(bdi->work_list.next,
struct wb_writeback_work, list);
list_del_init(&work->list);
}
- spin_unlock(&bdi->wb_lock);
+ spin_unlock_bh(&bdi->wb_lock);
return work;
}
struct wb_writeback_work *work;
long wrote = 0;
- while ((work = get_next_work_item(bdi, wb)) != NULL) {
+ while ((work = get_next_work_item(bdi)) != NULL) {
/*
* Override sync mode, in case we must wait for completion
* because this thread is exiting now.
if (force_wait)
work->sync_mode = WB_SYNC_ALL;
+ trace_writeback_exec(bdi, work);
+
wrote += wb_writeback(wb, work);
/*
* Handle writeback of dirty data for the device backed by this bdi. Also
* wakes up periodically and does kupdated style flushing.
*/
-int bdi_writeback_task(struct bdi_writeback *wb)
+int bdi_writeback_thread(void *data)
{
- unsigned long last_active = jiffies;
- unsigned long wait_jiffies = -1UL;
+ struct bdi_writeback *wb = data;
+ struct backing_dev_info *bdi = wb->bdi;
long pages_written;
+ current->flags |= PF_FLUSHER | PF_SWAPWRITE;
+ set_freezable();
+ wb->last_active = jiffies;
+
+ /*
+ * Our parent may run at a different priority, just set us to normal
+ */
+ set_user_nice(current, 0);
+
+ trace_writeback_thread_start(bdi);
+
while (!kthread_should_stop()) {
+ /*
+ * Remove own delayed wake-up timer, since we are already awake
+ * and we'll take care of the preriodic write-back.
+ */
+ del_timer(&wb->wakeup_timer);
+
pages_written = wb_do_writeback(wb, 0);
+ trace_writeback_pages_written(pages_written);
+
if (pages_written)
- last_active = jiffies;
- else if (wait_jiffies != -1UL) {
- unsigned long max_idle;
+ wb->last_active = jiffies;
- /*
- * Longest period of inactivity that we tolerate. If we
- * see dirty data again later, the task will get
- * recreated automatically.
- */
- max_idle = max(5UL * 60 * HZ, wait_jiffies);
- if (time_after(jiffies, max_idle + last_active))
- break;
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (!list_empty(&bdi->work_list)) {
+ __set_current_state(TASK_RUNNING);
+ continue;
}
- if (dirty_writeback_interval) {
- wait_jiffies = msecs_to_jiffies(dirty_writeback_interval * 10);
- schedule_timeout_interruptible(wait_jiffies);
- } else {
- set_current_state(TASK_INTERRUPTIBLE);
- if (list_empty_careful(&wb->bdi->work_list) &&
- !kthread_should_stop())
- schedule();
- __set_current_state(TASK_RUNNING);
+ if (wb_has_dirty_io(wb) && dirty_writeback_interval)
+ schedule_timeout(msecs_to_jiffies(dirty_writeback_interval * 10));
+ else {
+ /*
+ * We have nothing to do, so can go sleep without any
+ * timeout and save power. When a work is queued or
+ * something is made dirty - we will be woken up.
+ */
+ schedule();
}
try_to_freeze();
}
+ /* Flush any work that raced with us exiting */
+ if (!list_empty(&bdi->work_list))
+ wb_do_writeback(wb, 1);
+
+ trace_writeback_thread_stop(bdi);
return 0;
}
+
/*
* Start writeback of `nr_pages' pages. If `nr_pages' is zero, write back
* the whole world.
void __mark_inode_dirty(struct inode *inode, int flags)
{
struct super_block *sb = inode->i_sb;
+ struct backing_dev_info *bdi = NULL;
+ bool wakeup_bdi = false;
/*
* Don't do this for I_DIRTY_PAGES - that doesn't actually
* reposition it (that would break b_dirty time-ordering).
*/
if (!was_dirty) {
- struct bdi_writeback *wb = &inode_to_bdi(inode)->wb;
- struct backing_dev_info *bdi = wb->bdi;
-
- if (bdi_cap_writeback_dirty(bdi) &&
- !test_bit(BDI_registered, &bdi->state)) {
- WARN_ON(1);
- printk(KERN_ERR "bdi-%s not registered\n",
- bdi->name);
+ bdi = inode_to_bdi(inode);
+
+ if (bdi_cap_writeback_dirty(bdi)) {
+ WARN(!test_bit(BDI_registered, &bdi->state),
+ "bdi-%s not registered\n", bdi->name);
+
+ /*
+ * If this is the first dirty inode for this
+ * bdi, we have to wake-up the corresponding
+ * bdi thread to make sure background
+ * write-back happens later.
+ */
+ if (!wb_has_dirty_io(&bdi->wb))
+ wakeup_bdi = true;
}
inode->dirtied_when = jiffies;
- list_move(&inode->i_list, &wb->b_dirty);
+ list_move(&inode->i_list, &bdi->wb.b_dirty);
}
}
out:
spin_unlock(&inode_lock);
+
+ if (wakeup_bdi)
+ bdi_wakeup_thread_delayed(bdi);
}
EXPORT_SYMBOL(__mark_inode_dirty);
if (test_bit(SDF_NOBARRIERS, &sdp->sd_flags))
goto skip_barrier;
get_bh(bh);
- submit_bh(WRITE_SYNC | (1 << BIO_RW_BARRIER) | (1 << BIO_RW_META), bh);
+ submit_bh(WRITE_BARRIER | REQ_META, bh);
wait_on_buffer(bh);
if (buffer_eopnotsupp(bh)) {
clear_buffer_eopnotsupp(bh);
lock_buffer(bh);
skip_barrier:
get_bh(bh);
- submit_bh(WRITE_SYNC | (1 << BIO_RW_META), bh);
+ submit_bh(WRITE_SYNC | REQ_META, bh);
wait_on_buffer(bh);
}
if (!buffer_uptodate(bh))
{
struct buffer_head *bh, *head;
int nr_underway = 0;
- int write_op = (1 << BIO_RW_META) | ((wbc->sync_mode == WB_SYNC_ALL ?
- WRITE_SYNC_PLUG : WRITE));
+ int write_op = REQ_META |
+ (wbc->sync_mode == WB_SYNC_ALL ? WRITE_SYNC_PLUG : WRITE);
BUG_ON(!PageLocked(page));
BUG_ON(!page_has_buffers(page));
}
bh->b_end_io = end_buffer_read_sync;
get_bh(bh);
- submit_bh(READ_SYNC | (1 << BIO_RW_META), bh);
+ submit_bh(READ_SYNC | REQ_META, bh);
if (!(flags & DIO_WAIT))
return 0;
if (buffer_uptodate(first_bh))
goto out;
if (!buffer_locked(first_bh))
- ll_rw_block(READ_SYNC | (1 << BIO_RW_META), 1, &first_bh);
+ ll_rw_block(READ_SYNC | REQ_META, 1, &first_bh);
dblock++;
extlen--;
bio->bi_end_io = end_bio_io_page;
bio->bi_private = page;
- submit_bio(READ_SYNC | (1 << BIO_RW_META), bio);
+ submit_bio(READ_SYNC | REQ_META, bio);
wait_on_page_locked(page);
bio_put(bio);
if (!PageUptodate(page)) {
#define EA_MALLOC 0x0008
+static int is_known_namespace(const char *name)
+{
+ if (strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) &&
+ strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) &&
+ strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) &&
+ strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN))
+ return false;
+
+ return true;
+}
+
/*
* These three routines are used to recognize on-disk extended attributes
* that are in a recognized namespace. If the attribute is not recognized,
* "os2." is prepended to the name
*/
-static inline int is_os2_xattr(struct jfs_ea *ea)
+static int is_os2_xattr(struct jfs_ea *ea)
{
- /*
- * Check for "system."
- */
- if ((ea->namelen >= XATTR_SYSTEM_PREFIX_LEN) &&
- !strncmp(ea->name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
- return false;
- /*
- * Check for "user."
- */
- if ((ea->namelen >= XATTR_USER_PREFIX_LEN) &&
- !strncmp(ea->name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
- return false;
- /*
- * Check for "security."
- */
- if ((ea->namelen >= XATTR_SECURITY_PREFIX_LEN) &&
- !strncmp(ea->name, XATTR_SECURITY_PREFIX,
- XATTR_SECURITY_PREFIX_LEN))
- return false;
- /*
- * Check for "trusted."
- */
- if ((ea->namelen >= XATTR_TRUSTED_PREFIX_LEN) &&
- !strncmp(ea->name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN))
- return false;
- /*
- * Add any other valid namespace prefixes here
- */
-
- /*
- * We assume it's OS/2's flat namespace
- */
- return true;
+ return !is_known_namespace(ea->name);
}
static inline int name_size(struct jfs_ea *ea)
if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
return can_set_system_xattr(inode, name, value, value_len);
+ if (!strncmp(name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN)) {
+ /*
+ * This makes sure that we aren't trying to set an
+ * attribute in a different namespace by prefixing it
+ * with "os2."
+ */
+ if (is_known_namespace(name + XATTR_OS2_PREFIX_LEN))
+ return -EOPNOTSUPP;
+ return 0;
+ }
+
/*
* Don't allow setting an attribute in an unknown namespace.
*/
if (strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) &&
strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) &&
- strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) &&
- strncmp(name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN))
+ strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
return -EOPNOTSUPP;
return 0;
int xattr_size;
ssize_t size;
int namelen = strlen(name);
- char *os2name = NULL;
char *value;
- if (strncmp(name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) == 0) {
- os2name = kmalloc(namelen - XATTR_OS2_PREFIX_LEN + 1,
- GFP_KERNEL);
- if (!os2name)
- return -ENOMEM;
- strcpy(os2name, name + XATTR_OS2_PREFIX_LEN);
- name = os2name;
- namelen -= XATTR_OS2_PREFIX_LEN;
- }
-
down_read(&JFS_IP(inode)->xattr_sem);
xattr_size = ea_get(inode, &ea_buf, 0);
out:
up_read(&JFS_IP(inode)->xattr_sem);
- kfree(os2name);
-
return size;
}
{
int err;
+ if (strncmp(name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) == 0) {
+ /*
+ * skip past "os2." prefix
+ */
+ name += XATTR_OS2_PREFIX_LEN;
+ /*
+ * Don't allow retrieving properly prefixed attributes
+ * by prepending them with "os2."
+ */
+ if (is_known_namespace(name))
+ return -EOPNOTSUPP;
+ }
+
err = __jfs_getxattr(dentry->d_inode, name, data, buf_size);
return err;
#include <linux/smp_lock.h>
#include <linux/vmalloc.h>
#include <linux/sched.h>
-#include <linux/smp_lock.h>
#include <linux/ncp_fs.h>
* Last BIO is always sent through the following
* submission.
*/
- rw |= (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG);
+ rw |= REQ_SYNC | REQ_UNPLUG;
res = nilfs_segbuf_submit_bio(segbuf, &wi, rw);
}
/*
* This is used by subsystems that don't want seekable
- * file descriptors
+ * file descriptors. The function is not supposed to ever fail, the only
+ * reason it returns an 'int' and not 'void' is so that it can be plugged
+ * directly into file_operations structure.
*/
int nonseekable_open(struct inode *inode, struct file *filp)
{
nr_sects = (le32_to_cpu(dr->disc_size_high) << 23) |
(le32_to_cpu(dr->disc_size) >> 9);
- if (name)
- printk(" [%s]", name);
+ if (name) {
+ strlcat(state->pp_buf, " [", PAGE_SIZE);
+ strlcat(state->pp_buf, name, PAGE_SIZE);
+ strlcat(state->pp_buf, "]", PAGE_SIZE);
+ }
put_partition(state, slot, first_sector, nr_sects);
return dr;
}
if (!rr)
return -1;
- printk(" [RISCiX]");
+ strlcat(state->pp_buf, " [RISCiX]", PAGE_SIZE);
if (rr->magic == RISCIX_MAGIC) {
unsigned long size = nr_sects > 2 ? 2 : nr_sects;
int part;
- printk(" <");
+ strlcat(state->pp_buf, " <", PAGE_SIZE);
put_partition(state, slot++, first_sect, size);
for (part = 0; part < 8; part++) {
put_partition(state, slot++,
le32_to_cpu(rr->part[part].start),
le32_to_cpu(rr->part[part].length));
- printk("(%s)", rr->part[part].name);
+ strlcat(state->pp_buf, "(", PAGE_SIZE);
+ strlcat(state->pp_buf, rr->part[part].name, PAGE_SIZE);
+ strlcat(state->pp_buf, ")", PAGE_SIZE);
}
}
- printk(" >\n");
+ strlcat(state->pp_buf, " >\n", PAGE_SIZE);
} else {
put_partition(state, slot++, first_sect, nr_sects);
}
struct linux_part *linuxp;
unsigned long size = nr_sects > 2 ? 2 : nr_sects;
- printk(" [Linux]");
+ strlcat(state->pp_buf, " [Linux]", PAGE_SIZE);
put_partition(state, slot++, first_sect, size);
if (!linuxp)
return -1;
- printk(" <");
+ strlcat(state->pp_buf, " <", PAGE_SIZE);
while (linuxp->magic == cpu_to_le32(LINUX_NATIVE_MAGIC) ||
linuxp->magic == cpu_to_le32(LINUX_SWAP_MAGIC)) {
if (slot == state->limit)
le32_to_cpu(linuxp->nr_sects));
linuxp ++;
}
- printk(" >");
+ strlcat(state->pp_buf, " >", PAGE_SIZE);
put_dev_sector(sect);
return slot;
break;
}
}
- printk("\n");
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
return 1;
}
#endif
return 0;
}
- printk(" [ICS]");
+ strlcat(state->pp_buf, " [ICS]", PAGE_SIZE);
for (slot = 1, p = (const struct ics_part *)data; p->size; p++) {
u32 start = le32_to_cpu(p->start);
}
put_dev_sector(sect);
- printk("\n");
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
return 1;
}
#endif
return 0;
}
- printk(" [POWERTEC]");
+ strlcat(state->pp_buf, " [POWERTEC]", PAGE_SIZE);
for (i = 0, p = (const struct ptec_part *)data; i < 12; i++, p++) {
u32 start = le32_to_cpu(p->start);
}
put_dev_sector(sect);
- printk("\n");
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
return 1;
}
#endif
size = get_capacity(state->bdev->bd_disk);
put_partition(state, slot++, start, size - start);
- printk("\n");
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
}
return i ? 1 : 0;
/* blksize is blocks per 512 byte standard block */
blksize = be32_to_cpu( rdb->rdb_BlockBytes ) / 512;
- printk(" RDSK (%d)", blksize * 512); /* Be more informative */
+ {
+ char tmp[7 + 10 + 1 + 1];
+
+ /* Be more informative */
+ snprintf(tmp, sizeof(tmp), " RDSK (%d)", blksize * 512);
+ strlcat(state->pp_buf, tmp, PAGE_SIZE);
+ }
blk = be32_to_cpu(rdb->rdb_PartitionList);
put_dev_sector(sect);
for (part = 1; blk>0 && part<=16; part++, put_dev_sector(sect)) {
{
/* Be even more informative to aid mounting */
char dostype[4];
+ char tmp[42];
+
__be32 *dt = (__be32 *)dostype;
*dt = pb->pb_Environment[16];
if (dostype[3] < ' ')
- printk(" (%c%c%c^%c)",
+ snprintf(tmp, sizeof(tmp), " (%c%c%c^%c)",
dostype[0], dostype[1],
dostype[2], dostype[3] + '@' );
else
- printk(" (%c%c%c%c)",
+ snprintf(tmp, sizeof(tmp), " (%c%c%c%c)",
dostype[0], dostype[1],
dostype[2], dostype[3]);
- printk("(res %d spb %d)",
+ strlcat(state->pp_buf, tmp, PAGE_SIZE);
+ snprintf(tmp, sizeof(tmp), "(res %d spb %d)",
be32_to_cpu(pb->pb_Environment[6]),
be32_to_cpu(pb->pb_Environment[4]));
+ strlcat(state->pp_buf, tmp, PAGE_SIZE);
}
res = 1;
}
- printk("\n");
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
rdb_done:
return res;
}
pi = &rs->part[0];
- printk (" AHDI");
+ strlcat(state->pp_buf, " AHDI", PAGE_SIZE);
for (slot = 1; pi < &rs->part[4] && slot < state->limit; slot++, pi++) {
struct rootsector *xrs;
Sector sect2;
#ifdef ICD_PARTS
part_fmt = 1;
#endif
- printk(" XGM<");
+ strlcat(state->pp_buf, " XGM<", PAGE_SIZE);
partsect = extensect = be32_to_cpu(pi->st);
while (1) {
xrs = read_part_sector(state, partsect, §2);
break;
}
}
- printk(" >");
+ strlcat(state->pp_buf, " >", PAGE_SIZE);
}
#ifdef ICD_PARTS
if ( part_fmt!=1 ) { /* no extended partitions -> test ICD-format */
pi = &rs->icdpart[0];
/* sanity check: no ICD format if first partition invalid */
if (OK_id(pi->id)) {
- printk(" ICD<");
+ strlcat(state->pp_buf, " ICD<", PAGE_SIZE);
for (; pi < &rs->icdpart[8] && slot < state->limit; slot++, pi++) {
/* accept only GEM,BGM,RAW,LNX,SWP partitions */
if (!((pi->flg & 1) && OK_id(pi->id)))
be32_to_cpu(pi->st),
be32_to_cpu(pi->siz));
}
- printk(" >");
+ strlcat(state->pp_buf, " >", PAGE_SIZE);
}
}
#endif
put_dev_sector(sect);
- printk ("\n");
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
return 1;
}
state = kzalloc(sizeof(struct parsed_partitions), GFP_KERNEL);
if (!state)
return NULL;
+ state->pp_buf = (char *)__get_free_page(GFP_KERNEL);
+ if (!state->pp_buf) {
+ kfree(state);
+ return NULL;
+ }
+ state->pp_buf[0] = '\0';
state->bdev = bdev;
disk_name(hd, 0, state->name);
- printk(KERN_INFO " %s:", state->name);
+ snprintf(state->pp_buf, PAGE_SIZE, " %s:", state->name);
if (isdigit(state->name[strlen(state->name)-1]))
sprintf(state->name, "p");
}
}
- if (res > 0)
+ if (res > 0) {
+ printk(KERN_INFO "%s", state->pp_buf);
+
+ free_page((unsigned long)state->pp_buf);
return state;
+ }
if (state->access_beyond_eod)
err = -ENOSPC;
if (err)
/* The partition is unrecognized. So report I/O errors if there were any */
res = err;
if (!res)
- printk(" unknown partition table\n");
+ strlcat(state->pp_buf, " unknown partition table\n", PAGE_SIZE);
else if (warn_no_part)
- printk(" unable to read partition table\n");
+ strlcat(state->pp_buf, " unable to read partition table\n", PAGE_SIZE);
+
+ printk(KERN_INFO "%s", state->pp_buf);
+
+ free_page((unsigned long)state->pp_buf);
kfree(state);
return ERR_PTR(res);
}
int next;
int limit;
bool access_beyond_eod;
+ char *pp_buf;
};
static inline void *read_part_sector(struct parsed_partitions *state,
put_partition(struct parsed_partitions *p, int n, sector_t from, sector_t size)
{
if (n < p->limit) {
+ char tmp[1 + BDEVNAME_SIZE + 10 + 1];
+
p->parts[n].from = from;
p->parts[n].size = size;
- printk(" %s%d", p->name, n);
+ snprintf(tmp, sizeof(tmp), " %s%d", p->name, n);
+ strlcat(p->pp_buf, tmp, PAGE_SIZE);
}
}
}
kfree(ptes);
kfree(gpt);
- printk("\n");
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
return 1;
}
unsigned char *data;
Sector sect;
sector_t labelsect;
+ char tmp[64];
res = 0;
blocksize = bdev_logical_block_size(bdev);
*/
blocksize = label->cms.block_size;
if (label->cms.disk_offset != 0) {
- printk("CMS1/%8s(MDSK):", name);
+ snprintf(tmp, sizeof(tmp), "CMS1/%8s(MDSK):", name);
+ strlcat(state->pp_buf, tmp, PAGE_SIZE);
/* disk is reserved minidisk */
offset = label->cms.disk_offset;
size = (label->cms.block_count - 1)
* (blocksize >> 9);
} else {
- printk("CMS1/%8s:", name);
+ snprintf(tmp, sizeof(tmp), "CMS1/%8s:", name);
+ strlcat(state->pp_buf, tmp, PAGE_SIZE);
offset = (info->label_block + 1);
size = label->cms.block_count
* (blocksize >> 9);
size-offset*(blocksize >> 9));
} else {
if (strncmp(type, "LNX1", 4) == 0) {
- printk("LNX1/%8s:", name);
+ snprintf(tmp, sizeof(tmp), "LNX1/%8s:", name);
+ strlcat(state->pp_buf, tmp, PAGE_SIZE);
if (label->lnx.ldl_version == 0xf2) {
fmt_size = label->lnx.formatted_blocks
* (blocksize >> 9);
offset = (info->label_block + 1);
} else {
/* unlabeled disk */
- printk("(nonl)");
+ strlcat(tmp, sizeof(tmp), "(nonl)", PAGE_SIZE);
size = i_size >> 9;
offset = (info->label_block + 1);
}
* if not, something is wrong, skipping partition detection
*/
if (strncmp(type, "VOL1", 4) == 0) {
- printk("VOL1/%8s:", name);
+ snprintf(tmp, sizeof(tmp), "VOL1/%8s:", name);
+ strlcat(state->pp_buf, tmp, PAGE_SIZE);
/*
* get block number and read then go through format1
* labels
}
- printk("\n");
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
goto out_freeall;
}
slot++;
}
- printk("\n");
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
put_dev_sector(sect);
return 1;
}
return false;
}
- printk (" [LDM]");
+ strlcat(pp->pp_buf, " [LDM]", PAGE_SIZE);
/* Create the data partitions */
list_for_each (item, &ldb->v_part) {
part_num++;
}
- printk ("\n");
+ strlcat(pp->pp_buf, "\n", PAGE_SIZE);
return true;
}
put_dev_sector(sect);
return 0; /* not a MacOS disk */
}
- printk(" [mac]");
+ strlcat(state->pp_buf, " [mac]", PAGE_SIZE);
blocks_in_map = be32_to_cpu(part->map_count);
for (blk = 1; blk <= blocks_in_map; ++blk) {
int pos = blk * secsize;
#endif
put_dev_sector(sect);
- printk("\n");
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
return 1;
}
put_dev_sector(sect);
return;
}
- printk(" %s%d: <solaris:", state->name, origin);
+ {
+ char tmp[1 + BDEVNAME_SIZE + 10 + 11 + 1];
+
+ snprintf(tmp, sizeof(tmp), " %s%d: <solaris:", state->name, origin);
+ strlcat(state->pp_buf, tmp, PAGE_SIZE);
+ }
if (le32_to_cpu(v->v_version) != 1) {
- printk(" cannot handle version %d vtoc>\n",
- le32_to_cpu(v->v_version));
+ char tmp[64];
+
+ snprintf(tmp, sizeof(tmp), " cannot handle version %d vtoc>\n",
+ le32_to_cpu(v->v_version));
+ strlcat(state->pp_buf, tmp, PAGE_SIZE);
put_dev_sector(sect);
return;
}
max_nparts = le16_to_cpu (v->v_nparts) > 8 ? SOLARIS_X86_NUMSLICE : 8;
for (i=0; i<max_nparts && state->next<state->limit; i++) {
struct solaris_x86_slice *s = &v->v_slice[i];
+ char tmp[3 + 10 + 1 + 1];
+
if (s->s_size == 0)
continue;
- printk(" [s%d]", i);
+ snprintf(tmp, sizeof(tmp), " [s%d]", i);
+ strlcat(state->pp_buf, tmp, PAGE_SIZE);
/* solaris partitions are relative to current MS-DOS
* one; must add the offset of the current partition */
put_partition(state, state->next++,
le32_to_cpu(s->s_size));
}
put_dev_sector(sect);
- printk(" >\n");
+ strlcat(state->pp_buf, " >\n", PAGE_SIZE);
#endif
}
Sector sect;
struct bsd_disklabel *l;
struct bsd_partition *p;
+ char tmp[64];
l = read_part_sector(state, offset + 1, §);
if (!l)
put_dev_sector(sect);
return;
}
- printk(" %s%d: <%s:", state->name, origin, flavour);
+
+ snprintf(tmp, sizeof(tmp), " %s%d: <%s:", state->name, origin, flavour);
+ strlcat(state->pp_buf, tmp, PAGE_SIZE);
if (le16_to_cpu(l->d_npartitions) < max_partitions)
max_partitions = le16_to_cpu(l->d_npartitions);
/* full parent partition, we have it already */
continue;
if (offset > bsd_start || offset+size < bsd_start+bsd_size) {
- printk("bad subpartition - ignored\n");
+ strlcat(state->pp_buf, "bad subpartition - ignored\n", PAGE_SIZE);
continue;
}
put_partition(state, state->next++, bsd_start, bsd_size);
}
put_dev_sector(sect);
- if (le16_to_cpu(l->d_npartitions) > max_partitions)
- printk(" (ignored %d more)",
- le16_to_cpu(l->d_npartitions) - max_partitions);
- printk(" >\n");
+ if (le16_to_cpu(l->d_npartitions) > max_partitions) {
+ snprintf(tmp, sizeof(tmp), " (ignored %d more)",
+ le16_to_cpu(l->d_npartitions) - max_partitions);
+ strlcat(state->pp_buf, tmp, PAGE_SIZE);
+ }
+ strlcat(state->pp_buf, " >\n", PAGE_SIZE);
}
#endif
put_dev_sector(sect);
return;
}
- printk(" %s%d: <unixware:", state->name, origin);
+ {
+ char tmp[1 + BDEVNAME_SIZE + 10 + 12 + 1];
+
+ snprintf(tmp, sizeof(tmp), " %s%d: <unixware:", state->name, origin);
+ strlcat(state->pp_buf, tmp, PAGE_SIZE);
+ }
p = &l->vtoc.v_slice[1];
/* I omit the 0th slice as it is the same as whole disk. */
while (p - &l->vtoc.v_slice[0] < UNIXWARE_NUMSLICE) {
p++;
}
put_dev_sector(sect);
- printk(" >\n");
+ strlcat(state->pp_buf, " >\n", PAGE_SIZE);
#endif
}
* the normal boot sector. */
if (msdos_magic_present (data + 510) &&
SYS_IND(p) == MINIX_PARTITION) { /* subpartition table present */
+ char tmp[1 + BDEVNAME_SIZE + 10 + 9 + 1];
- printk(" %s%d: <minix:", state->name, origin);
+ snprintf(tmp, sizeof(tmp), " %s%d: <minix:", state->name, origin);
+ strlcat(state->pp_buf, tmp, PAGE_SIZE);
for (i = 0; i < MINIX_NR_SUBPARTITIONS; i++, p++) {
if (state->next == state->limit)
break;
put_partition(state, state->next++,
start_sect(p), nr_sects(p));
}
- printk(" >\n");
+ strlcat(state->pp_buf, " >\n", PAGE_SIZE);
}
put_dev_sector(sect);
#endif /* CONFIG_MINIX_SUBPARTITION */
if (aix_magic_present(state, data)) {
put_dev_sector(sect);
- printk( " [AIX]");
+ strlcat(state->pp_buf, " [AIX]", PAGE_SIZE);
return 0;
}
fb = (struct fat_boot_sector *) data;
if (slot == 1 && fb->reserved && fb->fats
&& fat_valid_media(fb->media)) {
- printk("\n");
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
put_dev_sector(sect);
return 1;
} else {
n = min(size, max(sector_size, n));
put_partition(state, slot, start, n);
- printk(" <");
+ strlcat(state->pp_buf, " <", PAGE_SIZE);
parse_extended(state, start, size);
- printk(" >");
+ strlcat(state->pp_buf, " >", PAGE_SIZE);
continue;
}
put_partition(state, slot, start, size);
if (SYS_IND(p) == LINUX_RAID_PARTITION)
state->parts[slot].flags = ADDPART_FLAG_RAID;
if (SYS_IND(p) == DM6_PARTITION)
- printk("[DM]");
+ strlcat(state->pp_buf, "[DM]", PAGE_SIZE);
if (SYS_IND(p) == EZD_PARTITION)
- printk("[EZD]");
+ strlcat(state->pp_buf, "[EZD]", PAGE_SIZE);
}
- printk("\n");
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
/* second pass - output for each on a separate line */
p = (struct partition *) (0x1be + data);
le32_to_cpu(partition->p_size));
slot++;
}
- printk("\n");
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
put_dev_sector(sect);
return 1;
}
}
slot++;
}
- printk("\n");
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
put_dev_sector(sect);
return 1;
}
}
slot++;
}
- printk("\n");
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
put_dev_sector(sect);
return 1;
}
unsigned char *data;
struct dkblk0 *b;
struct slice *slice;
+ char tmp[64];
data = read_part_sector(state, 0, §);
if (!data)
return -1;
slices -= 1; /* last slice is the whole disk */
- printk("sysV68: %s(s%u)", state->name, slices);
+ snprintf(tmp, sizeof(tmp), "sysV68: %s(s%u)", state->name, slices);
+ strlcat(state->pp_buf, tmp, PAGE_SIZE);
slice = (struct slice *)data;
for (i = 0; i < slices; i++, slice++) {
if (slot == state->limit)
put_partition(state, slot,
be32_to_cpu(slice->blkoff),
be32_to_cpu(slice->nblocks));
- printk("(s%u)", i);
+ snprintf(tmp, sizeof(tmp), "(s%u)", i);
+ strlcat(state->pp_buf, tmp, PAGE_SIZE);
}
slot++;
}
- printk("\n");
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
put_dev_sector(sect);
return 1;
}
label->pt_part[i].pi_blkoff,
label->pt_part[i].pi_nblocks);
put_dev_sector(sect);
- printk ("\n");
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
return 1;
} else {
put_dev_sector(sect);
# Makefile for the Linux proc filesystem routines.
#
-obj-$(CONFIG_PROC_FS) += proc.o
+obj-y += proc.o
proc-y := nommu.o task_nommu.o
proc-$(CONFIG_MMU) := mmu.o task_mmu.o
static int reiserfs_async_progress_wait(struct super_block *s)
{
- DEFINE_WAIT(wait);
struct reiserfs_journal *j = SB_JOURNAL(s);
if (atomic_read(&j->j_async_throttle)) {
err |= __put_user(kinfo->si_tid, &uinfo->ssi_tid);
err |= __put_user(kinfo->si_overrun, &uinfo->ssi_overrun);
err |= __put_user((long) kinfo->si_ptr, &uinfo->ssi_ptr);
+ err |= __put_user(kinfo->si_int, &uinfo->ssi_int);
break;
case __SI_POLL:
err |= __put_user(kinfo->si_band, &uinfo->ssi_band);
err |= __put_user(kinfo->si_pid, &uinfo->ssi_pid);
err |= __put_user(kinfo->si_uid, &uinfo->ssi_uid);
err |= __put_user((long) kinfo->si_ptr, &uinfo->ssi_ptr);
+ err |= __put_user(kinfo->si_int, &uinfo->ssi_int);
break;
default:
/*
* If the page isn't uptodate, we may need to start io on it
*/
if (!PageUptodate(page)) {
- /*
- * If in nonblock mode then dont block on waiting
- * for an in-flight io page
- */
- if (flags & SPLICE_F_NONBLOCK) {
- if (!trylock_page(page)) {
- error = -EAGAIN;
- break;
- }
- } else
- lock_page(page);
+ lock_page(page);
/*
* Page was truncated, or invalidated by the
struct page *pages[PIPE_DEF_BUFFERS];
struct partial_page partial[PIPE_DEF_BUFFERS];
struct iovec *vec, __vec[PIPE_DEF_BUFFERS];
- pgoff_t index;
ssize_t res;
size_t this_len;
int error;
goto shrink_ret;
}
- index = *ppos >> PAGE_CACHE_SHIFT;
offset = *ppos & ~PAGE_CACHE_MASK;
nr_pages = (len + offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
help
Saying Y here includes support for SquashFS 4.0 (a Compressed
Read-Only File System). Squashfs is a highly compressed read-only
- filesystem for Linux. It uses zlib compression to compress both
+ filesystem for Linux. It uses zlib/lzo compression to compress both
files, inodes and directories. Inodes in the system are very small
and all blocks are packed to minimise data overhead. Block sizes
greater than 4K are supported up to a maximum of 1 Mbytes (default
block size 128K). SquashFS 4.0 supports 64 bit filesystems and files
(larger than 4GB), full uid/gid information, hard links and
- timestamps.
+ timestamps.
Squashfs is intended for general read-only filesystem use, for
archival use (i.e. in cases where a .tar.gz file may be used), and in
If unsure, say N.
-config SQUASHFS_XATTRS
+config SQUASHFS_XATTR
bool "Squashfs XATTR support"
depends on SQUASHFS
default n
If unsure, say N.
-config SQUASHFS_EMBEDDED
+config SQUASHFS_LZO
+ bool "Include support for LZO compressed file systems"
+ depends on SQUASHFS
+ default n
+ select LZO_DECOMPRESS
+ help
+ Saying Y here includes support for reading Squashfs file systems
+ compressed with LZO compresssion. LZO compression is mainly
+ aimed at embedded systems with slower CPUs where the overheads
+ of zlib are too high.
- bool "Additional option for memory-constrained systems"
+ LZO is not the standard compression used in Squashfs and so most
+ file systems will be readable without selecting this option.
+
+ If unsure, say N.
+
+config SQUASHFS_EMBEDDED
+ bool "Additional option for memory-constrained systems"
depends on SQUASHFS
default n
help
obj-$(CONFIG_SQUASHFS) += squashfs.o
squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o
squashfs-y += namei.o super.o symlink.o zlib_wrapper.o decompressor.o
-squashfs-$(CONFIG_SQUASHFS_XATTRS) += xattr.o xattr_id.o
-
+squashfs-$(CONFIG_SQUASHFS_XATTR) += xattr.o xattr_id.o
+squashfs-$(CONFIG_SQUASHFS_LZO) += lzo_wrapper.o
NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0
};
+#ifndef CONFIG_SQUASHFS_LZO
static const struct squashfs_decompressor squashfs_lzo_unsupported_comp_ops = {
NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0
};
+#endif
static const struct squashfs_decompressor squashfs_unknown_comp_ops = {
NULL, NULL, NULL, 0, "unknown", 0
static const struct squashfs_decompressor *decompressor[] = {
&squashfs_zlib_comp_ops,
&squashfs_lzma_unsupported_comp_ops,
+#ifdef CONFIG_SQUASHFS_LZO
+ &squashfs_lzo_comp_ops,
+#else
&squashfs_lzo_unsupported_comp_ops,
+#endif
&squashfs_unknown_comp_ops
};
--- /dev/null
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2010 LG Electronics
+ * Chan Jeong <chan.jeong@lge.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * lzo_wrapper.c
+ */
+
+#include <linux/mutex.h>
+#include <linux/buffer_head.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/lzo.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+#include "decompressor.h"
+
+struct squashfs_lzo {
+ void *input;
+ void *output;
+};
+
+static void *lzo_init(struct squashfs_sb_info *msblk)
+{
+ int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE);
+
+ struct squashfs_lzo *stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (stream == NULL)
+ goto failed;
+ stream->input = vmalloc(block_size);
+ if (stream->input == NULL)
+ goto failed;
+ stream->output = vmalloc(block_size);
+ if (stream->output == NULL)
+ goto failed2;
+
+ return stream;
+
+failed2:
+ vfree(stream->input);
+failed:
+ ERROR("Failed to allocate lzo workspace\n");
+ kfree(stream);
+ return NULL;
+}
+
+
+static void lzo_free(void *strm)
+{
+ struct squashfs_lzo *stream = strm;
+
+ if (stream) {
+ vfree(stream->input);
+ vfree(stream->output);
+ }
+ kfree(stream);
+}
+
+
+static int lzo_uncompress(struct squashfs_sb_info *msblk, void **buffer,
+ struct buffer_head **bh, int b, int offset, int length, int srclength,
+ int pages)
+{
+ struct squashfs_lzo *stream = msblk->stream;
+ void *buff = stream->input;
+ int avail, i, bytes = length, res;
+ size_t out_len = srclength;
+
+ mutex_lock(&msblk->read_data_mutex);
+
+ for (i = 0; i < b; i++) {
+ wait_on_buffer(bh[i]);
+ if (!buffer_uptodate(bh[i]))
+ goto block_release;
+
+ avail = min(bytes, msblk->devblksize - offset);
+ memcpy(buff, bh[i]->b_data + offset, avail);
+ buff += avail;
+ bytes -= avail;
+ offset = 0;
+ put_bh(bh[i]);
+ }
+
+ res = lzo1x_decompress_safe(stream->input, (size_t)length,
+ stream->output, &out_len);
+ if (res != LZO_E_OK)
+ goto failed;
+
+ res = bytes = (int)out_len;
+ for (i = 0, buff = stream->output; bytes && i < pages; i++) {
+ avail = min_t(int, bytes, PAGE_CACHE_SIZE);
+ memcpy(buffer[i], buff, avail);
+ buff += avail;
+ bytes -= avail;
+ }
+
+ mutex_unlock(&msblk->read_data_mutex);
+ return res;
+
+block_release:
+ for (; i < b; i++)
+ put_bh(bh[i]);
+
+failed:
+ mutex_unlock(&msblk->read_data_mutex);
+
+ ERROR("lzo decompression failed, data probably corrupt\n");
+ return -EIO;
+}
+
+const struct squashfs_decompressor squashfs_lzo_comp_ops = {
+ .init = lzo_init,
+ .free = lzo_free,
+ .decompress = lzo_uncompress,
+ .id = LZO_COMPRESSION,
+ .name = "lzo",
+ .supported = 1
+};
/* zlib_wrapper.c */
extern const struct squashfs_decompressor squashfs_zlib_comp_ops;
+
+/* lzo_wrapper.c */
+extern const struct squashfs_decompressor squashfs_lzo_comp_ops;
__le16 uid;
__le16 guid;
__le32 mtime;
- __le32 inode_number;
+ __le32 inode_number;
};
struct squashfs_ipc_inode {
__le16 uid;
__le16 guid;
__le32 mtime;
- __le32 inode_number;
+ __le32 inode_number;
__le32 nlink;
};
__le16 uid;
__le16 guid;
__le32 mtime;
- __le32 inode_number;
+ __le32 inode_number;
__le32 nlink;
__le32 xattr;
};
__le16 uid;
__le16 guid;
__le32 mtime;
- __le32 inode_number;
+ __le32 inode_number;
__le32 nlink;
__le32 rdev;
};
__le16 uid;
__le16 guid;
__le32 mtime;
- __le32 inode_number;
+ __le32 inode_number;
__le32 nlink;
__le32 rdev;
__le32 xattr;
__le16 uid;
__le16 guid;
__le32 mtime;
- __le32 inode_number;
+ __le32 inode_number;
__le32 nlink;
__le32 symlink_size;
char symlink[0];
__le16 uid;
__le16 guid;
__le32 mtime;
- __le32 inode_number;
+ __le32 inode_number;
__le32 start_block;
__le32 fragment;
__le32 offset;
__le16 uid;
__le16 guid;
__le32 mtime;
- __le32 inode_number;
+ __le32 inode_number;
__le64 start_block;
__le64 file_size;
__le64 sparse;
__le16 uid;
__le16 guid;
__le32 mtime;
- __le32 inode_number;
+ __le32 inode_number;
__le32 start_block;
__le32 nlink;
__le16 file_size;
__le16 uid;
__le16 guid;
__le32 mtime;
- __le32 inode_number;
+ __le32 inode_number;
__le32 nlink;
__le32 file_size;
__le32 start_block;
* along with this program; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
- * xattr_id.c
+ * xattr.c
*/
#include <linux/init.h>
.get = squashfs_security_get
};
-static inline const struct xattr_handler *squashfs_xattr_handler(int type)
+static const struct xattr_handler *squashfs_xattr_handler(int type)
{
if (type & ~(SQUASHFS_XATTR_PREFIX_MASK | SQUASHFS_XATTR_VALUE_OOL))
/* ignore unrecognised type */
* xattr.h
*/
-#ifdef CONFIG_SQUASHFS_XATTRS
+#ifdef CONFIG_SQUASHFS_XATTR
extern __le64 *squashfs_read_xattr_id_table(struct super_block *, u64,
u64 *, int *);
extern int squashfs_xattr_lookup(struct super_block *, unsigned int, int *,
extern int
dma_get_cache_alignment(void);
-extern int
-dma_is_consistent(struct device *dev, dma_addr_t dma_handle);
-
extern void
dma_cache_sync(struct device *dev, void *vaddr, size_t size,
enum dma_data_direction direction);
* -Eric Paris
*/
+/*
+ * When introducing new O_* bits, please check its uniqueness in fcntl_init().
+ */
+
#define O_ACCMODE 00000003
#define O_RDONLY 00000000
#define O_WRONLY 00000001
#define TCSETX 0x5433
#define TCSETXF 0x5434
#define TCSETXW 0x5435
+#define TIOCSIG _IOW('T', 0x36, int) /* pty: generate signal */
#define FIONCLEX 0x5450
#define FIOCLEX 0x5451
#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
/*
- * some architectures define FIOQSIZE as 0x545E, which is used for
- * TIOCGHAYESESP on others
+ * Some arches already define FIOQSIZE due to a historical
+ * conflict with a Hayes modem-specific ioctl value.
*/
#ifndef FIOQSIZE
-# define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
-# define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
# define FIOQSIZE 0x5460
#endif
#define TIOCPKT_START 8
#define TIOCPKT_NOSTOP 16
#define TIOCPKT_DOSTOP 32
+#define TIOCPKT_IOCTL 64
#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
#define FLUSHO 0010000
#define PENDIN 0040000
#define IEXTEN 0100000
+#define EXTPROC 0200000
/* tcflow() and TCXONC use these */
#define TCOOFF 0
header-y += b1lli.h
header-y += baycom.h
header-y += bfs_fs.h
+header-y += blk_types.h
header-y += blkpg.h
header-y += bpqether.h
header-y += bsg.h
#define audit_putname(n) do { ; } while (0)
#define __audit_inode(n,d) do { ; } while (0)
#define __audit_inode_child(i,p) do { ; } while (0)
-#define audit_inode(n,d) do { ; } while (0)
+#define audit_inode(n,d) do { (void)(d); } while (0)
#define audit_inode_child(i,p) do { ; } while (0)
#define audit_core_dumps(i) do { ; } while (0)
#define auditsc_get_stamp(c,t,s) (0)
#define AUTOFS_IOC_FAIL _IO(0x93,0x61)
#define AUTOFS_IOC_CATATONIC _IO(0x93,0x62)
#define AUTOFS_IOC_PROTOVER _IOR(0x93,0x63,int)
+#define AUTOFS_IOC_SETTIMEOUT32 _IOWR(0x93,0x64,compat_ulong_t)
#define AUTOFS_IOC_SETTIMEOUT _IOWR(0x93,0x64,unsigned long)
#define AUTOFS_IOC_EXPIRE _IOR(0x93,0x65,struct autofs_packet_expire)
#define BDI_STAT_BATCH (8*(1+ilog2(nr_cpu_ids)))
struct bdi_writeback {
- struct list_head list; /* hangs off the bdi */
-
- struct backing_dev_info *bdi; /* our parent bdi */
+ struct backing_dev_info *bdi; /* our parent bdi */
unsigned int nr;
- unsigned long last_old_flush; /* last old data flush */
+ unsigned long last_old_flush; /* last old data flush */
+ unsigned long last_active; /* last time bdi thread was active */
- struct task_struct *task; /* writeback task */
- struct list_head b_dirty; /* dirty inodes */
- struct list_head b_io; /* parked for writeback */
- struct list_head b_more_io; /* parked for more writeback */
+ struct task_struct *task; /* writeback thread */
+ struct timer_list wakeup_timer; /* used for delayed bdi thread wakeup */
+ struct list_head b_dirty; /* dirty inodes */
+ struct list_head b_io; /* parked for writeback */
+ struct list_head b_more_io; /* parked for more writeback */
};
struct backing_dev_info {
struct list_head bdi_list;
- struct rcu_head rcu_head;
unsigned long ra_pages; /* max readahead in PAGE_CACHE_SIZE units */
unsigned long state; /* Always use atomic bitops on this */
unsigned int capabilities; /* Device capabilities */
unsigned int max_ratio, max_prop_frac;
struct bdi_writeback wb; /* default writeback info for this bdi */
- spinlock_t wb_lock; /* protects update side of wb_list */
- struct list_head wb_list; /* the flusher threads hanging off this bdi */
+ spinlock_t wb_lock; /* protects work_list */
struct list_head work_list;
int bdi_setup_and_register(struct backing_dev_info *, char *, unsigned int);
void bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages);
void bdi_start_background_writeback(struct backing_dev_info *bdi);
-int bdi_writeback_task(struct bdi_writeback *wb);
+int bdi_writeback_thread(void *data);
int bdi_has_dirty_io(struct backing_dev_info *bdi);
void bdi_arm_supers_timer(void);
+void bdi_wakeup_thread_delayed(struct backing_dev_info *bdi);
extern spinlock_t bdi_lock;
extern struct list_head bdi_list;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
-
+ *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
#include <asm/io.h>
+/* struct bio, bio_vec and BIO_* flags are defined in blk_types.h */
+#include <linux/blk_types.h>
+
#define BIO_DEBUG
#ifdef BIO_DEBUG
#define BIO_MAX_SIZE (BIO_MAX_PAGES << PAGE_CACHE_SHIFT)
#define BIO_MAX_SECTORS (BIO_MAX_SIZE >> 9)
-/*
- * was unsigned short, but we might as well be ready for > 64kB I/O pages
- */
-struct bio_vec {
- struct page *bv_page;
- unsigned int bv_len;
- unsigned int bv_offset;
-};
-
-struct bio_set;
-struct bio;
-struct bio_integrity_payload;
-typedef void (bio_end_io_t) (struct bio *, int);
-typedef void (bio_destructor_t) (struct bio *);
-
-/*
- * main unit of I/O for the block layer and lower layers (ie drivers and
- * stacking drivers)
- */
-struct bio {
- sector_t bi_sector; /* device address in 512 byte
- sectors */
- struct bio *bi_next; /* request queue link */
- struct block_device *bi_bdev;
- unsigned long bi_flags; /* status, command, etc */
- unsigned long bi_rw; /* bottom bits READ/WRITE,
- * top bits priority
- */
-
- unsigned short bi_vcnt; /* how many bio_vec's */
- unsigned short bi_idx; /* current index into bvl_vec */
-
- /* Number of segments in this BIO after
- * physical address coalescing is performed.
- */
- unsigned int bi_phys_segments;
-
- unsigned int bi_size; /* residual I/O count */
-
- /*
- * To keep track of the max segment size, we account for the
- * sizes of the first and last mergeable segments in this bio.
- */
- unsigned int bi_seg_front_size;
- unsigned int bi_seg_back_size;
-
- unsigned int bi_max_vecs; /* max bvl_vecs we can hold */
-
- unsigned int bi_comp_cpu; /* completion CPU */
-
- atomic_t bi_cnt; /* pin count */
-
- struct bio_vec *bi_io_vec; /* the actual vec list */
-
- bio_end_io_t *bi_end_io;
-
- void *bi_private;
-#if defined(CONFIG_BLK_DEV_INTEGRITY)
- struct bio_integrity_payload *bi_integrity; /* data integrity */
-#endif
-
- bio_destructor_t *bi_destructor; /* destructor */
-
- /*
- * We can inline a number of vecs at the end of the bio, to avoid
- * double allocations for a small number of bio_vecs. This member
- * MUST obviously be kept at the very end of the bio.
- */
- struct bio_vec bi_inline_vecs[0];
-};
-
-/*
- * bio flags
- */
-#define BIO_UPTODATE 0 /* ok after I/O completion */
-#define BIO_RW_BLOCK 1 /* RW_AHEAD set, and read/write would block */
-#define BIO_EOF 2 /* out-out-bounds error */
-#define BIO_SEG_VALID 3 /* bi_phys_segments valid */
-#define BIO_CLONED 4 /* doesn't own data */
-#define BIO_BOUNCED 5 /* bio is a bounce bio */
-#define BIO_USER_MAPPED 6 /* contains user pages */
-#define BIO_EOPNOTSUPP 7 /* not supported */
-#define BIO_CPU_AFFINE 8 /* complete bio on same CPU as submitted */
-#define BIO_NULL_MAPPED 9 /* contains invalid user pages */
-#define BIO_FS_INTEGRITY 10 /* fs owns integrity data, not block layer */
-#define BIO_QUIET 11 /* Make BIO Quiet */
-#define bio_flagged(bio, flag) ((bio)->bi_flags & (1 << (flag)))
-
-/*
- * top 4 bits of bio flags indicate the pool this bio came from
- */
-#define BIO_POOL_BITS (4)
-#define BIO_POOL_NONE ((1UL << BIO_POOL_BITS) - 1)
-#define BIO_POOL_OFFSET (BITS_PER_LONG - BIO_POOL_BITS)
-#define BIO_POOL_MASK (1UL << BIO_POOL_OFFSET)
-#define BIO_POOL_IDX(bio) ((bio)->bi_flags >> BIO_POOL_OFFSET)
-
-/*
- * bio bi_rw flags
- *
- * bit 0 -- data direction
- * If not set, bio is a read from device. If set, it's a write to device.
- * bit 1 -- fail fast device errors
- * bit 2 -- fail fast transport errors
- * bit 3 -- fail fast driver errors
- * bit 4 -- rw-ahead when set
- * bit 5 -- barrier
- * Insert a serialization point in the IO queue, forcing previously
- * submitted IO to be completed before this one is issued.
- * bit 6 -- synchronous I/O hint.
- * bit 7 -- Unplug the device immediately after submitting this bio.
- * bit 8 -- metadata request
- * Used for tracing to differentiate metadata and data IO. May also
- * get some preferential treatment in the IO scheduler
- * bit 9 -- discard sectors
- * Informs the lower level device that this range of sectors is no longer
- * used by the file system and may thus be freed by the device. Used
- * for flash based storage.
- * Don't want driver retries for any fast fail whatever the reason.
- * bit 10 -- Tell the IO scheduler not to wait for more requests after this
- one has been submitted, even if it is a SYNC request.
- */
-enum bio_rw_flags {
- BIO_RW,
- BIO_RW_FAILFAST_DEV,
- BIO_RW_FAILFAST_TRANSPORT,
- BIO_RW_FAILFAST_DRIVER,
- /* above flags must match REQ_* */
- BIO_RW_AHEAD,
- BIO_RW_BARRIER,
- BIO_RW_SYNCIO,
- BIO_RW_UNPLUG,
- BIO_RW_META,
- BIO_RW_DISCARD,
- BIO_RW_NOIDLE,
-};
-
-/*
- * First four bits must match between bio->bi_rw and rq->cmd_flags, make
- * that explicit here.
- */
-#define BIO_RW_RQ_MASK 0xf
-
-static inline bool bio_rw_flagged(struct bio *bio, enum bio_rw_flags flag)
-{
- return (bio->bi_rw & (1 << flag)) != 0;
-}
-
/*
* upper 16 bits of bi_rw define the io priority of this bio
*/
#define bio_offset(bio) bio_iovec((bio))->bv_offset
#define bio_segments(bio) ((bio)->bi_vcnt - (bio)->bi_idx)
#define bio_sectors(bio) ((bio)->bi_size >> 9)
-#define bio_empty_barrier(bio) (bio_rw_flagged(bio, BIO_RW_BARRIER) && !bio_has_data(bio) && !bio_rw_flagged(bio, BIO_RW_DISCARD))
+#define bio_empty_barrier(bio) \
+ ((bio->bi_rw & REQ_HARDBARRIER) && \
+ !bio_has_data(bio) && \
+ !(bio->bi_rw & REQ_DISCARD))
static inline unsigned int bio_cur_bytes(struct bio *bio)
{
--- /dev/null
+/*
+ * Block data types and constants. Directly include this file only to
+ * break include dependency loop.
+ */
+#ifndef __LINUX_BLK_TYPES_H
+#define __LINUX_BLK_TYPES_H
+
+#ifdef CONFIG_BLOCK
+
+#include <linux/types.h>
+
+struct bio_set;
+struct bio;
+struct bio_integrity_payload;
+struct page;
+struct block_device;
+typedef void (bio_end_io_t) (struct bio *, int);
+typedef void (bio_destructor_t) (struct bio *);
+
+/*
+ * was unsigned short, but we might as well be ready for > 64kB I/O pages
+ */
+struct bio_vec {
+ struct page *bv_page;
+ unsigned int bv_len;
+ unsigned int bv_offset;
+};
+
+/*
+ * main unit of I/O for the block layer and lower layers (ie drivers and
+ * stacking drivers)
+ */
+struct bio {
+ sector_t bi_sector; /* device address in 512 byte
+ sectors */
+ struct bio *bi_next; /* request queue link */
+ struct block_device *bi_bdev;
+ unsigned long bi_flags; /* status, command, etc */
+ unsigned long bi_rw; /* bottom bits READ/WRITE,
+ * top bits priority
+ */
+
+ unsigned short bi_vcnt; /* how many bio_vec's */
+ unsigned short bi_idx; /* current index into bvl_vec */
+
+ /* Number of segments in this BIO after
+ * physical address coalescing is performed.
+ */
+ unsigned int bi_phys_segments;
+
+ unsigned int bi_size; /* residual I/O count */
+
+ /*
+ * To keep track of the max segment size, we account for the
+ * sizes of the first and last mergeable segments in this bio.
+ */
+ unsigned int bi_seg_front_size;
+ unsigned int bi_seg_back_size;
+
+ unsigned int bi_max_vecs; /* max bvl_vecs we can hold */
+
+ unsigned int bi_comp_cpu; /* completion CPU */
+
+ atomic_t bi_cnt; /* pin count */
+
+ struct bio_vec *bi_io_vec; /* the actual vec list */
+
+ bio_end_io_t *bi_end_io;
+
+ void *bi_private;
+#if defined(CONFIG_BLK_DEV_INTEGRITY)
+ struct bio_integrity_payload *bi_integrity; /* data integrity */
+#endif
+
+ bio_destructor_t *bi_destructor; /* destructor */
+
+ /*
+ * We can inline a number of vecs at the end of the bio, to avoid
+ * double allocations for a small number of bio_vecs. This member
+ * MUST obviously be kept at the very end of the bio.
+ */
+ struct bio_vec bi_inline_vecs[0];
+};
+
+/*
+ * bio flags
+ */
+#define BIO_UPTODATE 0 /* ok after I/O completion */
+#define BIO_RW_BLOCK 1 /* RW_AHEAD set, and read/write would block */
+#define BIO_EOF 2 /* out-out-bounds error */
+#define BIO_SEG_VALID 3 /* bi_phys_segments valid */
+#define BIO_CLONED 4 /* doesn't own data */
+#define BIO_BOUNCED 5 /* bio is a bounce bio */
+#define BIO_USER_MAPPED 6 /* contains user pages */
+#define BIO_EOPNOTSUPP 7 /* not supported */
+#define BIO_CPU_AFFINE 8 /* complete bio on same CPU as submitted */
+#define BIO_NULL_MAPPED 9 /* contains invalid user pages */
+#define BIO_FS_INTEGRITY 10 /* fs owns integrity data, not block layer */
+#define BIO_QUIET 11 /* Make BIO Quiet */
+#define bio_flagged(bio, flag) ((bio)->bi_flags & (1 << (flag)))
+
+/*
+ * top 4 bits of bio flags indicate the pool this bio came from
+ */
+#define BIO_POOL_BITS (4)
+#define BIO_POOL_NONE ((1UL << BIO_POOL_BITS) - 1)
+#define BIO_POOL_OFFSET (BITS_PER_LONG - BIO_POOL_BITS)
+#define BIO_POOL_MASK (1UL << BIO_POOL_OFFSET)
+#define BIO_POOL_IDX(bio) ((bio)->bi_flags >> BIO_POOL_OFFSET)
+
+#endif /* CONFIG_BLOCK */
+
+/*
+ * Request flags. For use in the cmd_flags field of struct request, and in
+ * bi_rw of struct bio. Note that some flags are only valid in either one.
+ */
+enum rq_flag_bits {
+ /* common flags */
+ __REQ_WRITE, /* not set, read. set, write */
+ __REQ_FAILFAST_DEV, /* no driver retries of device errors */
+ __REQ_FAILFAST_TRANSPORT, /* no driver retries of transport errors */
+ __REQ_FAILFAST_DRIVER, /* no driver retries of driver errors */
+
+ __REQ_HARDBARRIER, /* may not be passed by drive either */
+ __REQ_SYNC, /* request is sync (sync write or read) */
+ __REQ_META, /* metadata io request */
+ __REQ_DISCARD, /* request to discard sectors */
+ __REQ_NOIDLE, /* don't anticipate more IO after this one */
+
+ /* bio only flags */
+ __REQ_UNPLUG, /* unplug the immediately after submission */
+ __REQ_RAHEAD, /* read ahead, can fail anytime */
+
+ /* request only flags */
+ __REQ_SORTED, /* elevator knows about this request */
+ __REQ_SOFTBARRIER, /* may not be passed by ioscheduler */
+ __REQ_FUA, /* forced unit access */
+ __REQ_NOMERGE, /* don't touch this for merging */
+ __REQ_STARTED, /* drive already may have started this one */
+ __REQ_DONTPREP, /* don't call prep for this one */
+ __REQ_QUEUED, /* uses queueing */
+ __REQ_ELVPRIV, /* elevator private data attached */
+ __REQ_FAILED, /* set if the request failed */
+ __REQ_QUIET, /* don't worry about errors */
+ __REQ_PREEMPT, /* set for "ide_preempt" requests */
+ __REQ_ORDERED_COLOR, /* is before or after barrier */
+ __REQ_ALLOCED, /* request came from our alloc pool */
+ __REQ_COPY_USER, /* contains copies of user pages */
+ __REQ_INTEGRITY, /* integrity metadata has been remapped */
+ __REQ_FLUSH, /* request for cache flush */
+ __REQ_IO_STAT, /* account I/O stat */
+ __REQ_MIXED_MERGE, /* merge of different types, fail separately */
+ __REQ_NR_BITS, /* stops here */
+};
+
+#define REQ_WRITE (1 << __REQ_WRITE)
+#define REQ_FAILFAST_DEV (1 << __REQ_FAILFAST_DEV)
+#define REQ_FAILFAST_TRANSPORT (1 << __REQ_FAILFAST_TRANSPORT)
+#define REQ_FAILFAST_DRIVER (1 << __REQ_FAILFAST_DRIVER)
+#define REQ_HARDBARRIER (1 << __REQ_HARDBARRIER)
+#define REQ_SYNC (1 << __REQ_SYNC)
+#define REQ_META (1 << __REQ_META)
+#define REQ_DISCARD (1 << __REQ_DISCARD)
+#define REQ_NOIDLE (1 << __REQ_NOIDLE)
+
+#define REQ_FAILFAST_MASK \
+ (REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER)
+#define REQ_COMMON_MASK \
+ (REQ_WRITE | REQ_FAILFAST_MASK | REQ_HARDBARRIER | REQ_SYNC | \
+ REQ_META| REQ_DISCARD | REQ_NOIDLE)
+
+#define REQ_UNPLUG (1 << __REQ_UNPLUG)
+#define REQ_RAHEAD (1 << __REQ_RAHEAD)
+
+#define REQ_SORTED (1 << __REQ_SORTED)
+#define REQ_SOFTBARRIER (1 << __REQ_SOFTBARRIER)
+#define REQ_FUA (1 << __REQ_FUA)
+#define REQ_NOMERGE (1 << __REQ_NOMERGE)
+#define REQ_STARTED (1 << __REQ_STARTED)
+#define REQ_DONTPREP (1 << __REQ_DONTPREP)
+#define REQ_QUEUED (1 << __REQ_QUEUED)
+#define REQ_ELVPRIV (1 << __REQ_ELVPRIV)
+#define REQ_FAILED (1 << __REQ_FAILED)
+#define REQ_QUIET (1 << __REQ_QUIET)
+#define REQ_PREEMPT (1 << __REQ_PREEMPT)
+#define REQ_ORDERED_COLOR (1 << __REQ_ORDERED_COLOR)
+#define REQ_ALLOCED (1 << __REQ_ALLOCED)
+#define REQ_COPY_USER (1 << __REQ_COPY_USER)
+#define REQ_INTEGRITY (1 << __REQ_INTEGRITY)
+#define REQ_FLUSH (1 << __REQ_FLUSH)
+#define REQ_IO_STAT (1 << __REQ_IO_STAT)
+#define REQ_MIXED_MERGE (1 << __REQ_MIXED_MERGE)
+
+#endif /* __LINUX_BLK_TYPES_H */
REQ_TYPE_PM_RESUME, /* resume request */
REQ_TYPE_PM_SHUTDOWN, /* shutdown request */
REQ_TYPE_SPECIAL, /* driver defined type */
- REQ_TYPE_LINUX_BLOCK, /* generic block layer message */
/*
* for ATA/ATAPI devices. this really doesn't belong here, ide should
* use REQ_TYPE_SPECIAL and use rq->cmd[0] with the range of driver
REQ_TYPE_ATA_PC,
};
-/*
- * For request of type REQ_TYPE_LINUX_BLOCK, rq->cmd[0] is the opcode being
- * sent down (similar to how REQ_TYPE_BLOCK_PC means that ->cmd[] holds a
- * SCSI cdb.
- *
- * 0x00 -> 0x3f are driver private, to be used for whatever purpose they need,
- * typically to differentiate REQ_TYPE_SPECIAL requests.
- *
- */
-enum {
- REQ_LB_OP_EJECT = 0x40, /* eject request */
- REQ_LB_OP_FLUSH = 0x41, /* flush request */
-};
-
-/*
- * request type modified bits. first four bits match BIO_RW* bits, important
- */
-enum rq_flag_bits {
- __REQ_RW, /* not set, read. set, write */
- __REQ_FAILFAST_DEV, /* no driver retries of device errors */
- __REQ_FAILFAST_TRANSPORT, /* no driver retries of transport errors */
- __REQ_FAILFAST_DRIVER, /* no driver retries of driver errors */
- /* above flags must match BIO_RW_* */
- __REQ_DISCARD, /* request to discard sectors */
- __REQ_SORTED, /* elevator knows about this request */
- __REQ_SOFTBARRIER, /* may not be passed by ioscheduler */
- __REQ_HARDBARRIER, /* may not be passed by drive either */
- __REQ_FUA, /* forced unit access */
- __REQ_NOMERGE, /* don't touch this for merging */
- __REQ_STARTED, /* drive already may have started this one */
- __REQ_DONTPREP, /* don't call prep for this one */
- __REQ_QUEUED, /* uses queueing */
- __REQ_ELVPRIV, /* elevator private data attached */
- __REQ_FAILED, /* set if the request failed */
- __REQ_QUIET, /* don't worry about errors */
- __REQ_PREEMPT, /* set for "ide_preempt" requests */
- __REQ_ORDERED_COLOR, /* is before or after barrier */
- __REQ_RW_SYNC, /* request is sync (sync write or read) */
- __REQ_ALLOCED, /* request came from our alloc pool */
- __REQ_RW_META, /* metadata io request */
- __REQ_COPY_USER, /* contains copies of user pages */
- __REQ_INTEGRITY, /* integrity metadata has been remapped */
- __REQ_NOIDLE, /* Don't anticipate more IO after this one */
- __REQ_IO_STAT, /* account I/O stat */
- __REQ_MIXED_MERGE, /* merge of different types, fail separately */
- __REQ_NR_BITS, /* stops here */
-};
-
-#define REQ_RW (1 << __REQ_RW)
-#define REQ_FAILFAST_DEV (1 << __REQ_FAILFAST_DEV)
-#define REQ_FAILFAST_TRANSPORT (1 << __REQ_FAILFAST_TRANSPORT)
-#define REQ_FAILFAST_DRIVER (1 << __REQ_FAILFAST_DRIVER)
-#define REQ_DISCARD (1 << __REQ_DISCARD)
-#define REQ_SORTED (1 << __REQ_SORTED)
-#define REQ_SOFTBARRIER (1 << __REQ_SOFTBARRIER)
-#define REQ_HARDBARRIER (1 << __REQ_HARDBARRIER)
-#define REQ_FUA (1 << __REQ_FUA)
-#define REQ_NOMERGE (1 << __REQ_NOMERGE)
-#define REQ_STARTED (1 << __REQ_STARTED)
-#define REQ_DONTPREP (1 << __REQ_DONTPREP)
-#define REQ_QUEUED (1 << __REQ_QUEUED)
-#define REQ_ELVPRIV (1 << __REQ_ELVPRIV)
-#define REQ_FAILED (1 << __REQ_FAILED)
-#define REQ_QUIET (1 << __REQ_QUIET)
-#define REQ_PREEMPT (1 << __REQ_PREEMPT)
-#define REQ_ORDERED_COLOR (1 << __REQ_ORDERED_COLOR)
-#define REQ_RW_SYNC (1 << __REQ_RW_SYNC)
-#define REQ_ALLOCED (1 << __REQ_ALLOCED)
-#define REQ_RW_META (1 << __REQ_RW_META)
-#define REQ_COPY_USER (1 << __REQ_COPY_USER)
-#define REQ_INTEGRITY (1 << __REQ_INTEGRITY)
-#define REQ_NOIDLE (1 << __REQ_NOIDLE)
-#define REQ_IO_STAT (1 << __REQ_IO_STAT)
-#define REQ_MIXED_MERGE (1 << __REQ_MIXED_MERGE)
-
-#define REQ_FAILFAST_MASK (REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | \
- REQ_FAILFAST_DRIVER)
-
#define BLK_MAX_CDB 16
/*
typedef void (request_fn_proc) (struct request_queue *q);
typedef int (make_request_fn) (struct request_queue *q, struct bio *bio);
typedef int (prep_rq_fn) (struct request_queue *, struct request *);
+typedef void (unprep_rq_fn) (struct request_queue *, struct request *);
typedef void (unplug_fn) (struct request_queue *);
struct bio_vec;
};
typedef int (merge_bvec_fn) (struct request_queue *, struct bvec_merge_data *,
struct bio_vec *);
-typedef void (prepare_flush_fn) (struct request_queue *, struct request *);
typedef void (softirq_done_fn)(struct request *);
typedef int (dma_drain_needed_fn)(struct request *);
typedef int (lld_busy_fn) (struct request_queue *q);
request_fn_proc *request_fn;
make_request_fn *make_request_fn;
prep_rq_fn *prep_rq_fn;
+ unprep_rq_fn *unprep_rq_fn;
unplug_fn *unplug_fn;
merge_bvec_fn *merge_bvec_fn;
- prepare_flush_fn *prepare_flush_fn;
softirq_done_fn *softirq_done_fn;
rq_timed_out_fn *rq_timed_out_fn;
dma_drain_needed_fn *dma_drain_needed;
#define QUEUE_FLAG_IO_STAT 15 /* do IO stats */
#define QUEUE_FLAG_DISCARD 16 /* supports DISCARD */
#define QUEUE_FLAG_NOXMERGES 17 /* No extended merges */
+#define QUEUE_FLAG_ADD_RANDOM 18 /* Contributes to random pool */
#define QUEUE_FLAG_DEFAULT ((1 << QUEUE_FLAG_IO_STAT) | \
(1 << QUEUE_FLAG_CLUSTER) | \
(1 << QUEUE_FLAG_STACKABLE) | \
- (1 << QUEUE_FLAG_SAME_COMP))
+ (1 << QUEUE_FLAG_SAME_COMP) | \
+ (1 << QUEUE_FLAG_ADD_RANDOM))
static inline int queue_is_locked(struct request_queue *q)
{
test_bit(QUEUE_FLAG_NOXMERGES, &(q)->queue_flags)
#define blk_queue_nonrot(q) test_bit(QUEUE_FLAG_NONROT, &(q)->queue_flags)
#define blk_queue_io_stat(q) test_bit(QUEUE_FLAG_IO_STAT, &(q)->queue_flags)
+#define blk_queue_add_random(q) test_bit(QUEUE_FLAG_ADD_RANDOM, &(q)->queue_flags)
#define blk_queue_flushing(q) ((q)->ordseq)
#define blk_queue_stackable(q) \
test_bit(QUEUE_FLAG_STACKABLE, &(q)->queue_flags)
#define blk_queue_discard(q) test_bit(QUEUE_FLAG_DISCARD, &(q)->queue_flags)
-#define blk_fs_request(rq) ((rq)->cmd_type == REQ_TYPE_FS)
-#define blk_pc_request(rq) ((rq)->cmd_type == REQ_TYPE_BLOCK_PC)
-#define blk_special_request(rq) ((rq)->cmd_type == REQ_TYPE_SPECIAL)
-#define blk_sense_request(rq) ((rq)->cmd_type == REQ_TYPE_SENSE)
-
-#define blk_failfast_dev(rq) ((rq)->cmd_flags & REQ_FAILFAST_DEV)
-#define blk_failfast_transport(rq) ((rq)->cmd_flags & REQ_FAILFAST_TRANSPORT)
-#define blk_failfast_driver(rq) ((rq)->cmd_flags & REQ_FAILFAST_DRIVER)
-#define blk_noretry_request(rq) (blk_failfast_dev(rq) || \
- blk_failfast_transport(rq) || \
- blk_failfast_driver(rq))
-#define blk_rq_started(rq) ((rq)->cmd_flags & REQ_STARTED)
-#define blk_rq_io_stat(rq) ((rq)->cmd_flags & REQ_IO_STAT)
-#define blk_rq_quiet(rq) ((rq)->cmd_flags & REQ_QUIET)
-
-#define blk_account_rq(rq) (blk_rq_started(rq) && (blk_fs_request(rq) || blk_discard_rq(rq)))
-
-#define blk_pm_suspend_request(rq) ((rq)->cmd_type == REQ_TYPE_PM_SUSPEND)
-#define blk_pm_resume_request(rq) ((rq)->cmd_type == REQ_TYPE_PM_RESUME)
+#define blk_noretry_request(rq) \
+ ((rq)->cmd_flags & (REQ_FAILFAST_DEV|REQ_FAILFAST_TRANSPORT| \
+ REQ_FAILFAST_DRIVER))
+
+#define blk_account_rq(rq) \
+ (((rq)->cmd_flags & REQ_STARTED) && \
+ ((rq)->cmd_type == REQ_TYPE_FS || \
+ ((rq)->cmd_flags & REQ_DISCARD)))
+
#define blk_pm_request(rq) \
- (blk_pm_suspend_request(rq) || blk_pm_resume_request(rq))
+ ((rq)->cmd_type == REQ_TYPE_PM_SUSPEND || \
+ (rq)->cmd_type == REQ_TYPE_PM_RESUME)
#define blk_rq_cpu_valid(rq) ((rq)->cpu != -1)
-#define blk_sorted_rq(rq) ((rq)->cmd_flags & REQ_SORTED)
-#define blk_barrier_rq(rq) ((rq)->cmd_flags & REQ_HARDBARRIER)
-#define blk_fua_rq(rq) ((rq)->cmd_flags & REQ_FUA)
-#define blk_discard_rq(rq) ((rq)->cmd_flags & REQ_DISCARD)
#define blk_bidi_rq(rq) ((rq)->next_rq != NULL)
/* rq->queuelist of dequeued request must be list_empty() */
#define blk_queued_rq(rq) (!list_empty(&(rq)->queuelist))
*/
static inline bool rw_is_sync(unsigned int rw_flags)
{
- return !(rw_flags & REQ_RW) || (rw_flags & REQ_RW_SYNC);
+ return !(rw_flags & REQ_WRITE) || (rw_flags & REQ_SYNC);
}
static inline bool rq_is_sync(struct request *rq)
return rw_is_sync(rq->cmd_flags);
}
-#define rq_is_meta(rq) ((rq)->cmd_flags & REQ_RW_META)
-#define rq_noidle(rq) ((rq)->cmd_flags & REQ_NOIDLE)
-
static inline int blk_queue_full(struct request_queue *q, int sync)
{
if (sync)
(REQ_NOMERGE | REQ_STARTED | REQ_HARDBARRIER | REQ_SOFTBARRIER)
#define rq_mergeable(rq) \
(!((rq)->cmd_flags & RQ_NOMERGE_FLAGS) && \
- (blk_discard_rq(rq) || blk_fs_request((rq))))
+ (((rq)->cmd_flags & REQ_DISCARD) || \
+ (rq)->cmd_type == REQ_TYPE_FS))
/*
* q->prep_rq_fn return values
#define BLK_BOUNCE_HIGH -1ULL
#endif
#define BLK_BOUNCE_ANY (-1ULL)
-#define BLK_BOUNCE_ISA (ISA_DMA_THRESHOLD)
+#define BLK_BOUNCE_ISA (DMA_BIT_MASK(24))
/*
* default timeout for SG_IO if none specified
gfp_t);
extern void blk_insert_request(struct request_queue *, struct request *, int, void *);
extern void blk_requeue_request(struct request_queue *, struct request *);
+extern void blk_add_request_payload(struct request *rq, struct page *page,
+ unsigned int len);
extern int blk_rq_check_limits(struct request_queue *q, struct request *rq);
extern int blk_lld_busy(struct request_queue *q);
extern int blk_rq_prep_clone(struct request *rq, struct request *rq_src,
extern void __blk_complete_request(struct request *);
extern void blk_abort_request(struct request *);
extern void blk_abort_queue(struct request_queue *);
+extern void blk_unprep_request(struct request *);
/*
* Access functions for manipulating queue properties
extern void blk_queue_lld_busy(struct request_queue *q, lld_busy_fn *fn);
extern void blk_queue_segment_boundary(struct request_queue *, unsigned long);
extern void blk_queue_prep_rq(struct request_queue *, prep_rq_fn *pfn);
+extern void blk_queue_unprep_rq(struct request_queue *, unprep_rq_fn *ufn);
extern void blk_queue_merge_bvec(struct request_queue *, merge_bvec_fn *);
extern void blk_queue_dma_alignment(struct request_queue *, int);
extern void blk_queue_update_dma_alignment(struct request_queue *, int);
extern void blk_queue_rq_timed_out(struct request_queue *, rq_timed_out_fn *);
extern void blk_queue_rq_timeout(struct request_queue *, unsigned int);
extern struct backing_dev_info *blk_get_backing_dev_info(struct block_device *bdev);
-extern int blk_queue_ordered(struct request_queue *, unsigned, prepare_flush_fn *);
+extern int blk_queue_ordered(struct request_queue *, unsigned);
extern bool blk_do_ordered(struct request_queue *, struct request **);
extern unsigned blk_ordered_cur_seq(struct request_queue *);
extern unsigned blk_ordered_req_seq(struct request *);
{
block <<= (sb->s_blocksize_bits - 9);
nr_blocks <<= (sb->s_blocksize_bits - 9);
- return blkdev_issue_discard(sb->s_bdev, block, nr_blocks, GFP_KERNEL,
+ return blkdev_issue_discard(sb->s_bdev, block, nr_blocks, GFP_NOFS,
BLKDEV_IFL_WAIT | BLKDEV_IFL_BARRIER);
}
struct block_device_operations {
int (*open) (struct block_device *, fmode_t);
int (*release) (struct gendisk *, fmode_t);
- int (*locked_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
int (*direct_access) (struct block_device *, sector_t,
#ifdef __KERNEL__
#include <linux/blkdev.h>
#include <linux/relay.h>
+#include <linux/compat.h>
#endif
/*
#endif /* CONFIG_BLK_DEV_IO_TRACE */
+#ifdef CONFIG_COMPAT
+
+struct compat_blk_user_trace_setup {
+ char name[32];
+ u16 act_mask;
+ u32 buf_size;
+ u32 buf_nr;
+ compat_u64 start_lba;
+ compat_u64 end_lba;
+ u32 pid;
+};
+#define BLKTRACESETUP32 _IOWR(0x12, 115, struct compat_blk_user_trace_setup)
+
+#endif
+
#if defined(CONFIG_EVENT_TRACING) && defined(CONFIG_BLOCK)
static inline int blk_cmd_buf_len(struct request *rq)
{
- return blk_pc_request(rq) ? rq->cmd_len * 3 : 1;
+ return (rq->cmd_type == REQ_TYPE_BLOCK_PC) ? rq->cmd_len * 3 : 1;
}
extern void blk_dump_cmd(char *buf, struct request *rq);
wait_queue_head_t uc_sleep; /* process' wait queue */
};
-#define REQ_ASYNC 0x1
-#define REQ_READ 0x2
-#define REQ_WRITE 0x4
-#define REQ_ABORT 0x8
+#define CODA_REQ_ASYNC 0x1
+#define CODA_REQ_READ 0x2
+#define CODA_REQ_WRITE 0x4
+#define CODA_REQ_ABORT 0x8
#endif
#define NPAR 16
struct vc_data {
+ struct tty_port port; /* Upper level data */
+
unsigned short vc_num; /* Console number */
unsigned int vc_cols; /* [#] Console size */
unsigned int vc_rows;
/* VT terminal data */
unsigned int vc_state; /* Escape sequence parser state */
unsigned int vc_npar,vc_par[NPAR]; /* Parameters of current escape sequence */
- struct tty_struct *vc_tty; /* TTY we are attached to */
/* data for manual vt switching */
struct vt_mode vt_mode;
struct pid *vt_pid;
struct vc_data **vc_display_fg; /* [!] Ptr to var holding fg console for this display */
unsigned long vc_uni_pagedir;
unsigned long *vc_uni_pagedir_loc; /* [!] Location of uni_pagedir variable for this console */
+ bool vc_panic_force_write; /* when oops/panic this VC can accept forced output/blanking */
/* additional information is in vt_kern.h */
};
return -EIO;
}
+static inline int dma_get_cache_alignment(void)
+{
+#ifdef ARCH_DMA_MINALIGN
+ return ARCH_DMA_MINALIGN;
+#endif
+ return 1;
+}
+
/* flags for the coherent memory api */
#define DMA_MEMORY_MAP 0x01
#define DMA_MEMORY_IO 0x02
extern const char *drbd_buildtag(void);
-#define REL_VERSION "8.3.8"
+#define REL_VERSION "8.3.8.1"
#define API_VERSION 88
#define PRO_VERSION_MIN 86
#define PRO_VERSION_MAX 94
NL_INTEGER( 30, T_MAY_IGNORE, rate)
NL_INTEGER( 31, T_MAY_IGNORE, after)
NL_INTEGER( 32, T_MAY_IGNORE, al_extents)
- NL_INTEGER( 71, T_MAY_IGNORE, dp_volume)
- NL_INTEGER( 72, T_MAY_IGNORE, dp_interval)
- NL_INTEGER( 73, T_MAY_IGNORE, throttle_th)
- NL_INTEGER( 74, T_MAY_IGNORE, hold_off_th)
+/* NL_INTEGER( 71, T_MAY_IGNORE, dp_volume)
+ * NL_INTEGER( 72, T_MAY_IGNORE, dp_interval)
+ * NL_INTEGER( 73, T_MAY_IGNORE, throttle_th)
+ * NL_INTEGER( 74, T_MAY_IGNORE, hold_off_th)
+ * feature will be reimplemented differently with 8.3.9 */
NL_STRING( 52, T_MAY_IGNORE, verify_alg, SHARED_SECRET_MAX)
NL_STRING( 51, T_MAY_IGNORE, cpu_mask, 32)
NL_STRING( 64, T_MAY_IGNORE, csums_alg, SHARED_SECRET_MAX)
*/
#define FBINFO_BE_MATH 0x100000
+/* report to the VT layer that this fb driver can accept forced console
+ output like oopses */
+#define FBINFO_CAN_FORCE_OUTPUT 0x200000
+
struct fb_info {
int node;
int flags;
#include <linux/limits.h>
#include <linux/ioctl.h>
+#include <linux/blk_types.h>
/*
* It's silly to have NR_OPEN bigger than NR_FILE, but you can change
#define FMODE_RANDOM ((__force fmode_t)0x1000)
/* File was opened by fanotify and shouldn't generate fanotify events */
-#define FMODE_NONOTIFY ((__force fmode_t)16777216) /* 0x1000000 */
+#define FMODE_NONOTIFY ((__force fmode_t)0x1000000)
/*
* The below are the various read and write types that we support. Some of
* immediately wait on this read without caring about
* unplugging.
* READA Used for read-ahead operations. Lower priority, and the
- * block layer could (in theory) choose to ignore this
+ * block layer could (in theory) choose to ignore this
* request if it runs into resource problems.
* WRITE A normal async write. Device will be plugged.
* SWRITE Like WRITE, but a special case for ll_rw_block() that
* SWRITE_SYNC
* SWRITE_SYNC_PLUG Like WRITE_SYNC/WRITE_SYNC_PLUG, but locks the buffer.
* See SWRITE.
- * WRITE_BARRIER Like WRITE, but tells the block layer that all
+ * WRITE_BARRIER Like WRITE_SYNC, but tells the block layer that all
* previously submitted writes must be safely on storage
* before this one is started. Also guarantees that when
* this write is complete, it itself is also safely on
* of this IO.
*
*/
-#define RW_MASK 1
-#define RWA_MASK 2
-#define READ 0
-#define WRITE 1
-#define READA 2 /* read-ahead - don't block if no resources */
-#define SWRITE 3 /* for ll_rw_block() - wait for buffer lock */
-#define READ_SYNC (READ | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG))
-#define READ_META (READ | (1 << BIO_RW_META))
-#define WRITE_SYNC_PLUG (WRITE | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_NOIDLE))
-#define WRITE_SYNC (WRITE_SYNC_PLUG | (1 << BIO_RW_UNPLUG))
-#define WRITE_ODIRECT_PLUG (WRITE | (1 << BIO_RW_SYNCIO))
-#define WRITE_META (WRITE | (1 << BIO_RW_META))
-#define SWRITE_SYNC_PLUG \
- (SWRITE | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_NOIDLE))
-#define SWRITE_SYNC (SWRITE_SYNC_PLUG | (1 << BIO_RW_UNPLUG))
-#define WRITE_BARRIER (WRITE | (1 << BIO_RW_BARRIER))
+#define RW_MASK REQ_WRITE
+#define RWA_MASK REQ_RAHEAD
+
+#define READ 0
+#define WRITE RW_MASK
+#define READA RWA_MASK
+#define SWRITE (WRITE | READA)
+
+#define READ_SYNC (READ | REQ_SYNC | REQ_UNPLUG)
+#define READ_META (READ | REQ_META)
+#define WRITE_SYNC_PLUG (WRITE | REQ_SYNC | REQ_NOIDLE)
+#define WRITE_SYNC (WRITE | REQ_SYNC | REQ_NOIDLE | REQ_UNPLUG)
+#define WRITE_ODIRECT_PLUG (WRITE | REQ_SYNC)
+#define WRITE_META (WRITE | REQ_META)
+#define WRITE_BARRIER (WRITE | REQ_SYNC | REQ_NOIDLE | REQ_UNPLUG | \
+ REQ_HARDBARRIER)
+#define SWRITE_SYNC_PLUG (SWRITE | REQ_SYNC | REQ_NOIDLE)
+#define SWRITE_SYNC (SWRITE | REQ_SYNC | REQ_NOIDLE | REQ_UNPLUG)
/*
* These aren't really reads or writes, they pass down information about
* parts of device that are now unused by the file system.
*/
-#define DISCARD_NOBARRIER (WRITE | (1 << BIO_RW_DISCARD))
-#define DISCARD_BARRIER (DISCARD_NOBARRIER | (1 << BIO_RW_BARRIER))
+#define DISCARD_NOBARRIER (WRITE | REQ_DISCARD)
+#define DISCARD_BARRIER (WRITE | REQ_DISCARD | REQ_HARDBARRIER)
#define SEL_IN 1
#define SEL_OUT 2
extern void file_move(struct file *f, struct list_head *list);
extern void file_kill(struct file *f);
#ifdef CONFIG_BLOCK
-struct bio;
extern void submit_bio(int, struct bio *);
extern int bdev_read_only(struct block_device *);
#endif
#endif
#ifdef CONFIG_BLOCK
-struct bio;
typedef void (dio_submit_t)(int rw, struct bio *bio, struct inode *inode,
loff_t file_offset);
struct gpio_keys_button *buttons;
int nbuttons;
unsigned int rep:1; /* enable input subsystem auto repeat */
+ int (*enable)(struct device *dev);
+ void (*disable)(struct device *dev);
};
#endif
* @shutdown: Callback for device shutdown
* @suspend: Callback for device suspend
* @resume: Callback for device resume
+ * @alert: Alert callback, for example for the SMBus alert protocol
* @command: Callback for bus-wide signaling (optional)
* @driver: Device driver model driver
* @id_table: List of I2C devices supported by this driver
* @addr: stored in i2c_client.addr
* @platform_data: stored in i2c_client.dev.platform_data
* @archdata: copied into i2c_client.dev.archdata
+ * @of_node: pointer to OpenFirmware device node
* @irq: stored in i2c_client.irq
*
* I2C doesn't actually support hardware probing, although controllers and
--- /dev/null
+/*
+ * Driver for the Semtech SX150x I2C GPIO Expanders
+ *
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+#ifndef __LINUX_I2C_SX150X_H
+#define __LINUX_I2C_SX150X_H
+
+/**
+ * struct sx150x_platform_data - config data for SX150x driver
+ * @gpio_base: The index number of the first GPIO assigned to this
+ * GPIO expander. The expander will create a block of
+ * consecutively numbered gpios beginning at the given base,
+ * with the size of the block depending on the model of the
+ * expander chip.
+ * @oscio_is_gpo: If set to true, the driver will configure OSCIO as a GPO
+ * instead of as an oscillator, increasing the size of the
+ * GP(I)O pool created by this expander by one. The
+ * output-only GPO pin will be added at the end of the block.
+ * @io_pullup_ena: A bit-mask which enables or disables the pull-up resistor
+ * for each IO line in the expander. Setting the bit at
+ * position n will enable the pull-up for the IO at
+ * the corresponding offset. For chips with fewer than
+ * 16 IO pins, high-end bits are ignored.
+ * @io_pulldn_ena: A bit-mask which enables-or disables the pull-down
+ * resistor for each IO line in the expander. Setting the
+ * bit at position n will enable the pull-down for the IO at
+ * the corresponding offset. For chips with fewer than
+ * 16 IO pins, high-end bits are ignored.
+ * @io_open_drain_ena: A bit-mask which enables-or disables open-drain
+ * operation for each IO line in the expander. Setting the
+ * bit at position n enables open-drain operation for
+ * the IO at the corresponding offset. Clearing the bit
+ * enables regular push-pull operation for that IO.
+ * For chips with fewer than 16 IO pins, high-end bits
+ * are ignored.
+ * @io_polarity: A bit-mask which enables polarity inversion for each IO line
+ * in the expander. Setting the bit at position n inverts
+ * the polarity of that IO line, while clearing it results
+ * in normal polarity. For chips with fewer than 16 IO pins,
+ * high-end bits are ignored.
+ * @irq_summary: The 'summary IRQ' line to which the GPIO expander's INT line
+ * is connected, via which it reports interrupt events
+ * across all GPIO lines. This must be a real,
+ * pre-existing IRQ line.
+ * Setting this value < 0 disables the irq_chip functionality
+ * of the driver.
+ * @irq_base: The first 'virtual IRQ' line at which our block of GPIO-based
+ * IRQ lines will appear. Similarly to gpio_base, the expander
+ * will create a block of irqs beginning at this number.
+ * This value is ignored if irq_summary is < 0.
+ */
+struct sx150x_platform_data {
+ unsigned gpio_base;
+ bool oscio_is_gpo;
+ u16 io_pullup_ena;
+ u16 io_pulldn_ena;
+ u16 io_open_drain_ena;
+ u16 io_polarity;
+ int irq_summary;
+ unsigned irq_base;
+};
+
+#endif /* __LINUX_I2C_SX150X_H */
#define REP_DELAY 0x00
#define REP_PERIOD 0x01
#define REP_MAX 0x01
+#define REP_CNT (REP_MAX+1)
/*
* Sounds
* @repeat_key: stores key code of the last key pressed; used to implement
* software autorepeat
* @timer: timer for software autorepeat
- * @abs: current values for reports from absolute axes
* @rep: current values for autorepeat parameters (delay, rate)
* @mt: pointer to array of struct input_mt_slot holding current values
* of tracked contacts
* @mtsize: number of MT slots the device uses
* @slot: MT slot currently being transmitted
+ * @absinfo: array of &struct absinfo elements holding information
+ * about absolute axes (current value, min, max, flat, fuzz,
+ * resolution)
* @key: reflects current state of device's keys/buttons
* @led: reflects current state of device's LEDs
* @snd: reflects current state of sound effects
* @sw: reflects current state of device's switches
- * @absmax: maximum values for events coming from absolute axes
- * @absmin: minimum values for events coming from absolute axes
- * @absfuzz: describes noisiness for axes
- * @absflat: size of the center flat position (used by joydev)
- * @absres: resolution used for events coming form absolute axes
* @open: this method is called when the very first user calls
* input_open_device(). The driver must prepare the device
* to start generating events (start polling thread,
unsigned int repeat_key;
struct timer_list timer;
- int abs[ABS_CNT];
- int rep[REP_MAX + 1];
+ int rep[REP_CNT];
struct input_mt_slot *mt;
int mtsize;
int slot;
+ struct input_absinfo *absinfo;
+
unsigned long key[BITS_TO_LONGS(KEY_CNT)];
unsigned long led[BITS_TO_LONGS(LED_CNT)];
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
unsigned long sw[BITS_TO_LONGS(SW_CNT)];
- int absmax[ABS_CNT];
- int absmin[ABS_CNT];
- int absfuzz[ABS_CNT];
- int absflat[ABS_CNT];
- int absres[ABS_CNT];
-
int (*open)(struct input_dev *dev);
void (*close)(struct input_dev *dev);
int (*flush)(struct input_dev *dev, struct file *file);
dev->hint_events_per_packet = n_events;
}
-static inline void input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat)
-{
- dev->absmin[axis] = min;
- dev->absmax[axis] = max;
- dev->absfuzz[axis] = fuzz;
- dev->absflat[axis] = flat;
-
- dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis);
+void input_alloc_absinfo(struct input_dev *dev);
+void input_set_abs_params(struct input_dev *dev, unsigned int axis,
+ int min, int max, int fuzz, int flat);
+
+#define INPUT_GENERATE_ABS_ACCESSORS(_suffix, _item) \
+static inline int input_abs_get_##_suffix(struct input_dev *dev, \
+ unsigned int axis) \
+{ \
+ return dev->absinfo ? dev->absinfo[axis]._item : 0; \
+} \
+ \
+static inline void input_abs_set_##_suffix(struct input_dev *dev, \
+ unsigned int axis, int val) \
+{ \
+ input_alloc_absinfo(dev); \
+ if (dev->absinfo) \
+ dev->absinfo[axis]._item = val; \
}
+INPUT_GENERATE_ABS_ACCESSORS(val, value)
+INPUT_GENERATE_ABS_ACCESSORS(min, minimum)
+INPUT_GENERATE_ABS_ACCESSORS(max, maximum)
+INPUT_GENERATE_ABS_ACCESSORS(fuzz, fuzz)
+INPUT_GENERATE_ABS_ACCESSORS(flat, flat)
+INPUT_GENERATE_ABS_ACCESSORS(res, resolution)
+
int input_get_keycode(struct input_dev *dev,
unsigned int scancode, unsigned int *keycode);
int input_set_keycode(struct input_dev *dev,
unsigned long magic;
unsigned int brdnr;
unsigned int brdtype;
- unsigned int state;
+ unsigned long state;
unsigned int nrpanels;
unsigned int nrports;
unsigned int nrdevs;
};
extern struct atomic_notifier_head panic_notifier_list;
-extern long (*panic_blink)(long time);
+extern long (*panic_blink)(int state);
NORET_TYPE void panic(const char * fmt, ...)
__attribute__ ((NORET_AND format (printf, 1, 2))) __cold;
extern void oops_enter(void);
extern void oops_exit(void);
+void print_oops_end_marker(void);
extern int oops_may_print(void);
NORET_TYPE void do_exit(long error_code)
ATTRIB_NORET;
/*
- * A generic kernel FIFO implementation.
+ * A generic kernel FIFO implementation
*
- * Copyright (C) 2009 Stefani Seibold <stefani@seibold.net>
- * Copyright (C) 2004 Stelian Pop <stelian@popies.net>
+ * Copyright (C) 2009/2010 Stefani Seibold <stefani@seibold.net>
*
* 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
*
*/
+#ifndef _LINUX_KFIFO_H
+#define _LINUX_KFIFO_H
+
/*
- * Howto porting drivers to the new generic fifo API:
+ * How to porting drivers to the new generic FIFO API:
*
* - Modify the declaration of the "struct kfifo *" object into a
* in-place "struct kfifo" object
* passed as the first argument to this functions
* - Replace the use of __kfifo_put into kfifo_in and __kfifo_get
* into kfifo_out
- * - Replace the use of kfifo_put into kfifo_in_locked and kfifo_get
- * into kfifo_out_locked
+ * - Replace the use of kfifo_put into kfifo_in_spinlocked and kfifo_get
+ * into kfifo_out_spinlocked
* Note: the spinlock pointer formerly passed to kfifo_init/kfifo_alloc
- * must be passed now to the kfifo_in_locked and kfifo_out_locked
- * as the last parameter.
- * - All formerly name __kfifo_* functions has been renamed into kfifo_*
+ * must be passed now to the kfifo_in_spinlocked and kfifo_out_spinlocked
+ * as the last parameter
+ * - The formerly __kfifo_* functions are renamed into kfifo_*
*/
-#ifndef _LINUX_KFIFO_H
-#define _LINUX_KFIFO_H
+/*
+ * Note about locking : There is no locking required until only * one reader
+ * and one writer is using the fifo and no kfifo_reset() will be * called
+ * kfifo_reset_out() can be safely used, until it will be only called
+ * in the reader thread.
+ * For multiple writer and one reader there is only a need to lock the writer.
+ * And vice versa for only one writer and multiple reader there is only a need
+ * to lock the reader.
+ */
#include <linux/kernel.h>
#include <linux/spinlock.h>
-
-struct kfifo {
- unsigned char *buffer; /* the buffer holding the data */
- unsigned int size; /* the size of the allocated buffer */
- unsigned int in; /* data is added at offset (in % size) */
- unsigned int out; /* data is extracted from off. (out % size) */
+#include <linux/stddef.h>
+#include <linux/scatterlist.h>
+
+struct __kfifo {
+ unsigned int in;
+ unsigned int out;
+ unsigned int mask;
+ unsigned int esize;
+ void *data;
};
-/*
- * Macros for declaration and initialization of the kfifo datatype
- */
-
-/* helper macro */
-#define __kfifo_initializer(s, b) \
- (struct kfifo) { \
- .size = s, \
- .in = 0, \
- .out = 0, \
- .buffer = b \
+#define __STRUCT_KFIFO_COMMON(datatype, recsize, ptrtype) \
+ union { \
+ struct __kfifo kfifo; \
+ datatype *type; \
+ char (*rectype)[recsize]; \
+ ptrtype *ptr; \
+ const ptrtype *ptr_const; \
}
-/**
- * DECLARE_KFIFO - macro to declare a kfifo and the associated buffer
- * @name: name of the declared kfifo datatype
- * @size: size of the fifo buffer. Must be a power of two.
- *
- * Note1: the macro can be used inside struct or union declaration
- * Note2: the macro creates two objects:
- * A kfifo object with the given name and a buffer for the kfifo
- * object named name##kfifo_buffer
- */
-#define DECLARE_KFIFO(name, size) \
-union { \
- struct kfifo name; \
- unsigned char name##kfifo_buffer[size + sizeof(struct kfifo)]; \
+#define __STRUCT_KFIFO(type, size, recsize, ptrtype) \
+{ \
+ __STRUCT_KFIFO_COMMON(type, recsize, ptrtype); \
+ type buf[((size < 2) || (size & (size - 1))) ? -1 : size]; \
}
-/**
- * INIT_KFIFO - Initialize a kfifo declared by DECLARE_KFIFO
- * @name: name of the declared kfifo datatype
+#define STRUCT_KFIFO(type, size) \
+ struct __STRUCT_KFIFO(type, size, 0, type)
+
+#define __STRUCT_KFIFO_PTR(type, recsize, ptrtype) \
+{ \
+ __STRUCT_KFIFO_COMMON(type, recsize, ptrtype); \
+ type buf[0]; \
+}
+
+#define STRUCT_KFIFO_PTR(type) \
+ struct __STRUCT_KFIFO_PTR(type, 0, type)
+
+/*
+ * define compatibility "struct kfifo" for dynamic allocated fifos
*/
-#define INIT_KFIFO(name) \
- name = __kfifo_initializer(sizeof(name##kfifo_buffer) - \
- sizeof(struct kfifo), \
- name##kfifo_buffer + sizeof(struct kfifo))
+struct kfifo __STRUCT_KFIFO_PTR(unsigned char, 0, void);
-/**
- * DEFINE_KFIFO - macro to define and initialize a kfifo
- * @name: name of the declared kfifo datatype
- * @size: size of the fifo buffer. Must be a power of two.
- *
- * Note1: the macro can be used for global and local kfifo data type variables
- * Note2: the macro creates two objects:
- * A kfifo object with the given name and a buffer for the kfifo
- * object named name##kfifo_buffer
+#define STRUCT_KFIFO_REC_1(size) \
+ struct __STRUCT_KFIFO(unsigned char, size, 1, void)
+
+#define STRUCT_KFIFO_REC_2(size) \
+ struct __STRUCT_KFIFO(unsigned char, size, 2, void)
+
+/*
+ * define kfifo_rec types
*/
-#define DEFINE_KFIFO(name, size) \
- unsigned char name##kfifo_buffer[size]; \
- struct kfifo name = __kfifo_initializer(size, name##kfifo_buffer)
+struct kfifo_rec_ptr_1 __STRUCT_KFIFO_PTR(unsigned char, 1, void);
+struct kfifo_rec_ptr_2 __STRUCT_KFIFO_PTR(unsigned char, 2, void);
-extern void kfifo_init(struct kfifo *fifo, void *buffer,
- unsigned int size);
-extern __must_check int kfifo_alloc(struct kfifo *fifo, unsigned int size,
- gfp_t gfp_mask);
-extern void kfifo_free(struct kfifo *fifo);
-extern unsigned int kfifo_in(struct kfifo *fifo,
- const void *from, unsigned int len);
-extern __must_check unsigned int kfifo_out(struct kfifo *fifo,
- void *to, unsigned int len);
-extern __must_check unsigned int kfifo_out_peek(struct kfifo *fifo,
- void *to, unsigned int len, unsigned offset);
+/*
+ * helper macro to distinguish between real in place fifo where the fifo
+ * array is a part of the structure and the fifo type where the array is
+ * outside of the fifo structure.
+ */
+#define __is_kfifo_ptr(fifo) (sizeof(*fifo) == sizeof(struct __kfifo))
/**
- * kfifo_initialized - Check if kfifo is initialized.
- * @fifo: fifo to check
- * Return %true if FIFO is initialized, otherwise %false.
- * Assumes the fifo was 0 before.
+ * DECLARE_KFIFO_PTR - macro to declare a fifo pointer object
+ * @fifo: name of the declared fifo
+ * @type: type of the fifo elements
*/
-static inline bool kfifo_initialized(struct kfifo *fifo)
-{
- return fifo->buffer != NULL;
-}
+#define DECLARE_KFIFO_PTR(fifo, type) STRUCT_KFIFO_PTR(type) fifo
/**
- * kfifo_reset - removes the entire FIFO contents
- * @fifo: the fifo to be emptied.
+ * DECLARE_KFIFO - macro to declare a fifo object
+ * @fifo: name of the declared fifo
+ * @type: type of the fifo elements
+ * @size: the number of elements in the fifo, this must be a power of 2
*/
-static inline void kfifo_reset(struct kfifo *fifo)
-{
- fifo->in = fifo->out = 0;
-}
+#define DECLARE_KFIFO(fifo, type, size) STRUCT_KFIFO(type, size) fifo
/**
- * kfifo_reset_out - skip FIFO contents
- * @fifo: the fifo to be emptied.
- */
-static inline void kfifo_reset_out(struct kfifo *fifo)
-{
- smp_mb();
- fifo->out = fifo->in;
-}
+ * INIT_KFIFO - Initialize a fifo declared by DECLARE_KFIFO
+ * @fifo: name of the declared fifo datatype
+ */
+#define INIT_KFIFO(fifo) \
+(void)({ \
+ typeof(&(fifo)) __tmp = &(fifo); \
+ struct __kfifo *__kfifo = &__tmp->kfifo; \
+ __kfifo->in = 0; \
+ __kfifo->out = 0; \
+ __kfifo->mask = __is_kfifo_ptr(__tmp) ? 0 : ARRAY_SIZE(__tmp->buf) - 1;\
+ __kfifo->esize = sizeof(*__tmp->buf); \
+ __kfifo->data = __is_kfifo_ptr(__tmp) ? NULL : __tmp->buf; \
+})
/**
- * kfifo_size - returns the size of the fifo in bytes
- * @fifo: the fifo to be used.
- */
-static inline __must_check unsigned int kfifo_size(struct kfifo *fifo)
+ * DEFINE_KFIFO - macro to define and initialize a fifo
+ * @fifo: name of the declared fifo datatype
+ * @type: type of the fifo elements
+ * @size: the number of elements in the fifo, this must be a power of 2
+ *
+ * Note: the macro can be used for global and local fifo data type variables.
+ */
+#define DEFINE_KFIFO(fifo, type, size) \
+ DECLARE_KFIFO(fifo, type, size) = \
+ (typeof(fifo)) { \
+ { \
+ { \
+ .in = 0, \
+ .out = 0, \
+ .mask = __is_kfifo_ptr(&(fifo)) ? \
+ 0 : \
+ ARRAY_SIZE((fifo).buf) - 1, \
+ .esize = sizeof(*(fifo).buf), \
+ .data = __is_kfifo_ptr(&(fifo)) ? \
+ NULL : \
+ (fifo).buf, \
+ } \
+ } \
+ }
+
+
+static inline unsigned int __must_check
+__kfifo_must_check_helper(unsigned int val)
{
- return fifo->size;
+ return val;
}
/**
- * kfifo_len - returns the number of used bytes in the FIFO
- * @fifo: the fifo to be used.
+ * kfifo_initialized - Check if the fifo is initialized
+ * @fifo: address of the fifo to check
+ *
+ * Return %true if fifo is initialized, otherwise %false.
+ * Assumes the fifo was 0 before.
*/
-static inline unsigned int kfifo_len(struct kfifo *fifo)
-{
- register unsigned int out;
-
- out = fifo->out;
- smp_rmb();
- return fifo->in - out;
-}
+#define kfifo_initialized(fifo) ((fifo)->kfifo.mask)
/**
- * kfifo_is_empty - returns true if the fifo is empty
- * @fifo: the fifo to be used.
+ * kfifo_esize - returns the size of the element managed by the fifo
+ * @fifo: address of the fifo to be used
*/
-static inline __must_check int kfifo_is_empty(struct kfifo *fifo)
-{
- return fifo->in == fifo->out;
-}
+#define kfifo_esize(fifo) ((fifo)->kfifo.esize)
/**
- * kfifo_is_full - returns true if the fifo is full
- * @fifo: the fifo to be used.
+ * kfifo_recsize - returns the size of the record length field
+ * @fifo: address of the fifo to be used
*/
-static inline __must_check int kfifo_is_full(struct kfifo *fifo)
-{
- return kfifo_len(fifo) == kfifo_size(fifo);
-}
+#define kfifo_recsize(fifo) (sizeof(*(fifo)->rectype))
/**
- * kfifo_avail - returns the number of bytes available in the FIFO
- * @fifo: the fifo to be used.
+ * kfifo_size - returns the size of the fifo in elements
+ * @fifo: address of the fifo to be used
*/
-static inline __must_check unsigned int kfifo_avail(struct kfifo *fifo)
-{
- return kfifo_size(fifo) - kfifo_len(fifo);
-}
+#define kfifo_size(fifo) ((fifo)->kfifo.mask + 1)
/**
- * kfifo_in_locked - puts some data into the FIFO using a spinlock for locking
- * @fifo: the fifo to be used.
- * @from: the data to be added.
- * @n: the length of the data to be added.
- * @lock: pointer to the spinlock to use for locking.
+ * kfifo_reset - removes the entire fifo content
+ * @fifo: address of the fifo to be used
*
- * This function copies at most @n bytes from the @from buffer into
- * the FIFO depending on the free space, and returns the number of
- * bytes copied.
+ * Note: usage of kfifo_reset() is dangerous. It should be only called when the
+ * fifo is exclusived locked or when it is secured that no other thread is
+ * accessing the fifo.
*/
-static inline unsigned int kfifo_in_locked(struct kfifo *fifo,
- const void *from, unsigned int n, spinlock_t *lock)
-{
- unsigned long flags;
- unsigned int ret;
-
- spin_lock_irqsave(lock, flags);
-
- ret = kfifo_in(fifo, from, n);
-
- spin_unlock_irqrestore(lock, flags);
-
- return ret;
-}
+#define kfifo_reset(fifo) \
+(void)({ \
+ typeof(fifo + 1) __tmp = (fifo); \
+ __tmp->kfifo.in = __tmp->kfifo.out = 0; \
+})
/**
- * kfifo_out_locked - gets some data from the FIFO using a spinlock for locking
- * @fifo: the fifo to be used.
- * @to: where the data must be copied.
- * @n: the size of the destination buffer.
- * @lock: pointer to the spinlock to use for locking.
+ * kfifo_reset_out - skip fifo content
+ * @fifo: address of the fifo to be used
*
- * This function copies at most @n bytes from the FIFO into the
- * @to buffer and returns the number of copied bytes.
- */
-static inline __must_check unsigned int kfifo_out_locked(struct kfifo *fifo,
- void *to, unsigned int n, spinlock_t *lock)
-{
- unsigned long flags;
- unsigned int ret;
-
- spin_lock_irqsave(lock, flags);
-
- ret = kfifo_out(fifo, to, n);
-
- spin_unlock_irqrestore(lock, flags);
-
- return ret;
-}
-
-extern void kfifo_skip(struct kfifo *fifo, unsigned int len);
-
-extern __must_check int kfifo_from_user(struct kfifo *fifo,
- const void __user *from, unsigned int n, unsigned *lenout);
-
-extern __must_check int kfifo_to_user(struct kfifo *fifo,
- void __user *to, unsigned int n, unsigned *lenout);
-
-/*
- * __kfifo_add_out internal helper function for updating the out offset
+ * Note: The usage of kfifo_reset_out() is safe until it will be only called
+ * from the reader thread and there is only one concurrent reader. Otherwise
+ * it is dangerous and must be handled in the same way as kfifo_reset().
*/
-static inline void __kfifo_add_out(struct kfifo *fifo,
- unsigned int off)
-{
- smp_mb();
- fifo->out += off;
-}
+#define kfifo_reset_out(fifo) \
+(void)({ \
+ typeof(fifo + 1) __tmp = (fifo); \
+ __tmp->kfifo.out = __tmp->kfifo.in; \
+})
-/*
- * __kfifo_add_in internal helper function for updating the in offset
+/**
+ * kfifo_len - returns the number of used elements in the fifo
+ * @fifo: address of the fifo to be used
*/
-static inline void __kfifo_add_in(struct kfifo *fifo,
- unsigned int off)
-{
- smp_wmb();
- fifo->in += off;
-}
+#define kfifo_len(fifo) \
+({ \
+ typeof(fifo + 1) __tmpl = (fifo); \
+ __tmpl->kfifo.in - __tmpl->kfifo.out; \
+})
-/*
- * __kfifo_off internal helper function for calculating the index of a
- * given offeset
+/**
+ * kfifo_is_empty - returns true if the fifo is empty
+ * @fifo: address of the fifo to be used
*/
-static inline unsigned int __kfifo_off(struct kfifo *fifo, unsigned int off)
-{
- return off & (fifo->size - 1);
-}
+#define kfifo_is_empty(fifo) \
+({ \
+ typeof(fifo + 1) __tmpq = (fifo); \
+ __tmpq->kfifo.in == __tmpq->kfifo.out; \
+})
-/*
- * __kfifo_peek_n internal helper function for determinate the length of
- * the next record in the fifo
+/**
+ * kfifo_is_full - returns true if the fifo is full
+ * @fifo: address of the fifo to be used
*/
-static inline unsigned int __kfifo_peek_n(struct kfifo *fifo,
- unsigned int recsize)
-{
-#define __KFIFO_GET(fifo, off, shift) \
- ((fifo)->buffer[__kfifo_off((fifo), (fifo)->out+(off))] << (shift))
+#define kfifo_is_full(fifo) \
+({ \
+ typeof(fifo + 1) __tmpq = (fifo); \
+ kfifo_len(__tmpq) > __tmpq->kfifo.mask; \
+})
- unsigned int l;
+/**
+ * kfifo_avail - returns the number of unused elements in the fifo
+ * @fifo: address of the fifo to be used
+ */
+#define kfifo_avail(fifo) \
+__kfifo_must_check_helper( \
+({ \
+ typeof(fifo + 1) __tmpq = (fifo); \
+ const size_t __recsize = sizeof(*__tmpq->rectype); \
+ unsigned int __avail = kfifo_size(__tmpq) - kfifo_len(__tmpq); \
+ (__recsize) ? ((__avail <= __recsize) ? 0 : \
+ __kfifo_max_r(__avail - __recsize, __recsize)) : \
+ __avail; \
+}) \
+)
- l = __KFIFO_GET(fifo, 0, 0);
+/**
+ * kfifo_skip - skip output data
+ * @fifo: address of the fifo to be used
+ */
+#define kfifo_skip(fifo) \
+(void)({ \
+ typeof(fifo + 1) __tmp = (fifo); \
+ const size_t __recsize = sizeof(*__tmp->rectype); \
+ struct __kfifo *__kfifo = &__tmp->kfifo; \
+ if (__recsize) \
+ __kfifo_skip_r(__kfifo, __recsize); \
+ else \
+ __kfifo->out++; \
+})
- if (--recsize)
- l |= __KFIFO_GET(fifo, 1, 8);
+/**
+ * kfifo_peek_len - gets the size of the next fifo record
+ * @fifo: address of the fifo to be used
+ *
+ * This function returns the size of the next fifo record in number of bytes.
+ */
+#define kfifo_peek_len(fifo) \
+__kfifo_must_check_helper( \
+({ \
+ typeof(fifo + 1) __tmp = (fifo); \
+ const size_t __recsize = sizeof(*__tmp->rectype); \
+ struct __kfifo *__kfifo = &__tmp->kfifo; \
+ (!__recsize) ? kfifo_len(__tmp) * sizeof(*__tmp->type) : \
+ __kfifo_len_r(__kfifo, __recsize); \
+}) \
+)
- return l;
-#undef __KFIFO_GET
-}
+/**
+ * kfifo_alloc - dynamically allocates a new fifo buffer
+ * @fifo: pointer to the fifo
+ * @size: the number of elements in the fifo, this must be a power of 2
+ * @gfp_mask: get_free_pages mask, passed to kmalloc()
+ *
+ * This macro dynamically allocates a new fifo buffer.
+ *
+ * The numer of elements will be rounded-up to a power of 2.
+ * The fifo will be release with kfifo_free().
+ * Return 0 if no error, otherwise an error code.
+ */
+#define kfifo_alloc(fifo, size, gfp_mask) \
+__kfifo_must_check_helper( \
+({ \
+ typeof(fifo + 1) __tmp = (fifo); \
+ struct __kfifo *__kfifo = &__tmp->kfifo; \
+ __is_kfifo_ptr(__tmp) ? \
+ __kfifo_alloc(__kfifo, size, sizeof(*__tmp->type), gfp_mask) : \
+ -EINVAL; \
+}) \
+)
-/*
- * __kfifo_poke_n internal helper function for storing the length of
- * the next record into the fifo
+/**
+ * kfifo_free - frees the fifo
+ * @fifo: the fifo to be freed
*/
-static inline void __kfifo_poke_n(struct kfifo *fifo,
- unsigned int recsize, unsigned int n)
-{
-#define __KFIFO_PUT(fifo, off, val, shift) \
- ( \
- (fifo)->buffer[__kfifo_off((fifo), (fifo)->in+(off))] = \
- (unsigned char)((val) >> (shift)) \
- )
+#define kfifo_free(fifo) \
+({ \
+ typeof(fifo + 1) __tmp = (fifo); \
+ struct __kfifo *__kfifo = &__tmp->kfifo; \
+ if (__is_kfifo_ptr(__tmp)) \
+ __kfifo_free(__kfifo); \
+})
- __KFIFO_PUT(fifo, 0, n, 0);
+/**
+ * kfifo_init - initialize a fifo using a preallocated buffer
+ * @fifo: the fifo to assign the buffer
+ * @buffer: the preallocated buffer to be used
+ * @size: the size of the internal buffer, this have to be a power of 2
+ *
+ * This macro initialize a fifo using a preallocated buffer.
+ *
+ * The numer of elements will be rounded-up to a power of 2.
+ * Return 0 if no error, otherwise an error code.
+ */
+#define kfifo_init(fifo, buffer, size) \
+({ \
+ typeof(fifo + 1) __tmp = (fifo); \
+ struct __kfifo *__kfifo = &__tmp->kfifo; \
+ __is_kfifo_ptr(__tmp) ? \
+ __kfifo_init(__kfifo, buffer, size, sizeof(*__tmp->type)) : \
+ -EINVAL; \
+})
- if (--recsize)
- __KFIFO_PUT(fifo, 1, n, 8);
-#undef __KFIFO_PUT
-}
+/**
+ * kfifo_put - put data into the fifo
+ * @fifo: address of the fifo to be used
+ * @val: the data to be added
+ *
+ * This macro copies the given value into the fifo.
+ * It returns 0 if the fifo was full. Otherwise it returns the number
+ * processed elements.
+ *
+ * Note that with only one concurrent reader and one concurrent
+ * writer, you don't need extra locking to use these macro.
+ */
+#define kfifo_put(fifo, val) \
+({ \
+ typeof(fifo + 1) __tmp = (fifo); \
+ typeof(val + 1) __val = (val); \
+ unsigned int __ret; \
+ const size_t __recsize = sizeof(*__tmp->rectype); \
+ struct __kfifo *__kfifo = &__tmp->kfifo; \
+ if (0) { \
+ typeof(__tmp->ptr_const) __dummy __attribute__ ((unused)); \
+ __dummy = (typeof(__val))NULL; \
+ } \
+ if (__recsize) \
+ __ret = __kfifo_in_r(__kfifo, __val, sizeof(*__val), \
+ __recsize); \
+ else { \
+ __ret = !kfifo_is_full(__tmp); \
+ if (__ret) { \
+ (__is_kfifo_ptr(__tmp) ? \
+ ((typeof(__tmp->type))__kfifo->data) : \
+ (__tmp->buf) \
+ )[__kfifo->in & __tmp->kfifo.mask] = \
+ *(typeof(__tmp->type))__val; \
+ smp_wmb(); \
+ __kfifo->in++; \
+ } \
+ } \
+ __ret; \
+})
-/*
- * __kfifo_in_... internal functions for put date into the fifo
- * do not call it directly, use kfifo_in_rec() instead
- */
-extern unsigned int __kfifo_in_n(struct kfifo *fifo,
- const void *from, unsigned int n, unsigned int recsize);
+/**
+ * kfifo_get - get data from the fifo
+ * @fifo: address of the fifo to be used
+ * @val: the var where to store the data to be added
+ *
+ * This macro reads the data from the fifo.
+ * It returns 0 if the fifo was empty. Otherwise it returns the number
+ * processed elements.
+ *
+ * Note that with only one concurrent reader and one concurrent
+ * writer, you don't need extra locking to use these macro.
+ */
+#define kfifo_get(fifo, val) \
+__kfifo_must_check_helper( \
+({ \
+ typeof(fifo + 1) __tmp = (fifo); \
+ typeof(val + 1) __val = (val); \
+ unsigned int __ret; \
+ const size_t __recsize = sizeof(*__tmp->rectype); \
+ struct __kfifo *__kfifo = &__tmp->kfifo; \
+ if (0) \
+ __val = (typeof(__tmp->ptr))0; \
+ if (__recsize) \
+ __ret = __kfifo_out_r(__kfifo, __val, sizeof(*__val), \
+ __recsize); \
+ else { \
+ __ret = !kfifo_is_empty(__tmp); \
+ if (__ret) { \
+ *(typeof(__tmp->type))__val = \
+ (__is_kfifo_ptr(__tmp) ? \
+ ((typeof(__tmp->type))__kfifo->data) : \
+ (__tmp->buf) \
+ )[__kfifo->out & __tmp->kfifo.mask]; \
+ smp_wmb(); \
+ __kfifo->out++; \
+ } \
+ } \
+ __ret; \
+}) \
+)
-extern unsigned int __kfifo_in_generic(struct kfifo *fifo,
- const void *from, unsigned int n, unsigned int recsize);
+/**
+ * kfifo_peek - get data from the fifo without removing
+ * @fifo: address of the fifo to be used
+ * @val: the var where to store the data to be added
+ *
+ * This reads the data from the fifo without removing it from the fifo.
+ * It returns 0 if the fifo was empty. Otherwise it returns the number
+ * processed elements.
+ *
+ * Note that with only one concurrent reader and one concurrent
+ * writer, you don't need extra locking to use these macro.
+ */
+#define kfifo_peek(fifo, val) \
+__kfifo_must_check_helper( \
+({ \
+ typeof(fifo + 1) __tmp = (fifo); \
+ typeof(val + 1) __val = (val); \
+ unsigned int __ret; \
+ const size_t __recsize = sizeof(*__tmp->rectype); \
+ struct __kfifo *__kfifo = &__tmp->kfifo; \
+ if (0) \
+ __val = (typeof(__tmp->ptr))NULL; \
+ if (__recsize) \
+ __ret = __kfifo_out_peek_r(__kfifo, __val, sizeof(*__val), \
+ __recsize); \
+ else { \
+ __ret = !kfifo_is_empty(__tmp); \
+ if (__ret) { \
+ *(typeof(__tmp->type))__val = \
+ (__is_kfifo_ptr(__tmp) ? \
+ ((typeof(__tmp->type))__kfifo->data) : \
+ (__tmp->buf) \
+ )[__kfifo->out & __tmp->kfifo.mask]; \
+ smp_wmb(); \
+ } \
+ } \
+ __ret; \
+}) \
+)
-static inline unsigned int __kfifo_in_rec(struct kfifo *fifo,
- const void *from, unsigned int n, unsigned int recsize)
-{
- unsigned int ret;
+/**
+ * kfifo_in - put data into the fifo
+ * @fifo: address of the fifo to be used
+ * @buf: the data to be added
+ * @n: number of elements to be added
+ *
+ * This macro copies the given buffer into the fifo and returns the
+ * number of copied elements.
+ *
+ * Note that with only one concurrent reader and one concurrent
+ * writer, you don't need extra locking to use these macro.
+ */
+#define kfifo_in(fifo, buf, n) \
+({ \
+ typeof(fifo + 1) __tmp = (fifo); \
+ typeof(buf + 1) __buf = (buf); \
+ unsigned long __n = (n); \
+ const size_t __recsize = sizeof(*__tmp->rectype); \
+ struct __kfifo *__kfifo = &__tmp->kfifo; \
+ if (0) { \
+ typeof(__tmp->ptr_const) __dummy __attribute__ ((unused)); \
+ __dummy = (typeof(__buf))NULL; \
+ } \
+ (__recsize) ?\
+ __kfifo_in_r(__kfifo, __buf, __n, __recsize) : \
+ __kfifo_in(__kfifo, __buf, __n); \
+})
- ret = __kfifo_in_n(fifo, from, n, recsize);
+/**
+ * kfifo_in_spinlocked - put data into the fifo using a spinlock for locking
+ * @fifo: address of the fifo to be used
+ * @buf: the data to be added
+ * @n: number of elements to be added
+ * @lock: pointer to the spinlock to use for locking
+ *
+ * This macro copies the given values buffer into the fifo and returns the
+ * number of copied elements.
+ */
+#define kfifo_in_spinlocked(fifo, buf, n, lock) \
+({ \
+ unsigned long __flags; \
+ unsigned int __ret; \
+ spin_lock_irqsave(lock, __flags); \
+ __ret = kfifo_in(fifo, buf, n); \
+ spin_unlock_irqrestore(lock, __flags); \
+ __ret; \
+})
+
+/* alias for kfifo_in_spinlocked, will be removed in a future release */
+#define kfifo_in_locked(fifo, buf, n, lock) \
+ kfifo_in_spinlocked(fifo, buf, n, lock)
- if (likely(ret == 0)) {
- if (recsize)
- __kfifo_poke_n(fifo, recsize, n);
- __kfifo_add_in(fifo, n + recsize);
- }
- return ret;
-}
+/**
+ * kfifo_out - get data from the fifo
+ * @fifo: address of the fifo to be used
+ * @buf: pointer to the storage buffer
+ * @n: max. number of elements to get
+ *
+ * This macro get some data from the fifo and return the numbers of elements
+ * copied.
+ *
+ * Note that with only one concurrent reader and one concurrent
+ * writer, you don't need extra locking to use these macro.
+ */
+#define kfifo_out(fifo, buf, n) \
+__kfifo_must_check_helper( \
+({ \
+ typeof(fifo + 1) __tmp = (fifo); \
+ typeof(buf + 1) __buf = (buf); \
+ unsigned long __n = (n); \
+ const size_t __recsize = sizeof(*__tmp->rectype); \
+ struct __kfifo *__kfifo = &__tmp->kfifo; \
+ if (0) { \
+ typeof(__tmp->ptr) __dummy = NULL; \
+ __buf = __dummy; \
+ } \
+ (__recsize) ?\
+ __kfifo_out_r(__kfifo, __buf, __n, __recsize) : \
+ __kfifo_out(__kfifo, __buf, __n); \
+}) \
+)
+
+/**
+ * kfifo_out_spinlocked - get data from the fifo using a spinlock for locking
+ * @fifo: address of the fifo to be used
+ * @buf: pointer to the storage buffer
+ * @n: max. number of elements to get
+ * @lock: pointer to the spinlock to use for locking
+ *
+ * This macro get the data from the fifo and return the numbers of elements
+ * copied.
+ */
+#define kfifo_out_spinlocked(fifo, buf, n, lock) \
+__kfifo_must_check_helper( \
+({ \
+ unsigned long __flags; \
+ unsigned int __ret; \
+ spin_lock_irqsave(lock, __flags); \
+ __ret = kfifo_out(fifo, buf, n); \
+ spin_unlock_irqrestore(lock, __flags); \
+ __ret; \
+}) \
+)
+
+/* alias for kfifo_out_spinlocked, will be removed in a future release */
+#define kfifo_out_locked(fifo, buf, n, lock) \
+ kfifo_out_spinlocked(fifo, buf, n, lock)
/**
- * kfifo_in_rec - puts some record data into the FIFO
- * @fifo: the fifo to be used.
- * @from: the data to be added.
- * @n: the length of the data to be added.
- * @recsize: size of record field
+ * kfifo_from_user - puts some data from user space into the fifo
+ * @fifo: address of the fifo to be used
+ * @from: pointer to the data to be added
+ * @len: the length of the data to be added
+ * @copied: pointer to output variable to store the number of copied bytes
*
- * This function copies @n bytes from the @from into the FIFO and returns
- * the number of bytes which cannot be copied.
- * A returned value greater than the @n value means that the record doesn't
- * fit into the buffer.
+ * This macro copies at most @len bytes from the @from into the
+ * fifo, depending of the available space and returns -EFAULT/0.
*
* Note that with only one concurrent reader and one concurrent
- * writer, you don't need extra locking to use these functions.
- */
-static inline __must_check unsigned int kfifo_in_rec(struct kfifo *fifo,
- void *from, unsigned int n, unsigned int recsize)
-{
- if (!__builtin_constant_p(recsize))
- return __kfifo_in_generic(fifo, from, n, recsize);
- return __kfifo_in_rec(fifo, from, n, recsize);
-}
+ * writer, you don't need extra locking to use these macro.
+ */
+#define kfifo_from_user(fifo, from, len, copied) \
+__kfifo_must_check_helper( \
+({ \
+ typeof(fifo + 1) __tmp = (fifo); \
+ const void __user *__from = (from); \
+ unsigned int __len = (len); \
+ unsigned int *__copied = (copied); \
+ const size_t __recsize = sizeof(*__tmp->rectype); \
+ struct __kfifo *__kfifo = &__tmp->kfifo; \
+ (__recsize) ? \
+ __kfifo_from_user_r(__kfifo, __from, __len, __copied, __recsize) : \
+ __kfifo_from_user(__kfifo, __from, __len, __copied); \
+}) \
+)
-/*
- * __kfifo_out_... internal functions for get date from the fifo
- * do not call it directly, use kfifo_out_rec() instead
- */
-extern unsigned int __kfifo_out_n(struct kfifo *fifo,
- void *to, unsigned int reclen, unsigned int recsize);
+/**
+ * kfifo_to_user - copies data from the fifo into user space
+ * @fifo: address of the fifo to be used
+ * @to: where the data must be copied
+ * @len: the size of the destination buffer
+ * @copied: pointer to output variable to store the number of copied bytes
+ *
+ * This macro copies at most @len bytes from the fifo into the
+ * @to buffer and returns -EFAULT/0.
+ *
+ * Note that with only one concurrent reader and one concurrent
+ * writer, you don't need extra locking to use these macro.
+ */
+#define kfifo_to_user(fifo, to, len, copied) \
+__kfifo_must_check_helper( \
+({ \
+ typeof(fifo + 1) __tmp = (fifo); \
+ void __user *__to = (to); \
+ unsigned int __len = (len); \
+ unsigned int *__copied = (copied); \
+ const size_t __recsize = sizeof(*__tmp->rectype); \
+ struct __kfifo *__kfifo = &__tmp->kfifo; \
+ (__recsize) ? \
+ __kfifo_to_user_r(__kfifo, __to, __len, __copied, __recsize) : \
+ __kfifo_to_user(__kfifo, __to, __len, __copied); \
+}) \
+)
+
+/**
+ * kfifo_dma_in_prepare - setup a scatterlist for DMA input
+ * @fifo: address of the fifo to be used
+ * @sgl: pointer to the scatterlist array
+ * @nents: number of entries in the scatterlist array
+ * @len: number of elements to transfer
+ *
+ * This macro fills a scatterlist for DMA input.
+ * It returns the number entries in the scatterlist array.
+ *
+ * Note that with only one concurrent reader and one concurrent
+ * writer, you don't need extra locking to use these macros.
+ */
+#define kfifo_dma_in_prepare(fifo, sgl, nents, len) \
+({ \
+ typeof(fifo + 1) __tmp = (fifo); \
+ struct scatterlist *__sgl = (sgl); \
+ int __nents = (nents); \
+ unsigned int __len = (len); \
+ const size_t __recsize = sizeof(*__tmp->rectype); \
+ struct __kfifo *__kfifo = &__tmp->kfifo; \
+ (__recsize) ? \
+ __kfifo_dma_in_prepare_r(__kfifo, __sgl, __nents, __len, __recsize) : \
+ __kfifo_dma_in_prepare(__kfifo, __sgl, __nents, __len); \
+})
-extern unsigned int __kfifo_out_generic(struct kfifo *fifo,
- void *to, unsigned int n,
- unsigned int recsize, unsigned int *total);
+/**
+ * kfifo_dma_in_finish - finish a DMA IN operation
+ * @fifo: address of the fifo to be used
+ * @len: number of bytes to received
+ *
+ * This macro finish a DMA IN operation. The in counter will be updated by
+ * the len parameter. No error checking will be done.
+ *
+ * Note that with only one concurrent reader and one concurrent
+ * writer, you don't need extra locking to use these macros.
+ */
+#define kfifo_dma_in_finish(fifo, len) \
+(void)({ \
+ typeof(fifo + 1) __tmp = (fifo); \
+ unsigned int __len = (len); \
+ const size_t __recsize = sizeof(*__tmp->rectype); \
+ struct __kfifo *__kfifo = &__tmp->kfifo; \
+ if (__recsize) \
+ __kfifo_dma_in_finish_r(__kfifo, __len, __recsize); \
+ else \
+ __kfifo->in += __len / sizeof(*__tmp->type); \
+})
-static inline unsigned int __kfifo_out_rec(struct kfifo *fifo,
- void *to, unsigned int n, unsigned int recsize,
- unsigned int *total)
-{
- unsigned int l;
-
- if (!recsize) {
- l = n;
- if (total)
- *total = l;
- } else {
- l = __kfifo_peek_n(fifo, recsize);
- if (total)
- *total = l;
- if (n < l)
- return l;
- }
+/**
+ * kfifo_dma_out_prepare - setup a scatterlist for DMA output
+ * @fifo: address of the fifo to be used
+ * @sgl: pointer to the scatterlist array
+ * @nents: number of entries in the scatterlist array
+ * @len: number of elements to transfer
+ *
+ * This macro fills a scatterlist for DMA output which at most @len bytes
+ * to transfer.
+ * It returns the number entries in the scatterlist array.
+ * A zero means there is no space available and the scatterlist is not filled.
+ *
+ * Note that with only one concurrent reader and one concurrent
+ * writer, you don't need extra locking to use these macros.
+ */
+#define kfifo_dma_out_prepare(fifo, sgl, nents, len) \
+({ \
+ typeof(fifo + 1) __tmp = (fifo); \
+ struct scatterlist *__sgl = (sgl); \
+ int __nents = (nents); \
+ unsigned int __len = (len); \
+ const size_t __recsize = sizeof(*__tmp->rectype); \
+ struct __kfifo *__kfifo = &__tmp->kfifo; \
+ (__recsize) ? \
+ __kfifo_dma_out_prepare_r(__kfifo, __sgl, __nents, __len, __recsize) : \
+ __kfifo_dma_out_prepare(__kfifo, __sgl, __nents, __len); \
+})
- return __kfifo_out_n(fifo, to, l, recsize);
-}
+/**
+ * kfifo_dma_out_finish - finish a DMA OUT operation
+ * @fifo: address of the fifo to be used
+ * @len: number of bytes transferd
+ *
+ * This macro finish a DMA OUT operation. The out counter will be updated by
+ * the len parameter. No error checking will be done.
+ *
+ * Note that with only one concurrent reader and one concurrent
+ * writer, you don't need extra locking to use these macros.
+ */
+#define kfifo_dma_out_finish(fifo, len) \
+(void)({ \
+ typeof(fifo + 1) __tmp = (fifo); \
+ unsigned int __len = (len); \
+ const size_t __recsize = sizeof(*__tmp->rectype); \
+ struct __kfifo *__kfifo = &__tmp->kfifo; \
+ if (__recsize) \
+ __kfifo_dma_out_finish_r(__kfifo, __recsize); \
+ else \
+ __kfifo->out += __len / sizeof(*__tmp->type); \
+})
/**
- * kfifo_out_rec - gets some record data from the FIFO
- * @fifo: the fifo to be used.
- * @to: where the data must be copied.
- * @n: the size of the destination buffer.
- * @recsize: size of record field
- * @total: pointer where the total number of to copied bytes should stored
+ * kfifo_out_peek - gets some data from the fifo
+ * @fifo: address of the fifo to be used
+ * @buf: pointer to the storage buffer
+ * @n: max. number of elements to get
*
- * This function copies at most @n bytes from the FIFO to @to and returns the
- * number of bytes which cannot be copied.
- * A returned value greater than the @n value means that the record doesn't
- * fit into the @to buffer.
+ * This macro get the data from the fifo and return the numbers of elements
+ * copied. The data is not removed from the fifo.
*
* Note that with only one concurrent reader and one concurrent
- * writer, you don't need extra locking to use these functions.
+ * writer, you don't need extra locking to use these macro.
*/
-static inline __must_check unsigned int kfifo_out_rec(struct kfifo *fifo,
- void *to, unsigned int n, unsigned int recsize,
- unsigned int *total)
+#define kfifo_out_peek(fifo, buf, n) \
+__kfifo_must_check_helper( \
+({ \
+ typeof(fifo + 1) __tmp = (fifo); \
+ typeof(buf + 1) __buf = (buf); \
+ unsigned long __n = (n); \
+ const size_t __recsize = sizeof(*__tmp->rectype); \
+ struct __kfifo *__kfifo = &__tmp->kfifo; \
+ if (0) { \
+ typeof(__tmp->ptr) __dummy __attribute__ ((unused)) = NULL; \
+ __buf = __dummy; \
+ } \
+ (__recsize) ? \
+ __kfifo_out_peek_r(__kfifo, __buf, __n, __recsize) : \
+ __kfifo_out_peek(__kfifo, __buf, __n); \
+}) \
+)
-{
- if (!__builtin_constant_p(recsize))
- return __kfifo_out_generic(fifo, to, n, recsize, total);
- return __kfifo_out_rec(fifo, to, n, recsize, total);
-}
+extern int __kfifo_alloc(struct __kfifo *fifo, unsigned int size,
+ size_t esize, gfp_t gfp_mask);
-/*
- * __kfifo_from_user_... internal functions for transfer from user space into
- * the fifo. do not call it directly, use kfifo_from_user_rec() instead
- */
-extern unsigned int __kfifo_from_user_n(struct kfifo *fifo,
- const void __user *from, unsigned int n, unsigned int recsize);
+extern void __kfifo_free(struct __kfifo *fifo);
-extern unsigned int __kfifo_from_user_generic(struct kfifo *fifo,
- const void __user *from, unsigned int n, unsigned int recsize);
+extern int __kfifo_init(struct __kfifo *fifo, void *buffer,
+ unsigned int size, size_t esize);
-static inline unsigned int __kfifo_from_user_rec(struct kfifo *fifo,
- const void __user *from, unsigned int n, unsigned int recsize)
-{
- unsigned int ret;
+extern unsigned int __kfifo_in(struct __kfifo *fifo,
+ const void *buf, unsigned int len);
- ret = __kfifo_from_user_n(fifo, from, n, recsize);
+extern unsigned int __kfifo_out(struct __kfifo *fifo,
+ void *buf, unsigned int len);
- if (likely(ret == 0)) {
- if (recsize)
- __kfifo_poke_n(fifo, recsize, n);
- __kfifo_add_in(fifo, n + recsize);
- }
- return ret;
-}
+extern int __kfifo_from_user(struct __kfifo *fifo,
+ const void __user *from, unsigned long len, unsigned int *copied);
-/**
- * kfifo_from_user_rec - puts some data from user space into the FIFO
- * @fifo: the fifo to be used.
- * @from: pointer to the data to be added.
- * @n: the length of the data to be added.
- * @recsize: size of record field
- *
- * This function copies @n bytes from the @from into the
- * FIFO and returns the number of bytes which cannot be copied.
- *
- * If the returned value is equal or less the @n value, the copy_from_user()
- * functions has failed. Otherwise the record doesn't fit into the buffer.
- *
- * Note that with only one concurrent reader and one concurrent
- * writer, you don't need extra locking to use these functions.
- */
-static inline __must_check unsigned int kfifo_from_user_rec(struct kfifo *fifo,
- const void __user *from, unsigned int n, unsigned int recsize)
-{
- if (!__builtin_constant_p(recsize))
- return __kfifo_from_user_generic(fifo, from, n, recsize);
- return __kfifo_from_user_rec(fifo, from, n, recsize);
-}
+extern int __kfifo_to_user(struct __kfifo *fifo,
+ void __user *to, unsigned long len, unsigned int *copied);
-/*
- * __kfifo_to_user_... internal functions for transfer fifo data into user space
- * do not call it directly, use kfifo_to_user_rec() instead
- */
-extern unsigned int __kfifo_to_user_n(struct kfifo *fifo,
- void __user *to, unsigned int n, unsigned int reclen,
- unsigned int recsize);
+extern unsigned int __kfifo_dma_in_prepare(struct __kfifo *fifo,
+ struct scatterlist *sgl, int nents, unsigned int len);
-extern unsigned int __kfifo_to_user_generic(struct kfifo *fifo,
- void __user *to, unsigned int n, unsigned int recsize,
- unsigned int *total);
+extern unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo,
+ struct scatterlist *sgl, int nents, unsigned int len);
-static inline unsigned int __kfifo_to_user_rec(struct kfifo *fifo,
- void __user *to, unsigned int n,
- unsigned int recsize, unsigned int *total)
-{
- unsigned int l;
-
- if (!recsize) {
- l = n;
- if (total)
- *total = l;
- } else {
- l = __kfifo_peek_n(fifo, recsize);
- if (total)
- *total = l;
- if (n < l)
- return l;
- }
+extern unsigned int __kfifo_out_peek(struct __kfifo *fifo,
+ void *buf, unsigned int len);
- return __kfifo_to_user_n(fifo, to, n, l, recsize);
-}
+extern unsigned int __kfifo_in_r(struct __kfifo *fifo,
+ const void *buf, unsigned int len, size_t recsize);
-/**
- * kfifo_to_user_rec - gets data from the FIFO and write it to user space
- * @fifo: the fifo to be used.
- * @to: where the data must be copied.
- * @n: the size of the destination buffer.
- * @recsize: size of record field
- * @total: pointer where the total number of to copied bytes should stored
- *
- * This function copies at most @n bytes from the FIFO to the @to.
- * In case of an error, the function returns the number of bytes which cannot
- * be copied.
- * If the returned value is equal or less the @n value, the copy_to_user()
- * functions has failed. Otherwise the record doesn't fit into the @to buffer.
- *
- * Note that with only one concurrent reader and one concurrent
- * writer, you don't need extra locking to use these functions.
- */
-static inline __must_check unsigned int kfifo_to_user_rec(struct kfifo *fifo,
- void __user *to, unsigned int n, unsigned int recsize,
- unsigned int *total)
-{
- if (!__builtin_constant_p(recsize))
- return __kfifo_to_user_generic(fifo, to, n, recsize, total);
- return __kfifo_to_user_rec(fifo, to, n, recsize, total);
-}
+extern unsigned int __kfifo_out_r(struct __kfifo *fifo,
+ void *buf, unsigned int len, size_t recsize);
-/*
- * __kfifo_peek_... internal functions for peek into the next fifo record
- * do not call it directly, use kfifo_peek_rec() instead
- */
-extern unsigned int __kfifo_peek_generic(struct kfifo *fifo,
- unsigned int recsize);
+extern int __kfifo_from_user_r(struct __kfifo *fifo,
+ const void __user *from, unsigned long len, unsigned int *copied,
+ size_t recsize);
-/**
- * kfifo_peek_rec - gets the size of the next FIFO record data
- * @fifo: the fifo to be used.
- * @recsize: size of record field
- *
- * This function returns the size of the next FIFO record in number of bytes
- */
-static inline __must_check unsigned int kfifo_peek_rec(struct kfifo *fifo,
- unsigned int recsize)
-{
- if (!__builtin_constant_p(recsize))
- return __kfifo_peek_generic(fifo, recsize);
- if (!recsize)
- return kfifo_len(fifo);
- return __kfifo_peek_n(fifo, recsize);
-}
+extern int __kfifo_to_user_r(struct __kfifo *fifo, void __user *to,
+ unsigned long len, unsigned int *copied, size_t recsize);
-/*
- * __kfifo_skip_... internal functions for skip the next fifo record
- * do not call it directly, use kfifo_skip_rec() instead
- */
-extern void __kfifo_skip_generic(struct kfifo *fifo, unsigned int recsize);
+extern unsigned int __kfifo_dma_in_prepare_r(struct __kfifo *fifo,
+ struct scatterlist *sgl, int nents, unsigned int len, size_t recsize);
-static inline void __kfifo_skip_rec(struct kfifo *fifo,
- unsigned int recsize)
-{
- unsigned int l;
+extern void __kfifo_dma_in_finish_r(struct __kfifo *fifo,
+ unsigned int len, size_t recsize);
- if (recsize) {
- l = __kfifo_peek_n(fifo, recsize);
+extern unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo,
+ struct scatterlist *sgl, int nents, unsigned int len, size_t recsize);
- if (l + recsize <= kfifo_len(fifo)) {
- __kfifo_add_out(fifo, l + recsize);
- return;
- }
- }
- kfifo_reset_out(fifo);
-}
+extern void __kfifo_dma_out_finish_r(struct __kfifo *fifo, size_t recsize);
-/**
- * kfifo_skip_rec - skip the next fifo out record
- * @fifo: the fifo to be used.
- * @recsize: size of record field
- *
- * This function skips the next FIFO record
- */
-static inline void kfifo_skip_rec(struct kfifo *fifo,
- unsigned int recsize)
-{
- if (!__builtin_constant_p(recsize))
- __kfifo_skip_generic(fifo, recsize);
- else
- __kfifo_skip_rec(fifo, recsize);
-}
+extern unsigned int __kfifo_len_r(struct __kfifo *fifo, size_t recsize);
-/**
- * kfifo_avail_rec - returns the number of bytes available in a record FIFO
- * @fifo: the fifo to be used.
- * @recsize: size of record field
- */
-static inline __must_check unsigned int kfifo_avail_rec(struct kfifo *fifo,
- unsigned int recsize)
-{
- unsigned int l = kfifo_size(fifo) - kfifo_len(fifo);
+extern unsigned int __kfifo_out_peek_r(struct __kfifo *fifo,
+ void *buf, unsigned int len, size_t recsize);
- return (l > recsize) ? l - recsize : 0;
-}
+extern unsigned int __kfifo_max_r(unsigned int len, size_t recsize);
#endif
void mem_cgroup_update_file_mapped(struct page *page, int val);
unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order,
- gfp_t gfp_mask, int nid,
- int zid);
+ gfp_t gfp_mask);
u64 mem_cgroup_get_limit(struct mem_cgroup *mem);
#else /* CONFIG_CGROUP_MEM_RES_CTLR */
static inline
unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order,
- gfp_t gfp_mask, int nid, int zid)
+ gfp_t gfp_mask)
{
return 0;
}
};
struct mmc_csd {
+ unsigned char structure;
unsigned char mmca_vsn;
unsigned short cmdclass;
unsigned short tacc_clks;
#define MMC_TYPE_MMC 0 /* MMC card */
#define MMC_TYPE_SD 1 /* SD card */
#define MMC_TYPE_SDIO 2 /* SDIO card */
+#define MMC_TYPE_SD_COMBO 3 /* SD combo (IO+mem) card */
unsigned int state; /* (our) card state */
#define MMC_STATE_PRESENT (1<<0) /* present in sysfs */
#define MMC_STATE_READONLY (1<<1) /* card is read-only */
#define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */
#define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */
/* for byte mode */
+#define MMC_QUIRK_NONSTD_SDIO (1<<2) /* non-standard SDIO card attached */
+ /* (missing CIA registers) */
u32 raw_cid[4]; /* raw card CID */
u32 raw_csd[4]; /* raw card CSD */
unsigned int f_min;
unsigned int f_max;
u32 ocr_avail;
+ struct notifier_block pm_notify;
#define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */
#define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */
/* Only used with MMC_CAP_DISABLE */
int enabled; /* host is enabled */
+ int rescan_disable; /* disable card detection */
int nesting_cnt; /* "enable" nesting count */
int en_dis_recurs; /* detect recursion */
unsigned int disable_delay; /* disable delay in msecs */
int mmc_host_enable(struct mmc_host *host);
int mmc_host_disable(struct mmc_host *host);
int mmc_host_lazy_disable(struct mmc_host *host);
+int mmc_pm_notify(struct notifier_block *notify_block, unsigned long, void *);
static inline void mmc_set_disable_delay(struct mmc_host *host,
unsigned int disable_delay)
#define EXT_CSD_BUS_WIDTH 183 /* R/W */
#define EXT_CSD_HS_TIMING 185 /* R/W */
#define EXT_CSD_CARD_TYPE 196 /* RO */
+#define EXT_CSD_STRUCTURE 194 /* RO */
#define EXT_CSD_REV 192 /* RO */
#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
#define EXT_CSD_S_A_TIMEOUT 217
extern unsigned long badness(struct task_struct *p, struct mem_cgroup *mem,
const nodemask_t *nodemask, unsigned long uptime);
+extern struct task_struct *find_lock_task_mm(struct task_struct *p);
+
/* sysctls */
extern int sysctl_oom_dump_tasks;
extern int sysctl_oom_kill_allocating_task;
#define PCI_VENDOR_ID_AKS 0x416c
#define PCI_DEVICE_ID_AKS_ALADDINCARD 0x0100
+#define PCI_VENDOR_ID_ACCESSIO 0x494f
+#define PCI_DEVICE_ID_ACCESSIO_WDG_CSM 0x22c0
+
#define PCI_VENDOR_ID_S3 0x5333
#define PCI_DEVICE_ID_S3_TRIO 0x8811
#define PCI_DEVICE_ID_S3_868 0x8880
#define PORT_16654 11
#define PORT_16850 12
#define PORT_RSA 13 /* RSA-DV II/S card */
-#define PORT_MAX 13
+#define PORT_U6_16550A 14
+#define PORT_MAX 14
#define SERIAL_IO_PORT 0
#define SERIAL_IO_HUB6 1
#define ASYNC_BUGGY_UART (1U << ASYNCB_BUGGY_UART)
#define ASYNC_AUTOPROBE (1U << ASYNCB_AUTOPROBE)
-#define ASYNC_FLAGS ((1U << ASYNCB_LAST_USER) - 1)
+#define ASYNC_FLAGS ((1U << (ASYNCB_LAST_USER + 1)) - 1)
#define ASYNC_USR_MASK (ASYNC_SPD_HI|ASYNC_SPD_VHI| \
ASYNC_CALLOUT_NOHUP|ASYNC_SPD_SHI|ASYNC_LOW_LATENCY)
#define ASYNC_SPD_CUST (ASYNC_SPD_HI|ASYNC_SPD_VHI)
#define SER_RS485_ENABLED (1 << 0)
#define SER_RS485_RTS_ON_SEND (1 << 1)
#define SER_RS485_RTS_AFTER_SEND (1 << 2)
+#define SER_RS485_RTS_BEFORE_SEND (1 << 3)
__u32 delay_rts_before_send; /* Milliseconds */
- __u32 padding[6]; /* Memory is cheap, new structs
+ __u32 delay_rts_after_send; /* Milliseconds */
+ __u32 padding[5]; /* Memory is cheap, new structs
are a royal PITA .. */
};
unsigned int type; /* If UPF_FIXED_TYPE */
unsigned int (*serial_in)(struct uart_port *, int);
void (*serial_out)(struct uart_port *, int, int);
+ void (*set_termios)(struct uart_port *,
+ struct ktermios *new,
+ struct ktermios *old);
};
/*
extern int serial8250_find_port(struct uart_port *p);
extern int serial8250_find_port_for_earlycon(void);
extern int setup_early_serial8250_console(char *cmdline);
+extern void serial8250_do_set_termios(struct uart_port *port,
+ struct ktermios *termios, struct ktermios *old);
#endif
#define PORT_ALTERA_JTAGUART 91
#define PORT_ALTERA_UART 92
+/* SH-SCI */
+#define PORT_SCIFB 93
+
+/* MAX3107 */
+#define PORT_MAX3107 94
+
+/* High Speed UART for Medfield */
+#define PORT_MFD 95
+
#ifdef __KERNEL__
#include <linux/compiler.h>
void (*flush_buffer)(struct uart_port *);
void (*set_termios)(struct uart_port *, struct ktermios *new,
struct ktermios *old);
- void (*set_ldisc)(struct uart_port *);
+ void (*set_ldisc)(struct uart_port *, int new);
void (*pm)(struct uart_port *, unsigned int state,
unsigned int oldstate);
int (*set_wake)(struct uart_port *, unsigned int state);
unsigned char __iomem *membase; /* read/write[bwl] */
unsigned int (*serial_in)(struct uart_port *, int);
void (*serial_out)(struct uart_port *, int, int);
+ void (*set_termios)(struct uart_port *,
+ struct ktermios *new,
+ struct ktermios *old);
unsigned int irq; /* irq number */
unsigned long irqflags; /* irq flags */
unsigned int uartclk; /* base uart clock */
--- /dev/null
+#ifndef _SERIAL_MFD_H_
+#define _SERIAL_MFD_H_
+
+/* HW register offset definition */
+#define UART_FOR 0x08
+#define UART_PS 0x0C
+#define UART_MUL 0x0D
+#define UART_DIV 0x0E
+
+#define HSU_GBL_IEN 0x0
+#define HSU_GBL_IST 0x4
+
+#define HSU_GBL_INT_BIT_PORT0 0x0
+#define HSU_GBL_INT_BIT_PORT1 0x1
+#define HSU_GBL_INT_BIT_PORT2 0x2
+#define HSU_GBL_INT_BIT_IRI 0x3
+#define HSU_GBL_INT_BIT_HDLC 0x4
+#define HSU_GBL_INT_BIT_DMA 0x5
+
+#define HSU_GBL_ISR 0x8
+#define HSU_GBL_DMASR 0x400
+#define HSU_GBL_DMAISR 0x404
+
+#define HSU_PORT_REG_OFFSET 0x80
+#define HSU_PORT0_REG_OFFSET 0x80
+#define HSU_PORT1_REG_OFFSET 0x100
+#define HSU_PORT2_REG_OFFSET 0x180
+#define HSU_PORT_REG_LENGTH 0x80
+
+#define HSU_DMA_CHANS_REG_OFFSET 0x500
+#define HSU_DMA_CHANS_REG_LENGTH 0x40
+
+#define HSU_CH_SR 0x0 /* channel status reg */
+#define HSU_CH_CR 0x4 /* control reg */
+#define HSU_CH_DCR 0x8 /* descriptor control reg */
+#define HSU_CH_BSR 0x10 /* max fifo buffer size reg */
+#define HSU_CH_MOTSR 0x14 /* minimum ocp transfer size */
+#define HSU_CH_D0SAR 0x20 /* desc 0 start addr */
+#define HSU_CH_D0TSR 0x24 /* desc 0 transfer size */
+#define HSU_CH_D1SAR 0x28
+#define HSU_CH_D1TSR 0x2C
+#define HSU_CH_D2SAR 0x30
+#define HSU_CH_D2TSR 0x34
+#define HSU_CH_D3SAR 0x38
+#define HSU_CH_D3TSR 0x3C
+
+#endif
#define UART_FCR_PXAR16 0x80 /* receive FIFO threshold = 16 */
#define UART_FCR_PXAR32 0xc0 /* receive FIFO threshold = 32 */
+/*
+ * Intel MID on-chip HSU (High Speed UART) defined bits
+ */
+#define UART_FCR_HSU_64_1B 0x00 /* receive FIFO treshold = 1 */
+#define UART_FCR_HSU_64_16B 0x40 /* receive FIFO treshold = 16 */
+#define UART_FCR_HSU_64_32B 0x80 /* receive FIFO treshold = 32 */
+#define UART_FCR_HSU_64_56B 0xc0 /* receive FIFO treshold = 56 */
+
+#define UART_FCR_HSU_16_1B 0x00 /* receive FIFO treshold = 1 */
+#define UART_FCR_HSU_16_4B 0x40 /* receive FIFO treshold = 4 */
+#define UART_FCR_HSU_16_8B 0x80 /* receive FIFO treshold = 8 */
+#define UART_FCR_HSU_16_14B 0xc0 /* receive FIFO treshold = 14 */
+#define UART_FCR_HSU_64B_FIFO 0x20 /* chose 64 bytes FIFO */
+#define UART_FCR_HSU_16B_FIFO 0x00 /* chose 16 bytes FIFO */
+#define UART_FCR_HALF_EMPT_TXI 0x00 /* trigger TX_EMPT IRQ for half empty */
+#define UART_FCR_FULL_EMPT_TXI 0x08 /* trigger TX_EMPT IRQ for full empty */
/*
* These register definitions are for the 16C950
int id;
struct clk *parent;
+ struct clk **parent_table; /* list of parents to */
+ unsigned short parent_num; /* choose between */
+ unsigned char src_shift; /* source clock field in the */
+ unsigned char src_width; /* configuration register */
struct clk_ops *ops;
struct list_head children;
int sh_clk_div4_reparent_register(struct clk *clks, int nr,
struct clk_div4_table *table);
-#define SH_CLK_DIV6(_parent, _reg, _flags) \
-{ \
- .parent = _parent, \
- .enable_reg = (void __iomem *)_reg, \
- .flags = _flags, \
+#define SH_CLK_DIV6_EXT(_parent, _reg, _flags, _parents, \
+ _num_parents, _src_shift, _src_width) \
+{ \
+ .parent = _parent, \
+ .enable_reg = (void __iomem *)_reg, \
+ .flags = _flags, \
+ .parent_table = _parents, \
+ .parent_num = _num_parents, \
+ .src_shift = _src_shift, \
+ .src_width = _src_width, \
}
+#define SH_CLK_DIV6(_parent, _reg, _flags) \
+ SH_CLK_DIV6_EXT(_parent, _reg, _flags, NULL, 0, 0, 0)
+
int sh_clk_div6_register(struct clk *clks, int nr);
+int sh_clk_div6_reparent_register(struct clk *clks, int nr);
#endif /* __SH_CLOCK_H */
#include <trace/events/kmem.h>
-#ifndef ARCH_KMALLOC_MINALIGN
/*
* Enforce a minimum alignment for the kmalloc caches.
* Usually, the kmalloc caches are cache_line_size() aligned, except when
* ARCH_KMALLOC_MINALIGN allows that.
* Note that increasing this value may disable some debug features.
*/
+#ifdef ARCH_DMA_MINALIGN
+#define ARCH_KMALLOC_MINALIGN ARCH_DMA_MINALIGN
+#else
#define ARCH_KMALLOC_MINALIGN __alignof__(unsigned long long)
#endif
#ifndef __LINUX_SLOB_DEF_H
#define __LINUX_SLOB_DEF_H
-#ifndef ARCH_KMALLOC_MINALIGN
+#ifdef ARCH_DMA_MINALIGN
+#define ARCH_KMALLOC_MINALIGN ARCH_DMA_MINALIGN
+#else
#define ARCH_KMALLOC_MINALIGN __alignof__(unsigned long)
#endif
/*
* Kmalloc subsystem.
*/
-#if defined(ARCH_KMALLOC_MINALIGN) && ARCH_KMALLOC_MINALIGN > 8
-#define KMALLOC_MIN_SIZE ARCH_KMALLOC_MINALIGN
+#if defined(ARCH_DMA_MINALIGN) && ARCH_DMA_MINALIGN > 8
+#define KMALLOC_MIN_SIZE ARCH_DMA_MINALIGN
#else
#define KMALLOC_MIN_SIZE 8
#endif
#define KMALLOC_SHIFT_LOW ilog2(KMALLOC_MIN_SIZE)
-#ifndef ARCH_KMALLOC_MINALIGN
+#ifdef ARCH_DMA_MINALIGN
+#define ARCH_KMALLOC_MINALIGN ARCH_DMA_MINALIGN
+#else
#define ARCH_KMALLOC_MINALIGN __alignof__(unsigned long long)
#endif
struct mutex lock;
u8 port_config[8]; /* field 0 is unused */
u32 out_level; /* cached output levels */
+ u32 input_pullup_active;
struct gpio_chip chip;
struct device *dev;
int (*write)(struct device *dev, unsigned int reg, unsigned int val);
struct max7301_platform_data {
/* number assigned to the first GPIO */
unsigned base;
+ /*
+ * bitmask controlling the pullup configuration,
+ *
+ * _note_ the 4 lowest bits are unused, because the first 4
+ * ports of the controller are not used, too.
+ */
+ u32 input_pullup_active;
};
extern int __max730x_remove(struct device *dev);
extern unsigned long mem_cgroup_shrink_node_zone(struct mem_cgroup *mem,
gfp_t gfp_mask, bool noswap,
unsigned int swappiness,
- struct zone *zone,
- int nid);
+ struct zone *zone);
extern int __isolate_lru_page(struct page *page, int mode, int file);
extern unsigned long shrink_all_memory(unsigned long nr_pages);
extern int vm_swappiness;
#include <linux/tty_driver.h>
#include <linux/tty_ldisc.h>
#include <linux/mutex.h>
+#include <linux/smp_lock.h>
#include <asm/system.h>
#define L_FLUSHO(tty) _L_FLAG((tty), FLUSHO)
#define L_PENDIN(tty) _L_FLAG((tty), PENDIN)
#define L_IEXTEN(tty) _L_FLAG((tty), IEXTEN)
+#define L_EXTPROC(tty) _L_FLAG((tty), EXTPROC)
struct device;
struct signal_struct;
extern int tty_signal(int sig, struct tty_struct *tty);
extern void tty_hangup(struct tty_struct *tty);
extern void tty_vhangup(struct tty_struct *tty);
+extern void tty_vhangup_locked(struct tty_struct *tty);
extern void tty_vhangup_self(void);
extern void tty_unhangup(struct file *filp);
extern int tty_hung_up_p(struct file *filp);
extern long vt_compat_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg);
+/* tty_mutex.c */
+/* functions for preparation of BKL removal */
+extern void __lockfunc tty_lock(void) __acquires(tty_lock);
+extern void __lockfunc tty_unlock(void) __releases(tty_lock);
+extern struct task_struct *__big_tty_mutex_owner;
+#define tty_locked() (current == __big_tty_mutex_owner)
+
+/*
+ * wait_event_interruptible_tty -- wait for a condition with the tty lock held
+ *
+ * The condition we are waiting for might take a long time to
+ * become true, or might depend on another thread taking the
+ * BTM. In either case, we need to drop the BTM to guarantee
+ * forward progress. This is a leftover from the conversion
+ * from the BKL and should eventually get removed as the BTM
+ * falls out of use.
+ *
+ * Do not use in new code.
+ */
+#define wait_event_interruptible_tty(wq, condition) \
+({ \
+ int __ret = 0; \
+ if (!(condition)) { \
+ __wait_event_interruptible_tty(wq, condition, __ret); \
+ } \
+ __ret; \
+})
+
+#define __wait_event_interruptible_tty(wq, condition, ret) \
+do { \
+ DEFINE_WAIT(__wait); \
+ \
+ for (;;) { \
+ prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \
+ if (condition) \
+ break; \
+ if (!signal_pending(current)) { \
+ tty_unlock(); \
+ schedule(); \
+ tty_lock(); \
+ continue; \
+ } \
+ ret = -ERESTARTSYS; \
+ break; \
+ } \
+ finish_wait(&wq, &__wait); \
+} while (0)
+
+
#endif /* __KERNEL__ */
#endif
* queued reset so that usb_cancel_queued_reset() doesn't try to
* remove from the workqueue when running inside the worker
* thread. See __usb_queue_reset_device().
+ * @resetting_device: USB core reset the device, so use alt setting 0 as
+ * current; needs bandwidth alloc after reset.
*
* USB device drivers attach to interfaces on a physical device. Each
* interface encapsulates a single high level function, such as feeding
void (*disconnect) (struct usb_interface *intf);
- int (*ioctl) (struct usb_interface *intf, unsigned int code,
+ int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code,
void *buf);
int (*suspend) (struct usb_interface *intf, pm_message_t message);
* is a different endpoint (and pipe) from "out" endpoint two.
* The current configuration controls the existence, type, and
* maximum packet size of any given endpoint.
+ * @stream_id: the endpoint's stream ID for bulk streams
* @dev: Identifies the USB device to perform the request.
* @status: This is read in non-iso completion functions to get the
* status of the particular request. ISO requests only use it
int (*bind)(struct usb_composite_dev *);
int (*unbind)(struct usb_composite_dev *);
+ void (*disconnect)(struct usb_composite_dev *);
+
/* global suspend hooks */
void (*suspend)(struct usb_composite_dev *);
void (*resume)(struct usb_composite_dev *);
};
extern int usb_string_id(struct usb_composite_dev *c);
+extern int usb_string_ids_tab(struct usb_composite_dev *c,
+ struct usb_string *str);
+extern int usb_string_ids_n(struct usb_composite_dev *c, unsigned n);
+
/* messaging utils */
#define DBG(d, fmt, args...) \
#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
u32 hcc_params; /* HCCPARAMS - offset 0x8 */
+/* EHCI 1.1 addendum */
+#define HCC_32FRAME_PERIODIC_LIST(p) ((p)&(1 << 19))
+#define HCC_PER_PORT_CHANGE_EVENT(p) ((p)&(1 << 18))
+#define HCC_LPM(p) ((p)&(1 << 17))
+#define HCC_HW_PREFETCH(p) ((p)&(1 << 16))
+
#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */
#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */
#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */
/* USBCMD: offset 0x00 */
u32 command;
+
+/* EHCI 1.1 addendum */
+#define CMD_HIRD (0xf<<24) /* host initiated resume duration */
+#define CMD_PPCEE (1<<15) /* per port change event enable */
+#define CMD_FSP (1<<14) /* fully synchronized prefetch */
+#define CMD_ASPE (1<<13) /* async schedule prefetch enable */
+#define CMD_PSPE (1<<12) /* periodic schedule prefetch enable */
/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
#define CMD_PARK (1<<11) /* enable "park" on async qh */
#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */
/* USBSTS: offset 0x04 */
u32 status;
+#define STS_PPCE_MASK (0xff<<16) /* Per-Port change event 1-16 */
#define STS_ASS (1<<15) /* Async Schedule Status */
#define STS_PSS (1<<14) /* Periodic Schedule Status */
#define STS_RECL (1<<13) /* Reclamation */
/* PORTSC: offset 0x44 */
u32 port_status[0]; /* up to N_PORTS */
+/* EHCI 1.1 addendum */
+#define PORTSC_SUSPEND_STS_ACK 0
+#define PORTSC_SUSPEND_STS_NYET 1
+#define PORTSC_SUSPEND_STS_STALL 2
+#define PORTSC_SUSPEND_STS_ERR 3
+
+#define PORT_DEV_ADDR (0x7f<<25) /* device address */
+#define PORT_SSTS (0x3<<23) /* suspend status */
/* 31:23 reserved */
#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */
#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */
#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */
/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
/* 9 reserved */
+#define PORT_LPM (1<<9) /* LPM transaction */
#define PORT_RESET (1<<8) /* reset port */
#define PORT_SUSPEND (1<<7) /* suspend port */
#define PORT_RESUME (1<<6) /* resume it */
static void functionfs_unbind(struct ffs_data *ffs)
__attribute__((nonnull));
-static int functionfs_add(struct usb_composite_dev *cdev,
- struct usb_configuration *c,
- struct ffs_data *ffs)
+static int functionfs_bind_config(struct usb_composite_dev *cdev,
+ struct usb_configuration *c,
+ struct ffs_data *ffs)
__attribute__((warn_unused_result, nonnull));
*/
const struct hc_driver *driver; /* hw-specific hooks */
- /* Flags that need to be manipulated atomically */
+ /* Flags that need to be manipulated atomically because they can
+ * change while the host controller is running. Always use
+ * set_bit() or clear_bit() to change their values.
+ */
unsigned long flags;
-#define HCD_FLAG_HW_ACCESSIBLE 0x00000001
-#define HCD_FLAG_SAW_IRQ 0x00000002
+#define HCD_FLAG_HW_ACCESSIBLE 0 /* at full power */
+#define HCD_FLAG_SAW_IRQ 1
+#define HCD_FLAG_POLL_RH 2 /* poll for rh status? */
+#define HCD_FLAG_POLL_PENDING 3 /* status has changed? */
+#define HCD_FLAG_WAKEUP_PENDING 4 /* root hub is resuming? */
+
+ /* The flags can be tested using these macros; they are likely to
+ * be slightly faster than test_bit().
+ */
+#define HCD_HW_ACCESSIBLE(hcd) ((hcd)->flags & (1U << HCD_FLAG_HW_ACCESSIBLE))
+#define HCD_SAW_IRQ(hcd) ((hcd)->flags & (1U << HCD_FLAG_SAW_IRQ))
+#define HCD_POLL_RH(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_RH))
+#define HCD_POLL_PENDING(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_PENDING))
+#define HCD_WAKEUP_PENDING(hcd) ((hcd)->flags & (1U << HCD_FLAG_WAKEUP_PENDING))
+ /* Flags that get set only during HCD registration or removal. */
unsigned rh_registered:1;/* is root hub registered? */
+ unsigned rh_pollable:1; /* may we poll the root hub? */
/* The next flag is a stopgap, to be removed when all the HCDs
* support the new root-hub polling mechanism. */
unsigned uses_new_polling:1;
- unsigned poll_rh:1; /* poll for rh status? */
- unsigned poll_pending:1; /* status has changed? */
unsigned wireless:1; /* Wireless USB HCD */
unsigned authorized_default:1;
unsigned has_tt:1; /* Integrated TT in root hub */
* a whole, not just the root hub; they're for PCI bus glue.
*/
/* called after suspending the hub, before entering D3 etc */
- int (*pci_suspend)(struct usb_hcd *hcd);
+ int (*pci_suspend)(struct usb_hcd *hcd, bool do_wakeup);
/* called after entering D0 (etc), before resuming the hub */
int (*pci_resume)(struct usb_hcd *hcd, bool hibernated);
int (*update_hub_device)(struct usb_hcd *, struct usb_device *hdev,
struct usb_tt *tt, gfp_t mem_flags);
int (*reset_device)(struct usb_hcd *, struct usb_device *);
+ /* Notifies the HCD after a device is connected and its
+ * address is set
+ */
+ int (*update_device)(struct usb_hcd *, struct usb_device *);
};
extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
USB_EVENT_ENUMERATED, /* gadget driver enumerated */
};
-#define USB_OTG_PULLUP_ID (1 << 0)
-#define USB_OTG_PULLDOWN_DP (1 << 1)
-#define USB_OTG_PULLDOWN_DM (1 << 2)
-#define USB_OTG_EXT_VBUS_INDICATOR (1 << 3)
-#define USB_OTG_DRV_VBUS (1 << 4)
-#define USB_OTG_DRV_VBUS_EXT (1 << 5)
-
struct otg_transceiver;
/* for transceivers connected thru an ULPI interface, the user must
return -EINVAL;
}
-static inline int otg_io_write(struct otg_transceiver *otg, u32 reg, u32 val)
+static inline int otg_io_write(struct otg_transceiver *otg, u32 val, u32 reg)
{
if (otg->io_ops && otg->io_ops->write)
- return otg->io_ops->write(otg, reg, val);
+ return otg->io_ops->write(otg, val, reg);
return -EINVAL;
}
and can't handle talking to these interfaces */
#define USB_QUIRK_HONOR_BNUMINTERFACES 0x00000020
+/* device needs a pause during initialization, after we read the device
+ descriptor */
+#define USB_QUIRK_DELAY_INIT 0x00000040
+
#endif /* __LINUX_USB_QUIRKS_H */
#ifndef __LINUX_USB_ULPI_H
#define __LINUX_USB_ULPI_H
+#include <linux/usb/otg.h>
+/*-------------------------------------------------------------------------*/
+
+/*
+ * ULPI Flags
+ */
+#define ULPI_OTG_ID_PULLUP (1 << 0)
+#define ULPI_OTG_DP_PULLDOWN_DIS (1 << 1)
+#define ULPI_OTG_DM_PULLDOWN_DIS (1 << 2)
+#define ULPI_OTG_DISCHRGVBUS (1 << 3)
+#define ULPI_OTG_CHRGVBUS (1 << 4)
+#define ULPI_OTG_DRVVBUS (1 << 5)
+#define ULPI_OTG_DRVVBUS_EXT (1 << 6)
+#define ULPI_OTG_EXTVBUSIND (1 << 7)
+
+#define ULPI_IC_6PIN_SERIAL (1 << 8)
+#define ULPI_IC_3PIN_SERIAL (1 << 9)
+#define ULPI_IC_CARKIT (1 << 10)
+#define ULPI_IC_CLKSUSPM (1 << 11)
+#define ULPI_IC_AUTORESUME (1 << 12)
+#define ULPI_IC_EXTVBUS_INDINV (1 << 13)
+#define ULPI_IC_IND_PASSTHRU (1 << 14)
+#define ULPI_IC_PROTECT_DIS (1 << 15)
+
+#define ULPI_FC_HS (1 << 16)
+#define ULPI_FC_FS (1 << 17)
+#define ULPI_FC_LS (1 << 18)
+#define ULPI_FC_FS4LS (1 << 19)
+#define ULPI_FC_TERMSEL (1 << 20)
+#define ULPI_FC_OP_NORM (1 << 21)
+#define ULPI_FC_OP_NODRV (1 << 22)
+#define ULPI_FC_OP_DIS_NRZI (1 << 23)
+#define ULPI_FC_OP_NSYNC_NEOP (1 << 24)
+#define ULPI_FC_RST (1 << 25)
+#define ULPI_FC_SUSPM (1 << 26)
+
/*-------------------------------------------------------------------------*/
/*
/*-------------------------------------------------------------------------*/
+/*
+ * Register Bits
+ */
+
/* Function Control */
#define ULPI_FUNC_CTRL_XCVRSEL (1 << 0)
#define ULPI_FUNC_CTRL_XCVRSEL_MASK (3 << 0)
#define V4L2_PIX_FMT_RGB565 v4l2_fourcc('R', 'G', 'B', 'P') /* 16 RGB-5-6-5 */
#define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R', 'G', 'B', 'Q') /* 16 RGB-5-5-5 BE */
#define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R', 'G', 'B', 'R') /* 16 RGB-5-6-5 BE */
+#define V4L2_PIX_FMT_BGR666 v4l2_fourcc('B', 'G', 'R', 'H') /* 18 BGR-6-6-6 */
#define V4L2_PIX_FMT_BGR24 v4l2_fourcc('B', 'G', 'R', '3') /* 24 BGR-8-8-8 */
#define V4L2_PIX_FMT_RGB24 v4l2_fourcc('R', 'G', 'B', '3') /* 24 RGB-8-8-8 */
#define V4L2_PIX_FMT_BGR32 v4l2_fourcc('B', 'G', 'R', '4') /* 32 BGR-8-8-8-8 */
#define _LINUX_VIRTIO_9P_H
/* This header is BSD licensed so anyone can use the definitions to implement
* compatible drivers/servers. */
+#include <linux/types.h>
#include <linux/virtio_ids.h>
#include <linux/virtio_config.h>
#include <linux/types.h>
#define vc_translate(vc, c) ((vc)->vc_translate[(c) | \
((vc)->vc_toggle_meta ? 0x80 : 0)])
#else
-#define con_set_trans_old(arg) (0)
-#define con_get_trans_old(arg) (-EINVAL)
-#define con_set_trans_new(arg) (0)
-#define con_get_trans_new(arg) (-EINVAL)
-#define con_clear_unimap(vc, ui) (0)
-#define con_set_unimap(vc, ct, list) (0)
-#define con_set_default_unimap(vc) (0)
-#define con_copy_unimap(d, s) (0)
-#define con_get_unimap(vc, ct, uct, list) (-EINVAL)
-#define con_free_unimap(vc) do { ; } while (0)
-#define con_protect_unimap(vc, rdonly) do { ; } while (0)
+static inline int con_set_trans_old(unsigned char __user *table)
+{
+ return 0;
+}
+static inline int con_get_trans_old(unsigned char __user *table)
+{
+ return -EINVAL;
+}
+static inline int con_set_trans_new(unsigned short __user *table)
+{
+ return 0;
+}
+static inline int con_get_trans_new(unsigned short __user *table)
+{
+ return -EINVAL;
+}
+static inline int con_clear_unimap(struct vc_data *vc, struct unimapinit *ui)
+{
+ return 0;
+}
+static inline
+int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)
+{
+ return 0;
+}
+static inline
+int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct,
+ struct unipair __user *list)
+{
+ return -EINVAL;
+}
+static inline int con_set_default_unimap(struct vc_data *vc)
+{
+ return 0;
+}
+static inline void con_free_unimap(struct vc_data *vc)
+{
+}
+static inline void con_protect_unimap(struct vc_data *vc, int rdonly)
+{
+}
+static inline
+int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc)
+{
+ return 0;
+}
#define vc_translate(vc, c) (c)
#endif
int deflt);
int vty_init(const struct file_operations *console_fops);
+static inline bool vt_force_oops_output(struct vc_data *vc)
+{
+ if (oops_in_progress && vc->vc_panic_force_write)
+ return true;
+ return false;
+}
+
/*
* vc_screen.c shares this temporary buffer with the console write code so that
* we can easily avoid touching user space while holding the console spinlock.
+++ /dev/null
-#ifndef _LINUX_WM97XX_BAT_H
-#define _LINUX_WM97XX_BAT_H
-
-#include <linux/wm97xx.h>
-
-#warning This file will be removed soon, use wm97xx.h instead!
-
-#define wm97xx_batt_info wm97xx_batt_pdata
-
-#ifdef CONFIG_BATTERY_WM97XX
-void wm97xx_bat_set_pdata(struct wm97xx_batt_info *data);
-#else
-static inline void wm97xx_bat_set_pdata(struct wm97xx_batt_info *data) {}
-#endif
-
-#endif
#ifndef CX2341X_H
#define CX2341X_H
+#include <media/v4l2-ctrls.h>
+
enum cx2341x_port {
CX2341X_PORT_MEMORY = 0,
CX2341X_PORT_STREAMING = 1,
void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p);
void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix);
+struct cx2341x_handler;
+
+struct cx2341x_handler_ops {
+ /* needed for the video clock freq */
+ int (*s_audio_sampling_freq)(struct cx2341x_handler *hdl, u32 val);
+ /* needed for dualwatch */
+ int (*s_audio_mode)(struct cx2341x_handler *hdl, u32 val);
+ /* needed for setting up the video resolution */
+ int (*s_video_encoding)(struct cx2341x_handler *hdl, u32 val);
+ /* needed for setting up the sliced vbi insertion data structures */
+ int (*s_stream_vbi_fmt)(struct cx2341x_handler *hdl, u32 val);
+};
+
+struct cx2341x_handler {
+ u32 capabilities;
+ enum cx2341x_port port;
+ u16 width;
+ u16 height;
+ u16 is_50hz;
+ u32 audio_properties;
+
+ struct v4l2_ctrl_handler hdl;
+ void *priv;
+ cx2341x_mbox_func func;
+ const struct cx2341x_handler_ops *ops;
+
+ struct v4l2_ctrl *stream_vbi_fmt;
+
+ struct {
+ /* audio cluster */
+ struct v4l2_ctrl *audio_sampling_freq;
+ struct v4l2_ctrl *audio_encoding;
+ struct v4l2_ctrl *audio_l2_bitrate;
+ struct v4l2_ctrl *audio_mode;
+ struct v4l2_ctrl *audio_mode_extension;
+ struct v4l2_ctrl *audio_emphasis;
+ struct v4l2_ctrl *audio_crc;
+ struct v4l2_ctrl *audio_ac3_bitrate;
+ };
+
+ struct {
+ /* video gop cluster */
+ struct v4l2_ctrl *video_b_frames;
+ struct v4l2_ctrl *video_gop_size;
+ };
+
+ struct {
+ /* stream type cluster */
+ struct v4l2_ctrl *stream_type;
+ struct v4l2_ctrl *video_encoding;
+ struct v4l2_ctrl *video_bitrate_mode;
+ struct v4l2_ctrl *video_bitrate;
+ struct v4l2_ctrl *video_bitrate_peak;
+ };
+
+ struct {
+ /* video mute cluster */
+ struct v4l2_ctrl *video_mute;
+ struct v4l2_ctrl *video_mute_yuv;
+ };
+
+ struct {
+ /* video filter mode cluster */
+ struct v4l2_ctrl *video_spatial_filter_mode;
+ struct v4l2_ctrl *video_temporal_filter_mode;
+ struct v4l2_ctrl *video_median_filter_type;
+ };
+
+ struct {
+ /* video filter type cluster */
+ struct v4l2_ctrl *video_luma_spatial_filter_type;
+ struct v4l2_ctrl *video_chroma_spatial_filter_type;
+ };
+
+ struct {
+ /* video filter cluster */
+ struct v4l2_ctrl *video_spatial_filter;
+ struct v4l2_ctrl *video_temporal_filter;
+ };
+
+ struct {
+ /* video median cluster */
+ struct v4l2_ctrl *video_luma_median_filter_top;
+ struct v4l2_ctrl *video_luma_median_filter_bottom;
+ struct v4l2_ctrl *video_chroma_median_filter_top;
+ struct v4l2_ctrl *video_chroma_median_filter_bottom;
+ };
+};
+
+int cx2341x_handler_init(struct cx2341x_handler *cxhdl,
+ unsigned nr_of_controls_hint);
+void cx2341x_handler_set_50hz(struct cx2341x_handler *cxhdl, int is_50hz);
+int cx2341x_handler_setup(struct cx2341x_handler *cxhdl);
+void cx2341x_handler_set_busy(struct cx2341x_handler *cxhdl, int busy);
+
/* Firmware names */
#define CX2341X_FIRM_ENC_FILENAME "v4l-cx2341x-enc.fw"
/* Decoder firmware for the cx23415 only */
CX25840_AUDIO8,
};
+enum cx25840_io_pin {
+ CX25840_PIN_DVALID_PRGM0 = 0,
+ CX25840_PIN_FIELD_PRGM1,
+ CX25840_PIN_HRESET_PRGM2,
+ CX25840_PIN_VRESET_HCTL_PRGM3,
+ CX25840_PIN_IRQ_N_PRGM4,
+ CX25840_PIN_IR_TX_PRGM6,
+ CX25840_PIN_IR_RX_PRGM5,
+ CX25840_PIN_GPIO0_PRGM8,
+ CX25840_PIN_GPIO1_PRGM9,
+ CX25840_PIN_SA_SDIN, /* Alternate GP Input only */
+ CX25840_PIN_SA_SDOUT, /* Alternate GP Input only */
+ CX25840_PIN_PLL_CLK_PRGM7,
+ CX25840_PIN_CHIP_SEL_VIPCLK, /* Output only */
+};
+
+enum cx25840_io_pad {
+ /* Output pads */
+ CX25840_PAD_DEFAULT = 0,
+ CX25840_PAD_ACTIVE,
+ CX25840_PAD_VACTIVE,
+ CX25840_PAD_CBFLAG,
+ CX25840_PAD_VID_DATA_EXT0,
+ CX25840_PAD_VID_DATA_EXT1,
+ CX25840_PAD_GPO0,
+ CX25840_PAD_GPO1,
+ CX25840_PAD_GPO2,
+ CX25840_PAD_GPO3,
+ CX25840_PAD_IRQ_N,
+ CX25840_PAD_AC_SYNC,
+ CX25840_PAD_AC_SDOUT,
+ CX25840_PAD_PLL_CLK,
+ CX25840_PAD_VRESET,
+ CX25840_PAD_RESERVED,
+ /* Pads for PLL_CLK output only */
+ CX25840_PAD_XTI_X5_DLL,
+ CX25840_PAD_AUX_PLL,
+ CX25840_PAD_VID_PLL,
+ CX25840_PAD_XTI,
+ /* Input Pads */
+ CX25840_PAD_GPI0,
+ CX25840_PAD_GPI1,
+ CX25840_PAD_GPI2,
+ CX25840_PAD_GPI3,
+};
+
+enum cx25840_io_pin_strength {
+ CX25840_PIN_DRIVE_MEDIUM = 0,
+ CX25840_PIN_DRIVE_SLOW,
+ CX25840_PIN_DRIVE_FAST,
+};
+
+enum cx23885_io_pin {
+ CX23885_PIN_IR_RX_GPIO19,
+ CX23885_PIN_IR_TX_GPIO20,
+ CX23885_PIN_I2S_SDAT_GPIO21,
+ CX23885_PIN_I2S_WCLK_GPIO22,
+ CX23885_PIN_I2S_BCLK_GPIO23,
+ CX23885_PIN_IRQ_N_GPIO16,
+};
+
+enum cx23885_io_pad {
+ CX23885_PAD_IR_RX,
+ CX23885_PAD_GPIO19,
+ CX23885_PAD_IR_TX,
+ CX23885_PAD_GPIO20,
+ CX23885_PAD_I2S_SDAT,
+ CX23885_PAD_GPIO21,
+ CX23885_PAD_I2S_WCLK,
+ CX23885_PAD_GPIO22,
+ CX23885_PAD_I2S_BCLK,
+ CX23885_PAD_GPIO23,
+ CX23885_PAD_IRQ_N,
+ CX23885_PAD_GPIO16,
+};
+
+/* pvr150_workaround activates a workaround for a hardware bug that is
+ present in Hauppauge PVR-150 (and possibly PVR-500) cards that have
+ certain NTSC tuners (tveeprom tuner model numbers 85, 99 and 112). The
+ audio autodetect fails on some channels for these models and the workaround
+ is to select the audio standard explicitly. Many thanks to Hauppauge for
+ providing this information.
+ This platform data only needs to be supplied by the ivtv driver. */
+struct cx25840_platform_data {
+ int pvr150_workaround;
+};
+
#endif
* anything with it. Yet, as the same keycode table can be used with other
* devices, a mask is provided to allow its usage. Drivers should generally
* leave this field in blank
+ * @timeout: optional time after which device stops sending data
+ * @min_timeout: minimum timeout supported by device
+ * @max_timeout: maximum timeout supported by device
+ * @rx_resolution : resolution (in ns) of input sampler
+ * @tx_resolution: resolution (in ns) of output sampler
* @priv: driver-specific data, to be used on the callbacks
* @change_protocol: allow changing the protocol used on hardware decoders
* @open: callback to allow drivers to enable polling/irq when IR input device
* is opened.
* @s_tx_mask: set transmitter mask (for devices with multiple tx outputs)
* @s_tx_carrier: set transmit carrier frequency
+ * @s_tx_duty_cycle: set transmit duty cycle (0% - 100%)
+ * @s_rx_carrier: inform driver about carrier it is expected to handle
* @tx_ir: transmit IR
+ * @s_idle: optional: enable/disable hardware idle mode, upon which,
+ device doesn't interrupt host until it sees IR pulses
+ * @s_learning_mode: enable wide band receiver used for learning
*/
struct ir_dev_props {
enum rc_driver_type driver_type;
unsigned long allowed_protos;
u32 scanmask;
+
+ u32 timeout;
+ u32 min_timeout;
+ u32 max_timeout;
+
+ u32 rx_resolution;
+ u32 tx_resolution;
+
void *priv;
int (*change_protocol)(void *priv, u64 ir_type);
int (*open)(void *priv);
void (*close)(void *priv);
int (*s_tx_mask)(void *priv, u32 mask);
int (*s_tx_carrier)(void *priv, u32 carrier);
+ int (*s_tx_duty_cycle)(void *priv, u32 duty_cycle);
+ int (*s_rx_carrier_range)(void *priv, u32 min, u32 max);
int (*tx_ir)(void *priv, int *txbuf, u32 n);
+ void (*s_idle)(void *priv, int enable);
+ int (*s_learning_mode)(void *priv, int enable);
};
struct ir_input_dev {
char *driver_name; /* Name of the driver module */
struct ir_scancode_table rc_tab; /* scan/key table */
unsigned long devno; /* device number */
- const struct ir_dev_props *props; /* Device properties */
+ struct ir_dev_props *props; /* Device properties */
struct ir_raw_event_ctrl *raw; /* for raw pulse/space events */
struct input_dev *input_dev; /* the input device associated with this device */
+ bool idle;
/* key info - needed by IR keycode handlers */
spinlock_t keylock; /* protects the below members */
/* From ir-keytable.c */
int __ir_input_register(struct input_dev *dev,
const struct ir_scancode_table *ir_codes,
- const struct ir_dev_props *props,
+ struct ir_dev_props *props,
const char *driver_name);
static inline int ir_input_register(struct input_dev *dev,
const char *map_name,
- const struct ir_dev_props *props,
+ struct ir_dev_props *props,
const char *driver_name) {
struct ir_scancode_table *ir_codes;
struct ir_input_dev *ir_dev;
return -EINVAL;
ir_codes = get_rc_map(map_name);
- if (!ir_codes)
- return -EINVAL;
+ if (!ir_codes) {
+ ir_codes = get_rc_map(RC_MAP_EMPTY);
+
+ if (!ir_codes)
+ return -EINVAL;
+ }
rc = __ir_input_register(dev, ir_codes, props, driver_name);
if (rc < 0)
void ir_raw_event_handle(struct input_dev *input_dev);
int ir_raw_event_store(struct input_dev *input_dev, struct ir_raw_event *ev);
int ir_raw_event_store_edge(struct input_dev *input_dev, enum raw_event_type type);
+int ir_raw_event_store_with_filter(struct input_dev *input_dev,
+ struct ir_raw_event *ev);
+void ir_raw_event_set_idle(struct input_dev *input_dev, int idle);
+
static inline void ir_raw_event_reset(struct input_dev *input_dev)
{
struct ir_raw_event ev = { .pulse = false, .duration = 0 };
#define LIRC_CAN_SET_REC_FILTER 0x08000000
#define LIRC_CAN_MEASURE_CARRIER 0x02000000
+#define LIRC_CAN_USE_WIDEBAND_RECEIVER 0x04000000
#define LIRC_CAN_SEND(x) ((x)&LIRC_CAN_SEND_MASK)
#define LIRC_CAN_REC(x) ((x)&LIRC_CAN_REC_MASK)
* if enabled from the next key press on the driver will send
* LIRC_MODE2_FREQUENCY packets
*/
-#define LIRC_SET_MEASURE_CARRIER_MODE _IOW('i', 0x0000001d, __u32)
+#define LIRC_SET_MEASURE_CARRIER_MODE _IOW('i', 0x0000001d, __u32)
/*
* to set a range use
#define LIRC_SETUP_START _IO('i', 0x00000021)
#define LIRC_SETUP_END _IO('i', 0x00000022)
+#define LIRC_SET_WIDEBAND_RECEIVER _IOW('i', 0x00000023, __u32)
+
#endif
#define RC_MAP_PURPLETV "rc-purpletv"
#define RC_MAP_PV951 "rc-pv951"
#define RC_MAP_RC5_HAUPPAUGE_NEW "rc-rc5-hauppauge-new"
+#define RC_MAP_RC5_STREAMZAP "rc-rc5-streamzap"
#define RC_MAP_RC5_TV "rc-rc5-tv"
#define RC_MAP_RC6_MCE "rc-rc6-mce"
#define RC_MAP_REAL_AUDIO_220_32_KEYS "rc-real-audio-220-32-keys"
--- /dev/null
+/*
+ V4L2 controls support header.
+
+ Copyright (C) 2010 Hans Verkuil <hverkuil@xs4all.nl>
+
+ 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
+ */
+
+#ifndef _V4L2_CTRLS_H
+#define _V4L2_CTRLS_H
+
+#include <linux/list.h>
+#include <linux/device.h>
+
+/* forward references */
+struct v4l2_ctrl_handler;
+struct v4l2_ctrl;
+struct video_device;
+struct v4l2_subdev;
+
+/** struct v4l2_ctrl_ops - The control operations that the driver has to provide.
+ * @g_volatile_ctrl: Get a new value for this control. Generally only relevant
+ * for volatile (and usually read-only) controls such as a control
+ * that returns the current signal strength which changes
+ * continuously.
+ * If not set, then the currently cached value will be returned.
+ * @try_ctrl: Test whether the control's value is valid. Only relevant when
+ * the usual min/max/step checks are not sufficient.
+ * @s_ctrl: Actually set the new control value. s_ctrl is compulsory. The
+ * ctrl->handler->lock is held when these ops are called, so no
+ * one else can access controls owned by that handler.
+ */
+struct v4l2_ctrl_ops {
+ int (*g_volatile_ctrl)(struct v4l2_ctrl *ctrl);
+ int (*try_ctrl)(struct v4l2_ctrl *ctrl);
+ int (*s_ctrl)(struct v4l2_ctrl *ctrl);
+};
+
+/** struct v4l2_ctrl - The control structure.
+ * @node: The list node.
+ * @handler: The handler that owns the control.
+ * @cluster: Point to start of cluster array.
+ * @ncontrols: Number of controls in cluster array.
+ * @has_new: Internal flag: set when there is a valid new value.
+ * @done: Internal flag: set for each processed control.
+ * @is_private: If set, then this control is private to its handler and it
+ * will not be added to any other handlers. Drivers can set
+ * this flag.
+ * @is_volatile: If set, then this control is volatile. This means that the
+ * control's current value cannot be cached and needs to be
+ * retrieved through the g_volatile_ctrl op. Drivers can set
+ * this flag.
+ * @ops: The control ops.
+ * @id: The control ID.
+ * @name: The control name.
+ * @type: The control type.
+ * @minimum: The control's minimum value.
+ * @maximum: The control's maximum value.
+ * @default_value: The control's default value.
+ * @step: The control's step value for non-menu controls.
+ * @menu_skip_mask: The control's skip mask for menu controls. This makes it
+ * easy to skip menu items that are not valid. If bit X is set,
+ * then menu item X is skipped. Of course, this only works for
+ * menus with <= 32 menu items. There are no menus that come
+ * close to that number, so this is OK. Should we ever need more,
+ * then this will have to be extended to a u64 or a bit array.
+ * @qmenu: A const char * array for all menu items. Array entries that are
+ * empty strings ("") correspond to non-existing menu items (this
+ * is in addition to the menu_skip_mask above). The last entry
+ * must be NULL.
+ * @flags: The control's flags.
+ * @cur: The control's current value.
+ * @val: The control's new s32 value.
+ * @val64: The control's new s64 value.
+ * @string: The control's new string value.
+ * @priv: The control's private pointer. For use by the driver. It is
+ * untouched by the control framework. Note that this pointer is
+ * not freed when the control is deleted. Should this be needed
+ * then a new internal bitfield can be added to tell the framework
+ * to free this pointer.
+ */
+struct v4l2_ctrl {
+ /* Administrative fields */
+ struct list_head node;
+ struct v4l2_ctrl_handler *handler;
+ struct v4l2_ctrl **cluster;
+ unsigned ncontrols;
+ unsigned int has_new:1;
+ unsigned int done:1;
+
+ unsigned int is_private:1;
+ unsigned int is_volatile:1;
+
+ const struct v4l2_ctrl_ops *ops;
+ u32 id;
+ const char *name;
+ enum v4l2_ctrl_type type;
+ s32 minimum, maximum, default_value;
+ union {
+ u32 step;
+ u32 menu_skip_mask;
+ };
+ const char **qmenu;
+ unsigned long flags;
+ union {
+ s32 val;
+ s64 val64;
+ char *string;
+ } cur;
+ union {
+ s32 val;
+ s64 val64;
+ char *string;
+ };
+ void *priv;
+};
+
+/** struct v4l2_ctrl_ref - The control reference.
+ * @node: List node for the sorted list.
+ * @next: Single-link list node for the hash.
+ * @ctrl: The actual control information.
+ *
+ * Each control handler has a list of these refs. The list_head is used to
+ * keep a sorted-by-control-ID list of all controls, while the next pointer
+ * is used to link the control in the hash's bucket.
+ */
+struct v4l2_ctrl_ref {
+ struct list_head node;
+ struct v4l2_ctrl_ref *next;
+ struct v4l2_ctrl *ctrl;
+};
+
+/** struct v4l2_ctrl_handler - The control handler keeps track of all the
+ * controls: both the controls owned by the handler and those inherited
+ * from other handlers.
+ * @lock: Lock to control access to this handler and its controls.
+ * @ctrls: The list of controls owned by this handler.
+ * @ctrl_refs: The list of control references.
+ * @cached: The last found control reference. It is common that the same
+ * control is needed multiple times, so this is a simple
+ * optimization.
+ * @buckets: Buckets for the hashing. Allows for quick control lookup.
+ * @nr_of_buckets: Total number of buckets in the array.
+ * @error: The error code of the first failed control addition.
+ */
+struct v4l2_ctrl_handler {
+ struct mutex lock;
+ struct list_head ctrls;
+ struct list_head ctrl_refs;
+ struct v4l2_ctrl_ref *cached;
+ struct v4l2_ctrl_ref **buckets;
+ u16 nr_of_buckets;
+ int error;
+};
+
+/** struct v4l2_ctrl_config - Control configuration structure.
+ * @ops: The control ops.
+ * @id: The control ID.
+ * @name: The control name.
+ * @type: The control type.
+ * @min: The control's minimum value.
+ * @max: The control's maximum value.
+ * @step: The control's step value for non-menu controls.
+ * @def: The control's default value.
+ * @flags: The control's flags.
+ * @menu_skip_mask: The control's skip mask for menu controls. This makes it
+ * easy to skip menu items that are not valid. If bit X is set,
+ * then menu item X is skipped. Of course, this only works for
+ * menus with <= 32 menu items. There are no menus that come
+ * close to that number, so this is OK. Should we ever need more,
+ * then this will have to be extended to a u64 or a bit array.
+ * @qmenu: A const char * array for all menu items. Array entries that are
+ * empty strings ("") correspond to non-existing menu items (this
+ * is in addition to the menu_skip_mask above). The last entry
+ * must be NULL.
+ * @is_private: If set, then this control is private to its handler and it
+ * will not be added to any other handlers.
+ * @is_volatile: If set, then this control is volatile. This means that the
+ * control's current value cannot be cached and needs to be
+ * retrieved through the g_volatile_ctrl op.
+ */
+struct v4l2_ctrl_config {
+ const struct v4l2_ctrl_ops *ops;
+ u32 id;
+ const char *name;
+ enum v4l2_ctrl_type type;
+ s32 min;
+ s32 max;
+ u32 step;
+ s32 def;
+ u32 flags;
+ u32 menu_skip_mask;
+ const char **qmenu;
+ unsigned int is_private:1;
+ unsigned int is_volatile:1;
+};
+
+/** v4l2_ctrl_fill() - Fill in the control fields based on the control ID.
+ *
+ * This works for all standard V4L2 controls.
+ * For non-standard controls it will only fill in the given arguments
+ * and @name will be NULL.
+ *
+ * This function will overwrite the contents of @name, @type and @flags.
+ * The contents of @min, @max, @step and @def may be modified depending on
+ * the type.
+ *
+ * Do not use in drivers! It is used internally for backwards compatibility
+ * control handling only. Once all drivers are converted to use the new
+ * control framework this function will no longer be exported.
+ */
+void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
+ s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags);
+
+
+/** v4l2_ctrl_handler_init() - Initialize the control handler.
+ * @hdl: The control handler.
+ * @nr_of_controls_hint: A hint of how many controls this handler is
+ * expected to refer to. This is the total number, so including
+ * any inherited controls. It doesn't have to be precise, but if
+ * it is way off, then you either waste memory (too many buckets
+ * are allocated) or the control lookup becomes slower (not enough
+ * buckets are allocated, so there are more slow list lookups).
+ * It will always work, though.
+ *
+ * Returns an error if the buckets could not be allocated. This error will
+ * also be stored in @hdl->error.
+ */
+int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl,
+ unsigned nr_of_controls_hint);
+
+/** v4l2_ctrl_handler_free() - Free all controls owned by the handler and free
+ * the control list.
+ * @hdl: The control handler.
+ *
+ * Does nothing if @hdl == NULL.
+ */
+void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl);
+
+/** v4l2_ctrl_handler_setup() - Call the s_ctrl op for all controls belonging
+ * to the handler to initialize the hardware to the current control values.
+ * @hdl: The control handler.
+ *
+ * Button controls will be skipped, as are read-only controls.
+ *
+ * If @hdl == NULL, then this just returns 0.
+ */
+int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl);
+
+/** v4l2_ctrl_handler_log_status() - Log all controls owned by the handler.
+ * @hdl: The control handler.
+ * @prefix: The prefix to use when logging the control values. If the
+ * prefix does not end with a space, then ": " will be added
+ * after the prefix. If @prefix == NULL, then no prefix will be
+ * used.
+ *
+ * For use with VIDIOC_LOG_STATUS.
+ *
+ * Does nothing if @hdl == NULL.
+ */
+void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl,
+ const char *prefix);
+
+/** v4l2_ctrl_new_custom() - Allocate and initialize a new custom V4L2
+ * control.
+ * @hdl: The control handler.
+ * @cfg: The control's configuration data.
+ * @priv: The control's driver-specific private data.
+ *
+ * If the &v4l2_ctrl struct could not be allocated then NULL is returned
+ * and @hdl->error is set to the error code (if it wasn't set already).
+ */
+struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_config *cfg, void *priv);
+
+/** v4l2_ctrl_new_std() - Allocate and initialize a new standard V4L2 non-menu control.
+ * @hdl: The control handler.
+ * @ops: The control ops.
+ * @id: The control ID.
+ * @min: The control's minimum value.
+ * @max: The control's maximum value.
+ * @step: The control's step value
+ * @def: The control's default value.
+ *
+ * If the &v4l2_ctrl struct could not be allocated, or the control
+ * ID is not known, then NULL is returned and @hdl->error is set to the
+ * appropriate error code (if it wasn't set already).
+ *
+ * If @id refers to a menu control, then this function will return NULL.
+ *
+ * Use v4l2_ctrl_new_std_menu() when adding menu controls.
+ */
+struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, s32 min, s32 max, u32 step, s32 def);
+
+/** v4l2_ctrl_new_std_menu() - Allocate and initialize a new standard V4L2 menu control.
+ * @hdl: The control handler.
+ * @ops: The control ops.
+ * @id: The control ID.
+ * @max: The control's maximum value.
+ * @mask: The control's skip mask for menu controls. This makes it
+ * easy to skip menu items that are not valid. If bit X is set,
+ * then menu item X is skipped. Of course, this only works for
+ * menus with <= 32 menu items. There are no menus that come
+ * close to that number, so this is OK. Should we ever need more,
+ * then this will have to be extended to a u64 or a bit array.
+ * @def: The control's default value.
+ *
+ * Same as v4l2_ctrl_new_std(), but @min is set to 0 and the @mask value
+ * determines which menu items are to be skipped.
+ *
+ * If @id refers to a non-menu control, then this function will return NULL.
+ */
+struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, s32 max, s32 mask, s32 def);
+
+/** v4l2_ctrl_add_ctrl() - Add a control from another handler to this handler.
+ * @hdl: The control handler.
+ * @ctrl: The control to add.
+ *
+ * It will return NULL if it was unable to add the control reference.
+ * If the control already belonged to the handler, then it will do
+ * nothing and just return @ctrl.
+ */
+struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ctrl *ctrl);
+
+/** v4l2_ctrl_add_handler() - Add all controls from handler @add to
+ * handler @hdl.
+ * @hdl: The control handler.
+ * @add: The control handler whose controls you want to add to
+ * the @hdl control handler.
+ *
+ * Does nothing if either of the two is a NULL pointer.
+ * In case of an error @hdl->error will be set to the error code (if it
+ * wasn't set already).
+ */
+int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ctrl_handler *add);
+
+
+/** v4l2_ctrl_cluster() - Mark all controls in the cluster as belonging to that cluster.
+ * @ncontrols: The number of controls in this cluster.
+ * @controls: The cluster control array of size @ncontrols.
+ */
+void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls);
+
+
+/** v4l2_ctrl_find() - Find a control with the given ID.
+ * @hdl: The control handler.
+ * @id: The control ID to find.
+ *
+ * If @hdl == NULL this will return NULL as well. Will lock the handler so
+ * do not use from inside &v4l2_ctrl_ops.
+ */
+struct v4l2_ctrl *v4l2_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id);
+
+/** v4l2_ctrl_activate() - Make the control active or inactive.
+ * @ctrl: The control to (de)activate.
+ * @active: True if the control should become active.
+ *
+ * This sets or clears the V4L2_CTRL_FLAG_INACTIVE flag atomically.
+ * Does nothing if @ctrl == NULL.
+ * This will usually be called from within the s_ctrl op.
+ *
+ * This function can be called regardless of whether the control handler
+ * is locked or not.
+ */
+void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active);
+
+/** v4l2_ctrl_grab() - Mark the control as grabbed or not grabbed.
+ * @ctrl: The control to (de)activate.
+ * @grabbed: True if the control should become grabbed.
+ *
+ * This sets or clears the V4L2_CTRL_FLAG_GRABBED flag atomically.
+ * Does nothing if @ctrl == NULL.
+ * This will usually be called when starting or stopping streaming in the
+ * driver.
+ *
+ * This function can be called regardless of whether the control handler
+ * is locked or not.
+ */
+void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed);
+
+/** v4l2_ctrl_lock() - Helper function to lock the handler
+ * associated with the control.
+ * @ctrl: The control to lock.
+ */
+static inline void v4l2_ctrl_lock(struct v4l2_ctrl *ctrl)
+{
+ mutex_lock(&ctrl->handler->lock);
+}
+
+/** v4l2_ctrl_lock() - Helper function to unlock the handler
+ * associated with the control.
+ * @ctrl: The control to unlock.
+ */
+static inline void v4l2_ctrl_unlock(struct v4l2_ctrl *ctrl)
+{
+ mutex_unlock(&ctrl->handler->lock);
+}
+
+/** v4l2_ctrl_g_ctrl() - Helper function to get the control's value from within a driver.
+ * @ctrl: The control.
+ *
+ * This returns the control's value safely by going through the control
+ * framework. This function will lock the control's handler, so it cannot be
+ * used from within the &v4l2_ctrl_ops functions.
+ *
+ * This function is for integer type controls only.
+ */
+s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl);
+
+/** v4l2_ctrl_s_ctrl() - Helper function to set the control's value from within a driver.
+ * @ctrl: The control.
+ * @val: The new value.
+ *
+ * This set the control's new value safely by going through the control
+ * framework. This function will lock the control's handler, so it cannot be
+ * used from within the &v4l2_ctrl_ops functions.
+ *
+ * This function is for integer type controls only.
+ */
+int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val);
+
+
+/* Helpers for ioctl_ops. If hdl == NULL then they will all return -EINVAL. */
+int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc);
+int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm);
+int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *ctrl);
+int v4l2_s_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *ctrl);
+int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *c);
+int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *c);
+int v4l2_s_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *c);
+
+/* Helpers for subdevices. If the associated ctrl_handler == NULL then they
+ will all return -EINVAL. */
+int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc);
+int v4l2_subdev_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qm);
+int v4l2_subdev_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs);
+int v4l2_subdev_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs);
+int v4l2_subdev_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs);
+int v4l2_subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
+int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
+
+#endif
struct v4l2_ioctl_callbacks;
struct video_device;
struct v4l2_device;
+struct v4l2_ctrl_handler;
/* Flag to mark the video_device struct as registered.
Drivers can clear this flag if they want to block all future
struct device *parent; /* device parent */
struct v4l2_device *v4l2_dev; /* v4l2_device parent */
+ /* Control handler associated with this device node. May be NULL. */
+ struct v4l2_ctrl_handler *ctrl_handler;
+
/* device info */
char name[32];
int vfl_type;
#define V4L2_DEVICE_NAME_SIZE (20 + 16)
+struct v4l2_ctrl_handler;
+
struct v4l2_device {
/* dev->driver_data points to this struct.
Note: dev might be NULL if there is no parent device
/* notify callback called by some sub-devices. */
void (*notify)(struct v4l2_subdev *sd,
unsigned int notification, void *arg);
+ /* The control handler. May be NULL. */
+ struct v4l2_ctrl_handler *ctrl_handler;
};
/* Initialize v4l2_dev and make dev->driver_data point to v4l2_dev.
#define V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ 0x00000001
struct v4l2_device;
+struct v4l2_ctrl_handler;
struct v4l2_subdev;
struct tuner_setup;
not yet implemented) since ops provide proper type-checking.
*/
+/* Subdevice external IO pin configuration */
+#define V4L2_SUBDEV_IO_PIN_DISABLE (1 << 0) /* ENABLE assumed */
+#define V4L2_SUBDEV_IO_PIN_OUTPUT (1 << 1)
+#define V4L2_SUBDEV_IO_PIN_INPUT (1 << 2)
+#define V4L2_SUBDEV_IO_PIN_SET_VALUE (1 << 3) /* Set output value */
+#define V4L2_SUBDEV_IO_PIN_ACTIVE_LOW (1 << 4) /* ACTIVE HIGH assumed */
+
+struct v4l2_subdev_io_pin_config {
+ u32 flags; /* V4L2_SUBDEV_IO_PIN_* flags for this pin's config */
+ u8 pin; /* Chip external IO pin to configure */
+ u8 function; /* Internal signal pad/function to route to IO pin */
+ u8 value; /* Initial value for pin - e.g. GPIO output value */
+ u8 strength; /* Pin drive strength */
+};
+
/* s_config: if set, then it is always called by the v4l2_i2c_new_subdev*
functions after the v4l2_subdev was registered. It is used to pass
platform data to the subdev which can be used during initialization.
+ s_io_pin_config: configure one or more chip I/O pins for chips that
+ multiplex different internal signal pads out to IO pins. This function
+ takes a pointer to an array of 'n' pin configuration entries, one for
+ each pin being configured. This function could be called at times
+ other than just subdevice initialization.
+
init: initialize the sensor registors to some sort of reasonable default
values. Do not use for new drivers and should be removed in existing
drivers.
s_power: puts subdevice in power saving mode (on == 0) or normal operation
mode (on == 1).
+
+ interrupt_service_routine: Called by the bridge chip's interrupt service
+ handler, when an interrupt status has be raised due to this subdev,
+ so that this subdev can handle the details. It may schedule work to be
+ performed later. It must not sleep. *Called from an IRQ context*.
*/
struct v4l2_subdev_core_ops {
int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip);
int (*log_status)(struct v4l2_subdev *sd);
int (*s_config)(struct v4l2_subdev *sd, int irq, void *platform_data);
+ int (*s_io_pin_config)(struct v4l2_subdev *sd, size_t n,
+ struct v4l2_subdev_io_pin_config *pincfg);
int (*init)(struct v4l2_subdev *sd, u32 val);
int (*load_fw)(struct v4l2_subdev *sd);
int (*reset)(struct v4l2_subdev *sd, u32 val);
int (*s_register)(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg);
#endif
int (*s_power)(struct v4l2_subdev *sd, int on);
+ int (*interrupt_service_routine)(struct v4l2_subdev *sd,
+ u32 status, bool *handled);
};
/* s_mode: switch the tuner to a specific tuner mode. Replacement of s_radio.
};
/*
- interrupt_service_routine: Called by the bridge chip's interrupt service
- handler, when an IR interrupt status has be raised due to this subdev,
- so that this subdev can handle the details. It may schedule work to be
- performed later. It must not sleep. *Called from an IRQ context*.
-
[rt]x_g_parameters: Get the current operating parameters and state of the
the IR receiver or transmitter.
*/
enum v4l2_subdev_ir_mode {
- V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, /* space & mark widths in nanosecs */
+ V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, /* uses struct ir_raw_event records */
};
-/* Data format of data read or written for V4L2_SUBDEV_IR_MODE_PULSE_WIDTH */
-#define V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS 0x7fffffff
-#define V4L2_SUBDEV_IR_PULSE_LEVEL_MASK 0x80000000
-#define V4L2_SUBDEV_IR_PULSE_RX_SEQ_END 0xffffffff
-
struct v4l2_subdev_ir_parameters {
/* Either Rx or Tx */
unsigned int bytes_per_data_element; /* of data in read or write call */
u32 max_pulse_width; /* ns, valid only for baseband signal */
unsigned int carrier_freq; /* Hz, valid only for modulated signal*/
unsigned int duty_cycle; /* percent, valid only for modulated signal*/
- bool invert; /* logically invert sense of mark/space */
+ bool invert_level; /* invert signal level */
+
+ /* Tx only */
+ bool invert_carrier_sense; /* Send 0/space as a carrier burst */
/* Rx only */
u32 noise_filter_min_width; /* ns, min time of a valid pulse */
};
struct v4l2_subdev_ir_ops {
- /* Common to receiver and transmitter */
- int (*interrupt_service_routine)(struct v4l2_subdev *sd,
- u32 status, bool *handled);
-
/* Receiver */
int (*rx_read)(struct v4l2_subdev *sd, u8 *buf, size_t count,
ssize_t *num);
u32 flags;
struct v4l2_device *v4l2_dev;
const struct v4l2_subdev_ops *ops;
+ /* The control handler of this subdev. May be NULL. */
+ struct v4l2_ctrl_handler *ctrl_handler;
/* name must be unique */
char name[V4L2_SUBDEV_NAME_SIZE];
/* can be used to group similar subdevs, value is driver-specific */
/* defined in T10 SCSI Primary Commands-2 (SPC2) */
struct scsi_varlen_cdb_hdr {
- u8 opcode; /* opcode always == VARIABLE_LENGTH_CMD */
- u8 control;
- u8 misc[5];
- u8 additional_cdb_length; /* total cdb length - 8 */
+ __u8 opcode; /* opcode always == VARIABLE_LENGTH_CMD */
+ __u8 control;
+ __u8 misc[5];
+ __u8 additional_cdb_length; /* total cdb length - 8 */
__be16 service_action;
/* service specific data follows */
};
* PCI : ID data is the 16 bit PCI Registered Vendor ID
*/
#define SCSI_NL_VID_TYPE_SHIFT 56
-#define SCSI_NL_VID_TYPE_MASK ((u64)0xFF << SCSI_NL_VID_TYPE_SHIFT)
-#define SCSI_NL_VID_TYPE_PCI ((u64)0x01 << SCSI_NL_VID_TYPE_SHIFT)
+#define SCSI_NL_VID_TYPE_MASK ((__u64)0xFF << SCSI_NL_VID_TYPE_SHIFT)
+#define SCSI_NL_VID_TYPE_PCI ((__u64)0x01 << SCSI_NL_VID_TYPE_SHIFT)
#define SCSI_NL_VID_ID_MASK (~ SCSI_NL_VID_TYPE_MASK)
TP_fast_assign(
__entry->dev = rq->rq_disk ? disk_devt(rq->rq_disk) : 0;
- __entry->sector = blk_pc_request(rq) ? 0 : blk_rq_pos(rq);
- __entry->nr_sector = blk_pc_request(rq) ? 0 : blk_rq_sectors(rq);
+ __entry->sector = (rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ 0 : blk_rq_pos(rq);
+ __entry->nr_sector = (rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ 0 : blk_rq_sectors(rq);
__entry->errors = rq->errors;
blk_fill_rwbs_rq(__entry->rwbs, rq);
TP_fast_assign(
__entry->dev = rq->rq_disk ? disk_devt(rq->rq_disk) : 0;
- __entry->sector = blk_pc_request(rq) ? 0 : blk_rq_pos(rq);
- __entry->nr_sector = blk_pc_request(rq) ? 0 : blk_rq_sectors(rq);
- __entry->bytes = blk_pc_request(rq) ? blk_rq_bytes(rq) : 0;
+ __entry->sector = (rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ 0 : blk_rq_pos(rq);
+ __entry->nr_sector = (rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ 0 : blk_rq_sectors(rq);
+ __entry->bytes = (rq->cmd_type == REQ_TYPE_BLOCK_PC) ?
+ blk_rq_bytes(rq) : 0;
blk_fill_rwbs_rq(__entry->rwbs, rq);
blk_dump_cmd(__get_str(cmd), rq);
--- /dev/null
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM writeback
+
+#if !defined(_TRACE_WRITEBACK_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_WRITEBACK_H
+
+#include <linux/backing-dev.h>
+#include <linux/device.h>
+#include <linux/writeback.h>
+
+struct wb_writeback_work;
+
+DECLARE_EVENT_CLASS(writeback_work_class,
+ TP_PROTO(struct backing_dev_info *bdi, struct wb_writeback_work *work),
+ TP_ARGS(bdi, work),
+ TP_STRUCT__entry(
+ __array(char, name, 32)
+ __field(long, nr_pages)
+ __field(dev_t, sb_dev)
+ __field(int, sync_mode)
+ __field(int, for_kupdate)
+ __field(int, range_cyclic)
+ __field(int, for_background)
+ ),
+ TP_fast_assign(
+ strncpy(__entry->name, dev_name(bdi->dev), 32);
+ __entry->nr_pages = work->nr_pages;
+ __entry->sb_dev = work->sb ? work->sb->s_dev : 0;
+ __entry->sync_mode = work->sync_mode;
+ __entry->for_kupdate = work->for_kupdate;
+ __entry->range_cyclic = work->range_cyclic;
+ __entry->for_background = work->for_background;
+ ),
+ TP_printk("bdi %s: sb_dev %d:%d nr_pages=%ld sync_mode=%d "
+ "kupdate=%d range_cyclic=%d background=%d",
+ __entry->name,
+ MAJOR(__entry->sb_dev), MINOR(__entry->sb_dev),
+ __entry->nr_pages,
+ __entry->sync_mode,
+ __entry->for_kupdate,
+ __entry->range_cyclic,
+ __entry->for_background
+ )
+);
+#define DEFINE_WRITEBACK_WORK_EVENT(name) \
+DEFINE_EVENT(writeback_work_class, name, \
+ TP_PROTO(struct backing_dev_info *bdi, struct wb_writeback_work *work), \
+ TP_ARGS(bdi, work))
+DEFINE_WRITEBACK_WORK_EVENT(writeback_nothread);
+DEFINE_WRITEBACK_WORK_EVENT(writeback_queue);
+DEFINE_WRITEBACK_WORK_EVENT(writeback_exec);
+
+TRACE_EVENT(writeback_pages_written,
+ TP_PROTO(long pages_written),
+ TP_ARGS(pages_written),
+ TP_STRUCT__entry(
+ __field(long, pages)
+ ),
+ TP_fast_assign(
+ __entry->pages = pages_written;
+ ),
+ TP_printk("%ld", __entry->pages)
+);
+
+DECLARE_EVENT_CLASS(writeback_class,
+ TP_PROTO(struct backing_dev_info *bdi),
+ TP_ARGS(bdi),
+ TP_STRUCT__entry(
+ __array(char, name, 32)
+ ),
+ TP_fast_assign(
+ strncpy(__entry->name, dev_name(bdi->dev), 32);
+ ),
+ TP_printk("bdi %s",
+ __entry->name
+ )
+);
+#define DEFINE_WRITEBACK_EVENT(name) \
+DEFINE_EVENT(writeback_class, name, \
+ TP_PROTO(struct backing_dev_info *bdi), \
+ TP_ARGS(bdi))
+
+DEFINE_WRITEBACK_EVENT(writeback_nowork);
+DEFINE_WRITEBACK_EVENT(writeback_wake_thread);
+DEFINE_WRITEBACK_EVENT(writeback_wake_forker_thread);
+DEFINE_WRITEBACK_EVENT(writeback_bdi_register);
+DEFINE_WRITEBACK_EVENT(writeback_bdi_unregister);
+DEFINE_WRITEBACK_EVENT(writeback_thread_start);
+DEFINE_WRITEBACK_EVENT(writeback_thread_stop);
+
+DECLARE_EVENT_CLASS(wbc_class,
+ TP_PROTO(struct writeback_control *wbc, struct backing_dev_info *bdi),
+ TP_ARGS(wbc, bdi),
+ TP_STRUCT__entry(
+ __array(char, name, 32)
+ __field(long, nr_to_write)
+ __field(long, pages_skipped)
+ __field(int, sync_mode)
+ __field(int, nonblocking)
+ __field(int, encountered_congestion)
+ __field(int, for_kupdate)
+ __field(int, for_background)
+ __field(int, for_reclaim)
+ __field(int, range_cyclic)
+ __field(int, more_io)
+ __field(unsigned long, older_than_this)
+ __field(long, range_start)
+ __field(long, range_end)
+ ),
+
+ TP_fast_assign(
+ strncpy(__entry->name, dev_name(bdi->dev), 32);
+ __entry->nr_to_write = wbc->nr_to_write;
+ __entry->pages_skipped = wbc->pages_skipped;
+ __entry->sync_mode = wbc->sync_mode;
+ __entry->for_kupdate = wbc->for_kupdate;
+ __entry->for_background = wbc->for_background;
+ __entry->for_reclaim = wbc->for_reclaim;
+ __entry->range_cyclic = wbc->range_cyclic;
+ __entry->more_io = wbc->more_io;
+ __entry->older_than_this = wbc->older_than_this ?
+ *wbc->older_than_this : 0;
+ __entry->range_start = (long)wbc->range_start;
+ __entry->range_end = (long)wbc->range_end;
+ ),
+
+ TP_printk("bdi %s: towrt=%ld skip=%ld mode=%d kupd=%d "
+ "bgrd=%d reclm=%d cyclic=%d more=%d older=0x%lx "
+ "start=0x%lx end=0x%lx",
+ __entry->name,
+ __entry->nr_to_write,
+ __entry->pages_skipped,
+ __entry->sync_mode,
+ __entry->for_kupdate,
+ __entry->for_background,
+ __entry->for_reclaim,
+ __entry->range_cyclic,
+ __entry->more_io,
+ __entry->older_than_this,
+ __entry->range_start,
+ __entry->range_end)
+)
+
+#define DEFINE_WBC_EVENT(name) \
+DEFINE_EVENT(wbc_class, name, \
+ TP_PROTO(struct writeback_control *wbc, struct backing_dev_info *bdi), \
+ TP_ARGS(wbc, bdi))
+DEFINE_WBC_EVENT(wbc_writeback_start);
+DEFINE_WBC_EVENT(wbc_writeback_written);
+DEFINE_WBC_EVENT(wbc_writeback_wait);
+DEFINE_WBC_EVENT(wbc_balance_dirty_start);
+DEFINE_WBC_EVENT(wbc_balance_dirty_written);
+DEFINE_WBC_EVENT(wbc_balance_dirty_wait);
+DEFINE_WBC_EVENT(wbc_writepage);
+
+#endif /* _TRACE_WRITEBACK_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
--- /dev/null
+/*
+ * Defines for Mobile Industry Processor Interface (MIPI(R))
+ * Display Working Group standards: DSI, DCS, DBI, DPI
+ *
+ * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ * Copyright (C) 2006 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef MIPI_DISPLAY_H
+#define MIPI_DISPLAY_H
+
+/* MIPI DSI Processor-to-Peripheral transaction types */
+enum {
+ MIPI_DSI_V_SYNC_START = 0x01,
+ MIPI_DSI_V_SYNC_END = 0x11,
+ MIPI_DSI_H_SYNC_START = 0x21,
+ MIPI_DSI_H_SYNC_END = 0x31,
+
+ MIPI_DSI_COLOR_MODE_OFF = 0x02,
+ MIPI_DSI_COLOR_MODE_ON = 0x12,
+ MIPI_DSI_SHUTDOWN_PERIPHERAL = 0x22,
+ MIPI_DSI_TURN_ON_PERIPHERAL = 0x32,
+
+ MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM = 0x03,
+ MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM = 0x13,
+ MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM = 0x23,
+
+ MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM = 0x04,
+ MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM = 0x14,
+ MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM = 0x24,
+
+ MIPI_DSI_DCS_SHORT_WRITE = 0x05,
+ MIPI_DSI_DCS_SHORT_WRITE_PARAM = 0x15,
+
+ MIPI_DSI_DCS_READ = 0x06,
+
+ MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE = 0x37,
+
+ MIPI_DSI_END_OF_TRANSMISSION = 0x08,
+
+ MIPI_DSI_NULL_PACKET = 0x09,
+ MIPI_DSI_BLANKING_PACKET = 0x19,
+ MIPI_DSI_GENERIC_LONG_WRITE = 0x29,
+ MIPI_DSI_DCS_LONG_WRITE = 0x39,
+
+ MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20 = 0x0c,
+ MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24 = 0x1c,
+ MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16 = 0x2c,
+
+ MIPI_DSI_PACKED_PIXEL_STREAM_30 = 0x0d,
+ MIPI_DSI_PACKED_PIXEL_STREAM_36 = 0x1d,
+ MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12 = 0x3d,
+
+ MIPI_DSI_PACKED_PIXEL_STREAM_16 = 0x0e,
+ MIPI_DSI_PACKED_PIXEL_STREAM_18 = 0x1e,
+ MIPI_DSI_PIXEL_STREAM_3BYTE_18 = 0x2e,
+ MIPI_DSI_PACKED_PIXEL_STREAM_24 = 0x3e,
+};
+
+/* MIPI DSI Peripheral-to-Processor transaction types */
+enum {
+ MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT = 0x02,
+ MIPI_DSI_RX_END_OF_TRANSMISSION = 0x08,
+ MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE = 0x11,
+ MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE = 0x12,
+ MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE = 0x1a,
+ MIPI_DSI_RX_DCS_LONG_READ_RESPONSE = 0x1c,
+ MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE = 0x21,
+ MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE = 0x22,
+};
+
+/* MIPI DCS commands */
+enum {
+ MIPI_DCS_NOP = 0x00,
+ MIPI_DCS_SOFT_RESET = 0x01,
+ MIPI_DCS_GET_DISPLAY_ID = 0x04,
+ MIPI_DCS_GET_RED_CHANNEL = 0x06,
+ MIPI_DCS_GET_GREEN_CHANNEL = 0x07,
+ MIPI_DCS_GET_BLUE_CHANNEL = 0x08,
+ MIPI_DCS_GET_DISPLAY_STATUS = 0x09,
+ MIPI_DCS_GET_POWER_MODE = 0x0A,
+ MIPI_DCS_GET_ADDRESS_MODE = 0x0B,
+ MIPI_DCS_GET_PIXEL_FORMAT = 0x0C,
+ MIPI_DCS_GET_DISPLAY_MODE = 0x0D,
+ MIPI_DCS_GET_SIGNAL_MODE = 0x0E,
+ MIPI_DCS_GET_DIAGNOSTIC_RESULT = 0x0F,
+ MIPI_DCS_ENTER_SLEEP_MODE = 0x10,
+ MIPI_DCS_EXIT_SLEEP_MODE = 0x11,
+ MIPI_DCS_ENTER_PARTIAL_MODE = 0x12,
+ MIPI_DCS_ENTER_NORMAL_MODE = 0x13,
+ MIPI_DCS_EXIT_INVERT_MODE = 0x20,
+ MIPI_DCS_ENTER_INVERT_MODE = 0x21,
+ MIPI_DCS_SET_GAMMA_CURVE = 0x26,
+ MIPI_DCS_SET_DISPLAY_OFF = 0x28,
+ MIPI_DCS_SET_DISPLAY_ON = 0x29,
+ MIPI_DCS_SET_COLUMN_ADDRESS = 0x2A,
+ MIPI_DCS_SET_PAGE_ADDRESS = 0x2B,
+ MIPI_DCS_WRITE_MEMORY_START = 0x2C,
+ MIPI_DCS_WRITE_LUT = 0x2D,
+ MIPI_DCS_READ_MEMORY_START = 0x2E,
+ MIPI_DCS_SET_PARTIAL_AREA = 0x30,
+ MIPI_DCS_SET_SCROLL_AREA = 0x33,
+ MIPI_DCS_SET_TEAR_OFF = 0x34,
+ MIPI_DCS_SET_TEAR_ON = 0x35,
+ MIPI_DCS_SET_ADDRESS_MODE = 0x36,
+ MIPI_DCS_SET_SCROLL_START = 0x37,
+ MIPI_DCS_EXIT_IDLE_MODE = 0x38,
+ MIPI_DCS_ENTER_IDLE_MODE = 0x39,
+ MIPI_DCS_SET_PIXEL_FORMAT = 0x3A,
+ MIPI_DCS_WRITE_MEMORY_CONTINUE = 0x3C,
+ MIPI_DCS_READ_MEMORY_CONTINUE = 0x3E,
+ MIPI_DCS_SET_TEAR_SCANLINE = 0x44,
+ MIPI_DCS_GET_SCANLINE = 0x45,
+ MIPI_DCS_READ_DDB_START = 0xA1,
+ MIPI_DCS_READ_DDB_CONTINUE = 0xA8,
+};
+
+/* MIPI DCS pixel formats */
+#define MIPI_DCS_PIXEL_FMT_24BIT 7
+#define MIPI_DCS_PIXEL_FMT_18BIT 6
+#define MIPI_DCS_PIXEL_FMT_16BIT 5
+#define MIPI_DCS_PIXEL_FMT_12BIT 3
+#define MIPI_DCS_PIXEL_FMT_8BIT 2
+#define MIPI_DCS_PIXEL_FMT_3BIT 1
+
+#endif
--- /dev/null
+/*
+ * Public SH-mobile MIPI DSI header
+ *
+ * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef VIDEO_SH_MIPI_DSI_H
+#define VIDEO_SH_MIPI_DSI_H
+
+enum sh_mipi_dsi_data_fmt {
+ MIPI_RGB888,
+ MIPI_RGB565,
+ MIPI_RGB666_LP,
+ MIPI_RGB666,
+ MIPI_BGR888,
+ MIPI_BGR565,
+ MIPI_BGR666_LP,
+ MIPI_BGR666,
+ MIPI_YUYV,
+ MIPI_UYVY,
+ MIPI_YUV420_L,
+ MIPI_YUV420,
+};
+
+struct sh_mobile_lcdc_chan_cfg;
+
+struct sh_mipi_dsi_info {
+ enum sh_mipi_dsi_data_fmt data_format;
+ struct sh_mobile_lcdc_chan_cfg *lcd_chan;
+};
+
+#endif
--- /dev/null
+/*
+ * SH-Mobile High-Definition Multimedia Interface (HDMI)
+ *
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef SH_MOBILE_HDMI_H
+#define SH_MOBILE_HDMI_H
+
+struct sh_mobile_lcdc_chan_cfg;
+struct device;
+
+struct sh_mobile_hdmi_info {
+ struct sh_mobile_lcdc_chan_cfg *lcd_chan;
+ struct device *lcd_dev;
+};
+
+#endif
#include <linux/fb.h>
-enum { RGB8, /* 24bpp, 8:8:8 */
- RGB9, /* 18bpp, 9:9 */
- RGB12A, /* 24bpp, 12:12 */
- RGB12B, /* 12bpp */
- RGB16, /* 16bpp */
- RGB18, /* 18bpp */
- RGB24, /* 24bpp */
- SYS8A, /* 24bpp, 8:8:8 */
- SYS8B, /* 18bpp, 8:8:2 */
- SYS8C, /* 18bpp, 2:8:8 */
- SYS8D, /* 16bpp, 8:8 */
- SYS9, /* 18bpp, 9:9 */
- SYS12, /* 24bpp, 12:12 */
- SYS16A, /* 16bpp */
- SYS16B, /* 18bpp, 16:2 */
- SYS16C, /* 18bpp, 2:16 */
- SYS18, /* 18bpp */
- SYS24 };/* 24bpp */
+enum {
+ RGB8, /* 24bpp, 8:8:8 */
+ RGB9, /* 18bpp, 9:9 */
+ RGB12A, /* 24bpp, 12:12 */
+ RGB12B, /* 12bpp */
+ RGB16, /* 16bpp */
+ RGB18, /* 18bpp */
+ RGB24, /* 24bpp */
+ YUV422, /* 16bpp */
+ SYS8A, /* 24bpp, 8:8:8 */
+ SYS8B, /* 18bpp, 8:8:2 */
+ SYS8C, /* 18bpp, 2:8:8 */
+ SYS8D, /* 16bpp, 8:8 */
+ SYS9, /* 18bpp, 9:9 */
+ SYS12, /* 24bpp, 12:12 */
+ SYS16A, /* 16bpp */
+ SYS16B, /* 18bpp, 16:2 */
+ SYS16C, /* 18bpp, 2:16 */
+ SYS18, /* 18bpp */
+ SYS24, /* 24bpp */
+};
enum { LCDC_CHAN_DISABLED = 0,
LCDC_CHAN_MAINLCD,
struct sh_mobile_lcdc_sys_bus_ops *sys_ops);
void (*start_transfer)(void *board_data, void *sys_ops_handle,
struct sh_mobile_lcdc_sys_bus_ops *sys_ops);
- void (*display_on)(void *board_data);
+ void (*display_on)(void *board_data, struct fb_info *info);
void (*display_off)(void *board_data);
};
could in turn add some fork/exit overhead.
config CGROUP_MEM_RES_CTLR_SWAP
- bool "Memory Resource Controller Swap Extension(EXPERIMENTAL)"
- depends on CGROUP_MEM_RES_CTLR && SWAP && EXPERIMENTAL
+ bool "Memory Resource Controller Swap Extension"
+ depends on CGROUP_MEM_RES_CTLR && SWAP
help
Add swap management feature to memory resource controller. When you
enable this, you can limit mem+swap usage per cgroup. In other words,
if (opts->release_agent)
return -EINVAL;
opts->release_agent =
- kstrndup(token + 14, PATH_MAX, GFP_KERNEL);
+ kstrndup(token + 14, PATH_MAX - 1, GFP_KERNEL);
if (!opts->release_agent)
return -ENOMEM;
} else if (!strncmp(token, "name=", 5)) {
if (opts->name)
return -EINVAL;
opts->name = kstrndup(name,
- MAX_CGROUP_ROOT_NAMELEN,
+ MAX_CGROUP_ROOT_NAMELEN - 1,
GFP_KERNEL);
if (!opts->name)
return -ENOMEM;
struct task_struct *p, *n, *reaper;
LIST_HEAD(dead_children);
- exit_ptrace(father);
-
write_lock_irq(&tasklist_lock);
+ /*
+ * Note that exit_ptrace() and find_new_reaper() might
+ * drop tasklist_lock and reacquire it.
+ */
+ exit_ptrace(father);
reaper = find_new_reaper(father);
list_for_each_entry_safe(p, n, &father->children, sibling) {
image->nr_segments = nr_segments;
segment_bytes = nr_segments * sizeof(*segments);
result = copy_from_user(image->segment, segments, segment_bytes);
- if (result)
+ if (result) {
+ result = -EFAULT;
goto out;
+ }
/*
* Verify we have good destination addresses. The caller is
result = copy_from_user(ptr, buf, uchunk);
kunmap(page);
if (result) {
- result = (result < 0) ? result : -EIO;
+ result = -EFAULT;
goto out;
}
ubytes -= uchunk;
kexec_flush_icache_page(page);
kunmap(page);
if (result) {
- result = (result < 0) ? result : -EIO;
+ result = -EFAULT;
goto out;
}
ubytes -= uchunk;
/*
- * A generic kernel FIFO implementation.
+ * A generic kernel FIFO implementation
*
- * Copyright (C) 2009 Stefani Seibold <stefani@seibold.net>
- * Copyright (C) 2004 Stelian Pop <stelian@popies.net>
+ * Copyright (C) 2009/2010 Stefani Seibold <stefani@seibold.net>
*
* 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
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/err.h>
-#include <linux/kfifo.h>
#include <linux/log2.h>
#include <linux/uaccess.h>
+#include <linux/kfifo.h>
-static void _kfifo_init(struct kfifo *fifo, void *buffer,
- unsigned int size)
-{
- fifo->buffer = buffer;
- fifo->size = size;
-
- kfifo_reset(fifo);
-}
-
-/**
- * kfifo_init - initialize a FIFO using a preallocated buffer
- * @fifo: the fifo to assign the buffer
- * @buffer: the preallocated buffer to be used.
- * @size: the size of the internal buffer, this has to be a power of 2.
- *
+/*
+ * internal helper to calculate the unused elements in a fifo
*/
-void kfifo_init(struct kfifo *fifo, void *buffer, unsigned int size)
+static inline unsigned int kfifo_unused(struct __kfifo *fifo)
{
- /* size must be a power of 2 */
- BUG_ON(!is_power_of_2(size));
-
- _kfifo_init(fifo, buffer, size);
+ return (fifo->mask + 1) - (fifo->in - fifo->out);
}
-EXPORT_SYMBOL(kfifo_init);
-/**
- * kfifo_alloc - allocates a new FIFO internal buffer
- * @fifo: the fifo to assign then new buffer
- * @size: the size of the buffer to be allocated, this have to be a power of 2.
- * @gfp_mask: get_free_pages mask, passed to kmalloc()
- *
- * This function dynamically allocates a new fifo internal buffer
- *
- * The size will be rounded-up to a power of 2.
- * The buffer will be release with kfifo_free().
- * Return 0 if no error, otherwise the an error code
- */
-int kfifo_alloc(struct kfifo *fifo, unsigned int size, gfp_t gfp_mask)
+int __kfifo_alloc(struct __kfifo *fifo, unsigned int size,
+ size_t esize, gfp_t gfp_mask)
{
- unsigned char *buffer;
-
/*
- * round up to the next power of 2, since our 'let the indices
+ * round down to the next power of 2, since our 'let the indices
* wrap' technique works only in this case.
*/
- if (!is_power_of_2(size)) {
- BUG_ON(size > 0x80000000);
- size = roundup_pow_of_two(size);
+ if (!is_power_of_2(size))
+ size = rounddown_pow_of_two(size);
+
+ fifo->in = 0;
+ fifo->out = 0;
+ fifo->esize = esize;
+
+ if (size < 2) {
+ fifo->data = NULL;
+ fifo->mask = 0;
+ return -EINVAL;
}
- buffer = kmalloc(size, gfp_mask);
- if (!buffer) {
- _kfifo_init(fifo, NULL, 0);
+ fifo->data = kmalloc(size * esize, gfp_mask);
+
+ if (!fifo->data) {
+ fifo->mask = 0;
return -ENOMEM;
}
-
- _kfifo_init(fifo, buffer, size);
+ fifo->mask = size - 1;
return 0;
}
-EXPORT_SYMBOL(kfifo_alloc);
+EXPORT_SYMBOL(__kfifo_alloc);
-/**
- * kfifo_free - frees the FIFO internal buffer
- * @fifo: the fifo to be freed.
- */
-void kfifo_free(struct kfifo *fifo)
+void __kfifo_free(struct __kfifo *fifo)
{
- kfree(fifo->buffer);
- _kfifo_init(fifo, NULL, 0);
+ kfree(fifo->data);
+ fifo->in = 0;
+ fifo->out = 0;
+ fifo->esize = 0;
+ fifo->data = NULL;
+ fifo->mask = 0;
}
-EXPORT_SYMBOL(kfifo_free);
+EXPORT_SYMBOL(__kfifo_free);
-/**
- * kfifo_skip - skip output data
- * @fifo: the fifo to be used.
- * @len: number of bytes to skip
- */
-void kfifo_skip(struct kfifo *fifo, unsigned int len)
+int __kfifo_init(struct __kfifo *fifo, void *buffer,
+ unsigned int size, size_t esize)
{
- if (len < kfifo_len(fifo)) {
- __kfifo_add_out(fifo, len);
- return;
+ size /= esize;
+
+ if (!is_power_of_2(size))
+ size = rounddown_pow_of_two(size);
+
+ fifo->in = 0;
+ fifo->out = 0;
+ fifo->esize = esize;
+ fifo->data = buffer;
+
+ if (size < 2) {
+ fifo->mask = 0;
+ return -EINVAL;
}
- kfifo_reset_out(fifo);
+ fifo->mask = size - 1;
+
+ return 0;
}
-EXPORT_SYMBOL(kfifo_skip);
+EXPORT_SYMBOL(__kfifo_init);
-static inline void __kfifo_in_data(struct kfifo *fifo,
- const void *from, unsigned int len, unsigned int off)
+static void kfifo_copy_in(struct __kfifo *fifo, const void *src,
+ unsigned int len, unsigned int off)
{
+ unsigned int size = fifo->mask + 1;
+ unsigned int esize = fifo->esize;
unsigned int l;
+ off &= fifo->mask;
+ if (esize != 1) {
+ off *= esize;
+ size *= esize;
+ len *= esize;
+ }
+ l = min(len, size - off);
+
+ memcpy(fifo->data + off, src, l);
+ memcpy(fifo->data, src + l, len - l);
/*
- * Ensure that we sample the fifo->out index -before- we
- * start putting bytes into the kfifo.
+ * make sure that the data in the fifo is up to date before
+ * incrementing the fifo->in index counter
*/
+ smp_wmb();
+}
- smp_mb();
-
- off = __kfifo_off(fifo, fifo->in + off);
+unsigned int __kfifo_in(struct __kfifo *fifo,
+ const void *buf, unsigned int len)
+{
+ unsigned int l;
- /* first put the data starting from fifo->in to buffer end */
- l = min(len, fifo->size - off);
- memcpy(fifo->buffer + off, from, l);
+ l = kfifo_unused(fifo);
+ if (len > l)
+ len = l;
- /* then put the rest (if any) at the beginning of the buffer */
- memcpy(fifo->buffer, from + l, len - l);
+ kfifo_copy_in(fifo, buf, len, fifo->in);
+ fifo->in += len;
+ return len;
}
+EXPORT_SYMBOL(__kfifo_in);
-static inline void __kfifo_out_data(struct kfifo *fifo,
- void *to, unsigned int len, unsigned int off)
+static void kfifo_copy_out(struct __kfifo *fifo, void *dst,
+ unsigned int len, unsigned int off)
{
+ unsigned int size = fifo->mask + 1;
+ unsigned int esize = fifo->esize;
unsigned int l;
+ off &= fifo->mask;
+ if (esize != 1) {
+ off *= esize;
+ size *= esize;
+ len *= esize;
+ }
+ l = min(len, size - off);
+
+ memcpy(dst, fifo->data + off, l);
+ memcpy(dst + l, fifo->data, len - l);
/*
- * Ensure that we sample the fifo->in index -before- we
- * start removing bytes from the kfifo.
+ * make sure that the data is copied before
+ * incrementing the fifo->out index counter
*/
+ smp_wmb();
+}
- smp_rmb();
+unsigned int __kfifo_out_peek(struct __kfifo *fifo,
+ void *buf, unsigned int len)
+{
+ unsigned int l;
- off = __kfifo_off(fifo, fifo->out + off);
+ l = fifo->in - fifo->out;
+ if (len > l)
+ len = l;
- /* first get the data from fifo->out until the end of the buffer */
- l = min(len, fifo->size - off);
- memcpy(to, fifo->buffer + off, l);
+ kfifo_copy_out(fifo, buf, len, fifo->out);
+ return len;
+}
+EXPORT_SYMBOL(__kfifo_out_peek);
- /* then get the rest (if any) from the beginning of the buffer */
- memcpy(to + l, fifo->buffer, len - l);
+unsigned int __kfifo_out(struct __kfifo *fifo,
+ void *buf, unsigned int len)
+{
+ len = __kfifo_out_peek(fifo, buf, len);
+ fifo->out += len;
+ return len;
}
+EXPORT_SYMBOL(__kfifo_out);
-static inline int __kfifo_from_user_data(struct kfifo *fifo,
- const void __user *from, unsigned int len, unsigned int off,
- unsigned *lenout)
+static unsigned long kfifo_copy_from_user(struct __kfifo *fifo,
+ const void __user *from, unsigned int len, unsigned int off,
+ unsigned int *copied)
{
+ unsigned int size = fifo->mask + 1;
+ unsigned int esize = fifo->esize;
unsigned int l;
- int ret;
+ unsigned long ret;
+ off &= fifo->mask;
+ if (esize != 1) {
+ off *= esize;
+ size *= esize;
+ len *= esize;
+ }
+ l = min(len, size - off);
+
+ ret = copy_from_user(fifo->data + off, from, l);
+ if (unlikely(ret))
+ ret = DIV_ROUND_UP(ret + len - l, esize);
+ else {
+ ret = copy_from_user(fifo->data, from + l, len - l);
+ if (unlikely(ret))
+ ret = DIV_ROUND_UP(ret, esize);
+ }
/*
- * Ensure that we sample the fifo->out index -before- we
- * start putting bytes into the kfifo.
+ * make sure that the data in the fifo is up to date before
+ * incrementing the fifo->in index counter
*/
+ smp_wmb();
+ *copied = len - ret;
+ /* return the number of elements which are not copied */
+ return ret;
+}
- smp_mb();
+int __kfifo_from_user(struct __kfifo *fifo, const void __user *from,
+ unsigned long len, unsigned int *copied)
+{
+ unsigned int l;
+ unsigned long ret;
+ unsigned int esize = fifo->esize;
+ int err;
- off = __kfifo_off(fifo, fifo->in + off);
+ if (esize != 1)
+ len /= esize;
- /* first put the data starting from fifo->in to buffer end */
- l = min(len, fifo->size - off);
- ret = copy_from_user(fifo->buffer + off, from, l);
- if (unlikely(ret)) {
- *lenout = ret;
- return -EFAULT;
- }
- *lenout = l;
+ l = kfifo_unused(fifo);
+ if (len > l)
+ len = l;
- /* then put the rest (if any) at the beginning of the buffer */
- ret = copy_from_user(fifo->buffer, from + l, len - l);
- *lenout += ret ? ret : len - l;
- return ret ? -EFAULT : 0;
+ ret = kfifo_copy_from_user(fifo, from, len, fifo->in, copied);
+ if (unlikely(ret)) {
+ len -= ret;
+ err = -EFAULT;
+ } else
+ err = 0;
+ fifo->in += len;
+ return err;
}
+EXPORT_SYMBOL(__kfifo_from_user);
-static inline int __kfifo_to_user_data(struct kfifo *fifo,
- void __user *to, unsigned int len, unsigned int off, unsigned *lenout)
+static unsigned long kfifo_copy_to_user(struct __kfifo *fifo, void __user *to,
+ unsigned int len, unsigned int off, unsigned int *copied)
{
unsigned int l;
- int ret;
-
+ unsigned long ret;
+ unsigned int size = fifo->mask + 1;
+ unsigned int esize = fifo->esize;
+
+ off &= fifo->mask;
+ if (esize != 1) {
+ off *= esize;
+ size *= esize;
+ len *= esize;
+ }
+ l = min(len, size - off);
+
+ ret = copy_to_user(to, fifo->data + off, l);
+ if (unlikely(ret))
+ ret = DIV_ROUND_UP(ret + len - l, esize);
+ else {
+ ret = copy_to_user(to + l, fifo->data, len - l);
+ if (unlikely(ret))
+ ret = DIV_ROUND_UP(ret, esize);
+ }
/*
- * Ensure that we sample the fifo->in index -before- we
- * start removing bytes from the kfifo.
+ * make sure that the data is copied before
+ * incrementing the fifo->out index counter
*/
+ smp_wmb();
+ *copied = len - ret;
+ /* return the number of elements which are not copied */
+ return ret;
+}
- smp_rmb();
+int __kfifo_to_user(struct __kfifo *fifo, void __user *to,
+ unsigned long len, unsigned int *copied)
+{
+ unsigned int l;
+ unsigned long ret;
+ unsigned int esize = fifo->esize;
+ int err;
- off = __kfifo_off(fifo, fifo->out + off);
+ if (esize != 1)
+ len /= esize;
- /* first get the data from fifo->out until the end of the buffer */
- l = min(len, fifo->size - off);
- ret = copy_to_user(to, fifo->buffer + off, l);
- *lenout = l;
+ l = fifo->in - fifo->out;
+ if (len > l)
+ len = l;
+ ret = kfifo_copy_to_user(fifo, to, len, fifo->out, copied);
if (unlikely(ret)) {
- *lenout -= ret;
- return -EFAULT;
- }
+ len -= ret;
+ err = -EFAULT;
+ } else
+ err = 0;
+ fifo->out += len;
+ return err;
+}
+EXPORT_SYMBOL(__kfifo_to_user);
- /* then get the rest (if any) from the beginning of the buffer */
- len -= l;
- ret = copy_to_user(to + l, fifo->buffer, len);
- if (unlikely(ret)) {
- *lenout += len - ret;
- return -EFAULT;
+static int setup_sgl_buf(struct scatterlist *sgl, void *buf,
+ int nents, unsigned int len)
+{
+ int n;
+ unsigned int l;
+ unsigned int off;
+ struct page *page;
+
+ if (!nents)
+ return 0;
+
+ if (!len)
+ return 0;
+
+ n = 0;
+ page = virt_to_page(buf);
+ off = offset_in_page(buf);
+ l = 0;
+
+ while (len >= l + PAGE_SIZE - off) {
+ struct page *npage;
+
+ l += PAGE_SIZE;
+ buf += PAGE_SIZE;
+ npage = virt_to_page(buf);
+ if (page_to_phys(page) != page_to_phys(npage) - l) {
+ sgl->page_link = 0;
+ sg_set_page(sgl++, page, l - off, off);
+ if (++n == nents)
+ return n;
+ page = npage;
+ len -= l - off;
+ l = off = 0;
+ }
}
- *lenout += len;
- return 0;
+ sgl->page_link = 0;
+ sg_set_page(sgl++, page, len, off);
+ return n + 1;
}
-unsigned int __kfifo_in_n(struct kfifo *fifo,
- const void *from, unsigned int len, unsigned int recsize)
+static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl,
+ int nents, unsigned int len, unsigned int off)
{
- if (kfifo_avail(fifo) < len + recsize)
- return len + 1;
+ unsigned int size = fifo->mask + 1;
+ unsigned int esize = fifo->esize;
+ unsigned int l;
+ unsigned int n;
- __kfifo_in_data(fifo, from, len, recsize);
- return 0;
+ off &= fifo->mask;
+ if (esize != 1) {
+ off *= esize;
+ size *= esize;
+ len *= esize;
+ }
+ l = min(len, size - off);
+
+ n = setup_sgl_buf(sgl, fifo->data + off, nents, l);
+ n += setup_sgl_buf(sgl + n, fifo->data, nents - n, len - l);
+
+ if (n)
+ sg_mark_end(sgl + n - 1);
+ return n;
}
-EXPORT_SYMBOL(__kfifo_in_n);
-/**
- * kfifo_in - puts some data into the FIFO
- * @fifo: the fifo to be used.
- * @from: the data to be added.
- * @len: the length of the data to be added.
- *
- * This function copies at most @len bytes from the @from buffer into
- * the FIFO depending on the free space, and returns the number of
- * bytes copied.
- *
- * Note that with only one concurrent reader and one concurrent
- * writer, you don't need extra locking to use these functions.
- */
-unsigned int kfifo_in(struct kfifo *fifo, const void *from,
- unsigned int len)
+unsigned int __kfifo_dma_in_prepare(struct __kfifo *fifo,
+ struct scatterlist *sgl, int nents, unsigned int len)
{
- len = min(kfifo_avail(fifo), len);
+ unsigned int l;
- __kfifo_in_data(fifo, from, len, 0);
- __kfifo_add_in(fifo, len);
- return len;
+ l = kfifo_unused(fifo);
+ if (len > l)
+ len = l;
+
+ return setup_sgl(fifo, sgl, nents, len, fifo->in);
}
-EXPORT_SYMBOL(kfifo_in);
+EXPORT_SYMBOL(__kfifo_dma_in_prepare);
-unsigned int __kfifo_in_generic(struct kfifo *fifo,
- const void *from, unsigned int len, unsigned int recsize)
+unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo,
+ struct scatterlist *sgl, int nents, unsigned int len)
{
- return __kfifo_in_rec(fifo, from, len, recsize);
+ unsigned int l;
+
+ l = fifo->in - fifo->out;
+ if (len > l)
+ len = l;
+
+ return setup_sgl(fifo, sgl, nents, len, fifo->out);
}
-EXPORT_SYMBOL(__kfifo_in_generic);
+EXPORT_SYMBOL(__kfifo_dma_out_prepare);
-unsigned int __kfifo_out_n(struct kfifo *fifo,
- void *to, unsigned int len, unsigned int recsize)
+unsigned int __kfifo_max_r(unsigned int len, size_t recsize)
{
- if (kfifo_len(fifo) < len + recsize)
- return len;
+ unsigned int max = (1 << (recsize << 3)) - 1;
- __kfifo_out_data(fifo, to, len, recsize);
- __kfifo_add_out(fifo, len + recsize);
- return 0;
+ if (len > max)
+ return max;
+ return len;
}
-EXPORT_SYMBOL(__kfifo_out_n);
-/**
- * kfifo_out - gets some data from the FIFO
- * @fifo: the fifo to be used.
- * @to: where the data must be copied.
- * @len: the size of the destination buffer.
- *
- * This function copies at most @len bytes from the FIFO into the
- * @to buffer and returns the number of copied bytes.
- *
- * Note that with only one concurrent reader and one concurrent
- * writer, you don't need extra locking to use these functions.
+#define __KFIFO_PEEK(data, out, mask) \
+ ((data)[(out) & (mask)])
+/*
+ * __kfifo_peek_n internal helper function for determinate the length of
+ * the next record in the fifo
*/
-unsigned int kfifo_out(struct kfifo *fifo, void *to, unsigned int len)
+static unsigned int __kfifo_peek_n(struct __kfifo *fifo, size_t recsize)
{
- len = min(kfifo_len(fifo), len);
+ unsigned int l;
+ unsigned int mask = fifo->mask;
+ unsigned char *data = fifo->data;
- __kfifo_out_data(fifo, to, len, 0);
- __kfifo_add_out(fifo, len);
+ l = __KFIFO_PEEK(data, fifo->out, mask);
- return len;
+ if (--recsize)
+ l |= __KFIFO_PEEK(data, fifo->out + 1, mask) << 8;
+
+ return l;
}
-EXPORT_SYMBOL(kfifo_out);
-
-/**
- * kfifo_out_peek - copy some data from the FIFO, but do not remove it
- * @fifo: the fifo to be used.
- * @to: where the data must be copied.
- * @len: the size of the destination buffer.
- * @offset: offset into the fifo
- *
- * This function copies at most @len bytes at @offset from the FIFO
- * into the @to buffer and returns the number of copied bytes.
- * The data is not removed from the FIFO.
+
+#define __KFIFO_POKE(data, in, mask, val) \
+ ( \
+ (data)[(in) & (mask)] = (unsigned char)(val) \
+ )
+
+/*
+ * __kfifo_poke_n internal helper function for storeing the length of
+ * the record into the fifo
*/
-unsigned int kfifo_out_peek(struct kfifo *fifo, void *to, unsigned int len,
- unsigned offset)
+static void __kfifo_poke_n(struct __kfifo *fifo, unsigned int n, size_t recsize)
{
- len = min(kfifo_len(fifo), len + offset);
+ unsigned int mask = fifo->mask;
+ unsigned char *data = fifo->data;
- __kfifo_out_data(fifo, to, len, offset);
- return len;
+ __KFIFO_POKE(data, fifo->in, mask, n);
+
+ if (recsize > 1)
+ __KFIFO_POKE(data, fifo->in + 1, mask, n >> 8);
}
-EXPORT_SYMBOL(kfifo_out_peek);
-unsigned int __kfifo_out_generic(struct kfifo *fifo,
- void *to, unsigned int len, unsigned int recsize,
- unsigned int *total)
+unsigned int __kfifo_len_r(struct __kfifo *fifo, size_t recsize)
{
- return __kfifo_out_rec(fifo, to, len, recsize, total);
+ return __kfifo_peek_n(fifo, recsize);
}
-EXPORT_SYMBOL(__kfifo_out_generic);
+EXPORT_SYMBOL(__kfifo_len_r);
-unsigned int __kfifo_from_user_n(struct kfifo *fifo,
- const void __user *from, unsigned int len, unsigned int recsize)
+unsigned int __kfifo_in_r(struct __kfifo *fifo, const void *buf,
+ unsigned int len, size_t recsize)
{
- unsigned total;
+ if (len + recsize > kfifo_unused(fifo))
+ return 0;
- if (kfifo_avail(fifo) < len + recsize)
- return len + 1;
+ __kfifo_poke_n(fifo, len, recsize);
- __kfifo_from_user_data(fifo, from, len, recsize, &total);
- return total;
+ kfifo_copy_in(fifo, buf, len, fifo->in + recsize);
+ fifo->in += len + recsize;
+ return len;
}
-EXPORT_SYMBOL(__kfifo_from_user_n);
-
-/**
- * kfifo_from_user - puts some data from user space into the FIFO
- * @fifo: the fifo to be used.
- * @from: pointer to the data to be added.
- * @len: the length of the data to be added.
- * @total: the actual returned data length.
- *
- * This function copies at most @len bytes from the @from into the
- * FIFO depending and returns -EFAULT/0.
- *
- * Note that with only one concurrent reader and one concurrent
- * writer, you don't need extra locking to use these functions.
- */
-int kfifo_from_user(struct kfifo *fifo,
- const void __user *from, unsigned int len, unsigned *total)
+EXPORT_SYMBOL(__kfifo_in_r);
+
+static unsigned int kfifo_out_copy_r(struct __kfifo *fifo,
+ void *buf, unsigned int len, size_t recsize, unsigned int *n)
{
- int ret;
- len = min(kfifo_avail(fifo), len);
- ret = __kfifo_from_user_data(fifo, from, len, 0, total);
- if (ret)
- return ret;
- __kfifo_add_in(fifo, len);
- return 0;
+ *n = __kfifo_peek_n(fifo, recsize);
+
+ if (len > *n)
+ len = *n;
+
+ kfifo_copy_out(fifo, buf, len, fifo->out + recsize);
+ return len;
+}
+
+unsigned int __kfifo_out_peek_r(struct __kfifo *fifo, void *buf,
+ unsigned int len, size_t recsize)
+{
+ unsigned int n;
+
+ if (fifo->in == fifo->out)
+ return 0;
+
+ return kfifo_out_copy_r(fifo, buf, len, recsize, &n);
}
-EXPORT_SYMBOL(kfifo_from_user);
+EXPORT_SYMBOL(__kfifo_out_peek_r);
-unsigned int __kfifo_from_user_generic(struct kfifo *fifo,
- const void __user *from, unsigned int len, unsigned int recsize)
+unsigned int __kfifo_out_r(struct __kfifo *fifo, void *buf,
+ unsigned int len, size_t recsize)
{
- return __kfifo_from_user_rec(fifo, from, len, recsize);
+ unsigned int n;
+
+ if (fifo->in == fifo->out)
+ return 0;
+
+ len = kfifo_out_copy_r(fifo, buf, len, recsize, &n);
+ fifo->out += n + recsize;
+ return len;
}
-EXPORT_SYMBOL(__kfifo_from_user_generic);
+EXPORT_SYMBOL(__kfifo_out_r);
-unsigned int __kfifo_to_user_n(struct kfifo *fifo,
- void __user *to, unsigned int len, unsigned int reclen,
- unsigned int recsize)
+int __kfifo_from_user_r(struct __kfifo *fifo, const void __user *from,
+ unsigned long len, unsigned int *copied, size_t recsize)
{
- unsigned int ret, total;
+ unsigned long ret;
- if (kfifo_len(fifo) < reclen + recsize)
- return len;
+ len = __kfifo_max_r(len, recsize);
- ret = __kfifo_to_user_data(fifo, to, reclen, recsize, &total);
+ if (len + recsize > kfifo_unused(fifo)) {
+ *copied = 0;
+ return 0;
+ }
- if (likely(ret == 0))
- __kfifo_add_out(fifo, reclen + recsize);
+ __kfifo_poke_n(fifo, len, recsize);
- return total;
+ ret = kfifo_copy_from_user(fifo, from, len, fifo->in + recsize, copied);
+ if (unlikely(ret)) {
+ *copied = 0;
+ return -EFAULT;
+ }
+ fifo->in += len + recsize;
+ return 0;
}
-EXPORT_SYMBOL(__kfifo_to_user_n);
-
-/**
- * kfifo_to_user - gets data from the FIFO and write it to user space
- * @fifo: the fifo to be used.
- * @to: where the data must be copied.
- * @len: the size of the destination buffer.
- * @lenout: pointer to output variable with copied data
- *
- * This function copies at most @len bytes from the FIFO into the
- * @to buffer and 0 or -EFAULT.
- *
- * Note that with only one concurrent reader and one concurrent
- * writer, you don't need extra locking to use these functions.
- */
-int kfifo_to_user(struct kfifo *fifo,
- void __user *to, unsigned int len, unsigned *lenout)
+EXPORT_SYMBOL(__kfifo_from_user_r);
+
+int __kfifo_to_user_r(struct __kfifo *fifo, void __user *to,
+ unsigned long len, unsigned int *copied, size_t recsize)
{
- int ret;
- len = min(kfifo_len(fifo), len);
- ret = __kfifo_to_user_data(fifo, to, len, 0, lenout);
- __kfifo_add_out(fifo, *lenout);
- return ret;
+ unsigned long ret;
+ unsigned int n;
+
+ if (fifo->in == fifo->out) {
+ *copied = 0;
+ return 0;
+ }
+
+ n = __kfifo_peek_n(fifo, recsize);
+ if (len > n)
+ len = n;
+
+ ret = kfifo_copy_to_user(fifo, to, len, fifo->out + recsize, copied);
+ if (unlikely(ret)) {
+ *copied = 0;
+ return -EFAULT;
+ }
+ fifo->out += n + recsize;
+ return 0;
}
-EXPORT_SYMBOL(kfifo_to_user);
+EXPORT_SYMBOL(__kfifo_to_user_r);
-unsigned int __kfifo_to_user_generic(struct kfifo *fifo,
- void __user *to, unsigned int len, unsigned int recsize,
- unsigned int *total)
+unsigned int __kfifo_dma_in_prepare_r(struct __kfifo *fifo,
+ struct scatterlist *sgl, int nents, unsigned int len, size_t recsize)
{
- return __kfifo_to_user_rec(fifo, to, len, recsize, total);
+ if (!nents)
+ BUG();
+
+ len = __kfifo_max_r(len, recsize);
+
+ if (len + recsize > kfifo_unused(fifo))
+ return 0;
+
+ return setup_sgl(fifo, sgl, nents, len, fifo->in + recsize);
}
-EXPORT_SYMBOL(__kfifo_to_user_generic);
+EXPORT_SYMBOL(__kfifo_dma_in_prepare_r);
-unsigned int __kfifo_peek_generic(struct kfifo *fifo, unsigned int recsize)
+void __kfifo_dma_in_finish_r(struct __kfifo *fifo,
+ unsigned int len, size_t recsize)
{
- if (recsize == 0)
- return kfifo_avail(fifo);
-
- return __kfifo_peek_n(fifo, recsize);
+ len = __kfifo_max_r(len, recsize);
+ __kfifo_poke_n(fifo, len, recsize);
+ fifo->in += len + recsize;
}
-EXPORT_SYMBOL(__kfifo_peek_generic);
+EXPORT_SYMBOL(__kfifo_dma_in_finish_r);
-void __kfifo_skip_generic(struct kfifo *fifo, unsigned int recsize)
+unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo,
+ struct scatterlist *sgl, int nents, unsigned int len, size_t recsize)
{
- __kfifo_skip_rec(fifo, recsize);
+ if (!nents)
+ BUG();
+
+ len = __kfifo_max_r(len, recsize);
+
+ if (len + recsize > fifo->in - fifo->out)
+ return 0;
+
+ return setup_sgl(fifo, sgl, nents, len, fifo->out + recsize);
}
-EXPORT_SYMBOL(__kfifo_skip_generic);
+EXPORT_SYMBOL(__kfifo_dma_out_prepare_r);
+
+void __kfifo_dma_out_finish_r(struct __kfifo *fifo, size_t recsize)
+{
+ unsigned int len;
+ len = __kfifo_peek_n(fifo, recsize);
+ fifo->out += len + recsize;
+}
+EXPORT_SYMBOL(__kfifo_dma_out_finish_r);
#include <linux/nmi.h>
#include <linux/dmi.h>
+#define PANIC_TIMER_STEP 100
+#define PANIC_BLINK_SPD 18
+
int panic_on_oops;
static unsigned long tainted_mask;
static int pause_on_oops;
EXPORT_SYMBOL(panic_notifier_list);
-/* Returns how long it waited in ms */
-long (*panic_blink)(long time);
-EXPORT_SYMBOL(panic_blink);
-
-static void panic_blink_one_second(void)
+static long no_blink(int state)
{
- static long i = 0, end;
-
- if (panic_blink) {
- end = i + MSEC_PER_SEC;
-
- while (i < end) {
- i += panic_blink(i);
- mdelay(1);
- i++;
- }
- } else {
- /*
- * When running under a hypervisor a small mdelay may get
- * rounded up to the hypervisor timeslice. For example, with
- * a 1ms in 10ms hypervisor timeslice we might inflate a
- * mdelay(1) loop by 10x.
- *
- * If we have nothing to blink, spin on 1 second calls to
- * mdelay to avoid this.
- */
- mdelay(MSEC_PER_SEC);
- }
+ return 0;
}
+/* Returns how long it waited in ms */
+long (*panic_blink)(int state);
+EXPORT_SYMBOL(panic_blink);
+
/**
* panic - halt the system
* @fmt: The text string to print
{
static char buf[1024];
va_list args;
- long i;
+ long i, i_next = 0;
+ int state = 0;
/*
* It's possible to come here directly from a panic-assertion and
bust_spinlocks(0);
+ if (!panic_blink)
+ panic_blink = no_blink;
+
if (panic_timeout > 0) {
/*
* Delay timeout seconds before rebooting the machine.
*/
printk(KERN_EMERG "Rebooting in %d seconds..", panic_timeout);
- for (i = 0; i < panic_timeout; i++) {
+ for (i = 0; i < panic_timeout * 1000; i += PANIC_TIMER_STEP) {
touch_nmi_watchdog();
- panic_blink_one_second();
+ if (i >= i_next) {
+ i += panic_blink(state ^= 1);
+ i_next = i + 3600 / PANIC_BLINK_SPD;
+ }
+ mdelay(PANIC_TIMER_STEP);
}
/*
* This will not be a clean reboot, with everything
}
#endif
local_irq_enable();
- while (1) {
+ for (i = 0; ; i += PANIC_TIMER_STEP) {
touch_softlockup_watchdog();
- panic_blink_one_second();
+ if (i >= i_next) {
+ i += panic_blink(state ^= 1);
+ i_next = i + 3600 / PANIC_BLINK_SPD;
+ }
+ mdelay(PANIC_TIMER_STEP);
}
}
}
late_initcall(init_oops_id);
-static void print_oops_end_marker(void)
+void print_oops_end_marker(void)
{
init_oops_id();
printk(KERN_WARNING "---[ end trace %016llx ]---\n",
atomic_inc(&map->nr_free);
}
+/*
+ * If we started walking pids at 'base', is 'a' seen before 'b'?
+ */
+static int pid_before(int base, int a, int b)
+{
+ /*
+ * This is the same as saying
+ *
+ * (a - base + MAXUINT) % MAXUINT < (b - base + MAXUINT) % MAXUINT
+ * and that mapping orders 'a' and 'b' with respect to 'base'.
+ */
+ return (unsigned)(a - base) < (unsigned)(b - base);
+}
+
+/*
+ * We might be racing with someone else trying to set pid_ns->last_pid.
+ * We want the winner to have the "later" value, because if the
+ * "earlier" value prevails, then a pid may get reused immediately.
+ *
+ * Since pids rollover, it is not sufficient to just pick the bigger
+ * value. We have to consider where we started counting from.
+ *
+ * 'base' is the value of pid_ns->last_pid that we observed when
+ * we started looking for a pid.
+ *
+ * 'pid' is the pid that we eventually found.
+ */
+static void set_last_pid(struct pid_namespace *pid_ns, int base, int pid)
+{
+ int prev;
+ int last_write = base;
+ do {
+ prev = last_write;
+ last_write = cmpxchg(&pid_ns->last_pid, prev, pid);
+ } while ((prev != last_write) && (pid_before(base, last_write, pid)));
+}
+
static int alloc_pidmap(struct pid_namespace *pid_ns)
{
int i, offset, max_scan, pid, last = pid_ns->last_pid;
pid = RESERVED_PIDS;
offset = pid & BITS_PER_PAGE_MASK;
map = &pid_ns->pidmap[pid/BITS_PER_PAGE];
- max_scan = (pid_max + BITS_PER_PAGE - 1)/BITS_PER_PAGE - !offset;
+ /*
+ * If last_pid points into the middle of the map->page we
+ * want to scan this bitmap block twice, the second time
+ * we start with offset == 0 (or RESERVED_PIDS).
+ */
+ max_scan = DIV_ROUND_UP(pid_max, BITS_PER_PAGE) - !offset;
for (i = 0; i <= max_scan; ++i) {
if (unlikely(!map->page)) {
void *page = kzalloc(PAGE_SIZE, GFP_KERNEL);
do {
if (!test_and_set_bit(offset, map->page)) {
atomic_dec(&map->nr_free);
- pid_ns->last_pid = pid;
+ set_last_pid(pid_ns, last, pid);
return pid;
}
offset = find_next_offset(map, offset);
pid = mk_pid(pid_ns, map, offset);
- /*
- * find_next_offset() found a bit, the pid from it
- * is in-bounds, and if we fell back to the last
- * bitmap block and the final block was the same
- * as the starting point, pid is before last_pid.
- */
- } while (offset < BITS_PER_PAGE && pid < pid_max &&
- (i != max_scan || pid < last ||
- !((last+1) & BITS_PER_PAGE_MASK)));
+ } while (offset < BITS_PER_PAGE && pid < pid_max);
}
if (map < &pid_ns->pidmap[(pid_max-1)/BITS_PER_PAGE]) {
++map;
static int submit(int rw, struct block_device *bdev, sector_t sector,
struct page *page, struct bio **bio_chain)
{
- const int bio_rw = rw | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG);
+ const int bio_rw = rw | REQ_SYNC | REQ_UNPLUG;
struct bio *bio;
bio = bio_alloc(__GFP_WAIT | __GFP_HIGH, 1);
}
/*
- * Detach all tasks we were using ptrace on.
+ * Detach all tasks we were using ptrace on. Called with tasklist held
+ * for writing, and returns with it held too. But note it can release
+ * and reacquire the lock.
*/
void exit_ptrace(struct task_struct *tracer)
{
struct task_struct *p, *n;
LIST_HEAD(ptrace_dead);
- write_lock_irq(&tasklist_lock);
+ if (likely(list_empty(&tracer->ptraced)))
+ return;
+
list_for_each_entry_safe(p, n, &tracer->ptraced, ptrace_entry) {
if (__ptrace_detach(tracer, p))
list_add(&p->ptrace_entry, &ptrace_dead);
}
- write_unlock_irq(&tasklist_lock);
+ write_unlock_irq(&tasklist_lock);
BUG_ON(!list_empty(&tracer->ptraced));
list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_entry) {
list_del_init(&p->ptrace_entry);
release_task(p);
}
+
+ write_lock_irq(&tasklist_lock);
}
int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len)
/**
* set_timer_slack - set the allowed slack for a timer
+ * @timer: the timer to be modified
* @slack_hz: the amount of time (in jiffies) allowed for rounding
*
* Set the amount of time, in jiffies, that a certain timer has
static const u32 ddir_act[2] = { BLK_TC_ACT(BLK_TC_READ),
BLK_TC_ACT(BLK_TC_WRITE) };
+#define BLK_TC_HARDBARRIER BLK_TC_BARRIER
+#define BLK_TC_RAHEAD BLK_TC_AHEAD
+
/* The ilog2() calls fall out because they're constant */
-#define MASK_TC_BIT(rw, __name) ((rw & (1 << BIO_RW_ ## __name)) << \
- (ilog2(BLK_TC_ ## __name) + BLK_TC_SHIFT - BIO_RW_ ## __name))
+#define MASK_TC_BIT(rw, __name) ((rw & REQ_ ## __name) << \
+ (ilog2(BLK_TC_ ## __name) + BLK_TC_SHIFT - __REQ_ ## __name))
/*
* The worker for the various blk_add_trace*() types. Fills out a
return;
what |= ddir_act[rw & WRITE];
- what |= MASK_TC_BIT(rw, BARRIER);
- what |= MASK_TC_BIT(rw, SYNCIO);
- what |= MASK_TC_BIT(rw, AHEAD);
+ what |= MASK_TC_BIT(rw, HARDBARRIER);
+ what |= MASK_TC_BIT(rw, SYNC);
+ what |= MASK_TC_BIT(rw, RAHEAD);
what |= MASK_TC_BIT(rw, META);
what |= MASK_TC_BIT(rw, DISCARD);
}
EXPORT_SYMBOL_GPL(blk_trace_setup);
+#if defined(CONFIG_COMPAT) && defined(CONFIG_X86_64)
+static int compat_blk_trace_setup(struct request_queue *q, char *name,
+ dev_t dev, struct block_device *bdev,
+ char __user *arg)
+{
+ struct blk_user_trace_setup buts;
+ struct compat_blk_user_trace_setup cbuts;
+ int ret;
+
+ if (copy_from_user(&cbuts, arg, sizeof(cbuts)))
+ return -EFAULT;
+
+ buts = (struct blk_user_trace_setup) {
+ .act_mask = cbuts.act_mask,
+ .buf_size = cbuts.buf_size,
+ .buf_nr = cbuts.buf_nr,
+ .start_lba = cbuts.start_lba,
+ .end_lba = cbuts.end_lba,
+ .pid = cbuts.pid,
+ };
+ memcpy(&buts.name, &cbuts.name, 32);
+
+ ret = do_blk_trace_setup(q, name, dev, bdev, &buts);
+ if (ret)
+ return ret;
+
+ if (copy_to_user(arg, &buts.name, 32)) {
+ blk_trace_remove(q);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+#endif
+
int blk_trace_startstop(struct request_queue *q, int start)
{
int ret;
if (!q)
return -ENXIO;
+ lock_kernel();
mutex_lock(&bdev->bd_mutex);
switch (cmd) {
bdevname(bdev, b);
ret = blk_trace_setup(q, b, bdev->bd_dev, bdev, arg);
break;
+#if defined(CONFIG_COMPAT) && defined(CONFIG_X86_64)
+ case BLKTRACESETUP32:
+ bdevname(bdev, b);
+ ret = compat_blk_trace_setup(q, b, bdev->bd_dev, bdev, arg);
+ break;
+#endif
case BLKTRACESTART:
start = 1;
case BLKTRACESTOP:
}
mutex_unlock(&bdev->bd_mutex);
+ unlock_kernel();
return ret;
}
if (likely(!bt))
return;
- if (blk_discard_rq(rq))
- rw |= (1 << BIO_RW_DISCARD);
+ if (rq->cmd_flags & REQ_DISCARD)
+ rw |= REQ_DISCARD;
- if (blk_pc_request(rq)) {
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
what |= BLK_TC_ACT(BLK_TC_PC);
__blk_add_trace(bt, 0, blk_rq_bytes(rq), rw,
what, rq->errors, rq->cmd_len, rq->cmd);
if (likely(!bt))
return;
- if (blk_pc_request(rq))
+ if (rq->cmd_type == REQ_TYPE_BLOCK_PC)
__blk_add_trace(bt, 0, blk_rq_bytes(rq), 0,
BLK_TA_DRV_DATA, rq->errors, len, data);
else
int len = rq->cmd_len;
unsigned char *cmd = rq->cmd;
- if (!blk_pc_request(rq)) {
+ if (rq->cmd_type != REQ_TYPE_BLOCK_PC) {
buf[0] = '\0';
return;
}
if (rw & WRITE)
rwbs[i++] = 'W';
- else if (rw & 1 << BIO_RW_DISCARD)
+ else if (rw & REQ_DISCARD)
rwbs[i++] = 'D';
else if (bytes)
rwbs[i++] = 'R';
else
rwbs[i++] = 'N';
- if (rw & 1 << BIO_RW_AHEAD)
+ if (rw & REQ_RAHEAD)
rwbs[i++] = 'A';
- if (rw & 1 << BIO_RW_BARRIER)
+ if (rw & REQ_HARDBARRIER)
rwbs[i++] = 'B';
- if (rw & 1 << BIO_RW_SYNCIO)
+ if (rw & REQ_SYNC)
rwbs[i++] = 'S';
- if (rw & 1 << BIO_RW_META)
+ if (rw & REQ_META)
rwbs[i++] = 'M';
rwbs[i] = '\0';
int rw = rq->cmd_flags & 0x03;
int bytes;
- if (blk_discard_rq(rq))
- rw |= (1 << BIO_RW_DISCARD);
+ if (rq->cmd_flags & REQ_DISCARD)
+ rw |= REQ_DISCARD;
bytes = blk_rq_bytes(rq);
menu "Library routines"
+config RAID6_PQ
+ tristate
+
config BITREVERSE
tristate
If unsure, say N.
+config DEBUG_KMEMLEAK_DEFAULT_OFF
+ bool "Default kmemleak to off"
+ depends on DEBUG_KMEMLEAK
+ help
+ Say Y here to disable kmemleak by default. It can then be enabled
+ on the command line via kmemleak=on.
+
config DEBUG_PREEMPT
bool "Debug preemptible kernel"
depends on DEBUG_KERNEL && PREEMPT && TRACE_IRQFLAGS_SUPPORT
obj-$(CONFIG_REED_SOLOMON) += reed_solomon/
obj-$(CONFIG_LZO_COMPRESS) += lzo/
obj-$(CONFIG_LZO_DECOMPRESS) += lzo/
+obj-$(CONFIG_RAID6_PQ) += raid6/
lib-$(CONFIG_DECOMPRESS_GZIP) += decompress_inflate.o
lib-$(CONFIG_DECOMPRESS_BZIP2) += decompress_bunzip2.o
bug = find_bug(bugaddr);
- printk(KERN_EMERG "------------[ cut here ]------------\n");
-
file = NULL;
line = 0;
warning = 0;
if (warning) {
/* this is a WARN_ON rather than BUG/BUG_ON */
+ printk(KERN_WARNING "------------[ cut here ]------------\n");
+
if (file)
- printk(KERN_ERR "Badness at %s:%u\n",
+ printk(KERN_WARNING "WARNING: at %s:%u\n",
file, line);
else
- printk(KERN_ERR "Badness at %p "
+ printk(KERN_WARNING "WARNING: at %p "
"[verbose debug info unavailable]\n",
(void *)bugaddr);
+ print_modules();
show_regs(regs);
+ print_oops_end_marker();
add_taint(BUG_GET_TAINT(bug));
return BUG_TRAP_TYPE_WARN;
}
+ printk(KERN_EMERG "------------[ cut here ]------------\n");
+
if (file)
printk(KERN_CRIT "kernel BUG at %s:%u!\n",
file, line);
unsigned char selectors[32768]; /* nSelectors = 15 bits */
struct group_data groups[MAX_GROUPS]; /* Huffman coding tables */
int io_error; /* non-zero if we have IO error */
+ int byteCount[256];
+ unsigned char symToByte[256], mtfSymbol[256];
};
int *base = NULL;
int *limit = NULL;
int dbufCount, nextSym, dbufSize, groupCount, selector,
- i, j, k, t, runPos, symCount, symTotal, nSelectors,
- byteCount[256];
- unsigned char uc, symToByte[256], mtfSymbol[256], *selectors;
+ i, j, k, t, runPos, symCount, symTotal, nSelectors, *byteCount;
+ unsigned char uc, *symToByte, *mtfSymbol, *selectors;
unsigned int *dbuf, origPtr;
dbuf = bd->dbuf;
dbufSize = bd->dbufSize;
selectors = bd->selectors;
+ byteCount = bd->byteCount;
+ symToByte = bd->symToByte;
+ mtfSymbol = bd->mtfSymbol;
/* Read in header signature and CRC, then validate signature.
(last block signature means CRC is for whole file, return now) */
--- /dev/null
+obj-$(CONFIG_RAID6_PQ) += raid6_pq.o
+
+raid6_pq-y += raid6algos.o raid6recov.o raid6tables.o \
+ raid6int1.o raid6int2.o raid6int4.o \
+ raid6int8.o raid6int16.o raid6int32.o \
+ raid6altivec1.o raid6altivec2.o raid6altivec4.o \
+ raid6altivec8.o \
+ raid6mmx.o raid6sse1.o raid6sse2.o
+hostprogs-y += mktables
+
+quiet_cmd_unroll = UNROLL $@
+ cmd_unroll = $(AWK) -f$(srctree)/$(src)/unroll.awk -vN=$(UNROLL) \
+ < $< > $@ || ( rm -f $@ && exit 1 )
+
+ifeq ($(CONFIG_ALTIVEC),y)
+altivec_flags := -maltivec -mabi=altivec
+endif
+
+targets += raid6int1.c
+$(obj)/raid6int1.c: UNROLL := 1
+$(obj)/raid6int1.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE
+ $(call if_changed,unroll)
+
+targets += raid6int2.c
+$(obj)/raid6int2.c: UNROLL := 2
+$(obj)/raid6int2.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE
+ $(call if_changed,unroll)
+
+targets += raid6int4.c
+$(obj)/raid6int4.c: UNROLL := 4
+$(obj)/raid6int4.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE
+ $(call if_changed,unroll)
+
+targets += raid6int8.c
+$(obj)/raid6int8.c: UNROLL := 8
+$(obj)/raid6int8.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE
+ $(call if_changed,unroll)
+
+targets += raid6int16.c
+$(obj)/raid6int16.c: UNROLL := 16
+$(obj)/raid6int16.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE
+ $(call if_changed,unroll)
+
+targets += raid6int32.c
+$(obj)/raid6int32.c: UNROLL := 32
+$(obj)/raid6int32.c: $(src)/raid6int.uc $(src)/unroll.awk FORCE
+ $(call if_changed,unroll)
+
+CFLAGS_raid6altivec1.o += $(altivec_flags)
+targets += raid6altivec1.c
+$(obj)/raid6altivec1.c: UNROLL := 1
+$(obj)/raid6altivec1.c: $(src)/raid6altivec.uc $(src)/unroll.awk FORCE
+ $(call if_changed,unroll)
+
+CFLAGS_raid6altivec2.o += $(altivec_flags)
+targets += raid6altivec2.c
+$(obj)/raid6altivec2.c: UNROLL := 2
+$(obj)/raid6altivec2.c: $(src)/raid6altivec.uc $(src)/unroll.awk FORCE
+ $(call if_changed,unroll)
+
+CFLAGS_raid6altivec4.o += $(altivec_flags)
+targets += raid6altivec4.c
+$(obj)/raid6altivec4.c: UNROLL := 4
+$(obj)/raid6altivec4.c: $(src)/raid6altivec.uc $(src)/unroll.awk FORCE
+ $(call if_changed,unroll)
+
+CFLAGS_raid6altivec8.o += $(altivec_flags)
+targets += raid6altivec8.c
+$(obj)/raid6altivec8.c: UNROLL := 8
+$(obj)/raid6altivec8.c: $(src)/raid6altivec.uc $(src)/unroll.awk FORCE
+ $(call if_changed,unroll)
+
+quiet_cmd_mktable = TABLE $@
+ cmd_mktable = $(obj)/mktables > $@ || ( rm -f $@ && exit 1 )
+
+targets += raid6tables.c
+$(obj)/raid6tables.c: $(obj)/mktables FORCE
+ $(call if_changed,mktable)
--- /dev/null
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved
+ *
+ * This file is part of the Linux kernel, and is made available under
+ * the terms of the GNU General Public License version 2 or (at your
+ * option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * mktables.c
+ *
+ * Make RAID-6 tables. This is a host user space program to be run at
+ * compile time.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <time.h>
+
+static uint8_t gfmul(uint8_t a, uint8_t b)
+{
+ uint8_t v = 0;
+
+ while (b) {
+ if (b & 1)
+ v ^= a;
+ a = (a << 1) ^ (a & 0x80 ? 0x1d : 0);
+ b >>= 1;
+ }
+
+ return v;
+}
+
+static uint8_t gfpow(uint8_t a, int b)
+{
+ uint8_t v = 1;
+
+ b %= 255;
+ if (b < 0)
+ b += 255;
+
+ while (b) {
+ if (b & 1)
+ v = gfmul(v, a);
+ a = gfmul(a, a);
+ b >>= 1;
+ }
+
+ return v;
+}
+
+int main(int argc, char *argv[])
+{
+ int i, j, k;
+ uint8_t v;
+ uint8_t exptbl[256], invtbl[256];
+
+ printf("#include <linux/raid/pq.h>\n");
+
+ /* Compute multiplication table */
+ printf("\nconst u8 __attribute__((aligned(256)))\n"
+ "raid6_gfmul[256][256] =\n"
+ "{\n");
+ for (i = 0; i < 256; i++) {
+ printf("\t{\n");
+ for (j = 0; j < 256; j += 8) {
+ printf("\t\t");
+ for (k = 0; k < 8; k++)
+ printf("0x%02x,%c", gfmul(i, j + k),
+ (k == 7) ? '\n' : ' ');
+ }
+ printf("\t},\n");
+ }
+ printf("};\n");
+ printf("#ifdef __KERNEL__\n");
+ printf("EXPORT_SYMBOL(raid6_gfmul);\n");
+ printf("#endif\n");
+
+ /* Compute power-of-2 table (exponent) */
+ v = 1;
+ printf("\nconst u8 __attribute__((aligned(256)))\n"
+ "raid6_gfexp[256] =\n" "{\n");
+ for (i = 0; i < 256; i += 8) {
+ printf("\t");
+ for (j = 0; j < 8; j++) {
+ exptbl[i + j] = v;
+ printf("0x%02x,%c", v, (j == 7) ? '\n' : ' ');
+ v = gfmul(v, 2);
+ if (v == 1)
+ v = 0; /* For entry 255, not a real entry */
+ }
+ }
+ printf("};\n");
+ printf("#ifdef __KERNEL__\n");
+ printf("EXPORT_SYMBOL(raid6_gfexp);\n");
+ printf("#endif\n");
+
+ /* Compute inverse table x^-1 == x^254 */
+ printf("\nconst u8 __attribute__((aligned(256)))\n"
+ "raid6_gfinv[256] =\n" "{\n");
+ for (i = 0; i < 256; i += 8) {
+ printf("\t");
+ for (j = 0; j < 8; j++) {
+ invtbl[i + j] = v = gfpow(i + j, 254);
+ printf("0x%02x,%c", v, (j == 7) ? '\n' : ' ');
+ }
+ }
+ printf("};\n");
+ printf("#ifdef __KERNEL__\n");
+ printf("EXPORT_SYMBOL(raid6_gfinv);\n");
+ printf("#endif\n");
+
+ /* Compute inv(2^x + 1) (exponent-xor-inverse) table */
+ printf("\nconst u8 __attribute__((aligned(256)))\n"
+ "raid6_gfexi[256] =\n" "{\n");
+ for (i = 0; i < 256; i += 8) {
+ printf("\t");
+ for (j = 0; j < 8; j++)
+ printf("0x%02x,%c", invtbl[exptbl[i + j] ^ 1],
+ (j == 7) ? '\n' : ' ');
+ }
+ printf("};\n");
+ printf("#ifdef __KERNEL__\n");
+ printf("EXPORT_SYMBOL(raid6_gfexi);\n");
+ printf("#endif\n");
+
+ return 0;
+}
--- /dev/null
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ * Copyright 2002 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * raid6algos.c
+ *
+ * Algorithm list and algorithm selection for RAID-6
+ */
+
+#include <linux/raid/pq.h>
+#include <linux/gfp.h>
+#ifndef __KERNEL__
+#include <sys/mman.h>
+#include <stdio.h>
+#else
+#if !RAID6_USE_EMPTY_ZERO_PAGE
+/* In .bss so it's zeroed */
+const char raid6_empty_zero_page[PAGE_SIZE] __attribute__((aligned(256)));
+EXPORT_SYMBOL(raid6_empty_zero_page);
+#endif
+#endif
+
+struct raid6_calls raid6_call;
+EXPORT_SYMBOL_GPL(raid6_call);
+
+const struct raid6_calls * const raid6_algos[] = {
+ &raid6_intx1,
+ &raid6_intx2,
+ &raid6_intx4,
+ &raid6_intx8,
+#if defined(__ia64__)
+ &raid6_intx16,
+ &raid6_intx32,
+#endif
+#if defined(__i386__) && !defined(__arch_um__)
+ &raid6_mmxx1,
+ &raid6_mmxx2,
+ &raid6_sse1x1,
+ &raid6_sse1x2,
+ &raid6_sse2x1,
+ &raid6_sse2x2,
+#endif
+#if defined(__x86_64__) && !defined(__arch_um__)
+ &raid6_sse2x1,
+ &raid6_sse2x2,
+ &raid6_sse2x4,
+#endif
+#ifdef CONFIG_ALTIVEC
+ &raid6_altivec1,
+ &raid6_altivec2,
+ &raid6_altivec4,
+ &raid6_altivec8,
+#endif
+ NULL
+};
+
+#ifdef __KERNEL__
+#define RAID6_TIME_JIFFIES_LG2 4
+#else
+/* Need more time to be stable in userspace */
+#define RAID6_TIME_JIFFIES_LG2 9
+#define time_before(x, y) ((x) < (y))
+#endif
+
+/* Try to pick the best algorithm */
+/* This code uses the gfmul table as convenient data set to abuse */
+
+int __init raid6_select_algo(void)
+{
+ const struct raid6_calls * const * algo;
+ const struct raid6_calls * best;
+ char *syndromes;
+ void *dptrs[(65536/PAGE_SIZE)+2];
+ int i, disks;
+ unsigned long perf, bestperf;
+ int bestprefer;
+ unsigned long j0, j1;
+
+ disks = (65536/PAGE_SIZE)+2;
+ for ( i = 0 ; i < disks-2 ; i++ ) {
+ dptrs[i] = ((char *)raid6_gfmul) + PAGE_SIZE*i;
+ }
+
+ /* Normal code - use a 2-page allocation to avoid D$ conflict */
+ syndromes = (void *) __get_free_pages(GFP_KERNEL, 1);
+
+ if ( !syndromes ) {
+ printk("raid6: Yikes! No memory available.\n");
+ return -ENOMEM;
+ }
+
+ dptrs[disks-2] = syndromes;
+ dptrs[disks-1] = syndromes + PAGE_SIZE;
+
+ bestperf = 0; bestprefer = 0; best = NULL;
+
+ for ( algo = raid6_algos ; *algo ; algo++ ) {
+ if ( !(*algo)->valid || (*algo)->valid() ) {
+ perf = 0;
+
+ preempt_disable();
+ j0 = jiffies;
+ while ( (j1 = jiffies) == j0 )
+ cpu_relax();
+ while (time_before(jiffies,
+ j1 + (1<<RAID6_TIME_JIFFIES_LG2))) {
+ (*algo)->gen_syndrome(disks, PAGE_SIZE, dptrs);
+ perf++;
+ }
+ preempt_enable();
+
+ if ( (*algo)->prefer > bestprefer ||
+ ((*algo)->prefer == bestprefer &&
+ perf > bestperf) ) {
+ best = *algo;
+ bestprefer = best->prefer;
+ bestperf = perf;
+ }
+ printk("raid6: %-8s %5ld MB/s\n", (*algo)->name,
+ (perf*HZ) >> (20-16+RAID6_TIME_JIFFIES_LG2));
+ }
+ }
+
+ if (best) {
+ printk("raid6: using algorithm %s (%ld MB/s)\n",
+ best->name,
+ (bestperf*HZ) >> (20-16+RAID6_TIME_JIFFIES_LG2));
+ raid6_call = *best;
+ } else
+ printk("raid6: Yikes! No algorithm found!\n");
+
+ free_pages((unsigned long)syndromes, 1);
+
+ return best ? 0 : -EINVAL;
+}
+
+static void raid6_exit(void)
+{
+ do { } while (0);
+}
+
+subsys_initcall(raid6_select_algo);
+module_exit(raid6_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RAID6 Q-syndrome calculations");
--- /dev/null
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ * Copyright 2002-2004 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * raid6altivec$#.c
+ *
+ * $#-way unrolled portable integer math RAID-6 instruction set
+ *
+ * This file is postprocessed using unroll.awk
+ *
+ * <benh> hpa: in process,
+ * you can just "steal" the vec unit with enable_kernel_altivec() (but
+ * bracked this with preempt_disable/enable or in a lock)
+ */
+
+#include <linux/raid/pq.h>
+
+#ifdef CONFIG_ALTIVEC
+
+#include <altivec.h>
+#ifdef __KERNEL__
+# include <asm/system.h>
+# include <asm/cputable.h>
+#endif
+
+/*
+ * This is the C data type to use. We use a vector of
+ * signed char so vec_cmpgt() will generate the right
+ * instruction.
+ */
+
+typedef vector signed char unative_t;
+
+#define NBYTES(x) ((vector signed char) {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x})
+#define NSIZE sizeof(unative_t)
+
+/*
+ * The SHLBYTE() operation shifts each byte left by 1, *not*
+ * rolling over into the next byte
+ */
+static inline __attribute_const__ unative_t SHLBYTE(unative_t v)
+{
+ return vec_add(v,v);
+}
+
+/*
+ * The MASK() operation returns 0xFF in any byte for which the high
+ * bit is 1, 0x00 for any byte for which the high bit is 0.
+ */
+static inline __attribute_const__ unative_t MASK(unative_t v)
+{
+ unative_t zv = NBYTES(0);
+
+ /* vec_cmpgt returns a vector bool char; thus the need for the cast */
+ return (unative_t)vec_cmpgt(zv, v);
+}
+
+
+/* This is noinline to make damned sure that gcc doesn't move any of the
+ Altivec code around the enable/disable code */
+static void noinline
+raid6_altivec$#_gen_syndrome_real(int disks, size_t bytes, void **ptrs)
+{
+ u8 **dptr = (u8 **)ptrs;
+ u8 *p, *q;
+ int d, z, z0;
+
+ unative_t wd$$, wq$$, wp$$, w1$$, w2$$;
+ unative_t x1d = NBYTES(0x1d);
+
+ z0 = disks - 3; /* Highest data disk */
+ p = dptr[z0+1]; /* XOR parity */
+ q = dptr[z0+2]; /* RS syndrome */
+
+ for ( d = 0 ; d < bytes ; d += NSIZE*$# ) {
+ wq$$ = wp$$ = *(unative_t *)&dptr[z0][d+$$*NSIZE];
+ for ( z = z0-1 ; z >= 0 ; z-- ) {
+ wd$$ = *(unative_t *)&dptr[z][d+$$*NSIZE];
+ wp$$ = vec_xor(wp$$, wd$$);
+ w2$$ = MASK(wq$$);
+ w1$$ = SHLBYTE(wq$$);
+ w2$$ = vec_and(w2$$, x1d);
+ w1$$ = vec_xor(w1$$, w2$$);
+ wq$$ = vec_xor(w1$$, wd$$);
+ }
+ *(unative_t *)&p[d+NSIZE*$$] = wp$$;
+ *(unative_t *)&q[d+NSIZE*$$] = wq$$;
+ }
+}
+
+static void raid6_altivec$#_gen_syndrome(int disks, size_t bytes, void **ptrs)
+{
+ preempt_disable();
+ enable_kernel_altivec();
+
+ raid6_altivec$#_gen_syndrome_real(disks, bytes, ptrs);
+
+ preempt_enable();
+}
+
+int raid6_have_altivec(void);
+#if $# == 1
+int raid6_have_altivec(void)
+{
+ /* This assumes either all CPUs have Altivec or none does */
+# ifdef __KERNEL__
+ return cpu_has_feature(CPU_FTR_ALTIVEC);
+# else
+ return 1;
+# endif
+}
+#endif
+
+const struct raid6_calls raid6_altivec$# = {
+ raid6_altivec$#_gen_syndrome,
+ raid6_have_altivec,
+ "altivecx$#",
+ 0
+};
+
+#endif /* CONFIG_ALTIVEC */
--- /dev/null
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ * Copyright 2002-2004 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * raid6int$#.c
+ *
+ * $#-way unrolled portable integer math RAID-6 instruction set
+ *
+ * This file is postprocessed using unroll.awk
+ */
+
+#include <linux/raid/pq.h>
+
+/*
+ * This is the C data type to use
+ */
+
+/* Change this from BITS_PER_LONG if there is something better... */
+#if BITS_PER_LONG == 64
+# define NBYTES(x) ((x) * 0x0101010101010101UL)
+# define NSIZE 8
+# define NSHIFT 3
+# define NSTRING "64"
+typedef u64 unative_t;
+#else
+# define NBYTES(x) ((x) * 0x01010101U)
+# define NSIZE 4
+# define NSHIFT 2
+# define NSTRING "32"
+typedef u32 unative_t;
+#endif
+
+
+
+/*
+ * IA-64 wants insane amounts of unrolling. On other architectures that
+ * is just a waste of space.
+ */
+#if ($# <= 8) || defined(__ia64__)
+
+
+/*
+ * These sub-operations are separate inlines since they can sometimes be
+ * specially optimized using architecture-specific hacks.
+ */
+
+/*
+ * The SHLBYTE() operation shifts each byte left by 1, *not*
+ * rolling over into the next byte
+ */
+static inline __attribute_const__ unative_t SHLBYTE(unative_t v)
+{
+ unative_t vv;
+
+ vv = (v << 1) & NBYTES(0xfe);
+ return vv;
+}
+
+/*
+ * The MASK() operation returns 0xFF in any byte for which the high
+ * bit is 1, 0x00 for any byte for which the high bit is 0.
+ */
+static inline __attribute_const__ unative_t MASK(unative_t v)
+{
+ unative_t vv;
+
+ vv = v & NBYTES(0x80);
+ vv = (vv << 1) - (vv >> 7); /* Overflow on the top bit is OK */
+ return vv;
+}
+
+
+static void raid6_int$#_gen_syndrome(int disks, size_t bytes, void **ptrs)
+{
+ u8 **dptr = (u8 **)ptrs;
+ u8 *p, *q;
+ int d, z, z0;
+
+ unative_t wd$$, wq$$, wp$$, w1$$, w2$$;
+
+ z0 = disks - 3; /* Highest data disk */
+ p = dptr[z0+1]; /* XOR parity */
+ q = dptr[z0+2]; /* RS syndrome */
+
+ for ( d = 0 ; d < bytes ; d += NSIZE*$# ) {
+ wq$$ = wp$$ = *(unative_t *)&dptr[z0][d+$$*NSIZE];
+ for ( z = z0-1 ; z >= 0 ; z-- ) {
+ wd$$ = *(unative_t *)&dptr[z][d+$$*NSIZE];
+ wp$$ ^= wd$$;
+ w2$$ = MASK(wq$$);
+ w1$$ = SHLBYTE(wq$$);
+ w2$$ &= NBYTES(0x1d);
+ w1$$ ^= w2$$;
+ wq$$ = w1$$ ^ wd$$;
+ }
+ *(unative_t *)&p[d+NSIZE*$$] = wp$$;
+ *(unative_t *)&q[d+NSIZE*$$] = wq$$;
+ }
+}
+
+const struct raid6_calls raid6_intx$# = {
+ raid6_int$#_gen_syndrome,
+ NULL, /* always valid */
+ "int" NSTRING "x$#",
+ 0
+};
+
+#endif
--- /dev/null
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ * Copyright 2002 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * raid6mmx.c
+ *
+ * MMX implementation of RAID-6 syndrome functions
+ */
+
+#if defined(__i386__) && !defined(__arch_um__)
+
+#include <linux/raid/pq.h>
+#include "raid6x86.h"
+
+/* Shared with raid6sse1.c */
+const struct raid6_mmx_constants {
+ u64 x1d;
+} raid6_mmx_constants = {
+ 0x1d1d1d1d1d1d1d1dULL,
+};
+
+static int raid6_have_mmx(void)
+{
+ /* Not really "boot_cpu" but "all_cpus" */
+ return boot_cpu_has(X86_FEATURE_MMX);
+}
+
+/*
+ * Plain MMX implementation
+ */
+static void raid6_mmx1_gen_syndrome(int disks, size_t bytes, void **ptrs)
+{
+ u8 **dptr = (u8 **)ptrs;
+ u8 *p, *q;
+ int d, z, z0;
+
+ z0 = disks - 3; /* Highest data disk */
+ p = dptr[z0+1]; /* XOR parity */
+ q = dptr[z0+2]; /* RS syndrome */
+
+ kernel_fpu_begin();
+
+ asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d));
+ asm volatile("pxor %mm5,%mm5"); /* Zero temp */
+
+ for ( d = 0 ; d < bytes ; d += 8 ) {
+ asm volatile("movq %0,%%mm2" : : "m" (dptr[z0][d])); /* P[0] */
+ asm volatile("movq %mm2,%mm4"); /* Q[0] */
+ for ( z = z0-1 ; z >= 0 ; z-- ) {
+ asm volatile("movq %0,%%mm6" : : "m" (dptr[z][d]));
+ asm volatile("pcmpgtb %mm4,%mm5");
+ asm volatile("paddb %mm4,%mm4");
+ asm volatile("pand %mm0,%mm5");
+ asm volatile("pxor %mm5,%mm4");
+ asm volatile("pxor %mm5,%mm5");
+ asm volatile("pxor %mm6,%mm2");
+ asm volatile("pxor %mm6,%mm4");
+ }
+ asm volatile("movq %%mm2,%0" : "=m" (p[d]));
+ asm volatile("pxor %mm2,%mm2");
+ asm volatile("movq %%mm4,%0" : "=m" (q[d]));
+ asm volatile("pxor %mm4,%mm4");
+ }
+
+ kernel_fpu_end();
+}
+
+const struct raid6_calls raid6_mmxx1 = {
+ raid6_mmx1_gen_syndrome,
+ raid6_have_mmx,
+ "mmxx1",
+ 0
+};
+
+/*
+ * Unrolled-by-2 MMX implementation
+ */
+static void raid6_mmx2_gen_syndrome(int disks, size_t bytes, void **ptrs)
+{
+ u8 **dptr = (u8 **)ptrs;
+ u8 *p, *q;
+ int d, z, z0;
+
+ z0 = disks - 3; /* Highest data disk */
+ p = dptr[z0+1]; /* XOR parity */
+ q = dptr[z0+2]; /* RS syndrome */
+
+ kernel_fpu_begin();
+
+ asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d));
+ asm volatile("pxor %mm5,%mm5"); /* Zero temp */
+ asm volatile("pxor %mm7,%mm7"); /* Zero temp */
+
+ for ( d = 0 ; d < bytes ; d += 16 ) {
+ asm volatile("movq %0,%%mm2" : : "m" (dptr[z0][d])); /* P[0] */
+ asm volatile("movq %0,%%mm3" : : "m" (dptr[z0][d+8]));
+ asm volatile("movq %mm2,%mm4"); /* Q[0] */
+ asm volatile("movq %mm3,%mm6"); /* Q[1] */
+ for ( z = z0-1 ; z >= 0 ; z-- ) {
+ asm volatile("pcmpgtb %mm4,%mm5");
+ asm volatile("pcmpgtb %mm6,%mm7");
+ asm volatile("paddb %mm4,%mm4");
+ asm volatile("paddb %mm6,%mm6");
+ asm volatile("pand %mm0,%mm5");
+ asm volatile("pand %mm0,%mm7");
+ asm volatile("pxor %mm5,%mm4");
+ asm volatile("pxor %mm7,%mm6");
+ asm volatile("movq %0,%%mm5" : : "m" (dptr[z][d]));
+ asm volatile("movq %0,%%mm7" : : "m" (dptr[z][d+8]));
+ asm volatile("pxor %mm5,%mm2");
+ asm volatile("pxor %mm7,%mm3");
+ asm volatile("pxor %mm5,%mm4");
+ asm volatile("pxor %mm7,%mm6");
+ asm volatile("pxor %mm5,%mm5");
+ asm volatile("pxor %mm7,%mm7");
+ }
+ asm volatile("movq %%mm2,%0" : "=m" (p[d]));
+ asm volatile("movq %%mm3,%0" : "=m" (p[d+8]));
+ asm volatile("movq %%mm4,%0" : "=m" (q[d]));
+ asm volatile("movq %%mm6,%0" : "=m" (q[d+8]));
+ }
+
+ kernel_fpu_end();
+}
+
+const struct raid6_calls raid6_mmxx2 = {
+ raid6_mmx2_gen_syndrome,
+ raid6_have_mmx,
+ "mmxx2",
+ 0
+};
+
+#endif
--- /dev/null
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ * Copyright 2002 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * raid6recov.c
+ *
+ * RAID-6 data recovery in dual failure mode. In single failure mode,
+ * use the RAID-5 algorithm (or, in the case of Q failure, just reconstruct
+ * the syndrome.)
+ */
+
+#include <linux/raid/pq.h>
+
+/* Recover two failed data blocks. */
+void raid6_2data_recov(int disks, size_t bytes, int faila, int failb,
+ void **ptrs)
+{
+ u8 *p, *q, *dp, *dq;
+ u8 px, qx, db;
+ const u8 *pbmul; /* P multiplier table for B data */
+ const u8 *qmul; /* Q multiplier table (for both) */
+
+ p = (u8 *)ptrs[disks-2];
+ q = (u8 *)ptrs[disks-1];
+
+ /* Compute syndrome with zero for the missing data pages
+ Use the dead data pages as temporary storage for
+ delta p and delta q */
+ dp = (u8 *)ptrs[faila];
+ ptrs[faila] = (void *)raid6_empty_zero_page;
+ ptrs[disks-2] = dp;
+ dq = (u8 *)ptrs[failb];
+ ptrs[failb] = (void *)raid6_empty_zero_page;
+ ptrs[disks-1] = dq;
+
+ raid6_call.gen_syndrome(disks, bytes, ptrs);
+
+ /* Restore pointer table */
+ ptrs[faila] = dp;
+ ptrs[failb] = dq;
+ ptrs[disks-2] = p;
+ ptrs[disks-1] = q;
+
+ /* Now, pick the proper data tables */
+ pbmul = raid6_gfmul[raid6_gfexi[failb-faila]];
+ qmul = raid6_gfmul[raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]]];
+
+ /* Now do it... */
+ while ( bytes-- ) {
+ px = *p ^ *dp;
+ qx = qmul[*q ^ *dq];
+ *dq++ = db = pbmul[px] ^ qx; /* Reconstructed B */
+ *dp++ = db ^ px; /* Reconstructed A */
+ p++; q++;
+ }
+}
+EXPORT_SYMBOL_GPL(raid6_2data_recov);
+
+/* Recover failure of one data block plus the P block */
+void raid6_datap_recov(int disks, size_t bytes, int faila, void **ptrs)
+{
+ u8 *p, *q, *dq;
+ const u8 *qmul; /* Q multiplier table */
+
+ p = (u8 *)ptrs[disks-2];
+ q = (u8 *)ptrs[disks-1];
+
+ /* Compute syndrome with zero for the missing data page
+ Use the dead data page as temporary storage for delta q */
+ dq = (u8 *)ptrs[faila];
+ ptrs[faila] = (void *)raid6_empty_zero_page;
+ ptrs[disks-1] = dq;
+
+ raid6_call.gen_syndrome(disks, bytes, ptrs);
+
+ /* Restore pointer table */
+ ptrs[faila] = dq;
+ ptrs[disks-1] = q;
+
+ /* Now, pick the proper data tables */
+ qmul = raid6_gfmul[raid6_gfinv[raid6_gfexp[faila]]];
+
+ /* Now do it... */
+ while ( bytes-- ) {
+ *p++ ^= *dq = qmul[*q ^ *dq];
+ q++; dq++;
+ }
+}
+EXPORT_SYMBOL_GPL(raid6_datap_recov);
+
+#ifndef __KERNEL__
+/* Testing only */
+
+/* Recover two failed blocks. */
+void raid6_dual_recov(int disks, size_t bytes, int faila, int failb, void **ptrs)
+{
+ if ( faila > failb ) {
+ int tmp = faila;
+ faila = failb;
+ failb = tmp;
+ }
+
+ if ( failb == disks-1 ) {
+ if ( faila == disks-2 ) {
+ /* P+Q failure. Just rebuild the syndrome. */
+ raid6_call.gen_syndrome(disks, bytes, ptrs);
+ } else {
+ /* data+Q failure. Reconstruct data from P,
+ then rebuild syndrome. */
+ /* NOT IMPLEMENTED - equivalent to RAID-5 */
+ }
+ } else {
+ if ( failb == disks-2 ) {
+ /* data+P failure. */
+ raid6_datap_recov(disks, bytes, faila, ptrs);
+ } else {
+ /* data+data failure. */
+ raid6_2data_recov(disks, bytes, faila, failb, ptrs);
+ }
+ }
+}
+
+#endif
--- /dev/null
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ * Copyright 2002 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * raid6sse1.c
+ *
+ * SSE-1/MMXEXT implementation of RAID-6 syndrome functions
+ *
+ * This is really an MMX implementation, but it requires SSE-1 or
+ * AMD MMXEXT for prefetch support and a few other features. The
+ * support for nontemporal memory accesses is enough to make this
+ * worthwhile as a separate implementation.
+ */
+
+#if defined(__i386__) && !defined(__arch_um__)
+
+#include <linux/raid/pq.h>
+#include "raid6x86.h"
+
+/* Defined in raid6mmx.c */
+extern const struct raid6_mmx_constants {
+ u64 x1d;
+} raid6_mmx_constants;
+
+static int raid6_have_sse1_or_mmxext(void)
+{
+ /* Not really boot_cpu but "all_cpus" */
+ return boot_cpu_has(X86_FEATURE_MMX) &&
+ (boot_cpu_has(X86_FEATURE_XMM) ||
+ boot_cpu_has(X86_FEATURE_MMXEXT));
+}
+
+/*
+ * Plain SSE1 implementation
+ */
+static void raid6_sse11_gen_syndrome(int disks, size_t bytes, void **ptrs)
+{
+ u8 **dptr = (u8 **)ptrs;
+ u8 *p, *q;
+ int d, z, z0;
+
+ z0 = disks - 3; /* Highest data disk */
+ p = dptr[z0+1]; /* XOR parity */
+ q = dptr[z0+2]; /* RS syndrome */
+
+ kernel_fpu_begin();
+
+ asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d));
+ asm volatile("pxor %mm5,%mm5"); /* Zero temp */
+
+ for ( d = 0 ; d < bytes ; d += 8 ) {
+ asm volatile("prefetchnta %0" : : "m" (dptr[z0][d]));
+ asm volatile("movq %0,%%mm2" : : "m" (dptr[z0][d])); /* P[0] */
+ asm volatile("prefetchnta %0" : : "m" (dptr[z0-1][d]));
+ asm volatile("movq %mm2,%mm4"); /* Q[0] */
+ asm volatile("movq %0,%%mm6" : : "m" (dptr[z0-1][d]));
+ for ( z = z0-2 ; z >= 0 ; z-- ) {
+ asm volatile("prefetchnta %0" : : "m" (dptr[z][d]));
+ asm volatile("pcmpgtb %mm4,%mm5");
+ asm volatile("paddb %mm4,%mm4");
+ asm volatile("pand %mm0,%mm5");
+ asm volatile("pxor %mm5,%mm4");
+ asm volatile("pxor %mm5,%mm5");
+ asm volatile("pxor %mm6,%mm2");
+ asm volatile("pxor %mm6,%mm4");
+ asm volatile("movq %0,%%mm6" : : "m" (dptr[z][d]));
+ }
+ asm volatile("pcmpgtb %mm4,%mm5");
+ asm volatile("paddb %mm4,%mm4");
+ asm volatile("pand %mm0,%mm5");
+ asm volatile("pxor %mm5,%mm4");
+ asm volatile("pxor %mm5,%mm5");
+ asm volatile("pxor %mm6,%mm2");
+ asm volatile("pxor %mm6,%mm4");
+
+ asm volatile("movntq %%mm2,%0" : "=m" (p[d]));
+ asm volatile("movntq %%mm4,%0" : "=m" (q[d]));
+ }
+
+ asm volatile("sfence" : : : "memory");
+ kernel_fpu_end();
+}
+
+const struct raid6_calls raid6_sse1x1 = {
+ raid6_sse11_gen_syndrome,
+ raid6_have_sse1_or_mmxext,
+ "sse1x1",
+ 1 /* Has cache hints */
+};
+
+/*
+ * Unrolled-by-2 SSE1 implementation
+ */
+static void raid6_sse12_gen_syndrome(int disks, size_t bytes, void **ptrs)
+{
+ u8 **dptr = (u8 **)ptrs;
+ u8 *p, *q;
+ int d, z, z0;
+
+ z0 = disks - 3; /* Highest data disk */
+ p = dptr[z0+1]; /* XOR parity */
+ q = dptr[z0+2]; /* RS syndrome */
+
+ kernel_fpu_begin();
+
+ asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d));
+ asm volatile("pxor %mm5,%mm5"); /* Zero temp */
+ asm volatile("pxor %mm7,%mm7"); /* Zero temp */
+
+ /* We uniformly assume a single prefetch covers at least 16 bytes */
+ for ( d = 0 ; d < bytes ; d += 16 ) {
+ asm volatile("prefetchnta %0" : : "m" (dptr[z0][d]));
+ asm volatile("movq %0,%%mm2" : : "m" (dptr[z0][d])); /* P[0] */
+ asm volatile("movq %0,%%mm3" : : "m" (dptr[z0][d+8])); /* P[1] */
+ asm volatile("movq %mm2,%mm4"); /* Q[0] */
+ asm volatile("movq %mm3,%mm6"); /* Q[1] */
+ for ( z = z0-1 ; z >= 0 ; z-- ) {
+ asm volatile("prefetchnta %0" : : "m" (dptr[z][d]));
+ asm volatile("pcmpgtb %mm4,%mm5");
+ asm volatile("pcmpgtb %mm6,%mm7");
+ asm volatile("paddb %mm4,%mm4");
+ asm volatile("paddb %mm6,%mm6");
+ asm volatile("pand %mm0,%mm5");
+ asm volatile("pand %mm0,%mm7");
+ asm volatile("pxor %mm5,%mm4");
+ asm volatile("pxor %mm7,%mm6");
+ asm volatile("movq %0,%%mm5" : : "m" (dptr[z][d]));
+ asm volatile("movq %0,%%mm7" : : "m" (dptr[z][d+8]));
+ asm volatile("pxor %mm5,%mm2");
+ asm volatile("pxor %mm7,%mm3");
+ asm volatile("pxor %mm5,%mm4");
+ asm volatile("pxor %mm7,%mm6");
+ asm volatile("pxor %mm5,%mm5");
+ asm volatile("pxor %mm7,%mm7");
+ }
+ asm volatile("movntq %%mm2,%0" : "=m" (p[d]));
+ asm volatile("movntq %%mm3,%0" : "=m" (p[d+8]));
+ asm volatile("movntq %%mm4,%0" : "=m" (q[d]));
+ asm volatile("movntq %%mm6,%0" : "=m" (q[d+8]));
+ }
+
+ asm volatile("sfence" : :: "memory");
+ kernel_fpu_end();
+}
+
+const struct raid6_calls raid6_sse1x2 = {
+ raid6_sse12_gen_syndrome,
+ raid6_have_sse1_or_mmxext,
+ "sse1x2",
+ 1 /* Has cache hints */
+};
+
+#endif
--- /dev/null
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ * Copyright 2002 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * raid6sse2.c
+ *
+ * SSE-2 implementation of RAID-6 syndrome functions
+ *
+ */
+
+#if (defined(__i386__) || defined(__x86_64__)) && !defined(__arch_um__)
+
+#include <linux/raid/pq.h>
+#include "raid6x86.h"
+
+static const struct raid6_sse_constants {
+ u64 x1d[2];
+} raid6_sse_constants __attribute__((aligned(16))) = {
+ { 0x1d1d1d1d1d1d1d1dULL, 0x1d1d1d1d1d1d1d1dULL },
+};
+
+static int raid6_have_sse2(void)
+{
+ /* Not really boot_cpu but "all_cpus" */
+ return boot_cpu_has(X86_FEATURE_MMX) &&
+ boot_cpu_has(X86_FEATURE_FXSR) &&
+ boot_cpu_has(X86_FEATURE_XMM) &&
+ boot_cpu_has(X86_FEATURE_XMM2);
+}
+
+/*
+ * Plain SSE2 implementation
+ */
+static void raid6_sse21_gen_syndrome(int disks, size_t bytes, void **ptrs)
+{
+ u8 **dptr = (u8 **)ptrs;
+ u8 *p, *q;
+ int d, z, z0;
+
+ z0 = disks - 3; /* Highest data disk */
+ p = dptr[z0+1]; /* XOR parity */
+ q = dptr[z0+2]; /* RS syndrome */
+
+ kernel_fpu_begin();
+
+ asm volatile("movdqa %0,%%xmm0" : : "m" (raid6_sse_constants.x1d[0]));
+ asm volatile("pxor %xmm5,%xmm5"); /* Zero temp */
+
+ for ( d = 0 ; d < bytes ; d += 16 ) {
+ asm volatile("prefetchnta %0" : : "m" (dptr[z0][d]));
+ asm volatile("movdqa %0,%%xmm2" : : "m" (dptr[z0][d])); /* P[0] */
+ asm volatile("prefetchnta %0" : : "m" (dptr[z0-1][d]));
+ asm volatile("movdqa %xmm2,%xmm4"); /* Q[0] */
+ asm volatile("movdqa %0,%%xmm6" : : "m" (dptr[z0-1][d]));
+ for ( z = z0-2 ; z >= 0 ; z-- ) {
+ asm volatile("prefetchnta %0" : : "m" (dptr[z][d]));
+ asm volatile("pcmpgtb %xmm4,%xmm5");
+ asm volatile("paddb %xmm4,%xmm4");
+ asm volatile("pand %xmm0,%xmm5");
+ asm volatile("pxor %xmm5,%xmm4");
+ asm volatile("pxor %xmm5,%xmm5");
+ asm volatile("pxor %xmm6,%xmm2");
+ asm volatile("pxor %xmm6,%xmm4");
+ asm volatile("movdqa %0,%%xmm6" : : "m" (dptr[z][d]));
+ }
+ asm volatile("pcmpgtb %xmm4,%xmm5");
+ asm volatile("paddb %xmm4,%xmm4");
+ asm volatile("pand %xmm0,%xmm5");
+ asm volatile("pxor %xmm5,%xmm4");
+ asm volatile("pxor %xmm5,%xmm5");
+ asm volatile("pxor %xmm6,%xmm2");
+ asm volatile("pxor %xmm6,%xmm4");
+
+ asm volatile("movntdq %%xmm2,%0" : "=m" (p[d]));
+ asm volatile("pxor %xmm2,%xmm2");
+ asm volatile("movntdq %%xmm4,%0" : "=m" (q[d]));
+ asm volatile("pxor %xmm4,%xmm4");
+ }
+
+ asm volatile("sfence" : : : "memory");
+ kernel_fpu_end();
+}
+
+const struct raid6_calls raid6_sse2x1 = {
+ raid6_sse21_gen_syndrome,
+ raid6_have_sse2,
+ "sse2x1",
+ 1 /* Has cache hints */
+};
+
+/*
+ * Unrolled-by-2 SSE2 implementation
+ */
+static void raid6_sse22_gen_syndrome(int disks, size_t bytes, void **ptrs)
+{
+ u8 **dptr = (u8 **)ptrs;
+ u8 *p, *q;
+ int d, z, z0;
+
+ z0 = disks - 3; /* Highest data disk */
+ p = dptr[z0+1]; /* XOR parity */
+ q = dptr[z0+2]; /* RS syndrome */
+
+ kernel_fpu_begin();
+
+ asm volatile("movdqa %0,%%xmm0" : : "m" (raid6_sse_constants.x1d[0]));
+ asm volatile("pxor %xmm5,%xmm5"); /* Zero temp */
+ asm volatile("pxor %xmm7,%xmm7"); /* Zero temp */
+
+ /* We uniformly assume a single prefetch covers at least 32 bytes */
+ for ( d = 0 ; d < bytes ; d += 32 ) {
+ asm volatile("prefetchnta %0" : : "m" (dptr[z0][d]));
+ asm volatile("movdqa %0,%%xmm2" : : "m" (dptr[z0][d])); /* P[0] */
+ asm volatile("movdqa %0,%%xmm3" : : "m" (dptr[z0][d+16])); /* P[1] */
+ asm volatile("movdqa %xmm2,%xmm4"); /* Q[0] */
+ asm volatile("movdqa %xmm3,%xmm6"); /* Q[1] */
+ for ( z = z0-1 ; z >= 0 ; z-- ) {
+ asm volatile("prefetchnta %0" : : "m" (dptr[z][d]));
+ asm volatile("pcmpgtb %xmm4,%xmm5");
+ asm volatile("pcmpgtb %xmm6,%xmm7");
+ asm volatile("paddb %xmm4,%xmm4");
+ asm volatile("paddb %xmm6,%xmm6");
+ asm volatile("pand %xmm0,%xmm5");
+ asm volatile("pand %xmm0,%xmm7");
+ asm volatile("pxor %xmm5,%xmm4");
+ asm volatile("pxor %xmm7,%xmm6");
+ asm volatile("movdqa %0,%%xmm5" : : "m" (dptr[z][d]));
+ asm volatile("movdqa %0,%%xmm7" : : "m" (dptr[z][d+16]));
+ asm volatile("pxor %xmm5,%xmm2");
+ asm volatile("pxor %xmm7,%xmm3");
+ asm volatile("pxor %xmm5,%xmm4");
+ asm volatile("pxor %xmm7,%xmm6");
+ asm volatile("pxor %xmm5,%xmm5");
+ asm volatile("pxor %xmm7,%xmm7");
+ }
+ asm volatile("movntdq %%xmm2,%0" : "=m" (p[d]));
+ asm volatile("movntdq %%xmm3,%0" : "=m" (p[d+16]));
+ asm volatile("movntdq %%xmm4,%0" : "=m" (q[d]));
+ asm volatile("movntdq %%xmm6,%0" : "=m" (q[d+16]));
+ }
+
+ asm volatile("sfence" : : : "memory");
+ kernel_fpu_end();
+}
+
+const struct raid6_calls raid6_sse2x2 = {
+ raid6_sse22_gen_syndrome,
+ raid6_have_sse2,
+ "sse2x2",
+ 1 /* Has cache hints */
+};
+
+#endif
+
+#if defined(__x86_64__) && !defined(__arch_um__)
+
+/*
+ * Unrolled-by-4 SSE2 implementation
+ */
+static void raid6_sse24_gen_syndrome(int disks, size_t bytes, void **ptrs)
+{
+ u8 **dptr = (u8 **)ptrs;
+ u8 *p, *q;
+ int d, z, z0;
+
+ z0 = disks - 3; /* Highest data disk */
+ p = dptr[z0+1]; /* XOR parity */
+ q = dptr[z0+2]; /* RS syndrome */
+
+ kernel_fpu_begin();
+
+ asm volatile("movdqa %0,%%xmm0" :: "m" (raid6_sse_constants.x1d[0]));
+ asm volatile("pxor %xmm2,%xmm2"); /* P[0] */
+ asm volatile("pxor %xmm3,%xmm3"); /* P[1] */
+ asm volatile("pxor %xmm4,%xmm4"); /* Q[0] */
+ asm volatile("pxor %xmm5,%xmm5"); /* Zero temp */
+ asm volatile("pxor %xmm6,%xmm6"); /* Q[1] */
+ asm volatile("pxor %xmm7,%xmm7"); /* Zero temp */
+ asm volatile("pxor %xmm10,%xmm10"); /* P[2] */
+ asm volatile("pxor %xmm11,%xmm11"); /* P[3] */
+ asm volatile("pxor %xmm12,%xmm12"); /* Q[2] */
+ asm volatile("pxor %xmm13,%xmm13"); /* Zero temp */
+ asm volatile("pxor %xmm14,%xmm14"); /* Q[3] */
+ asm volatile("pxor %xmm15,%xmm15"); /* Zero temp */
+
+ for ( d = 0 ; d < bytes ; d += 64 ) {
+ for ( z = z0 ; z >= 0 ; z-- ) {
+ /* The second prefetch seems to improve performance... */
+ asm volatile("prefetchnta %0" :: "m" (dptr[z][d]));
+ asm volatile("prefetchnta %0" :: "m" (dptr[z][d+32]));
+ asm volatile("pcmpgtb %xmm4,%xmm5");
+ asm volatile("pcmpgtb %xmm6,%xmm7");
+ asm volatile("pcmpgtb %xmm12,%xmm13");
+ asm volatile("pcmpgtb %xmm14,%xmm15");
+ asm volatile("paddb %xmm4,%xmm4");
+ asm volatile("paddb %xmm6,%xmm6");
+ asm volatile("paddb %xmm12,%xmm12");
+ asm volatile("paddb %xmm14,%xmm14");
+ asm volatile("pand %xmm0,%xmm5");
+ asm volatile("pand %xmm0,%xmm7");
+ asm volatile("pand %xmm0,%xmm13");
+ asm volatile("pand %xmm0,%xmm15");
+ asm volatile("pxor %xmm5,%xmm4");
+ asm volatile("pxor %xmm7,%xmm6");
+ asm volatile("pxor %xmm13,%xmm12");
+ asm volatile("pxor %xmm15,%xmm14");
+ asm volatile("movdqa %0,%%xmm5" :: "m" (dptr[z][d]));
+ asm volatile("movdqa %0,%%xmm7" :: "m" (dptr[z][d+16]));
+ asm volatile("movdqa %0,%%xmm13" :: "m" (dptr[z][d+32]));
+ asm volatile("movdqa %0,%%xmm15" :: "m" (dptr[z][d+48]));
+ asm volatile("pxor %xmm5,%xmm2");
+ asm volatile("pxor %xmm7,%xmm3");
+ asm volatile("pxor %xmm13,%xmm10");
+ asm volatile("pxor %xmm15,%xmm11");
+ asm volatile("pxor %xmm5,%xmm4");
+ asm volatile("pxor %xmm7,%xmm6");
+ asm volatile("pxor %xmm13,%xmm12");
+ asm volatile("pxor %xmm15,%xmm14");
+ asm volatile("pxor %xmm5,%xmm5");
+ asm volatile("pxor %xmm7,%xmm7");
+ asm volatile("pxor %xmm13,%xmm13");
+ asm volatile("pxor %xmm15,%xmm15");
+ }
+ asm volatile("movntdq %%xmm2,%0" : "=m" (p[d]));
+ asm volatile("pxor %xmm2,%xmm2");
+ asm volatile("movntdq %%xmm3,%0" : "=m" (p[d+16]));
+ asm volatile("pxor %xmm3,%xmm3");
+ asm volatile("movntdq %%xmm10,%0" : "=m" (p[d+32]));
+ asm volatile("pxor %xmm10,%xmm10");
+ asm volatile("movntdq %%xmm11,%0" : "=m" (p[d+48]));
+ asm volatile("pxor %xmm11,%xmm11");
+ asm volatile("movntdq %%xmm4,%0" : "=m" (q[d]));
+ asm volatile("pxor %xmm4,%xmm4");
+ asm volatile("movntdq %%xmm6,%0" : "=m" (q[d+16]));
+ asm volatile("pxor %xmm6,%xmm6");
+ asm volatile("movntdq %%xmm12,%0" : "=m" (q[d+32]));
+ asm volatile("pxor %xmm12,%xmm12");
+ asm volatile("movntdq %%xmm14,%0" : "=m" (q[d+48]));
+ asm volatile("pxor %xmm14,%xmm14");
+ }
+
+ asm volatile("sfence" : : : "memory");
+ kernel_fpu_end();
+}
+
+const struct raid6_calls raid6_sse2x4 = {
+ raid6_sse24_gen_syndrome,
+ raid6_have_sse2,
+ "sse2x4",
+ 1 /* Has cache hints */
+};
+
+#endif
--- /dev/null
+#
+# This is a simple Makefile to test some of the RAID-6 code
+# from userspace.
+#
+
+CC = gcc
+OPTFLAGS = -O2 # Adjust as desired
+CFLAGS = -I.. -I ../../../include -g $(OPTFLAGS)
+LD = ld
+AWK = awk
+AR = ar
+RANLIB = ranlib
+
+.c.o:
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+%.c: ../%.c
+ cp -f $< $@
+
+%.uc: ../%.uc
+ cp -f $< $@
+
+all: raid6.a raid6test
+
+raid6.a: raid6int1.o raid6int2.o raid6int4.o raid6int8.o raid6int16.o \
+ raid6int32.o \
+ raid6mmx.o raid6sse1.o raid6sse2.o \
+ raid6altivec1.o raid6altivec2.o raid6altivec4.o raid6altivec8.o \
+ raid6recov.o raid6algos.o \
+ raid6tables.o
+ rm -f $@
+ $(AR) cq $@ $^
+ $(RANLIB) $@
+
+raid6test: test.c raid6.a
+ $(CC) $(CFLAGS) -o raid6test $^
+
+raid6altivec1.c: raid6altivec.uc ../unroll.awk
+ $(AWK) ../unroll.awk -vN=1 < raid6altivec.uc > $@
+
+raid6altivec2.c: raid6altivec.uc ../unroll.awk
+ $(AWK) ../unroll.awk -vN=2 < raid6altivec.uc > $@
+
+raid6altivec4.c: raid6altivec.uc ../unroll.awk
+ $(AWK) ../unroll.awk -vN=4 < raid6altivec.uc > $@
+
+raid6altivec8.c: raid6altivec.uc ../unroll.awk
+ $(AWK) ../unroll.awk -vN=8 < raid6altivec.uc > $@
+
+raid6int1.c: raid6int.uc ../unroll.awk
+ $(AWK) ../unroll.awk -vN=1 < raid6int.uc > $@
+
+raid6int2.c: raid6int.uc ../unroll.awk
+ $(AWK) ../unroll.awk -vN=2 < raid6int.uc > $@
+
+raid6int4.c: raid6int.uc ../unroll.awk
+ $(AWK) ../unroll.awk -vN=4 < raid6int.uc > $@
+
+raid6int8.c: raid6int.uc ../unroll.awk
+ $(AWK) ../unroll.awk -vN=8 < raid6int.uc > $@
+
+raid6int16.c: raid6int.uc ../unroll.awk
+ $(AWK) ../unroll.awk -vN=16 < raid6int.uc > $@
+
+raid6int32.c: raid6int.uc ../unroll.awk
+ $(AWK) ../unroll.awk -vN=32 < raid6int.uc > $@
+
+raid6tables.c: mktables
+ ./mktables > raid6tables.c
+
+clean:
+ rm -f *.o *.a mktables mktables.c raid6int.uc raid6*.c raid6test
+
+spotless: clean
+ rm -f *~
--- /dev/null
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved
+ *
+ * This file is part of the Linux kernel, and is made available under
+ * the terms of the GNU General Public License version 2 or (at your
+ * option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * raid6test.c
+ *
+ * Test RAID-6 recovery with various algorithms
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <linux/raid/pq.h>
+
+#define NDISKS 16 /* Including P and Q */
+
+const char raid6_empty_zero_page[PAGE_SIZE] __attribute__((aligned(256)));
+struct raid6_calls raid6_call;
+
+char *dataptrs[NDISKS];
+char data[NDISKS][PAGE_SIZE];
+char recovi[PAGE_SIZE], recovj[PAGE_SIZE];
+
+static void makedata(void)
+{
+ int i, j;
+
+ for (i = 0; i < NDISKS; i++) {
+ for (j = 0; j < PAGE_SIZE; j++)
+ data[i][j] = rand();
+
+ dataptrs[i] = data[i];
+ }
+}
+
+static char disk_type(int d)
+{
+ switch (d) {
+ case NDISKS-2:
+ return 'P';
+ case NDISKS-1:
+ return 'Q';
+ default:
+ return 'D';
+ }
+}
+
+static int test_disks(int i, int j)
+{
+ int erra, errb;
+
+ memset(recovi, 0xf0, PAGE_SIZE);
+ memset(recovj, 0xba, PAGE_SIZE);
+
+ dataptrs[i] = recovi;
+ dataptrs[j] = recovj;
+
+ raid6_dual_recov(NDISKS, PAGE_SIZE, i, j, (void **)&dataptrs);
+
+ erra = memcmp(data[i], recovi, PAGE_SIZE);
+ errb = memcmp(data[j], recovj, PAGE_SIZE);
+
+ if (i < NDISKS-2 && j == NDISKS-1) {
+ /* We don't implement the DQ failure scenario, since it's
+ equivalent to a RAID-5 failure (XOR, then recompute Q) */
+ erra = errb = 0;
+ } else {
+ printf("algo=%-8s faila=%3d(%c) failb=%3d(%c) %s\n",
+ raid6_call.name,
+ i, disk_type(i),
+ j, disk_type(j),
+ (!erra && !errb) ? "OK" :
+ !erra ? "ERRB" :
+ !errb ? "ERRA" : "ERRAB");
+ }
+
+ dataptrs[i] = data[i];
+ dataptrs[j] = data[j];
+
+ return erra || errb;
+}
+
+int main(int argc, char *argv[])
+{
+ const struct raid6_calls *const *algo;
+ int i, j;
+ int err = 0;
+
+ makedata();
+
+ for (algo = raid6_algos; *algo; algo++) {
+ if (!(*algo)->valid || (*algo)->valid()) {
+ raid6_call = **algo;
+
+ /* Nuke syndromes */
+ memset(data[NDISKS-2], 0xee, 2*PAGE_SIZE);
+
+ /* Generate assumed good syndrome */
+ raid6_call.gen_syndrome(NDISKS, PAGE_SIZE,
+ (void **)&dataptrs);
+
+ for (i = 0; i < NDISKS-1; i++)
+ for (j = i+1; j < NDISKS; j++)
+ err += test_disks(i, j);
+ }
+ printf("\n");
+ }
+
+ printf("\n");
+ /* Pick the best algorithm test */
+ raid6_select_algo();
+
+ if (err)
+ printf("\n*** ERRORS FOUND ***\n");
+
+ return err;
+}
--- /dev/null
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2002-2004 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * raid6x86.h
+ *
+ * Definitions common to x86 and x86-64 RAID-6 code only
+ */
+
+#ifndef LINUX_RAID_RAID6X86_H
+#define LINUX_RAID_RAID6X86_H
+
+#if (defined(__i386__) || defined(__x86_64__)) && !defined(__arch_um__)
+
+#ifdef __KERNEL__ /* Real code */
+
+#include <asm/i387.h>
+
+#else /* Dummy code for user space testing */
+
+static inline void kernel_fpu_begin(void)
+{
+}
+
+static inline void kernel_fpu_end(void)
+{
+}
+
+#define X86_FEATURE_MMX (0*32+23) /* Multimedia Extensions */
+#define X86_FEATURE_FXSR (0*32+24) /* FXSAVE and FXRSTOR instructions
+ * (fast save and restore) */
+#define X86_FEATURE_XMM (0*32+25) /* Streaming SIMD Extensions */
+#define X86_FEATURE_XMM2 (0*32+26) /* Streaming SIMD Extensions-2 */
+#define X86_FEATURE_MMXEXT (1*32+22) /* AMD MMX extensions */
+
+/* Should work well enough on modern CPUs for testing */
+static inline int boot_cpu_has(int flag)
+{
+ u32 eax = (flag >> 5) ? 0x80000001 : 1;
+ u32 edx;
+
+ asm volatile("cpuid"
+ : "+a" (eax), "=d" (edx)
+ : : "ecx", "ebx");
+
+ return (edx >> (flag & 31)) & 1;
+}
+
+#endif /* ndef __KERNEL__ */
+
+#endif
+#endif
--- /dev/null
+
+# This filter requires one command line option of form -vN=n
+# where n must be a decimal number.
+#
+# Repeat each input line containing $$ n times, replacing $$ with 0...n-1.
+# Replace each $# with n, and each $* with a single $.
+
+BEGIN {
+ n = N + 0
+}
+{
+ if (/\$\$/) { rep = n } else { rep = 1 }
+ for (i = 0; i < rep; ++i) {
+ tmp = $0
+ gsub(/\$\$/, i, tmp)
+ gsub(/\$\#/, n, tmp)
+ gsub(/\$\*/, "$", tmp)
+ print tmp
+ }
+}
#include <linux/slab.h>
#include <linux/scatterlist.h>
#include <linux/highmem.h>
+#include <linux/kmemleak.h>
/**
* sg_next - return the next scatterlist entry in a list
*/
static struct scatterlist *sg_kmalloc(unsigned int nents, gfp_t gfp_mask)
{
- if (nents == SG_MAX_SINGLE_ALLOC)
- return (struct scatterlist *) __get_free_page(gfp_mask);
- else
+ if (nents == SG_MAX_SINGLE_ALLOC) {
+ /*
+ * Kmemleak doesn't track page allocations as they are not
+ * commonly used (in a raw form) for kernel data structures.
+ * As we chain together a list of pages and then a normal
+ * kmalloc (tracked by kmemleak), in order to for that last
+ * allocation not to become decoupled (and thus a
+ * false-positive) we need to inform kmemleak of all the
+ * intermediate allocations.
+ */
+ void *ptr = (void *) __get_free_page(gfp_mask);
+ kmemleak_alloc(ptr, PAGE_SIZE, 1, gfp_mask);
+ return ptr;
+ } else
return kmalloc(nents * sizeof(struct scatterlist), gfp_mask);
}
static void sg_kfree(struct scatterlist *sg, unsigned int nents)
{
- if (nents == SG_MAX_SINGLE_ALLOC)
+ if (nents == SG_MAX_SINGLE_ALLOC) {
+ kmemleak_free(sg);
free_page((unsigned long) sg);
- else
+ } else
kfree(sg);
}
#include <linux/module.h>
#include <linux/writeback.h>
#include <linux/device.h>
+#include <trace/events/writeback.h>
static atomic_long_t bdi_seq = ATOMIC_LONG_INIT(0);
static int bdi_sync_supers(void *);
static void sync_supers_timer_fn(unsigned long);
-static void bdi_add_default_flusher_task(struct backing_dev_info *bdi);
-
#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
#include <linux/seq_file.h>
static int bdi_debug_stats_show(struct seq_file *m, void *v)
{
struct backing_dev_info *bdi = m->private;
- struct bdi_writeback *wb;
+ struct bdi_writeback *wb = &bdi->wb;
unsigned long background_thresh;
unsigned long dirty_thresh;
unsigned long bdi_thresh;
unsigned long nr_dirty, nr_io, nr_more_io, nr_wb;
struct inode *inode;
- /*
- * inode lock is enough here, the bdi->wb_list is protected by
- * RCU on the reader side
- */
nr_wb = nr_dirty = nr_io = nr_more_io = 0;
spin_lock(&inode_lock);
- list_for_each_entry(wb, &bdi->wb_list, list) {
- nr_wb++;
- list_for_each_entry(inode, &wb->b_dirty, i_list)
- nr_dirty++;
- list_for_each_entry(inode, &wb->b_io, i_list)
- nr_io++;
- list_for_each_entry(inode, &wb->b_more_io, i_list)
- nr_more_io++;
- }
+ list_for_each_entry(inode, &wb->b_dirty, i_list)
+ nr_dirty++;
+ list_for_each_entry(inode, &wb->b_io, i_list)
+ nr_io++;
+ list_for_each_entry(inode, &wb->b_more_io, i_list)
+ nr_more_io++;
spin_unlock(&inode_lock);
get_dirty_limits(&background_thresh, &dirty_thresh, &bdi_thresh, bdi);
"BdiDirtyThresh: %8lu kB\n"
"DirtyThresh: %8lu kB\n"
"BackgroundThresh: %8lu kB\n"
- "WritebackThreads: %8lu\n"
"b_dirty: %8lu\n"
"b_io: %8lu\n"
"b_more_io: %8lu\n"
"bdi_list: %8u\n"
- "state: %8lx\n"
- "wb_list: %8u\n",
+ "state: %8lx\n",
(unsigned long) K(bdi_stat(bdi, BDI_WRITEBACK)),
(unsigned long) K(bdi_stat(bdi, BDI_RECLAIMABLE)),
K(bdi_thresh), K(dirty_thresh),
- K(background_thresh), nr_wb, nr_dirty, nr_io, nr_more_io,
- !list_empty(&bdi->bdi_list), bdi->state,
- !list_empty(&bdi->wb_list));
+ K(background_thresh), nr_dirty, nr_io, nr_more_io,
+ !list_empty(&bdi->bdi_list), bdi->state);
#undef K
return 0;
sync_supers_tsk = kthread_run(bdi_sync_supers, NULL, "sync_supers");
BUG_ON(IS_ERR(sync_supers_tsk));
- init_timer(&sync_supers_timer);
setup_timer(&sync_supers_timer, sync_supers_timer_fn, 0);
bdi_arm_supers_timer();
}
subsys_initcall(default_bdi_init);
-static void bdi_wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi)
-{
- memset(wb, 0, sizeof(*wb));
-
- wb->bdi = bdi;
- wb->last_old_flush = jiffies;
- INIT_LIST_HEAD(&wb->b_dirty);
- INIT_LIST_HEAD(&wb->b_io);
- INIT_LIST_HEAD(&wb->b_more_io);
-}
-
-static void bdi_task_init(struct backing_dev_info *bdi,
- struct bdi_writeback *wb)
-{
- struct task_struct *tsk = current;
-
- spin_lock(&bdi->wb_lock);
- list_add_tail_rcu(&wb->list, &bdi->wb_list);
- spin_unlock(&bdi->wb_lock);
-
- tsk->flags |= PF_FLUSHER | PF_SWAPWRITE;
- set_freezable();
-
- /*
- * Our parent may run at a different priority, just set us to normal
- */
- set_user_nice(tsk, 0);
-}
-
-static int bdi_start_fn(void *ptr)
-{
- struct bdi_writeback *wb = ptr;
- struct backing_dev_info *bdi = wb->bdi;
- int ret;
-
- /*
- * Add us to the active bdi_list
- */
- spin_lock_bh(&bdi_lock);
- list_add_rcu(&bdi->bdi_list, &bdi_list);
- spin_unlock_bh(&bdi_lock);
-
- bdi_task_init(bdi, wb);
-
- /*
- * Clear pending bit and wakeup anybody waiting to tear us down
- */
- clear_bit(BDI_pending, &bdi->state);
- smp_mb__after_clear_bit();
- wake_up_bit(&bdi->state, BDI_pending);
-
- ret = bdi_writeback_task(wb);
-
- /*
- * Remove us from the list
- */
- spin_lock(&bdi->wb_lock);
- list_del_rcu(&wb->list);
- spin_unlock(&bdi->wb_lock);
-
- /*
- * Flush any work that raced with us exiting. No new work
- * will be added, since this bdi isn't discoverable anymore.
- */
- if (!list_empty(&bdi->work_list))
- wb_do_writeback(wb, 1);
-
- wb->task = NULL;
- return ret;
-}
-
int bdi_has_dirty_io(struct backing_dev_info *bdi)
{
return wb_has_dirty_io(&bdi->wb);
}
/*
- * kupdated() used to do this. We cannot do it from the bdi_forker_task()
+ * kupdated() used to do this. We cannot do it from the bdi_forker_thread()
* or we risk deadlocking on ->s_umount. The longer term solution would be
* to implement sync_supers_bdi() or similar and simply do it from the
- * bdi writeback tasks individually.
+ * bdi writeback thread individually.
*/
static int bdi_sync_supers(void *unused)
{
bdi_arm_supers_timer();
}
-static int bdi_forker_task(void *ptr)
+static void wakeup_timer_fn(unsigned long data)
+{
+ struct backing_dev_info *bdi = (struct backing_dev_info *)data;
+
+ spin_lock_bh(&bdi->wb_lock);
+ if (bdi->wb.task) {
+ trace_writeback_wake_thread(bdi);
+ wake_up_process(bdi->wb.task);
+ } else {
+ /*
+ * When bdi tasks are inactive for long time, they are killed.
+ * In this case we have to wake-up the forker thread which
+ * should create and run the bdi thread.
+ */
+ trace_writeback_wake_forker_thread(bdi);
+ wake_up_process(default_backing_dev_info.wb.task);
+ }
+ spin_unlock_bh(&bdi->wb_lock);
+}
+
+/*
+ * This function is used when the first inode for this bdi is marked dirty. It
+ * wakes-up the corresponding bdi thread which should then take care of the
+ * periodic background write-out of dirty inodes. Since the write-out would
+ * starts only 'dirty_writeback_interval' centisecs from now anyway, we just
+ * set up a timer which wakes the bdi thread up later.
+ *
+ * Note, we wouldn't bother setting up the timer, but this function is on the
+ * fast-path (used by '__mark_inode_dirty()'), so we save few context switches
+ * by delaying the wake-up.
+ */
+void bdi_wakeup_thread_delayed(struct backing_dev_info *bdi)
+{
+ unsigned long timeout;
+
+ timeout = msecs_to_jiffies(dirty_writeback_interval * 10);
+ mod_timer(&bdi->wb.wakeup_timer, jiffies + timeout);
+}
+
+/*
+ * Calculate the longest interval (jiffies) bdi threads are allowed to be
+ * inactive.
+ */
+static unsigned long bdi_longest_inactive(void)
+{
+ unsigned long interval;
+
+ interval = msecs_to_jiffies(dirty_writeback_interval * 10);
+ return max(5UL * 60 * HZ, interval);
+}
+
+static int bdi_forker_thread(void *ptr)
{
struct bdi_writeback *me = ptr;
- bdi_task_init(me->bdi, me);
+ current->flags |= PF_FLUSHER | PF_SWAPWRITE;
+ set_freezable();
+
+ /*
+ * Our parent may run at a different priority, just set us to normal
+ */
+ set_user_nice(current, 0);
for (;;) {
- struct backing_dev_info *bdi, *tmp;
- struct bdi_writeback *wb;
+ struct task_struct *task = NULL;
+ struct backing_dev_info *bdi;
+ enum {
+ NO_ACTION, /* Nothing to do */
+ FORK_THREAD, /* Fork bdi thread */
+ KILL_THREAD, /* Kill inactive bdi thread */
+ } action = NO_ACTION;
/*
* Temporary measure, we want to make sure we don't see
* dirty data on the default backing_dev_info
*/
- if (wb_has_dirty_io(me) || !list_empty(&me->bdi->work_list))
+ if (wb_has_dirty_io(me) || !list_empty(&me->bdi->work_list)) {
+ del_timer(&me->wakeup_timer);
wb_do_writeback(me, 0);
+ }
spin_lock_bh(&bdi_lock);
+ set_current_state(TASK_INTERRUPTIBLE);
- /*
- * Check if any existing bdi's have dirty data without
- * a thread registered. If so, set that up.
- */
- list_for_each_entry_safe(bdi, tmp, &bdi_list, bdi_list) {
- if (bdi->wb.task)
- continue;
- if (list_empty(&bdi->work_list) &&
- !bdi_has_dirty_io(bdi))
+ list_for_each_entry(bdi, &bdi_list, bdi_list) {
+ bool have_dirty_io;
+
+ if (!bdi_cap_writeback_dirty(bdi) ||
+ bdi_cap_flush_forker(bdi))
continue;
- bdi_add_default_flusher_task(bdi);
- }
+ WARN(!test_bit(BDI_registered, &bdi->state),
+ "bdi %p/%s is not registered!\n", bdi, bdi->name);
- set_current_state(TASK_INTERRUPTIBLE);
+ have_dirty_io = !list_empty(&bdi->work_list) ||
+ wb_has_dirty_io(&bdi->wb);
- if (list_empty(&bdi_pending_list)) {
- unsigned long wait;
+ /*
+ * If the bdi has work to do, but the thread does not
+ * exist - create it.
+ */
+ if (!bdi->wb.task && have_dirty_io) {
+ /*
+ * Set the pending bit - if someone will try to
+ * unregister this bdi - it'll wait on this bit.
+ */
+ set_bit(BDI_pending, &bdi->state);
+ action = FORK_THREAD;
+ break;
+ }
+
+ spin_lock(&bdi->wb_lock);
+
+ /*
+ * If there is no work to do and the bdi thread was
+ * inactive long enough - kill it. The wb_lock is taken
+ * to make sure no-one adds more work to this bdi and
+ * wakes the bdi thread up.
+ */
+ if (bdi->wb.task && !have_dirty_io &&
+ time_after(jiffies, bdi->wb.last_active +
+ bdi_longest_inactive())) {
+ task = bdi->wb.task;
+ bdi->wb.task = NULL;
+ spin_unlock(&bdi->wb_lock);
+ set_bit(BDI_pending, &bdi->state);
+ action = KILL_THREAD;
+ break;
+ }
+ spin_unlock(&bdi->wb_lock);
+ }
+ spin_unlock_bh(&bdi_lock);
- spin_unlock_bh(&bdi_lock);
- wait = msecs_to_jiffies(dirty_writeback_interval * 10);
- if (wait)
- schedule_timeout(wait);
+ /* Keep working if default bdi still has things to do */
+ if (!list_empty(&me->bdi->work_list))
+ __set_current_state(TASK_RUNNING);
+
+ switch (action) {
+ case FORK_THREAD:
+ __set_current_state(TASK_RUNNING);
+ task = kthread_run(bdi_writeback_thread, &bdi->wb, "flush-%s",
+ dev_name(bdi->dev));
+ if (IS_ERR(task)) {
+ /*
+ * If thread creation fails, force writeout of
+ * the bdi from the thread.
+ */
+ bdi_flush_io(bdi);
+ } else {
+ /*
+ * The spinlock makes sure we do not lose
+ * wake-ups when racing with 'bdi_queue_work()'.
+ */
+ spin_lock_bh(&bdi->wb_lock);
+ bdi->wb.task = task;
+ spin_unlock_bh(&bdi->wb_lock);
+ }
+ break;
+
+ case KILL_THREAD:
+ __set_current_state(TASK_RUNNING);
+ kthread_stop(task);
+ break;
+
+ case NO_ACTION:
+ if (!wb_has_dirty_io(me) || !dirty_writeback_interval)
+ /*
+ * There are no dirty data. The only thing we
+ * should now care about is checking for
+ * inactive bdi threads and killing them. Thus,
+ * let's sleep for longer time, save energy and
+ * be friendly for battery-driven devices.
+ */
+ schedule_timeout(bdi_longest_inactive());
else
- schedule();
+ schedule_timeout(msecs_to_jiffies(dirty_writeback_interval * 10));
try_to_freeze();
+ /* Back to the main loop */
continue;
}
- __set_current_state(TASK_RUNNING);
-
- /*
- * This is our real job - check for pending entries in
- * bdi_pending_list, and create the tasks that got added
- */
- bdi = list_entry(bdi_pending_list.next, struct backing_dev_info,
- bdi_list);
- list_del_init(&bdi->bdi_list);
- spin_unlock_bh(&bdi_lock);
-
- wb = &bdi->wb;
- wb->task = kthread_run(bdi_start_fn, wb, "flush-%s",
- dev_name(bdi->dev));
/*
- * If task creation fails, then readd the bdi to
- * the pending list and force writeout of the bdi
- * from this forker thread. That will free some memory
- * and we can try again.
+ * Clear pending bit and wakeup anybody waiting to tear us down.
*/
- if (IS_ERR(wb->task)) {
- wb->task = NULL;
-
- /*
- * Add this 'bdi' to the back, so we get
- * a chance to flush other bdi's to free
- * memory.
- */
- spin_lock_bh(&bdi_lock);
- list_add_tail(&bdi->bdi_list, &bdi_pending_list);
- spin_unlock_bh(&bdi_lock);
-
- bdi_flush_io(bdi);
- }
+ clear_bit(BDI_pending, &bdi->state);
+ smp_mb__after_clear_bit();
+ wake_up_bit(&bdi->state, BDI_pending);
}
return 0;
}
-static void bdi_add_to_pending(struct rcu_head *head)
-{
- struct backing_dev_info *bdi;
-
- bdi = container_of(head, struct backing_dev_info, rcu_head);
- INIT_LIST_HEAD(&bdi->bdi_list);
-
- spin_lock(&bdi_lock);
- list_add_tail(&bdi->bdi_list, &bdi_pending_list);
- spin_unlock(&bdi_lock);
-
- /*
- * We are now on the pending list, wake up bdi_forker_task()
- * to finish the job and add us back to the active bdi_list
- */
- wake_up_process(default_backing_dev_info.wb.task);
-}
-
-/*
- * Add the default flusher task that gets created for any bdi
- * that has dirty data pending writeout
- */
-void static bdi_add_default_flusher_task(struct backing_dev_info *bdi)
-{
- if (!bdi_cap_writeback_dirty(bdi))
- return;
-
- if (WARN_ON(!test_bit(BDI_registered, &bdi->state))) {
- printk(KERN_ERR "bdi %p/%s is not registered!\n",
- bdi, bdi->name);
- return;
- }
-
- /*
- * Check with the helper whether to proceed adding a task. Will only
- * abort if we two or more simultanous calls to
- * bdi_add_default_flusher_task() occured, further additions will block
- * waiting for previous additions to finish.
- */
- if (!test_and_set_bit(BDI_pending, &bdi->state)) {
- list_del_rcu(&bdi->bdi_list);
-
- /*
- * We must wait for the current RCU period to end before
- * moving to the pending list. So schedule that operation
- * from an RCU callback.
- */
- call_rcu(&bdi->rcu_head, bdi_add_to_pending);
- }
-}
-
/*
* Remove bdi from bdi_list, and ensure that it is no longer visible
*/
const char *fmt, ...)
{
va_list args;
- int ret = 0;
struct device *dev;
if (bdi->dev) /* The driver needs to use separate queues per device */
- goto exit;
+ return 0;
va_start(args, fmt);
dev = device_create_vargs(bdi_class, parent, MKDEV(0, 0), bdi, fmt, args);
va_end(args);
- if (IS_ERR(dev)) {
- ret = PTR_ERR(dev);
- goto exit;
- }
-
- spin_lock_bh(&bdi_lock);
- list_add_tail_rcu(&bdi->bdi_list, &bdi_list);
- spin_unlock_bh(&bdi_lock);
+ if (IS_ERR(dev))
+ return PTR_ERR(dev);
bdi->dev = dev;
if (bdi_cap_flush_forker(bdi)) {
struct bdi_writeback *wb = &bdi->wb;
- wb->task = kthread_run(bdi_forker_task, wb, "bdi-%s",
+ wb->task = kthread_run(bdi_forker_thread, wb, "bdi-%s",
dev_name(dev));
- if (IS_ERR(wb->task)) {
- wb->task = NULL;
- ret = -ENOMEM;
-
- bdi_remove_from_list(bdi);
- goto exit;
- }
+ if (IS_ERR(wb->task))
+ return PTR_ERR(wb->task);
}
bdi_debug_register(bdi, dev_name(dev));
set_bit(BDI_registered, &bdi->state);
-exit:
- return ret;
+
+ spin_lock_bh(&bdi_lock);
+ list_add_tail_rcu(&bdi->bdi_list, &bdi_list);
+ spin_unlock_bh(&bdi_lock);
+
+ trace_writeback_bdi_register(bdi);
+ return 0;
}
EXPORT_SYMBOL(bdi_register);
*/
static void bdi_wb_shutdown(struct backing_dev_info *bdi)
{
- struct bdi_writeback *wb;
-
if (!bdi_cap_writeback_dirty(bdi))
return;
/*
- * If setup is pending, wait for that to complete first
+ * Make sure nobody finds us on the bdi_list anymore
*/
- wait_on_bit(&bdi->state, BDI_pending, bdi_sched_wait,
- TASK_UNINTERRUPTIBLE);
+ bdi_remove_from_list(bdi);
/*
- * Make sure nobody finds us on the bdi_list anymore
+ * If setup is pending, wait for that to complete first
*/
- bdi_remove_from_list(bdi);
+ wait_on_bit(&bdi->state, BDI_pending, bdi_sched_wait,
+ TASK_UNINTERRUPTIBLE);
/*
- * Finally, kill the kernel threads. We don't need to be RCU
+ * Finally, kill the kernel thread. We don't need to be RCU
* safe anymore, since the bdi is gone from visibility. Force
* unfreeze of the thread before calling kthread_stop(), otherwise
* it would never exet if it is currently stuck in the refrigerator.
*/
- list_for_each_entry(wb, &bdi->wb_list, list) {
- thaw_process(wb->task);
- kthread_stop(wb->task);
+ if (bdi->wb.task) {
+ thaw_process(bdi->wb.task);
+ kthread_stop(bdi->wb.task);
}
}
void bdi_unregister(struct backing_dev_info *bdi)
{
if (bdi->dev) {
+ trace_writeback_bdi_unregister(bdi);
bdi_prune_sb(bdi);
+ del_timer_sync(&bdi->wb.wakeup_timer);
if (!bdi_cap_flush_forker(bdi))
bdi_wb_shutdown(bdi);
}
EXPORT_SYMBOL(bdi_unregister);
+static void bdi_wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi)
+{
+ memset(wb, 0, sizeof(*wb));
+
+ wb->bdi = bdi;
+ wb->last_old_flush = jiffies;
+ INIT_LIST_HEAD(&wb->b_dirty);
+ INIT_LIST_HEAD(&wb->b_io);
+ INIT_LIST_HEAD(&wb->b_more_io);
+ setup_timer(&wb->wakeup_timer, wakeup_timer_fn, (unsigned long)bdi);
+}
+
int bdi_init(struct backing_dev_info *bdi)
{
int i, err;
bdi->max_prop_frac = PROP_FRAC_BASE;
spin_lock_init(&bdi->wb_lock);
INIT_LIST_HEAD(&bdi->bdi_list);
- INIT_LIST_HEAD(&bdi->wb_list);
INIT_LIST_HEAD(&bdi->work_list);
bdi_wb_init(&bdi->wb, bdi);
static int kmemleak_stack_scan = 1;
/* protects the memory scanning, parameters and debug/kmemleak file access */
static DEFINE_MUTEX(scan_mutex);
+/* setting kmemleak=on, will set this var, skipping the disable */
+static int kmemleak_skip_disable;
+
/*
* Early object allocation/freeing logging. Kmemleak is initialized after the
object = prio_tree_entry(node, struct kmemleak_object,
tree_node);
if (!alias && object->pointer != ptr) {
- kmemleak_warn("Found object by alias");
+ pr_warning("Found object by alias at 0x%08lx\n", ptr);
+ dump_stack();
+ dump_object_info(object);
object = NULL;
}
} else
}
/*
- * Make a object permanently as gray-colored so that it can no longer be
+ * Mark an object permanently as gray-colored so that it can no longer be
* reported as a leak. This is used in general to mark a false positive.
*/
static void make_gray_object(unsigned long ptr)
rcu_read_unlock();
}
-/*
- * Memory allocation function callback. This function is called from the
- * kernel allocators when a new block is allocated (kmem_cache_alloc, kmalloc,
- * vmalloc etc.).
+/**
+ * kmemleak_alloc - register a newly allocated object
+ * @ptr: pointer to beginning of the object
+ * @size: size of the object
+ * @min_count: minimum number of references to this object. If during memory
+ * scanning a number of references less than @min_count is found,
+ * the object is reported as a memory leak. If @min_count is 0,
+ * the object is never reported as a leak. If @min_count is -1,
+ * the object is ignored (not scanned and not reported as a leak)
+ * @gfp: kmalloc() flags used for kmemleak internal memory allocations
+ *
+ * This function is called from the kernel allocators when a new object
+ * (memory block) is allocated (kmem_cache_alloc, kmalloc, vmalloc etc.).
*/
void __ref kmemleak_alloc(const void *ptr, size_t size, int min_count,
gfp_t gfp)
}
EXPORT_SYMBOL_GPL(kmemleak_alloc);
-/*
- * Memory freeing function callback. This function is called from the kernel
- * allocators when a block is freed (kmem_cache_free, kfree, vfree etc.).
+/**
+ * kmemleak_free - unregister a previously registered object
+ * @ptr: pointer to beginning of the object
+ *
+ * This function is called from the kernel allocators when an object (memory
+ * block) is freed (kmem_cache_free, kfree, vfree etc.).
*/
void __ref kmemleak_free(const void *ptr)
{
}
EXPORT_SYMBOL_GPL(kmemleak_free);
-/*
- * Partial memory freeing function callback. This function is usually called
- * from bootmem allocator when (part of) a memory block is freed.
+/**
+ * kmemleak_free_part - partially unregister a previously registered object
+ * @ptr: pointer to the beginning or inside the object. This also
+ * represents the start of the range to be freed
+ * @size: size to be unregistered
+ *
+ * This function is called when only a part of a memory block is freed
+ * (usually from the bootmem allocator).
*/
void __ref kmemleak_free_part(const void *ptr, size_t size)
{
}
EXPORT_SYMBOL_GPL(kmemleak_free_part);
-/*
- * Mark an already allocated memory block as a false positive. This will cause
- * the block to no longer be reported as leak and always be scanned.
+/**
+ * kmemleak_not_leak - mark an allocated object as false positive
+ * @ptr: pointer to beginning of the object
+ *
+ * Calling this function on an object will cause the memory block to no longer
+ * be reported as leak and always be scanned.
*/
void __ref kmemleak_not_leak(const void *ptr)
{
}
EXPORT_SYMBOL(kmemleak_not_leak);
-/*
- * Ignore a memory block. This is usually done when it is known that the
- * corresponding block is not a leak and does not contain any references to
- * other allocated memory blocks.
+/**
+ * kmemleak_ignore - ignore an allocated object
+ * @ptr: pointer to beginning of the object
+ *
+ * Calling this function on an object will cause the memory block to be
+ * ignored (not scanned and not reported as a leak). This is usually done when
+ * it is known that the corresponding block is not a leak and does not contain
+ * any references to other allocated memory blocks.
*/
void __ref kmemleak_ignore(const void *ptr)
{
}
EXPORT_SYMBOL(kmemleak_ignore);
-/*
- * Limit the range to be scanned in an allocated memory block.
+/**
+ * kmemleak_scan_area - limit the range to be scanned in an allocated object
+ * @ptr: pointer to beginning or inside the object. This also
+ * represents the start of the scan area
+ * @size: size of the scan area
+ * @gfp: kmalloc() flags used for kmemleak internal memory allocations
+ *
+ * This function is used when it is known that only certain parts of an object
+ * contain references to other objects. Kmemleak will only scan these areas
+ * reducing the number false negatives.
*/
void __ref kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp)
{
}
EXPORT_SYMBOL(kmemleak_scan_area);
-/*
- * Inform kmemleak not to scan the given memory block.
+/**
+ * kmemleak_no_scan - do not scan an allocated object
+ * @ptr: pointer to beginning of the object
+ *
+ * This function notifies kmemleak not to scan the given memory block. Useful
+ * in situations where it is known that the given object does not contain any
+ * references to other objects. Kmemleak will not scan such objects reducing
+ * the number of false negatives.
*/
void __ref kmemleak_no_scan(const void *ptr)
{
return -EINVAL;
if (strcmp(str, "off") == 0)
kmemleak_disable();
- else if (strcmp(str, "on") != 0)
+ else if (strcmp(str, "on") == 0)
+ kmemleak_skip_disable = 1;
+ else
return -EINVAL;
return 0;
}
int i;
unsigned long flags;
+#ifdef CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF
+ if (!kmemleak_skip_disable) {
+ kmemleak_disable();
+ return;
+ }
+#endif
+
jiffies_min_age = msecs_to_jiffies(MSECS_MIN_AGE);
jiffies_scan_wait = msecs_to_jiffies(SECS_SCAN_WAIT * 1000);
#include <linux/mm_inline.h>
#include <linux/page_cgroup.h>
#include <linux/cpu.h>
+#include <linux/oom.h>
#include "internal.h"
#include <asm/uaccess.h>
/* "mc" and its members are protected by cgroup_mutex */
static struct move_charge_struct {
+ spinlock_t lock; /* for from, to, moving_task */
struct mem_cgroup *from;
struct mem_cgroup *to;
unsigned long precharge;
struct task_struct *moving_task; /* a task moving charges */
wait_queue_head_t waitq; /* a waitq for other context */
} mc = {
+ .lock = __SPIN_LOCK_UNLOCKED(mc.lock),
.waitq = __WAIT_QUEUE_HEAD_INITIALIZER(mc.waitq),
};
{
int ret;
struct mem_cgroup *curr = NULL;
+ struct task_struct *p;
- task_lock(task);
- rcu_read_lock();
- curr = try_get_mem_cgroup_from_mm(task->mm);
- rcu_read_unlock();
- task_unlock(task);
+ p = find_lock_task_mm(task);
+ if (!p)
+ return 0;
+ curr = try_get_mem_cgroup_from_mm(p->mm);
+ task_unlock(p);
if (!curr)
return 0;
/*
struct zone *zone,
enum lru_list lru)
{
- int nid = zone->zone_pgdat->node_id;
+ int nid = zone_to_nid(zone);
int zid = zone_idx(zone);
struct mem_cgroup_per_zone *mz = mem_cgroup_zoneinfo(memcg, nid, zid);
struct zone_reclaim_stat *mem_cgroup_get_reclaim_stat(struct mem_cgroup *memcg,
struct zone *zone)
{
- int nid = zone->zone_pgdat->node_id;
+ int nid = zone_to_nid(zone);
int zid = zone_idx(zone);
struct mem_cgroup_per_zone *mz = mem_cgroup_zoneinfo(memcg, nid, zid);
LIST_HEAD(pc_list);
struct list_head *src;
struct page_cgroup *pc, *tmp;
- int nid = z->zone_pgdat->node_id;
+ int nid = zone_to_nid(z);
int zid = zone_idx(z);
struct mem_cgroup_per_zone *mz;
int lru = LRU_FILE * file + active;
return swappiness;
}
+/* A routine for testing mem is not under move_account */
+
+static bool mem_cgroup_under_move(struct mem_cgroup *mem)
+{
+ struct mem_cgroup *from;
+ struct mem_cgroup *to;
+ bool ret = false;
+ /*
+ * Unlike task_move routines, we access mc.to, mc.from not under
+ * mutual exclusion by cgroup_mutex. Here, we take spinlock instead.
+ */
+ spin_lock(&mc.lock);
+ from = mc.from;
+ to = mc.to;
+ if (!from)
+ goto unlock;
+ if (from == mem || to == mem
+ || (mem->use_hierarchy && css_is_ancestor(&from->css, &mem->css))
+ || (mem->use_hierarchy && css_is_ancestor(&to->css, &mem->css)))
+ ret = true;
+unlock:
+ spin_unlock(&mc.lock);
+ return ret;
+}
+
+static bool mem_cgroup_wait_acct_move(struct mem_cgroup *mem)
+{
+ if (mc.moving_task && current != mc.moving_task) {
+ if (mem_cgroup_under_move(mem)) {
+ DEFINE_WAIT(wait);
+ prepare_to_wait(&mc.waitq, &wait, TASK_INTERRUPTIBLE);
+ /* moving charge context might have finished. */
+ if (mc.moving_task)
+ schedule();
+ finish_wait(&mc.waitq, &wait);
+ return true;
+ }
+ }
+ return false;
+}
+
static int mem_cgroup_count_children_cb(struct mem_cgroup *mem, void *data)
{
int *val = data;
/* we use swappiness of local cgroup */
if (check_soft)
ret = mem_cgroup_shrink_node_zone(victim, gfp_mask,
- noswap, get_swappiness(victim), zone,
- zone->zone_pgdat->node_id);
+ noswap, get_swappiness(victim), zone);
else
ret = try_to_free_mem_cgroup_pages(victim, gfp_mask,
noswap, get_swappiness(victim));
static void memcg_oom_recover(struct mem_cgroup *mem)
{
- if (atomic_read(&mem->oom_lock))
+ if (mem && atomic_read(&mem->oom_lock))
memcg_wakeup_oom(mem);
}
return NOTIFY_OK;
}
+
+/* See __mem_cgroup_try_charge() for details */
+enum {
+ CHARGE_OK, /* success */
+ CHARGE_RETRY, /* need to retry but retry is not bad */
+ CHARGE_NOMEM, /* we can't do more. return -ENOMEM */
+ CHARGE_WOULDBLOCK, /* GFP_WAIT wasn't set and no enough res. */
+ CHARGE_OOM_DIE, /* the current is killed because of OOM */
+};
+
+static int __mem_cgroup_do_charge(struct mem_cgroup *mem, gfp_t gfp_mask,
+ int csize, bool oom_check)
+{
+ struct mem_cgroup *mem_over_limit;
+ struct res_counter *fail_res;
+ unsigned long flags = 0;
+ int ret;
+
+ ret = res_counter_charge(&mem->res, csize, &fail_res);
+
+ if (likely(!ret)) {
+ if (!do_swap_account)
+ return CHARGE_OK;
+ ret = res_counter_charge(&mem->memsw, csize, &fail_res);
+ if (likely(!ret))
+ return CHARGE_OK;
+
+ mem_over_limit = mem_cgroup_from_res_counter(fail_res, memsw);
+ flags |= MEM_CGROUP_RECLAIM_NOSWAP;
+ } else
+ mem_over_limit = mem_cgroup_from_res_counter(fail_res, res);
+
+ if (csize > PAGE_SIZE) /* change csize and retry */
+ return CHARGE_RETRY;
+
+ if (!(gfp_mask & __GFP_WAIT))
+ return CHARGE_WOULDBLOCK;
+
+ ret = mem_cgroup_hierarchical_reclaim(mem_over_limit, NULL,
+ gfp_mask, flags);
+ /*
+ * try_to_free_mem_cgroup_pages() might not give us a full
+ * picture of reclaim. Some pages are reclaimed and might be
+ * moved to swap cache or just unmapped from the cgroup.
+ * Check the limit again to see if the reclaim reduced the
+ * current usage of the cgroup before giving up
+ */
+ if (ret || mem_cgroup_check_under_limit(mem_over_limit))
+ return CHARGE_RETRY;
+
+ /*
+ * At task move, charge accounts can be doubly counted. So, it's
+ * better to wait until the end of task_move if something is going on.
+ */
+ if (mem_cgroup_wait_acct_move(mem_over_limit))
+ return CHARGE_RETRY;
+
+ /* If we don't need to call oom-killer at el, return immediately */
+ if (!oom_check)
+ return CHARGE_NOMEM;
+ /* check OOM */
+ if (!mem_cgroup_handle_oom(mem_over_limit, gfp_mask))
+ return CHARGE_OOM_DIE;
+
+ return CHARGE_RETRY;
+}
+
/*
* Unlike exported interface, "oom" parameter is added. if oom==true,
* oom-killer can be invoked.
*/
static int __mem_cgroup_try_charge(struct mm_struct *mm,
- gfp_t gfp_mask, struct mem_cgroup **memcg, bool oom)
+ gfp_t gfp_mask, struct mem_cgroup **memcg, bool oom)
{
- struct mem_cgroup *mem, *mem_over_limit;
- int nr_retries = MEM_CGROUP_RECLAIM_RETRIES;
- struct res_counter *fail_res;
+ int nr_oom_retries = MEM_CGROUP_RECLAIM_RETRIES;
+ struct mem_cgroup *mem = NULL;
+ int ret;
int csize = CHARGE_SIZE;
/*
* thread group leader migrates. It's possible that mm is not
* set, if so charge the init_mm (happens for pagecache usage).
*/
- mem = *memcg;
- if (likely(!mem)) {
- mem = try_get_mem_cgroup_from_mm(mm);
- *memcg = mem;
- } else {
- css_get(&mem->css);
- }
- if (unlikely(!mem))
- return 0;
-
- VM_BUG_ON(css_is_removed(&mem->css));
- if (mem_cgroup_is_root(mem))
- goto done;
-
- while (1) {
- int ret = 0;
- unsigned long flags = 0;
-
+ if (!*memcg && !mm)
+ goto bypass;
+again:
+ if (*memcg) { /* css should be a valid one */
+ mem = *memcg;
+ VM_BUG_ON(css_is_removed(&mem->css));
+ if (mem_cgroup_is_root(mem))
+ goto done;
if (consume_stock(mem))
goto done;
+ css_get(&mem->css);
+ } else {
+ struct task_struct *p;
- ret = res_counter_charge(&mem->res, csize, &fail_res);
- if (likely(!ret)) {
- if (!do_swap_account)
- break;
- ret = res_counter_charge(&mem->memsw, csize, &fail_res);
- if (likely(!ret))
- break;
- /* mem+swap counter fails */
- res_counter_uncharge(&mem->res, csize);
- flags |= MEM_CGROUP_RECLAIM_NOSWAP;
- mem_over_limit = mem_cgroup_from_res_counter(fail_res,
- memsw);
- } else
- /* mem counter fails */
- mem_over_limit = mem_cgroup_from_res_counter(fail_res,
- res);
-
- /* reduce request size and retry */
- if (csize > PAGE_SIZE) {
- csize = PAGE_SIZE;
- continue;
- }
- if (!(gfp_mask & __GFP_WAIT))
- goto nomem;
-
- ret = mem_cgroup_hierarchical_reclaim(mem_over_limit, NULL,
- gfp_mask, flags);
- if (ret)
- continue;
-
+ rcu_read_lock();
+ p = rcu_dereference(mm->owner);
+ VM_BUG_ON(!p);
/*
- * try_to_free_mem_cgroup_pages() might not give us a full
- * picture of reclaim. Some pages are reclaimed and might be
- * moved to swap cache or just unmapped from the cgroup.
- * Check the limit again to see if the reclaim reduced the
- * current usage of the cgroup before giving up
- *
+ * because we don't have task_lock(), "p" can exit while
+ * we're here. In that case, "mem" can point to root
+ * cgroup but never be NULL. (and task_struct itself is freed
+ * by RCU, cgroup itself is RCU safe.) Then, we have small
+ * risk here to get wrong cgroup. But such kind of mis-account
+ * by race always happens because we don't have cgroup_mutex().
+ * It's overkill and we allow that small race, here.
*/
- if (mem_cgroup_check_under_limit(mem_over_limit))
- continue;
-
- /* try to avoid oom while someone is moving charge */
- if (mc.moving_task && current != mc.moving_task) {
- struct mem_cgroup *from, *to;
- bool do_continue = false;
+ mem = mem_cgroup_from_task(p);
+ VM_BUG_ON(!mem);
+ if (mem_cgroup_is_root(mem)) {
+ rcu_read_unlock();
+ goto done;
+ }
+ if (consume_stock(mem)) {
/*
- * There is a small race that "from" or "to" can be
- * freed by rmdir, so we use css_tryget().
+ * It seems dagerous to access memcg without css_get().
+ * But considering how consume_stok works, it's not
+ * necessary. If consume_stock success, some charges
+ * from this memcg are cached on this cpu. So, we
+ * don't need to call css_get()/css_tryget() before
+ * calling consume_stock().
*/
- from = mc.from;
- to = mc.to;
- if (from && css_tryget(&from->css)) {
- if (mem_over_limit->use_hierarchy)
- do_continue = css_is_ancestor(
- &from->css,
- &mem_over_limit->css);
- else
- do_continue = (from == mem_over_limit);
- css_put(&from->css);
- }
- if (!do_continue && to && css_tryget(&to->css)) {
- if (mem_over_limit->use_hierarchy)
- do_continue = css_is_ancestor(
- &to->css,
- &mem_over_limit->css);
- else
- do_continue = (to == mem_over_limit);
- css_put(&to->css);
- }
- if (do_continue) {
- DEFINE_WAIT(wait);
- prepare_to_wait(&mc.waitq, &wait,
- TASK_INTERRUPTIBLE);
- /* moving charge context might have finished. */
- if (mc.moving_task)
- schedule();
- finish_wait(&mc.waitq, &wait);
- continue;
- }
+ rcu_read_unlock();
+ goto done;
+ }
+ /* after here, we may be blocked. we need to get refcnt */
+ if (!css_tryget(&mem->css)) {
+ rcu_read_unlock();
+ goto again;
+ }
+ rcu_read_unlock();
+ }
+
+ do {
+ bool oom_check;
+
+ /* If killed, bypass charge */
+ if (fatal_signal_pending(current)) {
+ css_put(&mem->css);
+ goto bypass;
}
- if (!nr_retries--) {
- if (!oom)
+ oom_check = false;
+ if (oom && !nr_oom_retries) {
+ oom_check = true;
+ nr_oom_retries = MEM_CGROUP_RECLAIM_RETRIES;
+ }
+
+ ret = __mem_cgroup_do_charge(mem, gfp_mask, csize, oom_check);
+
+ switch (ret) {
+ case CHARGE_OK:
+ break;
+ case CHARGE_RETRY: /* not in OOM situation but retry */
+ csize = PAGE_SIZE;
+ css_put(&mem->css);
+ mem = NULL;
+ goto again;
+ case CHARGE_WOULDBLOCK: /* !__GFP_WAIT */
+ css_put(&mem->css);
+ goto nomem;
+ case CHARGE_NOMEM: /* OOM routine works */
+ if (!oom) {
+ css_put(&mem->css);
goto nomem;
- if (mem_cgroup_handle_oom(mem_over_limit, gfp_mask)) {
- nr_retries = MEM_CGROUP_RECLAIM_RETRIES;
- continue;
}
- /* When we reach here, current task is dying .*/
+ /* If oom, we never return -ENOMEM */
+ nr_oom_retries--;
+ break;
+ case CHARGE_OOM_DIE: /* Killed by OOM Killer */
css_put(&mem->css);
goto bypass;
}
- }
+ } while (ret != CHARGE_OK);
+
if (csize > PAGE_SIZE)
refill_stock(mem, csize - PAGE_SIZE);
+ css_put(&mem->css);
done:
+ *memcg = mem;
return 0;
nomem:
- css_put(&mem->css);
+ *memcg = NULL;
return -ENOMEM;
bypass:
*memcg = NULL;
res_counter_uncharge(&mem->res, PAGE_SIZE * count);
if (do_swap_account)
res_counter_uncharge(&mem->memsw, PAGE_SIZE * count);
- VM_BUG_ON(test_bit(CSS_ROOT, &mem->css.flags));
- WARN_ON_ONCE(count > INT_MAX);
- __css_put(&mem->css, (int)count);
}
- /* we don't need css_put for root */
}
static void mem_cgroup_cancel_charge(struct mem_cgroup *mem)
* < 0 if the cgroup is over its limit
*/
static int mem_cgroup_charge_common(struct page *page, struct mm_struct *mm,
- gfp_t gfp_mask, enum charge_type ctype,
- struct mem_cgroup *memcg)
+ gfp_t gfp_mask, enum charge_type ctype)
{
- struct mem_cgroup *mem;
+ struct mem_cgroup *mem = NULL;
struct page_cgroup *pc;
int ret;
return 0;
prefetchw(pc);
- mem = memcg;
ret = __mem_cgroup_try_charge(mm, gfp_mask, &mem, true);
if (ret || !mem)
return ret;
if (unlikely(!mm))
mm = &init_mm;
return mem_cgroup_charge_common(page, mm, gfp_mask,
- MEM_CGROUP_CHARGE_TYPE_MAPPED, NULL);
+ MEM_CGROUP_CHARGE_TYPE_MAPPED);
}
static void
int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm,
gfp_t gfp_mask)
{
- struct mem_cgroup *mem = NULL;
int ret;
if (mem_cgroup_disabled())
if (!(gfp_mask & __GFP_WAIT)) {
struct page_cgroup *pc;
-
pc = lookup_page_cgroup(page);
if (!pc)
return 0;
unlock_page_cgroup(pc);
}
- if (unlikely(!mm && !mem))
+ if (unlikely(!mm))
mm = &init_mm;
if (page_is_file_cache(page))
return mem_cgroup_charge_common(page, mm, gfp_mask,
- MEM_CGROUP_CHARGE_TYPE_CACHE, NULL);
+ MEM_CGROUP_CHARGE_TYPE_CACHE);
/* shmem */
if (PageSwapCache(page)) {
+ struct mem_cgroup *mem = NULL;
+
ret = mem_cgroup_try_charge_swapin(mm, page, gfp_mask, &mem);
if (!ret)
__mem_cgroup_commit_charge_swapin(page, mem,
MEM_CGROUP_CHARGE_TYPE_SHMEM);
} else
ret = mem_cgroup_charge_common(page, mm, gfp_mask,
- MEM_CGROUP_CHARGE_TYPE_SHMEM, mem);
+ MEM_CGROUP_CHARGE_TYPE_SHMEM);
return ret;
}
goto charge_cur_mm;
*ptr = mem;
ret = __mem_cgroup_try_charge(NULL, mask, ptr, true);
- /* drop extra refcnt from tryget */
css_put(&mem->css);
return ret;
charge_cur_mm:
{
struct page_cgroup *pc;
struct mem_cgroup *mem = NULL;
- struct mem_cgroup_per_zone *mz;
if (mem_cgroup_disabled())
return NULL;
break;
}
- if (!mem_cgroup_is_root(mem))
- __do_uncharge(mem, ctype);
- if (ctype == MEM_CGROUP_CHARGE_TYPE_SWAPOUT)
- mem_cgroup_swap_statistics(mem, true);
mem_cgroup_charge_statistics(mem, pc, false);
ClearPageCgroupUsed(pc);
* special functions.
*/
- mz = page_cgroup_zoneinfo(pc);
unlock_page_cgroup(pc);
-
+ /*
+ * even after unlock, we have mem->res.usage here and this memcg
+ * will never be freed.
+ */
memcg_check_events(mem, page);
- /* at swapout, this memcg will be accessed to record to swap */
- if (ctype != MEM_CGROUP_CHARGE_TYPE_SWAPOUT)
- css_put(&mem->css);
+ if (do_swap_account && ctype == MEM_CGROUP_CHARGE_TYPE_SWAPOUT) {
+ mem_cgroup_swap_statistics(mem, true);
+ mem_cgroup_get(mem);
+ }
+ if (!mem_cgroup_is_root(mem))
+ __do_uncharge(mem, ctype);
return mem;
memcg = __mem_cgroup_uncharge_common(page, ctype);
- /* record memcg information */
- if (do_swap_account && swapout && memcg) {
+ /*
+ * record memcg information, if swapout && memcg != NULL,
+ * mem_cgroup_get() was called in uncharge().
+ */
+ if (do_swap_account && swapout && memcg)
swap_cgroup_record(ent, css_id(&memcg->css));
- mem_cgroup_get(memcg);
- }
- if (swapout && memcg)
- css_put(&memcg->css);
}
#endif
*/
if (!mem_cgroup_is_root(to))
res_counter_uncharge(&to->res, PAGE_SIZE);
- css_put(&to->css);
}
return 0;
}
ClearPageCgroupMigration(pc);
unlock_page_cgroup(pc);
- if (unused != oldpage)
- pc = lookup_page_cgroup(unused);
__mem_cgroup_uncharge_common(unused, MEM_CGROUP_CHARGE_TYPE_FORCE);
- pc = lookup_page_cgroup(used);
/*
* If a page is a file cache, radix-tree replacement is very atomic
* and we can skip this check. When it was an Anon page, its mapcount
}
unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order,
- gfp_t gfp_mask, int nid,
- int zid)
+ gfp_t gfp_mask)
{
unsigned long nr_reclaimed = 0;
struct mem_cgroup_per_zone *mz, *next_mz = NULL;
if (order > 0)
return 0;
- mctz = soft_limit_tree_node_zone(nid, zid);
+ mctz = soft_limit_tree_node_zone(zone_to_nid(zone), zone_idx(zone));
/*
* This loop can run a while, specially if mem_cgroup's continuously
* keep exceeding their soft limit and putting the system under
return 0;
}
-/*
- */
static int mem_cgroup_oom_control_write(struct cgroup *cgrp,
struct cftype *cft, u64 val)
{
goto one_by_one;
}
mc.precharge += count;
- VM_BUG_ON(test_bit(CSS_ROOT, &mem->css.flags));
- WARN_ON_ONCE(count > INT_MAX);
- __css_get(&mem->css, (int)count);
return ret;
}
one_by_one:
static void mem_cgroup_clear_mc(void)
{
+ struct mem_cgroup *from = mc.from;
+ struct mem_cgroup *to = mc.to;
+
/* we must uncharge all the leftover precharges from mc.to */
if (mc.precharge) {
__mem_cgroup_cancel_charge(mc.to, mc.precharge);
mc.precharge = 0;
- memcg_oom_recover(mc.to);
}
/*
* we didn't uncharge from mc.from at mem_cgroup_move_account(), so
if (mc.moved_charge) {
__mem_cgroup_cancel_charge(mc.from, mc.moved_charge);
mc.moved_charge = 0;
- memcg_oom_recover(mc.from);
}
/* we must fixup refcnts and charges */
if (mc.moved_swap) {
- WARN_ON_ONCE(mc.moved_swap > INT_MAX);
/* uncharge swap account from the old cgroup */
if (!mem_cgroup_is_root(mc.from))
res_counter_uncharge(&mc.from->memsw,
*/
res_counter_uncharge(&mc.to->res,
PAGE_SIZE * mc.moved_swap);
- VM_BUG_ON(test_bit(CSS_ROOT, &mc.to->css.flags));
- __css_put(&mc.to->css, mc.moved_swap);
}
/* we've already done mem_cgroup_get(mc.to) */
mc.moved_swap = 0;
}
+ spin_lock(&mc.lock);
mc.from = NULL;
mc.to = NULL;
mc.moving_task = NULL;
+ spin_unlock(&mc.lock);
+ memcg_oom_recover(from);
+ memcg_oom_recover(to);
wake_up_all(&mc.waitq);
}
VM_BUG_ON(mc.moved_charge);
VM_BUG_ON(mc.moved_swap);
VM_BUG_ON(mc.moving_task);
+ spin_lock(&mc.lock);
mc.from = from;
mc.to = mem;
mc.precharge = 0;
mc.moved_charge = 0;
mc.moved_swap = 0;
mc.moving_task = current;
+ spin_unlock(&mc.lock);
ret = mem_cgroup_precharge_mc(mm);
if (ret)
* pointer. Return p, or any of its subthreads with a valid ->mm, with
* task_lock() held.
*/
-static struct task_struct *find_lock_task_mm(struct task_struct *p)
+struct task_struct *find_lock_task_mm(struct task_struct *p)
{
struct task_struct *t = p;
#include <linux/syscalls.h>
#include <linux/buffer_head.h>
#include <linux/pagevec.h>
+#include <trace/events/writeback.h>
/*
* After a CPU has dirtied this many pages, balance_dirty_pages_ratelimited
* threshold otherwise wait until the disk writes catch
* up.
*/
+ trace_wbc_balance_dirty_start(&wbc, bdi);
if (bdi_nr_reclaimable > bdi_thresh) {
writeback_inodes_wb(&bdi->wb, &wbc);
pages_written += write_chunk - wbc.nr_to_write;
get_dirty_limits(&background_thresh, &dirty_thresh,
&bdi_thresh, bdi);
+ trace_wbc_balance_dirty_written(&wbc, bdi);
}
/*
if (pages_written >= write_chunk)
break; /* We've done our duty */
+ trace_wbc_balance_dirty_wait(&wbc, bdi);
__set_current_state(TASK_INTERRUPTIBLE);
io_schedule_timeout(pause);
if (!clear_page_dirty_for_io(page))
goto continue_unlock;
+ trace_wbc_writepage(wbc, mapping->backing_dev_info);
ret = (*writepage)(page, wbc, data);
if (unlikely(ret)) {
if (ret == AOP_WRITEPAGE_ACTIVATE) {
goto out;
}
if (wbc->sync_mode == WB_SYNC_ALL)
- rw |= (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG);
+ rw |= REQ_SYNC | REQ_UNPLUG;
count_vm_event(PSWPOUT);
set_page_writeback(page);
unlock_page(page);
unsigned long mem_cgroup_shrink_node_zone(struct mem_cgroup *mem,
gfp_t gfp_mask, bool noswap,
unsigned int swappiness,
- struct zone *zone, int nid)
+ struct zone *zone)
{
struct scan_control sc = {
+ .nr_to_reclaim = SWAP_CLUSTER_MAX,
.may_writepage = !laptop_mode,
.may_unmap = 1,
.may_swap = !noswap,
.order = 0,
.mem_cgroup = mem,
};
- nodemask_t nm = nodemask_of_node(nid);
-
sc.gfp_mask = (gfp_mask & GFP_RECLAIM_MASK) |
(GFP_HIGHUSER_MOVABLE & ~GFP_RECLAIM_MASK);
- sc.nodemask = &nm;
- sc.nr_reclaimed = 0;
- sc.nr_scanned = 0;
trace_mm_vmscan_memcg_softlimit_reclaim_begin(0,
sc.may_writepage,
for (i = 0; i <= end_zone; i++) {
struct zone *zone = pgdat->node_zones + i;
int nr_slab;
- int nid, zid;
if (!populated_zone(zone))
continue;
sc.nr_scanned = 0;
- nid = pgdat->node_id;
- zid = zone_idx(zone);
/*
* Call soft limit reclaim before calling shrink_zone.
* For now we ignore the return value
*/
- mem_cgroup_soft_limit_reclaim(zone, order, sc.gfp_mask,
- nid, zid);
+ mem_cgroup_soft_limit_reclaim(zone, order, sc.gfp_mask);
+
/*
* We put equal pressure on every zone, unless one
* zone has way too many pages free already.
help
This builds kernel hardware breakpoint example modules.
+config SAMPLE_KFIFO
+ tristate "Build kfifo examples -- loadable modules only"
+ depends on m
+ help
+ This config option will allow you to build a number of
+ different kfifo sample modules showing how to use the
+ generic kfifo API.
+
+ If in doubt, say "N" here.
+
endif # SAMPLES
# Makefile for Linux samples code
obj-$(CONFIG_SAMPLES) += kobject/ kprobes/ tracepoints/ trace_events/ \
- hw_breakpoint/
+ hw_breakpoint/ kfifo/
--- /dev/null
+obj-$(CONFIG_SAMPLE_KFIFO) += bytestream-example.o dma-example.o inttype-example.o record-example.o
--- /dev/null
+/*
+ * Sample kfifo byte stream implementation
+ *
+ * Copyright (C) 2010 Stefani Seibold <stefani@seibold.net>
+ *
+ * Released under the GPL version 2 only.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/mutex.h>
+#include <linux/kfifo.h>
+
+/*
+ * This module shows how to create a byte stream fifo.
+ */
+
+/* fifo size in elements (bytes) */
+#define FIFO_SIZE 32
+
+/* name of the proc entry */
+#define PROC_FIFO "bytestream-fifo"
+
+/* lock for procfs read access */
+static DEFINE_MUTEX(read_lock);
+
+/* lock for procfs write access */
+static DEFINE_MUTEX(write_lock);
+
+/*
+ * define DYNAMIC in this example for a dynamically allocated fifo.
+ *
+ * Otherwise the fifo storage will be a part of the fifo structure.
+ */
+#if 0
+#define DYNAMIC
+#endif
+
+#ifdef DYNAMIC
+static struct kfifo test;
+#else
+static DECLARE_KFIFO(test, unsigned char, FIFO_SIZE);
+#endif
+
+static int __init testfunc(void)
+{
+ unsigned char buf[6];
+ unsigned char i;
+ unsigned int ret;
+
+ printk(KERN_INFO "byte stream fifo test start\n");
+
+ /* put string into the fifo */
+ kfifo_in(&test, "hello", 5);
+
+ /* put values into the fifo */
+ for (i = 0; i != 10; i++)
+ kfifo_put(&test, &i);
+
+ /* show the number of used elements */
+ printk(KERN_INFO "fifo len: %u\n", kfifo_len(&test));
+
+ /* get max of 5 bytes from the fifo */
+ i = kfifo_out(&test, buf, 5);
+ printk(KERN_INFO "buf: %.*s\n", i, buf);
+
+ /* get max of 2 elements from the fifo */
+ ret = kfifo_out(&test, buf, 2);
+ printk(KERN_INFO "ret: %d\n", ret);
+ /* and put it back to the end of the fifo */
+ ret = kfifo_in(&test, buf, ret);
+ printk(KERN_INFO "ret: %d\n", ret);
+
+ /* put values into the fifo until is full */
+ for (i = 20; kfifo_put(&test, &i); i++)
+ ;
+
+ printk(KERN_INFO "queue len: %u\n", kfifo_len(&test));
+
+ /* print out all values in the fifo */
+ while (kfifo_get(&test, &i))
+ printk("%d ", i);
+ printk("\n");
+
+ return 0;
+}
+
+static ssize_t fifo_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret;
+ unsigned int copied;
+
+ if (mutex_lock_interruptible(&write_lock))
+ return -ERESTARTSYS;
+
+ ret = kfifo_from_user(&test, buf, count, &copied);
+
+ mutex_unlock(&write_lock);
+
+ return ret ? ret : copied;
+}
+
+static ssize_t fifo_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret;
+ unsigned int copied;
+
+ if (mutex_lock_interruptible(&read_lock))
+ return -ERESTARTSYS;
+
+ ret = kfifo_to_user(&test, buf, count, &copied);
+
+ mutex_unlock(&read_lock);
+
+ return ret ? ret : copied;
+}
+
+static const struct file_operations fifo_fops = {
+ .owner = THIS_MODULE,
+ .read = fifo_read,
+ .write = fifo_write,
+};
+
+static int __init example_init(void)
+{
+#ifdef DYNAMIC
+ int ret;
+
+ ret = kfifo_alloc(&test, FIFO_SIZE, GFP_KERNEL);
+ if (ret) {
+ printk(KERN_ERR "error kfifo_alloc\n");
+ return ret;
+ }
+#else
+ INIT_KFIFO(test);
+#endif
+ testfunc();
+
+ if (proc_create(PROC_FIFO, 0, NULL, &fifo_fops) == NULL) {
+#ifdef DYNAMIC
+ kfifo_free(&test);
+#endif
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void __exit example_exit(void)
+{
+ remove_proc_entry(PROC_FIFO, NULL);
+#ifdef DYNAMIC
+ kfifo_free(&test);
+#endif
+}
+
+module_init(example_init);
+module_exit(example_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Stefani Seibold <stefani@seibold.net>");
--- /dev/null
+/*
+ * Sample fifo dma implementation
+ *
+ * Copyright (C) 2010 Stefani Seibold <stefani@seibold.net>
+ *
+ * Released under the GPL version 2 only.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kfifo.h>
+
+/*
+ * This module shows how to handle fifo dma operations.
+ */
+
+/* fifo size in elements (bytes) */
+#define FIFO_SIZE 32
+
+static struct kfifo fifo;
+
+static int __init example_init(void)
+{
+ int i;
+ unsigned int ret;
+ struct scatterlist sg[10];
+
+ printk(KERN_INFO "DMA fifo test start\n");
+
+ if (kfifo_alloc(&fifo, FIFO_SIZE, GFP_KERNEL)) {
+ printk(KERN_ERR "error kfifo_alloc\n");
+ return 1;
+ }
+
+ printk(KERN_INFO "queue size: %u\n", kfifo_size(&fifo));
+
+ kfifo_in(&fifo, "test", 4);
+
+ for (i = 0; i != 9; i++)
+ kfifo_put(&fifo, &i);
+
+ /* kick away first byte */
+ ret = kfifo_get(&fifo, &i);
+
+ printk(KERN_INFO "queue len: %u\n", kfifo_len(&fifo));
+
+ ret = kfifo_dma_in_prepare(&fifo, sg, ARRAY_SIZE(sg), FIFO_SIZE);
+ printk(KERN_INFO "DMA sgl entries: %d\n", ret);
+
+ /* if 0 was returned, fifo is full and no sgl was created */
+ if (ret) {
+ printk(KERN_INFO "scatterlist for receive:\n");
+ for (i = 0; i < ARRAY_SIZE(sg); i++) {
+ printk(KERN_INFO
+ "sg[%d] -> "
+ "page_link 0x%.8lx offset 0x%.8x length 0x%.8x\n",
+ i, sg[i].page_link, sg[i].offset, sg[i].length);
+
+ if (sg_is_last(&sg[i]))
+ break;
+ }
+
+ /* but here your code to setup and exectute the dma operation */
+ /* ... */
+
+ /* example: zero bytes received */
+ ret = 0;
+
+ /* finish the dma operation and update the received data */
+ kfifo_dma_in_finish(&fifo, ret);
+ }
+
+ ret = kfifo_dma_out_prepare(&fifo, sg, ARRAY_SIZE(sg), 8);
+ printk(KERN_INFO "DMA sgl entries: %d\n", ret);
+
+ /* if 0 was returned, no data was available and no sgl was created */
+ if (ret) {
+ printk(KERN_INFO "scatterlist for transmit:\n");
+ for (i = 0; i < ARRAY_SIZE(sg); i++) {
+ printk(KERN_INFO
+ "sg[%d] -> "
+ "page_link 0x%.8lx offset 0x%.8x length 0x%.8x\n",
+ i, sg[i].page_link, sg[i].offset, sg[i].length);
+
+ if (sg_is_last(&sg[i]))
+ break;
+ }
+
+ /* but here your code to setup and exectute the dma operation */
+ /* ... */
+
+ /* example: 5 bytes transmitted */
+ ret = 5;
+
+ /* finish the dma operation and update the transmitted data */
+ kfifo_dma_out_finish(&fifo, ret);
+ }
+
+ printk(KERN_INFO "queue len: %u\n", kfifo_len(&fifo));
+
+ return 0;
+}
+
+static void __exit example_exit(void)
+{
+#ifdef DYNAMIC
+ kfifo_free(&test);
+#endif
+}
+
+module_init(example_init);
+module_exit(example_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Stefani Seibold <stefani@seibold.net>");
--- /dev/null
+/*
+ * Sample kfifo int type implementation
+ *
+ * Copyright (C) 2010 Stefani Seibold <stefani@seibold.net>
+ *
+ * Released under the GPL version 2 only.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/mutex.h>
+#include <linux/kfifo.h>
+
+/*
+ * This module shows how to create a int type fifo.
+ */
+
+/* fifo size in elements (ints) */
+#define FIFO_SIZE 32
+
+/* name of the proc entry */
+#define PROC_FIFO "int-fifo"
+
+/* lock for procfs read access */
+static DEFINE_MUTEX(read_lock);
+
+/* lock for procfs write access */
+static DEFINE_MUTEX(write_lock);
+
+/*
+ * define DYNAMIC in this example for a dynamically allocated fifo.
+ *
+ * Otherwise the fifo storage will be a part of the fifo structure.
+ */
+#if 0
+#define DYNAMIC
+#endif
+
+#ifdef DYNAMIC
+static DECLARE_KFIFO_PTR(test, int);
+#else
+static DEFINE_KFIFO(test, int, FIFO_SIZE);
+#endif
+
+static int __init testfunc(void)
+{
+ int buf[6];
+ int i;
+ unsigned int ret;
+
+ printk(KERN_INFO "int fifo test start\n");
+
+ /* put values into the fifo */
+ for (i = 0; i != 10; i++)
+ kfifo_put(&test, &i);
+
+ /* show the number of used elements */
+ printk(KERN_INFO "fifo len: %u\n", kfifo_len(&test));
+
+ /* get max of 2 elements from the fifo */
+ ret = kfifo_out(&test, buf, 2);
+ printk(KERN_INFO "ret: %d\n", ret);
+ /* and put it back to the end of the fifo */
+ ret = kfifo_in(&test, buf, ret);
+ printk(KERN_INFO "ret: %d\n", ret);
+
+ for (i = 20; i != 30; i++)
+ kfifo_put(&test, &i);
+
+ printk(KERN_INFO "queue len: %u\n", kfifo_len(&test));
+
+ /* show the first value without removing from the fifo */
+ if (kfifo_peek(&test, &i))
+ printk(KERN_INFO "%d\n", i);
+
+ /* print out all values in the fifo */
+ while (kfifo_get(&test, &i))
+ printk("%d ", i);
+ printk("\n");
+
+ return 0;
+}
+
+static ssize_t fifo_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret;
+ unsigned int copied;
+
+ if (mutex_lock_interruptible(&write_lock))
+ return -ERESTARTSYS;
+
+ ret = kfifo_from_user(&test, buf, count, &copied);
+
+ mutex_unlock(&write_lock);
+
+ return ret ? ret : copied;
+}
+
+static ssize_t fifo_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret;
+ unsigned int copied;
+
+ if (mutex_lock_interruptible(&read_lock))
+ return -ERESTARTSYS;
+
+ ret = kfifo_to_user(&test, buf, count, &copied);
+
+ mutex_unlock(&read_lock);
+
+ return ret ? ret : copied;
+}
+
+static const struct file_operations fifo_fops = {
+ .owner = THIS_MODULE,
+ .read = fifo_read,
+ .write = fifo_write,
+};
+
+static int __init example_init(void)
+{
+#ifdef DYNAMIC
+ int ret;
+
+ ret = kfifo_alloc(&test, FIFO_SIZE, GFP_KERNEL);
+ if (ret) {
+ printk(KERN_ERR "error kfifo_alloc\n");
+ return ret;
+ }
+#endif
+ testfunc();
+
+ if (proc_create(PROC_FIFO, 0, NULL, &fifo_fops) == NULL) {
+#ifdef DYNAMIC
+ kfifo_free(&test);
+#endif
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void __exit example_exit(void)
+{
+ remove_proc_entry(PROC_FIFO, NULL);
+#ifdef DYNAMIC
+ kfifo_free(&test);
+#endif
+}
+
+module_init(example_init);
+module_exit(example_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Stefani Seibold <stefani@seibold.net>");
--- /dev/null
+/*
+ * Sample dynamic sized record fifo implementation
+ *
+ * Copyright (C) 2010 Stefani Seibold <stefani@seibold.net>
+ *
+ * Released under the GPL version 2 only.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/mutex.h>
+#include <linux/kfifo.h>
+
+/*
+ * This module shows how to create a variable sized record fifo.
+ */
+
+/* fifo size in elements (bytes) */
+#define FIFO_SIZE 128
+
+/* name of the proc entry */
+#define PROC_FIFO "record-fifo"
+
+/* lock for procfs read access */
+static DEFINE_MUTEX(read_lock);
+
+/* lock for procfs write access */
+static DEFINE_MUTEX(write_lock);
+
+/*
+ * define DYNAMIC in this example for a dynamically allocated fifo.
+ *
+ * Otherwise the fifo storage will be a part of the fifo structure.
+ */
+#if 0
+#define DYNAMIC
+#endif
+
+/*
+ * struct kfifo_rec_ptr_1 and STRUCT_KFIFO_REC_1 can handle records of a
+ * length between 0 and 255 bytes.
+ *
+ * struct kfifo_rec_ptr_2 and STRUCT_KFIFO_REC_2 can handle records of a
+ * length between 0 and 65535 bytes.
+ */
+
+#ifdef DYNAMIC
+struct kfifo_rec_ptr_1 test;
+
+#else
+typedef STRUCT_KFIFO_REC_1(FIFO_SIZE) mytest;
+
+static mytest test;
+#endif
+
+static int __init testfunc(void)
+{
+ char buf[100];
+ unsigned int i;
+ unsigned int ret;
+ struct { unsigned char buf[6]; } hello = { "hello" };
+
+ printk(KERN_INFO "record fifo test start\n");
+
+ kfifo_in(&test, &hello, sizeof(hello));
+
+ /* show the size of the next record in the fifo */
+ printk(KERN_INFO "fifo peek len: %u\n", kfifo_peek_len(&test));
+
+ /* put in variable length data */
+ for (i = 0; i < 10; i++) {
+ memset(buf, 'a' + i, i + 1);
+ kfifo_in(&test, buf, i + 1);
+ }
+
+ printk(KERN_INFO "fifo len: %u\n", kfifo_len(&test));
+
+ /* show the first record without removing from the fifo */
+ ret = kfifo_out_peek(&test, buf, sizeof(buf));
+ if (ret)
+ printk(KERN_INFO "%.*s\n", ret, buf);
+
+ /* print out all records in the fifo */
+ while (!kfifo_is_empty(&test)) {
+ ret = kfifo_out(&test, buf, sizeof(buf));
+ printk(KERN_INFO "%.*s\n", ret, buf);
+ }
+
+ return 0;
+}
+
+static ssize_t fifo_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret;
+ unsigned int copied;
+
+ if (mutex_lock_interruptible(&write_lock))
+ return -ERESTARTSYS;
+
+ ret = kfifo_from_user(&test, buf, count, &copied);
+
+ mutex_unlock(&write_lock);
+
+ return ret ? ret : copied;
+}
+
+static ssize_t fifo_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret;
+ unsigned int copied;
+
+ if (mutex_lock_interruptible(&read_lock))
+ return -ERESTARTSYS;
+
+ ret = kfifo_to_user(&test, buf, count, &copied);
+
+ mutex_unlock(&read_lock);
+
+ return ret ? ret : copied;
+}
+
+static const struct file_operations fifo_fops = {
+ .owner = THIS_MODULE,
+ .read = fifo_read,
+ .write = fifo_write,
+};
+
+static int __init example_init(void)
+{
+#ifdef DYNAMIC
+ int ret;
+
+ ret = kfifo_alloc(&test, FIFO_SIZE, GFP_KERNEL);
+ if (ret) {
+ printk(KERN_ERR "error kfifo_alloc\n");
+ return ret;
+ }
+#else
+ INIT_KFIFO(test);
+#endif
+ testfunc();
+
+ if (proc_create(PROC_FIFO, 0, NULL, &fifo_fops) == NULL) {
+#ifdef DYNAMIC
+ kfifo_free(&test);
+#endif
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void __exit example_exit(void)
+{
+ remove_proc_entry(PROC_FIFO, NULL);
+#ifdef DYNAMIC
+ kfifo_free(&test);
+#endif
+}
+
+module_init(example_init);
+module_exit(example_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Stefani Seibold <stefani@seibold.net>");
my $file = shift;
$x =~ s@/\*.*?\*/@@gos; # strip comments.
+ $x =~ s/^#\s*define\s+.*$//; # strip #define macros inside enums
+
if ($x =~ /enum\s+(\w+)\s*{(.*)}/) {
$declaration_name = $1;
my $members = $2;
#define _GNU_SOURCE
#include <stdio.h>
#include <ctype.h>
+#include <string.h>
#include "modpost.h"
#include "../../include/generated/autoconf.h"
#include "../../include/linux/license.h"
strcat(p, " ");
return r; /* we leak her but we do not care */
} else {
- return "";
+ return strdup("");
}
}
"%s%s so it may be used outside an exit section.\n",
from, prl_from, fromsym, from_p,
to, prl_to, tosym, to_p,
- sec2annotation(tosec), tosym, to_p);
+ prl_to, tosym, to_p);
free(prl_from);
free(prl_to);
break;