which included commits to RCS files with non-trunk default branches.
struct pci_dev *pdev;
...
- if (pci_set_dma_mask(pdev, PLAYBACK_ADDRESS_BITS)) {
+ if (!pci_set_dma_mask(pdev, PLAYBACK_ADDRESS_BITS)) {
card->playback_enabled = 1;
} else {
card->playback_enabled = 0;
printk(KERN_WARN "%s: Playback disabled due to DMA limitations.\n",
card->name);
}
- if (pci_set_dma_mask(pdev, RECORD_ADDRESS_BITS)) {
+ if (!pci_set_dma_mask(pdev, RECORD_ADDRESS_BITS)) {
card->record_enabled = 1;
} else {
card->record_enabled = 0;
modprobe ipmi_watchdog timeout=<t> pretimeout=<t> action=<action type>
preaction=<preaction type> preop=<preop type> start_now=x
+ nowayout=x
The timeout is the number of seconds to the action, and the pretimeout
is the amount of seconds before the reset that the pre-timeout panic will
If start_now is set to 1, the watchdog timer will start running as
soon as the driver is loaded.
+If nowayout is set to 1, the watchdog timer will not stop when the
+watchdog device is closed. The default value of nowayout is true
+if the CONFIG_WATCHDOG_NOWAYOUT option is enabled, or false if not.
+
When compiled into the kernel, the kernel command line is available
for configuring the watchdog:
ipmi_watchdog.preaction=<preaction type>
ipmi_watchdog.preop=<preop type>
ipmi_watchdog.start_now=x
+ ipmi_watchdog.nowayout=x
The options are the same as the module parameter options.
10/03/2003
Revised Feb 12, 2004 by Martine Silbermann
email: Martine.Silbermann@hp.com
+ Revised Jun 25, 2004 by Tom L Nguyen
1. About this guide
-This guide describes the basics of Message Signaled Interrupts(MSI), the
-advantages of using MSI over traditional interrupt mechanisms, and how
-to enable your driver to use MSI or MSI-X. Also included is a Frequently
-Asked Questions.
+This guide describes the basics of Message Signaled Interrupts (MSI),
+the advantages of using MSI over traditional interrupt mechanisms,
+and how to enable your driver to use MSI or MSI-X. Also included is
+a Frequently Asked Questions.
2. Copyright 2003 Intel Corporation
the MSI/MSI-X capability structure in its PCI capability list. The
device function may implement both the MSI capability structure and
the MSI-X capability structure; however, the bus driver should not
-enable both, but instead enable only the MSI-X capability structure.
+enable both.
The MSI capability structure contains Message Control register,
Message Address register and Message Data register. These registers
support for better interrupt performance.
Using MSI enables the device functions to support two or more
-vectors, which can be configure to target different CPU's to
+vectors, which can be configured to target different CPU's to
increase scalability.
5. Configuring a driver to use MSI/MSI-X
By default, the kernel will not enable MSI/MSI-X on all devices that
-support this capability. The CONFIG_PCI_USE_VECTOR kernel option
+support this capability. The CONFIG_PCI_MSI kernel option
must be selected to enable MSI/MSI-X support.
-5.1 Including MSI support into the kernel
+5.1 Including MSI/MSI-X support into the kernel
-To allow MSI-Capable device drivers to selectively enable MSI (using
-pci_enable_msi as described below), the VECTOR based scheme needs to
-be enabled by setting CONFIG_PCI_USE_VECTOR.
+To allow MSI/MSI-X capable device drivers to selectively enable
+MSI/MSI-X (using pci_enable_msi()/pci_enable_msix() as described
+below), the VECTOR based scheme needs to be enabled by setting
+CONFIG_PCI_MSI during kernel config.
Since the target of the inbound message is the local APIC, providing
-CONFIG_PCI_USE_VECTOR is dependent on whether CONFIG_X86_LOCAL_APIC
-is enabled or not.
+CONFIG_X86_LOCAL_APIC must be enabled as well as CONFIG_PCI_MSI.
-int pci_enable_msi(struct pci_dev *)
+5.2 Configuring for MSI support
+
+Due to the non-contiguous fashion in vector assignment of the
+existing Linux kernel, this version does not support multiple
+messages regardless of a device function is capable of supporting
+more than one vector. To enable MSI on a device function's MSI
+capability structure requires a device driver to call the function
+pci_enable_msi() explicitly.
+
+5.2.1 API pci_enable_msi
+
+int pci_enable_msi(struct pci_dev *dev)
With this new API, any existing device driver, which like to have
-MSI enabled on its device function, must call this explicitly. A
-successful call will initialize the MSI/MSI-X capability structure
-with ONE vector, regardless of whether the device function is
+MSI enabled on its device function, must call this API to enable MSI
+A successful call will initialize the MSI capability structure
+with ONE vector, regardless of whether a device function is
capable of supporting multiple messages. This vector replaces the
pre-assigned dev->irq with a new MSI vector. To avoid the conflict
of new assigned vector with existing pre-assigned vector requires
-the device driver to call this API before calling request_irq(...).
+a device driver to call this API before calling request_irq().
+
+5.2.2 API pci_disable_msi
+
+void pci_disable_msi(struct pci_dev *dev)
+
+This API should always be used to undo the effect of pci_enable_msi()
+when a device driver is unloading. This API restores dev->irq with
+the pre-assigned IOAPIC vector and switches a device's interrupt
+mode to PCI pin-irq assertion/INTx emulation mode.
+
+Note that a device driver should always call free_irq() on MSI vector
+it has done request_irq() on before calling this API. Failure to do
+so results a BUG_ON() and a device will be left with MSI enabled and
+leaks its vector.
+
+5.2.3 MSI mode vs. legacy mode diagram
The below diagram shows the events, which switches the interrupt
mode on the MSI-capable device function between MSI mode and
| | <=============== | |
| MSI MODE | | PIN-IRQ ASSERTION MODE |
| | ===============> | |
- ------------ free_irq ------------------------
+ ------------ pci_disable_msi ------------------------
-5.2 Configuring for MSI support
-Due to the non-contiguous fashion in vector assignment of the
-existing Linux kernel, this version does not support multiple
-messages regardless of the device function is capable of supporting
-more than one vector. The bus driver initializes only entry 0 of
-this capability if pci_enable_msi(...) is called successfully by
-the device driver.
+Figure 1.0 MSI Mode vs. Legacy Mode
+
+In Figure 1.0, a device operates by default in legacy mode. Legacy
+in this context means PCI pin-irq assertion or PCI-Express INTx
+emulation. A successful MSI request (using pci_enable_msi()) switches
+a device's interrupt mode to MSI mode. A pre-assigned IOAPIC vector
+stored in dev->irq will be saved by the PCI subsystem and a new
+assigned MSI vector will replace dev->irq.
+
+To return back to its default mode, a device driver should always call
+pci_disable_msi() to undo the effect of pci_enable_msi(). Note that a
+device driver should always call free_irq() on MSI vector it has done
+request_irq() on before calling pci_disable_msi(). Failure to do so
+results a BUG_ON() and a device will be left with MSI enabled and
+leaks its vector. Otherwise, the PCI subsystem restores a device's
+dev->irq with a pre-assigned IOAPIC vector and marks released
+MSI vector as unused.
+
+Once being marked as unused, there is no guarantee that the PCI
+subsystem will reserve this MSI vector for a device. Depending on
+the availability of current PCI vector resources and the number of
+MSI/MSI-X requests from other drivers, this MSI may be re-assigned.
+
+For the case where the PCI subsystem re-assigned this MSI vector
+another driver, a request to switching back to MSI mode may result
+in being assigned a different MSI vector or a failure if no more
+vectors are available.
5.3 Configuring for MSI-X support
-Both the MSI capability structure and the MSI-X capability structure
-share the same above semantics; however, due to the ability of the
-system software to configure each vector of the MSI-X capability
-structure with an independent message address and message data, the
-non-contiguous fashion in vector assignment of the existing Linux
-kernel has no impact on supporting multiple messages on an MSI-X
-capable device functions. By default, as mentioned above, ONE vector
-should be always allocated to the MSI-X capability structure at
-entry 0. The bus driver does not initialize other entries of the
-MSI-X table.
-
-Note that the PCI subsystem should have full control of a MSI-X
-table that resides in Memory Space. The software device driver
-should not access this table.
-
-To request for additional vectors, the device software driver should
-call function msi_alloc_vectors(). It is recommended that the
-software driver should call this function once during the
+Due to the ability of the system software to configure each vector of
+the MSI-X capability structure with an independent message address
+and message data, the non-contiguous fashion in vector assignment of
+the existing Linux kernel has no impact on supporting multiple
+messages on an MSI-X capable device functions. To enable MSI-X on
+a device function's MSI-X capability structure requires its device
+driver to call the function pci_enable_msix() explicitly.
+
+The function pci_enable_msix(), once invoked, enables either
+all or nothing, depending on the current availability of PCI vector
+resources. If the PCI vector resources are available for the number
+of vectors requested by a device driver, this function will configure
+the MSI-X table of the MSI-X capability structure of a device with
+requested messages. To emphasize this reason, for example, a device
+may be capable for supporting the maximum of 32 vectors while its
+software driver usually may request 4 vectors. It is recommended
+that the device driver should call this function once during the
initialization phase of the device driver.
-The function msi_alloc_vectors(), once invoked, enables either
-all or nothing, depending on the current availability of vector
-resources. If no vector resources are available, the device function
-still works with ONE vector. If the vector resources are available
-for the number of vectors requested by the driver, this function
-will reconfigure the MSI-X capability structure of the device with
-additional messages, starting from entry 1. To emphasize this
-reason, for example, the device may be capable for supporting the
-maximum of 32 vectors while its software driver usually may request
-4 vectors.
-
-For each vector, after this successful call, the device driver is
-responsible to call other functions like request_irq(), enable_irq(),
-etc. to enable this vector with its corresponding interrupt service
-handler. It is the device driver's choice to have all vectors shared
-the same interrupt service handler or each vector with a unique
-interrupt service handler.
-
-In addition to the function msi_alloc_vectors(), another function
-msi_free_vectors() is provided to allow the software driver to
-release a number of vectors back to the vector resources. Once
-invoked, the PCI subsystem disables (masks) each vector released.
-These vectors are no longer valid for the hardware device and its
-software driver to use. Like free_irq, it recommends that the
-device driver should also call msi_free_vectors to release all
-additional vectors previously requested.
-
-int msi_alloc_vectors(struct pci_dev *dev, int *vector, int nvec)
-
-This API enables the software driver to request the PCI subsystem
-for additional messages. Depending on the number of vectors
-available, the PCI subsystem enables either all or nothing.
+Unlike the function pci_enable_msi(), the function pci_enable_msix()
+does not replace the pre-assigned IOAPIC dev->irq with a new MSI
+vector because the PCI subsystem writes the 1:1 vector-to-entry mapping
+into the field vector of each element contained in a second argument.
+Note that the pre-assigned IO-APIC dev->irq is valid only if the device
+operates in PIN-IRQ assertion mode. In MSI-X mode, any attempt of
+using dev->irq by the device driver to request for interrupt service
+may result unpredictabe behavior.
+
+For each MSI-X vector granted, a device driver is responsible to call
+other functions like request_irq(), enable_irq(), etc. to enable
+this vector with its corresponding interrupt service handler. It is
+a device driver's choice to assign all vectors with the same
+interrupt service handler or each vector with a unique interrupt
+service handler.
+
+5.3.1 Handling MMIO address space of MSI-X Table
+
+The PCI 3.0 specification has implementation notes that MMIO address
+space for a device's MSI-X structure should be isolated so that the
+software system can set different page for controlling accesses to
+the MSI-X structure. The implementation of MSI patch requires the PCI
+subsystem, not a device driver, to maintain full control of the MSI-X
+table/MSI-X PBA and MMIO address space of the MSI-X table/MSI-X PBA.
+A device driver is prohibited from requesting the MMIO address space
+of the MSI-X table/MSI-X PBA. Otherwise, the PCI subsystem will fail
+enabling MSI-X on its hardware device when it calls the function
+pci_enable_msix().
+
+5.3.2 Handling MSI-X allocation
+
+Determining the number of MSI-X vectors allocated to a function is
+dependent on the number of MSI capable devices and MSI-X capable
+devices populated in the system. The policy of allocating MSI-X
+vectors to a function is defined as the following:
+
+#of MSI-X vectors allocated to a function = (x - y)/z where
+
+x = The number of available PCI vector resources by the time
+ the device driver calls pci_enable_msix(). The PCI vector
+ resources is the sum of the number of unassigned vectors
+ (new) and the number of released vectors when any MSI/MSI-X
+ device driver switches its hardware device back to a legacy
+ mode or is hot-removed. The number of unassigned vectors
+ may exclude some vectors reserved, as defined in parameter
+ NR_HP_RESERVED_VECTORS, for the case where the system is
+ capable of supporting hot-add/hot-remove operations. Users
+ may change the value defined in NR_HR_RESERVED_VECTORS to
+ meet their specific needs.
+
+y = The number of MSI capable devices populated in the system.
+ This policy ensures that each MSI capable device has its
+ vector reserved to avoid the case where some MSI-X capable
+ drivers may attempt to claim all available vector resources.
+
+z = The number of MSI-X capable devices pupulated in the system.
+ This policy ensures that maximum (x - y) is distributed
+ evenly among MSI-X capable devices.
+
+Note that the PCI subsystem scans y and z during a bus enumeration.
+When the PCI subsystem completes configuring MSI/MSI-X capability
+structure of a device as requested by its device driver, y/z is
+decremented accordingly.
+
+5.3.3 Handling MSI-X shortages
+
+For the case where fewer MSI-X vectors are allocated to a function
+than requested, the function pci_enable_msix() will return the
+maximum number of MSI-X vectors available to the caller. A device
+driver may re-send its request with fewer or equal vectors indicated
+in a return. For example, if a device driver requests 5 vectors, but
+the number of available vectors is 3 vectors, a value of 3 will be a
+return as a result of pci_enable_msix() call. A function could be
+designed for its driver to use only 3 MSI-X table entries as
+different combinations as ABC--, A-B-C, A--CB, etc. Note that this
+patch does not support multiple entries with the same vector. Such
+attempt by a device driver to use 5 MSI-X table entries with 3 vectors
+as ABBCC, AABCC, BCCBA, etc will result as a failure by the function
+pci_enable_msix(). Below are the reasons why supporting multiple
+entries with the same vector is an undesirable solution.
+
+ - The PCI subsystem can not determine which entry, which
+ generated the message, to mask/unmask MSI while handling
+ software driver ISR. Attempting to walk through all MSI-X
+ table entries (2048 max) to mask/unmask any match vector
+ is an undesirable solution.
+
+ - Walk through all MSI-X table entries (2048 max) to handle
+ SMP affinity of any match vector is an undesirable solution.
+
+5.3.4 API pci_enable_msix
+
+int pci_enable_msix(struct pci_dev *dev, u32 *entries, int nvec)
+
+This API enables a device driver to request the PCI subsystem
+for enabling MSI-X messages on its hardware device. Depending on
+the availability of PCI vectors resources, the PCI subsystem enables
+either all or nothing.
Argument dev points to the device (pci_dev) structure.
-Argument vector is a pointer of integer type. The number of
-elements is indicated in argument nvec.
+
+Argument entries is a pointer of unsigned integer type. The number of
+elements is indicated in argument nvec. The content of each element
+will be mapped to the following struct defined in /driver/pci/msi.h.
+
+struct msix_entry {
+ u16 vector; /* kernel uses to write alloc vector */
+ u16 entry; /* driver uses to specify entry */
+};
+
+A device driver is responsible for initializing the field entry of
+each element with unique entry supported by MSI-X table. Otherwise,
+-EINVAL will be returned as a result. A successful return of zero
+indicates the PCI subsystem completes initializing each of requested
+entries of the MSI-X table with message address and message data.
+Last but not least, the PCI subsystem will write the 1:1
+vector-to-entry mapping into the field vector of each element. A
+device driver is responsible of keeping track of allocated MSI-X
+vectors in its internal data structure.
+
Argument nvec is an integer indicating the number of messages
requested.
-A return of zero indicates that the number of allocated vector is
-successfully allocated. Otherwise, indicate resources not
-available.
-int msi_free_vectors(struct pci_dev* dev, int *vector, int nvec)
+A return of zero indicates that the number of MSI-X vectors is
+successfully allocated. A return of greater than zero indicates
+MSI-X vector shortage. Or a return of less than zero indicates
+a failure. This failure may be a result of duplicate entries
+specified in second argument, or a result of no available vector,
+or a result of failing to initialize MSI-X table entries.
-This API enables the software driver to inform the PCI subsystem
-that it is willing to release a number of vectors back to the
-MSI resource pool. Once invoked, the PCI subsystem disables each
-MSI-X entry associated with each vector stored in the argument 2.
-These vectors are no longer valid for the hardware device and
-its software driver to use.
+5.3.5 API pci_disable_msix
-Argument dev points to the device (pci_dev) structure.
-Argument vector is a pointer of integer type. The number of
-elements is indicated in argument nvec.
-Argument nvec is an integer indicating the number of messages
-released.
-A return of zero indicates that the number of allocated vectors
-is successfully released. Otherwise, indicates a failure.
+void pci_disable_msix(struct pci_dev *dev)
-5.4 Hardware requirements for MSI support
-MSI support requires support from both system hardware and
+This API should always be used to undo the effect of pci_enable_msix()
+when a device driver is unloading. Note that a device driver should
+always call free_irq() on all MSI-X vectors it has done request_irq()
+on before calling this API. Failure to do so results a BUG_ON() and
+a device will be left with MSI-X enabled and leaks its vectors.
+
+5.3.6 MSI-X mode vs. legacy mode diagram
+
+The below diagram shows the events, which switches the interrupt
+mode on the MSI-X capable device function between MSI-X mode and
+PIN-IRQ assertion mode (legacy).
+
+ ------------ pci_enable_msix(,,n) ------------------------
+ | | <=============== | |
+ | MSI-X MODE | | PIN-IRQ ASSERTION MODE |
+ | | ===============> | |
+ ------------ pci_disable_msix ------------------------
+
+Figure 2.0 MSI-X Mode vs. Legacy Mode
+
+In Figure 2.0, a device operates by default in legacy mode. A
+successful MSI-X request (using pci_enable_msix()) switches a
+device's interrupt mode to MSI-X mode. A pre-assigned IOAPIC vector
+stored in dev->irq will be saved by the PCI subsystem; however,
+unlike MSI mode, the PCI subsystem will not replace dev->irq with
+assigned MSI-X vector because the PCI subsystem already writes the 1:1
+vector-to-entry mapping into the field vector of each element
+specified in second argument.
+
+To return back to its default mode, a device driver should always call
+pci_disable_msix() to undo the effect of pci_enable_msix(). Note that
+a device driver should always call free_irq() on all MSI-X vectors it
+has done request_irq() on before calling pci_disable_msix(). Failure
+to do so results a BUG_ON() and a device will be left with MSI-X
+enabled and leaks its vectors. Otherwise, the PCI subsystem switches a
+device function's interrupt mode from MSI-X mode to legacy mode and
+marks all allocated MSI-X vectors as unused.
+
+Once being marked as unused, there is no guarantee that the PCI
+subsystem will reserve these MSI-X vectors for a device. Depending on
+the availability of current PCI vector resources and the number of
+MSI/MSI-X requests from other drivers, these MSI-X vectors may be
+re-assigned.
+
+For the case where the PCI subsystem re-assigned these MSI-X vectors
+to other driver, a request to switching back to MSI-X mode may result
+being assigned with another set of MSI-X vectors or a failure if no
+more vectors are available.
+
+5.4 Handling function implementng both MSI and MSI-X capabilities
+
+For the case where a function implements both MSI and MSI-X
+capabilities, the PCI subsystem enables a device to run either in MSI
+mode or MSI-X mode but not both. A device driver determines whether it
+wants MSI or MSI-X enabled on its hardware device. Once a device
+driver requests for MSI, for example, it is prohibited to request for
+MSI-X; in other words, a device driver is not permitted to ping-pong
+between MSI mod MSI-X mode during a run-time.
+
+5.5 Hardware requirements for MSI/MSI-X support
+MSI/MSI-X support requires support from both system hardware and
individual hardware device functions.
-5.4.1 System hardware support
+5.5.1 System hardware support
Since the target of MSI address is the local APIC CPU, enabling
-MSI support in Linux kernel is dependent on whether existing
+MSI/MSI-X support in Linux kernel is dependent on whether existing
system hardware supports local APIC. Users should verify their
system whether it runs when CONFIG_X86_LOCAL_APIC=y.
In SMP environment, CONFIG_X86_LOCAL_APIC is automatically set;
however, in UP environment, users must manually set
CONFIG_X86_LOCAL_APIC. Once CONFIG_X86_LOCAL_APIC=y, setting
-CONFIG_PCI_USE_VECTOR enables the VECTOR based scheme and
+CONFIG_PCI_MSI enables the VECTOR based scheme and
the option for MSI-capable device drivers to selectively enable
-MSI (using pci_enable_msi as described below).
+MSI/MSI-X.
-Note that CONFIG_X86_IO_APIC setting is irrelevant because MSI
-vector is allocated new during runtime and MSI support does not
-depend on BIOS support. This key independency enables MSI support
-on future IOxAPIC free platform.
+Note that CONFIG_X86_IO_APIC setting is irrelevant because MSI/MSI-X
+vector is allocated new during runtime and MSI/MSI-X support does not
+depend on BIOS support. This key independency enables MSI/MSI-X
+support on future IOxAPIC free platform.
-5.4.2 Device hardware support
+5.5.2 Device hardware support
The hardware device function supports MSI by indicating the
MSI/MSI-X capability structure on its PCI capability list. By
default, this capability structure will not be initialized by
MSI-capable hardware is responsible for whether calling
pci_enable_msi or not. A return of zero indicates the kernel
successfully initializes the MSI/MSI-X capability structure of the
-device funtion. The device function is now running on MSI mode.
+device funtion. The device function is now running on MSI/MSI-X mode.
-5.5 How to tell whether MSI is enabled on device function
+5.6 How to tell whether MSI/MSI-X is enabled on device function
-At the driver level, a return of zero from pci_enable_msi(...)
-indicates to the device driver that its device function is
-initialized successfully and ready to run in MSI mode.
+At the driver level, a return of zero from the function call of
+pci_enable_msi()/pci_enable_msix() indicates to a device driver that
+its device function is initialized successfully and ready to run in
+MSI/MSI-X mode.
At the user level, users can use command 'cat /proc/interrupts'
-to display the vector allocated for the device and its interrupt
-mode, as shown below.
+to display the vector allocated for a device and its interrupt
+MSI/MSI-X mode ("PCI MSI"/"PCI MSIX"). Below shows below MSI mode is
+enabled on a SCSI Adaptec 39320D Ultra320.
CPU0 CPU1
0: 324639 0 IO-APIC-edge timer
option to the kernel via the tagged lists specifying the port, and
serial format options as described in
- linux/Documentation/kernel-parameters.txt.
+ Documentation/kernel-parameters.txt.
3. Detect the machine type
--- /dev/null
+Anticipatory IO scheduler
+-------------------------
+Nick Piggin <piggin@cyberone.com.au> 13 Sep 2003
+
+Attention! Database servers, especially those using "TCQ" disks should
+investigate performance with the 'deadline' IO scheduler. Any system with high
+disk performance requirements should do so, in fact.
+
+If you see unusual performance characteristics of your disk systems, or you
+see big performance regressions versus the deadline scheduler, please email
+me. Database users don't bother unless you're willing to test a lot of patches
+from me ;) its a known issue.
+
+Also, users with hardware RAID controllers, doing striping, may find
+highly variable performance results with using the as-iosched. The
+as-iosched anticipatory implementation is based on the notion that a disk
+device has only one physical seeking head. A striped RAID controller
+actually has a head for each physical device in the logical RAID device.
+
+However, setting the antic_expire (see tunable parameters below) produces
+very similar behavior to the deadline IO scheduler.
+
+
+Selecting IO schedulers
+-----------------------
+To choose IO schedulers at boot time, use the argument 'elevator=deadline'.
+'noop' and 'as' (the default) are also available. IO schedulers are assigned
+globally at boot time only presently.
+
+
+Anticipatory IO scheduler Policies
+----------------------------------
+The as-iosched implementation implements several layers of policies
+to determine when an IO request is dispatched to the disk controller.
+Here are the policies outlined, in order of application.
+
+1. one-way Elevator algorithm.
+
+The elevator algorithm is similar to that used in deadline scheduler, with
+the addition that it allows limited backward movement of the elevator
+(i.e. seeks backwards). A seek backwards can occur when choosing between
+two IO requests where one is behind the elevator's current position, and
+the other is in front of the elevator's position. If the seek distance to
+the request in back of the elevator is less than half the seek distance to
+the request in front of the elevator, then the request in back can be chosen.
+Backward seeks are also limited to a maximum of MAXBACK (1024*1024) sectors.
+This favors forward movement of the elevator, while allowing opportunistic
+"short" backward seeks.
+
+2. FIFO expiration times for reads and for writes.
+
+This is again very similar to the deadline IO scheduler. The expiration
+times for requests on these lists is tunable using the parameters read_expire
+and write_expire discussed below. When a read or a write expires in this way,
+the IO scheduler will interrupt its current elevator sweep or read anticipation
+to service the expired request.
+
+3. Read and write request batching
+
+A batch is a collection of read requests or a collection of write
+requests. The as scheduler alternates dispatching read and write batches
+to the driver. In the case a read batch, the scheduler submits read
+requests to the driver as long as there are read requests to submit, and
+the read batch time limit has not been exceeded (read_batch_expire).
+The read batch time limit begins counting down only when there are
+competing write requests pending.
+
+In the case of a write batch, the scheduler submits write requests to
+the driver as long as there are write requests available, and the
+write batch time limit has not been exceeded (write_batch_expire).
+However, the length of write batches will be gradually shortened
+when read batches frequently exceed their time limit.
+
+When changing between batch types, the scheduler waits for all requests
+from the previous batch to complete before scheduling requests for the
+next batch.
+
+The read and write fifo expiration times described in policy 2 above
+are checked only when in scheduling IO of a batch for the corresponding
+(read/write) type. So for example, the read FIFO timeout values are
+tested only during read batches. Likewise, the write FIFO timeout
+values are tested only during write batches. For this reason,
+it is generally not recommended for the read batch time
+to be longer than the write expiration time, nor for the write batch
+time to exceed the read expiration time (see tunable parameters below).
+
+When the IO scheduler changes from a read to a write batch,
+it begins the elevator from the request that is on the head of the
+write expiration FIFO. Likewise, when changing from a write batch to
+a read batch, scheduler begins the elevator from the first entry
+on the read expiration FIFO.
+
+4. Read anticipation.
+
+Read anticipation occurs only when scheduling a read batch.
+This implementation of read anticipation allows only one read request
+to be dispatched to the disk controller at a time. In
+contrast, many write requests may be dispatched to the disk controller
+at a time during a write batch. It is this characteristic that can make
+the anticipatory scheduler perform anomalously with controllers supporting
+TCQ, or with hardware striped RAID devices. Setting the antic_expire
+queue paramter (see below) to zero disables this behavior, and the anticipatory
+scheduler behaves essentially like the deadline scheduler.
+
+When read anticipation is enabled (antic_expire is not zero), reads
+are dispatched to the disk controller one at a time.
+At the end of each read request, the IO scheduler examines its next
+candidate read request from its sorted read list. If that next request
+is from the same process as the request that just completed,
+or if the next request in the queue is "very close" to the
+just completed request, it is dispatched immediately. Otherwise,
+statistics (average think time, average seek distance) on the process
+that submitted the just completed request are examined. If it seems
+likely that that process will submit another request soon, and that
+request is likely to be near the just completed request, then the IO
+scheduler will stop dispatching more read requests for up time (antic_expire)
+milliseconds, hoping that process will submit a new request near the one
+that just completed. If such a request is made, then it is dispatched
+immediately. If the antic_expire wait time expires, then the IO scheduler
+will dispatch the next read request from the sorted read queue.
+
+To decide whether an anticipatory wait is worthwhile, the scheduler
+maintains statistics for each process that can be used to compute
+mean "think time" (the time between read requests), and mean seek
+distance for that process. One observation is that these statistics
+are associated with each process, but those statistics are not associated
+with a specific IO device. So for example, if a process is doing IO
+on several file systems on separate devices, the statistics will be
+a combination of IO behavior from all those devices.
+
+
+Tuning the anticipatory IO scheduler
+------------------------------------
+When using 'as', the anticipatory IO scheduler there are 5 parameters under
+/sys/block/*/iosched/. All are units of milliseconds.
+
+The parameters are:
+* read_expire
+ Controls how long until a read request becomes "expired". It also controls the
+ interval between which expired requests are served, so set to 50, a request
+ might take anywhere < 100ms to be serviced _if_ it is the next on the
+ expired list. Obviously request expiration strategies won't make the disk
+ go faster. The result basically equates to the timeslice a single reader
+ gets in the presence of other IO. 100*((seek time / read_expire) + 1) is
+ very roughly the % streaming read efficiency your disk should get with
+ multiple readers.
+
+* read_batch_expire
+ Controls how much time a batch of reads is given before pending writes are
+ served. A higher value is more efficient. This might be set below read_expire
+ if writes are to be given higher priority than reads, but reads are to be
+ as efficient as possible when there are no writes. Generally though, it
+ should be some multiple of read_expire.
+
+* write_expire, and
+* write_batch_expire are equivalent to the above, for writes.
+
+* antic_expire
+ Controls the maximum amount of time we can anticipate a good read (one
+ with a short seek distance from the most recently completed request) before
+ giving up. Many other factors may cause anticipation to be stopped early,
+ or some processes will not be "anticipated" at all. Should be a bit higher
+ for big seek time devices though not a linear correspondence - most
+ processes have only a few ms thinktime.
+
--- /dev/null
+Deadline IO scheduler tunables
+==============================
+
+This little file attempts to document how the deadline io scheduler works.
+In particular, it will clarify the meaning of the exposed tunables that may be
+of interest to power users.
+
+Each io queue has a set of io scheduler tunables associated with it. These
+tunables control how the io scheduler works. You can find these entries
+in:
+
+/sys/block/<device>/iosched
+
+assuming that you have sysfs mounted on /sys. If you don't have sysfs mounted,
+you can do so by typing:
+
+# mount none /sys -t sysfs
+
+
+********************************************************************************
+
+
+read_expire (in ms)
+-----------
+
+The goal of the deadline io scheduler is to attempt to guarentee a start
+service time for a request. As we focus mainly on read latencies, this is
+tunable. When a read request first enters the io scheduler, it is assigned
+a deadline that is the current time + the read_expire value in units of
+miliseconds.
+
+
+write_expire (in ms)
+-----------
+
+Similar to read_expire mentioned above, but for writes.
+
+
+fifo_batch
+----------
+
+When a read request expires its deadline, we must move some requests from
+the sorted io scheduler list to the block device dispatch queue. fifo_batch
+controls how many requests we move, based on the cost of each request. A
+request is either qualified as a seek or a stream. The io scheduler knows
+the last request that was serviced by the drive (or will be serviced right
+before this one). See seek_cost and stream_unit.
+
+
+write_starved (number of dispatches)
+-------------
+
+When we have to move requests from the io scheduler queue to the block
+device dispatch queue, we always give a preference to reads. However, we
+don't want to starve writes indefinitely either. So writes_starved controls
+how many times we give preference to reads over writes. When that has been
+done writes_starved number of times, we dispatch some writes based on the
+same criteria as reads.
+
+
+front_merges (bool)
+------------
+
+Sometimes it happens that a request enters the io scheduler that is contigious
+with a request that is already on the queue. Either it fits in the back of that
+request, or it fits at the front. That is called either a back merge candidate
+or a front merge candidate. Due to the way files are typically laid out,
+back merges are much more common than front merges. For some work loads, you
+may even know that it is a waste of time to spend any time attempting to
+front merge requests. Setting front_merges to 0 disables this functionality.
+Front merges may still occur due to the cached last_merge hint, but since
+that comes at basically 0 cost we leave that on. We simply disable the
+rbtree front sector lookup when the io scheduler merge function is called.
+
+
+Nov 11 2002, Jens Axboe <axboe@suse.de>
+
+
$Id: README.aztcd,v 2.60 1997/11/29 09:51:25 root Exp root $
- Readme-File /usr/src/Documentation/cdrom/aztcd
+ Readme-File Documentation/cdrom/aztcd
for
AZTECH CD-ROM CDA268-01A, ORCHID CD-3110,
OKANO/WEARNES CDD110, CONRAD TXC, CyCDROM CR520, CR540
A reworked and improved version called 'cdtester.c', which has yet more
features for testing CDROM-drives can be found in
-/usr/src/linux/Documentation/cdrom/sbpcd, written by E.Moenkeberg.
+Documentation/cdrom/sbpcd, written by E.Moenkeberg.
Werner Zimmermann
Fachhochschule fuer Technik Esslingen
To create the ip2mkdev shell script change to a convenient directory (/tmp
works just fine) and run the following command:
- unshar /usr/src/linux/Documentation/computone.txt
+ unshar Documentation/computone.txt
(This file)
You should now have a file ip2mkdev in your current working directory with
Herbert Valerio Riedel
Kyle McMartin
Adam J. Richter
+ Fruhwirth Clemens (i586)
+ Linus Torvalds (i586)
CAST5 algorithm contributors:
Kartikey Mahendra Bhatt (original developers unknown, FSF copyright).
+TEA/XTEA algorithm contributors:
+ Aaron Grothe
+
+Khazad algorithm contributors:
+ Aaron Grothe
+
Generic scatterwalk code by Adam J. Richter <adam@yggdrasil.com>
Please send any credits updates or corrections to:
Supporting Tools:
-----------------
Supporting tools include digiDload, digiConfig, buildPCI, and ditty. See
-/usr/src/linux/Documentation/README.epca.dir/user.doc for more details. Note,
+drivers/char/README.epca for more details. Note,
this driver REQUIRES that digiDload be executed prior to it being used.
Failure to do this will result in an ENODEV error.
binary-only firmware.
The DVB drivers will be converted to use the request_firmware()
-hotplug interface (see linux/Documentation/firmware_class/).
+hotplug interface (see Documentation/firmware_class/).
(CONFIG_FW_LOADER)
The firmware can be loaded automatically via the hotplug manager
Hotplug Firmware Loading for 2.6 kernels
----------------------------------------
For 2.6 kernels the firmware is loaded at the point that the driver module is
-loaded. See linux/Documentation/dvb/firmware.txt for more information.
+loaded. See Documentation/dvb/firmware.txt for more information.
mv STB_PC_T.bin /usr/lib/hotplug/firmware/dvb-ttusb-dec-2000t.fw
mv STB_PC_X.bin /usr/lib/hotplug/firmware/dvb-ttusb-dec-2540t.fw
--- /dev/null
+
+What is sisfb?
+==============
+
+sisfb is a framebuffer device driver for SiS (Silicon Integrated Systems)
+graphics chips. Supported are:
+
+- SiS 300 series: SiS 300/305, 540, 630(S), 730(S)
+- SiS 315 series: SiS 315/H/PRO, 55x, (M)65x, 740, (M)661(F/M)X, (M)741(GX)
+- SiS 330 series: SiS 330 ("Xabre"), (M)760
+
+
+Why do I need a framebuffer driver?
+===================================
+
+sisfb is eg. useful if you want a high-resolution text console. Besides that,
+sisfb is required to run DirectFB (which comes with an additional, dedicated
+driver for the 315 series).
+
+On the 300 series, sisfb on kernels older than 2.6.3 furthermore plays an
+important role in connection with DRM/DRI: Sisfb manages the memory heap
+used by DRM/DRI for 3D texture and other data. This memory management is
+required for using DRI/DRM.
+
+Kernels >= around 2.6.3 do not need sisfb any longer for DRI/DRM memory
+management. The SiS DRM driver has been updated and features a memory manager
+of its own (which will be used if sisfb is not compiled). So unless you want
+a graphical console, you don't need sisfb on kernels >=2.6.3.
+
+Sidenote: Since this seems to be a commonly made mistake: sisfb and vesafb
+cannot be active at the same time! Do only select one of them in your kernel
+configuration.
+
+
+How are parameters passed to sisfb?
+===================================
+
+Well, it depends: If compiled statically into the kernel, use lilo's append
+statement to add the parameters to the kernel command line. Please see lilo's
+(or GRUB's) documentation for more information. If sisfb is a kernel module,
+parameters are given with the modprobe (or insmod) command.
+
+Example for sisfb as part of the static kernel: Add the following line to your
+lilo.conf:
+
+ append="video=sisfb:mode:1024x768x16,mem:12288,rate:75"
+
+Example for sisfb as a module: Start sisfb by typing
+
+ modprobe sisfb mode=1024x768x16 rate=75 mem=12288
+
+A common mistake is that folks use a wrong parameter format when using the
+driver compiled into the kernel. Please note: If compiled into the kernel,
+the parameter format is video=sisfb:mode:none or video=sisfb:mode:1024x768x16
+(or whatever mode you want to use, alternatively using any other format
+described above or the vesa keyword instead of mode). If compiled as a module,
+the parameter format reads mode=none or mode=1024x768x16 (or whatever mode you
+want to use). Using a "=" for a ":" (and vice versa) is a huge difference!
+Additionally: If you give more than one argument to the in-kernel sisfb, the
+arguments are separated with ",". For example:
+
+ video=sisfb:mode:1024x768x16,rate:75,mem:12288
+
+
+How do I use it?
+================
+
+Preface statement: This file only covers very little of the driver's
+capabilities and features. Please refer to the author's and maintainer's
+website at http://www.winischhofer.net/linuxsisvga.shtml for more
+information. Additionally, "modinfo sisfb" gives an overview over all
+supported options including some explanation.
+
+The desired display mode can be specified using the keyword "mode" with
+a parameter in one of the follwing formats:
+ - XxYxDepth or
+ - XxY-Depth or
+ - XxY-Depth@Rate or
+ - XxY
+ - or simply use the VESA mode number in hexadecimal or decimal.
+
+For example: 1024x768x16, 1024x768-16@75, 1280x1024-16. If no depth is
+specified, it defaults to 8. If no rate is given, it defaults to 60Hz. Depth 32
+means 24bit color depth (but 32 bit framebuffer depth, which is not relevant
+to the user).
+
+Additionally, sisfb understands the keyword "vesa" followed by a VESA mode
+number in decimal or hexadecimal. For example: vesa=791 or vesa=0x117. Please
+use either "mode" or "vesa" but not both.
+
+Linux 2.4 only: If no mode is given, sisfb defaults to "no mode" (mode=none) if
+compiled as a module; if sisfb is statically compiled into the kernel, it
+defaults to 800x600x8 unless CRT2 type is LCD, in which case the LCD's native
+resolution is used. If you want to switch to a different mode, use the fbset
+shell command.
+
+Linux 2.6 only: If no mode is given, sisfb defaults to 800x600x8 unless CRT2
+type is LCD, in which case it defaults to the LCD's native resolution. If
+you want to switch to another mode, use the stty shell command.
+
+You should compile in both vgacon (to boot if you remove you SiS card from
+your system) and sisfb (for graphics mode). Under Linux 2.6, also "Framebuffer
+console support" (fbcon) is needed for a graphical console.
+
+You should *not* compile-in vesafb. And please do not use the "vga=" keyword
+in lilo's or grub's configuration file; mode selection is done using the
+"mode" or "vesa" keywords as a parameter. See above and below.
+
+
+X11
+===
+
+If using XFree86 or X.org, it is recommended that you don't use the "fbdev"
+driver but the dedicated "sis" X driver. The "sis" X driver and sisfb are
+developed by the same person (Thomas Winischhofer) and cooperate well with
+each other.
+
+
+SVGALib
+=======
+
+SVGALib, if directly accessing the hardware, never restores the screen
+correctly, especially on laptops or if the output devices are LCD or TV.
+Therefore, use the chipset "FBDEV" in SVGALib configuration. This will make
+SVGALib use the framebuffer device for mode switches and restoration.
+
+
+Configuration
+=============
+
+(Some) accepted options:
+
+off - Disable sisfb. This option is only understood if sisfb is
+ in-kernel, not a module.
+mem:X - size of memory for the console, rest will be used for DRI/DRM. X
+ is in kilobytes. On 300 series, the default is 4096, 8192 or
+ 16384 (each in kilobyte) depending on how much video ram the card
+ has. On 315/330 series, the default is the maximum available ram
+ (since DRI/DRM is not supported for these chipsets).
+noaccel - do not use 2D acceleration engine. (Default: use acceleration)
+noypan - disable y-panning and scroll by redrawing the entire screen.
+ This is much slower than y-panning. (Default: use y-panning)
+vesa:X - selects startup videomode. X is number from 0 to 0x1FF and
+ represents the VESA mode number (can be given in decimal or
+ hexadecimal form, the latter prefixed with "0x").
+mode:X - selects startup videomode. Please see above for the format of
+ "X".
+
+Boolean options such as "noaccel" or "noypan" are to be given without a
+parameter if sisfb is in-kernel (for example "video=sisfb:noypan). If
+sisfb is a module, these are to be set to 1 (for example "modprobe sisfb
+noypan=1").
+
+--
+Thomas Winischhofer <thomas@winischhofer.net>
+May 27, 2004
+
+
loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long,
unsigned long, unsigned long, unsigned long);
+ int (*check_flags)(int);
+ int (*dir_notify)(struct file *, unsigned long);
};
locking rules:
sendfile: no
sendpage: no
get_unmapped_area: no
+check_flags: no
+dir_notify: no
->llseek() locking has moved from llseek to the individual llseek
implementations. If your fs is not using generic_file_llseek, you
--- /dev/null
+Support is available for filesystems that wish to do automounting support (such
+as kAFS which can be found in fs/afs/). This facility includes allowing
+in-kernel mounts to be performed and mountpoint degradation to be
+requested. The latter can also be requested by userspace.
+
+
+======================
+IN-KERNEL AUTOMOUNTING
+======================
+
+A filesystem can now mount another filesystem on one of its directories by the
+following procedure:
+
+ (1) Give the directory a follow_link() operation.
+
+ When the directory is accessed, the follow_link op will be called, and
+ it will be provided with the location of the mountpoint in the nameidata
+ structure (vfsmount and dentry).
+
+ (2) Have the follow_link() op do the following steps:
+
+ (a) Call do_kern_mount() to call the appropriate filesystem to set up a
+ superblock and gain a vfsmount structure representing it.
+
+ (b) Copy the nameidata provided as an argument and substitute the dentry
+ argument into it the copy.
+
+ (c) Call do_add_mount() to install the new vfsmount into the namespace's
+ mountpoint tree, thus making it accessible to userspace. Use the
+ nameidata set up in (b) as the destination.
+
+ If the mountpoint will be automatically expired, then do_add_mount()
+ should also be given the location of an expiration list (see further
+ down).
+
+ (d) Release the path in the nameidata argument and substitute in the new
+ vfsmount and its root dentry. The ref counts on these will need
+ incrementing.
+
+Then from userspace, you can just do something like:
+
+ [root@andromeda root]# mount -t afs \#root.afs. /afs
+ [root@andromeda root]# ls /afs
+ asd cambridge cambridge.redhat.com grand.central.org
+ [root@andromeda root]# ls /afs/cambridge
+ afsdoc
+ [root@andromeda root]# ls /afs/cambridge/afsdoc/
+ ChangeLog html LICENSE pdf RELNOTES-1.2.2
+
+And then if you look in the mountpoint catalogue, you'll see something like:
+
+ [root@andromeda root]# cat /proc/mounts
+ ...
+ #root.afs. /afs afs rw 0 0
+ #root.cell. /afs/cambridge.redhat.com afs rw 0 0
+ #afsdoc. /afs/cambridge.redhat.com/afsdoc afs rw 0 0
+
+
+===========================
+AUTOMATIC MOUNTPOINT EXPIRY
+===========================
+
+Automatic expiration of mountpoints is easy, provided you've mounted the
+mountpoint to be expired in the automounting procedure outlined above.
+
+To do expiration, you need to follow these steps:
+
+ (3) Create at least one list off which the vfsmounts to be expired can be
+ hung. Access to this list will be governed by the vfsmount_lock.
+
+ (4) In step (2c) above, the call to do_add_mount() should be provided with a
+ pointer to this list. It will hang the vfsmount off of it if it succeeds.
+
+ (5) When you want mountpoints to be expired, call mark_mounts_for_expiry()
+ with a pointer to this list. This will process the list, marking every
+ vfsmount thereon for potential expiry on the next call.
+
+ If a vfsmount was already flagged for expiry, and if its usage count is 1
+ (it's only referenced by its parent vfsmount), then it will be deleted
+ from the namespace and thrown away (effectively unmounted).
+
+ It may prove simplest to simply call this at regular intervals, using
+ some sort of timed event to drive it.
+
+The expiration flag is cleared by calls to mntput. This means that expiration
+will only happen on the second expiration request after the last time the
+mountpoint was accessed.
+
+If a mountpoint is moved, it gets removed from the expiration list. If a bind
+mount is made on an expirable mount, the new vfsmount will not be on the
+expiration list and will not expire.
+
+If a namespace is copied, all mountpoints contained therein will be copied,
+and the copies of those that are on an expiration list will be added to the
+same expiration list.
+
+
+=======================
+USERSPACE DRIVEN EXPIRY
+=======================
+
+As an alternative, it is possible for userspace to request expiry of any
+mountpoint (though some will be rejected - the current process's idea of the
+rootfs for example). It does this by passing the MNT_EXPIRE flag to
+umount(). This flag is considered incompatible with MNT_FORCE and MNT_DETACH.
+
+If the mountpoint in question is in referenced by something other than
+umount() or its parent mountpoint, an EBUSY error will be returned and the
+mountpoint will not be marked for expiration or unmounted.
+
+If the mountpoint was not already marked for expiry at that time, an EAGAIN
+error will be given and it won't be unmounted.
+
+Otherwise if it was already marked and it wasn't referenced, unmounting will
+take place as usual.
+
+Again, the expiration flag is cleared every time anything other than umount()
+looks at a mountpoint.
*
-* ./Documentation/filesystems/udf.txt
+* Documentation/filesystems/udf.txt
*
UDF Filesystem version 0.9.8.1
--- /dev/null
+==================
+i2c-parport driver
+==================
+
+2004-07-06, Jean Delvare
+
+This is a unified driver for several i2c-over-parallel-port adapters,
+such as the ones made by Philips, Velleman or ELV. This driver is
+meant as a replacement for the older, individual drivers:
+ * i2c-philips-par
+ * i2c-elv
+ * i2c-velleman
+ * video/i2c-parport (NOT the same as this one, dedicated to home brew
+ teletext adapters)
+
+It currently supports the following devices:
+ * Philips adapter
+ * home brew teletext adapter
+ * Velleman K8000 adapter
+ * ELV adapter
+ * Analog Devices evaluation boards (ADM1025, ADM1030, ADM1031, ADM1032)
+
+These devices use different pinout configurations, so you have to tell
+the driver what you have, using the type module parameter. There is no
+way to autodetect the devices. Support for different pinout configurations
+can be easily added when needed.
+
+
+Building your own adapter
+-------------------------
+
+If you want to build you own i2c-over-parallel-port adapter, here is
+a sample electronics schema (credits go to Sylvain Munaut):
+
+Device PC
+Side ___________________Vdd (+) Side
+ | | |
+ --- --- ---
+ | | | | | |
+ |R| |R| |R|
+ | | | | | |
+ --- --- ---
+ | | |
+ | | /| |
+SCL ----------x--------o |-----------x------------------- pin 2
+ | \| | |
+ | | |
+ | |\ | |
+SDA ----------x----x---| o---x--------------------------- pin 13
+ | |/ |
+ | |
+ | /| |
+ ---------o |----------------x-------------- pin 3
+ \| | |
+ | |
+ --- ---
+ | | | |
+ |R| |R|
+ | | | |
+ --- ---
+ | |
+ ### ###
+ GND GND
+
+Remarks:
+ - This is the exact pinout and electronics used on the Analog Devices
+ evaluation boards.
+ /|
+ - All inverters -o |- must be 74HC05, they must be open collector output.
+ \|
+ - All resitors are 10k.
+ - Pins 18-25 of the parallel port connected to GND.
+ - Pins 4-9 (D2-D7) could be used as VDD is the driver drives them high.
+ The ADM1032 evaluation board uses D4-D7. Beware that the amount of
+ current you can draw from the parallel port is limited. Also note that
+ all connected lines MUST BE driven at the same state, else you'll short
+ circuit the output buffers! So plugging the I2C adapter after loading
+ the i2c-parport module might be a good safety since data line state
+ prior to init may be unknown.
+ - This is 5V!
+ - Obviously you cannot read SCL (so it's not really standard-compliant).
+ Pretty easy to add, just copy the SDA part and use another input pin.
+ That would give (ELV compatible pinout):
+
+
+Device PC
+Side ______________________________Vdd (+) Side
+ | | | |
+ --- --- --- ---
+ | | | | | | | |
+ |R| |R| |R| |R|
+ | | | | | | | |
+ --- --- --- ---
+ | | | |
+ | | |\ | |
+SCL ----------x--------x--| o---x------------------------ pin 15
+ | | |/ |
+ | | |
+ | | /| |
+ | ---o |-------------x-------------- pin 2
+ | \| | |
+ | | |
+ | | |
+ | |\ | |
+SDA ---------------x---x--| o--------x------------------- pin 10
+ | |/ |
+ | |
+ | /| |
+ ---o |------------------x--------- pin 3
+ \| | |
+ | |
+ --- ---
+ | | | |
+ |R| |R|
+ | | | |
+ --- ---
+ | |
+ ### ###
+ GND GND
+
+
+If possible, you should use the same pinout configuration as existing
+adapters do, so you won't even have to change the code.
+
+
+Similar (but different) drivers
+-------------------------------
+
+This driver is NOT the same as the i2c-pport driver found in the i2c package.
+The i2c-pport driver makes use of modern parallel port features so that
+you don't need additional electronics. It has other restrictions however, and
+was not ported to Linux 2.6 (yet).
+
+This driver is also NOT the same as the i2c-pcf-epp driver found in the
+lm_sensors package. The i2c-pcf-epp driver doesn't use the parallel port
+as an I2C bus directly. Instead, it uses it to control an external I2C bus
+master. That driver was not ported to Linux 2.6 (yet) either.
+
+
+Legacy documentation for Velleman adapter
+-----------------------------------------
+
+Useful links:
+Velleman http://www.velleman.be/
+Velleman K8000 Howto http://howto.htlw16.ac.at/k8000-howto.html
+
+The project has lead to new libs for the Velleman K8000 and K8005:
+ LIBK8000 v1.99.1 and LIBK8005 v0.21
+With these libs, you can control the K8000 interface card and the K8005
+stepper motor card with the simple commands which are in the original
+Velleman software, like SetIOchannel, ReadADchannel, SendStepCCWFull and
+many more, using /dev/velleman.
+ http://home.wanadoo.nl/hihihi/libk8000.htm
+ http://home.wanadoo.nl/hihihi/libk8005.htm
+ http://struyve.mine.nu:8080/index.php?block=k8000
+ http://sourceforge.net/projects/libk8005/
though not all of them are actually meaningful to the kernel. Boot
loader authors who need additional command line options for the boot
loader itself should get them registered in
-linux/Documentation/kernel-parameters.txt to make sure they will not
+Documentation/kernel-parameters.txt to make sure they will not
conflict with actual kernel options now or in the future.
vga=<mode>
module outside the kernel is to use the kernel build system,
kbuild. Use the following command-line:
-make -C path/to/kernel/src SUBDIRS=$PWD modules
+make -C path/to/kernel/src M=$PWD modules
This requires that a makefile exits made in accordance to
Documentation/kbuild/makefiles.txt. Read that file for more details on
# Invokes the kernel build system to come back to the current
# directory and build yourmodule.ko.
default:
- make -C ${KERNEL_SOURCE} SUBDIRS=`pwd` modules
+ make -C ${KERNEL_SOURCE} M=`pwd` modules
This should not cause problems for anybody, since everybody using a
2.1.x kernel should have updated their C library to a suitable version
-anyway (see the file "linux/Documentation/Changes".)
+anyway (see the file "Documentation/Changes".)
1.2 Allow Mixed Locks Again
---------------------------
The current list of parameters can be found in the files:
linux/net/TUNABLE
- linux/Documentation/networking/ip-sysctl.txt
+ Documentation/networking/ip-sysctl.txt
Some of these are accessible via the sysctl interface, and many more are
scheduled to be added in this way. For example, some parameters related
The SliceCOM board doesn't require firmware. You can have 4 of these cards
in one machine. The driver doesn't (yet) support shared interrupts, so
you will need a separate IRQ line for every board.
-Read linux/Documentation/networking/slicecom.txt for help on configuring
+Read Documentation/networking/slicecom.txt for help on configuring
this adapter.
THE HDLC/PPP LINE PROTOCOL DRIVER
you have to enable it with a boot time parameter. Prior to 2.4.2-ac18
the NMI-oopser is enabled unconditionally on x86 SMP boxes.
+On x86-64 the NMI oopser is on by default. On 64bit Intel CPUs
+it uses IO-APIC by default and on AMD it uses local APIC.
+
[ feel free to send bug reports, suggestions and patches to
Ingo Molnar <mingo@redhat.com> or the Linux SMP mailing
list at <linux-smp@vger.kernel.org> ]
A lot of the assembly code currently runs in real mode, which means
absolute addresses are used instead of virtual addresses as in the
rest of the kernel. To translate an absolute address to a virtual
-address you can lookup in System.map, add __PAGE_OFFSET (0xc0000000
+address you can lookup in System.map, add __PAGE_OFFSET (0x10000000
currently).
code tried to access.
Typical values for the System Responder address are addresses larger
-than __PAGE_OFFSET (0xc0000000) which mean a virtual address didn't
+than __PAGE_OFFSET (0x10000000) which mean a virtual address didn't
get translated to a physical address before real-mode code tried to
access it.
General Registers as specified by ABI
- FPU Registers must not be used in kernel mode
-
Control Registers
CR 0 (Recovery Counter) used for ptrace
CR 8 (Protection ID) per-process value*
CR 9, 12, 13 (PIDS) unused
CR10 (CCR) lazy FPU saving*
-CR11 as specified by ABI
+CR11 as specified by ABI (SAR)
CR14 (interruption vector) initialized to fault_vector
CR15 (EIEM) initialized to all ones*
CR16 (Interval Timer) read for cycle count/write starts Interval Tmr
CR17-CR22 interruption parameters
+CR19 Interrupt Instruction Register
+CR20 Interrupt Space Register
+CR21 Interrupt Offset Register
+CR22 Interrupt PSW
CR23 (EIRR) read for pending interrupts/write clears bits
CR24 (TR 0) Kernel Space Page Directory Pointer
CR25 (TR 1) User Space Page Directory Pointer
--- /dev/null
+===========================================================================
+ HVCS
+ IBM "Hypervisor Virtual Console Server" Installation Guide
+ for Linux Kernel 2.6.4+
+ Copyright (C) 2004 IBM Corporation
+
+===========================================================================
+NOTE:Eight space tabs are the optimum editor setting for reading this file.
+===========================================================================
+
+ Author(s) : Ryan S. Arnold <rsa@us.ibm.com>
+ Date Created: March, 02, 2004
+ Last Changed: July, 07, 2004
+
+---------------------------------------------------------------------------
+Table of contents:
+
+ 1. Driver Introduction:
+ 2. System Requirements
+ 3. Build Options:
+ 3.1 Built-in:
+ 3.2 Module:
+ 4. Installation:
+ 5. Connection:
+ 6. Disconnection:
+ 7. Configuration:
+ 8. Questions & Answers:
+ 9. Reporting Bugs:
+
+---------------------------------------------------------------------------
+1. Driver Introduction:
+
+This is the device driver for the IBM Hypervisor Virtual Console Server,
+"hvcs". The IBM hvcs provides a tty driver interface to allow Linux user
+space applications access to the system consoles of logically partitioned
+operating systems (Linux and AIX) running on the same partitioned Power5
+ppc64 system. Physical hardware consoles per partition are not practical
+on this hardware so system consoles are accessed by this driver using
+firmware interfaces to virtual terminal devices.
+
+---------------------------------------------------------------------------
+2. System Requirements:
+
+This device driver was written using 2.6.4 Linux kernel APIs and will only
+build and run on kernels of this version or later.
+
+This driver was written to operate solely on IBM Power5 ppc64 hardware
+though some care was taken to abstract the architecture dependent firmware
+calls from the driver code.
+
+Sysfs must be mounted on the system so that the user can determine which
+major and minor numbers are associated with each vty-server. Directions
+for sysfs mounting are outside the scope of this document.
+
+---------------------------------------------------------------------------
+3. Build Options:
+
+The hvcs driver registers itself as a tty driver. The tty layer
+dynamically allocates a block of major and minor numbers in a quantity
+requested by the registering driver. The hvcs driver asks the tty layer
+for 64 of these major/minor numbers by default to use for hvcs device node
+entries.
+
+If the default number of device entries is adequate then this driver can be
+built into the kernel. If not, the default can be over-ridden by inserting
+the driver as a module with insmod parameters.
+
+---------------------------------------------------------------------------
+3.1 Built-in:
+
+The following menuconfig example demonstrates selecting to build this
+driver into the kernel.
+
+ Device Drivers --->
+ Character devices --->
+ <*> IBM Hypervisor Virtual Console Server Support
+
+Begin the kernel make process.
+
+---------------------------------------------------------------------------
+3.2 Module:
+
+The following menuconfig example demonstrates selecting to build this
+driver as a kernel module.
+
+ Device Drivers --->
+ Character devices --->
+ <M> IBM Hypervisor Virtual Console Server Support
+
+The make process will build the following kernel modules:
+
+ hvcs.ko
+ hvcserver.ko
+
+To insert the module with the default allocation execute the following
+commands in the order they appear:
+
+ insmod hvcserver.ko
+ insmod hvcs.ko
+
+The hvcserver module contains architecture specific firmware calls and must
+be inserted first, otherwise the hvcs module will not find some of the
+symbols it expects.
+
+To override the default use an insmod parameter as follows (requesting 4
+tty devices as an example):
+
+ insmod hvcs.ko hvcs_parm_num_devs=4
+
+There is a maximum number of dev entries that can be specified on insmod.
+We think that 1024 is currently a decent maximum number of server adapters
+to allow. This can always be changed by modifying the constant in the
+source file before building.
+
+NOTE: The length of time it takes to insmod the driver seems to be related
+to the number of tty interfaces the registering driver requests.
+
+In order to remove the driver module execute the following command:
+
+ rmmod hvcs.ko
+
+The recommended method for installing hvcs as a module is to use depmod to
+build a current modules.dep file in /lib/modules/`uname -r` and then
+execute:
+
+modprobe hvcs hvcs_parm_num_devs=4
+
+The modules.dep file indicates that hvcserver.ko needs to be inserted
+before hvcs.ko and modprobe uses this file to smartly insert the modules in
+the proper order.
+
+The following modprobe command is used to remove hvcs and hvcserver in the
+proper order:
+
+modprobe -r hvcs
+
+---------------------------------------------------------------------------
+4. Installation:
+
+The tty layer creates sysfs entries which contain the major and minor
+numbers allocated for the hvcs driver. The following snippet of "tree"
+output of the sysfs directory shows where these numbers are presented:
+
+ sys/
+ |-- *other sysfs base dirs*
+ |
+ |-- class
+ | |-- *other classes of devices*
+ | |
+ | `-- tty
+ | |-- *other tty devices*
+ | |
+ | |-- hvcs0
+ | | `-- dev
+ | |-- hvcs1
+ | | `-- dev
+ | |-- hvcs2
+ | | `-- dev
+ | |-- hvcs3
+ | | `-- dev
+ | |
+ | |-- *other tty devices*
+ |
+ |-- *other sysfs base dirs*
+
+For the above examples the following output is a result of cat'ing the
+"dev" entry in the hvcs directory:
+
+ Pow5:/sys/class/tty/hvcs0/ # cat dev
+ 254:0
+
+ Pow5:/sys/class/tty/hvcs1/ # cat dev
+ 254:1
+
+ Pow5:/sys/class/tty/hvcs2/ # cat dev
+ 254:2
+
+ Pow5:/sys/class/tty/hvcs3/ # cat dev
+ 254:3
+
+The output from reading the "dev" attribute is the char device major and
+minor numbers that the tty layer has allocated for this driver's use. Most
+systems running hvcs will already have the device entries created or udev
+will do it automatically.
+
+Given the example output above, to manually create a /dev/hvcs* node entry
+mknod can be used as follows:
+
+ mknod /dev/hvcs0 c 254 0
+ mknod /dev/hvcs1 c 254 1
+ mknod /dev/hvcs2 c 254 2
+ mknod /dev/hvcs3 c 254 3
+
+Using mknod to manually create the device entries makes these device nodes
+persistent. Once created they will exist prior to the driver insmod.
+
+Attempting to connect an application to /dev/hvcs* prior to insertion of
+the hvcs module will result in an error message similar to the following:
+
+ "/dev/hvcs*: No such device".
+
+NOTE: Just because there is a device node present doesn't mean that there
+is a vty-server device configured for that node.
+
+---------------------------------------------------------------------------
+5. Connection
+
+Since this driver controls devices that provide a tty interface a user can
+interact with the device node entries using any standard tty-interactive
+method (e.g. "cat", "dd", "echo"). The intent of this driver however, is
+to provide real time console interaction with a Linux partition's console,
+which requires the use of applications that provide bi-directional,
+interactive I/O with a tty device.
+
+Applications (e.g. "minicom" and "screen") that act as terminal emulators
+or perform terminal type control sequence conversion on the data being
+passed through them are NOT acceptable for providing interactive console
+I/O. These programs often emulate antiquated terminal types (vt100 and
+ANSI) and expect inbound data to take the form of one of these supported
+terminal types but they either do not convert, or do not _adequately_
+convert, outbound data into the terminal type of the terminal which invoked
+them (though screen makes an attempt and can apparently be configured with
+much termcap wrestling.)
+
+For this reason kermit and cu are two of the recommended applications for
+interacting with a Linux console via an hvcs device. These programs simply
+act as a conduit for data transfer to and from the tty device. They do not
+require inbound data to take the form of a particular terminal type, nor do
+they cook outbound data to a particular terminal type.
+
+In order to ensure proper functioning of console applications one must make
+sure that once connected to a /dev/hvcs console that the console's $TERM
+env variable is set to the exact terminal type of the terminal emulator
+used to launch the interactive I/O application. If one is using xterm and
+kermit to connect to /dev/hvcs0 when the console prompt becomes available
+one should "export TERM=xterm" on the console. This tells ncurses
+applications that are invoked from the console that they should output
+control sequences that xterm can understand.
+
+As a precautionary measure an hvcs user should always "exit" from their
+session before disconnecting an application such as kermit from the device
+node. If this is not done, the next user to connect to the console will
+continue using the previous user's logged in session which includes
+using the $TERM variable that the previous user supplied.
+
+---------------------------------------------------------------------------
+6. Disconnection
+
+As a security feature to prevent the delivery of stale data to an
+unintended target the Power5 system firmware disables the fetching of data
+and discards that data when a connection between a vty-server and a vty has
+been severed. As an example, when a vty-server is immediately disconnected
+from a vty following output of data to the vty the vty adapter may not have
+enough time between when it received the data interrupt and when the
+connection was severed to fetch the data from firmware before the fetch is
+disabled by firmware.
+
+When hvcs is being used to serve consoles this behavior is not a huge issue
+because the adapter stays connected for large amounts of time following
+almost all data writes. When hvcs is being used as a tty conduit to tunnel
+data between two partitions [see Q & A below] this is a huge problem
+because the standard Linux behavior when cat'ing or dd'ing data to a device
+is to open the tty, send the data, and then close the tty. If this driver
+manually terminated vty-server connections on tty close this would close
+the vty-server and vty connection before the target vty has had a chance to
+fetch the data.
+
+Additionally, disconnecting a vty-server and vty only on module removal or
+adapter removal is impractical because other vty-servers in other
+partitions may require the usage of the target vty at any time.
+
+Due to this behavioral restriction disconnection of vty-servers from the
+connected vty is a manual procedure using a write to a sysfs attribute
+outlined below, on the other hand the initial vty-server connection to a
+vty is established automatically by this driver. Manual vty-server
+connection is never required.
+
+In order to terminate the connection between a vty-server and vty the
+"vterm_state" sysfs attribute within each vty-server's sysfs entry is used.
+Reading this attribute reveals the current connection state of the
+vty-server adapter. A zero means that the vty-server is not connected to a
+vty. A one indicates that a connection is active.
+
+Writing a '0' (zero) to the vterm_state attribute will disconnect the VTERM
+connection between the vty-server and target vty ONLY if the vterm_state
+previously read '1'. The write directive is ignored if the vterm_state
+read '0' or if any value other than '0' was written to the vterm_state
+attribute. The following example will show the method used for verifying
+the vty-server connection status and disconnecting a vty-server connection.
+
+ Pow5:/sys/bus/vio/drivers/hvcs/30000004 # cat vterm_state
+ 1
+
+ Pow5:/sys/bus/vio/drivers/hvcs/30000004 # echo 0 > vterm_state
+
+ Pow5:/sys/bus/vio/drivers/hvcs/30000004 # cat vterm_state
+ 0
+
+All vty-server connections are automatically terminated when the device is
+hotplug removed and when the module is removed.
+
+---------------------------------------------------------------------------
+7. Configuration
+
+Each vty-server has a sysfs entry in the /sys/devices/vio directory, which
+is symlinked in several other sysfs tree directories, notably under the
+hvcs driver entry, which looks like the following example:
+
+ Pow5:/sys/bus/vio/drivers/hvcs # ls
+ . .. 30000003 30000004 rescan
+
+By design, firmware notifies the hvcs driver of vty-server lifetimes and
+partner vty removals but not the addition of partner vtys. Since an HMC
+Super Admin can add partner info dynamically we have provided the hvcs
+driver sysfs directory with the "rescan" update attribute which will query
+firmware and update the partner info for all the vty-servers that this
+driver manages. Writing a '1' to the attribute triggers the update. An
+explicit example follows:
+
+ Pow5:/sys/bus/vio/drivers/hvcs # echo 1 > rescan
+
+Reading the attribute will indicate a state of '1' or '0'. A one indicates
+that an update is in process. A zero indicates that an update has
+completed or was never executed.
+
+Vty-server entries in this directory are a 32 bit partition unique unit
+address that is created by firmware. An example vty-server sysfs entry
+looks like the following:
+
+ Pow5:/sys/bus/vio/drivers/hvcs/30000004 # ls
+ . current_vty devspec partner_clcs vterm_state
+ .. detach_state name partner_vtys
+
+Each entry is provided, by default with a "name" attribute. Reading the
+"name" attribute will reveal the device type as shown in the following
+example:
+
+ Pow5:/sys/bus/vio/drivers/hvcs/30000003 # cat name
+ vty-server
+
+Each entry is also provided, by default, with a "devspec" attribute which
+reveals the full device specification when read, as shown in the following
+example:
+
+ Pow5:/sys/bus/vio/drivers/hvcs/30000004 # cat devspec
+ /vdevice/vty-server@30000004
+
+Each vty-server sysfs dir is provided with two read-only attributes that
+provide lists of easily parsed partner vty data: "partner_vtys" and
+"partner_clcs".
+
+ Pow5:/sys/bus/vio/drivers/hvcs/30000004 # cat partner_vtys
+ 30000000
+ 30000001
+ 30000002
+ 30000000
+ 30000000
+
+ Pow5:/sys/bus/vio/drivers/hvcs/30000004 # cat partner_clcs
+ U5112.428.103048A-V3-C0
+ U5112.428.103048A-V3-C2
+ U5112.428.103048A-V3-C3
+ U5112.428.103048A-V4-C0
+ U5112.428.103048A-V5-C0
+
+Reading partner_vtys returns a list of partner vtys. Vty unit address
+numbering is only per-partition-unique so entries will frequently repeat.
+
+Reading partner_clcs returns a list of "converged location codes" which are
+composed of a system serial number followed by "-V*", where the '*' is the
+target partition number, and "-C*", where the '*' is the slot of the
+adapter. The first vty partner corresponds to the first clc item, the
+second vty partner to the second clc item, etc.
+
+A vty-server can only be connected to a single vty at a time. The entry,
+"current_vty" prints the clc of the currently selected partner vty when
+read.
+
+The current_vty can be changed by writing a valid partner clc to the entry
+as in the following example:
+
+ Pow5:/sys/bus/vio/drivers/hvcs/30000004 # echo U5112.428.10304
+ 8A-V4-C0 > current_vty
+
+Changing the current_vty when a vty-server is already connected to a vty
+does not affect the current connection. The change takes effect when the
+currently open connection is freed.
+
+Information on the "vterm_state" attribute was covered earlier on the
+chapter entitled "disconnection".
+
+---------------------------------------------------------------------------
+8. Questions & Answers:
+===========================================================================
+Q: What are the security concerns involving hvcs?
+
+A: There are three main security concerns:
+
+ 1. The creator of the /dev/hvcs* nodes has the ability to restrict
+ the access of the device entries to certain users or groups. It
+ may be best to create a special hvcs group privilege for providing
+ access to system consoles.
+
+ 2. To provide network security when grabbing the console it is
+ suggested that the user connect to the console hosting partition
+ using a secure method, such as SSH or sit at a hardware console.
+
+ 3. Make sure to exit the user session when done with a console or
+ the next vty-server connection (which may be from another
+ partition) will experience the previously logged in session.
+
+---------------------------------------------------------------------------
+Q: How do I multiplex a console that I grab through hvcs so that other
+people can see it:
+
+A: You can use "screen" to directly connect to the /dev/hvcs* device and
+setup a session on your machine with the console group privileges. As
+pointed out earlier by default screen doesn't provide the termcap settings
+for most terminal emulators to provide adequate character conversion from
+term type "screen" to others. This means that curses based programs may
+not display properly in screen sessions.
+
+---------------------------------------------------------------------------
+Q: Why are the colors all messed up?
+Q: Why are the control characters acting strange or not working?
+Q: Why is the console output all strange and unintelligible?
+
+A: Please see the preceding section on "Connection" for a discussion of how
+applications can affect the display of character control sequences.
+Additionally, just because you logged into the console using and xterm
+doesn't mean someone else didn't log into the console with the HMC console
+(vt320) before you and leave the session logged in. The best thing to do
+is to export TERM to the terminal type of your terminal emulator when you
+get the console. Additionally make sure to "exit" the console before you
+disconnect from the console. This will ensure that the next user gets
+their own TERM type set when they login.
+
+---------------------------------------------------------------------------
+Q: When I try to CONNECT kermit to an hvcs device I get:
+"Sorry, can't open connection: /dev/hvcs*"What is happening?
+
+A: Some other Power5 console mechanism has a connection to the vty and
+isn't giving it up. You can try to force disconnect the consoles from the
+HMC by right clicking on the partition and then selecting "close terminal".
+Otherwise you have to hunt down the people who have console authority. It
+is possible that you already have the console open using another kermit
+session and just forgot about it. Please review the console options for
+Power5 systems to determine the many ways a system console can be held.
+
+OR
+
+A: Another user may not have a connectivity method currently attached to a
+/dev/hvcs device but the vterm_state may reveal that they still have the
+vty-server connection established. They need to free this using the method
+outlined in the section on "Disconnection" in order for others to connect
+to the target vty.
+
+OR
+
+A: The user profile you are using to execute kermit probably doesn't have
+permissions to use the /dev/hvcs* device.
+
+OR
+
+A: You probably haven't inserted the hvcs.ko module yet but the /dev/hvcs*
+entry still exists (on systems without udev).
+
+OR
+
+A: There is not a corresponding vty-server device that maps to an existing
+/dev/hvcs* entry.
+
+---------------------------------------------------------------------------
+Q: When I try to CONNECT kermit to an hvcs device I get:
+"Sorry, write access to UUCP lockfile directory denied."
+
+A: The /dev/hvcs* entry you have specified doesn't exist where you said it
+does? Maybe you haven't inserted the module (on systems with udev).
+
+---------------------------------------------------------------------------
+Q: If I already have one Linux partition installed can I use hvcs on said
+partition to provide the console for the install of a second Linux
+partition?
+
+A: Yes granted that your are connected to the /dev/hvcs* device using
+kermit or cu or some other program that doesn't provide terminal emulation.
+
+---------------------------------------------------------------------------
+Q: Can I connect to more than one partition's console at a time using this
+driver?
+
+A: Yes. Of course this means that there must be more than one vty-server
+configured for this partition and each must point to a disconnected vty.
+
+---------------------------------------------------------------------------
+Q: Does the hvcs driver support dynamic (hotplug) addition of devices?
+
+A: Yes, if you have dlpar and hotplug enabled for your system and it has
+been built into the kernel the hvcs drivers is configured to dynamically
+handle additions of new devices and removals of unused devices.
+
+---------------------------------------------------------------------------
+Q: Can I use /dev/hvcs* as a conduit to another partition and use a tty
+device on that partition as the other end of the pipe?
+
+A: Yes, on Power5 platforms the hvc_console driver provides a tty interface
+for extra /dev/hvc* devices (where /dev/hvc0 is most likely the console).
+In order to get a tty conduit working between the two partitions the HMC
+Super Admin must create an additional "serial server" for the target
+partition with the HMC gui which will show up as /dev/hvc* when the target
+partition is rebooted.
+
+The HMC Super Admin then creates an additional "serial client" for the
+current partition and points this at the target partition's newly created
+"serial server" adapter (remember the slot). This shows up as an
+additional /dev/hvcs* device.
+
+Now a program on the target system can be configured to read or write to
+/dev/hvc* and another program on the current partition can be configured to
+read or write to /dev/hvcs*. Now you have a tty conduit between two
+partitions.
+
+---------------------------------------------------------------------------
+9. Reporting Bugs:
+
+The proper channel for reporting bugs is either through the Linux OS
+distribution company that provided your OS or by posting issues to the
+ppc64 development mailing list at:
+
+linuxppc64-dev@lists.linuxppc.org
+
+This request is to provide a documented and searchable public exchange
+of the problems and solutions surrounding this driver for the benefit of
+all users.
Then notify /sbin/init that /etc/inittab has changed, by issuing
the telinit command with the q operand:
- cd /usr/src/linux/Documentation/s390
+ cd Documentation/s390
sh config3270.sh
sh /tmp/mkdev3270
telinit q
# insmod awe_wave
(Be sure to load awe_wave after sb!)
- See /usr/src/linux/Documentation/sound/oss/AWE32 for
+ See Documentation/sound/oss/AWE32 for
more details.
9. (only for obsolete systems) If you don't have /dev/sequencer
========
0.1.0 11/20/1998 First version, draft
1.0.0 11/1998 Alan Cox changes, incorporation in 2.2.0
- as /usr/src/linux/Documentation/sound/oss/Introduction
+ as Documentation/sound/oss/Introduction
1.1.0 6/30/1999 Second version, added notes on making the drivers,
added info on multiple sound cards of similar types,]
added more diagnostics info, added info about esd.
4) OSS's WWW site at http://www.opensound.com.
-5) All the files in linux/Documentation/sound.
+5) All the files in Documentation/sound.
6) The comments and code in linux/drivers/sound.
This documentation is relevant for the PAS16 driver (pas2_card.c and
friends) under kernel version 2.3.99 and later. If you are
unfamiliar with configuring sound under Linux, please read the
-Sound-HOWTO, linux/Documentation/sound/oss/Introduction and other
+Sound-HOWTO, Documentation/sound/oss/Introduction and other
relevant docs first.
The following information is relevant information from README.OSS
The new stuff for 2.3.99 and later
============================================================================
-The following configuration options from linux/Documentation/Configure.help
+The following configuration options from Documentation/Configure.help
are relevant to configuring the PAS16:
Sound card support
dev/ device specific information (eg dev/cdrom/info)
fs/ specific filesystems
filehandle, inode, dentry and quota tuning
- binfmt_misc <linux/Documentation/binfmt_misc.txt>
+ binfmt_misc <Documentation/binfmt_misc.txt>
kernel/ global kernel info / tuning
miscellaneous stuff
net/ networking stuff, for documentation look in:
- <linux/Documentation/networking/>
+ <Documentation/networking/>
proc/ <empty>
sunrpc/ SUN Remote Procedure Call (NFS)
vm/ memory management tuning
NOTE:
The USB subsystem now has a substantial section in "The Linux Kernel API"
- guide (in linux/Documentation/DocBook), generated from the current source
+ guide (in Documentation/DocBook), generated from the current source
code. This particular documentation file isn't particularly current or
complete; don't rely on it except for a quick overview.
--- /dev/null
+
+ SN9C10[12] PC Camera Controllers
+ Driver for Linux
+ ================================
+
+ - Documentation -
+
+
+Index
+=====
+1. Copyright
+2. License
+3. Overview
+4. Module dependencies
+5. Module loading
+6. Module parameters
+7. Device control through "sysfs"
+8. Supported devices
+9. How to add support for new image sensors
+10. Note for V4L2 developers
+11. Contact information
+12. Credits
+
+
+1. Copyright
+============
+Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it>
+
+SONiX is a trademark of SONiX Technology Company Limited, inc.
+This driver is not sponsored or developed by SONiX.
+
+
+2. License
+==========
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+3. Overview
+===========
+This driver attempts to support the video streaming capabilities of the devices
+mounting the SONiX SN9C101 or SONiX SN9C102 PC Camera Controllers.
+
+- It's worth to note that SONiX has never collaborated with me during the
+development of this project, despite of several requests for enough detailed
+specifications of the register tables, compression engine and video data format
+of the above chips -
+
+Up to 64 cameras can be handled at the same time. They can be connected and
+disconnected from the host many times without turning off the computer, if
+your system supports the hotplug facility.
+
+The driver relies on the Video4Linux2 and USB core modules. It has been
+designed to run properly on SMP systems as well.
+
+The latest version of the SN9C10[12] driver can be found at the following URL:
+http://go.lamarinapunto.com/
+
+
+4. Module dependencies
+======================
+For it to work properly, the driver needs kernel support for Video4Linux and
+USB.
+
+The following options of the kernel configuration file must be enabled and
+corresponding modules must be compiled:
+
+ # Multimedia devices
+ #
+ CONFIG_VIDEO_DEV=m
+
+ # USB support
+ #
+ CONFIG_USB=m
+
+In addition, depending on the hardware being used, the modules below are
+necessary:
+
+ # USB Host Controller Drivers
+ #
+ CONFIG_USB_EHCI_HCD=m
+ CONFIG_USB_UHCI_HCD=m
+ CONFIG_USB_OHCI_HCD=m
+
+And finally:
+
+ # USB Multimedia devices
+ #
+ CONFIG_USB_SN9C102=m
+
+
+5. Module loading
+=================
+To use the driver, it is necessary to load the "sn9c102" module into memory
+after every other module required: "videodev", "usbcore" and, depending on
+the USB host controller you have, "ehci-hcd", "uhci-hcd" or "ohci-hcd".
+
+Loading can be done as shown below:
+
+ [root@localhost home]# modprobe usbcore
+ [root@localhost home]# modprobe sn9c102
+
+At this point the devices should be recognized. You can invoke "dmesg" to
+analyze kernel messages and verify that the loading process has gone well:
+
+ [user@localhost home]$ dmesg
+
+
+6. Module parameters
+====================
+Module parameters are listed below:
+-------------------------------------------------------------------------------
+Name: video_nr
+Type: int array (min = 0, max = 32)
+Syntax: <-1|n[,...]>
+Description: Specify V4L2 minor mode number:
+ -1 = use next available
+ n = use minor number n
+ You can specify up to 32 cameras this way.
+ For example:
+ video_nr=-1,2,-1 would assign minor number 2 to the second
+ recognized camera and use auto for the first one and for every
+ other camera.
+Default: -1
+-------------------------------------------------------------------------------
+Name: debug
+Type: int
+Syntax: <n>
+Description: Debugging information level, from 0 to 3:
+ 0 = none (use carefully)
+ 1 = critical errors
+ 2 = significant informations
+ 3 = more verbose messages
+ Level 3 is useful for testing only, when just one device
+ is used.
+Default: 2
+-------------------------------------------------------------------------------
+
+
+7. Device control through "sysfs"
+=================================
+It is possible to read and write both the SN9C10[12] and the image sensor
+registers by using the "sysfs" filesystem interface.
+
+Every time a supported device is recognized, read-only files named "redblue"
+and "green" are created in the /sys/class/video4linux/videoX directory. You can
+set the red, blue and green channel's gain by writing the desired value to
+them. The value may range from 0 to 15 for each channel; this means that
+"redblue" accepts 8-bit values, where the low 4 bits are reserved for red and
+the others for blue.
+
+There are other four entries in the directory above for each registered camera:
+"reg", "val", "i2c_reg" and "i2c_val". The first two files control the
+SN9C10[12] bridge, while the other two control the sensor chip. "reg" and
+"i2c_reg" hold the values of the current register index where the following
+reading/writing operations are addressed at through "val" and "i2c_val". Their
+use is not intended for end-users, unless you know what you are doing. Note
+that "i2c_reg" and "i2c_val" won't be created if the sensor does not actually
+support the standard I2C protocol. Also, remember that you must be logged in as
+root before writing to them.
+
+As an example, suppose we were to want to read the value contained in the
+register number 1 of the sensor register table - which usually is the product
+identifier - of the camera registered as "/dev/video0":
+
+ [root@localhost #] cd /sys/class/video4linux/video0
+ [root@localhost #] echo 1 > i2c_reg
+ [root@localhost #] cat i2c_val
+
+Now let's set the green gain's register of the SN9C10[12] chip to 2:
+
+ [root@localhost #] echo 0x11 > reg
+ [root@localhost #] echo 2 > val
+
+Note that the SN9C10[12] always returns 0 when some of its registers are read.
+To avoid race conditions, all the I/O accesses to the files are serialized.
+
+
+8. Supported devices
+====================
+- I won't mention any of the names of the companies as well as their products
+here. They have never collaborated with me, so no advertising -
+
+From the point of view of a driver, what unambiguously identify a device are
+its vendor and product USB identifiers. Below is a list of known identifiers of
+devices mounting the SN9C10[12] PC camera controllers:
+
+Vendor ID Product ID
+--------- ----------
+0xc45 0x6001
+0xc45 0x6005
+0xc45 0x6009
+0xc45 0x600d
+0xc45 0x6024
+0xc45 0x6025
+0xc45 0x6028
+0xc45 0x6029
+0xc45 0x602a
+0xc45 0x602c
+0xc45 0x8001
+
+The list above does NOT imply that all those devices work with this driver: up
+until now only the ones that mount the following image sensors are supported.
+Kernel messages will always tell you whether this is the case:
+
+Model Manufacturer
+----- ------------
+PAS106B PixArt Imaging Inc.
+TAS5110C1B Taiwan Advanced Sensor Corporation
+TAS5130D1B Taiwan Advanced Sensor Corporation
+
+If you think your camera is based on the above hardware and is not actually
+listed in the above table, you may try to add the specific USB VendorID and
+ProductID identifiers to the sn9c102_id_table[] in the file "sn9c102_sensor.h";
+then compile, load the module again and look at the kernel output.
+If this works, please send an email to me reporting the kernel messages, so
+that I will add a new entry in the list of supported devices.
+
+Donations of new models for further testing and support would be much
+appreciated. I won't add official support for hardware that I don't actually
+have.
+
+
+9. How to add support for new image sensors
+===========================================
+It should be easy to write code for new sensors by using the small API that I
+have created for this purpose, which is present in "sn9c102_sensor.h"
+(documentation is included there). As an example, have a look at the code in
+"sn9c102_pas106b.c", which uses the mentioned interface.
+
+At the moment, not yet supported image sensors are: PAS202B (VGA),
+HV7131[D|E1] (VGA), MI03 (VGA), OV7620 (VGA).
+
+
+10. Note for V4L2 developers
+============================
+This driver follows the V4L2 API specifications. In particular, it enforces two
+rules:
+
+1) Exactly one I/O method, either "mmap" or "read", is associated with each
+file descriptor. Once it is selected, the application must close and reopen the
+device to switch to the other I/O method.
+
+2) Previously mapped buffer memory must always be unmapped before calling any
+of the "VIDIOC_S_CROP", "VIDIOC_TRY_FMT" and "VIDIOC_S_FMT" ioctl's. In case,
+the same number of buffers as before will be allocated again to match the size
+of the new video frames, so you have to map them again before any I/O attempts.
+
+
+11. Contact information
+=======================
+I may be contacted by e-mail at <luca.risolia@studio.unibo.it>.
+
+I can accept GPG/PGP encrypted e-mail. My GPG key ID is 'FCE635A4'.
+My public 1024-bit key should be available at any keyserver; the fingerprint
+is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'.
+
+
+12. Credits
+===========
+I would thank the following persons:
+
+- Stefano Mozzi, who donated 45 EU;
+- Luca Capello for the donation of a webcam;
+- Mizuno Takafumi for the donation of a webcam.
2000-July-12
For USB help other than the readme files that are located in
-linux/Documentation/usb/*, see the following:
+Documentation/usb/*, see the following:
Linux-USB project: http://www.linux-usb.org
mirrors at http://www.suse.cz/development/linux-usb/
Information - video4linux:
http://roadrunner.swansea.linux.org.uk/v4lapi.shtml
-/usr/src/linux/Documentation/video4linux/API.html
+Documentation/video4linux/API.html
/usr/include/linux/videodev.h
Information - video4linux/mjpeg extensions:
- There are various README files in the Documentation/ subdirectory:
these typically contain kernel-specific installation notes for some
- drivers for example. See ./Documentation/00-INDEX for a list of what
+ drivers for example. See Documentation/00-INDEX for a list of what
is contained in each file. Please read the Changes file, as it
contains information about the problems, which may result by upgrading
your kernel.
Compiling and running the 2.6.xx kernels requires up-to-date
versions of various software packages. Consult
- ./Documentation/Changes for the minimum version numbers required
+ Documentation/Changes for the minimum version numbers required
and how to get updates for these packages. Beware that using
excessively old versions of these packages can cause indirect
errors that are very difficult to track down, so don't assume that
gcc 2.91.66 (egcs-1.1.2), and gcc 2.7.2.3 are known to miscompile
some parts of the kernel, and are *no longer supported*.
Also remember to upgrade your binutils package (for as/ld/nm and company)
- if necessary. For more information, refer to ./Documentation/Changes.
+ if necessary. For more information, refer to Documentation/Changes.
Please note that you can still run a.out user programs with this kernel.
mov r0, #0x30
mcr p15, 0, r0, c1, c0, 0
mov r0, #0x13
- msr cpsr, r0
+ msr cpsr_cxsf, r0
mov r12, #0x03000000 @ point to LEDs
orr r12, r12, #0x00020000
orr r12, r12, #0xba00
/* Ensure all interrupts are off and MMU disabled */
mrs r0, cpsr
orr r0, r0, #0xc0
- msr cpsr, r0
+ msr cpsr_cxsf, r0
adr lr, 1b
orr lr, lr, #0x10000000
--- /dev/null
+/*
+ * linux/arch/arm/common/locomo.c
+ *
+ * Sharp LoCoMo 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.
+ *
+ * This file contains all generic LoCoMo support.
+ *
+ * All initialization functions provided here are intended to be called
+ * from machine specific code with proper arguments when required.
+ *
+ * Based on sa1111.c
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+
+#include <asm/hardware/locomo.h>
+
+/* the following is the overall data for the locomo chip */
+struct locomo {
+ struct device *dev;
+ unsigned long phys;
+ unsigned int irq;
+ void *base;
+};
+
+struct locomo_dev_info {
+ unsigned long offset;
+ unsigned long length;
+ unsigned int devid;
+ unsigned int irq[1];
+ const char * name;
+};
+
+static struct locomo_dev_info locomo_devices[] = {
+};
+
+
+/** LoCoMo interrupt handling stuff.
+ * NOTE: LoCoMo has a 1 to many mapping on all of its IRQs.
+ * that is, there is only one real hardware interrupt
+ * we determine which interrupt it is by reading some IO memory.
+ * We have two levels of expansion, first in the handler for the
+ * hardware interrupt we generate an interrupt
+ * IRQ_LOCOMO_*_BASE and those handlers generate more interrupts
+ *
+ * hardware irq reads LOCOMO_ICR & 0x0f00
+ * IRQ_LOCOMO_KEY_BASE
+ * IRQ_LOCOMO_GPIO_BASE
+ * IRQ_LOCOMO_LT_BASE
+ * IRQ_LOCOMO_SPI_BASE
+ * IRQ_LOCOMO_KEY_BASE reads LOCOMO_KIC & 0x0001
+ * IRQ_LOCOMO_KEY
+ * IRQ_LOCOMO_GPIO_BASE reads LOCOMO_GIR & LOCOMO_GPD & 0xffff
+ * IRQ_LOCOMO_GPIO[0-15]
+ * IRQ_LOCOMO_LT_BASE reads LOCOMO_LTINT & 0x0001
+ * IRQ_LOCOMO_LT
+ * IRQ_LOCOMO_SPI_BASE reads LOCOMO_SPIIR & 0x000F
+ * IRQ_LOCOMO_SPI_RFR
+ * IRQ_LOCOMO_SPI_RFW
+ * IRQ_LOCOMO_SPI_OVRN
+ * IRQ_LOCOMO_SPI_TEND
+ */
+
+#define LOCOMO_IRQ_START (IRQ_LOCOMO_KEY_BASE)
+#define LOCOMO_IRQ_KEY_START (IRQ_LOCOMO_KEY)
+#define LOCOMO_IRQ_GPIO_START (IRQ_LOCOMO_GPIO0)
+#define LOCOMO_IRQ_LT_START (IRQ_LOCOMO_LT)
+#define LOCOMO_IRQ_SPI_START (IRQ_LOCOMO_SPI_RFR)
+
+static void locomo_handler(unsigned int irq, struct irqdesc *desc,
+ struct pt_regs *regs)
+{
+ int req, i;
+ struct irqdesc *d;
+ void *mapbase = get_irq_chipdata(irq);
+
+ /* Acknowledge the parent IRQ */
+ desc->chip->ack(irq);
+
+ /* check why this interrupt was generated */
+ req = locomo_readl(mapbase + LOCOMO_ICR) & 0x0f00;
+
+ if (req) {
+ /* generate the next interrupt(s) */
+ irq = LOCOMO_IRQ_START;
+ d = irq_desc + irq;
+ for (i = 0; i <= 3; i++, d++, irq++) {
+ if (req & (0x0100 << i)) {
+ d->handle(irq, d, regs);
+ }
+
+ }
+ }
+}
+
+static void locomo_ack_irq(unsigned int irq)
+{
+}
+
+static void locomo_mask_irq(unsigned int irq)
+{
+ void *mapbase = get_irq_chipdata(irq);
+ unsigned int r;
+ r = locomo_readl(mapbase + LOCOMO_ICR);
+ r &= ~(0x0010 << (irq - LOCOMO_IRQ_START));
+ locomo_writel(r, mapbase + LOCOMO_ICR);
+}
+
+static void locomo_unmask_irq(unsigned int irq)
+{
+ void *mapbase = get_irq_chipdata(irq);
+ unsigned int r;
+ r = locomo_readl(mapbase + LOCOMO_ICR);
+ r |= (0x0010 << (irq - LOCOMO_IRQ_START));
+ locomo_writel(r, mapbase + LOCOMO_ICR);
+}
+
+static struct irqchip locomo_chip = {
+ .ack = locomo_ack_irq,
+ .mask = locomo_mask_irq,
+ .unmask = locomo_unmask_irq,
+};
+
+static void locomo_key_handler(unsigned int irq, struct irqdesc *desc,
+ struct pt_regs *regs)
+{
+ struct irqdesc *d;
+ void *mapbase = get_irq_chipdata(irq);
+
+ if (locomo_readl(mapbase + LOCOMO_KIC) & 0x0001) {
+ d = irq_desc + LOCOMO_IRQ_KEY_START;
+ d->handle(LOCOMO_IRQ_KEY_START, d, regs);
+ }
+}
+
+static void locomo_key_ack_irq(unsigned int irq)
+{
+ void *mapbase = get_irq_chipdata(irq);
+ unsigned int r;
+ r = locomo_readl(mapbase + LOCOMO_KIC);
+ r &= ~(0x0100 << (irq - LOCOMO_IRQ_KEY_START));
+ locomo_writel(r, mapbase + LOCOMO_KIC);
+}
+
+static void locomo_key_mask_irq(unsigned int irq)
+{
+ void *mapbase = get_irq_chipdata(irq);
+ unsigned int r;
+ r = locomo_readl(mapbase + LOCOMO_KIC);
+ r &= ~(0x0010 << (irq - LOCOMO_IRQ_KEY_START));
+ locomo_writel(r, mapbase + LOCOMO_KIC);
+}
+
+static void locomo_key_unmask_irq(unsigned int irq)
+{
+ void *mapbase = get_irq_chipdata(irq);
+ unsigned int r;
+ r = locomo_readl(mapbase + LOCOMO_KIC);
+ r |= (0x0010 << (irq - LOCOMO_IRQ_KEY_START));
+ locomo_writel(r, mapbase + LOCOMO_KIC);
+}
+
+static struct irqchip locomo_key_chip = {
+ .ack = locomo_key_ack_irq,
+ .mask = locomo_key_mask_irq,
+ .unmask = locomo_key_unmask_irq,
+};
+
+static void locomo_gpio_handler(unsigned int irq, struct irqdesc *desc,
+ struct pt_regs *regs)
+{
+ int req, i;
+ struct irqdesc *d;
+ void *mapbase = get_irq_chipdata(irq);
+
+ req = locomo_readl(mapbase + LOCOMO_GIR) &
+ locomo_readl(mapbase + LOCOMO_GPD) &
+ 0xffff;
+
+ if (req) {
+ irq = LOCOMO_IRQ_GPIO_START;
+ d = irq_desc + LOCOMO_IRQ_GPIO_START;
+ for (i = 0; i <= 15; i++, irq++, d++) {
+ if (req & (0x0001 << i)) {
+ d->handle(irq, d, regs);
+ }
+ }
+ }
+}
+
+static void locomo_gpio_ack_irq(unsigned int irq)
+{
+ void *mapbase = get_irq_chipdata(irq);
+ unsigned int r;
+ r = locomo_readl(mapbase + LOCOMO_GWE);
+ r |= (0x0001 << (irq - LOCOMO_IRQ_GPIO_START));
+ locomo_writel(r, mapbase + LOCOMO_GWE);
+
+ r = locomo_readl(mapbase + LOCOMO_GIS);
+ r &= ~(0x0001 << (irq - LOCOMO_IRQ_GPIO_START));
+ locomo_writel(r, mapbase + LOCOMO_GIS);
+
+ r = locomo_readl(mapbase + LOCOMO_GWE);
+ r &= ~(0x0001 << (irq - LOCOMO_IRQ_GPIO_START));
+ locomo_writel(r, mapbase + LOCOMO_GWE);
+}
+
+static void locomo_gpio_mask_irq(unsigned int irq)
+{
+ void *mapbase = get_irq_chipdata(irq);
+ unsigned int r;
+ r = locomo_readl(mapbase + LOCOMO_GIE);
+ r &= ~(0x0001 << (irq - LOCOMO_IRQ_GPIO_START));
+ locomo_writel(r, mapbase + LOCOMO_GIE);
+}
+
+static void locomo_gpio_unmask_irq(unsigned int irq)
+{
+ void *mapbase = get_irq_chipdata(irq);
+ unsigned int r;
+ r = locomo_readl(mapbase + LOCOMO_GIE);
+ r |= (0x0001 << (irq - LOCOMO_IRQ_GPIO_START));
+ locomo_writel(r, mapbase + LOCOMO_GIE);
+}
+
+static struct irqchip locomo_gpio_chip = {
+ .ack = locomo_gpio_ack_irq,
+ .mask = locomo_gpio_mask_irq,
+ .unmask = locomo_gpio_unmask_irq,
+};
+
+static void locomo_lt_handler(unsigned int irq, struct irqdesc *desc,
+ struct pt_regs *regs)
+{
+ struct irqdesc *d;
+ void *mapbase = get_irq_chipdata(irq);
+
+ if (locomo_readl(mapbase + LOCOMO_LTINT) & 0x0001) {
+ d = irq_desc + LOCOMO_IRQ_LT_START;
+ d->handle(LOCOMO_IRQ_LT_START, d, regs);
+ }
+}
+
+static void locomo_lt_ack_irq(unsigned int irq)
+{
+ void *mapbase = get_irq_chipdata(irq);
+ unsigned int r;
+ r = locomo_readl(mapbase + LOCOMO_LTINT);
+ r &= ~(0x0100 << (irq - LOCOMO_IRQ_LT_START));
+ locomo_writel(r, mapbase + LOCOMO_LTINT);
+}
+
+static void locomo_lt_mask_irq(unsigned int irq)
+{
+ void *mapbase = get_irq_chipdata(irq);
+ unsigned int r;
+ r = locomo_readl(mapbase + LOCOMO_LTINT);
+ r &= ~(0x0010 << (irq - LOCOMO_IRQ_LT_START));
+ locomo_writel(r, mapbase + LOCOMO_LTINT);
+}
+
+static void locomo_lt_unmask_irq(unsigned int irq)
+{
+ void *mapbase = get_irq_chipdata(irq);
+ unsigned int r;
+ r = locomo_readl(mapbase + LOCOMO_LTINT);
+ r |= (0x0010 << (irq - LOCOMO_IRQ_LT_START));
+ locomo_writel(r, mapbase + LOCOMO_LTINT);
+}
+
+static struct irqchip locomo_lt_chip = {
+ .ack = locomo_lt_ack_irq,
+ .mask = locomo_lt_mask_irq,
+ .unmask = locomo_lt_unmask_irq,
+};
+
+static void locomo_spi_handler(unsigned int irq, struct irqdesc *desc,
+ struct pt_regs *regs)
+{
+ int req, i;
+ struct irqdesc *d;
+ void *mapbase = get_irq_chipdata(irq);
+
+ req = locomo_readl(mapbase + LOCOMO_SPIIR) & 0x000F;
+ if (req) {
+ irq = LOCOMO_IRQ_SPI_START;
+ d = irq_desc + irq;
+
+ for (i = 0; i <= 3; i++, irq++, d++) {
+ if (req & (0x0001 << i)) {
+ d->handle(irq, d, regs);
+ }
+ }
+ }
+}
+
+static void locomo_spi_ack_irq(unsigned int irq)
+{
+ void *mapbase = get_irq_chipdata(irq);
+ unsigned int r;
+ r = locomo_readl(mapbase + LOCOMO_SPIWE);
+ r |= (0x0001 << (irq - LOCOMO_IRQ_SPI_START));
+ locomo_writel(r, mapbase + LOCOMO_SPIWE);
+
+ r = locomo_readl(mapbase + LOCOMO_SPIIS);
+ r &= ~(0x0001 << (irq - LOCOMO_IRQ_SPI_START));
+ locomo_writel(r, mapbase + LOCOMO_SPIIS);
+
+ r = locomo_readl(mapbase + LOCOMO_SPIWE);
+ r &= ~(0x0001 << (irq - LOCOMO_IRQ_SPI_START));
+ locomo_writel(r, mapbase + LOCOMO_SPIWE);
+}
+
+static void locomo_spi_mask_irq(unsigned int irq)
+{
+ void *mapbase = get_irq_chipdata(irq);
+ unsigned int r;
+ r = locomo_readl(mapbase + LOCOMO_SPIIE);
+ r &= ~(0x0001 << (irq - LOCOMO_IRQ_SPI_START));
+ locomo_writel(r, mapbase + LOCOMO_SPIIE);
+}
+
+static void locomo_spi_unmask_irq(unsigned int irq)
+{
+ void *mapbase = get_irq_chipdata(irq);
+ unsigned int r;
+ r = locomo_readl(mapbase + LOCOMO_SPIIE);
+ r |= (0x0001 << (irq - LOCOMO_IRQ_SPI_START));
+ locomo_writel(r, mapbase + LOCOMO_SPIIE);
+}
+
+static struct irqchip locomo_spi_chip = {
+ .ack = locomo_spi_ack_irq,
+ .mask = locomo_spi_mask_irq,
+ .unmask = locomo_spi_unmask_irq,
+};
+
+static void locomo_setup_irq(struct locomo *lchip)
+{
+ int irq;
+ void *irqbase = lchip->base;
+
+ /*
+ * Install handler for IRQ_LOCOMO_HW.
+ */
+ set_irq_type(lchip->irq, IRQT_FALLING);
+ set_irq_chipdata(lchip->irq, irqbase);
+ set_irq_chained_handler(lchip->irq, locomo_handler);
+
+ /* Install handlers for IRQ_LOCOMO_*_BASE */
+ set_irq_chip(IRQ_LOCOMO_KEY_BASE, &locomo_chip);
+ set_irq_chipdata(IRQ_LOCOMO_KEY_BASE, irqbase);
+ set_irq_chained_handler(IRQ_LOCOMO_KEY_BASE, locomo_key_handler);
+ set_irq_flags(IRQ_LOCOMO_KEY_BASE, IRQF_VALID | IRQF_PROBE);
+
+ set_irq_chip(IRQ_LOCOMO_GPIO_BASE, &locomo_chip);
+ set_irq_chipdata(IRQ_LOCOMO_GPIO_BASE, irqbase);
+ set_irq_chained_handler(IRQ_LOCOMO_GPIO_BASE, locomo_gpio_handler);
+ set_irq_flags(IRQ_LOCOMO_GPIO_BASE, IRQF_VALID | IRQF_PROBE);
+
+ set_irq_chip(IRQ_LOCOMO_LT_BASE, &locomo_chip);
+ set_irq_chipdata(IRQ_LOCOMO_LT_BASE, irqbase);
+ set_irq_chained_handler(IRQ_LOCOMO_LT_BASE, locomo_lt_handler);
+ set_irq_flags(IRQ_LOCOMO_LT_BASE, IRQF_VALID | IRQF_PROBE);
+
+ set_irq_chip(IRQ_LOCOMO_SPI_BASE, &locomo_chip);
+ set_irq_chipdata(IRQ_LOCOMO_SPI_BASE, irqbase);
+ set_irq_chained_handler(IRQ_LOCOMO_SPI_BASE, locomo_spi_handler);
+ set_irq_flags(IRQ_LOCOMO_SPI_BASE, IRQF_VALID | IRQF_PROBE);
+
+ /* install handlers for IRQ_LOCOMO_KEY_BASE generated interrupts */
+ set_irq_chip(LOCOMO_IRQ_KEY_START, &locomo_key_chip);
+ set_irq_chipdata(LOCOMO_IRQ_KEY_START, irqbase);
+ set_irq_handler(LOCOMO_IRQ_KEY_START, do_edge_IRQ);
+ set_irq_flags(LOCOMO_IRQ_KEY_START, IRQF_VALID | IRQF_PROBE);
+
+ /* install handlers for IRQ_LOCOMO_GPIO_BASE generated interrupts */
+ for (irq = LOCOMO_IRQ_GPIO_START; irq < LOCOMO_IRQ_GPIO_START + 16; irq++) {
+ set_irq_chip(irq, &locomo_gpio_chip);
+ set_irq_chipdata(irq, irqbase);
+ set_irq_handler(irq, do_edge_IRQ);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ }
+
+ /* install handlers for IRQ_LOCOMO_LT_BASE generated interrupts */
+ set_irq_chip(LOCOMO_IRQ_LT_START, &locomo_lt_chip);
+ set_irq_chipdata(LOCOMO_IRQ_LT_START, irqbase);
+ set_irq_handler(LOCOMO_IRQ_LT_START, do_edge_IRQ);
+ set_irq_flags(LOCOMO_IRQ_LT_START, IRQF_VALID | IRQF_PROBE);
+
+ /* install handlers for IRQ_LOCOMO_SPI_BASE generated interrupts */
+ for (irq = LOCOMO_IRQ_SPI_START; irq < LOCOMO_IRQ_SPI_START + 3; irq++) {
+ set_irq_chip(irq, &locomo_spi_chip);
+ set_irq_chipdata(irq, irqbase);
+ set_irq_handler(irq, do_edge_IRQ);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ }
+}
+
+
+static void locomo_dev_release(struct device *_dev)
+{
+ struct locomo_dev *dev = LOCOMO_DEV(_dev);
+
+ release_resource(&dev->res);
+ kfree(dev);
+}
+
+static int
+locomo_init_one_child(struct locomo *lchip, struct resource *parent,
+ struct locomo_dev_info *info)
+{
+ struct locomo_dev *dev;
+ int ret;
+
+ dev = kmalloc(sizeof(struct locomo_dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ memset(dev, 0, sizeof(struct locomo_dev));
+
+ strncpy(dev->dev.bus_id,info->name,sizeof(dev->dev.bus_id));
+ /*
+ * If the parent device has a DMA mask associated with it,
+ * propagate it down to the children.
+ */
+ if (lchip->dev->dma_mask) {
+ dev->dma_mask = *lchip->dev->dma_mask;
+ dev->dev.dma_mask = &dev->dma_mask;
+ }
+
+ dev->devid = info->devid;
+ dev->dev.parent = lchip->dev;
+ dev->dev.bus = &locomo_bus_type;
+ dev->dev.release = locomo_dev_release;
+ dev->dev.coherent_dma_mask = lchip->dev->coherent_dma_mask;
+ dev->res.start = lchip->phys + info->offset;
+ dev->res.end = dev->res.start + info->length;
+ dev->res.name = dev->dev.bus_id;
+ dev->res.flags = IORESOURCE_MEM;
+ dev->mapbase = lchip->base + info->offset;
+ memmove(dev->irq, info->irq, sizeof(dev->irq));
+
+ if (info->length) {
+ ret = request_resource(parent, &dev->res);
+ if (ret) {
+ printk("LoCoMo: failed to allocate resource for %s\n",
+ dev->res.name);
+ goto out;
+ }
+ }
+
+ ret = device_register(&dev->dev);
+ if (ret) {
+ release_resource(&dev->res);
+ out:
+ kfree(dev);
+ }
+ return ret;
+}
+
+/**
+ * locomo_probe - probe for a single LoCoMo chip.
+ * @phys_addr: physical address of device.
+ *
+ * Probe for a LoCoMo chip. This must be called
+ * before any other locomo-specific code.
+ *
+ * Returns:
+ * %-ENODEV device not found.
+ * %-EBUSY physical address already marked in-use.
+ * %0 successful.
+ */
+static int
+__locomo_probe(struct device *me, struct resource *mem, int irq)
+{
+ struct locomo *lchip;
+ unsigned long r;
+ int i, ret = -ENODEV;
+
+ lchip = kmalloc(sizeof(struct locomo), GFP_KERNEL);
+ if (!lchip)
+ return -ENOMEM;
+
+ memset(lchip, 0, sizeof(struct locomo));
+
+ lchip->dev = me;
+ dev_set_drvdata(lchip->dev, lchip);
+
+ lchip->phys = mem->start;
+ lchip->irq = irq;
+
+ /*
+ * Map the whole region. This also maps the
+ * registers for our children.
+ */
+ lchip->base = ioremap(mem->start, PAGE_SIZE);
+ if (!lchip->base) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* locomo initialize */
+ locomo_writel(0, lchip->base + LOCOMO_ICR);
+ /* KEYBOARD */
+ locomo_writel(0, lchip->base + LOCOMO_KIC);
+
+ /* GPIO */
+ locomo_writel(0, lchip->base + LOCOMO_GPO);
+ locomo_writel( (LOCOMO_GPIO(2) | LOCOMO_GPIO(3) | LOCOMO_GPIO(13) | LOCOMO_GPIO(14))
+ , lchip->base + LOCOMO_GPE);
+ locomo_writel( (LOCOMO_GPIO(2) | LOCOMO_GPIO(3) | LOCOMO_GPIO(13) | LOCOMO_GPIO(14))
+ , lchip->base + LOCOMO_GPD);
+ locomo_writel(0, lchip->base + LOCOMO_GIE);
+
+ /* FrontLight */
+ locomo_writel(0, lchip->base + LOCOMO_ALS);
+ locomo_writel(0, lchip->base + LOCOMO_ALD);
+ /* Longtime timer */
+ locomo_writel(0, lchip->base + LOCOMO_LTINT);
+ /* SPI */
+ locomo_writel(0, lchip->base + LOCOMO_SPIIE);
+
+ locomo_writel(6 + 8 + 320 + 30 - 10, lchip->base + LOCOMO_ASD);
+ r = locomo_readl(lchip->base + LOCOMO_ASD);
+ r |= 0x8000;
+ locomo_writel(r, lchip->base + LOCOMO_ASD);
+
+ locomo_writel(6 + 8 + 320 + 30 - 10 - 128 + 4, lchip->base + LOCOMO_HSD);
+ r = locomo_readl(lchip->base + LOCOMO_HSD);
+ r |= 0x8000;
+ locomo_writel(r, lchip->base + LOCOMO_HSD);
+
+ locomo_writel(128 / 8, lchip->base + LOCOMO_HSC);
+
+ /* XON */
+ locomo_writel(0x80, lchip->base + LOCOMO_TADC);
+ udelay(1000);
+ /* CLK9MEN */
+ r = locomo_readl(lchip->base + LOCOMO_TADC);
+ r |= 0x10;
+ locomo_writel(r, lchip->base + LOCOMO_TADC);
+ udelay(100);
+
+ /* init DAC */
+ r = locomo_readl(lchip->base + LOCOMO_DAC);
+ r |= LOCOMO_DAC_SCLOEB | LOCOMO_DAC_SDAOEB;
+ locomo_writel(r, lchip->base + LOCOMO_DAC);
+
+ r = locomo_readl(lchip->base + LOCOMO_VER);
+ printk(KERN_INFO "LoCoMo Chip: %lu%lu\n", (r >> 8), (r & 0xff));
+
+ /*
+ * The interrupt controller must be initialised before any
+ * other device to ensure that the interrupts are available.
+ */
+ if (lchip->irq != NO_IRQ)
+ locomo_setup_irq(lchip);
+
+ for (i = 0; i < ARRAY_SIZE(locomo_devices); i++)
+ locomo_init_one_child(lchip, mem, &locomo_devices[i]);
+
+ return 0;
+
+ out:
+ kfree(lchip);
+ return ret;
+}
+
+static void __locomo_remove(struct locomo *lchip)
+{
+ struct list_head *l, *n;
+
+ list_for_each_safe(l, n, &lchip->dev->children) {
+ struct device *d = list_to_dev(l);
+
+ device_unregister(d);
+ }
+
+ if (lchip->irq != NO_IRQ) {
+ set_irq_chained_handler(lchip->irq, NULL);
+ set_irq_data(lchip->irq, NULL);
+ }
+
+ iounmap(lchip->base);
+ kfree(lchip);
+}
+
+static int locomo_probe(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct resource *mem = NULL, *irq = NULL;
+ int i;
+
+ for (i = 0; i < pdev->num_resources; i++) {
+ if (pdev->resource[i].flags & IORESOURCE_MEM)
+ mem = &pdev->resource[i];
+ if (pdev->resource[i].flags & IORESOURCE_IRQ)
+ irq = &pdev->resource[i];
+ }
+
+ return __locomo_probe(dev, mem, irq ? irq->start : NO_IRQ);
+}
+
+static int locomo_remove(struct device *dev)
+{
+ struct locomo *lchip = dev_get_drvdata(dev);
+
+ if (lchip) {
+ __locomo_remove(lchip);
+ dev_set_drvdata(dev, NULL);
+
+ kfree(dev->saved_state);
+ dev->saved_state = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * Not sure if this should be on the system bus or not yet.
+ * We really want some way to register a system device at
+ * the per-machine level, and then have this driver pick
+ * up the registered devices.
+ */
+static struct device_driver locomo_device_driver = {
+ .name = "locomo",
+ .bus = &platform_bus_type,
+ .probe = locomo_probe,
+ .remove = locomo_remove,
+};
+
+/*
+ * Get the parent device driver (us) structure
+ * from a child function device
+ */
+static inline struct locomo *locomo_chip_driver(struct locomo_dev *ldev)
+{
+ return (struct locomo *)dev_get_drvdata(ldev->dev.parent);
+}
+
+/*
+ * LoCoMo "Register Access Bus."
+ *
+ * We model this as a regular bus type, and hang devices directly
+ * off this.
+ */
+static int locomo_match(struct device *_dev, struct device_driver *_drv)
+{
+ struct locomo_dev *dev = LOCOMO_DEV(_dev);
+ struct locomo_driver *drv = LOCOMO_DRV(_drv);
+
+ return dev->devid == drv->devid;
+}
+
+static int locomo_bus_suspend(struct device *dev, u32 state)
+{
+ struct locomo_dev *ldev = LOCOMO_DEV(dev);
+ struct locomo_driver *drv = LOCOMO_DRV(dev->driver);
+ int ret = 0;
+
+ if (drv && drv->suspend)
+ ret = drv->suspend(ldev, state);
+ return ret;
+}
+
+static int locomo_bus_resume(struct device *dev)
+{
+ struct locomo_dev *ldev = LOCOMO_DEV(dev);
+ struct locomo_driver *drv = LOCOMO_DRV(dev->driver);
+ int ret = 0;
+
+ if (drv && drv->resume)
+ ret = drv->resume(ldev);
+ return ret;
+}
+
+static int locomo_bus_probe(struct device *dev)
+{
+ struct locomo_dev *ldev = LOCOMO_DEV(dev);
+ struct locomo_driver *drv = LOCOMO_DRV(dev->driver);
+ int ret = -ENODEV;
+
+ if (drv->probe)
+ ret = drv->probe(ldev);
+ return ret;
+}
+
+static int locomo_bus_remove(struct device *dev)
+{
+ struct locomo_dev *ldev = LOCOMO_DEV(dev);
+ struct locomo_driver *drv = LOCOMO_DRV(dev->driver);
+ int ret = 0;
+
+ if (drv->remove)
+ ret = drv->remove(ldev);
+ return ret;
+}
+
+struct bus_type locomo_bus_type = {
+ .name = "locomo-bus",
+ .match = locomo_match,
+ .suspend = locomo_bus_suspend,
+ .resume = locomo_bus_resume,
+};
+
+int locomo_driver_register(struct locomo_driver *driver)
+{
+ driver->drv.probe = locomo_bus_probe;
+ driver->drv.remove = locomo_bus_remove;
+ driver->drv.bus = &locomo_bus_type;
+ return driver_register(&driver->drv);
+}
+
+void locomo_driver_unregister(struct locomo_driver *driver)
+{
+ driver_unregister(&driver->drv);
+}
+
+static int __init locomo_init(void)
+{
+ int ret = bus_register(&locomo_bus_type);
+ if (ret == 0)
+ driver_register(&locomo_device_driver);
+ return ret;
+}
+
+static void __exit locomo_exit(void)
+{
+ driver_unregister(&locomo_device_driver);
+ bus_unregister(&locomo_bus_type);
+}
+
+module_init(locomo_init);
+module_exit(locomo_exit);
+
+MODULE_DESCRIPTION("Sharp LoCoMo core driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Lenz <jelenz@students.wisc.edu>");
+
+EXPORT_SYMBOL(locomo_driver_register);
+EXPORT_SYMBOL(locomo_driver_unregister);
--- /dev/null
+/*
+ * linux/arch/arm/common/time-acorn.c
+ *
+ * Copyright (c) 1996-2000 Russell King.
+ *
+ * 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.
+ *
+ * Changelog:
+ * 24-Sep-1996 RMK Created
+ * 10-Oct-1996 RMK Brought up to date with arch-sa110eval
+ * 04-Dec-1997 RMK Updated for new arch/arm/time.c
+ * 13=Jun-2004 DS Moved to arch/arm/common b/c shared w/CLPS7500
+ */
+#include <linux/timex.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/hardware/ioc.h>
+
+#include <asm/mach/time.h>
+
+static unsigned long ioctime_gettimeoffset(void)
+{
+ unsigned int count1, count2, status;
+ long offset;
+
+ ioc_writeb (0, IOC_T0LATCH);
+ barrier ();
+ count1 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
+ barrier ();
+ status = ioc_readb(IOC_IRQREQA);
+ barrier ();
+ ioc_writeb (0, IOC_T0LATCH);
+ barrier ();
+ count2 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
+
+ offset = count2;
+ if (count2 < count1) {
+ /*
+ * We have not had an interrupt between reading count1
+ * and count2.
+ */
+ if (status & (1 << 5))
+ offset -= LATCH;
+ } else if (count2 > count1) {
+ /*
+ * We have just had another interrupt between reading
+ * count1 and count2.
+ */
+ offset -= LATCH;
+ }
+
+ offset = (LATCH - offset) * (tick_nsec / 1000);
+ return (offset + LATCH/2) / LATCH;
+}
+
+void __init ioctime_init(void)
+{
+ ioc_writeb(LATCH & 255, IOC_T0LTCHL);
+ ioc_writeb(LATCH >> 8, IOC_T0LTCHH);
+ ioc_writeb(0, IOC_T0GO);
+
+ gettimeoffset = ioctime_gettimeoffset;
+}
# CONFIG_LLC is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
#
CONFIG_SWAP=y
CONFIG_SYSVIPC=y
+# CONFIG_POSIX_MQUEUE is not set
# CONFIG_BSD_PROCESS_ACCT is not set
CONFIG_SYSCTL=y
-CONFIG_LOG_BUF_SHIFT=16
+# CONFIG_AUDIT is not set
+CONFIG_LOG_BUF_SHIFT=17
+# CONFIG_HOTPLUG is not set
# CONFIG_IKCONFIG is not set
# CONFIG_EMBEDDED is not set
CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
CONFIG_FUTEX=y
CONFIG_EPOLL=y
CONFIG_IOSCHED_NOOP=y
CONFIG_IOSCHED_AS=y
CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
#
# Loadable module support
#
# System Type
#
-# CONFIG_ARCH_ADIFCC is not set
-# CONFIG_ARCH_ANAKIN is not set
# CONFIG_ARCH_CLPS7500 is not set
# CONFIG_ARCH_CLPS711X is not set
# CONFIG_ARCH_CO285 is not set
-# CONFIG_ARCH_PXA is not set
# CONFIG_ARCH_EBSA110 is not set
# CONFIG_ARCH_CAMELOT is not set
# CONFIG_ARCH_FOOTBRIDGE is not set
# CONFIG_ARCH_INTEGRATOR is not set
# CONFIG_ARCH_IOP3XX is not set
+# CONFIG_ARCH_IXP4XX is not set
# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_PXA is not set
# CONFIG_ARCH_RPC is not set
# CONFIG_ARCH_SA1100 is not set
-# CONFIG_ARCH_SHARK is not set
CONFIG_ARCH_S3C2410=y
-
-#
-# CLPS711X/EP721X Implementations
-#
-
-#
-# Epxa10db
-#
-
-#
-# Footbridge Implementations
-#
-
-#
-# IOP3xx Implementation Options
-#
-# CONFIG_ARCH_IOP310 is not set
-# CONFIG_ARCH_IOP321 is not set
-
-#
-# IOP3xx Chipset Features
-#
-
-#
-# Intel PXA250/210 Implementations
-#
-
-#
-# SA11x0 Implementations
-#
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_VERSATILE_PB is not set
#
# S3C2410 Implementations
#
CONFIG_ARCH_BAST=y
+# CONFIG_ARCH_H1940 is not set
+# CONFIG_ARCH_SMDK2410 is not set
+CONFIG_MACH_VR1000=y
#
# Processor Type
# General setup
#
# CONFIG_ZBOOT_ROM is not set
-CONFIG_ZBOOT_ROM_TEXT=0
-CONFIG_ZBOOT_ROM_BSS=0
-# CONFIG_HOTPLUG is not set
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
#
# At least one math emulation must be selected
CONFIG_FPE_NWFPE=y
CONFIG_FPE_NWFPE_XP=y
# CONFIG_FPE_FASTFPE is not set
+# CONFIG_VFP is not set
CONFIG_BINFMT_ELF=y
CONFIG_BINFMT_AOUT=y
# CONFIG_BINFMT_MISC is not set
#
# Generic Driver Options
#
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_DEBUG_DRIVER is not set
# CONFIG_PM is not set
# CONFIG_PREEMPT is not set
# CONFIG_ARTHUR is not set
+CONFIG_S3C2410_DMA=y
+# CONFIG_S3C2410_DMA_DEBUG is not set
CONFIG_CMDLINE="root=/dev/hda1 ro init=/bin/bash console=ttySAC0"
CONFIG_ALIGNMENT_TRAP=y
CONFIG_PARPORT=y
CONFIG_PARPORT_PC=y
CONFIG_PARPORT_PC_CML1=y
-# CONFIG_PARPORT_SERIAL is not set
CONFIG_PARPORT_PC_FIFO=y
CONFIG_PARPORT_PC_SUPERIO=y
# CONFIG_PARPORT_ARC is not set
#
# Plug and Play support
#
-# CONFIG_PNP is not set
#
# Block devices
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
# CONFIG_ARPD is not set
-# CONFIG_INET_ECN is not set
# CONFIG_SYN_COOKIES is not set
# CONFIG_INET_AH is not set
# CONFIG_INET_ESP is not set
# CONFIG_INET_IPCOMP is not set
# CONFIG_IPV6 is not set
-# CONFIG_DECNET is not set
-# CONFIG_BRIDGE is not set
# CONFIG_NETFILTER is not set
#
# SCTP Configuration (EXPERIMENTAL)
#
-CONFIG_IPV6_SCTP__=y
# CONFIG_IP_SCTP is not set
# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
# CONFIG_LLC2 is not set
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# QoS and/or fair queueing
#
# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE is not set
#
# Network testing
#
# CONFIG_NET_PKTGEN is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
CONFIG_NETDEVICES=y
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
#
# Ethernet (10000 Mbit)
#
-# CONFIG_PLIP is not set
-# CONFIG_PPP is not set
-# CONFIG_SLIP is not set
#
-# Wireless LAN (non-hamradio)
+# Token Ring devices
#
-# CONFIG_NET_RADIO is not set
-# CONFIG_HOSTAP is not set
#
-# Token Ring devices
+# Wireless LAN (non-hamradio)
#
-# CONFIG_SHAPER is not set
+# CONFIG_NET_RADIO is not set
#
# Wan interfaces
#
# CONFIG_WAN is not set
-
-#
-# Amateur Radio support
-#
-# CONFIG_HAMRADIO is not set
-
-#
-# IrDA (infrared) support
-#
-# CONFIG_IRDA is not set
-
-#
-# Bluetooth support
-#
-# CONFIG_BT is not set
+# CONFIG_PLIP is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
#
# ATA/ATAPI/MFM/RLL support
#
# Please see Documentation/ide.txt for help/info on IDE drives
#
+# CONFIG_BLK_DEV_IDE_SATA is not set
CONFIG_BLK_DEV_IDEDISK=y
# CONFIG_IDEDISK_MULTI_MODE is not set
-# CONFIG_IDEDISK_STROKE is not set
CONFIG_BLK_DEV_IDECD=y
CONFIG_BLK_DEV_IDETAPE=m
CONFIG_BLK_DEV_IDEFLOPPY=m
#
# IDE chipset support/bugfixes
#
-CONFIG_BLK_DEV_IDE_BAST=y
+CONFIG_IDE_GENERIC=y
+# CONFIG_IDE_ARM is not set
# CONFIG_BLK_DEV_IDEDMA is not set
# CONFIG_IDEDMA_AUTO is not set
-# CONFIG_DMA_NONPCI is not set
# CONFIG_BLK_DEV_HD is not set
#
#
# CONFIG_SCSI is not set
+#
+# Fusion MPT device support
+#
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_IEEE1394 is not set
+
#
# I2O device support
#
#
# ISDN subsystem
#
-# CONFIG_ISDN_BOOL is not set
+# CONFIG_ISDN is not set
#
# Input device support
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
# CONFIG_INPUT_JOYDEV is not set
# CONFIG_INPUT_TSDEV is not set
-# CONFIG_INPUT_TSLIBDEV is not set
# CONFIG_INPUT_EVDEV is not set
# CONFIG_INPUT_EVBUG is not set
# CONFIG_GAMEPORT is not set
CONFIG_SOUND_GAMEPORT=y
CONFIG_SERIO=y
-CONFIG_SERIO_I8042=y
+# CONFIG_SERIO_I8042 is not set
CONFIG_SERIO_SERPORT=y
# CONFIG_SERIO_CT82C710 is not set
# CONFIG_SERIO_PARKBD is not set
# Input Device Drivers
#
CONFIG_INPUT_KEYBOARD=y
-CONFIG_KEYBOARD_ATKBD=y
+# CONFIG_KEYBOARD_ATKBD is not set
# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
# CONFIG_KEYBOARD_XTKBD is not set
# CONFIG_KEYBOARD_NEWTON is not set
CONFIG_INPUT_MOUSE=y
CONFIG_MOUSE_PS2=y
-# CONFIG_MOUSE_PS2_SYNAPTICS is not set
# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_VSXXXAA is not set
# CONFIG_INPUT_JOYSTICK is not set
# CONFIG_INPUT_TOUCHSCREEN is not set
# CONFIG_INPUT_MISC is not set
#
CONFIG_SERIAL_S3C2410=y
CONFIG_SERIAL_S3C2410_CONSOLE=y
-# CONFIG_SERIAL_DZ is not set
+# CONFIG_SERIAL_BAST_SIO is not set
CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
CONFIG_UNIX98_PTYS=y
-CONFIG_UNIX98_PTY_COUNT=256
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
CONFIG_PRINTER=y
# CONFIG_LP_CONSOLE is not set
CONFIG_PPDEV=y
# CONFIG_TIPAR is not set
+# CONFIG_QIC02_TAPE is not set
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_NVRAM is not set
+CONFIG_RTC=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+# CONFIG_AGP is not set
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
#
# I2C support
#
# CONFIG_I2C_AMD756 is not set
# CONFIG_I2C_AMD8111 is not set
-# CONFIG_I2C_PHILIPSPAR is not set
+# CONFIG_I2C_ISA is not set
+# CONFIG_I2C_PARPORT is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
# CONFIG_SCx200_ACB is not set
#
-# I2C Hardware Sensors Chip support
+# Hardware Sensors Chip support
#
CONFIG_I2C_SENSOR=m
# CONFIG_SENSORS_ADM1021 is not set
-CONFIG_SENSORS_EEPROM=m
+# CONFIG_SENSORS_ASB100 is not set
+# CONFIG_SENSORS_DS1621 is not set
+# CONFIG_SENSORS_FSCHER is not set
+# CONFIG_SENSORS_GL518SM is not set
# CONFIG_SENSORS_IT87 is not set
CONFIG_SENSORS_LM75=m
CONFIG_SENSORS_LM78=m
+# CONFIG_SENSORS_LM80 is not set
+# CONFIG_SENSORS_LM83 is not set
CONFIG_SENSORS_LM85=m
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_MAX1619 is not set
# CONFIG_SENSORS_VIA686A is not set
# CONFIG_SENSORS_W83781D is not set
+# CONFIG_SENSORS_W83L785TS is not set
+# CONFIG_SENSORS_W83627HF is not set
#
-# L3 serial bus support
-#
-# CONFIG_L3 is not set
-
-#
-# Mice
-#
-# CONFIG_BUSMOUSE is not set
-# CONFIG_QIC02_TAPE is not set
-
-#
-# IPMI
+# Other I2C Chip support
#
-# CONFIG_IPMI_HANDLER is not set
-
-#
-# Watchdog Cards
-#
-# CONFIG_WATCHDOG is not set
-# CONFIG_NVRAM is not set
-CONFIG_RTC=y
-# CONFIG_DTLK is not set
-# CONFIG_R3964 is not set
-# CONFIG_APPLICOM is not set
-
-#
-# Ftape, the floppy tape device driver
-#
-# CONFIG_FTAPE is not set
-# CONFIG_AGP is not set
-# CONFIG_DRM is not set
-# CONFIG_RAW_DRIVER is not set
+CONFIG_SENSORS_EEPROM=m
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_RTC8564 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
#
# Multimedia devices
#
# CONFIG_DVB is not set
-#
-# MMC/SD Card support
-#
-# CONFIG_MMC is not set
-
#
# File systems
#
CONFIG_FAT_FS=y
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
# CONFIG_NTFS_FS is not set
#
# Pseudo filesystems
#
CONFIG_PROC_FS=y
+CONFIG_SYSFS=y
# CONFIG_DEVFS_FS is not set
-CONFIG_DEVPTS_FS=y
# CONFIG_DEVPTS_FS_XATTR is not set
# CONFIG_TMPFS is not set
# CONFIG_HUGETLBFS is not set
# CONFIG_ADFS_FS is not set
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
# CONFIG_BEFS_FS is not set
# CONFIG_BFS_FS is not set
# CONFIG_EFS_FS is not set
CONFIG_LOCKD=y
# CONFIG_EXPORTFS is not set
CONFIG_SUNRPC=y
-# CONFIG_SUNRPC_GSS is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
# CONFIG_SMB_FS is not set
# CONFIG_CIFS is not set
# CONFIG_NCP_FS is not set
# CONFIG_CODA_FS is not set
-# CONFIG_INTERMEZZO_FS is not set
# CONFIG_AFS_FS is not set
#
CONFIG_SOLARIS_X86_PARTITION=y
# CONFIG_UNIXWARE_DISKLABEL is not set
# CONFIG_LDM_PARTITION is not set
-# CONFIG_NEC98_PARTITION is not set
# CONFIG_SGI_PARTITION is not set
# CONFIG_ULTRIX_PARTITION is not set
# CONFIG_SUN_PARTITION is not set
# CONFIG_EFI_PARTITION is not set
-CONFIG_NLS=y
#
# Native Language Support
#
+CONFIG_NLS=y
CONFIG_NLS_DEFAULT="iso8859-1"
# CONFIG_NLS_CODEPAGE_437 is not set
# CONFIG_NLS_CODEPAGE_737 is not set
# CONFIG_NLS_ISO8859_8 is not set
# CONFIG_NLS_CODEPAGE_1250 is not set
# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
# CONFIG_NLS_ISO8859_1 is not set
# CONFIG_NLS_ISO8859_2 is not set
# CONFIG_NLS_ISO8859_3 is not set
# CONFIG_NLS_KOI8_U is not set
# CONFIG_NLS_UTF8 is not set
+#
+# Profiling support
+#
+# CONFIG_PROFILING is not set
+
#
# Graphics support
#
# CONFIG_LOGO is not set
#
-# Misc devices
+# Sound
#
+# CONFIG_SOUND is not set
#
-# Multimedia Capabilities Port drivers
+# Misc devices
#
-# CONFIG_MCP is not set
#
-# Console Switches
+# USB support
#
-# CONFIG_SWITCHES is not set
#
-# USB support
+# USB Gadget Support
#
# CONFIG_USB_GADGET is not set
# CONFIG_DEBUG_BUGVERBOSE is not set
# CONFIG_DEBUG_ERRORS is not set
CONFIG_DEBUG_LL=y
-CONFIG_DEBUG_LL_PRINTK=y
+# CONFIG_DEBUG_ICEDCC is not set
CONFIG_DEBUG_S3C2410_PORT=y
CONFIG_DEBUG_S3C2410_UART=0
#
# Library routines
#
+# CONFIG_CRC_CCITT is not set
CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
CONFIG_ZLIB_INFLATE=y
CONFIG_ZLIB_DEFLATE=y
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_LLC is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
# CONFIG_CPU_IS_SLOW is not set
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_ECONET_AUNUDP is not set
# CONFIG_ECONET_NATIVE is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
CONFIG_ECONET_AUNUDP=y
CONFIG_ECONET_NATIVE=y
CONFIG_WAN_ROUTER=m
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_IKCONFIG is not set
# CONFIG_EMBEDDED is not set
CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
CONFIG_FUTEX=y
CONFIG_EPOLL=y
CONFIG_IOSCHED_NOOP=y
# CONFIG_ARCH_CLPS7500 is not set
# CONFIG_ARCH_CLPS711X is not set
# CONFIG_ARCH_CO285 is not set
-CONFIG_ARCH_PXA=y
# CONFIG_ARCH_EBSA110 is not set
# CONFIG_ARCH_CAMELOT is not set
# CONFIG_ARCH_FOOTBRIDGE is not set
# CONFIG_ARCH_INTEGRATOR is not set
# CONFIG_ARCH_IOP3XX is not set
+# CONFIG_ARCH_IXP4XX is not set
# CONFIG_ARCH_L7200 is not set
+CONFIG_ARCH_PXA=y
# CONFIG_ARCH_RPC is not set
# CONFIG_ARCH_SA1100 is not set
-# CONFIG_ARCH_SHARK is not set
# CONFIG_ARCH_S3C2410 is not set
-# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_SHARK is not set
# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_OMAP is not set
# CONFIG_ARCH_VERSATILE_PB is not set
-#
-# CLPS711X/EP721X Implementations
-#
-
-#
-# Epxa10db
-#
-
-#
-# Footbridge Implementations
-#
-
-#
-# IOP3xx Implementation Options
-#
-# CONFIG_ARCH_IOP310 is not set
-# CONFIG_ARCH_IOP321 is not set
-
-#
-# IOP3xx Chipset Features
-#
-
#
# Intel PXA2xx Implementations
#
CONFIG_PXA27x=y
CONFIG_IWMMXT=y
-#
-# SA11x0 Implementations
-#
-
-#
-# TI OMAP Implementations
-#
-
-#
-# OMAP Core Type
-#
-
-#
-# OMAP Board Type
-#
-
-#
-# OMAP Feature Selections
-#
-
-#
-# S3C2410 Implementations
-#
-
-#
-# LH7A40X Implementations
-#
-
#
# Processor Type
#
CONFIG_FPE_NWFPE=y
# CONFIG_FPE_NWFPE_XP is not set
# CONFIG_FPE_FASTFPE is not set
+# CONFIG_VFP is not set
CONFIG_BINFMT_ELF=y
# CONFIG_BINFMT_AOUT is not set
# CONFIG_BINFMT_MISC is not set
#
# Generic Driver Options
#
+CONFIG_PREVENT_FIRMWARE_BUILD=y
# CONFIG_FW_LOADER is not set
# CONFIG_DEBUG_DRIVER is not set
# CONFIG_PM is not set
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
#
CONFIG_BLK_DEV_IDEDISK=y
# CONFIG_IDEDISK_MULTI_MODE is not set
-# CONFIG_IDEDISK_STROKE is not set
CONFIG_BLK_DEV_IDECS=y
# CONFIG_BLK_DEV_IDECD is not set
# CONFIG_BLK_DEV_IDETAPE is not set
# IDE chipset support/bugfixes
#
# CONFIG_IDE_GENERIC is not set
+# CONFIG_IDE_ARM is not set
# CONFIG_BLK_DEV_IDEDMA is not set
# CONFIG_IDEDMA_AUTO is not set
# CONFIG_BLK_DEV_HD is not set
CONFIG_FAT_FS=y
CONFIG_MSDOS_FS=y
# CONFIG_VFAT_FS is not set
+CONFIG_FAT_DEFAULT_CODEPAGE=437
# CONFIG_NTFS_FS is not set
#
# CONFIG_CIFS is not set
# CONFIG_NCP_FS is not set
# CONFIG_CODA_FS is not set
-# CONFIG_INTERMEZZO_FS is not set
# CONFIG_AFS_FS is not set
#
# CONFIG_NLS_ISO8859_8 is not set
# CONFIG_NLS_CODEPAGE_1250 is not set
# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
CONFIG_NLS_ISO8859_1=y
# CONFIG_NLS_ISO8859_2 is not set
# CONFIG_NLS_ISO8859_3 is not set
#
# Graphics support
#
-# CONFIG_FB is not set
+CONFIG_FB=y
+CONFIG_FB_PXA=y
+# CONFIG_FB_PXA_PARAMETERS is not set
+# CONFIG_FB_VIRTUAL is not set
#
# Console display driver support
# CONFIG_VGA_CONSOLE is not set
# CONFIG_MDA_CONSOLE is not set
CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_PCI_CONSOLE=y
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+
+#
+# Logo configuration
+#
+CONFIG_LOGO=y
+CONFIG_LOGO_LINUX_MONO=y
+CONFIG_LOGO_LINUX_VGA16=y
+CONFIG_LOGO_LINUX_CLUT224=y
#
# Sound
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
#
CONFIG_EXPERIMENTAL=y
# CONFIG_CLEAN_COMPILE is not set
-CONFIG_STANDALONE=y
CONFIG_BROKEN=y
CONFIG_BROKEN_ON_SMP=y
#
CONFIG_SWAP=y
CONFIG_SYSVIPC=y
+# CONFIG_POSIX_MQUEUE is not set
# CONFIG_BSD_PROCESS_ACCT is not set
CONFIG_SYSCTL=y
+# CONFIG_AUDIT is not set
CONFIG_LOG_BUF_SHIFT=16
+# CONFIG_HOTPLUG is not set
# CONFIG_IKCONFIG is not set
# CONFIG_EMBEDDED is not set
CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
CONFIG_FUTEX=y
CONFIG_EPOLL=y
CONFIG_IOSCHED_NOOP=y
CONFIG_IOSCHED_AS=y
CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
#
# Loadable module support
#
# System Type
#
-# CONFIG_ARCH_ADIFCC is not set
-# CONFIG_ARCH_ANAKIN is not set
# CONFIG_ARCH_CLPS7500 is not set
# CONFIG_ARCH_CLPS711X is not set
# CONFIG_ARCH_CO285 is not set
-# CONFIG_ARCH_PXA is not set
# CONFIG_ARCH_EBSA110 is not set
# CONFIG_ARCH_CAMELOT is not set
# CONFIG_ARCH_FOOTBRIDGE is not set
# CONFIG_ARCH_INTEGRATOR is not set
# CONFIG_ARCH_IOP3XX is not set
+# CONFIG_ARCH_IXP4XX is not set
# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_PXA is not set
# CONFIG_ARCH_RPC is not set
# CONFIG_ARCH_SA1100 is not set
-# CONFIG_ARCH_SHARK is not set
CONFIG_ARCH_S3C2410=y
-
-#
-# CLPS711X/EP721X Implementations
-#
-
-#
-# Epxa10db
-#
-
-#
-# Footbridge Implementations
-#
-
-#
-# IOP3xx Implementation Options
-#
-# CONFIG_ARCH_IOP310 is not set
-# CONFIG_ARCH_IOP321 is not set
-
-#
-# IOP3xx Chipset Features
-#
-
-#
-# Intel PXA250/210 Implementations
-#
-
-#
-# SA11x0 Implementations
-#
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_VERSATILE_PB is not set
#
# S3C2410 Implementations
#
CONFIG_ARCH_BAST=y
CONFIG_ARCH_H1940=y
+CONFIG_ARCH_SMDK2410=y
+CONFIG_MACH_VR1000=y
#
# Processor Type
# General setup
#
# CONFIG_ZBOOT_ROM is not set
-CONFIG_ZBOOT_ROM_TEXT=0
-CONFIG_ZBOOT_ROM_BSS=0
-# CONFIG_HOTPLUG is not set
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
#
# At least one math emulation must be selected
CONFIG_FPE_NWFPE=y
CONFIG_FPE_NWFPE_XP=y
# CONFIG_FPE_FASTFPE is not set
+# CONFIG_VFP is not set
CONFIG_BINFMT_ELF=y
CONFIG_BINFMT_AOUT=y
# CONFIG_BINFMT_MISC is not set
#
# Generic Driver Options
#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_DEBUG_DRIVER is not set
# CONFIG_PM is not set
# CONFIG_PREEMPT is not set
# CONFIG_ARTHUR is not set
CONFIG_PARPORT=y
CONFIG_PARPORT_PC=y
CONFIG_PARPORT_PC_CML1=y
-# CONFIG_PARPORT_SERIAL is not set
CONFIG_PARPORT_PC_FIFO=y
CONFIG_PARPORT_PC_SUPERIO=y
# CONFIG_PARPORT_ARC is not set
# CONFIG_MTD_JEDECPROBE is not set
CONFIG_MTD_GEN_PROBE=y
# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
CONFIG_MTD_CFI_INTELEXT=y
# CONFIG_MTD_CFI_AMDSTD is not set
# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
# CONFIG_MTD_RAM is not set
# CONFIG_MTD_ROM is not set
# CONFIG_MTD_ABSENT is not set
# Self-contained MTD device drivers
#
# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
# CONFIG_MTD_MTDRAM is not set
# CONFIG_MTD_BLKMTD is not set
#
# Plug and Play support
#
-# CONFIG_PNP is not set
#
# Block devices
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
# CONFIG_ARPD is not set
-# CONFIG_INET_ECN is not set
# CONFIG_SYN_COOKIES is not set
# CONFIG_INET_AH is not set
# CONFIG_INET_ESP is not set
# CONFIG_INET_IPCOMP is not set
# CONFIG_IPV6 is not set
-# CONFIG_DECNET is not set
-# CONFIG_BRIDGE is not set
# CONFIG_NETFILTER is not set
#
# SCTP Configuration (EXPERIMENTAL)
#
-CONFIG_IPV6_SCTP__=y
# CONFIG_IP_SCTP is not set
# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
# CONFIG_LLC2 is not set
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# QoS and/or fair queueing
#
# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE is not set
#
# Network testing
#
# CONFIG_NET_PKTGEN is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
CONFIG_NETDEVICES=y
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
#
# Ethernet (10000 Mbit)
#
-# CONFIG_PLIP is not set
-# CONFIG_PPP is not set
-# CONFIG_SLIP is not set
#
-# Wireless LAN (non-hamradio)
+# Token Ring devices
#
-# CONFIG_NET_RADIO is not set
-# CONFIG_HOSTAP is not set
#
-# Token Ring devices
+# Wireless LAN (non-hamradio)
#
-# CONFIG_SHAPER is not set
+# CONFIG_NET_RADIO is not set
#
# Wan interfaces
#
# CONFIG_WAN is not set
-
-#
-# Amateur Radio support
-#
-# CONFIG_HAMRADIO is not set
-
-#
-# IrDA (infrared) support
-#
-# CONFIG_IRDA is not set
-
-#
-# Bluetooth support
-#
-# CONFIG_BT is not set
+# CONFIG_PLIP is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
#
# ATA/ATAPI/MFM/RLL support
#
# Please see Documentation/ide.txt for help/info on IDE drives
#
+# CONFIG_BLK_DEV_IDE_SATA is not set
CONFIG_BLK_DEV_IDEDISK=y
# CONFIG_IDEDISK_MULTI_MODE is not set
-# CONFIG_IDEDISK_STROKE is not set
CONFIG_BLK_DEV_IDECD=y
CONFIG_BLK_DEV_IDETAPE=m
CONFIG_BLK_DEV_IDEFLOPPY=m
#
# IDE chipset support/bugfixes
#
-CONFIG_BLK_DEV_IDE_BAST=y
+CONFIG_IDE_GENERIC=y
+# CONFIG_IDE_ARM is not set
# CONFIG_BLK_DEV_IDEDMA is not set
# CONFIG_IDEDMA_AUTO is not set
-# CONFIG_DMA_NONPCI is not set
# CONFIG_BLK_DEV_HD is not set
#
#
# CONFIG_SCSI is not set
+#
+# Fusion MPT device support
+#
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_IEEE1394 is not set
+
#
# I2O device support
#
#
# ISDN subsystem
#
-# CONFIG_ISDN_BOOL is not set
+# CONFIG_ISDN is not set
#
# Input device support
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
# CONFIG_INPUT_JOYDEV is not set
# CONFIG_INPUT_TSDEV is not set
-# CONFIG_INPUT_TSLIBDEV is not set
# CONFIG_INPUT_EVDEV is not set
# CONFIG_INPUT_EVBUG is not set
CONFIG_INPUT_KEYBOARD=y
CONFIG_KEYBOARD_ATKBD=y
# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
# CONFIG_KEYBOARD_XTKBD is not set
# CONFIG_KEYBOARD_NEWTON is not set
CONFIG_INPUT_MOUSE=y
CONFIG_MOUSE_PS2=y
-# CONFIG_MOUSE_PS2_SYNAPTICS is not set
# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_VSXXXAA is not set
# CONFIG_INPUT_JOYSTICK is not set
# CONFIG_INPUT_TOUCHSCREEN is not set
# CONFIG_INPUT_MISC is not set
# CONFIG_DIGI is not set
# CONFIG_MOXA_INTELLIO is not set
# CONFIG_MOXA_SMARTIO is not set
-# CONFIG_ISI is not set
-# CONFIG_SYNCLINK is not set
# CONFIG_SYNCLINKMP is not set
# CONFIG_N_HDLC is not set
# CONFIG_RISCOM8 is not set
CONFIG_SERIAL_S3C2410=y
CONFIG_SERIAL_S3C2410_CONSOLE=y
CONFIG_SERIAL_BAST_SIO=y
-# CONFIG_SERIAL_DZ is not set
CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
CONFIG_UNIX98_PTYS=y
-CONFIG_UNIX98_PTY_COUNT=256
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
CONFIG_PRINTER=y
# CONFIG_LP_CONSOLE is not set
CONFIG_PPDEV=y
# CONFIG_TIPAR is not set
+# CONFIG_QIC02_TAPE is not set
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_NVRAM is not set
+CONFIG_RTC=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_AGP is not set
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
#
# I2C support
#
# CONFIG_I2C_AMD756 is not set
# CONFIG_I2C_AMD8111 is not set
-# CONFIG_I2C_PHILIPSPAR is not set
+# CONFIG_I2C_ISA is not set
+# CONFIG_I2C_PARPORT is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
# CONFIG_SCx200_ACB is not set
#
-# I2C Hardware Sensors Chip support
+# Hardware Sensors Chip support
#
CONFIG_I2C_SENSOR=m
# CONFIG_SENSORS_ADM1021 is not set
-CONFIG_SENSORS_EEPROM=m
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1031 is not set
+# CONFIG_SENSORS_ASB100 is not set
+# CONFIG_SENSORS_DS1621 is not set
+# CONFIG_SENSORS_FSCHER is not set
+# CONFIG_SENSORS_GL518SM is not set
# CONFIG_SENSORS_IT87 is not set
CONFIG_SENSORS_LM75=m
+# CONFIG_SENSORS_LM77 is not set
CONFIG_SENSORS_LM78=m
+# CONFIG_SENSORS_LM80 is not set
+# CONFIG_SENSORS_LM83 is not set
CONFIG_SENSORS_LM85=m
-# CONFIG_SENSORS_VIA686A is not set
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_MAX1619 is not set
# CONFIG_SENSORS_W83781D is not set
+# CONFIG_SENSORS_W83L785TS is not set
+# CONFIG_SENSORS_W83627HF is not set
#
-# L3 serial bus support
-#
-# CONFIG_L3 is not set
-
-#
-# Mice
-#
-# CONFIG_BUSMOUSE is not set
-# CONFIG_QIC02_TAPE is not set
-
-#
-# IPMI
-#
-# CONFIG_IPMI_HANDLER is not set
-
-#
-# Watchdog Cards
-#
-# CONFIG_WATCHDOG is not set
-# CONFIG_NVRAM is not set
-CONFIG_RTC=y
-# CONFIG_DTLK is not set
-# CONFIG_R3964 is not set
-# CONFIG_APPLICOM is not set
-
-#
-# Ftape, the floppy tape device driver
+# Other I2C Chip support
#
-# CONFIG_FTAPE is not set
-# CONFIG_AGP is not set
-# CONFIG_DRM is not set
-# CONFIG_RAW_DRIVER is not set
+CONFIG_SENSORS_EEPROM=m
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_RTC8564 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
#
# Multimedia devices
#
# CONFIG_DVB is not set
-#
-# MMC/SD Card support
-#
-# CONFIG_MMC is not set
-
#
# File systems
#
# Pseudo filesystems
#
CONFIG_PROC_FS=y
+CONFIG_SYSFS=y
# CONFIG_DEVFS_FS is not set
-CONFIG_DEVPTS_FS=y
# CONFIG_DEVPTS_FS_XATTR is not set
# CONFIG_TMPFS is not set
# CONFIG_HUGETLBFS is not set
# CONFIG_ADFS_FS is not set
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
# CONFIG_BEFS_FS is not set
# CONFIG_BFS_FS is not set
# CONFIG_EFS_FS is not set
CONFIG_JFFS_FS=y
CONFIG_JFFS_FS_VERBOSE=0
+# CONFIG_JFFS_PROC_FS is not set
CONFIG_JFFS2_FS=y
CONFIG_JFFS2_FS_DEBUG=0
# CONFIG_JFFS2_FS_NAND is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
# CONFIG_CRAMFS is not set
# CONFIG_VXFS_FS is not set
# CONFIG_HPFS_FS is not set
CONFIG_LOCKD=y
# CONFIG_EXPORTFS is not set
CONFIG_SUNRPC=y
-# CONFIG_SUNRPC_GSS is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
# CONFIG_SMB_FS is not set
# CONFIG_CIFS is not set
# CONFIG_NCP_FS is not set
# CONFIG_CODA_FS is not set
-# CONFIG_INTERMEZZO_FS is not set
# CONFIG_AFS_FS is not set
#
CONFIG_SOLARIS_X86_PARTITION=y
# CONFIG_UNIXWARE_DISKLABEL is not set
# CONFIG_LDM_PARTITION is not set
-# CONFIG_NEC98_PARTITION is not set
# CONFIG_SGI_PARTITION is not set
# CONFIG_ULTRIX_PARTITION is not set
# CONFIG_SUN_PARTITION is not set
# CONFIG_EFI_PARTITION is not set
-CONFIG_NLS=y
#
# Native Language Support
#
+CONFIG_NLS=y
CONFIG_NLS_DEFAULT="iso8859-1"
# CONFIG_NLS_CODEPAGE_437 is not set
# CONFIG_NLS_CODEPAGE_737 is not set
# CONFIG_NLS_ISO8859_8 is not set
# CONFIG_NLS_CODEPAGE_1250 is not set
# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
# CONFIG_NLS_ISO8859_1 is not set
# CONFIG_NLS_ISO8859_2 is not set
# CONFIG_NLS_ISO8859_3 is not set
# CONFIG_NLS_KOI8_U is not set
# CONFIG_NLS_UTF8 is not set
+#
+# Profiling support
+#
+# CONFIG_PROFILING is not set
+
#
# Graphics support
#
# CONFIG_LOGO is not set
#
-# Misc devices
+# Sound
#
+# CONFIG_SOUND is not set
#
-# Multimedia Capabilities Port drivers
+# Misc devices
#
-# CONFIG_MCP is not set
#
-# Console Switches
+# USB support
#
-# CONFIG_SWITCHES is not set
#
-# USB support
+# USB Gadget Support
#
# CONFIG_USB_GADGET is not set
# CONFIG_DEBUG_BUGVERBOSE is not set
# CONFIG_DEBUG_ERRORS is not set
CONFIG_DEBUG_LL=y
-CONFIG_DEBUG_LL_PRINTK=y
# CONFIG_DEBUG_ICEDCC is not set
CONFIG_DEBUG_S3C2410_PORT=y
CONFIG_DEBUG_S3C2410_UART=0
#
# Library routines
#
+# CONFIG_CRC_CCITT is not set
CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
CONFIG_ZLIB_INFLATE=y
CONFIG_ZLIB_DEFLATE=y
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
return err;
}
-static ssize_t apm_read(struct file *fp, char *buf, size_t count, loff_t *ppos)
+static ssize_t apm_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos)
{
struct apm_user *as = fp->private_data;
apm_event_t event;
ldr r0, [sp, #S_PSR] @ Get calling cpsr
sub lr, lr, #4
str lr, [r8]
- msr spsr, r0
+ msr spsr_cxsf, r0
ldmia sp, {r0 - lr}^ @ Get calling r0 - lr
mov r0, r0
ldr lr, [sp, #S_PC] @ Get PC
ldr r1, [sp, #S_PSR] @ Get calling cpsr
disable_irq ip @ disable IRQs
ldr lr, [sp, #S_PC]! @ Get PC
- msr spsr, r1 @ save in spsr_svc
+ msr spsr_cxsf, r1 @ save in spsr_svc
ldmdb sp, {r0 - lr}^ @ Get calling r0 - lr
mov r0, r0
add sp, sp, #S_FRAME_SIZE - S_PC
.macro fast_restore_user_regs
ldr r1, [sp, #S_OFF + S_PSR] @ get calling cpsr
ldr lr, [sp, #S_OFF + S_PC]! @ get pc
- msr spsr, r1 @ save in spsr_svc
+ msr spsr_cxsf, r1 @ save in spsr_svc
ldmdb sp, {r1 - lr}^ @ get calling r1 - lr
mov r0, r0
add sp, sp, #S_FRAME_SIZE - S_PC
.macro slow_restore_user_regs
ldr r1, [sp, #S_PSR] @ get calling cpsr
ldr lr, [sp, #S_PC]! @ get pc
- msr spsr, r1 @ save in spsr_svc
+ msr spsr_cxsf, r1 @ save in spsr_svc
ldmdb sp, {r0 - lr}^ @ get calling r1 - lr
mov r0, r0
add sp, sp, #S_FRAME_SIZE - S_PC
* Copy data from IO memory space to "real" memory space.
* This needs to be optimized.
*/
-void _memcpy_fromio(void * to, unsigned long from, size_t count)
+void _memcpy_fromio(void *to, unsigned long from, size_t count)
{
+ unsigned char *t = to;
while (count) {
count--;
- *(char *) to = readb(from);
- ((char *) to)++;
+ *t = readb(from);
+ t++;
from++;
}
}
* Copy data from "real" memory space to IO memory space.
* This needs to be optimized.
*/
-void _memcpy_toio(unsigned long to, const void * from, size_t count)
+void _memcpy_toio(unsigned long to, const void *from, size_t count)
{
+ const unsigned char *f = from;
while (count) {
count--;
- writeb(*(char *) from, to);
- ((char *) from)++;
+ writeb(*f, to);
+ f++;
to++;
}
}
#define CPSR2SPSR(rt) \
mrs rt, cpsr; \
- msr spsr, rt
+ msr spsr_cxsf, rt
@ Purpose: call an expansion card loader to read bytes.
@ Proto : char read_loader(int offset, char *card_base, char *loader);
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-ebsa285/time.h
+ *
+ * Copyright (C) 1998 Russell King.
+ * Copyright (C) 1998 Phil Blundell
+ *
+ * CATS has a real-time clock, though the evaluation board doesn't.
+ *
+ * Changelog:
+ * 21-Mar-1998 RMK Created
+ * 27-Aug-1998 PJB CATS support
+ * 28-Dec-1998 APH Made leds optional
+ * 20-Jan-1999 RMK Started merge of EBSA285, CATS and NetWinder
+ * 16-Mar-1999 RMK More support for EBSA285-like machines with RTCs in
+ */
+
+#define RTC_PORT(x) (rtc_base+(x))
+#define RTC_ALWAYS_BCD 0
+
+#include <linux/timex.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/mc146818rtc.h>
+#include <linux/bcd.h>
+
+#include <asm/hardware/dec21285.h>
+
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/leds.h>
+#include <asm/mach-types.h>
+#include <asm/io.h>
+#include <asm/hardware/clps7111.h>
+
+#include <asm/mach/time.h>
+
+static int rtc_base;
+
+#define mSEC_10_from_14 ((14318180 + 100) / 200)
+
+static unsigned long isa_gettimeoffset(void)
+{
+ int count;
+
+ static int count_p = (mSEC_10_from_14/6); /* for the first call after boot */
+ static unsigned long jiffies_p = 0;
+
+ /*
+ * cache volatile jiffies temporarily; we have IRQs turned off.
+ */
+ unsigned long jiffies_t;
+
+ /* timer count may underflow right here */
+ outb_p(0x00, 0x43); /* latch the count ASAP */
+
+ count = inb_p(0x40); /* read the latched count */
+
+ /*
+ * We do this guaranteed double memory access instead of a _p
+ * postfix in the previous port access. Wheee, hackady hack
+ */
+ jiffies_t = jiffies;
+
+ count |= inb_p(0x40) << 8;
+
+ /* Detect timer underflows. If we haven't had a timer tick since
+ the last time we were called, and time is apparently going
+ backwards, the counter must have wrapped during this routine. */
+ if ((jiffies_t == jiffies_p) && (count > count_p))
+ count -= (mSEC_10_from_14/6);
+ else
+ jiffies_p = jiffies_t;
+
+ count_p = count;
+
+ count = (((mSEC_10_from_14/6)-1) - count) * (tick_nsec / 1000);
+ count = (count + (mSEC_10_from_14/6)/2) / (mSEC_10_from_14/6);
+
+ return count;
+}
+
+static irqreturn_t
+isa_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ timer_tick(regs);
+
+ return IRQ_HANDLED;
+}
+
+static unsigned long __init get_isa_cmos_time(void)
+{
+ unsigned int year, mon, day, hour, min, sec;
+ int i;
+
+ // check to see if the RTC makes sense.....
+ if ((CMOS_READ(RTC_VALID) & RTC_VRT) == 0)
+ return mktime(1970, 1, 1, 0, 0, 0);
+
+ /* The Linux interpretation of the CMOS clock register contents:
+ * When the Update-In-Progress (UIP) flag goes from 1 to 0, the
+ * RTC registers show the second which has precisely just started.
+ * Let's hope other operating systems interpret the RTC the same way.
+ */
+ /* read RTC exactly on falling edge of update flag */
+ for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */
+ if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
+ break;
+
+ for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */
+ if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
+ break;
+
+ do { /* Isn't this overkill ? UIP above should guarantee consistency */
+ sec = CMOS_READ(RTC_SECONDS);
+ min = CMOS_READ(RTC_MINUTES);
+ hour = CMOS_READ(RTC_HOURS);
+ day = CMOS_READ(RTC_DAY_OF_MONTH);
+ mon = CMOS_READ(RTC_MONTH);
+ year = CMOS_READ(RTC_YEAR);
+ } while (sec != CMOS_READ(RTC_SECONDS));
+
+ if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+ BCD_TO_BIN(sec);
+ BCD_TO_BIN(min);
+ BCD_TO_BIN(hour);
+ BCD_TO_BIN(day);
+ BCD_TO_BIN(mon);
+ BCD_TO_BIN(year);
+ }
+ if ((year += 1900) < 1970)
+ year += 100;
+ return mktime(year, mon, day, hour, min, sec);
+}
+
+static int
+set_isa_cmos_time(void)
+{
+ int retval = 0;
+ int real_seconds, real_minutes, cmos_minutes;
+ unsigned char save_control, save_freq_select;
+ unsigned long nowtime = xtime.tv_sec;
+
+ save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */
+ CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
+
+ save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */
+ CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
+
+ cmos_minutes = CMOS_READ(RTC_MINUTES);
+ if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+ BCD_TO_BIN(cmos_minutes);
+
+ /*
+ * since we're only adjusting minutes and seconds,
+ * don't interfere with hour overflow. This avoids
+ * messing with unknown time zones but requires your
+ * RTC not to be off by more than 15 minutes
+ */
+ real_seconds = nowtime % 60;
+ real_minutes = nowtime / 60;
+ if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
+ real_minutes += 30; /* correct for half hour time zone */
+ real_minutes %= 60;
+
+ if (abs(real_minutes - cmos_minutes) < 30) {
+ if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+ BIN_TO_BCD(real_seconds);
+ BIN_TO_BCD(real_minutes);
+ }
+ CMOS_WRITE(real_seconds,RTC_SECONDS);
+ CMOS_WRITE(real_minutes,RTC_MINUTES);
+ } else
+ retval = -1;
+
+ /* The following flags have to be released exactly in this order,
+ * otherwise the DS12887 (popular MC146818A clone with integrated
+ * battery and quartz) will not reset the oscillator and will not
+ * update precisely 500 ms later. You won't find this mentioned in
+ * the Dallas Semiconductor data sheets, but who believes data
+ * sheets anyway ... -- Markus Kuhn
+ */
+ CMOS_WRITE(save_control, RTC_CONTROL);
+ CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
+
+ return retval;
+}
+
+
+static unsigned long timer1_latch;
+
+static unsigned long timer1_gettimeoffset (void)
+{
+ unsigned long value = timer1_latch - *CSR_TIMER1_VALUE;
+
+ return ((tick_nsec / 1000) * value) / timer1_latch;
+}
+
+static irqreturn_t
+timer1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ *CSR_TIMER1_CLR = 0;
+
+ timer_tick(regs);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction footbridge_timer_irq = {
+ .flags = SA_INTERRUPT
+};
+
+/*
+ * Set up timer interrupt.
+ */
+void __init footbridge_init_time(void)
+{
+ if (machine_is_co285() ||
+ machine_is_personal_server())
+ /*
+ * Add-in 21285s shouldn't access the RTC
+ */
+ rtc_base = 0;
+ else
+ rtc_base = 0x70;
+
+ if (rtc_base) {
+ int reg_d, reg_b;
+
+ /*
+ * Probe for the RTC.
+ */
+ reg_d = CMOS_READ(RTC_REG_D);
+
+ /*
+ * make sure the divider is set
+ */
+ CMOS_WRITE(RTC_REF_CLCK_32KHZ, RTC_REG_A);
+
+ /*
+ * Set control reg B
+ * (24 hour mode, update enabled)
+ */
+ reg_b = CMOS_READ(RTC_REG_B) & 0x7f;
+ reg_b |= 2;
+ CMOS_WRITE(reg_b, RTC_REG_B);
+
+ if ((CMOS_READ(RTC_REG_A) & 0x7f) == RTC_REF_CLCK_32KHZ &&
+ CMOS_READ(RTC_REG_B) == reg_b) {
+ struct timespec tv;
+
+ /*
+ * We have a RTC. Check the battery
+ */
+ if ((reg_d & 0x80) == 0)
+ printk(KERN_WARNING "RTC: *** warning: CMOS battery bad\n");
+
+ tv.tv_nsec = 0;
+ tv.tv_sec = get_isa_cmos_time();
+ do_settimeofday(&tv);
+ set_rtc = set_isa_cmos_time;
+ } else
+ rtc_base = 0;
+ }
+
+ if (machine_is_ebsa285() ||
+ machine_is_co285() ||
+ machine_is_personal_server()) {
+ gettimeoffset = timer1_gettimeoffset;
+
+ timer1_latch = (mem_fclk_21285 + 8 * HZ) / (16 * HZ);
+
+ *CSR_TIMER1_CLR = 0;
+ *CSR_TIMER1_LOAD = timer1_latch;
+ *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_AUTORELOAD | TIMER_CNTL_DIV16;
+
+ footbridge_timer_irq.name = "Timer1 Timer Tick";
+ footbridge_timer_irq.handler = timer1_interrupt;
+
+ setup_irq(IRQ_TIMER1, &footbridge_timer_irq);
+
+ } else {
+ /* enable PIT timer */
+ /* set for periodic (4) and LSB/MSB write (0x30) */
+ outb(0x34, 0x43);
+ outb((mSEC_10_from_14/6) & 0xFF, 0x40);
+ outb((mSEC_10_from_14/6) >> 8, 0x40);
+
+ gettimeoffset = isa_gettimeoffset;
+
+ footbridge_timer_irq.name = "ISA Timer Tick";
+ footbridge_timer_irq.handler = isa_timer_interrupt;
+
+ setup_irq(IRQ_ISA_TIMER, &footbridge_timer_irq);
+ }
+}
EXPORT_SYMBOL(pci_set_dma_mask);
EXPORT_SYMBOL(pci_dac_set_dma_mask);
EXPORT_SYMBOL(pci_set_consistent_dma_mask);
+EXPORT_SYMBOL(ixp4xx_pci_read);
+EXPORT_SYMBOL(ixp4xx_pci_write);
#include <asm/mach/map.h>
#include <asm/mach/irq.h>
+#include <asm/mach/time.h>
/*************************************************************************
* Catch up with the real idea of time
*/
do {
- do_timer(regs);
+ timer_tick(regs);
last_jiffy_time += LATCH;
} while((*IXP4XX_OSTS - last_jiffy_time) > LATCH);
return IRQ_HANDLED;
}
-extern unsigned long (*gettimeoffset)(void);
-
-static struct irqaction timer_irq = {
- .name = "IXP4xx Timer Tick",
- .flags = SA_INTERRUPT
+static struct irqaction ixp4xx_timer_irq = {
+ .name = "IXP4xx Timer Tick",
+ .flags = SA_INTERRUPT,
+ .handler = ixp4xx_timer_interrupt
};
-void __init time_init(void)
+void __init ixp4xx_init_time(void)
{
gettimeoffset = ixp4xx_gettimeoffset;
- timer_irq.handler = ixp4xx_timer_interrupt;
/* Clear Pending Interrupt by writing '1' to it */
*IXP4XX_OSST = IXP4XX_OSST_TIMER_1_PEND;
last_jiffy_time = 0;
/* Connect the interrupt handler and enable the interrupt */
- setup_irq(IRQ_IXP4XX_TIMER1, &timer_irq);
+ setup_irq(IRQ_IXP4XX_TIMER1, &ixp4xx_timer_irq);
}
.flags = IORESOURCE_MEM,
};
-static struct platform_device prpmc1100_flash_device = {
+static struct platform_device prpmc1100_flash = {
.name = "IXP4XX-Flash",
.id = 0,
.dev = {
.resource = &prpmc1100_flash_resource,
};
+static struct platform_device *prpmc1100_devices[] __initdata = {
+ &prpmc1100_flash
+};
+
static void __init prpmc1100_init(void)
{
- platform_add_device(&prpmc1100_flash_device);
+ platform_add_devices(&prpmc1100_devices, ARRAY_SIZE(prpmc1100_devices));
}
MACHINE_START(PRPMC1100, "Motorola PrPMC1100")
IXP4XX_PERIPHERAL_BASE_VIRT)
MAPIO(prpmc1100_map_io)
INITIRQ(ixp4xx_init_irq)
+ INITTIME(ixp4xx_init_time)
BOOT_PARAMS(0x0100)
INIT_MACHINE(prpmc1100_init)
MACHINE_END
--- /dev/null
+/*
+ * arch/arm/mach-lh7a40x/time.c
+ *
+ * Copyright (C) 2004 Logic Product Development
+ *
+ * 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/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/leds.h>
+
+#include <asm/mach/time.h>
+
+#if HZ < 100
+# define TIMER_CONTROL TIMER_CONTROL1
+# define TIMER_LOAD TIMER_LOAD1
+# define TIMER_CONSTANT (508469/HZ)
+# define TIMER_MODE (TIMER_C_ENABLE | TIMER_C_PERIODIC | TIMER_C_508KHZ)
+# define TIMER_EOI TIMER_EOI1
+# define TIMER_IRQ IRQ_T1UI
+#else
+# define TIMER_CONTROL TIMER_CONTROL3
+# define TIMER_LOAD TIMER_LOAD3
+# define TIMER_CONSTANT (3686400/HZ)
+# define TIMER_MODE (TIMER_C_ENABLE | TIMER_C_PERIODIC)
+# define TIMER_EOI TIMER_EOI3
+# define TIMER_IRQ IRQ_T3UI
+#endif
+
+static irqreturn_t
+lh7a40x_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ TIMER_EOI = 0;
+ timer_tick(regs);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction lh7a40x_timer_irq = {
+ .name = "LHA740x Timer Tick",
+ .flags = SA_INTERRUPT,
+ .handler = lh7a40x_timer_interrupt
+};
+
+void __init lh7a40x_init_time(void)
+{
+ /* Stop/disable all timers */
+ TIMER_CONTROL1 = 0;
+ TIMER_CONTROL2 = 0;
+ TIMER_CONTROL3 = 0;
+
+ setup_irq (TIMER_IRQ, &lh7a40x_timer_irq);
+
+ TIMER_LOAD = TIMER_CONSTANT;
+ TIMER_CONTROL = TIMER_MODE;
+}
+
--- /dev/null
+/*
+ * arch/arm/mach-omap/time.c
+ *
+ * OMAP Timer Tick
+ *
+ * Copyright (C) 2000 RidgeRun, Inc.
+ * Author: Greg Lonnon <glonnon@ridgerun.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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+
+#include <asm/system.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/leds.h>
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/time.h>
+#include <asm/arch/clocks.h>
+
+#ifndef __instrument
+#define __instrument
+#define __noinstrument __attribute__ ((no_instrument_function))
+#endif
+
+typedef struct {
+ u32 cntl; /* CNTL_TIMER, R/W */
+ u32 load_tim; /* LOAD_TIM, W */
+ u32 read_tim; /* READ_TIM, R */
+} mputimer_regs_t;
+
+#define mputimer_base(n) \
+ ((volatile mputimer_regs_t*)IO_ADDRESS(OMAP_MPUTIMER_BASE + \
+ (n)*OMAP_MPUTIMER_OFFSET))
+
+static inline unsigned long timer32k_read(int reg) {
+ unsigned long val;
+ val = omap_readw(reg + OMAP_32kHz_TIMER_BASE);
+ return val;
+}
+static inline void timer32k_write(int reg,int val) {
+ omap_writew(val, reg + OMAP_32kHz_TIMER_BASE);
+}
+
+/*
+ * How long is the timer interval? 100 HZ, right...
+ * IRQ rate = (TVR + 1) / 32768 seconds
+ * TVR = 32768 * IRQ_RATE -1
+ * IRQ_RATE = 1/100
+ * TVR = 326
+ */
+#define TIMER32k_PERIOD 326
+//#define TIMER32k_PERIOD 0x7ff
+
+static inline void start_timer32k(void) {
+ timer32k_write(TIMER32k_CR,
+ TIMER32k_TSS | TIMER32k_TRB |
+ TIMER32k_INT | TIMER32k_ARL);
+}
+
+#ifdef CONFIG_MACH_OMAP_PERSEUS2
+/*
+ * After programming PTV with 0 and setting the MPUTIM_CLOCK_ENABLE
+ * (external clock enable) bit, the timer count rate is 6.5 MHz (13
+ * MHZ input/2). !! The divider by 2 is undocumented !!
+ */
+#define MPUTICKS_PER_SEC (13000000/2)
+#else
+/*
+ * After programming PTV with 0, the timer count rate is 6 MHz.
+ * WARNING! this must be an even number, or machinecycles_to_usecs
+ * below will break.
+ */
+#define MPUTICKS_PER_SEC (12000000/2)
+#endif
+
+static int mputimer_started[3] = {0,0,0};
+
+static inline void __noinstrument start_mputimer(int n,
+ unsigned long load_val)
+{
+ volatile mputimer_regs_t* timer = mputimer_base(n);
+
+ mputimer_started[n] = 0;
+ timer->cntl = MPUTIM_CLOCK_ENABLE;
+ udelay(1);
+
+ timer->load_tim = load_val;
+ udelay(1);
+ timer->cntl = (MPUTIM_CLOCK_ENABLE | MPUTIM_AR | MPUTIM_ST);
+ mputimer_started[n] = 1;
+}
+
+static inline unsigned long __noinstrument
+read_mputimer(int n)
+{
+ volatile mputimer_regs_t* timer = mputimer_base(n);
+ return (mputimer_started[n] ? timer->read_tim : 0);
+}
+
+void __noinstrument start_mputimer1(unsigned long load_val)
+{
+ start_mputimer(0, load_val);
+}
+void __noinstrument start_mputimer2(unsigned long load_val)
+{
+ start_mputimer(1, load_val);
+}
+void __noinstrument start_mputimer3(unsigned long load_val)
+{
+ start_mputimer(2, load_val);
+}
+
+unsigned long __noinstrument read_mputimer1(void)
+{
+ return read_mputimer(0);
+}
+unsigned long __noinstrument read_mputimer2(void)
+{
+ return read_mputimer(1);
+}
+unsigned long __noinstrument read_mputimer3(void)
+{
+ return read_mputimer(2);
+}
+
+unsigned long __noinstrument do_getmachinecycles(void)
+{
+ return 0 - read_mputimer(0);
+}
+
+unsigned long __noinstrument machinecycles_to_usecs(unsigned long mputicks)
+{
+ /* Round up to nearest usec */
+ return ((mputicks * 1000) / (MPUTICKS_PER_SEC / 2 / 1000) + 1) >> 1;
+}
+
+/*
+ * This marks the time of the last system timer interrupt
+ * that was *processed by the ISR* (timer 2).
+ */
+static unsigned long systimer_mark;
+
+static unsigned long omap_gettimeoffset(void)
+{
+ /* Return elapsed usecs since last system timer ISR */
+ return machinecycles_to_usecs(do_getmachinecycles() - systimer_mark);
+}
+
+static irqreturn_t
+omap_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long now, ilatency;
+
+ /*
+ * Mark the time at which the timer interrupt ocurred using
+ * timer1. We need to remove interrupt latency, which we can
+ * retrieve from the current system timer2 counter. Both the
+ * offset timer1 and the system timer2 are counting at 6MHz,
+ * so we're ok.
+ */
+ now = 0 - read_mputimer1();
+ ilatency = MPUTICKS_PER_SEC / 100 - read_mputimer2();
+ systimer_mark = now - ilatency;
+
+ timer_tick(regs);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction omap_timer_irq = {
+ .name = "OMAP Timer Tick",
+ .flags = SA_INTERRUPT,
+ .handler = omap_timer_interrupt
+};
+
+void __init omap_init_time(void)
+{
+ /* Since we don't call request_irq, we must init the structure */
+ gettimeoffset = omap_gettimeoffset;
+
+#ifdef OMAP1510_USE_32KHZ_TIMER
+ timer32k_write(TIMER32k_CR, 0x0);
+ timer32k_write(TIMER32k_TVR,TIMER32k_PERIOD);
+ setup_irq(INT_OS_32kHz_TIMER, &omap_timer_irq);
+ start_timer32k();
+#else
+ setup_irq(INT_TIMER2, &omap_timer_irq);
+ start_mputimer2(MPUTICKS_PER_SEC / 100 - 1);
+#endif
+}
+
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/bitops.h>
+#include <linux/fb.h>
#include <asm/types.h>
#include <asm/setup.h>
#include <asm/mach/irq.h>
#include <asm/arch/mainstone.h>
+#include <asm/arch/pxafb.h>
#include "generic.h"
.resource = smc91x_resources,
};
+
+static void mainstone_backlight_power(int on)
+{
+ if (on) {
+ pxa_gpio_mode(GPIO16_PWM0_MD);
+ pxa_set_cken(CKEN0_PWM0, 1);
+ PWM_CTRL0 = 0;
+ PWM_PWDUTY0 = 0x3ff;
+ PWM_PERVAL0 = 0x3ff;
+ } else {
+ PWM_CTRL0 = 0;
+ PWM_PWDUTY0 = 0x0;
+ PWM_PERVAL0 = 0x3FF;
+ pxa_set_cken(CKEN0_PWM0, 0);
+ }
+}
+
+static struct pxafb_mach_info toshiba_ltm04c380k __initdata = {
+ .pixclock = 50000,
+ .xres = 640,
+ .yres = 480,
+ .bpp = 16,
+ .hsync_len = 1,
+ .left_margin = 0x9f,
+ .right_margin = 1,
+ .vsync_len = 44,
+ .upper_margin = 0,
+ .lower_margin = 0,
+ .sync = FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT,
+ .lccr0 = LCCR0_Act,
+ .lccr3 = LCCR3_PCP,
+ .pxafb_backlight_power = mainstone_backlight_power,
+};
+
+static struct pxafb_mach_info toshiba_ltm035a776c __initdata = {
+ .pixclock = 110000,
+ .xres = 240,
+ .yres = 320,
+ .bpp = 16,
+ .hsync_len = 4,
+ .left_margin = 8,
+ .right_margin = 20,
+ .vsync_len = 3,
+ .upper_margin = 1,
+ .lower_margin = 10,
+ .sync = FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT,
+ .lccr0 = LCCR0_Act,
+ .lccr3 = LCCR3_PCP,
+ .pxafb_backlight_power = mainstone_backlight_power,
+};
+
static void __init mainstone_init(void)
{
- platform_add_device(&smc91x_device);
+ platform_device_register(&smc91x_device);
+
+ /* reading Mainstone's "Virtual Configuration Register"
+ might be handy to select LCD type here */
+ if (0)
+ set_pxa_fb_info(&toshiba_ltm04c380k);
+ else
+ set_pxa_fb_info(&toshiba_ltm035a776c);
}
BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000))
MAPIO(mainstone_map_io)
INITIRQ(mainstone_init_irq)
+ INITTIME(pxa_init_time)
INIT_MACHINE(mainstone_init)
MACHINE_END
--- /dev/null
+/*
+ * arch/arm/mach-pxa/time.c
+ *
+ * Author: Nicolas Pitre
+ * Created: Jun 15, 2001
+ * Copyright: 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/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+
+#include <asm/system.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/leds.h>
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/time.h>
+
+
+static inline unsigned long pxa_get_rtc_time(void)
+{
+ return RCNR;
+}
+
+static int pxa_set_rtc(void)
+{
+ unsigned long current_time = xtime.tv_sec;
+
+ if (RTSR & RTSR_ALE) {
+ /* make sure not to forward the clock over an alarm */
+ unsigned long alarm = RTAR;
+ if (current_time >= alarm && alarm >= RCNR)
+ return -ERESTARTSYS;
+ }
+ RCNR = current_time;
+ return 0;
+}
+
+/* IRQs are disabled before entering here from do_gettimeofday() */
+static unsigned long pxa_gettimeoffset (void)
+{
+ long ticks_to_match, elapsed, usec;
+
+ /* Get ticks before next timer match */
+ ticks_to_match = OSMR0 - OSCR;
+
+ /* We need elapsed ticks since last match */
+ elapsed = LATCH - ticks_to_match;
+
+ /* don't get fooled by the workaround in pxa_timer_interrupt() */
+ if (elapsed <= 0)
+ return 0;
+
+ /* Now convert them to usec */
+ usec = (unsigned long)(elapsed * (tick_nsec / 1000))/LATCH;
+
+ return usec;
+}
+
+static irqreturn_t
+pxa_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ int next_match;
+
+ /* Loop until we get ahead of the free running timer.
+ * This ensures an exact clock tick count and time accuracy.
+ * IRQs are disabled inside the loop to ensure coherence between
+ * lost_ticks (updated in do_timer()) and the match reg value, so we
+ * can use do_gettimeofday() from interrupt handlers.
+ *
+ * HACK ALERT: it seems that the PXA timer regs aren't updated right
+ * away in all cases when a write occurs. We therefore compare with
+ * 8 instead of 0 in the while() condition below to avoid missing a
+ * match if OSCR has already reached the next OSMR value.
+ * Experience has shown that up to 6 ticks are needed to work around
+ * this problem, but let's use 8 to be conservative. Note that this
+ * affect things only when the timer IRQ has been delayed by nearly
+ * exactly one tick period which should be a pretty rare event.
+ */
+ do {
+ timer_tick(regs);
+ OSSR = OSSR_M0; /* Clear match on timer 0 */
+ next_match = (OSMR0 += LATCH);
+ } while( (signed long)(next_match - OSCR) <= 8 );
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction pxa_timer_irq = {
+ .name = "PXA Timer Tick",
+ .flags = SA_INTERRUPT,
+ .handler = pxa_timer_interrupt
+};
+
+void __init pxa_init_time(void)
+{
+ struct timespec tv;
+
+ gettimeoffset = pxa_gettimeoffset;
+ set_rtc = pxa_set_rtc;
+
+ tv.tv_nsec = 0;
+ tv.tv_sec = pxa_get_rtc_time();
+ do_settimeofday(&tv);
+
+ OSMR0 = 0; /* set initial match at 0 */
+ OSSR = 0xf; /* clear status on all timers */
+ setup_irq(IRQ_OST0, &pxa_timer_irq);
+ OIER |= OIER_E0; /* enable match on timer 0 to cause interrupts */
+ OSCR = 0; /* initialize free-running timer, force first match */
+}
+
--- /dev/null
+/* linux/arch/arm/mach-s3c2410/gpio.c
+ *
+ * Copyright (c) 2004 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C2410 GPIO support
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+#include <asm/arch/regs-gpio.h>
+
+void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function)
+{
+ unsigned long base = S3C2410_GPIO_BASE(pin);
+ unsigned long shift = 1;
+ unsigned long mask = 3;
+ unsigned long con;
+ unsigned long flags;
+
+ if (pin < S3C2410_GPIO_BANKB) {
+ shift = 0;
+ mask = 1;
+ }
+
+ mask <<= S3C2410_GPIO_OFFSET(pin);
+
+ local_irq_save(flags);
+
+ con = __raw_readl(base + 0x00);
+
+ con &= mask << shift;
+ con |= function;
+
+ __raw_writel(con, base + 0x00);
+
+ local_irq_restore(flags);
+}
+
+void s3c2410_gpio_pullup(unsigned int pin, unsigned int to)
+{
+ unsigned long base = S3C2410_GPIO_BASE(pin);
+ unsigned long offs = S3C2410_GPIO_OFFSET(pin);
+ unsigned long flags;
+ unsigned long up;
+
+ if (pin < S3C2410_GPIO_BANKB)
+ return;
+
+ local_irq_save(flags);
+
+ up = __raw_readl(base + 0x08);
+ up &= 1 << offs;
+ up |= to << offs;
+ __raw_writel(up, base + 0x08);
+
+ local_irq_restore(flags);
+}
+
+void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
+{
+ unsigned long base = S3C2410_GPIO_BASE(pin);
+ unsigned long offs = S3C2410_GPIO_OFFSET(pin);
+ unsigned long flags;
+ unsigned long dat;
+
+ local_irq_save(flags);
+
+ dat = __raw_readl(base + 0x04);
+ dat &= 1 << offs;
+ dat |= to << offs;
+ __raw_writel(dat, base + 0x04);
+
+ local_irq_restore(flags);
+}
s3c2410_init_irq();
}
+void __init smdk2410_init_time(void)
+{
+ s3c2410_init_time();
+}
+
MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch
* to SMDK2410 */
MAINTAINER("Jonas Dietsche")
BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100)
MAPIO(smdk2410_map_io)
INITIRQ(smdk2410_init_irq)
+ INITTIME(smdk2410_init_time)
MACHINE_END
--- /dev/null
+/* linux/include/asm-arm/arch-s3c2410/time.h
+ *
+ * Copyright (C) 2003 Simtec Electronics <linux@simtec.co.uk>
+ * Ben Dooks, <ben@simtec.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <asm/system.h>
+#include <asm/leds.h>
+#include <asm/mach-types.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/arch/map.h>
+#include <asm/arch/regs-timer.h>
+#include <asm/mach/time.h>
+
+static unsigned long timer_startval;
+static unsigned long timer_ticks_usec;
+
+#ifdef CONFIG_S3C2410_RTC
+extern void s3c2410_rtc_check();
+#endif
+
+/* with an 12MHz clock, we get 12 ticks per-usec
+ */
+
+
+/***
+ * Returns microsecond since last clock interrupt. Note that interrupts
+ * will have been disabled by do_gettimeoffset()
+ * IRQs are disabled before entering here from do_gettimeofday()
+ */
+static unsigned long s3c2410_gettimeoffset (void)
+{
+ unsigned long tdone;
+ unsigned long usec;
+
+ /* work out how many ticks have gone since last timer interrupt */
+
+ tdone = timer_startval - __raw_readl(S3C2410_TCNTO(4));
+
+ /* currently, tcnt is in 12MHz units, but this may change
+ * for non-bast machines...
+ */
+
+ usec = tdone / timer_ticks_usec;
+
+ return usec;
+}
+
+
+/*
+ * IRQ handler for the timer
+ */
+static irqreturn_t
+s3c2410_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ timer_tick(regs);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction s3c2410_timer_irq = {
+ .name = "S32410 Timer Tick",
+ .flags = SA_INTERRUPT,
+ .handler = s3c2410_timer_interrupt
+};
+
+/*
+ * Set up timer interrupt, and return the current time in seconds.
+ *
+ * Currently we only use timer4, as it is the only timer which has no
+ * other function that can be exploited externally
+ */
+void __init s3c2410_init_time (void)
+{
+ unsigned long tcon;
+ unsigned long tcnt;
+ unsigned long tcfg1;
+ unsigned long tcfg0;
+
+ gettimeoffset = s3c2410_gettimeoffset;
+
+ tcnt = 0xffff; /* default value for tcnt */
+
+ /* read the current timer configuration bits */
+
+ tcon = __raw_readl(S3C2410_TCON);
+ tcfg1 = __raw_readl(S3C2410_TCFG1);
+ tcfg0 = __raw_readl(S3C2410_TCFG0);
+
+ /* configure the system for whichever machine is in use */
+
+ if (machine_is_bast() || machine_is_vr1000()) {
+ timer_ticks_usec = 12; /* timer is at 12MHz */
+ tcnt = (timer_ticks_usec * (1000*1000)) / HZ;
+ }
+
+ /* for the h1940, we use the pclk from the core to generate
+ * the timer values. since 67.5MHz is not a value we can directly
+ * generate the timer value from, we need to pre-scale and
+ * divied before using it.
+ *
+ * overall divsior to get 200Hz is 337500
+ * we can fit tcnt if we pre-scale by 6, producing a tick rate
+ * of 11.25MHz, and a tcnt of 56250.
+ */
+
+ if (machine_is_h1940() || machine_is_smdk2410() ) {
+ timer_ticks_usec = s3c2410_pclk / (1000*1000);
+ timer_ticks_usec /= 6;
+
+ tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK;
+ tcfg1 |= S3C2410_TCFG1_MUX4_DIV2;
+
+ tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK;
+ tcfg0 |= ((6 - 1) / 2) << S3C2410_TCFG_PRESCALER1_SHIFT;
+
+ tcnt = (s3c2410_pclk / 6) / HZ;
+ }
+
+
+ printk("setup_timer tcon=%08lx, tcnt %04lx, tcfg %08lx,%08lx\n",
+ tcon, tcnt, tcfg0, tcfg1);
+
+ /* check to see if timer is within 16bit range... */
+ if (tcnt > 0xffff) {
+ panic("setup_timer: HZ is too small, cannot configure timer!");
+ return;
+ }
+
+ __raw_writel(tcfg1, S3C2410_TCFG1);
+ __raw_writel(tcfg0, S3C2410_TCFG0);
+
+ timer_startval = tcnt;
+ __raw_writel(tcnt, S3C2410_TCNTB(4));
+
+ /* ensure timer is stopped... */
+
+ tcon &= ~(7<<20);
+ tcon |= S3C2410_TCON_T4RELOAD;
+ tcon |= S3C2410_TCON_T4MANUALUPD;
+
+ __raw_writel(tcon, S3C2410_TCON);
+ __raw_writel(tcnt, S3C2410_TCNTB(4));
+ __raw_writel(tcnt, S3C2410_TCMPB(4));
+
+ setup_irq(IRQ_TIMER4, &s3c2410_timer_irq);
+
+ /* start the timer running */
+ tcon |= S3C2410_TCON_T4START;
+ tcon &= ~S3C2410_TCON_T4MANUALUPD;
+ __raw_writel(tcon, S3C2410_TCON);
+}
+
+
+
MAPIO(collie_map_io)
INITIRQ(sa1100_init_irq)
INIT_MACHINE(collie_init)
+ INITTIME(sa1100_init_time)
MACHINE_END
--- /dev/null
+/*
+ * linux/arch/arm/mach-sa1100/time.c
+ *
+ * Copyright (C) 1998 Deborah Wallach.
+ * Twiddles (C) 1999 Hugo Fiennes <hugo@empeg.com>
+ *
+ * 2000/03/29 (C) Nicolas Pitre <nico@cam.org>
+ * Rewritten: big cleanup, much simpler, better HZ accuracy.
+ *
+ */
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/signal.h>
+
+#include <asm/mach/time.h>
+#include <asm/hardware.h>
+
+#define RTC_DEF_DIVIDER (32768 - 1)
+#define RTC_DEF_TRIM 0
+
+static unsigned long __init sa1100_get_rtc_time(void)
+{
+ /*
+ * According to the manual we should be able to let RTTR be zero
+ * and then a default diviser for a 32.768KHz clock is used.
+ * Apparently this doesn't work, at least for my SA1110 rev 5.
+ * If the clock divider is uninitialized then reset it to the
+ * default value to get the 1Hz clock.
+ */
+ if (RTTR == 0) {
+ RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16);
+ printk(KERN_WARNING "Warning: uninitialized Real Time Clock\n");
+ /* The current RTC value probably doesn't make sense either */
+ RCNR = 0;
+ return 0;
+ }
+ return RCNR;
+}
+
+static int sa1100_set_rtc(void)
+{
+ unsigned long current_time = xtime.tv_sec;
+
+ if (RTSR & RTSR_ALE) {
+ /* make sure not to forward the clock over an alarm */
+ unsigned long alarm = RTAR;
+ if (current_time >= alarm && alarm >= RCNR)
+ return -ERESTARTSYS;
+ }
+ RCNR = current_time;
+ return 0;
+}
+
+/* IRQs are disabled before entering here from do_gettimeofday() */
+static unsigned long sa1100_gettimeoffset (void)
+{
+ unsigned long ticks_to_match, elapsed, usec;
+
+ /* Get ticks before next timer match */
+ ticks_to_match = OSMR0 - OSCR;
+
+ /* We need elapsed ticks since last match */
+ elapsed = LATCH - ticks_to_match;
+
+ /* Now convert them to usec */
+ usec = (unsigned long)(elapsed * (tick_nsec / 1000))/LATCH;
+
+ return usec;
+}
+
+/*
+ * We will be entered with IRQs enabled.
+ *
+ * Loop until we get ahead of the free running timer.
+ * This ensures an exact clock tick count and time accuracy.
+ * IRQs are disabled inside the loop to ensure coherence between
+ * lost_ticks (updated in do_timer()) and the match reg value, so we
+ * can use do_gettimeofday() from interrupt handlers.
+ */
+static irqreturn_t
+sa1100_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned int next_match;
+
+ do {
+ timer_tick(regs);
+ OSSR = OSSR_M0; /* Clear match on timer 0 */
+ next_match = (OSMR0 += LATCH);
+ } while ((signed long)(next_match - OSCR) <= 0);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction sa1100_timer_irq = {
+ .name = "SA11xx Timer Tick",
+ .flags = SA_INTERRUPT,
+ .handler = sa1100_timer_interrupt
+};
+
+void __init sa1100_init_time(void)
+{
+ struct timespec tv;
+
+ gettimeoffset = sa1100_gettimeoffset;
+ set_rtc = sa1100_set_rtc;
+
+ tv.tv_nsec = 0;
+ tv.tv_sec = sa1100_get_rtc_time();
+ do_settimeofday(&tv);
+
+ OSMR0 = 0; /* set initial match at 0 */
+ OSSR = 0xf; /* clear status on all timers */
+ setup_irq(IRQ_OST0, &sa1100_timer_irq);
+ OIER |= OIER_E0; /* enable match on timer 0 to cause interrupts */
+ OSCR = 0; /* initialize free-running timer, force first match */
+}
+
/* We must not map this if we have highmem enabled */
pte = pte_offset_map(pmd, addr);
printk(", *pte=%08lx", pte_val(*pte));
-#ifdef CONFIG_CPU_32
printk(", *ppte=%08lx", pte_val(pte[-PTRS_PER_PTE]));
-#endif
pte_unmap(pte);
#endif
} while(0);
si.si_signo = SIGSEGV;
si.si_errno = 0;
si.si_code = code;
- si.si_addr = (void *)addr;
+ si.si_addr = (void __user *)addr;
force_sig_info(SIGSEGV, &si, tsk);
}
* stack+task struct. Use the same method as 'current' uses to
* reach them.
*/
-register unsigned int *user_registers asm("sl");
+register unsigned long *user_registers asm("sl");
#define GET_USERREG() (user_registers)
#include <asm/uaccess.h>
-static inline void loadSingle(const unsigned int Fn, const unsigned int *pMem)
+static inline void loadSingle(const unsigned int Fn, const unsigned int __user *pMem)
{
FPA11 *fpa11 = GET_FPA11();
fpa11->fType[Fn] = typeSingle;
get_user(fpa11->fpreg[Fn].fSingle, pMem);
}
-static inline void loadDouble(const unsigned int Fn, const unsigned int *pMem)
+static inline void loadDouble(const unsigned int Fn, const unsigned int __user *pMem)
{
FPA11 *fpa11 = GET_FPA11();
unsigned int *p;
}
#ifdef CONFIG_FPE_NWFPE_XP
-static inline void loadExtended(const unsigned int Fn, const unsigned int *pMem)
+static inline void loadExtended(const unsigned int Fn, const unsigned int __user *pMem)
{
FPA11 *fpa11 = GET_FPA11();
unsigned int *p;
}
#endif
-static inline void loadMultiple(const unsigned int Fn, const unsigned int *pMem)
+static inline void loadMultiple(const unsigned int Fn, const unsigned int __user *pMem)
{
FPA11 *fpa11 = GET_FPA11();
register unsigned int *p;
}
}
-static inline void storeSingle(const unsigned int Fn, unsigned int *pMem)
+static inline void storeSingle(const unsigned int Fn, unsigned int __user *pMem)
{
FPA11 *fpa11 = GET_FPA11();
union {
put_user(val.i[0], pMem);
}
-static inline void storeDouble(const unsigned int Fn, unsigned int *pMem)
+static inline void storeDouble(const unsigned int Fn, unsigned int __user *pMem)
{
FPA11 *fpa11 = GET_FPA11();
union {
}
#ifdef CONFIG_FPE_NWFPE_XP
-static inline void storeExtended(const unsigned int Fn, unsigned int *pMem)
+static inline void storeExtended(const unsigned int Fn, unsigned int __user *pMem)
{
FPA11 *fpa11 = GET_FPA11();
union {
}
#endif
-static inline void storeMultiple(const unsigned int Fn, unsigned int *pMem)
+static inline void storeMultiple(const unsigned int Fn, unsigned int __user *pMem)
{
FPA11 *fpa11 = GET_FPA11();
register unsigned int nType, *p;
unsigned int PerformLDF(const unsigned int opcode)
{
- unsigned int *pBase, *pAddress, *pFinal, nRc = 1,
- write_back = WRITE_BACK(opcode);
+ unsigned int __user *pBase, *pAddress, *pFinal;
+ unsigned int nRc = 1, write_back = WRITE_BACK(opcode);
- pBase = (unsigned int *) readRegister(getRn(opcode));
+ pBase = (unsigned int __user *) readRegister(getRn(opcode));
if (REG_PC == getRn(opcode)) {
pBase += 2;
write_back = 0;
}
if (write_back)
- writeRegister(getRn(opcode), (unsigned int) pFinal);
+ writeRegister(getRn(opcode), (unsigned long) pFinal);
return nRc;
}
unsigned int PerformSTF(const unsigned int opcode)
{
- unsigned int *pBase, *pAddress, *pFinal, nRc = 1,
- write_back = WRITE_BACK(opcode);
+ unsigned int __user *pBase, *pAddress, *pFinal;
+ unsigned int nRc = 1, write_back = WRITE_BACK(opcode);
SetRoundingMode(ROUND_TO_NEAREST);
- pBase = (unsigned int *) readRegister(getRn(opcode));
+ pBase = (unsigned int __user *) readRegister(getRn(opcode));
if (REG_PC == getRn(opcode)) {
pBase += 2;
write_back = 0;
}
if (write_back)
- writeRegister(getRn(opcode), (unsigned int) pFinal);
+ writeRegister(getRn(opcode), (unsigned long) pFinal);
return nRc;
}
unsigned int PerformLFM(const unsigned int opcode)
{
- unsigned int i, Fd, *pBase, *pAddress, *pFinal,
- write_back = WRITE_BACK(opcode);
+ unsigned int __user *pBase, *pAddress, *pFinal;
+ unsigned int i, Fd, write_back = WRITE_BACK(opcode);
- pBase = (unsigned int *) readRegister(getRn(opcode));
+ pBase = (unsigned int __user *) readRegister(getRn(opcode));
if (REG_PC == getRn(opcode)) {
pBase += 2;
write_back = 0;
}
if (write_back)
- writeRegister(getRn(opcode), (unsigned int) pFinal);
+ writeRegister(getRn(opcode), (unsigned long) pFinal);
return 1;
}
unsigned int PerformSFM(const unsigned int opcode)
{
- unsigned int i, Fd, *pBase, *pAddress, *pFinal,
- write_back = WRITE_BACK(opcode);
+ unsigned int __user *pBase, *pAddress, *pFinal;
+ unsigned int i, Fd, write_back = WRITE_BACK(opcode);
- pBase = (unsigned int *) readRegister(getRn(opcode));
+ pBase = (unsigned int __user *) readRegister(getRn(opcode));
if (REG_PC == getRn(opcode)) {
pBase += 2;
write_back = 0;
}
if (write_back)
- writeRegister(getRn(opcode), (unsigned int) pFinal);
+ writeRegister(getRn(opcode), (unsigned long) pFinal);
return 1;
}
#ifdef CONFIG_DEBUG_USER
printk(KERN_DEBUG
- "NWFPE: %s[%d] takes exception %08x at %p from %08x\n",
+ "NWFPE: %s[%d] takes exception %08x at %p from %08lx\n",
current->comm, current->pid, flags,
__builtin_return_address(0), GET_USERREG()[15]);
#endif
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-extern __inline__
-unsigned int readRegister(const unsigned int nReg)
+static inline unsigned long readRegister(const unsigned int nReg)
{
/* Note: The CPU thinks it has dealt with the current instruction.
As a result the program counter has been advanced to the next
for this in this routine. LDF/STF instructions with Rn = PC
depend on the PC being correct, as they use PC+8 in their
address calculations. */
- unsigned int *userRegisters = GET_USERREG();
+ unsigned long *userRegisters = GET_USERREG();
unsigned int val = userRegisters[nReg];
if (REG_PC == nReg)
val -= 4;
return val;
}
-extern __inline__
-void writeRegister(const unsigned int nReg, const unsigned int val)
+static inline void
+writeRegister(const unsigned int nReg, const unsigned long val)
{
- unsigned int *userRegisters = GET_USERREG();
+ unsigned long *userRegisters = GET_USERREG();
userRegisters[nReg] = val;
}
-extern __inline__
-unsigned int readCPSR(void)
+static inline unsigned long readCPSR(void)
{
return (readRegister(REG_CPSR));
}
-extern __inline__
-void writeCPSR(const unsigned int val)
+static inline void writeCPSR(const unsigned long val)
{
writeRegister(REG_CPSR, val);
}
-extern __inline__
-unsigned int readConditionCodes(void)
+static inline unsigned long readConditionCodes(void)
{
#ifdef __FPEM_TEST__
return (0);
#endif
}
-extern __inline__
-void writeConditionCodes(const unsigned int val)
+static inline void writeConditionCodes(const unsigned long val)
{
- unsigned int *userRegisters = GET_USERREG();
- unsigned int rval;
+ unsigned long *userRegisters = GET_USERREG();
+ unsigned long rval;
/*
* Operate directly on userRegisters since
* the CPSR may be the PC register itself.
rval = userRegisters[REG_CPSR] & ~CC_MASK;
userRegisters[REG_CPSR] = rval | (val & CC_MASK);
}
-
-extern __inline__
-unsigned int readMemoryInt(unsigned int *pMem)
-{
- return *pMem;
-}
--- /dev/null
+#
+# linux/arch/arm/vfp/Makefile
+#
+# Copyright (C) 2001 ARM Limited
+#
+
+# EXTRA_CFLAGS := -DDEBUG
+# EXTRA_AFLAGS := -DDEBUG
+
+obj-y += vfp.o
+
+vfp-$(CONFIG_VFP) += entry.o vfpmodule.o vfphw.o vfpsingle.o vfpdouble.o
--- /dev/null
+/*
+ * linux/arch/arm/vfp/entry.S
+ *
+ * Copyright (C) 2004 ARM Limited.
+ * Written by Deep Blue Solutions Limited.
+ *
+ * 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.
+ *
+ * Basic entry code, called from the kernel's undefined instruction trap.
+ * r0 = faulted instruction
+ * r5 = faulted PC+4
+ * r9 = successful return
+ * r10 = thread_info structure
+ * lr = failure return
+ */
+#include <linux/linkage.h>
+#include <linux/init.h>
+#include <asm/thread_info.h>
+#include <asm/vfpmacros.h>
+
+ .globl do_vfp
+do_vfp:
+ ldr r4, .LCvfp
+ add r10, r10, #TI_VFPSTATE @ r10 = workspace
+ ldr pc, [r4] @ call VFP entry point
+
+.LCvfp:
+ .word vfp_vector
+
+@ This code is called if the VFP does not exist. It needs to flag the
+@ failure to the VFP initialisation code.
+
+ __INIT
+ .globl vfp_testing_entry
+vfp_testing_entry:
+ ldr r0, VFP_arch_address
+ str r5, [r0] @ known non-zero value
+ mov pc, r9 @ we have handled the fault
+
+VFP_arch_address:
+ .word VFP_arch
+
+ __FINIT
--- /dev/null
+/*
+ * linux/arch/arm/vfp/vfp.h
+ *
+ * Copyright (C) 2004 ARM Limited.
+ * Written by Deep Blue Solutions Limited.
+ *
+ * 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.
+ */
+
+static inline u32 vfp_shiftright32jamming(u32 val, unsigned int shift)
+{
+ if (shift) {
+ if (shift < 32)
+ val = val >> shift | ((val << (32 - shift)) != 0);
+ else
+ val = val != 0;
+ }
+ return val;
+}
+
+static inline u64 vfp_shiftright64jamming(u64 val, unsigned int shift)
+{
+ if (shift) {
+ if (shift < 64)
+ val = val >> shift | ((val << (64 - shift)) != 0);
+ else
+ val = val != 0;
+ }
+ return val;
+}
+
+static inline u32 vfp_hi64to32jamming(u64 val)
+{
+ u32 v;
+
+ asm(
+ "cmp %Q1, #1 @ vfp_hi64to32jamming\n\t"
+ "movcc %0, %R1\n\t"
+ "orrcs %0, %R1, #1"
+ : "=r" (v) : "r" (val) : "cc");
+
+ return v;
+}
+
+static inline void add128(u64 *resh, u64 *resl, u64 nh, u64 nl, u64 mh, u64 ml)
+{
+ asm( "adds %Q0, %Q2, %Q4\n\t"
+ "adcs %R0, %R2, %R4\n\t"
+ "adcs %Q1, %Q3, %Q5\n\t"
+ "adc %R1, %R3, %R5"
+ : "=r" (nl), "=r" (nh)
+ : "0" (nl), "1" (nh), "r" (ml), "r" (mh)
+ : "cc");
+ *resh = nh;
+ *resl = nl;
+}
+
+static inline void sub128(u64 *resh, u64 *resl, u64 nh, u64 nl, u64 mh, u64 ml)
+{
+ asm( "subs %Q0, %Q2, %Q4\n\t"
+ "sbcs %R0, %R2, %R4\n\t"
+ "sbcs %Q1, %Q3, %Q5\n\t"
+ "sbc %R1, %R3, %R5\n\t"
+ : "=r" (nl), "=r" (nh)
+ : "0" (nl), "1" (nh), "r" (ml), "r" (mh)
+ : "cc");
+ *resh = nh;
+ *resl = nl;
+}
+
+static inline void mul64to128(u64 *resh, u64 *resl, u64 n, u64 m)
+{
+ u32 nh, nl, mh, ml;
+ u64 rh, rma, rmb, rl;
+
+ nl = n;
+ ml = m;
+ rl = (u64)nl * ml;
+
+ nh = n >> 32;
+ rma = (u64)nh * ml;
+
+ mh = m >> 32;
+ rmb = (u64)nl * mh;
+ rma += rmb;
+
+ rh = (u64)nh * mh;
+ rh += ((u64)(rma < rmb) << 32) + (rma >> 32);
+
+ rma <<= 32;
+ rl += rma;
+ rh += (rl < rma);
+
+ *resl = rl;
+ *resh = rh;
+}
+
+static inline void shift64left(u64 *resh, u64 *resl, u64 n)
+{
+ *resh = n >> 63;
+ *resl = n << 1;
+}
+
+static inline u64 vfp_hi64multiply64(u64 n, u64 m)
+{
+ u64 rh, rl;
+ mul64to128(&rh, &rl, n, m);
+ return rh | (rl != 0);
+}
+
+static inline u64 vfp_estimate_div128to64(u64 nh, u64 nl, u64 m)
+{
+ u64 mh, ml, remh, reml, termh, terml, z;
+
+ if (nh >= m)
+ return ~0ULL;
+ mh = m >> 32;
+ z = (mh << 32 <= nh) ? 0xffffffff00000000ULL : (nh / mh) << 32;
+ mul64to128(&termh, &terml, m, z);
+ sub128(&remh, &reml, nh, nl, termh, terml);
+ ml = m << 32;
+ while ((s64)remh < 0) {
+ z -= 0x100000000ULL;
+ add128(&remh, &reml, remh, reml, mh, ml);
+ }
+ remh = (remh << 32) | (reml >> 32);
+ z |= (mh << 32 <= remh) ? 0xffffffff : remh / mh;
+ return z;
+}
+
+/*
+ * Operations on unpacked elements
+ */
+#define vfp_sign_negate(sign) (sign ^ 0x8000)
+
+/*
+ * Single-precision
+ */
+struct vfp_single {
+ s16 exponent;
+ u16 sign;
+ u32 significand;
+};
+
+extern s32 vfp_get_float(unsigned int reg);
+extern void vfp_put_float(unsigned int reg, s32 val);
+
+/*
+ * VFP_SINGLE_MANTISSA_BITS - number of bits in the mantissa
+ * VFP_SINGLE_EXPONENT_BITS - number of bits in the exponent
+ * VFP_SINGLE_LOW_BITS - number of low bits in the unpacked significand
+ * which are not propagated to the float upon packing.
+ */
+#define VFP_SINGLE_MANTISSA_BITS (23)
+#define VFP_SINGLE_EXPONENT_BITS (8)
+#define VFP_SINGLE_LOW_BITS (32 - VFP_SINGLE_MANTISSA_BITS - 2)
+#define VFP_SINGLE_LOW_BITS_MASK ((1 << VFP_SINGLE_LOW_BITS) - 1)
+
+/*
+ * The bit in an unpacked float which indicates that it is a quiet NaN
+ */
+#define VFP_SINGLE_SIGNIFICAND_QNAN (1 << (VFP_SINGLE_MANTISSA_BITS - 1 + VFP_SINGLE_LOW_BITS))
+
+/*
+ * Operations on packed single-precision numbers
+ */
+#define vfp_single_packed_sign(v) ((v) & 0x80000000)
+#define vfp_single_packed_negate(v) ((v) ^ 0x80000000)
+#define vfp_single_packed_abs(v) ((v) & ~0x80000000)
+#define vfp_single_packed_exponent(v) (((v) >> VFP_SINGLE_MANTISSA_BITS) & ((1 << VFP_SINGLE_EXPONENT_BITS) - 1))
+#define vfp_single_packed_mantissa(v) ((v) & ((1 << VFP_SINGLE_MANTISSA_BITS) - 1))
+
+/*
+ * Unpack a single-precision float. Note that this returns the magnitude
+ * of the single-precision float mantissa with the 1. if necessary,
+ * aligned to bit 30.
+ */
+static inline void vfp_single_unpack(struct vfp_single *s, s32 val)
+{
+ u32 significand;
+
+ s->sign = vfp_single_packed_sign(val) >> 16,
+ s->exponent = vfp_single_packed_exponent(val);
+
+ significand = (u32) val;
+ significand = (significand << (32 - VFP_SINGLE_MANTISSA_BITS)) >> 2;
+ if (s->exponent && s->exponent != 255)
+ significand |= 0x40000000;
+ s->significand = significand;
+}
+
+/*
+ * Re-pack a single-precision float. This assumes that the float is
+ * already normalised such that the MSB is bit 30, _not_ bit 31.
+ */
+static inline s32 vfp_single_pack(struct vfp_single *s)
+{
+ u32 val;
+ val = (s->sign << 16) +
+ (s->exponent << VFP_SINGLE_MANTISSA_BITS) +
+ (s->significand >> VFP_SINGLE_LOW_BITS);
+ return (s32)val;
+}
+
+#define VFP_NUMBER (1<<0)
+#define VFP_ZERO (1<<1)
+#define VFP_DENORMAL (1<<2)
+#define VFP_INFINITY (1<<3)
+#define VFP_NAN (1<<4)
+#define VFP_NAN_SIGNAL (1<<5)
+
+#define VFP_QNAN (VFP_NAN)
+#define VFP_SNAN (VFP_NAN|VFP_NAN_SIGNAL)
+
+static inline int vfp_single_type(struct vfp_single *s)
+{
+ int type = VFP_NUMBER;
+ if (s->exponent == 255) {
+ if (s->significand == 0)
+ type = VFP_INFINITY;
+ else if (s->significand & VFP_SINGLE_SIGNIFICAND_QNAN)
+ type = VFP_QNAN;
+ else
+ type = VFP_SNAN;
+ } else if (s->exponent == 0) {
+ if (s->significand == 0)
+ type |= VFP_ZERO;
+ else
+ type |= VFP_DENORMAL;
+ }
+ return type;
+}
+
+#ifndef DEBUG
+#define vfp_single_normaliseround(sd,vsd,fpscr,except,func) __vfp_single_normaliseround(sd,vsd,fpscr,except)
+u32 __vfp_single_normaliseround(int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions);
+#else
+u32 vfp_single_normaliseround(int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions, const char *func);
+#endif
+
+/*
+ * Double-precision
+ */
+struct vfp_double {
+ s16 exponent;
+ u16 sign;
+ u64 significand;
+};
+
+extern u64 vfp_get_double(unsigned int reg);
+extern void vfp_put_double(unsigned int reg, u64 val);
+
+#define VFP_DOUBLE_MANTISSA_BITS (52)
+#define VFP_DOUBLE_EXPONENT_BITS (11)
+#define VFP_DOUBLE_LOW_BITS (64 - VFP_DOUBLE_MANTISSA_BITS - 2)
+#define VFP_DOUBLE_LOW_BITS_MASK ((1 << VFP_DOUBLE_LOW_BITS) - 1)
+
+/*
+ * The bit in an unpacked double which indicates that it is a quiet NaN
+ */
+#define VFP_DOUBLE_SIGNIFICAND_QNAN (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1 + VFP_DOUBLE_LOW_BITS))
+
+/*
+ * Operations on packed single-precision numbers
+ */
+#define vfp_double_packed_sign(v) ((v) & (1ULL << 63))
+#define vfp_double_packed_negate(v) ((v) ^ (1ULL << 63))
+#define vfp_double_packed_abs(v) ((v) & ~(1ULL << 63))
+#define vfp_double_packed_exponent(v) (((v) >> VFP_DOUBLE_MANTISSA_BITS) & ((1 << VFP_DOUBLE_EXPONENT_BITS) - 1))
+#define vfp_double_packed_mantissa(v) ((v) & ((1ULL << VFP_DOUBLE_MANTISSA_BITS) - 1))
+
+/*
+ * Unpack a double-precision float. Note that this returns the magnitude
+ * of the double-precision float mantissa with the 1. if necessary,
+ * aligned to bit 62.
+ */
+static inline void vfp_double_unpack(struct vfp_double *s, s64 val)
+{
+ u64 significand;
+
+ s->sign = vfp_double_packed_sign(val) >> 48;
+ s->exponent = vfp_double_packed_exponent(val);
+
+ significand = (u64) val;
+ significand = (significand << (64 - VFP_DOUBLE_MANTISSA_BITS)) >> 2;
+ if (s->exponent && s->exponent != 2047)
+ significand |= (1ULL << 62);
+ s->significand = significand;
+}
+
+/*
+ * Re-pack a double-precision float. This assumes that the float is
+ * already normalised such that the MSB is bit 30, _not_ bit 31.
+ */
+static inline s64 vfp_double_pack(struct vfp_double *s)
+{
+ u64 val;
+ val = ((u64)s->sign << 48) +
+ ((u64)s->exponent << VFP_DOUBLE_MANTISSA_BITS) +
+ (s->significand >> VFP_DOUBLE_LOW_BITS);
+ return (s64)val;
+}
+
+static inline int vfp_double_type(struct vfp_double *s)
+{
+ int type = VFP_NUMBER;
+ if (s->exponent == 2047) {
+ if (s->significand == 0)
+ type = VFP_INFINITY;
+ else if (s->significand & VFP_DOUBLE_SIGNIFICAND_QNAN)
+ type = VFP_QNAN;
+ else
+ type = VFP_SNAN;
+ } else if (s->exponent == 0) {
+ if (s->significand == 0)
+ type |= VFP_ZERO;
+ else
+ type |= VFP_DENORMAL;
+ }
+ return type;
+}
+
+u32 vfp_double_normaliseround(int dd, struct vfp_double *vd, u32 fpscr, u32 exceptions, const char *func);
+
+/*
+ * System registers
+ */
+extern u32 vfp_get_sys(unsigned int reg);
+extern void vfp_put_sys(unsigned int reg, u32 val);
+
+u32 vfp_estimate_sqrt_significand(u32 exponent, u32 significand);
--- /dev/null
+/*
+ * linux/arch/arm/vfp/vfpdouble.c
+ *
+ * This code is derived in part from John R. Housers softfloat library, which
+ * carries the following notice:
+ *
+ * ===========================================================================
+ * This C source file is part of the SoftFloat IEC/IEEE Floating-point
+ * Arithmetic Package, Release 2.
+ *
+ * Written by John R. Hauser. This work was made possible in part by the
+ * International Computer Science Institute, located at Suite 600, 1947 Center
+ * Street, Berkeley, California 94704. Funding was partially provided by the
+ * National Science Foundation under grant MIP-9311980. The original version
+ * of this code was written as part of a project to build a fixed-point vector
+ * processor in collaboration with the University of California at Berkeley,
+ * overseen by Profs. Nelson Morgan and John Wawrzynek. More information
+ * is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
+ * arithmetic/softfloat.html'.
+ *
+ * THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort
+ * has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
+ * TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO
+ * PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
+ * AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
+ *
+ * Derivative works are acceptable, even for commercial purposes, so long as
+ * (1) they include prominent notice that the work is derivative, and (2) they
+ * include prominent notice akin to these three paragraphs for those parts of
+ * this code that are retained.
+ * ===========================================================================
+ */
+#include <linux/kernel.h>
+#include <asm/bitops.h>
+#include <asm/ptrace.h>
+#include <asm/vfp.h>
+
+#include "vfpinstr.h"
+#include "vfp.h"
+
+static struct vfp_double vfp_double_default_qnan = {
+ .exponent = 2047,
+ .sign = 0,
+ .significand = VFP_DOUBLE_SIGNIFICAND_QNAN,
+};
+
+static void vfp_double_dump(const char *str, struct vfp_double *d)
+{
+ pr_debug("VFP: %s: sign=%d exponent=%d significand=%016llx\n",
+ str, d->sign != 0, d->exponent, d->significand);
+}
+
+static void vfp_double_normalise_denormal(struct vfp_double *vd)
+{
+ int bits = 31 - fls(vd->significand >> 32);
+ if (bits == 31)
+ bits = 62 - fls(vd->significand);
+
+ vfp_double_dump("normalise_denormal: in", vd);
+
+ if (bits) {
+ vd->exponent -= bits - 1;
+ vd->significand <<= bits;
+ }
+
+ vfp_double_dump("normalise_denormal: out", vd);
+}
+
+u32 vfp_double_normaliseround(int dd, struct vfp_double *vd, u32 fpscr, u32 exceptions, const char *func)
+{
+ u64 significand, incr;
+ int exponent, shift, underflow;
+ u32 rmode;
+
+ vfp_double_dump("pack: in", vd);
+
+ /*
+ * Infinities and NaNs are a special case.
+ */
+ if (vd->exponent == 2047 && (vd->significand == 0 || exceptions))
+ goto pack;
+
+ /*
+ * Special-case zero.
+ */
+ if (vd->significand == 0) {
+ vd->exponent = 0;
+ goto pack;
+ }
+
+ exponent = vd->exponent;
+ significand = vd->significand;
+
+ shift = 32 - fls(significand >> 32);
+ if (shift == 32)
+ shift = 64 - fls(significand);
+ if (shift) {
+ exponent -= shift;
+ significand <<= shift;
+ }
+
+#ifdef DEBUG
+ vd->exponent = exponent;
+ vd->significand = significand;
+ vfp_double_dump("pack: normalised", vd);
+#endif
+
+ /*
+ * Tiny number?
+ */
+ underflow = exponent < 0;
+ if (underflow) {
+ significand = vfp_shiftright64jamming(significand, -exponent);
+ exponent = 0;
+#ifdef DEBUG
+ vd->exponent = exponent;
+ vd->significand = significand;
+ vfp_double_dump("pack: tiny number", vd);
+#endif
+ if (!(significand & ((1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1)))
+ underflow = 0;
+ }
+
+ /*
+ * Select rounding increment.
+ */
+ incr = 0;
+ rmode = fpscr & FPSCR_RMODE_MASK;
+
+ if (rmode == FPSCR_ROUND_NEAREST) {
+ incr = 1ULL << VFP_DOUBLE_LOW_BITS;
+ if ((significand & (1ULL << (VFP_DOUBLE_LOW_BITS + 1))) == 0)
+ incr -= 1;
+ } else if (rmode == FPSCR_ROUND_TOZERO) {
+ incr = 0;
+ } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vd->sign != 0))
+ incr = (1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1;
+
+ pr_debug("VFP: rounding increment = 0x%08llx\n", incr);
+
+ /*
+ * Is our rounding going to overflow?
+ */
+ if ((significand + incr) < significand) {
+ exponent += 1;
+ significand = (significand >> 1) | (significand & 1);
+ incr >>= 1;
+#ifdef DEBUG
+ vd->exponent = exponent;
+ vd->significand = significand;
+ vfp_double_dump("pack: overflow", vd);
+#endif
+ }
+
+ /*
+ * If any of the low bits (which will be shifted out of the
+ * number) are non-zero, the result is inexact.
+ */
+ if (significand & ((1 << (VFP_DOUBLE_LOW_BITS + 1)) - 1))
+ exceptions |= FPSCR_IXC;
+
+ /*
+ * Do our rounding.
+ */
+ significand += incr;
+
+ /*
+ * Infinity?
+ */
+ if (exponent >= 2046) {
+ exceptions |= FPSCR_OFC | FPSCR_IXC;
+ if (incr == 0) {
+ vd->exponent = 2045;
+ vd->significand = 0x7fffffffffffffffULL;
+ } else {
+ vd->exponent = 2047; /* infinity */
+ vd->significand = 0;
+ }
+ } else {
+ if (significand >> (VFP_DOUBLE_LOW_BITS + 1) == 0)
+ exponent = 0;
+ if (exponent || significand > 0x8000000000000000ULL)
+ underflow = 0;
+ if (underflow)
+ exceptions |= FPSCR_UFC;
+ vd->exponent = exponent;
+ vd->significand = significand >> 1;
+ }
+
+ pack:
+ vfp_double_dump("pack: final", vd);
+ {
+ s64 d = vfp_double_pack(vd);
+ pr_debug("VFP: %s: d(d%d)=%016llx exceptions=%08x\n", func,
+ dd, d, exceptions);
+ vfp_put_double(dd, d);
+ }
+ return exceptions;
+}
+
+/*
+ * Propagate the NaN, setting exceptions if it is signalling.
+ * 'n' is always a NaN. 'm' may be a number, NaN or infinity.
+ */
+static u32
+vfp_propagate_nan(struct vfp_double *vdd, struct vfp_double *vdn,
+ struct vfp_double *vdm, u32 fpscr)
+{
+ struct vfp_double *nan;
+ int tn, tm = 0;
+
+ tn = vfp_double_type(vdn);
+
+ if (vdm)
+ tm = vfp_double_type(vdm);
+
+ if (fpscr & FPSCR_DEFAULT_NAN)
+ /*
+ * Default NaN mode - always returns a quiet NaN
+ */
+ nan = &vfp_double_default_qnan;
+ else {
+ /*
+ * Contemporary mode - select the first signalling
+ * NAN, or if neither are signalling, the first
+ * quiet NAN.
+ */
+ if (tn == VFP_SNAN || (tm != VFP_SNAN && tn == VFP_QNAN))
+ nan = vdn;
+ else
+ nan = vdm;
+ /*
+ * Make the NaN quiet.
+ */
+ nan->significand |= VFP_DOUBLE_SIGNIFICAND_QNAN;
+ }
+
+ *vdd = *nan;
+
+ /*
+ * If one was a signalling NAN, raise invalid operation.
+ */
+ return tn == VFP_SNAN || tm == VFP_SNAN ? FPSCR_IOC : 0x100;
+}
+
+/*
+ * Extended operations
+ */
+static u32 vfp_double_fabs(int dd, int unused, int dm, u32 fpscr)
+{
+ vfp_put_double(dd, vfp_double_packed_abs(vfp_get_double(dm)));
+ return 0;
+}
+
+static u32 vfp_double_fcpy(int dd, int unused, int dm, u32 fpscr)
+{
+ vfp_put_double(dd, vfp_get_double(dm));
+ return 0;
+}
+
+static u32 vfp_double_fneg(int dd, int unused, int dm, u32 fpscr)
+{
+ vfp_put_double(dd, vfp_double_packed_negate(vfp_get_double(dm)));
+ return 0;
+}
+
+static u32 vfp_double_fsqrt(int dd, int unused, int dm, u32 fpscr)
+{
+ struct vfp_double vdm, vdd;
+ int ret, tm;
+
+ vfp_double_unpack(&vdm, vfp_get_double(dm));
+ tm = vfp_double_type(&vdm);
+ if (tm & (VFP_NAN|VFP_INFINITY)) {
+ struct vfp_double *vdp = &vdd;
+
+ if (tm & VFP_NAN)
+ ret = vfp_propagate_nan(vdp, &vdm, NULL, fpscr);
+ else if (vdm.sign == 0) {
+ sqrt_copy:
+ vdp = &vdm;
+ ret = 0;
+ } else {
+ sqrt_invalid:
+ vdp = &vfp_double_default_qnan;
+ ret = FPSCR_IOC;
+ }
+ vfp_put_double(dd, vfp_double_pack(vdp));
+ return ret;
+ }
+
+ /*
+ * sqrt(+/- 0) == +/- 0
+ */
+ if (tm & VFP_ZERO)
+ goto sqrt_copy;
+
+ /*
+ * Normalise a denormalised number
+ */
+ if (tm & VFP_DENORMAL)
+ vfp_double_normalise_denormal(&vdm);
+
+ /*
+ * sqrt(<0) = invalid
+ */
+ if (vdm.sign)
+ goto sqrt_invalid;
+
+ vfp_double_dump("sqrt", &vdm);
+
+ /*
+ * Estimate the square root.
+ */
+ vdd.sign = 0;
+ vdd.exponent = ((vdm.exponent - 1023) >> 1) + 1023;
+ vdd.significand = (u64)vfp_estimate_sqrt_significand(vdm.exponent, vdm.significand >> 32) << 31;
+
+ vfp_double_dump("sqrt estimate1", &vdd);
+
+ vdm.significand >>= 1 + (vdm.exponent & 1);
+ vdd.significand += 2 + vfp_estimate_div128to64(vdm.significand, 0, vdd.significand);
+
+ vfp_double_dump("sqrt estimate2", &vdd);
+
+ /*
+ * And now adjust.
+ */
+ if ((vdd.significand & VFP_DOUBLE_LOW_BITS_MASK) <= 5) {
+ if (vdd.significand < 2) {
+ vdd.significand = ~0ULL;
+ } else {
+ u64 termh, terml, remh, reml;
+ vdm.significand <<= 2;
+ mul64to128(&termh, &terml, vdd.significand, vdd.significand);
+ sub128(&remh, &reml, vdm.significand, 0, termh, terml);
+ while ((s64)remh < 0) {
+ vdd.significand -= 1;
+ shift64left(&termh, &terml, vdd.significand);
+ terml |= 1;
+ add128(&remh, &reml, remh, reml, termh, terml);
+ }
+ vdd.significand |= (remh | reml) != 0;
+ }
+ }
+ vdd.significand = vfp_shiftright64jamming(vdd.significand, 1);
+
+ return vfp_double_normaliseround(dd, &vdd, fpscr, 0, "fsqrt");
+}
+
+/*
+ * Equal := ZC
+ * Less than := N
+ * Greater than := C
+ * Unordered := CV
+ */
+static u32 vfp_compare(int dd, int signal_on_qnan, int dm, u32 fpscr)
+{
+ s64 d, m;
+ u32 ret = 0;
+
+ m = vfp_get_double(dm);
+ if (vfp_double_packed_exponent(m) == 2047 && vfp_double_packed_mantissa(m)) {
+ ret |= FPSCR_C | FPSCR_V;
+ if (signal_on_qnan || !(vfp_double_packed_mantissa(m) & (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1))))
+ /*
+ * Signalling NaN, or signalling on quiet NaN
+ */
+ ret |= FPSCR_IOC;
+ }
+
+ d = vfp_get_double(dd);
+ if (vfp_double_packed_exponent(d) == 2047 && vfp_double_packed_mantissa(d)) {
+ ret |= FPSCR_C | FPSCR_V;
+ if (signal_on_qnan || !(vfp_double_packed_mantissa(d) & (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1))))
+ /*
+ * Signalling NaN, or signalling on quiet NaN
+ */
+ ret |= FPSCR_IOC;
+ }
+
+ if (ret == 0) {
+ if (d == m || vfp_double_packed_abs(d | m) == 0) {
+ /*
+ * equal
+ */
+ ret |= FPSCR_Z | FPSCR_C;
+ } else if (vfp_double_packed_sign(d ^ m)) {
+ /*
+ * different signs
+ */
+ if (vfp_double_packed_sign(d))
+ /*
+ * d is negative, so d < m
+ */
+ ret |= FPSCR_N;
+ else
+ /*
+ * d is positive, so d > m
+ */
+ ret |= FPSCR_C;
+ } else if ((vfp_double_packed_sign(d) != 0) ^ (d < m)) {
+ /*
+ * d < m
+ */
+ ret |= FPSCR_N;
+ } else if ((vfp_double_packed_sign(d) != 0) ^ (d > m)) {
+ /*
+ * d > m
+ */
+ ret |= FPSCR_C;
+ }
+ }
+
+ return ret;
+}
+
+static u32 vfp_double_fcmp(int dd, int unused, int dm, u32 fpscr)
+{
+ return vfp_compare(dd, 0, dm, fpscr);
+}
+
+static u32 vfp_double_fcmpe(int dd, int unused, int dm, u32 fpscr)
+{
+ return vfp_compare(dd, 1, dm, fpscr);
+}
+
+static u32 vfp_double_fcmpz(int dd, int unused, int dm, u32 fpscr)
+{
+ return vfp_compare(dd, 0, -1, fpscr);
+}
+
+static u32 vfp_double_fcmpez(int dd, int unused, int dm, u32 fpscr)
+{
+ return vfp_compare(dd, 1, -1, fpscr);
+}
+
+static u32 vfp_double_fcvts(int sd, int unused, int dm, u32 fpscr)
+{
+ struct vfp_double vdm;
+ struct vfp_single vsd;
+ int tm;
+ u32 exceptions = 0;
+
+ vfp_double_unpack(&vdm, vfp_get_double(dm));
+
+ tm = vfp_double_type(&vdm);
+
+ /*
+ * If we have a signalling NaN, signal invalid operation.
+ */
+ if (tm == VFP_SNAN)
+ exceptions = FPSCR_IOC;
+
+ if (tm & VFP_DENORMAL)
+ vfp_double_normalise_denormal(&vdm);
+
+ vsd.sign = vdm.sign;
+ vsd.significand = vfp_hi64to32jamming(vdm.significand);
+
+ /*
+ * If we have an infinity or a NaN, the exponent must be 255
+ */
+ if (tm & (VFP_INFINITY|VFP_NAN)) {
+ vsd.exponent = 255;
+ if (tm & VFP_NAN)
+ vsd.significand |= VFP_SINGLE_SIGNIFICAND_QNAN;
+ goto pack_nan;
+ } else if (tm & VFP_ZERO)
+ vsd.exponent = 0;
+ else
+ vsd.exponent = vdm.exponent - (1023 - 127);
+
+ return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fcvts");
+
+ pack_nan:
+ vfp_put_float(sd, vfp_single_pack(&vsd));
+ return exceptions;
+}
+
+static u32 vfp_double_fuito(int dd, int unused, int dm, u32 fpscr)
+{
+ struct vfp_double vdm;
+ u32 m = vfp_get_float(dm);
+
+ vdm.sign = 0;
+ vdm.exponent = 1023 + 63 - 1;
+ vdm.significand = (u64)m;
+
+ return vfp_double_normaliseround(dd, &vdm, fpscr, 0, "fuito");
+}
+
+static u32 vfp_double_fsito(int dd, int unused, int dm, u32 fpscr)
+{
+ struct vfp_double vdm;
+ u32 m = vfp_get_float(dm);
+
+ vdm.sign = (m & 0x80000000) >> 16;
+ vdm.exponent = 1023 + 63 - 1;
+ vdm.significand = vdm.sign ? -m : m;
+
+ return vfp_double_normaliseround(dd, &vdm, fpscr, 0, "fsito");
+}
+
+static u32 vfp_double_ftoui(int sd, int unused, int dm, u32 fpscr)
+{
+ struct vfp_double vdm;
+ u32 d, exceptions = 0;
+ int rmode = fpscr & FPSCR_RMODE_MASK;
+ int tm;
+
+ vfp_double_unpack(&vdm, vfp_get_double(dm));
+
+ /*
+ * Do we have a denormalised number?
+ */
+ tm = vfp_double_type(&vdm);
+ if (tm & VFP_DENORMAL)
+ exceptions |= FPSCR_IDC;
+
+ if (tm & VFP_NAN)
+ vdm.sign = 0;
+
+ if (vdm.exponent >= 1023 + 32) {
+ d = vdm.sign ? 0 : 0xffffffff;
+ exceptions = FPSCR_IOC;
+ } else if (vdm.exponent >= 1023 - 1) {
+ int shift = 1023 + 63 - vdm.exponent;
+ u64 rem, incr = 0;
+
+ /*
+ * 2^0 <= m < 2^32-2^8
+ */
+ d = (vdm.significand << 1) >> shift;
+ rem = vdm.significand << (65 - shift);
+
+ if (rmode == FPSCR_ROUND_NEAREST) {
+ incr = 0x8000000000000000ULL;
+ if ((d & 1) == 0)
+ incr -= 1;
+ } else if (rmode == FPSCR_ROUND_TOZERO) {
+ incr = 0;
+ } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vdm.sign != 0)) {
+ incr = ~0ULL;
+ }
+
+ if ((rem + incr) < rem) {
+ if (d < 0xffffffff)
+ d += 1;
+ else
+ exceptions |= FPSCR_IOC;
+ }
+
+ if (d && vdm.sign) {
+ d = 0;
+ exceptions |= FPSCR_IOC;
+ } else if (rem)
+ exceptions |= FPSCR_IXC;
+ } else {
+ d = 0;
+ if (vdm.exponent | vdm.significand) {
+ exceptions |= FPSCR_IXC;
+ if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0)
+ d = 1;
+ else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign) {
+ d = 0;
+ exceptions |= FPSCR_IOC;
+ }
+ }
+ }
+
+ pr_debug("VFP: ftoui: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
+
+ vfp_put_float(sd, d);
+
+ return exceptions;
+}
+
+static u32 vfp_double_ftouiz(int sd, int unused, int dm, u32 fpscr)
+{
+ return vfp_double_ftoui(sd, unused, dm, FPSCR_ROUND_TOZERO);
+}
+
+static u32 vfp_double_ftosi(int sd, int unused, int dm, u32 fpscr)
+{
+ struct vfp_double vdm;
+ u32 d, exceptions = 0;
+ int rmode = fpscr & FPSCR_RMODE_MASK;
+
+ vfp_double_unpack(&vdm, vfp_get_double(dm));
+ vfp_double_dump("VDM", &vdm);
+
+ /*
+ * Do we have denormalised number?
+ */
+ if (vfp_double_type(&vdm) & VFP_DENORMAL)
+ exceptions |= FPSCR_IDC;
+
+ if (vdm.exponent >= 1023 + 32) {
+ d = 0x7fffffff;
+ if (vdm.sign)
+ d = ~d;
+ exceptions |= FPSCR_IOC;
+ } else if (vdm.exponent >= 1023 - 1) {
+ int shift = 1023 + 63 - vdm.exponent; /* 58 */
+ u64 rem, incr = 0;
+
+ d = (vdm.significand << 1) >> shift;
+ rem = vdm.significand << (65 - shift);
+
+ if (rmode == FPSCR_ROUND_NEAREST) {
+ incr = 0x8000000000000000ULL;
+ if ((d & 1) == 0)
+ incr -= 1;
+ } else if (rmode == FPSCR_ROUND_TOZERO) {
+ incr = 0;
+ } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vdm.sign != 0)) {
+ incr = ~0ULL;
+ }
+
+ if ((rem + incr) < rem && d < 0xffffffff)
+ d += 1;
+ if (d > 0x7fffffff + (vdm.sign != 0)) {
+ d = 0x7fffffff + (vdm.sign != 0);
+ exceptions |= FPSCR_IOC;
+ } else if (rem)
+ exceptions |= FPSCR_IXC;
+
+ if (vdm.sign)
+ d = -d;
+ } else {
+ d = 0;
+ if (vdm.exponent | vdm.significand) {
+ exceptions |= FPSCR_IXC;
+ if (rmode == FPSCR_ROUND_PLUSINF && vdm.sign == 0)
+ d = 1;
+ else if (rmode == FPSCR_ROUND_MINUSINF && vdm.sign)
+ d = -1;
+ }
+ }
+
+ pr_debug("VFP: ftosi: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
+
+ vfp_put_float(sd, (s32)d);
+
+ return exceptions;
+}
+
+static u32 vfp_double_ftosiz(int dd, int unused, int dm, u32 fpscr)
+{
+ return vfp_double_ftosi(dd, unused, dm, FPSCR_ROUND_TOZERO);
+}
+
+
+static u32 (* const fop_extfns[32])(int dd, int unused, int dm, u32 fpscr) = {
+ [FEXT_TO_IDX(FEXT_FCPY)] = vfp_double_fcpy,
+ [FEXT_TO_IDX(FEXT_FABS)] = vfp_double_fabs,
+ [FEXT_TO_IDX(FEXT_FNEG)] = vfp_double_fneg,
+ [FEXT_TO_IDX(FEXT_FSQRT)] = vfp_double_fsqrt,
+ [FEXT_TO_IDX(FEXT_FCMP)] = vfp_double_fcmp,
+ [FEXT_TO_IDX(FEXT_FCMPE)] = vfp_double_fcmpe,
+ [FEXT_TO_IDX(FEXT_FCMPZ)] = vfp_double_fcmpz,
+ [FEXT_TO_IDX(FEXT_FCMPEZ)] = vfp_double_fcmpez,
+ [FEXT_TO_IDX(FEXT_FCVT)] = vfp_double_fcvts,
+ [FEXT_TO_IDX(FEXT_FUITO)] = vfp_double_fuito,
+ [FEXT_TO_IDX(FEXT_FSITO)] = vfp_double_fsito,
+ [FEXT_TO_IDX(FEXT_FTOUI)] = vfp_double_ftoui,
+ [FEXT_TO_IDX(FEXT_FTOUIZ)] = vfp_double_ftouiz,
+ [FEXT_TO_IDX(FEXT_FTOSI)] = vfp_double_ftosi,
+ [FEXT_TO_IDX(FEXT_FTOSIZ)] = vfp_double_ftosiz,
+};
+
+
+
+
+static u32
+vfp_double_fadd_nonnumber(struct vfp_double *vdd, struct vfp_double *vdn,
+ struct vfp_double *vdm, u32 fpscr)
+{
+ struct vfp_double *vdp;
+ u32 exceptions = 0;
+ int tn, tm;
+
+ tn = vfp_double_type(vdn);
+ tm = vfp_double_type(vdm);
+
+ if (tn & tm & VFP_INFINITY) {
+ /*
+ * Two infinities. Are they different signs?
+ */
+ if (vdn->sign ^ vdm->sign) {
+ /*
+ * different signs -> invalid
+ */
+ exceptions = FPSCR_IOC;
+ vdp = &vfp_double_default_qnan;
+ } else {
+ /*
+ * same signs -> valid
+ */
+ vdp = vdn;
+ }
+ } else if (tn & VFP_INFINITY && tm & VFP_NUMBER) {
+ /*
+ * One infinity and one number -> infinity
+ */
+ vdp = vdn;
+ } else {
+ /*
+ * 'n' is a NaN of some type
+ */
+ return vfp_propagate_nan(vdd, vdn, vdm, fpscr);
+ }
+ *vdd = *vdp;
+ return exceptions;
+}
+
+static u32
+vfp_double_add(struct vfp_double *vdd, struct vfp_double *vdn,
+ struct vfp_double *vdm, u32 fpscr)
+{
+ u32 exp_diff;
+ u64 m_sig;
+
+ if (vdn->significand & (1ULL << 63) ||
+ vdm->significand & (1ULL << 63)) {
+ pr_info("VFP: bad FP values in %s\n", __func__);
+ vfp_double_dump("VDN", vdn);
+ vfp_double_dump("VDM", vdm);
+ }
+
+ /*
+ * Ensure that 'n' is the largest magnitude number. Note that
+ * if 'n' and 'm' have equal exponents, we do not swap them.
+ * This ensures that NaN propagation works correctly.
+ */
+ if (vdn->exponent < vdm->exponent) {
+ struct vfp_double *t = vdn;
+ vdn = vdm;
+ vdm = t;
+ }
+
+ /*
+ * Is 'n' an infinity or a NaN? Note that 'm' may be a number,
+ * infinity or a NaN here.
+ */
+ if (vdn->exponent == 2047)
+ return vfp_double_fadd_nonnumber(vdd, vdn, vdm, fpscr);
+
+ /*
+ * We have two proper numbers, where 'vdn' is the larger magnitude.
+ *
+ * Copy 'n' to 'd' before doing the arithmetic.
+ */
+ *vdd = *vdn;
+
+ /*
+ * Align 'm' with the result.
+ */
+ exp_diff = vdn->exponent - vdm->exponent;
+ m_sig = vfp_shiftright64jamming(vdm->significand, exp_diff);
+
+ /*
+ * If the signs are different, we are really subtracting.
+ */
+ if (vdn->sign ^ vdm->sign) {
+ m_sig = vdn->significand - m_sig;
+ if ((s64)m_sig < 0) {
+ vdd->sign = vfp_sign_negate(vdd->sign);
+ m_sig = -m_sig;
+ }
+ } else {
+ m_sig += vdn->significand;
+ }
+ vdd->significand = m_sig;
+
+ return 0;
+}
+
+static u32
+vfp_double_multiply(struct vfp_double *vdd, struct vfp_double *vdn,
+ struct vfp_double *vdm, u32 fpscr)
+{
+ vfp_double_dump("VDN", vdn);
+ vfp_double_dump("VDM", vdm);
+
+ /*
+ * Ensure that 'n' is the largest magnitude number. Note that
+ * if 'n' and 'm' have equal exponents, we do not swap them.
+ * This ensures that NaN propagation works correctly.
+ */
+ if (vdn->exponent < vdm->exponent) {
+ struct vfp_double *t = vdn;
+ vdn = vdm;
+ vdm = t;
+ pr_debug("VFP: swapping M <-> N\n");
+ }
+
+ vdd->sign = vdn->sign ^ vdm->sign;
+
+ /*
+ * If 'n' is an infinity or NaN, handle it. 'm' may be anything.
+ */
+ if (vdn->exponent == 2047) {
+ if (vdn->significand || (vdm->exponent == 2047 && vdm->significand))
+ return vfp_propagate_nan(vdd, vdn, vdm, fpscr);
+ if ((vdm->exponent | vdm->significand) == 0) {
+ *vdd = vfp_double_default_qnan;
+ return FPSCR_IOC;
+ }
+ vdd->exponent = vdn->exponent;
+ vdd->significand = 0;
+ return 0;
+ }
+
+ /*
+ * If 'm' is zero, the result is always zero. In this case,
+ * 'n' may be zero or a number, but it doesn't matter which.
+ */
+ if ((vdm->exponent | vdm->significand) == 0) {
+ vdd->exponent = 0;
+ vdd->significand = 0;
+ return 0;
+ }
+
+ /*
+ * We add 2 to the destination exponent for the same reason
+ * as the addition case - though this time we have +1 from
+ * each input operand.
+ */
+ vdd->exponent = vdn->exponent + vdm->exponent - 1023 + 2;
+ vdd->significand = vfp_hi64multiply64(vdn->significand, vdm->significand);
+
+ vfp_double_dump("VDD", vdd);
+ return 0;
+}
+
+#define NEG_MULTIPLY (1 << 0)
+#define NEG_SUBTRACT (1 << 1)
+
+static u32
+vfp_double_multiply_accumulate(int dd, int dn, int dm, u32 fpscr, u32 negate, char *func)
+{
+ struct vfp_double vdd, vdp, vdn, vdm;
+ u32 exceptions;
+
+ vfp_double_unpack(&vdn, vfp_get_double(dn));
+ if (vdn.exponent == 0 && vdn.significand)
+ vfp_double_normalise_denormal(&vdn);
+
+ vfp_double_unpack(&vdm, vfp_get_double(dm));
+ if (vdm.exponent == 0 && vdm.significand)
+ vfp_double_normalise_denormal(&vdm);
+
+ exceptions = vfp_double_multiply(&vdp, &vdn, &vdm, fpscr);
+ if (negate & NEG_MULTIPLY)
+ vdp.sign = vfp_sign_negate(vdp.sign);
+
+ vfp_double_unpack(&vdn, vfp_get_double(dd));
+ if (negate & NEG_SUBTRACT)
+ vdn.sign = vfp_sign_negate(vdn.sign);
+
+ exceptions |= vfp_double_add(&vdd, &vdn, &vdp, fpscr);
+
+ return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, func);
+}
+
+/*
+ * Standard operations
+ */
+
+/*
+ * sd = sd + (sn * sm)
+ */
+static u32 vfp_double_fmac(int dd, int dn, int dm, u32 fpscr)
+{
+ return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, 0, "fmac");
+}
+
+/*
+ * sd = sd - (sn * sm)
+ */
+static u32 vfp_double_fnmac(int dd, int dn, int dm, u32 fpscr)
+{
+ return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_MULTIPLY, "fnmac");
+}
+
+/*
+ * sd = -sd + (sn * sm)
+ */
+static u32 vfp_double_fmsc(int dd, int dn, int dm, u32 fpscr)
+{
+ return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_SUBTRACT, "fmsc");
+}
+
+/*
+ * sd = -sd - (sn * sm)
+ */
+static u32 vfp_double_fnmsc(int dd, int dn, int dm, u32 fpscr)
+{
+ return vfp_double_multiply_accumulate(dd, dn, dm, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc");
+}
+
+/*
+ * sd = sn * sm
+ */
+static u32 vfp_double_fmul(int dd, int dn, int dm, u32 fpscr)
+{
+ struct vfp_double vdd, vdn, vdm;
+ u32 exceptions;
+
+ vfp_double_unpack(&vdn, vfp_get_double(dn));
+ if (vdn.exponent == 0 && vdn.significand)
+ vfp_double_normalise_denormal(&vdn);
+
+ vfp_double_unpack(&vdm, vfp_get_double(dm));
+ if (vdm.exponent == 0 && vdm.significand)
+ vfp_double_normalise_denormal(&vdm);
+
+ exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr);
+ return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fmul");
+}
+
+/*
+ * sd = -(sn * sm)
+ */
+static u32 vfp_double_fnmul(int dd, int dn, int dm, u32 fpscr)
+{
+ struct vfp_double vdd, vdn, vdm;
+ u32 exceptions;
+
+ vfp_double_unpack(&vdn, vfp_get_double(dn));
+ if (vdn.exponent == 0 && vdn.significand)
+ vfp_double_normalise_denormal(&vdn);
+
+ vfp_double_unpack(&vdm, vfp_get_double(dm));
+ if (vdm.exponent == 0 && vdm.significand)
+ vfp_double_normalise_denormal(&vdm);
+
+ exceptions = vfp_double_multiply(&vdd, &vdn, &vdm, fpscr);
+ vdd.sign = vfp_sign_negate(vdd.sign);
+
+ return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fnmul");
+}
+
+/*
+ * sd = sn + sm
+ */
+static u32 vfp_double_fadd(int dd, int dn, int dm, u32 fpscr)
+{
+ struct vfp_double vdd, vdn, vdm;
+ u32 exceptions;
+
+ vfp_double_unpack(&vdn, vfp_get_double(dn));
+ if (vdn.exponent == 0 && vdn.significand)
+ vfp_double_normalise_denormal(&vdn);
+
+ vfp_double_unpack(&vdm, vfp_get_double(dm));
+ if (vdm.exponent == 0 && vdm.significand)
+ vfp_double_normalise_denormal(&vdm);
+
+ exceptions = vfp_double_add(&vdd, &vdn, &vdm, fpscr);
+
+ return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fadd");
+}
+
+/*
+ * sd = sn - sm
+ */
+static u32 vfp_double_fsub(int dd, int dn, int dm, u32 fpscr)
+{
+ struct vfp_double vdd, vdn, vdm;
+ u32 exceptions;
+
+ vfp_double_unpack(&vdn, vfp_get_double(dn));
+ if (vdn.exponent == 0 && vdn.significand)
+ vfp_double_normalise_denormal(&vdn);
+
+ vfp_double_unpack(&vdm, vfp_get_double(dm));
+ if (vdm.exponent == 0 && vdm.significand)
+ vfp_double_normalise_denormal(&vdm);
+
+ /*
+ * Subtraction is like addition, but with a negated operand.
+ */
+ vdm.sign = vfp_sign_negate(vdm.sign);
+
+ exceptions = vfp_double_add(&vdd, &vdn, &vdm, fpscr);
+
+ return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fsub");
+}
+
+/*
+ * sd = sn / sm
+ */
+static u32 vfp_double_fdiv(int dd, int dn, int dm, u32 fpscr)
+{
+ struct vfp_double vdd, vdn, vdm;
+ u32 exceptions = 0;
+ int tm, tn;
+
+ vfp_double_unpack(&vdn, vfp_get_double(dn));
+ vfp_double_unpack(&vdm, vfp_get_double(dm));
+
+ vdd.sign = vdn.sign ^ vdm.sign;
+
+ tn = vfp_double_type(&vdn);
+ tm = vfp_double_type(&vdm);
+
+ /*
+ * Is n a NAN?
+ */
+ if (tn & VFP_NAN)
+ goto vdn_nan;
+
+ /*
+ * Is m a NAN?
+ */
+ if (tm & VFP_NAN)
+ goto vdm_nan;
+
+ /*
+ * If n and m are infinity, the result is invalid
+ * If n and m are zero, the result is invalid
+ */
+ if (tm & tn & (VFP_INFINITY|VFP_ZERO))
+ goto invalid;
+
+ /*
+ * If n is infinity, the result is infinity
+ */
+ if (tn & VFP_INFINITY)
+ goto infinity;
+
+ /*
+ * If m is zero, raise div0 exceptions
+ */
+ if (tm & VFP_ZERO)
+ goto divzero;
+
+ /*
+ * If m is infinity, or n is zero, the result is zero
+ */
+ if (tm & VFP_INFINITY || tn & VFP_ZERO)
+ goto zero;
+
+ if (tn & VFP_DENORMAL)
+ vfp_double_normalise_denormal(&vdn);
+ if (tm & VFP_DENORMAL)
+ vfp_double_normalise_denormal(&vdm);
+
+ /*
+ * Ok, we have two numbers, we can perform division.
+ */
+ vdd.exponent = vdn.exponent - vdm.exponent + 1023 - 1;
+ vdm.significand <<= 1;
+ if (vdm.significand <= (2 * vdn.significand)) {
+ vdn.significand >>= 1;
+ vdd.exponent++;
+ }
+ vdd.significand = vfp_estimate_div128to64(vdn.significand, 0, vdm.significand);
+ if ((vdd.significand & 0x1ff) <= 2) {
+ u64 termh, terml, remh, reml;
+ mul64to128(&termh, &terml, vdm.significand, vdd.significand);
+ sub128(&remh, &reml, vdn.significand, 0, termh, terml);
+ while ((s64)remh < 0) {
+ vdd.significand -= 1;
+ add128(&remh, &reml, remh, reml, 0, vdm.significand);
+ }
+ vdd.significand |= (reml != 0);
+ }
+ return vfp_double_normaliseround(dd, &vdd, fpscr, 0, "fdiv");
+
+ vdn_nan:
+ exceptions = vfp_propagate_nan(&vdd, &vdn, &vdm, fpscr);
+ pack:
+ vfp_put_double(dd, vfp_double_pack(&vdd));
+ return exceptions;
+
+ vdm_nan:
+ exceptions = vfp_propagate_nan(&vdd, &vdm, &vdn, fpscr);
+ goto pack;
+
+ zero:
+ vdd.exponent = 0;
+ vdd.significand = 0;
+ goto pack;
+
+ divzero:
+ exceptions = FPSCR_DZC;
+ infinity:
+ vdd.exponent = 2047;
+ vdd.significand = 0;
+ goto pack;
+
+ invalid:
+ vfp_put_double(dd, vfp_double_pack(&vfp_double_default_qnan));
+ return FPSCR_IOC;
+}
+
+static u32 (* const fop_fns[16])(int dd, int dn, int dm, u32 fpscr) = {
+ [FOP_TO_IDX(FOP_FMAC)] = vfp_double_fmac,
+ [FOP_TO_IDX(FOP_FNMAC)] = vfp_double_fnmac,
+ [FOP_TO_IDX(FOP_FMSC)] = vfp_double_fmsc,
+ [FOP_TO_IDX(FOP_FNMSC)] = vfp_double_fnmsc,
+ [FOP_TO_IDX(FOP_FMUL)] = vfp_double_fmul,
+ [FOP_TO_IDX(FOP_FNMUL)] = vfp_double_fnmul,
+ [FOP_TO_IDX(FOP_FADD)] = vfp_double_fadd,
+ [FOP_TO_IDX(FOP_FSUB)] = vfp_double_fsub,
+ [FOP_TO_IDX(FOP_FDIV)] = vfp_double_fdiv,
+};
+
+#define FREG_BANK(x) ((x) & 0x0c)
+#define FREG_IDX(x) ((x) & 3)
+
+u32 vfp_double_cpdo(u32 inst, u32 fpscr)
+{
+ u32 op = inst & FOP_MASK;
+ u32 exceptions = 0;
+ unsigned int dd = vfp_get_sd(inst);
+ unsigned int dn = vfp_get_sn(inst);
+ unsigned int dm = vfp_get_sm(inst);
+ unsigned int vecitr, veclen, vecstride;
+ u32 (*fop)(int, int, s32, u32);
+
+ veclen = fpscr & FPSCR_LENGTH_MASK;
+ vecstride = (1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK)) * 2;
+
+ /*
+ * If destination bank is zero, vector length is always '1'.
+ * ARM DDI0100F C5.1.3, C5.3.2.
+ */
+ if (FREG_BANK(dd) == 0)
+ veclen = 0;
+
+ pr_debug("VFP: vecstride=%u veclen=%u\n", vecstride,
+ (veclen >> FPSCR_LENGTH_BIT) + 1);
+
+ fop = (op == FOP_EXT) ? fop_extfns[dn] : fop_fns[FOP_TO_IDX(op)];
+ if (!fop)
+ goto invalid;
+
+ for (vecitr = 0; vecitr <= veclen; vecitr += 1 << FPSCR_LENGTH_BIT) {
+ u32 except;
+
+ if (op == FOP_EXT)
+ pr_debug("VFP: itr%d (d%u.%u) = op[%u] (d%u.%u)\n",
+ vecitr >> FPSCR_LENGTH_BIT,
+ dd >> 1, dd & 1, dn,
+ dm >> 1, dm & 1);
+ else
+ pr_debug("VFP: itr%d (d%u.%u) = (d%u.%u) op[%u] (d%u.%u)\n",
+ vecitr >> FPSCR_LENGTH_BIT,
+ dd >> 1, dd & 1,
+ dn >> 1, dn & 1,
+ FOP_TO_IDX(op),
+ dm >> 1, dm & 1);
+
+ except = fop(dd, dn, dm, fpscr);
+ pr_debug("VFP: itr%d: exceptions=%08x\n",
+ vecitr >> FPSCR_LENGTH_BIT, except);
+
+ exceptions |= except;
+
+ /*
+ * This ensures that comparisons only operate on scalars;
+ * comparisons always return with one FPSCR status bit set.
+ */
+ if (except & (FPSCR_N|FPSCR_Z|FPSCR_C|FPSCR_V))
+ break;
+
+ /*
+ * CHECK: It appears to be undefined whether we stop when
+ * we encounter an exception. We continue.
+ */
+
+ dd = FREG_BANK(dd) + ((FREG_IDX(dd) + vecstride) & 6);
+ dn = FREG_BANK(dn) + ((FREG_IDX(dn) + vecstride) & 6);
+ if (FREG_BANK(dm) != 0)
+ dm = FREG_BANK(dm) + ((FREG_IDX(dm) + vecstride) & 6);
+ }
+ return exceptions;
+
+ invalid:
+ return ~0;
+}
--- /dev/null
+/*
+ * linux/arch/arm/vfp/vfphw.S
+ *
+ * Copyright (C) 2004 ARM Limited.
+ * Written by Deep Blue Solutions Limited.
+ *
+ * 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 code is called from the kernel's undefined instruction trap.
+ * r9 holds the return address for successful handling.
+ * lr holds the return address for unrecognised instructions.
+ * r10 points at the start of the private FP workspace in the thread structure
+ * sp points to a struct pt_regs (as defined in include/asm/proc/ptrace.h)
+ */
+#include <asm/thread_info.h>
+#include <asm/vfpmacros.h>
+#include "../kernel/entry-header.S"
+
+ .macro DBGSTR, str
+#ifdef DEBUG
+ stmfd sp!, {r0-r3, ip, lr}
+ add r0, pc, #4
+ bl printk
+ b 1f
+ .asciz "<7>VFP: \str\n"
+ .balign 4
+1: ldmfd sp!, {r0-r3, ip, lr}
+#endif
+ .endm
+
+ .macro DBGSTR1, str, arg
+#ifdef DEBUG
+ stmfd sp!, {r0-r3, ip, lr}
+ mov r1, \arg
+ add r0, pc, #4
+ bl printk
+ b 1f
+ .asciz "<7>VFP: \str\n"
+ .balign 4
+1: ldmfd sp!, {r0-r3, ip, lr}
+#endif
+ .endm
+
+ .macro DBGSTR3, str, arg1, arg2, arg3
+#ifdef DEBUG
+ stmfd sp!, {r0-r3, ip, lr}
+ mov r3, \arg3
+ mov r2, \arg2
+ mov r1, \arg1
+ add r0, pc, #4
+ bl printk
+ b 1f
+ .asciz "<7>VFP: \str\n"
+ .balign 4
+1: ldmfd sp!, {r0-r3, ip, lr}
+#endif
+ .endm
+
+
+@ VFP hardware support entry point.
+@
+@ r0 = faulted instruction
+@ r5 = faulted PC+4
+@ r9 = successful return
+@ r10 = vfp_state union
+@ lr = failure return
+
+ .globl vfp_support_entry
+vfp_support_entry:
+ DBGSTR3 "instr %08x pc %08x state %p", r0, r5, r10
+
+ VFPFMRX r1, FPEXC @ Is the VFP enabled?
+ DBGSTR1 "fpexc %08x", r1
+ tst r1, #FPEXC_ENABLE
+ bne look_for_VFP_exceptions @ VFP is already enabled
+
+ DBGSTR1 "enable %x", r10
+ ldr r3, last_VFP_context_address
+ orr r1, r1, #FPEXC_ENABLE @ user FPEXC has the enable bit set
+ ldr r4, [r3] @ last_VFP_context pointer
+ bic r2, r1, #FPEXC_EXCEPTION @ make sure exceptions are disabled
+ cmp r4, r10
+ beq check_for_exception @ we are returning to the same
+ @ process, so the registers are
+ @ still there. In this case, we do
+ @ not want to drop a pending exception.
+
+ VFPFMXR FPEXC, r2 @ enable VFP, disable any pending
+ @ exceptions, so we can get at the
+ @ rest of it
+
+ @ Save out the current registers to the old thread state
+
+ DBGSTR1 "save old state %p", r4
+ cmp r4, #0
+ beq no_old_VFP_process
+ VFPFMRX r2, FPSCR @ current status
+ VFPFMRX r6, FPINST @ FPINST (always there, rev0 onwards)
+ tst r1, #FPEXC_FPV2 @ is there an FPINST2 to read?
+ VFPFMRX r8, FPINST2, NE @ FPINST2 if needed - avoids reading
+ @ nonexistant reg on rev0
+ VFPFSTMIA r4 @ save the working registers
+ add r4, r4, #8*16+4
+ stmia r4, {r1, r2, r6, r8} @ save FPEXC, FPSCR, FPINST, FPINST2
+ @ and point r4 at the word at the
+ @ start of the register dump
+
+no_old_VFP_process:
+ DBGSTR1 "load state %p", r10
+ str r10, [r3] @ update the last_VFP_context pointer
+ @ Load the saved state back into the VFP
+ add r4, r10, #8*16+4
+ ldmia r4, {r1, r2, r6, r8} @ load FPEXC, FPSCR, FPINST, FPINST2
+ VFPFLDMIA r10 @ reload the working registers while
+ @ FPEXC is in a safe state
+ tst r1, #FPEXC_FPV2 @ is there an FPINST2 to write?
+ VFPFMXR FPINST2, r8, NE @ FPINST2 if needed - avoids writing
+ @ nonexistant reg on rev0
+ VFPFMXR FPINST, r6
+ VFPFMXR FPSCR, r2 @ restore status
+
+check_for_exception:
+ tst r1, #FPEXC_EXCEPTION
+ bne process_exception @ might as well handle the pending
+ @ exception before retrying branch
+ @ out before setting an FPEXC that
+ @ stops us reading stuff
+ VFPFMXR FPEXC, r1 @ restore FPEXC last
+ sub r5, r5, #4
+ str r5, [sp, #S_PC] @ retry the instruction
+ mov pc, r9 @ we think we have handled things
+
+
+look_for_VFP_exceptions:
+ tst r1, #FPEXC_EXCEPTION
+ bne process_exception
+ VFPFMRX r2, FPSCR
+ tst r2, #FPSCR_IXE @ IXE doesn't set FPEXC_EXCEPTION !
+ bne process_exception
+
+ @ Fall into hand on to next handler - appropriate coproc instr
+ @ not recognised by VFP
+
+ DBGSTR "not VFP"
+ mov pc, lr
+
+process_exception:
+ DBGSTR "bounce"
+ sub r5, r5, #4
+ str r5, [sp, #S_PC] @ retry the instruction on exit from
+ @ the imprecise exception handling in
+ @ the support code
+ mov r2, sp @ nothing stacked - regdump is at TOS
+ mov lr, r9 @ setup for a return to the user code.
+
+ @ Now call the C code to package up the bounce to the support code
+ @ r0 holds the trigger instruction
+ @ r1 holds the FPEXC value
+ @ r2 pointer to register dump
+ b VFP9_bounce @ we have handled this - the support
+ @ code will raise an exception if
+ @ required. If not, the user code will
+ @ retry the faulted instruction
+
+last_VFP_context_address:
+ .word last_VFP_context
+
+ .globl vfp_get_float
+vfp_get_float:
+ add pc, pc, r0, lsl #3
+ mov r0, r0
+ .irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
+ mrc p10, 0, r0, c\dr, c0, 0 @ fmrs r0, s0
+ mov pc, lr
+ mrc p10, 0, r0, c\dr, c0, 4 @ fmrs r0, s1
+ mov pc, lr
+ .endr
+
+ .globl vfp_put_float
+vfp_put_float:
+ add pc, pc, r0, lsl #3
+ mov r0, r0
+ .irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
+ mcr p10, 0, r1, c\dr, c0, 0 @ fmsr r0, s0
+ mov pc, lr
+ mcr p10, 0, r1, c\dr, c0, 4 @ fmsr r0, s1
+ mov pc, lr
+ .endr
+
+ .globl vfp_get_double
+vfp_get_double:
+ mov r0, r0, lsr #1
+ add pc, pc, r0, lsl #3
+ mov r0, r0
+ .irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
+ mrrc p10, 1, r0, r1, c\dr @ fmrrd r0, r1, d\dr
+ mov pc, lr
+ .endr
+
+ .globl vfp_put_double
+vfp_put_double:
+ mov r0, r0, lsr #1
+ add pc, pc, r0, lsl #3
+ mov r0, r0
+ .irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
+ mcrr p10, 1, r1, r2, c\dr @ fmrrd r1, r2, d\dr
+ mov pc, lr
+ .endr
--- /dev/null
+/*
+ * linux/arch/arm/vfp/vfpinstr.h
+ *
+ * Copyright (C) 2004 ARM Limited.
+ * Written by Deep Blue Solutions Limited.
+ *
+ * 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.
+ *
+ * VFP instruction masks.
+ */
+#define INST_CPRTDO(inst) (((inst) & 0x0f000000) == 0x0e000000)
+#define INST_CPRT(inst) ((inst) & (1 << 4))
+#define INST_CPRT_L(inst) ((inst) & (1 << 20))
+#define INST_CPRT_Rd(inst) (((inst) & (15 << 12)) >> 12)
+#define INST_CPRT_OP(inst) (((inst) >> 21) & 7)
+#define INST_CPNUM(inst) ((inst) & 0xf00)
+#define CPNUM(cp) ((cp) << 8)
+
+#define FOP_MASK (0x00b00040)
+#define FOP_FMAC (0x00000000)
+#define FOP_FNMAC (0x00000040)
+#define FOP_FMSC (0x00100000)
+#define FOP_FNMSC (0x00100040)
+#define FOP_FMUL (0x00200000)
+#define FOP_FNMUL (0x00200040)
+#define FOP_FADD (0x00300000)
+#define FOP_FSUB (0x00300040)
+#define FOP_FDIV (0x00800000)
+#define FOP_EXT (0x00b00040)
+
+#define FOP_TO_IDX(inst) ((inst & 0x00b00000) >> 20 | (inst & (1 << 6)) >> 4)
+
+#define FEXT_MASK (0x000f0080)
+#define FEXT_FCPY (0x00000000)
+#define FEXT_FABS (0x00000080)
+#define FEXT_FNEG (0x00010000)
+#define FEXT_FSQRT (0x00010080)
+#define FEXT_FCMP (0x00040000)
+#define FEXT_FCMPE (0x00040080)
+#define FEXT_FCMPZ (0x00050000)
+#define FEXT_FCMPEZ (0x00050080)
+#define FEXT_FCVT (0x00070080)
+#define FEXT_FUITO (0x00080000)
+#define FEXT_FSITO (0x00080080)
+#define FEXT_FTOUI (0x000c0000)
+#define FEXT_FTOUIZ (0x000c0080)
+#define FEXT_FTOSI (0x000d0000)
+#define FEXT_FTOSIZ (0x000d0080)
+
+#define FEXT_TO_IDX(inst) ((inst & 0x000f0000) >> 15 | (inst & (1 << 7)) >> 7)
+
+#define vfp_get_sd(inst) ((inst & 0x0000f000) >> 11 | (inst & (1 << 22)) >> 22)
+#define vfp_get_dd(inst) ((inst & 0x0000f000) >> 12)
+#define vfp_get_sm(inst) ((inst & 0x0000000f) << 1 | (inst & (1 << 5)) >> 5)
+#define vfp_get_dm(inst) ((inst & 0x0000000f))
+#define vfp_get_sn(inst) ((inst & 0x000f0000) >> 15 | (inst & (1 << 7)) >> 7)
+#define vfp_get_dn(inst) ((inst & 0x000f0000) >> 16)
+
+#define vfp_single(inst) (((inst) & 0x0000f00) == 0xa00)
+
+#define FPSCR_N (1 << 31)
+#define FPSCR_Z (1 << 30)
+#define FPSCR_C (1 << 29)
+#define FPSCR_V (1 << 28)
+
+/*
+ * Since we aren't building with -mfpu=vfp, we need to code
+ * these instructions using their MRC/MCR equivalents.
+ */
+#define vfpreg(_vfp_) #_vfp_
+
+#define fmrx(_vfp_) ({ \
+ u32 __v; \
+ asm("mrc%? p10, 7, %0, " vfpreg(_vfp_) ", cr0, 0 @ fmrx %0, " #_vfp_ \
+ : "=r" (__v)); \
+ __v; \
+ })
+
+#define fmxr(_vfp_,_var_) \
+ asm("mcr%? p10, 7, %0, " vfpreg(_vfp_) ", cr0, 0 @ fmxr " #_vfp_ ", %0" \
+ : : "r" (_var_))
+
+u32 vfp_single_cpdo(u32 inst, u32 fpscr);
+u32 vfp_single_cprt(u32 inst, u32 fpscr, struct pt_regs *regs);
+
+u32 vfp_double_cpdo(u32 inst, u32 fpscr);
--- /dev/null
+/*
+ * linux/arch/arm/vfp/vfpmodule.c
+ *
+ * Copyright (C) 2004 ARM Limited.
+ * Written by Deep Blue Solutions Limited.
+ *
+ * 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/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <asm/vfp.h>
+
+#include "vfpinstr.h"
+#include "vfp.h"
+
+/*
+ * Our undef handlers (in entry.S)
+ */
+void vfp_testing_entry(void);
+void vfp_support_entry(void);
+
+void (*vfp_vector)(void) = vfp_testing_entry;
+union vfp_state *last_VFP_context;
+
+/*
+ * Dual-use variable.
+ * Used in startup: set to non-zero if VFP checks fail
+ * After startup, holds VFP architecture
+ */
+unsigned int VFP_arch;
+
+/*
+ * Per-thread VFP initialisation.
+ */
+void vfp_flush_thread(union vfp_state *vfp)
+{
+ memset(vfp, 0, sizeof(union vfp_state));
+
+ vfp->hard.fpexc = FPEXC_ENABLE;
+ vfp->hard.fpscr = FPSCR_ROUND_NEAREST;
+
+ /*
+ * Disable VFP to ensure we initialise it first.
+ */
+ fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_ENABLE);
+
+ /*
+ * Ensure we don't try to overwrite our newly initialised
+ * state information on the first fault.
+ */
+ if (last_VFP_context == vfp)
+ last_VFP_context = NULL;
+}
+
+/*
+ * Per-thread VFP cleanup.
+ */
+void vfp_release_thread(union vfp_state *vfp)
+{
+ if (last_VFP_context == vfp)
+ last_VFP_context = NULL;
+}
+
+/*
+ * Raise a SIGFPE for the current process.
+ * sicode describes the signal being raised.
+ */
+void vfp_raise_sigfpe(unsigned int sicode, struct pt_regs *regs)
+{
+ siginfo_t info;
+
+ memset(&info, 0, sizeof(info));
+
+ info.si_signo = SIGFPE;
+ info.si_code = sicode;
+ info.si_addr = (void *)(instruction_pointer(regs) - 4);
+
+ /*
+ * This is the same as NWFPE, because it's not clear what
+ * this is used for
+ */
+ current->thread.error_code = 0;
+ current->thread.trap_no = 6;
+
+ force_sig_info(SIGFPE, &info, current);
+}
+
+static void vfp_panic(char *reason)
+{
+ int i;
+
+ printk(KERN_ERR "VFP: Error: %s\n", reason);
+ printk(KERN_ERR "VFP: EXC 0x%08x SCR 0x%08x INST 0x%08x\n",
+ fmrx(FPEXC), fmrx(FPSCR), fmrx(FPINST));
+ for (i = 0; i < 32; i += 2)
+ printk(KERN_ERR "VFP: s%2u: 0x%08x s%2u: 0x%08x\n",
+ i, vfp_get_float(i), i+1, vfp_get_float(i+1));
+}
+
+/*
+ * Process bitmask of exception conditions.
+ */
+static void vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr, struct pt_regs *regs)
+{
+ int si_code = 0;
+
+ pr_debug("VFP: raising exceptions %08x\n", exceptions);
+
+ if (exceptions == (u32)-1) {
+ vfp_panic("unhandled bounce");
+ vfp_raise_sigfpe(0, regs);
+ return;
+ }
+
+ /*
+ * If any of the status flags are set, update the FPSCR.
+ * Comparison instructions always return at least one of
+ * these flags set.
+ */
+ if (exceptions & (FPSCR_N|FPSCR_Z|FPSCR_C|FPSCR_V))
+ fpscr &= ~(FPSCR_N|FPSCR_Z|FPSCR_C|FPSCR_V);
+
+ fpscr |= exceptions;
+
+ fmxr(FPSCR, fpscr);
+
+#define RAISE(stat,en,sig) \
+ if (exceptions & stat && fpscr & en) \
+ si_code = sig;
+
+ /*
+ * These are arranged in priority order, least to highest.
+ */
+ RAISE(FPSCR_IXC, FPSCR_IXE, FPE_FLTRES);
+ RAISE(FPSCR_UFC, FPSCR_UFE, FPE_FLTUND);
+ RAISE(FPSCR_OFC, FPSCR_OFE, FPE_FLTOVF);
+ RAISE(FPSCR_IOC, FPSCR_IOE, FPE_FLTINV);
+
+ if (si_code)
+ vfp_raise_sigfpe(si_code, regs);
+}
+
+/*
+ * Emulate a VFP instruction.
+ */
+static u32 vfp_emulate_instruction(u32 inst, u32 fpscr, struct pt_regs *regs)
+{
+ u32 exceptions = (u32)-1;
+
+ pr_debug("VFP: emulate: INST=0x%08x SCR=0x%08x\n", inst, fpscr);
+
+ if (INST_CPRTDO(inst)) {
+ if (!INST_CPRT(inst)) {
+ /*
+ * CPDO
+ */
+ if (vfp_single(inst)) {
+ exceptions = vfp_single_cpdo(inst, fpscr);
+ } else {
+ exceptions = vfp_double_cpdo(inst, fpscr);
+ }
+ } else {
+ /*
+ * A CPRT instruction can not appear in FPINST2, nor
+ * can it cause an exception. Therefore, we do not
+ * have to emulate it.
+ */
+ }
+ } else {
+ /*
+ * A CPDT instruction can not appear in FPINST2, nor can
+ * it cause an exception. Therefore, we do not have to
+ * emulate it.
+ */
+ }
+ return exceptions;
+}
+
+/*
+ * Package up a bounce condition.
+ */
+void VFP9_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs)
+{
+ u32 fpscr, orig_fpscr, exceptions, inst;
+
+ pr_debug("VFP: bounce: trigger %08x fpexc %08x\n", trigger, fpexc);
+
+ /*
+ * Enable access to the VFP so we can handle the bounce.
+ */
+ fmxr(FPEXC, fpexc & ~(FPEXC_EXCEPTION|FPEXC_INV|FPEXC_UFC|FPEXC_IOC));
+
+ orig_fpscr = fpscr = fmrx(FPSCR);
+
+ /*
+ * If we are running with inexact exceptions enabled, we need to
+ * emulate the trigger instruction. Note that as we're emulating
+ * the trigger instruction, we need to increment PC.
+ */
+ if (fpscr & FPSCR_IXE) {
+ regs->ARM_pc += 4;
+ goto emulate;
+ }
+
+ barrier();
+
+ /*
+ * Modify fpscr to indicate the number of iterations remaining
+ */
+ if (fpexc & FPEXC_EXCEPTION) {
+ u32 len;
+
+ len = fpexc + (1 << FPEXC_LENGTH_BIT);
+
+ fpscr &= ~FPSCR_LENGTH_MASK;
+ fpscr |= (len & FPEXC_LENGTH_MASK) << (FPSCR_LENGTH_BIT - FPEXC_LENGTH_BIT);
+ }
+
+ /*
+ * Handle the first FP instruction. We used to take note of the
+ * FPEXC bounce reason, but this appears to be unreliable.
+ * Emulate the bounced instruction instead.
+ */
+ inst = fmrx(FPINST);
+ exceptions = vfp_emulate_instruction(inst, fpscr, regs);
+ if (exceptions)
+ vfp_raise_exceptions(exceptions, inst, orig_fpscr, regs);
+
+ /*
+ * If there isn't a second FP instruction, exit now.
+ */
+ if (!(fpexc & FPEXC_FPV2))
+ return;
+
+ /*
+ * The barrier() here prevents fpinst2 being read
+ * before the condition above.
+ */
+ barrier();
+ trigger = fmrx(FPINST2);
+ fpscr = fmrx(FPSCR);
+
+ emulate:
+ exceptions = vfp_emulate_instruction(trigger, fpscr, regs);
+ if (exceptions)
+ vfp_raise_exceptions(exceptions, trigger, orig_fpscr, regs);
+}
+
+/*
+ * VFP support code initialisation.
+ */
+static int __init vfp_init(void)
+{
+ unsigned int vfpsid;
+
+ /*
+ * First check that there is a VFP that we can use.
+ * The handler is already setup to just log calls, so
+ * we just need to read the VFPSID register.
+ */
+ vfpsid = fmrx(FPSID);
+
+ printk(KERN_INFO "VFP support v0.3: ");
+ if (VFP_arch) {
+ printk("not present\n");
+ } else if (vfpsid & FPSID_NODOUBLE) {
+ printk("no double precision support\n");
+ } else {
+ VFP_arch = (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT; /* Extract the architecture version */
+ printk("implementor %02x architecture %d part %02x variant %x rev %x\n",
+ (vfpsid & FPSID_IMPLEMENTER_MASK) >> FPSID_IMPLEMENTER_BIT,
+ (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT,
+ (vfpsid & FPSID_PART_MASK) >> FPSID_PART_BIT,
+ (vfpsid & FPSID_VARIANT_MASK) >> FPSID_VARIANT_BIT,
+ (vfpsid & FPSID_REV_MASK) >> FPSID_REV_BIT);
+ vfp_vector = vfp_support_entry;
+ }
+ return 0;
+}
+
+late_initcall(vfp_init);
--- /dev/null
+/*
+ * linux/arch/arm/vfp/vfpsingle.c
+ *
+ * This code is derived in part from John R. Housers softfloat library, which
+ * carries the following notice:
+ *
+ * ===========================================================================
+ * This C source file is part of the SoftFloat IEC/IEEE Floating-point
+ * Arithmetic Package, Release 2.
+ *
+ * Written by John R. Hauser. This work was made possible in part by the
+ * International Computer Science Institute, located at Suite 600, 1947 Center
+ * Street, Berkeley, California 94704. Funding was partially provided by the
+ * National Science Foundation under grant MIP-9311980. The original version
+ * of this code was written as part of a project to build a fixed-point vector
+ * processor in collaboration with the University of California at Berkeley,
+ * overseen by Profs. Nelson Morgan and John Wawrzynek. More information
+ * is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
+ * arithmetic/softfloat.html'.
+ *
+ * THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort
+ * has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
+ * TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO
+ * PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
+ * AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
+ *
+ * Derivative works are acceptable, even for commercial purposes, so long as
+ * (1) they include prominent notice that the work is derivative, and (2) they
+ * include prominent notice akin to these three paragraphs for those parts of
+ * this code that are retained.
+ * ===========================================================================
+ */
+#include <linux/kernel.h>
+#include <asm/bitops.h>
+#include <asm/ptrace.h>
+#include <asm/vfp.h>
+
+#include "vfpinstr.h"
+#include "vfp.h"
+
+static struct vfp_single vfp_single_default_qnan = {
+ .exponent = 255,
+ .sign = 0,
+ .significand = VFP_SINGLE_SIGNIFICAND_QNAN,
+};
+
+static void vfp_single_dump(const char *str, struct vfp_single *s)
+{
+ pr_debug("VFP: %s: sign=%d exponent=%d significand=%08x\n",
+ str, s->sign != 0, s->exponent, s->significand);
+}
+
+static void vfp_single_normalise_denormal(struct vfp_single *vs)
+{
+ int bits = 31 - fls(vs->significand);
+
+ vfp_single_dump("normalise_denormal: in", vs);
+
+ if (bits) {
+ vs->exponent -= bits - 1;
+ vs->significand <<= bits;
+ }
+
+ vfp_single_dump("normalise_denormal: out", vs);
+}
+
+#ifndef DEBUG
+#define vfp_single_normaliseround(sd,vsd,fpscr,except,func) __vfp_single_normaliseround(sd,vsd,fpscr,except)
+u32 __vfp_single_normaliseround(int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions)
+#else
+u32 vfp_single_normaliseround(int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions, const char *func)
+#endif
+{
+ u32 significand, incr, rmode;
+ int exponent, shift, underflow;
+
+ vfp_single_dump("pack: in", vs);
+
+ /*
+ * Infinities and NaNs are a special case.
+ */
+ if (vs->exponent == 255 && (vs->significand == 0 || exceptions))
+ goto pack;
+
+ /*
+ * Special-case zero.
+ */
+ if (vs->significand == 0) {
+ vs->exponent = 0;
+ goto pack;
+ }
+
+ exponent = vs->exponent;
+ significand = vs->significand;
+
+ /*
+ * Normalise first. Note that we shift the significand up to
+ * bit 31, so we have VFP_SINGLE_LOW_BITS + 1 below the least
+ * significant bit.
+ */
+ shift = 32 - fls(significand);
+ if (shift < 32 && shift) {
+ exponent -= shift;
+ significand <<= shift;
+ }
+
+#ifdef DEBUG
+ vs->exponent = exponent;
+ vs->significand = significand;
+ vfp_single_dump("pack: normalised", vs);
+#endif
+
+ /*
+ * Tiny number?
+ */
+ underflow = exponent < 0;
+ if (underflow) {
+ significand = vfp_shiftright32jamming(significand, -exponent);
+ exponent = 0;
+#ifdef DEBUG
+ vs->exponent = exponent;
+ vs->significand = significand;
+ vfp_single_dump("pack: tiny number", vs);
+#endif
+ if (!(significand & ((1 << (VFP_SINGLE_LOW_BITS + 1)) - 1)))
+ underflow = 0;
+ }
+
+ /*
+ * Select rounding increment.
+ */
+ incr = 0;
+ rmode = fpscr & FPSCR_RMODE_MASK;
+
+ if (rmode == FPSCR_ROUND_NEAREST) {
+ incr = 1 << VFP_SINGLE_LOW_BITS;
+ if ((significand & (1 << (VFP_SINGLE_LOW_BITS + 1))) == 0)
+ incr -= 1;
+ } else if (rmode == FPSCR_ROUND_TOZERO) {
+ incr = 0;
+ } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vs->sign != 0))
+ incr = (1 << (VFP_SINGLE_LOW_BITS + 1)) - 1;
+
+ pr_debug("VFP: rounding increment = 0x%08x\n", incr);
+
+ /*
+ * Is our rounding going to overflow?
+ */
+ if ((significand + incr) < significand) {
+ exponent += 1;
+ significand = (significand >> 1) | (significand & 1);
+ incr >>= 1;
+#ifdef DEBUG
+ vs->exponent = exponent;
+ vs->significand = significand;
+ vfp_single_dump("pack: overflow", vs);
+#endif
+ }
+
+ /*
+ * If any of the low bits (which will be shifted out of the
+ * number) are non-zero, the result is inexact.
+ */
+ if (significand & ((1 << (VFP_SINGLE_LOW_BITS + 1)) - 1))
+ exceptions |= FPSCR_IXC;
+
+ /*
+ * Do our rounding.
+ */
+ significand += incr;
+
+ /*
+ * Infinity?
+ */
+ if (exponent >= 254) {
+ exceptions |= FPSCR_OFC | FPSCR_IXC;
+ if (incr == 0) {
+ vs->exponent = 253;
+ vs->significand = 0x7fffffff;
+ } else {
+ vs->exponent = 255; /* infinity */
+ vs->significand = 0;
+ }
+ } else {
+ if (significand >> (VFP_SINGLE_LOW_BITS + 1) == 0)
+ exponent = 0;
+ if (exponent || significand > 0x80000000)
+ underflow = 0;
+ if (underflow)
+ exceptions |= FPSCR_UFC;
+ vs->exponent = exponent;
+ vs->significand = significand >> 1;
+ }
+
+ pack:
+ vfp_single_dump("pack: final", vs);
+ {
+ s32 d = vfp_single_pack(vs);
+ pr_debug("VFP: %s: d(s%d)=%08x exceptions=%08x\n", func,
+ sd, d, exceptions);
+ vfp_put_float(sd, d);
+ }
+
+ return exceptions;
+}
+
+/*
+ * Propagate the NaN, setting exceptions if it is signalling.
+ * 'n' is always a NaN. 'm' may be a number, NaN or infinity.
+ */
+static u32
+vfp_propagate_nan(struct vfp_single *vsd, struct vfp_single *vsn,
+ struct vfp_single *vsm, u32 fpscr)
+{
+ struct vfp_single *nan;
+ int tn, tm = 0;
+
+ tn = vfp_single_type(vsn);
+
+ if (vsm)
+ tm = vfp_single_type(vsm);
+
+ if (fpscr & FPSCR_DEFAULT_NAN)
+ /*
+ * Default NaN mode - always returns a quiet NaN
+ */
+ nan = &vfp_single_default_qnan;
+ else {
+ /*
+ * Contemporary mode - select the first signalling
+ * NAN, or if neither are signalling, the first
+ * quiet NAN.
+ */
+ if (tn == VFP_SNAN || (tm != VFP_SNAN && tn == VFP_QNAN))
+ nan = vsn;
+ else
+ nan = vsm;
+ /*
+ * Make the NaN quiet.
+ */
+ nan->significand |= VFP_SINGLE_SIGNIFICAND_QNAN;
+ }
+
+ *vsd = *nan;
+
+ /*
+ * If one was a signalling NAN, raise invalid operation.
+ */
+ return tn == VFP_SNAN || tm == VFP_SNAN ? FPSCR_IOC : 0x100;
+}
+
+
+/*
+ * Extended operations
+ */
+static u32 vfp_single_fabs(int sd, int unused, s32 m, u32 fpscr)
+{
+ vfp_put_float(sd, vfp_single_packed_abs(m));
+ return 0;
+}
+
+static u32 vfp_single_fcpy(int sd, int unused, s32 m, u32 fpscr)
+{
+ vfp_put_float(sd, m);
+ return 0;
+}
+
+static u32 vfp_single_fneg(int sd, int unused, s32 m, u32 fpscr)
+{
+ vfp_put_float(sd, vfp_single_packed_negate(m));
+ return 0;
+}
+
+static const u16 sqrt_oddadjust[] = {
+ 0x0004, 0x0022, 0x005d, 0x00b1, 0x011d, 0x019f, 0x0236, 0x02e0,
+ 0x039c, 0x0468, 0x0545, 0x0631, 0x072b, 0x0832, 0x0946, 0x0a67
+};
+
+static const u16 sqrt_evenadjust[] = {
+ 0x0a2d, 0x08af, 0x075a, 0x0629, 0x051a, 0x0429, 0x0356, 0x029e,
+ 0x0200, 0x0179, 0x0109, 0x00af, 0x0068, 0x0034, 0x0012, 0x0002
+};
+
+u32 vfp_estimate_sqrt_significand(u32 exponent, u32 significand)
+{
+ int index;
+ u32 z, a;
+
+ if ((significand & 0xc0000000) != 0x40000000) {
+ printk(KERN_WARNING "VFP: estimate_sqrt: invalid significand\n");
+ }
+
+ a = significand << 1;
+ index = (a >> 27) & 15;
+ if (exponent & 1) {
+ z = 0x4000 + (a >> 17) - sqrt_oddadjust[index];
+ z = ((a / z) << 14) + (z << 15);
+ a >>= 1;
+ } else {
+ z = 0x8000 + (a >> 17) - sqrt_evenadjust[index];
+ z = a / z + z;
+ z = (z >= 0x20000) ? 0xffff8000 : (z << 15);
+ if (z <= a)
+ return (s32)a >> 1;
+ }
+ return (u32)(((u64)a << 31) / z) + (z >> 1);
+}
+
+static u32 vfp_single_fsqrt(int sd, int unused, s32 m, u32 fpscr)
+{
+ struct vfp_single vsm, vsd;
+ int ret, tm;
+
+ vfp_single_unpack(&vsm, m);
+ tm = vfp_single_type(&vsm);
+ if (tm & (VFP_NAN|VFP_INFINITY)) {
+ struct vfp_single *vsp = &vsd;
+
+ if (tm & VFP_NAN)
+ ret = vfp_propagate_nan(vsp, &vsm, NULL, fpscr);
+ else if (vsm.sign == 0) {
+ sqrt_copy:
+ vsp = &vsm;
+ ret = 0;
+ } else {
+ sqrt_invalid:
+ vsp = &vfp_single_default_qnan;
+ ret = FPSCR_IOC;
+ }
+ vfp_put_float(sd, vfp_single_pack(vsp));
+ return ret;
+ }
+
+ /*
+ * sqrt(+/- 0) == +/- 0
+ */
+ if (tm & VFP_ZERO)
+ goto sqrt_copy;
+
+ /*
+ * Normalise a denormalised number
+ */
+ if (tm & VFP_DENORMAL)
+ vfp_single_normalise_denormal(&vsm);
+
+ /*
+ * sqrt(<0) = invalid
+ */
+ if (vsm.sign)
+ goto sqrt_invalid;
+
+ vfp_single_dump("sqrt", &vsm);
+
+ /*
+ * Estimate the square root.
+ */
+ vsd.sign = 0;
+ vsd.exponent = ((vsm.exponent - 127) >> 1) + 127;
+ vsd.significand = vfp_estimate_sqrt_significand(vsm.exponent, vsm.significand) + 2;
+
+ vfp_single_dump("sqrt estimate", &vsd);
+
+ /*
+ * And now adjust.
+ */
+ if ((vsd.significand & VFP_SINGLE_LOW_BITS_MASK) <= 5) {
+ if (vsd.significand < 2) {
+ vsd.significand = 0xffffffff;
+ } else {
+ u64 term;
+ s64 rem;
+ vsm.significand <<= !(vsm.exponent & 1);
+ term = (u64)vsd.significand * vsd.significand;
+ rem = ((u64)vsm.significand << 32) - term;
+
+ pr_debug("VFP: term=%016llx rem=%016llx\n", term, rem);
+
+ while (rem < 0) {
+ vsd.significand -= 1;
+ rem += ((u64)vsd.significand << 1) | 1;
+ }
+ vsd.significand |= rem != 0;
+ }
+ }
+ vsd.significand = vfp_shiftright32jamming(vsd.significand, 1);
+
+ return vfp_single_normaliseround(sd, &vsd, fpscr, 0, "fsqrt");
+}
+
+/*
+ * Equal := ZC
+ * Less than := N
+ * Greater than := C
+ * Unordered := CV
+ */
+static u32 vfp_compare(int sd, int signal_on_qnan, s32 m, u32 fpscr)
+{
+ s32 d;
+ u32 ret = 0;
+
+ d = vfp_get_float(sd);
+ if (vfp_single_packed_exponent(m) == 255 && vfp_single_packed_mantissa(m)) {
+ ret |= FPSCR_C | FPSCR_V;
+ if (signal_on_qnan || !(vfp_single_packed_mantissa(m) & (1 << (VFP_SINGLE_MANTISSA_BITS - 1))))
+ /*
+ * Signalling NaN, or signalling on quiet NaN
+ */
+ ret |= FPSCR_IOC;
+ }
+
+ if (vfp_single_packed_exponent(d) == 255 && vfp_single_packed_mantissa(d)) {
+ ret |= FPSCR_C | FPSCR_V;
+ if (signal_on_qnan || !(vfp_single_packed_mantissa(d) & (1 << (VFP_SINGLE_MANTISSA_BITS - 1))))
+ /*
+ * Signalling NaN, or signalling on quiet NaN
+ */
+ ret |= FPSCR_IOC;
+ }
+
+ if (ret == 0) {
+ if (d == m || vfp_single_packed_abs(d | m) == 0) {
+ /*
+ * equal
+ */
+ ret |= FPSCR_Z | FPSCR_C;
+ } else if (vfp_single_packed_sign(d ^ m)) {
+ /*
+ * different signs
+ */
+ if (vfp_single_packed_sign(d))
+ /*
+ * d is negative, so d < m
+ */
+ ret |= FPSCR_N;
+ else
+ /*
+ * d is positive, so d > m
+ */
+ ret |= FPSCR_C;
+ } else if ((vfp_single_packed_sign(d) != 0) ^ (d < m)) {
+ /*
+ * d < m
+ */
+ ret |= FPSCR_N;
+ } else if ((vfp_single_packed_sign(d) != 0) ^ (d > m)) {
+ /*
+ * d > m
+ */
+ ret |= FPSCR_C;
+ }
+ }
+ return ret;
+}
+
+static u32 vfp_single_fcmp(int sd, int unused, s32 m, u32 fpscr)
+{
+ return vfp_compare(sd, 0, m, fpscr);
+}
+
+static u32 vfp_single_fcmpe(int sd, int unused, s32 m, u32 fpscr)
+{
+ return vfp_compare(sd, 1, m, fpscr);
+}
+
+static u32 vfp_single_fcmpz(int sd, int unused, s32 m, u32 fpscr)
+{
+ return vfp_compare(sd, 0, 0, fpscr);
+}
+
+static u32 vfp_single_fcmpez(int sd, int unused, s32 m, u32 fpscr)
+{
+ return vfp_compare(sd, 1, 0, fpscr);
+}
+
+static u32 vfp_single_fcvtd(int dd, int unused, s32 m, u32 fpscr)
+{
+ struct vfp_single vsm;
+ struct vfp_double vdd;
+ int tm;
+ u32 exceptions = 0;
+
+ vfp_single_unpack(&vsm, m);
+
+ tm = vfp_single_type(&vsm);
+
+ /*
+ * If we have a signalling NaN, signal invalid operation.
+ */
+ if (tm == VFP_SNAN)
+ exceptions = FPSCR_IOC;
+
+ if (tm & VFP_DENORMAL)
+ vfp_single_normalise_denormal(&vsm);
+
+ vdd.sign = vsm.sign;
+ vdd.significand = (u64)vsm.significand << 32;
+
+ /*
+ * If we have an infinity or NaN, the exponent must be 2047.
+ */
+ if (tm & (VFP_INFINITY|VFP_NAN)) {
+ vdd.exponent = 2047;
+ if (tm & VFP_NAN)
+ vdd.significand |= VFP_DOUBLE_SIGNIFICAND_QNAN;
+ goto pack_nan;
+ } else if (tm & VFP_ZERO)
+ vdd.exponent = 0;
+ else
+ vdd.exponent = vsm.exponent + (1023 - 127);
+
+ /*
+ * Technically, if bit 0 of dd is set, this is an invalid
+ * instruction. However, we ignore this for efficiency.
+ */
+ return vfp_double_normaliseround(dd, &vdd, fpscr, exceptions, "fcvtd");
+
+ pack_nan:
+ vfp_put_double(dd, vfp_double_pack(&vdd));
+ return exceptions;
+}
+
+static u32 vfp_single_fuito(int sd, int unused, s32 m, u32 fpscr)
+{
+ struct vfp_single vs;
+
+ vs.sign = 0;
+ vs.exponent = 127 + 31 - 1;
+ vs.significand = (u32)m;
+
+ return vfp_single_normaliseround(sd, &vs, fpscr, 0, "fuito");
+}
+
+static u32 vfp_single_fsito(int sd, int unused, s32 m, u32 fpscr)
+{
+ struct vfp_single vs;
+
+ vs.sign = (m & 0x80000000) >> 16;
+ vs.exponent = 127 + 31 - 1;
+ vs.significand = vs.sign ? -m : m;
+
+ return vfp_single_normaliseround(sd, &vs, fpscr, 0, "fsito");
+}
+
+static u32 vfp_single_ftoui(int sd, int unused, s32 m, u32 fpscr)
+{
+ struct vfp_single vsm;
+ u32 d, exceptions = 0;
+ int rmode = fpscr & FPSCR_RMODE_MASK;
+ int tm;
+
+ vfp_single_unpack(&vsm, m);
+ vfp_single_dump("VSM", &vsm);
+
+ /*
+ * Do we have a denormalised number?
+ */
+ tm = vfp_single_type(&vsm);
+ if (tm & VFP_DENORMAL)
+ exceptions |= FPSCR_IDC;
+
+ if (tm & VFP_NAN)
+ vsm.sign = 0;
+
+ if (vsm.exponent >= 127 + 32) {
+ d = vsm.sign ? 0 : 0xffffffff;
+ exceptions = FPSCR_IOC;
+ } else if (vsm.exponent >= 127 - 1) {
+ int shift = 127 + 31 - vsm.exponent;
+ u32 rem, incr = 0;
+
+ /*
+ * 2^0 <= m < 2^32-2^8
+ */
+ d = (vsm.significand << 1) >> shift;
+ rem = vsm.significand << (33 - shift);
+
+ if (rmode == FPSCR_ROUND_NEAREST) {
+ incr = 0x80000000;
+ if ((d & 1) == 0)
+ incr -= 1;
+ } else if (rmode == FPSCR_ROUND_TOZERO) {
+ incr = 0;
+ } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vsm.sign != 0)) {
+ incr = ~0;
+ }
+
+ if ((rem + incr) < rem) {
+ if (d < 0xffffffff)
+ d += 1;
+ else
+ exceptions |= FPSCR_IOC;
+ }
+
+ if (d && vsm.sign) {
+ d = 0;
+ exceptions |= FPSCR_IOC;
+ } else if (rem)
+ exceptions |= FPSCR_IXC;
+ } else {
+ d = 0;
+ if (vsm.exponent | vsm.significand) {
+ exceptions |= FPSCR_IXC;
+ if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0)
+ d = 1;
+ else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign) {
+ d = 0;
+ exceptions |= FPSCR_IOC;
+ }
+ }
+ }
+
+ pr_debug("VFP: ftoui: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
+
+ vfp_put_float(sd, d);
+
+ return exceptions;
+}
+
+static u32 vfp_single_ftouiz(int sd, int unused, s32 m, u32 fpscr)
+{
+ return vfp_single_ftoui(sd, unused, m, FPSCR_ROUND_TOZERO);
+}
+
+static u32 vfp_single_ftosi(int sd, int unused, s32 m, u32 fpscr)
+{
+ struct vfp_single vsm;
+ u32 d, exceptions = 0;
+ int rmode = fpscr & FPSCR_RMODE_MASK;
+
+ vfp_single_unpack(&vsm, m);
+ vfp_single_dump("VSM", &vsm);
+
+ /*
+ * Do we have a denormalised number?
+ */
+ if (vfp_single_type(&vsm) & VFP_DENORMAL)
+ exceptions |= FPSCR_IDC;
+
+ if (vsm.exponent >= 127 + 32) {
+ /*
+ * m >= 2^31-2^7: invalid
+ */
+ d = 0x7fffffff;
+ if (vsm.sign)
+ d = ~d;
+ exceptions |= FPSCR_IOC;
+ } else if (vsm.exponent >= 127 - 1) {
+ int shift = 127 + 31 - vsm.exponent;
+ u32 rem, incr = 0;
+
+ /* 2^0 <= m <= 2^31-2^7 */
+ d = (vsm.significand << 1) >> shift;
+ rem = vsm.significand << (33 - shift);
+
+ if (rmode == FPSCR_ROUND_NEAREST) {
+ incr = 0x80000000;
+ if ((d & 1) == 0)
+ incr -= 1;
+ } else if (rmode == FPSCR_ROUND_TOZERO) {
+ incr = 0;
+ } else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vsm.sign != 0)) {
+ incr = ~0;
+ }
+
+ if ((rem + incr) < rem && d < 0xffffffff)
+ d += 1;
+ if (d > 0x7fffffff + (vsm.sign != 0)) {
+ d = 0x7fffffff + (vsm.sign != 0);
+ exceptions |= FPSCR_IOC;
+ } else if (rem)
+ exceptions |= FPSCR_IXC;
+
+ if (vsm.sign)
+ d = -d;
+ } else {
+ d = 0;
+ if (vsm.exponent | vsm.significand) {
+ exceptions |= FPSCR_IXC;
+ if (rmode == FPSCR_ROUND_PLUSINF && vsm.sign == 0)
+ d = 1;
+ else if (rmode == FPSCR_ROUND_MINUSINF && vsm.sign)
+ d = -1;
+ }
+ }
+
+ pr_debug("VFP: ftosi: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
+
+ vfp_put_float(sd, (s32)d);
+
+ return exceptions;
+}
+
+static u32 vfp_single_ftosiz(int sd, int unused, s32 m, u32 fpscr)
+{
+ return vfp_single_ftosi(sd, unused, m, FPSCR_ROUND_TOZERO);
+}
+
+static u32 (* const fop_extfns[32])(int sd, int unused, s32 m, u32 fpscr) = {
+ [FEXT_TO_IDX(FEXT_FCPY)] = vfp_single_fcpy,
+ [FEXT_TO_IDX(FEXT_FABS)] = vfp_single_fabs,
+ [FEXT_TO_IDX(FEXT_FNEG)] = vfp_single_fneg,
+ [FEXT_TO_IDX(FEXT_FSQRT)] = vfp_single_fsqrt,
+ [FEXT_TO_IDX(FEXT_FCMP)] = vfp_single_fcmp,
+ [FEXT_TO_IDX(FEXT_FCMPE)] = vfp_single_fcmpe,
+ [FEXT_TO_IDX(FEXT_FCMPZ)] = vfp_single_fcmpz,
+ [FEXT_TO_IDX(FEXT_FCMPEZ)] = vfp_single_fcmpez,
+ [FEXT_TO_IDX(FEXT_FCVT)] = vfp_single_fcvtd,
+ [FEXT_TO_IDX(FEXT_FUITO)] = vfp_single_fuito,
+ [FEXT_TO_IDX(FEXT_FSITO)] = vfp_single_fsito,
+ [FEXT_TO_IDX(FEXT_FTOUI)] = vfp_single_ftoui,
+ [FEXT_TO_IDX(FEXT_FTOUIZ)] = vfp_single_ftouiz,
+ [FEXT_TO_IDX(FEXT_FTOSI)] = vfp_single_ftosi,
+ [FEXT_TO_IDX(FEXT_FTOSIZ)] = vfp_single_ftosiz,
+};
+
+
+
+
+
+static u32
+vfp_single_fadd_nonnumber(struct vfp_single *vsd, struct vfp_single *vsn,
+ struct vfp_single *vsm, u32 fpscr)
+{
+ struct vfp_single *vsp;
+ u32 exceptions = 0;
+ int tn, tm;
+
+ tn = vfp_single_type(vsn);
+ tm = vfp_single_type(vsm);
+
+ if (tn & tm & VFP_INFINITY) {
+ /*
+ * Two infinities. Are they different signs?
+ */
+ if (vsn->sign ^ vsm->sign) {
+ /*
+ * different signs -> invalid
+ */
+ exceptions = FPSCR_IOC;
+ vsp = &vfp_single_default_qnan;
+ } else {
+ /*
+ * same signs -> valid
+ */
+ vsp = vsn;
+ }
+ } else if (tn & VFP_INFINITY && tm & VFP_NUMBER) {
+ /*
+ * One infinity and one number -> infinity
+ */
+ vsp = vsn;
+ } else {
+ /*
+ * 'n' is a NaN of some type
+ */
+ return vfp_propagate_nan(vsd, vsn, vsm, fpscr);
+ }
+ *vsd = *vsp;
+ return exceptions;
+}
+
+static u32
+vfp_single_add(struct vfp_single *vsd, struct vfp_single *vsn,
+ struct vfp_single *vsm, u32 fpscr)
+{
+ u32 exp_diff, m_sig;
+
+ if (vsn->significand & 0x80000000 ||
+ vsm->significand & 0x80000000) {
+ pr_info("VFP: bad FP values in %s\n", __func__);
+ vfp_single_dump("VSN", vsn);
+ vfp_single_dump("VSM", vsm);
+ }
+
+ /*
+ * Ensure that 'n' is the largest magnitude number. Note that
+ * if 'n' and 'm' have equal exponents, we do not swap them.
+ * This ensures that NaN propagation works correctly.
+ */
+ if (vsn->exponent < vsm->exponent) {
+ struct vfp_single *t = vsn;
+ vsn = vsm;
+ vsm = t;
+ }
+
+ /*
+ * Is 'n' an infinity or a NaN? Note that 'm' may be a number,
+ * infinity or a NaN here.
+ */
+ if (vsn->exponent == 255)
+ return vfp_single_fadd_nonnumber(vsd, vsn, vsm, fpscr);
+
+ /*
+ * We have two proper numbers, where 'vsn' is the larger magnitude.
+ *
+ * Copy 'n' to 'd' before doing the arithmetic.
+ */
+ *vsd = *vsn;
+
+ /*
+ * Align both numbers.
+ */
+ exp_diff = vsn->exponent - vsm->exponent;
+ m_sig = vfp_shiftright32jamming(vsm->significand, exp_diff);
+
+ /*
+ * If the signs are different, we are really subtracting.
+ */
+ if (vsn->sign ^ vsm->sign) {
+ m_sig = vsn->significand - m_sig;
+ if ((s32)m_sig < 0) {
+ vsd->sign = vfp_sign_negate(vsd->sign);
+ m_sig = -m_sig;
+ } else if (m_sig == 0) {
+ vsd->sign = (fpscr & FPSCR_RMODE_MASK) ==
+ FPSCR_ROUND_MINUSINF ? 0x8000 : 0;
+ }
+ } else {
+ m_sig = vsn->significand + m_sig;
+ }
+ vsd->significand = m_sig;
+
+ return 0;
+}
+
+static u32
+vfp_single_multiply(struct vfp_single *vsd, struct vfp_single *vsn, struct vfp_single *vsm, u32 fpscr)
+{
+ vfp_single_dump("VSN", vsn);
+ vfp_single_dump("VSM", vsm);
+
+ /*
+ * Ensure that 'n' is the largest magnitude number. Note that
+ * if 'n' and 'm' have equal exponents, we do not swap them.
+ * This ensures that NaN propagation works correctly.
+ */
+ if (vsn->exponent < vsm->exponent) {
+ struct vfp_single *t = vsn;
+ vsn = vsm;
+ vsm = t;
+ pr_debug("VFP: swapping M <-> N\n");
+ }
+
+ vsd->sign = vsn->sign ^ vsm->sign;
+
+ /*
+ * If 'n' is an infinity or NaN, handle it. 'm' may be anything.
+ */
+ if (vsn->exponent == 255) {
+ if (vsn->significand || (vsm->exponent == 255 && vsm->significand))
+ return vfp_propagate_nan(vsd, vsn, vsm, fpscr);
+ if ((vsm->exponent | vsm->significand) == 0) {
+ *vsd = vfp_single_default_qnan;
+ return FPSCR_IOC;
+ }
+ vsd->exponent = vsn->exponent;
+ vsd->significand = 0;
+ return 0;
+ }
+
+ /*
+ * If 'm' is zero, the result is always zero. In this case,
+ * 'n' may be zero or a number, but it doesn't matter which.
+ */
+ if ((vsm->exponent | vsm->significand) == 0) {
+ vsd->exponent = 0;
+ vsd->significand = 0;
+ return 0;
+ }
+
+ /*
+ * We add 2 to the destination exponent for the same reason as
+ * the addition case - though this time we have +1 from each
+ * input operand.
+ */
+ vsd->exponent = vsn->exponent + vsm->exponent - 127 + 2;
+ vsd->significand = vfp_hi64to32jamming((u64)vsn->significand * vsm->significand);
+
+ vfp_single_dump("VSD", vsd);
+ return 0;
+}
+
+#define NEG_MULTIPLY (1 << 0)
+#define NEG_SUBTRACT (1 << 1)
+
+static u32
+vfp_single_multiply_accumulate(int sd, int sn, s32 m, u32 fpscr, u32 negate, char *func)
+{
+ struct vfp_single vsd, vsp, vsn, vsm;
+ u32 exceptions;
+ s32 v;
+
+ v = vfp_get_float(sn);
+ pr_debug("VFP: s%u = %08x\n", sn, v);
+ vfp_single_unpack(&vsn, v);
+ if (vsn.exponent == 0 && vsn.significand)
+ vfp_single_normalise_denormal(&vsn);
+
+ vfp_single_unpack(&vsm, m);
+ if (vsm.exponent == 0 && vsm.significand)
+ vfp_single_normalise_denormal(&vsm);
+
+ exceptions = vfp_single_multiply(&vsp, &vsn, &vsm, fpscr);
+ if (negate & NEG_MULTIPLY)
+ vsp.sign = vfp_sign_negate(vsp.sign);
+
+ v = vfp_get_float(sd);
+ pr_debug("VFP: s%u = %08x\n", sd, v);
+ vfp_single_unpack(&vsn, v);
+ if (negate & NEG_SUBTRACT)
+ vsn.sign = vfp_sign_negate(vsn.sign);
+
+ exceptions |= vfp_single_add(&vsd, &vsn, &vsp, fpscr);
+
+ return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, func);
+}
+
+/*
+ * Standard operations
+ */
+
+/*
+ * sd = sd + (sn * sm)
+ */
+static u32 vfp_single_fmac(int sd, int sn, s32 m, u32 fpscr)
+{
+ return vfp_single_multiply_accumulate(sd, sn, m, fpscr, 0, "fmac");
+}
+
+/*
+ * sd = sd - (sn * sm)
+ */
+static u32 vfp_single_fnmac(int sd, int sn, s32 m, u32 fpscr)
+{
+ return vfp_single_multiply_accumulate(sd, sn, m, fpscr, NEG_MULTIPLY, "fnmac");
+}
+
+/*
+ * sd = -sd + (sn * sm)
+ */
+static u32 vfp_single_fmsc(int sd, int sn, s32 m, u32 fpscr)
+{
+ return vfp_single_multiply_accumulate(sd, sn, m, fpscr, NEG_SUBTRACT, "fmsc");
+}
+
+/*
+ * sd = -sd - (sn * sm)
+ */
+static u32 vfp_single_fnmsc(int sd, int sn, s32 m, u32 fpscr)
+{
+ return vfp_single_multiply_accumulate(sd, sn, m, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc");
+}
+
+/*
+ * sd = sn * sm
+ */
+static u32 vfp_single_fmul(int sd, int sn, s32 m, u32 fpscr)
+{
+ struct vfp_single vsd, vsn, vsm;
+ u32 exceptions;
+ s32 n = vfp_get_float(sn);
+
+ pr_debug("VFP: s%u = %08x\n", sn, n);
+
+ vfp_single_unpack(&vsn, n);
+ if (vsn.exponent == 0 && vsn.significand)
+ vfp_single_normalise_denormal(&vsn);
+
+ vfp_single_unpack(&vsm, m);
+ if (vsm.exponent == 0 && vsm.significand)
+ vfp_single_normalise_denormal(&vsm);
+
+ exceptions = vfp_single_multiply(&vsd, &vsn, &vsm, fpscr);
+ return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fmul");
+}
+
+/*
+ * sd = -(sn * sm)
+ */
+static u32 vfp_single_fnmul(int sd, int sn, s32 m, u32 fpscr)
+{
+ struct vfp_single vsd, vsn, vsm;
+ u32 exceptions;
+ s32 n = vfp_get_float(sn);
+
+ pr_debug("VFP: s%u = %08x\n", sn, n);
+
+ vfp_single_unpack(&vsn, n);
+ if (vsn.exponent == 0 && vsn.significand)
+ vfp_single_normalise_denormal(&vsn);
+
+ vfp_single_unpack(&vsm, m);
+ if (vsm.exponent == 0 && vsm.significand)
+ vfp_single_normalise_denormal(&vsm);
+
+ exceptions = vfp_single_multiply(&vsd, &vsn, &vsm, fpscr);
+ vsd.sign = vfp_sign_negate(vsd.sign);
+ return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fnmul");
+}
+
+/*
+ * sd = sn + sm
+ */
+static u32 vfp_single_fadd(int sd, int sn, s32 m, u32 fpscr)
+{
+ struct vfp_single vsd, vsn, vsm;
+ u32 exceptions;
+ s32 n = vfp_get_float(sn);
+
+ pr_debug("VFP: s%u = %08x\n", sn, n);
+
+ /*
+ * Unpack and normalise denormals.
+ */
+ vfp_single_unpack(&vsn, n);
+ if (vsn.exponent == 0 && vsn.significand)
+ vfp_single_normalise_denormal(&vsn);
+
+ vfp_single_unpack(&vsm, m);
+ if (vsm.exponent == 0 && vsm.significand)
+ vfp_single_normalise_denormal(&vsm);
+
+ exceptions = vfp_single_add(&vsd, &vsn, &vsm, fpscr);
+
+ return vfp_single_normaliseround(sd, &vsd, fpscr, exceptions, "fadd");
+}
+
+/*
+ * sd = sn - sm
+ */
+static u32 vfp_single_fsub(int sd, int sn, s32 m, u32 fpscr)
+{
+ /*
+ * Subtraction is addition with one sign inverted.
+ */
+ return vfp_single_fadd(sd, sn, vfp_single_packed_negate(m), fpscr);
+}
+
+/*
+ * sd = sn / sm
+ */
+static u32 vfp_single_fdiv(int sd, int sn, s32 m, u32 fpscr)
+{
+ struct vfp_single vsd, vsn, vsm;
+ u32 exceptions = 0;
+ s32 n = vfp_get_float(sn);
+ int tm, tn;
+
+ pr_debug("VFP: s%u = %08x\n", sn, n);
+
+ vfp_single_unpack(&vsn, n);
+ vfp_single_unpack(&vsm, m);
+
+ vsd.sign = vsn.sign ^ vsm.sign;
+
+ tn = vfp_single_type(&vsn);
+ tm = vfp_single_type(&vsm);
+
+ /*
+ * Is n a NAN?
+ */
+ if (tn & VFP_NAN)
+ goto vsn_nan;
+
+ /*
+ * Is m a NAN?
+ */
+ if (tm & VFP_NAN)
+ goto vsm_nan;
+
+ /*
+ * If n and m are infinity, the result is invalid
+ * If n and m are zero, the result is invalid
+ */
+ if (tm & tn & (VFP_INFINITY|VFP_ZERO))
+ goto invalid;
+
+ /*
+ * If n is infinity, the result is infinity
+ */
+ if (tn & VFP_INFINITY)
+ goto infinity;
+
+ /*
+ * If m is zero, raise div0 exception
+ */
+ if (tm & VFP_ZERO)
+ goto divzero;
+
+ /*
+ * If m is infinity, or n is zero, the result is zero
+ */
+ if (tm & VFP_INFINITY || tn & VFP_ZERO)
+ goto zero;
+
+ if (tn & VFP_DENORMAL)
+ vfp_single_normalise_denormal(&vsn);
+ if (tm & VFP_DENORMAL)
+ vfp_single_normalise_denormal(&vsm);
+
+ /*
+ * Ok, we have two numbers, we can perform division.
+ */
+ vsd.exponent = vsn.exponent - vsm.exponent + 127 - 1;
+ vsm.significand <<= 1;
+ if (vsm.significand <= (2 * vsn.significand)) {
+ vsn.significand >>= 1;
+ vsd.exponent++;
+ }
+ vsd.significand = ((u64)vsn.significand << 32) / vsm.significand;
+ if ((vsd.significand & 0x3f) == 0)
+ vsd.significand |= ((u64)vsm.significand * vsd.significand != (u64)vsn.significand << 32);
+
+ return vfp_single_normaliseround(sd, &vsd, fpscr, 0, "fdiv");
+
+ vsn_nan:
+ exceptions = vfp_propagate_nan(&vsd, &vsn, &vsm, fpscr);
+ pack:
+ vfp_put_float(sd, vfp_single_pack(&vsd));
+ return exceptions;
+
+ vsm_nan:
+ exceptions = vfp_propagate_nan(&vsd, &vsm, &vsn, fpscr);
+ goto pack;
+
+ zero:
+ vsd.exponent = 0;
+ vsd.significand = 0;
+ goto pack;
+
+ divzero:
+ exceptions = FPSCR_DZC;
+ infinity:
+ vsd.exponent = 255;
+ vsd.significand = 0;
+ goto pack;
+
+ invalid:
+ vfp_put_float(sd, vfp_single_pack(&vfp_single_default_qnan));
+ return FPSCR_IOC;
+}
+
+static u32 (* const fop_fns[16])(int sd, int sn, s32 m, u32 fpscr) = {
+ [FOP_TO_IDX(FOP_FMAC)] = vfp_single_fmac,
+ [FOP_TO_IDX(FOP_FNMAC)] = vfp_single_fnmac,
+ [FOP_TO_IDX(FOP_FMSC)] = vfp_single_fmsc,
+ [FOP_TO_IDX(FOP_FNMSC)] = vfp_single_fnmsc,
+ [FOP_TO_IDX(FOP_FMUL)] = vfp_single_fmul,
+ [FOP_TO_IDX(FOP_FNMUL)] = vfp_single_fnmul,
+ [FOP_TO_IDX(FOP_FADD)] = vfp_single_fadd,
+ [FOP_TO_IDX(FOP_FSUB)] = vfp_single_fsub,
+ [FOP_TO_IDX(FOP_FDIV)] = vfp_single_fdiv,
+};
+
+#define FREG_BANK(x) ((x) & 0x18)
+#define FREG_IDX(x) ((x) & 7)
+
+u32 vfp_single_cpdo(u32 inst, u32 fpscr)
+{
+ u32 op = inst & FOP_MASK;
+ u32 exceptions = 0;
+ unsigned int sd = vfp_get_sd(inst);
+ unsigned int sn = vfp_get_sn(inst);
+ unsigned int sm = vfp_get_sm(inst);
+ unsigned int vecitr, veclen, vecstride;
+ u32 (*fop)(int, int, s32, u32);
+
+ veclen = fpscr & FPSCR_LENGTH_MASK;
+ vecstride = 1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK);
+
+ /*
+ * If destination bank is zero, vector length is always '1'.
+ * ARM DDI0100F C5.1.3, C5.3.2.
+ */
+ if (FREG_BANK(sd) == 0)
+ veclen = 0;
+
+ pr_debug("VFP: vecstride=%u veclen=%u\n", vecstride,
+ (veclen >> FPSCR_LENGTH_BIT) + 1);
+
+ fop = (op == FOP_EXT) ? fop_extfns[sn] : fop_fns[FOP_TO_IDX(op)];
+ if (!fop)
+ goto invalid;
+
+ for (vecitr = 0; vecitr <= veclen; vecitr += 1 << FPSCR_LENGTH_BIT) {
+ s32 m = vfp_get_float(sm);
+ u32 except;
+
+ if (op == FOP_EXT)
+ pr_debug("VFP: itr%d (s%u) = op[%u] (s%u=%08x)\n",
+ vecitr >> FPSCR_LENGTH_BIT, sd, sn, sm, m);
+ else
+ pr_debug("VFP: itr%d (s%u) = (s%u) op[%u] (s%u=%08x)\n",
+ vecitr >> FPSCR_LENGTH_BIT, sd, sn,
+ FOP_TO_IDX(op), sm, m);
+
+ except = fop(sd, sn, m, fpscr);
+ pr_debug("VFP: itr%d: exceptions=%08x\n",
+ vecitr >> FPSCR_LENGTH_BIT, except);
+
+ exceptions |= except;
+
+ /*
+ * This ensures that comparisons only operate on scalars;
+ * comparisons always return with one FPSCR status bit set.
+ */
+ if (except & (FPSCR_N|FPSCR_Z|FPSCR_C|FPSCR_V))
+ break;
+
+ /*
+ * CHECK: It appears to be undefined whether we stop when
+ * we encounter an exception. We continue.
+ */
+
+ sd = FREG_BANK(sd) + ((FREG_IDX(sd) + vecstride) & 7);
+ sn = FREG_BANK(sn) + ((FREG_IDX(sn) + vecstride) & 7);
+ if (FREG_BANK(sm) != 0)
+ sm = FREG_BANK(sm) + ((FREG_IDX(sm) + vecstride) & 7);
+ }
+ return exceptions;
+
+ invalid:
+ return (u32)-1;
+}
# CONFIG_LAPB is not set
# CONFIG_NET_DIVERT is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
--- /dev/null
+#
+# i386/crypto/Makefile
+#
+# Arch-specific CryptoAPI modules.
+#
+
+obj-$(CONFIG_CRYPTO_AES_586) += aes-i586.o
+
+aes-i586-y := aes-i586-asm.o aes.o
--- /dev/null
+// -------------------------------------------------------------------------
+// Copyright (c) 2001, Dr Brian Gladman < >, Worcester, UK.
+// All rights reserved.
+//
+// LICENSE TERMS
+//
+// The free distribution and use of this software in both source and binary
+// form is allowed (with or without changes) provided that:
+//
+// 1. distributions of this source code include the above copyright
+// notice, this list of conditions and the following disclaimer//
+//
+// 2. distributions in binary form include the above copyright
+// notice, this list of conditions and the following disclaimer
+// in the documentation and/or other associated materials//
+//
+// 3. the copyright holder's name is not used to endorse products
+// built using this software without specific written permission.
+//
+//
+// ALTERNATIVELY, provided that this notice is retained in full, this product
+// may be distributed under the terms of the GNU General Public License (GPL),
+// in which case the provisions of the GPL apply INSTEAD OF those given above.
+//
+// Copyright (c) 2004 Linus Torvalds <torvalds@osdl.org>
+// Copyright (c) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
+
+// DISCLAIMER
+//
+// This software is provided 'as is' with no explicit or implied warranties
+// in respect of its properties including, but not limited to, correctness
+// and fitness for purpose.
+// -------------------------------------------------------------------------
+// Issue Date: 29/07/2002
+
+.file "aes-i586-asm.S"
+.text
+
+// aes_rval aes_enc_blk(const unsigned char in_blk[], unsigned char out_blk[], const aes_ctx cx[1])//
+// aes_rval aes_dec_blk(const unsigned char in_blk[], unsigned char out_blk[], const aes_ctx cx[1])//
+
+#define tlen 1024 // length of each of 4 'xor' arrays (256 32-bit words)
+
+// offsets to parameters with one register pushed onto stack
+
+#define in_blk 8 // input byte array address parameter
+#define out_blk 12 // output byte array address parameter
+#define ctx 16 // AES context structure
+
+// offsets in context structure
+
+#define ekey 0 // encryption key schedule base address
+#define nrnd 256 // number of rounds
+#define dkey 260 // decryption key schedule base address
+
+// register mapping for encrypt and decrypt subroutines
+
+#define r0 eax
+#define r1 ebx
+#define r2 ecx
+#define r3 edx
+#define r4 esi
+#define r5 edi
+#define r6 ebp
+
+#define eaxl al
+#define eaxh ah
+#define ebxl bl
+#define ebxh bh
+#define ecxl cl
+#define ecxh ch
+#define edxl dl
+#define edxh dh
+
+#define _h(reg) reg##h
+#define h(reg) _h(reg)
+
+#define _l(reg) reg##l
+#define l(reg) _l(reg)
+
+// This macro takes a 32-bit word representing a column and uses
+// each of its four bytes to index into four tables of 256 32-bit
+// words to obtain values that are then xored into the appropriate
+// output registers r0, r1, r4 or r5.
+
+// Parameters:
+// %1 out_state[0]
+// %2 out_state[1]
+// %3 out_state[2]
+// %4 out_state[3]
+// %5 table base address
+// %6 input register for the round (destroyed)
+// %7 scratch register for the round
+
+#define do_col(a1, a2, a3, a4, a5, a6, a7) \
+ movzx %l(a6),%a7; \
+ xor a5(,%a7,4),%a1; \
+ movzx %h(a6),%a7; \
+ shr $16,%a6; \
+ xor a5+tlen(,%a7,4),%a2; \
+ movzx %l(a6),%a7; \
+ movzx %h(a6),%a6; \
+ xor a5+2*tlen(,%a7,4),%a3; \
+ xor a5+3*tlen(,%a6,4),%a4;
+
+// initialise output registers from the key schedule
+
+#define do_fcol(a1, a2, a3, a4, a5, a6, a7, a8) \
+ mov 0 a8,%a1; \
+ movzx %l(a6),%a7; \
+ mov 12 a8,%a2; \
+ xor a5(,%a7,4),%a1; \
+ mov 4 a8,%a4; \
+ movzx %h(a6),%a7; \
+ shr $16,%a6; \
+ xor a5+tlen(,%a7,4),%a2; \
+ movzx %l(a6),%a7; \
+ movzx %h(a6),%a6; \
+ xor a5+3*tlen(,%a6,4),%a4; \
+ mov %a3,%a6; \
+ mov 8 a8,%a3; \
+ xor a5+2*tlen(,%a7,4),%a3;
+
+// initialise output registers from the key schedule
+
+#define do_icol(a1, a2, a3, a4, a5, a6, a7, a8) \
+ mov 0 a8,%a1; \
+ movzx %l(a6),%a7; \
+ mov 4 a8,%a2; \
+ xor a5(,%a7,4),%a1; \
+ mov 12 a8,%a4; \
+ movzx %h(a6),%a7; \
+ shr $16,%a6; \
+ xor a5+tlen(,%a7,4),%a2; \
+ movzx %l(a6),%a7; \
+ movzx %h(a6),%a6; \
+ xor a5+3*tlen(,%a6,4),%a4; \
+ mov %a3,%a6; \
+ mov 8 a8,%a3; \
+ xor a5+2*tlen(,%a7,4),%a3;
+
+
+// original Gladman had conditional saves to MMX regs.
+#define save(a1, a2) \
+ mov %a2,4*a1(%esp)
+
+#define restore(a1, a2) \
+ mov 4*a2(%esp),%a1
+
+// This macro performs a forward encryption cycle. It is entered with
+// the first previous round column values in r0, r1, r4 and r5 and
+// exits with the final values in the same registers, using the MMX
+// registers mm0-mm1 or the stack for temporary storage
+
+// mov current column values into the MMX registers
+#define fwd_rnd(arg, table) \
+ /* mov current column values into the MMX registers */ \
+ mov %r0,%r2; \
+ save (0,r1); \
+ save (1,r5); \
+ \
+ /* compute new column values */ \
+ do_fcol(r0,r5,r4,r1,table, r2,r3, arg); \
+ do_col (r4,r1,r0,r5,table, r2,r3); \
+ restore(r2,0); \
+ do_col (r1,r0,r5,r4,table, r2,r3); \
+ restore(r2,1); \
+ do_col (r5,r4,r1,r0,table, r2,r3);
+
+// This macro performs an inverse encryption cycle. It is entered with
+// the first previous round column values in r0, r1, r4 and r5 and
+// exits with the final values in the same registers, using the MMX
+// registers mm0-mm1 or the stack for temporary storage
+
+#define inv_rnd(arg, table) \
+ /* mov current column values into the MMX registers */ \
+ mov %r0,%r2; \
+ save (0,r1); \
+ save (1,r5); \
+ \
+ /* compute new column values */ \
+ do_icol(r0,r1,r4,r5, table, r2,r3, arg); \
+ do_col (r4,r5,r0,r1, table, r2,r3); \
+ restore(r2,0); \
+ do_col (r1,r4,r5,r0, table, r2,r3); \
+ restore(r2,1); \
+ do_col (r5,r0,r1,r4, table, r2,r3);
+
+// AES (Rijndael) Encryption Subroutine
+
+.global aes_enc_blk
+
+.extern ft_tab
+.extern fl_tab
+
+.align 4
+
+aes_enc_blk:
+ push %ebp
+ mov ctx(%esp),%ebp // pointer to context
+ xor %eax,%eax
+
+// CAUTION: the order and the values used in these assigns
+// rely on the register mappings
+
+1: push %ebx
+ mov in_blk+4(%esp),%r2
+ push %esi
+ mov nrnd(%ebp),%r3 // number of rounds
+ push %edi
+ lea ekey(%ebp),%r6 // key pointer
+
+// input four columns and xor in first round key
+
+ mov (%r2),%r0
+ mov 4(%r2),%r1
+ mov 8(%r2),%r4
+ mov 12(%r2),%r5
+ xor (%r6),%r0
+ xor 4(%r6),%r1
+ xor 8(%r6),%r4
+ xor 12(%r6),%r5
+
+ sub $8,%esp // space for register saves on stack
+ add $16,%r6 // increment to next round key
+ sub $10,%r3
+ je 4f // 10 rounds for 128-bit key
+ add $32,%r6
+ sub $2,%r3
+ je 3f // 12 rounds for 128-bit key
+ add $32,%r6
+
+2: fwd_rnd( -64(%r6) ,ft_tab) // 14 rounds for 128-bit key
+ fwd_rnd( -48(%r6) ,ft_tab)
+3: fwd_rnd( -32(%r6) ,ft_tab) // 12 rounds for 128-bit key
+ fwd_rnd( -16(%r6) ,ft_tab)
+4: fwd_rnd( (%r6) ,ft_tab) // 10 rounds for 128-bit key
+ fwd_rnd( +16(%r6) ,ft_tab)
+ fwd_rnd( +32(%r6) ,ft_tab)
+ fwd_rnd( +48(%r6) ,ft_tab)
+ fwd_rnd( +64(%r6) ,ft_tab)
+ fwd_rnd( +80(%r6) ,ft_tab)
+ fwd_rnd( +96(%r6) ,ft_tab)
+ fwd_rnd(+112(%r6) ,ft_tab)
+ fwd_rnd(+128(%r6) ,ft_tab)
+ fwd_rnd(+144(%r6) ,fl_tab) // last round uses a different table
+
+// move final values to the output array. CAUTION: the
+// order of these assigns rely on the register mappings
+
+ add $8,%esp
+ mov out_blk+12(%esp),%r6
+ mov %r5,12(%r6)
+ pop %edi
+ mov %r4,8(%r6)
+ pop %esi
+ mov %r1,4(%r6)
+ pop %ebx
+ mov %r0,(%r6)
+ pop %ebp
+ mov $1,%eax
+ ret
+
+// AES (Rijndael) Decryption Subroutine
+
+.global aes_dec_blk
+
+.extern it_tab
+.extern il_tab
+
+.align 4
+
+aes_dec_blk:
+ push %ebp
+ mov ctx(%esp),%ebp // pointer to context
+ xor %eax,%eax
+
+// CAUTION: the order and the values used in these assigns
+// rely on the register mappings
+
+1: push %ebx
+ mov in_blk+4(%esp),%r2
+ push %esi
+ mov nrnd(%ebp),%r3 // number of rounds
+ push %edi
+ lea dkey(%ebp),%r6 // key pointer
+ mov %r3,%r0
+ shl $4,%r0
+ add %r0,%r6
+
+// input four columns and xor in first round key
+
+ mov (%r2),%r0
+ mov 4(%r2),%r1
+ mov 8(%r2),%r4
+ mov 12(%r2),%r5
+ xor (%r6),%r0
+ xor 4(%r6),%r1
+ xor 8(%r6),%r4
+ xor 12(%r6),%r5
+
+ sub $8,%esp // space for register saves on stack
+ sub $16,%r6 // increment to next round key
+ sub $10,%r3
+ je 4f // 10 rounds for 128-bit key
+ sub $32,%r6
+ sub $2,%r3
+ je 3f // 12 rounds for 128-bit key
+ sub $32,%r6
+
+2: inv_rnd( +64(%r6), it_tab) // 14 rounds for 128-bit key
+ inv_rnd( +48(%r6), it_tab)
+3: inv_rnd( +32(%r6), it_tab) // 12 rounds for 128-bit key
+ inv_rnd( +16(%r6), it_tab)
+4: inv_rnd( (%r6), it_tab) // 10 rounds for 128-bit key
+ inv_rnd( -16(%r6), it_tab)
+ inv_rnd( -32(%r6), it_tab)
+ inv_rnd( -48(%r6), it_tab)
+ inv_rnd( -64(%r6), it_tab)
+ inv_rnd( -80(%r6), it_tab)
+ inv_rnd( -96(%r6), it_tab)
+ inv_rnd(-112(%r6), it_tab)
+ inv_rnd(-128(%r6), it_tab)
+ inv_rnd(-144(%r6), il_tab) // last round uses a different table
+
+// move final values to the output array. CAUTION: the
+// order of these assigns rely on the register mappings
+
+ add $8,%esp
+ mov out_blk+12(%esp),%r6
+ mov %r5,12(%r6)
+ pop %edi
+ mov %r4,8(%r6)
+ pop %esi
+ mov %r1,4(%r6)
+ pop %ebx
+ mov %r0,(%r6)
+ pop %ebp
+ mov $1,%eax
+ ret
+
--- /dev/null
+/*
+ *
+ * Glue Code for optimized 586 assembler version of AES
+ *
+ * Copyright (c) 2002, Dr Brian Gladman <>, Worcester, UK.
+ * All rights reserved.
+ *
+ * LICENSE TERMS
+ *
+ * The free distribution and use of this software in both source and binary
+ * form is allowed (with or without changes) provided that:
+ *
+ * 1. distributions of this source code include the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ *
+ * 2. distributions in binary form include the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other associated materials;
+ *
+ * 3. the copyright holder's name is not used to endorse products
+ * built using this software without specific written permission.
+ *
+ * ALTERNATIVELY, provided that this notice is retained in full, this product
+ * may be distributed under the terms of the GNU General Public License (GPL),
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
+ *
+ * DISCLAIMER
+ *
+ * This software is provided 'as is' with no explicit or implied warranties
+ * in respect of its properties, including, but not limited to, correctness
+ * and/or fitness for purpose.
+ *
+ * Copyright (c) 2003, Adam J. Richter <adam@yggdrasil.com> (conversion to
+ * 2.5 API).
+ * Copyright (c) 2003, 2004 Fruhwirth Clemens <clemens@endorphin.org>
+ * Copyright (c) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/crypto.h>
+#include <linux/linkage.h>
+
+asmlinkage void aes_enc_blk(const u8 *src, u8 *dst, void *ctx);
+asmlinkage void aes_dec_blk(const u8 *src, u8 *dst, void *ctx);
+
+#define AES_MIN_KEY_SIZE 16
+#define AES_MAX_KEY_SIZE 32
+#define AES_BLOCK_SIZE 16
+#define AES_KS_LENGTH 4 * AES_BLOCK_SIZE
+#define RC_LENGTH 29
+
+struct aes_ctx {
+ u32 ekey[AES_KS_LENGTH];
+ u32 rounds;
+ u32 dkey[AES_KS_LENGTH];
+};
+
+#define WPOLY 0x011b
+#define u32_in(x) le32_to_cpu(*(const u32 *)(x))
+#define bytes2word(b0, b1, b2, b3) \
+ (((u32)(b3) << 24) | ((u32)(b2) << 16) | ((u32)(b1) << 8) | (b0))
+
+/* define the finite field multiplies required for Rijndael */
+#define f2(x) ((x) ? pow[log[x] + 0x19] : 0)
+#define f3(x) ((x) ? pow[log[x] + 0x01] : 0)
+#define f9(x) ((x) ? pow[log[x] + 0xc7] : 0)
+#define fb(x) ((x) ? pow[log[x] + 0x68] : 0)
+#define fd(x) ((x) ? pow[log[x] + 0xee] : 0)
+#define fe(x) ((x) ? pow[log[x] + 0xdf] : 0)
+#define fi(x) ((x) ? pow[255 - log[x]]: 0)
+
+static inline u32 upr(u32 x, int n)
+{
+ return (x << 8 * n) | (x >> (32 - 8 * n));
+}
+
+static inline u8 bval(u32 x, int n)
+{
+ return x >> 8 * n;
+}
+
+/* The forward and inverse affine transformations used in the S-box */
+#define fwd_affine(x) \
+ (w = (u32)x, w ^= (w<<1)^(w<<2)^(w<<3)^(w<<4), 0x63^(u8)(w^(w>>8)))
+
+#define inv_affine(x) \
+ (w = (u32)x, w = (w<<1)^(w<<3)^(w<<6), 0x05^(u8)(w^(w>>8)))
+
+static u32 rcon_tab[RC_LENGTH];
+
+u32 ft_tab[4][256];
+u32 fl_tab[4][256];
+u32 ls_tab[4][256];
+u32 im_tab[4][256];
+u32 il_tab[4][256];
+u32 it_tab[4][256];
+
+void gen_tabs(void)
+{
+ u32 i, w;
+ u8 pow[512], log[256];
+
+ /*
+ * log and power tables for GF(2^8) finite field with
+ * WPOLY as modular polynomial - the simplest primitive
+ * root is 0x03, used here to generate the tables.
+ */
+ i = 0; w = 1;
+
+ do {
+ pow[i] = (u8)w;
+ pow[i + 255] = (u8)w;
+ log[w] = (u8)i++;
+ w ^= (w << 1) ^ (w & 0x80 ? WPOLY : 0);
+ } while (w != 1);
+
+ for(i = 0, w = 1; i < RC_LENGTH; ++i) {
+ rcon_tab[i] = bytes2word(w, 0, 0, 0);
+ w = f2(w);
+ }
+
+ for(i = 0; i < 256; ++i) {
+ u8 b;
+
+ b = fwd_affine(fi((u8)i));
+ w = bytes2word(f2(b), b, b, f3(b));
+
+ /* tables for a normal encryption round */
+ ft_tab[0][i] = w;
+ ft_tab[1][i] = upr(w, 1);
+ ft_tab[2][i] = upr(w, 2);
+ ft_tab[3][i] = upr(w, 3);
+ w = bytes2word(b, 0, 0, 0);
+
+ /*
+ * tables for last encryption round
+ * (may also be used in the key schedule)
+ */
+ fl_tab[0][i] = w;
+ fl_tab[1][i] = upr(w, 1);
+ fl_tab[2][i] = upr(w, 2);
+ fl_tab[3][i] = upr(w, 3);
+
+ /*
+ * table for key schedule if fl_tab above is
+ * not of the required form
+ */
+ ls_tab[0][i] = w;
+ ls_tab[1][i] = upr(w, 1);
+ ls_tab[2][i] = upr(w, 2);
+ ls_tab[3][i] = upr(w, 3);
+
+ b = fi(inv_affine((u8)i));
+ w = bytes2word(fe(b), f9(b), fd(b), fb(b));
+
+ /* tables for the inverse mix column operation */
+ im_tab[0][b] = w;
+ im_tab[1][b] = upr(w, 1);
+ im_tab[2][b] = upr(w, 2);
+ im_tab[3][b] = upr(w, 3);
+
+ /* tables for a normal decryption round */
+ it_tab[0][i] = w;
+ it_tab[1][i] = upr(w,1);
+ it_tab[2][i] = upr(w,2);
+ it_tab[3][i] = upr(w,3);
+
+ w = bytes2word(b, 0, 0, 0);
+
+ /* tables for last decryption round */
+ il_tab[0][i] = w;
+ il_tab[1][i] = upr(w,1);
+ il_tab[2][i] = upr(w,2);
+ il_tab[3][i] = upr(w,3);
+ }
+}
+
+#define four_tables(x,tab,vf,rf,c) \
+( tab[0][bval(vf(x,0,c),rf(0,c))] ^ \
+ tab[1][bval(vf(x,1,c),rf(1,c))] ^ \
+ tab[2][bval(vf(x,2,c),rf(2,c))] ^ \
+ tab[3][bval(vf(x,3,c),rf(3,c))] \
+)
+
+#define vf1(x,r,c) (x)
+#define rf1(r,c) (r)
+#define rf2(r,c) ((r-c)&3)
+
+#define inv_mcol(x) four_tables(x,im_tab,vf1,rf1,0)
+#define ls_box(x,c) four_tables(x,fl_tab,vf1,rf2,c)
+
+#define ff(x) inv_mcol(x)
+
+#define ke4(k,i) \
+{ \
+ k[4*(i)+4] = ss[0] ^= ls_box(ss[3],3) ^ rcon_tab[i]; \
+ k[4*(i)+5] = ss[1] ^= ss[0]; \
+ k[4*(i)+6] = ss[2] ^= ss[1]; \
+ k[4*(i)+7] = ss[3] ^= ss[2]; \
+}
+
+#define kel4(k,i) \
+{ \
+ k[4*(i)+4] = ss[0] ^= ls_box(ss[3],3) ^ rcon_tab[i]; \
+ k[4*(i)+5] = ss[1] ^= ss[0]; \
+ k[4*(i)+6] = ss[2] ^= ss[1]; k[4*(i)+7] = ss[3] ^= ss[2]; \
+}
+
+#define ke6(k,i) \
+{ \
+ k[6*(i)+ 6] = ss[0] ^= ls_box(ss[5],3) ^ rcon_tab[i]; \
+ k[6*(i)+ 7] = ss[1] ^= ss[0]; \
+ k[6*(i)+ 8] = ss[2] ^= ss[1]; \
+ k[6*(i)+ 9] = ss[3] ^= ss[2]; \
+ k[6*(i)+10] = ss[4] ^= ss[3]; \
+ k[6*(i)+11] = ss[5] ^= ss[4]; \
+}
+
+#define kel6(k,i) \
+{ \
+ k[6*(i)+ 6] = ss[0] ^= ls_box(ss[5],3) ^ rcon_tab[i]; \
+ k[6*(i)+ 7] = ss[1] ^= ss[0]; \
+ k[6*(i)+ 8] = ss[2] ^= ss[1]; \
+ k[6*(i)+ 9] = ss[3] ^= ss[2]; \
+}
+
+#define ke8(k,i) \
+{ \
+ k[8*(i)+ 8] = ss[0] ^= ls_box(ss[7],3) ^ rcon_tab[i]; \
+ k[8*(i)+ 9] = ss[1] ^= ss[0]; \
+ k[8*(i)+10] = ss[2] ^= ss[1]; \
+ k[8*(i)+11] = ss[3] ^= ss[2]; \
+ k[8*(i)+12] = ss[4] ^= ls_box(ss[3],0); \
+ k[8*(i)+13] = ss[5] ^= ss[4]; \
+ k[8*(i)+14] = ss[6] ^= ss[5]; \
+ k[8*(i)+15] = ss[7] ^= ss[6]; \
+}
+
+#define kel8(k,i) \
+{ \
+ k[8*(i)+ 8] = ss[0] ^= ls_box(ss[7],3) ^ rcon_tab[i]; \
+ k[8*(i)+ 9] = ss[1] ^= ss[0]; \
+ k[8*(i)+10] = ss[2] ^= ss[1]; \
+ k[8*(i)+11] = ss[3] ^= ss[2]; \
+}
+
+#define kdf4(k,i) \
+{ \
+ ss[0] = ss[0] ^ ss[2] ^ ss[1] ^ ss[3]; \
+ ss[1] = ss[1] ^ ss[3]; \
+ ss[2] = ss[2] ^ ss[3]; \
+ ss[3] = ss[3]; \
+ ss[4] = ls_box(ss[(i+3) % 4], 3) ^ rcon_tab[i]; \
+ ss[i % 4] ^= ss[4]; \
+ ss[4] ^= k[4*(i)]; \
+ k[4*(i)+4] = ff(ss[4]); \
+ ss[4] ^= k[4*(i)+1]; \
+ k[4*(i)+5] = ff(ss[4]); \
+ ss[4] ^= k[4*(i)+2]; \
+ k[4*(i)+6] = ff(ss[4]); \
+ ss[4] ^= k[4*(i)+3]; \
+ k[4*(i)+7] = ff(ss[4]); \
+}
+
+#define kd4(k,i) \
+{ \
+ ss[4] = ls_box(ss[(i+3) % 4], 3) ^ rcon_tab[i]; \
+ ss[i % 4] ^= ss[4]; \
+ ss[4] = ff(ss[4]); \
+ k[4*(i)+4] = ss[4] ^= k[4*(i)]; \
+ k[4*(i)+5] = ss[4] ^= k[4*(i)+1]; \
+ k[4*(i)+6] = ss[4] ^= k[4*(i)+2]; \
+ k[4*(i)+7] = ss[4] ^= k[4*(i)+3]; \
+}
+
+#define kdl4(k,i) \
+{ \
+ ss[4] = ls_box(ss[(i+3) % 4], 3) ^ rcon_tab[i]; \
+ ss[i % 4] ^= ss[4]; \
+ k[4*(i)+4] = (ss[0] ^= ss[1]) ^ ss[2] ^ ss[3]; \
+ k[4*(i)+5] = ss[1] ^ ss[3]; \
+ k[4*(i)+6] = ss[0]; \
+ k[4*(i)+7] = ss[1]; \
+}
+
+#define kdf6(k,i) \
+{ \
+ ss[0] ^= ls_box(ss[5],3) ^ rcon_tab[i]; \
+ k[6*(i)+ 6] = ff(ss[0]); \
+ ss[1] ^= ss[0]; \
+ k[6*(i)+ 7] = ff(ss[1]); \
+ ss[2] ^= ss[1]; \
+ k[6*(i)+ 8] = ff(ss[2]); \
+ ss[3] ^= ss[2]; \
+ k[6*(i)+ 9] = ff(ss[3]); \
+ ss[4] ^= ss[3]; \
+ k[6*(i)+10] = ff(ss[4]); \
+ ss[5] ^= ss[4]; \
+ k[6*(i)+11] = ff(ss[5]); \
+}
+
+#define kd6(k,i) \
+{ \
+ ss[6] = ls_box(ss[5],3) ^ rcon_tab[i]; \
+ ss[0] ^= ss[6]; ss[6] = ff(ss[6]); \
+ k[6*(i)+ 6] = ss[6] ^= k[6*(i)]; \
+ ss[1] ^= ss[0]; \
+ k[6*(i)+ 7] = ss[6] ^= k[6*(i)+ 1]; \
+ ss[2] ^= ss[1]; \
+ k[6*(i)+ 8] = ss[6] ^= k[6*(i)+ 2]; \
+ ss[3] ^= ss[2]; \
+ k[6*(i)+ 9] = ss[6] ^= k[6*(i)+ 3]; \
+ ss[4] ^= ss[3]; \
+ k[6*(i)+10] = ss[6] ^= k[6*(i)+ 4]; \
+ ss[5] ^= ss[4]; \
+ k[6*(i)+11] = ss[6] ^= k[6*(i)+ 5]; \
+}
+
+#define kdl6(k,i) \
+{ \
+ ss[0] ^= ls_box(ss[5],3) ^ rcon_tab[i]; \
+ k[6*(i)+ 6] = ss[0]; \
+ ss[1] ^= ss[0]; \
+ k[6*(i)+ 7] = ss[1]; \
+ ss[2] ^= ss[1]; \
+ k[6*(i)+ 8] = ss[2]; \
+ ss[3] ^= ss[2]; \
+ k[6*(i)+ 9] = ss[3]; \
+}
+
+#define kdf8(k,i) \
+{ \
+ ss[0] ^= ls_box(ss[7],3) ^ rcon_tab[i]; \
+ k[8*(i)+ 8] = ff(ss[0]); \
+ ss[1] ^= ss[0]; \
+ k[8*(i)+ 9] = ff(ss[1]); \
+ ss[2] ^= ss[1]; \
+ k[8*(i)+10] = ff(ss[2]); \
+ ss[3] ^= ss[2]; \
+ k[8*(i)+11] = ff(ss[3]); \
+ ss[4] ^= ls_box(ss[3],0); \
+ k[8*(i)+12] = ff(ss[4]); \
+ ss[5] ^= ss[4]; \
+ k[8*(i)+13] = ff(ss[5]); \
+ ss[6] ^= ss[5]; \
+ k[8*(i)+14] = ff(ss[6]); \
+ ss[7] ^= ss[6]; \
+ k[8*(i)+15] = ff(ss[7]); \
+}
+
+#define kd8(k,i) \
+{ \
+ u32 __g = ls_box(ss[7],3) ^ rcon_tab[i]; \
+ ss[0] ^= __g; \
+ __g = ff(__g); \
+ k[8*(i)+ 8] = __g ^= k[8*(i)]; \
+ ss[1] ^= ss[0]; \
+ k[8*(i)+ 9] = __g ^= k[8*(i)+ 1]; \
+ ss[2] ^= ss[1]; \
+ k[8*(i)+10] = __g ^= k[8*(i)+ 2]; \
+ ss[3] ^= ss[2]; \
+ k[8*(i)+11] = __g ^= k[8*(i)+ 3]; \
+ __g = ls_box(ss[3],0); \
+ ss[4] ^= __g; \
+ __g = ff(__g); \
+ k[8*(i)+12] = __g ^= k[8*(i)+ 4]; \
+ ss[5] ^= ss[4]; \
+ k[8*(i)+13] = __g ^= k[8*(i)+ 5]; \
+ ss[6] ^= ss[5]; \
+ k[8*(i)+14] = __g ^= k[8*(i)+ 6]; \
+ ss[7] ^= ss[6]; \
+ k[8*(i)+15] = __g ^= k[8*(i)+ 7]; \
+}
+
+#define kdl8(k,i) \
+{ \
+ ss[0] ^= ls_box(ss[7],3) ^ rcon_tab[i]; \
+ k[8*(i)+ 8] = ss[0]; \
+ ss[1] ^= ss[0]; \
+ k[8*(i)+ 9] = ss[1]; \
+ ss[2] ^= ss[1]; \
+ k[8*(i)+10] = ss[2]; \
+ ss[3] ^= ss[2]; \
+ k[8*(i)+11] = ss[3]; \
+}
+
+static int
+aes_set_key(void *ctx_arg, const u8 *in_key, unsigned int key_len, u32 *flags)
+{
+ int i;
+ u32 ss[8];
+ struct aes_ctx *ctx = ctx_arg;
+
+ /* encryption schedule */
+
+ ctx->ekey[0] = ss[0] = u32_in(in_key);
+ ctx->ekey[1] = ss[1] = u32_in(in_key + 4);
+ ctx->ekey[2] = ss[2] = u32_in(in_key + 8);
+ ctx->ekey[3] = ss[3] = u32_in(in_key + 12);
+
+ switch(key_len) {
+ case 16:
+ for (i = 0; i < 9; i++)
+ ke4(ctx->ekey, i);
+ kel4(ctx->ekey, 9);
+ ctx->rounds = 10;
+ break;
+
+ case 24:
+ ctx->ekey[4] = ss[4] = u32_in(in_key + 16);
+ ctx->ekey[5] = ss[5] = u32_in(in_key + 20);
+ for (i = 0; i < 7; i++)
+ ke6(ctx->ekey, i);
+ kel6(ctx->ekey, 7);
+ ctx->rounds = 12;
+ break;
+
+ case 32:
+ ctx->ekey[4] = ss[4] = u32_in(in_key + 16);
+ ctx->ekey[5] = ss[5] = u32_in(in_key + 20);
+ ctx->ekey[6] = ss[6] = u32_in(in_key + 24);
+ ctx->ekey[7] = ss[7] = u32_in(in_key + 28);
+ for (i = 0; i < 6; i++)
+ ke8(ctx->ekey, i);
+ kel8(ctx->ekey, 6);
+ ctx->rounds = 14;
+ break;
+
+ default:
+ *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+ return -EINVAL;
+ }
+
+ /* decryption schedule */
+
+ ctx->dkey[0] = ss[0] = u32_in(in_key);
+ ctx->dkey[1] = ss[1] = u32_in(in_key + 4);
+ ctx->dkey[2] = ss[2] = u32_in(in_key + 8);
+ ctx->dkey[3] = ss[3] = u32_in(in_key + 12);
+
+ switch (key_len) {
+ case 16:
+ kdf4(ctx->dkey, 0);
+ for (i = 1; i < 9; i++)
+ kd4(ctx->dkey, i);
+ kdl4(ctx->dkey, 9);
+ break;
+
+ case 24:
+ ctx->dkey[4] = ff(ss[4] = u32_in(in_key + 16));
+ ctx->dkey[5] = ff(ss[5] = u32_in(in_key + 20));
+ kdf6(ctx->dkey, 0);
+ for (i = 1; i < 7; i++)
+ kd6(ctx->dkey, i);
+ kdl6(ctx->dkey, 7);
+ break;
+
+ case 32:
+ ctx->dkey[4] = ff(ss[4] = u32_in(in_key + 16));
+ ctx->dkey[5] = ff(ss[5] = u32_in(in_key + 20));
+ ctx->dkey[6] = ff(ss[6] = u32_in(in_key + 24));
+ ctx->dkey[7] = ff(ss[7] = u32_in(in_key + 28));
+ kdf8(ctx->dkey, 0);
+ for (i = 1; i < 6; i++)
+ kd8(ctx->dkey, i);
+ kdl8(ctx->dkey, 6);
+ break;
+ }
+ return 0;
+}
+
+static inline void aes_encrypt(void *ctx, u8 *dst, const u8 *src)
+{
+ aes_enc_blk(src, dst, ctx);
+}
+static inline void aes_decrypt(void *ctx, u8 *dst, const u8 *src)
+{
+ aes_dec_blk(src, dst, ctx);
+}
+
+
+static struct crypto_alg aes_alg = {
+ .cra_name = "aes",
+ .cra_flags = CRYPTO_ALG_TYPE_CIPHER,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct aes_ctx),
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(aes_alg.cra_list),
+ .cra_u = {
+ .cipher = {
+ .cia_min_keysize = AES_MIN_KEY_SIZE,
+ .cia_max_keysize = AES_MAX_KEY_SIZE,
+ .cia_setkey = aes_set_key,
+ .cia_encrypt = aes_encrypt,
+ .cia_decrypt = aes_decrypt
+ }
+ }
+};
+
+static int __init aes_init(void)
+{
+ gen_tabs();
+ return crypto_register_alg(&aes_alg);
+}
+
+static void __exit aes_fini(void)
+{
+ crypto_unregister_alg(&aes_alg);
+}
+
+module_init(aes_init);
+module_exit(aes_fini);
+
+MODULE_DESCRIPTION("Rijndael (AES) Cipher Algorithm, i586 asm optimized");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Fruhwirth Clemens, James Morris, Brian Gladman, Adam Richter");
+MODULE_ALIAS("aes");
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- /* Can't seek (pwrite) on this device */
- if (ppos != &file->f_pos)
- return -ESPIPE;
memset(line, 0, LINE_SIZE);
if (len > LINE_SIZE)
len = LINE_SIZE;
switch (cmd) {
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
case MTRRIOC_ADD_ENTRY:
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
#
CONFIG_EXPERIMENTAL=y
CONFIG_CLEAN_COMPILE=y
-CONFIG_STANDALONE=y
#
# General setup
#
CONFIG_SWAP=y
CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
# CONFIG_BSD_PROCESS_ACCT is not set
CONFIG_SYSCTL=y
+# CONFIG_AUDIT is not set
CONFIG_LOG_BUF_SHIFT=20
+CONFIG_HOTPLUG=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
# CONFIG_EMBEDDED is not set
CONFIG_KALLSYMS=y
+CONFIG_KALLSYMS_ALL=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
CONFIG_FUTEX=y
CONFIG_EPOLL=y
CONFIG_IOSCHED_NOOP=y
CONFIG_IOSCHED_AS=y
CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
#
CONFIG_OBSOLETE_MODPARM=y
CONFIG_MODVERSIONS=y
CONFIG_KMOD=y
+CONFIG_STOP_MACHINE=y
#
# Processor type and features
CONFIG_MMU=y
CONFIG_RWSEM_XCHGADD_ALGORITHM=y
CONFIG_TIME_INTERPOLATION=y
-# CONFIG_ITANIUM is not set
-CONFIG_MCKINLEY=y
+CONFIG_EFI=y
CONFIG_IA64_GENERIC=y
# CONFIG_IA64_DIG is not set
-# CONFIG_IA64_HP_SIM is not set
# CONFIG_IA64_HP_ZX1 is not set
# CONFIG_IA64_SGI_SN2 is not set
+# CONFIG_IA64_HP_SIM is not set
+# CONFIG_ITANIUM is not set
+CONFIG_MCKINLEY=y
# CONFIG_IA64_PAGE_SIZE_4KB is not set
# CONFIG_IA64_PAGE_SIZE_8KB is not set
CONFIG_IA64_PAGE_SIZE_16KB=y
# CONFIG_IA64_PAGE_SIZE_64KB is not set
-CONFIG_ACPI=y
-CONFIG_ACPI_INTERPRETER=y
-CONFIG_ACPI_KERNEL_CONFIG=y
CONFIG_IA64_L1_CACHE_SHIFT=7
-# CONFIG_MCKINLEY_ASTEP_SPECIFIC is not set
CONFIG_NUMA=y
-CONFIG_DISCONTIGMEM=y
CONFIG_VIRTUAL_MEM_MAP=y
-CONFIG_IA64_MCA=y
-CONFIG_PM=y
+CONFIG_DISCONTIGMEM=y
+CONFIG_IA64_CYCLONE=y
CONFIG_IOSAPIC=y
CONFIG_FORCE_MAX_ZONEORDER=18
-# CONFIG_HUGETLB_PAGE_SIZE_4GB is not set
-# CONFIG_HUGETLB_PAGE_SIZE_1GB is not set
-# CONFIG_HUGETLB_PAGE_SIZE_256MB is not set
-# CONFIG_HUGETLB_PAGE_SIZE_64MB is not set
-CONFIG_HUGETLB_PAGE_SIZE_16MB=y
-# CONFIG_HUGETLB_PAGE_SIZE_4MB is not set
-# CONFIG_HUGETLB_PAGE_SIZE_1MB is not set
-# CONFIG_HUGETLB_PAGE_SIZE_256KB is not set
-# CONFIG_IA64_PAL_IDLE is not set
CONFIG_SMP=y
+CONFIG_NR_CPUS=512
+CONFIG_HOTPLUG_CPU=y
# CONFIG_PREEMPT is not set
+CONFIG_HAVE_DEC_LOCK=y
CONFIG_IA32_SUPPORT=y
CONFIG_COMPAT=y
-CONFIG_HAVE_DEC_LOCK=y
CONFIG_PERFMON=y
CONFIG_IA64_PALINFO=y
-CONFIG_EFI=y
+
+#
+# Firmware Drivers
+#
CONFIG_EFI_VARS=y
-CONFIG_NR_CPUS=512
+CONFIG_EFI_PCDP=y
CONFIG_BINFMT_ELF=y
CONFIG_BINFMT_MISC=m
+#
+# Power management and ACPI
+#
+CONFIG_PM=y
+CONFIG_ACPI=y
+
#
# ACPI (Advanced Configuration and Power Interface) Support
#
CONFIG_ACPI_BOOT=y
+CONFIG_ACPI_INTERPRETER=y
CONFIG_ACPI_BUTTON=m
CONFIG_ACPI_FAN=m
CONFIG_ACPI_PROCESSOR=m
CONFIG_ACPI_POWER=y
CONFIG_ACPI_PCI=y
CONFIG_ACPI_SYSTEM=y
+
+#
+# Bus options (PCI, PCMCIA)
+#
CONFIG_PCI=y
CONFIG_PCI_DOMAINS=y
+# CONFIG_PCI_MSI is not set
CONFIG_PCI_LEGACY_PROC=y
CONFIG_PCI_NAMES=y
-CONFIG_HOTPLUG=y
#
# PCI Hotplug Support
# CONFIG_HOTPLUG_PCI_FAKE is not set
CONFIG_HOTPLUG_PCI_ACPI=m
# CONFIG_HOTPLUG_PCI_CPCI is not set
+# CONFIG_HOTPLUG_PCI_PCIE is not set
+# CONFIG_HOTPLUG_PCI_SHPC is not set
#
# PCMCIA/CardBus support
# CONFIG_PCMCIA is not set
#
-# Parallel port support
+# Device Drivers
#
-# CONFIG_PARPORT is not set
#
# Generic Driver Options
#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
# CONFIG_FW_LOADER is not set
+# CONFIG_DEBUG_DRIVER is not set
#
# Memory Technology Devices (MTD)
#
# CONFIG_MTD is not set
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
#
# Plug and Play support
#
-# CONFIG_PNP is not set
#
# Block devices
#
-# CONFIG_BLK_DEV_FD is not set
# CONFIG_BLK_CPQ_DA is not set
# CONFIG_BLK_CPQ_CISS_DA is not set
# CONFIG_BLK_DEV_DAC960 is not set
CONFIG_BLK_DEV_LOOP=m
CONFIG_BLK_DEV_CRYPTOLOOP=m
CONFIG_BLK_DEV_NBD=m
+# CONFIG_BLK_DEV_SX8 is not set
CONFIG_BLK_DEV_RAM=m
CONFIG_BLK_DEV_RAM_SIZE=4096
-# CONFIG_BLK_DEV_INITRD is not set
#
# ATA/ATAPI/MFM/RLL support
#
# Please see Documentation/ide.txt for help/info on IDE drives
#
+# CONFIG_BLK_DEV_IDE_SATA is not set
CONFIG_BLK_DEV_IDEDISK=y
# CONFIG_IDEDISK_MULTI_MODE is not set
-# CONFIG_IDEDISK_STROKE is not set
CONFIG_BLK_DEV_IDECD=y
# CONFIG_BLK_DEV_IDETAPE is not set
CONFIG_BLK_DEV_IDEFLOPPY=y
#
# IDE chipset support/bugfixes
#
+CONFIG_IDE_GENERIC=y
CONFIG_BLK_DEV_IDEPCI=y
# CONFIG_IDEPCI_SHARE_IRQ is not set
# CONFIG_BLK_DEV_OFFBOARD is not set
# CONFIG_BLK_DEV_PDC202XX_OLD is not set
# CONFIG_BLK_DEV_PDC202XX_NEW is not set
# CONFIG_BLK_DEV_SVWKS is not set
+CONFIG_BLK_DEV_SGIIOC4=y
# CONFIG_BLK_DEV_SIIMAGE is not set
# CONFIG_BLK_DEV_SLC90E66 is not set
# CONFIG_BLK_DEV_TRM290 is not set
# CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_IDE_ARM is not set
CONFIG_BLK_DEV_IDEDMA=y
# CONFIG_IDEDMA_IVB is not set
CONFIG_IDEDMA_AUTO=y
# CONFIG_BLK_DEV_HD is not set
-#
-# IEEE 1394 (FireWire) support (EXPERIMENTAL)
-#
-# CONFIG_IEEE1394 is not set
-
-#
-# I2O device support
-#
-# CONFIG_I2O is not set
-
-#
-# Multi-device support (RAID and LVM)
-#
-CONFIG_MD=y
-CONFIG_BLK_DEV_MD=m
-CONFIG_MD_LINEAR=m
-CONFIG_MD_RAID0=m
-CONFIG_MD_RAID1=m
-CONFIG_MD_RAID5=m
-CONFIG_MD_MULTIPATH=m
-CONFIG_BLK_DEV_DM=m
-
-#
-# Fusion MPT device support
-#
-CONFIG_FUSION=y
-CONFIG_FUSION_BOOT=y
-CONFIG_FUSION_MAX_SGE=40
-# CONFIG_FUSION_ISENSE is not set
-# CONFIG_FUSION_CTL is not set
-
#
# SCSI device support
#
# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
#
# CONFIG_SCSI_MULTI_LUN is not set
-CONFIG_SCSI_REPORT_LUNS=y
# CONFIG_SCSI_CONSTANTS is not set
# CONFIG_SCSI_LOGGING is not set
+#
+# SCSI Transport Attributes
+#
+CONFIG_SCSI_SPI_ATTRS=y
+CONFIG_SCSI_FC_ATTRS=y
+
#
# SCSI low-level drivers
#
# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_3W_9XXX is not set
# CONFIG_SCSI_ACARD is not set
# CONFIG_SCSI_AACRAID is not set
# CONFIG_SCSI_AIC7XXX is not set
# CONFIG_SCSI_AIC7XXX_OLD is not set
# CONFIG_SCSI_AIC79XX is not set
-# CONFIG_SCSI_ADVANSYS is not set
# CONFIG_SCSI_MEGARAID is not set
# CONFIG_SCSI_SATA is not set
# CONFIG_SCSI_BUSLOGIC is not set
-# CONFIG_SCSI_CPQFCTS is not set
# CONFIG_SCSI_DMX3191D is not set
# CONFIG_SCSI_EATA is not set
# CONFIG_SCSI_EATA_PIO is not set
CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set
+# CONFIG_SCSI_IPR is not set
# CONFIG_SCSI_QLOGIC_ISP is not set
CONFIG_SCSI_QLOGIC_FC=y
# CONFIG_SCSI_QLOGIC_FC_FIRMWARE is not set
CONFIG_SCSI_QLOGIC_1280=y
+CONFIG_SCSI_QLA2XXX=y
+CONFIG_SCSI_QLA21XX=m
+CONFIG_SCSI_QLA22XX=m
+CONFIG_SCSI_QLA2300=m
+CONFIG_SCSI_QLA2322=m
+# CONFIG_SCSI_QLA6312 is not set
+# CONFIG_SCSI_QLA6322 is not set
# CONFIG_SCSI_DC395x is not set
# CONFIG_SCSI_DC390T is not set
-# CONFIG_SCSI_NSP32 is not set
# CONFIG_SCSI_DEBUG is not set
+#
+# Multi-device support (RAID and LVM)
+#
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=m
+CONFIG_MD_LINEAR=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_RAID1=m
+CONFIG_MD_RAID5=m
+CONFIG_MD_RAID6=m
+CONFIG_MD_MULTIPATH=m
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+
+#
+# Fusion MPT device support
+#
+CONFIG_FUSION=y
+CONFIG_FUSION_MAX_SGE=40
+# CONFIG_FUSION_ISENSE is not set
+# CONFIG_FUSION_CTL is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_IEEE1394 is not set
+
+#
+# I2O device support
+#
+# CONFIG_I2O is not set
+
#
# Networking support
#
# CONFIG_NET_IPGRE is not set
# CONFIG_IP_MROUTE is not set
CONFIG_ARPD=y
-# CONFIG_INET_ECN is not set
CONFIG_SYN_COOKIES=y
# CONFIG_INET_AH is not set
# CONFIG_INET_ESP is not set
# CONFIG_INET_IPCOMP is not set
# CONFIG_IPV6 is not set
-# CONFIG_DECNET is not set
-# CONFIG_BRIDGE is not set
# CONFIG_NETFILTER is not set
#
# SCTP Configuration (EXPERIMENTAL)
#
-CONFIG_IPV6_SCTP__=y
# CONFIG_IP_SCTP is not set
# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
# CONFIG_LLC2 is not set
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# QoS and/or fair queueing
#
# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE is not set
#
# Network testing
#
# CONFIG_NET_PKTGEN is not set
+CONFIG_NETPOLL=y
+# CONFIG_NETPOLL_RX is not set
+# CONFIG_NETPOLL_TRAP is not set
+CONFIG_NET_POLL_CONTROLLER=y
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
CONFIG_NETDEVICES=y
-
-#
-# ARCnet devices
-#
-# CONFIG_ARCNET is not set
CONFIG_DUMMY=m
# CONFIG_BONDING is not set
# CONFIG_EQUALIZER is not set
# CONFIG_TUN is not set
# CONFIG_ETHERTAP is not set
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+
#
# Ethernet (10 or 100Mbit)
#
CONFIG_TULIP=m
# CONFIG_TULIP_MWI is not set
# CONFIG_TULIP_MMIO is not set
+# CONFIG_TULIP_NAPI is not set
# CONFIG_DE4X5 is not set
# CONFIG_WINBOND_840 is not set
# CONFIG_DM9102 is not set
# CONFIG_AMD8111_ETH is not set
# CONFIG_ADAPTEC_STARFIRE is not set
# CONFIG_B44 is not set
+# CONFIG_FORCEDETH is not set
# CONFIG_DGRS is not set
CONFIG_EEPRO100=m
# CONFIG_EEPRO100_PIO is not set
CONFIG_E100=m
+# CONFIG_E100_NAPI is not set
# CONFIG_FEALNX is not set
# CONFIG_NATSEMI is not set
# CONFIG_NE2K_PCI is not set
# CONFIG_EPIC100 is not set
# CONFIG_SUNDANCE is not set
# CONFIG_VIA_RHINE is not set
+# CONFIG_VIA_VELOCITY is not set
#
# Ethernet (1000 Mbit)
# CONFIG_HAMACHI is not set
# CONFIG_YELLOWFIN is not set
# CONFIG_R8169 is not set
-# CONFIG_SIS190 is not set
# CONFIG_SK98LIN is not set
CONFIG_TIGON3=y
# Ethernet (10000 Mbit)
#
# CONFIG_IXGB is not set
-# CONFIG_FDDI is not set
-# CONFIG_HIPPI is not set
-# CONFIG_PPP is not set
-# CONFIG_SLIP is not set
-
-#
-# Wireless LAN (non-hamradio)
-#
-# CONFIG_NET_RADIO is not set
+# CONFIG_S2IO is not set
#
# Token Ring devices
#
# CONFIG_TR is not set
-# CONFIG_NET_FC is not set
-# CONFIG_SHAPER is not set
-
-#
-# Wan interfaces
-#
-# CONFIG_WAN is not set
#
-# Amateur Radio support
+# Wireless LAN (non-hamradio)
#
-# CONFIG_HAMRADIO is not set
+# CONFIG_NET_RADIO is not set
#
-# IrDA (infrared) support
+# Wan interfaces
#
-# CONFIG_IRDA is not set
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NET_FC is not set
+# CONFIG_SHAPER is not set
+CONFIG_NETCONSOLE=y
#
-# Bluetooth support
+# ISDN subsystem
#
-# CONFIG_BT is not set
+# CONFIG_ISDN is not set
#
-# ISDN subsystem
+# Telephony Support
#
-# CONFIG_ISDN_BOOL is not set
+# CONFIG_PHONE is not set
#
# Input device support
CONFIG_INPUT_KEYBOARD=y
CONFIG_KEYBOARD_ATKBD=y
# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
# CONFIG_KEYBOARD_XTKBD is not set
# CONFIG_KEYBOARD_NEWTON is not set
CONFIG_INPUT_MOUSE=y
CONFIG_MOUSE_PS2=y
# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_VSXXXAA is not set
# CONFIG_INPUT_JOYSTICK is not set
# CONFIG_INPUT_TOUCHSCREEN is not set
# CONFIG_INPUT_MISC is not set
CONFIG_HW_CONSOLE=y
CONFIG_SERIAL_NONSTANDARD=y
# CONFIG_ROCKETPORT is not set
+# CONFIG_CYCLADES is not set
# CONFIG_SYNCLINK is not set
# CONFIG_SYNCLINKMP is not set
# CONFIG_N_HDLC is not set
# CONFIG_STALDRV is not set
-CONFIG_SGI_L1_SERIAL=y
-CONFIG_SGI_L1_SERIAL_CONSOLE=y
#
# Serial drivers
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_ACPI=y
-CONFIG_SERIAL_8250_HCDP=y
CONFIG_SERIAL_8250_NR_UARTS=6
CONFIG_SERIAL_8250_EXTENDED=y
CONFIG_SERIAL_8250_SHARE_IRQ=y
#
CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_SERIAL_SGI_L1_CONSOLE=y
CONFIG_UNIX98_PTYS=y
-CONFIG_UNIX98_PTY_COUNT=256
-
-#
-# I2C support
-#
-# CONFIG_I2C is not set
-
-#
-# I2C Algorithms
-#
-
-#
-# I2C Hardware Bus support
-#
-
-#
-# I2C Hardware Sensors Chip support
-#
-# CONFIG_I2C_SENSOR is not set
-
-#
-# Mice
-#
-# CONFIG_BUSMOUSE is not set
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
# CONFIG_QIC02_TAPE is not set
#
#
# CONFIG_WATCHDOG is not set
# CONFIG_HW_RANDOM is not set
-# CONFIG_NVRAM is not set
-# CONFIG_GEN_RTC is not set
CONFIG_EFI_RTC=y
# CONFIG_DTLK is not set
# CONFIG_R3964 is not set
CONFIG_DRM_MGA=m
CONFIG_DRM_SIS=m
CONFIG_RAW_DRIVER=m
+CONFIG_HPET=y
+# CONFIG_HPET_RTC_IRQ is not set
+CONFIG_HPET_MMAP=y
CONFIG_MAX_RAW_DEVS=256
#
-# Multimedia devices
-#
-# CONFIG_VIDEO_DEV is not set
-
-#
-# Digital Video Broadcasting Devices
-#
-# CONFIG_DVB is not set
-
-#
-# File systems
-#
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT2_FS_POSIX_ACL=y
-CONFIG_EXT2_FS_SECURITY=y
-CONFIG_EXT3_FS=y
-CONFIG_EXT3_FS_XATTR=y
-CONFIG_EXT3_FS_POSIX_ACL=y
-CONFIG_EXT3_FS_SECURITY=y
-CONFIG_JBD=y
-# CONFIG_JBD_DEBUG is not set
-CONFIG_FS_MBCACHE=y
-CONFIG_REISERFS_FS=m
-# CONFIG_REISERFS_CHECK is not set
-# CONFIG_REISERFS_PROC_INFO is not set
-# CONFIG_JFS_FS is not set
-CONFIG_FS_POSIX_ACL=y
-CONFIG_XFS_FS=y
-# CONFIG_XFS_RT is not set
-# CONFIG_XFS_QUOTA is not set
-# CONFIG_XFS_POSIX_ACL is not set
-# CONFIG_MINIX_FS is not set
-# CONFIG_ROMFS_FS is not set
-# CONFIG_QUOTA is not set
-CONFIG_AUTOFS_FS=y
-CONFIG_AUTOFS4_FS=y
-
-#
-# CD-ROM/DVD Filesystems
-#
-CONFIG_ISO9660_FS=m
-CONFIG_JOLIET=y
-# CONFIG_ZISOFS is not set
-CONFIG_UDF_FS=m
-
-#
-# DOS/FAT/NT Filesystems
-#
-CONFIG_FAT_FS=y
-# CONFIG_MSDOS_FS is not set
-CONFIG_VFAT_FS=y
-CONFIG_NTFS_FS=m
-# CONFIG_NTFS_DEBUG is not set
-# CONFIG_NTFS_RW is not set
-
-#
-# Pseudo filesystems
+# I2C support
#
-CONFIG_PROC_FS=y
-CONFIG_PROC_KCORE=y
-# CONFIG_DEVFS_FS is not set
-CONFIG_DEVPTS_FS=y
-# CONFIG_DEVPTS_FS_XATTR is not set
-CONFIG_TMPFS=y
-CONFIG_HUGETLBFS=y
-CONFIG_HUGETLB_PAGE=y
-CONFIG_RAMFS=y
+# CONFIG_I2C is not set
#
-# Miscellaneous filesystems
+# Dallas's 1-wire bus
#
-# CONFIG_ADFS_FS is not set
-# CONFIG_AFFS_FS is not set
-# CONFIG_HFS_FS is not set
-# CONFIG_BEFS_FS is not set
-# CONFIG_BFS_FS is not set
-# CONFIG_EFS_FS is not set
-# CONFIG_CRAMFS is not set
-# CONFIG_VXFS_FS is not set
-# CONFIG_HPFS_FS is not set
-# CONFIG_QNX4FS_FS is not set
-# CONFIG_SYSV_FS is not set
-# CONFIG_UFS_FS is not set
+# CONFIG_W1 is not set
#
-# Network File Systems
+# Misc devices
#
-CONFIG_NFS_FS=m
-CONFIG_NFS_V3=y
-CONFIG_NFS_V4=y
-CONFIG_NFS_DIRECTIO=y
-CONFIG_NFSD=m
-CONFIG_NFSD_V3=y
-CONFIG_NFSD_V4=y
-CONFIG_NFSD_TCP=y
-CONFIG_LOCKD=m
-CONFIG_LOCKD_V4=y
-CONFIG_EXPORTFS=m
-CONFIG_SUNRPC=m
-# CONFIG_SUNRPC_GSS is not set
-CONFIG_SMB_FS=m
-CONFIG_SMB_NLS_DEFAULT=y
-CONFIG_SMB_NLS_REMOTE="cp437"
-CONFIG_CIFS=m
-# CONFIG_NCP_FS is not set
-# CONFIG_CODA_FS is not set
-# CONFIG_INTERMEZZO_FS is not set
-# CONFIG_AFS_FS is not set
#
-# Partition Types
+# Multimedia devices
#
-CONFIG_PARTITION_ADVANCED=y
-# CONFIG_ACORN_PARTITION is not set
-# CONFIG_OSF_PARTITION is not set
-# CONFIG_AMIGA_PARTITION is not set
-# CONFIG_ATARI_PARTITION is not set
-# CONFIG_MAC_PARTITION is not set
-CONFIG_MSDOS_PARTITION=y
-# CONFIG_BSD_DISKLABEL is not set
-# CONFIG_MINIX_SUBPARTITION is not set
-# CONFIG_SOLARIS_X86_PARTITION is not set
-# CONFIG_UNIXWARE_DISKLABEL is not set
-# CONFIG_LDM_PARTITION is not set
-# CONFIG_NEC98_PARTITION is not set
-CONFIG_SGI_PARTITION=y
-# CONFIG_ULTRIX_PARTITION is not set
-# CONFIG_SUN_PARTITION is not set
-CONFIG_EFI_PARTITION=y
+# CONFIG_VIDEO_DEV is not set
#
-# Native Language Support
+# Digital Video Broadcasting Devices
#
-CONFIG_NLS=y
-CONFIG_NLS_DEFAULT="iso8859-1"
-CONFIG_NLS_CODEPAGE_437=m
-CONFIG_NLS_CODEPAGE_737=m
-CONFIG_NLS_CODEPAGE_775=m
-CONFIG_NLS_CODEPAGE_850=m
-CONFIG_NLS_CODEPAGE_852=m
-CONFIG_NLS_CODEPAGE_855=m
-CONFIG_NLS_CODEPAGE_857=m
-CONFIG_NLS_CODEPAGE_860=m
-CONFIG_NLS_CODEPAGE_861=m
-CONFIG_NLS_CODEPAGE_862=m
-CONFIG_NLS_CODEPAGE_863=m
-CONFIG_NLS_CODEPAGE_864=m
-CONFIG_NLS_CODEPAGE_865=m
-CONFIG_NLS_CODEPAGE_866=m
-CONFIG_NLS_CODEPAGE_869=m
-CONFIG_NLS_CODEPAGE_936=m
-CONFIG_NLS_CODEPAGE_950=m
-CONFIG_NLS_CODEPAGE_932=m
-CONFIG_NLS_CODEPAGE_949=m
-CONFIG_NLS_CODEPAGE_874=m
-CONFIG_NLS_ISO8859_8=m
-CONFIG_NLS_CODEPAGE_1250=m
-CONFIG_NLS_CODEPAGE_1251=m
-CONFIG_NLS_ISO8859_1=m
-CONFIG_NLS_ISO8859_2=m
-CONFIG_NLS_ISO8859_3=m
-CONFIG_NLS_ISO8859_4=m
-CONFIG_NLS_ISO8859_5=m
-CONFIG_NLS_ISO8859_6=m
-CONFIG_NLS_ISO8859_7=m
-CONFIG_NLS_ISO8859_9=m
-CONFIG_NLS_ISO8859_13=m
-CONFIG_NLS_ISO8859_14=m
-CONFIG_NLS_ISO8859_15=m
-CONFIG_NLS_KOI8_R=m
-CONFIG_NLS_KOI8_U=m
-CONFIG_NLS_UTF8=m
+# CONFIG_DVB is not set
#
# Graphics support
# Advanced Linux Sound Architecture
#
CONFIG_SND=m
+CONFIG_SND_TIMER=m
+CONFIG_SND_PCM=m
+CONFIG_SND_HWDEP=m
+CONFIG_SND_RAWMIDI=m
CONFIG_SND_SEQUENCER=m
CONFIG_SND_SEQ_DUMMY=m
CONFIG_SND_OSSEMUL=y
#
# Generic devices
#
+CONFIG_SND_MPU401_UART=m
+CONFIG_SND_OPL3_LIB=m
CONFIG_SND_DUMMY=m
CONFIG_SND_VIRMIDI=m
CONFIG_SND_MTPAV=m
#
# PCI devices
#
+CONFIG_SND_AC97_CODEC=m
# CONFIG_SND_ALI5451 is not set
+# CONFIG_SND_ATIIXP is not set
+# CONFIG_SND_AU8810 is not set
+# CONFIG_SND_AU8820 is not set
+# CONFIG_SND_AU8830 is not set
# CONFIG_SND_AZT3328 is not set
+# CONFIG_SND_BT87X is not set
CONFIG_SND_CS46XX=m
CONFIG_SND_CS46XX_NEW_DSP=y
CONFIG_SND_CS4281=m
CONFIG_SND_EMU10K1=m
# CONFIG_SND_KORG1212 is not set
+# CONFIG_SND_MIXART is not set
# CONFIG_SND_NM256 is not set
# CONFIG_SND_RME32 is not set
# CONFIG_SND_RME96 is not set
# CONFIG_SND_ES1968 is not set
# CONFIG_SND_MAESTRO3 is not set
CONFIG_SND_FM801=m
+# CONFIG_SND_FM801_TEA575X is not set
# CONFIG_SND_ICE1712 is not set
# CONFIG_SND_ICE1724 is not set
# CONFIG_SND_INTEL8X0 is not set
+# CONFIG_SND_INTEL8X0M is not set
# CONFIG_SND_SONICVIBES is not set
# CONFIG_SND_VIA82XX is not set
# CONFIG_SND_VX222 is not set
# USB Host Controller Drivers
#
CONFIG_USB_EHCI_HCD=m
+# CONFIG_USB_EHCI_SPLIT_ISO is not set
+# CONFIG_USB_EHCI_ROOT_HUB_TT is not set
CONFIG_USB_OHCI_HCD=m
CONFIG_USB_UHCI_HCD=m
# CONFIG_USB_PRINTER is not set
CONFIG_USB_STORAGE=m
# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_RW_DETECT is not set
# CONFIG_USB_STORAGE_DATAFAB is not set
# CONFIG_USB_STORAGE_FREECOM is not set
# CONFIG_USB_STORAGE_ISD200 is not set
# CONFIG_USB_WACOM is not set
# CONFIG_USB_KBTAB is not set
# CONFIG_USB_POWERMATE is not set
+# CONFIG_USB_MTOUCH is not set
+# CONFIG_USB_EGALAX is not set
# CONFIG_USB_XPAD is not set
+# CONFIG_USB_ATI_REMOTE is not set
#
# USB Imaging devices
#
# CONFIG_USB_MDC800 is not set
-# CONFIG_USB_SCANNER is not set
# CONFIG_USB_MICROTEK is not set
# CONFIG_USB_HPUSBSCSI is not set
#
# USB Miscellaneous drivers
#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
# CONFIG_USB_TIGL is not set
# CONFIG_USB_AUERSWALD is not set
# CONFIG_USB_RIO500 is not set
# CONFIG_USB_LEGOTOWER is not set
# CONFIG_USB_LCD is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_PHIDGETSERVO is not set
# CONFIG_USB_TEST is not set
+
+#
+# USB Gadget Support
+#
# CONFIG_USB_GADGET is not set
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_XATTR=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
+CONFIG_FS_MBCACHE=y
+CONFIG_REISERFS_FS=m
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_REISERFS_FS_XATTR is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_XFS_FS=y
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_SECURITY is not set
+# CONFIG_XFS_POSIX_ACL is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_QUOTA is not set
+CONFIG_AUTOFS_FS=y
+CONFIG_AUTOFS4_FS=y
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+# CONFIG_ZISOFS is not set
+CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+# CONFIG_MSDOS_FS is not set
+CONFIG_VFAT_FS=y
+CONFIG_NTFS_FS=m
+# CONFIG_NTFS_DEBUG is not set
+# CONFIG_NTFS_RW is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_SYSFS=y
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVPTS_FS_XATTR is not set
+CONFIG_TMPFS=y
+CONFIG_HUGETLBFS=y
+CONFIG_HUGETLB_PAGE=y
+CONFIG_RAMFS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=m
+CONFIG_NFS_V3=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_DIRECTIO=y
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_TCP=y
+CONFIG_LOCKD=m
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_SUNRPC=m
+CONFIG_SUNRPC_GSS=m
+CONFIG_RPCSEC_GSS_KRB5=m
+CONFIG_SMB_FS=m
+CONFIG_SMB_NLS_DEFAULT=y
+CONFIG_SMB_NLS_REMOTE="cp437"
+CONFIG_CIFS=m
+# CONFIG_CIFS_STATS is not set
+# CONFIG_CIFS_XATTR is not set
+# CONFIG_CIFS_POSIX is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+CONFIG_SGI_PARTITION=y
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+CONFIG_EFI_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=m
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+# CONFIG_NLS_ASCII is not set
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=m
+
#
# Library routines
#
+# CONFIG_CRC_CCITT is not set
CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
#
# HP Simulator drivers
# CONFIG_IA64_DEBUG_CMPXCHG is not set
# CONFIG_IA64_DEBUG_IRQ is not set
# CONFIG_DEBUG_INFO is not set
+CONFIG_SYSVIPC_COMPAT=y
#
# Security options
# CONFIG_CRYPTO_HMAC is not set
# CONFIG_CRYPTO_NULL is not set
# CONFIG_CRYPTO_MD4 is not set
-# CONFIG_CRYPTO_MD5 is not set
+CONFIG_CRYPTO_MD5=m
# CONFIG_CRYPTO_SHA1 is not set
# CONFIG_CRYPTO_SHA256 is not set
# CONFIG_CRYPTO_SHA512 is not set
-# CONFIG_CRYPTO_DES is not set
+CONFIG_CRYPTO_DES=m
# CONFIG_CRYPTO_BLOWFISH is not set
# CONFIG_CRYPTO_TWOFISH is not set
# CONFIG_CRYPTO_SERPENT is not set
-# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_AES_GENERIC is not set
# CONFIG_CRYPTO_CAST5 is not set
# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_ARC4 is not set
# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_CRC32C is not set
# CONFIG_CRYPTO_TEST is not set
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
ASSERT(((unsigned long) ioc->res_hint & (sizeof(unsigned long) - 1UL)) == 0);
ASSERT(res_ptr < res_end);
+ /*
+ * N.B. REO/Grande defect AR2305 can cause TLB fetch timeouts
+ * if a TLB entry is purged while in use. sba_mark_invalid()
+ * purges IOTLB entries in power-of-two sizes, so we also
+ * allocate IOVA space in power-of-two sizes.
+ */
+ bits_wanted = 1UL << get_iovp_order(bits_wanted << PAGE_SHIFT);
+
if (likely(bits_wanted == 1)) {
unsigned int bitshiftcnt;
for(; res_ptr < res_end ; res_ptr++) {
int bits_not_wanted = size >> iovp_shift;
unsigned long m;
+ /* Round up to power-of-two size: see AR2305 note above */
+ bits_not_wanted = 1UL << get_iovp_order(bits_not_wanted << PAGE_SHIFT);
for (; bits_not_wanted > 0 ; res_ptr++) {
if (unlikely(bits_not_wanted > BITS_PER_LONG)) {
{
}
+static void
+hpsim_set_affinity_noop (unsigned int a, cpumask_t b)
+{
+}
+
static struct hw_interrupt_type irq_type_hp_sim = {
.typename = "hpsim",
.startup = hpsim_irq_startup,
.disable = hpsim_irq_noop,
.ack = hpsim_irq_noop,
.end = hpsim_irq_noop,
- .set_affinity = (void (*)(unsigned int, unsigned long)) hpsim_irq_noop,
+ .set_affinity = hpsim_set_affinity_noop,
};
void __init
printk(KERN_WARNING "%s: set_multicast_list called\n", dev->name);
}
-#ifdef CONFIG_NET_FASTROUTE
-static int
-simeth_accept_fastpath(struct net_device *dev, struct dst_entry *dst)
-{
- printk(KERN_WARNING "%s: simeth_accept_fastpath called\n", dev->name);
- return -1;
-}
-#endif
-
__initcall(simeth_probe);
#define CYCLONE_TIMER_FREQ 100000000
int use_cyclone;
-int __init cyclone_setup(char *str)
+void __init cyclone_setup(void)
{
use_cyclone = 1;
- return 1;
}
static u32* volatile cyclone_timer; /* Cyclone MPMC0 register */
struct inode *inode = file->f_dentry->d_inode;
struct proc_dir_entry *entry = PDE(inode);
struct salinfo_data *data = entry->data;
- void *saldata;
- size_t size;
u8 *buf;
u64 bufsize;
buf = NULL;
bufsize = 0;
}
- if (*ppos >= bufsize)
- return 0;
-
- saldata = buf + file->f_pos;
- size = bufsize - file->f_pos;
- if (size > count)
- size = count;
- if (copy_to_user(buffer, saldata, size))
- return -EFAULT;
-
- *ppos += size;
- return size;
+ return simple_read_from_buffer(buffer, count, ppos, buf, bufsize);
}
static void
*oemdata_size = 0;
vfree(*oemdata);
*oemdata = NULL;
- if (efi_guidcmp(guid, SAL_PLAT_SPECIFIC_ERR_SECT_GUID) == 0)
+ if (efi_guidcmp(guid, SAL_PLAT_SPECIFIC_ERR_SECT_GUID) == 0 ||
+ efi_guidcmp(guid, SAL_PLAT_MEM_DEV_ERR_SECT_GUID) == 0)
return sn_platform_plat_specific_err_print(sect_header, oemdata, oemdata_size);
return 0;
}
return 1;
}
-void bvme6000_reset()
+void bvme6000_reset(void)
{
volatile PitRegsPtr pit = (PitRegsPtr)BVME_PIT_BASE;
* csum_partial_copy_from_user.
*/
+#include <linux/module.h>
#include <net/checksum.h>
/*
static inline void free_io_area(void *addr)
{
- return vfree((void *)(PAGE_MASK & (unsigned long)addr));
+ vfree((void *)(PAGE_MASK & (unsigned long)addr));
}
#else
return 1;
}
-void mvme16x_reset()
+void mvme16x_reset(void)
{
printk ("\r\n\nCalled mvme16x_reset\r\n"
"\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r");
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
--- /dev/null
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+static LIST_HEAD(dbe_list);
+static spinlock_t dbe_lock = SPIN_LOCK_UNLOCKED;
+
+/* Given an address, look for it in the module exception tables. */
+const struct exception_table_entry *search_module_dbetables(unsigned long addr)
+{
+ unsigned long flags;
+ const struct exception_table_entry *e = NULL;
+ struct mod_arch_specific *dbe;
+
+ spin_lock_irqsave(&dbe_lock, flags);
+ list_for_each_entry(dbe, &dbe_list, dbe_list) {
+ e = search_extable(dbe->dbe_start, dbe->dbe_end - 1, addr);
+ if (e)
+ break;
+ }
+ spin_unlock_irqrestore(&dbe_lock, flags);
+
+ /* Now, if we found one, we are running inside it now, hence
+ we cannot unload the module, hence no refcnt needed. */
+ return e;
+}
+
+/* Put in dbe list if neccessary. */
+int module_finalize(const Elf_Ehdr *hdr,
+ const Elf_Shdr *sechdrs,
+ struct module *me)
+{
+ const Elf_Shdr *s;
+ char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
+
+ INIT_LIST_HEAD(&me->arch.dbe_list);
+ for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) {
+ if (strcmp("__dbe_table", secstrings + s->sh_name) != 0)
+ continue;
+ me->arch.dbe_start = (void *)s->sh_addr;
+ me->arch.dbe_end = (void *)s->sh_addr + s->sh_size;
+ spin_lock_irq(&dbe_lock);
+ list_add(&me->arch.dbe_list, &dbe_list);
+ spin_unlock_irq(&dbe_lock);
+ }
+ return 0;
+}
+
+void module_arch_cleanup(struct module *mod)
+{
+ spin_lock_irq(&dbe_lock);
+ list_del(&mod->arch.dbe_list);
+ spin_unlock_irq(&dbe_lock);
+}
/* And the same for proc */
int proc_dolasatstring(ctl_table *table, int write, struct file *filp,
- void *buffer, size_t *lenp)
+ void *buffer, size_t *lenp, loff_t *ppos)
{
int r;
down(&lasat_info_sem);
- r = proc_dostring(table, write, filp, buffer, lenp);
+ r = proc_dostring(table, write, filp, buffer, lenp, ppos);
if ( (!write) || r) {
up(&lasat_info_sem);
return r;
/* proc function to write EEPROM after changing int entry */
int proc_dolasatint(ctl_table *table, int write, struct file *filp,
- void *buffer, size_t *lenp)
+ void *buffer, size_t *lenp, loff_t *ppos)
{
int r;
down(&lasat_info_sem);
- r = proc_dointvec(table, write, filp, buffer, lenp);
+ r = proc_dointvec(table, write, filp, buffer, lenp, ppos);
if ( (!write) || r) {
up(&lasat_info_sem);
return r;
#ifdef CONFIG_DS1603
/* proc function to read/write RealTime Clock */
int proc_dolasatrtc(ctl_table *table, int write, struct file *filp,
- void *buffer, size_t *lenp)
+ void *buffer, size_t *lenp, loff_t *ppos)
{
int r;
down(&lasat_info_sem);
if (rtctmp < 0)
rtctmp = 0;
}
- r = proc_dointvec(table, write, filp, buffer, lenp);
+ r = proc_dointvec(table, write, filp, buffer, lenp, ppos);
if ( (!write) || r) {
up(&lasat_info_sem);
return r;
static char proc_lasat_ipbuf[32];
/* Parsing of IP address */
int proc_lasat_ip(ctl_table *table, int write, struct file *filp,
- void *buffer, size_t *lenp)
+ void *buffer, size_t *lenp, loff_t *ppos)
{
int len;
unsigned int ip;
char *p, c;
if (!table->data || !table->maxlen || !*lenp ||
- (filp->f_pos && !write)) {
+ (*ppos && !write)) {
*lenp = 0;
return 0;
}
return -EFAULT;
}
proc_lasat_ipbuf[len] = 0;
- filp->f_pos += *lenp;
+ *ppos += *lenp;
/* Now see if we can convert it to a valid IP */
ip = in_aton(proc_lasat_ipbuf);
*(unsigned int *)(table->data) = ip;
len++;
}
*lenp = len;
- filp->f_pos += len;
+ *ppos += len;
}
update_bcastaddr();
up(&lasat_info_sem);
}
int proc_lasat_eeprom_value(ctl_table *table, int write, struct file *filp,
- void *buffer, size_t *lenp)
+ void *buffer, size_t *lenp, loff_t *ppos)
{
int r;
down(&lasat_info_sem);
- r = proc_dointvec(table, write, filp, buffer, lenp);
+ r = proc_dointvec(table, write, filp, buffer, lenp, ppos);
if ( (!write) || r) {
up(&lasat_info_sem);
return r;
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ * Copyright (C) 1997, 1998, 1999, 2000 Ralf Baechle ralf@gnu.org
+ * Carsten Langgaard, carstenl@mips.com
+ * Copyright (C) 2002 MIPS Technologies, Inc. All rights reserved.
+ */
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+
+#include <asm/cpu.h>
+#include <asm/bootinfo.h>
+#include <asm/mmu_context.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+
+extern void except_vec0_generic(void);
+extern void except_vec1_r8k(void);
+
+#define TFP_TLB_SIZE 384
+#define TFP_TLB_SET_SHIFT 7
+
+/* CP0 hazard avoidance. */
+#define BARRIER __asm__ __volatile__(".set noreorder\n\t" \
+ "nop; nop; nop; nop; nop; nop;\n\t" \
+ ".set reorder\n\t")
+
+void local_flush_tlb_all(void)
+{
+ unsigned long flags;
+ unsigned long old_ctx;
+ int entry;
+
+ local_irq_save(flags);
+ /* Save old context and create impossible VPN2 value */
+ old_ctx = read_c0_entryhi();
+ write_c0_entrylo(0);
+
+ for (entry = 0; entry < TFP_TLB_SIZE; entry++) {
+ write_c0_tlbset(entry >> TFP_TLB_SET_SHIFT);
+ write_c0_vaddr(entry << PAGE_SHIFT);
+ write_c0_entryhi(CKSEG0 + (entry << (PAGE_SHIFT + 1)));
+ mtc0_tlbw_hazard();
+ tlb_write();
+ }
+ tlbw_use_hazard();
+ write_c0_entryhi(old_ctx);
+ local_irq_restore(flags);
+}
+
+void local_flush_tlb_mm(struct mm_struct *mm)
+{
+ int cpu = smp_processor_id();
+
+ if (cpu_context(cpu, mm) != 0)
+ drop_mmu_context(mm,cpu);
+}
+
+void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end)
+{
+ struct mm_struct *mm = vma->vm_mm;
+ int cpu = smp_processor_id();
+ unsigned long flags;
+ int oldpid, newpid, size;
+
+ if (!cpu_context(cpu, mm))
+ return;
+
+ size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
+ size = (size + 1) >> 1;
+
+ local_irq_save(flags);
+
+ if (size > TFP_TLB_SIZE / 2) {
+ drop_mmu_context(mm, cpu);
+ goto out_restore;
+ }
+
+ oldpid = read_c0_entryhi();
+ newpid = cpu_asid(cpu, mm);
+
+ write_c0_entrylo(0);
+
+ start &= PAGE_MASK;
+ end += (PAGE_SIZE - 1);
+ end &= PAGE_MASK;
+ while (start < end) {
+ signed long idx;
+
+ write_c0_vaddr(start);
+ write_c0_entryhi(start);
+ start += PAGE_SIZE;
+ tlb_probe();
+ idx = read_c0_tlbset();
+ if (idx < 0)
+ continue;
+
+ write_c0_entryhi(CKSEG0 + (idx << (PAGE_SHIFT + 1)));
+ tlb_write();
+ }
+ write_c0_entryhi(oldpid);
+
+out_restore:
+ local_irq_restore(flags);
+}
+
+/* Usable for KV1 addresses only! */
+void local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
+{
+ unsigned long flags;
+ int size;
+
+ size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
+ size = (size + 1) >> 1;
+
+ if (size > TFP_TLB_SIZE / 2) {
+ local_flush_tlb_all();
+ return;
+ }
+
+ local_irq_save(flags);
+
+ write_c0_entrylo(0);
+
+ start &= PAGE_MASK;
+ end += (PAGE_SIZE - 1);
+ end &= PAGE_MASK;
+ while (start < end) {
+ signed long idx;
+
+ write_c0_vaddr(start);
+ write_c0_entryhi(start);
+ start += PAGE_SIZE;
+ tlb_probe();
+ idx = read_c0_tlbset();
+ if (idx < 0)
+ continue;
+
+ write_c0_entryhi(CKSEG0 + (idx << (PAGE_SHIFT + 1)));
+ tlb_write();
+ }
+
+ local_irq_restore(flags);
+}
+
+void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+ int cpu = smp_processor_id();
+ unsigned long flags;
+ int oldpid, newpid;
+ signed long idx;
+
+ if (!cpu_context(cpu, vma->vm_mm))
+ return;
+
+ newpid = cpu_asid(cpu, vma->vm_mm);
+ page &= PAGE_MASK;
+ local_irq_save(flags);
+ oldpid = read_c0_entryhi();
+ write_c0_vaddr(page);
+ write_c0_entryhi(newpid);
+ tlb_probe();
+ idx = read_c0_tlbset();
+ if (idx < 0)
+ goto finish;
+
+ write_c0_entrylo(0);
+ write_c0_entryhi(CKSEG0 + (idx << (PAGE_SHIFT + 1)));
+ tlb_write();
+
+finish:
+ write_c0_entryhi(oldpid);
+ local_irq_restore(flags);
+}
+
+/*
+ * We will need multiple versions of update_mmu_cache(), one that just
+ * updates the TLB with the new pte(s), and another which also checks
+ * for the R4k "end of page" hardware bug and does the needy.
+ */
+void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte)
+{
+ unsigned long flags;
+ pgd_t *pgdp;
+ pmd_t *pmdp;
+ pte_t *ptep;
+ int pid;
+
+ /*
+ * Handle debugger faulting in for debugee.
+ */
+ if (current->active_mm != vma->vm_mm)
+ return;
+
+ pid = read_c0_entryhi() & ASID_MASK;
+
+ local_irq_save(flags);
+ address &= PAGE_MASK;
+ write_c0_vaddr(address);
+ write_c0_entryhi(pid);
+ pgdp = pgd_offset(vma->vm_mm, address);
+ pmdp = pmd_offset(pgdp, address);
+ ptep = pte_offset_map(pmdp, address);
+ tlb_probe();
+
+ write_c0_entrylo(pte_val(*ptep++) >> 6);
+ tlb_write();
+
+ write_c0_entryhi(pid);
+ local_irq_restore(flags);
+}
+
+static void __init probe_tlb(unsigned long config)
+{
+ struct cpuinfo_mips *c = ¤t_cpu_data;
+
+ c->tlbsize = 3 * 128; /* 3 sets each 128 entries */
+}
+
+void __init tlb_init(void)
+{
+ unsigned int config = read_c0_config();
+ unsigned long status;
+
+ probe_tlb(config);
+
+ status = read_c0_status();
+ status &= ~(ST0_UPS | ST0_KPS);
+#ifdef CONFIG_PAGE_SIZE_4KB
+ status |= (TFP_PAGESIZE_4K << 32) | (TFP_PAGESIZE_4K << 36);
+#elif defined(CONFIG_PAGE_SIZE_8KB)
+ status |= (TFP_PAGESIZE_8K << 32) | (TFP_PAGESIZE_8K << 36);
+#elif defined(CONFIG_PAGE_SIZE_16KB)
+ status |= (TFP_PAGESIZE_16K << 32) | (TFP_PAGESIZE_16K << 36);
+#elif defined(CONFIG_PAGE_SIZE_64KB)
+ status |= (TFP_PAGESIZE_64K << 32) | (TFP_PAGESIZE_64K << 36);
+#endif
+ write_c0_status(status);
+
+ write_c0_wired(0);
+
+ local_flush_tlb_all();
+
+ memcpy((void *)(CKSEG0 + 0x00), &except_vec0_generic, 0x80);
+ memcpy((void *)(CKSEG0 + 0x80), except_vec1_r8k, 0x80);
+ flush_icache_range(CKSEG0 + 0x80, CKSEG0 + 0x100);
+}
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1999 Ralf Baechle
+ * Copyright (C) 1999 Silicon Graphics, Inc.
+ */
+#include <linux/init.h>
+#include <asm/mipsregs.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+
+ .macro __BUILD_cli
+ CLI
+ .endm
+
+ .macro __BUILD_sti
+ STI
+ .endm
+
+ .macro __BUILD_kmode
+ KMODE
+ .endm
+
+ .macro tlb_handler name interruptible writebit
+ NESTED(__\name, PT_SIZE, sp)
+ SAVE_ALL
+ dmfc0 a2, CP0_BADVADDR
+ __BUILD_\interruptible
+ li a1, \writebit
+ sd a2, PT_BVADDR(sp)
+ move a0, sp
+ jal do_page_fault
+ j ret_from_exception
+ END(__\name)
+ .endm
+
+ tlb_handler xtlb_mod kmode 1
+ tlb_handler xtlb_tlbl kmode 0
+ tlb_handler xtlb_tlbs kmode 1
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1999 Ralf Baechle
+ * Copyright (C) 1999 Silicon Graphics, Inc.
+ */
+#include <linux/init.h>
+#include <asm/mipsregs.h>
+#include <asm/page.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+#include <asm/war.h>
+
+ .macro __BUILD_cli
+ CLI
+ .endm
+
+ .macro __BUILD_sti
+ STI
+ .endm
+
+ .macro __BUILD_kmode
+ KMODE
+ .endm
+
+ .macro tlb_handler name interruptible writebit
+ NESTED(__\name, PT_SIZE, sp)
+ SAVE_ALL
+ dmfc0 a2, CP0_BADVADDR
+ __BUILD_\interruptible
+ li a1, \writebit
+ sd a2, PT_BVADDR(sp)
+ move a0, sp
+ jal do_page_fault
+ j ret_from_exception
+ END(__\name)
+ .endm
+
+ .macro tlb_handler_m3 name interruptible writebit
+ NESTED(__\name, PT_SIZE, sp)
+ dmfc0 k0, CP0_BADVADDR
+ dmfc0 k1, CP0_ENTRYHI
+ xor k0, k1
+ dsrl k0, k0, PAGE_SHIFT + 1
+ bnez k0, 1f
+ SAVE_ALL
+ dmfc0 a2, CP0_BADVADDR
+ __BUILD_\interruptible
+ li a1, \writebit
+ sd a2, PT_BVADDR(sp)
+ move a0, sp
+ jal do_page_fault
+1:
+ j ret_from_exception
+ END(__\name)
+ .endm
+
+ tlb_handler xtlb_mod kmode 1
+#if BCM1250_M3_WAR
+ tlb_handler_m3 xtlb_tlbl kmode 0
+#else
+ tlb_handler xtlb_tlbl kmode 0
+#endif
+ tlb_handler xtlb_tlbs kmode 1
--- /dev/null
+/*
+ * TLB exception handling code for R2000/R3000.
+ *
+ * Copyright (C) 1994, 1995, 1996 by Ralf Baechle and Andreas Busse
+ *
+ * Multi-CPU abstraction reworking:
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ *
+ * Further modifications to make this work:
+ * Copyright (c) 1998 Harald Koerfgen
+ * Copyright (c) 1998, 1999 Gleb Raiko & Vladimir Roganov
+ * Copyright (c) 2001 Ralf Baechle
+ * Copyright (c) 2001 MIPS Technologies, Inc.
+ */
+#include <linux/init.h>
+#include <asm/asm.h>
+#include <asm/cachectl.h>
+#include <asm/fpregdef.h>
+#include <asm/mipsregs.h>
+#include <asm/page.h>
+#include <asm/pgtable-bits.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+
+#define TLB_OPTIMIZE /* If you are paranoid, disable this. */
+
+ .text
+ .set mips1
+ .set noreorder
+
+ __INIT
+
+ /* TLB refill, R[23]00 version */
+ LEAF(except_vec0_r2300)
+ .set noat
+ .set mips1
+ mfc0 k0, CP0_BADVADDR
+ lw k1, pgd_current # get pgd pointer
+ srl k0, k0, 22
+ sll k0, k0, 2
+ addu k1, k1, k0
+ mfc0 k0, CP0_CONTEXT
+ lw k1, (k1)
+ and k0, k0, 0xffc
+ addu k1, k1, k0
+ lw k0, (k1)
+ nop
+ mtc0 k0, CP0_ENTRYLO0
+ mfc0 k1, CP0_EPC
+ tlbwr
+ jr k1
+ rfe
+ END(except_vec0_r2300)
+
+ __FINIT
+
+ /* ABUSE of CPP macros 101. */
+
+ /* After this macro runs, the pte faulted on is
+ * in register PTE, a ptr into the table in which
+ * the pte belongs is in PTR.
+ */
+#define LOAD_PTE(pte, ptr) \
+ mfc0 pte, CP0_BADVADDR; \
+ lw ptr, pgd_current; \
+ srl pte, pte, 22; \
+ sll pte, pte, 2; \
+ addu ptr, ptr, pte; \
+ mfc0 pte, CP0_CONTEXT; \
+ lw ptr, (ptr); \
+ andi pte, pte, 0xffc; \
+ addu ptr, ptr, pte; \
+ lw pte, (ptr); \
+ nop;
+
+ /* This places the even/odd pte pair in the page
+ * table at PTR into ENTRYLO0 and ENTRYLO1 using
+ * TMP as a scratch register.
+ */
+#define PTE_RELOAD(ptr) \
+ lw ptr, (ptr) ; \
+ nop ; \
+ mtc0 ptr, CP0_ENTRYLO0; \
+ nop;
+
+#define DO_FAULT(write) \
+ .set noat; \
+ .set macro; \
+ SAVE_ALL; \
+ mfc0 a2, CP0_BADVADDR; \
+ KMODE; \
+ .set at; \
+ move a0, sp; \
+ jal do_page_fault; \
+ li a1, write; \
+ j ret_from_exception; \
+ nop; \
+ .set noat; \
+ .set nomacro;
+
+ /* Check is PTE is present, if not then jump to LABEL.
+ * PTR points to the page table where this PTE is located,
+ * when the macro is done executing PTE will be restored
+ * with it's original value.
+ */
+#define PTE_PRESENT(pte, ptr, label) \
+ andi pte, pte, (_PAGE_PRESENT | _PAGE_READ); \
+ xori pte, pte, (_PAGE_PRESENT | _PAGE_READ); \
+ bnez pte, label; \
+ .set push; \
+ .set reorder; \
+ lw pte, (ptr); \
+ .set pop;
+
+ /* Make PTE valid, store result in PTR. */
+#define PTE_MAKEVALID(pte, ptr) \
+ ori pte, pte, (_PAGE_VALID | _PAGE_ACCESSED); \
+ sw pte, (ptr);
+
+ /* Check if PTE can be written to, if not branch to LABEL.
+ * Regardless restore PTE with value from PTR when done.
+ */
+#define PTE_WRITABLE(pte, ptr, label) \
+ andi pte, pte, (_PAGE_PRESENT | _PAGE_WRITE); \
+ xori pte, pte, (_PAGE_PRESENT | _PAGE_WRITE); \
+ bnez pte, label; \
+ .set push; \
+ .set reorder; \
+ lw pte, (ptr); \
+ .set pop;
+
+
+ /* Make PTE writable, update software status bits as well,
+ * then store at PTR.
+ */
+#define PTE_MAKEWRITE(pte, ptr) \
+ ori pte, pte, (_PAGE_ACCESSED | _PAGE_MODIFIED | \
+ _PAGE_VALID | _PAGE_DIRTY); \
+ sw pte, (ptr);
+
+/*
+ * The index register may have the probe fail bit set,
+ * because we would trap on access kseg2, i.e. without refill.
+ */
+#define TLB_WRITE(reg) \
+ mfc0 reg, CP0_INDEX; \
+ nop; \
+ bltz reg, 1f; \
+ nop; \
+ tlbwi; \
+ j 2f; \
+ nop; \
+1: tlbwr; \
+2:
+
+#define RET(reg) \
+ mfc0 reg, CP0_EPC; \
+ nop; \
+ jr reg; \
+ rfe
+
+ .set noreorder
+
+ .align 5
+NESTED(handle_tlbl, PT_SIZE, sp)
+ .set noat
+
+#ifdef TLB_OPTIMIZE
+ /* Test present bit in entry. */
+ LOAD_PTE(k0, k1)
+ tlbp
+ PTE_PRESENT(k0, k1, nopage_tlbl)
+ PTE_MAKEVALID(k0, k1)
+ PTE_RELOAD(k1)
+ TLB_WRITE(k0)
+ RET(k0)
+nopage_tlbl:
+#endif
+
+ DO_FAULT(0)
+END(handle_tlbl)
+
+NESTED(handle_tlbs, PT_SIZE, sp)
+ .set noat
+
+#ifdef TLB_OPTIMIZE
+ LOAD_PTE(k0, k1)
+ tlbp # find faulting entry
+ PTE_WRITABLE(k0, k1, nopage_tlbs)
+ PTE_MAKEWRITE(k0, k1)
+ PTE_RELOAD(k1)
+ TLB_WRITE(k0)
+ RET(k0)
+nopage_tlbs:
+#endif
+
+ DO_FAULT(1)
+END(handle_tlbs)
+
+ .align 5
+NESTED(handle_mod, PT_SIZE, sp)
+ .set noat
+#ifdef TLB_OPTIMIZE
+ LOAD_PTE(k0, k1)
+ tlbp # find faulting entry
+ andi k0, k0, _PAGE_WRITE
+ beqz k0, nowrite_mod
+ .set push
+ .set reorder
+ lw k0, (k1)
+ .set pop
+
+ /* Present and writable bits set, set accessed and dirty bits. */
+ PTE_MAKEWRITE(k0, k1)
+
+ /* Now reload the entry into the tlb. */
+ PTE_RELOAD(k1)
+ tlbwi
+ RET(k0)
+#endif
+
+nowrite_mod:
+ DO_FAULT(1)
+END(handle_mod)
--- /dev/null
+/*
+ * TLB exception handling code for r4k.
+ *
+ * Copyright (C) 1994, 1995, 1996 by Ralf Baechle and Andreas Busse
+ *
+ * Multi-cpu abstraction and reworking:
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ *
+ * Carsten Langgaard, carstenl@mips.com
+ * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved.
+ */
+#include <linux/init.h>
+#include <linux/config.h>
+
+#include <asm/asm.h>
+#include <asm/offset.h>
+#include <asm/cachectl.h>
+#include <asm/fpregdef.h>
+#include <asm/mipsregs.h>
+#include <asm/page.h>
+#include <asm/pgtable-bits.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+#include <asm/war.h>
+
+#define TLB_OPTIMIZE /* If you are paranoid, disable this. */
+
+#ifdef CONFIG_64BIT_PHYS_ADDR
+#define PTE_L ld
+#define PTE_S sd
+#define PTE_SRL dsrl
+#define P_MTC0 dmtc0
+#define PTE_SIZE 8
+#define PTEP_INDX_MSK 0xff0
+#define PTE_INDX_MSK 0xff8
+#define PTE_INDX_SHIFT 9
+#else
+#define PTE_L lw
+#define PTE_S sw
+#define PTE_SRL srl
+#define P_MTC0 mtc0
+#define PTE_SIZE 4
+#define PTEP_INDX_MSK 0xff8
+#define PTE_INDX_MSK 0xffc
+#define PTE_INDX_SHIFT 10
+#endif
+
+/*
+ * ABUSE of CPP macros 101.
+ *
+ * After this macro runs, the pte faulted on is
+ * in register PTE, a ptr into the table in which
+ * the pte belongs is in PTR.
+ */
+
+#ifdef CONFIG_SMP
+#define GET_PGD(scratch, ptr) \
+ mfc0 ptr, CP0_CONTEXT; \
+ la scratch, pgd_current;\
+ srl ptr, 23; \
+ sll ptr, 2; \
+ addu ptr, scratch, ptr; \
+ lw ptr, (ptr);
+#else
+#define GET_PGD(scratch, ptr) \
+ lw ptr, pgd_current;
+#endif
+
+#define LOAD_PTE(pte, ptr) \
+ GET_PGD(pte, ptr) \
+ mfc0 pte, CP0_BADVADDR; \
+ srl pte, pte, _PGDIR_SHIFT; \
+ sll pte, pte, 2; \
+ addu ptr, ptr, pte; \
+ mfc0 pte, CP0_BADVADDR; \
+ lw ptr, (ptr); \
+ srl pte, pte, PTE_INDX_SHIFT; \
+ and pte, pte, PTE_INDX_MSK; \
+ addu ptr, ptr, pte; \
+ PTE_L pte, (ptr);
+
+ /* This places the even/odd pte pair in the page
+ * table at PTR into ENTRYLO0 and ENTRYLO1 using
+ * TMP as a scratch register.
+ */
+#define PTE_RELOAD(ptr, tmp) \
+ ori ptr, ptr, PTE_SIZE; \
+ xori ptr, ptr, PTE_SIZE; \
+ PTE_L tmp, PTE_SIZE(ptr); \
+ PTE_L ptr, 0(ptr); \
+ PTE_SRL tmp, tmp, 6; \
+ P_MTC0 tmp, CP0_ENTRYLO1; \
+ PTE_SRL ptr, ptr, 6; \
+ P_MTC0 ptr, CP0_ENTRYLO0;
+
+#define DO_FAULT(write) \
+ .set noat; \
+ SAVE_ALL; \
+ mfc0 a2, CP0_BADVADDR; \
+ KMODE; \
+ .set at; \
+ move a0, sp; \
+ jal do_page_fault; \
+ li a1, write; \
+ j ret_from_exception; \
+ nop; \
+ .set noat;
+
+ /* Check is PTE is present, if not then jump to LABEL.
+ * PTR points to the page table where this PTE is located,
+ * when the macro is done executing PTE will be restored
+ * with it's original value.
+ */
+#define PTE_PRESENT(pte, ptr, label) \
+ andi pte, pte, (_PAGE_PRESENT | _PAGE_READ); \
+ xori pte, pte, (_PAGE_PRESENT | _PAGE_READ); \
+ bnez pte, label; \
+ PTE_L pte, (ptr);
+
+ /* Make PTE valid, store result in PTR. */
+#define PTE_MAKEVALID(pte, ptr) \
+ ori pte, pte, (_PAGE_VALID | _PAGE_ACCESSED); \
+ PTE_S pte, (ptr);
+
+ /* Check if PTE can be written to, if not branch to LABEL.
+ * Regardless restore PTE with value from PTR when done.
+ */
+#define PTE_WRITABLE(pte, ptr, label) \
+ andi pte, pte, (_PAGE_PRESENT | _PAGE_WRITE); \
+ xori pte, pte, (_PAGE_PRESENT | _PAGE_WRITE); \
+ bnez pte, label; \
+ PTE_L pte, (ptr);
+
+ /* Make PTE writable, update software status bits as well,
+ * then store at PTR.
+ */
+#define PTE_MAKEWRITE(pte, ptr) \
+ ori pte, pte, (_PAGE_ACCESSED | _PAGE_MODIFIED | \
+ _PAGE_VALID | _PAGE_DIRTY); \
+ PTE_S pte, (ptr);
+
+ __INIT
+
+#ifdef CONFIG_64BIT_PHYS_ADDR
+#define GET_PTE_OFF(reg)
+#elif CONFIG_CPU_VR41XX
+#define GET_PTE_OFF(reg) srl reg, reg, 3
+#else
+#define GET_PTE_OFF(reg) srl reg, reg, 1
+#endif
+
+/*
+ * These handlers much be written in a relocatable manner
+ * because based upon the cpu type an arbitrary one of the
+ * following pieces of code will be copied to the KSEG0
+ * vector location.
+ */
+ /* TLB refill, EXL == 0, R4xx0, non-R4600 version */
+ .set noreorder
+ .set noat
+ LEAF(except_vec0_r4000)
+ .set mips3
+ GET_PGD(k0, k1) # get pgd pointer
+ mfc0 k0, CP0_BADVADDR # Get faulting address
+ srl k0, k0, _PGDIR_SHIFT # get pgd only bits
+
+ sll k0, k0, 2
+ addu k1, k1, k0 # add in pgd offset
+ mfc0 k0, CP0_CONTEXT # get context reg
+ lw k1, (k1)
+ GET_PTE_OFF(k0) # get pte offset
+ and k0, k0, PTEP_INDX_MSK
+ addu k1, k1, k0 # add in offset
+ PTE_L k0, 0(k1) # get even pte
+ PTE_L k1, PTE_SIZE(k1) # get odd pte
+ PTE_SRL k0, k0, 6 # convert to entrylo0
+ P_MTC0 k0, CP0_ENTRYLO0 # load it
+ PTE_SRL k1, k1, 6 # convert to entrylo1
+ P_MTC0 k1, CP0_ENTRYLO1 # load it
+ mtc0_tlbw_hazard
+ tlbwr # write random tlb entry
+ tlbw_eret_hazard
+ eret # return from trap
+ END(except_vec0_r4000)
+
+ /* TLB refill, EXL == 0, R4600 version */
+ LEAF(except_vec0_r4600)
+ .set mips3
+ GET_PGD(k0, k1) # get pgd pointer
+ mfc0 k0, CP0_BADVADDR
+ srl k0, k0, _PGDIR_SHIFT
+ sll k0, k0, 2 # log2(sizeof(pgd_t)
+ addu k1, k1, k0
+ mfc0 k0, CP0_CONTEXT
+ lw k1, (k1)
+ GET_PTE_OFF(k0) # get pte offset
+ and k0, k0, PTEP_INDX_MSK
+ addu k1, k1, k0
+ PTE_L k0, 0(k1)
+ PTE_L k1, PTE_SIZE(k1)
+ PTE_SRL k0, k0, 6
+ P_MTC0 k0, CP0_ENTRYLO0
+ PTE_SRL k1, k1, 6
+ P_MTC0 k1, CP0_ENTRYLO1
+ nop
+ tlbwr
+ nop
+ eret
+ END(except_vec0_r4600)
+
+ /* TLB refill, EXL == 0, R52x0 "Nevada" version */
+ /*
+ * This version has a bug workaround for the Nevada. It seems
+ * as if under certain circumstances the move from cp0_context
+ * might produce a bogus result when the mfc0 instruction and
+ * it's consumer are in a different cacheline or a load instruction,
+ * probably any memory reference, is between them. This is
+ * potencially slower than the R4000 version, so we use this
+ * special version.
+ */
+ .set noreorder
+ .set noat
+ LEAF(except_vec0_nevada)
+ .set mips3
+ mfc0 k0, CP0_BADVADDR # Get faulting address
+ srl k0, k0, _PGDIR_SHIFT # get pgd only bits
+ lw k1, pgd_current # get pgd pointer
+ sll k0, k0, 2 # log2(sizeof(pgd_t)
+ addu k1, k1, k0 # add in pgd offset
+ lw k1, (k1)
+ mfc0 k0, CP0_CONTEXT # get context reg
+ GET_PTE_OFF(k0) # get pte offset
+ and k0, k0, PTEP_INDX_MSK
+ addu k1, k1, k0 # add in offset
+ PTE_L k0, 0(k1) # get even pte
+ PTE_L k1, PTE_SIZE(k1) # get odd pte
+ PTE_SRL k0, k0, 6 # convert to entrylo0
+ P_MTC0 k0, CP0_ENTRYLO0 # load it
+ PTE_SRL k1, k1, 6 # convert to entrylo1
+ P_MTC0 k1, CP0_ENTRYLO1 # load it
+ nop # QED specified nops
+ nop
+ tlbwr # write random tlb entry
+ nop # traditional nop
+ eret # return from trap
+ END(except_vec0_nevada)
+
+ /* TLB refill, EXL == 0, SB1 with M3 errata handling version */
+ LEAF(except_vec0_sb1)
+#if BCM1250_M3_WAR
+ mfc0 k0, CP0_BADVADDR
+ mfc0 k1, CP0_ENTRYHI
+ xor k0, k1
+ srl k0, k0, PAGE_SHIFT+1
+ bnez k0, 1f
+#endif
+ GET_PGD(k0, k1) # get pgd pointer
+ mfc0 k0, CP0_BADVADDR # Get faulting address
+ srl k0, k0, _PGDIR_SHIFT # get pgd only bits
+ sll k0, k0, 2
+ addu k1, k1, k0 # add in pgd offset
+ mfc0 k0, CP0_CONTEXT # get context reg
+ lw k1, (k1)
+ GET_PTE_OFF(k0) # get pte offset
+ and k0, k0, PTEP_INDX_MSK
+ addu k1, k1, k0 # add in offset
+ PTE_L k0, 0(k1) # get even pte
+ PTE_L k1, PTE_SIZE(k1) # get odd pte
+ PTE_SRL k0, k0, 6 # convert to entrylo0
+ P_MTC0 k0, CP0_ENTRYLO0 # load it
+ PTE_SRL k1, k1, 6 # convert to entrylo1
+ P_MTC0 k1, CP0_ENTRYLO1 # load it
+ tlbwr # write random tlb entry
+1: eret # return from trap
+ END(except_vec0_sb1)
+
+ /* TLB refill, EXL == 0, R4[40]00/R5000 badvaddr hwbug version */
+ LEAF(except_vec0_r45k_bvahwbug)
+ .set mips3
+ GET_PGD(k0, k1) # get pgd pointer
+ mfc0 k0, CP0_BADVADDR
+ srl k0, k0, _PGDIR_SHIFT
+ sll k0, k0, 2 # log2(sizeof(pgd_t)
+ addu k1, k1, k0
+ mfc0 k0, CP0_CONTEXT
+ lw k1, (k1)
+#ifndef CONFIG_64BIT_PHYS_ADDR
+ srl k0, k0, 1
+#endif
+ and k0, k0, PTEP_INDX_MSK
+ addu k1, k1, k0
+ PTE_L k0, 0(k1)
+ PTE_L k1, PTE_SIZE(k1)
+ nop /* XXX */
+ tlbp
+ PTE_SRL k0, k0, 6
+ P_MTC0 k0, CP0_ENTRYLO0
+ PTE_SRL k1, k1, 6
+ mfc0 k0, CP0_INDEX
+ P_MTC0 k1, CP0_ENTRYLO1
+ bltzl k0, 1f
+ tlbwr
+1:
+ nop
+ eret
+ END(except_vec0_r45k_bvahwbug)
+
+#ifdef CONFIG_SMP
+ /* TLB refill, EXL == 0, R4000 MP badvaddr hwbug version */
+ LEAF(except_vec0_r4k_mphwbug)
+ .set mips3
+ GET_PGD(k0, k1) # get pgd pointer
+ mfc0 k0, CP0_BADVADDR
+ srl k0, k0, _PGDIR_SHIFT
+ sll k0, k0, 2 # log2(sizeof(pgd_t)
+ addu k1, k1, k0
+ mfc0 k0, CP0_CONTEXT
+ lw k1, (k1)
+#ifndef CONFIG_64BIT_PHYS_ADDR
+ srl k0, k0, 1
+#endif
+ and k0, k0, PTEP_INDX_MSK
+ addu k1, k1, k0
+ PTE_L k0, 0(k1)
+ PTE_L k1, PTE_SIZE(k1)
+ nop /* XXX */
+ tlbp
+ PTE_SRL k0, k0, 6
+ P_MTC0 k0, CP0_ENTRYLO0
+ PTE_SRL k1, k1, 6
+ mfc0 k0, CP0_INDEX
+ P_MTC0 k1, CP0_ENTRYLO1
+ bltzl k0, 1f
+ tlbwr
+1:
+ nop
+ eret
+ END(except_vec0_r4k_mphwbug)
+#endif
+
+ /* TLB refill, EXL == 0, R4000 UP 250MHZ entrylo[01] hwbug version */
+ LEAF(except_vec0_r4k_250MHZhwbug)
+ .set mips3
+ GET_PGD(k0, k1) # get pgd pointer
+ mfc0 k0, CP0_BADVADDR
+ srl k0, k0, _PGDIR_SHIFT
+ sll k0, k0, 2 # log2(sizeof(pgd_t)
+ addu k1, k1, k0
+ mfc0 k0, CP0_CONTEXT
+ lw k1, (k1)
+#ifndef CONFIG_64BIT_PHYS_ADDR
+ srl k0, k0, 1
+#endif
+ and k0, k0, PTEP_INDX_MSK
+ addu k1, k1, k0
+ PTE_L k0, 0(k1)
+ PTE_L k1, PTE_SIZE(k1)
+ PTE_SRL k0, k0, 6
+ P_MTC0 zero, CP0_ENTRYLO0
+ P_MTC0 k0, CP0_ENTRYLO0
+ PTE_SRL k1, k1, 6
+ P_MTC0 zero, CP0_ENTRYLO1
+ P_MTC0 k1, CP0_ENTRYLO1
+ b 1f
+ tlbwr
+1:
+ nop
+ eret
+ END(except_vec0_r4k_250MHZhwbug)
+
+#ifdef CONFIG_SMP
+ /* TLB refill, EXL == 0, R4000 MP 250MHZ entrylo[01]+badvaddr bug version */
+ LEAF(except_vec0_r4k_MP250MHZhwbug)
+ .set mips3
+ GET_PGD(k0, k1) # get pgd pointer
+ mfc0 k0, CP0_BADVADDR
+ srl k0, k0, _PGDIR_SHIFT
+ sll k0, k0, 2 # log2(sizeof(pgd_t)
+ addu k1, k1, k0
+ mfc0 k0, CP0_CONTEXT
+ lw k1, (k1)
+#ifndef CONFIG_64BIT_PHYS_ADDR
+ srl k0, k0, 1
+#endif
+ and k0, k0, PTEP_INDX_MSK
+ addu k1, k1, k0
+ PTE_L k0, 0(k1)
+ PTE_L k1, PTE_SIZE(k1)
+ nop /* XXX */
+ tlbp
+ PTE_SRL k0, k0, 6
+ P_MTC0 zero, CP0_ENTRYLO0
+ P_MTC0 k0, CP0_ENTRYLO0
+ mfc0 k0, CP0_INDEX
+ PTE_SRL k1, k1, 6
+ P_MTC0 zero, CP0_ENTRYLO1
+ P_MTC0 k1, CP0_ENTRYLO1
+ bltzl k0, 1f
+ tlbwr
+1:
+ nop
+ eret
+ END(except_vec0_r4k_MP250MHZhwbug)
+#endif
+
+ __FINIT
+
+ .set noreorder
+
+/*
+ * From the IDT errata for the QED RM5230 (Nevada), processor revision 1.0:
+ * 2. A timing hazard exists for the TLBP instruction.
+ *
+ * stalling_instruction
+ * TLBP
+ *
+ * The JTLB is being read for the TLBP throughout the stall generated by the
+ * previous instruction. This is not really correct as the stalling instruction
+ * can modify the address used to access the JTLB. The failure symptom is that
+ * the TLBP instruction will use an address created for the stalling instruction
+ * and not the address held in C0_ENHI and thus report the wrong results.
+ *
+ * The software work-around is to not allow the instruction preceding the TLBP
+ * to stall - make it an NOP or some other instruction guaranteed not to stall.
+ *
+ * Errata 2 will not be fixed. This errata is also on the R5000.
+ *
+ * As if we MIPS hackers wouldn't know how to nop pipelines happy ...
+ */
+#define R5K_HAZARD nop
+
+ /*
+ * Note for many R4k variants tlb probes cannot be executed out
+ * of the instruction cache else you get bogus results.
+ */
+ .align 5
+ NESTED(handle_tlbl, PT_SIZE, sp)
+ .set noat
+#if BCM1250_M3_WAR
+ mfc0 k0, CP0_BADVADDR
+ mfc0 k1, CP0_ENTRYHI
+ xor k0, k1
+ srl k0, k0, PAGE_SHIFT+1
+ beqz k0, 1f
+ nop
+ .set mips3
+ eret
+ .set mips0
+1:
+#endif
+invalid_tlbl:
+#ifdef TLB_OPTIMIZE
+ .set mips3
+ /* Test present bit in entry. */
+ LOAD_PTE(k0, k1)
+ R5K_HAZARD
+ tlbp
+ PTE_PRESENT(k0, k1, nopage_tlbl)
+ PTE_MAKEVALID(k0, k1)
+ PTE_RELOAD(k1, k0)
+ mtc0_tlbw_hazard
+ tlbwi
+ tlbw_eret_hazard
+ .set mips3
+ eret
+ .set mips0
+#endif
+
+nopage_tlbl:
+ DO_FAULT(0)
+ END(handle_tlbl)
+
+ .align 5
+ NESTED(handle_tlbs, PT_SIZE, sp)
+ .set noat
+#ifdef TLB_OPTIMIZE
+ .set mips3
+ li k0,0
+ LOAD_PTE(k0, k1)
+ R5K_HAZARD
+ tlbp # find faulting entry
+ PTE_WRITABLE(k0, k1, nopage_tlbs)
+ PTE_MAKEWRITE(k0, k1)
+ PTE_RELOAD(k1, k0)
+ mtc0_tlbw_hazard
+ tlbwi
+ tlbw_eret_hazard
+ .set mips3
+ eret
+ .set mips0
+#endif
+
+nopage_tlbs:
+ DO_FAULT(1)
+ END(handle_tlbs)
+
+ .align 5
+ NESTED(handle_mod, PT_SIZE, sp)
+ .set noat
+#ifdef TLB_OPTIMIZE
+ .set mips3
+ LOAD_PTE(k0, k1)
+ R5K_HAZARD
+ tlbp # find faulting entry
+ andi k0, k0, _PAGE_WRITE
+ beqz k0, nowrite_mod
+ PTE_L k0, (k1)
+
+ /* Present and writable bits set, set accessed and dirty bits. */
+ PTE_MAKEWRITE(k0, k1)
+
+ /* Now reload the entry into the tlb. */
+ PTE_RELOAD(k1, k0)
+ mtc0_tlbw_hazard
+ tlbwi
+ tlbw_eret_hazard
+ .set mips3
+ eret
+ .set mips0
+#endif
+
+nowrite_mod:
+ DO_FAULT(1)
+ END(handle_mod)
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2000 Silicon Graphics, Inc.
+ * Written by Ulf Carlsson (ulfc@engr.sgi.com)
+ * Copyright (C) 2002 Maciej W. Rozycki
+ */
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/threads.h>
+
+#include <asm/asm.h>
+#include <asm/hazards.h>
+#include <asm/regdef.h>
+#include <asm/mipsregs.h>
+#include <asm/stackframe.h>
+#include <asm/war.h>
+
+#define _VMALLOC_START 0xc000000000000000
+
+ /*
+ * After this macro runs we have a pointer to the pte of the address
+ * that caused the fault in PTR.
+ */
+ .macro LOAD_PTE2, ptr, tmp, kaddr
+#ifdef CONFIG_SMP
+ dmfc0 \ptr, CP0_CONTEXT
+ dmfc0 \tmp, CP0_BADVADDR
+ dsra \ptr, 23 # get pgd_current[cpu]
+#else
+ dmfc0 \tmp, CP0_BADVADDR
+ dla \ptr, pgd_current
+#endif
+ bltz \tmp, \kaddr
+ ld \ptr, (\ptr)
+ dsrl \tmp, (_PGDIR_SHIFT-3) # get pgd offset in bytes
+ andi \tmp, ((_PTRS_PER_PGD - 1)<<3)
+ daddu \ptr, \tmp # add in pgd offset
+ dmfc0 \tmp, CP0_BADVADDR
+ ld \ptr, (\ptr) # get pmd pointer
+ dsrl \tmp, (_PMD_SHIFT-3) # get pmd offset in bytes
+ andi \tmp, ((_PTRS_PER_PMD - 1)<<3)
+ daddu \ptr, \tmp # add in pmd offset
+ dmfc0 \tmp, CP0_XCONTEXT
+ ld \ptr, (\ptr) # get pte pointer
+ andi \tmp, 0xff0 # get pte offset
+ daddu \ptr, \tmp
+ .endm
+
+
+ /*
+ * Ditto for the kernel table.
+ */
+ .macro LOAD_KPTE2, ptr, tmp, not_vmalloc
+ /*
+ * First, determine that the address is in/above vmalloc range.
+ */
+ dmfc0 \tmp, CP0_BADVADDR
+ dli \ptr, _VMALLOC_START
+
+ /*
+ * Now find offset into kptbl.
+ */
+ dsubu \tmp, \tmp, \ptr
+ dla \ptr, kptbl
+ dsrl \tmp, (_PAGE_SHIFT+1) # get vpn2
+ dsll \tmp, 4 # byte offset of pte
+ daddu \ptr, \ptr, \tmp
+
+ /*
+ * Determine that fault address is within vmalloc range.
+ */
+ dla \tmp, ekptbl
+ slt \tmp, \ptr, \tmp
+ beqz \tmp, \not_vmalloc # not vmalloc
+ nop
+ .endm
+
+
+ /*
+ * This places the even/odd pte pair in the page table at the pte
+ * entry pointed to by PTE into ENTRYLO0 and ENTRYLO1.
+ */
+ .macro PTE_RELOAD, pte0, pte1
+ dsrl \pte0, 6 # convert to entrylo0
+ dmtc0 \pte0, CP0_ENTRYLO0 # load it
+ dsrl \pte1, 6 # convert to entrylo1
+ dmtc0 \pte1, CP0_ENTRYLO1 # load it
+ .endm
+
+
+ .text
+ .set noreorder
+ .set mips3
+
+ __INIT
+
+ /*
+ * TLB refill handlers for the R4000 and SB1.
+ * Attention: We may only use 32 instructions / 128 bytes.
+ */
+ .align 5
+LEAF(except_vec1_r4k)
+ .set noat
+ dla k0, handle_vec1_r4k
+ jr k0
+ nop
+END(except_vec1_r4k)
+
+LEAF(except_vec1_sb1)
+#if BCM1250_M3_WAR
+ dmfc0 k0, CP0_BADVADDR
+ dmfc0 k1, CP0_ENTRYHI
+ xor k0, k1
+ dsrl k0, k0, _PAGE_SHIFT+1
+ bnez k0, 1f
+#endif
+ .set noat
+ dla k0, handle_vec1_r4k
+ jr k0
+ nop
+
+1: eret
+ nop
+END(except_vec1_sb1)
+
+ __FINIT
+
+ .align 5
+LEAF(handle_vec1_r4k)
+ .set noat
+ LOAD_PTE2 k1 k0 9f
+ ld k0, 0(k1) # get even pte
+ ld k1, 8(k1) # get odd pte
+ PTE_RELOAD k0 k1
+ mtc0_tlbw_hazard
+ tlbwr
+ tlbw_eret_hazard
+ eret
+
+9: # handle the vmalloc range
+ LOAD_KPTE2 k1 k0 invalid_vmalloc_address
+ ld k0, 0(k1) # get even pte
+ ld k1, 8(k1) # get odd pte
+ PTE_RELOAD k0 k1
+ mtc0_tlbw_hazard
+ tlbwr
+ tlbw_eret_hazard
+ eret
+END(handle_vec1_r4k)
+
+
+ __INIT
+
+ /*
+ * TLB refill handler for the R10000.
+ * Attention: We may only use 32 instructions / 128 bytes.
+ */
+ .align 5
+LEAF(except_vec1_r10k)
+ .set noat
+ dla k0, handle_vec1_r10k
+ jr k0
+ nop
+END(except_vec1_r10k)
+
+ __FINIT
+
+ .align 5
+LEAF(handle_vec1_r10k)
+ .set noat
+ LOAD_PTE2 k1 k0 9f
+ ld k0, 0(k1) # get even pte
+ ld k1, 8(k1) # get odd pte
+ PTE_RELOAD k0 k1
+ nop
+ tlbwr
+ eret
+
+9: # handle the vmalloc range
+ LOAD_KPTE2 k1 k0 invalid_vmalloc_address
+ ld k0, 0(k1) # get even pte
+ ld k1, 8(k1) # get odd pte
+ PTE_RELOAD k0 k1
+ nop
+ tlbwr
+ eret
+END(handle_vec1_r10k)
+
+
+ .align 5
+LEAF(invalid_vmalloc_address)
+ .set noat
+ SAVE_ALL
+ CLI
+ dmfc0 t0, CP0_BADVADDR
+ sd t0, PT_BVADDR(sp)
+ move a0, sp
+ jal show_regs
+ PANIC("Invalid kernel address")
+END(invalid_vmalloc_address)
--- /dev/null
+/*
+ * fixup-mpc30x.c, The Victor MP-C303/304 specific PCI fixups.
+ *
+ * Copyright (C) 2002,2004 Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include <asm/vr41xx/mpc30x.h>
+#include <asm/vr41xx/vrc4173.h>
+
+static const int internal_func_irqs[] __initdata = {
+ VRC4173_CASCADE_IRQ,
+ VRC4173_AC97_IRQ,
+ VRC4173_USB_IRQ,
+};
+
+static char irq_tab_mpc30x[] __initdata = {
+ [12] = VRC4173_PCMCIA1_IRQ,
+ [13] = VRC4173_PCMCIA2_IRQ,
+ [29] = MQ200_IRQ,
+};
+
+int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+ if (slot == 30)
+ return internal_func_irqs[PCI_FUNC(dev->devfn)];
+
+ return irq_tab_mpc30x[slot];
+}
+
+struct pci_fixup pcibios_fixups[] __initdata = {
+ { .pass = 0, },
+};
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2004 by Ralf Baechle
+ *
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <asm/gt64240.h>
+#include <asm/pci_channel.h>
+
+extern struct pci_ops titan_pci_ops;
+
+static struct resource py_mem_resource = {
+ "Titan PCI MEM", 0xe0000000UL, 0xe3ffffffUL, IORESOURCE_MEM
+};
+
+static struct resource py_io_resource = {
+ "Titan IO MEM", 0x00000000UL, 0x00ffffffUL, IORESOURCE_IO,
+};
+
+static struct pci_controller py_controller = {
+ .pci_ops = &titan_pci_ops,
+ .mem_resource = &py_mem_resource,
+ .mem_offset = 0x10000000UL,
+ .io_resource = &py_io_resource,
+ .io_offset = 0x00000000UL
+};
+
+static int __init pmc_yosemite_setup(void)
+{
+ register_pci_controller(&py_controller);
+}
--- /dev/null
+/*
+ * Copyright (C) 2003 PMC-Sierra Inc.
+ * Author: Manish Lachwani (lachwani@pmc-sierra.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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.
+ */
+
+/*
+ * Detailed Description:
+ *
+ * This block implements the I2C interface to the slave devices like the
+ * Atmel 24C32 EEPROM and the MAX 1619 Sensors device. The I2C Master interface
+ * can be controlled by the SCMB block. And the SCMB block kicks in only when
+ * using the Ethernet Mode of operation and __not__ the SysAD mode
+ *
+ * The SCMB controls the two modes: MDIO and the I2C. The MDIO mode is used to
+ * communicate with the Quad-PHY from Marvel. The I2C is used to communicate
+ * with the I2C slave devices. It seems that the driver does not explicitly
+ * deal with the control of SDA and SCL serial lines. So, the driver will set
+ * the slave address, drive the command and then the data. The SCMB will then
+ * control the two serial lines as required.
+ *
+ * It seems the documents are very unclear abt this. Hence, I took some time
+ * out to write the desciption to have an idea of how the I2C can actually
+ * work. Currently, this Linux driver wont be integrated into the generic Linux
+ * I2C framework. And finally, the I2C interface is also known as the 2BI
+ * interface. 2BI means 2-bit interface referring to SDA and SCL serial lines
+ * respectively.
+ *
+ * - Manish Lachwani (12/09/2003)
+ */
+
+#include "i2c-yosemite.h"
+
+/*
+ * Poll the I2C interface for the BUSY bit.
+ */
+static int titan_i2c_poll(void)
+{
+ int i = 0;
+ unsigned long val = 0;
+
+ for (i = 0; i < TITAN_I2C_MAX_POLL; i++) {
+ val = TITAN_I2C_READ(TITAN_I2C_COMMAND);
+
+ if (!(val & 0x8000))
+ return 0;
+ }
+
+ return TITAN_I2C_ERR_TIMEOUT;
+}
+
+/*
+ * Execute the I2C command
+ */
+int titan_i2c_xfer(unsigned int slave_addr, titan_i2c_command * cmd,
+ int size, unsigned int *addr)
+{
+ int loop = 0, bytes, i;
+ unsigned int *write_data, data, *read_data;
+ unsigned long reg_val, val;
+
+ write_data = cmd->data;
+ read_data = addr;
+
+ TITAN_I2C_WRITE(TITAN_I2C_SLAVE_ADDRESS, slave_addr);
+
+ if (cmd->type == TITAN_I2C_CMD_WRITE)
+ loop = cmd->write_size;
+ else
+ loop = size;
+
+ while (loop > 0) {
+ if ((cmd->type == TITAN_I2C_CMD_WRITE) ||
+ (cmd->type == TITAN_I2C_CMD_READ_WRITE)) {
+
+ reg_val = TITAN_I2C_DATA;
+ for (i = 0; i < TITAN_I2C_MAX_WORDS_PER_RW;
+ ++i, write_data += 2, reg_val += 4) {
+ if (bytes < cmd->write_size) {
+ data = write_data[0];
+ ++data;
+ }
+
+ if (bytes < cmd->write_size) {
+ data = write_data[1];
+ ++data;
+ }
+
+ TITAN_I2C_WRITE(reg_val, data);
+ }
+ }
+
+ TITAN_I2C_WRITE(TITAN_I2C_COMMAND,
+ (unsigned int) (cmd->type << 13));
+ if (titan_i2c_poll() != TITAN_I2C_ERR_OK)
+ return TITAN_I2C_ERR_TIMEOUT;
+
+ if ((cmd->type == TITAN_I2C_CMD_READ) ||
+ (cmd->type == TITAN_I2C_CMD_READ_WRITE)) {
+
+ reg_val = TITAN_I2C_DATA;
+ for (i = 0; i < TITAN_I2C_MAX_WORDS_PER_RW;
+ ++i, read_data += 2, reg_val += 4) {
+ data = TITAN_I2C_READ(reg_val);
+
+ if (bytes < size) {
+ read_data[0] = data & 0xff;
+ ++bytes;
+ }
+
+ if (bytes < size) {
+ read_data[1] =
+ ((data >> 8) & 0xff);
+ ++bytes;
+ }
+ }
+ }
+
+ loop -= (TITAN_I2C_MAX_WORDS_PER_RW * 2);
+ }
+
+ /*
+ * Read the Interrupt status and then return the appropriate error code
+ */
+
+ val = TITAN_I2C_READ(TITAN_I2C_INTERRUPTS);
+ if (val & 0x0020)
+ return TITAN_I2C_ERR_ARB_LOST;
+
+ if (val & 0x0040)
+ return TITAN_I2C_ERR_NO_RESP;
+
+ if (val & 0x0080)
+ return TITAN_I2C_ERR_DATA_COLLISION;
+
+ return TITAN_I2C_ERR_OK;
+}
+
+/*
+ * Init the I2C subsystem of the PMC-Sierra Yosemite board
+ */
+int titan_i2c_init(titan_i2c_config * config)
+{
+ unsigned int val;
+
+ /*
+ * Reset the SCMB and program into the I2C mode
+ */
+ TITAN_I2C_WRITE(TITAN_I2C_SCMB_CONTROL, 0xA000);
+ TITAN_I2C_WRITE(TITAN_I2C_SCMB_CONTROL, 0x2000);
+
+ /*
+ * Configure the filtera and clka values
+ */
+ val = TITAN_I2C_READ(TITAN_I2C_SCMB_CLOCK_A);
+ val |= ((val & ~(0xF000)) | ((config->filtera << 12) & 0xF000));
+ val |= ((val & ~(0x03FF)) | (config->clka & 0x03FF));
+ TITAN_I2C_WRITE(TITAN_I2C_SCMB_CLOCK_A, val);
+
+ /*
+ * Configure the filterb and clkb values
+ */
+ val = TITAN_I2C_READ(TITAN_I2C_SCMB_CLOCK_B);
+ val |= ((val & ~(0xF000)) | ((config->filterb << 12) & 0xF000));
+ val |= ((val & ~(0x03FF)) | (config->clkb & 0x03FF));
+ TITAN_I2C_WRITE(TITAN_I2C_SCMB_CLOCK_B, val);
+
+ return TITAN_I2C_ERR_OK;
+}
--- /dev/null
+/*
+ * tb0219.c, Setup for the TANBAC TB0219
+ *
+ * Copyright (C) 2003 Megasolution Inc. <matsu@megasolution.jp>
+ * Copyright (C) 2004 Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/reboot.h>
+
+#define TB0219_RESET_REGS KSEG1ADDR(0x0a00000e)
+
+#define tb0219_hard_reset() writew(0, TB0219_RESET_REGS)
+
+static void tanbac_tb0219_restart(char *command)
+{
+ local_irq_disable();
+ tb0219_hard_reset();
+ while (1);
+}
+
+static int __init tanbac_tb0219_setup(void)
+{
+ _machine_restart = tanbac_tb0219_restart;
+
+ return 0;
+}
+
+early_initcall(tanbac_tb0219_setup);
--- /dev/null
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_PARISC=y
+CONFIG_MMU=y
+CONFIG_STACK_GROWSUP=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+# CONFIG_CLEAN_COMPILE is not set
+# CONFIG_STANDALONE is not set
+CONFIG_BROKEN=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+# CONFIG_AUDIT is not set
+CONFIG_LOG_BUF_SHIFT=16
+CONFIG_HOTPLUG=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_EMBEDDED=y
+CONFIG_KALLSYMS=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# Processor type and features
+#
+# CONFIG_PA7000 is not set
+# CONFIG_PA7100LC is not set
+# CONFIG_PA7200 is not set
+# CONFIG_PA7300LC is not set
+CONFIG_PA8X00=y
+CONFIG_PA20=y
+CONFIG_PREFETCH=y
+CONFIG_PARISC64=y
+CONFIG_64BIT=y
+# CONFIG_SMP is not set
+CONFIG_DISCONTIGMEM=y
+# CONFIG_PREEMPT is not set
+CONFIG_COMPAT=y
+
+#
+# Bus options (PCI, PCMCIA, EISA, GSC, ISA)
+#
+# CONFIG_GSC is not set
+CONFIG_PCI=y
+CONFIG_PCI_LEGACY_PROC=y
+CONFIG_PCI_NAMES=y
+CONFIG_PCI_LBA=y
+CONFIG_IOSAPIC=y
+CONFIG_IOMMU_SBA=y
+# CONFIG_SUPERIO is not set
+CONFIG_CHASSIS_LCD_LED=y
+# CONFIG_PDC_CHASSIS is not set
+
+#
+# PCMCIA/CardBus support
+#
+CONFIG_PCMCIA=m
+CONFIG_PCMCIA_DEBUG=y
+CONFIG_YENTA=m
+CONFIG_CARDBUS=y
+# CONFIG_I82092 is not set
+# CONFIG_TCIC is not set
+
+#
+# PCI Hotplug Support
+#
+# CONFIG_HOTPLUG_PCI is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+# CONFIG_FW_LOADER is not set
+CONFIG_DEBUG_DRIVER=y
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Plug and Play support
+#
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+CONFIG_BLK_DEV_UMEM=m
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_CARMEL is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=6144
+CONFIG_BLK_DEV_INITRD=y
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+CONFIG_SCSI=y
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=y
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=y
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_CHR_DEV_SG=y
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+CONFIG_SCSI_SPI_ATTRS=y
+CONFIG_SCSI_FC_ATTRS=m
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_MEGARAID is not set
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_CPQFCTS is not set
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+CONFIG_SCSI_SYM53C8XX_2=y
+CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0
+CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
+CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
+CONFIG_SCSI_SYM53C8XX_IOMAPPED=y
+# CONFIG_SCSI_IPR is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_QLOGIC_ISP is not set
+CONFIG_SCSI_QLOGIC_FC=m
+# CONFIG_SCSI_QLOGIC_FC_FIRMWARE is not set
+CONFIG_SCSI_QLOGIC_1280=m
+CONFIG_SCSI_QLA2XXX=y
+# CONFIG_SCSI_QLA21XX is not set
+# CONFIG_SCSI_QLA22XX is not set
+CONFIG_SCSI_QLA2300=m
+CONFIG_SCSI_QLA2322=m
+CONFIG_SCSI_QLA6312=m
+CONFIG_SCSI_QLA6322=m
+# CONFIG_SCSI_DC395x is not set
+# CONFIG_SCSI_DC390T is not set
+CONFIG_SCSI_DEBUG=m
+
+#
+# PCMCIA SCSI adapter support
+#
+# CONFIG_PCMCIA_FDOMAIN is not set
+# CONFIG_PCMCIA_QLOGIC is not set
+# CONFIG_PCMCIA_SYM53C500 is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=y
+CONFIG_MD_LINEAR=y
+CONFIG_MD_RAID0=y
+CONFIG_MD_RAID1=y
+# CONFIG_MD_RAID5 is not set
+# CONFIG_MD_RAID6 is not set
+# CONFIG_MD_MULTIPATH is not set
+# CONFIG_BLK_DEV_DM is not set
+
+#
+# Fusion MPT device support
+#
+CONFIG_FUSION=m
+CONFIG_FUSION_MAX_SGE=40
+CONFIG_FUSION_ISENSE=m
+CONFIG_FUSION_CTL=m
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_IEEE1394 is not set
+
+#
+# I2O device support
+#
+# CONFIG_I2O is not set
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+CONFIG_PACKET_MMAP=y
+CONFIG_NETLINK_DEV=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=m
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+# CONFIG_INET_IPCOMP is not set
+
+#
+# IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+# CONFIG_IPV6 is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+CONFIG_IP_NF_FTP=m
+CONFIG_IP_NF_IRC=m
+CONFIG_IP_NF_TFTP=m
+CONFIG_IP_NF_AMANDA=m
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_LIMIT=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_MAC=m
+CONFIG_IP_NF_MATCH_PKTTYPE=m
+CONFIG_IP_NF_MATCH_MARK=m
+CONFIG_IP_NF_MATCH_MULTIPORT=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_DSCP=m
+CONFIG_IP_NF_MATCH_AH_ESP=m
+CONFIG_IP_NF_MATCH_LENGTH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_TCPMSS=m
+CONFIG_IP_NF_MATCH_HELPER=m
+CONFIG_IP_NF_MATCH_STATE=m
+CONFIG_IP_NF_MATCH_CONNTRACK=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_NAT=m
+CONFIG_IP_NF_NAT_NEEDED=y
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_TARGET_NETMAP=m
+CONFIG_IP_NF_TARGET_SAME=m
+# CONFIG_IP_NF_NAT_LOCAL is not set
+CONFIG_IP_NF_NAT_SNMP_BASIC=m
+CONFIG_IP_NF_NAT_IRC=m
+CONFIG_IP_NF_NAT_FTP=m
+CONFIG_IP_NF_NAT_TFTP=m
+CONFIG_IP_NF_NAT_AMANDA=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_DSCP=m
+CONFIG_IP_NF_TARGET_MARK=m
+CONFIG_IP_NF_TARGET_CLASSIFY=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_TCPMSS=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+# CONFIG_IP_NF_COMPAT_IPCHAINS is not set
+# CONFIG_IP_NF_COMPAT_IPFWADM is not set
+CONFIG_IP_NF_TARGET_NOTRACK=m
+CONFIG_IP_NF_RAW=m
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=m
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_SCTP is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+CONFIG_LLC=m
+CONFIG_LLC2=m
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_NET_DIVERT is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+CONFIG_NET_PKTGEN=m
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=m
+CONFIG_BONDING=m
+# CONFIG_EQUALIZER is not set
+CONFIG_TUN=m
+# CONFIG_ETHERTAP is not set
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+# CONFIG_HAPPYMEAL is not set
+# CONFIG_SUNGEM is not set
+CONFIG_NET_VENDOR_3COM=y
+CONFIG_VORTEX=m
+CONFIG_TYPHOON=m
+
+#
+# Tulip family network device support
+#
+CONFIG_NET_TULIP=y
+CONFIG_DE2104X=y
+CONFIG_TULIP=y
+# CONFIG_TULIP_MWI is not set
+CONFIG_TULIP_MMIO=y
+# CONFIG_TULIP_NAPI is not set
+# CONFIG_DE4X5 is not set
+# CONFIG_WINBOND_840 is not set
+# CONFIG_DM9102 is not set
+CONFIG_PCMCIA_XIRCOM=m
+CONFIG_PCMCIA_XIRTULIP=m
+CONFIG_HP100=m
+CONFIG_NET_PCI=y
+CONFIG_PCNET32=m
+# CONFIG_AMD8111_ETH is not set
+# CONFIG_ADAPTEC_STARFIRE is not set
+# CONFIG_B44 is not set
+# CONFIG_FORCEDETH is not set
+# CONFIG_DGRS is not set
+CONFIG_EEPRO100=m
+# CONFIG_EEPRO100_PIO is not set
+CONFIG_E100=m
+CONFIG_E100_NAPI=y
+# CONFIG_FEALNX is not set
+CONFIG_NATSEMI=m
+# CONFIG_NE2K_PCI is not set
+# CONFIG_8139CP is not set
+CONFIG_8139TOO=m
+# CONFIG_8139TOO_PIO is not set
+# CONFIG_8139TOO_TUNE_TWISTER is not set
+# CONFIG_8139TOO_8129 is not set
+# CONFIG_8139_OLD_RX_RESET is not set
+# CONFIG_SIS900 is not set
+CONFIG_EPIC100=m
+# CONFIG_SUNDANCE is not set
+CONFIG_VIA_RHINE=m
+CONFIG_VIA_RHINE_MMIO=y
+
+#
+# Ethernet (1000 Mbit)
+#
+CONFIG_ACENIC=m
+CONFIG_ACENIC_OMIT_TIGON_I=y
+CONFIG_DL2K=m
+CONFIG_E1000=m
+CONFIG_E1000_NAPI=y
+# CONFIG_NS83820 is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
+# CONFIG_SK98LIN is not set
+CONFIG_TIGON3=m
+
+#
+# Ethernet (10000 Mbit)
+#
+CONFIG_IXGB=m
+CONFIG_IXGB_NAPI=y
+CONFIG_S2IO=m
+CONFIG_S2IO_NAPI=y
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+CONFIG_NET_RADIO=y
+
+#
+# Obsolete Wireless cards support (pre-802.11)
+#
+# CONFIG_STRIP is not set
+CONFIG_PCMCIA_WAVELAN=m
+CONFIG_PCMCIA_NETWAVE=m
+
+#
+# Wireless 802.11 Frequency Hopping cards support
+#
+# CONFIG_PCMCIA_RAYCS is not set
+
+#
+# Wireless 802.11b ISA/PCI cards support
+#
+# CONFIG_AIRO is not set
+CONFIG_HERMES=m
+CONFIG_PLX_HERMES=m
+CONFIG_TMD_HERMES=m
+CONFIG_PCI_HERMES=m
+# CONFIG_ATMEL is not set
+
+#
+# Wireless 802.11b Pcmcia/Cardbus cards support
+#
+CONFIG_PCMCIA_HERMES=m
+CONFIG_AIRO_CS=m
+# CONFIG_PCMCIA_WL3501 is not set
+
+#
+# Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support
+#
+# CONFIG_PRISM54 is not set
+CONFIG_NET_WIRELESS=y
+
+#
+# PCMCIA network device support
+#
+CONFIG_NET_PCMCIA=y
+CONFIG_PCMCIA_3C589=m
+CONFIG_PCMCIA_3C574=m
+# CONFIG_PCMCIA_FMVJ18X is not set
+# CONFIG_PCMCIA_PCNET is not set
+# CONFIG_PCMCIA_NMCLAN is not set
+CONFIG_PCMCIA_SMC91C92=m
+CONFIG_PCMCIA_XIRC2PS=m
+# CONFIG_PCMCIA_AXNET is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+# CONFIG_PPPOE is not set
+# CONFIG_SLIP is not set
+# CONFIG_NET_FC is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_TSDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input I/O drivers
+#
+# CONFIG_GAMEPORT is not set
+CONFIG_SOUND_GAMEPORT=y
+# CONFIG_SERIO is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+# CONFIG_SERIAL_8250_CS is not set
+CONFIG_SERIAL_8250_NR_UARTS=8
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+# CONFIG_SERIAL_8250_DETECT_IRQ is not set
+# CONFIG_SERIAL_8250_MULTIPORT is not set
+# CONFIG_SERIAL_8250_RSA is not set
+
+#
+# Non-8250 serial port support
+#
+# CONFIG_SERIAL_MUX is not set
+CONFIG_PDC_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_QIC02_TAPE is not set
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+CONFIG_GEN_RTC=y
+CONFIG_GEN_RTC_X=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+# CONFIG_AGP is not set
+# CONFIG_DRM is not set
+
+#
+# PCMCIA character devices
+#
+# CONFIG_SYNCLINK_CS is not set
+CONFIG_RAW_DRIVER=y
+CONFIG_MAX_RAW_DEVS=256
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Misc devices
+#
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Digital Video Broadcasting Devices
+#
+# CONFIG_DVB is not set
+
+#
+# Graphics support
+#
+# CONFIG_FB is not set
+
+#
+# Console display driver support
+#
+# CONFIG_MDA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE_COLUMNS=160
+CONFIG_DUMMY_CONSOLE_ROWS=64
+CONFIG_DUMMY_CONSOLE=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_FS_XATTR is not set
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
+# CONFIG_REISERFS_FS is not set
+CONFIG_JFS_FS=m
+# CONFIG_JFS_POSIX_ACL is not set
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_STATISTICS is not set
+CONFIG_XFS_FS=m
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_SECURITY is not set
+# CONFIG_XFS_POSIX_ACL is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+# CONFIG_ZISOFS is not set
+CONFIG_UDF_FS=m
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_SYSFS=y
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVPTS_FS_XATTR is not set
+CONFIG_TMPFS=y
+# CONFIG_HUGETLBFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+CONFIG_UFS_FS=m
+# CONFIG_UFS_FS_WRITE is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_DIRECTIO=y
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_TCP=y
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+CONFIG_SMB_FS=m
+CONFIG_SMB_NLS_DEFAULT=y
+CONFIG_SMB_NLS_REMOTE="cp437"
+CONFIG_CIFS=m
+# CONFIG_CIFS_STATS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=m
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+CONFIG_NLS_CODEPAGE_863=m
+# CONFIG_NLS_CODEPAGE_864 is not set
+CONFIG_NLS_CODEPAGE_865=m
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+CONFIG_NLS_ISO8859_15=m
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+CONFIG_NLS_UTF8=m
+
+#
+# Profiling support
+#
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=m
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SLAB is not set
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_FRAME_POINTER is not set
+# CONFIG_DEBUG_INFO is not set
+
+#
+# Security options
+#
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+# CONFIG_CRYPTO_ARC4 is not set
+CONFIG_CRYPTO_DEFLATE=m
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_TEST=m
+
+#
+# Library routines
+#
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=m
+CONFIG_ZLIB_DEFLATE=m
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
CONFIG_DEBUG_KERNEL=y
# CONFIG_DEBUG_SLAB is not set
CONFIG_MAGIC_SYSRQ=y
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_RWLOCK is not set
CONFIG_FRAME_POINTER=y
# CONFIG_DEBUG_INFO is not set
#include <linux/mm.h>
#include <asm/hardware.h>
#include <asm/io.h>
+#include <asm/mmzone.h>
#include <asm/pdc.h>
+#include <asm/pdcpat.h>
#include <asm/processor.h>
#include <asm/page.h>
#include <asm/parisc-device.h>
extern void $$dyncall(void);
EXPORT_SYMBOL($$dyncall);
#endif
+
+#ifdef CONFIG_DISCONTIGMEM
+#include <asm/mmzone.h>
+EXPORT_SYMBOL(node_data);
+EXPORT_SYMBOL(pfnnid_map);
+#endif
* interfaces to log Chassis Codes via PDC (firmware)
*
* Copyright (C) 2002 Laurent Canet <canetl@esiee.fr>
- * Copyright (C) 2002-2003 Thibaut Varene <varenet@esiee.fr>
+ * Copyright (C) 2002-2004 Thibaut VARENE <varenet@esiee.fr>
*
* 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 <asm/pdc_chassis.h>
#include <asm/processor.h>
+#include <asm/pdc.h>
+#include <asm/pdcpat.h>
#ifdef CONFIG_PDC_CHASSIS
static int pdc_chassis_old = 0;
+static unsigned int pdc_chassis_enabled = 1;
+
+
+/**
+ * pdc_chassis_setup() - Enable/disable pdc_chassis code at boot time.
+ * @str configuration param: 0 to disable chassis log
+ * @return 1
+ */
+
+static int __init pdc_chassis_setup(char *str)
+{
+ /*panic_timeout = simple_strtoul(str, NULL, 0);*/
+ get_option(&str, &pdc_chassis_enabled);
+ return 1;
+}
+__setup("pdcchassis=", pdc_chassis_setup);
/**
{
#ifdef CONFIG_PDC_CHASSIS
int handle = 0;
+ if (pdc_chassis_enabled) {
+ DPRINTK(KERN_DEBUG "%s: parisc_pdc_chassis_init()\n", __FILE__);
+
+ /* Let see if we have something to handle... */
+ /* Check for PDC_PAT or old LED Panel */
+ pdc_chassis_checkold();
+ if (is_pdc_pat()) {
+ printk(KERN_INFO "Enabling PDC_PAT chassis codes support.\n");
+ handle = 1;
+ }
+ else if (pdc_chassis_old) {
+ printk(KERN_INFO "Enabling old style chassis LED panel support.\n");
+ handle = 1;
+ }
- DPRINTK(KERN_DEBUG "%s: parisc_pdc_chassis_init()\n", __FILE__);
-
- /* Let see if we have something to handle... */
- /* Check for PDC_PAT or old LED Panel */
- pdc_chassis_checkold();
- if (is_pdc_pat()) {
-#ifdef __LP64__ /* see pdc_chassis_send_status() */
- printk(KERN_INFO "Enabling PDC_PAT chassis codes support.\n");
- handle = 1;
-#endif /* __LP64__ */
- }
- else if (pdc_chassis_old) {
- printk(KERN_INFO "Enabling old style chassis LED panel support.\n");
- handle = 1;
- }
-
- if (handle) {
- /* initialize panic notifier chain */
- notifier_chain_register(&panic_notifier_list, &pdc_chassis_panic_block);
+ if (handle) {
+ /* initialize panic notifier chain */
+ notifier_chain_register(&panic_notifier_list, &pdc_chassis_panic_block);
- /* initialize reboot notifier chain */
- register_reboot_notifier(&pdc_chassis_reboot_block);
+ /* initialize reboot notifier chain */
+ register_reboot_notifier(&pdc_chassis_reboot_block);
+ }
}
#endif /* CONFIG_PDC_CHASSIS */
}
/* Maybe we should do that in an other way ? */
int retval = 0;
#ifdef CONFIG_PDC_CHASSIS
- DPRINTK(KERN_DEBUG "%s: pdc_chassis_send_status(%d)\n", __FILE__, message);
-
-#ifdef __LP64__ /* pdc_pat_chassis_send_log is defined only when #ifdef __LP64__ */
- if (is_pdc_pat()) {
- switch(message) {
- case PDC_CHASSIS_DIRECT_BSTART:
- retval = pdc_pat_chassis_send_log(PDC_CHASSIS_PMSG_BSTART, PDC_CHASSIS_LSTATE_RUN_NORMAL);
- break;
-
- case PDC_CHASSIS_DIRECT_BCOMPLETE:
- retval = pdc_pat_chassis_send_log(PDC_CHASSIS_PMSG_BCOMPLETE, PDC_CHASSIS_LSTATE_RUN_NORMAL);
- break;
-
- case PDC_CHASSIS_DIRECT_SHUTDOWN:
- retval = pdc_pat_chassis_send_log(PDC_CHASSIS_PMSG_SHUTDOWN, PDC_CHASSIS_LSTATE_NONOS);
- break;
-
- case PDC_CHASSIS_DIRECT_PANIC:
- retval = pdc_pat_chassis_send_log(PDC_CHASSIS_PMSG_PANIC, PDC_CHASSIS_LSTATE_RUN_CRASHREC);
- break;
-
- case PDC_CHASSIS_DIRECT_LPMC:
- retval = pdc_pat_chassis_send_log(PDC_CHASSIS_PMSG_LPMC, PDC_CHASSIS_LSTATE_RUN_SYSINT);
- break;
-
- case PDC_CHASSIS_DIRECT_HPMC:
- retval = pdc_pat_chassis_send_log(PDC_CHASSIS_PMSG_HPMC, PDC_CHASSIS_LSTATE_RUN_NCRIT);
- break;
-
- default:
- retval = -1;
- }
- } else retval = -1;
+ if (pdc_chassis_enabled) {
+
+ DPRINTK(KERN_DEBUG "%s: pdc_chassis_send_status(%d)\n", __FILE__, message);
+
+#ifdef CONFIG_PARISC64
+ if (is_pdc_pat()) {
+ switch(message) {
+ case PDC_CHASSIS_DIRECT_BSTART:
+ retval = pdc_pat_chassis_send_log(PDC_CHASSIS_PMSG_BSTART, PDC_CHASSIS_LSTATE_RUN_NORMAL);
+ break;
+
+ case PDC_CHASSIS_DIRECT_BCOMPLETE:
+ retval = pdc_pat_chassis_send_log(PDC_CHASSIS_PMSG_BCOMPLETE, PDC_CHASSIS_LSTATE_RUN_NORMAL);
+ break;
+
+ case PDC_CHASSIS_DIRECT_SHUTDOWN:
+ retval = pdc_pat_chassis_send_log(PDC_CHASSIS_PMSG_SHUTDOWN, PDC_CHASSIS_LSTATE_NONOS);
+ break;
+
+ case PDC_CHASSIS_DIRECT_PANIC:
+ retval = pdc_pat_chassis_send_log(PDC_CHASSIS_PMSG_PANIC, PDC_CHASSIS_LSTATE_RUN_CRASHREC);
+ break;
+
+ case PDC_CHASSIS_DIRECT_LPMC:
+ retval = pdc_pat_chassis_send_log(PDC_CHASSIS_PMSG_LPMC, PDC_CHASSIS_LSTATE_RUN_SYSINT);
+ break;
+
+ case PDC_CHASSIS_DIRECT_HPMC:
+ retval = pdc_pat_chassis_send_log(PDC_CHASSIS_PMSG_HPMC, PDC_CHASSIS_LSTATE_RUN_NCRIT);
+ break;
+
+ default:
+ retval = -1;
+ }
+ } else retval = -1;
#else
- if (pdc_chassis_old) {
- switch (message) {
- case PDC_CHASSIS_DIRECT_BSTART:
- case PDC_CHASSIS_DIRECT_BCOMPLETE:
- retval = pdc_chassis_disp(PDC_CHASSIS_DISP_DATA(OSTAT_RUN));
- break;
-
- case PDC_CHASSIS_DIRECT_SHUTDOWN:
- retval = pdc_chassis_disp(PDC_CHASSIS_DISP_DATA(OSTAT_SHUT));
- break;
-
- case PDC_CHASSIS_DIRECT_HPMC:
- case PDC_CHASSIS_DIRECT_PANIC:
- retval = pdc_chassis_disp(PDC_CHASSIS_DISP_DATA(OSTAT_FLT));
- break;
-
- case PDC_CHASSIS_DIRECT_LPMC:
- retval = pdc_chassis_disp(PDC_CHASSIS_DISP_DATA(OSTAT_WARN));
- break;
-
- default:
- retval = -1;
- }
- } else retval = -1;
-#endif /* __LP64__ */
+ if (pdc_chassis_old) {
+ switch (message) {
+ case PDC_CHASSIS_DIRECT_BSTART:
+ case PDC_CHASSIS_DIRECT_BCOMPLETE:
+ retval = pdc_chassis_disp(PDC_CHASSIS_DISP_DATA(OSTAT_RUN));
+ break;
+
+ case PDC_CHASSIS_DIRECT_SHUTDOWN:
+ retval = pdc_chassis_disp(PDC_CHASSIS_DISP_DATA(OSTAT_SHUT));
+ break;
+
+ case PDC_CHASSIS_DIRECT_HPMC:
+ case PDC_CHASSIS_DIRECT_PANIC:
+ retval = pdc_chassis_disp(PDC_CHASSIS_DISP_DATA(OSTAT_FLT));
+ break;
+
+ case PDC_CHASSIS_DIRECT_LPMC:
+ retval = pdc_chassis_disp(PDC_CHASSIS_DISP_DATA(OSTAT_WARN));
+ break;
+
+ default:
+ retval = -1;
+ }
+ } else retval = -1;
+#endif /* CONFIG_PARISC64 */
+ } /* if (pdc_chassis_enabled) */
#endif /* CONFIG_PDC_CHASSIS */
return retval;
}
* understand what is happening here
*/
-/*
- * J. David Anglin writes:
- *
- * "You have to adjust the current sp to that at the begining of the function.
- * There can be up to two stack additions to allocate the frame in the
- * prologue. Similar things happen in the epilogue. In the presence of
- * interrupts, you have to be concerned about where you are in the function
- * and what stack adjustments have taken place."
- *
- * For now these cases are not handled, but they should be!
- */
-
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/init.h>
#define dbg(x...)
#endif
-extern const struct unwind_table_entry __start___unwind[];
-extern const struct unwind_table_entry __stop___unwind[];
+extern struct unwind_table_entry __start___unwind[];
+extern struct unwind_table_entry __stop___unwind[];
static spinlock_t unwind_lock;
/*
const struct unwind_table_entry *e = 0;
unsigned long lo, hi, mid;
- addr -= table->base_addr;
-
for (lo = 0, hi = table->length; lo < hi; )
{
mid = (lo + hi) / 2;
static void
unwind_table_init(struct unwind_table *table, const char *name,
unsigned long base_addr, unsigned long gp,
- const void *table_start, const void *table_end)
+ void *table_start, void *table_end)
{
- const struct unwind_table_entry *start = table_start;
- const struct unwind_table_entry *end = table_end - 1;
+ struct unwind_table_entry *start = table_start;
+ struct unwind_table_entry *end =
+ (struct unwind_table_entry *)table_end - 1;
table->name = name;
table->base_addr = base_addr;
table->start = base_addr + start->region_start;
table->end = base_addr + end->region_end;
table->table = (struct unwind_table_entry *)table_start;
- table->length = end - start;
+ table->length = end - start + 1;
table->next = NULL;
+
+ for (; start <= end; start++) {
+ start->region_start += base_addr;
+ start->region_end += base_addr;
+ }
}
void *
unwind_table_add(const char *name, unsigned long base_addr,
unsigned long gp,
- const void *start, const void *end)
+ void *start, void *end)
{
struct unwind_table *table;
unsigned long flags;
sp = info->prev_sp;
} while (info->prev_ip < (unsigned long)_stext ||
info->prev_ip > (unsigned long)_etext);
+
+ dbg("analyzing func @ %lx with no unwind info, setting prev_sp=%lx prev_ip=%lx\n", info->ip, info->prev_sp, info->prev_ip);
} else {
dbg("e->start = 0x%x, e->end = 0x%x, Save_SP = %d, Save_RP = %d size = %u\n",
/* ldo X(sp), sp, or stwm X,D(sp) */
frame_size += (insn & 0x1 ? -1 << 13 : 0) |
((insn & 0x3fff) >> 1);
+ dbg("analyzing func @ %lx, insn=%08x @ %lx, frame_size = %ld\n", info->ip, insn, npc, frame_size);
} else if ((insn & 0xffe00008) == 0x7ec00008) {
/* std,ma X,D(sp) */
frame_size += (insn & 0x1 ? -1 << 13 : 0) |
(((insn >> 4) & 0x3ff) << 3);
+ dbg("analyzing func @ %lx, insn=%08x @ %lx, frame_size = %ld\n", info->ip, insn, npc, frame_size);
} else if (insn == 0x6bc23fd9) {
/* stw rp,-20(sp) */
rpoffset = 20;
looking_for_rp = 0;
+ dbg("analyzing func @ %lx, insn=stw rp,-20(sp) @ %lx\n", info->ip, npc);
} else if (insn == 0x0fc212c1) {
/* std rp,-16(sr0,sp) */
rpoffset = 16;
looking_for_rp = 0;
+ dbg("analyzing func @ %lx, insn=std rp,-16(sp) @ %lx\n", info->ip, npc);
}
}
info->prev_sp = info->sp - frame_size;
if (rpoffset)
- info->prev_ip = *(unsigned long *)(info->prev_sp - rpoffset);
+ info->rp = *(unsigned long *)(info->prev_sp - rpoffset);
+ info->prev_ip = info->rp;
+ info->rp = 0;
+
+ dbg("analyzing func @ %lx, setting prev_sp=%lx prev_ip=%lx\n", info->ip, info->prev_sp, info->prev_ip);
}
}
void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t,
- struct pt_regs *regs)
+ unsigned long sp, unsigned long ip, unsigned long rp)
{
memset(info, 0, sizeof(struct unwind_frame_info));
info->t = t;
- info->sp = regs->ksp;
- info->ip = regs->kpc;
+ info->sp = sp;
+ info->ip = ip;
+ info->rp = rp;
- dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n", (int)t->pid, info->sp, info->ip);
+ dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n", t ? (int)t->pid : 0, info->sp, info->ip);
}
void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t)
{
struct pt_regs *regs = &t->thread.regs;
- unwind_frame_init(info, t, regs);
+ unwind_frame_init(info, t, regs->ksp, regs->kpc, 0);
+}
+
+void unwind_frame_init_running(struct unwind_frame_info *info, struct pt_regs *regs)
+{
+ unwind_frame_init(info, current, regs->gr[30], regs->iaoq[0],
+ regs->gr[2]);
}
int unwind_once(struct unwind_frame_info *next_frame)
#
lib-y := lusercopy.o bitops.o checksum.o io.o memset.o
+
+lib-$(CONFIG_SMP) += debuglocks.o
--- /dev/null
+/*
+ * Debugging versions of SMP locking primitives.
+ *
+ * Copyright (C) 2004 Thibaut VARENE <varenet@esiee.fr>
+ *
+ * Some code stollen from alpha & sparc64 ;)
+ *
+ * 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/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <asm/system.h>
+#include <asm/hardirq.h> /* in_interrupt() */
+
+#undef INIT_STUCK
+#define INIT_STUCK 1L << 30
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+
+void _dbg_spin_lock(spinlock_t * lock, const char *base_file, int line_no)
+{
+ volatile unsigned int *a;
+ long stuck = INIT_STUCK;
+ void *inline_pc = __builtin_return_address(0);
+ unsigned long started = jiffies;
+ int printed = 0;
+ int cpu = smp_processor_id();
+
+try_again:
+
+ /* Do the actual locking */
+ /* <T-Bone> ggg: we can't get stuck on the outter loop?
+ * <ggg> T-Bone: We can hit the outer loop
+ * alot if multiple CPUs are constantly racing for a lock
+ * and the backplane is NOT fair about which CPU sees
+ * the update first. But it won't hang since every failed
+ * attempt will drop us back into the inner loop and
+ * decrement `stuck'.
+ * <ggg> K-class and some of the others are NOT fair in the HW
+ * implementation so we could see false positives.
+ * But fixing the lock contention is easier than
+ * fixing the HW to be fair.
+ * <tausq> __ldcw() returns 1 if we get the lock; otherwise we
+ * spin until the value of the lock changes, or we time out.
+ */
+ a = __ldcw_align(lock);
+ while (stuck && (__ldcw(a) == 0))
+ while ((*a == 0) && --stuck);
+
+ if (unlikely(stuck <= 0)) {
+ printk(KERN_WARNING
+ "%s:%d: spin_lock(%s/%p) stuck in %s at %p(%d)"
+ " owned by %s:%d in %s at %p(%d)\n",
+ base_file, line_no, lock->module, lock,
+ current->comm, inline_pc, cpu,
+ lock->bfile, lock->bline, lock->task->comm,
+ lock->previous, lock->oncpu);
+ stuck = INIT_STUCK;
+ printed = 1;
+ goto try_again;
+ }
+
+ /* Exiting. Got the lock. */
+ lock->oncpu = cpu;
+ lock->previous = inline_pc;
+ lock->task = current;
+ lock->bfile = (char *)base_file;
+ lock->bline = line_no;
+
+ if (unlikely(printed)) {
+ printk(KERN_WARNING
+ "%s:%d: spin_lock grabbed in %s at %p(%d) %ld ticks\n",
+ base_file, line_no, current->comm, inline_pc,
+ cpu, jiffies - started);
+ }
+}
+
+void _dbg_spin_unlock(spinlock_t * lock, const char *base_file, int line_no)
+{
+ CHECK_LOCK(lock);
+ volatile unsigned int *a = __ldcw_align(lock);
+ if (unlikely((*a != 0) && lock->babble)) {
+ lock->babble--;
+ printk(KERN_WARNING
+ "%s:%d: spin_unlock(%s:%p) not locked\n",
+ base_file, line_no, lock->module, lock);
+ }
+ *a = 1;
+}
+
+int _dbg_spin_trylock(spinlock_t * lock, const char *base_file, int line_no)
+{
+ int ret;
+ volatile unsigned int *a = __ldcw_align(lock);
+ if ((ret = (__ldcw(a) != 0))) {
+ lock->oncpu = smp_processor_id();
+ lock->previous = __builtin_return_address(0);
+ lock->task = current;
+ } else {
+ lock->bfile = (char *)base_file;
+ lock->bline = line_no;
+ }
+ return ret;
+}
+
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
+#ifdef CONFIG_DEBUG_RWLOCK
+
+/* Interrupts trouble detailed explanation, thx Grant:
+ *
+ * o writer (wants to modify data) attempts to acquire the rwlock
+ * o He gets the write lock.
+ * o Interupts are still enabled, we take an interrupt with the
+ * write still holding the lock.
+ * o interrupt handler tries to acquire the rwlock for read.
+ * o deadlock since the writer can't release it at this point.
+ *
+ * In general, any use of spinlocks that competes between "base"
+ * level and interrupt level code will risk deadlock. Interrupts
+ * need to be disabled in the base level routines to avoid it.
+ * Or more precisely, only the IRQ the base level routine
+ * is competing with for the lock. But it's more efficient/faster
+ * to just disable all interrupts on that CPU to guarantee
+ * once it gets the lock it can release it quickly too.
+ */
+
+void _dbg_write_lock(rwlock_t *rw, const char *bfile, int bline)
+{
+ void *inline_pc = __builtin_return_address(0);
+ unsigned long started = jiffies;
+ long stuck = INIT_STUCK;
+ int printed = 0;
+ int cpu = smp_processor_id();
+
+ if(unlikely(in_interrupt())) { /* acquiring write lock in interrupt context, bad idea */
+ printk(KERN_WARNING "write_lock caller: %s:%d, IRQs enabled,\n", bfile, bline);
+ BUG();
+ }
+
+ /* Note: if interrupts are disabled (which is most likely), the printk
+ will never show on the console. We might need a polling method to flush
+ the dmesg buffer anyhow. */
+
+retry:
+ _raw_spin_lock(&rw->lock);
+
+ if(rw->counter != 0) {
+ /* this basically never happens */
+ _raw_spin_unlock(&rw->lock);
+
+ stuck--;
+ if ((unlikely(stuck <= 0)) && (rw->counter < 0)) {
+ printk(KERN_WARNING
+ "%s:%d: write_lock stuck on writer"
+ " in %s at %p(%d) %ld ticks\n",
+ bfile, bline, current->comm, inline_pc,
+ cpu, jiffies - started);
+ stuck = INIT_STUCK;
+ printed = 1;
+ }
+ else if (unlikely(stuck <= 0)) {
+ printk(KERN_WARNING
+ "%s:%d: write_lock stuck on reader"
+ " in %s at %p(%d) %ld ticks\n",
+ bfile, bline, current->comm, inline_pc,
+ cpu, jiffies - started);
+ stuck = INIT_STUCK;
+ printed = 1;
+ }
+
+ while(rw->counter != 0);
+
+ goto retry;
+ }
+
+ /* got it. now leave without unlocking */
+ rw->counter = -1; /* remember we are locked */
+
+ if (unlikely(printed)) {
+ printk(KERN_WARNING
+ "%s:%d: write_lock grabbed in %s at %p(%d) %ld ticks\n",
+ bfile, bline, current->comm, inline_pc,
+ cpu, jiffies - started);
+ }
+}
+
+void _dbg_read_lock(rwlock_t * rw, const char *bfile, int bline)
+{
+#if 0
+ void *inline_pc = __builtin_return_address(0);
+ unsigned long started = jiffies;
+ int cpu = smp_processor_id();
+#endif
+ unsigned long flags;
+
+ local_irq_save(flags);
+ _raw_spin_lock(&rw->lock);
+
+ rw->counter++;
+#if 0
+ printk(KERN_WARNING
+ "%s:%d: read_lock grabbed in %s at %p(%d) %ld ticks\n",
+ bfile, bline, current->comm, inline_pc,
+ cpu, jiffies - started);
+#endif
+ _raw_spin_unlock(&rw->lock);
+ local_irq_restore(flags);
+}
+
+#endif /* CONFIG_DEBUG_RWLOCK */
goto bytecopy;
while (dest & 3) {
writeb(*(char *)src, dest++);
- ((char *)src)++;
+ src++;
count--;
}
while (count > 3) {
bytecopy:
while (count--) {
writeb(*(char *)src, dest++);
- ((char *)src)++;
+ src++;
}
}
/* Then check for misaligned start address */
if (src & 1) {
*(u8 *)dest = readb(src);
- ((u8 *)src)++;
- ((u8 *)dest)++;
+ src++;
+ dest++;
count--;
if (count < 2) goto bytecopy;
}
if (src & 2) {
*(u16 *)dest = __raw_readw(src);
- ((u16 *)src)++;
- ((u16 *)dest)++;
- count-=2;
+ src += 2;
+ dest += 2;
+ count -= 2;
}
while (count > 3) {
shortcopy:
while (count > 1) {
*(u16 *)dest = __raw_readw(src);
- ((u16 *)src)++;
- ((u16 *)dest)++;
- count-=2;
+ src += 2;
+ dest += 2;
+ count -= 2;
}
bytecopy:
while (count--) {
*(char *)dest = readb(src);
- ((char *)src)++;
- ((char *)dest)++;
+ src++;
+ dest++;
}
}
*/
void insb (unsigned long port, void *dst, unsigned long count)
{
- while (((unsigned long)dst) & 0x3) {
+ unsigned char *p;
+
+ p = (unsigned char *)dst;
+
+ while (((unsigned long)p) & 0x3) {
if (!count)
return;
count--;
- *(unsigned char *) dst = inb(port);
- ((unsigned char *) dst)++;
+ *p = inb(port);
+ p++;
}
while (count >= 4) {
w |= inb(port) << 16;
w |= inb(port) << 8;
w |= inb(port);
- *(unsigned int *) dst = w;
- ((unsigned int *) dst)++;
+ *(unsigned int *) p = w;
+ p += 4;
}
while (count) {
--count;
- *(unsigned char *) dst = inb(port);
- ((unsigned char *) dst)++;
+ *p = inb(port);
+ p++;
}
}
void insw (unsigned long port, void *dst, unsigned long count)
{
unsigned int l = 0, l2;
+ unsigned char *p;
+
+ p = (unsigned char *)dst;
if (!count)
return;
- switch (((unsigned long) dst) & 0x3)
+ switch (((unsigned long)p) & 0x3)
{
case 0x00: /* Buffer 32-bit aligned */
while (count>=2) {
count -= 2;
l = cpu_to_le16(inw(port)) << 16;
l |= cpu_to_le16(inw(port));
- *(unsigned int *) dst = l;
- ((unsigned int *) dst)++;
+ *(unsigned int *)p = l;
+ p += 4;
}
if (count) {
- *(unsigned short *) dst = cpu_to_le16(inw(port));
+ *(unsigned short *)p = cpu_to_le16(inw(port));
}
break;
case 0x02: /* Buffer 16-bit aligned */
- *(unsigned short *) dst = cpu_to_le16(inw(port));
- ((unsigned short *) dst)++;
+ *(unsigned short *)p = cpu_to_le16(inw(port));
+ p += 2;
count--;
while (count>=2) {
count -= 2;
l = cpu_to_le16(inw(port)) << 16;
l |= cpu_to_le16(inw(port));
- *(unsigned int *) dst = l;
- ((unsigned int *) dst)++;
+ *(unsigned int *)p = l;
+ p += 4;
}
if (count) {
- *(unsigned short *) dst = cpu_to_le16(inw(port));
+ *(unsigned short *)p = cpu_to_le16(inw(port));
}
break;
--count;
l = cpu_to_le16(inw(port));
- *(unsigned char *) dst = l >> 8;
- ((unsigned char *) dst)++;
+ *p = l >> 8;
+ p++;
while (count--)
{
l2 = cpu_to_le16(inw(port));
- *(unsigned short *) dst = (l & 0xff) << 8 | (l2 >> 8);
- ((unsigned short *) dst)++;
+ *(unsigned short *)p = (l & 0xff) << 8 | (l2 >> 8);
+ p += 2;
l = l2;
}
- *(unsigned char *) dst = l & 0xff;
+ *p = l & 0xff;
break;
}
}
void insl (unsigned long port, void *dst, unsigned long count)
{
unsigned int l = 0, l2;
+ unsigned char *p;
+
+ p = (unsigned char *)dst;
if (!count)
return;
case 0x00: /* Buffer 32-bit aligned */
while (count--)
{
- *(unsigned int *) dst = cpu_to_le32(inl(port));
- ((unsigned int *) dst)++;
+ *(unsigned int *)p = cpu_to_le32(inl(port));
+ p += 4;
}
break;
--count;
l = cpu_to_le32(inl(port));
- *(unsigned short *) dst = l >> 16;
- ((unsigned short *) dst)++;
+ *(unsigned short *)p = l >> 16;
+ p += 2;
while (count--)
{
l2 = cpu_to_le32(inl(port));
- *(unsigned int *) dst = (l & 0xffff) << 16 | (l2 >> 16);
- ((unsigned int *) dst)++;
+ *(unsigned int *)p = (l & 0xffff) << 16 | (l2 >> 16);
+ p += 4;
l = l2;
}
- *(unsigned short *) dst = l & 0xffff;
+ *(unsigned short *)p = l & 0xffff;
break;
case 0x01: /* Buffer 8-bit aligned */
--count;
l = cpu_to_le32(inl(port));
- *(unsigned char *) dst = l >> 24;
- ((unsigned char *) dst)++;
- *(unsigned short *) dst = (l >> 8) & 0xffff;
- ((unsigned short *) dst)++;
+ *(unsigned char *)p = l >> 24;
+ p++;
+ *(unsigned short *)p = (l >> 8) & 0xffff;
+ p += 2;
while (count--)
{
l2 = cpu_to_le32(inl(port));
- *(unsigned int *) dst = (l & 0xff) << 24 | (l2 >> 8);
- ((unsigned int *) dst)++;
+ *(unsigned int *)p = (l & 0xff) << 24 | (l2 >> 8);
+ p += 4;
l = l2;
}
- *(unsigned char *) dst = l & 0xff;
+ *p = l & 0xff;
break;
case 0x03: /* Buffer 8-bit aligned */
--count;
l = cpu_to_le32(inl(port));
- *(unsigned char *) dst = l >> 24;
- ((unsigned char *) dst)++;
+ *p = l >> 24;
+ p++;
while (count--)
{
l2 = cpu_to_le32(inl(port));
- *(unsigned int *) dst = (l & 0xffffff) << 8 | l2 >> 24;
- ((unsigned int *) dst)++;
+ *(unsigned int *)p = (l & 0xffffff) << 8 | l2 >> 24;
+ p += 4;
l = l2;
}
- *(unsigned short *) dst = (l >> 8) & 0xffff;
- ((unsigned short *) dst)++;
- *(unsigned char *) dst = l & 0xff;
+ *(unsigned short *)p = (l >> 8) & 0xffff;
+ p += 2;
+ *p = l & 0xff;
break;
}
}
*/
void outsb(unsigned long port, const void * src, unsigned long count)
{
+ const unsigned char *p;
+
+ p = (const unsigned char *)src;
while (count) {
count--;
- outb(*(char *)src, port);
- ((char *) src)++;
+ outb(*p, port);
+ p++;
}
}
void outsw (unsigned long port, const void *src, unsigned long count)
{
unsigned int l = 0, l2;
+ const unsigned char *p;
+
+ p = (const unsigned char *)src;
if (!count)
return;
- switch (((unsigned long) src) & 0x3)
+ switch (((unsigned long)p) & 0x3)
{
case 0x00: /* Buffer 32-bit aligned */
while (count>=2) {
count -= 2;
- l = *(unsigned int *) src;
- ((unsigned int *) src)++;
+ l = *(unsigned int *)p;
+ p += 4;
outw(le16_to_cpu(l >> 16), port);
outw(le16_to_cpu(l & 0xffff), port);
}
if (count) {
- outw(le16_to_cpu(*(unsigned short*)src), port);
+ outw(le16_to_cpu(*(unsigned short*)p), port);
}
break;
case 0x02: /* Buffer 16-bit aligned */
- outw(le16_to_cpu(*(unsigned short*)src), port);
- ((unsigned short *) src)++;
+ outw(le16_to_cpu(*(unsigned short*)p), port);
+ p += 2;
count--;
while (count>=2) {
count -= 2;
- l = *(unsigned int *) src;
- ((unsigned int *) src)++;
+ l = *(unsigned int *)p;
+ p += 4;
outw(le16_to_cpu(l >> 16), port);
outw(le16_to_cpu(l & 0xffff), port);
}
if (count) {
- outw(le16_to_cpu(*(unsigned short*)src), port);
+ outw(le16_to_cpu(*(unsigned short *)p), port);
}
break;
/* I don't bother with 32bit transfers
* in this case, 16bit will have to do -- DE */
- l = *(unsigned char *) src << 8;
- ((unsigned char *) src)++;
+ l = *p << 8;
+ p++;
count--;
while (count)
{
count--;
- l2 = *(unsigned short *) src;
- ((unsigned short *) src)++;
+ l2 = *(unsigned short *)p;
+ p += 2;
outw(le16_to_cpu(l | l2 >> 8), port);
l = l2 << 8;
}
- l2 = *(unsigned char *) src;
+ l2 = *(unsigned char *)p;
outw (le16_to_cpu(l | l2>>8), port);
break;
void outsl (unsigned long port, const void *src, unsigned long count)
{
unsigned int l = 0, l2;
+ const unsigned char *p;
+
+ p = (const unsigned char *)src;
if (!count)
return;
- switch (((unsigned long) src) & 0x3)
+ switch (((unsigned long)p) & 0x3)
{
case 0x00: /* Buffer 32-bit aligned */
while (count--)
{
- outl(le32_to_cpu(*(unsigned int *) src), port);
- ((unsigned int *) src)++;
+ outl(le32_to_cpu(*(unsigned int *)p), port);
+ p += 4;
}
break;
case 0x02: /* Buffer 16-bit aligned */
--count;
- l = *(unsigned short *) src;
- ((unsigned short *) src)++;
+ l = *(unsigned short *)p;
+ p += 2;
while (count--)
{
- l2 = *(unsigned int *) src;
- ((unsigned int *) src)++;
+ l2 = *(unsigned int *)p;
+ p += 4;
outl (le32_to_cpu(l << 16 | l2 >> 16), port);
l = l2;
}
- l2 = *(unsigned short *) src;
+ l2 = *(unsigned short *)p;
outl (le32_to_cpu(l << 16 | l2), port);
break;
case 0x01: /* Buffer 8-bit aligned */
--count;
-
- l = *(unsigned char *) src << 24;
- ((unsigned char *) src)++;
- l |= *(unsigned short *) src << 8;
- ((unsigned short *) src)++;
+
+ l = *p << 24;
+ p++;
+ l |= *(unsigned short *)p << 8;
+ p += 2;
+
while (count--)
{
- l2 = *(unsigned int *) src;
- ((unsigned int *) src)++;
+ l2 = *(unsigned int *)p;
+ p += 4;
outl (le32_to_cpu(l | l2 >> 24), port);
l = l2 << 8;
}
- l2 = *(unsigned char *) src;
- outl (le32_to_cpu(l | l2), port);
+ l2 = *p;
+ outl (le32_to_cpu(l | l2), port);
break;
case 0x03: /* Buffer 8-bit aligned */
--count;
- l = *(unsigned char *) src << 24;
- ((unsigned char *) src)++;
+ l = *p << 24;
+ p++;
+
while (count--)
{
- l2 = *(unsigned int *) src;
- ((unsigned int *) src)++;
+ l2 = *(unsigned int *)p;
+ p += 4;
outl (le32_to_cpu(l | l2 >> 8), port);
l = l2 << 24;
}
- l2 = *(unsigned short *) src << 16;
- ((unsigned short *) src)++;
- l2 |= *(unsigned char *) src;
+ l2 = *(unsigned short *)p << 16;
+ p += 2;
+ l2 |= *p;
outl (le32_to_cpu(l | l2), port);
break;
}
* initialize the serial console port.
*/
embed_config(&bp);
-#if defined(CONFIG_SERIAL_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE)
+#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE)
com_port = serial_init(0, bp);
#endif
rec = (struct bi_record *)((unsigned long)rec + rec->size);
}
puts("Now booting the kernel\n");
+#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE)
serial_close(com_port);
+#endif
return (unsigned long)hold_residual;
}
--- /dev/null
+/*
+ * arch/ppc/boot/simple/mpc52xx_tty.c
+ *
+ * Minimal serial functions needed to send messages out a MPC52xx
+ * Programmable Serial Controller (PSC).
+ *
+ * Author: Dale Farnsworth <dfarnsworth@mvista.com>
+ *
+ * 2003-2004 (c) MontaVista, Software, Inc. 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/config.h>
+#include <linux/types.h>
+#include <asm/uaccess.h>
+#include <asm/mpc52xx.h>
+#include <asm/mpc52xx_psc.h>
+#include <asm/serial.h>
+#include <asm/time.h>
+
+#if MPC52xx_PF_CONSOLE_PORT == 0
+#define MPC52xx_CONSOLE MPC52xx_PSC1
+#define MPC52xx_PSC_CONFIG_SHIFT 0
+#elif MPC52xx_PF_CONSOLE_PORT == 1
+#define MPC52xx_CONSOLE MPC52xx_PSC2
+#define MPC52xx_PSC_CONFIG_SHIFT 4
+#elif MPC52xx_PF_CONSOLE_PORT == 2
+#define MPC52xx_CONSOLE MPC52xx_PSC3
+#define MPC52xx_PSC_CONFIG_SHIFT 8
+#else
+#error "MPC52xx_PF_CONSOLE_PORT not defined"
+#endif
+
+static struct mpc52xx_psc *psc = (struct mpc52xx_psc *)MPC52xx_CONSOLE;
+
+/* The decrementer counts at the system bus clock frequency
+ * divided by four. The most accurate time base is connected to the
+ * rtc. We read the decrementer change during one rtc tick (one second)
+ * and multiply by 4 to get the system bus clock frequency.
+ */
+int
+mpc52xx_ipbfreq(void)
+{
+ struct mpc52xx_rtc *rtc = (struct mpc52xx_rtc*)MPC52xx_RTC;
+ struct mpc52xx_cdm *cdm = (struct mpc52xx_cdm*)MPC52xx_CDM;
+ int current_time, previous_time;
+ int tbl_start, tbl_end;
+ int xlbfreq, ipbfreq;
+
+ out_be32(&rtc->dividers, 0x8f1f0000); /* Set RTC 64x faster */
+ previous_time = in_be32(&rtc->time);
+ while ((current_time = in_be32(&rtc->time)) == previous_time) ;
+ tbl_start = get_tbl();
+ previous_time = current_time;
+ while ((current_time = in_be32(&rtc->time)) == previous_time) ;
+ tbl_end = get_tbl();
+ out_be32(&rtc->dividers, 0xffff0000); /* Restore RTC */
+
+ xlbfreq = (tbl_end - tbl_start) << 8;
+ ipbfreq = (in_8(&cdm->ipb_clk_sel) & 1) ? xlbfreq / 2 : xlbfreq;
+
+ return ipbfreq;
+}
+
+unsigned long
+serial_init(int ignored, void *ignored2)
+{
+ struct mpc52xx_gpio *gpio = (struct mpc52xx_gpio *)MPC52xx_GPIO;
+ int divisor;
+ int mode1;
+ int mode2;
+ u32 val32;
+
+ static int been_here = 0;
+
+ if (been_here)
+ return 0;
+
+ been_here = 1;
+
+ val32 = in_be32(&gpio->port_config);
+ val32 &= ~(0x7 << MPC52xx_PSC_CONFIG_SHIFT);
+ val32 |= MPC52xx_GPIO_PSC_CONFIG_UART_WITHOUT_CD
+ << MPC52xx_PSC_CONFIG_SHIFT;
+ out_be32(&gpio->port_config, val32);
+
+ out_8(&psc->command, MPC52xx_PSC_RST_TX
+ | MPC52xx_PSC_RX_DISABLE | MPC52xx_PSC_TX_ENABLE);
+ out_8(&psc->command, MPC52xx_PSC_RST_RX);
+
+ out_be32(&psc->sicr, 0x0);
+ out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00);
+ out_be16(&psc->tfalarm, 0xf8);
+
+ out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1
+ | MPC52xx_PSC_RX_ENABLE
+ | MPC52xx_PSC_TX_ENABLE);
+
+ divisor = ((mpc52xx_ipbfreq()
+ / (CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD * 16)) + 1) >> 1;
+
+ mode1 = MPC52xx_PSC_MODE_8_BITS | MPC52xx_PSC_MODE_PARNONE
+ | MPC52xx_PSC_MODE_ERR;
+ mode2 = MPC52xx_PSC_MODE_ONE_STOP;
+
+ out_8(&psc->ctur, divisor>>8);
+ out_8(&psc->ctlr, divisor);
+ out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1);
+ out_8(&psc->mode, mode1);
+ out_8(&psc->mode, mode2);
+
+ return 0; /* ignored */
+}
+
+void
+serial_putc(void *ignored, const char c)
+{
+ serial_init(0, 0);
+
+ while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP)) ;
+ out_8(&psc->mpc52xx_psc_buffer_8, c);
+ while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP)) ;
+}
+
+char
+serial_getc(void *ignored)
+{
+ while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_RXRDY)) ;
+
+ return in_8(&psc->mpc52xx_psc_buffer_8);
+}
+
+int
+serial_tstc(void *ignored)
+{
+ return (in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_RXRDY) != 0;
+}
#include <sys/stat.h>
#include <unistd.h>
#include <netinet/in.h>
+#include <stdint.h>
/* This gets tacked on the front of the image. There are also a few
* bytes allocated after the _start label used by the boot rom (see
* head.S for details).
*/
typedef struct boot_block {
- unsigned long bb_magic; /* 0x0052504F */
- unsigned long bb_dest; /* Target address of the image */
- unsigned long bb_num_512blocks; /* Size, rounded-up, in 512 byte blks */
- unsigned long bb_debug_flag; /* Run debugger or image after load */
- unsigned long bb_entry_point; /* The image address to start */
- unsigned long bb_checksum; /* 32 bit checksum including header */
- unsigned long reserved[2];
+ uint32_t bb_magic; /* 0x0052504F */
+ uint32_t bb_dest; /* Target address of the image */
+ uint32_t bb_num_512blocks; /* Size, rounded-up, in 512 byte blks */
+ uint32_t bb_debug_flag; /* Run debugger or image after load */
+ uint32_t bb_entry_point; /* The image address to start */
+ uint32_t bb_checksum; /* 32 bit checksum including header */
+ uint32_t reserved[2];
} boot_block_t;
#define IMGBLK 512
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
/* All of the bits we have to set.....
*/
- ori r11,r11,HID0_SGE | HID0_FOLD | HID0_BHTE | HID0_BTIC | HID0_LRSTK
+ ori r11,r11,HID0_SGE | HID0_FOLD | HID0_BHTE | HID0_LRSTK
+BEGIN_FTR_SECTION
+ ori r11,r11,HID0_BTIC
+END_FTR_SECTION_IFCLR(CPU_FTR_NO_BTIC)
BEGIN_FTR_SECTION
oris r11,r11,HID0_DPM@h /* enable dynamic power mgmt */
END_FTR_SECTION_IFCLR(CPU_FTR_NO_DPM)
no_page:
return NULL;
}
+EXPORT_SYMBOL(__dma_alloc_coherent);
/*
* free a page as defined by the above mapping.
__func__, vaddr);
dump_stack();
}
-EXPORT_SYMBOL(dma_free_coherent);
+EXPORT_SYMBOL(__dma_free_coherent);
/*
* Initialise the consistent memory allocation.
evmwumiaa evr6, evr6, evr6 /* evr6 <- ACC = 0 * 0 + ACC */
li r4,THREAD_ACC
evstddx evr6, r4, r3 /* save off accumulator */
+ mfspr r6,SPRN_SPEFSCR
+ stw r6,THREAD_SPEFSCR(r3) /* save spefscr register value */
beq 1f
lwz r4,_MSR-STACK_FRAME_OVERHEAD(r5)
lis r3,MSR_SPE@h
struct pci_dev* dev;
unsigned int *class_code, *reg;
- class_code = (unsigned int *) get_property(node, "class-code", 0);
+ class_code = (unsigned int *) get_property(node, "class-code", NULL);
if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI &&
(*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS))
continue;
- reg = (unsigned int *)get_property(node, "reg", 0);
+ reg = (unsigned int *)get_property(node, "reg", NULL);
if (!reg)
continue;
dev = pci_find_slot(pci_bus, ((reg[0] >> 8) & 0xff));
continue;
make_one_node_map(node, hose->first_busno);
}
- of_prop_map = get_property(find_path_device("/"), "pci-OF-bus-map", 0);
+ of_prop_map = get_property(find_path_device("/"), "pci-OF-bus-map", NULL);
if (of_prop_map)
memcpy(of_prop_map, pci_to_OF_bus_map, pci_bus_count);
#ifdef DEBUG
* a fake root for all functions of a multi-function device,
* we go down them as well.
*/
- class_code = (unsigned int *) get_property(node, "class-code", 0);
+ class_code = (unsigned int *) get_property(node, "class-code", NULL);
if ((!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI &&
(*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) &&
strcmp(node->name, "multifunc-device"))
unsigned int *reg;
u8* fdata = (u8*)data;
- reg = (unsigned int *) get_property(node, "reg", 0);
+ reg = (unsigned int *) get_property(node, "reg", NULL);
if (reg && ((reg[0] >> 8) & 0xff) == fdata[1]
&& ((reg[0] >> 16) & 0xff) == fdata[0])
return 1;
if (!scan_OF_pci_childs(((struct device_node*)hose->arch_data)->child,
find_OF_pci_device_filter, (void *)node))
return -ENODEV;
- reg = (unsigned int *) get_property(node, "reg", 0);
+ reg = (unsigned int *) get_property(node, "reg", NULL);
if (!reg)
return -ENODEV;
*bus = (reg[0] >> 16) & 0xff;
#include <linux/ctype.h>
#include <linux/threads.h>
#include <linux/smp_lock.h>
+#include <linux/seq_file.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <asm/system.h>
#include <asm/reg.h>
-static ssize_t ppc_htab_read(struct file * file, char __user * buf,
- size_t count, loff_t *ppos);
+static int ppc_htab_show(struct seq_file *m, void *v);
static ssize_t ppc_htab_write(struct file * file, const char __user * buffer,
size_t count, loff_t *ppos);
-static long long ppc_htab_lseek(struct file * file, loff_t offset, int orig);
int proc_dol2crvec(ctl_table *table, int write, struct file *filp,
- void __user *buffer, size_t *lenp);
+ void __user *buffer, size_t *lenp, loff_t *ppos);
extern PTE *Hash, *Hash_end;
extern unsigned long Hash_size, Hash_mask;
extern unsigned int primary_pteg_full;
extern unsigned int htab_hash_searches;
+static int ppc_htab_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ppc_htab_show, NULL);
+}
+
struct file_operations ppc_htab_operations = {
- .llseek = ppc_htab_lseek,
- .read = ppc_htab_read,
- .write = ppc_htab_write,
+ .open = ppc_htab_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = ppc_htab_write,
+ .release = single_release,
};
static char *pmc1_lookup(unsigned long mmcr0)
* is _REALLY_ slow (see the nested for loops below) but nothing
* in here should be really timing critical. -- Cort
*/
-static ssize_t ppc_htab_read(struct file * file, char __user * buf,
- size_t count, loff_t *ppos)
+static int ppc_htab_show(struct seq_file *m, void *v)
{
unsigned long mmcr0 = 0, pmc1 = 0, pmc2 = 0;
- int n = 0;
#if defined(CONFIG_PPC_STD_MMU) && !defined(CONFIG_PPC64BRIDGE)
unsigned int kptes = 0, uptes = 0;
PTE *ptr;
#endif /* CONFIG_PPC_STD_MMU */
- char buffer[512];
-
- if (count < 0)
- return -EINVAL;
if (cur_cpu_spec[0]->cpu_features & CPU_FTR_604_PERF_MON) {
mmcr0 = mfspr(SPRN_MMCR0);
pmc1 = mfspr(SPRN_PMC1);
pmc2 = mfspr(SPRN_PMC2);
- n += sprintf( buffer + n,
+ seq_printf(m,
"604 Performance Monitoring\n"
"MMCR0\t\t: %08lx %s%s ",
mmcr0,
( mmcr0>>28 & 0x2 ) ? "(user mode counted)" : "",
( mmcr0>>28 & 0x4 ) ? "(kernel mode counted)" : "");
- n += sprintf( buffer + n,
+ seq_printf(m,
"\nPMC1\t\t: %08lx (%s)\n"
"PMC2\t\t: %08lx (%s)\n",
pmc1, pmc1_lookup(mmcr0),
#ifdef CONFIG_PPC_STD_MMU
/* if we don't have a htab */
- if ( Hash_size == 0 )
- {
- n += sprintf( buffer + n, "No Hash Table used\n");
- goto return_string;
+ if ( Hash_size == 0 ) {
+ seq_printf(m, "No Hash Table used\n");
+ return 0;
}
#ifndef CONFIG_PPC64BRIDGE
}
#endif
- n += sprintf( buffer + n,
+ seq_printf(m,
"PTE Hash Table Information\n"
"Size\t\t: %luKb\n"
"Buckets\t\t: %lu\n"
#endif
);
- n += sprintf( buffer + n,
+ seq_printf(m,
"Reloads\t\t: %lu\n"
"Preloads\t: %lu\n"
"Searches\t: %u\n"
"Evicts\t\t: %lu\n",
htab_reloads, htab_preloads, htab_hash_searches,
primary_pteg_full, htab_evicts);
-return_string:
#endif /* CONFIG_PPC_STD_MMU */
- n += sprintf( buffer + n,
+ seq_printf(m,
"Non-error misses: %lu\n"
"Error misses\t: %lu\n",
pte_misses, pte_errors);
- if (*ppos >= strlen(buffer))
- return 0;
- if (n > strlen(buffer) - *ppos)
- n = strlen(buffer) - *ppos;
- if (n > count)
- n = count;
- if (copy_to_user(buf, buffer + *ppos, n))
- return -EFAULT;
- *ppos += n;
- return n;
+ return 0;
}
/*
unsigned long tmp;
char buffer[16];
- if ( current->uid != 0 )
+ if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (strncpy_from_user(buffer, ubuffer, 15))
return -EFAULT;
#endif /* CONFIG_PPC_STD_MMU */
}
-
-static long long
-ppc_htab_lseek(struct file * file, loff_t offset, int orig)
-{
- long long ret = -EINVAL;
-
- lock_kernel();
- switch (orig) {
- case 0:
- file->f_pos = offset;
- ret = file->f_pos;
- break;
- case 1:
- file->f_pos += offset;
- ret = file->f_pos;
- }
- unlock_kernel();
- return ret;
-}
-
int proc_dol2crvec(ctl_table *table, int write, struct file *filp,
- void __user *buffer_arg, size_t *lenp)
+ void __user *buffer_arg, size_t *lenp, loff_t *ppos)
{
int vleft, first=1, len, left, val;
char __user *buffer = (char __user *) buffer_arg;
if (!(cur_cpu_spec[0]->cpu_features & CPU_FTR_L2CR))
return -EFAULT;
- if ( /*!table->maxlen ||*/ (filp->f_pos && !write)) {
+ if ( /*!table->maxlen ||*/ (*ppos && !write)) {
*lenp = 0;
return 0;
}
}
if (!write && !first && left) {
- if(put_user('\n', (char *) buffer))
+ if(put_user('\n', (char __user *) buffer))
return -EFAULT;
left--, buffer++;
}
if (write) {
- p = (char *) buffer;
+ char __user *s = (char __user *) buffer;
while (left) {
char c;
- if(get_user(c, p++))
+ if(get_user(c, s++))
return -EFAULT;
if (!isspace(c))
break;
if (write && first)
return -EINVAL;
*lenp -= left;
- filp->f_pos += *lenp;
+ *ppos += *lenp;
return 0;
}
return (x + half) & ~(0x7fffff >> exp);
}
-int
-emulate_altivec(struct pt_regs *regs)
+int emulate_altivec(struct pt_regs *regs)
{
unsigned int instr, i;
unsigned int va, vb, vc, vd;
vector128 *vrs;
- if (get_user(instr, (unsigned int *) regs->nip))
+ if (get_user(instr, (unsigned int __user *) regs->nip))
return -EFAULT;
if ((instr >> 26) != 4)
return -EINVAL; /* not an altivec instruction */
/* Alignment must be a power of two */
if ((alignment & (alignment - 1)) != 0)
- return NULL;
+ return ERR_PTR(-EINVAL);
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL)
- return NULL;
+ return ERR_PTR(-ENOMEM);
info->alignment = alignment;
/* Validate size */
if (size <= 0)
- return NULL;
+ return ERR_PTR(-EINVAL);
/* The region must be aligned */
s = (unsigned long)start;
e = e & ~m;
if (assure_empty(info, 1) < 0)
- return NULL;
+ return ERR_PTR(-ENOMEM);
blk = NULL;
list_for_each(l, &info->free_list) {
}
if (blk == NULL)
- return NULL;
+ return ERR_PTR(-ENOMEM);
/* Perfect fit */
if (bs == s && be == e) {
/* Validate size */
if (size <= 0)
- return NULL;
+ return ERR_PTR(-EINVAL);
/* Align to configured alignment */
size = (size + (info->alignment - 1)) & ~(info->alignment - 1);
if (assure_empty(info, 1) < 0)
- return NULL;
+ return ERR_PTR(-ENOMEM);
blk = NULL;
list_for_each(l, &info->free_list) {
}
if (blk == NULL)
- return NULL;
+ return ERR_PTR(-ENOMEM);
/* Just fits */
if (blk->size == size) {
/* Validate size */
if (size <= 0)
- return NULL;
+ return ERR_PTR(-EINVAL);
/* The region must be aligned */
s = (unsigned long)start;
e = e & ~m;
if (assure_empty(info, 2) < 0)
- return NULL;
+ return ERR_PTR(-ENOMEM);
blk = NULL;
list_for_each(l, &info->free_list) {
}
if (blk == NULL)
- return NULL;
+ return ERR_PTR(-ENOMEM);
/* Perfect fit */
if (bs == s && be == e) {
}
/*
- * Configure PPC44x TLB for AS0 exception processing.
+ * MMU_init_hw does the chip-specific initialization of the MMU hardware.
*/
-static void __init
-ppc44x_tlb_config(void)
+void __init MMU_init_hw(void)
+{
+ flush_instruction_cache();
+}
+
+unsigned long __init mmu_mapin_ram(void)
{
unsigned int pinned_tlbs = 1;
int i;
unsigned int phys_addr = (PPC44x_LOW_SLOT-i) * PPC44x_PIN_SIZE;
ppc44x_pin_tlb(i, phys_addr+PAGE_OFFSET, phys_addr);
}
-}
-
-/*
- * MMU_init_hw does the chip-specific initialization of the MMU hardware.
- */
-void __init MMU_init_hw(void)
-{
- flush_instruction_cache();
-
- ppc44x_tlb_config();
-}
-
-/* TODO: Add large page lowmem mapping support */
-unsigned long __init mmu_mapin_ram(void)
-{
- unsigned long v, s, f = _PAGE_GUARDED;
- phys_addr_t p;
-
- v = KERNELBASE;
- p = PPC_MEMSTART;
-
- for (s = 0; s < total_lowmem; s += PAGE_SIZE) {
- if ((char *) v >= _stext && (char *) v < etext)
- f |= _PAGE_RAM_TEXT;
- else
- f |= _PAGE_RAM;
- map_page(v, p, f);
- v += PAGE_SIZE;
- p += PAGE_SIZE;
- }
-
- if (ppc_md.progress)
- ppc_md.progress("MMU:mmu_mapin_ram done", 0x401);
- return s;
+ return total_lowmem;
}
--- /dev/null
+/*
+ * Modifications by Kumar Gala (kumar.gala@freescale.com) to support
+ * E500 Book E processors.
+ *
+ * Copyright 2004 Freescale Semiconductor, Inc
+ *
+ * This file contains the routines for initializing the MMU
+ * on the 4xx series of chips.
+ * -- paulus
+ *
+ * Derived from arch/ppc/mm/init.c:
+ * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
+ *
+ * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
+ * and Cort Dougan (PReP) (cort@cs.nmt.edu)
+ * Copyright (C) 1996 Paul Mackerras
+ * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
+ *
+ * Derived from "arch/i386/mm/init.c"
+ * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
+ *
+ * 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/config.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/stddef.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/bootmem.h>
+#include <linux/highmem.h>
+
+#include <asm/pgalloc.h>
+#include <asm/prom.h>
+#include <asm/io.h>
+#include <asm/mmu_context.h>
+#include <asm/pgtable.h>
+#include <asm/mmu.h>
+#include <asm/uaccess.h>
+#include <asm/smp.h>
+#include <asm/bootx.h>
+#include <asm/machdep.h>
+#include <asm/setup.h>
+
+extern void loadcam_entry(unsigned int index);
+unsigned int tlbcam_index;
+unsigned int num_tlbcam_entries;
+static unsigned long __cam0, __cam1, __cam2;
+extern unsigned long total_lowmem;
+extern unsigned long __max_low_memory;
+#define MAX_LOW_MEM CONFIG_LOWMEM_SIZE
+
+struct tlbcam {
+ u32 MAS0;
+ u32 MAS1;
+ u32 MAS2;
+ u32 MAS3;
+ u32 MAS7;
+} TLBCAM[NUM_TLBCAMS];
+
+struct tlbcamrange {
+ unsigned long start;
+ unsigned long limit;
+ phys_addr_t phys;
+} tlbcam_addrs[NUM_TLBCAMS];
+
+extern unsigned int tlbcam_index;
+
+/*
+ * Return PA for this VA if it is mapped by a CAM, or 0
+ */
+unsigned long v_mapped_by_tlbcam(unsigned long va)
+{
+ int b;
+ for (b = 0; b < tlbcam_index; ++b)
+ if (va >= tlbcam_addrs[b].start && va < tlbcam_addrs[b].limit)
+ return tlbcam_addrs[b].phys + (va - tlbcam_addrs[b].start);
+ return 0;
+}
+
+/*
+ * Return VA for a given PA or 0 if not mapped
+ */
+unsigned long p_mapped_by_tlbcam(unsigned long pa)
+{
+ int b;
+ for (b = 0; b < tlbcam_index; ++b)
+ if (pa >= tlbcam_addrs[b].phys
+ && pa < (tlbcam_addrs[b].limit-tlbcam_addrs[b].start)
+ +tlbcam_addrs[b].phys)
+ return tlbcam_addrs[b].start+(pa-tlbcam_addrs[b].phys);
+ return 0;
+}
+
+/*
+ * Set up one of the I/D BAT (block address translation) register pairs.
+ * The parameters are not checked; in particular size must be a power
+ * of 4 between 4k and 256M.
+ */
+void settlbcam(int index, unsigned long virt, phys_addr_t phys,
+ unsigned int size, int flags, unsigned int pid)
+{
+ unsigned int tsize, lz;
+
+ asm ("cntlzw %0,%1" : "=r" (lz) : "r" (size));
+ tsize = (21 - lz) / 2;
+
+#ifdef CONFIG_SMP
+ if ((flags & _PAGE_NO_CACHE) == 0)
+ flags |= _PAGE_COHERENT;
+#endif
+
+ TLBCAM[index].MAS0 = MAS0_TLBSEL | (index << 16);
+ TLBCAM[index].MAS1 = MAS1_VALID | MAS1_IPROT | MAS1_TSIZE(tsize) | ((pid << 16) & MAS1_TID);
+ TLBCAM[index].MAS2 = virt & PAGE_MASK;
+
+ TLBCAM[index].MAS2 |= (flags & _PAGE_WRITETHRU) ? MAS2_W : 0;
+ TLBCAM[index].MAS2 |= (flags & _PAGE_NO_CACHE) ? MAS2_I : 0;
+ TLBCAM[index].MAS2 |= (flags & _PAGE_COHERENT) ? MAS2_M : 0;
+ TLBCAM[index].MAS2 |= (flags & _PAGE_GUARDED) ? MAS2_G : 0;
+ TLBCAM[index].MAS2 |= (flags & _PAGE_ENDIAN) ? MAS2_E : 0;
+
+ TLBCAM[index].MAS3 = (phys & PAGE_MASK) | MAS3_SX | MAS3_SR;
+ TLBCAM[index].MAS3 |= ((flags & _PAGE_RW) ? MAS3_SW : 0);
+
+#ifndef CONFIG_KGDB /* want user access for breakpoints */
+ if (flags & _PAGE_USER) {
+ TLBCAM[index].MAS3 |= MAS3_UX | MAS3_UR;
+ TLBCAM[index].MAS3 |= ((flags & _PAGE_RW) ? MAS3_UW : 0);
+ }
+#else
+ TLBCAM[index].MAS3 |= MAS3_UX | MAS3_UR;
+ TLBCAM[index].MAS3 |= ((flags & _PAGE_RW) ? MAS3_UW : 0);
+#endif
+
+ tlbcam_addrs[index].start = virt;
+ tlbcam_addrs[index].limit = virt + size - 1;
+ tlbcam_addrs[index].phys = phys;
+
+ loadcam_entry(index);
+}
+
+void invalidate_tlbcam_entry(int index)
+{
+ TLBCAM[index].MAS0 = MAS0_TLBSEL | (index << 16);
+ TLBCAM[index].MAS1 = ~MAS1_VALID;
+
+ loadcam_entry(index);
+}
+
+void __init cam_mapin_ram(unsigned long cam0, unsigned long cam1,
+ unsigned long cam2)
+{
+ settlbcam(0, KERNELBASE, PPC_MEMSTART, cam0, _PAGE_KERNEL, 0);
+ tlbcam_index++;
+ if (cam1) {
+ tlbcam_index++;
+ settlbcam(1, KERNELBASE+cam0, PPC_MEMSTART+cam0, cam1, _PAGE_KERNEL, 0);
+ }
+ if (cam2) {
+ tlbcam_index++;
+ settlbcam(2, KERNELBASE+cam0+cam1, PPC_MEMSTART+cam0+cam1, cam2, _PAGE_KERNEL, 0);
+ }
+}
+
+/*
+ * MMU_init_hw does the chip-specific initialization of the MMU hardware.
+ */
+void __init MMU_init_hw(void)
+{
+ flush_instruction_cache();
+}
+
+unsigned long __init mmu_mapin_ram(void)
+{
+ cam_mapin_ram(__cam0, __cam1, __cam2);
+
+ return __cam0 + __cam1 + __cam2;
+}
+
+
+void __init
+adjust_total_lowmem(void)
+{
+ unsigned long max_low_mem = MAX_LOW_MEM;
+ unsigned long cam_max = 0x10000000;
+ unsigned long ram;
+
+ /* adjust CAM size to max_low_mem */
+ if (max_low_mem < cam_max)
+ cam_max = max_low_mem;
+
+ /* adjust lowmem size to max_low_mem */
+ if (max_low_mem < total_lowmem)
+ ram = max_low_mem;
+ else
+ ram = total_lowmem;
+
+ /* Calculate CAM values */
+ __cam0 = 1UL << 2 * (__ilog2(ram) / 2);
+ if (__cam0 > cam_max)
+ __cam0 = cam_max;
+ ram -= __cam0;
+ if (ram) {
+ __cam1 = 1UL << 2 * (__ilog2(ram) / 2);
+ if (__cam1 > cam_max)
+ __cam1 = cam_max;
+ ram -= __cam1;
+ }
+ if (ram) {
+ __cam2 = 1UL << 2 * (__ilog2(ram) / 2);
+ if (__cam2 > cam_max)
+ __cam2 = cam_max;
+ ram -= __cam2;
+ }
+
+ printk(KERN_INFO "Memory CAM mapping: CAM0=%ldMb, CAM1=%ldMb,"
+ " CAM2=%ldMb residual: %ldMb\n",
+ __cam0 >> 20, __cam1 >> 20, __cam2 >> 20,
+ (total_lowmem - __cam0 - __cam1 - __cam2) >> 20);
+ __max_low_memory = max_low_mem = __cam0 + __cam1 + __cam2;
+}
--- /dev/null
+
+menu "Profiling support"
+ depends on EXPERIMENTAL
+
+config PROFILING
+ bool "Profiling support (EXPERIMENTAL)"
+ help
+ Say Y here to enable the extended profiling support mechanisms used
+ by profilers such as OProfile.
+
+
+config OPROFILE
+ tristate "OProfile system profiling (EXPERIMENTAL)"
+ depends on PROFILING
+ help
+ OProfile is a profiling system capable of profiling the
+ whole system, include the kernel, kernel modules, libraries,
+ and applications.
+
+ If unsure, say N.
+
+endmenu
+
--- /dev/null
+obj-$(CONFIG_OPROFILE) += oprofile.o
+
+DRIVER_OBJS := $(addprefix ../../../drivers/oprofile/, \
+ oprof.o cpu_buffer.o buffer_sync.o \
+ event_buffer.o oprofile_files.o \
+ oprofilefs.o oprofile_stats.o \
+ timer_int.o )
+
+oprofile-y := $(DRIVER_OBJS) init.o
--- /dev/null
+/**
+ * @file init.c
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon <levon@movementarian.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/oprofile.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+
+int __init oprofile_arch_init(struct oprofile_operations ** ops)
+{
+ return -ENODEV;
+}
+
+
+void oprofile_arch_exit(void)
+{
+}
default MPC8540_ADS
config MPC8540_ADS
- bool "MPC8540ADS"
+ bool "Freescale MPC8540 ADS"
help
This option enables support for the MPC 8540 ADS evaluation board.
+config MPC8555_CDS
+ bool "Freescale MPC8555 CDS"
+ help
+ This option enablese support for the MPC8555 CDS evaluation board.
+
+config MPC8560_ADS
+ bool "Freescale MPC8560 ADS"
+ help
+ This option enables support for the MPC 8560 ADS evaluation board.
+
+config SBC8560
+ bool "WindRiver PowerQUICC III SBC8560"
+ help
+ This option enables support for the WindRiver PowerQUICC III
+ SBC8560 board.
+
endchoice
# It's often necessary to know the specific 85xx processor type.
depends on MPC8540_ADS
default y
+config MPC8555
+ bool
+ depends on MPC8555_CDS
+ default y
+
+config MPC8560
+ bool
+ depends on SBC8560 || MPC8560_ADS
+ default y
+
+config 85xx_PCI2
+ bool "Supprt for 2nd PCI host controller"
+ depends on MPC8555_CDS
+ default y
+
config FSL_OCP
bool
depends on 85xx
config PPC_GEN550
bool
- depends on MPC8540
+ depends on MPC8540 || SBC8560 || MPC8555
default y
endmenu
--- /dev/null
+/*
+ * arch/ppc/platforms/85xx/mpc8540.c
+ *
+ * MPC8540 I/O descriptions
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2004 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <asm/mpc85xx.h>
+#include <asm/ocp.h>
+
+/* These should be defined in platform code */
+extern struct ocp_gfar_data mpc85xx_tsec1_def;
+extern struct ocp_gfar_data mpc85xx_tsec2_def;
+extern struct ocp_gfar_data mpc85xx_fec_def;
+extern struct ocp_mpc_i2c_data mpc85xx_i2c1_def;
+
+/* We use offsets for paddr since we do not know at compile time
+ * what CCSRBAR is, platform code should fix this up in
+ * setup_arch
+ *
+ * Only the first IRQ is given even if a device has
+ * multiple lines associated with ita
+ */
+struct ocp_def core_ocp[] = {
+ { .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_IIC,
+ .index = 0,
+ .paddr = MPC85xx_IIC1_OFFSET,
+ .irq = MPC85xx_IRQ_IIC1,
+ .pm = OCP_CPM_NA,
+ .additions = &mpc85xx_i2c1_def,
+ },
+ { .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_16550,
+ .index = 0,
+ .paddr = MPC85xx_UART0_OFFSET,
+ .irq = MPC85xx_IRQ_DUART,
+ .pm = OCP_CPM_NA,
+ },
+ { .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_16550,
+ .index = 1,
+ .paddr = MPC85xx_UART1_OFFSET,
+ .irq = MPC85xx_IRQ_DUART,
+ .pm = OCP_CPM_NA,
+ },
+ { .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_GFAR,
+ .index = 0,
+ .paddr = MPC85xx_ENET1_OFFSET,
+ .irq = MPC85xx_IRQ_TSEC1_TX,
+ .pm = OCP_CPM_NA,
+ .additions = &mpc85xx_tsec1_def,
+ },
+ { .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_GFAR,
+ .index = 1,
+ .paddr = MPC85xx_ENET2_OFFSET,
+ .irq = MPC85xx_IRQ_TSEC2_TX,
+ .pm = OCP_CPM_NA,
+ .additions = &mpc85xx_tsec2_def,
+ },
+ { .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_GFAR,
+ .index = 2,
+ .paddr = MPC85xx_ENET3_OFFSET,
+ .irq = MPC85xx_IRQ_FEC,
+ .pm = OCP_CPM_NA,
+ .additions = &mpc85xx_fec_def,
+ },
+ { .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_DMA,
+ .index = 0,
+ .paddr = MPC85xx_DMA_OFFSET,
+ .irq = MPC85xx_IRQ_DMA0,
+ .pm = OCP_CPM_NA,
+ },
+ { .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_PERFMON,
+ .index = 0,
+ .paddr = MPC85xx_PERFMON_OFFSET,
+ .irq = MPC85xx_IRQ_PERFMON,
+ .pm = OCP_CPM_NA,
+ },
+ { .vendor = OCP_VENDOR_INVALID
+ }
+};
#include <linux/serial.h>
#include <linux/tty.h> /* for linux/serial_core.h */
#include <linux/serial_core.h>
+#include <linux/initrd.h>
#include <linux/module.h>
#include <asm/system.h>
#define __MACH_MPC8540ADS_H__
#include <linux/config.h>
-#include <linux/serial.h>
#include <linux/initrd.h>
#include <syslib/ppc85xx_setup.h>
#include <platforms/85xx/mpc85xx_ads_common.h>
--- /dev/null
+/*
+ * arch/ppc/platform/85xx/mpc8555.c
+ *
+ * MPC8555 I/O descriptions
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2004 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <asm/mpc85xx.h>
+#include <asm/ocp.h>
+
+/* These should be defined in platform code */
+extern struct ocp_gfar_data mpc85xx_tsec1_def;
+extern struct ocp_gfar_data mpc85xx_tsec2_def;
+extern struct ocp_mpc_i2c_data mpc85xx_i2c1_def;
+
+/* We use offsets for paddr since we do not know at compile time
+ * what CCSRBAR is, platform code should fix this up in
+ * setup_arch
+ *
+ * Only the first IRQ is given even if a device has
+ * multiple lines associated with ita
+ */
+struct ocp_def core_ocp[] = {
+ { .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_IIC,
+ .index = 0,
+ .paddr = MPC85xx_IIC1_OFFSET,
+ .irq = MPC85xx_IRQ_IIC1,
+ .pm = OCP_CPM_NA,
+ .additions = &mpc85xx_i2c1_def,
+ },
+ { .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_16550,
+ .index = 0,
+ .paddr = MPC85xx_UART0_OFFSET,
+ .irq = MPC85xx_IRQ_DUART,
+ .pm = OCP_CPM_NA,
+ },
+ { .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_16550,
+ .index = 1,
+ .paddr = MPC85xx_UART1_OFFSET,
+ .irq = MPC85xx_IRQ_DUART,
+ .pm = OCP_CPM_NA,
+ },
+ { .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_GFAR,
+ .index = 0,
+ .paddr = MPC85xx_ENET1_OFFSET,
+ .irq = MPC85xx_IRQ_TSEC1_TX,
+ .pm = OCP_CPM_NA,
+ .additions = &mpc85xx_tsec1_def,
+ },
+ { .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_GFAR,
+ .index = 1,
+ .paddr = MPC85xx_ENET2_OFFSET,
+ .irq = MPC85xx_IRQ_TSEC2_TX,
+ .pm = OCP_CPM_NA,
+ .additions = &mpc85xx_tsec2_def,
+ },
+ { .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_DMA,
+ .index = 0,
+ .paddr = MPC85xx_DMA_OFFSET,
+ .irq = MPC85xx_IRQ_DMA0,
+ .pm = OCP_CPM_NA,
+ },
+ { .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_PERFMON,
+ .index = 0,
+ .paddr = MPC85xx_PERFMON_OFFSET,
+ .irq = MPC85xx_IRQ_PERFMON,
+ .pm = OCP_CPM_NA,
+ },
+ { .vendor = OCP_VENDOR_INVALID
+ }
+};
--- /dev/null
+/*
+ * arch/ppc/platforms/mpc8555_cds.h
+ *
+ * MPC8555CDS board definitions
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2004 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __MACH_MPC8555CDS_H__
+#define __MACH_MPC8555CDS_H__
+
+#include <linux/config.h>
+#include <linux/serial.h>
+#include <platforms/85xx/mpc85xx_cds_common.h>
+
+#define CPM_MAP_ADDR (CCSRBAR + MPC85xx_CPM_OFFSET)
+
+#endif /* __MACH_MPC8555CDS_H__ */
--- /dev/null
+/*
+ * arch/ppc/platforms/85xx/mpc8560.c
+ *
+ * MPC8560 I/O descriptions
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2004 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <asm/mpc85xx.h>
+#include <asm/ocp.h>
+
+/* These should be defined in platform code */
+extern struct ocp_gfar_data mpc85xx_tsec1_def;
+extern struct ocp_gfar_data mpc85xx_tsec2_def;
+extern struct ocp_mpc_i2c_data mpc85xx_i2c1_def;
+
+/* We use offsets for paddr since we do not know at compile time
+ * what CCSRBAR is, platform code should fix this up in
+ * setup_arch
+ *
+ * Only the first IRQ is given even if a device has
+ * multiple lines associated with ita
+ */
+struct ocp_def core_ocp[] = {
+ { .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_IIC,
+ .index = 0,
+ .paddr = MPC85xx_IIC1_OFFSET,
+ .irq = MPC85xx_IRQ_IIC1,
+ .pm = OCP_CPM_NA,
+ .additions = &mpc85xx_i2c1_def,
+ },
+ { .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_GFAR,
+ .index = 0,
+ .paddr = MPC85xx_ENET1_OFFSET,
+ .irq = MPC85xx_IRQ_TSEC1_TX,
+ .pm = OCP_CPM_NA,
+ .additions = &mpc85xx_tsec1_def,
+ },
+ { .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_GFAR,
+ .index = 1,
+ .paddr = MPC85xx_ENET2_OFFSET,
+ .irq = MPC85xx_IRQ_TSEC2_TX,
+ .pm = OCP_CPM_NA,
+ .additions = &mpc85xx_tsec2_def,
+ },
+ { .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_DMA,
+ .index = 0,
+ .paddr = MPC85xx_DMA_OFFSET,
+ .irq = MPC85xx_IRQ_DMA0,
+ .pm = OCP_CPM_NA,
+ },
+ { .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_PERFMON,
+ .index = 0,
+ .paddr = MPC85xx_PERFMON_OFFSET,
+ .irq = MPC85xx_IRQ_PERFMON,
+ .pm = OCP_CPM_NA,
+ },
+ { .vendor = OCP_VENDOR_INVALID
+ }
+};
--- /dev/null
+/*
+ * arch/ppc/platforms/85xx/mpc8560_ads.c
+ *
+ * MPC8560ADS board specific routines
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2004 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/reboot.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+#include <linux/major.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/seq_file.h>
+#include <linux/root_dev.h>
+#include <linux/serial.h>
+#include <linux/tty.h> /* for linux/serial_core.h */
+#include <linux/serial_core.h>
+#include <linux/initrd.h>
+#include <linux/module.h>
+
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/atomic.h>
+#include <asm/time.h>
+#include <asm/io.h>
+#include <asm/machdep.h>
+#include <asm/prom.h>
+#include <asm/open_pic.h>
+#include <asm/bootinfo.h>
+#include <asm/pci-bridge.h>
+#include <asm/mpc85xx.h>
+#include <asm/irq.h>
+#include <asm/immap_85xx.h>
+#include <asm/kgdb.h>
+#include <asm/ocp.h>
+#include <asm/cpm2.h>
+#include <mm/mmu_decl.h>
+
+#include <syslib/cpm2_pic.h>
+#include <syslib/ppc85xx_common.h>
+#include <syslib/ppc85xx_setup.h>
+
+extern void cpm2_reset(void);
+
+struct ocp_gfar_data mpc85xx_tsec1_def = {
+ .interruptTransmit = MPC85xx_IRQ_TSEC1_TX,
+ .interruptError = MPC85xx_IRQ_TSEC1_ERROR,
+ .interruptReceive = MPC85xx_IRQ_TSEC1_RX,
+ .interruptPHY = MPC85xx_IRQ_EXT5,
+ .flags = (GFAR_HAS_GIGABIT | GFAR_HAS_MULTI_INTR
+ | GFAR_HAS_RMON | GFAR_HAS_COALESCE
+ | GFAR_HAS_PHY_INTR),
+ .phyid = 0,
+ .phyregidx = 0,
+};
+
+struct ocp_gfar_data mpc85xx_tsec2_def = {
+ .interruptTransmit = MPC85xx_IRQ_TSEC2_TX,
+ .interruptError = MPC85xx_IRQ_TSEC2_ERROR,
+ .interruptReceive = MPC85xx_IRQ_TSEC2_RX,
+ .interruptPHY = MPC85xx_IRQ_EXT5,
+ .flags = (GFAR_HAS_GIGABIT | GFAR_HAS_MULTI_INTR
+ | GFAR_HAS_RMON | GFAR_HAS_COALESCE
+ | GFAR_HAS_PHY_INTR),
+ .phyid = 1,
+ .phyregidx = 0,
+};
+
+struct ocp_fs_i2c_data mpc85xx_i2c1_def = {
+ .flags = FS_I2C_SEPARATE_DFSRR,
+};
+
+/* ************************************************************************
+ *
+ * Setup the architecture
+ *
+ */
+
+static void __init
+mpc8560ads_setup_arch(void)
+{
+ struct ocp_def *def;
+ struct ocp_gfar_data *einfo;
+ bd_t *binfo = (bd_t *) __res;
+ unsigned int freq;
+
+ cpm2_reset();
+
+ /* get the core frequency */
+ freq = binfo->bi_intfreq;
+
+ if (ppc_md.progress)
+ ppc_md.progress("mpc8560ads_setup_arch()", 0);
+
+ /* Set loops_per_jiffy to a half-way reasonable value,
+ for use until calibrate_delay gets called. */
+ loops_per_jiffy = freq / HZ;
+
+#ifdef CONFIG_PCI
+ /* setup PCI host bridges */
+ mpc85xx_setup_hose();
+#endif
+
+ def = ocp_get_one_device(OCP_VENDOR_FREESCALE, OCP_FUNC_GFAR, 0);
+ if (def) {
+ einfo = (struct ocp_gfar_data *) def->additions;
+ memcpy(einfo->mac_addr, binfo->bi_enetaddr, 6);
+ }
+
+ def = ocp_get_one_device(OCP_VENDOR_FREESCALE, OCP_FUNC_GFAR, 1);
+ if (def) {
+ einfo = (struct ocp_gfar_data *) def->additions;
+ memcpy(einfo->mac_addr, binfo->bi_enet1addr, 6);
+ }
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (initrd_start)
+ ROOT_DEV = Root_RAM0;
+ else
+#endif
+#ifdef CONFIG_ROOT_NFS
+ ROOT_DEV = Root_NFS;
+#else
+ ROOT_DEV = Root_HDA1;
+#endif
+
+ ocp_for_each_device(mpc85xx_update_paddr_ocp, &(binfo->bi_immr_base));
+}
+
+static irqreturn_t cpm2_cascade(int irq, void *dev_id, struct pt_regs *regs)
+{
+ while ((irq = cpm2_get_irq(regs)) >= 0) {
+ ppc_irq_dispatch_handler(regs, irq);
+ }
+ return IRQ_HANDLED;
+}
+
+static void __init
+mpc8560_ads_init_IRQ(void)
+{
+ int i;
+ volatile cpm2_map_t *immap = cpm2_immr;
+
+ /* Setup OpenPIC */
+ mpc85xx_ads_init_IRQ();
+
+ /* disable all CPM interupts */
+ immap->im_intctl.ic_simrh = 0x0;
+ immap->im_intctl.ic_simrl = 0x0;
+
+ for (i = CPM_IRQ_OFFSET; i < (NR_CPM_INTS + CPM_IRQ_OFFSET); i++)
+ irq_desc[i].handler = &cpm2_pic;
+
+ /* Initialize the default interrupt mapping priorities,
+ * in case the boot rom changed something on us.
+ */
+ immap->im_intctl.ic_sicr = 0;
+ immap->im_intctl.ic_scprrh = 0x05309770;
+ immap->im_intctl.ic_scprrl = 0x05309770;
+
+ request_irq(MPC85xx_IRQ_CPM, cpm2_cascade, SA_INTERRUPT, "cpm2_cascade", NULL);
+
+ return;
+}
+
+
+
+/* ************************************************************************ */
+void __init
+platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7)
+{
+ /* parse_bootinfo must always be called first */
+ parse_bootinfo(find_bootinfo());
+
+ /*
+ * If we were passed in a board information, copy it into the
+ * residual data area.
+ */
+ if (r3) {
+ memcpy((void *) __res, (void *) (r3 + KERNELBASE),
+ sizeof (bd_t));
+
+ }
+#if defined(CONFIG_BLK_DEV_INITRD)
+ /*
+ * If the init RAM disk has been configured in, and there's a valid
+ * starting address for it, set it up.
+ */
+ if (r4) {
+ initrd_start = r4 + KERNELBASE;
+ initrd_end = r5 + KERNELBASE;
+ }
+#endif /* CONFIG_BLK_DEV_INITRD */
+
+ /* Copy the kernel command line arguments to a safe place. */
+
+ if (r6) {
+ *(char *) (r7 + KERNELBASE) = 0;
+ strcpy(cmd_line, (char *) (r6 + KERNELBASE));
+ }
+
+ /* setup the PowerPC module struct */
+ ppc_md.setup_arch = mpc8560ads_setup_arch;
+ ppc_md.show_cpuinfo = mpc85xx_ads_show_cpuinfo;
+
+ ppc_md.init_IRQ = mpc8560_ads_init_IRQ;
+ ppc_md.get_irq = openpic_get_irq;
+
+ ppc_md.restart = mpc85xx_restart;
+ ppc_md.power_off = mpc85xx_power_off;
+ ppc_md.halt = mpc85xx_halt;
+
+ ppc_md.find_end_of_memory = mpc85xx_find_end_of_memory;
+
+ ppc_md.time_init = NULL;
+ ppc_md.set_rtc_time = NULL;
+ ppc_md.get_rtc_time = NULL;
+ ppc_md.calibrate_decr = mpc85xx_calibrate_decr;
+
+ if (ppc_md.progress)
+ ppc_md.progress("mpc8560ads_init(): exit", 0);
+
+ return;
+}
--- /dev/null
+/*
+ * arch/ppc/platforms/mpc8560_ads.h
+ *
+ * MPC8540ADS board definitions
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2004 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __MACH_MPC8560ADS_H
+#define __MACH_MPC8560ADS_H
+
+#include <linux/config.h>
+#include <syslib/ppc85xx_setup.h>
+#include <platforms/85xx/mpc85xx_ads_common.h>
+
+#define CPM_MAP_ADDR (CCSRBAR + MPC85xx_CPM_OFFSET)
+#define PHY_INTERRUPT MPC85xx_IRQ_EXT7
+
+#endif /* __MACH_MPC8560ADS_H */
--- /dev/null
+/*
+ * arch/ppc/platforms/85xx/mpc85xx_ads_common.c
+ *
+ * MPC85xx ADS board common routines
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2004 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/reboot.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+#include <linux/major.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/seq_file.h>
+#include <linux/serial.h>
+#include <linux/module.h>
+
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/atomic.h>
+#include <asm/time.h>
+#include <asm/io.h>
+#include <asm/machdep.h>
+#include <asm/prom.h>
+#include <asm/open_pic.h>
+#include <asm/bootinfo.h>
+#include <asm/pci-bridge.h>
+#include <asm/mpc85xx.h>
+#include <asm/irq.h>
+#include <asm/immap_85xx.h>
+#include <asm/ocp.h>
+
+#include <mm/mmu_decl.h>
+
+#include <platforms/85xx/mpc85xx_ads_common.h>
+
+#ifndef CONFIG_PCI
+unsigned long isa_io_base = 0;
+unsigned long isa_mem_base = 0;
+#endif
+
+extern unsigned long total_memory; /* in mm/init */
+
+unsigned char __res[sizeof (bd_t)];
+
+/* Internal interrupts are all Level Sensitive, and Positive Polarity */
+
+static u_char mpc85xx_ads_openpic_initsenses[] __initdata = {
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 0: L2 Cache */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 1: ECM */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 2: DDR DRAM */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 3: LBIU */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 4: DMA 0 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 5: DMA 1 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 6: DMA 2 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 7: DMA 3 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 8: PCI/PCI-X */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 9: RIO Inbound Port Write Error */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 10: RIO Doorbell Inbound */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 11: RIO Outbound Message */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 12: RIO Inbound Message */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 13: TSEC 0 Transmit */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 14: TSEC 0 Receive */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 15: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 16: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 17: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 18: TSEC 0 Receive/Transmit Error */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 19: TSEC 1 Transmit */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 20: TSEC 1 Receive */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 21: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 22: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 23: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 24: TSEC 1 Receive/Transmit Error */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 25: Fast Ethernet */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 26: DUART */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 27: I2C */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 28: Performance Monitor */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 29: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 30: CPM */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 31: Unused */
+ 0x0, /* External 0: */
+#if defined(CONFIG_PCI)
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 1: PCI slot 0 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 2: PCI slot 1 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 3: PCI slot 2 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 4: PCI slot 3 */
+#else
+ 0x0, /* External 1: */
+ 0x0, /* External 2: */
+ 0x0, /* External 3: */
+ 0x0, /* External 4: */
+#endif
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 5: PHY */
+ 0x0, /* External 6: */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 7: PHY */
+ 0x0, /* External 8: */
+ 0x0, /* External 9: */
+ 0x0, /* External 10: */
+ 0x0, /* External 11: */
+};
+
+/* ************************************************************************ */
+int
+mpc85xx_ads_show_cpuinfo(struct seq_file *m)
+{
+ uint pvid, svid, phid1;
+ uint memsize = total_memory;
+ bd_t *binfo = (bd_t *) __res;
+ unsigned int freq;
+
+ /* get the core frequency */
+ freq = binfo->bi_intfreq;
+
+ pvid = mfspr(PVR);
+ svid = mfspr(SVR);
+
+ seq_printf(m, "Vendor\t\t: Freescale Semiconductor\n");
+
+ switch (svid & 0xffff0000) {
+ case SVR_8540:
+ seq_printf(m, "Machine\t\t: mpc8540ads\n");
+ break;
+ case SVR_8560:
+ seq_printf(m, "Machine\t\t: mpc8560ads\n");
+ break;
+ default:
+ seq_printf(m, "Machine\t\t: unknown\n");
+ break;
+ }
+ seq_printf(m, "bus freq\t: %u.%.6u MHz\n", freq / 1000000,
+ freq % 1000000);
+ seq_printf(m, "PVR\t\t: 0x%x\n", pvid);
+ seq_printf(m, "SVR\t\t: 0x%x\n", svid);
+
+ /* Display cpu Pll setting */
+ phid1 = mfspr(HID1);
+ seq_printf(m, "PLL setting\t: 0x%x\n", ((phid1 >> 24) & 0x3f));
+
+ /* Display the amount of memory */
+ seq_printf(m, "Memory\t\t: %d MB\n", memsize / (1024 * 1024));
+
+ return 0;
+}
+
+void __init
+mpc85xx_ads_init_IRQ(void)
+{
+ bd_t *binfo = (bd_t *) __res;
+ /* Determine the Physical Address of the OpenPIC regs */
+ phys_addr_t OpenPIC_PAddr =
+ binfo->bi_immr_base + MPC85xx_OPENPIC_OFFSET;
+ OpenPIC_Addr = ioremap(OpenPIC_PAddr, MPC85xx_OPENPIC_SIZE);
+ OpenPIC_InitSenses = mpc85xx_ads_openpic_initsenses;
+ OpenPIC_NumInitSenses = sizeof (mpc85xx_ads_openpic_initsenses);
+
+ /* Skip reserved space and internal sources */
+ openpic_set_sources(0, 32, OpenPIC_Addr + 0x10200);
+ /* Map PIC IRQs 0-11 */
+ openpic_set_sources(32, 12, OpenPIC_Addr + 0x10000);
+
+ /* we let openpic interrupts starting from an offset, to
+ * leave space for cascading interrupts underneath.
+ */
+ openpic_init(MPC85xx_OPENPIC_IRQ_OFFSET);
+
+ return;
+}
+
+#ifdef CONFIG_PCI
+/*
+ * interrupt routing
+ */
+
+int
+mpc85xx_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin)
+{
+ static char pci_irq_table[][4] =
+ /*
+ * This is little evil, but works around the fact
+ * that revA boards have IDSEL starting at 18
+ * and others boards (older) start at 12
+ *
+ * PCI IDSEL/INTPIN->INTLINE
+ * A B C D
+ */
+ {
+ {PIRQA, PIRQB, PIRQC, PIRQD}, /* IDSEL 2 */
+ {PIRQD, PIRQA, PIRQB, PIRQC},
+ {PIRQC, PIRQD, PIRQA, PIRQB},
+ {PIRQB, PIRQC, PIRQD, PIRQA}, /* IDSEL 5 */
+ {0, 0, 0, 0}, /* -- */
+ {0, 0, 0, 0}, /* -- */
+ {0, 0, 0, 0}, /* -- */
+ {0, 0, 0, 0}, /* -- */
+ {0, 0, 0, 0}, /* -- */
+ {0, 0, 0, 0}, /* -- */
+ {PIRQA, PIRQB, PIRQC, PIRQD}, /* IDSEL 12 */
+ {PIRQD, PIRQA, PIRQB, PIRQC},
+ {PIRQC, PIRQD, PIRQA, PIRQB},
+ {PIRQB, PIRQC, PIRQD, PIRQA}, /* IDSEL 15 */
+ {0, 0, 0, 0}, /* -- */
+ {0, 0, 0, 0}, /* -- */
+ {PIRQA, PIRQB, PIRQC, PIRQD}, /* IDSEL 18 */
+ {PIRQD, PIRQA, PIRQB, PIRQC},
+ {PIRQC, PIRQD, PIRQA, PIRQB},
+ {PIRQB, PIRQC, PIRQD, PIRQA}, /* IDSEL 21 */
+ };
+
+ const long min_idsel = 2, max_idsel = 21, irqs_per_slot = 4;
+ return PCI_IRQ_TABLE_LOOKUP;
+}
+
+int
+mpc85xx_exclude_device(u_char bus, u_char devfn)
+{
+ if (bus == 0 && PCI_SLOT(devfn) == 0)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ else
+ return PCIBIOS_SUCCESSFUL;
+}
+
+#endif /* CONFIG_PCI */
--- /dev/null
+/*
+ * arch/ppc/platforms/85xx/mpc85xx_ads_common.h
+ *
+ * MPC85XX ADS common board definitions
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2004 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __MACH_MPC85XX_ADS_H__
+#define __MACH_MPC85XX_ADS_H__
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/seq_file.h>
+#include <asm/ppcboot.h>
+
+#define BOARD_CCSRBAR ((uint)0xe0000000)
+#define BCSR_ADDR ((uint)0xf8000000)
+#define BCSR_SIZE ((uint)(32 * 1024))
+
+extern int mpc85xx_ads_show_cpuinfo(struct seq_file *m);
+extern void mpc85xx_ads_init_IRQ(void) __init;
+extern void mpc85xx_ads_map_io(void) __init;
+
+/* PCI interrupt controller */
+#define PIRQA MPC85xx_IRQ_EXT1
+#define PIRQB MPC85xx_IRQ_EXT2
+#define PIRQC MPC85xx_IRQ_EXT3
+#define PIRQD MPC85xx_IRQ_EXT4
+
+#define MPC85XX_PCI1_LOWER_IO 0x00000000
+#define MPC85XX_PCI1_UPPER_IO 0x00ffffff
+
+#define MPC85XX_PCI1_LOWER_MEM 0x80000000
+#define MPC85XX_PCI1_UPPER_MEM 0x9fffffff
+
+#define MPC85XX_PCI1_IO_BASE 0xe2000000
+#define MPC85XX_PCI1_MEM_OFFSET 0x00000000
+
+#define MPC85XX_PCI1_IO_SIZE 0x01000000
+
+#endif /* __MACH_MPC85XX_ADS_H__ */
--- /dev/null
+/*
+ * arch/ppc/platform/85xx/mpc85xx_cds_common.c
+ *
+ * MPC85xx CDS board specific routines
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2004 Freescale Semiconductor, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/reboot.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+#include <linux/major.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/seq_file.h>
+#include <linux/serial.h>
+#include <linux/module.h>
+#include <linux/root_dev.h>
+#include <linux/initrd.h>
+#include <linux/tty.h>
+#include <linux/serial_core.h>
+
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/atomic.h>
+#include <asm/time.h>
+#include <asm/io.h>
+#include <asm/machdep.h>
+#include <asm/prom.h>
+#include <asm/open_pic.h>
+#include <asm/bootinfo.h>
+#include <asm/pci-bridge.h>
+#include <asm/mpc85xx.h>
+#include <asm/irq.h>
+#include <asm/immap_85xx.h>
+#include <asm/immap_cpm2.h>
+#include <asm/ocp.h>
+#include <asm/kgdb.h>
+
+#include <mm/mmu_decl.h>
+#include <syslib/cpm2_pic.h>
+#include <syslib/ppc85xx_common.h>
+#include <syslib/ppc85xx_setup.h>
+
+
+#ifndef CONFIG_PCI
+unsigned long isa_io_base = 0;
+unsigned long isa_mem_base = 0;
+#endif
+
+extern unsigned long total_memory; /* in mm/init */
+
+unsigned char __res[sizeof (bd_t)];
+
+static int cds_pci_slot = 2;
+static volatile u8 * cadmus;
+
+/* Internal interrupts are all Level Sensitive, and Positive Polarity */
+
+static u_char mpc85xx_cds_openpic_initsenses[] __initdata = {
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 0: L2 Cache */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 1: ECM */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 2: DDR DRAM */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 3: LBIU */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 4: DMA 0 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 5: DMA 1 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 6: DMA 2 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 7: DMA 3 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 8: PCI/PCI-X */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 9: RIO Inbound Port Write Error */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 10: RIO Doorbell Inbound */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 11: RIO Outbound Message */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 12: RIO Inbound Message */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 13: TSEC 0 Transmit */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 14: TSEC 0 Receive */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 15: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 16: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 17: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 18: TSEC 0 Receive/Transmit Error */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 19: TSEC 1 Transmit */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 20: TSEC 1 Receive */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 21: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 22: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 23: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 24: TSEC 1 Receive/Transmit Error */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 25: Fast Ethernet */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 26: DUART */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 27: I2C */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 28: Performance Monitor */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 29: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 30: CPM */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 31: Unused */
+#if defined(CONFIG_PCI)
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 0: PCI1 slot */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 1: PCI1 slot */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 2: PCI1 slot */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 3: PCI1 slot */
+#else
+ 0x0, /* External 0: */
+ 0x0, /* External 1: */
+ 0x0, /* External 2: */
+ 0x0, /* External 3: */
+#endif
+ 0x0, /* External 4: */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 5: PHY */
+ 0x0, /* External 6: */
+ 0x0, /* External 7: */
+ 0x0, /* External 8: */
+ 0x0, /* External 9: */
+ 0x0, /* External 10: */
+#if defined(CONFIG_85xx_PCI2) && defined(CONFIG_PCI)
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* External 11: PCI2 slot 0 */
+#else
+ 0x0, /* External 11: */
+#endif
+};
+
+struct ocp_gfar_data mpc85xx_tsec1_def = {
+ .interruptTransmit = MPC85xx_IRQ_TSEC1_TX,
+ .interruptError = MPC85xx_IRQ_TSEC1_ERROR,
+ .interruptReceive = MPC85xx_IRQ_TSEC1_RX,
+ .interruptPHY = MPC85xx_IRQ_EXT5,
+ .flags = (GFAR_HAS_GIGABIT | GFAR_HAS_MULTI_INTR |
+ GFAR_HAS_PHY_INTR),
+ .phyid = 0,
+ .phyregidx = 0,
+};
+
+struct ocp_gfar_data mpc85xx_tsec2_def = {
+ .interruptTransmit = MPC85xx_IRQ_TSEC2_TX,
+ .interruptError = MPC85xx_IRQ_TSEC2_ERROR,
+ .interruptReceive = MPC85xx_IRQ_TSEC2_RX,
+ .interruptPHY = MPC85xx_IRQ_EXT5,
+ .flags = (GFAR_HAS_GIGABIT | GFAR_HAS_MULTI_INTR |
+ GFAR_HAS_PHY_INTR),
+ .phyid = 1,
+ .phyregidx = 0,
+};
+
+struct ocp_fs_i2c_data mpc85xx_i2c1_def = {
+ .flags = FS_I2C_SEPARATE_DFSRR,
+};
+
+/* ************************************************************************ */
+int
+mpc85xx_cds_show_cpuinfo(struct seq_file *m)
+{
+ uint pvid, svid, phid1;
+ uint memsize = total_memory;
+ bd_t *binfo = (bd_t *) __res;
+ unsigned int freq;
+
+ /* get the core frequency */
+ freq = binfo->bi_intfreq;
+
+ pvid = mfspr(PVR);
+ svid = mfspr(SVR);
+
+ seq_printf(m, "Vendor\t\t: Freescale Semiconductor\n");
+ seq_printf(m, "Machine\t\t: CDS (%x)\n", cadmus[CM_VER]);
+ seq_printf(m, "bus freq\t: %u.%.6u MHz\n", freq / 1000000,
+ freq % 1000000);
+ seq_printf(m, "PVR\t\t: 0x%x\n", pvid);
+ seq_printf(m, "SVR\t\t: 0x%x\n", svid);
+
+ /* Display cpu Pll setting */
+ phid1 = mfspr(HID1);
+ seq_printf(m, "PLL setting\t: 0x%x\n", ((phid1 >> 24) & 0x3f));
+
+ /* Display the amount of memory */
+ seq_printf(m, "Memory\t\t: %d MB\n", memsize / (1024 * 1024));
+
+ return 0;
+}
+
+#ifdef CONFIG_CPM2
+static void cpm2_cascade(int irq, void *dev_id, struct pt_regs *regs)
+{
+ while((irq = cpm2_get_irq(regs)) >= 0)
+ {
+ ppc_irq_dispatch_handler(regs,irq);
+ }
+}
+#endif /* CONFIG_CPM2 */
+
+void __init
+mpc85xx_cds_init_IRQ(void)
+{
+ bd_t *binfo = (bd_t *) __res;
+#ifdef CONFIG_CPM2
+ volatile cpm2_map_t *immap = cpm2_immr;
+ int i;
+#endif
+
+ /* Determine the Physical Address of the OpenPIC regs */
+ phys_addr_t OpenPIC_PAddr = binfo->bi_immr_base + MPC85xx_OPENPIC_OFFSET;
+ OpenPIC_Addr = ioremap(OpenPIC_PAddr, MPC85xx_OPENPIC_SIZE);
+ OpenPIC_InitSenses = mpc85xx_cds_openpic_initsenses;
+ OpenPIC_NumInitSenses = sizeof (mpc85xx_cds_openpic_initsenses);
+
+ /* Skip reserved space and internal sources */
+ openpic_set_sources(0, 32, OpenPIC_Addr + 0x10200);
+ /* Map PIC IRQs 0-11 */
+ openpic_set_sources(32, 12, OpenPIC_Addr + 0x10000);
+
+ /* we let openpic interrupts starting from an offset, to
+ * leave space for cascading interrupts underneath.
+ */
+ openpic_init(MPC85xx_OPENPIC_IRQ_OFFSET);
+
+#ifdef CONFIG_CPM2
+ /* disable all CPM interupts */
+ immap->im_intctl.ic_simrh = 0x0;
+ immap->im_intctl.ic_simrl = 0x0;
+
+ for (i = CPM_IRQ_OFFSET; i < (NR_CPM_INTS + CPM_IRQ_OFFSET); i++)
+ irq_desc[i].handler = &cpm2_pic;
+
+ /* Initialize the default interrupt mapping priorities,
+ * in case the boot rom changed something on us.
+ */
+ immap->im_intctl.ic_sicr = 0;
+ immap->im_intctl.ic_scprrh = 0x05309770;
+ immap->im_intctl.ic_scprrl = 0x05309770;
+
+ request_irq(MPC85xx_IRQ_CPM, cpm2_cascade, SA_INTERRUPT, "cpm2_cascade", NULL);
+#endif
+
+ return;
+}
+
+#ifdef CONFIG_PCI
+/*
+ * interrupt routing
+ */
+int
+mpc85xx_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin)
+{
+ struct pci_controller *hose = pci_bus_to_hose(dev->bus->number);
+
+ if (!hose->index)
+ {
+ /* Handle PCI1 interrupts */
+ char pci_irq_table[][4] =
+ /*
+ * PCI IDSEL/INTPIN->INTLINE
+ * A B C D
+ */
+
+ /* Note IRQ assignment for slots is based on which slot the elysium is
+ * in -- in this setup elysium is in slot #2 (this PIRQA as first
+ * interrupt on slot */
+ {
+ { 0, 1, 2, 3 }, /* 16 - PMC */
+ { 3, 0, 0, 0 }, /* 17 P2P (Tsi320) */
+ { 0, 1, 2, 3 }, /* 18 - Slot 1 */
+ { 1, 2, 3, 0 }, /* 19 - Slot 2 */
+ { 2, 3, 0, 1 }, /* 20 - Slot 3 */
+ { 3, 0, 1, 2 }, /* 21 - Slot 4 */
+ };
+
+ const long min_idsel = 16, max_idsel = 21, irqs_per_slot = 4;
+ int i, j;
+
+ for (i = 0; i < 6; i++)
+ for (j = 0; j < 4; j++)
+ pci_irq_table[i][j] =
+ ((pci_irq_table[i][j] + 5 -
+ cds_pci_slot) & 0x3) + PIRQ0A;
+
+ return PCI_IRQ_TABLE_LOOKUP;
+ } else {
+ /* Handle PCI2 interrupts (if we have one) */
+ char pci_irq_table[][4] =
+ {
+ /*
+ * We only have one slot and one interrupt
+ * going to PIRQA - PIRQD */
+ { PIRQ1A, PIRQ1A, PIRQ1A, PIRQ1A }, /* 21 - slot 0 */
+ };
+
+ const long min_idsel = 21, max_idsel = 21, irqs_per_slot = 4;
+
+ return PCI_IRQ_TABLE_LOOKUP;
+ }
+}
+
+#define ARCADIA_HOST_BRIDGE_IDSEL 17
+#define ARCADIA_2ND_BRIDGE_IDSEL 3
+
+int
+mpc85xx_exclude_device(u_char bus, u_char devfn)
+{
+ if (bus == 0 && PCI_SLOT(devfn) == 0)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+#if CONFIG_85xx_PCI2
+ /* With the current code we know PCI2 will be bus 2, however this may
+ * not be guarnteed */
+ if (bus == 2 && PCI_SLOT(devfn) == 0)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+#endif
+ /* We explicitly do not go past the Tundra 320 Bridge */
+ if (bus == 1)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ if ((bus == 0) && (PCI_SLOT(devfn) == ARCADIA_2ND_BRIDGE_IDSEL))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ else
+ return PCIBIOS_SUCCESSFUL;
+}
+#endif /* CONFIG_PCI */
+
+/* ************************************************************************
+ *
+ * Setup the architecture
+ *
+ */
+static void __init
+mpc85xx_cds_setup_arch(void)
+{
+ struct ocp_def *def;
+ struct ocp_gfar_data *einfo;
+ bd_t *binfo = (bd_t *) __res;
+ unsigned int freq;
+
+ /* get the core frequency */
+ freq = binfo->bi_intfreq;
+
+ printk("mpc85xx_cds_setup_arch\n");
+
+#ifdef CONFIG_CPM2
+ cpm2_reset();
+#endif
+
+ cadmus = ioremap(CADMUS_BASE, CADMUS_SIZE);
+ cds_pci_slot = ((cadmus[CM_CSR] >> 6) & 0x3) + 1;
+ printk("CDS Version = %x in PCI slot %d\n", cadmus[CM_VER], cds_pci_slot);
+
+ /* Set loops_per_jiffy to a half-way reasonable value,
+ for use until calibrate_delay gets called. */
+ loops_per_jiffy = freq / HZ;
+
+#ifdef CONFIG_PCI
+ /* setup PCI host bridges */
+ mpc85xx_setup_hose();
+#endif
+
+#ifdef CONFIG_DUMMY_CONSOLE
+ conswitchp = &dummy_con;
+#endif
+
+#ifdef CONFIG_SERIAL_8250
+ mpc85xx_early_serial_map();
+#endif
+
+#ifdef CONFIG_SERIAL_TEXT_DEBUG
+ /* Invalidate the entry we stole earlier the serial ports
+ * should be properly mapped */
+ invalidate_tlbcam_entry(NUM_TLBCAMS - 1);
+#endif
+
+ def = ocp_get_one_device(OCP_VENDOR_FREESCALE, OCP_FUNC_GFAR, 0);
+ if (def) {
+ einfo = (struct ocp_gfar_data *) def->additions;
+ memcpy(einfo->mac_addr, binfo->bi_enetaddr, 6);
+ }
+
+ def = ocp_get_one_device(OCP_VENDOR_FREESCALE, OCP_FUNC_GFAR, 1);
+ if (def) {
+ einfo = (struct ocp_gfar_data *) def->additions;
+ memcpy(einfo->mac_addr, binfo->bi_enet1addr, 6);
+ }
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (initrd_start)
+ ROOT_DEV = Root_RAM0;
+ else
+#endif
+#ifdef CONFIG_ROOT_NFS
+ ROOT_DEV = Root_NFS;
+#else
+ ROOT_DEV = Root_HDA1;
+#endif
+
+ ocp_for_each_device(mpc85xx_update_paddr_ocp, &(binfo->bi_immr_base));
+}
+
+/* ************************************************************************ */
+void __init
+platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7)
+{
+ /* parse_bootinfo must always be called first */
+ parse_bootinfo(find_bootinfo());
+
+ /*
+ * If we were passed in a board information, copy it into the
+ * residual data area.
+ */
+ if (r3) {
+ memcpy((void *) __res, (void *) (r3 + KERNELBASE),
+ sizeof (bd_t));
+
+ }
+#ifdef CONFIG_SERIAL_TEXT_DEBUG
+ {
+ bd_t *binfo = (bd_t *) __res;
+
+ /* Use the last TLB entry to map CCSRBAR to allow access to DUART regs */
+ settlbcam(NUM_TLBCAMS - 1, binfo->bi_immr_base,
+ binfo->bi_immr_base, MPC85xx_CCSRBAR_SIZE, _PAGE_IO, 0);
+
+ }
+#endif
+
+#if defined(CONFIG_BLK_DEV_INITRD)
+ /*
+ * If the init RAM disk has been configured in, and there's a valid
+ * starting address for it, set it up.
+ */
+ if (r4) {
+ initrd_start = r4 + KERNELBASE;
+ initrd_end = r5 + KERNELBASE;
+ }
+#endif /* CONFIG_BLK_DEV_INITRD */
+
+ /* Copy the kernel command line arguments to a safe place. */
+
+ if (r6) {
+ *(char *) (r7 + KERNELBASE) = 0;
+ strcpy(cmd_line, (char *) (r6 + KERNELBASE));
+ }
+
+ /* setup the PowerPC module struct */
+ ppc_md.setup_arch = mpc85xx_cds_setup_arch;
+ ppc_md.show_cpuinfo = mpc85xx_cds_show_cpuinfo;
+
+ ppc_md.init_IRQ = mpc85xx_cds_init_IRQ;
+ ppc_md.get_irq = openpic_get_irq;
+
+ ppc_md.restart = mpc85xx_restart;
+ ppc_md.power_off = mpc85xx_power_off;
+ ppc_md.halt = mpc85xx_halt;
+
+ ppc_md.find_end_of_memory = mpc85xx_find_end_of_memory;
+
+ ppc_md.time_init = NULL;
+ ppc_md.set_rtc_time = NULL;
+ ppc_md.get_rtc_time = NULL;
+ ppc_md.calibrate_decr = mpc85xx_calibrate_decr;
+
+#if defined(CONFIG_SERIAL_8250) && defined(CONFIG_SERIAL_TEXT_DEBUG)
+ ppc_md.progress = gen550_progress;
+#endif /* CONFIG_SERIAL_8250 && CONFIG_SERIAL_TEXT_DEBUG */
+
+ if (ppc_md.progress)
+ ppc_md.progress("mpc85xx_cds_init(): exit", 0);
+
+ return;
+}
--- /dev/null
+/*
+ * arch/ppc/platforms/85xx/mpc85xx_cds_common.h
+ *
+ * MPC85xx CDS board definitions
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2004 Freescale Semiconductor, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __MACH_MPC85XX_CDS_H__
+#define __MACH_MPC85XX_CDS_H__
+
+#include <linux/config.h>
+#include <linux/serial.h>
+#include <asm/ppcboot.h>
+#include <linux/initrd.h>
+#include <syslib/ppc85xx_setup.h>
+
+#define BOARD_CCSRBAR ((uint)0xe0000000)
+#define CCSRBAR_SIZE ((uint)1024*1024)
+
+/* CADMUS info */
+#define CADMUS_BASE (0xf8004000)
+#define CADMUS_SIZE (256)
+#define CM_VER (0)
+#define CM_CSR (1)
+#define CM_RST (2)
+
+/* PCI config */
+#define PCI1_CFG_ADDR_OFFSET (0x8000)
+#define PCI1_CFG_DATA_OFFSET (0x8004)
+
+#define PCI2_CFG_ADDR_OFFSET (0x9000)
+#define PCI2_CFG_DATA_OFFSET (0x9004)
+
+/* PCI interrupt controller */
+#define PIRQ0A MPC85xx_IRQ_EXT0
+#define PIRQ0B MPC85xx_IRQ_EXT1
+#define PIRQ0C MPC85xx_IRQ_EXT2
+#define PIRQ0D MPC85xx_IRQ_EXT3
+#define PIRQ1A MPC85xx_IRQ_EXT11
+
+/* PCI 1 memory map */
+#define MPC85XX_PCI1_LOWER_IO 0x00000000
+#define MPC85XX_PCI1_UPPER_IO 0x00ffffff
+
+#define MPC85XX_PCI1_LOWER_MEM 0x80000000
+#define MPC85XX_PCI1_UPPER_MEM 0x9fffffff
+
+#define MPC85XX_PCI1_IO_BASE 0xe2000000
+#define MPC85XX_PCI1_MEM_OFFSET 0x00000000
+
+#define MPC85XX_PCI1_IO_SIZE 0x01000000
+
+/* PCI 2 memory map */
+#define MPC85XX_PCI2_LOWER_IO 0x01000000
+#define MPC85XX_PCI2_UPPER_IO 0x01ffffff
+
+#define MPC85XX_PCI2_LOWER_MEM 0xa0000000
+#define MPC85XX_PCI2_UPPER_MEM 0xbfffffff
+
+#define MPC85XX_PCI2_IO_BASE 0xe3000000
+#define MPC85XX_PCI2_MEM_OFFSET 0x00000000
+
+#define MPC85XX_PCI2_IO_SIZE 0x01000000
+
+#define SERIAL_PORT_DFNS \
+ STD_UART_OP(0) \
+ STD_UART_OP(1)
+
+#endif /* __MACH_MPC85XX_CDS_H__ */
#include <linux/serial.h>
#include <linux/tty.h> /* for linux/serial_core.h */
#include <linux/serial_core.h>
+#include <linux/initrd.h>
#include <linux/module.h>
#include <asm/system.h>
uart_req.iotype = SERIAL_IO_MEM;
uart_req.mapbase = UARTA_ADDR;
uart_req.membase = ioremap(uart_req.mapbase, MPC85xx_UART0_SIZE);
+ uart_req.type = PORT_16650;
#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
gen550_init(0, &uart_req);
#define __MACH_SBC8560_H__
#include <linux/config.h>
-#include <linux/serial.h>
#include <platforms/85xx/sbc85xx.h>
+
+#define CPM_MAP_ADDR (CCSRBAR + MPC85xx_CPM_OFFSET)
#ifdef CONFIG_SERIAL_MANY_PORTS
#define RS_TABLE_SIZE 64
#define BASE_BAUD ( 1843200 / 16 )
#ifdef CONFIG_SERIAL_DETECT_IRQ
-#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ)
+#define STD_COM_FLAGS (ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ)
#else
-#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST)
+#define STD_COM_FLAGS (ASYNC_SKIP_TEST)
#endif
-
+
#define STD_SERIAL_PORT_DFNS \
{ 0, BASE_BAUD, UARTA_ADDR, MPC85xx_IRQ_EXT9, STD_COM_FLAGS, /* ttyS0 */ \
iomem_base: (u8 *)UARTA_ADDR, \
/* Internal interrupts are all Level Sensitive, and Positive Polarity */
static u_char sbc8560_openpic_initsenses[] __initdata = {
- (IRQ_POLARITY_POSITIVE), /* Internal 0: L2 Cache */
- (IRQ_POLARITY_POSITIVE), /* Internal 1: ECM */
- (IRQ_POLARITY_POSITIVE), /* Internal 2: DDR DRAM */
- (IRQ_POLARITY_POSITIVE), /* Internal 3: LBIU */
- (IRQ_POLARITY_POSITIVE), /* Internal 4: DMA 0 */
- (IRQ_POLARITY_POSITIVE), /* Internal 5: DMA 1 */
- (IRQ_POLARITY_POSITIVE), /* Internal 6: DMA 2 */
- (IRQ_POLARITY_POSITIVE), /* Internal 7: DMA 3 */
- (IRQ_POLARITY_POSITIVE), /* Internal 8: PCI/PCI-X */
- (IRQ_POLARITY_POSITIVE), /* Internal 9: RIO Inbound Port Write Error */
- (IRQ_POLARITY_POSITIVE), /* Internal 10: RIO Doorbell Inbound */
- (IRQ_POLARITY_POSITIVE), /* Internal 11: RIO Outbound Message */
- (IRQ_POLARITY_POSITIVE), /* Internal 12: RIO Inbound Message */
- (IRQ_POLARITY_POSITIVE), /* Internal 13: TSEC 0 Transmit */
- (IRQ_POLARITY_POSITIVE), /* Internal 14: TSEC 0 Receive */
- (IRQ_POLARITY_POSITIVE), /* Internal 15: Unused */
- (IRQ_POLARITY_POSITIVE), /* Internal 16: Unused */
- (IRQ_POLARITY_POSITIVE), /* Internal 17: Unused */
- (IRQ_POLARITY_POSITIVE), /* Internal 18: TSEC 0 Receive/Transmit Error */
- (IRQ_POLARITY_POSITIVE), /* Internal 19: TSEC 1 Transmit */
- (IRQ_POLARITY_POSITIVE), /* Internal 20: TSEC 1 Receive */
- (IRQ_POLARITY_POSITIVE), /* Internal 21: Unused */
- (IRQ_POLARITY_POSITIVE), /* Internal 22: Unused */
- (IRQ_POLARITY_POSITIVE), /* Internal 23: Unused */
- (IRQ_POLARITY_POSITIVE), /* Internal 24: TSEC 1 Receive/Transmit Error */
- (IRQ_POLARITY_POSITIVE), /* Internal 25: Fast Ethernet */
- (IRQ_POLARITY_POSITIVE), /* Internal 26: DUART */
- (IRQ_POLARITY_POSITIVE), /* Internal 27: I2C */
- (IRQ_POLARITY_POSITIVE), /* Internal 28: Performance Monitor */
- (IRQ_POLARITY_POSITIVE), /* Internal 29: Unused */
- (IRQ_POLARITY_POSITIVE), /* Internal 30: CPM */
- (IRQ_POLARITY_POSITIVE), /* Internal 31: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 0: L2 Cache */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 1: ECM */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 2: DDR DRAM */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 3: LBIU */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 4: DMA 0 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 5: DMA 1 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 6: DMA 2 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 7: DMA 3 */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 8: PCI/PCI-X */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 9: RIO Inbound Port Write Error */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 10: RIO Doorbell Inbound */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 11: RIO Outbound Message */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 12: RIO Inbound Message */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 13: TSEC 0 Transmit */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 14: TSEC 0 Receive */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 15: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 16: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 17: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 18: TSEC 0 Receive/Transmit Error */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 19: TSEC 1 Transmit */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 20: TSEC 1 Receive */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 21: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 22: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 23: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 24: TSEC 1 Receive/Transmit Error */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 25: Fast Ethernet */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 26: DUART */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 27: I2C */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 28: Performance Monitor */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 29: Unused */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 30: CPM */
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* Internal 31: Unused */
0x0, /* External 0: */
0x0, /* External 1: */
#if defined(CONFIG_PCI)
--- /dev/null
+/*
+ * arch/ppc/platforms/85xx/sbc85xx.h
+ *
+ * WindRiver PowerQUICC III SBC85xx common board definitions
+ *
+ * Copyright 2003 Motorola Inc.
+ * Copyright 2004 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __PLATFORMS_85XX_SBC85XX_H__
+#define __PLATFORMS_85XX_SBC85XX_H__
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/seq_file.h>
+#include <asm/ppcboot.h>
+
+#define BOARD_CCSRBAR ((uint)0xff700000)
+#define CCSRBAR_SIZE ((uint)1024*1024)
+
+#define BCSR_ADDR ((uint)0xfc000000)
+#define BCSR_SIZE ((uint)(16 * 1024 * 1024))
+
+#define UARTA_ADDR (BCSR_ADDR + 0x00700000)
+#define UARTB_ADDR (BCSR_ADDR + 0x00800000)
+#define RTC_DEVICE_ADDR (BCSR_ADDR + 0x00900000)
+#define EEPROM_ADDR (BCSR_ADDR + 0x00b00000)
+
+extern int sbc8560_show_cpuinfo(struct seq_file *m);
+extern void sbc8560_init_IRQ(void) __init;
+
+/* PCI interrupt controller */
+#define PIRQA MPC85xx_IRQ_EXT1
+#define PIRQB MPC85xx_IRQ_EXT2
+#define PIRQC MPC85xx_IRQ_EXT3
+#define PIRQD MPC85xx_IRQ_EXT4
+
+#define MPC85XX_PCI1_LOWER_IO 0x00000000
+#define MPC85XX_PCI1_UPPER_IO 0x00ffffff
+
+#define MPC85XX_PCI1_LOWER_MEM 0x80000000
+#define MPC85XX_PCI1_UPPER_MEM 0x9fffffff
+
+#define MPC85XX_PCI1_IO_BASE 0xe2000000
+#define MPC85XX_PCI1_MEM_OFFSET 0x00000000
+
+#define MPC85XX_PCI1_IO_SIZE 0x01000000
+
+#endif /* __PLATFORMS_85XX_SBC85XX_H__ */
--- /dev/null
+/*
+ * arch/ppc/platforms/lite5200.c
+ *
+ * Platform support file for the Freescale LITE5200 based on MPC52xx.
+ * A maximum of this file should be moved to syslib/mpc52xx_?????
+ * so that new platform based on MPC52xx need a minimal platform file
+ * ( avoid code duplication )
+ *
+ *
+ * Maintainer : Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Based on the 2.4 code written by Kent Borg,
+ * Dale Farnsworth <dale.farnsworth@mvista.com> and
+ * Wolfgang Denk <wd@denx.de>
+ *
+ * Copyright 2004 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright 2003 Motorola Inc.
+ * Copyright 2003 MontaVista Software Inc.
+ * Copyright 2003 DENX Software Engineering (wd@denx.de)
+ *
+ * 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/config.h>
+#include <linux/initrd.h>
+#include <linux/seq_file.h>
+#include <linux/kdev_t.h>
+#include <linux/root_dev.h>
+#include <linux/console.h>
+
+#include <asm/bootinfo.h>
+#include <asm/io.h>
+#include <asm/ocp.h>
+#include <asm/mpc52xx.h>
+
+
+/* Board data given by U-Boot */
+bd_t __res;
+EXPORT_SYMBOL(__res); /* For modules */
+
+
+/* ======================================================================== */
+/* OCP device definition */
+/* For board/shared resources like PSCs */
+/* ======================================================================== */
+/* Be sure not to load conficting devices : e.g. loading the UART drivers for
+ * PSC1 and then also loading a AC97 for this same PSC.
+ * For details about how to create an entry, look in the doc of the concerned
+ * driver ( eg drivers/serial/mpc52xx_uart.c for the PSC in uart mode )
+ */
+
+struct ocp_def board_ocp[] = {
+ {
+ .vendor = OCP_VENDOR_FREESCALE,
+ .function = OCP_FUNC_PSC_UART,
+ .index = 0,
+ .paddr = MPC52xx_PSC1,
+ .irq = MPC52xx_PSC1_IRQ,
+ .pm = OCP_CPM_NA,
+ },
+ { /* Terminating entry */
+ .vendor = OCP_VENDOR_INVALID
+ }
+};
+
+
+/* ======================================================================== */
+/* Platform specific code */
+/* ======================================================================== */
+
+static int
+icecube_show_cpuinfo(struct seq_file *m)
+{
+ seq_printf(m, "machine\t\t: Freescale LITE5200\n");
+ return 0;
+}
+
+static void __init
+icecube_setup_arch(void)
+{
+
+ /* Add board OCP definitions */
+ mpc52xx_add_board_devices(board_ocp);
+}
+
+void __init
+platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7)
+{
+ /* Generic MPC52xx platform initialization */
+ /* TODO Create one and move a max of stuff in it.
+ Put this init in the syslib */
+
+ struct bi_record *bootinfo = find_bootinfo();
+
+ if (bootinfo)
+ parse_bootinfo(bootinfo);
+ else {
+ /* Load the bd_t board info structure */
+ if (r3)
+ memcpy((void*)&__res,(void*)(r3+KERNELBASE),
+ sizeof(bd_t));
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ /* Load the initrd */
+ if (r4) {
+ initrd_start = r4 + KERNELBASE;
+ initrd_end = r5 + KERNELBASE;
+ }
+#endif
+
+ /* Load the command line */
+ if (r6) {
+ *(char *)(r7+KERNELBASE) = 0;
+ strcpy(cmd_line, (char *)(r6+KERNELBASE));
+ }
+ }
+
+ /* BAT setup */
+ mpc52xx_set_bat();
+
+ /* No ISA bus AFAIK */
+ isa_io_base = 0;
+ isa_mem_base = 0;
+
+ /* Setup the ppc_md struct */
+ ppc_md.setup_arch = icecube_setup_arch;
+ ppc_md.show_cpuinfo = icecube_show_cpuinfo;
+ ppc_md.show_percpuinfo = NULL;
+ ppc_md.init_IRQ = mpc52xx_init_irq;
+ ppc_md.get_irq = mpc52xx_get_irq;
+
+ ppc_md.find_end_of_memory = mpc52xx_find_end_of_memory;
+ ppc_md.setup_io_mappings = mpc52xx_map_io;
+
+ ppc_md.restart = mpc52xx_restart;
+ ppc_md.power_off = mpc52xx_power_off;
+ ppc_md.halt = mpc52xx_halt;
+
+ /* No time keeper on the IceCube */
+ ppc_md.time_init = NULL;
+ ppc_md.get_rtc_time = NULL;
+ ppc_md.set_rtc_time = NULL;
+
+ ppc_md.calibrate_decr = mpc52xx_calibrate_decr;
+#ifdef CONFIG_SERIAL_TEXT_DEBUG
+ ppc_md.progress = mpc52xx_progress;
+#endif
+}
+
--- /dev/null
+/*
+ * arch/ppc/platforms/lite5200.h
+ *
+ * Definitions for Freescale LITE5200 : MPC52xx Standard Development
+ * Platform board support
+ *
+ * Maintainer : Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.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.
+ */
+
+#ifndef __PLATFORMS_LITE5200_H__
+#define __PLATFORMS_LITE5200_H__
+
+/* Serial port used for low-level debug */
+#define MPC52xx_PF_CONSOLE_PORT 0 /* PSC1 */
+
+
+#endif /* __PLATFORMS_LITE5200_H__ */
--- /dev/null
+/*
+ * arch/ppc/platforms/mpc5200.c
+ *
+ * OCP Definitions for the boards based on MPC5200 processor. Contains
+ * definitions for every common peripherals. (Mostly all but PSCs)
+ *
+ * Maintainer : Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Copyright 2004 Sylvain Munaut <tnt@246tNt.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 <asm/ocp.h>
+#include <asm/mpc52xx.h>
+
+/* Here is the core_ocp struct.
+ * With all the devices common to all board. Even if port multiplexing is
+ * not setup for them (if the user don't want them, just don't select the
+ * config option). The potentially conflicting devices (like PSCs) goes in
+ * board specific file.
+ */
+struct ocp_def core_ocp[] = {
+ { /* Terminating entry */
+ .vendor = OCP_VENDOR_INVALID
+ }
+};
int len;
/* For PCI<->PCI bridges or CardBus bridges, we go down */
- class_code = (unsigned int *) get_property(node, "class-code", 0);
+ class_code = (unsigned int *) get_property(node, "class-code", NULL);
if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI &&
(*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS))
continue;
* (iBook, G4, new IMacs, and all the recent Apple machines).
* It contains 3 controllers in one ASIC.
*
- * The U3 is the bridge used on G5 machines. It contains on
+ * The U3 is the bridge used on G5 machines. It contains an
* AGP bus which is dealt with the old UniNorth access routines
- * and an HyperTransport bus which uses its own set of access
+ * and a HyperTransport bus which uses its own set of access
* functions.
*/
continue;
if (0x0035 != *prop)
continue;
- prop = (u32 *)get_property(nec, "reg", 0);
+ prop = (u32 *)get_property(nec, "reg", NULL);
if (prop == NULL)
continue;
devfn = (prop[0] >> 8) & 0xff;
* any of the 0xfxxxxxxx "fine" memory regions to /ht.
* We need to fix that sooner or later by either parsing all child "ranges"
* properties or figuring out the U3 address space decoding logic and
- * then read it's configuration register (if any).
+ * then read its configuration register (if any).
*/
hose->io_base_phys = 0xf4000000 + 0x00400000;
hose->io_base_virt = ioremap(hose->io_base_phys, 0x00400000);
* default, gmac is not powered up, and so will be absent
* from the kernel initial PCI lookup.
*
- * Should be replaced by 2.4 new PCI mecanisms and really
- * regiser the device.
+ * Should be replaced by 2.4 new PCI mechanisms and really
+ * register the device.
*/
pci_read_config_word(dev, PCI_COMMAND, &cmd);
cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE;
/* reset the entry point so if we get another intr we won't
* try to startup again */
out_be32(psurge_start, 0x100);
- if (request_irq(30, psurge_primary_intr, SA_INTERRUPT, "primary IPI", 0))
+ if (request_irq(30, psurge_primary_intr, SA_INTERRUPT, "primary IPI", NULL))
printk(KERN_ERR "Couldn't get primary IPI interrupt");
}
--- /dev/null
+/*
+ * A collection of structures, addresses, and values associated with
+ * the Motorola MPC8260ADS/MPC8266ADS-PCI boards.
+ * Copied from the RPX-Classic and SBS8260 stuff.
+ *
+ * Copyright (c) 2001 Dan Malek (dan@mvista.com)
+ */
+#ifdef __KERNEL__
+#ifndef __MACH_ADS8260_DEFS
+#define __MACH_ADS8260_DEFS
+
+#include <linux/config.h>
+
+#include <asm/ppcboot.h>
+
+/* Memory map is configured by the PROM startup.
+ * We just map a few things we need. The CSR is actually 4 byte-wide
+ * registers that can be accessed as 8-, 16-, or 32-bit values.
+ */
+#define CPM_MAP_ADDR ((uint)0xf0000000)
+#define BCSR_ADDR ((uint)0xf4500000)
+#define BCSR_SIZE ((uint)(32 * 1024))
+
+#define BOOTROM_RESTART_ADDR ((uint)0xff000104)
+
+/* The ADS8260 has 16, 32-bit wide control/status registers, accessed
+ * only on word boundaries.
+ * Not all are used (yet), or are interesting to us (yet).
+ */
+
+/* Things of interest in the CSR.
+*/
+#define BCSR0_LED0 ((uint)0x02000000) /* 0 == on */
+#define BCSR0_LED1 ((uint)0x01000000) /* 0 == on */
+#define BCSR1_FETHIEN ((uint)0x08000000) /* 0 == enable */
+#define BCSR1_FETH_RST ((uint)0x04000000) /* 0 == reset */
+#define BCSR1_RS232_EN1 ((uint)0x02000000) /* 0 == enable */
+#define BCSR1_RS232_EN2 ((uint)0x01000000) /* 0 == enable */
+
+#define PHY_INTERRUPT SIU_INT_IRQ7
+
+#ifdef CONFIG_PCI
+/* PCI interrupt controller */
+#define PCI_INT_STAT_REG 0xF8200000
+#define PCI_INT_MASK_REG 0xF8200004
+#define PIRQA (NR_SIU_INTS + 0)
+#define PIRQB (NR_SIU_INTS + 1)
+#define PIRQC (NR_SIU_INTS + 2)
+#define PIRQD (NR_SIU_INTS + 3)
+
+/*
+ * PCI memory map definitions for MPC8266ADS-PCI.
+ *
+ * processor view
+ * local address PCI address target
+ * 0x80000000-0x9FFFFFFF 0x80000000-0x9FFFFFFF PCI mem with prefetch
+ * 0xA0000000-0xBFFFFFFF 0xA0000000-0xBFFFFFFF PCI mem w/o prefetch
+ * 0xF4000000-0xF7FFFFFF 0x00000000-0x03FFFFFF PCI IO
+ *
+ * PCI master view
+ * local address PCI address target
+ * 0x00000000-0x1FFFFFFF 0x00000000-0x1FFFFFFF MPC8266 local memory
+ */
+
+/* window for a PCI master to access MPC8266 memory */
+#define PCI_SLV_MEM_LOCAL 0x00000000 /* Local base */
+#define PCI_SLV_MEM_BUS 0x00000000 /* PCI base */
+
+/* window for the processor to access PCI memory with prefetching */
+#define PCI_MSTR_MEM_LOCAL 0x80000000 /* Local base */
+#define PCI_MSTR_MEM_BUS 0x80000000 /* PCI base */
+#define PCI_MSTR_MEM_SIZE 0x20000000 /* 512MB */
+
+/* window for the processor to access PCI memory without prefetching */
+#define PCI_MSTR_MEMIO_LOCAL 0xA0000000 /* Local base */
+#define PCI_MSTR_MEMIO_BUS 0xA0000000 /* PCI base */
+#define PCI_MSTR_MEMIO_SIZE 0x20000000 /* 512MB */
+
+/* window for the processor to access PCI I/O */
+#define PCI_MSTR_IO_LOCAL 0xF4000000 /* Local base */
+#define PCI_MSTR_IO_BUS 0x00000000 /* PCI base */
+#define PCI_MSTR_IO_SIZE 0x04000000 /* 64MB */
+
+#define _IO_BASE PCI_MSTR_IO_LOCAL
+#define _ISA_MEM_BASE PCI_MSTR_MEMIO_LOCAL
+#define PCI_DRAM_OFFSET PCI_SLV_MEM_BUS
+#endif /* CONFIG_PCI */
+
+#endif /* __MACH_ADS8260_DEFS */
+#endif /* __KERNEL__ */
--- /dev/null
+/*
+ * arch/ppc/platforms/pq2ads_setup.c
+ *
+ * PQ2ADS platform support
+ *
+ * Author: Kumar Gala <kumar.gala@freescale.com>
+ * Derived from: est8260_setup.c by Allen Curtis
+ *
+ * Copyright 2004 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/seq_file.h>
+
+#include <asm/mpc8260.h>
+#include <asm/machdep.h>
+
+static void (*callback_setup_arch)(void);
+
+extern unsigned char __res[sizeof(bd_t)];
+
+extern void m8260_init(unsigned long r3, unsigned long r4,
+ unsigned long r5, unsigned long r6, unsigned long r7);
+
+static int
+pq2ads_show_cpuinfo(struct seq_file *m)
+{
+ bd_t *binfo = (bd_t *)__res;
+
+ seq_printf(m, "vendor\t\t: Motorola\n"
+ "machine\t\t: PQ2 ADS PowerPC\n"
+ "\n"
+ "mem size\t\t: 0x%08lx\n"
+ "console baud\t\t: %ld\n"
+ "\n",
+ binfo->bi_memsize,
+ binfo->bi_baudrate);
+ return 0;
+}
+
+static void __init
+pq2ads_setup_arch(void)
+{
+ printk("PQ2 ADS Port\n");
+ callback_setup_arch();
+ *(volatile uint *)(BCSR_ADDR + 4) &= ~BCSR1_RS232_EN2;
+}
+
+void __init
+platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7)
+{
+ /* Generic 8260 platform initialization */
+ m8260_init(r3, r4, r5, r6, r7);
+
+ /* Anything special for this platform */
+ ppc_md.show_cpuinfo = pq2ads_show_cpuinfo;
+
+ callback_setup_arch = ppc_md.setup_arch;
+ ppc_md.setup_arch = pq2ads_setup_arch;
+}
}
/* Check the first PCI device to see if it is a Raven. */
- early_read_config_dword(0, 0, 0, PCI_VENDOR_ID, &devid);
+ early_read_config_dword(NULL, 0, 0, PCI_VENDOR_ID, &devid);
switch (devid & 0xffff0000) {
case MPIC_RAVEN_ID:
/* Read the memory base register. */
- early_read_config_dword(0, 0, 0, PCI_BASE_ADDRESS_1, &pci_membase);
+ early_read_config_dword(NULL, 0, 0, PCI_BASE_ADDRESS_1, &pci_membase);
if (pci_membase == 0) {
OpenPIC_Addr = NULL;
!(n--) ) return res->Devices+i;
#undef Dev
}
- return 0;
+ return NULL;
}
PPC_DEVICE __init *residual_find_device_id(unsigned long BusMask,
!(n--) ) return res->Devices+i;
#undef Dev
}
- return 0;
+ return NULL;
}
PnP_TAG_PACKET *PnP_find_packet(unsigned char *p,
int n)
{
unsigned mask, masked_tag, size;
- if(!p) return 0;
+ if(!p) return NULL;
if (tag_type(packet_tag)) mask=0xff; else mask=0xF8;
masked_tag = packet_tag&mask;
for(; *p != END_TAG; p+=size) {
else
size=tag_small_count(*p)+1;
}
- return 0; /* not found */
+ return NULL; /* not found */
}
PnP_TAG_PACKET __init *PnP_find_small_vendor_packet(unsigned char *p,
return (PnP_TAG_PACKET *) p;
next = 1;
};
- return 0; /* not found */
+ return NULL; /* not found */
}
PnP_TAG_PACKET __init *PnP_find_large_vendor_packet(unsigned char *p,
return (PnP_TAG_PACKET *) p;
next = 1;
};
- return 0; /* not found */
+ return NULL; /* not found */
}
#ifdef CONFIG_PROC_PREPRESIDUAL
--- /dev/null
+/*
+ * arch/ppc/platforms/rpx8260.c
+ *
+ * RPC EP8260 platform support
+ *
+ * Author: Dan Malek <dan@embeddededge.com>
+ * Derived from: pq2ads_setup.c by Kumar
+ *
+ * Copyright 2004 Embedded Edge, LLC
+ *
+ * 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/config.h>
+#include <linux/seq_file.h>
+
+#include <asm/mpc8260.h>
+#include <asm/machdep.h>
+
+static void (*callback_setup_arch)(void);
+
+extern unsigned char __res[sizeof(bd_t)];
+
+extern void m8260_init(unsigned long r3, unsigned long r4,
+ unsigned long r5, unsigned long r6, unsigned long r7);
+
+static int
+ep8260_show_cpuinfo(struct seq_file *m)
+{
+ bd_t *binfo = (bd_t *)__res;
+
+ seq_printf(m, "vendor\t\t: RPC\n"
+ "machine\t\t: EP8260 PPC\n"
+ "\n"
+ "mem size\t\t: 0x%08x\n"
+ "console baud\t\t: %d\n"
+ "\n",
+ binfo->bi_memsize,
+ binfo->bi_baudrate);
+ return 0;
+}
+
+static void __init
+ep8260_setup_arch(void)
+{
+ printk("RPC EP8260 Port\n");
+ callback_setup_arch();
+}
+
+void __init
+platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7)
+{
+ /* Generic 8260 platform initialization */
+ m8260_init(r3, r4, r5, r6, r7);
+
+ /* Anything special for this platform */
+ ppc_md.show_cpuinfo = ep8260_show_cpuinfo;
+
+ callback_setup_arch = ppc_md.setup_arch;
+ ppc_md.setup_arch = ep8260_setup_arch;
+}
--- /dev/null
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <asm/irq.h>
+#include <asm/immap_cpm2.h>
+#include <asm/mpc8260.h>
+#include "cpm2_pic.h"
+
+/* The CPM2 internal interrupt controller. It is usually
+ * the only interrupt controller.
+ * There are two 32-bit registers (high/low) for up to 64
+ * possible interrupts.
+ *
+ * Now, the fun starts.....Interrupt Numbers DO NOT MAP
+ * in a simple arithmetic fashion to mask or pending registers.
+ * That is, interrupt 4 does not map to bit position 4.
+ * We create two tables, indexed by vector number, to indicate
+ * which register to use and which bit in the register to use.
+ */
+static u_char irq_to_siureg[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static u_char irq_to_siubit[] = {
+ 31, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 26, 27, 28, 29, 30,
+ 29, 30, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 31,
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 15, 14, 13, 12, 11, 10, 9, 8,
+ 7, 6, 5, 4, 3, 2, 1, 0
+};
+
+static void cpm2_mask_irq(unsigned int irq_nr)
+{
+ int bit, word;
+ volatile uint *simr;
+
+ bit = irq_to_siubit[irq_nr];
+ word = irq_to_siureg[irq_nr];
+
+ simr = &(cpm2_immr->im_intctl.ic_simrh);
+ ppc_cached_irq_mask[word] &= ~(1 << (31 - bit));
+ simr[word] = ppc_cached_irq_mask[word];
+}
+
+static void cpm2_unmask_irq(unsigned int irq_nr)
+{
+ int bit, word;
+ volatile uint *simr;
+
+ bit = irq_to_siubit[irq_nr];
+ word = irq_to_siureg[irq_nr];
+
+ simr = &(cpm2_immr->im_intctl.ic_simrh);
+ ppc_cached_irq_mask[word] |= (1 << (31 - bit));
+ simr[word] = ppc_cached_irq_mask[word];
+}
+
+static void cpm2_mask_and_ack(unsigned int irq_nr)
+{
+ int bit, word;
+ volatile uint *simr, *sipnr;
+
+ bit = irq_to_siubit[irq_nr];
+ word = irq_to_siureg[irq_nr];
+
+ simr = &(cpm2_immr->im_intctl.ic_simrh);
+ sipnr = &(cpm2_immr->im_intctl.ic_sipnrh);
+ ppc_cached_irq_mask[word] &= ~(1 << (31 - bit));
+ simr[word] = ppc_cached_irq_mask[word];
+ sipnr[word] = 1 << (31 - bit);
+}
+
+static void cpm2_end_irq(unsigned int irq_nr)
+{
+ int bit, word;
+ volatile uint *simr;
+
+ if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))
+ && irq_desc[irq_nr].action) {
+
+ bit = irq_to_siubit[irq_nr];
+ word = irq_to_siureg[irq_nr];
+
+ simr = &(cpm2_immr->im_intctl.ic_simrh);
+ ppc_cached_irq_mask[word] |= (1 << (31 - bit));
+ simr[word] = ppc_cached_irq_mask[word];
+ }
+}
+
+struct hw_interrupt_type cpm2_pic = {
+ " CPM2 SIU ",
+ NULL,
+ NULL,
+ cpm2_unmask_irq,
+ cpm2_mask_irq,
+ cpm2_mask_and_ack,
+ cpm2_end_irq,
+ 0
+};
+
+
+int
+cpm2_get_irq(struct pt_regs *regs)
+{
+ int irq;
+ unsigned long bits;
+
+ /* For CPM2, read the SIVEC register and shift the bits down
+ * to get the irq number. */
+ bits = cpm2_immr->im_intctl.ic_sivec;
+ irq = bits >> 26;
+
+ if (irq == 0)
+ return(-1);
+#if 0
+ irq += ppc8260_pic.irq_offset;
+#endif
+ return irq;
+}
+
--- /dev/null
+#ifndef _PPC_KERNEL_CPM2_H
+#define _PPC_KERNEL_CPM2_H
+
+#include <linux/irq.h>
+
+extern struct hw_interrupt_type cpm2_pic;
+
+void cpm2_pic_init(void);
+void cpm2_do_IRQ(struct pt_regs *regs,
+ int cpu);
+int cpm2_get_irq(struct pt_regs *regs);
+
+#endif /* _PPC_KERNEL_CPM2_H */
--- /dev/null
+/*
+ * (C) Copyright 2003
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * (C) Copyright 2004 Red Hat, Inc.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/machdep.h>
+#include <asm/pci-bridge.h>
+#include <asm/immap_cpm2.h>
+#include <asm/mpc8260.h>
+
+#include "m8260_pci.h"
+
+
+/* PCI bus configuration registers.
+ */
+
+static void __init m8260_setup_pci(struct pci_controller *hose)
+{
+ volatile cpm2_map_t *immap = cpm2_immr;
+ unsigned long pocmr;
+ u16 tempShort;
+
+#ifndef CONFIG_ATC /* already done in U-Boot */
+ /*
+ * Setting required to enable IRQ1-IRQ7 (SIUMCR [DPPC]),
+ * and local bus for PCI (SIUMCR [LBPC]).
+ */
+ immap->im_siu_conf.siu_82xx.sc_siumcr = 0x00640000;
+#endif
+
+ /* Make PCI lowest priority */
+ /* Each 4 bits is a device bus request and the MS 4bits
+ is highest priority */
+ /* Bus 4bit value
+ --- ----------
+ CPM high 0b0000
+ CPM middle 0b0001
+ CPM low 0b0010
+ PCI reguest 0b0011
+ Reserved 0b0100
+ Reserved 0b0101
+ Internal Core 0b0110
+ External Master 1 0b0111
+ External Master 2 0b1000
+ External Master 3 0b1001
+ The rest are reserved */
+ immap->im_siu_conf.siu_82xx.sc_ppc_alrh = 0x61207893;
+
+ /* Park bus on core while modifying PCI Bus accesses */
+ immap->im_siu_conf.siu_82xx.sc_ppc_acr = 0x6;
+
+ /*
+ * Set up master window that allows the CPU to access PCI space. This
+ * window is set up using the first SIU PCIBR registers.
+ */
+ immap->im_memctl.memc_pcimsk0 = MPC826x_PCI_MASK;
+ immap->im_memctl.memc_pcibr0 = MPC826x_PCI_BASE | PCIBR_ENABLE;
+
+ /* Disable machine check on no response or target abort */
+ immap->im_pci.pci_emr = cpu_to_le32(0x1fe7);
+ /* Release PCI RST (by default the PCI RST signal is held low) */
+ immap->im_pci.pci_gcr = cpu_to_le32(PCIGCR_PCI_BUS_EN);
+
+ /* give it some time */
+ mdelay(1);
+
+ /*
+ * Set up master window that allows the CPU to access PCI Memory (prefetch)
+ * space. This window is set up using the first set of Outbound ATU registers.
+ */
+ immap->im_pci.pci_potar0 = cpu_to_le32(MPC826x_PCI_LOWER_MEM >> 12);
+ immap->im_pci.pci_pobar0 = cpu_to_le32((MPC826x_PCI_LOWER_MEM - MPC826x_PCI_MEM_OFFSET) >> 12);
+ pocmr = ((MPC826x_PCI_UPPER_MEM - MPC826x_PCI_LOWER_MEM) >> 12) ^ 0xfffff;
+ immap->im_pci.pci_pocmr0 = cpu_to_le32(pocmr | POCMR_ENABLE | POCMR_PREFETCH_EN);
+
+ /*
+ * Set up master window that allows the CPU to access PCI Memory (non-prefetch)
+ * space. This window is set up using the second set of Outbound ATU registers.
+ */
+ immap->im_pci.pci_potar1 = cpu_to_le32(MPC826x_PCI_LOWER_MMIO >> 12);
+ immap->im_pci.pci_pobar1 = cpu_to_le32((MPC826x_PCI_LOWER_MMIO - MPC826x_PCI_MMIO_OFFSET) >> 12);
+ pocmr = ((MPC826x_PCI_UPPER_MMIO - MPC826x_PCI_LOWER_MMIO) >> 12) ^ 0xfffff;
+ immap->im_pci.pci_pocmr1 = cpu_to_le32(pocmr | POCMR_ENABLE);
+
+ /*
+ * Set up master window that allows the CPU to access PCI IO space. This window
+ * is set up using the third set of Outbound ATU registers.
+ */
+ immap->im_pci.pci_potar2 = cpu_to_le32(MPC826x_PCI_IO_BASE >> 12);
+ immap->im_pci.pci_pobar2 = cpu_to_le32(MPC826x_PCI_LOWER_IO >> 12);
+ pocmr = ((MPC826x_PCI_UPPER_IO - MPC826x_PCI_LOWER_IO) >> 12) ^ 0xfffff;
+ immap->im_pci.pci_pocmr2 = cpu_to_le32(pocmr | POCMR_ENABLE | POCMR_PCI_IO);
+
+ /*
+ * Set up slave window that allows PCI masters to access MPC826x local memory.
+ * This window is set up using the first set of Inbound ATU registers
+ */
+
+ immap->im_pci.pci_pitar0 = cpu_to_le32(MPC826x_PCI_SLAVE_MEM_LOCAL >> 12);
+ immap->im_pci.pci_pibar0 = cpu_to_le32(MPC826x_PCI_SLAVE_MEM_BUS >> 12);
+ pocmr = ((MPC826x_PCI_SLAVE_MEM_SIZE-1) >> 12) ^ 0xfffff;
+ immap->im_pci.pci_picmr0 = cpu_to_le32(pocmr | PICMR_ENABLE | PICMR_PREFETCH_EN);
+
+ /* See above for description - puts PCI request as highest priority */
+ immap->im_siu_conf.siu_82xx.sc_ppc_alrh = 0x03124567;
+
+ /* Park the bus on the PCI */
+ immap->im_siu_conf.siu_82xx.sc_ppc_acr = PPC_ACR_BUS_PARK_PCI;
+
+ /* Host mode - specify the bridge as a host-PCI bridge */
+ early_write_config_word(hose, 0, 0, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_HOST);
+
+ /* Enable the host bridge to be a master on the PCI bus, and to act as a PCI memory target */
+ early_read_config_word(hose, 0, 0, PCI_COMMAND, &tempShort);
+ early_write_config_word(hose, 0, 0, PCI_COMMAND,
+ tempShort | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
+}
+
+void __init m8260_find_bridges(void)
+{
+ extern int pci_assign_all_busses;
+ struct pci_controller * hose;
+
+ pci_assign_all_busses = 1;
+
+ hose = pcibios_alloc_controller();
+
+ if (!hose)
+ return;
+
+ ppc_md.pci_swizzle = common_swizzle;
+
+ hose->first_busno = 0;
+ hose->bus_offset = 0;
+ hose->last_busno = 0xff;
+
+ setup_m8260_indirect_pci(hose,
+ (unsigned long)&cpm2_immr->im_pci.pci_cfg_addr,
+ (unsigned long)&cpm2_immr->im_pci.pci_cfg_data);
+
+ m8260_setup_pci(hose);
+ hose->pci_mem_offset = MPC826x_PCI_MEM_OFFSET;
+
+ isa_io_base =
+ (unsigned long) ioremap(MPC826x_PCI_IO_BASE,
+ MPC826x_PCI_IO_SIZE);
+ hose->io_base_virt = (void *) isa_io_base;
+
+ /* setup resources */
+ pci_init_resource(&hose->mem_resources[0],
+ MPC826x_PCI_LOWER_MEM,
+ MPC826x_PCI_UPPER_MEM,
+ IORESOURCE_MEM|IORESOURCE_PREFETCH, "PCI prefetchable memory");
+
+ pci_init_resource(&hose->mem_resources[1],
+ MPC826x_PCI_LOWER_MMIO,
+ MPC826x_PCI_UPPER_MMIO,
+ IORESOURCE_MEM, "PCI memory");
+
+ pci_init_resource(&hose->io_resource,
+ MPC826x_PCI_LOWER_IO,
+ MPC826x_PCI_UPPER_IO,
+ IORESOURCE_IO, "PCI I/O");
+}
--- /dev/null
+
+#ifndef _PPC_KERNEL_M8260_PCI_H
+#define _PPC_KERNEL_M8260_PCI_H
+
+#include <asm/m8260_pci.h>
+
+/*
+ * Local->PCI map (from CPU) controlled by
+ * MPC826x master window
+ *
+ * 0x80000000 - 0xBFFFFFFF Total CPU2PCI space PCIBR0
+ *
+ * 0x80000000 - 0x9FFFFFFF PCI Mem with prefetch (Outbound ATU #1)
+ * 0xA0000000 - 0xAFFFFFFF PCI Mem w/o prefetch (Outbound ATU #2)
+ * 0xB0000000 - 0xB0FFFFFF 32-bit PCI IO (Outbound ATU #3)
+ *
+ * PCI->Local map (from PCI)
+ * MPC826x slave window controlled by
+ *
+ * 0x00000000 - 0x07FFFFFF MPC826x local memory (Inbound ATU #1)
+ */
+
+/*
+ * Slave window that allows PCI masters to access MPC826x local memory.
+ * This window is set up using the first set of Inbound ATU registers
+ */
+
+#ifndef MPC826x_PCI_SLAVE_MEM_LOCAL
+#define MPC826x_PCI_SLAVE_MEM_LOCAL (((struct bd_info *)__res)->bi_memstart)
+#define MPC826x_PCI_SLAVE_MEM_BUS (((struct bd_info *)__res)->bi_memstart)
+#define MPC826x_PCI_SLAVE_MEM_SIZE (((struct bd_info *)__res)->bi_memsize)
+#endif
+
+/*
+ * This is the window that allows the CPU to access PCI address space.
+ * It will be setup with the SIU PCIBR0 register. All three PCI master
+ * windows, which allow the CPU to access PCI prefetch, non prefetch,
+ * and IO space (see below), must all fit within this window.
+ */
+#ifndef MPC826x_PCI_BASE
+#define MPC826x_PCI_BASE 0x80000000
+#define MPC826x_PCI_MASK 0xc0000000
+#endif
+
+#ifndef MPC826x_PCI_LOWER_MEM
+#define MPC826x_PCI_LOWER_MEM 0x80000000
+#define MPC826x_PCI_UPPER_MEM 0x9fffffff
+#define MPC826x_PCI_MEM_OFFSET 0x00000000
+#endif
+
+#ifndef MPC826x_PCI_LOWER_MMIO
+#define MPC826x_PCI_LOWER_MMIO 0xa0000000
+#define MPC826x_PCI_UPPER_MMIO 0xafffffff
+#define MPC826x_PCI_MMIO_OFFSET 0x00000000
+#endif
+
+#ifndef MPC826x_PCI_LOWER_IO
+#define MPC826x_PCI_LOWER_IO 0x00000000
+#define MPC826x_PCI_UPPER_IO 0x00ffffff
+#define MPC826x_PCI_IO_BASE 0xb0000000
+#define MPC826x_PCI_IO_SIZE 0x01000000
+#endif
+
+#ifndef _IO_BASE
+#define _IO_BASE isa_io_base
+#endif
+
+#ifdef CONFIG_8260_PCI9
+extern void setup_m8260_indirect_pci(struct pci_controller* hose,
+ u32 cfg_addr, u32 cfg_data);
+#else
+#define setup_m8260_indirect_pci setup_indirect_pci
+#endif
+
+#endif /* _PPC_KERNEL_M8260_PCI_H */
volatile cpm2_map_t *immap = cpm2_immr;
/* allocate IDMA dpram */
- dpram_offset = cpm2_dpalloc(sizeof(idma_dpram_t), 64);
- idma_dpram =
- (volatile idma_dpram_t *)&immap->im_dprambase[dpram_offset];
+ dpram_offset = cpm_dpalloc(sizeof(idma_dpram_t), 64);
+ idma_dpram = cpm_dpram_addr(dpram_offset);
/* initialize the IDMA parameter RAM */
memset((void *)idma_dpram, 0, sizeof(idma_dpram_t));
--- /dev/null
+/*
+ * arch/ppc/syslib/mpc52xx_pic.c
+ *
+ * Programmable Interrupt Controller functions for the Freescale MPC52xx
+ * embedded CPU.
+ *
+ *
+ * Maintainer : Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Based on (well, mostly copied from) the code from the 2.4 kernel by
+ * Dale Farnsworth <dfarnsworth@mvista.com> and Kent Borg.
+ *
+ * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2003 Montavista Software, Inc
+ *
+ * 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/stddef.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/stddef.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/mpc52xx.h>
+
+
+static struct mpc52xx_intr *intr;
+static struct mpc52xx_sdma *sdma;
+
+static void
+mpc52xx_ic_disable(unsigned int irq)
+{
+ u32 val;
+
+ if (irq == MPC52xx_IRQ0) {
+ val = in_be32(&intr->ctrl);
+ val &= ~(1 << 11);
+ out_be32(&intr->ctrl, val);
+ }
+ else if (irq < MPC52xx_IRQ1) {
+ BUG();
+ }
+ else if (irq <= MPC52xx_IRQ3) {
+ val = in_be32(&intr->ctrl);
+ val &= ~(1 << (10 - (irq - MPC52xx_IRQ1)));
+ out_be32(&intr->ctrl, val);
+ }
+ else if (irq < MPC52xx_SDMA_IRQ_BASE) {
+ val = in_be32(&intr->main_mask);
+ val |= 1 << (16 - (irq - MPC52xx_MAIN_IRQ_BASE));
+ out_be32(&intr->main_mask, val);
+ }
+ else if (irq < MPC52xx_PERP_IRQ_BASE) {
+ val = in_be32(&sdma->IntMask);
+ val |= 1 << (irq - MPC52xx_SDMA_IRQ_BASE);
+ out_be32(&sdma->IntMask, val);
+ }
+ else {
+ val = in_be32(&intr->per_mask);
+ val |= 1 << (31 - (irq - MPC52xx_PERP_IRQ_BASE));
+ out_be32(&intr->per_mask, val);
+ }
+}
+
+static void
+mpc52xx_ic_enable(unsigned int irq)
+{
+ u32 val;
+
+ if (irq == MPC52xx_IRQ0) {
+ val = in_be32(&intr->ctrl);
+ val |= 1 << 11;
+ out_be32(&intr->ctrl, val);
+ }
+ else if (irq < MPC52xx_IRQ1) {
+ BUG();
+ }
+ else if (irq <= MPC52xx_IRQ3) {
+ val = in_be32(&intr->ctrl);
+ val |= 1 << (10 - (irq - MPC52xx_IRQ1));
+ out_be32(&intr->ctrl, val);
+ }
+ else if (irq < MPC52xx_SDMA_IRQ_BASE) {
+ val = in_be32(&intr->main_mask);
+ val &= ~(1 << (16 - (irq - MPC52xx_MAIN_IRQ_BASE)));
+ out_be32(&intr->main_mask, val);
+ }
+ else if (irq < MPC52xx_PERP_IRQ_BASE) {
+ val = in_be32(&sdma->IntMask);
+ val &= ~(1 << (irq - MPC52xx_SDMA_IRQ_BASE));
+ out_be32(&sdma->IntMask, val);
+ }
+ else {
+ val = in_be32(&intr->per_mask);
+ val &= ~(1 << (31 - (irq - MPC52xx_PERP_IRQ_BASE)));
+ out_be32(&intr->per_mask, val);
+ }
+}
+
+static void
+mpc52xx_ic_ack(unsigned int irq)
+{
+ u32 val;
+
+ /*
+ * Only some irqs are reset here, others in interrupting hardware.
+ */
+
+ switch (irq) {
+ case MPC52xx_IRQ0:
+ val = in_be32(&intr->ctrl);
+ val |= 0x08000000;
+ out_be32(&intr->ctrl, val);
+ break;
+ case MPC52xx_CCS_IRQ:
+ val = in_be32(&intr->enc_status);
+ val |= 0x00000400;
+ out_be32(&intr->enc_status, val);
+ break;
+ case MPC52xx_IRQ1:
+ val = in_be32(&intr->ctrl);
+ val |= 0x04000000;
+ out_be32(&intr->ctrl, val);
+ break;
+ case MPC52xx_IRQ2:
+ val = in_be32(&intr->ctrl);
+ val |= 0x02000000;
+ out_be32(&intr->ctrl, val);
+ break;
+ case MPC52xx_IRQ3:
+ val = in_be32(&intr->ctrl);
+ val |= 0x01000000;
+ out_be32(&intr->ctrl, val);
+ break;
+ default:
+ if (irq >= MPC52xx_SDMA_IRQ_BASE
+ && irq < (MPC52xx_SDMA_IRQ_BASE + MPC52xx_SDMA_IRQ_NUM)) {
+ out_be32(&sdma->IntPend,
+ 1 << (irq - MPC52xx_SDMA_IRQ_BASE));
+ }
+ break;
+ }
+}
+
+static void
+mpc52xx_ic_disable_and_ack(unsigned int irq)
+{
+ mpc52xx_ic_disable(irq);
+ mpc52xx_ic_ack(irq);
+}
+
+static void
+mpc52xx_ic_end(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
+ mpc52xx_ic_enable(irq);
+}
+
+static struct hw_interrupt_type mpc52xx_ic = {
+ "MPC52xx",
+ NULL, /* startup(irq) */
+ NULL, /* shutdown(irq) */
+ mpc52xx_ic_enable, /* enable(irq) */
+ mpc52xx_ic_disable, /* disable(irq) */
+ mpc52xx_ic_disable_and_ack, /* disable_and_ack(irq) */
+ mpc52xx_ic_end, /* end(irq) */
+ 0 /* set_affinity(irq, cpumask) SMP. */
+};
+
+void __init
+mpc52xx_init_irq(void)
+{
+ int i;
+
+ /* Remap the necessary zones */
+ intr = (struct mpc52xx_intr *)
+ ioremap(MPC52xx_INTR, sizeof(struct mpc52xx_intr));
+ sdma = (struct mpc52xx_sdma *)
+ ioremap(MPC52xx_SDMA, sizeof(struct mpc52xx_sdma));
+
+ if ((intr==NULL) || (sdma==NULL))
+ panic("Can't ioremap PIC/SDMA register for init_irq !");
+
+ /* Disable all interrupt sources. */
+ out_be32(&sdma->IntPend, 0xffffffff); /* 1 means clear pending */
+ out_be32(&sdma->IntMask, 0xffffffff); /* 1 means disabled */
+ out_be32(&intr->per_mask, 0x7ffffc00); /* 1 means disabled */
+ out_be32(&intr->main_mask, 0x00010fff); /* 1 means disabled */
+ out_be32(&intr->ctrl,
+ 0x0f000000 | /* clear IRQ 0-3 */
+ 0x00c00000 | /* IRQ0: level-sensitive, active low */
+ 0x00001000 | /* MEE master external enable */
+ 0x00000000 | /* 0 means disable IRQ 0-3 */
+ 0x00000001); /* CEb route critical normally */
+
+ /* Zero a bunch of the priority settings. */
+ out_be32(&intr->per_pri1, 0);
+ out_be32(&intr->per_pri2, 0);
+ out_be32(&intr->per_pri3, 0);
+ out_be32(&intr->main_pri1, 0);
+ out_be32(&intr->main_pri2, 0);
+
+ /* Initialize irq_desc[i].handler's with mpc52xx_ic. */
+ for (i = 0; i < NR_IRQS; i++) {
+ irq_desc[i].handler = &mpc52xx_ic;
+ irq_desc[i].status = IRQ_LEVEL;
+ }
+}
+
+int
+mpc52xx_get_irq(struct pt_regs *regs)
+{
+ u32 status;
+ int irq = -1;
+
+ status = in_be32(&intr->enc_status);
+
+ if (status & 0x00000400) { /* critical */
+ irq = (status >> 8) & 0x3;
+ if (irq == 2) /* high priority peripheral */
+ goto peripheral;
+ irq += MPC52xx_CRIT_IRQ_BASE;
+ }
+ else if (status & 0x00200000) { /* main */
+ irq = (status >> 16) & 0x1f;
+ if (irq == 4) /* low priority peripheral */
+ goto peripheral;
+ irq += MPC52xx_MAIN_IRQ_BASE;
+ }
+ else if (status & 0x20000000) { /* peripheral */
+peripheral:
+ irq = (status >> 24) & 0x1f;
+ if (irq == 0) { /* bestcomm */
+ status = in_be32(&sdma->IntPend);
+ irq = ffs(status) + MPC52xx_SDMA_IRQ_BASE-1;
+ }
+ else
+ irq += MPC52xx_PERP_IRQ_BASE;
+ }
+
+ return irq;
+}
+
--- /dev/null
+/*
+ * arch/ppc/syslib/mpc52xx_common.c
+ *
+ * Common code for the boards based on Freescale MPC52xx embedded CPU.
+ *
+ *
+ * Maintainer : Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Support for other bootloaders than UBoot by Dale Farnsworth
+ * <dfarnsworth@mvista.com>
+ *
+ * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2003 Montavista Software, Inc
+ *
+ * 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/config.h>
+
+#include <asm/time.h>
+#include <asm/mpc52xx.h>
+#include <asm/mpc52xx_psc.h>
+#include <asm/ocp.h>
+#include <asm/ppcboot.h>
+
+extern bd_t __res;
+
+static int core_mult[] = { /* CPU Frequency multiplier, taken */
+ 0, 0, 0, 10, 20, 20, 25, 45, /* from the datasheet used to compute */
+ 30, 55, 40, 50, 0, 60, 35, 0, /* CPU frequency from XLB freq and */
+ 30, 25, 65, 10, 70, 20, 75, 45, /* external jumper config */
+ 0, 55, 40, 50, 80, 60, 35, 0
+};
+
+void
+mpc52xx_restart(char *cmd)
+{
+ struct mpc52xx_gpt* gpt0 = (struct mpc52xx_gpt*) MPC52xx_GPTx(0);
+
+ local_irq_disable();
+
+ /* Turn on the watchdog and wait for it to expire. It effectively
+ does a reset */
+ if (gpt0 != NULL) {
+ out_be32(&gpt0->count, 0x000000ff);
+ out_be32(&gpt0->mode, 0x00009004);
+ } else
+ printk(KERN_ERR "mpc52xx_restart: Unable to ioremap GPT0 registers, -> looping ...");
+
+ while (1);
+}
+
+void
+mpc52xx_halt(void)
+{
+ local_irq_disable();
+
+ while (1);
+}
+
+void
+mpc52xx_power_off(void)
+{
+ /* By default we don't have any way of shut down.
+ If a specific board wants to, it can set the power down
+ code to any hardware implementation dependent code */
+ mpc52xx_halt();
+}
+
+
+void __init
+mpc52xx_set_bat(void)
+{
+ /* Set BAT 2 to map the 0xf0000000 area */
+ /* This mapping is used during mpc52xx_progress,
+ * mpc52xx_find_end_of_memory, and UARTs/GPIO access for debug
+ */
+ mb();
+ mtspr(DBAT2U, 0xf0001ffe);
+ mtspr(DBAT2L, 0xf000002a);
+ mb();
+}
+
+void __init
+mpc52xx_map_io(void)
+{
+ /* Here we only map the MBAR */
+ io_block_mapping(
+ MPC52xx_MBAR_VIRT, MPC52xx_MBAR, MPC52xx_MBAR_SIZE, _PAGE_IO);
+}
+
+
+#ifdef CONFIG_SERIAL_TEXT_DEBUG
+#ifdef MPC52xx_PF_CONSOLE_PORT
+#define MPC52xx_CONSOLE MPC52xx_PSCx(MPC52xx_PF_CONSOLE_PORT)
+#else
+#error "mpc52xx PSC for console not selected"
+#endif
+
+void
+mpc52xx_progress(char *s, unsigned short hex)
+{
+ struct mpc52xx_psc *psc = (struct mpc52xx_psc *)MPC52xx_CONSOLE;
+ char c;
+
+ /* Don't we need to disable serial interrupts ? */
+
+ while ((c = *s++) != 0) {
+ if (c == '\n') {
+ while (!(in_be16(&psc->mpc52xx_psc_status) &
+ MPC52xx_PSC_SR_TXRDY)) ;
+ out_8(&psc->mpc52xx_psc_buffer_8, '\r');
+ }
+ while (!(in_be16(&psc->mpc52xx_psc_status) &
+ MPC52xx_PSC_SR_TXRDY)) ;
+ out_8(&psc->mpc52xx_psc_buffer_8, c);
+ }
+}
+
+#endif /* CONFIG_SERIAL_TEXT_DEBUG */
+
+
+unsigned long __init
+mpc52xx_find_end_of_memory(void)
+{
+ u32 ramsize = __res.bi_memsize;
+
+ /*
+ * if bootloader passed a memsize, just use it
+ * else get size from sdram config registers
+ */
+ if (ramsize == 0) {
+ struct mpc52xx_mmap_ctl *mmap_ctl;
+ u32 sdram_config_0, sdram_config_1;
+
+ /* Temp BAT2 mapping active when this is called ! */
+ mmap_ctl = (struct mpc52xx_mmap_ctl*) MPC52xx_MMAP_CTL;
+
+ sdram_config_0 = in_be32(&mmap_ctl->sdram0);
+ sdram_config_1 = in_be32(&mmap_ctl->sdram1);
+
+ if ((sdram_config_0 & 0x1f) >= 0x13)
+ ramsize = 1 << ((sdram_config_0 & 0xf) + 17);
+
+ if (((sdram_config_1 & 0x1f) >= 0x13) &&
+ ((sdram_config_1 & 0xfff00000) == ramsize))
+ ramsize += 1 << ((sdram_config_1 & 0xf) + 17);
+
+ iounmap(mmap_ctl);
+ }
+
+ return ramsize;
+}
+
+void __init
+mpc52xx_calibrate_decr(void)
+{
+ int current_time, previous_time;
+ int tbl_start, tbl_end;
+ unsigned int xlbfreq, cpufreq, ipbfreq, pcifreq, divisor;
+
+ xlbfreq = __res.bi_busfreq;
+ /* if bootloader didn't pass bus frequencies, calculate them */
+ if (xlbfreq == 0) {
+ /* Get RTC & Clock manager modules */
+ struct mpc52xx_rtc *rtc;
+ struct mpc52xx_cdm *cdm;
+
+ rtc = (struct mpc52xx_rtc*)
+ ioremap(MPC52xx_RTC, sizeof(struct mpc52xx_rtc));
+ cdm = (struct mpc52xx_cdm*)
+ ioremap(MPC52xx_CDM, sizeof(struct mpc52xx_cdm));
+
+ if ((rtc==NULL) || (cdm==NULL))
+ panic("Can't ioremap RTC/CDM while computing bus freq");
+
+ /* Count bus clock during 1/64 sec */
+ out_be32(&rtc->dividers, 0x8f1f0000); /* Set RTC 64x faster */
+ previous_time = in_be32(&rtc->time);
+ while ((current_time = in_be32(&rtc->time)) == previous_time) ;
+ tbl_start = get_tbl();
+ previous_time = current_time;
+ while ((current_time = in_be32(&rtc->time)) == previous_time) ;
+ tbl_end = get_tbl();
+ out_be32(&rtc->dividers, 0xffff0000); /* Restore RTC */
+
+ /* Compute all frequency from that & CDM settings */
+ xlbfreq = (tbl_end - tbl_start) << 8;
+ cpufreq = (xlbfreq * core_mult[in_be32(&cdm->rstcfg)&0x1f])/10;
+ ipbfreq = (in_8(&cdm->ipb_clk_sel) & 1) ?
+ xlbfreq / 2 : xlbfreq;
+ switch (in_8(&cdm->pci_clk_sel) & 3) {
+ case 0:
+ pcifreq = ipbfreq;
+ break;
+ case 1:
+ pcifreq = ipbfreq / 2;
+ break;
+ default:
+ pcifreq = xlbfreq / 4;
+ break;
+ }
+ __res.bi_busfreq = xlbfreq;
+ __res.bi_intfreq = cpufreq;
+ __res.bi_ipbfreq = ipbfreq;
+ __res.bi_pcifreq = pcifreq;
+
+ /* Release mapping */
+ iounmap((void*)rtc);
+ iounmap((void*)cdm);
+ }
+
+ divisor = 4;
+
+ tb_ticks_per_jiffy = xlbfreq / HZ / divisor;
+ tb_to_us = mulhwu_scale_factor(xlbfreq / divisor, 1000000);
+}
+
+
+void __init
+mpc52xx_add_board_devices(struct ocp_def board_ocp[]) {
+ while (board_ocp->vendor != OCP_VENDOR_INVALID)
+ if(ocp_add_one_device(board_ocp++))
+ printk("mpc5200-ocp: Failed to add board device !\n");
+}
+
/*
- * Author: Pete Popov <ppopov@mvista.com> or source@mvista.com
+ * arch/ppc/kernel/ppc4xx_dma.c
*
- * arch/ppc/kernel/ppc405_dma.c
+ * IBM PPC4xx DMA engine core library
*
- * 2000 (c) MontaVista, Software, Inc. 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.
+ * Copyright 2000-2004 MontaVista Software Inc.
*
- * IBM 405 DMA Controller Functions
+ * Cleaned up and converted to new DCR access
+ * Matt Porter <mporter@kernel.crashing.org>
+ *
+ * Original code by Armin Kuster <akuster@mvista.com>
+ * and Pete Popov <ppopov@mvista.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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/config.h>
#include <linux/kernel.h>
-#include <asm/system.h>
-#include <asm/io.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <asm/ppc405_dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/ppc4xx_dma.h>
+
+ppc_dma_ch_t dma_channels[MAX_PPC4xx_DMA_CHANNELS];
+
+int
+ppc4xx_get_dma_status(void)
+{
+ return (mfdcr(DCRN_DMASR));
+}
+
+void
+ppc4xx_set_src_addr(int dmanr, phys_addr_t src_addr)
+{
+ if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
+ printk("set_src_addr: bad channel: %d\n", dmanr);
+ return;
+ }
+
+#ifdef PPC4xx_DMA64BIT
+ mtdcr(DCRN_DMASAH0 + dmanr*2, (u32)(src_addr >> 32));
+#else
+ mtdcr(DCRN_DMASA0 + dmanr*2, (u32)src_addr);
+#endif
+}
+
+void
+ppc4xx_set_dst_addr(int dmanr, phys_addr_t dst_addr)
+{
+ if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
+ printk("set_dst_addr: bad channel: %d\n", dmanr);
+ return;
+ }
+
+#ifdef PPC4xx_DMA64BIT
+ mtdcr(DCRN_DMADAH0 + dmanr*2, (u32)(dst_addr >> 32));
+#else
+ mtdcr(DCRN_DMADA0 + dmanr*2, (u32)dst_addr);
+#endif
+}
+
+void
+ppc4xx_enable_dma(unsigned int dmanr)
+{
+ unsigned int control;
+ ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
+ unsigned int status_bits[] = { DMA_CS0 | DMA_TS0 | DMA_CH0_ERR,
+ DMA_CS1 | DMA_TS1 | DMA_CH1_ERR,
+ DMA_CS2 | DMA_TS2 | DMA_CH2_ERR,
+ DMA_CS3 | DMA_TS3 | DMA_CH3_ERR};
+
+ if (p_dma_ch->in_use) {
+ printk("enable_dma: channel %d in use\n", dmanr);
+ return;
+ }
+
+ if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
+ printk("enable_dma: bad channel: %d\n", dmanr);
+ return;
+ }
+
+ if (p_dma_ch->mode == DMA_MODE_READ) {
+ /* peripheral to memory */
+ ppc4xx_set_src_addr(dmanr, 0);
+ ppc4xx_set_dst_addr(dmanr, p_dma_ch->addr);
+ } else if (p_dma_ch->mode == DMA_MODE_WRITE) {
+ /* memory to peripheral */
+ ppc4xx_set_src_addr(dmanr, p_dma_ch->addr);
+ ppc4xx_set_dst_addr(dmanr, 0);
+ }
+
+ /* for other xfer modes, the addresses are already set */
+ control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
+
+ control &= ~(DMA_TM_MASK | DMA_TD); /* clear all mode bits */
+ if (p_dma_ch->mode == DMA_MODE_MM) {
+ /* software initiated memory to memory */
+ control |= DMA_ETD_OUTPUT | DMA_TCE_ENABLE;
+ }
+
+ mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
+
+ /*
+ * Clear the CS, TS, RI bits for the channel from DMASR. This
+ * has been observed to happen correctly only after the mode and
+ * ETD/DCE bits in DMACRx are set above. Must do this before
+ * enabling the channel.
+ */
+
+ mtdcr(DCRN_DMASR, status_bits[dmanr]);
+
+ /*
+ * For device-paced transfers, Terminal Count Enable apparently
+ * must be on, and this must be turned on after the mode, etc.
+ * bits are cleared above (at least on Redwood-6).
+ */
+
+ if ((p_dma_ch->mode == DMA_MODE_MM_DEVATDST) ||
+ (p_dma_ch->mode == DMA_MODE_MM_DEVATSRC))
+ control |= DMA_TCE_ENABLE;
+
+ /*
+ * Now enable the channel.
+ */
+
+ control |= (p_dma_ch->mode | DMA_CE_ENABLE);
+
+ mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
+
+ p_dma_ch->in_use = 1;
+}
+
+void
+ppc4xx_disable_dma(unsigned int dmanr)
+{
+ unsigned int control;
+ ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
+
+ if (!p_dma_ch->in_use) {
+ printk("disable_dma: channel %d not in use\n", dmanr);
+ return;
+ }
+
+ if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
+ printk("disable_dma: bad channel: %d\n", dmanr);
+ return;
+ }
+
+ control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
+ control &= ~DMA_CE_ENABLE;
+ mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
+
+ p_dma_ch->in_use = 0;
+}
+
+/*
+ * Sets the dma mode for single DMA transfers only.
+ * For scatter/gather transfers, the mode is passed to the
+ * alloc_dma_handle() function as one of the parameters.
+ *
+ * The mode is simply saved and used later. This allows
+ * the driver to call set_dma_mode() and set_dma_addr() in
+ * any order.
+ *
+ * Valid mode values are:
+ *
+ * DMA_MODE_READ peripheral to memory
+ * DMA_MODE_WRITE memory to peripheral
+ * DMA_MODE_MM memory to memory
+ * DMA_MODE_MM_DEVATSRC device-paced memory to memory, device at src
+ * DMA_MODE_MM_DEVATDST device-paced memory to memory, device at dst
+ */
+int
+ppc4xx_set_dma_mode(unsigned int dmanr, unsigned int mode)
+{
+ ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
+
+ if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
+ printk("set_dma_mode: bad channel 0x%x\n", dmanr);
+ return DMA_STATUS_BAD_CHANNEL;
+ }
+
+ p_dma_ch->mode = mode;
+
+ return DMA_STATUS_GOOD;
+}
+
+/*
+ * Sets the DMA Count register. Note that 'count' is in bytes.
+ * However, the DMA Count register counts the number of "transfers",
+ * where each transfer is equal to the bus width. Thus, count
+ * MUST be a multiple of the bus width.
+ */
+void
+ppc4xx_set_dma_count(unsigned int dmanr, unsigned int count)
+{
+ ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
+
+#ifdef DEBUG_4xxDMA
+ {
+ int error = 0;
+ switch (p_dma_ch->pwidth) {
+ case PW_8:
+ break;
+ case PW_16:
+ if (count & 0x1)
+ error = 1;
+ break;
+ case PW_32:
+ if (count & 0x3)
+ error = 1;
+ break;
+ case PW_64:
+ if (count & 0x7)
+ error = 1;
+ break;
+ default:
+ printk("set_dma_count: invalid bus width: 0x%x\n",
+ p_dma_ch->pwidth);
+ return;
+ }
+ if (error)
+ printk
+ ("Warning: set_dma_count count 0x%x bus width %d\n",
+ count, p_dma_ch->pwidth);
+ }
+#endif
+ count = count >> p_dma_ch->shift;
+
+ mtdcr(DCRN_DMACT0 + (dmanr * 0x8), count);
+}
/*
- * Function prototypes
+ * Returns the number of bytes left to be transfered.
+ * After a DMA transfer, this should return zero.
+ * Reading this while a DMA transfer is still in progress will return
+ * unpredictable results.
*/
+int
+ppc4xx_get_dma_residue(unsigned int dmanr)
+{
+ unsigned int count;
+ ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
-int hw_init_dma_channel(unsigned int, ppc_dma_ch_t *);
-int init_dma_channel(unsigned int);
-int get_channel_config(unsigned int, ppc_dma_ch_t *);
-int set_channel_priority(unsigned int, unsigned int);
-unsigned int get_peripheral_width(unsigned int);
-int alloc_dma_handle(sgl_handle_t *, unsigned int, unsigned int);
-void free_dma_handle(sgl_handle_t);
+ if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
+ printk("ppc4xx_get_dma_residue: bad channel 0x%x\n", dmanr);
+ return DMA_STATUS_BAD_CHANNEL;
+ }
+ count = mfdcr(DCRN_DMACT0 + (dmanr * 0x8));
-ppc_dma_ch_t dma_channels[MAX_405GP_DMA_CHANNELS];
+ return (count << p_dma_ch->shift);
+}
+
+/*
+ * Sets the DMA address for a memory to peripheral or peripheral
+ * to memory transfer. The address is just saved in the channel
+ * structure for now and used later in enable_dma().
+ */
+void
+ppc4xx_set_dma_addr(unsigned int dmanr, phys_addr_t addr)
+{
+ ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
+
+ if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
+ printk("ppc4xx_set_dma_addr: bad channel: %d\n", dmanr);
+ return;
+ }
+
+#ifdef DEBUG_4xxDMA
+ {
+ int error = 0;
+ switch (p_dma_ch->pwidth) {
+ case PW_8:
+ break;
+ case PW_16:
+ if ((unsigned) addr & 0x1)
+ error = 1;
+ break;
+ case PW_32:
+ if ((unsigned) addr & 0x3)
+ error = 1;
+ break;
+ case PW_64:
+ if ((unsigned) addr & 0x7)
+ error = 1;
+ break;
+ default:
+ printk("ppc4xx_set_dma_addr: invalid bus width: 0x%x\n",
+ p_dma_ch->pwidth);
+ return;
+ }
+ if (error)
+ printk("Warning: ppc4xx_set_dma_addr addr 0x%x bus width %d\n",
+ addr, p_dma_ch->pwidth);
+ }
+#endif
+
+ /* save dma address and program it later after we know the xfer mode */
+ p_dma_ch->addr = addr;
+}
+
+/*
+ * Sets both DMA addresses for a memory to memory transfer.
+ * For memory to peripheral or peripheral to memory transfers
+ * the function set_dma_addr() should be used instead.
+ */
+void
+ppc4xx_set_dma_addr2(unsigned int dmanr, phys_addr_t src_dma_addr,
+ phys_addr_t dst_dma_addr)
+{
+ if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
+ printk("ppc4xx_set_dma_addr2: bad channel: %d\n", dmanr);
+ return;
+ }
+
+#ifdef DEBUG_4xxDMA
+ {
+ ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
+ int error = 0;
+ switch (p_dma_ch->pwidth) {
+ case PW_8:
+ break;
+ case PW_16:
+ if (((unsigned) src_dma_addr & 0x1) ||
+ ((unsigned) dst_dma_addr & 0x1)
+ )
+ error = 1;
+ break;
+ case PW_32:
+ if (((unsigned) src_dma_addr & 0x3) ||
+ ((unsigned) dst_dma_addr & 0x3)
+ )
+ error = 1;
+ break;
+ case PW_64:
+ if (((unsigned) src_dma_addr & 0x7) ||
+ ((unsigned) dst_dma_addr & 0x7)
+ )
+ error = 1;
+ break;
+ default:
+ printk("ppc4xx_set_dma_addr2: invalid bus width: 0x%x\n",
+ p_dma_ch->pwidth);
+ return;
+ }
+ if (error)
+ printk
+ ("Warning: ppc4xx_set_dma_addr2 src 0x%x dst 0x%x bus width %d\n",
+ src_dma_addr, dst_dma_addr, p_dma_ch->pwidth);
+ }
+#endif
+
+ ppc4xx_set_src_addr(dmanr, src_dma_addr);
+ ppc4xx_set_dst_addr(dmanr, dst_dma_addr);
+}
+
+/*
+ * Enables the channel interrupt.
+ *
+ * If performing a scatter/gatter transfer, this function
+ * MUST be called before calling alloc_dma_handle() and building
+ * the sgl list. Otherwise, interrupts will not be enabled, if
+ * they were previously disabled.
+ */
+int
+ppc4xx_enable_dma_interrupt(unsigned int dmanr)
+{
+ unsigned int control;
+ ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
+
+ if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
+ printk("ppc4xx_enable_dma_interrupt: bad channel: %d\n", dmanr);
+ return DMA_STATUS_BAD_CHANNEL;
+ }
+
+ p_dma_ch->int_enable = 1;
+
+ control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
+ control |= DMA_CIE_ENABLE; /* Channel Interrupt Enable */
+ mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
+
+ return DMA_STATUS_GOOD;
+}
+
+/*
+ * Disables the channel interrupt.
+ *
+ * If performing a scatter/gatter transfer, this function
+ * MUST be called before calling alloc_dma_handle() and building
+ * the sgl list. Otherwise, interrupts will not be disabled, if
+ * they were previously enabled.
+ */
+int
+ppc4xx_disable_dma_interrupt(unsigned int dmanr)
+{
+ unsigned int control;
+ ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
+
+ if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
+ printk("ppc4xx_disable_dma_interrupt: bad channel: %d\n", dmanr);
+ return DMA_STATUS_BAD_CHANNEL;
+ }
+
+ p_dma_ch->int_enable = 0;
+
+ control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
+ control &= ~DMA_CIE_ENABLE; /* Channel Interrupt Enable */
+ mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
+
+ return DMA_STATUS_GOOD;
+}
/*
* Configures a DMA channel, including the peripheral bus width, if a
* called from platform specific init code. The driver should not need to
* call this function.
*/
-int hw_init_dma_channel(unsigned int dmanr, ppc_dma_ch_t *p_init)
+int
+ppc4xx_init_dma_channel(unsigned int dmanr, ppc_dma_ch_t * p_init)
{
- unsigned int polarity;
- uint32_t control = 0;
- ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
-
-#ifdef DEBUG_405DMA
- if (!p_init) {
- printk("hw_init_dma_channel: NULL p_init\n");
- return DMA_STATUS_NULL_POINTER;
- }
- if (dmanr >= MAX_405GP_DMA_CHANNELS) {
- printk("hw_init_dma_channel: bad channel %d\n", dmanr);
- return DMA_STATUS_BAD_CHANNEL;
- }
-#endif
+ unsigned int polarity;
+ uint32_t control = 0;
+ ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
+
+ DMA_MODE_READ = (unsigned long) DMA_TD; /* Peripheral to Memory */
+ DMA_MODE_WRITE = 0; /* Memory to Peripheral */
+
+ if (!p_init) {
+ printk("ppc4xx_init_dma_channel: NULL p_init\n");
+ return DMA_STATUS_NULL_POINTER;
+ }
+
+ if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
+ printk("ppc4xx_init_dma_channel: bad channel %d\n", dmanr);
+ return DMA_STATUS_BAD_CHANNEL;
+ }
#if DCRN_POL > 0
- polarity = mfdcr(DCRN_POL);
+ polarity = mfdcr(DCRN_POL);
#else
- polarity = 0;
+ polarity = 0;
#endif
- /* Setup the control register based on the values passed to
- * us in p_init. Then, over-write the control register with this
- * new value.
- */
-
- control |= (
- SET_DMA_CIE_ENABLE(p_init->int_enable) | /* interrupt enable */
- SET_DMA_BEN(p_init->buffer_enable) | /* buffer enable */
- SET_DMA_ETD(p_init->etd_output) | /* end of transfer pin */
- SET_DMA_TCE(p_init->tce_enable) | /* terminal count enable */
- SET_DMA_PL(p_init->pl) | /* peripheral location */
- SET_DMA_DAI(p_init->dai) | /* dest addr increment */
- SET_DMA_SAI(p_init->sai) | /* src addr increment */
- SET_DMA_PRIORITY(p_init->cp) | /* channel priority */
- SET_DMA_PW(p_init->pwidth) | /* peripheral/bus width */
- SET_DMA_PSC(p_init->psc) | /* peripheral setup cycles */
- SET_DMA_PWC(p_init->pwc) | /* peripheral wait cycles */
- SET_DMA_PHC(p_init->phc) | /* peripheral hold cycles */
- SET_DMA_PREFETCH(p_init->pf) /* read prefetch */
- );
-
- switch (dmanr) {
- case 0:
- /* clear all polarity signals and then "or" in new signal levels */
- polarity &= ~(DMAReq0_ActiveLow | DMAAck0_ActiveLow | EOT0_ActiveLow);
- polarity |= p_dma_ch->polarity;
-#if DCRN_POL > 0
- mtdcr(DCRN_POL, polarity);
-#endif
- mtdcr(DCRN_DMACR0, control);
- break;
- case 1:
- polarity &= ~(DMAReq1_ActiveLow | DMAAck1_ActiveLow | EOT1_ActiveLow);
- polarity |= p_dma_ch->polarity;
-#if DCRN_POL > 0
- mtdcr(DCRN_POL, polarity);
-#endif
- mtdcr(DCRN_DMACR1, control);
- break;
- case 2:
- polarity &= ~(DMAReq2_ActiveLow | DMAAck2_ActiveLow | EOT2_ActiveLow);
- polarity |= p_dma_ch->polarity;
-#if DCRN_POL > 0
- mtdcr(DCRN_POL, polarity);
-#endif
- mtdcr(DCRN_DMACR2, control);
- break;
- case 3:
- polarity &= ~(DMAReq3_ActiveLow | DMAAck3_ActiveLow | EOT3_ActiveLow);
- polarity |= p_dma_ch->polarity;
+ /* Setup the control register based on the values passed to
+ * us in p_init. Then, over-write the control register with this
+ * new value.
+ */
+ control |= SET_DMA_CONTROL;
+
+ /* clear all polarity signals and then "or" in new signal levels */
+ polarity &= ~GET_DMA_POLARITY(dmanr);
+ polarity |= p_dma_ch->polarity;
#if DCRN_POL > 0
- mtdcr(DCRN_POL, polarity);
+ mtdcr(DCRN_POL, polarity);
#endif
- mtdcr(DCRN_DMACR3, control);
- break;
- default:
- return DMA_STATUS_BAD_CHANNEL;
- }
-
- /* save these values in our dma channel structure */
- memcpy(p_dma_ch, p_init, sizeof(ppc_dma_ch_t));
-
- /*
- * The peripheral width values written in the control register are:
- * PW_8 0
- * PW_16 1
- * PW_32 2
- * PW_64 3
- *
- * Since the DMA count register takes the number of "transfers",
- * we need to divide the count sent to us in certain
- * functions by the appropriate number. It so happens that our
- * right shift value is equal to the peripheral width value.
- */
- p_dma_ch->shift = p_init->pwidth;
-
- /*
- * Save the control word for easy access.
- */
- p_dma_ch->control = control;
-
- mtdcr(DCRN_DMASR, 0xffffffff); /* clear status register */
- return DMA_STATUS_GOOD;
+ mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
+
+ /* save these values in our dma channel structure */
+ memcpy(p_dma_ch, p_init, sizeof (ppc_dma_ch_t));
+
+ /*
+ * The peripheral width values written in the control register are:
+ * PW_8 0
+ * PW_16 1
+ * PW_32 2
+ * PW_64 3
+ *
+ * Since the DMA count register takes the number of "transfers",
+ * we need to divide the count sent to us in certain
+ * functions by the appropriate number. It so happens that our
+ * right shift value is equal to the peripheral width value.
+ */
+ p_dma_ch->shift = p_init->pwidth;
+
+ /*
+ * Save the control word for easy access.
+ */
+ p_dma_ch->control = control;
+
+ mtdcr(DCRN_DMASR, 0xffffffff); /* clear status register */
+ return DMA_STATUS_GOOD;
}
-
-
-
/*
* This function returns the channel configuration.
*/
-int get_channel_config(unsigned int dmanr, ppc_dma_ch_t *p_dma_ch)
+int
+ppc4xx_get_channel_config(unsigned int dmanr, ppc_dma_ch_t * p_dma_ch)
{
- unsigned int polarity;
- unsigned int control;
+ unsigned int polarity;
+ unsigned int control;
+
+ if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
+ printk("ppc4xx_get_channel_config: bad channel %d\n", dmanr);
+ return DMA_STATUS_BAD_CHANNEL;
+ }
#if DCRN_POL > 0
- polarity = mfdcr(DCRN_POL);
+ polarity = mfdcr(DCRN_POL);
#else
- polarity = 0;
+ polarity = 0;
#endif
- switch (dmanr) {
- case 0:
- p_dma_ch->polarity =
- polarity & (DMAReq0_ActiveLow | DMAAck0_ActiveLow | EOT0_ActiveLow);
- control = mfdcr(DCRN_DMACR0);
- break;
- case 1:
- p_dma_ch->polarity =
- polarity & (DMAReq1_ActiveLow | DMAAck1_ActiveLow | EOT1_ActiveLow);
- control = mfdcr(DCRN_DMACR1);
- break;
- case 2:
- p_dma_ch->polarity =
- polarity & (DMAReq2_ActiveLow | DMAAck2_ActiveLow | EOT2_ActiveLow);
- control = mfdcr(DCRN_DMACR2);
- break;
- case 3:
- p_dma_ch->polarity =
- polarity & (DMAReq3_ActiveLow | DMAAck3_ActiveLow | EOT3_ActiveLow);
- control = mfdcr(DCRN_DMACR3);
- break;
- default:
- return DMA_STATUS_BAD_CHANNEL;
- }
-
- p_dma_ch->cp = GET_DMA_PRIORITY(control);
- p_dma_ch->pwidth = GET_DMA_PW(control);
- p_dma_ch->psc = GET_DMA_PSC(control);
- p_dma_ch->pwc = GET_DMA_PWC(control);
- p_dma_ch->phc = GET_DMA_PHC(control);
- p_dma_ch->pf = GET_DMA_PREFETCH(control);
- p_dma_ch->int_enable = GET_DMA_CIE_ENABLE(control);
- p_dma_ch->shift = GET_DMA_PW(control);
-
- return DMA_STATUS_GOOD;
+ p_dma_ch->polarity = polarity & GET_DMA_POLARITY(dmanr);
+ control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
+
+ p_dma_ch->cp = GET_DMA_PRIORITY(control);
+ p_dma_ch->pwidth = GET_DMA_PW(control);
+ p_dma_ch->psc = GET_DMA_PSC(control);
+ p_dma_ch->pwc = GET_DMA_PWC(control);
+ p_dma_ch->phc = GET_DMA_PHC(control);
+ p_dma_ch->ce = GET_DMA_CE_ENABLE(control);
+ p_dma_ch->int_enable = GET_DMA_CIE_ENABLE(control);
+ p_dma_ch->shift = GET_DMA_PW(control);
+
+#ifdef CONFIG_PPC4xx_EDMA
+ p_dma_ch->pf = GET_DMA_PREFETCH(control);
+#else
+ p_dma_ch->ch_enable = GET_DMA_CH(control);
+ p_dma_ch->ece_enable = GET_DMA_ECE(control);
+ p_dma_ch->tcd_disable = GET_DMA_TCD(control);
+#endif
+ return DMA_STATUS_GOOD;
}
/*
* PRIORITY_HIGH
*
*/
-int set_channel_priority(unsigned int dmanr, unsigned int priority)
+int
+ppc4xx_set_channel_priority(unsigned int dmanr, unsigned int priority)
{
- unsigned int control;
-
-#ifdef DEBUG_405DMA
- if ( (priority != PRIORITY_LOW) &&
- (priority != PRIORITY_MID_LOW) &&
- (priority != PRIORITY_MID_HIGH) &&
- (priority != PRIORITY_HIGH)) {
- printk("set_channel_priority: bad priority: 0x%x\n", priority);
- }
-#endif
+ unsigned int control;
- switch (dmanr) {
- case 0:
- control = mfdcr(DCRN_DMACR0);
- control|= SET_DMA_PRIORITY(priority);
- mtdcr(DCRN_DMACR0, control);
- break;
- case 1:
- control = mfdcr(DCRN_DMACR1);
- control|= SET_DMA_PRIORITY(priority);
- mtdcr(DCRN_DMACR1, control);
- break;
- case 2:
- control = mfdcr(DCRN_DMACR2);
- control|= SET_DMA_PRIORITY(priority);
- mtdcr(DCRN_DMACR2, control);
- break;
- case 3:
- control = mfdcr(DCRN_DMACR3);
- control|= SET_DMA_PRIORITY(priority);
- mtdcr(DCRN_DMACR3, control);
- break;
- default:
-#ifdef DEBUG_405DMA
- printk("set_channel_priority: bad channel: %d\n", dmanr);
-#endif
- return DMA_STATUS_BAD_CHANNEL;
- }
- return DMA_STATUS_GOOD;
-}
+ if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
+ printk("ppc4xx_set_channel_priority: bad channel %d\n", dmanr);
+ return DMA_STATUS_BAD_CHANNEL;
+ }
+
+ if ((priority != PRIORITY_LOW) &&
+ (priority != PRIORITY_MID_LOW) &&
+ (priority != PRIORITY_MID_HIGH) && (priority != PRIORITY_HIGH)) {
+ printk("ppc4xx_set_channel_priority: bad priority: 0x%x\n", priority);
+ }
+ control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
+ control |= SET_DMA_PRIORITY(priority);
+ mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);
+ return DMA_STATUS_GOOD;
+}
/*
* Returns the width of the peripheral attached to this channel. This assumes
*
* The function returns 0 on error.
*/
-unsigned int get_peripheral_width(unsigned int dmanr)
+unsigned int
+ppc4xx_get_peripheral_width(unsigned int dmanr)
{
- unsigned int control;
-
- switch (dmanr) {
- case 0:
- control = mfdcr(DCRN_DMACR0);
- break;
- case 1:
- control = mfdcr(DCRN_DMACR1);
- break;
- case 2:
- control = mfdcr(DCRN_DMACR2);
- break;
- case 3:
- control = mfdcr(DCRN_DMACR3);
- break;
- default:
-#ifdef DEBUG_405DMA
- printk("get_peripheral_width: bad channel: %d\n", dmanr);
-#endif
- return 0;
- }
- return(GET_DMA_PW(control));
-}
-
-
-
-
-/*
- * Create a scatter/gather list handle. This is simply a structure which
- * describes a scatter/gather list.
- *
- * A handle is returned in "handle" which the driver should save in order to
- * be able to access this list later. A chunk of memory will be allocated
- * to be used by the API for internal management purposes, including managing
- * the sg list and allocating memory for the sgl descriptors. One page should
- * be more than enough for that purpose. Perhaps it's a bit wasteful to use
- * a whole page for a single sg list, but most likely there will be only one
- * sg list per channel.
- *
- * Interrupt notes:
- * Each sgl descriptor has a copy of the DMA control word which the DMA engine
- * loads in the control register. The control word has a "global" interrupt
- * enable bit for that channel. Interrupts are further qualified by a few bits
- * in the sgl descriptor count register. In order to setup an sgl, we have to
- * know ahead of time whether or not interrupts will be enabled at the completion
- * of the transfers. Thus, enable_dma_interrupt()/disable_dma_interrupt() MUST
- * be called before calling alloc_dma_handle(). If the interrupt mode will never
- * change after powerup, then enable_dma_interrupt()/disable_dma_interrupt()
- * do not have to be called -- interrupts will be enabled or disabled based
- * on how the channel was configured after powerup by the hw_init_dma_channel()
- * function. Each sgl descriptor will be setup to interrupt if an error occurs;
- * however, only the last descriptor will be setup to interrupt. Thus, an
- * interrupt will occur (if interrupts are enabled) only after the complete
- * sgl transfer is done.
- */
-int alloc_dma_handle(sgl_handle_t *phandle, unsigned int mode, unsigned int dmanr)
-{
- sgl_list_info_t *psgl;
- dma_addr_t dma_addr;
- ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
- uint32_t sg_command;
- void *ret;
-
-#ifdef DEBUG_405DMA
- if (!phandle) {
- printk("alloc_dma_handle: null handle pointer\n");
- return DMA_STATUS_NULL_POINTER;
- }
- switch (mode) {
- case DMA_MODE_READ:
- case DMA_MODE_WRITE:
- case DMA_MODE_MM:
- case DMA_MODE_MM_DEVATSRC:
- case DMA_MODE_MM_DEVATDST:
- break;
- default:
- printk("alloc_dma_handle: bad mode 0x%x\n", mode);
- return DMA_STATUS_BAD_MODE;
- }
- if (dmanr >= MAX_405GP_DMA_CHANNELS) {
- printk("alloc_dma_handle: invalid channel 0x%x\n", dmanr);
- return DMA_STATUS_BAD_CHANNEL;
- }
-#endif
+ unsigned int control;
- /* Get a page of memory, which is zeroed out by pci_alloc_consistent() */
-
-/* wrong not a pci device - armin */
- /* psgl = (sgl_list_info_t *) pci_alloc_consistent(NULL, SGL_LIST_SIZE, &dma_addr);
-*/
-
- ret = consistent_alloc(GFP_ATOMIC |GFP_DMA, SGL_LIST_SIZE, &dma_addr);
- if (ret != NULL) {
- memset(ret, 0,SGL_LIST_SIZE );
- psgl = (sgl_list_info_t *) ret;
+ if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
+ printk("ppc4xx_get_peripheral_width: bad channel %d\n", dmanr);
+ return DMA_STATUS_BAD_CHANNEL;
}
+ control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
- if (psgl == NULL) {
- *phandle = (sgl_handle_t)NULL;
- return DMA_STATUS_OUT_OF_MEMORY;
- }
-
- psgl->dma_addr = dma_addr;
- psgl->dmanr = dmanr;
-
- /*
- * Modify and save the control word. These word will get written to each sgl
- * descriptor. The DMA engine then loads this control word into the control
- * register every time it reads a new descriptor.
- */
- psgl->control = p_dma_ch->control;
- psgl->control &= ~(DMA_TM_MASK | DMA_TD); /* clear all "mode" bits first */
- psgl->control |= (mode | DMA_CH_ENABLE); /* save the control word along with the mode */
-
- if (p_dma_ch->int_enable) {
- psgl->control |= DMA_CIE_ENABLE; /* channel interrupt enabled */
- }
- else {
- psgl->control &= ~DMA_CIE_ENABLE;
- }
-
-#if DCRN_ASGC > 0
- sg_command = mfdcr(DCRN_ASGC);
- switch (dmanr) {
- case 0:
- sg_command |= SSG0_MASK_ENABLE;
- break;
- case 1:
- sg_command |= SSG1_MASK_ENABLE;
- break;
- case 2:
- sg_command |= SSG2_MASK_ENABLE;
- break;
- case 3:
- sg_command |= SSG3_MASK_ENABLE;
- break;
- default:
-#ifdef DEBUG_405DMA
- printk("alloc_dma_handle: bad channel: %d\n", dmanr);
-#endif
- free_dma_handle((sgl_handle_t)psgl);
- *phandle = (sgl_handle_t)NULL;
- return DMA_STATUS_BAD_CHANNEL;
- }
-
- mtdcr(DCRN_ASGC, sg_command); /* enable writing to this channel's sgl control bits */
-#else
- (void)sg_command;
-#endif
- psgl->sgl_control = SG_ERI_ENABLE | SG_LINK; /* sgl descriptor control bits */
-
- if (p_dma_ch->int_enable) {
- if (p_dma_ch->tce_enable)
- psgl->sgl_control |= SG_TCI_ENABLE;
- else
- psgl->sgl_control |= SG_ETI_ENABLE;
- }
-
- *phandle = (sgl_handle_t)psgl;
- return DMA_STATUS_GOOD;
-}
-
-
-
-/*
- * Destroy a scatter/gather list handle that was created by alloc_dma_handle().
- * The list must be empty (contain no elements).
- */
-void free_dma_handle(sgl_handle_t handle)
-{
- sgl_list_info_t *psgl = (sgl_list_info_t *)handle;
-
- if (!handle) {
-#ifdef DEBUG_405DMA
- printk("free_dma_handle: got NULL\n");
-#endif
- return;
- }
- else if (psgl->phead) {
-#ifdef DEBUG_405DMA
- printk("free_dma_handle: list not empty\n");
-#endif
- return;
- }
- else if (!psgl->dma_addr) { /* should never happen */
-#ifdef DEBUG_405DMA
- printk("free_dma_handle: no dma address\n");
-#endif
- return;
- }
-
- /* wrong not a PCI device -armin */
- /* pci_free_consistent(NULL, SGL_LIST_SIZE, (void *)psgl, psgl->dma_addr); */
- // free_pages((unsigned long)psgl, get_order(SGL_LIST_SIZE));
- consistent_free((void *)psgl);
-
-
+ return (GET_DMA_PW(control));
}
-EXPORT_SYMBOL(hw_init_dma_channel);
-EXPORT_SYMBOL(get_channel_config);
-EXPORT_SYMBOL(set_channel_priority);
-EXPORT_SYMBOL(get_peripheral_width);
-EXPORT_SYMBOL(alloc_dma_handle);
-EXPORT_SYMBOL(free_dma_handle);
+EXPORT_SYMBOL(ppc4xx_init_dma_channel);
+EXPORT_SYMBOL(ppc4xx_get_channel_config);
+EXPORT_SYMBOL(ppc4xx_set_channel_priority);
+EXPORT_SYMBOL(ppc4xx_get_peripheral_width);
EXPORT_SYMBOL(dma_channels);
+EXPORT_SYMBOL(ppc4xx_set_src_addr);
+EXPORT_SYMBOL(ppc4xx_set_dst_addr);
+EXPORT_SYMBOL(ppc4xx_set_dma_addr);
+EXPORT_SYMBOL(ppc4xx_set_dma_addr2);
+EXPORT_SYMBOL(ppc4xx_enable_dma);
+EXPORT_SYMBOL(ppc4xx_disable_dma);
+EXPORT_SYMBOL(ppc4xx_set_dma_mode);
+EXPORT_SYMBOL(ppc4xx_set_dma_count);
+EXPORT_SYMBOL(ppc4xx_get_dma_residue);
+EXPORT_SYMBOL(ppc4xx_enable_dma_interrupt);
+EXPORT_SYMBOL(ppc4xx_disable_dma_interrupt);
+EXPORT_SYMBOL(ppc4xx_get_dma_status);
--- /dev/null
+/*
+ * arch/ppc/kernel/ppc4xx_sgdma.c
+ *
+ * IBM PPC4xx DMA engine scatter/gather library
+ *
+ * Copyright 2002-2003 MontaVista Software Inc.
+ *
+ * Cleaned up and converted to new DCR access
+ * Matt Porter <mporter@kernel.crashing.org>
+ *
+ * Original code by Armin Kuster <akuster@mvista.com>
+ * and Pete Popov <ppopov@mvista.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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/ppc4xx_dma.h>
+
+void
+ppc4xx_set_sg_addr(int dmanr, phys_addr_t sg_addr)
+{
+ if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
+ printk("ppc4xx_set_sg_addr: bad channel: %d\n", dmanr);
+ return;
+ }
+
+#ifdef PPC4xx_DMA_64BIT
+ mtdcr(DCRN_ASGH0 + (dmanr * 0x8), (u32)(sg_addr >> 32));
+#endif
+ mtdcr(DCRN_ASG0 + (dmanr * 0x8), (u32)sg_addr);
+}
+
+/*
+ * Add a new sgl descriptor to the end of a scatter/gather list
+ * which was created by alloc_dma_handle().
+ *
+ * For a memory to memory transfer, both dma addresses must be
+ * valid. For a peripheral to memory transfer, one of the addresses
+ * must be set to NULL, depending on the direction of the transfer:
+ * memory to peripheral: set dst_addr to NULL,
+ * peripheral to memory: set src_addr to NULL.
+ */
+int
+ppc4xx_add_dma_sgl(sgl_handle_t handle, phys_addr_t src_addr, phys_addr_t dst_addr,
+ unsigned int count)
+{
+ sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
+ ppc_dma_ch_t *p_dma_ch;
+
+ if (!handle) {
+ printk("ppc4xx_add_dma_sgl: null handle\n");
+ return DMA_STATUS_BAD_HANDLE;
+ }
+
+ if (psgl->dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
+ printk("ppc4xx_add_dma_sgl: bad channel: %d\n", psgl->dmanr);
+ return DMA_STATUS_BAD_CHANNEL;
+ }
+
+ p_dma_ch = &dma_channels[psgl->dmanr];
+
+#ifdef DEBUG_4xxDMA
+ {
+ int error = 0;
+ unsigned int aligned =
+ (unsigned) src_addr | (unsigned) dst_addr | count;
+ switch (p_dma_ch->pwidth) {
+ case PW_8:
+ break;
+ case PW_16:
+ if (aligned & 0x1)
+ error = 1;
+ break;
+ case PW_32:
+ if (aligned & 0x3)
+ error = 1;
+ break;
+ case PW_64:
+ if (aligned & 0x7)
+ error = 1;
+ break;
+ default:
+ printk("ppc4xx_add_dma_sgl: invalid bus width: 0x%x\n",
+ p_dma_ch->pwidth);
+ return DMA_STATUS_GENERAL_ERROR;
+ }
+ if (error)
+ printk
+ ("Alignment warning: ppc4xx_add_dma_sgl src 0x%x dst 0x%x count 0x%x bus width var %d\n",
+ src_addr, dst_addr, count, p_dma_ch->pwidth);
+
+ }
+#endif
+
+ if ((unsigned) (psgl->ptail + 1) >= ((unsigned) psgl + SGL_LIST_SIZE)) {
+ printk("sgl handle out of memory \n");
+ return DMA_STATUS_OUT_OF_MEMORY;
+ }
+
+ if (!psgl->ptail) {
+ psgl->phead = (ppc_sgl_t *)
+ ((unsigned) psgl + sizeof (sgl_list_info_t));
+ psgl->phead_dma = psgl->dma_addr + sizeof(sgl_list_info_t);
+ psgl->ptail = psgl->phead;
+ psgl->ptail_dma = psgl->phead_dma;
+ } else {
+ psgl->ptail->next = psgl->ptail_dma + sizeof(ppc_sgl_t);
+ psgl->ptail++;
+ psgl->ptail_dma += sizeof(ppc_sgl_t);
+ }
+
+ psgl->ptail->control = psgl->control;
+ psgl->ptail->src_addr = src_addr;
+ psgl->ptail->dst_addr = dst_addr;
+ psgl->ptail->control_count = (count >> p_dma_ch->shift) |
+ psgl->sgl_control;
+ psgl->ptail->next = (uint32_t) NULL;
+
+ return DMA_STATUS_GOOD;
+}
+
+/*
+ * Enable (start) the DMA described by the sgl handle.
+ */
+void
+ppc4xx_enable_dma_sgl(sgl_handle_t handle)
+{
+ sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
+ ppc_dma_ch_t *p_dma_ch;
+ uint32_t sg_command;
+
+ if (!handle) {
+ printk("ppc4xx_enable_dma_sgl: null handle\n");
+ return;
+ } else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) {
+ printk("ppc4xx_enable_dma_sgl: bad channel in handle %d\n",
+ psgl->dmanr);
+ return;
+ } else if (!psgl->phead) {
+ printk("ppc4xx_enable_dma_sgl: sg list empty\n");
+ return;
+ }
+
+ p_dma_ch = &dma_channels[psgl->dmanr];
+ psgl->ptail->control_count &= ~SG_LINK; /* make this the last dscrptr */
+ sg_command = mfdcr(DCRN_ASGC);
+
+ ppc4xx_set_sg_addr(psgl->dmanr, psgl->phead_dma);
+
+ sg_command |= SSG_ENABLE(psgl->dmanr);
+
+ mtdcr(DCRN_ASGC, sg_command); /* start transfer */
+}
+
+/*
+ * Halt an active scatter/gather DMA operation.
+ */
+void
+ppc4xx_disable_dma_sgl(sgl_handle_t handle)
+{
+ sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
+ uint32_t sg_command;
+
+ if (!handle) {
+ printk("ppc4xx_enable_dma_sgl: null handle\n");
+ return;
+ } else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) {
+ printk("ppc4xx_enable_dma_sgl: bad channel in handle %d\n",
+ psgl->dmanr);
+ return;
+ }
+
+ sg_command = mfdcr(DCRN_ASGC);
+ sg_command &= ~SSG_ENABLE(psgl->dmanr);
+ mtdcr(DCRN_ASGC, sg_command); /* stop transfer */
+}
+
+/*
+ * Returns number of bytes left to be transferred from the entire sgl list.
+ * *src_addr and *dst_addr get set to the source/destination address of
+ * the sgl descriptor where the DMA stopped.
+ *
+ * An sgl transfer must NOT be active when this function is called.
+ */
+int
+ppc4xx_get_dma_sgl_residue(sgl_handle_t handle, phys_addr_t * src_addr,
+ phys_addr_t * dst_addr)
+{
+ sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
+ ppc_dma_ch_t *p_dma_ch;
+ ppc_sgl_t *pnext, *sgl_addr;
+ uint32_t count_left;
+
+ if (!handle) {
+ printk("ppc4xx_get_dma_sgl_residue: null handle\n");
+ return DMA_STATUS_BAD_HANDLE;
+ } else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) {
+ printk("ppc4xx_get_dma_sgl_residue: bad channel in handle %d\n",
+ psgl->dmanr);
+ return DMA_STATUS_BAD_CHANNEL;
+ }
+
+ sgl_addr = (ppc_sgl_t *) __va(mfdcr(DCRN_ASG0 + (psgl->dmanr * 0x8)));
+ count_left = mfdcr(DCRN_DMACT0 + (psgl->dmanr * 0x8));
+
+ if (!sgl_addr) {
+ printk("ppc4xx_get_dma_sgl_residue: sgl addr register is null\n");
+ goto error;
+ }
+
+ pnext = psgl->phead;
+ while (pnext &&
+ ((unsigned) pnext < ((unsigned) psgl + SGL_LIST_SIZE) &&
+ (pnext != sgl_addr))
+ ) {
+ pnext++;
+ }
+
+ if (pnext == sgl_addr) { /* found the sgl descriptor */
+
+ *src_addr = pnext->src_addr;
+ *dst_addr = pnext->dst_addr;
+
+ /*
+ * Now search the remaining descriptors and add their count.
+ * We already have the remaining count from this descriptor in
+ * count_left.
+ */
+ pnext++;
+
+ while ((pnext != psgl->ptail) &&
+ ((unsigned) pnext < ((unsigned) psgl + SGL_LIST_SIZE))
+ ) {
+ count_left += pnext->control_count & SG_COUNT_MASK;
+ }
+
+ if (pnext != psgl->ptail) { /* should never happen */
+ printk
+ ("ppc4xx_get_dma_sgl_residue error (1) psgl->ptail 0x%x handle 0x%x\n",
+ (unsigned int) psgl->ptail, (unsigned int) handle);
+ goto error;
+ }
+
+ /* success */
+ p_dma_ch = &dma_channels[psgl->dmanr];
+ return (count_left << p_dma_ch->shift); /* count in bytes */
+
+ } else {
+ /* this shouldn't happen */
+ printk
+ ("get_dma_sgl_residue, unable to match current address 0x%x, handle 0x%x\n",
+ (unsigned int) sgl_addr, (unsigned int) handle);
+
+ }
+
+ error:
+ *src_addr = (phys_addr_t) NULL;
+ *dst_addr = (phys_addr_t) NULL;
+ return 0;
+}
+
+/*
+ * Returns the address(es) of the buffer(s) contained in the head element of
+ * the scatter/gather list. The element is removed from the scatter/gather
+ * list and the next element becomes the head.
+ *
+ * This function should only be called when the DMA is not active.
+ */
+int
+ppc4xx_delete_dma_sgl_element(sgl_handle_t handle, phys_addr_t * src_dma_addr,
+ phys_addr_t * dst_dma_addr)
+{
+ sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
+
+ if (!handle) {
+ printk("ppc4xx_delete_sgl_element: null handle\n");
+ return DMA_STATUS_BAD_HANDLE;
+ } else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) {
+ printk("ppc4xx_delete_sgl_element: bad channel in handle %d\n",
+ psgl->dmanr);
+ return DMA_STATUS_BAD_CHANNEL;
+ }
+
+ if (!psgl->phead) {
+ printk("ppc4xx_delete_sgl_element: sgl list empty\n");
+ *src_dma_addr = (phys_addr_t) NULL;
+ *dst_dma_addr = (phys_addr_t) NULL;
+ return DMA_STATUS_SGL_LIST_EMPTY;
+ }
+
+ *src_dma_addr = (phys_addr_t) psgl->phead->src_addr;
+ *dst_dma_addr = (phys_addr_t) psgl->phead->dst_addr;
+
+ if (psgl->phead == psgl->ptail) {
+ /* last descriptor on the list */
+ psgl->phead = NULL;
+ psgl->ptail = NULL;
+ } else {
+ psgl->phead++;
+ psgl->phead_dma += sizeof(ppc_sgl_t);
+ }
+
+ return DMA_STATUS_GOOD;
+}
+
+
+/*
+ * Create a scatter/gather list handle. This is simply a structure which
+ * describes a scatter/gather list.
+ *
+ * A handle is returned in "handle" which the driver should save in order to
+ * be able to access this list later. A chunk of memory will be allocated
+ * to be used by the API for internal management purposes, including managing
+ * the sg list and allocating memory for the sgl descriptors. One page should
+ * be more than enough for that purpose. Perhaps it's a bit wasteful to use
+ * a whole page for a single sg list, but most likely there will be only one
+ * sg list per channel.
+ *
+ * Interrupt notes:
+ * Each sgl descriptor has a copy of the DMA control word which the DMA engine
+ * loads in the control register. The control word has a "global" interrupt
+ * enable bit for that channel. Interrupts are further qualified by a few bits
+ * in the sgl descriptor count register. In order to setup an sgl, we have to
+ * know ahead of time whether or not interrupts will be enabled at the completion
+ * of the transfers. Thus, enable_dma_interrupt()/disable_dma_interrupt() MUST
+ * be called before calling alloc_dma_handle(). If the interrupt mode will never
+ * change after powerup, then enable_dma_interrupt()/disable_dma_interrupt()
+ * do not have to be called -- interrupts will be enabled or disabled based
+ * on how the channel was configured after powerup by the hw_init_dma_channel()
+ * function. Each sgl descriptor will be setup to interrupt if an error occurs;
+ * however, only the last descriptor will be setup to interrupt. Thus, an
+ * interrupt will occur (if interrupts are enabled) only after the complete
+ * sgl transfer is done.
+ */
+int
+ppc4xx_alloc_dma_handle(sgl_handle_t * phandle, unsigned int mode, unsigned int dmanr)
+{
+ sgl_list_info_t *psgl;
+ dma_addr_t dma_addr;
+ ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
+ uint32_t sg_command;
+ void *ret;
+
+ if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
+ printk("ppc4xx_alloc_dma_handle: invalid channel 0x%x\n", dmanr);
+ return DMA_STATUS_BAD_CHANNEL;
+ }
+
+ if (!phandle) {
+ printk("ppc4xx_alloc_dma_handle: null handle pointer\n");
+ return DMA_STATUS_NULL_POINTER;
+ }
+
+ /* Get a page of memory, which is zeroed out by consistent_alloc() */
+ ret = dma_alloc_coherent(NULL, DMA_PPC4xx_SIZE, &dma_addr, GFP_KERNEL);
+ if (ret != NULL) {
+ memset(ret, 0, DMA_PPC4xx_SIZE);
+ psgl = (sgl_list_info_t *) ret;
+ }
+
+ if (psgl == NULL) {
+ *phandle = (sgl_handle_t) NULL;
+ return DMA_STATUS_OUT_OF_MEMORY;
+ }
+
+ psgl->dma_addr = dma_addr;
+ psgl->dmanr = dmanr;
+
+ /*
+ * Modify and save the control word. These words will be
+ * written to each sgl descriptor. The DMA engine then
+ * loads this control word into the control register
+ * every time it reads a new descriptor.
+ */
+ psgl->control = p_dma_ch->control;
+ /* Clear all mode bits */
+ psgl->control &= ~(DMA_TM_MASK | DMA_TD);
+ /* Save control word and mode */
+ psgl->control |= (mode | DMA_CE_ENABLE);
+
+ /* In MM mode, we must set ETD/TCE */
+ if (mode == DMA_MODE_MM)
+ psgl->control |= DMA_ETD_OUTPUT | DMA_TCE_ENABLE;
+
+ if (p_dma_ch->int_enable) {
+ /* Enable channel interrupt */
+ psgl->control |= DMA_CIE_ENABLE;
+ } else {
+ psgl->control &= ~DMA_CIE_ENABLE;
+ }
+
+ sg_command = mfdcr(DCRN_ASGC);
+ sg_command |= SSG_MASK_ENABLE(dmanr);
+
+ /* Enable SGL control access */
+ mtdcr(DCRN_ASGC, sg_command);
+ psgl->sgl_control = SG_ERI_ENABLE | SG_LINK;
+
+ if (p_dma_ch->int_enable) {
+ if (p_dma_ch->tce_enable)
+ psgl->sgl_control |= SG_TCI_ENABLE;
+ else
+ psgl->sgl_control |= SG_ETI_ENABLE;
+ }
+
+ *phandle = (sgl_handle_t) psgl;
+ return DMA_STATUS_GOOD;
+}
+
+/*
+ * Destroy a scatter/gather list handle that was created by alloc_dma_handle().
+ * The list must be empty (contain no elements).
+ */
+void
+ppc4xx_free_dma_handle(sgl_handle_t handle)
+{
+ sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
+
+ if (!handle) {
+ printk("ppc4xx_free_dma_handle: got NULL\n");
+ return;
+ } else if (psgl->phead) {
+ printk("ppc4xx_free_dma_handle: list not empty\n");
+ return;
+ } else if (!psgl->dma_addr) { /* should never happen */
+ printk("ppc4xx_free_dma_handle: no dma address\n");
+ return;
+ }
+
+ dma_free_coherent(NULL, DMA_PPC4xx_SIZE, (void *) psgl, 0);
+}
+
+EXPORT_SYMBOL(ppc4xx_alloc_dma_handle);
+EXPORT_SYMBOL(ppc4xx_free_dma_handle);
+EXPORT_SYMBOL(ppc4xx_add_dma_sgl);
+EXPORT_SYMBOL(ppc4xx_delete_dma_sgl_element);
+EXPORT_SYMBOL(ppc4xx_enable_dma_sgl);
+EXPORT_SYMBOL(ppc4xx_disable_dma_sgl);
+EXPORT_SYMBOL(ppc4xx_get_dma_sgl_residue);
--- /dev/null
+/*
+ * arch/ppc/syslib/ppc85xx_common.c
+ *
+ * MPC85xx support routines
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2004 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <asm/mpc85xx.h>
+#include <asm/mmu.h>
+#include <asm/ocp.h>
+
+/* ************************************************************************ */
+/* Return the value of CCSRBAR for the current board */
+
+phys_addr_t
+get_ccsrbar(void)
+{
+ return BOARD_CCSRBAR;
+}
+
+/* ************************************************************************ */
+/* Update the 85xx OCP tables paddr field */
+void
+mpc85xx_update_paddr_ocp(struct ocp_device *dev, void *arg)
+{
+ phys_addr_t ccsrbar;
+ if (arg) {
+ ccsrbar = *(phys_addr_t *)arg;
+ dev->def->paddr += ccsrbar;
+ }
+}
+
+EXPORT_SYMBOL(get_ccsrbar);
--- /dev/null
+/*
+ * arch/ppc/syslib/ppc85xx_common.h
+ *
+ * MPC85xx support routines
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2004 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __PPC_SYSLIB_PPC85XX_COMMON_H
+#define __PPC_SYSLIB_PPC85XX_COMMON_H
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <asm/ocp.h>
+
+/* Provide access to ccsrbar for any modules, etc */
+phys_addr_t get_ccsrbar(void);
+
+/* Update the 85xx OCP tables paddr field */
+void mpc85xx_update_paddr_ocp(struct ocp_device *dev, void *ccsrbar);
+
+#endif /* __PPC_SYSLIB_PPC85XX_COMMON_H */
#include <asm/ocp.h>
#include <asm/kgdb.h>
+#include <syslib/ppc85xx_setup.h>
+
/* Return the amount of memory */
unsigned long __init
mpc85xx_find_end_of_memory(void)
pci->piwar2 = 0;
pci->piwar3 = 0;
- /* Setup 512M Phys:PCI 1:1 outbound mem window @ 0x80000000 */
+ /* Setup Phys:PCI 1:1 outbound mem window @ MPC85XX_PCI1_LOWER_MEM */
pci->potar1 = (MPC85XX_PCI1_LOWER_MEM >> 12) & 0x000fffff;
pci->potear1 = 0x00000000;
pci->powbar1 = (MPC85XX_PCI1_LOWER_MEM >> 12) & 0x000fffff;
- pci->powar1 = 0x8004401c; /* Enable, Mem R/W, 512M */
+ /* Enable, Mem R/W */
+ pci->powar1 = 0x80044000 |
+ (__ilog2(MPC85XX_PCI1_UPPER_MEM - MPC85XX_PCI1_LOWER_MEM + 1) - 1);
- /* Setup 16M outboud IO windows @ 0xe2000000 */
+ /* Setup outboud IO windows @ MPC85XX_PCI1_IO_BASE */
pci->potar2 = 0x00000000;
pci->potear2 = 0x00000000;
pci->powbar2 = (MPC85XX_PCI1_IO_BASE >> 12) & 0x000fffff;
- pci->powar2 = 0x80088017; /* Enable, IO R/W, 16M */
+ /* Enable, IO R/W */
+ pci->powar2 = 0x80088000 | (__ilog2(MPC85XX_PCI1_IO_SIZE) - 1);
/* Setup 2G inbound Memory Window @ 0 */
pci->pitar1 = 0x00000000;
extern int mpc85xx_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin);
extern int mpc85xx_exclude_device(u_char bus, u_char devfn);
-#if CONFIG_85xx_PCI2
+#ifdef CONFIG_85xx_PCI2
static void __init
mpc85xx_setup_pci2(struct pci_controller *hose)
{
pci = ioremap(binfo->bi_immr_base + MPC85xx_PCI2_OFFSET,
MPC85xx_PCI2_SIZE);
- early_read_config_word(hose, 0, 0, PCI_COMMAND, &temps);
+ early_read_config_word(hose, hose->bus_offset, 0, PCI_COMMAND, &temps);
temps |= PCI_COMMAND_SERR | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
- early_write_config_word(hose, 0, 0, PCI_COMMAND, temps);
- early_write_config_byte(hose, 0, 0, PCI_LATENCY_TIMER, 0x80);
+ early_write_config_word(hose, hose->bus_offset, 0, PCI_COMMAND, temps);
+ early_write_config_byte(hose, hose->bus_offset, 0, PCI_LATENCY_TIMER, 0x80);
/* Disable all windows (except powar0 since its ignored) */
pci->powar1 = 0;
pci->piwar2 = 0;
pci->piwar3 = 0;
- /* Setup 512M Phys:PCI 1:1 outbound mem window @ 0xa0000000 */
+ /* Setup Phys:PCI 1:1 outbound mem window @ MPC85XX_PCI2_LOWER_MEM */
pci->potar1 = (MPC85XX_PCI2_LOWER_MEM >> 12) & 0x000fffff;
pci->potear1 = 0x00000000;
pci->powbar1 = (MPC85XX_PCI2_LOWER_MEM >> 12) & 0x000fffff;
- pci->powar1 = 0x8004401c; /* Enable, Mem R/W, 512M */
+ /* Enable, Mem R/W */
+ pci->powar1 = 0x80044000 |
+ (__ilog2(MPC85XX_PCI1_UPPER_MEM - MPC85XX_PCI1_LOWER_MEM + 1) - 1);
- /* Setup 16M outboud IO windows @ 0xe3000000 */
+ /* Setup outboud IO windows @ MPC85XX_PCI2_IO_BASE */
pci->potar2 = 0x00000000;
pci->potear2 = 0x00000000;
pci->powbar2 = (MPC85XX_PCI2_IO_BASE >> 12) & 0x000fffff;
- pci->powar2 = 0x80088017; /* Enable, IO R/W, 16M */
+ /* Enable, IO R/W */
+ pci->powar2 = 0x80088000 | (__ilog2(MPC85XX_PCI1_IO_SIZE) - 1);
/* Setup 2G inbound Memory Window @ 0 */
pci->pitar1 = 0x00000000;
#define __PPC_SYSLIB_PPC85XX_SETUP_H
#include <linux/config.h>
-#include <linux/serial.h>
#include <linux/init.h>
#include <asm/ppcboot.h>
#define RS_TABLE_SIZE 2
#endif
-#define BASE_BAUD 0
+#ifndef BASE_BAUD
+#define BASE_BAUD 115200
+#endif
#define STD_UART_OP(num) \
{ 0, BASE_BAUD, num, MPC85xx_IRQ_DUART, \
/* All newworld pmac machines and CHRPs now use the interrupt tree */
for (np = allnodes; np != NULL; np = np->allnext) {
- if (get_property(np, "interrupt-parent", 0)) {
+ if (get_property(np, "interrupt-parent", NULL)) {
use_of_interrupt_tree = 1;
break;
}
struct device_node *child;
int *ip;
- np->name = get_property(np, "name", 0);
- np->type = get_property(np, "device_type", 0);
+ np->name = get_property(np, "name", NULL);
+ np->type = get_property(np, "device_type", NULL);
if (!np->name)
np->name = "<NULL>";
mem_start = finish_node_interrupts(np, mem_start);
/* Look for #address-cells and #size-cells properties. */
- ip = (int *) get_property(np, "#address-cells", 0);
+ ip = (int *) get_property(np, "#address-cells", NULL);
if (ip != NULL)
naddrc = *ip;
- ip = (int *) get_property(np, "#size-cells", 0);
+ ip = (int *) get_property(np, "#size-cells", NULL);
if (ip != NULL)
nsizec = *ip;
do {
if (np->parent)
np = np->parent;
- ip = (int *) get_property(np, "#address-cells", 0);
+ ip = (int *) get_property(np, "#address-cells", NULL);
if (ip != NULL)
return *ip;
} while (np->parent);
do {
if (np->parent)
np = np->parent;
- ip = (int *) get_property(np, "#size-cells", 0);
+ ip = (int *) get_property(np, "#size-cells", NULL);
if (ip != NULL)
return *ip;
} while (np->parent);
prevp = &np->next;
}
}
- *prevp = 0;
+ *prevp = NULL;
return head;
}
prevp = &np->next;
}
}
- *prevp = 0;
+ *prevp = NULL;
return head;
}
*prevp = np;
prevp = &np->next;
}
- *prevp = 0;
+ *prevp = NULL;
return head;
}
prevp = &np->next;
}
}
- *prevp = 0;
+ *prevp = NULL;
return head;
}
*lenp = pp->length;
return pp->value;
}
- return 0;
+ return NULL;
}
/*
/* The zero index is used to indicate the end of the list of
operands. */
#define UNUSED (0)
- { 0, 0, 0, 0, 0 },
+ { 0, 0, NULL, NULL, 0 },
/* The BA field in an XL form instruction. */
#define BA (1)
#define BA_MASK (0x1f << 16)
- { 5, 16, 0, 0, PPC_OPERAND_CR },
+ { 5, 16, NULL, NULL, PPC_OPERAND_CR },
/* The BA field in an XL form instruction when it must be the same
as the BT field in the same instruction. */
/* The BB field in an XL form instruction. */
#define BB (3)
#define BB_MASK (0x1f << 11)
- { 5, 11, 0, 0, PPC_OPERAND_CR },
+ { 5, 11, NULL, NULL, PPC_OPERAND_CR },
/* The BB field in an XL form instruction when it must be the same
as the BA field in the same instruction. */
/* The BF field in an X or XL form instruction. */
#define BF (11)
- { 3, 23, 0, 0, PPC_OPERAND_CR },
+ { 3, 23, NULL, NULL, PPC_OPERAND_CR },
/* An optional BF field. This is used for comparison instructions,
in which an omitted BF field is taken as zero. */
#define OBF (12)
- { 3, 23, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL },
+ { 3, 23, NULL, NULL, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL },
/* The BFA field in an X or XL form instruction. */
#define BFA (13)
- { 3, 18, 0, 0, PPC_OPERAND_CR },
+ { 3, 18, NULL, NULL, PPC_OPERAND_CR },
/* The BI field in a B form or XL form instruction. */
#define BI (14)
#define BI_MASK (0x1f << 16)
- { 5, 16, 0, 0, PPC_OPERAND_CR },
+ { 5, 16, NULL, NULL, PPC_OPERAND_CR },
/* The BO field in a B form instruction. Certain values are
illegal. */
/* The BT field in an X or XL form instruction. */
#define BT (17)
- { 5, 21, 0, 0, PPC_OPERAND_CR },
+ { 5, 21, NULL, NULL, PPC_OPERAND_CR },
/* The condition register number portion of the BI field in a B form
or XL form instruction. This is used for the extended
conditional branch mnemonics, which set the lower two bits of the
BI field. This field is optional. */
#define CR (18)
- { 3, 18, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL },
+ { 3, 18, NULL, NULL, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL },
/* The D field in a D form instruction. This is a displacement off
a register, and implies that the next operand is a register in
parentheses. */
#define D (19)
- { 16, 0, 0, 0, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED },
+ { 16, 0, NULL, NULL, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED },
/* The DS field in a DS form instruction. This is like D, but the
lower two bits are forced to zero. */
/* The FL1 field in a POWER SC form instruction. */
#define FL1 (21)
- { 4, 12, 0, 0, 0 },
+ { 4, 12, NULL, NULL, 0 },
/* The FL2 field in a POWER SC form instruction. */
#define FL2 (22)
- { 3, 2, 0, 0, 0 },
+ { 3, 2, NULL, NULL, 0 },
/* The FLM field in an XFL form instruction. */
#define FLM (23)
- { 8, 17, 0, 0, 0 },
+ { 8, 17, NULL, NULL, 0 },
/* The FRA field in an X or A form instruction. */
#define FRA (24)
#define FRA_MASK (0x1f << 16)
- { 5, 16, 0, 0, PPC_OPERAND_FPR },
+ { 5, 16, NULL, NULL, PPC_OPERAND_FPR },
/* The FRB field in an X or A form instruction. */
#define FRB (25)
#define FRB_MASK (0x1f << 11)
- { 5, 11, 0, 0, PPC_OPERAND_FPR },
+ { 5, 11, NULL, NULL, PPC_OPERAND_FPR },
/* The FRC field in an A form instruction. */
#define FRC (26)
#define FRC_MASK (0x1f << 6)
- { 5, 6, 0, 0, PPC_OPERAND_FPR },
+ { 5, 6, NULL, NULL, PPC_OPERAND_FPR },
/* The FRS field in an X form instruction or the FRT field in a D, X
or A form instruction. */
#define FRS (27)
#define FRT (FRS)
- { 5, 21, 0, 0, PPC_OPERAND_FPR },
+ { 5, 21, NULL, NULL, PPC_OPERAND_FPR },
/* The FXM field in an XFX instruction. */
#define FXM (28)
#define FXM_MASK (0xff << 12)
- { 8, 12, 0, 0, 0 },
+ { 8, 12, NULL, NULL, 0 },
/* The L field in a D or X form instruction. */
#define L (29)
- { 1, 21, 0, 0, PPC_OPERAND_OPTIONAL },
+ { 1, 21, NULL, NULL, PPC_OPERAND_OPTIONAL },
/* The LEV field in a POWER SC form instruction. */
#define LEV (30)
- { 7, 5, 0, 0, 0 },
+ { 7, 5, NULL, NULL, 0 },
/* The LI field in an I form instruction. The lower two bits are
forced to zero. */
/* The MB field in an M form instruction. */
#define MB (33)
#define MB_MASK (0x1f << 6)
- { 5, 6, 0, 0, 0 },
+ { 5, 6, NULL, NULL, 0 },
/* The ME field in an M form instruction. */
#define ME (34)
#define ME_MASK (0x1f << 1)
- { 5, 1, 0, 0, 0 },
+ { 5, 1, NULL, NULL, 0 },
/* The MB and ME fields in an M form instruction expressed a single
operand which is a bitmask indicating which bits to select. This
is a two operand form using PPC_OPERAND_NEXT. See the
description in opcode/ppc.h for what this means. */
#define MBE (35)
- { 5, 6, 0, 0, PPC_OPERAND_OPTIONAL | PPC_OPERAND_NEXT },
+ { 5, 6, NULL, NULL, PPC_OPERAND_OPTIONAL | PPC_OPERAND_NEXT },
{ 32, 0, insert_mbe, extract_mbe, 0 },
/* The MB or ME field in an MD or MDS form instruction. The high
/* The RA field in an D, DS, X, XO, M, or MDS form instruction. */
#define RA (40)
#define RA_MASK (0x1f << 16)
- { 5, 16, 0, 0, PPC_OPERAND_GPR },
+ { 5, 16, NULL, NULL, PPC_OPERAND_GPR },
/* The RA field in a D or X form instruction which is an updating
load, which means that the RA field may not be zero and may not
equal the RT field. */
#define RAL (41)
- { 5, 16, insert_ral, 0, PPC_OPERAND_GPR },
+ { 5, 16, insert_ral, NULL, PPC_OPERAND_GPR },
/* The RA field in an lmw instruction, which has special value
restrictions. */
#define RAM (42)
- { 5, 16, insert_ram, 0, PPC_OPERAND_GPR },
+ { 5, 16, insert_ram, NULL, PPC_OPERAND_GPR },
/* The RA field in a D or X form instruction which is an updating
store or an updating floating point load, which means that the RA
field may not be zero. */
#define RAS (43)
- { 5, 16, insert_ras, 0, PPC_OPERAND_GPR },
+ { 5, 16, insert_ras, NULL, PPC_OPERAND_GPR },
/* The RB field in an X, XO, M, or MDS form instruction. */
#define RB (44)
#define RB_MASK (0x1f << 11)
- { 5, 11, 0, 0, PPC_OPERAND_GPR },
+ { 5, 11, NULL, NULL, PPC_OPERAND_GPR },
/* The RB field in an X form instruction when it must be the same as
the RS field in the instruction. This is used for extended
#define RS (46)
#define RT (RS)
#define RT_MASK (0x1f << 21)
- { 5, 21, 0, 0, PPC_OPERAND_GPR },
+ { 5, 21, NULL, NULL, PPC_OPERAND_GPR },
/* The SH field in an X or M form instruction. */
#define SH (47)
#define SH_MASK (0x1f << 11)
- { 5, 11, 0, 0, 0 },
+ { 5, 11, NULL, NULL, 0 },
/* The SH field in an MD form instruction. This is split. */
#define SH6 (48)
/* The SI field in a D form instruction. */
#define SI (49)
- { 16, 0, 0, 0, PPC_OPERAND_SIGNED },
+ { 16, 0, NULL, NULL, PPC_OPERAND_SIGNED },
/* The SI field in a D form instruction when we accept a wide range
of positive values. */
#define SISIGNOPT (50)
- { 16, 0, 0, 0, PPC_OPERAND_SIGNED | PPC_OPERAND_SIGNOPT },
+ { 16, 0, NULL, NULL, PPC_OPERAND_SIGNED | PPC_OPERAND_SIGNOPT },
/* The SPR field in an XFX form instruction. This is flipped--the
lower 5 bits are stored in the upper 5 and vice- versa. */
/* The BAT index number in an XFX form m[ft]ibat[lu] instruction. */
#define SPRBAT (52)
#define SPRBAT_MASK (0x3 << 17)
- { 2, 17, 0, 0, 0 },
+ { 2, 17, NULL, NULL, 0 },
/* The SPRG register number in an XFX form m[ft]sprg instruction. */
#define SPRG (53)
#define SPRG_MASK (0x3 << 16)
- { 2, 16, 0, 0, 0 },
+ { 2, 16, NULL, NULL, 0 },
/* The SR field in an X form instruction. */
#define SR (54)
- { 4, 16, 0, 0, 0 },
+ { 4, 16, NULL, NULL, 0 },
/* The SV field in a POWER SC form instruction. */
#define SV (55)
- { 14, 2, 0, 0, 0 },
+ { 14, 2, NULL, NULL, 0 },
/* The TBR field in an XFX form instruction. This is like the SPR
field, but it is optional. */
/* The TO field in a D or X form instruction. */
#define TO (57)
#define TO_MASK (0x1f << 21)
- { 5, 21, 0, 0, 0 },
+ { 5, 21, NULL, NULL, 0 },
/* The U field in an X form instruction. */
#define U (58)
- { 4, 12, 0, 0, 0 },
+ { 4, 12, NULL, NULL, 0 },
/* The UI field in a D form instruction. */
#define UI (59)
- { 16, 0, 0, 0, 0 },
+ { 16, 0, NULL, NULL, 0 },
};
/* The functions used to insert and extract complicated operands. */
scc_initialized = 1;
if (via_modem) {
for (;;) {
- xmon_write(0, "ATE1V1\r", 7);
+ xmon_write(NULL, "ATE1V1\r", 7);
if (xmon_expect("OK", 5)) {
- xmon_write(0, "ATA\r", 4);
+ xmon_write(NULL, "ATA\r", 4);
if (xmon_expect("CONNECT", 40))
break;
}
- xmon_write(0, "+++", 3);
+ xmon_write(NULL, "+++", 3);
xmon_expect("OK", 3);
}
}
c = xmon_getchar();
if (c == -1) {
if (p == str)
- return 0;
+ return NULL;
break;
}
*p++ = c;
set_backlight_level(BACKLIGHT_MAX);
sync();
}
- debugger_fault_handler = 0;
+ debugger_fault_handler = NULL;
#endif /* CONFIG_PMAC_BACKLIGHT */
cmd = cmds(excp);
if (cmd == 's') {
insert_bpts();
}
xmon_leave();
- xmon_regs[smp_processor_id()] = 0;
+ xmon_regs[smp_processor_id()] = NULL;
#ifdef CONFIG_SMP
clear_bit(0, &got_xmon);
clear_bit(smp_processor_id(), &cpus_in_xmon);
for (i = 0; i < NBPTS; ++i, ++bp)
if (bp->enabled && pc == bp->address)
return bp;
- return 0;
+ return NULL;
}
static void
xmon_puts(sysmap);
sync();
}
- debugger_fault_handler = 0;
+ debugger_fault_handler = NULL;
}
else
printf("No System.map\n");
__delay(200);
n = size;
}
- debugger_fault_handler = 0;
+ debugger_fault_handler = NULL;
return n;
}
} else {
printf("*** Error writing address %x\n", adrs + n);
}
- debugger_fault_handler = 0;
+ debugger_fault_handler = NULL;
return n;
}
} else {
printf("*** %x exception occurred\n", fault_except);
}
- debugger_fault_handler = 0;
+ debugger_fault_handler = NULL;
}
/* Input scanning routines */
} while (cur);
sync();
}
- debugger_fault_handler = 0;
+ debugger_fault_handler = NULL;
termch = 0;
break;
}
*(ep++) = 0;
if (saddr)
*saddr = prev;
- debugger_fault_handler = 0;
+ debugger_fault_handler = NULL;
return rbuffer;
}
prev = next;
bail:
sync();
}
- debugger_fault_handler = 0;
+ debugger_fault_handler = NULL;
return NULL;
}
}
sync();
}
- debugger_fault_handler = 0;
+ debugger_fault_handler = NULL;
return result;
}
* 2 of the License, or (at your option) any later version.
*/
#include <asm/ppc_asm.h>
-#include <asm/processor.h>
.globl __div64_32
__div64_32:
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <asm/hvcall.h>
#include <asm/prom.h>
#include <asm/hvconsole.h>
return 0;
}
+EXPORT_SYMBOL(hvc_get_chars);
+
int hvc_put_chars(int index, const char *buf, int count)
{
unsigned long *lbuf = (unsigned long *) buf;
return -1;
}
+EXPORT_SYMBOL(hvc_put_chars);
+
/* return the number of client vterms present */
/* XXX this requires an interface change to handle multiple discontiguous
* vterms */
* we should _always_ be able to find one. */
vty = of_find_node_by_name(NULL, "vty");
if (vty && device_is_compatible(vty, "hvterm1")) {
- u32 *termno = (u32 *)get_property(vty, "reg", 0);
+ u32 *termno = (u32 *)get_property(vty, "reg", NULL);
if (termno && start_termno)
*start_termno = *termno;
--- /dev/null
+/*
+ * hvcserver.c
+ * Copyright (C) 2004 Ryan S Arnold, IBM Corporation
+ *
+ * PPC64 virtual I/O console server support.
+ *
+ * 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/list.h>
+#include <linux/module.h>
+#include <asm/hvcall.h>
+#include <asm/hvcserver.h>
+#include <asm/io.h>
+
+#define HVCS_ARCH_VERSION "1.0.0"
+
+MODULE_AUTHOR("Ryan S. Arnold <rsa@us.ibm.com>");
+MODULE_DESCRIPTION("IBM hvcs ppc64 API");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(HVCS_ARCH_VERSION);
+
+/*
+ * Convert arch specific return codes into relevant errnos. The hvcs
+ * functions aren't performance sensitive, so this conversion isn't an
+ * issue.
+ */
+int hvcs_convert(long to_convert)
+{
+ switch (to_convert) {
+ case H_Success:
+ return 0;
+ case H_Parameter:
+ return -EINVAL;
+ case H_Hardware:
+ return -EIO;
+ case H_Busy:
+ case H_LongBusyOrder1msec:
+ case H_LongBusyOrder10msec:
+ case H_LongBusyOrder100msec:
+ case H_LongBusyOrder1sec:
+ case H_LongBusyOrder10sec:
+ case H_LongBusyOrder100sec:
+ return -EBUSY;
+ case H_Function: /* fall through */
+ default:
+ return -EPERM;
+ }
+}
+
+int hvcs_free_partner_info(struct list_head *head)
+{
+ struct hvcs_partner_info *pi;
+ struct list_head *element;
+
+ if (!head) {
+ return -EINVAL;
+ }
+
+ while (!list_empty(head)) {
+ element = head->next;
+ pi = list_entry(element, struct hvcs_partner_info, node);
+ list_del(element);
+ kfree(pi);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(hvcs_free_partner_info);
+
+/* Helper function for hvcs_get_partner_info */
+int hvcs_next_partner(unsigned int unit_address,
+ unsigned long last_p_partition_ID,
+ unsigned long last_p_unit_address, unsigned long *pi_buff)
+
+{
+ long retval;
+ retval = plpar_hcall_norets(H_VTERM_PARTNER_INFO, unit_address,
+ last_p_partition_ID,
+ last_p_unit_address, virt_to_phys(pi_buff));
+ return hvcs_convert(retval);
+}
+
+/*
+ * The unit_address parameter is the unit address of the vty-server vdevice
+ * in whose partner information the caller is interested. This function
+ * uses a pointer to a list_head instance in which to store the partner info.
+ * This function returns non-zero on success, or if there is no partner info.
+ *
+ * Invocation of this function should always be followed by an invocation of
+ * hvcs_free_partner_info() using a pointer to the SAME list head instance
+ * that was used to store the partner_info list.
+ */
+int hvcs_get_partner_info(unsigned int unit_address, struct list_head *head,
+ unsigned long *pi_buff)
+{
+ /*
+ * This is a page sized buffer to be passed to hvcall per invocation.
+ * NOTE: the first long returned is unit_address. The second long
+ * returned is the partition ID and starting with pi_buff[2] are
+ * HVCS_CLC_LENGTH characters, which are diff size than the unsigned
+ * long, hence the casting mumbojumbo you see later.
+ */
+ unsigned long last_p_partition_ID;
+ unsigned long last_p_unit_address;
+ struct hvcs_partner_info *next_partner_info = NULL;
+ int more = 1;
+ int retval;
+
+ memset(pi_buff, 0x00, PAGE_SIZE);
+ /* invalid parameters */
+ if (!head)
+ return -EINVAL;
+
+ last_p_partition_ID = last_p_unit_address = ~0UL;
+ INIT_LIST_HEAD(head);
+
+ if (!pi_buff)
+ return -ENOMEM;
+
+ do {
+ retval = hvcs_next_partner(unit_address, last_p_partition_ID,
+ last_p_unit_address, pi_buff);
+ if (retval) {
+ /*
+ * Don't indicate that we've failed if we have
+ * any list elements.
+ */
+ if (!list_empty(head))
+ return 0;
+ return retval;
+ }
+
+ last_p_partition_ID = pi_buff[0];
+ last_p_unit_address = pi_buff[1];
+
+ /* This indicates that there are no further partners */
+ if (last_p_partition_ID == ~0UL
+ && last_p_unit_address == ~0UL)
+ break;
+
+ /* This is a very small struct and will be freed soon in
+ * hvcs_free_partner_info(). */
+ next_partner_info = kmalloc(sizeof(struct hvcs_partner_info),
+ GFP_ATOMIC);
+
+ if (!next_partner_info) {
+ printk(KERN_WARNING "HVCONSOLE: kmalloc() failed to"
+ " allocate partner info struct.\n");
+ hvcs_free_partner_info(head);
+ return -ENOMEM;
+ }
+
+ next_partner_info->unit_address
+ = (unsigned int)last_p_unit_address;
+ next_partner_info->partition_ID
+ = (unsigned int)last_p_partition_ID;
+
+ /* copy the Null-term char too */
+ strncpy(&next_partner_info->location_code[0],
+ (char *)&pi_buff[2],
+ strlen((char *)&pi_buff[2]) + 1);
+
+ list_add_tail(&(next_partner_info->node), head);
+ next_partner_info = NULL;
+
+ } while (more);
+
+ return 0;
+}
+EXPORT_SYMBOL(hvcs_get_partner_info);
+
+/*
+ * If this function is called once and -EINVAL is returned it may
+ * indicate that the partner info needs to be refreshed for the
+ * target unit address at which point the caller must invoke
+ * hvcs_get_partner_info() and then call this function again. If,
+ * for a second time, -EINVAL is returned then it indicates that
+ * there is probably already a partner connection registered to a
+ * different vty-server@ vdevice. It is also possible that a second
+ * -EINVAL may indicate that one of the parms is not valid, for
+ * instance if the link was removed between the vty-server@ vdevice
+ * and the vty@ vdevice that you are trying to open. Don't shoot the
+ * messenger. Firmware implemented it this way.
+ */
+int hvcs_register_connection( unsigned int unit_address,
+ unsigned int p_partition_ID, unsigned int p_unit_address)
+{
+ long retval;
+ retval = plpar_hcall_norets(H_REGISTER_VTERM, unit_address,
+ p_partition_ID, p_unit_address);
+ return hvcs_convert(retval);
+}
+EXPORT_SYMBOL(hvcs_register_connection);
+
+/*
+ * If -EBUSY is returned continue to call this function
+ * until 0 is returned.
+ */
+int hvcs_free_connection(unsigned int unit_address)
+{
+ long retval;
+ retval = plpar_hcall_norets(H_FREE_VTERM, unit_address);
+ return hvcs_convert(retval);
+}
+EXPORT_SYMBOL(hvcs_free_connection);
#include <asm/mmu_context.h>
#include <asm/iSeries/HvCallHpt.h>
#include <asm/abs_addr.h>
-
-#if 0
#include <linux/spinlock.h>
-#include <linux/bitops.h>
-#include <linux/threads.h>
-#include <linux/smp.h>
-#include <asm/tlbflush.h>
-#include <asm/tlb.h>
-#include <asm/cputable.h>
-#endif
+static spinlock_t iSeries_hlocks[64] __cacheline_aligned_in_smp = { [0 ... 63] = SPIN_LOCK_UNLOCKED};
+
+/*
+ * Very primitive algorithm for picking up a lock
+ */
+static inline void iSeries_hlock(unsigned long slot)
+{
+ if (slot & 0x8)
+ slot = ~slot;
+ spin_lock(&iSeries_hlocks[(slot >> 4) & 0x3f]);
+}
+
+static inline void iSeries_hunlock(unsigned long slot)
+{
+ if (slot & 0x8)
+ slot = ~slot;
+ spin_unlock(&iSeries_hlocks[(slot >> 4) & 0x3f]);
+}
static long iSeries_hpte_insert(unsigned long hpte_group, unsigned long va,
unsigned long prpn, int secondary,
if (secondary)
return -1;
+ iSeries_hlock(hpte_group);
+
slot = HvCallHpt_findValid(&lhpte, va >> PAGE_SHIFT);
- if (lhpte.dw0.dw0.v)
- panic("select_hpte_slot found entry already valid\n");
+ BUG_ON(lhpte.dw0.dw0.v);
- if (slot == -1) /* No available entry found in either group */
+ if (slot == -1) { /* No available entry found in either group */
+ iSeries_hunlock(hpte_group);
return -1;
+ }
if (slot < 0) { /* MSB set means secondary group */
secondary = 1;
/* Now fill in the actual HPTE */
HvCallHpt_addValidate(slot, secondary, &lhpte);
+ iSeries_hunlock(hpte_group);
+
return (secondary << 3) | (slot & 7);
}
/* Pick a random slot to start at */
slot_offset = mftb() & 0x7;
+ iSeries_hlock(hpte_group);
+
for (i = 0; i < HPTES_PER_GROUP; i++) {
lhpte.dw0.dword0 =
iSeries_hpte_getword0(hpte_group + slot_offset);
if (!lhpte.dw0.dw0.bolted) {
HvCallHpt_invalidateSetSwBitsGet(hpte_group +
slot_offset, 0, 0);
+ iSeries_hunlock(hpte_group);
return i;
}
slot_offset &= 0x7;
}
+ iSeries_hunlock(hpte_group);
+
return -1;
}
HPTE hpte;
unsigned long avpn = va >> 23;
+ iSeries_hlock(slot);
+
HvCallHpt_get(&hpte, slot);
if ((hpte.dw0.dw0.avpn == avpn) && (hpte.dw0.dw0.v)) {
HvCallHpt_setPp(slot, (newpp & 0x3) | ((newpp & 0x4) << 1));
+ iSeries_hunlock(slot);
return 0;
}
+ iSeries_hunlock(slot);
+
return -1;
}
{
HPTE lhpte;
unsigned long avpn = va >> 23;
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ iSeries_hlock(slot);
lhpte.dw0.dword0 = iSeries_hpte_getword0(slot);
if ((lhpte.dw0.dw0.avpn == avpn) && lhpte.dw0.dw0.v)
HvCallHpt_invalidateSetSwBitsGet(slot, 0, 0);
+
+ iSeries_hunlock(slot);
+
+ local_irq_restore(flags);
}
void hpte_init_iSeries(void)
HPTE *hptep = htab_data.htab + slot;
Hpte_dword0 dw0;
unsigned long avpn = va >> 23;
- unsigned long flags;
int ret = 0;
if (large)
tlbiel(va);
} else {
if (!(cur_cpu_spec->cpu_features & CPU_FTR_LOCKLESS_TLBIE))
- spin_lock_irqsave(&pSeries_tlbie_lock, flags);
+ spin_lock(&pSeries_tlbie_lock);
tlbie(va, large);
if (!(cur_cpu_spec->cpu_features & CPU_FTR_LOCKLESS_TLBIE))
- spin_unlock_irqrestore(&pSeries_tlbie_lock, flags);
+ spin_unlock(&pSeries_tlbie_lock);
}
return ret;
if (large)
avpn &= ~0x1UL;
+ local_irq_save(flags);
pSeries_lock_hpte(hptep);
dw0 = hptep->dw0.dw0;
tlbiel(va);
} else {
if (!(cur_cpu_spec->cpu_features & CPU_FTR_LOCKLESS_TLBIE))
- spin_lock_irqsave(&pSeries_tlbie_lock, flags);
+ spin_lock(&pSeries_tlbie_lock);
tlbie(va, large);
if (!(cur_cpu_spec->cpu_features & CPU_FTR_LOCKLESS_TLBIE))
- spin_unlock_irqrestore(&pSeries_tlbie_lock, flags);
+ spin_unlock(&pSeries_tlbie_lock);
}
+ local_irq_restore(flags);
}
static void pSeries_flush_hash_range(unsigned long context,
/* XXX fix for large ptes */
unsigned long large = 0;
+ local_irq_save(flags);
+
j = 0;
for (i = 0; i < number; i++) {
if ((batch->addr[i] >= USER_START) &&
} else {
/* XXX double check that it is safe to take this late */
if (!(cur_cpu_spec->cpu_features & CPU_FTR_LOCKLESS_TLBIE))
- spin_lock_irqsave(&pSeries_tlbie_lock, flags);
+ spin_lock(&pSeries_tlbie_lock);
asm volatile("ptesync":::"memory");
asm volatile("eieio; tlbsync; ptesync":::"memory");
if (!(cur_cpu_spec->cpu_features & CPU_FTR_LOCKLESS_TLBIE))
- spin_unlock_irqrestore(&pSeries_tlbie_lock, flags);
+ spin_unlock(&pSeries_tlbie_lock);
}
+
+ local_irq_restore(flags);
}
void hpte_init_pSeries(void)
bus = pci_bus_b(ln);
busdn = PCI_GET_DN(bus);
- dma_window = (unsigned int *)get_property(busdn, "ibm,dma-window", 0);
+ dma_window = (unsigned int *)get_property(busdn, "ibm,dma-window", NULL);
if (dma_window) {
/* Bussubno hasn't been copied yet.
* Do it now because iommu_table_setparms_lpar needs it.
{
unsigned int *dma_window;
- dma_window = (unsigned int *)get_property(dn, "ibm,dma-window", 0);
+ dma_window = (unsigned int *)get_property(dn, "ibm,dma-window", NULL);
if (!dma_window)
panic("iommu_table_setparms_lpar: device %s has no"
}
/* now we have the stdout node; figure out what type of device it is. */
- name = (char *)get_property(stdout_node, "name", 0);
+ name = (char *)get_property(stdout_node, "name", NULL);
if (!name) {
printk(KERN_WARNING "stdout node missing 'name' property!\n");
goto out;
if (strncmp(name, "vty", 3) == 0) {
if (device_is_compatible(stdout_node, "hvterm1")) {
- termno = (u32 *)get_property(stdout_node, "reg", 0);
+ termno = (u32 *)get_property(stdout_node, "reg", NULL);
if (termno) {
vtermno = termno[0];
ppc_md.udbg_putc = udbg_putcLP;
* 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/config.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include "pci.h"
-/* Traverse_func that inits the PCI fields of the device node.
+/*
+ * Traverse_func that inits the PCI fields of the device node.
* NOTE: this *must* be done before read/write config to the device.
*/
-static void * __init
-update_dn_pci_info(struct device_node *dn, void *data)
+static void * __init update_dn_pci_info(struct device_node *dn, void *data)
{
-#ifdef CONFIG_PPC_PSERIES
- struct pci_controller *phb = (struct pci_controller *)data;
+ struct pci_controller *phb = data;
u32 *regs;
- char *device_type = get_property(dn, "device_type", 0);
+ char *device_type = get_property(dn, "device_type", NULL);
char *model;
dn->phb = phb;
- if (device_type && strcmp(device_type, "pci") == 0 && get_property(dn, "class-code", 0) == 0) {
+ if (device_type && (strcmp(device_type, "pci") == 0) &&
+ (get_property(dn, "class-code", NULL) == 0)) {
/* special case for PHB's. Sigh. */
- regs = (u32 *)get_property(dn, "bus-range", 0);
+ regs = (u32 *)get_property(dn, "bus-range", NULL);
dn->busno = regs[0];
model = (char *)get_property(dn, "model", NULL);
else
dn->devfn = 0; /* assumption */
} else {
- regs = (u32 *)get_property(dn, "reg", 0);
+ regs = (u32 *)get_property(dn, "reg", NULL);
if (regs) {
/* First register entry is addr (00BBSS00) */
dn->busno = (regs[0] >> 16) & 0xff;
dn->devfn = (regs[0] >> 8) & 0xff;
}
}
-#endif
return NULL;
}
-/******************************************************************
+/*
* Traverse a device tree stopping each PCI device in the tree.
* This is done depth first. As each node is processed, a "pre"
- * function is called, the children are processed recursively, and
- * then a "post" function is called.
+ * function is called and the children are processed recursively.
*
- * The "pre" and "post" funcs return a value. If non-zero
- * is returned from the "pre" func, the traversal stops and this
- * value is returned. The return value from "post" is not used.
- * This return value is useful when using traverse as
- * a method of finding a device.
+ * The "pre" func returns a value. If non-zero is returned from
+ * the "pre" func, the traversal stops and this value is returned.
+ * This return value is useful when using traverse as a method of
+ * finding a device.
*
- * NOTE: we do not run the funcs for devices that do not appear to
+ * NOTE: we do not run the func for devices that do not appear to
* be PCI except for the start node which we assume (this is good
* because the start node is often a phb which may be missing PCI
* properties).
* We use the class-code as an indicator. If we run into
* one of these nodes we also assume its siblings are non-pci for
* performance.
- *
- ******************************************************************/
-void *traverse_pci_devices(struct device_node *start, traverse_func pre, traverse_func post, void *data)
+ */
+void *traverse_pci_devices(struct device_node *start, traverse_func pre,
+ void *data)
{
struct device_node *dn, *nextdn;
void *ret;
- if (pre && (ret = pre(start, data)) != NULL)
+ if (pre && ((ret = pre(start, data)) != NULL))
return ret;
for (dn = start->child; dn; dn = nextdn) {
nextdn = NULL;
-#ifdef CONFIG_PPC_PSERIES
- if (get_property(dn, "class-code", 0)) {
- if (pre && (ret = pre(dn, data)) != NULL)
+ if (get_property(dn, "class-code", NULL)) {
+ if (pre && ((ret = pre(dn, data)) != NULL))
return ret;
- if (dn->child) {
+ if (dn->child)
/* Depth first...do children */
nextdn = dn->child;
- } else if (dn->sibling) {
+ else if (dn->sibling)
/* ok, try next sibling instead. */
nextdn = dn->sibling;
- } else {
- /* no more children or siblings...call "post" */
- if (post)
- post(dn, data);
- }
}
-#endif
if (!nextdn) {
/* Walk up to next valid sibling. */
do {
return NULL;
}
-/* Same as traverse_pci_devices except this does it for all phbs.
+/*
+ * Same as traverse_pci_devices except this does it for all phbs.
*/
-void *traverse_all_pci_devices(traverse_func pre)
+static void *traverse_all_pci_devices(traverse_func pre)
{
- struct pci_controller* phb;
+ struct pci_controller *phb;
void *ret;
- for (phb=hose_head;phb;phb=phb->next)
- if ((ret = traverse_pci_devices((struct device_node *)phb->arch_data, pre, NULL, phb)) != NULL)
+
+ for (phb = hose_head; phb; phb = phb->next)
+ if ((ret = traverse_pci_devices(phb->arch_data, pre, phb))
+ != NULL)
return ret;
return NULL;
}
-/* Traversal func that looks for a <busno,devfcn> value.
+/*
+ * Traversal func that looks for a <busno,devfcn> value.
* If found, the device_node is returned (thus terminating the traversal).
*/
-static void *
-is_devfn_node(struct device_node *dn, void *data)
+static void *is_devfn_node(struct device_node *dn, void *data)
{
int busno = ((unsigned long)data >> 8) & 0xff;
int devfn = ((unsigned long)data) & 0xff;
- return (devfn == dn->devfn && busno == dn->busno) ? dn : NULL;
+ return ((devfn == dn->devfn) && (busno == dn->busno)) ? dn : NULL;
}
-/* This is the "slow" path for looking up a device_node from a
+/*
+ * This is the "slow" path for looking up a device_node from a
* pci_dev. It will hunt for the device under its parent's
* phb and then update sysdata for a future fastpath.
*
*/
struct device_node *fetch_dev_dn(struct pci_dev *dev)
{
- struct device_node *orig_dn = (struct device_node *)dev->sysdata;
+ struct device_node *orig_dn = dev->sysdata;
struct pci_controller *phb = orig_dn->phb; /* assume same phb as orig_dn */
struct device_node *phb_dn;
struct device_node *dn;
unsigned long searchval = (dev->bus->number << 8) | dev->devfn;
- phb_dn = (struct device_node *)(phb->arch_data);
- dn = (struct device_node *)traverse_pci_devices(phb_dn, is_devfn_node, NULL, (void *)searchval);
+ phb_dn = phb->arch_data;
+ dn = traverse_pci_devices(phb_dn, is_devfn_node, (void *)searchval);
if (dn) {
dev->sysdata = dn;
/* ToDo: call some device init hook here */
EXPORT_SYMBOL(fetch_dev_dn);
-/******************************************************************
+/*
* Actually initialize the phbs.
* The buswalk on this phb has not happened yet.
- ******************************************************************/
-void __init
-pci_devs_phb_init(void)
+ */
+void __init pci_devs_phb_init(void)
{
/* This must be done first so the device nodes have valid pci info! */
traverse_all_pci_devices(update_dn_pci_info);
}
-static void __init
-pci_fixup_bus_sysdata_list(struct list_head *bus_list)
+static void __init pci_fixup_bus_sysdata_list(struct list_head *bus_list)
{
struct list_head *ln;
struct pci_bus *bus;
- for (ln=bus_list->next; ln != bus_list; ln=ln->next) {
+ for (ln = bus_list->next; ln != bus_list; ln = ln->next) {
bus = pci_bus_b(ln);
if (bus->self)
bus->sysdata = bus->self->sysdata;
}
}
-/******************************************************************
+/*
* Fixup the bus->sysdata ptrs to point to the bus' device_node.
* This is done late in pcibios_init(). We do this mostly for
* sanity, but pci_dma.c uses these at DMA time so they must be
* To do this we recurse down the bus hierarchy. Note that PHB's
* have bus->self == NULL, but fortunately bus->sysdata is already
* correct in this case.
- ******************************************************************/
-void __init
-pci_fix_bus_sysdata(void)
+ */
+void __init pci_fix_bus_sysdata(void)
{
pci_fixup_bus_sysdata_list(&pci_root_buses);
}
int len;
/* For PCI<->PCI bridges or CardBus bridges, we go down */
- class_code = (unsigned int *) get_property(node, "class-code", 0);
+ class_code = (unsigned int *) get_property(node, "class-code", NULL);
if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI &&
(*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS))
continue;
{
struct proc_dir_entry *root;
- root = proc_mkdir("ppc64", 0);
+ root = proc_mkdir("ppc64", NULL);
if (!root)
return 1;
if (!proc_mkdir("rtas", root))
return 1;
- if (!proc_symlink("rtas", 0, "ppc64/rtas"))
+ if (!proc_symlink("rtas", NULL, "ppc64/rtas"))
return 1;
return 0;
return (file->f_pos = new);
}
-static ssize_t page_map_read( struct file *file, char *buf, size_t nbytes, loff_t *ppos)
+static ssize_t page_map_read( struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
- unsigned pos = *ppos;
struct proc_dir_entry *dp = PDE(file->f_dentry->d_inode);
-
- if ( pos >= dp->size )
- return 0;
- if ( nbytes >= dp->size )
- nbytes = dp->size;
- if ( pos + nbytes > dp->size )
- nbytes = dp->size - pos;
-
- copy_to_user( buf, (char *)dp->data + pos, nbytes );
- *ppos = pos + nbytes;
- return nbytes;
+ return simple_read_from_buffer(buf, nbytes, ppos, dp->data, dp->size);
}
static int page_map_mmap( struct file *file, struct vm_area_struct *vma )
--- /dev/null
+/*
+ * Routines to emulate some Altivec/VMX instructions, specifically
+ * those that can trap when given denormalized operands in Java mode.
+ */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+
+/* Functions in vector.S */
+extern void vaddfp(vector128 *dst, vector128 *a, vector128 *b);
+extern void vsubfp(vector128 *dst, vector128 *a, vector128 *b);
+extern void vmaddfp(vector128 *dst, vector128 *a, vector128 *b, vector128 *c);
+extern void vnmsubfp(vector128 *dst, vector128 *a, vector128 *b, vector128 *c);
+extern void vrefp(vector128 *dst, vector128 *src);
+extern void vrsqrtefp(vector128 *dst, vector128 *src);
+extern void vexptep(vector128 *dst, vector128 *src);
+
+static unsigned int exp2s[8] = {
+ 0x800000,
+ 0x8b95c2,
+ 0x9837f0,
+ 0xa5fed7,
+ 0xb504f3,
+ 0xc5672a,
+ 0xd744fd,
+ 0xeac0c7
+};
+
+/*
+ * Computes an estimate of 2^x. The `s' argument is the 32-bit
+ * single-precision floating-point representation of x.
+ */
+static unsigned int eexp2(unsigned int s)
+{
+ int exp, pwr;
+ unsigned int mant, frac;
+
+ /* extract exponent field from input */
+ exp = ((s >> 23) & 0xff) - 127;
+ if (exp > 7) {
+ /* check for NaN input */
+ if (exp == 128 && (s & 0x7fffff) != 0)
+ return s | 0x400000; /* return QNaN */
+ /* 2^-big = 0, 2^+big = +Inf */
+ return (s & 0x80000000)? 0: 0x7f800000; /* 0 or +Inf */
+ }
+ if (exp < -23)
+ return 0x3f800000; /* 1.0 */
+
+ /* convert to fixed point integer in 9.23 representation */
+ pwr = (s & 0x7fffff) | 0x800000;
+ if (exp > 0)
+ pwr <<= exp;
+ else
+ pwr >>= -exp;
+ if (s & 0x80000000)
+ pwr = -pwr;
+
+ /* extract integer part, which becomes exponent part of result */
+ exp = (pwr >> 23) + 126;
+ if (exp >= 254)
+ return 0x7f800000;
+ if (exp < -23)
+ return 0;
+
+ /* table lookup on top 3 bits of fraction to get mantissa */
+ mant = exp2s[(pwr >> 20) & 7];
+
+ /* linear interpolation using remaining 20 bits of fraction */
+ asm("mulhwu %0,%1,%2" : "=r" (frac)
+ : "r" (pwr << 12), "r" (0x172b83ff));
+ asm("mulhwu %0,%1,%2" : "=r" (frac) : "r" (frac), "r" (mant));
+ mant += frac;
+
+ if (exp >= 0)
+ return mant + (exp << 23);
+
+ /* denormalized result */
+ exp = -exp;
+ mant += 1 << (exp - 1);
+ return mant >> exp;
+}
+
+/*
+ * Computes an estimate of log_2(x). The `s' argument is the 32-bit
+ * single-precision floating-point representation of x.
+ */
+static unsigned int elog2(unsigned int s)
+{
+ int exp, mant, lz, frac;
+
+ exp = s & 0x7f800000;
+ mant = s & 0x7fffff;
+ if (exp == 0x7f800000) { /* Inf or NaN */
+ if (mant != 0)
+ s |= 0x400000; /* turn NaN into QNaN */
+ return s;
+ }
+ if ((exp | mant) == 0) /* +0 or -0 */
+ return 0xff800000; /* return -Inf */
+
+ if (exp == 0) {
+ /* denormalized */
+ asm("cntlzw %0,%1" : "=r" (lz) : "r" (mant));
+ mant <<= lz - 8;
+ exp = (-118 - lz) << 23;
+ } else {
+ mant |= 0x800000;
+ exp -= 127 << 23;
+ }
+
+ if (mant >= 0xb504f3) { /* 2^0.5 * 2^23 */
+ exp |= 0x400000; /* 0.5 * 2^23 */
+ asm("mulhwu %0,%1,%2" : "=r" (mant)
+ : "r" (mant), "r" (0xb504f334)); /* 2^-0.5 * 2^32 */
+ }
+ if (mant >= 0x9837f0) { /* 2^0.25 * 2^23 */
+ exp |= 0x200000; /* 0.25 * 2^23 */
+ asm("mulhwu %0,%1,%2" : "=r" (mant)
+ : "r" (mant), "r" (0xd744fccb)); /* 2^-0.25 * 2^32 */
+ }
+ if (mant >= 0x8b95c2) { /* 2^0.125 * 2^23 */
+ exp |= 0x100000; /* 0.125 * 2^23 */
+ asm("mulhwu %0,%1,%2" : "=r" (mant)
+ : "r" (mant), "r" (0xeac0c6e8)); /* 2^-0.125 * 2^32 */
+ }
+ if (mant > 0x800000) { /* 1.0 * 2^23 */
+ /* calculate (mant - 1) * 1.381097463 */
+ /* 1.381097463 == 0.125 / (2^0.125 - 1) */
+ asm("mulhwu %0,%1,%2" : "=r" (frac)
+ : "r" ((mant - 0x800000) << 1), "r" (0xb0c7cd3a));
+ exp += frac;
+ }
+ s = exp & 0x80000000;
+ if (exp != 0) {
+ if (s)
+ exp = -exp;
+ asm("cntlzw %0,%1" : "=r" (lz) : "r" (exp));
+ lz = 8 - lz;
+ if (lz > 0)
+ exp >>= lz;
+ else if (lz < 0)
+ exp <<= -lz;
+ s += ((lz + 126) << 23) + exp;
+ }
+ return s;
+}
+
+#define VSCR_SAT 1
+
+static int ctsxs(unsigned int x, int scale, unsigned int *vscrp)
+{
+ int exp, mant;
+
+ exp = (x >> 23) & 0xff;
+ mant = x & 0x7fffff;
+ if (exp == 255 && mant != 0)
+ return 0; /* NaN -> 0 */
+ exp = exp - 127 + scale;
+ if (exp < 0)
+ return 0; /* round towards zero */
+ if (exp >= 31) {
+ /* saturate, unless the result would be -2^31 */
+ if (x + (scale << 23) != 0xcf000000)
+ *vscrp |= VSCR_SAT;
+ return (x & 0x80000000)? 0x80000000: 0x7fffffff;
+ }
+ mant |= 0x800000;
+ mant = (mant << 7) >> (30 - exp);
+ return (x & 0x80000000)? -mant: mant;
+}
+
+static unsigned int ctuxs(unsigned int x, int scale, unsigned int *vscrp)
+{
+ int exp;
+ unsigned int mant;
+
+ exp = (x >> 23) & 0xff;
+ mant = x & 0x7fffff;
+ if (exp == 255 && mant != 0)
+ return 0; /* NaN -> 0 */
+ exp = exp - 127 + scale;
+ if (exp < 0)
+ return 0; /* round towards zero */
+ if (x & 0x80000000) {
+ /* negative => saturate to 0 */
+ *vscrp |= VSCR_SAT;
+ return 0;
+ }
+ if (exp >= 32) {
+ /* saturate */
+ *vscrp |= VSCR_SAT;
+ return 0xffffffff;
+ }
+ mant |= 0x800000;
+ mant = (mant << 8) >> (31 - exp);
+ return mant;
+}
+
+/* Round to floating integer, towards 0 */
+static unsigned int rfiz(unsigned int x)
+{
+ int exp;
+
+ exp = ((x >> 23) & 0xff) - 127;
+ if (exp == 128 && (x & 0x7fffff) != 0)
+ return x | 0x400000; /* NaN -> make it a QNaN */
+ if (exp >= 23)
+ return x; /* it's an integer already (or Inf) */
+ if (exp < 0)
+ return x & 0x80000000; /* |x| < 1.0 rounds to 0 */
+ return x & ~(0x7fffff >> exp);
+}
+
+/* Round to floating integer, towards +/- Inf */
+static unsigned int rfii(unsigned int x)
+{
+ int exp, mask;
+
+ exp = ((x >> 23) & 0xff) - 127;
+ if (exp == 128 && (x & 0x7fffff) != 0)
+ return x | 0x400000; /* NaN -> make it a QNaN */
+ if (exp >= 23)
+ return x; /* it's an integer already (or Inf) */
+ if ((x & 0x7fffffff) == 0)
+ return x; /* +/-0 -> +/-0 */
+ if (exp < 0)
+ /* 0 < |x| < 1.0 rounds to +/- 1.0 */
+ return (x & 0x80000000) | 0x3f800000;
+ mask = 0x7fffff >> exp;
+ /* mantissa overflows into exponent - that's OK,
+ it can't overflow into the sign bit */
+ return (x + mask) & ~mask;
+}
+
+/* Round to floating integer, to nearest */
+static unsigned int rfin(unsigned int x)
+{
+ int exp, half;
+
+ exp = ((x >> 23) & 0xff) - 127;
+ if (exp == 128 && (x & 0x7fffff) != 0)
+ return x | 0x400000; /* NaN -> make it a QNaN */
+ if (exp >= 23)
+ return x; /* it's an integer already (or Inf) */
+ if (exp < -1)
+ return x & 0x80000000; /* |x| < 0.5 -> +/-0 */
+ if (exp == -1)
+ /* 0.5 <= |x| < 1.0 rounds to +/- 1.0 */
+ return (x & 0x80000000) | 0x3f800000;
+ half = 0x400000 >> exp;
+ /* add 0.5 to the magnitude and chop off the fraction bits */
+ return (x + half) & ~(0x7fffff >> exp);
+}
+
+int
+emulate_altivec(struct pt_regs *regs)
+{
+ unsigned int instr, i;
+ unsigned int va, vb, vc, vd;
+ vector128 *vrs;
+
+ if (get_user(instr, (unsigned int *) regs->nip))
+ return -EFAULT;
+ if ((instr >> 26) != 4)
+ return -EINVAL; /* not an altivec instruction */
+ vd = (instr >> 21) & 0x1f;
+ va = (instr >> 16) & 0x1f;
+ vb = (instr >> 11) & 0x1f;
+ vc = (instr >> 6) & 0x1f;
+
+ vrs = current->thread.vr;
+ switch (instr & 0x3f) {
+ case 10:
+ switch (vc) {
+ case 0: /* vaddfp */
+ vaddfp(&vrs[vd], &vrs[va], &vrs[vb]);
+ break;
+ case 1: /* vsubfp */
+ vsubfp(&vrs[vd], &vrs[va], &vrs[vb]);
+ break;
+ case 4: /* vrefp */
+ vrefp(&vrs[vd], &vrs[vb]);
+ break;
+ case 5: /* vrsqrtefp */
+ vrsqrtefp(&vrs[vd], &vrs[vb]);
+ break;
+ case 6: /* vexptefp */
+ for (i = 0; i < 4; ++i)
+ vrs[vd].u[i] = eexp2(vrs[vb].u[i]);
+ break;
+ case 7: /* vlogefp */
+ for (i = 0; i < 4; ++i)
+ vrs[vd].u[i] = elog2(vrs[vb].u[i]);
+ break;
+ case 8: /* vrfin */
+ for (i = 0; i < 4; ++i)
+ vrs[vd].u[i] = rfin(vrs[vb].u[i]);
+ break;
+ case 9: /* vrfiz */
+ for (i = 0; i < 4; ++i)
+ vrs[vd].u[i] = rfiz(vrs[vb].u[i]);
+ break;
+ case 10: /* vrfip */
+ for (i = 0; i < 4; ++i) {
+ u32 x = vrs[vb].u[i];
+ x = (x & 0x80000000)? rfiz(x): rfii(x);
+ vrs[vd].u[i] = x;
+ }
+ break;
+ case 11: /* vrfim */
+ for (i = 0; i < 4; ++i) {
+ u32 x = vrs[vb].u[i];
+ x = (x & 0x80000000)? rfii(x): rfiz(x);
+ vrs[vd].u[i] = x;
+ }
+ break;
+ case 14: /* vctuxs */
+ for (i = 0; i < 4; ++i)
+ vrs[vd].u[i] = ctuxs(vrs[vb].u[i], va,
+ ¤t->thread.vscr.u[3]);
+ break;
+ case 15: /* vctsxs */
+ for (i = 0; i < 4; ++i)
+ vrs[vd].u[i] = ctsxs(vrs[vb].u[i], va,
+ ¤t->thread.vscr.u[3]);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case 46: /* vmaddfp */
+ vmaddfp(&vrs[vd], &vrs[va], &vrs[vb], &vrs[vc]);
+ break;
+ case 47: /* vnmsubfp */
+ vnmsubfp(&vrs[vd], &vrs[va], &vrs[vb], &vrs[vc]);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
--- /dev/null
+#include <asm/ppc_asm.h>
+#include <asm/processor.h>
+
+/*
+ * The routines below are in assembler so we can closely control the
+ * usage of floating-point registers. These routines must be called
+ * with preempt disabled.
+ */
+ .section ".toc","aw"
+fpzero:
+ .tc FD_0_0[TC],0
+fpone:
+ .tc FD_3ff00000_0[TC],0x3ff0000000000000 /* 1.0 */
+fphalf:
+ .tc FD_3fe00000_0[TC],0x3fe0000000000000 /* 0.5 */
+
+ .text
+/*
+ * Internal routine to enable floating point and set FPSCR to 0.
+ * Don't call it from C; it doesn't use the normal calling convention.
+ */
+fpenable:
+ mfmsr r10
+ ori r11,r10,MSR_FP
+ mtmsr r11
+ isync
+ stfd fr31,-8(r1)
+ stfd fr0,-16(r1)
+ stfd fr1,-24(r1)
+ mffs fr31
+ lfd fr1,fpzero@toc(r2)
+ mtfsf 0xff,fr1
+ blr
+
+fpdisable:
+ mtlr r12
+ mtfsf 0xff,fr31
+ lfd fr1,-24(r1)
+ lfd fr0,-16(r1)
+ lfd fr31,-8(r1)
+ mtmsr r10
+ isync
+ blr
+
+/*
+ * Vector add, floating point.
+ */
+_GLOBAL(vaddfp)
+ mflr r12
+ bl fpenable
+ li r0,4
+ mtctr r0
+ li r6,0
+1: lfsx fr0,r4,r6
+ lfsx fr1,r5,r6
+ fadds fr0,fr0,fr1
+ stfsx fr0,r3,r6
+ addi r6,r6,4
+ bdnz 1b
+ b fpdisable
+
+/*
+ * Vector subtract, floating point.
+ */
+_GLOBAL(vsubfp)
+ mflr r12
+ bl fpenable
+ li r0,4
+ mtctr r0
+ li r6,0
+1: lfsx fr0,r4,r6
+ lfsx fr1,r5,r6
+ fsubs fr0,fr0,fr1
+ stfsx fr0,r3,r6
+ addi r6,r6,4
+ bdnz 1b
+ b fpdisable
+
+/*
+ * Vector multiply and add, floating point.
+ */
+_GLOBAL(vmaddfp)
+ mflr r12
+ bl fpenable
+ stfd fr2,-32(r1)
+ li r0,4
+ mtctr r0
+ li r7,0
+1: lfsx fr0,r4,r7
+ lfsx fr1,r5,r7
+ lfsx fr2,r6,r7
+ fmadds fr0,fr0,fr1,fr2
+ stfsx fr0,r3,r7
+ addi r7,r7,4
+ bdnz 1b
+ lfd fr2,-32(r1)
+ b fpdisable
+
+/*
+ * Vector negative multiply and subtract, floating point.
+ */
+_GLOBAL(vnmsubfp)
+ mflr r12
+ bl fpenable
+ stfd fr2,-32(r1)
+ li r0,4
+ mtctr r0
+ li r7,0
+1: lfsx fr0,r4,r7
+ lfsx fr1,r5,r7
+ lfsx fr2,r6,r7
+ fnmsubs fr0,fr0,fr1,fr2
+ stfsx fr0,r3,r7
+ addi r7,r7,4
+ bdnz 1b
+ lfd fr2,-32(r1)
+ b fpdisable
+
+/*
+ * Vector reciprocal estimate. We just compute 1.0/x.
+ * r3 -> destination, r4 -> source.
+ */
+_GLOBAL(vrefp)
+ mflr r12
+ bl fpenable
+ li r0,4
+ lfd fr1,fpone@toc(r2)
+ mtctr r0
+ li r6,0
+1: lfsx fr0,r4,r6
+ fdivs fr0,fr1,fr0
+ stfsx fr0,r3,r6
+ addi r6,r6,4
+ bdnz 1b
+ b fpdisable
+
+/*
+ * Vector reciprocal square-root estimate, floating point.
+ * We use the frsqrte instruction for the initial estimate followed
+ * by 2 iterations of Newton-Raphson to get sufficient accuracy.
+ * r3 -> destination, r4 -> source.
+ */
+_GLOBAL(vrsqrtefp)
+ mflr r12
+ bl fpenable
+ stfd fr2,-32(r1)
+ stfd fr3,-40(r1)
+ stfd fr4,-48(r1)
+ stfd fr5,-56(r1)
+ li r0,4
+ lfd fr4,fpone@toc(r2)
+ lfd fr5,fphalf@toc(r2)
+ mtctr r0
+ li r6,0
+1: lfsx fr0,r4,r6
+ frsqrte fr1,fr0 /* r = frsqrte(s) */
+ fmuls fr3,fr1,fr0 /* r * s */
+ fmuls fr2,fr1,fr5 /* r * 0.5 */
+ fnmsubs fr3,fr1,fr3,fr4 /* 1 - s * r * r */
+ fmadds fr1,fr2,fr3,fr1 /* r = r + 0.5 * r * (1 - s * r * r) */
+ fmuls fr3,fr1,fr0 /* r * s */
+ fmuls fr2,fr1,fr5 /* r * 0.5 */
+ fnmsubs fr3,fr1,fr3,fr4 /* 1 - s * r * r */
+ fmadds fr1,fr2,fr3,fr1 /* r = r + 0.5 * r * (1 - s * r * r) */
+ stfsx fr1,r3,r6
+ addi r6,r6,4
+ bdnz 1b
+ lfd fr5,-56(r1)
+ lfd fr4,-48(r1)
+ lfd fr3,-40(r1)
+ lfd fr2,-32(r1)
+ b fpdisable
--- /dev/null
+/*
+ * arch/ppc64/lib/e2a.c
+ *
+ * EBCDIC to ASCII conversion
+ *
+ * This function moved here from arch/ppc64/kernel/viopath.c
+ *
+ * (C) Copyright 2000-2004 IBM 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; either version 2 of the
+ * License, or (at your option) anyu later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+
+unsigned char e2a(unsigned char x)
+{
+ switch (x) {
+ case 0xF0:
+ return '0';
+ case 0xF1:
+ return '1';
+ case 0xF2:
+ return '2';
+ case 0xF3:
+ return '3';
+ case 0xF4:
+ return '4';
+ case 0xF5:
+ return '5';
+ case 0xF6:
+ return '6';
+ case 0xF7:
+ return '7';
+ case 0xF8:
+ return '8';
+ case 0xF9:
+ return '9';
+ case 0xC1:
+ return 'A';
+ case 0xC2:
+ return 'B';
+ case 0xC3:
+ return 'C';
+ case 0xC4:
+ return 'D';
+ case 0xC5:
+ return 'E';
+ case 0xC6:
+ return 'F';
+ case 0xC7:
+ return 'G';
+ case 0xC8:
+ return 'H';
+ case 0xC9:
+ return 'I';
+ case 0xD1:
+ return 'J';
+ case 0xD2:
+ return 'K';
+ case 0xD3:
+ return 'L';
+ case 0xD4:
+ return 'M';
+ case 0xD5:
+ return 'N';
+ case 0xD6:
+ return 'O';
+ case 0xD7:
+ return 'P';
+ case 0xD8:
+ return 'Q';
+ case 0xD9:
+ return 'R';
+ case 0xE2:
+ return 'S';
+ case 0xE3:
+ return 'T';
+ case 0xE4:
+ return 'U';
+ case 0xE5:
+ return 'V';
+ case 0xE6:
+ return 'W';
+ case 0xE7:
+ return 'X';
+ case 0xE8:
+ return 'Y';
+ case 0xE9:
+ return 'Z';
+ }
+ return ' ';
+}
+EXPORT_SYMBOL(e2a);
+
+
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/module.h>
+#include <linux/stringify.h>
#include <asm/hvcall.h>
#include <asm/iSeries/HvCall.h>
/* waiting for a spinlock... */
#if defined(CONFIG_PPC_SPLPAR) || defined(CONFIG_PPC_ISERIES)
+
+/* We only yield to the hypervisor if we are in shared processor mode */
+#define SHARED_PROCESSOR (get_paca()->lppaca.xSharedProc)
+
void __spin_yield(spinlock_t *lock)
{
unsigned int lock_value, holder_cpu, yield_count;
holder_cpu = lock_value & 0xffff;
BUG_ON(holder_cpu >= NR_CPUS);
holder_paca = &paca[holder_cpu];
- yield_count = holder_paca->xLpPaca.xYieldCount;
+ yield_count = holder_paca->lppaca.xYieldCount;
if ((yield_count & 1) == 0)
return; /* virtual cpu is currently running */
rmb();
#else /* SPLPAR || ISERIES */
#define __spin_yield(x) barrier()
+#define SHARED_PROCESSOR 0
#endif
/*
unsigned long tmp, tmp2;
__asm__ __volatile__(
-" lwz %1,24(13) # __spin_trylock\n\
+" lwz %1,%3(13) # __spin_trylock\n\
1: lwarx %0,0,%2\n\
cmpwi 0,%0,0\n\
bne- 2f\n\
bne- 1b\n\
isync\n\
2:" : "=&r" (tmp), "=&r" (tmp2)
- : "r" (&lock->lock)
+ : "r" (&lock->lock), "i" (offsetof(struct paca_struct, lock_token))
: "cr0", "memory");
return tmp;
break;
do {
HMT_low();
- __spin_yield(lock);
+ if (SHARED_PROCESSOR)
+ __spin_yield(lock);
} while (likely(lock->lock != 0));
HMT_medium();
}
local_irq_restore(flags);
do {
HMT_low();
- __spin_yield(lock);
+ if (SHARED_PROCESSOR)
+ __spin_yield(lock);
} while (likely(lock->lock != 0));
HMT_medium();
local_irq_restore(flags_dis);
void spin_unlock_wait(spinlock_t *lock)
{
- while (lock->lock)
- __spin_yield(lock);
+ while (lock->lock) {
+ HMT_low();
+ if (SHARED_PROCESSOR)
+ __spin_yield(lock);
+ }
+ HMT_medium();
}
EXPORT_SYMBOL(spin_unlock_wait);
holder_cpu = lock_value & 0xffff;
BUG_ON(holder_cpu >= NR_CPUS);
holder_paca = &paca[holder_cpu];
- yield_count = holder_paca->xLpPaca.xYieldCount;
+ yield_count = holder_paca->lppaca.xYieldCount;
if ((yield_count & 1) == 0)
return; /* virtual cpu is currently running */
rmb();
break;
do {
HMT_low();
- __rw_yield(rw);
+ if (SHARED_PROCESSOR)
+ __rw_yield(rw);
} while (likely(rw->lock < 0));
HMT_medium();
}
long tmp, tmp2;
__asm__ __volatile__(
-" lwz %1,24(13) # write_trylock\n\
+" lwz %1,%3(13) # write_trylock\n\
1: lwarx %0,0,%2\n\
cmpwi 0,%0,0\n\
bne- 2f\n\
bne- 1b\n\
isync\n\
2:" : "=&r" (tmp), "=&r" (tmp2)
- : "r" (&rw->lock)
+ : "r" (&rw->lock), "i" (offsetof(struct paca_struct, lock_token))
: "cr0", "memory");
return tmp;
break;
do {
HMT_low();
- __rw_yield(rw);
+ if (SHARED_PROCESSOR)
+ __rw_yield(rw);
} while (likely(rw->lock != 0));
HMT_medium();
}
--- /dev/null
+/*
+ * Functions which are too large to be inlined.
+ *
+ * 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 <asm/uaccess.h>
+
+unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
+{
+ if (likely(access_ok(VERIFY_READ, from, n)))
+ n = __copy_from_user(to, from, n);
+ else
+ memset(to, 0, n);
+ return n;
+}
+
+unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)
+{
+ if (likely(access_ok(VERIFY_WRITE, to, n)))
+ n = __copy_to_user(to, from, n);
+ return n;
+}
+
+unsigned long copy_in_user(void __user *to, const void __user *from,
+ unsigned long n)
+{
+ might_sleep();
+ if (likely(access_ok(VERIFY_READ, from, n) &&
+ access_ok(VERIFY_WRITE, to, n)))
+ n =__copy_tofrom_user(to, from, n);
+ return n;
+}
+
+EXPORT_SYMBOL(copy_from_user);
+EXPORT_SYMBOL(copy_to_user);
+EXPORT_SYMBOL(copy_in_user);
+
EXTRA_CFLAGS += -mno-minimal-toc
-obj-y := fault.o init.o imalloc.o hash_utils.o hash_low.o tlb.o
+obj-y := fault.o init.o imalloc.o hash_utils.o hash_low.o tlb.o slb_low.o slb.o
obj-$(CONFIG_DISCONTIGMEM) += numa.o
obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
--- /dev/null
+/*
+ * PowerPC64 SLB support.
+ *
+ * Copyright (C) 2004 David Gibson <dwg@au.ibm.com>, IBM
+ * Based on earlier code writteh by:
+ * Dave Engebretsen and Mike Corrigan {engebret|mikejc}@us.ibm.com
+ * Copyright (c) 2001 Dave Engebretsen
+ * Copyright (C) 2002 Anton Blanchard <anton@au.ibm.com>, IBM
+ *
+ *
+ * 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/config.h>
+#include <asm/pgtable.h>
+#include <asm/mmu.h>
+#include <asm/mmu_context.h>
+#include <asm/paca.h>
+#include <asm/naca.h>
+#include <asm/cputable.h>
+
+extern void slb_allocate(unsigned long ea);
+
+static inline void create_slbe(unsigned long ea, unsigned long vsid,
+ unsigned long flags, unsigned long entry)
+{
+ ea = (ea & ESID_MASK) | SLB_ESID_V | entry;
+ vsid = (vsid << SLB_VSID_SHIFT) | flags;
+ asm volatile("slbmte %0,%1" :
+ : "r" (vsid), "r" (ea)
+ : "memory" );
+}
+
+static void slb_add_bolted(void)
+{
+#ifndef CONFIG_PPC_ISERIES
+ WARN_ON(!irqs_disabled());
+
+ /* If you change this make sure you change SLB_NUM_BOLTED
+ * appropriately too */
+
+ /* Slot 1 - first VMALLOC segment
+ * Since modules end up there it gets hit very heavily.
+ */
+ create_slbe(VMALLOCBASE, get_kernel_vsid(VMALLOCBASE),
+ SLB_VSID_KERNEL, 1);
+
+ asm volatile("isync":::"memory");
+#endif
+}
+
+/* Flush all user entries from the segment table of the current processor. */
+void switch_slb(struct task_struct *tsk, struct mm_struct *mm)
+{
+ unsigned long offset = get_paca()->slb_cache_ptr;
+ unsigned long esid_data;
+ unsigned long pc = KSTK_EIP(tsk);
+ unsigned long stack = KSTK_ESP(tsk);
+ unsigned long unmapped_base;
+
+ if (offset <= SLB_CACHE_ENTRIES) {
+ int i;
+ asm volatile("isync" : : : "memory");
+ for (i = 0; i < offset; i++) {
+ esid_data = (unsigned long)get_paca()->slb_cache[i]
+ << SID_SHIFT;
+ asm volatile("slbie %0" : : "r" (esid_data));
+ }
+ asm volatile("isync" : : : "memory");
+ } else {
+ asm volatile("isync; slbia; isync" : : : "memory");
+ slb_add_bolted();
+ }
+
+ /* Workaround POWER5 < DD2.1 issue */
+ if (offset == 1 || offset > SLB_CACHE_ENTRIES) {
+ /* flush segment in EEH region, we shouldn't ever
+ * access addresses in this region. */
+ asm volatile("slbie %0" : : "r"(EEHREGIONBASE));
+ }
+
+ get_paca()->slb_cache_ptr = 0;
+ get_paca()->context = mm->context;
+
+ /*
+ * preload some userspace segments into the SLB.
+ */
+ if (test_tsk_thread_flag(tsk, TIF_32BIT))
+ unmapped_base = TASK_UNMAPPED_BASE_USER32;
+ else
+ unmapped_base = TASK_UNMAPPED_BASE_USER64;
+
+ if (pc >= KERNELBASE)
+ return;
+ slb_allocate(pc);
+
+ if (GET_ESID(pc) == GET_ESID(stack))
+ return;
+
+ if (stack >= KERNELBASE)
+ return;
+ slb_allocate(stack);
+
+ if ((GET_ESID(pc) == GET_ESID(unmapped_base))
+ || (GET_ESID(stack) == GET_ESID(unmapped_base)))
+ return;
+
+ if (unmapped_base >= KERNELBASE)
+ return;
+ slb_allocate(unmapped_base);
+}
+
+void slb_initialize(void)
+{
+#ifdef CONFIG_PPC_ISERIES
+ asm volatile("isync; slbia; isync":::"memory");
+#else
+ unsigned long flags = SLB_VSID_KERNEL;
+
+ /* Invalidate the entire SLB (even slot 0) & all the ERATS */
+ if (cur_cpu_spec->cpu_features & CPU_FTR_16M_PAGE)
+ flags |= SLB_VSID_L;
+
+ asm volatile("isync":::"memory");
+ asm volatile("slbmte %0,%0"::"r" (0) : "memory");
+ asm volatile("isync; slbia; isync":::"memory");
+ create_slbe(KERNELBASE, get_kernel_vsid(KERNELBASE),
+ flags, 0);
+
+#endif
+ slb_add_bolted();
+ get_paca()->stab_rr = SLB_NUM_BOLTED;
+}
--- /dev/null
+/*
+ * arch/ppc64/mm/slb_low.S
+ *
+ * Low-level SLB routines
+ *
+ * Copyright (C) 2004 David Gibson <dwg@au.ibm.com>, IBM
+ *
+ * Based on earlier C version:
+ * Dave Engebretsen and Mike Corrigan {engebret|mikejc}@us.ibm.com
+ * Copyright (c) 2001 Dave Engebretsen
+ * Copyright (C) 2002 Anton Blanchard <anton@au.ibm.com>, IBM
+ *
+ * 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/config.h>
+#include <asm/processor.h>
+#include <asm/page.h>
+#include <asm/mmu.h>
+#include <asm/ppc_asm.h>
+#include <asm/offsets.h>
+#include <asm/cputable.h>
+
+/* void slb_allocate(unsigned long ea);
+ *
+ * Create an SLB entry for the given EA (user or kernel).
+ * r3 = faulting address, r13 = PACA
+ * r9, r10, r11 are clobbered by this function
+ * No other registers are examined or changed.
+ */
+_GLOBAL(slb_allocate)
+ /*
+ * First find a slot, round robin. Previously we tried to find
+ * a free slot first but that took too long. Unfortunately we
+ * dont have any LRU information to help us choose a slot.
+ */
+ ld r10,PACASTABRR(r13)
+3:
+ addi r10,r10,1
+ /* use a cpu feature mask if we ever change our slb size */
+ cmpldi r10,SLB_NUM_ENTRIES
+
+ blt+ 4f
+ li r10,SLB_NUM_BOLTED
+
+ /*
+ * Never cast out the segment for our kernel stack. Since we
+ * dont invalidate the ERAT we could have a valid translation
+ * for the kernel stack during the first part of exception exit
+ * which gets invalidated due to a tlbie from another cpu at a
+ * non recoverable point (after setting srr0/1) - Anton
+ */
+4: slbmfee r11,r10
+ srdi r11,r11,27
+ /*
+ * Use paca->ksave as the value of the kernel stack pointer,
+ * because this is valid at all times.
+ * The >> 27 (rather than >> 28) is so that the LSB is the
+ * valid bit - this way we check valid and ESID in one compare.
+ * In order to completely close the tiny race in the context
+ * switch (between updating r1 and updating paca->ksave),
+ * we check against both r1 and paca->ksave.
+ */
+ srdi r9,r1,27
+ ori r9,r9,1 /* mangle SP for later compare */
+ cmpd r11,r9
+ beq- 3b
+ ld r9,PACAKSAVE(r13)
+ srdi r9,r9,27
+ ori r9,r9,1
+ cmpd r11,r9
+ beq- 3b
+
+ std r10,PACASTABRR(r13)
+
+ /* r3 = faulting address, r10 = entry */
+
+ srdi r9,r3,60 /* get region */
+ srdi r3,r3,28 /* get esid */
+ cmpldi cr7,r9,0xc /* cmp KERNELBASE for later use */
+
+ /* r9 = region, r3 = esid, cr7 = <>KERNELBASE */
+
+ rldicr. r11,r3,32,16
+ bne- 8f /* invalid ea bits set */
+ addi r11,r9,-1
+ cmpldi r11,0xb
+ blt- 8f /* invalid region */
+
+ /* r9 = region, r3 = esid, r10 = entry, cr7 = <>KERNELBASE */
+
+ blt cr7,0f /* user or kernel? */
+
+ /* kernel address */
+ li r11,SLB_VSID_KERNEL
+BEGIN_FTR_SECTION
+ bne cr7,9f
+ li r11,(SLB_VSID_KERNEL|SLB_VSID_L)
+END_FTR_SECTION_IFSET(CPU_FTR_16M_PAGE)
+ b 9f
+
+0: /* user address */
+ li r11,SLB_VSID_USER
+#ifdef CONFIG_HUGETLB_PAGE
+BEGIN_FTR_SECTION
+ /* check against the hugepage ranges */
+ cmpldi r3,(TASK_HPAGE_END>>SID_SHIFT)
+ bge 6f /* >= TASK_HPAGE_END */
+ cmpldi r3,(TASK_HPAGE_BASE>>SID_SHIFT)
+ bge 5f /* TASK_HPAGE_BASE..TASK_HPAGE_END */
+ cmpldi r3,16
+ bge 6f /* 4GB..TASK_HPAGE_BASE */
+
+ lhz r9,PACAHTLBSEGS(r13)
+ srd r9,r9,r3
+ andi. r9,r9,1
+ beq 6f
+
+5: /* this is a hugepage user address */
+ li r11,(SLB_VSID_USER|SLB_VSID_L)
+END_FTR_SECTION_IFSET(CPU_FTR_16M_PAGE)
+#endif /* CONFIG_HUGETLB_PAGE */
+
+6: ld r9,PACACONTEXTID(r13)
+
+9: /* r9 = "context", r3 = esid, r11 = flags, r10 = entry */
+
+ rldimi r9,r3,15,0 /* r9= VSID ordinal */
+
+7: rldimi r10,r3,28,0 /* r10= ESID<<28 | entry */
+ oris r10,r10,SLB_ESID_V@h /* r10 |= SLB_ESID_V */
+
+ /* r9 = ordinal, r3 = esid, r11 = flags, r10 = esid_data */
+
+ li r3,VSID_RANDOMIZER@higher
+ sldi r3,r3,32
+ oris r3,r3,VSID_RANDOMIZER@h
+ ori r3,r3,VSID_RANDOMIZER@l
+
+ mulld r9,r3,r9 /* r9 = ordinal * VSID_RANDOMIZER */
+ clrldi r9,r9,28 /* r9 &= VSID_MASK */
+ sldi r9,r9,SLB_VSID_SHIFT /* r9 <<= SLB_VSID_SHIFT */
+ or r9,r9,r11 /* r9 |= flags */
+
+ /* r9 = vsid_data, r10 = esid_data, cr7 = <>KERNELBASE */
+
+ /*
+ * No need for an isync before or after this slbmte. The exception
+ * we enter with and the rfid we exit with are context synchronizing.
+ */
+ slbmte r9,r10
+
+ bgelr cr7 /* we're done for kernel addresses */
+
+ /* Update the slb cache */
+ lhz r3,PACASLBCACHEPTR(r13) /* offset = paca->slb_cache_ptr */
+ cmpldi r3,SLB_CACHE_ENTRIES
+ bge 1f
+
+ /* still room in the slb cache */
+ sldi r11,r3,1 /* r11 = offset * sizeof(u16) */
+ rldicl r10,r10,36,28 /* get low 16 bits of the ESID */
+ add r11,r11,r13 /* r11 = (u16 *)paca + offset */
+ sth r10,PACASLBCACHE(r11) /* paca->slb_cache[offset] = esid */
+ addi r3,r3,1 /* offset++ */
+ b 2f
+1: /* offset >= SLB_CACHE_ENTRIES */
+ li r3,SLB_CACHE_ENTRIES+1
+2:
+ sth r3,PACASLBCACHEPTR(r13) /* paca->slb_cache_ptr = offset */
+ blr
+
+8: /* invalid EA */
+ li r9,0 /* 0 VSID ordinal -> BAD_VSID */
+ li r11,SLB_VSID_USER /* flags don't much matter */
+ b 7b
} _sigev_un;
};
+extern int copy_siginfo_to_user32(siginfo_t32 __user *to, siginfo_t *from);
+extern int copy_siginfo_from_user32(siginfo_t *to, siginfo_t32 __user *from);
+
#endif /* _ASM_S390X_S390_H */
--- /dev/null
+/*
+ * arch/s390/kernel/vtime.c
+ * Virtual cpu timer based timer functions.
+ *
+ * S390 version
+ * Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s): Jan Glauber <jan.glauber@de.ibm.com>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/types.h>
+#include <linux/timex.h>
+#include <linux/notifier.h>
+
+#include <asm/s390_ext.h>
+#include <asm/timer.h>
+
+#define VTIMER_MAGIC (0x4b87ad6e + 1)
+static ext_int_info_t ext_int_info_timer;
+DEFINE_PER_CPU(struct vtimer_queue, virt_cpu_timer);
+
+void start_cpu_timer(void)
+{
+ struct vtimer_queue *vt_list;
+
+ vt_list = &per_cpu(virt_cpu_timer, smp_processor_id());
+ set_vtimer(vt_list->idle);
+}
+
+void stop_cpu_timer(void)
+{
+ __u64 done;
+ struct vtimer_queue *vt_list;
+
+ vt_list = &per_cpu(virt_cpu_timer, smp_processor_id());
+
+ /* nothing to do */
+ if (list_empty(&vt_list->list)) {
+ vt_list->idle = VTIMER_MAX_SLICE;
+ goto fire;
+ }
+
+ /* store progress */
+ asm volatile ("STPT %0" : "=m" (done));
+
+ /*
+ * If done is negative we do not stop the CPU timer
+ * because we will get instantly an interrupt that
+ * will start the CPU timer again.
+ */
+ if (done & 1LL<<63)
+ return;
+ else
+ vt_list->offset += vt_list->to_expire - done;
+
+ /* save the actual expire value */
+ vt_list->idle = done;
+
+ /*
+ * We cannot halt the CPU timer, we just write a value that
+ * nearly never expires (only after 71 years) and re-write
+ * the stored expire value if we continue the timer
+ */
+ fire:
+ set_vtimer(VTIMER_MAX_SLICE);
+}
+
+void set_vtimer(__u64 expires)
+{
+ asm volatile ("SPT %0" : : "m" (expires));
+
+ /* store expire time for this CPU timer */
+ per_cpu(virt_cpu_timer, smp_processor_id()).to_expire = expires;
+}
+
+/*
+ * Sorted add to a list. List is linear searched until first bigger
+ * element is found.
+ */
+void list_add_sorted(struct vtimer_list *timer, struct list_head *head)
+{
+ struct vtimer_list *event;
+
+ list_for_each_entry(event, head, entry) {
+ if (event->expires > timer->expires) {
+ list_add_tail(&timer->entry, &event->entry);
+ return;
+ }
+ }
+ list_add_tail(&timer->entry, head);
+}
+
+/*
+ * Do the callback functions of expired vtimer events.
+ * Called from within the interrupt handler.
+ */
+static void do_callbacks(struct list_head *cb_list, struct pt_regs *regs)
+{
+ struct vtimer_queue *vt_list;
+ struct vtimer_list *event, *tmp;
+ void (*fn)(unsigned long, struct pt_regs*);
+ unsigned long data;
+
+ if (list_empty(cb_list))
+ return;
+
+ vt_list = &per_cpu(virt_cpu_timer, smp_processor_id());
+
+ list_for_each_entry_safe(event, tmp, cb_list, entry) {
+ fn = event->function;
+ data = event->data;
+ fn(data, regs);
+
+ if (!event->interval)
+ /* delete one shot timer */
+ list_del_init(&event->entry);
+ else {
+ /* move interval timer back to list */
+ spin_lock(&vt_list->lock);
+ list_del_init(&event->entry);
+ list_add_sorted(event, &vt_list->list);
+ spin_unlock(&vt_list->lock);
+ }
+ }
+}
+
+/*
+ * Handler for the virtual CPU timer.
+ */
+static void do_cpu_timer_interrupt(struct pt_regs *regs, __u16 error_code)
+{
+ int cpu;
+ __u64 next, delta;
+ struct vtimer_queue *vt_list;
+ struct vtimer_list *event, *tmp;
+ struct list_head *ptr;
+ /* the callback queue */
+ struct list_head cb_list;
+
+ INIT_LIST_HEAD(&cb_list);
+ cpu = smp_processor_id();
+ vt_list = &per_cpu(virt_cpu_timer, cpu);
+
+ /* walk timer list, fire all expired events */
+ spin_lock(&vt_list->lock);
+
+ if (vt_list->to_expire < VTIMER_MAX_SLICE)
+ vt_list->offset += vt_list->to_expire;
+
+ list_for_each_entry_safe(event, tmp, &vt_list->list, entry) {
+ if (event->expires > vt_list->offset)
+ /* found first unexpired event, leave */
+ break;
+
+ /* re-charge interval timer, we have to add the offset */
+ if (event->interval)
+ event->expires = event->interval + vt_list->offset;
+
+ /* move expired timer to the callback queue */
+ list_move_tail(&event->entry, &cb_list);
+ }
+ spin_unlock(&vt_list->lock);
+ do_callbacks(&cb_list, regs);
+
+ /* next event is first in list */
+ spin_lock(&vt_list->lock);
+ if (!list_empty(&vt_list->list)) {
+ ptr = vt_list->list.next;
+ event = list_entry(ptr, struct vtimer_list, entry);
+ next = event->expires - vt_list->offset;
+
+ /* add the expired time from this interrupt handler
+ * and the callback functions
+ */
+ asm volatile ("STPT %0" : "=m" (delta));
+ delta = 0xffffffffffffffffLL - delta + 1;
+ vt_list->offset += delta;
+ next -= delta;
+ } else {
+ vt_list->offset = 0;
+ next = VTIMER_MAX_SLICE;
+ }
+ spin_unlock(&vt_list->lock);
+ set_vtimer(next);
+}
+
+void init_virt_timer(struct vtimer_list *timer)
+{
+ timer->magic = VTIMER_MAGIC;
+ timer->function = NULL;
+ INIT_LIST_HEAD(&timer->entry);
+ spin_lock_init(&timer->lock);
+}
+EXPORT_SYMBOL(init_virt_timer);
+
+static inline int check_vtimer(struct vtimer_list *timer)
+{
+ if (timer->magic != VTIMER_MAGIC)
+ return -EINVAL;
+ return 0;
+}
+
+static inline int vtimer_pending(struct vtimer_list *timer)
+{
+ return (!list_empty(&timer->entry));
+}
+
+/*
+ * this function should only run on the specified CPU
+ */
+static void internal_add_vtimer(struct vtimer_list *timer)
+{
+ unsigned long flags;
+ __u64 done;
+ struct vtimer_list *event;
+ struct vtimer_queue *vt_list;
+
+ vt_list = &per_cpu(virt_cpu_timer, timer->cpu);
+ spin_lock_irqsave(&vt_list->lock, flags);
+
+ if (timer->cpu != smp_processor_id())
+ printk("internal_add_vtimer: BUG, running on wrong CPU");
+
+ /* if list is empty we only have to set the timer */
+ if (list_empty(&vt_list->list)) {
+ /* reset the offset, this may happen if the last timer was
+ * just deleted by mod_virt_timer and the interrupt
+ * didn't happen until here
+ */
+ vt_list->offset = 0;
+ goto fire;
+ }
+
+ /* save progress */
+ asm volatile ("STPT %0" : "=m" (done));
+
+ /* calculate completed work */
+ done = vt_list->to_expire - done + vt_list->offset;
+ vt_list->offset = 0;
+
+ list_for_each_entry(event, &vt_list->list, entry)
+ event->expires -= done;
+
+ fire:
+ list_add_sorted(timer, &vt_list->list);
+
+ /* get first element, which is the next vtimer slice */
+ event = list_entry(vt_list->list.next, struct vtimer_list, entry);
+
+ set_vtimer(event->expires);
+ spin_unlock_irqrestore(&vt_list->lock, flags);
+ /* release CPU aquired in prepare_vtimer or mod_virt_timer() */
+ put_cpu();
+}
+
+static inline int prepare_vtimer(struct vtimer_list *timer)
+{
+ if (check_vtimer(timer) || !timer->function) {
+ printk("add_virt_timer: uninitialized timer\n");
+ return -EINVAL;
+ }
+
+ if (!timer->expires || timer->expires > VTIMER_MAX_SLICE) {
+ printk("add_virt_timer: invalid timer expire value!\n");
+ return -EINVAL;
+ }
+
+ if (vtimer_pending(timer)) {
+ printk("add_virt_timer: timer pending\n");
+ return -EBUSY;
+ }
+
+ timer->cpu = get_cpu();
+ return 0;
+}
+
+/*
+ * add_virt_timer - add an oneshot virtual CPU timer
+ */
+void add_virt_timer(void *new)
+{
+ struct vtimer_list *timer;
+
+ timer = (struct vtimer_list *)new;
+
+ if (prepare_vtimer(timer) < 0)
+ return;
+
+ timer->interval = 0;
+ internal_add_vtimer(timer);
+}
+EXPORT_SYMBOL(add_virt_timer);
+
+/*
+ * add_virt_timer_int - add an interval virtual CPU timer
+ */
+void add_virt_timer_periodic(void *new)
+{
+ struct vtimer_list *timer;
+
+ timer = (struct vtimer_list *)new;
+
+ if (prepare_vtimer(timer) < 0)
+ return;
+
+ timer->interval = timer->expires;
+ internal_add_vtimer(timer);
+}
+EXPORT_SYMBOL(add_virt_timer_periodic);
+
+/*
+ * If we change a pending timer the function must be called on the CPU
+ * where the timer is running on, e.g. by smp_call_function_on()
+ *
+ * The original mod_timer adds the timer if it is not pending. For compatibility
+ * we do the same. The timer will be added on the current CPU as a oneshot timer.
+ *
+ * returns whether it has modified a pending timer (1) or not (0)
+ */
+int mod_virt_timer(struct vtimer_list *timer, __u64 expires)
+{
+ struct vtimer_queue *vt_list;
+ unsigned long flags;
+ int cpu;
+
+ if (check_vtimer(timer) || !timer->function) {
+ printk("mod_virt_timer: uninitialized timer\n");
+ return -EINVAL;
+ }
+
+ if (!expires || expires > VTIMER_MAX_SLICE) {
+ printk("mod_virt_timer: invalid expire range\n");
+ return -EINVAL;
+ }
+
+ /*
+ * This is a common optimization triggered by the
+ * networking code - if the timer is re-modified
+ * to be the same thing then just return:
+ */
+ if (timer->expires == expires && vtimer_pending(timer))
+ return 1;
+
+ cpu = get_cpu();
+ vt_list = &per_cpu(virt_cpu_timer, cpu);
+
+ /* disable interrupts before test if timer is pending */
+ spin_lock_irqsave(&vt_list->lock, flags);
+
+ /* if timer isn't pending add it on the current CPU */
+ if (!vtimer_pending(timer)) {
+ spin_unlock_irqrestore(&vt_list->lock, flags);
+ /* we do not activate an interval timer with mod_virt_timer */
+ timer->interval = 0;
+ timer->expires = expires;
+ timer->cpu = cpu;
+ internal_add_vtimer(timer);
+ return 0;
+ }
+
+ /* check if we run on the right CPU */
+ if (timer->cpu != cpu) {
+ printk("mod_virt_timer: running on wrong CPU, check your code\n");
+ spin_unlock_irqrestore(&vt_list->lock, flags);
+ put_cpu();
+ return -EINVAL;
+ }
+
+ list_del_init(&timer->entry);
+ timer->expires = expires;
+
+ /* also change the interval if we have an interval timer */
+ if (timer->interval)
+ timer->interval = expires;
+
+ /* the timer can't expire anymore so we can release the lock */
+ spin_unlock_irqrestore(&vt_list->lock, flags);
+ internal_add_vtimer(timer);
+ return 1;
+}
+EXPORT_SYMBOL(mod_virt_timer);
+
+/*
+ * delete a virtual timer
+ *
+ * returns whether the deleted timer was pending (1) or not (0)
+ */
+int del_virt_timer(struct vtimer_list *timer)
+{
+ unsigned long flags;
+ struct vtimer_queue *vt_list;
+
+ if (check_vtimer(timer)) {
+ printk("del_virt_timer: timer not initialized\n");
+ return -EINVAL;
+ }
+
+ /* check if timer is pending */
+ if (!vtimer_pending(timer))
+ return 0;
+
+ vt_list = &per_cpu(virt_cpu_timer, timer->cpu);
+ spin_lock_irqsave(&vt_list->lock, flags);
+
+ /* we don't interrupt a running timer, just let it expire! */
+ list_del_init(&timer->entry);
+
+ /* last timer removed */
+ if (list_empty(&vt_list->list)) {
+ vt_list->to_expire = 0;
+ vt_list->offset = 0;
+ }
+
+ spin_unlock_irqrestore(&vt_list->lock, flags);
+ return 1;
+}
+EXPORT_SYMBOL(del_virt_timer);
+
+/*
+ * Start the virtual CPU timer on the current CPU.
+ */
+void init_cpu_vtimer(void)
+{
+ struct vtimer_queue *vt_list;
+ unsigned long cr0;
+ __u64 timer;
+
+ /* kick the virtual timer */
+ timer = VTIMER_MAX_SLICE;
+ asm volatile ("SPT %0" : : "m" (timer));
+ __ctl_store(cr0, 0, 0);
+ cr0 |= 0x400;
+ __ctl_load(cr0, 0, 0);
+
+ vt_list = &per_cpu(virt_cpu_timer, smp_processor_id());
+ INIT_LIST_HEAD(&vt_list->list);
+ spin_lock_init(&vt_list->lock);
+ vt_list->to_expire = 0;
+ vt_list->offset = 0;
+ vt_list->idle = 0;
+
+}
+
+static int vtimer_idle_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ switch (action) {
+ case CPU_IDLE:
+ stop_cpu_timer();
+ break;
+ case CPU_NOT_IDLE:
+ start_cpu_timer();
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block vtimer_idle_nb = {
+ .notifier_call = vtimer_idle_notify,
+};
+
+void __init vtime_init(void)
+{
+ /* request the cpu timer external interrupt */
+ if (register_early_external_interrupt(0x1005, do_cpu_timer_interrupt,
+ &ext_int_info_timer) != 0)
+ panic("Couldn't request external interrupt 0x1005");
+
+ if (register_idle_notifier(&vtimer_idle_nb))
+ panic("Couldn't register idle notifier");
+
+ init_cpu_vtimer();
+}
+
return s;
}
EXPORT_SYMBOL_NOVERS(memset);
-
-/*
- * missing exports for string functions defined in lib/string.c
- */
-EXPORT_SYMBOL_NOVERS(memmove);
-EXPORT_SYMBOL_NOVERS(strchr);
-EXPORT_SYMBOL_NOVERS(strnchr);
-EXPORT_SYMBOL_NOVERS(strncmp);
-EXPORT_SYMBOL_NOVERS(strpbrk);
--- /dev/null
+#
+# Makefile for the HS7751RVoIP specific parts of the kernel
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+
+obj-y := mach.o setup.o io.o irq.o led.o
+
+obj-$(CONFIG_PCI) += pci.o
+
--- /dev/null
+/*
+ * linux/arch/sh/kernel/io_hs7751rvoip.c
+ *
+ * Copyright (C) 2001 Ian da Silva, Jeremy Siegel
+ * Based largely on io_se.c.
+ *
+ * I/O routine for Renesas Technology sales HS7751RVoIP
+ *
+ * Initial version only to support LAN access; some
+ * placeholder code from io_hs7751rvoip.c left in with the
+ * expectation of later SuperIO and PCMCIA access.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+#include <asm/hs7751rvoip/hs7751rvoip.h>
+#include <asm/addrspace.h>
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include "../../../drivers/pci/pci-sh7751.h"
+
+extern void *area5_io8_base; /* Area 5 8bit I/O Base address */
+extern void *area6_io8_base; /* Area 6 8bit I/O Base address */
+extern void *area5_io16_base; /* Area 5 16bit I/O Base address */
+extern void *area6_io16_base; /* Area 6 16bit I/O Base address */
+
+/*
+ * The 7751R HS7751RVoIP uses the built-in PCI controller (PCIC)
+ * of the 7751R processor, and has a SuperIO accessible via the PCI.
+ * The board also includes a PCMCIA controller on its memory bus,
+ * like the other Solution Engine boards.
+ */
+
+#define PCIIOBR (volatile long *)PCI_REG(SH7751_PCIIOBR)
+#define PCIMBR (volatile long *)PCI_REG(SH7751_PCIMBR)
+#define PCI_IO_AREA SH7751_PCI_IO_BASE
+#define PCI_MEM_AREA SH7751_PCI_CONFIG_BASE
+
+#define PCI_IOMAP(adr) (PCI_IO_AREA + (adr & ~SH7751_PCIIOBR_MASK))
+
+#if defined(CONFIG_HS7751RVOIP_CODEC)
+#define CODEC_IO_BASE 0x1000
+#endif
+
+#define maybebadio(name,port) \
+ printk("bad PC-like io %s for port 0x%lx at 0x%08x\n", \
+ #name, (port), (__u32) __builtin_return_address(0))
+
+static inline void delay(void)
+{
+ ctrl_inw(0xa0000000);
+}
+
+static inline unsigned long port2adr(unsigned int port)
+{
+ if ((0x1f0 <= port && port < 0x1f8) || port == 0x3f6)
+ if (port == 0x3f6)
+ return ((unsigned long)area5_io16_base + 0x0c);
+ else
+ return ((unsigned long)area5_io16_base + 0x800 + ((port-0x1f0) << 1));
+ else
+ maybebadio(port2adr, (unsigned long)port);
+ return port;
+}
+
+/* The 7751R HS7751RVoIP seems to have everything hooked */
+/* up pretty normally (nothing on high-bytes only...) so this */
+/* shouldn't be needed */
+static inline int shifted_port(unsigned long port)
+{
+ /* For IDE registers, value is not shifted */
+ if ((0x1f0 <= port && port < 0x1f8) || port == 0x3f6)
+ return 0;
+ else
+ return 1;
+}
+
+#if defined(CONFIG_HS7751RVOIP_CODEC)
+static inline int
+codec_port(unsigned long port)
+{
+ if (CODEC_IO_BASE <= port && port < (CODEC_IO_BASE+0x20))
+ return 1;
+ else
+ return 0;
+}
+#endif
+
+/* In case someone configures the kernel w/o PCI support: in that */
+/* scenario, don't ever bother to check for PCI-window addresses */
+
+/* NOTE: WINDOW CHECK MAY BE A BIT OFF, HIGH PCIBIOS_MIN_IO WRAPS? */
+#if defined(CONFIG_PCI)
+#define CHECK_SH7751_PCIIO(port) \
+ ((port >= PCIBIOS_MIN_IO) && (port < (PCIBIOS_MIN_IO + SH7751_PCI_IO_SIZE)))
+#else
+#define CHECK_SH7751_PCIIO(port) (0)
+#endif
+
+/*
+ * General outline: remap really low stuff [eventually] to SuperIO,
+ * stuff in PCI IO space (at or above window at pci.h:PCIBIOS_MIN_IO)
+ * is mapped through the PCI IO window. Stuff with high bits (PXSEG)
+ * should be way beyond the window, and is used w/o translation for
+ * compatibility.
+ */
+unsigned char hs7751rvoip_inb(unsigned long port)
+{
+ if (PXSEG(port))
+ return *(volatile unsigned char *)port;
+#if defined(CONFIG_HS7751RVOIP_CODEC)
+ else if (codec_port(port))
+ return *(volatile unsigned char *)((unsigned long)area6_io8_base+(port-CODEC_IO_BASE));
+#endif
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port))
+ return *(volatile unsigned char *)PCI_IOMAP(port);
+ else
+ return (*(volatile unsigned short *)port2adr(port) & 0xff);
+}
+
+unsigned char hs7751rvoip_inb_p(unsigned long port)
+{
+ unsigned char v;
+
+ if (PXSEG(port))
+ v = *(volatile unsigned char *)port;
+#if defined(CONFIG_HS7751RVOIP_CODEC)
+ else if (codec_port(port))
+ v = *(volatile unsigned char *)((unsigned long)area6_io8_base+(port-CODEC_IO_BASE));
+#endif
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port))
+ v = *(volatile unsigned char *)PCI_IOMAP(port);
+ else
+ v = (*(volatile unsigned short *)port2adr(port) & 0xff);
+ delay();
+ return v;
+}
+
+unsigned short hs7751rvoip_inw(unsigned long port)
+{
+ if (PXSEG(port))
+ return *(volatile unsigned short *)port;
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port))
+ return *(volatile unsigned short *)PCI_IOMAP(port);
+ else
+ maybebadio(inw, port);
+ return 0;
+}
+
+unsigned int hs7751rvoip_inl(unsigned long port)
+{
+ if (PXSEG(port))
+ return *(volatile unsigned long *)port;
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port))
+ return *(volatile unsigned long *)PCI_IOMAP(port);
+ else
+ maybebadio(inl, port);
+ return 0;
+}
+
+void hs7751rvoip_outb(unsigned char value, unsigned long port)
+{
+
+ if (PXSEG(port))
+ *(volatile unsigned char *)port = value;
+#if defined(CONFIG_HS7751RVOIP_CIDEC)
+ else if (codec_port(port))
+ *(volatile unsigned cjar *)((unsigned long)area6_io8_base+(port-CODEC_IO_BASE)) = value;
+#endif
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port))
+ *(unsigned char *)PCI_IOMAP(port) = value;
+ else
+ *(volatile unsigned short *)port2adr(port) = value;
+}
+
+void hs7751rvoip_outb_p(unsigned char value, unsigned long port)
+{
+ if (PXSEG(port))
+ *(volatile unsigned char *)port = value;
+#if defined(CONFIG_HS7751RVOIP_CIDEC)
+ else if (codec_port(port))
+ *(volatile unsigned cjar *)((unsigned long)area6_io8_base+(port-CODEC_IO_BASE)) = value;
+#endif
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port))
+ *(unsigned char *)PCI_IOMAP(port) = value;
+ else
+ *(volatile unsigned short *)port2adr(port) = value;
+ delay();
+}
+
+void hs7751rvoip_outw(unsigned short value, unsigned long port)
+{
+ if (PXSEG(port))
+ *(volatile unsigned short *)port = value;
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port))
+ *(unsigned short *)PCI_IOMAP(port) = value;
+ else
+ maybebadio(outw, port);
+}
+
+void hs7751rvoip_outl(unsigned int value, unsigned long port)
+{
+ if (PXSEG(port))
+ *(volatile unsigned long *)port = value;
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port))
+ *((unsigned long *)PCI_IOMAP(port)) = value;
+ else
+ maybebadio(outl, port);
+}
+
+void hs7751rvoip_insb(unsigned long port, void *addr, unsigned long count)
+{
+ if (PXSEG(port))
+ while (count--) *((unsigned char *) addr)++ = *(volatile unsigned char *)port;
+#if defined(CONFIG_HS7751RVOIP_CODEC)
+ else if (codec_port(port))
+ while (count--) *((unsigned char *) addr)++ = *(volatile unsigned char *)((unsigned long)area6_io8_base+(port-CODEC_IO_BASE));
+#endif
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port)) {
+ volatile __u8 *bp = (__u8 *)PCI_IOMAP(port);
+
+ while (count--) *((volatile unsigned char *) addr)++ = *bp;
+ } else {
+ volatile __u16 *p = (volatile unsigned short *)port2adr(port);
+
+ while (count--) *((unsigned char *) addr)++ = *p & 0xff;
+ }
+}
+
+void hs7751rvoip_insw(unsigned long port, void *addr, unsigned long count)
+{
+ volatile __u16 *p;
+
+ if (PXSEG(port))
+ p = (volatile unsigned short *)port;
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port))
+ p = (volatile unsigned short *)PCI_IOMAP(port);
+ else
+ p = (volatile unsigned short *)port2adr(port);
+ while (count--) *((__u16 *) addr)++ = *p;
+}
+
+void hs7751rvoip_insl(unsigned long port, void *addr, unsigned long count)
+{
+ if (CHECK_SH7751_PCIIO(port) || shifted_port(port)) {
+ volatile __u32 *p = (__u32 *)PCI_IOMAP(port);
+
+ while (count--) *((__u32 *) addr)++ = *p;
+ } else
+ maybebadio(insl, port);
+}
+
+void hs7751rvoip_outsb(unsigned long port, const void *addr, unsigned long count)
+{
+ if (PXSEG(port))
+ while (count--) *(volatile unsigned char *)port = *((unsigned char *) addr)++;
+#if defined(CONFIG_HS7751RVOIP_CODEC)
+ else if (codec_port(port))
+ while (count--) *(volatile unsigned char *)((unsigned long)area6_io8_base+(port-CODEC_IO_BASE)) = *((unsigned char *) addr)++;
+#endif
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port)) {
+ volatile __u8 *bp = (__u8 *)PCI_IOMAP(port);
+
+ while (count--) *bp = *((volatile unsigned char *) addr)++;
+ } else {
+ volatile __u16 *p = (volatile unsigned short *)port2adr(port);
+
+ while (count--) *p = *((unsigned char *) addr)++;
+ }
+}
+
+void hs7751rvoip_outsw(unsigned long port, const void *addr, unsigned long count)
+{
+ volatile __u16 *p;
+
+ if (PXSEG(port))
+ p = (volatile unsigned short *)port;
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port))
+ p = (volatile unsigned short *)PCI_IOMAP(port);
+ else
+ p = (volatile unsigned short *)port2adr(port);
+ while (count--) *p = *((__u16 *) addr)++;
+}
+
+void hs7751rvoip_outsl(unsigned long port, const void *addr, unsigned long count)
+{
+ if (CHECK_SH7751_PCIIO(port) || shifted_port(port)) {
+ volatile __u32 *p = (__u32 *)PCI_IOMAP(port);
+
+ while (count--) *p = *((__u32 *) addr)++;
+ } else
+ maybebadio(outsl, port);
+}
+
+void *hs7751rvoip_ioremap(unsigned long offset, unsigned long size)
+{
+ if (offset >= 0xfd000000)
+ return (void *)offset;
+ else
+ return (void *)P2SEGADDR(offset);
+}
+EXPORT_SYMBOL(hs7751rvoip_ioremap);
+
+unsigned long hs7751rvoip_isa_port2addr(unsigned long offset)
+{
+ return port2adr(offset);
+}
--- /dev/null
+/*
+ * linux/arch/sh/boards/renesas/hs7751rvoip/irq.c
+ *
+ * Copyright (C) 2000 Kazumoto Kojima
+ *
+ * Renesas Technology Sales HS7751RVoIP Support.
+ *
+ * Modified for HS7751RVoIP by
+ * Atom Create Engineering Co., Ltd. 2002.
+ * Lineo uSolutions, Inc. 2003.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/hs7751rvoip/hs7751rvoip.h>
+
+static int mask_pos[] = {8, 9, 10, 11, 12, 13, 0, 1, 2, 3, 4, 5, 6, 7};
+
+static void enable_hs7751rvoip_irq(unsigned int irq);
+static void disable_hs7751rvoip_irq(unsigned int irq);
+
+/* shutdown is same as "disable" */
+#define shutdown_hs7751rvoip_irq disable_hs7751rvoip_irq
+
+static void ack_hs7751rvoip_irq(unsigned int irq);
+static void end_hs7751rvoip_irq(unsigned int irq);
+
+static unsigned int startup_hs7751rvoip_irq(unsigned int irq)
+{
+ enable_hs7751rvoip_irq(irq);
+ return 0; /* never anything pending */
+}
+
+static void disable_hs7751rvoip_irq(unsigned int irq)
+{
+ unsigned long flags;
+ unsigned short val;
+ unsigned short mask = 0xffff ^ (0x0001 << mask_pos[irq]);
+
+ /* Set the priority in IPR to 0 */
+ local_irq_save(flags);
+ val = ctrl_inw(IRLCNTR3);
+ val &= mask;
+ ctrl_outw(val, IRLCNTR3);
+ local_irq_restore(flags);
+}
+
+static void enable_hs7751rvoip_irq(unsigned int irq)
+{
+ unsigned long flags;
+ unsigned short val;
+ unsigned short value = (0x0001 << mask_pos[irq]);
+
+ /* Set priority in IPR back to original value */
+ local_irq_save(flags);
+ val = ctrl_inw(IRLCNTR3);
+ val |= value;
+ ctrl_outw(val, IRLCNTR3);
+ local_irq_restore(flags);
+}
+
+static void ack_hs7751rvoip_irq(unsigned int irq)
+{
+ disable_hs7751rvoip_irq(irq);
+}
+
+static void end_hs7751rvoip_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ enable_hs7751rvoip_irq(irq);
+}
+
+static struct hw_interrupt_type hs7751rvoip_irq_type = {
+ "HS7751RVoIP IRQ",
+ startup_hs7751rvoip_irq,
+ shutdown_hs7751rvoip_irq,
+ enable_hs7751rvoip_irq,
+ disable_hs7751rvoip_irq,
+ ack_hs7751rvoip_irq,
+ end_hs7751rvoip_irq,
+};
+
+static void make_hs7751rvoip_irq(unsigned int irq)
+{
+ disable_irq_nosync(irq);
+ irq_desc[irq].handler = &hs7751rvoip_irq_type;
+ disable_hs7751rvoip_irq(irq);
+}
+
+/*
+ * Initialize IRQ setting
+ */
+void __init init_hs7751rvoip_IRQ(void)
+{
+ int i;
+
+ /* IRL0=ON HOOK1
+ * IRL1=OFF HOOK1
+ * IRL2=ON HOOK2
+ * IRL3=OFF HOOK2
+ * IRL4=Ringing Detection
+ * IRL5=CODEC
+ * IRL6=Ethernet
+ * IRL7=Ethernet Hub
+ * IRL8=USB Communication
+ * IRL9=USB Connection
+ * IRL10=USB DMA
+ * IRL11=CF Card
+ * IRL12=PCMCIA
+ * IRL13=PCI Slot
+ */
+ ctrl_outw(0x9876, IRLCNTR1);
+ ctrl_outw(0xdcba, IRLCNTR2);
+ ctrl_outw(0x0050, IRLCNTR4);
+ ctrl_outw(0x4321, IRLCNTR5);
+
+ for (i=0; i<14; i++)
+ make_hs7751rvoip_irq(i);
+}
--- /dev/null
+/*
+ * linux/arch/sh/kernel/setup_hs7751rvoip.c
+ *
+ * Copyright (C) 2000 Kazumoto Kojima
+ *
+ * Renesas Technology Sales HS7751RVoIP Support.
+ *
+ * Modified for HS7751RVoIP by
+ * Atom Create Engineering Co., Ltd. 2002.
+ * Lineo uSolutions, Inc. 2003.
+ */
+
+#include <linux/config.h>
+#include <asm/io.h>
+#include <asm/hs7751rvoip/hs7751rvoip.h>
+
+extern unsigned int debug_counter;
+
+void debug_led_disp(void)
+{
+ unsigned short value;
+
+ value = (unsigned char)debug_counter++;
+ ctrl_outb((0xf0|value), PA_OUTPORTR);
+ if (value == 0x0f)
+ debug_counter = 0;
+}
--- /dev/null
+/*
+ * linux/arch/sh/kernel/mach_hs7751rvoip.c
+ *
+ * Minor tweak of mach_se.c file to reference hs7751rvoip-specific items.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * Machine vector for the Renesas Technology sales HS7751RVoIP
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+
+#include <asm/machvec.h>
+#include <asm/rtc.h>
+#include <asm/irq.h>
+#include <asm/hs7751rvoip/io.h>
+
+extern void init_hs7751rvoip_IRQ(void);
+extern void *hs7751rvoip_ioremap(unsigned long, unsigned long);
+
+/*
+ * The Machine Vector
+ */
+
+struct sh_machine_vector mv_hs7751rvoip __initmv = {
+ .mv_nr_irqs = 72,
+
+ .mv_inb = hs7751rvoip_inb,
+ .mv_inw = hs7751rvoip_inw,
+ .mv_inl = hs7751rvoip_inl,
+ .mv_outb = hs7751rvoip_outb,
+ .mv_outw = hs7751rvoip_outw,
+ .mv_outl = hs7751rvoip_outl,
+
+ .mv_inb_p = hs7751rvoip_inb_p,
+ .mv_inw_p = hs7751rvoip_inw,
+ .mv_inl_p = hs7751rvoip_inl,
+ .mv_outb_p = hs7751rvoip_outb_p,
+ .mv_outw_p = hs7751rvoip_outw,
+ .mv_outl_p = hs7751rvoip_outl,
+
+ .mv_insb = hs7751rvoip_insb,
+ .mv_insw = hs7751rvoip_insw,
+ .mv_insl = hs7751rvoip_insl,
+ .mv_outsb = hs7751rvoip_outsb,
+ .mv_outsw = hs7751rvoip_outsw,
+ .mv_outsl = hs7751rvoip_outsl,
+
+ .mv_ioremap = hs7751rvoip_ioremap,
+ .mv_isa_port2addr = hs7751rvoip_isa_port2addr,
+ .mv_init_irq = init_hs7751rvoip_IRQ,
+};
+ALIAS_MV(hs7751rvoip)
--- /dev/null
+/*
+ * linux/arch/sh/kernel/pci-hs7751rvoip.c
+ *
+ * Author: Ian DaSilva (idasilva@mvista.com)
+ *
+ * Highly leveraged from pci-bigsur.c, written by Dustin McIntire.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * PCI initialization for the Renesas SH7751R HS7751RVoIP board
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/module.h>
+
+#include <asm/io.h>
+#include "../../../drivers/pci/pci-sh7751.h"
+#include <asm/hs7751rvoip/hs7751rvoip.h>
+
+#define PCIMCR_MRSET_OFF 0xBFFFFFFF
+#define PCIMCR_RFSH_OFF 0xFFFFFFFB
+
+/*
+ * Only long word accesses of the PCIC's internal local registers and the
+ * configuration registers from the CPU is supported.
+ */
+#define PCIC_WRITE(x,v) writel((v), PCI_REG(x))
+#define PCIC_READ(x) readl(PCI_REG(x))
+
+/*
+ * Description: This function sets up and initializes the pcic, sets
+ * up the BARS, maps the DRAM into the address space etc, etc.
+ */
+int __init pcibios_init_platform(void)
+{
+ unsigned long bcr1, wcr1, wcr2, wcr3, mcr;
+ unsigned short bcr2, bcr3;
+
+ /*
+ * Initialize the slave bus controller on the pcic. The values used
+ * here should not be hardcoded, but they should be taken from the bsc
+ * on the processor, to make this function as generic as possible.
+ * (i.e. Another sbc may usr different SDRAM timing settings -- in order
+ * for the pcic to work, its settings need to be exactly the same.)
+ */
+ bcr1 = (*(volatile unsigned long *)(SH7751_BCR1));
+ bcr2 = (*(volatile unsigned short *)(SH7751_BCR2));
+ bcr3 = (*(volatile unsigned short *)(SH7751_BCR3));
+ wcr1 = (*(volatile unsigned long *)(SH7751_WCR1));
+ wcr2 = (*(volatile unsigned long *)(SH7751_WCR2));
+ wcr3 = (*(volatile unsigned long *)(SH7751_WCR3));
+ mcr = (*(volatile unsigned long *)(SH7751_MCR));
+
+ bcr1 = bcr1 | 0x00080000; /* Enable Bit 19, BREQEN */
+ (*(volatile unsigned long *)(SH7751_BCR1)) = bcr1;
+
+ bcr1 = bcr1 | 0x40080000; /* Enable Bit 19 BREQEN, set PCIC to slave */
+ PCIC_WRITE(SH7751_PCIBCR1, bcr1); /* PCIC BCR1 */
+ PCIC_WRITE(SH7751_PCIBCR2, bcr2); /* PCIC BCR2 */
+ PCIC_WRITE(SH7751_PCIBCR3, bcr3); /* PCIC BCR3 */
+ PCIC_WRITE(SH7751_PCIWCR1, wcr1); /* PCIC WCR1 */
+ PCIC_WRITE(SH7751_PCIWCR2, wcr2); /* PCIC WCR2 */
+ PCIC_WRITE(SH7751_PCIWCR3, wcr3); /* PCIC WCR3 */
+ mcr = (mcr & PCIMCR_MRSET_OFF) & PCIMCR_RFSH_OFF;
+ PCIC_WRITE(SH7751_PCIMCR, mcr); /* PCIC MCR */
+
+ /* Enable all interrupts, so we know what to fix */
+ PCIC_WRITE(SH7751_PCIINTM, 0x0000c3ff);
+ PCIC_WRITE(SH7751_PCIAINTM, 0x0000380f);
+
+ /* Set up standard PCI config registers */
+ PCIC_WRITE(SH7751_PCICONF1, 0xFB900047); /* Bus Master, Mem & I/O access */
+ PCIC_WRITE(SH7751_PCICONF2, 0x00000000); /* PCI Class code & Revision ID */
+ PCIC_WRITE(SH7751_PCICONF4, 0xab000001); /* PCI I/O address (local regs) */
+ PCIC_WRITE(SH7751_PCICONF5, 0x0c000000); /* PCI MEM address (local RAM) */
+ PCIC_WRITE(SH7751_PCICONF6, 0xd0000000); /* PCI MEM address (unused) */
+ PCIC_WRITE(SH7751_PCICONF11, 0x35051054); /* PCI Subsystem ID & Vendor ID */
+ PCIC_WRITE(SH7751_PCILSR0, 0x03f00000); /* MEM (full 64M exposed) */
+ PCIC_WRITE(SH7751_PCILSR1, 0x00000000); /* MEM (unused) */
+ PCIC_WRITE(SH7751_PCILAR0, 0x0c000000); /* MEM (direct map from PCI) */
+ PCIC_WRITE(SH7751_PCILAR1, 0x00000000); /* MEM (unused) */
+
+ /* Now turn it on... */
+ PCIC_WRITE(SH7751_PCICR, 0xa5000001);
+
+ /*
+ * Set PCIMBR and PCIIOBR here, assuming a single window
+ * (16M MEM, 256K IO) is enough. If a larger space is
+ * needed, the readx/writex and inx/outx functions will
+ * have to do more (e.g. setting registers for each call).
+ */
+
+ /*
+ * Set the MBR so PCI address is one-to-one with window,
+ * meaning all calls go straight through... use ifdef to
+ * catch erroneous assumption.
+ */
+ BUG_ON(PCIBIOS_MIN_MEM != SH7751_PCI_MEMORY_BASE);
+
+ PCIC_WRITE(SH7751_PCIMBR, PCIBIOS_MIN_MEM);
+
+ /* Set IOBR for window containing area specified in pci.h */
+ PCIC_WRITE(SH7751_PCIIOBR, (PCIBIOS_MIN_IO & SH7751_PCIIOBR_MASK));
+
+ /* All done, may as well say so... */
+ printk("SH7751R PCI: Finished initialization of the PCI controller\n");
+
+ return 1;
+}
+
+int __init pcibios_map_platform_irq(u8 slot, u8 pin)
+{
+ switch (slot) {
+ case 0: return IRQ_PCISLOT; /* PCI Extend slot */
+ case 1: return IRQ_PCMCIA; /* PCI Cardbus Bridge */
+ case 2: return IRQ_PCIETH; /* Realtek Ethernet controller */
+ case 3: return IRQ_PCIHUB; /* Realtek Ethernet Hub controller */
+ default:
+ printk("PCI: Bad IRQ mapping request for slot %d\n", slot);
+ return -1;
+ }
+}
+
+static struct resource sh7751_io_resource = {
+ .name = "SH7751_IO",
+ .start = 0x4000,
+ .end = 0x4000 + SH7751_PCI_IO_SIZE - 1,
+ .flags = IORESOURCE_IO
+};
+
+static struct resource sh7751_mem_resource = {
+ .name = "SH7751_mem",
+ .start = SH7751_PCI_MEMORY_BASE,
+ .end = SH7751_PCI_MEMORY_BASE + SH7751_PCI_MEM_SIZE - 1,
+ .flags = IORESOURCE_MEM
+};
+
+extern struct pci_ops sh7751_pci_ops;
+
+struct pci_channel board_pci_channels[] = {
+ { &sh7751_pci_ops, &sh7751_io_resource, &sh7751_mem_resource, 0, 0xff },
+ { NULL, NULL, NULL, 0, 0 },
+};
+EXPORT_SYMBOL(board_pci_channels);
--- /dev/null
+/*
+ * linux/arch/sh/kernel/setup_hs7751rvoip.c
+ *
+ * Copyright (C) 2000 Kazumoto Kojima
+ *
+ * Renesas Technology Sales HS7751RVoIP Support.
+ *
+ * Modified for HS7751RVoIP by
+ * Atom Create Engineering Co., Ltd. 2002.
+ * Lineo uSolutions, Inc. 2003.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+#include <asm/io.h>
+#include <asm/hs7751rvoip/hs7751rvoip.h>
+
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+
+/* defined in mm/ioremap.c */
+extern void * p3_ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags);
+
+unsigned int debug_counter;
+
+const char *get_system_type(void)
+{
+ return "HS7751RVoIP";
+}
+
+/*
+ * Initialize the board
+ */
+void __init platform_setup(void)
+{
+ printk(KERN_INFO "Renesas Technology Sales HS7751RVoIP-2 support.\n");
+ ctrl_outb(0xf0, PA_OUTPORTR);
+ debug_counter = 0;
+}
+
+void *area5_io8_base;
+void *area6_io8_base;
+void *area5_io16_base;
+void *area6_io16_base;
+
+int __init cf_init(void)
+{
+ pgprot_t prot;
+ unsigned long paddrbase, psize;
+
+ /* open I/O area window */
+ paddrbase = virt_to_phys((void *)(PA_AREA5_IO+0x00000800));
+ psize = PAGE_SIZE;
+ prot = PAGE_KERNEL_PCC(1, _PAGE_PCC_COM16);
+ area5_io16_base = p3_ioremap(paddrbase, psize, prot.pgprot);
+ if (!area5_io16_base) {
+ printk("allocate_cf_area : can't open CF I/O window!\n");
+ return -ENOMEM;
+ }
+
+ /* XXX : do we need attribute and common-memory area also? */
+
+ paddrbase = virt_to_phys((void *)PA_AREA6_IO);
+ psize = PAGE_SIZE;
+#if defined(CONFIG_HS7751RVOIP_CODEC)
+ prot = PAGE_KERNEL_PCC(0, _PAGE_PCC_COM8);
+#else
+ prot = PAGE_KERNEL_PCC(0, _PAGE_PCC_IO8);
+#endif
+ area6_io8_base = p3_ioremap(paddrbase, psize, prot.pgprot);
+ if (!area6_io8_base) {
+ printk("allocate_cf_area : can't open CODEC I/O 8bit window!\n");
+ return -ENOMEM;
+ }
+ prot = PAGE_KERNEL_PCC(0, _PAGE_PCC_IO16);
+ area6_io16_base = p3_ioremap(paddrbase, psize, prot.pgprot);
+ if (!area6_io16_base) {
+ printk("allocate_cf_area : can't open CODEC I/O 16bit window!\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+__initcall (cf_init);
--- /dev/null
+#
+# Makefile for the RTS7751R2D specific parts of the kernel
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+
+obj-y := mach.o setup.o io.o irq.o led.o
+
--- /dev/null
+/*
+ * linux/arch/sh/kernel/io_rts7751r2d.c
+ *
+ * Copyright (C) 2001 Ian da Silva, Jeremy Siegel
+ * Based largely on io_se.c.
+ *
+ * I/O routine for Renesas Technology sales RTS7751R2D.
+ *
+ * Initial version only to support LAN access; some
+ * placeholder code from io_rts7751r2d.c left in with the
+ * expectation of later SuperIO and PCMCIA access.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+#include <asm/rts7751r2d/rts7751r2d.h>
+#include <asm/addrspace.h>
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include "../../../drivers/pci/pci-sh7751.h"
+
+/*
+ * The 7751R RTS7751R2D uses the built-in PCI controller (PCIC)
+ * of the 7751R processor, and has a SuperIO accessible via the PCI.
+ * The board also includes a PCMCIA controller on its memory bus,
+ * like the other Solution Engine boards.
+ */
+
+#define PCIIOBR (volatile long *)PCI_REG(SH7751_PCIIOBR)
+#define PCIMBR (volatile long *)PCI_REG(SH7751_PCIMBR)
+#define PCI_IO_AREA SH7751_PCI_IO_BASE
+#define PCI_MEM_AREA SH7751_PCI_CONFIG_BASE
+
+#define PCI_IOMAP(adr) (PCI_IO_AREA + (adr & ~SH7751_PCIIOBR_MASK))
+
+#define maybebadio(name,port) \
+ printk("bad PC-like io %s for port 0x%lx at 0x%08x\n", \
+ #name, (port), (__u32) __builtin_return_address(0))
+
+static inline void delay(void)
+{
+ ctrl_inw(0xa0000000);
+}
+
+static inline unsigned long port2adr(unsigned int port)
+{
+ if ((0x1f0 <= port && port < 0x1f8) || port == 0x3f6)
+ if (port == 0x3f6)
+ return (PA_AREA5_IO + 0x80c);
+ else
+ return (PA_AREA5_IO + 0x1000 + ((port-0x1f0) << 1));
+ else
+ maybebadio(port2adr, (unsigned long)port);
+
+ return port;
+}
+
+static inline unsigned long port88796l(unsigned int port, int flag)
+{
+ unsigned long addr;
+
+ if (flag)
+ addr = PA_AX88796L + ((port - AX88796L_IO_BASE) << 1);
+ else
+ addr = PA_AX88796L + ((port - AX88796L_IO_BASE) << 1) + 0x1000;
+
+ return addr;
+}
+
+/* The 7751R RTS7751R2D seems to have everything hooked */
+/* up pretty normally (nothing on high-bytes only...) so this */
+/* shouldn't be needed */
+static inline int shifted_port(unsigned long port)
+{
+ /* For IDE registers, value is not shifted */
+ if ((0x1f0 <= port && port < 0x1f8) || port == 0x3f6)
+ return 0;
+ else
+ return 1;
+}
+
+/* In case someone configures the kernel w/o PCI support: in that */
+/* scenario, don't ever bother to check for PCI-window addresses */
+
+/* NOTE: WINDOW CHECK MAY BE A BIT OFF, HIGH PCIBIOS_MIN_IO WRAPS? */
+#if defined(CONFIG_PCI)
+#define CHECK_SH7751_PCIIO(port) \
+ ((port >= PCIBIOS_MIN_IO) && (port < (PCIBIOS_MIN_IO + SH7751_PCI_IO_SIZE)))
+#else
+#define CHECK_SH7751_PCIIO(port) (0)
+#endif
+
+#if defined(CONFIG_NE2000) || defined(CONFIG_NE2000_MODULE)
+#define CHECK_AX88796L_PORT(port) \
+ ((port >= AX88796L_IO_BASE) && (port < (AX88796L_IO_BASE+0x20)))
+#else
+#define CHECK_AX88796L_PORT(port) (0)
+#endif
+
+/*
+ * General outline: remap really low stuff [eventually] to SuperIO,
+ * stuff in PCI IO space (at or above window at pci.h:PCIBIOS_MIN_IO)
+ * is mapped through the PCI IO window. Stuff with high bits (PXSEG)
+ * should be way beyond the window, and is used w/o translation for
+ * compatibility.
+ */
+unsigned char rts7751r2d_inb(unsigned long port)
+{
+ if (CHECK_AX88796L_PORT(port))
+ return (*(volatile unsigned short *)port88796l(port, 0)) & 0xff;
+ else if (PXSEG(port))
+ return *(volatile unsigned char *)port;
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port))
+ return *(volatile unsigned char *)PCI_IOMAP(port);
+ else
+ return (*(volatile unsigned short *)port2adr(port) & 0xff);
+}
+
+unsigned char rts7751r2d_inb_p(unsigned long port)
+{
+ unsigned char v;
+
+ if (CHECK_AX88796L_PORT(port))
+ v = (*(volatile unsigned short *)port88796l(port, 0)) & 0xff;
+ else if (PXSEG(port))
+ v = *(volatile unsigned char *)port;
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port))
+ v = *(volatile unsigned char *)PCI_IOMAP(port);
+ else
+ v = (*(volatile unsigned short *)port2adr(port) & 0xff);
+ delay();
+
+ return v;
+}
+
+unsigned short rts7751r2d_inw(unsigned long port)
+{
+ if (CHECK_AX88796L_PORT(port))
+ maybebadio(inw, port);
+ else if (PXSEG(port))
+ return *(volatile unsigned short *)port;
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port))
+ return *(volatile unsigned short *)PCI_IOMAP(port);
+ else
+ maybebadio(inw, port);
+
+ return 0;
+}
+
+unsigned int rts7751r2d_inl(unsigned long port)
+{
+ if (CHECK_AX88796L_PORT(port))
+ maybebadio(inl, port);
+ else if (PXSEG(port))
+ return *(volatile unsigned long *)port;
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port))
+ return *(volatile unsigned long *)PCI_IOMAP(port);
+ else
+ maybebadio(inl, port);
+
+ return 0;
+}
+
+void rts7751r2d_outb(unsigned char value, unsigned long port)
+{
+ if (CHECK_AX88796L_PORT(port))
+ *((volatile unsigned short *)port88796l(port, 0)) = value;
+ else if (PXSEG(port))
+ *(volatile unsigned char *)port = value;
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port))
+ *(volatile unsigned char *)PCI_IOMAP(port) = value;
+ else
+ *(volatile unsigned short *)port2adr(port) = value;
+}
+
+void rts7751r2d_outb_p(unsigned char value, unsigned long port)
+{
+ if (CHECK_AX88796L_PORT(port))
+ *((volatile unsigned short *)port88796l(port, 0)) = value;
+ else if (PXSEG(port))
+ *(volatile unsigned char *)port = value;
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port))
+ *(volatile unsigned char *)PCI_IOMAP(port) = value;
+ else
+ *(volatile unsigned short *)port2adr(port) = value;
+ delay();
+}
+
+void rts7751r2d_outw(unsigned short value, unsigned long port)
+{
+ if (CHECK_AX88796L_PORT(port))
+ maybebadio(outw, port);
+ else if (PXSEG(port))
+ *(volatile unsigned short *)port = value;
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port))
+ *(volatile unsigned short *)PCI_IOMAP(port) = value;
+ else
+ maybebadio(outw, port);
+}
+
+void rts7751r2d_outl(unsigned int value, unsigned long port)
+{
+ if (CHECK_AX88796L_PORT(port))
+ maybebadio(outl, port);
+ else if (PXSEG(port))
+ *(volatile unsigned long *)port = value;
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port))
+ *(volatile unsigned long *)PCI_IOMAP(port) = value;
+ else
+ maybebadio(outl, port);
+}
+
+void rts7751r2d_insb(unsigned long port, void *addr, unsigned long count)
+{
+ volatile __u8 *bp;
+ volatile __u16 *p;
+
+ if (CHECK_AX88796L_PORT(port)) {
+ p = (volatile unsigned short *)port88796l(port, 0);
+ while (count--) *((unsigned char *) addr)++ = *p & 0xff;
+ } else if (PXSEG(port))
+ while (count--) *((unsigned char *) addr)++ = *(volatile unsigned char *)port;
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port)) {
+ bp = (__u8 *)PCI_IOMAP(port);
+ while (count--) *((volatile unsigned char *) addr)++ = *bp;
+ } else {
+ p = (volatile unsigned short *)port2adr(port);
+ while (count--) *((unsigned char *) addr)++ = *p & 0xff;
+ }
+}
+
+void rts7751r2d_insw(unsigned long port, void *addr, unsigned long count)
+{
+ volatile __u16 *p;
+
+ if (CHECK_AX88796L_PORT(port))
+ p = (volatile unsigned short *)port88796l(port, 1);
+ else if (PXSEG(port))
+ p = (volatile unsigned short *)port;
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port))
+ p = (volatile unsigned short *)PCI_IOMAP(port);
+ else
+ p = (volatile unsigned short *)port2adr(port);
+ while (count--) *((__u16 *) addr)++ = *p;
+}
+
+void rts7751r2d_insl(unsigned long port, void *addr, unsigned long count)
+{
+ if (CHECK_AX88796L_PORT(port))
+ maybebadio(insl, port);
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port)) {
+ volatile __u32 *p = (__u32 *)PCI_IOMAP(port);
+
+ while (count--) *((__u32 *) addr)++ = *p;
+ } else
+ maybebadio(insl, port);
+}
+
+void rts7751r2d_outsb(unsigned long port, const void *addr, unsigned long count)
+{
+ volatile __u8 *bp;
+ volatile __u16 *p;
+
+ if (CHECK_AX88796L_PORT(port)) {
+ p = (volatile unsigned short *)port88796l(port, 0);
+ while (count--) *p = *((unsigned char *) addr)++;
+ } else if (PXSEG(port))
+ while (count--) *(volatile unsigned char *)port = *((unsigned char *) addr)++;
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port)) {
+ bp = (__u8 *)PCI_IOMAP(port);
+ while (count--) *bp = *((volatile unsigned char *) addr)++;
+ } else {
+ p = (volatile unsigned short *)port2adr(port);
+ while (count--) *p = *((unsigned char *) addr)++;
+ }
+}
+
+void rts7751r2d_outsw(unsigned long port, const void *addr, unsigned long count)
+{
+ volatile __u16 *p;
+
+ if (CHECK_AX88796L_PORT(port))
+ p = (volatile unsigned short *)port88796l(port, 1);
+ else if (PXSEG(port))
+ p = (volatile unsigned short *)port;
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port))
+ p = (volatile unsigned short *)PCI_IOMAP(port);
+ else
+ p = (volatile unsigned short *)port2adr(port);
+ while (count--) *p = *((__u16 *) addr)++;
+}
+
+void rts7751r2d_outsl(unsigned long port, const void *addr, unsigned long count)
+{
+ if (CHECK_AX88796L_PORT(port))
+ maybebadio(outsl, port);
+ else if (CHECK_SH7751_PCIIO(port) || shifted_port(port)) {
+ volatile __u32 *p = (__u32 *)PCI_IOMAP(port);
+
+ while (count--) *p = *((__u32 *) addr)++;
+ } else
+ maybebadio(outsl, port);
+}
+
+void *rts7751r2d_ioremap(unsigned long offset, unsigned long size)
+{
+ if (offset >= 0xfd000000)
+ return (void *)offset;
+ else
+ return (void *)P2SEGADDR(offset);
+}
+EXPORT_SYMBOL(rts7751r2d_ioremap);
+
+unsigned long rts7751r2d_isa_port2addr(unsigned long offset)
+{
+ return port2adr(offset);
+}
--- /dev/null
+/*
+ * linux/arch/sh/boards/renesas/rts7751r2d/irq.c
+ *
+ * Copyright (C) 2000 Kazumoto Kojima
+ *
+ * Renesas Technology Sales RTS7751R2D Support.
+ *
+ * Modified for RTS7751R2D by
+ * Atom Create Engineering Co., Ltd. 2002.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/rts7751r2d/rts7751r2d.h>
+
+#if defined(CONFIG_RTS7751R2D_REV11)
+static int mask_pos[] = {11, 9, 8, 12, 10, 6, 5, 4, 7, 14, 13, 0, 0, 0, 0};
+#else
+static int mask_pos[] = {6, 11, 9, 8, 12, 10, 5, 4, 7, 14, 13, 0, 0, 0, 0};
+#endif
+
+extern int voyagergx_irq_demux(int irq);
+extern void setup_voyagergx_irq(void);
+
+static void enable_rts7751r2d_irq(unsigned int irq);
+static void disable_rts7751r2d_irq(unsigned int irq);
+
+/* shutdown is same as "disable" */
+#define shutdown_rts7751r2d_irq disable_rts7751r2d_irq
+
+static void ack_rts7751r2d_irq(unsigned int irq);
+static void end_rts7751r2d_irq(unsigned int irq);
+
+static unsigned int startup_rts7751r2d_irq(unsigned int irq)
+{
+ enable_rts7751r2d_irq(irq);
+ return 0; /* never anything pending */
+}
+
+static void disable_rts7751r2d_irq(unsigned int irq)
+{
+ unsigned long flags;
+ unsigned short val;
+ unsigned short mask = 0xffff ^ (0x0001 << mask_pos[irq]);
+
+ /* Set the priority in IPR to 0 */
+ local_irq_save(flags);
+ val = ctrl_inw(IRLCNTR1);
+ val &= mask;
+ ctrl_outw(val, IRLCNTR1);
+ local_irq_restore(flags);
+}
+
+static void enable_rts7751r2d_irq(unsigned int irq)
+{
+ unsigned long flags;
+ unsigned short val;
+ unsigned short value = (0x0001 << mask_pos[irq]);
+
+ /* Set priority in IPR back to original value */
+ local_irq_save(flags);
+ val = ctrl_inw(IRLCNTR1);
+ val |= value;
+ ctrl_outw(val, IRLCNTR1);
+ local_irq_restore(flags);
+}
+
+int rts7751r2d_irq_demux(int irq)
+{
+ int demux_irq;
+
+ demux_irq = voyagergx_irq_demux(irq);
+ return demux_irq;
+}
+
+static void ack_rts7751r2d_irq(unsigned int irq)
+{
+ disable_rts7751r2d_irq(irq);
+}
+
+static void end_rts7751r2d_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ enable_rts7751r2d_irq(irq);
+}
+
+static struct hw_interrupt_type rts7751r2d_irq_type = {
+ "RTS7751R2D IRQ",
+ startup_rts7751r2d_irq,
+ shutdown_rts7751r2d_irq,
+ enable_rts7751r2d_irq,
+ disable_rts7751r2d_irq,
+ ack_rts7751r2d_irq,
+ end_rts7751r2d_irq,
+};
+
+static void make_rts7751r2d_irq(unsigned int irq)
+{
+ disable_irq_nosync(irq);
+ irq_desc[irq].handler = &rts7751r2d_irq_type;
+ disable_rts7751r2d_irq(irq);
+}
+
+/*
+ * Initialize IRQ setting
+ */
+void __init init_rts7751r2d_IRQ(void)
+{
+ int i;
+
+ /* IRL0=KEY Input
+ * IRL1=Ethernet
+ * IRL2=CF Card
+ * IRL3=CF Card Insert
+ * IRL4=PCMCIA
+ * IRL5=VOYAGER
+ * IRL6=RTC Alarm
+ * IRL7=RTC Timer
+ * IRL8=SD Card
+ * IRL9=PCI Slot #1
+ * IRL10=PCI Slot #2
+ * IRL11=Extention #0
+ * IRL12=Extention #1
+ * IRL13=Extention #2
+ * IRL14=Extention #3
+ */
+
+ for (i=0; i<15; i++)
+ make_rts7751r2d_irq(i);
+
+ setup_voyagergx_irq();
+}
--- /dev/null
+/*
+ * linux/arch/sh/kernel/led_rts7751r2d.c
+ *
+ * Copyright (C) Atom Create Engineering Co., Ltd.
+ *
+ * May be copied or modified under the terms of GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * This file contains Renesas Technology Sales RTS7751R2D specific LED code.
+ */
+
+#include <linux/config.h>
+#include <asm/io.h>
+#include <asm/rts7751r2d/rts7751r2d.h>
+
+extern unsigned int debug_counter;
+
+#ifdef CONFIG_HEARTBEAT
+
+#include <linux/sched.h>
+
+/* Cycle the LED's in the clasic Knightriger/Sun pattern */
+void heartbeat_rts7751r2d(void)
+{
+ static unsigned int cnt = 0, period = 0;
+ volatile unsigned short *p = (volatile unsigned short *)PA_OUTPORT;
+ static unsigned bit = 0, up = 1;
+
+ cnt += 1;
+ if (cnt < period)
+ return;
+
+ cnt = 0;
+
+ /* Go through the points (roughly!):
+ * f(0)=10, f(1)=16, f(2)=20, f(5)=35, f(int)->110
+ */
+ period = 110 - ((300 << FSHIFT)/((avenrun[0]/5) + (3<<FSHIFT)));
+
+ *p = 1 << bit;
+ if (up)
+ if (bit == 7) {
+ bit--;
+ up = 0;
+ } else
+ bit++;
+ else if (bit == 0)
+ up = 1;
+ else
+ bit--;
+}
+#endif /* CONFIG_HEARTBEAT */
+
+void rts7751r2d_led(unsigned short value)
+{
+ ctrl_outw(value, PA_OUTPORT);
+}
+
+void debug_led_disp(void)
+{
+ unsigned short value;
+
+ value = (unsigned short)debug_counter++;
+ rts7751r2d_led(value);
+ if (value == 0xff)
+ debug_counter = 0;
+}
--- /dev/null
+/*
+ * linux/arch/sh/kernel/mach_rts7751r2d.c
+ *
+ * Minor tweak of mach_se.c file to reference rts7751r2d-specific items.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * Machine vector for the Renesas Technology sales RTS7751R2D
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/types.h>
+
+#include <asm/machvec.h>
+#include <asm/rtc.h>
+#include <asm/irq.h>
+#include <asm/rts7751r2d/io.h>
+
+extern void heartbeat_rts7751r2d(void);
+extern void init_rts7751r2d_IRQ(void);
+extern void *rts7751r2d_ioremap(unsigned long, unsigned long);
+extern int rts7751r2d_irq_demux(int irq);
+
+extern void *voyagergx_consistent_alloc(struct device *, size_t, dma_addr_t *, int);
+extern void voyagergx_consistent_free(struct device *, size_t, void *, dma_addr_t);
+
+/*
+ * The Machine Vector
+ */
+
+struct sh_machine_vector mv_rts7751r2d __initmv = {
+ .mv_nr_irqs = 72,
+
+ .mv_inb = rts7751r2d_inb,
+ .mv_inw = rts7751r2d_inw,
+ .mv_inl = rts7751r2d_inl,
+ .mv_outb = rts7751r2d_outb,
+ .mv_outw = rts7751r2d_outw,
+ .mv_outl = rts7751r2d_outl,
+
+ .mv_inb_p = rts7751r2d_inb_p,
+ .mv_inw_p = rts7751r2d_inw,
+ .mv_inl_p = rts7751r2d_inl,
+ .mv_outb_p = rts7751r2d_outb_p,
+ .mv_outw_p = rts7751r2d_outw,
+ .mv_outl_p = rts7751r2d_outl,
+
+ .mv_insb = rts7751r2d_insb,
+ .mv_insw = rts7751r2d_insw,
+ .mv_insl = rts7751r2d_insl,
+ .mv_outsb = rts7751r2d_outsb,
+ .mv_outsw = rts7751r2d_outsw,
+ .mv_outsl = rts7751r2d_outsl,
+
+ .mv_ioremap = rts7751r2d_ioremap,
+ .mv_isa_port2addr = rts7751r2d_isa_port2addr,
+ .mv_init_irq = init_rts7751r2d_IRQ,
+#ifdef CONFIG_HEARTBEAT
+ .mv_heartbeat = heartbeat_rts7751r2d,
+#endif
+ .mv_irq_demux = rts7751r2d_irq_demux,
+
+ .mv_consistent_alloc = voyagergx_consistent_alloc,
+ .mv_consistent_free = voyagergx_consistent_free,
+};
+ALIAS_MV(rts7751r2d)
--- /dev/null
+/*
+ * linux/arch/sh/kernel/setup_rts7751r2d.c
+ *
+ * Copyright (C) 2000 Kazumoto Kojima
+ *
+ * Renesas Technology Sales RTS7751R2D Support.
+ *
+ * Modified for RTS7751R2D by
+ * Atom Create Engineering Co., Ltd. 2002.
+ */
+
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/rts7751r2d/rts7751r2d.h>
+
+unsigned int debug_counter;
+
+const char *get_system_type(void)
+{
+ return "RTS7751R2D";
+}
+
+/*
+ * Initialize the board
+ */
+void __init platform_setup(void)
+{
+ printk(KERN_INFO "Renesas Technology Sales RTS7751R2D support.\n");
+ ctrl_outw(0x0000, PA_OUTPORT);
+ debug_counter = 0;
+}
--- /dev/null
+/*
+ * linux/arch/sh/boards/systemh/io.c
+ *
+ * Copyright (C) 2001 Ian da Silva, Jeremy Siegel
+ * Based largely on io_se.c.
+ *
+ * I/O routine for Hitachi 7751 Systemh.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/systemh/7751systemh.h>
+#include <asm/addrspace.h>
+#include <asm/io.h>
+
+#include <linux/pci.h>
+#include "../../drivers/pci/pci-sh7751.h"
+
+/*
+ * The 7751 SystemH Engine uses the built-in PCI controller (PCIC)
+ * of the 7751 processor, and has a SuperIO accessible on its memory
+ * bus.
+ */
+
+#define PCIIOBR (volatile long *)PCI_REG(SH7751_PCIIOBR)
+#define PCIMBR (volatile long *)PCI_REG(SH7751_PCIMBR)
+#define PCI_IO_AREA SH7751_PCI_IO_BASE
+#define PCI_MEM_AREA SH7751_PCI_CONFIG_BASE
+
+#define PCI_IOMAP(adr) (PCI_IO_AREA + (adr & ~SH7751_PCIIOBR_MASK))
+#define ETHER_IOMAP(adr) (0xB3000000 + (adr)) /*map to 16bits access area
+ of smc lan chip*/
+
+#define maybebadio(name,port) \
+ printk("bad PC-like io %s for port 0x%lx at 0x%08x\n", \
+ #name, (port), (__u32) __builtin_return_address(0))
+
+static inline void delay(void)
+{
+ ctrl_inw(0xa0000000);
+}
+
+static inline volatile __u16 *
+port2adr(unsigned int port)
+{
+ if (port >= 0x2000)
+ return (volatile __u16 *) (PA_MRSHPC + (port - 0x2000));
+#if 0
+ else
+ return (volatile __u16 *) (PA_SUPERIO + (port << 1));
+#endif
+ maybebadio(name,(unsigned long)port);
+ return (volatile __u16*)port;
+}
+
+/* In case someone configures the kernel w/o PCI support: in that */
+/* scenario, don't ever bother to check for PCI-window addresses */
+
+/* NOTE: WINDOW CHECK MAY BE A BIT OFF, HIGH PCIBIOS_MIN_IO WRAPS? */
+#if defined(CONFIG_PCI)
+#define CHECK_SH7751_PCIIO(port) \
+ ((port >= PCIBIOS_MIN_IO) && (port < (PCIBIOS_MIN_IO + SH7751_PCI_IO_SIZE)))
+#else
+#define CHECK_SH7751_PCIIO(port) (0)
+#endif
+
+/*
+ * General outline: remap really low stuff [eventually] to SuperIO,
+ * stuff in PCI IO space (at or above window at pci.h:PCIBIOS_MIN_IO)
+ * is mapped through the PCI IO window. Stuff with high bits (PXSEG)
+ * should be way beyond the window, and is used w/o translation for
+ * compatibility.
+ */
+unsigned char sh7751systemh_inb(unsigned long port)
+{
+ if (PXSEG(port))
+ return *(volatile unsigned char *)port;
+ else if (CHECK_SH7751_PCIIO(port))
+ return *(volatile unsigned char *)PCI_IOMAP(port);
+ else if (port <= 0x3F1)
+ return *(volatile unsigned char *)ETHER_IOMAP(port);
+ else
+ return (*port2adr(port))&0xff;
+}
+
+unsigned char sh7751systemh_inb_p(unsigned long port)
+{
+ unsigned char v;
+
+ if (PXSEG(port))
+ v = *(volatile unsigned char *)port;
+ else if (CHECK_SH7751_PCIIO(port))
+ v = *(volatile unsigned char *)PCI_IOMAP(port);
+ else if (port <= 0x3F1)
+ v = *(volatile unsigned char *)ETHER_IOMAP(port);
+ else
+ v = (*port2adr(port))&0xff;
+ delay();
+ return v;
+}
+
+unsigned short sh7751systemh_inw(unsigned long port)
+{
+ if (PXSEG(port))
+ return *(volatile unsigned short *)port;
+ else if (CHECK_SH7751_PCIIO(port))
+ return *(volatile unsigned short *)PCI_IOMAP(port);
+ else if (port >= 0x2000)
+ return *port2adr(port);
+ else if (port <= 0x3F1)
+ return *(volatile unsigned int *)ETHER_IOMAP(port);
+ else
+ maybebadio(inw, port);
+ return 0;
+}
+
+unsigned int sh7751systemh_inl(unsigned long port)
+{
+ if (PXSEG(port))
+ return *(volatile unsigned long *)port;
+ else if (CHECK_SH7751_PCIIO(port))
+ return *(volatile unsigned int *)PCI_IOMAP(port);
+ else if (port >= 0x2000)
+ return *port2adr(port);
+ else if (port <= 0x3F1)
+ return *(volatile unsigned int *)ETHER_IOMAP(port);
+ else
+ maybebadio(inl, port);
+ return 0;
+}
+
+void sh7751systemh_outb(unsigned char value, unsigned long port)
+{
+
+ if (PXSEG(port))
+ *(volatile unsigned char *)port = value;
+ else if (CHECK_SH7751_PCIIO(port))
+ *((unsigned char*)PCI_IOMAP(port)) = value;
+ else if (port <= 0x3F1)
+ *(volatile unsigned char *)ETHER_IOMAP(port) = value;
+ else
+ *(port2adr(port)) = value;
+}
+
+void sh7751systemh_outb_p(unsigned char value, unsigned long port)
+{
+ if (PXSEG(port))
+ *(volatile unsigned char *)port = value;
+ else if (CHECK_SH7751_PCIIO(port))
+ *((unsigned char*)PCI_IOMAP(port)) = value;
+ else if (port <= 0x3F1)
+ *(volatile unsigned char *)ETHER_IOMAP(port) = value;
+ else
+ *(port2adr(port)) = value;
+ delay();
+}
+
+void sh7751systemh_outw(unsigned short value, unsigned long port)
+{
+ if (PXSEG(port))
+ *(volatile unsigned short *)port = value;
+ else if (CHECK_SH7751_PCIIO(port))
+ *((unsigned short *)PCI_IOMAP(port)) = value;
+ else if (port >= 0x2000)
+ *port2adr(port) = value;
+ else if (port <= 0x3F1)
+ *(volatile unsigned short *)ETHER_IOMAP(port) = value;
+ else
+ maybebadio(outw, port);
+}
+
+void sh7751systemh_outl(unsigned int value, unsigned long port)
+{
+ if (PXSEG(port))
+ *(volatile unsigned long *)port = value;
+ else if (CHECK_SH7751_PCIIO(port))
+ *((unsigned long*)PCI_IOMAP(port)) = value;
+ else
+ maybebadio(outl, port);
+}
+
+void sh7751systemh_insb(unsigned long port, void *addr, unsigned long count)
+{
+ unsigned char *p = addr;
+ while (count--) *p++ = sh7751systemh_inb(port);
+}
+
+void sh7751systemh_insw(unsigned long port, void *addr, unsigned long count)
+{
+ unsigned short *p = addr;
+ while (count--) *p++ = sh7751systemh_inw(port);
+}
+
+void sh7751systemh_insl(unsigned long port, void *addr, unsigned long count)
+{
+ maybebadio(insl, port);
+}
+
+void sh7751systemh_outsb(unsigned long port, const void *addr, unsigned long count)
+{
+ unsigned char *p = (unsigned char*)addr;
+ while (count--) sh7751systemh_outb(*p++, port);
+}
+
+void sh7751systemh_outsw(unsigned long port, const void *addr, unsigned long count)
+{
+ unsigned short *p = (unsigned short*)addr;
+ while (count--) sh7751systemh_outw(*p++, port);
+}
+
+void sh7751systemh_outsl(unsigned long port, const void *addr, unsigned long count)
+{
+ maybebadio(outsw, port);
+}
+
+/* For read/write calls, just copy generic (pass-thru); PCIMBR is */
+/* already set up. For a larger memory space, these would need to */
+/* reset PCIMBR as needed on a per-call basis... */
+
+unsigned char sh7751systemh_readb(unsigned long addr)
+{
+ return *(volatile unsigned char*)addr;
+}
+
+unsigned short sh7751systemh_readw(unsigned long addr)
+{
+ return *(volatile unsigned short*)addr;
+}
+
+unsigned int sh7751systemh_readl(unsigned long addr)
+{
+ return *(volatile unsigned long*)addr;
+}
+
+void sh7751systemh_writeb(unsigned char b, unsigned long addr)
+{
+ *(volatile unsigned char*)addr = b;
+}
+
+void sh7751systemh_writew(unsigned short b, unsigned long addr)
+{
+ *(volatile unsigned short*)addr = b;
+}
+
+void sh7751systemh_writel(unsigned int b, unsigned long addr)
+{
+ *(volatile unsigned long*)addr = b;
+}
+
+\f
+
+/* Map ISA bus address to the real address. Only for PCMCIA. */
+
+/* ISA page descriptor. */
+static __u32 sh_isa_memmap[256];
+
+#if 0
+static int
+sh_isa_mmap(__u32 start, __u32 length, __u32 offset)
+{
+ int idx;
+
+ if (start >= 0x100000 || (start & 0xfff) || (length != 0x1000))
+ return -1;
+
+ idx = start >> 12;
+ sh_isa_memmap[idx] = 0xb8000000 + (offset &~ 0xfff);
+ printk("sh_isa_mmap: start %x len %x offset %x (idx %x paddr %x)\n",
+ start, length, offset, idx, sh_isa_memmap[idx]);
+ return 0;
+}
+#endif
+
+unsigned long
+sh7751systemh_isa_port2addr(unsigned long offset)
+{
+ int idx;
+
+ idx = (offset >> 12) & 0xff;
+ offset &= 0xfff;
+ return sh_isa_memmap[idx] + offset;
+}
--- /dev/null
+/*
+ * linux/arch/sh/boards/systemh/irq.c
+ *
+ * Copyright (C) 2000 Kazumoto Kojima
+ *
+ * Hitachi SystemH Support.
+ *
+ * Modified for 7751 SystemH by
+ * Jonathan Short.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+#include <asm/io.h>
+#include <asm/mach/7751systemh.h>
+#include <asm/smc37c93x.h>
+
+/* address of external interrupt mask register
+ * address must be set prior to use these (maybe in init_XXX_irq())
+ * XXX : is it better to use .config than specifying it in code? */
+static unsigned long *systemh_irq_mask_register = (unsigned long *)0xB3F10004;
+static unsigned long *systemh_irq_request_register = (unsigned long *)0xB3F10000;
+
+/* forward declaration */
+static unsigned int startup_systemh_irq(unsigned int irq);
+static void shutdown_systemh_irq(unsigned int irq);
+static void enable_systemh_irq(unsigned int irq);
+static void disable_systemh_irq(unsigned int irq);
+static void mask_and_ack_systemh(unsigned int);
+static void end_systemh_irq(unsigned int irq);
+
+/* hw_interrupt_type */
+static struct hw_interrupt_type systemh_irq_type = {
+ " SystemH Register",
+ startup_systemh_irq,
+ shutdown_systemh_irq,
+ enable_systemh_irq,
+ disable_systemh_irq,
+ mask_and_ack_systemh,
+ end_systemh_irq
+};
+
+static unsigned int startup_systemh_irq(unsigned int irq)
+{
+ enable_systemh_irq(irq);
+ return 0; /* never anything pending */
+}
+
+static void shutdown_systemh_irq(unsigned int irq)
+{
+ disable_systemh_irq(irq);
+}
+
+static void disable_systemh_irq(unsigned int irq)
+{
+ if (systemh_irq_mask_register) {
+ unsigned long flags;
+ unsigned long val, mask = 0x01 << 1;
+
+ /* Clear the "irq"th bit in the mask and set it in the request */
+ local_irq_save(flags);
+
+ val = ctrl_inl((unsigned long)systemh_irq_mask_register);
+ val &= ~mask;
+ ctrl_outl(val, (unsigned long)systemh_irq_mask_register);
+
+ val = ctrl_inl((unsigned long)systemh_irq_request_register);
+ val |= mask;
+ ctrl_outl(val, (unsigned long)systemh_irq_request_register);
+
+ local_irq_restore(flags);
+ }
+}
+
+static void enable_systemh_irq(unsigned int irq)
+{
+ if (systemh_irq_mask_register) {
+ unsigned long flags;
+ unsigned long val, mask = 0x01 << 1;
+
+ /* Set "irq"th bit in the mask register */
+ local_irq_save(flags);
+ val = ctrl_inl((unsigned long)systemh_irq_mask_register);
+ val |= mask;
+ ctrl_outl(val, (unsigned long)systemh_irq_mask_register);
+ local_irq_restore(flags);
+ }
+}
+
+static void mask_and_ack_systemh(unsigned int irq)
+{
+ disable_systemh_irq(irq);
+}
+
+static void end_systemh_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ enable_systemh_irq(irq);
+}
+
+void make_systemh_irq(unsigned int irq)
+{
+ disable_irq_nosync(irq);
+ irq_desc[irq].handler = &systemh_irq_type;
+ disable_systemh_irq(irq);
+}
+
--- /dev/null
+/*
+ * linux/arch/sh/boards/systemh/setup.c
+ *
+ * Copyright (C) 2000 Kazumoto Kojima
+ * Copyright (C) 2003 Paul Mundt
+ *
+ * Hitachi SystemH Support.
+ *
+ * Modified for 7751 SystemH by Jonathan Short.
+ *
+ * Rewritten for 2.6 by Paul Mundt.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <asm/mach/7751systemh.h>
+#include <asm/mach/io.h>
+#include <asm/machvec.h>
+
+extern void make_systemh_irq(unsigned int irq);
+
+const char *get_system_type(void)
+{
+ return "7751 SystemH";
+}
+
+/*
+ * Initialize IRQ setting
+ */
+void __init init_7751systemh_IRQ(void)
+{
+/* make_ipr_irq(10, BCR_ILCRD, 1, 0x0f-10); LAN */
+/* make_ipr_irq(14, BCR_ILCRA, 2, 0x0f-4); */
+ make_systemh_irq(0xb); /* Ethernet interrupt */
+}
+
+struct sh_machine_vector mv_7751systemh __initmv = {
+ .mv_nr_irqs = 72,
+
+ .mv_inb = sh7751systemh_inb,
+ .mv_inw = sh7751systemh_inw,
+ .mv_inl = sh7751systemh_inl,
+ .mv_outb = sh7751systemh_outb,
+ .mv_outw = sh7751systemh_outw,
+ .mv_outl = sh7751systemh_outl,
+
+ .mv_inb_p = sh7751systemh_inb_p,
+ .mv_inw_p = sh7751systemh_inw,
+ .mv_inl_p = sh7751systemh_inl,
+ .mv_outb_p = sh7751systemh_outb_p,
+ .mv_outw_p = sh7751systemh_outw,
+ .mv_outl_p = sh7751systemh_outl,
+
+ .mv_insb = sh7751systemh_insb,
+ .mv_insw = sh7751systemh_insw,
+ .mv_insl = sh7751systemh_insl,
+ .mv_outsb = sh7751systemh_outsb,
+ .mv_outsw = sh7751systemh_outsw,
+ .mv_outsl = sh7751systemh_outsl,
+
+ .mv_readb = sh7751systemh_readb,
+ .mv_readw = sh7751systemh_readw,
+ .mv_readl = sh7751systemh_readl,
+ .mv_writeb = sh7751systemh_writeb,
+ .mv_writew = sh7751systemh_writew,
+ .mv_writel = sh7751systemh_writel,
+
+ .mv_isa_port2addr = sh7751systemh_isa_port2addr,
+
+ .mv_init_irq = init_7751systemh_IRQ,
+};
+ALIAS_MV(7751systemh)
+
+int __init platform_setup(void)
+{
+ return 0;
+}
+
--- /dev/null
+/*
+ * linux/arch/sh/boards/se/7300/io.c
+ *
+ * Copyright (C) 2003 YOSHII Takashi <yoshii-takashi@hitachi-ul.co.jp>
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <asm/se7300/se7300.h>
+#include <asm/io.h>
+
+#define badio(fn, a) panic("bad i/o operation %s for %08lx.", #fn, a)
+
+struct iop {
+ unsigned long start, end;
+ unsigned long base;
+ struct iop *(*check) (struct iop * p, unsigned long port);
+ unsigned char (*inb) (struct iop * p, unsigned long port);
+ unsigned short (*inw) (struct iop * p, unsigned long port);
+ void (*outb) (struct iop * p, unsigned char value, unsigned long port);
+ void (*outw) (struct iop * p, unsigned short value, unsigned long port);
+};
+
+struct iop *
+simple_check(struct iop *p, unsigned long port)
+{
+ if ((p->start <= port) && (port <= p->end))
+ return p;
+ else
+ badio(check, port);
+}
+
+struct iop *
+ide_check(struct iop *p, unsigned long port)
+{
+ if (((0x1f0 <= port) && (port <= 0x1f7)) || (port == 0x3f7))
+ return p;
+ return NULL;
+}
+
+unsigned char
+simple_inb(struct iop *p, unsigned long port)
+{
+ return *(unsigned char *) (p->base + port);
+}
+
+unsigned short
+simple_inw(struct iop *p, unsigned long port)
+{
+ return *(unsigned short *) (p->base + port);
+}
+
+void
+simple_outb(struct iop *p, unsigned char value, unsigned long port)
+{
+ *(unsigned char *) (p->base + port) = value;
+}
+
+void
+simple_outw(struct iop *p, unsigned short value, unsigned long port)
+{
+ *(unsigned short *) (p->base + port) = value;
+}
+
+unsigned char
+pcc_inb(struct iop *p, unsigned long port)
+{
+ unsigned long addr = p->base + port + 0x40000;
+ unsigned long v;
+
+ if (port & 1)
+ addr += 0x00400000;
+ v = *(volatile unsigned char *) addr;
+ return v;
+}
+
+void
+pcc_outb(struct iop *p, unsigned char value, unsigned long port)
+{
+ unsigned long addr = p->base + port + 0x40000;
+
+ if (port & 1)
+ addr += 0x00400000;
+ *(volatile unsigned char *) addr = value;
+}
+
+unsigned char
+bad_inb(struct iop *p, unsigned long port)
+{
+ badio(inb, port);
+}
+
+void
+bad_outb(struct iop *p, unsigned char value, unsigned long port)
+{
+ badio(inw, port);
+}
+
+/* MSTLANEX01 LAN at 0xb400:0000 */
+static struct iop laniop = {
+ .start = 0x300,
+ .end = 0x30f,
+ .base = 0xb4000000,
+ .check = simple_check,
+ .inb = simple_inb,
+ .inw = simple_inw,
+ .outb = simple_outb,
+ .outw = simple_outw,
+};
+
+/* NE2000 pc card NIC */
+static struct iop neiop = {
+ .start = 0x280,
+ .end = 0x29f,
+ .base = 0xb0600000 + 0x80, /* soft 0x280 -> hard 0x300 */
+ .check = simple_check,
+ .inb = pcc_inb,
+ .inw = simple_inw,
+ .outb = pcc_outb,
+ .outw = simple_outw,
+};
+
+/* CF in CF slot */
+static struct iop cfiop = {
+ .base = 0xb0600000,
+ .check = ide_check,
+ .inb = pcc_inb,
+ .inw = simple_inw,
+ .outb = pcc_outb,
+ .outw = simple_outw,
+};
+
+static __inline__ struct iop *
+port2iop(unsigned long port)
+{
+ if (0) ;
+#if defined(CONFIG_SMC91111)
+ else if (laniop.check(&laniop, port))
+ return &laniop;
+#endif
+#if defined(CONFIG_NE2000)
+ else if (neiop.check(&neiop, port))
+ return &neiop;
+#endif
+#if defined(CONFIG_IDE)
+ else if (cfiop.check(&cfiop, port))
+ return &cfiop;
+#endif
+ else
+ return &neiop; /* fallback */
+}
+
+static inline void
+delay(void)
+{
+ ctrl_inw(0xac000000);
+ ctrl_inw(0xac000000);
+}
+
+unsigned char
+sh7300se_inb(unsigned long port)
+{
+ struct iop *p = port2iop(port);
+ return (p->inb) (p, port);
+}
+
+unsigned char
+sh7300se_inb_p(unsigned long port)
+{
+ unsigned char v = sh7300se_inb(port);
+ delay();
+ return v;
+}
+
+unsigned short
+sh7300se_inw(unsigned long port)
+{
+ struct iop *p = port2iop(port);
+ return (p->inw) (p, port);
+}
+
+unsigned int
+sh7300se_inl(unsigned long port)
+{
+ badio(inl, port);
+}
+
+void
+sh7300se_outb(unsigned char value, unsigned long port)
+{
+ struct iop *p = port2iop(port);
+ (p->outb) (p, value, port);
+}
+
+void
+sh7300se_outb_p(unsigned char value, unsigned long port)
+{
+ sh7300se_outb(value, port);
+ delay();
+}
+
+void
+sh7300se_outw(unsigned short value, unsigned long port)
+{
+ struct iop *p = port2iop(port);
+ (p->outw) (p, value, port);
+}
+
+void
+sh7300se_outl(unsigned int value, unsigned long port)
+{
+ badio(outl, port);
+}
+
+void
+sh7300se_insb(unsigned long port, void *addr, unsigned long count)
+{
+ unsigned char *a = addr;
+ struct iop *p = port2iop(port);
+ while (count--)
+ *a++ = (p->inb) (p, port);
+}
+
+void
+sh7300se_insw(unsigned long port, void *addr, unsigned long count)
+{
+ unsigned short *a = addr;
+ struct iop *p = port2iop(port);
+ while (count--)
+ *a++ = (p->inw) (p, port);
+}
+
+void
+sh7300se_insl(unsigned long port, void *addr, unsigned long count)
+{
+ badio(insl, port);
+}
+
+void
+sh7300se_outsb(unsigned long port, const void *addr, unsigned long count)
+{
+ unsigned char *a = (unsigned char *) addr;
+ struct iop *p = port2iop(port);
+ while (count--)
+ (p->outb) (p, *a++, port);
+}
+
+void
+sh7300se_outsw(unsigned long port, const void *addr, unsigned long count)
+{
+ unsigned short *a = (unsigned short *) addr;
+ struct iop *p = port2iop(port);
+ while (count--)
+ (p->outw) (p, *a++, port);
+}
+
+void
+sh7300se_outsl(unsigned long port, const void *addr, unsigned long count)
+{
+ badio(outsw, port);
+}
--- /dev/null
+/*
+ * linux/arch/sh/boards/se/7300/irq.c
+ *
+ * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp>
+ *
+ * SH-Mobile SolutionEngine 7300 Support.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/mach/se7300.h>
+
+/*
+ * Initialize IRQ setting
+ */
+void __init
+init_7300se_IRQ(void)
+{
+ ctrl_outw(0x0028, PA_EPLD_MODESET); /* mode set IRQ0,1 active low. */
+ ctrl_outw(0xa000, INTC_ICR1); /* IRQ mode; IRQ0,1 enable. */
+ ctrl_outw(0x0000, PORT_PFCR); /* use F for IRQ[3:0] and SIU. */
+
+ /* PC_IRQ[0-3] -> IRQ0 (32) */
+ make_ipr_irq(IRQ0_IRQ, IRQ0_IPR_ADDR, IRQ0_IPR_POS, 0x0f - IRQ0_IRQ);
+ /* A_IRQ[0-3] -> IRQ1 (33) */
+ make_ipr_irq(IRQ1_IRQ, IRQ1_IPR_ADDR, IRQ1_IPR_POS, 0x0f - IRQ1_IRQ);
+ make_ipr_irq(SIOF0_IRQ, SIOF0_IPR_ADDR, SIOF0_IPR_POS, SIOF0_PRIORITY);
+ make_ipr_irq(DMTE2_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY);
+ make_ipr_irq(DMTE3_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY);
+ make_ipr_irq(VIO_IRQ, VIO_IPR_ADDR, VIO_IPR_POS, VIO_PRIORITY);
+
+ ctrl_outw(0x2000, PA_MRSHPC + 0x0c); /* mrshpc irq enable */
+}
--- /dev/null
+/*
+ * linux/arch/sh/boards/se/7300/led.c
+ *
+ * Derived from linux/arch/sh/boards/se/770x/led.c
+ *
+ * Copyright (C) 2000 Stuart Menefy <stuart.menefy@st.com>
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * This file contains Solution Engine specific LED code.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <asm/mach/se7300.h>
+
+static void
+mach_led(int position, int value)
+{
+ volatile unsigned short *p = (volatile unsigned short *) PA_LED;
+
+ if (value) {
+ *p |= (1 << 8);
+ } else {
+ *p &= ~(1 << 8);
+ }
+}
+
+
+/* Cycle the LED's in the clasic Knightrider/Sun pattern */
+void
+heartbeat_7300se(void)
+{
+ static unsigned int cnt = 0, period = 0;
+ volatile unsigned short *p = (volatile unsigned short *) PA_LED;
+ static unsigned bit = 0, up = 1;
+
+ cnt += 1;
+ if (cnt < period) {
+ return;
+ }
+
+ cnt = 0;
+
+ /* Go through the points (roughly!):
+ * f(0)=10, f(1)=16, f(2)=20, f(5)=35,f(inf)->110
+ */
+ period = 110 - ((300 << FSHIFT) / ((avenrun[0] / 5) + (3 << FSHIFT)));
+
+ if (up) {
+ if (bit == 7) {
+ bit--;
+ up = 0;
+ } else {
+ bit++;
+ }
+ } else {
+ if (bit == 0) {
+ bit++;
+ up = 1;
+ } else {
+ bit--;
+ }
+ }
+ *p = 1 << (bit + 8);
+
+}
+
--- /dev/null
+/*
+ * linux/arch/sh/boards/se/7300/setup.c
+ *
+ * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp>
+ *
+ * SH-Mobile SolutionEngine 7300 Support.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <asm/machvec.h>
+#include <asm/machvec_init.h>
+#include <asm/mach/io.h>
+
+void heartbeat_7300se(void);
+void init_7300se_IRQ(void);
+
+const char *
+get_system_type(void)
+{
+ return "SolutionEngine 7300";
+}
+
+/*
+ * The Machine Vector
+ */
+
+struct sh_machine_vector mv_7300se __initmv = {
+ .mv_nr_irqs = 109,
+ .mv_inb = sh7300se_inb,
+ .mv_inw = sh7300se_inw,
+ .mv_inl = sh7300se_inl,
+ .mv_outb = sh7300se_outb,
+ .mv_outw = sh7300se_outw,
+ .mv_outl = sh7300se_outl,
+
+ .mv_inb_p = sh7300se_inb_p,
+ .mv_inw_p = sh7300se_inw,
+ .mv_inl_p = sh7300se_inl,
+ .mv_outb_p = sh7300se_outb_p,
+ .mv_outw_p = sh7300se_outw,
+ .mv_outl_p = sh7300se_outl,
+
+ .mv_insb = sh7300se_insb,
+ .mv_insw = sh7300se_insw,
+ .mv_insl = sh7300se_insl,
+ .mv_outsb = sh7300se_outsb,
+ .mv_outsw = sh7300se_outsw,
+ .mv_outsl = sh7300se_outsl,
+
+ .mv_init_irq = init_7300se_IRQ,
+#ifdef CONFIG_HEARTBEAT
+ .mv_heartbeat = heartbeat_7300se,
+#endif
+};
+
+ALIAS_MV(7300se)
+/*
+ * Initialize the board
+ */
+void __init
+platform_setup(void)
+{
+
+}
--- /dev/null
+/* -------------------------------------------------------------------- */
+/* setup_voyagergx.c: */
+/* -------------------------------------------------------------------- */
+/* This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Copyright 2003 (c) Lineo uSolutions,Inc.
+*/
+/* -------------------------------------------------------------------- */
+
+#undef DEBUG
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/rts7751r2d/rts7751r2d.h>
+#include <asm/rts7751r2d/voyagergx_reg.h>
+
+static void disable_voyagergx_irq(unsigned int irq)
+{
+ unsigned long flags, val;
+ unsigned long mask = 1 << (irq - VOYAGER_IRQ_BASE);
+
+ pr_debug("disable_voyagergx_irq(%d): mask=%x\n", irq, mask);
+ local_irq_save(flags);
+ val = inl(VOYAGER_INT_MASK);
+ val &= ~mask;
+ outl(val, VOYAGER_INT_MASK);
+ local_irq_restore(flags);
+}
+
+
+static void enable_voyagergx_irq(unsigned int irq)
+{
+ unsigned long flags, val;
+ unsigned long mask = 1 << (irq - VOYAGER_IRQ_BASE);
+
+ pr_debug("disable_voyagergx_irq(%d): mask=%x\n", irq, mask);
+ local_irq_save(flags);
+ val = inl(VOYAGER_INT_MASK);
+ val |= mask;
+ outl(val, VOYAGER_INT_MASK);
+ local_irq_restore(flags);
+}
+
+
+static void mask_and_ack_voyagergx(unsigned int irq)
+{
+ disable_voyagergx_irq(irq);
+}
+
+static void end_voyagergx_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ enable_voyagergx_irq(irq);
+}
+
+static unsigned int startup_voyagergx_irq(unsigned int irq)
+{
+ enable_voyagergx_irq(irq);
+ return 0;
+}
+
+static void shutdown_voyagergx_irq(unsigned int irq)
+{
+ disable_voyagergx_irq(irq);
+}
+
+static struct hw_interrupt_type voyagergx_irq_type = {
+ "VOYAGERGX-IRQ",
+ startup_voyagergx_irq,
+ shutdown_voyagergx_irq,
+ enable_voyagergx_irq,
+ disable_voyagergx_irq,
+ mask_and_ack_voyagergx,
+ end_voyagergx_irq,
+};
+
+static irqreturn_t voyagergx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ printk(KERN_INFO
+ "VoyagerGX: spurious interrupt, status: 0x%x\n",
+ inl(INT_STATUS));
+ return IRQ_HANDLED;
+}
+
+
+/*====================================================*/
+
+static struct {
+ int (*func)(int, void *);
+ void *dev;
+} voyagergx_demux[VOYAGER_IRQ_NUM];
+
+void voyagergx_register_irq_demux(int irq,
+ int (*demux)(int irq, void *dev), void *dev)
+{
+ voyagergx_demux[irq - VOYAGER_IRQ_BASE].func = demux;
+ voyagergx_demux[irq - VOYAGER_IRQ_BASE].dev = dev;
+}
+
+void voyagergx_unregister_irq_demux(int irq)
+{
+ voyagergx_demux[irq - VOYAGER_IRQ_BASE].func = 0;
+}
+
+int voyagergx_irq_demux(int irq)
+{
+
+ if (irq == IRQ_VOYAGER ) {
+ unsigned long i = 0, bit __attribute__ ((unused));
+ unsigned long val = inl(INT_STATUS);
+#if 1
+ if ( val & ( 1 << 1 )){
+ i = 1;
+ } else if ( val & ( 1 << 2 )){
+ i = 2;
+ } else if ( val & ( 1 << 6 )){
+ i = 6;
+ } else if( val & ( 1 << 10 )){
+ i = 10;
+ } else if( val & ( 1 << 11 )){
+ i = 11;
+ } else if( val & ( 1 << 12 )){
+ i = 12;
+ } else if( val & ( 1 << 17 )){
+ i = 17;
+ } else {
+ printk("Unexpected IRQ irq = %d status = 0x%08lx\n", irq, val);
+ }
+ pr_debug("voyagergx_irq_demux %d \n", i);
+#else
+ for (bit = 1, i = 0 ; i < VOYAGER_IRQ_NUM ; bit <<= 1, i++)
+ if (val & bit)
+ break;
+#endif
+ if (i < VOYAGER_IRQ_NUM) {
+ irq = VOYAGER_IRQ_BASE + i;
+ if (voyagergx_demux[i].func != 0)
+ irq = voyagergx_demux[i].func(irq, voyagergx_demux[i].dev);
+ }
+ }
+ return irq;
+}
+
+static struct irqaction irq0 = { voyagergx_interrupt, SA_INTERRUPT, 0, "VOYAGERGX", NULL, NULL};
+
+void __init setup_voyagergx_irq(void)
+{
+ int i, flag;
+
+ printk(KERN_INFO "VoyagerGX configured at 0x%x on irq %d(mapped into %d to %d)\n",
+ VOYAGER_BASE,
+ IRQ_VOYAGER,
+ VOYAGER_IRQ_BASE,
+ VOYAGER_IRQ_BASE + VOYAGER_IRQ_NUM - 1);
+
+ for (i=0; i<VOYAGER_IRQ_NUM; i++) {
+ flag = 0;
+ switch (VOYAGER_IRQ_BASE + i) {
+ case VOYAGER_USBH_IRQ:
+ case VOYAGER_8051_IRQ:
+ case VOYAGER_UART0_IRQ:
+ case VOYAGER_UART1_IRQ:
+ case VOYAGER_AC97_IRQ:
+ flag = 1;
+ }
+ if (flag == 1)
+ irq_desc[VOYAGER_IRQ_BASE + i].handler = &voyagergx_irq_type;
+ }
+
+ setup_irq(IRQ_VOYAGER, &irq0);
+}
+
--- /dev/null
+/*
+ * arch/sh/cchips/voyagergx/setup.c
+ *
+ * Setup routines for VoyagerGX cchip.
+ *
+ * Copyright (C) 2003 Lineo uSolutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <asm/io.h>
+#include <asm/rts7751r2d/voyagergx_reg.h>
+
+static int __init setup_voyagergx(void)
+{
+ unsigned long val;
+
+ val = inl(DRAM_CTRL);
+ val |= (DRAM_CTRL_CPU_COLUMN_SIZE_256 |
+ DRAM_CTRL_CPU_ACTIVE_PRECHARGE |
+ DRAM_CTRL_CPU_RESET |
+ DRAM_CTRL_REFRESH_COMMAND |
+ DRAM_CTRL_BLOCK_WRITE_TIME |
+ DRAM_CTRL_BLOCK_WRITE_PRECHARGE |
+ DRAM_CTRL_ACTIVE_PRECHARGE |
+ DRAM_CTRL_RESET |
+ DRAM_CTRL_REMAIN_ACTIVE);
+ outl(val, DRAM_CTRL);
+
+ return 0;
+}
+
+module_init(setup_voyagergx);
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
--- /dev/null
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_SUPERH=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_STANDALONE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+# CONFIG_SWAP is not set
+# CONFIG_SYSVIPC is not set
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+# CONFIG_AUDIT is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_HOTPLUG is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_EMBEDDED=y
+# CONFIG_KALLSYMS is not set
+# CONFIG_FUTEX is not set
+# CONFIG_EPOLL is not set
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_AS is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+
+#
+# Loadable module support
+#
+# CONFIG_MODULES is not set
+
+#
+# System type
+#
+# CONFIG_SH_SOLUTION_ENGINE is not set
+# CONFIG_SH_7751_SOLUTION_ENGINE is not set
+CONFIG_SH_7300_SOLUTION_ENGINE=y
+# CONFIG_SH_7751_SYSTEMH is not set
+# CONFIG_SH_STB1_HARP is not set
+# CONFIG_SH_STB1_OVERDRIVE is not set
+# CONFIG_SH_HP620 is not set
+# CONFIG_SH_HP680 is not set
+# CONFIG_SH_HP690 is not set
+# CONFIG_SH_CQREEK is not set
+# CONFIG_SH_DMIDA is not set
+# CONFIG_SH_EC3104 is not set
+# CONFIG_SH_SATURN is not set
+# CONFIG_SH_DREAMCAST is not set
+# CONFIG_SH_CAT68701 is not set
+# CONFIG_SH_BIGSUR is not set
+# CONFIG_SH_SH2000 is not set
+# CONFIG_SH_ADX is not set
+# CONFIG_SH_MPC1211 is not set
+# CONFIG_SH_SECUREEDGE5410 is not set
+# CONFIG_SH_HS7751RVOIP is not set
+# CONFIG_SH_RTS7751R2D is not set
+# CONFIG_SH_UNKNOWN is not set
+# CONFIG_CPU_SH2 is not set
+CONFIG_CPU_SH3=y
+# CONFIG_CPU_SH4 is not set
+# CONFIG_CPU_SUBTYPE_SH7604 is not set
+CONFIG_CPU_SUBTYPE_SH7300=y
+# CONFIG_CPU_SUBTYPE_SH7707 is not set
+# CONFIG_CPU_SUBTYPE_SH7708 is not set
+# CONFIG_CPU_SUBTYPE_SH7709 is not set
+# CONFIG_CPU_SUBTYPE_SH7750 is not set
+# CONFIG_CPU_SUBTYPE_SH7751 is not set
+# CONFIG_CPU_SUBTYPE_SH7760 is not set
+# CONFIG_CPU_SUBTYPE_ST40STB1 is not set
+# CONFIG_CPU_SUBTYPE_ST40GX1 is not set
+CONFIG_MMU=y
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttySC0,38400 root=/dev/ram0"
+CONFIG_MEMORY_START=0x0c000000
+CONFIG_MEMORY_SIZE=0x04000000
+# CONFIG_MEMORY_OVERRIDE is not set
+CONFIG_SH_DSP=y
+# CONFIG_SH_ADC is not set
+CONFIG_ZERO_PAGE_OFFSET=0x00001000
+CONFIG_BOOT_LINK_OFFSET=0x00210000
+CONFIG_CPU_LITTLE_ENDIAN=y
+# CONFIG_PREEMPT is not set
+# CONFIG_UBC_WAKEUP is not set
+# CONFIG_SH_WRITETHROUGH is not set
+# CONFIG_SH_OCRAM is not set
+# CONFIG_SMP is not set
+# CONFIG_SH_PCLK_CALC is not set
+CONFIG_SH_PCLK_FREQ=33333333
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# DMA support
+#
+# CONFIG_SH_DMA is not set
+
+#
+# Companion Chips
+#
+# CONFIG_HD6446X_SERIES is not set
+CONFIG_HEARTBEAT=y
+
+#
+# Bus options (PCI, PCMCIA, EISA, MCA, ISA)
+#
+# CONFIG_PCI is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_FLAT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# SH initrd options
+#
+CONFIG_EMBEDDED_RAMDISK=y
+CONFIG_EMBEDDED_RAMDISK_IMAGE="ramdisk.gz"
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Plug and Play support
+#
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_LOOP is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_LBD is not set
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_SCSI is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+
+#
+# Fusion MPT device support
+#
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_IEEE1394 is not set
+
+#
+# I2O device support
+#
+
+#
+# Networking support
+#
+# CONFIG_NET is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+
+#
+# ISDN subsystem
+#
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_PSAUX=y
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_TSDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input I/O drivers
+#
+# CONFIG_GAMEPORT is not set
+CONFIG_SOUND_GAMEPORT=y
+CONFIG_SERIO=y
+# CONFIG_SERIO_I8042 is not set
+# CONFIG_SERIO_SERPORT is not set
+# CONFIG_SERIO_CT82C710 is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_SH_SCI=y
+CONFIG_SERIAL_SH_SCI_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_UNIX98_PTYS is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_QIC02_TAPE is not set
+
+#
+# IPMI
+#
+CONFIG_IPMI_HANDLER=y
+# CONFIG_IPMI_PANIC_EVENT is not set
+CONFIG_IPMI_DEVICE_INTERFACE=y
+# CONFIG_IPMI_SI is not set
+CONFIG_IPMI_WATCHDOG=y
+
+#
+# Watchdog Cards
+#
+CONFIG_WATCHDOG=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+
+#
+# Watchdog Device Drivers
+#
+CONFIG_SOFT_WATCHDOG=y
+# CONFIG_SH_WDT is not set
+# CONFIG_RTC is not set
+# CONFIG_GEN_RTC is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+# CONFIG_AGP is not set
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Misc devices
+#
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Digital Video Broadcasting Devices
+#
+
+#
+# Graphics support
+#
+# CONFIG_FB is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_JBD is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_FAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_SYSFS=y
+CONFIG_DEVFS_FS=y
+CONFIG_DEVFS_MOUNT=y
+# CONFIG_DEVFS_DEBUG is not set
+# CONFIG_TMPFS is not set
+# CONFIG_HUGETLBFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+# CONFIG_NLS is not set
+
+#
+# Profiling support
+#
+# CONFIG_PROFILING is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_INFO is not set
+CONFIG_SH_STANDARD_BIOS=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_KGDB=y
+
+#
+# KGDB configuration options
+#
+# CONFIG_MORE_COMPILE_OPTIONS is not set
+# CONFIG_KGDB_NMI is not set
+# CONFIG_KGDB_THREAD is not set
+# CONFIG_SH_KGDB_CONSOLE is not set
+# CONFIG_KGDB_SYSRQ is not set
+# CONFIG_KGDB_KERNEL_ASSERTS is not set
+
+#
+# Serial port setup
+#
+CONFIG_KGDB_DEFPORT=1
+CONFIG_KGDB_DEFBAUD=115200
+CONFIG_KGDB_DEFPARITY_N=y
+# CONFIG_KGDB_DEFPARITY_E is not set
+# CONFIG_KGDB_DEFPARITY_O is not set
+CONFIG_KGDB_DEFBITS_8=y
+# CONFIG_KGDB_DEFBITS_7 is not set
+# CONFIG_FRAME_POINTER is not set
+
+#
+# Security options
+#
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+# CONFIG_CRYPTO is not set
+
+#
+# Library routines
+#
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
--- /dev/null
+/*
+ * arch/sh/drivers/dma/dma-sysfs.c
+ *
+ * sysfs interface for SH DMA API
+ *
+ * Copyright (C) 2004 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sysdev.h>
+#include <linux/module.h>
+#include <asm/dma.h>
+
+static struct sysdev_class dma_sysclass = {
+ set_kset_name("dma"),
+};
+
+EXPORT_SYMBOL(dma_sysclass);
+
+static ssize_t dma_show_devices(struct sys_device *dev, char *buf)
+{
+ ssize_t len = 0;
+ int i;
+
+ for (i = 0; i < MAX_DMA_CHANNELS; i++) {
+ struct dma_info *info = get_dma_info(i);
+ struct dma_channel *channel = &info->channels[i];
+
+ len += sprintf(buf + len, "%2d: %14s %s\n",
+ channel->chan, info->name,
+ channel->dev_id);
+ }
+
+ return len;
+}
+
+static SYSDEV_ATTR(devices, S_IRUGO, dma_show_devices, NULL);
+
+static int __init dma_sysclass_init(void)
+{
+ int ret;
+
+ ret = sysdev_class_register(&dma_sysclass);
+ if (ret == 0)
+ sysfs_create_file(&dma_sysclass.kset.kobj, &attr_devices.attr);
+
+ return ret;
+}
+
+postcore_initcall(dma_sysclass_init);
+
+static ssize_t dma_show_dev_id(struct sys_device *dev, char *buf)
+{
+ struct dma_channel *channel = to_dma_channel(dev);
+ return sprintf(buf, "%s\n", channel->dev_id);
+}
+
+static ssize_t dma_store_dev_id(struct sys_device *dev,
+ const char *buf, size_t count)
+{
+ struct dma_channel *channel = to_dma_channel(dev);
+ strcpy(channel->dev_id, buf);
+ return count;
+}
+
+static SYSDEV_ATTR(dev_id, S_IRUGO | S_IWUSR, dma_show_dev_id, dma_store_dev_id);
+
+static ssize_t dma_store_config(struct sys_device *dev,
+ const char *buf, size_t count)
+{
+ struct dma_channel *channel = to_dma_channel(dev);
+ unsigned long config;
+
+ config = simple_strtoul(buf, NULL, 0);
+ dma_configure_channel(channel->chan, config);
+
+ return count;
+}
+
+static SYSDEV_ATTR(config, S_IWUSR, NULL, dma_store_config);
+
+static ssize_t dma_show_mode(struct sys_device *dev, char *buf)
+{
+ struct dma_channel *channel = to_dma_channel(dev);
+ return sprintf(buf, "0x%08x\n", channel->mode);
+}
+
+static ssize_t dma_store_mode(struct sys_device *dev,
+ const char *buf, size_t count)
+{
+ struct dma_channel *channel = to_dma_channel(dev);
+ channel->mode = simple_strtoul(buf, NULL, 0);
+ return count;
+}
+
+static SYSDEV_ATTR(mode, S_IRUGO | S_IWUSR, dma_show_mode, dma_store_mode);
+
+#define dma_ro_attr(field, fmt) \
+static ssize_t dma_show_##field(struct sys_device *dev, char *buf) \
+{ \
+ struct dma_channel *channel = to_dma_channel(dev); \
+ return sprintf(buf, fmt, channel->field); \
+} \
+static SYSDEV_ATTR(field, S_IRUGO, dma_show_##field, NULL);
+
+dma_ro_attr(count, "0x%08x\n");
+dma_ro_attr(flags, "0x%08lx\n");
+
+int __init dma_create_sysfs_files(struct dma_channel *chan)
+{
+ struct sys_device *dev = &chan->dev;
+ int ret;
+
+ dev->id = chan->chan;
+ dev->cls = &dma_sysclass;
+
+ ret = sysdev_register(dev);
+ if (ret)
+ return ret;
+
+ sysdev_create_file(dev, &attr_dev_id);
+ sysdev_create_file(dev, &attr_count);
+ sysdev_create_file(dev, &attr_mode);
+ sysdev_create_file(dev, &attr_flags);
+ sysdev_create_file(dev, &attr_config);
+
+ return 0;
+}
+
--- /dev/null
+/*
+ * arch/sh/drivers/pci/fixups-rts7751r2d.c
+ *
+ * RTS7751R2D PCI fixups
+ *
+ * Copyright (C) 2003 Lineo uSolutions, Inc.
+ * Copyright (C) 2004 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include "pci-sh7751.h"
+#include <asm/io.h>
+
+#define PCIMCR_MRSET_OFF 0xBFFFFFFF
+#define PCIMCR_RFSH_OFF 0xFFFFFFFB
+
+int pci_fixup_pcic(void)
+{
+ unsigned long mcr;
+
+ outl(0xfb900047, SH7751_PCICONF1);
+ outl(0xab000001, SH7751_PCICONF4);
+
+ mcr = inl(SH7751_MCR);
+ mcr = (mcr & PCIMCR_MRSET_OFF) & PCIMCR_RFSH_OFF;
+ outl(mcr, SH7751_PCIMCR);
+
+ return 0;
+}
+
--- /dev/null
+/*
+ * linux/arch/sh/kernel/pci-rts7751r2d.c
+ *
+ * Author: Ian DaSilva (idasilva@mvista.com)
+ *
+ * Highly leveraged from pci-bigsur.c, written by Dustin McIntire.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * PCI initialization for the Renesas SH7751R RTS7751R2D board
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/module.h>
+
+#include <asm/io.h>
+#include "pci-sh7751.h"
+#include <asm/rts7751r2d/rts7751r2d.h>
+
+int __init pcibios_map_platform_irq(u8 slot, u8 pin)
+{
+ switch (slot) {
+ case 0: return IRQ_PCISLOT1; /* PCI Extend slot #1 */
+ case 1: return IRQ_PCISLOT2; /* PCI Extend slot #2 */
+ case 2: return IRQ_PCMCIA; /* PCI Cardbus Bridge */
+ case 3: return IRQ_PCIETH; /* Realtek Ethernet controller */
+ default:
+ printk("PCI: Bad IRQ mapping request for slot %d\n", slot);
+ return -1;
+ }
+}
+
+static struct resource sh7751_io_resource = {
+ .name = "SH7751_IO",
+ .start = 0x4000,
+ .end = 0x4000 + SH7751_PCI_IO_SIZE - 1,
+ .flags = IORESOURCE_IO
+};
+
+static struct resource sh7751_mem_resource = {
+ .name = "SH7751_mem",
+ .start = SH7751_PCI_MEMORY_BASE,
+ .end = SH7751_PCI_MEMORY_BASE + SH7751_PCI_MEM_SIZE - 1,
+ .flags = IORESOURCE_MEM
+};
+
+extern struct pci_ops sh7751_pci_ops;
+
+struct pci_channel board_pci_channels[] = {
+ { &sh7751_pci_ops, &sh7751_io_resource, &sh7751_mem_resource, 0, 0xff },
+ { NULL, NULL, NULL, 0, 0 },
+};
+EXPORT_SYMBOL(board_pci_channels);
+
+static struct sh7751_pci_address_map sh7751_pci_map = {
+ .window0 = {
+ .base = SH7751_CS3_BASE_ADDR,
+ .size = 0x03f00000,
+ },
+
+ .flags = SH7751_PCIC_NO_RESET,
+};
+
+int __init pcibios_init_platform(void)
+{
+ return sh7751_pcic_init(&sh7751_pci_map);
+}
+
--- /dev/null
+/*
+ * arch/sh/kernel/cpu/bus.c
+ *
+ * Virtual bus for SuperH.
+ *
+ * Copyright (C) 2004 Paul Mundt
+ *
+ * Shamelessly cloned from arch/arm/mach-omap/bus.c, which was written
+ * by:
+ *
+ * Copyright (C) 2003 - 2004 Nokia Corporation
+ * Written by Tony Lindgren <tony@atomide.com>
+ * Portions of code based on sa1111.c.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <asm/bus-sh.h>
+
+static int sh_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct sh_driver *shdrv = to_sh_driver(drv);
+ struct sh_dev *shdev = to_sh_dev(dev);
+
+ return shdev->dev_id == shdrv->dev_id;
+}
+
+static int sh_bus_suspend(struct device *dev, u32 state)
+{
+ struct sh_dev *shdev = to_sh_dev(dev);
+ struct sh_driver *shdrv = to_sh_driver(dev->driver);
+
+ if (shdrv && shdrv->suspend)
+ return shdrv->suspend(shdev, state);
+
+ return 0;
+}
+
+static int sh_bus_resume(struct device *dev)
+{
+ struct sh_dev *shdev = to_sh_dev(dev);
+ struct sh_driver *shdrv = to_sh_driver(dev->driver);
+
+ if (shdrv && shdrv->resume)
+ return shdrv->resume(shdev);
+
+ return 0;
+}
+
+static struct device sh_bus_devices[SH_NR_BUSES] = {
+ {
+ .bus_id = SH_BUS_NAME_VIRT,
+ },
+};
+
+struct bus_type sh_bus_types[SH_NR_BUSES] = {
+ {
+ .name = SH_BUS_NAME_VIRT,
+ .match = sh_bus_match,
+ .suspend = sh_bus_suspend,
+ .resume = sh_bus_resume,
+ },
+};
+
+static int sh_device_probe(struct device *dev)
+{
+ struct sh_dev *shdev = to_sh_dev(dev);
+ struct sh_driver *shdrv = to_sh_driver(dev->driver);
+
+ if (shdrv && shdrv->probe)
+ return shdrv->probe(shdev);
+
+ return -ENODEV;
+}
+
+static int sh_device_remove(struct device *dev)
+{
+ struct sh_dev *shdev = to_sh_dev(dev);
+ struct sh_driver *shdrv = to_sh_driver(dev->driver);
+
+ if (shdrv && shdrv->remove)
+ return shdrv->remove(shdev);
+
+ return 0;
+}
+
+int sh_device_register(struct sh_dev *dev)
+{
+ if (!dev)
+ return -EINVAL;
+
+ if (dev->bus_id < 0 || dev->bus_id >= SH_NR_BUSES) {
+ printk(KERN_ERR "%s: bus_id invalid: %s bus: %d\n",
+ __FUNCTION__, dev->name, dev->bus_id);
+ return -EINVAL;
+ }
+
+ dev->dev.parent = &sh_bus_devices[dev->bus_id];
+ dev->dev.bus = &sh_bus_types[dev->bus_id];
+
+ /* This is needed for USB OHCI to work */
+ if (dev->dma_mask)
+ dev->dev.dma_mask = dev->dma_mask;
+
+ snprintf(dev->dev.bus_id, BUS_ID_SIZE, "%s%u",
+ dev->name, dev->dev_id);
+
+ printk(KERN_INFO "Registering SH device '%s'. Parent at %s\n",
+ dev->dev.bus_id, dev->dev.parent->bus_id);
+
+ return device_register(&dev->dev);
+}
+
+void sh_device_unregister(struct sh_dev *dev)
+{
+ device_unregister(&dev->dev);
+}
+
+int sh_driver_register(struct sh_driver *drv)
+{
+ if (!drv)
+ return -EINVAL;
+
+ if (drv->bus_id < 0 || drv->bus_id >= SH_NR_BUSES) {
+ printk(KERN_ERR "%s: bus_id invalid: bus: %d device %d\n",
+ __FUNCTION__, drv->bus_id, drv->dev_id);
+ return -EINVAL;
+ }
+
+ drv->drv.probe = sh_device_probe;
+ drv->drv.remove = sh_device_remove;
+ drv->drv.bus = &sh_bus_types[drv->bus_id];
+
+ return driver_register(&drv->drv);
+}
+
+void sh_driver_unregister(struct sh_driver *drv)
+{
+ driver_unregister(&drv->drv);
+}
+
+static int __init sh_bus_init(void)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < SH_NR_BUSES; i++) {
+ ret = device_register(&sh_bus_devices[i]);
+ if (ret != 0) {
+ printk(KERN_ERR "Unable to register bus device %s\n",
+ sh_bus_devices[i].bus_id);
+ continue;
+ }
+
+ ret = bus_register(&sh_bus_types[i]);
+ if (ret != 0) {
+ printk(KERN_ERR "Unable to register bus %s\n",
+ sh_bus_types[i].name);
+ device_unregister(&sh_bus_devices[i]);
+ }
+ }
+
+ printk(KERN_INFO "SH Virtual Bus initialized\n");
+
+ return ret;
+}
+
+static void __exit sh_bus_exit(void)
+{
+ int i;
+
+ for (i = 0; i < SH_NR_BUSES; i++) {
+ bus_unregister(&sh_bus_types[i]);
+ device_unregister(&sh_bus_devices[i]);
+ }
+}
+
+module_init(sh_bus_init);
+module_exit(sh_bus_exit);
+
+MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
+MODULE_DESCRIPTION("SH Virtual Bus");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(sh_bus_types);
+EXPORT_SYMBOL(sh_device_register);
+EXPORT_SYMBOL(sh_device_unregister);
+EXPORT_SYMBOL(sh_driver_register);
+EXPORT_SYMBOL(sh_driver_unregister);
+
--- /dev/null
+/*
+ * arch/sh/kernel/early_printk.c
+ *
+ * Copyright (C) 1999, 2000 Niibe Yutaka
+ * Copyright (C) 2002 M. R. Brown
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/init.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_SH_STANDARD_BIOS
+#include <asm/sh_bios.h>
+
+/*
+ * Print a string through the BIOS
+ */
+static void sh_console_write(struct console *co, const char *s,
+ unsigned count)
+{
+ sh_bios_console_write(s, count);
+}
+
+/*
+ * Setup initial baud/bits/parity. We do two things here:
+ * - construct a cflag setting for the first rs_open()
+ * - initialize the serial port
+ * Return non-zero if we didn't find a serial port.
+ */
+static int __init sh_console_setup(struct console *co, char *options)
+{
+ int cflag = CREAD | HUPCL | CLOCAL;
+
+ /*
+ * Now construct a cflag setting.
+ * TODO: this is a totally bogus cflag, as we have
+ * no idea what serial settings the BIOS is using, or
+ * even if its using the serial port at all.
+ */
+ cflag |= B115200 | CS8 | /*no parity*/0;
+
+ co->cflag = cflag;
+
+ return 0;
+}
+
+static struct console early_console = {
+ .name = "bios",
+ .write = sh_console_write,
+ .setup = sh_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+#endif
+
+#ifdef CONFIG_EARLY_SCIF_CONSOLE
+#define SCIF_REG 0xffe80000
+
+static void scif_sercon_putc(int c)
+{
+ while (!(ctrl_inw(SCIF_REG + 0x10) & 0x20)) ;
+
+ ctrl_outb(c, SCIF_REG + 12);
+ ctrl_outw((ctrl_inw(SCIF_REG + 0x10) & 0x9f), SCIF_REG + 0x10);
+
+ if (c == '\n')
+ scif_sercon_putc('\r');
+}
+
+static void scif_sercon_flush(void)
+{
+ ctrl_outw((ctrl_inw(SCIF_REG + 0x10) & 0xbf), SCIF_REG + 0x10);
+
+ while (!(ctrl_inw(SCIF_REG + 0x10) & 0x40)) ;
+
+ ctrl_outw((ctrl_inw(SCIF_REG + 0x10) & 0xbf), SCIF_REG + 0x10);
+}
+
+static void scif_sercon_write(struct console *con, const char *s, unsigned count)
+{
+ while (count-- > 0)
+ scif_sercon_putc(*s++);
+
+ scif_sercon_flush();
+}
+
+static int __init scif_sercon_setup(struct console *con, char *options)
+{
+ con->cflag = CREAD | HUPCL | CLOCAL | B115200 | CS8;
+
+ return 0;
+}
+
+static struct console early_console = {
+ .name = "sercon",
+ .write = scif_sercon_write,
+ .setup = scif_sercon_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+
+void scif_sercon_init(int baud)
+{
+ ctrl_outw(0, SCIF_REG + 8);
+ ctrl_outw(0, SCIF_REG);
+
+ /* Set baud rate */
+ ctrl_outb((50000000 / (32 * baud)) - 1, SCIF_REG + 4);
+
+ ctrl_outw(12, SCIF_REG + 24);
+ ctrl_outw(8, SCIF_REG + 24);
+ ctrl_outw(0, SCIF_REG + 32);
+ ctrl_outw(0x60, SCIF_REG + 16);
+ ctrl_outw(0, SCIF_REG + 36);
+ ctrl_outw(0x30, SCIF_REG + 8);
+}
+#endif
+
+void __init enable_early_printk(void)
+{
+#ifdef CONFIG_EARLY_SCIF_CONSOLE
+ scif_sercon_init(115200);
+#endif
+ register_console(&early_console);
+}
+
+void disable_early_printk(void)
+{
+ unregister_console(&early_console);
+}
+
--- /dev/null
+#
+# Makefile for a ramdisk image
+#
+
+obj-y += ramdisk.o
+
+
+O_FORMAT = $(shell $(OBJDUMP) -i | head -n 2 | grep elf32)
+img := $(subst ",,$(CONFIG_EMBEDDED_RAMDISK_IMAGE))
+# add $(src) when $(img) is relative
+img := $(subst $(src)//,/,$(src)/$(img))
+
+quiet_cmd_ramdisk = LD $@
+define cmd_ramdisk
+ $(LD) -T $(src)/ld.script -b binary --oformat $(O_FORMAT) -o $@ $(img)
+endef
+
+$(obj)/ramdisk.o: $(img) $(src)/ld.script
+ $(call cmd,ramdisk)
--- /dev/null
+OUTPUT_ARCH(sh)
+SECTIONS
+{
+ .initrd :
+ {
+ *(.data)
+ }
+}
+
--- /dev/null
+#
+# For a description of the syntax of this configuration file,
+# see Documentation/kbuild/config-language.txt.
+#
+
+mainmenu "Linux/SH64 Kernel Configuration"
+
+config SUPERH
+ bool
+ default y
+
+config SUPERH64
+ bool
+ default y
+
+config MMU
+ bool
+ default y
+
+config UID16
+ bool
+ default y
+
+config RWSEM_GENERIC_SPINLOCK
+ bool
+ default y
+
+config LOG_BUF_SHIFT
+ int
+ default 14
+
+config RWSEM_XCHGADD_ALGORITHM
+ bool
+
+config GENERIC_ISA_DMA
+ bool
+
+source init/Kconfig
+
+menu "System type"
+
+choice
+ prompt "SuperH system type"
+ default SH_SIMULATOR
+
+config SH_GENERIC
+ bool "Generic"
+
+config SH_SIMULATOR
+ bool "Simulator"
+
+config SH_CAYMAN
+ bool "Cayman"
+
+config SH_ROMRAM
+ bool "ROM/RAM"
+
+config SH_HARP
+ bool "ST50-Harp"
+
+endchoice
+
+choice
+ prompt "Processor family"
+ default CPU_SH5
+
+config CPU_SH5
+ bool "SH-5"
+
+endchoice
+
+choice
+ prompt "Processor type"
+
+config CPU_SUBTYPE_SH5_101
+ bool "SH5-101"
+ depends on CPU_SH5
+
+config CPU_SUBTYPE_SH5_103
+ bool "SH5-103"
+ depends on CPU_SH5
+
+endchoice
+
+choice
+ prompt "Endianness"
+ default LITTLE_ENDIAN
+
+config LITTLE_ENDIAN
+ bool "Little-Endian"
+
+config BIG_ENDIAN
+ bool "Big-Endian"
+
+endchoice
+
+config SH64_FPU_DENORM_FLUSH
+ bool "Flush floating point denorms to zero"
+
+choice
+ prompt "Page table levels"
+ default SH64_PGTABLE_2_LEVEL
+
+config SH64_PGTABLE_2_LEVEL
+ bool "2"
+
+config SH64_PGTABLE_3_LEVEL
+ bool "3"
+
+endchoice
+
+choice
+ prompt "HugeTLB page size"
+ depends on HUGETLB_PAGE && MMU
+ default HUGETLB_PAGE_SIZE_64K
+
+config HUGETLB_PAGE_SIZE_64K
+ bool "64K"
+
+config HUGETLB_PAGE_SIZE_1MB
+ bool "1MB"
+
+config HUGETLB_PAGE_SIZE_512MB
+ bool "512MB"
+
+endchoice
+
+config SH64_USER_MISALIGNED_FIXUP
+ bool "Fixup misaligned loads/stores occurring in user mode"
+
+comment "Memory options"
+
+config CACHED_MEMORY_OFFSET
+ hex "Cached Area Offset"
+ depends on SH_HARP || SH_CAYMAN || SH_SIMULATOR
+ default "20000000"
+
+config MEMORY_START
+ hex "Physical memory start address"
+ depends on SH_HARP || SH_CAYMAN || SH_SIMULATOR
+ default "80000000"
+
+config MEMORY_SIZE_IN_MB
+ int "Memory size (in MB)" if SH_HARP || SH_CAYMAN || SH_SIMULATOR
+ default "64" if SH_HARP || SH_CAYMAN
+ default "8" if SH_SIMULATOR
+
+comment "Cache options"
+
+config DCACHE_DISABLED
+ bool "DCache Disabling"
+ depends on SH_HARP || SH_CAYMAN || SH_SIMULATOR
+
+choice
+ prompt "DCache mode"
+ depends on !DCACHE_DISABLED && !SH_SIMULATOR
+ default DCACHE_WRITE_BACK
+
+config DCACHE_WRITE_BACK
+ bool "Write-back"
+
+config DCACHE_WRITE_THROUGH
+ bool "Write-through"
+
+endchoice
+
+config ICACHE_DISABLED
+ bool "ICache Disabling"
+ depends on SH_HARP || SH_CAYMAN || SH_SIMULATOR
+
+config PCIDEVICE_MEMORY_START
+ hex
+ depends on SH_HARP || SH_CAYMAN || SH_SIMULATOR
+ default "C0000000"
+
+config DEVICE_MEMORY_START
+ hex
+ depends on SH_HARP || SH_CAYMAN || SH_SIMULATOR
+ default "E0000000"
+
+config FLASH_MEMORY_START
+ hex "Flash memory/on-chip devices start address"
+ depends on SH_HARP || SH_CAYMAN || SH_SIMULATOR
+ default "00000000"
+
+config PCI_BLOCK_START
+ hex "PCI block start address"
+ depends on SH_HARP || SH_CAYMAN || SH_SIMULATOR
+ default "40000000"
+
+comment "CPU Subtype specific options"
+
+config SH64_ID2815_WORKAROUND
+ bool "Include workaround for SH5-101 cut2 silicon defect ID2815"
+
+comment "Misc options"
+config HEARTBEAT
+ bool "Heartbeat LED"
+
+config HDSP253_LED
+ bool "Support for HDSP-253 LED"
+ depends on SH_CAYMAN
+
+config SH_DMA
+ tristate "DMA controller (DMAC) support"
+
+config PREEMPT
+ bool "Preemptible Kernel (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+
+endmenu
+
+menu "Bus options (PCI, PCMCIA, EISA, MCA, ISA)"
+
+config ISA
+ bool
+
+config SBUS
+ bool
+
+config PCI
+ bool "PCI support"
+ help
+ Find out whether you have a PCI motherboard. PCI is the name of a
+ bus system, i.e. the way the CPU talks to the other stuff inside
+ your box. Other bus systems are ISA, EISA, MicroChannel (MCA) or
+ VESA. If you have PCI, say Y, otherwise N.
+
+ The PCI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>, contains valuable
+ information about which PCI hardware does work under Linux and which
+ doesn't.
+
+config SH_PCIDMA_NONCOHERENT
+ bool "Cache and PCI noncoherent"
+ depends on PCI
+ default y
+ help
+ Enable this option if your platform does not have a CPU cache which
+ remains coherent with PCI DMA. It is safest to say 'Y', although you
+ will see better performance if you can say 'N', because the PCI DMA
+ code will not have to flush the CPU's caches. If you have a PCI host
+ bridge integrated with your SH CPU, refer carefully to the chip specs
+ to see if you can say 'N' here. Otherwise, leave it as 'Y'.
+
+source "drivers/pci/Kconfig"
+
+source "drivers/pcmcia/Kconfig"
+
+source "drivers/pci/hotplug/Kconfig"
+
+endmenu
+
+menu "Executable file formats"
+
+source "fs/Kconfig.binfmt"
+
+endmenu
+
+source "drivers/Kconfig"
+
+source "fs/Kconfig"
+
+source "arch/sh64/oprofile/Kconfig"
+
+menu "Kernel hacking"
+
+config MAGIC_SYSRQ
+ bool "Magic SysRq key"
+ help
+ If you say Y here, you will have some control over the system even
+ if the system crashes for example during kernel debugging (e.g., you
+ will be able to flush the buffer cache to disk, reboot the system
+ immediately or dump some status information). This is accomplished
+ by pressing various keys while holding SysRq (Alt+PrintScreen). It
+ also works on a serial console (on PC hardware at least), if you
+ send a BREAK and then within 5 seconds a command keypress. The
+ keys are documented in Documentation/sysrq.txt. Don't say Y unless
+ you really know what this hack does.
+
+config EARLY_PRINTK
+ bool "Early SCIF console support"
+
+config DEBUG_KERNEL_WITH_GDB_STUB
+ bool "GDB Stub kernel debug"
+
+config SH64_PROC_TLB
+ bool "Debug: report TLB fill/purge activity through /proc/tlb"
+ depends on PROC_FS
+
+config SH64_PROC_ASIDS
+ bool "Debug: report ASIDs through /proc/asids"
+ depends on PROC_FS
+
+config SH64_SR_WATCH
+ bool "Debug: set SR.WATCH to enable hardware watchpoints and trace"
+
+config SH_ALPHANUMERIC
+ bool "Enable debug outputs to on-board alphanumeric display"
+
+config SH_NO_BSS_INIT
+ bool "Avoid zeroing BSS (to speed-up startup on suitable platforms)"
+
+config FRAME_POINTER
+ bool "Compile the kernel with frame pointers"
+ default y if KGDB
+ help
+ If you say Y here the resulting kernel image will be slightly larger
+ and slower, but it will give very useful debugging information.
+ If you don't debug the kernel, you can say N, but we may not be able
+ to solve problems without frame pointers.
+
+endmenu
+
+source "security/Kconfig"
+
+source "crypto/Kconfig"
+
+source "lib/Kconfig"
+
--- /dev/null
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License. See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 2000, 2001 Paolo Alberelli
+# Copyright (C) 2003, 2004 Paul Mundt
+#
+# This file is included by the global makefile so that you can add your own
+# architecture-specific flags and dependencies. Remember to do have actions
+# for "archclean" and "archdep" for cleaning up and making dependencies for
+# this architecture
+#
+# Note that top level Makefile automagically builds dependencies for SUBDIRS
+# but does not automagically clean SUBDIRS. Therefore "archclean" should clean
+# up all, "archdep" does nothing on added SUBDIRS.
+#
+ifndef include_config
+-include .config
+endif
+
+cpu-y := -mb
+cpu-$(CONFIG_LITTLE_ENDIAN) := -ml
+
+cpu-$(CONFIG_CPU_SH5) += -m5-32media-nofpu
+
+ifdef CONFIG_LITTLE_ENDIAN
+LDFLAGS_vmlinux += --defsym 'jiffies=jiffies_64'
+LDFLAGS += -EL -mshlelf32_linux
+else
+LDFLAGS_vmlinux += --defsym 'jiffies=jiffies_64+4'
+LDFLAGS += -EB -mshelf32_linux
+endif
+
+# No requirements for endianess support from AFLAGS, 'as' always run through gcc
+AFLAGS += -m5 -isa=sh64 -traditional
+CFLAGS += $(cpu-y)
+
+LDFLAGS_vmlinux += --defsym phys_stext=_stext-$(CONFIG_CACHED_MEMORY_OFFSET) \
+ -e phys_stext
+
+OBJCOPYFLAGS := -O binary -R .note -R .comment -R .stab -R .stabstr -S
+
+ifdef LOADADDR
+LINKFLAGS += -Ttext $(word 1,$(LOADADDR))
+endif
+
+machine-$(CONFIG_SH_CAYMAN) := cayman
+machine-$(CONFIG_SH_SIMULATOR) := sim
+machine-$(CONFIG_SH_HARP) := harp
+machine-$(CONFIG_SH_ROMRAM) := romram
+
+head-y := arch/$(ARCH)/kernel/head.o arch/$(ARCH)/kernel/init_task.o
+
+core-y += $(addprefix arch/$(ARCH)/, kernel/ mm/ mach-$(machine-y)/)
+
+LIBGCC := $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
+libs-y += arch/$(ARCH)/lib/ $(LIBGCC)
+
+drivers-$(CONFIG_OPROFILE) += arch/sh64/oprofile/
+
+boot := arch/$(ARCH)/boot
+
+zImage: vmlinux
+ $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
+
+compressed: zImage
+
+archclean:
+ $(Q)$(MAKE) $(clean)=$(boot)
+
+prepare: include/asm-$(ARCH)/asm-offsets.h arch/$(ARCH)/lib/syscalltab.h
+
+include/asm-$(ARCH)/asm-offsets.h: arch/$(ARCH)/kernel/asm-offsets.s \
+ include/asm include/linux/version.h
+ $(call filechk,gen-asm-offsets)
+
+define filechk_gen-syscalltab
+ (set -e; \
+ echo "/*"; \
+ echo " * DO NOT MODIFY."; \
+ echo " *"; \
+ echo " * This file was generated by arch/$(ARCH)/Makefile"; \
+ echo " * Any changes will be reverted at build time."; \
+ echo " */"; \
+ echo ""; \
+ echo "#ifndef __SYSCALLTAB_H"; \
+ echo "#define __SYSCALLTAB_H"; \
+ echo ""; \
+ echo "#include <linux/kernel.h>"; \
+ echo ""; \
+ echo "struct syscall_info {"; \
+ echo " const char *name;"; \
+ echo "} syscall_info_table[] = {"; \
+ sed -e '/^.*\.long /!d;s//\t{ "/;s/\(\([^/]*\)\/\)\{1\}.*/\2/; \
+ s/[ \t]*$$//g;s/$$/" },/;s/\("\)sys_/\1/g'; \
+ echo "};"; \
+ echo ""; \
+ echo "#define NUM_SYSCALL_INFO_ENTRIES ARRAY_SIZE(syscall_info_table)"; \
+ echo ""; \
+ echo "#endif /* __SYSCALLTAB_H */" )
+endef
+
+arch/$(ARCH)/lib/syscalltab.h: arch/sh64/kernel/syscalls.S
+ $(call filechk,gen-syscalltab)
+
+CLEAN_FILES += include/asm-$(ARCH)/asm-offsets.h arch/$(ARCH)/lib/syscalltab.h
+
+define archhelp
+ @echo ' zImage - Compressed kernel image (arch/sh64/boot/zImage)'
+endef
+
--- /dev/null
+/*
+ * arch/shmedia/boot/compressed/misc.c
+ *
+ * This is a collection of several routines from gzip-1.0.3
+ * adapted for Linux.
+ *
+ * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
+ *
+ * Adapted for SHmedia from sh by Stuart Menefy, May 2002
+ */
+
+#include <linux/config.h>
+#include <asm/uaccess.h>
+
+/* cache.c */
+#define CACHE_ENABLE 0
+#define CACHE_DISABLE 1
+int cache_control(unsigned int command);
+
+/*
+ * gzip declarations
+ */
+
+#define OF(args) args
+#define STATIC static
+
+#undef memset
+#undef memcpy
+#define memzero(s, n) memset ((s), 0, (n))
+
+typedef unsigned char uch;
+typedef unsigned short ush;
+typedef unsigned long ulg;
+
+#define WSIZE 0x8000 /* Window size must be at least 32k, */
+ /* and a power of two */
+
+static uch *inbuf; /* input buffer */
+static uch window[WSIZE]; /* Sliding window buffer */
+
+static unsigned insize = 0; /* valid bytes in inbuf */
+static unsigned inptr = 0; /* index of next byte to be processed in inbuf */
+static unsigned outcnt = 0; /* bytes in output buffer */
+
+/* gzip flag byte */
+#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */
+#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
+#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
+#define COMMENT 0x10 /* bit 4 set: file comment present */
+#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */
+#define RESERVED 0xC0 /* bit 6,7: reserved */
+
+#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf())
+
+/* Diagnostic functions */
+#ifdef DEBUG
+# define Assert(cond,msg) {if(!(cond)) error(msg);}
+# define Trace(x) fprintf x
+# define Tracev(x) {if (verbose) fprintf x ;}
+# define Tracevv(x) {if (verbose>1) fprintf x ;}
+# define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
+# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
+#else
+# define Assert(cond,msg)
+# define Trace(x)
+# define Tracev(x)
+# define Tracevv(x)
+# define Tracec(c,x)
+# define Tracecv(c,x)
+#endif
+
+static int fill_inbuf(void);
+static void flush_window(void);
+static void error(char *m);
+static void gzip_mark(void **);
+static void gzip_release(void **);
+
+extern char input_data[];
+extern int input_len;
+
+static long bytes_out = 0;
+static uch *output_data;
+static unsigned long output_ptr = 0;
+
+static void *malloc(int size);
+static void free(void *where);
+static void error(char *m);
+static void gzip_mark(void **);
+static void gzip_release(void **);
+
+static void puts(const char *);
+
+extern int _text; /* Defined in vmlinux.lds.S */
+extern int _end;
+static unsigned long free_mem_ptr;
+static unsigned long free_mem_end_ptr;
+
+#define HEAP_SIZE 0x10000
+
+#include "../../../../lib/inflate.c"
+
+static void *malloc(int size)
+{
+ void *p;
+
+ if (size < 0)
+ error("Malloc error\n");
+ if (free_mem_ptr == 0)
+ error("Memory error\n");
+
+ free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */
+
+ p = (void *) free_mem_ptr;
+ free_mem_ptr += size;
+
+ if (free_mem_ptr >= free_mem_end_ptr)
+ error("\nOut of memory\n");
+
+ return p;
+}
+
+static void free(void *where)
+{ /* Don't care */
+}
+
+static void gzip_mark(void **ptr)
+{
+ *ptr = (void *) free_mem_ptr;
+}
+
+static void gzip_release(void **ptr)
+{
+ free_mem_ptr = (long) *ptr;
+}
+
+void puts(const char *s)
+{
+}
+
+void *memset(void *s, int c, size_t n)
+{
+ int i;
+ char *ss = (char *) s;
+
+ for (i = 0; i < n; i++)
+ ss[i] = c;
+ return s;
+}
+
+void *memcpy(void *__dest, __const void *__src, size_t __n)
+{
+ int i;
+ char *d = (char *) __dest, *s = (char *) __src;
+
+ for (i = 0; i < __n; i++)
+ d[i] = s[i];
+ return __dest;
+}
+
+/* ===========================================================================
+ * Fill the input buffer. This is called only when the buffer is empty
+ * and at least one byte is really needed.
+ */
+static int fill_inbuf(void)
+{
+ if (insize != 0) {
+ error("ran out of input data\n");
+ }
+
+ inbuf = input_data;
+ insize = input_len;
+ inptr = 1;
+ return inbuf[0];
+}
+
+/* ===========================================================================
+ * Write the output window window[0..outcnt-1] and update crc and bytes_out.
+ * (Used for the decompressed data only.)
+ */
+static void flush_window(void)
+{
+ ulg c = crc; /* temporary variable */
+ unsigned n;
+ uch *in, *out, ch;
+
+ in = window;
+ out = &output_data[output_ptr];
+ for (n = 0; n < outcnt; n++) {
+ ch = *out++ = *in++;
+ c = crc_32_tab[((int) c ^ ch) & 0xff] ^ (c >> 8);
+ }
+ crc = c;
+ bytes_out += (ulg) outcnt;
+ output_ptr += (ulg) outcnt;
+ outcnt = 0;
+ puts(".");
+}
+
+static void error(char *x)
+{
+ puts("\n\n");
+ puts(x);
+ puts("\n\n -- System halted");
+
+ while (1) ; /* Halt */
+}
+
+#define STACK_SIZE (4096)
+long __attribute__ ((aligned(8))) user_stack[STACK_SIZE];
+long *stack_start = &user_stack[STACK_SIZE];
+
+void decompress_kernel(void)
+{
+ output_data = (uch *) (CONFIG_MEMORY_START + 0x2000);
+ free_mem_ptr = (unsigned long) &_end;
+ free_mem_end_ptr = free_mem_ptr + HEAP_SIZE;
+
+ makecrc();
+ puts("Uncompressing Linux... ");
+ cache_control(CACHE_ENABLE);
+ gunzip();
+ puts("\n");
+
+#if 0
+ /* When booting from ROM may want to do something like this if the
+ * boot loader doesn't.
+ */
+
+ /* Set up the parameters and command line */
+ {
+ volatile unsigned int *parambase =
+ (int *) (CONFIG_MEMORY_START + 0x1000);
+
+ parambase[0] = 0x1; /* MOUNT_ROOT_RDONLY */
+ parambase[1] = 0x0; /* RAMDISK_FLAGS */
+ parambase[2] = 0x0200; /* ORIG_ROOT_DEV */
+ parambase[3] = 0x0; /* LOADER_TYPE */
+ parambase[4] = 0x0; /* INITRD_START */
+ parambase[5] = 0x0; /* INITRD_SIZE */
+ parambase[6] = 0;
+
+ strcpy((char *) ((int) parambase + 0x100),
+ "console=ttySC0,38400");
+ }
+#endif
+
+ puts("Ok, booting the kernel.\n");
+
+ cache_control(CACHE_DISABLE);
+}
--- /dev/null
+/*
+ * ld script to make compressed SuperH/shmedia Linux kernel+decompression
+ * bootstrap
+ * Modified by Stuart Menefy from arch/sh/vmlinux.lds.S written by Niibe Yutaka
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_LITTLE_ENDIAN
+/* OUTPUT_FORMAT("elf32-sh64l-linux", "elf32-sh64l-linux", "elf32-sh64l-linux") */
+#define NOP 0x6ff0fff0
+#else
+/* OUTPUT_FORMAT("elf32-sh64", "elf32-sh64", "elf32-sh64") */
+#define NOP 0xf0fff06f
+#endif
+
+OUTPUT_FORMAT("elf32-sh64-linux")
+OUTPUT_ARCH(sh)
+ENTRY(_start)
+
+#define ALIGNED_GAP(section, align) (((ADDR(section)+SIZEOF(section)+(align)-1) & ~((align)-1))-ADDR(section))
+#define FOLLOWING(section, align) AT (LOADADDR(section) + ALIGNED_GAP(section,align))
+
+SECTIONS
+{
+ _text = .; /* Text and read-only data */
+
+ .text : {
+ *(.text)
+ *(.text64)
+ *(.text..SHmedia32)
+ *(.fixup)
+ *(.gnu.warning)
+ } = NOP
+ . = ALIGN(4);
+ .rodata : { *(.rodata) }
+
+ /* There is no 'real' reason for eight byte alignment, four would work
+ * as well, but gdb downloads much (*4) faster with this.
+ */
+ . = ALIGN(8);
+ .image : { *(.image) }
+ . = ALIGN(4);
+ _etext = .; /* End of text section */
+
+ .data : /* Data */
+ FOLLOWING(.image, 4)
+ {
+ _data = .;
+ *(.data)
+ }
+ _data_image = LOADADDR(.data);/* Address of data section in ROM */
+
+ _edata = .; /* End of data section */
+
+ .stack : { stack = .; _stack = .; }
+
+ . = ALIGN(4);
+ __bss_start = .; /* BSS */
+ .bss : {
+ *(.bss)
+ }
+ . = ALIGN(4);
+ _end = . ;
+}
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
# CONFIG_NET_DIVERT is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
#
--- /dev/null
+/*
+ * arch/sh64/kernel/alpanum.c
+ *
+ * Copyright (C) 2002 Stuart Menefy <stuart.menefy@st.com>
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * Machine-independent functions for handling 8-digit alphanumeric display
+ * (e.g. Agilent HDSP-253x)
+ */
+#include <linux/config.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+
+void mach_alphanum(int pos, unsigned char val);
+void mach_led(int pos, int val);
+
+void print_seg(char *file, int line)
+{
+ int i;
+ unsigned int nibble;
+
+ for (i = 0; i < 5; i++) {
+ mach_alphanum(i, file[i]);
+ }
+
+ for (i = 0; i < 3; i++) {
+ nibble = ((line >> (i * 4)) & 0xf);
+ mach_alphanum(7 - i, nibble + ((nibble > 9) ? 55 : 48));
+ }
+}
+
+void print_seg_num(unsigned num)
+{
+ int i;
+ unsigned int nibble;
+
+ for (i = 0; i < 8; i++) {
+ nibble = ((num >> (i * 4)) & 0xf);
+
+ mach_alphanum(7 - i, nibble + ((nibble > 9) ? 55 : 48));
+ }
+}
+
--- /dev/null
+/*
+ * arch/sh64/kernel/dma.c
+ *
+ * DMA routines for the SH-5 DMAC.
+ *
+ * Copyright (C) 2003 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/irq.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <asm/hardware.h>
+#include <asm/dma.h>
+#include <asm/signal.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+
+typedef struct {
+ unsigned long dev_addr;
+ unsigned long mem_addr;
+
+ unsigned int mode;
+ unsigned int count;
+} dma_info_t;
+
+static dma_info_t dma_info[MAX_DMA_CHANNELS];
+extern spinlock_t dma_spin_lock;
+
+/* arch/sh64/kernel/irq_intc.c */
+extern void make_intc_irq(unsigned int irq);
+
+/* DMAC Interrupts */
+#define DMA_IRQ_DMTE0 18
+#define DMA_IRQ_DERR 22
+
+#define DMAC_COMMON_BASE (dmac_base + 0x08)
+#define DMAC_SAR_BASE (dmac_base + 0x10)
+#define DMAC_DAR_BASE (dmac_base + 0x18)
+#define DMAC_COUNT_BASE (dmac_base + 0x20)
+#define DMAC_CTRL_BASE (dmac_base + 0x28)
+#define DMAC_STATUS_BASE (dmac_base + 0x30)
+
+#define DMAC_SAR(n) (DMAC_SAR_BASE + ((n) * 0x28))
+#define DMAC_DAR(n) (DMAC_DAR_BASE + ((n) * 0x28))
+#define DMAC_COUNT(n) (DMAC_COUNT_BASE + ((n) * 0x28))
+#define DMAC_CTRL(n) (DMAC_CTRL_BASE + ((n) * 0x28))
+#define DMAC_STATUS(n) (DMAC_STATUS_BASE + ((n) * 0x28))
+
+/* DMAC.COMMON Bit Definitions */
+#define DMAC_COMMON_PR 0x00000001 /* Priority */
+ /* Bits 1-2 Reserved */
+#define DMAC_COMMON_ME 0x00000008 /* Master Enable */
+#define DMAC_COMMON_NMI 0x00000010 /* NMI Flag */
+ /* Bits 5-6 Reserved */
+#define DMAC_COMMON_ER 0x00000780 /* Error Response */
+#define DMAC_COMMON_AAE 0x00007800 /* Address Alignment Error */
+ /* Bits 15-63 Reserved */
+
+/* DMAC.SAR Bit Definitions */
+#define DMAC_SAR_ADDR 0xffffffff /* Source Address */
+
+/* DMAC.DAR Bit Definitions */
+#define DMAC_DAR_ADDR 0xffffffff /* Destination Address */
+
+/* DMAC.COUNT Bit Definitions */
+#define DMAC_COUNT_CNT 0xffffffff /* Transfer Count */
+
+/* DMAC.CTRL Bit Definitions */
+#define DMAC_CTRL_TS 0x00000007 /* Transfer Size */
+#define DMAC_CTRL_SI 0x00000018 /* Source Increment */
+#define DMAC_CTRL_DI 0x00000060 /* Destination Increment */
+#define DMAC_CTRL_RS 0x00000780 /* Resource Select */
+#define DMAC_CTRL_IE 0x00000800 /* Interrupt Enable */
+#define DMAC_CTRL_TE 0x00001000 /* Transfer Enable */
+ /* Bits 15-63 Reserved */
+
+/* DMAC.STATUS Bit Definitions */
+#define DMAC_STATUS_TE 0x00000001 /* Transfer End */
+#define DMAC_STATUS_AAE 0x00000002 /* Address Alignment Error */
+ /* Bits 2-63 Reserved */
+
+static unsigned long dmac_base;
+
+void set_dma_count(unsigned int chan, unsigned int count);
+void set_dma_addr(unsigned int chan, unsigned int addr);
+
+static irqreturn_t dma_mte(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned int chan = irq - DMA_IRQ_DMTE0;
+ dma_info_t *info = dma_info + chan;
+ u64 status;
+
+ if (info->mode & DMA_MODE_WRITE) {
+ sh64_out64(info->mem_addr & DMAC_SAR_ADDR, DMAC_SAR(chan));
+ } else {
+ sh64_out64(info->mem_addr & DMAC_DAR_ADDR, DMAC_DAR(chan));
+ }
+
+ set_dma_count(chan, info->count);
+
+ /* Clear the TE bit */
+ status = sh64_in64(DMAC_STATUS(chan));
+ status &= ~DMAC_STATUS_TE;
+ sh64_out64(status, DMAC_STATUS(chan));
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction irq_dmte = {
+ .handler = dma_mte,
+ .flags = SA_INTERRUPT,
+ .name = "DMA MTE",
+};
+
+static irqreturn_t dma_err(int irq, void *dev_id, struct pt_regs *regs)
+{
+ u64 tmp;
+ u8 chan;
+
+ printk(KERN_NOTICE "DMAC: Got a DMA Error!\n");
+
+ tmp = sh64_in64(DMAC_COMMON_BASE);
+
+ /* Check for the type of error */
+ if ((chan = tmp & DMAC_COMMON_AAE)) {
+ /* It's an address alignment error.. */
+ printk(KERN_NOTICE "DMAC: Alignment error on channel %d, ", chan);
+
+ printk(KERN_NOTICE "SAR: 0x%08llx, DAR: 0x%08llx, COUNT: %lld\n",
+ (sh64_in64(DMAC_SAR(chan)) & DMAC_SAR_ADDR),
+ (sh64_in64(DMAC_DAR(chan)) & DMAC_DAR_ADDR),
+ (sh64_in64(DMAC_COUNT(chan)) & DMAC_COUNT_CNT));
+
+ } else if ((chan = tmp & DMAC_COMMON_ER)) {
+ /* Something else went wrong.. */
+ printk(KERN_NOTICE "DMAC: Error on channel %d\n", chan);
+ }
+
+ /* Reset the ME bit to clear the interrupt */
+ tmp |= DMAC_COMMON_ME;
+ sh64_out64(tmp, DMAC_COMMON_BASE);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction irq_derr = {
+ .handler = dma_err,
+ .flags = SA_INTERRUPT,
+ .name = "DMA Error",
+};
+
+static inline unsigned long calc_xmit_shift(unsigned int chan)
+{
+ return sh64_in64(DMAC_CTRL(chan)) & 0x03;
+}
+
+void setup_dma(unsigned int chan, dma_info_t *info)
+{
+ unsigned int irq = DMA_IRQ_DMTE0 + chan;
+ dma_info_t *dma = dma_info + chan;
+
+ make_intc_irq(irq);
+ setup_irq(irq, &irq_dmte);
+ dma = info;
+}
+
+void enable_dma(unsigned int chan)
+{
+ u64 ctrl;
+
+ ctrl = sh64_in64(DMAC_CTRL(chan));
+ ctrl |= DMAC_CTRL_TE;
+ sh64_out64(ctrl, DMAC_CTRL(chan));
+}
+
+void disable_dma(unsigned int chan)
+{
+ u64 ctrl;
+
+ ctrl = sh64_in64(DMAC_CTRL(chan));
+ ctrl &= ~DMAC_CTRL_TE;
+ sh64_out64(ctrl, DMAC_CTRL(chan));
+}
+
+void set_dma_mode(unsigned int chan, char mode)
+{
+ dma_info_t *info = dma_info + chan;
+
+ info->mode = mode;
+
+ set_dma_addr(chan, info->mem_addr);
+ set_dma_count(chan, info->count);
+}
+
+void set_dma_addr(unsigned int chan, unsigned int addr)
+{
+ dma_info_t *info = dma_info + chan;
+ unsigned long sar, dar;
+
+ info->mem_addr = addr;
+ sar = (info->mode & DMA_MODE_WRITE) ? info->mem_addr : info->dev_addr;
+ dar = (info->mode & DMA_MODE_WRITE) ? info->dev_addr : info->mem_addr;
+
+ sh64_out64(sar & DMAC_SAR_ADDR, DMAC_SAR(chan));
+ sh64_out64(dar & DMAC_SAR_ADDR, DMAC_DAR(chan));
+}
+
+void set_dma_count(unsigned int chan, unsigned int count)
+{
+ dma_info_t *info = dma_info + chan;
+ u64 tmp;
+
+ info->count = count;
+
+ tmp = (info->count >> calc_xmit_shift(chan)) & DMAC_COUNT_CNT;
+
+ sh64_out64(tmp, DMAC_COUNT(chan));
+}
+
+unsigned long claim_dma_lock(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dma_spin_lock, flags);
+
+ return flags;
+}
+
+void release_dma_lock(unsigned long flags)
+{
+ spin_unlock_irqrestore(&dma_spin_lock, flags);
+}
+
+int get_dma_residue(unsigned int chan)
+{
+ return sh64_in64(DMAC_COUNT(chan) << calc_xmit_shift(chan));
+}
+
+int __init init_dma(void)
+{
+ struct vcr_info vcr;
+ u64 tmp;
+
+ /* Remap the DMAC */
+ dmac_base = onchip_remap(PHYS_DMAC_BLOCK, 1024, "DMAC");
+ if (!dmac_base) {
+ printk(KERN_ERR "Unable to remap DMAC\n");
+ return -ENOMEM;
+ }
+
+ /* Report DMAC.VCR Info */
+ vcr = sh64_get_vcr_info(dmac_base);
+ printk("DMAC: Module ID: 0x%04x, Module version: 0x%04x\n",
+ vcr.mod_id, vcr.mod_vers);
+
+ /* Set the ME bit */
+ tmp = sh64_in64(DMAC_COMMON_BASE);
+ tmp |= DMAC_COMMON_ME;
+ sh64_out64(tmp, DMAC_COMMON_BASE);
+
+ /* Enable the DMAC Error Interrupt */
+ make_intc_irq(DMA_IRQ_DERR);
+ setup_irq(DMA_IRQ_DERR, &irq_derr);
+
+ return 0;
+}
+
+static void __exit exit_dma(void)
+{
+ onchip_unmap(dmac_base);
+ free_irq(DMA_IRQ_DERR, 0);
+}
+
+module_init(init_dma);
+module_exit(exit_dma);
+
+MODULE_AUTHOR("Paul Mundt");
+MODULE_DESCRIPTION("DMA API for SH-5 DMAC");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(setup_dma);
+EXPORT_SYMBOL(claim_dma_lock);
+EXPORT_SYMBOL(release_dma_lock);
+EXPORT_SYMBOL(enable_dma);
+EXPORT_SYMBOL(disable_dma);
+EXPORT_SYMBOL(set_dma_mode);
+EXPORT_SYMBOL(set_dma_addr);
+EXPORT_SYMBOL(set_dma_count);
+EXPORT_SYMBOL(get_dma_residue);
+
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/kernel/entry.S
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2004 Paul Mundt
+ * Copyright (C) 2003, 2004 Richard Curnow
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sys.h>
+
+#include <asm/processor.h>
+#include <asm/registers.h>
+#include <asm/unistd.h>
+#include <asm/thread_info.h>
+#include <asm/asm-offsets.h>
+
+/*
+ * SR fields.
+ */
+#define SR_ASID_MASK 0x00ff0000
+#define SR_FD_MASK 0x00008000
+#define SR_SS 0x08000000
+#define SR_BL 0x10000000
+#define SR_MD 0x40000000
+
+/*
+ * Event code.
+ */
+#define EVENT_INTERRUPT 0
+#define EVENT_FAULT_TLB 1
+#define EVENT_FAULT_NOT_TLB 2
+#define EVENT_DEBUG 3
+
+/* EXPEVT values */
+#define RESET_CAUSE 0x20
+#define DEBUGSS_CAUSE 0x980
+
+/*
+ * Frame layout. Quad index.
+ */
+#define FRAME_T(x) FRAME_TBASE+(x*8)
+#define FRAME_R(x) FRAME_RBASE+(x*8)
+#define FRAME_S(x) FRAME_SBASE+(x*8)
+#define FSPC 0
+#define FSSR 1
+#define FSYSCALL_ID 2
+
+/* Arrange the save frame to be a multiple of 32 bytes long */
+#define FRAME_SBASE 0
+#define FRAME_RBASE (FRAME_SBASE+(3*8)) /* SYSCALL_ID - SSR - SPC */
+#define FRAME_TBASE (FRAME_RBASE+(63*8)) /* r0 - r62 */
+#define FRAME_PBASE (FRAME_TBASE+(8*8)) /* tr0 -tr7 */
+#define FRAME_SIZE (FRAME_PBASE+(2*8)) /* pad0-pad1 */
+
+#define FP_FRAME_SIZE FP_FRAME_BASE+(33*8) /* dr0 - dr31 + fpscr */
+#define FP_FRAME_BASE 0
+
+#define SAVED_R2 0*8
+#define SAVED_R3 1*8
+#define SAVED_R4 2*8
+#define SAVED_R5 3*8
+#define SAVED_R18 4*8
+#define SAVED_R6 5*8
+#define SAVED_TR0 6*8
+
+/* These are the registers saved in the TLB path that aren't saved in the first
+ level of the normal one. */
+#define TLB_SAVED_R25 7*8
+#define TLB_SAVED_TR1 8*8
+#define TLB_SAVED_TR2 9*8
+#define TLB_SAVED_TR3 10*8
+#define TLB_SAVED_TR4 11*8
+/* Save R0/R1 : PT-migrating compiler currently dishounours -ffixed-r0 and -ffixed-r1 causing
+ breakage otherwise. */
+#define TLB_SAVED_R0 12*8
+#define TLB_SAVED_R1 13*8
+
+#define CLI() \
+ getcon SR, r6; \
+ ori r6, 0xf0, r6; \
+ putcon r6, SR;
+
+#define STI() \
+ getcon SR, r6; \
+ andi r6, ~0xf0, r6; \
+ putcon r6, SR;
+
+#ifdef CONFIG_PREEMPT
+# define preempt_stop() CLI()
+#else
+# define preempt_stop()
+# define resume_kernel restore_all
+#endif
+
+ .section .data, "aw"
+
+#define FAST_TLBMISS_STACK_CACHELINES 4
+#define FAST_TLBMISS_STACK_QUADWORDS (4*FAST_TLBMISS_STACK_CACHELINES)
+
+/* Register back-up area for all exceptions */
+ .balign 32
+ /* Allow for 16 quadwords to be pushed by fast tlbmiss handling
+ * register saves etc. */
+ .fill FAST_TLBMISS_STACK_QUADWORDS, 8, 0x0
+/* This is 32 byte aligned by construction */
+/* Register back-up area for all exceptions */
+reg_save_area:
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+
+ .quad 0
+ .quad 0
+
+/* Save area for RESVEC exceptions. We cannot use reg_save_area because of
+ * reentrancy. Note this area may be accessed via physical address.
+ * Align so this fits a whole single cache line, for ease of purging.
+ */
+ .balign 32,0,32
+resvec_save_area:
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+ .balign 32,0,32
+
+/* Jump table of 3rd level handlers */
+trap_jtable:
+ .long do_exception_error /* 0x000 */
+ .long do_exception_error /* 0x020 */
+ .long tlb_miss_load /* 0x040 */
+ .long tlb_miss_store /* 0x060 */
+ ! ARTIFICIAL pseudo-EXPEVT setting
+ .long do_debug_interrupt /* 0x080 */
+ .long tlb_miss_load /* 0x0A0 */
+ .long tlb_miss_store /* 0x0C0 */
+ .long do_address_error_load /* 0x0E0 */
+ .long do_address_error_store /* 0x100 */
+#ifndef CONFIG_NOFPU_SUPPORT
+ .long do_fpu_error /* 0x120 */
+#else
+ .long do_exception_error /* 0x120 */
+#endif
+ .long do_exception_error /* 0x140 */
+ .long system_call /* 0x160 */
+ .long do_reserved_inst /* 0x180 */
+ .long do_illegal_slot_inst /* 0x1A0 */
+ .long do_NMI /* 0x1C0 */
+ .long do_exception_error /* 0x1E0 */
+ .rept 15
+ .long do_IRQ /* 0x200 - 0x3C0 */
+ .endr
+ .long do_exception_error /* 0x3E0 */
+ .rept 32
+ .long do_IRQ /* 0x400 - 0x7E0 */
+ .endr
+ .long fpu_error_or_IRQA /* 0x800 */
+ .long fpu_error_or_IRQB /* 0x820 */
+ .long do_IRQ /* 0x840 */
+ .long do_IRQ /* 0x860 */
+ .rept 6
+ .long do_exception_error /* 0x880 - 0x920 */
+ .endr
+ .long do_software_break_point /* 0x940 */
+ .long do_exception_error /* 0x960 */
+ .long do_single_step /* 0x980 */
+
+ .rept 3
+ .long do_exception_error /* 0x9A0 - 0x9E0 */
+ .endr
+ .long do_IRQ /* 0xA00 */
+ .long do_IRQ /* 0xA20 */
+ .long itlb_miss_or_IRQ /* 0xA40 */
+ .long do_IRQ /* 0xA60 */
+ .long do_IRQ /* 0xA80 */
+ .long itlb_miss_or_IRQ /* 0xAA0 */
+ .long do_exception_error /* 0xAC0 */
+ .long do_address_error_exec /* 0xAE0 */
+ .rept 8
+ .long do_exception_error /* 0xB00 - 0xBE0 */
+ .endr
+ .rept 18
+ .long do_IRQ /* 0xC00 - 0xE20 */
+ .endr
+
+ .section .text64, "ax"
+
+/*
+ * --- Exception/Interrupt/Event Handling Section
+ */
+
+/*
+ * VBR and RESVEC blocks.
+ *
+ * First level handler for VBR-based exceptions.
+ *
+ * To avoid waste of space, align to the maximum text block size.
+ * This is assumed to be at most 128 bytes or 32 instructions.
+ * DO NOT EXCEED 32 instructions on the first level handlers !
+ *
+ * Also note that RESVEC is contained within the VBR block
+ * where the room left (1KB - TEXT_SIZE) allows placing
+ * the RESVEC block (at most 512B + TEXT_SIZE).
+ *
+ * So first (and only) level handler for RESVEC-based exceptions.
+ *
+ * Where the fault/interrupt is handled (not_a_tlb_miss, tlb_miss
+ * and interrupt) we are a lot tight with register space until
+ * saving onto the stack frame, which is done in handle_exception().
+ *
+ */
+
+#define TEXT_SIZE 128
+#define BLOCK_SIZE 1664 /* Dynamic check, 13*128 */
+
+ .balign TEXT_SIZE
+LVBR_block:
+ .space 256, 0 /* Power-on class handler, */
+ /* not required here */
+not_a_tlb_miss:
+ /* Save original stack pointer into KCR1 */
+ putcon SP, KCR1
+
+ /* Save other original registers into reg_save_area */
+ movi reg_save_area, SP
+ st.q SP, SAVED_R2, r2
+ st.q SP, SAVED_R3, r3
+ st.q SP, SAVED_R4, r4
+ st.q SP, SAVED_R5, r5
+ st.q SP, SAVED_R6, r6
+ st.q SP, SAVED_R18, r18
+ gettr tr0, r3
+ st.q SP, SAVED_TR0, r3
+
+ /* Set args for Non-debug, Not a TLB miss class handler */
+ getcon EXPEVT, r2
+ movi ret_from_exception, r3
+ ori r3, 1, r3
+ movi EVENT_FAULT_NOT_TLB, r4
+ or SP, ZERO, r5
+ getcon KCR1, SP
+ pta handle_exception, tr0
+ blink tr0, ZERO
+
+ .balign 256
+ ! VBR+0x200
+ nop
+ .balign 256
+ ! VBR+0x300
+ nop
+ .balign 256
+ /*
+ * Instead of the natural .balign 1024 place RESVEC here
+ * respecting the final 1KB alignment.
+ */
+ .balign TEXT_SIZE
+ /*
+ * Instead of '.space 1024-TEXT_SIZE' place the RESVEC
+ * block making sure the final alignment is correct.
+ */
+tlb_miss:
+ putcon SP, KCR1
+ movi reg_save_area, SP
+ /* SP is guaranteed 32-byte aligned. */
+ st.q SP, TLB_SAVED_R0 , r0
+ st.q SP, TLB_SAVED_R1 , r1
+ st.q SP, SAVED_R2 , r2
+ st.q SP, SAVED_R3 , r3
+ st.q SP, SAVED_R4 , r4
+ st.q SP, SAVED_R5 , r5
+ st.q SP, SAVED_R6 , r6
+ st.q SP, SAVED_R18, r18
+
+ /* Save R25 for safety; as/ld may want to use it to achieve the call to
+ * the code in mm/tlbmiss.c */
+ st.q SP, TLB_SAVED_R25, r25
+ gettr tr0, r2
+ gettr tr1, r3
+ gettr tr2, r4
+ gettr tr3, r5
+ gettr tr4, r18
+ st.q SP, SAVED_TR0 , r2
+ st.q SP, TLB_SAVED_TR1 , r3
+ st.q SP, TLB_SAVED_TR2 , r4
+ st.q SP, TLB_SAVED_TR3 , r5
+ st.q SP, TLB_SAVED_TR4 , r18
+
+ pt do_fast_page_fault, tr0
+ getcon SSR, r2
+ getcon EXPEVT, r3
+ getcon TEA, r4
+ shlri r2, 30, r2
+ andi r2, 1, r2 /* r2 = SSR.MD */
+ blink tr0, LINK
+
+ pt fixup_to_invoke_general_handler, tr1
+
+ /* If the fast path handler fixed the fault, just drop through quickly
+ to the restore code right away to return to the excepting context.
+ */
+ beqi/u r2, 0, tr1
+
+fast_tlb_miss_restore:
+ ld.q SP, SAVED_TR0, r2
+ ld.q SP, TLB_SAVED_TR1, r3
+ ld.q SP, TLB_SAVED_TR2, r4
+
+ ld.q SP, TLB_SAVED_TR3, r5
+ ld.q SP, TLB_SAVED_TR4, r18
+
+ ptabs r2, tr0
+ ptabs r3, tr1
+ ptabs r4, tr2
+ ptabs r5, tr3
+ ptabs r18, tr4
+
+ ld.q SP, TLB_SAVED_R0, r0
+ ld.q SP, TLB_SAVED_R1, r1
+ ld.q SP, SAVED_R2, r2
+ ld.q SP, SAVED_R3, r3
+ ld.q SP, SAVED_R4, r4
+ ld.q SP, SAVED_R5, r5
+ ld.q SP, SAVED_R6, r6
+ ld.q SP, SAVED_R18, r18
+ ld.q SP, TLB_SAVED_R25, r25
+
+ getcon KCR1, SP
+ rte
+ nop /* for safety, in case the code is run on sh5-101 cut1.x */
+
+fixup_to_invoke_general_handler:
+
+ /* OK, new method. Restore stuff that's not expected to get saved into
+ the 'first-level' reg save area, then just fall through to setting
+ up the registers and calling the second-level handler. */
+
+ /* 2nd level expects r2,3,4,5,6,18,tr0 to be saved. So we must restore
+ r25,tr1-4 and save r6 to get into the right state. */
+
+ ld.q SP, TLB_SAVED_TR1, r3
+ ld.q SP, TLB_SAVED_TR2, r4
+ ld.q SP, TLB_SAVED_TR3, r5
+ ld.q SP, TLB_SAVED_TR4, r18
+ ld.q SP, TLB_SAVED_R25, r25
+
+ ld.q SP, TLB_SAVED_R0, r0
+ ld.q SP, TLB_SAVED_R1, r1
+
+ ptabs/u r3, tr1
+ ptabs/u r4, tr2
+ ptabs/u r5, tr3
+ ptabs/u r18, tr4
+
+ /* Set args for Non-debug, TLB miss class handler */
+ getcon EXPEVT, r2
+ movi ret_from_exception, r3
+ ori r3, 1, r3
+ movi EVENT_FAULT_TLB, r4
+ or SP, ZERO, r5
+ getcon KCR1, SP
+ pta handle_exception, tr0
+ blink tr0, ZERO
+
+/* NB TAKE GREAT CARE HERE TO ENSURE THAT THE INTERRUPT CODE
+ DOES END UP AT VBR+0x600 */
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+ .balign 256
+ /* VBR + 0x600 */
+
+interrupt:
+ /* Save original stack pointer into KCR1 */
+ putcon SP, KCR1
+
+ /* Save other original registers into reg_save_area */
+ movi reg_save_area, SP
+ st.q SP, SAVED_R2, r2
+ st.q SP, SAVED_R3, r3
+ st.q SP, SAVED_R4, r4
+ st.q SP, SAVED_R5, r5
+ st.q SP, SAVED_R6, r6
+ st.q SP, SAVED_R18, r18
+ gettr tr0, r3
+ st.q SP, SAVED_TR0, r3
+
+ /* Set args for interrupt class handler */
+ getcon INTEVT, r2
+ movi ret_from_irq, r3
+ ori r3, 1, r3
+ movi EVENT_INTERRUPT, r4
+ or SP, ZERO, r5
+ getcon KCR1, SP
+ pta handle_exception, tr0
+ blink tr0, ZERO
+ .balign TEXT_SIZE /* let's waste the bare minimum */
+
+LVBR_block_end: /* Marker. Used for total checking */
+
+ .balign 256
+LRESVEC_block:
+ /* Panic handler. Called with MMU off. Possible causes/actions:
+ * - Reset: Jump to program start.
+ * - Single Step: Turn off Single Step & return.
+ * - Others: Call panic handler, passing PC as arg.
+ * (this may need to be extended...)
+ */
+reset_or_panic:
+ putcon SP, DCR
+ /* First save r0-1 and tr0, as we need to use these */
+ movi resvec_save_area-CONFIG_CACHED_MEMORY_OFFSET, SP
+ st.q SP, 0, r0
+ st.q SP, 8, r1
+ gettr tr0, r0
+ st.q SP, 32, r0
+
+ /* Check cause */
+ getcon EXPEVT, r0
+ movi RESET_CAUSE, r1
+ sub r1, r0, r1 /* r1=0 if reset */
+ movi _stext-CONFIG_CACHED_MEMORY_OFFSET, r0
+ ori r0, 1, r0
+ ptabs r0, tr0
+ beqi r1, 0, tr0 /* Jump to start address if reset */
+
+ getcon EXPEVT, r0
+ movi DEBUGSS_CAUSE, r1
+ sub r1, r0, r1 /* r1=0 if single step */
+ pta single_step_panic, tr0
+ beqi r1, 0, tr0 /* jump if single step */
+
+ /* Now jump to where we save the registers. */
+ movi panic_stash_regs-CONFIG_CACHED_MEMORY_OFFSET, r1
+ ptabs r1, tr0
+ blink tr0, r63
+
+single_step_panic:
+ /* We are in a handler with Single Step set. We need to resume the
+ * handler, by turning on MMU & turning off Single Step. */
+ getcon SSR, r0
+ movi SR_MMU, r1
+ or r0, r1, r0
+ movi ~SR_SS, r1
+ and r0, r1, r0
+ putcon r0, SSR
+ /* Restore EXPEVT, as the rte won't do this */
+ getcon PEXPEVT, r0
+ putcon r0, EXPEVT
+ /* Restore regs */
+ ld.q SP, 32, r0
+ ptabs r0, tr0
+ ld.q SP, 0, r0
+ ld.q SP, 8, r1
+ getcon DCR, SP
+ synco
+ rte
+
+
+ .balign 256
+debug_exception:
+ /*
+ * Single step/software_break_point first level handler.
+ * Called with MMU off, so the first thing we do is enable it
+ * by doing an rte with appropriate SSR.
+ */
+ putcon SP, DCR
+ /* Save SSR & SPC, together with R0 & R1, as we need to use 2 regs. */
+ movi resvec_save_area-CONFIG_CACHED_MEMORY_OFFSET, SP
+
+ /* With the MMU off, we are bypassing the cache, so purge any
+ * data that will be made stale by the following stores.
+ */
+ ocbp SP, 0
+ synco
+
+ st.q SP, 0, r0
+ st.q SP, 8, r1
+ getcon SPC, r0
+ st.q SP, 16, r0
+ getcon SSR, r0
+ st.q SP, 24, r0
+
+ /* Enable MMU, block exceptions, set priv mode, disable single step */
+ movi SR_MMU | SR_BL | SR_MD, r1
+ or r0, r1, r0
+ movi ~SR_SS, r1
+ and r0, r1, r0
+ putcon r0, SSR
+ /* Force control to debug_exception_2 when rte is executed */
+ movi debug_exeception_2, r0
+ ori r0, 1, r0 /* force SHmedia, just in case */
+ putcon r0, SPC
+ getcon DCR, SP
+ synco
+ rte
+debug_exeception_2:
+ /* Restore saved regs */
+ putcon SP, KCR1
+ movi resvec_save_area, SP
+ ld.q SP, 24, r0
+ putcon r0, SSR
+ ld.q SP, 16, r0
+ putcon r0, SPC
+ ld.q SP, 0, r0
+ ld.q SP, 8, r1
+
+ /* Save other original registers into reg_save_area */
+ movi reg_save_area, SP
+ st.q SP, SAVED_R2, r2
+ st.q SP, SAVED_R3, r3
+ st.q SP, SAVED_R4, r4
+ st.q SP, SAVED_R5, r5
+ st.q SP, SAVED_R6, r6
+ st.q SP, SAVED_R18, r18
+ gettr tr0, r3
+ st.q SP, SAVED_TR0, r3
+
+ /* Set args for debug class handler */
+ getcon EXPEVT, r2
+ movi ret_from_exception, r3
+ ori r3, 1, r3
+ movi EVENT_DEBUG, r4
+ or SP, ZERO, r5
+ getcon KCR1, SP
+ pta handle_exception, tr0
+ blink tr0, ZERO
+
+ .balign 256
+debug_interrupt:
+ /* !!! WE COME HERE IN REAL MODE !!! */
+ /* Hook-up debug interrupt to allow various debugging options to be
+ * hooked into its handler. */
+ /* Save original stack pointer into KCR1 */
+ synco
+ putcon SP, KCR1
+ movi resvec_save_area-CONFIG_CACHED_MEMORY_OFFSET, SP
+ ocbp SP, 0
+ ocbp SP, 32
+ synco
+
+ /* Save other original registers into reg_save_area thru real addresses */
+ st.q SP, SAVED_R2, r2
+ st.q SP, SAVED_R3, r3
+ st.q SP, SAVED_R4, r4
+ st.q SP, SAVED_R5, r5
+ st.q SP, SAVED_R6, r6
+ st.q SP, SAVED_R18, r18
+ gettr tr0, r3
+ st.q SP, SAVED_TR0, r3
+
+ /* move (spc,ssr)->(pspc,pssr). The rte will shift
+ them back again, so that they look like the originals
+ as far as the real handler code is concerned. */
+ getcon spc, r6
+ putcon r6, pspc
+ getcon ssr, r6
+ putcon r6, pssr
+
+ ! construct useful SR for handle_exception
+ movi 3, r6
+ shlli r6, 30, r6
+ getcon sr, r18
+ or r18, r6, r6
+ putcon r6, ssr
+
+ ! SSR is now the current SR with the MD and MMU bits set
+ ! i.e. the rte will switch back to priv mode and put
+ ! the mmu back on
+
+ ! construct spc
+ movi handle_exception, r18
+ ori r18, 1, r18 ! for safety (do we need this?)
+ putcon r18, spc
+
+ /* Set args for Non-debug, Not a TLB miss class handler */
+
+ ! EXPEVT==0x80 is unused, so 'steal' this value to put the
+ ! debug interrupt handler in the vectoring table
+ movi 0x80, r2
+ movi ret_from_exception, r3
+ ori r3, 1, r3
+ movi EVENT_FAULT_NOT_TLB, r4
+
+ or SP, ZERO, r5
+ movi CONFIG_CACHED_MEMORY_OFFSET, r6
+ add r6, r5, r5
+ getcon KCR1, SP
+
+ synco ! for safety
+ rte ! -> handle_exception, switch back to priv mode again
+
+LRESVEC_block_end: /* Marker. Unused. */
+
+ .balign TEXT_SIZE
+
+/*
+ * Second level handler for VBR-based exceptions. Pre-handler.
+ * In common to all stack-frame sensitive handlers.
+ *
+ * Inputs:
+ * (KCR0) Current [current task union]
+ * (KCR1) Original SP
+ * (r2) INTEVT/EXPEVT
+ * (r3) appropriate return address
+ * (r4) Event (0 = interrupt, 1 = TLB miss fault, 2 = Not TLB miss fault, 3=debug)
+ * (r5) Pointer to reg_save_area
+ * (SP) Original SP
+ *
+ * Available registers:
+ * (r6)
+ * (r18)
+ * (tr0)
+ *
+ */
+handle_exception:
+ /* Common 2nd level handler. */
+
+ /* First thing we need an appropriate stack pointer */
+ getcon SSR, r6
+ shlri r6, 30, r6
+ andi r6, 1, r6
+ pta stack_ok, tr0
+ bne r6, ZERO, tr0 /* Original stack pointer is fine */
+
+ /* Set stack pointer for user fault */
+ getcon KCR0, SP
+ movi THREAD_SIZE, r6 /* Point to the end */
+ add SP, r6, SP
+
+stack_ok:
+
+/* DEBUG : check for underflow/overflow of the kernel stack */
+ pta no_underflow, tr0
+ getcon KCR0, r6
+ movi 1024, r18
+ add r6, r18, r6
+ bge SP, r6, tr0 ! ? below 1k from bottom of stack : danger zone
+
+/* Just panic to cause a crash. */
+bad_sp:
+ ld.b r63, 0, r6
+ nop
+
+no_underflow:
+ pta bad_sp, tr0
+ getcon kcr0, r6
+ movi THREAD_SIZE, r18
+ add r18, r6, r6
+ bgt SP, r6, tr0 ! sp above the stack
+
+ /* Make some room for the BASIC frame. */
+ movi -(FRAME_SIZE), r6
+ add SP, r6, SP
+
+/* Could do this with no stalling if we had another spare register, but the
+ code below will be OK. */
+ ld.q r5, SAVED_R2, r6
+ ld.q r5, SAVED_R3, r18
+ st.q SP, FRAME_R(2), r6
+ ld.q r5, SAVED_R4, r6
+ st.q SP, FRAME_R(3), r18
+ ld.q r5, SAVED_R5, r18
+ st.q SP, FRAME_R(4), r6
+ ld.q r5, SAVED_R6, r6
+ st.q SP, FRAME_R(5), r18
+ ld.q r5, SAVED_R18, r18
+ st.q SP, FRAME_R(6), r6
+ ld.q r5, SAVED_TR0, r6
+ st.q SP, FRAME_R(18), r18
+ st.q SP, FRAME_T(0), r6
+
+ /* Keep old SP around */
+ getcon KCR1, r6
+
+ /* Save the rest of the general purpose registers */
+ st.q SP, FRAME_R(0), r0
+ st.q SP, FRAME_R(1), r1
+ st.q SP, FRAME_R(7), r7
+ st.q SP, FRAME_R(8), r8
+ st.q SP, FRAME_R(9), r9
+ st.q SP, FRAME_R(10), r10
+ st.q SP, FRAME_R(11), r11
+ st.q SP, FRAME_R(12), r12
+ st.q SP, FRAME_R(13), r13
+ st.q SP, FRAME_R(14), r14
+
+ /* SP is somewhere else */
+ st.q SP, FRAME_R(15), r6
+
+ st.q SP, FRAME_R(16), r16
+ st.q SP, FRAME_R(17), r17
+ /* r18 is saved earlier. */
+ st.q SP, FRAME_R(19), r19
+ st.q SP, FRAME_R(20), r20
+ st.q SP, FRAME_R(21), r21
+ st.q SP, FRAME_R(22), r22
+ st.q SP, FRAME_R(23), r23
+ st.q SP, FRAME_R(24), r24
+ st.q SP, FRAME_R(25), r25
+ st.q SP, FRAME_R(26), r26
+ st.q SP, FRAME_R(27), r27
+ st.q SP, FRAME_R(28), r28
+ st.q SP, FRAME_R(29), r29
+ st.q SP, FRAME_R(30), r30
+ st.q SP, FRAME_R(31), r31
+ st.q SP, FRAME_R(32), r32
+ st.q SP, FRAME_R(33), r33
+ st.q SP, FRAME_R(34), r34
+ st.q SP, FRAME_R(35), r35
+ st.q SP, FRAME_R(36), r36
+ st.q SP, FRAME_R(37), r37
+ st.q SP, FRAME_R(38), r38
+ st.q SP, FRAME_R(39), r39
+ st.q SP, FRAME_R(40), r40
+ st.q SP, FRAME_R(41), r41
+ st.q SP, FRAME_R(42), r42
+ st.q SP, FRAME_R(43), r43
+ st.q SP, FRAME_R(44), r44
+ st.q SP, FRAME_R(45), r45
+ st.q SP, FRAME_R(46), r46
+ st.q SP, FRAME_R(47), r47
+ st.q SP, FRAME_R(48), r48
+ st.q SP, FRAME_R(49), r49
+ st.q SP, FRAME_R(50), r50
+ st.q SP, FRAME_R(51), r51
+ st.q SP, FRAME_R(52), r52
+ st.q SP, FRAME_R(53), r53
+ st.q SP, FRAME_R(54), r54
+ st.q SP, FRAME_R(55), r55
+ st.q SP, FRAME_R(56), r56
+ st.q SP, FRAME_R(57), r57
+ st.q SP, FRAME_R(58), r58
+ st.q SP, FRAME_R(59), r59
+ st.q SP, FRAME_R(60), r60
+ st.q SP, FRAME_R(61), r61
+ st.q SP, FRAME_R(62), r62
+
+ /*
+ * Save the S* registers.
+ */
+ getcon SSR, r61
+ st.q SP, FRAME_S(FSSR), r61
+ getcon SPC, r62
+ st.q SP, FRAME_S(FSPC), r62
+ movi -1, r62 /* Reset syscall_nr */
+ st.q SP, FRAME_S(FSYSCALL_ID), r62
+
+ /* Save the rest of the target registers */
+ gettr tr1, r6
+ st.q SP, FRAME_T(1), r6
+ gettr tr2, r6
+ st.q SP, FRAME_T(2), r6
+ gettr tr3, r6
+ st.q SP, FRAME_T(3), r6
+ gettr tr4, r6
+ st.q SP, FRAME_T(4), r6
+ gettr tr5, r6
+ st.q SP, FRAME_T(5), r6
+ gettr tr6, r6
+ st.q SP, FRAME_T(6), r6
+ gettr tr7, r6
+ st.q SP, FRAME_T(7), r6
+
+ ! setup FP so that unwinder can wind back through nested kernel mode
+ ! exceptions
+ add SP, ZERO, r14
+
+#define POOR_MANS_STRACE 0
+
+#if POOR_MANS_STRACE
+ /* We've pushed all the registers now, so only r2-r4 hold anything
+ * useful. Move them into callee save registers */
+ or r2, ZERO, r28
+ or r3, ZERO, r29
+ or r4, ZERO, r30
+
+ /* Preserve r2 as the event code */
+ movi evt_debug, r3
+ ori r3, 1, r3
+ ptabs r3, tr0
+
+ or SP, ZERO, r6
+ getcon TRA, r5
+ blink tr0, LINK
+
+ or r28, ZERO, r2
+ or r29, ZERO, r3
+ or r30, ZERO, r4
+#endif
+
+
+ /* For syscall and debug race condition, get TRA now */
+ getcon TRA, r5
+
+ /* We are in a safe position to turn SR.BL off, but set IMASK=0xf
+ * Also set FD, to catch FPU usage in the kernel.
+ *
+ * benedict.gaster@superh.com 29/07/2002
+ *
+ * On all SH5-101 revisions it is unsafe to raise the IMASK and at the
+ * same time change BL from 1->0, as any pending interrupt of a level
+ * higher than he previous value of IMASK will leak through and be
+ * taken unexpectedly.
+ *
+ * To avoid this we raise the IMASK and then issue another PUTCON to
+ * enable interrupts.
+ */
+ getcon SR, r6
+ movi SR_IMASK | SR_FD, r7
+ or r6, r7, r6
+ putcon r6, SR
+ movi SR_UNBLOCK_EXC, r7
+ and r6, r7, r6
+ putcon r6, SR
+
+
+ /* Now call the appropriate 3rd level handler */
+ or r3, ZERO, LINK
+ movi trap_jtable, r3
+ shlri r2, 3, r2
+ ldx.l r2, r3, r3
+ shlri r2, 2, r2
+ ptabs r3, tr0
+ or SP, ZERO, r3
+ blink tr0, ZERO
+
+/*
+ * Second level handler for VBR-based exceptions. Post-handlers.
+ *
+ * Post-handlers for interrupts (ret_from_irq), exceptions
+ * (ret_from_exception) and common reentrance doors (restore_all
+ * to get back to the original context, ret_from_syscall loop to
+ * check kernel exiting).
+ *
+ * ret_with_reschedule and work_notifysig are an inner lables of
+ * the ret_from_syscall loop.
+ *
+ * In common to all stack-frame sensitive handlers.
+ *
+ * Inputs:
+ * (SP) struct pt_regs *, original register's frame pointer (basic)
+ *
+ */
+ .global ret_from_irq
+ret_from_irq:
+#if POOR_MANS_STRACE
+ pta evt_debug_ret_from_irq, tr0
+ ori SP, 0, r2
+ blink tr0, LINK
+#endif
+ ld.q SP, FRAME_S(FSSR), r6
+ shlri r6, 30, r6
+ andi r6, 1, r6
+ pta resume_kernel, tr0
+ bne r6, ZERO, tr0 /* no further checks */
+ STI()
+ pta ret_with_reschedule, tr0
+ blink tr0, ZERO /* Do not check softirqs */
+
+ .global ret_from_exception
+ret_from_exception:
+ preempt_stop()
+
+#if POOR_MANS_STRACE
+ pta evt_debug_ret_from_exc, tr0
+ ori SP, 0, r2
+ blink tr0, LINK
+#endif
+
+ ld.q SP, FRAME_S(FSSR), r6
+ shlri r6, 30, r6
+ andi r6, 1, r6
+ pta resume_kernel, tr0
+ bne r6, ZERO, tr0 /* no further checks */
+
+ /* Check softirqs */
+
+#ifdef CONFIG_PREEMPT
+ pta ret_from_syscall, tr0
+ blink tr0, ZERO
+
+resume_kernel:
+ pta restore_all, tr0
+
+ getcon KCR0, r6
+ ld.l r6, TI_PRE_COUNT, r7
+ beq/u r7, ZERO, tr0
+
+need_resched:
+ ld.l r6, TI_FLAGS, r7
+ movi (1 << TIF_NEED_RESCHED), r8
+ and r8, r7, r8
+ bne r8, ZERO, tr0
+
+ getcon SR, r7
+ andi r7, 0xf0, r7
+ bne r7, ZERO, tr0
+
+ movi ((PREEMPT_ACTIVE >> 16) & 65535), r8
+ shori (PREEMPT_ACTIVE & 65535), r8
+ st.l r6, TI_PRE_COUNT, r8
+
+ STI()
+ movi schedule, r7
+ ori r7, 1, r7
+ ptabs r7, tr1
+ blink tr1, LINK
+
+ st.l r6, TI_PRE_COUNT, ZERO
+ CLI()
+
+ pta need_resched, tr1
+ blink tr1, ZERO
+#endif
+
+ .global ret_from_syscall
+ret_from_syscall:
+
+ret_with_reschedule:
+ getcon KCR0, r6 ! r6 contains current_thread_info
+ ld.l r6, TI_FLAGS, r7 ! r7 contains current_thread_info->flags
+
+ ! FIXME:!!!
+ ! no handling of TIF_SYSCALL_TRACE yet!!
+
+ movi (1 << TIF_NEED_RESCHED), r8
+ and r8, r7, r8
+ pta work_resched, tr0
+ bne r8, ZERO, tr0
+
+ pta restore_all, tr1
+
+ movi (1 << TIF_SIGPENDING), r8
+ and r8, r7, r8
+ pta work_notifysig, tr0
+ bne r8, ZERO, tr0
+
+ blink tr1, ZERO
+
+work_resched:
+ pta ret_from_syscall, tr0
+ gettr tr0, LINK
+ movi schedule, r6
+ ptabs r6, tr0
+ blink tr0, ZERO /* Call schedule(), return on top */
+
+work_notifysig:
+ gettr tr1, LINK
+
+ movi do_signal, r6
+ ptabs r6, tr0
+ or SP, ZERO, r2
+ or ZERO, ZERO, r3
+ blink tr0, LINK /* Call do_signal(regs, 0), return here */
+
+restore_all:
+ /* Do prefetches */
+
+ ld.q SP, FRAME_T(0), r6
+ ld.q SP, FRAME_T(1), r7
+ ld.q SP, FRAME_T(2), r8
+ ld.q SP, FRAME_T(3), r9
+ ptabs r6, tr0
+ ptabs r7, tr1
+ ptabs r8, tr2
+ ptabs r9, tr3
+ ld.q SP, FRAME_T(4), r6
+ ld.q SP, FRAME_T(5), r7
+ ld.q SP, FRAME_T(6), r8
+ ld.q SP, FRAME_T(7), r9
+ ptabs r6, tr4
+ ptabs r7, tr5
+ ptabs r8, tr6
+ ptabs r9, tr7
+
+ ld.q SP, FRAME_R(0), r0
+ ld.q SP, FRAME_R(1), r1
+ ld.q SP, FRAME_R(2), r2
+ ld.q SP, FRAME_R(3), r3
+ ld.q SP, FRAME_R(4), r4
+ ld.q SP, FRAME_R(5), r5
+ ld.q SP, FRAME_R(6), r6
+ ld.q SP, FRAME_R(7), r7
+ ld.q SP, FRAME_R(8), r8
+ ld.q SP, FRAME_R(9), r9
+ ld.q SP, FRAME_R(10), r10
+ ld.q SP, FRAME_R(11), r11
+ ld.q SP, FRAME_R(12), r12
+ ld.q SP, FRAME_R(13), r13
+ ld.q SP, FRAME_R(14), r14
+
+ ld.q SP, FRAME_R(16), r16
+ ld.q SP, FRAME_R(17), r17
+ ld.q SP, FRAME_R(18), r18
+ ld.q SP, FRAME_R(19), r19
+ ld.q SP, FRAME_R(20), r20
+ ld.q SP, FRAME_R(21), r21
+ ld.q SP, FRAME_R(22), r22
+ ld.q SP, FRAME_R(23), r23
+ ld.q SP, FRAME_R(24), r24
+ ld.q SP, FRAME_R(25), r25
+ ld.q SP, FRAME_R(26), r26
+ ld.q SP, FRAME_R(27), r27
+ ld.q SP, FRAME_R(28), r28
+ ld.q SP, FRAME_R(29), r29
+ ld.q SP, FRAME_R(30), r30
+ ld.q SP, FRAME_R(31), r31
+ ld.q SP, FRAME_R(32), r32
+ ld.q SP, FRAME_R(33), r33
+ ld.q SP, FRAME_R(34), r34
+ ld.q SP, FRAME_R(35), r35
+ ld.q SP, FRAME_R(36), r36
+ ld.q SP, FRAME_R(37), r37
+ ld.q SP, FRAME_R(38), r38
+ ld.q SP, FRAME_R(39), r39
+ ld.q SP, FRAME_R(40), r40
+ ld.q SP, FRAME_R(41), r41
+ ld.q SP, FRAME_R(42), r42
+ ld.q SP, FRAME_R(43), r43
+ ld.q SP, FRAME_R(44), r44
+ ld.q SP, FRAME_R(45), r45
+ ld.q SP, FRAME_R(46), r46
+ ld.q SP, FRAME_R(47), r47
+ ld.q SP, FRAME_R(48), r48
+ ld.q SP, FRAME_R(49), r49
+ ld.q SP, FRAME_R(50), r50
+ ld.q SP, FRAME_R(51), r51
+ ld.q SP, FRAME_R(52), r52
+ ld.q SP, FRAME_R(53), r53
+ ld.q SP, FRAME_R(54), r54
+ ld.q SP, FRAME_R(55), r55
+ ld.q SP, FRAME_R(56), r56
+ ld.q SP, FRAME_R(57), r57
+ ld.q SP, FRAME_R(58), r58
+
+ getcon SR, r59
+ movi SR_BLOCK_EXC, r60
+ or r59, r60, r59
+ putcon r59, SR /* SR.BL = 1, keep nesting out */
+ ld.q SP, FRAME_S(FSSR), r61
+ ld.q SP, FRAME_S(FSPC), r62
+ movi SR_ASID_MASK, r60
+ and r59, r60, r59
+ andc r61, r60, r61 /* Clear out older ASID */
+ or r59, r61, r61 /* Retain current ASID */
+ putcon r61, SSR
+ putcon r62, SPC
+
+ /* Ignore FSYSCALL_ID */
+
+ ld.q SP, FRAME_R(59), r59
+ ld.q SP, FRAME_R(60), r60
+ ld.q SP, FRAME_R(61), r61
+ ld.q SP, FRAME_R(62), r62
+
+ /* Last touch */
+ ld.q SP, FRAME_R(15), SP
+ rte
+ nop
+
+/*
+ * Third level handlers for VBR-based exceptions. Adapting args to
+ * and/or deflecting to fourth level handlers.
+ *
+ * Fourth level handlers interface.
+ * Most are C-coded handlers directly pointed by the trap_jtable.
+ * (Third = Fourth level)
+ * Inputs:
+ * (r2) fault/interrupt code, entry number (e.g. NMI = 14,
+ * IRL0-3 (0000) = 16, RTLBMISS = 2, SYSCALL = 11, etc ...)
+ * (r3) struct pt_regs *, original register's frame pointer
+ * (r4) Event (0 = interrupt, 1 = TLB miss fault, 2 = Not TLB miss fault)
+ * (r5) TRA control register (for syscall/debug benefit only)
+ * (LINK) return address
+ * (SP) = r3
+ *
+ * Kernel TLB fault handlers will get a slightly different interface.
+ * (r2) struct pt_regs *, original register's frame pointer
+ * (r3) writeaccess, whether it's a store fault as opposed to load fault
+ * (r4) execaccess, whether it's a ITLB fault as opposed to DTLB fault
+ * (r5) Effective Address of fault
+ * (LINK) return address
+ * (SP) = r2
+ *
+ * fpu_error_or_IRQ? is a helper to deflect to the right cause.
+ *
+ */
+tlb_miss_load:
+ or SP, ZERO, r2
+ or ZERO, ZERO, r3 /* Read */
+ or ZERO, ZERO, r4 /* Data */
+ getcon TEA, r5
+ pta call_do_page_fault, tr0
+ beq ZERO, ZERO, tr0
+
+tlb_miss_store:
+ or SP, ZERO, r2
+ movi 1, r3 /* Write */
+ or ZERO, ZERO, r4 /* Data */
+ getcon TEA, r5
+ pta call_do_page_fault, tr0
+ beq ZERO, ZERO, tr0
+
+itlb_miss_or_IRQ:
+ pta its_IRQ, tr0
+ beqi/u r4, EVENT_INTERRUPT, tr0
+ or SP, ZERO, r2
+ or ZERO, ZERO, r3 /* Read */
+ movi 1, r4 /* Text */
+ getcon TEA, r5
+ /* Fall through */
+
+call_do_page_fault:
+ movi do_page_fault, r6
+ ptabs r6, tr0
+ blink tr0, ZERO
+
+fpu_error_or_IRQA:
+ pta its_IRQ, tr0
+ beqi/l r4, EVENT_INTERRUPT, tr0
+#ifndef CONFIG_NOFPU_SUPPORT
+ movi do_fpu_state_restore, r6
+#else
+ movi do_exception_error, r6
+#endif
+ ptabs r6, tr0
+ blink tr0, ZERO
+
+fpu_error_or_IRQB:
+ pta its_IRQ, tr0
+ beqi/l r4, EVENT_INTERRUPT, tr0
+#ifndef CONFIG_NOFPU_SUPPORT
+ movi do_fpu_state_restore, r6
+#else
+ movi do_exception_error, r6
+#endif
+ ptabs r6, tr0
+ blink tr0, ZERO
+
+its_IRQ:
+ movi do_IRQ, r6
+ ptabs r6, tr0
+ blink tr0, ZERO
+
+/*
+ * system_call/unknown_trap third level handler:
+ *
+ * Inputs:
+ * (r2) fault/interrupt code, entry number (TRAP = 11)
+ * (r3) struct pt_regs *, original register's frame pointer
+ * (r4) Not used. Event (0=interrupt, 1=TLB miss fault, 2=Not TLB miss fault)
+ * (r5) TRA Control Reg (0x00xyzzzz: x=1 SYSCALL, y = #args, z=nr)
+ * (SP) = r3
+ * (LINK) return address: ret_from_exception
+ * (*r3) Syscall parms: SC#, arg0, arg1, ..., arg5 in order (Saved r2/r7)
+ *
+ * Outputs:
+ * (*r3) Syscall reply (Saved r2)
+ * (LINK) In case of syscall only it can be scrapped.
+ * Common second level post handler will be ret_from_syscall.
+ * Common (non-trace) exit point to that is syscall_ret (saving
+ * result to r2). Common bad exit point is syscall_bad (returning
+ * ENOSYS then saved to r2).
+ *
+ */
+
+unknown_trap:
+ /* Unknown Trap or User Trace */
+ movi do_unknown_trapa, r6
+ ptabs r6, tr0
+ ld.q r3, FRAME_R(9), r2 /* r2 = #arg << 16 | syscall # */
+ andi r2, 0x1ff, r2 /* r2 = syscall # */
+ blink tr0, LINK
+
+ pta syscall_ret, tr0
+ blink tr0, ZERO
+
+ /* New syscall implementation*/
+system_call:
+ pta unknown_trap, tr0
+ or r5, ZERO, r4 /* TRA (=r5) -> r4 */
+ shlri r4, 20, r4
+ bnei r4, 1, tr0 /* unknown_trap if not 0x1yzzzz */
+
+ /* It's a system call */
+ st.q r3, FRAME_S(FSYSCALL_ID), r5 /* ID (0x1yzzzz) -> stack */
+ andi r5, 0x1ff, r5 /* syscall # -> r5 */
+
+ STI()
+
+ pta syscall_allowed, tr0
+ movi NR_syscalls - 1, r4 /* Last valid */
+ bgeu/l r4, r5, tr0
+
+syscall_bad:
+ /* Return ENOSYS ! */
+ movi -(ENOSYS), r2 /* Fall-through */
+
+ .global syscall_ret
+syscall_ret:
+ st.q SP, FRAME_R(9), r2 /* Expecting SP back to BASIC frame */
+
+#if POOR_MANS_STRACE
+ /* nothing useful in registers at this point */
+
+ movi evt_debug2, r5
+ ori r5, 1, r5
+ ptabs r5, tr0
+ ld.q SP, FRAME_R(9), r2
+ or SP, ZERO, r3
+ blink tr0, LINK
+#endif
+
+ ld.q SP, FRAME_S(FSPC), r2
+ addi r2, 4, r2 /* Move PC, being pre-execution event */
+ st.q SP, FRAME_S(FSPC), r2
+ pta ret_from_syscall, tr0
+ blink tr0, ZERO
+
+
+/* A different return path for ret_from_fork, because we now need
+ * to call schedule_tail with the later kernels. Because prev is
+ * loaded into r2 by switch_to() means we can just call it straight away
+ */
+
+.global ret_from_fork
+ret_from_fork:
+
+ movi schedule_tail,r5
+ ori r5, 1, r5
+ ptabs r5, tr0
+ blink tr0, LINK
+
+#if POOR_MANS_STRACE
+ /* nothing useful in registers at this point */
+
+ movi evt_debug2, r5
+ ori r5, 1, r5
+ ptabs r5, tr0
+ ld.q SP, FRAME_R(9), r2
+ or SP, ZERO, r3
+ blink tr0, LINK
+#endif
+
+ ld.q SP, FRAME_S(FSPC), r2
+ addi r2, 4, r2 /* Move PC, being pre-execution event */
+ st.q SP, FRAME_S(FSPC), r2
+ pta ret_from_syscall, tr0
+ blink tr0, ZERO
+
+
+
+syscall_allowed:
+ /* Use LINK to deflect the exit point, default is syscall_ret */
+ pta syscall_ret, tr0
+ gettr tr0, LINK
+ pta syscall_notrace, tr0
+
+ getcon KCR0, r2
+ ld.l r2, TI_FLAGS, r4
+ movi (1 << TIF_SYSCALL_TRACE), r6
+ and r6, r4, r6
+ beq/l r6, ZERO, tr0
+
+ /* Trace it by calling syscall_trace before and after */
+ movi syscall_trace, r4
+ ptabs r4, tr0
+ blink tr0, LINK
+ /* Reload syscall number as r5 is trashed by syscall_trace */
+ ld.q SP, FRAME_S(FSYSCALL_ID), r5
+ andi r5, 0x1ff, r5
+
+ pta syscall_ret_trace, tr0
+ gettr tr0, LINK
+
+syscall_notrace:
+ /* Now point to the appropriate 4th level syscall handler */
+ movi sys_call_table, r4
+ shlli r5, 2, r5
+ ldx.l r4, r5, r5
+ ptabs r5, tr0
+
+ /* Prepare original args */
+ ld.q SP, FRAME_R(2), r2
+ ld.q SP, FRAME_R(3), r3
+ ld.q SP, FRAME_R(4), r4
+ ld.q SP, FRAME_R(5), r5
+ ld.q SP, FRAME_R(6), r6
+ ld.q SP, FRAME_R(7), r7
+
+ /* And now the trick for those syscalls requiring regs * ! */
+ or SP, ZERO, r8
+
+ /* Call it */
+ blink tr0, ZERO /* LINK is already properly set */
+
+syscall_ret_trace:
+ /* We get back here only if under trace */
+ st.q SP, FRAME_R(9), r2 /* Save return value */
+
+ movi syscall_trace, LINK
+ ptabs LINK, tr0
+ blink tr0, LINK
+
+ /* This needs to be done after any syscall tracing */
+ ld.q SP, FRAME_S(FSPC), r2
+ addi r2, 4, r2 /* Move PC, being pre-execution event */
+ st.q SP, FRAME_S(FSPC), r2
+
+ pta ret_from_syscall, tr0
+ blink tr0, ZERO /* Resume normal return sequence */
+
+/*
+ * --- Switch to running under a particular ASID and return the previous ASID value
+ * --- The caller is assumed to have done a cli before calling this.
+ *
+ * Input r2 : new ASID
+ * Output r2 : old ASID
+ */
+
+ .global switch_and_save_asid
+switch_and_save_asid:
+ getcon sr, r0
+ movi 255, r4
+ shlli r4, 16, r4 /* r4 = mask to select ASID */
+ and r0, r4, r3 /* r3 = shifted old ASID */
+ andi r2, 255, r2 /* mask down new ASID */
+ shlli r2, 16, r2 /* align new ASID against SR.ASID */
+ andc r0, r4, r0 /* efface old ASID from SR */
+ or r0, r2, r0 /* insert the new ASID */
+ putcon r0, ssr
+ movi 1f, r0
+ putcon r0, spc
+ rte
+ nop
+1:
+ ptabs LINK, tr0
+ shlri r3, 16, r2 /* r2 = old ASID */
+ blink tr0, r63
+
+ .global route_to_panic_handler
+route_to_panic_handler:
+ /* Switch to real mode, goto panic_handler, don't return. Useful for
+ last-chance debugging, e.g. if no output wants to go to the console.
+ */
+
+ movi panic_handler - CONFIG_CACHED_MEMORY_OFFSET, r1
+ ptabs r1, tr0
+ pta 1f, tr1
+ gettr tr1, r0
+ putcon r0, spc
+ getcon sr, r0
+ movi 1, r1
+ shlli r1, 31, r1
+ andc r0, r1, r0
+ putcon r0, ssr
+ rte
+ nop
+1: /* Now in real mode */
+ blink tr0, r63
+ nop
+
+ .global peek_real_address_q
+peek_real_address_q:
+ /* Two args:
+ r2 : real mode address to peek
+ r2(out) : result quadword
+
+ This is provided as a cheapskate way of manipulating device
+ registers for debugging (to avoid the need to onchip_remap the debug
+ module, and to avoid the need to onchip_remap the watchpoint
+ controller in a way that identity maps sufficient bits to avoid the
+ SH5-101 cut2 silicon defect).
+
+ This code is not performance critical
+ */
+
+ add.l r2, r63, r2 /* sign extend address */
+ getcon sr, r0 /* r0 = saved original SR */
+ movi 1, r1
+ shlli r1, 28, r1
+ or r0, r1, r1 /* r0 with block bit set */
+ putcon r1, sr /* now in critical section */
+ movi 1, r36
+ shlli r36, 31, r36
+ andc r1, r36, r1 /* turn sr.mmu off in real mode section */
+
+ putcon r1, ssr
+ movi .peek0 - CONFIG_CACHED_MEMORY_OFFSET, r36 /* real mode target address */
+ movi 1f, r37 /* virtual mode return addr */
+ putcon r36, spc
+
+ synco
+ rte
+ nop
+
+.peek0: /* come here in real mode, don't touch caches!!
+ still in critical section (sr.bl==1) */
+ putcon r0, ssr
+ putcon r37, spc
+ /* Here's the actual peek. If the address is bad, all bets are now off
+ * what will happen (handlers invoked in real-mode = bad news) */
+ ld.q r2, 0, r2
+ synco
+ rte /* Back to virtual mode */
+ nop
+
+1:
+ ptabs LINK, tr0
+ blink tr0, r63
+
+ .global poke_real_address_q
+poke_real_address_q:
+ /* Two args:
+ r2 : real mode address to poke
+ r3 : quadword value to write.
+
+ This is provided as a cheapskate way of manipulating device
+ registers for debugging (to avoid the need to onchip_remap the debug
+ module, and to avoid the need to onchip_remap the watchpoint
+ controller in a way that identity maps sufficient bits to avoid the
+ SH5-101 cut2 silicon defect).
+
+ This code is not performance critical
+ */
+
+ add.l r2, r63, r2 /* sign extend address */
+ getcon sr, r0 /* r0 = saved original SR */
+ movi 1, r1
+ shlli r1, 28, r1
+ or r0, r1, r1 /* r0 with block bit set */
+ putcon r1, sr /* now in critical section */
+ movi 1, r36
+ shlli r36, 31, r36
+ andc r1, r36, r1 /* turn sr.mmu off in real mode section */
+
+ putcon r1, ssr
+ movi .poke0-CONFIG_CACHED_MEMORY_OFFSET, r36 /* real mode target address */
+ movi 1f, r37 /* virtual mode return addr */
+ putcon r36, spc
+
+ synco
+ rte
+ nop
+
+.poke0: /* come here in real mode, don't touch caches!!
+ still in critical section (sr.bl==1) */
+ putcon r0, ssr
+ putcon r37, spc
+ /* Here's the actual poke. If the address is bad, all bets are now off
+ * what will happen (handlers invoked in real-mode = bad news) */
+ st.q r2, 0, r3
+ synco
+ rte /* Back to virtual mode */
+ nop
+
+1:
+ ptabs LINK, tr0
+ blink tr0, r63
+
+/*
+ * --- User Access Handling Section
+ */
+
+/*
+ * User Access support. It all moved to non inlined Assembler
+ * functions in here.
+ *
+ * __kernel_size_t __copy_user(void *__to, const void *__from,
+ * __kernel_size_t __n)
+ *
+ * Inputs:
+ * (r2) target address
+ * (r3) source address
+ * (r4) size in bytes
+ *
+ * Ouputs:
+ * (*r2) target data
+ * (r2) non-copied bytes
+ *
+ * If a fault occurs on the user pointer, bail out early and return the
+ * number of bytes not copied in r2.
+ * Strategy : for large blocks, call a real memcpy function which can
+ * move >1 byte at a time using unaligned ld/st instructions, and can
+ * manipulate the cache using prefetch + alloco to improve the speed
+ * further. If a fault occurs in that function, just revert to the
+ * byte-by-byte approach used for small blocks; this is rare so the
+ * performance hit for that case does not matter.
+ *
+ * For small blocks it's not worth the overhead of setting up and calling
+ * the memcpy routine; do the copy a byte at a time.
+ *
+ */
+ .global __copy_user
+__copy_user:
+ pta __copy_user_byte_by_byte, tr1
+ movi 16, r0 ! this value is a best guess, should tune it by benchmarking
+ bge/u r0, r4, tr1
+ pta copy_user_memcpy, tr0
+ addi SP, -32, SP
+ /* Save arguments in case we have to fix-up unhandled page fault */
+ st.q SP, 0, r2
+ st.q SP, 8, r3
+ st.q SP, 16, r4
+ st.q SP, 24, r35 ! r35 is callee-save
+ /* Save LINK in a register to reduce RTS time later (otherwise
+ ld SP,*,LINK;ptabs LINK;trn;blink trn,r63 becomes a critical path) */
+ ori LINK, 0, r35
+ blink tr0, LINK
+
+ /* Copy completed normally if we get back here */
+ ptabs r35, tr0
+ ld.q SP, 24, r35
+ /* don't restore r2-r4, pointless */
+ /* set result=r2 to zero as the copy must have succeeded. */
+ or r63, r63, r2
+ addi SP, 32, SP
+ blink tr0, r63 ! RTS
+
+ .global __copy_user_fixup
+__copy_user_fixup:
+ /* Restore stack frame */
+ ori r35, 0, LINK
+ ld.q SP, 24, r35
+ ld.q SP, 16, r4
+ ld.q SP, 8, r3
+ ld.q SP, 0, r2
+ addi SP, 32, SP
+ /* Fall through to original code, in the 'same' state we entered with */
+
+/* The slow byte-by-byte method is used if the fast copy traps due to a bad
+ user address. In that rare case, the speed drop can be tolerated. */
+__copy_user_byte_by_byte:
+ pta ___copy_user_exit, tr1
+ pta ___copy_user1, tr0
+ beq/u r4, r63, tr1 /* early exit for zero length copy */
+ sub r2, r3, r0
+ addi r0, -1, r0
+
+___copy_user1:
+ ld.b r3, 0, r5 /* Fault address 1 */
+
+ /* Could rewrite this to use just 1 add, but the second comes 'free'
+ due to load latency */
+ addi r3, 1, r3
+ addi r4, -1, r4 /* No real fixup required */
+___copy_user2:
+ stx.b r3, r0, r5 /* Fault address 2 */
+ bne r4, ZERO, tr0
+
+___copy_user_exit:
+ or r4, ZERO, r2
+ ptabs LINK, tr0
+ blink tr0, ZERO
+
+/*
+ * __kernel_size_t __clear_user(void *addr, __kernel_size_t size)
+ *
+ * Inputs:
+ * (r2) target address
+ * (r3) size in bytes
+ *
+ * Ouputs:
+ * (*r2) zero-ed target data
+ * (r2) non-zero-ed bytes
+ */
+ .global __clear_user
+__clear_user:
+ pta ___clear_user_exit, tr1
+ pta ___clear_user1, tr0
+ beq/u r3, r63, tr1
+
+___clear_user1:
+ st.b r2, 0, ZERO /* Fault address */
+ addi r2, 1, r2
+ addi r3, -1, r3 /* No real fixup required */
+ bne r3, ZERO, tr0
+
+___clear_user_exit:
+ or r3, ZERO, r2
+ ptabs LINK, tr0
+ blink tr0, ZERO
+
+
+/*
+ * int __strncpy_from_user(unsigned long __dest, unsigned long __src,
+ * int __count)
+ *
+ * Inputs:
+ * (r2) target address
+ * (r3) source address
+ * (r4) maximum size in bytes
+ *
+ * Ouputs:
+ * (*r2) copied data
+ * (r2) -EFAULT (in case of faulting)
+ * copied data (otherwise)
+ */
+ .global __strncpy_from_user
+__strncpy_from_user:
+ pta ___strncpy_from_user1, tr0
+ pta ___strncpy_from_user_done, tr1
+ or r4, ZERO, r5 /* r5 = original count */
+ beq/u r4, r63, tr1 /* early exit if r4==0 */
+ movi -(EFAULT), r6 /* r6 = reply, no real fixup */
+ or ZERO, ZERO, r7 /* r7 = data, clear top byte of data */
+
+___strncpy_from_user1:
+ ld.b r3, 0, r7 /* Fault address: only in reading */
+ st.b r2, 0, r7
+ addi r2, 1, r2
+ addi r3, 1, r3
+ beq/u ZERO, r7, tr1
+ addi r4, -1, r4 /* return real number of copied bytes */
+ bne/l ZERO, r4, tr0
+
+___strncpy_from_user_done:
+ sub r5, r4, r6 /* If done, return copied */
+
+___strncpy_from_user_exit:
+ or r6, ZERO, r2
+ ptabs LINK, tr0
+ blink tr0, ZERO
+
+/*
+ * extern long __strnlen_user(const char *__s, long __n)
+ *
+ * Inputs:
+ * (r2) source address
+ * (r3) source size in bytes
+ *
+ * Ouputs:
+ * (r2) -EFAULT (in case of faulting)
+ * string length (otherwise)
+ */
+ .global __strnlen_user
+__strnlen_user:
+ pta ___strnlen_user_set_reply, tr0
+ pta ___strnlen_user1, tr1
+ or ZERO, ZERO, r5 /* r5 = counter */
+ movi -(EFAULT), r6 /* r6 = reply, no real fixup */
+ or ZERO, ZERO, r7 /* r7 = data, clear top byte of data */
+ beq r3, ZERO, tr0
+
+___strnlen_user1:
+ ldx.b r2, r5, r7 /* Fault address: only in reading */
+ addi r3, -1, r3 /* No real fixup */
+ addi r5, 1, r5
+ beq r3, ZERO, tr0
+ bne r7, ZERO, tr1
+! The line below used to be active. This meant led to a junk byte lying between each pair
+! of entries in the argv & envp structures in memory. Whilst the program saw the right data
+! via the argv and envp arguments to main, it meant the 'flat' representation visible through
+! /proc/$pid/cmdline was corrupt, causing trouble with ps, for example.
+! addi r5, 1, r5 /* Include '\0' */
+
+___strnlen_user_set_reply:
+ or r5, ZERO, r6 /* If done, return counter */
+
+___strnlen_user_exit:
+ or r6, ZERO, r2
+ ptabs LINK, tr0
+ blink tr0, ZERO
+
+/*
+ * extern long __get_user_asm_?(void *val, long addr)
+ *
+ * Inputs:
+ * (r2) dest address
+ * (r3) source address (in User Space)
+ *
+ * Ouputs:
+ * (r2) -EFAULT (faulting)
+ * 0 (not faulting)
+ */
+ .global __get_user_asm_b
+__get_user_asm_b:
+ or r2, ZERO, r4
+ movi -(EFAULT), r2 /* r2 = reply, no real fixup */
+
+___get_user_asm_b1:
+ ld.b r3, 0, r5 /* r5 = data */
+ st.b r4, 0, r5
+ or ZERO, ZERO, r2
+
+___get_user_asm_b_exit:
+ ptabs LINK, tr0
+ blink tr0, ZERO
+
+
+ .global __get_user_asm_w
+__get_user_asm_w:
+ or r2, ZERO, r4
+ movi -(EFAULT), r2 /* r2 = reply, no real fixup */
+
+___get_user_asm_w1:
+ ld.w r3, 0, r5 /* r5 = data */
+ st.w r4, 0, r5
+ or ZERO, ZERO, r2
+
+___get_user_asm_w_exit:
+ ptabs LINK, tr0
+ blink tr0, ZERO
+
+
+ .global __get_user_asm_l
+__get_user_asm_l:
+ or r2, ZERO, r4
+ movi -(EFAULT), r2 /* r2 = reply, no real fixup */
+
+___get_user_asm_l1:
+ ld.l r3, 0, r5 /* r5 = data */
+ st.l r4, 0, r5
+ or ZERO, ZERO, r2
+
+___get_user_asm_l_exit:
+ ptabs LINK, tr0
+ blink tr0, ZERO
+
+
+ .global __get_user_asm_q
+__get_user_asm_q:
+ or r2, ZERO, r4
+ movi -(EFAULT), r2 /* r2 = reply, no real fixup */
+
+___get_user_asm_q1:
+ ld.q r3, 0, r5 /* r5 = data */
+ st.q r4, 0, r5
+ or ZERO, ZERO, r2
+
+___get_user_asm_q_exit:
+ ptabs LINK, tr0
+ blink tr0, ZERO
+
+/*
+ * extern long __put_user_asm_?(void *pval, long addr)
+ *
+ * Inputs:
+ * (r2) kernel pointer to value
+ * (r3) dest address (in User Space)
+ *
+ * Ouputs:
+ * (r2) -EFAULT (faulting)
+ * 0 (not faulting)
+ */
+ .global __put_user_asm_b
+__put_user_asm_b:
+ ld.b r2, 0, r4 /* r4 = data */
+ movi -(EFAULT), r2 /* r2 = reply, no real fixup */
+
+___put_user_asm_b1:
+ st.b r3, 0, r4
+ or ZERO, ZERO, r2
+
+___put_user_asm_b_exit:
+ ptabs LINK, tr0
+ blink tr0, ZERO
+
+
+ .global __put_user_asm_w
+__put_user_asm_w:
+ ld.w r2, 0, r4 /* r4 = data */
+ movi -(EFAULT), r2 /* r2 = reply, no real fixup */
+
+___put_user_asm_w1:
+ st.w r3, 0, r4
+ or ZERO, ZERO, r2
+
+___put_user_asm_w_exit:
+ ptabs LINK, tr0
+ blink tr0, ZERO
+
+
+ .global __put_user_asm_l
+__put_user_asm_l:
+ ld.l r2, 0, r4 /* r4 = data */
+ movi -(EFAULT), r2 /* r2 = reply, no real fixup */
+
+___put_user_asm_l1:
+ st.l r3, 0, r4
+ or ZERO, ZERO, r2
+
+___put_user_asm_l_exit:
+ ptabs LINK, tr0
+ blink tr0, ZERO
+
+
+ .global __put_user_asm_q
+__put_user_asm_q:
+ ld.q r2, 0, r4 /* r4 = data */
+ movi -(EFAULT), r2 /* r2 = reply, no real fixup */
+
+___put_user_asm_q1:
+ st.q r3, 0, r4
+ or ZERO, ZERO, r2
+
+___put_user_asm_q_exit:
+ ptabs LINK, tr0
+ blink tr0, ZERO
+
+panic_stash_regs:
+ /* The idea is : when we get an unhandled panic, we dump the registers
+ to a known memory location, the just sit in a tight loop.
+ This allows the human to look at the memory region through the GDB
+ session (assuming the debug module's SHwy initiator isn't locked up
+ or anything), to hopefully analyze the cause of the panic. */
+
+ /* On entry, former r15 (SP) is in DCR
+ former r0 is at resvec_saved_area + 0
+ former r1 is at resvec_saved_area + 8
+ former tr0 is at resvec_saved_area + 32
+ DCR is the only register whose value is lost altogether.
+ */
+
+ movi 0xffffffff80000000, r0 ! phy of dump area
+ ld.q SP, 0x000, r1 ! former r0
+ st.q r0, 0x000, r1
+ ld.q SP, 0x008, r1 ! former r1
+ st.q r0, 0x008, r1
+ st.q r0, 0x010, r2
+ st.q r0, 0x018, r3
+ st.q r0, 0x020, r4
+ st.q r0, 0x028, r5
+ st.q r0, 0x030, r6
+ st.q r0, 0x038, r7
+ st.q r0, 0x040, r8
+ st.q r0, 0x048, r9
+ st.q r0, 0x050, r10
+ st.q r0, 0x058, r11
+ st.q r0, 0x060, r12
+ st.q r0, 0x068, r13
+ st.q r0, 0x070, r14
+ getcon dcr, r14
+ st.q r0, 0x078, r14
+ st.q r0, 0x080, r16
+ st.q r0, 0x088, r17
+ st.q r0, 0x090, r18
+ st.q r0, 0x098, r19
+ st.q r0, 0x0a0, r20
+ st.q r0, 0x0a8, r21
+ st.q r0, 0x0b0, r22
+ st.q r0, 0x0b8, r23
+ st.q r0, 0x0c0, r24
+ st.q r0, 0x0c8, r25
+ st.q r0, 0x0d0, r26
+ st.q r0, 0x0d8, r27
+ st.q r0, 0x0e0, r28
+ st.q r0, 0x0e8, r29
+ st.q r0, 0x0f0, r30
+ st.q r0, 0x0f8, r31
+ st.q r0, 0x100, r32
+ st.q r0, 0x108, r33
+ st.q r0, 0x110, r34
+ st.q r0, 0x118, r35
+ st.q r0, 0x120, r36
+ st.q r0, 0x128, r37
+ st.q r0, 0x130, r38
+ st.q r0, 0x138, r39
+ st.q r0, 0x140, r40
+ st.q r0, 0x148, r41
+ st.q r0, 0x150, r42
+ st.q r0, 0x158, r43
+ st.q r0, 0x160, r44
+ st.q r0, 0x168, r45
+ st.q r0, 0x170, r46
+ st.q r0, 0x178, r47
+ st.q r0, 0x180, r48
+ st.q r0, 0x188, r49
+ st.q r0, 0x190, r50
+ st.q r0, 0x198, r51
+ st.q r0, 0x1a0, r52
+ st.q r0, 0x1a8, r53
+ st.q r0, 0x1b0, r54
+ st.q r0, 0x1b8, r55
+ st.q r0, 0x1c0, r56
+ st.q r0, 0x1c8, r57
+ st.q r0, 0x1d0, r58
+ st.q r0, 0x1d8, r59
+ st.q r0, 0x1e0, r60
+ st.q r0, 0x1e8, r61
+ st.q r0, 0x1f0, r62
+ st.q r0, 0x1f8, r63 ! bogus, but for consistency's sake...
+
+ ld.q SP, 0x020, r1 ! former tr0
+ st.q r0, 0x200, r1
+ gettr tr1, r1
+ st.q r0, 0x208, r1
+ gettr tr2, r1
+ st.q r0, 0x210, r1
+ gettr tr3, r1
+ st.q r0, 0x218, r1
+ gettr tr4, r1
+ st.q r0, 0x220, r1
+ gettr tr5, r1
+ st.q r0, 0x228, r1
+ gettr tr6, r1
+ st.q r0, 0x230, r1
+ gettr tr7, r1
+ st.q r0, 0x238, r1
+
+ getcon sr, r1
+ getcon ssr, r2
+ getcon pssr, r3
+ getcon spc, r4
+ getcon pspc, r5
+ getcon intevt, r6
+ getcon expevt, r7
+ getcon pexpevt, r8
+ getcon tra, r9
+ getcon tea, r10
+ getcon kcr0, r11
+ getcon kcr1, r12
+ getcon vbr, r13
+ getcon resvec, r14
+
+ st.q r0, 0x240, r1
+ st.q r0, 0x248, r2
+ st.q r0, 0x250, r3
+ st.q r0, 0x258, r4
+ st.q r0, 0x260, r5
+ st.q r0, 0x268, r6
+ st.q r0, 0x270, r7
+ st.q r0, 0x278, r8
+ st.q r0, 0x280, r9
+ st.q r0, 0x288, r10
+ st.q r0, 0x290, r11
+ st.q r0, 0x298, r12
+ st.q r0, 0x2a0, r13
+ st.q r0, 0x2a8, r14
+
+ getcon SPC,r2
+ getcon SSR,r3
+ getcon EXPEVT,r4
+ /* Prepare to jump to C - physical address */
+ movi panic_handler-CONFIG_CACHED_MEMORY_OFFSET, r1
+ ori r1, 1, r1
+ ptabs r1, tr0
+ getcon DCR, SP
+ blink tr0, ZERO
+ nop
+ nop
+ nop
+ nop
+
+
+
+
+/*
+ * --- Signal Handling Section
+ */
+
+/*
+ * extern long long _sa_default_rt_restorer
+ * extern long long _sa_default_restorer
+ *
+ * or, better,
+ *
+ * extern void _sa_default_rt_restorer(void)
+ * extern void _sa_default_restorer(void)
+ *
+ * Code prototypes to do a sys_rt_sigreturn() or sys_sysreturn()
+ * from user space. Copied into user space by signal management.
+ * Both must be quad aligned and 2 quad long (4 instructions).
+ *
+ */
+ .balign 8
+ .global sa_default_rt_restorer
+sa_default_rt_restorer:
+ movi 0x10, r9
+ shori __NR_rt_sigreturn, r9
+ trapa r9
+ nop
+
+ .balign 8
+ .global sa_default_restorer
+sa_default_restorer:
+ movi 0x10, r9
+ shori __NR_sigreturn, r9
+ trapa r9
+ nop
+
+/*
+ * --- __ex_table Section
+ */
+
+/*
+ * User Access Exception Table.
+ */
+ .section __ex_table, "a"
+
+ .global asm_uaccess_start /* Just a marker */
+asm_uaccess_start:
+
+ .long ___copy_user1, ___copy_user_exit
+ .long ___copy_user2, ___copy_user_exit
+ .long ___clear_user1, ___clear_user_exit
+ .long ___strncpy_from_user1, ___strncpy_from_user_exit
+ .long ___strnlen_user1, ___strnlen_user_exit
+ .long ___get_user_asm_b1, ___get_user_asm_b_exit
+ .long ___get_user_asm_w1, ___get_user_asm_w_exit
+ .long ___get_user_asm_l1, ___get_user_asm_l_exit
+ .long ___get_user_asm_q1, ___get_user_asm_q_exit
+ .long ___put_user_asm_b1, ___put_user_asm_b_exit
+ .long ___put_user_asm_w1, ___put_user_asm_w_exit
+ .long ___put_user_asm_l1, ___put_user_asm_l_exit
+ .long ___put_user_asm_q1, ___put_user_asm_q_exit
+
+ .global asm_uaccess_end /* Just a marker */
+asm_uaccess_end:
+
+
+
+
+/*
+ * --- .text.init Section
+ */
+
+ .section .text.init, "ax"
+
+/*
+ * void trap_init (void)
+ *
+ */
+ .global trap_init
+trap_init:
+ addi SP, -24, SP /* Room to save r28/r29/r30 */
+ st.q SP, 0, r28
+ st.q SP, 8, r29
+ st.q SP, 16, r30
+
+ /* Set VBR and RESVEC */
+ movi LVBR_block, r19
+ andi r19, -4, r19 /* reset MMUOFF + reserved */
+ /* For RESVEC exceptions we force the MMU off, which means we need the
+ physical address. */
+ movi LRESVEC_block-CONFIG_CACHED_MEMORY_OFFSET, r20
+ andi r20, -4, r20 /* reset reserved */
+ ori r20, 1, r20 /* set MMUOFF */
+ putcon r19, VBR
+ putcon r20, RESVEC
+
+ /* Sanity check */
+ movi LVBR_block_end, r21
+ andi r21, -4, r21
+ movi BLOCK_SIZE, r29 /* r29 = expected size */
+ or r19, ZERO, r30
+ add r19, r29, r19
+
+ /*
+ * Ugly, but better loop forever now than crash afterwards.
+ * We should print a message, but if we touch LVBR or
+ * LRESVEC blocks we should not be surprised if we get stuck
+ * in trap_init().
+ */
+ pta trap_init_loop, tr1
+ gettr tr1, r28 /* r28 = trap_init_loop */
+ sub r21, r30, r30 /* r30 = actual size */
+
+ /*
+ * VBR/RESVEC handlers overlap by being bigger than
+ * allowed. Very bad. Just loop forever.
+ * (r28) panic/loop address
+ * (r29) expected size
+ * (r30) actual size
+ */
+trap_init_loop:
+ bne r19, r21, tr1
+
+ /* Now that exception vectors are set up reset SR.BL */
+ getcon SR, r22
+ movi SR_UNBLOCK_EXC, r23
+ and r22, r23, r22
+ putcon r22, SR
+
+ addi SP, 24, SP
+ ptabs LINK, tr0
+ blink tr0, ZERO
+
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/kernel/head.S
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003, 2004 Paul Mundt
+ *
+ *
+ * benedict.gaster@superh.com: 2nd May 2002
+ * Moved definition of empty_zero_page to its own section allowing
+ * it to be placed at an absolute address known at load time.
+ *
+ * lethal@linux-sh.org: 9th May 2003
+ * Kill off GLOBAL_NAME() usage.
+ *
+ * lethal@linux-sh.org: 8th May 2004
+ * Add early SCIF console DTLB mapping.
+ */
+
+#include <linux/config.h>
+
+#include <asm/page.h>
+#include <asm/mmu_context.h>
+#include <asm/cache.h>
+#include <asm/tlb.h>
+#include <asm/processor.h>
+#include <asm/registers.h>
+#include <asm/thread_info.h>
+
+/*
+ * MMU defines: TLB boundaries.
+ */
+
+#define MMUIR_FIRST ITLB_FIXED
+#define MMUIR_END ITLB_LAST_VAR_UNRESTRICTED+TLB_STEP
+#define MMUIR_STEP TLB_STEP
+
+#define MMUDR_FIRST DTLB_FIXED
+#define MMUDR_END DTLB_LAST_VAR_UNRESTRICTED+TLB_STEP
+#define MMUDR_STEP TLB_STEP
+
+/* Safety check : CONFIG_CACHED_MEMORY_OFFSET has to be a multiple of 512Mb */
+#if (CONFIG_CACHED_MEMORY_OFFSET & ((1UL<<29)-1))
+#error "CONFIG_CACHED_MEMORY_OFFSET must be a multiple of 512Mb"
+#endif
+
+/*
+ * MMU defines: Fixed TLBs.
+ */
+/* Deal safely with the case where the base of RAM is not 512Mb aligned */
+
+#define ALIGN_512M_MASK (0xffffffffe0000000)
+#define ALIGNED_EFFECTIVE ((CONFIG_CACHED_MEMORY_OFFSET + CONFIG_MEMORY_START) & ALIGN_512M_MASK)
+#define ALIGNED_PHYSICAL (CONFIG_MEMORY_START & ALIGN_512M_MASK)
+
+#define MMUIR_TEXT_H (0x0000000000000003 | ALIGNED_EFFECTIVE)
+ /* Enabled, Shared, ASID 0, Eff. Add. 0xA0000000 */
+
+#define MMUIR_TEXT_L (0x000000000000009a | ALIGNED_PHYSICAL)
+ /* 512 Mb, Cacheable, Write-back, execute, Not User, Ph. Add. */
+
+#define MMUDR_CACHED_H 0x0000000000000003 | ALIGNED_EFFECTIVE
+ /* Enabled, Shared, ASID 0, Eff. Add. 0xA0000000 */
+#define MMUDR_CACHED_L 0x000000000000015a | ALIGNED_PHYSICAL
+ /* 512 Mb, Cacheable, Write-back, read/write, Not User, Ph. Add. */
+
+#ifdef CONFIG_ICACHE_DISABLED
+#define ICCR0_INIT_VAL ICCR0_OFF /* ICACHE off */
+#else
+#define ICCR0_INIT_VAL ICCR0_ON | ICCR0_ICI /* ICE + ICI */
+#endif
+#define ICCR1_INIT_VAL ICCR1_NOLOCK /* No locking */
+
+#if defined (CONFIG_DCACHE_DISABLED)
+#define OCCR0_INIT_VAL OCCR0_OFF /* D-cache: off */
+#elif defined (CONFIG_DCACHE_WRITE_THROUGH)
+#define OCCR0_INIT_VAL OCCR0_ON | OCCR0_OCI | OCCR0_WT /* D-cache: on, */
+ /* WT, invalidate */
+#elif defined (CONFIG_DCACHE_WRITE_BACK)
+#define OCCR0_INIT_VAL OCCR0_ON | OCCR0_OCI | OCCR0_WB /* D-cache: on, */
+ /* WB, invalidate */
+#else
+#error preprocessor flag CONFIG_DCACHE_... not recognized!
+#endif
+
+#define OCCR1_INIT_VAL OCCR1_NOLOCK /* No locking */
+
+ .section .empty_zero_page, "aw"
+ .global empty_zero_page
+
+empty_zero_page:
+ .long 1 /* MOUNT_ROOT_RDONLY */
+ .long 0 /* RAMDISK_FLAGS */
+ .long 0x0200 /* ORIG_ROOT_DEV */
+ .long 1 /* LOADER_TYPE */
+ .long 0x00360000 /* INITRD_START */
+ .long 0x000a0000 /* INITRD_SIZE */
+ .long 0
+
+ .text
+ .balign 4096,0,4096
+
+ .section .data, "aw"
+ .balign PAGE_SIZE
+
+ .section .data, "aw"
+ .balign PAGE_SIZE
+
+ .global swapper_pg_dir
+swapper_pg_dir:
+ .space PAGE_SIZE, 0
+
+ .global empty_bad_page
+empty_bad_page:
+ .space PAGE_SIZE, 0
+
+ .global empty_bad_pte_table
+empty_bad_pte_table:
+ .space PAGE_SIZE, 0
+
+ .global fpu_in_use
+fpu_in_use: .quad 0
+
+
+ .section .text, "ax"
+ .balign L1_CACHE_BYTES
+/*
+ * Condition at the entry of __stext:
+ * . Reset state:
+ * . SR.FD = 1 (FPU disabled)
+ * . SR.BL = 1 (Exceptions disabled)
+ * . SR.MD = 1 (Privileged Mode)
+ * . SR.MMU = 0 (MMU Disabled)
+ * . SR.CD = 0 (CTC User Visible)
+ * . SR.IMASK = Undefined (Interrupt Mask)
+ *
+ * Operations supposed to be performed by __stext:
+ * . prevent speculative fetch onto device memory while MMU is off
+ * . reflect as much as possible SH5 ABI (r15, r26, r27, r18)
+ * . first, save CPU state and set it to something harmless
+ * . any CPU detection and/or endianness settings (?)
+ * . initialize EMI/LMI (but not TMU/RTC/INTC/SCIF): TBD
+ * . set initial TLB entries for cached and uncached regions
+ * (no fine granularity paging)
+ * . set initial cache state
+ * . enable MMU and caches
+ * . set CPU to a consistent state
+ * . registers (including stack pointer and current/KCR0)
+ * . NOT expecting to set Exception handling nor VBR/RESVEC/DCR
+ * at this stage. This is all to later Linux initialization steps.
+ * . initialize FPU
+ * . clear BSS
+ * . jump into start_kernel()
+ * . be prepared to hopeless start_kernel() returns.
+ *
+ */
+ .global _stext
+_stext:
+ /*
+ * Prevent speculative fetch on device memory due to
+ * uninitialized target registers.
+ */
+ ptabs/u ZERO, tr0
+ ptabs/u ZERO, tr1
+ ptabs/u ZERO, tr2
+ ptabs/u ZERO, tr3
+ ptabs/u ZERO, tr4
+ ptabs/u ZERO, tr5
+ ptabs/u ZERO, tr6
+ ptabs/u ZERO, tr7
+ synci
+
+ /*
+ * Read/Set CPU state. After this block:
+ * r29 = Initial SR
+ */
+ getcon SR, r29
+ movi SR_HARMLESS, r20
+ putcon r20, SR
+
+ /*
+ * Initialize EMI/LMI. To Be Done.
+ */
+
+ /*
+ * CPU detection and/or endianness settings (?). To Be Done.
+ * Pure PIC code here, please ! Just save state into r30.
+ * After this block:
+ * r30 = CPU type/Platform Endianness
+ */
+
+ /*
+ * Set initial TLB entries for cached and uncached regions.
+ * Note: PTA/BLINK is PIC code, PTABS/BLINK isn't !
+ */
+ /* Clear ITLBs */
+ pta clear_ITLB, tr1
+ movi MMUIR_FIRST, r21
+ movi MMUIR_END, r22
+clear_ITLB:
+ putcfg r21, 0, ZERO /* Clear MMUIR[n].PTEH.V */
+ addi r21, MMUIR_STEP, r21
+ bne r21, r22, tr1
+
+ /* Clear DTLBs */
+ pta clear_DTLB, tr1
+ movi MMUDR_FIRST, r21
+ movi MMUDR_END, r22
+clear_DTLB:
+ putcfg r21, 0, ZERO /* Clear MMUDR[n].PTEH.V */
+ addi r21, MMUDR_STEP, r21
+ bne r21, r22, tr1
+
+ /* Map one big (512Mb) page for ITLB */
+ movi MMUIR_FIRST, r21
+ movi MMUIR_TEXT_L, r22 /* PTEL first */
+ add.l r22, r63, r22 /* Sign extend */
+ putcfg r21, 1, r22 /* Set MMUIR[0].PTEL */
+ movi MMUIR_TEXT_H, r22 /* PTEH last */
+ add.l r22, r63, r22 /* Sign extend */
+ putcfg r21, 0, r22 /* Set MMUIR[0].PTEH */
+
+ /* Map one big CACHED (512Mb) page for DTLB */
+ movi MMUDR_FIRST, r21
+ movi MMUDR_CACHED_L, r22 /* PTEL first */
+ add.l r22, r63, r22 /* Sign extend */
+ putcfg r21, 1, r22 /* Set MMUDR[0].PTEL */
+ movi MMUDR_CACHED_H, r22 /* PTEH last */
+ add.l r22, r63, r22 /* Sign extend */
+ putcfg r21, 0, r22 /* Set MMUDR[0].PTEH */
+
+#ifdef CONFIG_EARLY_PRINTK
+ /*
+ * Setup a DTLB translation for SCIF phys.
+ */
+ addi r21, MMUDR_STEP, r21
+ movi 0x0a03, r22 /* SCIF phys */
+ shori 0x0148, r22
+ putcfg r21, 1, r22 /* PTEL first */
+ movi 0xfa03, r22 /* 0xfa030000, fixed SCIF virt */
+ shori 0x0003, r22
+ putcfg r21, 0, r22 /* PTEH last */
+#endif
+
+ /*
+ * Set cache behaviours.
+ */
+ /* ICache */
+ movi ICCR_BASE, r21
+ movi ICCR0_INIT_VAL, r22
+ movi ICCR1_INIT_VAL, r23
+ putcfg r21, ICCR_REG0, r22
+ putcfg r21, ICCR_REG1, r23
+
+ /* OCache */
+ movi OCCR_BASE, r21
+ movi OCCR0_INIT_VAL, r22
+ movi OCCR1_INIT_VAL, r23
+ putcfg r21, OCCR_REG0, r22
+ putcfg r21, OCCR_REG1, r23
+
+
+ /*
+ * Enable Caches and MMU. Do the first non-PIC jump.
+ * Now head.S global variables, constants and externs
+ * can be used.
+ */
+ getcon SR, r21
+ movi SR_ENABLE_MMU, r22
+ or r21, r22, r21
+ putcon r21, SSR
+ movi hyperspace, r22
+ ori r22, 1, r22 /* Make it SHmedia, not required but..*/
+ putcon r22, SPC
+ synco
+ rte /* And now go into the hyperspace ... */
+hyperspace: /* ... that's the next instruction ! */
+
+ /*
+ * Set CPU to a consistent state.
+ * r31 = FPU support flag
+ * tr0/tr7 in use. Others give a chance to loop somewhere safe
+ */
+ movi start_kernel, r32
+ ori r32, 1, r32
+
+ ptabs r32, tr0 /* r32 = _start_kernel address */
+ pta/u hopeless, tr1
+ pta/u hopeless, tr2
+ pta/u hopeless, tr3
+ pta/u hopeless, tr4
+ pta/u hopeless, tr5
+ pta/u hopeless, tr6
+ pta/u hopeless, tr7
+ gettr tr1, r28 /* r28 = hopeless address */
+
+ /* Set initial stack pointer */
+ movi init_thread_union, SP
+ putcon SP, KCR0 /* Set current to init_task */
+ movi THREAD_SIZE, r22 /* Point to the end */
+ add SP, r22, SP
+
+ /*
+ * Initialize FPU.
+ * Keep FPU flag in r31. After this block:
+ * r31 = FPU flag
+ */
+ movi fpu_in_use, r31 /* Temporary */
+
+#ifndef CONFIG_NOFPU_SUPPORT
+ getcon SR, r21
+ movi SR_ENABLE_FPU, r22
+ and r21, r22, r22
+ putcon r22, SR /* Try to enable */
+ getcon SR, r22
+ xor r21, r22, r21
+ shlri r21, 15, r21 /* Supposedly 0/1 */
+ st.q r31, 0 , r21 /* Set fpu_in_use */
+#else
+ movi 0, r21
+ st.q r31, 0 , r21 /* Set fpu_in_use */
+#endif
+ or r21, ZERO, r31 /* Set FPU flag at last */
+
+#ifndef CONFIG_SH_NO_BSS_INIT
+/* Don't clear BSS if running on slow platforms such as an RTL simulation,
+ remote memory via SHdebug link, etc. For these the memory can be guaranteed
+ to be all zero on boot anyway. */
+ /*
+ * Clear bss
+ */
+ pta clear_quad, tr1
+ movi __bss_start, r22
+ movi _end, r23
+clear_quad:
+ st.q r22, 0, ZERO
+ addi r22, 8, r22
+ bne r22, r23, tr1 /* Both quad aligned, see vmlinux.lds.S */
+#endif
+ pta/u hopeless, tr1
+
+ /* Say bye to head.S but be prepared to wrongly get back ... */
+ blink tr0, LINK
+
+ /* If we ever get back here through LINK/tr1-tr7 */
+ pta/u hopeless, tr7
+
+hopeless:
+ /*
+ * Something's badly wrong here. Loop endlessly,
+ * there's nothing more we can do about it.
+ *
+ * Note on hopeless: it can be jumped into invariably
+ * before or after jumping into hyperspace. The only
+ * requirement is to be PIC called (PTA) before and
+ * any way (PTA/PTABS) after. According to Virtual
+ * to Physical mapping a simulator/emulator can easily
+ * tell where we came here from just looking at hopeless
+ * (PC) address.
+ *
+ * For debugging purposes:
+ * (r28) hopeless/loop address
+ * (r29) Original SR
+ * (r30) CPU type/Platform endianness
+ * (r31) FPU Support
+ * (r32) _start_kernel address
+ */
+ blink tr7, ZERO
+
+
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/kernel/irq.c
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003 Paul Mundt
+ *
+ */
+
+/*
+ * IRQs are in fact implemented a bit like signal handlers for the kernel.
+ * Naturally it's not a 1:1 relation, but there are similarities.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/seq_file.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/smp.h>
+#include <asm/pgalloc.h>
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <linux/irq.h>
+
+/*
+ * Controller mappings for all interrupt sources:
+ */
+irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = {
+ [0 ... NR_IRQS-1] = {
+ .handler = &no_irq_type,
+ .lock = SPIN_LOCK_UNLOCKED
+ }
+};
+
+
+/*
+ * Special irq handlers.
+ */
+
+irqreturn_t no_action(int cpl, void *dev_id, struct pt_regs *regs)
+{
+ return IRQ_NONE;
+}
+
+/*
+ * Generic no controller code
+ */
+
+static void enable_none(unsigned int irq) { }
+static unsigned int startup_none(unsigned int irq) { return 0; }
+static void disable_none(unsigned int irq) { }
+static void ack_none(unsigned int irq)
+{
+/*
+ * 'what should we do if we get a hw irq event on an illegal vector'.
+ * each architecture has to answer this themselves, it doesnt deserve
+ * a generic callback i think.
+ */
+ printk("unexpected IRQ trap at irq %02x\n", irq);
+}
+
+/* startup is the same as "enable", shutdown is same as "disable" */
+#define shutdown_none disable_none
+#define end_none enable_none
+
+struct hw_interrupt_type no_irq_type = {
+ "none",
+ startup_none,
+ shutdown_none,
+ enable_none,
+ disable_none,
+ ack_none,
+ end_none
+};
+
+#if defined(CONFIG_PROC_FS)
+int show_interrupts(struct seq_file *p, void *v)
+{
+ int i = *(loff_t *) v, j;
+ struct irqaction * action;
+ unsigned long flags;
+
+ if (i == 0) {
+ seq_puts(p, " ");
+ for (j=0; j<NR_CPUS; j++)
+ if (cpu_online(j))
+ seq_printf(p, "CPU%d ",j);
+ seq_putc(p, '\n');
+ }
+
+ if (i < NR_IRQS) {
+ spin_lock_irqsave(&irq_desc[i].lock, flags);
+ action = irq_desc[i].action;
+ if (!action)
+ goto unlock;
+ seq_printf(p, "%3d: ",i);
+ seq_printf(p, "%10u ", kstat_irqs(i));
+ seq_printf(p, " %14s", irq_desc[i].handler->typename);
+ seq_printf(p, " %s", action->name);
+
+ for (action=action->next; action; action = action->next)
+ seq_printf(p, ", %s", action->name);
+ seq_putc(p, '\n');
+unlock:
+ spin_unlock_irqrestore(&irq_desc[i].lock, flags);
+ }
+ return 0;
+}
+#endif
+
+/*
+ * do_NMI handles all Non-Maskable Interrupts.
+ */
+asmlinkage void do_NMI(unsigned long vector_num, struct pt_regs * regs)
+{
+ if (regs->sr & 0x40000000)
+ printk("unexpected NMI trap in system mode\n");
+ else
+ printk("unexpected NMI trap in user mode\n");
+
+ /* No statistics */
+}
+
+/*
+ * This should really return information about whether
+ * we should do bottom half handling etc. Right now we
+ * end up _always_ checking the bottom half, which is a
+ * waste of time and is not what some drivers would
+ * prefer.
+ */
+int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action)
+{
+ int status;
+
+ status = 1; /* Force the "do bottom halves" bit */
+
+ if (!(action->flags & SA_INTERRUPT))
+ local_irq_enable();
+
+ do {
+ status |= action->flags;
+ action->handler(irq, action->dev_id, regs);
+ action = action->next;
+ } while (action);
+ if (status & SA_SAMPLE_RANDOM)
+ add_interrupt_randomness(irq);
+
+ local_irq_disable();
+
+ return status;
+}
+
+/*
+ * Generic enable/disable code: this just calls
+ * down into the PIC-specific version for the actual
+ * hardware disable after having gotten the irq
+ * controller lock.
+ */
+
+/**
+ * disable_irq_nosync - disable an irq without waiting
+ * @irq: Interrupt to disable
+ *
+ * Disable the selected interrupt line. Disables of an interrupt
+ * stack. Unlike disable_irq(), this function does not ensure existing
+ * instances of the IRQ handler have completed before returning.
+ *
+ * This function may be called from IRQ context.
+ */
+void disable_irq_nosync(unsigned int irq)
+{
+ irq_desc_t *desc = irq_desc + irq;
+ unsigned long flags;
+
+ spin_lock_irqsave(&desc->lock, flags);
+ if (!desc->depth++) {
+ desc->status |= IRQ_DISABLED;
+ desc->handler->disable(irq);
+ }
+ spin_unlock_irqrestore(&desc->lock, flags);
+}
+
+/**
+ * disable_irq - disable an irq and wait for completion
+ * @irq: Interrupt to disable
+ *
+ * Disable the selected interrupt line. Disables of an interrupt
+ * stack. That is for two disables you need two enables. This
+ * function waits for any pending IRQ handlers for this interrupt
+ * to complete before returning. If you use this function while
+ * holding a resource the IRQ handler may need you will deadlock.
+ *
+ * This function may be called - with care - from IRQ context.
+ */
+void disable_irq(unsigned int irq)
+{
+ disable_irq_nosync(irq);
+ synchronize_irq(irq);
+}
+
+/**
+ * enable_irq - enable interrupt handling on an irq
+ * @irq: Interrupt to enable
+ *
+ * Re-enables the processing of interrupts on this IRQ line
+ * providing no disable_irq calls are now in effect.
+ *
+ * This function may be called from IRQ context.
+ */
+void enable_irq(unsigned int irq)
+{
+ irq_desc_t *desc = irq_desc + irq;
+ unsigned long flags;
+
+ spin_lock_irqsave(&desc->lock, flags);
+ switch (desc->depth) {
+ case 1: {
+ unsigned int status = desc->status & ~IRQ_DISABLED;
+ desc->status = status;
+ if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) {
+ desc->status = status | IRQ_REPLAY;
+ hw_resend_irq(desc->handler,irq);
+ }
+ desc->handler->enable(irq);
+ /* fall-through */
+ }
+ default:
+ desc->depth--;
+ break;
+ case 0:
+ printk("enable_irq() unbalanced from %p\n",
+ __builtin_return_address(0));
+ }
+ spin_unlock_irqrestore(&desc->lock, flags);
+}
+
+/*
+ * do_IRQ handles all normal device IRQ's.
+ */
+asmlinkage int do_IRQ(unsigned long vector_num, struct pt_regs * regs)
+{
+ /*
+ * We ack quickly, we don't want the irq controller
+ * thinking we're snobs just because some other CPU has
+ * disabled global interrupts (we have already done the
+ * INT_ACK cycles, it's too late to try to pretend to the
+ * controller that we aren't taking the interrupt).
+ *
+ * 0 return value means that this irq is already being
+ * handled by some other CPU. (or is disabled)
+ */
+ int irq;
+ int cpu = smp_processor_id();
+ irq_desc_t *desc = NULL;
+ struct irqaction * action;
+ unsigned int status;
+
+ irq_enter();
+
+#ifdef CONFIG_PREEMPT
+ /*
+ * At this point we're now about to actually call handlers,
+ * and interrupts might get reenabled during them... bump
+ * preempt_count to prevent any preemption while the handler
+ * called here is pending...
+ */
+ preempt_disable();
+#endif
+
+ irq = irq_demux(vector_num);
+
+ /*
+ * Should never happen, if it does check
+ * vectorN_to_IRQ[] against trap_jtable[].
+ */
+ if (irq == -1) {
+ printk("unexpected IRQ trap at vector %03lx\n", vector_num);
+ goto out;
+ }
+
+ desc = irq_desc + irq;
+
+ kstat_cpu(cpu).irqs[irq]++;
+ spin_lock(&desc->lock);
+ desc->handler->ack(irq);
+ /*
+ REPLAY is when Linux resends an IRQ that was dropped earlier
+ WAITING is used by probe to mark irqs that are being tested
+ */
+ status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING | IRQ_INPROGRESS);
+ status |= IRQ_PENDING; /* we _want_ to handle it */
+
+ /*
+ * If the IRQ is disabled for whatever reason, we cannot
+ * use the action we have.
+ */
+ action = NULL;
+ if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
+ action = desc->action;
+ status &= ~IRQ_PENDING; /* we commit to handling */
+ status |= IRQ_INPROGRESS; /* we are handling it */
+ }
+ desc->status = status;
+
+ /*
+ * If there is no IRQ handler or it was disabled, exit early.
+ Since we set PENDING, if another processor is handling
+ a different instance of this same irq, the other processor
+ will take care of it.
+ */
+ if (!action)
+ goto out;
+
+ /*
+ * Edge triggered interrupts need to remember
+ * pending events.
+ * This applies to any hw interrupts that allow a second
+ * instance of the same irq to arrive while we are in do_IRQ
+ * or in the handler. But the code here only handles the _second_
+ * instance of the irq, not the third or fourth. So it is mostly
+ * useful for irq hardware that does not mask cleanly in an
+ * SMP environment.
+ */
+ for (;;) {
+ spin_unlock(&desc->lock);
+ handle_IRQ_event(irq, regs, action);
+ spin_lock(&desc->lock);
+
+ if (!(desc->status & IRQ_PENDING))
+ break;
+ desc->status &= ~IRQ_PENDING;
+ }
+ desc->status &= ~IRQ_INPROGRESS;
+out:
+ /*
+ * The ->end() handler has to deal with interrupts which got
+ * disabled while the handler was running.
+ */
+ if (desc) {
+ desc->handler->end(irq);
+ spin_unlock(&desc->lock);
+ }
+
+ irq_exit();
+
+#ifdef CONFIG_PREEMPT
+ /*
+ * We're done with the handlers, interrupts should be
+ * currently disabled; decrement preempt_count now so
+ * as we return preemption may be allowed...
+ */
+ preempt_enable_no_resched();
+#endif
+
+ return 1;
+}
+
+/**
+ * request_irq - allocate an interrupt line
+ * @irq: Interrupt line to allocate
+ * @handler: Function to be called when the IRQ occurs
+ * @irqflags: Interrupt type flags
+ * @devname: An ascii name for the claiming device
+ * @dev_id: A cookie passed back to the handler function
+ *
+ * This call allocates interrupt resources and enables the
+ * interrupt line and IRQ handling. From the point this
+ * call is made your handler function may be invoked. Since
+ * your handler function must clear any interrupt the board
+ * raises, you must take care both to initialise your hardware
+ * and to set up the interrupt handler in the right order.
+ *
+ * Dev_id must be globally unique. Normally the address of the
+ * device data structure is used as the cookie. Since the handler
+ * receives this value it makes sense to use it.
+ *
+ * If your interrupt is shared you must pass a non NULL dev_id
+ * as this is required when freeing the interrupt.
+ *
+ * Flags:
+ *
+ * SA_SHIRQ Interrupt is shared
+ *
+ * SA_INTERRUPT Disable local interrupts while processing
+ *
+ * SA_SAMPLE_RANDOM The interrupt can be used for entropy
+ *
+ */
+int request_irq(unsigned int irq,
+ irqreturn_t (*handler)(int, void *, struct pt_regs *),
+ unsigned long irqflags,
+ const char * devname,
+ void *dev_id)
+{
+ int retval;
+ struct irqaction * action;
+
+#if 1
+ /*
+ * Sanity-check: shared interrupts should REALLY pass in
+ * a real dev-ID, otherwise we'll have trouble later trying
+ * to figure out which interrupt is which (messes up the
+ * interrupt freeing logic etc).
+ */
+ if (irqflags & SA_SHIRQ) {
+ if (!dev_id)
+ printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n", devname, (&irq)[-1]);
+ }
+#endif
+
+ if (irq >= NR_IRQS)
+ return -EINVAL;
+ if (!handler)
+ return -EINVAL;
+
+ action = (struct irqaction *)
+ kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+ if (!action)
+ return -ENOMEM;
+
+ action->handler = handler;
+ action->flags = irqflags;
+ cpus_clear(action->mask);
+ action->name = devname;
+ action->next = NULL;
+ action->dev_id = dev_id;
+
+ retval = setup_irq(irq, action);
+ if (retval)
+ kfree(action);
+ return retval;
+}
+
+/**
+ * free_irq - free an interrupt
+ * @irq: Interrupt line to free
+ * @dev_id: Device identity to free
+ *
+ * Remove an interrupt handler. The handler is removed and if the
+ * interrupt line is no longer in use by any driver it is disabled.
+ * On a shared IRQ the caller must ensure the interrupt is disabled
+ * on the card it drives before calling this function. The function
+ * does not return until any executing interrupts for this IRQ
+ * have completed.
+ *
+ * This function may be called from interrupt context.
+ *
+ * Bugs: Attempting to free an irq in a handler for the same irq hangs
+ * the machine.
+ */
+void free_irq(unsigned int irq, void *dev_id)
+{
+ irq_desc_t *desc;
+ struct irqaction **p;
+ unsigned long flags;
+
+ if (irq >= NR_IRQS)
+ return;
+
+ desc = irq_desc + irq;
+ spin_lock_irqsave(&desc->lock,flags);
+ p = &desc->action;
+ for (;;) {
+ struct irqaction * action = *p;
+ if (action) {
+ struct irqaction **pp = p;
+ p = &action->next;
+ if (action->dev_id != dev_id)
+ continue;
+
+ /* Found it - now remove it from the list of entries */
+ *pp = action->next;
+ if (!desc->action) {
+ desc->status |= IRQ_DISABLED;
+ desc->handler->shutdown(irq);
+ }
+ spin_unlock_irqrestore(&desc->lock,flags);
+ kfree(action);
+ return;
+ }
+ printk("Trying to free free IRQ%d\n",irq);
+ spin_unlock_irqrestore(&desc->lock,flags);
+ return;
+ }
+}
+
+/*
+ * IRQ autodetection code..
+ *
+ * This depends on the fact that any interrupt that
+ * comes in on to an unassigned handler will get stuck
+ * with "IRQ_WAITING" cleared and the interrupt
+ * disabled.
+ */
+
+/**
+ * probe_irq_on - begin an interrupt autodetect
+ *
+ * Commence probing for an interrupt. The interrupts are scanned
+ * and a mask of potential interrupt lines is returned.
+ *
+ */
+unsigned long probe_irq_on(void)
+{
+ unsigned int i;
+ irq_desc_t *desc;
+ unsigned long val;
+ unsigned long delay;
+
+ /*
+ * something may have generated an irq long ago and we want to
+ * flush such a longstanding irq before considering it as spurious.
+ */
+ for (i = NR_IRQS-1; i >= 0; i--) {
+ desc = irq_desc + i;
+
+ spin_lock_irq(&desc->lock);
+ if (!irq_desc[i].action) {
+ irq_desc[i].handler->startup(i);
+ }
+ spin_unlock_irq(&desc->lock);
+ }
+
+ /* Wait for longstanding interrupts to trigger. */
+ for (delay = jiffies + HZ/50; time_after(delay, jiffies); )
+ /* about 20ms delay */ synchronize_irq();
+
+ /*
+ * enable any unassigned irqs
+ * (we must startup again here because if a longstanding irq
+ * happened in the previous stage, it may have masked itself)
+ */
+ for (i = NR_IRQS-1; i >= 0; i--) {
+ desc = irq_desc + 1;
+
+ spin_lock_irq(&desc->lock);
+ if (!desc->action) {
+ desc->status |= IRQ_AUTODETECT | IRQ_WAITING;
+ if (desc->handler->startup(i))
+ desc->status |= IRQ_PENDING;
+ }
+ spin_unlock_irq(&desc->lock);
+ }
+
+ /*
+ * Wait for spurious interrupts to trigger
+ */
+ for (delay = jiffies + HZ/10; time_after(delay, jiffies); )
+ /* about 100ms delay */ synchronize_irq();
+
+ /*
+ * Now filter out any obviously spurious interrupts
+ */
+ val = 0;
+ for (i = 0; i < NR_IRQS; i++) {
+ irq_desc_t *desc = irq_desc + i;
+ unsigned int status;
+
+ spin_lock_irq(&desc->lock);
+ status = desc->status;
+
+ if (status & IRQ_AUTODETECT) {
+ /* It triggered already - consider it spurious. */
+ if (!(status & IRQ_WAITING)) {
+ desc->status = status & ~IRQ_AUTODETECT;
+ desc->handler->shutdown(i);
+ } else
+ if (i < 32)
+ val |= 1 << i;
+ }
+ spin_unlock_irq(&desc->lock);
+ }
+
+ return val;
+}
+
+/*
+ * Return the one interrupt that triggered (this can
+ * handle any interrupt source).
+ */
+
+/**
+ * probe_irq_off - end an interrupt autodetect
+ * @val: mask of potential interrupts (unused)
+ *
+ * Scans the unused interrupt lines and returns the line which
+ * appears to have triggered the interrupt. If no interrupt was
+ * found then zero is returned. If more than one interrupt is
+ * found then minus the first candidate is returned to indicate
+ * their is doubt.
+ *
+ * The interrupt probe logic state is returned to its previous
+ * value.
+ *
+ * BUGS: When used in a module (which arguably shouldnt happen)
+ * nothing prevents two IRQ probe callers from overlapping. The
+ * results of this are non-optimal.
+ */
+int probe_irq_off(unsigned long val)
+{
+ int i, irq_found, nr_irqs;
+
+ nr_irqs = 0;
+ irq_found = 0;
+ for (i=0; i<NR_IRQS; i++) {
+ irq_desc_t *desc = irq_desc + i;
+ unsigned int status;
+
+ spin_lock_irq(&desc->lock);
+ status = desc->status;
+ if (!(status & IRQ_AUTODETECT))
+ continue;
+
+ if (status & IRQ_AUTODETECT) {
+ if (!(status & IRQ_WAITING)) {
+ if (!nr_irqs)
+ irq_found = i;
+ nr_irqs++;
+ }
+
+ desc->status = status & ~IRQ_AUTODETECT;
+ desc->handler->shutdown(i);
+ }
+ spin_unlock_irq(&desc->lock);
+ }
+
+ if (nr_irqs > 1)
+ irq_found = -irq_found;
+ return irq_found;
+}
+
+int setup_irq(unsigned int irq, struct irqaction * new)
+{
+ int shared = 0;
+ unsigned long flags;
+ struct irqaction *old, **p;
+ irq_desc_t *desc = irq_desc + irq;
+
+ /*
+ * Some drivers like serial.c use request_irq() heavily,
+ * so we have to be careful not to interfere with a
+ * running system.
+ */
+ if (new->flags & SA_SAMPLE_RANDOM) {
+ /*
+ * This function might sleep, we want to call it first,
+ * outside of the atomic block.
+ * Yes, this might clear the entropy pool if the wrong
+ * driver is attempted to be loaded, without actually
+ * installing a new handler, but is this really a problem,
+ * only the sysadmin is able to do this.
+ */
+ rand_initialize_irq(irq);
+ }
+
+ /*
+ * The following block of code has to be executed atomically
+ */
+ spin_lock_irqsave(&desc->lock,flags);
+ p = &desc->action;
+ if ((old = *p) != NULL) {
+ /* Can't share interrupts unless both agree to */
+ if (!(old->flags & new->flags & SA_SHIRQ)) {
+ spin_unlock_irqrestore(&desc->lock,flags);
+ return -EBUSY;
+ }
+
+ /* add new interrupt at end of irq queue */
+ do {
+ p = &old->next;
+ old = *p;
+ } while (old);
+ shared = 1;
+ }
+
+ *p = new;
+
+ if (!shared) {
+ desc->depth = 0;
+ desc->status &= ~IRQ_DISABLED;
+ desc->handler->startup(irq);
+ }
+ spin_unlock_irqrestore(&desc->lock,flags);
+
+ /*
+ * No PROC FS support for interrupts.
+ * For improvements in this area please check
+ * the i386 branch.
+ */
+ return 0;
+}
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_SYSCTL)
+
+void init_irq_proc(void)
+{
+ /*
+ * No PROC FS support for interrupts.
+ * For improvements in this area please check
+ * the i386 branch.
+ */
+}
+#endif
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/kernel/irq_intc.c
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003 Paul Mundt
+ *
+ * Interrupt Controller support for SH5 INTC.
+ * Per-interrupt selective. IRLM=0 (Fixed priority) is not
+ * supported being useless without a cascaded interrupt
+ * controller.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+
+#include <asm/hardware.h>
+#include <asm/platform.h>
+#include <asm/bitops.h> /* this includes also <asm/registers.h */
+ /* which is required to remap register */
+ /* names used into __asm__ blocks... */
+#include <asm/page.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+/*
+ * Maybe the generic Peripheral block could move to a more
+ * generic include file. INTC Block will be defined here
+ * and only here to make INTC self-contained in a single
+ * file.
+ */
+#define INTC_BLOCK_OFFSET 0x01000000
+
+/* Base */
+#define INTC_BASE PHYS_PERIPHERAL_BLOCK + \
+ INTC_BLOCK_OFFSET
+
+/* Address */
+#define INTC_ICR_SET (intc_virt + 0x0)
+#define INTC_ICR_CLEAR (intc_virt + 0x8)
+#define INTC_INTPRI_0 (intc_virt + 0x10)
+#define INTC_INTSRC_0 (intc_virt + 0x50)
+#define INTC_INTSRC_1 (intc_virt + 0x58)
+#define INTC_INTREQ_0 (intc_virt + 0x60)
+#define INTC_INTREQ_1 (intc_virt + 0x68)
+#define INTC_INTENB_0 (intc_virt + 0x70)
+#define INTC_INTENB_1 (intc_virt + 0x78)
+#define INTC_INTDSB_0 (intc_virt + 0x80)
+#define INTC_INTDSB_1 (intc_virt + 0x88)
+
+#define INTC_ICR_IRLM 0x1
+#define INTC_INTPRI_PREGS 8 /* 8 Priority Registers */
+#define INTC_INTPRI_PPREG 8 /* 8 Priorities per Register */
+
+
+/*
+ * Mapper between the vector ordinal and the IRQ number
+ * passed to kernel/device drivers.
+ */
+int intc_evt_to_irq[(0xE20/0x20)+1] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, /* 0x000 - 0x0E0 */
+ -1, -1, -1, -1, -1, -1, -1, -1, /* 0x100 - 0x1E0 */
+ 0, 0, 0, 0, 0, 1, 0, 0, /* 0x200 - 0x2E0 */
+ 2, 0, 0, 3, 0, 0, 0, -1, /* 0x300 - 0x3E0 */
+ 32, 33, 34, 35, 36, 37, 38, -1, /* 0x400 - 0x4E0 */
+ -1, -1, -1, 63, -1, -1, -1, -1, /* 0x500 - 0x5E0 */
+ -1, -1, 18, 19, 20, 21, 22, -1, /* 0x600 - 0x6E0 */
+ 39, 40, 41, 42, -1, -1, -1, -1, /* 0x700 - 0x7E0 */
+ 4, 5, 6, 7, -1, -1, -1, -1, /* 0x800 - 0x8E0 */
+ -1, -1, -1, -1, -1, -1, -1, -1, /* 0x900 - 0x9E0 */
+ 12, 13, 14, 15, 16, 17, -1, -1, /* 0xA00 - 0xAE0 */
+ -1, -1, -1, -1, -1, -1, -1, -1, /* 0xB00 - 0xBE0 */
+ -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC00 - 0xCE0 */
+ -1, -1, -1, -1, -1, -1, -1, -1, /* 0xD00 - 0xDE0 */
+ -1, -1 /* 0xE00 - 0xE20 */
+};
+
+/*
+ * Opposite mapper.
+ */
+static int IRQ_to_vectorN[NR_INTC_IRQS] = {
+ 0x12, 0x15, 0x18, 0x1B, 0x40, 0x41, 0x42, 0x43, /* 0- 7 */
+ -1, -1, -1, -1, 0x50, 0x51, 0x52, 0x53, /* 8-15 */
+ 0x54, 0x55, 0x32, 0x33, 0x34, 0x35, 0x36, -1, /* 16-23 */
+ -1, -1, -1, -1, -1, -1, -1, -1, /* 24-31 */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x38, /* 32-39 */
+ 0x39, 0x3A, 0x3B, -1, -1, -1, -1, -1, /* 40-47 */
+ -1, -1, -1, -1, -1, -1, -1, -1, /* 48-55 */
+ -1, -1, -1, -1, -1, -1, -1, 0x2B, /* 56-63 */
+
+};
+
+static unsigned long intc_virt;
+
+static unsigned int startup_intc_irq(unsigned int irq);
+static void shutdown_intc_irq(unsigned int irq);
+static void enable_intc_irq(unsigned int irq);
+static void disable_intc_irq(unsigned int irq);
+static void mask_and_ack_intc(unsigned int);
+static void end_intc_irq(unsigned int irq);
+
+static struct hw_interrupt_type intc_irq_type = {
+ "INTC",
+ startup_intc_irq,
+ shutdown_intc_irq,
+ enable_intc_irq,
+ disable_intc_irq,
+ mask_and_ack_intc,
+ end_intc_irq
+};
+
+static int irlm; /* IRL mode */
+
+static unsigned int startup_intc_irq(unsigned int irq)
+{
+ enable_intc_irq(irq);
+ return 0; /* never anything pending */
+}
+
+static void shutdown_intc_irq(unsigned int irq)
+{
+ disable_intc_irq(irq);
+}
+
+static void enable_intc_irq(unsigned int irq)
+{
+ unsigned long reg;
+ unsigned long bitmask;
+
+ if ((irq <= IRQ_IRL3) && (irlm == NO_PRIORITY))
+ printk("Trying to use straight IRL0-3 with an encoding platform.\n");
+
+ if (irq < 32) {
+ reg = INTC_INTENB_0;
+ bitmask = 1 << irq;
+ } else {
+ reg = INTC_INTENB_1;
+ bitmask = 1 << (irq - 32);
+ }
+
+ ctrl_outl(bitmask, reg);
+}
+
+static void disable_intc_irq(unsigned int irq)
+{
+ unsigned long reg;
+ unsigned long bitmask;
+
+ if (irq < 32) {
+ reg = INTC_INTDSB_0;
+ bitmask = 1 << irq;
+ } else {
+ reg = INTC_INTDSB_1;
+ bitmask = 1 << (irq - 32);
+ }
+
+ ctrl_outl(bitmask, reg);
+}
+
+static void mask_and_ack_intc(unsigned int irq)
+{
+ disable_intc_irq(irq);
+}
+
+static void end_intc_irq(unsigned int irq)
+{
+ enable_intc_irq(irq);
+}
+
+/* For future use, if we ever support IRLM=0) */
+void make_intc_irq(unsigned int irq)
+{
+ disable_irq_nosync(irq);
+ irq_desc[irq].handler = &intc_irq_type;
+ disable_intc_irq(irq);
+}
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_SYSCTL)
+int intc_irq_describe(char* p, int irq)
+{
+ if (irq < NR_INTC_IRQS)
+ return sprintf(p, "(0x%3x)", IRQ_to_vectorN[irq]*0x20);
+ else
+ return 0;
+}
+#endif
+
+void __init init_IRQ(void)
+{
+ unsigned long long __dummy0, __dummy1=~0x00000000100000f0;
+ unsigned long reg;
+ unsigned long data;
+ int i;
+
+ intc_virt = onchip_remap(INTC_BASE, 1024, "INTC");
+ if (!intc_virt) {
+ panic("Unable to remap INTC\n");
+ }
+
+
+ /* Set default: per-line enable/disable, priority driven ack/eoi */
+ for (i = 0; i < NR_INTC_IRQS; i++) {
+ if (platform_int_priority[i] != NO_PRIORITY) {
+ irq_desc[i].handler = &intc_irq_type;
+ }
+ }
+
+
+ /* Disable all interrupts and set all priorities to 0 to avoid trouble */
+ ctrl_outl(-1, INTC_INTDSB_0);
+ ctrl_outl(-1, INTC_INTDSB_1);
+
+ for (reg = INTC_INTPRI_0, i = 0; i < INTC_INTPRI_PREGS; i++, reg += 8)
+ ctrl_outl( NO_PRIORITY, reg);
+
+
+ /* Set IRLM */
+ /* If all the priorities are set to 'no priority', then
+ * assume we are using encoded mode.
+ */
+ irlm = platform_int_priority[IRQ_IRL0] + platform_int_priority[IRQ_IRL1] + \
+ platform_int_priority[IRQ_IRL2] + platform_int_priority[IRQ_IRL3];
+
+ if (irlm == NO_PRIORITY) {
+ /* IRLM = 0 */
+ reg = INTC_ICR_CLEAR;
+ i = IRQ_INTA;
+ printk("Trying to use encoded IRL0-3. IRLs unsupported.\n");
+ } else {
+ /* IRLM = 1 */
+ reg = INTC_ICR_SET;
+ i = IRQ_IRL0;
+ }
+ ctrl_outl(INTC_ICR_IRLM, reg);
+
+ /* Set interrupt priorities according to platform description */
+ for (data = 0, reg = INTC_INTPRI_0; i < NR_INTC_IRQS; i++) {
+ data |= platform_int_priority[i] << ((i % INTC_INTPRI_PPREG) * 4);
+ if ((i % INTC_INTPRI_PPREG) == (INTC_INTPRI_PPREG - 1)) {
+ /* Upon the 7th, set Priority Register */
+ ctrl_outl(data, reg);
+ data = 0;
+ reg += 8;
+ }
+ }
+
+#ifdef CONFIG_SH_CAYMAN
+ {
+ extern void init_cayman_irq(void);
+
+ init_cayman_irq();
+ }
+#endif
+
+ /*
+ * And now let interrupts come in.
+ * sti() is not enough, we need to
+ * lower priority, too.
+ */
+ __asm__ __volatile__("getcon " __SR ", %0\n\t"
+ "and %0, %1, %0\n\t"
+ "putcon %0, " __SR "\n\t"
+ : "=&r" (__dummy0)
+ : "r" (__dummy1));
+}
--- /dev/null
+/*
+ * arch/sh64/kernel/led.c
+ *
+ * Copyright (C) 2002 Stuart Menefy <stuart.menefy@st.com>
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * Flash the LEDs
+ */
+#include <linux/config.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+
+void mach_led(int pos, int val);
+
+/* acts like an actual heart beat -- ie thump-thump-pause... */
+void heartbeat(void)
+{
+ static unsigned int cnt = 0, period = 0, dist = 0;
+
+ if (cnt == 0 || cnt == dist) {
+ mach_led(-1, 1);
+ } else if (cnt == 7 || cnt == dist + 7) {
+ mach_led(-1, 0);
+ }
+
+ if (++cnt > period) {
+ cnt = 0;
+
+ /*
+ * The hyperbolic function below modifies the heartbeat period
+ * length in dependency of the current (5min) load. It goes
+ * through the points f(0)=126, f(1)=86, f(5)=51, f(inf)->30.
+ */
+ period = ((672 << FSHIFT) / (5 * avenrun[0] +
+ (7 << FSHIFT))) + 30;
+ dist = period / 4;
+ }
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2001 David J. Mckay (david.mckay@st.com)
+ * Copyright (C) 2003, 2004 Paul Mundt
+ * Copyright (C) 2004 Richard Curnow
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * Support functions for the SH5 PCI hardware.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/rwsem.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <asm/pci.h>
+#include <linux/irq.h>
+
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include "pci_sh5.h"
+
+static unsigned long pcicr_virt;
+unsigned long pciio_virt;
+
+static void __init pci_fixup_ide_bases(struct pci_dev *d)
+{
+ int i;
+
+ /*
+ * PCI IDE controllers use non-standard I/O port decoding, respect it.
+ */
+ if ((d->class >> 8) != PCI_CLASS_STORAGE_IDE)
+ return;
+ printk("PCI: IDE base address fixup for %s\n", d->slot_name);
+ for(i=0; i<4; i++) {
+ struct resource *r = &d->resource[i];
+ if ((r->start & ~0x80) == 0x374) {
+ r->start |= 2;
+ r->end = r->start;
+ }
+ }
+}
+
+/* Add future fixups here... */
+struct pci_fixup pcibios_fixups[] = {
+ { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases },
+ { 0 }
+};
+
+char * __init pcibios_setup(char *str)
+{
+ return str;
+}
+
+/* Rounds a number UP to the nearest power of two. Used for
+ * sizing the PCI window.
+ */
+static u32 __init r2p2(u32 num)
+{
+ int i = 31;
+ u32 tmp = num;
+
+ if (num == 0)
+ return 0;
+
+ do {
+ if (tmp & (1 << 31))
+ break;
+ i--;
+ tmp <<= 1;
+ } while (i >= 0);
+
+ tmp = 1 << i;
+ /* If the original number isn't a power of 2, round it up */
+ if (tmp != num)
+ tmp <<= 1;
+
+ return tmp;
+}
+
+extern unsigned long long memory_start, memory_end;
+
+int __init sh5pci_init(unsigned memStart, unsigned memSize)
+{
+ u32 lsr0;
+ u32 uval;
+
+ pcicr_virt = onchip_remap(SH5PCI_ICR_BASE, 1024, "PCICR");
+ if (!pcicr_virt) {
+ panic("Unable to remap PCICR\n");
+ }
+
+ pciio_virt = onchip_remap(SH5PCI_IO_BASE, 0x10000, "PCIIO");
+ if (!pciio_virt) {
+ panic("Unable to remap PCIIO\n");
+ }
+
+ pr_debug("Register base addres is 0x%08lx\n", pcicr_virt);
+
+ /* Clear snoop registers */
+ SH5PCI_WRITE(CSCR0, 0);
+ SH5PCI_WRITE(CSCR1, 0);
+
+ pr_debug("Wrote to reg\n");
+
+ /* Switch off interrupts */
+ SH5PCI_WRITE(INTM, 0);
+ SH5PCI_WRITE(AINTM, 0);
+ SH5PCI_WRITE(PINTM, 0);
+
+ /* Set bus active, take it out of reset */
+ uval = SH5PCI_READ(CR);
+
+ /* Set command Register */
+ SH5PCI_WRITE(CR, uval | CR_LOCK_MASK | CR_CFINT| CR_FTO | CR_PFE | CR_PFCS | CR_BMAM);
+
+ uval=SH5PCI_READ(CR);
+ pr_debug("CR is actually 0x%08x\n",uval);
+
+ /* Allow it to be a master */
+ /* NB - WE DISABLE I/O ACCESS to stop overlap */
+ /* set WAIT bit to enable stepping, an attempt to improve stability */
+ SH5PCI_WRITE_SHORT(CSR_CMD,
+ PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_WAIT);
+
+ /*
+ ** Set translation mapping memory in order to convert the address
+ ** used for the main bus, to the PCI internal address.
+ */
+ SH5PCI_WRITE(MBR,0x40000000);
+
+ /* Always set the max size 512M */
+ SH5PCI_WRITE(MBMR, PCISH5_MEM_SIZCONV(512*1024*1024));
+
+ /*
+ ** I/O addresses are mapped at internal PCI specific address
+ ** as is described into the configuration bridge table.
+ ** These are changed to 0, to allow cards that have legacy
+ ** io such as vga to function correctly. We set the SH5 IOBAR to
+ ** 256K, which is a bit big as we can only have 64K of address space
+ */
+
+ SH5PCI_WRITE(IOBR,0x0);
+
+ pr_debug("PCI:Writing 0x%08x to IOBR\n",0);
+
+ /* Set up a 256K window. Totally pointless waste of address space */
+ SH5PCI_WRITE(IOBMR,0);
+ pr_debug("PCI:Writing 0x%08x to IOBMR\n",0);
+
+ /* The SH5 has a HUGE 256K I/O region, which breaks the PCI spec. Ideally,
+ * we would want to map the I/O region somewhere, but it is so big this is not
+ * that easy!
+ */
+ SH5PCI_WRITE(CSR_IBAR0,~0);
+ /* Set memory size value */
+ memSize = memory_end - memory_start;
+
+ /* Now we set up the mbars so the PCI bus can see the memory of the machine */
+ if (memSize < (1024 * 1024)) {
+ printk(KERN_ERR "PCISH5: Ridiculous memory size of 0x%x?\n", memSize);
+ return -EINVAL;
+ }
+
+ /* Set LSR 0 */
+ lsr0 = (memSize > (512 * 1024 * 1024)) ? 0x1ff00001 : ((r2p2(memSize) - 0x100000) | 0x1);
+ SH5PCI_WRITE(LSR0, lsr0);
+
+ pr_debug("PCI:Writing 0x%08x to LSR0\n",lsr0);
+
+ /* Set MBAR 0 */
+ SH5PCI_WRITE(CSR_MBAR0, memory_start);
+ SH5PCI_WRITE(LAR0, memory_start);
+
+ SH5PCI_WRITE(CSR_MBAR1,0);
+ SH5PCI_WRITE(LAR1,0);
+ SH5PCI_WRITE(LSR1,0);
+
+ pr_debug("PCI:Writing 0x%08llx to CSR_MBAR0\n",memory_start);
+ pr_debug("PCI:Writing 0x%08llx to LAR0\n",memory_start);
+
+ /* Enable the PCI interrupts on the device */
+ SH5PCI_WRITE(INTM, ~0);
+ SH5PCI_WRITE(AINTM, ~0);
+ SH5PCI_WRITE(PINTM, ~0);
+
+ pr_debug("Switching on all error interrupts\n");
+
+ return(0);
+}
+
+static int sh5pci_read(struct pci_bus *bus, unsigned int devfn, int where,
+ int size, u32 *val)
+{
+ SH5PCI_WRITE(PAR, CONFIG_CMD(bus, devfn, where));
+
+ switch (size) {
+ case 1:
+ *val = (u8)SH5PCI_READ_BYTE(PDR + (where & 3));
+ break;
+ case 2:
+ *val = (u16)SH5PCI_READ_SHORT(PDR + (where & 2));
+ break;
+ case 4:
+ *val = SH5PCI_READ(PDR);
+ break;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int sh5pci_write(struct pci_bus *bus, unsigned int devfn, int where,
+ int size, u32 val)
+{
+ SH5PCI_WRITE(PAR, CONFIG_CMD(bus, devfn, where));
+
+ switch (size) {
+ case 1:
+ SH5PCI_WRITE_BYTE(PDR + (where & 3), (u8)val);
+ break;
+ case 2:
+ SH5PCI_WRITE_SHORT(PDR + (where & 2), (u16)val);
+ break;
+ case 4:
+ SH5PCI_WRITE(PDR, val);
+ break;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops pci_config_ops = {
+ .read = sh5pci_read,
+ .write = sh5pci_write,
+};
+
+/* Everything hangs off this */
+static struct pci_bus *pci_root_bus;
+
+
+static u8 __init no_swizzle(struct pci_dev *dev, u8 * pin)
+{
+ pr_debug("swizzle for dev %d on bus %d slot %d pin is %d\n",
+ dev->devfn,dev->bus->number, PCI_SLOT(dev->devfn),*pin);
+ return PCI_SLOT(dev->devfn);
+}
+
+static inline u8 bridge_swizzle(u8 pin, u8 slot)
+{
+ return (((pin-1) + slot) % 4) + 1;
+}
+
+u8 __init common_swizzle(struct pci_dev *dev, u8 *pinp)
+{
+ if (dev->bus->number != 0) {
+ u8 pin = *pinp;
+ do {
+ pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn));
+ /* Move up the chain of bridges. */
+ dev = dev->bus->self;
+ } while (dev->bus->self);
+ *pinp = pin;
+
+ /* The slot is the slot of the last bridge. */
+ }
+
+ return PCI_SLOT(dev->devfn);
+}
+
+/* This needs to be shunted out of here into the board specific bit */
+
+static int __init map_cayman_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+ int result = -1;
+
+ /* The complication here is that the PCI IRQ lines from the Cayman's 2
+ 5V slots get into the CPU via a different path from the IRQ lines
+ from the 3 3.3V slots. Thus, we have to detect whether the card's
+ interrupts go via the 5V or 3.3V path, i.e. the 'bridge swizzling'
+ at the point where we cross from 5V to 3.3V is not the normal case.
+
+ The added complication is that we don't know that the 5V slots are
+ always bus 2, because a card containing a PCI-PCI bridge may be
+ plugged into a 3.3V slot, and this changes the bus numbering.
+
+ Also, the Cayman has an intermediate PCI bus that goes a custom
+ expansion board header (and to the secondary bridge). This bus has
+ never been used in practice.
+
+ The 1ary onboard PCI-PCI bridge is device 3 on bus 0
+ The 2ary onboard PCI-PCI bridge is device 0 on the 2ary bus of the 1ary bridge.
+ */
+
+ struct slot_pin {
+ int slot;
+ int pin;
+ } path[4];
+ int i=0;
+
+ while (dev->bus->number > 0) {
+
+ slot = path[i].slot = PCI_SLOT(dev->devfn);
+ pin = path[i].pin = bridge_swizzle(pin, slot);
+ dev = dev->bus->self;
+ i++;
+ if (i > 3) panic("PCI path to root bus too long!\n");
+ }
+
+ slot = PCI_SLOT(dev->devfn);
+ /* This is the slot on bus 0 through which the device is eventually
+ reachable. */
+
+ /* Now work back up. */
+ if ((slot < 3) || (i == 0)) {
+ /* Bus 0 (incl. PCI-PCI bridge itself) : perform the final
+ swizzle now. */
+ result = IRQ_INTA + bridge_swizzle(pin, slot) - 1;
+ } else {
+ i--;
+ slot = path[i].slot;
+ pin = path[i].pin;
+ if (slot > 0) {
+ panic("PCI expansion bus device found - not handled!\n");
+ } else {
+ if (i > 0) {
+ /* 5V slots */
+ i--;
+ slot = path[i].slot;
+ pin = path[i].pin;
+ /* 'pin' was swizzled earlier wrt slot, don't do it again. */
+ result = IRQ_P2INTA + (pin - 1);
+ } else {
+ /* IRQ for 2ary PCI-PCI bridge : unused */
+ result = -1;
+ }
+ }
+ }
+
+ return result;
+}
+
+irqreturn_t pcish5_err_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned pci_int, pci_air, pci_cir, pci_aint;
+
+ pci_int = SH5PCI_READ(INT);
+ pci_cir = SH5PCI_READ(CIR);
+ pci_air = SH5PCI_READ(AIR);
+
+ if (pci_int) {
+ printk("PCI INTERRUPT (at %08llx)!\n", regs->pc);
+ printk("PCI INT -> 0x%x\n", pci_int & 0xffff);
+ printk("PCI AIR -> 0x%x\n", pci_air);
+ printk("PCI CIR -> 0x%x\n", pci_cir);
+ SH5PCI_WRITE(INT, ~0);
+ }
+
+ pci_aint = SH5PCI_READ(AINT);
+ if (pci_aint) {
+ printk("PCI ARB INTERRUPT!\n");
+ printk("PCI AINT -> 0x%x\n", pci_aint);
+ printk("PCI AIR -> 0x%x\n", pci_air);
+ printk("PCI CIR -> 0x%x\n", pci_cir);
+ SH5PCI_WRITE(AINT, ~0);
+ }
+
+ return IRQ_HANDLED;
+}
+
+irqreturn_t pcish5_serr_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+ printk("SERR IRQ\n");
+
+ return IRQ_NONE;
+}
+
+#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1))
+
+static void __init
+pcibios_size_bridge(struct pci_bus *bus, struct resource *ior,
+ struct resource *memr)
+{
+ struct resource io_res, mem_res;
+ struct pci_dev *dev;
+ struct pci_dev *bridge = bus->self;
+ struct list_head *ln;
+
+ if (!bridge)
+ return; /* host bridge, nothing to do */
+
+ /* set reasonable default locations for pcibios_align_resource */
+ io_res.start = PCIBIOS_MIN_IO;
+ mem_res.start = PCIBIOS_MIN_MEM;
+
+ io_res.end = io_res.start;
+ mem_res.end = mem_res.start;
+
+ /* Collect information about how our direct children are layed out. */
+ for (ln=bus->devices.next; ln != &bus->devices; ln=ln->next) {
+ int i;
+ dev = pci_dev_b(ln);
+
+ /* Skip bridges for now */
+ if (dev->class >> 8 == PCI_CLASS_BRIDGE_PCI)
+ continue;
+
+ for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+ struct resource res;
+ unsigned long size;
+
+ memcpy(&res, &dev->resource[i], sizeof(res));
+ size = res.end - res.start + 1;
+
+ if (res.flags & IORESOURCE_IO) {
+ res.start = io_res.end;
+ pcibios_align_resource(dev, &res, size, 0);
+ io_res.end = res.start + size;
+ } else if (res.flags & IORESOURCE_MEM) {
+ res.start = mem_res.end;
+ pcibios_align_resource(dev, &res, size, 0);
+ mem_res.end = res.start + size;
+ }
+ }
+ }
+
+ /* And for all of the subordinate busses. */
+ for (ln=bus->children.next; ln != &bus->children; ln=ln->next)
+ pcibios_size_bridge(pci_bus_b(ln), &io_res, &mem_res);
+
+ /* turn the ending locations into sizes (subtract start) */
+ io_res.end -= io_res.start;
+ mem_res.end -= mem_res.start;
+
+ /* Align the sizes up by bridge rules */
+ io_res.end = ROUND_UP(io_res.end, 4*1024) - 1;
+ mem_res.end = ROUND_UP(mem_res.end, 1*1024*1024) - 1;
+
+ /* Adjust the bridge's allocation requirements */
+ bridge->resource[0].end = bridge->resource[0].start + io_res.end;
+ bridge->resource[1].end = bridge->resource[1].start + mem_res.end;
+
+ bridge->resource[PCI_BRIDGE_RESOURCES].end =
+ bridge->resource[PCI_BRIDGE_RESOURCES].start + io_res.end;
+ bridge->resource[PCI_BRIDGE_RESOURCES+1].end =
+ bridge->resource[PCI_BRIDGE_RESOURCES+1].start + mem_res.end;
+
+ /* adjust parent's resource requirements */
+ if (ior) {
+ ior->end = ROUND_UP(ior->end, 4*1024);
+ ior->end += io_res.end;
+ }
+
+ if (memr) {
+ memr->end = ROUND_UP(memr->end, 1*1024*1024);
+ memr->end += mem_res.end;
+ }
+}
+
+#undef ROUND_UP
+
+static void __init pcibios_size_bridges(void)
+{
+ struct resource io_res, mem_res;
+
+ memset(&io_res, 0, sizeof(io_res));
+ memset(&mem_res, 0, sizeof(mem_res));
+
+ pcibios_size_bridge(pci_root_bus, &io_res, &mem_res);
+}
+
+static int __init pcibios_init(void)
+{
+ if (request_irq(IRQ_ERR, pcish5_err_irq,
+ SA_INTERRUPT, "PCI Error",NULL) < 0) {
+ printk(KERN_ERR "PCISH5: Cannot hook PCI_PERR interrupt\n");
+ return -EINVAL;
+ }
+
+ if (request_irq(IRQ_SERR, pcish5_serr_irq,
+ SA_INTERRUPT, "PCI SERR interrupt", NULL) < 0) {
+ printk(KERN_ERR "PCISH5: Cannot hook PCI_SERR interrupt\n");
+ return -EINVAL;
+ }
+
+ /* The pci subsytem needs to know where memory is and how much
+ * of it there is. I've simply made these globals. A better mechanism
+ * is probably needed.
+ */
+ sh5pci_init(__pa(memory_start),
+ __pa(memory_end) - __pa(memory_start));
+
+ pci_root_bus = pci_scan_bus(0, &pci_config_ops, NULL);
+ pcibios_size_bridges();
+ pci_assign_unassigned_resources();
+ pci_fixup_irqs(no_swizzle, map_cayman_irq);
+
+ return 0;
+}
+
+subsys_initcall(pcibios_init);
+
+void __init pcibios_fixup_bus(struct pci_bus *bus)
+{
+ struct pci_dev *dev = bus->self;
+ int i;
+
+#if 1
+ if(dev) {
+ for(i=0; i<3; i++) {
+ bus->resource[i] =
+ &dev->resource[PCI_BRIDGE_RESOURCES+i];
+ bus->resource[i]->name = bus->name;
+ }
+ bus->resource[0]->flags |= IORESOURCE_IO;
+ bus->resource[1]->flags |= IORESOURCE_MEM;
+
+ /* For now, propogate host limits to the bus;
+ * we'll adjust them later. */
+
+#if 1
+ bus->resource[0]->end = 64*1024 - 1 ;
+ bus->resource[1]->end = PCIBIOS_MIN_MEM+(256*1024*1024)-1;
+ bus->resource[0]->start = PCIBIOS_MIN_IO;
+ bus->resource[1]->start = PCIBIOS_MIN_MEM;
+#else
+ bus->resource[0]->end = 0
+ bus->resource[1]->end = 0
+ bus->resource[0]->start =0
+ bus->resource[1]->start = 0;
+#endif
+ /* Turn off downstream PF memory address range by default */
+ bus->resource[2]->start = 1024*1024;
+ bus->resource[2]->end = bus->resource[2]->start - 1;
+ }
+#endif
+
+}
+
--- /dev/null
+/*
+ * $Id: pcibios.c,v 1.1 2001/08/24 12:38:19 dwmw2 Exp $
+ *
+ * arch/sh/kernel/pcibios.c
+ *
+ * Copyright (C) 2002 STMicroelectronics Limited
+ * Author : David J. McKay
+ *
+ * Copyright (C) 2004 Richard Curnow, SuperH UK Limited
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ * This is GPL'd.
+ *
+ * Provided here are generic versions of:
+ * pcibios_update_resource()
+ * pcibios_align_resource()
+ * pcibios_enable_device()
+ * pcibios_set_master()
+ * pcibios_update_irq()
+ *
+ * These functions are collected here to reduce duplication of common
+ * code amongst the many platform-specific PCI support code files.
+ *
+ * Platform-specific files are expected to provide:
+ * pcibios_fixup_bus()
+ * pcibios_init()
+ * pcibios_setup()
+ * pcibios_fixup_pbus_ranges()
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+
+void
+pcibios_update_resource(struct pci_dev *dev, struct resource *root,
+ struct resource *res, int resource)
+{
+ u32 new, check;
+ int reg;
+
+ new = res->start | (res->flags & PCI_REGION_FLAG_MASK);
+ if (resource < 6) {
+ reg = PCI_BASE_ADDRESS_0 + 4*resource;
+ } else if (resource == PCI_ROM_RESOURCE) {
+ res->flags |= PCI_ROM_ADDRESS_ENABLE;
+ new |= PCI_ROM_ADDRESS_ENABLE;
+ reg = dev->rom_base_reg;
+ } else {
+ /* Somebody might have asked allocation of a non-standard resource */
+ return;
+ }
+
+ pci_write_config_dword(dev, reg, new);
+ pci_read_config_dword(dev, reg, &check);
+ if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) {
+ printk(KERN_ERR "PCI: Error while updating region "
+ "%s/%d (%08x != %08x)\n", dev->slot_name, resource,
+ new, check);
+ }
+}
+
+/*
+ * We need to avoid collisions with `mirrored' VGA ports
+ * and other strange ISA hardware, so we always want the
+ * addresses to be allocated in the 0x000-0x0ff region
+ * modulo 0x400.
+ */
+void pcibios_align_resource(void *data, struct resource *res,
+ unsigned long size, unsigned long align)
+{
+ if (res->flags & IORESOURCE_IO) {
+ unsigned long start = res->start;
+
+ if (start & 0x300) {
+ start = (start + 0x3ff) & ~0x3ff;
+ res->start = start;
+ }
+ }
+}
+
+static void pcibios_enable_bridge(struct pci_dev *dev)
+{
+ struct pci_bus *bus = dev->subordinate;
+ u16 cmd, old_cmd;
+
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ old_cmd = cmd;
+
+ if (bus->resource[0]->flags & IORESOURCE_IO) {
+ cmd |= PCI_COMMAND_IO;
+ }
+ if ((bus->resource[1]->flags & IORESOURCE_MEM) ||
+ (bus->resource[2]->flags & IORESOURCE_PREFETCH)) {
+ cmd |= PCI_COMMAND_MEMORY;
+ }
+
+ if (cmd != old_cmd) {
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ }
+
+ printk("PCI bridge %s, command register -> %04x\n",
+ pci_name(dev), cmd);
+
+}
+
+
+
+int pcibios_enable_device(struct pci_dev *dev, int mask)
+{
+ u16 cmd, old_cmd;
+ int idx;
+ struct resource *r;
+
+ if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
+ pcibios_enable_bridge(dev);
+ }
+
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ old_cmd = cmd;
+ for(idx=0; idx<6; idx++) {
+ if (!(mask & (1 << idx)))
+ continue;
+ r = &dev->resource[idx];
+ if (!r->start && r->end) {
+ printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", dev->slot_name);
+ return -EINVAL;
+ }
+ if (r->flags & IORESOURCE_IO)
+ cmd |= PCI_COMMAND_IO;
+ if (r->flags & IORESOURCE_MEM)
+ cmd |= PCI_COMMAND_MEMORY;
+ }
+ if (dev->resource[PCI_ROM_RESOURCE].start)
+ cmd |= PCI_COMMAND_MEMORY;
+ if (cmd != old_cmd) {
+ printk(KERN_INFO "PCI: Enabling device %s (%04x -> %04x)\n", pci_name(dev), old_cmd, cmd);
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ }
+ return 0;
+}
+
+/*
+ * If we set up a device for bus mastering, we need to check and set
+ * the latency timer as it may not be properly set.
+ */
+unsigned int pcibios_max_latency = 255;
+
+void pcibios_set_master(struct pci_dev *dev)
+{
+ u8 lat;
+ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
+ if (lat < 16)
+ lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency;
+ else if (lat > pcibios_max_latency)
+ lat = pcibios_max_latency;
+ else
+ return;
+ printk(KERN_INFO "PCI: Setting latency timer of device %s to %d\n", pci_name(dev), lat);
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
+}
+
+void __init pcibios_update_irq(struct pci_dev *dev, int irq)
+{
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
+}
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/kernel/process.c
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003 Paul Mundt
+ * Copyright (C) 2003, 2004 Richard Curnow
+ *
+ * Started from SH3/4 version:
+ * Copyright (C) 1999, 2000 Niibe Yutaka & Kaz Kojima
+ *
+ * In turn started from i386 version:
+ * Copyright (C) 1995 Linus Torvalds
+ *
+ */
+
+/*
+ * This file handles the architecture-dependent parts of process handling..
+ */
+
+/* Temporary flags/tests. All to be removed/undefined. BEGIN */
+#define IDLE_TRACE
+#define VM_SHOW_TABLES
+#define VM_TEST_FAULT
+#define VM_TEST_RTLBMISS
+#define VM_TEST_WTLBMISS
+
+#undef VM_SHOW_TABLES
+#undef IDLE_TRACE
+/* Temporary flags/tests. All to be removed/undefined. END */
+
+#define __KERNEL_SYSCALLS__
+#include <stdarg.h>
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/rwsem.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/interrupt.h>
+#include <linux/unistd.h>
+#include <linux/delay.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/processor.h> /* includes also <asm/registers.h> */
+#include <asm/mmu_context.h>
+#include <asm/elf.h>
+#include <asm/page.h>
+
+#include <linux/irq.h>
+
+struct task_struct *last_task_used_math = NULL;
+
+#ifdef IDLE_TRACE
+#ifdef VM_SHOW_TABLES
+/* For testing */
+static void print_PTE(long base)
+{
+ int i, skip=0;
+ long long x, y, *p = (long long *) base;
+
+ for (i=0; i< 512; i++, p++){
+ if (*p == 0) {
+ if (!skip) {
+ skip++;
+ printk("(0s) ");
+ }
+ } else {
+ skip=0;
+ x = (*p) >> 32;
+ y = (*p) & 0xffffffff;
+ printk("%08Lx%08Lx ", x, y);
+ if (!((i+1)&0x3)) printk("\n");
+ }
+ }
+}
+
+/* For testing */
+static void print_DIR(long base)
+{
+ int i, skip=0;
+ long *p = (long *) base;
+
+ for (i=0; i< 512; i++, p++){
+ if (*p == 0) {
+ if (!skip) {
+ skip++;
+ printk("(0s) ");
+ }
+ } else {
+ skip=0;
+ printk("%08lx ", *p);
+ if (!((i+1)&0x7)) printk("\n");
+ }
+ }
+}
+
+/* For testing */
+static void print_vmalloc_first_tables(void)
+{
+
+#define PRESENT 0x800 /* Bit 11 */
+
+ /*
+ * Do it really dirty by looking at raw addresses,
+ * raw offsets, no types. If we used pgtable/pgalloc
+ * macros/definitions we could hide potential bugs.
+ *
+ * Note that pointers are 32-bit for CDC.
+ */
+ long pgdt, pmdt, ptet;
+
+ pgdt = (long) &swapper_pg_dir;
+ printk("-->PGD (0x%08lx):\n", pgdt);
+ print_DIR(pgdt);
+ printk("\n");
+
+ /* VMALLOC pool is mapped at 0xc0000000, second (pointer) entry in PGD */
+ pgdt += 4;
+ pmdt = (long) (* (long *) pgdt);
+ if (!(pmdt & PRESENT)) {
+ printk("No PMD\n");
+ return;
+ } else pmdt &= 0xfffff000;
+
+ printk("-->PMD (0x%08lx):\n", pmdt);
+ print_DIR(pmdt);
+ printk("\n");
+
+ /* Get the pmdt displacement for 0xc0000000 */
+ pmdt += 2048;
+
+ /* just look at first two address ranges ... */
+ /* ... 0xc0000000 ... */
+ ptet = (long) (* (long *) pmdt);
+ if (!(ptet & PRESENT)) {
+ printk("No PTE0\n");
+ return;
+ } else ptet &= 0xfffff000;
+
+ printk("-->PTE0 (0x%08lx):\n", ptet);
+ print_PTE(ptet);
+ printk("\n");
+
+ /* ... 0xc0001000 ... */
+ ptet += 4;
+ if (!(ptet & PRESENT)) {
+ printk("No PTE1\n");
+ return;
+ } else ptet &= 0xfffff000;
+ printk("-->PTE1 (0x%08lx):\n", ptet);
+ print_PTE(ptet);
+ printk("\n");
+}
+#else
+#define print_vmalloc_first_tables()
+#endif /* VM_SHOW_TABLES */
+
+static void test_VM(void)
+{
+ void *a, *b, *c;
+
+#ifdef VM_SHOW_TABLES
+ printk("Initial PGD/PMD/PTE\n");
+#endif
+ print_vmalloc_first_tables();
+
+ printk("Allocating 2 bytes\n");
+ a = vmalloc(2);
+ print_vmalloc_first_tables();
+
+ printk("Allocating 4100 bytes\n");
+ b = vmalloc(4100);
+ print_vmalloc_first_tables();
+
+ printk("Allocating 20234 bytes\n");
+ c = vmalloc(20234);
+ print_vmalloc_first_tables();
+
+#ifdef VM_TEST_FAULT
+ /* Here you may want to fault ! */
+
+#ifdef VM_TEST_RTLBMISS
+ printk("Ready to fault upon read.\n");
+ if (* (char *) a) {
+ printk("RTLBMISSed on area a !\n");
+ }
+ printk("RTLBMISSed on area a !\n");
+#endif
+
+#ifdef VM_TEST_WTLBMISS
+ printk("Ready to fault upon write.\n");
+ *((char *) b) = 'L';
+ printk("WTLBMISSed on area b !\n");
+#endif
+
+#endif /* VM_TEST_FAULT */
+
+ printk("Deallocating the 4100 byte chunk\n");
+ vfree(b);
+ print_vmalloc_first_tables();
+
+ printk("Deallocating the 2 byte chunk\n");
+ vfree(a);
+ print_vmalloc_first_tables();
+
+ printk("Deallocating the last chunk\n");
+ vfree(c);
+ print_vmalloc_first_tables();
+}
+
+extern unsigned long volatile jiffies;
+int once = 0;
+unsigned long old_jiffies;
+int pid = -1, pgid = -1;
+
+void idle_trace(void)
+{
+
+ _syscall0(int, getpid)
+ _syscall1(int, getpgid, int, pid)
+
+ if (!once) {
+ /* VM allocation/deallocation simple test */
+ test_VM();
+ pid = getpid();
+
+ printk("Got all through to Idle !!\n");
+ printk("I'm now going to loop forever ...\n");
+ printk("Any ! below is a timer tick.\n");
+ printk("Any . below is a getpgid system call from pid = %d.\n", pid);
+
+
+ old_jiffies = jiffies;
+ once++;
+ }
+
+ if (old_jiffies != jiffies) {
+ old_jiffies = jiffies - old_jiffies;
+ switch (old_jiffies) {
+ case 1:
+ printk("!");
+ break;
+ case 2:
+ printk("!!");
+ break;
+ case 3:
+ printk("!!!");
+ break;
+ case 4:
+ printk("!!!!");
+ break;
+ default:
+ printk("(%d!)", (int) old_jiffies);
+ }
+ old_jiffies = jiffies;
+ }
+ pgid = getpgid(pid);
+ printk(".");
+}
+#else
+#define idle_trace() do { } while (0)
+#endif /* IDLE_TRACE */
+
+static int hlt_counter = 1;
+
+#define HARD_IDLE_TIMEOUT (HZ / 3)
+
+void disable_hlt(void)
+{
+ hlt_counter++;
+}
+
+void enable_hlt(void)
+{
+ hlt_counter--;
+}
+
+static int __init nohlt_setup(char *__unused)
+{
+ hlt_counter = 1;
+ return 1;
+}
+
+static int __init hlt_setup(char *__unused)
+{
+ hlt_counter = 0;
+ return 1;
+}
+
+__setup("nohlt", nohlt_setup);
+__setup("hlt", hlt_setup);
+
+static inline void hlt(void)
+{
+ if (hlt_counter)
+ return;
+
+ __asm__ __volatile__ ("sleep" : : : "memory");
+}
+
+/*
+ * The idle loop on a uniprocessor SH..
+ */
+void default_idle(void)
+{
+ /* endless idle loop with no priority at all */
+ while (1) {
+ if (hlt_counter) {
+ while (1)
+ if (need_resched())
+ break;
+ } else {
+ local_irq_disable();
+ while (!need_resched()) {
+ local_irq_enable();
+ idle_trace();
+ hlt();
+ local_irq_disable();
+ }
+ local_irq_enable();
+ }
+ schedule();
+ }
+}
+
+void cpu_idle(void *unused)
+{
+ default_idle();
+}
+
+void machine_restart(char * __unused)
+{
+ extern void phys_stext(void);
+
+ phys_stext();
+}
+
+void machine_halt(void)
+{
+ for (;;);
+}
+
+void machine_power_off(void)
+{
+ extern void enter_deep_standby(void);
+
+ enter_deep_standby();
+}
+
+void show_regs(struct pt_regs * regs)
+{
+ unsigned long long ah, al, bh, bl, ch, cl;
+
+ printk("\n");
+
+ ah = (regs->pc) >> 32;
+ al = (regs->pc) & 0xffffffff;
+ bh = (regs->regs[18]) >> 32;
+ bl = (regs->regs[18]) & 0xffffffff;
+ ch = (regs->regs[15]) >> 32;
+ cl = (regs->regs[15]) & 0xffffffff;
+ printk("PC : %08Lx%08Lx LINK: %08Lx%08Lx SP : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->sr) >> 32;
+ al = (regs->sr) & 0xffffffff;
+ asm volatile ("getcon " __TEA ", %0" : "=r" (bh));
+ asm volatile ("getcon " __TEA ", %0" : "=r" (bl));
+ bh = (bh) >> 32;
+ bl = (bl) & 0xffffffff;
+ asm volatile ("getcon " __KCR0 ", %0" : "=r" (ch));
+ asm volatile ("getcon " __KCR0 ", %0" : "=r" (cl));
+ ch = (ch) >> 32;
+ cl = (cl) & 0xffffffff;
+ printk("SR : %08Lx%08Lx TEA : %08Lx%08Lx KCR0: %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[0]) >> 32;
+ al = (regs->regs[0]) & 0xffffffff;
+ bh = (regs->regs[1]) >> 32;
+ bl = (regs->regs[1]) & 0xffffffff;
+ ch = (regs->regs[2]) >> 32;
+ cl = (regs->regs[2]) & 0xffffffff;
+ printk("R0 : %08Lx%08Lx R1 : %08Lx%08Lx R2 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[3]) >> 32;
+ al = (regs->regs[3]) & 0xffffffff;
+ bh = (regs->regs[4]) >> 32;
+ bl = (regs->regs[4]) & 0xffffffff;
+ ch = (regs->regs[5]) >> 32;
+ cl = (regs->regs[5]) & 0xffffffff;
+ printk("R3 : %08Lx%08Lx R4 : %08Lx%08Lx R5 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[6]) >> 32;
+ al = (regs->regs[6]) & 0xffffffff;
+ bh = (regs->regs[7]) >> 32;
+ bl = (regs->regs[7]) & 0xffffffff;
+ ch = (regs->regs[8]) >> 32;
+ cl = (regs->regs[8]) & 0xffffffff;
+ printk("R6 : %08Lx%08Lx R7 : %08Lx%08Lx R8 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[9]) >> 32;
+ al = (regs->regs[9]) & 0xffffffff;
+ bh = (regs->regs[10]) >> 32;
+ bl = (regs->regs[10]) & 0xffffffff;
+ ch = (regs->regs[11]) >> 32;
+ cl = (regs->regs[11]) & 0xffffffff;
+ printk("R9 : %08Lx%08Lx R10 : %08Lx%08Lx R11 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[12]) >> 32;
+ al = (regs->regs[12]) & 0xffffffff;
+ bh = (regs->regs[13]) >> 32;
+ bl = (regs->regs[13]) & 0xffffffff;
+ ch = (regs->regs[14]) >> 32;
+ cl = (regs->regs[14]) & 0xffffffff;
+ printk("R12 : %08Lx%08Lx R13 : %08Lx%08Lx R14 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[16]) >> 32;
+ al = (regs->regs[16]) & 0xffffffff;
+ bh = (regs->regs[17]) >> 32;
+ bl = (regs->regs[17]) & 0xffffffff;
+ ch = (regs->regs[19]) >> 32;
+ cl = (regs->regs[19]) & 0xffffffff;
+ printk("R16 : %08Lx%08Lx R17 : %08Lx%08Lx R19 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[20]) >> 32;
+ al = (regs->regs[20]) & 0xffffffff;
+ bh = (regs->regs[21]) >> 32;
+ bl = (regs->regs[21]) & 0xffffffff;
+ ch = (regs->regs[22]) >> 32;
+ cl = (regs->regs[22]) & 0xffffffff;
+ printk("R20 : %08Lx%08Lx R21 : %08Lx%08Lx R22 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[23]) >> 32;
+ al = (regs->regs[23]) & 0xffffffff;
+ bh = (regs->regs[24]) >> 32;
+ bl = (regs->regs[24]) & 0xffffffff;
+ ch = (regs->regs[25]) >> 32;
+ cl = (regs->regs[25]) & 0xffffffff;
+ printk("R23 : %08Lx%08Lx R24 : %08Lx%08Lx R25 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[26]) >> 32;
+ al = (regs->regs[26]) & 0xffffffff;
+ bh = (regs->regs[27]) >> 32;
+ bl = (regs->regs[27]) & 0xffffffff;
+ ch = (regs->regs[28]) >> 32;
+ cl = (regs->regs[28]) & 0xffffffff;
+ printk("R26 : %08Lx%08Lx R27 : %08Lx%08Lx R28 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[29]) >> 32;
+ al = (regs->regs[29]) & 0xffffffff;
+ bh = (regs->regs[30]) >> 32;
+ bl = (regs->regs[30]) & 0xffffffff;
+ ch = (regs->regs[31]) >> 32;
+ cl = (regs->regs[31]) & 0xffffffff;
+ printk("R29 : %08Lx%08Lx R30 : %08Lx%08Lx R31 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[32]) >> 32;
+ al = (regs->regs[32]) & 0xffffffff;
+ bh = (regs->regs[33]) >> 32;
+ bl = (regs->regs[33]) & 0xffffffff;
+ ch = (regs->regs[34]) >> 32;
+ cl = (regs->regs[34]) & 0xffffffff;
+ printk("R32 : %08Lx%08Lx R33 : %08Lx%08Lx R34 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[35]) >> 32;
+ al = (regs->regs[35]) & 0xffffffff;
+ bh = (regs->regs[36]) >> 32;
+ bl = (regs->regs[36]) & 0xffffffff;
+ ch = (regs->regs[37]) >> 32;
+ cl = (regs->regs[37]) & 0xffffffff;
+ printk("R35 : %08Lx%08Lx R36 : %08Lx%08Lx R37 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[38]) >> 32;
+ al = (regs->regs[38]) & 0xffffffff;
+ bh = (regs->regs[39]) >> 32;
+ bl = (regs->regs[39]) & 0xffffffff;
+ ch = (regs->regs[40]) >> 32;
+ cl = (regs->regs[40]) & 0xffffffff;
+ printk("R38 : %08Lx%08Lx R39 : %08Lx%08Lx R40 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[41]) >> 32;
+ al = (regs->regs[41]) & 0xffffffff;
+ bh = (regs->regs[42]) >> 32;
+ bl = (regs->regs[42]) & 0xffffffff;
+ ch = (regs->regs[43]) >> 32;
+ cl = (regs->regs[43]) & 0xffffffff;
+ printk("R41 : %08Lx%08Lx R42 : %08Lx%08Lx R43 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[44]) >> 32;
+ al = (regs->regs[44]) & 0xffffffff;
+ bh = (regs->regs[45]) >> 32;
+ bl = (regs->regs[45]) & 0xffffffff;
+ ch = (regs->regs[46]) >> 32;
+ cl = (regs->regs[46]) & 0xffffffff;
+ printk("R44 : %08Lx%08Lx R45 : %08Lx%08Lx R46 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[47]) >> 32;
+ al = (regs->regs[47]) & 0xffffffff;
+ bh = (regs->regs[48]) >> 32;
+ bl = (regs->regs[48]) & 0xffffffff;
+ ch = (regs->regs[49]) >> 32;
+ cl = (regs->regs[49]) & 0xffffffff;
+ printk("R47 : %08Lx%08Lx R48 : %08Lx%08Lx R49 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[50]) >> 32;
+ al = (regs->regs[50]) & 0xffffffff;
+ bh = (regs->regs[51]) >> 32;
+ bl = (regs->regs[51]) & 0xffffffff;
+ ch = (regs->regs[52]) >> 32;
+ cl = (regs->regs[52]) & 0xffffffff;
+ printk("R50 : %08Lx%08Lx R51 : %08Lx%08Lx R52 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[53]) >> 32;
+ al = (regs->regs[53]) & 0xffffffff;
+ bh = (regs->regs[54]) >> 32;
+ bl = (regs->regs[54]) & 0xffffffff;
+ ch = (regs->regs[55]) >> 32;
+ cl = (regs->regs[55]) & 0xffffffff;
+ printk("R53 : %08Lx%08Lx R54 : %08Lx%08Lx R55 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[56]) >> 32;
+ al = (regs->regs[56]) & 0xffffffff;
+ bh = (regs->regs[57]) >> 32;
+ bl = (regs->regs[57]) & 0xffffffff;
+ ch = (regs->regs[58]) >> 32;
+ cl = (regs->regs[58]) & 0xffffffff;
+ printk("R56 : %08Lx%08Lx R57 : %08Lx%08Lx R58 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[59]) >> 32;
+ al = (regs->regs[59]) & 0xffffffff;
+ bh = (regs->regs[60]) >> 32;
+ bl = (regs->regs[60]) & 0xffffffff;
+ ch = (regs->regs[61]) >> 32;
+ cl = (regs->regs[61]) & 0xffffffff;
+ printk("R59 : %08Lx%08Lx R60 : %08Lx%08Lx R61 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[62]) >> 32;
+ al = (regs->regs[62]) & 0xffffffff;
+ bh = (regs->tregs[0]) >> 32;
+ bl = (regs->tregs[0]) & 0xffffffff;
+ ch = (regs->tregs[1]) >> 32;
+ cl = (regs->tregs[1]) & 0xffffffff;
+ printk("R62 : %08Lx%08Lx T0 : %08Lx%08Lx T1 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->tregs[2]) >> 32;
+ al = (regs->tregs[2]) & 0xffffffff;
+ bh = (regs->tregs[3]) >> 32;
+ bl = (regs->tregs[3]) & 0xffffffff;
+ ch = (regs->tregs[4]) >> 32;
+ cl = (regs->tregs[4]) & 0xffffffff;
+ printk("T2 : %08Lx%08Lx T3 : %08Lx%08Lx T4 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->tregs[5]) >> 32;
+ al = (regs->tregs[5]) & 0xffffffff;
+ bh = (regs->tregs[6]) >> 32;
+ bl = (regs->tregs[6]) & 0xffffffff;
+ ch = (regs->tregs[7]) >> 32;
+ cl = (regs->tregs[7]) & 0xffffffff;
+ printk("T5 : %08Lx%08Lx T6 : %08Lx%08Lx T7 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ /*
+ * If we're in kernel mode, dump the stack too..
+ */
+ if (!user_mode(regs)) {
+ void show_stack(struct task_struct *tsk, unsigned long *sp);
+ unsigned long sp = regs->regs[15] & 0xffffffff;
+ struct task_struct *tsk = get_current();
+
+ tsk->thread.kregs = regs;
+
+ show_stack(tsk, (unsigned long *)sp);
+ }
+}
+
+struct task_struct * alloc_task_struct(void)
+{
+ /* Get task descriptor pages */
+ return (struct task_struct *)
+ __get_free_pages(GFP_KERNEL, get_order(THREAD_SIZE));
+}
+
+void free_task_struct(struct task_struct *p)
+{
+ free_pages((unsigned long) p, get_order(THREAD_SIZE));
+}
+
+/*
+ * Create a kernel thread
+ */
+
+/*
+ * This is the mechanism for creating a new kernel thread.
+ *
+ * NOTE! Only a kernel-only process(ie the swapper or direct descendants
+ * who haven't done an "execve()") should use this: it will work within
+ * a system call from a "real" process, but the process memory space will
+ * not be free'd until both the parent and the child have exited.
+ */
+int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+{
+ /* A bit less processor dependent than older sh ... */
+
+ unsigned int reply;
+
+static __inline__ _syscall2(int,clone,unsigned long,flags,unsigned long,newsp)
+static __inline__ _syscall1(int,exit,int,ret)
+
+ reply = clone(flags | CLONE_VM, 0);
+ if (!reply) {
+ /* Child */
+ reply = exit(fn(arg));
+ }
+
+ return reply;
+}
+
+/*
+ * Free current thread data structures etc..
+ */
+void exit_thread(void)
+{
+ /* See arch/sparc/kernel/process.c for the precedent for doing this -- RPC.
+
+ The SH-5 FPU save/restore approach relies on last_task_used_math
+ pointing to a live task_struct. When another task tries to use the
+ FPU for the 1st time, the FPUDIS trap handling (see
+ arch/sh64/kernel/fpu.c) will save the existing FPU state to the
+ FP regs field within last_task_used_math before re-loading the new
+ task's FPU state (or initialising it if the FPU has been used
+ before). So if last_task_used_math is stale, and its page has already been
+ re-allocated for another use, the consequences are rather grim. Unless we
+ null it here, there is no other path through which it would get safely
+ nulled. */
+
+#ifndef CONFIG_NOFPU_SUPPORT
+ if (last_task_used_math == current) {
+ last_task_used_math = NULL;
+ }
+#endif
+}
+
+void flush_thread(void)
+{
+
+ /* Called by fs/exec.c (flush_old_exec) to remove traces of a
+ * previously running executable. */
+#ifndef CONFIG_NOFPU_SUPPORT
+ if (last_task_used_math == current) {
+ last_task_used_math = NULL;
+ }
+ /* Force FPU state to be reinitialised after exec */
+ current->used_math = 0;
+#endif
+
+ /* if we are a kernel thread, about to change to user thread,
+ * update kreg
+ */
+ if(current->thread.kregs==&fake_swapper_regs) {
+ current->thread.kregs =
+ ((struct pt_regs *)(THREAD_SIZE + (unsigned long) current) - 1);
+ current->thread.uregs = current->thread.kregs;
+ }
+}
+
+void release_thread(struct task_struct *dead_task)
+{
+ /* do nothing */
+}
+
+/* Fill in the fpu structure for a core dump.. */
+int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
+{
+#ifndef CONFIG_NOFPU_SUPPORT
+ int fpvalid;
+ struct task_struct *tsk = current;
+
+ fpvalid = tsk->used_math;
+ if (fpvalid) {
+ if (current == last_task_used_math) {
+ grab_fpu();
+ fpsave(&tsk->thread.fpu.hard);
+ release_fpu();
+ last_task_used_math = 0;
+ regs->sr |= SR_FD;
+ }
+
+ memcpy(fpu, &tsk->thread.fpu.hard, sizeof(*fpu));
+ }
+
+ return fpvalid;
+#else
+ return 0; /* Task didn't use the fpu at all. */
+#endif
+}
+
+asmlinkage void ret_from_fork(void);
+
+int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
+ unsigned long unused,
+ struct task_struct *p, struct pt_regs *regs)
+{
+ struct pt_regs *childregs;
+ unsigned long long se; /* Sign extension */
+
+#ifndef CONFIG_NOFPU_SUPPORT
+ if(last_task_used_math == current) {
+ grab_fpu();
+ fpsave(¤t->thread.fpu.hard);
+ release_fpu();
+ last_task_used_math = NULL;
+ regs->sr |= SR_FD;
+ }
+#endif
+ /* Copy from sh version */
+ childregs = ((struct pt_regs *)(THREAD_SIZE + (unsigned long) p->thread_info )) - 1;
+
+ *childregs = *regs;
+
+ if (user_mode(regs)) {
+ childregs->regs[15] = usp;
+ p->thread.uregs = childregs;
+ } else {
+ childregs->regs[15] = (unsigned long)p->thread_info + THREAD_SIZE;
+ }
+
+ childregs->regs[9] = 0; /* Set return value for child */
+ childregs->sr |= SR_FD; /* Invalidate FPU flag */
+
+ /* From sh */
+ p->set_child_tid = p->clear_child_tid = NULL;
+
+ p->thread.sp = (unsigned long) childregs;
+ p->thread.pc = (unsigned long) ret_from_fork;
+
+ /*
+ * Sign extend the edited stack.
+ * Note that thread.pc and thread.pc will stay
+ * 32-bit wide and context switch must take care
+ * of NEFF sign extension.
+ */
+
+ se = childregs->regs[15];
+ se = (se & NEFF_SIGN) ? (se | NEFF_MASK) : se;
+ childregs->regs[15] = se;
+
+ return 0;
+}
+
+/*
+ * fill in the user structure for a core dump..
+ */
+void dump_thread(struct pt_regs * regs, struct user * dump)
+{
+ dump->magic = CMAGIC;
+ dump->start_code = current->mm->start_code;
+ dump->start_data = current->mm->start_data;
+ dump->start_stack = regs->regs[15] & ~(PAGE_SIZE - 1);
+ dump->u_tsize = (current->mm->end_code - dump->start_code) >> PAGE_SHIFT;
+ dump->u_dsize = (current->mm->brk + (PAGE_SIZE-1) - dump->start_data) >> PAGE_SHIFT;
+ dump->u_ssize = (current->mm->start_stack - dump->start_stack +
+ PAGE_SIZE - 1) >> PAGE_SHIFT;
+ /* Debug registers will come here. */
+
+ dump->regs = *regs;
+
+ dump->u_fpvalid = dump_fpu(regs, &dump->fpu);
+}
+
+asmlinkage int sys_fork(unsigned long r2, unsigned long r3,
+ unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7,
+ struct pt_regs *pregs)
+{
+ return do_fork(SIGCHLD, pregs->regs[15], pregs, 0, 0, 0);
+}
+
+asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
+ unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7,
+ struct pt_regs *pregs)
+{
+ if (!newsp)
+ newsp = pregs->regs[15];
+ return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, pregs, 0, 0, 0);
+}
+
+/*
+ * This is trivial, and on the face of it looks like it
+ * could equally well be done in user mode.
+ *
+ * Not so, for quite unobvious reasons - register pressure.
+ * In user mode vfork() cannot have a stack frame, and if
+ * done by calling the "clone()" system call directly, you
+ * do not have enough call-clobbered registers to hold all
+ * the information you need.
+ */
+asmlinkage int sys_vfork(unsigned long r2, unsigned long r3,
+ unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7,
+ struct pt_regs *pregs)
+{
+ return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, pregs->regs[15], pregs, 0, 0, 0);
+}
+
+/*
+ * sys_execve() executes a new program.
+ */
+asmlinkage int sys_execve(char *ufilename, char **uargv,
+ char **uenvp, unsigned long r5,
+ unsigned long r6, unsigned long r7,
+ struct pt_regs *pregs)
+{
+ int error;
+ char *filename;
+
+ lock_kernel();
+ filename = getname((char __user *)ufilename);
+ error = PTR_ERR(filename);
+ if (IS_ERR(filename))
+ goto out;
+
+ error = do_execve(filename,
+ (char __user * __user *)uargv,
+ (char __user * __user *)uenvp,
+ pregs);
+ if (error == 0)
+ current->ptrace &= ~PT_DTRACE;
+ putname(filename);
+out:
+ unlock_kernel();
+ return error;
+}
+
+/*
+ * These bracket the sleeping functions..
+ */
+extern void interruptible_sleep_on(wait_queue_head_t *q);
+
+#define mid_sched ((unsigned long) interruptible_sleep_on)
+
+static int in_sh64_switch_to(unsigned long pc)
+{
+ extern char __sh64_switch_to_end;
+ /* For a sleeping task, the PC is somewhere in the middle of the function,
+ so we don't have to worry about masking the LSB off */
+ return (pc >= (unsigned long) sh64_switch_to) &&
+ (pc < (unsigned long) &__sh64_switch_to_end);
+}
+
+unsigned long get_wchan(struct task_struct *p)
+{
+ unsigned long schedule_fp;
+ unsigned long sh64_switch_to_fp;
+ unsigned long schedule_caller_pc;
+ unsigned long pc;
+
+ if (!p || p == current || p->state == TASK_RUNNING)
+ return 0;
+
+ /*
+ * The same comment as on the Alpha applies here, too ...
+ */
+ pc = thread_saved_pc(p);
+
+#if CONFIG_FRAME_POINTER
+ if (in_sh64_switch_to(pc)) {
+ sh64_switch_to_fp = (long) p->thread.sp;
+ /* r14 is saved at offset 4 in the sh64_switch_to frame */
+ schedule_fp = *(unsigned long *) (long)(sh64_switch_to_fp + 4);
+
+ /* and the caller of 'schedule' is (currently!) saved at offset 24
+ in the frame of schedule (from disasm) */
+ schedule_caller_pc = *(unsigned long *) (long)(schedule_fp + 24);
+ return schedule_caller_pc;
+ }
+#endif
+ return pc;
+}
+
+/* Provide a /proc/asids file that lists out the
+ ASIDs currently associated with the processes. (If the DM.PC register is
+ examined through the debug link, this shows ASID + PC. To make use of this,
+ the PID->ASID relationship needs to be known. This is primarily for
+ debugging.)
+ */
+
+#if defined(CONFIG_SH64_PROC_ASIDS)
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+
+static int
+asids_proc_info(char *buf, char **start, off_t fpos, int length, int *eof, void *data)
+{
+ int len=0;
+ struct task_struct *p;
+ read_lock(&tasklist_lock);
+ for_each_task(p) {
+ int pid = p->pid;
+ struct mm_struct *mm;
+ if (!pid) continue;
+ mm = p->mm;
+ if (mm) {
+ unsigned long asid, context;
+ context = mm->context;
+ asid = (context & 0xff);
+ len += sprintf(buf+len, "%5d : %02x\n", pid, asid);
+ } else {
+ len += sprintf(buf+len, "%5d : (none)\n", pid);
+ }
+ }
+ read_unlock(&tasklist_lock);
+ *eof = 1;
+ return len;
+}
+
+static int __init register_proc_asids(void)
+{
+ create_proc_read_entry("asids", 0, NULL, asids_proc_info, NULL);
+ return 0;
+}
+
+__initcall(register_proc_asids);
+#endif
+
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/kernel/ptrace.c
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003 Paul Mundt
+ *
+ * Started from SH3/4 version:
+ * SuperH version: Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka
+ *
+ * Original x86 implementation:
+ * By Ross Biro 1/23/92
+ * edited by Linus Torvalds
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/processor.h>
+#include <asm/mmu_context.h>
+
+/* This mask defines the bits of the SR which the user is not allowed to
+ change, which are everything except S, Q, M, PR, SZ, FR. */
+#define SR_MASK (0xffff8cfd)
+
+/*
+ * does not yet catch signals sent when the child dies.
+ * in exit.c or in signal.c.
+ */
+
+/*
+ * This routine will get a word from the user area in the process kernel stack.
+ */
+static inline int get_stack_long(struct task_struct *task, int offset)
+{
+ unsigned char *stack;
+
+ stack = (unsigned char *)(task->thread.uregs);
+ stack += offset;
+ return (*((int *)stack));
+}
+
+static inline unsigned long
+get_fpu_long(struct task_struct *task, unsigned long addr)
+{
+ unsigned long tmp;
+ struct pt_regs *regs;
+ regs = (struct pt_regs*)((unsigned char *)task + THREAD_SIZE) - 1;
+
+ if (!task->used_math) {
+ if (addr == offsetof(struct user_fpu_struct, fpscr)) {
+ tmp = FPSCR_INIT;
+ } else {
+ tmp = 0xffffffffUL; /* matches initial value in fpu.c */
+ }
+ return tmp;
+ }
+
+ if (last_task_used_math == task) {
+ grab_fpu();
+ fpsave(&task->thread.fpu.hard);
+ release_fpu();
+ last_task_used_math = 0;
+ regs->sr |= SR_FD;
+ }
+
+ tmp = ((long *)&task->thread.fpu)[addr / sizeof(unsigned long)];
+ return tmp;
+}
+
+/*
+ * This routine will put a word into the user area in the process kernel stack.
+ */
+static inline int put_stack_long(struct task_struct *task, int offset,
+ unsigned long data)
+{
+ unsigned char *stack;
+
+ stack = (unsigned char *)(task->thread.uregs);
+ stack += offset;
+ *(unsigned long *) stack = data;
+ return 0;
+}
+
+static inline int
+put_fpu_long(struct task_struct *task, unsigned long addr, unsigned long data)
+{
+ struct pt_regs *regs;
+
+ regs = (struct pt_regs*)((unsigned char *)task + THREAD_SIZE) - 1;
+
+ if (!task->used_math) {
+ fpinit(&task->thread.fpu.hard);
+ task->used_math = 1;
+ } else if (last_task_used_math == task) {
+ grab_fpu();
+ fpsave(&task->thread.fpu.hard);
+ release_fpu();
+ last_task_used_math = 0;
+ regs->sr |= SR_FD;
+ }
+
+ ((long *)&task->thread.fpu)[addr / sizeof(unsigned long)] = data;
+ return 0;
+}
+
+asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
+{
+ struct task_struct *child;
+ int ret;
+
+ lock_kernel();
+ ret = -EPERM;
+ if (request == PTRACE_TRACEME) {
+ /* are we already being traced? */
+ if (current->ptrace & PT_PTRACED)
+ goto out;
+ /* set the ptrace bit in the process flags. */
+ current->ptrace |= PT_PTRACED;
+ ret = 0;
+ goto out;
+ }
+ ret = -ESRCH;
+ read_lock(&tasklist_lock);
+ child = find_task_by_pid(pid);
+ if (child)
+ get_task_struct(child);
+ read_unlock(&tasklist_lock);
+ if (!child)
+ goto out;
+
+ ret = -EPERM;
+ if (pid == 1) /* you may not mess with init */
+ goto out_tsk;
+
+ if (request == PTRACE_ATTACH) {
+ ret = ptrace_attach(child);
+ goto out_tsk;
+ }
+
+ ret = ptrace_check_attach(child, request == PTRACE_KILL);
+ if (ret < 0)
+ goto out_tsk;
+
+ switch (request) {
+ /* when I and D space are separate, these will need to be fixed. */
+ case PTRACE_PEEKTEXT: /* read word at location addr. */
+ case PTRACE_PEEKDATA: {
+ unsigned long tmp;
+ int copied;
+
+ copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+ ret = -EIO;
+ if (copied != sizeof(tmp))
+ break;
+ ret = put_user(tmp,(unsigned long *) data);
+ break;
+ }
+
+ /* read the word at location addr in the USER area. */
+ case PTRACE_PEEKUSR: {
+ unsigned long tmp;
+
+ ret = -EIO;
+ if ((addr & 3) || addr < 0)
+ break;
+
+ if (addr < sizeof(struct pt_regs))
+ tmp = get_stack_long(child, addr);
+ else if ((addr >= offsetof(struct user, fpu)) &&
+ (addr < offsetof(struct user, u_fpvalid))) {
+ tmp = get_fpu_long(child, addr - offsetof(struct user, fpu));
+ } else if (addr == offsetof(struct user, u_fpvalid)) {
+ tmp = child->used_math;
+ } else {
+ break;
+ }
+ ret = put_user(tmp, (unsigned long *)data);
+ break;
+ }
+
+ /* when I and D space are separate, this will have to be fixed. */
+ case PTRACE_POKETEXT: /* write the word at location addr. */
+ case PTRACE_POKEDATA:
+ ret = 0;
+ if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
+ break;
+ ret = -EIO;
+ break;
+
+ case PTRACE_POKEUSR:
+ /* write the word at location addr in the USER area. We must
+ disallow any changes to certain SR bits or u_fpvalid, since
+ this could crash the kernel or result in a security
+ loophole. */
+ ret = -EIO;
+ if ((addr & 3) || addr < 0)
+ break;
+
+ if (addr < sizeof(struct pt_regs)) {
+ /* Ignore change of top 32 bits of SR */
+ if (addr == offsetof (struct pt_regs, sr)+4)
+ {
+ ret = 0;
+ break;
+ }
+ /* If lower 32 bits of SR, ignore non-user bits */
+ if (addr == offsetof (struct pt_regs, sr))
+ {
+ long cursr = get_stack_long(child, addr);
+ data &= ~(SR_MASK);
+ data |= (cursr & SR_MASK);
+ }
+ ret = put_stack_long(child, addr, data);
+ }
+ else if ((addr >= offsetof(struct user, fpu)) &&
+ (addr < offsetof(struct user, u_fpvalid))) {
+ ret = put_fpu_long(child, addr - offsetof(struct user, fpu), data);
+ }
+ break;
+
+ case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
+ case PTRACE_CONT: { /* restart after signal. */
+ ret = -EIO;
+ if ((unsigned long) data > _NSIG)
+ break;
+ if (request == PTRACE_SYSCALL)
+ set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+ else
+ clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+ child->exit_code = data;
+ wake_up_process(child);
+ ret = 0;
+ break;
+ }
+
+/*
+ * make the child exit. Best I can do is send it a sigkill.
+ * perhaps it should be put in the status that it wants to
+ * exit.
+ */
+ case PTRACE_KILL: {
+ ret = 0;
+ if (child->state == TASK_ZOMBIE) /* already dead */
+ break;
+ child->exit_code = SIGKILL;
+ wake_up_process(child);
+ break;
+ }
+
+ case PTRACE_SINGLESTEP: { /* set the trap flag. */
+ struct pt_regs *regs;
+
+ ret = -EIO;
+ if ((unsigned long) data > _NSIG)
+ break;
+ clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+ if ((child->ptrace & PT_DTRACE) == 0) {
+ /* Spurious delayed TF traps may occur */
+ child->ptrace |= PT_DTRACE;
+ }
+
+ regs = child->thread.uregs;
+
+ regs->sr |= SR_SSTEP; /* auto-resetting upon exception */
+
+ child->exit_code = data;
+ /* give it a chance to run. */
+ wake_up_process(child);
+ ret = 0;
+ break;
+ }
+
+ case PTRACE_DETACH: /* detach a process that was attached. */
+ ret = ptrace_detach(child, data);
+ break;
+
+ default:
+ ret = ptrace_request(child, request, addr, data);
+ break;
+ }
+out_tsk:
+ put_task_struct(child);
+out:
+ unlock_kernel();
+ return ret;
+}
+
+asmlinkage void syscall_trace(void)
+{
+ struct task_struct *tsk = current;
+
+ if (!test_thread_flag(TIF_SYSCALL_TRACE))
+ return;
+ if (!(tsk->ptrace & PT_PTRACED))
+ return;
+
+ tsk->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
+ ? 0x80 : 0);
+ tsk->state = TASK_STOPPED;
+ notify_parent(tsk, SIGCHLD);
+ schedule();
+ /*
+ * this isn't the same as continuing with a signal, but it will do
+ * for normal use. strace only continues with a signal if the
+ * stopping signal is not SIGTRAP. -brl
+ */
+ if (tsk->exit_code) {
+ send_sig(tsk->exit_code, tsk, 1);
+ tsk->exit_code = 0;
+ }
+}
+
+/* Called with interrupts disabled */
+asmlinkage void do_single_step(unsigned long long vec, struct pt_regs *regs)
+{
+ /* This is called after a single step exception (DEBUGSS).
+ There is no need to change the PC, as it is a post-execution
+ exception, as entry.S does not do anything to the PC for DEBUGSS.
+ We need to clear the Single Step setting in SR to avoid
+ continually stepping. */
+ local_irq_enable();
+ regs->sr &= ~SR_SSTEP;
+ force_sig(SIGTRAP, current);
+}
+
+/* Called with interrupts disabled */
+asmlinkage void do_software_break_point(unsigned long long vec,
+ struct pt_regs *regs)
+{
+ /* We need to forward step the PC, to counteract the backstep done
+ in signal.c. */
+ local_irq_enable();
+ force_sig(SIGTRAP, current);
+ regs->pc += 4;
+}
+
+/*
+ * Called by kernel/ptrace.c when detaching..
+ *
+ * Make sure single step bits etc are not set.
+ */
+void ptrace_disable(struct task_struct *child)
+{
+ /* nothing to do.. */
+}
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/kernel/setup.c
+ *
+ * sh64 Arch Support
+ *
+ * This file handles the architecture-dependent parts of initialization
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003, 2004 Paul Mundt
+ *
+ * benedict.gaster@superh.com: 2nd May 2002
+ * Modified to use the empty_zero_page to pass command line arguments.
+ *
+ * benedict.gaster@superh.com: 3rd May 2002
+ * Added support for ramdisk, removing statically linked romfs at the same time.
+ *
+ * lethal@linux-sh.org: 15th May 2003
+ * Added generic procfs cpuinfo reporting. Make boards just export their name.
+ *
+ * lethal@linux-sh.org: 25th May 2003
+ * Added generic get_cpu_subtype() for subtype reporting from cpu_data->type.
+ *
+ */
+#include <linux/errno.h>
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/seq_file.h>
+#include <linux/blkdev.h>
+#include <linux/bootmem.h>
+#include <linux/console.h>
+#include <linux/root_dev.h>
+#include <linux/cpu.h>
+#include <linux/initrd.h>
+#include <asm/processor.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/platform.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/sections.h>
+#include <asm/setup.h>
+#include <asm/smp.h>
+
+#ifdef CONFIG_VT
+#include <linux/console.h>
+#endif
+
+struct screen_info screen_info;
+
+/* On a PC this would be initialised as a result of the BIOS detecting the
+ * mouse. */
+unsigned char aux_device_present = 0xaa;
+
+#ifdef CONFIG_BLK_DEV_RAM
+extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */
+extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */
+extern int rd_image_start; /* starting block # of image */
+#endif
+
+extern int root_mountflags;
+extern char *get_system_type(void);
+extern void platform_setup(void);
+extern void platform_monitor(void);
+extern void platform_reserve(void);
+extern int sh64_cache_init(void);
+extern int sh64_tlb_init(void);
+
+#define RAMDISK_IMAGE_START_MASK 0x07FF
+#define RAMDISK_PROMPT_FLAG 0x8000
+#define RAMDISK_LOAD_FLAG 0x4000
+
+static char command_line[COMMAND_LINE_SIZE] = { 0, };
+unsigned long long memory_start = CONFIG_MEMORY_START;
+unsigned long long memory_end = CONFIG_MEMORY_START + (CONFIG_MEMORY_SIZE_IN_MB * 1024 * 1024);
+
+struct sh_cpuinfo boot_cpu_data;
+
+static inline void parse_mem_cmdline (char ** cmdline_p)
+{
+ char c = ' ', *to = command_line, *from = COMMAND_LINE;
+ int len = 0;
+
+ /* Save unparsed command line copy for /proc/cmdline */
+ memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE);
+ saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
+
+ for (;;) {
+ /*
+ * "mem=XXX[kKmM]" defines a size of memory.
+ */
+ if (c == ' ' && !memcmp(from, "mem=", 4)) {
+ if (to != command_line)
+ to--;
+ {
+ unsigned long mem_size;
+
+ mem_size = memparse(from+4, &from);
+ memory_end = memory_start + mem_size;
+ }
+ }
+ c = *(from++);
+ if (!c)
+ break;
+ if (COMMAND_LINE_SIZE <= ++len)
+ break;
+ *(to++) = c;
+ }
+ *to = '\0';
+
+ *cmdline_p = command_line;
+}
+
+static void __init sh64_cpu_type_detect(void)
+{
+ extern unsigned long long peek_real_address_q(unsigned long long addr);
+ unsigned long long cir;
+ /* Do peeks in real mode to avoid having to set up a mapping for the
+ WPC registers. On SH5-101 cut2, such a mapping would be exposed to
+ an address translation erratum which would make it hard to set up
+ correctly. */
+ cir = peek_real_address_q(0x0d000008);
+
+ if ((cir & 0xffff) == 0x5103) {
+ boot_cpu_data.type = CPU_SH5_103;
+ } else if (((cir >> 32) & 0xffff) == 0x51e2) {
+ /* CPU.VCR aliased at CIR address on SH5-101 */
+ boot_cpu_data.type = CPU_SH5_101;
+ } else {
+ boot_cpu_data.type = CPU_SH_NONE;
+ }
+}
+
+void __init setup_arch(char **cmdline_p)
+{
+ unsigned long bootmap_size, i;
+ unsigned long first_pfn, start_pfn, last_pfn, pages;
+
+#ifdef CONFIG_EARLY_PRINTK
+ extern void enable_early_printk(void);
+
+ /*
+ * Setup Early SCIF console
+ */
+ enable_early_printk();
+#endif
+
+ /*
+ * Setup TLB mappings
+ */
+ sh64_tlb_init();
+
+ /*
+ * Caches are already initialized by the time we get here, so we just
+ * fill in cpu_data info for the caches.
+ */
+ sh64_cache_init();
+
+ platform_setup();
+ platform_monitor();
+
+ sh64_cpu_type_detect();
+
+ ROOT_DEV = old_decode_dev(ORIG_ROOT_DEV);
+
+#ifdef CONFIG_BLK_DEV_RAM
+ rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK;
+ rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0);
+ rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0);
+#endif
+
+ if (!MOUNT_ROOT_RDONLY)
+ root_mountflags &= ~MS_RDONLY;
+ init_mm.start_code = (unsigned long) _text;
+ init_mm.end_code = (unsigned long) _etext;
+ init_mm.end_data = (unsigned long) _edata;
+ init_mm.brk = (unsigned long) _end;
+
+ code_resource.start = __pa(_text);
+ code_resource.end = __pa(_etext)-1;
+ data_resource.start = __pa(_etext);
+ data_resource.end = __pa(_edata)-1;
+
+ parse_mem_cmdline(cmdline_p);
+
+ /*
+ * Find the lowest and highest page frame numbers we have available
+ */
+ first_pfn = PFN_DOWN(memory_start);
+ last_pfn = PFN_DOWN(memory_end);
+ pages = last_pfn - first_pfn;
+
+ /*
+ * Partially used pages are not usable - thus
+ * we are rounding upwards:
+ */
+ start_pfn = PFN_UP(__pa(_end));
+
+ /*
+ * Find a proper area for the bootmem bitmap. After this
+ * bootstrap step all allocations (until the page allocator
+ * is intact) must be done via bootmem_alloc().
+ */
+ bootmap_size = init_bootmem_node(NODE_DATA(0), start_pfn,
+ first_pfn,
+ last_pfn);
+ /*
+ * Round it up.
+ */
+ bootmap_size = PFN_PHYS(PFN_UP(bootmap_size));
+
+ /*
+ * Register fully available RAM pages with the bootmem allocator.
+ */
+ free_bootmem_node(NODE_DATA(0), PFN_PHYS(first_pfn), PFN_PHYS(pages));
+
+ /*
+ * Reserve all kernel sections + bootmem bitmap + a guard page.
+ */
+ reserve_bootmem_node(NODE_DATA(0), PFN_PHYS(first_pfn),
+ (PFN_PHYS(start_pfn) + bootmap_size + PAGE_SIZE) - PFN_PHYS(first_pfn));
+
+ /*
+ * Reserve platform dependent sections
+ */
+ platform_reserve();
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (LOADER_TYPE && INITRD_START) {
+ if (INITRD_START + INITRD_SIZE <= (PFN_PHYS(last_pfn))) {
+ reserve_bootmem_node(NODE_DATA(0), INITRD_START + __MEMORY_START, INITRD_SIZE);
+
+ initrd_start =
+ (long) INITRD_START ? INITRD_START + PAGE_OFFSET + __MEMORY_START : 0;
+
+ initrd_end = initrd_start + INITRD_SIZE;
+ } else {
+ printk("initrd extends beyond end of memory "
+ "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
+ (long) INITRD_START + INITRD_SIZE,
+ PFN_PHYS(last_pfn));
+ initrd_start = 0;
+ }
+ }
+#endif
+
+ /*
+ * Claim all RAM, ROM, and I/O resources.
+ */
+
+ /* Kernel RAM */
+ request_resource(&iomem_resource, &code_resource);
+ request_resource(&iomem_resource, &data_resource);
+
+ /* Other KRAM space */
+ for (i = 0; i < STANDARD_KRAM_RESOURCES - 2; i++)
+ request_resource(&iomem_resource,
+ &platform_parms.kram_res_p[i]);
+
+ /* XRAM space */
+ for (i = 0; i < STANDARD_XRAM_RESOURCES; i++)
+ request_resource(&iomem_resource,
+ &platform_parms.xram_res_p[i]);
+
+ /* ROM space */
+ for (i = 0; i < STANDARD_ROM_RESOURCES; i++)
+ request_resource(&iomem_resource,
+ &platform_parms.rom_res_p[i]);
+
+ /* I/O space */
+ for (i = 0; i < STANDARD_IO_RESOURCES; i++)
+ request_resource(&ioport_resource,
+ &platform_parms.io_res_p[i]);
+
+
+#ifdef CONFIG_VT
+#if defined(CONFIG_VGA_CONSOLE)
+ conswitchp = &vga_con;
+#elif defined(CONFIG_DUMMY_CONSOLE)
+ conswitchp = &dummy_con;
+#endif
+#endif
+
+ printk("Hardware FPU: %s\n", fpu_in_use ? "enabled" : "disabled");
+
+ paging_init();
+}
+
+void __xchg_called_with_bad_pointer(void)
+{
+ printk(KERN_EMERG "xchg() called with bad pointer !\n");
+}
+
+static struct cpu cpu[1];
+
+static int __init topology_init(void)
+{
+ return register_cpu(cpu, 0, NULL);
+}
+
+subsys_initcall(topology_init);
+
+/*
+ * Get CPU information
+ */
+static const char *cpu_name[] = {
+ [CPU_SH5_101] = "SH5-101",
+ [CPU_SH5_103] = "SH5-103",
+ [CPU_SH_NONE] = "Unknown",
+};
+
+const char *get_cpu_subtype(void)
+{
+ return cpu_name[boot_cpu_data.type];
+}
+
+#ifdef CONFIG_PROC_FS
+static int show_cpuinfo(struct seq_file *m,void *v)
+{
+ unsigned int cpu = smp_processor_id();
+
+ if (!cpu)
+ seq_printf(m, "machine\t\t: %s\n", get_system_type());
+
+ seq_printf(m, "processor\t: %d\n", cpu);
+ seq_printf(m, "cpu family\t: SH-5\n");
+ seq_printf(m, "cpu type\t: %s\n", get_cpu_subtype());
+
+ seq_printf(m, "icache size\t: %dK-bytes\n",
+ (boot_cpu_data.icache.ways *
+ boot_cpu_data.icache.sets *
+ boot_cpu_data.icache.linesz) >> 10);
+ seq_printf(m, "dcache size\t: %dK-bytes\n",
+ (boot_cpu_data.dcache.ways *
+ boot_cpu_data.dcache.sets *
+ boot_cpu_data.dcache.linesz) >> 10);
+ seq_printf(m, "itlb entries\t: %d\n", boot_cpu_data.itlb.entries);
+ seq_printf(m, "dtlb entries\t: %d\n", boot_cpu_data.dtlb.entries);
+
+#define PRINT_CLOCK(name, value) \
+ seq_printf(m, name " clock\t: %d.%02dMHz\n", \
+ ((value) / 1000000), ((value) % 1000000)/10000)
+
+ PRINT_CLOCK("cpu", boot_cpu_data.cpu_clock);
+ PRINT_CLOCK("bus", boot_cpu_data.bus_clock);
+ PRINT_CLOCK("module", boot_cpu_data.module_clock);
+
+ seq_printf(m, "bogomips\t: %lu.%02lu\n\n",
+ (loops_per_jiffy*HZ+2500)/500000,
+ ((loops_per_jiffy*HZ+2500)/5000) % 100);
+
+ return 0;
+}
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+ return (void*)(*pos == 0);
+}
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ return NULL;
+}
+static void c_stop(struct seq_file *m, void *v)
+{
+}
+struct seq_operations cpuinfo_op = {
+ .start = c_start,
+ .next = c_next,
+ .stop = c_stop,
+ .show = show_cpuinfo,
+};
+#endif /* CONFIG_PROC_FS */
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/kernel/sh_ksyms.c
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/rwsem.h>
+#include <linux/module.h>
+#include <linux/smp.h>
+#include <linux/user.h>
+#include <linux/elfcore.h>
+#include <linux/sched.h>
+#include <linux/in6.h>
+#include <linux/interrupt.h>
+#include <linux/smp_lock.h>
+
+#include <asm/semaphore.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+#include <asm/io.h>
+#include <asm/hardirq.h>
+#include <asm/delay.h>
+#include <asm/irq.h>
+
+extern void dump_thread(struct pt_regs *, struct user *);
+extern int dump_fpu(struct pt_regs *, elf_fpregset_t *);
+
+#if 0
+/* Not yet - there's no declaration of drive_info anywhere. */
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_HD) || defined(CONFIG_BLK_DEV_IDE_MODULE) || defined(CONFIG_BLK_DEV_HD_MODULE)
+extern struct drive_info_struct drive_info;
+EXPORT_SYMBOL(drive_info);
+#endif
+#endif
+
+/* platform dependent support */
+EXPORT_SYMBOL(dump_thread);
+EXPORT_SYMBOL(dump_fpu);
+EXPORT_SYMBOL(iounmap);
+EXPORT_SYMBOL(enable_irq);
+EXPORT_SYMBOL(disable_irq);
+EXPORT_SYMBOL(kernel_thread);
+
+/* Networking helper routines. */
+EXPORT_SYMBOL(csum_partial_copy);
+
+EXPORT_SYMBOL(strtok);
+EXPORT_SYMBOL(strpbrk);
+EXPORT_SYMBOL(strstr);
+
+#ifdef CONFIG_VT
+EXPORT_SYMBOL(screen_info);
+#endif
+
+EXPORT_SYMBOL_NOVERS(__down);
+EXPORT_SYMBOL_NOVERS(__down_trylock);
+EXPORT_SYMBOL_NOVERS(__up);
+EXPORT_SYMBOL_NOVERS(__put_user_asm_l);
+EXPORT_SYMBOL_NOVERS(__get_user_asm_l);
+EXPORT_SYMBOL_NOVERS(memcmp);
+EXPORT_SYMBOL_NOVERS(memcpy);
+EXPORT_SYMBOL_NOVERS(memset);
+EXPORT_SYMBOL_NOVERS(memscan);
+EXPORT_SYMBOL_NOVERS(strchr);
+EXPORT_SYMBOL_NOVERS(strlen);
+
+EXPORT_SYMBOL(flush_dcache_page);
+
+/* Ugh. These come in from libgcc.a at link time. */
+
+extern void __sdivsi3(void);
+extern void __muldi3(void);
+extern void __udivsi3(void);
+EXPORT_SYMBOL_NOVERS(__sdivsi3);
+EXPORT_SYMBOL_NOVERS(__muldi3);
+EXPORT_SYMBOL_NOVERS(__udivsi3);
+
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/kernel/signal.c
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003 Paul Mundt
+ * Copyright (C) 2004 Richard Curnow
+ *
+ * Started from sh version.
+ *
+ */
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/personality.h>
+#include <linux/suspend.h>
+#include <linux/ptrace.h>
+#include <linux/unistd.h>
+#include <linux/stddef.h>
+#include <linux/personality.h>
+#include <asm/ucontext.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+
+
+#define REG_RET 9
+#define REG_ARG1 2
+#define REG_ARG2 3
+#define REG_ARG3 4
+#define REG_SP 15
+#define REG_PR 18
+#define REF_REG_RET regs->regs[REG_RET]
+#define REF_REG_SP regs->regs[REG_SP]
+#define DEREF_REG_PR regs->regs[REG_PR]
+
+#define DEBUG_SIG 0
+
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset);
+
+/*
+ * Atomically swap in the new signal mask, and wait for a signal.
+ */
+
+asmlinkage int
+sys_sigsuspend(old_sigset_t mask,
+ unsigned long r3, unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7,
+ struct pt_regs * regs)
+{
+ sigset_t saveset;
+
+ mask &= _BLOCKABLE;
+ spin_lock_irq(¤t->sighand->siglock);
+ saveset = current->blocked;
+ siginitset(¤t->blocked, mask);
+ recalc_sigpending();
+ spin_unlock_irq(¤t->sighand->siglock);
+
+ REF_REG_RET = -EINTR;
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ regs->pc += 4; /* because sys_sigreturn decrements the pc */
+ if (do_signal(regs, &saveset)) {
+ /* pc now points at signal handler. Need to decrement
+ it because entry.S will increment it. */
+ regs->pc -= 4;
+ return -EINTR;
+ }
+ }
+}
+
+asmlinkage int
+sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize,
+ unsigned long r4, unsigned long r5, unsigned long r6,
+ unsigned long r7,
+ struct pt_regs * regs)
+{
+ sigset_t saveset, newset;
+
+ /* XXX: Don't preclude handling different sized sigset_t's. */
+ if (sigsetsize != sizeof(sigset_t))
+ return -EINVAL;
+
+ if (copy_from_user(&newset, unewset, sizeof(newset)))
+ return -EFAULT;
+ sigdelsetmask(&newset, ~_BLOCKABLE);
+ spin_lock_irq(¤t->sighand->siglock);
+ saveset = current->blocked;
+ current->blocked = newset;
+ recalc_sigpending();
+ spin_unlock_irq(¤t->sighand->siglock);
+
+ REF_REG_RET = -EINTR;
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ regs->pc += 4; /* because sys_sigreturn decrements the pc */
+ if (do_signal(regs, &saveset)) {
+ /* pc now points at signal handler. Need to decrement
+ it because entry.S will increment it. */
+ regs->pc -= 4;
+ return -EINTR;
+ }
+ }
+}
+
+asmlinkage int
+sys_sigaction(int sig, const struct old_sigaction __user *act,
+ struct old_sigaction __user *oact)
+{
+ struct k_sigaction new_ka, old_ka;
+ int ret;
+
+ if (act) {
+ old_sigset_t mask;
+ if (verify_area(VERIFY_READ, act, sizeof(*act)) ||
+ __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
+ __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
+ return -EFAULT;
+ __get_user(new_ka.sa.sa_flags, &act->sa_flags);
+ __get_user(mask, &act->sa_mask);
+ siginitset(&new_ka.sa.sa_mask, mask);
+ }
+
+ ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+ if (!ret && oact) {
+ if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) ||
+ __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
+ __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
+ return -EFAULT;
+ __put_user(old_ka.sa.sa_flags, &oact->sa_flags);
+ __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
+ }
+
+ return ret;
+}
+
+asmlinkage int
+sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss,
+ unsigned long r4, unsigned long r5, unsigned long r6,
+ unsigned long r7,
+ struct pt_regs * regs)
+{
+ return do_sigaltstack(uss, uoss, REF_REG_SP);
+}
+
+
+/*
+ * Do a signal return; undo the signal stack.
+ */
+
+struct sigframe
+{
+ struct sigcontext sc;
+ unsigned long extramask[_NSIG_WORDS-1];
+ long long retcode[2];
+};
+
+struct rt_sigframe
+{
+ struct siginfo __user *pinfo;
+ void *puc;
+ struct siginfo info;
+ struct ucontext uc;
+ long long retcode[2];
+};
+
+#ifndef CONFIG_NOFPU_SUPPORT
+static inline int
+restore_sigcontext_fpu(struct pt_regs *regs, struct sigcontext __user *sc)
+{
+ int err = 0;
+ int fpvalid;
+
+ err |= __get_user (fpvalid, &sc->sc_fpvalid);
+ current->used_math = fpvalid;
+ if (! fpvalid)
+ return err;
+
+ if (current == last_task_used_math) {
+ last_task_used_math = NULL;
+ regs->sr |= SR_FD;
+ }
+
+ err |= __copy_from_user(¤t->thread.fpu.hard, &sc->sc_fpregs[0],
+ (sizeof(long long) * 32) + (sizeof(int) * 1));
+
+ return err;
+}
+
+static inline int
+setup_sigcontext_fpu(struct pt_regs *regs, struct sigcontext __user *sc)
+{
+ int err = 0;
+ int fpvalid;
+
+ fpvalid = current->used_math;
+ err |= __put_user(fpvalid, &sc->sc_fpvalid);
+ if (! fpvalid)
+ return err;
+
+ if (current == last_task_used_math) {
+ grab_fpu();
+ fpsave(¤t->thread.fpu.hard);
+ release_fpu();
+ last_task_used_math = NULL;
+ regs->sr |= SR_FD;
+ }
+
+ err |= __copy_to_user(&sc->sc_fpregs[0], ¤t->thread.fpu.hard,
+ (sizeof(long long) * 32) + (sizeof(int) * 1));
+ current->used_math = 0;
+
+ return err;
+}
+#else
+static inline int
+restore_sigcontext_fpu(struct pt_regs *regs, struct sigcontext __user *sc)
+{}
+static inline int
+setup_sigcontext_fpu(struct pt_regs *regs, struct sigcontext __user *sc)
+{}
+#endif
+
+static int
+restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, long long *r2_p)
+{
+ unsigned int err = 0;
+ unsigned long long current_sr, new_sr;
+#define SR_MASK 0xffff8cfd
+
+#define COPY(x) err |= __get_user(regs->x, &sc->sc_##x)
+
+ COPY(regs[0]); COPY(regs[1]); COPY(regs[2]); COPY(regs[3]);
+ COPY(regs[4]); COPY(regs[5]); COPY(regs[6]); COPY(regs[7]);
+ COPY(regs[8]); COPY(regs[9]); COPY(regs[10]); COPY(regs[11]);
+ COPY(regs[12]); COPY(regs[13]); COPY(regs[14]); COPY(regs[15]);
+ COPY(regs[16]); COPY(regs[17]); COPY(regs[18]); COPY(regs[19]);
+ COPY(regs[20]); COPY(regs[21]); COPY(regs[22]); COPY(regs[23]);
+ COPY(regs[24]); COPY(regs[25]); COPY(regs[26]); COPY(regs[27]);
+ COPY(regs[28]); COPY(regs[29]); COPY(regs[30]); COPY(regs[31]);
+ COPY(regs[32]); COPY(regs[33]); COPY(regs[34]); COPY(regs[35]);
+ COPY(regs[36]); COPY(regs[37]); COPY(regs[38]); COPY(regs[39]);
+ COPY(regs[40]); COPY(regs[41]); COPY(regs[42]); COPY(regs[43]);
+ COPY(regs[44]); COPY(regs[45]); COPY(regs[46]); COPY(regs[47]);
+ COPY(regs[48]); COPY(regs[49]); COPY(regs[50]); COPY(regs[51]);
+ COPY(regs[52]); COPY(regs[53]); COPY(regs[54]); COPY(regs[55]);
+ COPY(regs[56]); COPY(regs[57]); COPY(regs[58]); COPY(regs[59]);
+ COPY(regs[60]); COPY(regs[61]); COPY(regs[62]);
+ COPY(tregs[0]); COPY(tregs[1]); COPY(tregs[2]); COPY(tregs[3]);
+ COPY(tregs[4]); COPY(tregs[5]); COPY(tregs[6]); COPY(tregs[7]);
+
+ /* Prevent the signal handler manipulating SR in a way that can
+ crash the kernel. i.e. only allow S, Q, M, PR, SZ, FR to be
+ modified */
+ current_sr = regs->sr;
+ err |= __get_user(new_sr, &sc->sc_sr);
+ regs->sr &= SR_MASK;
+ regs->sr |= (new_sr & ~SR_MASK);
+
+ COPY(pc);
+
+#undef COPY
+
+ /* Must do this last in case it sets regs->sr.fd (i.e. after rest of sr
+ * has been restored above.) */
+ err |= restore_sigcontext_fpu(regs, sc);
+
+ regs->syscall_nr = -1; /* disable syscall checks */
+ err |= __get_user(*r2_p, &sc->sc_regs[REG_RET]);
+ return err;
+}
+
+asmlinkage int sys_sigreturn(unsigned long r2, unsigned long r3,
+ unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7,
+ struct pt_regs * regs)
+{
+ struct sigframe __user *frame = (struct sigframe __user *) (long) REF_REG_SP;
+ sigset_t set;
+ long long ret;
+
+ if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+
+ if (__get_user(set.sig[0], &frame->sc.oldmask)
+ || (_NSIG_WORDS > 1
+ && __copy_from_user(&set.sig[1], &frame->extramask,
+ sizeof(frame->extramask))))
+ goto badframe;
+
+ sigdelsetmask(&set, ~_BLOCKABLE);
+
+ spin_lock_irq(¤t->sighand->siglock);
+ current->blocked = set;
+ recalc_sigpending();
+ spin_unlock_irq(¤t->sighand->siglock);
+
+ if (restore_sigcontext(regs, &frame->sc, &ret))
+ goto badframe;
+ regs->pc -= 4;
+
+ return (int) ret;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+asmlinkage int sys_rt_sigreturn(unsigned long r2, unsigned long r3,
+ unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7,
+ struct pt_regs * regs)
+{
+ struct rt_sigframe __user *frame = (struct rt_sigframe __user *) (long) REF_REG_SP;
+ sigset_t set;
+ stack_t __user st;
+ long long ret;
+
+ if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+
+ if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
+ goto badframe;
+
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ spin_lock_irq(¤t->sighand->siglock);
+ current->blocked = set;
+ recalc_sigpending();
+ spin_unlock_irq(¤t->sighand->siglock);
+
+ if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &ret))
+ goto badframe;
+ regs->pc -= 4;
+
+ if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st)))
+ goto badframe;
+ /* It is more difficult to avoid calling this function than to
+ call it and ignore errors. */
+ do_sigaltstack(&st, NULL, REF_REG_SP);
+
+ return (int) ret;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+/*
+ * Set up a signal frame.
+ */
+
+static int
+setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
+ unsigned long mask)
+{
+ int err = 0;
+
+ /* Do this first, otherwise is this sets sr->fd, that value isn't preserved. */
+ err |= setup_sigcontext_fpu(regs, sc);
+
+#define COPY(x) err |= __put_user(regs->x, &sc->sc_##x)
+
+ COPY(regs[0]); COPY(regs[1]); COPY(regs[2]); COPY(regs[3]);
+ COPY(regs[4]); COPY(regs[5]); COPY(regs[6]); COPY(regs[7]);
+ COPY(regs[8]); COPY(regs[9]); COPY(regs[10]); COPY(regs[11]);
+ COPY(regs[12]); COPY(regs[13]); COPY(regs[14]); COPY(regs[15]);
+ COPY(regs[16]); COPY(regs[17]); COPY(regs[18]); COPY(regs[19]);
+ COPY(regs[20]); COPY(regs[21]); COPY(regs[22]); COPY(regs[23]);
+ COPY(regs[24]); COPY(regs[25]); COPY(regs[26]); COPY(regs[27]);
+ COPY(regs[28]); COPY(regs[29]); COPY(regs[30]); COPY(regs[31]);
+ COPY(regs[32]); COPY(regs[33]); COPY(regs[34]); COPY(regs[35]);
+ COPY(regs[36]); COPY(regs[37]); COPY(regs[38]); COPY(regs[39]);
+ COPY(regs[40]); COPY(regs[41]); COPY(regs[42]); COPY(regs[43]);
+ COPY(regs[44]); COPY(regs[45]); COPY(regs[46]); COPY(regs[47]);
+ COPY(regs[48]); COPY(regs[49]); COPY(regs[50]); COPY(regs[51]);
+ COPY(regs[52]); COPY(regs[53]); COPY(regs[54]); COPY(regs[55]);
+ COPY(regs[56]); COPY(regs[57]); COPY(regs[58]); COPY(regs[59]);
+ COPY(regs[60]); COPY(regs[61]); COPY(regs[62]);
+ COPY(tregs[0]); COPY(tregs[1]); COPY(tregs[2]); COPY(tregs[3]);
+ COPY(tregs[4]); COPY(tregs[5]); COPY(tregs[6]); COPY(tregs[7]);
+ COPY(sr); COPY(pc);
+
+#undef COPY
+
+ err |= __put_user(mask, &sc->oldmask);
+
+ return err;
+}
+
+/*
+ * Determine which stack to use..
+ */
+static inline void __user *
+get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size)
+{
+ if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! on_sig_stack(sp))
+ sp = current->sas_ss_sp + current->sas_ss_size;
+
+ return (void __user *)((sp - frame_size) & -8ul);
+}
+
+void sa_default_restorer(void); /* See comments below */
+void sa_default_rt_restorer(void); /* See comments below */
+
+static void setup_frame(int sig, struct k_sigaction *ka,
+ sigset_t *set, struct pt_regs *regs)
+{
+ struct sigframe __user *frame;
+ int err = 0;
+ int signal;
+
+ frame = get_sigframe(ka, regs->regs[REG_SP], sizeof(*frame));
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ goto give_sigsegv;
+
+ signal = current_thread_info()->exec_domain
+ && current_thread_info()->exec_domain->signal_invmap
+ && sig < 32
+ ? current_thread_info()->exec_domain->signal_invmap[sig]
+ : sig;
+
+ err |= setup_sigcontext(&frame->sc, regs, set->sig[0]);
+
+ /* Give up earlier as i386, in case */
+ if (err)
+ goto give_sigsegv;
+
+ if (_NSIG_WORDS > 1) {
+ err |= __copy_to_user(frame->extramask, &set->sig[1],
+ sizeof(frame->extramask)); }
+
+ /* Give up earlier as i386, in case */
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa.sa_flags & SA_RESTORER) {
+ DEREF_REG_PR = (unsigned long) ka->sa.sa_restorer | 0x1;
+
+ /*
+ * On SH5 all edited pointers are subject to NEFF
+ */
+ DEREF_REG_PR = (DEREF_REG_PR & NEFF_SIGN) ?
+ (DEREF_REG_PR | NEFF_MASK) : DEREF_REG_PR;
+ } else {
+ /*
+ * Different approach on SH5.
+ * . Endianness independent asm code gets placed in entry.S .
+ * This is limited to four ASM instructions corresponding
+ * to two long longs in size.
+ * . err checking is done on the else branch only
+ * . flush_icache_range() is called upon __put_user() only
+ * . all edited pointers are subject to NEFF
+ * . being code, linker turns ShMedia bit on, always
+ * dereference index -1.
+ */
+ DEREF_REG_PR = (unsigned long) frame->retcode | 0x01;
+ DEREF_REG_PR = (DEREF_REG_PR & NEFF_SIGN) ?
+ (DEREF_REG_PR | NEFF_MASK) : DEREF_REG_PR;
+
+ if (__copy_to_user(frame->retcode,
+ (unsigned long long)sa_default_restorer & (~1), 16) != 0)
+ goto give_sigsegv;
+
+ /* Cohere the trampoline with the I-cache. */
+ flush_cache_sigtramp(DEREF_REG_PR-1, DEREF_REG_PR-1+16);
+ }
+
+ /*
+ * Set up registers for signal handler.
+ * All edited pointers are subject to NEFF.
+ */
+ regs->regs[REG_SP] = (unsigned long) frame;
+ regs->regs[REG_SP] = (regs->regs[REG_SP] & NEFF_SIGN) ?
+ (regs->regs[REG_SP] | NEFF_MASK) : regs->regs[REG_SP];
+ regs->regs[REG_ARG1] = signal; /* Arg for signal handler */
+
+ /* FIXME:
+ The glibc profiling support for SH-5 needs to be passed a sigcontext
+ so it can retrieve the PC. At some point during 2003 the glibc
+ support was changed to receive the sigcontext through the 2nd
+ argument, but there are still versions of libc.so in use that use
+ the 3rd argument. Until libc.so is stabilised, pass the sigcontext
+ through both 2nd and 3rd arguments.
+ */
+
+ regs->regs[REG_ARG2] = (unsigned long long)(unsigned long)(signed long)&frame->sc;
+ regs->regs[REG_ARG3] = (unsigned long long)(unsigned long)(signed long)&frame->sc;
+
+ regs->pc = (unsigned long) ka->sa.sa_handler;
+ regs->pc = (regs->pc & NEFF_SIGN) ? (regs->pc | NEFF_MASK) : regs->pc;
+
+ set_fs(USER_DS);
+
+#if DEBUG_SIG
+ /* Broken %016Lx */
+ printk("SIG deliver (#%d,%s:%d): sp=%p pc=%08Lx%08Lx link=%08Lx%08Lx\n",
+ signal,
+ current->comm, current->pid, frame,
+ regs->pc >> 32, regs->pc & 0xffffffff,
+ DEREF_REG_PR >> 32, DEREF_REG_PR & 0xffffffff);
+#endif
+
+ return;
+
+give_sigsegv:
+ if (sig == SIGSEGV)
+ ka->sa.sa_handler = SIG_DFL;
+ force_sig(SIGSEGV, current);
+}
+
+static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs *regs)
+{
+ struct rt_sigframe __user *frame;
+ int err = 0;
+ int signal;
+
+ frame = get_sigframe(ka, regs->regs[REG_SP], sizeof(*frame));
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ goto give_sigsegv;
+
+ signal = current_thread_info()->exec_domain
+ && current_thread_info()->exec_domain->signal_invmap
+ && sig < 32
+ ? current_thread_info()->exec_domain->signal_invmap[sig]
+ : sig;
+
+ err |= __put_user(&frame->info, &frame->pinfo);
+ err |= __put_user(&frame->uc, &frame->puc);
+ err |= copy_siginfo_to_user(&frame->info, info);
+
+ /* Give up earlier as i386, in case */
+ if (err)
+ goto give_sigsegv;
+
+ /* Create the ucontext. */
+ err |= __put_user(0, &frame->uc.uc_flags);
+ err |= __put_user(0, &frame->uc.uc_link);
+ err |= __put_user((void *)current->sas_ss_sp,
+ &frame->uc.uc_stack.ss_sp);
+ err |= __put_user(sas_ss_flags(regs->regs[REG_SP]),
+ &frame->uc.uc_stack.ss_flags);
+ err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
+ err |= setup_sigcontext(&frame->uc.uc_mcontext,
+ regs, set->sig[0]);
+ err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+
+ /* Give up earlier as i386, in case */
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa.sa_flags & SA_RESTORER) {
+ DEREF_REG_PR = (unsigned long) ka->sa.sa_restorer | 0x1;
+
+ /*
+ * On SH5 all edited pointers are subject to NEFF
+ */
+ DEREF_REG_PR = (DEREF_REG_PR & NEFF_SIGN) ?
+ (DEREF_REG_PR | NEFF_MASK) : DEREF_REG_PR;
+ } else {
+ /*
+ * Different approach on SH5.
+ * . Endianness independent asm code gets placed in entry.S .
+ * This is limited to four ASM instructions corresponding
+ * to two long longs in size.
+ * . err checking is done on the else branch only
+ * . flush_icache_range() is called upon __put_user() only
+ * . all edited pointers are subject to NEFF
+ * . being code, linker turns ShMedia bit on, always
+ * dereference index -1.
+ */
+
+ DEREF_REG_PR = (unsigned long) frame->retcode | 0x01;
+ DEREF_REG_PR = (DEREF_REG_PR & NEFF_SIGN) ?
+ (DEREF_REG_PR | NEFF_MASK) : DEREF_REG_PR;
+
+ if (__copy_to_user(frame->retcode,
+ (unsigned long long)sa_default_rt_restorer & (~1), 16) != 0)
+ goto give_sigsegv;
+
+ flush_icache_range(DEREF_REG_PR-1, DEREF_REG_PR-1+15);
+ }
+
+ /*
+ * Set up registers for signal handler.
+ * All edited pointers are subject to NEFF.
+ */
+ regs->regs[REG_SP] = (unsigned long) frame;
+ regs->regs[REG_SP] = (regs->regs[REG_SP] & NEFF_SIGN) ?
+ (regs->regs[REG_SP] | NEFF_MASK) : regs->regs[REG_SP];
+ regs->regs[REG_ARG1] = signal; /* Arg for signal handler */
+ regs->regs[REG_ARG2] = (unsigned long long)(unsigned long)(signed long)&frame->info;
+ regs->regs[REG_ARG3] = (unsigned long long)(unsigned long)(signed long)&frame->uc.uc_mcontext;
+ regs->pc = (unsigned long) ka->sa.sa_handler;
+ regs->pc = (regs->pc & NEFF_SIGN) ? (regs->pc | NEFF_MASK) : regs->pc;
+
+ set_fs(USER_DS);
+
+#if DEBUG_SIG
+ /* Broken %016Lx */
+ printk("SIG deliver (#%d,%s:%d): sp=%p pc=%08Lx%08Lx link=%08Lx%08Lx\n",
+ signal,
+ current->comm, current->pid, frame,
+ regs->pc >> 32, regs->pc & 0xffffffff,
+ DEREF_REG_PR >> 32, DEREF_REG_PR & 0xffffffff);
+#endif
+
+ return;
+
+give_sigsegv:
+ if (sig == SIGSEGV)
+ ka->sa.sa_handler = SIG_DFL;
+ force_sig(SIGSEGV, current);
+}
+
+/*
+ * OK, we're invoking a handler
+ */
+
+static void
+handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset,
+ struct pt_regs * regs)
+{
+ struct k_sigaction *ka = ¤t->sighand->action[sig-1];
+
+ /* Are we from a system call? */
+ if (regs->syscall_nr >= 0) {
+ /* If so, check system call restarting.. */
+ switch (regs->regs[REG_RET]) {
+ case -ERESTARTNOHAND:
+ regs->regs[REG_RET] = -EINTR;
+ break;
+
+ case -ERESTARTSYS:
+ if (!(ka->sa.sa_flags & SA_RESTART)) {
+ regs->regs[REG_RET] = -EINTR;
+ break;
+ }
+ /* fallthrough */
+ case -ERESTARTNOINTR:
+ /* Decode syscall # */
+ regs->regs[REG_RET] = regs->syscall_nr;
+ regs->pc -= 4;
+ }
+ }
+
+ /* Set up the stack frame */
+ if (ka->sa.sa_flags & SA_SIGINFO)
+ setup_rt_frame(sig, ka, info, oldset, regs);
+ else
+ setup_frame(sig, ka, oldset, regs);
+
+ if (ka->sa.sa_flags & SA_ONESHOT)
+ ka->sa.sa_handler = SIG_DFL;
+
+ if (!(ka->sa.sa_flags & SA_NODEFER)) {
+ spin_lock_irq(¤t->sighand->siglock);
+ sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask);
+ sigaddset(¤t->blocked,sig);
+ recalc_sigpending();
+ spin_unlock_irq(¤t->sighand->siglock);
+ }
+}
+
+/*
+ * Note that 'init' is a special process: it doesn't get signals it doesn't
+ * want to handle. Thus you cannot kill init even with a SIGKILL even by
+ * mistake.
+ *
+ * Note that we go through the signals twice: once to check the signals that
+ * the kernel can handle, and then we build all the user-level signal handling
+ * stack-frames in one go after that.
+ */
+int do_signal(struct pt_regs *regs, sigset_t *oldset)
+{
+ siginfo_t info;
+ int signr;
+
+ /*
+ * We want the common case to go fast, which
+ * is why we may in certain cases get here from
+ * kernel mode. Just return without doing anything
+ * if so.
+ */
+ if (!user_mode(regs))
+ return 1;
+
+ if (current->flags & PF_FREEZE) {
+ refrigerator(0);
+ goto no_signal;
+ }
+
+ if (!oldset)
+ oldset = ¤t->blocked;
+
+ signr = get_signal_to_deliver(&info, regs, 0);
+
+ if (signr > 0) {
+ /* Whee! Actually deliver the signal. */
+ handle_signal(signr, &info, oldset, regs);
+ return 1;
+ }
+
+no_signal:
+ /* Did we come from a system call? */
+ if (regs->syscall_nr >= 0) {
+ /* Restart the system call - no handlers present */
+ if (regs->regs[REG_RET] == -ERESTARTNOHAND ||
+ regs->regs[REG_RET] == -ERESTARTSYS ||
+ regs->regs[REG_RET] == -ERESTARTNOINTR) {
+ /* Decode Syscall # */
+ regs->regs[REG_RET] = regs->syscall_nr;
+ regs->pc -= 4;
+ }
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/kernel/sys_sh64.c
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ *
+ * This file contains various random system calls that
+ * have a non-standard calling sequence on the Linux/SH5
+ * platform.
+ *
+ * Mostly taken from i386 version.
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/sem.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+#include <linux/stat.h>
+#include <linux/mman.h>
+#include <linux/file.h>
+#include <linux/utsname.h>
+#include <linux/syscalls.h>
+#include <asm/uaccess.h>
+#include <asm/ipc.h>
+#include <asm/ptrace.h>
+
+#define REG_3 3
+
+/*
+ * sys_pipe() is the normal C calling standard for creating
+ * a pipe. It's not the way Unix traditionally does this, though.
+ */
+#ifdef NEW_PIPE_IMPLEMENTATION
+asmlinkage int sys_pipe(unsigned long * fildes,
+ unsigned long dummy_r3,
+ unsigned long dummy_r4,
+ unsigned long dummy_r5,
+ unsigned long dummy_r6,
+ unsigned long dummy_r7,
+ struct pt_regs * regs) /* r8 = pt_regs forced by entry.S */
+{
+ int fd[2];
+ int ret;
+
+ ret = do_pipe(fd);
+ if (ret == 0)
+ /*
+ ***********************************************************************
+ * To avoid the copy_to_user we prefer to break the ABIs convention, *
+ * packing the valid pair of file IDs into a single register (r3); *
+ * while r2 is the return code as defined by the sh5-ABIs. *
+ * BE CAREFUL: pipe stub, into glibc, must be aware of this solution *
+ ***********************************************************************
+
+#ifdef __LITTLE_ENDIAN__
+ regs->regs[REG_3] = (((unsigned long long) fd[1]) << 32) | ((unsigned long long) fd[0]);
+#else
+ regs->regs[REG_3] = (((unsigned long long) fd[0]) << 32) | ((unsigned long long) fd[1]);
+#endif
+
+ */
+ /* although not very clever this is endianess independent */
+ regs->regs[REG_3] = (unsigned long long) *((unsigned long long *) fd);
+
+ return ret;
+}
+
+#else
+asmlinkage int sys_pipe(unsigned long * fildes)
+{
+ int fd[2];
+ int error;
+
+ error = do_pipe(fd);
+ if (!error) {
+ if (copy_to_user(fildes, fd, 2*sizeof(int)))
+ error = -EFAULT;
+ }
+ return error;
+}
+
+#endif
+
+/*
+ * To avoid cache alias, we map the shard page with same color.
+ */
+#define COLOUR_ALIGN(addr) (((addr)+SHMLBA-1)&~(SHMLBA-1))
+
+unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
+ unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+ struct vm_area_struct *vma;
+
+ if (flags & MAP_FIXED) {
+ /* We do not accept a shared mapping if it would violate
+ * cache aliasing constraints.
+ */
+ if ((flags & MAP_SHARED) && (addr & (SHMLBA - 1)))
+ return -EINVAL;
+ return addr;
+ }
+
+ if (len > TASK_SIZE)
+ return -ENOMEM;
+ if (!addr)
+ addr = TASK_UNMAPPED_BASE;
+
+ if (flags & MAP_PRIVATE)
+ addr = PAGE_ALIGN(addr);
+ else
+ addr = COLOUR_ALIGN(addr);
+
+ for (vma = find_vma(current->mm, addr); ; vma = vma->vm_next) {
+ /* At this point: (!vma || addr < vma->vm_end). */
+ if (TASK_SIZE - len < addr)
+ return -ENOMEM;
+ if (!vma || addr + len <= vma->vm_start)
+ return addr;
+ addr = vma->vm_end;
+ if (!(flags & MAP_PRIVATE))
+ addr = COLOUR_ALIGN(addr);
+ }
+}
+
+/* common code for old and new mmaps */
+static inline long do_mmap2(
+ unsigned long addr, unsigned long len,
+ unsigned long prot, unsigned long flags,
+ unsigned long fd, unsigned long pgoff)
+{
+ int error = -EBADF;
+ struct file * file = NULL;
+
+ flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+ if (!(flags & MAP_ANONYMOUS)) {
+ file = fget(fd);
+ if (!file)
+ goto out;
+ }
+
+ down_write(¤t->mm->mmap_sem);
+ error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
+ up_write(¤t->mm->mmap_sem);
+
+ if (file)
+ fput(file);
+out:
+ return error;
+}
+
+asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
+ unsigned long prot, unsigned long flags,
+ unsigned long fd, unsigned long pgoff)
+{
+ return do_mmap2(addr, len, prot, flags, fd, pgoff);
+}
+
+asmlinkage int old_mmap(unsigned long addr, unsigned long len,
+ unsigned long prot, unsigned long flags,
+ int fd, unsigned long off)
+{
+ if (off & ~PAGE_MASK)
+ return -EINVAL;
+ return do_mmap2(addr, len, prot, flags, fd, off>>PAGE_SHIFT);
+}
+
+/*
+ * sys_ipc() is the de-multiplexer for the SysV IPC calls..
+ *
+ * This is really horribly ugly.
+ */
+asmlinkage int sys_ipc(uint call, int first, int second,
+ int third, void __user *ptr, long fifth)
+{
+ int version, ret;
+
+ version = call >> 16; /* hack for backward compatibility */
+ call &= 0xffff;
+
+ if (call <= SEMCTL)
+ switch (call) {
+ case SEMOP:
+ return sys_semtimedop(first, (struct sembuf __user *)ptr,
+ second, NULL);
+ case SEMTIMEDOP:
+ return sys_semtimedop(first, (struct sembuf __user *)ptr,
+ second,
+ (const struct timespec __user *)fifth);
+ case SEMGET:
+ return sys_semget (first, second, third);
+ case SEMCTL: {
+ union semun fourth;
+ if (!ptr)
+ return -EINVAL;
+ if (get_user(fourth.__pad, (void * __user *) ptr))
+ return -EFAULT;
+ return sys_semctl (first, second, third, fourth);
+ }
+ default:
+ return -EINVAL;
+ }
+
+ if (call <= MSGCTL)
+ switch (call) {
+ case MSGSND:
+ return sys_msgsnd (first, (struct msgbuf __user *) ptr,
+ second, third);
+ case MSGRCV:
+ switch (version) {
+ case 0: {
+ struct ipc_kludge tmp;
+ if (!ptr)
+ return -EINVAL;
+
+ if (copy_from_user(&tmp,
+ (struct ipc_kludge __user *) ptr,
+ sizeof (tmp)))
+ return -EFAULT;
+ return sys_msgrcv (first, tmp.msgp, second,
+ tmp.msgtyp, third);
+ }
+ default:
+ return sys_msgrcv (first,
+ (struct msgbuf __user *) ptr,
+ second, fifth, third);
+ }
+ case MSGGET:
+ return sys_msgget ((key_t) first, second);
+ case MSGCTL:
+ return sys_msgctl (first, second,
+ (struct msqid_ds __user *) ptr);
+ default:
+ return -EINVAL;
+ }
+ if (call <= SHMCTL)
+ switch (call) {
+ case SHMAT:
+ switch (version) {
+ default: {
+ ulong raddr;
+ ret = do_shmat (first, (char __user *) ptr,
+ second, &raddr);
+ if (ret)
+ return ret;
+ return put_user (raddr, (ulong __user *) third);
+ }
+ case 1: /* iBCS2 emulator entry point */
+ if (!segment_eq(get_fs(), get_ds()))
+ return -EINVAL;
+ return do_shmat (first, (char __user *) ptr,
+ second, (ulong *) third);
+ }
+ case SHMDT:
+ return sys_shmdt ((char __user *)ptr);
+ case SHMGET:
+ return sys_shmget (first, second, third);
+ case SHMCTL:
+ return sys_shmctl (first, second,
+ (struct shmid_ds __user *) ptr);
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+asmlinkage int sys_uname(struct old_utsname * name)
+{
+ int err;
+ if (!name)
+ return -EFAULT;
+ down_read(&uts_sem);
+ err=copy_to_user(name, &system_utsname, sizeof (*name));
+ up_read(&uts_sem);
+ return err?-EFAULT:0;
+}
+
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/kernel/time.c
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003, 2004 Paul Mundt
+ * Copyright (C) 2003 Richard Curnow
+ *
+ * Original TMU/RTC code taken from sh version.
+ * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
+ * Some code taken from i386 version.
+ * Copyright (C) 1991, 1992, 1995 Linus Torvalds
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/profile.h>
+#include <linux/smp.h>
+
+#include <asm/registers.h> /* required by inline __asm__ stmt. */
+
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/delay.h>
+
+#include <linux/timex.h>
+#include <linux/irq.h>
+#include <asm/hardware.h>
+
+#define TMU_TOCR_INIT 0x00
+#define TMU0_TCR_INIT 0x0020
+#define TMU_TSTR_INIT 1
+
+/* RCR1 Bits */
+#define RCR1_CF 0x80 /* Carry Flag */
+#define RCR1_CIE 0x10 /* Carry Interrupt Enable */
+#define RCR1_AIE 0x08 /* Alarm Interrupt Enable */
+#define RCR1_AF 0x01 /* Alarm Flag */
+
+/* RCR2 Bits */
+#define RCR2_PEF 0x80 /* PEriodic interrupt Flag */
+#define RCR2_PESMASK 0x70 /* Periodic interrupt Set */
+#define RCR2_RTCEN 0x08 /* ENable RTC */
+#define RCR2_ADJ 0x04 /* ADJustment (30-second) */
+#define RCR2_RESET 0x02 /* Reset bit */
+#define RCR2_START 0x01 /* Start bit */
+
+/* Clock, Power and Reset Controller */
+#define CPRC_BLOCK_OFF 0x01010000
+#define CPRC_BASE PHYS_PERIPHERAL_BLOCK + CPRC_BLOCK_OFF
+
+#define FRQCR (cprc_base+0x0)
+#define WTCSR (cprc_base+0x0018)
+#define STBCR (cprc_base+0x0030)
+
+/* Time Management Unit */
+#define TMU_BLOCK_OFF 0x01020000
+#define TMU_BASE PHYS_PERIPHERAL_BLOCK + TMU_BLOCK_OFF
+#define TMU0_BASE tmu_base + 0x8 + (0xc * 0x0)
+#define TMU1_BASE tmu_base + 0x8 + (0xc * 0x1)
+#define TMU2_BASE tmu_base + 0x8 + (0xc * 0x2)
+
+#define TMU_TOCR tmu_base+0x0 /* Byte access */
+#define TMU_TSTR tmu_base+0x4 /* Byte access */
+
+#define TMU0_TCOR TMU0_BASE+0x0 /* Long access */
+#define TMU0_TCNT TMU0_BASE+0x4 /* Long access */
+#define TMU0_TCR TMU0_BASE+0x8 /* Word access */
+
+/* Real Time Clock */
+#define RTC_BLOCK_OFF 0x01040000
+#define RTC_BASE PHYS_PERIPHERAL_BLOCK + RTC_BLOCK_OFF
+
+#define R64CNT rtc_base+0x00
+#define RSECCNT rtc_base+0x04
+#define RMINCNT rtc_base+0x08
+#define RHRCNT rtc_base+0x0c
+#define RWKCNT rtc_base+0x10
+#define RDAYCNT rtc_base+0x14
+#define RMONCNT rtc_base+0x18
+#define RYRCNT rtc_base+0x1c /* 16bit */
+#define RSECAR rtc_base+0x20
+#define RMINAR rtc_base+0x24
+#define RHRAR rtc_base+0x28
+#define RWKAR rtc_base+0x2c
+#define RDAYAR rtc_base+0x30
+#define RMONAR rtc_base+0x34
+#define RCR1 rtc_base+0x38
+#define RCR2 rtc_base+0x3c
+
+#ifndef BCD_TO_BIN
+#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)
+#endif
+
+#ifndef BIN_TO_BCD
+#define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10)
+#endif
+
+#define TICK_SIZE (tick_nsec / 1000)
+
+extern unsigned long wall_jiffies;
+
+u64 jiffies_64 = INITIAL_JIFFIES;
+
+static unsigned long tmu_base, rtc_base;
+unsigned long cprc_base;
+
+/* Variables to allow interpolation of time of day to resolution better than a
+ * jiffy. */
+
+/* This is effectively protected by xtime_lock */
+static unsigned long ctc_last_interrupt;
+static unsigned long long usecs_per_jiffy = 1000000/HZ; /* Approximation */
+
+#define CTC_JIFFY_SCALE_SHIFT 40
+
+/* 2**CTC_JIFFY_SCALE_SHIFT / ctc_ticks_per_jiffy */
+static unsigned long long scaled_recip_ctc_ticks_per_jiffy;
+
+/* Estimate number of microseconds that have elapsed since the last timer tick,
+ by scaling the delta that has occured in the CTC register.
+
+ WARNING WARNING WARNING : This algorithm relies on the CTC decrementing at
+ the CPU clock rate. If the CPU sleeps, the CTC stops counting. Bear this
+ in mind if enabling SLEEP_WORKS in process.c. In that case, this algorithm
+ probably needs to use TMU.TCNT0 instead. This will work even if the CPU is
+ sleeping, though will be coarser.
+
+ FIXME : What if usecs_per_tick is moving around too much, e.g. if an adjtime
+ is running or if the freq or tick arguments of adjtimex are modified after
+ we have calibrated the scaling factor? This will result in either a jump at
+ the end of a tick period, or a wrap backwards at the start of the next one,
+ if the application is reading the time of day often enough. I think we
+ ought to do better than this. For this reason, usecs_per_jiffy is left
+ separated out in the calculation below. This allows some future hook into
+ the adjtime-related stuff in kernel/timer.c to remove this hazard.
+
+*/
+
+static unsigned long usecs_since_tick(void)
+{
+ unsigned long long current_ctc;
+ long ctc_ticks_since_interrupt;
+ unsigned long long ull_ctc_ticks_since_interrupt;
+ unsigned long result;
+
+ unsigned long long mul1_out;
+ unsigned long long mul1_out_high;
+ unsigned long long mul2_out_low, mul2_out_high;
+
+ /* Read CTC register */
+ asm ("getcon cr62, %0" : "=r" (current_ctc));
+ /* Note, the CTC counts down on each CPU clock, not up.
+ Note(2), use long type to get correct wraparound arithmetic when
+ the counter crosses zero. */
+ ctc_ticks_since_interrupt = (long) ctc_last_interrupt - (long) current_ctc;
+ ull_ctc_ticks_since_interrupt = (unsigned long long) ctc_ticks_since_interrupt;
+
+ /* Inline assembly to do 32x32x32->64 multiplier */
+ asm volatile ("mulu.l %1, %2, %0" :
+ "=r" (mul1_out) :
+ "r" (ull_ctc_ticks_since_interrupt), "r" (usecs_per_jiffy));
+
+ mul1_out_high = mul1_out >> 32;
+
+ asm volatile ("mulu.l %1, %2, %0" :
+ "=r" (mul2_out_low) :
+ "r" (mul1_out), "r" (scaled_recip_ctc_ticks_per_jiffy));
+
+#if 1
+ asm volatile ("mulu.l %1, %2, %0" :
+ "=r" (mul2_out_high) :
+ "r" (mul1_out_high), "r" (scaled_recip_ctc_ticks_per_jiffy));
+#endif
+
+ result = (unsigned long) (((mul2_out_high << 32) + mul2_out_low) >> CTC_JIFFY_SCALE_SHIFT);
+
+ return result;
+}
+
+void do_gettimeofday(struct timeval *tv)
+{
+ unsigned long flags;
+ unsigned long seq;
+ unsigned long usec, sec;
+
+ do {
+ seq = read_seqbegin_irqsave(&xtime_lock, flags);
+ usec = usecs_since_tick();
+ {
+ unsigned long lost = jiffies - wall_jiffies;
+
+ if (lost)
+ usec += lost * (1000000 / HZ);
+ }
+
+ sec = xtime.tv_sec;
+ usec += xtime.tv_nsec / 1000;
+ } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
+
+ while (usec >= 1000000) {
+ usec -= 1000000;
+ sec++;
+ }
+
+ tv->tv_sec = sec;
+ tv->tv_usec = usec;
+}
+
+int do_settimeofday(struct timespec *tv)
+{
+ time_t wtm_sec, sec = tv->tv_sec;
+ long wtm_nsec, nsec = tv->tv_nsec;
+
+ if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
+ return -EINVAL;
+
+ write_seqlock_irq(&xtime_lock);
+ /*
+ * This is revolting. We need to set "xtime" correctly. However, the
+ * value in this location is the value at the most recent update of
+ * wall time. Discover what correction gettimeofday() would have
+ * made, and then undo it!
+ */
+ nsec -= 1000 * (usecs_since_tick() +
+ (jiffies - wall_jiffies) * (1000000 / HZ));
+
+ wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
+ wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
+
+ set_normalized_timespec(&xtime, sec, nsec);
+ set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
+
+ time_adjust = 0; /* stop active adjtime() */
+ time_status |= STA_UNSYNC;
+ time_maxerror = NTP_PHASE_LIMIT;
+ time_esterror = NTP_PHASE_LIMIT;
+ write_sequnlock_irq(&xtime_lock);
+ clock_was_set();
+
+ return 0;
+}
+
+static int set_rtc_time(unsigned long nowtime)
+{
+ int retval = 0;
+ int real_seconds, real_minutes, cmos_minutes;
+
+ ctrl_outb(RCR2_RESET, RCR2); /* Reset pre-scaler & stop RTC */
+
+ cmos_minutes = ctrl_inb(RMINCNT);
+ BCD_TO_BIN(cmos_minutes);
+
+ /*
+ * since we're only adjusting minutes and seconds,
+ * don't interfere with hour overflow. This avoids
+ * messing with unknown time zones but requires your
+ * RTC not to be off by more than 15 minutes
+ */
+ real_seconds = nowtime % 60;
+ real_minutes = nowtime / 60;
+ if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
+ real_minutes += 30; /* correct for half hour time zone */
+ real_minutes %= 60;
+
+ if (abs(real_minutes - cmos_minutes) < 30) {
+ BIN_TO_BCD(real_seconds);
+ BIN_TO_BCD(real_minutes);
+ ctrl_outb(real_seconds, RSECCNT);
+ ctrl_outb(real_minutes, RMINCNT);
+ } else {
+ printk(KERN_WARNING
+ "set_rtc_time: can't update from %d to %d\n",
+ cmos_minutes, real_minutes);
+ retval = -1;
+ }
+
+ ctrl_outb(RCR2_RTCEN|RCR2_START, RCR2); /* Start RTC */
+
+ return retval;
+}
+
+/* last time the RTC clock got updated */
+static long last_rtc_update = 0;
+
+static inline void sh64_do_profile(struct pt_regs *regs)
+{
+ extern int _stext;
+ unsigned long pc;
+
+ profile_hook(regs);
+
+ if (user_mode(regs))
+ return;
+
+ /* Don't profile cpu_idle.. */
+ if (!prof_buffer || !current->pid)
+ return;
+
+ pc = instruction_pointer(regs);
+ pc -= (unsigned long) &_stext;
+ pc >>= prof_shift;
+
+ /*
+ * Don't ignore out-of-bounds PC values silently, put them into the
+ * last histogram slot, so if present, they will show up as a sharp
+ * peak.
+ */
+ if (pc > prof_len - 1)
+ pc = prof_len - 1;
+
+ /* We could just be sloppy and not lock against a re-entry on this
+ increment, but the profiling code won't always be linked in anyway. */
+ atomic_inc((atomic_t *)&prof_buffer[pc]);
+}
+
+/*
+ * timer_interrupt() needs to keep up the real-time clock,
+ * as well as call the "do_timer()" routine every clocktick
+ */
+static inline void do_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long long current_ctc;
+ asm ("getcon cr62, %0" : "=r" (current_ctc));
+ ctc_last_interrupt = (unsigned long) current_ctc;
+
+ do_timer(regs);
+
+ sh64_do_profile(regs);
+
+#ifdef CONFIG_HEARTBEAT
+ {
+ extern void heartbeat(void);
+
+ heartbeat();
+ }
+#endif
+
+ /*
+ * If we have an externally synchronized Linux clock, then update
+ * RTC clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
+ * called as close as possible to 500 ms before the new second starts.
+ */
+ if ((time_status & STA_UNSYNC) == 0 &&
+ xtime.tv_sec > last_rtc_update + 660 &&
+ (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 &&
+ (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) {
+ if (set_rtc_time(xtime.tv_sec) == 0)
+ last_rtc_update = xtime.tv_sec;
+ else
+ last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+ }
+}
+
+/*
+ * This is the same as the above, except we _also_ save the current
+ * Time Stamp Counter value at the time of the timer interrupt, so that
+ * we later on can estimate the time of day more exactly.
+ */
+static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long timer_status;
+
+ /* Clear UNF bit */
+ timer_status = ctrl_inw(TMU0_TCR);
+ timer_status &= ~0x100;
+ ctrl_outw(timer_status, TMU0_TCR);
+
+ /*
+ * Here we are in the timer irq handler. We just have irqs locally
+ * disabled but we don't know if the timer_bh is running on the other
+ * CPU. We need to avoid to SMP race with it. NOTE: we don' t need
+ * the irq version of write_lock because as just said we have irq
+ * locally disabled. -arca
+ */
+ write_lock(&xtime_lock);
+ do_timer_interrupt(irq, NULL, regs);
+ write_unlock(&xtime_lock);
+
+ return IRQ_HANDLED;
+}
+
+static unsigned long get_rtc_time(void)
+{
+ unsigned int sec, min, hr, wk, day, mon, yr, yr100;
+
+ again:
+ do {
+ ctrl_outb(0, RCR1); /* Clear CF-bit */
+ sec = ctrl_inb(RSECCNT);
+ min = ctrl_inb(RMINCNT);
+ hr = ctrl_inb(RHRCNT);
+ wk = ctrl_inb(RWKCNT);
+ day = ctrl_inb(RDAYCNT);
+ mon = ctrl_inb(RMONCNT);
+ yr = ctrl_inw(RYRCNT);
+ yr100 = (yr >> 8);
+ yr &= 0xff;
+ } while ((ctrl_inb(RCR1) & RCR1_CF) != 0);
+
+ BCD_TO_BIN(yr100);
+ BCD_TO_BIN(yr);
+ BCD_TO_BIN(mon);
+ BCD_TO_BIN(day);
+ BCD_TO_BIN(hr);
+ BCD_TO_BIN(min);
+ BCD_TO_BIN(sec);
+
+ if (yr > 99 || mon < 1 || mon > 12 || day > 31 || day < 1 ||
+ hr > 23 || min > 59 || sec > 59) {
+ printk(KERN_ERR
+ "SH RTC: invalid value, resetting to 1 Jan 2000\n");
+ ctrl_outb(RCR2_RESET, RCR2); /* Reset & Stop */
+ ctrl_outb(0, RSECCNT);
+ ctrl_outb(0, RMINCNT);
+ ctrl_outb(0, RHRCNT);
+ ctrl_outb(6, RWKCNT);
+ ctrl_outb(1, RDAYCNT);
+ ctrl_outb(1, RMONCNT);
+ ctrl_outw(0x2000, RYRCNT);
+ ctrl_outb(RCR2_RTCEN|RCR2_START, RCR2); /* Start */
+ goto again;
+ }
+
+ return mktime(yr100 * 100 + yr, mon, day, hr, min, sec);
+}
+
+static __init unsigned int get_cpu_hz(void)
+{
+ unsigned int count;
+ unsigned long __dummy;
+ unsigned long ctc_val_init, ctc_val;
+
+ /*
+ ** Regardless the toolchain, force the compiler to use the
+ ** arbitrary register r3 as a clock tick counter.
+ ** NOTE: r3 must be in accordance with rtc_interrupt()
+ */
+ register unsigned long long __rtc_irq_flag __asm__ ("r3");
+
+ sti();
+ do {} while (ctrl_inb(R64CNT) != 0);
+ ctrl_outb(RCR1_CIE, RCR1); /* Enable carry interrupt */
+
+ /*
+ * r3 is arbitrary. CDC does not support "=z".
+ */
+ ctc_val_init = 0xffffffff;
+ ctc_val = ctc_val_init;
+
+ asm volatile("gettr tr0, %1\n\t"
+ "putcon %0, " __CTC "\n\t"
+ "and %2, r63, %2\n\t"
+ "pta $+4, tr0\n\t"
+ "beq/l %2, r63, tr0\n\t"
+ "ptabs %1, tr0\n\t"
+ "getcon " __CTC ", %0\n\t"
+ : "=r"(ctc_val), "=r" (__dummy), "=r" (__rtc_irq_flag)
+ : "0" (0));
+ cli();
+ /*
+ * SH-3:
+ * CPU clock = 4 stages * loop
+ * tst rm,rm if id ex
+ * bt/s 1b if id ex
+ * add #1,rd if id ex
+ * (if) pipe line stole
+ * tst rm,rm if id ex
+ * ....
+ *
+ *
+ * SH-4:
+ * CPU clock = 6 stages * loop
+ * I don't know why.
+ * ....
+ *
+ * SH-5:
+ * Use CTC register to count. This approach returns the right value
+ * even if the I-cache is disabled (e.g. whilst debugging.)
+ *
+ */
+
+ count = ctc_val_init - ctc_val; /* CTC counts down */
+
+#if defined (CONFIG_SH_SIMULATOR)
+ /*
+ * Let's pretend we are a 5MHz SH-5 to avoid a too
+ * little timer interval. Also to keep delay
+ * calibration within a reasonable time.
+ */
+ return 5000000;
+#else
+ /*
+ * This really is count by the number of clock cycles
+ * by the ratio between a complete R64CNT
+ * wrap-around (128) and CUI interrupt being raised (64).
+ */
+ return count*2;
+#endif
+}
+
+static irqreturn_t rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ ctrl_outb(0, RCR1); /* Disable Carry Interrupts */
+ regs->regs[3] = 1; /* Using r3 */
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, CPU_MASK_NONE, "timer", NULL, NULL};
+static struct irqaction irq1 = { rtc_interrupt, SA_INTERRUPT, CPU_MASK_NONE, "rtc", NULL, NULL};
+
+void __init time_init(void)
+{
+ unsigned int cpu_clock, master_clock, bus_clock, module_clock;
+ unsigned long interval;
+ unsigned long frqcr, ifc, pfc;
+ static int ifc_table[] = { 2, 4, 6, 8, 10, 12, 16, 24 };
+#define bfc_table ifc_table /* Same */
+#define pfc_table ifc_table /* Same */
+
+ tmu_base = onchip_remap(TMU_BASE, 1024, "TMU");
+ if (!tmu_base) {
+ panic("Unable to remap TMU\n");
+ }
+
+ rtc_base = onchip_remap(RTC_BASE, 1024, "RTC");
+ if (!rtc_base) {
+ panic("Unable to remap RTC\n");
+ }
+
+ cprc_base = onchip_remap(CPRC_BASE, 1024, "CPRC");
+ if (!cprc_base) {
+ panic("Unable to remap CPRC\n");
+ }
+
+ xtime.tv_sec = get_rtc_time();
+ xtime.tv_nsec = 0;
+
+ setup_irq(TIMER_IRQ, &irq0);
+ setup_irq(RTC_IRQ, &irq1);
+
+ /* Check how fast it is.. */
+ cpu_clock = get_cpu_hz();
+
+ /* Note careful order of operations to maintain reasonable precision and avoid overflow. */
+ scaled_recip_ctc_ticks_per_jiffy = ((1ULL << CTC_JIFFY_SCALE_SHIFT) / (unsigned long long)(cpu_clock / HZ));
+
+ disable_irq(RTC_IRQ);
+
+ printk("CPU clock: %d.%02dMHz\n",
+ (cpu_clock / 1000000), (cpu_clock % 1000000)/10000);
+ {
+ unsigned short bfc;
+ frqcr = ctrl_inl(FRQCR);
+ ifc = ifc_table[(frqcr>> 6) & 0x0007];
+ bfc = bfc_table[(frqcr>> 3) & 0x0007];
+ pfc = pfc_table[(frqcr>> 12) & 0x0007];
+ master_clock = cpu_clock * ifc;
+ bus_clock = master_clock/bfc;
+ }
+
+ printk("Bus clock: %d.%02dMHz\n",
+ (bus_clock/1000000), (bus_clock % 1000000)/10000);
+ module_clock = master_clock/pfc;
+ printk("Module clock: %d.%02dMHz\n",
+ (module_clock/1000000), (module_clock % 1000000)/10000);
+ interval = (module_clock/(HZ*4));
+
+ printk("Interval = %ld\n", interval);
+
+ current_cpu_data.cpu_clock = cpu_clock;
+ current_cpu_data.master_clock = master_clock;
+ current_cpu_data.bus_clock = bus_clock;
+ current_cpu_data.module_clock = module_clock;
+
+ /* Start TMU0 */
+ ctrl_outb(TMU_TOCR_INIT, TMU_TOCR);
+ ctrl_outw(TMU0_TCR_INIT, TMU0_TCR);
+ ctrl_outl(interval, TMU0_TCOR);
+ ctrl_outl(interval, TMU0_TCNT);
+ ctrl_outb(TMU_TSTR_INIT, TMU_TSTR);
+}
+
+void enter_deep_standby(void)
+{
+ /* Disable watchdog timer */
+ ctrl_outl(0xa5000000, WTCSR);
+ /* Configure deep standby on sleep */
+ ctrl_outl(0x03, STBCR);
+
+#ifdef CONFIG_SH_ALPHANUMERIC
+ {
+ extern void mach_alphanum(int position, unsigned char value);
+ extern void mach_alphanum_brightness(int setting);
+ char halted[] = "Halted. ";
+ int i;
+ mach_alphanum_brightness(6); /* dimmest setting above off */
+ for (i=0; i<8; i++) {
+ mach_alphanum(i, halted[i]);
+ }
+ asm __volatile__ ("synco");
+ }
+#endif
+
+ asm __volatile__ ("sleep");
+ asm __volatile__ ("synci");
+ asm __volatile__ ("nop");
+ asm __volatile__ ("nop");
+ asm __volatile__ ("nop");
+ asm __volatile__ ("nop");
+ panic("Unexpected wakeup!\n");
+}
+
+/*
+ * Scheduler clock - returns current time in nanosec units.
+ */
+unsigned long long sched_clock(void)
+{
+ return (unsigned long long)jiffies * (1000000000 / HZ);
+}
+
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh5/vmlinux.lds.S
+ *
+ * ld script to make ST50 Linux kernel
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ *
+ * benedict.gaster@superh.com: 2nd May 2002
+ * Add definition of empty_zero_page to be the first page of kernel image.
+ *
+ * benedict.gaster@superh.com: 3rd May 2002
+ * Added support for ramdisk, removing statically linked romfs at the same time.
+ *
+ * lethal@linux-sh.org: 9th May 2003
+ * Kill off GLOBAL_NAME() usage and other CDC-isms.
+ *
+ * lethal@linux-sh.org: 19th May 2003
+ * Remove support for ancient toolchains.
+ */
+
+#include <linux/config.h>
+#include <asm/page.h>
+#include <asm/cache.h>
+#include <asm/processor.h>
+#include <asm/thread_info.h>
+
+#define LOAD_OFFSET CONFIG_CACHED_MEMORY_OFFSET
+#include <asm-generic/vmlinux.lds.h>
+
+#ifdef NOTDEF
+#ifdef CONFIG_LITTLE_ENDIAN
+OUTPUT_FORMAT("elf32-sh64l-linux", "elf32-sh64l-linux", "elf32-sh64l-linux")
+#else
+OUTPUT_FORMAT("elf32-sh64", "elf32-sh64", "elf32-sh64")
+#endif
+#endif
+
+OUTPUT_ARCH(sh:sh5)
+
+#define C_PHYS(x) AT (ADDR(x) - LOAD_OFFSET)
+
+ENTRY(__start)
+SECTIONS
+{
+ . = CONFIG_CACHED_MEMORY_OFFSET + CONFIG_MEMORY_START + PAGE_SIZE;
+ _text = .; /* Text and read-only data */
+ text = .; /* Text and read-only data */
+
+ .empty_zero_page : C_PHYS(.empty_zero_page) {
+ *(.empty_zero_page)
+ } = 0
+
+ .text : C_PHYS(.text) {
+ *(.text)
+ *(.text64)
+ *(.text..SHmedia32)
+ SCHED_TEXT
+ *(.fixup)
+ *(.gnu.warning)
+#ifdef CONFIG_LITTLE_ENDIAN
+ } = 0x6ff0fff0
+#else
+ } = 0xf0fff06f
+#endif
+
+ /* We likely want __ex_table to be Cache Line aligned */
+ . = ALIGN(L1_CACHE_BYTES); /* Exception table */
+ __start___ex_table = .;
+ __ex_table : C_PHYS(__ex_table) { *(__ex_table) }
+ __stop___ex_table = .;
+
+ RODATA
+
+ _etext = .; /* End of text section */
+
+ .data : C_PHYS(.data) { /* Data */
+ *(.data)
+ CONSTRUCTORS
+ }
+
+ . = ALIGN(PAGE_SIZE);
+ .data.page_aligned : C_PHYS(.data.page_aligned) { *(.data.page_aligned) }
+
+ . = ALIGN(L1_CACHE_BYTES);
+ __per_cpu_start = .;
+ .data.percpu : C_PHYS(.data.percpu) { *(.data.percpu) }
+ __per_cpu_end = . ;
+ .data.cacheline_aligned : C_PHYS(.data.cacheline_aligned) { *(.data.cacheline_aligned) }
+
+ _edata = .; /* End of data section */
+
+ . = ALIGN(THREAD_SIZE); /* init_task: structure size aligned */
+ .data.init_task : C_PHYS(.data.init_task) { *(.data.init_task) }
+
+ . = ALIGN(PAGE_SIZE); /* Init code and data */
+ __init_begin = .;
+ _sinittext = .;
+ .init.text : C_PHYS(.init.text) { *(.init.text) }
+ _einittext = .;
+ .init.data : C_PHYS(.init.data) { *(.init.data) }
+ . = ALIGN(L1_CACHE_BYTES); /* Better if Cache Line aligned */
+ __setup_start = .;
+ .init.setup : C_PHYS(.init.setup) { *(.init.setup) }
+ __setup_end = .;
+ __start___param = .;
+ __param : C_PHYS(__param) { *(__param) }
+ __stop___param = .;
+ __initcall_start = .;
+ .initcall.init : C_PHYS(.initcall.init) {
+ *(.initcall1.init)
+ *(.initcall2.init)
+ *(.initcall3.init)
+ *(.initcall4.init)
+ *(.initcall5.init)
+ *(.initcall6.init)
+ *(.initcall7.init)
+ }
+ __initcall_end = .;
+ __con_initcall_start = .;
+ .con_initcall.init : C_PHYS(.con_initcall.init) { *(.con_initcall.init) }
+ __con_initcall_end = .;
+ SECURITY_INIT
+ __initramfs_start = .;
+ .init.ramfs : C_PHYS(.init.ramfs) { *(.init.ramfs) }
+ __initramfs_end = .;
+ . = ALIGN(PAGE_SIZE);
+ __init_end = .;
+
+ /* Align to the biggest single data representation, head and tail */
+ . = ALIGN(8);
+ __bss_start = .; /* BSS */
+ .bss : C_PHYS(.bss) {
+ *(.bss)
+ }
+ . = ALIGN(8);
+ _end = . ;
+
+ /* Sections to be discarded */
+ /DISCARD/ : {
+ *(.exit.text)
+ *(.exit.data)
+ *(.exitcall.exit)
+ }
+
+ /* Stabs debugging sections. */
+ .stab 0 : C_PHYS(.stab) { *(.stab) }
+ .stabstr 0 : C_PHYS(.stabstr) { *(.stabstr) }
+ .stab.excl 0 : C_PHYS(.stab.excl) { *(.stab.excl) }
+ .stab.exclstr 0 : C_PHYS(.stab.exclstr) { *(.stab.exclstr) }
+ .stab.index 0 : C_PHYS(.stab.index) { *(.stab.index) }
+ .stab.indexstr 0 : C_PHYS(.stab.indexstr) { *(.stab.indexstr) }
+ .comment 0 : C_PHYS(.comment) { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging section are relative to the beginning
+ of the section so we begin .debug at 0. */
+ /* DWARF 1 */
+ .debug 0 : C_PHYS(.debug) { *(.debug) }
+ .line 0 : C_PHYS(.line) { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : C_PHYS(.debug_srcinfo) { *(.debug_srcinfo) }
+ .debug_sfnames 0 : C_PHYS(.debug_sfnames) { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : C_PHYS(.debug_aranges) { *(.debug_aranges) }
+ .debug_pubnames 0 : C_PHYS(.debug_pubnames) { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : C_PHYS(.debug_info) { *(.debug_info) }
+ .debug_abbrev 0 : C_PHYS(.debug_abbrev) { *(.debug_abbrev) }
+ .debug_line 0 : C_PHYS(.debug_line) { *(.debug_line) }
+ .debug_frame 0 : C_PHYS(.debug_frame) { *(.debug_frame) }
+ .debug_str 0 : C_PHYS(.debug_str) { *(.debug_str) }
+ .debug_loc 0 : C_PHYS(.debug_loc) { *(.debug_loc) }
+ .debug_macinfo 0 : C_PHYS(.debug_macinfo) { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : C_PHYS(.debug_weaknames) { *(.debug_weaknames) }
+ .debug_funcnames 0 : C_PHYS(.debug_funcnames) { *(.debug_funcnames) }
+ .debug_typenames 0 : C_PHYS(.debug_typenames) { *(.debug_typenames) }
+ .debug_varnames 0 : C_PHYS(.debug_varnames) { *(.debug_varnames) }
+ /* These must appear regardless of . */
+}
--- /dev/null
+/*
+ * arch/sh/lib/csum_parial.c
+ *
+ * This file contains network checksum routines that are better done
+ * in an architecture-specific manner due to speed..
+ */
+
+#undef DEBUG
+
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+static inline unsigned short from64to16(unsigned long long x)
+{
+ /* add up 32-bit words for 33 bits */
+ x = (x & 0xffffffff) + (x >> 32);
+ /* add up 16-bit and 17-bit words for 17+c bits */
+ x = (x & 0xffff) + (x >> 16);
+ /* add up 16-bit and 2-bit for 16+c bit */
+ x = (x & 0xffff) + (x >> 16);
+ /* add up carry.. */
+ x = (x & 0xffff) + (x >> 16);
+ return x;
+}
+
+static inline unsigned short foldto16(unsigned long x)
+{
+ /* add up 16-bit for 17 bits */
+ x = (x & 0xffff) + (x >> 16);
+ /* add up carry.. */
+ x = (x & 0xffff) + (x >> 16);
+ return x;
+}
+
+static inline unsigned short myfoldto16(unsigned long long x)
+{
+ /* Fold down to 32-bits so we don't loose in the typedef-less
+ network stack. */
+ /* 64 to 33 */
+ x = (x & 0xffffffff) + (x >> 32);
+ /* 33 to 32 */
+ x = (x & 0xffffffff) + (x >> 32);
+
+ /* add up 16-bit for 17 bits */
+ x = (x & 0xffff) + (x >> 16);
+ /* add up carry.. */
+ x = (x & 0xffff) + (x >> 16);
+ return x;
+}
+
+#define odd(x) ((x)&1)
+#define U16(x) ntohs(x)
+
+static unsigned long do_csum(const unsigned char *buff, int len)
+{
+ int odd, count;
+ unsigned long result = 0;
+
+ pr_debug("do_csum buff %p, len %d (0x%x)\n", buff, len, len);
+#ifdef DEBUG
+ for (i = 0; i < len; i++) {
+ if ((i % 26) == 0)
+ printk("\n");
+ printk("%02X ", buff[i]);
+ }
+#endif
+
+ if (len <= 0)
+ goto out;
+
+ odd = 1 & (unsigned long) buff;
+ if (odd) {
+ result = *buff << 8;
+ len--;
+ buff++;
+ }
+ count = len >> 1; /* nr of 16-bit words.. */
+ if (count) {
+ if (2 & (unsigned long) buff) {
+ result += *(unsigned short *) buff;
+ count--;
+ len -= 2;
+ buff += 2;
+ }
+ count >>= 1; /* nr of 32-bit words.. */
+ if (count) {
+ unsigned long carry = 0;
+ do {
+ unsigned long w = *(unsigned long *) buff;
+ buff += 4;
+ count--;
+ result += carry;
+ result += w;
+ carry = (w > result);
+ } while (count);
+ result += carry;
+ result = (result & 0xffff) + (result >> 16);
+ }
+ if (len & 2) {
+ result += *(unsigned short *) buff;
+ buff += 2;
+ }
+ }
+ if (len & 1)
+ result += *buff;
+ result = foldto16(result);
+ if (odd)
+ result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
+
+ pr_debug("\nCHECKSUM is 0x%x\n", result);
+
+ out:
+ return result;
+}
+
+/* computes the checksum of a memory block at buff, length len,
+ and adds in "sum" (32-bit) */
+unsigned int csum_partial(const unsigned char *buff, int len, unsigned int sum)
+{
+ unsigned long long result = do_csum(buff, len);
+
+ /* add in old sum, and carry.. */
+ result += sum;
+ /* 32+c bits -> 32 bits */
+ result = (result & 0xffffffff) + (result >> 32);
+
+ pr_debug("csum_partial, buff %p len %d sum 0x%x result=0x%016Lx\n",
+ buff, len, sum, result);
+
+ return result;
+}
+
+/* Copy while checksumming, otherwise like csum_partial. */
+unsigned int
+csum_partial_copy(const char *src, char *dst, int len, unsigned int sum)
+{
+ sum = csum_partial(src, len, sum);
+ memcpy(dst, src, len);
+
+ return sum;
+}
+
+/* Copy from userspace and compute checksum. If we catch an exception
+ then zero the rest of the buffer. */
+unsigned int
+csum_partial_copy_from_user(const char *src, char *dst, int len,
+ unsigned int sum, int *err_ptr)
+{
+ int missing;
+
+ pr_debug
+ ("csum_partial_copy_from_user src %p, dest %p, len %d, sum %08x, err_ptr %p\n",
+ src, dst, len, sum, err_ptr);
+ missing = copy_from_user(dst, src, len);
+ pr_debug(" access_ok %d\n", __access_ok((unsigned long) src, len));
+ pr_debug(" missing %d\n", missing);
+ if (missing) {
+ memset(dst + len - missing, 0, missing);
+ *err_ptr = -EFAULT;
+ }
+
+ return csum_partial(dst, len, sum);
+}
+
+/* Copy to userspace and compute checksum. */
+unsigned int
+csum_partial_copy_to_user(const char *src, char *dst, int len,
+ unsigned int sum, int *err_ptr)
+{
+ sum = csum_partial(src, len, sum);
+
+ if (copy_to_user(dst, src, len))
+ *err_ptr = -EFAULT;
+
+ return sum;
+}
+
+/*
+ * This is a version of ip_compute_csum() optimized for IP headers,
+ * which always checksum on 4 octet boundaries.
+ */
+unsigned short ip_fast_csum(unsigned char *iph, unsigned int ihl)
+{
+ pr_debug("ip_fast_csum %p,%d\n", iph, ihl);
+
+ return ~do_csum(iph, ihl * 4);
+}
+
+unsigned int csum_tcpudp_nofold(unsigned long saddr,
+ unsigned long daddr,
+ unsigned short len,
+ unsigned short proto, unsigned int sum)
+{
+ unsigned long long result;
+
+ pr_debug("ntohs(0x%x)=0x%x\n", 0xdead, ntohs(0xdead));
+ pr_debug("htons(0x%x)=0x%x\n", 0xdead, htons(0xdead));
+
+ result = ((unsigned long long) saddr +
+ (unsigned long long) daddr +
+ (unsigned long long) sum +
+ ((unsigned long long) ntohs(len) << 16) +
+ ((unsigned long long) proto << 8));
+
+ /* Fold down to 32-bits so we don't loose in the typedef-less
+ network stack. */
+ /* 64 to 33 */
+ result = (result & 0xffffffff) + (result >> 32);
+ /* 33 to 32 */
+ result = (result & 0xffffffff) + (result >> 32);
+
+ pr_debug("%s saddr %x daddr %x len %x proto %x sum %x result %08Lx\n",
+ __FUNCTION__, saddr, daddr, len, proto, sum, result);
+
+ return result;
+}
+
+// Post SIM:
+unsigned int
+csum_partial_copy_nocheck(const char *src, char *dst, int len, unsigned int sum)
+{
+ // unsigned dummy;
+ pr_debug("csum_partial_copy_nocheck src %p dst %p len %d\n", src, dst,
+ len);
+
+ return csum_partial_copy(src, dst, len, sum);
+}
--- /dev/null
+/*--------------------------------------------------------------------------
+--
+-- Identity : Linux50 Debug Funcions
+--
+-- File : arch/sh64/lib/dbg.C
+--
+-- Copyright 2000, 2001 STMicroelectronics Limited.
+-- Copyright 2004 Richard Curnow (evt_debug etc)
+--
+--------------------------------------------------------------------------*/
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <asm/mmu_context.h>
+
+typedef u64 regType_t;
+
+static regType_t getConfigReg(u64 id)
+{
+ register u64 reg __asm__("r2");
+ asm volatile ("getcfg %1, 0, %0":"=r" (reg):"r"(id));
+ return (reg);
+}
+
+/* ======================================================================= */
+
+static char *szTab[] = { "4k", "64k", "1M", "512M" };
+static char *protTab[] = { "----",
+ "---R",
+ "--X-",
+ "--XR",
+ "-W--",
+ "-W-R",
+ "-WX-",
+ "-WXR",
+ "U---",
+ "U--R",
+ "U-X-",
+ "U-XR",
+ "UW--",
+ "UW-R",
+ "UWX-",
+ "UWXR"
+};
+#define ITLB_BASE 0x00000000
+#define DTLB_BASE 0x00800000
+#define MAX_TLBs 64
+/* PTE High */
+#define GET_VALID(pte) ((pte) & 0x1)
+#define GET_SHARED(pte) ((pte) & 0x2)
+#define GET_ASID(pte) ((pte >> 2) & 0x0ff)
+#define GET_EPN(pte) ((pte) & 0xfffff000)
+
+/* PTE Low */
+#define GET_CBEHAVIOR(pte) ((pte) & 0x3)
+#define GET_PAGE_SIZE(pte) szTab[((pte >> 3) & 0x3)]
+#define GET_PROTECTION(pte) protTab[((pte >> 6) & 0xf)]
+#define GET_PPN(pte) ((pte) & 0xfffff000)
+
+#define PAGE_1K_MASK 0x00000000
+#define PAGE_4K_MASK 0x00000010
+#define PAGE_64K_MASK 0x00000080
+#define MMU_PAGESIZE_MASK (PAGE_64K_MASK | PAGE_4K_MASK)
+#define PAGE_1MB_MASK MMU_PAGESIZE_MASK
+#define PAGE_1K (1024)
+#define PAGE_4K (1024 * 4)
+#define PAGE_64K (1024 * 64)
+#define PAGE_1MB (1024 * 1024)
+
+#define HOW_TO_READ_TLB_CONTENT \
+ "[ ID] PPN EPN ASID Share CB P.Size PROT.\n"
+
+void print_single_tlb(unsigned long tlb, int single_print)
+{
+ regType_t pteH;
+ regType_t pteL;
+ unsigned int valid, shared, asid, epn, cb, ppn;
+ char *pSize;
+ char *pProt;
+
+ /*
+ ** in case of single print <single_print> is true, this implies:
+ ** 1) print the TLB in any case also if NOT VALID
+ ** 2) print out the header
+ */
+
+ pteH = getConfigReg(tlb);
+ valid = GET_VALID(pteH);
+ if (single_print)
+ printk(HOW_TO_READ_TLB_CONTENT);
+ else if (!valid)
+ return;
+
+ pteL = getConfigReg(tlb + 1);
+
+ shared = GET_SHARED(pteH);
+ asid = GET_ASID(pteH);
+ epn = GET_EPN(pteH);
+ cb = GET_CBEHAVIOR(pteL);
+ pSize = GET_PAGE_SIZE(pteL);
+ pProt = GET_PROTECTION(pteL);
+ ppn = GET_PPN(pteL);
+ printk("[%c%2ld] 0x%08x 0x%08x %03d %02x %02x %4s %s\n",
+ ((valid) ? ' ' : 'u'), ((tlb & 0x0ffff) / TLB_STEP),
+ ppn, epn, asid, shared, cb, pSize, pProt);
+}
+
+void print_dtlb(void)
+{
+ int count;
+ unsigned long tlb;
+
+ printk(" ================= SH-5 D-TLBs Status ===================\n");
+ printk(HOW_TO_READ_TLB_CONTENT);
+ tlb = DTLB_BASE;
+ for (count = 0; count < MAX_TLBs; count++, tlb += TLB_STEP)
+ print_single_tlb(tlb, 0);
+ printk
+ (" =============================================================\n");
+}
+
+void print_itlb(void)
+{
+ int count;
+ unsigned long tlb;
+
+ printk(" ================= SH-5 I-TLBs Status ===================\n");
+ printk(HOW_TO_READ_TLB_CONTENT);
+ tlb = ITLB_BASE;
+ for (count = 0; count < MAX_TLBs; count++, tlb += TLB_STEP)
+ print_single_tlb(tlb, 0);
+ printk
+ (" =============================================================\n");
+}
+
+/* ======================================================================= */
+
+#include "syscalltab.h"
+
+struct ring_node {
+ int evt;
+ int ret_addr;
+ int event;
+ int tra;
+ int pid;
+ unsigned long sp;
+ unsigned long pc;
+};
+
+static struct ring_node event_ring[16];
+static int event_ptr = 0;
+
+void evt_debug(int evt, int ret_addr, int event, int tra, struct pt_regs *regs)
+{
+ int syscallno = tra & 0xff;
+ unsigned long sp;
+ unsigned long stack_bottom;
+ int pid;
+ struct ring_node *rr;
+
+ pid = current->pid;
+ stack_bottom = (unsigned long) current->thread_info;
+ asm volatile("ori r15, 0, %0" : "=r" (sp));
+ rr = event_ring + event_ptr;
+ rr->evt = evt;
+ rr->ret_addr = ret_addr;
+ rr->event = event;
+ rr->tra = tra;
+ rr->pid = pid;
+ rr->sp = sp;
+ rr->pc = regs->pc;
+
+ if (sp < stack_bottom + 3092) {
+ printk("evt_debug : stack underflow report\n");
+ int i, j;
+ for (j=0, i = event_ptr; j<16; j++) {
+ rr = event_ring + i;
+ printk("evt=%08x event=%08x tra=%08x pid=%5d sp=%08lx pc=%08lx\n",
+ rr->evt, rr->event, rr->tra, rr->pid, rr->sp, rr->pc);
+ i--;
+ i &= 15;
+ }
+ panic("STACK UNDERFLOW\n");
+ }
+
+ event_ptr = (event_ptr + 1) & 15;
+
+ if ((event == 2) && (evt == 0x160)) {
+ if (syscallno < NUM_SYSCALL_INFO_ENTRIES)
+ printk("Task %d: %s()\n",
+ current->pid,
+ syscall_info_table[syscallno].name);
+ }
+}
+
+void evt_debug2(unsigned int ret)
+{
+ printk("Task %d: syscall returns %08x\n", current->pid, ret);
+}
+
+void evt_debug_ret_from_irq(struct pt_regs *regs)
+{
+ int pid;
+ struct ring_node *rr;
+
+ pid = current->pid;
+ rr = event_ring + event_ptr;
+ rr->evt = 0xffff;
+ rr->ret_addr = 0;
+ rr->event = 0;
+ rr->tra = 0;
+ rr->pid = pid;
+ rr->pc = regs->pc;
+ event_ptr = (event_ptr + 1) & 15;
+}
+
+void evt_debug_ret_from_exc(struct pt_regs *regs)
+{
+ int pid;
+ struct ring_node *rr;
+
+ pid = current->pid;
+ rr = event_ring + event_ptr;
+ rr->evt = 0xfffe;
+ rr->ret_addr = 0;
+ rr->event = 0;
+ rr->tra = 0;
+ rr->pid = pid;
+ rr->pc = regs->pc;
+ event_ptr = (event_ptr + 1) & 15;
+}
+
+/* ======================================================================= */
+
+void show_excp_regs(char *from, int trapnr, int signr, struct pt_regs *regs)
+{
+
+ unsigned long long ah, al, bh, bl, ch, cl;
+
+ printk("\n");
+ printk("EXCEPTION - %s: task %d; Linux trap # %d; signal = %d\n",
+ ((from) ? from : "???"), current->pid, trapnr, signr);
+
+ asm volatile ("getcon " __EXPEVT ", %0":"=r"(ah));
+ asm volatile ("getcon " __EXPEVT ", %0":"=r"(al));
+ ah = (ah) >> 32;
+ al = (al) & 0xffffffff;
+ asm volatile ("getcon " __KCR1 ", %0":"=r"(bh));
+ asm volatile ("getcon " __KCR1 ", %0":"=r"(bl));
+ bh = (bh) >> 32;
+ bl = (bl) & 0xffffffff;
+ asm volatile ("getcon " __INTEVT ", %0":"=r"(ch));
+ asm volatile ("getcon " __INTEVT ", %0":"=r"(cl));
+ ch = (ch) >> 32;
+ cl = (cl) & 0xffffffff;
+ printk("EXPE: %08Lx%08Lx KCR1: %08Lx%08Lx INTE: %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ asm volatile ("getcon " __PEXPEVT ", %0":"=r"(ah));
+ asm volatile ("getcon " __PEXPEVT ", %0":"=r"(al));
+ ah = (ah) >> 32;
+ al = (al) & 0xffffffff;
+ asm volatile ("getcon " __PSPC ", %0":"=r"(bh));
+ asm volatile ("getcon " __PSPC ", %0":"=r"(bl));
+ bh = (bh) >> 32;
+ bl = (bl) & 0xffffffff;
+ asm volatile ("getcon " __PSSR ", %0":"=r"(ch));
+ asm volatile ("getcon " __PSSR ", %0":"=r"(cl));
+ ch = (ch) >> 32;
+ cl = (cl) & 0xffffffff;
+ printk("PEXP: %08Lx%08Lx PSPC: %08Lx%08Lx PSSR: %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->pc) >> 32;
+ al = (regs->pc) & 0xffffffff;
+ bh = (regs->regs[18]) >> 32;
+ bl = (regs->regs[18]) & 0xffffffff;
+ ch = (regs->regs[15]) >> 32;
+ cl = (regs->regs[15]) & 0xffffffff;
+ printk("PC : %08Lx%08Lx LINK: %08Lx%08Lx SP : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->sr) >> 32;
+ al = (regs->sr) & 0xffffffff;
+ asm volatile ("getcon " __TEA ", %0":"=r"(bh));
+ asm volatile ("getcon " __TEA ", %0":"=r"(bl));
+ bh = (bh) >> 32;
+ bl = (bl) & 0xffffffff;
+ asm volatile ("getcon " __KCR0 ", %0":"=r"(ch));
+ asm volatile ("getcon " __KCR0 ", %0":"=r"(cl));
+ ch = (ch) >> 32;
+ cl = (cl) & 0xffffffff;
+ printk("SR : %08Lx%08Lx TEA : %08Lx%08Lx KCR0: %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[0]) >> 32;
+ al = (regs->regs[0]) & 0xffffffff;
+ bh = (regs->regs[1]) >> 32;
+ bl = (regs->regs[1]) & 0xffffffff;
+ ch = (regs->regs[2]) >> 32;
+ cl = (regs->regs[2]) & 0xffffffff;
+ printk("R0 : %08Lx%08Lx R1 : %08Lx%08Lx R2 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[3]) >> 32;
+ al = (regs->regs[3]) & 0xffffffff;
+ bh = (regs->regs[4]) >> 32;
+ bl = (regs->regs[4]) & 0xffffffff;
+ ch = (regs->regs[5]) >> 32;
+ cl = (regs->regs[5]) & 0xffffffff;
+ printk("R3 : %08Lx%08Lx R4 : %08Lx%08Lx R5 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[6]) >> 32;
+ al = (regs->regs[6]) & 0xffffffff;
+ bh = (regs->regs[7]) >> 32;
+ bl = (regs->regs[7]) & 0xffffffff;
+ ch = (regs->regs[8]) >> 32;
+ cl = (regs->regs[8]) & 0xffffffff;
+ printk("R6 : %08Lx%08Lx R7 : %08Lx%08Lx R8 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+
+ ah = (regs->regs[9]) >> 32;
+ al = (regs->regs[9]) & 0xffffffff;
+ bh = (regs->regs[10]) >> 32;
+ bl = (regs->regs[10]) & 0xffffffff;
+ ch = (regs->regs[11]) >> 32;
+ cl = (regs->regs[11]) & 0xffffffff;
+ printk("R9 : %08Lx%08Lx R10 : %08Lx%08Lx R11 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+ printk("....\n");
+
+ ah = (regs->tregs[0]) >> 32;
+ al = (regs->tregs[0]) & 0xffffffff;
+ bh = (regs->tregs[1]) >> 32;
+ bl = (regs->tregs[1]) & 0xffffffff;
+ ch = (regs->tregs[2]) >> 32;
+ cl = (regs->tregs[2]) & 0xffffffff;
+ printk("T0 : %08Lx%08Lx T1 : %08Lx%08Lx T2 : %08Lx%08Lx\n",
+ ah, al, bh, bl, ch, cl);
+ printk("....\n");
+
+ print_dtlb();
+ print_itlb();
+}
+
+/* ======================================================================= */
+
+/*
+** Depending on <base> scan the MMU, Data or Instrction side
+** looking for a valid mapping matching Eaddr & asid.
+** Return -1 if not found or the TLB id entry otherwise.
+** Note: it works only for 4k pages!
+*/
+static unsigned long
+lookup_mmu_side(unsigned long base, unsigned long Eaddr, unsigned long asid)
+{
+ regType_t pteH;
+ unsigned long epn;
+ int count;
+
+ epn = Eaddr & 0xfffff000;
+
+ for (count = 0; count < MAX_TLBs; count++, base += TLB_STEP) {
+ pteH = getConfigReg(base);
+ if (GET_VALID(pteH))
+ if ((unsigned long) GET_EPN(pteH) == epn)
+ if ((unsigned long) GET_ASID(pteH) == asid)
+ break;
+ }
+ return ((unsigned long) ((count < MAX_TLBs) ? base : -1));
+}
+
+unsigned long lookup_dtlb(unsigned long Eaddr)
+{
+ unsigned long asid = get_asid();
+ return (lookup_mmu_side((u64) DTLB_BASE, Eaddr, asid));
+}
+
+unsigned long lookup_itlb(unsigned long Eaddr)
+{
+ unsigned long asid = get_asid();
+ return (lookup_mmu_side((u64) ITLB_BASE, Eaddr, asid));
+}
+
+void print_page(struct page *page)
+{
+ printk(" page[%p] -> index 0x%lx, count 0x%x, flags 0x%lx\n",
+ page, page->index, page_count(page), page->flags);
+ printk(" address_space = %p, pages =%ld\n", page->mapping,
+ page->mapping->nrpages);
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2000 David J. Mckay (david.mckay@st.com)
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * This file contains the I/O routines for use on the overdrive board
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <asm/system.h>
+#include <asm/processor.h>
+#include <asm/io.h>
+#ifdef CONFIG_SH_CAYMAN
+#include <asm/cayman.h>
+#endif
+
+/*
+ * readX/writeX() are used to access memory mapped devices. On some
+ * architectures the memory mapped IO stuff needs to be accessed
+ * differently. On the SuperH architecture, we just read/write the
+ * memory location directly.
+ */
+
+#define dprintk(x...)
+
+static int io_addr(int x) {
+ if (x < 0x400) {
+#ifdef CONFIG_SH_CAYMAN
+ return (x << 2) | smsc_superio_virt;
+#else
+ panic ("Illegal access to I/O port 0x%04x\n", x);
+ return 0;
+#endif
+ } else {
+#ifdef CONFIG_PCI
+ return (x + pciio_virt);
+#else
+ panic ("Illegal access to I/O port 0x%04x\n", x);
+ return 0;
+#endif
+ }
+}
+
+unsigned long inb(unsigned long port)
+{
+ unsigned long r;
+
+ r = ctrl_inb(io_addr(port));
+ dprintk("inb(0x%x)=0x%x (0x%x)\n", port, r, io_addr(port));
+ return r;
+}
+
+unsigned long inw(unsigned long port)
+{
+ unsigned long r;
+
+ r = ctrl_inw(io_addr(port));
+ dprintk("inw(0x%x)=0x%x (0x%x)\n", port, r, io_addr(port));
+ return r;
+}
+
+unsigned long inl(unsigned long port)
+{
+ unsigned long r;
+
+ r = ctrl_inl(io_addr(port));
+ dprintk("inl(0x%x)=0x%x (0x%x)\n", port, r, io_addr(port));
+ return r;
+}
+
+void outb(unsigned long value, unsigned long port)
+{
+ dprintk("outb(0x%x,0x%x) (0x%x)\n", value, port, io_addr(port));
+ ctrl_outb(value, io_addr(port));
+}
+
+void outw(unsigned long value, unsigned long port)
+{
+ dprintk("outw(0x%x,0x%x) (0x%x)\n", value, port, io_addr(port));
+ ctrl_outw(value, io_addr(port));
+}
+
+void outl(unsigned long value, unsigned long port)
+{
+ dprintk("outw(0x%x,0x%x) (0x%x)\n", value, port, io_addr(port));
+ ctrl_outl(value, io_addr(port));
+}
+
+/* This is horrible at the moment - needs more work to do something sensible */
+#define IO_DELAY()
+
+#define OUT_DELAY(x,type) \
+void out##x##_p(unsigned type value,unsigned long port){out##x(value,port);IO_DELAY();}
+
+#define IN_DELAY(x,type) \
+unsigned type in##x##_p(unsigned long port) {unsigned type tmp=in##x(port);IO_DELAY();return tmp;}
+
+#if 1
+OUT_DELAY(b, long) OUT_DELAY(w, long) OUT_DELAY(l, long)
+ IN_DELAY(b, long) IN_DELAY(w, long) IN_DELAY(l, long)
+#endif
+/* Now for the string version of these functions */
+void outsb(unsigned long port, const void *addr, unsigned long count)
+{
+ int i;
+ unsigned char *p = (unsigned char *) addr;
+
+ for (i = 0; i < count; i++, p++) {
+ outb(*p, port);
+ }
+}
+
+void insb(unsigned long port, void *addr, unsigned long count)
+{
+ int i;
+ unsigned char *p = (unsigned char *) addr;
+
+ for (i = 0; i < count; i++, p++) {
+ *p = inb(port);
+ }
+}
+
+/* For the 16 and 32 bit string functions, we have to worry about alignment.
+ * The SH does not do unaligned accesses, so we have to read as bytes and
+ * then write as a word or dword.
+ * This can be optimised a lot more, especially in the case where the data
+ * is aligned
+ */
+
+void outsw(unsigned long port, const void *addr, unsigned long count)
+{
+ int i;
+ unsigned short tmp;
+ unsigned char *p = (unsigned char *) addr;
+
+ for (i = 0; i < count; i++, p += 2) {
+ tmp = (*p) | ((*(p + 1)) << 8);
+ outw(tmp, port);
+ }
+}
+
+void insw(unsigned long port, void *addr, unsigned long count)
+{
+ int i;
+ unsigned short tmp;
+ unsigned char *p = (unsigned char *) addr;
+
+ for (i = 0; i < count; i++, p += 2) {
+ tmp = inw(port);
+ p[0] = tmp & 0xff;
+ p[1] = (tmp >> 8) & 0xff;
+ }
+}
+
+void outsl(unsigned long port, const void *addr, unsigned long count)
+{
+ int i;
+ unsigned tmp;
+ unsigned char *p = (unsigned char *) addr;
+
+ for (i = 0; i < count; i++, p += 4) {
+ tmp = (*p) | ((*(p + 1)) << 8) | ((*(p + 2)) << 16) |
+ ((*(p + 3)) << 24);
+ outl(tmp, port);
+ }
+}
+
+void insl(unsigned long port, void *addr, unsigned long count)
+{
+ int i;
+ unsigned tmp;
+ unsigned char *p = (unsigned char *) addr;
+
+ for (i = 0; i < count; i++, p += 4) {
+ tmp = inl(port);
+ p[0] = tmp & 0xff;
+ p[1] = (tmp >> 8) & 0xff;
+ p[2] = (tmp >> 16) & 0xff;
+ p[3] = (tmp >> 24) & 0xff;
+
+ }
+}
+
+void memcpy_toio(unsigned long to, const void *from, long count)
+{
+ unsigned char *p = (unsigned char *) from;
+
+ while (count) {
+ count--;
+ writeb(*p++, to++);
+ }
+}
+
+void memcpy_fromio(void *to, unsigned long from, long count)
+{
+ int i;
+ unsigned char *p = (unsigned char *) to;
+
+ for (i = 0; i < count; i++) {
+ p[i] = readb(from);
+ from++;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2002 Mark Debbage (Mark.Debbage@superh.com)
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <asm/string.h>
+
+// This is a simplistic optimization of memcpy to increase the
+// granularity of access beyond one byte using aligned
+// loads and stores. This is not an optimal implementation
+// for SH-5 (especially with regard to prefetching and the cache),
+// and a better version should be provided later ...
+
+void *memcpy(void *dest, const void *src, size_t count)
+{
+ char *d = (char *) dest, *s = (char *) src;
+
+ if (count >= 32) {
+ int i = 8 - (((unsigned long) d) & 0x7);
+
+ if (i != 8)
+ while (i-- && count--) {
+ *d++ = *s++;
+ }
+
+ if (((((unsigned long) d) & 0x7) == 0) &&
+ ((((unsigned long) s) & 0x7) == 0)) {
+ while (count >= 32) {
+ unsigned long long t1, t2, t3, t4;
+ t1 = *(unsigned long long *) (s);
+ t2 = *(unsigned long long *) (s + 8);
+ t3 = *(unsigned long long *) (s + 16);
+ t4 = *(unsigned long long *) (s + 24);
+ *(unsigned long long *) (d) = t1;
+ *(unsigned long long *) (d + 8) = t2;
+ *(unsigned long long *) (d + 16) = t3;
+ *(unsigned long long *) (d + 24) = t4;
+ d += 32;
+ s += 32;
+ count -= 32;
+ }
+ while (count >= 8) {
+ *(unsigned long long *) d =
+ *(unsigned long long *) s;
+ d += 8;
+ s += 8;
+ count -= 8;
+ }
+ }
+
+ if (((((unsigned long) d) & 0x3) == 0) &&
+ ((((unsigned long) s) & 0x3) == 0)) {
+ while (count >= 4) {
+ *(unsigned long *) d = *(unsigned long *) s;
+ d += 4;
+ s += 4;
+ count -= 4;
+ }
+ }
+
+ if (((((unsigned long) d) & 0x1) == 0) &&
+ ((((unsigned long) s) & 0x1) == 0)) {
+ while (count >= 2) {
+ *(unsigned short *) d = *(unsigned short *) s;
+ d += 2;
+ s += 2;
+ count -= 2;
+ }
+ }
+ }
+
+ while (count--) {
+ *d++ = *s++;
+ }
+
+ return d;
+}
--- /dev/null
+/*
+ * FIXME: old compatibility stuff, will be removed soon.
+ */
+
+#include <net/checksum.h>
+
+unsigned int csum_partial_copy( const char *src, char *dst, int len, int sum)
+{
+ int src_err=0, dst_err=0;
+
+ sum = csum_partial_copy_generic ( src, dst, len, sum, &src_err, &dst_err);
+
+ if (src_err || dst_err)
+ printk("old csum_partial_copy_fromuser(), tell mingo to convert me.\n");
+
+ return sum;
+}
--- /dev/null
+/*
+ * arch/sh64/lib/udelay.c
+ *
+ * Delay routines, using a pre-computed "loops_per_jiffy" value.
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003, 2004 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <asm/param.h>
+
+extern unsigned long loops_per_jiffy;
+
+/*
+ * Use only for very small delays (< 1 msec).
+ *
+ * The active part of our cycle counter is only 32-bits wide, and
+ * we're treating the difference between two marks as signed. On
+ * a 1GHz box, that's about 2 seconds.
+ */
+
+void __delay(int loops)
+{
+ long long dummy;
+ __asm__ __volatile__("gettr tr0, %1\n\t"
+ "pta $+4, tr0\n\t"
+ "addi %0, -1, %0\n\t"
+ "bne %0, r63, tr0\n\t"
+ "ptabs %1, tr0\n\t":"=r"(loops),
+ "=r"(dummy)
+ :"0"(loops));
+}
+
+void __udelay(unsigned long long usecs, unsigned long lpj)
+{
+ usecs *= (((unsigned long long) HZ << 32) / 1000000) * lpj;
+ __delay((long long) usecs >> 32);
+}
+
+void __ndelay(unsigned long long nsecs, unsigned long lpj)
+{
+ nsecs *= (((unsigned long long) HZ << 32) / 1000000000) * lpj;
+ __delay((long long) nsecs >> 32);
+}
+
+void udelay(unsigned long usecs)
+{
+ __udelay(usecs, loops_per_jiffy);
+}
+
+void ndelay(unsigned long nsecs)
+{
+ __ndelay(nsecs, loops_per_jiffy);
+}
+
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/kernel/irq_cayman.c
+ *
+ * SH-5 Cayman Interrupt Support
+ *
+ * This file handles the board specific parts of the Cayman interrupt system
+ *
+ * Copyright (C) 2002 Stuart Menefy
+ */
+
+#include <linux/config.h>
+#include <asm/irq.h>
+#include <asm/page.h>
+#include <asm/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/signal.h>
+#include <asm/cayman.h>
+
+unsigned long epld_virt;
+
+#define EPLD_BASE 0x04002000
+#define EPLD_STATUS_BASE (epld_virt + 0x10)
+#define EPLD_MASK_BASE (epld_virt + 0x20)
+
+/* Note the SMSC SuperIO chip and SMSC LAN chip interrupts are all muxed onto
+ the same SH-5 interrupt */
+
+static irqreturn_t cayman_interrupt_smsc(int irq, void *dev_id, struct pt_regs *regs)
+{
+ printk(KERN_INFO "CAYMAN: spurious SMSC interrupt\n");
+ return IRQ_NONE;
+}
+
+static irqreturn_t cayman_interrupt_pci2(int irq, void *dev_id, struct pt_regs *regs)
+{
+ printk(KERN_INFO "CAYMAN: spurious PCI interrupt, IRQ %d\n", irq);
+ return IRQ_NONE;
+}
+
+static struct irqaction cayman_action_smsc = {
+ .name = "Cayman SMSC Mux",
+ .handler = cayman_interrupt_smsc,
+ .flags = SA_INTERRUPT,
+};
+
+static struct irqaction cayman_action_pci2 = {
+ .name = "Cayman PCI2 Mux",
+ .handler = cayman_interrupt_pci2,
+ .flags = SA_INTERRUPT,
+};
+
+static void enable_cayman_irq(unsigned int irq)
+{
+ unsigned long flags;
+ unsigned long mask;
+ unsigned int reg;
+ unsigned char bit;
+
+ irq -= START_EXT_IRQS;
+ reg = EPLD_MASK_BASE + ((irq / 8) << 2);
+ bit = 1<<(irq % 8);
+ save_and_cli(flags);
+ mask = ctrl_inl(reg);
+ mask |= bit;
+ ctrl_outl(mask, reg);
+ restore_flags(flags);
+}
+
+void disable_cayman_irq(unsigned int irq)
+{
+ unsigned long flags;
+ unsigned long mask;
+ unsigned int reg;
+ unsigned char bit;
+
+ irq -= START_EXT_IRQS;
+ reg = EPLD_MASK_BASE + ((irq / 8) << 2);
+ bit = 1<<(irq % 8);
+ save_and_cli(flags);
+ mask = ctrl_inl(reg);
+ mask &= ~bit;
+ ctrl_outl(mask, reg);
+ restore_flags(flags);
+}
+
+static void ack_cayman_irq(unsigned int irq)
+{
+ disable_cayman_irq(irq);
+}
+
+static void end_cayman_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ enable_cayman_irq(irq);
+}
+
+static unsigned int startup_cayman_irq(unsigned int irq)
+{
+ enable_cayman_irq(irq);
+ return 0; /* never anything pending */
+}
+
+static void shutdown_cayman_irq(unsigned int irq)
+{
+ disable_cayman_irq(irq);
+}
+
+struct hw_interrupt_type cayman_irq_type = {
+ .typename = "Cayman-IRQ",
+ .startup = startup_cayman_irq,
+ .shutdown = shutdown_cayman_irq,
+ .enable = enable_cayman_irq,
+ .disable = disable_cayman_irq,
+ .ack = ack_cayman_irq,
+ .end = end_cayman_irq,
+};
+
+int cayman_irq_demux(int evt)
+{
+ int irq = intc_evt_to_irq[evt];
+
+ if (irq == SMSC_IRQ) {
+ unsigned long status;
+ int i;
+
+ status = ctrl_inl(EPLD_STATUS_BASE) &
+ ctrl_inl(EPLD_MASK_BASE) & 0xff;
+ if (status == 0) {
+ irq = -1;
+ } else {
+ for (i=0; i<8; i++) {
+ if (status & (1<<i))
+ break;
+ }
+ irq = START_EXT_IRQS + i;
+ }
+ }
+
+ if (irq == PCI2_IRQ) {
+ unsigned long status;
+ int i;
+
+ status = ctrl_inl(EPLD_STATUS_BASE + 3 * sizeof(u32)) &
+ ctrl_inl(EPLD_MASK_BASE + 3 * sizeof(u32)) & 0xff;
+ if (status == 0) {
+ irq = -1;
+ } else {
+ for (i=0; i<8; i++) {
+ if (status & (1<<i))
+ break;
+ }
+ irq = START_EXT_IRQS + (3 * 8) + i;
+ }
+ }
+
+ return irq;
+}
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_SYSCTL)
+int cayman_irq_describe(char* p, int irq)
+{
+ if (irq < NR_INTC_IRQS) {
+ return intc_irq_describe(p, irq);
+ } else if (irq < NR_INTC_IRQS + 8) {
+ return sprintf(p, "(SMSC %d)", irq - NR_INTC_IRQS);
+ } else if ((irq >= NR_INTC_IRQS + 24) && (irq < NR_INTC_IRQS + 32)) {
+ return sprintf(p, "(PCI2 %d)", irq - (NR_INTC_IRQS + 24));
+ }
+
+ return 0;
+}
+#endif
+
+void init_cayman_irq(void)
+{
+ int i;
+
+ epld_virt = onchip_remap(EPLD_BASE, 1024, "EPLD");
+ if (!epld_virt) {
+ printk(KERN_ERR "Cayman IRQ: Unable to remap EPLD\n");
+ return;
+ }
+
+ for (i=0; i<NR_EXT_IRQS; i++) {
+ irq_desc[START_EXT_IRQS + i].handler = &cayman_irq_type;
+ }
+
+ /* Setup the SMSC interrupt */
+ setup_irq(SMSC_IRQ, &cayman_action_smsc);
+ setup_irq(PCI2_IRQ, &cayman_action_pci2);
+}
--- /dev/null
+/*
+ * arch/sh64/kernel/led_cayman.c
+ *
+ * Copyright (C) 2002 Stuart Menefy <stuart.menefy@st.com>
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * Flash the LEDs
+ */
+#include <asm/io.h>
+
+/*
+** It is supposed these functions to be used for a low level
+** debugging (via Cayman LEDs), hence to be available as soon
+** as possible.
+** Unfortunately Cayman LEDs relies on Cayman EPLD to be mapped
+** (this happen when IRQ are initialized... quite late).
+** These triky dependencies should be removed. Temporary, it
+** may be enough to NOP until EPLD is mapped.
+*/
+
+extern unsigned long epld_virt;
+
+#define LED_ADDR (epld_virt + 0x008)
+#define HDSP2534_ADDR (epld_virt + 0x100)
+
+void mach_led(int position, int value)
+{
+ if (!epld_virt)
+ return;
+
+ if (value)
+ ctrl_outl(0, LED_ADDR);
+ else
+ ctrl_outl(1, LED_ADDR);
+
+}
+
+void mach_alphanum(int position, unsigned char value)
+{
+ if (!epld_virt)
+ return;
+
+ ctrl_outb(value, HDSP2534_ADDR + 0xe0 + (position << 2));
+}
+
+void mach_alphanum_brightness(int setting)
+{
+ ctrl_outb(setting & 7, HDSP2534_ADDR + 0xc0);
+}
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/mach-cayman/setup.c
+ *
+ * SH5 Cayman support
+ *
+ * This file handles the architecture-dependent parts of initialization
+ *
+ * Copyright David J. Mckay.
+ * Needs major work!
+ *
+ * benedict.gaster@superh.com: 3rd May 2002
+ * Added support for ramdisk, removing statically linked romfs at the same time.
+ *
+ * lethal@linux-sh.org: 15th May 2003
+ * Use the generic procfs cpuinfo interface, just return a valid board name.
+ */
+
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/bootmem.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/seq_file.h>
+#include <asm/processor.h>
+#include <asm/platform.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/page.h>
+
+#define RES_COUNT(res) ((sizeof((res))/sizeof(struct resource)))
+
+/*
+ * Platform Dependent Interrupt Priorities.
+ */
+
+/* Using defaults defined in irq.h */
+#define RES NO_PRIORITY /* Disabled */
+#define IR0 IRL0_PRIORITY /* IRLs */
+#define IR1 IRL1_PRIORITY
+#define IR2 IRL2_PRIORITY
+#define IR3 IRL3_PRIORITY
+#define PCA INTA_PRIORITY /* PCI Ints */
+#define PCB INTB_PRIORITY
+#define PCC INTC_PRIORITY
+#define PCD INTD_PRIORITY
+#define SER TOP_PRIORITY
+#define ERR TOP_PRIORITY
+#define PW0 TOP_PRIORITY
+#define PW1 TOP_PRIORITY
+#define PW2 TOP_PRIORITY
+#define PW3 TOP_PRIORITY
+#define DM0 NO_PRIORITY /* DMA Ints */
+#define DM1 NO_PRIORITY
+#define DM2 NO_PRIORITY
+#define DM3 NO_PRIORITY
+#define DAE NO_PRIORITY
+#define TU0 TIMER_PRIORITY /* TMU Ints */
+#define TU1 NO_PRIORITY
+#define TU2 NO_PRIORITY
+#define TI2 NO_PRIORITY
+#define ATI NO_PRIORITY /* RTC Ints */
+#define PRI NO_PRIORITY
+#define CUI RTC_PRIORITY
+#define ERI SCIF_PRIORITY /* SCIF Ints */
+#define RXI SCIF_PRIORITY
+#define BRI SCIF_PRIORITY
+#define TXI SCIF_PRIORITY
+#define ITI TOP_PRIORITY /* WDT Ints */
+
+/* Setup for the SMSC FDC37C935 */
+#define SMSC_SUPERIO_BASE 0x04000000
+#define SMSC_CONFIG_PORT_ADDR 0x3f0
+#define SMSC_INDEX_PORT_ADDR SMSC_CONFIG_PORT_ADDR
+#define SMSC_DATA_PORT_ADDR 0x3f1
+
+#define SMSC_ENTER_CONFIG_KEY 0x55
+#define SMSC_EXIT_CONFIG_KEY 0xaa
+
+#define SMCS_LOGICAL_DEV_INDEX 0x07
+#define SMSC_DEVICE_ID_INDEX 0x20
+#define SMSC_DEVICE_REV_INDEX 0x21
+#define SMSC_ACTIVATE_INDEX 0x30
+#define SMSC_PRIMARY_INT_INDEX 0x70
+#define SMSC_SECONDARY_INT_INDEX 0x72
+
+#define SMSC_KEYBOARD_DEVICE 7
+
+#define SMSC_SUPERIO_READ_INDEXED(index) ({ \
+ outb((index), SMSC_INDEX_PORT_ADDR); \
+ inb(SMSC_DATA_PORT_ADDR); })
+#define SMSC_SUPERIO_WRITE_INDEXED(val, index) ({ \
+ outb((index), SMSC_INDEX_PORT_ADDR); \
+ outb((val), SMSC_DATA_PORT_ADDR); })
+
+unsigned long smsc_superio_virt;
+
+/*
+ * Platform dependent structures: maps and parms block.
+ */
+struct resource io_resources[] = {
+ /* To be updated with external devices */
+};
+
+struct resource kram_resources[] = {
+ { "Kernel code", 0, 0 }, /* These must be last in the array */
+ { "Kernel data", 0, 0 } /* These must be last in the array */
+};
+
+struct resource xram_resources[] = {
+ /* To be updated with external devices */
+};
+
+struct resource rom_resources[] = {
+ /* To be updated with external devices */
+};
+
+struct sh64_platform platform_parms = {
+ .readonly_rootfs = 1,
+ .initial_root_dev = 0x0100,
+ .loader_type = 1,
+ .io_res_p = io_resources,
+ .io_res_count = RES_COUNT(io_resources),
+ .kram_res_p = kram_resources,
+ .kram_res_count = RES_COUNT(kram_resources),
+ .xram_res_p = xram_resources,
+ .xram_res_count = RES_COUNT(xram_resources),
+ .rom_res_p = rom_resources,
+ .rom_res_count = RES_COUNT(rom_resources),
+};
+
+int platform_int_priority[NR_INTC_IRQS] = {
+ IR0, IR1, IR2, IR3, PCA, PCB, PCC, PCD, /* IRQ 0- 7 */
+ RES, RES, RES, RES, SER, ERR, PW3, PW2, /* IRQ 8-15 */
+ PW1, PW0, DM0, DM1, DM2, DM3, DAE, RES, /* IRQ 16-23 */
+ RES, RES, RES, RES, RES, RES, RES, RES, /* IRQ 24-31 */
+ TU0, TU1, TU2, TI2, ATI, PRI, CUI, ERI, /* IRQ 32-39 */
+ RXI, BRI, TXI, RES, RES, RES, RES, RES, /* IRQ 40-47 */
+ RES, RES, RES, RES, RES, RES, RES, RES, /* IRQ 48-55 */
+ RES, RES, RES, RES, RES, RES, RES, ITI, /* IRQ 56-63 */
+};
+
+static int __init smsc_superio_setup(void)
+{
+ unsigned char devid, devrev;
+
+ smsc_superio_virt = onchip_remap(SMSC_SUPERIO_BASE, 1024, "SMSC SuperIO");
+ if (!smsc_superio_virt) {
+ panic("Unable to remap SMSC SuperIO\n");
+ }
+
+ /* Initially the chip is in run state */
+ /* Put it into configuration state */
+ outb(SMSC_ENTER_CONFIG_KEY, SMSC_CONFIG_PORT_ADDR);
+ outb(SMSC_ENTER_CONFIG_KEY, SMSC_CONFIG_PORT_ADDR);
+
+ /* Read device ID info */
+ devid = SMSC_SUPERIO_READ_INDEXED(SMSC_DEVICE_ID_INDEX);
+ devrev = SMSC_SUPERIO_READ_INDEXED(SMSC_DEVICE_REV_INDEX);
+ printk("SMSC SuperIO devid %02x rev %02x\n", devid, devrev);
+
+ /* Select the keyboard device */
+ SMSC_SUPERIO_WRITE_INDEXED(SMSC_KEYBOARD_DEVICE, SMCS_LOGICAL_DEV_INDEX);
+
+ /* enable it */
+ SMSC_SUPERIO_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
+
+ /* Select the interrupts */
+ /* On a PC keyboard is IRQ1, mouse is IRQ12 */
+ SMSC_SUPERIO_WRITE_INDEXED(1, SMSC_PRIMARY_INT_INDEX);
+ SMSC_SUPERIO_WRITE_INDEXED(12, SMSC_SECONDARY_INT_INDEX);
+
+ /* Exit the configuraton state */
+ outb(SMSC_EXIT_CONFIG_KEY, SMSC_CONFIG_PORT_ADDR);
+
+ return 0;
+}
+
+/* This is grotty, but, because kernel is always referenced on the link line
+ * before any devices, this is safe.
+ */
+__initcall(smsc_superio_setup);
+
+void __init platform_setup(void)
+{
+ /* Cayman platform leaves the decision to head.S, for now */
+ platform_parms.fpu_flags = fpu_in_use;
+}
+
+void __init platform_monitor(void)
+{
+ /* Nothing yet .. */
+}
+
+void __init platform_reserve(void)
+{
+ /* Nothing yet .. */
+}
+
+const char *get_system_type(void)
+{
+ return "Hitachi Cayman";
+}
+
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/mach-harp/setup.c
+ *
+ * SH-5 Simulator Platform Support
+ *
+ * This file handles the architecture-dependent parts of initialization
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ *
+ * benedict.gaster@superh.com: 3rd May 2002
+ * Added support for ramdisk, removing statically linked romfs at the same time. *
+ *
+ * lethal@linux-sh.org: 15th May 2003
+ * Use the generic procfs cpuinfo interface, just return a valid board name.
+ */
+
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/bootmem.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <asm/processor.h>
+#include <asm/platform.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/page.h>
+
+#define RES_COUNT(res) ((sizeof((res))/sizeof(struct resource)))
+
+/*
+ * Platform Dependent Interrupt Priorities.
+ */
+
+/* Using defaults defined in irq.h */
+#define RES NO_PRIORITY /* Disabled */
+#define IR0 IRL0_PRIORITY /* IRLs */
+#define IR1 IRL1_PRIORITY
+#define IR2 IRL2_PRIORITY
+#define IR3 IRL3_PRIORITY
+#define PCA INTA_PRIORITY /* PCI Ints */
+#define PCB INTB_PRIORITY
+#define PCC INTC_PRIORITY
+#define PCD INTD_PRIORITY
+#define SER TOP_PRIORITY
+#define ERR TOP_PRIORITY
+#define PW0 TOP_PRIORITY
+#define PW1 TOP_PRIORITY
+#define PW2 TOP_PRIORITY
+#define PW3 TOP_PRIORITY
+#define DM0 NO_PRIORITY /* DMA Ints */
+#define DM1 NO_PRIORITY
+#define DM2 NO_PRIORITY
+#define DM3 NO_PRIORITY
+#define DAE NO_PRIORITY
+#define TU0 TIMER_PRIORITY /* TMU Ints */
+#define TU1 NO_PRIORITY
+#define TU2 NO_PRIORITY
+#define TI2 NO_PRIORITY
+#define ATI NO_PRIORITY /* RTC Ints */
+#define PRI NO_PRIORITY
+#define CUI RTC_PRIORITY
+#define ERI SCIF_PRIORITY /* SCIF Ints */
+#define RXI SCIF_PRIORITY
+#define BRI SCIF_PRIORITY
+#define TXI SCIF_PRIORITY
+#define ITI TOP_PRIORITY /* WDT Ints */
+
+/*
+ * Platform dependent structures: maps and parms block.
+ */
+struct resource io_resources[] = {
+ /* To be updated with external devices */
+};
+
+struct resource kram_resources[] = {
+ { "Kernel code", 0, 0 }, /* These must be last in the array */
+ { "Kernel data", 0, 0 } /* These must be last in the array */
+};
+
+struct resource xram_resources[] = {
+ /* To be updated with external devices */
+};
+
+struct resource rom_resources[] = {
+ /* To be updated with external devices */
+};
+
+struct sh64_platform platform_parms = {
+ .readonly_rootfs = 1,
+ .initial_root_dev = 0x0100,
+ .loader_type = 1,
+ .io_res_p = io_resources,
+ .io_res_count = RES_COUNT(io_resources),
+ .kram_res_p = kram_resources,
+ .kram_res_count = RES_COUNT(kram_resources),
+ .xram_res_p = xram_resources,
+ .xram_res_count = RES_COUNT(xram_resources),
+ .rom_res_p = rom_resources,
+ .rom_res_count = RES_COUNT(rom_resources),
+};
+
+int platform_int_priority[NR_INTC_IRQS] = {
+ IR0, IR1, IR2, IR3, PCA, PCB, PCC, PCD, /* IRQ 0- 7 */
+ RES, RES, RES, RES, SER, ERR, PW3, PW2, /* IRQ 8-15 */
+ PW1, PW0, DM0, DM1, DM2, DM3, DAE, RES, /* IRQ 16-23 */
+ RES, RES, RES, RES, RES, RES, RES, RES, /* IRQ 24-31 */
+ TU0, TU1, TU2, TI2, ATI, PRI, CUI, ERI, /* IRQ 32-39 */
+ RXI, BRI, TXI, RES, RES, RES, RES, RES, /* IRQ 40-47 */
+ RES, RES, RES, RES, RES, RES, RES, RES, /* IRQ 48-55 */
+ RES, RES, RES, RES, RES, RES, RES, ITI, /* IRQ 56-63 */
+};
+
+void __init platform_setup(void)
+{
+ /* Harp platform leaves the decision to head.S, for now */
+ platform_parms.fpu_flags = fpu_in_use;
+}
+
+void __init platform_monitor(void)
+{
+ /* Nothing yet .. */
+}
+
+void __init platform_reserve(void)
+{
+ /* Nothing yet .. */
+}
+
+const char *get_system_type(void)
+{
+ return "ST50 Harp";
+}
+
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/mach-romram/setup.c
+ *
+ * SH-5 ROM/RAM Platform Support
+ *
+ * This file handles the architecture-dependent parts of initialization
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ *
+ * benedict.gaster@superh.com: 3rd May 2002
+ * Added support for ramdisk, removing statically linked romfs at the same time. *
+ *
+ * lethal@linux-sh.org: 15th May 2003
+ * Use the generic procfs cpuinfo interface, just return a valid board name.
+ *
+ * Sean.McGoogan@superh.com 17th Feb 2004
+ * copied from arch/sh64/mach-harp/setup.c
+ */
+
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/bootmem.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <asm/processor.h>
+#include <asm/platform.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/page.h>
+
+#define RES_COUNT(res) ((sizeof((res))/sizeof(struct resource)))
+
+/*
+ * Platform Dependent Interrupt Priorities.
+ */
+
+/* Using defaults defined in irq.h */
+#define RES NO_PRIORITY /* Disabled */
+#define IR0 IRL0_PRIORITY /* IRLs */
+#define IR1 IRL1_PRIORITY
+#define IR2 IRL2_PRIORITY
+#define IR3 IRL3_PRIORITY
+#define PCA INTA_PRIORITY /* PCI Ints */
+#define PCB INTB_PRIORITY
+#define PCC INTC_PRIORITY
+#define PCD INTD_PRIORITY
+#define SER TOP_PRIORITY
+#define ERR TOP_PRIORITY
+#define PW0 TOP_PRIORITY
+#define PW1 TOP_PRIORITY
+#define PW2 TOP_PRIORITY
+#define PW3 TOP_PRIORITY
+#define DM0 NO_PRIORITY /* DMA Ints */
+#define DM1 NO_PRIORITY
+#define DM2 NO_PRIORITY
+#define DM3 NO_PRIORITY
+#define DAE NO_PRIORITY
+#define TU0 TIMER_PRIORITY /* TMU Ints */
+#define TU1 NO_PRIORITY
+#define TU2 NO_PRIORITY
+#define TI2 NO_PRIORITY
+#define ATI NO_PRIORITY /* RTC Ints */
+#define PRI NO_PRIORITY
+#define CUI RTC_PRIORITY
+#define ERI SCIF_PRIORITY /* SCIF Ints */
+#define RXI SCIF_PRIORITY
+#define BRI SCIF_PRIORITY
+#define TXI SCIF_PRIORITY
+#define ITI TOP_PRIORITY /* WDT Ints */
+
+/*
+ * Platform dependent structures: maps and parms block.
+ */
+struct resource io_resources[] = {
+ /* To be updated with external devices */
+};
+
+struct resource kram_resources[] = {
+ { "Kernel code", 0, 0 }, /* These must be last in the array */
+ { "Kernel data", 0, 0 } /* These must be last in the array */
+};
+
+struct resource xram_resources[] = {
+ /* To be updated with external devices */
+};
+
+struct resource rom_resources[] = {
+ /* To be updated with external devices */
+};
+
+struct sh64_platform platform_parms = {
+ .readonly_rootfs = 1,
+ .initial_root_dev = 0x0100,
+ .loader_type = 1,
+ .io_res_p = io_resources,
+ .io_res_count = RES_COUNT(io_resources),
+ .kram_res_p = kram_resources,
+ .kram_res_count = RES_COUNT(kram_resources),
+ .xram_res_p = xram_resources,
+ .xram_res_count = RES_COUNT(xram_resources),
+ .rom_res_p = rom_resources,
+ .rom_res_count = RES_COUNT(rom_resources),
+};
+
+int platform_int_priority[NR_INTC_IRQS] = {
+ IR0, IR1, IR2, IR3, PCA, PCB, PCC, PCD, /* IRQ 0- 7 */
+ RES, RES, RES, RES, SER, ERR, PW3, PW2, /* IRQ 8-15 */
+ PW1, PW0, DM0, DM1, DM2, DM3, DAE, RES, /* IRQ 16-23 */
+ RES, RES, RES, RES, RES, RES, RES, RES, /* IRQ 24-31 */
+ TU0, TU1, TU2, TI2, ATI, PRI, CUI, ERI, /* IRQ 32-39 */
+ RXI, BRI, TXI, RES, RES, RES, RES, RES, /* IRQ 40-47 */
+ RES, RES, RES, RES, RES, RES, RES, RES, /* IRQ 48-55 */
+ RES, RES, RES, RES, RES, RES, RES, ITI, /* IRQ 56-63 */
+};
+
+void __init platform_setup(void)
+{
+ /* ROM/RAM platform leaves the decision to head.S, for now */
+ platform_parms.fpu_flags = fpu_in_use;
+}
+
+void __init platform_monitor(void)
+{
+ /* Nothing yet .. */
+}
+
+void __init platform_reserve(void)
+{
+ /* Nothing yet .. */
+}
+
+const char *get_system_type(void)
+{
+ return "ROM/RAM";
+}
+
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/mach-sim/setup.c
+ *
+ * ST50 Simulator Platform Support
+ *
+ * This file handles the architecture-dependent parts of initialization
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ *
+ * lethal@linux-sh.org: 15th May 2003
+ * Use the generic procfs cpuinfo interface, just return a valid board name.
+ */
+
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/bootmem.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <asm/addrspace.h>
+#include <asm/processor.h>
+#include <asm/platform.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/page.h>
+
+#ifdef CONFIG_BLK_DEV_INITRD
+#include "../rootfs/rootfs.h"
+#endif
+
+static __init void platform_monitor(void);
+static __init void platform_setup(void);
+static __init void platform_reserve(void);
+
+
+#define PHYS_MEMORY CONFIG_MEMORY_SIZE_IN_MB*1024*1024
+
+#if (PHYS_MEMORY < P1SEG_FOOTPRINT_RAM)
+#error "Invalid kernel configuration. Physical memory below footprint requirements."
+#endif
+
+#define RAM_DISK_START CONFIG_MEMORY_START+P1SEG_INITRD_BLOCK /* Top of 4MB */
+#ifdef PLATFORM_ROMFS_SIZE
+#define RAM_DISK_SIZE (PAGE_ALIGN(PLATFORM_ROMFS_SIZE)) /* Variable Top */
+#if ((RAM_DISK_START + RAM_DISK_SIZE) > (CONFIG_MEMORY_START + PHYS_MEMORY))
+#error "Invalid kernel configuration. ROM RootFS exceeding physical memory."
+#endif
+#else
+#define RAM_DISK_SIZE P1SEG_INITRD_BLOCK_SIZE /* Top of 4MB */
+#endif
+
+#define RES_COUNT(res) ((sizeof((res))/sizeof(struct resource)))
+
+/*
+ * Platform Dependent Interrupt Priorities.
+ */
+
+/* Using defaults defined in irq.h */
+#define RES NO_PRIORITY /* Disabled */
+#define IR0 IRL0_PRIORITY /* IRLs */
+#define IR1 IRL1_PRIORITY
+#define IR2 IRL2_PRIORITY
+#define IR3 IRL3_PRIORITY
+#define PCA INTA_PRIORITY /* PCI Ints */
+#define PCB INTB_PRIORITY
+#define PCC INTC_PRIORITY
+#define PCD INTD_PRIORITY
+#define SER TOP_PRIORITY
+#define ERR TOP_PRIORITY
+#define PW0 TOP_PRIORITY
+#define PW1 TOP_PRIORITY
+#define PW2 TOP_PRIORITY
+#define PW3 TOP_PRIORITY
+#define DM0 NO_PRIORITY /* DMA Ints */
+#define DM1 NO_PRIORITY
+#define DM2 NO_PRIORITY
+#define DM3 NO_PRIORITY
+#define DAE NO_PRIORITY
+#define TU0 TIMER_PRIORITY /* TMU Ints */
+#define TU1 NO_PRIORITY
+#define TU2 NO_PRIORITY
+#define TI2 NO_PRIORITY
+#define ATI NO_PRIORITY /* RTC Ints */
+#define PRI NO_PRIORITY
+#define CUI RTC_PRIORITY
+#define ERI SCIF_PRIORITY /* SCIF Ints */
+#define RXI SCIF_PRIORITY
+#define BRI SCIF_PRIORITY
+#define TXI SCIF_PRIORITY
+#define ITI TOP_PRIORITY /* WDT Ints */
+
+/*
+ * Platform dependent structures: maps and parms block.
+ */
+struct resource io_resources[] = {
+ /* Nothing yet .. */
+};
+
+struct resource kram_resources[] = {
+ { "Kernel code", 0, 0 }, /* These must be last in the array */
+ { "Kernel data", 0, 0 } /* These must be last in the array */
+};
+
+struct resource xram_resources[] = {
+ /* Nothing yet .. */
+};
+
+struct resource rom_resources[] = {
+ /* Nothing yet .. */
+};
+
+struct sh64_platform platform_parms = {
+ .readonly_rootfs = 1,
+ .initial_root_dev = 0x0100,
+ .loader_type = 1,
+ .initrd_start = RAM_DISK_START,
+ .initrd_size = RAM_DISK_SIZE,
+ .io_res_p = io_resources,
+ .io_res_count = RES_COUNT(io_resources),
+ .kram_res_p = kram_resources,
+ .kram_res_count = RES_COUNT(kram_resources),
+ .xram_res_p = xram_resources,
+ .xram_res_count = RES_COUNT(xram_resources),
+ .rom_res_p = rom_resources,
+ .rom_res_count = RES_COUNT(rom_resources),
+};
+
+int platform_int_priority[NR_IRQS] = {
+ IR0, IR1, IR2, IR3, PCA, PCB, PCC, PCD, /* IRQ 0- 7 */
+ RES, RES, RES, RES, SER, ERR, PW3, PW2, /* IRQ 8-15 */
+ PW1, PW0, DM0, DM1, DM2, DM3, DAE, RES, /* IRQ 16-23 */
+ RES, RES, RES, RES, RES, RES, RES, RES, /* IRQ 24-31 */
+ TU0, TU1, TU2, TI2, ATI, PRI, CUI, ERI, /* IRQ 32-39 */
+ RXI, BRI, TXI, RES, RES, RES, RES, RES, /* IRQ 40-47 */
+ RES, RES, RES, RES, RES, RES, RES, RES, /* IRQ 48-55 */
+ RES, RES, RES, RES, RES, RES, RES, ITI, /* IRQ 56-63 */
+};
+
+void __init platform_setup(void)
+{
+ /* Simulator platform leaves the decision to head.S */
+ platform_parms.fpu_flags = fpu_in_use;
+}
+
+void __init platform_monitor(void)
+{
+ /* Nothing yet .. */
+}
+
+void __init platform_reserve(void)
+{
+ /* Nothing yet .. */
+}
+
+const char *get_system_type(void)
+{
+ return "SH-5 Simulator";
+}
+
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/mm/cache.c
+ *
+ * Original version Copyright (C) 2000, 2001 Paolo Alberelli
+ * Second version Copyright (C) benedict.gaster@superh.com 2002
+ * Third version Copyright Richard.Curnow@superh.com 2003
+ * Hacks to third version Copyright (C) 2003 Paul Mundt
+ */
+
+/****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/threads.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+#include <asm/cache.h>
+#include <asm/tlb.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/mmu_context.h>
+#include <asm/pgalloc.h> /* for flush_itlb_range */
+
+#include <linux/proc_fs.h>
+
+/* This function is in entry.S */
+extern unsigned long switch_and_save_asid(unsigned long new_asid);
+
+/* Wired TLB entry for the D-cache */
+static unsigned long long dtlb_cache_slot;
+
+/**
+ * sh64_cache_init()
+ *
+ * This is pretty much just a straightforward clone of the SH
+ * detect_cpu_and_cache_system().
+ *
+ * This function is responsible for setting up all of the cache
+ * info dynamically as well as taking care of CPU probing and
+ * setting up the relevant subtype data.
+ *
+ * FIXME: For the time being, we only really support the SH5-101
+ * out of the box, and don't support dynamic probing for things
+ * like the SH5-103 or even cut2 of the SH5-101. Implement this
+ * later!
+ */
+int __init sh64_cache_init(void)
+{
+ /*
+ * First, setup some sane values for the I-cache.
+ */
+ cpu_data->icache.ways = 4;
+ cpu_data->icache.sets = 256;
+ cpu_data->icache.linesz = L1_CACHE_BYTES;
+
+ /*
+ * FIXME: This can probably be cleaned up a bit as well.. for example,
+ * do we really need the way shift _and_ the way_step_shift ?? Judging
+ * by the existing code, I would guess no.. is there any valid reason
+ * why we need to be tracking this around?
+ */
+ cpu_data->icache.way_shift = 13;
+ cpu_data->icache.entry_shift = 5;
+ cpu_data->icache.set_shift = 4;
+ cpu_data->icache.way_step_shift = 16;
+ cpu_data->icache.asid_shift = 2;
+
+ /*
+ * way offset = cache size / associativity, so just don't factor in
+ * associativity in the first place..
+ */
+ cpu_data->icache.way_ofs = cpu_data->icache.sets *
+ cpu_data->icache.linesz;
+
+ cpu_data->icache.asid_mask = 0x3fc;
+ cpu_data->icache.idx_mask = 0x1fe0;
+ cpu_data->icache.epn_mask = 0xffffe000;
+ cpu_data->icache.flags = 0;
+
+ /*
+ * Next, setup some sane values for the D-cache.
+ *
+ * On the SH5, these are pretty consistent with the I-cache settings,
+ * so we just copy over the existing definitions.. these can be fixed
+ * up later, especially if we add runtime CPU probing.
+ *
+ * Though in the meantime it saves us from having to duplicate all of
+ * the above definitions..
+ */
+ cpu_data->dcache = cpu_data->icache;
+
+ /*
+ * Setup any cache-related flags here
+ */
+#if defined(CONFIG_DCACHE_WRITE_THROUGH)
+ set_bit(SH_CACHE_MODE_WT, &(cpu_data->dcache.flags));
+#elif defined(CONFIG_DCACHE_WRITE_BACK)
+ set_bit(SH_CACHE_MODE_WB, &(cpu_data->dcache.flags));
+#endif
+
+ /*
+ * We also need to reserve a slot for the D-cache in the DTLB, so we
+ * do this now ..
+ */
+ dtlb_cache_slot = sh64_get_wired_dtlb_entry();
+
+ return 0;
+}
+
+/*##########################################################################*/
+
+/* From here onwards, a rewrite of the implementation,
+ by Richard.Curnow@superh.com.
+
+ The major changes in this compared to the old version are;
+ 1. use more selective purging through OCBP instead of using ALLOCO to purge
+ by natural replacement. This avoids purging out unrelated cache lines
+ that happen to be in the same set.
+ 2. exploit the APIs copy_user_page and clear_user_page better
+ 3. be more selective about I-cache purging, in particular use invalidate_all
+ more sparingly.
+
+ */
+
+/*##########################################################################
+ SUPPORT FUNCTIONS
+ ##########################################################################*/
+
+/****************************************************************************/
+/* The following group of functions deal with mapping and unmapping a temporary
+ page into the DTLB slot that have been set aside for our exclusive use. */
+/* In order to accomplish this, we use the generic interface for adding and
+ removing a wired slot entry as defined in arch/sh64/mm/tlb.c */
+/****************************************************************************/
+
+static unsigned long slot_own_flags;
+
+static inline void sh64_setup_dtlb_cache_slot(unsigned long eaddr, unsigned long asid, unsigned long paddr)
+{
+ local_irq_save(slot_own_flags);
+ sh64_setup_tlb_slot(dtlb_cache_slot, eaddr, asid, paddr);
+}
+
+static inline void sh64_teardown_dtlb_cache_slot(void)
+{
+ sh64_teardown_tlb_slot(dtlb_cache_slot);
+ local_irq_restore(slot_own_flags);
+}
+
+/****************************************************************************/
+
+#ifndef CONFIG_ICACHE_DISABLED
+
+static void __inline__ sh64_icache_inv_all(void)
+{
+ unsigned long long addr, flag, data;
+ unsigned int flags;
+
+ addr=ICCR0;
+ flag=ICCR0_ICI;
+ data=0;
+
+ /* Make this a critical section for safety (probably not strictly necessary.) */
+ local_irq_save(flags);
+
+ /* Without %1 it gets unexplicably wrong */
+ asm volatile("getcfg %3, 0, %0\n\t"
+ "or %0, %2, %0\n\t"
+ "putcfg %3, 0, %0\n\t"
+ "synci"
+ : "=&r" (data)
+ : "0" (data), "r" (flag), "r" (addr));
+
+ local_irq_restore(flags);
+}
+
+static void sh64_icache_inv_kernel_range(unsigned long start, unsigned long end)
+{
+ /* Invalidate range of addresses [start,end] from the I-cache, where
+ * the addresses lie in the kernel superpage. */
+
+ unsigned long long ullend, addr, aligned_start;
+#if (NEFF == 32)
+ aligned_start = (unsigned long long)(signed long long)(signed long) start;
+#else
+#error "NEFF != 32"
+#endif
+ aligned_start &= L1_CACHE_ALIGN_MASK;
+ addr = aligned_start;
+#if (NEFF == 32)
+ ullend = (unsigned long long) (signed long long) (signed long) end;
+#else
+#error "NEFF != 32"
+#endif
+ while (addr <= ullend) {
+ asm __volatile__ ("icbi %0, 0" : : "r" (addr));
+ addr += L1_CACHE_BYTES;
+ }
+}
+
+static void sh64_icache_inv_user_page(struct vm_area_struct *vma, unsigned long eaddr)
+{
+ /* If we get called, we know that vma->vm_flags contains VM_EXEC.
+ Also, eaddr is page-aligned. */
+
+ unsigned long long addr, end_addr;
+ unsigned long flags = 0;
+ unsigned long running_asid, vma_asid;
+ addr = eaddr;
+ end_addr = addr + PAGE_SIZE;
+
+ /* Check whether we can use the current ASID for the I-cache
+ invalidation. For example, if we're called via
+ access_process_vm->flush_cache_page->here, (e.g. when reading from
+ /proc), 'running_asid' will be that of the reader, not of the
+ victim.
+
+ Also, note the risk that we might get pre-empted between the ASID
+ compare and blocking IRQs, and before we regain control, the
+ pid->ASID mapping changes. However, the whole cache will get
+ invalidated when the mapping is renewed, so the worst that can
+ happen is that the loop below ends up invalidating somebody else's
+ cache entries.
+ */
+
+ running_asid = get_asid();
+ vma_asid = (vma->vm_mm->context & MMU_CONTEXT_ASID_MASK);
+ if (running_asid != vma_asid) {
+ local_irq_save(flags);
+ switch_and_save_asid(vma_asid);
+ }
+ while (addr < end_addr) {
+ /* Worth unrolling a little */
+ asm __volatile__("icbi %0, 0" : : "r" (addr));
+ asm __volatile__("icbi %0, 32" : : "r" (addr));
+ asm __volatile__("icbi %0, 64" : : "r" (addr));
+ asm __volatile__("icbi %0, 96" : : "r" (addr));
+ addr += 128;
+ }
+ if (running_asid != vma_asid) {
+ switch_and_save_asid(running_asid);
+ local_irq_restore(flags);
+ }
+}
+
+/****************************************************************************/
+
+static void sh64_icache_inv_user_page_range(struct mm_struct *mm,
+ unsigned long start, unsigned long end)
+{
+ /* Used for invalidating big chunks of I-cache, i.e. assume the range
+ is whole pages. If 'start' or 'end' is not page aligned, the code
+ is conservative and invalidates to the ends of the enclosing pages.
+ This is functionally OK, just a performance loss. */
+
+ /* See the comments below in sh64_dcache_purge_user_range() regarding
+ the choice of algorithm. However, for the I-cache option (2) isn't
+ available because there are no physical tags so aliases can't be
+ resolved. The icbi instruction has to be used through the user
+ mapping. Because icbi is cheaper than ocbp on a cache hit, it
+ would be cheaper to use the selective code for a large range than is
+ possible with the D-cache. Just assume 64 for now as a working
+ figure.
+ */
+
+ int n_pages;
+
+ if (!mm) return;
+
+ n_pages = ((end - start) >> PAGE_SHIFT);
+ if (n_pages >= 64) {
+ sh64_icache_inv_all();
+ } else {
+ unsigned long aligned_start;
+ unsigned long eaddr;
+ unsigned long after_last_page_start;
+ unsigned long mm_asid, current_asid;
+ unsigned long long flags = 0ULL;
+
+ mm_asid = mm->context & MMU_CONTEXT_ASID_MASK;
+ current_asid = get_asid();
+
+ if (mm_asid != current_asid) {
+ /* Switch ASID and run the invalidate loop under cli */
+ local_irq_save(flags);
+ switch_and_save_asid(mm_asid);
+ }
+
+ aligned_start = start & PAGE_MASK;
+ after_last_page_start = PAGE_SIZE + ((end - 1) & PAGE_MASK);
+
+ while (aligned_start < after_last_page_start) {
+ struct vm_area_struct *vma;
+ unsigned long vma_end;
+ vma = find_vma(mm, aligned_start);
+ if (!vma || (aligned_start <= vma->vm_end)) {
+ /* Avoid getting stuck in an error condition */
+ aligned_start += PAGE_SIZE;
+ continue;
+ }
+ vma_end = vma->vm_end;
+ if (vma->vm_flags & VM_EXEC) {
+ /* Executable */
+ eaddr = aligned_start;
+ while (eaddr < vma_end) {
+ sh64_icache_inv_user_page(vma, eaddr);
+ eaddr += PAGE_SIZE;
+ }
+ }
+ aligned_start = vma->vm_end; /* Skip to start of next region */
+ }
+ if (mm_asid != current_asid) {
+ switch_and_save_asid(current_asid);
+ local_irq_restore(flags);
+ }
+ }
+}
+
+static void sh64_icache_inv_user_small_range(struct mm_struct *mm,
+ unsigned long start, int len)
+{
+
+ /* Invalidate a small range of user context I-cache, not necessarily
+ page (or even cache-line) aligned. */
+
+ unsigned long long eaddr = start;
+ unsigned long long eaddr_end = start + len;
+ unsigned long current_asid, mm_asid;
+ unsigned long long flags;
+ unsigned long long epage_start;
+
+ /* Since this is used inside ptrace, the ASID in the mm context
+ typically won't match current_asid. We'll have to switch ASID to do
+ this. For safety, and given that the range will be small, do all
+ this under cli.
+
+ Note, there is a hazard that the ASID in mm->context is no longer
+ actually associated with mm, i.e. if the mm->context has started a
+ new cycle since mm was last active. However, this is just a
+ performance issue: all that happens is that we invalidate lines
+ belonging to another mm, so the owning process has to refill them
+ when that mm goes live again. mm itself can't have any cache
+ entries because there will have been a flush_cache_all when the new
+ mm->context cycle started. */
+
+ /* Align to start of cache line. Otherwise, suppose len==8 and start
+ was at 32N+28 : the last 4 bytes wouldn't get invalidated. */
+ eaddr = start & L1_CACHE_ALIGN_MASK;
+ eaddr_end = start + len;
+
+ local_irq_save(flags);
+ mm_asid = mm->context & MMU_CONTEXT_ASID_MASK;
+ current_asid = switch_and_save_asid(mm_asid);
+
+ epage_start = eaddr & PAGE_MASK;
+
+ while (eaddr < eaddr_end)
+ {
+ asm __volatile__("icbi %0, 0" : : "r" (eaddr));
+ eaddr += L1_CACHE_BYTES;
+ }
+ switch_and_save_asid(current_asid);
+ local_irq_restore(flags);
+}
+
+static void sh64_icache_inv_current_user_range(unsigned long start, unsigned long end)
+{
+ /* The icbi instruction never raises ITLBMISS. i.e. if there's not a
+ cache hit on the virtual tag the instruction ends there, without a
+ TLB lookup. */
+
+ unsigned long long aligned_start;
+ unsigned long long ull_end;
+ unsigned long long addr;
+
+ ull_end = end;
+
+ /* Just invalidate over the range using the natural addresses. TLB
+ miss handling will be OK (TBC). Since it's for the current process,
+ either we're already in the right ASID context, or the ASIDs have
+ been recycled since we were last active in which case we might just
+ invalidate another processes I-cache entries : no worries, just a
+ performance drop for him. */
+ aligned_start = start & L1_CACHE_ALIGN_MASK;
+ addr = aligned_start;
+ while (addr < ull_end) {
+ asm __volatile__ ("icbi %0, 0" : : "r" (addr));
+ asm __volatile__ ("nop");
+ asm __volatile__ ("nop");
+ addr += L1_CACHE_BYTES;
+ }
+}
+
+#endif /* !CONFIG_ICACHE_DISABLED */
+
+/****************************************************************************/
+
+#ifndef CONFIG_DCACHE_DISABLED
+
+/* Buffer used as the target of alloco instructions to purge data from cache
+ sets by natural eviction. -- RPC */
+#define DUMMY_ALLOCO_AREA_SIZE L1_CACHE_SIZE_BYTES + (1024 * 4)
+static unsigned char dummy_alloco_area[DUMMY_ALLOCO_AREA_SIZE] __cacheline_aligned = { 0, };
+
+/****************************************************************************/
+
+static void __inline__ sh64_dcache_purge_sets(int sets_to_purge_base, int n_sets)
+{
+ /* Purge all ways in a particular block of sets, specified by the base
+ set number and number of sets. Can handle wrap-around, if that's
+ needed. */
+
+ int dummy_buffer_base_set;
+ unsigned long long eaddr, eaddr0, eaddr1;
+ int j;
+ int set_offset;
+
+ dummy_buffer_base_set = ((int)&dummy_alloco_area & cpu_data->dcache.idx_mask) >> cpu_data->dcache.entry_shift;
+ set_offset = sets_to_purge_base - dummy_buffer_base_set;
+
+ for (j=0; j<n_sets; j++, set_offset++) {
+ set_offset &= (cpu_data->dcache.sets - 1);
+ eaddr0 = (unsigned long long)dummy_alloco_area + (set_offset << cpu_data->dcache.entry_shift);
+
+ /* Do one alloco which hits the required set per cache way. For
+ write-back mode, this will purge the #ways resident lines. There's
+ little point unrolling this loop because the allocos stall more if
+ they're too close together. */
+ eaddr1 = eaddr0 + cpu_data->dcache.way_ofs * cpu_data->dcache.ways;
+ for (eaddr=eaddr0; eaddr<eaddr1; eaddr+=cpu_data->dcache.way_ofs) {
+ asm __volatile__ ("alloco %0, 0" : : "r" (eaddr));
+ }
+
+ eaddr1 = eaddr0 + cpu_data->dcache.way_ofs * cpu_data->dcache.ways;
+ for (eaddr=eaddr0; eaddr<eaddr1; eaddr+=cpu_data->dcache.way_ofs) {
+ /* Load from each address. Required because alloco is a NOP if
+ the cache is write-through. Write-through is a config option. */
+ if (test_bit(SH_CACHE_MODE_WT, &(cpu_data->dcache.flags)))
+ *(volatile unsigned char *)(int)eaddr;
+ }
+ }
+
+ /* Don't use OCBI to invalidate the lines. That costs cycles directly.
+ If the dummy block is just left resident, it will naturally get
+ evicted as required. */
+
+ return;
+}
+
+/****************************************************************************/
+
+static void sh64_dcache_purge_all(void)
+{
+ /* Purge the entire contents of the dcache. The most efficient way to
+ achieve this is to use alloco instructions on a region of unused
+ memory equal in size to the cache, thereby causing the current
+ contents to be discarded by natural eviction. The alternative,
+ namely reading every tag, setting up a mapping for the corresponding
+ page and doing an OCBP for the line, would be much more expensive.
+ */
+
+ sh64_dcache_purge_sets(0, cpu_data->dcache.sets);
+
+ return;
+
+}
+
+/****************************************************************************/
+
+static void sh64_dcache_purge_kernel_range(unsigned long start, unsigned long end)
+{
+ /* Purge the range of addresses [start,end] from the D-cache. The
+ addresses lie in the superpage mapping. There's no harm if we
+ overpurge at either end - just a small performance loss. */
+ unsigned long long ullend, addr, aligned_start;
+#if (NEFF == 32)
+ aligned_start = (unsigned long long)(signed long long)(signed long) start;
+#else
+#error "NEFF != 32"
+#endif
+ aligned_start &= L1_CACHE_ALIGN_MASK;
+ addr = aligned_start;
+#if (NEFF == 32)
+ ullend = (unsigned long long) (signed long long) (signed long) end;
+#else
+#error "NEFF != 32"
+#endif
+ while (addr <= ullend) {
+ asm __volatile__ ("ocbp %0, 0" : : "r" (addr));
+ addr += L1_CACHE_BYTES;
+ }
+ return;
+}
+
+/* Assumes this address (+ (2**n_synbits) pages up from it) aren't used for
+ anything else in the kernel */
+#define MAGIC_PAGE0_START 0xffffffffec000000ULL
+
+static void sh64_dcache_purge_coloured_phy_page(unsigned long paddr, unsigned long eaddr)
+{
+ /* Purge the physical page 'paddr' from the cache. It's known that any
+ cache lines requiring attention have the same page colour as the the
+ address 'eaddr'.
+
+ This relies on the fact that the D-cache matches on physical tags
+ when no virtual tag matches. So we create an alias for the original
+ page and purge through that. (Alternatively, we could have done
+ this by switching ASID to match the original mapping and purged
+ through that, but that involves ASID switching cost + probably a
+ TLBMISS + refill anyway.)
+ */
+
+ unsigned long long magic_page_start;
+ unsigned long long magic_eaddr, magic_eaddr_end;
+
+ magic_page_start = MAGIC_PAGE0_START + (eaddr & CACHE_OC_SYN_MASK);
+
+ /* As long as the kernel is not pre-emptible, this doesn't need to be
+ under cli/sti. */
+
+ sh64_setup_dtlb_cache_slot(magic_page_start, get_asid(), paddr);
+
+ magic_eaddr = magic_page_start;
+ magic_eaddr_end = magic_eaddr + PAGE_SIZE;
+ while (magic_eaddr < magic_eaddr_end) {
+ /* Little point in unrolling this loop - the OCBPs are blocking
+ and won't go any quicker (i.e. the loop overhead is parallel
+ to part of the OCBP execution.) */
+ asm __volatile__ ("ocbp %0, 0" : : "r" (magic_eaddr));
+ magic_eaddr += L1_CACHE_BYTES;
+ }
+
+ sh64_teardown_dtlb_cache_slot();
+}
+
+/****************************************************************************/
+
+static void sh64_dcache_purge_phy_page(unsigned long paddr)
+{
+ /* Pure a page given its physical start address, by creating a
+ temporary 1 page mapping and purging across that. Even if we know
+ the virtual address (& vma or mm) of the page, the method here is
+ more elegant because it avoids issues of coping with page faults on
+ the purge instructions (i.e. no special-case code required in the
+ critical path in the TLB miss handling). */
+
+ unsigned long long eaddr_start, eaddr, eaddr_end;
+ int i;
+
+ /* As long as the kernel is not pre-emptible, this doesn't need to be
+ under cli/sti. */
+
+ eaddr_start = MAGIC_PAGE0_START;
+ for (i=0; i < (1 << CACHE_OC_N_SYNBITS); i++) {
+ sh64_setup_dtlb_cache_slot(eaddr_start, get_asid(), paddr);
+
+ eaddr = eaddr_start;
+ eaddr_end = eaddr + PAGE_SIZE;
+ while (eaddr < eaddr_end) {
+ asm __volatile__ ("ocbp %0, 0" : : "r" (eaddr));
+ eaddr += L1_CACHE_BYTES;
+ }
+
+ sh64_teardown_dtlb_cache_slot();
+ eaddr_start += PAGE_SIZE;
+ }
+}
+
+static void sh64_dcache_purge_virt_page(struct mm_struct *mm, unsigned long eaddr)
+{
+ unsigned long phys;
+ pgd_t *pgd;
+ pmd_t *pmd;
+ pte_t *pte;
+ pte_t entry;
+
+ pgd = pgd_offset(mm, eaddr);
+ pmd = pmd_offset(pgd, eaddr);
+
+ if (pmd_none(*pmd) || pmd_bad(*pmd))
+ return;
+
+ pte = pte_offset_kernel(pmd, eaddr);
+ entry = *pte;
+
+ if (pte_none(entry) || !pte_present(entry))
+ return;
+
+ phys = pte_val(entry) & PAGE_MASK;
+
+ sh64_dcache_purge_phy_page(phys);
+}
+
+static void sh64_dcache_purge_user_page(struct mm_struct *mm, unsigned long eaddr)
+{
+ pgd_t *pgd;
+ pmd_t *pmd;
+ pte_t *pte;
+ pte_t entry;
+ unsigned long paddr;
+
+ /* NOTE : all the callers of this have mm->page_table_lock held, so the
+ following page table traversal is safe even on SMP/pre-emptible. */
+
+ if (!mm) return; /* No way to find physical address of page */
+ pgd = pgd_offset(mm, eaddr);
+ if (pgd_bad(*pgd)) return;
+
+ pmd = pmd_offset(pgd, eaddr);
+ if (pmd_none(*pmd) || pmd_bad(*pmd)) return;
+
+ pte = pte_offset_kernel(pmd, eaddr);
+ entry = *pte;
+ if (pte_none(entry) || !pte_present(entry)) return;
+
+ paddr = pte_val(entry) & PAGE_MASK;
+
+ sh64_dcache_purge_coloured_phy_page(paddr, eaddr);
+
+}
+/****************************************************************************/
+
+static void sh64_dcache_purge_user_range(struct mm_struct *mm,
+ unsigned long start, unsigned long end)
+{
+ /* There are at least 5 choices for the implementation of this, with
+ pros (+), cons(-), comments(*):
+
+ 1. ocbp each line in the range through the original user's ASID
+ + no lines spuriously evicted
+ - tlbmiss handling (must either handle faults on demand => extra
+ special-case code in tlbmiss critical path), or map the page in
+ advance (=> flush_tlb_range in advance to avoid multiple hits)
+ - ASID switching
+ - expensive for large ranges
+
+ 2. temporarily map each page in the range to a special effective
+ address and ocbp through the temporary mapping; relies on the
+ fact that SH-5 OCB* always do TLB lookup and match on ptags (they
+ never look at the etags)
+ + no spurious evictions
+ - expensive for large ranges
+ * surely cheaper than (1)
+
+ 3. walk all the lines in the cache, check the tags, if a match
+ occurs create a page mapping to ocbp the line through
+ + no spurious evictions
+ - tag inspection overhead
+ - (especially for small ranges)
+ - potential cost of setting up/tearing down page mapping for
+ every line that matches the range
+ * cost partly independent of range size
+
+ 4. walk all the lines in the cache, check the tags, if a match
+ occurs use 4 * alloco to purge the line (+3 other probably
+ innocent victims) by natural eviction
+ + no tlb mapping overheads
+ - spurious evictions
+ - tag inspection overhead
+
+ 5. implement like flush_cache_all
+ + no tag inspection overhead
+ - spurious evictions
+ - bad for small ranges
+
+ (1) can be ruled out as more expensive than (2). (2) appears best
+ for small ranges. The choice between (3), (4) and (5) for large
+ ranges and the range size for the large/small boundary need
+ benchmarking to determine.
+
+ For now use approach (2) for small ranges and (5) for large ones.
+
+ */
+
+ int n_pages;
+
+ n_pages = ((end - start) >> PAGE_SHIFT);
+ if (n_pages >= 64) {
+#if 1
+ sh64_dcache_purge_all();
+#else
+ unsigned long long set, way;
+ unsigned long mm_asid = mm->context & MMU_CONTEXT_ASID_MASK;
+ for (set = 0; set < cpu_data->dcache.sets; set++) {
+ unsigned long long set_base_config_addr = CACHE_OC_ADDRESS_ARRAY + (set << cpu_data->dcache.set_shift);
+ for (way = 0; way < cpu_data->dcache.ways; way++) {
+ unsigned long long config_addr = set_base_config_addr + (way << cpu_data->dcache.way_step_shift);
+ unsigned long long tag0;
+ unsigned long line_valid;
+
+ asm __volatile__("getcfg %1, 0, %0" : "=r" (tag0) : "r" (config_addr));
+ line_valid = tag0 & SH_CACHE_VALID;
+ if (line_valid) {
+ unsigned long cache_asid;
+ unsigned long epn;
+
+ cache_asid = (tag0 & cpu_data->dcache.asid_mask) >> cpu_data->dcache.asid_shift;
+ /* The next line needs some
+ explanation. The virtual tags
+ encode bits [31:13] of the virtual
+ address, bit [12] of the 'tag' being
+ implied by the cache set index. */
+ epn = (tag0 & cpu_data->dcache.epn_mask) | ((set & 0x80) << cpu_data->dcache.entry_shift);
+
+ if ((cache_asid == mm_asid) && (start <= epn) && (epn < end)) {
+ /* TODO : could optimise this
+ call by batching multiple
+ adjacent sets together. */
+ sh64_dcache_purge_sets(set, 1);
+ break; /* Don't waste time inspecting other ways for this set */
+ }
+ }
+ }
+ }
+#endif
+ } else {
+ /* 'Small' range */
+ unsigned long aligned_start;
+ unsigned long eaddr;
+ unsigned long last_page_start;
+
+ aligned_start = start & PAGE_MASK;
+ /* 'end' is 1 byte beyond the end of the range */
+ last_page_start = (end - 1) & PAGE_MASK;
+
+ eaddr = aligned_start;
+ while (eaddr <= last_page_start) {
+ sh64_dcache_purge_user_page(mm, eaddr);
+ eaddr += PAGE_SIZE;
+ }
+ }
+ return;
+}
+
+static void sh64_dcache_wback_current_user_range(unsigned long start, unsigned long end)
+{
+ unsigned long long aligned_start;
+ unsigned long long ull_end;
+ unsigned long long addr;
+
+ ull_end = end;
+
+ /* Just wback over the range using the natural addresses. TLB miss
+ handling will be OK (TBC) : the range has just been written to by
+ the signal frame setup code, so the PTEs must exist.
+
+ Note, if we have CONFIG_PREEMPT and get preempted inside this loop,
+ it doesn't matter, even if the pid->ASID mapping changes whilst
+ we're away. In that case the cache will have been flushed when the
+ mapping was renewed. So the writebacks below will be nugatory (and
+ we'll doubtless have to fault the TLB entry/ies in again with the
+ new ASID), but it's a rare case.
+ */
+ aligned_start = start & L1_CACHE_ALIGN_MASK;
+ addr = aligned_start;
+ while (addr < ull_end) {
+ asm __volatile__ ("ocbwb %0, 0" : : "r" (addr));
+ addr += L1_CACHE_BYTES;
+ }
+}
+
+#endif /* !CONFIG_DCACHE_DISABLED */
+
+/****************************************************************************/
+
+/* These *MUST* lie in an area of virtual address space that's otherwise unused. */
+#define UNIQUE_EADDR_START 0xe0000000UL
+#define UNIQUE_EADDR_END 0xe8000000UL
+
+static unsigned long sh64_make_unique_eaddr(unsigned long user_eaddr, unsigned long paddr)
+{
+ /* Given a physical address paddr, and a user virtual address
+ user_eaddr which will eventually be mapped to it, create a one-off
+ kernel-private eaddr mapped to the same paddr. This is used for
+ creating special destination pages for copy_user_page and
+ clear_user_page */
+
+ static unsigned long current_pointer = UNIQUE_EADDR_START;
+ unsigned long coloured_pointer;
+
+ if (current_pointer == UNIQUE_EADDR_END) {
+ sh64_dcache_purge_all();
+ current_pointer = UNIQUE_EADDR_START;
+ }
+
+ coloured_pointer = (current_pointer & ~CACHE_OC_SYN_MASK) | (user_eaddr & CACHE_OC_SYN_MASK);
+ sh64_setup_dtlb_cache_slot(coloured_pointer, get_asid(), paddr);
+
+ current_pointer += (PAGE_SIZE << CACHE_OC_N_SYNBITS);
+
+ return coloured_pointer;
+}
+
+/****************************************************************************/
+
+static void sh64_copy_user_page_coloured(void *to, void *from, unsigned long address)
+{
+ void *coloured_to;
+
+ /* Discard any existing cache entries of the wrong colour. These are
+ present quite often, if the kernel has recently used the page
+ internally, then given it up, then it's been allocated to the user.
+ */
+ sh64_dcache_purge_coloured_phy_page(__pa(to), (unsigned long) to);
+
+ coloured_to = (void *) sh64_make_unique_eaddr(address, __pa(to));
+ sh64_page_copy(from, coloured_to);
+
+ sh64_teardown_dtlb_cache_slot();
+}
+
+static void sh64_clear_user_page_coloured(void *to, unsigned long address)
+{
+ void *coloured_to;
+
+ /* Discard any existing kernel-originated lines of the wrong colour (as
+ above) */
+ sh64_dcache_purge_coloured_phy_page(__pa(to), (unsigned long) to);
+
+ coloured_to = (void *) sh64_make_unique_eaddr(address, __pa(to));
+ sh64_page_clear(coloured_to);
+
+ sh64_teardown_dtlb_cache_slot();
+}
+
+/****************************************************************************/
+
+/*##########################################################################
+ EXTERNALLY CALLABLE API.
+ ##########################################################################*/
+
+/* These functions are described in Documentation/cachetlb.txt.
+ Each one of these functions varies in behaviour depending on whether the
+ I-cache and/or D-cache are configured out.
+
+ Note that the Linux term 'flush' corresponds to what is termed 'purge' in
+ the sh/sh64 jargon for the D-cache, i.e. write back dirty data then
+ invalidate the cache lines, and 'invalidate' for the I-cache.
+ */
+
+#undef FLUSH_TRACE
+
+void flush_cache_all(void)
+{
+ /* Invalidate the entire contents of both caches, after writing back to
+ memory any dirty data from the D-cache. */
+ sh64_dcache_purge_all();
+ sh64_icache_inv_all();
+}
+
+/****************************************************************************/
+
+void flush_cache_mm(struct mm_struct *mm)
+{
+ /* Invalidate an entire user-address space from both caches, after
+ writing back dirty data (e.g. for shared mmap etc). */
+
+ /* This could be coded selectively by inspecting all the tags then
+ doing 4*alloco on any set containing a match (as for
+ flush_cache_range), but fork/exit/execve (where this is called from)
+ are expensive anyway. */
+
+ /* Have to do a purge here, despite the comments re I-cache below.
+ There could be odd-coloured dirty data associated with the mm still
+ in the cache - if this gets written out through natural eviction
+ after the kernel has reused the page there will be chaos.
+ */
+
+ sh64_dcache_purge_all();
+
+ /* The mm being torn down won't ever be active again, so any Icache
+ lines tagged with its ASID won't be visible for the rest of the
+ lifetime of this ASID cycle. Before the ASID gets reused, there
+ will be a flush_cache_all. Hence we don't need to touch the
+ I-cache. This is similar to the lack of action needed in
+ flush_tlb_mm - see fault.c. */
+}
+
+/****************************************************************************/
+
+void flush_cache_range(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end)
+{
+ struct mm_struct *mm = vma->vm_mm;
+
+ /* Invalidate (from both caches) the range [start,end) of virtual
+ addresses from the user address space specified by mm, after writing
+ back any dirty data.
+
+ Note(1), 'end' is 1 byte beyond the end of the range to flush.
+
+ Note(2), this is called with mm->page_table_lock held.*/
+
+ sh64_dcache_purge_user_range(mm, start, end);
+ sh64_icache_inv_user_page_range(mm, start, end);
+}
+
+/****************************************************************************/
+
+void flush_cache_page(struct vm_area_struct *vma, unsigned long eaddr)
+{
+ /* Invalidate any entries in either cache for the vma within the user
+ address space vma->vm_mm for the page starting at virtual address
+ 'eaddr'. This seems to be used primarily in breaking COW. Note,
+ the I-cache must be searched too in case the page in question is
+ both writable and being executed from (e.g. stack trampolines.)
+
+ Note(1), this is called with mm->page_table_lock held.
+ */
+
+ sh64_dcache_purge_virt_page(vma->vm_mm, eaddr);
+
+ if (vma->vm_flags & VM_EXEC) {
+ sh64_icache_inv_user_page(vma, eaddr);
+ }
+}
+
+/****************************************************************************/
+
+#ifndef CONFIG_DCACHE_DISABLED
+
+void copy_user_page(void *to, void *from, unsigned long address, struct page *page)
+{
+ /* 'from' and 'to' are kernel virtual addresses (within the superpage
+ mapping of the physical RAM). 'address' is the user virtual address
+ where the copy 'to' will be mapped after. This allows a custom
+ mapping to be used to ensure that the new copy is placed in the
+ right cache sets for the user to see it without having to bounce it
+ out via memory. Note however : the call to flush_page_to_ram in
+ (generic)/mm/memory.c:(break_cow) undoes all this good work in that one
+ very important case!
+
+ TBD : can we guarantee that on every call, any cache entries for
+ 'from' are in the same colour sets as 'address' also? i.e. is this
+ always used just to deal with COW? (I suspect not). */
+
+ /* There are two possibilities here for when the page 'from' was last accessed:
+ * by the kernel : this is OK, no purge required.
+ * by the/a user (e.g. for break_COW) : need to purge.
+
+ If the potential user mapping at 'address' is the same colour as
+ 'from' there is no need to purge any cache lines from the 'from'
+ page mapped into cache sets of colour 'address'. (The copy will be
+ accessing the page through 'from').
+ */
+
+ if (((address ^ (unsigned long) from) & CACHE_OC_SYN_MASK) != 0) {
+ sh64_dcache_purge_coloured_phy_page(__pa(from), address);
+ }
+
+ if (((address ^ (unsigned long) to) & CACHE_OC_SYN_MASK) == 0) {
+ /* No synonym problem on destination */
+ sh64_page_copy(from, to);
+ } else {
+ sh64_copy_user_page_coloured(to, from, address);
+ }
+
+ /* Note, don't need to flush 'from' page from the cache again - it's
+ done anyway by the generic code */
+}
+
+void clear_user_page(void *to, unsigned long address, struct page *page)
+{
+ /* 'to' is a kernel virtual address (within the superpage
+ mapping of the physical RAM). 'address' is the user virtual address
+ where the 'to' page will be mapped after. This allows a custom
+ mapping to be used to ensure that the new copy is placed in the
+ right cache sets for the user to see it without having to bounce it
+ out via memory.
+ */
+
+ if (((address ^ (unsigned long) to) & CACHE_OC_SYN_MASK) == 0) {
+ /* No synonym problem on destination */
+ sh64_page_clear(to);
+ } else {
+ sh64_clear_user_page_coloured(to, address);
+ }
+}
+
+#endif /* !CONFIG_DCACHE_DISABLED */
+
+/****************************************************************************/
+
+void flush_dcache_page(struct page *page)
+{
+ sh64_dcache_purge_phy_page(page_to_phys(page));
+ wmb();
+}
+
+/****************************************************************************/
+
+void flush_icache_range(unsigned long start, unsigned long end)
+{
+ /* Flush the range [start,end] of kernel virtual adddress space from
+ the I-cache. The corresponding range must be purged from the
+ D-cache also because the SH-5 doesn't have cache snooping between
+ the caches. The addresses will be visible through the superpage
+ mapping, therefore it's guaranteed that there no cache entries for
+ the range in cache sets of the wrong colour.
+
+ Primarily used for cohering the I-cache after a module has
+ been loaded. */
+
+ /* We also make sure to purge the same range from the D-cache since
+ flush_page_to_ram() won't be doing this for us! */
+
+ sh64_dcache_purge_kernel_range(start, end);
+ wmb();
+ sh64_icache_inv_kernel_range(start, end);
+}
+
+/****************************************************************************/
+
+void flush_icache_user_range(struct vm_area_struct *vma,
+ struct page *page, unsigned long addr, int len)
+{
+ /* Flush the range of user (defined by vma->vm_mm) address space
+ starting at 'addr' for 'len' bytes from the cache. The range does
+ not straddle a page boundary, the unique physical page containing
+ the range is 'page'. This seems to be used mainly for invalidating
+ an address range following a poke into the program text through the
+ ptrace() call from another process (e.g. for BRK instruction
+ insertion). */
+
+ sh64_dcache_purge_coloured_phy_page(page_to_phys(page), addr);
+ mb();
+
+ if (vma->vm_flags & VM_EXEC) {
+ sh64_icache_inv_user_small_range(vma->vm_mm, addr, len);
+ }
+}
+
+/*##########################################################################
+ ARCH/SH64 PRIVATE CALLABLE API.
+ ##########################################################################*/
+
+void flush_cache_sigtramp(unsigned long start, unsigned long end)
+{
+ /* For the address range [start,end), write back the data from the
+ D-cache and invalidate the corresponding region of the I-cache for
+ the current process. Used to flush signal trampolines on the stack
+ to make them executable. */
+
+ sh64_dcache_wback_current_user_range(start, end);
+ wmb();
+ sh64_icache_inv_current_user_range(start, end);
+}
+
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/mm/extable.c
+ *
+ * Copyright (C) 2003 Richard Curnow
+ * Copyright (C) 2003, 2004 Paul Mundt
+ *
+ * Cloned from the 2.5 SH version..
+ */
+#include <linux/config.h>
+#include <linux/rwsem.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+
+extern unsigned long copy_user_memcpy, copy_user_memcpy_end, __copy_user_fixup;
+
+static const struct exception_table_entry __copy_user_fixup_ex = {
+ .fixup = (unsigned long)&__copy_user_fixup,
+};
+
+/* Some functions that may trap due to a bad user-mode address have too many loads
+ and stores in them to make it at all practical to label each one and put them all in
+ the main exception table.
+
+ In particular, the fast memcpy routine is like this. It's fix-up is just to fall back
+ to a slow byte-at-a-time copy, which is handled the conventional way. So it's functionally
+ OK to just handle any trap occurring in the fast memcpy with that fixup. */
+static const struct exception_table_entry *check_exception_ranges(unsigned long addr)
+{
+ if ((addr >= (unsigned long)©_user_memcpy) &&
+ (addr <= (unsigned long)©_user_memcpy_end))
+ return &__copy_user_fixup_ex;
+
+ return NULL;
+}
+
+/* Simple binary search */
+const struct exception_table_entry *
+search_extable(const struct exception_table_entry *first,
+ const struct exception_table_entry *last,
+ unsigned long value)
+{
+ const struct exception_table_entry *mid;
+
+ mid = check_exception_ranges(value);
+ if (mid)
+ return mid;
+
+ while (first <= last) {
+ long diff;
+
+ mid = (last - first) / 2 + first;
+ diff = mid->insn - value;
+ if (diff == 0)
+ return mid;
+ else if (diff < 0)
+ first = mid+1;
+ else
+ last = mid-1;
+ }
+
+ return NULL;
+}
+
+int fixup_exception(struct pt_regs *regs)
+{
+ const struct exception_table_entry *fixup;
+
+ fixup = search_exception_tables(regs->pc);
+ if (fixup) {
+ regs->pc = fixup->fixup;
+ return 1;
+ }
+
+ return 0;
+}
+
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/mm/fault.c
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003 Richard Curnow (/proc/tlb, bug fixes)
+ * Copyright (C) 2003 Paul Mundt
+ *
+ */
+
+#include <linux/signal.h>
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/tlb.h>
+#include <asm/uaccess.h>
+#include <asm/pgalloc.h>
+#include <asm/hardirq.h>
+#include <asm/mmu_context.h>
+#include <asm/registers.h> /* required by inline asm statements */
+
+#if defined(CONFIG_SH64_PROC_TLB)
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+/* Count numbers of tlb refills in each region */
+static unsigned long long calls_to_update_mmu_cache = 0ULL;
+static unsigned long long calls_to_flush_tlb_page = 0ULL;
+static unsigned long long calls_to_flush_tlb_range = 0ULL;
+static unsigned long long calls_to_flush_tlb_mm = 0ULL;
+static unsigned long long calls_to_flush_tlb_all = 0ULL;
+unsigned long long calls_to_do_slow_page_fault = 0ULL;
+unsigned long long calls_to_do_fast_page_fault = 0ULL;
+
+/* Count size of ranges for flush_tlb_range */
+static unsigned long long flush_tlb_range_1 = 0ULL;
+static unsigned long long flush_tlb_range_2 = 0ULL;
+static unsigned long long flush_tlb_range_3_4 = 0ULL;
+static unsigned long long flush_tlb_range_5_7 = 0ULL;
+static unsigned long long flush_tlb_range_8_11 = 0ULL;
+static unsigned long long flush_tlb_range_12_15 = 0ULL;
+static unsigned long long flush_tlb_range_16_up = 0ULL;
+
+static unsigned long long page_not_present = 0ULL;
+
+#endif
+
+extern void die(const char *,struct pt_regs *,long);
+
+#define PFLAG(val,flag) (( (val) & (flag) ) ? #flag : "" )
+#define PPROT(flag) PFLAG(pgprot_val(prot),flag)
+
+static inline void print_prots(pgprot_t prot)
+{
+ printk("prot is 0x%08lx\n",pgprot_val(prot));
+
+ printk("%s %s %s %s %s\n",PPROT(_PAGE_SHARED),PPROT(_PAGE_READ),
+ PPROT(_PAGE_EXECUTE),PPROT(_PAGE_WRITE),PPROT(_PAGE_USER));
+}
+
+static inline void print_vma(struct vm_area_struct *vma)
+{
+ printk("vma start 0x%08lx\n", vma->vm_start);
+ printk("vma end 0x%08lx\n", vma->vm_end);
+
+ print_prots(vma->vm_page_prot);
+ printk("vm_flags 0x%08lx\n", vma->vm_flags);
+}
+
+static inline void print_task(struct task_struct *tsk)
+{
+ printk("Task pid %d\n", tsk->pid);
+}
+
+static pte_t *lookup_pte(struct mm_struct *mm, unsigned long address)
+{
+ pgd_t *dir;
+ pmd_t *pmd;
+ pte_t *pte;
+ pte_t entry;
+
+ dir = pgd_offset(mm, address);
+ if (pgd_none(*dir)) {
+ return NULL;
+ }
+
+ pmd = pmd_offset(dir, address);
+ if (pmd_none(*pmd)) {
+ return NULL;
+ }
+
+ pte = pte_offset_kernel(pmd, address);
+ entry = *pte;
+
+ if (pte_none(entry)) {
+ return NULL;
+ }
+ if (!pte_present(entry)) {
+ return NULL;
+ }
+
+ return pte;
+}
+
+/*
+ * This routine handles page faults. It determines the address,
+ * and the problem, and then passes it off to one of the appropriate
+ * routines.
+ */
+asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long writeaccess,
+ unsigned long textaccess, unsigned long address)
+{
+ struct task_struct *tsk;
+ struct mm_struct *mm;
+ struct vm_area_struct * vma;
+ const struct exception_table_entry *fixup;
+ pte_t *pte;
+
+#if defined(CONFIG_SH64_PROC_TLB)
+ ++calls_to_do_slow_page_fault;
+#endif
+
+ /* SIM
+ * Note this is now called with interrupts still disabled
+ * This is to cope with being called for a missing IO port
+ * address with interupts disabled. This should be fixed as
+ * soon as we have a better 'fast path' miss handler.
+ *
+ * Plus take care how you try and debug this stuff.
+ * For example, writing debug data to a port which you
+ * have just faulted on is not going to work.
+ */
+
+ tsk = current;
+ mm = tsk->mm;
+
+ /* Not an IO address, so reenable interrupts */
+ sti();
+
+ /*
+ * If we're in an interrupt or have no user
+ * context, we must not take the fault..
+ */
+ if (in_interrupt() || !mm)
+ goto no_context;
+
+ /* TLB misses upon some cache flushes get done under cli() */
+ down_read(&mm->mmap_sem);
+
+ vma = find_vma(mm, address);
+
+ if (!vma) {
+#ifdef DEBUG_FAULT
+ print_task(tsk);
+ printk("%s:%d fault, address is 0x%08x PC %016Lx textaccess %d writeaccess %d\n",
+ __FUNCTION__,__LINE__,
+ address,regs->pc,textaccess,writeaccess);
+ show_regs(regs);
+#endif
+ goto bad_area;
+ }
+ if (vma->vm_start <= address) {
+ goto good_area;
+ }
+
+ if (!(vma->vm_flags & VM_GROWSDOWN)) {
+#ifdef DEBUG_FAULT
+ print_task(tsk);
+ printk("%s:%d fault, address is 0x%08x PC %016Lx textaccess %d writeaccess %d\n",
+ __FUNCTION__,__LINE__,
+ address,regs->pc,textaccess,writeaccess);
+ show_regs(regs);
+
+ print_vma(vma);
+#endif
+ goto bad_area;
+ }
+ if (expand_stack(vma, address)) {
+#ifdef DEBUG_FAULT
+ print_task(tsk);
+ printk("%s:%d fault, address is 0x%08x PC %016Lx textaccess %d writeaccess %d\n",
+ __FUNCTION__,__LINE__,
+ address,regs->pc,textaccess,writeaccess);
+ show_regs(regs);
+#endif
+ goto bad_area;
+ }
+/*
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
+ */
+good_area:
+ if (writeaccess) {
+ if (!(vma->vm_flags & VM_WRITE))
+ goto bad_area;
+ } else {
+ if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
+ goto bad_area;
+ }
+
+ if (textaccess) {
+ if (!(vma->vm_flags & VM_EXEC))
+ goto bad_area;
+ }
+
+ /*
+ * If for any reason at all we couldn't handle the fault,
+ * make sure we exit gracefully rather than endlessly redo
+ * the fault.
+ */
+survive:
+ switch (handle_mm_fault(mm, vma, address, writeaccess)) {
+ case 1:
+ tsk->min_flt++;
+ break;
+ case 2:
+ tsk->maj_flt++;
+ break;
+ case 0:
+ goto do_sigbus;
+ default:
+ goto out_of_memory;
+ }
+ /* If we get here, the page fault has been handled. Do the TLB refill
+ now from the newly-setup PTE, to avoid having to fault again right
+ away on the same instruction. */
+ pte = lookup_pte (mm, address);
+ if (!pte) {
+ /* From empirical evidence, we can get here, due to
+ !pte_present(pte). (e.g. if a swap-in occurs, and the page
+ is swapped back out again before the process that wanted it
+ gets rescheduled?) */
+ goto no_pte;
+ }
+
+ __do_tlb_refill(address, textaccess, pte);
+
+no_pte:
+
+ up_read(&mm->mmap_sem);
+ return;
+
+/*
+ * Something tried to access memory that isn't in our memory map..
+ * Fix it, but check if it's kernel or user first..
+ */
+bad_area:
+#ifdef DEBUG_FAULT
+ printk("fault:bad area\n");
+#endif
+ up_read(&mm->mmap_sem);
+
+ if (user_mode(regs)) {
+ printk("user mode bad_area address=%08lx pid=%d (%s) pc=%08lx opcode=%08lx\n",
+ address, current->pid, current->comm,
+ (unsigned long) regs->pc,
+ *(unsigned long *)(u32)(regs->pc & ~3));
+ show_regs(regs);
+ if (tsk->pid == 1) {
+ panic("INIT had user mode bad_area\n");
+ }
+ tsk->thread.address = address;
+ tsk->thread.error_code = writeaccess;
+ force_sig(SIGSEGV, tsk);
+ return;
+ }
+
+no_context:
+#ifdef DEBUG_FAULT
+ printk("fault:No context\n");
+#endif
+ /* Are we prepared to handle this kernel fault? */
+ fixup = search_exception_tables(regs->pc);
+ if (fixup) {
+ regs->pc = fixup->fixup;
+ return;
+ }
+
+/*
+ * Oops. The kernel tried to access some bad page. We'll have to
+ * terminate things with extreme prejudice.
+ *
+ */
+ if (address < PAGE_SIZE)
+ printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
+ else
+ printk(KERN_ALERT "Unable to handle kernel paging request");
+ printk(" at virtual address %08lx\n", address);
+ printk(KERN_ALERT "pc = %08Lx%08Lx\n", regs->pc >> 32, regs->pc & 0xffffffff);
+ die("Oops", regs, writeaccess);
+ do_exit(SIGKILL);
+
+/*
+ * We ran out of memory, or some other thing happened to us that made
+ * us unable to handle the page fault gracefully.
+ */
+out_of_memory:
+ if (current->pid == 1) {
+ panic("INIT out of memory\n");
+ yield();
+ goto survive;
+ }
+ printk("fault:Out of memory\n");
+ up_read(&mm->mmap_sem);
+ if (current->pid == 1) {
+ yield();
+ down_read(&mm->mmap_sem);
+ goto survive;
+ }
+ printk("VM: killing process %s\n", tsk->comm);
+ if (user_mode(regs))
+ do_exit(SIGKILL);
+ goto no_context;
+
+do_sigbus:
+ printk("fault:Do sigbus\n");
+ up_read(&mm->mmap_sem);
+
+ /*
+ * Send a sigbus, regardless of whether we were in kernel
+ * or user mode.
+ */
+ tsk->thread.address = address;
+ tsk->thread.error_code = writeaccess;
+ tsk->thread.trap_no = 14;
+ force_sig(SIGBUS, tsk);
+
+ /* Kernel mode? Handle exceptions or die */
+ if (!user_mode(regs))
+ goto no_context;
+}
+
+
+void flush_tlb_all(void);
+
+void update_mmu_cache(struct vm_area_struct * vma,
+ unsigned long address, pte_t pte)
+{
+#if defined(CONFIG_SH64_PROC_TLB)
+ ++calls_to_update_mmu_cache;
+#endif
+
+ /*
+ * This appears to get called once for every pte entry that gets
+ * established => I don't think it's efficient to try refilling the
+ * TLBs with the pages - some may not get accessed even. Also, for
+ * executable pages, it is impossible to determine reliably here which
+ * TLB they should be mapped into (or both even).
+ *
+ * So, just do nothing here and handle faults on demand. In the
+ * TLBMISS handling case, the refill is now done anyway after the pte
+ * has been fixed up, so that deals with most useful cases.
+ */
+}
+
+static void __flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+ unsigned long long match, pteh=0, lpage;
+ unsigned long tlb;
+ struct mm_struct *mm;
+
+ mm = vma->vm_mm;
+
+ if (mm->context == NO_CONTEXT)
+ return;
+
+ /*
+ * Sign-extend based on neff.
+ */
+ lpage = (page & NEFF_SIGN) ? (page | NEFF_MASK) : page;
+ match = ((mm->context & MMU_CONTEXT_ASID_MASK) << PTEH_ASID_SHIFT) | PTEH_VALID;
+ match |= lpage;
+
+ /* Do ITLB : don't bother for pages in non-exectutable VMAs */
+ if (vma->vm_flags & VM_EXEC) {
+ for_each_itlb_entry(tlb) {
+ asm volatile ("getcfg %1, 0, %0"
+ : "=r" (pteh)
+ : "r" (tlb) );
+
+ if (pteh == match) {
+ __flush_tlb_slot(tlb);
+ break;
+ }
+
+ }
+ }
+
+ /* Do DTLB : any page could potentially be in here. */
+ for_each_dtlb_entry(tlb) {
+ asm volatile ("getcfg %1, 0, %0"
+ : "=r" (pteh)
+ : "r" (tlb) );
+
+ if (pteh == match) {
+ __flush_tlb_slot(tlb);
+ break;
+ }
+
+ }
+}
+
+void flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+ unsigned long flags;
+
+#if defined(CONFIG_SH64_PROC_TLB)
+ ++calls_to_flush_tlb_page;
+#endif
+
+ if (vma->vm_mm) {
+ page &= PAGE_MASK;
+ local_irq_save(flags);
+ __flush_tlb_page(vma, page);
+ local_irq_restore(flags);
+ }
+}
+
+void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end)
+{
+ unsigned long flags;
+ unsigned long long match, pteh=0, pteh_epn, pteh_low;
+ unsigned long tlb;
+ struct mm_struct *mm;
+
+ mm = vma->vm_mm;
+
+#if defined(CONFIG_SH64_PROC_TLB)
+ ++calls_to_flush_tlb_range;
+
+ {
+ unsigned long size = (end - 1) - start;
+ size >>= 12; /* divide by PAGE_SIZE */
+ size++; /* end=start+4096 => 1 page */
+ switch (size) {
+ case 1 : flush_tlb_range_1++; break;
+ case 2 : flush_tlb_range_2++; break;
+ case 3 ... 4 : flush_tlb_range_3_4++; break;
+ case 5 ... 7 : flush_tlb_range_5_7++; break;
+ case 8 ... 11 : flush_tlb_range_8_11++; break;
+ case 12 ... 15 : flush_tlb_range_12_15++; break;
+ default : flush_tlb_range_16_up++; break;
+ }
+ }
+#endif
+
+ if (mm->context == NO_CONTEXT)
+ return;
+
+ local_irq_save(flags);
+
+ start &= PAGE_MASK;
+ end &= PAGE_MASK;
+
+ match = ((mm->context & MMU_CONTEXT_ASID_MASK) << PTEH_ASID_SHIFT) | PTEH_VALID;
+
+ /* Flush ITLB */
+ for_each_itlb_entry(tlb) {
+ asm volatile ("getcfg %1, 0, %0"
+ : "=r" (pteh)
+ : "r" (tlb) );
+
+ pteh_epn = pteh & PAGE_MASK;
+ pteh_low = pteh & ~PAGE_MASK;
+
+ if (pteh_low == match && pteh_epn >= start && pteh_epn <= end)
+ __flush_tlb_slot(tlb);
+ }
+
+ /* Flush DTLB */
+ for_each_dtlb_entry(tlb) {
+ asm volatile ("getcfg %1, 0, %0"
+ : "=r" (pteh)
+ : "r" (tlb) );
+
+ pteh_epn = pteh & PAGE_MASK;
+ pteh_low = pteh & ~PAGE_MASK;
+
+ if (pteh_low == match && pteh_epn >= start && pteh_epn <= end)
+ __flush_tlb_slot(tlb);
+ }
+
+ local_irq_restore(flags);
+}
+
+void flush_tlb_mm(struct mm_struct *mm)
+{
+ unsigned long flags;
+
+#if defined(CONFIG_SH64_PROC_TLB)
+ ++calls_to_flush_tlb_mm;
+#endif
+
+ if (mm->context == NO_CONTEXT)
+ return;
+
+ local_irq_save(flags);
+
+ mm->context=NO_CONTEXT;
+ if(mm==current->mm)
+ activate_context(mm);
+
+ local_irq_restore(flags);
+
+}
+
+void flush_tlb_all(void)
+{
+ /* Invalidate all, including shared pages, excluding fixed TLBs */
+
+ unsigned long flags, tlb;
+
+#if defined(CONFIG_SH64_PROC_TLB)
+ ++calls_to_flush_tlb_all;
+#endif
+
+ local_irq_save(flags);
+
+ /* Flush each ITLB entry */
+ for_each_itlb_entry(tlb) {
+ __flush_tlb_slot(tlb);
+ }
+
+ /* Flush each DTLB entry */
+ for_each_dtlb_entry(tlb) {
+ __flush_tlb_slot(tlb);
+ }
+
+ local_irq_restore(flags);
+}
+
+void flush_tlb_kernel_range(unsigned long start, unsigned long end)
+{
+ /* FIXME: Optimize this later.. */
+ flush_tlb_all();
+}
+
+#if defined(CONFIG_SH64_PROC_TLB)
+/* Procfs interface to read the performance information */
+
+static int
+tlb_proc_info(char *buf, char **start, off_t fpos, int length, int *eof, void *data)
+{
+ int len=0;
+ len += sprintf(buf+len, "do_fast_page_fault called %12lld times\n", calls_to_do_fast_page_fault);
+ len += sprintf(buf+len, "do_slow_page_fault called %12lld times\n", calls_to_do_slow_page_fault);
+ len += sprintf(buf+len, "update_mmu_cache called %12lld times\n", calls_to_update_mmu_cache);
+ len += sprintf(buf+len, "flush_tlb_page called %12lld times\n", calls_to_flush_tlb_page);
+ len += sprintf(buf+len, "flush_tlb_range called %12lld times\n", calls_to_flush_tlb_range);
+ len += sprintf(buf+len, "flush_tlb_mm called %12lld times\n", calls_to_flush_tlb_mm);
+ len += sprintf(buf+len, "flush_tlb_all called %12lld times\n", calls_to_flush_tlb_all);
+ len += sprintf(buf+len, "flush_tlb_range_sizes\n"
+ " 1 : %12lld\n"
+ " 2 : %12lld\n"
+ " 3 - 4 : %12lld\n"
+ " 5 - 7 : %12lld\n"
+ " 8 - 11 : %12lld\n"
+ "12 - 15 : %12lld\n"
+ "16+ : %12lld\n",
+ flush_tlb_range_1, flush_tlb_range_2, flush_tlb_range_3_4,
+ flush_tlb_range_5_7, flush_tlb_range_8_11, flush_tlb_range_12_15,
+ flush_tlb_range_16_up);
+ len += sprintf(buf+len, "page not present %12lld times\n", page_not_present);
+ *eof = 1;
+ return len;
+}
+
+static int __init register_proc_tlb(void)
+{
+ create_proc_read_entry("tlb", 0, NULL, tlb_proc_info, NULL);
+ return 0;
+}
+
+__initcall(register_proc_tlb);
+
+#endif
--- /dev/null
+/*
+ * arch/sh64/mm/hugetlbpage.c
+ *
+ * SuperH HugeTLB page support.
+ *
+ * Cloned from sparc64 by Paul Mundt.
+ *
+ * Copyright (C) 2002, 2003 David S. Miller (davem@redhat.com)
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/hugetlb.h>
+#include <linux/pagemap.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <linux/sysctl.h>
+
+#include <asm/mman.h>
+#include <asm/pgalloc.h>
+#include <asm/tlb.h>
+#include <asm/tlbflush.h>
+#include <asm/cacheflush.h>
+
+static pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr)
+{
+ pgd_t *pgd;
+ pmd_t *pmd;
+ pte_t *pte = NULL;
+
+ pgd = pgd_offset(mm, addr);
+ if (pgd) {
+ pmd = pmd_alloc(mm, pgd, addr);
+ if (pmd)
+ pte = pte_alloc_map(mm, pmd, addr);
+ }
+ return pte;
+}
+
+static pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr)
+{
+ pgd_t *pgd;
+ pmd_t *pmd;
+ pte_t *pte = NULL;
+
+ pgd = pgd_offset(mm, addr);
+ if (pgd) {
+ pmd = pmd_offset(pgd, addr);
+ if (pmd)
+ pte = pte_offset_map(pmd, addr);
+ }
+ return pte;
+}
+
+#define mk_pte_huge(entry) do { pte_val(entry) |= _PAGE_SZHUGE; } while (0)
+
+static void set_huge_pte(struct mm_struct *mm, struct vm_area_struct *vma,
+ struct page *page, pte_t * page_table, int write_access)
+{
+ unsigned long i;
+ pte_t entry;
+
+ mm->rss += (HPAGE_SIZE / PAGE_SIZE);
+
+ if (write_access)
+ entry = pte_mkwrite(pte_mkdirty(mk_pte(page,
+ vma->vm_page_prot)));
+ else
+ entry = pte_wrprotect(mk_pte(page, vma->vm_page_prot));
+ entry = pte_mkyoung(entry);
+ mk_pte_huge(entry);
+
+ for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) {
+ set_pte(page_table, entry);
+ page_table++;
+
+ pte_val(entry) += PAGE_SIZE;
+ }
+}
+
+/*
+ * This function checks for proper alignment of input addr and len parameters.
+ */
+int is_aligned_hugepage_range(unsigned long addr, unsigned long len)
+{
+ if (len & ~HPAGE_MASK)
+ return -EINVAL;
+ if (addr & ~HPAGE_MASK)
+ return -EINVAL;
+ return 0;
+}
+
+int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
+ struct vm_area_struct *vma)
+{
+ pte_t *src_pte, *dst_pte, entry;
+ struct page *ptepage;
+ unsigned long addr = vma->vm_start;
+ unsigned long end = vma->vm_end;
+ int i;
+
+ while (addr < end) {
+ dst_pte = huge_pte_alloc(dst, addr);
+ if (!dst_pte)
+ goto nomem;
+ src_pte = huge_pte_offset(src, addr);
+ BUG_ON(!src_pte || pte_none(*src_pte));
+ entry = *src_pte;
+ ptepage = pte_page(entry);
+ get_page(ptepage);
+ for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) {
+ set_pte(dst_pte, entry);
+ pte_val(entry) += PAGE_SIZE;
+ dst_pte++;
+ }
+ dst->rss += (HPAGE_SIZE / PAGE_SIZE);
+ addr += HPAGE_SIZE;
+ }
+ return 0;
+
+nomem:
+ return -ENOMEM;
+}
+
+int follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
+ struct page **pages, struct vm_area_struct **vmas,
+ unsigned long *position, int *length, int i)
+{
+ unsigned long vaddr = *position;
+ int remainder = *length;
+
+ WARN_ON(!is_vm_hugetlb_page(vma));
+
+ while (vaddr < vma->vm_end && remainder) {
+ if (pages) {
+ pte_t *pte;
+ struct page *page;
+
+ pte = huge_pte_offset(mm, vaddr);
+
+ /* hugetlb should be locked, and hence, prefaulted */
+ BUG_ON(!pte || pte_none(*pte));
+
+ page = pte_page(*pte);
+
+ WARN_ON(!PageCompound(page));
+
+ get_page(page);
+ pages[i] = page;
+ }
+
+ if (vmas)
+ vmas[i] = vma;
+
+ vaddr += PAGE_SIZE;
+ --remainder;
+ ++i;
+ }
+
+ *length = remainder;
+ *position = vaddr;
+
+ return i;
+}
+
+struct page *follow_huge_addr(struct mm_struct *mm,
+ unsigned long address, int write)
+{
+ return ERR_PTR(-EINVAL);
+}
+
+int pmd_huge(pmd_t pmd)
+{
+ return 0;
+}
+
+struct page *follow_huge_pmd(struct mm_struct *mm, unsigned long address,
+ pmd_t *pmd, int write)
+{
+ return NULL;
+}
+
+void unmap_hugepage_range(struct vm_area_struct *vma,
+ unsigned long start, unsigned long end)
+{
+ struct mm_struct *mm = vma->vm_mm;
+ unsigned long address;
+ pte_t *pte;
+ struct page *page;
+ int i;
+
+ BUG_ON(start & (HPAGE_SIZE - 1));
+ BUG_ON(end & (HPAGE_SIZE - 1));
+
+ for (address = start; address < end; address += HPAGE_SIZE) {
+ pte = huge_pte_offset(mm, address);
+ BUG_ON(!pte);
+ if (pte_none(*pte))
+ continue;
+ page = pte_page(*pte);
+ put_page(page);
+ for (i = 0; i < (1 << HUGETLB_PAGE_ORDER); i++) {
+ pte_clear(pte);
+ pte++;
+ }
+ }
+ mm->rss -= (end - start) >> PAGE_SHIFT;
+ flush_tlb_range(vma, start, end);
+}
+
+int hugetlb_prefault(struct address_space *mapping, struct vm_area_struct *vma)
+{
+ struct mm_struct *mm = current->mm;
+ unsigned long addr;
+ int ret = 0;
+
+ BUG_ON(vma->vm_start & ~HPAGE_MASK);
+ BUG_ON(vma->vm_end & ~HPAGE_MASK);
+
+ spin_lock(&mm->page_table_lock);
+ for (addr = vma->vm_start; addr < vma->vm_end; addr += HPAGE_SIZE) {
+ unsigned long idx;
+ pte_t *pte = huge_pte_alloc(mm, addr);
+ struct page *page;
+
+ if (!pte) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ if (!pte_none(*pte))
+ continue;
+
+ idx = ((addr - vma->vm_start) >> HPAGE_SHIFT)
+ + (vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT));
+ page = find_get_page(mapping, idx);
+ if (!page) {
+ /* charge the fs quota first */
+ if (hugetlb_get_quota(mapping)) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ page = alloc_huge_page();
+ if (!page) {
+ hugetlb_put_quota(mapping);
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = add_to_page_cache(page, mapping, idx, GFP_ATOMIC);
+ if (! ret) {
+ unlock_page(page);
+ } else {
+ hugetlb_put_quota(mapping);
+ free_huge_page(page);
+ goto out;
+ }
+ }
+ set_huge_pte(mm, vma, page, pte, vma->vm_flags & VM_WRITE);
+ }
+out:
+ spin_unlock(&mm->page_table_lock);
+ return ret;
+}
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/mm/init.c
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003, 2004 Paul Mundt
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/rwsem.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/bootmem.h>
+
+#include <asm/mmu_context.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/tlb.h>
+
+#ifdef CONFIG_BLK_DEV_INITRD
+#include <linux/blk.h>
+#endif
+
+DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
+
+/*
+ * Cache of MMU context last used.
+ */
+unsigned long mmu_context_cache;
+pgd_t * mmu_pdtp_cache;
+int after_bootmem = 0;
+
+/*
+ * BAD_PAGE is the page that is used for page faults when linux
+ * is out-of-memory. Older versions of linux just did a
+ * do_exit(), but using this instead means there is less risk
+ * for a process dying in kernel mode, possibly leaving an inode
+ * unused etc..
+ *
+ * BAD_PAGETABLE is the accompanying page-table: it is initialized
+ * to point to BAD_PAGE entries.
+ *
+ * ZERO_PAGE is a special page that is used for zero-initialized
+ * data and COW.
+ */
+
+extern unsigned char empty_zero_page[PAGE_SIZE];
+extern unsigned char empty_bad_page[PAGE_SIZE];
+extern pte_t empty_bad_pte_table[PTRS_PER_PTE];
+extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
+
+extern char _text, _etext, _edata, __bss_start, _end;
+extern char __init_begin, __init_end;
+
+/* It'd be good if these lines were in the standard header file. */
+#define START_PFN (NODE_DATA(0)->bdata->node_boot_start >> PAGE_SHIFT)
+#define MAX_LOW_PFN (NODE_DATA(0)->bdata->node_low_pfn)
+
+
+void show_mem(void)
+{
+ int i, total = 0, reserved = 0;
+ int shared = 0, cached = 0;
+
+ printk("Mem-info:\n");
+ show_free_areas();
+ printk("Free swap: %6ldkB\n",nr_swap_pages<<(PAGE_SHIFT-10));
+ i = max_mapnr;
+ while (i-- > 0) {
+ total++;
+ if (PageReserved(mem_map+i))
+ reserved++;
+ else if (PageSwapCache(mem_map+i))
+ cached++;
+ else if (page_count(mem_map+i))
+ shared += page_count(mem_map+i) - 1;
+ }
+ printk("%d pages of RAM\n",total);
+ printk("%d reserved pages\n",reserved);
+ printk("%d pages shared\n",shared);
+ printk("%d pages swap cached\n",cached);
+ printk("%ld pages in page table cache\n",pgtable_cache_size);
+}
+
+/*
+ * paging_init() sets up the page tables.
+ *
+ * head.S already did a lot to set up address translation for the kernel.
+ * Here we comes with:
+ * . MMU enabled
+ * . ASID set (SR)
+ * . some 512MB regions being mapped of which the most relevant here is:
+ * . CACHED segment (ASID 0 [irrelevant], shared AND NOT user)
+ * . possible variable length regions being mapped as:
+ * . UNCACHED segment (ASID 0 [irrelevant], shared AND NOT user)
+ * . All of the memory regions are placed, independently from the platform
+ * on high addresses, above 0x80000000.
+ * . swapper_pg_dir is already cleared out by the .space directive
+ * in any case swapper does not require a real page directory since
+ * it's all kernel contained.
+ *
+ * Those pesky NULL-reference errors in the kernel are then
+ * dealt with by not mapping address 0x00000000 at all.
+ *
+ */
+void __init paging_init(void)
+{
+ unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0};
+
+ pgd_init((unsigned long)swapper_pg_dir);
+ pgd_init((unsigned long)swapper_pg_dir +
+ sizeof(pgd_t) * USER_PTRS_PER_PGD);
+
+ mmu_context_cache = MMU_CONTEXT_FIRST_VERSION;
+
+ /*
+ * All memory is good as ZONE_NORMAL (fall-through) and ZONE_DMA.
+ */
+ zones_size[ZONE_DMA] = MAX_LOW_PFN - START_PFN;
+
+ free_area_init_node(0, NODE_DATA(0), 0, zones_size, __MEMORY_START >> PAGE_SHIFT, 0);
+
+ /* XXX: MRB-remove - this doesn't seem sane, should this be done somewhere else ?*/
+ mem_map = NODE_DATA(0)->node_mem_map;
+}
+
+void __init mem_init(void)
+{
+ int codesize, reservedpages, datasize, initsize;
+ int tmp;
+
+ max_mapnr = num_physpages = MAX_LOW_PFN - START_PFN;
+ high_memory = (void *)__va(MAX_LOW_PFN * PAGE_SIZE);
+
+ /*
+ * Clear the zero-page.
+ * This is not required but we might want to re-use
+ * this very page to pass boot parameters, one day.
+ */
+ memset(empty_zero_page, 0, PAGE_SIZE);
+
+ /* this will put all low memory onto the freelists */
+ totalram_pages += free_all_bootmem_node(NODE_DATA(0));
+ reservedpages = 0;
+ for (tmp = 0; tmp < num_physpages; tmp++)
+ /*
+ * Only count reserved RAM pages
+ */
+ if (PageReserved(mem_map+tmp))
+ reservedpages++;
+
+ after_bootmem = 1;
+
+ codesize = (unsigned long) &_etext - (unsigned long) &_text;
+ datasize = (unsigned long) &_edata - (unsigned long) &_etext;
+ initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin;
+
+ printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init)\n",
+ (unsigned long) nr_free_pages() << (PAGE_SHIFT-10),
+ max_mapnr << (PAGE_SHIFT-10),
+ codesize >> 10,
+ reservedpages << (PAGE_SHIFT-10),
+ datasize >> 10,
+ initsize >> 10);
+}
+
+void free_initmem(void)
+{
+ unsigned long addr;
+
+ addr = (unsigned long)(&__init_begin);
+ for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) {
+ ClearPageReserved(virt_to_page(addr));
+ set_page_count(virt_to_page(addr), 1);
+ free_page(addr);
+ totalram_pages++;
+ }
+ printk ("Freeing unused kernel memory: %ldk freed\n", (&__init_end - &__init_begin) >> 10);
+}
+
+#ifdef CONFIG_BLK_DEV_INITRD
+void free_initrd_mem(unsigned long start, unsigned long end)
+{
+ unsigned long p;
+ for (p = start; p < end; p += PAGE_SIZE) {
+ ClearPageReserved(virt_to_page(p));
+ set_page_count(virt_to_page(p), 1);
+ free_page(p);
+ totalram_pages++;
+ }
+ printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10);
+}
+#endif
+
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * arch/sh64/mm/ioremap.c
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003, 2004 Paul Mundt
+ *
+ * Mostly derived from arch/sh/mm/ioremap.c which, in turn is mostly
+ * derived from arch/i386/mm/ioremap.c .
+ *
+ * (C) Copyright 1995 1996 Linus Torvalds
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <asm/io.h>
+#include <asm/pgalloc.h>
+#include <asm/tlbflush.h>
+#include <linux/ioport.h>
+#include <linux/bootmem.h>
+#include <linux/proc_fs.h>
+
+static void shmedia_mapioaddr(unsigned long, unsigned long);
+static unsigned long shmedia_ioremap(struct resource *, u32, int);
+
+static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size,
+ unsigned long phys_addr, unsigned long flags)
+{
+ unsigned long end;
+ unsigned long pfn;
+ pgprot_t pgprot = __pgprot(_PAGE_PRESENT | _PAGE_READ |
+ _PAGE_WRITE | _PAGE_DIRTY |
+ _PAGE_ACCESSED | _PAGE_SHARED | flags);
+
+ address &= ~PMD_MASK;
+ end = address + size;
+ if (end > PMD_SIZE)
+ end = PMD_SIZE;
+ if (address >= end)
+ BUG();
+
+ pfn = phys_addr >> PAGE_SHIFT;
+
+ pr_debug(" %s: pte %p address %lx size %lx phys_addr %lx\n",
+ __FUNCTION__,pte,address,size,phys_addr);
+
+ do {
+ if (!pte_none(*pte)) {
+ printk("remap_area_pte: page already exists\n");
+ BUG();
+ }
+
+ set_pte(pte, pfn_pte(pfn, pgprot));
+ address += PAGE_SIZE;
+ pfn++;
+ pte++;
+ } while (address && (address < end));
+}
+
+static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size,
+ unsigned long phys_addr, unsigned long flags)
+{
+ unsigned long end;
+
+ address &= ~PGDIR_MASK;
+ end = address + size;
+
+ if (end > PGDIR_SIZE)
+ end = PGDIR_SIZE;
+
+ phys_addr -= address;
+
+ if (address >= end)
+ BUG();
+
+ do {
+ pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address);
+ if (!pte)
+ return -ENOMEM;
+ remap_area_pte(pte, address, end - address, address + phys_addr, flags);
+ address = (address + PMD_SIZE) & PMD_MASK;
+ pmd++;
+ } while (address && (address < end));
+ return 0;
+}
+
+static int remap_area_pages(unsigned long address, unsigned long phys_addr,
+ unsigned long size, unsigned long flags)
+{
+ int error;
+ pgd_t * dir;
+ unsigned long end = address + size;
+
+ phys_addr -= address;
+ dir = pgd_offset_k(address);
+ flush_cache_all();
+ if (address >= end)
+ BUG();
+ spin_lock(&init_mm.page_table_lock);
+ do {
+ pmd_t *pmd = pmd_alloc(&init_mm, dir, address);
+ error = -ENOMEM;
+ if (!pmd)
+ break;
+ if (remap_area_pmd(pmd, address, end - address,
+ phys_addr + address, flags)) {
+ break;
+ }
+ error = 0;
+ address = (address + PGDIR_SIZE) & PGDIR_MASK;
+ dir++;
+ } while (address && (address < end));
+ spin_unlock(&init_mm.page_table_lock);
+ flush_tlb_all();
+ return 0;
+}
+
+/*
+ * Generic mapping function (not visible outside):
+ */
+
+/*
+ * Remap an arbitrary physical address space into the kernel virtual
+ * address space. Needed when the kernel wants to access high addresses
+ * directly.
+ *
+ * NOTE! We need to allow non-page-aligned mappings too: we will obviously
+ * have to convert them into an offset in a page-aligned mapping, but the
+ * caller shouldn't need to know that small detail.
+ */
+void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags)
+{
+ void * addr;
+ struct vm_struct * area;
+ unsigned long offset, last_addr;
+
+ /* Don't allow wraparound or zero size */
+ last_addr = phys_addr + size - 1;
+ if (!size || last_addr < phys_addr)
+ return NULL;
+
+ /*
+ * Mappings have to be page-aligned
+ */
+ offset = phys_addr & ~PAGE_MASK;
+ phys_addr &= PAGE_MASK;
+ size = PAGE_ALIGN(last_addr + 1) - phys_addr;
+
+ /*
+ * Ok, go for it..
+ */
+ area = get_vm_area(size, VM_IOREMAP);
+ pr_debug("Get vm_area returns %p addr %p\n",area,area->addr);
+ if (!area)
+ return NULL;
+ area->phys_addr = phys_addr;
+ addr = area->addr;
+ if (remap_area_pages((unsigned long)addr, phys_addr, size, flags)) {
+ vunmap(addr);
+ return NULL;
+ }
+ return (void *) (offset + (char *)addr);
+}
+
+void iounmap(void *addr)
+{
+ struct vm_struct *area;
+
+ vfree((void *) (PAGE_MASK & (unsigned long) addr));
+ area = remove_vm_area((void *) (PAGE_MASK & (unsigned long) addr));
+ if (!area) {
+ printk(KERN_ERR "iounmap: bad address %p\n", addr);
+ return;
+ }
+
+ kfree(area);
+}
+
+static struct resource shmedia_iomap = {
+ .name = "shmedia_iomap",
+ .start = IOBASE_VADDR,
+ .end = IOBASE_END - 1,
+};
+
+static void shmedia_mapioaddr(unsigned long pa, unsigned long va);
+static void shmedia_unmapioaddr(unsigned long vaddr);
+static unsigned long shmedia_ioremap(struct resource *res, u32 pa, int sz);
+
+/*
+ * We have the same problem as the SPARC, so lets have the same comment:
+ * Our mini-allocator...
+ * Boy this is gross! We need it because we must map I/O for
+ * timers and interrupt controller before the kmalloc is available.
+ */
+
+#define XNMLN 15
+#define XNRES 10
+
+struct xresource {
+ struct resource xres; /* Must be first */
+ int xflag; /* 1 == used */
+ char xname[XNMLN+1];
+};
+
+static struct xresource xresv[XNRES];
+
+static struct xresource *xres_alloc(void)
+{
+ struct xresource *xrp;
+ int n;
+
+ xrp = xresv;
+ for (n = 0; n < XNRES; n++) {
+ if (xrp->xflag == 0) {
+ xrp->xflag = 1;
+ return xrp;
+ }
+ xrp++;
+ }
+ return NULL;
+}
+
+static void xres_free(struct xresource *xrp)
+{
+ xrp->xflag = 0;
+}
+
+static struct resource *shmedia_find_resource(struct resource *root,
+ unsigned long vaddr)
+{
+ struct resource *res;
+
+ for (res = root->child; res; res = res->sibling)
+ if (res->start <= vaddr && res->end >= vaddr)
+ return res;
+
+ return NULL;
+}
+
+static unsigned long shmedia_alloc_io(unsigned long phys, unsigned long size,
+ const char *name)
+{
+ static int printed_full = 0;
+ struct xresource *xres;
+ struct resource *res;
+ char *tack;
+ int tlen;
+
+ if (name == NULL) name = "???";
+
+ if ((xres = xres_alloc()) != 0) {
+ tack = xres->xname;
+ res = &xres->xres;
+ } else {
+ if (!printed_full) {
+ printk("%s: done with statics, switching to kmalloc\n",
+ __FUNCTION__);
+ printed_full = 1;
+ }
+ tlen = strlen(name);
+ tack = kmalloc(sizeof (struct resource) + tlen + 1, GFP_KERNEL);
+ if (!tack)
+ return -ENOMEM;
+ memset(tack, 0, sizeof(struct resource));
+ res = (struct resource *) tack;
+ tack += sizeof (struct resource);
+ }
+
+ strncpy(tack, name, XNMLN);
+ tack[XNMLN] = 0;
+ res->name = tack;
+
+ return shmedia_ioremap(res, phys, size);
+}
+
+static unsigned long shmedia_ioremap(struct resource *res, u32 pa, int sz)
+{
+ unsigned long offset = ((unsigned long) pa) & (~PAGE_MASK);
+ unsigned long round_sz = (offset + sz + PAGE_SIZE-1) & PAGE_MASK;
+ unsigned long va;
+ unsigned int psz;
+
+ if (allocate_resource(&shmedia_iomap, res, round_sz,
+ shmedia_iomap.start, shmedia_iomap.end,
+ PAGE_SIZE, NULL, NULL) != 0) {
+ panic("alloc_io_res(%s): cannot occupy\n",
+ (res->name != NULL)? res->name: "???");
+ }
+
+ va = res->start;
+ pa &= PAGE_MASK;
+
+ psz = (res->end - res->start + (PAGE_SIZE - 1)) / PAGE_SIZE;
+
+ /* log at boot time ... */
+ printk("mapioaddr: %6s [%2d page%s] va 0x%08lx pa 0x%08x\n",
+ ((res->name != NULL) ? res->name : "???"),
+ psz, psz == 1 ? " " : "s", va, pa);
+
+ for (psz = res->end - res->start + 1; psz != 0; psz -= PAGE_SIZE) {
+ shmedia_mapioaddr(pa, va);
+ va += PAGE_SIZE;
+ pa += PAGE_SIZE;
+ }
+
+ res->start += offset;
+ res->end = res->start + sz - 1; /* not strictly necessary.. */
+
+ return res->start;
+}
+
+static void shmedia_free_io(struct resource *res)
+{
+ unsigned long len = res->end - res->start + 1;
+
+ BUG_ON((len & (PAGE_SIZE - 1)) != 0);
+
+ while (len) {
+ len -= PAGE_SIZE;
+ shmedia_unmapioaddr(res->start + len);
+ }
+
+ release_resource(res);
+}
+
+static void *sh64_get_page(void)
+{
+ extern int after_bootmem;
+ void *page;
+
+ if (after_bootmem) {
+ page = (void *)get_zeroed_page(GFP_ATOMIC);
+ } else {
+ page = alloc_bootmem_pages(PAGE_SIZE);
+ }
+
+ if (!page || ((unsigned long)page & ~PAGE_MASK))
+ panic("sh64_get_page: Out of memory already?\n");
+
+ return page;
+}
+
+static void shmedia_mapioaddr(unsigned long pa, unsigned long va)
+{
+ pgd_t *pgdp;
+ pmd_t *pmdp;
+ pte_t *ptep, pte;
+ pgprot_t prot;
+ unsigned long flags = 1; /* 1 = CB0-1 device */
+
+ pr_debug("shmedia_mapiopage pa %08lx va %08lx\n", pa, va);
+
+ pgdp = pgd_offset_k(va);
+ if (pgd_none(*pgdp) || !pgd_present(*pgdp)) {
+ pmdp = (pmd_t *)sh64_get_page();
+ set_pgd(pgdp, __pgd((unsigned long)pmdp | _KERNPG_TABLE));
+ }
+
+ pmdp = pmd_offset(pgdp, va);
+ if (pmd_none(*pmdp) || !pmd_present(*pmdp) ) {
+ ptep = (pte_t *)sh64_get_page();
+ set_pmd(pmdp, __pmd((unsigned long)ptep + _PAGE_TABLE));
+ }
+
+ prot = __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE |
+ _PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_SHARED | flags);
+
+ pte = pfn_pte(pa >> PAGE_SHIFT, prot);
+ ptep = pte_offset_kernel(pmdp, va);
+
+ if (!pte_none(*ptep) &&
+ pte_val(*ptep) != pte_val(pte))
+ pte_ERROR(*ptep);
+
+ set_pte(ptep, pte);
+
+ flush_tlb_kernel_range(va, PAGE_SIZE);
+}
+
+static void shmedia_unmapioaddr(unsigned long vaddr)
+{
+ pgd_t *pgdp;
+ pmd_t *pmdp;
+ pte_t *ptep;
+
+ pgdp = pgd_offset_k(vaddr);
+ pmdp = pmd_offset(pgdp, vaddr);
+
+ if (pmd_none(*pmdp) || pmd_bad(*pmdp))
+ return;
+
+ ptep = pte_offset_kernel(pmdp, vaddr);
+
+ if (pte_none(*ptep) || !pte_present(*ptep))
+ return;
+
+ clear_page((void *)ptep);
+ pte_clear(ptep);
+}
+
+unsigned long onchip_remap(unsigned long phys, unsigned long size, const char *name)
+{
+ if (size < PAGE_SIZE)
+ size = PAGE_SIZE;
+
+ return shmedia_alloc_io(phys, size, name);
+}
+
+void onchip_unmap(unsigned long vaddr)
+{
+ struct resource *res;
+ unsigned int psz;
+
+ res = shmedia_find_resource(&shmedia_iomap, vaddr);
+ if (!res) {
+ printk(KERN_ERR "%s: Failed to free 0x%08lx\n",
+ __FUNCTION__, vaddr);
+ return;
+ }
+
+ psz = (res->end - res->start + (PAGE_SIZE - 1)) / PAGE_SIZE;
+
+ printk(KERN_DEBUG "unmapioaddr: %6s [%2d page%s] freed\n",
+ res->name, psz, psz == 1 ? " " : "s");
+
+ shmedia_free_io(res);
+
+ if ((char *)res >= (char *)xresv &&
+ (char *)res < (char *)&xresv[XNRES]) {
+ xres_free((struct xresource *)res);
+ } else {
+ kfree(res);
+ }
+}
+
+#ifdef CONFIG_PROC_FS
+static int
+ioremap_proc_info(char *buf, char **start, off_t fpos, int length, int *eof,
+ void *data)
+{
+ char *p = buf, *e = buf + length;
+ struct resource *r;
+ const char *nm;
+
+ for (r = ((struct resource *)data)->child; r != NULL; r = r->sibling) {
+ if (p + 32 >= e) /* Better than nothing */
+ break;
+ if ((nm = r->name) == 0) nm = "???";
+ p += sprintf(p, "%08lx-%08lx: %s\n", r->start, r->end, nm);
+ }
+
+ return p-buf;
+}
+#endif /* CONFIG_PROC_FS */
+
+static int __init register_proc_onchip(void)
+{
+#ifdef CONFIG_PROC_FS
+ create_proc_read_entry("io_map",0,0, ioremap_proc_info, &shmedia_iomap);
+#endif
+ return 0;
+}
+
+__initcall(register_proc_onchip);
--- /dev/null
+/*
+ * arch/sh/oprofile/op_model_null.c
+ *
+ * Copyright (C) 2003 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/oprofile.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+
+int __init oprofile_arch_init(struct oprofile_operations **ops)
+{
+ return -ENODEV;
+}
+
+void oprofile_arch_exit(void)
+{
+}
+
extern void calibrate_delay(void);
extern volatile int smp_processors_ready;
-extern unsigned long cpu_present_map;
extern int smp_num_cpus;
static int smp_highest_cpu;
extern int smp_threads_ready;
* the SMP initialization the master will be just allowed
* to call the scheduler code.
*/
- init_idle();
-
/* Get our local ticker going. */
smp_setup_percpu_timer();
extern int cpu_idle(void *unused);
extern void init_IRQ(void);
extern void cpu_panic(void);
-extern int start_secondary(void *unused);
/*
* Cycle through the processors asking the PROM to start each one.
current_set[0] = NULL;
local_irq_enable();
- cpu_present_map = 0;
+ cpus_clear(cpu_present_map);
/* XXX This whole thing has to go. See sparc64. */
for (i = 0; !cpu_find_by_instance(i, NULL, &mid); i++)
- cpu_present_map |= (1<<mid);
- SMP_PRINTK(("cpu_present_map %08lx\n", cpu_present_map));
+ cpu_set(mid, cpu_present_map);
+ SMP_PRINTK(("cpu_present_map %08lx\n", cpus_addr(cpu_present_map)[0]));
for(i=0; i < NR_CPUS; i++)
__cpu_number_map[i] = -1;
for(i=0; i < NR_CPUS; i++)
if(i == boot_cpu_id)
continue;
- if(cpu_present_map & (1 << i)) {
+ if (cpu_isset(i, cpu_present_map)) {
extern unsigned long sun4d_cpu_startup;
unsigned long *entry = &sun4d_cpu_startup;
struct task_struct *p;
}
}
if(!(cpu_callin_map[i])) {
- cpu_present_map &= ~(1 << i);
+ cpu_clear(i, cpu_present_map);
__cpu_number_map[i] = -1;
}
}
local_flush_cache_all();
if(cpucount == 0) {
printk("Error: only one Processor found.\n");
- cpu_present_map = (1 << hard_smp4d_processor_id());
+ cpu_present_map = cpumask_of_cpu(hard_smp4d_processor_id());
} else {
unsigned long bogosum = 0;
for(i = 0; i < NR_CPUS; i++) {
- if(cpu_present_map & (1 << i)) {
+ if (cpu_isset(i, cpu_present_map)) {
bogosum += cpu_data(i).udelay_val;
smp_highest_cpu = i;
}
/* Init receive/complete mapping, plus fire the IPI's off. */
{
- register unsigned long mask;
+ cpumask_t mask;
register int i;
- mask = (cpu_present_map & ~(1 << hard_smp4d_processor_id()));
+ mask = cpumask_of_cpu(hard_smp4d_processor_id());
+ cpus_andnot(mask, cpu_present_map, mask);
for(i = 0; i <= high; i++) {
- if(mask & (1 << i)) {
+ if (cpu_isset(i, mask)) {
ccall_info.processors_in[i] = 0;
ccall_info.processors_out[i] = 0;
sun4d_send_ipi(i, IRQ_CROSS_CALL);
t_nmi[1] = t_nmi[1] + (linux_trap_ipi15_sun4d - linux_trap_ipi15_sun4m);
/* And set btfixup... */
- BTFIXUPSET_BLACKBOX(smp_processor_id, smp4d_blackbox_id);
+ BTFIXUPSET_BLACKBOX(hard_smp_processor_id, smp4d_blackbox_id);
BTFIXUPSET_BLACKBOX(load_current, smp4d_blackbox_current);
BTFIXUPSET_CALL(smp_cross_call, smp4d_cross_call, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(smp_message_pass, smp4d_message_pass, BTFIXUPCALL_NORM);
- BTFIXUPSET_CALL(__smp_processor_id, __smp4d_processor_id, BTFIXUPCALL_NORM);
+ BTFIXUPSET_CALL(__hard_smp_processor_id, __smp4d_processor_id, BTFIXUPCALL_NORM);
for (i = 0; i < NR_CPUS; i++) {
ccall_info.processors_in[i] = 1;
sun4_esp_physaddr=SUN4_400_ESP_PHYSADDR;
break;
default:
+ ;
}
}
.align 4
smp_do_cpu_idle:
- call init_idle
- nop
call cpu_idle
mov 0, %o0
/* Both these macros have to start with exactly the same insn */
#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
- ldd [%src + offset + 0x00], %t0; \
- ldd [%src + offset + 0x08], %t2; \
- ldd [%src + offset + 0x10], %t4; \
- ldd [%src + offset + 0x18], %t6; \
- st %t0, [%dst + offset + 0x00]; \
- st %t1, [%dst + offset + 0x04]; \
- st %t2, [%dst + offset + 0x08]; \
- st %t3, [%dst + offset + 0x0c]; \
- st %t4, [%dst + offset + 0x10]; \
- st %t5, [%dst + offset + 0x14]; \
- st %t6, [%dst + offset + 0x18]; \
- st %t7, [%dst + offset + 0x1c];
+ ldd [%src + (offset) + 0x00], %t0; \
+ ldd [%src + (offset) + 0x08], %t2; \
+ ldd [%src + (offset) + 0x10], %t4; \
+ ldd [%src + (offset) + 0x18], %t6; \
+ st %t0, [%dst + (offset) + 0x00]; \
+ st %t1, [%dst + (offset) + 0x04]; \
+ st %t2, [%dst + (offset) + 0x08]; \
+ st %t3, [%dst + (offset) + 0x0c]; \
+ st %t4, [%dst + (offset) + 0x10]; \
+ st %t5, [%dst + (offset) + 0x14]; \
+ st %t6, [%dst + (offset) + 0x18]; \
+ st %t7, [%dst + (offset) + 0x1c];
#define MOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
- ldd [%src + offset + 0x00], %t0; \
- ldd [%src + offset + 0x08], %t2; \
- ldd [%src + offset + 0x10], %t4; \
- ldd [%src + offset + 0x18], %t6; \
- std %t0, [%dst + offset + 0x00]; \
- std %t2, [%dst + offset + 0x08]; \
- std %t4, [%dst + offset + 0x10]; \
- std %t6, [%dst + offset + 0x18];
+ ldd [%src + (offset) + 0x00], %t0; \
+ ldd [%src + (offset) + 0x08], %t2; \
+ ldd [%src + (offset) + 0x10], %t4; \
+ ldd [%src + (offset) + 0x18], %t6; \
+ std %t0, [%dst + (offset) + 0x00]; \
+ std %t2, [%dst + (offset) + 0x08]; \
+ std %t4, [%dst + (offset) + 0x10]; \
+ std %t6, [%dst + (offset) + 0x18];
#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \
- ldd [%src - offset - 0x10], %t0; \
- ldd [%src - offset - 0x08], %t2; \
- st %t0, [%dst - offset - 0x10]; \
- st %t1, [%dst - offset - 0x0c]; \
- st %t2, [%dst - offset - 0x08]; \
- st %t3, [%dst - offset - 0x04];
+ ldd [%src - (offset) - 0x10], %t0; \
+ ldd [%src - (offset) - 0x08], %t2; \
+ st %t0, [%dst - (offset) - 0x10]; \
+ st %t1, [%dst - (offset) - 0x0c]; \
+ st %t2, [%dst - (offset) - 0x08]; \
+ st %t3, [%dst - (offset) - 0x04];
#define MOVE_HALFCHUNK(src, dst, offset, t0, t1, t2, t3) \
- lduh [%src + offset + 0x00], %t0; \
- lduh [%src + offset + 0x02], %t1; \
- lduh [%src + offset + 0x04], %t2; \
- lduh [%src + offset + 0x06], %t3; \
- sth %t0, [%dst + offset + 0x00]; \
- sth %t1, [%dst + offset + 0x02]; \
- sth %t2, [%dst + offset + 0x04]; \
- sth %t3, [%dst + offset + 0x06];
+ lduh [%src + (offset) + 0x00], %t0; \
+ lduh [%src + (offset) + 0x02], %t1; \
+ lduh [%src + (offset) + 0x04], %t2; \
+ lduh [%src + (offset) + 0x06], %t3; \
+ sth %t0, [%dst + (offset) + 0x00]; \
+ sth %t1, [%dst + (offset) + 0x02]; \
+ sth %t2, [%dst + (offset) + 0x04]; \
+ sth %t3, [%dst + (offset) + 0x06];
#define MOVE_SHORTCHUNK(src, dst, offset, t0, t1) \
- ldub [%src - offset - 0x02], %t0; \
- ldub [%src - offset - 0x01], %t1; \
- stb %t0, [%dst - offset - 0x02]; \
- stb %t1, [%dst - offset - 0x01];
+ ldub [%src - (offset) - 0x02], %t0; \
+ ldub [%src - (offset) - 0x01], %t1; \
+ stb %t0, [%dst - (offset) - 0x02]; \
+ stb %t1, [%dst - (offset) - 0x01];
.text
.align 4
#endif
/* Both these macros have to start with exactly the same insn */
-#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
- ldd [%src + offset + 0x00], %t0; \
- ldd [%src + offset + 0x08], %t2; \
- ldd [%src + offset + 0x10], %t4; \
- ldd [%src + offset + 0x18], %t6; \
- st %t0, [%dst + offset + 0x00]; \
- st %t1, [%dst + offset + 0x04]; \
- st %t2, [%dst + offset + 0x08]; \
- st %t3, [%dst + offset + 0x0c]; \
- st %t4, [%dst + offset + 0x10]; \
- st %t5, [%dst + offset + 0x14]; \
- st %t6, [%dst + offset + 0x18]; \
- st %t7, [%dst + offset + 0x1c];
-
-#define MOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
- ldd [%src + offset + 0x00], %t0; \
- ldd [%src + offset + 0x08], %t2; \
- ldd [%src + offset + 0x10], %t4; \
- ldd [%src + offset + 0x18], %t6; \
- std %t0, [%dst + offset + 0x00]; \
- std %t2, [%dst + offset + 0x08]; \
- std %t4, [%dst + offset + 0x10]; \
- std %t6, [%dst + offset + 0x18];
-
-#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \
- ldd [%src - offset - 0x10], %t0; \
- ldd [%src - offset - 0x08], %t2; \
- st %t0, [%dst - offset - 0x10]; \
- st %t1, [%dst - offset - 0x0c]; \
- st %t2, [%dst - offset - 0x08]; \
- st %t3, [%dst - offset - 0x04];
-
-#define MOVE_LASTALIGNCHUNK(src, dst, offset, t0, t1, t2, t3) \
- ldd [%src - offset - 0x10], %t0; \
- ldd [%src - offset - 0x08], %t2; \
- std %t0, [%dst - offset - 0x10]; \
- std %t2, [%dst - offset - 0x08];
-
-#define MOVE_SHORTCHUNK(src, dst, offset, t0, t1) \
- ldub [%src - offset - 0x02], %t0; \
- ldub [%src - offset - 0x01], %t1; \
- stb %t0, [%dst - offset - 0x02]; \
- stb %t1, [%dst - offset - 0x01];
+#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
+ ldd [%src + (offset) + 0x00], %t0; \
+ ldd [%src + (offset) + 0x08], %t2; \
+ ldd [%src + (offset) + 0x10], %t4; \
+ ldd [%src + (offset) + 0x18], %t6; \
+ st %t0, [%dst + (offset) + 0x00]; \
+ st %t1, [%dst + (offset) + 0x04]; \
+ st %t2, [%dst + (offset) + 0x08]; \
+ st %t3, [%dst + (offset) + 0x0c]; \
+ st %t4, [%dst + (offset) + 0x10]; \
+ st %t5, [%dst + (offset) + 0x14]; \
+ st %t6, [%dst + (offset) + 0x18]; \
+ st %t7, [%dst + (offset) + 0x1c];
+
+#define MOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
+ ldd [%src + (offset) + 0x00], %t0; \
+ ldd [%src + (offset) + 0x08], %t2; \
+ ldd [%src + (offset) + 0x10], %t4; \
+ ldd [%src + (offset) + 0x18], %t6; \
+ std %t0, [%dst + (offset) + 0x00]; \
+ std %t2, [%dst + (offset) + 0x08]; \
+ std %t4, [%dst + (offset) + 0x10]; \
+ std %t6, [%dst + (offset) + 0x18];
+
+#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \
+ ldd [%src - (offset) - 0x10], %t0; \
+ ldd [%src - (offset) - 0x08], %t2; \
+ st %t0, [%dst - (offset) - 0x10]; \
+ st %t1, [%dst - (offset) - 0x0c]; \
+ st %t2, [%dst - (offset) - 0x08]; \
+ st %t3, [%dst - (offset) - 0x04];
+
+#define MOVE_LASTALIGNCHUNK(src, dst, offset, t0, t1, t2, t3) \
+ ldd [%src - (offset) - 0x10], %t0; \
+ ldd [%src - (offset) - 0x08], %t2; \
+ std %t0, [%dst - (offset) - 0x10]; \
+ std %t2, [%dst - (offset) - 0x08];
+
+#define MOVE_SHORTCHUNK(src, dst, offset, t0, t1) \
+ ldub [%src - (offset) - 0x02], %t0; \
+ ldub [%src - (offset) - 0x01], %t1; \
+ stb %t0, [%dst - (offset) - 0x02]; \
+ stb %t1, [%dst - (offset) - 0x01];
/* Both these macros have to start with exactly the same insn */
-#define RMOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
- ldd [%src - offset - 0x20], %t0; \
- ldd [%src - offset - 0x18], %t2; \
- ldd [%src - offset - 0x10], %t4; \
- ldd [%src - offset - 0x08], %t6; \
- st %t0, [%dst - offset - 0x20]; \
- st %t1, [%dst - offset - 0x1c]; \
- st %t2, [%dst - offset - 0x18]; \
- st %t3, [%dst - offset - 0x14]; \
- st %t4, [%dst - offset - 0x10]; \
- st %t5, [%dst - offset - 0x0c]; \
- st %t6, [%dst - offset - 0x08]; \
- st %t7, [%dst - offset - 0x04];
-
-#define RMOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
- ldd [%src - offset - 0x20], %t0; \
- ldd [%src - offset - 0x18], %t2; \
- ldd [%src - offset - 0x10], %t4; \
- ldd [%src - offset - 0x08], %t6; \
- std %t0, [%dst - offset - 0x20]; \
- std %t2, [%dst - offset - 0x18]; \
- std %t4, [%dst - offset - 0x10]; \
- std %t6, [%dst - offset - 0x08];
-
-#define RMOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \
- ldd [%src + offset + 0x00], %t0; \
- ldd [%src + offset + 0x08], %t2; \
- st %t0, [%dst + offset + 0x00]; \
- st %t1, [%dst + offset + 0x04]; \
- st %t2, [%dst + offset + 0x08]; \
- st %t3, [%dst + offset + 0x0c];
-
-#define RMOVE_SHORTCHUNK(src, dst, offset, t0, t1) \
- ldub [%src + offset + 0x00], %t0; \
- ldub [%src + offset + 0x01], %t1; \
- stb %t0, [%dst + offset + 0x00]; \
- stb %t1, [%dst + offset + 0x01];
-
-#define SMOVE_CHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, prev, shil, shir, offset2) \
- ldd [%src + offset + 0x00], %t0; \
- ldd [%src + offset + 0x08], %t2; \
- srl %t0, shir, %t5; \
- srl %t1, shir, %t6; \
- sll %t0, shil, %t0; \
- or %t5, %prev, %t5; \
- sll %t1, shil, %prev; \
- or %t6, %t0, %t0; \
- srl %t2, shir, %t1; \
- srl %t3, shir, %t6; \
- sll %t2, shil, %t2; \
- or %t1, %prev, %t1; \
- std %t4, [%dst + offset + offset2 - 0x04]; \
- std %t0, [%dst + offset + offset2 + 0x04]; \
- sll %t3, shil, %prev; \
+#define RMOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
+ ldd [%src - (offset) - 0x20], %t0; \
+ ldd [%src - (offset) - 0x18], %t2; \
+ ldd [%src - (offset) - 0x10], %t4; \
+ ldd [%src - (offset) - 0x08], %t6; \
+ st %t0, [%dst - (offset) - 0x20]; \
+ st %t1, [%dst - (offset) - 0x1c]; \
+ st %t2, [%dst - (offset) - 0x18]; \
+ st %t3, [%dst - (offset) - 0x14]; \
+ st %t4, [%dst - (offset) - 0x10]; \
+ st %t5, [%dst - (offset) - 0x0c]; \
+ st %t6, [%dst - (offset) - 0x08]; \
+ st %t7, [%dst - (offset) - 0x04];
+
+#define RMOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
+ ldd [%src - (offset) - 0x20], %t0; \
+ ldd [%src - (offset) - 0x18], %t2; \
+ ldd [%src - (offset) - 0x10], %t4; \
+ ldd [%src - (offset) - 0x08], %t6; \
+ std %t0, [%dst - (offset) - 0x20]; \
+ std %t2, [%dst - (offset) - 0x18]; \
+ std %t4, [%dst - (offset) - 0x10]; \
+ std %t6, [%dst - (offset) - 0x08];
+
+#define RMOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \
+ ldd [%src + (offset) + 0x00], %t0; \
+ ldd [%src + (offset) + 0x08], %t2; \
+ st %t0, [%dst + (offset) + 0x00]; \
+ st %t1, [%dst + (offset) + 0x04]; \
+ st %t2, [%dst + (offset) + 0x08]; \
+ st %t3, [%dst + (offset) + 0x0c];
+
+#define RMOVE_SHORTCHUNK(src, dst, offset, t0, t1) \
+ ldub [%src + (offset) + 0x00], %t0; \
+ ldub [%src + (offset) + 0x01], %t1; \
+ stb %t0, [%dst + (offset) + 0x00]; \
+ stb %t1, [%dst + (offset) + 0x01];
+
+#define SMOVE_CHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, prev, shil, shir, offset2) \
+ ldd [%src + (offset) + 0x00], %t0; \
+ ldd [%src + (offset) + 0x08], %t2; \
+ srl %t0, shir, %t5; \
+ srl %t1, shir, %t6; \
+ sll %t0, shil, %t0; \
+ or %t5, %prev, %t5; \
+ sll %t1, shil, %prev; \
+ or %t6, %t0, %t0; \
+ srl %t2, shir, %t1; \
+ srl %t3, shir, %t6; \
+ sll %t2, shil, %t2; \
+ or %t1, %prev, %t1; \
+ std %t4, [%dst + (offset) + (offset2) - 0x04]; \
+ std %t0, [%dst + (offset) + (offset2) + 0x04]; \
+ sll %t3, shil, %prev; \
or %t6, %t2, %t4;
-#define SMOVE_ALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, prev, shil, shir, offset2) \
- ldd [%src + offset + 0x00], %t0; \
- ldd [%src + offset + 0x08], %t2; \
- srl %t0, shir, %t4; \
- srl %t1, shir, %t5; \
- sll %t0, shil, %t6; \
- or %t4, %prev, %t0; \
- sll %t1, shil, %prev; \
- or %t5, %t6, %t1; \
- srl %t2, shir, %t4; \
- srl %t3, shir, %t5; \
- sll %t2, shil, %t6; \
- or %t4, %prev, %t2; \
- sll %t3, shil, %prev; \
- or %t5, %t6, %t3; \
- std %t0, [%dst + offset + offset2 + 0x00]; \
- std %t2, [%dst + offset + offset2 + 0x08];
+#define SMOVE_ALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, prev, shil, shir, offset2) \
+ ldd [%src + (offset) + 0x00], %t0; \
+ ldd [%src + (offset) + 0x08], %t2; \
+ srl %t0, shir, %t4; \
+ srl %t1, shir, %t5; \
+ sll %t0, shil, %t6; \
+ or %t4, %prev, %t0; \
+ sll %t1, shil, %prev; \
+ or %t5, %t6, %t1; \
+ srl %t2, shir, %t4; \
+ srl %t3, shir, %t5; \
+ sll %t2, shil, %t6; \
+ or %t4, %prev, %t2; \
+ sll %t3, shil, %prev; \
+ or %t5, %t6, %t3; \
+ std %t0, [%dst + (offset) + (offset2) + 0x00]; \
+ std %t2, [%dst + (offset) + (offset2) + 0x08];
.text
.align 4
static void turbosparc_flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
{
- struct mm_struct *mm = vma->vm_mm;
-
- FLUSH_BEGIN(mm)
+ FLUSH_BEGIN(vma->vm_mm)
flush_user_windows();
turbosparc_idflash_clear();
FLUSH_END
static void turbosparc_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
{
- struct mm_struct *mm = vma->vm_mm;
-
- FLUSH_BEGIN(mm)
+ FLUSH_BEGIN(vma->vm_mm)
srmmu_flush_whole_tlb();
FLUSH_END
}
.globl rtrap_irq, rtrap_clr_l6, rtrap, irqsz_patchme, rtrap_xcall
rtrap_irq:
rtrap_clr_l6: clr %l6
-rtrap: ldub [%g6 + TI_CPU], %l0
+rtrap:
+ ldub [%g6 + TI_CPU], %l0
sethi %hi(irq_stat), %l2 ! &softirq_active
or %l2, %lo(irq_stat), %l2 ! &softirq_active
irqsz_patchme: sllx %l0, 0, %l0
-/* $Id: U3copy_from_user.S,v 1.4 2002/01/15 07:16:26 davem Exp $
- * U3memcpy.S: UltraSparc-III optimized copy from userspace.
+/* U3copy_from_user.S: UltraSparc-III optimized copy from userspace.
*
- * Copyright (C) 1999, 2000 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1999, 2000, 2004 David S. Miller (davem@redhat.com)
*/
-#ifdef __KERNEL__
#include <asm/visasm.h>
#include <asm/asi.h>
#include <asm/dcu.h>
#include <asm/spitfire.h>
-#undef SMALL_COPY_USES_FPU
+
+#define XCC xcc
+
+#define EXNV_RAW(x,y,a,b) \
+98: x,y; \
+ .section .fixup; \
+ .align 4; \
+99: ba U3cfu_fixup; \
+ a, b, %o1; \
+ .section __ex_table; \
+ .align 4; \
+ .word 98b, 99b; \
+ .text; \
+ .align 4;
#define EXNV(x,y,a,b) \
98: x,y; \
.section .fixup; \
.align 4; \
-99: VISExitHalf; \
+99: add %o1, %o3, %o0; \
ba U3cfu_fixup; \
a, b, %o1; \
.section __ex_table; \
.word 98b, 99b; \
.text; \
.align 4;
+#define EXNV4(x,y,a,b) \
+98: x,y; \
+ .section .fixup; \
+ .align 4; \
+99: add %o1, %o3, %o0; \
+ a, b, %o1; \
+ ba U3cfu_fixup; \
+ add %o1, 4, %o1; \
+ .section __ex_table; \
+ .align 4; \
+ .word 98b, 99b; \
+ .text; \
+ .align 4;
+#define EXNV8(x,y,a,b) \
+98: x,y; \
+ .section .fixup; \
+ .align 4; \
+99: add %o1, %o3, %o0; \
+ a, b, %o1; \
+ ba U3cfu_fixup; \
+ add %o1, 8, %o1; \
+ .section __ex_table; \
+ .align 4; \
+ .word 98b, 99b; \
+ .text; \
+ .align 4;
#define EX(x,y,a,b) \
98: x,y; \
.section .fixup; \
.word 98b, 99b; \
.text; \
.align 4;
-#else
-#define ASI_BLK_P 0xf0
-#define FPRS_FEF 0x04
-#define VISEntryHalf rd %fprs, %o5; wr %g0, FPRS_FEF, %fprs
-#define VISExitHalf and %o5, FPRS_FEF, %o5; wr %o5, 0x0, %fprs
-#define SMALL_COPY_USES_FPU
-#define EXNV(x,y,a,b) x,y;
-#define EX(x,y,a,b) x,y;
-#define EX2(x,y) x,y;
-#define EX3(x,y) x,y;
-#define EX4(x,y) x,y;
-#endif
+
+ .register %g2,#scratch
+ .register %g3,#scratch
/* Special/non-trivial issues of this code:
*
* of up to 2.4GB per second.
*/
- .globl U3copy_from_user
-U3copy_from_user: /* %o0=dst, %o1=src, %o2=len */
-#ifndef __KERNEL__
- /* Save away original 'dst' for memcpy return value. */
- mov %o0, %g3 ! A0 Group
-#endif
- /* Anything to copy at all? */
- cmp %o2, 0 ! A1
- ble,pn %icc, U3copy_from_user_short_ret! BR
-
- /* Extremely small copy? */
- cmp %o2, 31 ! A0 Group
- ble,pn %icc, U3copy_from_user_short ! BR
-
- /* Large enough to use unrolled prefetch loops? */
- cmp %o2, 0x100 ! A1
- bge,a,pt %icc, U3copy_from_user_enter ! BR Group
- andcc %o0, 0x3f, %g2 ! A0
-
- ba,pt %xcc, U3copy_from_user_toosmall ! BR Group
- andcc %o0, 0x7, %g2 ! A0
-
- .align 32
-U3copy_from_user_short:
- /* Copy %o2 bytes from src to dst, one byte at a time. */
- EXNV(lduba [%o1 + 0x00] %asi, %o3, add %o2, %g0)! MS Group
- add %o1, 0x1, %o1 ! A0
- add %o0, 0x1, %o0 ! A1
- subcc %o2, 1, %o2 ! A0 Group
-
- bg,pt %icc, U3copy_from_user_short ! BR
- stb %o3, [%o0 + -1] ! MS Group (1-cycle stall)
-
-U3copy_from_user_short_ret:
-#ifdef __KERNEL__
- retl ! BR Group (0-4 cycle stall)
- clr %o0 ! A0
-#else
- retl ! BR Group (0-4 cycle stall)
- mov %g3, %o0 ! A0
-#endif
-
- /* Here len >= (6 * 64) and condition codes reflect execution
+ .globl U3copy_from_user
+U3copy_from_user: /* %o0=dst, %o1=src, %o2=len */
+ cmp %o2, 0
+ be,pn %XCC, 85f
+ or %o0, %o1, %o3
+ cmp %o2, 16
+ bleu,a,pn %XCC, 80f
+ or %o3, %o2, %o3
+
+ cmp %o2, 256
+ blu,pt %XCC, 70f
+ andcc %o3, 0x7, %g0
+
+ ba,pt %xcc, 1f
+ andcc %o0, 0x3f, %g2
+
+ /* Here len >= 256 and condition codes reflect execution
* of "andcc %o0, 0x7, %g2", done by caller.
*/
.align 64
-U3copy_from_user_enter:
+1:
/* Is 'dst' already aligned on an 64-byte boundary? */
- be,pt %xcc, 2f ! BR
+ be,pt %XCC, 2f
/* Compute abs((dst & 0x3f) - 0x40) into %g2. This is the number
* of bytes to copy to make 'dst' 64-byte aligned. We pre-
* subtract this from 'len'.
*/
- sub %g2, 0x40, %g2 ! A0 Group
- sub %g0, %g2, %g2 ! A0 Group
- sub %o2, %g2, %o2 ! A0 Group
+ sub %g2, 0x40, %g2
+ sub %g0, %g2, %g2
+ sub %o2, %g2, %o2
/* Copy %g2 bytes from src to dst, one byte at a time. */
-1: EXNV(lduba [%o1 + 0x00] %asi, %o3, add %o2, %g2)! MS (Group)
- add %o1, 0x1, %o1 ! A1
- add %o0, 0x1, %o0 ! A0 Group
- subcc %g2, 0x1, %g2 ! A1
+1: EXNV_RAW(lduba [%o1 + 0x00] %asi, %o3, add %o2, %g2)
+ add %o1, 0x1, %o1
+ add %o0, 0x1, %o0
+ subcc %g2, 0x1, %g2
- bg,pt %icc, 1b ! BR Group
- stb %o3, [%o0 + -1] ! MS Group
+ bg,pt %XCC, 1b
+ stb %o3, [%o0 + -1]
-2: VISEntryHalf ! MS+MS
- and %o1, 0x7, %g1 ! A1
- ba,pt %xcc, U3copy_from_user_begin ! BR
- alignaddr %o1, %g0, %o1 ! MS (Break-after)
+2: VISEntryHalf
+ and %o1, 0x7, %g1
+ ba,pt %xcc, 1f
+ alignaddr %o1, %g0, %o1
.align 64
-U3copy_from_user_begin:
-#ifdef __KERNEL__
- .globl U3copy_from_user_nop_1_6
-U3copy_from_user_nop_1_6:
- ldxa [%g0] ASI_DCU_CONTROL_REG, %g3
- sethi %uhi(DCU_PE), %o3
- sllx %o3, 32, %o3
- or %g3, %o3, %o3
- stxa %o3, [%g0] ASI_DCU_CONTROL_REG ! Enable P-cache
- membar #Sync
-#endif
- prefetcha [%o1 + 0x000] %asi, #one_read ! MS Group1
- prefetcha [%o1 + 0x040] %asi, #one_read ! MS Group2
- andn %o2, (0x40 - 1), %o4 ! A0
- prefetcha [%o1 + 0x080] %asi, #one_read ! MS Group3
- cmp %o4, 0x140 ! A0
- prefetcha [%o1 + 0x0c0] %asi, #one_read ! MS Group4
- EX(ldda [%o1 + 0x000] %asi, %f0, add %o2, %g0) ! MS Group5 (%f0 results at G8)
- bge,a,pt %icc, 1f ! BR
-
- prefetcha [%o1 + 0x100] %asi, #one_read ! MS Group6
-1: EX(ldda [%o1 + 0x008] %asi, %f2, add %o2, %g0) ! AX (%f2 results at G9)
- cmp %o4, 0x180 ! A1
- bge,a,pt %icc, 1f ! BR
- prefetcha [%o1 + 0x140] %asi, #one_read ! MS Group7
-1: EX(ldda [%o1 + 0x010] %asi, %f4, add %o2, %g0) ! AX (%f4 results at G10)
- cmp %o4, 0x1c0 ! A1
- bge,a,pt %icc, 1f ! BR
-
- prefetcha [%o1 + 0x180] %asi, #one_read ! MS Group8
-1: faligndata %f0, %f2, %f16 ! FGA Group9 (%f16 at G12)
- EX(ldda [%o1 + 0x018] %asi, %f6, add %o2, %g0) ! AX (%f6 results at G12)
- faligndata %f2, %f4, %f18 ! FGA Group10 (%f18 results at G13)
- EX(ldda [%o1 + 0x020] %asi, %f8, add %o2, %g0) ! MS (%f8 results at G13)
- faligndata %f4, %f6, %f20 ! FGA Group12 (1-cycle stall,%f20 at G15)
- EX(ldda [%o1 + 0x028] %asi, %f10, add %o2, %g0) ! MS (%f10 results at G15)
- faligndata %f6, %f8, %f22 ! FGA Group13 (%f22 results at G16)
-
- EX(ldda [%o1 + 0x030] %asi, %f12, add %o2, %g0) ! MS (%f12 results at G16)
- faligndata %f8, %f10, %f24 ! FGA Group15 (1-cycle stall,%f24 at G18)
- EX(ldda [%o1 + 0x038] %asi, %f14, add %o2, %g0) ! MS (%f14 results at G18)
- faligndata %f10, %f12, %f26 ! FGA Group16 (%f26 results at G19)
- EX(ldda [%o1 + 0x040] %asi, %f0, add %o2, %g0) ! MS (%f0 results at G19)
-
- /* We only use the first loop if len > (7 * 64). */
- subcc %o4, 0x1c0, %o4 ! A0 Group17
- bg,pt %icc, U3copy_from_user_loop1 ! BR
- add %o1, 0x40, %o1 ! A1
-
- add %o4, 0x140, %o4 ! A0 Group18
- ba,pt %xcc, U3copy_from_user_loop2 ! BR
- srl %o4, 6, %o3 ! A0 Group19
- nop
- nop
- nop
- nop
- nop
-
- nop
- nop
-
- /* This loop performs the copy and queues new prefetches.
- * We drop into the second loop when len <= (5 * 64). Note
- * that this (5 * 64) factor has been subtracted from len
- * already.
- */
-U3copy_from_user_loop1:
- EX2(ldda [%o1 + 0x008] %asi, %f2) ! MS Group2 (%f2 results at G5)
- faligndata %f12, %f14, %f28 ! FGA (%f28 results at G5)
- EX2(ldda [%o1 + 0x010] %asi, %f4) ! MS Group3 (%f4 results at G6)
- faligndata %f14, %f0, %f30 ! FGA Group4 (1-cycle stall, %f30 at G7)
- stda %f16, [%o0] ASI_BLK_P ! MS
- EX2(ldda [%o1 + 0x018] %asi, %f6) ! AX (%f6 results at G7)
-
- faligndata %f0, %f2, %f16 ! FGA Group12 (7-cycle stall)
- EX2(ldda [%o1 + 0x020] %asi, %f8) ! MS (%f8 results at G15)
- faligndata %f2, %f4, %f18 ! FGA Group13 (%f18 results at G16)
- EX2(ldda [%o1 + 0x028] %asi, %f10) ! MS (%f10 results at G16)
- faligndata %f4, %f6, %f20 ! FGA Group14 (%f20 results at G17)
- EX2(ldda [%o1 + 0x030] %asi, %f12) ! MS (%f12 results at G17)
- faligndata %f6, %f8, %f22 ! FGA Group15 (%f22 results at G18)
- EX2(ldda [%o1 + 0x038] %asi, %f14) ! MS (%f14 results at G18)
-
- faligndata %f8, %f10, %f24 ! FGA Group16 (%f24 results at G19)
- EX2(ldda [%o1 + 0x040] %asi, %f0) ! AX (%f0 results at G19)
- prefetcha [%o1 + 0x180] %asi, #one_read ! MS
- faligndata %f10, %f12, %f26 ! FGA Group17 (%f26 results at G20)
- subcc %o4, 0x40, %o4 ! A0
- add %o1, 0x40, %o1 ! A1
- bg,pt %xcc, U3copy_from_user_loop1 ! BR
- add %o0, 0x40, %o0 ! A0 Group18
-
-U3copy_from_user_loop2_enter:
- mov 5, %o3 ! A1
-
- /* This loop performs on the copy, no new prefetches are
- * queued. We do things this way so that we do not perform
- * any spurious prefetches past the end of the src buffer.
- */
-U3copy_from_user_loop2:
- EX3(ldda [%o1 + 0x008] %asi, %f2) ! MS
- faligndata %f12, %f14, %f28 ! FGA Group2
- EX3(ldda [%o1 + 0x010] %asi, %f4) ! MS
- faligndata %f14, %f0, %f30 ! FGA Group4 (1-cycle stall)
- stda %f16, [%o0] ASI_BLK_P ! MS
- EX3(ldda [%o1 + 0x018] %asi, %f6) ! AX
- faligndata %f0, %f2, %f16 ! FGA Group12 (7-cycle stall)
-
- EX3(ldda [%o1 + 0x020] %asi, %f8) ! MS
- faligndata %f2, %f4, %f18 ! FGA Group13
- EX3(ldda [%o1 + 0x028] %asi, %f10) ! MS
- faligndata %f4, %f6, %f20 ! FGA Group14
- EX3(ldda [%o1 + 0x030] %asi, %f12) ! MS
- faligndata %f6, %f8, %f22 ! FGA Group15
- EX3(ldda [%o1 + 0x038] %asi, %f14) ! MS
- faligndata %f8, %f10, %f24 ! FGA Group16
-
- EX3(ldda [%o1 + 0x040] %asi, %f0) ! AX
- faligndata %f10, %f12, %f26 ! FGA Group17
- subcc %o3, 0x01, %o3 ! A0
- add %o1, 0x40, %o1 ! A1
- bg,pt %xcc, U3copy_from_user_loop2 ! BR
- add %o0, 0x40, %o0 ! A0 Group18
+1:
+ membar #StoreLoad | #StoreStore | #LoadStore
+ prefetcha [%o1 + 0x000] %asi, #one_read
+ prefetcha [%o1 + 0x040] %asi, #one_read
+ andn %o2, (0x40 - 1), %o4
+ prefetcha [%o1 + 0x080] %asi, #one_read
+ prefetcha [%o1 + 0x0c0] %asi, #one_read
+ EX(ldda [%o1 + 0x000] %asi, %f0, add %o2, %g0)
+ prefetcha [%o1 + 0x100] %asi, #one_read
+ EX(ldda [%o1 + 0x008] %asi, %f2, add %o2, %g0)
+ prefetcha [%o1 + 0x140] %asi, #one_read
+ EX(ldda [%o1 + 0x010] %asi, %f4, add %o2, %g0)
+ prefetcha [%o1 + 0x180] %asi, #one_read
+ faligndata %f0, %f2, %f16
+ EX(ldda [%o1 + 0x018] %asi, %f6, add %o2, %g0)
+ faligndata %f2, %f4, %f18
+ EX(ldda [%o1 + 0x020] %asi, %f8, add %o2, %g0)
+ faligndata %f4, %f6, %f20
+ EX(ldda [%o1 + 0x028] %asi, %f10, add %o2, %g0)
+ faligndata %f6, %f8, %f22
+
+ EX(ldda [%o1 + 0x030] %asi, %f12, add %o2, %g0)
+ faligndata %f8, %f10, %f24
+ EX(ldda [%o1 + 0x038] %asi, %f14, add %o2, %g0)
+ faligndata %f10, %f12, %f26
+ EX(ldda [%o1 + 0x040] %asi, %f0, add %o2, %g0)
+
+ sub %o4, 0x80, %o4
+ add %o1, 0x40, %o1
+ ba,pt %xcc, 1f
+ srl %o4, 6, %o3
+
+ .align 64
+1:
+ EX3(ldda [%o1 + 0x008] %asi, %f2)
+ faligndata %f12, %f14, %f28
+ EX3(ldda [%o1 + 0x010] %asi, %f4)
+ faligndata %f14, %f0, %f30
+ stda %f16, [%o0] ASI_BLK_P
+ EX3(ldda [%o1 + 0x018] %asi, %f6)
+ faligndata %f0, %f2, %f16
+
+ EX3(ldda [%o1 + 0x020] %asi, %f8)
+ faligndata %f2, %f4, %f18
+ EX3(ldda [%o1 + 0x028] %asi, %f10)
+ faligndata %f4, %f6, %f20
+ EX3(ldda [%o1 + 0x030] %asi, %f12)
+ faligndata %f6, %f8, %f22
+ EX3(ldda [%o1 + 0x038] %asi, %f14)
+ faligndata %f8, %f10, %f24
+
+ EX3(ldda [%o1 + 0x040] %asi, %f0)
+ prefetcha [%o1 + 0x180] %asi, #one_read
+ faligndata %f10, %f12, %f26
+ subcc %o3, 0x01, %o3
+ add %o1, 0x40, %o1
+ bg,pt %XCC, 1b
+ add %o0, 0x40, %o0
/* Finally we copy the last full 64-byte block. */
-U3copy_from_user_loopfini:
- EX3(ldda [%o1 + 0x008] %asi, %f2) ! MS
- faligndata %f12, %f14, %f28 ! FGA
- EX3(ldda [%o1 + 0x010] %asi, %f4) ! MS Group19
- faligndata %f14, %f0, %f30 ! FGA
- stda %f16, [%o0] ASI_BLK_P ! MS Group20
- EX3(ldda [%o1 + 0x018] %asi, %f6) ! AX
- faligndata %f0, %f2, %f16 ! FGA Group11 (7-cycle stall)
- EX3(ldda [%o1 + 0x020] %asi, %f8) ! MS
- faligndata %f2, %f4, %f18 ! FGA Group12
- EX3(ldda [%o1 + 0x028] %asi, %f10) ! MS
- faligndata %f4, %f6, %f20 ! FGA Group13
- EX3(ldda [%o1 + 0x030] %asi, %f12) ! MS
- faligndata %f6, %f8, %f22 ! FGA Group14
- EX3(ldda [%o1 + 0x038] %asi, %f14) ! MS
- faligndata %f8, %f10, %f24 ! FGA Group15
- cmp %g1, 0 ! A0
- be,pt %icc, 1f ! BR
- add %o0, 0x40, %o0 ! A1
- EX4(ldda [%o1 + 0x040] %asi, %f0) ! MS
-1: faligndata %f10, %f12, %f26 ! FGA Group16
- faligndata %f12, %f14, %f28 ! FGA Group17
- faligndata %f14, %f0, %f30 ! FGA Group18
- stda %f16, [%o0] ASI_BLK_P ! MS
- add %o0, 0x40, %o0 ! A0
- add %o1, 0x40, %o1 ! A1
-#ifdef __KERNEL__
- .globl U3copy_from_user_nop_2_3
-U3copy_from_user_nop_2_3:
- mov PRIMARY_CONTEXT, %o3
- stxa %g0, [%o3] ASI_DMMU ! Flush P-cache
- stxa %g3, [%g0] ASI_DCU_CONTROL_REG ! Disable P-cache
-#endif
- membar #Sync ! MS Group26 (7-cycle stall)
+ EX3(ldda [%o1 + 0x008] %asi, %f2)
+ faligndata %f12, %f14, %f28
+ EX3(ldda [%o1 + 0x010] %asi, %f4)
+ faligndata %f14, %f0, %f30
+ stda %f16, [%o0] ASI_BLK_P
+ EX3(ldda [%o1 + 0x018] %asi, %f6)
+ faligndata %f0, %f2, %f16
+ EX3(ldda [%o1 + 0x020] %asi, %f8)
+ faligndata %f2, %f4, %f18
+ EX3(ldda [%o1 + 0x028] %asi, %f10)
+ faligndata %f4, %f6, %f20
+ EX3(ldda [%o1 + 0x030] %asi, %f12)
+ faligndata %f6, %f8, %f22
+ EX3(ldda [%o1 + 0x038] %asi, %f14)
+ faligndata %f8, %f10, %f24
+ cmp %g1, 0
+ be,pt %XCC, 1f
+ add %o0, 0x40, %o0
+ EX4(ldda [%o1 + 0x040] %asi, %f0)
+1: faligndata %f10, %f12, %f26
+ faligndata %f12, %f14, %f28
+ faligndata %f14, %f0, %f30
+ stda %f16, [%o0] ASI_BLK_P
+ add %o0, 0x40, %o0
+ add %o1, 0x40, %o1
+
+ membar #Sync
/* Now we copy the (len modulo 64) bytes at the end.
* Note how we borrow the %f0 loaded above.
*
* Also notice how this code is careful not to perform a
- * load past the end of the src buffer just like similar
- * code found in U3copy_from_user_toosmall processing.
+ * load past the end of the src buffer.
*/
-U3copy_from_user_loopend:
- and %o2, 0x3f, %o2 ! A0 Group
- andcc %o2, 0x38, %g2 ! A0 Group
- be,pn %icc, U3copy_from_user_endcruft ! BR
- subcc %g2, 0x8, %g2 ! A1
- be,pn %icc, U3copy_from_user_endcruft ! BR Group
- cmp %g1, 0 ! A0
-
- be,a,pt %icc, 1f ! BR Group
- EX(ldda [%o1 + 0x00] %asi, %f0, add %o2, %g0) ! MS
-
-1: EX(ldda [%o1 + 0x08] %asi, %f2, add %o2, %g0) ! MS Group
- add %o1, 0x8, %o1 ! A0
- sub %o2, 0x8, %o2 ! A1
- subcc %g2, 0x8, %g2 ! A0 Group
- faligndata %f0, %f2, %f8 ! FGA Group
- std %f8, [%o0 + 0x00] ! MS (XXX does it stall here? XXX)
- be,pn %icc, U3copy_from_user_endcruft ! BR
- add %o0, 0x8, %o0 ! A0
- EX(ldda [%o1 + 0x08] %asi, %f0, add %o2, %g0) ! MS Group
- add %o1, 0x8, %o1 ! A0
- sub %o2, 0x8, %o2 ! A1
- subcc %g2, 0x8, %g2 ! A0 Group
- faligndata %f2, %f0, %f8 ! FGA
- std %f8, [%o0 + 0x00] ! MS (XXX does it stall here? XXX)
- bne,pn %icc, 1b ! BR
- add %o0, 0x8, %o0 ! A0 Group
+ and %o2, 0x3f, %o2
+ andcc %o2, 0x38, %g2
+ be,pn %XCC, 10f
+ subcc %g2, 0x8, %g2
+ be,pn %XCC, 10f
+ cmp %g1, 0
+
+ be,a,pt %XCC, 1f
+ EX(ldda [%o1 + 0x00] %asi, %f0, add %o2, %g0)
+
+1: EX(ldda [%o1 + 0x08] %asi, %f2, add %o2, %g0)
+ add %o1, 0x8, %o1
+ sub %o2, 0x8, %o2
+ subcc %g2, 0x8, %g2
+ faligndata %f0, %f2, %f8
+ std %f8, [%o0 + 0x00]
+ be,pn %XCC, 10f
+ add %o0, 0x8, %o0
+ EX(ldda [%o1 + 0x08] %asi, %f0, add %o2, %g0)
+ add %o1, 0x8, %o1
+ sub %o2, 0x8, %o2
+ subcc %g2, 0x8, %g2
+ faligndata %f2, %f0, %f8
+ std %f8, [%o0 + 0x00]
+ bne,pn %XCC, 1b
+ add %o0, 0x8, %o0
/* If anything is left, we copy it one byte at a time.
* Note that %g1 is (src & 0x3) saved above before the
* alignaddr was performed.
*/
-U3copy_from_user_endcruft:
+10:
cmp %o2, 0
add %o1, %g1, %o1
VISExitHalf
- be,pn %icc, U3copy_from_user_short_ret
- nop
- ba,a,pt %xcc, U3copy_from_user_short
-
- /* If we get here, then 32 <= len < (6 * 64) */
-U3copy_from_user_toosmall:
-
-#ifdef SMALL_COPY_USES_FPU
-
- /* Is 'dst' already aligned on an 8-byte boundary? */
- be,pt %xcc, 2f ! BR Group
-
- /* Compute abs((dst & 7) - 8) into %g2. This is the number
- * of bytes to copy to make 'dst' 8-byte aligned. We pre-
- * subtract this from 'len'.
- */
- sub %g2, 0x8, %g2 ! A0
- sub %g0, %g2, %g2 ! A0 Group (reg-dep)
- sub %o2, %g2, %o2 ! A0 Group (reg-dep)
+ be,pn %XCC, 85f
+ sub %o0, %o1, %o3
- /* Copy %g2 bytes from src to dst, one byte at a time. */
-1: EXNV(lduba [%o1 + 0x00] %asi, %o3, add %o2, %g2)! MS (Group) (%o3 in 3 cycles)
- add %o1, 0x1, %o1 ! A1
- add %o0, 0x1, %o0 ! A0 Group
- subcc %g2, 0x1, %g2 ! A1
-
- bg,pt %icc, 1b ! BR Group
- stb %o3, [%o0 + -1] ! MS Group
+ andcc %g1, 0x7, %g0
+ bne,pn %icc, 90f
+ andcc %o2, 0x8, %g0
+ be,pt %icc, 1f
+ nop
+ EXNV(ldxa [%o1] %asi, %o5, add %o2, %g0)
+ stx %o5, [%o1 + %o3]
+ add %o1, 0x8, %o1
-2: VISEntryHalf ! MS+MS
+1: andcc %o2, 0x4, %g0
+ be,pt %icc, 1f
+ nop
+ EXNV(lduwa [%o1] %asi, %o5, and %o2, 0x7)
+ stw %o5, [%o1 + %o3]
+ add %o1, 0x4, %o1
- /* Compute (len - (len % 8)) into %g2. This is guaranteed
- * to be nonzero.
- */
- andn %o2, 0x7, %g2 ! A0 Group
-
- /* You may read this and believe that it allows reading
- * one 8-byte longword past the end of src. It actually
- * does not, as %g2 is subtracted as loads are done from
- * src, so we always stop before running off the end.
- * Also, we are guaranteed to have at least 0x10 bytes
- * to move here.
- */
- sub %g2, 0x8, %g2 ! A0 Group (reg-dep)
- alignaddr %o1, %g0, %g1 ! MS (Break-after)
- EX(ldda [%g1 + 0x00] %asi, %f0, add %o2, %g0) ! MS Group (1-cycle stall)
- add %g1, 0x8, %g1 ! A0
-
-1: EX(ldda [%g1 + 0x00] %asi, %f2, add %o2, %g0) ! MS Group
- add %g1, 0x8, %g1 ! A0
- sub %o2, 0x8, %o2 ! A1
- subcc %g2, 0x8, %g2 ! A0 Group
-
- faligndata %f0, %f2, %f8 ! FGA Group (1-cycle stall)
- std %f8, [%o0 + 0x00] ! MS Group (2-cycle stall)
- add %o1, 0x8, %o1 ! A0
- be,pn %icc, 2f ! BR
-
- add %o0, 0x8, %o0 ! A1
- EX(ldda [%g1 + 0x00] %asi, %f0, add %o2, %g0) ! MS Group
- add %g1, 0x8, %g1 ! A0
- sub %o2, 0x8, %o2 ! A1
-
- subcc %g2, 0x8, %g2 ! A0 Group
- faligndata %f2, %f0, %f8 ! FGA Group (1-cycle stall)
- std %f8, [%o0 + 0x00] ! MS Group (2-cycle stall)
- add %o1, 0x8, %o1 ! A0
-
- bne,pn %icc, 1b ! BR
- add %o0, 0x8, %o0 ! A1
-
- /* Nothing left to copy? */
-2: cmp %o2, 0 ! A0 Group
- VISExitHalf ! A0+MS
- be,pn %icc, U3copy_from_user_short_ret! BR Group
- nop ! A0
- ba,a,pt %xcc, U3copy_from_user_short ! BR Group
-
-#else /* !(SMALL_COPY_USES_FPU) */
-
- xor %o1, %o0, %g2
- andcc %g2, 0x7, %g0
- bne,pn %icc, U3copy_from_user_short
- andcc %o1, 0x7, %g2
-
- be,pt %xcc, 2f
- sub %g2, 0x8, %g2
- sub %g0, %g2, %g2
- sub %o2, %g2, %o2
+1: andcc %o2, 0x2, %g0
+ be,pt %icc, 1f
+ nop
+ EXNV(lduha [%o1] %asi, %o5, and %o2, 0x3)
+ sth %o5, [%o1 + %o3]
+ add %o1, 0x2, %o1
-1: EXNV(lduba [%o1 + 0x00] %asi, %o3, add %o2, %g2)
- add %o1, 0x1, %o1
- add %o0, 0x1, %o0
- subcc %g2, 0x1, %g2
- bg,pt %icc, 1b
- stb %o3, [%o0 + -1]
+1: andcc %o2, 0x1, %g0
+ be,pt %icc, 85f
+ nop
+ EXNV(lduba [%o1] %asi, %o5, and %o2, 0x1)
+ ba,pt %xcc, 85f
+ stb %o5, [%o1 + %o3]
+
+70: /* 16 < len <= 64 */
+ bne,pn %XCC, 90f
+ sub %o0, %o1, %o3
+
+ andn %o2, 0x7, %o4
+ and %o2, 0x7, %o2
+1: subcc %o4, 0x8, %o4
+ EXNV8(ldxa [%o1] %asi, %o5, add %o2, %o4)
+ stx %o5, [%o1 + %o3]
+ bgu,pt %XCC, 1b
+ add %o1, 0x8, %o1
+ andcc %o2, 0x4, %g0
+ be,pt %XCC, 1f
+ nop
+ sub %o2, 0x4, %o2
+ EXNV4(lduwa [%o1] %asi, %o5, add %o2, %g0)
+ stw %o5, [%o1 + %o3]
+ add %o1, 0x4, %o1
+1: cmp %o2, 0
+ be,pt %XCC, 85f
+ nop
+ ba,pt %xcc, 90f
+ nop
-2: andn %o2, 0x7, %g2
- sub %o2, %g2, %o2
+80: /* 0 < len <= 16 */
+ andcc %o3, 0x3, %g0
+ bne,pn %XCC, 90f
+ sub %o0, %o1, %o3
-3: EXNV(ldxa [%o1 + 0x00] %asi, %o3, add %o2, %g2)
- add %o1, 0x8, %o1
- add %o0, 0x8, %o0
- subcc %g2, 0x8, %g2
- bg,pt %icc, 3b
- stx %o3, [%o0 + -8]
+1:
+ subcc %o2, 4, %o2
+ EXNV(lduwa [%o1] %asi, %g1, add %o2, %g0)
+ stw %g1, [%o1 + %o3]
+ bgu,pt %XCC, 1b
+ add %o1, 4, %o1
- cmp %o2, 0
- bne,pn %icc, U3copy_from_user_short
- nop
- ba,a,pt %xcc, U3copy_from_user_short_ret
+85: retl
+ clr %o0
-#endif /* !(SMALL_COPY_USES_FPU) */
+ .align 32
+90:
+ subcc %o2, 1, %o2
+ EXNV(lduba [%o1] %asi, %g1, add %o2, %g0)
+ stb %g1, [%o1 + %o3]
+ bgu,pt %XCC, 90b
+ add %o1, 1, %o1
+ retl
+ clr %o0
-#ifdef __KERNEL__
- .globl U3cfu_fixup
U3cfu_fixup:
/* Since this is copy_from_user(), zero out the rest of the
* kernel buffer.
2: retl
mov %o1, %o0
-#endif
-/* $Id: U3copy_in_user.S,v 1.4 2001/03/21 05:58:47 davem Exp $
- * U3memcpy.S: UltraSparc-III optimized copy within userspace.
+/* U3copy_in_user.S: UltraSparc-III optimized memcpy.
*
- * Copyright (C) 1999, 2000 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1999, 2000, 2004 David S. Miller (davem@redhat.com)
*/
-#ifdef __KERNEL__
#include <asm/visasm.h>
#include <asm/asi.h>
-#undef SMALL_COPY_USES_FPU
+#include <asm/dcu.h>
+#include <asm/spitfire.h>
+
+#define XCC xcc
+
#define EXNV(x,y,a,b) \
98: x,y; \
.section .fixup; \
.word 98b, 99b; \
.text; \
.align 4;
-#define EXNV2(x,y,a,b) \
+#define EXNV1(x,y,a,b) \
98: x,y; \
.section .fixup; \
.align 4; \
.word 98b, 99b; \
.text; \
.align 4;
-#define EXNV3(x,y,a,b) \
+#define EXNV4(x,y,a,b) \
98: x,y; \
.section .fixup; \
.align 4; \
99: a, b, %o0; \
retl; \
- add %o0, 8, %o0; \
- .section __ex_table; \
- .align 4; \
- .word 98b, 99b; \
- .text; \
- .align 4;
-#define EX(x,y,a,b) \
-98: x,y; \
- .section .fixup; \
- .align 4; \
-99: VISExitHalf; \
- retl; \
- a, b, %o0; \
+ add %o0, 4, %o0; \
.section __ex_table; \
.align 4; \
.word 98b, 99b; \
.text; \
.align 4;
-#define EXBLK1(x,y) \
+#define EXNV8(x,y,a,b) \
98: x,y; \
.section .fixup; \
.align 4; \
-99: VISExitHalf; \
- add %o4, 0x1c0, %o1; \
- and %o2, (0x40 - 1), %o2; \
- retl; \
- add %o1, %o2, %o0; \
- .section __ex_table; \
- .align 4; \
- .word 98b, 99b; \
- .text; \
- .align 4;
-#define EXBLK2(x,y) \
-98: x,y; \
- .section .fixup; \
- .align 4; \
-99: VISExitHalf; \
- sll %o3, 6, %o3; \
- and %o2, (0x40 - 1), %o2; \
- add %o3, 0x80, %o1; \
- retl; \
- add %o1, %o2, %o0; \
- .section __ex_table; \
- .align 4; \
- .word 98b, 99b; \
- .text; \
- .align 4;
-#define EXBLK3(x,y) \
-98: x,y; \
- .section .fixup; \
- .align 4; \
-99: VISExitHalf; \
- and %o2, (0x40 - 1), %o2; \
- retl; \
- add %o2, 0x80, %o0; \
- .section __ex_table; \
- .align 4; \
- .word 98b, 99b; \
- .text; \
- .align 4;
-#define EXBLK4(x,y) \
-98: x,y; \
- .section .fixup; \
- .align 4; \
-99: VISExitHalf; \
- and %o2, (0x40 - 1), %o2; \
+99: a, b, %o0; \
retl; \
- add %o2, 0x40, %o0; \
+ add %o0, 8, %o0; \
.section __ex_table; \
.align 4; \
.word 98b, 99b; \
.text; \
.align 4;
-#else
-#define ASI_AIUS 0x80
-#define ASI_BLK_AIUS 0xf0
-#define FPRS_FEF 0x04
-#define VISEntryHalf rd %fprs, %o5; wr %g0, FPRS_FEF, %fprs
-#define VISExitHalf and %o5, FPRS_FEF, %o5; wr %o5, 0x0, %fprs
-#define SMALL_COPY_USES_FPU
-#define EXNV(x,y,a,b) x,y;
-#define EXNV2(x,y,a,b) x,y;
-#define EXNV3(x,y,a,b) x,y;
-#define EX(x,y,a,b) x,y;
-#define EXBLK1(x,y) x,y;
-#define EXBLK2(x,y) x,y;
-#define EXBLK3(x,y) x,y;
-#define EXBLK4(x,y) x,y;
-#endif
- /* Special/non-trivial issues of this code:
- *
- * 1) %o5 is preserved from VISEntryHalf to VISExitHalf
- * 2) Only low 32 FPU registers are used so that only the
- * lower half of the FPU register set is dirtied by this
- * code. This is especially important in the kernel.
- * 3) This code never prefetches cachelines past the end
- * of the source buffer.
- *
- * XXX Actually, Cheetah can buffer up to 8 concurrent
- * XXX prefetches, revisit this...
- */
+ .register %g2,#scratch
+ .register %g3,#scratch
.text
.align 32
- /* The cheetah's flexible spine, oversized liver, enlarged heart,
- * slender muscular body, and claws make it the swiftest hunter
- * in Africa and the fastest animal on land. Can reach speeds
- * of up to 2.4GB per second.
+ /* Don't try to get too fancy here, just nice and
+ * simple. This is predominantly used for well aligned
+ * small copies in the compat layer. It is also used
+ * to copy register windows around during thread cloning.
*/
- .globl U3copy_in_user
-U3copy_in_user: /* %o0=dst, %o1=src, %o2=len */
+ .globl U3copy_in_user
+U3copy_in_user: /* %o0=dst, %o1=src, %o2=len */
/* Writing to %asi is _expensive_ so we hardcode it.
* Reading %asi to check for KERNEL_DS is comparatively
* cheap.
*/
- rd %asi, %g1 ! MS Group (4 cycles)
- cmp %g1, ASI_AIUS ! A0 Group
- bne U3memcpy ! BR
- nop ! A1
-#ifndef __KERNEL__
- /* Save away original 'dst' for memcpy return value. */
- mov %o0, %g3 ! A0 Group
-#endif
- /* Anything to copy at all? */
- cmp %o2, 0 ! A1
- ble,pn %icc, U3copy_in_user_short_ret ! BR
-
- /* Extremely small copy? */
- cmp %o2, 31 ! A0 Group
- ble,pn %icc, U3copy_in_user_short ! BR
-
- /* Large enough to use unrolled prefetch loops? */
- cmp %o2, 0x100 ! A1
- bge,a,pt %icc, U3copy_in_user_enter ! BR Group
- andcc %o0, 0x3f, %g2 ! A0
-
- ba,pt %xcc, U3copy_in_user_toosmall ! BR Group
- andcc %o0, 0x7, %g2 ! A0
-
- .align 32
-U3copy_in_user_short:
- /* Copy %o2 bytes from src to dst, one byte at a time. */
- EXNV(lduba [%o1 + 0x00] %asi, %o3, add %o2, %g0)! MS Group
- add %o1, 0x1, %o1 ! A0
- add %o0, 0x1, %o0 ! A1
- subcc %o2, 1, %o2 ! A0 Group
-
- bg,pt %icc, U3copy_in_user_short ! BR
- EXNV(stba %o3, [%o0 + -1] %asi, add %o2, 1) ! MS Group (1-cycle stall)
-
-U3copy_in_user_short_ret:
-#ifdef __KERNEL__
- retl ! BR Group (0-4 cycle stall)
- clr %o0 ! A0
-#else
- retl ! BR Group (0-4 cycle stall)
- mov %g3, %o0 ! A0
-#endif
-
- /* Here len >= (6 * 64) and condition codes reflect execution
- * of "andcc %o0, 0x7, %g2", done by caller.
- */
- .align 64
-U3copy_in_user_enter:
- /* Is 'dst' already aligned on an 64-byte boundary? */
- be,pt %xcc, 2f ! BR
-
- /* Compute abs((dst & 0x3f) - 0x40) into %g2. This is the number
- * of bytes to copy to make 'dst' 64-byte aligned. We pre-
- * subtract this from 'len'.
- */
- sub %g2, 0x40, %g2 ! A0 Group
- sub %g0, %g2, %g2 ! A0 Group
- sub %o2, %g2, %o2 ! A0 Group
-
- /* Copy %g2 bytes from src to dst, one byte at a time. */
-1: EXNV(lduba [%o1 + 0x00] %asi, %o3, add %o2, %g2)! MS (Group)
- add %o1, 0x1, %o1 ! A1
- add %o0, 0x1, %o0 ! A0 Group
- subcc %g2, 0x1, %g2 ! A1
-
- bg,pt %icc, 1b ! BR Group
- EXNV2(stba %o3, [%o0 + -1] %asi, add %o2, %g2) ! MS Group
-
-2: VISEntryHalf ! MS+MS
- and %o1, 0x7, %g1 ! A1
- ba,pt %xcc, U3copy_in_user_begin ! BR
- alignaddr %o1, %g0, %o1 ! MS (Break-after)
-
- .align 64
-U3copy_in_user_begin:
- prefetcha [%o1 + 0x000] %asi, #one_read ! MS Group1
- prefetcha [%o1 + 0x040] %asi, #one_read ! MS Group2
- andn %o2, (0x40 - 1), %o4 ! A0
- prefetcha [%o1 + 0x080] %asi, #one_read ! MS Group3
- cmp %o4, 0x140 ! A0
- prefetcha [%o1 + 0x0c0] %asi, #one_read ! MS Group4
- EX(ldda [%o1 + 0x000] %asi, %f0, add %o2, %g0) ! MS Group5 (%f0 results at G8)
- bge,a,pt %icc, 1f ! BR
-
- prefetcha [%o1 + 0x100] %asi, #one_read ! MS Group6
-1: EX(ldda [%o1 + 0x008] %asi, %f2, add %o2, %g0) ! AX (%f2 results at G9)
- cmp %o4, 0x180 ! A1
- bge,a,pt %icc, 1f ! BR
- prefetcha [%o1 + 0x140] %asi, #one_read ! MS Group7
-1: EX(ldda [%o1 + 0x010] %asi, %f4, add %o2, %g0) ! AX (%f4 results at G10)
- cmp %o4, 0x1c0 ! A1
- bge,a,pt %icc, 1f ! BR
-
- prefetcha [%o1 + 0x180] %asi, #one_read ! MS Group8
-1: faligndata %f0, %f2, %f16 ! FGA Group9 (%f16 at G12)
- EX(ldda [%o1 + 0x018] %asi, %f6, add %o2, %g0) ! AX (%f6 results at G12)
- faligndata %f2, %f4, %f18 ! FGA Group10 (%f18 results at G13)
- EX(ldda [%o1 + 0x020] %asi, %f8, add %o2, %g0) ! MS (%f8 results at G13)
- faligndata %f4, %f6, %f20 ! FGA Group12 (1-cycle stall,%f20 at G15)
- EX(ldda [%o1 + 0x028] %asi, %f10, add %o2, %g0) ! MS (%f10 results at G15)
- faligndata %f6, %f8, %f22 ! FGA Group13 (%f22 results at G16)
-
- EX(ldda [%o1 + 0x030] %asi, %f12, add %o2, %g0) ! MS (%f12 results at G16)
- faligndata %f8, %f10, %f24 ! FGA Group15 (1-cycle stall,%f24 at G18)
- EX(ldda [%o1 + 0x038] %asi, %f14, add %o2, %g0) ! MS (%f14 results at G18)
- faligndata %f10, %f12, %f26 ! FGA Group16 (%f26 results at G19)
- EX(ldda [%o1 + 0x040] %asi, %f0, add %o2, %g0) ! MS (%f0 results at G19)
-
- /* We only use the first loop if len > (7 * 64). */
- subcc %o4, 0x1c0, %o4 ! A0 Group17
- bg,pt %icc, U3copy_in_user_loop1 ! BR
- add %o1, 0x40, %o1 ! A1
-
- add %o4, 0x140, %o4 ! A0 Group18
- ba,pt %xcc, U3copy_in_user_loop2 ! BR
- srl %o4, 6, %o3 ! A0 Group19
- nop
- nop
- nop
- nop
- nop
-
- nop
- nop
-
- /* This loop performs the copy and queues new prefetches.
- * We drop into the second loop when len <= (5 * 64). Note
- * that this (5 * 64) factor has been subtracted from len
- * already.
- */
-U3copy_in_user_loop1:
- EXBLK1(ldda [%o1 + 0x008] %asi, %f2) ! MS Group2 (%f2 results at G5)
- faligndata %f12, %f14, %f28 ! FGA (%f28 results at G5)
- EXBLK1(ldda [%o1 + 0x010] %asi, %f4) ! MS Group3 (%f4 results at G6)
- faligndata %f14, %f0, %f30 ! FGA Group4 (1-cycle stall, %f30 at G7)
- EXBLK1(stda %f16, [%o0] ASI_BLK_AIUS) ! MS
- EXBLK1(ldda [%o1 + 0x018] %asi, %f6) ! AX (%f6 results at G7)
-
- faligndata %f0, %f2, %f16 ! FGA Group12 (7-cycle stall)
- EXBLK1(ldda [%o1 + 0x020] %asi, %f8) ! MS (%f8 results at G15)
- faligndata %f2, %f4, %f18 ! FGA Group13 (%f18 results at G16)
- EXBLK1(ldda [%o1 + 0x028] %asi, %f10) ! MS (%f10 results at G16)
- faligndata %f4, %f6, %f20 ! FGA Group14 (%f20 results at G17)
- EXBLK1(ldda [%o1 + 0x030] %asi, %f12) ! MS (%f12 results at G17)
- faligndata %f6, %f8, %f22 ! FGA Group15 (%f22 results at G18)
- EXBLK1(ldda [%o1 + 0x038] %asi, %f14) ! MS (%f14 results at G18)
-
- faligndata %f8, %f10, %f24 ! FGA Group16 (%f24 results at G19)
- EXBLK1(ldda [%o1 + 0x040] %asi, %f0) ! AX (%f0 results at G19)
- prefetcha [%o1 + 0x180] %asi, #one_read ! MS
- faligndata %f10, %f12, %f26 ! FGA Group17 (%f26 results at G20)
- subcc %o4, 0x40, %o4 ! A0
- add %o1, 0x40, %o1 ! A1
- bg,pt %xcc, U3copy_in_user_loop1 ! BR
- add %o0, 0x40, %o0 ! A0 Group18
-
-U3copy_in_user_loop2_enter:
- mov 5, %o3 ! A1
-
- /* This loop performs on the copy, no new prefetches are
- * queued. We do things this way so that we do not perform
- * any spurious prefetches past the end of the src buffer.
- */
-U3copy_in_user_loop2:
- EXBLK2(ldda [%o1 + 0x008] %asi, %f2) ! MS
- faligndata %f12, %f14, %f28 ! FGA Group2
- EXBLK2(ldda [%o1 + 0x010] %asi, %f4) ! MS
- faligndata %f14, %f0, %f30 ! FGA Group4 (1-cycle stall)
- EXBLK2(stda %f16, [%o0] ASI_BLK_AIUS) ! MS
- EXBLK2(ldda [%o1 + 0x018] %asi, %f6) ! AX
- faligndata %f0, %f2, %f16 ! FGA Group12 (7-cycle stall)
-
- EXBLK2(ldda [%o1 + 0x020] %asi, %f8) ! MS
- faligndata %f2, %f4, %f18 ! FGA Group13
- EXBLK2(ldda [%o1 + 0x028] %asi, %f10) ! MS
- faligndata %f4, %f6, %f20 ! FGA Group14
- EXBLK2(ldda [%o1 + 0x030] %asi, %f12) ! MS
- faligndata %f6, %f8, %f22 ! FGA Group15
- EXBLK2(ldda [%o1 + 0x038] %asi, %f14) ! MS
- faligndata %f8, %f10, %f24 ! FGA Group16
-
- EXBLK2(ldda [%o1 + 0x040] %asi, %f0) ! AX
- faligndata %f10, %f12, %f26 ! FGA Group17
- subcc %o3, 0x01, %o3 ! A0
- add %o1, 0x40, %o1 ! A1
- bg,pt %xcc, U3copy_in_user_loop2 ! BR
- add %o0, 0x40, %o0 ! A0 Group18
-
- /* Finally we copy the last full 64-byte block. */
-U3copy_in_user_loopfini:
- EXBLK3(ldda [%o1 + 0x008] %asi, %f2) ! MS
- faligndata %f12, %f14, %f28 ! FGA
- EXBLK3(ldda [%o1 + 0x010] %asi, %f4) ! MS Group19
- faligndata %f14, %f0, %f30 ! FGA
- EXBLK3(stda %f16, [%o0] ASI_BLK_AIUS) ! MS Group20
- EXBLK4(ldda [%o1 + 0x018] %asi, %f6) ! AX
- faligndata %f0, %f2, %f16 ! FGA Group11 (7-cycle stall)
- EXBLK4(ldda [%o1 + 0x020] %asi, %f8) ! MS
- faligndata %f2, %f4, %f18 ! FGA Group12
- EXBLK4(ldda [%o1 + 0x028] %asi, %f10) ! MS
- faligndata %f4, %f6, %f20 ! FGA Group13
- EXBLK4(ldda [%o1 + 0x030] %asi, %f12) ! MS
- faligndata %f6, %f8, %f22 ! FGA Group14
- EXBLK4(ldda [%o1 + 0x038] %asi, %f14) ! MS
- faligndata %f8, %f10, %f24 ! FGA Group15
- cmp %g1, 0 ! A0
- be,pt %icc, 1f ! BR
- add %o0, 0x40, %o0 ! A1
- EXBLK4(ldda [%o1 + 0x040] %asi, %f0) ! MS
-1: faligndata %f10, %f12, %f26 ! FGA Group16
- faligndata %f12, %f14, %f28 ! FGA Group17
- faligndata %f14, %f0, %f30 ! FGA Group18
- EXBLK4(stda %f16, [%o0] ASI_BLK_AIUS) ! MS
- add %o0, 0x40, %o0 ! A0
- add %o1, 0x40, %o1 ! A1
- membar #Sync ! MS Group26 (7-cycle stall)
-
- /* Now we copy the (len modulo 64) bytes at the end.
- * Note how we borrow the %f0 loaded above.
- *
- * Also notice how this code is careful not to perform a
- * load past the end of the src buffer just like similar
- * code found in U3copy_in_user_toosmall processing.
- */
-U3copy_in_user_loopend:
- and %o2, 0x3f, %o2 ! A0 Group
- andcc %o2, 0x38, %g2 ! A0 Group
- be,pn %icc, U3copy_in_user_endcruft ! BR
- subcc %g2, 0x8, %g2 ! A1
- be,pn %icc, U3copy_in_user_endcruft ! BR Group
- cmp %g1, 0 ! A0
-
- be,a,pt %icc, 1f ! BR Group
- EX(ldda [%o1 + 0x00] %asi, %f0, add %o2, %g0) ! MS
-
-1: EX(ldda [%o1 + 0x08] %asi, %f2, add %o2, %g0) ! MS Group
- add %o1, 0x8, %o1 ! A0
- sub %o2, 0x8, %o2 ! A1
- subcc %g2, 0x8, %g2 ! A0 Group
- faligndata %f0, %f2, %f8 ! FGA Group
- EX(stda %f8, [%o0 + 0x00] %asi, add %o2, 0x8) ! MS (XXX does it stall here? XXX)
- be,pn %icc, U3copy_in_user_endcruft ! BR
- add %o0, 0x8, %o0 ! A0
- EX(ldda [%o1 + 0x08] %asi, %f0, add %o2, %g0) ! MS Group
- add %o1, 0x8, %o1 ! A0
- sub %o2, 0x8, %o2 ! A1
- subcc %g2, 0x8, %g2 ! A0 Group
- faligndata %f2, %f0, %f8 ! FGA
- EX(stda %f8, [%o0 + 0x00] %asi, add %o2, 0x8) ! MS (XXX does it stall here? XXX)
- bne,pn %icc, 1b ! BR
- add %o0, 0x8, %o0 ! A0 Group
+ rd %asi, %g1
+ cmp %g1, ASI_AIUS
+ bne,pn %icc, U3memcpy_user_stub
+ nop
- /* If anything is left, we copy it one byte at a time.
- * Note that %g1 is (src & 0x3) saved above before the
- * alignaddr was performed.
- */
-U3copy_in_user_endcruft:
cmp %o2, 0
- add %o1, %g1, %o1
- VISExitHalf
- be,pn %icc, U3copy_in_user_short_ret
+ be,pn %XCC, out
+ or %o0, %o1, %o3
+ cmp %o2, 16
+ bleu,a,pn %XCC, small_copy
+ or %o3, %o2, %o3
+
+medium_copy: /* 16 < len <= 64 */
+ andcc %o3, 0x7, %g0
+ bne,pn %XCC, small_copy_unaligned
+ sub %o0, %o1, %o3
+
+medium_copy_aligned:
+ andn %o2, 0x7, %o4
+ and %o2, 0x7, %o2
+1: subcc %o4, 0x8, %o4
+ EXNV8(ldxa [%o1] %asi, %o5, add %o4, %o2)
+ EXNV8(stxa %o5, [%o1 + %o3] ASI_AIUS, add %o4, %o2)
+ bgu,pt %XCC, 1b
+ add %o1, 0x8, %o1
+ andcc %o2, 0x4, %g0
+ be,pt %XCC, 1f
+ nop
+ sub %o2, 0x4, %o2
+ EXNV4(lduwa [%o1] %asi, %o5, add %o4, %o2)
+ EXNV4(stwa %o5, [%o1 + %o3] ASI_AIUS, add %o4, %o2)
+ add %o1, 0x4, %o1
+1: cmp %o2, 0
+ be,pt %XCC, out
+ nop
+ ba,pt %xcc, small_copy_unaligned
nop
- ba,a,pt %xcc, U3copy_in_user_short
-
- /* If we get here, then 32 <= len < (6 * 64) */
-U3copy_in_user_toosmall:
-
-#ifdef SMALL_COPY_USES_FPU
-
- /* Is 'dst' already aligned on an 8-byte boundary? */
- be,pt %xcc, 2f ! BR Group
-
- /* Compute abs((dst & 7) - 8) into %g2. This is the number
- * of bytes to copy to make 'dst' 8-byte aligned. We pre-
- * subtract this from 'len'.
- */
- sub %g2, 0x8, %g2 ! A0
- sub %g0, %g2, %g2 ! A0 Group (reg-dep)
- sub %o2, %g2, %o2 ! A0 Group (reg-dep)
-
- /* Copy %g2 bytes from src to dst, one byte at a time. */
-1: EXNV2(lduba [%o1 + 0x00] %asi, %o3, add %o2, %g2)! MS (Group) (%o3 in 3 cycles)
- add %o1, 0x1, %o1 ! A1
- add %o0, 0x1, %o0 ! A0 Group
- subcc %g2, 0x1, %g2 ! A1
-
- bg,pt %icc, 1b ! BR Group
- EXNV2(stba %o3, [%o0 + -1] %asi, add %o2, %g2) ! MS Group
-
-2: VISEntryHalf ! MS+MS
-
- /* Compute (len - (len % 8)) into %g2. This is guaranteed
- * to be nonzero.
- */
- andn %o2, 0x7, %g2 ! A0 Group
-
- /* You may read this and believe that it allows reading
- * one 8-byte longword past the end of src. It actually
- * does not, as %g2 is subtracted as loads are done from
- * src, so we always stop before running off the end.
- * Also, we are guaranteed to have at least 0x10 bytes
- * to move here.
- */
- sub %g2, 0x8, %g2 ! A0 Group (reg-dep)
- alignaddr %o1, %g0, %g1 ! MS (Break-after)
- EX(ldda [%g1 + 0x00] %asi, %f0, add %o2, %g0) ! MS Group (1-cycle stall)
- add %g1, 0x8, %g1 ! A0
-
-1: EX(ldda [%g1 + 0x00] %asi, %f2, add %o2, %g0) ! MS Group
- add %g1, 0x8, %g1 ! A0
- sub %o2, 0x8, %o2 ! A1
- subcc %g2, 0x8, %g2 ! A0 Group
-
- faligndata %f0, %f2, %f8 ! FGA Group (1-cycle stall)
- EX(stda %f8, [%o0 + 0x00] %asi, add %o2, 0x8) ! MS Group (2-cycle stall)
- add %o1, 0x8, %o1 ! A0
- be,pn %icc, 2f ! BR
-
- add %o0, 0x8, %o0 ! A1
- EX(ldda [%g1 + 0x00] %asi, %f0, add %o2, %g0) ! MS Group
- add %g1, 0x8, %g1 ! A0
- sub %o2, 0x8, %o2 ! A1
-
- subcc %g2, 0x8, %g2 ! A0 Group
- faligndata %f2, %f0, %f8 ! FGA Group (1-cycle stall)
- EX(stda %f8, [%o0 + 0x00] %asi, add %o2, 0x8) ! MS Group (2-cycle stall)
- add %o1, 0x8, %o1 ! A0
-
- bne,pn %icc, 1b ! BR
- add %o0, 0x8, %o0 ! A1
-
- /* Nothing left to copy? */
-2: cmp %o2, 0 ! A0 Group
- VISExitHalf ! A0+MS
- be,pn %icc, U3copy_in_user_short_ret ! BR Group
- nop ! A0
- ba,a,pt %xcc, U3copy_in_user_short ! BR Group
-
-#else /* !(SMALL_COPY_USES_FPU) */
-
- xor %o1, %o0, %g2
- andcc %g2, 0x7, %g0
- bne,pn %icc, U3copy_in_user_short
- andcc %o1, 0x7, %g2
-
- be,pt %xcc, 2f
- sub %g2, 0x8, %g2
- sub %g0, %g2, %g2
- sub %o2, %g2, %o2
-
-1: EXNV2(lduba [%o1 + 0x00] %asi, %o3, add %o2, %g2)
- add %o1, 0x1, %o1
- add %o0, 0x1, %o0
- subcc %g2, 0x1, %g2
- bg,pt %icc, 1b
- EXNV2(stba %o3, [%o0 + -1] %asi, add %o2, %g2)
-2: andn %o2, 0x7, %g2
- sub %o2, %g2, %o2
+small_copy: /* 0 < len <= 16 */
+ andcc %o3, 0x3, %g0
+ bne,pn %XCC, small_copy_unaligned
+ sub %o0, %o1, %o3
-3: EXNV3(ldxa [%o1 + 0x00] %asi, %o3, add %o2, %g2)
- add %o1, 0x8, %o1
- add %o0, 0x8, %o0
- subcc %g2, 0x8, %g2
- bg,pt %icc, 3b
- EXNV3(stxa %o3, [%o0 + -8] %asi, add %o2, %g2)
+small_copy_aligned:
+ subcc %o2, 4, %o2
+ EXNV4(lduwa [%o1] %asi, %g1, add %o2, %g0)
+ EXNV4(stwa %g1, [%o1 + %o3] ASI_AIUS, add %o2, %g0)
+ bgu,pt %XCC, small_copy_aligned
+ add %o1, 4, %o1
- cmp %o2, 0
- bne,pn %icc, U3copy_in_user_short
- nop
- ba,a,pt %xcc, U3copy_in_user_short_ret
+out: retl
+ clr %o0
-#endif /* !(SMALL_COPY_USES_FPU) */
+ .align 32
+small_copy_unaligned:
+ subcc %o2, 1, %o2
+ EXNV1(lduba [%o1] %asi, %g1, add %o2, %g0)
+ EXNV1(stba %g1, [%o1 + %o3] ASI_AIUS, add %o2, %g0)
+ bgu,pt %XCC, small_copy_unaligned
+ add %o1, 1, %o1
+ retl
+ clr %o0
-/* $Id: U3copy_to_user.S,v 1.3 2000/11/01 09:29:19 davem Exp $
- * U3memcpy.S: UltraSparc-III optimized copy to userspace.
+/* U3copy_to_user.S: UltraSparc-III optimized memcpy.
*
- * Copyright (C) 1999, 2000 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1999, 2000, 2004 David S. Miller (davem@redhat.com)
*/
-#ifdef __KERNEL__
#include <asm/visasm.h>
#include <asm/asi.h>
#include <asm/dcu.h>
#include <asm/spitfire.h>
-#undef SMALL_COPY_USES_FPU
+
+#define XCC xcc
+
#define EXNV(x,y,a,b) \
98: x,y; \
.section .fixup; \
.text; \
.align 4;
#define EXNV3(x,y,a,b) \
+98: x,y; \
+ .section .fixup; \
+ .align 4; \
+99: a, b, %o0; \
+ retl; \
+ add %o0, 4, %o0; \
+ .section __ex_table; \
+ .align 4; \
+ .word 98b, 99b; \
+ .text; \
+ .align 4;
+#define EXNV4(x,y,a,b) \
98: x,y; \
.section .fixup; \
.align 4; \
.word 98b, 99b; \
.text; \
.align 4;
-#else
-#define ASI_AIUS 0x80
-#define ASI_BLK_AIUS 0xf0
-#define FPRS_FEF 0x04
-#define VISEntryHalf rd %fprs, %o5; wr %g0, FPRS_FEF, %fprs
-#define VISExitHalf and %o5, FPRS_FEF, %o5; wr %o5, 0x0, %fprs
-#define SMALL_COPY_USES_FPU
-#define EXNV(x,y,a,b) x,y;
-#define EXNV2(x,y,a,b) x,y;
-#define EXNV3(x,y,a,b) x,y;
-#define EX(x,y,a,b) x,y;
-#define EXBLK1(x,y) x,y;
-#define EXBLK2(x,y) x,y;
-#define EXBLK3(x,y) x,y;
-#define EXBLK4(x,y) x,y;
-#endif
+
+ .register %g2,#scratch
+ .register %g3,#scratch
/* Special/non-trivial issues of this code:
*
* of up to 2.4GB per second.
*/
- .globl U3copy_to_user
-U3copy_to_user: /* %o0=dst, %o1=src, %o2=len */
+ .globl U3copy_to_user
+U3copy_to_user: /* %o0=dst, %o1=src, %o2=len */
/* Writing to %asi is _expensive_ so we hardcode it.
* Reading %asi to check for KERNEL_DS is comparatively
* cheap.
*/
- rd %asi, %g1 ! MS Group (4 cycles)
- cmp %g1, ASI_AIUS ! A0 Group
- bne U3memcpy ! BR
- nop ! A1
-#ifndef __KERNEL__
- /* Save away original 'dst' for memcpy return value. */
- mov %o0, %g3 ! A0 Group
-#endif
- /* Anything to copy at all? */
- cmp %o2, 0 ! A1
- ble,pn %icc, U3copy_to_user_short_ret ! BR
-
- /* Extremely small copy? */
- cmp %o2, 31 ! A0 Group
- ble,pn %icc, U3copy_to_user_short ! BR
-
- /* Large enough to use unrolled prefetch loops? */
- cmp %o2, 0x100 ! A1
- bge,a,pt %icc, U3copy_to_user_enter ! BR Group
- andcc %o0, 0x3f, %g2 ! A0
-
- ba,pt %xcc, U3copy_to_user_toosmall ! BR Group
- andcc %o0, 0x7, %g2 ! A0
-
- .align 32
-U3copy_to_user_short:
- /* Copy %o2 bytes from src to dst, one byte at a time. */
- ldub [%o1 + 0x00], %o3 ! MS Group
- add %o1, 0x1, %o1 ! A0
- add %o0, 0x1, %o0 ! A1
- subcc %o2, 1, %o2 ! A0 Group
-
- bg,pt %icc, U3copy_to_user_short ! BR
- EXNV(stba %o3, [%o0 + -1] %asi, add %o2, 1) ! MS Group (1-cycle stall)
-
-U3copy_to_user_short_ret:
-#ifdef __KERNEL__
- retl ! BR Group (0-4 cycle stall)
- clr %o0 ! A0
-#else
- retl ! BR Group (0-4 cycle stall)
- mov %g3, %o0 ! A0
-#endif
-
- /* Here len >= (6 * 64) and condition codes reflect execution
+ rd %asi, %g1
+ cmp %g1, ASI_AIUS
+ bne,pn %icc, U3memcpy_user_stub
+ nop
+
+ cmp %o2, 0
+ be,pn %XCC, 85f
+ or %o0, %o1, %o3
+ cmp %o2, 16
+ bleu,a,pn %XCC, 80f
+ or %o3, %o2, %o3
+
+ cmp %o2, 256
+ blu,pt %XCC, 70f
+ andcc %o3, 0x7, %g0
+
+ ba,pt %xcc, 1f
+ andcc %o0, 0x3f, %g2
+
+ /* Here len >= 256 and condition codes reflect execution
* of "andcc %o0, 0x7, %g2", done by caller.
*/
.align 64
-U3copy_to_user_enter:
+1:
/* Is 'dst' already aligned on an 64-byte boundary? */
- be,pt %xcc, 2f ! BR
+ be,pt %XCC, 2f
/* Compute abs((dst & 0x3f) - 0x40) into %g2. This is the number
* of bytes to copy to make 'dst' 64-byte aligned. We pre-
* subtract this from 'len'.
*/
- sub %g2, 0x40, %g2 ! A0 Group
- sub %g0, %g2, %g2 ! A0 Group
- sub %o2, %g2, %o2 ! A0 Group
+ sub %g2, 0x40, %g2
+ sub %g0, %g2, %g2
+ sub %o2, %g2, %o2
/* Copy %g2 bytes from src to dst, one byte at a time. */
-1: ldub [%o1 + 0x00], %o3 ! MS (Group)
- add %o1, 0x1, %o1 ! A1
- add %o0, 0x1, %o0 ! A0 Group
- subcc %g2, 0x1, %g2 ! A1
+1: ldub [%o1 + 0x00], %o3
+ add %o1, 0x1, %o1
+ add %o0, 0x1, %o0
+ subcc %g2, 0x1, %g2
- bg,pt %icc, 1b ! BR Group
- EXNV2(stba %o3, [%o0 + -1] %asi, add %o2, %g2) ! MS Group
+ bg,pt %XCC, 1b
+ EXNV2(stba %o3, [%o0 + -1] %asi, add %o2, %g2)
-2: VISEntryHalf ! MS+MS
- and %o1, 0x7, %g1 ! A1
- ba,pt %xcc, U3copy_to_user_begin ! BR
- alignaddr %o1, %g0, %o1 ! MS (Break-after)
+2: VISEntryHalf
+ and %o1, 0x7, %g1
+ ba,pt %xcc, 1f
+ alignaddr %o1, %g0, %o1
.align 64
-U3copy_to_user_begin:
-#ifdef __KERNEL__
- .globl U3copy_to_user_nop_1_6
-U3copy_to_user_nop_1_6:
- ldxa [%g0] ASI_DCU_CONTROL_REG, %g3
- sethi %uhi(DCU_PE), %o3
- sllx %o3, 32, %o3
- or %g3, %o3, %o3
- stxa %o3, [%g0] ASI_DCU_CONTROL_REG ! Enable P-cache
- membar #Sync
-#endif
- prefetch [%o1 + 0x000], #one_read ! MS Group1
- prefetch [%o1 + 0x040], #one_read ! MS Group2
- andn %o2, (0x40 - 1), %o4 ! A0
- prefetch [%o1 + 0x080], #one_read ! MS Group3
- cmp %o4, 0x140 ! A0
- prefetch [%o1 + 0x0c0], #one_read ! MS Group4
- ldd [%o1 + 0x000], %f0 ! MS Group5 (%f0 results at G8)
- bge,a,pt %icc, 1f ! BR
-
- prefetch [%o1 + 0x100], #one_read ! MS Group6
-1: ldd [%o1 + 0x008], %f2 ! AX (%f2 results at G9)
- cmp %o4, 0x180 ! A1
- bge,a,pt %icc, 1f ! BR
- prefetch [%o1 + 0x140], #one_read ! MS Group7
-1: ldd [%o1 + 0x010], %f4 ! AX (%f4 results at G10)
- cmp %o4, 0x1c0 ! A1
- bge,a,pt %icc, 1f ! BR
-
- prefetch [%o1 + 0x180], #one_read ! MS Group8
-1: faligndata %f0, %f2, %f16 ! FGA Group9 (%f16 at G12)
- ldd [%o1 + 0x018], %f6 ! AX (%f6 results at G12)
- faligndata %f2, %f4, %f18 ! FGA Group10 (%f18 results at G13)
- ldd [%o1 + 0x020], %f8 ! MS (%f8 results at G13)
- faligndata %f4, %f6, %f20 ! FGA Group12 (1-cycle stall,%f20 at G15)
- ldd [%o1 + 0x028], %f10 ! MS (%f10 results at G15)
- faligndata %f6, %f8, %f22 ! FGA Group13 (%f22 results at G16)
-
- ldd [%o1 + 0x030], %f12 ! MS (%f12 results at G16)
- faligndata %f8, %f10, %f24 ! FGA Group15 (1-cycle stall,%f24 at G18)
- ldd [%o1 + 0x038], %f14 ! MS (%f14 results at G18)
- faligndata %f10, %f12, %f26 ! FGA Group16 (%f26 results at G19)
- ldd [%o1 + 0x040], %f0 ! MS (%f0 results at G19)
-
- /* We only use the first loop if len > (7 * 64). */
- subcc %o4, 0x1c0, %o4 ! A0 Group17
- bg,pt %icc, U3copy_to_user_loop1 ! BR
- add %o1, 0x40, %o1 ! A1
-
- add %o4, 0x140, %o4 ! A0 Group18
- ba,pt %xcc, U3copy_to_user_loop2 ! BR
- srl %o4, 6, %o3 ! A0 Group19
- nop
- nop
- nop
- nop
- nop
-
- nop
- nop
-
- /* This loop performs the copy and queues new prefetches.
- * We drop into the second loop when len <= (5 * 64). Note
- * that this (5 * 64) factor has been subtracted from len
- * already.
- */
-U3copy_to_user_loop1:
- ldd [%o1 + 0x008], %f2 ! MS Group2 (%f2 results at G5)
- faligndata %f12, %f14, %f28 ! FGA (%f28 results at G5)
- ldd [%o1 + 0x010], %f4 ! MS Group3 (%f4 results at G6)
- faligndata %f14, %f0, %f30 ! FGA Group4 (1-cycle stall, %f30 at G7)
- EXBLK1(stda %f16, [%o0] ASI_BLK_AIUS) ! MS
- ldd [%o1 + 0x018], %f6 ! AX (%f6 results at G7)
-
- faligndata %f0, %f2, %f16 ! FGA Group12 (7-cycle stall)
- ldd [%o1 + 0x020], %f8 ! MS (%f8 results at G15)
- faligndata %f2, %f4, %f18 ! FGA Group13 (%f18 results at G16)
- ldd [%o1 + 0x028], %f10 ! MS (%f10 results at G16)
- faligndata %f4, %f6, %f20 ! FGA Group14 (%f20 results at G17)
- ldd [%o1 + 0x030], %f12 ! MS (%f12 results at G17)
- faligndata %f6, %f8, %f22 ! FGA Group15 (%f22 results at G18)
- ldd [%o1 + 0x038], %f14 ! MS (%f14 results at G18)
-
- faligndata %f8, %f10, %f24 ! FGA Group16 (%f24 results at G19)
- ldd [%o1 + 0x040], %f0 ! AX (%f0 results at G19)
- prefetch [%o1 + 0x180], #one_read ! MS
- faligndata %f10, %f12, %f26 ! FGA Group17 (%f26 results at G20)
- subcc %o4, 0x40, %o4 ! A0
- add %o1, 0x40, %o1 ! A1
- bg,pt %xcc, U3copy_to_user_loop1 ! BR
- add %o0, 0x40, %o0 ! A0 Group18
-
-U3copy_to_user_loop2_enter:
- mov 5, %o3 ! A1
-
- /* This loop performs on the copy, no new prefetches are
- * queued. We do things this way so that we do not perform
- * any spurious prefetches past the end of the src buffer.
- */
-U3copy_to_user_loop2:
- ldd [%o1 + 0x008], %f2 ! MS
- faligndata %f12, %f14, %f28 ! FGA Group2
- ldd [%o1 + 0x010], %f4 ! MS
- faligndata %f14, %f0, %f30 ! FGA Group4 (1-cycle stall)
- EXBLK2(stda %f16, [%o0] ASI_BLK_AIUS) ! MS
- ldd [%o1 + 0x018], %f6 ! AX
- faligndata %f0, %f2, %f16 ! FGA Group12 (7-cycle stall)
-
- ldd [%o1 + 0x020], %f8 ! MS
- faligndata %f2, %f4, %f18 ! FGA Group13
- ldd [%o1 + 0x028], %f10 ! MS
- faligndata %f4, %f6, %f20 ! FGA Group14
- ldd [%o1 + 0x030], %f12 ! MS
- faligndata %f6, %f8, %f22 ! FGA Group15
- ldd [%o1 + 0x038], %f14 ! MS
- faligndata %f8, %f10, %f24 ! FGA Group16
-
- ldd [%o1 + 0x040], %f0 ! AX
- faligndata %f10, %f12, %f26 ! FGA Group17
- subcc %o3, 0x01, %o3 ! A0
- add %o1, 0x40, %o1 ! A1
- bg,pt %xcc, U3copy_to_user_loop2 ! BR
- add %o0, 0x40, %o0 ! A0 Group18
+1:
+ membar #StoreLoad | #StoreStore | #LoadStore
+ prefetch [%o1 + 0x000], #one_read
+ prefetch [%o1 + 0x040], #one_read
+ andn %o2, (0x40 - 1), %o4
+ prefetch [%o1 + 0x080], #one_read
+ prefetch [%o1 + 0x0c0], #one_read
+ ldd [%o1 + 0x000], %f0
+ prefetch [%o1 + 0x100], #one_read
+ ldd [%o1 + 0x008], %f2
+ prefetch [%o1 + 0x140], #one_read
+ ldd [%o1 + 0x010], %f4
+ prefetch [%o1 + 0x180], #one_read
+ faligndata %f0, %f2, %f16
+ ldd [%o1 + 0x018], %f6
+ faligndata %f2, %f4, %f18
+ ldd [%o1 + 0x020], %f8
+ faligndata %f4, %f6, %f20
+ ldd [%o1 + 0x028], %f10
+ faligndata %f6, %f8, %f22
+
+ ldd [%o1 + 0x030], %f12
+ faligndata %f8, %f10, %f24
+ ldd [%o1 + 0x038], %f14
+ faligndata %f10, %f12, %f26
+ ldd [%o1 + 0x040], %f0
+
+ sub %o4, 0x80, %o4
+ add %o1, 0x40, %o1
+ ba,pt %xcc, 1f
+ srl %o4, 6, %o3
+
+ .align 64
+1:
+ ldd [%o1 + 0x008], %f2
+ faligndata %f12, %f14, %f28
+ ldd [%o1 + 0x010], %f4
+ faligndata %f14, %f0, %f30
+ EXBLK2(stda %f16, [%o0] ASI_BLK_AIUS)
+ ldd [%o1 + 0x018], %f6
+ faligndata %f0, %f2, %f16
+
+ ldd [%o1 + 0x020], %f8
+ faligndata %f2, %f4, %f18
+ ldd [%o1 + 0x028], %f10
+ faligndata %f4, %f6, %f20
+ ldd [%o1 + 0x030], %f12
+ faligndata %f6, %f8, %f22
+ ldd [%o1 + 0x038], %f14
+ faligndata %f8, %f10, %f24
+
+ ldd [%o1 + 0x040], %f0
+ prefetch [%o1 + 0x180], #one_read
+ faligndata %f10, %f12, %f26
+ subcc %o3, 0x01, %o3
+ add %o1, 0x40, %o1
+ bg,pt %XCC, 1b
+ add %o0, 0x40, %o0
/* Finally we copy the last full 64-byte block. */
-U3copy_to_user_loopfini:
- ldd [%o1 + 0x008], %f2 ! MS
- faligndata %f12, %f14, %f28 ! FGA
- ldd [%o1 + 0x010], %f4 ! MS Group19
- faligndata %f14, %f0, %f30 ! FGA
- EXBLK3(stda %f16, [%o0] ASI_BLK_AIUS) ! MS Group20
- ldd [%o1 + 0x018], %f6 ! AX
- faligndata %f0, %f2, %f16 ! FGA Group11 (7-cycle stall)
- ldd [%o1 + 0x020], %f8 ! MS
- faligndata %f2, %f4, %f18 ! FGA Group12
- ldd [%o1 + 0x028], %f10 ! MS
- faligndata %f4, %f6, %f20 ! FGA Group13
- ldd [%o1 + 0x030], %f12 ! MS
- faligndata %f6, %f8, %f22 ! FGA Group14
- ldd [%o1 + 0x038], %f14 ! MS
- faligndata %f8, %f10, %f24 ! FGA Group15
- cmp %g1, 0 ! A0
- be,pt %icc, 1f ! BR
- add %o0, 0x40, %o0 ! A1
- ldd [%o1 + 0x040], %f0 ! MS
-1: faligndata %f10, %f12, %f26 ! FGA Group16
- faligndata %f12, %f14, %f28 ! FGA Group17
- faligndata %f14, %f0, %f30 ! FGA Group18
- EXBLK4(stda %f16, [%o0] ASI_BLK_AIUS) ! MS
- add %o0, 0x40, %o0 ! A0
- add %o1, 0x40, %o1 ! A1
-#ifdef __KERNEL__
- .globl U3copy_to_user_nop_2_3
-U3copy_to_user_nop_2_3:
- mov PRIMARY_CONTEXT, %o3
- stxa %g0, [%o3] ASI_DMMU ! Flush P-cache
- stxa %g3, [%g0] ASI_DCU_CONTROL_REG ! Disable P-cache
-#endif
- membar #Sync ! MS Group26 (7-cycle stall)
+ ldd [%o1 + 0x008], %f2
+ faligndata %f12, %f14, %f28
+ ldd [%o1 + 0x010], %f4
+ faligndata %f14, %f0, %f30
+ EXBLK3(stda %f16, [%o0] ASI_BLK_AIUS)
+ ldd [%o1 + 0x018], %f6
+ faligndata %f0, %f2, %f16
+ ldd [%o1 + 0x020], %f8
+ faligndata %f2, %f4, %f18
+ ldd [%o1 + 0x028], %f10
+ faligndata %f4, %f6, %f20
+ ldd [%o1 + 0x030], %f12
+ faligndata %f6, %f8, %f22
+ ldd [%o1 + 0x038], %f14
+ faligndata %f8, %f10, %f24
+ cmp %g1, 0
+ be,pt %XCC, 1f
+ add %o0, 0x40, %o0
+ ldd [%o1 + 0x040], %f0
+1: faligndata %f10, %f12, %f26
+ faligndata %f12, %f14, %f28
+ faligndata %f14, %f0, %f30
+ EXBLK4(stda %f16, [%o0] ASI_BLK_AIUS)
+ add %o0, 0x40, %o0
+ add %o1, 0x40, %o1
+
+ membar #Sync
/* Now we copy the (len modulo 64) bytes at the end.
* Note how we borrow the %f0 loaded above.
*
* Also notice how this code is careful not to perform a
- * load past the end of the src buffer just like similar
- * code found in U3copy_to_user_toosmall processing.
+ * load past the end of the src buffer.
*/
-U3copy_to_user_loopend:
- and %o2, 0x3f, %o2 ! A0 Group
- andcc %o2, 0x38, %g2 ! A0 Group
- be,pn %icc, U3copy_to_user_endcruft ! BR
- subcc %g2, 0x8, %g2 ! A1
- be,pn %icc, U3copy_to_user_endcruft ! BR Group
- cmp %g1, 0 ! A0
-
- be,a,pt %icc, 1f ! BR Group
- ldd [%o1 + 0x00], %f0 ! MS
-
-1: ldd [%o1 + 0x08], %f2 ! MS Group
- add %o1, 0x8, %o1 ! A0
- sub %o2, 0x8, %o2 ! A1
- subcc %g2, 0x8, %g2 ! A0 Group
- faligndata %f0, %f2, %f8 ! FGA Group
- EX(stda %f8, [%o0 + 0x00] %asi, add %o2, 0x8) ! MS (XXX does it stall here? XXX)
- be,pn %icc, U3copy_to_user_endcruft ! BR
- add %o0, 0x8, %o0 ! A0
- ldd [%o1 + 0x08], %f0 ! MS Group
- add %o1, 0x8, %o1 ! A0
- sub %o2, 0x8, %o2 ! A1
- subcc %g2, 0x8, %g2 ! A0 Group
- faligndata %f2, %f0, %f8 ! FGA
- EX(stda %f8, [%o0 + 0x00] %asi, add %o2, 0x8) ! MS (XXX does it stall here? XXX)
- bne,pn %icc, 1b ! BR
- add %o0, 0x8, %o0 ! A0 Group
+ and %o2, 0x3f, %o2
+ andcc %o2, 0x38, %g2
+ be,pn %XCC, 2f
+ subcc %g2, 0x8, %g2
+ be,pn %XCC, 2f
+ cmp %g1, 0
+
+ be,a,pt %XCC, 1f
+ ldd [%o1 + 0x00], %f0
+
+1: ldd [%o1 + 0x08], %f2
+ add %o1, 0x8, %o1
+ sub %o2, 0x8, %o2
+ subcc %g2, 0x8, %g2
+ faligndata %f0, %f2, %f8
+ EX(stda %f8, [%o0 + 0x00] %asi, add %o2, 0x8)
+ be,pn %XCC, 2f
+ add %o0, 0x8, %o0
+ ldd [%o1 + 0x08], %f0
+ add %o1, 0x8, %o1
+ sub %o2, 0x8, %o2
+ subcc %g2, 0x8, %g2
+ faligndata %f2, %f0, %f8
+ EX(stda %f8, [%o0 + 0x00] %asi, add %o2, 0x8)
+ bne,pn %XCC, 1b
+ add %o0, 0x8, %o0
/* If anything is left, we copy it one byte at a time.
* Note that %g1 is (src & 0x3) saved above before the
* alignaddr was performed.
*/
-U3copy_to_user_endcruft:
+2:
cmp %o2, 0
add %o1, %g1, %o1
VISExitHalf
- be,pn %icc, U3copy_to_user_short_ret
- nop
- ba,a,pt %xcc, U3copy_to_user_short
-
- /* If we get here, then 32 <= len < (6 * 64) */
-U3copy_to_user_toosmall:
-
-#ifdef SMALL_COPY_USES_FPU
-
- /* Is 'dst' already aligned on an 8-byte boundary? */
- be,pt %xcc, 2f ! BR Group
-
- /* Compute abs((dst & 7) - 8) into %g2. This is the number
- * of bytes to copy to make 'dst' 8-byte aligned. We pre-
- * subtract this from 'len'.
- */
- sub %g2, 0x8, %g2 ! A0
- sub %g0, %g2, %g2 ! A0 Group (reg-dep)
- sub %o2, %g2, %o2 ! A0 Group (reg-dep)
-
- /* Copy %g2 bytes from src to dst, one byte at a time. */
-1: ldub [%o1 + 0x00], %o3 ! MS (Group) (%o3 in 3 cycles)
- add %o1, 0x1, %o1 ! A1
- add %o0, 0x1, %o0 ! A0 Group
- subcc %g2, 0x1, %g2 ! A1
+ be,pn %XCC, 85f
+ sub %o0, %o1, %o3
- bg,pt %icc, 1b ! BR Group
- EXNV2(stba %o3, [%o0 + -1] %asi, add %o2, %g2) ! MS Group
+ andcc %g1, 0x7, %g0
+ bne,pn %icc, 90f
+ andcc %o2, 0x8, %g0
+ be,pt %icc, 1f
+ nop
+ ldx [%o1], %o5
+ EXNV(stxa %o5, [%o1 + %o3] ASI_AIUS, add %o2, %g0)
+ add %o1, 0x8, %o1
-2: VISEntryHalf ! MS+MS
+1: andcc %o2, 0x4, %g0
+ be,pt %icc, 1f
+ nop
+ lduw [%o1], %o5
+ EXNV(stwa %o5, [%o1 + %o3] ASI_AIUS, and %o2, 0x7)
+ add %o1, 0x4, %o1
- /* Compute (len - (len % 8)) into %g2. This is guaranteed
- * to be nonzero.
- */
- andn %o2, 0x7, %g2 ! A0 Group
-
- /* You may read this and believe that it allows reading
- * one 8-byte longword past the end of src. It actually
- * does not, as %g2 is subtracted as loads are done from
- * src, so we always stop before running off the end.
- * Also, we are guaranteed to have at least 0x10 bytes
- * to move here.
- */
- sub %g2, 0x8, %g2 ! A0 Group (reg-dep)
- alignaddr %o1, %g0, %g1 ! MS (Break-after)
- ldd [%g1 + 0x00], %f0 ! MS Group (1-cycle stall)
- add %g1, 0x8, %g1 ! A0
-
-1: ldd [%g1 + 0x00], %f2 ! MS Group
- add %g1, 0x8, %g1 ! A0
- sub %o2, 0x8, %o2 ! A1
- subcc %g2, 0x8, %g2 ! A0 Group
-
- faligndata %f0, %f2, %f8 ! FGA Group (1-cycle stall)
- EX(stda %f8, [%o0 + 0x00] %asi, add %o2, 0x8) ! MS Group (2-cycle stall)
- add %o1, 0x8, %o1 ! A0
- be,pn %icc, 2f ! BR
-
- add %o0, 0x8, %o0 ! A1
- ldd [%g1 + 0x00], %f0 ! MS Group
- add %g1, 0x8, %g1 ! A0
- sub %o2, 0x8, %o2 ! A1
-
- subcc %g2, 0x8, %g2 ! A0 Group
- faligndata %f2, %f0, %f8 ! FGA Group (1-cycle stall)
- EX(stda %f8, [%o0 + 0x00] %asi, add %o2, 0x8) ! MS Group (2-cycle stall)
- add %o1, 0x8, %o1 ! A0
-
- bne,pn %icc, 1b ! BR
- add %o0, 0x8, %o0 ! A1
-
- /* Nothing left to copy? */
-2: cmp %o2, 0 ! A0 Group
- VISExitHalf ! A0+MS
- be,pn %icc, U3copy_to_user_short_ret ! BR Group
- nop ! A0
- ba,a,pt %xcc, U3copy_to_user_short ! BR Group
-
-#else /* !(SMALL_COPY_USES_FPU) */
-
- xor %o1, %o0, %g2
- andcc %g2, 0x7, %g0
- bne,pn %icc, U3copy_to_user_short
- andcc %o1, 0x7, %g2
-
- be,pt %xcc, 2f
- sub %g2, 0x8, %g2
- sub %g0, %g2, %g2
- sub %o2, %g2, %o2
+1: andcc %o2, 0x2, %g0
+ be,pt %icc, 1f
+ nop
+ lduh [%o1], %o5
+ EXNV(stha %o5, [%o1 + %o3] ASI_AIUS, and %o2, 0x3)
+ add %o1, 0x2, %o1
-1: ldub [%o1 + 0x00], %o3
- add %o1, 0x1, %o1
- add %o0, 0x1, %o0
- subcc %g2, 0x1, %g2
- bg,pt %icc, 1b
- EXNV2(stba %o3, [%o0 + -1] %asi, add %o2, %g2)
+1: andcc %o2, 0x1, %g0
+ be,pt %icc, 85f
+ nop
+ ldub [%o1], %o5
+ ba,pt %xcc, 85f
+ EXNV(stba %o5, [%o1 + %o3] ASI_AIUS, and %o2, 0x1)
+
+70: /* 16 < len <= 64 */
+ bne,pn %XCC, 90f
+ sub %o0, %o1, %o3
+
+ andn %o2, 0x7, %o4
+ and %o2, 0x7, %o2
+1: subcc %o4, 0x8, %o4
+ ldx [%o1], %o5
+ EXNV4(stxa %o5, [%o1 + %o3] ASI_AIUS, add %o2, %o4)
+ bgu,pt %XCC, 1b
+ add %o1, 0x8, %o1
+ andcc %o2, 0x4, %g0
+ be,pt %XCC, 1f
+ nop
+ sub %o2, 0x4, %o2
+ lduw [%o1], %o5
+ EXNV3(stwa %o5, [%o1 + %o3] ASI_AIUS, add %o2, %g0)
+ add %o1, 0x4, %o1
+1: cmp %o2, 0
+ be,pt %XCC, 85f
+ nop
+ ba,pt %xcc, 90f
+ nop
-2: andn %o2, 0x7, %g2
- sub %o2, %g2, %o2
+80: /* 0 < len <= 16 */
+ andcc %o3, 0x3, %g0
+ bne,pn %XCC, 90f
+ sub %o0, %o1, %o3
-3: ldx [%o1 + 0x00], %o3
- add %o1, 0x8, %o1
- add %o0, 0x8, %o0
- subcc %g2, 0x8, %g2
- bg,pt %icc, 3b
- EXNV3(stxa %o3, [%o0 + -8] %asi, add %o2, %g2)
+1:
+ subcc %o2, 4, %o2
+ lduw [%o1], %g1
+ EXNV3(stwa %g1, [%o1 + %o3] ASI_AIUS, add %o2, %g0)
+ bgu,pt %XCC, 1b
+ add %o1, 4, %o1
- cmp %o2, 0
- bne,pn %icc, U3copy_to_user_short
- nop
- ba,a,pt %xcc, U3copy_to_user_short_ret
+85: retl
+ clr %o0
-#endif /* !(SMALL_COPY_USES_FPU) */
+ .align 32
+90:
+ subcc %o2, 1, %o2
+ ldub [%o1], %g1
+ EXNV2(stba %g1, [%o1 + %o3] ASI_AIUS, add %o2, %g0)
+ bgu,pt %XCC, 90b
+ add %o1, 1, %o1
+ retl
+ clr %o0
-/* $Id: U3memcpy.S,v 1.2 2000/11/01 09:29:19 davem Exp $
- * U3memcpy.S: UltraSparc-III optimized memcpy.
+/* U3memcpy.S: UltraSparc-III optimized memcpy.
*
- * Copyright (C) 1999, 2000 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1999, 2000, 2004 David S. Miller (davem@redhat.com)
*/
#ifdef __KERNEL__
#include <asm/asi.h>
#include <asm/dcu.h>
#include <asm/spitfire.h>
-#undef SMALL_COPY_USES_FPU
#else
#define ASI_BLK_P 0xf0
#define FPRS_FEF 0x04
#define VISEntryHalf rd %fprs, %o5; wr %g0, FPRS_FEF, %fprs
#define VISExitHalf and %o5, FPRS_FEF, %o5; wr %o5, 0x0, %fprs
-#define SMALL_COPY_USES_FPU
#endif
+#ifndef XCC
+#define XCC xcc
+#endif
+
+ .register %g2,#scratch
+ .register %g3,#scratch
+
/* Special/non-trivial issues of this code:
*
* 1) %o5 is preserved from VISEntryHalf to VISExitHalf
* of up to 2.4GB per second.
*/
- .globl U3memcpy
-U3memcpy: /* %o0=dst, %o1=src, %o2=len */
-#ifndef __KERNEL__
- /* Save away original 'dst' for memcpy return value. */
- mov %o0, %g3 ! A0 Group
-#endif
- /* Anything to copy at all? */
- cmp %o2, 0 ! A1
- ble,pn %icc, U3memcpy_short_ret ! BR
-
- /* Extremely small copy? */
- cmp %o2, 31 ! A0 Group
- ble,pn %icc, U3memcpy_short ! BR
-
- /* Large enough to use unrolled prefetch loops? */
- cmp %o2, 0x100 ! A1
- bge,a,pt %icc, U3memcpy_enter ! BR Group
- andcc %o0, 0x3f, %g2 ! A0
-
- ba,pt %xcc, U3memcpy_toosmall ! BR Group
- andcc %o0, 0x7, %g2 ! A0
-
- .align 32
-U3memcpy_short:
- /* Copy %o2 bytes from src to dst, one byte at a time. */
- ldub [%o1 + 0x00], %o3 ! MS Group
- add %o1, 0x1, %o1 ! A0
- add %o0, 0x1, %o0 ! A1
- subcc %o2, 1, %o2 ! A0 Group
-
- bg,pt %icc, U3memcpy_short ! BR
- stb %o3, [%o0 + -1] ! MS Group (1-cycle stall)
-
-U3memcpy_short_ret:
-#ifdef __KERNEL__
- retl ! BR Group (0-4 cycle stall)
- clr %o0 ! A0
-#else
- retl ! BR Group (0-4 cycle stall)
- mov %g3, %o0 ! A0
-#endif
+ .globl U3memcpy
+U3memcpy: /* %o0=dst, %o1=src, %o2=len */
+ mov %o0, %g5
+ cmp %o2, 0
+ be,pn %XCC, 85f
+ or %o0, %o1, %o3
+ cmp %o2, 16
+ bleu,a,pn %XCC, 70f
+ or %o3, %o2, %o3
- /* Here len >= (6 * 64) and condition codes reflect execution
+ cmp %o2, 256
+ blu,pt %XCC, 80f
+ andcc %o3, 0x7, %g0
+
+ ba,pt %xcc, 1f
+ andcc %o0, 0x3f, %g2
+
+ /* Here len >= 256 and condition codes reflect execution
* of "andcc %o0, 0x7, %g2", done by caller.
*/
.align 64
-U3memcpy_enter:
+1:
/* Is 'dst' already aligned on an 64-byte boundary? */
- be,pt %xcc, 2f ! BR
+ be,pt %XCC, 2f
/* Compute abs((dst & 0x3f) - 0x40) into %g2. This is the number
* of bytes to copy to make 'dst' 64-byte aligned. We pre-
* subtract this from 'len'.
*/
- sub %g2, 0x40, %g2 ! A0 Group
- sub %g0, %g2, %g2 ! A0 Group
- sub %o2, %g2, %o2 ! A0 Group
+ sub %g2, 0x40, %g2
+ sub %g0, %g2, %g2
+ sub %o2, %g2, %o2
/* Copy %g2 bytes from src to dst, one byte at a time. */
-1: ldub [%o1 + 0x00], %o3 ! MS (Group)
- add %o1, 0x1, %o1 ! A1
- add %o0, 0x1, %o0 ! A0 Group
- subcc %g2, 0x1, %g2 ! A1
+1: ldub [%o1 + 0x00], %o3
+ add %o1, 0x1, %o1
+ add %o0, 0x1, %o0
+ subcc %g2, 0x1, %g2
- bg,pt %icc, 1b ! BR Group
- stb %o3, [%o0 + -1] ! MS Group
+ bg,pt %XCC, 1b
+ stb %o3, [%o0 + -1]
-2: VISEntryHalf ! MS+MS
- and %o1, 0x7, %g1 ! A1
- ba,pt %xcc, U3memcpy_begin ! BR
- alignaddr %o1, %g0, %o1 ! MS (Break-after)
+2: VISEntryHalf
+ and %o1, 0x7, %g1
+ ba,pt %xcc, 1f
+ alignaddr %o1, %g0, %o1
.align 64
-U3memcpy_begin:
-#ifdef __KERNEL__
- .globl U3memcpy_nop_1_6
-U3memcpy_nop_1_6:
- ldxa [%g0] ASI_DCU_CONTROL_REG, %g3
- sethi %uhi(DCU_PE), %o3
- sllx %o3, 32, %o3
- or %g3, %o3, %o3
- stxa %o3, [%g0] ASI_DCU_CONTROL_REG ! Enable P-cache
- membar #Sync
-#endif
- prefetch [%o1 + 0x000], #one_read ! MS Group1
- prefetch [%o1 + 0x040], #one_read ! MS Group2
- andn %o2, (0x40 - 1), %o4 ! A0
- prefetch [%o1 + 0x080], #one_read ! MS Group3
- cmp %o4, 0x140 ! A0
- prefetch [%o1 + 0x0c0], #one_read ! MS Group4
- ldd [%o1 + 0x000], %f0 ! MS Group5 (%f0 results at G8)
- bge,a,pt %icc, 1f ! BR
-
- prefetch [%o1 + 0x100], #one_read ! MS Group6
-1: ldd [%o1 + 0x008], %f2 ! AX (%f2 results at G9)
- cmp %o4, 0x180 ! A1
- bge,a,pt %icc, 1f ! BR
- prefetch [%o1 + 0x140], #one_read ! MS Group7
-1: ldd [%o1 + 0x010], %f4 ! AX (%f4 results at G10)
- cmp %o4, 0x1c0 ! A1
- bge,a,pt %icc, 1f ! BR
-
- prefetch [%o1 + 0x180], #one_read ! MS Group8
-1: faligndata %f0, %f2, %f16 ! FGA Group9 (%f16 at G12)
- ldd [%o1 + 0x018], %f6 ! AX (%f6 results at G12)
- faligndata %f2, %f4, %f18 ! FGA Group10 (%f18 results at G13)
- ldd [%o1 + 0x020], %f8 ! MS (%f8 results at G13)
- faligndata %f4, %f6, %f20 ! FGA Group12 (1-cycle stall,%f20 at G15)
- ldd [%o1 + 0x028], %f10 ! MS (%f10 results at G15)
- faligndata %f6, %f8, %f22 ! FGA Group13 (%f22 results at G16)
-
- ldd [%o1 + 0x030], %f12 ! MS (%f12 results at G16)
- faligndata %f8, %f10, %f24 ! FGA Group15 (1-cycle stall,%f24 at G18)
- ldd [%o1 + 0x038], %f14 ! MS (%f14 results at G18)
- faligndata %f10, %f12, %f26 ! FGA Group16 (%f26 results at G19)
- ldd [%o1 + 0x040], %f0 ! MS (%f0 results at G19)
-
- /* We only use the first loop if len > (7 * 64). */
- subcc %o4, 0x1c0, %o4 ! A0 Group17
- bg,pt %icc, U3memcpy_loop1 ! BR
- add %o1, 0x40, %o1 ! A1
-
- add %o4, 0x140, %o4 ! A0 Group18
- ba,pt %xcc, U3memcpy_loop2 ! BR
- srl %o4, 6, %o3 ! A0 Group19
- nop
- nop
- nop
- nop
- nop
-
- nop
- nop
-
- /* This loop performs the copy and queues new prefetches.
- * We drop into the second loop when len <= (5 * 64). Note
- * that this (5 * 64) factor has been subtracted from len
- * already.
- */
-U3memcpy_loop1:
- ldd [%o1 + 0x008], %f2 ! MS Group2 (%f2 results at G5)
- faligndata %f12, %f14, %f28 ! FGA (%f28 results at G5)
- ldd [%o1 + 0x010], %f4 ! MS Group3 (%f4 results at G6)
- faligndata %f14, %f0, %f30 ! FGA Group4 (1-cycle stall, %f30 at G7)
- stda %f16, [%o0] ASI_BLK_P ! MS
- ldd [%o1 + 0x018], %f6 ! AX (%f6 results at G7)
-
- faligndata %f0, %f2, %f16 ! FGA Group12 (7-cycle stall)
- ldd [%o1 + 0x020], %f8 ! MS (%f8 results at G15)
- faligndata %f2, %f4, %f18 ! FGA Group13 (%f18 results at G16)
- ldd [%o1 + 0x028], %f10 ! MS (%f10 results at G16)
- faligndata %f4, %f6, %f20 ! FGA Group14 (%f20 results at G17)
- ldd [%o1 + 0x030], %f12 ! MS (%f12 results at G17)
- faligndata %f6, %f8, %f22 ! FGA Group15 (%f22 results at G18)
- ldd [%o1 + 0x038], %f14 ! MS (%f14 results at G18)
-
- faligndata %f8, %f10, %f24 ! FGA Group16 (%f24 results at G19)
- ldd [%o1 + 0x040], %f0 ! AX (%f0 results at G19)
- prefetch [%o1 + 0x180], #one_read ! MS
- faligndata %f10, %f12, %f26 ! FGA Group17 (%f26 results at G20)
- subcc %o4, 0x40, %o4 ! A0
- add %o1, 0x40, %o1 ! A1
- bg,pt %xcc, U3memcpy_loop1 ! BR
- add %o0, 0x40, %o0 ! A0 Group18
-
-U3memcpy_loop2_enter:
- mov 5, %o3 ! A1
-
- /* This loop performs on the copy, no new prefetches are
- * queued. We do things this way so that we do not perform
- * any spurious prefetches past the end of the src buffer.
- */
-U3memcpy_loop2:
- ldd [%o1 + 0x008], %f2 ! MS
- faligndata %f12, %f14, %f28 ! FGA Group2
- ldd [%o1 + 0x010], %f4 ! MS
- faligndata %f14, %f0, %f30 ! FGA Group4 (1-cycle stall)
- stda %f16, [%o0] ASI_BLK_P ! MS
- ldd [%o1 + 0x018], %f6 ! AX
- faligndata %f0, %f2, %f16 ! FGA Group12 (7-cycle stall)
-
- ldd [%o1 + 0x020], %f8 ! MS
- faligndata %f2, %f4, %f18 ! FGA Group13
- ldd [%o1 + 0x028], %f10 ! MS
- faligndata %f4, %f6, %f20 ! FGA Group14
- ldd [%o1 + 0x030], %f12 ! MS
- faligndata %f6, %f8, %f22 ! FGA Group15
- ldd [%o1 + 0x038], %f14 ! MS
- faligndata %f8, %f10, %f24 ! FGA Group16
-
- ldd [%o1 + 0x040], %f0 ! AX
- faligndata %f10, %f12, %f26 ! FGA Group17
- subcc %o3, 0x01, %o3 ! A0
- add %o1, 0x40, %o1 ! A1
- bg,pt %xcc, U3memcpy_loop2 ! BR
- add %o0, 0x40, %o0 ! A0 Group18
+1:
+ membar #StoreLoad | #StoreStore | #LoadStore
+ prefetch [%o1 + 0x000], #one_read
+ prefetch [%o1 + 0x040], #one_read
+ andn %o2, (0x40 - 1), %o4
+ prefetch [%o1 + 0x080], #one_read
+ prefetch [%o1 + 0x0c0], #one_read
+ ldd [%o1 + 0x000], %f0
+ prefetch [%o1 + 0x100], #one_read
+ ldd [%o1 + 0x008], %f2
+ prefetch [%o1 + 0x140], #one_read
+ ldd [%o1 + 0x010], %f4
+ prefetch [%o1 + 0x180], #one_read
+ faligndata %f0, %f2, %f16
+ ldd [%o1 + 0x018], %f6
+ faligndata %f2, %f4, %f18
+ ldd [%o1 + 0x020], %f8
+ faligndata %f4, %f6, %f20
+ ldd [%o1 + 0x028], %f10
+ faligndata %f6, %f8, %f22
+
+ ldd [%o1 + 0x030], %f12
+ faligndata %f8, %f10, %f24
+ ldd [%o1 + 0x038], %f14
+ faligndata %f10, %f12, %f26
+ ldd [%o1 + 0x040], %f0
+
+ sub %o4, 0x80, %o4
+ add %o1, 0x40, %o1
+ ba,pt %xcc, 1f
+ srl %o4, 6, %o3
+
+ .align 64
+1:
+ ldd [%o1 + 0x008], %f2
+ faligndata %f12, %f14, %f28
+ ldd [%o1 + 0x010], %f4
+ faligndata %f14, %f0, %f30
+ stda %f16, [%o0] ASI_BLK_P
+ ldd [%o1 + 0x018], %f6
+ faligndata %f0, %f2, %f16
+
+ ldd [%o1 + 0x020], %f8
+ faligndata %f2, %f4, %f18
+ ldd [%o1 + 0x028], %f10
+ faligndata %f4, %f6, %f20
+ ldd [%o1 + 0x030], %f12
+ faligndata %f6, %f8, %f22
+ ldd [%o1 + 0x038], %f14
+ faligndata %f8, %f10, %f24
+
+ ldd [%o1 + 0x040], %f0
+ prefetch [%o1 + 0x180], #one_read
+ faligndata %f10, %f12, %f26
+ subcc %o3, 0x01, %o3
+ add %o1, 0x40, %o1
+ bg,pt %XCC, 1b
+ add %o0, 0x40, %o0
/* Finally we copy the last full 64-byte block. */
-U3memcpy_loopfini:
- ldd [%o1 + 0x008], %f2 ! MS
- faligndata %f12, %f14, %f28 ! FGA
- ldd [%o1 + 0x010], %f4 ! MS Group19
- faligndata %f14, %f0, %f30 ! FGA
- stda %f16, [%o0] ASI_BLK_P ! MS Group20
- ldd [%o1 + 0x018], %f6 ! AX
- faligndata %f0, %f2, %f16 ! FGA Group11 (7-cycle stall)
- ldd [%o1 + 0x020], %f8 ! MS
- faligndata %f2, %f4, %f18 ! FGA Group12
- ldd [%o1 + 0x028], %f10 ! MS
- faligndata %f4, %f6, %f20 ! FGA Group13
- ldd [%o1 + 0x030], %f12 ! MS
- faligndata %f6, %f8, %f22 ! FGA Group14
- ldd [%o1 + 0x038], %f14 ! MS
- faligndata %f8, %f10, %f24 ! FGA Group15
- cmp %g1, 0 ! A0
- be,pt %icc, 1f ! BR
- add %o0, 0x40, %o0 ! A1
- ldd [%o1 + 0x040], %f0 ! MS
-1: faligndata %f10, %f12, %f26 ! FGA Group16
- faligndata %f12, %f14, %f28 ! FGA Group17
- faligndata %f14, %f0, %f30 ! FGA Group18
- stda %f16, [%o0] ASI_BLK_P ! MS
- add %o0, 0x40, %o0 ! A0
- add %o1, 0x40, %o1 ! A1
-#ifdef __KERNEL__
- .globl U3memcpy_nop_2_3
-U3memcpy_nop_2_3:
- mov PRIMARY_CONTEXT, %o3
- stxa %g0, [%o3] ASI_DMMU ! Flush P-cache
- stxa %g3, [%g0] ASI_DCU_CONTROL_REG ! Disable P-cache
-#endif
- membar #Sync ! MS Group26 (7-cycle stall)
+ ldd [%o1 + 0x008], %f2
+ faligndata %f12, %f14, %f28
+ ldd [%o1 + 0x010], %f4
+ faligndata %f14, %f0, %f30
+ stda %f16, [%o0] ASI_BLK_P
+ ldd [%o1 + 0x018], %f6
+ faligndata %f0, %f2, %f16
+ ldd [%o1 + 0x020], %f8
+ faligndata %f2, %f4, %f18
+ ldd [%o1 + 0x028], %f10
+ faligndata %f4, %f6, %f20
+ ldd [%o1 + 0x030], %f12
+ faligndata %f6, %f8, %f22
+ ldd [%o1 + 0x038], %f14
+ faligndata %f8, %f10, %f24
+ cmp %g1, 0
+ be,pt %XCC, 1f
+ add %o0, 0x40, %o0
+ ldd [%o1 + 0x040], %f0
+1: faligndata %f10, %f12, %f26
+ faligndata %f12, %f14, %f28
+ faligndata %f14, %f0, %f30
+ stda %f16, [%o0] ASI_BLK_P
+ add %o0, 0x40, %o0
+ add %o1, 0x40, %o1
+ membar #Sync
/* Now we copy the (len modulo 64) bytes at the end.
* Note how we borrow the %f0 loaded above.
*
* Also notice how this code is careful not to perform a
- * load past the end of the src buffer just like similar
- * code found in U3memcpy_toosmall processing.
+ * load past the end of the src buffer.
*/
-U3memcpy_loopend:
- and %o2, 0x3f, %o2 ! A0 Group
- andcc %o2, 0x38, %g2 ! A0 Group
- be,pn %icc, U3memcpy_endcruft ! BR
- subcc %g2, 0x8, %g2 ! A1
- be,pn %icc, U3memcpy_endcruft ! BR Group
- cmp %g1, 0 ! A0
-
- be,a,pt %icc, 1f ! BR Group
- ldd [%o1 + 0x00], %f0 ! MS
-
-1: ldd [%o1 + 0x08], %f2 ! MS Group
- add %o1, 0x8, %o1 ! A0
- sub %o2, 0x8, %o2 ! A1
- subcc %g2, 0x8, %g2 ! A0 Group
- faligndata %f0, %f2, %f8 ! FGA Group
- std %f8, [%o0 + 0x00] ! MS (XXX does it stall here? XXX)
- be,pn %icc, U3memcpy_endcruft ! BR
- add %o0, 0x8, %o0 ! A0
- ldd [%o1 + 0x08], %f0 ! MS Group
- add %o1, 0x8, %o1 ! A0
- sub %o2, 0x8, %o2 ! A1
- subcc %g2, 0x8, %g2 ! A0 Group
- faligndata %f2, %f0, %f8 ! FGA
- std %f8, [%o0 + 0x00] ! MS (XXX does it stall here? XXX)
- bne,pn %icc, 1b ! BR
- add %o0, 0x8, %o0 ! A0 Group
+ and %o2, 0x3f, %o2
+ andcc %o2, 0x38, %g2
+ be,pn %XCC, 2f
+ subcc %g2, 0x8, %g2
+ be,pn %XCC, 2f
+ cmp %g1, 0
+
+ be,a,pt %XCC, 1f
+ ldd [%o1 + 0x00], %f0
+
+1: ldd [%o1 + 0x08], %f2
+ add %o1, 0x8, %o1
+ sub %o2, 0x8, %o2
+ subcc %g2, 0x8, %g2
+ faligndata %f0, %f2, %f8
+ std %f8, [%o0 + 0x00]
+ be,pn %XCC, 2f
+ add %o0, 0x8, %o0
+ ldd [%o1 + 0x08], %f0
+ add %o1, 0x8, %o1
+ sub %o2, 0x8, %o2
+ subcc %g2, 0x8, %g2
+ faligndata %f2, %f0, %f8
+ std %f8, [%o0 + 0x00]
+ bne,pn %XCC, 1b
+ add %o0, 0x8, %o0
/* If anything is left, we copy it one byte at a time.
* Note that %g1 is (src & 0x3) saved above before the
* alignaddr was performed.
*/
-U3memcpy_endcruft:
+2:
cmp %o2, 0
add %o1, %g1, %o1
VISExitHalf
- be,pn %icc, U3memcpy_short_ret
- nop
- ba,a,pt %xcc, U3memcpy_short
+ be,pn %XCC, 85f
+ sub %o0, %o1, %o3
- /* If we get here, then 32 <= len < (6 * 64) */
-U3memcpy_toosmall:
+ andcc %g1, 0x7, %g0
+ bne,pn %icc, 90f
+ andcc %o2, 0x8, %g0
+ be,pt %icc, 1f
+ nop
+ ldx [%o1], %o5
+ stx %o5, [%o1 + %o3]
+ add %o1, 0x8, %o1
-#ifdef SMALL_COPY_USES_FPU
+1: andcc %o2, 0x4, %g0
+ be,pt %icc, 1f
+ nop
+ lduw [%o1], %o5
+ stw %o5, [%o1 + %o3]
+ add %o1, 0x4, %o1
- /* Is 'dst' already aligned on an 8-byte boundary? */
- be,pt %xcc, 2f ! BR Group
+1: andcc %o2, 0x2, %g0
+ be,pt %icc, 1f
+ nop
+ lduh [%o1], %o5
+ sth %o5, [%o1 + %o3]
+ add %o1, 0x2, %o1
- /* Compute abs((dst & 7) - 8) into %g2. This is the number
- * of bytes to copy to make 'dst' 8-byte aligned. We pre-
- * subtract this from 'len'.
- */
- sub %g2, 0x8, %g2 ! A0
- sub %g0, %g2, %g2 ! A0 Group (reg-dep)
- sub %o2, %g2, %o2 ! A0 Group (reg-dep)
+1: andcc %o2, 0x1, %g0
+ be,pt %icc, 85f
+ nop
+ ldub [%o1], %o5
+ ba,pt %xcc, 85f
+ stb %o5, [%o1 + %o3]
+
+70: /* 16 < len <= 64 */
+ bne,pn %XCC, 90f
+ sub %o0, %o1, %o3
+
+ andn %o2, 0x7, %o4
+ and %o2, 0x7, %o2
+1: subcc %o4, 0x8, %o4
+ ldx [%o1], %o5
+ stx %o5, [%o1 + %o3]
+ bgu,pt %XCC, 1b
+ add %o1, 0x8, %o1
+ andcc %o2, 0x4, %g0
+ be,pt %XCC, 1f
+ nop
+ sub %o2, 0x4, %o2
+ lduw [%o1], %o5
+ stw %o5, [%o1 + %o3]
+ add %o1, 0x4, %o1
+1: cmp %o2, 0
+ be,pt %XCC, 85f
+ nop
+ ba,pt %xcc, 90f
+ nop
- /* Copy %g2 bytes from src to dst, one byte at a time. */
-1: ldub [%o1 + 0x00], %o3 ! MS (Group) (%o3 in 3 cycles)
- add %o1, 0x1, %o1 ! A1
- add %o0, 0x1, %o0 ! A0 Group
- subcc %g2, 0x1, %g2 ! A1
+80: /* 0 < len <= 16 */
+ andcc %o3, 0x3, %g0
+ bne,pn %XCC, 90f
+ sub %o0, %o1, %o3
- bg,pt %icc, 1b ! BR Group
- stb %o3, [%o0 + -1] ! MS Group
+1:
+ subcc %o2, 4, %o2
+ lduw [%o1], %g1
+ stw %g1, [%o1 + %o3]
+ bgu,pt %XCC, 1b
+ add %o1, 4, %o1
-2: VISEntryHalf ! MS+MS
+85: retl
+ mov %g5, %o0
- /* Compute (len - (len % 8)) into %g2. This is guaranteed
- * to be nonzero.
- */
- andn %o2, 0x7, %g2 ! A0 Group
-
- /* You may read this and believe that it allows reading
- * one 8-byte longword past the end of src. It actually
- * does not, as %g2 is subtracted as loads are done from
- * src, so we always stop before running off the end.
- * Also, we are guaranteed to have at least 0x10 bytes
- * to move here.
+ .align 32
+90:
+ subcc %o2, 1, %o2
+ ldub [%o1], %g1
+ stb %g1, [%o1 + %o3]
+ bgu,pt %XCC, 90b
+ add %o1, 1, %o1
+ retl
+ mov %g5, %o0
+
+ /* Act like copy_{to,in}_user(), ie. return zero instead
+ * of original destination pointer. This is invoked when
+ * copy_{to,in}_user() finds that %asi is kernel space.
*/
- sub %g2, 0x8, %g2 ! A0 Group (reg-dep)
- alignaddr %o1, %g0, %g1 ! MS (Break-after)
- ldd [%g1 + 0x00], %f0 ! MS Group (1-cycle stall)
- add %g1, 0x8, %g1 ! A0
-
-1: ldd [%g1 + 0x00], %f2 ! MS Group
- add %g1, 0x8, %g1 ! A0
- sub %o2, 0x8, %o2 ! A1
- subcc %g2, 0x8, %g2 ! A0 Group
-
- faligndata %f0, %f2, %f8 ! FGA Group (1-cycle stall)
- std %f8, [%o0 + 0x00] ! MS Group (2-cycle stall)
- add %o1, 0x8, %o1 ! A0
- be,pn %icc, 2f ! BR
-
- add %o0, 0x8, %o0 ! A1
- ldd [%g1 + 0x00], %f0 ! MS Group
- add %g1, 0x8, %g1 ! A0
- sub %o2, 0x8, %o2 ! A1
-
- subcc %g2, 0x8, %g2 ! A0 Group
- faligndata %f2, %f0, %f8 ! FGA Group (1-cycle stall)
- std %f8, [%o0 + 0x00] ! MS Group (2-cycle stall)
- add %o1, 0x8, %o1 ! A0
-
- bne,pn %icc, 1b ! BR
- add %o0, 0x8, %o0 ! A1
-
- /* Nothing left to copy? */
-2: cmp %o2, 0 ! A0 Group
- VISExitHalf ! A0+MS
- be,pn %icc, U3memcpy_short_ret ! BR Group
- nop ! A0
- ba,a,pt %xcc, U3memcpy_short ! BR Group
-
-#else /* !(SMALL_COPY_USES_FPU) */
-
- xor %o1, %o0, %g2
- andcc %g2, 0x7, %g0
- bne,pn %icc, U3memcpy_short
- andcc %o1, 0x7, %g2
-
- be,pt %xcc, 2f
- sub %g2, 0x8, %g2
- sub %g0, %g2, %g2
- sub %o2, %g2, %o2
-
-1: ldub [%o1 + 0x00], %o3
- add %o1, 0x1, %o1
- add %o0, 0x1, %o0
- subcc %g2, 0x1, %g2
- bg,pt %icc, 1b
- stb %o3, [%o0 + -1]
-
-2: andn %o2, 0x7, %g2
- sub %o2, %g2, %o2
-
-3: ldx [%o1 + 0x00], %o3
- add %o1, 0x8, %o1
- add %o0, 0x8, %o0
- subcc %g2, 0x8, %g2
- bg,pt %icc, 3b
- stx %o3, [%o0 + -8]
-
- cmp %o2, 0
- bne,pn %icc, U3memcpy_short
- nop
- ba,a,pt %xcc, U3memcpy_short_ret
-
-#endif /* !(SMALL_COPY_USES_FPU) */
+ .globl U3memcpy_user_stub
+U3memcpy_user_stub:
+ save %sp, -192, %sp
+ mov %i0, %o0
+ mov %i1, %o1
+ call U3memcpy
+ mov %i2, %o2
+ ret
+ restore %g0, %g0, %o0
.text
.align 32
#ifdef __KERNEL__
- .globl __bzero_begin
-__bzero_begin:
.globl __bzero, __bzero_noasi
__bzero_noasi:
rd %asi, %g5
ba,pt %xcc, VISbzerofixup_ret0
sub %o1, %g2, %o0
#endif
- .globl __bzero_end
-__bzero_end:
.type bcopy,@function
#ifdef __KERNEL__
- .globl __memcpy_begin
-__memcpy_begin:
-
- .globl __memcpy
- .type __memcpy,@function
-
memcpy_private:
-__memcpy:
memcpy: mov ASI_P, asi_src ! IEU0 Group
brnz,pt %o2, __memcpy_entry ! CTI
mov ASI_P, asi_dest ! IEU1
or %g3, %lo(NOP), %g3; \
stw %g3, [%g2 + 0x4]; \
flush %g2;
-#define ULTRA3_PCACHE_DO_NOP(symbol) \
- sethi %hi(symbol##_nop_1_6), %g1; \
- or %g1, %lo(symbol##_nop_1_6), %g1; \
- sethi %hi(NOP), %g2; \
- stw %g2, [%g1 + 0x00]; \
- stw %g2, [%g1 + 0x04]; \
- flush %g1 + 0x00; \
- stw %g2, [%g1 + 0x08]; \
- stw %g2, [%g1 + 0x0c]; \
- flush %g1 + 0x08; \
- stw %g2, [%g1 + 0x10]; \
- stw %g2, [%g1 + 0x04]; \
- flush %g1 + 0x10; \
- sethi %hi(symbol##_nop_2_3), %g1; \
- or %g1, %lo(symbol##_nop_2_3), %g1; \
- stw %g2, [%g1 + 0x00]; \
- stw %g2, [%g1 + 0x04]; \
- flush %g1 + 0x00; \
- stw %g2, [%g1 + 0x08]; \
- flush %g1 + 0x08;
-
-#include <asm/dcu.h>
.globl cheetah_patch_copyops
cheetah_patch_copyops:
ULTRA3_DO_PATCH(__copy_from_user, U3copy_from_user)
ULTRA3_DO_PATCH(__copy_to_user, U3copy_to_user)
ULTRA3_DO_PATCH(__copy_in_user, U3copy_in_user)
-#if 0 /* Causes data corruption, nop out the optimization
- * for now -DaveM
- */
- ldxa [%g0] ASI_DCU_CONTROL_REG, %g3
- sethi %uhi(DCU_PE), %o3
- sllx %o3, 32, %o3
- andcc %g3, %o3, %g0
- be,pn %xcc, pcache_disabled
- nop
-#endif
- ULTRA3_PCACHE_DO_NOP(U3memcpy)
- ULTRA3_PCACHE_DO_NOP(U3copy_from_user)
- ULTRA3_PCACHE_DO_NOP(U3copy_to_user)
- ULTRA3_PCACHE_DO_NOP(cheetah_copy_user_page)
-#if 0
-pcache_disabled:
-#endif
retl
nop
#undef BRANCH_ALWAYS
FPU_RETL
#ifdef __KERNEL__
- .globl __memcpy_end
-__memcpy_end:
-
.section .fixup
.align 4
VIScopyfixup_reto2:
.text
.align 64
- .globl atomic_impl_begin, atomic_impl_end
-
.globl __atomic_add
-atomic_impl_begin:
__atomic_add: /* %o0 = increment, %o1 = atomic_ptr */
lduw [%o1], %g5
add %g5, %o0, %g7
retl
sub %g7, %o0, %o0
-atomic_impl_end:
.text
.align 64
- .globl __bitops_begin
-__bitops_begin:
-
.globl ___test_and_set_bit
___test_and_set_bit: /* %o0=nr, %o1=addr */
srlx %o0, 6, %g1
lduwa [%o1] ASI_PL, %g7
2: retl
membar #StoreLoad | #StoreStore
-
- .globl __bitops_end
-__bitops_end:
--- /dev/null
+/* clear_page.S: UltraSparc optimized clear page.
+ *
+ * Copyright (C) 1996, 1998, 1999, 2000, 2004 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1997 Jakub Jelinek (jakub@redhat.com)
+ */
+
+#include <asm/visasm.h>
+#include <asm/thread_info.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/spitfire.h>
+
+ /* What we used to do was lock a TLB entry into a specific
+ * TLB slot, clear the page with interrupts disabled, then
+ * restore the original TLB entry. This was great for
+ * disturbing the TLB as little as possible, but it meant
+ * we had to keep interrupts disabled for a long time.
+ *
+ * Now, we simply use the normal TLB loading mechanism,
+ * and this makes the cpu choose a slot all by itself.
+ * Then we do a normal TLB flush on exit. We need only
+ * disable preemption during the clear.
+ */
+
+#define TTE_BITS_TOP (_PAGE_VALID | _PAGE_SZBITS)
+#define TTE_BITS_BOTTOM (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W)
+
+ .text
+
+ .globl _clear_page
+_clear_page: /* %o0=dest */
+ ba,pt %xcc, clear_page_common
+ clr %o4
+
+ /* This thing is pretty important, it shows up
+ * on the profiles via do_anonymous_page().
+ */
+ .align 32
+ .globl clear_user_page
+clear_user_page: /* %o0=dest, %o1=vaddr */
+ lduw [%g6 + TI_PRE_COUNT], %o2
+ sethi %uhi(PAGE_OFFSET), %g2
+ sethi %hi(PAGE_SIZE), %o4
+
+ sllx %g2, 32, %g2
+ sethi %uhi(TTE_BITS_TOP), %g3
+
+ sllx %g3, 32, %g3
+ sub %o0, %g2, %g1 ! paddr
+
+ or %g3, TTE_BITS_BOTTOM, %g3
+ and %o1, %o4, %o0 ! vaddr D-cache alias bit
+
+ or %g1, %g3, %g1 ! TTE data
+ sethi %hi(TLBTEMP_BASE), %o3
+
+ add %o2, 1, %o4
+ add %o0, %o3, %o0 ! TTE vaddr
+
+ /* Disable preemption. */
+ mov TLB_TAG_ACCESS, %g3
+ stw %o4, [%g6 + TI_PRE_COUNT]
+
+ /* Load TLB entry. */
+ rdpr %pstate, %o4
+ wrpr %o4, PSTATE_IE, %pstate
+ stxa %o0, [%g3] ASI_DMMU
+ stxa %g1, [%g0] ASI_DTLB_DATA_IN
+ flush %g6
+ wrpr %o4, 0x0, %pstate
+
+ mov 1, %o4
+
+clear_page_common:
+ VISEntryHalf
+ membar #StoreLoad | #StoreStore | #LoadStore
+ fzero %f0
+ sethi %hi(PAGE_SIZE/64), %o1
+ mov %o0, %g1 ! remember vaddr for tlbflush
+ fzero %f2
+ or %o1, %lo(PAGE_SIZE/64), %o1
+ faddd %f0, %f2, %f4
+ fmuld %f0, %f2, %f6
+ faddd %f0, %f2, %f8
+ fmuld %f0, %f2, %f10
+
+ faddd %f0, %f2, %f12
+ fmuld %f0, %f2, %f14
+1: stda %f0, [%o0 + %g0] ASI_BLK_P
+ subcc %o1, 1, %o1
+ bne,pt %icc, 1b
+ add %o0, 0x40, %o0
+ membar #Sync
+ VISExitHalf
+
+ brz,pn %o4, out
+ nop
+
+ stxa %g0, [%g1] ASI_DMMU_DEMAP
+ membar #Sync
+ stw %o2, [%g6 + TI_PRE_COUNT]
+
+out: retl
+ nop
+
--- /dev/null
+/* clear_page.S: UltraSparc optimized copy page.
+ *
+ * Copyright (C) 1996, 1998, 1999, 2000, 2004 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1997 Jakub Jelinek (jakub@redhat.com)
+ */
+
+#include <asm/visasm.h>
+#include <asm/thread_info.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/spitfire.h>
+#include <asm/head.h>
+
+ /* What we used to do was lock a TLB entry into a specific
+ * TLB slot, clear the page with interrupts disabled, then
+ * restore the original TLB entry. This was great for
+ * disturbing the TLB as little as possible, but it meant
+ * we had to keep interrupts disabled for a long time.
+ *
+ * Now, we simply use the normal TLB loading mechanism,
+ * and this makes the cpu choose a slot all by itself.
+ * Then we do a normal TLB flush on exit. We need only
+ * disable preemption during the clear.
+ */
+
+#define TTE_BITS_TOP (_PAGE_VALID | _PAGE_SZBITS)
+#define TTE_BITS_BOTTOM (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W)
+#define DCACHE_SIZE (PAGE_SIZE * 2)
+
+#if (PAGE_SHIFT == 13) || (PAGE_SHIFT == 19)
+#define PAGE_SIZE_REM 0x80
+#elif (PAGE_SHIFT == 16) || (PAGE_SHIFT == 22)
+#define PAGE_SIZE_REM 0x100
+#else
+#error Wrong PAGE_SHIFT specified
+#endif
+
+#define TOUCH(reg0, reg1, reg2, reg3, reg4, reg5, reg6, reg7) \
+ fmovd %reg0, %f48; fmovd %reg1, %f50; \
+ fmovd %reg2, %f52; fmovd %reg3, %f54; \
+ fmovd %reg4, %f56; fmovd %reg5, %f58; \
+ fmovd %reg6, %f60; fmovd %reg7, %f62;
+
+ .text
+
+ .align 32
+ .globl copy_user_page
+copy_user_page: /* %o0=dest, %o1=src, %o2=vaddr */
+ lduw [%g6 + TI_PRE_COUNT], %o4
+ sethi %uhi(PAGE_OFFSET), %g2
+ sethi %hi(PAGE_SIZE), %o3
+
+ sllx %g2, 32, %g2
+ sethi %uhi(TTE_BITS_TOP), %g3
+
+ sllx %g3, 32, %g3
+ sub %o0, %g2, %g1 ! dest paddr
+
+ sub %o1, %g2, %g2 ! src paddr
+ or %g3, TTE_BITS_BOTTOM, %g3
+
+ and %o2, %o3, %o0 ! vaddr D-cache alias bit
+ or %g1, %g3, %g1 ! dest TTE data
+
+ or %g2, %g3, %g2 ! src TTE data
+ sethi %hi(TLBTEMP_BASE), %o3
+
+ sethi %hi(DCACHE_SIZE), %o1
+ add %o0, %o3, %o0 ! dest TTE vaddr
+
+ add %o4, 1, %o2
+ add %o0, %o1, %o1 ! src TTE vaddr
+
+ /* Disable preemption. */
+ mov TLB_TAG_ACCESS, %g3
+ stw %o2, [%g6 + TI_PRE_COUNT]
+
+ /* Load TLB entries. */
+ rdpr %pstate, %o2
+ wrpr %o2, PSTATE_IE, %pstate
+ stxa %o0, [%g3] ASI_DMMU
+ stxa %g1, [%g0] ASI_DTLB_DATA_IN
+ membar #Sync
+ stxa %o1, [%g3] ASI_DMMU
+ stxa %g2, [%g0] ASI_DTLB_DATA_IN
+ membar #Sync
+ wrpr %o2, 0x0, %pstate
+
+ BRANCH_IF_ANY_CHEETAH(g3,o2,1f)
+ ba,pt %xcc, 9f
+ nop
+
+1:
+ VISEntryHalf
+ membar #StoreLoad | #StoreStore | #LoadStore
+ sethi %hi((PAGE_SIZE/64)-2), %o2
+ mov %o0, %g1
+ prefetch [%o1 + 0x000], #one_read
+ or %o2, %lo((PAGE_SIZE/64)-2), %o2
+ prefetch [%o1 + 0x040], #one_read
+ prefetch [%o1 + 0x080], #one_read
+ prefetch [%o1 + 0x0c0], #one_read
+ ldd [%o1 + 0x000], %f0
+ prefetch [%o1 + 0x100], #one_read
+ ldd [%o1 + 0x008], %f2
+ prefetch [%o1 + 0x140], #one_read
+ ldd [%o1 + 0x010], %f4
+ prefetch [%o1 + 0x180], #one_read
+ fmovd %f0, %f16
+ ldd [%o1 + 0x018], %f6
+ fmovd %f2, %f18
+ ldd [%o1 + 0x020], %f8
+ fmovd %f4, %f20
+ ldd [%o1 + 0x028], %f10
+ fmovd %f6, %f22
+ ldd [%o1 + 0x030], %f12
+ fmovd %f8, %f24
+ ldd [%o1 + 0x038], %f14
+ fmovd %f10, %f26
+ ldd [%o1 + 0x040], %f0
+1: ldd [%o1 + 0x048], %f2
+ fmovd %f12, %f28
+ ldd [%o1 + 0x050], %f4
+ fmovd %f14, %f30
+ stda %f16, [%o0] ASI_BLK_P
+ ldd [%o1 + 0x058], %f6
+ fmovd %f0, %f16
+ ldd [%o1 + 0x060], %f8
+ fmovd %f2, %f18
+ ldd [%o1 + 0x068], %f10
+ fmovd %f4, %f20
+ ldd [%o1 + 0x070], %f12
+ fmovd %f6, %f22
+ ldd [%o1 + 0x078], %f14
+ fmovd %f8, %f24
+ ldd [%o1 + 0x080], %f0
+ prefetch [%o1 + 0x180], #one_read
+ fmovd %f10, %f26
+ subcc %o2, 1, %o2
+ add %o0, 0x40, %o0
+ bne,pt %xcc, 1b
+ add %o1, 0x40, %o1
+
+ ldd [%o1 + 0x048], %f2
+ fmovd %f12, %f28
+ ldd [%o1 + 0x050], %f4
+ fmovd %f14, %f30
+ stda %f16, [%o0] ASI_BLK_P
+ ldd [%o1 + 0x058], %f6
+ fmovd %f0, %f16
+ ldd [%o1 + 0x060], %f8
+ fmovd %f2, %f18
+ ldd [%o1 + 0x068], %f10
+ fmovd %f4, %f20
+ ldd [%o1 + 0x070], %f12
+ fmovd %f6, %f22
+ add %o0, 0x40, %o0
+ ldd [%o1 + 0x078], %f14
+ fmovd %f8, %f24
+ fmovd %f10, %f26
+ fmovd %f12, %f28
+ fmovd %f14, %f30
+ stda %f16, [%o0] ASI_BLK_P
+ membar #Sync
+ VISExitHalf
+ ba,pt %xcc, 5f
+ nop
+
+9:
+ VISEntry
+ ldub [%g6 + TI_FAULT_CODE], %g3
+ mov %o0, %g1
+ cmp %g3, 0
+ rd %asi, %g3
+ be,a,pt %icc, 1f
+ wr %g0, ASI_BLK_P, %asi
+ wr %g0, ASI_BLK_COMMIT_P, %asi
+1: ldda [%o1] ASI_BLK_P, %f0
+ add %o1, 0x40, %o1
+ ldda [%o1] ASI_BLK_P, %f16
+ add %o1, 0x40, %o1
+ sethi %hi(PAGE_SIZE), %o2
+1: TOUCH(f0, f2, f4, f6, f8, f10, f12, f14)
+ ldda [%o1] ASI_BLK_P, %f32
+ stda %f48, [%o0] %asi
+ add %o1, 0x40, %o1
+ sub %o2, 0x40, %o2
+ add %o0, 0x40, %o0
+ TOUCH(f16, f18, f20, f22, f24, f26, f28, f30)
+ ldda [%o1] ASI_BLK_P, %f0
+ stda %f48, [%o0] %asi
+ add %o1, 0x40, %o1
+ sub %o2, 0x40, %o2
+ add %o0, 0x40, %o0
+ TOUCH(f32, f34, f36, f38, f40, f42, f44, f46)
+ ldda [%o1] ASI_BLK_P, %f16
+ stda %f48, [%o0] %asi
+ sub %o2, 0x40, %o2
+ add %o1, 0x40, %o1
+ cmp %o2, PAGE_SIZE_REM
+ bne,pt %xcc, 1b
+ add %o0, 0x40, %o0
+#if (PAGE_SHIFT == 16) || (PAGE_SHIFT == 22)
+ TOUCH(f0, f2, f4, f6, f8, f10, f12, f14)
+ ldda [%o1] ASI_BLK_P, %f32
+ stda %f48, [%o0] %asi
+ add %o1, 0x40, %o1
+ sub %o2, 0x40, %o2
+ add %o0, 0x40, %o0
+ TOUCH(f16, f18, f20, f22, f24, f26, f28, f30)
+ ldda [%o1] ASI_BLK_P, %f0
+ stda %f48, [%o0] %asi
+ add %o1, 0x40, %o1
+ sub %o2, 0x40, %o2
+ add %o0, 0x40, %o0
+ membar #Sync
+ stda %f32, [%o0] %asi
+ add %o0, 0x40, %o0
+ stda %f0, [%o0] %asi
+#else
+ membar #Sync
+ stda %f0, [%o0] %asi
+ add %o0, 0x40, %o0
+ stda %f16, [%o0] %asi
+#endif
+ membar #Sync
+ wr %g3, 0x0, %asi
+ VISExit
+
+5:
+ stxa %g0, [%g1] ASI_DMMU_DEMAP
+ membar #Sync
+
+ sethi %hi(DCACHE_SIZE), %g2
+ stxa %g0, [%g1 + %g2] ASI_DMMU_DEMAP
+ membar #Sync
+
+ retl
+ stw %o4, [%g6 + TI_PRE_COUNT]
.text
.align 64
- .globl rwlock_impl_begin, rwlock_impl_end
-
/* The non-contention read lock usage is 2 cache lines. */
.globl __read_lock, __read_unlock
-rwlock_impl_begin:
__read_lock: /* %o0 = lock_ptr */
ldsw [%o0], %g5
brlz,pn %g5, __read_wait_for_writer
__write_trylock_fail:
retl
mov 0, %o0
-rwlock_impl_end:
.text
.align 64
+ .globl _raw_spin_lock
+_raw_spin_lock: /* %o0 = lock_ptr */
+1: ldstub [%o0], %g7
+ brnz,pn %g7, 2f
+ membar #StoreLoad | #StoreStore
+ retl
+ nop
+2: ldub [%o0], %g7
+ brnz,pt %g7, 2b
+ membar #LoadLoad
+ ba,a,pt %xcc, 1b
+
.globl _raw_spin_lock_flags
_raw_spin_lock_flags: /* %o0 = lock_ptr, %o1 = irq_flags */
1: ldstub [%o0], %g7
EXTRA_AFLAGS := -ansi
EXTRA_CFLAGS := -Werror
-obj-y := ultra.o fault.o init.o generic.o extable.o
+obj-y := ultra.o tlb.o fault.o init.o generic.o extable.o
obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
--- /dev/null
+/* arch/sparc64/mm/tlb.c
+ *
+ * Copyright (C) 2004 David S. Miller <davem@redhat.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/percpu.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/tlbflush.h>
+#include <asm/cacheflush.h>
+#include <asm/mmu_context.h>
+#include <asm/tlb.h>
+
+/* Heavily inspired by the ppc64 code. */
+
+DEFINE_PER_CPU(struct mmu_gather, mmu_gathers) =
+ { NULL, 0, 0, 0, 0, 0, { 0 }, { NULL }, };
+
+void flush_tlb_pending(void)
+{
+ struct mmu_gather *mp = &__get_cpu_var(mmu_gathers);
+
+ if (mp->tlb_nr) {
+ unsigned long context = mp->mm->context;
+
+ if (CTX_VALID(context)) {
+#ifdef CONFIG_SMP
+ smp_flush_tlb_pending(mp->mm, mp->tlb_nr,
+ &mp->vaddrs[0]);
+#else
+ __flush_tlb_pending(CTX_HWBITS(context), mp->tlb_nr,
+ &mp->vaddrs[0]);
+#endif
+ }
+ mp->tlb_nr = 0;
+ }
+}
+
+void tlb_batch_add(pte_t *ptep, pte_t orig)
+{
+ struct mmu_gather *mp = &__get_cpu_var(mmu_gathers);
+ struct page *ptepage;
+ struct mm_struct *mm;
+ unsigned long vaddr, nr;
+
+ ptepage = virt_to_page(ptep);
+ mm = (struct mm_struct *) ptepage->mapping;
+
+ /* It is more efficient to let flush_tlb_kernel_range()
+ * handle these cases.
+ */
+ if (mm == &init_mm)
+ return;
+
+ vaddr = ptepage->index +
+ (((unsigned long)ptep & ~PAGE_MASK) * PTRS_PER_PTE);
+ if (pte_exec(orig))
+ vaddr |= 0x1UL;
+
+ if (pte_dirty(orig)) {
+ unsigned long paddr, pfn = pte_pfn(orig);
+ struct address_space *mapping;
+ struct page *page;
+
+ if (!pfn_valid(pfn))
+ goto no_cache_flush;
+
+ page = pfn_to_page(pfn);
+ if (PageReserved(page))
+ goto no_cache_flush;
+
+ /* A real file page? */
+ mapping = page_mapping(page);
+ if (!mapping)
+ goto no_cache_flush;
+
+ paddr = (unsigned long) page_address(page);
+ if ((paddr ^ vaddr) & (1 << 13))
+ flush_dcache_page_all(mm, page);
+ }
+
+no_cache_flush:
+ if (mp->tlb_frozen)
+ return;
+
+ nr = mp->tlb_nr;
+
+ if (unlikely(nr != 0 && mm != mp->mm)) {
+ flush_tlb_pending();
+ nr = 0;
+ }
+
+ if (nr == 0)
+ mp->mm = mm;
+
+ mp->vaddrs[nr] = vaddr;
+ mp->tlb_nr = ++nr;
+ if (nr >= TLB_BATCH_NR)
+ flush_tlb_pending();
+}
+
+void flush_tlb_pgtables(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+ struct mmu_gather *mp = &__get_cpu_var(mmu_gathers);
+ unsigned long nr = mp->tlb_nr;
+ long s = start, e = end, vpte_base;
+
+ if (mp->tlb_frozen)
+ return;
+
+ /* Nobody should call us with start below VM hole and end above.
+ * See if it is really true.
+ */
+ BUG_ON(s > e);
+
+#if 0
+ /* Currently free_pgtables guarantees this. */
+ s &= PMD_MASK;
+ e = (e + PMD_SIZE - 1) & PMD_MASK;
+#endif
+ vpte_base = (tlb_type == spitfire ?
+ VPTE_BASE_SPITFIRE :
+ VPTE_BASE_CHEETAH);
+
+ if (unlikely(nr != 0 && mm != mp->mm)) {
+ flush_tlb_pending();
+ nr = 0;
+ }
+
+ if (nr == 0)
+ mp->mm = mm;
+
+ start = vpte_base + (s >> (PAGE_SHIFT - 3));
+ end = vpte_base + (e >> (PAGE_SHIFT - 3));
+ while (start < end) {
+ mp->vaddrs[nr] = start;
+ mp->tlb_nr = ++nr;
+ if (nr >= TLB_BATCH_NR) {
+ flush_tlb_pending();
+ nr = 0;
+ }
+ start += PAGE_SIZE;
+ }
+ if (nr)
+ flush_tlb_pending();
+}
+
+unsigned long __ptrs_per_pmd(void)
+{
+ if (test_thread_flag(TIF_32BIT))
+ return (1UL << (32 - (PAGE_SHIFT-3) - PAGE_SHIFT));
+ return REAL_PTRS_PER_PMD;
+}
*/
.text
.align 32
- .globl __flush_tlb_page, __flush_tlb_mm, __flush_tlb_range
-__flush_tlb_page: /* %o0=(ctx & TAG_CONTEXT_BITS), %o1=page&PAGE_MASK, %o2=SECONDARY_CONTEXT */
- ldxa [%o2] ASI_DMMU, %g2
- cmp %g2, %o0
- bne,pn %icc, __spitfire_flush_tlb_page_slow
- or %o1, 0x10, %g3
- stxa %g0, [%g3] ASI_DMMU_DEMAP
- stxa %g0, [%g3] ASI_IMMU_DEMAP
- retl
- flush %g6
- nop
- nop
- nop
- nop
- nop
- nop
- nop
- nop
-
+ .globl __flush_tlb_mm
__flush_tlb_mm: /* %o0=(ctx & TAG_CONTEXT_BITS), %o1=SECONDARY_CONTEXT */
ldxa [%o1] ASI_DMMU, %g2
cmp %g2, %o0
nop
nop
-__flush_tlb_range: /* %o0=(ctx&TAG_CONTEXT_BITS), %o1=start&PAGE_MASK, %o2=SECONDARY_CONTEXT,
- * %o3=end&PAGE_MASK, %o4=PAGE_SIZE, %o5=(end - start)
- */
-#define TLB_MAGIC 207 /* Students, do you know how I calculated this? -DaveM */
- cmp %o5, %o4
- bleu,pt %xcc, __flush_tlb_page
- srlx %o5, PAGE_SHIFT, %g5
- cmp %g5, TLB_MAGIC
- bgeu,pn %icc, __spitfire_flush_tlb_range_constant_time
- or %o1, 0x10, %g5
- ldxa [%o2] ASI_DMMU, %g2
- cmp %g2, %o0
-__spitfire_flush_tlb_range_page_by_page:
- bne,pn %icc, __spitfire_flush_tlb_range_pbp_slow
- sub %o5, %o4, %o5
-1: stxa %g0, [%g5 + %o5] ASI_DMMU_DEMAP
- stxa %g0, [%g5 + %o5] ASI_IMMU_DEMAP
- brnz,pt %o5, 1b
- sub %o5, %o4, %o5
- retl
- flush %g6
-__spitfire_flush_tlb_range_constant_time: /* %o0=ctx, %o1=start, %o3=end */
- rdpr %pstate, %g1
- wrpr %g1, PSTATE_IE, %pstate
- mov TLB_TAG_ACCESS, %g3
- mov ((SPITFIRE_HIGHEST_LOCKED_TLBENT-1) << 3), %g2
-
- /* Spitfire Errata #32 workaround. */
- mov 0x8, %o4
- stxa %g0, [%o4] ASI_DMMU
- flush %g6
-
-1: ldxa [%g2] ASI_ITLB_TAG_READ, %o4
- and %o4, TAG_CONTEXT_BITS, %o5
- cmp %o5, %o0
- bne,pt %icc, 2f
- andn %o4, TAG_CONTEXT_BITS, %o4
- cmp %o4, %o1
- blu,pt %xcc, 2f
- cmp %o4, %o3
- blu,pn %xcc, 4f
-2: ldxa [%g2] ASI_DTLB_TAG_READ, %o4
- and %o4, TAG_CONTEXT_BITS, %o5
- cmp %o5, %o0
- andn %o4, TAG_CONTEXT_BITS, %o4
- bne,pt %icc, 3f
- cmp %o4, %o1
- blu,pt %xcc, 3f
- cmp %o4, %o3
- blu,pn %xcc, 5f
- nop
-3: brnz,pt %g2, 1b
- sub %g2, (1 << 3), %g2
- retl
- wrpr %g1, 0x0, %pstate
-4: stxa %g0, [%g3] ASI_IMMU
- stxa %g0, [%g2] ASI_ITLB_DATA_ACCESS
- flush %g6
-
- /* Spitfire Errata #32 workaround. */
- mov 0x8, %o4
- stxa %g0, [%o4] ASI_DMMU
- flush %g6
-
- ba,pt %xcc, 2b
+ .align 32
+ .globl __flush_tlb_pending
+__flush_tlb_pending:
+ /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
+ rdpr %pstate, %g5
+ sllx %o1, 3, %o1
+ andn %g5, PSTATE_IE, %g2
+ wrpr %g2, %pstate
+ mov SECONDARY_CONTEXT, %o4
+ ldxa [%o4] ASI_DMMU, %g2
+ stxa %o0, [%o4] ASI_DMMU
+1: sub %o1, (1 << 3), %o1
+ ldx [%o2 + %o1], %o3
+ andcc %o3, 1, %g0
+ andn %o3, 1, %o3
+ be,pn %icc, 2f
+ or %o3, 0x10, %o3
+ stxa %g0, [%o3] ASI_IMMU_DEMAP
+2: stxa %g0, [%o3] ASI_DMMU_DEMAP
+ membar #Sync
+ brnz,pt %o1, 1b
nop
-
-5: stxa %g0, [%g3] ASI_DMMU
- stxa %g0, [%g2] ASI_DTLB_DATA_ACCESS
+ stxa %g2, [%o4] ASI_DMMU
flush %g6
-
- /* Spitfire Errata #32 workaround. */
- mov 0x8, %o4
- stxa %g0, [%o4] ASI_DMMU
- flush %g6
-
- ba,pt %xcc, 3b
- nop
+ retl
+ wrpr %g5, 0x0, %pstate
.align 32
.globl __flush_tlb_kernel_range
retl
wrpr %g1, 0, %pstate
-__spitfire_flush_tlb_page_slow:
- rdpr %pstate, %g1
- wrpr %g1, PSTATE_IE, %pstate
- stxa %o0, [%o2] ASI_DMMU
- stxa %g0, [%g3] ASI_DMMU_DEMAP
- stxa %g0, [%g3] ASI_IMMU_DEMAP
- flush %g6
- stxa %g2, [%o2] ASI_DMMU
- flush %g6
- retl
- wrpr %g1, 0, %pstate
-
-__spitfire_flush_tlb_range_pbp_slow:
- rdpr %pstate, %g1
- wrpr %g1, PSTATE_IE, %pstate
- stxa %o0, [%o2] ASI_DMMU
-
-2: stxa %g0, [%g5 + %o5] ASI_DMMU_DEMAP
- stxa %g0, [%g5 + %o5] ASI_IMMU_DEMAP
- brnz,pt %o5, 2b
- sub %o5, %o4, %o5
- flush %g6
- stxa %g2, [%o2] ASI_DMMU
- flush %g6
- retl
- wrpr %g1, 0x0, %pstate
-
/*
* The following code flushes one page_size worth.
*/
ba,a,pt %xcc, __prefill_itlb
/* Cheetah specific versions, patched at boot time. */
-__cheetah_flush_tlb_page: /* 14 insns */
- rdpr %pstate, %g5
- andn %g5, PSTATE_IE, %g2
- wrpr %g2, 0x0, %pstate
- wrpr %g0, 1, %tl
- mov PRIMARY_CONTEXT, %o2
- ldxa [%o2] ASI_DMMU, %g2
- stxa %o0, [%o2] ASI_DMMU
- stxa %g0, [%o1] ASI_DMMU_DEMAP
- stxa %g0, [%o1] ASI_IMMU_DEMAP
- stxa %g2, [%o2] ASI_DMMU
- flush %g6
- wrpr %g0, 0, %tl
- retl
- wrpr %g5, 0x0, %pstate
-
__cheetah_flush_tlb_mm: /* 15 insns */
rdpr %pstate, %g5
andn %g5, PSTATE_IE, %g2
retl
wrpr %g5, 0x0, %pstate
-__cheetah_flush_tlb_range: /* 20 insns */
- cmp %o5, %o4
- blu,pt %xcc, 9f
- rdpr %pstate, %g5
+__cheetah_flush_tlb_pending: /* 22 insns */
+ /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
+ rdpr %pstate, %g5
+ sllx %o1, 3, %o1
andn %g5, PSTATE_IE, %g2
wrpr %g2, 0x0, %pstate
wrpr %g0, 1, %tl
- mov PRIMARY_CONTEXT, %o2
- sub %o5, %o4, %o5
- ldxa [%o2] ASI_DMMU, %g2
- stxa %o0, [%o2] ASI_DMMU
-1: stxa %g0, [%o1 + %o5] ASI_DMMU_DEMAP
- stxa %g0, [%o1 + %o5] ASI_IMMU_DEMAP
- membar #Sync
- brnz,pt %o5, 1b
- sub %o5, %o4, %o5
- stxa %g2, [%o2] ASI_DMMU
+ mov PRIMARY_CONTEXT, %o4
+ ldxa [%o4] ASI_DMMU, %g2
+ stxa %o0, [%o4] ASI_DMMU
+1: sub %o1, (1 << 3), %o1
+ ldx [%o2 + %o1], %o3
+ andcc %o3, 1, %g0
+ be,pn %icc, 2f
+ andn %o3, 1, %o3
+ stxa %g0, [%o3] ASI_IMMU_DEMAP
+2: stxa %g0, [%o3] ASI_DMMU_DEMAP
+ brnz,pt %o1, 1b
+ membar #Sync
+ stxa %g2, [%o4] ASI_DMMU
flush %g6
wrpr %g0, 0, %tl
-9: retl
+ retl
wrpr %g5, 0x0, %pstate
flush_dcpage_cheetah: /* 11 insns */
cheetah_patch_cachetlbops:
save %sp, -128, %sp
- sethi %hi(__flush_tlb_page), %o0
- or %o0, %lo(__flush_tlb_page), %o0
- sethi %hi(__cheetah_flush_tlb_page), %o1
- or %o1, %lo(__cheetah_flush_tlb_page), %o1
- call cheetah_patch_one
- mov 14, %o2
-
sethi %hi(__flush_tlb_mm), %o0
or %o0, %lo(__flush_tlb_mm), %o0
sethi %hi(__cheetah_flush_tlb_mm), %o1
call cheetah_patch_one
mov 15, %o2
- sethi %hi(__flush_tlb_range), %o0
- or %o0, %lo(__flush_tlb_range), %o0
- sethi %hi(__cheetah_flush_tlb_range), %o1
- or %o1, %lo(__cheetah_flush_tlb_range), %o1
+ sethi %hi(__flush_tlb_pending), %o0
+ or %o0, %lo(__flush_tlb_pending), %o0
+ sethi %hi(__cheetah_flush_tlb_pending), %o1
+ or %o1, %lo(__cheetah_flush_tlb_pending), %o1
call cheetah_patch_one
- mov 20, %o2
+ mov 22, %o2
sethi %hi(__flush_dcache_page), %o0
or %o0, %lo(__flush_dcache_page), %o0
* TODO: Make xcall TLB range flushes use the tricks above... -DaveM
*/
.align 32
- .globl xcall_flush_tlb_page, xcall_flush_tlb_mm, xcall_flush_tlb_range
-xcall_flush_tlb_page:
- mov PRIMARY_CONTEXT, %g2
- ldxa [%g2] ASI_DMMU, %g3
- stxa %g5, [%g2] ASI_DMMU
- stxa %g0, [%g1] ASI_DMMU_DEMAP
- stxa %g0, [%g1] ASI_IMMU_DEMAP
- stxa %g3, [%g2] ASI_DMMU
- retry
- nop
-
+ .globl xcall_flush_tlb_mm
xcall_flush_tlb_mm:
mov PRIMARY_CONTEXT, %g2
mov 0x40, %g4
stxa %g3, [%g2] ASI_DMMU
retry
-xcall_flush_tlb_range:
- sethi %hi(PAGE_SIZE - 1), %g2
- or %g2, %lo(PAGE_SIZE - 1), %g2
- andn %g1, %g2, %g1
- andn %g7, %g2, %g7
- sub %g7, %g1, %g3
- add %g2, 1, %g2
- srlx %g3, PAGE_SHIFT, %g4
- cmp %g4, 96
-
- bgu,pn %icc, xcall_flush_tlb_mm
- mov PRIMARY_CONTEXT, %g4
- ldxa [%g4] ASI_DMMU, %g7
- sub %g3, %g2, %g3
+ .globl xcall_flush_tlb_pending
+xcall_flush_tlb_pending:
+ /* %g5=context, %g1=nr, %g7=vaddrs[] */
+ sllx %g1, 3, %g1
+ mov PRIMARY_CONTEXT, %g4
+ ldxa [%g4] ASI_DMMU, %g2
stxa %g5, [%g4] ASI_DMMU
- nop
- nop
- nop
-
-1: stxa %g0, [%g1 + %g3] ASI_DMMU_DEMAP
- stxa %g0, [%g1 + %g3] ASI_IMMU_DEMAP
+1: sub %g1, (1 << 3), %g1
+ ldx [%g7 + %g1], %g5
+ andcc %g5, 0x1, %g0
+ be,pn %icc, 2f
+
+ andn %g5, 0x1, %g5
+ stxa %g0, [%g5] ASI_IMMU_DEMAP
+2: stxa %g0, [%g5] ASI_DMMU_DEMAP
membar #Sync
- brnz,pt %g3, 1b
- sub %g3, %g2, %g3
- stxa %g7, [%g4] ASI_DMMU
+ brnz,pt %g1, 1b
+ nop
+ stxa %g2, [%g4] ASI_DMMU
retry
- nop
- nop
.globl xcall_flush_tlb_kernel_range
xcall_flush_tlb_kernel_range:
retry
nop
nop
- nop
/* This runs in a very controlled environment, so we do
* not need to worry about BH races etc.
__asm__ ("srl %0, 0, %0" \
: "=r" (__ret) \
: "0" (__x)); \
- __ret; \
+ (void __user *)__ret; \
})
extern unsigned sys_call_table[];
#define UFSMAGIC (((unsigned)'u'<<24)||((unsigned)'f'<<16)||((unsigned)'s'<<8))
-static inline int putstat(struct sol_stat *ubuf, struct kstat *kbuf)
+static inline int putstat(struct sol_stat __user *ubuf, struct kstat *kbuf)
{
if (kbuf->size > MAX_NON_LFS ||
!sysv_valid_dev(kbuf->dev) ||
__put_user (kbuf->ctime.tv_nsec, &ubuf->st_ctime.tv_nsec) ||
__put_user (kbuf->blksize, &ubuf->st_blksize) ||
__put_user (kbuf->blocks, &ubuf->st_blocks) ||
- __put_user (UFSMAGIC, (unsigned *)ubuf->st_fstype))
+ __put_user (UFSMAGIC, (unsigned __user *)ubuf->st_fstype))
return -EFAULT;
return 0;
}
-static inline int putstat64(struct sol_stat64 *ubuf, struct kstat *kbuf)
+static inline int putstat64(struct sol_stat64 __user *ubuf, struct kstat *kbuf)
{
if (!sysv_valid_dev(kbuf->dev) || !sysv_valid_dev(kbuf->rdev))
return -EOVERFLOW;
__put_user (kbuf->ctime.tv_nsec, &ubuf->st_ctime.tv_nsec) ||
__put_user (kbuf->blksize, &ubuf->st_blksize) ||
__put_user (kbuf->blocks, &ubuf->st_blocks) ||
- __put_user (UFSMAGIC, (unsigned *)ubuf->st_fstype))
+ __put_user (UFSMAGIC, (unsigned __user *)ubuf->st_fstype))
return -EFAULT;
return 0;
}
asmlinkage int solaris_stat(u32 filename, u32 statbuf)
{
- int ret;
struct kstat s;
- char *filenam;
- mm_segment_t old_fs = get_fs();
-
- filenam = getname ((char *)A(filename));
- ret = PTR_ERR(filenam);
- if (!IS_ERR(filenam)) {
- set_fs (KERNEL_DS);
- ret = vfs_stat(filenam, &s);
- set_fs (old_fs);
- putname (filenam);
- return putstat((struct sol_stat *)A(statbuf), &s);
- }
+ int ret = vfs_stat(A(filename), &s);
+ if (!ret)
+ return putstat(A(statbuf), &s);
return ret;
}
asmlinkage int solaris_stat64(u32 filename, u32 statbuf)
{
- int ret;
struct kstat s;
- char *filenam;
- mm_segment_t old_fs = get_fs();
-
- filenam = getname ((char *)A(filename));
- ret = PTR_ERR(filenam);
- if (!IS_ERR(filenam)) {
- set_fs (KERNEL_DS);
- ret = vfs_stat(filenam, &s);
- set_fs (old_fs);
- putname (filenam);
- return putstat64((struct sol_stat64 *)A(statbuf), &s);
- }
+ int ret = vfs_stat(A(filename), &s);
+ if (!ret)
+ return putstat64(A(statbuf), &s);
return ret;
}
asmlinkage int solaris_lstat(u32 filename, u32 statbuf)
{
- int ret;
struct kstat s;
- char *filenam;
- mm_segment_t old_fs = get_fs();
-
- filenam = getname ((char *)A(filename));
- ret = PTR_ERR(filenam);
- if (!IS_ERR(filenam)) {
- set_fs (KERNEL_DS);
- ret = vfs_lstat(filenam, &s);
- set_fs (old_fs);
- putname (filenam);
- return putstat((struct sol_stat *)A(statbuf), &s);
- }
+ int ret = vfs_lstat(A(filename), &s);
+ if (!ret)
+ return putstat(A(statbuf), &s);
return ret;
}
asmlinkage int solaris_lstat64(u32 filename, u32 statbuf)
{
- int ret;
struct kstat s;
- char *filenam;
- mm_segment_t old_fs = get_fs();
-
- filenam = getname ((char *)A(filename));
- ret = PTR_ERR(filenam);
- if (!IS_ERR(filenam)) {
- set_fs (KERNEL_DS);
- ret = vfs_lstat(filenam, &s);
- set_fs (old_fs);
- putname (filenam);
- return putstat64((struct sol_stat64 *)A(statbuf), &s);
- }
+ int ret = vfs_lstat(A(filename), &s);
+ if (!ret)
+ return putstat64(A(statbuf), &s);
return ret;
}
asmlinkage int solaris_fstat(unsigned int fd, u32 statbuf)
{
- int ret;
struct kstat s;
- ret = vfs_fstat(fd, &s);
+ int ret = vfs_fstat(fd, &s);
if (!ret)
- return putstat((struct sol_stat *)A(statbuf), &s);
+ return putstat(A(statbuf), &s);
return ret;
}
asmlinkage int solaris_fstat64(unsigned int fd, u32 statbuf)
{
- int ret;
struct kstat s;
-
- ret = vfs_fstat(fd, &s);
+ int ret = vfs_fstat(fd, &s);
if (!ret)
- return putstat64((struct sol_stat64 *)A(statbuf), &s);
+ return putstat64(A(statbuf), &s);
return ret;
}
asmlinkage int solaris_mknod(u32 path, u32 mode, s32 dev)
{
- int (*sys_mknod)(const char *,int,unsigned) =
- (int (*)(const char *,int,unsigned))SYS(mknod);
+ int (*sys_mknod)(const char __user *,int,unsigned) =
+ (int (*)(const char __user *,int,unsigned))SYS(mknod);
int major = sysv_major(dev);
int minor = sysv_minor(dev);
/* minor is guaranteed to be OK for MKDEV, major might be not */
if (major > 0xfff)
return -EINVAL;
- return sys_mknod((const char *)A(path), mode,
- new_encode_dev(MKDEV(major,minor)));
+ return sys_mknod(A(path), mode, new_encode_dev(MKDEV(major,minor)));
}
asmlinkage int solaris_xmknod(int vers, u32 path, u32 mode, s32 dev)
return solaris_mknod(path, mode, dev);
}
-asmlinkage int solaris_getdents64(unsigned int fd, void *dirent, unsigned int count)
+asmlinkage int solaris_getdents64(unsigned int fd, void __user *dirent, unsigned int count)
{
- int (*sys_getdents)(unsigned int, void *, unsigned int) =
- (int (*)(unsigned int, void *, unsigned int))SYS(getdents);
+ int (*sys_getdents)(unsigned int, void __user *, unsigned int) =
+ (int (*)(unsigned int, void __user *, unsigned int))SYS(getdents);
return sys_getdents(fd, dirent, count);
}
int ret;
struct statfs s;
mm_segment_t old_fs = get_fs();
- int (*sys_statfs)(const char *,struct statfs *) =
- (int (*)(const char *,struct statfs *))SYS(statfs);
- struct sol_statfs *ss = (struct sol_statfs *)A(buf);
+ int (*sys_statfs)(const char __user *,struct statfs __user *) =
+ (int (*)(const char __user *,struct statfs __user *))SYS(statfs);
+ struct sol_statfs __user *ss = A(buf);
if (len != sizeof(struct sol_statfs)) return -EINVAL;
if (!fstype) {
+ /* FIXME: mixing userland and kernel pointers */
set_fs (KERNEL_DS);
- ret = sys_statfs((const char *)A(path), &s);
+ ret = sys_statfs(A(path), &s);
set_fs (old_fs);
if (!ret) {
if (put_user (s.f_type, &ss->f_type) ||
int ret;
struct statfs s;
mm_segment_t old_fs = get_fs();
- int (*sys_fstatfs)(unsigned,struct statfs *) =
- (int (*)(unsigned,struct statfs *))SYS(fstatfs);
- struct sol_statfs *ss = (struct sol_statfs *)A(buf);
+ int (*sys_fstatfs)(unsigned,struct statfs __user *) =
+ (int (*)(unsigned,struct statfs __user *))SYS(fstatfs);
+ struct sol_statfs __user *ss = A(buf);
if (len != sizeof(struct sol_statfs)) return -EINVAL;
if (!fstype) {
{
struct kstatfs s;
int error;
- struct sol_statvfs *ss = (struct sol_statvfs *)A(buf);
+ struct sol_statvfs __user *ss = A(buf);
error = vfs_statfs(mnt->mnt_sb, &s);
if (!error) {
__put_user (s.f_ffree, &ss->f_favail) ||
__put_user (sysv_encode_dev(inode->i_sb->s_dev), &ss->f_fsid) ||
__copy_to_user (ss->f_basetype,p,j) ||
- __put_user (0, (char *)&ss->f_basetype[j]) ||
+ __put_user (0, (char __user *)&ss->f_basetype[j]) ||
__put_user (s.f_namelen, &ss->f_namemax) ||
__put_user (i, &ss->f_flag) ||
__clear_user (&ss->f_fstr, 32))
{
struct kstatfs s;
int error;
- struct sol_statvfs64 *ss = (struct sol_statvfs64 *)A(buf);
+ struct sol_statvfs64 __user *ss = A(buf);
error = vfs_statfs(mnt->mnt_sb, &s);
if (!error) {
__put_user (s.f_ffree, &ss->f_favail) ||
__put_user (sysv_encode_dev(inode->i_sb->s_dev), &ss->f_fsid) ||
__copy_to_user (ss->f_basetype,p,j) ||
- __put_user (0, (char *)&ss->f_basetype[j]) ||
+ __put_user (0, (char __user *)&ss->f_basetype[j]) ||
__put_user (s.f_namelen, &ss->f_namemax) ||
__put_user (i, &ss->f_flag) ||
__clear_user (&ss->f_fstr, 32))
struct nameidata nd;
int error;
- error = user_path_walk((const char *)A(path),&nd);
+ error = user_path_walk(A(path),&nd);
if (!error) {
struct inode * inode = nd.dentry->d_inode;
error = report_statvfs(nd.mnt, inode, buf);
int error;
lock_kernel();
- error = user_path_walk((const char *)A(path), &nd);
+ error = user_path_walk(A(path), &nd);
if (!error) {
struct inode * inode = nd.dentry->d_inode;
error = report_statvfs64(nd.mnt, inode, buf);
case SOL_F_SETLKW:
{
struct flock f;
+ struct sol_flock __user *p = A(arg);
mm_segment_t old_fs = get_fs();
switch (cmd) {
case SOL_F_SETLKW: cmd = F_SETLKW; break;
}
- if (get_user (f.l_type, &((struct sol_flock *)A(arg))->l_type) ||
- __get_user (f.l_whence, &((struct sol_flock *)A(arg))->l_whence) ||
- __get_user (f.l_start, &((struct sol_flock *)A(arg))->l_start) ||
- __get_user (f.l_len, &((struct sol_flock *)A(arg))->l_len) ||
- __get_user (f.l_pid, &((struct sol_flock *)A(arg))->l_sysid))
+ if (get_user (f.l_type, &p->l_type) ||
+ __get_user (f.l_whence, &p->l_whence) ||
+ __get_user (f.l_start, &p->l_start) ||
+ __get_user (f.l_len, &p->l_len) ||
+ __get_user (f.l_pid, &p->l_sysid))
return -EFAULT;
set_fs(KERNEL_DS);
ret = sys_fcntl(fd, cmd, (unsigned long)&f);
set_fs(old_fs);
- if (__put_user (f.l_type, &((struct sol_flock *)A(arg))->l_type) ||
- __put_user (f.l_whence, &((struct sol_flock *)A(arg))->l_whence) ||
- __put_user (f.l_start, &((struct sol_flock *)A(arg))->l_start) ||
- __put_user (f.l_len, &((struct sol_flock *)A(arg))->l_len) ||
- __put_user (f.l_pid, &((struct sol_flock *)A(arg))->l_pid) ||
- __put_user (0, &((struct sol_flock *)A(arg))->l_sysid))
+ if (__put_user (f.l_type, &p->l_type) ||
+ __put_user (f.l_whence, &p->l_whence) ||
+ __put_user (f.l_start, &p->l_start) ||
+ __put_user (f.l_len, &p->l_len) ||
+ __put_user (f.l_pid, &p->l_pid) ||
+ __put_user (0, &p->l_sysid))
return -EFAULT;
return ret;
int (*sys_newftruncate)(unsigned int, unsigned long)=
(int (*)(unsigned int, unsigned long))SYS(ftruncate);
- if (get_user(length, &((struct sol_flock*)A(arg))->l_start))
+ if (get_user(length, &((struct sol_flock __user *)A(arg))->l_start))
return -EFAULT;
return sys_newftruncate(fd, length);
return -ENOSYS;
}
-asmlinkage int solaris_pread(unsigned int fd, char *buf, u32 count, u32 pos)
+asmlinkage int solaris_pread(unsigned int fd, char __user *buf, u32 count, u32 pos)
{
- ssize_t (*sys_pread64)(unsigned int, char *, size_t, loff_t) =
- (ssize_t (*)(unsigned int, char *, size_t, loff_t))SYS(pread64);
+ ssize_t (*sys_pread64)(unsigned int, char __user *, size_t, loff_t) =
+ (ssize_t (*)(unsigned int, char __user *, size_t, loff_t))SYS(pread64);
return sys_pread64(fd, buf, count, (loff_t)pos);
}
-asmlinkage int solaris_pwrite(unsigned int fd, char *buf, u32 count, u32 pos)
+asmlinkage int solaris_pwrite(unsigned int fd, char __user *buf, u32 count, u32 pos)
{
- ssize_t (*sys_pwrite64)(unsigned int, char *, size_t, loff_t) =
- (ssize_t (*)(unsigned int, char *, size_t, loff_t))SYS(pwrite64);
+ ssize_t (*sys_pwrite64)(unsigned int, char __user *, size_t, loff_t) =
+ (ssize_t (*)(unsigned int, char __user *, size_t, loff_t))SYS(pwrite64);
return sys_pwrite64(fd, buf, count, (loff_t)pos);
}
/* solaris_llseek returns long long - quite difficult */
asmlinkage long solaris_llseek(struct pt_regs *regs, u32 off_hi, u32 off_lo, int whence)
{
- int (*sys_llseek)(unsigned int, unsigned long, unsigned long, loff_t *, unsigned int) =
- (int (*)(unsigned int, unsigned long, unsigned long, loff_t *, unsigned int))SYS(_llseek);
+ int (*sys_llseek)(unsigned int, unsigned long, unsigned long, loff_t __user *, unsigned int) =
+ (int (*)(unsigned int, unsigned long, unsigned long, loff_t __user *, unsigned int))SYS(_llseek);
int ret;
mm_segment_t old_fs = get_fs();
loff_t retval;
/* Have to mask out all but lower 3 bits */
asmlinkage int solaris_access(u32 filename, long mode)
{
- int (*sys_access)(const char *, int) =
- (int (*)(const char *, int))SYS(access);
+ int (*sys_access)(const char __user *, int) =
+ (int (*)(const char __user *, int))SYS(access);
- return sys_access((const char *)A(filename), mode & 7);
+ return sys_access(A(filename), mode & 7);
}
u32 arg);
asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg);
-extern int timod_putmsg(unsigned int fd, char *ctl_buf, int ctl_len,
- char *data_buf, int data_len, int flags);
-extern int timod_getmsg(unsigned int fd, char *ctl_buf, int ctl_maxlen, int *ctl_len,
- char *data_buf, int data_maxlen, int *data_len, int *flags);
+extern int timod_putmsg(unsigned int fd, char __user *ctl_buf, int ctl_len,
+ char __user *data_buf, int data_len, int flags);
+extern int timod_getmsg(unsigned int fd, char __user *ctl_buf, int ctl_maxlen, int __user *ctl_len,
+ char __user *data_buf, int data_maxlen, int __user *data_len, int *flags);
/* termio* stuff {{{ */
static inline int linux_to_solaris_termio(unsigned int fd, unsigned int cmd, u32 arg)
{
+ struct solaris_termio __user *p = A(arg);
int ret;
- ret = sys_ioctl(fd, cmd, A(arg));
+ ret = sys_ioctl(fd, cmd, (unsigned long)p);
if (!ret) {
u32 cflag;
- if (__get_user (cflag, &((struct solaris_termio *)A(arg))->c_cflag))
+ if (__get_user (cflag, &p->c_cflag))
return -EFAULT;
cflag = linux_to_solaris_cflag(cflag);
- if (__put_user (cflag, &((struct solaris_termio *)A(arg))->c_cflag))
+ if (__put_user (cflag, &p->c_cflag))
return -EFAULT;
}
return ret;
struct solaris_termio s;
mm_segment_t old_fs = get_fs();
- if (copy_from_user (&s, (struct solaris_termio *)A(arg), sizeof(struct solaris_termio)))
+ if (copy_from_user (&s, (struct solaris_termio __user *)A(arg), sizeof(struct solaris_termio)))
return -EFAULT;
s.c_cflag = solaris_to_linux_cflag(s.c_cflag);
set_fs(KERNEL_DS);
ret = sys_ioctl(fd, cmd, (unsigned long)&s);
set_fs(old_fs);
if (!ret) {
- if (put_user (s.c_iflag, &((struct solaris_termios *)A(arg))->c_iflag) ||
- __put_user (s.c_oflag, &((struct solaris_termios *)A(arg))->c_oflag) ||
- __put_user (linux_to_solaris_cflag(s.c_cflag), &((struct solaris_termios *)A(arg))->c_cflag) ||
- __put_user (s.c_lflag, &((struct solaris_termios *)A(arg))->c_lflag) ||
- __copy_to_user (((struct solaris_termios *)A(arg))->c_cc, s.c_cc, 16) ||
- __clear_user (((struct solaris_termios *)A(arg))->c_cc + 16, 2))
+ struct solaris_termios __user *p = A(arg);
+ if (put_user (s.c_iflag, &p->c_iflag) ||
+ __put_user (s.c_oflag, &p->c_oflag) ||
+ __put_user (linux_to_solaris_cflag(s.c_cflag), &p->c_cflag) ||
+ __put_user (s.c_lflag, &p->c_lflag) ||
+ __copy_to_user (p->c_cc, s.c_cc, 16) ||
+ __clear_user (p->c_cc + 16, 2))
return -EFAULT;
}
return ret;
{
int ret;
struct solaris_termios s;
+ struct solaris_termios __user *p = A(arg);
mm_segment_t old_fs = get_fs();
set_fs(KERNEL_DS);
ret = sys_ioctl(fd, TCGETS, (unsigned long)&s);
set_fs(old_fs);
if (ret) return ret;
- if (put_user (s.c_iflag, &((struct solaris_termios *)A(arg))->c_iflag) ||
- __put_user (s.c_oflag, &((struct solaris_termios *)A(arg))->c_oflag) ||
- __put_user (s.c_cflag, &((struct solaris_termios *)A(arg))->c_cflag) ||
- __put_user (s.c_lflag, &((struct solaris_termios *)A(arg))->c_lflag) ||
- __copy_from_user (s.c_cc, ((struct solaris_termios *)A(arg))->c_cc, 16))
+ if (put_user (s.c_iflag, &p->c_iflag) ||
+ __put_user (s.c_oflag, &p->c_oflag) ||
+ __put_user (s.c_cflag, &p->c_cflag) ||
+ __put_user (s.c_lflag, &p->c_lflag) ||
+ __copy_from_user (s.c_cc, p->c_cc, 16))
return -EFAULT;
s.c_cflag = solaris_to_linux_cflag(s.c_cflag);
set_fs(KERNEL_DS);
case 109: /* SI_SOCKPARAMS */
{
struct solaris_si_sockparams si;
- if (copy_from_user (&si, (struct solaris_si_sockparams *) A(arg), sizeof(si)))
+ if (copy_from_user (&si, A(arg), sizeof(si)))
return (EFAULT << 8) | TSYSERR;
/* Should we modify socket ino->socket_i.ops and type? */
case 110: /* SI_GETUDATA */
{
int etsdusize, servtype;
+ struct solaris_si_udata __user *p = A(arg);
switch (SOCKET_I(ino)->type) {
case SOCK_STREAM:
etsdusize = 1;
servtype = 3;
break;
}
- if (put_user(16384, &((struct solaris_si_udata *)A(arg))->tidusize) ||
- __put_user(sizeof(struct sockaddr), &((struct solaris_si_udata *)A(arg))->addrsize) ||
- __put_user(-1, &((struct solaris_si_udata *)A(arg))->optsize) ||
- __put_user(etsdusize, &((struct solaris_si_udata *)A(arg))->etsdusize) ||
- __put_user(servtype, &((struct solaris_si_udata *)A(arg))->servtype) ||
- __put_user(0, &((struct solaris_si_udata *)A(arg))->so_state) ||
- __put_user(0, &((struct solaris_si_udata *)A(arg))->so_options) ||
- __put_user(16384, &((struct solaris_si_udata *)A(arg))->tsdusize) ||
- __put_user(SOCKET_I(ino)->ops->family, &((struct solaris_si_udata *)A(arg))->sockparams.sp_family) ||
- __put_user(SOCKET_I(ino)->type, &((struct solaris_si_udata *)A(arg))->sockparams.sp_type) ||
- __put_user(SOCKET_I(ino)->ops->family, &((struct solaris_si_udata *)A(arg))->sockparams.sp_protocol))
+ if (put_user(16384, &p->tidusize) ||
+ __put_user(sizeof(struct sockaddr), &p->addrsize) ||
+ __put_user(-1, &p->optsize) ||
+ __put_user(etsdusize, &p->etsdusize) ||
+ __put_user(servtype, &p->servtype) ||
+ __put_user(0, &p->so_state) ||
+ __put_user(0, &p->so_options) ||
+ __put_user(16384, &p->tsdusize) ||
+ __put_user(SOCKET_I(ino)->ops->family, &p->sockparams.sp_family) ||
+ __put_user(SOCKET_I(ino)->type, &p->sockparams.sp_type) ||
+ __put_user(SOCKET_I(ino)->ops->family, &p->sockparams.sp_protocol))
return (EFAULT << 8) | TSYSERR;
return 0;
}
case 101: /* O_SI_GETUDATA */
{
int etsdusize, servtype;
+ struct solaris_o_si_udata __user *p = A(arg);
switch (SOCKET_I(ino)->type) {
case SOCK_STREAM:
etsdusize = 1;
servtype = 3;
break;
}
- if (put_user(16384, &((struct solaris_o_si_udata *)A(arg))->tidusize) ||
- __put_user(sizeof(struct sockaddr), &((struct solaris_o_si_udata *)A(arg))->addrsize) ||
- __put_user(-1, &((struct solaris_o_si_udata *)A(arg))->optsize) ||
- __put_user(etsdusize, &((struct solaris_o_si_udata *)A(arg))->etsdusize) ||
- __put_user(servtype, &((struct solaris_o_si_udata *)A(arg))->servtype) ||
- __put_user(0, &((struct solaris_o_si_udata *)A(arg))->so_state) ||
- __put_user(0, &((struct solaris_o_si_udata *)A(arg))->so_options) ||
- __put_user(16384, &((struct solaris_o_si_udata *)A(arg))->tsdusize))
+ if (put_user(16384, &p->tidusize) ||
+ __put_user(sizeof(struct sockaddr), &p->addrsize) ||
+ __put_user(-1, &p->optsize) ||
+ __put_user(etsdusize, &p->etsdusize) ||
+ __put_user(servtype, &p->servtype) ||
+ __put_user(0, &p->so_state) ||
+ __put_user(0, &p->so_options) ||
+ __put_user(16384, &p->tsdusize))
return (EFAULT << 8) | TSYSERR;
return 0;
}
}
static inline int solaris_timod(unsigned int fd, unsigned int cmd, u32 arg,
- int len, int *len_p)
+ int len, int __user *len_p)
{
int ret;
int i;
u32 prim;
SOLD("TI_OPMGMT entry");
- ret = timod_putmsg(fd, (char *)A(arg), len, NULL, -1, 0);
+ ret = timod_putmsg(fd, A(arg), len, NULL, -1, 0);
SOLD("timod_putmsg() returned");
if (ret)
return (-ret << 8) | TSYSERR;
i = MSG_HIPRI;
SOLD("calling timod_getmsg()");
- ret = timod_getmsg(fd, (char *)A(arg), len, len_p, NULL, -1, NULL, &i);
+ ret = timod_getmsg(fd, A(arg), len, len_p, NULL, -1, NULL, &i);
SOLD("timod_getmsg() returned");
if (ret)
return (-ret << 8) | TSYSERR;
SOLD("ret ok");
- if (get_user(prim, (u32 *)A(arg)))
+ if (get_user(prim, (u32 __user *)A(arg)))
return (EFAULT << 8) | TSYSERR;
SOLD("got prim");
if (prim == T_ERROR_ACK) {
u32 tmp, tmp2;
SOLD("prim is T_ERROR_ACK");
- if (get_user(tmp, (u32 *)A(arg)+3) ||
- get_user(tmp2, (u32 *)A(arg)+2))
+ if (get_user(tmp, (u32 __user *)A(arg)+3) ||
+ get_user(tmp2, (u32 __user *)A(arg)+2))
return (EFAULT << 8) | TSYSERR;
return (tmp2 << 8) | tmp;
}
int i;
u32 prim;
SOLD("TI_BIND entry");
- ret = timod_putmsg(fd, (char *)A(arg), len, NULL, -1, 0);
+ ret = timod_putmsg(fd, A(arg), len, NULL, -1, 0);
SOLD("timod_putmsg() returned");
if (ret)
return (-ret << 8) | TSYSERR;
len = 1024; /* Solaris allows arbitrary return size */
i = MSG_HIPRI;
SOLD("calling timod_getmsg()");
- ret = timod_getmsg(fd, (char *)A(arg), len, len_p, NULL, -1, NULL, &i);
+ ret = timod_getmsg(fd, A(arg), len, len_p, NULL, -1, NULL, &i);
SOLD("timod_getmsg() returned");
if (ret)
return (-ret << 8) | TSYSERR;
SOLD("ret ok");
- if (get_user(prim, (u32 *)A(arg)))
+ if (get_user(prim, (u32 __user *)A(arg)))
return (EFAULT << 8) | TSYSERR;
SOLD("got prim");
if (prim == T_ERROR_ACK) {
u32 tmp, tmp2;
SOLD("prim is T_ERROR_ACK");
- if (get_user(tmp, (u32 *)A(arg)+3) ||
- get_user(tmp2, (u32 *)A(arg)+2))
+ if (get_user(tmp, (u32 __user *)A(arg)+3) ||
+ get_user(tmp2, (u32 __user *)A(arg)+2))
return (EFAULT << 8) | TSYSERR;
return (tmp2 << 8) | tmp;
}
SOLD("OK_ACK requested");
i = MSG_HIPRI;
SOLD("calling timod_getmsg()");
- ret = timod_getmsg(fd, (char *)A(arg), len, len_p, NULL, -1, NULL, &i);
+ ret = timod_getmsg(fd, A(arg), len, len_p, NULL, -1, NULL, &i);
SOLD("timod_getmsg() returned");
if (ret)
return (-ret << 8) | TSYSERR;
return -ENOSYS;
case 2: /* I_PUSH */
{
- p = getname ((char *)A(arg));
+ p = getname (A(arg));
if (IS_ERR (p))
return PTR_ERR(p);
ret = -EINVAL;
const char *p;
if (sock->modcount <= 0) return -EINVAL;
p = module_table[(unsigned)sock->module[sock->modcount]].name;
- if (copy_to_user ((char *)A(arg), p, strlen(p)))
+ if (copy_to_user (A(arg), p, strlen(p)))
return -EFAULT;
return 0;
}
case 5: /* I_FLUSH */
return 0;
case 8: /* I_STR */
- if (copy_from_user(&si, (struct strioctl *)A(arg), sizeof(struct strioctl)))
+ if (copy_from_user(&si, A(arg), sizeof(struct strioctl)))
return -EFAULT;
/* We ignore what module is actually at the top of stack. */
switch ((si.cmd >> 8) & 0xff) {
return solaris_sockmod(fd, si.cmd, si.data);
case 'T':
return solaris_timod(fd, si.cmd, si.data, si.len,
- &((struct strioctl*)A(arg))->len);
+ &((struct strioctl __user *)A(arg))->len);
default:
return solaris_ioctl(fd, si.cmd, si.data);
}
case 11: /* I_FIND */
{
int i;
- p = getname ((char *)A(arg));
+ p = getname (A(arg));
if (IS_ERR (p))
return PTR_ERR(p);
ret = 0;
return 0; /* We don't support them */
case 1: /* SIOCGHIWAT */
case 3: /* SIOCGLOWAT */
- if (put_user (0, (u32 *)A(arg)))
+ if (put_user (0, (u32 __user *)A(arg)))
return -EFAULT;
return 0; /* Lie */
case 7: /* SIOCATMARK */
args);
set_fs(old_fs);
if (ret >= 0) {
- if (copy_to_user((char *)A(arg), &uaddr, uaddr_len))
+ if (copy_to_user(A(arg), &uaddr, uaddr_len))
return -EFAULT;
}
return ret;
for (d = dev_base; d; d = d->next) i++;
read_unlock_bh(&dev_base_lock);
- if (put_user (i, (int *)A(arg)))
+ if (put_user (i, (int __user *)A(arg)))
return -EFAULT;
return 0;
}
asmlinkage long solaris_shmsys(int cmd, u32 arg1, u32 arg2, u32 arg3)
{
- int (*sys_ipc)(unsigned,int,int,unsigned long,void *,long) =
- (int (*)(unsigned,int,int,unsigned long,void *,long))SYS(ipc);
+ int (*sys_ipc)(unsigned,int,int,unsigned long,void __user *,long) =
+ (int (*)(unsigned,int,int,unsigned long,void __user *,long))SYS(ipc);
mm_segment_t old_fs;
unsigned long raddr;
int ret;
case 0: /* shmat */
old_fs = get_fs();
set_fs(KERNEL_DS);
- ret = sys_ipc(SHMAT, arg1, arg3 & ~0x4000, (unsigned long)&raddr, (void *)A(arg2), 0);
+ ret = sys_ipc(SHMAT, arg1, arg3 & ~0x4000, (unsigned long)&raddr, A(arg2), 0);
set_fs(old_fs);
if (ret >= 0) return (u32)raddr;
else return ret;
case 11: /* IPC_SET */
{
struct shmid_ds s;
+ struct solaris_shmid_ds __user *p = A(arg3);
- if (get_user (s.shm_perm.uid, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.uid)) ||
- __get_user (s.shm_perm.gid, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.gid)) ||
- __get_user (s.shm_perm.mode, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.mode)))
+ if (get_user (s.shm_perm.uid, &p->shm_perm.uid) ||
+ __get_user (s.shm_perm.gid, &p->shm_perm.gid) ||
+ __get_user (s.shm_perm.mode, &p->shm_perm.mode))
return -EFAULT;
old_fs = get_fs();
set_fs(KERNEL_DS);
case 12: /* IPC_STAT */
{
struct shmid_ds s;
+ struct solaris_shmid_ds __user *p = A(arg3);
old_fs = get_fs();
set_fs(KERNEL_DS);
ret = sys_ipc(SHMCTL, arg1, IPC_SET, 0, &s, 0);
set_fs(old_fs);
- if (get_user (s.shm_perm.uid, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.uid)) ||
- __get_user (s.shm_perm.gid, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.gid)) ||
- __get_user (s.shm_perm.cuid, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.cuid)) ||
- __get_user (s.shm_perm.cgid, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.cgid)) ||
- __get_user (s.shm_perm.mode, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.mode)) ||
- __get_user (s.shm_perm.seq, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.seq)) ||
- __get_user (s.shm_perm.key, &(((struct solaris_shmid_ds *)A(arg3))->shm_perm.key)) ||
- __get_user (s.shm_segsz, &(((struct solaris_shmid_ds *)A(arg3))->shm_segsz)) ||
- __get_user (s.shm_lpid, &(((struct solaris_shmid_ds *)A(arg3))->shm_lpid)) ||
- __get_user (s.shm_cpid, &(((struct solaris_shmid_ds *)A(arg3))->shm_cpid)) ||
- __get_user (s.shm_nattch, &(((struct solaris_shmid_ds *)A(arg3))->shm_nattch)) ||
- __get_user (s.shm_atime, &(((struct solaris_shmid_ds *)A(arg3))->shm_atime)) ||
- __get_user (s.shm_dtime, &(((struct solaris_shmid_ds *)A(arg3))->shm_dtime)) ||
- __get_user (s.shm_ctime, &(((struct solaris_shmid_ds *)A(arg3))->shm_ctime)))
+ if (put_user (s.shm_perm.uid, &(p->shm_perm.uid)) ||
+ __put_user (s.shm_perm.gid, &(p->shm_perm.gid)) ||
+ __put_user (s.shm_perm.cuid, &(p->shm_perm.cuid)) ||
+ __put_user (s.shm_perm.cgid, &(p->shm_perm.cgid)) ||
+ __put_user (s.shm_perm.mode, &(p->shm_perm.mode)) ||
+ __put_user (s.shm_perm.seq, &(p->shm_perm.seq)) ||
+ __put_user (s.shm_perm.key, &(p->shm_perm.key)) ||
+ __put_user (s.shm_segsz, &(p->shm_segsz)) ||
+ __put_user (s.shm_lpid, &(p->shm_lpid)) ||
+ __put_user (s.shm_cpid, &(p->shm_cpid)) ||
+ __put_user (s.shm_nattch, &(p->shm_nattch)) ||
+ __put_user (s.shm_atime, &(p->shm_atime)) ||
+ __put_user (s.shm_dtime, &(p->shm_dtime)) ||
+ __put_user (s.shm_ctime, &(p->shm_ctime)))
return -EFAULT;
return ret;
}
default: return -EINVAL;
}
case 2: /* shmdt */
- return sys_ipc(SHMDT, 0, 0, 0, (void *)A(arg1), 0);
+ return sys_ipc(SHMDT, 0, 0, 0, A(arg1), 0);
case 3: /* shmget */
return sys_ipc(SHMGET, arg1, arg2, arg3, NULL, 0);
}
u32 offlo;
if (regs->u_regs[UREG_G1]) {
- if (get_user (offlo, (u32 *)(long)((u32)regs->u_regs[UREG_I6] + 0x5c)))
+ if (get_user (offlo, (u32 __user *)(long)((u32)regs->u_regs[UREG_I6] + 0x5c)))
return -EFAULT;
} else {
- if (get_user (offlo, (u32 *)(long)((u32)regs->u_regs[UREG_I6] + 0x60)))
+ if (get_user (offlo, (u32 __user *)(long)((u32)regs->u_regs[UREG_I6] + 0x60)))
return -EFAULT;
}
return do_solaris_mmap((u32)regs->u_regs[UREG_I0], len, prot, flags, fd, (((u64)offhi)<<32)|offlo);
for (p=from,i=0; *p && *p != '.' && --len; p++,i++); \
else \
i = len - 1; \
- if (__put_user('\0', (char *)(to+i))) \
+ if (__put_user('\0', (char __user *)((to)+i))) \
return -EFAULT; \
}
asmlinkage int solaris_utssys(u32 buf, u32 flags, int which, u32 buf2)
{
+ struct sol_uname __user *v = A(buf);
switch (which) {
case 0: /* old uname */
/* Let's cheat */
- set_utsfield(((struct sol_uname *)A(buf))->sysname,
- "SunOS", 1, 0);
+ set_utsfield(v->sysname, "SunOS", 1, 0);
down_read(&uts_sem);
- set_utsfield(((struct sol_uname *)A(buf))->nodename,
- system_utsname.nodename, 1, 1);
+ set_utsfield(v->nodename, system_utsname.nodename, 1, 1);
up_read(&uts_sem);
- set_utsfield(((struct sol_uname *)A(buf))->release,
- "2.6", 0, 0);
- set_utsfield(((struct sol_uname *)A(buf))->version,
- "Generic", 0, 0);
- set_utsfield(((struct sol_uname *)A(buf))->machine,
- machine(), 0, 0);
+ set_utsfield(v->release, "2.6", 0, 0);
+ set_utsfield(v->version, "Generic", 0, 0);
+ set_utsfield(v->machine, machine(), 0, 0);
return 0;
case 2: /* ustat */
return -ENOSYS;
asmlinkage int solaris_utsname(u32 buf)
{
+ struct sol_utsname __user *v = A(buf);
/* Why should we not lie a bit? */
down_read(&uts_sem);
- set_utsfield(((struct sol_utsname *)A(buf))->sysname,
- "SunOS", 0, 0);
- set_utsfield(((struct sol_utsname *)A(buf))->nodename,
- system_utsname.nodename, 1, 1);
- set_utsfield(((struct sol_utsname *)A(buf))->release,
- "5.6", 0, 0);
- set_utsfield(((struct sol_utsname *)A(buf))->version,
- "Generic", 0, 0);
- set_utsfield(((struct sol_utsname *)A(buf))->machine,
- machine(), 0, 0);
+ set_utsfield(v->sysname, "SunOS", 0, 0);
+ set_utsfield(v->nodename, system_utsname.nodename, 1, 1);
+ set_utsfield(v->release, "5.6", 0, 0);
+ set_utsfield(v->version, "Generic", 0, 0);
+ set_utsfield(v->machine, machine(), 0, 0);
up_read(&uts_sem);
return 0;
}
}
len = strlen(r) + 1;
if (count < len) {
- if (copy_to_user((char *)A(buf), r, count - 1) ||
- __put_user(0, (char *)A(buf) + count - 1))
+ if (copy_to_user(A(buf), r, count - 1) ||
+ __put_user(0, (char __user *)A(buf) + count - 1))
return -EFAULT;
} else {
- if (copy_to_user((char *)A(buf), r, len))
+ if (copy_to_user(A(buf), r, len))
return -EFAULT;
}
return len;
u32 rlim_max;
};
-asmlinkage int solaris_getrlimit(unsigned int resource, struct rlimit32 *rlim)
+asmlinkage int solaris_getrlimit(unsigned int resource, struct rlimit32 __user *rlim)
{
struct rlimit r;
int ret;
return ret;
}
-asmlinkage int solaris_setrlimit(unsigned int resource, struct rlimit32 *rlim)
+asmlinkage int solaris_setrlimit(unsigned int resource, struct rlimit32 __user *rlim)
{
struct rlimit r, rold;
int ret;
mm_segment_t old_fs = get_fs ();
- int (*sys_getrlimit)(unsigned int, struct rlimit *) =
- (int (*)(unsigned int, struct rlimit *))SYS(getrlimit);
- int (*sys_setrlimit)(unsigned int, struct rlimit *) =
- (int (*)(unsigned int, struct rlimit *))SYS(setrlimit);
+ int (*sys_getrlimit)(unsigned int, struct rlimit __user *) =
+ (int (*)(unsigned int, struct rlimit __user *))SYS(getrlimit);
+ int (*sys_setrlimit)(unsigned int, struct rlimit __user *) =
+ (int (*)(unsigned int, struct rlimit __user *))SYS(setrlimit);
if (resource > RLIMIT_SOL_VMEM)
return -EINVAL;
return ret;
}
-asmlinkage int solaris_getrlimit64(unsigned int resource, struct rlimit *rlim)
+asmlinkage int solaris_getrlimit64(unsigned int resource, struct rlimit __user *rlim)
{
struct rlimit r;
int ret;
mm_segment_t old_fs = get_fs ();
- int (*sys_getrlimit)(unsigned int, struct rlimit *) =
- (int (*)(unsigned int, struct rlimit *))SYS(getrlimit);
+ int (*sys_getrlimit)(unsigned int, struct rlimit __user *) =
+ (int (*)(unsigned int, struct rlimit __user *))SYS(getrlimit);
if (resource > RLIMIT_SOL_VMEM)
return -EINVAL;
return ret;
}
-asmlinkage int solaris_setrlimit64(unsigned int resource, struct rlimit *rlim)
+asmlinkage int solaris_setrlimit64(unsigned int resource, struct rlimit __user *rlim)
{
struct rlimit r, rold;
int ret;
mm_segment_t old_fs = get_fs ();
- int (*sys_getrlimit)(unsigned int, struct rlimit *) =
- (int (*)(unsigned int, struct rlimit *))SYS(getrlimit);
- int (*sys_setrlimit)(unsigned int, struct rlimit *) =
- (int (*)(unsigned int, struct rlimit *))SYS(setrlimit);
+ int (*sys_getrlimit)(unsigned int, struct rlimit __user *) =
+ (int (*)(unsigned int, struct rlimit __user *))SYS(getrlimit);
+ int (*sys_setrlimit)(unsigned int, struct rlimit __user *) =
+ (int (*)(unsigned int, struct rlimit __user *))SYS(setrlimit);
if (resource > RLIMIT_SOL_VMEM)
return -EINVAL;
s32 stbcnt;
};
-asmlinkage int solaris_ntp_gettime(struct sol_ntptimeval *ntp)
+asmlinkage int solaris_ntp_gettime(struct sol_ntptimeval __user *ntp)
{
- int (*sys_adjtimex)(struct timex *) =
- (int (*)(struct timex *))SYS(adjtimex);
+ int (*sys_adjtimex)(struct timex __user *) =
+ (int (*)(struct timex __user *))SYS(adjtimex);
struct timex t;
int ret;
mm_segment_t old_fs = get_fs();
return ret;
}
-asmlinkage int solaris_ntp_adjtime(struct sol_timex *txp)
+asmlinkage int solaris_ntp_adjtime(struct sol_timex __user *txp)
{
- int (*sys_adjtimex)(struct timex *) =
- (int (*)(struct timex *))SYS(adjtimex);
+ int (*sys_adjtimex)(struct timex __user *) =
+ (int (*)(struct timex __user *))SYS(adjtimex);
struct timex t;
int ret, err;
mm_segment_t old_fs = get_fs();
struct sigaction sa, old;
int ret;
mm_segment_t old_fs = get_fs();
- int (*sys_sigaction)(int,struct sigaction *,struct sigaction *) =
- (int (*)(int,struct sigaction *,struct sigaction *))SYS(sigaction);
+ int (*sys_sigaction)(int,struct sigaction __user *,struct sigaction __user *) =
+ (int (*)(int,struct sigaction __user *,struct sigaction __user *))SYS(sigaction);
sigemptyset(&sa.sa_mask);
sa.sa_restorer = NULL;
sa.sa_flags = 0;
if (one_shot) sa.sa_flags = SA_ONESHOT | SA_NOMASK;
set_fs (KERNEL_DS);
- ret = sys_sigaction(sig, &sa, &old);
+ ret = sys_sigaction(sig, (void __user *)&sa, (void __user *)&old);
set_fs (old_fs);
if (ret < 0) return ret;
- return (u32)(long)old.sa_handler;
+ return (u32)(unsigned long)old.sa_handler;
}
static inline long solaris_signal(int sig, u32 arg)
static inline long solaris_sigignore(int sig)
{
- return sig_handler (sig, (u32)SIG_IGN, 0);
+ return sig_handler(sig, (u32)(unsigned long)SIG_IGN, 0);
}
static inline long solaris_sigpause(int sig)
sigset_t in_s, *ins, out_s, *outs;
mm_segment_t old_fs = get_fs();
int ret;
- int (*sys_sigprocmask)(int,sigset_t *,sigset_t *) =
- (int (*)(int,sigset_t *,sigset_t *))SYS(sigprocmask);
+ int (*sys_sigprocmask)(int,sigset_t __user *,sigset_t __user *) =
+ (int (*)(int,sigset_t __user *,sigset_t __user *))SYS(sigprocmask);
ins = NULL; outs = NULL;
if (in) {
u32 tmp[2];
- if (copy_from_user (tmp, (sol_sigset_t *)A(in), 2*sizeof(u32)))
+ if (copy_from_user (tmp, (void __user *)A(in), 2*sizeof(u32)))
return -EFAULT;
ins = &in_s;
if (mapin (tmp, ins)) return -EINVAL;
}
if (out) outs = &out_s;
set_fs (KERNEL_DS);
- ret = sys_sigprocmask((how == 3) ? SIG_SETMASK : how, ins, outs);
+ ret = sys_sigprocmask((how == 3) ? SIG_SETMASK : how,
+ (void __user *)ins, (void __user *)outs);
set_fs (old_fs);
if (ret) return ret;
if (out) {
tmp[2] = 0; tmp[3] = 0;
if (mapout (outs, tmp)) return -EINVAL;
- if (copy_to_user((sol_sigset_t *)A(out), tmp, 4*sizeof(u32)))
+ if (copy_to_user((void __user *)A(out), tmp, 4*sizeof(u32)))
return -EFAULT;
}
return 0;
sigset_t s;
u32 tmp[2];
- if (copy_from_user (tmp, (sol_sigset_t *)A(mask), 2*sizeof(u32)))
+ if (copy_from_user (tmp, (sol_sigset_t __user *)A(mask), 2*sizeof(u32)))
return -EFAULT;
if (mapin (tmp, &s)) return -EINVAL;
return (long)s.sig[0];
struct sigaction s, s2;
int ret;
mm_segment_t old_fs = get_fs();
- int (*sys_sigaction)(int,struct sigaction *,struct sigaction *) =
- (int (*)(int,struct sigaction *,struct sigaction *))SYS(sigaction);
+ struct sol_sigaction __user *p = (void __user *)A(old);
+ int (*sys_sigaction)(int,struct sigaction __user *,struct sigaction __user *) =
+ (int (*)(int,struct sigaction __user *,struct sigaction __user *))SYS(sigaction);
sig = mapsig(sig);
if (sig < 0) {
/* We cheat a little bit for Solaris only signals */
- if (old && clear_user((struct sol_sigaction *)A(old), sizeof(struct sol_sigaction)))
+ if (old && clear_user(p, sizeof(struct sol_sigaction)))
return -EFAULT;
return 0;
}
if (act) {
- if (get_user (tmp, &((struct sol_sigaction *)A(act))->sa_flags))
+ if (get_user (tmp, &p->sa_flags))
return -EFAULT;
s.sa_flags = 0;
if (tmp & SOLARIS_SA_ONSTACK) s.sa_flags |= SA_STACK;
if (tmp & SOLARIS_SA_NODEFER) s.sa_flags |= SA_NOMASK;
if (tmp & SOLARIS_SA_RESETHAND) s.sa_flags |= SA_ONESHOT;
if (tmp & SOLARIS_SA_NOCLDSTOP) s.sa_flags |= SA_NOCLDSTOP;
- if (get_user (tmp, &((struct sol_sigaction *)A(act))->sa_handler) ||
- copy_from_user (tmp2, &((struct sol_sigaction *)A(act))->sa_mask, 2*sizeof(u32)))
+ if (get_user (tmp, &p->sa_handler) ||
+ copy_from_user (tmp2, &p->sa_mask, 2*sizeof(u32)))
return -EFAULT;
s.sa_handler = (__sighandler_t)A(tmp);
if (mapin (tmp2, &s.sa_mask)) return -EINVAL;
- s.sa_restorer = 0;
+ s.sa_restorer = NULL;
}
set_fs(KERNEL_DS);
- ret = sys_sigaction(sig, act ? &s : NULL, old ? &s2 : NULL);
+ ret = sys_sigaction(sig, act ? (void __user *)&s : NULL,
+ old ? (void __user *)&s2 : NULL);
set_fs(old_fs);
if (ret) return ret;
if (old) {
if (s2.sa_flags & SA_NOMASK) tmp |= SOLARIS_SA_NODEFER;
if (s2.sa_flags & SA_ONESHOT) tmp |= SOLARIS_SA_RESETHAND;
if (s2.sa_flags & SA_NOCLDSTOP) tmp |= SOLARIS_SA_NOCLDSTOP;
- if (put_user (tmp, &((struct sol_sigaction *)A(old))->sa_flags) ||
- __put_user ((u32)(long)s2.sa_handler, &((struct sol_sigaction *)A(old))->sa_handler) ||
- copy_to_user (&((struct sol_sigaction *)A(old))->sa_mask, tmp2, 4*sizeof(u32)))
+ if (put_user (tmp, &p->sa_flags) ||
+ __put_user ((u32)(unsigned long)s2.sa_handler, &p->sa_handler) ||
+ copy_to_user (&p->sa_mask, tmp2, 4*sizeof(u32)))
return -EFAULT;
}
return 0;
}
if (mapout (&s, tmp)) return -EINVAL;
tmp[2] = 0; tmp[3] = 0;
- if (copy_to_user ((u32 *)A(set), tmp, sizeof(tmp)))
+ if (copy_to_user ((u32 __user *)A(set), tmp, sizeof(tmp)))
return -EFAULT;
return 0;
}
asmlinkage int solaris_wait(u32 stat_loc)
{
- int (*sys_wait4)(pid_t,unsigned int *, int, struct rusage *) =
- (int (*)(pid_t,unsigned int *, int, struct rusage *))SYS(wait4);
+ unsigned __user *p = (unsigned __user *)A(stat_loc);
+ int (*sys_wait4)(pid_t,unsigned __user *, int, struct rusage __user *) =
+ (int (*)(pid_t,unsigned __user *, int, struct rusage __user *))SYS(wait4);
int ret, status;
- ret = sys_wait4(-1, (unsigned int *)A(stat_loc), WUNTRACED, NULL);
+ ret = sys_wait4(-1, p, WUNTRACED, NULL);
if (ret >= 0 && stat_loc) {
- if (get_user (status, (unsigned int *)A(stat_loc)))
+ if (get_user (status, p))
return -EFAULT;
if (((status - 1) & 0xffff) < 0xff)
status = linux_to_solaris_signals[status & 0x7f] & 0x7f;
else if ((status & 0xff) == 0x7f)
status = (linux_to_solaris_signals[(status >> 8) & 0xff] << 8) | 0x7f;
- if (__put_user (status, (unsigned int *)A(stat_loc)))
+ if (__put_user (status, p))
return -EFAULT;
}
return ret;
asmlinkage int solaris_waitid(int idtype, s32 pid, u32 info, int options)
{
- int (*sys_wait4)(pid_t,unsigned int *, int, struct rusage *) =
- (int (*)(pid_t,unsigned int *, int, struct rusage *))SYS(wait4);
+ int (*sys_wait4)(pid_t,unsigned __user *, int, struct rusage __user *) =
+ (int (*)(pid_t,unsigned __user *, int, struct rusage __user *))SYS(wait4);
int opts, status, ret;
switch (idtype) {
if (options & SOLARIS_WUNTRACED) opts |= WUNTRACED;
if (options & SOLARIS_WNOHANG) opts |= WNOHANG;
current->state = TASK_RUNNING;
- ret = sys_wait4(pid, (unsigned int *)A(info), opts, NULL);
+ ret = sys_wait4(pid, (unsigned int __user *)A(info), opts, NULL);
if (ret < 0) return ret;
if (info) {
- struct sol_siginfo *s = (struct sol_siginfo *)A(info);
+ struct sol_siginfo __user *s = (void __user *)A(info);
- if (get_user (status, (unsigned int *)A(info)))
+ if (get_user (status, (unsigned int __user *)A(info)))
return -EFAULT;
if (__put_user (SOLARIS_SIGCLD, &s->si_signo) ||
return sunos_getsockopt(fd, level, optname, optval, optlen);
}
-asmlinkage int solaris_connect(int fd, struct sockaddr *addr, int addrlen)
+asmlinkage int solaris_connect(int fd, struct sockaddr __user *addr, int addrlen)
{
- int (*sys_connect)(int, struct sockaddr *, int) =
- (int (*)(int, struct sockaddr *, int))SYS(connect);
+ int (*sys_connect)(int, struct sockaddr __user *, int) =
+ (int (*)(int, struct sockaddr __user *, int))SYS(connect);
return sys_connect(fd, addr, addrlen);
}
-asmlinkage int solaris_accept(int fd, struct sockaddr *addr, int *addrlen)
+asmlinkage int solaris_accept(int fd, struct sockaddr __user *addr, int __user *addrlen)
{
- int (*sys_accept)(int, struct sockaddr *, int *) =
- (int (*)(int, struct sockaddr *, int *))SYS(accept);
+ int (*sys_accept)(int, struct sockaddr __user *, int __user *) =
+ (int (*)(int, struct sockaddr __user *, int __user *))SYS(accept);
return sys_accept(fd, addr, addrlen);
}
return fl;
}
-asmlinkage int solaris_recvfrom(int s, char *buf, int len, int flags, u32 from, u32 fromlen)
+asmlinkage int solaris_recvfrom(int s, char __user *buf, int len, int flags, u32 from, u32 fromlen)
{
- int (*sys_recvfrom)(int, void *, size_t, unsigned, struct sockaddr *, int *) =
- (int (*)(int, void *, size_t, unsigned, struct sockaddr *, int *))SYS(recvfrom);
+ int (*sys_recvfrom)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *) =
+ (int (*)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *))SYS(recvfrom);
- return sys_recvfrom(s, buf, len, solaris_to_linux_msgflags(flags), (struct sockaddr *)A(from), (int *)A(fromlen));
+ return sys_recvfrom(s, buf, len, solaris_to_linux_msgflags(flags), A(from), A(fromlen));
}
-asmlinkage int solaris_recv(int s, char *buf, int len, int flags)
+asmlinkage int solaris_recv(int s, char __user *buf, int len, int flags)
{
- int (*sys_recvfrom)(int, void *, size_t, unsigned, struct sockaddr *, int *) =
- (int (*)(int, void *, size_t, unsigned, struct sockaddr *, int *))SYS(recvfrom);
+ int (*sys_recvfrom)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *) =
+ (int (*)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *))SYS(recvfrom);
return sys_recvfrom(s, buf, len, solaris_to_linux_msgflags(flags), NULL, NULL);
}
-asmlinkage int solaris_sendto(int s, char *buf, int len, int flags, u32 to, u32 tolen)
+asmlinkage int solaris_sendto(int s, char __user *buf, int len, int flags, u32 to, u32 tolen)
{
- int (*sys_sendto)(int, void *, size_t, unsigned, struct sockaddr *, int *) =
- (int (*)(int, void *, size_t, unsigned, struct sockaddr *, int *))SYS(sendto);
+ int (*sys_sendto)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *) =
+ (int (*)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *))SYS(sendto);
- return sys_sendto(s, buf, len, solaris_to_linux_msgflags(flags), (struct sockaddr *)A(to), (int *)A(tolen));
+ return sys_sendto(s, buf, len, solaris_to_linux_msgflags(flags), A(to), A(tolen));
}
asmlinkage int solaris_send(int s, char *buf, int len, int flags)
};
static inline int msghdr_from_user32_to_kern(struct msghdr *kmsg,
- struct sol_nmsghdr *umsg)
+ struct sol_nmsghdr __user *umsg)
{
u32 tmp1, tmp2, tmp3;
int err;
if (err)
return -EFAULT;
- kmsg->msg_name = (void *)A(tmp1);
- kmsg->msg_iov = (struct iovec *)A(tmp2);
- kmsg->msg_control = (void *)A(tmp3);
+ kmsg->msg_name = A(tmp1);
+ kmsg->msg_iov = A(tmp2);
+ kmsg->msg_control = A(tmp3);
err = get_user(kmsg->msg_namelen, &umsg->msg_namelen);
err |= get_user(kmsg->msg_controllen, &umsg->msg_controllen);
return err;
}
-asmlinkage int solaris_sendmsg(int fd, struct sol_nmsghdr *user_msg, unsigned user_flags)
+asmlinkage int solaris_sendmsg(int fd, struct sol_nmsghdr __user *user_msg, unsigned user_flags)
{
struct socket *sock;
char address[MAX_SOCK_ADDR];
total_len = err;
if(kern_msg.msg_controllen) {
- struct sol_cmsghdr *ucmsg = (struct sol_cmsghdr *)kern_msg.msg_control;
+ struct sol_cmsghdr __user *ucmsg = kern_msg.msg_control;
unsigned long *kcmsg;
compat_size_t cmlen;
return err;
}
-asmlinkage int solaris_recvmsg(int fd, struct sol_nmsghdr *user_msg, unsigned int user_flags)
+asmlinkage int solaris_recvmsg(int fd, struct sol_nmsghdr __user *user_msg, unsigned int user_flags)
{
struct iovec iovstack[UIO_FASTIOV];
struct msghdr kern_msg;
char addr[MAX_SOCK_ADDR];
struct socket *sock;
struct iovec *iov = iovstack;
- struct sockaddr *uaddr;
- int *uaddr_len;
+ struct sockaddr __user *uaddr;
+ int __user *uaddr_len;
unsigned long cmsg_ptr;
int err, total_len, len = 0;
SOLD("done");
}
-static int timod_optmgmt(unsigned int fd, int flag, char *opt_buf, int opt_len, int do_ret)
+static int timod_optmgmt(unsigned int fd, int flag, char __user *opt_buf, int opt_len, int do_ret)
{
int error, failed;
int ret_space, ret_len;
return 0;
}
-int timod_putmsg(unsigned int fd, char *ctl_buf, int ctl_len,
- char *data_buf, int data_len, int flags)
+int timod_putmsg(unsigned int fd, char __user *ctl_buf, int ctl_len,
+ char __user *data_buf, int data_len, int flags)
{
int ret, error, terror;
char *buf;
struct sol_socket_struct *sock;
mm_segment_t old_fs = get_fs();
long args[6];
- int (*sys_socketcall)(int, unsigned long *) =
- (int (*)(int, unsigned long *))SYS(socketcall);
- int (*sys_sendto)(int, void *, size_t, unsigned, struct sockaddr *, int) =
- (int (*)(int, void *, size_t, unsigned, struct sockaddr *, int))SYS(sendto);
+ int (*sys_socketcall)(int, unsigned long __user *) =
+ (int (*)(int, unsigned long __user *))SYS(socketcall);
+ int (*sys_sendto)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int) =
+ (int (*)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int))SYS(sendto);
filp = current->files->fd[fd];
ino = filp->f_dentry->d_inode;
sock = (struct sol_socket_struct *)filp->private_data;
SOLD("entry");
- if (get_user(ret, (int *)A(ctl_buf)))
+ if (get_user(ret, (int __user *)A(ctl_buf)))
return -EFAULT;
switch (ret) {
case T_BIND_REQ:
printk("\n");
}
#endif
- err = sys_sendto(fd, data_buf, data_len, 0, req.DEST_length > 0 ? (struct sockaddr*)(ctl_buf+req.DEST_offset) : NULL, req.DEST_length);
+ err = sys_sendto(fd, data_buf, data_len, 0, req.DEST_length > 0 ? (struct sockaddr __user *)(ctl_buf+req.DEST_offset) : NULL, req.DEST_length);
if (err == data_len)
return 0;
if(err >= 0) {
return -EINVAL;
}
-int timod_getmsg(unsigned int fd, char *ctl_buf, int ctl_maxlen, s32 *ctl_len,
- char *data_buf, int data_maxlen, s32 *data_len, int *flags_p)
+int timod_getmsg(unsigned int fd, char __user *ctl_buf, int ctl_maxlen, s32 __user *ctl_len,
+ char __user *data_buf, int data_maxlen, s32 __user *data_len, int *flags_p)
{
int error;
int oldflags;
struct T_unitdata_ind udi;
mm_segment_t old_fs = get_fs();
long args[6];
- char *tmpbuf;
+ char __user *tmpbuf;
int tmplen;
- int (*sys_socketcall)(int, unsigned long *) =
- (int (*)(int, unsigned long *))SYS(socketcall);
- int (*sys_recvfrom)(int, void *, size_t, unsigned, struct sockaddr *, int *);
+ int (*sys_socketcall)(int, unsigned long __user *) =
+ (int (*)(int, unsigned long __user *))SYS(socketcall);
+ int (*sys_recvfrom)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *);
SOLD("entry");
SOLDD(("%u %p %d %p %p %d %p %d\n", fd, ctl_buf, ctl_maxlen, ctl_len, data_buf, data_maxlen, data_len, *flags_p));
oldflags = filp->f_flags;
filp->f_flags |= O_NONBLOCK;
SOLD("calling recvfrom");
- sys_recvfrom = (int (*)(int, void *, size_t, unsigned, struct sockaddr *, int *))SYS(recvfrom);
- error = sys_recvfrom(fd, data_buf, data_maxlen, 0, (struct sockaddr*)tmpbuf, ctl_len);
+ sys_recvfrom = (int (*)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *))SYS(recvfrom);
+ error = sys_recvfrom(fd, data_buf, data_maxlen, 0, (struct sockaddr __user *)tmpbuf, ctl_len);
filp->f_flags = oldflags;
if (error < 0)
return error;
{
struct file *filp;
struct inode *ino;
- struct strbuf *ctlptr, *datptr;
+ struct strbuf __user *ctlptr;
+ struct strbuf __user *datptr;
struct strbuf ctl, dat;
- int *flgptr;
+ int __user *flgptr;
int flags;
int error = -EBADF;
if (!ino->i_sock)
goto out;
- ctlptr = (struct strbuf *)A(arg1);
- datptr = (struct strbuf *)A(arg2);
- flgptr = (int *)A(arg3);
+ ctlptr = (struct strbuf __user *)A(arg1);
+ datptr = (struct strbuf __user *)A(arg2);
+ flgptr = (int __user *)A(arg3);
error = -EFAULT;
goto out;
}
- error = timod_getmsg(fd,(char*)A(ctl.buf),ctl.maxlen,&ctlptr->len,
- (char*)A(dat.buf),dat.maxlen,&datptr->len,&flags);
+ error = timod_getmsg(fd,A(ctl.buf),ctl.maxlen,&ctlptr->len,
+ A(dat.buf),dat.maxlen,&datptr->len,&flags);
if (!error && put_user(flags,flgptr))
error = -EFAULT;
{
struct file *filp;
struct inode *ino;
- struct strbuf *ctlptr, *datptr;
+ struct strbuf __user *ctlptr;
+ struct strbuf __user *datptr;
struct strbuf ctl, dat;
int flags = (int) arg3;
int error = -EBADF;
(imajor(ino) != 30 || iminor(ino) != 1))
goto out;
- ctlptr = (struct strbuf *)A(arg1);
- datptr = (struct strbuf *)A(arg2);
+ ctlptr = A(arg1);
+ datptr = A(arg2);
error = -EFAULT;
dat.buf = 0;
}
- error = timod_putmsg(fd,(char*)A(ctl.buf),ctl.len,
- (char*)A(dat.buf),dat.len,flags);
+ error = timod_putmsg(fd,A(ctl.buf),ctl.len,
+ A(dat.buf),dat.len,flags);
out:
unlock_kernel();
SOLD("done");
--- /dev/null
+/*
+ * Cryptographic API.
+ *
+ * Khazad Algorithm
+ *
+ * The Khazad algorithm was developed by Paulo S. L. M. Barreto and
+ * Vincent Rijmen. It was a finalist in the NESSIE encryption contest.
+ *
+ * The original authors have disclaimed all copyright interest in this
+ * code and thus put it in the public domain. The subsequent authors
+ * have put this under the GNU General Public License.
+ *
+ * By Aaron Grothe ajgrothe@yahoo.com, August 1, 2004
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <asm/scatterlist.h>
+#include <linux/crypto.h>
+
+#define KHAZAD_KEY_SIZE 16
+#define KHAZAD_BLOCK_SIZE 8
+#define KHAZAD_ROUNDS 8
+
+struct khazad_ctx {
+ u64 E[KHAZAD_ROUNDS + 1];
+ u64 D[KHAZAD_ROUNDS + 1];
+};
+
+static const u64 T0[256] = {
+ 0xbad3d268bbb96a01ULL, 0x54fc4d19e59a66b1ULL, 0x2f71bc93e26514cdULL,
+ 0x749ccdb925871b51ULL, 0x53f55102f7a257a4ULL, 0xd3686bb8d0d6be03ULL,
+ 0xd26b6fbdd6deb504ULL, 0x4dd72964b35285feULL, 0x50f05d0dfdba4aadULL,
+ 0xace98a26cf09e063ULL, 0x8d8a0e83091c9684ULL, 0xbfdcc679a5914d1aULL,
+ 0x7090ddad3da7374dULL, 0x52f65507f1aa5ca3ULL, 0x9ab352c87ba417e1ULL,
+ 0x4cd42d61b55a8ef9ULL, 0xea238f65460320acULL, 0xd56273a6c4e68411ULL,
+ 0x97a466f155cc68c2ULL, 0xd16e63b2dcc6a80dULL, 0x3355ccffaa85d099ULL,
+ 0x51f35908fbb241aaULL, 0x5bed712ac7e20f9cULL, 0xa6f7a204f359ae55ULL,
+ 0xde7f5f81febec120ULL, 0x48d83d75ad7aa2e5ULL, 0xa8e59a32d729cc7fULL,
+ 0x99b65ec771bc0ae8ULL, 0xdb704b90e096e63bULL, 0x3256c8faac8ddb9eULL,
+ 0xb7c4e65195d11522ULL, 0xfc19d72b32b3aaceULL, 0xe338ab48704b7393ULL,
+ 0x9ebf42dc63843bfdULL, 0x91ae7eef41fc52d0ULL, 0x9bb056cd7dac1ce6ULL,
+ 0xe23baf4d76437894ULL, 0xbbd0d66dbdb16106ULL, 0x41c319589b32f1daULL,
+ 0x6eb2a5cb7957e517ULL, 0xa5f2ae0bf941b35cULL, 0xcb400bc08016564bULL,
+ 0x6bbdb1da677fc20cULL, 0x95a26efb59dc7eccULL, 0xa1febe1fe1619f40ULL,
+ 0xf308eb1810cbc3e3ULL, 0xb1cefe4f81e12f30ULL, 0x0206080a0c10160eULL,
+ 0xcc4917db922e675eULL, 0xc45137f3a26e3f66ULL, 0x1d2774694ee8cf53ULL,
+ 0x143c504478a09c6cULL, 0xc3582be8b0560e73ULL, 0x63a591f2573f9a34ULL,
+ 0xda734f95e69eed3cULL, 0x5de76934d3d2358eULL, 0x5fe1613edfc22380ULL,
+ 0xdc79578bf2aed72eULL, 0x7d87e99413cf486eULL, 0xcd4a13de94266c59ULL,
+ 0x7f81e19e1fdf5e60ULL, 0x5aee752fc1ea049bULL, 0x6cb4adc17547f319ULL,
+ 0x5ce46d31d5da3e89ULL, 0xf704fb0c08ebefffULL, 0x266a98bed42d47f2ULL,
+ 0xff1cdb2438abb7c7ULL, 0xed2a937e543b11b9ULL, 0xe825876f4a1336a2ULL,
+ 0x9dba4ed3699c26f4ULL, 0x6fb1a1ce7f5fee10ULL, 0x8e8f028c03048b8dULL,
+ 0x192b647d56c8e34fULL, 0xa0fdba1ae7699447ULL, 0xf00de7171ad3deeaULL,
+ 0x89861e97113cba98ULL, 0x0f113c332278692dULL, 0x07091c1b12383115ULL,
+ 0xafec8629c511fd6aULL, 0xfb10cb30208b9bdbULL, 0x0818202830405838ULL,
+ 0x153f54417ea8976bULL, 0x0d1734392e687f23ULL, 0x040c101418202c1cULL,
+ 0x0103040506080b07ULL, 0x64ac8de94507ab21ULL, 0xdf7c5b84f8b6ca27ULL,
+ 0x769ac5b329970d5fULL, 0x798bf9800bef6472ULL, 0xdd7a538ef4a6dc29ULL,
+ 0x3d47f4c98ef5b2b3ULL, 0x163a584e74b08a62ULL, 0x3f41fcc382e5a4bdULL,
+ 0x3759dcebb2a5fc85ULL, 0x6db7a9c4734ff81eULL, 0x3848e0d890dd95a8ULL,
+ 0xb9d6de67b1a17708ULL, 0x7395d1a237bf2a44ULL, 0xe926836a4c1b3da5ULL,
+ 0x355fd4e1beb5ea8bULL, 0x55ff491ce3926db6ULL, 0x7193d9a83baf3c4aULL,
+ 0x7b8df18a07ff727cULL, 0x8c890a860f149d83ULL, 0x7296d5a731b72143ULL,
+ 0x88851a921734b19fULL, 0xf607ff090ee3e4f8ULL, 0x2a7ea882fc4d33d6ULL,
+ 0x3e42f8c684edafbaULL, 0x5ee2653bd9ca2887ULL, 0x27699cbbd2254cf5ULL,
+ 0x46ca0543890ac0cfULL, 0x0c14303c28607424ULL, 0x65af89ec430fa026ULL,
+ 0x68b8bdd56d67df05ULL, 0x61a399f85b2f8c3aULL, 0x03050c0f0a181d09ULL,
+ 0xc15e23e2bc46187dULL, 0x57f94116ef827bb8ULL, 0xd6677fa9cefe9918ULL,
+ 0xd976439aec86f035ULL, 0x58e87d25cdfa1295ULL, 0xd875479fea8efb32ULL,
+ 0x66aa85e34917bd2fULL, 0xd7647bacc8f6921fULL, 0x3a4ee8d29ccd83a6ULL,
+ 0xc84507cf8a0e4b42ULL, 0x3c44f0cc88fdb9b4ULL, 0xfa13cf35268390dcULL,
+ 0x96a762f453c463c5ULL, 0xa7f4a601f551a552ULL, 0x98b55ac277b401efULL,
+ 0xec29977b52331abeULL, 0xb8d5da62b7a97c0fULL, 0xc7543bfca876226fULL,
+ 0xaeef822cc319f66dULL, 0x69bbb9d06b6fd402ULL, 0x4bdd317aa762bfecULL,
+ 0xabe0963ddd31d176ULL, 0xa9e69e37d121c778ULL, 0x67a981e64f1fb628ULL,
+ 0x0a1e28223c504e36ULL, 0x47c901468f02cbc8ULL, 0xf20bef1d16c3c8e4ULL,
+ 0xb5c2ee5b99c1032cULL, 0x226688aacc0d6beeULL, 0xe532b356647b4981ULL,
+ 0xee2f9f715e230cb0ULL, 0xbedfc27ca399461dULL, 0x2b7dac87fa4538d1ULL,
+ 0x819e3ebf217ce2a0ULL, 0x1236485a6c90a67eULL, 0x839836b52d6cf4aeULL,
+ 0x1b2d6c775ad8f541ULL, 0x0e1238362470622aULL, 0x23658cafca0560e9ULL,
+ 0xf502f30604fbf9f1ULL, 0x45cf094c8312ddc6ULL, 0x216384a5c61576e7ULL,
+ 0xce4f1fd19e3e7150ULL, 0x49db3970ab72a9e2ULL, 0x2c74b09ce87d09c4ULL,
+ 0xf916c33a2c9b8dd5ULL, 0xe637bf596e635488ULL, 0xb6c7e25493d91e25ULL,
+ 0x2878a088f05d25d8ULL, 0x17395c4b72b88165ULL, 0x829b32b02b64ffa9ULL,
+ 0x1a2e68725cd0fe46ULL, 0x8b80169d1d2cac96ULL, 0xfe1fdf213ea3bcc0ULL,
+ 0x8a8312981b24a791ULL, 0x091b242d3648533fULL, 0xc94603ca8c064045ULL,
+ 0x879426a1354cd8b2ULL, 0x4ed2256bb94a98f7ULL, 0xe13ea3427c5b659dULL,
+ 0x2e72b896e46d1fcaULL, 0xe431b75362734286ULL, 0xe03da7477a536e9aULL,
+ 0xeb208b60400b2babULL, 0x90ad7aea47f459d7ULL, 0xa4f1aa0eff49b85bULL,
+ 0x1e22786644f0d25aULL, 0x85922eab395ccebcULL, 0x60a09dfd5d27873dULL,
+ 0x0000000000000000ULL, 0x256f94b1de355afbULL, 0xf401f70302f3f2f6ULL,
+ 0xf10ee3121cdbd5edULL, 0x94a16afe5fd475cbULL, 0x0b1d2c273a584531ULL,
+ 0xe734bb5c686b5f8fULL, 0x759fc9bc238f1056ULL, 0xef2c9b74582b07b7ULL,
+ 0x345cd0e4b8bde18cULL, 0x3153c4f5a695c697ULL, 0xd46177a3c2ee8f16ULL,
+ 0xd06d67b7dacea30aULL, 0x869722a43344d3b5ULL, 0x7e82e59b19d75567ULL,
+ 0xadea8e23c901eb64ULL, 0xfd1ad32e34bba1c9ULL, 0x297ba48df6552edfULL,
+ 0x3050c0f0a09dcd90ULL, 0x3b4decd79ac588a1ULL, 0x9fbc46d9658c30faULL,
+ 0xf815c73f2a9386d2ULL, 0xc6573ff9ae7e2968ULL, 0x13354c5f6a98ad79ULL,
+ 0x060a181e14303a12ULL, 0x050f14111e28271bULL, 0xc55233f6a4663461ULL,
+ 0x113344556688bb77ULL, 0x7799c1b62f9f0658ULL, 0x7c84ed9115c74369ULL,
+ 0x7a8ef58f01f7797bULL, 0x7888fd850de76f75ULL, 0x365ad8eeb4adf782ULL,
+ 0x1c24706c48e0c454ULL, 0x394be4dd96d59eafULL, 0x59eb7920cbf21992ULL,
+ 0x1828607850c0e848ULL, 0x56fa4513e98a70bfULL, 0xb3c8f6458df1393eULL,
+ 0xb0cdfa4a87e92437ULL, 0x246c90b4d83d51fcULL, 0x206080a0c01d7de0ULL,
+ 0xb2cbf2408bf93239ULL, 0x92ab72e04be44fd9ULL, 0xa3f8b615ed71894eULL,
+ 0xc05d27e7ba4e137aULL, 0x44cc0d49851ad6c1ULL, 0x62a695f751379133ULL,
+ 0x103040506080b070ULL, 0xb4c1ea5e9fc9082bULL, 0x84912aae3f54c5bbULL,
+ 0x43c511529722e7d4ULL, 0x93a876e54dec44deULL, 0xc25b2fedb65e0574ULL,
+ 0x4ade357fa16ab4ebULL, 0xbddace73a9815b14ULL, 0x8f8c0689050c808aULL,
+ 0x2d77b499ee7502c3ULL, 0xbcd9ca76af895013ULL, 0x9cb94ad66f942df3ULL,
+ 0x6abeb5df6177c90bULL, 0x40c01d5d9d3afaddULL, 0xcf4c1bd498367a57ULL,
+ 0xa2fbb210eb798249ULL, 0x809d3aba2774e9a7ULL, 0x4fd1216ebf4293f0ULL,
+ 0x1f217c6342f8d95dULL, 0xca430fc5861e5d4cULL, 0xaae39238db39da71ULL,
+ 0x42c61557912aecd3ULL
+};
+
+static const u64 T1[256] = {
+ 0xd3ba68d2b9bb016aULL, 0xfc54194d9ae5b166ULL, 0x712f93bc65e2cd14ULL,
+ 0x9c74b9cd8725511bULL, 0xf5530251a2f7a457ULL, 0x68d3b86bd6d003beULL,
+ 0x6bd2bd6fded604b5ULL, 0xd74d642952b3fe85ULL, 0xf0500d5dbafdad4aULL,
+ 0xe9ac268a09cf63e0ULL, 0x8a8d830e1c098496ULL, 0xdcbf79c691a51a4dULL,
+ 0x9070addda73d4d37ULL, 0xf6520755aaf1a35cULL, 0xb39ac852a47be117ULL,
+ 0xd44c612d5ab5f98eULL, 0x23ea658f0346ac20ULL, 0x62d5a673e6c41184ULL,
+ 0xa497f166cc55c268ULL, 0x6ed1b263c6dc0da8ULL, 0x5533ffcc85aa99d0ULL,
+ 0xf3510859b2fbaa41ULL, 0xed5b2a71e2c79c0fULL, 0xf7a604a259f355aeULL,
+ 0x7fde815fbefe20c1ULL, 0xd848753d7aade5a2ULL, 0xe5a8329a29d77fccULL,
+ 0xb699c75ebc71e80aULL, 0x70db904b96e03be6ULL, 0x5632fac88dac9edbULL,
+ 0xc4b751e6d1952215ULL, 0x19fc2bd7b332ceaaULL, 0x38e348ab4b709373ULL,
+ 0xbf9edc428463fd3bULL, 0xae91ef7efc41d052ULL, 0xb09bcd56ac7de61cULL,
+ 0x3be24daf43769478ULL, 0xd0bb6dd6b1bd0661ULL, 0xc3415819329bdaf1ULL,
+ 0xb26ecba5577917e5ULL, 0xf2a50bae41f95cb3ULL, 0x40cbc00b16804b56ULL,
+ 0xbd6bdab17f670cc2ULL, 0xa295fb6edc59cc7eULL, 0xfea11fbe61e1409fULL,
+ 0x08f318ebcb10e3c3ULL, 0xceb14ffee181302fULL, 0x06020a08100c0e16ULL,
+ 0x49ccdb172e925e67ULL, 0x51c4f3376ea2663fULL, 0x271d6974e84e53cfULL,
+ 0x3c144450a0786c9cULL, 0x58c3e82b56b0730eULL, 0xa563f2913f57349aULL,
+ 0x73da954f9ee63cedULL, 0xe75d3469d2d38e35ULL, 0xe15f3e61c2df8023ULL,
+ 0x79dc8b57aef22ed7ULL, 0x877d94e9cf136e48ULL, 0x4acdde132694596cULL,
+ 0x817f9ee1df1f605eULL, 0xee5a2f75eac19b04ULL, 0xb46cc1ad477519f3ULL,
+ 0xe45c316ddad5893eULL, 0x04f70cfbeb08ffefULL, 0x6a26be982dd4f247ULL,
+ 0x1cff24dbab38c7b7ULL, 0x2aed7e933b54b911ULL, 0x25e86f87134aa236ULL,
+ 0xba9dd34e9c69f426ULL, 0xb16fcea15f7f10eeULL, 0x8f8e8c0204038d8bULL,
+ 0x2b197d64c8564fe3ULL, 0xfda01aba69e74794ULL, 0x0df017e7d31aeadeULL,
+ 0x8689971e3c1198baULL, 0x110f333c78222d69ULL, 0x09071b1c38121531ULL,
+ 0xecaf298611c56afdULL, 0x10fb30cb8b20db9bULL, 0x1808282040303858ULL,
+ 0x3f154154a87e6b97ULL, 0x170d3934682e237fULL, 0x0c04141020181c2cULL,
+ 0x030105040806070bULL, 0xac64e98d074521abULL, 0x7cdf845bb6f827caULL,
+ 0x9a76b3c597295f0dULL, 0x8b7980f9ef0b7264ULL, 0x7add8e53a6f429dcULL,
+ 0x473dc9f4f58eb3b2ULL, 0x3a164e58b074628aULL, 0x413fc3fce582bda4ULL,
+ 0x5937ebdca5b285fcULL, 0xb76dc4a94f731ef8ULL, 0x4838d8e0dd90a895ULL,
+ 0xd6b967dea1b10877ULL, 0x9573a2d1bf37442aULL, 0x26e96a831b4ca53dULL,
+ 0x5f35e1d4b5be8beaULL, 0xff551c4992e3b66dULL, 0x9371a8d9af3b4a3cULL,
+ 0x8d7b8af1ff077c72ULL, 0x898c860a140f839dULL, 0x9672a7d5b7314321ULL,
+ 0x8588921a34179fb1ULL, 0x07f609ffe30ef8e4ULL, 0x7e2a82a84dfcd633ULL,
+ 0x423ec6f8ed84baafULL, 0xe25e3b65cad98728ULL, 0x6927bb9c25d2f54cULL,
+ 0xca4643050a89cfc0ULL, 0x140c3c3060282474ULL, 0xaf65ec890f4326a0ULL,
+ 0xb868d5bd676d05dfULL, 0xa361f8992f5b3a8cULL, 0x05030f0c180a091dULL,
+ 0x5ec1e22346bc7d18ULL, 0xf957164182efb87bULL, 0x67d6a97ffece1899ULL,
+ 0x76d99a4386ec35f0ULL, 0xe858257dfacd9512ULL, 0x75d89f478eea32fbULL,
+ 0xaa66e38517492fbdULL, 0x64d7ac7bf6c81f92ULL, 0x4e3ad2e8cd9ca683ULL,
+ 0x45c8cf070e8a424bULL, 0x443cccf0fd88b4b9ULL, 0x13fa35cf8326dc90ULL,
+ 0xa796f462c453c563ULL, 0xf4a701a651f552a5ULL, 0xb598c25ab477ef01ULL,
+ 0x29ec7b973352be1aULL, 0xd5b862daa9b70f7cULL, 0x54c7fc3b76a86f22ULL,
+ 0xefae2c8219c36df6ULL, 0xbb69d0b96f6b02d4ULL, 0xdd4b7a3162a7ecbfULL,
+ 0xe0ab3d9631dd76d1ULL, 0xe6a9379e21d178c7ULL, 0xa967e6811f4f28b6ULL,
+ 0x1e0a2228503c364eULL, 0xc9474601028fc8cbULL, 0x0bf21defc316e4c8ULL,
+ 0xc2b55beec1992c03ULL, 0x6622aa880dccee6bULL, 0x32e556b37b648149ULL,
+ 0x2fee719f235eb00cULL, 0xdfbe7cc299a31d46ULL, 0x7d2b87ac45fad138ULL,
+ 0x9e81bf3e7c21a0e2ULL, 0x36125a48906c7ea6ULL, 0x9883b5366c2daef4ULL,
+ 0x2d1b776cd85a41f5ULL, 0x120e363870242a62ULL, 0x6523af8c05cae960ULL,
+ 0x02f506f3fb04f1f9ULL, 0xcf454c091283c6ddULL, 0x6321a58415c6e776ULL,
+ 0x4fced11f3e9e5071ULL, 0xdb49703972abe2a9ULL, 0x742c9cb07de8c409ULL,
+ 0x16f93ac39b2cd58dULL, 0x37e659bf636e8854ULL, 0xc7b654e2d993251eULL,
+ 0x782888a05df0d825ULL, 0x39174b5cb8726581ULL, 0x9b82b032642ba9ffULL,
+ 0x2e1a7268d05c46feULL, 0x808b9d162c1d96acULL, 0x1ffe21dfa33ec0bcULL,
+ 0x838a9812241b91a7ULL, 0x1b092d2448363f53ULL, 0x46c9ca03068c4540ULL,
+ 0x9487a1264c35b2d8ULL, 0xd24e6b254ab9f798ULL, 0x3ee142a35b7c9d65ULL,
+ 0x722e96b86de4ca1fULL, 0x31e453b773628642ULL, 0x3de047a7537a9a6eULL,
+ 0x20eb608b0b40ab2bULL, 0xad90ea7af447d759ULL, 0xf1a40eaa49ff5bb8ULL,
+ 0x221e6678f0445ad2ULL, 0x9285ab2e5c39bcceULL, 0xa060fd9d275d3d87ULL,
+ 0x0000000000000000ULL, 0x6f25b19435defb5aULL, 0x01f403f7f302f6f2ULL,
+ 0x0ef112e3db1cedd5ULL, 0xa194fe6ad45fcb75ULL, 0x1d0b272c583a3145ULL,
+ 0x34e75cbb6b688f5fULL, 0x9f75bcc98f235610ULL, 0x2cef749b2b58b707ULL,
+ 0x5c34e4d0bdb88ce1ULL, 0x5331f5c495a697c6ULL, 0x61d4a377eec2168fULL,
+ 0x6dd0b767ceda0aa3ULL, 0x9786a4224433b5d3ULL, 0x827e9be5d7196755ULL,
+ 0xeaad238e01c964ebULL, 0x1afd2ed3bb34c9a1ULL, 0x7b298da455f6df2eULL,
+ 0x5030f0c09da090cdULL, 0x4d3bd7ecc59aa188ULL, 0xbc9fd9468c65fa30ULL,
+ 0x15f83fc7932ad286ULL, 0x57c6f93f7eae6829ULL, 0x35135f4c986a79adULL,
+ 0x0a061e183014123aULL, 0x0f051114281e1b27ULL, 0x52c5f63366a46134ULL,
+ 0x33115544886677bbULL, 0x9977b6c19f2f5806ULL, 0x847c91edc7156943ULL,
+ 0x8e7a8ff5f7017b79ULL, 0x887885fde70d756fULL, 0x5a36eed8adb482f7ULL,
+ 0x241c6c70e04854c4ULL, 0x4b39dde4d596af9eULL, 0xeb592079f2cb9219ULL,
+ 0x28187860c05048e8ULL, 0xfa5613458ae9bf70ULL, 0xc8b345f6f18d3e39ULL,
+ 0xcdb04afae9873724ULL, 0x6c24b4903dd8fc51ULL, 0x6020a0801dc0e07dULL,
+ 0xcbb240f2f98b3932ULL, 0xab92e072e44bd94fULL, 0xf8a315b671ed4e89ULL,
+ 0x5dc0e7274eba7a13ULL, 0xcc44490d1a85c1d6ULL, 0xa662f79537513391ULL,
+ 0x30105040806070b0ULL, 0xc1b45eeac99f2b08ULL, 0x9184ae2a543fbbc5ULL,
+ 0xc54352112297d4e7ULL, 0xa893e576ec4dde44ULL, 0x5bc2ed2f5eb67405ULL,
+ 0xde4a7f356aa1ebb4ULL, 0xdabd73ce81a9145bULL, 0x8c8f89060c058a80ULL,
+ 0x772d99b475eec302ULL, 0xd9bc76ca89af1350ULL, 0xb99cd64a946ff32dULL,
+ 0xbe6adfb577610bc9ULL, 0xc0405d1d3a9dddfaULL, 0x4ccfd41b3698577aULL,
+ 0xfba210b279eb4982ULL, 0x9d80ba3a7427a7e9ULL, 0xd14f6e2142bff093ULL,
+ 0x211f637cf8425dd9ULL, 0x43cac50f1e864c5dULL, 0xe3aa389239db71daULL,
+ 0xc64257152a91d3ecULL
+};
+
+static const u64 T2[256] = {
+ 0xd268bad36a01bbb9ULL, 0x4d1954fc66b1e59aULL, 0xbc932f7114cde265ULL,
+ 0xcdb9749c1b512587ULL, 0x510253f557a4f7a2ULL, 0x6bb8d368be03d0d6ULL,
+ 0x6fbdd26bb504d6deULL, 0x29644dd785feb352ULL, 0x5d0d50f04aadfdbaULL,
+ 0x8a26ace9e063cf09ULL, 0x0e838d8a9684091cULL, 0xc679bfdc4d1aa591ULL,
+ 0xddad7090374d3da7ULL, 0x550752f65ca3f1aaULL, 0x52c89ab317e17ba4ULL,
+ 0x2d614cd48ef9b55aULL, 0x8f65ea2320ac4603ULL, 0x73a6d5628411c4e6ULL,
+ 0x66f197a468c255ccULL, 0x63b2d16ea80ddcc6ULL, 0xccff3355d099aa85ULL,
+ 0x590851f341aafbb2ULL, 0x712a5bed0f9cc7e2ULL, 0xa204a6f7ae55f359ULL,
+ 0x5f81de7fc120febeULL, 0x3d7548d8a2e5ad7aULL, 0x9a32a8e5cc7fd729ULL,
+ 0x5ec799b60ae871bcULL, 0x4b90db70e63be096ULL, 0xc8fa3256db9eac8dULL,
+ 0xe651b7c4152295d1ULL, 0xd72bfc19aace32b3ULL, 0xab48e3387393704bULL,
+ 0x42dc9ebf3bfd6384ULL, 0x7eef91ae52d041fcULL, 0x56cd9bb01ce67dacULL,
+ 0xaf4de23b78947643ULL, 0xd66dbbd06106bdb1ULL, 0x195841c3f1da9b32ULL,
+ 0xa5cb6eb2e5177957ULL, 0xae0ba5f2b35cf941ULL, 0x0bc0cb40564b8016ULL,
+ 0xb1da6bbdc20c677fULL, 0x6efb95a27ecc59dcULL, 0xbe1fa1fe9f40e161ULL,
+ 0xeb18f308c3e310cbULL, 0xfe4fb1ce2f3081e1ULL, 0x080a0206160e0c10ULL,
+ 0x17dbcc49675e922eULL, 0x37f3c4513f66a26eULL, 0x74691d27cf534ee8ULL,
+ 0x5044143c9c6c78a0ULL, 0x2be8c3580e73b056ULL, 0x91f263a59a34573fULL,
+ 0x4f95da73ed3ce69eULL, 0x69345de7358ed3d2ULL, 0x613e5fe12380dfc2ULL,
+ 0x578bdc79d72ef2aeULL, 0xe9947d87486e13cfULL, 0x13decd4a6c599426ULL,
+ 0xe19e7f815e601fdfULL, 0x752f5aee049bc1eaULL, 0xadc16cb4f3197547ULL,
+ 0x6d315ce43e89d5daULL, 0xfb0cf704efff08ebULL, 0x98be266a47f2d42dULL,
+ 0xdb24ff1cb7c738abULL, 0x937eed2a11b9543bULL, 0x876fe82536a24a13ULL,
+ 0x4ed39dba26f4699cULL, 0xa1ce6fb1ee107f5fULL, 0x028c8e8f8b8d0304ULL,
+ 0x647d192be34f56c8ULL, 0xba1aa0fd9447e769ULL, 0xe717f00ddeea1ad3ULL,
+ 0x1e978986ba98113cULL, 0x3c330f11692d2278ULL, 0x1c1b070931151238ULL,
+ 0x8629afecfd6ac511ULL, 0xcb30fb109bdb208bULL, 0x2028081858383040ULL,
+ 0x5441153f976b7ea8ULL, 0x34390d177f232e68ULL, 0x1014040c2c1c1820ULL,
+ 0x040501030b070608ULL, 0x8de964acab214507ULL, 0x5b84df7cca27f8b6ULL,
+ 0xc5b3769a0d5f2997ULL, 0xf980798b64720befULL, 0x538edd7adc29f4a6ULL,
+ 0xf4c93d47b2b38ef5ULL, 0x584e163a8a6274b0ULL, 0xfcc33f41a4bd82e5ULL,
+ 0xdceb3759fc85b2a5ULL, 0xa9c46db7f81e734fULL, 0xe0d8384895a890ddULL,
+ 0xde67b9d67708b1a1ULL, 0xd1a273952a4437bfULL, 0x836ae9263da54c1bULL,
+ 0xd4e1355fea8bbeb5ULL, 0x491c55ff6db6e392ULL, 0xd9a871933c4a3bafULL,
+ 0xf18a7b8d727c07ffULL, 0x0a868c899d830f14ULL, 0xd5a77296214331b7ULL,
+ 0x1a928885b19f1734ULL, 0xff09f607e4f80ee3ULL, 0xa8822a7e33d6fc4dULL,
+ 0xf8c63e42afba84edULL, 0x653b5ee22887d9caULL, 0x9cbb27694cf5d225ULL,
+ 0x054346cac0cf890aULL, 0x303c0c1474242860ULL, 0x89ec65afa026430fULL,
+ 0xbdd568b8df056d67ULL, 0x99f861a38c3a5b2fULL, 0x0c0f03051d090a18ULL,
+ 0x23e2c15e187dbc46ULL, 0x411657f97bb8ef82ULL, 0x7fa9d6679918cefeULL,
+ 0x439ad976f035ec86ULL, 0x7d2558e81295cdfaULL, 0x479fd875fb32ea8eULL,
+ 0x85e366aabd2f4917ULL, 0x7bacd764921fc8f6ULL, 0xe8d23a4e83a69ccdULL,
+ 0x07cfc8454b428a0eULL, 0xf0cc3c44b9b488fdULL, 0xcf35fa1390dc2683ULL,
+ 0x62f496a763c553c4ULL, 0xa601a7f4a552f551ULL, 0x5ac298b501ef77b4ULL,
+ 0x977bec291abe5233ULL, 0xda62b8d57c0fb7a9ULL, 0x3bfcc754226fa876ULL,
+ 0x822caeeff66dc319ULL, 0xb9d069bbd4026b6fULL, 0x317a4bddbfeca762ULL,
+ 0x963dabe0d176dd31ULL, 0x9e37a9e6c778d121ULL, 0x81e667a9b6284f1fULL,
+ 0x28220a1e4e363c50ULL, 0x014647c9cbc88f02ULL, 0xef1df20bc8e416c3ULL,
+ 0xee5bb5c2032c99c1ULL, 0x88aa22666beecc0dULL, 0xb356e5324981647bULL,
+ 0x9f71ee2f0cb05e23ULL, 0xc27cbedf461da399ULL, 0xac872b7d38d1fa45ULL,
+ 0x3ebf819ee2a0217cULL, 0x485a1236a67e6c90ULL, 0x36b58398f4ae2d6cULL,
+ 0x6c771b2df5415ad8ULL, 0x38360e12622a2470ULL, 0x8caf236560e9ca05ULL,
+ 0xf306f502f9f104fbULL, 0x094c45cfddc68312ULL, 0x84a5216376e7c615ULL,
+ 0x1fd1ce4f71509e3eULL, 0x397049dba9e2ab72ULL, 0xb09c2c7409c4e87dULL,
+ 0xc33af9168dd52c9bULL, 0xbf59e63754886e63ULL, 0xe254b6c71e2593d9ULL,
+ 0xa088287825d8f05dULL, 0x5c4b1739816572b8ULL, 0x32b0829bffa92b64ULL,
+ 0x68721a2efe465cd0ULL, 0x169d8b80ac961d2cULL, 0xdf21fe1fbcc03ea3ULL,
+ 0x12988a83a7911b24ULL, 0x242d091b533f3648ULL, 0x03cac94640458c06ULL,
+ 0x26a18794d8b2354cULL, 0x256b4ed298f7b94aULL, 0xa342e13e659d7c5bULL,
+ 0xb8962e721fcae46dULL, 0xb753e43142866273ULL, 0xa747e03d6e9a7a53ULL,
+ 0x8b60eb202bab400bULL, 0x7aea90ad59d747f4ULL, 0xaa0ea4f1b85bff49ULL,
+ 0x78661e22d25a44f0ULL, 0x2eab8592cebc395cULL, 0x9dfd60a0873d5d27ULL,
+ 0x0000000000000000ULL, 0x94b1256f5afbde35ULL, 0xf703f401f2f602f3ULL,
+ 0xe312f10ed5ed1cdbULL, 0x6afe94a175cb5fd4ULL, 0x2c270b1d45313a58ULL,
+ 0xbb5ce7345f8f686bULL, 0xc9bc759f1056238fULL, 0x9b74ef2c07b7582bULL,
+ 0xd0e4345ce18cb8bdULL, 0xc4f53153c697a695ULL, 0x77a3d4618f16c2eeULL,
+ 0x67b7d06da30adaceULL, 0x22a48697d3b53344ULL, 0xe59b7e82556719d7ULL,
+ 0x8e23adeaeb64c901ULL, 0xd32efd1aa1c934bbULL, 0xa48d297b2edff655ULL,
+ 0xc0f03050cd90a09dULL, 0xecd73b4d88a19ac5ULL, 0x46d99fbc30fa658cULL,
+ 0xc73ff81586d22a93ULL, 0x3ff9c6572968ae7eULL, 0x4c5f1335ad796a98ULL,
+ 0x181e060a3a121430ULL, 0x1411050f271b1e28ULL, 0x33f6c5523461a466ULL,
+ 0x44551133bb776688ULL, 0xc1b6779906582f9fULL, 0xed917c84436915c7ULL,
+ 0xf58f7a8e797b01f7ULL, 0xfd8578886f750de7ULL, 0xd8ee365af782b4adULL,
+ 0x706c1c24c45448e0ULL, 0xe4dd394b9eaf96d5ULL, 0x792059eb1992cbf2ULL,
+ 0x60781828e84850c0ULL, 0x451356fa70bfe98aULL, 0xf645b3c8393e8df1ULL,
+ 0xfa4ab0cd243787e9ULL, 0x90b4246c51fcd83dULL, 0x80a020607de0c01dULL,
+ 0xf240b2cb32398bf9ULL, 0x72e092ab4fd94be4ULL, 0xb615a3f8894eed71ULL,
+ 0x27e7c05d137aba4eULL, 0x0d4944ccd6c1851aULL, 0x95f762a691335137ULL,
+ 0x40501030b0706080ULL, 0xea5eb4c1082b9fc9ULL, 0x2aae8491c5bb3f54ULL,
+ 0x115243c5e7d49722ULL, 0x76e593a844de4decULL, 0x2fedc25b0574b65eULL,
+ 0x357f4adeb4eba16aULL, 0xce73bdda5b14a981ULL, 0x06898f8c808a050cULL,
+ 0xb4992d7702c3ee75ULL, 0xca76bcd95013af89ULL, 0x4ad69cb92df36f94ULL,
+ 0xb5df6abec90b6177ULL, 0x1d5d40c0fadd9d3aULL, 0x1bd4cf4c7a579836ULL,
+ 0xb210a2fb8249eb79ULL, 0x3aba809de9a72774ULL, 0x216e4fd193f0bf42ULL,
+ 0x7c631f21d95d42f8ULL, 0x0fc5ca435d4c861eULL, 0x9238aae3da71db39ULL,
+ 0x155742c6ecd3912aULL
+};
+
+static const u64 T3[256] = {
+ 0x68d2d3ba016ab9bbULL, 0x194dfc54b1669ae5ULL, 0x93bc712fcd1465e2ULL,
+ 0xb9cd9c74511b8725ULL, 0x0251f553a457a2f7ULL, 0xb86b68d303bed6d0ULL,
+ 0xbd6f6bd204b5ded6ULL, 0x6429d74dfe8552b3ULL, 0x0d5df050ad4abafdULL,
+ 0x268ae9ac63e009cfULL, 0x830e8a8d84961c09ULL, 0x79c6dcbf1a4d91a5ULL,
+ 0xaddd90704d37a73dULL, 0x0755f652a35caaf1ULL, 0xc852b39ae117a47bULL,
+ 0x612dd44cf98e5ab5ULL, 0x658f23eaac200346ULL, 0xa67362d51184e6c4ULL,
+ 0xf166a497c268cc55ULL, 0xb2636ed10da8c6dcULL, 0xffcc553399d085aaULL,
+ 0x0859f351aa41b2fbULL, 0x2a71ed5b9c0fe2c7ULL, 0x04a2f7a655ae59f3ULL,
+ 0x815f7fde20c1befeULL, 0x753dd848e5a27aadULL, 0x329ae5a87fcc29d7ULL,
+ 0xc75eb699e80abc71ULL, 0x904b70db3be696e0ULL, 0xfac856329edb8dacULL,
+ 0x51e6c4b72215d195ULL, 0x2bd719fcceaab332ULL, 0x48ab38e393734b70ULL,
+ 0xdc42bf9efd3b8463ULL, 0xef7eae91d052fc41ULL, 0xcd56b09be61cac7dULL,
+ 0x4daf3be294784376ULL, 0x6dd6d0bb0661b1bdULL, 0x5819c341daf1329bULL,
+ 0xcba5b26e17e55779ULL, 0x0baef2a55cb341f9ULL, 0xc00b40cb4b561680ULL,
+ 0xdab1bd6b0cc27f67ULL, 0xfb6ea295cc7edc59ULL, 0x1fbefea1409f61e1ULL,
+ 0x18eb08f3e3c3cb10ULL, 0x4ffeceb1302fe181ULL, 0x0a0806020e16100cULL,
+ 0xdb1749cc5e672e92ULL, 0xf33751c4663f6ea2ULL, 0x6974271d53cfe84eULL,
+ 0x44503c146c9ca078ULL, 0xe82b58c3730e56b0ULL, 0xf291a563349a3f57ULL,
+ 0x954f73da3ced9ee6ULL, 0x3469e75d8e35d2d3ULL, 0x3e61e15f8023c2dfULL,
+ 0x8b5779dc2ed7aef2ULL, 0x94e9877d6e48cf13ULL, 0xde134acd596c2694ULL,
+ 0x9ee1817f605edf1fULL, 0x2f75ee5a9b04eac1ULL, 0xc1adb46c19f34775ULL,
+ 0x316de45c893edad5ULL, 0x0cfb04f7ffefeb08ULL, 0xbe986a26f2472dd4ULL,
+ 0x24db1cffc7b7ab38ULL, 0x7e932aedb9113b54ULL, 0x6f8725e8a236134aULL,
+ 0xd34eba9df4269c69ULL, 0xcea1b16f10ee5f7fULL, 0x8c028f8e8d8b0403ULL,
+ 0x7d642b194fe3c856ULL, 0x1abafda0479469e7ULL, 0x17e70df0eaded31aULL,
+ 0x971e868998ba3c11ULL, 0x333c110f2d697822ULL, 0x1b1c090715313812ULL,
+ 0x2986ecaf6afd11c5ULL, 0x30cb10fbdb9b8b20ULL, 0x2820180838584030ULL,
+ 0x41543f156b97a87eULL, 0x3934170d237f682eULL, 0x14100c041c2c2018ULL,
+ 0x05040301070b0806ULL, 0xe98dac6421ab0745ULL, 0x845b7cdf27cab6f8ULL,
+ 0xb3c59a765f0d9729ULL, 0x80f98b797264ef0bULL, 0x8e537add29dca6f4ULL,
+ 0xc9f4473db3b2f58eULL, 0x4e583a16628ab074ULL, 0xc3fc413fbda4e582ULL,
+ 0xebdc593785fca5b2ULL, 0xc4a9b76d1ef84f73ULL, 0xd8e04838a895dd90ULL,
+ 0x67ded6b90877a1b1ULL, 0xa2d19573442abf37ULL, 0x6a8326e9a53d1b4cULL,
+ 0xe1d45f358beab5beULL, 0x1c49ff55b66d92e3ULL, 0xa8d993714a3caf3bULL,
+ 0x8af18d7b7c72ff07ULL, 0x860a898c839d140fULL, 0xa7d596724321b731ULL,
+ 0x921a85889fb13417ULL, 0x09ff07f6f8e4e30eULL, 0x82a87e2ad6334dfcULL,
+ 0xc6f8423ebaafed84ULL, 0x3b65e25e8728cad9ULL, 0xbb9c6927f54c25d2ULL,
+ 0x4305ca46cfc00a89ULL, 0x3c30140c24746028ULL, 0xec89af6526a00f43ULL,
+ 0xd5bdb86805df676dULL, 0xf899a3613a8c2f5bULL, 0x0f0c0503091d180aULL,
+ 0xe2235ec17d1846bcULL, 0x1641f957b87b82efULL, 0xa97f67d61899feceULL,
+ 0x9a4376d935f086ecULL, 0x257de8589512facdULL, 0x9f4775d832fb8eeaULL,
+ 0xe385aa662fbd1749ULL, 0xac7b64d71f92f6c8ULL, 0xd2e84e3aa683cd9cULL,
+ 0xcf0745c8424b0e8aULL, 0xccf0443cb4b9fd88ULL, 0x35cf13fadc908326ULL,
+ 0xf462a796c563c453ULL, 0x01a6f4a752a551f5ULL, 0xc25ab598ef01b477ULL,
+ 0x7b9729ecbe1a3352ULL, 0x62dad5b80f7ca9b7ULL, 0xfc3b54c76f2276a8ULL,
+ 0x2c82efae6df619c3ULL, 0xd0b9bb6902d46f6bULL, 0x7a31dd4becbf62a7ULL,
+ 0x3d96e0ab76d131ddULL, 0x379ee6a978c721d1ULL, 0xe681a96728b61f4fULL,
+ 0x22281e0a364e503cULL, 0x4601c947c8cb028fULL, 0x1def0bf2e4c8c316ULL,
+ 0x5beec2b52c03c199ULL, 0xaa886622ee6b0dccULL, 0x56b332e581497b64ULL,
+ 0x719f2feeb00c235eULL, 0x7cc2dfbe1d4699a3ULL, 0x87ac7d2bd13845faULL,
+ 0xbf3e9e81a0e27c21ULL, 0x5a4836127ea6906cULL, 0xb5369883aef46c2dULL,
+ 0x776c2d1b41f5d85aULL, 0x3638120e2a627024ULL, 0xaf8c6523e96005caULL,
+ 0x06f302f5f1f9fb04ULL, 0x4c09cf45c6dd1283ULL, 0xa5846321e77615c6ULL,
+ 0xd11f4fce50713e9eULL, 0x7039db49e2a972abULL, 0x9cb0742cc4097de8ULL,
+ 0x3ac316f9d58d9b2cULL, 0x59bf37e68854636eULL, 0x54e2c7b6251ed993ULL,
+ 0x88a07828d8255df0ULL, 0x4b5c39176581b872ULL, 0xb0329b82a9ff642bULL,
+ 0x72682e1a46fed05cULL, 0x9d16808b96ac2c1dULL, 0x21df1ffec0bca33eULL,
+ 0x9812838a91a7241bULL, 0x2d241b093f534836ULL, 0xca0346c94540068cULL,
+ 0xa1269487b2d84c35ULL, 0x6b25d24ef7984ab9ULL, 0x42a33ee19d655b7cULL,
+ 0x96b8722eca1f6de4ULL, 0x53b731e486427362ULL, 0x47a73de09a6e537aULL,
+ 0x608b20ebab2b0b40ULL, 0xea7aad90d759f447ULL, 0x0eaaf1a45bb849ffULL,
+ 0x6678221e5ad2f044ULL, 0xab2e9285bcce5c39ULL, 0xfd9da0603d87275dULL,
+ 0x0000000000000000ULL, 0xb1946f25fb5a35deULL, 0x03f701f4f6f2f302ULL,
+ 0x12e30ef1edd5db1cULL, 0xfe6aa194cb75d45fULL, 0x272c1d0b3145583aULL,
+ 0x5cbb34e78f5f6b68ULL, 0xbcc99f7556108f23ULL, 0x749b2cefb7072b58ULL,
+ 0xe4d05c348ce1bdb8ULL, 0xf5c4533197c695a6ULL, 0xa37761d4168feec2ULL,
+ 0xb7676dd00aa3cedaULL, 0xa4229786b5d34433ULL, 0x9be5827e6755d719ULL,
+ 0x238eeaad64eb01c9ULL, 0x2ed31afdc9a1bb34ULL, 0x8da47b29df2e55f6ULL,
+ 0xf0c0503090cd9da0ULL, 0xd7ec4d3ba188c59aULL, 0xd946bc9ffa308c65ULL,
+ 0x3fc715f8d286932aULL, 0xf93f57c668297eaeULL, 0x5f4c351379ad986aULL,
+ 0x1e180a06123a3014ULL, 0x11140f051b27281eULL, 0xf63352c5613466a4ULL,
+ 0x5544331177bb8866ULL, 0xb6c1997758069f2fULL, 0x91ed847c6943c715ULL,
+ 0x8ff58e7a7b79f701ULL, 0x85fd8878756fe70dULL, 0xeed85a3682f7adb4ULL,
+ 0x6c70241c54c4e048ULL, 0xdde44b39af9ed596ULL, 0x2079eb599219f2cbULL,
+ 0x7860281848e8c050ULL, 0x1345fa56bf708ae9ULL, 0x45f6c8b33e39f18dULL,
+ 0x4afacdb03724e987ULL, 0xb4906c24fc513dd8ULL, 0xa0806020e07d1dc0ULL,
+ 0x40f2cbb23932f98bULL, 0xe072ab92d94fe44bULL, 0x15b6f8a34e8971edULL,
+ 0xe7275dc07a134ebaULL, 0x490dcc44c1d61a85ULL, 0xf795a66233913751ULL,
+ 0x5040301070b08060ULL, 0x5eeac1b42b08c99fULL, 0xae2a9184bbc5543fULL,
+ 0x5211c543d4e72297ULL, 0xe576a893de44ec4dULL, 0xed2f5bc274055eb6ULL,
+ 0x7f35de4aebb46aa1ULL, 0x73cedabd145b81a9ULL, 0x89068c8f8a800c05ULL,
+ 0x99b4772dc30275eeULL, 0x76cad9bc135089afULL, 0xd64ab99cf32d946fULL,
+ 0xdfb5be6a0bc97761ULL, 0x5d1dc040ddfa3a9dULL, 0xd41b4ccf577a3698ULL,
+ 0x10b2fba2498279ebULL, 0xba3a9d80a7e97427ULL, 0x6e21d14ff09342bfULL,
+ 0x637c211f5dd9f842ULL, 0xc50f43ca4c5d1e86ULL, 0x3892e3aa71da39dbULL,
+ 0x5715c642d3ec2a91ULL
+};
+
+static const u64 T4[256] = {
+ 0xbbb96a01bad3d268ULL, 0xe59a66b154fc4d19ULL, 0xe26514cd2f71bc93ULL,
+ 0x25871b51749ccdb9ULL, 0xf7a257a453f55102ULL, 0xd0d6be03d3686bb8ULL,
+ 0xd6deb504d26b6fbdULL, 0xb35285fe4dd72964ULL, 0xfdba4aad50f05d0dULL,
+ 0xcf09e063ace98a26ULL, 0x091c96848d8a0e83ULL, 0xa5914d1abfdcc679ULL,
+ 0x3da7374d7090ddadULL, 0xf1aa5ca352f65507ULL, 0x7ba417e19ab352c8ULL,
+ 0xb55a8ef94cd42d61ULL, 0x460320acea238f65ULL, 0xc4e68411d56273a6ULL,
+ 0x55cc68c297a466f1ULL, 0xdcc6a80dd16e63b2ULL, 0xaa85d0993355ccffULL,
+ 0xfbb241aa51f35908ULL, 0xc7e20f9c5bed712aULL, 0xf359ae55a6f7a204ULL,
+ 0xfebec120de7f5f81ULL, 0xad7aa2e548d83d75ULL, 0xd729cc7fa8e59a32ULL,
+ 0x71bc0ae899b65ec7ULL, 0xe096e63bdb704b90ULL, 0xac8ddb9e3256c8faULL,
+ 0x95d11522b7c4e651ULL, 0x32b3aacefc19d72bULL, 0x704b7393e338ab48ULL,
+ 0x63843bfd9ebf42dcULL, 0x41fc52d091ae7eefULL, 0x7dac1ce69bb056cdULL,
+ 0x76437894e23baf4dULL, 0xbdb16106bbd0d66dULL, 0x9b32f1da41c31958ULL,
+ 0x7957e5176eb2a5cbULL, 0xf941b35ca5f2ae0bULL, 0x8016564bcb400bc0ULL,
+ 0x677fc20c6bbdb1daULL, 0x59dc7ecc95a26efbULL, 0xe1619f40a1febe1fULL,
+ 0x10cbc3e3f308eb18ULL, 0x81e12f30b1cefe4fULL, 0x0c10160e0206080aULL,
+ 0x922e675ecc4917dbULL, 0xa26e3f66c45137f3ULL, 0x4ee8cf531d277469ULL,
+ 0x78a09c6c143c5044ULL, 0xb0560e73c3582be8ULL, 0x573f9a3463a591f2ULL,
+ 0xe69eed3cda734f95ULL, 0xd3d2358e5de76934ULL, 0xdfc223805fe1613eULL,
+ 0xf2aed72edc79578bULL, 0x13cf486e7d87e994ULL, 0x94266c59cd4a13deULL,
+ 0x1fdf5e607f81e19eULL, 0xc1ea049b5aee752fULL, 0x7547f3196cb4adc1ULL,
+ 0xd5da3e895ce46d31ULL, 0x08ebeffff704fb0cULL, 0xd42d47f2266a98beULL,
+ 0x38abb7c7ff1cdb24ULL, 0x543b11b9ed2a937eULL, 0x4a1336a2e825876fULL,
+ 0x699c26f49dba4ed3ULL, 0x7f5fee106fb1a1ceULL, 0x03048b8d8e8f028cULL,
+ 0x56c8e34f192b647dULL, 0xe7699447a0fdba1aULL, 0x1ad3deeaf00de717ULL,
+ 0x113cba9889861e97ULL, 0x2278692d0f113c33ULL, 0x1238311507091c1bULL,
+ 0xc511fd6aafec8629ULL, 0x208b9bdbfb10cb30ULL, 0x3040583808182028ULL,
+ 0x7ea8976b153f5441ULL, 0x2e687f230d173439ULL, 0x18202c1c040c1014ULL,
+ 0x06080b0701030405ULL, 0x4507ab2164ac8de9ULL, 0xf8b6ca27df7c5b84ULL,
+ 0x29970d5f769ac5b3ULL, 0x0bef6472798bf980ULL, 0xf4a6dc29dd7a538eULL,
+ 0x8ef5b2b33d47f4c9ULL, 0x74b08a62163a584eULL, 0x82e5a4bd3f41fcc3ULL,
+ 0xb2a5fc853759dcebULL, 0x734ff81e6db7a9c4ULL, 0x90dd95a83848e0d8ULL,
+ 0xb1a17708b9d6de67ULL, 0x37bf2a447395d1a2ULL, 0x4c1b3da5e926836aULL,
+ 0xbeb5ea8b355fd4e1ULL, 0xe3926db655ff491cULL, 0x3baf3c4a7193d9a8ULL,
+ 0x07ff727c7b8df18aULL, 0x0f149d838c890a86ULL, 0x31b721437296d5a7ULL,
+ 0x1734b19f88851a92ULL, 0x0ee3e4f8f607ff09ULL, 0xfc4d33d62a7ea882ULL,
+ 0x84edafba3e42f8c6ULL, 0xd9ca28875ee2653bULL, 0xd2254cf527699cbbULL,
+ 0x890ac0cf46ca0543ULL, 0x286074240c14303cULL, 0x430fa02665af89ecULL,
+ 0x6d67df0568b8bdd5ULL, 0x5b2f8c3a61a399f8ULL, 0x0a181d0903050c0fULL,
+ 0xbc46187dc15e23e2ULL, 0xef827bb857f94116ULL, 0xcefe9918d6677fa9ULL,
+ 0xec86f035d976439aULL, 0xcdfa129558e87d25ULL, 0xea8efb32d875479fULL,
+ 0x4917bd2f66aa85e3ULL, 0xc8f6921fd7647bacULL, 0x9ccd83a63a4ee8d2ULL,
+ 0x8a0e4b42c84507cfULL, 0x88fdb9b43c44f0ccULL, 0x268390dcfa13cf35ULL,
+ 0x53c463c596a762f4ULL, 0xf551a552a7f4a601ULL, 0x77b401ef98b55ac2ULL,
+ 0x52331abeec29977bULL, 0xb7a97c0fb8d5da62ULL, 0xa876226fc7543bfcULL,
+ 0xc319f66daeef822cULL, 0x6b6fd40269bbb9d0ULL, 0xa762bfec4bdd317aULL,
+ 0xdd31d176abe0963dULL, 0xd121c778a9e69e37ULL, 0x4f1fb62867a981e6ULL,
+ 0x3c504e360a1e2822ULL, 0x8f02cbc847c90146ULL, 0x16c3c8e4f20bef1dULL,
+ 0x99c1032cb5c2ee5bULL, 0xcc0d6bee226688aaULL, 0x647b4981e532b356ULL,
+ 0x5e230cb0ee2f9f71ULL, 0xa399461dbedfc27cULL, 0xfa4538d12b7dac87ULL,
+ 0x217ce2a0819e3ebfULL, 0x6c90a67e1236485aULL, 0x2d6cf4ae839836b5ULL,
+ 0x5ad8f5411b2d6c77ULL, 0x2470622a0e123836ULL, 0xca0560e923658cafULL,
+ 0x04fbf9f1f502f306ULL, 0x8312ddc645cf094cULL, 0xc61576e7216384a5ULL,
+ 0x9e3e7150ce4f1fd1ULL, 0xab72a9e249db3970ULL, 0xe87d09c42c74b09cULL,
+ 0x2c9b8dd5f916c33aULL, 0x6e635488e637bf59ULL, 0x93d91e25b6c7e254ULL,
+ 0xf05d25d82878a088ULL, 0x72b8816517395c4bULL, 0x2b64ffa9829b32b0ULL,
+ 0x5cd0fe461a2e6872ULL, 0x1d2cac968b80169dULL, 0x3ea3bcc0fe1fdf21ULL,
+ 0x1b24a7918a831298ULL, 0x3648533f091b242dULL, 0x8c064045c94603caULL,
+ 0x354cd8b2879426a1ULL, 0xb94a98f74ed2256bULL, 0x7c5b659de13ea342ULL,
+ 0xe46d1fca2e72b896ULL, 0x62734286e431b753ULL, 0x7a536e9ae03da747ULL,
+ 0x400b2babeb208b60ULL, 0x47f459d790ad7aeaULL, 0xff49b85ba4f1aa0eULL,
+ 0x44f0d25a1e227866ULL, 0x395ccebc85922eabULL, 0x5d27873d60a09dfdULL,
+ 0x0000000000000000ULL, 0xde355afb256f94b1ULL, 0x02f3f2f6f401f703ULL,
+ 0x1cdbd5edf10ee312ULL, 0x5fd475cb94a16afeULL, 0x3a5845310b1d2c27ULL,
+ 0x686b5f8fe734bb5cULL, 0x238f1056759fc9bcULL, 0x582b07b7ef2c9b74ULL,
+ 0xb8bde18c345cd0e4ULL, 0xa695c6973153c4f5ULL, 0xc2ee8f16d46177a3ULL,
+ 0xdacea30ad06d67b7ULL, 0x3344d3b5869722a4ULL, 0x19d755677e82e59bULL,
+ 0xc901eb64adea8e23ULL, 0x34bba1c9fd1ad32eULL, 0xf6552edf297ba48dULL,
+ 0xa09dcd903050c0f0ULL, 0x9ac588a13b4decd7ULL, 0x658c30fa9fbc46d9ULL,
+ 0x2a9386d2f815c73fULL, 0xae7e2968c6573ff9ULL, 0x6a98ad7913354c5fULL,
+ 0x14303a12060a181eULL, 0x1e28271b050f1411ULL, 0xa4663461c55233f6ULL,
+ 0x6688bb7711334455ULL, 0x2f9f06587799c1b6ULL, 0x15c743697c84ed91ULL,
+ 0x01f7797b7a8ef58fULL, 0x0de76f757888fd85ULL, 0xb4adf782365ad8eeULL,
+ 0x48e0c4541c24706cULL, 0x96d59eaf394be4ddULL, 0xcbf2199259eb7920ULL,
+ 0x50c0e84818286078ULL, 0xe98a70bf56fa4513ULL, 0x8df1393eb3c8f645ULL,
+ 0x87e92437b0cdfa4aULL, 0xd83d51fc246c90b4ULL, 0xc01d7de0206080a0ULL,
+ 0x8bf93239b2cbf240ULL, 0x4be44fd992ab72e0ULL, 0xed71894ea3f8b615ULL,
+ 0xba4e137ac05d27e7ULL, 0x851ad6c144cc0d49ULL, 0x5137913362a695f7ULL,
+ 0x6080b07010304050ULL, 0x9fc9082bb4c1ea5eULL, 0x3f54c5bb84912aaeULL,
+ 0x9722e7d443c51152ULL, 0x4dec44de93a876e5ULL, 0xb65e0574c25b2fedULL,
+ 0xa16ab4eb4ade357fULL, 0xa9815b14bddace73ULL, 0x050c808a8f8c0689ULL,
+ 0xee7502c32d77b499ULL, 0xaf895013bcd9ca76ULL, 0x6f942df39cb94ad6ULL,
+ 0x6177c90b6abeb5dfULL, 0x9d3afadd40c01d5dULL, 0x98367a57cf4c1bd4ULL,
+ 0xeb798249a2fbb210ULL, 0x2774e9a7809d3abaULL, 0xbf4293f04fd1216eULL,
+ 0x42f8d95d1f217c63ULL, 0x861e5d4cca430fc5ULL, 0xdb39da71aae39238ULL,
+ 0x912aecd342c61557ULL
+};
+
+static const u64 T5[256] = {
+ 0xb9bb016ad3ba68d2ULL, 0x9ae5b166fc54194dULL, 0x65e2cd14712f93bcULL,
+ 0x8725511b9c74b9cdULL, 0xa2f7a457f5530251ULL, 0xd6d003be68d3b86bULL,
+ 0xded604b56bd2bd6fULL, 0x52b3fe85d74d6429ULL, 0xbafdad4af0500d5dULL,
+ 0x09cf63e0e9ac268aULL, 0x1c0984968a8d830eULL, 0x91a51a4ddcbf79c6ULL,
+ 0xa73d4d379070adddULL, 0xaaf1a35cf6520755ULL, 0xa47be117b39ac852ULL,
+ 0x5ab5f98ed44c612dULL, 0x0346ac2023ea658fULL, 0xe6c4118462d5a673ULL,
+ 0xcc55c268a497f166ULL, 0xc6dc0da86ed1b263ULL, 0x85aa99d05533ffccULL,
+ 0xb2fbaa41f3510859ULL, 0xe2c79c0fed5b2a71ULL, 0x59f355aef7a604a2ULL,
+ 0xbefe20c17fde815fULL, 0x7aade5a2d848753dULL, 0x29d77fcce5a8329aULL,
+ 0xbc71e80ab699c75eULL, 0x96e03be670db904bULL, 0x8dac9edb5632fac8ULL,
+ 0xd1952215c4b751e6ULL, 0xb332ceaa19fc2bd7ULL, 0x4b70937338e348abULL,
+ 0x8463fd3bbf9edc42ULL, 0xfc41d052ae91ef7eULL, 0xac7de61cb09bcd56ULL,
+ 0x437694783be24dafULL, 0xb1bd0661d0bb6dd6ULL, 0x329bdaf1c3415819ULL,
+ 0x577917e5b26ecba5ULL, 0x41f95cb3f2a50baeULL, 0x16804b5640cbc00bULL,
+ 0x7f670cc2bd6bdab1ULL, 0xdc59cc7ea295fb6eULL, 0x61e1409ffea11fbeULL,
+ 0xcb10e3c308f318ebULL, 0xe181302fceb14ffeULL, 0x100c0e1606020a08ULL,
+ 0x2e925e6749ccdb17ULL, 0x6ea2663f51c4f337ULL, 0xe84e53cf271d6974ULL,
+ 0xa0786c9c3c144450ULL, 0x56b0730e58c3e82bULL, 0x3f57349aa563f291ULL,
+ 0x9ee63ced73da954fULL, 0xd2d38e35e75d3469ULL, 0xc2df8023e15f3e61ULL,
+ 0xaef22ed779dc8b57ULL, 0xcf136e48877d94e9ULL, 0x2694596c4acdde13ULL,
+ 0xdf1f605e817f9ee1ULL, 0xeac19b04ee5a2f75ULL, 0x477519f3b46cc1adULL,
+ 0xdad5893ee45c316dULL, 0xeb08ffef04f70cfbULL, 0x2dd4f2476a26be98ULL,
+ 0xab38c7b71cff24dbULL, 0x3b54b9112aed7e93ULL, 0x134aa23625e86f87ULL,
+ 0x9c69f426ba9dd34eULL, 0x5f7f10eeb16fcea1ULL, 0x04038d8b8f8e8c02ULL,
+ 0xc8564fe32b197d64ULL, 0x69e74794fda01abaULL, 0xd31aeade0df017e7ULL,
+ 0x3c1198ba8689971eULL, 0x78222d69110f333cULL, 0x3812153109071b1cULL,
+ 0x11c56afdecaf2986ULL, 0x8b20db9b10fb30cbULL, 0x4030385818082820ULL,
+ 0xa87e6b973f154154ULL, 0x682e237f170d3934ULL, 0x20181c2c0c041410ULL,
+ 0x0806070b03010504ULL, 0x074521abac64e98dULL, 0xb6f827ca7cdf845bULL,
+ 0x97295f0d9a76b3c5ULL, 0xef0b72648b7980f9ULL, 0xa6f429dc7add8e53ULL,
+ 0xf58eb3b2473dc9f4ULL, 0xb074628a3a164e58ULL, 0xe582bda4413fc3fcULL,
+ 0xa5b285fc5937ebdcULL, 0x4f731ef8b76dc4a9ULL, 0xdd90a8954838d8e0ULL,
+ 0xa1b10877d6b967deULL, 0xbf37442a9573a2d1ULL, 0x1b4ca53d26e96a83ULL,
+ 0xb5be8bea5f35e1d4ULL, 0x92e3b66dff551c49ULL, 0xaf3b4a3c9371a8d9ULL,
+ 0xff077c728d7b8af1ULL, 0x140f839d898c860aULL, 0xb73143219672a7d5ULL,
+ 0x34179fb18588921aULL, 0xe30ef8e407f609ffULL, 0x4dfcd6337e2a82a8ULL,
+ 0xed84baaf423ec6f8ULL, 0xcad98728e25e3b65ULL, 0x25d2f54c6927bb9cULL,
+ 0x0a89cfc0ca464305ULL, 0x60282474140c3c30ULL, 0x0f4326a0af65ec89ULL,
+ 0x676d05dfb868d5bdULL, 0x2f5b3a8ca361f899ULL, 0x180a091d05030f0cULL,
+ 0x46bc7d185ec1e223ULL, 0x82efb87bf9571641ULL, 0xfece189967d6a97fULL,
+ 0x86ec35f076d99a43ULL, 0xfacd9512e858257dULL, 0x8eea32fb75d89f47ULL,
+ 0x17492fbdaa66e385ULL, 0xf6c81f9264d7ac7bULL, 0xcd9ca6834e3ad2e8ULL,
+ 0x0e8a424b45c8cf07ULL, 0xfd88b4b9443cccf0ULL, 0x8326dc9013fa35cfULL,
+ 0xc453c563a796f462ULL, 0x51f552a5f4a701a6ULL, 0xb477ef01b598c25aULL,
+ 0x3352be1a29ec7b97ULL, 0xa9b70f7cd5b862daULL, 0x76a86f2254c7fc3bULL,
+ 0x19c36df6efae2c82ULL, 0x6f6b02d4bb69d0b9ULL, 0x62a7ecbfdd4b7a31ULL,
+ 0x31dd76d1e0ab3d96ULL, 0x21d178c7e6a9379eULL, 0x1f4f28b6a967e681ULL,
+ 0x503c364e1e0a2228ULL, 0x028fc8cbc9474601ULL, 0xc316e4c80bf21defULL,
+ 0xc1992c03c2b55beeULL, 0x0dccee6b6622aa88ULL, 0x7b64814932e556b3ULL,
+ 0x235eb00c2fee719fULL, 0x99a31d46dfbe7cc2ULL, 0x45fad1387d2b87acULL,
+ 0x7c21a0e29e81bf3eULL, 0x906c7ea636125a48ULL, 0x6c2daef49883b536ULL,
+ 0xd85a41f52d1b776cULL, 0x70242a62120e3638ULL, 0x05cae9606523af8cULL,
+ 0xfb04f1f902f506f3ULL, 0x1283c6ddcf454c09ULL, 0x15c6e7766321a584ULL,
+ 0x3e9e50714fced11fULL, 0x72abe2a9db497039ULL, 0x7de8c409742c9cb0ULL,
+ 0x9b2cd58d16f93ac3ULL, 0x636e885437e659bfULL, 0xd993251ec7b654e2ULL,
+ 0x5df0d825782888a0ULL, 0xb872658139174b5cULL, 0x642ba9ff9b82b032ULL,
+ 0xd05c46fe2e1a7268ULL, 0x2c1d96ac808b9d16ULL, 0xa33ec0bc1ffe21dfULL,
+ 0x241b91a7838a9812ULL, 0x48363f531b092d24ULL, 0x068c454046c9ca03ULL,
+ 0x4c35b2d89487a126ULL, 0x4ab9f798d24e6b25ULL, 0x5b7c9d653ee142a3ULL,
+ 0x6de4ca1f722e96b8ULL, 0x7362864231e453b7ULL, 0x537a9a6e3de047a7ULL,
+ 0x0b40ab2b20eb608bULL, 0xf447d759ad90ea7aULL, 0x49ff5bb8f1a40eaaULL,
+ 0xf0445ad2221e6678ULL, 0x5c39bcce9285ab2eULL, 0x275d3d87a060fd9dULL,
+ 0x0000000000000000ULL, 0x35defb5a6f25b194ULL, 0xf302f6f201f403f7ULL,
+ 0xdb1cedd50ef112e3ULL, 0xd45fcb75a194fe6aULL, 0x583a31451d0b272cULL,
+ 0x6b688f5f34e75cbbULL, 0x8f2356109f75bcc9ULL, 0x2b58b7072cef749bULL,
+ 0xbdb88ce15c34e4d0ULL, 0x95a697c65331f5c4ULL, 0xeec2168f61d4a377ULL,
+ 0xceda0aa36dd0b767ULL, 0x4433b5d39786a422ULL, 0xd7196755827e9be5ULL,
+ 0x01c964ebeaad238eULL, 0xbb34c9a11afd2ed3ULL, 0x55f6df2e7b298da4ULL,
+ 0x9da090cd5030f0c0ULL, 0xc59aa1884d3bd7ecULL, 0x8c65fa30bc9fd946ULL,
+ 0x932ad28615f83fc7ULL, 0x7eae682957c6f93fULL, 0x986a79ad35135f4cULL,
+ 0x3014123a0a061e18ULL, 0x281e1b270f051114ULL, 0x66a4613452c5f633ULL,
+ 0x886677bb33115544ULL, 0x9f2f58069977b6c1ULL, 0xc7156943847c91edULL,
+ 0xf7017b798e7a8ff5ULL, 0xe70d756f887885fdULL, 0xadb482f75a36eed8ULL,
+ 0xe04854c4241c6c70ULL, 0xd596af9e4b39dde4ULL, 0xf2cb9219eb592079ULL,
+ 0xc05048e828187860ULL, 0x8ae9bf70fa561345ULL, 0xf18d3e39c8b345f6ULL,
+ 0xe9873724cdb04afaULL, 0x3dd8fc516c24b490ULL, 0x1dc0e07d6020a080ULL,
+ 0xf98b3932cbb240f2ULL, 0xe44bd94fab92e072ULL, 0x71ed4e89f8a315b6ULL,
+ 0x4eba7a135dc0e727ULL, 0x1a85c1d6cc44490dULL, 0x37513391a662f795ULL,
+ 0x806070b030105040ULL, 0xc99f2b08c1b45eeaULL, 0x543fbbc59184ae2aULL,
+ 0x2297d4e7c5435211ULL, 0xec4dde44a893e576ULL, 0x5eb674055bc2ed2fULL,
+ 0x6aa1ebb4de4a7f35ULL, 0x81a9145bdabd73ceULL, 0x0c058a808c8f8906ULL,
+ 0x75eec302772d99b4ULL, 0x89af1350d9bc76caULL, 0x946ff32db99cd64aULL,
+ 0x77610bc9be6adfb5ULL, 0x3a9dddfac0405d1dULL, 0x3698577a4ccfd41bULL,
+ 0x79eb4982fba210b2ULL, 0x7427a7e99d80ba3aULL, 0x42bff093d14f6e21ULL,
+ 0xf8425dd9211f637cULL, 0x1e864c5d43cac50fULL, 0x39db71dae3aa3892ULL,
+ 0x2a91d3ecc6425715ULL
+};
+
+static const u64 T6[256] = {
+ 0x6a01bbb9d268bad3ULL, 0x66b1e59a4d1954fcULL, 0x14cde265bc932f71ULL,
+ 0x1b512587cdb9749cULL, 0x57a4f7a2510253f5ULL, 0xbe03d0d66bb8d368ULL,
+ 0xb504d6de6fbdd26bULL, 0x85feb35229644dd7ULL, 0x4aadfdba5d0d50f0ULL,
+ 0xe063cf098a26ace9ULL, 0x9684091c0e838d8aULL, 0x4d1aa591c679bfdcULL,
+ 0x374d3da7ddad7090ULL, 0x5ca3f1aa550752f6ULL, 0x17e17ba452c89ab3ULL,
+ 0x8ef9b55a2d614cd4ULL, 0x20ac46038f65ea23ULL, 0x8411c4e673a6d562ULL,
+ 0x68c255cc66f197a4ULL, 0xa80ddcc663b2d16eULL, 0xd099aa85ccff3355ULL,
+ 0x41aafbb2590851f3ULL, 0x0f9cc7e2712a5bedULL, 0xae55f359a204a6f7ULL,
+ 0xc120febe5f81de7fULL, 0xa2e5ad7a3d7548d8ULL, 0xcc7fd7299a32a8e5ULL,
+ 0x0ae871bc5ec799b6ULL, 0xe63be0964b90db70ULL, 0xdb9eac8dc8fa3256ULL,
+ 0x152295d1e651b7c4ULL, 0xaace32b3d72bfc19ULL, 0x7393704bab48e338ULL,
+ 0x3bfd638442dc9ebfULL, 0x52d041fc7eef91aeULL, 0x1ce67dac56cd9bb0ULL,
+ 0x78947643af4de23bULL, 0x6106bdb1d66dbbd0ULL, 0xf1da9b32195841c3ULL,
+ 0xe5177957a5cb6eb2ULL, 0xb35cf941ae0ba5f2ULL, 0x564b80160bc0cb40ULL,
+ 0xc20c677fb1da6bbdULL, 0x7ecc59dc6efb95a2ULL, 0x9f40e161be1fa1feULL,
+ 0xc3e310cbeb18f308ULL, 0x2f3081e1fe4fb1ceULL, 0x160e0c10080a0206ULL,
+ 0x675e922e17dbcc49ULL, 0x3f66a26e37f3c451ULL, 0xcf534ee874691d27ULL,
+ 0x9c6c78a05044143cULL, 0x0e73b0562be8c358ULL, 0x9a34573f91f263a5ULL,
+ 0xed3ce69e4f95da73ULL, 0x358ed3d269345de7ULL, 0x2380dfc2613e5fe1ULL,
+ 0xd72ef2ae578bdc79ULL, 0x486e13cfe9947d87ULL, 0x6c59942613decd4aULL,
+ 0x5e601fdfe19e7f81ULL, 0x049bc1ea752f5aeeULL, 0xf3197547adc16cb4ULL,
+ 0x3e89d5da6d315ce4ULL, 0xefff08ebfb0cf704ULL, 0x47f2d42d98be266aULL,
+ 0xb7c738abdb24ff1cULL, 0x11b9543b937eed2aULL, 0x36a24a13876fe825ULL,
+ 0x26f4699c4ed39dbaULL, 0xee107f5fa1ce6fb1ULL, 0x8b8d0304028c8e8fULL,
+ 0xe34f56c8647d192bULL, 0x9447e769ba1aa0fdULL, 0xdeea1ad3e717f00dULL,
+ 0xba98113c1e978986ULL, 0x692d22783c330f11ULL, 0x311512381c1b0709ULL,
+ 0xfd6ac5118629afecULL, 0x9bdb208bcb30fb10ULL, 0x5838304020280818ULL,
+ 0x976b7ea85441153fULL, 0x7f232e6834390d17ULL, 0x2c1c18201014040cULL,
+ 0x0b07060804050103ULL, 0xab2145078de964acULL, 0xca27f8b65b84df7cULL,
+ 0x0d5f2997c5b3769aULL, 0x64720beff980798bULL, 0xdc29f4a6538edd7aULL,
+ 0xb2b38ef5f4c93d47ULL, 0x8a6274b0584e163aULL, 0xa4bd82e5fcc33f41ULL,
+ 0xfc85b2a5dceb3759ULL, 0xf81e734fa9c46db7ULL, 0x95a890dde0d83848ULL,
+ 0x7708b1a1de67b9d6ULL, 0x2a4437bfd1a27395ULL, 0x3da54c1b836ae926ULL,
+ 0xea8bbeb5d4e1355fULL, 0x6db6e392491c55ffULL, 0x3c4a3bafd9a87193ULL,
+ 0x727c07fff18a7b8dULL, 0x9d830f140a868c89ULL, 0x214331b7d5a77296ULL,
+ 0xb19f17341a928885ULL, 0xe4f80ee3ff09f607ULL, 0x33d6fc4da8822a7eULL,
+ 0xafba84edf8c63e42ULL, 0x2887d9ca653b5ee2ULL, 0x4cf5d2259cbb2769ULL,
+ 0xc0cf890a054346caULL, 0x74242860303c0c14ULL, 0xa026430f89ec65afULL,
+ 0xdf056d67bdd568b8ULL, 0x8c3a5b2f99f861a3ULL, 0x1d090a180c0f0305ULL,
+ 0x187dbc4623e2c15eULL, 0x7bb8ef82411657f9ULL, 0x9918cefe7fa9d667ULL,
+ 0xf035ec86439ad976ULL, 0x1295cdfa7d2558e8ULL, 0xfb32ea8e479fd875ULL,
+ 0xbd2f491785e366aaULL, 0x921fc8f67bacd764ULL, 0x83a69ccde8d23a4eULL,
+ 0x4b428a0e07cfc845ULL, 0xb9b488fdf0cc3c44ULL, 0x90dc2683cf35fa13ULL,
+ 0x63c553c462f496a7ULL, 0xa552f551a601a7f4ULL, 0x01ef77b45ac298b5ULL,
+ 0x1abe5233977bec29ULL, 0x7c0fb7a9da62b8d5ULL, 0x226fa8763bfcc754ULL,
+ 0xf66dc319822caeefULL, 0xd4026b6fb9d069bbULL, 0xbfeca762317a4bddULL,
+ 0xd176dd31963dabe0ULL, 0xc778d1219e37a9e6ULL, 0xb6284f1f81e667a9ULL,
+ 0x4e363c5028220a1eULL, 0xcbc88f02014647c9ULL, 0xc8e416c3ef1df20bULL,
+ 0x032c99c1ee5bb5c2ULL, 0x6beecc0d88aa2266ULL, 0x4981647bb356e532ULL,
+ 0x0cb05e239f71ee2fULL, 0x461da399c27cbedfULL, 0x38d1fa45ac872b7dULL,
+ 0xe2a0217c3ebf819eULL, 0xa67e6c90485a1236ULL, 0xf4ae2d6c36b58398ULL,
+ 0xf5415ad86c771b2dULL, 0x622a247038360e12ULL, 0x60e9ca058caf2365ULL,
+ 0xf9f104fbf306f502ULL, 0xddc68312094c45cfULL, 0x76e7c61584a52163ULL,
+ 0x71509e3e1fd1ce4fULL, 0xa9e2ab72397049dbULL, 0x09c4e87db09c2c74ULL,
+ 0x8dd52c9bc33af916ULL, 0x54886e63bf59e637ULL, 0x1e2593d9e254b6c7ULL,
+ 0x25d8f05da0882878ULL, 0x816572b85c4b1739ULL, 0xffa92b6432b0829bULL,
+ 0xfe465cd068721a2eULL, 0xac961d2c169d8b80ULL, 0xbcc03ea3df21fe1fULL,
+ 0xa7911b2412988a83ULL, 0x533f3648242d091bULL, 0x40458c0603cac946ULL,
+ 0xd8b2354c26a18794ULL, 0x98f7b94a256b4ed2ULL, 0x659d7c5ba342e13eULL,
+ 0x1fcae46db8962e72ULL, 0x42866273b753e431ULL, 0x6e9a7a53a747e03dULL,
+ 0x2bab400b8b60eb20ULL, 0x59d747f47aea90adULL, 0xb85bff49aa0ea4f1ULL,
+ 0xd25a44f078661e22ULL, 0xcebc395c2eab8592ULL, 0x873d5d279dfd60a0ULL,
+ 0x0000000000000000ULL, 0x5afbde3594b1256fULL, 0xf2f602f3f703f401ULL,
+ 0xd5ed1cdbe312f10eULL, 0x75cb5fd46afe94a1ULL, 0x45313a582c270b1dULL,
+ 0x5f8f686bbb5ce734ULL, 0x1056238fc9bc759fULL, 0x07b7582b9b74ef2cULL,
+ 0xe18cb8bdd0e4345cULL, 0xc697a695c4f53153ULL, 0x8f16c2ee77a3d461ULL,
+ 0xa30adace67b7d06dULL, 0xd3b5334422a48697ULL, 0x556719d7e59b7e82ULL,
+ 0xeb64c9018e23adeaULL, 0xa1c934bbd32efd1aULL, 0x2edff655a48d297bULL,
+ 0xcd90a09dc0f03050ULL, 0x88a19ac5ecd73b4dULL, 0x30fa658c46d99fbcULL,
+ 0x86d22a93c73ff815ULL, 0x2968ae7e3ff9c657ULL, 0xad796a984c5f1335ULL,
+ 0x3a121430181e060aULL, 0x271b1e281411050fULL, 0x3461a46633f6c552ULL,
+ 0xbb77668844551133ULL, 0x06582f9fc1b67799ULL, 0x436915c7ed917c84ULL,
+ 0x797b01f7f58f7a8eULL, 0x6f750de7fd857888ULL, 0xf782b4add8ee365aULL,
+ 0xc45448e0706c1c24ULL, 0x9eaf96d5e4dd394bULL, 0x1992cbf2792059ebULL,
+ 0xe84850c060781828ULL, 0x70bfe98a451356faULL, 0x393e8df1f645b3c8ULL,
+ 0x243787e9fa4ab0cdULL, 0x51fcd83d90b4246cULL, 0x7de0c01d80a02060ULL,
+ 0x32398bf9f240b2cbULL, 0x4fd94be472e092abULL, 0x894eed71b615a3f8ULL,
+ 0x137aba4e27e7c05dULL, 0xd6c1851a0d4944ccULL, 0x9133513795f762a6ULL,
+ 0xb070608040501030ULL, 0x082b9fc9ea5eb4c1ULL, 0xc5bb3f542aae8491ULL,
+ 0xe7d49722115243c5ULL, 0x44de4dec76e593a8ULL, 0x0574b65e2fedc25bULL,
+ 0xb4eba16a357f4adeULL, 0x5b14a981ce73bddaULL, 0x808a050c06898f8cULL,
+ 0x02c3ee75b4992d77ULL, 0x5013af89ca76bcd9ULL, 0x2df36f944ad69cb9ULL,
+ 0xc90b6177b5df6abeULL, 0xfadd9d3a1d5d40c0ULL, 0x7a5798361bd4cf4cULL,
+ 0x8249eb79b210a2fbULL, 0xe9a727743aba809dULL, 0x93f0bf42216e4fd1ULL,
+ 0xd95d42f87c631f21ULL, 0x5d4c861e0fc5ca43ULL, 0xda71db399238aae3ULL,
+ 0xecd3912a155742c6ULL
+};
+
+static const u64 T7[256] = {
+ 0x016ab9bb68d2d3baULL, 0xb1669ae5194dfc54ULL, 0xcd1465e293bc712fULL,
+ 0x511b8725b9cd9c74ULL, 0xa457a2f70251f553ULL, 0x03bed6d0b86b68d3ULL,
+ 0x04b5ded6bd6f6bd2ULL, 0xfe8552b36429d74dULL, 0xad4abafd0d5df050ULL,
+ 0x63e009cf268ae9acULL, 0x84961c09830e8a8dULL, 0x1a4d91a579c6dcbfULL,
+ 0x4d37a73daddd9070ULL, 0xa35caaf10755f652ULL, 0xe117a47bc852b39aULL,
+ 0xf98e5ab5612dd44cULL, 0xac200346658f23eaULL, 0x1184e6c4a67362d5ULL,
+ 0xc268cc55f166a497ULL, 0x0da8c6dcb2636ed1ULL, 0x99d085aaffcc5533ULL,
+ 0xaa41b2fb0859f351ULL, 0x9c0fe2c72a71ed5bULL, 0x55ae59f304a2f7a6ULL,
+ 0x20c1befe815f7fdeULL, 0xe5a27aad753dd848ULL, 0x7fcc29d7329ae5a8ULL,
+ 0xe80abc71c75eb699ULL, 0x3be696e0904b70dbULL, 0x9edb8dacfac85632ULL,
+ 0x2215d19551e6c4b7ULL, 0xceaab3322bd719fcULL, 0x93734b7048ab38e3ULL,
+ 0xfd3b8463dc42bf9eULL, 0xd052fc41ef7eae91ULL, 0xe61cac7dcd56b09bULL,
+ 0x947843764daf3be2ULL, 0x0661b1bd6dd6d0bbULL, 0xdaf1329b5819c341ULL,
+ 0x17e55779cba5b26eULL, 0x5cb341f90baef2a5ULL, 0x4b561680c00b40cbULL,
+ 0x0cc27f67dab1bd6bULL, 0xcc7edc59fb6ea295ULL, 0x409f61e11fbefea1ULL,
+ 0xe3c3cb1018eb08f3ULL, 0x302fe1814ffeceb1ULL, 0x0e16100c0a080602ULL,
+ 0x5e672e92db1749ccULL, 0x663f6ea2f33751c4ULL, 0x53cfe84e6974271dULL,
+ 0x6c9ca07844503c14ULL, 0x730e56b0e82b58c3ULL, 0x349a3f57f291a563ULL,
+ 0x3ced9ee6954f73daULL, 0x8e35d2d33469e75dULL, 0x8023c2df3e61e15fULL,
+ 0x2ed7aef28b5779dcULL, 0x6e48cf1394e9877dULL, 0x596c2694de134acdULL,
+ 0x605edf1f9ee1817fULL, 0x9b04eac12f75ee5aULL, 0x19f34775c1adb46cULL,
+ 0x893edad5316de45cULL, 0xffefeb080cfb04f7ULL, 0xf2472dd4be986a26ULL,
+ 0xc7b7ab3824db1cffULL, 0xb9113b547e932aedULL, 0xa236134a6f8725e8ULL,
+ 0xf4269c69d34eba9dULL, 0x10ee5f7fcea1b16fULL, 0x8d8b04038c028f8eULL,
+ 0x4fe3c8567d642b19ULL, 0x479469e71abafda0ULL, 0xeaded31a17e70df0ULL,
+ 0x98ba3c11971e8689ULL, 0x2d697822333c110fULL, 0x153138121b1c0907ULL,
+ 0x6afd11c52986ecafULL, 0xdb9b8b2030cb10fbULL, 0x3858403028201808ULL,
+ 0x6b97a87e41543f15ULL, 0x237f682e3934170dULL, 0x1c2c201814100c04ULL,
+ 0x070b080605040301ULL, 0x21ab0745e98dac64ULL, 0x27cab6f8845b7cdfULL,
+ 0x5f0d9729b3c59a76ULL, 0x7264ef0b80f98b79ULL, 0x29dca6f48e537addULL,
+ 0xb3b2f58ec9f4473dULL, 0x628ab0744e583a16ULL, 0xbda4e582c3fc413fULL,
+ 0x85fca5b2ebdc5937ULL, 0x1ef84f73c4a9b76dULL, 0xa895dd90d8e04838ULL,
+ 0x0877a1b167ded6b9ULL, 0x442abf37a2d19573ULL, 0xa53d1b4c6a8326e9ULL,
+ 0x8beab5bee1d45f35ULL, 0xb66d92e31c49ff55ULL, 0x4a3caf3ba8d99371ULL,
+ 0x7c72ff078af18d7bULL, 0x839d140f860a898cULL, 0x4321b731a7d59672ULL,
+ 0x9fb13417921a8588ULL, 0xf8e4e30e09ff07f6ULL, 0xd6334dfc82a87e2aULL,
+ 0xbaafed84c6f8423eULL, 0x8728cad93b65e25eULL, 0xf54c25d2bb9c6927ULL,
+ 0xcfc00a894305ca46ULL, 0x247460283c30140cULL, 0x26a00f43ec89af65ULL,
+ 0x05df676dd5bdb868ULL, 0x3a8c2f5bf899a361ULL, 0x091d180a0f0c0503ULL,
+ 0x7d1846bce2235ec1ULL, 0xb87b82ef1641f957ULL, 0x1899fecea97f67d6ULL,
+ 0x35f086ec9a4376d9ULL, 0x9512facd257de858ULL, 0x32fb8eea9f4775d8ULL,
+ 0x2fbd1749e385aa66ULL, 0x1f92f6c8ac7b64d7ULL, 0xa683cd9cd2e84e3aULL,
+ 0x424b0e8acf0745c8ULL, 0xb4b9fd88ccf0443cULL, 0xdc90832635cf13faULL,
+ 0xc563c453f462a796ULL, 0x52a551f501a6f4a7ULL, 0xef01b477c25ab598ULL,
+ 0xbe1a33527b9729ecULL, 0x0f7ca9b762dad5b8ULL, 0x6f2276a8fc3b54c7ULL,
+ 0x6df619c32c82efaeULL, 0x02d46f6bd0b9bb69ULL, 0xecbf62a77a31dd4bULL,
+ 0x76d131dd3d96e0abULL, 0x78c721d1379ee6a9ULL, 0x28b61f4fe681a967ULL,
+ 0x364e503c22281e0aULL, 0xc8cb028f4601c947ULL, 0xe4c8c3161def0bf2ULL,
+ 0x2c03c1995beec2b5ULL, 0xee6b0dccaa886622ULL, 0x81497b6456b332e5ULL,
+ 0xb00c235e719f2feeULL, 0x1d4699a37cc2dfbeULL, 0xd13845fa87ac7d2bULL,
+ 0xa0e27c21bf3e9e81ULL, 0x7ea6906c5a483612ULL, 0xaef46c2db5369883ULL,
+ 0x41f5d85a776c2d1bULL, 0x2a6270243638120eULL, 0xe96005caaf8c6523ULL,
+ 0xf1f9fb0406f302f5ULL, 0xc6dd12834c09cf45ULL, 0xe77615c6a5846321ULL,
+ 0x50713e9ed11f4fceULL, 0xe2a972ab7039db49ULL, 0xc4097de89cb0742cULL,
+ 0xd58d9b2c3ac316f9ULL, 0x8854636e59bf37e6ULL, 0x251ed99354e2c7b6ULL,
+ 0xd8255df088a07828ULL, 0x6581b8724b5c3917ULL, 0xa9ff642bb0329b82ULL,
+ 0x46fed05c72682e1aULL, 0x96ac2c1d9d16808bULL, 0xc0bca33e21df1ffeULL,
+ 0x91a7241b9812838aULL, 0x3f5348362d241b09ULL, 0x4540068cca0346c9ULL,
+ 0xb2d84c35a1269487ULL, 0xf7984ab96b25d24eULL, 0x9d655b7c42a33ee1ULL,
+ 0xca1f6de496b8722eULL, 0x8642736253b731e4ULL, 0x9a6e537a47a73de0ULL,
+ 0xab2b0b40608b20ebULL, 0xd759f447ea7aad90ULL, 0x5bb849ff0eaaf1a4ULL,
+ 0x5ad2f0446678221eULL, 0xbcce5c39ab2e9285ULL, 0x3d87275dfd9da060ULL,
+ 0x0000000000000000ULL, 0xfb5a35deb1946f25ULL, 0xf6f2f30203f701f4ULL,
+ 0xedd5db1c12e30ef1ULL, 0xcb75d45ffe6aa194ULL, 0x3145583a272c1d0bULL,
+ 0x8f5f6b685cbb34e7ULL, 0x56108f23bcc99f75ULL, 0xb7072b58749b2cefULL,
+ 0x8ce1bdb8e4d05c34ULL, 0x97c695a6f5c45331ULL, 0x168feec2a37761d4ULL,
+ 0x0aa3cedab7676dd0ULL, 0xb5d34433a4229786ULL, 0x6755d7199be5827eULL,
+ 0x64eb01c9238eeaadULL, 0xc9a1bb342ed31afdULL, 0xdf2e55f68da47b29ULL,
+ 0x90cd9da0f0c05030ULL, 0xa188c59ad7ec4d3bULL, 0xfa308c65d946bc9fULL,
+ 0xd286932a3fc715f8ULL, 0x68297eaef93f57c6ULL, 0x79ad986a5f4c3513ULL,
+ 0x123a30141e180a06ULL, 0x1b27281e11140f05ULL, 0x613466a4f63352c5ULL,
+ 0x77bb886655443311ULL, 0x58069f2fb6c19977ULL, 0x6943c71591ed847cULL,
+ 0x7b79f7018ff58e7aULL, 0x756fe70d85fd8878ULL, 0x82f7adb4eed85a36ULL,
+ 0x54c4e0486c70241cULL, 0xaf9ed596dde44b39ULL, 0x9219f2cb2079eb59ULL,
+ 0x48e8c05078602818ULL, 0xbf708ae91345fa56ULL, 0x3e39f18d45f6c8b3ULL,
+ 0x3724e9874afacdb0ULL, 0xfc513dd8b4906c24ULL, 0xe07d1dc0a0806020ULL,
+ 0x3932f98b40f2cbb2ULL, 0xd94fe44be072ab92ULL, 0x4e8971ed15b6f8a3ULL,
+ 0x7a134ebae7275dc0ULL, 0xc1d61a85490dcc44ULL, 0x33913751f795a662ULL,
+ 0x70b0806050403010ULL, 0x2b08c99f5eeac1b4ULL, 0xbbc5543fae2a9184ULL,
+ 0xd4e722975211c543ULL, 0xde44ec4de576a893ULL, 0x74055eb6ed2f5bc2ULL,
+ 0xebb46aa17f35de4aULL, 0x145b81a973cedabdULL, 0x8a800c0589068c8fULL,
+ 0xc30275ee99b4772dULL, 0x135089af76cad9bcULL, 0xf32d946fd64ab99cULL,
+ 0x0bc97761dfb5be6aULL, 0xddfa3a9d5d1dc040ULL, 0x577a3698d41b4ccfULL,
+ 0x498279eb10b2fba2ULL, 0xa7e97427ba3a9d80ULL, 0xf09342bf6e21d14fULL,
+ 0x5dd9f842637c211fULL, 0x4c5d1e86c50f43caULL, 0x71da39db3892e3aaULL,
+ 0xd3ec2a915715c642ULL
+};
+
+static const u64 c[KHAZAD_ROUNDS + 1] = {
+ 0xba542f7453d3d24dULL, 0x50ac8dbf70529a4cULL, 0xead597d133515ba6ULL,
+ 0xde48a899db32b7fcULL, 0xe39e919be2bb416eULL, 0xa5cb6b95a1f3b102ULL,
+ 0xccc41d14c363da5dULL, 0x5fdc7dcd7f5a6c5cULL, 0xf726ffede89d6f8eULL
+};
+
+static int khazad_setkey(void *ctx_arg, const u8 *in_key,
+ unsigned int key_len, u32 *flags)
+{
+
+ struct khazad_ctx *ctx = ctx_arg;
+ int r;
+ const u64 *S = T7;
+ u64 K2, K1;
+
+ if (key_len != 16)
+ {
+ *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+ return -EINVAL;
+ }
+
+ K2 = ((u64)in_key[ 0] << 56) ^
+ ((u64)in_key[ 1] << 48) ^
+ ((u64)in_key[ 2] << 40) ^
+ ((u64)in_key[ 3] << 32) ^
+ ((u64)in_key[ 4] << 24) ^
+ ((u64)in_key[ 5] << 16) ^
+ ((u64)in_key[ 6] << 8) ^
+ ((u64)in_key[ 7] );
+ K1 = ((u64)in_key[ 8] << 56) ^
+ ((u64)in_key[ 9] << 48) ^
+ ((u64)in_key[10] << 40) ^
+ ((u64)in_key[11] << 32) ^
+ ((u64)in_key[12] << 24) ^
+ ((u64)in_key[13] << 16) ^
+ ((u64)in_key[14] << 8) ^
+ ((u64)in_key[15] );
+
+ /* setup the encrypt key */
+ for (r = 0; r <= KHAZAD_ROUNDS; r++) {
+ ctx->E[r] = T0[(int)(K1 >> 56) ] ^
+ T1[(int)(K1 >> 48) & 0xff] ^
+ T2[(int)(K1 >> 40) & 0xff] ^
+ T3[(int)(K1 >> 32) & 0xff] ^
+ T4[(int)(K1 >> 24) & 0xff] ^
+ T5[(int)(K1 >> 16) & 0xff] ^
+ T6[(int)(K1 >> 8) & 0xff] ^
+ T7[(int)(K1 ) & 0xff] ^
+ c[r] ^ K2;
+ K2 = K1;
+ K1 = ctx->E[r];
+ }
+ /* Setup the decrypt key */
+ ctx->D[0] = ctx->E[KHAZAD_ROUNDS];
+ for (r = 1; r < KHAZAD_ROUNDS; r++) {
+ K1 = ctx->E[KHAZAD_ROUNDS - r];
+ ctx->D[r] = T0[(int)S[(int)(K1 >> 56) ] & 0xff] ^
+ T1[(int)S[(int)(K1 >> 48) & 0xff] & 0xff] ^
+ T2[(int)S[(int)(K1 >> 40) & 0xff] & 0xff] ^
+ T3[(int)S[(int)(K1 >> 32) & 0xff] & 0xff] ^
+ T4[(int)S[(int)(K1 >> 24) & 0xff] & 0xff] ^
+ T5[(int)S[(int)(K1 >> 16) & 0xff] & 0xff] ^
+ T6[(int)S[(int)(K1 >> 8) & 0xff] & 0xff] ^
+ T7[(int)S[(int)(K1 ) & 0xff] & 0xff];
+ }
+ ctx->D[KHAZAD_ROUNDS] = ctx->E[0];
+
+ return 0;
+
+}
+
+static void khazad_crypt(const u64 roundKey[KHAZAD_ROUNDS + 1],
+ u8 *ciphertext, const u8 *plaintext)
+{
+
+ int r;
+ u64 state;
+
+ state = ((u64)plaintext[0] << 56) ^
+ ((u64)plaintext[1] << 48) ^
+ ((u64)plaintext[2] << 40) ^
+ ((u64)plaintext[3] << 32) ^
+ ((u64)plaintext[4] << 24) ^
+ ((u64)plaintext[5] << 16) ^
+ ((u64)plaintext[6] << 8) ^
+ ((u64)plaintext[7] ) ^
+ roundKey[0];
+
+ for (r = 1; r < KHAZAD_ROUNDS; r++) {
+ state = T0[(int)(state >> 56) ] ^
+ T1[(int)(state >> 48) & 0xff] ^
+ T2[(int)(state >> 40) & 0xff] ^
+ T3[(int)(state >> 32) & 0xff] ^
+ T4[(int)(state >> 24) & 0xff] ^
+ T5[(int)(state >> 16) & 0xff] ^
+ T6[(int)(state >> 8) & 0xff] ^
+ T7[(int)(state ) & 0xff] ^
+ roundKey[r];
+ }
+
+ state = (T0[(int)(state >> 56) ] & 0xff00000000000000ULL) ^
+ (T1[(int)(state >> 48) & 0xff] & 0x00ff000000000000ULL) ^
+ (T2[(int)(state >> 40) & 0xff] & 0x0000ff0000000000ULL) ^
+ (T3[(int)(state >> 32) & 0xff] & 0x000000ff00000000ULL) ^
+ (T4[(int)(state >> 24) & 0xff] & 0x00000000ff000000ULL) ^
+ (T5[(int)(state >> 16) & 0xff] & 0x0000000000ff0000ULL) ^
+ (T6[(int)(state >> 8) & 0xff] & 0x000000000000ff00ULL) ^
+ (T7[(int)(state ) & 0xff] & 0x00000000000000ffULL) ^
+ roundKey[KHAZAD_ROUNDS];
+
+ ciphertext[0] = (u8)(state >> 56);
+ ciphertext[1] = (u8)(state >> 48);
+ ciphertext[2] = (u8)(state >> 40);
+ ciphertext[3] = (u8)(state >> 32);
+ ciphertext[4] = (u8)(state >> 24);
+ ciphertext[5] = (u8)(state >> 16);
+ ciphertext[6] = (u8)(state >> 8);
+ ciphertext[7] = (u8)(state );
+
+}
+
+static void khazad_encrypt(void *ctx_arg, u8 *dst, const u8 *src)
+{
+ struct khazad_ctx *ctx = ctx_arg;
+ khazad_crypt(ctx->E, dst, src);
+}
+
+static void khazad_decrypt(void *ctx_arg, u8 *dst, const u8 *src)
+{
+ struct khazad_ctx *ctx = ctx_arg;
+ khazad_crypt(ctx->D, dst, src);
+}
+
+static struct crypto_alg khazad_alg = {
+ .cra_name = "khazad",
+ .cra_flags = CRYPTO_ALG_TYPE_CIPHER,
+ .cra_blocksize = KHAZAD_BLOCK_SIZE,
+ .cra_ctxsize = sizeof (struct khazad_ctx),
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(khazad_alg.cra_list),
+ .cra_u = { .cipher = {
+ .cia_min_keysize = KHAZAD_KEY_SIZE,
+ .cia_max_keysize = KHAZAD_KEY_SIZE,
+ .cia_setkey = khazad_setkey,
+ .cia_encrypt = khazad_encrypt,
+ .cia_decrypt = khazad_decrypt } }
+};
+
+static int __init init(void)
+{
+ int ret = 0;
+
+ ret = crypto_register_alg(&khazad_alg);
+ return ret;
+}
+
+static void __exit fini(void)
+{
+ crypto_unregister_alg(&khazad_alg);
+}
+
+
+module_init(init);
+module_exit(fini);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Khazad Cryptographic Algorithm");
--- /dev/null
+/*
+ * Cryptographic API.
+ *
+ * TEA and Xtended TEA Algorithms
+ *
+ * The TEA and Xtended TEA algorithms were developed by David Wheeler
+ * and Roger Needham at the Computer Laboratory of Cambridge University.
+ *
+ * Copyright (c) 2004 Aaron Grothe ajgrothe@yahoo.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/init.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <asm/scatterlist.h>
+#include <linux/crypto.h>
+
+#define TEA_KEY_SIZE 16
+#define TEA_BLOCK_SIZE 8
+#define TEA_ROUNDS 32
+#define TEA_DELTA 0x9e3779b9
+
+#define XTEA_KEY_SIZE 16
+#define XTEA_BLOCK_SIZE 8
+#define XTEA_ROUNDS 32
+#define XTEA_DELTA 0x9e3779b9
+
+#define u32_in(x) le32_to_cpu(*(const u32 *)(x))
+#define u32_out(to, from) (*(u32 *)(to) = cpu_to_le32(from))
+
+struct tea_ctx {
+ u32 KEY[4];
+};
+
+struct xtea_ctx {
+ u32 KEY[4];
+};
+
+static int tea_setkey(void *ctx_arg, const u8 *in_key,
+ unsigned int key_len, u32 *flags)
+{
+
+ struct tea_ctx *ctx = ctx_arg;
+
+ if (key_len != 16)
+ {
+ *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+ return -EINVAL;
+ }
+
+ ctx->KEY[0] = u32_in (in_key);
+ ctx->KEY[1] = u32_in (in_key + 4);
+ ctx->KEY[2] = u32_in (in_key + 8);
+ ctx->KEY[3] = u32_in (in_key + 12);
+
+ return 0;
+
+}
+
+static void tea_encrypt(void *ctx_arg, u8 *dst, const u8 *src)
+{
+ u32 y, z, n, sum = 0;
+ u32 k0, k1, k2, k3;
+
+ struct tea_ctx *ctx = ctx_arg;
+
+ y = u32_in (src);
+ z = u32_in (src + 4);
+
+ k0 = ctx->KEY[0];
+ k1 = ctx->KEY[1];
+ k2 = ctx->KEY[2];
+ k3 = ctx->KEY[3];
+
+ n = TEA_ROUNDS;
+
+ while (n-- > 0) {
+ sum += TEA_DELTA;
+ y += ((z << 4) + k0) ^ (z + sum) ^ ((z >> 5) + k1);
+ z += ((y << 4) + k2) ^ (y + sum) ^ ((y >> 5) + k3);
+ }
+
+ u32_out (dst, y);
+ u32_out (dst + 4, z);
+}
+
+static void tea_decrypt(void *ctx_arg, u8 *dst, const u8 *src)
+{
+ u32 y, z, n, sum;
+ u32 k0, k1, k2, k3;
+
+ struct tea_ctx *ctx = ctx_arg;
+
+ y = u32_in (src);
+ z = u32_in (src + 4);
+
+ k0 = ctx->KEY[0];
+ k1 = ctx->KEY[1];
+ k2 = ctx->KEY[2];
+ k3 = ctx->KEY[3];
+
+ sum = TEA_DELTA << 5;
+
+ n = TEA_ROUNDS;
+
+ while (n-- > 0) {
+ z -= ((y << 4) + k2) ^ (y + sum) ^ ((y >> 5) + k3);
+ y -= ((z << 4) + k0) ^ (z + sum) ^ ((z >> 5) + k1);
+ sum -= TEA_DELTA;
+ }
+
+ u32_out (dst, y);
+ u32_out (dst + 4, z);
+
+}
+
+static int xtea_setkey(void *ctx_arg, const u8 *in_key,
+ unsigned int key_len, u32 *flags)
+{
+
+ struct xtea_ctx *ctx = ctx_arg;
+
+ if (key_len != 16)
+ {
+ *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+ return -EINVAL;
+ }
+
+ ctx->KEY[0] = u32_in (in_key);
+ ctx->KEY[1] = u32_in (in_key + 4);
+ ctx->KEY[2] = u32_in (in_key + 8);
+ ctx->KEY[3] = u32_in (in_key + 12);
+
+ return 0;
+
+}
+
+static void xtea_encrypt(void *ctx_arg, u8 *dst, const u8 *src)
+{
+
+ u32 y, z, sum = 0;
+ u32 limit = XTEA_DELTA * XTEA_ROUNDS;
+
+ struct xtea_ctx *ctx = ctx_arg;
+
+ y = u32_in (src);
+ z = u32_in (src + 4);
+
+ while (sum != limit) {
+ y += (z << 4 ^ z >> 5) + (z ^ sum) + ctx->KEY[sum&3];
+ sum += TEA_DELTA;
+ z += (y << 4 ^ y >> 5) + (y ^ sum) + ctx->KEY[sum>>11 &3];
+ }
+
+ u32_out (dst, y);
+ u32_out (dst + 4, z);
+
+}
+
+static void xtea_decrypt(void *ctx_arg, u8 *dst, const u8 *src)
+{
+
+ u32 y, z, sum;
+ struct tea_ctx *ctx = ctx_arg;
+
+ y = u32_in (src);
+ z = u32_in (src + 4);
+
+ sum = XTEA_DELTA * XTEA_ROUNDS;
+
+ while (sum) {
+ z -= (y << 4 ^ y >> 5) + (y ^ sum) + ctx->KEY[sum>>11 & 3];
+ sum -= XTEA_DELTA;
+ y -= (z << 4 ^ z >> 5) + (z ^ sum) + ctx->KEY[sum & 3];
+ }
+
+ u32_out (dst, y);
+ u32_out (dst + 4, z);
+
+}
+
+static struct crypto_alg tea_alg = {
+ .cra_name = "tea",
+ .cra_flags = CRYPTO_ALG_TYPE_CIPHER,
+ .cra_blocksize = TEA_BLOCK_SIZE,
+ .cra_ctxsize = sizeof (struct tea_ctx),
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(tea_alg.cra_list),
+ .cra_u = { .cipher = {
+ .cia_min_keysize = TEA_KEY_SIZE,
+ .cia_max_keysize = TEA_KEY_SIZE,
+ .cia_setkey = tea_setkey,
+ .cia_encrypt = tea_encrypt,
+ .cia_decrypt = tea_decrypt } }
+};
+
+static struct crypto_alg xtea_alg = {
+ .cra_name = "xtea",
+ .cra_flags = CRYPTO_ALG_TYPE_CIPHER,
+ .cra_blocksize = XTEA_BLOCK_SIZE,
+ .cra_ctxsize = sizeof (struct xtea_ctx),
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(xtea_alg.cra_list),
+ .cra_u = { .cipher = {
+ .cia_min_keysize = XTEA_KEY_SIZE,
+ .cia_max_keysize = XTEA_KEY_SIZE,
+ .cia_setkey = xtea_setkey,
+ .cia_encrypt = xtea_encrypt,
+ .cia_decrypt = xtea_decrypt } }
+};
+
+static int __init init(void)
+{
+ int ret = 0;
+
+ ret = crypto_register_alg(&tea_alg);
+ if (ret < 0)
+ goto out;
+
+ ret = crypto_register_alg(&xtea_alg);
+ if (ret < 0) {
+ crypto_unregister_alg(&tea_alg);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+static void __exit fini(void)
+{
+ crypto_unregister_alg(&tea_alg);
+ crypto_unregister_alg(&xtea_alg);
+}
+
+MODULE_ALIAS("xtea");
+
+module_init(init);
+module_exit(fini);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("TEA & XTEA Cryptographic Algorithms");
acpi_set_register(ACPI_BITREG_RT_CLOCK_ENABLE, 1, ACPI_MTX_LOCK);
- file->f_pos += count;
+ *ppos += count;
result = 0;
end:
#define IF_IADBG_SUNI_STAT 0x02000000 // suni statistics
#define IF_IADBG_RESET 0x04000000
-extern unsigned int IADebugFlag;
-
#define IF_IADBG(f) if (IADebugFlag & (f))
#ifdef CONFIG_ATM_IA_DEBUG /* Debug build */
static void fd_deselect( void );
static void fd_motor_off_timer( unsigned long dummy );
static void check_change( unsigned long dummy );
-static __inline__ void set_head_settle_flag( void );
-static __inline__ int get_head_settle_flag( void );
static irqreturn_t floppy_irq (int irq, void *dummy, struct pt_regs *fp);
static void fd_error( void );
static int do_format(int drive, int type, struct atari_format_descr *desc);
static void fd_times_out( unsigned long dummy );
static void finish_fdc( void );
static void finish_fdc_done( int dummy );
-static __inline__ void copy_buffer( void *from, void *to);
static void setup_req_params( int drive );
static void redo_fd_request( void);
static int fd_ioctl( struct inode *inode, struct file *filp, unsigned int
static struct timer_list fd_timer =
TIMER_INITIALIZER(check_change, 0, 0);
-static inline void
-start_motor_off_timer(void)
+static inline void start_motor_off_timer(void)
{
mod_timer(&motor_off_timer, jiffies + FD_MOTOR_OFF_DELAY);
MotorOffTrys = 0;
}
-static inline void
-start_check_change_timer( void )
+static inline void start_check_change_timer( void )
{
mod_timer(&fd_timer, jiffies + CHECK_CHANGE_DELAY);
}
-static inline void
-start_timeout(void)
+static inline void start_timeout(void)
{
mod_timer(&timeout_timer, jiffies + FLOPPY_TIMEOUT);
}
-static inline void
-stop_timeout(void)
+static inline void stop_timeout(void)
{
del_timer(&timeout_timer);
}
* seek operation, because we don't use seeks with verify.
*/
-static __inline__ void set_head_settle_flag( void )
+static inline void set_head_settle_flag(void)
{
HeadSettleFlag = FDCCMDADD_E;
}
-static __inline__ int get_head_settle_flag( void )
+static inline int get_head_settle_flag(void)
{
int tmp = HeadSettleFlag;
HeadSettleFlag = 0;
return( tmp );
}
+static inline void copy_buffer(void *from, void *to)
+{
+ ulong *p1 = (ulong *)from, *p2 = (ulong *)to;
+ int cnt;
+
+ for (cnt = 512/4; cnt; cnt--)
+ *p2++ = *p1++;
+}
+
return 0;
}
-static __inline__ void copy_buffer(void *from, void *to)
-{
- ulong *p1 = (ulong *)from, *p2 = (ulong *)to;
- int cnt;
-
- for( cnt = 512/4; cnt; cnt-- )
- *p2++ = *p1++;
-}
-
/* This sets up the global variables describing the current request. */
static int sock_xmit(struct socket *sock, int send, void *buf, int size,
int msg_flags)
{
- mm_segment_t oldfs;
int result;
struct msghdr msg;
- struct iovec iov;
+ struct kvec iov;
unsigned long flags;
sigset_t oldset;
- oldfs = get_fs();
- set_fs(get_ds());
/* Allow interception of SIGKILL only
* Don't allow other signals to interrupt the transmission */
spin_lock_irqsave(¤t->sighand->siglock, flags);
iov.iov_len = size;
msg.msg_name = NULL;
msg.msg_namelen = 0;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_namelen = 0;
msg.msg_flags = msg_flags | MSG_NOSIGNAL;
if (send)
- result = sock_sendmsg(sock, &msg, size);
+ result = kernel_sendmsg(sock, &msg, &iov, 1, size);
else
- result = sock_recvmsg(sock, &msg, size, 0);
+ result = kernel_recvmsg(sock, &msg, &iov, 1, size, 0);
if (signal_pending(current)) {
siginfo_t info;
recalc_sigpending();
spin_unlock_irqrestore(¤t->sighand->siglock, flags);
- set_fs(oldfs);
return result;
}
-#define PPCSTRUCT(pi) ((PPC *)(pi->private))
+#define PPCSTRUCT(pi) ((Interface *)(pi->private))
/****************************************************************/
/*
static int bpck6_init_proto(PIA *pi)
{
- PPC *p = kmalloc(sizeof(PPC), GFP_KERNEL);
+ Interface *p = kmalloc(sizeof(Interface), GFP_KERNEL);
if (p) {
- memset(p, 0, sizeof(PPC));
+ memset(p, 0, sizeof(Interface));
pi->private = (unsigned long)p;
return 0;
}
u8 org_data; // original LPT data port contents
u8 org_ctrl; // original LPT control port contents
u8 cur_ctrl; // current control port contents
-} PPC;
+} Interface;
//***************************************************************************
//***************************************************************************
-static int ppc6_select(PPC *ppc);
-static void ppc6_deselect(PPC *ppc);
-static void ppc6_send_cmd(PPC *ppc, u8 cmd);
-static void ppc6_wr_data_byte(PPC *ppc, u8 data);
-static u8 ppc6_rd_data_byte(PPC *ppc);
-static u8 ppc6_rd_port(PPC *ppc, u8 port);
-static void ppc6_wr_port(PPC *ppc, u8 port, u8 data);
-static void ppc6_rd_data_blk(PPC *ppc, u8 *data, long count);
-static void ppc6_wait_for_fifo(PPC *ppc);
-static void ppc6_wr_data_blk(PPC *ppc, u8 *data, long count);
-static void ppc6_rd_port16_blk(PPC *ppc, u8 port, u8 *data, long length);
-static void ppc6_wr_port16_blk(PPC *ppc, u8 port, u8 *data, long length);
-static void ppc6_wr_extout(PPC *ppc, u8 regdata);
-static int ppc6_open(PPC *ppc);
-static void ppc6_close(PPC *ppc);
+static int ppc6_select(Interface *ppc);
+static void ppc6_deselect(Interface *ppc);
+static void ppc6_send_cmd(Interface *ppc, u8 cmd);
+static void ppc6_wr_data_byte(Interface *ppc, u8 data);
+static u8 ppc6_rd_data_byte(Interface *ppc);
+static u8 ppc6_rd_port(Interface *ppc, u8 port);
+static void ppc6_wr_port(Interface *ppc, u8 port, u8 data);
+static void ppc6_rd_data_blk(Interface *ppc, u8 *data, long count);
+static void ppc6_wait_for_fifo(Interface *ppc);
+static void ppc6_wr_data_blk(Interface *ppc, u8 *data, long count);
+static void ppc6_rd_port16_blk(Interface *ppc, u8 port, u8 *data, long length);
+static void ppc6_wr_port16_blk(Interface *ppc, u8 port, u8 *data, long length);
+static void ppc6_wr_extout(Interface *ppc, u8 regdata);
+static int ppc6_open(Interface *ppc);
+static void ppc6_close(Interface *ppc);
//***************************************************************************
-static int ppc6_select(PPC *ppc)
+static int ppc6_select(Interface *ppc)
{
u8 i, j, k;
//***************************************************************************
-static void ppc6_deselect(PPC *ppc)
+static void ppc6_deselect(Interface *ppc)
{
if (ppc->mode & 4) // EPP
ppc->cur_ctrl |= port_init;
//***************************************************************************
-static void ppc6_send_cmd(PPC *ppc, u8 cmd)
+static void ppc6_send_cmd(Interface *ppc, u8 cmd)
{
switch(ppc->mode)
{
//***************************************************************************
-static void ppc6_wr_data_byte(PPC *ppc, u8 data)
+static void ppc6_wr_data_byte(Interface *ppc, u8 data)
{
switch(ppc->mode)
{
//***************************************************************************
-static u8 ppc6_rd_data_byte(PPC *ppc)
+static u8 ppc6_rd_data_byte(Interface *ppc)
{
u8 data = 0;
//***************************************************************************
-static u8 ppc6_rd_port(PPC *ppc, u8 port)
+static u8 ppc6_rd_port(Interface *ppc, u8 port)
{
ppc6_send_cmd(ppc,(u8)(port | ACCESS_PORT | ACCESS_READ));
//***************************************************************************
-static void ppc6_wr_port(PPC *ppc, u8 port, u8 data)
+static void ppc6_wr_port(Interface *ppc, u8 port, u8 data)
{
ppc6_send_cmd(ppc,(u8)(port | ACCESS_PORT | ACCESS_WRITE));
//***************************************************************************
-static void ppc6_rd_data_blk(PPC *ppc, u8 *data, long count)
+static void ppc6_rd_data_blk(Interface *ppc, u8 *data, long count)
{
switch(ppc->mode)
{
//***************************************************************************
-static void ppc6_wait_for_fifo(PPC *ppc)
+static void ppc6_wait_for_fifo(Interface *ppc)
{
int i;
//***************************************************************************
-static void ppc6_wr_data_blk(PPC *ppc, u8 *data, long count)
+static void ppc6_wr_data_blk(Interface *ppc, u8 *data, long count)
{
switch(ppc->mode)
{
//***************************************************************************
-static void ppc6_rd_port16_blk(PPC *ppc, u8 port, u8 *data, long length)
+static void ppc6_rd_port16_blk(Interface *ppc, u8 port, u8 *data, long length)
{
length = length << 1;
//***************************************************************************
-static void ppc6_wr_port16_blk(PPC *ppc, u8 port, u8 *data, long length)
+static void ppc6_wr_port16_blk(Interface *ppc, u8 port, u8 *data, long length)
{
length = length << 1;
//***************************************************************************
-static void ppc6_wr_extout(PPC *ppc, u8 regdata)
+static void ppc6_wr_extout(Interface *ppc, u8 regdata)
{
ppc6_send_cmd(ppc,(REG_VERSION | ACCESS_REG | ACCESS_WRITE));
//***************************************************************************
-static int ppc6_open(PPC *ppc)
+static int ppc6_open(Interface *ppc)
{
int ret;
//***************************************************************************
-static void ppc6_close(PPC *ppc)
+static void ppc6_close(Interface *ppc)
{
ppc6_deselect(ppc);
}
--- /dev/null
+/*
+ * sx8.c: Driver for Promise SATA SX8 looks-like-I2O hardware
+ *
+ * Copyright 2004 Red Hat, Inc.
+ *
+ * Author/maintainer: Jeff Garzik <jgarzik@pobox.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/blkdev.h>
+#include <linux/sched.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/interrupt.h>
+#include <linux/compiler.h>
+#include <linux/workqueue.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <linux/hdreg.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+
+MODULE_AUTHOR("Jeff Garzik");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Promise SATA SX8 block driver");
+
+#if 0
+#define CARM_DEBUG
+#define CARM_VERBOSE_DEBUG
+#else
+#undef CARM_DEBUG
+#undef CARM_VERBOSE_DEBUG
+#endif
+#undef CARM_NDEBUG
+
+#define DRV_NAME "sx8"
+#define DRV_VERSION "0.8"
+#define PFX DRV_NAME ": "
+
+#define NEXT_RESP(idx) ((idx + 1) % RMSG_Q_LEN)
+
+/* 0xf is just arbitrary, non-zero noise; this is sorta like poisoning */
+#define TAG_ENCODE(tag) (((tag) << 16) | 0xf)
+#define TAG_DECODE(tag) (((tag) >> 16) & 0x1f)
+#define TAG_VALID(tag) ((((tag) & 0xf) == 0xf) && (TAG_DECODE(tag) < 32))
+
+/* note: prints function name for you */
+#ifdef CARM_DEBUG
+#define DPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args)
+#ifdef CARM_VERBOSE_DEBUG
+#define VPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args)
+#else
+#define VPRINTK(fmt, args...)
+#endif /* CARM_VERBOSE_DEBUG */
+#else
+#define DPRINTK(fmt, args...)
+#define VPRINTK(fmt, args...)
+#endif /* CARM_DEBUG */
+
+#ifdef CARM_NDEBUG
+#define assert(expr)
+#else
+#define assert(expr) \
+ if(unlikely(!(expr))) { \
+ printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \
+ #expr,__FILE__,__FUNCTION__,__LINE__); \
+ }
+#endif
+
+/* defines only for the constants which don't work well as enums */
+struct carm_host;
+
+enum {
+ /* adapter-wide limits */
+ CARM_MAX_PORTS = 8,
+ CARM_SHM_SIZE = (4096 << 7),
+ CARM_MINORS_PER_MAJOR = 256 / CARM_MAX_PORTS,
+ CARM_MAX_WAIT_Q = CARM_MAX_PORTS + 1,
+
+ /* command message queue limits */
+ CARM_MAX_REQ = 64, /* max command msgs per host */
+ CARM_MAX_Q = 1, /* one command at a time */
+ CARM_MSG_LOW_WATER = (CARM_MAX_REQ / 4), /* refill mark */
+
+ /* S/G limits, host-wide and per-request */
+ CARM_MAX_REQ_SG = 32, /* max s/g entries per request */
+ CARM_SG_BOUNDARY = 0xffffUL, /* s/g segment boundary */
+ CARM_MAX_HOST_SG = 600, /* max s/g entries per host */
+ CARM_SG_LOW_WATER = (CARM_MAX_HOST_SG / 4), /* re-fill mark */
+
+ /* hardware registers */
+ CARM_IHQP = 0x1c,
+ CARM_INT_STAT = 0x10, /* interrupt status */
+ CARM_INT_MASK = 0x14, /* interrupt mask */
+ CARM_HMUC = 0x18, /* host message unit control */
+ RBUF_ADDR_LO = 0x20, /* response msg DMA buf low 32 bits */
+ RBUF_ADDR_HI = 0x24, /* response msg DMA buf high 32 bits */
+ RBUF_BYTE_SZ = 0x28,
+ CARM_RESP_IDX = 0x2c,
+ CARM_CMS0 = 0x30, /* command message size reg 0 */
+ CARM_LMUC = 0x48,
+ CARM_HMPHA = 0x6c,
+ CARM_INITC = 0xb5,
+
+ /* bits in CARM_INT_{STAT,MASK} */
+ INT_RESERVED = 0xfffffff0,
+ INT_WATCHDOG = (1 << 3), /* watchdog timer */
+ INT_Q_OVERFLOW = (1 << 2), /* cmd msg q overflow */
+ INT_Q_AVAILABLE = (1 << 1), /* cmd msg q has free space */
+ INT_RESPONSE = (1 << 0), /* response msg available */
+ INT_ACK_MASK = INT_WATCHDOG | INT_Q_OVERFLOW,
+ INT_DEF_MASK = INT_RESERVED | INT_Q_OVERFLOW |
+ INT_RESPONSE,
+
+ /* command messages, and related register bits */
+ CARM_HAVE_RESP = 0x01,
+ CARM_MSG_READ = 1,
+ CARM_MSG_WRITE = 2,
+ CARM_MSG_VERIFY = 3,
+ CARM_MSG_GET_CAPACITY = 4,
+ CARM_MSG_FLUSH = 5,
+ CARM_MSG_IOCTL = 6,
+ CARM_MSG_ARRAY = 8,
+ CARM_MSG_MISC = 9,
+ CARM_CME = (1 << 2),
+ CARM_RME = (1 << 1),
+ CARM_WZBC = (1 << 0),
+ CARM_RMI = (1 << 0),
+ CARM_Q_FULL = (1 << 3),
+ CARM_MSG_SIZE = 288,
+ CARM_Q_LEN = 48,
+
+ /* CARM_MSG_IOCTL messages */
+ CARM_IOC_SCAN_CHAN = 5, /* scan channels for devices */
+ CARM_IOC_GET_TCQ = 13, /* get tcq/ncq depth */
+ CARM_IOC_SET_TCQ = 14, /* set tcq/ncq depth */
+
+ IOC_SCAN_CHAN_NODEV = 0x1f,
+ IOC_SCAN_CHAN_OFFSET = 0x40,
+
+ /* CARM_MSG_ARRAY messages */
+ CARM_ARRAY_INFO = 0,
+
+ ARRAY_NO_EXIST = (1 << 31),
+
+ /* response messages */
+ RMSG_SZ = 8, /* sizeof(struct carm_response) */
+ RMSG_Q_LEN = 48, /* resp. msg list length */
+ RMSG_OK = 1, /* bit indicating msg was successful */
+ /* length of entire resp. msg buffer */
+ RBUF_LEN = RMSG_SZ * RMSG_Q_LEN,
+
+ PDC_SHM_SIZE = (4096 << 7), /* length of entire h/w buffer */
+
+ /* CARM_MSG_MISC messages */
+ MISC_GET_FW_VER = 2,
+ MISC_ALLOC_MEM = 3,
+ MISC_SET_TIME = 5,
+
+ /* MISC_GET_FW_VER feature bits */
+ FW_VER_4PORT = (1 << 2), /* 1=4 ports, 0=8 ports */
+ FW_VER_NON_RAID = (1 << 1), /* 1=non-RAID firmware, 0=RAID */
+ FW_VER_ZCR = (1 << 0), /* zero channel RAID (whatever that is) */
+
+ /* carm_host flags */
+ FL_NON_RAID = FW_VER_NON_RAID,
+ FL_4PORT = FW_VER_4PORT,
+ FL_FW_VER_MASK = (FW_VER_NON_RAID | FW_VER_4PORT),
+ FL_DAC = (1 << 16),
+ FL_DYN_MAJOR = (1 << 17),
+};
+
+enum scatter_gather_types {
+ SGT_32BIT = 0,
+ SGT_64BIT = 1,
+};
+
+enum host_states {
+ HST_INVALID, /* invalid state; never used */
+ HST_ALLOC_BUF, /* setting up master SHM area */
+ HST_ERROR, /* we never leave here */
+ HST_PORT_SCAN, /* start dev scan */
+ HST_DEV_SCAN_START, /* start per-device probe */
+ HST_DEV_SCAN, /* continue per-device probe */
+ HST_DEV_ACTIVATE, /* activate devices we found */
+ HST_PROBE_FINISHED, /* probe is complete */
+ HST_PROBE_START, /* initiate probe */
+ HST_SYNC_TIME, /* tell firmware what time it is */
+ HST_GET_FW_VER, /* get firmware version, adapter port cnt */
+};
+
+#ifdef CARM_DEBUG
+static const char *state_name[] = {
+ "HST_INVALID",
+ "HST_ALLOC_BUF",
+ "HST_ERROR",
+ "HST_PORT_SCAN",
+ "HST_DEV_SCAN_START",
+ "HST_DEV_SCAN",
+ "HST_DEV_ACTIVATE",
+ "HST_PROBE_FINISHED",
+ "HST_PROBE_START",
+ "HST_SYNC_TIME",
+ "HST_GET_FW_VER",
+};
+#endif
+
+struct carm_port {
+ unsigned int port_no;
+ unsigned int n_queued;
+ struct gendisk *disk;
+ struct carm_host *host;
+
+ /* attached device characteristics */
+ u64 capacity;
+ char name[41];
+ u16 dev_geom_head;
+ u16 dev_geom_sect;
+ u16 dev_geom_cyl;
+};
+
+struct carm_request {
+ unsigned int tag;
+ int n_elem;
+ unsigned int msg_type;
+ unsigned int msg_subtype;
+ unsigned int msg_bucket;
+ struct request *rq;
+ struct carm_port *port;
+ struct scatterlist sg[CARM_MAX_REQ_SG];
+};
+
+struct carm_host {
+ unsigned long flags;
+ void *mmio;
+ void *shm;
+ dma_addr_t shm_dma;
+
+ int major;
+ int id;
+ char name[32];
+
+ spinlock_t lock;
+ struct pci_dev *pdev;
+ unsigned int state;
+ u32 fw_ver;
+
+ request_queue_t *oob_q;
+ unsigned int n_oob;
+
+ unsigned int hw_sg_used;
+
+ unsigned int resp_idx;
+
+ unsigned int wait_q_prod;
+ unsigned int wait_q_cons;
+ request_queue_t *wait_q[CARM_MAX_WAIT_Q];
+
+ unsigned int n_msgs;
+ u64 msg_alloc;
+ struct carm_request req[CARM_MAX_REQ];
+ void *msg_base;
+ dma_addr_t msg_dma;
+
+ int cur_scan_dev;
+ unsigned long dev_active;
+ unsigned long dev_present;
+ struct carm_port port[CARM_MAX_PORTS];
+
+ struct work_struct fsm_task;
+
+ struct semaphore probe_sem;
+};
+
+struct carm_response {
+ u32 ret_handle;
+ u32 status;
+} __attribute__((packed));
+
+struct carm_msg_sg {
+ u32 start;
+ u32 len;
+} __attribute__((packed));
+
+struct carm_msg_rw {
+ u8 type;
+ u8 id;
+ u8 sg_count;
+ u8 sg_type;
+ u32 handle;
+ u32 lba;
+ u16 lba_count;
+ u16 lba_high;
+ struct carm_msg_sg sg[32];
+} __attribute__((packed));
+
+struct carm_msg_allocbuf {
+ u8 type;
+ u8 subtype;
+ u8 n_sg;
+ u8 sg_type;
+ u32 handle;
+ u32 addr;
+ u32 len;
+ u32 evt_pool;
+ u32 n_evt;
+ u32 rbuf_pool;
+ u32 n_rbuf;
+ u32 msg_pool;
+ u32 n_msg;
+ struct carm_msg_sg sg[8];
+} __attribute__((packed));
+
+struct carm_msg_ioctl {
+ u8 type;
+ u8 subtype;
+ u8 array_id;
+ u8 reserved1;
+ u32 handle;
+ u32 data_addr;
+ u32 reserved2;
+} __attribute__((packed));
+
+struct carm_msg_sync_time {
+ u8 type;
+ u8 subtype;
+ u16 reserved1;
+ u32 handle;
+ u32 reserved2;
+ u32 timestamp;
+} __attribute__((packed));
+
+struct carm_msg_get_fw_ver {
+ u8 type;
+ u8 subtype;
+ u16 reserved1;
+ u32 handle;
+ u32 data_addr;
+ u32 reserved2;
+} __attribute__((packed));
+
+struct carm_fw_ver {
+ u32 version;
+ u8 features;
+ u8 reserved1;
+ u16 reserved2;
+} __attribute__((packed));
+
+struct carm_array_info {
+ u32 size;
+
+ u16 size_hi;
+ u16 stripe_size;
+
+ u32 mode;
+
+ u16 stripe_blk_sz;
+ u16 reserved1;
+
+ u16 cyl;
+ u16 head;
+
+ u16 sect;
+ u8 array_id;
+ u8 reserved2;
+
+ char name[40];
+
+ u32 array_status;
+
+ /* device list continues beyond this point? */
+} __attribute__((packed));
+
+static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
+static void carm_remove_one (struct pci_dev *pdev);
+static int carm_bdev_ioctl(struct inode *ino, struct file *fil,
+ unsigned int cmd, unsigned long arg);
+
+static struct pci_device_id carm_pci_tbl[] = {
+ { PCI_VENDOR_ID_PROMISE, 0x8000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
+ { PCI_VENDOR_ID_PROMISE, 0x8002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, },
+ { } /* terminate list */
+};
+MODULE_DEVICE_TABLE(pci, carm_pci_tbl);
+
+static struct pci_driver carm_driver = {
+ .name = DRV_NAME,
+ .id_table = carm_pci_tbl,
+ .probe = carm_init_one,
+ .remove = carm_remove_one,
+};
+
+static struct block_device_operations carm_bd_ops = {
+ .owner = THIS_MODULE,
+ .ioctl = carm_bdev_ioctl,
+};
+
+static unsigned int carm_host_id;
+static unsigned long carm_major_alloc;
+
+
+
+static int carm_bdev_ioctl(struct inode *ino, struct file *fil,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *usermem = (void __user *) arg;
+ struct carm_port *port = ino->i_bdev->bd_disk->private_data;
+ struct hd_geometry geom;
+
+ switch (cmd) {
+ case HDIO_GETGEO:
+ if (!usermem)
+ return -EINVAL;
+
+ geom.heads = (u8) port->dev_geom_head;
+ geom.sectors = (u8) port->dev_geom_sect;
+ geom.cylinders = port->dev_geom_cyl;
+ geom.start = get_start_sect(ino->i_bdev);
+
+ if (copy_to_user(usermem, &geom, sizeof(geom)))
+ return -EFAULT;
+ return 0;
+
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static const u32 msg_sizes[] = { 32, 64, 128, CARM_MSG_SIZE };
+
+static inline int carm_lookup_bucket(u32 msg_size)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(msg_sizes); i++)
+ if (msg_size <= msg_sizes[i])
+ return i;
+
+ return -ENOENT;
+}
+
+static void carm_init_buckets(void *mmio)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(msg_sizes); i++)
+ writel(msg_sizes[i], mmio + CARM_CMS0 + (4 * i));
+}
+
+static inline void *carm_ref_msg(struct carm_host *host,
+ unsigned int msg_idx)
+{
+ return host->msg_base + (msg_idx * CARM_MSG_SIZE);
+}
+
+static inline dma_addr_t carm_ref_msg_dma(struct carm_host *host,
+ unsigned int msg_idx)
+{
+ return host->msg_dma + (msg_idx * CARM_MSG_SIZE);
+}
+
+static int carm_send_msg(struct carm_host *host,
+ struct carm_request *crq)
+{
+ void *mmio = host->mmio;
+ u32 msg = (u32) carm_ref_msg_dma(host, crq->tag);
+ u32 cm_bucket = crq->msg_bucket;
+ u32 tmp;
+ int rc = 0;
+
+ VPRINTK("ENTER\n");
+
+ tmp = readl(mmio + CARM_HMUC);
+ if (tmp & CARM_Q_FULL) {
+#if 0
+ tmp = readl(mmio + CARM_INT_MASK);
+ tmp |= INT_Q_AVAILABLE;
+ writel(tmp, mmio + CARM_INT_MASK);
+ readl(mmio + CARM_INT_MASK); /* flush */
+#endif
+ DPRINTK("host msg queue full\n");
+ rc = -EBUSY;
+ } else {
+ writel(msg | (cm_bucket << 1), mmio + CARM_IHQP);
+ readl(mmio + CARM_IHQP); /* flush */
+ }
+
+ return rc;
+}
+
+static struct carm_request *carm_get_request(struct carm_host *host)
+{
+ unsigned int i;
+
+ /* obey global hardware limit on S/G entries */
+ if (host->hw_sg_used >= (CARM_MAX_HOST_SG - CARM_MAX_REQ_SG))
+ return NULL;
+
+ for (i = 0; i < CARM_MAX_Q; i++)
+ if ((host->msg_alloc & (1ULL << i)) == 0) {
+ struct carm_request *crq = &host->req[i];
+ crq->port = NULL;
+ crq->n_elem = 0;
+
+ host->msg_alloc |= (1ULL << i);
+ host->n_msgs++;
+
+ assert(host->n_msgs <= CARM_MAX_REQ);
+ return crq;
+ }
+
+ DPRINTK("no request available, returning NULL\n");
+ return NULL;
+}
+
+static int carm_put_request(struct carm_host *host, struct carm_request *crq)
+{
+ assert(crq->tag < CARM_MAX_Q);
+
+ if (unlikely((host->msg_alloc & (1ULL << crq->tag)) == 0))
+ return -EINVAL; /* tried to clear a tag that was not active */
+
+ assert(host->hw_sg_used >= crq->n_elem);
+
+ host->msg_alloc &= ~(1ULL << crq->tag);
+ host->hw_sg_used -= crq->n_elem;
+ host->n_msgs--;
+
+ return 0;
+}
+
+static struct carm_request *carm_get_special(struct carm_host *host)
+{
+ unsigned long flags;
+ struct carm_request *crq = NULL;
+ struct request *rq;
+ int tries = 5000;
+
+ while (tries-- > 0) {
+ spin_lock_irqsave(&host->lock, flags);
+ crq = carm_get_request(host);
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ if (crq)
+ break;
+ msleep(10);
+ }
+
+ if (!crq)
+ return NULL;
+
+ rq = blk_get_request(host->oob_q, WRITE /* bogus */, GFP_KERNEL);
+ if (!rq) {
+ spin_lock_irqsave(&host->lock, flags);
+ carm_put_request(host, crq);
+ spin_unlock_irqrestore(&host->lock, flags);
+ return NULL;
+ }
+
+ crq->rq = rq;
+ return crq;
+}
+
+static int carm_array_info (struct carm_host *host, unsigned int array_idx)
+{
+ struct carm_msg_ioctl *ioc;
+ unsigned int idx;
+ u32 msg_data;
+ dma_addr_t msg_dma;
+ struct carm_request *crq;
+ int rc;
+
+ crq = carm_get_special(host);
+ if (!crq) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+
+ idx = crq->tag;
+
+ ioc = carm_ref_msg(host, idx);
+ msg_dma = carm_ref_msg_dma(host, idx);
+ msg_data = (u32) (msg_dma + sizeof(struct carm_array_info));
+
+ crq->msg_type = CARM_MSG_ARRAY;
+ crq->msg_subtype = CARM_ARRAY_INFO;
+ rc = carm_lookup_bucket(sizeof(struct carm_msg_ioctl) +
+ sizeof(struct carm_array_info));
+ BUG_ON(rc < 0);
+ crq->msg_bucket = (u32) rc;
+
+ memset(ioc, 0, sizeof(*ioc));
+ ioc->type = CARM_MSG_ARRAY;
+ ioc->subtype = CARM_ARRAY_INFO;
+ ioc->array_id = (u8) array_idx;
+ ioc->handle = cpu_to_le32(TAG_ENCODE(idx));
+ ioc->data_addr = cpu_to_le32(msg_data);
+
+ spin_lock_irq(&host->lock);
+ assert(host->state == HST_DEV_SCAN_START ||
+ host->state == HST_DEV_SCAN);
+ spin_unlock_irq(&host->lock);
+
+ DPRINTK("blk_insert_request, tag == %u\n", idx);
+ blk_insert_request(host->oob_q, crq->rq, 1, crq, 0);
+
+ return 0;
+
+err_out:
+ spin_lock_irq(&host->lock);
+ host->state = HST_ERROR;
+ spin_unlock_irq(&host->lock);
+ return rc;
+}
+
+typedef unsigned int (*carm_sspc_t)(struct carm_host *, unsigned int, void *);
+
+static int carm_send_special (struct carm_host *host, carm_sspc_t func)
+{
+ struct carm_request *crq;
+ struct carm_msg_ioctl *ioc;
+ void *mem;
+ unsigned int idx, msg_size;
+ int rc;
+
+ crq = carm_get_special(host);
+ if (!crq)
+ return -ENOMEM;
+
+ idx = crq->tag;
+
+ mem = carm_ref_msg(host, idx);
+
+ msg_size = func(host, idx, mem);
+
+ ioc = mem;
+ crq->msg_type = ioc->type;
+ crq->msg_subtype = ioc->subtype;
+ rc = carm_lookup_bucket(msg_size);
+ BUG_ON(rc < 0);
+ crq->msg_bucket = (u32) rc;
+
+ DPRINTK("blk_insert_request, tag == %u\n", idx);
+ blk_insert_request(host->oob_q, crq->rq, 1, crq, 0);
+
+ return 0;
+}
+
+static unsigned int carm_fill_sync_time(struct carm_host *host,
+ unsigned int idx, void *mem)
+{
+ struct timeval tv;
+ struct carm_msg_sync_time *st = mem;
+
+ do_gettimeofday(&tv);
+
+ memset(st, 0, sizeof(*st));
+ st->type = CARM_MSG_MISC;
+ st->subtype = MISC_SET_TIME;
+ st->handle = cpu_to_le32(TAG_ENCODE(idx));
+ st->timestamp = cpu_to_le32(tv.tv_sec);
+
+ return sizeof(struct carm_msg_sync_time);
+}
+
+static unsigned int carm_fill_alloc_buf(struct carm_host *host,
+ unsigned int idx, void *mem)
+{
+ struct carm_msg_allocbuf *ab = mem;
+
+ memset(ab, 0, sizeof(*ab));
+ ab->type = CARM_MSG_MISC;
+ ab->subtype = MISC_ALLOC_MEM;
+ ab->handle = cpu_to_le32(TAG_ENCODE(idx));
+ ab->n_sg = 1;
+ ab->sg_type = SGT_32BIT;
+ ab->addr = cpu_to_le32(host->shm_dma + (PDC_SHM_SIZE >> 1));
+ ab->len = cpu_to_le32(PDC_SHM_SIZE >> 1);
+ ab->evt_pool = cpu_to_le32(host->shm_dma + (16 * 1024));
+ ab->n_evt = cpu_to_le32(1024);
+ ab->rbuf_pool = cpu_to_le32(host->shm_dma);
+ ab->n_rbuf = cpu_to_le32(RMSG_Q_LEN);
+ ab->msg_pool = cpu_to_le32(host->shm_dma + RBUF_LEN);
+ ab->n_msg = cpu_to_le32(CARM_Q_LEN);
+ ab->sg[0].start = cpu_to_le32(host->shm_dma + (PDC_SHM_SIZE >> 1));
+ ab->sg[0].len = cpu_to_le32(65536);
+
+ return sizeof(struct carm_msg_allocbuf);
+}
+
+static unsigned int carm_fill_scan_channels(struct carm_host *host,
+ unsigned int idx, void *mem)
+{
+ struct carm_msg_ioctl *ioc = mem;
+ u32 msg_data = (u32) (carm_ref_msg_dma(host, idx) +
+ IOC_SCAN_CHAN_OFFSET);
+
+ memset(ioc, 0, sizeof(*ioc));
+ ioc->type = CARM_MSG_IOCTL;
+ ioc->subtype = CARM_IOC_SCAN_CHAN;
+ ioc->handle = cpu_to_le32(TAG_ENCODE(idx));
+ ioc->data_addr = cpu_to_le32(msg_data);
+
+ /* fill output data area with "no device" default values */
+ mem += IOC_SCAN_CHAN_OFFSET;
+ memset(mem, IOC_SCAN_CHAN_NODEV, CARM_MAX_PORTS);
+
+ return IOC_SCAN_CHAN_OFFSET + CARM_MAX_PORTS;
+}
+
+static unsigned int carm_fill_get_fw_ver(struct carm_host *host,
+ unsigned int idx, void *mem)
+{
+ struct carm_msg_get_fw_ver *ioc = mem;
+ u32 msg_data = (u32) (carm_ref_msg_dma(host, idx) + sizeof(*ioc));
+
+ memset(ioc, 0, sizeof(*ioc));
+ ioc->type = CARM_MSG_MISC;
+ ioc->subtype = MISC_GET_FW_VER;
+ ioc->handle = cpu_to_le32(TAG_ENCODE(idx));
+ ioc->data_addr = cpu_to_le32(msg_data);
+
+ return sizeof(struct carm_msg_get_fw_ver) +
+ sizeof(struct carm_fw_ver);
+}
+
+static inline void carm_end_request_queued(struct carm_host *host,
+ struct carm_request *crq,
+ int uptodate)
+{
+ struct request *req = crq->rq;
+ int rc;
+
+ rc = end_that_request_first(req, uptodate, req->hard_nr_sectors);
+ assert(rc == 0);
+
+ end_that_request_last(req);
+
+ rc = carm_put_request(host, crq);
+ assert(rc == 0);
+}
+
+static inline void carm_push_q (struct carm_host *host, request_queue_t *q)
+{
+ unsigned int idx = host->wait_q_prod % CARM_MAX_WAIT_Q;
+
+ blk_stop_queue(q);
+ VPRINTK("STOPPED QUEUE %p\n", q);
+
+ host->wait_q[idx] = q;
+ host->wait_q_prod++;
+ BUG_ON(host->wait_q_prod == host->wait_q_cons); /* overrun */
+}
+
+static inline request_queue_t *carm_pop_q(struct carm_host *host)
+{
+ unsigned int idx;
+
+ if (host->wait_q_prod == host->wait_q_cons)
+ return NULL;
+
+ idx = host->wait_q_cons % CARM_MAX_WAIT_Q;
+ host->wait_q_cons++;
+
+ return host->wait_q[idx];
+}
+
+static inline void carm_round_robin(struct carm_host *host)
+{
+ request_queue_t *q = carm_pop_q(host);
+ if (q) {
+ blk_start_queue(q);
+ VPRINTK("STARTED QUEUE %p\n", q);
+ }
+}
+
+static inline void carm_end_rq(struct carm_host *host, struct carm_request *crq,
+ int is_ok)
+{
+ carm_end_request_queued(host, crq, is_ok);
+ if (CARM_MAX_Q == 1)
+ carm_round_robin(host);
+ else if ((host->n_msgs <= CARM_MSG_LOW_WATER) &&
+ (host->hw_sg_used <= CARM_SG_LOW_WATER)) {
+ carm_round_robin(host);
+ }
+}
+
+static void carm_oob_rq_fn(request_queue_t *q)
+{
+ struct carm_host *host = q->queuedata;
+ struct carm_request *crq;
+ struct request *rq;
+ int rc;
+
+ while (1) {
+ DPRINTK("get req\n");
+ rq = elv_next_request(q);
+ if (!rq)
+ break;
+
+ blkdev_dequeue_request(rq);
+
+ crq = rq->special;
+ assert(crq != NULL);
+ assert(crq->rq == rq);
+
+ crq->n_elem = 0;
+
+ DPRINTK("send req\n");
+ rc = carm_send_msg(host, crq);
+ if (rc) {
+ blk_requeue_request(q, rq);
+ carm_push_q(host, q);
+ return; /* call us again later, eventually */
+ }
+ }
+}
+
+static void carm_rq_fn(request_queue_t *q)
+{
+ struct carm_port *port = q->queuedata;
+ struct carm_host *host = port->host;
+ struct carm_msg_rw *msg;
+ struct carm_request *crq;
+ struct request *rq;
+ struct scatterlist *sg;
+ int writing = 0, pci_dir, i, n_elem, rc;
+ u32 tmp;
+ unsigned int msg_size;
+
+queue_one_request:
+ VPRINTK("get req\n");
+ rq = elv_next_request(q);
+ if (!rq)
+ return;
+
+ crq = carm_get_request(host);
+ if (!crq) {
+ carm_push_q(host, q);
+ return; /* call us again later, eventually */
+ }
+ crq->rq = rq;
+
+ blkdev_dequeue_request(rq);
+
+ if (rq_data_dir(rq) == WRITE) {
+ writing = 1;
+ pci_dir = PCI_DMA_TODEVICE;
+ } else {
+ pci_dir = PCI_DMA_FROMDEVICE;
+ }
+
+ /* get scatterlist from block layer */
+ sg = &crq->sg[0];
+ n_elem = blk_rq_map_sg(q, rq, sg);
+ if (n_elem <= 0) {
+ carm_end_rq(host, crq, 0);
+ return; /* request with no s/g entries? */
+ }
+
+ /* map scatterlist to PCI bus addresses */
+ n_elem = pci_map_sg(host->pdev, sg, n_elem, pci_dir);
+ if (n_elem <= 0) {
+ carm_end_rq(host, crq, 0);
+ return; /* request with no s/g entries? */
+ }
+ crq->n_elem = n_elem;
+ crq->port = port;
+ host->hw_sg_used += n_elem;
+
+ /*
+ * build read/write message
+ */
+
+ VPRINTK("build msg\n");
+ msg = (struct carm_msg_rw *) carm_ref_msg(host, crq->tag);
+
+ if (writing) {
+ msg->type = CARM_MSG_WRITE;
+ crq->msg_type = CARM_MSG_WRITE;
+ } else {
+ msg->type = CARM_MSG_READ;
+ crq->msg_type = CARM_MSG_READ;
+ }
+
+ msg->id = port->port_no;
+ msg->sg_count = n_elem;
+ msg->sg_type = SGT_32BIT;
+ msg->handle = cpu_to_le32(TAG_ENCODE(crq->tag));
+ msg->lba = cpu_to_le32(rq->sector & 0xffffffff);
+ tmp = (rq->sector >> 16) >> 16;
+ msg->lba_high = cpu_to_le16( (u16) tmp );
+ msg->lba_count = cpu_to_le16(rq->nr_sectors);
+
+ msg_size = sizeof(struct carm_msg_rw) - sizeof(msg->sg);
+ for (i = 0; i < n_elem; i++) {
+ struct carm_msg_sg *carm_sg = &msg->sg[i];
+ carm_sg->start = cpu_to_le32(sg_dma_address(&crq->sg[i]));
+ carm_sg->len = cpu_to_le32(sg_dma_len(&crq->sg[i]));
+ msg_size += sizeof(struct carm_msg_sg);
+ }
+
+ rc = carm_lookup_bucket(msg_size);
+ BUG_ON(rc < 0);
+ crq->msg_bucket = (u32) rc;
+
+ /*
+ * queue read/write message to hardware
+ */
+
+ VPRINTK("send msg, tag == %u\n", crq->tag);
+ rc = carm_send_msg(host, crq);
+ if (rc) {
+ carm_put_request(host, crq);
+ blk_requeue_request(q, rq);
+ carm_push_q(host, q);
+ return; /* call us again later, eventually */
+ }
+
+ goto queue_one_request;
+}
+
+static void carm_handle_array_info(struct carm_host *host,
+ struct carm_request *crq, u8 *mem,
+ int is_ok)
+{
+ struct carm_port *port;
+ u8 *msg_data = mem + sizeof(struct carm_array_info);
+ struct carm_array_info *desc = (struct carm_array_info *) msg_data;
+ u64 lo, hi;
+ int cur_port;
+ size_t slen;
+
+ DPRINTK("ENTER\n");
+
+ carm_end_rq(host, crq, is_ok);
+
+ if (!is_ok)
+ goto out;
+ if (le32_to_cpu(desc->array_status) & ARRAY_NO_EXIST)
+ goto out;
+
+ cur_port = host->cur_scan_dev;
+
+ /* should never occur */
+ if ((cur_port < 0) || (cur_port >= CARM_MAX_PORTS)) {
+ printk(KERN_ERR PFX "BUG: cur_scan_dev==%d, array_id==%d\n",
+ cur_port, (int) desc->array_id);
+ goto out;
+ }
+
+ port = &host->port[cur_port];
+
+ lo = (u64) le32_to_cpu(desc->size);
+ hi = (u64) le32_to_cpu(desc->size_hi);
+
+ port->capacity = lo | (hi << 32);
+ port->dev_geom_head = le16_to_cpu(desc->head);
+ port->dev_geom_sect = le16_to_cpu(desc->sect);
+ port->dev_geom_cyl = le16_to_cpu(desc->cyl);
+
+ host->dev_active |= (1 << cur_port);
+
+ strncpy(port->name, desc->name, sizeof(port->name));
+ port->name[sizeof(port->name) - 1] = 0;
+ slen = strlen(port->name);
+ while (slen && (port->name[slen - 1] == ' ')) {
+ port->name[slen - 1] = 0;
+ slen--;
+ }
+
+ printk(KERN_INFO DRV_NAME "(%s): port %u device %Lu sectors\n",
+ pci_name(host->pdev), port->port_no,
+ (unsigned long long) port->capacity);
+ printk(KERN_INFO DRV_NAME "(%s): port %u device \"%s\"\n",
+ pci_name(host->pdev), port->port_no, port->name);
+
+out:
+ assert(host->state == HST_DEV_SCAN);
+ schedule_work(&host->fsm_task);
+}
+
+static void carm_handle_scan_chan(struct carm_host *host,
+ struct carm_request *crq, u8 *mem,
+ int is_ok)
+{
+ u8 *msg_data = mem + IOC_SCAN_CHAN_OFFSET;
+ unsigned int i, dev_count = 0;
+ int new_state = HST_DEV_SCAN_START;
+
+ DPRINTK("ENTER\n");
+
+ carm_end_rq(host, crq, is_ok);
+
+ if (!is_ok) {
+ new_state = HST_ERROR;
+ goto out;
+ }
+
+ /* TODO: scan and support non-disk devices */
+ for (i = 0; i < 8; i++)
+ if (msg_data[i] == 0) { /* direct-access device (disk) */
+ host->dev_present |= (1 << i);
+ dev_count++;
+ }
+
+ printk(KERN_INFO DRV_NAME "(%s): found %u interesting devices\n",
+ pci_name(host->pdev), dev_count);
+
+out:
+ assert(host->state == HST_PORT_SCAN);
+ host->state = new_state;
+ schedule_work(&host->fsm_task);
+}
+
+static void carm_handle_generic(struct carm_host *host,
+ struct carm_request *crq, int is_ok,
+ int cur_state, int next_state)
+{
+ DPRINTK("ENTER\n");
+
+ carm_end_rq(host, crq, is_ok);
+
+ assert(host->state == cur_state);
+ if (is_ok)
+ host->state = next_state;
+ else
+ host->state = HST_ERROR;
+ schedule_work(&host->fsm_task);
+}
+
+static inline void carm_handle_rw(struct carm_host *host,
+ struct carm_request *crq, int is_ok)
+{
+ int pci_dir;
+
+ VPRINTK("ENTER\n");
+
+ if (rq_data_dir(crq->rq) == WRITE)
+ pci_dir = PCI_DMA_TODEVICE;
+ else
+ pci_dir = PCI_DMA_FROMDEVICE;
+
+ pci_unmap_sg(host->pdev, &crq->sg[0], crq->n_elem, pci_dir);
+
+ carm_end_rq(host, crq, is_ok);
+}
+
+static inline void carm_handle_resp(struct carm_host *host,
+ u32 ret_handle_le, u32 status)
+{
+ u32 handle = le32_to_cpu(ret_handle_le);
+ unsigned int msg_idx;
+ struct carm_request *crq;
+ int is_ok = (status == RMSG_OK);
+ u8 *mem;
+
+ VPRINTK("ENTER, handle == 0x%x\n", handle);
+
+ if (unlikely(!TAG_VALID(handle))) {
+ printk(KERN_ERR DRV_NAME "(%s): BUG: invalid tag 0x%x\n",
+ pci_name(host->pdev), handle);
+ return;
+ }
+
+ msg_idx = TAG_DECODE(handle);
+ VPRINTK("tag == %u\n", msg_idx);
+
+ crq = &host->req[msg_idx];
+
+ /* fast path */
+ if (likely(crq->msg_type == CARM_MSG_READ ||
+ crq->msg_type == CARM_MSG_WRITE)) {
+ carm_handle_rw(host, crq, is_ok);
+ return;
+ }
+
+ mem = carm_ref_msg(host, msg_idx);
+
+ switch (crq->msg_type) {
+ case CARM_MSG_IOCTL: {
+ switch (crq->msg_subtype) {
+ case CARM_IOC_SCAN_CHAN:
+ carm_handle_scan_chan(host, crq, mem, is_ok);
+ break;
+ default:
+ /* unknown / invalid response */
+ goto err_out;
+ }
+ break;
+ }
+
+ case CARM_MSG_MISC: {
+ switch (crq->msg_subtype) {
+ case MISC_ALLOC_MEM:
+ carm_handle_generic(host, crq, is_ok,
+ HST_ALLOC_BUF, HST_SYNC_TIME);
+ break;
+ case MISC_SET_TIME:
+ carm_handle_generic(host, crq, is_ok,
+ HST_SYNC_TIME, HST_GET_FW_VER);
+ break;
+ case MISC_GET_FW_VER: {
+ struct carm_fw_ver *ver = (struct carm_fw_ver *)
+ mem + sizeof(struct carm_msg_get_fw_ver);
+ if (is_ok) {
+ host->fw_ver = le32_to_cpu(ver->version);
+ host->flags |= (ver->features & FL_FW_VER_MASK);
+ }
+ carm_handle_generic(host, crq, is_ok,
+ HST_GET_FW_VER, HST_PORT_SCAN);
+ break;
+ }
+ default:
+ /* unknown / invalid response */
+ goto err_out;
+ }
+ break;
+ }
+
+ case CARM_MSG_ARRAY: {
+ switch (crq->msg_subtype) {
+ case CARM_ARRAY_INFO:
+ carm_handle_array_info(host, crq, mem, is_ok);
+ break;
+ default:
+ /* unknown / invalid response */
+ goto err_out;
+ }
+ break;
+ }
+
+ default:
+ /* unknown / invalid response */
+ goto err_out;
+ }
+
+ return;
+
+err_out:
+ printk(KERN_WARNING DRV_NAME "(%s): BUG: unhandled message type %d/%d\n",
+ pci_name(host->pdev), crq->msg_type, crq->msg_subtype);
+ carm_end_rq(host, crq, 0);
+}
+
+static inline void carm_handle_responses(struct carm_host *host)
+{
+ void *mmio = host->mmio;
+ struct carm_response *resp = (struct carm_response *) host->shm;
+ unsigned int work = 0;
+ unsigned int idx = host->resp_idx % RMSG_Q_LEN;
+
+ while (1) {
+ u32 status = le32_to_cpu(resp[idx].status);
+
+ if (status == 0xffffffff) {
+ VPRINTK("ending response on index %u\n", idx);
+ writel(idx << 3, mmio + CARM_RESP_IDX);
+ break;
+ }
+
+ /* response to a message we sent */
+ else if ((status & (1 << 31)) == 0) {
+ VPRINTK("handling msg response on index %u\n", idx);
+ carm_handle_resp(host, resp[idx].ret_handle, status);
+ resp[idx].status = 0xffffffff;
+ }
+
+ /* asynchronous events the hardware throws our way */
+ else if ((status & 0xff000000) == (1 << 31)) {
+ u8 *evt_type_ptr = (u8 *) &resp[idx];
+ u8 evt_type = *evt_type_ptr;
+ printk(KERN_WARNING DRV_NAME "(%s): unhandled event type %d\n",
+ pci_name(host->pdev), (int) evt_type);
+ resp[idx].status = 0xffffffff;
+ }
+
+ idx = NEXT_RESP(idx);
+ work++;
+ }
+
+ VPRINTK("EXIT, work==%u\n", work);
+ host->resp_idx += work;
+}
+
+static irqreturn_t carm_interrupt(int irq, void *__host, struct pt_regs *regs)
+{
+ struct carm_host *host = __host;
+ void *mmio;
+ u32 mask;
+ int handled = 0;
+ unsigned long flags;
+
+ if (!host) {
+ VPRINTK("no host\n");
+ return IRQ_NONE;
+ }
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ mmio = host->mmio;
+
+ /* reading should also clear interrupts */
+ mask = readl(mmio + CARM_INT_STAT);
+
+ if (mask == 0 || mask == 0xffffffff) {
+ VPRINTK("no work, mask == 0x%x\n", mask);
+ goto out;
+ }
+
+ if (mask & INT_ACK_MASK)
+ writel(mask, mmio + CARM_INT_STAT);
+
+ if (unlikely(host->state == HST_INVALID)) {
+ VPRINTK("not initialized yet, mask = 0x%x\n", mask);
+ goto out;
+ }
+
+ if (mask & CARM_HAVE_RESP) {
+ handled = 1;
+ carm_handle_responses(host);
+ }
+
+out:
+ spin_unlock_irqrestore(&host->lock, flags);
+ VPRINTK("EXIT\n");
+ return IRQ_RETVAL(handled);
+}
+
+static void carm_fsm_task (void *_data)
+{
+ struct carm_host *host = _data;
+ unsigned long flags;
+ unsigned int state;
+ int rc, i, next_dev;
+ int reschedule = 0;
+ int new_state = HST_INVALID;
+
+ spin_lock_irqsave(&host->lock, flags);
+ state = host->state;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ DPRINTK("ENTER, state == %s\n", state_name[state]);
+
+ switch (state) {
+ case HST_PROBE_START:
+ new_state = HST_ALLOC_BUF;
+ reschedule = 1;
+ break;
+
+ case HST_ALLOC_BUF:
+ rc = carm_send_special(host, carm_fill_alloc_buf);
+ if (rc) {
+ new_state = HST_ERROR;
+ reschedule = 1;
+ }
+ break;
+
+ case HST_SYNC_TIME:
+ rc = carm_send_special(host, carm_fill_sync_time);
+ if (rc) {
+ new_state = HST_ERROR;
+ reschedule = 1;
+ }
+ break;
+
+ case HST_GET_FW_VER:
+ rc = carm_send_special(host, carm_fill_get_fw_ver);
+ if (rc) {
+ new_state = HST_ERROR;
+ reschedule = 1;
+ }
+ break;
+
+ case HST_PORT_SCAN:
+ rc = carm_send_special(host, carm_fill_scan_channels);
+ if (rc) {
+ new_state = HST_ERROR;
+ reschedule = 1;
+ }
+ break;
+
+ case HST_DEV_SCAN_START:
+ host->cur_scan_dev = -1;
+ new_state = HST_DEV_SCAN;
+ reschedule = 1;
+ break;
+
+ case HST_DEV_SCAN:
+ next_dev = -1;
+ for (i = host->cur_scan_dev + 1; i < CARM_MAX_PORTS; i++)
+ if (host->dev_present & (1 << i)) {
+ next_dev = i;
+ break;
+ }
+
+ if (next_dev >= 0) {
+ host->cur_scan_dev = next_dev;
+ rc = carm_array_info(host, next_dev);
+ if (rc) {
+ new_state = HST_ERROR;
+ reschedule = 1;
+ }
+ } else {
+ new_state = HST_DEV_ACTIVATE;
+ reschedule = 1;
+ }
+ break;
+
+ case HST_DEV_ACTIVATE: {
+ int activated = 0;
+ for (i = 0; i < CARM_MAX_PORTS; i++)
+ if (host->dev_active & (1 << i)) {
+ struct carm_port *port = &host->port[i];
+ struct gendisk *disk = port->disk;
+
+ set_capacity(disk, port->capacity);
+ add_disk(disk);
+ activated++;
+ }
+
+ printk(KERN_INFO DRV_NAME "(%s): %d ports activated\n",
+ pci_name(host->pdev), activated);
+
+ new_state = HST_PROBE_FINISHED;
+ reschedule = 1;
+ break;
+ }
+
+ case HST_PROBE_FINISHED:
+ up(&host->probe_sem);
+ break;
+
+ case HST_ERROR:
+ /* FIXME: TODO */
+ break;
+
+ default:
+ /* should never occur */
+ printk(KERN_ERR PFX "BUG: unknown state %d\n", state);
+ assert(0);
+ break;
+ }
+
+ if (new_state != HST_INVALID) {
+ spin_lock_irqsave(&host->lock, flags);
+ host->state = new_state;
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
+ if (reschedule)
+ schedule_work(&host->fsm_task);
+}
+
+static int carm_init_wait(void *mmio, u32 bits, unsigned int test_bit)
+{
+ unsigned int i;
+
+ for (i = 0; i < 50000; i++) {
+ u32 tmp = readl(mmio + CARM_LMUC);
+ udelay(100);
+
+ if (test_bit) {
+ if ((tmp & bits) == bits)
+ return 0;
+ } else {
+ if ((tmp & bits) == 0)
+ return 0;
+ }
+
+ cond_resched();
+ }
+
+ printk(KERN_ERR PFX "carm_init_wait timeout, bits == 0x%x, test_bit == %s\n",
+ bits, test_bit ? "yes" : "no");
+ return -EBUSY;
+}
+
+static void carm_init_responses(struct carm_host *host)
+{
+ void *mmio = host->mmio;
+ unsigned int i;
+ struct carm_response *resp = (struct carm_response *) host->shm;
+
+ for (i = 0; i < RMSG_Q_LEN; i++)
+ resp[i].status = 0xffffffff;
+
+ writel(0, mmio + CARM_RESP_IDX);
+}
+
+static int carm_init_host(struct carm_host *host)
+{
+ void *mmio = host->mmio;
+ u32 tmp;
+ u8 tmp8;
+ int rc;
+
+ DPRINTK("ENTER\n");
+
+ writel(0, mmio + CARM_INT_MASK);
+
+ tmp8 = readb(mmio + CARM_INITC);
+ if (tmp8 & 0x01) {
+ tmp8 &= ~0x01;
+ writeb(tmp8, CARM_INITC);
+ readb(mmio + CARM_INITC); /* flush */
+
+ DPRINTK("snooze...\n");
+ msleep(5000);
+ }
+
+ tmp = readl(mmio + CARM_HMUC);
+ if (tmp & CARM_CME) {
+ DPRINTK("CME bit present, waiting\n");
+ rc = carm_init_wait(mmio, CARM_CME, 1);
+ if (rc) {
+ DPRINTK("EXIT, carm_init_wait 1 failed\n");
+ return rc;
+ }
+ }
+ if (tmp & CARM_RME) {
+ DPRINTK("RME bit present, waiting\n");
+ rc = carm_init_wait(mmio, CARM_RME, 1);
+ if (rc) {
+ DPRINTK("EXIT, carm_init_wait 2 failed\n");
+ return rc;
+ }
+ }
+
+ tmp &= ~(CARM_RME | CARM_CME);
+ writel(tmp, mmio + CARM_HMUC);
+ readl(mmio + CARM_HMUC); /* flush */
+
+ rc = carm_init_wait(mmio, CARM_RME | CARM_CME, 0);
+ if (rc) {
+ DPRINTK("EXIT, carm_init_wait 3 failed\n");
+ return rc;
+ }
+
+ carm_init_buckets(mmio);
+
+ writel(host->shm_dma & 0xffffffff, mmio + RBUF_ADDR_LO);
+ writel((host->shm_dma >> 16) >> 16, mmio + RBUF_ADDR_HI);
+ writel(RBUF_LEN, mmio + RBUF_BYTE_SZ);
+
+ tmp = readl(mmio + CARM_HMUC);
+ tmp |= (CARM_RME | CARM_CME | CARM_WZBC);
+ writel(tmp, mmio + CARM_HMUC);
+ readl(mmio + CARM_HMUC); /* flush */
+
+ rc = carm_init_wait(mmio, CARM_RME | CARM_CME, 1);
+ if (rc) {
+ DPRINTK("EXIT, carm_init_wait 4 failed\n");
+ return rc;
+ }
+
+ writel(0, mmio + CARM_HMPHA);
+ writel(INT_DEF_MASK, mmio + CARM_INT_MASK);
+
+ carm_init_responses(host);
+
+ /* start initialization, probing state machine */
+ spin_lock_irq(&host->lock);
+ assert(host->state == HST_INVALID);
+ host->state = HST_PROBE_START;
+ spin_unlock_irq(&host->lock);
+ schedule_work(&host->fsm_task);
+
+ DPRINTK("EXIT\n");
+ return 0;
+}
+
+static int carm_init_disks(struct carm_host *host)
+{
+ unsigned int i;
+ int rc = 0;
+
+ for (i = 0; i < CARM_MAX_PORTS; i++) {
+ struct gendisk *disk;
+ request_queue_t *q;
+ struct carm_port *port;
+
+ port = &host->port[i];
+ port->host = host;
+ port->port_no = i;
+
+ disk = alloc_disk(CARM_MINORS_PER_MAJOR);
+ if (!disk) {
+ rc = -ENOMEM;
+ break;
+ }
+
+ port->disk = disk;
+ sprintf(disk->disk_name, DRV_NAME "%u_%u", host->id, i);
+ sprintf(disk->devfs_name, DRV_NAME "/%u_%u", host->id, i);
+ disk->major = host->major;
+ disk->first_minor = i * CARM_MINORS_PER_MAJOR;
+ disk->fops = &carm_bd_ops;
+ disk->private_data = port;
+
+ q = blk_init_queue(carm_rq_fn, &host->lock);
+ if (!q) {
+ rc = -ENOMEM;
+ break;
+ }
+ disk->queue = q;
+ blk_queue_max_hw_segments(q, CARM_MAX_REQ_SG);
+ blk_queue_max_phys_segments(q, CARM_MAX_REQ_SG);
+ blk_queue_segment_boundary(q, CARM_SG_BOUNDARY);
+
+ q->queuedata = port;
+ }
+
+ return rc;
+}
+
+static void carm_free_disks(struct carm_host *host)
+{
+ unsigned int i;
+
+ for (i = 0; i < CARM_MAX_PORTS; i++) {
+ struct gendisk *disk = host->port[i].disk;
+ if (disk) {
+ request_queue_t *q = disk->queue;
+
+ if (disk->flags & GENHD_FL_UP)
+ del_gendisk(disk);
+ if (q)
+ blk_cleanup_queue(q);
+ put_disk(disk);
+ }
+ }
+}
+
+static int carm_init_shm(struct carm_host *host)
+{
+ host->shm = pci_alloc_consistent(host->pdev, CARM_SHM_SIZE,
+ &host->shm_dma);
+ if (!host->shm)
+ return -ENOMEM;
+
+ host->msg_base = host->shm + RBUF_LEN;
+ host->msg_dma = host->shm_dma + RBUF_LEN;
+
+ memset(host->shm, 0xff, RBUF_LEN);
+ memset(host->msg_base, 0, PDC_SHM_SIZE - RBUF_LEN);
+
+ return 0;
+}
+
+static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ static unsigned int printed_version;
+ struct carm_host *host;
+ unsigned int pci_dac;
+ int rc;
+ request_queue_t *q;
+ unsigned int i;
+
+ if (!printed_version++)
+ printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+
+ rc = pci_enable_device(pdev);
+ if (rc)
+ return rc;
+
+ rc = pci_request_regions(pdev, DRV_NAME);
+ if (rc)
+ goto err_out;
+
+#if IF_64BIT_DMA_IS_POSSIBLE /* grrrr... */
+ rc = pci_set_dma_mask(pdev, 0xffffffffffffffffULL);
+ if (!rc) {
+ rc = pci_set_consistent_dma_mask(pdev, 0xffffffffffffffffULL);
+ if (rc) {
+ printk(KERN_ERR DRV_NAME "(%s): consistent DMA mask failure\n",
+ pci_name(pdev));
+ goto err_out_regions;
+ }
+ pci_dac = 1;
+ } else {
+#endif
+ rc = pci_set_dma_mask(pdev, 0xffffffffULL);
+ if (rc) {
+ printk(KERN_ERR DRV_NAME "(%s): DMA mask failure\n",
+ pci_name(pdev));
+ goto err_out_regions;
+ }
+ pci_dac = 0;
+#if IF_64BIT_DMA_IS_POSSIBLE /* grrrr... */
+ }
+#endif
+
+ host = kmalloc(sizeof(*host), GFP_KERNEL);
+ if (!host) {
+ printk(KERN_ERR DRV_NAME "(%s): memory alloc failure\n",
+ pci_name(pdev));
+ rc = -ENOMEM;
+ goto err_out_regions;
+ }
+
+ memset(host, 0, sizeof(*host));
+ host->pdev = pdev;
+ host->flags = pci_dac ? FL_DAC : 0;
+ spin_lock_init(&host->lock);
+ INIT_WORK(&host->fsm_task, carm_fsm_task, host);
+ init_MUTEX_LOCKED(&host->probe_sem);
+
+ for (i = 0; i < ARRAY_SIZE(host->req); i++)
+ host->req[i].tag = i;
+
+ host->mmio = ioremap(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0));
+ if (!host->mmio) {
+ printk(KERN_ERR DRV_NAME "(%s): MMIO alloc failure\n",
+ pci_name(pdev));
+ rc = -ENOMEM;
+ goto err_out_kfree;
+ }
+
+ rc = carm_init_shm(host);
+ if (rc) {
+ printk(KERN_ERR DRV_NAME "(%s): DMA SHM alloc failure\n",
+ pci_name(pdev));
+ goto err_out_iounmap;
+ }
+
+ q = blk_init_queue(carm_oob_rq_fn, &host->lock);
+ if (!q) {
+ printk(KERN_ERR DRV_NAME "(%s): OOB queue alloc failure\n",
+ pci_name(pdev));
+ rc = -ENOMEM;
+ goto err_out_pci_free;
+ }
+ host->oob_q = q;
+ q->queuedata = host;
+
+ /*
+ * Figure out which major to use: 160, 161, or dynamic
+ */
+ if (!test_and_set_bit(0, &carm_major_alloc))
+ host->major = 160;
+ else if (!test_and_set_bit(1, &carm_major_alloc))
+ host->major = 161;
+ else
+ host->flags |= FL_DYN_MAJOR;
+
+ host->id = carm_host_id;
+ sprintf(host->name, DRV_NAME "%d", carm_host_id);
+
+ rc = register_blkdev(host->major, host->name);
+ if (rc < 0)
+ goto err_out_free_majors;
+ if (host->flags & FL_DYN_MAJOR)
+ host->major = rc;
+
+ devfs_mk_dir(DRV_NAME);
+
+ rc = carm_init_disks(host);
+ if (rc)
+ goto err_out_blkdev_disks;
+
+ pci_set_master(pdev);
+
+ rc = request_irq(pdev->irq, carm_interrupt, SA_SHIRQ, DRV_NAME, host);
+ if (rc) {
+ printk(KERN_ERR DRV_NAME "(%s): irq alloc failure\n",
+ pci_name(pdev));
+ goto err_out_blkdev_disks;
+ }
+
+ rc = carm_init_host(host);
+ if (rc)
+ goto err_out_free_irq;
+
+ DPRINTK("waiting for probe_sem\n");
+ down(&host->probe_sem);
+
+ printk(KERN_INFO "%s: pci %s, ports %d, io %lx, irq %u, major %d\n",
+ host->name, pci_name(pdev), (int) CARM_MAX_PORTS,
+ pci_resource_start(pdev, 0), pdev->irq, host->major);
+
+ carm_host_id++;
+ pci_set_drvdata(pdev, host);
+ return 0;
+
+err_out_free_irq:
+ free_irq(pdev->irq, host);
+err_out_blkdev_disks:
+ carm_free_disks(host);
+ unregister_blkdev(host->major, host->name);
+err_out_free_majors:
+ if (host->major == 160)
+ clear_bit(0, &carm_major_alloc);
+ else if (host->major == 161)
+ clear_bit(1, &carm_major_alloc);
+ blk_cleanup_queue(host->oob_q);
+err_out_pci_free:
+ pci_free_consistent(pdev, CARM_SHM_SIZE, host->shm, host->shm_dma);
+err_out_iounmap:
+ iounmap(host->mmio);
+err_out_kfree:
+ kfree(host);
+err_out_regions:
+ pci_release_regions(pdev);
+err_out:
+ pci_disable_device(pdev);
+ return rc;
+}
+
+static void carm_remove_one (struct pci_dev *pdev)
+{
+ struct carm_host *host = pci_get_drvdata(pdev);
+
+ if (!host) {
+ printk(KERN_ERR PFX "BUG: no host data for PCI(%s)\n",
+ pci_name(pdev));
+ return;
+ }
+
+ free_irq(pdev->irq, host);
+ carm_free_disks(host);
+ devfs_remove(DRV_NAME);
+ unregister_blkdev(host->major, host->name);
+ if (host->major == 160)
+ clear_bit(0, &carm_major_alloc);
+ else if (host->major == 161)
+ clear_bit(1, &carm_major_alloc);
+ blk_cleanup_queue(host->oob_q);
+ pci_free_consistent(pdev, CARM_SHM_SIZE, host->shm, host->shm_dma);
+ iounmap(host->mmio);
+ kfree(host);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+static int __init carm_init(void)
+{
+ return pci_module_init(&carm_driver);
+}
+
+static void __exit carm_exit(void)
+{
+ pci_unregister_driver(&carm_driver);
+}
+
+module_init(carm_init);
+module_exit(carm_exit);
+
+
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/sched.h>
+#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
outb(0x80, iobase + 0x30);
/* Wait some time */
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ / 100);
+ msleep(10);
/* Turn FPGA on */
outb(0x00, iobase + 0x30);
outb((0x0f << RTS_LEVEL_SHIFT_BITS) | 1, iobase + REG_RX_CONTROL);
/* Timeout before it is safe to send the first HCI packet */
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout((HZ * 5) / 4); // or set it to 3/2
+ msleep(1250);
/* Register HCI device */
if (hci_register_dev(hdev) < 0) {
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/sched.h>
+#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
btuart_change_speed(info, DEFAULT_BAUD_RATE);
/* Timeout before it is safe to send the first HCI packet */
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ);
+ msleep(1000);
/* Register HCI device */
if (hci_register_dev(hdev) < 0) {
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/sched.h>
+#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
spin_unlock_irqrestore(&(info->lock), flags);
/* Timeout before it is safe to send the first HCI packet */
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ * 2);
+ msleep(2000);
/* Register HCI device */
if (hci_register_dev(hdev) < 0) {
}
file->private_data = hci_vhci;
- return 0;
+ return nonseekable_open(inode, file);
}
static int hci_vhci_chr_close(struct inode *inode, struct file *file)
static int scd_block_ioctl(struct inode *inode, struct file *file,
unsigned cmd, unsigned long arg)
{
- return cdrom_ioctl(&scd_info, inode, cmd, arg);
+ return cdrom_ioctl(file, &scd_info, inode, cmd, arg);
}
static int scd_block_media_changed(struct gendisk *disk)
static int cm206_block_ioctl(struct inode *inode, struct file *file,
unsigned cmd, unsigned long arg)
{
- return cdrom_ioctl(&cm206_info, inode, cmd, arg);
+ return cdrom_ioctl(file, &cm206_info, inode, cmd, arg);
}
static int cm206_block_media_changed(struct gendisk *disk)
static int mcd_block_ioctl(struct inode *inode, struct file *file,
unsigned cmd, unsigned long arg)
{
- return cdrom_ioctl(&mcd_info, inode, cmd, arg);
+ return cdrom_ioctl(file, &mcd_info, inode, cmd, arg);
}
static int mcd_block_media_changed(struct gendisk *disk)
#endif /* MULTISESSION */
if (disk_info.multi)
printk(KERN_WARNING "optcd: Multisession support experimental, "
- "see linux/Documentation/cdrom/optcd\n");
+ "see Documentation/cdrom/optcd\n");
DEBUG((DEBUG_TOC, "exiting update_toc"));
/*==========================================================================*/
-#if FUTURE
+#ifdef FUTURE
static DECLARE_WAIT_QUEUE_HEAD(sbp_waitq);
#endif /* FUTURE */
u_char TocEnt_number;
u_char TocEnt_format; /* em */
u_int TocEnt_address;
-#if SAFE_MIXED
+#ifdef SAFE_MIXED
char has_data;
#endif /* SAFE_MIXED */
u_char ored_ctl_adr; /* to detect if CDROM contains data tracks */
return (0);
}
/*==========================================================================*/
-#if FUTURE
+#ifdef FUTURE
static int cc_SubChanInfo(int frame, int count, u_char *buffer)
/* "frame" is a RED BOOK (msf-bin) address */
{
return (0);
}
/*==========================================================================*/
-#if FUTURE
+#ifdef FUTURE
/*
* obtain if requested service disturbs current audio state
*/
/*==========================================================================*/
-#if FUTURE
+#ifdef FUTURE
/*
* called always if driver gets entered
* returns 0 or ERROR2 or ERROR15
case CDROMREADMODE1:
msg(DBG_IOC,"ioctl: CDROMREADMODE1 requested.\n");
-#if SAFE_MIXED
+#ifdef SAFE_MIXED
if (current_drive->has_data>1) RETURN_UP(-EBUSY);
#endif /* SAFE_MIXED */
cc_ModeSelect(CD_FRAMESIZE);
case CDROMREADMODE2: /* not usable at the moment */
msg(DBG_IOC,"ioctl: CDROMREADMODE2 requested.\n");
-#if SAFE_MIXED
+#ifdef SAFE_MIXED
if (current_drive->has_data>1) RETURN_UP(-EBUSY);
#endif /* SAFE_MIXED */
cc_ModeSelect(CD_FRAMESIZE_RAW1);
if (famL_drive) RETURN_UP(-EINVAL);
if (famV_drive) RETURN_UP(-EINVAL);
if (famT_drive) RETURN_UP(-EINVAL);
-#if SAFE_MIXED
+#ifdef SAFE_MIXED
if (current_drive->has_data>1) RETURN_UP(-EBUSY);
#endif /* SAFE_MIXED */
if (current_drive->aud_buf==NULL) RETURN_UP(-EINVAL);
- if (copy_from_user(&read_audio, (void *)arg,
+ if (copy_from_user(&read_audio, (void __user *)arg,
sizeof(struct cdrom_read_audio)))
RETURN_UP(-EFAULT);
if (read_audio.nframes < 0 || read_audio.nframes>current_drive->sbp_audsiz) RETURN_UP(-EINVAL);
msg(DBG_AUD,"read_audio: cc_ReadError was necessary after read: %02X\n",i);
continue;
}
- if (copy_to_user((u_char *)read_audio.buf,
- (u_char *) current_drive->aud_buf,
+ if (copy_to_user(read_audio.buf,
+ current_drive->aud_buf,
read_audio.nframes * CD_FRAMESIZE_RAW))
RETURN_UP(-EFAULT);
msg(DBG_AUD,"read_audio: copy_to_user done.\n");
case CDROMPLAYMSF:
msg(DBG_IOC,"ioctl: CDROMPLAYMSF entered.\n");
-#if SAFE_MIXED
+#ifdef SAFE_MIXED
if (current_drive->has_data>1) RETURN_UP(-EBUSY);
#endif /* SAFE_MIXED */
if (current_drive->audio_state==audio_playing)
case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */
msg(DBG_IOC,"ioctl: CDROMPLAYTRKIND entered.\n");
-#if SAFE_MIXED
+#ifdef SAFE_MIXED
if (current_drive->has_data>1) RETURN_UP(-EBUSY);
#endif /* SAFE_MIXED */
if (current_drive->audio_state==audio_playing)
case CDROMSTOP: /* Spin down the drive */
msg(DBG_IOC,"ioctl: CDROMSTOP entered.\n");
-#if SAFE_MIXED
+#ifdef SAFE_MIXED
if (current_drive->has_data>1) RETURN_UP(-EBUSY);
#endif /* SAFE_MIXED */
i=cc_Pause_Resume(1);
goto request_loop;
}
-#if FUTURE
+#ifdef FUTURE
i=prepare(0,0); /* at moment not really a hassle check, but ... */
if (i!=0)
msg(DBG_INF,"\"prepare\" tells error %d -- ignored\n", i);
sbp_sleep(0);
if (sbp_data(req) != 0)
{
-#if SAFE_MIXED
+#ifdef SAFE_MIXED
current_drive->has_data=2; /* is really a data disk */
#endif /* SAFE_MIXED */
#ifdef DEBUG_GTL
unsigned cmd, unsigned long arg)
{
struct sbpcd_drive *p = inode->i_bdev->bd_disk->private_data;
- return cdrom_ioctl(p->sbpcd_infop, inode, cmd, arg);
+ return cdrom_ioctl(file, p->sbpcd_infop, inode, cmd, arg);
}
static int sbpcd_block_media_changed(struct gendisk *disk)
if ((current_drive->ored_ctl_adr&0x40)==0)
{
msg(DBG_INF,"CD contains no data tracks.\n");
-#if SAFE_MIXED
+#ifdef SAFE_MIXED
current_drive->has_data=0;
#endif /* SAFE_MIXED */
}
-#if SAFE_MIXED
+#ifdef SAFE_MIXED
else if (current_drive->has_data<1) current_drive->has_data=1;
#endif /* SAFE_MIXED */
}
if (p->f_eject) cc_SpinDown();
p->diskstate_flags &= ~cd_size_bit;
p->open_count=0;
-#if SAFE_MIXED
+#ifdef SAFE_MIXED
p->has_data=0;
#endif /* SAFE_MIXED */
}
if (port_index>0)
{
- msg(DBG_INF, "You should read linux/Documentation/cdrom/sbpcd\n");
+ msg(DBG_INF, "You should read Documentation/cdrom/sbpcd\n");
msg(DBG_INF, "and then configure sbpcd.h for your hardware.\n");
}
check_datarate();
if (p->drv_id==-1) continue;
switch_drive(p);
-#if SAFE_MIXED
+#ifdef SAFE_MIXED
p->has_data=0;
#endif /* SAFE_MIXED */
/*
current_drive->diskstate_flags &= ~toc_bit;
/* we *don't* need invalidate here, it's done by caller */
current_drive->diskstate_flags &= ~cd_size_bit;
-#if SAFE_MIXED
+#ifdef SAFE_MIXED
current_drive->has_data=0;
#endif /* SAFE_MIXED */
/*
* Attention! This file contains user-serviceable parts!
* I recommend to make use of it...
- * If you feel helpless, look into linux/Documentation/cdrom/sbpcd
+ * If you feel helpless, look into Documentation/cdrom/sbpcd
* (good idea anyway, at least before mailing me).
*
* The definitions for the first controller can get overridden by
../net/scc.c
A subset of the documentation is in
- ../../Documentation/networking/z8530drv.txt
+ Documentation/networking/z8530drv.txt
/*
- * HP AGPGART routines.
- * Copyright (C) 2002-2003 Hewlett-Packard Co
- * Bjorn Helgaas <bjorn_helgaas@hp.com>
+ * HP zx1 AGPGART routines.
+ *
+ * (c) Copyright 2002, 2003 Hewlett-Packard Development Company, L.P.
+ * Bjorn Helgaas <bjorn.helgaas@hp.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/acpi.h>
}
DRM_DEBUG("%u\n", auth.magic);
- if (copy_to_user((drm_auth_t *)arg, &auth, sizeof(auth)))
+ if (copy_to_user((drm_auth_t __user *)arg, &auth, sizeof(auth)))
return -EFAULT;
return 0;
}
drm_auth_t auth;
drm_file_t *file;
- if (copy_from_user(&auth, (drm_auth_t *)arg, sizeof(auth)))
+ if (copy_from_user(&auth, (drm_auth_t __user *)arg, sizeof(auth)))
return -EFAULT;
DRM_DEBUG("%u\n", auth.magic);
if ((file = DRM(find_file)(dev, auth.magic))) {
draw.handle = 0; /* NOOP */
DRM_DEBUG("%d\n", draw.handle);
- if (copy_to_user((drm_draw_t *)arg, &draw, sizeof(draw)))
+ if (copy_to_user((drm_draw_t __user *)arg, &draw, sizeof(draw)))
return -EFAULT;
return 0;
}
{
drm_file_t *priv = filp->private_data;
drm_device_t *dev = priv->dev;
+ drm_irq_busid_t __user *argp = (void __user *)arg;
drm_irq_busid_t p;
- if (copy_from_user(&p, (drm_irq_busid_t *)arg, sizeof(p)))
+ if (copy_from_user(&p, argp, sizeof(p)))
return -EFAULT;
if ((p.busnum >> 8) != dev->pci_domain ||
DRM_DEBUG("%d:%d:%d => IRQ %d\n",
p.busnum, p.devnum, p.funcnum, p.irq);
- if (copy_to_user((drm_irq_busid_t *)arg, &p, sizeof(p)))
+ if (copy_to_user(argp, &p, sizeof(p)))
return -EFAULT;
return 0;
}
dev->dma->this_buffer = NULL;
#endif
-#if __HAVE_IRQ_BH
+#ifdef __HAVE_IRQ_BH
INIT_WORK(&dev->work, DRM(irq_immediate_bh), dev);
#endif
-#if __HAVE_VBL_IRQ
+#ifdef __HAVE_VBL_IRQ
init_waitqueue_head(&dev->vbl_queue);
spin_lock_init( &dev->vbl_lock );
drm_device_t *dev = priv->dev;
drm_control_t ctl;
- if ( copy_from_user( &ctl, (drm_control_t *)arg, sizeof(ctl) ) )
+ if ( copy_from_user( &ctl, (drm_control_t __user *)arg, sizeof(ctl) ) )
return -EFAULT;
switch ( ctl.func ) {
}
}
-#if __HAVE_VBL_IRQ
+#ifdef __HAVE_VBL_IRQ
/**
* Wait for VBLANK.
{
drm_file_t *priv = filp->private_data;
drm_device_t *dev = priv->dev;
+ drm_wait_vblank_t __user *argp = (void __user *)data;
drm_wait_vblank_t vblwait;
struct timeval now;
int ret = 0;
if (!dev->irq)
return -EINVAL;
- DRM_COPY_FROM_USER_IOCTL( vblwait, (drm_wait_vblank_t *)data,
- sizeof(vblwait) );
+ DRM_COPY_FROM_USER_IOCTL( vblwait, argp, sizeof(vblwait) );
switch ( vblwait.request.type & ~_DRM_VBLANK_FLAGS_MASK ) {
case _DRM_VBLANK_RELATIVE:
}
done:
- DRM_COPY_TO_USER_IOCTL( (drm_wait_vblank_t *)data, vblwait,
- sizeof(vblwait) );
+ DRM_COPY_TO_USER_IOCTL( argp, vblwait, sizeof(vblwait) );
return ret;
}
{
drm_file_t *priv = filp->private_data;
drm_device_t *dev = priv->dev;
+ drm_scatter_gather_t __user *argp = (void __user *)arg;
drm_scatter_gather_t request;
drm_sg_mem_t *entry;
unsigned long pages, i, j;
if ( dev->sg )
return -EINVAL;
- if ( copy_from_user( &request,
- (drm_scatter_gather_t *)arg,
- sizeof(request) ) )
+ if ( copy_from_user( &request, argp, sizeof(request) ) )
return -EFAULT;
entry = DRM(alloc)( sizeof(*entry), DRM_MEM_SGLISTS );
request.handle = entry->handle;
- if ( copy_to_user( (drm_scatter_gather_t *)arg,
- &request,
- sizeof(request) ) ) {
+ if ( copy_to_user( argp, &request, sizeof(request) ) ) {
DRM(sg_cleanup)( entry );
return -EFAULT;
}
drm_sg_mem_t *entry;
if ( copy_from_user( &request,
- (drm_scatter_gather_t *)arg,
+ (drm_scatter_gather_t __user *)arg,
sizeof(request) ) )
return -EFAULT;
{
ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) dev->dev_private;
-#if DRM_DMA_HISTOGRAM
+#ifdef DRM_DMA_HISTOGRAM
dev->ctx_start = get_cycles();
#endif
int i;
DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
- if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res)))
+ if (copy_from_user(&res, (drm_ctx_res_t __user *)arg, sizeof(res)))
return -EFAULT;
if (res.count >= DRM_RESERVED_CONTEXTS) {
memset(&ctx, 0, sizeof(ctx));
}
}
res.count = DRM_RESERVED_CONTEXTS;
- if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res)))
+ if (copy_to_user((drm_ctx_res_t __user *)arg, &res, sizeof(res)))
return -EFAULT;
return 0;
}
drm_ctx_t ctx;
int idx;
- if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+ if (copy_from_user(&ctx, (drm_ctx_t __user *)arg, sizeof(ctx)))
return -EFAULT;
idx = DRM(alloc_queue)(dev, (ctx.flags & _DRM_CONTEXT_2DONLY));
if (idx < 0)
DRM_DEBUG("%d\n", ctx.handle);
ctx.handle = idx;
- if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx)))
+ if (copy_to_user((drm_ctx_t __user *)arg, &ctx, sizeof(ctx)))
return -EFAULT;
return 0;
}
drm_ctx_t ctx;
int idx;
- if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+ if (copy_from_user(&ctx, (drm_ctx_t __user *)arg, sizeof(ctx)))
return -EFAULT;
idx = ctx.handle;
drm_ctx_t ctx;
int idx;
- if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+ if (copy_from_user(&ctx, (drm_ctx_t __user *)arg, sizeof(ctx)))
return -EFAULT;
idx = ctx.handle;
else
ctx.flags = 0;
- if (copy_to_user((drm_ctx_t*)arg, &ctx, sizeof(ctx)))
+ if (copy_to_user((drm_ctx_t __user *)arg, &ctx, sizeof(ctx)))
return -EFAULT;
return 0;
drm_device_t *dev = priv->dev;
drm_ctx_t ctx;
- if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+ if (copy_from_user(&ctx, (drm_ctx_t __user *)arg, sizeof(ctx)))
return -EFAULT;
DRM_DEBUG("%d\n", ctx.handle);
return DRM(context_switch)(dev, dev->last_context, ctx.handle);
{
drm_ctx_t ctx;
- if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+ if (copy_from_user(&ctx, (drm_ctx_t __user *)arg, sizeof(ctx)))
return -EFAULT;
DRM_DEBUG("%d\n", ctx.handle);
ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) dev->dev_private;
int idx;
- if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+ if (copy_from_user(&ctx, (drm_ctx_t __user *)arg, sizeof(ctx)))
return -EFAULT;
DRM_DEBUG("%d\n", ctx.handle);
the circular buffer), is based on Alessandro Rubini's LINUX DEVICE
DRIVERS (Cambridge: O'Reilly, 1998), pages 111-113. */
-ssize_t DRM(read)(struct file *filp, char *buf, size_t count, loff_t *off)
+ssize_t DRM(read)(struct file *filp, char __user *buf, size_t count, loff_t *off)
{
drm_file_t *priv = filp->private_data;
drm_device_t *dev = priv->dev;
int DRM(resctx)(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
+ drm_ctx_res_t __user *argp = (void __user *)arg;
drm_ctx_res_t res;
drm_ctx_t ctx;
int i;
DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
- if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res)))
+ if (copy_from_user(&res, argp, sizeof(res)))
return -EFAULT;
if (res.count >= DRM_RESERVED_CONTEXTS) {
memset(&ctx, 0, sizeof(ctx));
}
}
res.count = DRM_RESERVED_CONTEXTS;
- if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res)))
+ if (copy_to_user(argp, &res, sizeof(res)))
return -EFAULT;
return 0;
}
drm_file_t *priv = filp->private_data;
drm_device_t *dev = priv->dev;
drm_ctx_t ctx;
+ drm_ctx_t __user *argp = (void __user *)arg;
- if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+ if (copy_from_user(&ctx, argp, sizeof(ctx)))
return -EFAULT;
if ((ctx.handle = DRM(alloc_queue)(dev)) == DRM_KERNEL_CONTEXT) {
/* Init kernel's context and get a new one. */
}
DRM(init_queue)(dev, dev->queuelist[ctx.handle], &ctx);
DRM_DEBUG("%d\n", ctx.handle);
- if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx)))
+ if (copy_to_user(argp, &ctx, sizeof(ctx)))
return -EFAULT;
return 0;
}
drm_ctx_t ctx;
drm_queue_t *q;
- if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+ if (copy_from_user(&ctx, (drm_ctx_t __user *)arg, sizeof(ctx)))
return -EFAULT;
DRM_DEBUG("%d\n", ctx.handle);
{
drm_file_t *priv = filp->private_data;
drm_device_t *dev = priv->dev;
+ drm_ctx_t __user *argp = (void __user *)arg;
drm_ctx_t ctx;
drm_queue_t *q;
- if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+ if (copy_from_user(&ctx, argp, sizeof(ctx)))
return -EFAULT;
DRM_DEBUG("%d\n", ctx.handle);
ctx.flags = q->flags;
atomic_dec(&q->use_count);
- if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx)))
+ if (copy_to_user(argp, &ctx, sizeof(ctx)))
return -EFAULT;
return 0;
drm_device_t *dev = priv->dev;
drm_ctx_t ctx;
- if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+ if (copy_from_user(&ctx, (drm_ctx_t __user *)arg, sizeof(ctx)))
return -EFAULT;
DRM_DEBUG("%d\n", ctx.handle);
return DRM(context_switch)(dev, dev->last_context, ctx.handle);
drm_device_t *dev = priv->dev;
drm_ctx_t ctx;
- if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+ if (copy_from_user(&ctx, (drm_ctx_t __user *)arg, sizeof(ctx)))
return -EFAULT;
DRM_DEBUG("%d\n", ctx.handle);
DRM(context_switch_complete)(dev, ctx.handle);
drm_queue_t *q;
drm_buf_t *buf;
- if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+ if (copy_from_user(&ctx, (drm_ctx_t __user *)arg, sizeof(ctx)))
return -EFAULT;
DRM_DEBUG("%d\n", ctx.handle);
DRM_DEBUG("\n");
- if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+ if (copy_from_user(&lock, (drm_lock_t __user *)arg, sizeof(lock)))
return -EFAULT;
ret = DRM(flush_block_and_flush)(dev, lock.context, lock.flags);
DRM(flush_unblock)(dev, lock.context, lock.flags);
int idx;
int while_locked = 0;
drm_device_dma_t *dma = dev->dma;
+ int *ind;
+ int err;
DECLARE_WAITQUEUE(entry, current);
DRM_DEBUG("%d\n", d->send_count);
remove_wait_queue(&q->write_queue, &entry);
}
+ ind = DRM(alloc)(d->send_count * sizeof(int), DRM_MEM_DRIVER);
+ if (!ind)
+ return -ENOMEM;
+
+ if (copy_from_user(ind, d->send_indices, d->send_count * sizeof(int))) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ err = -EINVAL;
for (i = 0; i < d->send_count; i++) {
- idx = d->send_indices[i];
+ idx = ind[i];
if (idx < 0 || idx >= dma->buf_count) {
- atomic_dec(&q->use_count);
DRM_ERROR("Index %d (of %d max)\n",
- d->send_indices[i], dma->buf_count - 1);
- return -EINVAL;
+ ind[i], dma->buf_count - 1);
+ goto out;
}
buf = dma->buflist[ idx ];
if (buf->filp != filp) {
- atomic_dec(&q->use_count);
DRM_ERROR("Process %d using buffer not owned\n",
current->pid);
- return -EINVAL;
+ goto out;
}
if (buf->list != DRM_LIST_NONE) {
- atomic_dec(&q->use_count);
DRM_ERROR("Process %d using buffer %d on list %d\n",
current->pid, buf->idx, buf->list);
+ goto out;
}
- buf->used = d->send_sizes[i];
+ buf->used = ind[i];
buf->while_locked = while_locked;
buf->context = d->context;
if (!buf->used) {
DRM_ERROR("Queueing 0 length buffer\n");
}
if (buf->pending) {
- atomic_dec(&q->use_count);
DRM_ERROR("Queueing pending buffer:"
" buffer %d, offset %d\n",
- d->send_indices[i], i);
- return -EINVAL;
+ ind[i], i);
+ goto out;
}
if (buf->waiting) {
- atomic_dec(&q->use_count);
DRM_ERROR("Queueing waiting buffer:"
" buffer %d, offset %d\n",
- d->send_indices[i], i);
- return -EINVAL;
+ ind[i], i);
+ goto out;
}
buf->waiting = 1;
if (atomic_read(&q->use_count) == 1
atomic_dec(&q->use_count);
return 0;
+
+out:
+ DRM(free)(ind, d->send_count * sizeof(int), DRM_MEM_DRIVER);
+ atomic_dec(&q->use_count);
+ return err;
}
static int DRM(dma_get_buffers_of_order)(struct file *filp, drm_dma_t *d,
LOCK_TEST_WITH_RETURN( dev, filp );
- DRM_COPY_FROM_USER_IOCTL( clear, (drm_mga_clear_t *)data, sizeof(clear) );
+ DRM_COPY_FROM_USER_IOCTL( clear, (drm_mga_clear_t __user *)data, sizeof(clear) );
if ( sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS )
sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS;
LOCK_TEST_WITH_RETURN( dev, filp );
DRM_COPY_FROM_USER_IOCTL( vertex,
- (drm_mga_vertex_t *)data,
+ (drm_mga_vertex_t __user *)data,
sizeof(vertex) );
if(vertex.idx < 0 || vertex.idx > dma->buf_count) return DRM_ERR(EINVAL);
LOCK_TEST_WITH_RETURN( dev, filp );
DRM_COPY_FROM_USER_IOCTL( indices,
- (drm_mga_indices_t *)data,
+ (drm_mga_indices_t __user *)data,
sizeof(indices) );
if(indices.idx < 0 || indices.idx > dma->buf_count) return DRM_ERR(EINVAL);
LOCK_TEST_WITH_RETURN( dev, filp );
- DRM_COPY_FROM_USER_IOCTL( iload, (drm_mga_iload_t *)data, sizeof(iload) );
+ DRM_COPY_FROM_USER_IOCTL( iload, (drm_mga_iload_t __user *)data, sizeof(iload) );
#if 0
if ( mga_do_wait_for_idle( dev_priv ) < 0 ) {
LOCK_TEST_WITH_RETURN( dev, filp );
- DRM_COPY_FROM_USER_IOCTL( blit, (drm_mga_blit_t *)data, sizeof(blit) );
+ DRM_COPY_FROM_USER_IOCTL( blit, (drm_mga_blit_t __user *)data, sizeof(blit) );
if ( sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS )
sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS;
return DRM_ERR(EINVAL);
}
- DRM_COPY_FROM_USER_IOCTL( param, (drm_mga_getparam_t *)data,
+ DRM_COPY_FROM_USER_IOCTL( param, (drm_mga_getparam_t __user *)data,
sizeof(param) );
DRM_DEBUG( "pid=%d\n", DRM_CURRENTPID );
--- /dev/null
+/*
+ * DS1286 Real Time Clock interface for Linux
+ *
+ * Copyright (C) 1998, 1999, 2000 Ralf Baechle
+ *
+ * Based on code written by Paul Gortmaker.
+ *
+ * This driver allows use of the real time clock (built into nearly all
+ * computers) from user space. It exports the /dev/rtc interface supporting
+ * various ioctl() and also the /proc/rtc pseudo-file for status
+ * information.
+ *
+ * The ioctls can be used to set the interrupt behaviour and generation rate
+ * from the RTC via IRQ 8. Then the /dev/rtc interface can be used to make
+ * use of these timer interrupts, be they interval or alarm based.
+ *
+ * The /dev/rtc interface will block on reads until an interrupt has been
+ * received. If a RTC interrupt has already happened, it will output an
+ * unsigned long and then block. The output value contains the interrupt
+ * status in the low byte and the number of interrupts since the last read
+ * in the remaining high bytes. The /dev/rtc interface can also be used with
+ * the select(2) call.
+ *
+ * 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/ds1286.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/rtc.h>
+#include <linux/spinlock.h>
+#include <linux/bcd.h>
+#include <linux/proc_fs.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#define DS1286_VERSION "1.0"
+
+/*
+ * We sponge a minor off of the misc major. No need slurping
+ * up another valuable major dev number for this. If you add
+ * an ioctl, make sure you don't conflict with SPARC's RTC
+ * ioctls.
+ */
+
+static DECLARE_WAIT_QUEUE_HEAD(ds1286_wait);
+
+static ssize_t ds1286_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos);
+
+static int ds1286_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+
+static unsigned int ds1286_poll(struct file *file, poll_table *wait);
+
+static void ds1286_get_alm_time (struct rtc_time *alm_tm);
+static void ds1286_get_time(struct rtc_time *rtc_tm);
+static int ds1286_set_time(struct rtc_time *rtc_tm);
+
+static inline unsigned char ds1286_is_updating(void);
+
+static spinlock_t ds1286_lock = SPIN_LOCK_UNLOCKED;
+
+static int ds1286_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+
+/*
+ * Bits in rtc_status. (7 bits of room for future expansion)
+ */
+
+#define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */
+#define RTC_TIMER_ON 0x02 /* missed irq timer active */
+
+static unsigned char ds1286_status; /* bitmapped status byte. */
+
+static unsigned char days_in_mo[] = {
+ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * Now all the various file operations that we export.
+ */
+
+static ssize_t ds1286_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos)
+{
+ return -EIO;
+}
+
+static int ds1286_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct rtc_time wtime;
+
+ switch (cmd) {
+ case RTC_AIE_OFF: /* Mask alarm int. enab. bit */
+ {
+ unsigned int flags;
+ unsigned char val;
+
+ if (!capable(CAP_SYS_TIME))
+ return -EACCES;
+
+ spin_lock_irqsave(&ds1286_lock, flags);
+ val = rtc_read(RTC_CMD);
+ val |= RTC_TDM;
+ rtc_write(val, RTC_CMD);
+ spin_unlock_irqrestore(&ds1286_lock, flags);
+
+ return 0;
+ }
+ case RTC_AIE_ON: /* Allow alarm interrupts. */
+ {
+ unsigned int flags;
+ unsigned char val;
+
+ if (!capable(CAP_SYS_TIME))
+ return -EACCES;
+
+ spin_lock_irqsave(&ds1286_lock, flags);
+ val = rtc_read(RTC_CMD);
+ val &= ~RTC_TDM;
+ rtc_write(val, RTC_CMD);
+ spin_unlock_irqrestore(&ds1286_lock, flags);
+
+ return 0;
+ }
+ case RTC_WIE_OFF: /* Mask watchdog int. enab. bit */
+ {
+ unsigned int flags;
+ unsigned char val;
+
+ if (!capable(CAP_SYS_TIME))
+ return -EACCES;
+
+ spin_lock_irqsave(&ds1286_lock, flags);
+ val = rtc_read(RTC_CMD);
+ val |= RTC_WAM;
+ rtc_write(val, RTC_CMD);
+ spin_unlock_irqrestore(&ds1286_lock, flags);
+
+ return 0;
+ }
+ case RTC_WIE_ON: /* Allow watchdog interrupts. */
+ {
+ unsigned int flags;
+ unsigned char val;
+
+ if (!capable(CAP_SYS_TIME))
+ return -EACCES;
+
+ spin_lock_irqsave(&ds1286_lock, flags);
+ val = rtc_read(RTC_CMD);
+ val &= ~RTC_WAM;
+ rtc_write(val, RTC_CMD);
+ spin_unlock_irqrestore(&ds1286_lock, flags);
+
+ return 0;
+ }
+ case RTC_ALM_READ: /* Read the present alarm time */
+ {
+ /*
+ * This returns a struct rtc_time. Reading >= 0xc0
+ * means "don't care" or "match all". Only the tm_hour,
+ * tm_min, and tm_sec values are filled in.
+ */
+
+ memset(&wtime, 0, sizeof(wtime));
+ ds1286_get_alm_time(&wtime);
+ break;
+ }
+ case RTC_ALM_SET: /* Store a time into the alarm */
+ {
+ /*
+ * This expects a struct rtc_time. Writing 0xff means
+ * "don't care" or "match all". Only the tm_hour,
+ * tm_min and tm_sec are used.
+ */
+ unsigned char hrs, min, sec;
+ struct rtc_time alm_tm;
+
+ if (!capable(CAP_SYS_TIME))
+ return -EACCES;
+
+ if (copy_from_user(&alm_tm, (struct rtc_time*)arg,
+ sizeof(struct rtc_time)))
+ return -EFAULT;
+
+ hrs = alm_tm.tm_hour;
+ min = alm_tm.tm_min;
+
+ if (hrs >= 24)
+ hrs = 0xff;
+
+ if (min >= 60)
+ min = 0xff;
+
+ BIN_TO_BCD(sec);
+ BIN_TO_BCD(min);
+ BIN_TO_BCD(hrs);
+
+ spin_lock(&ds1286_lock);
+ rtc_write(hrs, RTC_HOURS_ALARM);
+ rtc_write(min, RTC_MINUTES_ALARM);
+ spin_unlock(&ds1286_lock);
+
+ return 0;
+ }
+ case RTC_RD_TIME: /* Read the time/date from RTC */
+ {
+ memset(&wtime, 0, sizeof(wtime));
+ ds1286_get_time(&wtime);
+ break;
+ }
+ case RTC_SET_TIME: /* Set the RTC */
+ {
+ struct rtc_time rtc_tm;
+
+ if (!capable(CAP_SYS_TIME))
+ return -EACCES;
+
+ if (copy_from_user(&rtc_tm, (struct rtc_time*)arg,
+ sizeof(struct rtc_time)))
+ return -EFAULT;
+
+ return ds1286_set_time(&rtc_tm);
+ }
+ default:
+ return -EINVAL;
+ }
+ return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
+}
+
+/*
+ * We enforce only one user at a time here with the open/close.
+ * Also clear the previous interrupt data on an open, and clean
+ * up things on a close.
+ */
+
+static int ds1286_open(struct inode *inode, struct file *file)
+{
+ spin_lock_irq(&ds1286_lock);
+
+ if (ds1286_status & RTC_IS_OPEN)
+ goto out_busy;
+
+ ds1286_status |= RTC_IS_OPEN;
+
+ spin_unlock_irq(&ds1286_lock);
+ return 0;
+
+out_busy:
+ spin_lock_irq(&ds1286_lock);
+ return -EBUSY;
+}
+
+static int ds1286_release(struct inode *inode, struct file *file)
+{
+ ds1286_status &= ~RTC_IS_OPEN;
+
+ return 0;
+}
+
+static unsigned int ds1286_poll(struct file *file, poll_table *wait)
+{
+ poll_wait(file, &ds1286_wait, wait);
+
+ return 0;
+}
+
+/*
+ * The various file operations we support.
+ */
+
+static struct file_operations ds1286_fops = {
+ .llseek = no_llseek,
+ .read = ds1286_read,
+ .poll = ds1286_poll,
+ .ioctl = ds1286_ioctl,
+ .open = ds1286_open,
+ .release = ds1286_release,
+};
+
+static struct miscdevice ds1286_dev=
+{
+ .minor = RTC_MINOR,
+ .name = "rtc",
+ .fops = &ds1286_fops,
+};
+
+static int __init ds1286_init(void)
+{
+ int err;
+
+ printk(KERN_INFO "DS1286 Real Time Clock Driver v%s\n", DS1286_VERSION);
+
+ err = misc_register(&ds1286_dev);
+ if (err)
+ goto out;
+
+ if (!create_proc_read_entry("driver/rtc", 0, 0, ds1286_read_proc, NULL)) {
+ err = -ENOMEM;
+
+ goto out_deregister;
+ }
+
+ return 0;
+
+out_deregister:
+ misc_deregister(&ds1286_dev);
+
+out:
+ return err;
+}
+
+static void __exit ds1286_exit(void)
+{
+ remove_proc_entry("driver/rtc", NULL);
+ misc_deregister(&ds1286_dev);
+}
+
+static char *days[] = {
+ "***", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+/*
+ * Info exported via "/proc/rtc".
+ */
+static int ds1286_proc_output(char *buf)
+{
+ char *p, *s;
+ struct rtc_time tm;
+ unsigned char hundredth, month, cmd, amode;
+
+ p = buf;
+
+ ds1286_get_time(&tm);
+ hundredth = rtc_read(RTC_HUNDREDTH_SECOND);
+ BCD_TO_BIN(hundredth);
+
+ p += sprintf(p,
+ "rtc_time\t: %02d:%02d:%02d.%02d\n"
+ "rtc_date\t: %04d-%02d-%02d\n",
+ tm.tm_hour, tm.tm_min, tm.tm_sec, hundredth,
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
+
+ /*
+ * We implicitly assume 24hr mode here. Alarm values >= 0xc0 will
+ * match any value for that particular field. Values that are
+ * greater than a valid time, but less than 0xc0 shouldn't appear.
+ */
+ ds1286_get_alm_time(&tm);
+ p += sprintf(p, "alarm\t\t: %s ", days[tm.tm_wday]);
+ if (tm.tm_hour <= 24)
+ p += sprintf(p, "%02d:", tm.tm_hour);
+ else
+ p += sprintf(p, "**:");
+
+ if (tm.tm_min <= 59)
+ p += sprintf(p, "%02d\n", tm.tm_min);
+ else
+ p += sprintf(p, "**\n");
+
+ month = rtc_read(RTC_MONTH);
+ p += sprintf(p,
+ "oscillator\t: %s\n"
+ "square_wave\t: %s\n",
+ (month & RTC_EOSC) ? "disabled" : "enabled",
+ (month & RTC_ESQW) ? "disabled" : "enabled");
+
+ amode = ((rtc_read(RTC_MINUTES_ALARM) & 0x80) >> 5) |
+ ((rtc_read(RTC_HOURS_ALARM) & 0x80) >> 6) |
+ ((rtc_read(RTC_DAY_ALARM) & 0x80) >> 7);
+ if (amode == 7) s = "each minute";
+ else if (amode == 3) s = "minutes match";
+ else if (amode == 1) s = "hours and minutes match";
+ else if (amode == 0) s = "days, hours and minutes match";
+ else s = "invalid";
+ p += sprintf(p, "alarm_mode\t: %s\n", s);
+
+ cmd = rtc_read(RTC_CMD);
+ p += sprintf(p,
+ "alarm_enable\t: %s\n"
+ "wdog_alarm\t: %s\n"
+ "alarm_mask\t: %s\n"
+ "wdog_alarm_mask\t: %s\n"
+ "interrupt_mode\t: %s\n"
+ "INTB_mode\t: %s_active\n"
+ "interrupt_pins\t: %s\n",
+ (cmd & RTC_TDF) ? "yes" : "no",
+ (cmd & RTC_WAF) ? "yes" : "no",
+ (cmd & RTC_TDM) ? "disabled" : "enabled",
+ (cmd & RTC_WAM) ? "disabled" : "enabled",
+ (cmd & RTC_PU_LVL) ? "pulse" : "level",
+ (cmd & RTC_IBH_LO) ? "low" : "high",
+ (cmd & RTC_IPSW) ? "unswapped" : "swapped");
+
+ return p - buf;
+}
+
+static int ds1286_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = ds1286_proc_output (page);
+ if (len <= off+count) *eof = 1;
+ *start = page + off;
+ len -= off;
+ if (len>count)
+ len = count;
+ if (len<0)
+ len = 0;
+
+ return len;
+}
+
+/*
+ * Returns true if a clock update is in progress
+ */
+static inline unsigned char ds1286_is_updating(void)
+{
+ return rtc_read(RTC_CMD) & RTC_TE;
+}
+
+
+static void ds1286_get_time(struct rtc_time *rtc_tm)
+{
+ unsigned char save_control;
+ unsigned int flags;
+ unsigned long uip_watchdog = jiffies;
+
+ /*
+ * read RTC once any update in progress is done. The update
+ * can take just over 2ms. We wait 10 to 20ms. There is no need to
+ * to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP.
+ * If you need to know *exactly* when a second has started, enable
+ * periodic update complete interrupts, (via ioctl) and then
+ * immediately read /dev/rtc which will block until you get the IRQ.
+ * Once the read clears, read the RTC time (again via ioctl). Easy.
+ */
+
+ if (ds1286_is_updating() != 0)
+ while (jiffies - uip_watchdog < 2*HZ/100)
+ barrier();
+
+ /*
+ * Only the values that we read from the RTC are set. We leave
+ * tm_wday, tm_yday and tm_isdst untouched. Even though the
+ * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
+ * by the RTC when initially set to a non-zero value.
+ */
+ spin_lock_irqsave(&ds1286_lock, flags);
+ save_control = rtc_read(RTC_CMD);
+ rtc_write((save_control|RTC_TE), RTC_CMD);
+
+ rtc_tm->tm_sec = rtc_read(RTC_SECONDS);
+ rtc_tm->tm_min = rtc_read(RTC_MINUTES);
+ rtc_tm->tm_hour = rtc_read(RTC_HOURS) & 0x3f;
+ rtc_tm->tm_mday = rtc_read(RTC_DATE);
+ rtc_tm->tm_mon = rtc_read(RTC_MONTH) & 0x1f;
+ rtc_tm->tm_year = rtc_read(RTC_YEAR);
+
+ rtc_write(save_control, RTC_CMD);
+ spin_unlock_irqrestore(&ds1286_lock, flags);
+
+ BCD_TO_BIN(rtc_tm->tm_sec);
+ BCD_TO_BIN(rtc_tm->tm_min);
+ BCD_TO_BIN(rtc_tm->tm_hour);
+ BCD_TO_BIN(rtc_tm->tm_mday);
+ BCD_TO_BIN(rtc_tm->tm_mon);
+ BCD_TO_BIN(rtc_tm->tm_year);
+
+ /*
+ * Account for differences between how the RTC uses the values
+ * and how they are defined in a struct rtc_time;
+ */
+ if (rtc_tm->tm_year < 45)
+ rtc_tm->tm_year += 30;
+ if ((rtc_tm->tm_year += 40) < 70)
+ rtc_tm->tm_year += 100;
+
+ rtc_tm->tm_mon--;
+}
+
+static int ds1286_set_time(struct rtc_time *rtc_tm)
+{
+ unsigned char mon, day, hrs, min, sec, leap_yr;
+ unsigned char save_control;
+ unsigned int yrs, flags;
+
+
+ yrs = rtc_tm->tm_year + 1900;
+ mon = rtc_tm->tm_mon + 1; /* tm_mon starts at zero */
+ day = rtc_tm->tm_mday;
+ hrs = rtc_tm->tm_hour;
+ min = rtc_tm->tm_min;
+ sec = rtc_tm->tm_sec;
+
+ if (yrs < 1970)
+ return -EINVAL;
+
+ leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
+
+ if ((mon > 12) || (day == 0))
+ return -EINVAL;
+
+ if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
+ return -EINVAL;
+
+ if ((hrs >= 24) || (min >= 60) || (sec >= 60))
+ return -EINVAL;
+
+ if ((yrs -= 1940) > 255) /* They are unsigned */
+ return -EINVAL;
+
+ if (yrs >= 100)
+ yrs -= 100;
+
+ BIN_TO_BCD(sec);
+ BIN_TO_BCD(min);
+ BIN_TO_BCD(hrs);
+ BIN_TO_BCD(day);
+ BIN_TO_BCD(mon);
+ BIN_TO_BCD(yrs);
+
+ spin_lock_irqsave(&ds1286_lock, flags);
+ save_control = rtc_read(RTC_CMD);
+ rtc_write((save_control|RTC_TE), RTC_CMD);
+
+ rtc_write(yrs, RTC_YEAR);
+ rtc_write(mon, RTC_MONTH);
+ rtc_write(day, RTC_DATE);
+ rtc_write(hrs, RTC_HOURS);
+ rtc_write(min, RTC_MINUTES);
+ rtc_write(sec, RTC_SECONDS);
+ rtc_write(0, RTC_HUNDREDTH_SECOND);
+
+ rtc_write(save_control, RTC_CMD);
+ spin_unlock_irqrestore(&ds1286_lock, flags);
+
+ return 0;
+}
+
+static void ds1286_get_alm_time(struct rtc_time *alm_tm)
+{
+ unsigned char cmd;
+ unsigned int flags;
+
+ /*
+ * Only the values that we read from the RTC are set. That
+ * means only tm_wday, tm_hour, tm_min.
+ */
+ spin_lock_irqsave(&ds1286_lock, flags);
+ alm_tm->tm_min = rtc_read(RTC_MINUTES_ALARM) & 0x7f;
+ alm_tm->tm_hour = rtc_read(RTC_HOURS_ALARM) & 0x1f;
+ alm_tm->tm_wday = rtc_read(RTC_DAY_ALARM) & 0x07;
+ cmd = rtc_read(RTC_CMD);
+ spin_unlock_irqrestore(&ds1286_lock, flags);
+
+ BCD_TO_BIN(alm_tm->tm_min);
+ BCD_TO_BIN(alm_tm->tm_hour);
+ alm_tm->tm_sec = 0;
+}
+
+module_init(ds1286_init);
+module_exit(ds1286_exit);
+
+MODULE_AUTHOR("Ralf Baechle");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(RTC_MINOR);
}
static ssize_t
-ds1620_read(struct file *file, char *buf, size_t count, loff_t *ptr)
+ds1620_read(struct file *file, char __user *buf, size_t count, loff_t *ptr)
{
signed int cur_temp;
signed char cur_temp_degF;
- /* Can't seek (pread) on this device */
- if (ptr != &file->f_pos)
- return -ESPIPE;
-
cur_temp = cvt_9_to_int(ds1620_in(THERM_READ_TEMP, 9)) >> 1;
/* convert to Fahrenheit, as per wdt.c */
ds1620_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct therm therm;
+ union {
+ struct therm __user *therm;
+ int __user *i;
+ } uarg;
int i;
+ uarg.i = (int __user *)arg;
+
switch(cmd) {
case CMD_SET_THERMOSTATE:
case CMD_SET_THERMOSTATE2:
return -EPERM;
if (cmd == CMD_SET_THERMOSTATE) {
- if (get_user(therm.hi, (int *)arg))
+ if (get_user(therm.hi, uarg.i))
return -EFAULT;
therm.lo = therm.hi - 3;
} else {
- if (copy_from_user(&therm, (void *)arg, sizeof(therm)))
+ if (copy_from_user(&therm, uarg.therm, sizeof(therm)))
return -EFAULT;
}
therm.hi >>= 1;
if (cmd == CMD_GET_THERMOSTATE) {
- if (put_user(therm.hi, (int *)arg))
+ if (put_user(therm.hi, uarg.i))
return -EFAULT;
} else {
- if (copy_to_user((void *)arg, &therm, sizeof(therm)))
+ if (copy_to_user(uarg.therm, &therm, sizeof(therm)))
return -EFAULT;
}
break;
if (cmd == CMD_GET_TEMPERATURE)
i >>= 1;
- return put_user(i, (int *)arg) ? -EFAULT : 0;
+ return put_user(i, uarg.i) ? -EFAULT : 0;
case CMD_GET_STATUS:
i = ds1620_in(THERM_READ_CONFIG, 8) & 0xe3;
- return put_user(i, (int *)arg) ? -EFAULT : 0;
+ return put_user(i, uarg.i) ? -EFAULT : 0;
case CMD_GET_FAN:
i = netwinder_get_fan();
- return put_user(i, (int *)arg) ? -EFAULT : 0;
+ return put_user(i, uarg.i) ? -EFAULT : 0;
case CMD_SET_FAN:
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- if (get_user(i, (int *)arg))
+ if (get_user(i, uarg.i))
return -EFAULT;
netwinder_set_fan(i);
static struct file_operations ds1620_fops = {
.owner = THIS_MODULE,
+ .open = nonseekable_open,
.read = ds1620_read,
.ioctl = ds1620_ioctl,
};
return ret;
#ifdef THERM_USE_PROC
- proc_therm_ds1620 = create_proc_entry("therm", 0, 0);
+ proc_therm_ds1620 = create_proc_entry("therm", 0, NULL);
if (proc_therm_ds1620)
proc_therm_ds1620->read_proc = proc_therm_ds1620_read;
else
}
case 2: /* 16 bit */
{
- short *data;
+ const short *data;
count /= 2;
- data = (short*) buf;
+ data = (const short *)buf;
handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
get_user(dsp56k_host_interface.data.w[1], data+n++));
return 2*n;
}
case 4: /* 32 bit */
{
- long *data;
+ const long *data;
count /= 4;
- data = (long*) buf;
+ data = (const long *)buf;
handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
get_user(dsp56k_host_interface.data.l, data+n++));
return 4*n;
#define ENABLE_PCI
#endif /* CONFIG_PCI */
-#define putUser(arg1, arg2) put_user(arg1, (unsigned long *)arg2)
-#define getUser(arg1, arg2) get_user(arg1, (unsigned int *)arg2)
+#define putUser(arg1, arg2) put_user(arg1, (unsigned long __user *)arg2)
+#define getUser(arg1, arg2) get_user(arg1, (unsigned __user *)arg2)
#ifdef ENABLE_PCI
#include <linux/pci.h>
void epca_setup(char *, int *);
void console_print(const char *);
-static int get_termio(struct tty_struct *, struct termio *);
+static int get_termio(struct tty_struct *, struct termio __user *);
static int pc_write(struct tty_struct *, int, const unsigned char *, int);
int pc_init(void);
if (bytesAvailable)
{ /* Begin bytesAvailable */
+ /* ---------------------------------------------------------------
+ The below function reads data from user memory. This routine
+ can not be used in an interrupt routine. (Because it may
+ generate a page fault) It can only be called while we can the
+ user context is accessible.
+
+ The prototype is :
+ inline void copy_from_user(void * to, const void * from,
+ unsigned long count);
+
+ I also think (Check hackers guide) that optimization must
+ be turned ON. (Which sounds strange to me...)
+
+ Remember copy_from_user WILL generate a page fault if the
+ user memory being accessed has been swapped out. This can
+ cause this routine to temporarily sleep while this page
+ fault is occurring.
+
+ ----------------------------------------------------------------- */
- /* Can the user buffer be accessed at the moment ? */
- if (verify_area(VERIFY_READ, (char*)buf, bytesAvailable))
- bytesAvailable = 0; /* Can't do; try again later */
- else /* Evidently it can, began transmission */
- { /* Begin if area verified */
- /* ---------------------------------------------------------------
- The below function reads data from user memory. This routine
- can not be used in an interrupt routine. (Because it may
- generate a page fault) It can only be called while we can the
- user context is accessible.
-
- The prototype is :
- inline void copy_from_user(void * to, const void * from,
- unsigned long count);
-
- I also think (Check hackers guide) that optimization must
- be turned ON. (Which sounds strange to me...)
-
- Remember copy_from_user WILL generate a page fault if the
- user memory being accessed has been swapped out. This can
- cause this routine to temporarily sleep while this page
- fault is occurring.
-
- ----------------------------------------------------------------- */
-
- if (copy_from_user(ch->tmp_buf, buf,
- bytesAvailable))
- return -EFAULT;
-
- } /* End if area verified */
-
+ if (copy_from_user(ch->tmp_buf, buf,
+ bytesAvailable))
+ return -EFAULT;
} /* End bytesAvailable */
/* ------------------------------------------------------------------
ch->boardnum = crd;
ch->channelnum = i;
ch->magic = EPCA_MAGIC;
- ch->tty = 0;
+ ch->tty = NULL;
if (shrinkmem)
{
{ /* Begin receive_data */
unchar *rptr;
- struct termios *ts = 0;
+ struct termios *ts = NULL;
struct tty_struct *tty;
volatile struct board_chan *bc;
register int dataToRead, wrapgap, bytesAvailable;
static int info_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
{
- int error;
-
switch (cmd)
{ /* Begin switch cmd */
struct digi_info di ;
int brd;
- getUser(brd, (unsigned int *)arg);
-
- if ((error = verify_area(VERIFY_WRITE, (char*)arg, sizeof(di))))
- {
- printk(KERN_ERR "DIGI_GETINFO : verify area size 0x%x failed\n",sizeof(di));
- return(error);
- }
+ getUser(brd, (unsigned int __user *)arg);
if ((brd < 0) || (brd >= num_cards) || (num_cards == 0))
return (-ENODEV);
di.port = boards[brd].port ;
di.membase = boards[brd].membase ;
- if (copy_to_user((char *)arg, &di, sizeof (di)))
+ if (copy_to_user((void __user *)arg, &di, sizeof (di)))
return -EFAULT;
break;
epcaparam(tty,ch);
memoff(ch);
restore_flags(flags);
+ return 0;
}
static int pc_ioctl(struct tty_struct *tty, struct file * file,
{ /* Begin pc_ioctl */
digiflow_t dflow;
- int retval, error;
+ int retval;
unsigned long flags;
unsigned int mflag, mstat;
unsigned char startc, stopc;
volatile struct board_chan *bc;
struct channel *ch = (struct channel *) tty->driver_data;
+ void __user *argp = (void __user *)arg;
if (ch)
bc = ch->brdchan;
{ /* Begin switch cmd */
case TCGETS:
- if (copy_to_user((struct termios *)arg,
+ if (copy_to_user(argp,
tty->termios, sizeof(struct termios)))
return -EFAULT;
return(0);
case TCGETA:
- return get_termio(tty, (struct termio *)arg);
+ return get_termio(tty, argp);
case TCSBRK: /* SVID version: non-zero arg --> no break */
return 0;
case TIOCGSOFTCAR:
-
- error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long));
- if (error)
- return error;
-
- putUser(C_CLOCAL(tty) ? 1 : 0,
- (unsigned long *) arg);
+ if (put_user(C_CLOCAL(tty)?1:0, (unsigned long __user *)arg))
+ return -EFAULT;
return 0;
case TIOCSSOFTCAR:
- /*RONNIE PUT VERIFY_READ (See above) check here */
{
unsigned int value;
- getUser(value, (unsigned int *)arg);
+ if (get_user(value, (unsigned __user *)argp))
+ return -EFAULT;
tty->termios->c_cflag =
((tty->termios->c_cflag & ~CLOCAL) |
(value ? CLOCAL : 0));
case TIOCMODG:
mflag = pc_tiocmget(tty, file);
- if (putUser(mflag, (unsigned int *) arg))
+ if (put_user(mflag, (unsigned long __user *)argp))
return -EFAULT;
break;
case TIOCMODS:
- if (getUser(mstat, (unsigned int *)arg))
+ if (get_user(mstat, (unsigned __user *)argp))
return -EFAULT;
return pc_tiocmset(tty, file, mstat, ~mstat);
break;
case DIGI_GETA:
- if (copy_to_user((char*)arg, &ch->digiext,
- sizeof(digi_t)))
+ if (copy_to_user(argp, &ch->digiext, sizeof(digi_t)))
return -EFAULT;
break;
/* Fall Thru */
case DIGI_SETA:
- if (copy_from_user(&ch->digiext, (char*)arg,
- sizeof(digi_t)))
+ if (copy_from_user(&ch->digiext, argp, sizeof(digi_t)))
return -EFAULT;
if (ch->digiext.digi_flags & DIGI_ALTPIN)
memoff(ch);
restore_flags(flags);
- if (copy_to_user((char*)arg, &dflow, sizeof(dflow)))
+ if (copy_to_user(argp, &dflow, sizeof(dflow)))
return -EFAULT;
break;
stopc = ch->stopca;
}
- if (copy_from_user(&dflow, (char*)arg, sizeof(dflow)))
+ if (copy_from_user(&dflow, argp, sizeof(dflow)))
return -EFAULT;
if (dflow.startc != startc || dflow.stopc != stopc)
/* --------------------- Begin get_termio ----------------------- */
-static int get_termio(struct tty_struct * tty, struct termio * termio)
+static int get_termio(struct tty_struct * tty, struct termio __user * termio)
{ /* Begin get_termio */
- int error;
-
- error = verify_area(VERIFY_WRITE, termio, sizeof (struct termio));
- if (error)
- return error;
-
- kernel_termios_to_user_termio(termio, tty->termios);
-
- return 0;
+ return kernel_termios_to_user_termio(termio, tty->termios);
} /* End get_termio */
/* ---------------------- Begin epca_setup -------------------------- */
void epca_setup(char *str, int *ints)
/* compress a block of memory, decompress a block of memory, or to identify */
/* itself. For more information, see the specification file "compress.h". */
-EXPORT void lzrw3_compress(action,wrk_mem,src_adr,src_len,dst_adr,p_dst_len)
-UWORD action; /* Action to be performed. */
-UBYTE *wrk_mem; /* Address of working memory we can use. */
-UBYTE *src_adr; /* Address of input data. */
-LONG src_len; /* Length of input data. */
-UBYTE *dst_adr; /* Address to put output data. */
-void *p_dst_len; /* Address of longword for length of output data. */
+EXPORT void lzrw3_compress(
+ UWORD action, /* Action to be performed. */
+ UBYTE *wrk_mem, /* Address of working memory we can use.*/
+ UBYTE *src_adr, /* Address of input data. */
+ LONG src_len, /* Length of input data. */
+ UBYTE *dst_adr, /* Address to put output data. */
+ void *p_dst_len /* Address of longword for length of output data.*/
+)
{
switch (action)
{
(((40543*(((*(PTR))<<8)^((*((PTR)+1))<<4)^(*((PTR)+2))))>>4) & 0xFFF)
/******************************************************************************/
-
-LOCAL void compress_compress
- (p_wrk_mem,p_src_first,src_len,p_dst_first,p_dst_len)
+
/* Input : Hand over the required amount of working memory in p_wrk_mem. */
/* Input : Specify input block using p_src_first and src_len. */
/* Input : Point p_dst_first to the start of the output zone (OZ). */
/* Output : Output block in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. May */
/* Output : write in OZ=Mem[p_dst_first..p_dst_first+src_len+MAX_CMP_GROUP-1].*/
/* Output : Upon completion guaranteed *p_dst_len<=src_len+FLAG_BYTES. */
-UBYTE *p_wrk_mem;
-UBYTE *p_src_first;
-ULONG src_len;
-UBYTE *p_dst_first;
-LONG *p_dst_len;
+LOCAL void compress_compress(UBYTE *p_wrk_mem,
+ UBYTE *p_src_first, ULONG src_len,
+ UBYTE *p_dst_first, LONG *p_dst_len)
{
/* p_src and p_dst step through the source and destination blocks. */
register UBYTE *p_src = p_src_first;
/* to the hash table entry corresponding to the second youngest literal. */
/* Note: p_h1=0=>p_h2=0 because zero values denote absence of a pending */
/* literal. The variables are initialized to zero meaning an empty "buffer". */
- UBYTE **p_h1=0;
- UBYTE **p_h2=0;
+ UBYTE **p_h1=NULL;
+ UBYTE **p_h2=NULL;
/* To start, we write the flag bytes. Being optimistic, we set the flag to */
/* FLAG_COMPRESS. The remaining flag bytes are zeroed so as to keep the */
/* upon the arrival of extra context bytes. */
if (p_h1!=0)
{
- if (p_h2!=0)
- {*p_h2=p_ziv-2; p_h2=0;}
- *p_h1=p_ziv-1; p_h1=0;
+ if (p_h2)
+ {*p_h2=p_ziv-2; p_h2=NULL;}
+ *p_h1=p_ziv-1; p_h1=NULL;
}
/* In any case, we can update the hash table based on the current */
/******************************************************************************/
-LOCAL void compress_decompress
- (p_wrk_mem,p_src_first,src_len,p_dst_first,p_dst_len)
/* Input : Hand over the required amount of working memory in p_wrk_mem. */
/* Input : Specify input block using p_src_first and src_len. */
/* Input : Point p_dst_first to the start of the output zone. */
/* Output : Length of output block written to *p_dst_len. */
/* Output : Output block in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. */
/* Output : Writes only in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. */
-UBYTE *p_wrk_mem;
-UBYTE *p_src_first;
-LONG src_len;
-UBYTE *p_dst_first;
-ULONG *p_dst_len;
+LOCAL void compress_decompress( UBYTE *p_wrk_mem,
+ UBYTE *p_src_first, LONG src_len,
+ UBYTE *p_dst_first, ULONG *p_dst_len)
{
/* Byte pointers p_src and p_dst scan through the input and output blocks. */
register UBYTE *p_src = p_src_first+FLAG_BYTES;
/* forward */
static int zftc_write(int *write_cnt,
__u8 *dst_buf, const int seg_sz,
- const __u8 *src_buf, const int req_len,
+ const __u8 __user *src_buf, const int req_len,
const zft_position *pos, const zft_volinfo *volume);
static int zftc_read(int *read_cnt,
- __u8 *dst_buf, const int to_do,
+ __u8 __user *dst_buf, const int to_do,
const __u8 *src_buf, const int seg_sz,
const zft_position *pos, const zft_volinfo *volume);
static int zftc_seek(unsigned int new_block_pos,
*/
static int zftc_write(int *write_cnt,
__u8 *dst_buf, const int seg_sz,
- const __u8 *src_buf, const int req_len,
+ const __u8 __user *src_buf, const int req_len,
const zft_position *pos, const zft_volinfo *volume)
{
int req_len_left = req_len;
* be set to 0
*/
static int zftc_read (int *read_cnt,
- __u8 *dst_buf, const int to_do,
+ __u8 __user *dst_buf, const int to_do,
const __u8 *src_buf, const int seg_sz,
const zft_position *pos, const zft_volinfo *volume)
{
ptr += get_history_info(ptr);
len = strlen(page);
- *start = 0;
+ *start = NULL;
if (off+count >= len) {
*eof = 1;
} else {
/* IOCTL routine called by kernel-interface code
*/
-int _zft_ioctl(unsigned int command, void * arg)
+int _zft_ioctl(unsigned int command, void __user * arg)
{
int result;
union { struct mtop mtop;
*/
extern int _zft_open(unsigned int dev_minor, unsigned int access_mode);
extern int _zft_close(void);
-extern int _zft_ioctl(unsigned int command, void *arg);
+extern int _zft_ioctl(unsigned int command, void __user *arg);
#endif
static int zft_ioctl(struct inode *ino, struct file *filep,
unsigned int command, unsigned long arg);
static int zft_mmap(struct file *filep, struct vm_area_struct *vma);
-static ssize_t zft_read (struct file *fp, char *buff,
+static ssize_t zft_read (struct file *fp, char __user *buff,
size_t req_len, loff_t *ppos);
-static ssize_t zft_write(struct file *fp, const char *buff,
+static ssize_t zft_write(struct file *fp, const char __user *buff,
size_t req_len, loff_t *ppos);
static struct file_operations zft_cdev =
int result;
TRACE_FUN(ft_t_flow);
+ nonseekable_open(ino, filep);
TRACE(ft_t_flow, "called for minor %d", iminor(ino));
if ( test_and_set_bit(0,&busy_flag) ) {
TRACE_ABORT(-EBUSY, ft_t_warn, "failed: already busy");
old_sigmask = current->blocked; /* save mask */
sigfillset(¤t->blocked);
/* This will work as long as sizeof(void *) == sizeof(long) */
- result = _zft_ioctl(command, (void *) arg);
+ result = _zft_ioctl(command, (void __user *) arg);
current->blocked = old_sigmask; /* restore mask */
TRACE_EXIT result;
}
/* Read from floppy tape device
*/
-static ssize_t zft_read(struct file *fp, char *buff,
+static ssize_t zft_read(struct file *fp, char __user *buff,
size_t req_len, loff_t *ppos)
{
int result = -EIO;
/* Write to tape device
*/
-static ssize_t zft_write(struct file *fp, const char *buff,
+static ssize_t zft_write(struct file *fp, const char __user *buff,
size_t req_len, loff_t *ppos)
{
int result = -EIO;
struct zft_cmpr_ops {
int (*write)(int *write_cnt,
__u8 *dst_buf, const int seg_sz,
- const __u8 *src_buf, const int req_len,
+ const __u8 __user *src_buf, const int req_len,
const zft_position *pos, const zft_volinfo *volume);
int (*read)(int *read_cnt,
- __u8 *dst_buf, const int req_len,
+ __u8 __user *dst_buf, const int req_len,
const __u8 *src_buf, const int seg_sz,
const zft_position *pos, const zft_volinfo *volume);
int (*seek)(unsigned int new_block_pos,
* amount of data actually * copied to the user-buffer
*/
static int zft_simple_read (int *read_cnt,
- __u8 *dst_buf,
+ __u8 __user *dst_buf,
const int to_do,
const __u8 *src_buf,
const int seg_sz,
* req_len: how much data should be read at most.
* volume: contains information on current volume (blk_sz etc.)
*/
-static int empty_deblock_buf(__u8 *usr_buf, const int req_len,
+static int empty_deblock_buf(__u8 __user *usr_buf, const int req_len,
const __u8 *src_buf, const int seg_sz,
zft_position *pos,
const zft_volinfo *volume)
* use small block-sizes. The block-size may be 1kb (SECTOR_SIZE). In
* this case a MTFSR 28 maybe still inside the same segment.
*/
-int _zft_read(char* buff, int req_len)
+int _zft_read(char __user *buff, int req_len)
{
int req_clipped;
int result = 0;
0, FT_SEGMENT_SIZE)
/* hook for the VFS interface
*/
-extern int _zft_read(char* buff, int req_len);
+extern int _zft_read(char __user *buff, int req_len);
#endif /* _ZFTAPE_READ_H */
*/
static int zft_simple_write(int *cnt,
__u8 *dst_buf, const int seg_sz,
- const __u8 *src_buf, const int req_len,
+ const __u8 __user *src_buf, const int req_len,
const zft_position *pos,const zft_volinfo *volume)
{
int space_left;
static int fill_deblock_buf(__u8 *dst_buf, const int seg_sz,
zft_position *pos, const zft_volinfo *volume,
- const char *usr_buf, const int req_len)
+ const char __user *usr_buf, const int req_len)
{
int cnt = 0;
int result = 0;
/* called by the kernel-interface routine "zft_write()"
*/
-int _zft_write(const char* buff, int req_len)
+int _zft_write(const char __user *buff, int req_len)
{
int result = 0;
int written = 0;
/* hook for the VFS interface
*/
-extern int _zft_write(const char *buff, int req_len);
+extern int _zft_write(const char __user *buff, int req_len);
#endif /* _ZFTAPE_WRITE_H */
#define func_enter() gs_dprintk (GS_DEBUG_FLOW, "gs: enter %s\n", __FUNCTION__)
#define func_exit() gs_dprintk (GS_DEBUG_FLOW, "gs: exit %s\n", __FUNCTION__)
-#if NEW_WRITE_LOCKING
+#ifdef NEW_WRITE_LOCKING
#define DECL /* Nothing */
#define LOCKIT down (& port->port_write_sem);
#define RELEASEIT up (&port->port_write_sem);
if (port->xmit_buf) {
free_page((unsigned long) port->xmit_buf);
- port->xmit_buf = 0;
+ port->xmit_buf = NULL;
}
if (port->tty)
port->event = 0;
port->rd->close (port);
port->rd->shutdown_port (port);
- port->tty = 0;
+ port->tty = NULL;
if (port->blocked_open) {
if (port->close_delay) {
}
-int gs_setserial(struct gs_port *port, struct serial_struct *sp)
+int gs_setserial(struct gs_port *port, struct serial_struct __user *sp)
{
struct serial_struct sio;
* Generate the serial struct info.
*/
-int gs_getserial(struct gs_port *port, struct serial_struct *sp)
+int gs_getserial(struct gs_port *port, struct serial_struct __user *sp)
{
struct serial_struct sio;
/*
* Intel & MS High Precision Event Timer Implementation.
- * Contributors:
+ *
+ * Copyright (C) 2003 Intel Corporation
* Venki Pallipadi
- * Bob Picco
+ * (c) Copyright 2004 Hewlett-Packard Development Company, L.P.
+ * Bob Picco <robert.picco@hp.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/config.h>
/* A lock for concurrent intermodule access to hpet and isr hpet activity. */
static spinlock_t hpet_task_lock = SPIN_LOCK_UNLOCKED;
+#define HPET_DEV_NAME (7)
+
struct hpet_dev {
struct hpets *hd_hpets;
struct hpet *hd_hpet;
unsigned int hd_flags;
unsigned int hd_irq;
unsigned int hd_hdwirq;
+ char hd_name[HPET_DEV_NAME];
};
struct hpets {
struct hpets *hpetp;
int i;
+ if (file->f_mode & FMODE_WRITE)
+ return -EINVAL;
+
spin_lock_irq(&hpet_lock);
for (devp = NULL, hpetp = hpets; hpetp && !devp; hpetp = hpetp->hp_next)
}
static ssize_t
-hpet_read(struct file *file, char *buf, size_t count, loff_t * ppos)
+hpet_read(struct file *file, char __user *buf, size_t count, loff_t * ppos)
{
DECLARE_WAITQUEUE(wait, current);
unsigned long data;
add_wait_queue(&devp->hd_waitqueue, &wait);
- do {
- __set_current_state(TASK_INTERRUPTIBLE);
+ for ( ; ; ) {
+ set_current_state(TASK_INTERRUPTIBLE);
spin_lock_irq(&hpet_lock);
data = devp->hd_irqdata;
retval = -ERESTARTSYS;
goto out;
}
-
schedule();
+ }
- } while (1);
-
- retval = put_user(data, (unsigned long *)buf);
+ retval = put_user(data, (unsigned long __user *)buf);
if (!retval)
retval = sizeof(unsigned long);
- out:
- current->state = TASK_RUNNING;
+out:
+ __set_current_state(TASK_RUNNING);
remove_wait_queue(&devp->hd_waitqueue, &wait);
return retval;
static int hpet_mmap(struct file *file, struct vm_area_struct *vma)
{
-#ifdef CONFIG_HPET_NOMMAP
- return -ENOSYS;
-#else
+#ifdef CONFIG_HPET_MMAP
struct hpet_dev *devp;
unsigned long addr;
if (((vma->vm_end - vma->vm_start) != PAGE_SIZE) || vma->vm_pgoff)
return -EINVAL;
- if (vma->vm_flags & VM_WRITE)
- return -EPERM;
-
devp = file->private_data;
addr = (unsigned long)devp->hd_hpet;
}
return 0;
+#else
+ return -ENOSYS;
#endif
}
if (file->f_flags & FASYNC)
hpet_fasync(-1, file, 0);
- file->private_data = 0;
+ file->private_data = NULL;
return 0;
}
irq = devp->hd_hdwirq;
if (irq) {
- char name[7];
-
- sprintf(name, "hpet%d", (int)(devp - hpetp->hp_dev));
+ sprintf(devp->hd_name, "hpet%d", (int)(devp - hpetp->hp_dev));
if (request_irq
- (irq, hpet_interrupt, SA_INTERRUPT, name, (void *)devp)) {
+ (irq, hpet_interrupt, SA_INTERRUPT, devp->hd_name, (void *)devp)) {
printk(KERN_ERR "hpet: IRQ %d is not free\n", irq);
irq = 0;
}
readq(&timer->hpet_config) & Tn_PER_INT_CAP_MASK;
info.hi_hpet = devp->hd_hpets->hp_which;
info.hi_timer = devp - devp->hd_hpets->hp_dev;
- if (copy_to_user((void *)arg, &info, sizeof(info)))
+ if (copy_to_user((void __user *)arg, &info, sizeof(info)))
err = -EFAULT;
break;
}
spin_lock_irq(&hpet_task_lock);
spin_lock(&hpet_lock);
- for (devp = 0, hpetp = hpets; hpetp && !devp; hpetp = hpetp->hp_next)
+ for (devp = NULL, hpetp = hpets; hpetp && !devp; hpetp = hpetp->hp_next)
for (timer = hpetp->hp_hpet->hpet_timers, i = 0;
i < hpetp->hp_ntimer; i++, timer++) {
if ((readq(&timer->hpet_config) & Tn_PER_INT_CAP_MASK)
devp = &hpetp->hp_dev[i];
if (devp->hd_flags & HPET_OPEN || devp->hd_task) {
- devp = 0;
+ devp = NULL;
continue;
}
writeq((readq(&timer->hpet_config) & ~Tn_INT_ENB_CNF_MASK),
&timer->hpet_config);
devp->hd_flags &= ~(HPET_IE | HPET_PERIODIC);
- devp->hd_task = 0;
+ devp->hd_task = NULL;
spin_unlock(&hpet_lock);
spin_unlock_irq(&hpet_task_lock);
static struct ctl_table_header *sysctl_header;
-static void *hpet_start(struct seq_file *s, loff_t * pos)
-{
- struct hpets *hpetp;
- loff_t n;
-
- for (n = *pos, hpetp = hpets; hpetp; hpetp = hpetp->hp_next)
- if (!n--)
- return hpetp;
-
- return 0;
-}
-
-static void *hpet_next(struct seq_file *s, void *v, loff_t * pos)
-{
- struct hpets *hpetp;
-
- hpetp = v;
- ++*pos;
- return hpetp->hp_next;
-}
-
-static void hpet_stop(struct seq_file *s, void *v)
-{
- return;
-}
-
-static int hpet_show(struct seq_file *s, void *v)
-{
- struct hpets *hpetp;
- struct hpet *hpet;
- u64 cap, vendor, period;
-
- hpetp = v;
- hpet = hpetp->hp_hpet;
-
- cap = readq(&hpet->hpet_cap);
- period = (cap & HPET_COUNTER_CLK_PERIOD_MASK) >>
- HPET_COUNTER_CLK_PERIOD_SHIFT;
- vendor = (cap & HPET_VENDOR_ID_MASK) >> HPET_VENDOR_ID_SHIFT;
-
- seq_printf(s,
- "HPET%d period = %d 10**-15 vendor = 0x%x number timer = %d\n",
- hpetp->hp_which, (u32) period, (u32) vendor,
- hpetp->hp_ntimer);
-
- return 0;
-}
-
-static struct seq_operations hpet_seq_ops = {
- .start = hpet_start,
- .next = hpet_next,
- .stop = hpet_stop,
- .show = hpet_show
-};
-
-static int hpet_proc_open(struct inode *inode, struct file *file)
-{
- return seq_open(file, &hpet_seq_ops);
-}
-
-static struct file_operations hpet_proc_fops = {
- .open = hpet_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release
-};
-
/*
* Adjustment for when arming the timer with
* initial conditions. That is, main counter
static unsigned long __init hpet_calibrate(struct hpets *hpetp)
{
- struct hpet_timer *timer;
+ struct hpet_timer *timer = NULL;
unsigned long t, m, count, i, flags, start;
struct hpet_dev *devp;
int j;
struct hpet *hpet;
- for (timer = 0, j = 0, devp = hpetp->hp_dev; j < hpetp->hp_ntimer;
- j++, devp++)
+ for (j = 0, devp = hpetp->hp_dev; j < hpetp->hp_ntimer; j++, devp++)
if ((devp->hd_flags & HPET_OPEN) == 0) {
timer = devp->hd_timer;
break;
hdp->hd_nirqs = irqp->number_of_interrupts;
for (i = 0; i < hdp->hd_nirqs; i++)
-#ifdef CONFIG_IA64
hdp->hd_irq[i] =
acpi_register_gsi(irqp->interrupts[i],
irqp->edge_level,
irqp->active_high_low);
-#else
- hdp->hd_irq[i] = irqp->interrupts[i];
-#endif
}
}
static int __init hpet_init(void)
{
- struct proc_dir_entry *entry;
-
(void)acpi_bus_register_driver(&hpet_acpi_driver);
if (hpets) {
if (misc_register(&hpet_misc))
return -ENODEV;
- entry = create_proc_entry("driver/hpet", 0, 0);
-
- if (entry)
- entry->proc_fops = &hpet_proc_fops;
-
sysctl_header = register_sysctl_table(dev_root, 0);
#ifdef CONFIG_TIME_INTERPOLATION
{
acpi_bus_unregister_driver(&hpet_acpi_driver);
- if (hpets) {
+ if (hpets)
unregister_sysctl_table(sysctl_header);
- remove_proc_entry("driver/hpet", NULL);
- }
return;
}
--- /dev/null
+/*
+ * IBM eServer Hypervisor Virtual Console Server Device Driver
+ * Copyright (C) 2003, 2004 IBM Corp.
+ * Ryan S. Arnold (rsa@us.ibm.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
+ *
+ * Author(s) : Ryan S. Arnold <rsa@us.ibm.com>
+ *
+ * This is the device driver for the IBM Hypervisor Virtual Console Server,
+ * "hvcs". The IBM hvcs provides a tty driver interface to allow Linux
+ * user space applications access to the system consoles of logically
+ * partitioned operating systems, e.g. Linux, running on the same partitioned
+ * Power5 ppc64 system. Physical hardware consoles per partition are not
+ * practical on this hardware so system consoles are accessed by this driver
+ * using inter-partition firmware interfaces to virtual terminal devices.
+ *
+ * A vty is known to the HMC as a "virtual serial server adapter". It is a
+ * virtual terminal device that is created by firmware upon partition creation
+ * to act as a partitioned OS's console device.
+ *
+ * Firmware dynamically (via hotplug) exposes vty-servers to a running ppc64
+ * Linux system upon their creation by the HMC or their exposure during boot.
+ * The non-user interactive backend of this driver is implemented as a vio
+ * device driver so that it can receive notification of vty-server lifetimes
+ * after it registers with the vio bus to handle vty-server probe and remove
+ * callbacks.
+ *
+ * Many vty-servers can be configured to connect to one vty, but a vty can
+ * only be actively connected to by a single vty-server, in any manner, at one
+ * time. If the HMC is currently hosting the console for a target Linux
+ * partition; attempts to open the tty device to the partition's console using
+ * the hvcs on any partition will return -EBUSY with every open attempt until
+ * the HMC frees the connection between its vty-server and the desired
+ * partition's vty device. Conversely, a vty-server may only be connected to
+ * a single vty at one time even though it may have several configured vty
+ * partner possibilities.
+ *
+ * Firmware does not provide notification of vty partner changes to this
+ * driver. This means that an HMC Super Admin may add or remove partner vtys
+ * from a vty-server's partner list but the changes will not be signaled to
+ * the vty-server. Firmware only notifies the driver when a vty-server is
+ * added or removed from the system. To compensate for this deficiency, this
+ * driver implements a sysfs update attribute which provides a method for
+ * rescanning partner information upon a user's request.
+ *
+ * Each vty-server, prior to being exposed to this driver is reference counted
+ * using the 2.6 Linux kernel kobject construct. This kobject is also used by
+ * the vio bus to provide a vio device sysfs entry that this driver attaches
+ * device specific attributes to, including partner information. The vio bus
+ * framework also provides a sysfs entry for each vio driver. The hvcs driver
+ * provides driver attributes in this entry.
+ *
+ * For direction on installation and usage of this driver please reference
+ * Documentation/powerpc/hvcs.txt.
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/kthread.h>
+#include <linux/list.h>
+#include <linux/major.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <asm/hvconsole.h>
+#include <asm/hvcserver.h>
+#include <asm/uaccess.h>
+#include <asm/vio.h>
+
+/*
+ * 1.0.0 -> 1.1.0 Added kernel_thread scheduling methodology to driver to
+ * replace wait_task constructs.
+ *
+ * 1.1.0 -> 1.2.0 Moved pi_buff initialization out of arch code into driver code
+ * and added locking to share this buffer between hvcs_struct instances. This
+ * is because the page_size kmalloc can't be done with a spin_lock held.
+ *
+ * Also added sysfs attribute to manually disconnect the vty-server from the vty
+ * due to stupid firmware behavior when opening the connection then sending data
+ * then then quickly closing the connection would cause data loss on the
+ * receiving side. This required some reordering of the termination code.
+ *
+ * Fixed the hangup scenario and fixed memory leaks on module_exit.
+ *
+ * 1.2.0 -> 1.3.0 Moved from manual kernel thread creation & execution to
+ * kthread construct which replaced in-kernel IPC for thread termination with
+ * kthread_stop and kthread_should_stop. Explicit wait_queue handling was
+ * removed because kthread handles this. Minor bug fix to postpone partner_info
+ * clearing on hvcs_close until adapter removal to preserve context data for
+ * printk on partner connection free. Added lock to protect hvcs_structs so
+ * that hvcs_struct instances aren't added or removed during list traversal.
+ * Cleaned up comment style, added spaces after commas, and broke function
+ * declaration lines to be under 80 columns.
+ */
+#define HVCS_DRIVER_VERSION "1.3.0"
+
+MODULE_AUTHOR("Ryan S. Arnold <rsa@us.ibm.com>");
+MODULE_DESCRIPTION("IBM hvcs (Hypervisor Virtual Console Server) Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(HVCS_DRIVER_VERSION);
+
+/*
+ * Since the Linux TTY code does not currently (2-04-2004) support dynamic
+ * addition of tty derived devices and we shouldn't allocate thousands of
+ * tty_device pointers when the number of vty-server & vty partner connections
+ * will most often be much lower than this, we'll arbitrarily allocate
+ * HVCS_DEFAULT_SERVER_ADAPTERS tty_structs and cdev's by default when we
+ * register the tty_driver. This can be overridden using an insmod parameter.
+ */
+#define HVCS_DEFAULT_SERVER_ADAPTERS 64
+
+/*
+ * The user can't insmod with more than HVCS_MAX_SERVER_ADAPTERS hvcs device
+ * nodes as a sanity check. Theoretically there can be over 1 Billion
+ * vty-server & vty partner connections.
+ */
+#define HVCS_MAX_SERVER_ADAPTERS 1024
+
+/*
+ * We let Linux assign us a major number and we start the minors at zero. There
+ * is no intuitive mapping between minor number and the target partition. The
+ * mapping of minor number is related to the order the vty-servers are exposed
+ * to this driver via the hvcs_probe function.
+ */
+#define HVCS_MINOR_START 0
+
+/*
+ * The hcall interface involves putting 8 chars into each of two registers.
+ * We load up those 2 registers (in arch/ppc64/hvconsole.c) by casting char[16]
+ * to long[2]. It would work without __ALIGNED__, but a little (tiny) bit
+ * slower because an unaligned load is slower than aligned load.
+ */
+#define __ALIGNED__ __attribute__((__aligned__(8)))
+
+/* Converged location code string length + 1 null terminator */
+#define CLC_LENGTH 80
+
+/*
+ * How much data can firmware send with each hvc_put_chars()? Maybe this
+ * should be moved into an architecture specific area.
+ */
+#define HVCS_BUFF_LEN 16
+
+/*
+ * This is the maximum amount of data we'll let the user send us (hvcs_write) at
+ * once in a chunk as a sanity check.
+ */
+#define HVCS_MAX_FROM_USER 4096
+
+/*
+ * Be careful when adding flags to this line discipline. Don't add anything
+ * that will cause echoing or we'll go into recursive loop echoing chars back
+ * and forth with the console drivers.
+ */
+static struct termios hvcs_tty_termios = {
+ .c_iflag = IGNBRK | IGNPAR,
+ .c_oflag = OPOST,
+ .c_cflag = B38400 | CS8 | CREAD | HUPCL,
+ .c_cc = INIT_C_CC
+};
+
+/*
+ * This value is used to take the place of a command line parameter when the
+ * module is inserted. It starts as -1 and stays as such if the user doesn't
+ * specify a module insmod parameter. If they DO specify one then it is set to
+ * the value of the integer passed in.
+ */
+static int hvcs_parm_num_devs = -1;
+module_param(hvcs_parm_num_devs, int, 0);
+
+char hvcs_driver_name[] = "hvcs";
+char hvcs_device_node[] = "hvcs";
+char hvcs_driver_string[]
+ = "IBM hvcs (Hypervisor Virtual Console Server) Driver";
+
+/* Status of partner info rescan triggered via sysfs. */
+static int hvcs_rescan_status = 0;
+
+static struct tty_driver *hvcs_tty_driver;
+
+/*
+ * This is used to associate a vty-server, as it is exposed to this driver, with
+ * a preallocated tty_struct.index. The dev node and hvcs index numbers are not
+ * re-used after device removal otherwise removing and adding a new one would
+ * link a /dev/hvcs* entry to a different vty-server than it did before the
+ * removal. Incidentally, a newly exposed vty-server will always map to an
+ * incrementally higher /dev/hvcs* entry than the last exposed vty-server.
+ */
+static int hvcs_struct_count = -1;
+
+/*
+ * Used by the khvcsd to pick up I/O operations when the kernel_thread is
+ * already awake but potentially shifted to TASK_INTERRUPTIBLE state.
+ */
+static int hvcs_kicked = 0;
+
+/* Used the the kthread construct for task operations */
+static struct task_struct *hvcs_task;
+
+/*
+ * We allocate this for the use of all of the hvcs_structs when they fetch
+ * partner info.
+ */
+static unsigned long *hvcs_pi_buff;
+
+static spinlock_t hvcs_pi_lock;
+
+/* One vty-server per hvcs_struct */
+struct hvcs_struct {
+ spinlock_t lock;
+
+ /*
+ * This index identifies this hvcs device as the complement to a
+ * specific tty index.
+ */
+ unsigned int index;
+
+ struct tty_struct *tty;
+ unsigned int open_count;
+
+ /*
+ * Used to tell the driver kernel_thread what operations need to take
+ * place upon this hvcs_struct instance.
+ */
+ int todo_mask;
+
+ /*
+ * This buffer is required so that when hvcs_write_room() reports that
+ * it can send HVCS_BUFF_LEN characters that it will buffer the full
+ * HVCS_BUFF_LEN characters if need be. This is essential for opost
+ * writes since they do not do high level buffering and expect to be
+ * able to send what the driver commits to sending buffering
+ * [e.g. tab to space conversions in n_tty.c opost()].
+ */
+ char buffer[HVCS_BUFF_LEN];
+ int chars_in_buffer;
+
+ /*
+ * Any variable below the kobject is valid before a tty is connected and
+ * stays valid after the tty is disconnected. These shouldn't be
+ * whacked until the koject refcount reaches zero though some entries
+ * may be changed via sysfs initiatives.
+ */
+ struct kobject kobj; /* ref count & hvcs_struct lifetime */
+ int connected; /* is the vty-server currently connected to a vty? */
+ unsigned int p_unit_address; /* partner unit address */
+ unsigned int p_partition_ID; /* partner partition ID */
+ char p_location_code[CLC_LENGTH];
+ struct list_head next; /* list management */
+ struct vio_dev *vdev;
+};
+
+/* Required to back map a kobject to its containing object */
+#define from_kobj(kobj) container_of(kobj, struct hvcs_struct, kobj)
+
+static struct list_head hvcs_structs = LIST_HEAD_INIT(hvcs_structs);
+static spinlock_t hvcs_structs_lock;
+
+static void hvcs_unthrottle(struct tty_struct *tty);
+static void hvcs_throttle(struct tty_struct *tty);
+static irqreturn_t hvcs_handle_interrupt(int irq, void *dev_instance,
+ struct pt_regs *regs);
+
+static int hvcs_write(struct tty_struct *tty, int from_user,
+ const unsigned char *buf, int count);
+static int hvcs_write_room(struct tty_struct *tty);
+static int hvcs_chars_in_buffer(struct tty_struct *tty);
+
+static int hvcs_has_pi(struct hvcs_struct *hvcsd);
+static void hvcs_set_pi(struct hvcs_partner_info *pi,
+ struct hvcs_struct *hvcsd);
+static int hvcs_get_pi(struct hvcs_struct *hvcsd);
+static int hvcs_rescan_devices_list(void);
+
+static int hvcs_partner_connect(struct hvcs_struct *hvcsd);
+static void hvcs_partner_free(struct hvcs_struct *hvcsd);
+
+static int hvcs_enable_device(struct hvcs_struct *hvcsd,
+ uint32_t unit_address, unsigned int irq, struct vio_dev *dev);
+static void hvcs_final_close(struct hvcs_struct *hvcsd);
+
+static void destroy_hvcs_struct(struct kobject *kobj);
+static int hvcs_open(struct tty_struct *tty, struct file *filp);
+static void hvcs_close(struct tty_struct *tty, struct file *filp);
+static void hvcs_hangup(struct tty_struct * tty);
+
+static void hvcs_create_device_attrs(struct hvcs_struct *hvcsd);
+static void hvcs_remove_device_attrs(struct vio_dev *vdev);
+static void hvcs_create_driver_attrs(void);
+static void hvcs_remove_driver_attrs(void);
+
+static int __devinit hvcs_probe(struct vio_dev *dev,
+ const struct vio_device_id *id);
+static int __devexit hvcs_remove(struct vio_dev *dev);
+static int __init hvcs_module_init(void);
+static void __exit hvcs_module_exit(void);
+
+#define HVCS_SCHED_READ 0x00000001
+#define HVCS_QUICK_READ 0x00000002
+#define HVCS_TRY_WRITE 0x00000004
+#define HVCS_READ_MASK (HVCS_SCHED_READ | HVCS_QUICK_READ)
+
+static void hvcs_kick(void)
+{
+ hvcs_kicked = 1;
+ wmb();
+ wake_up_process(hvcs_task);
+}
+
+static void hvcs_unthrottle(struct tty_struct *tty)
+{
+ struct hvcs_struct *hvcsd = tty->driver_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hvcsd->lock, flags);
+ hvcsd->todo_mask |= HVCS_SCHED_READ;
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ hvcs_kick();
+}
+
+static void hvcs_throttle(struct tty_struct *tty)
+{
+ struct hvcs_struct *hvcsd = tty->driver_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hvcsd->lock, flags);
+ vio_disable_interrupts(hvcsd->vdev);
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+}
+
+/*
+ * If the device is being removed we don't have to worry about this interrupt
+ * handler taking any further interrupts because they are disabled which means
+ * the hvcs_struct will always be valid in this handler.
+ */
+static irqreturn_t hvcs_handle_interrupt(int irq, void *dev_instance,
+ struct pt_regs *regs)
+{
+ struct hvcs_struct *hvcsd = dev_instance;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hvcsd->lock, flags);
+ vio_disable_interrupts(hvcsd->vdev);
+ hvcsd->todo_mask |= HVCS_SCHED_READ;
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ hvcs_kick();
+
+ return IRQ_HANDLED;
+}
+
+/* This function must be called with the hvcsd->lock held */
+static void hvcs_try_write(struct hvcs_struct *hvcsd)
+{
+ unsigned int unit_address = hvcsd->vdev->unit_address;
+ struct tty_struct *tty = hvcsd->tty;
+ int sent;
+
+ if (hvcsd->todo_mask & HVCS_TRY_WRITE) {
+ /* won't send partial writes */
+ sent = hvc_put_chars(unit_address,
+ &hvcsd->buffer[0],
+ hvcsd->chars_in_buffer );
+ if (sent > 0) {
+ hvcsd->chars_in_buffer = 0;
+ wmb();
+ hvcsd->todo_mask &= ~(HVCS_TRY_WRITE);
+ wmb();
+
+ /*
+ * We are still obligated to deliver the data to the
+ * hypervisor even if the tty has been closed because
+ * we commited to delivering it. But don't try to wake
+ * a non-existent tty.
+ */
+ if (tty) {
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+ && tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup) (tty);
+ wake_up_interruptible(&tty->write_wait);
+ }
+ }
+ }
+}
+
+static int hvcs_io(struct hvcs_struct *hvcsd)
+{
+ unsigned int unit_address;
+ struct tty_struct *tty;
+ char buf[HVCS_BUFF_LEN] __ALIGNED__;
+ unsigned long flags;
+ int got;
+ int i;
+
+ spin_lock_irqsave(&hvcsd->lock, flags);
+
+ unit_address = hvcsd->vdev->unit_address;
+ tty = hvcsd->tty;
+
+ hvcs_try_write(hvcsd);
+
+ if (!tty || test_bit(TTY_THROTTLED, &tty->flags)) {
+ hvcsd->todo_mask &= ~(HVCS_READ_MASK);
+ goto bail;
+ } else if (!(hvcsd->todo_mask & (HVCS_READ_MASK)))
+ goto bail;
+
+ /* remove the read masks */
+ hvcsd->todo_mask &= ~(HVCS_READ_MASK);
+
+ if ((tty->flip.count + HVCS_BUFF_LEN) < TTY_FLIPBUF_SIZE) {
+ got = hvc_get_chars(unit_address,
+ &buf[0],
+ HVCS_BUFF_LEN);
+ for (i=0;got && i<got;i++)
+ tty_insert_flip_char(tty, buf[i], TTY_NORMAL);
+ }
+
+ /* Give the TTY time to process the data we just sent. */
+ if (got)
+ hvcsd->todo_mask |= HVCS_QUICK_READ;
+
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ if (tty->flip.count) {
+ /* This is synch because tty->low_latency == 1 */
+ tty_flip_buffer_push(tty);
+ }
+
+ if (!got) {
+ /* Do this _after_ the flip_buffer_push */
+ spin_lock_irqsave(&hvcsd->lock, flags);
+ vio_enable_interrupts(hvcsd->vdev);
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ }
+
+ return hvcsd->todo_mask;
+
+ bail:
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ return hvcsd->todo_mask;
+}
+
+static int khvcsd(void *unused)
+{
+ struct hvcs_struct *hvcsd = NULL;
+ struct list_head *element;
+ struct list_head *safe_temp;
+ int hvcs_todo_mask;
+ unsigned long structs_flags;
+
+ __set_current_state(TASK_RUNNING);
+
+ do {
+ hvcs_todo_mask = 0;
+ hvcs_kicked = 0;
+ wmb();
+
+ spin_lock_irqsave(&hvcs_structs_lock, structs_flags);
+ list_for_each_safe(element, safe_temp, &hvcs_structs) {
+ hvcsd = list_entry(element, struct hvcs_struct, next);
+ hvcs_todo_mask |= hvcs_io(hvcsd);
+ }
+ spin_unlock_irqrestore(&hvcs_structs_lock, structs_flags);
+
+ /*
+ * If any of the hvcs adapters want to try a write or quick read
+ * don't schedule(), yield a smidgen then execute the hvcs_io
+ * thread again for those that want the write.
+ */
+ if (hvcs_todo_mask & (HVCS_TRY_WRITE | HVCS_QUICK_READ)) {
+ yield();
+ continue;
+ }
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (!hvcs_kicked)
+ schedule();
+ __set_current_state(TASK_RUNNING);
+ } while (!kthread_should_stop());
+
+ return 0;
+}
+
+static struct vio_device_id hvcs_driver_table[] __devinitdata= {
+ {"serial-server", "hvterm2"},
+ { 0, }
+};
+MODULE_DEVICE_TABLE(vio, hvcs_driver_table);
+
+/* callback when the kboject ref count reaches zero */
+static void destroy_hvcs_struct(struct kobject *kobj)
+{
+ struct hvcs_struct *hvcsd = from_kobj(kobj);
+ struct vio_dev *vdev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hvcsd->lock, flags);
+
+ /* the list_del poisons the pointers */
+ list_del(&(hvcsd->next));
+
+ if (hvcsd->connected == 1) {
+ hvcs_partner_free(hvcsd);
+ printk(KERN_INFO "HVCS: Closed vty-server@%X and"
+ " partner vty@%X:%d connection.\n",
+ hvcsd->vdev->unit_address,
+ hvcsd->p_unit_address,
+ (unsigned int)hvcsd->p_partition_ID);
+ }
+ printk(KERN_INFO "HVCS: Destroyed hvcs_struct for vty-server@%X.\n",
+ hvcsd->vdev->unit_address);
+
+ vdev = hvcsd->vdev;
+ hvcsd->vdev = NULL;
+
+ hvcsd->p_unit_address = 0;
+ hvcsd->p_partition_ID = 0;
+ memset(&hvcsd->p_location_code[0], 0x00, CLC_LENGTH);
+
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+
+ hvcs_remove_device_attrs(vdev);
+
+ kfree(hvcsd);
+}
+
+/* This function must be called with hvcsd->lock held. */
+static void hvcs_final_close(struct hvcs_struct *hvcsd)
+{
+ vio_disable_interrupts(hvcsd->vdev);
+ free_irq(hvcsd->vdev->irq, hvcsd);
+
+ hvcsd->todo_mask = 0;
+
+ /* These two may be redundant if the operation was a close. */
+ if (hvcsd->tty) {
+ hvcsd->tty->driver_data = NULL;
+ hvcsd->tty = NULL;
+ }
+
+ hvcsd->open_count = 0;
+
+ memset(&hvcsd->buffer[0], 0x00, HVCS_BUFF_LEN);
+ hvcsd->chars_in_buffer = 0;
+}
+
+static struct kobj_type hvcs_kobj_type = {
+ .release = destroy_hvcs_struct,
+};
+
+static int __devinit hvcs_probe(
+ struct vio_dev *dev,
+ const struct vio_device_id *id)
+{
+ struct hvcs_struct *hvcsd;
+ unsigned long structs_flags;
+
+ if (!dev || !id) {
+ printk(KERN_ERR "HVCS: probed with invalid parameter.\n");
+ return -EPERM;
+ }
+
+ hvcsd = kmalloc(sizeof(*hvcsd), GFP_KERNEL);
+ if (!hvcsd) {
+ return -ENODEV;
+ }
+
+ /* hvcsd->tty is zeroed out with the memset */
+ memset(hvcsd, 0x00, sizeof(*hvcsd));
+
+ hvcsd->lock = SPIN_LOCK_UNLOCKED;
+ /* Automatically incs the refcount the first time */
+ kobject_init(&hvcsd->kobj);
+ /* Set up the callback for terminating the hvcs_struct's life */
+ hvcsd->kobj.ktype = &hvcs_kobj_type;
+
+ hvcsd->vdev = dev;
+ dev->dev.driver_data = hvcsd;
+
+ hvcsd->index = ++hvcs_struct_count;
+ hvcsd->chars_in_buffer = 0;
+ hvcsd->todo_mask = 0;
+ hvcsd->connected = 0;
+
+ /*
+ * This will populate the hvcs_struct's partner info fields for the
+ * first time.
+ */
+ if (hvcs_get_pi(hvcsd)) {
+ printk(KERN_ERR "HVCS: Failed to fetch partner"
+ " info for vty-server@%X on device probe.\n",
+ hvcsd->vdev->unit_address);
+ }
+
+ /*
+ * If a user app opens a tty that corresponds to this vty-server before
+ * the hvcs_struct has been added to the devices list then the user app
+ * will get -ENODEV.
+ */
+
+ spin_lock_irqsave(&hvcs_structs_lock, structs_flags);
+
+ list_add_tail(&(hvcsd->next), &hvcs_structs);
+
+ spin_unlock_irqrestore(&hvcs_structs_lock, structs_flags);
+
+ hvcs_create_device_attrs(hvcsd);
+
+ printk(KERN_INFO "HVCS: Added vty-server@%X.\n", dev->unit_address);
+
+ /*
+ * DON'T enable interrupts here because there is no user to receive the
+ * data.
+ */
+ return 0;
+}
+
+static int __devexit hvcs_remove(struct vio_dev *dev)
+{
+ struct hvcs_struct *hvcsd = dev->dev.driver_data;
+ unsigned long flags;
+ struct kobject *kobjp;
+ struct tty_struct *tty;
+
+ if (!hvcsd)
+ return -ENODEV;
+
+ /* By this time the vty-server won't be getting any more interrups */
+
+ spin_lock_irqsave(&hvcsd->lock, flags);
+
+ tty = hvcsd->tty;
+
+ kobjp = &hvcsd->kobj;
+
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+
+ /*
+ * Let the last holder of this object cause it to be removed, which
+ * would probably be tty_hangup below.
+ */
+ kobject_put (kobjp);
+
+ /*
+ * The hangup is a scheduled function which will auto chain call
+ * hvcs_hangup. The tty should always be valid at this time unless a
+ * simultaneous tty close already cleaned up the hvcs_struct.
+ */
+ if (tty)
+ tty_hangup(tty);
+
+ printk(KERN_INFO "HVCS: vty-server@%X removed from the"
+ " vio bus.\n", dev->unit_address);
+ return 0;
+};
+
+static struct vio_driver hvcs_vio_driver = {
+ .name = hvcs_driver_name,
+ .id_table = hvcs_driver_table,
+ .probe = hvcs_probe,
+ .remove = hvcs_remove,
+};
+
+/* Only called from hvcs_get_pi please */
+static void hvcs_set_pi(struct hvcs_partner_info *pi, struct hvcs_struct *hvcsd)
+{
+ int clclength;
+
+ hvcsd->p_unit_address = pi->unit_address;
+ hvcsd->p_partition_ID = pi->partition_ID;
+ clclength = strlen(&pi->location_code[0]);
+ if (clclength > CLC_LENGTH - 1)
+ clclength = CLC_LENGTH - 1;
+
+ /* copy the null-term char too */
+ strncpy(&hvcsd->p_location_code[0],
+ &pi->location_code[0], clclength + 1);
+}
+
+/*
+ * Traverse the list and add the partner info that is found to the hvcs_struct
+ * struct entry. NOTE: At this time I know that partner info will return a
+ * single entry but in the future there may be multiple partner info entries per
+ * vty-server and you'll want to zero out that list and reset it. If for some
+ * reason you have an old version of this driver but there IS more than one
+ * partner info then hvcsd->p_* will hold the last partner info data from the
+ * firmware query. A good way to update this code would be to replace the three
+ * partner info fields in hvcs_struct with a list of hvcs_partner_info
+ * instances.
+ *
+ * This function must be called with the hvcsd->lock held.
+ */
+static int hvcs_get_pi(struct hvcs_struct *hvcsd)
+{
+ /* struct hvcs_partner_info *head_pi = NULL; */
+ struct hvcs_partner_info *pi = NULL;
+ unsigned int unit_address = hvcsd->vdev->unit_address;
+ struct list_head head;
+ unsigned long flags;
+ int retval;
+
+ spin_lock_irqsave(&hvcs_pi_lock, flags);
+ if (!hvcs_pi_buff) {
+ spin_unlock_irqrestore(&hvcs_pi_lock, flags);
+ return -EFAULT;
+ }
+ retval = hvcs_get_partner_info(unit_address, &head, hvcs_pi_buff);
+ spin_unlock_irqrestore(&hvcs_pi_lock, flags);
+ if (retval) {
+ printk(KERN_ERR "HVCS: Failed to fetch partner"
+ " info for vty-server@%x.\n", unit_address);
+ return retval;
+ }
+
+ /* nixes the values if the partner vty went away */
+ hvcsd->p_unit_address = 0;
+ hvcsd->p_partition_ID = 0;
+
+ list_for_each_entry(pi, &head, node)
+ hvcs_set_pi(pi, hvcsd);
+
+ hvcs_free_partner_info(&head);
+ return 0;
+}
+
+/*
+ * This function is executed by the driver "rescan" sysfs entry. It shouldn't
+ * be executed elsewhere, in order to prevent deadlock issues.
+ */
+static int hvcs_rescan_devices_list(void)
+{
+ struct hvcs_struct *hvcsd = NULL;
+ unsigned long flags;
+ unsigned long structs_flags;
+
+ spin_lock_irqsave(&hvcs_structs_lock, structs_flags);
+
+ list_for_each_entry(hvcsd, &hvcs_structs, next) {
+ spin_lock_irqsave(&hvcsd->lock, flags);
+ hvcs_get_pi(hvcsd);
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ }
+
+ spin_unlock_irqrestore(&hvcs_structs_lock, structs_flags);
+
+ return 0;
+}
+
+/*
+ * Farm this off into its own function because it could be more complex once
+ * multiple partners support is added. This function should be called with
+ * the hvcsd->lock held.
+ */
+static int hvcs_has_pi(struct hvcs_struct *hvcsd)
+{
+ if ((!hvcsd->p_unit_address) || (!hvcsd->p_partition_ID))
+ return 0;
+ return 1;
+}
+
+/*
+ * NOTE: It is possible that the super admin removed a partner vty and then
+ * added a different vty as the new partner.
+ *
+ * This function must be called with the hvcsd->lock held.
+ */
+static int hvcs_partner_connect(struct hvcs_struct *hvcsd)
+{
+ int retval;
+ unsigned int unit_address = hvcsd->vdev->unit_address;
+
+ /*
+ * If there wasn't any pi when the device was added it doesn't meant
+ * there isn't any now. This driver isn't notified when a new partner
+ * vty is added to a vty-server so we discover changes on our own.
+ * Please see comments in hvcs_register_connection() for justification
+ * of this bizarre code.
+ */
+ retval = hvcs_register_connection(unit_address,
+ hvcsd->p_partition_ID,
+ hvcsd->p_unit_address);
+ if (!retval) {
+ hvcsd->connected = 1;
+ return 0;
+ } else if (retval != -EINVAL)
+ return retval;
+
+ /*
+ * As per the spec re-get the pi and try again if -EINVAL after the
+ * first connection attempt.
+ */
+ if (hvcs_get_pi(hvcsd))
+ return -ENOMEM;
+
+ if (!hvcs_has_pi(hvcsd))
+ return -ENODEV;
+
+ retval = hvcs_register_connection(unit_address,
+ hvcsd->p_partition_ID,
+ hvcsd->p_unit_address);
+ if (retval != -EINVAL) {
+ hvcsd->connected = 1;
+ return retval;
+ }
+
+ /*
+ * EBUSY is the most likely scenario though the vty could have been
+ * removed or there really could be an hcall error due to the parameter
+ * data but thanks to ambiguous firmware return codes we can't really
+ * tell.
+ */
+ printk(KERN_INFO "HVCS: vty-server or partner"
+ " vty is busy. Try again later.\n");
+ return -EBUSY;
+}
+
+/* This function must be called with the hvcsd->lock held */
+static void hvcs_partner_free(struct hvcs_struct *hvcsd)
+{
+ int retval;
+ do {
+ retval = hvcs_free_connection(hvcsd->vdev->unit_address);
+ } while (retval == -EBUSY);
+ hvcsd->connected = 0;
+}
+
+/* This helper function must be called WITHOUT the hvcsd->lock held */
+static int hvcs_enable_device(struct hvcs_struct *hvcsd, uint32_t unit_address,
+ unsigned int irq, struct vio_dev *vdev)
+{
+ unsigned long flags;
+
+ /*
+ * It is possible that the vty-server was removed between the time that
+ * the conn was registered and now.
+ */
+ if (!request_irq(irq, &hvcs_handle_interrupt,
+ SA_INTERRUPT, "ibmhvcs", hvcsd)) {
+ /*
+ * It is possible the vty-server was removed after the irq was
+ * requested but before we have time to enable interrupts.
+ */
+ if (vio_enable_interrupts(vdev) == H_Success)
+ return 0;
+ else {
+ printk(KERN_ERR "HVCS: int enable failed for"
+ " vty-server@%X.\n", unit_address);
+ free_irq(irq, hvcsd);
+ }
+ } else
+ printk(KERN_ERR "HVCS: irq req failed for"
+ " vty-server@%X.\n", unit_address);
+
+ spin_lock_irqsave(&hvcsd->lock, flags);
+ hvcs_partner_free(hvcsd);
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+
+ return -ENODEV;
+
+}
+
+/*
+ * This always increments the kobject ref count if the call is successful.
+ * Please remember to dec when you are done with the instance.
+ *
+ * NOTICE: Do NOT hold either the hvcs_struct.lock or hvcs_structs_lock when
+ * calling this function or you will get deadlock.
+ */
+struct hvcs_struct *hvcs_get_by_index(int index)
+{
+ struct hvcs_struct *hvcsd = NULL;
+ struct list_head *element;
+ struct list_head *safe_temp;
+ unsigned long flags;
+ unsigned long structs_flags;
+
+ spin_lock_irqsave(&hvcs_structs_lock, structs_flags);
+ /* We can immediately discard OOB requests */
+ if (index >= 0 && index < HVCS_MAX_SERVER_ADAPTERS) {
+ list_for_each_safe(element, safe_temp, &hvcs_structs) {
+ hvcsd = list_entry(element, struct hvcs_struct, next);
+ spin_lock_irqsave(&hvcsd->lock, flags);
+ if (hvcsd->index == index) {
+ kobject_get(&hvcsd->kobj);
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ spin_unlock_irqrestore(&hvcs_structs_lock,
+ structs_flags);
+ return hvcsd;
+ }
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ }
+ hvcsd = NULL;
+ }
+
+ spin_unlock_irqrestore(&hvcs_structs_lock, structs_flags);
+ return hvcsd;
+}
+
+/*
+ * This is invoked via the tty_open interface when a user app connects to the
+ * /dev node.
+ */
+static int hvcs_open(struct tty_struct *tty, struct file *filp)
+{
+ struct hvcs_struct *hvcsd = NULL;
+ int retval = 0;
+ unsigned long flags;
+ unsigned int irq;
+ struct vio_dev *vdev;
+ unsigned long unit_address;
+
+ if (tty->driver_data)
+ goto fast_open;
+
+ /*
+ * Is there a vty-server that shares the same index?
+ * This function increments the kobject index.
+ */
+ if (!(hvcsd = hvcs_get_by_index(tty->index))) {
+ printk(KERN_WARNING "HVCS: open failed, no index.\n");
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&hvcsd->lock, flags);
+
+ if (hvcsd->connected == 0)
+ if ((retval = hvcs_partner_connect(hvcsd)))
+ goto error_release;
+
+ hvcsd->open_count = 1;
+ hvcsd->tty = tty;
+ tty->driver_data = hvcsd;
+
+ /*
+ * Set this driver to low latency so that we actually have a chance at
+ * catching a throttled TTY after we flip_buffer_push. Otherwise the
+ * flush_to_async may not execute until after the kernel_thread has
+ * yielded and resumed the next flip_buffer_push resulting in data
+ * loss.
+ */
+ tty->low_latency = 1;
+
+ memset(&hvcsd->buffer[0], 0x3F, HVCS_BUFF_LEN);
+
+ /*
+ * Save these in the spinlock for the enable operations that need them
+ * outside of the spinlock.
+ */
+ irq = hvcsd->vdev->irq;
+ vdev = hvcsd->vdev;
+ unit_address = hvcsd->vdev->unit_address;
+
+ hvcsd->todo_mask |= HVCS_SCHED_READ;
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+
+ /*
+ * This must be done outside of the spinlock because it requests irqs
+ * and will grab the spinlcok and free the connection if it fails.
+ */
+ if ((hvcs_enable_device(hvcsd, unit_address, irq, vdev))) {
+ kobject_put(&hvcsd->kobj);
+ printk(KERN_WARNING "HVCS: enable device failed.\n");
+ return -ENODEV;
+ }
+
+ goto open_success;
+
+fast_open:
+ hvcsd = tty->driver_data;
+
+ spin_lock_irqsave(&hvcsd->lock, flags);
+ if (!kobject_get(&hvcsd->kobj)) {
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ printk(KERN_ERR "HVCS: Kobject of open"
+ " hvcs doesn't exist.\n");
+ return -EFAULT; /* Is this the right return value? */
+ }
+
+ hvcsd->open_count++;
+
+ hvcsd->todo_mask |= HVCS_SCHED_READ;
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+open_success:
+ hvcs_kick();
+
+ printk(KERN_INFO "HVCS: vty-server@%X opened.\n",
+ hvcsd->vdev->unit_address );
+
+ return 0;
+
+error_release:
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ kobject_put(&hvcsd->kobj);
+
+ printk(KERN_WARNING "HVCS: HVCS partner connect failed.\n");
+ return retval;
+}
+
+static void hvcs_close(struct tty_struct *tty, struct file *filp)
+{
+ struct hvcs_struct *hvcsd;
+ unsigned long flags;
+ struct kobject *kobjp;
+
+ /*
+ * Is someone trying to close the file associated with this device after
+ * we have hung up? If so tty->driver_data wouldn't be valid.
+ */
+ if (tty_hung_up_p(filp))
+ return;
+
+ /*
+ * No driver_data means that this close was probably issued after a
+ * failed hvcs_open by the tty layer's release_dev() api and we can just
+ * exit cleanly.
+ */
+ if (!tty->driver_data)
+ return;
+
+ hvcsd = tty->driver_data;
+
+ spin_lock_irqsave(&hvcsd->lock, flags);
+ if (--hvcsd->open_count == 0) {
+
+ /*
+ * This line is important because it tells hvcs_open that this
+ * device needs to be re-configured the next time hvcs_open is
+ * called.
+ */
+ hvcsd->tty->driver_data = NULL;
+
+ /*
+ * NULL this early so that the kernel_thread doesn't try to
+ * execute any operations on the TTY even though it is obligated
+ * to deliver any pending I/O to the hypervisor.
+ */
+ hvcsd->tty = NULL;
+
+ /*
+ * Block the close until all the buffered data has been
+ * delivered.
+ */
+ while(hvcsd->chars_in_buffer) {
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+
+ /*
+ * Give the kernel thread the hvcs_struct so that it can
+ * try to deliver the remaining data but block the close
+ * operation by spinning in this function so that other
+ * tty operations have to wait.
+ */
+ yield();
+ spin_lock_irqsave(&hvcsd->lock, flags);
+ }
+
+ hvcs_final_close(hvcsd);
+
+ } else if (hvcsd->open_count < 0) {
+ printk(KERN_ERR "HVCS: vty-server@%X open_count: %d"
+ " is missmanaged.\n",
+ hvcsd->vdev->unit_address, hvcsd->open_count);
+ }
+ kobjp = &hvcsd->kobj;
+
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+
+ kobject_put(kobjp);
+}
+
+static void hvcs_hangup(struct tty_struct * tty)
+{
+ struct hvcs_struct *hvcsd = tty->driver_data;
+ unsigned long flags;
+ int temp_open_count;
+ struct kobject *kobjp;
+
+ spin_lock_irqsave(&hvcsd->lock, flags);
+ /* Preserve this so that we know how many kobject refs to put */
+ temp_open_count = hvcsd->open_count;
+
+ /*
+ * Don't kobject put inside the spinlock because the destruction
+ * callback may use the spinlock and it may get called before the
+ * spinlock has been released. Get a pointer to the kobject and
+ * kobject_put on that instead.
+ */
+ kobjp = &hvcsd->kobj;
+
+ /* Calling this will drop any buffered data on the floor. */
+ hvcs_final_close(hvcsd);
+
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+
+ /*
+ * We need to kobject_put() for every open_count we have since the
+ * tty_hangup() function doesn't invoke a close per open connection on a
+ * non-console device.
+ */
+ while(temp_open_count) {
+ --temp_open_count;
+ /*
+ * The final put will trigger destruction of the hvcs_struct.
+ * NOTE: If this hangup was signaled from user space then the
+ * final put will never happen.
+ */
+ kobject_put(kobjp);
+ }
+}
+
+/*
+ * NOTE: This is almost always from_user since user level apps interact with the
+ * /dev nodes. I'm trusting that if hvcs_write gets called and interrupted by
+ * hvcs_remove (which removes the target device and executes tty_hangup()) that
+ * tty_hangup will allow hvcs_write time to complete execution before it
+ * terminates our device.
+ */
+static int hvcs_write(struct tty_struct *tty, int from_user,
+ const unsigned char *buf, int count)
+{
+ struct hvcs_struct *hvcsd = tty->driver_data;
+ unsigned int unit_address;
+ unsigned char *charbuf;
+ unsigned long flags;
+ int total_sent = 0;
+ int tosend = 0;
+ int result = 0;
+
+ /*
+ * If they don't check the return code off of their open they may
+ * attempt this even if there is no connected device.
+ */
+ if (!hvcsd)
+ return -ENODEV;
+
+ /* Reasonable size to prevent user level flooding */
+ if (count > HVCS_MAX_FROM_USER) {
+ printk(KERN_WARNING "HVCS write: count being truncated to"
+ " HVCS_MAX_FROM_USER.\n");
+ count = HVCS_MAX_FROM_USER;
+ }
+
+ if (!from_user)
+ charbuf = (unsigned char *)buf;
+ else {
+ charbuf = kmalloc(count, GFP_KERNEL);
+ if (!charbuf) {
+ printk(KERN_WARNING "HVCS: write -ENOMEM.\n");
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(charbuf, buf, count)) {
+ kfree(charbuf);
+ printk(KERN_WARNING "HVCS: write -EFAULT.\n");
+ return -EFAULT;
+ }
+ }
+
+ spin_lock_irqsave(&hvcsd->lock, flags);
+
+ /*
+ * Somehow an open succedded but the device was removed or the
+ * connection terminated between the vty-server and partner vty during
+ * the middle of a write operation? This is a crummy place to do this
+ * but we want to keep it all in the spinlock.
+ */
+ if (hvcsd->open_count <= 0) {
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ if (from_user)
+ kfree(charbuf);
+ return -ENODEV;
+ }
+
+ unit_address = hvcsd->vdev->unit_address;
+
+ while (count > 0) {
+ tosend = min(count, (HVCS_BUFF_LEN - hvcsd->chars_in_buffer));
+ /*
+ * No more space, this probably means that the last call to
+ * hvcs_write() didn't succeed and the buffer was filled up.
+ */
+ if (!tosend)
+ break;
+
+ memcpy(&hvcsd->buffer[hvcsd->chars_in_buffer],
+ &charbuf[total_sent],
+ tosend);
+
+ hvcsd->chars_in_buffer += tosend;
+
+ result = 0;
+
+ /*
+ * If this is true then we don't want to try writing to the
+ * hypervisor because that is the kernel_threads job now. We'll
+ * just add to the buffer.
+ */
+ if (!(hvcsd->todo_mask & HVCS_TRY_WRITE))
+ /* won't send partial writes */
+ result = hvc_put_chars(unit_address,
+ &hvcsd->buffer[0],
+ hvcsd->chars_in_buffer);
+
+ /*
+ * Since we know we have enough room in hvcsd->buffer for
+ * tosend we record that it was sent regardless of whether the
+ * hypervisor actually took it because we have it buffered.
+ */
+ total_sent+=tosend;
+ count-=tosend;
+ if (result == 0) {
+ hvcsd->todo_mask |= HVCS_TRY_WRITE;
+ hvcs_kick();
+ break;
+ }
+
+ hvcsd->chars_in_buffer = 0;
+ /*
+ * Test after the chars_in_buffer reset otherwise this could
+ * deadlock our writes if hvc_put_chars fails.
+ */
+ if (result < 0)
+ break;
+ }
+
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ if (from_user)
+ kfree(charbuf);
+
+ if (result == -1)
+ return -EIO;
+ else
+ return total_sent;
+}
+
+/*
+ * This is really asking how much can we guarentee that we can send or that we
+ * absolutely WILL BUFFER if we can't send it. This driver MUST honor the
+ * return value, hence the reason for hvcs_struct buffering.
+ */
+static int hvcs_write_room(struct tty_struct *tty)
+{
+ struct hvcs_struct *hvcsd = tty->driver_data;
+ unsigned long flags;
+ int retval;
+
+ if (!hvcsd || hvcsd->open_count <= 0)
+ return 0;
+
+ spin_lock_irqsave(&hvcsd->lock, flags);
+ retval = HVCS_BUFF_LEN - hvcsd->chars_in_buffer;
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ return retval;
+}
+
+static int hvcs_chars_in_buffer(struct tty_struct *tty)
+{
+ struct hvcs_struct *hvcsd = tty->driver_data;
+ unsigned long flags;
+ int retval;
+
+ spin_lock_irqsave(&hvcsd->lock, flags);
+ retval = hvcsd->chars_in_buffer;
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ return retval;
+}
+
+static struct tty_operations hvcs_ops = {
+ .open = hvcs_open,
+ .close = hvcs_close,
+ .hangup = hvcs_hangup,
+ .write = hvcs_write,
+ .write_room = hvcs_write_room,
+ .chars_in_buffer = hvcs_chars_in_buffer,
+ .unthrottle = hvcs_unthrottle,
+ .throttle = hvcs_throttle,
+};
+
+static int __init hvcs_module_init(void)
+{
+ int rc;
+ int num_ttys_to_alloc;
+
+ printk(KERN_INFO "Initializing %s\n", hvcs_driver_string);
+
+ /* Has the user specified an overload with an insmod param? */
+ if (hvcs_parm_num_devs <= 0 ||
+ (hvcs_parm_num_devs > HVCS_MAX_SERVER_ADAPTERS)) {
+ num_ttys_to_alloc = HVCS_DEFAULT_SERVER_ADAPTERS;
+ } else
+ num_ttys_to_alloc = hvcs_parm_num_devs;
+
+ hvcs_tty_driver = alloc_tty_driver(num_ttys_to_alloc);
+ if (!hvcs_tty_driver)
+ return -ENOMEM;
+
+ hvcs_tty_driver->owner = THIS_MODULE;
+
+ hvcs_tty_driver->driver_name = hvcs_driver_name;
+ hvcs_tty_driver->name = hvcs_device_node;
+
+ /*
+ * We'll let the system assign us a major number, indicated by leaving
+ * it blank.
+ */
+
+ hvcs_tty_driver->minor_start = HVCS_MINOR_START;
+ hvcs_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM;
+
+ /*
+ * We role our own so that we DONT ECHO. We can't echo because the
+ * device we are connecting to already echoes by default and this would
+ * throw us into a horrible recursive echo-echo-echo loop.
+ */
+ hvcs_tty_driver->init_termios = hvcs_tty_termios;
+ hvcs_tty_driver->flags = TTY_DRIVER_REAL_RAW;
+
+ tty_set_operations(hvcs_tty_driver, &hvcs_ops);
+
+ /*
+ * The following call will result in sysfs entries that denote the
+ * dynamically assigned major and minor numbers for our devices.
+ */
+ if (tty_register_driver(hvcs_tty_driver)) {
+ printk(KERN_ERR "HVCS: registration "
+ " as a tty driver failed.\n");
+ put_tty_driver(hvcs_tty_driver);
+ return rc;
+ }
+
+ hvcs_structs_lock = SPIN_LOCK_UNLOCKED;
+
+ hvcs_pi_lock = SPIN_LOCK_UNLOCKED;
+ hvcs_pi_buff = kmalloc(PAGE_SIZE, GFP_KERNEL);
+
+ hvcs_task = kthread_run(khvcsd, NULL, "khvcsd");
+ if (IS_ERR(hvcs_task)) {
+ printk("khvcsd creation failed. Driver not loaded.\n");
+ kfree(hvcs_pi_buff);
+ put_tty_driver(hvcs_tty_driver);
+ return -EIO;
+ }
+
+ rc = vio_register_driver(&hvcs_vio_driver);
+
+ /*
+ * This needs to be done AFTER the vio_register_driver() call or else
+ * the kobjects won't be initialized properly.
+ */
+ hvcs_create_driver_attrs();
+
+ printk(KERN_INFO "HVCS: driver module inserted.\n");
+
+ return rc;
+}
+
+static void __exit hvcs_module_exit(void)
+{
+ unsigned long flags;
+
+ /*
+ * This driver receives hvcs_remove callbacks for each device upon
+ * module removal.
+ */
+
+ /*
+ * This synchronous operation will wake the khvcsd kthread if it is
+ * asleep and will return when khvcsd has terminated.
+ */
+ kthread_stop(hvcs_task);
+
+ spin_lock_irqsave(&hvcs_pi_lock, flags);
+ kfree(hvcs_pi_buff);
+ hvcs_pi_buff = NULL;
+ spin_unlock_irqrestore(&hvcs_pi_lock, flags);
+
+ hvcs_remove_driver_attrs();
+
+ vio_unregister_driver(&hvcs_vio_driver);
+
+ tty_unregister_driver(hvcs_tty_driver);
+
+ put_tty_driver(hvcs_tty_driver);
+
+ printk(KERN_INFO "HVCS: driver module removed.\n");
+}
+
+module_init(hvcs_module_init);
+module_exit(hvcs_module_exit);
+
+static inline struct hvcs_struct *from_vio_dev(struct vio_dev *viod)
+{
+ return viod->dev.driver_data;
+}
+/* The sysfs interface for the driver and devices */
+
+static ssize_t hvcs_partner_vtys_show(struct device *dev, char *buf)
+{
+ struct vio_dev *viod = to_vio_dev(dev);
+ struct hvcs_struct *hvcsd = from_vio_dev(viod);
+ unsigned long flags;
+ int retval;
+
+ spin_lock_irqsave(&hvcsd->lock, flags);
+ retval = sprintf(buf, "%X\n", hvcsd->p_unit_address);
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ return retval;
+}
+static DEVICE_ATTR(partner_vtys, S_IRUGO, hvcs_partner_vtys_show, NULL);
+
+static ssize_t hvcs_partner_clcs_show(struct device *dev, char *buf)
+{
+ struct vio_dev *viod = to_vio_dev(dev);
+ struct hvcs_struct *hvcsd = from_vio_dev(viod);
+ unsigned long flags;
+ int retval;
+
+ spin_lock_irqsave(&hvcsd->lock, flags);
+ retval = sprintf(buf, "%s\n", &hvcsd->p_location_code[0]);
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ return retval;
+}
+static DEVICE_ATTR(partner_clcs, S_IRUGO, hvcs_partner_clcs_show, NULL);
+
+static ssize_t hvcs_current_vty_store(struct device *dev, const char * buf,
+ size_t count)
+{
+ /*
+ * Don't need this feature at the present time because firmware doesn't
+ * yet support multiple partners.
+ */
+ printk(KERN_INFO "HVCS: Denied current_vty change: -EPERM.\n");
+ return -EPERM;
+}
+
+static ssize_t hvcs_current_vty_show(struct device *dev, char *buf)
+{
+ struct vio_dev *viod = to_vio_dev(dev);
+ struct hvcs_struct *hvcsd = from_vio_dev(viod);
+ unsigned long flags;
+ int retval;
+
+ spin_lock_irqsave(&hvcsd->lock, flags);
+ retval = sprintf(buf, "%s\n", &hvcsd->p_location_code[0]);
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ return retval;
+}
+
+static DEVICE_ATTR(current_vty,
+ S_IRUGO | S_IWUSR, hvcs_current_vty_show, hvcs_current_vty_store);
+
+static ssize_t hvcs_vterm_state_store(struct device *dev, const char *buf,
+ size_t count)
+{
+ struct vio_dev *viod = to_vio_dev(dev);
+ struct hvcs_struct *hvcsd = from_vio_dev(viod);
+ unsigned long flags;
+
+ /* writing a '0' to this sysfs entry will result in the disconnect. */
+ if (simple_strtol(buf, NULL, 0) != 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&hvcsd->lock, flags);
+
+ if (hvcsd->open_count > 0) {
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ printk(KERN_INFO "HVCS: vterm state unchanged. "
+ "The hvcs device node is still in use.\n");
+ return -EPERM;
+ }
+
+ if (hvcsd->connected == 0) {
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ printk(KERN_INFO "HVCS: vterm state unchanged. The"
+ " vty-server is not connected to a vty.\n");
+ return -EPERM;
+ }
+
+ hvcs_partner_free(hvcsd);
+ printk(KERN_INFO "HVCS: Closed vty-server@%X and"
+ " partner vty@%X:%d connection.\n",
+ hvcsd->vdev->unit_address,
+ hvcsd->p_unit_address,
+ (unsigned int)hvcsd->p_partition_ID);
+
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ return count;
+}
+
+static ssize_t hvcs_vterm_state_show(struct device *dev, char *buf)
+{
+ struct vio_dev *viod = to_vio_dev(dev);
+ struct hvcs_struct *hvcsd = from_vio_dev(viod);
+ unsigned long flags;
+ int retval;
+
+ spin_lock_irqsave(&hvcsd->lock, flags);
+ retval = sprintf(buf, "%d\n", hvcsd->connected);
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ return retval;
+}
+static DEVICE_ATTR(vterm_state, S_IRUGO | S_IWUSR,
+ hvcs_vterm_state_show, hvcs_vterm_state_store);
+
+static struct attribute *hvcs_attrs[] = {
+ &dev_attr_partner_vtys.attr,
+ &dev_attr_partner_clcs.attr,
+ &dev_attr_current_vty.attr,
+ &dev_attr_vterm_state.attr,
+ NULL,
+};
+
+static struct attribute_group hvcs_attr_group = {
+ .attrs = hvcs_attrs,
+};
+
+static void hvcs_create_device_attrs(struct hvcs_struct *hvcsd)
+{
+ struct vio_dev *vdev = hvcsd->vdev;
+ sysfs_create_group(&vdev->dev.kobj, &hvcs_attr_group);
+}
+
+static void hvcs_remove_device_attrs(struct vio_dev *vdev)
+{
+ sysfs_remove_group(&vdev->dev.kobj, &hvcs_attr_group);
+}
+
+static ssize_t hvcs_rescan_show(struct device_driver *ddp, char *buf)
+{
+ /* A 1 means it is updating, a 0 means it is done updating */
+ return snprintf(buf, PAGE_SIZE, "%d\n", hvcs_rescan_status);
+}
+
+static ssize_t hvcs_rescan_store(struct device_driver *ddp, const char * buf,
+ size_t count)
+{
+ if ((simple_strtol(buf, NULL, 0) != 1)
+ && (hvcs_rescan_status != 0))
+ return -EINVAL;
+
+ hvcs_rescan_status = 1;
+ printk(KERN_INFO "HVCS: rescanning partner info for all"
+ " vty-servers.\n");
+ hvcs_rescan_devices_list();
+ hvcs_rescan_status = 0;
+ return count;
+}
+static DRIVER_ATTR(rescan,
+ S_IRUGO | S_IWUSR, hvcs_rescan_show, hvcs_rescan_store);
+
+static void hvcs_create_driver_attrs(void)
+{
+ struct device_driver *driverfs = &(hvcs_vio_driver.driver);
+ driver_create_file(driverfs, &driver_attr_rescan);
+}
+
+static void hvcs_remove_driver_attrs(void)
+{
+ struct device_driver *driverfs = &(hvcs_vio_driver.driver);
+ driver_remove_file(driverfs, &driver_attr_rescan);
+}
--- /dev/null
+/*
+ * Driver for the SGS-Thomson M48T35 Timekeeper RAM chip
+ *
+ * Real Time Clock interface for Linux
+ *
+ * TODO: Implement periodic interrupts.
+ *
+ * Copyright (C) 2000 Silicon Graphics, Inc.
+ * Written by Ulf Carlsson (ulfc@engr.sgi.com)
+ *
+ * Based on code written by Paul Gortmaker.
+ *
+ * This driver allows use of the real time clock (built into
+ * nearly all computers) from user space. It exports the /dev/rtc
+ * interface supporting various ioctl() and also the /proc/rtc
+ * pseudo-file for status information.
+ *
+ * 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.
+ *
+ */
+
+#define RTC_VERSION "1.09b"
+
+#include <linux/bcd.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/smp_lock.h>
+
+#include <asm/m48t35.h>
+#include <asm/sn/ioc3.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/sn/klconfig.h>
+#include <asm/sn/sn0/ip27.h>
+#include <asm/sn/sn0/hub.h>
+#include <asm/sn/sn_private.h>
+
+static int rtc_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+
+static int rtc_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+
+static void get_rtc_time(struct rtc_time *rtc_tm);
+
+/*
+ * Bits in rtc_status. (6 bits of room for future expansion)
+ */
+
+#define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */
+#define RTC_TIMER_ON 0x02 /* missed irq timer active */
+
+static unsigned char rtc_status; /* bitmapped status byte. */
+static unsigned long rtc_freq; /* Current periodic IRQ rate */
+static struct m48t35_rtc *rtc;
+
+/*
+ * If this driver ever becomes modularised, it will be really nice
+ * to make the epoch retain its value across module reload...
+ */
+
+static unsigned long epoch = 1970; /* year corresponding to 0x00 */
+
+static const unsigned char days_in_mo[] =
+{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+
+ struct rtc_time wtime;
+
+ switch (cmd) {
+ case RTC_RD_TIME: /* Read the time/date from RTC */
+ {
+ get_rtc_time(&wtime);
+ break;
+ }
+ case RTC_SET_TIME: /* Set the RTC */
+ {
+ struct rtc_time rtc_tm;
+ unsigned char mon, day, hrs, min, sec, leap_yr;
+ unsigned int yrs;
+
+ if (!capable(CAP_SYS_TIME))
+ return -EACCES;
+
+ if (copy_from_user(&rtc_tm, (struct rtc_time*)arg,
+ sizeof(struct rtc_time)))
+ return -EFAULT;
+
+ yrs = rtc_tm.tm_year + 1900;
+ mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */
+ day = rtc_tm.tm_mday;
+ hrs = rtc_tm.tm_hour;
+ min = rtc_tm.tm_min;
+ sec = rtc_tm.tm_sec;
+
+ if (yrs < 1970)
+ return -EINVAL;
+
+ leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
+
+ if ((mon > 12) || (day == 0))
+ return -EINVAL;
+
+ if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
+ return -EINVAL;
+
+ if ((hrs >= 24) || (min >= 60) || (sec >= 60))
+ return -EINVAL;
+
+ if ((yrs -= epoch) > 255) /* They are unsigned */
+ return -EINVAL;
+
+ if (yrs > 169)
+ return -EINVAL;
+
+ if (yrs >= 100)
+ yrs -= 100;
+
+ sec = BIN2BCD(sec);
+ min = BIN2BCD(min);
+ hrs = BIN2BCD(hrs);
+ day = BIN2BCD(day);
+ mon = BIN2BCD(mon);
+ yrs = BIN2BCD(yrs);
+
+ spin_lock_irq(&rtc_lock);
+ rtc->control |= M48T35_RTC_SET;
+ rtc->year = yrs;
+ rtc->month = mon;
+ rtc->date = day;
+ rtc->hour = hrs;
+ rtc->min = min;
+ rtc->sec = sec;
+ rtc->control &= ~M48T35_RTC_SET;
+ spin_unlock_irq(&rtc_lock);
+
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+ return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
+}
+
+/*
+ * We enforce only one user at a time here with the open/close.
+ * Also clear the previous interrupt data on an open, and clean
+ * up things on a close.
+ */
+
+static int rtc_open(struct inode *inode, struct file *file)
+{
+ spin_lock_irq(&rtc_lock);
+
+ if (rtc_status & RTC_IS_OPEN) {
+ spin_unlock_irq(&rtc_lock);
+ return -EBUSY;
+ }
+
+ rtc_status |= RTC_IS_OPEN;
+ spin_unlock_irq(&rtc_lock);
+
+ return 0;
+}
+
+static int rtc_release(struct inode *inode, struct file *file)
+{
+ /*
+ * Turn off all interrupts once the device is no longer
+ * in use, and clear the data.
+ */
+
+ spin_lock_irq(&rtc_lock);
+ rtc_status &= ~RTC_IS_OPEN;
+ spin_unlock_irq(&rtc_lock);
+
+ return 0;
+}
+
+/*
+ * The various file operations we support.
+ */
+
+static struct file_operations rtc_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = rtc_ioctl,
+ .open = rtc_open,
+ .release = rtc_release,
+};
+
+static struct miscdevice rtc_dev=
+{
+ RTC_MINOR,
+ "rtc",
+ &rtc_fops
+};
+
+static int __init rtc_init(void)
+{
+ rtc = (struct m48t35_rtc *)
+ (KL_CONFIG_CH_CONS_INFO(master_nasid)->memory_base + IOC3_BYTEBUS_DEV0);
+
+ printk(KERN_INFO "Real Time Clock Driver v%s\n", RTC_VERSION);
+ if (misc_register(&rtc_dev)) {
+ printk(KERN_ERR "rtc: cannot register misc device.\n");
+ return -ENODEV;
+ }
+ if (!create_proc_read_entry("driver/rtc", 0, NULL, rtc_read_proc, NULL)) {
+ printk(KERN_ERR "rtc: cannot create /proc/rtc.\n");
+ misc_deregister(&rtc_dev);
+ return -ENOENT;
+ }
+
+ rtc_freq = 1024;
+
+ return 0;
+}
+
+static void __exit rtc_exit (void)
+{
+ /* interrupts and timer disabled at this point by rtc_release */
+
+ remove_proc_entry ("rtc", NULL);
+ misc_deregister(&rtc_dev);
+}
+
+module_init(rtc_init);
+module_exit(rtc_exit);
+
+/*
+ * Info exported via "/proc/rtc".
+ */
+
+static int rtc_get_status(char *buf)
+{
+ char *p;
+ struct rtc_time tm;
+
+ /*
+ * Just emulate the standard /proc/rtc
+ */
+
+ p = buf;
+
+ get_rtc_time(&tm);
+
+ /*
+ * There is no way to tell if the luser has the RTC set for local
+ * time or for Universal Standard Time (GMT). Probably local though.
+ */
+ p += sprintf(p,
+ "rtc_time\t: %02d:%02d:%02d\n"
+ "rtc_date\t: %04d-%02d-%02d\n"
+ "rtc_epoch\t: %04lu\n"
+ "24hr\t\t: yes\n",
+ tm.tm_hour, tm.tm_min, tm.tm_sec,
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch);
+
+ return p - buf;
+}
+
+static int rtc_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = rtc_get_status(page);
+ if (len <= off+count) *eof = 1;
+ *start = page + off;
+ len -= off;
+ if (len>count) len = count;
+ if (len<0) len = 0;
+ return len;
+}
+
+static void get_rtc_time(struct rtc_time *rtc_tm)
+{
+ /*
+ * Do we need to wait for the last update to finish?
+ */
+
+ /*
+ * Only the values that we read from the RTC are set. We leave
+ * tm_wday, tm_yday and tm_isdst untouched. Even though the
+ * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
+ * by the RTC when initially set to a non-zero value.
+ */
+ spin_lock_irq(&rtc_lock);
+ rtc->control |= M48T35_RTC_READ;
+ rtc_tm->tm_sec = rtc->sec;
+ rtc_tm->tm_min = rtc->min;
+ rtc_tm->tm_hour = rtc->hour;
+ rtc_tm->tm_mday = rtc->date;
+ rtc_tm->tm_mon = rtc->month;
+ rtc_tm->tm_year = rtc->year;
+ rtc->control &= ~M48T35_RTC_READ;
+ spin_unlock_irq(&rtc_lock);
+
+ rtc_tm->tm_sec = BCD2BIN(rtc_tm->tm_sec);
+ rtc_tm->tm_min = BCD2BIN(rtc_tm->tm_min);
+ rtc_tm->tm_hour = BCD2BIN(rtc_tm->tm_hour);
+ rtc_tm->tm_mday = BCD2BIN(rtc_tm->tm_mday);
+ rtc_tm->tm_mon = BCD2BIN(rtc_tm->tm_mon);
+ rtc_tm->tm_year = BCD2BIN(rtc_tm->tm_year);
+
+ /*
+ * Account for differences between how the RTC uses the values
+ * and how they are defined in a struct rtc_time;
+ */
+ if ((rtc_tm->tm_year += (epoch - 1900)) <= 69)
+ rtc_tm->tm_year += 100;
+
+ rtc_tm->tm_mon--;
+}
static void isicom_tx(unsigned long _data);
static void isicom_start(struct tty_struct * tty);
-static unsigned char * tmp_buf = 0;
+static unsigned char * tmp_buf;
static DECLARE_MUTEX(tmp_buf_sem);
/* baud index mappings from linux defns to isi */
unsigned long t;
unsigned short word_count, base;
bin_frame frame;
+ void __user *argp = (void __user *)arg;
/* exec_record exec_rec; */
- if(get_user(card, (int *)arg))
+ if(get_user(card, (int __user *)argp))
return -EFAULT;
if(card < 0 || card >= BOARD_COUNT)
return -EIO;
}
printk("-Done\n");
- return put_user(signature,(unsigned int*)arg);
+ return put_user(signature,(unsigned __user *)argp);
case MIOCTL_LOAD_FIRMWARE:
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- if(copy_from_user(&frame, (void *) arg, sizeof(bin_frame)))
+ if(copy_from_user(&frame, argp, sizeof(bin_frame)))
return -EFAULT;
if (WaitTillCardIsFree(base))
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- if(copy_from_user(&frame, (void *) arg, sizeof(bin_header)))
+ if(copy_from_user(&frame, argp, sizeof(bin_header)))
return -EFAULT;
if (WaitTillCardIsFree(base))
return -EIO;
}
- if(copy_to_user((void *) arg, &frame, sizeof(bin_frame)))
+ if(copy_to_user(argp, &frame, sizeof(bin_frame)))
return -EFAULT;
return 0;
if (tty->ldisc.flush_buffer)
tty->ldisc.flush_buffer(tty);
tty->closing = 0;
- port->tty = 0;
+ port->tty = NULL;
if (port->blocked_open) {
if (port->close_delay) {
set_current_state(TASK_INTERRUPTIBLE);
}
static int isicom_set_serial_info(struct isi_port * port,
- struct serial_struct * info)
+ struct serial_struct __user *info)
{
struct serial_struct newinfo;
unsigned long flags;
}
static int isicom_get_serial_info(struct isi_port * port,
- struct serial_struct * info)
+ struct serial_struct __user *info)
{
struct serial_struct out_info;
unsigned int cmd, unsigned long arg)
{
struct isi_port * port = (struct isi_port *) tty->driver_data;
+ void __user *argp = (void __user *)arg;
int retval;
if (isicom_paranoia_check(port, tty->name, "isicom_ioctl"))
return 0;
case TIOCGSOFTCAR:
- return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg);
+ return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *)argp);
case TIOCSSOFTCAR:
- if(get_user(arg, (unsigned long *) arg))
+ if(get_user(arg, (unsigned long __user *) argp))
return -EFAULT;
tty->termios->c_cflag =
((tty->termios->c_cflag & ~CLOCAL) |
return 0;
case TIOCGSERIAL:
- return isicom_get_serial_info(port,
- (struct serial_struct *) arg);
+ return isicom_get_serial_info(port, argp);
case TIOCSSERIAL:
- return isicom_set_serial_info(port,
- (struct serial_struct *) arg);
+ return isicom_set_serial_info(port, argp);
default:
return -ENOIOCTLCMD;
isicom_shutdown_port(port);
port->count = 0;
port->flags &= ~ASYNC_NORMAL_ACTIVE;
- port->tty = 0;
+ port->tty = NULL;
wake_up_interruptible(&port->open_wait);
}
"CP-204J series",
};
+#ifdef CONFIG_PCI
static struct pci_device_id moxa_pcibrds[] = {
{ PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_C218, PCI_ANY_ID, PCI_ANY_ID,
0, 0, MOXA_BOARD_C218_PCI },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, moxa_pcibrds);
+#endif /* CONFIG_PCI */
typedef struct _moxa_isa_board_conf {
int boardType;
static int verbose = 0;
static int ttymajor = MOXAMAJOR;
/* Variables for insmod */
+#ifdef MODULE
static int baseaddr[] = {0, 0, 0, 0};
static int type[] = {0, 0, 0, 0};
static int numports[] = {0, 0, 0, 0};
+#endif
MODULE_AUTHOR("William Chen");
MODULE_DESCRIPTION("MOXA Intellio Family Multiport Board Device Driver");
/*
* static functions:
*/
-static int moxa_get_PCI_conf(struct pci_dev *, int, moxa_board_conf *);
static void do_moxa_softint(void *);
static int moxa_open(struct tty_struct *, struct file *);
static void moxa_close(struct tty_struct *, struct file *);
static void MoxaPortTxEnable(int);
static int MoxaPortResetBrkCnt(int);
static void MoxaPortSendBreak(int, int);
-static int moxa_get_serial_info(struct moxa_str *, struct serial_struct *);
-static int moxa_set_serial_info(struct moxa_str *, struct serial_struct *);
+static int moxa_get_serial_info(struct moxa_str *, struct serial_struct __user *);
+static int moxa_set_serial_info(struct moxa_str *, struct serial_struct __user *);
static void MoxaSetFifo(int port, int enable);
static struct tty_operations moxa_ops = {
.tiocmset = moxa_tiocmset,
};
+#ifdef CONFIG_PCI
+static int moxa_get_PCI_conf(struct pci_dev *p, int board_type, moxa_board_conf * board)
+{
+ board->baseAddr = pci_resource_start (p, 2);
+ board->boardType = board_type;
+ switch (board_type) {
+ case MOXA_BOARD_C218_ISA:
+ case MOXA_BOARD_C218_PCI:
+ board->numPorts = 8;
+ break;
+
+ case MOXA_BOARD_CP204J:
+ board->numPorts = 4;
+ break;
+ default:
+ board->numPorts = 0;
+ break;
+ }
+ board->busType = MOXA_BUS_TYPE_PCI;
+ board->pciInfo.busNum = p->bus->number;
+ board->pciInfo.devNum = p->devfn >> 3;
+
+ return (0);
+}
+#endif /* CONFIG_PCI */
+
static int __init moxa_init(void)
{
int i, numBoards;
moxaDriver->flags = TTY_DRIVER_REAL_RAW;
tty_set_operations(moxaDriver, &moxa_ops);
- moxaXmitBuff = 0;
+ moxaXmitBuff = NULL;
for (i = 0, ch = moxaChannels; i < MAX_PORTS; i++, ch++) {
ch->type = PORT_16550A;
ch->port = i;
INIT_WORK(&ch->tqueue, do_moxa_softint, ch);
- ch->tty = 0;
+ ch->tty = NULL;
ch->close_delay = 5 * HZ / 10;
ch->closing_wait = 30 * HZ;
ch->count = 0;
module_init(moxa_init);
module_exit(moxa_exit);
-static int moxa_get_PCI_conf(struct pci_dev *p, int board_type, moxa_board_conf * board)
-{
- board->baseAddr = pci_resource_start (p, 2);
- board->boardType = board_type;
- switch (board_type) {
- case MOXA_BOARD_C218_ISA:
- case MOXA_BOARD_C218_PCI:
- board->numPorts = 8;
- break;
-
- case MOXA_BOARD_CP204J:
- board->numPorts = 4;
- break;
- default:
- board->numPorts = 0;
- break;
- }
- board->busType = MOXA_BUS_TYPE_PCI;
- board->pciInfo.busNum = p->bus->number;
- board->pciInfo.devNum = p->devfn >> 3;
-
- return (0);
-}
-
static void do_moxa_softint(void *private_)
{
struct moxa_str *ch = (struct moxa_str *) private_;
tty->ldisc.flush_buffer(tty);
tty->closing = 0;
ch->event = 0;
- ch->tty = 0;
+ ch->tty = NULL;
if (ch->blocked_open) {
if (ch->close_delay) {
set_current_state(TASK_INTERRUPTIBLE);
{
struct moxa_str *ch = (struct moxa_str *) tty->driver_data;
register int port;
+ void __user *argp = (void __user *)arg;
int retval;
port = PORTNO(tty);
MoxaPortSendBreak(ch->port, arg);
return (0);
case TIOCGSOFTCAR:
- return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg);
+ return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *) argp);
case TIOCSSOFTCAR:
- if(get_user(retval, (unsigned long *) arg))
+ if(get_user(retval, (unsigned long __user *) argp))
return -EFAULT;
arg = retval;
tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) |
ch->asyncflags |= ASYNC_CHECK_CD;
return (0);
case TIOCGSERIAL:
- return (moxa_get_serial_info(ch, (struct serial_struct *) arg));
+ return moxa_get_serial_info(ch, argp);
case TIOCSSERIAL:
- return (moxa_set_serial_info(ch, (struct serial_struct *) arg));
+ return moxa_set_serial_info(ch, argp);
default:
retval = MoxaDriverIoctl(cmd, arg, port);
}
ch->event = 0;
ch->count = 0;
ch->asyncflags &= ~ASYNC_NORMAL_ACTIVE;
- ch->tty = 0;
+ ch->tty = NULL;
wake_up_interruptible(&ch->open_wait);
}
unsigned char *charptr, *flagptr;
unsigned long flags;
- ts = 0;
+ ts = NULL;
tp = ch->tty;
if (tp)
ts = tp->termios;
static void moxafunc(unsigned long, int, ushort);
static void wait_finish(unsigned long);
static void low_water_check(unsigned long);
-static int moxaloadbios(int, unsigned char *, int);
+static int moxaloadbios(int, unsigned char __user *, int);
static int moxafindcard(int);
-static int moxaload320b(int, unsigned char *, int);
-static int moxaloadcode(int, unsigned char *, int);
+static int moxaload320b(int, unsigned char __user *, int);
+static int moxaloadcode(int, unsigned char __user *, int);
static int moxaloadc218(int, unsigned long, int);
static int moxaloadc320(int, unsigned long, int, int *);
};
struct dl_str {
- char *buf;
+ char __user *buf;
int len;
int cardno;
};
int i;
int status;
int MoxaPortTxQueue(int), MoxaPortRxQueue(int);
+ void __user *argp = (void __user *)arg;
if (port == QueryPort) {
if ((cmd != MOXA_GET_CONF) && (cmd != MOXA_INIT_DRIVER) &&
}
switch (cmd) {
case MOXA_GET_CONF:
- if(copy_to_user((void *)arg, &moxa_boards, MAX_BOARDS * sizeof(moxa_board_conf)))
+ if(copy_to_user(argp, &moxa_boards, MAX_BOARDS * sizeof(moxa_board_conf)))
return -EFAULT;
return (0);
case MOXA_INIT_DRIVER:
return (0);
case MOXA_GETDATACOUNT:
moxaLog.tick = jiffies;
- if(copy_to_user((void *)arg, &moxaLog, sizeof(mon_st)))
+ if(copy_to_user(argp, &moxaLog, sizeof(mon_st)))
return -EFAULT;
return (0);
case MOXA_FLUSH_QUEUE:
temp_queue[i].outq = MoxaPortTxQueue(i);
}
}
- if(copy_to_user((void *)arg, temp_queue, sizeof(struct moxaq_str) * MAX_PORTS))
+ if(copy_to_user(argp, temp_queue, sizeof(struct moxaq_str) * MAX_PORTS))
return -EFAULT;
return (0);
case MOXA_GET_OQUEUE:
i = MoxaPortTxQueue(port);
- return put_user(i, (unsigned long *) arg);
+ return put_user(i, (unsigned long __user *)argp);
case MOXA_GET_IQUEUE:
i = MoxaPortRxQueue(port);
- return put_user(i, (unsigned long *) arg);
+ return put_user(i, (unsigned long __user *)argp);
case MOXA_GET_MAJOR:
- if(copy_to_user((void *)arg, &ttymajor, sizeof(int)))
+ if(copy_to_user(argp, &ttymajor, sizeof(int)))
return -EFAULT;
return 0;
case MOXA_GET_CUMAJOR:
i = 0;
- if(copy_to_user((void *)arg, &i, sizeof(int)))
+ if(copy_to_user(argp, &i, sizeof(int)))
return -EFAULT;
return 0;
case MOXA_GETMSTATUS:
else
GMStatus[i].cflag = moxaChannels[i].tty->termios->c_cflag;
}
- if(copy_to_user((void *)arg, GMStatus, sizeof(struct mxser_mstatus) * MAX_PORTS))
+ if(copy_to_user(argp, GMStatus, sizeof(struct mxser_mstatus) * MAX_PORTS))
return -EFAULT;
return 0;
default:
break;
}
- if(copy_from_user(&dltmp, (void *)arg, sizeof(struct dl_str)))
+ if(copy_from_user(&dltmp, argp, sizeof(struct dl_str)))
return -EFAULT;
if(dltmp.cardno < 0 || dltmp.cardno >= MAX_BOARDS)
return -EINVAL;
}
static int moxa_get_serial_info(struct moxa_str *info,
- struct serial_struct *retinfo)
+ struct serial_struct __user *retinfo)
{
struct serial_struct tmp;
- if (!retinfo)
- return (-EFAULT);
memset(&tmp, 0, sizeof(tmp));
tmp.type = info->type;
tmp.line = info->port;
static int moxa_set_serial_info(struct moxa_str *info,
- struct serial_struct *new_info)
+ struct serial_struct __user *new_info)
{
struct serial_struct new_serial;
}
}
-static int moxaloadbios(int cardno, unsigned char *tmp, int len)
+static int moxaloadbios(int cardno, unsigned char __user *tmp, int len)
{
unsigned long baseAddr;
int i;
return (0);
}
-static int moxaload320b(int cardno, unsigned char * tmp, int len)
+static int moxaload320b(int cardno, unsigned char __user *tmp, int len)
{
unsigned long baseAddr;
int i;
return (0);
}
-static int moxaloadcode(int cardno, unsigned char * tmp, int len)
+static int moxaloadcode(int cardno, unsigned char __user *tmp, int len)
{
unsigned long baseAddr, ofsAddr;
int retval, port, i;
NVRAM_MINOR);
goto out;
}
- if (!create_proc_read_entry("driver/nvram", 0, 0, nvram_read_proc,
+ if (!create_proc_read_entry("driver/nvram", 0, NULL, nvram_read_proc,
NULL)) {
printk(KERN_ERR "nvram: can't create /proc/driver/nvram\n");
ret = -ENOMEM;
static void __exit
nvram_cleanup_module(void)
{
- remove_proc_entry("driver/nvram", 0);
+ remove_proc_entry("driver/nvram", NULL);
misc_deregister(&nvram_dev);
}
* device at any one time.
*/
-static int button_read (struct file *filp, char *buffer,
+static int button_read (struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
interruptible_sleep_on (&button_wait_queue);
static void button_sequence_finished (unsigned long parameters);
static irqreturn_t button_handler (int irq, void *dev_id, struct pt_regs *regs);
-static int button_read (struct file *filp, char *buffer,
- size_t count, loff_t *ppos);
int button_init (void);
int button_add_callback (void (*callback) (void), int count);
int button_del_callback (void (*callback) (void));
static void kick_open(void);
static int get_flash_id(void);
static int erase_block(int nBlock);
-static int write_block(unsigned long p, const char *buf, int count);
-static int flash_ioctl(struct inode *inodep, struct file *filep, unsigned int cmd, unsigned long arg);
-static ssize_t flash_read(struct file *file, char *buf, size_t count, loff_t * ppos);
-static ssize_t flash_write(struct file *file, const char *buf, size_t count, loff_t * ppos);
-static loff_t flash_llseek(struct file *file, loff_t offset, int orig);
+static int write_block(unsigned long p, const char __user *buf, int count);
#define KFLASH_SIZE 1024*1024 //1 Meg
#define KFLASH_SIZE4 4*1024*1024 //4 Meg
return 0;
}
-static ssize_t flash_read(struct file *file, char *buf, size_t size, loff_t * ppos)
+static ssize_t flash_read(struct file *file, char __user *buf, size_t size,
+ loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
if (flashdebug)
- printk(KERN_DEBUG "flash_read: flash_read: offset=0x%lX, buffer=%p, count=0x%X.\n",
- p, buf, count);
+ printk(KERN_DEBUG "flash_read: flash_read: offset=0x%lX, "
+ "buffer=%p, count=0x%X.\n", p, buf, count);
if (count)
ret = -ENXIO;
return ret;
}
-static ssize_t flash_write(struct file *file, const char *buf, size_t size, loff_t * ppos)
+static ssize_t flash_write(struct file *file, const char __user *buf,
+ size_t size, loff_t * ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
break;
}
if (flashdebug)
- printk(KERN_DEBUG "flash_write: writing offset %lX, from buf "
- "%p, bytes left %X.\n", p, buf, count - written);
+ printk(KERN_DEBUG "flash_write: writing offset %lX, "
+ "from buf %p, bytes left %X.\n", p, buf,
+ count - written);
/*
* write_block will limit write to space left in this block
/*
* write_block will limit number of bytes written to the space in this block
*/
-static int write_block(unsigned long p, const char *buf, int count)
+static int write_block(unsigned long p, const char __user *buf, int count)
{
volatile unsigned int c1;
volatile unsigned int c2;
tty->ldisc.flush_buffer(tty);
tty->closing = 0;
port->event = 0;
- port->tty = 0;
+ port->tty = NULL;
if (port->blocked_open) {
if (port->close_delay) {
current->state = TASK_INTERRUPTIBLE;
}
static inline int rc_set_serial_info(struct riscom_port * port,
- struct serial_struct * newinfo)
+ struct serial_struct __user * newinfo)
{
struct serial_struct tmp;
struct riscom_board *bp = port_Board(port);
}
static inline int rc_get_serial_info(struct riscom_port * port,
- struct serial_struct * retinfo)
+ struct serial_struct __user *retinfo)
{
struct serial_struct tmp;
struct riscom_board *bp = port_Board(port);
{
struct riscom_port *port = (struct riscom_port *)tty->driver_data;
+ void __user *argp = (void __user *)arg;
int retval;
if (rc_paranoia_check(port, tty->name, "rc_ioctl"))
rc_send_break(port, arg ? arg*(HZ/10) : HZ/4);
break;
case TIOCGSOFTCAR:
- return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned int *) arg);
+ return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned __user *)argp);
case TIOCSSOFTCAR:
- if (get_user(arg,(unsigned int *) arg))
+ if (get_user(arg,(unsigned __user *) argp))
return -EFAULT;
tty->termios->c_cflag =
((tty->termios->c_cflag & ~CLOCAL) |
(arg ? CLOCAL : 0));
break;
case TIOCGSERIAL:
- return rc_get_serial_info(port, (struct serial_struct *) arg);
+ return rc_get_serial_info(port, argp);
case TIOCSSERIAL:
- return rc_set_serial_info(port, (struct serial_struct *) arg);
+ return rc_set_serial_info(port, argp);
default:
return -ENOIOCTLCMD;
}
port->event = 0;
port->count = 0;
port->flags &= ~ASYNC_NORMAL_ACTIVE;
- port->tty = 0;
+ port->tty = NULL;
wake_up_interruptible(&port->open_wait);
}
unsigned m = iminor(file->f_dentry->d_inode);
size_t i;
- if (ppos != &file->f_pos)
- return -ESPIPE;
-
for (i = 0; i < len; ++i) {
char c;
if (get_user(c, data+i))
unsigned m = iminor(file->f_dentry->d_inode);
int value;
- if (ppos != &file->f_pos)
- return -ESPIPE;
-
value = scx200_gpio_get(m);
if (put_user(value ? '1' : '0', buf))
return -EFAULT;
unsigned m = iminor(inode);
if (m > 63)
return -EINVAL;
- return 0;
+ return nonseekable_open(inode, file);
}
static int scx200_gpio_release(struct inode *inode, struct file *file)
/************************* End of Includes **************************/
/***************************** Prototypes ***************************/
-/* Helper functions */
-static __inline__ volatile struct a2232status *a2232stat(unsigned int board,
- unsigned int portonboard);
-static __inline__ volatile struct a2232memory *a2232mem (unsigned int board);
-static __inline__ void a2232_receive_char( struct a2232_port *port,
- int ch, int err );
/* The interrupt service routine */
static irqreturn_t a2232_vbl_inter(int irq, void *data, struct pt_regs *fp);
/* Initialize the port structures */
static struct zorro_dev *zd_a2232[MAX_A2232_BOARDS];
/***************************** End of Global variables **************/
+/* Helper functions */
+
+static inline volatile struct a2232memory *a2232mem(unsigned int board)
+{
+ return (volatile struct a2232memory *)ZTWO_VADDR(zd_a2232[board]->resource.start);
+}
+
+static inline volatile struct a2232status *a2232stat(unsigned int board,
+ unsigned int portonboard)
+{
+ volatile struct a2232memory *mem = a2232mem(board);
+ return &(mem->Status[portonboard]);
+}
+
+static inline void a2232_receive_char(struct a2232_port *port, int ch, int err)
+{
+/* Mostly stolen from other drivers.
+ Maybe one could implement a more efficient version by not only
+ transferring one character at a time.
+*/
+ struct tty_struct *tty = port->gs.tty;
+
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+ return;
+
+ tty->flip.count++;
+
+#if 0
+ switch(err) {
+ case TTY_BREAK:
+ break;
+ case TTY_PARITY:
+ break;
+ case TTY_OVERRUN:
+ break;
+ case TTY_FRAME:
+ break;
+ }
+#endif
+
+ *tty->flip.flag_buf_ptr++ = err;
+ *tty->flip.char_buf_ptr++ = ch;
+ tty_flip_buffer_push(tty);
+}
+
/***************************** Functions ****************************/
/*** BEGIN OF REAL_DRIVER FUNCTIONS ***/
}
/*** END OF FUNCTIONS EXPECTED BY TTY DRIVER STRUCTS ***/
-static __inline__ volatile struct a2232status *a2232stat(unsigned int board, unsigned int portonboard)
-{
- volatile struct a2232memory *mem = a2232mem(board);
- return &(mem->Status[portonboard]);
-}
-
-static __inline__ volatile struct a2232memory *a2232mem (unsigned int board)
-{
- return (volatile struct a2232memory *) ZTWO_VADDR( zd_a2232[board]->resource.start );
-}
-
-static __inline__ void a2232_receive_char( struct a2232_port *port,
- int ch, int err )
-{
-/* Mostly stolen from other drivers.
- Maybe one could implement a more efficient version by not only
- transferring one character at a time.
-*/
- struct tty_struct *tty = port->gs.tty;
-
- if (tty->flip.count >= TTY_FLIPBUF_SIZE)
- return;
-
- tty->flip.count++;
-
-#if 0
- switch(err) {
- case TTY_BREAK:
- break;
- case TTY_PARITY:
- break;
- case TTY_OVERRUN:
- break;
- case TTY_FRAME:
- break;
- }
-#endif
-
- *tty->flip.flag_buf_ptr++ = err;
- *tty->flip.char_buf_ptr++ = ch;
- tty_flip_buffer_push(tty);
-}
-
static irqreturn_t a2232_vbl_inter(int irq, void *data, struct pt_regs *fp)
{
#if A2232_IOBUFLEN != 256
/*
* There is a bunch of documentation about the card, jumpers, config
* settings, restrictions, cables, device names and numbers in
- * ../../Documentation/specialix.txt
+ * Documentation/specialix.txt
*/
#include <linux/config.h>
tty->ldisc.flush_buffer(tty);
tty->closing = 0;
port->event = 0;
- port->tty = 0;
+ port->tty = NULL;
if (port->blocked_open) {
if (port->close_delay) {
current->state = TASK_INTERRUPTIBLE;
static inline int sx_set_serial_info(struct specialix_port * port,
- struct serial_struct * newinfo)
+ struct serial_struct __user * newinfo)
{
struct serial_struct tmp;
struct specialix_board *bp = port_Board(port);
int change_speed;
unsigned long flags;
- int error;
- error = verify_area(VERIFY_READ, (void *) newinfo, sizeof(tmp));
- if (error)
- return error;
-
if (copy_from_user(&tmp, newinfo, sizeof(tmp)))
return -EFAULT;
static inline int sx_get_serial_info(struct specialix_port * port,
- struct serial_struct * retinfo)
+ struct serial_struct __user *retinfo)
{
struct serial_struct tmp;
struct specialix_board *bp = port_Board(port);
- int error;
- error = verify_area(VERIFY_WRITE, (void *) retinfo, sizeof(tmp));
- if (error)
- return error;
-
memset(&tmp, 0, sizeof(tmp));
tmp.type = PORT_CIRRUS;
tmp.line = port - sx_port;
unsigned int cmd, unsigned long arg)
{
struct specialix_port *port = (struct specialix_port *)tty->driver_data;
- int error;
int retval;
+ void __user *argp = (void __user *)arg;
if (sx_paranoia_check(port, tty->name, "sx_ioctl"))
return -ENODEV;
sx_send_break(port, arg ? arg*(HZ/10) : HZ/4);
return 0;
case TIOCGSOFTCAR:
- error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
- if (error)
- return error;
- put_user(C_CLOCAL(tty) ? 1 : 0,
- (unsigned long *) arg);
+ if (put_user(C_CLOCAL(tty)?1:0, (unsigned long __user *)argp))
+ return -EFAULT;
return 0;
case TIOCSSOFTCAR:
- get_user(arg, (unsigned long *) arg);
+ if (get_user(arg, (unsigned long __user *) argp))
+ return -EFAULT;
tty->termios->c_cflag =
((tty->termios->c_cflag & ~CLOCAL) |
(arg ? CLOCAL : 0));
return 0;
case TIOCGSERIAL:
- return sx_get_serial_info(port, (struct serial_struct *) arg);
+ return sx_get_serial_info(port, argp);
case TIOCSSERIAL:
- return sx_set_serial_info(port, (struct serial_struct *) arg);
+ return sx_set_serial_info(port, argp);
default:
return -ENOIOCTLCMD;
}
port->event = 0;
port->count = 0;
port->flags &= ~ASYNC_NORMAL_ACTIVE;
- port->tty = 0;
+ port->tty = NULL;
wake_up_interruptible(&port->open_wait);
}
#define PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8 0x2000
#endif
+#ifdef CONFIG_PCI
static struct pci_device_id sx_pci_tbl[] = {
{ PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_SX_XIO_IO8, PCI_ANY_ID, PCI_ANY_ID },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, sx_pci_tbl);
+#endif /* CONFIG_PCI */
/* Configurable options:
(Don't be too sure that it'll work if you toggle them) */
unsigned int cmd, unsigned long arg)
{
int rc = 0;
- int *descr = (int *)arg, i;
+ int __user *descr = (int __user *)arg;
+ int i;
static struct sx_board *board = NULL;
int nbytes, offset;
unsigned long data;
get_user (data, descr++);
while (nbytes && data) {
for (i=0;i<nbytes;i += SX_CHUNK_SIZE) {
- if (copy_from_user(tmp, (char *)data + i,
+ if (copy_from_user(tmp, (char __user *)data+i,
(i + SX_CHUNK_SIZE >
nbytes) ? nbytes - i :
SX_CHUNK_SIZE)) {
{
int rc;
struct sx_port *port = tty->driver_data;
+ void __user *argp = (void __user *)arg;
int ival;
/* func_enter2(); */
switch (cmd) {
case TIOCGSOFTCAR:
rc = put_user(((tty->termios->c_cflag & CLOCAL) ? 1 : 0),
- (unsigned int *) arg);
+ (unsigned __user *) argp);
break;
case TIOCSSOFTCAR:
- if ((rc = get_user(ival, (unsigned int *) arg)) == 0) {
+ if ((rc = get_user(ival, (unsigned __user *) argp)) == 0) {
tty->termios->c_cflag =
(tty->termios->c_cflag & ~CLOCAL) |
(ival ? CLOCAL : 0);
}
break;
case TIOCGSERIAL:
- if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(struct serial_struct))) == 0)
- rc = gs_getserial(&port->gs, (struct serial_struct *) arg);
+ rc = gs_getserial(&port->gs, argp);
break;
case TIOCSSERIAL:
- if ((rc = verify_area(VERIFY_READ, (void *) arg,
- sizeof(struct serial_struct))) == 0)
- rc = gs_setserial(&port->gs, (struct serial_struct *) arg);
+ rc = gs_setserial(&port->gs, argp);
break;
default:
rc = -ENOIOCTLCMD;
if (info->xmit_buf) {
free_page((unsigned long) info->xmit_buf);
- info->xmit_buf = 0;
+ info->xmit_buf = NULL;
}
spin_lock_irqsave(&info->irq_spinlock,flags);
shutdown(info);
tty->closing = 0;
- info->tty = 0;
+ info->tty = NULL;
if (info->blocked_open) {
if (info->close_delay) {
info->count = 0;
info->flags &= ~ASYNC_NORMAL_ACTIVE;
- info->tty = 0;
+ info->tty = NULL;
wake_up_interruptible(&info->open_wait);
cleanup:
if (retval) {
if (tty->count == 1)
- info->tty = 0; /* tty layer will release tty struct */
+ info->tty = NULL;/* tty layer will release tty struct */
if(info->count)
info->count--;
}
}
if (info->memory_base){
iounmap(info->memory_base);
- info->memory_base = 0;
+ info->memory_base = NULL;
}
if (info->lcr_base){
iounmap(info->lcr_base - info->lcr_offset);
- info->lcr_base = 0;
+ info->lcr_base = NULL;
}
if ( debug_level >= DEBUG_LEVEL_INFO )
printk(TPQIC02_NAME ": ll_do_qic_cmd(%x, %ld) failed\n", cmd, (long) timeout);
return -EIO;
}
-#if OBSOLETE
+#ifdef OBSOLETE
/* wait for ready since it may not be active immediately after reading status */
while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) != 0)
cpu_relax();
if (stat != TE_OK)
return stat;
-#if OBSOLETE
+#ifdef OBSOLETE
/************* not needed iff rd_status() would wait for ready!!!!!! **********/
if (wait_for_ready(TIM_S) != TE_OK) { /*** not sure this is needed ***/
tpqputs(TPQD_ALWAYS, "wait_for_ready failed in start_dma");
release_region(QIC02_TAPE_PORT, QIC02_TAPE_PORT_RANGE);
if (buffaddr)
free_pages((unsigned long) buffaddr, get_order(TPQBUF_SIZE));
- buffaddr = 0; /* Better to cause a panic than overwite someone else */
+ buffaddr = NULL; /* Better to cause a panic than overwite someone else */
status_zombie = YES;
} /* qic02_release_resources */
--- /dev/null
+/*
+ * drivers/watchdog/ixp2000_wdt.c
+ *
+ * Watchdog driver for Intel IXP2000 network processors
+ *
+ * Adapted from the IXP4xx watchdog driver by Lennert Buytenhek.
+ * The original version carries these notices:
+ *
+ * Author: Deepak Saxena <dsaxena@plexity.net>
+ *
+ * Copyright 2004 (c) MontaVista, Software, Inc.
+ * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
+ *
+ * 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/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_WATCHDOG_NOWAYOUT
+static int nowayout = 1;
+#else
+static int nowayout = 0;
+#endif
+static unsigned int heartbeat = 60; /* (secs) Default is 1 minute */
+static unsigned long wdt_status;
+
+#define WDT_IN_USE 0
+#define WDT_OK_TO_CLOSE 1
+
+static unsigned long wdt_tick_rate;
+
+static void
+wdt_enable(void)
+{
+ ixp2000_reg_write(IXP2000_RESET0, *(IXP2000_RESET0) | WDT_RESET_ENABLE);
+ ixp2000_reg_write(IXP2000_TWDE, WDT_ENABLE);
+ ixp2000_reg_write(IXP2000_T4_CLD, heartbeat * wdt_tick_rate);
+ ixp2000_reg_write(IXP2000_T4_CTL, TIMER_DIVIDER_256 | TIMER_ENABLE);
+}
+
+static void
+wdt_disable(void)
+{
+ ixp2000_reg_write(IXP2000_T4_CTL, 0);
+}
+
+static void
+wdt_keepalive(void)
+{
+ ixp2000_reg_write(IXP2000_T4_CLD, heartbeat * wdt_tick_rate);
+}
+
+static int
+ixp2000_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_IN_USE, &wdt_status))
+ return -EBUSY;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ wdt_enable();
+
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t
+ixp2000_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ set_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ }
+ }
+ wdt_keepalive();
+ }
+
+ return len;
+}
+
+
+static struct watchdog_info ident = {
+ .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING,
+ .identity = "IXP2000 Watchdog",
+};
+
+static int
+ixp2000_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -ENOIOCTLCMD;
+ int time;
+
+ 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_GETBOOTSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(time, (int *)arg);
+ if (ret)
+ break;
+
+ if (time <= 0 || time > 60) {
+ ret = -EINVAL;
+ break;
+ }
+
+ heartbeat = time;
+ wdt_keepalive();
+ /* Fall through */
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(heartbeat, (int *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ wdt_enable();
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static int
+ixp2000_wdt_release(struct inode *inode, struct file *file)
+{
+ if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) {
+ wdt_disable();
+ } else {
+ printk(KERN_CRIT "WATCHDOG: Device closed unexpectdly - "
+ "timer will not stop\n");
+ }
+
+ clear_bit(WDT_IN_USE, &wdt_status);
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ return 0;
+}
+
+
+static struct file_operations ixp2000_wdt_fops =
+{
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = ixp2000_wdt_write,
+ .ioctl = ixp2000_wdt_ioctl,
+ .open = ixp2000_wdt_open,
+ .release = ixp2000_wdt_release,
+};
+
+static struct miscdevice ixp2000_wdt_miscdev =
+{
+ .minor = WATCHDOG_MINOR,
+ .name = "IXP2000 Watchdog",
+ .fops = &ixp2000_wdt_fops,
+};
+
+static int __init ixp2000_wdt_init(void)
+{
+ wdt_tick_rate = (*IXP2000_T1_CLD * HZ)/ 256;;
+
+ return misc_register(&ixp2000_wdt_miscdev);
+}
+
+static void __exit ixp2000_wdt_exit(void)
+{
+ misc_deregister(&ixp2000_wdt_miscdev);
+}
+
+module_init(ixp2000_wdt_init);
+module_exit(ixp2000_wdt_exit);
+
+MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net">);
+MODULE_DESCRIPTION("IXP2000 Network Processor Watchdog");
+
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)");
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
wdt_enable();
- return 0;
+ return nonseekable_open(inode, file);
}
static ssize_t
ixp4xx_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
{
- /* Can't seek (pwrite) on this device */
- if (ppos != &file->f_pos)
- return -ESPIPE;
-
if (len) {
if (!nowayout) {
size_t i;
static ssize_t pcwd_write(struct file *file, const char __user *buf, size_t len,
loff_t *ppos)
{
- /* Can't seek (pwrite) on this device */
- if (ppos != &file->f_pos)
- return -ESPIPE;
-
if (len) {
if (!nowayout) {
size_t i;
/* Activate */
pcwd_start();
pcwd_keepalive();
- return(0);
+ return nonseekable_open(inode, file);
}
static int pcwd_close(struct inode *inode, struct file *file)
{
int temperature;
- /* Can't seek (pread) on this device */
- if (ppos != &file->f_pos)
- return -ESPIPE;
-
if (pcwd_get_temperature(&temperature))
return -EFAULT;
if (!supports_temp)
return -ENODEV;
- return 0;
+ return nonseekable_open(inode, file);
}
static int pcwd_temp_close(struct inode *inode, struct file *file)
ret = 0;
#endif
+ nonseekable_open(inode, file);
return ret;
}
static ssize_t
watchdog_write(struct file *file, const char *data, size_t len, loff_t *ppos)
{
- /* Can't seek (pwrite) on this device */
- if (ppos != &file->f_pos)
- return -ESPIPE;
-
/*
* Refresh the timer.
*/
__module_get(THIS_MODULE);
wdt977_start();
- return 0;
+ return nonseekable_open(inode, file);
}
static int wdt977_release(struct inode *inode, struct file *file)
* write of data will do, as we we don't define content meaning.
*/
-static ssize_t wdt977_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+static ssize_t wdt977_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
{
- /* Can't seek (pwrite) on this device */
- if (ppos != &file->f_pos)
- return -ESPIPE;
-
if (count) {
if (!nowayout) {
size_t i;
int status;
int new_options, retval = -EINVAL;
int new_timeout;
+ union {
+ struct watchdog_info __user *ident;
+ int __user *i;
+ } uarg;
+
+ uarg.i = (int __user *)arg;
switch(cmd)
{
return -ENOIOCTLCMD;
case WDIOC_GETSUPPORT:
- return copy_to_user((struct watchdog_info *)arg, &ident,
+ return copy_to_user(uarg.ident, &ident,
sizeof(ident)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
wdt977_get_status(&status);
- return put_user(status, (int *) arg);
+ return put_user(status, uarg.i);
case WDIOC_GETBOOTSTATUS:
- return put_user(0, (int *) arg);
+ return put_user(0, uarg.i);
case WDIOC_KEEPALIVE:
wdt977_keepalive();
return 0;
case WDIOC_SETOPTIONS:
- if (get_user (new_options, (int *) arg))
+ if (get_user (new_options, uarg.i))
return -EFAULT;
if (new_options & WDIOS_DISABLECARD) {
return retval;
case WDIOC_SETTIMEOUT:
- if (get_user(new_timeout, (int *) arg))
+ if (get_user(new_timeout, uarg.i))
return -EFAULT;
if (wdt977_set_timeout(new_timeout))
/* Fall */
case WDIOC_GETTIMEOUT:
- return put_user(timeout, (int *)arg);
+ return put_user(timeout, uarg.i);
}
}
static int __init soc_probe(void)
{
struct sbus_bus *sbus;
- struct sbus_dev *sdev = 0;
+ struct sbus_dev *sdev = NULL;
struct soc *s;
int cards = 0;
static int __init socal_probe(void)
{
struct sbus_bus *sbus;
- struct sbus_dev *sdev = 0;
+ struct sbus_dev *sdev = NULL;
struct socal *s;
int cards = 0;
/*
- * Copyright (C) 2002, 2003, 2004 Hewlett-Packard Co.
- * Khalid Aziz <khalid_aziz@hp.com>
+ * Parse the EFI PCDP table to locate the console device.
+ *
+ * (c) Copyright 2002, 2003, 2004 Hewlett-Packard Development Company, L.P.
+ * Khalid Aziz <khalid.aziz@hp.com>
* Alex Williamson <alex.williamson@hp.com>
* Bjorn Helgaas <bjorn.helgaas@hp.com>
*
- * Parse the EFI PCDP table to locate the console 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/acpi.h>
--- /dev/null
+/*
+ * adm1025.c
+ *
+ * Copyright (C) 2000 Chen-Yuan Wu <gwu@esoft.com>
+ * Copyright (C) 2003-2004 Jean Delvare <khali@linux-fr.org>
+ *
+ * The ADM1025 is a sensor chip made by Analog Devices. It reports up to 6
+ * voltages (including its own power source) and up to two temperatures
+ * (its own plus up to one external one). Voltages are scaled internally
+ * (which is not the common way) with ratios such that the nominal value
+ * of each voltage correspond to a register value of 192 (which means a
+ * resolution of about 0.5% of the nominal value). Temperature values are
+ * reported with a 1 deg resolution and a 3 deg accuracy. Complete
+ * datasheet can be obtained from Analog's website at:
+ * http://www.analog.com/Analog_Root/productPage/productHome/0,2121,ADM1025,00.html
+ *
+ * This driver also supports the ADM1025A, which differs from the ADM1025
+ * only in that it has "open-drain VID inputs while the ADM1025 has
+ * on-chip 100k pull-ups on the VID inputs". It doesn't make any
+ * difference for us.
+ *
+ * This driver also supports the NE1619, a sensor chip made by Philips.
+ * That chip is similar to the ADM1025A, with a few differences. The only
+ * difference that matters to us is that the NE1619 has only two possible
+ * addresses while the ADM1025A has a third one. Complete datasheet can be
+ * obtained from Philips's website at:
+ * http://www.semiconductors.philips.com/pip/NE1619DS.html
+ *
+ * Since the ADM1025 was the first chipset supported by this driver, most
+ * comments will refer to this chipset, but are actually general and
+ * concern all supported chipsets, unless mentioned otherwise.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+#include <linux/i2c-vid.h>
+
+/*
+ * Addresses to scan
+ * ADM1025 and ADM1025A have three possible addresses: 0x2c, 0x2d and 0x2e.
+ * NE1619 has two possible addresses: 0x2c and 0x2d.
+ */
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, I2C_CLIENT_END };
+static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
+static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
+
+/*
+ * Insmod parameters
+ */
+
+SENSORS_INSMOD_2(adm1025, ne1619);
+
+/*
+ * The ADM1025 registers
+ */
+
+#define ADM1025_REG_MAN_ID 0x3E
+#define ADM1025_REG_CHIP_ID 0x3F
+#define ADM1025_REG_CONFIG 0x40
+#define ADM1025_REG_STATUS1 0x41
+#define ADM1025_REG_STATUS2 0x42
+#define ADM1025_REG_IN(nr) (0x20 + (nr))
+#define ADM1025_REG_IN_MAX(nr) (0x2B + (nr) * 2)
+#define ADM1025_REG_IN_MIN(nr) (0x2C + (nr) * 2)
+#define ADM1025_REG_TEMP(nr) (0x26 + (nr))
+#define ADM1025_REG_TEMP_HIGH(nr) (0x37 + (nr) * 2)
+#define ADM1025_REG_TEMP_LOW(nr) (0x38 + (nr) * 2)
+#define ADM1025_REG_VID 0x47
+#define ADM1025_REG_VID4 0x49
+
+/*
+ * Conversions and various macros
+ * The ADM1025 uses signed 8-bit values for temperatures.
+ */
+
+static int in_scale[6] = { 2500, 2250, 3300, 5000, 12000, 3300 };
+
+#define IN_FROM_REG(reg,scale) (((reg) * (scale) + 96) / 192)
+#define IN_TO_REG(val,scale) ((val) <= 0 ? 0 : \
+ (val) * 192 >= (scale) * 255 ? 255 : \
+ ((val) * 192 + (scale)/2) / (scale))
+
+#define TEMP_FROM_REG(reg) ((reg) * 1000)
+#define TEMP_TO_REG(val) ((val) <= -127500 ? -128 : \
+ (val) >= 126500 ? 127 : \
+ (((val) < 0 ? (val)-500 : (val)+500) / 1000))
+
+/*
+ * Functions declaration
+ */
+
+static int adm1025_attach_adapter(struct i2c_adapter *adapter);
+static int adm1025_detect(struct i2c_adapter *adapter, int address, int kind);
+static void adm1025_init_client(struct i2c_client *client);
+static int adm1025_detach_client(struct i2c_client *client);
+static struct adm1025_data *adm1025_update_device(struct device *dev);
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static struct i2c_driver adm1025_driver = {
+ .owner = THIS_MODULE,
+ .name = "adm1025",
+ .id = I2C_DRIVERID_ADM1025,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = adm1025_attach_adapter,
+ .detach_client = adm1025_detach_client,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct adm1025_data {
+ struct i2c_client client;
+ struct semaphore update_lock;
+ char valid; /* zero until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ u8 in[6]; /* register value */
+ u8 in_max[6]; /* register value */
+ u8 in_min[6]; /* register value */
+ s8 temp[2]; /* register value */
+ s8 temp_min[2]; /* register value */
+ s8 temp_max[2]; /* register value */
+ u16 alarms; /* register values, combined */
+ u8 vid; /* register values, combined */
+ u8 vrm;
+};
+
+/*
+ * Internal variables
+ */
+
+static int adm1025_id = 0;
+
+/*
+ * Sysfs stuff
+ */
+
+#define show_in(offset) \
+static ssize_t show_in##offset(struct device *dev, char *buf) \
+{ \
+ struct adm1025_data *data = adm1025_update_device(dev); \
+ return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset], \
+ in_scale[offset])); \
+} \
+static ssize_t show_in##offset##_min(struct device *dev, char *buf) \
+{ \
+ struct adm1025_data *data = adm1025_update_device(dev); \
+ return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset], \
+ in_scale[offset])); \
+} \
+static ssize_t show_in##offset##_max(struct device *dev, char *buf) \
+{ \
+ struct adm1025_data *data = adm1025_update_device(dev); \
+ return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset], \
+ in_scale[offset])); \
+} \
+static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in##offset, NULL);
+show_in(0);
+show_in(1);
+show_in(2);
+show_in(3);
+show_in(4);
+show_in(5);
+
+#define show_temp(offset) \
+static ssize_t show_temp##offset(struct device *dev, char *buf) \
+{ \
+ struct adm1025_data *data = adm1025_update_device(dev); \
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[offset-1])); \
+} \
+static ssize_t show_temp##offset##_min(struct device *dev, char *buf) \
+{ \
+ struct adm1025_data *data = adm1025_update_device(dev); \
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[offset-1])); \
+} \
+static ssize_t show_temp##offset##_max(struct device *dev, char *buf) \
+{ \
+ struct adm1025_data *data = adm1025_update_device(dev); \
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[offset-1])); \
+}\
+static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp##offset, NULL);
+show_temp(1);
+show_temp(2);
+
+#define set_in(offset) \
+static ssize_t set_in##offset##_min(struct device *dev, const char *buf, \
+ size_t count) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct adm1025_data *data = i2c_get_clientdata(client); \
+ data->in_min[offset] = IN_TO_REG(simple_strtol(buf, NULL, 10), \
+ in_scale[offset]); \
+ i2c_smbus_write_byte_data(client, ADM1025_REG_IN_MIN(offset), \
+ data->in_min[offset]); \
+ return count; \
+} \
+static ssize_t set_in##offset##_max(struct device *dev, const char *buf, \
+ size_t count) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct adm1025_data *data = i2c_get_clientdata(client); \
+ data->in_max[offset] = IN_TO_REG(simple_strtol(buf, NULL, 10), \
+ in_scale[offset]); \
+ i2c_smbus_write_byte_data(client, ADM1025_REG_IN_MAX(offset), \
+ data->in_max[offset]); \
+ return count; \
+} \
+static DEVICE_ATTR(in##offset##_min, S_IWUSR | S_IRUGO, \
+ show_in##offset##_min, set_in##offset##_min); \
+static DEVICE_ATTR(in##offset##_max, S_IWUSR | S_IRUGO, \
+ show_in##offset##_max, set_in##offset##_max);
+set_in(0);
+set_in(1);
+set_in(2);
+set_in(3);
+set_in(4);
+set_in(5);
+
+#define set_temp(offset) \
+static ssize_t set_temp##offset##_min(struct device *dev, const char *buf, \
+ size_t count) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct adm1025_data *data = i2c_get_clientdata(client); \
+ data->temp_min[offset-1] = TEMP_TO_REG(simple_strtol(buf, NULL, 10)); \
+ i2c_smbus_write_byte_data(client, ADM1025_REG_TEMP_LOW(offset-1), \
+ data->temp_min[offset-1]); \
+ return count; \
+} \
+static ssize_t set_temp##offset##_max(struct device *dev, const char *buf, \
+ size_t count) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct adm1025_data *data = i2c_get_clientdata(client); \
+ data->temp_max[offset-1] = TEMP_TO_REG(simple_strtol(buf, NULL, 10)); \
+ i2c_smbus_write_byte_data(client, ADM1025_REG_TEMP_HIGH(offset-1), \
+ data->temp_max[offset-1]); \
+ return count; \
+} \
+static DEVICE_ATTR(temp##offset##_min, S_IWUSR | S_IRUGO, \
+ show_temp##offset##_min, set_temp##offset##_min); \
+static DEVICE_ATTR(temp##offset##_max, S_IWUSR | S_IRUGO, \
+ show_temp##offset##_max, set_temp##offset##_max);
+set_temp(1);
+set_temp(2);
+
+static ssize_t show_alarms(struct device *dev, char *buf)
+{
+ struct adm1025_data *data = adm1025_update_device(dev);
+ return sprintf(buf, "%u\n", data->alarms);
+}
+static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+
+static ssize_t show_vid(struct device *dev, char *buf)
+{
+ struct adm1025_data *data = adm1025_update_device(dev);
+ return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm));
+}
+static DEVICE_ATTR(in1_ref, S_IRUGO, show_vid, NULL);
+
+static ssize_t show_vrm(struct device *dev, char *buf)
+{
+ struct adm1025_data *data = adm1025_update_device(dev);
+ return sprintf(buf, "%u\n", data->vrm);
+}
+static ssize_t set_vrm(struct device *dev, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adm1025_data *data = i2c_get_clientdata(client);
+ data->vrm = simple_strtoul(buf, NULL, 10);
+ return count;
+}
+static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
+
+/*
+ * Real code
+ */
+
+static int adm1025_attach_adapter(struct i2c_adapter *adapter)
+{
+ if (!(adapter->class & I2C_CLASS_HWMON))
+ return 0;
+ return i2c_detect(adapter, &addr_data, adm1025_detect);
+}
+
+/*
+ * The following function does more than just detection. If detection
+ * succeeds, it also registers the new chip.
+ */
+static int adm1025_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+ struct i2c_client *new_client;
+ struct adm1025_data *data;
+ int err = 0;
+ const char *name = "";
+ u8 config;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ goto exit;
+
+ if (!(data = kmalloc(sizeof(struct adm1025_data), GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ memset(data, 0, sizeof(struct adm1025_data));
+
+ /* The common I2C client data is placed right before the
+ ADM1025-specific data. */
+ new_client = &data->client;
+ i2c_set_clientdata(new_client, data);
+ new_client->addr = address;
+ new_client->adapter = adapter;
+ new_client->driver = &adm1025_driver;
+ new_client->flags = 0;
+
+ /*
+ * Now we do the remaining detection. A negative kind means that
+ * the driver was loaded with no force parameter (default), so we
+ * must both detect and identify the chip. A zero kind means that
+ * the driver was loaded with the force parameter, the detection
+ * step shall be skipped. A positive kind means that the driver
+ * was loaded with the force parameter and a given kind of chip is
+ * requested, so both the detection and the identification steps
+ * are skipped.
+ */
+ config = i2c_smbus_read_byte_data(new_client, ADM1025_REG_CONFIG);
+ if (kind < 0) { /* detection */
+ if ((config & 0x80) != 0x00
+ || (i2c_smbus_read_byte_data(new_client,
+ ADM1025_REG_STATUS1) & 0xC0) != 0x00
+ || (i2c_smbus_read_byte_data(new_client,
+ ADM1025_REG_STATUS2) & 0xBC) != 0x00) {
+ dev_dbg(&adapter->dev,
+ "ADM1025 detection failed at 0x%02x.\n",
+ address);
+ goto exit_free;
+ }
+ }
+
+ if (kind <= 0) { /* identification */
+ u8 man_id, chip_id;
+
+ man_id = i2c_smbus_read_byte_data(new_client,
+ ADM1025_REG_MAN_ID);
+ chip_id = i2c_smbus_read_byte_data(new_client,
+ ADM1025_REG_CHIP_ID);
+
+ if (man_id == 0x41) { /* Analog Devices */
+ if ((chip_id & 0xF0) == 0x20) { /* ADM1025/ADM1025A */
+ kind = adm1025;
+ }
+ } else
+ if (man_id == 0xA1) { /* Philips */
+ if (address != 0x2E
+ && (chip_id & 0xF0) == 0x20) { /* NE1619 */
+ kind = ne1619;
+ }
+ }
+
+ if (kind <= 0) { /* identification failed */
+ dev_info(&adapter->dev,
+ "Unsupported chip (man_id=0x%02X, "
+ "chip_id=0x%02X).\n", man_id, chip_id);
+ goto exit_free;
+ }
+ }
+
+ if (kind == adm1025) {
+ name = "adm1025";
+ } else if (kind == ne1619) {
+ name = "ne1619";
+ }
+
+ /* We can fill in the remaining client fields */
+ strlcpy(new_client->name, name, I2C_NAME_SIZE);
+ new_client->id = adm1025_id++;
+ data->valid = 0;
+ init_MUTEX(&data->update_lock);
+
+ /* Tell the I2C layer a new client has arrived */
+ if ((err = i2c_attach_client(new_client)))
+ goto exit_free;
+
+ /* Initialize the ADM1025 chip */
+ adm1025_init_client(new_client);
+
+ /* Register sysfs hooks */
+ device_create_file(&new_client->dev, &dev_attr_in0_input);
+ device_create_file(&new_client->dev, &dev_attr_in1_input);
+ device_create_file(&new_client->dev, &dev_attr_in2_input);
+ device_create_file(&new_client->dev, &dev_attr_in3_input);
+ device_create_file(&new_client->dev, &dev_attr_in5_input);
+ device_create_file(&new_client->dev, &dev_attr_in0_min);
+ device_create_file(&new_client->dev, &dev_attr_in1_min);
+ device_create_file(&new_client->dev, &dev_attr_in2_min);
+ device_create_file(&new_client->dev, &dev_attr_in3_min);
+ device_create_file(&new_client->dev, &dev_attr_in5_min);
+ device_create_file(&new_client->dev, &dev_attr_in0_max);
+ device_create_file(&new_client->dev, &dev_attr_in1_max);
+ device_create_file(&new_client->dev, &dev_attr_in2_max);
+ device_create_file(&new_client->dev, &dev_attr_in3_max);
+ device_create_file(&new_client->dev, &dev_attr_in5_max);
+ device_create_file(&new_client->dev, &dev_attr_temp1_input);
+ device_create_file(&new_client->dev, &dev_attr_temp2_input);
+ device_create_file(&new_client->dev, &dev_attr_temp1_min);
+ device_create_file(&new_client->dev, &dev_attr_temp2_min);
+ device_create_file(&new_client->dev, &dev_attr_temp1_max);
+ device_create_file(&new_client->dev, &dev_attr_temp2_max);
+ device_create_file(&new_client->dev, &dev_attr_alarms);
+ device_create_file(&new_client->dev, &dev_attr_in1_ref);
+ device_create_file(&new_client->dev, &dev_attr_vrm);
+
+ /* Pin 11 is either in4 (+12V) or VID4 */
+ if (!(config & 0x20)) {
+ device_create_file(&new_client->dev, &dev_attr_in4_input);
+ device_create_file(&new_client->dev, &dev_attr_in4_min);
+ device_create_file(&new_client->dev, &dev_attr_in4_max);
+ }
+
+ return 0;
+
+exit_free:
+ kfree(data);
+exit:
+ return err;
+}
+
+static void adm1025_init_client(struct i2c_client *client)
+{
+ u8 reg;
+ struct adm1025_data *data = i2c_get_clientdata(client);
+ int i;
+
+ data->vrm = 82;
+
+ /*
+ * Set high limits
+ * Usually we avoid setting limits on driver init, but it happens
+ * that the ADM1025 comes with stupid default limits (all registers
+ * set to 0). In case the chip has not gone through any limit
+ * setting yet, we better set the high limits to the max so that
+ * no alarm triggers.
+ */
+ for (i=0; i<6; i++) {
+ reg = i2c_smbus_read_byte_data(client,
+ ADM1025_REG_IN_MAX(i));
+ if (reg == 0)
+ i2c_smbus_write_byte_data(client,
+ ADM1025_REG_IN_MAX(i),
+ 0xFF);
+ }
+ for (i=0; i<2; i++) {
+ reg = i2c_smbus_read_byte_data(client,
+ ADM1025_REG_TEMP_HIGH(i));
+ if (reg == 0)
+ i2c_smbus_write_byte_data(client,
+ ADM1025_REG_TEMP_HIGH(i),
+ 0x7F);
+ }
+
+ /*
+ * Start the conversions
+ */
+ reg = i2c_smbus_read_byte_data(client, ADM1025_REG_CONFIG);
+ if (!(reg & 0x01))
+ i2c_smbus_write_byte_data(client, ADM1025_REG_CONFIG,
+ (reg&0x7E)|0x01);
+}
+
+static int adm1025_detach_client(struct i2c_client *client)
+{
+ int err;
+
+ if ((err = i2c_detach_client(client))) {
+ dev_err(&client->dev, "Client deregistration failed, "
+ "client not detached.\n");
+ return err;
+ }
+
+ kfree(i2c_get_clientdata(client));
+ return 0;
+}
+
+static struct adm1025_data *adm1025_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adm1025_data *data = i2c_get_clientdata(client);
+
+ down(&data->update_lock);
+
+ if ((jiffies - data->last_updated > HZ * 2) ||
+ (jiffies < data->last_updated) ||
+ !data->valid) {
+ int i;
+
+ dev_dbg(&client->dev, "Updating data.\n");
+ for (i=0; i<6; i++) {
+ data->in[i] = i2c_smbus_read_byte_data(client,
+ ADM1025_REG_IN(i));
+ data->in_min[i] = i2c_smbus_read_byte_data(client,
+ ADM1025_REG_IN_MIN(i));
+ data->in_max[i] = i2c_smbus_read_byte_data(client,
+ ADM1025_REG_IN_MAX(i));
+ }
+ for (i=0; i<2; i++) {
+ data->temp[i] = i2c_smbus_read_byte_data(client,
+ ADM1025_REG_TEMP(i));
+ data->temp_min[i] = i2c_smbus_read_byte_data(client,
+ ADM1025_REG_TEMP_LOW(i));
+ data->temp_max[i] = i2c_smbus_read_byte_data(client,
+ ADM1025_REG_TEMP_HIGH(i));
+ }
+ data->alarms = i2c_smbus_read_byte_data(client,
+ ADM1025_REG_STATUS1)
+ | (i2c_smbus_read_byte_data(client,
+ ADM1025_REG_STATUS2) << 8);
+ data->vid = (i2c_smbus_read_byte_data(client,
+ ADM1025_REG_VID) & 0x0f)
+ | ((i2c_smbus_read_byte_data(client,
+ ADM1025_REG_VID4) & 0x01) << 4);
+
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+
+ up(&data->update_lock);
+
+ return data;
+}
+
+static int __init sensors_adm1025_init(void)
+{
+ return i2c_add_driver(&adm1025_driver);
+}
+
+static void __exit sensors_adm1025_exit(void)
+{
+ i2c_del_driver(&adm1025_driver);
+}
+
+MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("ADM1025 driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_adm1025_init);
+module_exit(sensors_adm1025_exit);
--- /dev/null
+/*
+ adm1031.c - Part of lm_sensors, Linux kernel modules for hardware
+ monitoring
+ Based on lm75.c and lm85.c
+ Supports adm1030 / adm1031
+ Copyright (C) 2004 Alexandre d'Alton <alex@alexdalton.org>
+ Reworked by Jean Delvare <khali@linux-fr.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.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+
+/* Following macros takes channel parameter starting from 0 to 2 */
+#define ADM1031_REG_FAN_SPEED(nr) (0x08 + (nr))
+#define ADM1031_REG_FAN_DIV(nr) (0x20 + (nr))
+#define ADM1031_REG_PWM (0x22)
+#define ADM1031_REG_FAN_MIN(nr) (0x10 + (nr))
+
+#define ADM1031_REG_TEMP_MAX(nr) (0x14 + 4*(nr))
+#define ADM1031_REG_TEMP_MIN(nr) (0x15 + 4*(nr))
+#define ADM1031_REG_TEMP_CRIT(nr) (0x16 + 4*(nr))
+
+#define ADM1031_REG_TEMP(nr) (0xa + (nr))
+#define ADM1031_REG_AUTO_TEMP(nr) (0x24 + (nr))
+
+#define ADM1031_REG_STATUS(nr) (0x2 + (nr))
+
+#define ADM1031_REG_CONF1 0x0
+#define ADM1031_REG_CONF2 0x1
+#define ADM1031_REG_EXT_TEMP 0x6
+
+#define ADM1031_CONF1_MONITOR_ENABLE 0x01 /* Monitoring enable */
+#define ADM1031_CONF1_PWM_INVERT 0x08 /* PWM Invert */
+#define ADM1031_CONF1_AUTO_MODE 0x80 /* Auto FAN */
+
+#define ADM1031_CONF2_PWM1_ENABLE 0x01
+#define ADM1031_CONF2_PWM2_ENABLE 0x02
+#define ADM1031_CONF2_TACH1_ENABLE 0x04
+#define ADM1031_CONF2_TACH2_ENABLE 0x08
+#define ADM1031_CONF2_TEMP_ENABLE(chan) (0x10 << (chan))
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, I2C_CLIENT_END };
+static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
+static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_2(adm1030, adm1031);
+
+typedef u8 auto_chan_table_t[8][2];
+
+/* Each client has this additional data */
+struct adm1031_data {
+ struct i2c_client client;
+ struct semaphore update_lock;
+ int chip_type;
+ char valid; /* !=0 if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+ /* The chan_select_table contains the possible configurations for
+ * auto fan control.
+ */
+ auto_chan_table_t *chan_select_table;
+ u16 alarm;
+ u8 conf1;
+ u8 conf2;
+ u8 fan[2];
+ u8 fan_div[2];
+ u8 fan_min[2];
+ u8 pwm[2];
+ u8 old_pwm[2];
+ s8 temp[3];
+ u8 ext_temp[3];
+ u8 auto_temp[3];
+ u8 auto_temp_min[3];
+ u8 auto_temp_off[3];
+ u8 auto_temp_max[3];
+ s8 temp_min[3];
+ s8 temp_max[3];
+ s8 temp_crit[3];
+};
+
+static int adm1031_attach_adapter(struct i2c_adapter *adapter);
+static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind);
+static void adm1031_init_client(struct i2c_client *client);
+static int adm1031_detach_client(struct i2c_client *client);
+static struct adm1031_data *adm1031_update_device(struct device *dev);
+
+/* This is the driver that will be inserted */
+static struct i2c_driver adm1031_driver = {
+ .owner = THIS_MODULE,
+ .name = "adm1031",
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = adm1031_attach_adapter,
+ .detach_client = adm1031_detach_client,
+};
+
+static int adm1031_id;
+
+static inline u8 adm1031_read_value(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static inline int
+adm1031_write_value(struct i2c_client *client, u8 reg, unsigned int value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+
+#define TEMP_TO_REG(val) (((val) < 0 ? ((val - 500) / 1000) : \
+ ((val + 500) / 1000)))
+
+#define TEMP_FROM_REG(val) ((val) * 1000)
+
+#define TEMP_FROM_REG_EXT(val, ext) (TEMP_FROM_REG(val) + (ext) * 125)
+
+#define FAN_FROM_REG(reg, div) ((reg) ? (11250 * 60) / ((reg) * (div)) : 0)
+
+static int FAN_TO_REG(int reg, int div)
+{
+ int tmp;
+ tmp = FAN_FROM_REG(SENSORS_LIMIT(reg, 0, 65535), div);
+ return tmp > 255 ? 255 : tmp;
+}
+
+#define FAN_DIV_FROM_REG(reg) (1<<(((reg)&0xc0)>>6))
+
+#define PWM_TO_REG(val) (SENSORS_LIMIT((val), 0, 255) >> 4)
+#define PWM_FROM_REG(val) ((val) << 4)
+
+#define FAN_CHAN_FROM_REG(reg) (((reg) >> 5) & 7)
+#define FAN_CHAN_TO_REG(val, reg) \
+ (((reg) & 0x1F) | (((val) << 5) & 0xe0))
+
+#define AUTO_TEMP_MIN_TO_REG(val, reg) \
+ ((((val)/500) & 0xf8)|((reg) & 0x7))
+#define AUTO_TEMP_RANGE_FROM_REG(reg) (5000 * (1<< ((reg)&0x7)))
+#define AUTO_TEMP_MIN_FROM_REG(reg) (1000 * ((((reg) >> 3) & 0x1f) << 2))
+
+#define AUTO_TEMP_MIN_FROM_REG_DEG(reg) ((((reg) >> 3) & 0x1f) << 2)
+
+#define AUTO_TEMP_OFF_FROM_REG(reg) \
+ (AUTO_TEMP_MIN_FROM_REG(reg) - 5000)
+
+#define AUTO_TEMP_MAX_FROM_REG(reg) \
+ (AUTO_TEMP_RANGE_FROM_REG(reg) + \
+ AUTO_TEMP_MIN_FROM_REG(reg))
+
+static int AUTO_TEMP_MAX_TO_REG(int val, int reg, int pwm)
+{
+ int ret;
+ int range = val - AUTO_TEMP_MIN_FROM_REG(reg);
+
+ range = ((val - AUTO_TEMP_MIN_FROM_REG(reg))*10)/(16 - pwm);
+ ret = ((reg & 0xf8) |
+ (range < 10000 ? 0 :
+ range < 20000 ? 1 :
+ range < 40000 ? 2 : range < 80000 ? 3 : 4));
+ return ret;
+}
+
+/* FAN auto control */
+#define GET_FAN_AUTO_BITFIELD(data, idx) \
+ (*(data)->chan_select_table)[FAN_CHAN_FROM_REG((data)->conf1)][idx%2]
+
+/* The tables below contains the possible values for the auto fan
+ * control bitfields. the index in the table is the register value.
+ * MSb is the auto fan control enable bit, so the four first entries
+ * in the table disables auto fan control when both bitfields are zero.
+ */
+static auto_chan_table_t auto_channel_select_table_adm1031 = {
+ {0, 0}, {0, 0}, {0, 0}, {0, 0},
+ {2 /*0b010 */ , 4 /*0b100 */ },
+ {2 /*0b010 */ , 2 /*0b010 */ },
+ {4 /*0b100 */ , 4 /*0b100 */ },
+ {7 /*0b111 */ , 7 /*0b111 */ },
+};
+
+static auto_chan_table_t auto_channel_select_table_adm1030 = {
+ {0, 0}, {0, 0}, {0, 0}, {0, 0},
+ {2 /*0b10 */ , 0},
+ {0xff /*invalid */ , 0},
+ {0xff /*invalid */ , 0},
+ {3 /*0b11 */ , 0},
+};
+
+/* That function checks if a bitfield is valid and returns the other bitfield
+ * nearest match if no exact match where found.
+ */
+static int
+get_fan_auto_nearest(struct adm1031_data *data,
+ int chan, u8 val, u8 reg, u8 * new_reg)
+{
+ int i;
+ int first_match = -1, exact_match = -1;
+ u8 other_reg_val =
+ (*data->chan_select_table)[FAN_CHAN_FROM_REG(reg)][chan ? 0 : 1];
+
+ if (val == 0) {
+ *new_reg = 0;
+ return 0;
+ }
+
+ for (i = 0; i < 8; i++) {
+ if ((val == (*data->chan_select_table)[i][chan]) &&
+ ((*data->chan_select_table)[i][chan ? 0 : 1] ==
+ other_reg_val)) {
+ /* We found an exact match */
+ exact_match = i;
+ break;
+ } else if (val == (*data->chan_select_table)[i][chan] &&
+ first_match == -1) {
+ /* Save the first match in case of an exact match has not been
+ * found
+ */
+ first_match = i;
+ }
+ }
+
+ if (exact_match >= 0) {
+ *new_reg = exact_match;
+ } else if (first_match >= 0) {
+ *new_reg = first_match;
+ } else {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static ssize_t show_fan_auto_channel(struct device *dev, char *buf, int nr)
+{
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n", GET_FAN_AUTO_BITFIELD(data, nr));
+}
+
+static ssize_t
+set_fan_auto_channel(struct device *dev, const char *buf, size_t count, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adm1031_data *data = i2c_get_clientdata(client);
+ int val;
+ u8 reg;
+ int ret;
+ u8 old_fan_mode;
+
+ old_fan_mode = data->conf1;
+
+ down(&data->update_lock);
+ val = simple_strtol(buf, NULL, 10);
+
+ if ((ret = get_fan_auto_nearest(data, nr, val, data->conf1, ®))) {
+ up(&data->update_lock);
+ return ret;
+ }
+ if (((data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1)) & ADM1031_CONF1_AUTO_MODE) ^
+ (old_fan_mode & ADM1031_CONF1_AUTO_MODE)) {
+ if (data->conf1 & ADM1031_CONF1_AUTO_MODE){
+ /* Switch to Auto Fan Mode
+ * Save PWM registers
+ * Set PWM registers to 33% Both */
+ data->old_pwm[0] = data->pwm[0];
+ data->old_pwm[1] = data->pwm[1];
+ adm1031_write_value(client, ADM1031_REG_PWM, 0x55);
+ } else {
+ /* Switch to Manual Mode */
+ data->pwm[0] = data->old_pwm[0];
+ data->pwm[1] = data->old_pwm[1];
+ /* Restore PWM registers */
+ adm1031_write_value(client, ADM1031_REG_PWM,
+ data->pwm[0] | (data->pwm[1] << 4));
+ }
+ }
+ data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1);
+ adm1031_write_value(client, ADM1031_REG_CONF1, data->conf1);
+ up(&data->update_lock);
+ return count;
+}
+
+#define fan_auto_channel_offset(offset) \
+static ssize_t show_fan_auto_channel_##offset (struct device *dev, char *buf) \
+{ \
+ return show_fan_auto_channel(dev, buf, 0x##offset - 1); \
+} \
+static ssize_t set_fan_auto_channel_##offset (struct device *dev, \
+ const char *buf, size_t count) \
+{ \
+ return set_fan_auto_channel(dev, buf, count, 0x##offset - 1); \
+} \
+static DEVICE_ATTR(auto_fan##offset##_channel, S_IRUGO | S_IWUSR, \
+ show_fan_auto_channel_##offset, \
+ set_fan_auto_channel_##offset)
+
+fan_auto_channel_offset(1);
+fan_auto_channel_offset(2);
+
+/* Auto Temps */
+static ssize_t show_auto_temp_off(struct device *dev, char *buf, int nr)
+{
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n",
+ AUTO_TEMP_OFF_FROM_REG(data->auto_temp[nr]));
+}
+static ssize_t show_auto_temp_min(struct device *dev, char *buf, int nr)
+{
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n",
+ AUTO_TEMP_MIN_FROM_REG(data->auto_temp[nr]));
+}
+static ssize_t
+set_auto_temp_min(struct device *dev, const char *buf, size_t count, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adm1031_data *data = i2c_get_clientdata(client);
+ int val;
+
+ down(&data->update_lock);
+ val = simple_strtol(buf, NULL, 10);
+ data->auto_temp[nr] = AUTO_TEMP_MIN_TO_REG(val, data->auto_temp[nr]);
+ adm1031_write_value(client, ADM1031_REG_AUTO_TEMP(nr),
+ data->auto_temp[nr]);
+ up(&data->update_lock);
+ return count;
+}
+static ssize_t show_auto_temp_max(struct device *dev, char *buf, int nr)
+{
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n",
+ AUTO_TEMP_MAX_FROM_REG(data->auto_temp[nr]));
+}
+static ssize_t
+set_auto_temp_max(struct device *dev, const char *buf, size_t count, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adm1031_data *data = i2c_get_clientdata(client);
+ int val;
+
+ down(&data->update_lock);
+ val = simple_strtol(buf, NULL, 10);
+ data->temp_max[nr] = AUTO_TEMP_MAX_TO_REG(val, data->auto_temp[nr], data->pwm[nr]);
+ adm1031_write_value(client, ADM1031_REG_AUTO_TEMP(nr),
+ data->temp_max[nr]);
+ up(&data->update_lock);
+ return count;
+}
+
+#define auto_temp_reg(offset) \
+static ssize_t show_auto_temp_##offset##_off (struct device *dev, char *buf) \
+{ \
+ return show_auto_temp_off(dev, buf, 0x##offset - 1); \
+} \
+static ssize_t show_auto_temp_##offset##_min (struct device *dev, char *buf) \
+{ \
+ return show_auto_temp_min(dev, buf, 0x##offset - 1); \
+} \
+static ssize_t show_auto_temp_##offset##_max (struct device *dev, char *buf) \
+{ \
+ return show_auto_temp_max(dev, buf, 0x##offset - 1); \
+} \
+static ssize_t set_auto_temp_##offset##_min (struct device *dev, \
+ const char *buf, size_t count) \
+{ \
+ return set_auto_temp_min(dev, buf, count, 0x##offset - 1); \
+} \
+static ssize_t set_auto_temp_##offset##_max (struct device *dev, \
+ const char *buf, size_t count) \
+{ \
+ return set_auto_temp_max(dev, buf, count, 0x##offset - 1); \
+} \
+static DEVICE_ATTR(auto_temp##offset##_off, S_IRUGO, \
+ show_auto_temp_##offset##_off, NULL); \
+static DEVICE_ATTR(auto_temp##offset##_min, S_IRUGO | S_IWUSR, \
+ show_auto_temp_##offset##_min, set_auto_temp_##offset##_min);\
+static DEVICE_ATTR(auto_temp##offset##_max, S_IRUGO | S_IWUSR, \
+ show_auto_temp_##offset##_max, set_auto_temp_##offset##_max)
+
+auto_temp_reg(1);
+auto_temp_reg(2);
+auto_temp_reg(3);
+
+/* pwm */
+static ssize_t show_pwm(struct device *dev, char *buf, int nr)
+{
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[nr]));
+}
+static ssize_t
+set_pwm(struct device *dev, const char *buf, size_t count, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adm1031_data *data = i2c_get_clientdata(client);
+ int val;
+ int reg;
+ down(&data->update_lock);
+ val = simple_strtol(buf, NULL, 10);
+ if ((data->conf1 & ADM1031_CONF1_AUTO_MODE) &&
+ (((val>>4) & 0xf) != 5)) {
+ /* In automatic mode, the only PWM accepted is 33% */
+ up(&data->update_lock);
+ return -EINVAL;
+ }
+ data->pwm[nr] = PWM_TO_REG(val);
+ reg = adm1031_read_value(client, ADM1031_REG_PWM);
+ adm1031_write_value(client, ADM1031_REG_PWM,
+ nr ? ((data->pwm[nr] << 4) & 0xf0) | (reg & 0xf)
+ : (data->pwm[nr] & 0xf) | (reg & 0xf0));
+ up(&data->update_lock);
+ return count;
+}
+
+#define pwm_reg(offset) \
+static ssize_t show_pwm_##offset (struct device *dev, char *buf) \
+{ \
+ return show_pwm(dev, buf, 0x##offset - 1); \
+} \
+static ssize_t set_pwm_##offset (struct device *dev, \
+ const char *buf, size_t count) \
+{ \
+ return set_pwm(dev, buf, count, 0x##offset - 1); \
+} \
+static DEVICE_ATTR(fan##offset##_pwm, S_IRUGO | S_IWUSR, \
+ show_pwm_##offset, set_pwm_##offset)
+
+pwm_reg(1);
+pwm_reg(2);
+
+/* Fans */
+
+/*
+ * That function checks the cases where the fan reading is not
+ * relevent. It is used to provide 0 as fan reading when the fan is
+ * not supposed to run
+ */
+static int trust_fan_readings(struct adm1031_data *data, int chan)
+{
+ int res = 0;
+
+ if (data->conf1 & ADM1031_CONF1_AUTO_MODE) {
+ switch (data->conf1 & 0x60) {
+ case 0x00: /* remote temp1 controls fan1 remote temp2 controls fan2 */
+ res = data->temp[chan+1] >=
+ AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[chan+1]);
+ break;
+ case 0x20: /* remote temp1 controls both fans */
+ res =
+ data->temp[1] >=
+ AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[1]);
+ break;
+ case 0x40: /* remote temp2 controls both fans */
+ res =
+ data->temp[2] >=
+ AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[2]);
+ break;
+ case 0x60: /* max controls both fans */
+ res =
+ data->temp[0] >=
+ AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[0])
+ || data->temp[1] >=
+ AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[1])
+ || (data->chip_type == adm1031
+ && data->temp[2] >=
+ AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[2]));
+ break;
+ }
+ } else {
+ res = data->pwm[chan] > 0;
+ }
+ return res;
+}
+
+
+static ssize_t show_fan(struct device *dev, char *buf, int nr)
+{
+ struct adm1031_data *data = adm1031_update_device(dev);
+ int value;
+
+ value = trust_fan_readings(data, nr) ? FAN_FROM_REG(data->fan[nr],
+ FAN_DIV_FROM_REG(data->fan_div[nr])) : 0;
+ return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t show_fan_div(struct device *dev, char *buf, int nr)
+{
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n", FAN_DIV_FROM_REG(data->fan_div[nr]));
+}
+static ssize_t show_fan_min(struct device *dev, char *buf, int nr)
+{
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n",
+ FAN_FROM_REG(data->fan_min[nr],
+ FAN_DIV_FROM_REG(data->fan_div[nr])));
+}
+static ssize_t
+set_fan_min(struct device *dev, const char *buf, size_t count, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adm1031_data *data = i2c_get_clientdata(client);
+ int val;
+
+ down(&data->update_lock);
+ val = simple_strtol(buf, NULL, 10);
+ if (val) {
+ data->fan_min[nr] =
+ FAN_TO_REG(val, FAN_DIV_FROM_REG(data->fan_div[nr]));
+ } else {
+ data->fan_min[nr] = 0xff;
+ }
+ adm1031_write_value(client, ADM1031_REG_FAN_MIN(nr), data->fan_min[nr]);
+ up(&data->update_lock);
+ return count;
+}
+static ssize_t
+set_fan_div(struct device *dev, const char *buf, size_t count, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adm1031_data *data = i2c_get_clientdata(client);
+ int val;
+ u8 tmp;
+ int old_div = FAN_DIV_FROM_REG(data->fan_div[nr]);
+ int new_min;
+
+ val = simple_strtol(buf, NULL, 10);
+ tmp = val == 8 ? 0xc0 :
+ val == 4 ? 0x80 :
+ val == 2 ? 0x40 :
+ val == 1 ? 0x00 :
+ 0xff;
+ if (tmp == 0xff)
+ return -EINVAL;
+ down(&data->update_lock);
+ data->fan_div[nr] = (tmp & 0xC0) | (0x3f & data->fan_div[nr]);
+ new_min = data->fan_min[nr] * old_div /
+ FAN_DIV_FROM_REG(data->fan_div[nr]);
+ data->fan_min[nr] = new_min > 0xff ? 0xff : new_min;
+ data->fan[nr] = data->fan[nr] * old_div /
+ FAN_DIV_FROM_REG(data->fan_div[nr]);
+
+ adm1031_write_value(client, ADM1031_REG_FAN_DIV(nr),
+ data->fan_div[nr]);
+ adm1031_write_value(client, ADM1031_REG_FAN_MIN(nr),
+ data->fan_min[nr]);
+ up(&data->update_lock);
+ return count;
+}
+
+#define fan_offset(offset) \
+static ssize_t show_fan_##offset (struct device *dev, char *buf) \
+{ \
+ return show_fan(dev, buf, 0x##offset - 1); \
+} \
+static ssize_t show_fan_##offset##_min (struct device *dev, char *buf) \
+{ \
+ return show_fan_min(dev, buf, 0x##offset - 1); \
+} \
+static ssize_t show_fan_##offset##_div (struct device *dev, char *buf) \
+{ \
+ return show_fan_div(dev, buf, 0x##offset - 1); \
+} \
+static ssize_t set_fan_##offset##_min (struct device *dev, \
+ const char *buf, size_t count) \
+{ \
+ return set_fan_min(dev, buf, count, 0x##offset - 1); \
+} \
+static ssize_t set_fan_##offset##_div (struct device *dev, \
+ const char *buf, size_t count) \
+{ \
+ return set_fan_div(dev, buf, count, 0x##offset - 1); \
+} \
+static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, \
+ NULL); \
+static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
+ show_fan_##offset##_min, set_fan_##offset##_min); \
+static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
+ show_fan_##offset##_div, set_fan_##offset##_div); \
+static DEVICE_ATTR(auto_fan##offset##_min_pwm, S_IRUGO | S_IWUSR, \
+ show_pwm_##offset, set_pwm_##offset)
+
+fan_offset(1);
+fan_offset(2);
+
+
+/* Temps */
+static ssize_t show_temp(struct device *dev, char *buf, int nr)
+{
+ struct adm1031_data *data = adm1031_update_device(dev);
+ int ext;
+ ext = nr == 0 ?
+ ((data->ext_temp[nr] >> 6) & 0x3) * 2 :
+ (((data->ext_temp[nr] >> ((nr - 1) * 3)) & 7));
+ return sprintf(buf, "%d\n", TEMP_FROM_REG_EXT(data->temp[nr], ext));
+}
+static ssize_t show_temp_min(struct device *dev, char *buf, int nr)
+{
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[nr]));
+}
+static ssize_t show_temp_max(struct device *dev, char *buf, int nr)
+{
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[nr]));
+}
+static ssize_t show_temp_crit(struct device *dev, char *buf, int nr)
+{
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit[nr]));
+}
+static ssize_t
+set_temp_min(struct device *dev, const char *buf, size_t count, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adm1031_data *data = i2c_get_clientdata(client);
+ int val;
+
+ val = simple_strtol(buf, NULL, 10);
+ val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875);
+ down(&data->update_lock);
+ data->temp_min[nr] = TEMP_TO_REG(val);
+ adm1031_write_value(client, ADM1031_REG_TEMP_MIN(nr),
+ data->temp_min[nr]);
+ up(&data->update_lock);
+ return count;
+}
+static ssize_t
+set_temp_max(struct device *dev, const char *buf, size_t count, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adm1031_data *data = i2c_get_clientdata(client);
+ int val;
+
+ val = simple_strtol(buf, NULL, 10);
+ val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875);
+ down(&data->update_lock);
+ data->temp_max[nr] = TEMP_TO_REG(val);
+ adm1031_write_value(client, ADM1031_REG_TEMP_MAX(nr),
+ data->temp_max[nr]);
+ up(&data->update_lock);
+ return count;
+}
+static ssize_t
+set_temp_crit(struct device *dev, const char *buf, size_t count, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adm1031_data *data = i2c_get_clientdata(client);
+ int val;
+
+ val = simple_strtol(buf, NULL, 10);
+ val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875);
+ down(&data->update_lock);
+ data->temp_crit[nr] = TEMP_TO_REG(val);
+ adm1031_write_value(client, ADM1031_REG_TEMP_CRIT(nr),
+ data->temp_crit[nr]);
+ up(&data->update_lock);
+ return count;
+}
+
+#define temp_reg(offset) \
+static ssize_t show_temp_##offset (struct device *dev, char *buf) \
+{ \
+ return show_temp(dev, buf, 0x##offset - 1); \
+} \
+static ssize_t show_temp_##offset##_min (struct device *dev, char *buf) \
+{ \
+ return show_temp_min(dev, buf, 0x##offset - 1); \
+} \
+static ssize_t show_temp_##offset##_max (struct device *dev, char *buf) \
+{ \
+ return show_temp_max(dev, buf, 0x##offset - 1); \
+} \
+static ssize_t show_temp_##offset##_crit (struct device *dev, char *buf) \
+{ \
+ return show_temp_crit(dev, buf, 0x##offset - 1); \
+} \
+static ssize_t set_temp_##offset##_min (struct device *dev, \
+ const char *buf, size_t count) \
+{ \
+ return set_temp_min(dev, buf, count, 0x##offset - 1); \
+} \
+static ssize_t set_temp_##offset##_max (struct device *dev, \
+ const char *buf, size_t count) \
+{ \
+ return set_temp_max(dev, buf, count, 0x##offset - 1); \
+} \
+static ssize_t set_temp_##offset##_crit (struct device *dev, \
+ const char *buf, size_t count) \
+{ \
+ return set_temp_crit(dev, buf, count, 0x##offset - 1); \
+} \
+static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, \
+ NULL); \
+static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \
+ show_temp_##offset##_min, set_temp_##offset##_min); \
+static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \
+ show_temp_##offset##_max, set_temp_##offset##_max); \
+static DEVICE_ATTR(temp##offset##_crit, S_IRUGO | S_IWUSR, \
+ show_temp_##offset##_crit, set_temp_##offset##_crit)
+
+temp_reg(1);
+temp_reg(2);
+temp_reg(3);
+
+/* Alarms */
+static ssize_t show_alarms(struct device *dev, char *buf)
+{
+ struct adm1031_data *data = adm1031_update_device(dev);
+ return sprintf(buf, "%d\n", data->alarm);
+}
+
+static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+
+
+static int adm1031_attach_adapter(struct i2c_adapter *adapter)
+{
+ if (!(adapter->class & I2C_CLASS_HWMON))
+ return 0;
+ return i2c_detect(adapter, &addr_data, adm1031_detect);
+}
+
+/* This function is called by i2c_detect */
+static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+ struct i2c_client *new_client;
+ struct adm1031_data *data;
+ int err = 0;
+ const char *name = "";
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ goto exit;
+
+ if (!(data = kmalloc(sizeof(struct adm1031_data), GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ memset(data, 0, sizeof(struct adm1031_data));
+
+ new_client = &data->client;
+ i2c_set_clientdata(new_client, data);
+ new_client->addr = address;
+ new_client->adapter = adapter;
+ new_client->driver = &adm1031_driver;
+ new_client->flags = 0;
+
+ if (kind < 0) {
+ int id, co;
+ id = i2c_smbus_read_byte_data(new_client, 0x3d);
+ co = i2c_smbus_read_byte_data(new_client, 0x3e);
+
+ if (!((id == 0x31 || id == 0x30) && co == 0x41))
+ goto exit_free;
+ kind = (id == 0x30) ? adm1030 : adm1031;
+ }
+
+ if (kind <= 0)
+ kind = adm1031;
+
+ /* Given the detected chip type, set the chip name and the
+ * auto fan control helper table. */
+ if (kind == adm1030) {
+ name = "adm1030";
+ data->chan_select_table = &auto_channel_select_table_adm1030;
+ } else if (kind == adm1031) {
+ name = "adm1031";
+ data->chan_select_table = &auto_channel_select_table_adm1031;
+ }
+ data->chip_type = kind;
+
+ strlcpy(new_client->name, name, I2C_NAME_SIZE);
+
+ new_client->id = adm1031_id++;
+ data->valid = 0;
+ init_MUTEX(&data->update_lock);
+
+ /* Tell the I2C layer a new client has arrived */
+ if ((err = i2c_attach_client(new_client)))
+ goto exit_free;
+
+ /* Initialize the ADM1031 chip */
+ adm1031_init_client(new_client);
+
+ /* Register sysfs hooks */
+ device_create_file(&new_client->dev, &dev_attr_fan1_input);
+ device_create_file(&new_client->dev, &dev_attr_fan1_div);
+ device_create_file(&new_client->dev, &dev_attr_fan1_min);
+ device_create_file(&new_client->dev, &dev_attr_fan1_pwm);
+ device_create_file(&new_client->dev, &dev_attr_auto_fan1_channel);
+ device_create_file(&new_client->dev, &dev_attr_temp1_input);
+ device_create_file(&new_client->dev, &dev_attr_temp1_min);
+ device_create_file(&new_client->dev, &dev_attr_temp1_max);
+ device_create_file(&new_client->dev, &dev_attr_temp1_crit);
+ device_create_file(&new_client->dev, &dev_attr_temp2_input);
+ device_create_file(&new_client->dev, &dev_attr_temp2_min);
+ device_create_file(&new_client->dev, &dev_attr_temp2_max);
+ device_create_file(&new_client->dev, &dev_attr_temp2_crit);
+
+ device_create_file(&new_client->dev, &dev_attr_auto_temp1_off);
+ device_create_file(&new_client->dev, &dev_attr_auto_temp1_min);
+ device_create_file(&new_client->dev, &dev_attr_auto_temp1_max);
+
+ device_create_file(&new_client->dev, &dev_attr_auto_temp2_off);
+ device_create_file(&new_client->dev, &dev_attr_auto_temp2_min);
+ device_create_file(&new_client->dev, &dev_attr_auto_temp2_max);
+
+ device_create_file(&new_client->dev, &dev_attr_auto_fan1_min_pwm);
+
+ device_create_file(&new_client->dev, &dev_attr_alarms);
+
+ if (kind == adm1031) {
+ device_create_file(&new_client->dev, &dev_attr_fan2_input);
+ device_create_file(&new_client->dev, &dev_attr_fan2_div);
+ device_create_file(&new_client->dev, &dev_attr_fan2_min);
+ device_create_file(&new_client->dev, &dev_attr_fan2_pwm);
+ device_create_file(&new_client->dev,
+ &dev_attr_auto_fan2_channel);
+ device_create_file(&new_client->dev, &dev_attr_temp3_input);
+ device_create_file(&new_client->dev, &dev_attr_temp3_min);
+ device_create_file(&new_client->dev, &dev_attr_temp3_max);
+ device_create_file(&new_client->dev, &dev_attr_temp3_crit);
+ device_create_file(&new_client->dev, &dev_attr_auto_temp3_off);
+ device_create_file(&new_client->dev, &dev_attr_auto_temp3_min);
+ device_create_file(&new_client->dev, &dev_attr_auto_temp3_max);
+ device_create_file(&new_client->dev, &dev_attr_auto_fan2_min_pwm);
+ }
+
+ return 0;
+
+exit_free:
+ kfree(new_client);
+exit:
+ return err;
+}
+
+static int adm1031_detach_client(struct i2c_client *client)
+{
+ int ret;
+ if ((ret = i2c_detach_client(client)) != 0) {
+ return ret;
+ }
+ kfree(client);
+ return 0;
+}
+
+static void adm1031_init_client(struct i2c_client *client)
+{
+ unsigned int read_val;
+ unsigned int mask;
+ struct adm1031_data *data = i2c_get_clientdata(client);
+
+ mask = (ADM1031_CONF2_PWM1_ENABLE | ADM1031_CONF2_TACH1_ENABLE);
+ if (data->chip_type == adm1031) {
+ mask |= (ADM1031_CONF2_PWM2_ENABLE |
+ ADM1031_CONF2_TACH2_ENABLE);
+ }
+ /* Initialize the ADM1031 chip (enables fan speed reading ) */
+ read_val = adm1031_read_value(client, ADM1031_REG_CONF2);
+ if ((read_val | mask) != read_val) {
+ adm1031_write_value(client, ADM1031_REG_CONF2, read_val | mask);
+ }
+
+ read_val = adm1031_read_value(client, ADM1031_REG_CONF1);
+ if ((read_val | ADM1031_CONF1_MONITOR_ENABLE) != read_val) {
+ adm1031_write_value(client, ADM1031_REG_CONF1, read_val |
+ ADM1031_CONF1_MONITOR_ENABLE);
+ }
+
+}
+
+static struct adm1031_data *adm1031_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adm1031_data *data = i2c_get_clientdata(client);
+ int chan;
+
+ down(&data->update_lock);
+
+ if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+ (jiffies < data->last_updated) || !data->valid) {
+
+ dev_dbg(&client->dev, "Starting adm1031 update\n");
+ for (chan = 0;
+ chan < ((data->chip_type == adm1031) ? 3 : 2); chan++) {
+ u8 oldh, newh;
+
+ oldh =
+ adm1031_read_value(client, ADM1031_REG_TEMP(chan));
+ data->ext_temp[chan] =
+ adm1031_read_value(client, ADM1031_REG_EXT_TEMP);
+ newh =
+ adm1031_read_value(client, ADM1031_REG_TEMP(chan));
+ if (newh != oldh) {
+ data->ext_temp[chan] =
+ adm1031_read_value(client,
+ ADM1031_REG_EXT_TEMP);
+#ifdef DEBUG
+ oldh =
+ adm1031_read_value(client,
+ ADM1031_REG_TEMP(chan));
+
+ /* oldh is actually newer */
+ if (newh != oldh)
+ dev_warn(&client->dev,
+ "Remote temperature may be "
+ "wrong.\n");
+#endif
+ }
+ data->temp[chan] = newh;
+
+ data->temp_min[chan] =
+ adm1031_read_value(client,
+ ADM1031_REG_TEMP_MIN(chan));
+ data->temp_max[chan] =
+ adm1031_read_value(client,
+ ADM1031_REG_TEMP_MAX(chan));
+ data->temp_crit[chan] =
+ adm1031_read_value(client,
+ ADM1031_REG_TEMP_CRIT(chan));
+ data->auto_temp[chan] =
+ adm1031_read_value(client,
+ ADM1031_REG_AUTO_TEMP(chan));
+
+ }
+
+ data->conf1 = adm1031_read_value(client, ADM1031_REG_CONF1);
+ data->conf2 = adm1031_read_value(client, ADM1031_REG_CONF2);
+
+ data->alarm = adm1031_read_value(client, ADM1031_REG_STATUS(0))
+ | (adm1031_read_value(client, ADM1031_REG_STATUS(1))
+ << 8);
+ if (data->chip_type == adm1030) {
+ data->alarm &= 0xc0ff;
+ }
+
+ for (chan=0; chan<(data->chip_type == adm1030 ? 1 : 2); chan++) {
+ data->fan_div[chan] =
+ adm1031_read_value(client, ADM1031_REG_FAN_DIV(chan));
+ data->fan_min[chan] =
+ adm1031_read_value(client, ADM1031_REG_FAN_MIN(chan));
+ data->fan[chan] =
+ adm1031_read_value(client, ADM1031_REG_FAN_SPEED(chan));
+ data->pwm[chan] =
+ 0xf & (adm1031_read_value(client, ADM1031_REG_PWM) >>
+ (4*chan));
+ }
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+
+ up(&data->update_lock);
+
+ return data;
+}
+
+static int __init sensors_adm1031_init(void)
+{
+ return i2c_add_driver(&adm1031_driver);
+}
+
+static void __exit sensors_adm1031_exit(void)
+{
+ i2c_del_driver(&adm1031_driver);
+}
+
+MODULE_AUTHOR("Alexandre d'Alton <alex@alexdalton.org>");
+MODULE_DESCRIPTION("ADM1031/ADM1030 driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_adm1031_init);
+module_exit(sensors_adm1031_exit);
--- /dev/null
+/*
+ lm77.c - Part of lm_sensors, Linux kernel modules for hardware
+ monitoring
+
+ Copyright (c) 2004 Andras BALI <drewie@freemail.hu>
+
+ Heavily based on lm75.c by Frodo Looijaard <frodol@dds.nl>. The LM77
+ is a temperature sensor and thermal window comparator with 0.5 deg
+ resolution made by National Semiconductor. Complete datasheet can be
+ obtained at their site:
+ http://www.national.com/pf/LM/LM77.html
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+static unsigned short normal_i2c_range[] = { 0x48, 0x4b, I2C_CLIENT_END };
+static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
+static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(lm77);
+
+/* The LM77 registers */
+#define LM77_REG_TEMP 0x00
+#define LM77_REG_CONF 0x01
+#define LM77_REG_TEMP_HYST 0x02
+#define LM77_REG_TEMP_CRIT 0x03
+#define LM77_REG_TEMP_MIN 0x04
+#define LM77_REG_TEMP_MAX 0x05
+
+/* Each client has this additional data */
+struct lm77_data {
+ struct i2c_client client;
+ struct semaphore update_lock;
+ char valid;
+ unsigned long last_updated; /* In jiffies */
+ int temp_input; /* Temperatures */
+ int temp_crit;
+ int temp_min;
+ int temp_max;
+ int temp_hyst;
+ u8 alarms;
+};
+
+static int lm77_attach_adapter(struct i2c_adapter *adapter);
+static int lm77_detect(struct i2c_adapter *adapter, int address, int kind);
+static void lm77_init_client(struct i2c_client *client);
+static int lm77_detach_client(struct i2c_client *client);
+static u16 lm77_read_value(struct i2c_client *client, u8 reg);
+static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value);
+
+static struct lm77_data *lm77_update_device(struct device *dev);
+
+
+/* This is the driver that will be inserted */
+static struct i2c_driver lm77_driver = {
+ .owner = THIS_MODULE,
+ .name = "lm77",
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = lm77_attach_adapter,
+ .detach_client = lm77_detach_client,
+};
+
+static int lm77_id = 0;
+
+/* straight from the datasheet */
+#define LM77_TEMP_MIN (-55000)
+#define LM77_TEMP_MAX 125000
+
+/* In the temperature registers, the low 3 bits are not part of the
+ temperature values; they are the status bits. */
+static inline u16 LM77_TEMP_TO_REG(int temp)
+{
+ int ntemp = SENSORS_LIMIT(temp, LM77_TEMP_MIN, LM77_TEMP_MAX);
+ return (u16)((ntemp / 500) * 8);
+}
+
+static inline int LM77_TEMP_FROM_REG(u16 reg)
+{
+ return ((int)reg / 8) * 500;
+}
+
+/* sysfs stuff */
+
+/* read routines for temperature limits */
+#define show(value) \
+static ssize_t show_##value(struct device *dev, char *buf) \
+{ \
+ struct lm77_data *data = lm77_update_device(dev); \
+ return sprintf(buf, "%d\n", data->value); \
+}
+
+show(temp_input);
+show(temp_crit);
+show(temp_min);
+show(temp_max);
+show(alarms);
+
+/* read routines for hysteresis values */
+static ssize_t show_temp_crit_hyst(struct device *dev, char *buf)
+{
+ struct lm77_data *data = lm77_update_device(dev);
+ return sprintf(buf, "%d\n", data->temp_crit - data->temp_hyst);
+}
+static ssize_t show_temp_min_hyst(struct device *dev, char *buf)
+{
+ struct lm77_data *data = lm77_update_device(dev);
+ return sprintf(buf, "%d\n", data->temp_min + data->temp_hyst);
+}
+static ssize_t show_temp_max_hyst(struct device *dev, char *buf)
+{
+ struct lm77_data *data = lm77_update_device(dev);
+ return sprintf(buf, "%d\n", data->temp_max - data->temp_hyst);
+}
+
+/* write routines */
+#define set(value, reg) \
+static ssize_t set_##value(struct device *dev, const char *buf, size_t count) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct lm77_data *data = i2c_get_clientdata(client); \
+ data->value = simple_strtoul(buf, NULL, 10); \
+ lm77_write_value(client, reg, LM77_TEMP_TO_REG(data->value)); \
+ return count; \
+}
+
+set(temp_min, LM77_REG_TEMP_MIN);
+set(temp_max, LM77_REG_TEMP_MAX);
+
+/* hysteresis is stored as a relative value on the chip, so it has to be
+ converted first */
+static ssize_t set_temp_crit_hyst(struct device *dev, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm77_data *data = i2c_get_clientdata(client);
+ data->temp_hyst = data->temp_crit - simple_strtoul(buf, NULL, 10);
+ lm77_write_value(client, LM77_REG_TEMP_HYST,
+ LM77_TEMP_TO_REG(data->temp_hyst));
+ return count;
+}
+
+/* preserve hysteresis when setting T_crit */
+static ssize_t set_temp_crit(struct device *dev, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm77_data *data = i2c_get_clientdata(client);
+ int oldcrithyst = data->temp_crit - data->temp_hyst;
+ data->temp_crit = simple_strtoul(buf, NULL, 10);
+ data->temp_hyst = data->temp_crit - oldcrithyst;
+ lm77_write_value(client, LM77_REG_TEMP_CRIT,
+ LM77_TEMP_TO_REG(data->temp_crit));
+ lm77_write_value(client, LM77_REG_TEMP_HYST,
+ LM77_TEMP_TO_REG(data->temp_hyst));
+ return count;
+}
+
+static DEVICE_ATTR(temp1_input, S_IRUGO,
+ show_temp_input, NULL);
+static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
+ show_temp_crit, set_temp_crit);
+static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
+ show_temp_min, set_temp_min);
+static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
+ show_temp_max, set_temp_max);
+
+static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO,
+ show_temp_crit_hyst, set_temp_crit_hyst);
+static DEVICE_ATTR(temp1_min_hyst, S_IRUGO,
+ show_temp_min_hyst, NULL);
+static DEVICE_ATTR(temp1_max_hyst, S_IRUGO,
+ show_temp_max_hyst, NULL);
+
+static DEVICE_ATTR(alarms, S_IRUGO,
+ show_alarms, NULL);
+
+static int lm77_attach_adapter(struct i2c_adapter *adapter)
+{
+ if (!(adapter->class & I2C_CLASS_HWMON))
+ return 0;
+ return i2c_detect(adapter, &addr_data, lm77_detect);
+}
+
+/* This function is called by i2c_detect */
+static int lm77_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+ struct i2c_client *new_client;
+ struct lm77_data *data;
+ int err = 0;
+ const char *name = "";
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA))
+ goto exit;
+
+ /* OK. For now, we presume we have a valid client. We now create the
+ client structure, even though we cannot fill it completely yet.
+ But it allows us to access lm77_{read,write}_value. */
+ if (!(data = kmalloc(sizeof(struct lm77_data), GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ memset(data, 0, sizeof(struct lm77_data));
+
+ new_client = &data->client;
+ i2c_set_clientdata(new_client, data);
+ new_client->addr = address;
+ new_client->adapter = adapter;
+ new_client->driver = &lm77_driver;
+ new_client->flags = 0;
+
+ /* Here comes the remaining detection. Since the LM77 has no
+ register dedicated to identification, we have to rely on the
+ following tricks:
+
+ 1. the high 4 bits represent the sign and thus they should
+ always be the same
+ 2. the high 3 bits are unused in the configuration register
+ 3. addresses 0x06 and 0x07 return the last read value
+ 4. registers cycling over 8-address boundaries
+
+ Word-sized registers are high-byte first. */
+ if (kind < 0) {
+ int i, cur, conf, hyst, crit, min, max;
+
+ /* addresses cycling */
+ cur = i2c_smbus_read_word_data(new_client, 0);
+ conf = i2c_smbus_read_byte_data(new_client, 1);
+ hyst = i2c_smbus_read_word_data(new_client, 2);
+ crit = i2c_smbus_read_word_data(new_client, 3);
+ min = i2c_smbus_read_word_data(new_client, 4);
+ max = i2c_smbus_read_word_data(new_client, 5);
+ for (i = 8; i <= 0xff; i += 8)
+ if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
+ || i2c_smbus_read_word_data(new_client, i + 2) != hyst
+ || i2c_smbus_read_word_data(new_client, i + 3) != crit
+ || i2c_smbus_read_word_data(new_client, i + 4) != min
+ || i2c_smbus_read_word_data(new_client, i + 5) != max)
+ goto exit_free;
+
+ /* sign bits */
+ if (((cur & 0x00f0) != 0xf0 && (cur & 0x00f0) != 0x0)
+ || ((hyst & 0x00f0) != 0xf0 && (hyst & 0x00f0) != 0x0)
+ || ((crit & 0x00f0) != 0xf0 && (crit & 0x00f0) != 0x0)
+ || ((min & 0x00f0) != 0xf0 && (min & 0x00f0) != 0x0)
+ || ((max & 0x00f0) != 0xf0 && (max & 0x00f0) != 0x0))
+ goto exit_free;
+
+ /* unused bits */
+ if (conf & 0xe0)
+ goto exit_free;
+
+ /* 0x06 and 0x07 return the last read value */
+ cur = i2c_smbus_read_word_data(new_client, 0);
+ if (i2c_smbus_read_word_data(new_client, 6) != cur
+ || i2c_smbus_read_word_data(new_client, 7) != cur)
+ goto exit_free;
+ hyst = i2c_smbus_read_word_data(new_client, 2);
+ if (i2c_smbus_read_word_data(new_client, 6) != hyst
+ || i2c_smbus_read_word_data(new_client, 7) != hyst)
+ goto exit_free;
+ min = i2c_smbus_read_word_data(new_client, 4);
+ if (i2c_smbus_read_word_data(new_client, 6) != min
+ || i2c_smbus_read_word_data(new_client, 7) != min)
+ goto exit_free;
+
+ }
+
+ /* Determine the chip type - only one kind supported! */
+ if (kind <= 0)
+ kind = lm77;
+
+ if (kind == lm77) {
+ name = "lm77";
+ }
+
+ /* Fill in the remaining client fields and put it into the global list */
+ strlcpy(new_client->name, name, I2C_NAME_SIZE);
+
+ new_client->id = lm77_id++;
+ data->valid = 0;
+ init_MUTEX(&data->update_lock);
+
+ /* Tell the I2C layer a new client has arrived */
+ if ((err = i2c_attach_client(new_client)))
+ goto exit_free;
+
+ /* Initialize the LM77 chip */
+ lm77_init_client(new_client);
+
+ /* Register sysfs hooks */
+ device_create_file(&new_client->dev, &dev_attr_temp1_input);
+ device_create_file(&new_client->dev, &dev_attr_temp1_crit);
+ device_create_file(&new_client->dev, &dev_attr_temp1_min);
+ device_create_file(&new_client->dev, &dev_attr_temp1_max);
+ device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst);
+ device_create_file(&new_client->dev, &dev_attr_temp1_min_hyst);
+ device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
+ device_create_file(&new_client->dev, &dev_attr_alarms);
+ return 0;
+
+exit_free:
+ kfree(data);
+exit:
+ return err;
+}
+
+static int lm77_detach_client(struct i2c_client *client)
+{
+ i2c_detach_client(client);
+ kfree(i2c_get_clientdata(client));
+ return 0;
+}
+
+/* All registers are word-sized, except for the configuration register.
+ The LM77 uses the high-byte first convention. */
+static u16 lm77_read_value(struct i2c_client *client, u8 reg)
+{
+ if (reg == LM77_REG_CONF)
+ return i2c_smbus_read_byte_data(client, reg);
+ else
+ return swab16(i2c_smbus_read_word_data(client, reg));
+}
+
+static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+ if (reg == LM77_REG_CONF)
+ return i2c_smbus_write_byte_data(client, reg, value);
+ else
+ return i2c_smbus_write_word_data(client, reg, swab16(value));
+}
+
+static void lm77_init_client(struct i2c_client *client)
+{
+ /* Initialize the LM77 chip - turn off shutdown mode */
+ int conf = lm77_read_value(client, LM77_REG_CONF);
+ if (conf & 1)
+ lm77_write_value(client, LM77_REG_CONF, conf & 0xfe);
+}
+
+static struct lm77_data *lm77_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm77_data *data = i2c_get_clientdata(client);
+
+ down(&data->update_lock);
+
+ if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+ (jiffies < data->last_updated) || !data->valid) {
+ dev_dbg(&client->dev, "Starting lm77 update\n");
+ data->temp_input =
+ LM77_TEMP_FROM_REG(lm77_read_value(client,
+ LM77_REG_TEMP));
+ data->temp_hyst =
+ LM77_TEMP_FROM_REG(lm77_read_value(client,
+ LM77_REG_TEMP_HYST));
+ data->temp_crit =
+ LM77_TEMP_FROM_REG(lm77_read_value(client,
+ LM77_REG_TEMP_CRIT));
+ data->temp_min =
+ LM77_TEMP_FROM_REG(lm77_read_value(client,
+ LM77_REG_TEMP_MIN));
+ data->temp_max =
+ LM77_TEMP_FROM_REG(lm77_read_value(client,
+ LM77_REG_TEMP_MAX));
+ data->alarms =
+ lm77_read_value(client, LM77_REG_TEMP) & 0x0007;
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+
+ up(&data->update_lock);
+
+ return data;
+}
+
+static int __init sensors_lm77_init(void)
+{
+ return i2c_add_driver(&lm77_driver);
+}
+
+static void __exit sensors_lm77_exit(void)
+{
+ i2c_del_driver(&lm77_driver);
+}
+
+MODULE_AUTHOR("Andras BALI <drewie@freemail.hu>");
+MODULE_DESCRIPTION("LM77 driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_lm77_init);
+module_exit(sensors_lm77_exit);
ide_drive_t *drive = bdev->bd_disk->private_data;
idefloppy_floppy_t *floppy = drive->driver_data;
void __user *argp = (void __user *)arg;
- int err = generic_ide_ioctl(bdev, cmd, arg);
+ int err = generic_ide_ioctl(file, bdev, cmd, arg);
int prevent = (arg) ? 1 : 0;
idefloppy_pc_t pc;
if (err != -EINVAL)
if (!pmif->mediabay) {
ppc_md.feature_call(PMAC_FTR_IDE_RESET, pmif->node, pmif->aapl_bus_id, 1);
ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, pmif->node, pmif->aapl_bus_id, 1);
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(HZ/100);
+ msleep(10);
ppc_md.feature_call(PMAC_FTR_IDE_RESET, pmif->node, pmif->aapl_bus_id, 0);
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(IDE_WAKEUP_DELAY);
+ msleep(jiffies_to_msecs(IDE_WAKEUP_DELAY));
}
/* Sanitize drive timings */
/* This is necessary to enable IDE when net-booting */
ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmif->aapl_bus_id, 1);
ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, pmif->aapl_bus_id, 1);
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(HZ/100);
+ msleep(10);
ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmif->aapl_bus_id, 0);
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(IDE_WAKEUP_DELAY);
+ msleep(jiffies_to_msecs(IDE_WAKEUP_DELAY));
}
/* Setup MMIO ops */
config IEEE1394
tristate "IEEE 1394 (FireWire) support"
+ depends on PCI || BROKEN
help
IEEE 1394 describes a high performance serial bus, which is also
known as FireWire(tm) or i.Link(tm) and is used for connecting all
config IEEE1394_SBP2
tristate "SBP-2 support (Harddisks etc.)"
- depends on IEEE1394 && SCSI
+ depends on IEEE1394 && SCSI && (PCI || BROKEN)
help
This option enables you to use SBP-2 devices connected to your IEEE
1394 bus. SBP-2 devices include harddrives and DVD devices.
struct video1394_queue_variable {
unsigned int channel;
unsigned int buffer;
- unsigned int* packet_sizes; /* Buffer of size:
+ unsigned int __user * packet_sizes; /* Buffer of size:
buf_size / packet_size */
};
char name[64];
char phys[32];
char type;
- volatile char reset;
- volatile char layout;
+ volatile s8 reset;
+ volatile s8 layout;
};
/*
if (pc110pad_used++)
return 0;
- pc110pad_interrupt(0,0,0);
- pc110pad_interrupt(0,0,0);
- pc110pad_interrupt(0,0,0);
+ pc110pad_interrupt(0,NULL,NULL);
+ pc110pad_interrupt(0,NULL,NULL);
+ pc110pad_interrupt(0,NULL,NULL);
outb(PC110PAD_ON, pc110pad_io + 2);
pc110pad_count = 0;
outb(PC110PAD_OFF, pc110pad_io + 2);
- if (request_irq(pc110pad_irq, pc110pad_interrupt, 0, "pc110pad", 0))
+ if (request_irq(pc110pad_irq, pc110pad_interrupt, 0, "pc110pad", NULL))
{
release_region(pc110pad_io, 4);
printk(KERN_ERR "pc110pad: Unable to get irq %d.\n", pc110pad_irq);
outb(PC110PAD_OFF, pc110pad_io + 2);
- free_irq(pc110pad_irq, 0);
+ free_irq(pc110pad_irq, NULL);
release_region(pc110pad_io, 4);
}
if ((len = strlen(inf->info_start)) <= count) {
if (copy_to_user(buf, inf->info_start, len))
return -EFAULT;
- file->f_pos += len;
+ *off += len;
return (len);
}
return (0);
(struct divert_info **) filep->private_data = &divert_info_head;
spin_unlock_irqrestore( &divert_info_lock, flags );
/* start_divert(); */
- return (0);
+ return nonseekable_open(ino, filep);
} /* isdn_divert_open */
/*******************/
card = kmalloc(sizeof(*card), GFP_KERNEL);
if (!card)
- return 0;
+ return NULL;
memset(card, 0, sizeof(*card));
cinfo = kmalloc(sizeof(*cinfo) * nr_controllers, GFP_KERNEL);
if (!cinfo) {
kfree(card);
- return 0;
+ return NULL;
}
memset(cinfo, 0, sizeof(*cinfo) * nr_controllers);
err_kfree:
kfree(p);
err:
- return 0;
+ return NULL;
}
void avmcard_dma_free(avmcard_dmainfo *p)
-/* $Id: platform.h,v 1.37 2004/03/20 17:44:29 armin Exp $
+/* $Id: platform.h,v 1.37.4.1 2004/07/28 14:47:21 armin Exp $
*
* platform.h
*
*/
static __inline__ void diva_os_sleep(dword mSec)
{
- unsigned long timeout = HZ * mSec / 1000 + 1;
-
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(timeout);
+ msleep(mSec);
}
static __inline__ void diva_os_wait(dword mSec)
{
return(0);
}
+#ifdef CONFIG_PCI
static struct pci_dev *dev_avm __initdata = NULL;
+#endif
#ifdef __ISAPNP__
static struct pnp_card *pnp_avm_c __initdata = NULL;
#endif
printk(KERN_INFO "FritzPnP: no ISA PnP present\n");
}
#endif
-#if CONFIG_PCI
+#ifdef CONFIG_PCI
if ((dev_avm = pci_find_device(PCI_VENDOR_ID_AVM,
PCI_DEVICE_ID_AVM_A1, dev_avm))) {
cs->irq = dev_avm->irq;
* of the GNU General Public License, incorporated herein by reference.
*
* For changes and modifications please read
- * ../../../Documentation/isdn/HiSax.cert
+ * Documentation/isdn/HiSax.cert
*
* based on the teles driver from Jan den Ouden
*
* of the GNU General Public License, incorporated herein by reference.
*
* For changes and modifications please read
- * ../../../Documentation/isdn/HiSax.cert
+ * Documentation/isdn/HiSax.cert
*
* Thanks to Elsa GmbH for documents and information
*
byteout(cs->hw.hfcD.addr | 1, reg);
}
ret = bytein(cs->hw.hfcD.addr);
-#if HFC_REG_DEBUG
+#ifdef HFC_REG_DEBUG
if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2))
debugl1(cs, "t3c RD %02x %02x", reg, ret);
#endif
}
if (data)
byteout(cs->hw.hfcD.addr, value);
-#if HFC_REG_DEBUG
+#ifdef HFC_REG_DEBUG
if (cs->debug & L1_DEB_HSCX_FIFO && (data != HFCD_DATA_NODEB))
debugl1(cs, "t3c W%c %02x %02x", data ? 'D' : 'C', reg, value);
#endif
* of the GNU General Public License, incorporated herein by reference.
*
* For changes and modifications please read
- * ../../../Documentation/isdn/HiSax.cert
+ * Documentation/isdn/HiSax.cert
*
*/
* of the GNU General Public License, incorporated herein by reference.
*
* For changes and modifications please read
- * ../../../Documentation/isdn/HiSax.cert
+ * Documentation/isdn/HiSax.cert
*
*/
* of the GNU General Public License, incorporated herein by reference.
*
* For changes and modifications please read
- * ../../../Documentation/isdn/HiSax.cert
+ * Documentation/isdn/HiSax.cert
*
* Thanks to Jan den Ouden
* Fritz Elfert
* of the GNU General Public License, incorporated herein by reference.
*
* For changes and modifications please read
- * ../../../Documentation/isdn/HiSax.cert
+ * Documentation/isdn/HiSax.cert
*
* Thanks to Jan den Ouden
* Fritz Elfert
* of the GNU General Public License, incorporated herein by reference.
*
* For changes and modifications please read
- * ../../../Documentation/isdn/HiSax.cert
+ * Documentation/isdn/HiSax.cert
*
* Thanks to Jan den Ouden
* Fritz Elfert
* of the GNU General Public License, incorporated herein by reference.
*
* For changes and modifications please read
- * ../../../Documentation/isdn/HiSax.cert
+ * Documentation/isdn/HiSax.cert
*
*/
* of the GNU General Public License, incorporated herein by reference.
*
* For changes and modifications please read
- * ../../../Documentation/isdn/HiSax.cert
+ * Documentation/isdn/HiSax.cert
*
* Thanks to Jan den Ouden
* Fritz Elfert
#
config HYSDN
tristate "Hypercope HYSDN cards (Champ, Ergo, Metro) support (module only)"
- depends on m && PROC_FS && BROKEN_ON_SMP
+ depends on m && PROC_FS && PCI && BROKEN_ON_SMP
help
Say Y here if you have one of Hypercope's active PCI ISDN cards
Champ, Ergo and Metro. You will then get a module called hysdn.
}
}
detach_capi_ctr(ctrl);
- ctrl->driverdata = 0;
+ ctrl->driverdata = NULL;
kfree(card->hyctrlinfo);
***********************************************************/
-int hycapi_init()
+int hycapi_init(void)
{
int i;
for(i=0;i<CAPI_MAXAPPL;i++) {
/* write conf file -> boot or send cfg line to card */
/****************************************************/
static ssize_t
-hysdn_conf_write(struct file *file, const char *buf, size_t count, loff_t * off)
+hysdn_conf_write(struct file *file, const char __user *buf, size_t count, loff_t * off)
{
struct conf_writedata *cnf;
int i;
uchar ch, *cp;
- if (&file->f_pos != off) /* fs error check */
- return (-ESPIPE);
if (!count)
return (0); /* nothing to handle */
/* read conf file -> output card info data */
/*******************************************/
static ssize_t
-hysdn_conf_read(struct file *file, char *buf, size_t count, loff_t * off)
+hysdn_conf_read(struct file *file, char __user *buf, size_t count, loff_t * off)
{
char *cp;
int i;
- if (off != &file->f_pos) /* fs error check */
- return -ESPIPE;
-
if (file->f_mode & FMODE_READ) {
if (!(cp = file->private_data))
return (-EFAULT); /* should never happen */
return (-EPERM); /* no permission this time */
}
unlock_kernel();
- return (0);
+ return nonseekable_open(ino, filep);
} /* hysdn_conf_open */
/***************************/
/* write log file -> set log level bits */
/****************************************/
static ssize_t
-hysdn_log_write(struct file *file, const char *buf, size_t count, loff_t * off)
+hysdn_log_write(struct file *file, const char __user *buf, size_t count, loff_t * off)
{
ulong u = 0;
int found = 0;
long base = 10;
hysdn_card *card = (hysdn_card *) file->private_data;
- if (&file->f_pos != off) /* fs error check */
- return (-ESPIPE);
-
if (count > (sizeof(valbuf) - 1))
count = sizeof(valbuf) - 1; /* limit length */
if (copy_from_user(valbuf, buf, count))
/* read log file */
/******************/
static ssize_t
-hysdn_log_read(struct file *file, char *buf, size_t count, loff_t * off)
+hysdn_log_read(struct file *file, char __user *buf, size_t count, loff_t * off)
{
struct log_data *inf;
int len;
if ((len = strlen(inf->log_start)) <= count) {
if (copy_to_user(buf, inf->log_start, len))
return -EFAULT;
- file->f_pos += len;
+ *off += len;
return (len);
}
return (0);
return (-EPERM); /* no permission this time */
}
unlock_kernel();
- return (0);
+ return nonseekable_open(ino, filep);
} /* hysdn_log_open */
/*******************************************************************************/
#
config ISDN_DRV_PCBIT
tristate "PCBIT-D support"
- depends on ISDN_I4L && ISA
+ depends on ISDN_I4L && ISA && (BROKEN || !PPC)
help
This enables support for the PCBIT ISDN-card. This card is
manufactured in Portugal by Octal. For running this card,
#define FLAG_POWER_FROM_FN 0x00000002
#define FLAG_EMU_FWDEL_DOWN 0x00000004
-static struct adbhid *adbhid[16] = { 0 };
+static struct adbhid *adbhid[16];
static void adbhid_probe(void);
if (adbhid[id]->keycode)
kfree(adbhid[id]->keycode);
kfree(adbhid[id]);
- adbhid[id] = 0;
+ adbhid[id] = NULL;
}
}
static ssize_t __pmac
-anslcd_write( struct file * file, const char * buf,
+anslcd_write( struct file * file, const char __user * buf,
size_t count, loff_t *ppos )
{
- const char * p = buf;
+ const char __user *p = buf;
int i;
#ifdef DEBUG
anslcd_ioctl( struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg )
{
- char ch, *temp;
+ char ch, __user *temp;
#ifdef DEBUG
printk(KERN_DEBUG "LCD: ioctl(%d,%d)\n",cmd,arg);
anslcd_write_byte_ctrl ( 0x02 );
return 0;
case ANSLCD_SENDCTRL:
- temp = (char *) arg;
+ temp = (char __user *) arg;
__get_user(ch, temp);
for (; ch; temp++) { /* FIXME: This is ugly, but should work, as a \0 byte is not a valid command code */
anslcd_write_byte_ctrl ( ch );
"* Welcome to *" /* Line #2 */
"********************"; /* Line #4 */
-int __init
+static int __init
anslcd_init(void)
{
int a;
return 0;
}
-__initcall(anslcd_init);
+static void __exit
+anslcd_exit(void)
+{
+ misc_deregister(&anslcd_dev);
+ iounmap(anslcd_ptr);
+}
+module_init(anslcd_init);
+module_exit(anslcd_exit);
req->data[i] = req->data[i+1];
--req->nbytes;
- req->next = 0;
+ req->next = NULL;
req->sent = 0;
req->complete = 0;
req->reply_len = 0;
local_irq_save(flags);
if (in_8(&adb->intr.r) != 0)
- macio_adb_interrupt(0, 0, 0);
+ macio_adb_interrupt(0, NULL, NULL);
local_irq_restore(flags);
}
static void rxdma_start(struct mac_serial * info, int current);
static void rxdma_to_tty(struct mac_serial * info);
-#ifndef MIN
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-#endif
-
/*
* tmp_buf is used as a temporary buffer by serial_write. We need to
* lock it in case the copy_from_user blocks while swapping in a page,
if (from_user) {
down(&tmp_buf_sem);
while (1) {
- c = MIN(count,
- MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
- SERIAL_XMIT_SIZE - info->xmit_head));
+ c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - info->xmit_head));
if (c <= 0)
break;
break;
}
spin_lock_irqsave(&info->lock, flags);
- c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
- SERIAL_XMIT_SIZE - info->xmit_head));
+ c = min_t(int, c, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - info->xmit_head));
memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
info->xmit_head = ((info->xmit_head + c) &
(SERIAL_XMIT_SIZE-1));
} else {
while (1) {
spin_lock_irqsave(&info->lock, flags);
- c = MIN(count,
- MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
- SERIAL_XMIT_SIZE - info->xmit_head));
+ c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - info->xmit_head));
if (c <= 0) {
spin_unlock_irqrestore(&info->lock, flags);
break;
} else if (char_time == 0)
char_time = 1;
if (timeout)
- char_time = MIN(char_time, timeout);
+ char_time = min_t(unsigned long, char_time, timeout);
while ((read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) {
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(char_time);
#endif /* CONFIG_BLK_DEV_IDE */
return -ENODEV;
}
+EXPORT_SYMBOL(check_media_bay);
int __pmac check_media_bay_by_base(unsigned long base, int what)
{
/* Force an immediate detect */
set_mb_power(bay, 0);
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(MS_TO_HZ(MB_POWER_DELAY));
+ msleep(MB_POWER_DELAY);
bay->content_id = MB_NO;
bay->last_value = bay->ops->content(bay);
bay->value_count = MS_TO_HZ(MB_STABLE_DELAY);
bay->state = mb_empty;
do {
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(MS_TO_HZ(MB_POLL_DELAY));
+ msleep(MB_POLL_DELAY);
media_bay_step(i);
} while((bay->state != mb_empty) &&
(bay->state != mb_up));
bay->sleeping = 1;
set_mb_power(bay, 0);
up(&bay->lock);
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(MS_TO_HZ(MB_POLL_DELAY));
+ msleep(MB_POLL_DELAY);
mdev->ofdev.dev.power_state = state;
}
return 0;
/* Force MB power to 0 */
down(&bay->lock);
set_mb_power(bay, 0);
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(MS_TO_HZ(MB_POWER_DELAY));
+ msleep(MB_POWER_DELAY);
if (bay->ops->content(bay) != bay->content_id) {
printk("mediabay%d: content changed during sleep...\n", bay->index);
up(&bay->lock);
bay->cd_retry = 0;
#endif
do {
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(MS_TO_HZ(MB_POLL_DELAY));
+ msleep(MB_POLL_DELAY);
media_bay_step(bay->index);
} while((bay->state != mb_empty) &&
(bay->state != mb_up));
req->complete = 1;
return -EINVAL;
}
- req->next = 0;
+ req->next = NULL;
req->sent = 0;
req->complete = 0;
req->reply_len = 0;
* disable_irq(), would that work on m68k ? --BenH
*/
local_irq_save(flags);
- cuda_interrupt(0, 0, 0);
+ cuda_interrupt(0, NULL, NULL);
local_irq_restore(flags);
}
}
#endif /* CONFIG_PMAC_PBOOK */
/* Create /proc/pmu */
- proc_pmu_root = proc_mkdir("pmu", 0);
+ proc_pmu_root = proc_mkdir("pmu", NULL);
if (proc_pmu_root) {
int i;
proc_pmu_info = create_proc_read_entry("info", 0, proc_pmu_root,
}
if (pmu_state == idle)
adb_int_pending = 1;
- via_pmu_interrupt(0, 0, 0);
+ via_pmu_interrupt(0, NULL, NULL);
udelay(10);
}
return -EINVAL;
}
- req->next = 0;
+ req->next = NULL;
req->sent = 0;
req->complete = 0;
return;
if (disable_poll)
return;
- via_pmu_interrupt(0, 0, 0);
+ via_pmu_interrupt(0, NULL, NULL);
}
void __openfirmware
/* Kicks ADB read when PMU is suspended */
adb_int_pending = 1;
do {
- via_pmu_interrupt(0, 0, 0);
+ via_pmu_interrupt(0, NULL, NULL);
} while (pmu_suspended && (adb_int_pending || pmu_state != idle
|| req_awaiting_reply));
}
if (!via)
return;
while((pmu_state != idle && pmu_state != locked) || !req->complete)
- via_pmu_interrupt(0, 0, 0);
+ via_pmu_interrupt(0, NULL, NULL);
}
/* This function loops until the PMU is idle and prevents it from
spin_unlock_irqrestore(&pmu_lock, flags);
if (req_awaiting_reply)
adb_int_pending = 1;
- via_pmu_interrupt(0, 0, 0);
+ via_pmu_interrupt(0, NULL, NULL);
spin_lock_irqsave(&pmu_lock, flags);
if (!adb_int_pending && pmu_state == idle && !req_awaiting_reply) {
#ifdef SUSPEND_USES_PMU
printk(KERN_ERR "PMU: extra ADB reply\n");
return;
}
- req_awaiting_reply = 0;
+ req_awaiting_reply = NULL;
if (len <= 2)
req->reply_len = 0;
else {
pmu_irq_stats[1]++;
adb_int_pending = 1;
spin_unlock_irqrestore(&pmu_lock, flags);
- via_pmu_interrupt(0, 0, 0);
+ via_pmu_interrupt(0, NULL, NULL);
return IRQ_HANDLED;
}
return IRQ_NONE;
if (n->list.next == 0)
return -ENOENT;
list_del(&n->list);
- n->list.next = 0;
+ n->list.next = NULL;
return 0;
}
/* Force a poll of ADB interrupts */
adb_int_pending = 1;
- via_pmu_interrupt(0, 0, 0);
+ via_pmu_interrupt(0, NULL, NULL);
/* Restart jiffies & scheduling */
wakeup_decrementer();
lock_kernel();
if (pp != 0) {
- file->private_data = 0;
+ file->private_data = NULL;
spin_lock_irqsave(&all_pvt_lock, flags);
list_del(&pp->list);
spin_unlock_irqrestore(&all_pvt_lock, flags);
u_int cmd, u_long arg)
{
struct pmu_private *pp = filp->private_data;
+ __u32 __user *argp = (__u32 __user *)arg;
int error;
switch (cmd) {
sleep_in_progress = 0;
return error;
case PMU_IOC_CAN_SLEEP:
- return put_user((u32)can_sleep, (__u32 *)arg);
+ return put_user((u32)can_sleep, argp);
#ifdef CONFIG_PMAC_BACKLIGHT
/* Backlight should have its own device or go via
error = get_backlight_level();
if (error < 0)
return error;
- return put_user(error, (__u32 *)arg);
+ return put_user(error, argp);
case PMU_IOC_SET_BACKLIGHT:
{
__u32 value;
if (sleep_in_progress)
return -EBUSY;
- error = get_user(value, (__u32 *)arg);
+ error = get_user(value, argp);
if (!error)
error = set_backlight_level(value);
return error;
#endif /* CONFIG_INPUT_ADBHID */
#endif /* CONFIG_PMAC_BACKLIGHT */
case PMU_IOC_GET_MODEL:
- return put_user(pmu_kind, (__u32 *)arg);
+ return put_user(pmu_kind, argp);
case PMU_IOC_HAS_ADB:
- return put_user(pmu_has_adb, (__u32 *)arg);
+ return put_user(pmu_has_adb, argp);
}
return -EINVAL;
}
/*f8*/ {-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
};
-int pmu_probe()
+int pmu_probe(void)
{
if (macintosh_config->adb_type == MAC_ADB_PB1) {
pmu_kind = PMU_68K_V1;
}
static void
-recv_byte()
+recv_byte(void)
{
char c;
}
static void
-pmu_start()
+pmu_start(void)
{
unsigned long flags;
struct adb_request *req;
}
void
-pmu_poll()
+pmu_poll(void)
{
unsigned long flags;
--- /dev/null
+/*
+ * dm-snapshot.c
+ *
+ * Copyright (C) 2001-2002 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm.h"
+#include "dm-snap.h"
+#include "dm-io.h"
+#include "kcopyd.h"
+
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+
+/*-----------------------------------------------------------------
+ * Persistent snapshots, by persistent we mean that the snapshot
+ * will survive a reboot.
+ *---------------------------------------------------------------*/
+
+/*
+ * We need to store a record of which parts of the origin have
+ * been copied to the snapshot device. The snapshot code
+ * requires that we copy exception chunks to chunk aligned areas
+ * of the COW store. It makes sense therefore, to store the
+ * metadata in chunk size blocks.
+ *
+ * There is no backward or forward compatibility implemented,
+ * snapshots with different disk versions than the kernel will
+ * not be usable. It is expected that "lvcreate" will blank out
+ * the start of a fresh COW device before calling the snapshot
+ * constructor.
+ *
+ * The first chunk of the COW device just contains the header.
+ * After this there is a chunk filled with exception metadata,
+ * followed by as many exception chunks as can fit in the
+ * metadata areas.
+ *
+ * All on disk structures are in little-endian format. The end
+ * of the exceptions info is indicated by an exception with a
+ * new_chunk of 0, which is invalid since it would point to the
+ * header chunk.
+ */
+
+/*
+ * Magic for persistent snapshots: "SnAp" - Feeble isn't it.
+ */
+#define SNAP_MAGIC 0x70416e53
+
+/*
+ * The on-disk version of the metadata.
+ */
+#define SNAPSHOT_DISK_VERSION 1
+
+struct disk_header {
+ uint32_t magic;
+
+ /*
+ * Is this snapshot valid. There is no way of recovering
+ * an invalid snapshot.
+ */
+ uint32_t valid;
+
+ /*
+ * Simple, incrementing version. no backward
+ * compatibility.
+ */
+ uint32_t version;
+
+ /* In sectors */
+ uint32_t chunk_size;
+};
+
+struct disk_exception {
+ uint64_t old_chunk;
+ uint64_t new_chunk;
+};
+
+struct commit_callback {
+ void (*callback)(void *, int success);
+ void *context;
+};
+
+/*
+ * The top level structure for a persistent exception store.
+ */
+struct pstore {
+ struct dm_snapshot *snap; /* up pointer to my snapshot */
+ int version;
+ int valid;
+ uint32_t chunk_size;
+ uint32_t exceptions_per_area;
+
+ /*
+ * Now that we have an asynchronous kcopyd there is no
+ * need for large chunk sizes, so it wont hurt to have a
+ * whole chunks worth of metadata in memory at once.
+ */
+ void *area;
+
+ /*
+ * Used to keep track of which metadata area the data in
+ * 'chunk' refers to.
+ */
+ uint32_t current_area;
+
+ /*
+ * The next free chunk for an exception.
+ */
+ uint32_t next_free;
+
+ /*
+ * The index of next free exception in the current
+ * metadata area.
+ */
+ uint32_t current_committed;
+
+ atomic_t pending_count;
+ uint32_t callback_count;
+ struct commit_callback *callbacks;
+};
+
+static inline unsigned int sectors_to_pages(unsigned int sectors)
+{
+ return sectors / (PAGE_SIZE >> 9);
+}
+
+static int alloc_area(struct pstore *ps)
+{
+ int r = -ENOMEM;
+ size_t len;
+
+ len = ps->chunk_size << SECTOR_SHIFT;
+
+ /*
+ * Allocate the chunk_size block of memory that will hold
+ * a single metadata area.
+ */
+ ps->area = vmalloc(len);
+ if (!ps->area)
+ return r;
+
+ return 0;
+}
+
+static void free_area(struct pstore *ps)
+{
+ vfree(ps->area);
+}
+
+/*
+ * Read or write a chunk aligned and sized block of data from a device.
+ */
+static int chunk_io(struct pstore *ps, uint32_t chunk, int rw)
+{
+ struct io_region where;
+ unsigned long bits;
+
+ where.bdev = ps->snap->cow->bdev;
+ where.sector = ps->chunk_size * chunk;
+ where.count = ps->chunk_size;
+
+ return dm_io_sync_vm(1, &where, rw, ps->area, &bits);
+}
+
+/*
+ * Read or write a metadata area. Remembering to skip the first
+ * chunk which holds the header.
+ */
+static int area_io(struct pstore *ps, uint32_t area, int rw)
+{
+ int r;
+ uint32_t chunk;
+
+ /* convert a metadata area index to a chunk index */
+ chunk = 1 + ((ps->exceptions_per_area + 1) * area);
+
+ r = chunk_io(ps, chunk, rw);
+ if (r)
+ return r;
+
+ ps->current_area = area;
+ return 0;
+}
+
+static int zero_area(struct pstore *ps, uint32_t area)
+{
+ memset(ps->area, 0, ps->chunk_size << SECTOR_SHIFT);
+ return area_io(ps, area, WRITE);
+}
+
+static int read_header(struct pstore *ps, int *new_snapshot)
+{
+ int r;
+ struct disk_header *dh;
+
+ r = chunk_io(ps, 0, READ);
+ if (r)
+ return r;
+
+ dh = (struct disk_header *) ps->area;
+
+ if (le32_to_cpu(dh->magic) == 0) {
+ *new_snapshot = 1;
+
+ } else if (le32_to_cpu(dh->magic) == SNAP_MAGIC) {
+ *new_snapshot = 0;
+ ps->valid = le32_to_cpu(dh->valid);
+ ps->version = le32_to_cpu(dh->version);
+ ps->chunk_size = le32_to_cpu(dh->chunk_size);
+
+ } else {
+ DMWARN("Invalid/corrupt snapshot");
+ r = -ENXIO;
+ }
+
+ return r;
+}
+
+static int write_header(struct pstore *ps)
+{
+ struct disk_header *dh;
+
+ memset(ps->area, 0, ps->chunk_size << SECTOR_SHIFT);
+
+ dh = (struct disk_header *) ps->area;
+ dh->magic = cpu_to_le32(SNAP_MAGIC);
+ dh->valid = cpu_to_le32(ps->valid);
+ dh->version = cpu_to_le32(ps->version);
+ dh->chunk_size = cpu_to_le32(ps->chunk_size);
+
+ return chunk_io(ps, 0, WRITE);
+}
+
+/*
+ * Access functions for the disk exceptions, these do the endian conversions.
+ */
+static struct disk_exception *get_exception(struct pstore *ps, uint32_t index)
+{
+ if (index >= ps->exceptions_per_area)
+ return NULL;
+
+ return ((struct disk_exception *) ps->area) + index;
+}
+
+static int read_exception(struct pstore *ps,
+ uint32_t index, struct disk_exception *result)
+{
+ struct disk_exception *e;
+
+ e = get_exception(ps, index);
+ if (!e)
+ return -EINVAL;
+
+ /* copy it */
+ result->old_chunk = le64_to_cpu(e->old_chunk);
+ result->new_chunk = le64_to_cpu(e->new_chunk);
+
+ return 0;
+}
+
+static int write_exception(struct pstore *ps,
+ uint32_t index, struct disk_exception *de)
+{
+ struct disk_exception *e;
+
+ e = get_exception(ps, index);
+ if (!e)
+ return -EINVAL;
+
+ /* copy it */
+ e->old_chunk = cpu_to_le64(de->old_chunk);
+ e->new_chunk = cpu_to_le64(de->new_chunk);
+
+ return 0;
+}
+
+/*
+ * Registers the exceptions that are present in the current area.
+ * 'full' is filled in to indicate if the area has been
+ * filled.
+ */
+static int insert_exceptions(struct pstore *ps, int *full)
+{
+ int r;
+ unsigned int i;
+ struct disk_exception de;
+
+ /* presume the area is full */
+ *full = 1;
+
+ for (i = 0; i < ps->exceptions_per_area; i++) {
+ r = read_exception(ps, i, &de);
+
+ if (r)
+ return r;
+
+ /*
+ * If the new_chunk is pointing at the start of
+ * the COW device, where the first metadata area
+ * is we know that we've hit the end of the
+ * exceptions. Therefore the area is not full.
+ */
+ if (de.new_chunk == 0LL) {
+ ps->current_committed = i;
+ *full = 0;
+ break;
+ }
+
+ /*
+ * Keep track of the start of the free chunks.
+ */
+ if (ps->next_free <= de.new_chunk)
+ ps->next_free = de.new_chunk + 1;
+
+ /*
+ * Otherwise we add the exception to the snapshot.
+ */
+ r = dm_add_exception(ps->snap, de.old_chunk, de.new_chunk);
+ if (r)
+ return r;
+ }
+
+ return 0;
+}
+
+static int read_exceptions(struct pstore *ps)
+{
+ uint32_t area;
+ int r, full = 1;
+
+ /*
+ * Keeping reading chunks and inserting exceptions until
+ * we find a partially full area.
+ */
+ for (area = 0; full; area++) {
+ r = area_io(ps, area, READ);
+ if (r)
+ return r;
+
+ r = insert_exceptions(ps, &full);
+ if (r)
+ return r;
+ }
+
+ return 0;
+}
+
+static inline struct pstore *get_info(struct exception_store *store)
+{
+ return (struct pstore *) store->context;
+}
+
+static void persistent_fraction_full(struct exception_store *store,
+ sector_t *numerator, sector_t *denominator)
+{
+ *numerator = get_info(store)->next_free * store->snap->chunk_size;
+ *denominator = get_dev_size(store->snap->cow->bdev);
+}
+
+static void persistent_destroy(struct exception_store *store)
+{
+ struct pstore *ps = get_info(store);
+
+ dm_io_put(sectors_to_pages(ps->chunk_size));
+ vfree(ps->callbacks);
+ free_area(ps);
+ kfree(ps);
+}
+
+static int persistent_read_metadata(struct exception_store *store)
+{
+ int r, new_snapshot;
+ struct pstore *ps = get_info(store);
+
+ /*
+ * Read the snapshot header.
+ */
+ r = read_header(ps, &new_snapshot);
+ if (r)
+ return r;
+
+ /*
+ * Do we need to setup a new snapshot ?
+ */
+ if (new_snapshot) {
+ r = write_header(ps);
+ if (r) {
+ DMWARN("write_header failed");
+ return r;
+ }
+
+ r = zero_area(ps, 0);
+ if (r) {
+ DMWARN("zero_area(0) failed");
+ return r;
+ }
+
+ } else {
+ /*
+ * Sanity checks.
+ */
+ if (!ps->valid) {
+ DMWARN("snapshot is marked invalid");
+ return -EINVAL;
+ }
+
+ if (ps->version != SNAPSHOT_DISK_VERSION) {
+ DMWARN("unable to handle snapshot disk version %d",
+ ps->version);
+ return -EINVAL;
+ }
+
+ /*
+ * Read the metadata.
+ */
+ r = read_exceptions(ps);
+ if (r)
+ return r;
+ }
+
+ return 0;
+}
+
+static int persistent_prepare(struct exception_store *store,
+ struct exception *e)
+{
+ struct pstore *ps = get_info(store);
+ uint32_t stride;
+ sector_t size = get_dev_size(store->snap->cow->bdev);
+
+ /* Is there enough room ? */
+ if (size < ((ps->next_free + 1) * store->snap->chunk_size))
+ return -ENOSPC;
+
+ e->new_chunk = ps->next_free;
+
+ /*
+ * Move onto the next free pending, making sure to take
+ * into account the location of the metadata chunks.
+ */
+ stride = (ps->exceptions_per_area + 1);
+ if ((++ps->next_free % stride) == 1)
+ ps->next_free++;
+
+ atomic_inc(&ps->pending_count);
+ return 0;
+}
+
+static void persistent_commit(struct exception_store *store,
+ struct exception *e,
+ void (*callback) (void *, int success),
+ void *callback_context)
+{
+ int r;
+ unsigned int i;
+ struct pstore *ps = get_info(store);
+ struct disk_exception de;
+ struct commit_callback *cb;
+
+ de.old_chunk = e->old_chunk;
+ de.new_chunk = e->new_chunk;
+ write_exception(ps, ps->current_committed++, &de);
+
+ /*
+ * Add the callback to the back of the array. This code
+ * is the only place where the callback array is
+ * manipulated, and we know that it will never be called
+ * multiple times concurrently.
+ */
+ cb = ps->callbacks + ps->callback_count++;
+ cb->callback = callback;
+ cb->context = callback_context;
+
+ /*
+ * If there are no more exceptions in flight, or we have
+ * filled this metadata area we commit the exceptions to
+ * disk.
+ */
+ if (atomic_dec_and_test(&ps->pending_count) ||
+ (ps->current_committed == ps->exceptions_per_area)) {
+ r = area_io(ps, ps->current_area, WRITE);
+ if (r)
+ ps->valid = 0;
+
+ for (i = 0; i < ps->callback_count; i++) {
+ cb = ps->callbacks + i;
+ cb->callback(cb->context, r == 0 ? 1 : 0);
+ }
+
+ ps->callback_count = 0;
+ }
+
+ /*
+ * Have we completely filled the current area ?
+ */
+ if (ps->current_committed == ps->exceptions_per_area) {
+ ps->current_committed = 0;
+ r = zero_area(ps, ps->current_area + 1);
+ if (r)
+ ps->valid = 0;
+ }
+}
+
+static void persistent_drop(struct exception_store *store)
+{
+ struct pstore *ps = get_info(store);
+
+ ps->valid = 0;
+ if (write_header(ps))
+ DMWARN("write header failed");
+}
+
+int dm_create_persistent(struct exception_store *store, uint32_t chunk_size)
+{
+ int r;
+ struct pstore *ps;
+
+ r = dm_io_get(sectors_to_pages(chunk_size));
+ if (r)
+ return r;
+
+ /* allocate the pstore */
+ ps = kmalloc(sizeof(*ps), GFP_KERNEL);
+ if (!ps) {
+ r = -ENOMEM;
+ goto bad;
+ }
+
+ ps->snap = store->snap;
+ ps->valid = 1;
+ ps->version = SNAPSHOT_DISK_VERSION;
+ ps->chunk_size = chunk_size;
+ ps->exceptions_per_area = (chunk_size << SECTOR_SHIFT) /
+ sizeof(struct disk_exception);
+ ps->next_free = 2; /* skipping the header and first area */
+ ps->current_committed = 0;
+
+ r = alloc_area(ps);
+ if (r)
+ goto bad;
+
+ /*
+ * Allocate space for all the callbacks.
+ */
+ ps->callback_count = 0;
+ atomic_set(&ps->pending_count, 0);
+ ps->callbacks = dm_vcalloc(ps->exceptions_per_area,
+ sizeof(*ps->callbacks));
+
+ if (!ps->callbacks) {
+ r = -ENOMEM;
+ goto bad;
+ }
+
+ store->destroy = persistent_destroy;
+ store->read_metadata = persistent_read_metadata;
+ store->prepare_exception = persistent_prepare;
+ store->commit_exception = persistent_commit;
+ store->drop_snapshot = persistent_drop;
+ store->fraction_full = persistent_fraction_full;
+ store->context = ps;
+
+ return 0;
+
+ bad:
+ dm_io_put(sectors_to_pages(chunk_size));
+ if (ps) {
+ if (ps->area)
+ free_area(ps);
+
+ kfree(ps);
+ }
+ return r;
+}
+
+/*-----------------------------------------------------------------
+ * Implementation of the store for non-persistent snapshots.
+ *---------------------------------------------------------------*/
+struct transient_c {
+ sector_t next_free;
+};
+
+static void transient_destroy(struct exception_store *store)
+{
+ kfree(store->context);
+}
+
+static int transient_read_metadata(struct exception_store *store)
+{
+ return 0;
+}
+
+static int transient_prepare(struct exception_store *store, struct exception *e)
+{
+ struct transient_c *tc = (struct transient_c *) store->context;
+ sector_t size = get_dev_size(store->snap->cow->bdev);
+
+ if (size < (tc->next_free + store->snap->chunk_size))
+ return -1;
+
+ e->new_chunk = sector_to_chunk(store->snap, tc->next_free);
+ tc->next_free += store->snap->chunk_size;
+
+ return 0;
+}
+
+static void transient_commit(struct exception_store *store,
+ struct exception *e,
+ void (*callback) (void *, int success),
+ void *callback_context)
+{
+ /* Just succeed */
+ callback(callback_context, 1);
+}
+
+static void transient_fraction_full(struct exception_store *store,
+ sector_t *numerator, sector_t *denominator)
+{
+ *numerator = ((struct transient_c *) store->context)->next_free;
+ *denominator = get_dev_size(store->snap->cow->bdev);
+}
+
+int dm_create_transient(struct exception_store *store,
+ struct dm_snapshot *s, int blocksize)
+{
+ struct transient_c *tc;
+
+ memset(store, 0, sizeof(*store));
+ store->destroy = transient_destroy;
+ store->read_metadata = transient_read_metadata;
+ store->prepare_exception = transient_prepare;
+ store->commit_exception = transient_commit;
+ store->fraction_full = transient_fraction_full;
+ store->snap = s;
+
+ tc = kmalloc(sizeof(struct transient_c), GFP_KERNEL);
+ if (!tc)
+ return -ENOMEM;
+
+ tc->next_free = 0;
+ store->context = tc;
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2003 Sistina Software
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-io.h"
+
+#include <linux/bio.h>
+#include <linux/mempool.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#define BIO_POOL_SIZE 256
+
+
+/*-----------------------------------------------------------------
+ * Bio set, move this to bio.c
+ *---------------------------------------------------------------*/
+#define BV_NAME_SIZE 16
+struct biovec_pool {
+ int nr_vecs;
+ char name[BV_NAME_SIZE];
+ kmem_cache_t *slab;
+ mempool_t *pool;
+ atomic_t allocated; /* FIXME: debug */
+};
+
+#define BIOVEC_NR_POOLS 6
+struct bio_set {
+ char name[BV_NAME_SIZE];
+ kmem_cache_t *bio_slab;
+ mempool_t *bio_pool;
+ struct biovec_pool pools[BIOVEC_NR_POOLS];
+};
+
+static void bio_set_exit(struct bio_set *bs)
+{
+ unsigned i;
+ struct biovec_pool *bp;
+
+ if (bs->bio_pool)
+ mempool_destroy(bs->bio_pool);
+
+ if (bs->bio_slab)
+ kmem_cache_destroy(bs->bio_slab);
+
+ for (i = 0; i < BIOVEC_NR_POOLS; i++) {
+ bp = bs->pools + i;
+ if (bp->pool)
+ mempool_destroy(bp->pool);
+
+ if (bp->slab)
+ kmem_cache_destroy(bp->slab);
+ }
+}
+
+static void mk_name(char *str, size_t len, const char *prefix, unsigned count)
+{
+ snprintf(str, len, "%s-%u", prefix, count);
+}
+
+static int bio_set_init(struct bio_set *bs, const char *slab_prefix,
+ unsigned pool_entries, unsigned scale)
+{
+ /* FIXME: this must match bvec_index(), why not go the
+ * whole hog and have a pool per power of 2 ? */
+ static unsigned _vec_lengths[BIOVEC_NR_POOLS] = {
+ 1, 4, 16, 64, 128, BIO_MAX_PAGES
+ };
+
+
+ unsigned i, size;
+ struct biovec_pool *bp;
+
+ /* zero the bs so we can tear down properly on error */
+ memset(bs, 0, sizeof(*bs));
+
+ /*
+ * Set up the bio pool.
+ */
+ snprintf(bs->name, sizeof(bs->name), "%s-bio", slab_prefix);
+
+ bs->bio_slab = kmem_cache_create(bs->name, sizeof(struct bio), 0,
+ SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (!bs->bio_slab) {
+ DMWARN("can't init bio slab");
+ goto bad;
+ }
+
+ bs->bio_pool = mempool_create(pool_entries, mempool_alloc_slab,
+ mempool_free_slab, bs->bio_slab);
+ if (!bs->bio_pool) {
+ DMWARN("can't init bio pool");
+ goto bad;
+ }
+
+ /*
+ * Set up the biovec pools.
+ */
+ for (i = 0; i < BIOVEC_NR_POOLS; i++) {
+ bp = bs->pools + i;
+ bp->nr_vecs = _vec_lengths[i];
+ atomic_set(&bp->allocated, 1); /* FIXME: debug */
+
+
+ size = bp->nr_vecs * sizeof(struct bio_vec);
+
+ mk_name(bp->name, sizeof(bp->name), slab_prefix, i);
+ bp->slab = kmem_cache_create(bp->name, size, 0,
+ SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (!bp->slab) {
+ DMWARN("can't init biovec slab cache");
+ goto bad;
+ }
+
+ if (i >= scale)
+ pool_entries >>= 1;
+
+ bp->pool = mempool_create(pool_entries, mempool_alloc_slab,
+ mempool_free_slab, bp->slab);
+ if (!bp->pool) {
+ DMWARN("can't init biovec mempool");
+ goto bad;
+ }
+ }
+
+ return 0;
+
+ bad:
+ bio_set_exit(bs);
+ return -ENOMEM;
+}
+
+/* FIXME: blech */
+static inline unsigned bvec_index(unsigned nr)
+{
+ switch (nr) {
+ case 1: return 0;
+ case 2 ... 4: return 1;
+ case 5 ... 16: return 2;
+ case 17 ... 64: return 3;
+ case 65 ... 128:return 4;
+ case 129 ... BIO_MAX_PAGES: return 5;
+ }
+
+ BUG();
+ return 0;
+}
+
+static inline void bs_bio_init(struct bio *bio)
+{
+ bio->bi_next = NULL;
+ bio->bi_flags = 1 << BIO_UPTODATE;
+ bio->bi_rw = 0;
+ bio->bi_vcnt = 0;
+ bio->bi_idx = 0;
+ bio->bi_phys_segments = 0;
+ bio->bi_hw_segments = 0;
+ bio->bi_size = 0;
+ bio->bi_max_vecs = 0;
+ bio->bi_end_io = NULL;
+ atomic_set(&bio->bi_cnt, 1);
+ bio->bi_private = NULL;
+}
+
+static unsigned _bio_count = 0;
+struct bio *bio_set_alloc(struct bio_set *bs, int gfp_mask, int nr_iovecs)
+{
+ struct biovec_pool *bp;
+ struct bio_vec *bv = NULL;
+ unsigned long idx;
+ struct bio *bio;
+
+ bio = mempool_alloc(bs->bio_pool, gfp_mask);
+ if (unlikely(!bio))
+ return NULL;
+
+ bio_init(bio);
+
+ if (likely(nr_iovecs)) {
+ idx = bvec_index(nr_iovecs);
+ bp = bs->pools + idx;
+ bv = mempool_alloc(bp->pool, gfp_mask);
+ if (!bv) {
+ mempool_free(bio, bs->bio_pool);
+ return NULL;
+ }
+
+ memset(bv, 0, bp->nr_vecs * sizeof(*bv));
+ bio->bi_flags |= idx << BIO_POOL_OFFSET;
+ bio->bi_max_vecs = bp->nr_vecs;
+ atomic_inc(&bp->allocated);
+ }
+
+ bio->bi_io_vec = bv;
+ return bio;
+}
+
+static void bio_set_free(struct bio_set *bs, struct bio *bio)
+{
+ struct biovec_pool *bp = bs->pools + BIO_POOL_IDX(bio);
+
+ if (atomic_dec_and_test(&bp->allocated))
+ BUG();
+
+ mempool_free(bio->bi_io_vec, bp->pool);
+ mempool_free(bio, bs->bio_pool);
+}
+
+/*-----------------------------------------------------------------
+ * dm-io proper
+ *---------------------------------------------------------------*/
+static struct bio_set _bios;
+
+/* FIXME: can we shrink this ? */
+struct io {
+ unsigned long error;
+ atomic_t count;
+ struct task_struct *sleeper;
+ io_notify_fn callback;
+ void *context;
+};
+
+/*
+ * io contexts are only dynamically allocated for asynchronous
+ * io. Since async io is likely to be the majority of io we'll
+ * have the same number of io contexts as buffer heads ! (FIXME:
+ * must reduce this).
+ */
+static unsigned _num_ios;
+static mempool_t *_io_pool;
+
+static void *alloc_io(int gfp_mask, void *pool_data)
+{
+ return kmalloc(sizeof(struct io), gfp_mask);
+}
+
+static void free_io(void *element, void *pool_data)
+{
+ kfree(element);
+}
+
+static unsigned int pages_to_ios(unsigned int pages)
+{
+ return 4 * pages; /* too many ? */
+}
+
+static int resize_pool(unsigned int new_ios)
+{
+ int r = 0;
+
+ if (_io_pool) {
+ if (new_ios == 0) {
+ /* free off the pool */
+ mempool_destroy(_io_pool);
+ _io_pool = NULL;
+ bio_set_exit(&_bios);
+
+ } else {
+ /* resize the pool */
+ r = mempool_resize(_io_pool, new_ios, GFP_KERNEL);
+ }
+
+ } else {
+ /* create new pool */
+ _io_pool = mempool_create(new_ios, alloc_io, free_io, NULL);
+ if (!_io_pool)
+ r = -ENOMEM;
+
+ r = bio_set_init(&_bios, "dm-io", 512, 1);
+ if (r) {
+ mempool_destroy(_io_pool);
+ _io_pool = NULL;
+ }
+ }
+
+ if (!r)
+ _num_ios = new_ios;
+
+ return r;
+}
+
+int dm_io_get(unsigned int num_pages)
+{
+ return resize_pool(_num_ios + pages_to_ios(num_pages));
+}
+
+void dm_io_put(unsigned int num_pages)
+{
+ resize_pool(_num_ios - pages_to_ios(num_pages));
+}
+
+/*-----------------------------------------------------------------
+ * We need to keep track of which region a bio is doing io for.
+ * In order to save a memory allocation we store this the last
+ * bvec which we know is unused (blech).
+ *---------------------------------------------------------------*/
+static inline void bio_set_region(struct bio *bio, unsigned region)
+{
+ bio->bi_io_vec[bio->bi_max_vecs - 1].bv_len = region;
+}
+
+static inline unsigned bio_get_region(struct bio *bio)
+{
+ return bio->bi_io_vec[bio->bi_max_vecs - 1].bv_len;
+}
+
+/*-----------------------------------------------------------------
+ * We need an io object to keep track of the number of bios that
+ * have been dispatched for a particular io.
+ *---------------------------------------------------------------*/
+static void dec_count(struct io *io, unsigned int region, int error)
+{
+ if (error)
+ set_bit(region, &io->error);
+
+ if (atomic_dec_and_test(&io->count)) {
+ if (io->sleeper)
+ wake_up_process(io->sleeper);
+
+ else {
+ int r = io->error;
+ io_notify_fn fn = io->callback;
+ void *context = io->context;
+
+ mempool_free(io, _io_pool);
+ fn(r, context);
+ }
+ }
+}
+
+/* FIXME Move this to bio.h? */
+static void zero_fill_bio(struct bio *bio)
+{
+ unsigned long flags;
+ struct bio_vec *bv;
+ int i;
+
+ bio_for_each_segment(bv, bio, i) {
+ char *data = bvec_kmap_irq(bv, &flags);
+ memset(data, 0, bv->bv_len);
+ flush_dcache_page(bv->bv_page);
+ bvec_kunmap_irq(data, &flags);
+ }
+}
+
+static int endio(struct bio *bio, unsigned int done, int error)
+{
+ struct io *io = (struct io *) bio->bi_private;
+
+ /* keep going until we've finished */
+ if (bio->bi_size)
+ return 1;
+
+ if (error && bio_data_dir(bio) == READ)
+ zero_fill_bio(bio);
+
+ dec_count(io, bio_get_region(bio), error);
+ bio_put(bio);
+
+ return 0;
+}
+
+static void bio_dtr(struct bio *bio)
+{
+ _bio_count--;
+ bio_set_free(&_bios, bio);
+}
+
+/*-----------------------------------------------------------------
+ * These little objects provide an abstraction for getting a new
+ * destination page for io.
+ *---------------------------------------------------------------*/
+struct dpages {
+ void (*get_page)(struct dpages *dp,
+ struct page **p, unsigned long *len, unsigned *offset);
+ void (*next_page)(struct dpages *dp);
+
+ unsigned context_u;
+ void *context_ptr;
+};
+
+/*
+ * Functions for getting the pages from a list.
+ */
+static void list_get_page(struct dpages *dp,
+ struct page **p, unsigned long *len, unsigned *offset)
+{
+ unsigned o = dp->context_u;
+ struct page_list *pl = (struct page_list *) dp->context_ptr;
+
+ *p = pl->page;
+ *len = PAGE_SIZE - o;
+ *offset = o;
+}
+
+static void list_next_page(struct dpages *dp)
+{
+ struct page_list *pl = (struct page_list *) dp->context_ptr;
+ dp->context_ptr = pl->next;
+ dp->context_u = 0;
+}
+
+static void list_dp_init(struct dpages *dp, struct page_list *pl, unsigned offset)
+{
+ dp->get_page = list_get_page;
+ dp->next_page = list_next_page;
+ dp->context_u = offset;
+ dp->context_ptr = pl;
+}
+
+/*
+ * Functions for getting the pages from a bvec.
+ */
+static void bvec_get_page(struct dpages *dp,
+ struct page **p, unsigned long *len, unsigned *offset)
+{
+ struct bio_vec *bvec = (struct bio_vec *) dp->context_ptr;
+ *p = bvec->bv_page;
+ *len = bvec->bv_len;
+ *offset = bvec->bv_offset;
+}
+
+static void bvec_next_page(struct dpages *dp)
+{
+ struct bio_vec *bvec = (struct bio_vec *) dp->context_ptr;
+ dp->context_ptr = bvec + 1;
+}
+
+static void bvec_dp_init(struct dpages *dp, struct bio_vec *bvec)
+{
+ dp->get_page = bvec_get_page;
+ dp->next_page = bvec_next_page;
+ dp->context_ptr = bvec;
+}
+
+static void vm_get_page(struct dpages *dp,
+ struct page **p, unsigned long *len, unsigned *offset)
+{
+ *p = vmalloc_to_page(dp->context_ptr);
+ *offset = dp->context_u;
+ *len = PAGE_SIZE - dp->context_u;
+}
+
+static void vm_next_page(struct dpages *dp)
+{
+ dp->context_ptr += PAGE_SIZE - dp->context_u;
+ dp->context_u = 0;
+}
+
+static void vm_dp_init(struct dpages *dp, void *data)
+{
+ dp->get_page = vm_get_page;
+ dp->next_page = vm_next_page;
+ dp->context_u = ((unsigned long) data) & (PAGE_SIZE - 1);
+ dp->context_ptr = data;
+}
+
+/*-----------------------------------------------------------------
+ * IO routines that accept a list of pages.
+ *---------------------------------------------------------------*/
+static void do_region(int rw, unsigned int region, struct io_region *where,
+ struct dpages *dp, struct io *io)
+{
+ struct bio *bio;
+ struct page *page;
+ unsigned long len;
+ unsigned offset;
+ unsigned num_bvecs;
+ sector_t remaining = where->count;
+
+ while (remaining) {
+ /*
+ * Allocate a suitably sized bio, we add an extra
+ * bvec for bio_get/set_region().
+ */
+ num_bvecs = (remaining / (PAGE_SIZE >> 9)) + 2;
+ _bio_count++;
+ bio = bio_set_alloc(&_bios, GFP_NOIO, num_bvecs);
+ bio->bi_sector = where->sector + (where->count - remaining);
+ bio->bi_bdev = where->bdev;
+ bio->bi_end_io = endio;
+ bio->bi_private = io;
+ bio->bi_destructor = bio_dtr;
+ bio_set_region(bio, region);
+
+ /*
+ * Try and add as many pages as possible.
+ */
+ while (remaining) {
+ dp->get_page(dp, &page, &len, &offset);
+ len = min(len, to_bytes(remaining));
+ if (!bio_add_page(bio, page, len, offset))
+ break;
+
+ offset = 0;
+ remaining -= to_sector(len);
+ dp->next_page(dp);
+ }
+
+ atomic_inc(&io->count);
+ submit_bio(rw, bio);
+ }
+}
+
+static void dispatch_io(int rw, unsigned int num_regions,
+ struct io_region *where, struct dpages *dp,
+ struct io *io, int sync)
+{
+ int i;
+ struct dpages old_pages = *dp;
+
+ if (sync)
+ rw |= (1 << BIO_RW_SYNC);
+
+ /*
+ * For multiple regions we need to be careful to rewind
+ * the dp object for each call to do_region.
+ */
+ for (i = 0; i < num_regions; i++) {
+ *dp = old_pages;
+ if (where[i].count)
+ do_region(rw, i, where + i, dp, io);
+ }
+
+ /*
+ * Drop the extra refence that we were holding to avoid
+ * the io being completed too early.
+ */
+ dec_count(io, 0, 0);
+}
+
+static int sync_io(unsigned int num_regions, struct io_region *where,
+ int rw, struct dpages *dp, unsigned long *error_bits)
+{
+ struct io io;
+
+ if (num_regions > 1 && rw != WRITE) {
+ WARN_ON(1);
+ return -EIO;
+ }
+
+ io.error = 0;
+ atomic_set(&io.count, 1); /* see dispatch_io() */
+ io.sleeper = current;
+
+ dispatch_io(rw, num_regions, where, dp, &io, 1);
+
+ while (1) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+
+ if (!atomic_read(&io.count) || signal_pending(current))
+ break;
+
+ io_schedule();
+ }
+ set_current_state(TASK_RUNNING);
+
+ if (atomic_read(&io.count))
+ return -EINTR;
+
+ *error_bits = io.error;
+ return io.error ? -EIO : 0;
+}
+
+static int async_io(unsigned int num_regions, struct io_region *where, int rw,
+ struct dpages *dp, io_notify_fn fn, void *context)
+{
+ struct io *io;
+
+ if (num_regions > 1 && rw != WRITE) {
+ WARN_ON(1);
+ fn(1, context);
+ return -EIO;
+ }
+
+ io = mempool_alloc(_io_pool, GFP_NOIO);
+ io->error = 0;
+ atomic_set(&io->count, 1); /* see dispatch_io() */
+ io->sleeper = NULL;
+ io->callback = fn;
+ io->context = context;
+
+ dispatch_io(rw, num_regions, where, dp, io, 0);
+ return 0;
+}
+
+int dm_io_sync(unsigned int num_regions, struct io_region *where, int rw,
+ struct page_list *pl, unsigned int offset,
+ unsigned long *error_bits)
+{
+ struct dpages dp;
+ list_dp_init(&dp, pl, offset);
+ return sync_io(num_regions, where, rw, &dp, error_bits);
+}
+
+int dm_io_sync_bvec(unsigned int num_regions, struct io_region *where, int rw,
+ struct bio_vec *bvec, unsigned long *error_bits)
+{
+ struct dpages dp;
+ bvec_dp_init(&dp, bvec);
+ return sync_io(num_regions, where, rw, &dp, error_bits);
+}
+
+int dm_io_sync_vm(unsigned int num_regions, struct io_region *where, int rw,
+ void *data, unsigned long *error_bits)
+{
+ struct dpages dp;
+ vm_dp_init(&dp, data);
+ return sync_io(num_regions, where, rw, &dp, error_bits);
+}
+
+int dm_io_async(unsigned int num_regions, struct io_region *where, int rw,
+ struct page_list *pl, unsigned int offset,
+ io_notify_fn fn, void *context)
+{
+ struct dpages dp;
+ list_dp_init(&dp, pl, offset);
+ return async_io(num_regions, where, rw, &dp, fn, context);
+}
+
+int dm_io_async_bvec(unsigned int num_regions, struct io_region *where, int rw,
+ struct bio_vec *bvec, io_notify_fn fn, void *context)
+{
+ struct dpages dp;
+ bvec_dp_init(&dp, bvec);
+ return async_io(num_regions, where, rw, &dp, fn, context);
+}
+
+int dm_io_async_vm(unsigned int num_regions, struct io_region *where, int rw,
+ void *data, io_notify_fn fn, void *context)
+{
+ struct dpages dp;
+ vm_dp_init(&dp, data);
+ return async_io(num_regions, where, rw, &dp, fn, context);
+}
+
+EXPORT_SYMBOL(dm_io_get);
+EXPORT_SYMBOL(dm_io_put);
+EXPORT_SYMBOL(dm_io_sync);
+EXPORT_SYMBOL(dm_io_async);
+EXPORT_SYMBOL(dm_io_sync_bvec);
+EXPORT_SYMBOL(dm_io_async_bvec);
+EXPORT_SYMBOL(dm_io_sync_vm);
+EXPORT_SYMBOL(dm_io_async_vm);
--- /dev/null
+/*
+ * Copyright (C) 2003 Sistina Software
+ *
+ * This file is released under the LGPL.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+
+#include "dm-log.h"
+#include "dm-io.h"
+
+static LIST_HEAD(_log_types);
+static spinlock_t _lock = SPIN_LOCK_UNLOCKED;
+
+int dm_register_dirty_log_type(struct dirty_log_type *type)
+{
+ if (!try_module_get(type->module))
+ return -EINVAL;
+
+ spin_lock(&_lock);
+ type->use_count = 0;
+ list_add(&type->list, &_log_types);
+ spin_unlock(&_lock);
+
+ return 0;
+}
+
+int dm_unregister_dirty_log_type(struct dirty_log_type *type)
+{
+ spin_lock(&_lock);
+
+ if (type->use_count)
+ DMWARN("Attempt to unregister a log type that is still in use");
+ else {
+ list_del(&type->list);
+ module_put(type->module);
+ }
+
+ spin_unlock(&_lock);
+
+ return 0;
+}
+
+static struct dirty_log_type *get_type(const char *type_name)
+{
+ struct dirty_log_type *type;
+
+ spin_lock(&_lock);
+ list_for_each_entry (type, &_log_types, list)
+ if (!strcmp(type_name, type->name)) {
+ type->use_count++;
+ spin_unlock(&_lock);
+ return type;
+ }
+
+ spin_unlock(&_lock);
+ return NULL;
+}
+
+static void put_type(struct dirty_log_type *type)
+{
+ spin_lock(&_lock);
+ type->use_count--;
+ spin_unlock(&_lock);
+}
+
+struct dirty_log *dm_create_dirty_log(const char *type_name, struct dm_target *ti,
+ unsigned int argc, char **argv)
+{
+ struct dirty_log_type *type;
+ struct dirty_log *log;
+
+ log = kmalloc(sizeof(*log), GFP_KERNEL);
+ if (!log)
+ return NULL;
+
+ type = get_type(type_name);
+ if (!type) {
+ kfree(log);
+ return NULL;
+ }
+
+ log->type = type;
+ if (type->ctr(log, ti, argc, argv)) {
+ kfree(log);
+ put_type(type);
+ return NULL;
+ }
+
+ return log;
+}
+
+void dm_destroy_dirty_log(struct dirty_log *log)
+{
+ log->type->dtr(log);
+ put_type(log->type);
+ kfree(log);
+}
+
+/*-----------------------------------------------------------------
+ * Persistent and core logs share a lot of their implementation.
+ * FIXME: need a reload method to be called from a resume
+ *---------------------------------------------------------------*/
+/*
+ * Magic for persistent mirrors: "MiRr"
+ */
+#define MIRROR_MAGIC 0x4D695272
+
+/*
+ * The on-disk version of the metadata.
+ */
+#define MIRROR_DISK_VERSION 1
+#define LOG_OFFSET 2
+
+struct log_header {
+ uint32_t magic;
+
+ /*
+ * Simple, incrementing version. no backward
+ * compatibility.
+ */
+ uint32_t version;
+ sector_t nr_regions;
+};
+
+struct log_c {
+ struct dm_target *ti;
+ int touched;
+ sector_t region_size;
+ unsigned int region_count;
+ region_t sync_count;
+
+ unsigned bitset_uint32_count;
+ uint32_t *clean_bits;
+ uint32_t *sync_bits;
+ uint32_t *recovering_bits; /* FIXME: this seems excessive */
+
+ int sync_search;
+
+ /*
+ * Disk log fields
+ */
+ struct dm_dev *log_dev;
+ struct log_header header;
+
+ struct io_region header_location;
+ struct log_header *disk_header;
+
+ struct io_region bits_location;
+ uint32_t *disk_bits;
+};
+
+/*
+ * The touched member needs to be updated every time we access
+ * one of the bitsets.
+ */
+static inline int log_test_bit(uint32_t *bs, unsigned bit)
+{
+ return test_bit(bit, (unsigned long *) bs) ? 1 : 0;
+}
+
+static inline void log_set_bit(struct log_c *l,
+ uint32_t *bs, unsigned bit)
+{
+ set_bit(bit, (unsigned long *) bs);
+ l->touched = 1;
+}
+
+static inline void log_clear_bit(struct log_c *l,
+ uint32_t *bs, unsigned bit)
+{
+ clear_bit(bit, (unsigned long *) bs);
+ l->touched = 1;
+}
+
+/*----------------------------------------------------------------
+ * Header IO
+ *--------------------------------------------------------------*/
+static void header_to_disk(struct log_header *core, struct log_header *disk)
+{
+ disk->magic = cpu_to_le32(core->magic);
+ disk->version = cpu_to_le32(core->version);
+ disk->nr_regions = cpu_to_le64(core->nr_regions);
+}
+
+static void header_from_disk(struct log_header *core, struct log_header *disk)
+{
+ core->magic = le32_to_cpu(disk->magic);
+ core->version = le32_to_cpu(disk->version);
+ core->nr_regions = le64_to_cpu(disk->nr_regions);
+}
+
+static int read_header(struct log_c *log)
+{
+ int r;
+ unsigned long ebits;
+
+ r = dm_io_sync_vm(1, &log->header_location, READ,
+ log->disk_header, &ebits);
+ if (r)
+ return r;
+
+ header_from_disk(&log->header, log->disk_header);
+
+ if (log->header.magic != MIRROR_MAGIC) {
+ log->header.magic = MIRROR_MAGIC;
+ log->header.version = MIRROR_DISK_VERSION;
+ log->header.nr_regions = 0;
+ }
+
+ if (log->header.version != MIRROR_DISK_VERSION) {
+ DMWARN("incompatible disk log version");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static inline int write_header(struct log_c *log)
+{
+ unsigned long ebits;
+
+ header_to_disk(&log->header, log->disk_header);
+ return dm_io_sync_vm(1, &log->header_location, WRITE,
+ log->disk_header, &ebits);
+}
+
+/*----------------------------------------------------------------
+ * Bits IO
+ *--------------------------------------------------------------*/
+static inline void bits_to_core(uint32_t *core, uint32_t *disk, unsigned count)
+{
+ unsigned i;
+
+ for (i = 0; i < count; i++)
+ core[i] = le32_to_cpu(disk[i]);
+}
+
+static inline void bits_to_disk(uint32_t *core, uint32_t *disk, unsigned count)
+{
+ unsigned i;
+
+ /* copy across the clean/dirty bitset */
+ for (i = 0; i < count; i++)
+ disk[i] = cpu_to_le32(core[i]);
+}
+
+static int read_bits(struct log_c *log)
+{
+ int r;
+ unsigned long ebits;
+
+ r = dm_io_sync_vm(1, &log->bits_location, READ,
+ log->disk_bits, &ebits);
+ if (r)
+ return r;
+
+ bits_to_core(log->clean_bits, log->disk_bits,
+ log->bitset_uint32_count);
+ return 0;
+}
+
+static int write_bits(struct log_c *log)
+{
+ unsigned long ebits;
+ bits_to_disk(log->clean_bits, log->disk_bits,
+ log->bitset_uint32_count);
+ return dm_io_sync_vm(1, &log->bits_location, WRITE,
+ log->disk_bits, &ebits);
+}
+
+/*----------------------------------------------------------------
+ * constructor/destructor
+ *--------------------------------------------------------------*/
+#define BYTE_SHIFT 3
+static int core_ctr(struct dirty_log *log, struct dm_target *ti,
+ unsigned int argc, char **argv)
+{
+ struct log_c *lc;
+ sector_t region_size;
+ unsigned int region_count;
+ size_t bitset_size;
+
+ if (argc != 1) {
+ DMWARN("wrong number of arguments to log_c");
+ return -EINVAL;
+ }
+
+ if (sscanf(argv[0], SECTOR_FORMAT, ®ion_size) != 1) {
+ DMWARN("invalid region size string");
+ return -EINVAL;
+ }
+
+ region_count = dm_div_up(ti->len, region_size);
+
+ lc = kmalloc(sizeof(*lc), GFP_KERNEL);
+ if (!lc) {
+ DMWARN("couldn't allocate core log");
+ return -ENOMEM;
+ }
+
+ lc->ti = ti;
+ lc->touched = 0;
+ lc->region_size = region_size;
+ lc->region_count = region_count;
+
+ /*
+ * Work out how many words we need to hold the bitset.
+ */
+ bitset_size = dm_round_up(region_count,
+ sizeof(*lc->clean_bits) << BYTE_SHIFT);
+ bitset_size >>= BYTE_SHIFT;
+
+ lc->bitset_uint32_count = bitset_size / 4;
+ lc->clean_bits = vmalloc(bitset_size);
+ if (!lc->clean_bits) {
+ DMWARN("couldn't allocate clean bitset");
+ kfree(lc);
+ return -ENOMEM;
+ }
+ memset(lc->clean_bits, -1, bitset_size);
+
+ lc->sync_bits = vmalloc(bitset_size);
+ if (!lc->sync_bits) {
+ DMWARN("couldn't allocate sync bitset");
+ vfree(lc->clean_bits);
+ kfree(lc);
+ return -ENOMEM;
+ }
+ memset(lc->sync_bits, 0, bitset_size);
+ lc->sync_count = 0;
+
+ lc->recovering_bits = vmalloc(bitset_size);
+ if (!lc->recovering_bits) {
+ DMWARN("couldn't allocate sync bitset");
+ vfree(lc->sync_bits);
+ vfree(lc->clean_bits);
+ kfree(lc);
+ return -ENOMEM;
+ }
+ memset(lc->recovering_bits, 0, bitset_size);
+ lc->sync_search = 0;
+ log->context = lc;
+ return 0;
+}
+
+static void core_dtr(struct dirty_log *log)
+{
+ struct log_c *lc = (struct log_c *) log->context;
+ vfree(lc->clean_bits);
+ vfree(lc->sync_bits);
+ vfree(lc->recovering_bits);
+ kfree(lc);
+}
+
+static int disk_ctr(struct dirty_log *log, struct dm_target *ti,
+ unsigned int argc, char **argv)
+{
+ int r;
+ size_t size;
+ struct log_c *lc;
+ struct dm_dev *dev;
+
+ if (argc != 2) {
+ DMWARN("wrong number of arguments to log_d");
+ return -EINVAL;
+ }
+
+ r = dm_get_device(ti, argv[0], 0, 0 /* FIXME */,
+ FMODE_READ | FMODE_WRITE, &dev);
+ if (r)
+ return r;
+
+ r = core_ctr(log, ti, argc - 1, argv + 1);
+ if (r) {
+ dm_put_device(ti, dev);
+ return r;
+ }
+
+ lc = (struct log_c *) log->context;
+ lc->log_dev = dev;
+
+ /* setup the disk header fields */
+ lc->header_location.bdev = lc->log_dev->bdev;
+ lc->header_location.sector = 0;
+ lc->header_location.count = 1;
+
+ /*
+ * We can't read less than this amount, even though we'll
+ * not be using most of this space.
+ */
+ lc->disk_header = vmalloc(1 << SECTOR_SHIFT);
+ if (!lc->disk_header)
+ goto bad;
+
+ /* setup the disk bitset fields */
+ lc->bits_location.bdev = lc->log_dev->bdev;
+ lc->bits_location.sector = LOG_OFFSET;
+
+ size = dm_round_up(lc->bitset_uint32_count * sizeof(uint32_t),
+ 1 << SECTOR_SHIFT);
+ lc->bits_location.count = size >> SECTOR_SHIFT;
+ lc->disk_bits = vmalloc(size);
+ if (!lc->disk_bits) {
+ vfree(lc->disk_header);
+ goto bad;
+ }
+ return 0;
+
+ bad:
+ dm_put_device(ti, lc->log_dev);
+ core_dtr(log);
+ return -ENOMEM;
+}
+
+static void disk_dtr(struct dirty_log *log)
+{
+ struct log_c *lc = (struct log_c *) log->context;
+ dm_put_device(lc->ti, lc->log_dev);
+ vfree(lc->disk_header);
+ vfree(lc->disk_bits);
+ core_dtr(log);
+}
+
+static int count_bits32(uint32_t *addr, unsigned size)
+{
+ int count = 0, i;
+
+ for (i = 0; i < size; i++) {
+ count += hweight32(*(addr+i));
+ }
+ return count;
+}
+
+static int disk_resume(struct dirty_log *log)
+{
+ int r;
+ unsigned i;
+ struct log_c *lc = (struct log_c *) log->context;
+ size_t size = lc->bitset_uint32_count * sizeof(uint32_t);
+
+ /* read the disk header */
+ r = read_header(lc);
+ if (r)
+ return r;
+
+ /* read the bits */
+ r = read_bits(lc);
+ if (r)
+ return r;
+
+ /* zero any new bits if the mirror has grown */
+ for (i = lc->header.nr_regions; i < lc->region_count; i++)
+ /* FIXME: amazingly inefficient */
+ log_clear_bit(lc, lc->clean_bits, i);
+
+ /* copy clean across to sync */
+ memcpy(lc->sync_bits, lc->clean_bits, size);
+ lc->sync_count = count_bits32(lc->clean_bits, lc->bitset_uint32_count);
+
+ /* write the bits */
+ r = write_bits(lc);
+ if (r)
+ return r;
+
+ /* set the correct number of regions in the header */
+ lc->header.nr_regions = lc->region_count;
+
+ /* write the new header */
+ return write_header(lc);
+}
+
+static sector_t core_get_region_size(struct dirty_log *log)
+{
+ struct log_c *lc = (struct log_c *) log->context;
+ return lc->region_size;
+}
+
+static int core_is_clean(struct dirty_log *log, region_t region)
+{
+ struct log_c *lc = (struct log_c *) log->context;
+ return log_test_bit(lc->clean_bits, region);
+}
+
+static int core_in_sync(struct dirty_log *log, region_t region, int block)
+{
+ struct log_c *lc = (struct log_c *) log->context;
+ return log_test_bit(lc->sync_bits, region);
+}
+
+static int core_flush(struct dirty_log *log)
+{
+ /* no op */
+ return 0;
+}
+
+static int disk_flush(struct dirty_log *log)
+{
+ int r;
+ struct log_c *lc = (struct log_c *) log->context;
+
+ /* only write if the log has changed */
+ if (!lc->touched)
+ return 0;
+
+ r = write_bits(lc);
+ if (!r)
+ lc->touched = 0;
+
+ return r;
+}
+
+static void core_mark_region(struct dirty_log *log, region_t region)
+{
+ struct log_c *lc = (struct log_c *) log->context;
+ log_clear_bit(lc, lc->clean_bits, region);
+}
+
+static void core_clear_region(struct dirty_log *log, region_t region)
+{
+ struct log_c *lc = (struct log_c *) log->context;
+ log_set_bit(lc, lc->clean_bits, region);
+}
+
+static int core_get_resync_work(struct dirty_log *log, region_t *region)
+{
+ struct log_c *lc = (struct log_c *) log->context;
+
+ if (lc->sync_search >= lc->region_count)
+ return 0;
+
+ do {
+ *region = find_next_zero_bit((unsigned long *) lc->sync_bits,
+ lc->region_count,
+ lc->sync_search);
+ lc->sync_search = *region + 1;
+
+ if (*region == lc->region_count)
+ return 0;
+
+ } while (log_test_bit(lc->recovering_bits, *region));
+
+ log_set_bit(lc, lc->recovering_bits, *region);
+ return 1;
+}
+
+static void core_complete_resync_work(struct dirty_log *log, region_t region,
+ int success)
+{
+ struct log_c *lc = (struct log_c *) log->context;
+
+ log_clear_bit(lc, lc->recovering_bits, region);
+ if (success) {
+ log_set_bit(lc, lc->sync_bits, region);
+ lc->sync_count++;
+ }
+}
+
+static region_t core_get_sync_count(struct dirty_log *log)
+{
+ struct log_c *lc = (struct log_c *) log->context;
+
+ return lc->sync_count;
+}
+
+static struct dirty_log_type _core_type = {
+ .name = "core",
+ .module = THIS_MODULE,
+ .ctr = core_ctr,
+ .dtr = core_dtr,
+ .get_region_size = core_get_region_size,
+ .is_clean = core_is_clean,
+ .in_sync = core_in_sync,
+ .flush = core_flush,
+ .mark_region = core_mark_region,
+ .clear_region = core_clear_region,
+ .get_resync_work = core_get_resync_work,
+ .complete_resync_work = core_complete_resync_work,
+ .get_sync_count = core_get_sync_count
+};
+
+static struct dirty_log_type _disk_type = {
+ .name = "disk",
+ .module = THIS_MODULE,
+ .ctr = disk_ctr,
+ .dtr = disk_dtr,
+ .suspend = disk_flush,
+ .resume = disk_resume,
+ .get_region_size = core_get_region_size,
+ .is_clean = core_is_clean,
+ .in_sync = core_in_sync,
+ .flush = disk_flush,
+ .mark_region = core_mark_region,
+ .clear_region = core_clear_region,
+ .get_resync_work = core_get_resync_work,
+ .complete_resync_work = core_complete_resync_work,
+ .get_sync_count = core_get_sync_count
+};
+
+int __init dm_dirty_log_init(void)
+{
+ int r;
+
+ r = dm_register_dirty_log_type(&_core_type);
+ if (r)
+ DMWARN("couldn't register core log");
+
+ r = dm_register_dirty_log_type(&_disk_type);
+ if (r) {
+ DMWARN("couldn't register disk type");
+ dm_unregister_dirty_log_type(&_core_type);
+ }
+
+ return r;
+}
+
+void dm_dirty_log_exit(void)
+{
+ dm_unregister_dirty_log_type(&_disk_type);
+ dm_unregister_dirty_log_type(&_core_type);
+}
+
+EXPORT_SYMBOL(dm_register_dirty_log_type);
+EXPORT_SYMBOL(dm_unregister_dirty_log_type);
+EXPORT_SYMBOL(dm_create_dirty_log);
+EXPORT_SYMBOL(dm_destroy_dirty_log);
--- /dev/null
+/*
+ * Copyright (C) 2003 Sistina Software
+ *
+ * This file is released under the LGPL.
+ */
+
+#ifndef DM_DIRTY_LOG
+#define DM_DIRTY_LOG
+
+#include "dm.h"
+
+typedef sector_t region_t;
+
+struct dirty_log_type;
+
+struct dirty_log {
+ struct dirty_log_type *type;
+ void *context;
+};
+
+struct dirty_log_type {
+ struct list_head list;
+ const char *name;
+ struct module *module;
+ unsigned int use_count;
+
+ int (*ctr)(struct dirty_log *log, struct dm_target *ti,
+ unsigned int argc, char **argv);
+ void (*dtr)(struct dirty_log *log);
+
+ /*
+ * There are times when we don't want the log to touch
+ * the disk.
+ */
+ int (*suspend)(struct dirty_log *log);
+ int (*resume)(struct dirty_log *log);
+
+ /*
+ * Retrieves the smallest size of region that the log can
+ * deal with.
+ */
+ sector_t (*get_region_size)(struct dirty_log *log);
+
+ /*
+ * A predicate to say whether a region is clean or not.
+ * May block.
+ */
+ int (*is_clean)(struct dirty_log *log, region_t region);
+
+ /*
+ * Returns: 0, 1, -EWOULDBLOCK, < 0
+ *
+ * A predicate function to check the area given by
+ * [sector, sector + len) is in sync.
+ *
+ * If -EWOULDBLOCK is returned the state of the region is
+ * unknown, typically this will result in a read being
+ * passed to a daemon to deal with, since a daemon is
+ * allowed to block.
+ */
+ int (*in_sync)(struct dirty_log *log, region_t region, int can_block);
+
+ /*
+ * Flush the current log state (eg, to disk). This
+ * function may block.
+ */
+ int (*flush)(struct dirty_log *log);
+
+ /*
+ * Mark an area as clean or dirty. These functions may
+ * block, though for performance reasons blocking should
+ * be extremely rare (eg, allocating another chunk of
+ * memory for some reason).
+ */
+ void (*mark_region)(struct dirty_log *log, region_t region);
+ void (*clear_region)(struct dirty_log *log, region_t region);
+
+ /*
+ * Returns: <0 (error), 0 (no region), 1 (region)
+ *
+ * The mirrord will need perform recovery on regions of
+ * the mirror that are in the NOSYNC state. This
+ * function asks the log to tell the caller about the
+ * next region that this machine should recover.
+ *
+ * Do not confuse this function with 'in_sync()', one
+ * tells you if an area is synchronised, the other
+ * assigns recovery work.
+ */
+ int (*get_resync_work)(struct dirty_log *log, region_t *region);
+
+ /*
+ * This notifies the log that the resync of an area has
+ * been completed. The log should then mark this region
+ * as CLEAN.
+ */
+ void (*complete_resync_work)(struct dirty_log *log,
+ region_t region, int success);
+
+ /*
+ * Returns the number of regions that are in sync.
+ */
+ region_t (*get_sync_count)(struct dirty_log *log);
+};
+
+int dm_register_dirty_log_type(struct dirty_log_type *type);
+int dm_unregister_dirty_log_type(struct dirty_log_type *type);
+
+
+/*
+ * Make sure you use these two functions, rather than calling
+ * type->constructor/destructor() directly.
+ */
+struct dirty_log *dm_create_dirty_log(const char *type_name, struct dm_target *ti,
+ unsigned int argc, char **argv);
+void dm_destroy_dirty_log(struct dirty_log *log);
+
+/*
+ * init/exit functions.
+ */
+int dm_dirty_log_init(void);
+void dm_dirty_log_exit(void);
+
+#endif
{
int r;
unsigned int i;
- struct io_region from, to[ms->nr_mirrors - 1], *dest;
+ struct io_region from, to[KCOPYD_MAX_REGIONS], *dest;
struct mirror *m;
unsigned long flags = 0;
static void do_write(struct mirror_set *ms, struct bio *bio)
{
unsigned int i;
- struct io_region io[ms->nr_mirrors];
+ struct io_region io[KCOPYD_MAX_REGIONS+1];
struct mirror *m;
for (i = 0; i < ms->nr_mirrors; i++) {
argc -= args_used;
if (!argc || sscanf(argv[0], "%u", &nr_mirrors) != 1 ||
- nr_mirrors < 2) {
+ nr_mirrors < 2 || nr_mirrors > KCOPYD_MAX_REGIONS + 1) {
ti->error = "dm-mirror: Invalid number of mirrors";
dm_destroy_dirty_log(dl);
return -EINVAL;
--- /dev/null
+/*
+ * dm-snapshot.c
+ *
+ * Copyright (C) 2001-2002 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include <linux/blkdev.h>
+#include <linux/config.h>
+#include <linux/ctype.h>
+#include <linux/device-mapper.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kdev_t.h>
+#include <linux/list.h>
+#include <linux/mempool.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include "dm-snap.h"
+#include "dm-bio-list.h"
+#include "kcopyd.h"
+
+/*
+ * The percentage increment we will wake up users at
+ */
+#define WAKE_UP_PERCENT 5
+
+/*
+ * kcopyd priority of snapshot operations
+ */
+#define SNAPSHOT_COPY_PRIORITY 2
+
+/*
+ * Each snapshot reserves this many pages for io
+ */
+#define SNAPSHOT_PAGES 256
+
+struct pending_exception {
+ struct exception e;
+
+ /*
+ * Origin buffers waiting for this to complete are held
+ * in a bio list
+ */
+ struct bio_list origin_bios;
+ struct bio_list snapshot_bios;
+
+ /*
+ * Other pending_exceptions that are processing this
+ * chunk. When this list is empty, we know we can
+ * complete the origins.
+ */
+ struct list_head siblings;
+
+ /* Pointer back to snapshot context */
+ struct dm_snapshot *snap;
+
+ /*
+ * 1 indicates the exception has already been sent to
+ * kcopyd.
+ */
+ int started;
+};
+
+/*
+ * Hash table mapping origin volumes to lists of snapshots and
+ * a lock to protect it
+ */
+static kmem_cache_t *exception_cache;
+static kmem_cache_t *pending_cache;
+static mempool_t *pending_pool;
+
+/*
+ * One of these per registered origin, held in the snapshot_origins hash
+ */
+struct origin {
+ /* The origin device */
+ struct block_device *bdev;
+
+ struct list_head hash_list;
+
+ /* List of snapshots for this origin */
+ struct list_head snapshots;
+};
+
+/*
+ * Size of the hash table for origin volumes. If we make this
+ * the size of the minors list then it should be nearly perfect
+ */
+#define ORIGIN_HASH_SIZE 256
+#define ORIGIN_MASK 0xFF
+static struct list_head *_origins;
+static struct rw_semaphore _origins_lock;
+
+static int init_origin_hash(void)
+{
+ int i;
+
+ _origins = kmalloc(ORIGIN_HASH_SIZE * sizeof(struct list_head),
+ GFP_KERNEL);
+ if (!_origins) {
+ DMERR("Device mapper: Snapshot: unable to allocate memory");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < ORIGIN_HASH_SIZE; i++)
+ INIT_LIST_HEAD(_origins + i);
+ init_rwsem(&_origins_lock);
+
+ return 0;
+}
+
+static void exit_origin_hash(void)
+{
+ kfree(_origins);
+}
+
+static inline unsigned int origin_hash(struct block_device *bdev)
+{
+ return bdev->bd_dev & ORIGIN_MASK;
+}
+
+static struct origin *__lookup_origin(struct block_device *origin)
+{
+ struct list_head *ol;
+ struct origin *o;
+
+ ol = &_origins[origin_hash(origin)];
+ list_for_each_entry (o, ol, hash_list)
+ if (bdev_equal(o->bdev, origin))
+ return o;
+
+ return NULL;
+}
+
+static void __insert_origin(struct origin *o)
+{
+ struct list_head *sl = &_origins[origin_hash(o->bdev)];
+ list_add_tail(&o->hash_list, sl);
+}
+
+/*
+ * Make a note of the snapshot and its origin so we can look it
+ * up when the origin has a write on it.
+ */
+static int register_snapshot(struct dm_snapshot *snap)
+{
+ struct origin *o;
+ struct block_device *bdev = snap->origin->bdev;
+
+ down_write(&_origins_lock);
+ o = __lookup_origin(bdev);
+
+ if (!o) {
+ /* New origin */
+ o = kmalloc(sizeof(*o), GFP_KERNEL);
+ if (!o) {
+ up_write(&_origins_lock);
+ return -ENOMEM;
+ }
+
+ /* Initialise the struct */
+ INIT_LIST_HEAD(&o->snapshots);
+ o->bdev = bdev;
+
+ __insert_origin(o);
+ }
+
+ list_add_tail(&snap->list, &o->snapshots);
+
+ up_write(&_origins_lock);
+ return 0;
+}
+
+static void unregister_snapshot(struct dm_snapshot *s)
+{
+ struct origin *o;
+
+ down_write(&_origins_lock);
+ o = __lookup_origin(s->origin->bdev);
+
+ list_del(&s->list);
+ if (list_empty(&o->snapshots)) {
+ list_del(&o->hash_list);
+ kfree(o);
+ }
+
+ up_write(&_origins_lock);
+}
+
+/*
+ * Implementation of the exception hash tables.
+ */
+static int init_exception_table(struct exception_table *et, uint32_t size)
+{
+ unsigned int i;
+
+ et->hash_mask = size - 1;
+ et->table = dm_vcalloc(size, sizeof(struct list_head));
+ if (!et->table)
+ return -ENOMEM;
+
+ for (i = 0; i < size; i++)
+ INIT_LIST_HEAD(et->table + i);
+
+ return 0;
+}
+
+static void exit_exception_table(struct exception_table *et, kmem_cache_t *mem)
+{
+ struct list_head *slot;
+ struct exception *ex, *next;
+ int i, size;
+
+ size = et->hash_mask + 1;
+ for (i = 0; i < size; i++) {
+ slot = et->table + i;
+
+ list_for_each_entry_safe (ex, next, slot, hash_list)
+ kmem_cache_free(mem, ex);
+ }
+
+ vfree(et->table);
+}
+
+static inline uint32_t exception_hash(struct exception_table *et, chunk_t chunk)
+{
+ return chunk & et->hash_mask;
+}
+
+static void insert_exception(struct exception_table *eh, struct exception *e)
+{
+ struct list_head *l = &eh->table[exception_hash(eh, e->old_chunk)];
+ list_add(&e->hash_list, l);
+}
+
+static inline void remove_exception(struct exception *e)
+{
+ list_del(&e->hash_list);
+}
+
+/*
+ * Return the exception data for a sector, or NULL if not
+ * remapped.
+ */
+static struct exception *lookup_exception(struct exception_table *et,
+ chunk_t chunk)
+{
+ struct list_head *slot;
+ struct exception *e;
+
+ slot = &et->table[exception_hash(et, chunk)];
+ list_for_each_entry (e, slot, hash_list)
+ if (e->old_chunk == chunk)
+ return e;
+
+ return NULL;
+}
+
+static inline struct exception *alloc_exception(void)
+{
+ struct exception *e;
+
+ e = kmem_cache_alloc(exception_cache, GFP_NOIO);
+ if (!e)
+ e = kmem_cache_alloc(exception_cache, GFP_ATOMIC);
+
+ return e;
+}
+
+static inline void free_exception(struct exception *e)
+{
+ kmem_cache_free(exception_cache, e);
+}
+
+static inline struct pending_exception *alloc_pending_exception(void)
+{
+ return mempool_alloc(pending_pool, GFP_NOIO);
+}
+
+static inline void free_pending_exception(struct pending_exception *pe)
+{
+ mempool_free(pe, pending_pool);
+}
+
+int dm_add_exception(struct dm_snapshot *s, chunk_t old, chunk_t new)
+{
+ struct exception *e;
+
+ e = alloc_exception();
+ if (!e)
+ return -ENOMEM;
+
+ e->old_chunk = old;
+ e->new_chunk = new;
+ insert_exception(&s->complete, e);
+ return 0;
+}
+
+/*
+ * Hard coded magic.
+ */
+static int calc_max_buckets(void)
+{
+ /* use a fixed size of 2MB */
+ unsigned long mem = 2 * 1024 * 1024;
+ mem /= sizeof(struct list_head);
+
+ return mem;
+}
+
+/*
+ * Rounds a number down to a power of 2.
+ */
+static inline uint32_t round_down(uint32_t n)
+{
+ while (n & (n - 1))
+ n &= (n - 1);
+ return n;
+}
+
+/*
+ * Allocate room for a suitable hash table.
+ */
+static int init_hash_tables(struct dm_snapshot *s)
+{
+ sector_t hash_size, cow_dev_size, origin_dev_size, max_buckets;
+
+ /*
+ * Calculate based on the size of the original volume or
+ * the COW volume...
+ */
+ cow_dev_size = get_dev_size(s->cow->bdev);
+ origin_dev_size = get_dev_size(s->origin->bdev);
+ max_buckets = calc_max_buckets();
+
+ hash_size = min(origin_dev_size, cow_dev_size) >> s->chunk_shift;
+ hash_size = min(hash_size, max_buckets);
+
+ /* Round it down to a power of 2 */
+ hash_size = round_down(hash_size);
+ if (init_exception_table(&s->complete, hash_size))
+ return -ENOMEM;
+
+ /*
+ * Allocate hash table for in-flight exceptions
+ * Make this smaller than the real hash table
+ */
+ hash_size >>= 3;
+ if (hash_size < 64)
+ hash_size = 64;
+
+ if (init_exception_table(&s->pending, hash_size)) {
+ exit_exception_table(&s->complete, exception_cache);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/*
+ * Round a number up to the nearest 'size' boundary. size must
+ * be a power of 2.
+ */
+static inline ulong round_up(ulong n, ulong size)
+{
+ size--;
+ return (n + size) & ~size;
+}
+
+/*
+ * Construct a snapshot mapping: <origin_dev> <COW-dev> <p/n> <chunk-size>
+ */
+static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+ struct dm_snapshot *s;
+ unsigned long chunk_size;
+ int r = -EINVAL;
+ char persistent;
+ char *origin_path;
+ char *cow_path;
+ char *value;
+ int blocksize;
+
+ if (argc < 4) {
+ ti->error = "dm-snapshot: requires exactly 4 arguments";
+ r = -EINVAL;
+ goto bad1;
+ }
+
+ origin_path = argv[0];
+ cow_path = argv[1];
+ persistent = toupper(*argv[2]);
+
+ if (persistent != 'P' && persistent != 'N') {
+ ti->error = "Persistent flag is not P or N";
+ r = -EINVAL;
+ goto bad1;
+ }
+
+ chunk_size = simple_strtoul(argv[3], &value, 10);
+ if (chunk_size == 0 || value == NULL) {
+ ti->error = "Invalid chunk size";
+ r = -EINVAL;
+ goto bad1;
+ }
+
+ s = kmalloc(sizeof(*s), GFP_KERNEL);
+ if (s == NULL) {
+ ti->error = "Cannot allocate snapshot context private "
+ "structure";
+ r = -ENOMEM;
+ goto bad1;
+ }
+
+ r = dm_get_device(ti, origin_path, 0, ti->len, FMODE_READ, &s->origin);
+ if (r) {
+ ti->error = "Cannot get origin device";
+ goto bad2;
+ }
+
+ r = dm_get_device(ti, cow_path, 0, 0,
+ FMODE_READ | FMODE_WRITE, &s->cow);
+ if (r) {
+ dm_put_device(ti, s->origin);
+ ti->error = "Cannot get COW device";
+ goto bad2;
+ }
+
+ /*
+ * Chunk size must be multiple of page size. Silently
+ * round up if it's not.
+ */
+ chunk_size = round_up(chunk_size, PAGE_SIZE >> 9);
+
+ /* Validate the chunk size against the device block size */
+ blocksize = s->cow->bdev->bd_disk->queue->hardsect_size;
+ if (chunk_size % (blocksize >> 9)) {
+ ti->error = "Chunk size is not a multiple of device blocksize";
+ r = -EINVAL;
+ goto bad3;
+ }
+
+ /* Check chunk_size is a power of 2 */
+ if (chunk_size & (chunk_size - 1)) {
+ ti->error = "Chunk size is not a power of 2";
+ r = -EINVAL;
+ goto bad3;
+ }
+
+ s->chunk_size = chunk_size;
+ s->chunk_mask = chunk_size - 1;
+ s->type = persistent;
+ s->chunk_shift = ffs(chunk_size) - 1;
+
+ s->valid = 1;
+ s->have_metadata = 0;
+ s->last_percent = 0;
+ init_rwsem(&s->lock);
+ s->table = ti->table;
+
+ /* Allocate hash table for COW data */
+ if (init_hash_tables(s)) {
+ ti->error = "Unable to allocate hash table space";
+ r = -ENOMEM;
+ goto bad3;
+ }
+
+ /*
+ * Check the persistent flag - done here because we need the iobuf
+ * to check the LV header
+ */
+ s->store.snap = s;
+
+ if (persistent == 'P')
+ r = dm_create_persistent(&s->store, chunk_size);
+ else
+ r = dm_create_transient(&s->store, s, blocksize);
+
+ if (r) {
+ ti->error = "Couldn't create exception store";
+ r = -EINVAL;
+ goto bad4;
+ }
+
+ r = kcopyd_client_create(SNAPSHOT_PAGES, &s->kcopyd_client);
+ if (r) {
+ ti->error = "Could not create kcopyd client";
+ goto bad5;
+ }
+
+ /* Add snapshot to the list of snapshots for this origin */
+ if (register_snapshot(s)) {
+ r = -EINVAL;
+ ti->error = "Cannot register snapshot origin";
+ goto bad6;
+ }
+
+ ti->private = s;
+ ti->split_io = chunk_size;
+
+ return 0;
+
+ bad6:
+ kcopyd_client_destroy(s->kcopyd_client);
+
+ bad5:
+ s->store.destroy(&s->store);
+
+ bad4:
+ exit_exception_table(&s->pending, pending_cache);
+ exit_exception_table(&s->complete, exception_cache);
+
+ bad3:
+ dm_put_device(ti, s->cow);
+ dm_put_device(ti, s->origin);
+
+ bad2:
+ kfree(s);
+
+ bad1:
+ return r;
+}
+
+static void snapshot_dtr(struct dm_target *ti)
+{
+ struct dm_snapshot *s = (struct dm_snapshot *) ti->private;
+
+ unregister_snapshot(s);
+
+ exit_exception_table(&s->pending, pending_cache);
+ exit_exception_table(&s->complete, exception_cache);
+
+ /* Deallocate memory used */
+ s->store.destroy(&s->store);
+
+ dm_put_device(ti, s->origin);
+ dm_put_device(ti, s->cow);
+ kcopyd_client_destroy(s->kcopyd_client);
+ kfree(s);
+}
+
+/*
+ * Flush a list of buffers.
+ */
+static void flush_bios(struct bio *bio)
+{
+ struct bio *n;
+
+ while (bio) {
+ n = bio->bi_next;
+ bio->bi_next = NULL;
+ generic_make_request(bio);
+ bio = n;
+ }
+}
+
+/*
+ * Error a list of buffers.
+ */
+static void error_bios(struct bio *bio)
+{
+ struct bio *n;
+
+ while (bio) {
+ n = bio->bi_next;
+ bio->bi_next = NULL;
+ bio_io_error(bio, bio->bi_size);
+ bio = n;
+ }
+}
+
+static struct bio *__flush_bios(struct pending_exception *pe)
+{
+ struct pending_exception *sibling;
+
+ if (list_empty(&pe->siblings))
+ return bio_list_get(&pe->origin_bios);
+
+ sibling = list_entry(pe->siblings.next,
+ struct pending_exception, siblings);
+
+ list_del(&pe->siblings);
+
+ /* This is fine as long as kcopyd is single-threaded. If kcopyd
+ * becomes multi-threaded, we'll need some locking here.
+ */
+ bio_list_merge(&sibling->origin_bios, &pe->origin_bios);
+
+ return NULL;
+}
+
+static void pending_complete(struct pending_exception *pe, int success)
+{
+ struct exception *e;
+ struct dm_snapshot *s = pe->snap;
+ struct bio *flush = NULL;
+
+ if (success) {
+ e = alloc_exception();
+ if (!e) {
+ DMWARN("Unable to allocate exception.");
+ down_write(&s->lock);
+ s->store.drop_snapshot(&s->store);
+ s->valid = 0;
+ flush = __flush_bios(pe);
+ up_write(&s->lock);
+
+ error_bios(bio_list_get(&pe->snapshot_bios));
+ goto out;
+ }
+ *e = pe->e;
+
+ /*
+ * Add a proper exception, and remove the
+ * in-flight exception from the list.
+ */
+ down_write(&s->lock);
+ insert_exception(&s->complete, e);
+ remove_exception(&pe->e);
+ flush = __flush_bios(pe);
+
+ /* Submit any pending write bios */
+ up_write(&s->lock);
+
+ flush_bios(bio_list_get(&pe->snapshot_bios));
+ } else {
+ /* Read/write error - snapshot is unusable */
+ down_write(&s->lock);
+ if (s->valid)
+ DMERR("Error reading/writing snapshot");
+ s->store.drop_snapshot(&s->store);
+ s->valid = 0;
+ remove_exception(&pe->e);
+ flush = __flush_bios(pe);
+ up_write(&s->lock);
+
+ error_bios(bio_list_get(&pe->snapshot_bios));
+
+ dm_table_event(s->table);
+ }
+
+ out:
+ free_pending_exception(pe);
+
+ if (flush)
+ flush_bios(flush);
+}
+
+static void commit_callback(void *context, int success)
+{
+ struct pending_exception *pe = (struct pending_exception *) context;
+ pending_complete(pe, success);
+}
+
+/*
+ * Called when the copy I/O has finished. kcopyd actually runs
+ * this code so don't block.
+ */
+static void copy_callback(int read_err, unsigned int write_err, void *context)
+{
+ struct pending_exception *pe = (struct pending_exception *) context;
+ struct dm_snapshot *s = pe->snap;
+
+ if (read_err || write_err)
+ pending_complete(pe, 0);
+
+ else
+ /* Update the metadata if we are persistent */
+ s->store.commit_exception(&s->store, &pe->e, commit_callback,
+ pe);
+}
+
+/*
+ * Dispatches the copy operation to kcopyd.
+ */
+static inline void start_copy(struct pending_exception *pe)
+{
+ struct dm_snapshot *s = pe->snap;
+ struct io_region src, dest;
+ struct block_device *bdev = s->origin->bdev;
+ sector_t dev_size;
+
+ dev_size = get_dev_size(bdev);
+
+ src.bdev = bdev;
+ src.sector = chunk_to_sector(s, pe->e.old_chunk);
+ src.count = min(s->chunk_size, dev_size - src.sector);
+
+ dest.bdev = s->cow->bdev;
+ dest.sector = chunk_to_sector(s, pe->e.new_chunk);
+ dest.count = src.count;
+
+ /* Hand over to kcopyd */
+ kcopyd_copy(s->kcopyd_client,
+ &src, 1, &dest, 0, copy_callback, pe);
+}
+
+/*
+ * Looks to see if this snapshot already has a pending exception
+ * for this chunk, otherwise it allocates a new one and inserts
+ * it into the pending table.
+ *
+ * NOTE: a write lock must be held on snap->lock before calling
+ * this.
+ */
+static struct pending_exception *
+__find_pending_exception(struct dm_snapshot *s, struct bio *bio)
+{
+ struct exception *e;
+ struct pending_exception *pe;
+ chunk_t chunk = sector_to_chunk(s, bio->bi_sector);
+
+ /*
+ * Is there a pending exception for this already ?
+ */
+ e = lookup_exception(&s->pending, chunk);
+ if (e) {
+ /* cast the exception to a pending exception */
+ pe = container_of(e, struct pending_exception, e);
+
+ } else {
+ /*
+ * Create a new pending exception, we don't want
+ * to hold the lock while we do this.
+ */
+ up_write(&s->lock);
+ pe = alloc_pending_exception();
+ down_write(&s->lock);
+
+ e = lookup_exception(&s->pending, chunk);
+ if (e) {
+ free_pending_exception(pe);
+ pe = container_of(e, struct pending_exception, e);
+ } else {
+ pe->e.old_chunk = chunk;
+ bio_list_init(&pe->origin_bios);
+ bio_list_init(&pe->snapshot_bios);
+ INIT_LIST_HEAD(&pe->siblings);
+ pe->snap = s;
+ pe->started = 0;
+
+ if (s->store.prepare_exception(&s->store, &pe->e)) {
+ free_pending_exception(pe);
+ s->valid = 0;
+ return NULL;
+ }
+
+ insert_exception(&s->pending, &pe->e);
+ }
+ }
+
+ return pe;
+}
+
+static inline void remap_exception(struct dm_snapshot *s, struct exception *e,
+ struct bio *bio)
+{
+ bio->bi_bdev = s->cow->bdev;
+ bio->bi_sector = chunk_to_sector(s, e->new_chunk) +
+ (bio->bi_sector & s->chunk_mask);
+}
+
+static int snapshot_map(struct dm_target *ti, struct bio *bio,
+ union map_info *map_context)
+{
+ struct exception *e;
+ struct dm_snapshot *s = (struct dm_snapshot *) ti->private;
+ int r = 1;
+ chunk_t chunk;
+ struct pending_exception *pe;
+
+ chunk = sector_to_chunk(s, bio->bi_sector);
+
+ /* Full snapshots are not usable */
+ if (!s->valid)
+ return -1;
+
+ /*
+ * Write to snapshot - higher level takes care of RW/RO
+ * flags so we should only get this if we are
+ * writeable.
+ */
+ if (bio_rw(bio) == WRITE) {
+
+ /* FIXME: should only take write lock if we need
+ * to copy an exception */
+ down_write(&s->lock);
+
+ /* If the block is already remapped - use that, else remap it */
+ e = lookup_exception(&s->complete, chunk);
+ if (e) {
+ remap_exception(s, e, bio);
+ up_write(&s->lock);
+
+ } else {
+ pe = __find_pending_exception(s, bio);
+
+ if (!pe) {
+ if (s->store.drop_snapshot)
+ s->store.drop_snapshot(&s->store);
+ s->valid = 0;
+ r = -EIO;
+ up_write(&s->lock);
+ } else {
+ remap_exception(s, &pe->e, bio);
+ bio_list_add(&pe->snapshot_bios, bio);
+
+ if (!pe->started) {
+ /* this is protected by snap->lock */
+ pe->started = 1;
+ up_write(&s->lock);
+ start_copy(pe);
+ } else
+ up_write(&s->lock);
+ r = 0;
+ }
+ }
+
+ } else {
+ /*
+ * FIXME: this read path scares me because we
+ * always use the origin when we have a pending
+ * exception. However I can't think of a
+ * situation where this is wrong - ejt.
+ */
+
+ /* Do reads */
+ down_read(&s->lock);
+
+ /* See if it it has been remapped */
+ e = lookup_exception(&s->complete, chunk);
+ if (e)
+ remap_exception(s, e, bio);
+ else
+ bio->bi_bdev = s->origin->bdev;
+
+ up_read(&s->lock);
+ }
+
+ return r;
+}
+
+static void snapshot_resume(struct dm_target *ti)
+{
+ struct dm_snapshot *s = (struct dm_snapshot *) ti->private;
+
+ if (s->have_metadata)
+ return;
+
+ if (s->store.read_metadata(&s->store)) {
+ down_write(&s->lock);
+ s->valid = 0;
+ up_write(&s->lock);
+ }
+
+ s->have_metadata = 1;
+}
+
+static int snapshot_status(struct dm_target *ti, status_type_t type,
+ char *result, unsigned int maxlen)
+{
+ struct dm_snapshot *snap = (struct dm_snapshot *) ti->private;
+ char cow[32];
+ char org[32];
+
+ switch (type) {
+ case STATUSTYPE_INFO:
+ if (!snap->valid)
+ snprintf(result, maxlen, "Invalid");
+ else {
+ if (snap->store.fraction_full) {
+ sector_t numerator, denominator;
+ snap->store.fraction_full(&snap->store,
+ &numerator,
+ &denominator);
+ snprintf(result, maxlen,
+ SECTOR_FORMAT "/" SECTOR_FORMAT,
+ numerator, denominator);
+ }
+ else
+ snprintf(result, maxlen, "Unknown");
+ }
+ break;
+
+ case STATUSTYPE_TABLE:
+ /*
+ * kdevname returns a static pointer so we need
+ * to make private copies if the output is to
+ * make sense.
+ */
+ format_dev_t(cow, snap->cow->bdev->bd_dev);
+ format_dev_t(org, snap->origin->bdev->bd_dev);
+ snprintf(result, maxlen, "%s %s %c " SECTOR_FORMAT, org, cow,
+ snap->type, snap->chunk_size);
+ break;
+ }
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------
+ * Origin methods
+ *---------------------------------------------------------------*/
+static void list_merge(struct list_head *l1, struct list_head *l2)
+{
+ struct list_head *l1_n, *l2_p;
+
+ l1_n = l1->next;
+ l2_p = l2->prev;
+
+ l1->next = l2;
+ l2->prev = l1;
+
+ l2_p->next = l1_n;
+ l1_n->prev = l2_p;
+}
+
+static int __origin_write(struct list_head *snapshots, struct bio *bio)
+{
+ int r = 1, first = 1;
+ struct dm_snapshot *snap;
+ struct exception *e;
+ struct pending_exception *pe, *last = NULL;
+ chunk_t chunk;
+
+ /* Do all the snapshots on this origin */
+ list_for_each_entry (snap, snapshots, list) {
+
+ /* Only deal with valid snapshots */
+ if (!snap->valid)
+ continue;
+
+ down_write(&snap->lock);
+
+ /*
+ * Remember, different snapshots can have
+ * different chunk sizes.
+ */
+ chunk = sector_to_chunk(snap, bio->bi_sector);
+
+ /*
+ * Check exception table to see if block
+ * is already remapped in this snapshot
+ * and trigger an exception if not.
+ */
+ e = lookup_exception(&snap->complete, chunk);
+ if (!e) {
+ pe = __find_pending_exception(snap, bio);
+ if (!pe) {
+ snap->store.drop_snapshot(&snap->store);
+ snap->valid = 0;
+
+ } else {
+ if (last)
+ list_merge(&pe->siblings,
+ &last->siblings);
+
+ last = pe;
+ r = 0;
+ }
+ }
+
+ up_write(&snap->lock);
+ }
+
+ /*
+ * Now that we have a complete pe list we can start the copying.
+ */
+ if (last) {
+ pe = last;
+ do {
+ down_write(&pe->snap->lock);
+ if (first)
+ bio_list_add(&pe->origin_bios, bio);
+ if (!pe->started) {
+ pe->started = 1;
+ up_write(&pe->snap->lock);
+ start_copy(pe);
+ } else
+ up_write(&pe->snap->lock);
+ first = 0;
+ pe = list_entry(pe->siblings.next,
+ struct pending_exception, siblings);
+
+ } while (pe != last);
+ }
+
+ return r;
+}
+
+/*
+ * Called on a write from the origin driver.
+ */
+static int do_origin(struct dm_dev *origin, struct bio *bio)
+{
+ struct origin *o;
+ int r = 1;
+
+ down_read(&_origins_lock);
+ o = __lookup_origin(origin->bdev);
+ if (o)
+ r = __origin_write(&o->snapshots, bio);
+ up_read(&_origins_lock);
+
+ return r;
+}
+
+/*
+ * Origin: maps a linear range of a device, with hooks for snapshotting.
+ */
+
+/*
+ * Construct an origin mapping: <dev_path>
+ * The context for an origin is merely a 'struct dm_dev *'
+ * pointing to the real device.
+ */
+static int origin_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+ int r;
+ struct dm_dev *dev;
+
+ if (argc != 1) {
+ ti->error = "dm-origin: incorrect number of arguments";
+ return -EINVAL;
+ }
+
+ r = dm_get_device(ti, argv[0], 0, ti->len,
+ dm_table_get_mode(ti->table), &dev);
+ if (r) {
+ ti->error = "Cannot get target device";
+ return r;
+ }
+
+ ti->private = dev;
+ return 0;
+}
+
+static void origin_dtr(struct dm_target *ti)
+{
+ struct dm_dev *dev = (struct dm_dev *) ti->private;
+ dm_put_device(ti, dev);
+}
+
+static int origin_map(struct dm_target *ti, struct bio *bio,
+ union map_info *map_context)
+{
+ struct dm_dev *dev = (struct dm_dev *) ti->private;
+ bio->bi_bdev = dev->bdev;
+
+ /* Only tell snapshots if this is a write */
+ return (bio_rw(bio) == WRITE) ? do_origin(dev, bio) : 1;
+}
+
+#define min_not_zero(l, r) (l == 0) ? r : ((r == 0) ? l : min(l, r))
+
+/*
+ * Set the target "split_io" field to the minimum of all the snapshots'
+ * chunk sizes.
+ */
+static void origin_resume(struct dm_target *ti)
+{
+ struct dm_dev *dev = (struct dm_dev *) ti->private;
+ struct dm_snapshot *snap;
+ struct origin *o;
+ chunk_t chunk_size = 0;
+
+ down_read(&_origins_lock);
+ o = __lookup_origin(dev->bdev);
+ if (o)
+ list_for_each_entry (snap, &o->snapshots, list)
+ chunk_size = min_not_zero(chunk_size, snap->chunk_size);
+ up_read(&_origins_lock);
+
+ ti->split_io = chunk_size;
+}
+
+static int origin_status(struct dm_target *ti, status_type_t type, char *result,
+ unsigned int maxlen)
+{
+ struct dm_dev *dev = (struct dm_dev *) ti->private;
+ char buffer[32];
+
+ switch (type) {
+ case STATUSTYPE_INFO:
+ result[0] = '\0';
+ break;
+
+ case STATUSTYPE_TABLE:
+ format_dev_t(buffer, dev->bdev->bd_dev);
+ snprintf(result, maxlen, "%s", buffer);
+ break;
+ }
+
+ return 0;
+}
+
+static struct target_type origin_target = {
+ .name = "snapshot-origin",
+ .version = {1, 0, 1},
+ .module = THIS_MODULE,
+ .ctr = origin_ctr,
+ .dtr = origin_dtr,
+ .map = origin_map,
+ .resume = origin_resume,
+ .status = origin_status,
+};
+
+static struct target_type snapshot_target = {
+ .name = "snapshot",
+ .version = {1, 0, 1},
+ .module = THIS_MODULE,
+ .ctr = snapshot_ctr,
+ .dtr = snapshot_dtr,
+ .map = snapshot_map,
+ .resume = snapshot_resume,
+ .status = snapshot_status,
+};
+
+static int __init dm_snapshot_init(void)
+{
+ int r;
+
+ r = dm_register_target(&snapshot_target);
+ if (r) {
+ DMERR("snapshot target register failed %d", r);
+ return r;
+ }
+
+ r = dm_register_target(&origin_target);
+ if (r < 0) {
+ DMERR("Device mapper: Origin: register failed %d\n", r);
+ goto bad1;
+ }
+
+ r = init_origin_hash();
+ if (r) {
+ DMERR("init_origin_hash failed.");
+ goto bad2;
+ }
+
+ exception_cache = kmem_cache_create("dm-snapshot-ex",
+ sizeof(struct exception),
+ __alignof__(struct exception),
+ 0, NULL, NULL);
+ if (!exception_cache) {
+ DMERR("Couldn't create exception cache.");
+ r = -ENOMEM;
+ goto bad3;
+ }
+
+ pending_cache =
+ kmem_cache_create("dm-snapshot-in",
+ sizeof(struct pending_exception),
+ __alignof__(struct pending_exception),
+ 0, NULL, NULL);
+ if (!pending_cache) {
+ DMERR("Couldn't create pending cache.");
+ r = -ENOMEM;
+ goto bad4;
+ }
+
+ pending_pool = mempool_create(128, mempool_alloc_slab,
+ mempool_free_slab, pending_cache);
+ if (!pending_pool) {
+ DMERR("Couldn't create pending pool.");
+ r = -ENOMEM;
+ goto bad5;
+ }
+
+ return 0;
+
+ bad5:
+ kmem_cache_destroy(pending_cache);
+ bad4:
+ kmem_cache_destroy(exception_cache);
+ bad3:
+ exit_origin_hash();
+ bad2:
+ dm_unregister_target(&origin_target);
+ bad1:
+ dm_unregister_target(&snapshot_target);
+ return r;
+}
+
+static void __exit dm_snapshot_exit(void)
+{
+ int r;
+
+ r = dm_unregister_target(&snapshot_target);
+ if (r)
+ DMERR("snapshot unregister failed %d", r);
+
+ r = dm_unregister_target(&origin_target);
+ if (r)
+ DMERR("origin unregister failed %d", r);
+
+ exit_origin_hash();
+ mempool_destroy(pending_pool);
+ kmem_cache_destroy(pending_cache);
+ kmem_cache_destroy(exception_cache);
+}
+
+/* Module hooks */
+module_init(dm_snapshot_init);
+module_exit(dm_snapshot_exit);
+
+MODULE_DESCRIPTION(DM_NAME " snapshot target");
+MODULE_AUTHOR("Joe Thornber");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * dm-snapshot.c
+ *
+ * Copyright (C) 2001-2002 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef DM_SNAPSHOT_H
+#define DM_SNAPSHOT_H
+
+#include "dm.h"
+#include <linux/blkdev.h>
+
+struct exception_table {
+ uint32_t hash_mask;
+ struct list_head *table;
+};
+
+/*
+ * The snapshot code deals with largish chunks of the disk at a
+ * time. Typically 64k - 256k.
+ */
+/* FIXME: can we get away with limiting these to a uint32_t ? */
+typedef sector_t chunk_t;
+
+/*
+ * An exception is used where an old chunk of data has been
+ * replaced by a new one.
+ */
+struct exception {
+ struct list_head hash_list;
+
+ chunk_t old_chunk;
+ chunk_t new_chunk;
+};
+
+/*
+ * Abstraction to handle the meta/layout of exception stores (the
+ * COW device).
+ */
+struct exception_store {
+
+ /*
+ * Destroys this object when you've finished with it.
+ */
+ void (*destroy) (struct exception_store *store);
+
+ /*
+ * The target shouldn't read the COW device until this is
+ * called.
+ */
+ int (*read_metadata) (struct exception_store *store);
+
+ /*
+ * Find somewhere to store the next exception.
+ */
+ int (*prepare_exception) (struct exception_store *store,
+ struct exception *e);
+
+ /*
+ * Update the metadata with this exception.
+ */
+ void (*commit_exception) (struct exception_store *store,
+ struct exception *e,
+ void (*callback) (void *, int success),
+ void *callback_context);
+
+ /*
+ * The snapshot is invalid, note this in the metadata.
+ */
+ void (*drop_snapshot) (struct exception_store *store);
+
+ /*
+ * Return how full the snapshot is.
+ */
+ void (*fraction_full) (struct exception_store *store,
+ sector_t *numerator,
+ sector_t *denominator);
+
+ struct dm_snapshot *snap;
+ void *context;
+};
+
+struct dm_snapshot {
+ struct rw_semaphore lock;
+ struct dm_table *table;
+
+ struct dm_dev *origin;
+ struct dm_dev *cow;
+
+ /* List of snapshots per Origin */
+ struct list_head list;
+
+ /* Size of data blocks saved - must be a power of 2 */
+ chunk_t chunk_size;
+ chunk_t chunk_mask;
+ chunk_t chunk_shift;
+
+ /* You can't use a snapshot if this is 0 (e.g. if full) */
+ int valid;
+ int have_metadata;
+
+ /* Used for display of table */
+ char type;
+
+ /* The last percentage we notified */
+ int last_percent;
+
+ struct exception_table pending;
+ struct exception_table complete;
+
+ /* The on disk metadata handler */
+ struct exception_store store;
+
+ struct kcopyd_client *kcopyd_client;
+};
+
+/*
+ * Used by the exception stores to load exceptions hen
+ * initialising.
+ */
+int dm_add_exception(struct dm_snapshot *s, chunk_t old, chunk_t new);
+
+/*
+ * Constructor and destructor for the default persistent
+ * store.
+ */
+int dm_create_persistent(struct exception_store *store, uint32_t chunk_size);
+
+int dm_create_transient(struct exception_store *store,
+ struct dm_snapshot *s, int blocksize);
+
+/*
+ * Return the number of sectors in the device.
+ */
+static inline sector_t get_dev_size(struct block_device *bdev)
+{
+ return bdev->bd_inode->i_size >> SECTOR_SHIFT;
+}
+
+static inline chunk_t sector_to_chunk(struct dm_snapshot *s, sector_t sector)
+{
+ return (sector & ~s->chunk_mask) >> s->chunk_shift;
+}
+
+static inline sector_t chunk_to_sector(struct dm_snapshot *s, chunk_t chunk)
+{
+ return chunk << s->chunk_shift;
+}
+
+static inline int bdev_equal(struct block_device *lhs, struct block_device *rhs)
+{
+ /*
+ * There is only ever one instance of a particular block
+ * device so we can compare pointers safely.
+ */
+ return lhs == rhs;
+}
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2003 Christophe Saout <christophe@saout.de>
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm.h"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bio.h>
+
+/*
+ * Construct a dummy mapping that only returns zeros
+ */
+static int zero_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+ if (argc != 0) {
+ ti->error = "dm-zero: No arguments required";
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Fills the bio pages with zeros
+ */
+static void zero_fill_bio(struct bio *bio)
+{
+ unsigned long flags;
+ struct bio_vec *bv;
+ int i;
+
+ bio_for_each_segment(bv, bio, i) {
+ char *data = bvec_kmap_irq(bv, &flags);
+ memset(data, 0, bv->bv_len);
+ flush_dcache_page(bv->bv_page);
+ bvec_kunmap_irq(data, &flags);
+ }
+}
+
+/*
+ * Return zeros only on reads
+ */
+static int zero_map(struct dm_target *ti, struct bio *bio,
+ union map_info *map_context)
+{
+ switch(bio_rw(bio)) {
+ case READ:
+ zero_fill_bio(bio);
+ break;
+ case READA:
+ /* readahead of null bytes only wastes buffer cache */
+ return -EIO;
+ case WRITE:
+ /* writes get silently dropped */
+ break;
+ }
+
+ bio_endio(bio, bio->bi_size, 0);
+
+ /* accepted bio, don't make new request */
+ return 0;
+}
+
+static struct target_type zero_target = {
+ .name = "zero",
+ .version = {1, 0, 0},
+ .module = THIS_MODULE,
+ .ctr = zero_ctr,
+ .map = zero_map,
+};
+
+int __init dm_zero_init(void)
+{
+ int r = dm_register_target(&zero_target);
+
+ if (r < 0)
+ DMERR("zero: register failed %d", r);
+
+ return r;
+}
+
+void __exit dm_zero_exit(void)
+{
+ int r = dm_unregister_target(&zero_target);
+
+ if (r < 0)
+ DMERR("zero: unregister failed %d", r);
+}
+
+module_init(dm_zero_init)
+module_exit(dm_zero_exit)
+
+MODULE_AUTHOR("Christophe Saout <christophe@saout.de>");
+MODULE_DESCRIPTION(DM_NAME " dummy target returning zeros");
+MODULE_LICENSE("GPL");
#include "kcopyd.h"
-/* FIXME: this is only needed for the DMERR macros */
-#include "dm.h"
-
static struct workqueue_struct *_kcopyd_wq;
static struct work_struct _kcopyd_work;
;
kc->pages = pl->next;
- pl->next = 0;
+ pl->next = NULL;
spin_unlock(&kc->lock);
static DECLARE_MUTEX(_client_lock);
static LIST_HEAD(_clients);
-static int client_add(struct kcopyd_client *kc)
+static void client_add(struct kcopyd_client *kc)
{
down(&_client_lock);
list_add(&kc->list, &_clients);
up(&_client_lock);
- return 0;
}
static void client_del(struct kcopyd_client *kc)
return r;
}
- r = client_add(kc);
- if (r) {
- dm_io_put(nr_pages);
- client_free_pages(kc);
- kfree(kc);
- kcopyd_exit();
- return r;
- }
-
+ client_add(kc);
*result = kc;
return 0;
}
config DVB_B2C2_SKYSTAR
tristate "Technisat Skystar2 PCI"
- depends on DVB_CORE
+ depends on DVB_CORE && PCI
help
Support for the Skystar2 PCI DVB card by Technisat, which
is equipped with the FlexCopII chipset by B2C2.
#include <linux/list.h>
#include <linux/devfs_fs_kernel.h>
-#define DVB_MAJOR 250
+#define DVB_MAJOR 212
#define DVB_DEVICE_VIDEO 0
#define DVB_DEVICE_AUDIO 1
*/
-
-#define __KERNEL_SYSCALLS__
#include <linux/module.h>
#include <linux/init.h>
#include <linux/vmalloc.h>
loff_t filesize;
char *dp;
- fd = open(fn, 0, 0);
+ fd = sys_open(fn, 0, 0);
if (fd == -1) {
printk("%s: unable to open '%s'.\n", __FUNCTION__, fn);
return -EIO;
}
- filesize = lseek(fd, 0L, 2);
+ filesize = sys_lseek(fd, 0L, 2);
if (filesize <= 0 || filesize < SP8870_FIRMWARE_OFFSET + SP8870_FIRMWARE_SIZE) {
printk("%s: firmware filesize to small '%s'\n", __FUNCTION__, fn);
sys_close(fd);
return -EIO;
}
- lseek(fd, SP8870_FIRMWARE_OFFSET, 0);
- if (read(fd, dp, SP8870_FIRMWARE_SIZE) != SP8870_FIRMWARE_SIZE) {
+ sys_lseek(fd, SP8870_FIRMWARE_OFFSET, 0);
+ if (sys_read(fd, dp, SP8870_FIRMWARE_SIZE) != SP8870_FIRMWARE_SIZE) {
printk("%s: failed to read '%s'.\n",__FUNCTION__, fn);
vfree(dp);
sys_close(fd);
next 0x4000 loaded. This may change in future versions.
*/
-#define __KERNEL_SYSCALLS__
#include <linux/kernel.h>
#include <linux/vmalloc.h>
#include <linux/module.h>
// Load the firmware
set_fs(get_ds());
- fd = open(sp887x_firmware, 0, 0);
+ fd = sys_open(sp887x_firmware, 0, 0);
if (fd < 0) {
printk(KERN_WARNING "%s: Unable to open firmware %s\n", __FUNCTION__,
sp887x_firmware);
return -EIO;
}
- filesize = lseek(fd, 0L, 2);
+ filesize = sys_lseek(fd, 0L, 2);
if (filesize <= 0) {
printk(KERN_WARNING "%s: Firmware %s is empty\n", __FUNCTION__,
sp887x_firmware);
// read it!
// read the first 16384 bytes from the file
// ignore the first 10 bytes
- lseek(fd, 10, 0);
- if (read(fd, firmware, fw_size) != fw_size) {
+ sys_lseek(fd, 10, 0);
+ if (sys_read(fd, firmware, fw_size) != fw_size) {
printk(KERN_WARNING "%s: Failed to read firmware\n", __FUNCTION__);
vfree(firmware);
sys_close(fd);
config RADIO_MAXIRADIO
tristate "Guillemot MAXI Radio FM 2000 radio"
- depends on VIDEO_DEV
+ depends on VIDEO_DEV && PCI
---help---
Choose Y here if you have this radio card. This card may also be
found as Gemtek PCI FM.
--- /dev/null
+ovcamchip-objs := ovcamchip_core.o ov6x20.o ov6x30.o ov7x10.o ov7x20.o \
+ ov76be.o
+
+obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip.o
--- /dev/null
+/* Shared Code for OmniVision Camera Chip Drivers
+ *
+ * Copyright (c) 2004 Mark McClelland <mark@alpha.dyndns.org>
+ * http://alpha.dyndns.org/ov511/
+ *
+ * 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. NO WARRANTY OF ANY KIND is expressed or implied.
+ */
+
+#define DEBUG
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include "ovcamchip_priv.h"
+
+#define DRIVER_VERSION "v2.27 for Linux 2.6"
+#define DRIVER_AUTHOR "Mark McClelland <mark@alpha.dyndns.org>"
+#define DRIVER_DESC "OV camera chip I2C driver"
+
+#define PINFO(fmt, args...) printk(KERN_INFO "ovcamchip: " fmt "\n" , ## args);
+#define PERROR(fmt, args...) printk(KERN_ERR "ovcamchip: " fmt "\n" , ## args);
+
+#ifdef DEBUG
+int ovcamchip_debug = 0;
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug,
+ "Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=all");
+#endif
+
+/* By default, let bridge driver tell us if chip is monochrome. mono=0
+ * will ignore that and always treat chips as color. mono=1 will force
+ * monochrome mode for all chips. */
+static int mono = -1;
+module_param(mono, int, 0);
+MODULE_PARM_DESC(mono,
+ "1=chips are monochrome (OVx1xx), 0=force color, -1=autodetect (default)");
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/* Registers common to all chips, that are needed for detection */
+#define GENERIC_REG_ID_HIGH 0x1C /* manufacturer ID MSB */
+#define GENERIC_REG_ID_LOW 0x1D /* manufacturer ID LSB */
+#define GENERIC_REG_COM_I 0x29 /* misc ID bits */
+
+extern struct ovcamchip_ops ov6x20_ops;
+extern struct ovcamchip_ops ov6x30_ops;
+extern struct ovcamchip_ops ov7x10_ops;
+extern struct ovcamchip_ops ov7x20_ops;
+extern struct ovcamchip_ops ov76be_ops;
+
+static char *chip_names[NUM_CC_TYPES] = {
+ [CC_UNKNOWN] = "Unknown chip",
+ [CC_OV76BE] = "OV76BE",
+ [CC_OV7610] = "OV7610",
+ [CC_OV7620] = "OV7620",
+ [CC_OV7620AE] = "OV7620AE",
+ [CC_OV6620] = "OV6620",
+ [CC_OV6630] = "OV6630",
+ [CC_OV6630AE] = "OV6630AE",
+ [CC_OV6630AF] = "OV6630AF",
+};
+
+/* Forward declarations */
+static struct i2c_driver driver;
+static struct i2c_client client_template;
+
+/* ----------------------------------------------------------------------- */
+
+int ov_write_regvals(struct i2c_client *c, struct ovcamchip_regvals *rvals)
+{
+ int rc;
+
+ while (rvals->reg != 0xff) {
+ rc = ov_write(c, rvals->reg, rvals->val);
+ if (rc < 0)
+ return rc;
+ rvals++;
+ }
+
+ return 0;
+}
+
+/* Writes bits at positions specified by mask to an I2C reg. Bits that are in
+ * the same position as 1's in "mask" are cleared and set to "value". Bits
+ * that are in the same position as 0's in "mask" are preserved, regardless
+ * of their respective state in "value".
+ */
+int ov_write_mask(struct i2c_client *c,
+ unsigned char reg,
+ unsigned char value,
+ unsigned char mask)
+{
+ int rc;
+ unsigned char oldval, newval;
+
+ if (mask == 0xff) {
+ newval = value;
+ } else {
+ rc = ov_read(c, reg, &oldval);
+ if (rc < 0)
+ return rc;
+
+ oldval &= (~mask); /* Clear the masked bits */
+ value &= mask; /* Enforce mask on value */
+ newval = oldval | value; /* Set the desired bits */
+ }
+
+ return ov_write(c, reg, newval);
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* Reset the chip and ensure that I2C is synchronized. Returns <0 if failure.
+ */
+static int init_camchip(struct i2c_client *c)
+{
+ int i, success;
+ unsigned char high, low;
+
+ /* Reset the chip */
+ ov_write(c, 0x12, 0x80);
+
+ /* Wait for it to initialize */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1 + 150 * HZ / 1000);
+
+ for (i = 0, success = 0; i < I2C_DETECT_RETRIES && !success; i++) {
+ if (ov_read(c, GENERIC_REG_ID_HIGH, &high) >= 0) {
+ if (ov_read(c, GENERIC_REG_ID_LOW, &low) >= 0) {
+ if (high == 0x7F && low == 0xA2) {
+ success = 1;
+ continue;
+ }
+ }
+ }
+
+ /* Reset the chip */
+ ov_write(c, 0x12, 0x80);
+
+ /* Wait for it to initialize */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1 + 150 * HZ / 1000);
+
+ /* Dummy read to sync I2C */
+ ov_read(c, 0x00, &low);
+ }
+
+ if (!success)
+ return -EIO;
+
+ PDEBUG(1, "I2C synced in %d attempt(s)", i);
+
+ return 0;
+}
+
+/* This detects the OV7610, OV7620, or OV76BE chip. */
+static int ov7xx0_detect(struct i2c_client *c)
+{
+ struct ovcamchip *ov = i2c_get_clientdata(c);
+ int rc;
+ unsigned char val;
+
+ PDEBUG(4, "");
+
+ /* Detect chip (sub)type */
+ rc = ov_read(c, GENERIC_REG_COM_I, &val);
+ if (rc < 0) {
+ PERROR("Error detecting ov7xx0 type");
+ return rc;
+ }
+
+ if ((val & 3) == 3) {
+ PINFO("Camera chip is an OV7610");
+ ov->subtype = CC_OV7610;
+ } else if ((val & 3) == 1) {
+ rc = ov_read(c, 0x15, &val);
+ if (rc < 0) {
+ PERROR("Error detecting ov7xx0 type");
+ return rc;
+ }
+
+ if (val & 1) {
+ PINFO("Camera chip is an OV7620AE");
+ /* OV7620 is a close enough match for now. There are
+ * some definite differences though, so this should be
+ * fixed */
+ ov->subtype = CC_OV7620;
+ } else {
+ PINFO("Camera chip is an OV76BE");
+ ov->subtype = CC_OV76BE;
+ }
+ } else if ((val & 3) == 0) {
+ PINFO("Camera chip is an OV7620");
+ ov->subtype = CC_OV7620;
+ } else {
+ PERROR("Unknown camera chip version: %d", val & 3);
+ return -ENOSYS;
+ }
+
+ if (ov->subtype == CC_OV76BE)
+ ov->sops = &ov76be_ops;
+ else if (ov->subtype == CC_OV7620)
+ ov->sops = &ov7x20_ops;
+ else
+ ov->sops = &ov7x10_ops;
+
+ return 0;
+}
+
+/* This detects the OV6620, OV6630, OV6630AE, or OV6630AF chip. */
+static int ov6xx0_detect(struct i2c_client *c)
+{
+ struct ovcamchip *ov = i2c_get_clientdata(c);
+ int rc;
+ unsigned char val;
+
+ PDEBUG(4, "");
+
+ /* Detect chip (sub)type */
+ rc = ov_read(c, GENERIC_REG_COM_I, &val);
+ if (rc < 0) {
+ PERROR("Error detecting ov6xx0 type");
+ return -1;
+ }
+
+ if ((val & 3) == 0) {
+ ov->subtype = CC_OV6630;
+ PINFO("Camera chip is an OV6630");
+ } else if ((val & 3) == 1) {
+ ov->subtype = CC_OV6620;
+ PINFO("Camera chip is an OV6620");
+ } else if ((val & 3) == 2) {
+ ov->subtype = CC_OV6630;
+ PINFO("Camera chip is an OV6630AE");
+ } else if ((val & 3) == 3) {
+ ov->subtype = CC_OV6630;
+ PINFO("Camera chip is an OV6630AF");
+ }
+
+ if (ov->subtype == CC_OV6620)
+ ov->sops = &ov6x20_ops;
+ else
+ ov->sops = &ov6x30_ops;
+
+ return 0;
+}
+
+static int ovcamchip_detect(struct i2c_client *c)
+{
+ /* Ideally we would just try a single register write and see if it NAKs.
+ * That isn't possible since the OV518 can't report I2C transaction
+ * failures. So, we have to try to initialize the chip (i.e. reset it
+ * and check the ID registers) to detect its presence. */
+
+ /* Test for 7xx0 */
+ PDEBUG(3, "Testing for 0V7xx0");
+ c->addr = OV7xx0_SID;
+ if (init_camchip(c) < 0) {
+ /* Test for 6xx0 */
+ PDEBUG(3, "Testing for 0V6xx0");
+ c->addr = OV6xx0_SID;
+ if (init_camchip(c) < 0) {
+ return -ENODEV;
+ } else {
+ if (ov6xx0_detect(c) < 0) {
+ PERROR("Failed to init OV6xx0");
+ return -EIO;
+ }
+ }
+ } else {
+ if (ov7xx0_detect(c) < 0) {
+ PERROR("Failed to init OV7xx0");
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int ovcamchip_attach(struct i2c_adapter *adap)
+{
+ int rc = 0;
+ struct ovcamchip *ov;
+ struct i2c_client *c;
+
+ /* I2C is not a PnP bus, so we can never be certain that we're talking
+ * to the right chip. To prevent damage to EEPROMS and such, only
+ * attach to adapters that are known to contain OV camera chips. */
+
+ switch (adap->id) {
+ case (I2C_ALGO_SMBUS | I2C_HW_SMBUS_OV511):
+ case (I2C_ALGO_SMBUS | I2C_HW_SMBUS_OV518):
+ case (I2C_ALGO_SMBUS | I2C_HW_SMBUS_OVFX2):
+ case (I2C_ALGO_SMBUS | I2C_HW_SMBUS_W9968CF):
+ PDEBUG(1, "Adapter ID 0x%06x accepted", adap->id);
+ break;
+ default:
+ PDEBUG(1, "Adapter ID 0x%06x rejected", adap->id);
+ return -ENODEV;
+ }
+
+ c = kmalloc(sizeof *c, GFP_KERNEL);
+ if (!c) {
+ rc = -ENOMEM;
+ goto no_client;
+ }
+ memcpy(c, &client_template, sizeof *c);
+ c->adapter = adap;
+ strcpy(i2c_clientname(c), "OV????");
+
+ ov = kmalloc(sizeof *ov, GFP_KERNEL);
+ if (!ov) {
+ rc = -ENOMEM;
+ goto no_ov;
+ }
+ memset(ov, 0, sizeof *ov);
+ i2c_set_clientdata(c, ov);
+
+ rc = ovcamchip_detect(c);
+ if (rc < 0)
+ goto error;
+
+ strcpy(i2c_clientname(c), chip_names[ov->subtype]);
+
+ PDEBUG(1, "Camera chip detection complete");
+
+ i2c_attach_client(c);
+
+ return rc;
+error:
+ kfree(ov);
+no_ov:
+ kfree(c);
+no_client:
+ PDEBUG(1, "returning %d", rc);
+ return rc;
+}
+
+static int ovcamchip_detach(struct i2c_client *c)
+{
+ struct ovcamchip *ov = i2c_get_clientdata(c);
+ int rc;
+
+ rc = ov->sops->free(c);
+ if (rc < 0)
+ return rc;
+
+ i2c_detach_client(c);
+
+ kfree(ov);
+ kfree(c);
+ return 0;
+}
+
+static int ovcamchip_command(struct i2c_client *c, unsigned int cmd, void *arg)
+{
+ struct ovcamchip *ov = i2c_get_clientdata(c);
+
+ if (!ov->initialized &&
+ cmd != OVCAMCHIP_CMD_Q_SUBTYPE &&
+ cmd != OVCAMCHIP_CMD_INITIALIZE) {
+ dev_err(&c->dev, "ERROR: Camera chip not initialized yet!\n");
+ return -EPERM;
+ }
+
+ switch (cmd) {
+ case OVCAMCHIP_CMD_Q_SUBTYPE:
+ {
+ *(int *)arg = ov->subtype;
+ return 0;
+ }
+ case OVCAMCHIP_CMD_INITIALIZE:
+ {
+ int rc;
+
+ if (mono == -1)
+ ov->mono = *(int *)arg;
+ else
+ ov->mono = mono;
+
+ if (ov->mono) {
+ if (ov->subtype != CC_OV7620)
+ dev_warn(&c->dev, "Warning: Monochrome not "
+ "implemented for this chip\n");
+ else
+ dev_info(&c->dev, "Initializing chip as "
+ "monochrome\n");
+ }
+
+ rc = ov->sops->init(c);
+ if (rc < 0)
+ return rc;
+
+ ov->initialized = 1;
+ return 0;
+ }
+ default:
+ return ov->sops->command(c, cmd, arg);
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_driver driver = {
+ .owner = THIS_MODULE,
+ .name = "ovcamchip",
+ .id = I2C_DRIVERID_OVCAMCHIP,
+ .class = I2C_CLASS_CAM_DIGITAL,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = ovcamchip_attach,
+ .detach_client = ovcamchip_detach,
+ .command = ovcamchip_command,
+};
+
+static struct i2c_client client_template = {
+ I2C_DEVNAME("(unset)"),
+ .id = -1,
+ .driver = &driver,
+};
+
+static int __init ovcamchip_init(void)
+{
+#ifdef DEBUG
+ ovcamchip_debug = debug;
+#endif
+
+ PINFO(DRIVER_VERSION " : " DRIVER_DESC);
+ return i2c_add_driver(&driver);
+}
+
+static void __exit ovcamchip_exit(void)
+{
+ i2c_del_driver(&driver);
+}
+
+module_init(ovcamchip_init);
+module_exit(ovcamchip_exit);
--- /dev/null
+/* OmniVision* camera chip driver private definitions for core code and
+ * chip-specific code
+ *
+ * Copyright (c) 1999-2004 Mark McClelland
+ *
+ * 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. NO WARRANTY OF ANY KIND is expressed or implied.
+ *
+ * * OmniVision is a trademark of OmniVision Technologies, Inc. This driver
+ * is not sponsored or developed by them.
+ */
+
+#ifndef __LINUX_OVCAMCHIP_PRIV_H
+#define __LINUX_OVCAMCHIP_PRIV_H
+
+#include <media/ovcamchip.h>
+
+#ifdef DEBUG
+extern int ovcamchip_debug;
+#endif
+
+#define PDEBUG(level, fmt, args...) \
+ if (ovcamchip_debug >= (level)) pr_debug("[%s:%d] " fmt "\n", \
+ __FUNCTION__, __LINE__ , ## args)
+
+#define DDEBUG(level, dev, fmt, args...) \
+ if (ovcamchip_debug >= (level)) dev_dbg(dev, "[%s:%d] " fmt "\n", \
+ __FUNCTION__, __LINE__ , ## args)
+
+/* Number of times to retry chip detection. Increase this if you are getting
+ * "Failed to init camera chip" */
+#define I2C_DETECT_RETRIES 10
+
+struct ovcamchip_regvals {
+ unsigned char reg;
+ unsigned char val;
+};
+
+struct ovcamchip_ops {
+ int (*init)(struct i2c_client *);
+ int (*free)(struct i2c_client *);
+ int (*command)(struct i2c_client *, unsigned int, void *);
+};
+
+struct ovcamchip {
+ struct ovcamchip_ops *sops;
+ void *spriv; /* Private data for OV7x10.c etc... */
+ int subtype; /* = SEN_OV7610 etc... */
+ int mono; /* Monochrome chip? (invalid until init) */
+ int initialized; /* OVCAMCHIP_CMD_INITIALIZE was successful */
+};
+
+/* --------------------------------- */
+/* I2C I/O */
+/* --------------------------------- */
+
+static inline int ov_read(struct i2c_client *c, unsigned char reg,
+ unsigned char *value)
+{
+ int rc;
+
+ rc = i2c_smbus_read_byte_data(c, reg);
+ *value = (unsigned char) rc;
+ return rc;
+}
+
+static inline int ov_write(struct i2c_client *c, unsigned char reg,
+ unsigned char value )
+{
+ return i2c_smbus_write_byte_data(c, reg, value);
+}
+
+/* --------------------------------- */
+/* FUNCTION PROTOTYPES */
+/* --------------------------------- */
+
+/* Functions in ovcamchip_core.c */
+
+extern int ov_write_regvals(struct i2c_client *c,
+ struct ovcamchip_regvals *rvals);
+
+extern int ov_write_mask(struct i2c_client *c, unsigned char reg,
+ unsigned char value, unsigned char mask);
+
+#endif
/*
* mtdram - a test mtd device
- * $Id: mtdram.c,v 1.32 2003/05/21 15:15:07 dwmw2 Exp $
+ * $Id: mtdram.c,v 1.33 2004/08/09 13:19:44 dwmw2 Exp $
* Author: Alexander Larsson <alex@cendio.se>
*
* Copyright (c) 1999 Alexander Larsson <alex@cendio.se>
memset((char *)mtd->priv + instr->addr, 0xff, instr->len);
instr->state = MTD_ERASE_DONE;
+ mtd_erase_callback(instr);
- if (instr->callback)
- (*(instr->callback))(instr);
return 0;
}
/**
*
- * $Id: phram.c,v 1.1 2003/08/21 17:52:30 joern Exp $
+ * $Id: phram.c,v 1.2 2004/08/09 13:19:44 dwmw2 Exp $
*
* Copyright (c) Jochen Schaeuble <psionic@psionic.de>
* 07/2003 rewritten by Joern Engel <joern@wh.fh-wedel.de>
instr->state = MTD_ERASE_DONE;
- if (instr->callback)
- (*(instr->callback))(instr);
- else
- kfree(instr);
+ mtd_erase_callback(instr);
return 0;
}
/*======================================================================
- $Id: slram.c,v 1.30 2003/05/20 21:03:08 dwmw2 Exp $
+ $Id: slram.c,v 1.31 2004/08/09 13:19:44 dwmw2 Exp $
This driver provides a method to access memory not used by the kernel
itself (i.e. if the kernel commandline mem=xxx is used). To actually
instr->state = MTD_ERASE_DONE;
- if (instr->callback) {
- (*(instr->callback))(instr);
- }
- else {
- kfree(instr);
- }
+ mtd_erase_callback(instr);
return(0);
}
--- /dev/null
+/*
+ * Flash memory access on Alchemy Db1550 board
+ *
+ * $Id: db1550-flash.c,v 1.3 2004/07/14 17:45:40 dwmw2 Exp $
+ *
+ * (C) 2004 Embedded Edge, LLC, based on db1550-flash.c:
+ * (C) 2003 Pete Popov <pete_popov@yahoo.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/au1000.h>
+
+#ifdef DEBUG_RW
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+static unsigned long window_addr;
+static unsigned long window_size;
+
+
+static struct map_info db1550_map = {
+ .name = "Db1550 flash",
+};
+
+static unsigned char flash_bankwidth = 4;
+
+/*
+ * Support only 64MB NOR Flash parts
+ */
+
+#if defined(CONFIG_MTD_DB1550_BOOT) && defined(CONFIG_MTD_DB1550_USER)
+#define DB1550_BOTH_BANKS
+#elif defined(CONFIG_MTD_DB1550_BOOT) && !defined(CONFIG_MTD_DB1550_USER)
+#define DB1550_BOOT_ONLY
+#elif !defined(CONFIG_MTD_DB1550_BOOT) && defined(CONFIG_MTD_DB1550_USER)
+#define DB1550_USER_ONLY
+#endif
+
+#ifdef DB1550_BOTH_BANKS
+/* both banks will be used. Combine the first bank and the first
+ * part of the second bank together into a single jffs/jffs2
+ * partition.
+ */
+static struct mtd_partition db1550_partitions[] = {
+ /* assume boot[2:0]:swap is '0000' or '1000', which translates to:
+ * 1C00 0000 1FFF FFFF CE0 64MB Boot NOR Flash
+ * 1800 0000 1BFF FFFF CE0 64MB Param NOR Flash
+ */
+ {
+ .name = "User FS",
+ .size = (0x1FC00000 - 0x18000000),
+ .offset = 0x0000000
+ },{
+ .name = "yamon",
+ .size = 0x0100000,
+ .offset = MTDPART_OFS_APPEND,
+ .mask_flags = MTD_WRITEABLE
+ },{
+ .name = "raw kernel",
+ .size = (0x300000 - 0x40000), /* last 256KB is yamon env */
+ .offset = MTDPART_OFS_APPEND,
+ }
+};
+#elif defined(DB1550_BOOT_ONLY)
+static struct mtd_partition db1550_partitions[] = {
+ /* assume boot[2:0]:swap is '0000' or '1000', which translates to:
+ * 1C00 0000 1FFF FFFF CE0 64MB Boot NOR Flash
+ */
+ {
+ .name = "User FS",
+ .size = 0x03c00000,
+ .offset = 0x0000000
+ },{
+ .name = "yamon",
+ .size = 0x0100000,
+ .offset = MTDPART_OFS_APPEND,
+ .mask_flags = MTD_WRITEABLE
+ },{
+ .name = "raw kernel",
+ .size = (0x300000-0x40000), /* last 256KB is yamon env */
+ .offset = MTDPART_OFS_APPEND,
+ }
+};
+#elif defined(DB1550_USER_ONLY)
+static struct mtd_partition db1550_partitions[] = {
+ /* assume boot[2:0]:swap is '0000' or '1000', which translates to:
+ * 1800 0000 1BFF FFFF CE0 64MB Param NOR Flash
+ */
+ {
+ .name = "User FS",
+ .size = (0x4000000 - 0x200000), /* reserve 2MB for raw kernel */
+ .offset = 0x0000000
+ },{
+ .name = "raw kernel",
+ .size = MTDPART_SIZ_FULL,
+ .offset = MTDPART_OFS_APPEND,
+ }
+};
+#else
+#error MTD_DB1550 define combo error /* should never happen */
+#endif
+
+#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
+
+static struct mtd_info *mymtd;
+
+/*
+ * Probe the flash density and setup window address and size
+ * based on user CONFIG options. There are times when we don't
+ * want the MTD driver to be probing the boot or user flash,
+ * so having the option to enable only one bank is important.
+ */
+int setup_flash_params(void)
+{
+#if defined(DB1550_BOTH_BANKS)
+ window_addr = 0x18000000;
+ window_size = 0x8000000;
+#elif defined(DB1550_BOOT_ONLY)
+ window_addr = 0x1C000000;
+ window_size = 0x4000000;
+#else /* USER ONLY */
+ window_addr = 0x1E000000;
+ window_size = 0x4000000;
+#endif
+ return 0;
+}
+
+int __init db1550_mtd_init(void)
+{
+ struct mtd_partition *parts;
+ int nb_parts = 0;
+
+ /* Default flash bankwidth */
+ db1550_map.bankwidth = flash_bankwidth;
+
+ if (setup_flash_params())
+ return -ENXIO;
+
+ /*
+ * Static partition definition selection
+ */
+ parts = db1550_partitions;
+ nb_parts = NB_OF(db1550_partitions);
+ db1550_map.size = window_size;
+
+ /*
+ * Now let's probe for the actual flash. Do it here since
+ * specific machine settings might have been set above.
+ */
+ printk(KERN_NOTICE "Pb1550 flash: probing %d-bit flash bus\n",
+ db1550_map.bankwidth*8);
+ db1550_map.virt =
+ (unsigned long)ioremap(window_addr, window_size);
+ mymtd = do_map_probe("cfi_probe", &db1550_map);
+ if (!mymtd) return -ENXIO;
+ mymtd->owner = THIS_MODULE;
+
+ add_mtd_partitions(mymtd, parts, nb_parts);
+ return 0;
+}
+
+static void __exit db1550_mtd_cleanup(void)
+{
+ if (mymtd) {
+ del_mtd_partitions(mymtd);
+ map_destroy(mymtd);
+ }
+}
+
+module_init(db1550_mtd_init);
+module_exit(db1550_mtd_cleanup);
+
+MODULE_AUTHOR("Embedded Edge, LLC");
+MODULE_DESCRIPTION("Db1550 mtd map driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Flash memory access on Alchemy Db1xxx boards
+ *
+ * $Id: db1x00-flash.c,v 1.3 2004/07/14 17:45:40 dwmw2 Exp $
+ *
+ * (C) 2003 Pete Popov <ppopov@pacbell.net>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/au1000.h>
+#include <asm/db1x00.h>
+
+#ifdef DEBUG_RW
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+static unsigned long window_addr;
+static unsigned long window_size;
+static unsigned long flash_size;
+
+static BCSR * const bcsr = (BCSR *)0xAE000000;
+static unsigned char flash_bankwidth = 4;
+
+/*
+ * The Db1x boards support different flash densities. We setup
+ * the mtd_partition structures below for default of 64Mbit
+ * flash densities, and override the partitions sizes, if
+ * necessary, after we check the board status register.
+ */
+
+#ifdef DB1X00_BOTH_BANKS
+/* both banks will be used. Combine the first bank and the first
+ * part of the second bank together into a single jffs/jffs2
+ * partition.
+ */
+static struct mtd_partition db1x00_partitions[] = {
+ {
+ .name = "User FS",
+ .size = 0x1c00000,
+ .offset = 0x0000000
+ },{
+ .name = "yamon",
+ .size = 0x0100000,
+ .offset = MTDPART_OFS_APPEND,
+ .mask_flags = MTD_WRITEABLE
+ },{
+ .name = "raw kernel",
+ .size = (0x300000-0x40000), /* last 256KB is env */
+ .offset = MTDPART_OFS_APPEND,
+ }
+};
+#elif defined(DB1X00_BOOT_ONLY)
+static struct mtd_partition db1x00_partitions[] = {
+ {
+ .name = "User FS",
+ .size = 0x00c00000,
+ .offset = 0x0000000
+ },{
+ .name = "yamon",
+ .size = 0x0100000,
+ .offset = MTDPART_OFS_APPEND,
+ .mask_flags = MTD_WRITEABLE
+ },{
+ .name = "raw kernel",
+ .size = (0x300000-0x40000), /* last 256KB is env */
+ .offset = MTDPART_OFS_APPEND,
+ }
+};
+#elif defined(DB1X00_USER_ONLY)
+static struct mtd_partition db1x00_partitions[] = {
+ {
+ .name = "User FS",
+ .size = 0x0e00000,
+ .offset = 0x0000000
+ },{
+ .name = "raw kernel",
+ .size = MTDPART_SIZ_FULL,
+ .offset = MTDPART_OFS_APPEND,
+ }
+};
+#else
+#error MTD_DB1X00 define combo error /* should never happen */
+#endif
+#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
+
+#define NAME "Db1x00 Linux Flash"
+
+static struct map_info db1xxx_mtd_map = {
+ .name = NAME,
+};
+
+static struct mtd_partition *parsed_parts;
+static struct mtd_info *db1xxx_mtd;
+
+/*
+ * Probe the flash density and setup window address and size
+ * based on user CONFIG options. There are times when we don't
+ * want the MTD driver to be probing the boot or user flash,
+ * so having the option to enable only one bank is important.
+ */
+int setup_flash_params(void)
+{
+ switch ((bcsr->status >> 14) & 0x3) {
+ case 0: /* 64Mbit devices */
+ flash_size = 0x800000; /* 8MB per part */
+#if defined(DB1X00_BOTH_BANKS)
+ window_addr = 0x1E000000;
+ window_size = 0x2000000;
+#elif defined(DB1X00_BOOT_ONLY)
+ window_addr = 0x1F000000;
+ window_size = 0x1000000;
+#else /* USER ONLY */
+ window_addr = 0x1E000000;
+ window_size = 0x1000000;
+#endif
+ break;
+ case 1:
+ /* 128 Mbit devices */
+ flash_size = 0x1000000; /* 16MB per part */
+#if defined(DB1X00_BOTH_BANKS)
+ window_addr = 0x1C000000;
+ window_size = 0x4000000;
+ /* USERFS from 0x1C00 0000 to 0x1FC0 0000 */
+ db1x00_partitions[0].size = 0x3C00000;
+#elif defined(DB1X00_BOOT_ONLY)
+ window_addr = 0x1E000000;
+ window_size = 0x2000000;
+ /* USERFS from 0x1E00 0000 to 0x1FC0 0000 */
+ db1x00_partitions[0].size = 0x1C00000;
+#else /* USER ONLY */
+ window_addr = 0x1C000000;
+ window_size = 0x2000000;
+ /* USERFS from 0x1C00 0000 to 0x1DE00000 */
+ db1x00_partitions[0].size = 0x1DE0000;
+#endif
+ break;
+ case 2:
+ /* 256 Mbit devices */
+ flash_size = 0x4000000; /* 64MB per part */
+#if defined(DB1X00_BOTH_BANKS)
+ return 1;
+#elif defined(DB1X00_BOOT_ONLY)
+ /* Boot ROM flash bank only; no user bank */
+ window_addr = 0x1C000000;
+ window_size = 0x4000000;
+ /* USERFS from 0x1C00 0000 to 0x1FC00000 */
+ db1x00_partitions[0].size = 0x3C00000;
+#else /* USER ONLY */
+ return 1;
+#endif
+ break;
+ default:
+ return 1;
+ }
+ db1xxx_mtd_map.size = window_size;
+ db1xxx_mtd_map.bankwidth = flash_bankwidth;
+ db1xxx_mtd_map.phys = window_addr;
+ db1xxx_mtd_map.bankwidth = flash_bankwidth;
+ return 0;
+}
+
+int __init db1x00_mtd_init(void)
+{
+ struct mtd_partition *parts;
+ int nb_parts = 0;
+
+ if (setup_flash_params())
+ return -ENXIO;
+
+ /*
+ * Static partition definition selection
+ */
+ parts = db1x00_partitions;
+ nb_parts = NB_OF(db1x00_partitions);
+
+ /*
+ * Now let's probe for the actual flash. Do it here since
+ * specific machine settings might have been set above.
+ */
+ printk(KERN_NOTICE "Db1xxx flash: probing %d-bit flash bus\n",
+ db1xxx_mtd_map.bankwidth*8);
+ db1xxx_mtd_map.virt = (unsigned long)ioremap(window_addr, window_size);
+ db1xxx_mtd = do_map_probe("cfi_probe", &db1xxx_mtd_map);
+ if (!db1xxx_mtd) return -ENXIO;
+ db1xxx_mtd->owner = THIS_MODULE;
+
+ add_mtd_partitions(db1xxx_mtd, parts, nb_parts);
+ return 0;
+}
+
+static void __exit db1x00_mtd_cleanup(void)
+{
+ if (db1xxx_mtd) {
+ del_mtd_partitions(db1xxx_mtd);
+ map_destroy(db1xxx_mtd);
+ if (parsed_parts)
+ kfree(parsed_parts);
+ }
+}
+
+module_init(db1x00_mtd_init);
+module_exit(db1x00_mtd_cleanup);
+
+MODULE_AUTHOR("Pete Popov");
+MODULE_DESCRIPTION("Db1x00 mtd map driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+
+/*
+ * drivers/mtd/maps/svme182.c
+ *
+ * Flash map driver for the Dy4 SVME182 board
+ *
+ * $Id: dmv182.c,v 1.3 2004/07/14 17:45:40 dwmw2 Exp $
+ *
+ * Copyright 2003-2004, TimeSys Corporation
+ *
+ * Based on the SVME181 flash map, by Tom Nelson, Dot4, Inc. for TimeSys Corp.
+ *
+ * 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/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/errno.h>
+
+/*
+ * This driver currently handles only the 16MiB user flash bank 1 on the
+ * board. It does not provide access to bank 0 (contains the Dy4 FFW), bank 2
+ * (VxWorks boot), or the optional 48MiB expansion flash.
+ *
+ * scott.wood@timesys.com: On the newer boards with 128MiB flash, it
+ * now supports the first 96MiB (the boot flash bank containing FFW
+ * is excluded). The VxWorks loader is in partition 1.
+ */
+
+#define FLASH_BASE_ADDR 0xf0000000
+#define FLASH_BANK_SIZE (128*1024*1024)
+
+MODULE_AUTHOR("Scott Wood, TimeSys Corporation <scott.wood@timesys.com>");
+MODULE_DESCRIPTION("User-programmable flash device on the Dy4 SVME182 board");
+MODULE_LICENSE("GPL");
+
+static struct map_info svme182_map = {
+ .name = "Dy4 SVME182",
+ .bankwidth = 32,
+ .size = 128 * 1024 * 1024
+};
+
+#define BOOTIMAGE_PART_SIZE ((6*1024*1024)-RESERVED_PART_SIZE)
+
+// Allow 6MiB for the kernel
+#define NEW_BOOTIMAGE_PART_SIZE (6 * 1024 * 1024)
+// Allow 1MiB for the bootloader
+#define NEW_BOOTLOADER_PART_SIZE (1024 * 1024)
+// Use the remaining 9MiB at the end of flash for the RFS
+#define NEW_RFS_PART_SIZE (0x01000000 - NEW_BOOTLOADER_PART_SIZE - \
+ NEW_BOOTIMAGE_PART_SIZE)
+
+static struct mtd_partition svme182_partitions[] = {
+ // The Lower PABS is only 128KiB, but the partition code doesn't
+ // like partitions that don't end on the largest erase block
+ // size of the device, even if all of the erase blocks in the
+ // partition are small ones. The hardware should prevent
+ // writes to the actual PABS areas.
+ {
+ name: "Lower PABS and CPU 0 bootloader or kernel",
+ size: 6*1024*1024,
+ offset: 0,
+ },
+ {
+ name: "Root Filesystem",
+ size: 10*1024*1024,
+ offset: MTDPART_OFS_NXTBLK
+ },
+ {
+ name: "CPU1 Bootloader",
+ size: 1024*1024,
+ offset: MTDPART_OFS_NXTBLK,
+ },
+ {
+ name: "Extra",
+ size: 110*1024*1024,
+ offset: MTDPART_OFS_NXTBLK
+ },
+ {
+ name: "Foundation Firmware and Upper PABS",
+ size: 1024*1024,
+ offset: MTDPART_OFS_NXTBLK,
+ mask_flags: MTD_WRITEABLE // read-only
+ }
+};
+
+static struct mtd_info *this_mtd;
+
+static int __init init_svme182(void)
+{
+ struct mtd_partition *partitions;
+ int num_parts = sizeof(svme182_partitions) / sizeof(struct mtd_partition);
+
+ partitions = svme182_partitions;
+
+ svme182_map.virt =
+ (unsigned long)ioremap(FLASH_BASE_ADDR, svme182_map.size);
+
+ if (svme182_map.virt == 0) {
+ printk("Failed to ioremap FLASH memory area.\n");
+ return -EIO;
+ }
+
+ simple_map_init(&svme182_map);
+
+ this_mtd = do_map_probe("cfi_probe", &svme182_map);
+ if (!this_mtd)
+ {
+ iounmap((void *)svme182_map.virt);
+ return -ENXIO;
+ }
+
+ printk(KERN_NOTICE "SVME182 flash device: %dMiB at 0x%08x\n",
+ this_mtd->size >> 20, FLASH_BASE_ADDR);
+
+ this_mtd->owner = THIS_MODULE;
+ add_mtd_partitions(this_mtd, partitions, num_parts);
+
+ return 0;
+}
+
+static void __exit cleanup_svme182(void)
+{
+ if (this_mtd)
+ {
+ del_mtd_partitions(this_mtd);
+ map_destroy(this_mtd);
+ }
+
+ if (svme182_map.virt)
+ {
+ iounmap((void *)svme182_map.virt);
+ svme182_map.virt = 0;
+ }
+
+ return;
+}
+
+module_init(init_svme182);
+module_exit(cleanup_svme182);
* ichxrom.c
*
* Normal mappings of chips in physical memory
- * $Id: ichxrom.c,v 1.7 2004/07/14 18:14:09 eric Exp $
+ * $Id: ichxrom.c,v 1.8 2004/07/16 17:43:11 dwmw2 Exp $
*/
#include <linux/module.h>
info->mtd->unlock = ichxrom_unlock;
}
if (info->mtd->size > info->map.size) {
- printk(KERN_WARNING MOD_NAME " rom(%u) larger than window(%u). fixing...\n",
+ printk(KERN_WARNING MOD_NAME " rom(%u) larger than window(%lu). fixing...\n",
info->mtd->size, info->map.size);
info->mtd->size = info->map.size;
}
--- /dev/null
+/*======================================================================
+
+ drivers/mtd/maps/armflash.c: ARM Flash Layout/Partitioning
+
+ Copyright (C) 2000 ARM Limited
+
+ 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
+
+ This is access code for flashes using ARM's flash partitioning
+ standards.
+
+ $Id: integrator-flash-v24.c,v 1.13 2004/07/12 21:59:44 dwmw2 Exp $
+
+======================================================================*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+// board specific stuff - sorry, it should be in arch/arm/mach-*.
+#ifdef CONFIG_ARCH_INTEGRATOR
+
+#define FLASH_BASE INTEGRATOR_FLASH_BASE
+#define FLASH_SIZE INTEGRATOR_FLASH_SIZE
+
+#define FLASH_PART_SIZE 0x400000
+
+#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET)
+#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET)
+#define EBI_CSR1 (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_CSR1_OFFSET)
+#define EBI_LOCK (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_LOCK_OFFSET)
+
+/*
+ * Initialise the flash access systems:
+ * - Disable VPP
+ * - Assert WP
+ * - Set write enable bit in EBI reg
+ */
+static void armflash_flash_init(void)
+{
+ unsigned int tmp;
+
+ __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC);
+
+ tmp = __raw_readl(EBI_CSR1) | INTEGRATOR_EBI_WRITE_ENABLE;
+ __raw_writel(tmp, EBI_CSR1);
+
+ if (!(__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE)) {
+ __raw_writel(0xa05f, EBI_LOCK);
+ __raw_writel(tmp, EBI_CSR1);
+ __raw_writel(0, EBI_LOCK);
+ }
+}
+
+/*
+ * Shutdown the flash access systems:
+ * - Disable VPP
+ * - Assert WP
+ * - Clear write enable bit in EBI reg
+ */
+static void armflash_flash_exit(void)
+{
+ unsigned int tmp;
+
+ __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC);
+
+ /*
+ * Clear the write enable bit in system controller EBI register.
+ */
+ tmp = __raw_readl(EBI_CSR1) & ~INTEGRATOR_EBI_WRITE_ENABLE;
+ __raw_writel(tmp, EBI_CSR1);
+
+ if (__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE) {
+ __raw_writel(0xa05f, EBI_LOCK);
+ __raw_writel(tmp, EBI_CSR1);
+ __raw_writel(0, EBI_LOCK);
+ }
+}
+
+static void armflash_flash_wp(int on)
+{
+ unsigned int reg;
+
+ if (on)
+ reg = SC_CTRLC;
+ else
+ reg = SC_CTRLS;
+
+ __raw_writel(INTEGRATOR_SC_CTRL_nFLWP, reg);
+}
+
+static void armflash_set_vpp(struct map_info *map, int on)
+{
+ unsigned int reg;
+
+ if (on)
+ reg = SC_CTRLS;
+ else
+ reg = SC_CTRLC;
+
+ __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN, reg);
+}
+#endif
+
+#ifdef CONFIG_ARCH_P720T
+
+#define FLASH_BASE (0x04000000)
+#define FLASH_SIZE (64*1024*1024)
+
+#define FLASH_PART_SIZE (4*1024*1024)
+#define FLASH_BLOCK_SIZE (128*1024)
+
+static void armflash_flash_init(void)
+{
+}
+
+static void armflash_flash_exit(void)
+{
+}
+
+static void armflash_flash_wp(int on)
+{
+}
+
+static void armflash_set_vpp(struct map_info *map, int on)
+{
+}
+#endif
+
+
+static struct map_info armflash_map =
+{
+ .name = "AFS",
+ .set_vpp = armflash_set_vpp,
+ .phys = FLASH_BASE,
+};
+
+static struct mtd_info *mtd;
+static struct mtd_partition *parts;
+static const char *probes[] = { "RedBoot", "afs", NULL };
+
+static int __init armflash_cfi_init(void *base, u_int size)
+{
+ int ret;
+
+ armflash_flash_init();
+ armflash_flash_wp(1);
+
+ /*
+ * look for CFI based flash parts fitted to this board
+ */
+ armflash_map.size = size;
+ armflash_map.bankwidth = 4;
+ armflash_map.virt = (unsigned long) base;
+
+ simple_map_init(&armflash_map);
+
+ /*
+ * Also, the CFI layer automatically works out what size
+ * of chips we have, and does the necessary identification
+ * for us automatically.
+ */
+ mtd = do_map_probe("cfi_probe", &armflash_map);
+ if (!mtd)
+ return -ENXIO;
+
+ mtd->owner = THIS_MODULE;
+
+ ret = parse_mtd_partitions(mtd, probes, &parts, (void *)0);
+ if (ret > 0) {
+ ret = add_mtd_partitions(mtd, parts, ret);
+ if (ret)
+ printk(KERN_ERR "mtd partition registration "
+ "failed: %d\n", ret);
+ }
+
+ /*
+ * If we got an error, free all resources.
+ */
+ if (ret < 0) {
+ del_mtd_partitions(mtd);
+ map_destroy(mtd);
+ }
+
+ return ret;
+}
+
+static void armflash_cfi_exit(void)
+{
+ if (mtd) {
+ del_mtd_partitions(mtd);
+ map_destroy(mtd);
+ }
+ if (parts)
+ kfree(parts);
+}
+
+static int __init armflash_init(void)
+{
+ int err = -EBUSY;
+ void *base;
+
+ if (request_mem_region(FLASH_BASE, FLASH_SIZE, "flash") == NULL)
+ goto out;
+
+ base = ioremap(FLASH_BASE, FLASH_SIZE);
+ err = -ENOMEM;
+ if (base == NULL)
+ goto release;
+
+ err = armflash_cfi_init(base, FLASH_SIZE);
+ if (err) {
+ iounmap(base);
+release:
+ release_mem_region(FLASH_BASE, FLASH_SIZE);
+ }
+out:
+ return err;
+}
+
+static void __exit armflash_exit(void)
+{
+ armflash_cfi_exit();
+ iounmap((void *)armflash_map.virt);
+ release_mem_region(FLASH_BASE, FLASH_SIZE);
+ armflash_flash_exit();
+}
+
+module_init(armflash_init);
+module_exit(armflash_exit);
+
+MODULE_AUTHOR("ARM Ltd");
+MODULE_DESCRIPTION("ARM Integrator CFI map driver");
+MODULE_LICENSE("GPL");
/*
- * $Id: ixp4xx.c,v 1.1 2004/05/13 22:21:26 dsaxena Exp $
+ * $Id: ixp4xx.c,v 1.3 2004/07/12 22:38:29 dwmw2 Exp $
*
* drivers/mtd/maps/ixp4xx.c
*
#define BYTE1(h) ((h) & 0xFF)
#endif
-static __u16
-ixp4xx_read16(struct map_info *map, unsigned long ofs)
+static map_word ixp4xx_read16(struct map_info *map, unsigned long ofs)
{
- return *(__u16 *) (map->map_priv_1 + ofs);
+ map_word val;
+ val.x[0] = *(__u16 *) (map->map_priv_1 + ofs);
+ return val;
}
/*
* when attached to a 16-bit wide device (such as the 28F128J3A),
* so we can't just memcpy_fromio().
*/
-static void
-ixp4xx_copy_from(struct map_info *map, void *to,
- unsigned long from, ssize_t len)
+static void ixp4xx_copy_from(struct map_info *map, void *to,
+ unsigned long from, ssize_t len)
{
int i;
u8 *dest = (u8 *) to;
dest[len - 1] = BYTE0(src[i]);
}
-static void
-ixp4xx_write16(struct map_info *map, __u16 d, unsigned long adr)
+static void ixp4xx_write16(struct map_info *map, map_word d, unsigned long adr)
{
- *(__u16 *) (map->map_priv_1 + adr) = d;
+ *(__u16 *) (map->map_priv_1 + adr) = d.x[0];
}
struct ixp4xx_flash_info {
static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
-static int
-ixp4xx_flash_remove(struct device *_dev)
+static int ixp4xx_flash_remove(struct device *_dev)
{
struct platform_device *dev = to_platform_device(_dev);
struct flash_platform_data *plat = dev->dev.platform_data;
* any board use 8-bit access, we'll fixup the driver to
* handle that.
*/
- info->map.buswidth = 2;
+ info->map.bankwidth = 2;
info->map.name = dev->dev.bus_id;
- info->map.read16 = ixp4xx_read16,
- info->map.write16 = ixp4xx_write16,
+ info->map.read = ixp4xx_read16,
+ info->map.write = ixp4xx_write16,
info->map.copy_from = ixp4xx_copy_from,
info->res = request_mem_region(dev->resource->start,
--- /dev/null
+/*
+ * Flash on MPC-1211
+ *
+ * $Id: mpc1211.c,v 1.3 2004/07/14 17:45:40 dwmw2 Exp $
+ *
+ * (C) 2002 Interface, Saito.K & Jeanne
+ *
+ * GPL'd
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/config.h>
+
+static struct mtd_info *flash_mtd;
+static struct mtd_partition *parsed_parts;
+
+struct map_info mpc1211_flash_map = {
+ .name = "MPC-1211 FLASH",
+ .size = 0x80000,
+ .bankwidth = 1,
+};
+
+static struct mtd_partition mpc1211_partitions[] = {
+ {
+ .name = "IPL & ETH-BOOT",
+ .offset = 0x00000000,
+ .size = 0x10000,
+ },
+ {
+ .name = "Flash FS",
+ .offset = 0x00010000,
+ .size = MTDPART_SIZ_FULL,
+ }
+};
+
+static int __init init_mpc1211_maps(void)
+{
+ int nr_parts;
+
+ mpc1211_flash_map.phys = 0;
+ mpc1211_flash_map.virt = P2SEGADDR(0);
+
+ simple_map_init(&mpc1211_flash_map);
+
+ printk(KERN_NOTICE "Probing for flash chips at 0x00000000:\n");
+ flash_mtd = do_map_probe("jedec_probe", &mpc1211_flash_map);
+ if (!flash_mtd) {
+ printk(KERN_NOTICE "Flash chips not detected at either possible location.\n");
+ return -ENXIO;
+ }
+ printk(KERN_NOTICE "MPC-1211: Flash at 0x%08lx\n", mpc1211_flash_map.virt & 0x1fffffff);
+ flash_mtd->module = THIS_MODULE;
+
+ parsed_parts = mpc1211_partitions;
+ nr_parts = ARRAY_SIZE(mpc1211_partitions);
+
+ add_mtd_partitions(flash_mtd, parsed_parts, nr_parts);
+ return 0;
+}
+
+static void __exit cleanup_mpc1211_maps(void)
+{
+ if (parsed_parts)
+ del_mtd_partitions(flash_mtd);
+ else
+ del_mtd_device(flash_mtd);
+ map_destroy(flash_mtd);
+}
+
+module_init(init_mpc1211_maps);
+module_exit(cleanup_mpc1211_maps);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Saito.K & Jeanne <ksaito@interface.co.jp>");
+MODULE_DESCRIPTION("MTD map driver for MPC-1211 boards. Interface");
--- /dev/null
+/*
+ * NOR Flash memory access on TI Toto board
+ *
+ * jzhang@ti.com (C) 2003 Texas Instruments.
+ *
+ * (C) 2002 MontVista Software, Inc.
+ *
+ * $Id: omap-toto-flash.c,v 1.2 2004/07/12 21:59:44 dwmw2 Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+
+
+#ifndef CONFIG_ARCH_OMAP
+#error This is for OMAP architecture only
+#endif
+
+//these lines need be moved to a hardware header file
+#define OMAP_TOTO_FLASH_BASE 0xd8000000
+#define OMAP_TOTO_FLASH_SIZE 0x80000
+
+static struct map_info omap_toto_map_flash = {
+ .name = "OMAP Toto flash",
+ .bankwidth = 2,
+ .virt = OMAP_TOTO_FLASH_BASE,
+};
+
+
+static struct mtd_partition toto_flash_partitions[] = {
+ {
+ .name = "BootLoader",
+ .size = 0x00040000, /* hopefully u-boot will stay 128k + 128*/
+ .offset = 0,
+ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ }, {
+ .name = "ReservedSpace",
+ .size = 0x00030000,
+ .offset = MTDPART_OFS_APPEND,
+ //mask_flags: MTD_WRITEABLE, /* force read-only */
+ }, {
+ .name = "EnvArea", /* bottom 64KiB for env vars */
+ .size = MTDPART_SIZ_FULL,
+ .offset = MTDPART_OFS_APPEND,
+ }
+};
+
+static struct mtd_partition *parsed_parts;
+
+static struct mtd_info *flash_mtd;
+
+static int __init init_flash (void)
+{
+
+ struct mtd_partition *parts;
+ int nb_parts = 0;
+ int parsed_nr_parts = 0;
+ const char *part_type;
+
+ /*
+ * Static partition definition selection
+ */
+ part_type = "static";
+
+ parts = toto_flash_partitions;
+ nb_parts = ARRAY_SIZE(toto_flash_partitions);
+ omap_toto_map_flash.size = OMAP_TOTO_FLASH_SIZE;
+ omap_toto_map_flash.phys = virt_to_phys(OMAP_TOTO_FLASH_BASE);
+
+ simple_map_init(&omap_toto_map_flash);
+ /*
+ * Now let's probe for the actual flash. Do it here since
+ * specific machine settings might have been set above.
+ */
+ printk(KERN_NOTICE "OMAP toto flash: probing %d-bit flash bus\n",
+ omap_toto_map_flash.bankwidth*8);
+ flash_mtd = do_map_probe("jedec_probe", &omap_toto_map_flash);
+ if (!flash_mtd)
+ return -ENXIO;
+
+ if (parsed_nr_parts > 0) {
+ parts = parsed_parts;
+ nb_parts = parsed_nr_parts;
+ }
+
+ if (nb_parts == 0) {
+ printk(KERN_NOTICE "OMAP toto flash: no partition info available,"
+ "registering whole flash at once\n");
+ if (add_mtd_device(flash_mtd)){
+ return -ENXIO;
+ }
+ } else {
+ printk(KERN_NOTICE "Using %s partition definition\n",
+ part_type);
+ return add_mtd_partitions(flash_mtd, parts, nb_parts);
+ }
+ return 0;
+}
+
+int __init omap_toto_mtd_init(void)
+{
+ int status;
+
+ if (status = init_flash()) {
+ printk(KERN_ERR "OMAP Toto Flash: unable to init map for toto flash\n");
+ }
+ return status;
+}
+
+static void __exit omap_toto_mtd_cleanup(void)
+{
+ if (flash_mtd) {
+ del_mtd_partitions(flash_mtd);
+ map_destroy(flash_mtd);
+ if (parsed_parts)
+ kfree(parsed_parts);
+ }
+}
+
+module_init(omap_toto_mtd_init);
+module_exit(omap_toto_mtd_cleanup);
+
+MODULE_AUTHOR("Jian Zhang");
+MODULE_DESCRIPTION("OMAP Toto board map driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Flash memory access on Alchemy Pb1550 board
+ *
+ * $Id: pb1550-flash.c,v 1.4 2004/07/14 17:45:40 dwmw2 Exp $
+ *
+ * (C) 2004 Embedded Edge, LLC, based on pb1550-flash.c:
+ * (C) 2003 Pete Popov <ppopov@pacbell.net>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/au1000.h>
+#include <asm/pb1550.h>
+
+#ifdef DEBUG_RW
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+static unsigned long window_addr;
+static unsigned long window_size;
+
+
+static struct map_info pb1550_map = {
+ .name = "Pb1550 flash",
+};
+
+static unsigned char flash_bankwidth = 4;
+
+/*
+ * Support only 64MB NOR Flash parts
+ */
+
+#ifdef PB1550_BOTH_BANKS
+/* both banks will be used. Combine the first bank and the first
+ * part of the second bank together into a single jffs/jffs2
+ * partition.
+ */
+static struct mtd_partition pb1550_partitions[] = {
+ /* assume boot[2:0]:swap is '0000' or '1000', which translates to:
+ * 1C00 0000 1FFF FFFF CE0 64MB Boot NOR Flash
+ * 1800 0000 1BFF FFFF CE0 64MB Param NOR Flash
+ */
+ {
+ .name = "User FS",
+ .size = (0x1FC00000 - 0x18000000),
+ .offset = 0x0000000
+ },{
+ .name = "yamon",
+ .size = 0x0100000,
+ .offset = MTDPART_OFS_APPEND,
+ .mask_flags = MTD_WRITEABLE
+ },{
+ .name = "raw kernel",
+ .size = (0x300000 - 0x40000), /* last 256KB is yamon env */
+ .offset = MTDPART_OFS_APPEND,
+ }
+};
+#elif defined(PB1550_BOOT_ONLY)
+static struct mtd_partition pb1550_partitions[] = {
+ /* assume boot[2:0]:swap is '0000' or '1000', which translates to:
+ * 1C00 0000 1FFF FFFF CE0 64MB Boot NOR Flash
+ */
+ {
+ .name = "User FS",
+ .size = 0x03c00000,
+ .offset = 0x0000000
+ },{
+ .name = "yamon",
+ .size = 0x0100000,
+ .offset = MTDPART_OFS_APPEND,
+ .mask_flags = MTD_WRITEABLE
+ },{
+ .name = "raw kernel",
+ .size = (0x300000-0x40000), /* last 256KB is yamon env */
+ .offset = MTDPART_OFS_APPEND,
+ }
+};
+#elif defined(PB1550_USER_ONLY)
+static struct mtd_partition pb1550_partitions[] = {
+ /* assume boot[2:0]:swap is '0000' or '1000', which translates to:
+ * 1800 0000 1BFF FFFF CE0 64MB Param NOR Flash
+ */
+ {
+ .name = "User FS",
+ .size = (0x4000000 - 0x200000), /* reserve 2MB for raw kernel */
+ .offset = 0x0000000
+ },{
+ .name = "raw kernel",
+ .size = MTDPART_SIZ_FULL,
+ .offset = MTDPART_OFS_APPEND,
+ }
+};
+#else
+#error MTD_PB1550 define combo error /* should never happen */
+#endif
+
+#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
+
+static struct mtd_info *mymtd;
+
+/*
+ * Probe the flash density and setup window address and size
+ * based on user CONFIG options. There are times when we don't
+ * want the MTD driver to be probing the boot or user flash,
+ * so having the option to enable only one bank is important.
+ */
+int setup_flash_params(void)
+{
+ u16 boot_swapboot;
+ boot_swapboot = (au_readl(MEM_STSTAT) & (0x7<<1)) |
+ ((bcsr->status >> 6) & 0x1);
+ printk("Pb1550 MTD: boot:swap %d\n", boot_swapboot);
+
+ switch (boot_swapboot) {
+ case 0: /* 512Mbit devices, both enabled */
+ case 1:
+ case 8:
+ case 9:
+#if defined(PB1550_BOTH_BANKS)
+ window_addr = 0x18000000;
+ window_size = 0x8000000;
+#elif defined(PB1550_BOOT_ONLY)
+ window_addr = 0x1C000000;
+ window_size = 0x4000000;
+#else /* USER ONLY */
+ window_addr = 0x1E000000;
+ window_size = 0x4000000;
+#endif
+ break;
+ case 0xC:
+ case 0xD:
+ case 0xE:
+ case 0xF:
+ /* 64 MB Boot NOR Flash is disabled */
+ /* and the start address is moved to 0x0C00000 */
+ window_addr = 0x0C000000;
+ window_size = 0x4000000;
+ default:
+ printk("Pb1550 MTD: unsupported boot:swap setting\n");
+ return 1;
+ }
+ return 0;
+}
+
+int __init pb1550_mtd_init(void)
+{
+ struct mtd_partition *parts;
+ int nb_parts = 0;
+
+ /* Default flash bankwidth */
+ pb1550_map.bankwidth = flash_bankwidth;
+
+ if (setup_flash_params())
+ return -ENXIO;
+
+ /*
+ * Static partition definition selection
+ */
+ parts = pb1550_partitions;
+ nb_parts = NB_OF(pb1550_partitions);
+ pb1550_map.size = window_size;
+
+ /*
+ * Now let's probe for the actual flash. Do it here since
+ * specific machine settings might have been set above.
+ */
+ printk(KERN_NOTICE "Pb1550 flash: probing %d-bit flash bus\n",
+ pb1550_map.bankwidth*8);
+ pb1550_map.virt =
+ (unsigned long)ioremap(window_addr, window_size);
+ mymtd = do_map_probe("cfi_probe", &pb1550_map);
+ if (!mymtd) return -ENXIO;
+ mymtd->owner = THIS_MODULE;
+
+ add_mtd_partitions(mymtd, parts, nb_parts);
+ return 0;
+}
+
+static void __exit pb1550_mtd_cleanup(void)
+{
+ if (mymtd) {
+ del_mtd_partitions(mymtd);
+ map_destroy(mymtd);
+ }
+}
+
+module_init(pb1550_mtd_init);
+module_exit(pb1550_mtd_cleanup);
+
+MODULE_AUTHOR("Embedded Edge, LLC");
+MODULE_DESCRIPTION("Pb1550 mtd map driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Handle mapping of the flash memory access routines on the SBC8240 board.
+ *
+ * Carolyn Smith, Tektronix, Inc.
+ *
+ * This code is GPLed
+ *
+ * $Id: sbc8240.c,v 1.4 2004/07/12 22:38:29 dwmw2 Exp $
+ *
+ */
+
+/*
+ * The SBC8240 has 2 flash banks.
+ * Bank 0 is a 512 KiB AMD AM29F040B; 8 x 64 KiB sectors.
+ * It contains the U-Boot code (7 sectors) and the environment (1 sector).
+ * Bank 1 is 4 x 1 MiB AMD AM29LV800BT; 15 x 64 KiB sectors, 1 x 32 KiB sector,
+ * 2 x 8 KiB sectors, 1 x 16 KiB sectors.
+ * Both parts are JEDEC compatible.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/cfi.h>
+
+#ifdef CONFIG_MTD_PARTITIONS
+#include <linux/mtd/partitions.h>
+#endif
+
+#define DEBUG
+
+#ifdef DEBUG
+# define debugk(fmt,args...) printk(fmt ,##args)
+#else
+# define debugk(fmt,args...)
+#endif
+
+
+#define WINDOW_ADDR0 0xFFF00000 /* 512 KiB */
+#define WINDOW_SIZE0 0x00080000
+#define BUSWIDTH0 1
+
+#define WINDOW_ADDR1 0xFF000000 /* 4 MiB */
+#define WINDOW_SIZE1 0x00400000
+#define BUSWIDTH1 8
+
+#define MSG_PREFIX "sbc8240:" /* prefix for our printk()'s */
+#define MTDID "sbc8240-%d" /* for mtdparts= partitioning */
+
+
+static struct map_info sbc8240_map[2] = {
+ {
+ .name = "sbc8240 Flash Bank #0",
+ .size = WINDOW_SIZE0,
+ .bankwidth = BUSWIDTH0,
+ },
+ {
+ .name = "sbc8240 Flash Bank #1",
+ .size = WINDOW_SIZE1,
+ .bankwidth = BUSWIDTH1,
+ }
+};
+
+#define NUM_FLASH_BANKS (sizeof(sbc8240_map) / sizeof(struct map_info))
+
+/*
+ * The following defines the partition layout of SBC8240 boards.
+ *
+ * See include/linux/mtd/partitions.h for definition of the
+ * mtd_partition structure.
+ *
+ * The *_max_flash_size is the maximum possible mapped flash size
+ * which is not necessarily the actual flash size. It must correspond
+ * to the value specified in the mapping definition defined by the
+ * "struct map_desc *_io_desc" for the corresponding machine.
+ */
+
+#ifdef CONFIG_MTD_PARTITIONS
+
+static struct mtd_partition sbc8240_uboot_partitions [] = {
+ /* Bank 0 */
+ {
+ .name = "U-boot", /* U-Boot Firmware */
+ .offset = 0,
+ .size = 0x00070000, /* 7 x 64 KiB sectors */
+ .mask_flags = MTD_WRITEABLE, /* force read-only */
+ },
+ {
+ .name = "environment", /* U-Boot environment */
+ .offset = 0x00070000,
+ .size = 0x00010000, /* 1 x 64 KiB sector */
+ },
+};
+
+static struct mtd_partition sbc8240_fs_partitions [] = {
+ {
+ .name = "jffs", /* JFFS filesystem */
+ .offset = 0,
+ .size = 0x003C0000, /* 4 * 15 * 64KiB */
+ },
+ {
+ .name = "tmp32",
+ .offset = 0x003C0000,
+ .size = 0x00020000, /* 4 * 32KiB */
+ },
+ {
+ .name = "tmp8a",
+ .offset = 0x003E0000,
+ .size = 0x00008000, /* 4 * 8KiB */
+ },
+ {
+ .name = "tmp8b",
+ .offset = 0x003E8000,
+ .size = 0x00008000, /* 4 * 8KiB */
+ },
+ {
+ .name = "tmp16",
+ .offset = 0x003F0000,
+ .size = 0x00010000, /* 4 * 16KiB */
+ }
+};
+
+#define NB_OF(x) (sizeof (x) / sizeof (x[0]))
+
+/* trivial struct to describe partition information */
+struct mtd_part_def
+{
+ int nums;
+ unsigned char *type;
+ struct mtd_partition* mtd_part;
+};
+
+static struct mtd_info *sbc8240_mtd[NUM_FLASH_BANKS];
+static struct mtd_part_def sbc8240_part_banks[NUM_FLASH_BANKS];
+
+
+#endif /* CONFIG_MTD_PARTITIONS */
+
+
+int __init init_sbc8240_mtd (void)
+{
+ static struct _cjs {
+ u_long addr;
+ u_long size;
+ } pt[NUM_FLASH_BANKS] = {
+ {
+ .addr = WINDOW_ADDR0,
+ .size = WINDOW_SIZE0
+ },
+ {
+ .addr = WINDOW_ADDR1,
+ .size = WINDOW_SIZE1
+ },
+ };
+
+ int devicesfound = 0;
+ int i;
+
+ for (i = 0; i < NUM_FLASH_BANKS; i++) {
+ printk (KERN_NOTICE MSG_PREFIX
+ "Probing 0x%08lx at 0x%08lx\n", pt[i].size, pt[i].addr);
+
+ sbc8240_map[i].map_priv_1 =
+ (unsigned long) ioremap (pt[i].addr, pt[i].size);
+ if (!sbc8240_map[i].map_priv_1) {
+ printk (MSG_PREFIX "failed to ioremap\n");
+ return -EIO;
+ }
+ simple_map_init(&sbc8240_mtd[i]);
+
+ sbc8240_mtd[i] = do_map_probe("jedec_probe", &sbc8240_map[i]);
+
+ if (sbc8240_mtd[i]) {
+ sbc8240_mtd[i]->module = THIS_MODULE;
+ devicesfound++;
+ }
+ }
+
+ if (!devicesfound) {
+ printk(KERN_NOTICE MSG_PREFIX
+ "No suppported flash chips found!\n");
+ return -ENXIO;
+ }
+
+#ifdef CONFIG_MTD_PARTITIONS
+ sbc8240_part_banks[0].mtd_part = sbc8240_uboot_partitions;
+ sbc8240_part_banks[0].type = "static image";
+ sbc8240_part_banks[0].nums = NB_OF(sbc8240_uboot_partitions);
+ sbc8240_part_banks[1].mtd_part = sbc8240_fs_partitions;
+ sbc8240_part_banks[1].type = "static file system";
+ sbc8240_part_banks[1].nums = NB_OF(sbc8240_fs_partitions);
+
+ for (i = 0; i < NUM_FLASH_BANKS; i++) {
+
+ if (!sbc8240_mtd[i]) continue;
+ if (sbc8240_part_banks[i].nums == 0) {
+ printk (KERN_NOTICE MSG_PREFIX
+ "No partition info available, registering whole device\n");
+ add_mtd_device(sbc8240_mtd[i]);
+ } else {
+ printk (KERN_NOTICE MSG_PREFIX
+ "Using %s partition definition\n", sbc8240_part_banks[i].mtd_part->name);
+ add_mtd_partitions (sbc8240_mtd[i],
+ sbc8240_part_banks[i].mtd_part,
+ sbc8240_part_banks[i].nums);
+ }
+ }
+#else
+ printk(KERN_NOTICE MSG_PREFIX
+ "Registering %d flash banks at once\n", devicesfound);
+
+ for (i = 0; i < devicesfound; i++) {
+ add_mtd_device(sbc8240_mtd[i]);
+ }
+#endif /* CONFIG_MTD_PARTITIONS */
+
+ return devicesfound == 0 ? -ENXIO : 0;
+}
+
+static void __exit cleanup_sbc8240_mtd (void)
+{
+ int i;
+
+ for (i = 0; i < NUM_FLASH_BANKS; i++) {
+ if (sbc8240_mtd[i]) {
+ del_mtd_device (sbc8240_mtd[i]);
+ map_destroy (sbc8240_mtd[i]);
+ }
+ if (sbc8240_map[i].map_priv_1) {
+ iounmap ((void *) sbc8240_map[i].map_priv_1);
+ sbc8240_map[i].map_priv_1 = 0;
+ }
+ }
+}
+
+module_init (init_sbc8240_mtd);
+module_exit (cleanup_sbc8240_mtd);
+
+MODULE_LICENSE ("GPL");
+MODULE_AUTHOR ("Carolyn Smith <carolyn.smith@tektronix.com>");
+MODULE_DESCRIPTION ("MTD map driver for SBC8240 boards");
+
--- /dev/null
+/*
+ * drivers/mtd/nand/au1550nd.c
+ *
+ * Copyright (C) 2004 Embedded Edge, LLC
+ *
+ * $Id: au1550nd.c,v 1.5 2004/05/17 07:19:35 ppopov Exp $
+ *
+ * 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/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+#include <asm/au1000.h>
+#ifdef CONFIG_MIPS_PB1550
+#include <asm/pb1550.h>
+#endif
+#ifdef CONFIG_MIPS_DB1550
+#include <asm/db1x00.h>
+#endif
+
+
+/*
+ * MTD structure for NAND controller
+ */
+static struct mtd_info *au1550_mtd = NULL;
+static volatile u32 p_nand;
+static int nand_width = 1; /* default, only x8 supported for now */
+
+/* Internal buffers. Page buffer and oob buffer for one block*/
+static u_char data_buf[512 + 16];
+static u_char oob_buf[16 * 32];
+
+/*
+ * Define partitions for flash device
+ */
+const static struct mtd_partition partition_info[] = {
+#ifdef CONFIG_MIPS_PB1550
+#define NUM_PARTITIONS 2
+ {
+ .name = "Pb1550 NAND FS 0",
+ .offset = 0,
+ .size = 8*1024*1024
+ },
+ {
+ .name = "Pb1550 NAND FS 1",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL
+ }
+#endif
+#ifdef CONFIG_MIPS_DB1550
+#define NUM_PARTITIONS 2
+ {
+ .name = "Db1550 NAND FS 0",
+ .offset = 0,
+ .size = 8*1024*1024
+ },
+ {
+ .name = "Db1550 NAND FS 1",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL
+ }
+#endif
+};
+
+static inline void write_cmd_reg(u8 cmd)
+{
+ if (nand_width)
+ *((volatile u8 *)(p_nand + MEM_STNAND_CMD)) = cmd;
+ else
+ *((volatile u16 *)(p_nand + MEM_STNAND_CMD)) = cmd;
+ au_sync();
+}
+
+static inline void write_addr_reg(u8 addr)
+{
+ if (nand_width)
+ *((volatile u8 *)(p_nand + MEM_STNAND_ADDR)) = addr;
+ else
+ *((volatile u16 *)(p_nand + MEM_STNAND_ADDR)) = addr;
+ au_sync();
+}
+
+static inline void write_data_reg(u8 data)
+{
+ if (nand_width)
+ *((volatile u8 *)(p_nand + MEM_STNAND_DATA)) = data;
+ else
+ *((volatile u16 *)(p_nand + MEM_STNAND_DATA)) = data;
+ au_sync();
+}
+
+static inline u32 read_data_reg(void)
+{
+ u32 data;
+ if (nand_width) {
+ data = *((volatile u8 *)(p_nand + MEM_STNAND_DATA));
+ au_sync();
+ }
+ else {
+ data = *((volatile u16 *)(p_nand + MEM_STNAND_DATA));
+ au_sync();
+ }
+ return data;
+}
+
+void au1550_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+}
+
+int au1550_device_ready(struct mtd_info *mtd)
+{
+ int ready;
+ ready = (au_readl(MEM_STSTAT) & 0x1) ? 1 : 0;
+ return ready;
+}
+
+static u_char au1550_nand_read_byte(struct mtd_info *mtd)
+{
+ u_char ret;
+ ret = read_data_reg();
+ return ret;
+}
+
+static void au1550_nand_write_byte(struct mtd_info *mtd, u_char byte)
+{
+ write_data_reg((u8)byte);
+}
+
+static void
+au1550_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ int i;
+
+ for (i=0; i<len; i++)
+ write_data_reg(buf[i]);
+}
+
+static void
+au1550_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ int i;
+
+ for (i=0; i<len; i++)
+ buf[i] = (u_char)read_data_reg();
+}
+
+static int
+au1550_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ int i;
+
+ for (i=0; i<len; i++)
+ if (buf[i] != (u_char)read_data_reg())
+ return -EFAULT;
+
+ return 0;
+}
+
+static void au1550_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+ switch(chip) {
+ case -1:
+ /* deassert chip enable */
+ au_writel(au_readl(MEM_STNDCTL) & ~0x20 , MEM_STNDCTL);
+ break;
+ case 0:
+ /* assert (force assert) chip enable */
+ au_writel(au_readl(MEM_STNDCTL) | 0x20 , MEM_STNDCTL);
+ break;
+
+ default:
+ BUG();
+ }
+}
+
+static void au1550_nand_command (struct mtd_info *mtd, unsigned command,
+ int column, int page_addr)
+{
+ register struct nand_chip *this = mtd->priv;
+
+ /*
+ * Write out the command to the device.
+ */
+ if (command == NAND_CMD_SEQIN) {
+ int readcmd;
+
+ if (column >= mtd->oobblock) {
+ /* OOB area */
+ column -= mtd->oobblock;
+ readcmd = NAND_CMD_READOOB;
+ } else if (column < 256) {
+ /* First 256 bytes --> READ0 */
+ readcmd = NAND_CMD_READ0;
+ } else {
+ column -= 256;
+ readcmd = NAND_CMD_READ1;
+ }
+ write_cmd_reg(readcmd);
+ }
+ write_cmd_reg(command);
+
+ if (column != -1 || page_addr != -1) {
+
+ /* Serially input address */
+ if (column != -1)
+ write_addr_reg(column);
+ if (page_addr != -1) {
+ write_addr_reg((unsigned char) (page_addr & 0xff));
+ write_addr_reg(((page_addr >> 8) & 0xff));
+ /* One more address cycle for higher density devices */
+ if (mtd->size & 0x0c000000)
+ write_addr_reg((unsigned char) ((page_addr >> 16) & 0x0f));
+ }
+ }
+
+ switch (command) {
+
+ case NAND_CMD_PAGEPROG:
+ case NAND_CMD_ERASE1:
+ case NAND_CMD_ERASE2:
+ case NAND_CMD_SEQIN:
+ case NAND_CMD_STATUS:
+ break;
+
+ case NAND_CMD_RESET:
+ if (this->dev_ready)
+ break;
+ udelay(this->chip_delay);
+ write_cmd_reg(NAND_CMD_STATUS);
+ while ( !(read_data_reg() & 0x40));
+ return;
+
+ /* This applies to read commands */
+ default:
+ udelay (this->chip_delay);
+ }
+
+ /* wait until command is processed */
+ while (!this->dev_ready(mtd));
+}
+
+
+/*
+ * Main initialization routine
+ */
+int __init au1550_init (void)
+{
+ struct nand_chip *this;
+ u16 boot_swapboot = 0; /* default value */
+ u32 mem_time;
+
+ /* Allocate memory for MTD device structure and private data */
+ au1550_mtd = kmalloc (sizeof(struct mtd_info) +
+ sizeof (struct nand_chip), GFP_KERNEL);
+ if (!au1550_mtd) {
+ printk ("Unable to allocate NAND MTD dev structure.\n");
+ return -ENOMEM;
+ }
+
+ /* Get pointer to private data */
+ this = (struct nand_chip *) (&au1550_mtd[1]);
+
+ /* Initialize structures */
+ memset((char *) au1550_mtd, 0, sizeof(struct mtd_info));
+ memset((char *) this, 0, sizeof(struct nand_chip));
+
+ /* Link the private data with the MTD structure */
+ au1550_mtd->priv = this;
+
+ /* disable interrupts */
+ au_writel(au_readl(MEM_STNDCTL) & ~(1<<8), MEM_STNDCTL);
+
+ /* disable NAND boot */
+ au_writel(au_readl(MEM_STNDCTL) & ~(1<<0), MEM_STNDCTL);
+
+#ifdef CONFIG_MIPS_PB1550
+ /* set gpio206 high */
+ au_writel(au_readl(GPIO2_DIR) & ~(1<<6), GPIO2_DIR);
+
+ boot_swapboot = (au_readl(MEM_STSTAT) & (0x7<<1)) |
+ ((bcsr->status >> 6) & 0x1);
+ switch (boot_swapboot) {
+ case 0:
+ case 2:
+ case 8:
+ case 0xC:
+ case 0xD:
+ /* x16 NAND Flash */
+ nand_width = 0;
+ printk("Pb1550 NAND: 16-bit NAND not supported by MTD\n");
+ break;
+ case 1:
+ case 9:
+ case 3:
+ case 0xE:
+ case 0xF:
+ /* x8 NAND Flash */
+ nand_width = 1;
+ break;
+ default:
+ printk("Pb1550 NAND: bad boot:swap\n");
+ kfree(au1550_mtd);
+ return 1;
+ }
+
+ /* Configure RCE1 - should be done by YAMON */
+ au_writel(0x5 | (nand_width << 22), MEM_STCFG1);
+ au_writel(NAND_TIMING, MEM_STTIME1);
+ mem_time = au_readl(MEM_STTIME1);
+ au_sync();
+
+ /* setup and enable chip select */
+ /* we really need to decode offsets only up till 0x20 */
+ au_writel((1<<28) | (NAND_PHYS_ADDR>>4) |
+ (((NAND_PHYS_ADDR + 0x1000)-1) & (0x3fff<<18)>>18),
+ MEM_STADDR1);
+ au_sync();
+#endif
+
+#ifdef CONFIG_MIPS_DB1550
+ /* Configure RCE1 - should be done by YAMON */
+ au_writel(0x00400005, MEM_STCFG1);
+ au_writel(0x00007774, MEM_STTIME1);
+ au_writel(0x12000FFF, MEM_STADDR1);
+#endif
+
+ p_nand = (volatile struct nand_regs *)ioremap(NAND_PHYS_ADDR, 0x1000);
+
+ /* Set address of hardware control function */
+ this->hwcontrol = au1550_hwcontrol;
+ this->dev_ready = au1550_device_ready;
+ /* 30 us command delay time */
+ this->chip_delay = 30;
+
+ this->cmdfunc = au1550_nand_command;
+ this->select_chip = au1550_nand_select_chip;
+ this->write_byte = au1550_nand_write_byte;
+ this->read_byte = au1550_nand_read_byte;
+ this->write_buf = au1550_nand_write_buf;
+ this->read_buf = au1550_nand_read_buf;
+ this->verify_buf = au1550_nand_verify_buf;
+ this->eccmode = NAND_ECC_SOFT;
+
+ /* Set internal data buffer */
+ this->data_buf = data_buf;
+ this->oob_buf = oob_buf;
+
+ /* Scan to find existence of the device */
+ if (nand_scan (au1550_mtd, 1)) {
+ kfree (au1550_mtd);
+ return -ENXIO;
+ }
+
+ /* Register the partitions */
+ add_mtd_partitions(au1550_mtd, partition_info, NUM_PARTITIONS);
+
+ return 0;
+}
+
+module_init(au1550_init);
+
+/*
+ * Clean up routine
+ */
+#ifdef MODULE
+static void __exit au1550_cleanup (void)
+{
+ struct nand_chip *this = (struct nand_chip *) &au1550_mtd[1];
+
+ iounmap ((void *)p_nand);
+
+ /* Unregister partitions */
+ del_mtd_partitions(au1550_mtd);
+
+ /* Unregister the device */
+ del_mtd_device (au1550_mtd);
+
+ /* Free the MTD device structure */
+ kfree (au1550_mtd);
+}
+module_exit(au1550_cleanup);
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Embedded Edge, LLC");
+MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on Pb1550 board");
* drivers/mtd/nand/diskonchip.c
*
* (C) 2003 Red Hat, Inc.
+ * (C) 2004 Dan Brown <dan_brown@ieee.org>
+ * (C) 2004 Kalev Lember <kalev@smartlink.ee>
*
* Author: David Woodhouse <dwmw2@infradead.org>
+ * Additional Diskonchip 2000 and Millennium support by Dan Brown <dan_brown@ieee.org>
+ * Diskonchip Millennium Plus support by Kalev Lember <kalev@smartlink.ee>
*
* Interface to generic NAND code for M-Systems DiskOnChip devices
*
- * $Id: diskonchip.c,v 1.23 2004/07/13 00:14:35 dbrown Exp $
+ * $Id: diskonchip.c,v 1.34 2004/08/09 19:41:12 dbrown Exp $
*/
#include <linux/kernel.h>
#include <linux/mtd/inftl.h>
/* Where to look for the devices? */
-#ifndef CONFIG_MTD_DOCPROBE_ADDRESS
-#define CONFIG_MTD_DOCPROBE_ADDRESS 0
+#ifndef CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS
+#define CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS 0
#endif
static unsigned long __initdata doc_locations[] = {
#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__)
-#ifdef CONFIG_MTD_DOCPROBE_HIGH
+#ifdef CONFIG_MTD_DISKONCHIP_PROBE_HIGH
0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000,
0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000,
0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000,
#define INFTL_BBT_RESERVED_BLOCKS 4
+#define DoC_is_MillenniumPlus(doc) ((doc)->ChipID == DOC_ChipID_DocMilPlus16 || (doc)->ChipID == DOC_ChipID_DocMilPlus32)
#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil)
#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k)
#endif
MODULE_PARM(inftl_bbt_write, "i");
-static unsigned long doc_config_location = CONFIG_MTD_DOCPROBE_ADDRESS;
+static unsigned long doc_config_location = CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS;
MODULE_PARM(doc_config_location, "l");
MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");
for (i = 0; i < cycles; i++) {
if (DoC_is_Millennium(doc))
dummy = ReadDOC(doc->virtadr, NOP);
+ else if (DoC_is_MillenniumPlus(doc))
+ dummy = ReadDOC(doc->virtadr, Mplus_NOP);
else
dummy = ReadDOC(doc->virtadr, DOCStatus);
}
}
+
+#define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1)
+
/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
static int _DoC_WaitReady(struct doc_priv *doc)
{
if(debug) printk("_DoC_WaitReady...\n");
/* Out-of-line routine to wait for chip response */
- while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
- if (time_after(jiffies, timeo)) {
- printk("_DoC_WaitReady timed out.\n");
- return -EIO;
+ if (DoC_is_MillenniumPlus(doc)) {
+ while ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) {
+ if (time_after(jiffies, timeo)) {
+ printk("_DoC_WaitReady timed out.\n");
+ return -EIO;
+ }
+ udelay(1);
+ cond_resched();
+ }
+ } else {
+ while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
+ if (time_after(jiffies, timeo)) {
+ printk("_DoC_WaitReady timed out.\n");
+ return -EIO;
+ }
+ udelay(1);
+ cond_resched();
}
- udelay(1);
- cond_resched();
}
return 0;
unsigned long docptr = doc->virtadr;
int ret = 0;
- DoC_Delay(doc, 4);
+ if (DoC_is_MillenniumPlus(doc)) {
+ DoC_Delay(doc, 4);
- if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B))
- /* Call the out-of-line routine to wait */
- ret = _DoC_WaitReady(doc);
+ if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK)
+ /* Call the out-of-line routine to wait */
+ ret = _DoC_WaitReady(doc);
+ } else {
+ DoC_Delay(doc, 4);
+
+ if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B))
+ /* Call the out-of-line routine to wait */
+ ret = _DoC_WaitReady(doc);
+ DoC_Delay(doc, 2);
+ }
- DoC_Delay(doc, 2);
if(debug) printk("DoC_WaitReady OK\n");
return ret;
}
ReadDOC(docptr, ReadPipeInit);
for (i=0; i < len-1; i++)
- buf[i] = ReadDOC(docptr, Mil_CDSN_IO);
+ buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff));
/* Terminate read pipeline */
buf[i] = ReadDOC(docptr, LastDataRead);
return 0;
}
-static void doc200x_select_chip(struct mtd_info *mtd, int chip)
+static u_char doc2001plus_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+ struct doc_priv *doc = (void *)this->priv;
+ unsigned long docptr = doc->virtadr;
+ u_char ret;
+
+ ReadDOC(docptr, Mplus_ReadPipeInit);
+ ReadDOC(docptr, Mplus_ReadPipeInit);
+ ret = ReadDOC(docptr, Mplus_LastDataRead);
+ if (debug) printk("read_byte returns %02x\n", ret);
+ return ret;
+}
+
+static void doc2001plus_writebuf(struct mtd_info *mtd,
+ const u_char *buf, int len)
+{
+ struct nand_chip *this = mtd->priv;
+ struct doc_priv *doc = (void *)this->priv;
+ unsigned long docptr = doc->virtadr;
+ int i;
+
+ if (debug)printk("writebuf of %d bytes: ", len);
+ for (i=0; i < len; i++) {
+ WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i);
+ if (debug && i < 16)
+ printk("%02x ", buf[i]);
+ }
+ if (debug) printk("\n");
+}
+
+static void doc2001plus_readbuf(struct mtd_info *mtd,
+ u_char *buf, int len)
+{
+ struct nand_chip *this = mtd->priv;
+ struct doc_priv *doc = (void *)this->priv;
+ unsigned long docptr = doc->virtadr;
+ int i;
+
+ if (debug)printk("readbuf of %d bytes: ", len);
+
+ /* Start read pipeline */
+ ReadDOC(docptr, Mplus_ReadPipeInit);
+ ReadDOC(docptr, Mplus_ReadPipeInit);
+
+ for (i=0; i < len-2; i++) {
+ buf[i] = ReadDOC(docptr, Mil_CDSN_IO);
+ if (debug && i < 16)
+ printk("%02x ", buf[i]);
+ }
+
+ /* Terminate read pipeline */
+ buf[len-2] = ReadDOC(docptr, Mplus_LastDataRead);
+ if (debug && i < 16)
+ printk("%02x ", buf[len-2]);
+ buf[len-1] = ReadDOC(docptr, Mplus_LastDataRead);
+ if (debug && i < 16)
+ printk("%02x ", buf[len-1]);
+ if (debug) printk("\n");
+}
+
+static int doc2001plus_verifybuf(struct mtd_info *mtd,
+ const u_char *buf, int len)
+{
+ struct nand_chip *this = mtd->priv;
+ struct doc_priv *doc = (void *)this->priv;
+ unsigned long docptr = doc->virtadr;
+ int i;
+
+ if (debug)printk("verifybuf of %d bytes: ", len);
+
+ /* Start read pipeline */
+ ReadDOC(docptr, Mplus_ReadPipeInit);
+ ReadDOC(docptr, Mplus_ReadPipeInit);
+
+ for (i=0; i < len-2; i++)
+ if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) {
+ ReadDOC(docptr, Mplus_LastDataRead);
+ ReadDOC(docptr, Mplus_LastDataRead);
+ return i;
+ }
+ if (buf[len-2] != ReadDOC(docptr, Mplus_LastDataRead))
+ return len-2;
+ if (buf[len-1] != ReadDOC(docptr, Mplus_LastDataRead))
+ return len-1;
+ return 0;
+}
+
+static void doc2001plus_select_chip(struct mtd_info *mtd, int chip)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
int floor = 0;
- /* 11.4.4 -- deassert CE before changing chip */
- doc200x_hwcontrol(mtd, NAND_CTL_CLRNCE);
+ if(debug)printk("select chip (%d)\n", chip);
+
+ if (chip == -1) {
+ /* Disable flash internally */
+ WriteDOC(0, docptr, Mplus_FlashSelect);
+ return;
+ }
+
+ floor = chip / doc->chips_per_floor;
+ chip -= (floor * doc->chips_per_floor);
+
+ /* Assert ChipEnable and deassert WriteProtect */
+ WriteDOC((DOC_FLASH_CE), docptr, Mplus_FlashSelect);
+ this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+ doc->curchip = chip;
+ doc->curfloor = floor;
+}
+
+static void doc200x_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct nand_chip *this = mtd->priv;
+ struct doc_priv *doc = (void *)this->priv;
+ unsigned long docptr = doc->virtadr;
+ int floor = 0;
if(debug)printk("select chip (%d)\n", chip);
floor = chip / doc->chips_per_floor;
chip -= (floor * doc->chips_per_floor);
+ /* 11.4.4 -- deassert CE before changing chip */
+ doc200x_hwcontrol(mtd, NAND_CTL_CLRNCE);
+
WriteDOC(floor, docptr, FloorSelect);
WriteDOC(chip, docptr, CDSNDeviceSelect);
DoC_Delay(doc, 4);
}
+static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
+{
+ struct nand_chip *this = mtd->priv;
+ struct doc_priv *doc = (void *)this->priv;
+ unsigned long docptr = doc->virtadr;
+
+ /*
+ * Must terminate write pipeline before sending any commands
+ * to the device.
+ */
+ if (command == NAND_CMD_PAGEPROG) {
+ WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
+ WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
+ }
+
+ /*
+ * Write out the command to the device.
+ */
+ if (command == NAND_CMD_SEQIN) {
+ int readcmd;
+
+ if (column >= mtd->oobblock) {
+ /* OOB area */
+ column -= mtd->oobblock;
+ readcmd = NAND_CMD_READOOB;
+ } else if (column < 256) {
+ /* First 256 bytes --> READ0 */
+ readcmd = NAND_CMD_READ0;
+ } else {
+ column -= 256;
+ readcmd = NAND_CMD_READ1;
+ }
+ WriteDOC(readcmd, docptr, Mplus_FlashCmd);
+ }
+ WriteDOC(command, docptr, Mplus_FlashCmd);
+ WriteDOC(0, docptr, Mplus_WritePipeTerm);
+ WriteDOC(0, docptr, Mplus_WritePipeTerm);
+
+ if (column != -1 || page_addr != -1) {
+ /* Serially input address */
+ if (column != -1) {
+ /* Adjust columns for 16 bit buswidth */
+ if (this->options & NAND_BUSWIDTH_16)
+ column >>= 1;
+ WriteDOC(column, docptr, Mplus_FlashAddress);
+ }
+ if (page_addr != -1) {
+ WriteDOC((unsigned char) (page_addr & 0xff), docptr, Mplus_FlashAddress);
+ WriteDOC((unsigned char) ((page_addr >> 8) & 0xff), docptr, Mplus_FlashAddress);
+ /* One more address cycle for higher density devices */
+ if (this->chipsize & 0x0c000000) {
+ WriteDOC((unsigned char) ((page_addr >> 16) & 0x0f), docptr, Mplus_FlashAddress);
+ printk("high density\n");
+ }
+ }
+ WriteDOC(0, docptr, Mplus_WritePipeTerm);
+ WriteDOC(0, docptr, Mplus_WritePipeTerm);
+ /* deassert ALE */
+ if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || command == NAND_CMD_READOOB || command == NAND_CMD_READID)
+ WriteDOC(0, docptr, Mplus_FlashControl);
+ }
+
+ /*
+ * program and erase have their own busy handlers
+ * status and sequential in needs no delay
+ */
+ switch (command) {
+
+ case NAND_CMD_PAGEPROG:
+ case NAND_CMD_ERASE1:
+ case NAND_CMD_ERASE2:
+ case NAND_CMD_SEQIN:
+ case NAND_CMD_STATUS:
+ return;
+
+ case NAND_CMD_RESET:
+ if (this->dev_ready)
+ break;
+ udelay(this->chip_delay);
+ WriteDOC(NAND_CMD_STATUS, docptr, Mplus_FlashCmd);
+ WriteDOC(0, docptr, Mplus_WritePipeTerm);
+ WriteDOC(0, docptr, Mplus_WritePipeTerm);
+ while ( !(this->read_byte(mtd) & 0x40));
+ return;
+
+ /* This applies to read commands */
+ default:
+ /*
+ * If we don't have access to the busy pin, we apply the given
+ * command delay
+ */
+ if (!this->dev_ready) {
+ udelay (this->chip_delay);
+ return;
+ }
+ }
+
+ /* Apply this short delay always to ensure that we do wait tWB in
+ * any case on any machine. */
+ ndelay (100);
+ /* wait until command is processed */
+ while (!this->dev_ready(mtd));
+}
+
static int doc200x_dev_ready(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
- /* 11.4.2 -- must NOP four times before checking FR/B# */
- DoC_Delay(doc, 4);
- if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
- if(debug)
- printk("not ready\n");
- return 0;
+ if (DoC_is_MillenniumPlus(doc)) {
+ /* 11.4.2 -- must NOP four times before checking FR/B# */
+ DoC_Delay(doc, 4);
+ if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) {
+ if(debug)
+ printk("not ready\n");
+ return 0;
+ }
+ if (debug)printk("was ready\n");
+ return 1;
+ } else {
+ /* 11.4.2 -- must NOP four times before checking FR/B# */
+ DoC_Delay(doc, 4);
+ if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
+ if(debug)
+ printk("not ready\n");
+ return 0;
+ }
+ /* 11.4.2 -- Must NOP twice if it's ready */
+ DoC_Delay(doc, 2);
+ if (debug)printk("was ready\n");
+ return 1;
}
- /* 11.4.2 -- Must NOP twice if it's ready */
- DoC_Delay(doc, 2);
- if (debug)printk("was ready\n");
- return 1;
-}
+}
static int doc200x_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
{
WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
break;
- }
+ }
+}
+
+static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ struct nand_chip *this = mtd->priv;
+ struct doc_priv *doc = (void *)this->priv;
+ unsigned long docptr = doc->virtadr;
+
+ /* Prime the ECC engine */
+ switch(mode) {
+ case NAND_ECC_READ:
+ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
+ WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf);
+ break;
+ case NAND_ECC_WRITE:
+ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
+ WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf);
+ break;
+ }
}
/* This code is only called on write */
WriteDOC(0, docptr, 2k_CDSN_IO);
WriteDOC(0, docptr, 2k_CDSN_IO);
WriteDOC(doc->CDSNControl, docptr, CDSNControl);
+ } else if (DoC_is_MillenniumPlus(doc)) {
+ WriteDOC(0, docptr, Mplus_NOP);
+ WriteDOC(0, docptr, Mplus_NOP);
+ WriteDOC(0, docptr, Mplus_NOP);
} else {
WriteDOC(0, docptr, NOP);
WriteDOC(0, docptr, NOP);
}
for (i = 0; i < 6; i++) {
- ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
+ if (DoC_is_MillenniumPlus(doc))
+ ecc_code[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i);
+ else
+ ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
if (ecc_code[i] != empty_write_ecc[i])
emptymatch = 0;
}
- WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+ if (DoC_is_MillenniumPlus(doc))
+ WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);
+ else
+ WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
#if 0
/* If emptymatch=1, we might have an all-0xff data buffer. Check. */
if (emptymatch) {
dummy = ReadDOC(docptr, 2k_ECCStatus);
dummy = ReadDOC(docptr, 2k_ECCStatus);
dummy = ReadDOC(docptr, 2k_ECCStatus);
+ } else if (DoC_is_MillenniumPlus(doc)) {
+ dummy = ReadDOC(docptr, Mplus_ECCConf);
+ dummy = ReadDOC(docptr, Mplus_ECCConf);
+ dummy = ReadDOC(docptr, Mplus_ECCConf);
} else {
dummy = ReadDOC(docptr, ECCConf);
dummy = ReadDOC(docptr, ECCConf);
/* Error occured ? */
if (dummy & 0x80) {
for (i = 0; i < 6; i++) {
- calc_ecc[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
+ if (DoC_is_MillenniumPlus(doc))
+ calc_ecc[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i);
+ else
+ calc_ecc[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
if (calc_ecc[i] != empty_read_syndrome[i])
emptymatch = 0;
}
if (ret > 0)
printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret);
}
- WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+ if (DoC_is_MillenniumPlus(doc))
+ WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);
+ else
+ WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
if (no_ecc_failures && (ret == -1)) {
printk(KERN_ERR "suppressing ECC failure\n");
ret = 0;
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- int offs, end = (MAX_MEDIAHEADER_SCAN << this->phys_erase_shift);
- int ret, retlen;
+ unsigned offs, end = (MAX_MEDIAHEADER_SCAN << this->phys_erase_shift);
+ int ret;
+ size_t retlen;
end = min(end, mtd->size); // paranoia
for (offs = 0; offs < end; offs += mtd->erasesize) {
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- u_char *buf = this->data_buf;
- struct NFTLMediaHeader *mh = (struct NFTLMediaHeader *) buf;
- const int psize = 1 << this->page_shift;
- int blocks, maxblocks;
+ int ret = 0;
+ u_char *buf;
+ struct NFTLMediaHeader *mh;
+ const unsigned psize = 1 << this->page_shift;
+ unsigned blocks, maxblocks;
int offs, numheaders;
- if (!(numheaders=find_media_headers(mtd, buf, "ANAND", 1))) return 0;
+ buf = kmalloc(mtd->oobblock, GFP_KERNEL);
+ if (!buf) {
+ printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n");
+ return 0;
+ }
+ if (!(numheaders=find_media_headers(mtd, buf, "ANAND", 1))) goto out;
+ mh = (struct NFTLMediaHeader *) buf;
//#ifdef CONFIG_MTD_DEBUG_VERBOSE
// if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
//#endif
blocks = mtd->size >> this->phys_erase_shift;
- maxblocks = min(32768, mtd->erasesize - psize);
+ maxblocks = min(32768U, mtd->erasesize - psize);
if (mh->UnitSizeFactor == 0x00) {
/* Auto-determine UnitSizeFactor. The constraints are:
mh->UnitSizeFactor = 0xff;
while (blocks > maxblocks) {
blocks >>= 1;
- maxblocks = min(32768, (maxblocks << 1) + psize);
+ maxblocks = min(32768U, (maxblocks << 1) + psize);
mh->UnitSizeFactor--;
}
printk(KERN_WARNING "UnitSizeFactor=0x00 detected. Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor);
mtd->erasesize <<= (0xff - mh->UnitSizeFactor);
printk(KERN_INFO "Setting virtual erase size to %d\n", mtd->erasesize);
blocks = mtd->size >> this->bbt_erase_shift;
- maxblocks = min(32768, mtd->erasesize - psize);
+ maxblocks = min(32768U, mtd->erasesize - psize);
}
if (blocks > maxblocks) {
printk(KERN_ERR "UnitSizeFactor of 0x%02x is inconsistent with device size. Aborting.\n", mh->UnitSizeFactor);
- return 0;
+ goto out;
}
/* Skip past the media headers. */
parts[1].name = " DiskOnChip Remainder partition";
parts[1].offset = offs;
parts[1].size = mtd->size - offs;
- return 2;
+ ret = 2;
+ goto out;
}
- return 1;
+ ret = 1;
+out:
+ kfree(buf);
+ return ret;
}
/* This is a stripped-down copy of the code in inftlmount.c */
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
- u_char *buf = this->data_buf;
- struct INFTLMediaHeader *mh = (struct INFTLMediaHeader *) buf;
+ int ret = 0;
+ u_char *buf;
+ struct INFTLMediaHeader *mh;
struct INFTLPartition *ip;
int numparts = 0;
int blocks;
if (inftl_bbt_write)
end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift);
- if (!find_media_headers(mtd, buf, "BNAND", 0)) return 0;
+ buf = kmalloc(mtd->oobblock, GFP_KERNEL);
+ if (!buf) {
+ printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n");
+ return 0;
+ }
+
+ if (!find_media_headers(mtd, buf, "BNAND", 0)) goto out;
doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift);
+ mh = (struct INFTLMediaHeader *) buf;
mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks);
mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions);
" NoOfBDTLPartitions = %d\n"
" BlockMultiplerBits = %d\n"
" FormatFlgs = %d\n"
- " OsakVersion = 0x%x\n"
+ " OsakVersion = %d.%d.%d.%d\n"
" PercentUsed = %d\n",
mh->bootRecordID, mh->NoOfBootImageBlocks,
mh->NoOfBinaryPartitions,
mh->NoOfBDTLPartitions,
mh->BlockMultiplierBits, mh->FormatFlags,
- mh->OsakVersion, mh->PercentUsed);
+ ((unsigned char *) &mh->OsakVersion)[0] & 0xf,
+ ((unsigned char *) &mh->OsakVersion)[1] & 0xf,
+ ((unsigned char *) &mh->OsakVersion)[2] & 0xf,
+ ((unsigned char *) &mh->OsakVersion)[3] & 0xf,
+ mh->PercentUsed);
//#endif
vshift = this->phys_erase_shift + mh->BlockMultiplierBits;
blocks = mtd->size >> vshift;
if (blocks > 32768) {
printk(KERN_ERR "BlockMultiplierBits=%d is inconsistent with device size. Aborting.\n", mh->BlockMultiplierBits);
- return 0;
+ goto out;
}
blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift);
if (inftl_bbt_write && (blocks > mtd->erasesize)) {
printk(KERN_ERR "Writeable BBTs spanning more than one erase block are not yet supported. FIX ME!\n");
- return 0;
+ goto out;
}
/* Scan the partitions */
parts[numparts].size = end - parts[numparts].offset;
numparts++;
}
- return numparts;
+ ret = numparts;
+out:
+ kfree(buf);
+ return ret;
}
static int __init nftl_scan_bbt(struct mtd_info *mtd)
if ((ret = nand_scan_bbt(mtd, NULL)))
return ret;
add_mtd_device(mtd);
-#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
- if (!no_autopart) add_mtd_partitions(mtd, parts, numparts);
+#ifdef CONFIG_MTD_PARTITIONS
+ if (!no_autopart)
+ add_mtd_partitions(mtd, parts, numparts);
#endif
return 0;
}
return -EIO;
}
- if (mtd->size == (8<<20)) {
-#if 0
-/* This doesn't seem to work for me. I get ECC errors on every page. */
- /* The Millennium 8MiB is actually an NFTL device! */
- mtd->name = "DiskOnChip Millennium 8MiB (NFTL)";
- return nftl_scan_bbt(mtd);
-#endif
- printk(KERN_ERR "DiskOnChip Millennium 8MiB is not supported.\n");
- return -EIO;
+ if (DoC_is_MillenniumPlus(doc)) {
+ this->bbt_td->options = NAND_BBT_2BIT | NAND_BBT_ABSPAGE;
+ if (inftl_bbt_write)
+ this->bbt_td->options |= NAND_BBT_WRITE;
+ this->bbt_td->pages[0] = 2;
+ this->bbt_md = NULL;
+ } else {
+ this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT |
+ NAND_BBT_VERSION;
+ if (inftl_bbt_write)
+ this->bbt_td->options |= NAND_BBT_WRITE;
+ this->bbt_td->offs = 8;
+ this->bbt_td->len = 8;
+ this->bbt_td->veroffs = 7;
+ this->bbt_td->maxblocks = INFTL_BBT_RESERVED_BLOCKS;
+ this->bbt_td->reserved_block_code = 0x01;
+ this->bbt_td->pattern = "MSYS_BBT";
+
+ this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT |
+ NAND_BBT_VERSION;
+ if (inftl_bbt_write)
+ this->bbt_md->options |= NAND_BBT_WRITE;
+ this->bbt_md->offs = 8;
+ this->bbt_md->len = 8;
+ this->bbt_md->veroffs = 7;
+ this->bbt_md->maxblocks = INFTL_BBT_RESERVED_BLOCKS;
+ this->bbt_md->reserved_block_code = 0x01;
+ this->bbt_md->pattern = "TBB_SYSM";
}
- this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT |
- NAND_BBT_VERSION;
- if (inftl_bbt_write)
- this->bbt_td->options |= NAND_BBT_WRITE;
- this->bbt_td->offs = 8;
- this->bbt_td->len = 8;
- this->bbt_td->veroffs = 7;
- this->bbt_td->maxblocks = INFTL_BBT_RESERVED_BLOCKS;
- this->bbt_td->reserved_block_code = 0x01;
- this->bbt_td->pattern = "MSYS_BBT";
-
- this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT |
- NAND_BBT_VERSION;
- if (inftl_bbt_write)
- this->bbt_md->options |= NAND_BBT_WRITE;
- this->bbt_md->offs = 8;
- this->bbt_md->len = 8;
- this->bbt_md->veroffs = 7;
- this->bbt_md->maxblocks = INFTL_BBT_RESERVED_BLOCKS;
- this->bbt_md->reserved_block_code = 0x01;
- this->bbt_md->pattern = "TBB_SYSM";
-
/* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
At least as nand_bbt.c is currently written. */
if ((ret = nand_scan_bbt(mtd, NULL)))
autopartitioning, but I want to give it more thought. */
if (!numparts) return -EIO;
add_mtd_device(mtd);
-#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
- if (!no_autopart) add_mtd_partitions(mtd, parts, numparts);
+#ifdef CONFIG_MTD_PARTITIONS
+ if (!no_autopart)
+ add_mtd_partitions(mtd, parts, numparts);
#endif
return 0;
}
this->write_buf = doc2001_writebuf;
this->read_buf = doc2001_readbuf;
this->verify_buf = doc2001_verifybuf;
- this->scan_bbt = inftl_scan_bbt;
ReadDOC(doc->virtadr, ChipID);
ReadDOC(doc->virtadr, ChipID);
can have multiple chips. */
doc2000_count_chips(mtd);
mtd->name = "DiskOnChip 2000 (INFTL Model)";
+ this->scan_bbt = inftl_scan_bbt;
return (4 * doc->chips_per_floor);
} else {
/* Bog-standard Millennium */
doc->chips_per_floor = 1;
mtd->name = "DiskOnChip Millennium";
+ this->scan_bbt = nftl_scan_bbt;
return 1;
}
}
+static inline int __init doc2001plus_init(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+ struct doc_priv *doc = (void *)this->priv;
+
+ this->write_byte = NULL;
+ this->read_byte = doc2001plus_read_byte;
+ this->write_buf = doc2001plus_writebuf;
+ this->read_buf = doc2001plus_readbuf;
+ this->verify_buf = doc2001plus_verifybuf;
+ this->scan_bbt = inftl_scan_bbt;
+ this->hwcontrol = NULL;
+ this->select_chip = doc2001plus_select_chip;
+ this->cmdfunc = doc2001plus_command;
+ this->enable_hwecc = doc2001plus_enable_hwecc;
+
+ doc->chips_per_floor = 1;
+ mtd->name = "DiskOnChip Millennium Plus";
+
+ return 1;
+}
+
static inline int __init doc_probe(unsigned long physadr)
{
unsigned char ChipID;
case DOC_ChipID_DocMil:
reg = DoC_ECCConf;
break;
+ case DOC_ChipID_DocMilPlus16:
+ case DOC_ChipID_DocMilPlus32:
+ case 0:
+ /* Possible Millennium Plus, need to do more checks */
+ /* Possibly release from power down mode */
+ for (tmp = 0; (tmp < 4); tmp++)
+ ReadDOC(virtadr, Mplus_Power);
+
+ /* Reset the Millennium Plus ASIC */
+ tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT |
+ DOC_MODE_BDECT;
+ WriteDOC(tmp, virtadr, Mplus_DOCControl);
+ WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm);
+
+ mdelay(1);
+ /* Enable the Millennium Plus ASIC */
+ tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT |
+ DOC_MODE_BDECT;
+ WriteDOC(tmp, virtadr, Mplus_DOCControl);
+ WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm);
+ mdelay(1);
+
+ ChipID = ReadDOC(virtadr, ChipID);
+
+ switch (ChipID) {
+ case DOC_ChipID_DocMilPlus16:
+ reg = DoC_Mplus_Toggle;
+ break;
+ case DOC_ChipID_DocMilPlus32:
+ printk(KERN_ERR "DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n");
+ default:
+ ret = -ENODEV;
+ goto notfound;
+ }
+ break;
+
default:
ret = -ENODEV;
goto notfound;
}
for (mtd = doclist; mtd; mtd = doc->nextdoc) {
+ unsigned char oldval;
+ unsigned char newval;
nand = mtd->priv;
doc = (void *)nand->priv;
/* Use the alias resolution register to determine if this is
in fact the same DOC aliased to a new address. If writes
to one chip's alias resolution register change the value on
the other chip, they're the same chip. */
- unsigned char oldval = ReadDOC(doc->virtadr, AliasResolution);
- unsigned char newval = ReadDOC(virtadr, AliasResolution);
+ if (ChipID == DOC_ChipID_DocMilPlus16) {
+ oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution);
+ newval = ReadDOC(virtadr, Mplus_AliasResolution);
+ } else {
+ oldval = ReadDOC(doc->virtadr, AliasResolution);
+ newval = ReadDOC(virtadr, AliasResolution);
+ }
if (oldval != newval)
continue;
- WriteDOC(~newval, virtadr, AliasResolution);
- oldval = ReadDOC(doc->virtadr, AliasResolution);
- WriteDOC(newval, virtadr, AliasResolution); // restore it
+ if (ChipID == DOC_ChipID_DocMilPlus16) {
+ WriteDOC(~newval, virtadr, Mplus_AliasResolution);
+ oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution);
+ WriteDOC(newval, virtadr, Mplus_AliasResolution); // restore it
+ } else {
+ WriteDOC(~newval, virtadr, AliasResolution);
+ oldval = ReadDOC(doc->virtadr, AliasResolution);
+ WriteDOC(newval, virtadr, AliasResolution); // restore it
+ }
newval = ~newval;
if (oldval == newval) {
- //printk(KERN_DEBUG "Found alias of DOC at 0x%lx to 0x%lx\n", doc->physadr, physadr);
+ printk(KERN_DEBUG "Found alias of DOC at 0x%lx to 0x%lx\n", doc->physadr, physadr);
goto notfound;
}
}
if (ChipID == DOC_ChipID_Doc2k)
numchips = doc2000_init(mtd);
+ else if (ChipID == DOC_ChipID_DocMilPlus16)
+ numchips = doc2001plus_init(mtd);
else
numchips = doc2001_init(mtd);
kfree(mtd);
}
}
-
+
module_init(init_nanddoc);
module_exit(cleanup_nanddoc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
-MODULE_DESCRIPTION("M-Systems DiskOnChip 2000 and Millennium device driver\n");
+MODULE_DESCRIPTION("M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver\n");
* The AG-AND chips have nice features for speed improvement,
* which are not supported yet. Read / program 4 pages in one go.
*
- * $Id: nand_base.c,v 1.113 2004/07/14 16:31:31 gleixner Exp $
+ * $Id: nand_base.c,v 1.115 2004/08/09 13:19:45 dwmw2 Exp $
*
* 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/bitops.h>
#include <asm/io.h>
-#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
+#ifdef CONFIG_MTD_PARTITIONS
#include <linux/mtd/partitions.h>
#endif
nand_release_chip(mtd);
/*
- * Return success, if no ECC failures, else -EIO
+ * Return success, if no ECC failures, else -EBADMSG
* fs driver will take care of that, because
- * retlen == desired len and result == -EIO
+ * retlen == desired len and result == -EBADMSG
*/
*retlen = read;
- return ecc_failed ? -EIO : 0;
+ return ecc_failed ? -EBADMSG : 0;
}
/**
ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
/* Do call back function */
- if (!ret && instr->callback)
- instr->callback (instr);
+ if (!ret)
+ mtd_erase_callback(instr);
/* Deselect and wake up anyone waiting on the device */
nand_release_chip(mtd);
{
struct nand_chip *this = mtd->priv;
-#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
- /* Unregister partitions */
+#ifdef CONFIG_MTD_PARTITIONS
+ /* Deregister partitions */
del_mtd_partitions (mtd);
#endif
- /* Unregister the device */
+ /* Deregister the device */
del_mtd_device (mtd);
/* Free bad block table memory, if allocated */
--- /dev/null
+/*
+ * drivers/mtd/nand_bbt.c
+ *
+ * Overview:
+ * Bad block table support for the NAND driver
+ *
+ * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
+ *
+ * $Id: nand_bbt.c,v 1.24 2004/06/28 08:25:35 gleixner Exp $
+ *
+ * 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.
+ *
+ * Description:
+ *
+ * When nand_scan_bbt is called, then it tries to find the bad block table
+ * depending on the options in the bbt descriptor(s). If a bbt is found
+ * then the contents are read and the memory based bbt is created. If a
+ * mirrored bbt is selected then the mirror is searched too and the
+ * versions are compared. If the mirror has a greater version number
+ * than the mirror bbt is used to build the memory based bbt.
+ * If the tables are not versioned, then we "or" the bad block information.
+ * If one of the bbt's is out of date or does not exist it is (re)created.
+ * If no bbt exists at all then the device is scanned for factory marked
+ * good / bad blocks and the bad block tables are created.
+ *
+ * For manufacturer created bbts like the one found on M-SYS DOC devices
+ * the bbt is searched and read but never created
+ *
+ * The autogenerated bad block table is located in the last good blocks
+ * of the device. The table is mirrored, so it can be updated eventually.
+ * The table is marked in the oob area with an ident pattern and a version
+ * number which indicates which of both tables is more up to date.
+ *
+ * The table uses 2 bits per block
+ * 11b: block is good
+ * 00b: block is factory marked bad
+ * 01b, 10b: block is marked bad due to wear
+ *
+ * The memory bad block table uses the following scheme:
+ * 00b: block is good
+ * 01b: block is marked bad due to wear
+ * 10b: block is reserved (to protect the bbt area)
+ * 11b: block is factory marked bad
+ *
+ * Multichip devices like DOC store the bad block info per floor.
+ *
+ * Following assumptions are made:
+ * - bbts start at a page boundary, if autolocated on a block boundary
+ * - the space neccecary for a bbt in FLASH does not exceed a block boundary
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/compatmac.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+
+
+/**
+ * check_pattern - [GENERIC] check if a pattern is in the buffer
+ * @buf: the buffer to search
+ * @len: the length of buffer to search
+ * @paglen: the pagelength
+ * @td: search pattern descriptor
+ *
+ * Check for a pattern at the given place. Used to search bad block
+ * tables and good / bad block identifiers.
+ * If the SCAN_EMPTY option is set then check, if all bytes except the
+ * pattern area contain 0xff
+ *
+*/
+static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
+{
+ int i, end;
+ uint8_t *p = buf;
+
+ end = paglen + td->offs;
+ if (td->options & NAND_BBT_SCANEMPTY) {
+ for (i = 0; i < end; i++) {
+ if (p[i] != 0xff)
+ return -1;
+ }
+ }
+ p += end;
+
+ /* Compare the pattern */
+ for (i = 0; i < td->len; i++) {
+ if (p[i] != td->pattern[i])
+ return -1;
+ }
+
+ p += td->len;
+ end += td->len;
+ if (td->options & NAND_BBT_SCANEMPTY) {
+ for (i = end; i < len; i++) {
+ if (*p++ != 0xff)
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * read_bbt - [GENERIC] Read the bad block table starting from page
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @page: the starting page
+ * @num: the number of bbt descriptors to read
+ * @bits: number of bits per block
+ * @offs: offset in the memory table
+ *
+ * Read the bad block table starting from page.
+ *
+ */
+static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,
+ int bits, int offs, int reserved_block_code)
+{
+ int res, i, j, act = 0;
+ struct nand_chip *this = mtd->priv;
+ size_t retlen, len, totlen;
+ loff_t from;
+ uint8_t msk = (uint8_t) ((1 << bits) - 1);
+
+ totlen = (num * bits) >> 3;
+ from = ((loff_t)page) << this->page_shift;
+
+ while (totlen) {
+ len = min (totlen, (size_t) (1 << this->bbt_erase_shift));
+ res = mtd->read_ecc (mtd, from, len, &retlen, buf, NULL, this->autooob);
+ if (res < 0) {
+ if (retlen != len) {
+ printk (KERN_INFO "nand_bbt: Error reading bad block table\n");
+ return res;
+ }
+ printk (KERN_WARNING "nand_bbt: ECC error while reading bad block table\n");
+ }
+
+ /* Analyse data */
+ for (i = 0; i < len; i++) {
+ uint8_t dat = buf[i];
+ for (j = 0; j < 8; j += bits, act += 2) {
+ uint8_t tmp = (dat >> j) & msk;
+ if (tmp == msk)
+ continue;
+ if (reserved_block_code &&
+ (tmp == reserved_block_code)) {
+ printk (KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n",
+ ((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
+ this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);
+ continue;
+ }
+ /* Leave it for now, if its matured we can move this
+ * message to MTD_DEBUG_LEVEL0 */
+ printk (KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n",
+ ((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
+ /* Factory marked bad or worn out ? */
+ if (tmp == 0)
+ this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
+ else
+ this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06);
+ }
+ }
+ totlen -= len;
+ from += len;
+ }
+ return 0;
+}
+
+/**
+ * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @chip: read the table for a specific chip, -1 read all chips.
+ * Applies only if NAND_BBT_PERCHIP option is set
+ *
+ * Read the bad block table for all chips starting at a given page
+ * We assume that the bbt bits are in consecutive order.
+*/
+static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
+{
+ struct nand_chip *this = mtd->priv;
+ int res = 0, i;
+ int bits;
+
+ bits = td->options & NAND_BBT_NRBITS_MSK;
+ if (td->options & NAND_BBT_PERCHIP) {
+ int offs = 0;
+ for (i = 0; i < this->numchips; i++) {
+ if (chip == -1 || chip == i)
+ res = read_bbt (mtd, buf, td->pages[i], this->chipsize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code);
+ if (res)
+ return res;
+ offs += this->chipsize >> (this->bbt_erase_shift + 2);
+ }
+ } else {
+ res = read_bbt (mtd, buf, td->pages[0], mtd->size >> this->bbt_erase_shift, bits, 0, td->reserved_block_code);
+ if (res)
+ return res;
+ }
+ return 0;
+}
+
+/**
+ * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md: descriptor for the bad block table mirror
+ *
+ * Read the bad block table(s) for all chips starting at a given page
+ * We assume that the bbt bits are in consecutive order.
+ *
+*/
+static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td,
+ struct nand_bbt_descr *md)
+{
+ struct nand_chip *this = mtd->priv;
+
+ /* Read the primary version, if available */
+ if (td->options & NAND_BBT_VERSION) {
+ nand_read_raw (mtd, buf, td->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize);
+ td->version[0] = buf[mtd->oobblock + td->veroffs];
+ printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]);
+ }
+
+ /* Read the mirror version, if available */
+ if (md && (md->options & NAND_BBT_VERSION)) {
+ nand_read_raw (mtd, buf, md->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize);
+ md->version[0] = buf[mtd->oobblock + md->veroffs];
+ printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]);
+ }
+
+ return 1;
+}
+
+/**
+ * create_bbt - [GENERIC] Create a bad block table by scanning the device
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @bd: descriptor for the good/bad block search pattern
+ * @chip: create the table for a specific chip, -1 read all chips.
+ * Applies only if NAND_BBT_PERCHIP option is set
+ *
+ * Create a bad block table by scanning the device
+ * for the given good/bad block identify pattern
+ */
+static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip)
+{
+ struct nand_chip *this = mtd->priv;
+ int i, j, numblocks, len, scanlen;
+ int startblock;
+ loff_t from;
+ size_t readlen, ooblen;
+
+ printk (KERN_INFO "Scanning device for bad blocks\n");
+
+ if (bd->options & NAND_BBT_SCANALLPAGES)
+ len = 1 << (this->bbt_erase_shift - this->page_shift);
+ else {
+ if (bd->options & NAND_BBT_SCAN2NDPAGE)
+ len = 2;
+ else
+ len = 1;
+ }
+ scanlen = mtd->oobblock + mtd->oobsize;
+ readlen = len * mtd->oobblock;
+ ooblen = len * mtd->oobsize;
+
+ if (chip == -1) {
+ /* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it
+ * makes shifting and masking less painful */
+ numblocks = mtd->size >> (this->bbt_erase_shift - 1);
+ startblock = 0;
+ from = 0;
+ } else {
+ if (chip >= this->numchips) {
+ printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n",
+ chip + 1, this->numchips);
+ return;
+ }
+ numblocks = this->chipsize >> (this->bbt_erase_shift - 1);
+ startblock = chip * numblocks;
+ numblocks += startblock;
+ from = startblock << (this->bbt_erase_shift - 1);
+ }
+
+ for (i = startblock; i < numblocks;) {
+ nand_read_raw (mtd, buf, from, readlen, ooblen);
+ for (j = 0; j < len; j++) {
+ if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {
+ this->bbt[i >> 3] |= 0x03 << (i & 0x6);
+ printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
+ i >> 1, (unsigned int) from);
+ break;
+ }
+ }
+ i += 2;
+ from += (1 << this->bbt_erase_shift);
+ }
+}
+
+/**
+ * search_bbt - [GENERIC] scan the device for a specific bad block table
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ *
+ * Read the bad block table by searching for a given ident pattern.
+ * Search is preformed either from the beginning up or from the end of
+ * the device downwards. The search starts always at the start of a
+ * block.
+ * If the option NAND_BBT_PERCHIP is given, each chip is searched
+ * for a bbt, which contains the bad block information of this chip.
+ * This is neccecary to provide support for certain DOC devices.
+ *
+ * The bbt ident pattern resides in the oob area of the first page
+ * in a block.
+ */
+static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
+{
+ struct nand_chip *this = mtd->priv;
+ int i, chips;
+ int bits, startblock, block, dir;
+ int scanlen = mtd->oobblock + mtd->oobsize;
+ int bbtblocks;
+
+ /* Search direction top -> down ? */
+ if (td->options & NAND_BBT_LASTBLOCK) {
+ startblock = (mtd->size >> this->bbt_erase_shift) -1;
+ dir = -1;
+ } else {
+ startblock = 0;
+ dir = 1;
+ }
+
+ /* Do we have a bbt per chip ? */
+ if (td->options & NAND_BBT_PERCHIP) {
+ chips = this->numchips;
+ bbtblocks = this->chipsize >> this->bbt_erase_shift;
+ startblock &= bbtblocks - 1;
+ } else {
+ chips = 1;
+ bbtblocks = mtd->size >> this->bbt_erase_shift;
+ }
+
+ /* Number of bits for each erase block in the bbt */
+ bits = td->options & NAND_BBT_NRBITS_MSK;
+
+ for (i = 0; i < chips; i++) {
+ /* Reset version information */
+ td->version[i] = 0;
+ td->pages[i] = -1;
+ /* Scan the maximum number of blocks */
+ for (block = 0; block < td->maxblocks; block++) {
+ int actblock = startblock + dir * block;
+ /* Read first page */
+ nand_read_raw (mtd, buf, actblock << this->bbt_erase_shift, mtd->oobblock, mtd->oobsize);
+ if (!check_pattern(buf, scanlen, mtd->oobblock, td)) {
+ td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift);
+ if (td->options & NAND_BBT_VERSION) {
+ td->version[i] = buf[mtd->oobblock + td->veroffs];
+ }
+ break;
+ }
+ }
+ startblock += this->chipsize >> this->bbt_erase_shift;
+ }
+ /* Check, if we found a bbt for each requested chip */
+ for (i = 0; i < chips; i++) {
+ if (td->pages[i] == -1)
+ printk (KERN_WARNING "Bad block table not found for chip %d\n", i);
+ else
+ printk (KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i], td->version[i]);
+ }
+ return 0;
+}
+
+/**
+ * search_read_bbts - [GENERIC] scan the device for bad block table(s)
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md: descriptor for the bad block table mirror
+ *
+ * Search and read the bad block table(s)
+*/
+static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf,
+ struct nand_bbt_descr *td, struct nand_bbt_descr *md)
+{
+ /* Search the primary table */
+ search_bbt (mtd, buf, td);
+
+ /* Search the mirror table */
+ if (md)
+ search_bbt (mtd, buf, md);
+
+ /* Force result check */
+ return 1;
+}
+
+
+/**
+ * write_bbt - [GENERIC] (Re)write the bad block table
+ *
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @td: descriptor for the bad block table
+ * @md: descriptor for the bad block table mirror
+ * @chipsel: selector for a specific chip, -1 for all
+ *
+ * (Re)write the bad block table
+ *
+*/
+static int write_bbt (struct mtd_info *mtd, uint8_t *buf,
+ struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chipsel)
+{
+ struct nand_chip *this = mtd->priv;
+ struct nand_oobinfo oobinfo;
+ struct erase_info einfo;
+ int i, j, res, chip = 0;
+ int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
+ int nrchips, bbtoffs, pageoffs;
+ uint8_t msk[4];
+ uint8_t rcode = td->reserved_block_code;
+ size_t retlen, len = 0;
+ loff_t to;
+
+ if (!rcode)
+ rcode = 0xff;
+ /* Write bad block table per chip rather than per device ? */
+ if (td->options & NAND_BBT_PERCHIP) {
+ numblocks = (int) (this->chipsize >> this->bbt_erase_shift);
+ /* Full device write or specific chip ? */
+ if (chipsel == -1) {
+ nrchips = this->numchips;
+ } else {
+ nrchips = chipsel + 1;
+ chip = chipsel;
+ }
+ } else {
+ numblocks = (int) (mtd->size >> this->bbt_erase_shift);
+ nrchips = 1;
+ }
+
+ /* Loop through the chips */
+ for (; chip < nrchips; chip++) {
+
+ /* There was already a version of the table, reuse the page
+ * This applies for absolute placement too, as we have the
+ * page nr. in td->pages.
+ */
+ if (td->pages[chip] != -1) {
+ page = td->pages[chip];
+ goto write;
+ }
+
+ /* Automatic placement of the bad block table */
+ /* Search direction top -> down ? */
+ if (td->options & NAND_BBT_LASTBLOCK) {
+ startblock = numblocks * (chip + 1) - 1;
+ dir = -1;
+ } else {
+ startblock = chip * numblocks;
+ dir = 1;
+ }
+
+ for (i = 0; i < td->maxblocks; i++) {
+ int block = startblock + dir * i;
+ /* Check, if the block is bad */
+ switch ((this->bbt[block >> 2] >> (2 * (block & 0x03))) & 0x03) {
+ case 0x01:
+ case 0x03:
+ continue;
+ }
+ page = block << (this->bbt_erase_shift - this->page_shift);
+ /* Check, if the block is used by the mirror table */
+ if (!md || md->pages[chip] != page)
+ goto write;
+ }
+ printk (KERN_ERR "No space left to write bad block table\n");
+ return -ENOSPC;
+write:
+
+ /* Set up shift count and masks for the flash table */
+ bits = td->options & NAND_BBT_NRBITS_MSK;
+ switch (bits) {
+ case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x01; break;
+ case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x03; break;
+ case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; msk[2] = ~rcode; msk[3] = 0x0f; break;
+ case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; msk[2] = ~rcode; msk[3] = 0xff; break;
+ default: return -EINVAL;
+ }
+
+ bbtoffs = chip * (numblocks >> 2);
+
+ to = ((loff_t) page) << this->page_shift;
+
+ memcpy (&oobinfo, this->autooob, sizeof(oobinfo));
+ oobinfo.useecc = MTD_NANDECC_PLACEONLY;
+
+ /* Must we save the block contents ? */
+ if (td->options & NAND_BBT_SAVECONTENT) {
+ /* Make it block aligned */
+ to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1));
+ len = 1 << this->bbt_erase_shift;
+ res = mtd->read_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo);
+ if (res < 0) {
+ if (retlen != len) {
+ printk (KERN_INFO "nand_bbt: Error reading block for writing the bad block table\n");
+ return res;
+ }
+ printk (KERN_WARNING "nand_bbt: ECC error while reading block for writing bad block table\n");
+ }
+ /* Calc the byte offset in the buffer */
+ pageoffs = page - (int)(to >> this->page_shift);
+ offs = pageoffs << this->page_shift;
+ /* Preset the bbt area with 0xff */
+ memset (&buf[offs], 0xff, (size_t)(numblocks >> sft));
+ /* Preset the bbt's oob area with 0xff */
+ memset (&buf[len + pageoffs * mtd->oobsize], 0xff,
+ ((len >> this->page_shift) - pageoffs) * mtd->oobsize);
+ if (td->options & NAND_BBT_VERSION) {
+ buf[len + (pageoffs * mtd->oobsize) + td->veroffs] = td->version[chip];
+ }
+ } else {
+ /* Calc length */
+ len = (size_t) (numblocks >> sft);
+ /* Make it page aligned ! */
+ len = (len + (mtd->oobblock-1)) & ~(mtd->oobblock-1);
+ /* Preset the buffer with 0xff */
+ memset (buf, 0xff, len + (len >> this->page_shift) * mtd->oobsize);
+ offs = 0;
+ /* Pattern is located in oob area of first page */
+ memcpy (&buf[len + td->offs], td->pattern, td->len);
+ if (td->options & NAND_BBT_VERSION) {
+ buf[len + td->veroffs] = td->version[chip];
+ }
+ }
+
+ /* walk through the memory table */
+ for (i = 0; i < numblocks; ) {
+ uint8_t dat;
+ dat = this->bbt[bbtoffs + (i >> 2)];
+ for (j = 0; j < 4; j++ , i++) {
+ int sftcnt = (i << (3 - sft)) & sftmsk;
+ /* Do not store the reserved bbt blocks ! */
+ buf[offs + (i >> sft)] &= ~(msk[dat & 0x03] << sftcnt);
+ dat >>= 2;
+ }
+ }
+
+ memset (&einfo, 0, sizeof (einfo));
+ einfo.mtd = mtd;
+ einfo.addr = (unsigned long) to;
+ einfo.len = 1 << this->bbt_erase_shift;
+ res = nand_erase_nand (mtd, &einfo, 1);
+ if (res < 0) {
+ printk (KERN_WARNING "nand_bbt: Error during block erase: %d\n", res);
+ return res;
+ }
+
+ res = mtd->write_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo);
+ if (res < 0) {
+ printk (KERN_WARNING "nand_bbt: Error while writing bad block table %d\n", res);
+ return res;
+ }
+ printk (KERN_DEBUG "Bad block table written to 0x%08x, version 0x%02X\n",
+ (unsigned int) to, td->version[chip]);
+
+ /* Mark it as used */
+ td->pages[chip] = page;
+ }
+ return 0;
+}
+
+/**
+ * nand_memory_bbt - [GENERIC] create a memory based bad block table
+ * @mtd: MTD device structure
+ * @bd: descriptor for the good/bad block search pattern
+ *
+ * The function creates a memory based bbt by scanning the device
+ * for manufacturer / software marked good / bad blocks
+*/
+static int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+ struct nand_chip *this = mtd->priv;
+
+ /* Ensure that we only scan for the pattern and nothing else */
+ bd->options = 0;
+ create_bbt (mtd, this->data_buf, bd, -1);
+ return 0;
+}
+
+/**
+ * check_create - [GENERIC] create and write bbt(s) if neccecary
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @bd: descriptor for the good/bad block search pattern
+ *
+ * The function checks the results of the previous call to read_bbt
+ * and creates / updates the bbt(s) if neccecary
+ * Creation is neccecary if no bbt was found for the chip/device
+ * Update is neccecary if one of the tables is missing or the
+ * version nr. of one table is less than the other
+*/
+static int check_create (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
+{
+ int i, chips, writeops, chipsel, res;
+ struct nand_chip *this = mtd->priv;
+ struct nand_bbt_descr *td = this->bbt_td;
+ struct nand_bbt_descr *md = this->bbt_md;
+ struct nand_bbt_descr *rd, *rd2;
+
+ /* Do we have a bbt per chip ? */
+ if (td->options & NAND_BBT_PERCHIP)
+ chips = this->numchips;
+ else
+ chips = 1;
+
+ for (i = 0; i < chips; i++) {
+ writeops = 0;
+ rd = NULL;
+ rd2 = NULL;
+ /* Per chip or per device ? */
+ chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1;
+ /* Mirrored table avilable ? */
+ if (md) {
+ if (td->pages[i] == -1 && md->pages[i] == -1) {
+ writeops = 0x03;
+ goto create;
+ }
+
+ if (td->pages[i] == -1) {
+ rd = md;
+ td->version[i] = md->version[i];
+ writeops = 1;
+ goto writecheck;
+ }
+
+ if (md->pages[i] == -1) {
+ rd = td;
+ md->version[i] = td->version[i];
+ writeops = 2;
+ goto writecheck;
+ }
+
+ if (td->version[i] == md->version[i]) {
+ rd = td;
+ if (!(td->options & NAND_BBT_VERSION))
+ rd2 = md;
+ goto writecheck;
+ }
+
+ if (((int8_t) (td->version[i] - md->version[i])) > 0) {
+ rd = td;
+ md->version[i] = td->version[i];
+ writeops = 2;
+ } else {
+ rd = md;
+ td->version[i] = md->version[i];
+ writeops = 1;
+ }
+
+ goto writecheck;
+
+ } else {
+ if (td->pages[i] == -1) {
+ writeops = 0x01;
+ goto create;
+ }
+ rd = td;
+ goto writecheck;
+ }
+create:
+ /* Create the bad block table by scanning the device ? */
+ if (!(td->options & NAND_BBT_CREATE))
+ continue;
+
+ /* Create the table in memory by scanning the chip(s) */
+ create_bbt (mtd, buf, bd, chipsel);
+
+ td->version[i] = 1;
+ if (md)
+ md->version[i] = 1;
+writecheck:
+ /* read back first ? */
+ if (rd)
+ read_abs_bbt (mtd, buf, rd, chipsel);
+ /* If they weren't versioned, read both. */
+ if (rd2)
+ read_abs_bbt (mtd, buf, rd2, chipsel);
+
+ /* Write the bad block table to the device ? */
+ if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
+ res = write_bbt (mtd, buf, td, md, chipsel);
+ if (res < 0)
+ return res;
+ }
+
+ /* Write the mirror bad block table to the device ? */
+ if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
+ res = write_bbt (mtd, buf, md, td, chipsel);
+ if (res < 0)
+ return res;
+ }
+ }
+ return 0;
+}
+
+/**
+ * mark_bbt_regions - [GENERIC] mark the bad block table regions
+ * @mtd: MTD device structure
+ * @td: bad block table descriptor
+ *
+ * The bad block table regions are marked as "bad" to prevent
+ * accidental erasures / writes. The regions are identified by
+ * the mark 0x02.
+*/
+static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)
+{
+ struct nand_chip *this = mtd->priv;
+ int i, j, chips, block, nrblocks, update;
+ uint8_t oldval, newval;
+
+ /* Do we have a bbt per chip ? */
+ if (td->options & NAND_BBT_PERCHIP) {
+ chips = this->numchips;
+ nrblocks = (int)(this->chipsize >> this->bbt_erase_shift);
+ } else {
+ chips = 1;
+ nrblocks = (int)(mtd->size >> this->bbt_erase_shift);
+ }
+
+ for (i = 0; i < chips; i++) {
+ if ((td->options & NAND_BBT_ABSPAGE) ||
+ !(td->options & NAND_BBT_WRITE)) {
+ if (td->pages[i] == -1) continue;
+ block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);
+ block <<= 1;
+ oldval = this->bbt[(block >> 3)];
+ newval = oldval | (0x2 << (block & 0x06));
+ this->bbt[(block >> 3)] = newval;
+ if ((oldval != newval) && td->reserved_block_code)
+ nand_update_bbt(mtd, block << (this->bbt_erase_shift - 1));
+ continue;
+ }
+ update = 0;
+ if (td->options & NAND_BBT_LASTBLOCK)
+ block = ((i + 1) * nrblocks) - td->maxblocks;
+ else
+ block = i * nrblocks;
+ block <<= 1;
+ for (j = 0; j < td->maxblocks; j++) {
+ oldval = this->bbt[(block >> 3)];
+ newval = oldval | (0x2 << (block & 0x06));
+ this->bbt[(block >> 3)] = newval;
+ if (oldval != newval) update = 1;
+ block += 2;
+ }
+ /* If we want reserved blocks to be recorded to flash, and some
+ new ones have been marked, then we need to update the stored
+ bbts. This should only happen once. */
+ if (update && td->reserved_block_code)
+ nand_update_bbt(mtd, (block - 2) << (this->bbt_erase_shift - 1));
+ }
+}
+
+/**
+ * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
+ * @mtd: MTD device structure
+ * @bd: descriptor for the good/bad block search pattern
+ *
+ * The function checks, if a bad block table(s) is/are already
+ * available. If not it scans the device for manufacturer
+ * marked good / bad blocks and writes the bad block table(s) to
+ * the selected place.
+ *
+ * The bad block table memory is allocated here. It must be freed
+ * by calling the nand_free_bbt function.
+ *
+*/
+int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+ struct nand_chip *this = mtd->priv;
+ int len, res = 0;
+ uint8_t *buf;
+ struct nand_bbt_descr *td = this->bbt_td;
+ struct nand_bbt_descr *md = this->bbt_md;
+
+ len = mtd->size >> (this->bbt_erase_shift + 2);
+ /* Allocate memory (2bit per block) */
+ this->bbt = (uint8_t *) kmalloc (len, GFP_KERNEL);
+ if (!this->bbt) {
+ printk (KERN_ERR "nand_scan_bbt: Out of memory\n");
+ return -ENOMEM;
+ }
+ /* Clear the memory bad block table */
+ memset (this->bbt, 0x00, len);
+
+ /* If no primary table decriptor is given, scan the device
+ * to build a memory based bad block table
+ */
+ if (!td)
+ return nand_memory_bbt(mtd, bd);
+
+ /* Allocate a temporary buffer for one eraseblock incl. oob */
+ len = (1 << this->bbt_erase_shift);
+ len += (len >> this->page_shift) * mtd->oobsize;
+ buf = kmalloc (len, GFP_KERNEL);
+ if (!buf) {
+ printk (KERN_ERR "nand_bbt: Out of memory\n");
+ kfree (this->bbt);
+ this->bbt = NULL;
+ return -ENOMEM;
+ }
+
+ /* Is the bbt at a given page ? */
+ if (td->options & NAND_BBT_ABSPAGE) {
+ res = read_abs_bbts (mtd, buf, td, md);
+ } else {
+ /* Search the bad block table using a pattern in oob */
+ res = search_read_bbts (mtd, buf, td, md);
+ }
+
+ if (res)
+ res = check_create (mtd, buf, bd);
+
+ /* Prevent the bbt regions from erasing / writing */
+ mark_bbt_region (mtd, td);
+ if (md)
+ mark_bbt_region (mtd, md);
+
+ kfree (buf);
+ return res;
+}
+
+
+/**
+ * nand_update_bbt - [NAND Interface] update bad block table(s)
+ * @mtd: MTD device structure
+ * @offs: the offset of the newly marked block
+ *
+ * The function updates the bad block table(s)
+*/
+int nand_update_bbt (struct mtd_info *mtd, loff_t offs)
+{
+ struct nand_chip *this = mtd->priv;
+ int len, res = 0, writeops = 0;
+ int chip, chipsel;
+ uint8_t *buf;
+ struct nand_bbt_descr *td = this->bbt_td;
+ struct nand_bbt_descr *md = this->bbt_md;
+
+ if (!this->bbt || !td)
+ return -EINVAL;
+
+ len = mtd->size >> (this->bbt_erase_shift + 2);
+ /* Allocate a temporary buffer for one eraseblock incl. oob */
+ len = (1 << this->bbt_erase_shift);
+ len += (len >> this->page_shift) * mtd->oobsize;
+ buf = kmalloc (len, GFP_KERNEL);
+ if (!buf) {
+ printk (KERN_ERR "nand_update_bbt: Out of memory\n");
+ return -ENOMEM;
+ }
+
+ writeops = md != NULL ? 0x03 : 0x01;
+
+ /* Do we have a bbt per chip ? */
+ if (td->options & NAND_BBT_PERCHIP) {
+ chip = (int) (offs >> this->chip_shift);
+ chipsel = chip;
+ } else {
+ chip = 0;
+ chipsel = -1;
+ }
+
+ td->version[chip]++;
+ if (md)
+ md->version[chip]++;
+
+ /* Write the bad block table to the device ? */
+ if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
+ res = write_bbt (mtd, buf, td, md, chipsel);
+ if (res < 0)
+ goto out;
+ }
+ /* Write the mirror bad block table to the device ? */
+ if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
+ res = write_bbt (mtd, buf, md, td, chipsel);
+ }
+
+out:
+ kfree (buf);
+ return res;
+}
+
+/* Define some generic bad / good block scan pattern which are used
+ * while scanning a device for factory marked good / bad blocks
+ *
+ * The memory based patterns just
+ */
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr smallpage_memorybased = {
+ .options = 0,
+ .offs = 5,
+ .len = 1,
+ .pattern = scan_ff_pattern
+};
+
+static struct nand_bbt_descr largepage_memorybased = {
+ .options = 0,
+ .offs = 0,
+ .len = 2,
+ .pattern = scan_ff_pattern
+};
+
+static struct nand_bbt_descr smallpage_flashbased = {
+ .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
+ .offs = 5,
+ .len = 1,
+ .pattern = scan_ff_pattern
+};
+
+static struct nand_bbt_descr largepage_flashbased = {
+ .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
+ .offs = 0,
+ .len = 2,
+ .pattern = scan_ff_pattern
+};
+
+static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 };
+
+static struct nand_bbt_descr agand_flashbased = {
+ .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
+ .offs = 0x20,
+ .len = 6,
+ .pattern = scan_agand_pattern
+};
+
+/* Generic flash bbt decriptors
+*/
+static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 8,
+ .len = 4,
+ .veroffs = 12,
+ .maxblocks = 4,
+ .pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 8,
+ .len = 4,
+ .veroffs = 12,
+ .maxblocks = 4,
+ .pattern = mirror_pattern
+};
+
+/**
+ * nand_default_bbt - [NAND Interface] Select a default bad block table for the device
+ * @mtd: MTD device structure
+ *
+ * This function selects the default bad block table
+ * support for the device and calls the nand_scan_bbt function
+ *
+*/
+int nand_default_bbt (struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+
+ /* Default for AG-AND. We must use a flash based
+ * bad block table as the devices have factory marked
+ * _good_ blocks. Erasing those blocks leads to loss
+ * of the good / bad information, so we _must_ store
+ * this information in a good / bad table during
+ * startup
+ */
+ if (this->options & NAND_IS_AND) {
+ /* Use the default pattern descriptors */
+ if (!this->bbt_td) {
+ this->bbt_td = &bbt_main_descr;
+ this->bbt_md = &bbt_mirror_descr;
+ }
+ this->options |= NAND_USE_FLASH_BBT;
+ return nand_scan_bbt (mtd, &agand_flashbased);
+ }
+
+ /* Is a flash based bad block table requested ? */
+ if (this->options & NAND_USE_FLASH_BBT) {
+ /* Use the default pattern descriptors */
+ if (!this->bbt_td) {
+ this->bbt_td = &bbt_main_descr;
+ this->bbt_md = &bbt_mirror_descr;
+ }
+ if (mtd->oobblock > 512)
+ return nand_scan_bbt (mtd, &largepage_flashbased);
+ else
+ return nand_scan_bbt (mtd, &smallpage_flashbased);
+ } else {
+ this->bbt_td = NULL;
+ this->bbt_md = NULL;
+ if (mtd->oobblock > 512)
+ return nand_scan_bbt (mtd, &largepage_memorybased);
+ else
+ return nand_scan_bbt (mtd, &smallpage_memorybased);
+ }
+}
+
+/**
+ * nand_isbad_bbt - [NAND Interface] Check if a block is bad
+ * @mtd: MTD device structure
+ * @offs: offset in the device
+ * @allowbbt: allow access to bad block table region
+ *
+*/
+int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt)
+{
+ struct nand_chip *this = mtd->priv;
+ int block;
+ uint8_t res;
+
+ /* Get block number * 2 */
+ block = (int) (offs >> (this->bbt_erase_shift - 1));
+ res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
+
+ DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
+ (unsigned int)offs, res, block >> 1);
+
+ switch ((int)res) {
+ case 0x00: return 0;
+ case 0x01: return 1;
+ case 0x02: return allowbbt ? 0 : 1;
+ }
+ return 1;
+}
+
+EXPORT_SYMBOL (nand_scan_bbt);
+EXPORT_SYMBOL (nand_default_bbt);
--- /dev/null
+/*
+ * drivers/mtd/nand/ppchameleonevb.c
+ *
+ * Copyright (C) 2003 DAVE Srl (info@wawnet.biz)
+ *
+ * Derived from drivers/mtd/nand/edb7312.c
+ *
+ *
+ * $Id: ppchameleonevb.c,v 1.2 2004/05/05 22:09:54 gleixner Exp $
+ *
+ * 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.
+ *
+ * Overview:
+ * This is a device driver for the NAND flash devices found on the
+ * PPChameleon/PPChameleonEVB system.
+ * PPChameleon options (autodetected):
+ * - BA model: no NAND
+ * - ME model: 32MB (Samsung K9F5608U0B)
+ * - HI model: 128MB (Samsung K9F1G08UOM)
+ * PPChameleonEVB options:
+ * - 32MB (Samsung K9F5608U0B)
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+#include <platforms/PPChameleonEVB.h>
+
+#undef USE_READY_BUSY_PIN
+#define USE_READY_BUSY_PIN
+/* see datasheets (tR) */
+#define NAND_BIG_DELAY_US 25
+#define NAND_SMALL_DELAY_US 10
+
+/* handy sizes */
+#define SZ_4M 0x00400000
+#define NAND_SMALL_SIZE 0x02000000
+#define NAND_MTD_NAME "ppchameleon-nand"
+#define NAND_EVB_MTD_NAME "ppchameleonevb-nand"
+
+/* GPIO pins used to drive NAND chip mounted on processor module */
+#define NAND_nCE_GPIO_PIN (0x80000000 >> 1)
+#define NAND_CLE_GPIO_PIN (0x80000000 >> 2)
+#define NAND_ALE_GPIO_PIN (0x80000000 >> 3)
+#define NAND_RB_GPIO_PIN (0x80000000 >> 4)
+/* GPIO pins used to drive NAND chip mounted on EVB */
+#define NAND_EVB_nCE_GPIO_PIN (0x80000000 >> 14)
+#define NAND_EVB_CLE_GPIO_PIN (0x80000000 >> 15)
+#define NAND_EVB_ALE_GPIO_PIN (0x80000000 >> 16)
+#define NAND_EVB_RB_GPIO_PIN (0x80000000 >> 31)
+
+/*
+ * MTD structure for PPChameleonEVB board
+ */
+static struct mtd_info *ppchameleon_mtd = NULL;
+static struct mtd_info *ppchameleonevb_mtd = NULL;
+
+/*
+ * Module stuff
+ */
+static int ppchameleon_fio_pbase = CFG_NAND0_PADDR;
+static int ppchameleonevb_fio_pbase = CFG_NAND1_PADDR;
+
+#ifdef MODULE
+MODULE_PARM(ppchameleon_fio_pbase, "i");
+__setup("ppchameleon_fio_pbase=",ppchameleon_fio_pbase);
+MODULE_PARM(ppchameleonevb_fio_pbase, "i");
+__setup("ppchameleonevb_fio_pbase=",ppchameleonevb_fio_pbase);
+#endif
+
+/* Internal buffers. Page buffer and oob buffer for one block */
+static u_char data_buf[2048 + 64];
+static u_char oob_buf[64 * 64];
+static u_char data_buf_evb[512 + 16];
+static u_char oob_buf_evb[16 * 32];
+
+#ifdef CONFIG_MTD_PARTITIONS
+/*
+ * Define static partitions for flash devices
+ */
+static struct mtd_partition partition_info_hi[] = {
+ { name: "PPChameleon HI Nand Flash",
+ offset: 0,
+ size: 128*1024*1024 }
+};
+
+static struct mtd_partition partition_info_me[] = {
+ { name: "PPChameleon ME Nand Flash",
+ offset: 0,
+ size: 32*1024*1024 }
+};
+
+static struct mtd_partition partition_info_evb[] = {
+ { name: "PPChameleonEVB Nand Flash",
+ offset: 0,
+ size: 32*1024*1024 }
+};
+
+#define NUM_PARTITIONS 1
+
+extern int parse_cmdline_partitions(struct mtd_info *master,
+ struct mtd_partition **pparts,
+ const char *mtd_id);
+#endif
+
+
+/*
+ * hardware specific access to control-lines
+ */
+static void ppchameleon_hwcontrol(struct mtd_info *mtdinfo, int cmd)
+{
+ switch(cmd) {
+
+ case NAND_CTL_SETCLE:
+ MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND0_PADDR);
+ break;
+ case NAND_CTL_CLRCLE:
+ MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND0_PADDR);
+ break;
+ case NAND_CTL_SETALE:
+ MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND0_PADDR);
+ break;
+ case NAND_CTL_CLRALE:
+ MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND0_PADDR);
+ break;
+ case NAND_CTL_SETNCE:
+ MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND0_PADDR);
+ break;
+ case NAND_CTL_CLRNCE:
+ MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND0_PADDR);
+ break;
+ }
+}
+
+static void ppchameleonevb_hwcontrol(struct mtd_info *mtdinfo, int cmd)
+{
+ switch(cmd) {
+
+ case NAND_CTL_SETCLE:
+ MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND1_PADDR);
+ break;
+ case NAND_CTL_CLRCLE:
+ MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND1_PADDR);
+ break;
+ case NAND_CTL_SETALE:
+ MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND1_PADDR);
+ break;
+ case NAND_CTL_CLRALE:
+ MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND1_PADDR);
+ break;
+ case NAND_CTL_SETNCE:
+ MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND1_PADDR);
+ break;
+ case NAND_CTL_CLRNCE:
+ MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND1_PADDR);
+ break;
+ }
+}
+
+#ifdef USE_READY_BUSY_PIN
+/*
+ * read device ready pin
+ */
+static int ppchameleon_device_ready(struct mtd_info *minfo)
+{
+ if (in_be32((volatile unsigned*)GPIO0_IR) & NAND_RB_GPIO_PIN)
+ return 1;
+ return 0;
+}
+
+static int ppchameleonevb_device_ready(struct mtd_info *minfo)
+{
+ if (in_be32((volatile unsigned*)GPIO0_IR) & NAND_EVB_RB_GPIO_PIN)
+ return 1;
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_MTD_PARTITIONS
+const char *part_probes[] = { "cmdlinepart", NULL };
+const char *part_probes_evb[] = { "cmdlinepart", NULL };
+#endif
+
+/*
+ * Main initialization routine
+ */
+static int __init ppchameleonevb_init (void)
+{
+ struct nand_chip *this;
+ const char *part_type = 0;
+ int mtd_parts_nb = 0;
+ struct mtd_partition *mtd_parts = 0;
+ int ppchameleon_fio_base;
+ int ppchameleonevb_fio_base;
+
+
+ /*********************************
+ * Processor module NAND (if any) *
+ *********************************/
+ /* Allocate memory for MTD device structure and private data */
+ ppchameleon_mtd = kmalloc(sizeof(struct mtd_info) +
+ sizeof(struct nand_chip),
+ GFP_KERNEL);
+ if (!ppchameleon_mtd) {
+ printk("Unable to allocate PPChameleon NAND MTD device structure.\n");
+ return -ENOMEM;
+ }
+
+ /* map physical address */
+ ppchameleon_fio_base = (unsigned long)ioremap(ppchameleon_fio_pbase, SZ_4M);
+ if(!ppchameleon_fio_base) {
+ printk("ioremap PPChameleon NAND flash failed\n");
+ kfree(ppchameleon_mtd);
+ return -EIO;
+ }
+
+ /* Get pointer to private data */
+ this = (struct nand_chip *) (&ppchameleon_mtd[1]);
+
+ /* Initialize structures */
+ memset((char *) ppchameleon_mtd, 0, sizeof(struct mtd_info));
+ memset((char *) this, 0, sizeof(struct nand_chip));
+
+ /* Link the private data with the MTD structure */
+ ppchameleon_mtd->priv = this;
+
+ /* Initialize GPIOs */
+ /* Pin mapping for NAND chip */
+ /*
+ CE GPIO_01
+ CLE GPIO_02
+ ALE GPIO_03
+ R/B GPIO_04
+ */
+ /* output select */
+ out_be32((volatile unsigned*)GPIO0_OSRH, in_be32((volatile unsigned*)GPIO0_OSRH) & 0xC0FFFFFF);
+ /* three-state select */
+ out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xC0FFFFFF);
+ /* enable output driver */
+ out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) | NAND_nCE_GPIO_PIN | NAND_CLE_GPIO_PIN | NAND_ALE_GPIO_PIN);
+#ifdef USE_READY_BUSY_PIN
+ /* three-state select */
+ out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xFF3FFFFF);
+ /* high-impedecence */
+ out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) & (~NAND_RB_GPIO_PIN));
+ /* input select */
+ out_be32((volatile unsigned*)GPIO0_ISR1H, (in_be32((volatile unsigned*)GPIO0_ISR1H) & 0xFF3FFFFF) | 0x00400000);
+#endif
+
+ /* insert callbacks */
+ this->IO_ADDR_R = ppchameleon_fio_base;
+ this->IO_ADDR_W = ppchameleon_fio_base;
+ this->hwcontrol = ppchameleon_hwcontrol;
+#ifdef USE_READY_BUSY_PIN
+ this->dev_ready = ppchameleon_device_ready;
+#endif
+ this->chip_delay = NAND_BIG_DELAY_US;
+ /* ECC mode */
+ this->eccmode = NAND_ECC_SOFT;
+
+ /* Set internal data buffer */
+ this->data_buf = data_buf;
+ this->oob_buf = oob_buf;
+
+ /* Scan to find existence of the device (it could not be mounted) */
+ if (nand_scan (ppchameleon_mtd, 1)) {
+ iounmap((void *)ppchameleon_fio_base);
+ kfree (ppchameleon_mtd);
+ goto nand_evb_init;
+ }
+
+#ifndef USE_READY_BUSY_PIN
+ /* Adjust delay if necessary */
+ if (ppchameleon_mtd->size == NAND_SMALL_SIZE)
+ this->chip_delay = NAND_SMALL_DELAY_US;
+#endif
+
+#ifdef CONFIG_MTD_PARTITIONS
+ ppchameleon_mtd->name = "ppchameleon-nand";
+ mtd_parts_nb = parse_mtd_partitions(ppchameleon_mtd, part_probes, &mtd_parts, 0);
+ if (mtd_parts_nb > 0)
+ part_type = "command line";
+ else
+ mtd_parts_nb = 0;
+#endif
+ if (mtd_parts_nb == 0)
+ {
+ if (ppchameleon_mtd->size == NAND_SMALL_SIZE)
+ mtd_parts = partition_info_me;
+ else
+ mtd_parts = partition_info_hi;
+ mtd_parts_nb = NUM_PARTITIONS;
+ part_type = "static";
+ }
+
+ /* Register the partitions */
+ printk(KERN_NOTICE "Using %s partition definition\n", part_type);
+ add_mtd_partitions(ppchameleon_mtd, mtd_parts, mtd_parts_nb);
+
+nand_evb_init:
+ /****************************
+ * EVB NAND (always present) *
+ ****************************/
+ /* Allocate memory for MTD device structure and private data */
+ ppchameleonevb_mtd = kmalloc(sizeof(struct mtd_info) +
+ sizeof(struct nand_chip),
+ GFP_KERNEL);
+ if (!ppchameleonevb_mtd) {
+ printk("Unable to allocate PPChameleonEVB NAND MTD device structure.\n");
+ return -ENOMEM;
+ }
+
+ /* map physical address */
+ ppchameleonevb_fio_base = (unsigned long)ioremap(ppchameleonevb_fio_pbase, SZ_4M);
+ if(!ppchameleonevb_fio_base) {
+ printk("ioremap PPChameleonEVB NAND flash failed\n");
+ kfree(ppchameleonevb_mtd);
+ return -EIO;
+ }
+
+ /* Get pointer to private data */
+ this = (struct nand_chip *) (&ppchameleonevb_mtd[1]);
+
+ /* Initialize structures */
+ memset((char *) ppchameleonevb_mtd, 0, sizeof(struct mtd_info));
+ memset((char *) this, 0, sizeof(struct nand_chip));
+
+ /* Link the private data with the MTD structure */
+ ppchameleonevb_mtd->priv = this;
+
+ /* Initialize GPIOs */
+ /* Pin mapping for NAND chip */
+ /*
+ CE GPIO_14
+ CLE GPIO_15
+ ALE GPIO_16
+ R/B GPIO_31
+ */
+ /* output select */
+ out_be32((volatile unsigned*)GPIO0_OSRH, in_be32((volatile unsigned*)GPIO0_OSRH) & 0xFFFFFFF0);
+ out_be32((volatile unsigned*)GPIO0_OSRL, in_be32((volatile unsigned*)GPIO0_OSRL) & 0x3FFFFFFF);
+ /* three-state select */
+ out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xFFFFFFF0);
+ out_be32((volatile unsigned*)GPIO0_TSRL, in_be32((volatile unsigned*)GPIO0_TSRL) & 0x3FFFFFFF);
+ /* enable output driver */
+ out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) | NAND_EVB_nCE_GPIO_PIN | NAND_EVB_CLE_GPIO_PIN | NAND_EVB_ALE_GPIO_PIN);
+#ifdef USE_READY_BUSY_PIN
+ /* three-state select */
+ out_be32((volatile unsigned*)GPIO0_TSRL, in_be32((volatile unsigned*)GPIO0_TSRL) & 0xFFFFFFFC);
+ /* high-impedecence */
+ out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) & (~NAND_EVB_RB_GPIO_PIN));
+ /* input select */
+ out_be32((volatile unsigned*)GPIO0_ISR1L, (in_be32((volatile unsigned*)GPIO0_ISR1L) & 0xFFFFFFFC) | 0x00000001);
+#endif
+
+
+ /* insert callbacks */
+ this->IO_ADDR_R = ppchameleonevb_fio_base;
+ this->IO_ADDR_W = ppchameleonevb_fio_base;
+ this->hwcontrol = ppchameleonevb_hwcontrol;
+#ifdef USE_READY_BUSY_PIN
+ this->dev_ready = ppchameleonevb_device_ready;
+#endif
+ this->chip_delay = NAND_SMALL_DELAY_US;
+
+ /* ECC mode */
+ this->eccmode = NAND_ECC_SOFT;
+
+ /* Set internal data buffer */
+ this->data_buf = data_buf_evb;
+ this->oob_buf = oob_buf_evb;
+
+ /* Scan to find existence of the device */
+ if (nand_scan (ppchameleonevb_mtd, 1)) {
+ iounmap((void *)ppchameleonevb_fio_base);
+ kfree (ppchameleonevb_mtd);
+ return -ENXIO;
+ }
+
+#ifdef CONFIG_MTD_PARTITIONS
+ ppchameleonevb_mtd->name = NAND_EVB_MTD_NAME;
+ mtd_parts_nb = parse_mtd_partitions(ppchameleonevb_mtd, part_probes_evb, &mtd_parts, 0);
+ if (mtd_parts_nb > 0)
+ part_type = "command line";
+ else
+ mtd_parts_nb = 0;
+#endif
+ if (mtd_parts_nb == 0)
+ {
+ mtd_parts = partition_info_evb;
+ mtd_parts_nb = NUM_PARTITIONS;
+ part_type = "static";
+ }
+
+ /* Register the partitions */
+ printk(KERN_NOTICE "Using %s partition definition\n", part_type);
+ add_mtd_partitions(ppchameleonevb_mtd, mtd_parts, mtd_parts_nb);
+
+ /* Return happy */
+ return 0;
+}
+module_init(ppchameleonevb_init);
+
+/*
+ * Clean up routine
+ */
+static void __exit ppchameleonevb_cleanup (void)
+{
+ struct nand_chip *this = (struct nand_chip *) &ppchameleonevb_mtd[1];
+
+ /* Unregister the device */
+ del_mtd_device (ppchameleonevb_mtd);
+
+ /* Free internal data buffer */
+ kfree (this->data_buf);
+
+ /* Free the MTD device structure */
+ kfree (ppchameleonevb_mtd);
+}
+module_exit(ppchameleonevb_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("DAVE Srl <support-ppchameleon@dave-tech.it>");
+MODULE_DESCRIPTION("MTD map driver for DAVE Srl PPChameleonEVB board");
--- /dev/null
+/*
+ * drivers/mtd/nand/toto.c
+ *
+ * Copyright (c) 2003 Texas Instruments
+ *
+ * Derived from drivers/mtd/autcpu12.c
+ *
+ * Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.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.
+ *
+ * Overview:
+ * This is a device driver for the NAND flash device found on the
+ * TI fido board. It supports 32MiB and 64MiB cards
+ *
+ * $Id: toto.c,v 1.2 2003/10/21 10:04:58 dwmw2 Exp $
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+#include <asm/arch/hardware.h>
+#include <asm/sizes.h>
+#include <asm/arch/toto.h>
+#include <asm/arch-omap1510/hardware.h>
+#include <asm/arch/gpio.h>
+
+/*
+ * MTD structure for TOTO board
+ */
+static struct mtd_info *toto_mtd = NULL;
+
+static int toto_io_base = OMAP_FLASH_1_BASE;
+
+#define CONFIG_NAND_WORKAROUND 1
+
+#define NAND_NCE 0x4000
+#define NAND_CLE 0x1000
+#define NAND_ALE 0x0002
+#define NAND_MASK (NAND_CLE | NAND_ALE | NAND_NCE)
+
+#define T_NAND_CTL_CLRALE(iob) gpiosetout(NAND_ALE, 0)
+#define T_NAND_CTL_SETALE(iob) gpiosetout(NAND_ALE, NAND_ALE)
+#ifdef CONFIG_NAND_WORKAROUND /* "some" dev boards busted, blue wired to rts2 :( */
+#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0); rts2setout(2, 2)
+#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE); rts2setout(2, 0)
+#else
+#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0)
+#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE)
+#endif
+#define T_NAND_CTL_SETNCE(iob) gpiosetout(NAND_NCE, 0)
+#define T_NAND_CTL_CLRNCE(iob) gpiosetout(NAND_NCE, NAND_NCE)
+
+/*
+ * Define partitions for flash devices
+ */
+
+static struct mtd_partition partition_info64M[] = {
+ { .name = "toto kernel partition 1",
+ .offset = 0,
+ .size = 2 * SZ_1M },
+ { .name = "toto file sys partition 2",
+ .offset = 2 * SZ_1M,
+ .size = 14 * SZ_1M },
+ { .name = "toto user partition 3",
+ .offset = 16 * SZ_1M,
+ .size = 16 * SZ_1M },
+ { .name = "toto devboard extra partition 4",
+ .offset = 32 * SZ_1M,
+ .size = 32 * SZ_1M },
+};
+
+static struct mtd_partition partition_info32M[] = {
+ { .name = "toto kernel partition 1",
+ .offset = 0,
+ .size = 2 * SZ_1M },
+ { .name = "toto file sys partition 2",
+ .offset = 2 * SZ_1M,
+ .size = 14 * SZ_1M },
+ { .name = "toto user partition 3",
+ .offset = 16 * SZ_1M,
+ .size = 16 * SZ_1M },
+};
+
+#define NUM_PARTITIONS32M 3
+#define NUM_PARTITIONS64M 4
+/*
+ * hardware specific access to control-lines
+*/
+
+static void toto_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+
+ udelay(1); /* hopefully enough time for tc make proceding write to clear */
+ switch(cmd){
+
+ case NAND_CTL_SETCLE: T_NAND_CTL_SETCLE(cmd); break;
+ case NAND_CTL_CLRCLE: T_NAND_CTL_CLRCLE(cmd); break;
+
+ case NAND_CTL_SETALE: T_NAND_CTL_SETALE(cmd); break;
+ case NAND_CTL_CLRALE: T_NAND_CTL_CLRALE(cmd); break;
+
+ case NAND_CTL_SETNCE: T_NAND_CTL_SETNCE(cmd); break;
+ case NAND_CTL_CLRNCE: T_NAND_CTL_CLRNCE(cmd); break;
+ }
+ udelay(1); /* allow time to ensure gpio state to over take memory write */
+}
+
+/*
+ * Main initialization routine
+ */
+int __init toto_init (void)
+{
+ struct nand_chip *this;
+ int err = 0;
+
+ /* Allocate memory for MTD device structure and private data */
+ toto_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
+ GFP_KERNEL);
+ if (!toto_mtd) {
+ printk (KERN_WARNING "Unable to allocate toto NAND MTD device structure.\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /* Get pointer to private data */
+ this = (struct nand_chip *) (&toto_mtd[1]);
+
+ /* Initialize structures */
+ memset((char *) toto_mtd, 0, sizeof(struct mtd_info));
+ memset((char *) this, 0, sizeof(struct nand_chip));
+
+ /* Link the private data with the MTD structure */
+ toto_mtd->priv = this;
+
+ /* Set address of NAND IO lines */
+ this->IO_ADDR_R = toto_io_base;
+ this->IO_ADDR_W = toto_io_base;
+ this->hwcontrol = toto_hwcontrol;
+ this->dev_ready = NULL;
+ /* 25 us command delay time */
+ this->chip_delay = 30;
+ this->eccmode = NAND_ECC_SOFT;
+
+ /* Scan to find existance of the device */
+ if (nand_scan (toto_mtd, 1)) {
+ err = -ENXIO;
+ goto out_mtd;
+ }
+
+ /* Allocate memory for internal data buffer */
+ this->data_buf = kmalloc (sizeof(u_char) * (toto_mtd->oobblock + toto_mtd->oobsize), GFP_KERNEL);
+ if (!this->data_buf) {
+ printk (KERN_WARNING "Unable to allocate NAND data buffer for toto.\n");
+ err = -ENOMEM;
+ goto out_mtd;
+ }
+
+ /* Register the partitions */
+ switch(toto_mtd->size){
+ case SZ_64M: add_mtd_partitions(toto_mtd, partition_info64M, NUM_PARTITIONS64M); break;
+ case SZ_32M: add_mtd_partitions(toto_mtd, partition_info32M, NUM_PARTITIONS32M); break;
+ default: {
+ printk (KERN_WARNING "Unsupported Nand device\n");
+ err = -ENXIO;
+ goto out_buf;
+ }
+ }
+
+ gpioreserve(NAND_MASK); /* claim our gpios */
+ archflashwp(0,0); /* open up flash for writing */
+
+ goto out;
+
+out_buf:
+ kfree (this->data_buf);
+out_mtd:
+ kfree (toto_mtd);
+out:
+ return err;
+}
+
+module_init(toto_init);
+
+/*
+ * Clean up routine
+ */
+static void __exit toto_cleanup (void)
+{
+ struct nand_chip *this = (struct nand_chip *) &toto_mtd[1];
+
+ /* Unregister partitions */
+ del_mtd_partitions(toto_mtd);
+
+ /* Unregister the device */
+ del_mtd_device (toto_mtd);
+
+ /* Free internal data buffers */
+ kfree (this->data_buf);
+
+ /* Free the MTD device structure */
+ kfree (toto_mtd);
+
+ /* stop flash writes */
+ archflashwp(0,1);
+
+ /* release gpios to system */
+ gpiorelease(NAND_MASK);
+}
+module_exit(toto_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Richard Woodruff <r-woodruff2@ti.com>");
+MODULE_DESCRIPTION("Glue layer for NAND flash on toto board");
* Derived from drivers/mtd/autcpu12.c
* Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
*
- * $Id: tx4925ndfmc.c,v 1.2 2004/03/27 19:55:53 gleixner Exp $
+ * $Id: tx4925ndfmc.c,v 1.3 2004/07/20 02:44:26 dwmw2 Exp $
*
* Copyright (C) 2001 Toshiba Corporation
*
*/
static struct mtd_info *tx4925ndfmc_mtd = NULL;
-/*
- * Module stuff
- */
-#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
-#define tx4925ndfmc_init init_module
-#define tx4925ndfmc_cleanup cleanup_module
-#endif
-
/*
* Define partitions for flash devices
*/
--- /dev/null
+/*
+ * drivers/mtd/nand/tx4938ndfmc.c
+ *
+ * Overview:
+ * This is a device driver for the NAND flash device connected to
+ * TX4938 internal NAND Memory Controller.
+ * TX4938 NDFMC is almost same as TX4925 NDFMC, but register size are 64 bit.
+ *
+ * Author: source@mvista.com
+ *
+ * Based on spia.c by Steven J. Hill
+ *
+ * $Id: tx4938ndfmc.c,v 1.2 2004/03/27 19:55:53 gleixner Exp $
+ *
+ * Copyright (C) 2000-2001 Toshiba Corporation
+ *
+ * 2003 (c) MontaVista Software, Inc. 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/config.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+#include <asm/bootinfo.h>
+#include <linux/delay.h>
+#include <asm/tx4938/rbtx4938.h>
+
+extern struct nand_oobinfo jffs2_oobinfo;
+
+/*
+ * MTD structure for TX4938 NDFMC
+ */
+static struct mtd_info *tx4938ndfmc_mtd;
+
+/*
+ * Define partitions for flash device
+ */
+#define flush_wb() (void)tx4938_ndfmcptr->mcr;
+
+#define NUM_PARTITIONS 3
+#define NUMBER_OF_CIS_BLOCKS 24
+#define SIZE_OF_BLOCK 0x00004000
+#define NUMBER_OF_BLOCK_PER_ZONE 1024
+#define SIZE_OF_ZONE (NUMBER_OF_BLOCK_PER_ZONE * SIZE_OF_BLOCK)
+#ifndef CONFIG_MTD_CMDLINE_PARTS
+/*
+ * You can use the following sample of MTD partitions
+ * on the NAND Flash Memory 32MB or more.
+ *
+ * The following figure shows the image of the sample partition on
+ * the 32MB NAND Flash Memory.
+ *
+ * Block No.
+ * 0 +-----------------------------+ ------
+ * | CIS | ^
+ * 24 +-----------------------------+ |
+ * | kernel image | | Zone 0
+ * | | |
+ * +-----------------------------+ |
+ * 1023 | unused area | v
+ * +-----------------------------+ ------
+ * 1024 | JFFS2 | ^
+ * | | |
+ * | | | Zone 1
+ * | | |
+ * | | |
+ * | | v
+ * 2047 +-----------------------------+ ------
+ *
+ */
+static struct mtd_partition partition_info[NUM_PARTITIONS] = {
+ {
+ .name = "RBTX4938 CIS Area",
+ .offset = 0,
+ .size = (NUMBER_OF_CIS_BLOCKS * SIZE_OF_BLOCK),
+ .mask_flags = MTD_WRITEABLE /* This partition is NOT writable */
+ },
+ {
+ .name = "RBTX4938 kernel image",
+ .offset = MTDPART_OFS_APPEND,
+ .size = 8 * 0x00100000, /* 8MB (Depends on size of kernel image) */
+ .mask_flags = MTD_WRITEABLE /* This partition is NOT writable */
+ },
+ {
+ .name = "Root FS (JFFS2)",
+ .offset = (0 + SIZE_OF_ZONE), /* start address of next zone */
+ .size = MTDPART_SIZ_FULL
+ },
+};
+#endif
+
+static void tx4938ndfmc_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+ switch (cmd) {
+ case NAND_CTL_SETCLE:
+ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CLE;
+ break;
+ case NAND_CTL_CLRCLE:
+ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CLE;
+ break;
+ case NAND_CTL_SETALE:
+ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_ALE;
+ break;
+ case NAND_CTL_CLRALE:
+ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_ALE;
+ break;
+ /* TX4938_NDFMCR_CE bit is 0:high 1:low */
+ case NAND_CTL_SETNCE:
+ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CE;
+ break;
+ case NAND_CTL_CLRNCE:
+ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CE;
+ break;
+ case NAND_CTL_SETWP:
+ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_WE;
+ break;
+ case NAND_CTL_CLRWP:
+ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_WE;
+ break;
+ }
+}
+static int tx4938ndfmc_dev_ready(struct mtd_info *mtd)
+{
+ flush_wb();
+ return !(tx4938_ndfmcptr->sr & TX4938_NDFSR_BUSY);
+}
+static void tx4938ndfmc_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
+{
+ u32 mcr = tx4938_ndfmcptr->mcr;
+ mcr &= ~TX4938_NDFMCR_ECC_ALL;
+ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
+ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_READ;
+ ecc_code[1] = tx4938_ndfmcptr->dtr;
+ ecc_code[0] = tx4938_ndfmcptr->dtr;
+ ecc_code[2] = tx4938_ndfmcptr->dtr;
+ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
+}
+static void tx4938ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ u32 mcr = tx4938_ndfmcptr->mcr;
+ mcr &= ~TX4938_NDFMCR_ECC_ALL;
+ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_RESET;
+ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
+ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_ON;
+}
+
+static u_char tx4938ndfmc_nand_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+ return tx4938_read_nfmc(this->IO_ADDR_R);
+}
+
+static void tx4938ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte)
+{
+ struct nand_chip *this = mtd->priv;
+ tx4938_write_nfmc(byte, this->IO_ADDR_W);
+}
+
+static void tx4938ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ int i;
+ struct nand_chip *this = mtd->priv;
+
+ for (i=0; i<len; i++)
+ tx4938_write_nfmc(buf[i], this->IO_ADDR_W);
+}
+
+static void tx4938ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+ int i;
+ struct nand_chip *this = mtd->priv;
+
+ for (i=0; i<len; i++)
+ buf[i] = tx4938_read_nfmc(this->IO_ADDR_R);
+}
+
+static int tx4938ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+ int i;
+ struct nand_chip *this = mtd->priv;
+
+ for (i=0; i<len; i++)
+ if (buf[i] != tx4938_read_nfmc(this->IO_ADDR_R))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * Send command to NAND device
+ */
+static void tx4938ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
+{
+ register struct nand_chip *this = mtd->priv;
+
+ /* Begin command latch cycle */
+ this->hwcontrol(mtd, NAND_CTL_SETCLE);
+ /*
+ * Write out the command to the device.
+ */
+ if (command == NAND_CMD_SEQIN) {
+ int readcmd;
+
+ if (column >= mtd->oobblock) {
+ /* OOB area */
+ column -= mtd->oobblock;
+ readcmd = NAND_CMD_READOOB;
+ } else if (column < 256) {
+ /* First 256 bytes --> READ0 */
+ readcmd = NAND_CMD_READ0;
+ } else {
+ column -= 256;
+ readcmd = NAND_CMD_READ1;
+ }
+ this->write_byte(mtd, readcmd);
+ }
+ this->write_byte(mtd, command);
+
+ /* Set ALE and clear CLE to start address cycle */
+ this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+
+ if (column != -1 || page_addr != -1) {
+ this->hwcontrol(mtd, NAND_CTL_SETALE);
+
+ /* Serially input address */
+ if (column != -1)
+ this->write_byte(mtd, column);
+ if (page_addr != -1) {
+ this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
+ this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
+ /* One more address cycle for higher density devices */
+ if (mtd->size & 0x0c000000)
+ this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
+ }
+ /* Latch in address */
+ this->hwcontrol(mtd, NAND_CTL_CLRALE);
+ }
+
+ /*
+ * program and erase have their own busy handlers
+ * status and sequential in needs no delay
+ */
+ switch (command) {
+
+ case NAND_CMD_PAGEPROG:
+ /* Turn off WE */
+ this->hwcontrol (mtd, NAND_CTL_CLRWP);
+ return;
+
+ case NAND_CMD_SEQIN:
+ /* Turn on WE */
+ this->hwcontrol (mtd, NAND_CTL_SETWP);
+ return;
+
+ case NAND_CMD_ERASE1:
+ case NAND_CMD_ERASE2:
+ case NAND_CMD_STATUS:
+ return;
+
+ case NAND_CMD_RESET:
+ if (this->dev_ready)
+ break;
+ this->hwcontrol(mtd, NAND_CTL_SETCLE);
+ this->write_byte(mtd, NAND_CMD_STATUS);
+ this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+ while ( !(this->read_byte(mtd) & 0x40));
+ return;
+
+ /* This applies to read commands */
+ default:
+ /*
+ * If we don't have access to the busy pin, we apply the given
+ * command delay
+ */
+ if (!this->dev_ready) {
+ udelay (this->chip_delay);
+ return;
+ }
+ }
+
+ /* wait until command is processed */
+ while (!this->dev_ready(mtd));
+}
+
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *);
+#endif
+/*
+ * Main initialization routine
+ */
+int __init tx4938ndfmc_init (void)
+{
+ struct nand_chip *this;
+ int bsprt = 0, hold = 0xf, spw = 0xf;
+ int protected = 0;
+
+ if ((*rbtx4938_piosel_ptr & 0x0c) != 0x08) {
+ printk("TX4938 NDFMC: disabled by IOC PIOSEL\n");
+ return -ENODEV;
+ }
+ bsprt = 1;
+ hold = 2;
+ spw = 9 - 1; /* 8 GBUSCLK = 80ns (@ GBUSCLK 100MHz) */
+
+ if ((tx4938_ccfgptr->pcfg &
+ (TX4938_PCFG_ATA_SEL|TX4938_PCFG_ISA_SEL|TX4938_PCFG_NDF_SEL))
+ != TX4938_PCFG_NDF_SEL) {
+ printk("TX4938 NDFMC: disabled by PCFG.\n");
+ return -ENODEV;
+ }
+
+ /* reset NDFMC */
+ tx4938_ndfmcptr->rstr |= TX4938_NDFRSTR_RST;
+ while (tx4938_ndfmcptr->rstr & TX4938_NDFRSTR_RST)
+ ;
+ /* setup BusSeparete, Hold Time, Strobe Pulse Width */
+ tx4938_ndfmcptr->mcr = bsprt ? TX4938_NDFMCR_BSPRT : 0;
+ tx4938_ndfmcptr->spr = hold << 4 | spw;
+
+ /* Allocate memory for MTD device structure and private data */
+ tx4938ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
+ GFP_KERNEL);
+ if (!tx4938ndfmc_mtd) {
+ printk ("Unable to allocate TX4938 NDFMC MTD device structure.\n");
+ return -ENOMEM;
+ }
+
+ /* Get pointer to private data */
+ this = (struct nand_chip *) (&tx4938ndfmc_mtd[1]);
+
+ /* Initialize structures */
+ memset((char *) tx4938ndfmc_mtd, 0, sizeof(struct mtd_info));
+ memset((char *) this, 0, sizeof(struct nand_chip));
+
+ /* Link the private data with the MTD structure */
+ tx4938ndfmc_mtd->priv = this;
+
+ /* Set address of NAND IO lines */
+ this->IO_ADDR_R = (unsigned long)&tx4938_ndfmcptr->dtr;
+ this->IO_ADDR_W = (unsigned long)&tx4938_ndfmcptr->dtr;
+ this->hwcontrol = tx4938ndfmc_hwcontrol;
+ this->dev_ready = tx4938ndfmc_dev_ready;
+ this->calculate_ecc = tx4938ndfmc_calculate_ecc;
+ this->correct_data = nand_correct_data;
+ this->enable_hwecc = tx4938ndfmc_enable_hwecc;
+ this->eccmode = NAND_ECC_HW3_256;
+ this->chip_delay = 100;
+ this->read_byte = tx4938ndfmc_nand_read_byte;
+ this->write_byte = tx4938ndfmc_nand_write_byte;
+ this->cmdfunc = tx4938ndfmc_nand_command;
+ this->write_buf = tx4938ndfmc_nand_write_buf;
+ this->read_buf = tx4938ndfmc_nand_read_buf;
+ this->verify_buf = tx4938ndfmc_nand_verify_buf;
+
+ /* Scan to find existance of the device */
+ if (nand_scan (tx4938ndfmc_mtd, 1)) {
+ kfree (tx4938ndfmc_mtd);
+ return -ENXIO;
+ }
+
+ /* Allocate memory for internal data buffer */
+ this->data_buf = kmalloc (sizeof(u_char) * (tx4938ndfmc_mtd->oobblock + tx4938ndfmc_mtd->oobsize), GFP_KERNEL);
+ if (!this->data_buf) {
+ printk ("Unable to allocate NAND data buffer for TX4938.\n");
+ kfree (tx4938ndfmc_mtd);
+ return -ENOMEM;
+ }
+
+ if (protected) {
+ printk(KERN_INFO "TX4938 NDFMC: write protected.\n");
+ tx4938ndfmc_mtd->flags &= ~(MTD_WRITEABLE | MTD_ERASEABLE);
+ }
+
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+ {
+ int mtd_parts_nb = 0;
+ struct mtd_partition *mtd_parts = 0;
+ mtd_parts_nb = parse_cmdline_partitions(tx4938ndfmc_mtd, &mtd_parts, "tx4938ndfmc");
+ if (mtd_parts_nb > 0)
+ add_mtd_partitions(tx4938ndfmc_mtd, mtd_parts, mtd_parts_nb);
+ else
+ add_mtd_device(tx4938ndfmc_mtd);
+ }
+#else
+ add_mtd_partitions(tx4938ndfmc_mtd, partition_info, NUM_PARTITIONS );
+#endif
+
+ return 0;
+}
+module_init(tx4938ndfmc_init);
+
+/*
+ * Clean up routine
+ */
+static void __exit tx4938ndfmc_cleanup (void)
+{
+ struct nand_chip *this = (struct nand_chip *) tx4938ndfmc_mtd->priv;
+
+ /* Unregister the device */
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+ del_mtd_partitions(tx4938ndfmc_mtd);
+#endif
+ del_mtd_device (tx4938ndfmc_mtd);
+
+ /* Free the MTD device structure */
+ kfree (tx4938ndfmc_mtd);
+
+ /* Free internal data buffer */
+ kfree (this->data_buf);
+}
+module_exit(tx4938ndfmc_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>");
+MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on TX4938 NDFMC");
outb(PKT_BUF_SZ >> 8, ioaddr + TxFreeThreshold); /* Room for a packet. */
/* Clear the Tx ring. */
for (i = 0; i < TX_RING_SIZE; i++)
- vp->tx_skbuff[i] = 0;
+ vp->tx_skbuff[i] = NULL;
outl(0, ioaddr + DownListPtr);
}
/* Set receiver mode: presumably accept b-case and phys addr only. */
break; /* It still hasn't been processed. */
if (lp->tx_skbuff[entry]) {
dev_kfree_skb_irq(lp->tx_skbuff[entry]);
- lp->tx_skbuff[entry] = 0;
+ lp->tx_skbuff[entry] = NULL;
}
dirty_tx++;
}
for (i = 0; i < RX_RING_SIZE; i++)
if (vp->rx_skbuff[i]) {
dev_kfree_skb(vp->rx_skbuff[i]);
- vp->rx_skbuff[i] = 0;
+ vp->rx_skbuff[i] = NULL;
}
}
if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */
for (i = 0; i < TX_RING_SIZE; i++)
if (vp->tx_skbuff[i]) {
dev_kfree_skb(vp->tx_skbuff[i]);
- vp->tx_skbuff[i] = 0;
+ vp->tx_skbuff[i] = NULL;
}
}
#define tigonFwBssAddr 0x00015dd0
#define tigonFwBssLen 0x2080
#ifdef CONFIG_ACENIC_OMIT_TIGON_I
-#define tigonFwText 0
-#define tigonFwData 0
-#define tigonFwRodata 0
+#define tigonFwText NULL
+#define tigonFwData NULL
+#define tigonFwRodata NULL
#else
/* Generated by genfw.c */
static u32 tigonFwText[(MAX_TEXT_LEN/4) + 1] __initdata = {
lp->tx_skbuff[tx_index]->len,
PCI_DMA_TODEVICE);
dev_kfree_skb_irq (lp->tx_skbuff[tx_index]);
- lp->tx_skbuff[tx_index] = 0;
+ lp->tx_skbuff[tx_index] = NULL;
lp->tx_dma_addr[tx_index] = 0;
}
lp->tx_complete_idx++;
if( dev->mc_count == 0 ){
/* get only own packets */
mc_filter[1] = mc_filter[0] = 0;
- lp->mc_list = 0;
+ lp->mc_list = NULL;
lp->options &= ~OPTION_MULTICAST_ENABLE;
amd8111e_writeq(*(u64*)mc_filter,lp->mmio + LADRF);
/* disable promiscous mode */
return 0;
}
-#ifdef CONFIG_NET_FASTROUTE
-static int bond_accept_fastpath(struct net_device *bond_dev, struct dst_entry *dst)
-{
- return -1;
-}
-#endif
-
/*------------------------- Device initialization ---------------------------*/
/*
bond_set_mode_ops(bond_dev, bond->params.mode);
bond_dev->destructor = free_netdev;
-#ifdef CONFIG_NET_FASTROUTE
- bond_dev->accept_fastpath = bond_accept_fastpath;
-#endif
/* Initialize the device options */
bond_dev->tx_queue_len = 0;
* $Id: asstruct.h,v 1.1.1.1 1994/10/23 05:08:32 rick Exp $
*/
-#if ASSEMBLER
+#ifdef ASSEMBLER
# define MO(t,a) (a)
# define VMO(t,a) (a)
/************************************************************************/
typedef volatile struct _I596_RBD
{
-#if INTEL_RETENTIVE
+#ifdef INTEL_RETENTIVE
ushort count; /* Length of data in buf */
ushort offset;
#else
#endif
vol struct _I596_RBD *next; /* Next buffer descriptor in list */
uchar *buf; /* Data buffer */
-#if INTEL_RETENTIVE
+#ifdef INTEL_RETENTIVE
ushort size; /* Size of buf (constant) */
ushort zero;
#else
/* Initialize Tx descriptors, TFDListPtr leaves in start_xmit(). */
for (i = 0; i < TX_RING_SIZE; i++) {
- np->tx_skbuff[i] = 0;
+ np->tx_skbuff[i] = NULL;
np->tx_ring[i].status = cpu_to_le64 (TFDDone);
np->tx_ring[i].next_desc = cpu_to_le64 (np->tx_ring_dma +
((i+1)%TX_RING_SIZE) *
sizeof (struct netdev_desc));
np->rx_ring[i].status = 0;
np->rx_ring[i].fraginfo = 0;
- np->rx_skbuff[i] = 0;
+ np->rx_skbuff[i] = NULL;
}
/* Allocate the rx buffers */
else
dev_kfree_skb (skb);
- np->tx_skbuff[entry] = 0;
+ np->tx_skbuff[entry] = NULL;
entry = (entry + 1) % TX_RING_SIZE;
tx_use++;
}
pci_unmap_single (np->pdev, np->rx_ring[i].fraginfo,
skb->len, PCI_DMA_FROMDEVICE);
dev_kfree_skb (skb);
- np->rx_skbuff[i] = 0;
+ np->rx_skbuff[i] = NULL;
}
}
for (i = 0; i < TX_RING_SIZE; i++) {
pci_unmap_single (np->pdev, np->tx_ring[i].fraginfo,
skb->len, PCI_DMA_TODEVICE);
dev_kfree_skb (skb);
- np->tx_skbuff[i] = 0;
+ np->tx_skbuff[i] = NULL;
}
}
#define MSGOUT(S, A, B) printk(KERN_DEBUG S "\n", A, B)
-#if DBG
+#ifdef DBG
#define DEBUGOUT(S) printk(KERN_DEBUG S "\n")
#define DEBUGOUT1(S, A...) printk(KERN_DEBUG S "\n", A)
#else
#include <linux/config.h>
#include <linux/module.h>
-
+#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
static int ethertap_debug;
static int max_taps = 1;
-MODULE_PARM(max_taps, "i");
+module_param(max_taps, int, 0);
MODULE_PARM_DESC(max_taps,"Max number of ethernet tap devices");
static struct net_device **tap_map; /* Returns the tap device for a given netlink */
--- /dev/null
+/*
+ * FEC instantatiation file for NETTA
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+
+#include <asm/8xx_immap.h>
+#include <asm/pgtable.h>
+#include <asm/mpc8xx.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/commproc.h>
+
+#include "fec_8xx.h"
+
+/*************************************************/
+
+static struct fec_platform_info fec1_info = {
+ .fec_no = 0,
+ .use_mdio = 1,
+ .phy_addr = 8,
+ .fec_irq = SIU_LEVEL1,
+ .phy_irq = CPM_IRQ_OFFSET + CPMVEC_PIO_PC6,
+ .rx_ring = 128,
+ .tx_ring = 16,
+ .rx_copybreak = 240,
+ .use_napi = 1,
+ .napi_weight = 17,
+};
+
+static struct fec_platform_info fec2_info = {
+ .fec_no = 1,
+ .use_mdio = 1,
+ .phy_addr = 2,
+ .fec_irq = SIU_LEVEL3,
+ .phy_irq = CPM_IRQ_OFFSET + CPMVEC_PIO_PC7,
+ .rx_ring = 128,
+ .tx_ring = 16,
+ .rx_copybreak = 240,
+ .use_napi = 1,
+ .napi_weight = 17,
+};
+
+static struct net_device *fec1_dev;
+static struct net_device *fec2_dev;
+
+/* XXX custom u-boot & Linux startup needed */
+extern const char *__fw_getenv(const char *var);
+
+/* access ports */
+#define setbits32(_addr, _v) __fec_out32(&(_addr), __fec_in32(&(_addr)) | (_v))
+#define clrbits32(_addr, _v) __fec_out32(&(_addr), __fec_in32(&(_addr)) & ~(_v))
+
+#define setbits16(_addr, _v) __fec_out16(&(_addr), __fec_in16(&(_addr)) | (_v))
+#define clrbits16(_addr, _v) __fec_out16(&(_addr), __fec_in16(&(_addr)) & ~(_v))
+
+int fec_8xx_platform_init(void)
+{
+ immap_t *immap = (immap_t *)IMAP_ADDR;
+ bd_t *bd = (bd_t *) __res;
+ const char *s;
+ char *e;
+ int i;
+
+ /* use MDC for MII */
+ setbits16(immap->im_ioport.iop_pdpar, 0x0080);
+ clrbits16(immap->im_ioport.iop_pddir, 0x0080);
+
+ /* configure FEC1 pins */
+ setbits16(immap->im_ioport.iop_papar, 0xe810);
+ setbits16(immap->im_ioport.iop_padir, 0x0810);
+ clrbits16(immap->im_ioport.iop_padir, 0xe000);
+
+ setbits32(immap->im_cpm.cp_pbpar, 0x00000001);
+ clrbits32(immap->im_cpm.cp_pbdir, 0x00000001);
+
+ setbits32(immap->im_cpm.cp_cptr, 0x00000100);
+ clrbits32(immap->im_cpm.cp_cptr, 0x00000050);
+
+ clrbits16(immap->im_ioport.iop_pcpar, 0x0200);
+ clrbits16(immap->im_ioport.iop_pcdir, 0x0200);
+ clrbits16(immap->im_ioport.iop_pcso, 0x0200);
+ setbits16(immap->im_ioport.iop_pcint, 0x0200);
+
+ /* configure FEC2 pins */
+ setbits32(immap->im_cpm.cp_pepar, 0x00039620);
+ setbits32(immap->im_cpm.cp_pedir, 0x00039620);
+ setbits32(immap->im_cpm.cp_peso, 0x00031000);
+ clrbits32(immap->im_cpm.cp_peso, 0x00008620);
+
+ setbits32(immap->im_cpm.cp_cptr, 0x00000080);
+ clrbits32(immap->im_cpm.cp_cptr, 0x00000028);
+
+ clrbits16(immap->im_ioport.iop_pcpar, 0x0200);
+ clrbits16(immap->im_ioport.iop_pcdir, 0x0200);
+ clrbits16(immap->im_ioport.iop_pcso, 0x0200);
+ setbits16(immap->im_ioport.iop_pcint, 0x0200);
+
+ /* fill up */
+ fec1_info.sys_clk = bd->bi_intfreq;
+ fec2_info.sys_clk = bd->bi_intfreq;
+
+ s = __fw_getenv("ethaddr");
+ if (s != NULL) {
+ for (i = 0; i < 6; i++) {
+ fec1_info.macaddr[i] = simple_strtoul(s, &e, 16);
+ if (*e)
+ s = e + 1;
+ }
+ }
+
+ s = __fw_getenv("eth1addr");
+ if (s != NULL) {
+ for (i = 0; i < 6; i++) {
+ fec2_info.macaddr[i] = simple_strtoul(s, &e, 16);
+ if (*e)
+ s = e + 1;
+ }
+ }
+
+ fec_8xx_init_one(&fec1_info, &fec1_dev);
+ fec_8xx_init_one(&fec2_info, &fec2_dev);
+
+ return fec1_dev != NULL && fec2_dev != NULL ? 0 : -1;
+}
+
+void fec_8xx_platform_cleanup(void)
+{
+ if (fec2_dev != NULL)
+ fec_8xx_cleanup_one(fec2_dev);
+
+ if (fec1_dev != NULL)
+ fec_8xx_cleanup_one(fec1_dev);
+}
--- /dev/null
+/*
+ * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx.
+ *
+ * Copyright (c) 2003 Intracom S.A.
+ * by Pantelis Antoniou <panto@intracom.gr>
+ *
+ * Heavily based on original FEC driver by Dan Malek <dan@embeddededge.com>
+ * and modifications by Joakim Tjernlund <joakim.tjernlund@lumentis.se>
+ *
+ * Released under the GPL
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+
+#include <asm/8xx_immap.h>
+#include <asm/pgtable.h>
+#include <asm/mpc8xx.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/commproc.h>
+#include <asm/dma-mapping.h>
+
+#include "fec_8xx.h"
+
+/*************************************************/
+
+#define FEC_MAX_MULTICAST_ADDRS 64
+
+/*************************************************/
+
+static char version[] __devinitdata =
+ DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")" "\n";
+
+MODULE_AUTHOR("Pantelis Antoniou <panto@intracom.gr>");
+MODULE_DESCRIPTION("Motorola 8xx FEC ethernet driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(fec_8xx_debug, "i");
+MODULE_PARM_DESC(fec_8xx_debug,
+ "FEC 8xx bitmapped debugging message enable value");
+
+int fec_8xx_debug = -1; /* -1 == use FEC_8XX_DEF_MSG_ENABLE as value */
+
+/*************************************************/
+
+/*
+ * Delay to wait for FEC reset command to complete (in us)
+ */
+#define FEC_RESET_DELAY 50
+
+/*****************************************************************************************/
+
+static void fec_whack_reset(fec_t * fecp)
+{
+ int i;
+
+ /*
+ * Whack a reset. We should wait for this.
+ */
+ FW(fecp, ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET);
+ for (i = 0;
+ (FR(fecp, ecntrl) & FEC_ECNTRL_RESET) != 0 && i < FEC_RESET_DELAY;
+ i++)
+ udelay(1);
+
+ if (i == FEC_RESET_DELAY)
+ printk(KERN_WARNING "FEC Reset timeout!\n");
+
+}
+
+/****************************************************************************/
+
+/*
+ * Transmitter timeout.
+ */
+#define TX_TIMEOUT (2*HZ)
+
+/****************************************************************************/
+
+/*
+ * Returns the CRC needed when filling in the hash table for
+ * multicast group filtering
+ * pAddr must point to a MAC address (6 bytes)
+ */
+static __u32 fec_mulicast_calc_crc(char *pAddr)
+{
+ u8 byte;
+ int byte_count;
+ int bit_count;
+ __u32 crc = 0xffffffff;
+ u8 msb;
+
+ for (byte_count = 0; byte_count < 6; byte_count++) {
+ byte = pAddr[byte_count];
+ for (bit_count = 0; bit_count < 8; bit_count++) {
+ msb = crc >> 31;
+ crc <<= 1;
+ if (msb ^ (byte & 0x1)) {
+ crc ^= FEC_CRC_POLY;
+ }
+ byte >>= 1;
+ }
+ }
+ return (crc);
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * Skeleton taken from sunlance driver.
+ * The CPM Ethernet implementation allows Multicast as well as individual
+ * MAC address filtering. Some of the drivers check to make sure it is
+ * a group multicast address, and discard those that are not. I guess I
+ * will do the same for now, but just remove the test if you want
+ * individual filtering as well (do the upper net layers want or support
+ * this kind of feature?).
+ */
+static void fec_set_multicast_list(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ fec_t *fecp = fep->fecp;
+ struct dev_mc_list *pmc;
+ __u32 crc;
+ int temp;
+ __u32 csrVal;
+ int hash_index;
+ __u32 hthi, htlo;
+ unsigned long flags;
+
+
+ if ((dev->flags & IFF_PROMISC) != 0) {
+
+ spin_lock_irqsave(&fep->lock, flags);
+ FS(fecp, r_cntrl, FEC_RCNTRL_PROM);
+ spin_unlock_irqrestore(&fep->lock, flags);
+
+ /*
+ * Log any net taps.
+ */
+ printk(KERN_WARNING DRV_MODULE_NAME
+ ": %s: Promiscuous mode enabled.\n", dev->name);
+ return;
+
+ }
+
+ if ((dev->flags & IFF_ALLMULTI) != 0 ||
+ dev->mc_count > FEC_MAX_MULTICAST_ADDRS) {
+ /*
+ * Catch all multicast addresses, set the filter to all 1's.
+ */
+ hthi = 0xffffffffU;
+ htlo = 0xffffffffU;
+ } else {
+ hthi = 0;
+ htlo = 0;
+
+ /*
+ * Now populate the hash table
+ */
+ for (pmc = dev->mc_list; pmc != NULL; pmc = pmc->next) {
+ crc = fec_mulicast_calc_crc(pmc->dmi_addr);
+ temp = (crc & 0x3f) >> 1;
+ hash_index = ((temp & 0x01) << 4) |
+ ((temp & 0x02) << 2) |
+ ((temp & 0x04)) |
+ ((temp & 0x08) >> 2) |
+ ((temp & 0x10) >> 4);
+ csrVal = (1 << hash_index);
+ if (crc & 1)
+ hthi |= csrVal;
+ else
+ htlo |= csrVal;
+ }
+ }
+
+ spin_lock_irqsave(&fep->lock, flags);
+ FC(fecp, r_cntrl, FEC_RCNTRL_PROM);
+ FW(fecp, hash_table_high, hthi);
+ FW(fecp, hash_table_low, htlo);
+ spin_unlock_irqrestore(&fep->lock, flags);
+}
+
+static int fec_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *mac = addr;
+ struct fec_enet_private *fep = netdev_priv(dev);
+ struct fec *fecp = fep->fecp;
+ int i;
+ __u32 addrhi, addrlo;
+ unsigned long flags;
+
+ /* Get pointer to SCC area in parameter RAM. */
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = mac->sa_data[i];
+
+ /*
+ * Set station address.
+ */
+ addrhi = ((__u32) dev->dev_addr[0] << 24) |
+ ((__u32) dev->dev_addr[1] << 16) |
+ ((__u32) dev->dev_addr[2] << 8) |
+ (__u32) dev->dev_addr[3];
+ addrlo = ((__u32) dev->dev_addr[4] << 24) |
+ ((__u32) dev->dev_addr[5] << 16);
+
+ spin_lock_irqsave(&fep->lock, flags);
+ FW(fecp, addr_low, addrhi);
+ FW(fecp, addr_high, addrlo);
+ spin_unlock_irqrestore(&fep->lock, flags);
+
+ return 0;
+}
+
+/*
+ * This function is called to start or restart the FEC during a link
+ * change. This only happens when switching between half and full
+ * duplex.
+ */
+void fec_restart(struct net_device *dev, int duplex, int speed)
+{
+#ifdef CONFIG_DUET
+ immap_t *immap = (immap_t *) IMAP_ADDR;
+ __u32 cptr;
+#endif
+ struct fec_enet_private *fep = netdev_priv(dev);
+ struct fec *fecp = fep->fecp;
+ const struct fec_platform_info *fpi = fep->fpi;
+ cbd_t *bdp;
+ struct sk_buff *skb;
+ int i;
+ __u32 addrhi, addrlo;
+
+ fec_whack_reset(fep->fecp);
+
+ /*
+ * Set station address.
+ */
+ addrhi = ((__u32) dev->dev_addr[0] << 24) |
+ ((__u32) dev->dev_addr[1] << 16) |
+ ((__u32) dev->dev_addr[2] << 8) |
+ (__u32) dev->dev_addr[3];
+ addrlo = ((__u32) dev->dev_addr[4] << 24) |
+ ((__u32) dev->dev_addr[5] << 16);
+ FW(fecp, addr_low, addrhi);
+ FW(fecp, addr_high, addrlo);
+
+ /*
+ * Reset all multicast.
+ */
+ FW(fecp, hash_table_high, 0);
+ FW(fecp, hash_table_low, 0);
+
+ /*
+ * Set maximum receive buffer size.
+ */
+ FW(fecp, r_buff_size, PKT_MAXBLR_SIZE);
+ FW(fecp, r_hash, PKT_MAXBUF_SIZE);
+
+ /*
+ * Set receive and transmit descriptor base.
+ */
+ FW(fecp, r_des_start, iopa((__u32) (fep->rx_bd_base)));
+ FW(fecp, x_des_start, iopa((__u32) (fep->tx_bd_base)));
+
+ fep->dirty_tx = fep->cur_tx = fep->tx_bd_base;
+ fep->tx_free = fep->tx_ring;
+ fep->cur_rx = fep->rx_bd_base;
+
+ /*
+ * Reset SKB receive buffers
+ */
+ for (i = 0; i < fep->rx_ring; i++) {
+ if ((skb = fep->rx_skbuff[i]) == NULL)
+ continue;
+ fep->rx_skbuff[i] = NULL;
+ dev_kfree_skb(skb);
+ }
+
+ /*
+ * Initialize the receive buffer descriptors.
+ */
+ for (i = 0, bdp = fep->rx_bd_base; i < fep->rx_ring; i++, bdp++) {
+ skb = dev_alloc_skb(ENET_RX_FRSIZE);
+ if (skb == NULL) {
+ printk(KERN_WARNING DRV_MODULE_NAME
+ ": %s Memory squeeze, unable to allocate skb\n",
+ dev->name);
+ fep->stats.rx_dropped++;
+ break;
+ }
+ fep->rx_skbuff[i] = skb;
+ skb->dev = dev;
+ CBDW_BUFADDR(bdp, dma_map_single(NULL, skb->data,
+ L1_CACHE_ALIGN(PKT_MAXBUF_SIZE),
+ DMA_FROM_DEVICE));
+ CBDW_DATLEN(bdp, 0); /* zero */
+ CBDW_SC(bdp, BD_ENET_RX_EMPTY |
+ ((i < fep->rx_ring - 1) ? 0 : BD_SC_WRAP));
+ }
+ /*
+ * if we failed, fillup remainder
+ */
+ for (; i < fep->rx_ring; i++, bdp++) {
+ fep->rx_skbuff[i] = NULL;
+ CBDW_SC(bdp, (i < fep->rx_ring - 1) ? 0 : BD_SC_WRAP);
+ }
+
+ /*
+ * Reset SKB transmit buffers.
+ */
+ for (i = 0; i < fep->tx_ring; i++) {
+ if ((skb = fep->tx_skbuff[i]) == NULL)
+ continue;
+ fep->tx_skbuff[i] = NULL;
+ dev_kfree_skb(skb);
+ }
+
+ /*
+ * ...and the same for transmit.
+ */
+ for (i = 0, bdp = fep->tx_bd_base; i < fep->tx_ring; i++, bdp++) {
+ fep->tx_skbuff[i] = NULL;
+ CBDW_BUFADDR(bdp, virt_to_bus(NULL));
+ CBDW_DATLEN(bdp, 0);
+ CBDW_SC(bdp, (i < fep->tx_ring - 1) ? 0 : BD_SC_WRAP);
+ }
+
+ /*
+ * Enable big endian and don't care about SDMA FC.
+ */
+ FW(fecp, fun_code, 0x78000000);
+
+ /*
+ * Set MII speed.
+ */
+ FW(fecp, mii_speed, fep->fec_phy_speed);
+
+ /*
+ * Clear any outstanding interrupt.
+ */
+ FW(fecp, ievent, 0xffc0);
+ FW(fecp, ivec, (fpi->fec_irq / 2) << 29);
+
+ /*
+ * adjust to speed (only for DUET & RMII)
+ */
+#ifdef CONFIG_DUET
+ cptr = in_be32(&immap->im_cpm.cp_cptr);
+ switch (fpi->fec_no) {
+ case 0:
+ /*
+ * check if in RMII mode
+ */
+ if ((cptr & 0x100) == 0)
+ break;
+
+ if (speed == 10)
+ cptr |= 0x0000010;
+ else if (speed == 100)
+ cptr &= ~0x0000010;
+ break;
+ case 1:
+ /*
+ * check if in RMII mode
+ */
+ if ((cptr & 0x80) == 0)
+ break;
+
+ if (speed == 10)
+ cptr |= 0x0000008;
+ else if (speed == 100)
+ cptr &= ~0x0000008;
+ break;
+ default:
+ break;
+ }
+ out_be32(&immap->im_cpm.cp_cptr, cptr);
+#endif
+
+ FW(fecp, r_cntrl, FEC_RCNTRL_MII_MODE); /* MII enable */
+ /*
+ * adjust to duplex mode
+ */
+ if (duplex) {
+ FC(fecp, r_cntrl, FEC_RCNTRL_DRT);
+ FS(fecp, x_cntrl, FEC_TCNTRL_FDEN); /* FD enable */
+ } else {
+ FS(fecp, r_cntrl, FEC_RCNTRL_DRT);
+ FC(fecp, x_cntrl, FEC_TCNTRL_FDEN); /* FD disable */
+ }
+
+ /*
+ * Enable interrupts we wish to service.
+ */
+ FW(fecp, imask, FEC_ENET_TXF | FEC_ENET_TXB |
+ FEC_ENET_RXF | FEC_ENET_RXB);
+
+ /*
+ * And last, enable the transmit and receive processing.
+ */
+ FW(fecp, ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN);
+ FW(fecp, r_des_active, 0x01000000);
+}
+
+void fec_stop(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ fec_t *fecp = fep->fecp;
+ struct sk_buff *skb;
+ int i;
+
+ if ((FR(fecp, ecntrl) & FEC_ECNTRL_ETHER_EN) == 0)
+ return; /* already down */
+
+ FW(fecp, x_cntrl, 0x01); /* Graceful transmit stop */
+ for (i = 0; ((FR(fecp, ievent) & 0x10000000) == 0) &&
+ i < FEC_RESET_DELAY; i++)
+ udelay(1);
+
+ if (i == FEC_RESET_DELAY)
+ printk(KERN_WARNING DRV_MODULE_NAME
+ ": %s FEC timeout on graceful transmit stop\n",
+ dev->name);
+ /*
+ * Disable FEC. Let only MII interrupts.
+ */
+ FW(fecp, imask, 0);
+ FW(fecp, ecntrl, ~FEC_ECNTRL_ETHER_EN);
+
+ /*
+ * Reset SKB transmit buffers.
+ */
+ for (i = 0; i < fep->tx_ring; i++) {
+ if ((skb = fep->tx_skbuff[i]) == NULL)
+ continue;
+ fep->tx_skbuff[i] = NULL;
+ dev_kfree_skb(skb);
+ }
+
+ /*
+ * Reset SKB receive buffers
+ */
+ for (i = 0; i < fep->rx_ring; i++) {
+ if ((skb = fep->rx_skbuff[i]) == NULL)
+ continue;
+ fep->rx_skbuff[i] = NULL;
+ dev_kfree_skb(skb);
+ }
+}
+
+/* common receive function */
+static int fec_enet_rx_common(struct net_device *dev, int *budget)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ fec_t *fecp = fep->fecp;
+ const struct fec_platform_info *fpi = fep->fpi;
+ cbd_t *bdp;
+ struct sk_buff *skb, *skbn, *skbt;
+ int received = 0;
+ __u16 pkt_len, sc;
+ int curidx;
+ int rx_work_limit;
+
+ if (fpi->use_napi) {
+ rx_work_limit = min(dev->quota, *budget);
+
+ if (!netif_running(dev))
+ return 0;
+ }
+
+ /*
+ * First, grab all of the stats for the incoming packet.
+ * These get messed up if we get called due to a busy condition.
+ */
+ bdp = fep->cur_rx;
+
+ /* clear RX status bits for napi*/
+ if (fpi->use_napi)
+ FW(fecp, ievent, FEC_ENET_RXF | FEC_ENET_RXB);
+
+ while (((sc = CBDR_SC(bdp)) & BD_ENET_RX_EMPTY) == 0) {
+
+ curidx = bdp - fep->rx_bd_base;
+
+ /*
+ * Since we have allocated space to hold a complete frame,
+ * the last indicator should be set.
+ */
+ if ((sc & BD_ENET_RX_LAST) == 0)
+ printk(KERN_WARNING DRV_MODULE_NAME
+ ": %s rcv is not +last\n",
+ dev->name);
+
+ /*
+ * Check for errors.
+ */
+ if (sc & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_CL |
+ BD_ENET_RX_NO | BD_ENET_RX_CR | BD_ENET_RX_OV)) {
+ fep->stats.rx_errors++;
+ /* Frame too long or too short. */
+ if (sc & (BD_ENET_RX_LG | BD_ENET_RX_SH))
+ fep->stats.rx_length_errors++;
+ /* Frame alignment */
+ if (sc & (BD_ENET_RX_NO | BD_ENET_RX_CL))
+ fep->stats.rx_frame_errors++;
+ /* CRC Error */
+ if (sc & BD_ENET_RX_CR)
+ fep->stats.rx_crc_errors++;
+ /* FIFO overrun */
+ if (sc & BD_ENET_RX_OV)
+ fep->stats.rx_crc_errors++;
+
+ skbn = fep->rx_skbuff[curidx];
+ BUG_ON(skbn == NULL);
+
+ } else {
+
+ /* napi, got packet but no quota */
+ if (fpi->use_napi && --rx_work_limit < 0)
+ break;
+
+ skb = fep->rx_skbuff[curidx];
+ BUG_ON(skb == NULL);
+
+ /*
+ * Process the incoming frame.
+ */
+ fep->stats.rx_packets++;
+ pkt_len = CBDR_DATLEN(bdp) - 4; /* remove CRC */
+ fep->stats.rx_bytes += pkt_len + 4;
+
+ if (pkt_len <= fpi->rx_copybreak) {
+ /* +2 to make IP header L1 cache aligned */
+ skbn = dev_alloc_skb(pkt_len + 2);
+ if (skbn != NULL) {
+ skb_reserve(skbn, 2); /* align IP header */
+ memcpy(skbn->data, skb->data, pkt_len);
+ /* swap */
+ skbt = skb;
+ skb = skbn;
+ skbn = skbt;
+ }
+ } else
+ skbn = dev_alloc_skb(ENET_RX_FRSIZE);
+
+ if (skbn != NULL) {
+ skb->dev = dev;
+ skb_put(skb, pkt_len); /* Make room */
+ skb->protocol = eth_type_trans(skb, dev);
+ received++;
+ if (!fpi->use_napi)
+ netif_rx(skb);
+ else
+ netif_receive_skb(skb);
+ } else {
+ printk(KERN_WARNING DRV_MODULE_NAME
+ ": %s Memory squeeze, dropping packet.\n",
+ dev->name);
+ fep->stats.rx_dropped++;
+ skbn = skb;
+ }
+ }
+
+ fep->rx_skbuff[curidx] = skbn;
+ CBDW_BUFADDR(bdp, dma_map_single(NULL, skbn->data,
+ L1_CACHE_ALIGN(PKT_MAXBUF_SIZE),
+ DMA_FROM_DEVICE));
+ CBDW_DATLEN(bdp, 0);
+ CBDW_SC(bdp, (sc & ~BD_ENET_RX_STATS) | BD_ENET_RX_EMPTY);
+
+ /*
+ * Update BD pointer to next entry.
+ */
+ if ((sc & BD_ENET_RX_WRAP) == 0)
+ bdp++;
+ else
+ bdp = fep->rx_bd_base;
+
+ /*
+ * Doing this here will keep the FEC running while we process
+ * incoming frames. On a heavily loaded network, we should be
+ * able to keep up at the expense of system resources.
+ */
+ FW(fecp, r_des_active, 0x01000000);
+ }
+
+ fep->cur_rx = bdp;
+
+ if (fpi->use_napi) {
+ dev->quota -= received;
+ *budget -= received;
+
+ if (rx_work_limit < 0)
+ return 1; /* not done */
+
+ /* done */
+ netif_rx_complete(dev);
+
+ /* enable RX interrupt bits */
+ FS(fecp, imask, FEC_ENET_RXF | FEC_ENET_RXB);
+ }
+
+ return 0;
+}
+
+static void fec_enet_tx(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ cbd_t *bdp;
+ struct sk_buff *skb;
+ int dirtyidx, do_wake;
+ __u16 sc;
+
+ spin_lock(&fep->lock);
+ bdp = fep->dirty_tx;
+
+ do_wake = 0;
+ while (((sc = CBDR_SC(bdp)) & BD_ENET_TX_READY) == 0) {
+
+ dirtyidx = bdp - fep->tx_bd_base;
+
+ if (fep->tx_free == fep->tx_ring)
+ break;
+
+ skb = fep->tx_skbuff[dirtyidx];
+
+ /*
+ * Check for errors.
+ */
+ if (sc & (BD_ENET_TX_HB | BD_ENET_TX_LC |
+ BD_ENET_TX_RL | BD_ENET_TX_UN | BD_ENET_TX_CSL)) {
+ fep->stats.tx_errors++;
+ if (sc & BD_ENET_TX_HB) /* No heartbeat */
+ fep->stats.tx_heartbeat_errors++;
+ if (sc & BD_ENET_TX_LC) /* Late collision */
+ fep->stats.tx_window_errors++;
+ if (sc & BD_ENET_TX_RL) /* Retrans limit */
+ fep->stats.tx_aborted_errors++;
+ if (sc & BD_ENET_TX_UN) /* Underrun */
+ fep->stats.tx_fifo_errors++;
+ if (sc & BD_ENET_TX_CSL) /* Carrier lost */
+ fep->stats.tx_carrier_errors++;
+ } else
+ fep->stats.tx_packets++;
+
+ if (sc & BD_ENET_TX_READY)
+ printk(KERN_WARNING DRV_MODULE_NAME
+ ": %s HEY! Enet xmit interrupt and TX_READY.\n",
+ dev->name);
+
+ /*
+ * Deferred means some collisions occurred during transmit,
+ * but we eventually sent the packet OK.
+ */
+ if (sc & BD_ENET_TX_DEF)
+ fep->stats.collisions++;
+
+ /*
+ * Free the sk buffer associated with this last transmit.
+ */
+ dev_kfree_skb_irq(skb);
+ fep->tx_skbuff[dirtyidx] = NULL;
+
+ /*
+ * Update pointer to next buffer descriptor to be transmitted.
+ */
+ if ((sc & BD_ENET_TX_WRAP) == 0)
+ bdp++;
+ else
+ bdp = fep->tx_bd_base;
+
+ /*
+ * Since we have freed up a buffer, the ring is no longer
+ * full.
+ */
+ if (!fep->tx_free++)
+ do_wake = 1;
+ }
+
+ fep->dirty_tx = bdp;
+
+ spin_unlock(&fep->lock);
+
+ if (do_wake && netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+}
+
+/*
+ * The interrupt handler.
+ * This is called from the MPC core interrupt.
+ */
+static irqreturn_t
+fec_enet_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = dev_id;
+ struct fec_enet_private *fep;
+ const struct fec_platform_info *fpi;
+ fec_t *fecp;
+ __u32 int_events;
+ __u32 int_events_napi;
+
+ if (unlikely(dev == NULL))
+ return IRQ_NONE;
+
+ fep = netdev_priv(dev);
+ fecp = fep->fecp;
+ fpi = fep->fpi;
+
+ /*
+ * Get the interrupt events that caused us to be here.
+ */
+ while ((int_events = FR(fecp, ievent) & FR(fecp, imask)) != 0) {
+
+ if (!fpi->use_napi)
+ FW(fecp, ievent, int_events);
+ else {
+ int_events_napi = int_events & ~(FEC_ENET_RXF | FEC_ENET_RXB);
+ FW(fecp, ievent, int_events_napi);
+ }
+
+ if ((int_events & (FEC_ENET_HBERR | FEC_ENET_BABR |
+ FEC_ENET_BABT | FEC_ENET_EBERR)) != 0)
+ printk(KERN_WARNING DRV_MODULE_NAME
+ ": %s FEC ERROR(s) 0x%x\n",
+ dev->name, int_events);
+
+ if ((int_events & FEC_ENET_RXF) != 0) {
+ if (!fpi->use_napi)
+ fec_enet_rx_common(dev, NULL);
+ else {
+ if (netif_rx_schedule_prep(dev)) {
+ /* disable rx interrupts */
+ FC(fecp, imask, FEC_ENET_RXF | FEC_ENET_RXB);
+ __netif_rx_schedule(dev);
+ } else {
+ printk(KERN_ERR DRV_MODULE_NAME
+ ": %s driver bug! interrupt while in poll!\n",
+ dev->name);
+ FC(fecp, imask, FEC_ENET_RXF | FEC_ENET_RXB);
+ }
+ }
+ }
+
+ if ((int_events & FEC_ENET_TXF) != 0)
+ fec_enet_tx(dev);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* This interrupt occurs when the PHY detects a link change. */
+static irqreturn_t
+fec_mii_link_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = dev_id;
+ struct fec_enet_private *fep;
+ const struct fec_platform_info *fpi;
+
+ if (unlikely(dev == NULL))
+ return IRQ_NONE;
+
+ fep = netdev_priv(dev);
+ fpi = fep->fpi;
+
+ if (!fpi->use_mdio)
+ return IRQ_NONE;
+
+ /*
+ * Acknowledge the interrupt if possible. If we have not
+ * found the PHY yet we can't process or acknowledge the
+ * interrupt now. Instead we ignore this interrupt for now,
+ * which we can do since it is edge triggered. It will be
+ * acknowledged later by fec_enet_open().
+ */
+ if (!fep->phy)
+ return IRQ_NONE;
+
+ fec_mii_ack_int(dev);
+ fec_mii_link_status_change_check(dev, 0);
+
+ return IRQ_HANDLED;
+}
+
+
+/**********************************************************************************/
+
+static int fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ fec_t *fecp = fep->fecp;
+ cbd_t *bdp;
+ int curidx;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fep->tx_lock, flags);
+
+ /*
+ * Fill in a Tx ring entry
+ */
+ bdp = fep->cur_tx;
+
+ if (!fep->tx_free || (CBDR_SC(bdp) & BD_ENET_TX_READY)) {
+ netif_stop_queue(dev);
+ spin_unlock_irqrestore(&fep->tx_lock, flags);
+
+ /*
+ * Ooops. All transmit buffers are full. Bail out.
+ * This should not happen, since the tx queue should be stopped.
+ */
+ printk(KERN_WARNING DRV_MODULE_NAME
+ ": %s tx queue full!.\n", dev->name);
+ return 1;
+ }
+
+ curidx = bdp - fep->tx_bd_base;
+ /*
+ * Clear all of the status flags.
+ */
+ CBDC_SC(bdp, BD_ENET_TX_STATS);
+
+ /*
+ * Save skb pointer.
+ */
+ fep->tx_skbuff[curidx] = skb;
+
+ fep->stats.tx_bytes += skb->len;
+
+ /*
+ * Push the data cache so the CPM does not get stale memory data.
+ */
+ CBDW_BUFADDR(bdp, dma_map_single(NULL, skb->data,
+ skb->len, DMA_TO_DEVICE));
+ CBDW_DATLEN(bdp, skb->len);
+
+ dev->trans_start = jiffies;
+
+ /*
+ * If this was the last BD in the ring, start at the beginning again.
+ */
+ if ((CBDR_SC(bdp) & BD_ENET_TX_WRAP) == 0)
+ fep->cur_tx++;
+ else
+ fep->cur_tx = fep->tx_bd_base;
+
+ if (!--fep->tx_free)
+ netif_stop_queue(dev);
+
+ /*
+ * Trigger transmission start
+ */
+ CBDS_SC(bdp, BD_ENET_TX_READY | BD_ENET_TX_INTR |
+ BD_ENET_TX_LAST | BD_ENET_TX_TC);
+ FW(fecp, x_des_active, 0x01000000);
+
+ spin_unlock_irqrestore(&fep->tx_lock, flags);
+
+ return 0;
+}
+
+static void fec_timeout(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+
+ fep->stats.tx_errors++;
+
+ if (fep->tx_free)
+ netif_wake_queue(dev);
+
+ /* check link status again */
+ fec_mii_link_status_change_check(dev, 0);
+}
+
+static int fec_enet_open(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ const struct fec_platform_info *fpi = fep->fpi;
+ unsigned long flags;
+
+ /* Install our interrupt handler. */
+ if (request_irq(fpi->fec_irq, fec_enet_interrupt, 0, "fec", dev) != 0) {
+ printk(KERN_ERR DRV_MODULE_NAME
+ ": %s Could not allocate FEC IRQ!", dev->name);
+ return -EINVAL;
+ }
+
+ /* Install our phy interrupt handler */
+ if (fpi->phy_irq != -1 &&
+ request_irq(fpi->phy_irq, fec_mii_link_interrupt, 0, "fec-phy",
+ dev) != 0) {
+ printk(KERN_ERR DRV_MODULE_NAME
+ ": %s Could not allocate PHY IRQ!", dev->name);
+ free_irq(fpi->fec_irq, dev);
+ return -EINVAL;
+ }
+
+ if (fpi->use_mdio) {
+ fec_mii_startup(dev);
+ netif_carrier_off(dev);
+ fec_mii_link_status_change_check(dev, 1);
+ } else {
+ spin_lock_irqsave(&fep->lock, flags);
+ fec_restart(dev, 1, 100); /* XXX this sucks */
+ spin_unlock_irqrestore(&fep->lock, flags);
+
+ netif_carrier_on(dev);
+ netif_start_queue(dev);
+ }
+ return 0;
+}
+
+static int fec_enet_close(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ const struct fec_platform_info *fpi = fep->fpi;
+ unsigned long flags;
+
+ netif_stop_queue(dev);
+ netif_carrier_off(dev);
+
+ if (fpi->use_mdio)
+ fec_mii_shutdown(dev);
+
+ spin_lock_irqsave(&fep->lock, flags);
+ fec_stop(dev);
+ spin_unlock_irqrestore(&fep->lock, flags);
+
+ /* release any irqs */
+ if (fpi->phy_irq != -1)
+ free_irq(fpi->phy_irq, dev);
+ free_irq(fpi->fec_irq, dev);
+
+ return 0;
+}
+
+static struct net_device_stats *fec_enet_get_stats(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ return &fep->stats;
+}
+
+static int fec_enet_poll(struct net_device *dev, int *budget)
+{
+ return fec_enet_rx_common(dev, budget);
+}
+
+/*************************************************************************/
+
+static void fec_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, DRV_MODULE_NAME);
+ strcpy(info->version, DRV_MODULE_VERSION);
+}
+
+static int fec_get_regs_len(struct net_device *dev)
+{
+ return sizeof(fec_t);
+}
+
+static void fec_get_regs(struct net_device *dev, struct ethtool_regs *regs,
+ void *p)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ unsigned long flags;
+
+ if (regs->len < sizeof(fec_t))
+ return;
+
+ regs->version = 0;
+ spin_lock_irqsave(&fep->lock, flags);
+ memcpy_fromio(p, fep->fecp, sizeof(fec_t));
+ spin_unlock_irqrestore(&fep->lock, flags);
+}
+
+static int fec_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&fep->lock, flags);
+ rc = mii_ethtool_gset(&fep->mii_if, cmd);
+ spin_unlock_irqrestore(&fep->lock, flags);
+
+ return rc;
+}
+
+static int fec_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&fep->lock, flags);
+ rc = mii_ethtool_sset(&fep->mii_if, cmd);
+ spin_unlock_irqrestore(&fep->lock, flags);
+
+ return rc;
+}
+
+static int fec_nway_reset(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ return mii_nway_restart(&fep->mii_if);
+}
+
+static __u32 fec_get_msglevel(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ return fep->msg_enable;
+}
+
+static void fec_set_msglevel(struct net_device *dev, __u32 value)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ fep->msg_enable = value;
+}
+
+static struct ethtool_ops fec_ethtool_ops = {
+ .get_drvinfo = fec_get_drvinfo,
+ .get_regs_len = fec_get_regs_len,
+ .get_settings = fec_get_settings,
+ .set_settings = fec_set_settings,
+ .nway_reset = fec_nway_reset,
+ .get_link = ethtool_op_get_link,
+ .get_msglevel = fec_get_msglevel,
+ .set_msglevel = fec_set_msglevel,
+ .get_tx_csum = ethtool_op_get_tx_csum,
+ .set_tx_csum = ethtool_op_set_tx_csum, /* local! */
+ .get_sg = ethtool_op_get_sg,
+ .set_sg = ethtool_op_set_sg,
+ .get_regs = fec_get_regs,
+};
+
+static int fec_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&rq->ifr_data;
+ unsigned long flags;
+ int rc;
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ spin_lock_irqsave(&fep->lock, flags);
+ rc = generic_mii_ioctl(&fep->mii_if, mii, cmd, NULL);
+ spin_unlock_irqrestore(&fep->lock, flags);
+ return rc;
+}
+
+int fec_8xx_init_one(const struct fec_platform_info *fpi,
+ struct net_device **devp)
+{
+ immap_t *immap = (immap_t *) IMAP_ADDR;
+ static int fec_8xx_version_printed = 0;
+ struct net_device *dev = NULL;
+ struct fec_enet_private *fep = NULL;
+ fec_t *fecp = NULL;
+ int i;
+ int err = 0;
+ int registered = 0;
+ __u32 siel;
+
+ *devp = NULL;
+
+ switch (fpi->fec_no) {
+ case 0:
+ fecp = &((immap_t *) IMAP_ADDR)->im_cpm.cp_fec;
+ break;
+#ifdef CONFIG_DUET
+ case 1:
+ fecp = &((immap_t *) IMAP_ADDR)->im_cpm.cp_fec2;
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+ if (fec_8xx_version_printed++ == 0)
+ printk(KERN_INFO "%s", version);
+
+ i = sizeof(*fep) + (sizeof(struct sk_buff **) *
+ (fpi->rx_ring + fpi->tx_ring));
+
+ dev = alloc_etherdev(i);
+ if (!dev) {
+ err = -ENOMEM;
+ goto err;
+ }
+ SET_MODULE_OWNER(dev);
+
+ fep = netdev_priv(dev);
+
+ /* partial reset of FEC */
+ fec_whack_reset(fecp);
+
+ /* point rx_skbuff, tx_skbuff */
+ fep->rx_skbuff = (struct sk_buff **)&fep[1];
+ fep->tx_skbuff = fep->rx_skbuff + fpi->rx_ring;
+
+ fep->fecp = fecp;
+ fep->fpi = fpi;
+
+ /* init locks */
+ spin_lock_init(&fep->lock);
+ spin_lock_init(&fep->tx_lock);
+
+ /*
+ * Set the Ethernet address.
+ */
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = fpi->macaddr[i];
+
+ fep->ring_base = dma_alloc_coherent(NULL,
+ (fpi->tx_ring + fpi->rx_ring) *
+ sizeof(cbd_t), &fep->ring_mem_addr,
+ GFP_KERNEL);
+ if (fep->ring_base == NULL) {
+ printk(KERN_ERR DRV_MODULE_NAME
+ ": %s dma alloc failed.\n", dev->name);
+ err = -ENOMEM;
+ goto err;
+ }
+
+ /*
+ * Set receive and transmit descriptor base.
+ */
+ fep->rx_bd_base = fep->ring_base;
+ fep->tx_bd_base = fep->rx_bd_base + fpi->rx_ring;
+
+ /* initialize ring size variables */
+ fep->tx_ring = fpi->tx_ring;
+ fep->rx_ring = fpi->rx_ring;
+
+ /* SIU interrupt */
+ if (fpi->phy_irq != -1 &&
+ (fpi->phy_irq >= SIU_IRQ0 && fpi->phy_irq < SIU_LEVEL7)) {
+
+ siel = in_be32(&immap->im_siu_conf.sc_siel);
+ if ((fpi->phy_irq & 1) == 0)
+ siel |= (0x80000000 >> fpi->phy_irq);
+ else
+ siel &= ~(0x80000000 >> (fpi->phy_irq & ~1));
+ out_be32(&immap->im_siu_conf.sc_siel, siel);
+ }
+
+ /*
+ * The FEC Ethernet specific entries in the device structure.
+ */
+ dev->open = fec_enet_open;
+ dev->hard_start_xmit = fec_enet_start_xmit;
+ dev->tx_timeout = fec_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+ dev->stop = fec_enet_close;
+ dev->get_stats = fec_enet_get_stats;
+ dev->set_multicast_list = fec_set_multicast_list;
+ dev->set_mac_address = fec_set_mac_address;
+ if (fpi->use_napi) {
+ dev->poll = fec_enet_poll;
+ dev->weight = fpi->napi_weight;
+ }
+ dev->ethtool_ops = &fec_ethtool_ops;
+ dev->do_ioctl = fec_ioctl;
+
+ fep->fec_phy_speed =
+ ((((fpi->sys_clk + 4999999) / 2500000) / 2) & 0x3F) << 1;
+
+ init_timer(&fep->phy_timer_list);
+
+ /* partial reset of FEC so that only MII works */
+ FW(fecp, mii_speed, fep->fec_phy_speed);
+ FW(fecp, ievent, 0xffc0);
+ FW(fecp, ivec, (fpi->fec_irq / 2) << 29);
+ FW(fecp, imask, 0);
+ FW(fecp, r_cntrl, FEC_RCNTRL_MII_MODE); /* MII enable */
+ FW(fecp, ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN);
+
+ netif_carrier_off(dev);
+
+ err = register_netdev(dev);
+ if (err != 0)
+ goto err;
+ registered = 1;
+
+ if (fpi->use_mdio) {
+ fep->mii_if.dev = dev;
+ fep->mii_if.mdio_read = fec_mii_read;
+ fep->mii_if.mdio_write = fec_mii_write;
+ fep->mii_if.phy_id_mask = 0x1f;
+ fep->mii_if.reg_num_mask = 0x1f;
+ fep->mii_if.phy_id = fec_mii_phy_id_detect(dev);
+ }
+
+ *devp = dev;
+
+ return 0;
+
+ err:
+ if (dev != NULL) {
+ if (fecp != NULL)
+ fec_whack_reset(fecp);
+
+ if (registered)
+ unregister_netdev(dev);
+
+ if (fep != NULL) {
+ if (fep->ring_base)
+ dma_free_coherent(NULL,
+ (fpi->tx_ring +
+ fpi->rx_ring) *
+ sizeof(cbd_t), fep->ring_base,
+ fep->ring_mem_addr);
+ }
+ free_netdev(dev);
+ }
+ return err;
+}
+
+int fec_8xx_cleanup_one(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ fec_t *fecp = fep->fecp;
+ const struct fec_platform_info *fpi = fep->fpi;
+
+ fec_whack_reset(fecp);
+
+ unregister_netdev(dev);
+
+ dma_free_coherent(NULL, (fpi->tx_ring + fpi->rx_ring) * sizeof(cbd_t),
+ fep->ring_base, fep->ring_mem_addr);
+
+ free_netdev(dev);
+
+ return 0;
+}
+
+/**************************************************************************************/
+/**************************************************************************************/
+/**************************************************************************************/
+
+static int __init fec_8xx_init(void)
+{
+ return fec_8xx_platform_init();
+}
+
+static void __exit fec_8xx_cleanup(void)
+{
+ fec_8xx_platform_cleanup();
+}
+
+/**************************************************************************************/
+/**************************************************************************************/
+/**************************************************************************************/
+
+module_init(fec_8xx_init);
+module_exit(fec_8xx_cleanup);
--- /dev/null
+/*
+ * Fast Ethernet Controller (FEC) driver for Motorola MPC8xx.
+ *
+ * Copyright (c) 2003 Intracom S.A.
+ * by Pantelis Antoniou <panto@intracom.gr>
+ *
+ * Heavily based on original FEC driver by Dan Malek <dan@embeddededge.com>
+ * and modifications by Joakim Tjernlund <joakim.tjernlund@lumentis.se>
+ *
+ * Released under the GPL
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+
+#include <asm/8xx_immap.h>
+#include <asm/pgtable.h>
+#include <asm/mpc8xx.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/commproc.h>
+
+/*************************************************/
+
+#include "fec_8xx.h"
+
+/*************************************************/
+
+/* Make MII read/write commands for the FEC.
+*/
+#define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18))
+#define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | (VAL & 0xffff))
+#define mk_mii_end 0
+
+/*************************************************/
+
+/* XXX both FECs use the MII interface of FEC1 */
+static spinlock_t fec_mii_lock = SPIN_LOCK_UNLOCKED;
+
+#define FEC_MII_LOOPS 10000
+
+int fec_mii_read(struct net_device *dev, int phy_id, int location)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ fec_t *fecp;
+ int i, ret = -1;
+ unsigned long flags;
+
+ /* XXX MII interface is only connected to FEC1 */
+ fecp = &((immap_t *) IMAP_ADDR)->im_cpm.cp_fec;
+
+ spin_lock_irqsave(&fec_mii_lock, flags);
+
+ if ((FR(fecp, r_cntrl) & FEC_RCNTRL_MII_MODE) == 0) {
+ FS(fecp, r_cntrl, FEC_RCNTRL_MII_MODE); /* MII enable */
+ FS(fecp, ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN);
+ FW(fecp, ievent, FEC_ENET_MII);
+ }
+
+ /* Add PHY address to register command. */
+ FW(fecp, mii_speed, fep->fec_phy_speed);
+ FW(fecp, mii_data, (phy_id << 23) | mk_mii_read(location));
+
+ for (i = 0; i < FEC_MII_LOOPS; i++)
+ if ((FR(fecp, ievent) & FEC_ENET_MII) != 0)
+ break;
+
+ if (i < FEC_MII_LOOPS) {
+ FW(fecp, ievent, FEC_ENET_MII);
+ ret = FR(fecp, mii_data) & 0xffff;
+ }
+
+ spin_unlock_irqrestore(&fec_mii_lock, flags);
+
+ return ret;
+}
+
+void fec_mii_write(struct net_device *dev, int phy_id, int location, int value)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ fec_t *fecp;
+ unsigned long flags;
+ int i;
+
+ /* XXX MII interface is only connected to FEC1 */
+ fecp = &((immap_t *) IMAP_ADDR)->im_cpm.cp_fec;
+
+ spin_lock_irqsave(&fec_mii_lock, flags);
+
+ if ((FR(fecp, r_cntrl) & FEC_RCNTRL_MII_MODE) == 0) {
+ FS(fecp, r_cntrl, FEC_RCNTRL_MII_MODE); /* MII enable */
+ FS(fecp, ecntrl, FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN);
+ FW(fecp, ievent, FEC_ENET_MII);
+ }
+
+ /* Add PHY address to register command. */
+ FW(fecp, mii_speed, fep->fec_phy_speed); /* always adapt mii speed */
+ FW(fecp, mii_data, (phy_id << 23) | mk_mii_write(location, value));
+
+ for (i = 0; i < FEC_MII_LOOPS; i++)
+ if ((FR(fecp, ievent) & FEC_ENET_MII) != 0)
+ break;
+
+ if (i < FEC_MII_LOOPS)
+ FW(fecp, ievent, FEC_ENET_MII);
+
+ spin_unlock_irqrestore(&fec_mii_lock, flags);
+}
+
+/*************************************************/
+
+#ifdef CONFIG_FEC_8XX_GENERIC_PHY
+
+/*
+ * Generic PHY support.
+ * Should work for all PHYs, but link change is detected by polling
+ */
+
+static void generic_timer_callback(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct fec_enet_private *fep = netdev_priv(dev);
+
+ fep->phy_timer_list.expires = jiffies + HZ / 2;
+
+ add_timer(&fep->phy_timer_list);
+
+ fec_mii_link_status_change_check(dev, 0);
+}
+
+static void generic_startup(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+
+ fep->phy_timer_list.expires = jiffies + HZ / 2; /* every 500ms */
+ fep->phy_timer_list.data = (unsigned long)dev;
+ fep->phy_timer_list.function = generic_timer_callback;
+ add_timer(&fep->phy_timer_list);
+}
+
+static void generic_shutdown(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+
+ del_timer_sync(&fep->phy_timer_list);
+}
+
+#endif
+
+#ifdef CONFIG_FEC_8XX_DM9161_PHY
+
+/* ------------------------------------------------------------------------- */
+/* The Davicom DM9161 is used on the NETTA board */
+
+/* register definitions */
+
+#define MII_DM9161_ACR 16 /* Aux. Config Register */
+#define MII_DM9161_ACSR 17 /* Aux. Config/Status Register */
+#define MII_DM9161_10TCSR 18 /* 10BaseT Config/Status Reg. */
+#define MII_DM9161_INTR 21 /* Interrupt Register */
+#define MII_DM9161_RECR 22 /* Receive Error Counter Reg. */
+#define MII_DM9161_DISCR 23 /* Disconnect Counter Register */
+
+static void dm9161_startup(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+
+ fec_mii_write(dev, fep->mii_if.phy_id, MII_DM9161_INTR, 0x0000);
+}
+
+static void dm9161_ack_int(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+
+ fec_mii_read(dev, fep->mii_if.phy_id, MII_DM9161_INTR);
+}
+
+static void dm9161_shutdown(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+
+ fec_mii_write(dev, fep->mii_if.phy_id, MII_DM9161_INTR, 0x0f00);
+}
+
+#endif
+
+/**********************************************************************************/
+
+static const struct phy_info phy_info[] = {
+#ifdef CONFIG_FEC_8XX_DM9161_PHY
+ {
+ .id = 0x00181b88,
+ .name = "DM9161",
+ .startup = dm9161_startup,
+ .ack_int = dm9161_ack_int,
+ .shutdown = dm9161_shutdown,
+ },
+#endif
+#ifdef CONFIG_FEC_8XX_GENERIC_PHY
+ {
+ .id = 0,
+ .name = "GENERIC",
+ .startup = generic_startup,
+ .shutdown = generic_shutdown,
+ },
+#endif
+};
+
+/**********************************************************************************/
+
+int fec_mii_phy_id_detect(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ const struct fec_platform_info *fpi = fep->fpi;
+ int i, r, start, end, phytype, physubtype;
+ const struct phy_info *phy;
+ int phy_hwid, phy_id;
+
+ /* if no MDIO */
+ if (fpi->use_mdio == 0)
+ return -1;
+
+ phy_hwid = -1;
+ fep->phy = NULL;
+
+ /* auto-detect? */
+ if (fpi->phy_addr == -1) {
+ start = 0;
+ end = 32;
+ } else { /* direct */
+ start = fpi->phy_addr;
+ end = start + 1;
+ }
+
+ for (phy_id = start; phy_id < end; phy_id++) {
+ r = fec_mii_read(dev, phy_id, MII_PHYSID1);
+ if (r == -1 || (phytype = (r & 0xffff)) == 0xffff)
+ continue;
+ r = fec_mii_read(dev, phy_id, MII_PHYSID2);
+ if (r == -1 || (physubtype = (r & 0xffff)) == 0xffff)
+ continue;
+ phy_hwid = (phytype << 16) | physubtype;
+ if (phy_hwid != -1)
+ break;
+ }
+
+ if (phy_hwid == -1) {
+ printk(KERN_ERR DRV_MODULE_NAME
+ ": %s No PHY detected!\n", dev->name);
+ return -1;
+ }
+
+ for (i = 0, phy = phy_info; i < sizeof(phy_info) / sizeof(phy_info[0]);
+ i++, phy++)
+ if (phy->id == (phy_hwid >> 4) || phy->id == 0)
+ break;
+
+ if (i >= sizeof(phy_info) / sizeof(phy_info[0])) {
+ printk(KERN_ERR DRV_MODULE_NAME
+ ": %s PHY id 0x%08x is not supported!\n",
+ dev->name, phy_hwid);
+ return -1;
+ }
+
+ fep->phy = phy;
+
+ printk(KERN_INFO DRV_MODULE_NAME
+ ": %s Phy @ 0x%x, type %s (0x%08x)\n",
+ dev->name, phy_id, fep->phy->name, phy_hwid);
+
+ return phy_id;
+}
+
+void fec_mii_startup(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ const struct fec_platform_info *fpi = fep->fpi;
+
+ if (!fpi->use_mdio || fep->phy == NULL)
+ return;
+
+ if (fep->phy->startup == NULL)
+ return;
+
+ (*fep->phy->startup) (dev);
+}
+
+void fec_mii_shutdown(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ const struct fec_platform_info *fpi = fep->fpi;
+
+ if (!fpi->use_mdio || fep->phy == NULL)
+ return;
+
+ if (fep->phy->shutdown == NULL)
+ return;
+
+ (*fep->phy->shutdown) (dev);
+}
+
+void fec_mii_ack_int(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ const struct fec_platform_info *fpi = fep->fpi;
+
+ if (!fpi->use_mdio || fep->phy == NULL)
+ return;
+
+ if (fep->phy->ack_int == NULL)
+ return;
+
+ (*fep->phy->ack_int) (dev);
+}
+
+/* helper function */
+static int mii_negotiated(struct mii_if_info *mii)
+{
+ int advert, lpa, val;
+
+ if (!mii_link_ok(mii))
+ return 0;
+
+ val = (*mii->mdio_read) (mii->dev, mii->phy_id, MII_BMSR);
+ if ((val & BMSR_ANEGCOMPLETE) == 0)
+ return 0;
+
+ advert = (*mii->mdio_read) (mii->dev, mii->phy_id, MII_ADVERTISE);
+ lpa = (*mii->mdio_read) (mii->dev, mii->phy_id, MII_LPA);
+
+ return mii_nway_result(advert & lpa);
+}
+
+void fec_mii_link_status_change_check(struct net_device *dev, int init_media)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ unsigned int media;
+ unsigned long flags;
+
+ if (mii_check_media(&fep->mii_if, netif_msg_link(fep), init_media) == 0)
+ return;
+
+ media = mii_negotiated(&fep->mii_if);
+
+ if (netif_carrier_ok(dev)) {
+ spin_lock_irqsave(&fep->lock, flags);
+ fec_restart(dev, !!(media & ADVERTISE_FULL),
+ (media & (ADVERTISE_100FULL | ADVERTISE_100HALF)) ?
+ 100 : 10);
+ spin_unlock_irqrestore(&fep->lock, flags);
+
+ netif_start_queue(dev);
+ } else {
+ netif_stop_queue(dev);
+
+ spin_lock_irqsave(&fep->lock, flags);
+ fec_stop(dev);
+ spin_unlock_irqrestore(&fep->lock, flags);
+
+ }
+}
write_lock(&disc_data_lock);
sp = tty->disc_data;
- tty->disc_data = 0;
+ tty->disc_data = NULL;
write_unlock(&disc_data_lock);
if (sp == 0)
return;
unregister_netdev(sp->dev);
}
-static int sp_set_mac_address(struct net_device *dev, void *addr)
+static int sp_set_mac_address(struct net_device *dev, void __user *addr)
{
return copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN) ? -EFAULT : 0;
}
switch(cmd) {
case SIOCGIFNAME:
- err = copy_to_user((void *) arg, sp->dev->name,
+ err = copy_to_user((void __user *) arg, sp->dev->name,
strlen(sp->dev->name) + 1) ? -EFAULT : 0;
break;
case SIOCGIFENCAP:
- err = put_user(0, (int *)arg);
+ err = put_user(0, (int __user *)arg);
break;
case SIOCSIFENCAP:
- if (get_user(tmp, (int *) arg)) {
+ if (get_user(tmp, (int __user *) arg)) {
err = -EFAULT;
break;
}
break;
case SIOCSIFHWADDR:
- err = sp_set_mac_address(sp->dev, (void *) arg);
+ err = sp_set_mac_address(sp->dev, (void __user *) arg);
break;
/* Allow stty to read, but not set, the serial port */
unregister_netdev(ax->dev);
- tty->disc_data = 0;
+ tty->disc_data = NULL;
ax->tty = NULL;
ax_free(ax);
}
-static int ax_set_mac_address(struct net_device *dev, void *addr)
+static int ax_set_mac_address(struct net_device *dev, void __user *addr)
{
if (copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN))
return -EFAULT;
/* Perform I/O control on an active ax25 channel. */
-static int ax25_disp_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
+static int ax25_disp_ioctl(struct tty_struct *tty, void *file, int cmd, void __user *arg)
{
struct ax_disp *ax = (struct ax_disp *) tty->disc_data;
unsigned int tmp;
return 0;
case SIOCGIFENCAP:
- return put_user(4, (int *)arg);
+ return put_user(4, (int __user *)arg);
case SIOCSIFENCAP:
- if (get_user(tmp, (int *)arg))
+ if (get_user(tmp, (int __user *)arg))
return -EFAULT;
ax->mode = tmp;
ax->dev->addr_len = AX25_ADDR_LEN; /* sizeof an AX.25 addr */
* ------------------
*
* You can find a subset of the documentation in
- * linux/Documentation/networking/z8530drv.txt.
+ * Documentation/networking/z8530drv.txt.
*/
/*
for (i = 0; i < N_RX_RING; ++i) {
if (mp->rx_bufs[i] != 0) {
dev_kfree_skb(mp->rx_bufs[i]);
- mp->rx_bufs[i] = 0;
+ mp->rx_bufs[i] = NULL;
}
}
for (i = mp->tx_empty; i != mp->tx_fill; ) {
cp->xfer_status = 0;
++cp;
}
- mp->rx_bufs[i] = 0;
+ mp->rx_bufs[i] = NULL;
st_le16(&cp->command, DBDMA_STOP);
mp->rx_fill = i;
mp->rx_empty = 0;
mp->stats.rx_bytes += skb->len;
netif_rx(skb);
dev->last_rx = jiffies;
- mp->rx_bufs[i] = 0;
+ mp->rx_bufs[i] = NULL;
++mp->stats.rx_packets;
}
} else {
static int __init myri_sbus_probe(void)
{
struct sbus_bus *bus;
- struct sbus_dev *sdev = 0;
+ struct sbus_dev *sdev = NULL;
static int called;
int cards = 0, v;
#include "8390.h"
+#define DRV_NAME "ne-h8300"
+
/* Some defines that people can play with if so inclined. */
/* Do we perform extra sanity checks on stuff ? */
release_region(dev->base_addr, NE_IO_EXTENT);
}
+#ifndef MODULE
struct net_device * __init ne_probe(int unit)
{
struct net_device *dev = alloc_ei_netdev();
free_netdev(dev);
return ERR_PTR(err);
}
+#endif
static int __init ne_probe1(struct net_device *dev, int ioaddr)
{
struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev);
unsigned char bus_width;
- if (!request_region(ioaddr, NE_IO_EXTENT, dev->name))
+ if (!request_region(ioaddr, NE_IO_EXTENT, DRV_NAME))
return -EBUSY;
reg0 = inb_p(ioaddr);
return 0;
}
\f
+/*
+ * wait_for_buffer
+ *
+ * This routine waits for the SEEQ chip to assert that the FIFO is ready
+ * by checking for a window interrupt, and then clearing it. This has to
+ * occur in the interrupt handler!
+ */
+inline void wait_for_buffer(struct net_device * dev)
+{
+ int ioaddr = dev->base_addr;
+ unsigned long tmp;
+ int status;
+
+ tmp = jiffies + HZ;
+ while ( ( ((status=inw(SEEQ_STATUS)) & SEEQSTAT_WINDOW_INT) != SEEQSTAT_WINDOW_INT) && time_before(jiffies, tmp))
+ cpu_relax();
+
+ if ( (status & SEEQSTAT_WINDOW_INT) == SEEQSTAT_WINDOW_INT)
+ outw( SEEQCMD_WINDOW_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
+}
+\f
/* The typical workload of the driver:
Handle the network interface interrupts. */
static irqreturn_t seeq8005_interrupt(int irq, void *dev_id, struct pt_regs * regs)
}
-/*
- * wait_for_buffer
- *
- * This routine waits for the SEEQ chip to assert that the FIFO is ready
- * by checking for a window interrupt, and then clearing it. This has to
- * occur in the interrupt handler!
- */
-inline void wait_for_buffer(struct net_device * dev)
-{
- int ioaddr = dev->base_addr;
- unsigned long tmp;
- int status;
-
- tmp = jiffies + HZ;
- while ( ( ((status=inw(SEEQ_STATUS)) & SEEQSTAT_WINDOW_INT) != SEEQSTAT_WINDOW_INT) && time_before(jiffies, tmp))
- cpu_relax();
-
- if ( (status & SEEQSTAT_WINDOW_INT) == SEEQSTAT_WINDOW_INT)
- outw( SEEQCMD_WINDOW_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
-}
-
#ifdef MODULE
static struct net_device *dev_seeq;
if (!sl || sl->magic != SLIP_MAGIC || sl->tty != tty)
return;
- tty->disc_data = 0;
+ tty->disc_data = NULL;
sl->tty = NULL;
if (!sl->leased)
sl->line = 0;
--- /dev/null
+/*
+ * smc91x.c
+ * This is a driver for SMSC's 91C9x/91C1xx single-chip Ethernet devices.
+ *
+ * Copyright (C) 1996 by Erik Stahlman
+ * Copyright (C) 2001 Standard Microsystems Corporation
+ * Developed by Simple Network Magic Corporation
+ * Copyright (C) 2003 Monta Vista Software, Inc.
+ * Unified SMC91x driver by Nicolas Pitre
+ *
+ * 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
+ *
+ * Arguments:
+ * io = for the base address
+ * irq = for the IRQ
+ * nowait = 0 for normal wait states, 1 eliminates additional wait states
+ *
+ * original author:
+ * Erik Stahlman <erik@vt.edu>
+ *
+ * hardware multicast code:
+ * Peter Cammaert <pc@denkart.be>
+ *
+ * contributors:
+ * Daris A Nevil <dnevil@snmc.com>
+ * Nicolas Pitre <nico@cam.org>
+ * Russell King <rmk@arm.linux.org.uk>
+ *
+ * History:
+ * 08/20/00 Arnaldo Melo fix kfree(skb) in smc_hardware_send_packet
+ * 12/15/00 Christian Jullien fix "Warning: kfree_skb on hard IRQ"
+ * 03/16/01 Daris A Nevil modified smc9194.c for use with LAN91C111
+ * 08/22/01 Scott Anderson merge changes from smc9194 to smc91111
+ * 08/21/01 Pramod B Bhardwaj added support for RevB of LAN91C111
+ * 12/20/01 Jeff Sutherland initial port to Xscale PXA with DMA support
+ * 04/07/03 Nicolas Pitre unified SMC91x driver, killed irq races,
+ * more bus abstraction, big cleanup, etc.
+ * 29/09/03 Russell King - add driver model support
+ * - ethtool support
+ * - convert to use generic MII interface
+ * - add link up/down notification
+ * - don't try to handle full negotiation in
+ * smc_phy_configure
+ * - clean up (and fix stack overrun) in PHY
+ * MII read/write functions
+ */
+static const char version[] =
+ "smc91x.c: v1.0, mar 07 2003 by Nicolas Pitre <nico@cam.org>\n";
+
+/* Debugging level */
+#ifndef SMC_DEBUG
+#define SMC_DEBUG 0
+#endif
+
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/crc32.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "smc91x.h"
+
+#ifdef CONFIG_ISA
+/*
+ * the LAN91C111 can be at any of the following port addresses. To change,
+ * for a slightly different card, you can add it to the array. Keep in
+ * mind that the array must end in zero.
+ */
+static unsigned int smc_portlist[] __initdata = {
+ 0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0,
+ 0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0, 0x3C0, 0x3E0, 0
+};
+
+#ifndef SMC_IOADDR
+# define SMC_IOADDR -1
+#endif
+static unsigned long io = SMC_IOADDR;
+module_param(io, ulong, 0400);
+MODULE_PARM_DESC(io, "I/O base address");
+
+#ifndef SMC_IRQ
+# define SMC_IRQ -1
+#endif
+static int irq = SMC_IRQ;
+module_param(irq, int, 0400);
+MODULE_PARM_DESC(irq, "IRQ number");
+
+#endif /* CONFIG_ISA */
+
+#ifndef SMC_NOWAIT
+# define SMC_NOWAIT 0
+#endif
+static int nowait = SMC_NOWAIT;
+module_param(nowait, int, 0400);
+MODULE_PARM_DESC(nowait, "set to 1 for no wait state");
+
+/*
+ * Transmit timeout, default 5 seconds.
+ */
+static int watchdog = 5000;
+module_param(watchdog, int, 0400);
+MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
+
+MODULE_LICENSE("GPL");
+
+/*
+ * The internal workings of the driver. If you are changing anything
+ * here with the SMC stuff, you should have the datasheet and know
+ * what you are doing.
+ */
+#define CARDNAME "smc91x"
+
+/*
+ * Use power-down feature of the chip
+ */
+#define POWER_DOWN 1
+
+/*
+ * Wait time for memory to be free. This probably shouldn't be
+ * tuned that much, as waiting for this means nothing else happens
+ * in the system
+ */
+#define MEMORY_WAIT_TIME 16
+
+/*
+ * This selects whether TX packets are sent one by one to the SMC91x internal
+ * memory and throttled until transmission completes. This may prevent
+ * RX overruns a litle by keeping much of the memory free for RX packets
+ * but to the expense of reduced TX throughput and increased IRQ overhead.
+ * Note this is not a cure for a too slow data bus or too high IRQ latency.
+ */
+#define THROTTLE_TX_PKTS 0
+
+/*
+ * The MII clock high/low times. 2x this number gives the MII clock period
+ * in microseconds. (was 50, but this gives 6.4ms for each MII transaction!)
+ */
+#define MII_DELAY 1
+
+/* store this information for the driver.. */
+struct smc_local {
+ /*
+ * If I have to wait until memory is available to send a
+ * packet, I will store the skbuff here, until I get the
+ * desired memory. Then, I'll send it out and free it.
+ */
+ struct sk_buff *saved_skb;
+
+ /*
+ * these are things that the kernel wants me to keep, so users
+ * can find out semi-useless statistics of how well the card is
+ * performing
+ */
+ struct net_device_stats stats;
+
+ /* version/revision of the SMC91x chip */
+ int version;
+
+ /* Contains the current active transmission mode */
+ int tcr_cur_mode;
+
+ /* Contains the current active receive mode */
+ int rcr_cur_mode;
+
+ /* Contains the current active receive/phy mode */
+ int rpc_cur_mode;
+ int ctl_rfduplx;
+ int ctl_rspeed;
+
+ u32 msg_enable;
+ u32 phy_type;
+ struct mii_if_info mii;
+ spinlock_t lock;
+
+#ifdef SMC_USE_PXA_DMA
+ /* DMA needs the physical address of the chip */
+ u_long physaddr;
+#endif
+};
+
+#if SMC_DEBUG > 0
+#define DBG(n, args...) \
+ do { \
+ if (SMC_DEBUG >= (n)) \
+ printk(KERN_DEBUG args); \
+ } while (0)
+
+#define PRINTK(args...) printk(args)
+#else
+#define DBG(n, args...) do { } while(0)
+#define PRINTK(args...) printk(KERN_DEBUG args)
+#endif
+
+#if SMC_DEBUG > 3
+static void PRINT_PKT(u_char *buf, int length)
+{
+ int i;
+ int remainder;
+ int lines;
+
+ lines = length / 16;
+ remainder = length % 16;
+
+ for (i = 0; i < lines ; i ++) {
+ int cur;
+ for (cur = 0; cur < 8; cur++) {
+ u_char a, b;
+ a = *buf++;
+ b = *buf++;
+ printk("%02x%02x ", a, b);
+ }
+ printk("\n");
+ }
+ for (i = 0; i < remainder/2 ; i++) {
+ u_char a, b;
+ a = *buf++;
+ b = *buf++;
+ printk("%02x%02x ", a, b);
+ }
+ printk("\n");
+}
+#else
+#define PRINT_PKT(x...) do { } while(0)
+#endif
+
+
+/* this enables an interrupt in the interrupt mask register */
+#define SMC_ENABLE_INT(x) do { \
+ unsigned long flags; \
+ unsigned char mask; \
+ spin_lock_irqsave(&lp->lock, flags); \
+ mask = SMC_GET_INT_MASK(); \
+ mask |= (x); \
+ SMC_SET_INT_MASK(mask); \
+ spin_unlock_irqrestore(&lp->lock, flags); \
+} while (0)
+
+/* this disables an interrupt from the interrupt mask register */
+#define SMC_DISABLE_INT(x) do { \
+ unsigned long flags; \
+ unsigned char mask; \
+ spin_lock_irqsave(&lp->lock, flags); \
+ mask = SMC_GET_INT_MASK(); \
+ mask &= ~(x); \
+ SMC_SET_INT_MASK(mask); \
+ spin_unlock_irqrestore(&lp->lock, flags); \
+} while (0)
+
+/*
+ * Wait while MMU is busy. This is usually in the order of a few nanosecs
+ * if at all, but let's avoid deadlocking the system if the hardware
+ * decides to go south.
+ */
+#define SMC_WAIT_MMU_BUSY() do { \
+ if (unlikely(SMC_GET_MMU_CMD() & MC_BUSY)) { \
+ unsigned long timeout = jiffies + 2; \
+ while (SMC_GET_MMU_CMD() & MC_BUSY) { \
+ if (time_after(jiffies, timeout)) { \
+ printk("%s: timeout %s line %d\n", \
+ dev->name, __FILE__, __LINE__); \
+ break; \
+ } \
+ cpu_relax(); \
+ } \
+ } \
+} while (0)
+
+
+/*
+ * this does a soft reset on the device
+ */
+static void smc_reset(struct net_device *dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ unsigned int ctl, cfg;
+
+ DBG(2, "%s: %s\n", dev->name, __FUNCTION__);
+
+ /*
+ * This resets the registers mostly to defaults, but doesn't
+ * affect EEPROM. That seems unnecessary
+ */
+ SMC_SELECT_BANK(0);
+ SMC_SET_RCR(RCR_SOFTRST);
+
+ /*
+ * Setup the Configuration Register
+ * This is necessary because the CONFIG_REG is not affected
+ * by a soft reset
+ */
+ SMC_SELECT_BANK(1);
+
+ cfg = CONFIG_DEFAULT;
+
+ /*
+ * Setup for fast accesses if requested. If the card/system
+ * can't handle it then there will be no recovery except for
+ * a hard reset or power cycle
+ */
+ if (nowait)
+ cfg |= CONFIG_NO_WAIT;
+
+ /*
+ * Release from possible power-down state
+ * Configuration register is not affected by Soft Reset
+ */
+ cfg |= CONFIG_EPH_POWER_EN;
+
+ SMC_SET_CONFIG(cfg);
+
+ /* this should pause enough for the chip to be happy */
+ /*
+ * elaborate? What does the chip _need_? --jgarzik
+ *
+ * This seems to be undocumented, but something the original
+ * driver(s) have always done. Suspect undocumented timing
+ * info/determined empirically. --rmk
+ */
+ udelay(1);
+
+ /* Disable transmit and receive functionality */
+ SMC_SELECT_BANK(0);
+ SMC_SET_RCR(RCR_CLEAR);
+ SMC_SET_TCR(TCR_CLEAR);
+
+ SMC_SELECT_BANK(1);
+ ctl = SMC_GET_CTL() | CTL_LE_ENABLE;
+
+ /*
+ * Set the control register to automatically release successfully
+ * transmitted packets, to make the best use out of our limited
+ * memory
+ */
+#if ! THROTTLE_TX_PKTS
+ ctl |= CTL_AUTO_RELEASE;
+#else
+ ctl &= ~CTL_AUTO_RELEASE;
+#endif
+ SMC_SET_CTL(ctl);
+
+ /* Disable all interrupts */
+ SMC_SELECT_BANK(2);
+ SMC_SET_INT_MASK(0);
+
+ /* Reset the MMU */
+ SMC_SET_MMU_CMD(MC_RESET);
+ SMC_WAIT_MMU_BUSY();
+}
+
+/*
+ * Enable Interrupts, Receive, and Transmit
+ */
+static void smc_enable(struct net_device *dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ struct smc_local *lp = netdev_priv(dev);
+ int mask;
+
+ DBG(2, "%s: %s\n", dev->name, __FUNCTION__);
+
+ /* see the header file for options in TCR/RCR DEFAULT */
+ SMC_SELECT_BANK(0);
+ SMC_SET_TCR(lp->tcr_cur_mode);
+ SMC_SET_RCR(lp->rcr_cur_mode);
+
+ /* now, enable interrupts */
+ mask = IM_EPH_INT|IM_RX_OVRN_INT|IM_RCV_INT;
+ if (lp->version >= (CHIP_91100 << 4))
+ mask |= IM_MDINT;
+ SMC_SELECT_BANK(2);
+ SMC_SET_INT_MASK(mask);
+}
+
+/*
+ * this puts the device in an inactive state
+ */
+static void smc_shutdown(unsigned long ioaddr)
+{
+ DBG(2, "%s: %s\n", CARDNAME, __FUNCTION__);
+
+ /* no more interrupts for me */
+ SMC_SELECT_BANK(2);
+ SMC_SET_INT_MASK(0);
+
+ /* and tell the card to stay away from that nasty outside world */
+ SMC_SELECT_BANK(0);
+ SMC_SET_RCR(RCR_CLEAR);
+ SMC_SET_TCR(TCR_CLEAR);
+
+#ifdef POWER_DOWN
+ /* finally, shut the chip down */
+ SMC_SELECT_BANK(1);
+ SMC_SET_CONFIG(SMC_GET_CONFIG() & ~CONFIG_EPH_POWER_EN);
+#endif
+}
+
+/*
+ * This is the procedure to handle the receipt of a packet.
+ */
+static inline void smc_rcv(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ unsigned int packet_number, status, packet_len;
+
+ DBG(3, "%s: %s\n", dev->name, __FUNCTION__);
+
+ packet_number = SMC_GET_RXFIFO();
+ if (unlikely(packet_number & RXFIFO_REMPTY)) {
+ PRINTK("%s: smc_rcv with nothing on FIFO.\n", dev->name);
+ return;
+ }
+
+ /* read from start of packet */
+ SMC_SET_PTR(PTR_READ | PTR_RCV | PTR_AUTOINC);
+
+ /* First two words are status and packet length */
+ SMC_GET_PKT_HDR(status, packet_len);
+ packet_len &= 0x07ff; /* mask off top bits */
+ DBG(2, "%s: RX PNR 0x%x STATUS 0x%04x LENGTH 0x%04x (%d)\n",
+ dev->name, packet_number, status,
+ packet_len, packet_len);
+
+ if (unlikely(status & RS_ERRORS)) {
+ lp->stats.rx_errors++;
+ if (status & RS_ALGNERR)
+ lp->stats.rx_frame_errors++;
+ if (status & (RS_TOOSHORT | RS_TOOLONG))
+ lp->stats.rx_length_errors++;
+ if (status & RS_BADCRC)
+ lp->stats.rx_crc_errors++;
+ } else {
+ struct sk_buff *skb;
+ unsigned char *data;
+ unsigned int data_len;
+
+ /* set multicast stats */
+ if (status & RS_MULTICAST)
+ lp->stats.multicast++;
+
+ /*
+ * Actual payload is packet_len - 4 (or 3 if odd byte).
+ * We want skb_reserve(2) and the final ctrl word
+ * (2 bytes, possibly containing the payload odd byte).
+ * Ence packet_len - 4 + 2 + 2.
+ */
+ skb = dev_alloc_skb(packet_len);
+ if (unlikely(skb == NULL)) {
+ printk(KERN_NOTICE "%s: Low memory, packet dropped.\n",
+ dev->name);
+ lp->stats.rx_dropped++;
+ goto done;
+ }
+
+ /* Align IP header to 32 bits */
+ skb_reserve(skb, 2);
+
+ /* BUG: the LAN91C111 rev A never sets this bit. Force it. */
+ if (lp->version == 0x90)
+ status |= RS_ODDFRAME;
+
+ /*
+ * If odd length: packet_len - 3,
+ * otherwise packet_len - 4.
+ */
+ data_len = packet_len - ((status & RS_ODDFRAME) ? 3 : 4);
+ data = skb_put(skb, data_len);
+ SMC_PULL_DATA(data, packet_len - 2);
+
+ PRINT_PKT(data, packet_len - 2);
+
+ dev->last_rx = jiffies;
+ skb->dev = dev;
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ lp->stats.rx_bytes += data_len;
+ }
+
+done:
+ SMC_WAIT_MMU_BUSY();
+ SMC_SET_MMU_CMD(MC_RELEASE);
+}
+
+/*
+ * This is called to actually send a packet to the chip.
+ * Returns non-zero when successful.
+ */
+static void smc_hardware_send_packet(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ struct sk_buff *skb = lp->saved_skb;
+ unsigned int packet_no, len;
+ unsigned char *buf;
+
+ DBG(3, "%s: %s\n", dev->name, __FUNCTION__);
+
+ packet_no = SMC_GET_AR();
+ if (unlikely(packet_no & AR_FAILED)) {
+ printk("%s: Memory allocation failed.\n", dev->name);
+ lp->saved_skb = NULL;
+ lp->stats.tx_errors++;
+ lp->stats.tx_fifo_errors++;
+ dev_kfree_skb_any(skb);
+ return;
+ }
+
+ /* point to the beginning of the packet */
+ SMC_SET_PN(packet_no);
+ SMC_SET_PTR(PTR_AUTOINC);
+
+ buf = skb->data;
+ len = skb->len;
+ DBG(2, "%s: TX PNR 0x%x LENGTH 0x%04x (%d) BUF 0x%p\n",
+ dev->name, packet_no, len, len, buf);
+ PRINT_PKT(buf, len);
+
+ /*
+ * Send the packet length (+6 for status words, length, and ctl.
+ * The card will pad to 64 bytes with zeroes if packet is too small.
+ */
+ SMC_PUT_PKT_HDR(0, len + 6);
+
+ /* send the actual data */
+ SMC_PUSH_DATA(buf, len & ~1);
+
+ /* Send final ctl word with the last byte if there is one */
+ SMC_outw(((len & 1) ? (0x2000 | buf[len-1]) : 0), ioaddr, DATA_REG);
+
+ /* and let the chipset deal with it */
+ SMC_SET_MMU_CMD(MC_ENQUEUE);
+ SMC_ACK_INT(IM_TX_EMPTY_INT);
+
+ dev->trans_start = jiffies;
+ dev_kfree_skb_any(skb);
+ lp->saved_skb = NULL;
+ lp->stats.tx_packets++;
+ lp->stats.tx_bytes += len;
+}
+
+/*
+ * Since I am not sure if I will have enough room in the chip's ram
+ * to store the packet, I call this routine which either sends it
+ * now, or set the card to generates an interrupt when ready
+ * for the packet.
+ */
+static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ unsigned int numPages, poll_count, status, saved_bank;
+
+ DBG(3, "%s: %s\n", dev->name, __FUNCTION__);
+
+ BUG_ON(lp->saved_skb != NULL);
+ lp->saved_skb = skb;
+
+ /*
+ * The MMU wants the number of pages to be the number of 256 bytes
+ * 'pages', minus 1 (since a packet can't ever have 0 pages :))
+ *
+ * The 91C111 ignores the size bits, but earlier models don't.
+ *
+ * Pkt size for allocating is data length +6 (for additional status
+ * words, length and ctl)
+ *
+ * If odd size then last byte is included in ctl word.
+ */
+ numPages = ((skb->len & ~1) + (6 - 1)) >> 8;
+ if (unlikely(numPages > 7)) {
+ printk("%s: Far too big packet error.\n", dev->name);
+ lp->saved_skb = NULL;
+ lp->stats.tx_errors++;
+ lp->stats.tx_dropped++;
+ dev_kfree_skb(skb);
+ return 0;
+ }
+
+ /* now, try to allocate the memory */
+ saved_bank = SMC_CURRENT_BANK();
+ SMC_SELECT_BANK(2);
+ SMC_SET_MMU_CMD(MC_ALLOC | numPages);
+
+ /*
+ * Poll the chip for a short amount of time in case the
+ * allocation succeeds quickly.
+ */
+ poll_count = MEMORY_WAIT_TIME;
+ do {
+ status = SMC_GET_INT();
+ if (status & IM_ALLOC_INT) {
+ SMC_ACK_INT(IM_ALLOC_INT);
+ break;
+ }
+ } while (--poll_count);
+
+ if (!poll_count) {
+ /* oh well, wait until the chip finds memory later */
+ netif_stop_queue(dev);
+ DBG(2, "%s: TX memory allocation deferred.\n", dev->name);
+ SMC_ENABLE_INT(IM_ALLOC_INT);
+ } else {
+ /*
+ * Allocation succeeded: push packet to the chip's own memory
+ * immediately.
+ *
+ * If THROTTLE_TX_PKTS is selected that means we don't want
+ * more than a single TX packet taking up space in the chip's
+ * internal memory at all time, in which case we stop the
+ * queue right here until we're notified of TX completion.
+ *
+ * Otherwise we're quite happy to feed more TX packets right
+ * away for better TX throughput, in which case the queue is
+ * left active.
+ */
+#if THROTTLE_TX_PKTS
+ netif_stop_queue(dev);
+#endif
+ smc_hardware_send_packet(dev);
+ SMC_ENABLE_INT(IM_TX_INT | IM_TX_EMPTY_INT);
+ }
+
+ SMC_SELECT_BANK(saved_bank);
+ return 0;
+}
+
+/*
+ * This handles a TX interrupt, which is only called when:
+ * - a TX error occurred, or
+ * - CTL_AUTO_RELEASE is not set and TX of a packet completed.
+ */
+static void smc_tx(struct net_device *dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ struct smc_local *lp = netdev_priv(dev);
+ unsigned int saved_packet, packet_no, tx_status, pkt_len;
+
+ DBG(3, "%s: %s\n", dev->name, __FUNCTION__);
+
+ /* If the TX FIFO is empty then nothing to do */
+ packet_no = SMC_GET_TXFIFO();
+ if (unlikely(packet_no & TXFIFO_TEMPTY)) {
+ PRINTK("%s: smc_tx with nothing on FIFO.\n", dev->name);
+ return;
+ }
+
+ /* select packet to read from */
+ saved_packet = SMC_GET_PN();
+ SMC_SET_PN(packet_no);
+
+ /* read the first word (status word) from this packet */
+ SMC_SET_PTR(PTR_AUTOINC | PTR_READ);
+ SMC_GET_PKT_HDR(tx_status, pkt_len);
+ DBG(2, "%s: TX STATUS 0x%04x PNR 0x%02x\n",
+ dev->name, tx_status, packet_no);
+
+ if (!(tx_status & TS_SUCCESS))
+ lp->stats.tx_errors++;
+ if (tx_status & TS_LOSTCAR)
+ lp->stats.tx_carrier_errors++;
+
+ if (tx_status & TS_LATCOL) {
+ PRINTK("%s: late collision occurred on last xmit\n", dev->name);
+ lp->stats.tx_window_errors++;
+ if (!(lp->stats.tx_window_errors & 63) && net_ratelimit()) {
+ printk(KERN_INFO "%s: unexpectedly large numbers of "
+ "late collisions. Please check duplex "
+ "setting.\n", dev->name);
+ }
+ }
+
+ /* kill the packet */
+ SMC_WAIT_MMU_BUSY();
+ SMC_SET_MMU_CMD(MC_FREEPKT);
+
+ /* Don't restore Packet Number Reg until busy bit is cleared */
+ SMC_WAIT_MMU_BUSY();
+ SMC_SET_PN(saved_packet);
+
+ /* re-enable transmit */
+ SMC_SELECT_BANK(0);
+ SMC_SET_TCR(lp->tcr_cur_mode);
+ SMC_SELECT_BANK(2);
+}
+
+
+/*---PHY CONTROL AND CONFIGURATION-----------------------------------------*/
+
+static void smc_mii_out(struct net_device *dev, unsigned int val, int bits)
+{
+ unsigned long ioaddr = dev->base_addr;
+ unsigned int mii_reg, mask;
+
+ mii_reg = SMC_GET_MII() & ~(MII_MCLK | MII_MDOE | MII_MDO);
+ mii_reg |= MII_MDOE;
+
+ for (mask = 1 << (bits - 1); mask; mask >>= 1) {
+ if (val & mask)
+ mii_reg |= MII_MDO;
+ else
+ mii_reg &= ~MII_MDO;
+
+ SMC_SET_MII(mii_reg);
+ udelay(MII_DELAY);
+ SMC_SET_MII(mii_reg | MII_MCLK);
+ udelay(MII_DELAY);
+ }
+}
+
+static unsigned int smc_mii_in(struct net_device *dev, int bits)
+{
+ unsigned long ioaddr = dev->base_addr;
+ unsigned int mii_reg, mask, val;
+
+ mii_reg = SMC_GET_MII() & ~(MII_MCLK | MII_MDOE | MII_MDO);
+ SMC_SET_MII(mii_reg);
+
+ for (mask = 1 << (bits - 1), val = 0; mask; mask >>= 1) {
+ if (SMC_GET_MII() & MII_MDI)
+ val |= mask;
+
+ SMC_SET_MII(mii_reg);
+ udelay(MII_DELAY);
+ SMC_SET_MII(mii_reg | MII_MCLK);
+ udelay(MII_DELAY);
+ }
+
+ return val;
+}
+
+/*
+ * Reads a register from the MII Management serial interface
+ */
+static int smc_phy_read(struct net_device *dev, int phyaddr, int phyreg)
+{
+ unsigned long ioaddr = dev->base_addr;
+ unsigned int phydata, old_bank;
+
+ /* Save the current bank, and select bank 3 */
+ old_bank = SMC_CURRENT_BANK();
+ SMC_SELECT_BANK(3);
+
+ /* Idle - 32 ones */
+ smc_mii_out(dev, 0xffffffff, 32);
+
+ /* Start code (01) + read (10) + phyaddr + phyreg */
+ smc_mii_out(dev, 6 << 10 | phyaddr << 5 | phyreg, 14);
+
+ /* Turnaround (2bits) + phydata */
+ phydata = smc_mii_in(dev, 18);
+
+ /* Return to idle state */
+ SMC_SET_MII(SMC_GET_MII() & ~(MII_MCLK|MII_MDOE|MII_MDO));
+
+ /* And select original bank */
+ SMC_SELECT_BANK(old_bank);
+
+ DBG(3, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n",
+ __FUNCTION__, phyaddr, phyreg, phydata);
+
+ return phydata;
+}
+
+/*
+ * Writes a register to the MII Management serial interface
+ */
+static void smc_phy_write(struct net_device *dev, int phyaddr, int phyreg,
+ int phydata)
+{
+ unsigned long ioaddr = dev->base_addr;
+ unsigned int old_bank;
+
+ /* Save the current bank, and select bank 3 */
+ old_bank = SMC_CURRENT_BANK();
+ SMC_SELECT_BANK(3);
+
+ /* Idle - 32 ones */
+ smc_mii_out(dev, 0xffffffff, 32);
+
+ /* Start code (01) + write (01) + phyaddr + phyreg + turnaround + phydata */
+ smc_mii_out(dev, 5 << 28 | phyaddr << 23 | phyreg << 18 | 2 << 16 | phydata, 32);
+
+ /* Return to idle state */
+ SMC_SET_MII(SMC_GET_MII() & ~(MII_MCLK|MII_MDOE|MII_MDO));
+
+ /* And select original bank */
+ SMC_SELECT_BANK(old_bank);
+
+ DBG(3, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n",
+ __FUNCTION__, phyaddr, phyreg, phydata);
+}
+
+/*
+ * Finds and reports the PHY address
+ */
+static void smc_detect_phy(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ int phyaddr;
+
+ DBG(2, "%s: %s\n", dev->name, __FUNCTION__);
+
+ lp->phy_type = 0;
+
+ /*
+ * Scan all 32 PHY addresses if necessary, starting at
+ * PHY#1 to PHY#31, and then PHY#0 last.
+ */
+ for (phyaddr = 1; phyaddr < 33; ++phyaddr) {
+ unsigned int id1, id2;
+
+ /* Read the PHY identifiers */
+ id1 = smc_phy_read(dev, phyaddr & 31, MII_PHYSID1);
+ id2 = smc_phy_read(dev, phyaddr & 31, MII_PHYSID2);
+
+ DBG(3, "%s: phy_id1=0x%x, phy_id2=0x%x\n",
+ dev->name, id1, id2);
+
+ /* Make sure it is a valid identifier */
+ if (id1 != 0x0000 && id1 != 0xffff && id1 != 0x8000 &&
+ id2 != 0x0000 && id2 != 0xffff && id2 != 0x8000) {
+ /* Save the PHY's address */
+ lp->mii.phy_id = phyaddr & 31;
+ lp->phy_type = id1 << 16 | id2;
+ break;
+ }
+ }
+}
+
+/*
+ * Sets the PHY to a configuration as determined by the user
+ */
+static int smc_phy_fixed(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ int phyaddr = lp->mii.phy_id;
+ int bmcr, cfg1;
+
+ DBG(3, "%s: %s\n", dev->name, __FUNCTION__);
+
+ /* Enter Link Disable state */
+ cfg1 = smc_phy_read(dev, phyaddr, PHY_CFG1_REG);
+ cfg1 |= PHY_CFG1_LNKDIS;
+ smc_phy_write(dev, phyaddr, PHY_CFG1_REG, cfg1);
+
+ /*
+ * Set our fixed capabilities
+ * Disable auto-negotiation
+ */
+ bmcr = 0;
+
+ if (lp->ctl_rfduplx)
+ bmcr |= BMCR_FULLDPLX;
+
+ if (lp->ctl_rspeed == 100)
+ bmcr |= BMCR_SPEED100;
+
+ /* Write our capabilities to the phy control register */
+ smc_phy_write(dev, phyaddr, MII_BMCR, bmcr);
+
+ /* Re-Configure the Receive/Phy Control register */
+ SMC_SET_RPC(lp->rpc_cur_mode);
+
+ return 1;
+}
+
+/*
+ * smc_phy_reset - reset the phy
+ * @dev: net device
+ * @phy: phy address
+ *
+ * Issue a software reset for the specified PHY and
+ * wait up to 100ms for the reset to complete. We should
+ * not access the PHY for 50ms after issuing the reset.
+ *
+ * The time to wait appears to be dependent on the PHY.
+ *
+ * Must be called with lp->lock locked.
+ */
+static int smc_phy_reset(struct net_device *dev, int phy)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ unsigned int bmcr;
+ int timeout;
+
+ smc_phy_write(dev, phy, MII_BMCR, BMCR_RESET);
+
+ for (timeout = 2; timeout; timeout--) {
+ spin_unlock_irq(&lp->lock);
+ msleep(50);
+ spin_lock_irq(&lp->lock);
+
+ bmcr = smc_phy_read(dev, phy, MII_BMCR);
+ if (!(bmcr & BMCR_RESET))
+ break;
+ }
+
+ return bmcr & BMCR_RESET;
+}
+
+/*
+ * smc_phy_powerdown - powerdown phy
+ * @dev: net device
+ * @phy: phy address
+ *
+ * Power down the specified PHY
+ */
+static void smc_phy_powerdown(struct net_device *dev, int phy)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ unsigned int bmcr;
+
+ spin_lock_irq(&lp->lock);
+ bmcr = smc_phy_read(dev, phy, MII_BMCR);
+ smc_phy_write(dev, phy, MII_BMCR, bmcr | BMCR_PDOWN);
+ spin_unlock_irq(&lp->lock);
+}
+
+/*
+ * smc_phy_check_media - check the media status and adjust TCR
+ * @dev: net device
+ * @init: set true for initialisation
+ *
+ * Select duplex mode depending on negotiation state. This
+ * also updates our carrier state.
+ */
+static void smc_phy_check_media(struct net_device *dev, int init)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+
+ if (mii_check_media(&lp->mii, netif_msg_link(lp), init)) {
+ unsigned int old_bank;
+
+ /* duplex state has changed */
+ if (lp->mii.full_duplex) {
+ lp->tcr_cur_mode |= TCR_SWFDUP;
+ } else {
+ lp->tcr_cur_mode &= ~TCR_SWFDUP;
+ }
+
+ old_bank = SMC_CURRENT_BANK();
+ SMC_SELECT_BANK(0);
+ SMC_SET_TCR(lp->tcr_cur_mode);
+ SMC_SELECT_BANK(old_bank);
+ }
+}
+
+/*
+ * Configures the specified PHY through the MII management interface
+ * using Autonegotiation.
+ * Calls smc_phy_fixed() if the user has requested a certain config.
+ * If RPC ANEG bit is set, the media selection is dependent purely on
+ * the selection by the MII (either in the MII BMCR reg or the result
+ * of autonegotiation.) If the RPC ANEG bit is cleared, the selection
+ * is controlled by the RPC SPEED and RPC DPLX bits.
+ */
+static void smc_phy_configure(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ int phyaddr = lp->mii.phy_id;
+ int my_phy_caps; /* My PHY capabilities */
+ int my_ad_caps; /* My Advertised capabilities */
+ int status;
+
+ DBG(3, "%s:smc_program_phy()\n", dev->name);
+
+ spin_lock_irq(&lp->lock);
+
+ /*
+ * We should not be called if phy_type is zero.
+ */
+ if (lp->phy_type == 0)
+ goto smc_phy_configure_exit;
+
+ if (smc_phy_reset(dev, phyaddr)) {
+ printk("%s: PHY reset timed out\n", dev->name);
+ goto smc_phy_configure_exit;
+ }
+
+ /*
+ * Enable PHY Interrupts (for register 18)
+ * Interrupts listed here are disabled
+ */
+ smc_phy_write(dev, phyaddr, PHY_MASK_REG,
+ PHY_INT_LOSSSYNC | PHY_INT_CWRD | PHY_INT_SSD |
+ PHY_INT_ESD | PHY_INT_RPOL | PHY_INT_JAB |
+ PHY_INT_SPDDET | PHY_INT_DPLXDET);
+
+ /* Configure the Receive/Phy Control register */
+ SMC_SELECT_BANK(0);
+ SMC_SET_RPC(lp->rpc_cur_mode);
+
+ /* If the user requested no auto neg, then go set his request */
+ if (lp->mii.force_media) {
+ smc_phy_fixed(dev);
+ goto smc_phy_configure_exit;
+ }
+
+ /* Copy our capabilities from MII_BMSR to MII_ADVERTISE */
+ my_phy_caps = smc_phy_read(dev, phyaddr, MII_BMSR);
+
+ if (!(my_phy_caps & BMSR_ANEGCAPABLE)) {
+ printk(KERN_INFO "Auto negotiation NOT supported\n");
+ smc_phy_fixed(dev);
+ goto smc_phy_configure_exit;
+ }
+
+ my_ad_caps = ADVERTISE_CSMA; /* I am CSMA capable */
+
+ if (my_phy_caps & BMSR_100BASE4)
+ my_ad_caps |= ADVERTISE_100BASE4;
+ if (my_phy_caps & BMSR_100FULL)
+ my_ad_caps |= ADVERTISE_100FULL;
+ if (my_phy_caps & BMSR_100HALF)
+ my_ad_caps |= ADVERTISE_100HALF;
+ if (my_phy_caps & BMSR_10FULL)
+ my_ad_caps |= ADVERTISE_10FULL;
+ if (my_phy_caps & BMSR_10HALF)
+ my_ad_caps |= ADVERTISE_10HALF;
+
+ /* Disable capabilities not selected by our user */
+ if (lp->ctl_rspeed != 100)
+ my_ad_caps &= ~(ADVERTISE_100BASE4|ADVERTISE_100FULL|ADVERTISE_100HALF);
+
+ if (!lp->ctl_rfduplx)
+ my_ad_caps &= ~(ADVERTISE_100FULL|ADVERTISE_10FULL);
+
+ /* Update our Auto-Neg Advertisement Register */
+ smc_phy_write(dev, phyaddr, MII_ADVERTISE, my_ad_caps);
+ lp->mii.advertising = my_ad_caps;
+
+ /*
+ * Read the register back. Without this, it appears that when
+ * auto-negotiation is restarted, sometimes it isn't ready and
+ * the link does not come up.
+ */
+ status = smc_phy_read(dev, phyaddr, MII_ADVERTISE);
+
+ DBG(2, "%s: phy caps=%x\n", dev->name, my_phy_caps);
+ DBG(2, "%s: phy advertised caps=%x\n", dev->name, my_ad_caps);
+
+ /* Restart auto-negotiation process in order to advertise my caps */
+ smc_phy_write(dev, phyaddr, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
+
+ smc_phy_check_media(dev, 1);
+
+smc_phy_configure_exit:
+ spin_unlock_irq(&lp->lock);
+}
+
+/*
+ * smc_phy_interrupt
+ *
+ * Purpose: Handle interrupts relating to PHY register 18. This is
+ * called from the "hard" interrupt handler under our private spinlock.
+ */
+static void smc_phy_interrupt(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ int phyaddr = lp->mii.phy_id;
+ int phy18;
+
+ DBG(2, "%s: %s\n", dev->name, __FUNCTION__);
+
+ if (lp->phy_type == 0)
+ return;
+
+ for(;;) {
+ smc_phy_check_media(dev, 0);
+
+ /* Read PHY Register 18, Status Output */
+ phy18 = smc_phy_read(dev, phyaddr, PHY_INT_REG);
+ if ((phy18 & PHY_INT_INT) == 0)
+ break;
+ }
+}
+
+/*--- END PHY CONTROL AND CONFIGURATION-------------------------------------*/
+
+static void smc_10bt_check_media(struct net_device *dev, int init)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+ unsigned int old_carrier, new_carrier, old_bank;
+
+ old_bank = SMC_CURRENT_BANK();
+ SMC_SELECT_BANK(0);
+ old_carrier = netif_carrier_ok(dev) ? 1 : 0;
+ new_carrier = SMC_inw(ioaddr, EPH_STATUS_REG) & ES_LINK_OK ? 1 : 0;
+
+ if (init || (old_carrier != new_carrier)) {
+ if (!new_carrier) {
+ netif_carrier_off(dev);
+ } else {
+ netif_carrier_on(dev);
+ }
+ if (netif_msg_link(lp))
+ printk(KERN_INFO "%s: link %s\n", dev->name,
+ new_carrier ? "up" : "down");
+ }
+ SMC_SELECT_BANK(old_bank);
+}
+
+static void smc_eph_interrupt(struct net_device *dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ unsigned int old_bank, ctl;
+
+ smc_10bt_check_media(dev, 0);
+
+ old_bank = SMC_CURRENT_BANK();
+ SMC_SELECT_BANK(1);
+
+ ctl = SMC_GET_CTL();
+ SMC_SET_CTL(ctl & ~CTL_LE_ENABLE);
+ SMC_SET_CTL(ctl);
+
+ SMC_SELECT_BANK(old_bank);
+}
+
+/*
+ * This is the main routine of the driver, to handle the device when
+ * it needs some attention.
+ */
+static irqreturn_t smc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = dev_id;
+ unsigned long ioaddr = dev->base_addr;
+ struct smc_local *lp = netdev_priv(dev);
+ int status, mask, timeout, card_stats;
+ int saved_bank, saved_pointer;
+
+ DBG(3, "%s: %s\n", dev->name, __FUNCTION__);
+
+ saved_bank = SMC_CURRENT_BANK();
+ SMC_SELECT_BANK(2);
+ saved_pointer = SMC_GET_PTR();
+ mask = SMC_GET_INT_MASK();
+ SMC_SET_INT_MASK(0);
+
+ /* set a timeout value, so I don't stay here forever */
+ timeout = 8;
+
+ do {
+ status = SMC_GET_INT();
+
+ DBG(2, "%s: IRQ 0x%02x MASK 0x%02x MEM 0x%04x FIFO 0x%04x\n",
+ dev->name, status, mask,
+ ({ int meminfo; SMC_SELECT_BANK(0);
+ meminfo = SMC_GET_MIR();
+ SMC_SELECT_BANK(2); meminfo; }),
+ SMC_GET_FIFO());
+
+ status &= mask;
+ if (!status)
+ break;
+
+ spin_lock(&lp->lock);
+
+ if (status & IM_RCV_INT) {
+ DBG(3, "%s: RX irq\n", dev->name);
+ smc_rcv(dev);
+ } else if (status & IM_TX_INT) {
+ DBG(3, "%s: TX int\n", dev->name);
+ smc_tx(dev);
+ SMC_ACK_INT(IM_TX_INT);
+#if THROTTLE_TX_PKTS
+ netif_wake_queue(dev);
+#endif
+ } else if (status & IM_ALLOC_INT) {
+ DBG(3, "%s: Allocation irq\n", dev->name);
+ smc_hardware_send_packet(dev);
+ mask |= (IM_TX_INT | IM_TX_EMPTY_INT);
+ mask &= ~IM_ALLOC_INT;
+#if ! THROTTLE_TX_PKTS
+ netif_wake_queue(dev);
+#endif
+ } else if (status & IM_TX_EMPTY_INT) {
+ DBG(3, "%s: TX empty\n", dev->name);
+ mask &= ~IM_TX_EMPTY_INT;
+
+ /* update stats */
+ SMC_SELECT_BANK(0);
+ card_stats = SMC_GET_COUNTER();
+ SMC_SELECT_BANK(2);
+
+ /* single collisions */
+ lp->stats.collisions += card_stats & 0xF;
+ card_stats >>= 4;
+
+ /* multiple collisions */
+ lp->stats.collisions += card_stats & 0xF;
+ } else if (status & IM_RX_OVRN_INT) {
+ DBG(1, "%s: RX overrun\n", dev->name);
+ SMC_ACK_INT(IM_RX_OVRN_INT);
+ lp->stats.rx_errors++;
+ lp->stats.rx_fifo_errors++;
+ } else if (status & IM_EPH_INT) {
+ smc_eph_interrupt(dev);
+ } else if (status & IM_MDINT) {
+ SMC_ACK_INT(IM_MDINT);
+ smc_phy_interrupt(dev);
+ } else if (status & IM_ERCV_INT) {
+ SMC_ACK_INT(IM_ERCV_INT);
+ PRINTK("%s: UNSUPPORTED: ERCV INTERRUPT \n", dev->name);
+ }
+
+ spin_unlock(&lp->lock);
+ } while (--timeout);
+
+ /* restore register states */
+ SMC_SET_INT_MASK(mask);
+ SMC_SET_PTR(saved_pointer);
+ SMC_SELECT_BANK(saved_bank);
+
+ DBG(3, "%s: Interrupt done (%d loops)\n", dev->name, 8-timeout);
+
+ /*
+ * We return IRQ_HANDLED unconditionally here even if there was
+ * nothing to do. There is a possibility that a packet might
+ * get enqueued into the chip right after TX_EMPTY_INT is raised
+ * but just before the CPU acknowledges the IRQ.
+ * Better take an unneeded IRQ in some occasions than complexifying
+ * the code for all cases.
+ */
+ return IRQ_HANDLED;
+}
+
+/* Our watchdog timed out. Called by the networking layer */
+static void smc_timeout(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+
+ DBG(2, "%s: %s\n", dev->name, __FUNCTION__);
+
+ smc_reset(dev);
+ smc_enable(dev);
+
+#if 0
+ /*
+ * Reconfiguring the PHY doesn't seem like a bad idea here, but
+ * it introduced a problem. Now that this is a timeout routine,
+ * we are getting called from within an interrupt context.
+ * smc_phy_configure() calls msleep() which calls
+ * schedule_timeout() which calls schedule(). When schedule()
+ * is called from an interrupt context, it prints out
+ * "Scheduling in interrupt" and then calls BUG(). This is
+ * obviously not desirable. This was worked around by removing
+ * the call to smc_phy_configure() here because it didn't seem
+ * absolutely necessary. Ultimately, if msleep() is
+ * supposed to be usable from an interrupt context (which it
+ * looks like it thinks it should handle), it should be fixed.
+ */
+ if (lp->phy_type != 0)
+ smc_phy_configure(dev);
+#endif
+
+ /* clear anything saved */
+ if (lp->saved_skb != NULL) {
+ dev_kfree_skb (lp->saved_skb);
+ lp->saved_skb = NULL;
+ lp->stats.tx_errors++;
+ lp->stats.tx_aborted_errors++;
+ }
+ /* We can accept TX packets again */
+ dev->trans_start = jiffies;
+ netif_wake_queue(dev);
+}
+
+/*
+ * This sets the internal hardware table to filter out unwanted multicast
+ * packets before they take up memory.
+ *
+ * The SMC chip uses a hash table where the high 6 bits of the CRC of
+ * address are the offset into the table. If that bit is 1, then the
+ * multicast packet is accepted. Otherwise, it's dropped silently.
+ *
+ * To use the 6 bits as an offset into the table, the high 3 bits are the
+ * number of the 8 bit register, while the low 3 bits are the bit within
+ * that register.
+ *
+ * This routine is based very heavily on the one provided by Peter Cammaert.
+ */
+static void
+smc_setmulticast(unsigned long ioaddr, int count, struct dev_mc_list *addrs)
+{
+ int i;
+ unsigned char multicast_table[8];
+ struct dev_mc_list *cur_addr;
+
+ /* table for flipping the order of 3 bits */
+ static unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+
+ /* start with a table of all zeros: reject all */
+ memset(multicast_table, 0, sizeof(multicast_table));
+
+ cur_addr = addrs;
+ for (i = 0; i < count; i++, cur_addr = cur_addr->next) {
+ int position;
+
+ /* do we have a pointer here? */
+ if (!cur_addr)
+ break;
+ /* make sure this is a multicast address - shouldn't this
+ be a given if we have it here ? */
+ if (!(*cur_addr->dmi_addr & 1))
+ continue;
+
+ /* only use the low order bits */
+ position = crc32_le(~0, cur_addr->dmi_addr, 6) & 0x3f;
+
+ /* do some messy swapping to put the bit in the right spot */
+ multicast_table[invert3[position&7]] |=
+ (1<<invert3[(position>>3)&7]);
+
+ }
+ /* now, the table can be loaded into the chipset */
+ SMC_SELECT_BANK(3);
+ SMC_SET_MCAST(multicast_table);
+}
+
+/*
+ * This routine will, depending on the values passed to it,
+ * either make it accept multicast packets, go into
+ * promiscuous mode (for TCPDUMP and cousins) or accept
+ * a select set of multicast packets
+ */
+static void smc_set_multicast_list(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+
+ DBG(2, "%s: %s\n", dev->name, __FUNCTION__);
+
+ SMC_SELECT_BANK(0);
+ if (dev->flags & IFF_PROMISC) {
+ DBG(2, "%s: RCR_PRMS\n", dev->name);
+ lp->rcr_cur_mode |= RCR_PRMS;
+ SMC_SET_RCR(lp->rcr_cur_mode);
+ }
+
+/* BUG? I never disable promiscuous mode if multicasting was turned on.
+ Now, I turn off promiscuous mode, but I don't do anything to multicasting
+ when promiscuous mode is turned on.
+*/
+
+ /*
+ * Here, I am setting this to accept all multicast packets.
+ * I don't need to zero the multicast table, because the flag is
+ * checked before the table is
+ */
+ else if (dev->flags & IFF_ALLMULTI || dev->mc_count > 16) {
+ lp->rcr_cur_mode |= RCR_ALMUL;
+ SMC_SET_RCR(lp->rcr_cur_mode);
+ DBG(2, "%s: RCR_ALMUL\n", dev->name);
+ }
+
+ /*
+ * We just get all multicast packets even if we only want them
+ * from one source. This will be changed at some future point.
+ */
+ else if (dev->mc_count) {
+ /* support hardware multicasting */
+
+ /* be sure I get rid of flags I might have set */
+ lp->rcr_cur_mode &= ~(RCR_PRMS | RCR_ALMUL);
+ SMC_SET_RCR(lp->rcr_cur_mode);
+ /*
+ * NOTE: this has to set the bank, so make sure it is the
+ * last thing called. The bank is set to zero at the top
+ */
+ smc_setmulticast(ioaddr, dev->mc_count, dev->mc_list);
+ } else {
+ DBG(2, "%s: ~(RCR_PRMS|RCR_ALMUL)\n", dev->name);
+ lp->rcr_cur_mode &= ~(RCR_PRMS | RCR_ALMUL);
+ SMC_SET_RCR(lp->rcr_cur_mode);
+
+ /*
+ * since I'm disabling all multicast entirely, I need to
+ * clear the multicast list
+ */
+ SMC_SELECT_BANK(3);
+ SMC_CLEAR_MCAST();
+ }
+}
+
+
+/*
+ * Open and Initialize the board
+ *
+ * Set up everything, reset the card, etc..
+ */
+static int
+smc_open(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ unsigned long ioaddr = dev->base_addr;
+
+ DBG(2, "%s: %s\n", dev->name, __FUNCTION__);
+
+ /*
+ * Check that the address is valid. If its not, refuse
+ * to bring the device up. The user must specify an
+ * address using ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx
+ */
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ DBG(2, (KERN_DEBUG "smc_open: no valid ethernet hw addr\n"));
+ return -EINVAL;
+ }
+
+ /* clear out all the junk that was put here before... */
+ lp->saved_skb = NULL;
+
+ /* Setup the default Register Modes */
+ lp->tcr_cur_mode = TCR_DEFAULT;
+ lp->rcr_cur_mode = RCR_DEFAULT;
+ lp->rpc_cur_mode = RPC_DEFAULT;
+
+ /*
+ * If we are not using a MII interface, we need to
+ * monitor our own carrier signal to detect faults.
+ */
+ if (lp->phy_type == 0)
+ lp->tcr_cur_mode |= TCR_MON_CSN;
+
+ /* reset the hardware */
+ smc_reset(dev);
+ smc_enable(dev);
+
+ SMC_SELECT_BANK(1);
+ SMC_SET_MAC_ADDR(dev->dev_addr);
+
+ /* Configure the PHY */
+ if (lp->phy_type != 0)
+ smc_phy_configure(dev);
+ else {
+ spin_lock_irq(&lp->lock);
+ smc_10bt_check_media(dev, 1);
+ spin_unlock_irq(&lp->lock);
+ }
+
+ /*
+ * make sure to initialize the link state with netif_carrier_off()
+ * somewhere, too --jgarzik
+ *
+ * smc_phy_configure() and smc_10bt_check_media() does that. --rmk
+ */
+ netif_start_queue(dev);
+ return 0;
+}
+
+/*
+ * smc_close
+ *
+ * this makes the board clean up everything that it can
+ * and not talk to the outside world. Caused by
+ * an 'ifconfig ethX down'
+ */
+static int smc_close(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+
+ DBG(2, "%s: %s\n", dev->name, __FUNCTION__);
+
+ netif_stop_queue(dev);
+ netif_carrier_off(dev);
+
+ /* clear everything */
+ smc_shutdown(dev->base_addr);
+
+ if (lp->phy_type != 0)
+ smc_phy_powerdown(dev, lp->mii.phy_id);
+
+ return 0;
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct net_device_stats *smc_query_statistics(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+
+ DBG(2, "%s: %s\n", dev->name, __FUNCTION__);
+
+ return &lp->stats;
+}
+
+/*
+ * Ethtool support
+ */
+static int
+smc_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ int ret;
+
+ cmd->maxtxpkt = 1;
+ cmd->maxrxpkt = 1;
+
+ if (lp->phy_type != 0) {
+ spin_lock_irq(&lp->lock);
+ ret = mii_ethtool_gset(&lp->mii, cmd);
+ spin_unlock_irq(&lp->lock);
+ } else {
+ cmd->supported = SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_TP | SUPPORTED_AUI;
+
+ if (lp->ctl_rspeed == 10)
+ cmd->speed = SPEED_10;
+ else if (lp->ctl_rspeed == 100)
+ cmd->speed = SPEED_100;
+
+ cmd->autoneg = AUTONEG_DISABLE;
+ cmd->transceiver = XCVR_INTERNAL;
+ cmd->port = 0;
+ cmd->duplex = lp->tcr_cur_mode & TCR_SWFDUP ? DUPLEX_FULL : DUPLEX_HALF;
+
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int
+smc_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ int ret;
+
+ if (lp->phy_type != 0) {
+ spin_lock_irq(&lp->lock);
+ ret = mii_ethtool_sset(&lp->mii, cmd);
+ spin_unlock_irq(&lp->lock);
+ } else {
+ if (cmd->autoneg != AUTONEG_DISABLE ||
+ cmd->speed != SPEED_10 ||
+ (cmd->duplex != DUPLEX_HALF && cmd->duplex != DUPLEX_FULL) ||
+ (cmd->port != PORT_TP && cmd->port != PORT_AUI))
+ return -EINVAL;
+
+// lp->port = cmd->port;
+ lp->ctl_rfduplx = cmd->duplex == DUPLEX_FULL;
+
+// if (netif_running(dev))
+// smc_set_port(dev);
+
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void
+smc_ethtool_getdrvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ strncpy(info->driver, CARDNAME, sizeof(info->driver));
+ strncpy(info->version, version, sizeof(info->version));
+ strncpy(info->bus_info, dev->class_dev.dev->bus_id, sizeof(info->bus_info));
+}
+
+static int smc_ethtool_nwayreset(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ int ret = -EINVAL;
+
+ if (lp->phy_type != 0) {
+ spin_lock_irq(&lp->lock);
+ ret = mii_nway_restart(&lp->mii);
+ spin_unlock_irq(&lp->lock);
+ }
+
+ return ret;
+}
+
+static u32 smc_ethtool_getmsglevel(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ return lp->msg_enable;
+}
+
+static void smc_ethtool_setmsglevel(struct net_device *dev, u32 level)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ lp->msg_enable = level;
+}
+
+static struct ethtool_ops smc_ethtool_ops = {
+ .get_settings = smc_ethtool_getsettings,
+ .set_settings = smc_ethtool_setsettings,
+ .get_drvinfo = smc_ethtool_getdrvinfo,
+
+ .get_msglevel = smc_ethtool_getmsglevel,
+ .set_msglevel = smc_ethtool_setmsglevel,
+ .nway_reset = smc_ethtool_nwayreset,
+ .get_link = ethtool_op_get_link,
+// .get_eeprom = smc_ethtool_geteeprom,
+// .set_eeprom = smc_ethtool_seteeprom,
+};
+
+/*
+ * smc_findirq
+ *
+ * This routine has a simple purpose -- make the SMC chip generate an
+ * interrupt, so an auto-detect routine can detect it, and find the IRQ,
+ */
+/*
+ * does this still work?
+ *
+ * I just deleted auto_irq.c, since it was never built...
+ * --jgarzik
+ */
+static int __init smc_findirq(unsigned long ioaddr)
+{
+ int timeout = 20;
+ unsigned long cookie;
+
+ DBG(2, "%s: %s\n", CARDNAME, __FUNCTION__);
+
+ cookie = probe_irq_on();
+
+ /*
+ * What I try to do here is trigger an ALLOC_INT. This is done
+ * by allocating a small chunk of memory, which will give an interrupt
+ * when done.
+ */
+ /* enable ALLOCation interrupts ONLY */
+ SMC_SELECT_BANK(2);
+ SMC_SET_INT_MASK(IM_ALLOC_INT);
+
+ /*
+ * Allocate 512 bytes of memory. Note that the chip was just
+ * reset so all the memory is available
+ */
+ SMC_SET_MMU_CMD(MC_ALLOC | 1);
+
+ /*
+ * Wait until positive that the interrupt has been generated
+ */
+ do {
+ int int_status;
+ udelay(10);
+ int_status = SMC_GET_INT();
+ if (int_status & IM_ALLOC_INT)
+ break; /* got the interrupt */
+ } while (--timeout);
+
+ /*
+ * there is really nothing that I can do here if timeout fails,
+ * as autoirq_report will return a 0 anyway, which is what I
+ * want in this case. Plus, the clean up is needed in both
+ * cases.
+ */
+
+ /* and disable all interrupts again */
+ SMC_SET_INT_MASK(0);
+
+ /* and return what I found */
+ return probe_irq_off(cookie);
+}
+
+/*
+ * Function: smc_probe(unsigned long ioaddr)
+ *
+ * Purpose:
+ * Tests to see if a given ioaddr points to an SMC91x chip.
+ * Returns a 0 on success
+ *
+ * Algorithm:
+ * (1) see if the high byte of BANK_SELECT is 0x33
+ * (2) compare the ioaddr with the base register's address
+ * (3) see if I recognize the chip ID in the appropriate register
+ *
+ * Here I do typical initialization tasks.
+ *
+ * o Initialize the structure if needed
+ * o print out my vanity message if not done so already
+ * o print out what type of hardware is detected
+ * o print out the ethernet address
+ * o find the IRQ
+ * o set up my private data
+ * o configure the dev structure with my subroutines
+ * o actually GRAB the irq.
+ * o GRAB the region
+ */
+static int __init smc_probe(struct net_device *dev, unsigned long ioaddr)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ static int version_printed = 0;
+ int i, retval;
+ unsigned int val, revision_register;
+ const char *version_string;
+
+ DBG(2, "%s: %s\n", CARDNAME, __FUNCTION__);
+
+ /* First, see if the high byte is 0x33 */
+ val = SMC_CURRENT_BANK();
+ DBG(2, "%s: bank signature probe returned 0x%04x\n", CARDNAME, val);
+ if ((val & 0xFF00) != 0x3300) {
+ if ((val & 0xFF) == 0x33) {
+ printk(KERN_WARNING
+ "%s: Detected possible byte-swapped interface"
+ " at IOADDR 0x%lx\n", CARDNAME, ioaddr);
+ }
+ retval = -ENODEV;
+ goto err_out;
+ }
+
+ /*
+ * The above MIGHT indicate a device, but I need to write to
+ * further test this.
+ */
+ SMC_SELECT_BANK(0);
+ val = SMC_CURRENT_BANK();
+ if ((val & 0xFF00) != 0x3300) {
+ retval = -ENODEV;
+ goto err_out;
+ }
+
+ /*
+ * well, we've already written once, so hopefully another
+ * time won't hurt. This time, I need to switch the bank
+ * register to bank 1, so I can access the base address
+ * register
+ */
+ SMC_SELECT_BANK(1);
+ val = SMC_GET_BASE();
+ val = ((val & 0x1F00) >> 3) << SMC_IO_SHIFT;
+ if ((ioaddr & ((PAGE_SIZE-1)<<SMC_IO_SHIFT)) != val) {
+ printk("%s: IOADDR %lx doesn't match configuration (%x).\n",
+ CARDNAME, ioaddr, val);
+ }
+
+ /*
+ * check if the revision register is something that I
+ * recognize. These might need to be added to later,
+ * as future revisions could be added.
+ */
+ SMC_SELECT_BANK(3);
+ revision_register = SMC_GET_REV();
+ DBG(2, "%s: revision = 0x%04x\n", CARDNAME, revision_register);
+ version_string = chip_ids[ (revision_register >> 4) & 0xF];
+ if (!version_string || (revision_register & 0xff00) != 0x3300) {
+ /* I don't recognize this chip, so... */
+ printk("%s: IO 0x%lx: Unrecognized revision register 0x%04x"
+ ", Contact author.\n", CARDNAME,
+ ioaddr, revision_register);
+
+ retval = -ENODEV;
+ goto err_out;
+ }
+
+ /* At this point I'll assume that the chip is an SMC91x. */
+ if (version_printed++ == 0)
+ printk("%s", version);
+
+ /* fill in some of the fields */
+ dev->base_addr = ioaddr;
+ lp->version = revision_register & 0xff;
+
+ /* Get the MAC address */
+ SMC_SELECT_BANK(1);
+ SMC_GET_MAC_ADDR(dev->dev_addr);
+
+ /* now, reset the chip, and put it into a known state */
+ smc_reset(dev);
+
+ /*
+ * If dev->irq is 0, then the device has to be banged on to see
+ * what the IRQ is.
+ *
+ * This banging doesn't always detect the IRQ, for unknown reasons.
+ * a workaround is to reset the chip and try again.
+ *
+ * Interestingly, the DOS packet driver *SETS* the IRQ on the card to
+ * be what is requested on the command line. I don't do that, mostly
+ * because the card that I have uses a non-standard method of accessing
+ * the IRQs, and because this _should_ work in most configurations.
+ *
+ * Specifying an IRQ is done with the assumption that the user knows
+ * what (s)he is doing. No checking is done!!!!
+ */
+ if (dev->irq < 1) {
+ int trials;
+
+ trials = 3;
+ while (trials--) {
+ dev->irq = smc_findirq(ioaddr);
+ if (dev->irq)
+ break;
+ /* kick the card and try again */
+ smc_reset(dev);
+ }
+ }
+ if (dev->irq == 0) {
+ printk("%s: Couldn't autodetect your IRQ. Use irq=xx.\n",
+ dev->name);
+ retval = -ENODEV;
+ goto err_out;
+ }
+ dev->irq = irq_canonicalize(dev->irq);
+
+ /* Fill in the fields of the device structure with ethernet values. */
+ ether_setup(dev);
+
+ dev->open = smc_open;
+ dev->stop = smc_close;
+ dev->hard_start_xmit = smc_hard_start_xmit;
+ dev->tx_timeout = smc_timeout;
+ dev->watchdog_timeo = msecs_to_jiffies(watchdog);
+ dev->get_stats = smc_query_statistics;
+ dev->set_multicast_list = smc_set_multicast_list;
+ dev->ethtool_ops = &smc_ethtool_ops;
+
+ spin_lock_init(&lp->lock);
+ lp->mii.phy_id_mask = 0x1f;
+ lp->mii.reg_num_mask = 0x1f;
+ lp->mii.force_media = 0;
+ lp->mii.full_duplex = 0;
+ lp->mii.dev = dev;
+ lp->mii.mdio_read = smc_phy_read;
+ lp->mii.mdio_write = smc_phy_write;
+
+ /*
+ * Locate the phy, if any.
+ */
+ if (lp->version >= (CHIP_91100 << 4))
+ smc_detect_phy(dev);
+
+ /* Set default parameters */
+ lp->msg_enable = NETIF_MSG_LINK;
+ lp->ctl_rfduplx = 0;
+ lp->ctl_rspeed = 10;
+
+ if (lp->version >= (CHIP_91100 << 4)) {
+ lp->ctl_rfduplx = 1;
+ lp->ctl_rspeed = 100;
+ }
+
+ /* Grab the IRQ */
+ retval = request_irq(dev->irq, &smc_interrupt, 0, dev->name, dev);
+ if (retval)
+ goto err_out;
+
+ set_irq_type(dev->irq, IRQT_RISING);
+#ifdef SMC_USE_PXA_DMA
+ {
+ int dma = pxa_request_dma(dev->name, DMA_PRIO_LOW,
+ smc_pxa_dma_irq, NULL);
+ if (dma >= 0)
+ dev->dma = dma;
+ }
+#endif
+
+ retval = register_netdev(dev);
+ if (retval == 0) {
+ /* now, print out the card info, in a short format.. */
+ printk("%s: %s (rev %d) at %#lx IRQ %d",
+ dev->name, version_string, revision_register & 0x0f,
+ dev->base_addr, dev->irq);
+
+ if (dev->dma != (unsigned char)-1)
+ printk(" DMA %d", dev->dma);
+
+ printk("%s%s\n", nowait ? " [nowait]" : "",
+ THROTTLE_TX_PKTS ? " [throttle_tx]" : "");
+
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ printk("%s: Invalid ethernet MAC address. Please "
+ "set using ifconfig\n", dev->name);
+ } else {
+ /* Print the Ethernet address */
+ printk("%s: Ethernet addr: ", dev->name);
+ for (i = 0; i < 5; i++)
+ printk("%2.2x:", dev->dev_addr[i]);
+ printk("%2.2x\n", dev->dev_addr[5]);
+ }
+
+ if (lp->phy_type == 0) {
+ PRINTK("%s: No PHY found\n", dev->name);
+ } else if ((lp->phy_type & 0xfffffff0) == 0x0016f840) {
+ PRINTK("%s: PHY LAN83C183 (LAN91C111 Internal)\n", dev->name);
+ } else if ((lp->phy_type & 0xfffffff0) == 0x02821c50) {
+ PRINTK("%s: PHY LAN83C180\n", dev->name);
+ }
+ }
+
+err_out:
+#ifdef SMC_USE_PXA_DMA
+ if (retval && dev->dma != (unsigned char)-1)
+ pxa_free_dma(dev->dma);
+#endif
+ return retval;
+}
+
+static int smc_enable_device(unsigned long attrib_phys)
+{
+ unsigned long flags;
+ unsigned char ecor, ecsr;
+ void *addr;
+
+ /*
+ * Map the attribute space. This is overkill, but clean.
+ */
+ addr = ioremap(attrib_phys, ATTRIB_SIZE);
+ if (!addr)
+ return -ENOMEM;
+
+ /*
+ * Reset the device. We must disable IRQs around this
+ * since a reset causes the IRQ line become active.
+ */
+ local_irq_save(flags);
+ ecor = readb(addr + (ECOR << SMC_IO_SHIFT)) & ~ECOR_RESET;
+ writeb(ecor | ECOR_RESET, addr + (ECOR << SMC_IO_SHIFT));
+ readb(addr + (ECOR << SMC_IO_SHIFT));
+
+ /*
+ * Wait 100us for the chip to reset.
+ */
+ udelay(100);
+
+ /*
+ * The device will ignore all writes to the enable bit while
+ * reset is asserted, even if the reset bit is cleared in the
+ * same write. Must clear reset first, then enable the device.
+ */
+ writeb(ecor, addr + (ECOR << SMC_IO_SHIFT));
+ writeb(ecor | ECOR_ENABLE, addr + (ECOR << SMC_IO_SHIFT));
+
+ /*
+ * Set the appropriate byte/word mode.
+ */
+ ecsr = readb(addr + (ECSR << SMC_IO_SHIFT)) & ~ECSR_IOIS8;
+#ifndef SMC_CAN_USE_16BIT
+ ecsr |= ECSR_IOIS8;
+#endif
+ writeb(ecsr, addr + (ECSR << SMC_IO_SHIFT));
+ local_irq_restore(flags);
+
+ iounmap(addr);
+
+ /*
+ * Wait for the chip to wake up. We could poll the control
+ * register in the main register space, but that isn't mapped
+ * yet. We know this is going to take 750us.
+ */
+ msleep(1);
+
+ return 0;
+}
+
+/*
+ * smc_init(void)
+ * Input parameters:
+ * dev->base_addr == 0, try to find all possible locations
+ * dev->base_addr > 0x1ff, this is the address to check
+ * dev->base_addr == <anything else>, return failure code
+ *
+ * Output:
+ * 0 --> there is a device
+ * anything else, error
+ */
+static int smc_drv_probe(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct net_device *ndev;
+ struct resource *res, *ext = NULL;
+ unsigned int *addr;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ /*
+ * Request the regions.
+ */
+ if (!request_mem_region(res->start, SMC_IO_EXTENT, "smc91x")) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ndev = alloc_etherdev(sizeof(struct smc_local));
+ if (!ndev) {
+ printk("%s: could not allocate device.\n", CARDNAME);
+ ret = -ENOMEM;
+ goto release_1;
+ }
+ SET_MODULE_OWNER(ndev);
+ SET_NETDEV_DEV(ndev, dev);
+
+ ndev->dma = (unsigned char)-1;
+ ndev->irq = platform_get_irq(pdev, 0);
+
+ ext = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (ext) {
+ if (!request_mem_region(ext->start, ATTRIB_SIZE, ndev->name)) {
+ ret = -EBUSY;
+ goto release_1;
+ }
+
+#if defined(CONFIG_SA1100_ASSABET)
+ NCR_0 |= NCR_ENET_OSC_EN;
+#endif
+
+ ret = smc_enable_device(ext->start);
+ if (ret)
+ goto release_both;
+ }
+
+ addr = ioremap(res->start, SMC_IO_EXTENT);
+ if (!addr) {
+ ret = -ENOMEM;
+ goto release_both;
+ }
+
+ dev_set_drvdata(dev, ndev);
+ ret = smc_probe(ndev, (unsigned long)addr);
+ if (ret != 0) {
+ dev_set_drvdata(dev, NULL);
+ iounmap(addr);
+ release_both:
+ if (ext)
+ release_mem_region(ext->start, ATTRIB_SIZE);
+ free_netdev(ndev);
+ release_1:
+ release_mem_region(res->start, SMC_IO_EXTENT);
+ out:
+ printk("%s: not found (%d).\n", CARDNAME, ret);
+ }
+#ifdef SMC_USE_PXA_DMA
+ else {
+ struct smc_local *lp = netdev_priv(ndev);
+ lp->physaddr = res->start;
+ }
+#endif
+
+ return ret;
+}
+
+static int smc_drv_remove(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct resource *res;
+
+ dev_set_drvdata(dev, NULL);
+
+ unregister_netdev(ndev);
+
+ free_irq(ndev->irq, ndev);
+
+#ifdef SMC_USE_PXA_DMA
+ if (ndev->dma != (unsigned char)-1)
+ pxa_free_dma(ndev->dma);
+#endif
+ iounmap((void *)ndev->base_addr);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res)
+ release_mem_region(res->start, ATTRIB_SIZE);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, SMC_IO_EXTENT);
+
+ free_netdev(ndev);
+
+ return 0;
+}
+
+static int smc_drv_suspend(struct device *dev, u32 state, u32 level)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+
+ if (ndev && level == SUSPEND_DISABLE) {
+ if (netif_running(ndev)) {
+ netif_device_detach(ndev);
+ smc_shutdown(ndev->base_addr);
+ }
+ }
+ return 0;
+}
+
+static int smc_drv_resume(struct device *dev, u32 level)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct net_device *ndev = dev_get_drvdata(dev);
+
+ if (ndev && level == RESUME_ENABLE) {
+ struct smc_local *lp = netdev_priv(ndev);
+ unsigned long ioaddr = ndev->base_addr;
+
+ if (pdev->num_resources == 3)
+ smc_enable_device(pdev->resource[2].start);
+ if (netif_running(ndev)) {
+ smc_reset(ndev);
+ smc_enable(ndev);
+ SMC_SELECT_BANK(1);
+ SMC_SET_MAC_ADDR(ndev->dev_addr);
+ if (lp->phy_type != 0)
+ smc_phy_configure(ndev);
+ netif_device_attach(ndev);
+ }
+ }
+ return 0;
+}
+
+static struct device_driver smc_driver = {
+ .name = CARDNAME,
+ .bus = &platform_bus_type,
+ .probe = smc_drv_probe,
+ .remove = smc_drv_remove,
+ .suspend = smc_drv_suspend,
+ .resume = smc_drv_resume,
+};
+
+static int __init smc_init(void)
+{
+#ifdef MODULE
+ if (io == -1)
+ printk(KERN_WARNING
+ "%s: You shouldn't use auto-probing with insmod!\n",
+ CARDNAME);
+#endif
+
+ return driver_register(&smc_driver);
+}
+
+static void __exit smc_cleanup(void)
+{
+ driver_unregister(&smc_driver);
+}
+
+module_init(smc_init);
+module_exit(smc_cleanup);
--- /dev/null
+/*------------------------------------------------------------------------
+ . smc91x.h - macros for SMSC's 91C9x/91C1xx single-chip Ethernet device.
+ .
+ . Copyright (C) 1996 by Erik Stahlman
+ . Copyright (C) 2001 Standard Microsystems Corporation
+ . Developed by Simple Network Magic Corporation
+ . Copyright (C) 2003 Monta Vista Software, Inc.
+ . Unified SMC91x driver by Nicolas Pitre
+ .
+ . 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
+ .
+ . Information contained in this file was obtained from the LAN91C111
+ . manual from SMC. To get a copy, if you really want one, you can find
+ . information under www.smsc.com.
+ .
+ . Authors
+ . Erik Stahlman <erik@vt.edu>
+ . Daris A Nevil <dnevil@snmc.com>
+ . Nicolas Pitre <nico@cam.org>
+ .
+ ---------------------------------------------------------------------------*/
+#ifndef _SMC91X_H_
+#define _SMC91X_H_
+
+
+/*
+ * Define your architecture specific bus configuration parameters here.
+ */
+
+#if defined(CONFIG_SA1100_GRAPHICSCLIENT) || \
+ defined(CONFIG_SA1100_PFS168) || \
+ defined(CONFIG_SA1100_FLEXANET) || \
+ defined(CONFIG_SA1100_GRAPHICSMASTER) || \
+ defined(CONFIG_ARCH_LUBBOCK)
+
+/* We can only do 16-bit reads and writes in the static memory space. */
+#define SMC_CAN_USE_8BIT 0
+#define SMC_CAN_USE_16BIT 1
+#define SMC_CAN_USE_32BIT 0
+#define SMC_NOWAIT 1
+
+/* The first two address lines aren't connected... */
+#define SMC_IO_SHIFT 2
+
+#define SMC_inw(a, r) readw((a) + (r))
+#define SMC_outw(v, a, r) writew(v, (a) + (r))
+#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l)
+#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l)
+
+#elif defined(CONFIG_REDWOOD_5) || defined(CONFIG_REDWOOD_6)
+
+/* We can only do 16-bit reads and writes in the static memory space. */
+#define SMC_CAN_USE_8BIT 0
+#define SMC_CAN_USE_16BIT 1
+#define SMC_CAN_USE_32BIT 0
+#define SMC_NOWAIT 1
+
+#define SMC_IO_SHIFT 0
+
+#define SMC_inw(a, r) in_be16((volatile u16 *)((a) + (r)))
+#define SMC_outw(v, a, r) out_be16((volatile u16 *)((a) + (r)), v)
+#define SMC_insw(a, r, p, l) \
+ do { \
+ unsigned long __port = (a) + (r); \
+ u16 *__p = (u16 *)(p); \
+ int __l = (l); \
+ insw(__port, __p, __l); \
+ while (__l > 0) { \
+ *__p = swab16(*__p); \
+ __p++; \
+ __l--; \
+ } \
+ } while (0)
+#define SMC_outsw(a, r, p, l) \
+ do { \
+ unsigned long __port = (a) + (r); \
+ u16 *__p = (u16 *)(p); \
+ int __l = (l); \
+ while (__l > 0) { \
+ /* Believe it or not, the swab isn't needed. */ \
+ outw( /* swab16 */ (*__p++), __port); \
+ __l--; \
+ } \
+ } while (0)
+#define set_irq_type(irq, type)
+
+#elif defined(CONFIG_SA1100_ASSABET)
+
+#include <asm/arch/neponset.h>
+
+/* We can only do 8-bit reads and writes in the static memory space. */
+#define SMC_CAN_USE_8BIT 1
+#define SMC_CAN_USE_16BIT 0
+#define SMC_CAN_USE_32BIT 0
+#define SMC_NOWAIT 1
+
+/* The first two address lines aren't connected... */
+#define SMC_IO_SHIFT 2
+
+#define SMC_inb(a, r) readb((a) + (r))
+#define SMC_outb(v, a, r) writeb(v, (a) + (r))
+#define SMC_insb(a, r, p, l) readsb((a) + (r), p, (l))
+#define SMC_outsb(a, r, p, l) writesb((a) + (r), p, (l))
+
+#elif defined(CONFIG_ARCH_INNOKOM) || \
+ defined(CONFIG_MACH_MAINSTONE) || \
+ defined(CONFIG_ARCH_PXA_IDP) || \
+ defined(CONFIG_ARCH_RAMSES)
+
+#define SMC_CAN_USE_8BIT 1
+#define SMC_CAN_USE_16BIT 1
+#define SMC_CAN_USE_32BIT 1
+#define SMC_IO_SHIFT 0
+#define SMC_NOWAIT 1
+#define SMC_USE_PXA_DMA 1
+
+#define SMC_inb(a, r) readb((a) + (r))
+#define SMC_inw(a, r) readw((a) + (r))
+#define SMC_inl(a, r) readl((a) + (r))
+#define SMC_outb(v, a, r) writeb(v, (a) + (r))
+#define SMC_outl(v, a, r) writel(v, (a) + (r))
+#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l)
+#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l)
+
+/* We actually can't write halfwords properly if not word aligned */
+static inline void
+SMC_outw(u16 val, unsigned long ioaddr, int reg)
+{
+ if (reg & 2) {
+ unsigned int v = val << 16;
+ v |= readl(ioaddr + (reg & ~2)) & 0xffff;
+ writel(v, ioaddr + (reg & ~2));
+ } else {
+ writew(val, ioaddr + reg);
+ }
+}
+
+#elif defined(CONFIG_ISA)
+
+#define SMC_CAN_USE_8BIT 1
+#define SMC_CAN_USE_16BIT 1
+#define SMC_CAN_USE_32BIT 0
+
+#define SMC_inb(a, r) inb((a) + (r))
+#define SMC_inw(a, r) inw((a) + (r))
+#define SMC_outb(v, a, r) outb(v, (a) + (r))
+#define SMC_outw(v, a, r) outw(v, (a) + (r))
+#define SMC_insw(a, r, p, l) insw((a) + (r), p, l)
+#define SMC_outsw(a, r, p, l) outsw((a) + (r), p, l)
+
+#else
+
+#define SMC_CAN_USE_8BIT 1
+#define SMC_CAN_USE_16BIT 1
+#define SMC_CAN_USE_32BIT 1
+#define SMC_NOWAIT 1
+
+#define SMC_inb(a, r) readb((a) + (r))
+#define SMC_inw(a, r) readw((a) + (r))
+#define SMC_inl(a, r) readl((a) + (r))
+#define SMC_outb(v, a, r) writeb(v, (a) + (r))
+#define SMC_outw(v, a, r) writew(v, (a) + (r))
+#define SMC_outl(v, a, r) writel(v, (a) + (r))
+#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l)
+#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l)
+
+#define RPC_LSA_DEFAULT RPC_LED_100_10
+#define RPC_LSB_DEFAULT RPC_LED_TX_RX
+
+#endif
+
+
+#ifdef SMC_USE_PXA_DMA
+/*
+ * Let's use the DMA engine on the XScale PXA2xx for RX packets. This is
+ * always happening in irq context so no need to worry about races. TX is
+ * different and probably not worth it for that reason, and not as critical
+ * as RX which can overrun memory and lose packets.
+ */
+#include <linux/pci.h>
+#include <asm/dma.h>
+
+#ifdef SMC_insl
+#undef SMC_insl
+#define SMC_insl(a, r, p, l) \
+ smc_pxa_dma_insl(a, lp->physaddr, r, dev->dma, p, l)
+static inline void
+smc_pxa_dma_insl(u_long ioaddr, u_long physaddr, int reg, int dma,
+ u_char *buf, int len)
+{
+ dma_addr_t dmabuf;
+
+ /* fallback if no DMA available */
+ if (dma == (unsigned char)-1) {
+ readsl(ioaddr + reg, buf, len);
+ return;
+ }
+
+ /* 64 bit alignment is required for memory to memory DMA */
+ if ((long)buf & 4) {
+ *((u32 *)buf)++ = SMC_inl(ioaddr, reg);
+ len--;
+ }
+
+ len *= 4;
+ dmabuf = dma_map_single(NULL, buf, len, PCI_DMA_FROMDEVICE);
+ DCSR(dma) = DCSR_NODESC;
+ DTADR(dma) = dmabuf;
+ DSADR(dma) = physaddr + reg;
+ DCMD(dma) = (DCMD_INCTRGADDR | DCMD_BURST32 |
+ DCMD_WIDTH4 | (DCMD_LENGTH & len));
+ DCSR(dma) = DCSR_NODESC | DCSR_RUN;
+ while (!(DCSR(dma) & DCSR_STOPSTATE));
+ DCSR(dma) = 0;
+ dma_unmap_single(NULL, dmabuf, len, PCI_DMA_FROMDEVICE);
+}
+#endif
+
+#ifdef SMC_insw
+#undef SMC_insw
+#define SMC_insw(a, r, p, l) \
+ smc_pxa_dma_insw(a, lp->physaddr, r, dev->dma, p, l)
+static inline void
+smc_pxa_dma_insw(u_long ioaddr, u_long physaddr, int reg, int dma,
+ u_char *buf, int len)
+{
+ dma_addr_t dmabuf;
+
+ /* fallback if no DMA available */
+ if (dma == (unsigned char)-1) {
+ readsw(ioaddr + reg, buf, len);
+ return;
+ }
+
+ /* 64 bit alignment is required for memory to memory DMA */
+ while ((long)buf & 6) {
+ *((u16 *)buf)++ = SMC_inw(ioaddr, reg);
+ len--;
+ }
+
+ len *= 2;
+ dmabuf = dma_map_single(NULL, buf, len, PCI_DMA_FROMDEVICE);
+ DCSR(dma) = DCSR_NODESC;
+ DTADR(dma) = dmabuf;
+ DSADR(dma) = physaddr + reg;
+ DCMD(dma) = (DCMD_INCTRGADDR | DCMD_BURST32 |
+ DCMD_WIDTH2 | (DCMD_LENGTH & len));
+ DCSR(dma) = DCSR_NODESC | DCSR_RUN;
+ while (!(DCSR(dma) & DCSR_STOPSTATE));
+ DCSR(dma) = 0;
+ dma_unmap_single(NULL, dmabuf, len, PCI_DMA_FROMDEVICE);
+}
+#endif
+
+static void
+smc_pxa_dma_irq(int dma, void *dummy, struct pt_regs *regs)
+{
+ DCSR(dma) = 0;
+}
+#endif /* SMC_USE_PXA_DMA */
+
+
+/* Because of bank switching, the LAN91x uses only 16 I/O ports */
+#ifndef SMC_IO_SHIFT
+#define SMC_IO_SHIFT 0
+#endif
+#define SMC_IO_EXTENT (16 << SMC_IO_SHIFT)
+
+
+/*
+ . Bank Select Register:
+ .
+ . yyyy yyyy 0000 00xx
+ . xx = bank number
+ . yyyy yyyy = 0x33, for identification purposes.
+*/
+#define BANK_SELECT (14 << SMC_IO_SHIFT)
+
+
+// Transmit Control Register
+/* BANK 0 */
+#define TCR_REG SMC_REG(0x0000, 0)
+#define TCR_ENABLE 0x0001 // When 1 we can transmit
+#define TCR_LOOP 0x0002 // Controls output pin LBK
+#define TCR_FORCOL 0x0004 // When 1 will force a collision
+#define TCR_PAD_EN 0x0080 // When 1 will pad tx frames < 64 bytes w/0
+#define TCR_NOCRC 0x0100 // When 1 will not append CRC to tx frames
+#define TCR_MON_CSN 0x0400 // When 1 tx monitors carrier
+#define TCR_FDUPLX 0x0800 // When 1 enables full duplex operation
+#define TCR_STP_SQET 0x1000 // When 1 stops tx if Signal Quality Error
+#define TCR_EPH_LOOP 0x2000 // When 1 enables EPH block loopback
+#define TCR_SWFDUP 0x8000 // When 1 enables Switched Full Duplex mode
+
+#define TCR_CLEAR 0 /* do NOTHING */
+/* the default settings for the TCR register : */
+#define TCR_DEFAULT (TCR_ENABLE | TCR_PAD_EN)
+
+
+// EPH Status Register
+/* BANK 0 */
+#define EPH_STATUS_REG SMC_REG(0x0002, 0)
+#define ES_TX_SUC 0x0001 // Last TX was successful
+#define ES_SNGL_COL 0x0002 // Single collision detected for last tx
+#define ES_MUL_COL 0x0004 // Multiple collisions detected for last tx
+#define ES_LTX_MULT 0x0008 // Last tx was a multicast
+#define ES_16COL 0x0010 // 16 Collisions Reached
+#define ES_SQET 0x0020 // Signal Quality Error Test
+#define ES_LTXBRD 0x0040 // Last tx was a broadcast
+#define ES_TXDEFR 0x0080 // Transmit Deferred
+#define ES_LATCOL 0x0200 // Late collision detected on last tx
+#define ES_LOSTCARR 0x0400 // Lost Carrier Sense
+#define ES_EXC_DEF 0x0800 // Excessive Deferral
+#define ES_CTR_ROL 0x1000 // Counter Roll Over indication
+#define ES_LINK_OK 0x4000 // Driven by inverted value of nLNK pin
+#define ES_TXUNRN 0x8000 // Tx Underrun
+
+
+// Receive Control Register
+/* BANK 0 */
+#define RCR_REG SMC_REG(0x0004, 0)
+#define RCR_RX_ABORT 0x0001 // Set if a rx frame was aborted
+#define RCR_PRMS 0x0002 // Enable promiscuous mode
+#define RCR_ALMUL 0x0004 // When set accepts all multicast frames
+#define RCR_RXEN 0x0100 // IFF this is set, we can receive packets
+#define RCR_STRIP_CRC 0x0200 // When set strips CRC from rx packets
+#define RCR_ABORT_ENB 0x0200 // When set will abort rx on collision
+#define RCR_FILT_CAR 0x0400 // When set filters leading 12 bit s of carrier
+#define RCR_SOFTRST 0x8000 // resets the chip
+
+/* the normal settings for the RCR register : */
+#define RCR_DEFAULT (RCR_STRIP_CRC | RCR_RXEN)
+#define RCR_CLEAR 0x0 // set it to a base state
+
+
+// Counter Register
+/* BANK 0 */
+#define COUNTER_REG SMC_REG(0x0006, 0)
+
+
+// Memory Information Register
+/* BANK 0 */
+#define MIR_REG SMC_REG(0x0008, 0)
+
+
+// Receive/Phy Control Register
+/* BANK 0 */
+#define RPC_REG SMC_REG(0x000A, 0)
+#define RPC_SPEED 0x2000 // When 1 PHY is in 100Mbps mode.
+#define RPC_DPLX 0x1000 // When 1 PHY is in Full-Duplex Mode
+#define RPC_ANEG 0x0800 // When 1 PHY is in Auto-Negotiate Mode
+#define RPC_LSXA_SHFT 5 // Bits to shift LS2A,LS1A,LS0A to lsb
+#define RPC_LSXB_SHFT 2 // Bits to get LS2B,LS1B,LS0B to lsb
+#define RPC_LED_100_10 (0x00) // LED = 100Mbps OR's with 10Mbps link detect
+#define RPC_LED_RES (0x01) // LED = Reserved
+#define RPC_LED_10 (0x02) // LED = 10Mbps link detect
+#define RPC_LED_FD (0x03) // LED = Full Duplex Mode
+#define RPC_LED_TX_RX (0x04) // LED = TX or RX packet occurred
+#define RPC_LED_100 (0x05) // LED = 100Mbps link dectect
+#define RPC_LED_TX (0x06) // LED = TX packet occurred
+#define RPC_LED_RX (0x07) // LED = RX packet occurred
+
+#ifndef RPC_LSA_DEFAULT
+#define RPC_LSA_DEFAULT RPC_LED_100
+#endif
+#ifndef RPC_LSB_DEFAULT
+#define RPC_LSB_DEFAULT RPC_LED_FD
+#endif
+
+#define RPC_DEFAULT (RPC_ANEG | (RPC_LSA_DEFAULT << RPC_LSXA_SHFT) | (RPC_LSB_DEFAULT << RPC_LSXB_SHFT) | RPC_SPEED | RPC_DPLX)
+
+
+/* Bank 0 0x0C is reserved */
+
+// Bank Select Register
+/* All Banks */
+#define BSR_REG 0x000E
+
+
+// Configuration Reg
+/* BANK 1 */
+#define CONFIG_REG SMC_REG(0x0000, 1)
+#define CONFIG_EXT_PHY 0x0200 // 1=external MII, 0=internal Phy
+#define CONFIG_GPCNTRL 0x0400 // Inverse value drives pin nCNTRL
+#define CONFIG_NO_WAIT 0x1000 // When 1 no extra wait states on ISA bus
+#define CONFIG_EPH_POWER_EN 0x8000 // When 0 EPH is placed into low power mode.
+
+// Default is powered-up, Internal Phy, Wait States, and pin nCNTRL=low
+#define CONFIG_DEFAULT (CONFIG_EPH_POWER_EN)
+
+
+// Base Address Register
+/* BANK 1 */
+#define BASE_REG SMC_REG(0x0002, 1)
+
+
+// Individual Address Registers
+/* BANK 1 */
+#define ADDR0_REG SMC_REG(0x0004, 1)
+#define ADDR1_REG SMC_REG(0x0006, 1)
+#define ADDR2_REG SMC_REG(0x0008, 1)
+
+
+// General Purpose Register
+/* BANK 1 */
+#define GP_REG SMC_REG(0x000A, 1)
+
+
+// Control Register
+/* BANK 1 */
+#define CTL_REG SMC_REG(0x000C, 1)
+#define CTL_RCV_BAD 0x4000 // When 1 bad CRC packets are received
+#define CTL_AUTO_RELEASE 0x0800 // When 1 tx pages are released automatically
+#define CTL_LE_ENABLE 0x0080 // When 1 enables Link Error interrupt
+#define CTL_CR_ENABLE 0x0040 // When 1 enables Counter Rollover interrupt
+#define CTL_TE_ENABLE 0x0020 // When 1 enables Transmit Error interrupt
+#define CTL_EEPROM_SELECT 0x0004 // Controls EEPROM reload & store
+#define CTL_RELOAD 0x0002 // When set reads EEPROM into registers
+#define CTL_STORE 0x0001 // When set stores registers into EEPROM
+
+
+// MMU Command Register
+/* BANK 2 */
+#define MMU_CMD_REG SMC_REG(0x0000, 2)
+#define MC_BUSY 1 // When 1 the last release has not completed
+#define MC_NOP (0<<5) // No Op
+#define MC_ALLOC (1<<5) // OR with number of 256 byte packets
+#define MC_RESET (2<<5) // Reset MMU to initial state
+#define MC_REMOVE (3<<5) // Remove the current rx packet
+#define MC_RELEASE (4<<5) // Remove and release the current rx packet
+#define MC_FREEPKT (5<<5) // Release packet in PNR register
+#define MC_ENQUEUE (6<<5) // Enqueue the packet for transmit
+#define MC_RSTTXFIFO (7<<5) // Reset the TX FIFOs
+
+
+// Packet Number Register
+/* BANK 2 */
+#define PN_REG SMC_REG(0x0002, 2)
+
+
+// Allocation Result Register
+/* BANK 2 */
+#define AR_REG SMC_REG(0x0003, 2)
+#define AR_FAILED 0x80 // Alocation Failed
+
+
+// TX FIFO Ports Register
+/* BANK 2 */
+#define TXFIFO_REG SMC_REG(0x0004, 2)
+#define TXFIFO_TEMPTY 0x80 // TX FIFO Empty
+
+// RX FIFO Ports Register
+/* BANK 2 */
+#define RXFIFO_REG SMC_REG(0x0005, 2)
+#define RXFIFO_REMPTY 0x80 // RX FIFO Empty
+
+#define FIFO_REG SMC_REG(0x0004, 2)
+
+// Pointer Register
+/* BANK 2 */
+#define PTR_REG SMC_REG(0x0006, 2)
+#define PTR_RCV 0x8000 // 1=Receive area, 0=Transmit area
+#define PTR_AUTOINC 0x4000 // Auto increment the pointer on each access
+#define PTR_READ 0x2000 // When 1 the operation is a read
+
+
+// Data Register
+/* BANK 2 */
+#define DATA_REG SMC_REG(0x0008, 2)
+
+
+// Interrupt Status/Acknowledge Register
+/* BANK 2 */
+#define INT_REG SMC_REG(0x000C, 2)
+
+
+// Interrupt Mask Register
+/* BANK 2 */
+#define IM_REG SMC_REG(0x000D, 2)
+#define IM_MDINT 0x80 // PHY MI Register 18 Interrupt
+#define IM_ERCV_INT 0x40 // Early Receive Interrupt
+#define IM_EPH_INT 0x20 // Set by Ethernet Protocol Handler section
+#define IM_RX_OVRN_INT 0x10 // Set by Receiver Overruns
+#define IM_ALLOC_INT 0x08 // Set when allocation request is completed
+#define IM_TX_EMPTY_INT 0x04 // Set if the TX FIFO goes empty
+#define IM_TX_INT 0x02 // Transmit Interrupt
+#define IM_RCV_INT 0x01 // Receive Interrupt
+
+
+// Multicast Table Registers
+/* BANK 3 */
+#define MCAST_REG1 SMC_REG(0x0000, 3)
+#define MCAST_REG2 SMC_REG(0x0002, 3)
+#define MCAST_REG3 SMC_REG(0x0004, 3)
+#define MCAST_REG4 SMC_REG(0x0006, 3)
+
+
+// Management Interface Register (MII)
+/* BANK 3 */
+#define MII_REG SMC_REG(0x0008, 3)
+#define MII_MSK_CRS100 0x4000 // Disables CRS100 detection during tx half dup
+#define MII_MDOE 0x0008 // MII Output Enable
+#define MII_MCLK 0x0004 // MII Clock, pin MDCLK
+#define MII_MDI 0x0002 // MII Input, pin MDI
+#define MII_MDO 0x0001 // MII Output, pin MDO
+
+
+// Revision Register
+/* BANK 3 */
+/* ( hi: chip id low: rev # ) */
+#define REV_REG SMC_REG(0x000A, 3)
+
+
+// Early RCV Register
+/* BANK 3 */
+/* this is NOT on SMC9192 */
+#define ERCV_REG SMC_REG(0x000C, 3)
+#define ERCV_RCV_DISCRD 0x0080 // When 1 discards a packet being received
+#define ERCV_THRESHOLD 0x001F // ERCV Threshold Mask
+
+
+// External Register
+/* BANK 7 */
+#define EXT_REG SMC_REG(0x0000, 7)
+
+
+#define CHIP_9192 3
+#define CHIP_9194 4
+#define CHIP_9195 5
+#define CHIP_9196 6
+#define CHIP_91100 7
+#define CHIP_91100FD 8
+#define CHIP_91111FD 9
+
+static const char * chip_ids[ 16 ] = {
+ NULL, NULL, NULL,
+ /* 3 */ "SMC91C90/91C92",
+ /* 4 */ "SMC91C94",
+ /* 5 */ "SMC91C95",
+ /* 6 */ "SMC91C96",
+ /* 7 */ "SMC91C100",
+ /* 8 */ "SMC91C100FD",
+ /* 9 */ "SMC91C11xFD",
+ NULL, NULL, NULL,
+ NULL, NULL, NULL};
+
+
+/*
+ . Transmit status bits
+*/
+#define TS_SUCCESS 0x0001
+#define TS_LOSTCAR 0x0400
+#define TS_LATCOL 0x0200
+#define TS_16COL 0x0010
+
+/*
+ . Receive status bits
+*/
+#define RS_ALGNERR 0x8000
+#define RS_BRODCAST 0x4000
+#define RS_BADCRC 0x2000
+#define RS_ODDFRAME 0x1000
+#define RS_TOOLONG 0x0800
+#define RS_TOOSHORT 0x0400
+#define RS_MULTICAST 0x0001
+#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
+
+
+/*
+ * PHY IDs
+ * LAN83C183 == LAN91C111 Internal PHY
+ */
+#define PHY_LAN83C183 0x0016f840
+#define PHY_LAN83C180 0x02821c50
+
+/*
+ * PHY Register Addresses (LAN91C111 Internal PHY)
+ *
+ * Generic PHY registers can be found in <linux/mii.h>
+ *
+ * These phy registers are specific to our on-board phy.
+ */
+
+// PHY Configuration Register 1
+#define PHY_CFG1_REG 0x10
+#define PHY_CFG1_LNKDIS 0x8000 // 1=Rx Link Detect Function disabled
+#define PHY_CFG1_XMTDIS 0x4000 // 1=TP Transmitter Disabled
+#define PHY_CFG1_XMTPDN 0x2000 // 1=TP Transmitter Powered Down
+#define PHY_CFG1_BYPSCR 0x0400 // 1=Bypass scrambler/descrambler
+#define PHY_CFG1_UNSCDS 0x0200 // 1=Unscramble Idle Reception Disable
+#define PHY_CFG1_EQLZR 0x0100 // 1=Rx Equalizer Disabled
+#define PHY_CFG1_CABLE 0x0080 // 1=STP(150ohm), 0=UTP(100ohm)
+#define PHY_CFG1_RLVL0 0x0040 // 1=Rx Squelch level reduced by 4.5db
+#define PHY_CFG1_TLVL_SHIFT 2 // Transmit Output Level Adjust
+#define PHY_CFG1_TLVL_MASK 0x003C
+#define PHY_CFG1_TRF_MASK 0x0003 // Transmitter Rise/Fall time
+
+
+// PHY Configuration Register 2
+#define PHY_CFG2_REG 0x11
+#define PHY_CFG2_APOLDIS 0x0020 // 1=Auto Polarity Correction disabled
+#define PHY_CFG2_JABDIS 0x0010 // 1=Jabber disabled
+#define PHY_CFG2_MREG 0x0008 // 1=Multiple register access (MII mgt)
+#define PHY_CFG2_INTMDIO 0x0004 // 1=Interrupt signaled with MDIO pulseo
+
+// PHY Status Output (and Interrupt status) Register
+#define PHY_INT_REG 0x12 // Status Output (Interrupt Status)
+#define PHY_INT_INT 0x8000 // 1=bits have changed since last read
+#define PHY_INT_LNKFAIL 0x4000 // 1=Link Not detected
+#define PHY_INT_LOSSSYNC 0x2000 // 1=Descrambler has lost sync
+#define PHY_INT_CWRD 0x1000 // 1=Invalid 4B5B code detected on rx
+#define PHY_INT_SSD 0x0800 // 1=No Start Of Stream detected on rx
+#define PHY_INT_ESD 0x0400 // 1=No End Of Stream detected on rx
+#define PHY_INT_RPOL 0x0200 // 1=Reverse Polarity detected
+#define PHY_INT_JAB 0x0100 // 1=Jabber detected
+#define PHY_INT_SPDDET 0x0080 // 1=100Base-TX mode, 0=10Base-T mode
+#define PHY_INT_DPLXDET 0x0040 // 1=Device in Full Duplex
+
+// PHY Interrupt/Status Mask Register
+#define PHY_MASK_REG 0x13 // Interrupt Mask
+// Uses the same bit definitions as PHY_INT_REG
+
+
+/*
+ * SMC91C96 ethernet config and status registers.
+ * These are in the "attribute" space.
+ */
+#define ECOR 0x8000
+#define ECOR_RESET 0x80
+#define ECOR_LEVEL_IRQ 0x40
+#define ECOR_WR_ATTRIB 0x04
+#define ECOR_ENABLE 0x01
+
+#define ECSR 0x8002
+#define ECSR_IOIS8 0x20
+#define ECSR_PWRDWN 0x04
+#define ECSR_INT 0x02
+
+#define ATTRIB_SIZE ((64*1024) << SMC_IO_SHIFT)
+
+
+/*
+ * Macros to abstract register access according to the data bus
+ * capabilities. Please use those and not the in/out primitives.
+ * Note: the following macros do *not* select the bank -- this must
+ * be done separately as needed in the main code. The SMC_REG() macro
+ * only uses the bank argument for debugging purposes (when enabled).
+ */
+
+#if SMC_DEBUG > 0
+#define SMC_REG(reg, bank) \
+ ({ \
+ int __b = SMC_CURRENT_BANK(); \
+ if (unlikely((__b & ~0xf0) != (0x3300 | bank))) { \
+ printk( "%s: bank reg screwed (0x%04x)\n", \
+ CARDNAME, __b ); \
+ BUG(); \
+ } \
+ reg<<SMC_IO_SHIFT; \
+ })
+#else
+#define SMC_REG(reg, bank) (reg<<SMC_IO_SHIFT)
+#endif
+
+#if SMC_CAN_USE_8BIT
+#define SMC_GET_PN() SMC_inb( ioaddr, PN_REG )
+#define SMC_SET_PN(x) SMC_outb( x, ioaddr, PN_REG )
+#define SMC_GET_AR() SMC_inb( ioaddr, AR_REG )
+#define SMC_GET_TXFIFO() SMC_inb( ioaddr, TXFIFO_REG )
+#define SMC_GET_RXFIFO() SMC_inb( ioaddr, RXFIFO_REG )
+#define SMC_GET_INT() SMC_inb( ioaddr, INT_REG )
+#define SMC_ACK_INT(x) SMC_outb( x, ioaddr, INT_REG )
+#define SMC_GET_INT_MASK() SMC_inb( ioaddr, IM_REG )
+#define SMC_SET_INT_MASK(x) SMC_outb( x, ioaddr, IM_REG )
+#else
+#define SMC_GET_PN() (SMC_inw( ioaddr, PN_REG ) & 0xFF)
+#define SMC_SET_PN(x) SMC_outw( x, ioaddr, PN_REG )
+#define SMC_GET_AR() (SMC_inw( ioaddr, PN_REG ) >> 8)
+#define SMC_GET_TXFIFO() (SMC_inw( ioaddr, TXFIFO_REG ) & 0xFF)
+#define SMC_GET_RXFIFO() (SMC_inw( ioaddr, TXFIFO_REG ) >> 8)
+#define SMC_GET_INT() (SMC_inw( ioaddr, INT_REG ) & 0xFF)
+#define SMC_ACK_INT(x) \
+ do { \
+ unsigned long __flags; \
+ int __mask; \
+ local_irq_save(__flags); \
+ __mask = SMC_inw( ioaddr, INT_REG ) & ~0xff; \
+ SMC_outw( __mask | (x), ioaddr, INT_REG ); \
+ local_irq_restore(__flags); \
+ } while (0)
+#define SMC_GET_INT_MASK() (SMC_inw( ioaddr, INT_REG ) >> 8)
+#define SMC_SET_INT_MASK(x) SMC_outw( (x) << 8, ioaddr, INT_REG )
+#endif
+
+#define SMC_CURRENT_BANK() SMC_inw( ioaddr, BANK_SELECT )
+#define SMC_SELECT_BANK(x) SMC_outw( x, ioaddr, BANK_SELECT )
+#define SMC_GET_BASE() SMC_inw( ioaddr, BASE_REG )
+#define SMC_SET_BASE(x) SMC_outw( x, ioaddr, BASE_REG )
+#define SMC_GET_CONFIG() SMC_inw( ioaddr, CONFIG_REG )
+#define SMC_SET_CONFIG(x) SMC_outw( x, ioaddr, CONFIG_REG )
+#define SMC_GET_COUNTER() SMC_inw( ioaddr, COUNTER_REG )
+#define SMC_GET_CTL() SMC_inw( ioaddr, CTL_REG )
+#define SMC_SET_CTL(x) SMC_outw( x, ioaddr, CTL_REG )
+#define SMC_GET_MII() SMC_inw( ioaddr, MII_REG )
+#define SMC_SET_MII(x) SMC_outw( x, ioaddr, MII_REG )
+#define SMC_GET_MIR() SMC_inw( ioaddr, MIR_REG )
+#define SMC_SET_MIR(x) SMC_outw( x, ioaddr, MIR_REG )
+#define SMC_GET_MMU_CMD() SMC_inw( ioaddr, MMU_CMD_REG )
+#define SMC_SET_MMU_CMD(x) SMC_outw( x, ioaddr, MMU_CMD_REG )
+#define SMC_GET_FIFO() SMC_inw( ioaddr, FIFO_REG )
+#define SMC_GET_PTR() SMC_inw( ioaddr, PTR_REG )
+#define SMC_SET_PTR(x) SMC_outw( x, ioaddr, PTR_REG )
+#define SMC_GET_RCR() SMC_inw( ioaddr, RCR_REG )
+#define SMC_SET_RCR(x) SMC_outw( x, ioaddr, RCR_REG )
+#define SMC_GET_REV() SMC_inw( ioaddr, REV_REG )
+#define SMC_GET_RPC() SMC_inw( ioaddr, RPC_REG )
+#define SMC_SET_RPC(x) SMC_outw( x, ioaddr, RPC_REG )
+#define SMC_GET_TCR() SMC_inw( ioaddr, TCR_REG )
+#define SMC_SET_TCR(x) SMC_outw( x, ioaddr, TCR_REG )
+
+#ifndef SMC_GET_MAC_ADDR
+#define SMC_GET_MAC_ADDR(addr) \
+ do { \
+ unsigned int __v; \
+ __v = SMC_inw( ioaddr, ADDR0_REG ); \
+ addr[0] = __v; addr[1] = __v >> 8; \
+ __v = SMC_inw( ioaddr, ADDR1_REG ); \
+ addr[2] = __v; addr[3] = __v >> 8; \
+ __v = SMC_inw( ioaddr, ADDR2_REG ); \
+ addr[4] = __v; addr[5] = __v >> 8; \
+ } while (0)
+#endif
+
+#define SMC_SET_MAC_ADDR(addr) \
+ do { \
+ SMC_outw( addr[0]|(addr[1] << 8), ioaddr, ADDR0_REG ); \
+ SMC_outw( addr[2]|(addr[3] << 8), ioaddr, ADDR1_REG ); \
+ SMC_outw( addr[4]|(addr[5] << 8), ioaddr, ADDR2_REG ); \
+ } while (0)
+
+#define SMC_CLEAR_MCAST() \
+ do { \
+ SMC_outw( 0, ioaddr, MCAST_REG1 ); \
+ SMC_outw( 0, ioaddr, MCAST_REG2 ); \
+ SMC_outw( 0, ioaddr, MCAST_REG3 ); \
+ SMC_outw( 0, ioaddr, MCAST_REG4 ); \
+ } while (0)
+#define SMC_SET_MCAST(x) \
+ do { \
+ unsigned char *mt = (x); \
+ SMC_outw( mt[0] | (mt[1] << 8), ioaddr, MCAST_REG1 ); \
+ SMC_outw( mt[2] | (mt[3] << 8), ioaddr, MCAST_REG2 ); \
+ SMC_outw( mt[4] | (mt[5] << 8), ioaddr, MCAST_REG3 ); \
+ SMC_outw( mt[6] | (mt[7] << 8), ioaddr, MCAST_REG4 ); \
+ } while (0)
+
+#if SMC_CAN_USE_32BIT
+/*
+ * Some setups just can't write 8 or 16 bits reliably when not aligned
+ * to a 32 bit boundary. I tell you that exists!
+ * We re-do the ones here that can be easily worked around if they can have
+ * their low parts written to 0 without adverse effects.
+ */
+#undef SMC_SELECT_BANK
+#define SMC_SELECT_BANK(x) SMC_outl( (x)<<16, ioaddr, 12<<SMC_IO_SHIFT )
+#undef SMC_SET_RPC
+#define SMC_SET_RPC(x) SMC_outl( (x)<<16, ioaddr, SMC_REG(8, 0) )
+#undef SMC_SET_PN
+#define SMC_SET_PN(x) SMC_outl( (x)<<16, ioaddr, SMC_REG(0, 2) )
+#undef SMC_SET_PTR
+#define SMC_SET_PTR(x) SMC_outl( (x)<<16, ioaddr, SMC_REG(4, 2) )
+#endif
+
+#if SMC_CAN_USE_32BIT
+#define SMC_PUT_PKT_HDR(status, length) \
+ SMC_outl( (status) | (length) << 16, ioaddr, DATA_REG )
+#define SMC_GET_PKT_HDR(status, length) \
+ do { \
+ unsigned int __val = SMC_inl( ioaddr, DATA_REG ); \
+ (status) = __val & 0xffff; \
+ (length) = __val >> 16; \
+ } while (0)
+#else
+#define SMC_PUT_PKT_HDR(status, length) \
+ do { \
+ SMC_outw( status, ioaddr, DATA_REG ); \
+ SMC_outw( length, ioaddr, DATA_REG ); \
+ } while (0)
+#define SMC_GET_PKT_HDR(status, length) \
+ do { \
+ (status) = SMC_inw( ioaddr, DATA_REG ); \
+ (length) = SMC_inw( ioaddr, DATA_REG ); \
+ } while (0)
+#endif
+
+#if SMC_CAN_USE_32BIT
+#define SMC_PUSH_DATA(p, l) \
+ do { \
+ char *__ptr = (p); \
+ int __len = (l); \
+ if (__len >= 2 && (long)__ptr & 2) { \
+ __len -= 2; \
+ SMC_outw( *((u16 *)__ptr)++, ioaddr, DATA_REG );\
+ } \
+ SMC_outsl( ioaddr, DATA_REG, __ptr, __len >> 2); \
+ if (__len & 2) { \
+ __ptr += (__len & ~3); \
+ SMC_outw( *((u16 *)__ptr), ioaddr, DATA_REG ); \
+ } \
+ } while (0)
+#define SMC_PULL_DATA(p, l) \
+ do { \
+ char *__ptr = (p); \
+ int __len = (l); \
+ if ((long)__ptr & 2) { \
+ /* \
+ * We want 32bit alignment here. \
+ * Since some buses perform a full 32bit \
+ * fetch even for 16bit data we can't use \
+ * SMC_inw() here. Back both source (on chip \
+ * and destination) pointers of 2 bytes. \
+ */ \
+ (long)__ptr &= ~2; \
+ __len += 2; \
+ SMC_SET_PTR( 2|PTR_READ|PTR_RCV|PTR_AUTOINC ); \
+ } \
+ __len += 2; \
+ SMC_insl( ioaddr, DATA_REG, __ptr, __len >> 2); \
+ } while (0)
+#elif SMC_CAN_USE_16BIT
+#define SMC_PUSH_DATA(p, l) SMC_outsw( ioaddr, DATA_REG, p, (l) >> 1 )
+#define SMC_PULL_DATA(p, l) SMC_insw ( ioaddr, DATA_REG, p, (l) >> 1 )
+#elif SMC_CAN_USE_8BIT
+#define SMC_PUSH_DATA(p, l) SMC_outsb( ioaddr, DATA_REG, p, l )
+#define SMC_PULL_DATA(p, l) SMC_insb ( ioaddr, DATA_REG, p, l )
+#endif
+
+#if ! SMC_CAN_USE_16BIT
+#define SMC_outw(x, ioaddr, reg) \
+ do { \
+ unsigned int __val16 = (x); \
+ SMC_outb( __val16, ioaddr, reg ); \
+ SMC_outb( __val16 >> 8, ioaddr, reg + (1 << SMC_IO_SHIFT));\
+ } while (0)
+#define SMC_inw(ioaddr, reg) \
+ ({ \
+ unsigned int __val16; \
+ __val16 = SMC_inb( ioaddr, reg ); \
+ __val16 |= SMC_inb( ioaddr, reg + (1 << SMC_IO_SHIFT)) << 8; \
+ __val16; \
+ })
+#endif
+
+
+#endif /* _SMC91X_H_ */
static int __init bigmac_probe(void)
{
struct sbus_bus *sbus;
- struct sbus_dev *sdev = 0;
+ struct sbus_dev *sdev = NULL;
static int called;
int cards = 0, v;
lp->tx_new = TX_NEXT(entry);
}
-struct net_device *last_dev = 0;
+struct net_device *last_dev;
static int lance_open(struct net_device *dev)
{
static int __init sparc_lance_probe(void)
{
struct sbus_bus *bus;
- struct sbus_dev *sdev = 0;
- struct sbus_dma *ledma = 0;
+ struct sbus_dev *sdev = NULL;
+ struct sbus_dma *ledma = NULL;
static int called;
int cards = 0, v;
for_each_sbusdev (sdev, bus) {
if (strcmp(sdev->prom_name, "le") == 0) {
cards++;
- if ((v = sparc_lance_init(sdev, 0, 0)))
+ if ((v = sparc_lance_init(sdev, NULL, NULL)))
return v;
continue;
}
cards++;
ledma = find_ledma(sdev);
if ((v = sparc_lance_init(sdev->child,
- ledma, 0)))
+ ledma, NULL)))
return v;
continue;
}
if (strcmp(sdev->prom_name, "lebuffer") == 0){
cards++;
if ((v = sparc_lance_init(sdev->child,
- 0, sdev)))
+ NULL, sdev)))
return v;
continue;
}
{
struct net_device *dev = NULL;
struct sbus_bus *bus;
- struct sbus_dev *sdev = 0;
+ struct sbus_dev *sdev = NULL;
static int called;
int cards = 0, v;
spin_lock(&card->lock);
status = inl(card->io_port+CSR5);
-#if DEBUG
+#ifdef DEBUG
print_binary(status);
printk("tx status 0x%08x 0x%08x \n",card->tx_buffer[0],card->tx_buffer[4]);
printk("rx status 0x%08x 0x%08x \n",card->rx_buffer[0],card->rx_buffer[4]);
xircom_init_ring(dev);
/* Clear the tx ring */
for (i = 0; i < TX_RING_SIZE; i++) {
- tp->tx_skbuff[i] = 0;
+ tp->tx_skbuff[i] = NULL;
tp->tx_ring[i].status = 0;
}
/* The Tx buffer descriptor is filled in as needed, but we
do need to clear the ownership bit. */
for (i = 0; i < TX_RING_SIZE; i++) {
- tp->tx_skbuff[i] = 0;
+ tp->tx_skbuff[i] = NULL;
tp->tx_ring[i].status = 0;
tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]);
#ifdef CARDBUS
/* Free the original skb. */
dev_kfree_skb_irq(tp->tx_skbuff[entry]);
- tp->tx_skbuff[entry] = 0;
+ tp->tx_skbuff[entry] = NULL;
}
#ifndef final_version
/* Free all the skbuffs in the Rx queue. */
for (i = 0; i < RX_RING_SIZE; i++) {
struct sk_buff *skb = tp->rx_skbuff[i];
- tp->rx_skbuff[i] = 0;
+ tp->rx_skbuff[i] = NULL;
tp->rx_ring[i].status = 0; /* Not owned by Xircom chip. */
tp->rx_ring[i].length = 0;
tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */
for (i = 0; i < TX_RING_SIZE; i++) {
if (tp->tx_skbuff[i])
dev_kfree_skb(tp->tx_skbuff[i]);
- tp->tx_skbuff[i] = 0;
+ tp->tx_skbuff[i] = NULL;
}
tp->open = 0;
if (entry != 0) {
/* Avoid a chip errata by prefixing a dummy entry. */
- tp->tx_skbuff[entry] = 0;
+ tp->tx_skbuff[entry] = NULL;
tp->tx_ring[entry].length =
(entry == TX_RING_SIZE - 1) ? Tx1RingWrap : 0;
tp->tx_ring[entry].buffer1 = 0;
entry = tp->cur_tx++ % TX_RING_SIZE;
}
- tp->tx_skbuff[entry] = 0;
+ tp->tx_skbuff[entry] = NULL;
/* Put the setup frame on the Tx list. */
if (entry == TX_RING_SIZE - 1)
tx_flags |= Tx1RingWrap; /* Wrap ring. */
struct velocity_info *vptr = dev->priv;
struct mac_regs * regs = vptr->mac_regs;
unsigned long flags;
- struct mii_ioctl_data *miidata = (struct mii_ioctl_data *) &(ifr->ifr_data);
+ struct mii_ioctl_data *miidata = if_mii(ifr);
int err;
switch (cmd) {
--- /dev/null
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This software may be redistributed and/or modified under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or
+ * 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.
+ *
+ * File: via-velocity.h
+ *
+ * Purpose: Header file to define driver's private structures.
+ *
+ * Author: Chuang Liang-Shing, AJ Jiang
+ *
+ * Date: Jan 24, 2003
+ */
+
+
+#ifndef VELOCITY_H
+#define VELOCITY_H
+
+#define VELOCITY_TX_CSUM_SUPPORT
+
+#define VELOCITY_NAME "via-velocity"
+#define VELOCITY_FULL_DRV_NAM "VIA Networking Velocity Family Gigabit Ethernet Adapter Driver"
+#define VELOCITY_VERSION "1.13"
+
+#define PKT_BUF_SZ 1540
+
+#define MAX_UNITS 8
+#define OPTION_DEFAULT { [0 ... MAX_UNITS-1] = -1}
+
+#define REV_ID_VT6110 (0)
+#define DEVICE_ID (0x3119)
+
+#define BYTE_REG_BITS_ON(x,p) do { writeb(readb((p))|(x),(p));} while (0)
+#define WORD_REG_BITS_ON(x,p) do { writew(readw((p))|(x),(p));} while (0)
+#define DWORD_REG_BITS_ON(x,p) do { writel(readl((p))|(x),(p));} while (0)
+
+#define BYTE_REG_BITS_IS_ON(x,p) (readb((p)) & (x))
+#define WORD_REG_BITS_IS_ON(x,p) (readw((p)) & (x))
+#define DWORD_REG_BITS_IS_ON(x,p) (readl((p)) & (x))
+
+#define BYTE_REG_BITS_OFF(x,p) do { writeb(readb((p)) & (~(x)),(p));} while (0)
+#define WORD_REG_BITS_OFF(x,p) do { writew(readw((p)) & (~(x)),(p));} while (0)
+#define DWORD_REG_BITS_OFF(x,p) do { writel(readl((p)) & (~(x)),(p));} while (0)
+
+#define BYTE_REG_BITS_SET(x,m,p) do { writeb( (readb((p)) & (~(m))) |(x),(p));} while (0)
+#define WORD_REG_BITS_SET(x,m,p) do { writew( (readw((p)) & (~(m))) |(x),(p));} while (0)
+#define DWORD_REG_BITS_SET(x,m,p) do { writel( (readl((p)) & (~(m)))|(x),(p));} while (0)
+
+#define VAR_USED(p) do {(p)=(p);} while (0)
+
+/*
+ * Purpose: Structures for MAX RX/TX descriptors.
+ */
+
+
+#define B_OWNED_BY_CHIP 1
+#define B_OWNED_BY_HOST 0
+
+/*
+ * Bits in the RSR0 register
+ */
+
+#define RSR_DETAG 0x0080
+#define RSR_SNTAG 0x0040
+#define RSR_RXER 0x0020
+#define RSR_RL 0x0010
+#define RSR_CE 0x0008
+#define RSR_FAE 0x0004
+#define RSR_CRC 0x0002
+#define RSR_VIDM 0x0001
+
+/*
+ * Bits in the RSR1 register
+ */
+
+#define RSR_RXOK 0x8000 // rx OK
+#define RSR_PFT 0x4000 // Perfect filtering address match
+#define RSR_MAR 0x2000 // MAC accept multicast address packet
+#define RSR_BAR 0x1000 // MAC accept broadcast address packet
+#define RSR_PHY 0x0800 // MAC accept physical address packet
+#define RSR_VTAG 0x0400 // 802.1p/1q tagging packet indicator
+#define RSR_STP 0x0200 // start of packet
+#define RSR_EDP 0x0100 // end of packet
+
+/*
+ * Bits in the RSR1 register
+ */
+
+#define RSR1_RXOK 0x80 // rx OK
+#define RSR1_PFT 0x40 // Perfect filtering address match
+#define RSR1_MAR 0x20 // MAC accept multicast address packet
+#define RSR1_BAR 0x10 // MAC accept broadcast address packet
+#define RSR1_PHY 0x08 // MAC accept physical address packet
+#define RSR1_VTAG 0x04 // 802.1p/1q tagging packet indicator
+#define RSR1_STP 0x02 // start of packet
+#define RSR1_EDP 0x01 // end of packet
+
+/*
+ * Bits in the CSM register
+ */
+
+#define CSM_IPOK 0x40 //IP Checkusm validatiaon ok
+#define CSM_TUPOK 0x20 //TCP/UDP Checkusm validatiaon ok
+#define CSM_FRAG 0x10 //Fragment IP datagram
+#define CSM_IPKT 0x04 //Received an IP packet
+#define CSM_TCPKT 0x02 //Received a TCP packet
+#define CSM_UDPKT 0x01 //Received a UDP packet
+
+/*
+ * Bits in the TSR0 register
+ */
+
+#define TSR0_ABT 0x0080 // Tx abort because of excessive collision
+#define TSR0_OWT 0x0040 // Jumbo frame Tx abort
+#define TSR0_OWC 0x0020 // Out of window collision
+#define TSR0_COLS 0x0010 // experience collision in this transmit event
+#define TSR0_NCR3 0x0008 // collision retry counter[3]
+#define TSR0_NCR2 0x0004 // collision retry counter[2]
+#define TSR0_NCR1 0x0002 // collision retry counter[1]
+#define TSR0_NCR0 0x0001 // collision retry counter[0]
+#define TSR0_TERR 0x8000 //
+#define TSR0_FDX 0x4000 // current transaction is serviced by full duplex mode
+#define TSR0_GMII 0x2000 // current transaction is serviced by GMII mode
+#define TSR0_LNKFL 0x1000 // packet serviced during link down
+#define TSR0_SHDN 0x0400 // shutdown case
+#define TSR0_CRS 0x0200 // carrier sense lost
+#define TSR0_CDH 0x0100 // AQE test fail (CD heartbeat)
+
+/*
+ * Bits in the TSR1 register
+ */
+
+#define TSR1_TERR 0x80 //
+#define TSR1_FDX 0x40 // current transaction is serviced by full duplex mode
+#define TSR1_GMII 0x20 // current transaction is serviced by GMII mode
+#define TSR1_LNKFL 0x10 // packet serviced during link down
+#define TSR1_SHDN 0x04 // shutdown case
+#define TSR1_CRS 0x02 // carrier sense lost
+#define TSR1_CDH 0x01 // AQE test fail (CD heartbeat)
+
+//
+// Bits in the TCR0 register
+//
+#define TCR0_TIC 0x80 // assert interrupt immediately while descriptor has been send complete
+#define TCR0_PIC 0x40 // priority interrupt request, INA# is issued over adaptive interrupt scheme
+#define TCR0_VETAG 0x20 // enable VLAN tag
+#define TCR0_IPCK 0x10 // request IP checksum calculation.
+#define TCR0_UDPCK 0x08 // request UDP checksum calculation.
+#define TCR0_TCPCK 0x04 // request TCP checksum calculation.
+#define TCR0_JMBO 0x02 // indicate a jumbo packet in GMAC side
+#define TCR0_CRC 0x01 // disable CRC generation
+
+#define TCPLS_NORMAL 3
+#define TCPLS_START 2
+#define TCPLS_END 1
+#define TCPLS_MED 0
+
+
+// max transmit or receive buffer size
+#define CB_RX_BUF_SIZE 2048UL // max buffer size
+ // NOTE: must be multiple of 4
+
+#define CB_MAX_RD_NUM 512 // MAX # of RD
+#define CB_MAX_TD_NUM 256 // MAX # of TD
+
+#define CB_INIT_RD_NUM_3119 128 // init # of RD, for setup VT3119
+#define CB_INIT_TD_NUM_3119 64 // init # of TD, for setup VT3119
+
+#define CB_INIT_RD_NUM 128 // init # of RD, for setup default
+#define CB_INIT_TD_NUM 64 // init # of TD, for setup default
+
+// for 3119
+#define CB_TD_RING_NUM 4 // # of TD rings.
+#define CB_MAX_SEG_PER_PKT 7 // max data seg per packet (Tx)
+
+
+/*
+ * If collisions excess 15 times , tx will abort, and
+ * if tx fifo underflow, tx will fail
+ * we should try to resend it
+ */
+
+#define CB_MAX_TX_ABORT_RETRY 3
+
+/*
+ * Receive descriptor
+ */
+
+struct rdesc0 {
+ u16 RSR; /* Receive status */
+ u16 len:14; /* Received packet length */
+ u16 reserved:1;
+ u16 owner:1; /* Who owns this buffer ? */
+};
+
+struct rdesc1 {
+ u16 PQTAG;
+ u8 CSM;
+ u8 IPKT;
+};
+
+struct rx_desc {
+ struct rdesc0 rdesc0;
+ struct rdesc1 rdesc1;
+ u32 pa_low; /* Low 32 bit PCI address */
+ u16 pa_high; /* Next 16 bit PCI address (48 total) */
+ u16 len:15; /* Frame size */
+ u16 inten:1; /* Enable interrupt */
+} __attribute__ ((__packed__));
+
+/*
+ * Transmit descriptor
+ */
+
+struct tdesc0 {
+ u16 TSR; /* Transmit status register */
+ u16 pktsize:14; /* Size of frame */
+ u16 reserved:1;
+ u16 owner:1; /* Who owns the buffer */
+};
+
+struct pqinf { /* Priority queue info */
+ u16 VID:12;
+ u16 CFI:1;
+ u16 priority:3;
+} __attribute__ ((__packed__));
+
+struct tdesc1 {
+ struct pqinf pqinf;
+ u8 TCR;
+ u8 TCPLS:2;
+ u8 reserved:2;
+ u8 CMDZ:4;
+} __attribute__ ((__packed__));
+
+struct td_buf {
+ u32 pa_low;
+ u16 pa_high;
+ u16 bufsize:14;
+ u16 reserved:1;
+ u16 queue:1;
+} __attribute__ ((__packed__));
+
+struct tx_desc {
+ struct tdesc0 tdesc0;
+ struct tdesc1 tdesc1;
+ struct td_buf td_buf[7];
+};
+
+struct velocity_rd_info {
+ struct sk_buff *skb;
+ dma_addr_t skb_dma;
+};
+
+/**
+ * alloc_rd_info - allocate an rd info block
+ *
+ * Alocate and initialize a receive info structure used for keeping
+ * track of kernel side information related to each receive
+ * descriptor we are using
+ */
+
+static inline struct velocity_rd_info *alloc_rd_info(void)
+{
+ struct velocity_rd_info *ptr;
+ if ((ptr = kmalloc(sizeof(struct velocity_rd_info), GFP_ATOMIC)) == NULL)
+ return NULL;
+ else {
+ memset(ptr, 0, sizeof(struct velocity_rd_info));
+ return ptr;
+ }
+}
+
+/*
+ * Used to track transmit side buffers.
+ */
+
+struct velocity_td_info {
+ struct sk_buff *skb;
+ u8 *buf;
+ int nskb_dma;
+ dma_addr_t skb_dma[7];
+ dma_addr_t buf_dma;
+};
+
+enum {
+ OWNED_BY_HOST = 0,
+ OWNED_BY_NIC = 1
+} velocity_owner;
+
+
+/*
+ * MAC registers and macros.
+ */
+
+
+#define MCAM_SIZE 64
+#define VCAM_SIZE 64
+#define TX_QUEUE_NO 4
+
+#define MAX_HW_MIB_COUNTER 32
+#define VELOCITY_MIN_MTU (1514-14)
+#define VELOCITY_MAX_MTU (9000)
+
+/*
+ * Registers in the MAC
+ */
+
+#define MAC_REG_PAR 0x00 // physical address
+#define MAC_REG_RCR 0x06
+#define MAC_REG_TCR 0x07
+#define MAC_REG_CR0_SET 0x08
+#define MAC_REG_CR1_SET 0x09
+#define MAC_REG_CR2_SET 0x0A
+#define MAC_REG_CR3_SET 0x0B
+#define MAC_REG_CR0_CLR 0x0C
+#define MAC_REG_CR1_CLR 0x0D
+#define MAC_REG_CR2_CLR 0x0E
+#define MAC_REG_CR3_CLR 0x0F
+#define MAC_REG_MAR 0x10
+#define MAC_REG_CAM 0x10
+#define MAC_REG_DEC_BASE_HI 0x18
+#define MAC_REG_DBF_BASE_HI 0x1C
+#define MAC_REG_ISR_CTL 0x20
+#define MAC_REG_ISR_HOTMR 0x20
+#define MAC_REG_ISR_TSUPTHR 0x20
+#define MAC_REG_ISR_RSUPTHR 0x20
+#define MAC_REG_ISR_CTL1 0x21
+#define MAC_REG_TXE_SR 0x22
+#define MAC_REG_RXE_SR 0x23
+#define MAC_REG_ISR 0x24
+#define MAC_REG_ISR0 0x24
+#define MAC_REG_ISR1 0x25
+#define MAC_REG_ISR2 0x26
+#define MAC_REG_ISR3 0x27
+#define MAC_REG_IMR 0x28
+#define MAC_REG_IMR0 0x28
+#define MAC_REG_IMR1 0x29
+#define MAC_REG_IMR2 0x2A
+#define MAC_REG_IMR3 0x2B
+#define MAC_REG_TDCSR_SET 0x30
+#define MAC_REG_RDCSR_SET 0x32
+#define MAC_REG_TDCSR_CLR 0x34
+#define MAC_REG_RDCSR_CLR 0x36
+#define MAC_REG_RDBASE_LO 0x38
+#define MAC_REG_RDINDX 0x3C
+#define MAC_REG_TDBASE_LO 0x40
+#define MAC_REG_RDCSIZE 0x50
+#define MAC_REG_TDCSIZE 0x52
+#define MAC_REG_TDINDX 0x54
+#define MAC_REG_TDIDX0 0x54
+#define MAC_REG_TDIDX1 0x56
+#define MAC_REG_TDIDX2 0x58
+#define MAC_REG_TDIDX3 0x5A
+#define MAC_REG_PAUSE_TIMER 0x5C
+#define MAC_REG_RBRDU 0x5E
+#define MAC_REG_FIFO_TEST0 0x60
+#define MAC_REG_FIFO_TEST1 0x64
+#define MAC_REG_CAMADDR 0x68
+#define MAC_REG_CAMCR 0x69
+#define MAC_REG_GFTEST 0x6A
+#define MAC_REG_FTSTCMD 0x6B
+#define MAC_REG_MIICFG 0x6C
+#define MAC_REG_MIISR 0x6D
+#define MAC_REG_PHYSR0 0x6E
+#define MAC_REG_PHYSR1 0x6F
+#define MAC_REG_MIICR 0x70
+#define MAC_REG_MIIADR 0x71
+#define MAC_REG_MIIDATA 0x72
+#define MAC_REG_SOFT_TIMER0 0x74
+#define MAC_REG_SOFT_TIMER1 0x76
+#define MAC_REG_CFGA 0x78
+#define MAC_REG_CFGB 0x79
+#define MAC_REG_CFGC 0x7A
+#define MAC_REG_CFGD 0x7B
+#define MAC_REG_DCFG0 0x7C
+#define MAC_REG_DCFG1 0x7D
+#define MAC_REG_MCFG0 0x7E
+#define MAC_REG_MCFG1 0x7F
+
+#define MAC_REG_TBIST 0x80
+#define MAC_REG_RBIST 0x81
+#define MAC_REG_PMCC 0x82
+#define MAC_REG_STICKHW 0x83
+#define MAC_REG_MIBCR 0x84
+#define MAC_REG_EERSV 0x85
+#define MAC_REG_REVID 0x86
+#define MAC_REG_MIBREAD 0x88
+#define MAC_REG_BPMA 0x8C
+#define MAC_REG_EEWR_DATA 0x8C
+#define MAC_REG_BPMD_WR 0x8F
+#define MAC_REG_BPCMD 0x90
+#define MAC_REG_BPMD_RD 0x91
+#define MAC_REG_EECHKSUM 0x92
+#define MAC_REG_EECSR 0x93
+#define MAC_REG_EERD_DATA 0x94
+#define MAC_REG_EADDR 0x96
+#define MAC_REG_EMBCMD 0x97
+#define MAC_REG_JMPSR0 0x98
+#define MAC_REG_JMPSR1 0x99
+#define MAC_REG_JMPSR2 0x9A
+#define MAC_REG_JMPSR3 0x9B
+#define MAC_REG_CHIPGSR 0x9C
+#define MAC_REG_TESTCFG 0x9D
+#define MAC_REG_DEBUG 0x9E
+#define MAC_REG_CHIPGCR 0x9F
+#define MAC_REG_WOLCR0_SET 0xA0
+#define MAC_REG_WOLCR1_SET 0xA1
+#define MAC_REG_PWCFG_SET 0xA2
+#define MAC_REG_WOLCFG_SET 0xA3
+#define MAC_REG_WOLCR0_CLR 0xA4
+#define MAC_REG_WOLCR1_CLR 0xA5
+#define MAC_REG_PWCFG_CLR 0xA6
+#define MAC_REG_WOLCFG_CLR 0xA7
+#define MAC_REG_WOLSR0_SET 0xA8
+#define MAC_REG_WOLSR1_SET 0xA9
+#define MAC_REG_WOLSR0_CLR 0xAC
+#define MAC_REG_WOLSR1_CLR 0xAD
+#define MAC_REG_PATRN_CRC0 0xB0
+#define MAC_REG_PATRN_CRC1 0xB2
+#define MAC_REG_PATRN_CRC2 0xB4
+#define MAC_REG_PATRN_CRC3 0xB6
+#define MAC_REG_PATRN_CRC4 0xB8
+#define MAC_REG_PATRN_CRC5 0xBA
+#define MAC_REG_PATRN_CRC6 0xBC
+#define MAC_REG_PATRN_CRC7 0xBE
+#define MAC_REG_BYTEMSK0_0 0xC0
+#define MAC_REG_BYTEMSK0_1 0xC4
+#define MAC_REG_BYTEMSK0_2 0xC8
+#define MAC_REG_BYTEMSK0_3 0xCC
+#define MAC_REG_BYTEMSK1_0 0xD0
+#define MAC_REG_BYTEMSK1_1 0xD4
+#define MAC_REG_BYTEMSK1_2 0xD8
+#define MAC_REG_BYTEMSK1_3 0xDC
+#define MAC_REG_BYTEMSK2_0 0xE0
+#define MAC_REG_BYTEMSK2_1 0xE4
+#define MAC_REG_BYTEMSK2_2 0xE8
+#define MAC_REG_BYTEMSK2_3 0xEC
+#define MAC_REG_BYTEMSK3_0 0xF0
+#define MAC_REG_BYTEMSK3_1 0xF4
+#define MAC_REG_BYTEMSK3_2 0xF8
+#define MAC_REG_BYTEMSK3_3 0xFC
+
+/*
+ * Bits in the RCR register
+ */
+
+#define RCR_AS 0x80
+#define RCR_AP 0x40
+#define RCR_AL 0x20
+#define RCR_PROM 0x10
+#define RCR_AB 0x08
+#define RCR_AM 0x04
+#define RCR_AR 0x02
+#define RCR_SEP 0x01
+
+/*
+ * Bits in the TCR register
+ */
+
+#define TCR_TB2BDIS 0x80
+#define TCR_COLTMC1 0x08
+#define TCR_COLTMC0 0x04
+#define TCR_LB1 0x02 /* loopback[1] */
+#define TCR_LB0 0x01 /* loopback[0] */
+
+/*
+ * Bits in the CR0 register
+ */
+
+#define CR0_TXON 0x00000008UL
+#define CR0_RXON 0x00000004UL
+#define CR0_STOP 0x00000002UL /* stop MAC, default = 1 */
+#define CR0_STRT 0x00000001UL /* start MAC */
+#define CR0_SFRST 0x00008000UL /* software reset */
+#define CR0_TM1EN 0x00004000UL
+#define CR0_TM0EN 0x00002000UL
+#define CR0_DPOLL 0x00000800UL /* disable rx/tx auto polling */
+#define CR0_DISAU 0x00000100UL
+#define CR0_XONEN 0x00800000UL
+#define CR0_FDXTFCEN 0x00400000UL /* full-duplex TX flow control enable */
+#define CR0_FDXRFCEN 0x00200000UL /* full-duplex RX flow control enable */
+#define CR0_HDXFCEN 0x00100000UL /* half-duplex flow control enable */
+#define CR0_XHITH1 0x00080000UL /* TX XON high threshold 1 */
+#define CR0_XHITH0 0x00040000UL /* TX XON high threshold 0 */
+#define CR0_XLTH1 0x00020000UL /* TX pause frame low threshold 1 */
+#define CR0_XLTH0 0x00010000UL /* TX pause frame low threshold 0 */
+#define CR0_GSPRST 0x80000000UL
+#define CR0_FORSRST 0x40000000UL
+#define CR0_FPHYRST 0x20000000UL
+#define CR0_DIAG 0x10000000UL
+#define CR0_INTPCTL 0x04000000UL
+#define CR0_GINTMSK1 0x02000000UL
+#define CR0_GINTMSK0 0x01000000UL
+
+/*
+ * Bits in the CR1 register
+ */
+
+#define CR1_SFRST 0x80 /* software reset */
+#define CR1_TM1EN 0x40
+#define CR1_TM0EN 0x20
+#define CR1_DPOLL 0x08 /* disable rx/tx auto polling */
+#define CR1_DISAU 0x01
+
+/*
+ * Bits in the CR2 register
+ */
+
+#define CR2_XONEN 0x80
+#define CR2_FDXTFCEN 0x40 /* full-duplex TX flow control enable */
+#define CR2_FDXRFCEN 0x20 /* full-duplex RX flow control enable */
+#define CR2_HDXFCEN 0x10 /* half-duplex flow control enable */
+#define CR2_XHITH1 0x08 /* TX XON high threshold 1 */
+#define CR2_XHITH0 0x04 /* TX XON high threshold 0 */
+#define CR2_XLTH1 0x02 /* TX pause frame low threshold 1 */
+#define CR2_XLTH0 0x01 /* TX pause frame low threshold 0 */
+
+/*
+ * Bits in the CR3 register
+ */
+
+#define CR3_GSPRST 0x80
+#define CR3_FORSRST 0x40
+#define CR3_FPHYRST 0x20
+#define CR3_DIAG 0x10
+#define CR3_INTPCTL 0x04
+#define CR3_GINTMSK1 0x02
+#define CR3_GINTMSK0 0x01
+
+#define ISRCTL_UDPINT 0x8000
+#define ISRCTL_TSUPDIS 0x4000
+#define ISRCTL_RSUPDIS 0x2000
+#define ISRCTL_PMSK1 0x1000
+#define ISRCTL_PMSK0 0x0800
+#define ISRCTL_INTPD 0x0400
+#define ISRCTL_HCRLD 0x0200
+#define ISRCTL_SCRLD 0x0100
+
+/*
+ * Bits in the ISR_CTL1 register
+ */
+
+#define ISRCTL1_UDPINT 0x80
+#define ISRCTL1_TSUPDIS 0x40
+#define ISRCTL1_RSUPDIS 0x20
+#define ISRCTL1_PMSK1 0x10
+#define ISRCTL1_PMSK0 0x08
+#define ISRCTL1_INTPD 0x04
+#define ISRCTL1_HCRLD 0x02
+#define ISRCTL1_SCRLD 0x01
+
+/*
+ * Bits in the TXE_SR register
+ */
+
+#define TXESR_TFDBS 0x08
+#define TXESR_TDWBS 0x04
+#define TXESR_TDRBS 0x02
+#define TXESR_TDSTR 0x01
+
+/*
+ * Bits in the RXE_SR register
+ */
+
+#define RXESR_RFDBS 0x08
+#define RXESR_RDWBS 0x04
+#define RXESR_RDRBS 0x02
+#define RXESR_RDSTR 0x01
+
+/*
+ * Bits in the ISR register
+ */
+
+#define ISR_ISR3 0x80000000UL
+#define ISR_ISR2 0x40000000UL
+#define ISR_ISR1 0x20000000UL
+#define ISR_ISR0 0x10000000UL
+#define ISR_TXSTLI 0x02000000UL
+#define ISR_RXSTLI 0x01000000UL
+#define ISR_HFLD 0x00800000UL
+#define ISR_UDPI 0x00400000UL
+#define ISR_MIBFI 0x00200000UL
+#define ISR_SHDNI 0x00100000UL
+#define ISR_PHYI 0x00080000UL
+#define ISR_PWEI 0x00040000UL
+#define ISR_TMR1I 0x00020000UL
+#define ISR_TMR0I 0x00010000UL
+#define ISR_SRCI 0x00008000UL
+#define ISR_LSTPEI 0x00004000UL
+#define ISR_LSTEI 0x00002000UL
+#define ISR_OVFI 0x00001000UL
+#define ISR_FLONI 0x00000800UL
+#define ISR_RACEI 0x00000400UL
+#define ISR_TXWB1I 0x00000200UL
+#define ISR_TXWB0I 0x00000100UL
+#define ISR_PTX3I 0x00000080UL
+#define ISR_PTX2I 0x00000040UL
+#define ISR_PTX1I 0x00000020UL
+#define ISR_PTX0I 0x00000010UL
+#define ISR_PTXI 0x00000008UL
+#define ISR_PRXI 0x00000004UL
+#define ISR_PPTXI 0x00000002UL
+#define ISR_PPRXI 0x00000001UL
+
+/*
+ * Bits in the IMR register
+ */
+
+#define IMR_TXSTLM 0x02000000UL
+#define IMR_UDPIM 0x00400000UL
+#define IMR_MIBFIM 0x00200000UL
+#define IMR_SHDNIM 0x00100000UL
+#define IMR_PHYIM 0x00080000UL
+#define IMR_PWEIM 0x00040000UL
+#define IMR_TMR1IM 0x00020000UL
+#define IMR_TMR0IM 0x00010000UL
+
+#define IMR_SRCIM 0x00008000UL
+#define IMR_LSTPEIM 0x00004000UL
+#define IMR_LSTEIM 0x00002000UL
+#define IMR_OVFIM 0x00001000UL
+#define IMR_FLONIM 0x00000800UL
+#define IMR_RACEIM 0x00000400UL
+#define IMR_TXWB1IM 0x00000200UL
+#define IMR_TXWB0IM 0x00000100UL
+
+#define IMR_PTX3IM 0x00000080UL
+#define IMR_PTX2IM 0x00000040UL
+#define IMR_PTX1IM 0x00000020UL
+#define IMR_PTX0IM 0x00000010UL
+#define IMR_PTXIM 0x00000008UL
+#define IMR_PRXIM 0x00000004UL
+#define IMR_PPTXIM 0x00000002UL
+#define IMR_PPRXIM 0x00000001UL
+
+/* 0x0013FB0FUL = initial value of IMR */
+
+#define INT_MASK_DEF (IMR_PPTXIM|IMR_PPRXIM|IMR_PTXIM|IMR_PRXIM|\
+ IMR_PWEIM|IMR_TXWB0IM|IMR_TXWB1IM|IMR_FLONIM|\
+ IMR_OVFIM|IMR_LSTEIM|IMR_LSTPEIM|IMR_SRCIM|IMR_MIBFIM|\
+ IMR_SHDNIM|IMR_TMR1IM|IMR_TMR0IM|IMR_TXSTLM)
+
+/*
+ * Bits in the TDCSR0/1, RDCSR0 register
+ */
+
+#define TRDCSR_DEAD 0x0008
+#define TRDCSR_WAK 0x0004
+#define TRDCSR_ACT 0x0002
+#define TRDCSR_RUN 0x0001
+
+/*
+ * Bits in the CAMADDR register
+ */
+
+#define CAMADDR_CAMEN 0x80
+#define CAMADDR_VCAMSL 0x40
+
+/*
+ * Bits in the CAMCR register
+ */
+
+#define CAMCR_PS1 0x80
+#define CAMCR_PS0 0x40
+#define CAMCR_AITRPKT 0x20
+#define CAMCR_AITR16 0x10
+#define CAMCR_CAMRD 0x08
+#define CAMCR_CAMWR 0x04
+#define CAMCR_PS_CAM_MASK 0x40
+#define CAMCR_PS_CAM_DATA 0x80
+#define CAMCR_PS_MAR 0x00
+
+/*
+ * Bits in the MIICFG register
+ */
+
+#define MIICFG_MPO1 0x80
+#define MIICFG_MPO0 0x40
+#define MIICFG_MFDC 0x20
+
+/*
+ * Bits in the MIISR register
+ */
+
+#define MIISR_MIDLE 0x80
+
+/*
+ * Bits in the PHYSR0 register
+ */
+
+#define PHYSR0_PHYRST 0x80
+#define PHYSR0_LINKGD 0x40
+#define PHYSR0_FDPX 0x10
+#define PHYSR0_SPDG 0x08
+#define PHYSR0_SPD10 0x04
+#define PHYSR0_RXFLC 0x02
+#define PHYSR0_TXFLC 0x01
+
+/*
+ * Bits in the PHYSR1 register
+ */
+
+#define PHYSR1_PHYTBI 0x01
+
+/*
+ * Bits in the MIICR register
+ */
+
+#define MIICR_MAUTO 0x80
+#define MIICR_RCMD 0x40
+#define MIICR_WCMD 0x20
+#define MIICR_MDPM 0x10
+#define MIICR_MOUT 0x08
+#define MIICR_MDO 0x04
+#define MIICR_MDI 0x02
+#define MIICR_MDC 0x01
+
+/*
+ * Bits in the MIIADR register
+ */
+
+#define MIIADR_SWMPL 0x80
+
+/*
+ * Bits in the CFGA register
+ */
+
+#define CFGA_PMHCTG 0x08
+#define CFGA_GPIO1PD 0x04
+#define CFGA_ABSHDN 0x02
+#define CFGA_PACPI 0x01
+
+/*
+ * Bits in the CFGB register
+ */
+
+#define CFGB_GTCKOPT 0x80
+#define CFGB_MIIOPT 0x40
+#define CFGB_CRSEOPT 0x20
+#define CFGB_OFSET 0x10
+#define CFGB_CRANDOM 0x08
+#define CFGB_CAP 0x04
+#define CFGB_MBA 0x02
+#define CFGB_BAKOPT 0x01
+
+/*
+ * Bits in the CFGC register
+ */
+
+#define CFGC_EELOAD 0x80
+#define CFGC_BROPT 0x40
+#define CFGC_DLYEN 0x20
+#define CFGC_DTSEL 0x10
+#define CFGC_BTSEL 0x08
+#define CFGC_BPS2 0x04 /* bootrom select[2] */
+#define CFGC_BPS1 0x02 /* bootrom select[1] */
+#define CFGC_BPS0 0x01 /* bootrom select[0] */
+
+/*
+ * Bits in the CFGD register
+ */
+
+#define CFGD_IODIS 0x80
+#define CFGD_MSLVDACEN 0x40
+#define CFGD_CFGDACEN 0x20
+#define CFGD_PCI64EN 0x10
+#define CFGD_HTMRL4 0x08
+
+/*
+ * Bits in the DCFG1 register
+ */
+
+#define DCFG_XMWI 0x8000
+#define DCFG_XMRM 0x4000
+#define DCFG_XMRL 0x2000
+#define DCFG_PERDIS 0x1000
+#define DCFG_MRWAIT 0x0400
+#define DCFG_MWWAIT 0x0200
+#define DCFG_LATMEN 0x0100
+
+/*
+ * Bits in the MCFG0 register
+ */
+
+#define MCFG_RXARB 0x0080
+#define MCFG_RFT1 0x0020
+#define MCFG_RFT0 0x0010
+#define MCFG_LOWTHOPT 0x0008
+#define MCFG_PQEN 0x0004
+#define MCFG_RTGOPT 0x0002
+#define MCFG_VIDFR 0x0001
+
+/*
+ * Bits in the MCFG1 register
+ */
+
+#define MCFG_TXARB 0x8000
+#define MCFG_TXQBK1 0x0800
+#define MCFG_TXQBK0 0x0400
+#define MCFG_TXQNOBK 0x0200
+#define MCFG_SNAPOPT 0x0100
+
+/*
+ * Bits in the PMCC register
+ */
+
+#define PMCC_DSI 0x80
+#define PMCC_D2_DIS 0x40
+#define PMCC_D1_DIS 0x20
+#define PMCC_D3C_EN 0x10
+#define PMCC_D3H_EN 0x08
+#define PMCC_D2_EN 0x04
+#define PMCC_D1_EN 0x02
+#define PMCC_D0_EN 0x01
+
+/*
+ * Bits in STICKHW
+ */
+
+#define STICKHW_SWPTAG 0x10
+#define STICKHW_WOLSR 0x08
+#define STICKHW_WOLEN 0x04
+#define STICKHW_DS1 0x02 /* R/W by software/cfg cycle */
+#define STICKHW_DS0 0x01 /* suspend well DS write port */
+
+/*
+ * Bits in the MIBCR register
+ */
+
+#define MIBCR_MIBISTOK 0x80
+#define MIBCR_MIBISTGO 0x40
+#define MIBCR_MIBINC 0x20
+#define MIBCR_MIBHI 0x10
+#define MIBCR_MIBFRZ 0x08
+#define MIBCR_MIBFLSH 0x04
+#define MIBCR_MPTRINI 0x02
+#define MIBCR_MIBCLR 0x01
+
+/*
+ * Bits in the EERSV register
+ */
+
+#define EERSV_BOOT_RPL ((u8) 0x01) /* Boot method selection for VT6110 */
+
+#define EERSV_BOOT_MASK ((u8) 0x06)
+#define EERSV_BOOT_INT19 ((u8) 0x00)
+#define EERSV_BOOT_INT18 ((u8) 0x02)
+#define EERSV_BOOT_LOCAL ((u8) 0x04)
+#define EERSV_BOOT_BEV ((u8) 0x06)
+
+
+/*
+ * Bits in BPCMD
+ */
+
+#define BPCMD_BPDNE 0x80
+#define BPCMD_EBPWR 0x02
+#define BPCMD_EBPRD 0x01
+
+/*
+ * Bits in the EECSR register
+ */
+
+#define EECSR_EMBP 0x40 /* eeprom embeded programming */
+#define EECSR_RELOAD 0x20 /* eeprom content reload */
+#define EECSR_DPM 0x10 /* eeprom direct programming */
+#define EECSR_ECS 0x08 /* eeprom CS pin */
+#define EECSR_ECK 0x04 /* eeprom CK pin */
+#define EECSR_EDI 0x02 /* eeprom DI pin */
+#define EECSR_EDO 0x01 /* eeprom DO pin */
+
+/*
+ * Bits in the EMBCMD register
+ */
+
+#define EMBCMD_EDONE 0x80
+#define EMBCMD_EWDIS 0x08
+#define EMBCMD_EWEN 0x04
+#define EMBCMD_EWR 0x02
+#define EMBCMD_ERD 0x01
+
+/*
+ * Bits in TESTCFG register
+ */
+
+#define TESTCFG_HBDIS 0x80
+
+/*
+ * Bits in CHIPGCR register
+ */
+
+#define CHIPGCR_FCGMII 0x80
+#define CHIPGCR_FCFDX 0x40
+#define CHIPGCR_FCRESV 0x20
+#define CHIPGCR_FCMODE 0x10
+#define CHIPGCR_LPSOPT 0x08
+#define CHIPGCR_TM1US 0x04
+#define CHIPGCR_TM0US 0x02
+#define CHIPGCR_PHYINTEN 0x01
+
+/*
+ * Bits in WOLCR0
+ */
+
+#define WOLCR_MSWOLEN7 0x0080 /* enable pattern match filtering */
+#define WOLCR_MSWOLEN6 0x0040
+#define WOLCR_MSWOLEN5 0x0020
+#define WOLCR_MSWOLEN4 0x0010
+#define WOLCR_MSWOLEN3 0x0008
+#define WOLCR_MSWOLEN2 0x0004
+#define WOLCR_MSWOLEN1 0x0002
+#define WOLCR_MSWOLEN0 0x0001
+#define WOLCR_ARP_EN 0x0001
+
+/*
+ * Bits in WOLCR1
+ */
+
+#define WOLCR_LINKOFF_EN 0x0800 /* link off detected enable */
+#define WOLCR_LINKON_EN 0x0400 /* link on detected enable */
+#define WOLCR_MAGIC_EN 0x0200 /* magic packet filter enable */
+#define WOLCR_UNICAST_EN 0x0100 /* unicast filter enable */
+
+
+/*
+ * Bits in PWCFG
+ */
+
+#define PWCFG_PHYPWOPT 0x80 /* internal MII I/F timing */
+#define PWCFG_PCISTICK 0x40 /* PCI sticky R/W enable */
+#define PWCFG_WOLTYPE 0x20 /* pulse(1) or button (0) */
+#define PWCFG_LEGCY_WOL 0x10
+#define PWCFG_PMCSR_PME_SR 0x08
+#define PWCFG_PMCSR_PME_EN 0x04 /* control by PCISTICK */
+#define PWCFG_LEGACY_WOLSR 0x02 /* Legacy WOL_SR shadow */
+#define PWCFG_LEGACY_WOLEN 0x01 /* Legacy WOL_EN shadow */
+
+/*
+ * Bits in WOLCFG
+ */
+
+#define WOLCFG_PMEOVR 0x80 /* for legacy use, force PMEEN always */
+#define WOLCFG_SAM 0x20 /* accept multicast case reset, default=0 */
+#define WOLCFG_SAB 0x10 /* accept broadcast case reset, default=0 */
+#define WOLCFG_SMIIACC 0x08 /* ?? */
+#define WOLCFG_SGENWH 0x02
+#define WOLCFG_PHYINTEN 0x01 /* 0:PHYINT trigger enable, 1:use internal MII
+ to report status change */
+/*
+ * Bits in WOLSR1
+ */
+
+#define WOLSR_LINKOFF_INT 0x0800
+#define WOLSR_LINKON_INT 0x0400
+#define WOLSR_MAGIC_INT 0x0200
+#define WOLSR_UNICAST_INT 0x0100
+
+/*
+ * Ethernet address filter type
+ */
+
+#define PKT_TYPE_NONE 0x0000 /* Turn off receiver */
+#define PKT_TYPE_DIRECTED 0x0001 /* obselete, directed address is always accepted */
+#define PKT_TYPE_MULTICAST 0x0002
+#define PKT_TYPE_ALL_MULTICAST 0x0004
+#define PKT_TYPE_BROADCAST 0x0008
+#define PKT_TYPE_PROMISCUOUS 0x0020
+#define PKT_TYPE_LONG 0x2000 /* NOTE.... the definition of LONG is >2048 bytes in our chip */
+#define PKT_TYPE_RUNT 0x4000
+#define PKT_TYPE_ERROR 0x8000 /* Accept error packets, e.g. CRC error */
+
+/*
+ * Loopback mode
+ */
+
+#define MAC_LB_NONE 0x00
+#define MAC_LB_INTERNAL 0x01
+#define MAC_LB_EXTERNAL 0x02
+
+/*
+ * Enabled mask value of irq
+ */
+
+#if defined(_SIM)
+#define IMR_MASK_VALUE 0x0033FF0FUL /* initial value of IMR
+ set IMR0 to 0x0F according to spec */
+
+#else
+#define IMR_MASK_VALUE 0x0013FB0FUL /* initial value of IMR
+ ignore MIBFI,RACEI to
+ reduce intr. frequency
+ NOTE.... do not enable NoBuf int mask at driver driver
+ when (1) NoBuf -> RxThreshold = SF
+ (2) OK -> RxThreshold = original value
+ */
+#endif
+
+/*
+ * Revision id
+ */
+
+#define REV_ID_VT3119_A0 0x00
+#define REV_ID_VT3119_A1 0x01
+#define REV_ID_VT3216_A0 0x10
+
+/*
+ * Max time out delay time
+ */
+
+#define W_MAX_TIMEOUT 0x0FFFU
+
+
+/*
+ * MAC registers as a structure. Cannot be directly accessed this
+ * way but generates offsets for readl/writel() calls
+ */
+
+struct mac_regs {
+ volatile u8 PAR[6]; /* 0x00 */
+ volatile u8 RCR;
+ volatile u8 TCR;
+
+ volatile u32 CR0Set; /* 0x08 */
+ volatile u32 CR0Clr; /* 0x0C */
+
+ volatile u8 MARCAM[8]; /* 0x10 */
+
+ volatile u32 DecBaseHi; /* 0x18 */
+ volatile u16 DbfBaseHi; /* 0x1C */
+ volatile u16 reserved_1E;
+
+ volatile u16 ISRCTL; /* 0x20 */
+ volatile u8 TXESR;
+ volatile u8 RXESR;
+
+ volatile u32 ISR; /* 0x24 */
+ volatile u32 IMR;
+
+ volatile u32 TDStatusPort; /* 0x2C */
+
+ volatile u16 TDCSRSet; /* 0x30 */
+ volatile u8 RDCSRSet;
+ volatile u8 reserved_33;
+ volatile u16 TDCSRClr;
+ volatile u8 RDCSRClr;
+ volatile u8 reserved_37;
+
+ volatile u32 RDBaseLo; /* 0x38 */
+ volatile u16 RDIdx; /* 0x3C */
+ volatile u16 reserved_3E;
+
+ volatile u32 TDBaseLo[4]; /* 0x40 */
+
+ volatile u16 RDCSize; /* 0x50 */
+ volatile u16 TDCSize; /* 0x52 */
+ volatile u16 TDIdx[4]; /* 0x54 */
+ volatile u16 tx_pause_timer; /* 0x5C */
+ volatile u16 RBRDU; /* 0x5E */
+
+ volatile u32 FIFOTest0; /* 0x60 */
+ volatile u32 FIFOTest1; /* 0x64 */
+
+ volatile u8 CAMADDR; /* 0x68 */
+ volatile u8 CAMCR; /* 0x69 */
+ volatile u8 GFTEST; /* 0x6A */
+ volatile u8 FTSTCMD; /* 0x6B */
+
+ volatile u8 MIICFG; /* 0x6C */
+ volatile u8 MIISR;
+ volatile u8 PHYSR0;
+ volatile u8 PHYSR1;
+ volatile u8 MIICR;
+ volatile u8 MIIADR;
+ volatile u16 MIIDATA;
+
+ volatile u16 SoftTimer0; /* 0x74 */
+ volatile u16 SoftTimer1;
+
+ volatile u8 CFGA; /* 0x78 */
+ volatile u8 CFGB;
+ volatile u8 CFGC;
+ volatile u8 CFGD;
+
+ volatile u16 DCFG; /* 0x7C */
+ volatile u16 MCFG;
+
+ volatile u8 TBIST; /* 0x80 */
+ volatile u8 RBIST;
+ volatile u8 PMCPORT;
+ volatile u8 STICKHW;
+
+ volatile u8 MIBCR; /* 0x84 */
+ volatile u8 reserved_85;
+ volatile u8 rev_id;
+ volatile u8 PORSTS;
+
+ volatile u32 MIBData; /* 0x88 */
+
+ volatile u16 EEWrData;
+
+ volatile u8 reserved_8E;
+ volatile u8 BPMDWr;
+ volatile u8 BPCMD;
+ volatile u8 BPMDRd;
+
+ volatile u8 EECHKSUM; /* 0x92 */
+ volatile u8 EECSR;
+
+ volatile u16 EERdData; /* 0x94 */
+ volatile u8 EADDR;
+ volatile u8 EMBCMD;
+
+
+ volatile u8 JMPSR0; /* 0x98 */
+ volatile u8 JMPSR1;
+ volatile u8 JMPSR2;
+ volatile u8 JMPSR3;
+ volatile u8 CHIPGSR; /* 0x9C */
+ volatile u8 TESTCFG;
+ volatile u8 DEBUG;
+ volatile u8 CHIPGCR;
+
+ volatile u16 WOLCRSet; /* 0xA0 */
+ volatile u8 PWCFGSet;
+ volatile u8 WOLCFGSet;
+
+ volatile u16 WOLCRClr; /* 0xA4 */
+ volatile u8 PWCFGCLR;
+ volatile u8 WOLCFGClr;
+
+ volatile u16 WOLSRSet; /* 0xA8 */
+ volatile u16 reserved_AA;
+
+ volatile u16 WOLSRClr; /* 0xAC */
+ volatile u16 reserved_AE;
+
+ volatile u16 PatternCRC[8]; /* 0xB0 */
+ volatile u32 ByteMask[4][4]; /* 0xC0 */
+} __attribute__ ((__packed__));
+
+
+enum hw_mib {
+ HW_MIB_ifRxAllPkts = 0,
+ HW_MIB_ifRxOkPkts,
+ HW_MIB_ifTxOkPkts,
+ HW_MIB_ifRxErrorPkts,
+ HW_MIB_ifRxRuntOkPkt,
+ HW_MIB_ifRxRuntErrPkt,
+ HW_MIB_ifRx64Pkts,
+ HW_MIB_ifTx64Pkts,
+ HW_MIB_ifRx65To127Pkts,
+ HW_MIB_ifTx65To127Pkts,
+ HW_MIB_ifRx128To255Pkts,
+ HW_MIB_ifTx128To255Pkts,
+ HW_MIB_ifRx256To511Pkts,
+ HW_MIB_ifTx256To511Pkts,
+ HW_MIB_ifRx512To1023Pkts,
+ HW_MIB_ifTx512To1023Pkts,
+ HW_MIB_ifRx1024To1518Pkts,
+ HW_MIB_ifTx1024To1518Pkts,
+ HW_MIB_ifTxEtherCollisions,
+ HW_MIB_ifRxPktCRCE,
+ HW_MIB_ifRxJumboPkts,
+ HW_MIB_ifTxJumboPkts,
+ HW_MIB_ifRxMacControlFrames,
+ HW_MIB_ifTxMacControlFrames,
+ HW_MIB_ifRxPktFAE,
+ HW_MIB_ifRxLongOkPkt,
+ HW_MIB_ifRxLongPktErrPkt,
+ HW_MIB_ifTXSQEErrors,
+ HW_MIB_ifRxNobuf,
+ HW_MIB_ifRxSymbolErrors,
+ HW_MIB_ifInRangeLengthErrors,
+ HW_MIB_ifLateCollisions,
+ HW_MIB_SIZE
+};
+
+enum chip_type {
+ CHIP_TYPE_VT6110 = 1,
+};
+
+struct velocity_info_tbl {
+ enum chip_type chip_id;
+ char *name;
+ int io_size;
+ int txqueue;
+ u32 flags;
+};
+
+#define mac_hw_mibs_init(regs) {\
+ BYTE_REG_BITS_ON(MIBCR_MIBFRZ,&((regs)->MIBCR));\
+ BYTE_REG_BITS_ON(MIBCR_MIBCLR,&((regs)->MIBCR));\
+ do {}\
+ while (BYTE_REG_BITS_IS_ON(MIBCR_MIBCLR,&((regs)->MIBCR)));\
+ BYTE_REG_BITS_OFF(MIBCR_MIBFRZ,&((regs)->MIBCR));\
+}
+
+#define mac_read_isr(regs) readl(&((regs)->ISR))
+#define mac_write_isr(regs, x) writel((x),&((regs)->ISR))
+#define mac_clear_isr(regs) writel(0xffffffffL,&((regs)->ISR))
+
+#define mac_write_int_mask(mask, regs) writel((mask),&((regs)->IMR));
+#define mac_disable_int(regs) writel(CR0_GINTMSK1,&((regs)->CR0Clr))
+#define mac_enable_int(regs) writel(CR0_GINTMSK1,&((regs)->CR0Set))
+
+#define mac_hw_mibs_read(regs, MIBs) {\
+ int i;\
+ BYTE_REG_BITS_ON(MIBCR_MPTRINI,&((regs)->MIBCR));\
+ for (i=0;i<HW_MIB_SIZE;i++) {\
+ (MIBs)[i]=readl(&((regs)->MIBData));\
+ }\
+}
+
+#define mac_set_dma_length(regs, n) {\
+ BYTE_REG_BITS_SET((n),0x07,&((regs)->DCFG));\
+}
+
+#define mac_set_rx_thresh(regs, n) {\
+ BYTE_REG_BITS_SET((n),(MCFG_RFT0|MCFG_RFT1),&((regs)->MCFG));\
+}
+
+#define mac_rx_queue_run(regs) {\
+ writeb(TRDCSR_RUN, &((regs)->RDCSRSet));\
+}
+
+#define mac_rx_queue_wake(regs) {\
+ writeb(TRDCSR_WAK, &((regs)->RDCSRSet));\
+}
+
+#define mac_tx_queue_run(regs, n) {\
+ writew(TRDCSR_RUN<<((n)*4),&((regs)->TDCSRSet));\
+}
+
+#define mac_tx_queue_wake(regs, n) {\
+ writew(TRDCSR_WAK<<(n*4),&((regs)->TDCSRSet));\
+}
+
+#define mac_eeprom_reload(regs) {\
+ int i=0;\
+ BYTE_REG_BITS_ON(EECSR_RELOAD,&((regs)->EECSR));\
+ do {\
+ udelay(10);\
+ if (i++>0x1000) {\
+ break;\
+ }\
+ }while (BYTE_REG_BITS_IS_ON(EECSR_RELOAD,&((regs)->EECSR)));\
+}
+
+enum velocity_cam_type {
+ VELOCITY_VLAN_ID_CAM = 0,
+ VELOCITY_MULTICAST_CAM
+};
+
+/**
+ * mac_get_cam_mask - Read a CAM mask
+ * @regs: register block for this velocity
+ * @mask: buffer to store mask
+ * @cam_type: CAM to fetch
+ *
+ * Fetch the mask bits of the selected CAM and store them into the
+ * provided mask buffer.
+ */
+
+static inline void mac_get_cam_mask(struct mac_regs * regs, u8 * mask, enum velocity_cam_type cam_type)
+{
+ int i;
+ /* Select CAM mask */
+ BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR);
+
+ if (cam_type == VELOCITY_VLAN_ID_CAM)
+ writeb(CAMADDR_VCAMSL, ®s->CAMADDR);
+ else
+ writeb(0, ®s->CAMADDR);
+
+ /* read mask */
+ for (i = 0; i < 8; i++)
+ *mask++ = readb(&(regs->MARCAM[i]));
+
+ /* disable CAMEN */
+ writeb(0, ®s->CAMADDR);
+
+ /* Select mar */
+ BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR);
+
+}
+
+/**
+ * mac_set_cam_mask - Set a CAM mask
+ * @regs: register block for this velocity
+ * @mask: CAM mask to load
+ * @cam_type: CAM to store
+ *
+ * Store a new mask into a CAM
+ */
+
+static inline void mac_set_cam_mask(struct mac_regs * regs, u8 * mask, enum velocity_cam_type cam_type)
+{
+ int i;
+ /* Select CAM mask */
+ BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR);
+
+ if (cam_type == VELOCITY_VLAN_ID_CAM)
+ writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL, ®s->CAMADDR);
+ else
+ writeb(CAMADDR_CAMEN, ®s->CAMADDR);
+
+ for (i = 0; i < 8; i++) {
+ writeb(*mask++, &(regs->MARCAM[i]));
+ }
+ /* disable CAMEN */
+ writeb(0, ®s->CAMADDR);
+
+ /* Select CAM mask */
+ BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR);
+}
+
+/**
+ * mac_set_cam - set CAM data
+ * @regs: register block of this velocity
+ * @idx: Cam index
+ * @addr: 2 or 6 bytes of CAM data
+ * @cam_type: CAM to load
+ *
+ * Load an address or vlan tag into a CAM
+ */
+
+static inline void mac_set_cam(struct mac_regs * regs, int idx, u8 *addr, enum velocity_cam_type cam_type)
+{
+ int i;
+
+ /* Select CAM mask */
+ BYTE_REG_BITS_SET(CAMCR_PS_CAM_DATA, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR);
+
+ idx &= (64 - 1);
+
+ if (cam_type == VELOCITY_VLAN_ID_CAM)
+ writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL | idx, ®s->CAMADDR);
+ else
+ writeb(CAMADDR_CAMEN | idx, ®s->CAMADDR);
+
+ if (cam_type == VELOCITY_VLAN_ID_CAM)
+ writew(*((u16 *) addr), ®s->MARCAM[0]);
+ else {
+ for (i = 0; i < 6; i++) {
+ writeb(*addr++, &(regs->MARCAM[i]));
+ }
+ }
+ BYTE_REG_BITS_ON(CAMCR_CAMWR, ®s->CAMCR);
+
+ udelay(10);
+
+ writeb(0, ®s->CAMADDR);
+
+ /* Select CAM mask */
+ BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR);
+}
+
+/**
+ * mac_get_cam - fetch CAM data
+ * @regs: register block of this velocity
+ * @idx: Cam index
+ * @addr: buffer to hold up to 6 bytes of CAM data
+ * @cam_type: CAM to load
+ *
+ * Load an address or vlan tag from a CAM into the buffer provided by
+ * the caller. VLAN tags are 2 bytes the address cam entries are 6.
+ */
+
+static inline void mac_get_cam(struct mac_regs * regs, int idx, u8 *addr, enum velocity_cam_type cam_type)
+{
+ int i;
+
+ /* Select CAM mask */
+ BYTE_REG_BITS_SET(CAMCR_PS_CAM_DATA, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR);
+
+ idx &= (64 - 1);
+
+ if (cam_type == VELOCITY_VLAN_ID_CAM)
+ writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL | idx, ®s->CAMADDR);
+ else
+ writeb(CAMADDR_CAMEN | idx, ®s->CAMADDR);
+
+ BYTE_REG_BITS_ON(CAMCR_CAMRD, ®s->CAMCR);
+
+ udelay(10);
+
+ if (cam_type == VELOCITY_VLAN_ID_CAM)
+ *((u16 *) addr) = readw(&(regs->MARCAM[0]));
+ else
+ for (i = 0; i < 6; i++, addr++)
+ *((u8 *) addr) = readb(&(regs->MARCAM[i]));
+
+ writeb(0, ®s->CAMADDR);
+
+ /* Select CAM mask */
+ BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, ®s->CAMCR);
+}
+
+/**
+ * mac_wol_reset - reset WOL after exiting low power
+ * @regs: register block of this velocity
+ *
+ * Called after we drop out of wake on lan mode in order to
+ * reset the Wake on lan features. This function doesn't restore
+ * the rest of the logic from the result of sleep/wakeup
+ */
+
+inline static void mac_wol_reset(struct mac_regs * regs)
+{
+
+ /* Turn off SWPTAG right after leaving power mode */
+ BYTE_REG_BITS_OFF(STICKHW_SWPTAG, ®s->STICKHW);
+ /* clear sticky bits */
+ BYTE_REG_BITS_OFF((STICKHW_DS1 | STICKHW_DS0), ®s->STICKHW);
+
+ BYTE_REG_BITS_OFF(CHIPGCR_FCGMII, ®s->CHIPGCR);
+ BYTE_REG_BITS_OFF(CHIPGCR_FCMODE, ®s->CHIPGCR);
+ /* disable force PME-enable */
+ writeb(WOLCFG_PMEOVR, ®s->WOLCFGClr);
+ /* disable power-event config bit */
+ writew(0xFFFF, ®s->WOLCRClr);
+ /* clear power status */
+ writew(0xFFFF, ®s->WOLSRClr);
+}
+
+
+/*
+ * Header for WOL definitions. Used to compute hashes
+ */
+
+typedef u8 MCAM_ADDR[ETH_ALEN];
+
+struct arp_packet {
+ u8 dest_mac[ETH_ALEN];
+ u8 src_mac[ETH_ALEN];
+ u16 type;
+ u16 ar_hrd;
+ u16 ar_pro;
+ u8 ar_hln;
+ u8 ar_pln;
+ u16 ar_op;
+ u8 ar_sha[ETH_ALEN];
+ u8 ar_sip[4];
+ u8 ar_tha[ETH_ALEN];
+ u8 ar_tip[4];
+} __attribute__ ((__packed__));
+
+struct _magic_packet {
+ u8 dest_mac[6];
+ u8 src_mac[6];
+ u16 type;
+ u8 MAC[16][6];
+ u8 password[6];
+} __attribute__ ((__packed__));
+
+/*
+ * Store for chip context when saving and restoring status. Not
+ * all fields are saved/restored currently.
+ */
+
+struct velocity_context {
+ u8 mac_reg[256];
+ MCAM_ADDR cam_addr[MCAM_SIZE];
+ u16 vcam[VCAM_SIZE];
+ u32 cammask[2];
+ u32 patcrc[2];
+ u32 pattern[8];
+};
+
+
+/*
+ * MII registers.
+ */
+
+
+/*
+ * Registers in the MII (offset unit is WORD)
+ */
+
+#define MII_REG_BMCR 0x00 // physical address
+#define MII_REG_BMSR 0x01 //
+#define MII_REG_PHYID1 0x02 // OUI
+#define MII_REG_PHYID2 0x03 // OUI + Module ID + REV ID
+#define MII_REG_ANAR 0x04 //
+#define MII_REG_ANLPAR 0x05 //
+#define MII_REG_G1000CR 0x09 //
+#define MII_REG_G1000SR 0x0A //
+#define MII_REG_MODCFG 0x10 //
+#define MII_REG_TCSR 0x16 //
+#define MII_REG_PLED 0x1B //
+// NS, MYSON only
+#define MII_REG_PCR 0x17 //
+// ESI only
+#define MII_REG_PCSR 0x17 //
+#define MII_REG_AUXCR 0x1C //
+
+// Marvell 88E1000/88E1000S
+#define MII_REG_PSCR 0x10 // PHY specific control register
+
+//
+// Bits in the BMCR register
+//
+#define BMCR_RESET 0x8000 //
+#define BMCR_LBK 0x4000 //
+#define BMCR_SPEED100 0x2000 //
+#define BMCR_AUTO 0x1000 //
+#define BMCR_PD 0x0800 //
+#define BMCR_ISO 0x0400 //
+#define BMCR_REAUTO 0x0200 //
+#define BMCR_FDX 0x0100 //
+#define BMCR_SPEED1G 0x0040 //
+//
+// Bits in the BMSR register
+//
+#define BMSR_AUTOCM 0x0020 //
+#define BMSR_LNK 0x0004 //
+
+//
+// Bits in the ANAR register
+//
+#define ANAR_ASMDIR 0x0800 // Asymmetric PAUSE support
+#define ANAR_PAUSE 0x0400 // Symmetric PAUSE Support
+#define ANAR_T4 0x0200 //
+#define ANAR_TXFD 0x0100 //
+#define ANAR_TX 0x0080 //
+#define ANAR_10FD 0x0040 //
+#define ANAR_10 0x0020 //
+//
+// Bits in the ANLPAR register
+//
+#define ANLPAR_ASMDIR 0x0800 // Asymmetric PAUSE support
+#define ANLPAR_PAUSE 0x0400 // Symmetric PAUSE Support
+#define ANLPAR_T4 0x0200 //
+#define ANLPAR_TXFD 0x0100 //
+#define ANLPAR_TX 0x0080 //
+#define ANLPAR_10FD 0x0040 //
+#define ANLPAR_10 0x0020 //
+
+//
+// Bits in the G1000CR register
+//
+#define G1000CR_1000FD 0x0200 // PHY is 1000-T Full-duplex capable
+#define G1000CR_1000 0x0100 // PHY is 1000-T Half-duplex capable
+
+//
+// Bits in the G1000SR register
+//
+#define G1000SR_1000FD 0x0800 // LP PHY is 1000-T Full-duplex capable
+#define G1000SR_1000 0x0400 // LP PHY is 1000-T Half-duplex capable
+
+#define TCSR_ECHODIS 0x2000 //
+#define AUXCR_MDPPS 0x0004 //
+
+// Bits in the PLED register
+#define PLED_LALBE 0x0004 //
+
+// Marvell 88E1000/88E1000S Bits in the PHY specific control register (10h)
+#define PSCR_ACRSTX 0x0800 // Assert CRS on Transmit
+
+#define PHYID_CICADA_CS8201 0x000FC410UL
+#define PHYID_VT3216_32BIT 0x000FC610UL
+#define PHYID_VT3216_64BIT 0x000FC600UL
+#define PHYID_MARVELL_1000 0x01410C50UL
+#define PHYID_MARVELL_1000S 0x01410C40UL
+
+#define PHYID_REV_ID_MASK 0x0000000FUL
+
+#define PHYID_GET_PHY_REV_ID(i) ((i) & PHYID_REV_ID_MASK)
+#define PHYID_GET_PHY_ID(i) ((i) & ~PHYID_REV_ID_MASK)
+
+#define MII_REG_BITS_ON(x,i,p) do {\
+ u16 w;\
+ velocity_mii_read((p),(i),&(w));\
+ (w)|=(x);\
+ velocity_mii_write((p),(i),(w));\
+} while (0)
+
+#define MII_REG_BITS_OFF(x,i,p) do {\
+ u16 w;\
+ velocity_mii_read((p),(i),&(w));\
+ (w)&=(~(x));\
+ velocity_mii_write((p),(i),(w));\
+} while (0)
+
+#define MII_REG_BITS_IS_ON(x,i,p) ({\
+ u16 w;\
+ velocity_mii_read((p),(i),&(w));\
+ ((int) ((w) & (x)));})
+
+#define MII_GET_PHY_ID(p) ({\
+ u32 id;\
+ velocity_mii_read((p),MII_REG_PHYID2,(u16 *) &id);\
+ velocity_mii_read((p),MII_REG_PHYID1,((u16 *) &id)+1);\
+ (id);})
+
+/*
+ * Inline debug routine
+ */
+
+
+enum velocity_msg_level {
+ MSG_LEVEL_ERR = 0, //Errors that will cause abnormal operation.
+ MSG_LEVEL_NOTICE = 1, //Some errors need users to be notified.
+ MSG_LEVEL_INFO = 2, //Normal message.
+ MSG_LEVEL_VERBOSE = 3, //Will report all trival errors.
+ MSG_LEVEL_DEBUG = 4 //Only for debug purpose.
+};
+
+#ifdef VELOCITY_DEBUG
+#define ASSERT(x) { \
+ if (!(x)) { \
+ printk(KERN_ERR "assertion %s failed: file %s line %d\n", #x,\
+ __FUNCTION__, __LINE__);\
+ BUG(); \
+ }\
+}
+#define VELOCITY_DBG(p,args...) printk(p, ##args)
+#else
+#define ASSERT(x)
+#define VELOCITY_DBG(x)
+#endif
+
+#define VELOCITY_PRT(l, p, args...) do {if (l<=msglevel) printk( p ,##args);} while (0)
+
+#define VELOCITY_PRT_CAMMASK(p,t) {\
+ int i;\
+ if ((t)==VELOCITY_MULTICAST_CAM) {\
+ for (i=0;i<(MCAM_SIZE/8);i++)\
+ printk("%02X",(p)->mCAMmask[i]);\
+ }\
+ else {\
+ for (i=0;i<(VCAM_SIZE/8);i++)\
+ printk("%02X",(p)->vCAMmask[i]);\
+ }\
+ printk("\n");\
+}
+
+
+
+#define VELOCITY_WOL_MAGIC 0x00000000UL
+#define VELOCITY_WOL_PHY 0x00000001UL
+#define VELOCITY_WOL_ARP 0x00000002UL
+#define VELOCITY_WOL_UCAST 0x00000004UL
+#define VELOCITY_WOL_BCAST 0x00000010UL
+#define VELOCITY_WOL_MCAST 0x00000020UL
+#define VELOCITY_WOL_MAGIC_SEC 0x00000040UL
+
+/*
+ * Flags for options
+ */
+
+#define VELOCITY_FLAGS_TAGGING 0x00000001UL
+#define VELOCITY_FLAGS_TX_CSUM 0x00000002UL
+#define VELOCITY_FLAGS_RX_CSUM 0x00000004UL
+#define VELOCITY_FLAGS_IP_ALIGN 0x00000008UL
+#define VELOCITY_FLAGS_VAL_PKT_LEN 0x00000010UL
+
+#define VELOCITY_FLAGS_FLOW_CTRL 0x01000000UL
+
+/*
+ * Flags for driver status
+ */
+
+#define VELOCITY_FLAGS_OPENED 0x00010000UL
+#define VELOCITY_FLAGS_VMNS_CONNECTED 0x00020000UL
+#define VELOCITY_FLAGS_VMNS_COMMITTED 0x00040000UL
+#define VELOCITY_FLAGS_WOL_ENABLED 0x00080000UL
+
+/*
+ * Flags for MII status
+ */
+
+#define VELOCITY_LINK_FAIL 0x00000001UL
+#define VELOCITY_SPEED_10 0x00000002UL
+#define VELOCITY_SPEED_100 0x00000004UL
+#define VELOCITY_SPEED_1000 0x00000008UL
+#define VELOCITY_DUPLEX_FULL 0x00000010UL
+#define VELOCITY_AUTONEG_ENABLE 0x00000020UL
+#define VELOCITY_FORCED_BY_EEPROM 0x00000040UL
+
+/*
+ * For velocity_set_media_duplex
+ */
+
+#define VELOCITY_LINK_CHANGE 0x00000001UL
+
+enum speed_opt {
+ SPD_DPX_AUTO = 0,
+ SPD_DPX_100_HALF = 1,
+ SPD_DPX_100_FULL = 2,
+ SPD_DPX_10_HALF = 3,
+ SPD_DPX_10_FULL = 4
+};
+
+enum velocity_init_type {
+ VELOCITY_INIT_COLD = 0,
+ VELOCITY_INIT_RESET,
+ VELOCITY_INIT_WOL
+};
+
+enum velocity_flow_cntl_type {
+ FLOW_CNTL_DEFAULT = 1,
+ FLOW_CNTL_TX,
+ FLOW_CNTL_RX,
+ FLOW_CNTL_TX_RX,
+ FLOW_CNTL_DISABLE,
+};
+
+struct velocity_opt {
+ int numrx; /* Number of RX descriptors */
+ int numtx; /* Number of TX descriptors */
+ enum speed_opt spd_dpx; /* Media link mode */
+ int vid; /* vlan id */
+ int DMA_length; /* DMA length */
+ int rx_thresh; /* RX_THRESH */
+ int flow_cntl;
+ int wol_opts; /* Wake on lan options */
+ int td_int_count;
+ int int_works;
+ int rx_bandwidth_hi;
+ int rx_bandwidth_lo;
+ int rx_bandwidth_en;
+ u32 flags;
+};
+
+struct velocity_info {
+ struct velocity_info *next;
+ struct velocity_info *prev;
+
+ struct pci_dev *pdev;
+ struct net_device *dev;
+ struct net_device_stats stats;
+
+#if CONFIG_PM
+ u32 pci_state[16];
+#endif
+
+ dma_addr_t rd_pool_dma;
+ dma_addr_t td_pool_dma[TX_QUEUE_NO];
+
+ dma_addr_t tx_bufs_dma;
+ u8 *tx_bufs;
+
+ u8 ip_addr[4];
+ enum chip_type chip_id;
+
+ struct mac_regs * mac_regs;
+ unsigned long memaddr;
+ unsigned long ioaddr;
+ u32 io_size;
+
+ u8 rev_id;
+
+#define AVAIL_TD(p,q) ((p)->options.numtx-((p)->td_used[(q)]))
+
+ int num_txq;
+
+ volatile int td_used[TX_QUEUE_NO];
+ int td_curr[TX_QUEUE_NO];
+ int td_tail[TX_QUEUE_NO];
+ struct tx_desc *td_rings[TX_QUEUE_NO];
+ struct velocity_td_info *td_infos[TX_QUEUE_NO];
+
+ int rd_curr;
+ int rd_used;
+ struct rx_desc *rd_ring;
+ struct velocity_rd_info *rd_info; /* It's an array */
+
+#define GET_RD_BY_IDX(vptr, idx) (vptr->rd_ring[idx])
+ u32 mib_counter[MAX_HW_MIB_COUNTER];
+ struct velocity_opt options;
+
+ u32 int_mask;
+
+ u32 flags;
+
+ int rx_buf_sz;
+ u32 mii_status;
+ u32 phy_id;
+ int multicast_limit;
+
+ u8 vCAMmask[(VCAM_SIZE / 8)];
+ u8 mCAMmask[(MCAM_SIZE / 8)];
+
+ spinlock_t lock;
+ spinlock_t xmit_lock;
+
+ int wol_opts;
+ u8 wol_passwd[6];
+
+ struct velocity_context context;
+
+ u32 ticks;
+ u32 rx_bytes;
+
+};
+
+/**
+ * velocity_get_ip - find an IP address for the device
+ * @vptr: Velocity to query
+ *
+ * Dig out an IP address for this interface so that we can
+ * configure wakeup with WOL for ARP. If there are multiple IP
+ * addresses on this chain then we use the first - multi-IP WOL is not
+ * supported.
+ *
+ * CHECK ME: locking
+ */
+
+inline static int velocity_get_ip(struct velocity_info *vptr)
+{
+ struct in_device *in_dev = (struct in_device *) vptr->dev->ip_ptr;
+ struct in_ifaddr *ifa;
+
+ if (in_dev != NULL) {
+ ifa = (struct in_ifaddr *) in_dev->ifa_list;
+ if (ifa != NULL) {
+ memcpy(vptr->ip_addr, &ifa->ifa_address, 4);
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+/**
+ * velocity_update_hw_mibs - fetch MIB counters from chip
+ * @vptr: velocity to update
+ *
+ * The velocity hardware keeps certain counters in the hardware
+ * side. We need to read these when the user asks for statistics
+ * or when they overflow (causing an interrupt). The read of the
+ * statistic clears it, so we keep running master counters in user
+ * space.
+ */
+
+static inline void velocity_update_hw_mibs(struct velocity_info *vptr)
+{
+ u32 tmp;
+ int i;
+ BYTE_REG_BITS_ON(MIBCR_MIBFLSH, &(vptr->mac_regs->MIBCR));
+
+ while (BYTE_REG_BITS_IS_ON(MIBCR_MIBFLSH, &(vptr->mac_regs->MIBCR)));
+
+ BYTE_REG_BITS_ON(MIBCR_MPTRINI, &(vptr->mac_regs->MIBCR));
+ for (i = 0; i < HW_MIB_SIZE; i++) {
+ tmp = readl(&(vptr->mac_regs->MIBData)) & 0x00FFFFFFUL;
+ vptr->mib_counter[i] += tmp;
+ }
+}
+
+/**
+ * init_flow_control_register - set up flow control
+ * @vptr: velocity to configure
+ *
+ * Configure the flow control registers for this velocity device.
+ */
+
+static inline void init_flow_control_register(struct velocity_info *vptr)
+{
+ struct mac_regs * regs = vptr->mac_regs;
+
+ /* Set {XHITH1, XHITH0, XLTH1, XLTH0} in FlowCR1 to {1, 0, 1, 1}
+ depend on RD=64, and Turn on XNOEN in FlowCR1 */
+ writel((CR0_XONEN | CR0_XHITH1 | CR0_XLTH1 | CR0_XLTH0), ®s->CR0Set);
+ writel((CR0_FDXTFCEN | CR0_FDXRFCEN | CR0_HDXFCEN | CR0_XHITH0), ®s->CR0Clr);
+
+ /* Set TxPauseTimer to 0xFFFF */
+ writew(0xFFFF, ®s->tx_pause_timer);
+
+ /* Initialize RBRDU to Rx buffer count. */
+ writew(vptr->options.numrx, ®s->RBRDU);
+}
+
+
+#endif
config ARLAN
tristate "Aironet Arlan 655 & IC2200 DS support"
- depends on NET_RADIO && ISA
+ depends on NET_RADIO && ISA && !64BIT
---help---
Aironet makes Arlan, a class of wireless LAN adapters. These use the
www.Telxon.com chip, which is also used on several similar cards.
config AIRO
tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards"
- depends on NET_RADIO && (ISA || PCI)
+ depends on NET_RADIO && ISA && (PCI || BROKEN)
---help---
This is the standard Linux driver to support Cisco/Aironet ISA and
PCI 802.11 wireless cards.
if (card->vaddr)
iounmap(card->vaddr);
- card->vaddr = 0;
+ card->vaddr = NULL;
macio_release_resource(mdev, 0);
extern int arlan_entry_debug;
extern int arlan_exit_debug;
extern int testMemory;
-extern const char* arlan_version;
extern int arlan_command(struct net_device * dev, int command);
#define SIDUNKNOWN -1
--- /dev/null
+/*
+ * (C) 2004 Margit Schubert-While <margitsw@t-online.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * 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
+ *
+ */
+
+/*
+ * Compatibility header file to aid support of different kernel versions
+ */
+
+#ifdef PRISM54_COMPAT24
+#include "prismcompat24.h"
+#else /* PRISM54_COMPAT24 */
+
+#ifndef _PRISM_COMPAT_H
+#define _PRISM_COMPAT_H
+
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/config.h>
+#include <linux/moduleparam.h>
+#include <linux/workqueue.h>
+#include <linux/compiler.h>
+
+#if !defined(CONFIG_FW_LOADER) && !defined(CONFIG_FW_LOADER_MODULE)
+#error Firmware Loading is not configured in the kernel !
+#endif
+
+#define prism54_synchronize_irq(irq) synchronize_irq(irq)
+
+#define PRISM_FW_PDEV &priv->pdev->dev
+
+#endif /* _PRISM_COMPAT_H */
+#endif /* PRISM54_COMPAT24 */
struct dino_device *dino_dev; // Dino specific control struct
const char *version = "unknown";
const int name_len = 32;
+ char hw_path[64];
char *name;
int is_cujo = 0;
struct pci_bus *bus;
-
+
name = kmalloc(name_len, GFP_KERNEL);
- if(name)
- snprintf(name, name_len, "Dino %s", dev->dev.bus_id);
+ if(name) {
+ print_pa_hwpath(dev, hw_path);
+ snprintf(name, name_len, "Dino [%s]", hw_path);
+ }
else
name = "Dino";
sg_dma_len(startsg) = 0;
dma_offset = (unsigned long) pide & ~IOVP_MASK;
n_mappings++;
+#if defined(ZX1_SUPPORT)
+ /* Pluto IOMMU IO Virt Address is not zero based */
+ sg_dma_address(dma_sg) = pide | ioc->ibase;
+#else
+ /* SBA, ccio, and dino are zero based.
+ * Trying to save a few CPU cycles for most users.
+ */
sg_dma_address(dma_sg) = pide;
+#endif
pdirp = &(ioc->pdir_base[pide >> IOVP_SHIFT]);
prefetchw(pdirp);
}
#include <asm/byteorder.h> /* get in-line asm for swab */
#include <asm/pdc.h>
+#include <asm/pdcpat.h>
#include <asm/page.h>
#include <asm/segment.h>
#include <asm/system.h>
-#include <asm/io.h> /* gsc_read/write functions */
+#include <asm/io.h> /* read/write functions */
#ifdef CONFIG_SUPERIO
#include <asm/superio.h>
#endif
#endif
-#define READ_U8(addr) gsc_readb(addr)
-#define READ_U16(addr) le16_to_cpu(gsc_readw((u16 *) (addr)))
-#define READ_U32(addr) le32_to_cpu(gsc_readl((u32 *) (addr)))
-#define READ_REG16(addr) gsc_readw((u16 *) (addr))
-#define READ_REG32(addr) gsc_readl((u32 *) (addr))
-#define WRITE_U8(value, addr) gsc_writeb(value, addr)
-#define WRITE_U16(value, addr) gsc_writew(cpu_to_le16(value), (u16 *) (addr))
-#define WRITE_U32(value, addr) gsc_writel(cpu_to_le32(value), (u32 *) (addr))
-#define WRITE_REG16(value, addr) gsc_writew(value, (u16 *) (addr))
-#define WRITE_REG32(value, addr) gsc_writel(value, (u32 *) (addr))
-
-
-#define IOSAPIC_REG_SELECT 0
+#define IOSAPIC_REG_SELECT 0x00
#define IOSAPIC_REG_WINDOW 0x10
#define IOSAPIC_REG_EOI 0x40
#define IOSAPIC_IRDT_ENTRY(idx) (0x10+(idx)*2)
#define IOSAPIC_IRDT_ENTRY_HI(idx) (0x11+(idx)*2)
+static inline unsigned int iosapic_read(unsigned long iosapic, unsigned int reg)
+{
+ writel(reg, iosapic + IOSAPIC_REG_SELECT);
+ return readl(iosapic + IOSAPIC_REG_WINDOW);
+}
+
+static inline void iosapic_write(unsigned long iosapic, unsigned int reg, u32 val)
+{
+ writel(reg, iosapic + IOSAPIC_REG_SELECT);
+ writel(val, iosapic + IOSAPIC_REG_WINDOW);
+}
+
/*
-** FIXME: revisit which GFP flags we should really be using.
** GFP_KERNEL includes __GFP_WAIT flag and that may not
** be acceptable. Since this is boot time, we shouldn't have
** to wait ever and this code should (will?) never get called
#define IOSAPIC_UNLOCK(lck) spin_unlock_irqrestore(lck, irqflags)
-#define IOSAPIC_VERSION_MASK 0x000000ff
-#define IOSAPIC_VERSION_SHIFT 0x0
-#define IOSAPIC_VERSION(ver) \
- (int) ((ver & IOSAPIC_VERSION_MASK) >> IOSAPIC_VERSION_SHIFT)
+#define IOSAPIC_VERSION_MASK 0x000000ff
+#define IOSAPIC_VERSION(ver) ((int) (ver & IOSAPIC_VERSION_MASK))
#define IOSAPIC_MAX_ENTRY_MASK 0x00ff0000
-
#define IOSAPIC_MAX_ENTRY_SHIFT 0x10
-#define IOSAPIC_IRDT_MAX_ENTRY(ver) \
- (int) ((ver&IOSAPIC_MAX_ENTRY_MASK) >> IOSAPIC_MAX_ENTRY_SHIFT)
+#define IOSAPIC_IRDT_MAX_ENTRY(ver) \
+ (int) (((ver) & IOSAPIC_MAX_ENTRY_MASK) >> IOSAPIC_MAX_ENTRY_SHIFT)
/* bits in the "low" I/O Sapic IRdT entry */
#define IOSAPIC_IRDT_ENABLE 0x10000
#define IOSAPIC_IRDT_ID_EID_SHIFT 0x10
-
-#define IOSAPIC_EOI(eoi_addr, eoi_data) gsc_writel(eoi_data, eoi_addr)
-
static struct iosapic_info *iosapic_list;
static spinlock_t iosapic_lock;
static int iosapic_count;
struct irt_entry *p = table;
int i;
- printk(KERN_DEBUG MODULE_NAME " Interrupt Routing Table (cell %ld)\n", cell_num);
- printk(KERN_DEBUG MODULE_NAME " start = 0x%p num_entries %ld entry_size %d\n",
+ printk(MODULE_NAME " Interrupt Routing Table (cell %ld)\n", cell_num);
+ printk(MODULE_NAME " start = 0x%p num_entries %ld entry_size %d\n",
table,
num_entries,
(int) sizeof(struct irt_entry));
for (i = 0 ; i < num_entries ; i++, p++) {
- printk(KERN_DEBUG MODULE_NAME " %02x %02x %02x %02x %02x %02x %02x %02x %08x%08x\n",
+ printk(MODULE_NAME " %02x %02x %02x %02x %02x %02x %02x %02x %08x%08x\n",
p->entry_type, p->entry_length, p->interrupt_type,
p->polarity_trigger, p->src_bus_irq_devno, p->src_bus_id,
p->src_seg_id, p->dest_iosapic_intin,
static irqreturn_t
iosapic_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
- struct vector_info *vi = (struct vector_info *)dev_id;
+ struct vector_info *vi = (struct vector_info *) dev_id;
extern void do_irq(struct irqaction *a, int i, struct pt_regs *p);
int irq_num = vi->iosapic->isi_region->data.irqbase + vi->irqline;
- DBG("iosapic_interrupt(): irq %d line %d eoi %p\n",
- irq, vi->irqline, vi->eoi_addr);
+ DBG("iosapic_interrupt(): irq %d line %d eoi 0x%p 0x%x\n",
+ irq, vi->irqline, vi->eoi_addr, vi->eoi_data);
+
+ /* Do NOT need to mask/unmask IRQ. processor is already masked. */
-/* FIXME: Need to mask/unmask? processor IRQ is already masked... */
do_irq(&vi->iosapic->isi_region->action[vi->irqline], irq_num, regs);
/*
+ ** PARISC only supports PCI devices below I/O SAPIC.
** PCI only supports level triggered in order to share IRQ lines.
- ** I/O SAPIC must always issue EOI.
+ ** ergo I/O SAPIC must always issue EOI on parisc.
+ **
+ ** i386/ia64 support ISA devices and have to deal with
+ ** edge-triggered interrupts too.
*/
- IOSAPIC_EOI(vi->eoi_addr, vi->eoi_data);
-
+ __raw_writel(vi->eoi_data, vi->eoi_addr);
return IRQ_HANDLED;
}
ASSERT(tmp == 0);
vi->eoi_addr = (u32 *) (isi->isi_hpa + IOSAPIC_REG_EOI);
- vi->eoi_data = cpu_to_le32(vi->irqline);
-
+ vi->eoi_data = cpu_to_le32(vi->txn_data);
ASSERT(NULL != isi->isi_region);
DBG_IRT("iosapic_fixup_irq() %d:%d %x %x line %d irq %d\n",
struct iosapic_info *isp = vi->iosapic;
u8 idx = vi->irqline;
- /* point the window register to the lower word */
- WRITE_U32(IOSAPIC_IRDT_ENTRY(idx), isp->isi_hpa+IOSAPIC_REG_SELECT);
- *dp0 = READ_U32(isp->isi_hpa+IOSAPIC_REG_WINDOW);
-
- /* point the window register to the higher word */
- WRITE_U32(IOSAPIC_IRDT_ENTRY_HI(idx), isp->isi_hpa+IOSAPIC_REG_SELECT);
- *dp1 = READ_U32(isp->isi_hpa+IOSAPIC_REG_WINDOW);
+ *dp0 = iosapic_read(isp->isi_hpa, IOSAPIC_IRDT_ENTRY(idx));
+ *dp1 = iosapic_read(isp->isi_hpa, IOSAPIC_IRDT_ENTRY_HI(idx));
}
ASSERT(NULL != isp);
ASSERT(0 != isp->isi_hpa);
- DBG_IRT("iosapic_wr_irt_entry(): irq %d hpa %p WINDOW %p 0x%x 0x%x\n",
+ DBG_IRT("iosapic_wr_irt_entry(): irq %d hpa %p 0x%x 0x%x\n",
vi->irqline,
- isp->isi_hpa, isp->isi_hpa+IOSAPIC_REG_WINDOW,
+ isp->isi_hpa,
dp0, dp1);
- /* point the window register to the lower word */
- WRITE_U32(IOSAPIC_IRDT_ENTRY(vi->irqline), isp->isi_hpa+IOSAPIC_REG_SELECT);
- WRITE_U32( dp0, isp->isi_hpa+IOSAPIC_REG_WINDOW);
+ iosapic_write(isp->isi_hpa, IOSAPIC_IRDT_ENTRY(vi->irqline), dp0);
/* Read the window register to flush the writes down to HW */
- dp0 = READ_U32(isp->isi_hpa+IOSAPIC_REG_WINDOW);
+ dp0 = readl(isp->isi_hpa+IOSAPIC_REG_WINDOW);
- /* point the window register to the higher word */
- WRITE_U32(IOSAPIC_IRDT_ENTRY_HI(vi->irqline), isp->isi_hpa+IOSAPIC_REG_SELECT);
- WRITE_U32( dp1, isp->isi_hpa+IOSAPIC_REG_WINDOW);
+ iosapic_write(isp->isi_hpa, IOSAPIC_IRDT_ENTRY_HI(vi->irqline), dp1);
/* Read the window register to flush the writes down to HW */
- dp1 = READ_U32(isp->isi_hpa+IOSAPIC_REG_WINDOW);
+ dp1 = readl(isp->isi_hpa+IOSAPIC_REG_WINDOW);
}
iosapic_set_irt_data(vi, &d0, &d1);
iosapic_wr_irt_entry(vi, d0, d1);
-
#ifdef DEBUG_IOSAPIC_IRT
{
u32 *t = (u32 *) ((ulong) vi->eoi_addr & ~0xffUL);
printk("iosapic_enable_irq(): regs %p", vi->eoi_addr);
- while (t < vi->eoi_addr) printk(" %x", READ_U32(t++));
+ for ( ; t < vi->eoi_addr; t++)
+ printk(" %x", readl(t));
printk("\n");
}
struct iosapic_info *isp = vi->iosapic;
for (d0=0x10; d0<0x1e; d0++) {
- /* point the window register to the lower word */
- WRITE_U32(d0, isp->isi_hpa+IOSAPIC_REG_SELECT);
-
- /* read the word */
- d1 = READ_U32(isp->isi_hpa+IOSAPIC_REG_WINDOW);
+ d1 = iosapic_read(isp->isi_hpa, d0);
printk(" %x", d1);
}
}
#endif
/*
- ** KLUGE: IRQ should not be asserted when Drivers enabling their IRQ.
- ** PCI supports level triggered in order to share IRQ lines.
- **
- ** Issueing I/O SAPIC an EOI causes an interrupt iff IRQ line is
- ** asserted.
+ ** Issueing I/O SAPIC an EOI causes an interrupt IFF IRQ line is
+ ** asserted. IRQ generally should not be asserted when a driver
+ ** enables their IRQ. It can lead to "interesting" race conditions
+ ** in the driver initialization sequence.
*/
- IOSAPIC_EOI(vi->eoi_addr, vi->eoi_data);
+ __raw_writel(vi->eoi_data, vi->eoi_addr);
}
ASSERT(isi);
ASSERT(isi->isi_hpa);
- /* point window to the version register */
- WRITE_U32(IOSAPIC_REG_VERSION, isi->isi_hpa+IOSAPIC_REG_SELECT);
-
- /* now read the version register */
- return (READ_U32(isi->isi_hpa+IOSAPIC_REG_WINDOW));
+ return iosapic_read(isi->isi_hpa, IOSAPIC_REG_VERSION);
}
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/init.h> /* for __init and __devinit */
-/* #define PCI_DEBUG enable ASSERT */
#include <linux/pci.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <asm/byteorder.h>
#include <asm/irq.h> /* for struct irq_region support */
#include <asm/pdc.h>
+#include <asm/pdcpat.h>
#include <asm/page.h>
#include <asm/segment.h>
#include <asm/system.h>
#define DBG_PAT(x...)
#endif
+#ifdef DEBUG_LBA
+#undef ASSERT
+#define ASSERT(expr) \
+ if(!(expr)) { \
+ printk("\n%s:%d: Assertion " #expr " failed!\n", \
+ __FILE__, __LINE__); \
+ panic(#expr); \
+ }
+#else
+#define ASSERT(expr)
+#endif
+
+
/*
** Config accessor functions only pass in the 8-bit bus number and not
** the 8-bit "PCI Segment" number. Each LBA will be assigned a PCI bus
#define LBA_HINT_CFG 0x0310
#define LBA_HINT_BASE 0x0380 /* 14 registers at every 8 bytes. */
+#define LBA_BUS_MODE 0x0620
+
/* ERROR regs are needed for config cycle kluges */
#define LBA_ERROR_CONFIG 0x0680
#define LBA_SMART_MODE 0x20
#define LBA_IOSAPIC_BASE 0x800 /* Offset of IRQ logic */
/* non-postable I/O port space, densely packed */
-#ifdef __LP64__
+#ifdef CONFIG_PARISC64
#define LBA_ASTRO_PORT_BASE (0xfffffffffee00000UL)
#else
#define LBA_ASTRO_PORT_BASE (0xfee00000UL)
#endif
+#define ELROY_HVERS 0x782
+#define MERCURY_HVERS 0x783
+#define QUICKSILVER_HVERS 0x784
+
+static inline int IS_ELROY(struct parisc_device *d)
+{
+ return (d->id.hversion == ELROY_HVERS);
+}
+
+static inline int IS_MERCURY(struct parisc_device *d)
+{
+ return (d->id.hversion == MERCURY_HVERS);
+}
+
+static inline int IS_QUICKSILVER(struct parisc_device *d)
+{
+ return (d->id.hversion == QUICKSILVER_HVERS);
+}
+
/*
** lba_device: Per instance Elroy data structure
spinlock_t lba_lock;
void *iosapic_obj;
-#ifdef __LP64__
+#ifdef CONFIG_PARISC64
unsigned long iop_base; /* PA_VIEW - for IO port accessor funcs */
#endif
{
u8 first_bus = d->hba.hba_bus->secondary;
u8 last_sub_bus = d->hba.hba_bus->subordinate;
-#if 0
-/* FIXME - see below in this function */
- u8 dev = PCI_SLOT(dfn);
- u8 func = PCI_FUNC(dfn);
-#endif
ASSERT(bus >= first_bus);
ASSERT(bus <= last_sub_bus);
return(FALSE);
}
-#if 0
-/*
-** FIXME: Need to implement code to fill the devices bitmap based
-** on contents of the local pci_bus tree "data base".
-** pci_register_ops() walks the bus for us and builds the tree.
-** For now, always do the config cycle.
-*/
- bus -= first_bus;
-
- return (((d->devices[bus][dev]) >> func) & 0x1);
-#else
return TRUE;
-#endif
}
return(data);
}
+#ifdef CONFIG_PARISC64
+#define pat_cfg_addr(bus, devfn, addr) (((bus) << 16) | ((devfn) << 8) | (addr))
+
+static int pat_cfg_read(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 *data)
+{
+ int tok = pat_cfg_addr(bus->number, devfn, pos);
+ u32 tmp;
+ int ret = pdc_pat_io_pci_cfg_read(tok, size, &tmp);
+
+ DBG_CFG("%s(%d:%d.%d+0x%02x) -> 0x%x %d\n", __FUNCTION__, bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), pos, tmp, ret);
+
+ switch (size) {
+ case 1: *data = (u8) tmp; return (tmp == (u8) ~0);
+ case 2: *data = (u16) tmp; return (tmp == (u16) ~0);
+ case 4: *data = (u32) tmp; return (tmp == (u32) ~0);
+ }
+ *data = ~0;
+ return (ret);
+}
+
+static int pat_cfg_write(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 data)
+{
+ int tok = pat_cfg_addr(bus->number, devfn, pos);
+ int ret = pdc_pat_io_pci_cfg_write(tok, size, data);
+
+ DBG_CFG("%s(%d:%d.%d+0x%02x, 0x%lx/%d)\n", __FUNCTION__, bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), pos, data, size);
+ return (ret);
+}
+
+static struct pci_ops pat_cfg_ops = {
+ .read = pat_cfg_read,
+ .write = pat_cfg_write,
+};
+#else
+/* keep the compiler from complaining about undeclared variables */
+#define pat_cfg_ops lba_cfg_ops
+#endif
static int lba_cfg_read(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 *data)
{
}
DBG_CFG("%s(%x+%2x) = 0x%x (c)\n", __FUNCTION__, tok, pos, data);
+
/* Basic Algorithm */
LBA_CFG_TR4_ADDR_SETUP(d, tok | pos);
switch(size) {
}
-#ifdef __LP64__
+#ifdef CONFIG_PARISC64
/*
** Determine if a device is already configured.
}
}
}
+#else
+#define lba_claim_dev_resources(dev)
#endif
lba_dump_res(&iomem_resource, 2);
}
-#ifdef __LP64__
+#ifdef CONFIG_PARISC64
if (ldev->hba.gmmio_space.flags) {
err = request_resource(&iomem_resource, &(ldev->hba.gmmio_space));
if (err < 0) {
bus->bridge_ctl &= ~(status & PCI_STATUS_FAST_BACK);
#endif
-#ifdef __LP64__
if (is_pdc_pat()) {
/* Claim resources for PDC's devices */
lba_claim_dev_resources(dev);
}
-#endif
/*
** P2PB's have no IRQs. ignore them.
};
-#ifdef __LP64__
+#ifdef CONFIG_PARISC64
#define PIOP_TO_GMMIO(lba, addr) \
((lba)->iop_base + (((addr)&0xFFFC)<<10) + ((addr)&3))
}
}
}
-#endif /* __LP64__ */
+#else
+/* keep compiler from complaining about missing declarations */
+#define lba_pat_port_ops lba_astro_port_ops
+#define lba_pat_resources(pa_dev, lba_dev)
+#endif /* CONFIG_PARISC64 */
static void
unsigned long rsize;
int lba_num;
-#ifdef __LP64__
+#ifdef CONFIG_PARISC64
/*
** Sign extend all BAR values on "legacy" platforms.
** "Sprockets" PDC (Forte/Allegro) initializes everything
printk("\n");
#endif /* DEBUG_LBA_PAT */
-#ifdef __LP64__
+#ifdef CONFIG_PARISC64
/*
* FIXME add support for PDC_PAT_IO "Get slot status" - OLAR support
* Only N-Class and up can really make use of Get slot status.
** have work to do.
*/
static int __init
-lba_driver_callback(struct parisc_device *dev)
+lba_driver_probe(struct parisc_device *dev)
{
struct lba_device *lba_dev;
struct pci_bus *lba_bus;
/* Read HW Rev First */
func_class = READ_REG32(dev->hpa + LBA_FCLASS);
- func_class &= 0xf;
-
- switch (func_class) {
- case 0: version = "TR1.0"; break;
- case 1: version = "TR2.0"; break;
- case 2: version = "TR2.1"; break;
- case 3: version = "TR2.2"; break;
- case 4: version = "TR3.0"; break;
- case 5: version = "TR4.0"; break;
- default: version = "TR4+";
- }
- printk(KERN_INFO "%s version %s (0x%x) found at 0x%lx\n",
- MODULE_NAME, version, func_class & 0xf, dev->hpa);
+ if (IS_ELROY(dev)) {
+ func_class &= 0xf;
+ switch (func_class) {
+ case 0: version = "TR1.0"; break;
+ case 1: version = "TR2.0"; break;
+ case 2: version = "TR2.1"; break;
+ case 3: version = "TR2.2"; break;
+ case 4: version = "TR3.0"; break;
+ case 5: version = "TR4.0"; break;
+ default: version = "TR4+";
+ }
+ printk(KERN_INFO "%s version %s (0x%x) found at 0x%lx\n",
+ MODULE_NAME, version, func_class & 0xf, dev->hpa);
+
+ /* Just in case we find some prototypes... */
+ } else if (IS_MERCURY(dev) || IS_QUICKSILVER(dev)) {
+ func_class &= 0xff;
+ version = kmalloc(6, GFP_KERNEL);
+ sprintf(version,"TR%d.%d",(func_class >> 4),(func_class & 0xf));
+ /* We could use one printk for both and have it outside,
+ * but for the mask for func_class.
+ */
+ printk(KERN_INFO "%s version %s (0x%x) found at 0x%lx\n",
+ MODULE_NAME, version, func_class & 0xff, dev->hpa);
+ }
- /* Just in case we find some prototypes... */
if (func_class < 2) {
- printk(KERN_WARNING "Can't support LBA older than TR2.1 "
- "- continuing under adversity.\n");
+ printk(KERN_WARNING "Can't support LBA older than TR2.1"
+ " - continuing under adversity.\n");
}
/*
/* ---------- Third : setup I/O Port and MMIO resources --------- */
-#ifdef __LP64__
if (is_pdc_pat()) {
/* PDC PAT firmware uses PIOP region of GMMIO space. */
pci_port = &lba_pat_port_ops;
-
/* Go ask PDC PAT what resources this LBA has */
lba_pat_resources(dev, lba_dev);
- } else
-#endif
- {
+ } else {
/* Sprockets PDC uses NPIOP region */
pci_port = &lba_astro_port_ops;
dev->dev.platform_data = lba_dev;
lba_bus = lba_dev->hba.hba_bus =
pci_scan_bus_parented(&dev->dev, lba_dev->hba.bus_num.start,
- &lba_cfg_ops, NULL);
+ is_pdc_pat() ? &pat_cfg_ops : &lba_cfg_ops,
+ NULL);
-#ifdef __LP64__
if (is_pdc_pat()) {
/* assign resources to un-initialized devices */
DBG_PAT("LBA pci_bus_assign_resources()\n");
lba_dump_res(&lba_dev->hba.lmmio_space, 2);
#endif
}
-#endif
/*
** Once PCI register ops has walked the bus, access to config
}
static struct parisc_device_id lba_tbl[] = {
- { HPHW_BRIDGE, HVERSION_REV_ANY_ID, 0x782, 0xa },
+ { HPHW_BRIDGE, HVERSION_REV_ANY_ID, ELROY_HVERS, 0xa },
+ { HPHW_BRIDGE, HVERSION_REV_ANY_ID, MERCURY_HVERS, 0xa },
+ { HPHW_BRIDGE, HVERSION_REV_ANY_ID, QUICKSILVER_HVERS, 0xa },
{ 0, }
};
static struct parisc_driver lba_driver = {
.name = MODULE_NAME,
.id_table = lba_tbl,
- .probe = lba_driver_callback,
+ .probe = lba_driver_probe,
};
/*
static int led_proc_write(struct file *file, const char *buf,
unsigned long count, void *data)
{
- char *cur, lbuf[count];
+ char *cur, lbuf[count + 1];
int d;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
- memset(lbuf, 0, count);
+ memset(lbuf, 0, count + 1);
if (copy_from_user(lbuf, buf, count))
return -EFAULT;
break;
case LED_HASLCD:
- while (*cur && cur[strlen(cur)-1] == '\n')
+ if (*cur && cur[strlen(cur)-1] == '\n')
cur[strlen(cur)-1] = 0;
if (*cur == 0)
cur = lcd_text_default;
#include <linux/mm.h>
#include <linux/string.h>
-#undef PCI_DEBUG /* for ASSERT */
#include <linux/pci.h>
-#undef PCI_DEBUG
#include <asm/byteorder.h>
#include <asm/io.h>
#include <linux/proc_fs.h>
#include <asm/runway.h> /* for proc_runway_root */
#include <asm/pdc.h> /* for PDC_MODEL_* */
+#include <asm/pdcpat.h> /* for is_pdc_pat() */
#include <asm/parisc-device.h>
+
+/* declared in arch/parisc/kernel/setup.c */
+extern struct proc_dir_entry * proc_mckinley_root;
+
#define MODULE_NAME "SBA"
#ifdef CONFIG_PROC_FS
** Don't even think about messing with it unless you have
** plenty of 710's to sacrifice to the computer gods. :^)
*/
+#undef DEBUG_SBA_ASSERT
#undef DEBUG_SBA_INIT
#undef DEBUG_SBA_RUN
#undef DEBUG_SBA_RUN_SG
#undef DEBUG_LARGE_SG_ENTRIES
#undef DEBUG_DMB_TRAP
-#define SBA_INLINE __inline__
-
#ifdef DEBUG_SBA_INIT
#define DBG_INIT(x...) printk(x)
#else
#define DBG_RES(x...)
#endif
+#ifdef DEBUG_SBA_ASSERT
+#undef ASSERT
+#define ASSERT(expr) \
+ if(!(expr)) { \
+ printk("\n%s:%d: Assertion " #expr " failed!\n", \
+ __FILE__, __LINE__); \
+ panic(#expr); \
+ }
+#else
+#define ASSERT(expr)
+#endif
+
+
+#if defined(__LP64__) && !defined(CONFIG_PDC_NARROW)
+/* "low end" PA8800 machines use ZX1 chipset */
+#define ZX1_SUPPORT
+#endif
+
+#define SBA_INLINE __inline__
+
+
/*
** The number of pdir entries to "free" before issueing
** a read to PCOM register to flush out PCOM writes.
#define REOG_MERCED_PORT 0x805
#define REOG_ROPES_PORT 0x783
+#define PLUTO_MCKINLEY_PORT 0x880
+#define PLUTO_ROPES_PORT 0x784
+
#define SBA_FUNC_ID 0x0000 /* function id */
#define SBA_FCLASS 0x0008 /* function class, bist, header, rev... */
#define IS_IKE(id) \
(((id)->hversion == IKE_MERCED_PORT) || ((id)->hversion == IKE_ROPES_PORT))
+#define IS_PLUTO(id) \
+(((id)->hversion == PLUTO_MCKINLEY_PORT) || ((id)->hversion == PLUTO_ROPES_PORT))
+
#define SBA_FUNC_SIZE 4096 /* SBA configuration function reg set */
#define ASTRO_IOC_OFFSET 0x20000
/* Ike's IOC's occupy functions 2 and 3 (not 0 and 1) */
#define IKE_IOC_OFFSET(p) ((p+2)*SBA_FUNC_SIZE)
+#define PLUTO_IOC_OFFSET 0x1000
+
#define IOC_CTRL 0x8 /* IOC_CTRL offset */
#define IOC_CTRL_TC (1 << 0) /* TOC Enable */
#define IOC_CTRL_CE (1 << 1) /* Coalesce Enable */
#define IOC_CTRL_RM (1 << 8) /* Real Mode */
#define IOC_CTRL_NC (1 << 9) /* Non Coherent Mode */
-#define MAX_IOC 2 /* per Ike. Astro only has 1 */
+#define MAX_IOC 2 /* per Ike. Pluto/Astro only have 1. */
/*
#define IOC_TCNFG 0x318
#define IOC_PDIR_BASE 0x320
-#define IOC_IOVA_SPACE_BASE 0 /* IOVA ranges start at 0 */
+/* AGP GART driver looks for this */
+#define SBA_IOMMU_COOKIE 0x0000badbadc0ffeeUL
+
/*
** IOC supports 4/8/16/64KB page sizes (see TCNFG register)
** page since the Virtual Coherence Index has to be generated
** and updated for each page.
**
-** IOVP_SIZE could only be greater than PAGE_SIZE if we are
-** confident the drivers really only touch the next physical
-** page iff that driver instance owns it.
+** PAGE_SIZE could be greater than IOVP_SIZE. But not the inverse.
*/
#define IOVP_SIZE PAGE_SIZE
#define IOVP_SHIFT PAGE_SHIFT
unsigned long ioc_hpa; /* I/O MMU base address */
char *res_map; /* resource map, bit == pdir entry */
u64 *pdir_base; /* physical base address */
-
+ unsigned long ibase; /* pdir IOV Space base - shared w/lba_pci */
+ unsigned long imask; /* pdir IOV Space mask - shared w/lba_pci */
+#ifdef ZX1_SUPPORT
+ unsigned long iovp_mask; /* help convert IOVA to IOVP */
+#endif
unsigned long *res_hint; /* next avail IOVP - circular search */
spinlock_t res_lock;
- unsigned long hint_mask_pdir; /* bits used for DMA hints */
unsigned int res_bitshift; /* from the LEFT! */
unsigned int res_size; /* size of resource map in bytes */
+#if SBA_HINT_SUPPORT
+/* FIXME : DMA HINTs not used */
+ unsigned long hint_mask_pdir; /* bits used for DMA hints */
unsigned int hint_shift_pdir;
+#endif
#if DELAYED_RESOURCE_CNT > 0
int saved_cnt;
struct sba_dma_pair {
/* STUFF We don't need in performance path */
unsigned int pdir_size; /* in bytes, determined by IOV Space size */
- unsigned long ibase; /* pdir IOV Space base - shared w/lba_pci */
- unsigned long imask; /* pdir IOV Space mask - shared w/lba_pci */
};
struct sba_device {
/* Looks nice and keeps the compiler happy */
#define SBA_DEV(d) ((struct sba_device *) (d))
+#if SBA_AGP_SUPPORT
+static int reserve_sba_gart = 1;
+#endif
#define ROUNDUP(x,y) ((x + ((y)-1)) & ~((y)-1))
sba_dump_tlb(unsigned long hpa)
{
DBG_INIT("IO TLB at 0x%lx\n", hpa);
- DBG_INIT("IOC_IBASE : %Lx\n", READ_REG64(hpa+IOC_IBASE));
- DBG_INIT("IOC_IMASK : %Lx\n", READ_REG64(hpa+IOC_IMASK));
- DBG_INIT("IOC_TCNFG : %Lx\n", READ_REG64(hpa+IOC_TCNFG));
- DBG_INIT("IOC_PDIR_BASE: %Lx\n", READ_REG64(hpa+IOC_PDIR_BASE));
+ DBG_INIT("IOC_IBASE : 0x%Lx\n", READ_REG64(hpa+IOC_IBASE));
+ DBG_INIT("IOC_IMASK : 0x%Lx\n", READ_REG64(hpa+IOC_IMASK));
+ DBG_INIT("IOC_TCNFG : 0x%Lx\n", READ_REG64(hpa+IOC_TCNFG));
+ DBG_INIT("IOC_PDIR_BASE: 0x%Lx\n", READ_REG64(hpa+IOC_PDIR_BASE));
DBG_INIT("\n");
}
+#else
+#define sba_dump_ranges(x)
+#define sba_dump_tlb(x)
#endif
#define PAGES_PER_RANGE 1 /* could increase this to 4 or 8 if needed */
/* Convert from IOVP to IOVA and vice versa. */
-#define SBA_IOVA(ioc,iovp,offset,hint_reg) ((iovp) | (offset) | ((hint_reg)<<(ioc->hint_shift_pdir)))
-#define SBA_IOVP(ioc,iova) ((iova) & ioc->hint_mask_pdir)
-/* FIXME : review these macros to verify correctness and usage */
+#ifdef ZX1_SUPPORT
+/* Pluto (aka ZX1) boxes need to set or clear the ibase bits appropriately */
+#define SBA_IOVA(ioc,iovp,offset,hint_reg) ((ioc->ibase) | (iovp) | (offset))
+#define SBA_IOVP(ioc,iova) ((iova) & (ioc)->iovp_mask)
+#else
+/* only support Astro and ancestors. Saves a few cycles in key places */
+#define SBA_IOVA(ioc,iovp,offset,hint_reg) ((iovp) | (offset))
+#define SBA_IOVP(ioc,iova) (iova)
+#endif
+
#define PDIR_INDEX(iovp) ((iovp)>>IOVP_SHIFT)
-#define MKIOVP(dma_hint,pide) (dma_addr_t)((long)(dma_hint) | ((long)(pide) << IOVP_SHIFT))
-#define MKIOVA(iovp,offset) (dma_addr_t)((long)iovp | (long)offset)
#define RESMAP_MASK(n) (~0UL << (BITS_PER_LONG - (n)))
#define RESMAP_IDX_MASK (sizeof(unsigned long) - 1)
*
***************************************************************/
+#if SBA_HINT_SUPPORT
#define SBA_DMA_HINT(ioc, val) ((val) << (ioc)->hint_shift_pdir)
-
+#endif
typedef unsigned long space_t;
#define KERNEL_SPACE 0
*
* Given a virtual address (vba, arg2) and space id, (sid, arg1)
* sba_io_pdir_entry() loads the I/O PDIR entry pointed to by
- * pdir_ptr (arg0). Each IO Pdir entry consists of 8 bytes as
- * shown below (MSB == bit 0):
+ * pdir_ptr (arg0).
+ * Using the bass-ackwards HP bit numbering, Each IO Pdir entry
+ * for Astro/Ike looks like:
+ *
*
* 0 19 51 55 63
* +-+---------------------+----------------------------------+----+--------+
* |V| U | PPN[43:12] | U | VI |
* +-+---------------------+----------------------------------+----+--------+
*
- * V == Valid Bit
+ * Pluto is basically identical, supports fewer physical address bits:
+ *
+ * 0 23 51 55 63
+ * +-+------------------------+-------------------------------+----+--------+
+ * |V| U | PPN[39:12] | U | VI |
+ * +-+------------------------+-------------------------------+----+--------+
+ *
+ * V == Valid Bit (Most Significant Bit is bit 0)
* U == Unused
* PPN == Physical Page Number
* VI == Virtual Index (aka Coherent Index)
*
- * The physical address fields are filled with the results of the LPA
- * instruction. The virtual index field is filled with the results of
- * of the LCI (Load Coherence Index) instruction. The 8 bits used for
- * the virtual index are bits 12:19 of the value returned by LCI.
+ * LPA instruction output is put into PPN field.
+ * LCI (Load Coherence Index) instruction provides the "VI" bits.
*
- * We need to pre-swap the bytes since PCX-W is Big Endian.
+ * We pre-swap the bytes since PCX-W is Big Endian and the
+ * IOMMU uses little endian for the pdir.
*/
ASSERT(sid == KERNEL_SPACE);
pa = virt_to_phys(vba);
- pa &= ~4095ULL; /* clear out offset bits */
+ pa &= IOVP_MASK;
mtsp(sid,1);
asm("lci 0(%%sr1, %1), %0" : "=r" (ci) : "r" (vba));
} while (byte_cnt > 0);
}
- WRITE_REG(iovp, ioc->ioc_hpa+IOC_PCOM);
+ WRITE_REG( SBA_IOVA(ioc, iovp, 0, 0), ioc->ioc_hpa+IOC_PCOM);
}
/**
pide = sba_alloc_range(ioc, size);
iovp = (dma_addr_t) pide << IOVP_SHIFT;
- DBG_RUN("%s() 0x%p -> 0x%lx",
+ DBG_RUN("%s() 0x%p -> 0x%lx\n",
__FUNCTION__, addr, (long) iovp | offset);
pdir_start = &(ioc->pdir_base[pide]);
ASSERT(((u8 *)pdir_start)[7] == 0); /* verify availability */
sba_io_pdir_entry(pdir_start, KERNEL_SPACE, (unsigned long) addr, 0);
- DBG_RUN(" pdir 0x%p %02x%02x%02x%02x%02x%02x%02x%02x\n",
+ DBG_RUN(" pdir 0x%p %02x%02x%02x%02x%02x%02x%02x%02x\n",
pdir_start,
(u8) (((u8 *) pdir_start)[7]),
(u8) (((u8 *) pdir_start)[6]),
ioc->usingle_pages += size >> IOVP_SHIFT;
#endif
+ sba_mark_invalid(ioc, iova, size);
+
#if DELAYED_RESOURCE_CNT > 0
+ /* Delaying when we re-use a IO Pdir entry reduces the number
+ * of MMIO reads needed to flush writes to the PCOM register.
+ */
d = &(ioc->saved[ioc->saved_cnt]);
d->iova = iova;
d->size = size;
if (++(ioc->saved_cnt) >= DELAYED_RESOURCE_CNT) {
int cnt = ioc->saved_cnt;
while (cnt--) {
- sba_mark_invalid(ioc, d->iova, d->size);
sba_free_range(ioc, d->iova, d->size);
d--;
}
READ_REG(ioc->ioc_hpa+IOC_PCOM); /* flush purges */
}
#else /* DELAYED_RESOURCE_CNT == 0 */
- sba_mark_invalid(ioc, iova, size);
sba_free_range(ioc, iova, size);
READ_REG(ioc->ioc_hpa+IOC_PCOM); /* flush purges */
#endif /* DELAYED_RESOURCE_CNT == 0 */
return (void *) pdir_base;
}
+static void
+sba_ioc_init_pluto(struct parisc_device *sba, struct ioc *ioc, int ioc_num)
+{
+ /* lba_set_iregs() is in arch/parisc/kernel/lba_pci.c */
+ extern void lba_set_iregs(struct parisc_device *, u32, u32);
+
+ u32 iova_space_mask;
+ u32 iova_space_size;
+ int iov_order, tcnfg;
+ struct parisc_device *lba;
+#if SBA_AGP_SUPPORT
+ int agp_found = 0;
+#endif
+ /*
+ ** Firmware programs the base and size of a "safe IOVA space"
+ ** (one that doesn't overlap memory or LMMIO space) in the
+ ** IBASE and IMASK registers.
+ */
+ ioc->ibase = READ_REG(ioc->ioc_hpa + IOC_IBASE);
+ iova_space_size = ~(READ_REG(ioc->ioc_hpa + IOC_IMASK) & 0xFFFFFFFFUL) + 1;
+
+ if ((ioc->ibase < 0xfed00000UL) && ((ioc->ibase + iova_space_size) > 0xfee00000UL)) {
+ printk("WARNING: IOV space overlaps local config and interrupt message, truncating\n");
+ iova_space_size /= 2;
+ }
+
+ /*
+ ** iov_order is always based on a 1GB IOVA space since we want to
+ ** turn on the other half for AGP GART.
+ */
+ iov_order = get_order(iova_space_size >> (IOVP_SHIFT - PAGE_SHIFT));
+ ioc->pdir_size = (iova_space_size / IOVP_SIZE) * sizeof(u64);
+
+ DBG_INIT("%s() hpa 0x%lx IOV %dMB (%d bits)\n",
+ __FUNCTION__, ioc->ioc_hpa, iova_space_size >> 20,
+ iov_order + PAGE_SHIFT);
+
+ ioc->pdir_base = (void *) __get_free_pages(GFP_KERNEL,
+ get_order(ioc->pdir_size));
+ if (!ioc->pdir_base)
+ panic("Couldn't allocate I/O Page Table\n");
+
+ memset(ioc->pdir_base, 0, ioc->pdir_size);
+
+ DBG_INIT("%s() pdir %p size %x\n",
+ __FUNCTION__, ioc->pdir_base, ioc->pdir_size);
+
+#if SBA_HINT_SUPPORT
+ ioc->hint_shift_pdir = iov_order + PAGE_SHIFT;
+ ioc->hint_mask_pdir = ~(0x3 << (iov_order + PAGE_SHIFT));
+
+ DBG_INIT(" hint_shift_pdir %x hint_mask_pdir %lx\n",
+ ioc->hint_shift_pdir, ioc->hint_mask_pdir);
+#endif
+
+ ASSERT((((unsigned long) ioc->pdir_base) & PAGE_MASK) == (unsigned long) ioc->pdir_base);
+ WRITE_REG(virt_to_phys(ioc->pdir_base), ioc->ioc_hpa + IOC_PDIR_BASE);
+
+ /* build IMASK for IOC and Elroy */
+ iova_space_mask = 0xffffffff;
+ iova_space_mask <<= (iov_order + PAGE_SHIFT);
+ ioc->imask = iova_space_mask;
+#ifdef ZX1_SUPPORT
+ ioc->iovp_mask = ~(iova_space_mask + PAGE_SIZE - 1);
+#endif
+ sba_dump_tlb(ioc->ioc_hpa);
+
+ /*
+ ** setup Mercury IBASE/IMASK registers as well.
+ */
+ for (lba = sba->child; lba; lba = lba->sibling) {
+ int rope_num = (lba->hpa >> 13) & 0xf;
+ if (rope_num >> 3 == ioc_num)
+ lba_set_iregs(lba, ioc->ibase, ioc->imask);
+ }
+
+ WRITE_REG(ioc->imask, ioc->ioc_hpa + IOC_IMASK);
+
+#ifdef __LP64__
+ /*
+ ** Setting the upper bits makes checking for bypass addresses
+ ** a little faster later on.
+ */
+ ioc->imask |= 0xFFFFFFFF00000000UL;
+#endif
+
+ /* Set I/O PDIR Page size to system page size */
+ switch (PAGE_SHIFT) {
+ case 12: tcnfg = 0; break; /* 4K */
+ case 13: tcnfg = 1; break; /* 8K */
+ case 14: tcnfg = 2; break; /* 16K */
+ case 16: tcnfg = 3; break; /* 64K */
+ default:
+ panic(__FILE__ "Unsupported system page size %d",
+ 1 << PAGE_SHIFT);
+ break;
+ }
+ WRITE_REG(tcnfg, ioc->ioc_hpa + IOC_TCNFG);
+
+ /*
+ ** Program the IOC's ibase and enable IOVA translation
+ ** Bit zero == enable bit.
+ */
+ WRITE_REG(ioc->ibase | 1, ioc->ioc_hpa + IOC_IBASE);
+
+ /*
+ ** Clear I/O TLB of any possible entries.
+ ** (Yes. This is a bit paranoid...but so what)
+ */
+ WRITE_REG(ioc->ibase | 31, ioc->ioc_hpa + IOC_PCOM);
+
+#if SBA_AGP_SUPPORT
+ /*
+ ** If an AGP device is present, only use half of the IOV space
+ ** for PCI DMA. Unfortunately we can't know ahead of time
+ ** whether GART support will actually be used, for now we
+ ** can just key on any AGP device found in the system.
+ ** We program the next pdir index after we stop w/ a key for
+ ** the GART code to handshake on.
+ */
+ device=NULL;
+ for (lba = sba->child; lba; lba = lba->sibling) {
+ if (IS_QUICKSILVER(lba))
+ break;
+ }
+
+ if (lba) {
+ DBG_INIT("%s: Reserving half of IOVA space for AGP GART support\n", __FUNCTION__);
+ ioc->pdir_size /= 2;
+ ((u64 *)ioc->pdir_base)[PDIR_INDEX(iova_space_size/2)] = SBA_IOMMU_COOKIE;
+ } else {
+ DBG_INIT("%s: No GART needed - no AGP controller found\n", __FUNCTION__);
+ }
+#endif /* 0 */
+
+}
static void
sba_ioc_init(struct parisc_device *sba, struct ioc *ioc, int ioc_num)
__FUNCTION__, ioc->ioc_hpa, (int) (physmem>>20),
iova_space_size>>20, iov_order + PAGE_SHIFT, pdir_size);
+ ioc->pdir_base = sba_alloc_pdir(pdir_size);
+
+ DBG_INIT("%s() pdir %p size %x\n",
+ __FUNCTION__, ioc->pdir_base, pdir_size);
+
+#if SBA_HINT_SUPPORT
/* FIXME : DMA HINTs not used */
ioc->hint_shift_pdir = iov_order + PAGE_SHIFT;
ioc->hint_mask_pdir = ~(0x3 << (iov_order + PAGE_SHIFT));
- ioc->pdir_base = sba_alloc_pdir(pdir_size);
-
- DBG_INIT("%s() pdir %p size %x hint_shift_pdir %x hint_mask_pdir %lx\n",
- __FUNCTION__, ioc->pdir_base, pdir_size,
- ioc->hint_shift_pdir, ioc->hint_mask_pdir);
+ DBG_INIT(" hint_shift_pdir %x hint_mask_pdir %lx\n",
+ ioc->hint_shift_pdir, ioc->hint_mask_pdir);
+#endif
ASSERT((((unsigned long) ioc->pdir_base) & PAGE_MASK) == (unsigned long) ioc->pdir_base);
WRITE_REG64(virt_to_phys(ioc->pdir_base), ioc->ioc_hpa + IOC_PDIR_BASE);
** On C3000 w/512MB mem, HP-UX 10.20 reports:
** ibase=0, imask=0xFE000000, size=0x2000000.
*/
- ioc->ibase = IOC_IOVA_SPACE_BASE | 1; /* bit 0 == enable bit */
+ ioc->ibase = 0;
ioc->imask = iova_space_mask; /* save it */
+#ifdef ZX1_SUPPORT
+ ioc->iovp_mask = ~(iova_space_mask + PAGE_SIZE - 1);
+#endif
DBG_INIT("%s() IOV base 0x%lx mask 0x%0lx\n",
__FUNCTION__, ioc->ibase, ioc->imask);
/*
** Program the IOC's ibase and enable IOVA translation
*/
- WRITE_REG(ioc->ibase, ioc->ioc_hpa+IOC_IBASE);
+ WRITE_REG(ioc->ibase | 1, ioc->ioc_hpa+IOC_IBASE);
WRITE_REG(ioc->imask, ioc->ioc_hpa+IOC_IMASK);
/* Set I/O PDIR Page size to 4K */
*/
WRITE_REG(0 | 31, ioc->ioc_hpa+IOC_PCOM);
+ ioc->ibase = 0; /* used by SBA_IOVA and related macros */
+
DBG_INIT("%s() DONE\n", __FUNCTION__);
}
*/
}
- ioc_ctl = READ_REG(sba_dev->sba_hpa+IOC_CTRL);
- DBG_INIT("%s() hpa 0x%lx ioc_ctl 0x%Lx ->",
- __FUNCTION__, sba_dev->sba_hpa, ioc_ctl);
- ioc_ctl &= ~(IOC_CTRL_RM | IOC_CTRL_NC | IOC_CTRL_CE);
- ioc_ctl |= IOC_CTRL_TC; /* Astro: firmware enables this */
+ if (!IS_PLUTO(sba_dev->iodc)) {
+ ioc_ctl = READ_REG(sba_dev->sba_hpa+IOC_CTRL);
+ DBG_INIT("%s() hpa 0x%lx ioc_ctl 0x%Lx ->",
+ __FUNCTION__, sba_dev->sba_hpa, ioc_ctl);
+ ioc_ctl &= ~(IOC_CTRL_RM | IOC_CTRL_NC | IOC_CTRL_CE);
+ ioc_ctl |= IOC_CTRL_TC; /* Astro: firmware enables this */
- WRITE_REG(ioc_ctl, sba_dev->sba_hpa+IOC_CTRL);
+ WRITE_REG(ioc_ctl, sba_dev->sba_hpa+IOC_CTRL);
#ifdef DEBUG_SBA_INIT
- ioc_ctl = READ_REG64(sba_dev->sba_hpa+IOC_CTRL);
- DBG_INIT(" 0x%Lx\n", ioc_ctl);
+ ioc_ctl = READ_REG64(sba_dev->sba_hpa+IOC_CTRL);
+ DBG_INIT(" 0x%Lx\n", ioc_ctl);
#endif
+ } /* if !PLUTO */
if (IS_ASTRO(sba_dev->iodc)) {
/* PAT_PDC (L-class) also reports the same goofy base */
sba_dev->ioc[0].ioc_hpa = ASTRO_IOC_OFFSET;
num_ioc = 1;
+ } else if (IS_PLUTO(sba_dev->iodc)) {
+ /* We use a negative value for IOC HPA so it gets
+ * corrected when we add it with IKE's IOC offset.
+ * Doesnt look clean, but fewer code.
+ */
+ sba_dev->ioc[0].ioc_hpa = -PLUTO_IOC_OFFSET;
+ num_ioc = 1;
} else {
sba_dev->ioc[0].ioc_hpa = sba_dev->ioc[1].ioc_hpa = 0;
num_ioc = 2;
/* flush out the writes */
READ_REG(sba_dev->ioc[i].ioc_hpa + ROPE7_CTL);
- sba_ioc_init(sba_dev->dev, &(sba_dev->ioc[i]), i);
+ if (IS_PLUTO(sba_dev->iodc)) {
+ sba_ioc_init_pluto(sba_dev->dev, &(sba_dev->ioc[i]), i);
+ } else {
+ sba_ioc_init(sba_dev->dev, &(sba_dev->ioc[i]), i);
+ }
}
}
{ HPHW_BCPORT, HVERSION_REV_ANY_ID, IKE_MERCED_PORT, 0xc },
{ HPHW_BCPORT, HVERSION_REV_ANY_ID, REO_MERCED_PORT, 0xc },
{ HPHW_BCPORT, HVERSION_REV_ANY_ID, REOG_MERCED_PORT, 0xc },
+ { HPHW_IOA, HVERSION_REV_ANY_ID, PLUTO_MCKINLEY_PORT, 0xc },
/* These two entries commented out because we don't find them in a
* buswalk yet. If/when we do, they would cause us to think we had
* many more SBAs then we really do.
* { HPHW_BCPORT, HVERSION_REV_ANY_ID, ASTRO_ROPES_PORT, 0xc },
* { HPHW_BCPORT, HVERSION_REV_ANY_ID, IKE_ROPES_PORT, 0xc },
+ */
+/* We shall also comment out Pluto Ropes Port since bus walk doesnt
+ * report it yet.
+ * { HPHW_BCPORT, HVERSION_REV_ANY_ID, PLUTO_ROPES_PORT, 0xc },
*/
{ 0, }
};
int i;
char *version;
-#ifdef DEBUG_SBA_INIT
sba_dump_ranges(dev->hpa);
-#endif
/* Read HW Rev First */
func_class = READ_REG(dev->hpa + SBA_FCLASS);
version = astro_rev;
} else if (IS_IKE(&dev->id)) {
- static char ike_rev[]="Ike rev ?";
-
+ static char ike_rev[] = "Ike rev ?";
ike_rev[8] = '0' + (char) (func_class & 0xff);
version = ike_rev;
+ } else if (IS_PLUTO(&dev->id)) {
+ static char pluto_rev[]="Pluto ?.?";
+ pluto_rev[6] = '0' + (char) ((func_class & 0xf0) >> 4);
+ pluto_rev[8] = '0' + (char) (func_class & 0x0f);
+ version = pluto_rev;
} else {
- static char reo_rev[]="REO rev ?";
-
+ static char reo_rev[] = "REO rev ?";
reo_rev[8] = '0' + (char) (func_class & 0xff);
version = reo_rev;
}
if (!global_ioc_cnt) {
global_ioc_cnt = count_parisc_driver(&sba_driver);
- /* Only Astro has one IOC per SBA */
- if (!IS_ASTRO(&dev->id))
+ /* Astro and Pluto have one IOC per SBA */
+ if ((!IS_ASTRO(&dev->id)) || (!IS_PLUTO(&dev->id)))
global_ioc_cnt *= 2;
}
printk(KERN_INFO "%s found %s at 0x%lx\n",
MODULE_NAME, version, dev->hpa);
-#ifdef DEBUG_SBA_INIT
- sba_dump_tlb(dev->hpa);
-#endif
-
sba_dev = kmalloc(sizeof(struct sba_device), GFP_KERNEL);
if (NULL == sba_dev) {
printk(KERN_ERR MODULE_NAME " - couldn't alloc sba_device\n");
create_proc_info_entry("Astro", 0, proc_runway_root, sba_proc_info);
} else if (IS_IKE(&dev->id)) {
create_proc_info_entry("Ike", 0, proc_runway_root, sba_proc_info);
+ } else if (IS_PLUTO(&dev->id)) {
+ create_proc_info_entry("Pluto", 0, proc_mckinley_root, sba_proc_info);
} else {
create_proc_info_entry("Reo", 0, proc_runway_root, sba_proc_info);
}
obj-$(CONFIG_PPC64) += setup-bus.o
obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o
obj-$(CONFIG_X86_VISWS) += setup-irq.o
-obj-$(CONFIG_PCI_USE_VECTOR) += msi.o
+obj-$(CONFIG_PCI_MSI) += msi.o
# Cardbus & CompactPCI use setup-bus
obj-$(CONFIG_HOTPLUG) += setup-bus.o
struct {
__u8 type : 5; /* {0: unused, 5h:MSI, 11h:MSI-X} */
__u8 maskbit : 1; /* mask-pending bit supported ? */
- __u8 reserved: 2; /* reserved */
+ __u8 state : 1; /* {0: free, 1: busy} */
+ __u8 reserved: 1; /* reserved */
__u8 entry_nr; /* specific enabled entry */
__u8 default_vector; /* default pre-assigned vector */
__u8 current_cpu; /* current destination cpu */
#include "cirrus.h"
MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Driver for the Cirrus PD6729 PCI-PCMCIA bridge");
+MODULE_AUTHOR("Jun Komuro <komurojun@mbn.nifty.com>");
#define MAX_SOCKETS 2
-/* simple helper functions */
-/* External clock time, in nanoseconds. 120 ns = 8.33 MHz */
+/*
+ * simple helper functions
+ * External clock time, in nanoseconds. 120 ns = 8.33 MHz
+ */
#define to_cycles(ns) ((ns)/120)
static spinlock_t port_lock = SPIN_LOCK_UNLOCKED;
*value |= SS_DETECT;
}
- /* IO cards have a different meaning of bits 0,1 */
- /* Also notice the inverse-logic on the bits */
+ /*
+ * IO cards have a different meaning of bits 0,1
+ * Also notice the inverse-logic on the bits
+ */
if (indirect_read(socket, I365_INTCTL) & I365_PC_IOCARD) {
/* IO card */
if (!(status & I365_CS_STSCHG))
state->io_irq = 0;
state->csc_mask = 0;
- /* First the power status of the socket */
- /* PCTRL - Power Control Register */
+ /*
+ * First the power status of the socket
+ * PCTRL - Power Control Register
+ */
reg = indirect_read(socket, I365_POWER);
if (reg & I365_PWR_AUTO)
state->Vpp = 120;
}
- /* Now the IO card, RESET flags and IO interrupt */
- /* IGENC, Interrupt and General Control */
+ /*
+ * Now the IO card, RESET flags and IO interrupt
+ * IGENC, Interrupt and General Control
+ */
reg = indirect_read(socket, I365_INTCTL);
if ((reg & I365_PC_RESET) == 0)
/* Set the IRQ number */
state->io_irq = socket->socket.pci_irq;
- /* Card status change */
- /* CSCICR, Card Status Change Interrupt Configuration */
+ /*
+ * Card status change
+ * CSCICR, Card Status Change Interrupt Configuration
+ */
reg = indirect_read(socket, I365_CSCINT);
if (reg & I365_CSC_DETECT)
printk(KERN_INFO "pd6729: Cirrus PD6729 PCI to PCMCIA Bridge at 0x%lx on irq %d\n",
pci_resource_start(dev, 0), dev->irq);
printk(KERN_INFO "pd6729: configured as a %d socket device.\n", MAX_SOCKETS);
- /* Since we have no memory BARs some firmware we may not
- have had PCI_COMMAND_MEM enabled, yet the device needs
- it. */
+ /*
+ * Since we have no memory BARs some firmware we may not
+ * have had PCI_COMMAND_MEM enabled, yet the device needs
+ * it.
+ */
pci_read_config_byte(dev, PCI_COMMAND, &configbyte);
if (!(configbyte & PCI_COMMAND_MEMORY)) {
printk(KERN_DEBUG "pd6729: Enabling PCI_COMMAND_MEMORY.\n");
--- /dev/null
+/*
+ * socket_sysfs.c -- most of socket-related sysfs output
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * (C) 2003 - 2004 Dominik Brodowski
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/suspend.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+
+#define IN_CARD_SERVICES
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include "cs_internal.h"
+
+#define to_socket(_dev) container_of(_dev, struct pcmcia_socket, dev)
+
+static ssize_t pccard_show_type(struct class_device *dev, char *buf)
+{
+ int val;
+ struct pcmcia_socket *s = to_socket(dev);
+
+ if (!(s->state & SOCKET_PRESENT))
+ return -ENODEV;
+ s->ops->get_status(s, &val);
+ if (val & SS_CARDBUS)
+ return sprintf(buf, "32-bit\n");
+ if (val & SS_DETECT)
+ return sprintf(buf, "16-bit\n");
+ return sprintf(buf, "invalid\n");
+}
+static CLASS_DEVICE_ATTR(card_type, 0400, pccard_show_type, NULL);
+
+static ssize_t pccard_show_voltage(struct class_device *dev, char *buf)
+{
+ int val;
+ struct pcmcia_socket *s = to_socket(dev);
+
+ if (!(s->state & SOCKET_PRESENT))
+ return -ENODEV;
+ s->ops->get_status(s, &val);
+ if (val & SS_3VCARD)
+ return sprintf(buf, "3.3V\n");
+ if (val & SS_XVCARD)
+ return sprintf(buf, "X.XV\n");
+ return sprintf(buf, "5.0V\n");
+}
+static CLASS_DEVICE_ATTR(card_voltage, 0400, pccard_show_voltage, NULL);
+
+static ssize_t pccard_show_vpp(struct class_device *dev, char *buf)
+{
+ struct pcmcia_socket *s = to_socket(dev);
+ if (!(s->state & SOCKET_PRESENT))
+ return -ENODEV;
+ return sprintf(buf, "%d.%dV\n", s->socket.Vpp / 10, s->socket.Vpp % 10);
+}
+static CLASS_DEVICE_ATTR(card_vpp, 0400, pccard_show_vpp, NULL);
+
+static ssize_t pccard_show_vcc(struct class_device *dev, char *buf)
+{
+ struct pcmcia_socket *s = to_socket(dev);
+ if (!(s->state & SOCKET_PRESENT))
+ return -ENODEV;
+ return sprintf(buf, "%d.%dV\n", s->socket.Vcc / 10, s->socket.Vcc % 10);
+}
+static CLASS_DEVICE_ATTR(card_vcc, 0400, pccard_show_vcc, NULL);
+
+
+static ssize_t pccard_store_insert(struct class_device *dev, const char *buf, size_t count)
+{
+ ssize_t ret;
+ struct pcmcia_socket *s = to_socket(dev);
+
+ if (!count)
+ return -EINVAL;
+
+ ret = pcmcia_insert_card(s);
+
+ return ret ? ret : count;
+}
+static CLASS_DEVICE_ATTR(card_insert, 0200, NULL, pccard_store_insert);
+
+static ssize_t pccard_store_eject(struct class_device *dev, const char *buf, size_t count)
+{
+ ssize_t ret;
+ struct pcmcia_socket *s = to_socket(dev);
+
+ if (!count)
+ return -EINVAL;
+
+ ret = pcmcia_eject_card(s);
+
+ return ret ? ret : count;
+}
+static CLASS_DEVICE_ATTR(card_eject, 0200, NULL, pccard_store_eject);
+
+
+static struct class_device_attribute *pccard_socket_attributes[] = {
+ &class_device_attr_card_type,
+ &class_device_attr_card_voltage,
+ &class_device_attr_card_vpp,
+ &class_device_attr_card_vcc,
+ &class_device_attr_card_insert,
+ &class_device_attr_card_eject,
+ NULL,
+};
+
+static int __devinit pccard_sysfs_add_socket(struct class_device *class_dev)
+{
+ struct class_device_attribute **attr;
+ int ret = 0;
+
+ for (attr = pccard_socket_attributes; *attr; attr++) {
+ ret = class_device_create_file(class_dev, *attr);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static void __devexit pccard_sysfs_remove_socket(struct class_device *class_dev)
+{
+ struct class_device_attribute **attr;
+
+ for (attr = pccard_socket_attributes; *attr; attr++)
+ class_device_remove_file(class_dev, *attr);
+}
+
+struct class_interface pccard_sysfs_interface = {
+ .class = &pcmcia_socket_class,
+ .add = &pccard_sysfs_add_socket,
+ .remove = __devexit_p(&pccard_sysfs_remove_socket),
+};
DBF_EVENT(6, "TCHAR:read\n");
device = (struct tape_device *) filp->private_data;
- /* Check position. */
- if (ppos != &filp->f_pos) {
- /*
- * "A request was outside the capabilities of the device."
- * This check uses internal knowledge about how pread and
- * read work...
- */
- DBF_EVENT(6, "TCHAR:ppos wrong\n");
- return -EOVERFLOW;
- }
/*
* If the tape isn't terminated yet, do it now. And since we then
DBF_EVENT(6, "TCHAR:write\n");
device = (struct tape_device *) filp->private_data;
- /* Check position */
- if (ppos != &filp->f_pos) {
- /* "A request was outside the capabilities of the device." */
- DBF_EVENT(6, "TCHAR:ppos wrong\n");
- return -EOVERFLOW;
- }
/* Find out block size and number of blocks */
if (device->char_data.block_size != 0) {
if (count < device->char_data.block_size) {
rc = tape_open(device);
if (rc == 0) {
filp->private_data = device;
- return 0;
+ return nonseekable_open(inode, filp);
}
tape_put_device(device);
extern int cio_get_options (struct subchannel *);
extern int cio_modify (struct subchannel *);
/* Use with care. */
+#ifdef CONFIG_CCW_CONSOLE
extern struct subchannel *cio_probe_console(void);
extern void cio_release_console(void);
extern int cio_is_console(int irq);
extern struct subchannel *cio_get_console_subchannel(void);
+#else
+#define cio_is_console(irq) 0
+#define cio_get_console_subchannel() NULL
+#endif
extern int cio_show_msg;
*/
#include <linux/types.h>
+#include <asm/debug.h>
+
+/**
+ * Debug Facility stuff
+ */
+#define IUCV_DBF_SETUP_NAME "iucv_setup"
+#define IUCV_DBF_SETUP_LEN 32
+#define IUCV_DBF_SETUP_INDEX 1
+#define IUCV_DBF_SETUP_NR_AREAS 1
+#define IUCV_DBF_SETUP_LEVEL 3
+
+#define IUCV_DBF_DATA_NAME "iucv_data"
+#define IUCV_DBF_DATA_LEN 128
+#define IUCV_DBF_DATA_INDEX 1
+#define IUCV_DBF_DATA_NR_AREAS 1
+#define IUCV_DBF_DATA_LEVEL 2
+
+#define IUCV_DBF_TRACE_NAME "iucv_trace"
+#define IUCV_DBF_TRACE_LEN 16
+#define IUCV_DBF_TRACE_INDEX 2
+#define IUCV_DBF_TRACE_NR_AREAS 1
+#define IUCV_DBF_TRACE_LEVEL 3
+
+#define IUCV_DBF_TEXT(name,level,text) \
+ do { \
+ debug_text_event(iucv_dbf_##name,level,text); \
+ } while (0)
+
+#define IUCV_DBF_HEX(name,level,addr,len) \
+ do { \
+ debug_event(iucv_dbf_##name,level,(void*)(addr),len); \
+ } while (0)
+
+extern DEFINE_PER_CPU(char[256], iucv_dbf_txt_buf);
+
+#define IUCV_DBF_TEXT_(name,level,text...) \
+ do { \
+ char* iucv_dbf_txt_buf = get_cpu_var(iucv_dbf_txt_buf); \
+ sprintf(iucv_dbf_txt_buf, text); \
+ debug_text_event(iucv_dbf_##name,level,iucv_dbf_txt_buf); \
+ put_cpu_var(iucv_dbf_txt_buf); \
+ } while (0)
+
+#define IUCV_DBF_SPRINTF(name,level,text...) \
+ do { \
+ debug_sprintf_event(iucv_dbf_trace, level, ##text ); \
+ debug_sprintf_event(iucv_dbf_trace, level, text ); \
+ } while (0)
+
+/**
+ * some more debug stuff
+ */
+#define IUCV_HEXDUMP16(importance,header,ptr) \
+PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
+ *(((char*)ptr)),*(((char*)ptr)+1),*(((char*)ptr)+2), \
+ *(((char*)ptr)+3),*(((char*)ptr)+4),*(((char*)ptr)+5), \
+ *(((char*)ptr)+6),*(((char*)ptr)+7),*(((char*)ptr)+8), \
+ *(((char*)ptr)+9),*(((char*)ptr)+10),*(((char*)ptr)+11), \
+ *(((char*)ptr)+12),*(((char*)ptr)+13), \
+ *(((char*)ptr)+14),*(((char*)ptr)+15)); \
+PRINT_##importance(header "%02x %02x %02x %02x %02x %02x %02x %02x " \
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n", \
+ *(((char*)ptr)+16),*(((char*)ptr)+17), \
+ *(((char*)ptr)+18),*(((char*)ptr)+19), \
+ *(((char*)ptr)+20),*(((char*)ptr)+21), \
+ *(((char*)ptr)+22),*(((char*)ptr)+23), \
+ *(((char*)ptr)+24),*(((char*)ptr)+25), \
+ *(((char*)ptr)+26),*(((char*)ptr)+27), \
+ *(((char*)ptr)+28),*(((char*)ptr)+29), \
+ *(((char*)ptr)+30),*(((char*)ptr)+31));
+
+static inline void
+iucv_hex_dump(unsigned char *buf, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ if (i && !(i % 16))
+ printk("\n");
+ printk("%02x ", *(buf + i));
+ }
+ printk("\n");
+}
+/**
+ * end of debug stuff
+ */
+
#define uchar unsigned char
#define ushort unsigned short
#define ulong unsigned long
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/delay.h>
#include <asm/oplib.h>
#include <asm/ebus.h>
#define __KERNEL_SYSCALLS__
read_unlock(&tasklist_lock);
if (!found)
break;
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(HZ);
- current->state = TASK_RUNNING;
+ msleep(1000);
}
kenvctrld_task = NULL;
}
u32 current_time_ms;
TW_Device_Extension *tw_dev = twa_device_extension_list[iminor(inode)];
int retval = TW_IOCTL_ERROR_OS_EFAULT;
+ void __user *argp = (void __user *)arg;
/* Only let one of these through at a time */
if (down_interruptible(&tw_dev->ioctl_sem)) {
}
/* First copy down the driver command */
- if (copy_from_user(&driver_command, (void *)arg, sizeof(TW_Ioctl_Driver_Command)))
+ if (copy_from_user(&driver_command, argp, sizeof(TW_Ioctl_Driver_Command)))
goto out2;
/* Check data buffer size */
tw_ioctl = (TW_Ioctl_Buf_Apache *)cpu_addr;
/* Now copy down the entire ioctl */
- if (copy_from_user(tw_ioctl, (void *)arg, driver_command.buffer_length + sizeof(TW_Ioctl_Buf_Apache) - 1))
+ if (copy_from_user(tw_ioctl, argp, driver_command.buffer_length + sizeof(TW_Ioctl_Buf_Apache) - 1))
goto out3;
/* See which ioctl we are doing */
twa_get_request_id(tw_dev, &request_id);
/* Flag internal command */
- tw_dev->srb[request_id] = 0;
+ tw_dev->srb[request_id] = NULL;
/* Flag chrdev ioctl */
tw_dev->chrdev_request_id = request_id;
}
/* Now copy the entire response to userspace */
- if (copy_to_user((void *)arg, tw_ioctl, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length - 1) == 0)
+ if (copy_to_user(argp, tw_ioctl, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length - 1) == 0)
retval = 0;
out3:
/* Now free ioctl buf memory */
--- /dev/null
+/*
+ 3w-9xxx.h -- 3ware 9000 Storage Controller device driver for Linux.
+
+ Written By: Adam Radford <linuxraid@amcc.com>
+
+ Copyright (C) 2004 Applied Micro Circuits Corporation.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ NO WARRANTY
+ THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ solely responsible for determining the appropriateness of using and
+ distributing the Program and assumes all risks associated with its
+ exercise of rights under this Agreement, including but not limited to
+ the risks and costs of program errors, damage to or loss of data,
+ programs or equipment, and unavailability or interruption of operations.
+
+ DISCLAIMER OF LIABILITY
+ NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ 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
+
+ Bugs/Comments/Suggestions should be mailed to:
+ linuxraid@amcc.com
+
+ For more information, goto:
+ http://www.amcc.com
+*/
+
+#ifndef _3W_9XXX_H
+#define _3W_9XXX_H
+
+/* AEN string type */
+typedef struct TAG_twa_message_type {
+ unsigned int code;
+ char* text;
+} twa_message_type;
+
+/* AEN strings */
+static twa_message_type twa_aen_table[] = {
+ {0x0000, "AEN queue empty"},
+ {0x0001, "Controller reset occurred"},
+ {0x0002, "Degraded unit detected"},
+ {0x0003, "Controller error occured"},
+ {0x0004, "Background rebuild failed"},
+ {0x0005, "Background rebuild done"},
+ {0x0006, "Incomplete unit detected"},
+ {0x0007, "Background initialize done"},
+ {0x0008, "Unclean shutdown detected"},
+ {0x0009, "Drive timeout detected"},
+ {0x000A, "Drive error detected"},
+ {0x000B, "Rebuild started"},
+ {0x000C, "Background initialize started"},
+ {0x000D, "Entire logical unit was deleted"},
+ {0x000E, "Background initialize failed"},
+ {0x000F, "SMART attribute exceeded threshold"},
+ {0x0010, "Power supply reported AC under range"},
+ {0x0011, "Power supply reported DC out of range"},
+ {0x0012, "Power supply reported a malfunction"},
+ {0x0013, "Power supply predicted malfunction"},
+ {0x0014, "Battery charge is below threshold"},
+ {0x0015, "Fan speed is below threshold"},
+ {0x0016, "Temperature sensor is above threshold"},
+ {0x0017, "Power supply was removed"},
+ {0x0018, "Power supply was inserted"},
+ {0x0019, "Drive was removed from a bay"},
+ {0x001A, "Drive was inserted into a bay"},
+ {0x001B, "Drive bay cover door was opened"},
+ {0x001C, "Drive bay cover door was closed"},
+ {0x001D, "Product case was opened"},
+ {0x0020, "Prepare for shutdown (power-off)"},
+ {0x0021, "Downgrade UDMA mode to lower speed"},
+ {0x0022, "Upgrade UDMA mode to higher speed"},
+ {0x0023, "Sector repair completed"},
+ {0x0024, "Sbuf memory test failed"},
+ {0x0025, "Error flushing cached write data to array"},
+ {0x0026, "Drive reported data ECC error"},
+ {0x0027, "DCB has checksum error"},
+ {0x0028, "DCB version is unsupported"},
+ {0x0029, "Background verify started"},
+ {0x002A, "Background verify failed"},
+ {0x002B, "Background verify done"},
+ {0x002C, "Bad sector overwritten during rebuild"},
+ {0x002D, "Background rebuild error on source drive"},
+ {0x002E, "Replace failed because replacement drive too small"},
+ {0x002F, "Verify failed because array was never initialized"},
+ {0x0030, "Unsupported ATA drive"},
+ {0x0031, "Synchronize host/controller time"},
+ {0x0032, "Spare capacity is inadequate for some units"},
+ {0x0033, "Background migration started"},
+ {0x0034, "Background migration failed"},
+ {0x0035, "Background migration done"},
+ {0x0036, "Verify detected and fixed data/parity mismatch"},
+ {0x0037, "SO-DIMM incompatible"},
+ {0x0038, "SO-DIMM not detected"},
+ {0x0039, "Corrected Sbuf ECC error"},
+ {0x003A, "Drive power on reset detected"},
+ {0x003B, "Background rebuild paused"},
+ {0x003C, "Background initialize paused"},
+ {0x003D, "Background verify paused"},
+ {0x003E, "Background migration paused"},
+ {0x003F, "Corrupt flash file system detected"},
+ {0x0040, "Flash file system repaired"},
+ {0x0041, "Unit number assignments were lost"},
+ {0x0042, "Error during read of primary DCB"},
+ {0x0043, "Latent error found in backup DCB"},
+ {0x00FC, "Recovered/finished array membership update"},
+ {0x00FD, "Handler lockup"},
+ {0x00FE, "Retrying PCI transfer"},
+ {0x00FF, "AEN queue is full"},
+ {0xFFFFFFFF, (char*) 0}
+};
+
+/* AEN severity table */
+static char *twa_aen_severity_table[] =
+{
+ "None", "ERROR", "WARNING", "INFO", "DEBUG", (char*) 0
+};
+
+/* Error strings */
+static twa_message_type twa_error_table[] = {
+ {0x0100, "SGL entry contains zero data"},
+ {0x0101, "Invalid command opcode"},
+ {0x0102, "SGL entry has unaligned address"},
+ {0x0103, "SGL size does not match command"},
+ {0x0104, "SGL entry has illegal length"},
+ {0x0105, "Command packet is not aligned"},
+ {0x0106, "Invalid request ID"},
+ {0x0107, "Duplicate request ID"},
+ {0x0108, "ID not locked"},
+ {0x0109, "LBA out of range"},
+ {0x010A, "Logical unit not supported"},
+ {0x010B, "Parameter table does not exist"},
+ {0x010C, "Parameter index does not exist"},
+ {0x010D, "Invalid field in CDB"},
+ {0x010E, "Specified port has invalid drive"},
+ {0x010F, "Parameter item size mismatch"},
+ {0x0110, "Failed memory allocation"},
+ {0x0111, "Memory request too large"},
+ {0x0112, "Out of memory segments"},
+ {0x0113, "Invalid address to deallocate"},
+ {0x0114, "Out of memory"},
+ {0x0115, "Out of heap"},
+ {0x0120, "Double degrade"},
+ {0x0121, "Drive not degraded"},
+ {0x0122, "Reconstruct error"},
+ {0x0123, "Replace not accepted"},
+ {0x0124, "Replace drive capacity too small"},
+ {0x0125, "Sector count not allowed"},
+ {0x0126, "No spares left"},
+ {0x0127, "Reconstruct error"},
+ {0x0128, "Unit is offline"},
+ {0x0129, "Cannot update status to DCB"},
+ {0x0130, "Invalid stripe handle"},
+ {0x0131, "Handle that was not locked"},
+ {0x0132, "Handle that was not empty"},
+ {0x0133, "Handle has different owner"},
+ {0x0140, "IPR has parent"},
+ {0x0150, "Illegal Pbuf address alignment"},
+ {0x0151, "Illegal Pbuf transfer length"},
+ {0x0152, "Illegal Sbuf address alignment"},
+ {0x0153, "Illegal Sbuf transfer length"},
+ {0x0160, "Command packet too large"},
+ {0x0161, "SGL exceeds maximum length"},
+ {0x0162, "SGL has too many entries"},
+ {0x0170, "Insufficient resources for rebuilder"},
+ {0x0171, "Verify error (data != parity)"},
+ {0x0180, "Requested segment not in directory of this DCB"},
+ {0x0181, "DCB segment has unsupported version"},
+ {0x0182, "DCB segment has checksum error"},
+ {0x0183, "DCB support (settings) segment invalid"},
+ {0x0184, "DCB UDB (unit descriptor block) segment invalid"},
+ {0x0185, "DCB GUID (globally unique identifier) segment invalid"},
+ {0x01A0, "Could not clear Sbuf"},
+ {0x01C0, "Flash identify failed"},
+ {0x01C1, "Flash out of bounds"},
+ {0x01C2, "Flash verify error"},
+ {0x01C3, "Flash file object not found"},
+ {0x01C4, "Flash file already present"},
+ {0x01C5, "Flash file system full"},
+ {0x01C6, "Flash file not present"},
+ {0x01C7, "Flash file size error"},
+ {0x01C8, "Bad flash file checksum"},
+ {0x01CA, "Corrupt flash file system detected"},
+ {0x01D0, "Invalid field in parameter list"},
+ {0x01D1, "Parameter list length error"},
+ {0x01D2, "Parameter item is not changeable"},
+ {0x01D3, "Parameter item is not saveable"},
+ {0x0200, "UDMA CRC error"},
+ {0x0201, "Internal CRC error"},
+ {0x0202, "Data ECC error"},
+ {0x0203, "ADP level 1 error"},
+ {0x0204, "Port timeout"},
+ {0x0205, "Drive power on reset"},
+ {0x0206, "ADP level 2 error"},
+ {0x0207, "Soft reset failed"},
+ {0x0208, "Drive not ready"},
+ {0x0209, "Unclassified port error"},
+ {0x020A, "Drive aborted command"},
+ {0x0210, "Internal CRC error"},
+ {0x0211, "PCI abort error"},
+ {0x0212, "PCI parity error"},
+ {0x0213, "Port handler error"},
+ {0x0214, "Token interrupt count error"},
+ {0x0215, "Timeout waiting for PCI transfer"},
+ {0x0216, "Corrected buffer ECC"},
+ {0x0217, "Uncorrected buffer ECC"},
+ {0x0230, "Unsupported command during flash recovery"},
+ {0x0231, "Next image buffer expected"},
+ {0x0232, "Binary image architecture incompatible"},
+ {0x0233, "Binary image has no signature"},
+ {0x0234, "Binary image has bad checksum"},
+ {0x0235, "Image downloaded overflowed buffer"},
+ {0x0240, "I2C device not found"},
+ {0x0241, "I2C transaction aborted"},
+ {0x0242, "SO-DIMM parameter(s) incompatible using defaults"},
+ {0x0243, "SO-DIMM unsupported"},
+ {0x0248, "SPI transfer status error"},
+ {0x0249, "SPI transfer timeout error"},
+ {0x0250, "Invalid unit descriptor size in CreateUnit"},
+ {0x0251, "Unit descriptor size exceeds data buffer in CreateUnit"},
+ {0x0252, "Invalid value in CreateUnit descriptor"},
+ {0x0253, "Inadequate disk space to support descriptor in CreateUnit"},
+ {0x0254, "Unable to create data channel for this unit descriptor"},
+ {0x0255, "CreateUnit descriptor specifies a drive already in use"},
+ {0x0256, "Unable to write configuration to all disks during CreateUnit"},
+ {0x0257, "CreateUnit does not support this descriptor version"},
+ {0x0258, "Invalid subunit for RAID 0 or 5 in CreateUnit"},
+ {0x0259, "Too many descriptors in CreateUnit"},
+ {0x025A, "Invalid configuration specified in CreateUnit descriptor"},
+ {0x025B, "Invalid LBA offset specified in CreateUnit descriptor"},
+ {0x025C, "Invalid stripelet size specified in CreateUnit descriptor"},
+ {0x0260, "SMART attribute exceeded threshold"},
+ {0xFFFFFFFF, (char*) 0}
+};
+
+/* Control register bit definitions */
+#define TW_CONTROL_CLEAR_HOST_INTERRUPT 0x00080000
+#define TW_CONTROL_CLEAR_ATTENTION_INTERRUPT 0x00040000
+#define TW_CONTROL_MASK_COMMAND_INTERRUPT 0x00020000
+#define TW_CONTROL_MASK_RESPONSE_INTERRUPT 0x00010000
+#define TW_CONTROL_UNMASK_COMMAND_INTERRUPT 0x00008000
+#define TW_CONTROL_UNMASK_RESPONSE_INTERRUPT 0x00004000
+#define TW_CONTROL_CLEAR_ERROR_STATUS 0x00000200
+#define TW_CONTROL_ISSUE_SOFT_RESET 0x00000100
+#define TW_CONTROL_ENABLE_INTERRUPTS 0x00000080
+#define TW_CONTROL_DISABLE_INTERRUPTS 0x00000040
+#define TW_CONTROL_ISSUE_HOST_INTERRUPT 0x00000020
+#define TW_CONTROL_CLEAR_PARITY_ERROR 0x00800000
+#define TW_CONTROL_CLEAR_QUEUE_ERROR 0x00400000
+#define TW_CONTROL_CLEAR_PCI_ABORT 0x00100000
+#define TW_CONTROL_CLEAR_SBUF_WRITE_ERROR 0x00000008
+
+/* Status register bit definitions */
+#define TW_STATUS_MAJOR_VERSION_MASK 0xF0000000
+#define TW_STATUS_MINOR_VERSION_MASK 0x0F000000
+#define TW_STATUS_PCI_PARITY_ERROR 0x00800000
+#define TW_STATUS_QUEUE_ERROR 0x00400000
+#define TW_STATUS_MICROCONTROLLER_ERROR 0x00200000
+#define TW_STATUS_PCI_ABORT 0x00100000
+#define TW_STATUS_HOST_INTERRUPT 0x00080000
+#define TW_STATUS_ATTENTION_INTERRUPT 0x00040000
+#define TW_STATUS_COMMAND_INTERRUPT 0x00020000
+#define TW_STATUS_RESPONSE_INTERRUPT 0x00010000
+#define TW_STATUS_COMMAND_QUEUE_FULL 0x00008000
+#define TW_STATUS_RESPONSE_QUEUE_EMPTY 0x00004000
+#define TW_STATUS_MICROCONTROLLER_READY 0x00002000
+#define TW_STATUS_COMMAND_QUEUE_EMPTY 0x00001000
+#define TW_STATUS_EXPECTED_BITS 0x00002000
+#define TW_STATUS_UNEXPECTED_BITS 0x00F00008
+#define TW_STATUS_SBUF_WRITE_ERROR 0x00000008
+#define TW_STATUS_VALID_INTERRUPT 0x00DF0008
+
+/* RESPONSE QUEUE BIT DEFINITIONS */
+#define TW_RESPONSE_ID_MASK 0x00000FF0
+
+/* PCI related defines */
+#define TW_DEVICE_NAME "3w-9xxx"
+#define TW_NUMDEVICES 1
+#define TW_PCI_CLEAR_PARITY_ERRORS 0xc100
+#define TW_PCI_CLEAR_PCI_ABORT 0x2000
+
+/* Command packet opcodes used by the driver */
+#define TW_OP_INIT_CONNECTION 0x1
+#define TW_OP_GET_PARAM 0x12
+#define TW_OP_SET_PARAM 0x13
+#define TW_OP_EXECUTE_SCSI 0x10
+#define TW_OP_DOWNLOAD_FIRMWARE 0x16
+#define TW_OP_RESET 0x1C
+
+/* Asynchronous Event Notification (AEN) codes used by the driver */
+#define TW_AEN_QUEUE_EMPTY 0x0000
+#define TW_AEN_SOFT_RESET 0x0001
+#define TW_AEN_SYNC_TIME_WITH_HOST 0x031
+#define TW_AEN_SEVERITY_ERROR 0x1
+#define TW_AEN_SEVERITY_DEBUG 0x4
+#define TW_AEN_NOT_RETRIEVED 0x1
+#define TW_AEN_RETRIEVED 0x2
+
+/* Command state defines */
+#define TW_S_INITIAL 0x1 /* Initial state */
+#define TW_S_STARTED 0x2 /* Id in use */
+#define TW_S_POSTED 0x4 /* Posted to the controller */
+#define TW_S_PENDING 0x8 /* Waiting to be posted in isr */
+#define TW_S_COMPLETED 0x10 /* Completed by isr */
+#define TW_S_FINISHED 0x20 /* I/O completely done */
+
+/* Compatibility defines */
+#define TW_9000_ARCH_ID 0x5
+#define TW_CURRENT_FW_SRL 24
+#define TW_CURRENT_FW_BUILD 5
+#define TW_CURRENT_FW_BRANCH 1
+
+/* Phase defines */
+#define TW_PHASE_INITIAL 0
+#define TW_PHASE_SINGLE 1
+#define TW_PHASE_SGLIST 2
+
+/* Misc defines */
+#define TW_SECTOR_SIZE 512
+#define TW_ALIGNMENT_9000 4 /* 4 bytes */
+#define TW_ALIGNMENT_9000_SGL 0x3
+#define TW_MAX_UNITS 16
+#define TW_INIT_MESSAGE_CREDITS 0x100
+#define TW_INIT_COMMAND_PACKET_SIZE 0x3
+#define TW_INIT_COMMAND_PACKET_SIZE_EXTENDED 0x6
+#define TW_EXTENDED_INIT_CONNECT 0x2
+#define TW_BUNDLED_FW_SAFE_TO_FLASH 0x4
+#define TW_CTLR_FW_RECOMMENDS_FLASH 0x8
+#define TW_CTLR_FW_COMPATIBLE 0x2
+#define TW_BASE_FW_SRL 0x17
+#define TW_BASE_FW_BRANCH 0
+#define TW_BASE_FW_BUILD 1
+#if BITS_PER_LONG > 32
+#define TW_APACHE_MAX_SGL_LENGTH 72
+#define TW_ESCALADE_MAX_SGL_LENGTH 41
+#define TW_APACHE_CMD_PKT_SIZE 5
+#else
+#define TW_APACHE_MAX_SGL_LENGTH 109
+#define TW_ESCALADE_MAX_SGL_LENGTH 62
+#define TW_APACHE_CMD_PKT_SIZE 4
+#endif
+#define TW_ATA_PASS_SGL_MAX 60
+#define TW_Q_LENGTH 256
+#define TW_Q_START 0
+#define TW_MAX_SLOT 32
+#define TW_MAX_RESET_TRIES 2
+#define TW_MAX_CMDS_PER_LUN 254
+#define TW_MAX_RESPONSE_DRAIN 256
+#define TW_MAX_AEN_DRAIN 40
+#define TW_IN_IOCTL 2
+#define TW_IN_CHRDEV_IOCTL 3
+#define TW_IN_ATTENTION_LOOP 4
+#define TW_MAX_SECTORS 256
+#define TW_AEN_WAIT_TIME 1000
+#define TW_IOCTL_WAIT_TIME (1 * HZ) /* 1 second */
+#define TW_MAX_CDB_LEN 16
+#define TW_ISR_DONT_COMPLETE 2
+#define TW_ISR_DONT_RESULT 3
+#define TW_IOCTL_CHRDEV_TIMEOUT 60 /* 60 seconds */
+#define TW_IOCTL_CHRDEV_FREE -1
+#define TW_COMMAND_OFFSET 128 /* 128 bytes */
+#define TW_VERSION_TABLE 0x0402
+#define TW_TIMEKEEP_TABLE 0x040A
+#define TW_INFORMATION_TABLE 0x0403
+#define TW_PARAM_FWVER 3
+#define TW_PARAM_FWVER_LENGTH 16
+#define TW_PARAM_BIOSVER 4
+#define TW_PARAM_BIOSVER_LENGTH 16
+#define TW_PARAM_PORTCOUNT 3
+#define TW_PARAM_PORTCOUNT_LENGTH 1
+#define TW_MIN_SGL_LENGTH 0x200 /* 512 bytes */
+#define TW_MAX_SENSE_LENGTH 256
+#define TW_EVENT_SOURCE_AEN 0x1000
+#define TW_EVENT_SOURCE_COMMAND 0x1001
+#define TW_EVENT_SOURCE_PCHIP 0x1002
+#define TW_EVENT_SOURCE_DRIVER 0x1003
+#define TW_IOCTL_GET_COMPATIBILITY_INFO 0x101
+#define TW_IOCTL_GET_LAST_EVENT 0x102
+#define TW_IOCTL_GET_FIRST_EVENT 0x103
+#define TW_IOCTL_GET_NEXT_EVENT 0x104
+#define TW_IOCTL_GET_PREVIOUS_EVENT 0x105
+#define TW_IOCTL_GET_LOCK 0x106
+#define TW_IOCTL_RELEASE_LOCK 0x107
+#define TW_IOCTL_FIRMWARE_PASS_THROUGH 0x108
+#define TW_IOCTL_ERROR_STATUS_NOT_LOCKED 0x1001 // Not locked
+#define TW_IOCTL_ERROR_STATUS_LOCKED 0x1002 // Already locked
+#define TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS 0x1003 // No more events
+#define TW_IOCTL_ERROR_STATUS_AEN_CLOBBER 0x1004 // AEN clobber occurred
+#define TW_IOCTL_ERROR_OS_EFAULT -EFAULT // Bad address
+#define TW_IOCTL_ERROR_OS_EINTR -EINTR // Interrupted system call
+#define TW_IOCTL_ERROR_OS_EINVAL -EINVAL // Invalid argument
+#define TW_IOCTL_ERROR_OS_ENOMEM -ENOMEM // Out of memory
+#define TW_IOCTL_ERROR_OS_ERESTARTSYS -ERESTARTSYS // Restart system call
+#define TW_IOCTL_ERROR_OS_EIO -EIO // I/O error
+#define TW_IOCTL_ERROR_OS_ENOTTY -ENOTTY // Not a typewriter
+#define TW_IOCTL_ERROR_OS_ENODEV -ENODEV // No such device
+#define TW_ALLOCATION_LENGTH 128
+#define TW_SENSE_DATA_LENGTH 18
+#define TW_STATUS_CHECK_CONDITION 2
+#define TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED 0x10a
+#define TW_ERROR_UNIT_OFFLINE 0x128
+#define TW_MESSAGE_SOURCE_CONTROLLER_ERROR 3
+#define TW_MESSAGE_SOURCE_CONTROLLER_EVENT 4
+#define TW_MESSAGE_SOURCE_LINUX_DRIVER 6
+#define TW_DRIVER TW_MESSAGE_SOURCE_LINUX_DRIVER
+#define TW_MESSAGE_SOURCE_LINUX_OS 9
+#define TW_OS TW_MESSAGE_SOURCE_LINUX_OS
+#if BITS_PER_LONG > 32
+#define TW_COMMAND_SIZE 5
+#define TW_DMA_MASK DMA_64BIT_MASK
+#else
+#define TW_COMMAND_SIZE 4
+#define TW_DMA_MASK DMA_32BIT_MASK
+#endif
+#ifndef PCI_DEVICE_ID_3WARE_9000
+#define PCI_DEVICE_ID_3WARE_9000 0x1002
+#endif
+
+/* Bitmask macros to eliminate bitfields */
+
+/* opcode: 5, reserved: 3 */
+#define TW_OPRES_IN(x,y) ((x << 5) | (y & 0x1f))
+#define TW_OP_OUT(x) (x & 0x1f)
+
+/* opcode: 5, sgloffset: 3 */
+#define TW_OPSGL_IN(x,y) ((x << 5) | (y & 0x1f))
+#define TW_SGL_OUT(x) ((x >> 5) & 0x7)
+
+/* severity: 3, reserved: 5 */
+#define TW_SEV_OUT(x) (x & 0x7)
+
+/* reserved_1: 4, response_id: 8, reserved_2: 20 */
+#define TW_RESID_OUT(x) ((x >> 4) & 0xff)
+
+/* Macros */
+#define TW_CONTROL_REG_ADDR(x) (x->base_addr)
+#define TW_STATUS_REG_ADDR(x) ((unsigned char *)x->base_addr + 0x4)
+#if BITS_PER_LONG > 32
+#define TW_COMMAND_QUEUE_REG_ADDR(x) ((unsigned char *)x->base_addr + 0x20)
+#else
+#define TW_COMMAND_QUEUE_REG_ADDR(x) ((unsigned char *)x->base_addr + 0x8)
+#endif
+#define TW_RESPONSE_QUEUE_REG_ADDR(x) ((unsigned char *)x->base_addr + 0xC)
+#define TW_CLEAR_ALL_INTERRUPTS(x) (writel(TW_STATUS_VALID_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_CLEAR_ATTENTION_INTERRUPT(x) (writel(TW_CONTROL_CLEAR_ATTENTION_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_CLEAR_HOST_INTERRUPT(x) (writel(TW_CONTROL_CLEAR_HOST_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_DISABLE_INTERRUPTS(x) (writel(TW_CONTROL_DISABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x)))
+#define TW_ENABLE_AND_CLEAR_INTERRUPTS(x) (writel(TW_CONTROL_CLEAR_ATTENTION_INTERRUPT | TW_CONTROL_UNMASK_RESPONSE_INTERRUPT | TW_CONTROL_ENABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x)))
+#define TW_MASK_COMMAND_INTERRUPT(x) (writel(TW_CONTROL_MASK_COMMAND_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_UNMASK_COMMAND_INTERRUPT(x) (writel(TW_CONTROL_UNMASK_COMMAND_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+#define TW_SOFT_RESET(x) (writel(TW_CONTROL_ISSUE_SOFT_RESET | \
+ TW_CONTROL_CLEAR_HOST_INTERRUPT | \
+ TW_CONTROL_CLEAR_ATTENTION_INTERRUPT | \
+ TW_CONTROL_MASK_COMMAND_INTERRUPT | \
+ TW_CONTROL_MASK_RESPONSE_INTERRUPT | \
+ TW_CONTROL_CLEAR_ERROR_STATUS | \
+ TW_CONTROL_DISABLE_INTERRUPTS, TW_CONTROL_REG_ADDR(x)))
+#define TW_PRINTK(h,a,b,c) { \
+if (h) \
+printk(KERN_WARNING "3w-9xxx: scsi%d: ERROR: (0x%02X:0x%04X): %s.\n",h->host_no,a,b,c); \
+else \
+printk(KERN_WARNING "3w-9xxx: ERROR: (0x%02X:0x%04X): %s.\n",a,b,c); \
+}
+
+#pragma pack(1)
+
+/* Scatter Gather List Entry */
+typedef struct TAG_TW_SG_Entry {
+ unsigned long address;
+ u32 length;
+} TW_SG_Entry;
+
+/* Command Packet */
+typedef struct TW_Command {
+ unsigned char opcode__sgloffset;
+ unsigned char size;
+ unsigned char request_id;
+ unsigned char unit__hostid;
+ /* Second DWORD */
+ unsigned char status;
+ unsigned char flags;
+ union {
+ unsigned short block_count;
+ unsigned short parameter_count;
+ } byte6_offset;
+ union {
+ struct {
+ u32 lba;
+ TW_SG_Entry sgl[TW_ESCALADE_MAX_SGL_LENGTH];
+#if BITS_PER_LONG > 32
+ u32 padding[2]; /* pad to 512 bytes */
+#else
+ u32 padding;
+#endif
+ } io;
+ struct {
+ TW_SG_Entry sgl[TW_ESCALADE_MAX_SGL_LENGTH];
+#if BITS_PER_LONG > 32
+ u32 padding[3];
+#else
+ u32 padding[2];
+#endif
+ } param;
+ } byte8_offset;
+} TW_Command;
+
+/* Scatter gather element for 9000+ controllers */
+typedef struct TAG_TW_SG_Apache {
+ unsigned long address;
+ u32 length;
+} TW_SG_Apache;
+
+/* Command Packet for 9000+ controllers */
+typedef struct TAG_TW_Command_Apache {
+ unsigned char opcode__reserved;
+ unsigned char unit;
+ unsigned short request_id;
+ unsigned char status;
+ unsigned char sgl_offset;
+ unsigned short sgl_entries;
+ unsigned char cdb[16];
+ TW_SG_Apache sg_list[TW_APACHE_MAX_SGL_LENGTH];
+#if BITS_PER_LONG > 32
+ unsigned char padding[8];
+#endif
+} TW_Command_Apache;
+
+/* New command packet header */
+typedef struct TAG_TW_Command_Apache_Header {
+ unsigned char sense_data[TW_SENSE_DATA_LENGTH];
+ struct {
+ char reserved[4];
+ unsigned short error;
+ unsigned char padding;
+ unsigned char severity__reserved;
+ } status_block;
+ unsigned char err_specific_desc[98];
+ struct {
+ unsigned char size_header;
+ unsigned short reserved;
+ unsigned char size_sense;
+ } header_desc;
+} TW_Command_Apache_Header;
+
+/* This struct is a union of the 2 command packets */
+typedef struct TAG_TW_Command_Full {
+ TW_Command_Apache_Header header;
+ union {
+ TW_Command oldcommand;
+ TW_Command_Apache newcommand;
+ } command;
+} TW_Command_Full;
+
+/* Initconnection structure */
+typedef struct TAG_TW_Initconnect {
+ unsigned char opcode__reserved;
+ unsigned char size;
+ unsigned char request_id;
+ unsigned char res2;
+ unsigned char status;
+ unsigned char flags;
+ unsigned short message_credits;
+ u32 features;
+ unsigned short fw_srl;
+ unsigned short fw_arch_id;
+ unsigned short fw_branch;
+ unsigned short fw_build;
+ u32 result;
+} TW_Initconnect;
+
+/* Event info structure */
+typedef struct TAG_TW_Event
+{
+ unsigned int sequence_id;
+ unsigned int time_stamp_sec;
+ unsigned short aen_code;
+ unsigned char severity;
+ unsigned char retrieved;
+ unsigned char repeat_count;
+ unsigned char parameter_len;
+ unsigned char parameter_data[98];
+} TW_Event;
+
+typedef struct TAG_TW_Ioctl_Driver_Command {
+ unsigned int control_code;
+ unsigned int status;
+ unsigned int unique_id;
+ unsigned int sequence_id;
+ unsigned int os_specific;
+ unsigned int buffer_length;
+} TW_Ioctl_Driver_Command;
+
+typedef struct TAG_TW_Ioctl_Apache {
+ TW_Ioctl_Driver_Command driver_command;
+ char padding[488];
+ TW_Command_Full firmware_command;
+ char data_buffer[1];
+} TW_Ioctl_Buf_Apache;
+
+/* Lock structure for ioctl get/release lock */
+typedef struct TAG_TW_Lock {
+ unsigned long timeout_msec;
+ unsigned long time_remaining_msec;
+ unsigned long force_flag;
+} TW_Lock;
+
+/* GetParam descriptor */
+typedef struct {
+ unsigned short table_id;
+ unsigned short parameter_id;
+ unsigned short parameter_size_bytes;
+ unsigned short actual_parameter_size_bytes;
+ unsigned char data[1];
+} TW_Param_Apache, *PTW_Param_Apache;
+
+/* Response queue */
+typedef union TAG_TW_Response_Queue {
+ u32 response_id;
+ u32 value;
+} TW_Response_Queue;
+
+typedef struct TAG_TW_Info {
+ char *buffer;
+ int length;
+ int offset;
+ int position;
+} TW_Info;
+
+/* Compatibility information structure */
+typedef struct TAG_TW_Compatibility_Info
+{
+ char driver_version[32];
+ unsigned short working_srl;
+ unsigned short working_branch;
+ unsigned short working_build;
+} TW_Compatibility_Info;
+
+typedef struct TAG_TW_Device_Extension {
+ u32 *base_addr;
+ unsigned long *generic_buffer_virt[TW_Q_LENGTH];
+ unsigned long generic_buffer_phys[TW_Q_LENGTH];
+ TW_Command_Full *command_packet_virt[TW_Q_LENGTH];
+ unsigned long command_packet_phys[TW_Q_LENGTH];
+ struct pci_dev *tw_pci_dev;
+ struct scsi_cmnd *srb[TW_Q_LENGTH];
+ unsigned char free_queue[TW_Q_LENGTH];
+ unsigned char free_head;
+ unsigned char free_tail;
+ unsigned char pending_queue[TW_Q_LENGTH];
+ unsigned char pending_head;
+ unsigned char pending_tail;
+ int state[TW_Q_LENGTH];
+ unsigned int posted_request_count;
+ unsigned int max_posted_request_count;
+ unsigned int pending_request_count;
+ unsigned int max_pending_request_count;
+ unsigned int max_sgl_entries;
+ unsigned int sgl_entries;
+ unsigned int num_aborts;
+ unsigned int num_resets;
+ unsigned int sector_count;
+ unsigned int max_sector_count;
+ unsigned int aen_count;
+ struct Scsi_Host *host;
+ long flags;
+ int reset_print;
+ TW_Event *event_queue[TW_Q_LENGTH];
+ unsigned char error_index;
+ unsigned char event_queue_wrapped;
+ unsigned int error_sequence_id;
+ int ioctl_sem_lock;
+ u32 ioctl_msec;
+ int chrdev_request_id;
+ wait_queue_head_t ioctl_wqueue;
+ struct semaphore ioctl_sem;
+ char aen_clobber;
+ unsigned short working_srl;
+ unsigned short working_branch;
+ unsigned short working_build;
+} TW_Device_Extension;
+
+#pragma pack()
+
+#endif /* _3W_9XXX_H */
+
* and see if we can do an information transfer,
* with failures we will restart.
*/
- hostdata->selecting = 0;
+ hostdata->selecting = NULL;
/* RvC: have to preset this to indicate a new command is being performed */
if (!NCR5380_select(instance, tmp,
to go to sleep */
}
- hostdata->selecting = 0; /* clear this pointer, because we passed the
+ hostdata->selecting = NULL;/* clear this pointer, because we passed the
waiting period */
if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == (SR_SEL | SR_IO)) {
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
struct ahd_devinfo *devinfo,
u_int lun, cam_status status,
char *message, int verbose_level);
-#if AHD_TARGET_MODE
+#ifdef AHD_TARGET_MODE
static void ahd_setup_target_msgin(struct ahd_softc *ahd,
struct ahd_devinfo *devinfo,
struct scb *scb);
ahd->msgin_index = 0;
}
}
-#if AHD_TARGET_MODE
+#ifdef AHD_TARGET_MODE
else {
if (bus_phase == P_MESGOUT) {
ahd->msg_type =
tstate = ahd->enabled_targets[i];
if (tstate != NULL) {
-#if AHD_TARGET_MODE
+#ifdef AHD_TARGET_MODE
int j;
for (j = 0; j < AHD_NUM_LUNS; j++) {
free(tstate, M_DEVBUF);
}
}
-#if AHD_TARGET_MODE
+#ifdef AHD_TARGET_MODE
if (ahd->black_hole != NULL) {
xpt_free_path(ahd->black_hole->path);
free(ahd->black_hole, M_DEVBUF);
ahd_outb(ahd, CLRSINT3, NTRAMPERR|OSRAMPERR);
ahd_outb(ahd, CLRINT, CLRSCSIINT);
-#if NEEDS_MORE_TESTING
+#ifdef NEEDS_MORE_TESTING
/*
* Always enable abort on incoming L_Qs if this feature is
* supported. We use this to catch invalid SCB references.
if (match != 0)
match = ((lun == slun) || (lun == CAM_LUN_WILDCARD));
if (match != 0) {
-#if AHD_TARGET_MODE
+#ifdef AHD_TARGET_MODE
int group;
group = XPT_FC_GROUP(scb->io_ctx->ccb_h.func_code);
/* Make sure the sequencer is in a safe location. */
ahd_clear_critical_section(ahd);
-#if AHD_TARGET_MODE
+#ifdef AHD_TARGET_MODE
if ((ahd->flags & AHD_TARGETROLE) != 0) {
ahd_run_tqinfifo(ahd, /*paused*/TRUE);
}
{
ipr_reinit_ipr_cmnd(ipr_cmd);
ipr_cmd->u.scratch = 0;
+ ipr_cmd->sibling = NULL;
init_timer(&ipr_cmd->timer);
}
**/
static void ipr_internal_cmd_done(struct ipr_cmnd *ipr_cmd)
{
- if (ipr_cmd->u.sibling)
- ipr_cmd->u.sibling = NULL;
+ if (ipr_cmd->sibling)
+ ipr_cmd->sibling = NULL;
else
complete(&ipr_cmd->completion);
}
(res->cfgte.res_addr.lun == sdev->lun)) {
res->sdev = sdev;
res->add_to_ml = 0;
+ res->in_erp = 0;
sdev->hostdata = res;
res->needs_sync_complete = 1;
break;
* If abort has not completed, indicate the reset has, else call the
* abort's done function to wake the sleeping eh thread
*/
- if (ipr_cmd->u.sibling->u.sibling)
- ipr_cmd->u.sibling->u.sibling = NULL;
+ if (ipr_cmd->sibling->sibling)
+ ipr_cmd->sibling->sibling = NULL;
else
- ipr_cmd->u.sibling->done(ipr_cmd->u.sibling);
+ ipr_cmd->sibling->done(ipr_cmd->sibling);
list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
LEAVE;
ipr_sdev_err(ipr_cmd->u.sdev, "Abort timed out. Resetting bus\n");
reset_cmd = ipr_get_free_ipr_cmnd(ioa_cfg);
- ipr_cmd->u.sibling = reset_cmd;
- reset_cmd->u.sibling = ipr_cmd;
+ ipr_cmd->sibling = reset_cmd;
+ reset_cmd->sibling = ipr_cmd;
reset_cmd->ioarcb.res_handle = ipr_cmd->ioarcb.res_handle;
cmd_pkt = &reset_cmd->ioarcb.cmd_pkt;
cmd_pkt->request_type = IPR_RQTYPE_IOACMD;
SCSI_SENSE_BUFFERSIZE);
}
- if (res)
+ if (res) {
res->needs_sync_complete = 1;
+ res->in_erp = 0;
+ }
ipr_unmap_sglist(ioa_cfg, ipr_cmd);
list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q);
scsi_cmd->scsi_done(scsi_cmd);
static void ipr_erp_request_sense(struct ipr_cmnd *ipr_cmd)
{
struct ipr_cmd_pkt *cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt;
+ u32 ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc);
+
+ if (IPR_IOASC_SENSE_KEY(ioasc) > 0) {
+ ipr_erp_done(ipr_cmd);
+ return;
+ }
ipr_reinit_ipr_cmnd_for_erp(ipr_cmd);
ipr_reinit_ipr_cmnd_for_erp(ipr_cmd);
+ if (!res->tcq_active) {
+ ipr_erp_request_sense(ipr_cmd);
+ return;
+ }
+
cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt;
cmd_pkt->request_type = IPR_RQTYPE_IOACMD;
cmd_pkt->cdb[0] = IPR_CANCEL_ALL_REQUESTS;
ipr_erp_cancel_all(ipr_cmd);
return;
}
+ res->needs_sync_complete = 1;
break;
case IPR_IOASC_NR_INIT_CMD_REQUIRED:
break;
ipr_cmd->timer.data = (unsigned long) ipr_cmd;
ipr_cmd->timer.expires = jiffies + IPR_OPERATIONAL_TIMEOUT;
ipr_cmd->timer.function = (void (*)(unsigned long))ipr_timeout;
+ ipr_cmd->done = ipr_reset_ioa_job;
add_timer(&ipr_cmd->timer);
list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q);
/*
* Literals
*/
-#define IPR_DRIVER_VERSION "2.0.7"
-#define IPR_DRIVER_DATE "(May 21, 2004)"
+#define IPR_DRIVER_VERSION "2.0.10"
+#define IPR_DRIVER_DATE "(June 7, 2004)"
/*
* IPR_DBG_TRACE: Setting this to 1 will turn on some general function tracing
dma_addr_t sense_buffer_dma;
unsigned short dma_use_sg;
dma_addr_t dma_handle;
+ struct ipr_cmnd *sibling;
union {
enum ipr_shutdown_type shutdown_type;
struct ipr_hostrcb *hostrcb;
unsigned long time_left;
unsigned long scratch;
struct ipr_resource_entry *res;
- struct ipr_cmnd *sibling;
struct scsi_device *sdev;
} u;
#include <asm/dma.h>
#include "scsi.h"
-#include "hosts.h"
+#include <scsi/scsi_host.h>
#include "qlogicfas408.h"
/*----------------------------------------------------------------*/
* If you do not delete the provisions above, a recipient may use your
* version of this file under either the OSL or the GPL.
*
+ * 0.02
+ * - Added support for CK804 SATA controller.
+ *
+ * 0.01
+ * - Initial revision.
*/
#include <linux/config.h>
#include <linux/libata.h>
#define DRV_NAME "sata_nv"
-#define DRV_VERSION "0.01"
+#define DRV_VERSION "0.02"
#define NV_PORTS 2
#define NV_PIO_MASK 0x1f
#define NV_PORT1_SCR_REG_OFFSET 0x40
#define NV_INT_STATUS 0x10
+#define NV_INT_STATUS_CK804 0x440
#define NV_INT_STATUS_PDEV_INT 0x01
#define NV_INT_STATUS_PDEV_PM 0x02
#define NV_INT_STATUS_PDEV_ADDED 0x04
NV_INT_STATUS_SDEV_HOTPLUG)
#define NV_INT_ENABLE 0x11
+#define NV_INT_ENABLE_CK804 0x441
#define NV_INT_ENABLE_PDEV_MASK 0x01
#define NV_INT_ENABLE_PDEV_PM 0x02
#define NV_INT_ENABLE_PDEV_ADDED 0x04
#define NV_INT_CONFIG 0x12
#define NV_INT_CONFIG_METHD 0x01 // 0 = INT, 1 = SMI
+// For PCI config register 20
+#define NV_MCP_SATA_CFG_20 0x50
+#define NV_MCP_SATA_CFG_20_SATA_SPACE_EN 0x04
+
static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
irqreturn_t nv_interrupt (int irq, void *dev_instance, struct pt_regs *regs);
static u32 nv_scr_read (struct ata_port *ap, unsigned int sc_reg);
static void nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
static void nv_host_stop (struct ata_host_set *host_set);
+static void nv_enable_hotplug(struct ata_probe_ent *probe_ent);
+static void nv_disable_hotplug(struct ata_host_set *host_set);
+static void nv_check_hotplug(struct ata_host_set *host_set);
+static void nv_enable_hotplug_ck804(struct ata_probe_ent *probe_ent);
+static void nv_disable_hotplug_ck804(struct ata_host_set *host_set);
+static void nv_check_hotplug_ck804(struct ata_host_set *host_set);
+
+enum nv_host_type
+{
+ NFORCE2,
+ NFORCE3,
+ CK804
+};
static struct pci_device_id nv_pci_tbl[] = {
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA,
- PCI_ANY_ID, PCI_ANY_ID, },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE2 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA,
- PCI_ANY_ID, PCI_ANY_ID, },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE3 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2,
- PCI_ANY_ID, PCI_ANY_ID, },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE3 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA,
- PCI_ANY_ID, PCI_ANY_ID, },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2,
- PCI_ANY_ID, PCI_ANY_ID, },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA,
- PCI_ANY_ID, PCI_ANY_ID, },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2,
- PCI_ANY_ID, PCI_ANY_ID, },
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 },
{ 0, } /* terminate list */
};
+#define NV_HOST_FLAGS_SCR_MMIO 0x00000001
+
+struct nv_host_desc
+{
+ enum nv_host_type host_type;
+ unsigned long host_flags;
+ void (*enable_hotplug)(struct ata_probe_ent *probe_ent);
+ void (*disable_hotplug)(struct ata_host_set *host_set);
+ void (*check_hotplug)(struct ata_host_set *host_set);
+
+};
+static struct nv_host_desc nv_device_tbl[] = {
+ {
+ .host_type = NFORCE2,
+ .host_flags = 0x00000000,
+ .enable_hotplug = nv_enable_hotplug,
+ .disable_hotplug= nv_disable_hotplug,
+ .check_hotplug = nv_check_hotplug,
+ },
+ {
+ .host_type = NFORCE3,
+ .host_flags = 0x00000000,
+ .enable_hotplug = nv_enable_hotplug,
+ .disable_hotplug= nv_disable_hotplug,
+ .check_hotplug = nv_check_hotplug,
+ },
+ { .host_type = CK804,
+ .host_flags = NV_HOST_FLAGS_SCR_MMIO,
+ .enable_hotplug = nv_enable_hotplug_ck804,
+ .disable_hotplug= nv_disable_hotplug_ck804,
+ .check_hotplug = nv_check_hotplug_ck804,
+ },
+};
+
+struct nv_host
+{
+ struct nv_host_desc *host_desc;
+};
+
static struct pci_driver nv_pci_driver = {
.name = DRV_NAME,
.id_table = nv_pci_tbl,
irqreturn_t nv_interrupt (int irq, void *dev_instance, struct pt_regs *regs)
{
struct ata_host_set *host_set = dev_instance;
+ struct nv_host *host = host_set->private_data;
unsigned int i;
unsigned int handled = 0;
unsigned long flags;
- u8 intr_status;
- u8 intr_enable;
spin_lock_irqsave(&host_set->lock, flags);
handled += ata_host_intr(ap, qc);
}
- intr_status = inb(ap->ioaddr.scr_addr + NV_INT_STATUS);
- intr_enable = inb(ap->ioaddr.scr_addr + NV_INT_ENABLE);
-
- // Clear interrupt status.
- outb(0xff, ap->ioaddr.scr_addr + NV_INT_STATUS);
-
- if (intr_status & NV_INT_STATUS_HOTPLUG) {
- if (intr_status & NV_INT_STATUS_PDEV_ADDED) {
- printk(KERN_WARNING "ata%u: "
- "Primary device added\n", ap->id);
- }
-
- if (intr_status & NV_INT_STATUS_PDEV_REMOVED) {
- printk(KERN_WARNING "ata%u: "
- "Primary device removed\n", ap->id);
- }
-
- if (intr_status & NV_INT_STATUS_SDEV_ADDED) {
- printk(KERN_WARNING "ata%u: "
- "Secondary device added\n", ap->id);
- }
-
- if (intr_status & NV_INT_STATUS_SDEV_REMOVED) {
- printk(KERN_WARNING "ata%u: "
- "Secondary device removed\n", ap->id);
- }
- }
}
+ if (host->host_desc->check_hotplug)
+ host->host_desc->check_hotplug(host_set);
+
spin_unlock_irqrestore(&host_set->lock, flags);
return IRQ_RETVAL(handled);
static u32 nv_scr_read (struct ata_port *ap, unsigned int sc_reg)
{
+ struct ata_host_set *host_set = ap->host_set;
+ struct nv_host *host = host_set->private_data;
+
if (sc_reg > SCR_CONTROL)
return 0xffffffffU;
- return inl(ap->ioaddr.scr_addr + (sc_reg * 4));
+ if (host->host_desc->host_flags & NV_HOST_FLAGS_SCR_MMIO)
+ return readl(ap->ioaddr.scr_addr + (sc_reg * 4));
+ else
+ return inl(ap->ioaddr.scr_addr + (sc_reg * 4));
}
static void nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val)
{
+ struct ata_host_set *host_set = ap->host_set;
+ struct nv_host *host = host_set->private_data;
+
if (sc_reg > SCR_CONTROL)
return;
- outl(val, ap->ioaddr.scr_addr + (sc_reg * 4));
+ if (host->host_desc->host_flags & NV_HOST_FLAGS_SCR_MMIO)
+ writel(val, ap->ioaddr.scr_addr + (sc_reg * 4));
+ else
+ outl(val, ap->ioaddr.scr_addr + (sc_reg * 4));
}
static void nv_host_stop (struct ata_host_set *host_set)
{
- int i;
+ struct nv_host *host = host_set->private_data;
- for (i=0; i<host_set->n_ports; i++) {
- u8 intr_mask;
+ // Disable hotplug event interrupts.
+ if (host->host_desc->disable_hotplug)
+ host->host_desc->disable_hotplug(host_set);
- // Disable hotplug event interrupts.
- intr_mask = inb(host_set->ports[i]->ioaddr.scr_addr +
- NV_INT_ENABLE);
- intr_mask &= ~(NV_INT_ENABLE_HOTPLUG);
- outb(intr_mask, host_set->ports[i]->ioaddr.scr_addr +
- NV_INT_ENABLE);
- }
+ kfree(host);
}
static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version = 0;
+ struct nv_host *host;
struct ata_probe_ent *probe_ent = NULL;
- int i;
int rc;
if (!printed_version++)
goto err_out_regions;
}
+ host = kmalloc(sizeof(struct nv_host), GFP_KERNEL);
+ if (!host) {
+ rc = -ENOMEM;
+ goto err_out_free_ent;
+ }
+
+ host->host_desc = &nv_device_tbl[ent->driver_data];
+
memset(probe_ent, 0, sizeof(*probe_ent));
INIT_LIST_HEAD(&probe_ent->node);
ATA_FLAG_SATA_RESET |
ATA_FLAG_SRST |
ATA_FLAG_NO_LEGACY;
+
probe_ent->port_ops = &nv_ops;
probe_ent->n_ports = NV_PORTS;
probe_ent->irq = pdev->irq;
pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS;
probe_ent->port[0].bmdma_addr =
pci_resource_start(pdev, 4) | NV_PORT0_BMDMA_REG_OFFSET;
- probe_ent->port[0].scr_addr =
- pci_resource_start(pdev, 5) | NV_PORT0_SCR_REG_OFFSET;
probe_ent->port[1].cmd_addr = pci_resource_start(pdev, 2);
ata_std_ports(&probe_ent->port[1]);
pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS;
probe_ent->port[1].bmdma_addr =
pci_resource_start(pdev, 4) | NV_PORT1_BMDMA_REG_OFFSET;
- probe_ent->port[1].scr_addr =
- pci_resource_start(pdev, 5) | NV_PORT1_SCR_REG_OFFSET;
- pci_set_master(pdev);
+ probe_ent->private_data = host;
- rc = ata_device_add(probe_ent);
- if (rc != NV_PORTS)
- goto err_out_regions;
+ if (host->host_desc->host_flags & NV_HOST_FLAGS_SCR_MMIO) {
+ unsigned long base;
- // Enable hotplug event interrupts.
- for (i=0; i<probe_ent->n_ports; i++) {
- u8 intr_mask;
+ probe_ent->mmio_base = ioremap(pci_resource_start(pdev, 5),
+ pci_resource_len(pdev, 5));
+ if (probe_ent->mmio_base == NULL)
+ goto err_out_free_ent;
+
+ base = (unsigned long)probe_ent->mmio_base;
- outb(NV_INT_STATUS_HOTPLUG, probe_ent->port[i].scr_addr +
- NV_INT_STATUS);
+ probe_ent->port[0].scr_addr =
+ base + NV_PORT0_SCR_REG_OFFSET;
+ probe_ent->port[1].scr_addr =
+ base + NV_PORT1_SCR_REG_OFFSET;
+ } else {
- intr_mask = inb(probe_ent->port[i].scr_addr + NV_INT_ENABLE);
- intr_mask |= NV_INT_ENABLE_HOTPLUG;
- outb(intr_mask, probe_ent->port[i].scr_addr + NV_INT_ENABLE);
+ probe_ent->port[0].scr_addr =
+ pci_resource_start(pdev, 5) | NV_PORT0_SCR_REG_OFFSET;
+ probe_ent->port[1].scr_addr =
+ pci_resource_start(pdev, 5) | NV_PORT1_SCR_REG_OFFSET;
}
+ pci_set_master(pdev);
+
+ // Enable hotplug event interrupts.
+ if (host->host_desc->enable_hotplug)
+ host->host_desc->enable_hotplug(probe_ent);
+
+ rc = ata_device_add(probe_ent);
+ if (rc != NV_PORTS)
+ goto err_out_free_ent;
+
kfree(probe_ent);
return 0;
+err_out_free_ent:
+ kfree(probe_ent);
+
err_out_regions:
pci_release_regions(pdev);
return rc;
}
+static void nv_enable_hotplug(struct ata_probe_ent *probe_ent)
+{
+ u8 intr_mask;
+
+ outb(NV_INT_STATUS_HOTPLUG,
+ (unsigned long)probe_ent->mmio_base + NV_INT_STATUS);
+
+ intr_mask = inb((unsigned long)probe_ent->mmio_base + NV_INT_ENABLE);
+ intr_mask |= NV_INT_ENABLE_HOTPLUG;
+
+ outb(intr_mask, (unsigned long)probe_ent->mmio_base + NV_INT_ENABLE);
+}
+
+static void nv_disable_hotplug(struct ata_host_set *host_set)
+{
+ u8 intr_mask;
+
+ intr_mask = inb((unsigned long)host_set->mmio_base + NV_INT_ENABLE);
+
+ intr_mask &= ~(NV_INT_ENABLE_HOTPLUG);
+
+ outb(intr_mask, (unsigned long)host_set->mmio_base + NV_INT_ENABLE);
+}
+
+static void nv_check_hotplug(struct ata_host_set *host_set)
+{
+ u8 intr_status;
+
+ intr_status = inb((unsigned long)host_set->mmio_base + NV_INT_STATUS);
+
+ // Clear interrupt status.
+ outb(0xff, (unsigned long)host_set->mmio_base + NV_INT_STATUS);
+
+ if (intr_status & NV_INT_STATUS_HOTPLUG) {
+ if (intr_status & NV_INT_STATUS_PDEV_ADDED)
+ printk(KERN_WARNING "nv_sata: "
+ "Primary device added\n");
+
+ if (intr_status & NV_INT_STATUS_PDEV_REMOVED)
+ printk(KERN_WARNING "nv_sata: "
+ "Primary device removed\n");
+
+ if (intr_status & NV_INT_STATUS_SDEV_ADDED)
+ printk(KERN_WARNING "nv_sata: "
+ "Secondary device added\n");
+
+ if (intr_status & NV_INT_STATUS_SDEV_REMOVED)
+ printk(KERN_WARNING "nv_sata: "
+ "Secondary device removed\n");
+ }
+}
+
+static void nv_enable_hotplug_ck804(struct ata_probe_ent *probe_ent)
+{
+ u8 intr_mask;
+ u8 regval;
+
+ pci_read_config_byte(probe_ent->pdev, NV_MCP_SATA_CFG_20, ®val);
+ regval |= NV_MCP_SATA_CFG_20_SATA_SPACE_EN;
+ pci_write_config_byte(probe_ent->pdev, NV_MCP_SATA_CFG_20, regval);
+
+ writeb(NV_INT_STATUS_HOTPLUG, probe_ent->mmio_base + NV_INT_STATUS_CK804);
+
+ intr_mask = readb(probe_ent->mmio_base + NV_INT_ENABLE_CK804);
+ intr_mask |= NV_INT_ENABLE_HOTPLUG;
+
+ writeb(intr_mask, probe_ent->mmio_base + NV_INT_ENABLE_CK804);
+}
+
+static void nv_disable_hotplug_ck804(struct ata_host_set *host_set)
+{
+ u8 intr_mask;
+ u8 regval;
+
+ intr_mask = readb(host_set->mmio_base + NV_INT_ENABLE_CK804);
+
+ intr_mask &= ~(NV_INT_ENABLE_HOTPLUG);
+
+ writeb(intr_mask, host_set->mmio_base + NV_INT_ENABLE_CK804);
+
+ pci_read_config_byte(host_set->pdev, NV_MCP_SATA_CFG_20, ®val);
+ regval &= ~NV_MCP_SATA_CFG_20_SATA_SPACE_EN;
+ pci_write_config_byte(host_set->pdev, NV_MCP_SATA_CFG_20, regval);
+}
+
+static void nv_check_hotplug_ck804(struct ata_host_set *host_set)
+{
+ u8 intr_status;
+
+ intr_status = readb(host_set->mmio_base + NV_INT_STATUS_CK804);
+
+ // Clear interrupt status.
+ writeb(0xff, host_set->mmio_base + NV_INT_STATUS_CK804);
+
+ if (intr_status & NV_INT_STATUS_HOTPLUG) {
+ if (intr_status & NV_INT_STATUS_PDEV_ADDED)
+ printk(KERN_WARNING "nv_sata: "
+ "Primary device added\n");
+
+ if (intr_status & NV_INT_STATUS_PDEV_REMOVED)
+ printk(KERN_WARNING "nv_sata: "
+ "Primary device removed\n");
+
+ if (intr_status & NV_INT_STATUS_SDEV_ADDED)
+ printk(KERN_WARNING "nv_sata: "
+ "Secondary device added\n");
+
+ if (intr_status & NV_INT_STATUS_SDEV_REMOVED)
+ printk(KERN_WARNING "nv_sata: "
+ "Secondary device removed\n");
+ }
+}
+
static int __init nv_init(void)
{
return pci_module_init(&nv_pci_driver);
static void pdc_20621_phy_reset (struct ata_port *ap);
static int pdc_port_start(struct ata_port *ap);
static void pdc_port_stop(struct ata_port *ap);
-static void pdc20621_fill_sg(struct ata_queued_cmd *qc);
+static void pdc20621_qc_prep(struct ata_queued_cmd *qc);
static void pdc_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf);
static void pdc_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf);
static void pdc20621_host_stop(struct ata_host_set *host_set);
#endif
static void pdc20621_put_to_dimm(struct ata_probe_ent *pe,
void *psource, u32 offset, u32 size);
+static void pdc20621_irq_clear(struct ata_port *ap);
static Scsi_Host_Template pdc_sata_sht = {
.phy_reset = pdc_20621_phy_reset,
.bmdma_setup = pdc20621_dma_setup,
.bmdma_start = pdc20621_dma_start,
- .fill_sg = pdc20621_fill_sg,
+ .qc_prep = pdc20621_qc_prep,
+ .qc_issue = ata_qc_issue_prot,
.eng_timeout = pdc_eng_timeout,
.irq_handler = pdc20621_interrupt,
+ .irq_clear = pdc20621_irq_clear,
.port_start = pdc_port_start,
.port_stop = pdc_port_stop,
.host_stop = pdc20621_host_stop,
buf32[dw + 3]);
}
-static void pdc20621_fill_sg(struct ata_queued_cmd *qc)
+static void pdc20621_qc_prep(struct ata_queued_cmd *qc)
{
struct scatterlist *sg = qc->sg;
struct ata_port *ap = qc->ap;
unsigned int i, last, idx, total_len = 0, sgt_len;
u32 *buf = (u32 *) &pp->dimm_buf[PDC_DIMM_HEADER_SZ];
+ if (!(qc->flags & ATA_QCFLAG_DMAMAP))
+ return;
+
VPRINTK("ata%u: ENTER\n", ap->id);
/* hard-code chip #0 */
return handled;
}
+static void pdc20621_irq_clear(struct ata_port *ap)
+{
+ struct ata_host_set *host_set = ap->host_set;
+ void *mmio = host_set->mmio_base;
+
+ mmio += PDC_CHIP0_OFS;
+
+ readl(mmio + PDC_20621_SEQMASK);
+}
+
static irqreturn_t pdc20621_interrupt (int irq, void *dev_instance, struct pt_regs *regs)
{
struct ata_host_set *host_set = dev_instance;
return &sym_fw1;
#endif
else
- return 0;
+ return NULL;
}
/*
}
/* Revert everything */
- SYM_UCMD_PTR(cmd)->eh_wait = 0;
+ SYM_UCMD_PTR(cmd)->eh_wait = NULL;
cmd->scsi_done = ep->old_done;
/* Wake up the eh thread if it wants to sleep */
/* On error, restore everything and cross fingers :) */
if (sts) {
- SYM_UCMD_PTR(cmd)->eh_wait = 0;
+ SYM_UCMD_PTR(cmd)->eh_wait = NULL;
cmd->scsi_done = ep->old_done;
to_do = SYM_EH_DO_IGNORE;
}
char **start, off_t offset, int length, int func)
{
struct host_data *host_data;
- struct sym_hcb *np = 0;
+ struct sym_hcb *np = NULL;
int retv;
host_data = (struct host_data *) host->hostdata;
static struct sym_driver_setup
sym_driver_safe_setup __initdata = SYM_LINUX_DRIVER_SAFE_SETUP;
#ifdef MODULE
-char *sym53c8xx = 0; /* command line passed by insmod */
+char *sym53c8xx; /* command line passed by insmod */
MODULE_PARM(sym53c8xx, "s");
#endif
static __inline m_addr_t sym_m_get_dma_mem_cluster(m_pool_p mp, m_vtob_p vbp)
{
- void *vaddr = 0;
+ void *vaddr = NULL;
dma_addr_t baddr = 0;
vaddr = pci_alloc_consistent(mp->dev_dmat,SYM_MEM_CLUSTER_SIZE, &baddr);
} else {
script_ofs = dsp;
script_size = 0;
- script_base = 0;
+ script_base = NULL;
script_name = "mem";
}
return chip;
}
- return 0;
+ return NULL;
}
#if SYM_CONF_DMA_ADDRESSING_MODE == 2
* try to find the interrupted script command,
* and the address at which to continue.
*/
- vdsp = 0;
+ vdsp = NULL;
nxtdsp = 0;
if (dsp > np->scripta_ba &&
dsp <= np->scripta_ba + np->scripta_sz) {
* we are not in race.
*/
i = 0;
- cp = 0;
+ cp = NULL;
FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) {
cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
if (cp->host_status != HS_BUSY &&
* abort for this target.
*/
i = 0;
- cp = 0;
+ cp = NULL;
FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) {
cp = sym_que_entry(qp, struct sym_ccb, link_ccbq);
if (cp->host_status != HS_DISCONNECT)
else if (dp_scr == SCRIPTA_BA (np, pm1_data))
pm = &cp->phys.pm1;
else
- pm = 0;
+ pm = NULL;
if (pm) {
dp_scr = scr_to_cpu(pm->ret);
* used for negotiation, clear this info in the tcb.
*/
if (cp == tp->nego_cp)
- tp->nego_cp = 0;
+ tp->nego_cp = NULL;
#ifdef SYM_CONF_IARB_SUPPORT
/*
/*
* Make this CCB available.
*/
- cp->cam_ccb = 0;
+ cp->cam_ccb = NULL;
cp->host_status = HS_IDLE;
sym_remque(&cp->link_ccbq);
sym_insque_head(&cp->link_ccbq, &np->free_ccbq);
*/
static ccb_p sym_alloc_ccb(hcb_p np)
{
- ccb_p cp = 0;
+ ccb_p cp = NULL;
int hcode;
/*
* queue to the controller.
*/
if (np->actccbs >= SYM_CONF_MAX_START)
- return 0;
+ return NULL;
/*
* Allocate memory for this CCB.
sym_mfree_dma(cp->sns_bbuf,SYM_SNS_BBUF_LEN,"SNS_BBUF");
sym_mfree_dma(cp, sizeof(*cp), "CCB");
}
- return 0;
+ return NULL;
}
/*
* allocation for not probed LUNs.
*/
if (!sym_is_bit(tp->lun_map, ln))
- return 0;
+ return NULL;
/*
* Initialize the target control block if not yet.
lp->cb_tags = sym_calloc(SYM_CONF_MAX_TASK, "CB_TAGS");
if (!lp->cb_tags) {
sym_mfree_dma(lp->itlq_tbl, SYM_CONF_MAX_TASK*4, "ITLQ_TBL");
- lp->itlq_tbl = 0;
+ lp->itlq_tbl = NULL;
goto fail;
}
/*
* Look up our CCB control block.
*/
- cp = 0;
+ cp = NULL;
FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) {
ccb_p cp2 = sym_que_entry(qp, struct sym_ccb, link_ccbq);
if (cp2->cam_ccb == ccb) {
* LUN(s) > 0.
*/
#if SYM_CONF_MAX_LUN <= 1
-#define sym_lp(np, tp, lun) (!lun) ? (tp)->lun0p : 0
+#define sym_lp(np, tp, lun) (!lun) ? (tp)->lun0p : NULL
#else
#define sym_lp(np, tp, lun) \
- (!lun) ? (tp)->lun0p : (tp)->lunmp ? (tp)->lunmp[(lun)] : 0
+ (!lun) ? (tp)->lun0p : (tp)->lunmp ? (tp)->lunmp[(lun)] : NULL
#endif
/*
m_link_p h = mp->h;
if (size > SYM_MEM_CLUSTER_SIZE)
- return 0;
+ return NULL;
while (size > s) {
s <<= 1;
if (s == SYM_MEM_CLUSTER_SIZE) {
h[j].next = (m_link_p) M_GET_MEM_CLUSTER();
if (h[j].next)
- h[j].next->next = 0;
+ h[j].next->next = NULL;
break;
}
++j;
j -= 1;
s >>= 1;
h[j].next = (m_link_p) (a+s);
- h[j].next->next = 0;
+ h[j].next->next = NULL;
}
}
#ifdef DEBUG
#ifdef SYM_MEM_FREE_UNUSED
static struct sym_m_pool mp0 =
- {0, ___mp0_get_mem_cluster, ___mp0_free_mem_cluster};
+ {NULL, ___mp0_get_mem_cluster, ___mp0_free_mem_cluster};
#else
static struct sym_m_pool mp0 =
- {0, ___mp0_get_mem_cluster};
+ {NULL, ___mp0_get_mem_cluster};
#endif
/*
/* Create a new memory DMAable pool (when fetch failed) */
static m_pool_p ___cre_dma_pool(m_pool_ident_t dev_dmat)
{
- m_pool_p mp = 0;
+ m_pool_p mp = NULL;
mp = __sym_calloc(&mp0, sizeof(*mp), "MPOOL");
if (mp) {
}
if (mp)
__sym_mfree(&mp0, mp, sizeof(*mp), "MPOOL");
- return 0;
+ return NULL;
}
#ifdef SYM_MEM_FREE_UNUSED
void *__sym_calloc_dma_unlocked(m_pool_ident_t dev_dmat, int size, char *name)
{
m_pool_p mp;
- void *m = 0;
+ void *m = NULL;
mp = ___get_dma_pool(dev_dmat);
if (!mp)
{
m_pool_p mp;
int hc = VTOB_HASH_CODE(m);
- m_vtob_p vp = 0;
+ m_vtob_p vp = NULL;
m_addr_t a = ((m_addr_t) m) & ~SYM_MEM_CLUSTER_MASK;
mp = ___get_dma_pool(dev_dmat);
if (elem != head)
__sym_que_del(head, elem->flink);
else
- elem = 0;
+ elem = NULL;
return elem;
}
u_char *gpcntl)
{
OUTB (nc_gpcntl, *gpcntl & 0xfe);
- S24C16_do_bit(np, 0, write_bit, gpreg);
+ S24C16_do_bit(np, NULL, write_bit, gpreg);
OUTB (nc_gpcntl, *gpcntl);
}
int x;
for (x = 0; x < 8; x++)
- S24C16_do_bit(np, 0, (write_data >> (7 - x)) & 0x01, gpreg);
+ S24C16_do_bit(np, NULL, (write_data >> (7 - x)) & 0x01, gpreg);
S24C16_read_ack(np, ack_data, gpreg, gpcntl);
}
--- /dev/null
+/*
+ * linux/drivers/serial/cpm_uart.h
+ *
+ * Driver for CPM (SCC/SMC) serial ports
+ *
+ * Copyright (C) 2004 Freescale Semiconductor, Inc.
+ *
+ */
+#ifndef CPM_UART_H
+#define CPM_UART_H
+
+#include <linux/config.h>
+
+#if defined(CONFIG_CPM2)
+#include "cpm_uart_cpm2.h"
+#elif defined(CONFIG_8xx)
+#include "cpm_uart_cpm1.h"
+#endif
+
+#ifndef CONFIG_SERIAL_8250
+#define SERIAL_CPM_MAJOR TTY_MAJOR
+#define SERIAL_CPM_MINOR 64
+#else
+#define SERIAL_CPM_MAJOR 204
+#define SERIAL_CPM_MINOR 42
+#endif
+
+#define IS_SMC(pinfo) (pinfo->flags & FLAG_SMC)
+#define IS_DISCARDING(pinfo) (pinfo->flags & FLAG_DISCARDING)
+#define FLAG_DISCARDING 0x00000004 /* when set, don't discard */
+#define FLAG_SMC 0x00000002
+#define FLAG_CONSOLE 0x00000001
+
+#define UART_SMC1 0
+#define UART_SMC2 1
+#define UART_SCC1 2
+#define UART_SCC2 3
+#define UART_SCC3 4
+#define UART_SCC4 5
+
+#define UART_NR 6
+
+#define RX_NUM_FIFO 4
+#define RX_BUF_SIZE 32
+#define TX_NUM_FIFO 4
+#define TX_BUF_SIZE 32
+
+struct uart_cpm_port {
+ struct uart_port port;
+ u16 rx_nrfifos;
+ u16 rx_fifosize;
+ u16 tx_nrfifos;
+ u16 tx_fifosize;
+ smc_t *smcp;
+ smc_uart_t *smcup;
+ scc_t *sccp;
+ scc_uart_t *sccup;
+ volatile cbd_t *rx_bd_base;
+ volatile cbd_t *rx_cur;
+ volatile cbd_t *tx_bd_base;
+ volatile cbd_t *tx_cur;
+ unsigned char *tx_buf;
+ unsigned char *rx_buf;
+ u32 flags;
+ void (*set_lineif)(struct uart_cpm_port *);
+ u8 brg;
+ uint dp_addr;
+ void *mem_addr;
+ dma_addr_t dma_addr;
+ /* helpers */
+ int baud;
+ int bits;
+};
+
+extern int cpm_uart_port_map[UART_NR];
+extern int cpm_uart_nr;
+extern struct uart_cpm_port cpm_uart_ports[UART_NR];
+
+/* these are located in their respective files */
+void cpm_line_cr_cmd(int line, int cmd);
+int cpm_uart_init_portdesc(void);
+int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con);
+void cpm_uart_freebuf(struct uart_cpm_port *pinfo);
+
+void smc1_lineif(struct uart_cpm_port *pinfo);
+void smc2_lineif(struct uart_cpm_port *pinfo);
+void scc1_lineif(struct uart_cpm_port *pinfo);
+void scc2_lineif(struct uart_cpm_port *pinfo);
+void scc3_lineif(struct uart_cpm_port *pinfo);
+void scc4_lineif(struct uart_cpm_port *pinfo);
+
+#endif /* CPM_UART_H */
/**************************************************************/
static int cpm_uart_tx_pump(struct uart_port *port);
-static void cpm_uart_init_smc(struct uart_cpm_port *pinfo, int bits, u16 cval);
-static void cpm_uart_init_scc(struct uart_cpm_port *pinfo, int sbits, u16 sval);
+static void cpm_uart_init_smc(struct uart_cpm_port *pinfo);
+static void cpm_uart_init_scc(struct uart_cpm_port *pinfo);
+static void cpm_uart_initbd(struct uart_cpm_port *pinfo);
/**************************************************************/
pr_debug("CPM uart[%d]:start tx\n", port->line);
- /* if in the middle of discarding return */
- if (IS_DISCARDING(pinfo))
- return;
-
if (IS_SMC(pinfo)) {
if (smcp->smc_smcm & SMCM_TX)
return;
static int cpm_uart_startup(struct uart_port *port)
{
int retval;
+ struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
pr_debug("CPM uart[%d]:startup\n", port->line);
if (retval)
return retval;
+ /* Startup rx-int */
+ if (IS_SMC(pinfo)) {
+ pinfo->smcp->smc_smcm |= SMCM_RX;
+ pinfo->smcp->smc_smcmr |= SMCMR_REN;
+ } else {
+ pinfo->sccp->scc_sccm |= UART_SCCM_RX;
+ }
+
return 0;
}
}
/* Shut them really down and reinit buffer descriptors */
- cpm_line_cr_cmd(line, CPM_CR_INIT_TRX);
+ cpm_line_cr_cmd(line, CPM_CR_STOP_TX);
+ cpm_uart_initbd(pinfo);
}
}
{
int baud;
unsigned long flags;
- u16 cval, scval;
+ u16 cval, scval, prev_mode;
int bits, sbits;
struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
- int line = pinfo - cpm_uart_ports;
- volatile cbd_t *bdp;
+ volatile smc_t *smcp = pinfo->smcp;
+ volatile scc_t *sccp = pinfo->sccp;
pr_debug("CPM uart[%d]:set_termios\n", port->line);
- spin_lock_irqsave(&port->lock, flags);
- /* disable uart interrupts */
- if (IS_SMC(pinfo))
- pinfo->smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX);
- else
- pinfo->sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX);
- pinfo->flags |= FLAG_DISCARDING;
- spin_unlock_irqrestore(&port->lock, flags);
-
- /* if previous configuration exists wait for tx to finish */
- if (pinfo->baud != 0 && pinfo->bits != 0) {
-
- /* point to the last txed bd */
- bdp = pinfo->tx_cur;
- if (bdp == pinfo->tx_bd_base)
- bdp = pinfo->tx_bd_base + (pinfo->tx_nrfifos - 1);
- else
- bdp--;
-
- /* wait for it to be transmitted */
- while ((bdp->cbd_sc & BD_SC_READY) != 0)
- schedule();
-
- /* and delay for the hw fifo to drain */
- udelay((3 * 1000000 * pinfo->bits) / pinfo->baud);
- }
-
- spin_lock_irqsave(&port->lock, flags);
-
- /* Send the CPM an initialize command. */
- cpm_line_cr_cmd(line, CPM_CR_STOP_TX);
-
- /* Stop uart */
- if (IS_SMC(pinfo))
- pinfo->smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
- else
- pinfo->sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
-
- /* Send the CPM an initialize command. */
- cpm_line_cr_cmd(line, CPM_CR_INIT_TRX);
-
- spin_unlock_irqrestore(&port->lock, flags);
-
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
/* Character length programmed into the mode register is the
spin_lock_irqsave(&port->lock, flags);
- cpm_set_brg(pinfo->brg - 1, baud);
-
/* Start bit has not been added (so don't, because we would just
* subtract it later), and we need to add one for the number of
* stops bits (there is always at least one).
*/
bits++;
+ if (IS_SMC(pinfo)) {
+ /* Set the mode register. We want to keep a copy of the
+ * enables, because we want to put them back if they were
+ * present.
+ */
+ prev_mode = smcp->smc_smcmr;
+ smcp->smc_smcmr = smcr_mk_clen(bits) | cval | SMCMR_SM_UART;
+ smcp->smc_smcmr |= (prev_mode & (SMCMR_REN | SMCMR_TEN));
+ } else {
+ sccp->scc_psmr = (sbits << 12) | scval;
+ }
- /* re-init */
- if (IS_SMC(pinfo))
- cpm_uart_init_smc(pinfo, bits, cval);
- else
- cpm_uart_init_scc(pinfo, sbits, scval);
-
- pinfo->baud = baud;
- pinfo->bits = bits;
-
- pinfo->flags &= ~FLAG_DISCARDING;
+ cpm_set_brg(pinfo->brg - 1, baud);
spin_unlock_irqrestore(&port->lock, flags);
}
return 1;
}
-static void cpm_uart_init_scc(struct uart_cpm_port *pinfo, int bits, u16 scval)
+/*
+ * init buffer descriptors
+ */
+static void cpm_uart_initbd(struct uart_cpm_port *pinfo)
{
- int line = pinfo - cpm_uart_ports;
- volatile scc_t *scp;
- volatile scc_uart_t *sup;
+ int i;
u8 *mem_addr;
volatile cbd_t *bdp;
- int i;
- pr_debug("CPM uart[%d]:init_scc\n", pinfo->port.line);
-
- scp = pinfo->sccp;
- sup = pinfo->sccup;
+ pr_debug("CPM uart[%d]:initbd\n", pinfo->port.line);
/* Set the physical address of the host memory
* buffers in the buffer descriptors, and the
* virtual address for us to work with.
*/
- pinfo->rx_cur = pinfo->rx_bd_base;
mem_addr = pinfo->mem_addr;
- for (bdp = pinfo->rx_bd_base, i = 0; i < pinfo->rx_nrfifos; i++, bdp++) {
+ bdp = pinfo->rx_cur = pinfo->rx_bd_base;
+ for (i = 0; i < (pinfo->rx_nrfifos - 1); i++, bdp++) {
bdp->cbd_bufaddr = virt_to_bus(mem_addr);
- bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT | (i < (pinfo->rx_nrfifos - 1) ? 0 : BD_SC_WRAP);
+ bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT;
mem_addr += pinfo->rx_fifosize;
}
+
+ bdp->cbd_bufaddr = virt_to_bus(mem_addr);
+ bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT;
/* Set the physical address of the host memory
* buffers in the buffer descriptors, and the
* virtual address for us to work with.
*/
mem_addr = pinfo->mem_addr + L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize);
- pinfo->tx_cur = pinfo->tx_bd_base;
- for (bdp = pinfo->tx_bd_base, i = 0; i < pinfo->tx_nrfifos; i++, bdp++) {
+ bdp = pinfo->tx_cur = pinfo->tx_bd_base;
+ for (i = 0; i < (pinfo->tx_nrfifos - 1); i++, bdp++) {
bdp->cbd_bufaddr = virt_to_bus(mem_addr);
- bdp->cbd_sc = BD_SC_INTRPT | (i < (pinfo->tx_nrfifos - 1) ? 0 : BD_SC_WRAP);
+ bdp->cbd_sc = BD_SC_INTRPT;
mem_addr += pinfo->tx_fifosize;
- bdp++;
}
+
+ bdp->cbd_bufaddr = virt_to_bus(mem_addr);
+ bdp->cbd_sc = BD_SC_WRAP | BD_SC_INTRPT;
+}
+
+static void cpm_uart_init_scc(struct uart_cpm_port *pinfo)
+{
+ int line = pinfo - cpm_uart_ports;
+ volatile scc_t *scp;
+ volatile scc_uart_t *sup;
+
+ pr_debug("CPM uart[%d]:init_scc\n", pinfo->port.line);
+
+ scp = pinfo->sccp;
+ sup = pinfo->sccup;
/* Store address */
pinfo->sccup->scc_genscc.scc_rbase = (unsigned char *)pinfo->rx_bd_base - DPRAM_BASE;
(SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16);
/* Enable rx interrupts and clear all pending events. */
- scp->scc_sccm = UART_SCCM_RX;
+ scp->scc_sccm = 0;
scp->scc_scce = 0xffff;
scp->scc_dsr = 0x7e7e;
- scp->scc_psmr = (bits << 12) | scval;
+ scp->scc_psmr = 0x3000;
scp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT);
}
-static void cpm_uart_init_smc(struct uart_cpm_port *pinfo, int bits, u16 cval)
+static void cpm_uart_init_smc(struct uart_cpm_port *pinfo)
{
int line = pinfo - cpm_uart_ports;
volatile smc_t *sp;
volatile smc_uart_t *up;
- volatile u8 *mem_addr;
- volatile cbd_t *bdp;
- int i;
pr_debug("CPM uart[%d]:init_smc\n", pinfo->port.line);
sp = pinfo->smcp;
up = pinfo->smcup;
- /* Set the physical address of the host memory
- * buffers in the buffer descriptors, and the
- * virtual address for us to work with.
- */
- mem_addr = pinfo->mem_addr;
- pinfo->rx_cur = pinfo->rx_bd_base;
- for (bdp = pinfo->rx_bd_base, i = 0; i < pinfo->rx_nrfifos; i++, bdp++) {
- bdp->cbd_bufaddr = virt_to_bus(mem_addr);
- bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT | (i < (pinfo->rx_nrfifos - 1) ? 0 : BD_SC_WRAP);
- mem_addr += pinfo->rx_fifosize;
- }
-
- /* Set the physical address of the host memory
- * buffers in the buffer descriptors, and the
- * virtual address for us to work with.
- */
- mem_addr = pinfo->mem_addr + L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize);
- pinfo->tx_cur = pinfo->tx_bd_base;
- for (bdp = pinfo->tx_bd_base, i = 0; i < pinfo->tx_nrfifos; i++, bdp++) {
- bdp->cbd_bufaddr = virt_to_bus(mem_addr);
- bdp->cbd_sc = BD_SC_INTRPT | (i < (pinfo->tx_nrfifos - 1) ? 0 : BD_SC_WRAP);
- mem_addr += pinfo->tx_fifosize;
- }
-
/* Store address */
pinfo->smcup->smc_rbase = (u_char *)pinfo->rx_bd_base - DPRAM_BASE;
pinfo->smcup->smc_tbase = (u_char *)pinfo->tx_bd_base - DPRAM_BASE;
cpm_line_cr_cmd(line, CPM_CR_INIT_TRX);
- /* Set UART mode, according to the parameters */
- sp->smc_smcmr = smcr_mk_clen(bits) | cval | SMCMR_SM_UART;
+ /* Set UART mode, 8 bit, no parity, one stop.
+ * Enable receive and transmit.
+ */
+ sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART;
/* Enable only rx interrupts clear all pending events. */
- sp->smc_smcm = SMCM_RX;
+ sp->smc_smcm = 0;
sp->smc_smce = 0xff;
sp->smc_smcmr |= (SMCMR_REN | SMCMR_TEN);
if (pinfo->set_lineif)
pinfo->set_lineif(pinfo);
+ if (IS_SMC(pinfo)) {
+ pinfo->smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX);
+ pinfo->smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
+ } else {
+ pinfo->sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX);
+ pinfo->sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
+ }
+
ret = cpm_uart_allocbuf(pinfo, 0);
+
if (ret)
return ret;
+ cpm_uart_initbd(pinfo);
+
return 0;
}
volatile cbd_t *bdp, *bdbase;
volatile unsigned char *cp;
- if (IS_DISCARDING(pinfo))
- return;
-
/* Get the address of the host memory buffer.
*/
bdp = pinfo->tx_cur;
if (pinfo->set_lineif)
pinfo->set_lineif(pinfo);
+ if (IS_SMC(pinfo)) {
+ pinfo->smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX);
+ pinfo->smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
+ } else {
+ pinfo->sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX);
+ pinfo->sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
+ }
+
ret = cpm_uart_allocbuf(pinfo, 1);
+
if (ret)
return ret;
+ cpm_uart_initbd(pinfo);
+
+ if (IS_SMC(pinfo))
+ cpm_uart_init_smc(pinfo);
+ else
+ cpm_uart_init_scc(pinfo);
+
uart_set_options(port, co, baud, parity, bits, flow);
return 0;
{
int dpmemsz, memsz;
u8 *dp_mem;
- uint dp_addr;
+ uint dp_offset;
u8 *mem_addr;
- dma_addr_t dma_addr;
+ dma_addr_t dma_addr = 0;
pr_debug("CPM uart[%d]:allocbuf\n", pinfo->port.line);
dpmemsz = sizeof(cbd_t) * (pinfo->rx_nrfifos + pinfo->tx_nrfifos);
- dp_mem = m8xx_cpm_dpalloc(dpmemsz);
- if (dp_mem == NULL) {
+ dp_offset = cpm_dpalloc(dpmemsz, 8);
+ if (IS_DPERR(dp_offset)) {
printk(KERN_ERR
"cpm_uart_cpm1.c: could not allocate buffer descriptors\n");
return -ENOMEM;
}
- dp_addr = m8xx_cpm_dpram_offset(dp_mem);
+ dp_mem = cpm_dpram_addr(dp_offset);
memsz = L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize) +
L1_CACHE_ALIGN(pinfo->tx_nrfifos * pinfo->tx_fifosize);
GFP_KERNEL);
if (mem_addr == NULL) {
- m8xx_cpm_dpfree(dp_mem);
+ cpm_dpfree(dp_offset);
printk(KERN_ERR
"cpm_uart_cpm1.c: could not allocate coherent memory\n");
return -ENOMEM;
}
- pinfo->dp_addr = dp_addr;
+ pinfo->dp_addr = dp_offset;
pinfo->mem_addr = mem_addr;
pinfo->dma_addr = dma_addr;
pinfo->tx_fifosize), pinfo->mem_addr,
pinfo->dma_addr);
- m8xx_cpm_dpfree(m8xx_cpm_dpram_addr(pinfo->dp_addr));
+ cpm_dpfree(pinfo->dp_addr);
}
/* Setup any dynamic params in the uart desc */
static inline void cpm_set_brg(int brg, int baud)
{
- m8xx_cpm_setbrg(brg, baud);
+ cpm_setbrg(brg, baud);
}
static inline void cpm_set_scc_fcr(volatile scc_uart_t * sup)
{
int dpmemsz, memsz;
u8 *dp_mem;
- uint dp_addr;
+ uint dp_offset;
u8 *mem_addr;
dma_addr_t dma_addr = 0;
pr_debug("CPM uart[%d]:allocbuf\n", pinfo->port.line);
dpmemsz = sizeof(cbd_t) * (pinfo->rx_nrfifos + pinfo->tx_nrfifos);
- dp_mem = cpm2_dpalloc(dpmemsz, 8);
- if (dp_mem == NULL) {
+ dp_offset = cpm_dpalloc(dpmemsz, 8);
+ if (IS_DPERR(dp_offset)) {
printk(KERN_ERR
- "cpm_uart_cpm1.c: could not allocate buffer descriptors\n");
+ "cpm_uart_cpm.c: could not allocate buffer descriptors\n");
return -ENOMEM;
}
- dp_addr = cpm2_dpram_offset(dp_mem);
+ dp_mem = cpm_dpram_addr(dp_offset);
memsz = L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize) +
L1_CACHE_ALIGN(pinfo->tx_nrfifos * pinfo->tx_fifosize);
GFP_KERNEL);
if (mem_addr == NULL) {
- cpm2_dpfree(dp_mem);
+ cpm_dpfree(dp_offset);
printk(KERN_ERR
- "cpm_uart_cpm1.c: could not allocate coherent memory\n");
+ "cpm_uart_cpm.c: could not allocate coherent memory\n");
return -ENOMEM;
}
- pinfo->dp_addr = dp_addr;
+ pinfo->dp_addr = dp_offset;
pinfo->mem_addr = mem_addr;
pinfo->dma_addr = dma_addr;
pinfo->tx_fifosize), pinfo->mem_addr,
pinfo->dma_addr);
- cpm2_dpfree(&pinfo->dp_addr);
+ cpm_dpfree(pinfo->dp_addr);
}
/* Setup any dynamic params in the uart desc */
static inline void cpm_set_brg(int brg, int baud)
{
- cpm2_setbrg(brg, baud);
+ cpm_setbrg(brg, baud);
}
static inline void cpm_set_scc_fcr(volatile scc_uart_t * sup)
--- /dev/null
+/*
+ * drivers/serial/mpc52xx_uart.c
+ *
+ * Driver for the PSC of the Freescale MPC52xx PSCs configured as UARTs.
+ *
+ * FIXME According to the usermanual the status bits in the status register
+ * are only updated when the peripherals access the FIFO and not when the
+ * CPU access them. So since we use this bits to know when we stop writing
+ * and reading, they may not be updated in-time and a race condition may
+ * exists. But I haven't be able to prove this and I don't care. But if
+ * any problem arises, it might worth checking. The TX/RX FIFO Stats
+ * registers should be used in addition.
+ * Update: Actually, they seem updated ... At least the bits we use.
+ *
+ *
+ * Maintainer : Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Some of the code has been inspired/copied from the 2.4 code written
+ * by Dale Farnsworth <dfarnsworth@mvista.com>.
+ *
+ * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2003 MontaVista, Software, Inc.
+ *
+ * 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.
+ */
+
+/* OCP Usage :
+ *
+ * This drivers uses the OCP model. To load the serial driver for one of the
+ * PSCs, just add this to the core_ocp table :
+ *
+ * {
+ * .vendor = OCP_VENDOR_FREESCALE,
+ * .function = OCP_FUNC_PSC_UART,
+ * .index = 0,
+ * .paddr = MPC52xx_PSC1,
+ * .irq = MPC52xx_PSC1_IRQ,
+ * .pm = OCP_CPM_NA,
+ * },
+ *
+ * This is for PSC1, replace the paddr and irq according to the PSC you want to
+ * use. The driver all necessary registers to place the PSC in uart mode without
+ * DCD. However, the pin multiplexing aren't changed and should be set either
+ * by the bootloader or in the platform init code.
+ * The index field must be equal to the PSC index ( e.g. 0 for PSC1, 1 for PSC2,
+ * and so on). So the PSC1 is mapped to /dev/ttyS0, PSC2 to /dev/ttyS1 and so
+ * on. But be warned, it's an ABSOLUTE REQUIREMENT ! This is needed mainly for
+ * the console code : without this 1:1 mapping, at early boot time, when we are
+ * parsing the kernel args console=ttyS?, we wouldn't know wich PSC it will be
+ * mapped to because OCP stuff is not yet initialized.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/sysrq.h>
+#include <linux/console.h>
+
+#include <asm/delay.h>
+#include <asm/io.h>
+#include <asm/ocp.h>
+
+#include <asm/mpc52xx.h>
+#include <asm/mpc52xx_psc.h>
+
+#if defined(CONFIG_SERIAL_MPC52xx_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+
+
+
+#define ISR_PASS_LIMIT 256 /* Max number of iteration in the interrupt */
+
+
+static struct uart_port mpc52xx_uart_ports[MPC52xx_PSC_MAXNUM];
+ /* Rem: - We use the read_status_mask as a shadow of
+ * psc->mpc52xx_psc_imr
+ * - It's important that is array is all zero on start as we
+ * use it to know if it's initialized or not ! If it's not sure
+ * it's cleared, then a memset(...,0,...) should be added to
+ * the console_init
+ */
+
+#define PSC(port) ((struct mpc52xx_psc *)((port)->membase))
+
+
+/* Forward declaration of the interruption handling routine */
+static irqreturn_t mpc52xx_uart_int(int irq,void *dev_id,struct pt_regs *regs);
+
+
+/* Simple macro to test if a port is console or not. This one is taken
+ * for serial_core.c and maybe should be moved to serial_core.h ? */
+#ifdef CONFIG_SERIAL_CORE_CONSOLE
+#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line)
+#else
+#define uart_console(port) (0)
+#endif
+
+
+/* ======================================================================== */
+/* UART operations */
+/* ======================================================================== */
+
+static unsigned int
+mpc52xx_uart_tx_empty(struct uart_port *port)
+{
+ int status = in_be16(&PSC(port)->mpc52xx_psc_status);
+ return (status & MPC52xx_PSC_SR_TXEMP) ? TIOCSER_TEMT : 0;
+}
+
+static void
+mpc52xx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ /* Not implemented */
+}
+
+static unsigned int
+mpc52xx_uart_get_mctrl(struct uart_port *port)
+{
+ /* Not implemented */
+ return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+static void
+mpc52xx_uart_stop_tx(struct uart_port *port, unsigned int tty_stop)
+{
+ /* port->lock taken by caller */
+ port->read_status_mask &= ~MPC52xx_PSC_IMR_TXRDY;
+ out_be16(&PSC(port)->mpc52xx_psc_imr,port->read_status_mask);
+}
+
+static void
+mpc52xx_uart_start_tx(struct uart_port *port, unsigned int tty_start)
+{
+ /* port->lock taken by caller */
+ port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY;
+ out_be16(&PSC(port)->mpc52xx_psc_imr,port->read_status_mask);
+}
+
+static void
+mpc52xx_uart_send_xchar(struct uart_port *port, char ch)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&port->lock, flags);
+
+ port->x_char = ch;
+ if (ch) {
+ /* Make sure tx interrupts are on */
+ /* Truly necessary ??? They should be anyway */
+ port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY;
+ out_be16(&PSC(port)->mpc52xx_psc_imr,port->read_status_mask);
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void
+mpc52xx_uart_stop_rx(struct uart_port *port)
+{
+ /* port->lock taken by caller */
+ port->read_status_mask &= ~MPC52xx_PSC_IMR_RXRDY;
+ out_be16(&PSC(port)->mpc52xx_psc_imr,port->read_status_mask);
+}
+
+static void
+mpc52xx_uart_enable_ms(struct uart_port *port)
+{
+ /* Not implemented */
+}
+
+static void
+mpc52xx_uart_break_ctl(struct uart_port *port, int ctl)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&port->lock, flags);
+
+ if ( ctl == -1 )
+ out_8(&PSC(port)->command,MPC52xx_PSC_START_BRK);
+ else
+ out_8(&PSC(port)->command,MPC52xx_PSC_STOP_BRK);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int
+mpc52xx_uart_startup(struct uart_port *port)
+{
+ struct mpc52xx_psc *psc = PSC(port);
+
+ /* Reset/activate the port, clear and enable interrupts */
+ out_8(&psc->command,MPC52xx_PSC_RST_RX);
+ out_8(&psc->command,MPC52xx_PSC_RST_TX);
+
+ out_be32(&psc->sicr,0); /* UART mode DCD ignored */
+
+ out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00); /* /16 prescaler on */
+
+ out_8(&psc->rfcntl, 0x00);
+ out_be16(&psc->rfalarm, 0x1ff);
+ out_8(&psc->tfcntl, 0x07);
+ out_be16(&psc->tfalarm, 0x80);
+
+ port->read_status_mask |= MPC52xx_PSC_IMR_RXRDY | MPC52xx_PSC_IMR_TXRDY;
+ out_be16(&psc->mpc52xx_psc_imr,port->read_status_mask);
+
+ out_8(&psc->command,MPC52xx_PSC_TX_ENABLE);
+ out_8(&psc->command,MPC52xx_PSC_RX_ENABLE);
+
+ return 0;
+}
+
+static void
+mpc52xx_uart_shutdown(struct uart_port *port)
+{
+ struct mpc52xx_psc *psc = PSC(port);
+
+ /* Shut down the port, interrupt and all */
+ out_8(&psc->command,MPC52xx_PSC_RST_RX);
+ out_8(&psc->command,MPC52xx_PSC_RST_TX);
+
+ port->read_status_mask = 0;
+ out_be16(&psc->mpc52xx_psc_imr,port->read_status_mask);
+}
+
+static void
+mpc52xx_uart_set_termios(struct uart_port *port, struct termios *new,
+ struct termios *old)
+{
+ struct mpc52xx_psc *psc = PSC(port);
+ unsigned long flags;
+ unsigned char mr1, mr2;
+ unsigned short ctr;
+ unsigned int j, baud, quot;
+
+ /* Prepare what we're gonna write */
+ mr1 = 0;
+
+ switch (new->c_cflag & CSIZE) {
+ case CS5: mr1 |= MPC52xx_PSC_MODE_5_BITS;
+ break;
+ case CS6: mr1 |= MPC52xx_PSC_MODE_6_BITS;
+ break;
+ case CS7: mr1 |= MPC52xx_PSC_MODE_7_BITS;
+ break;
+ case CS8:
+ default: mr1 |= MPC52xx_PSC_MODE_8_BITS;
+ }
+
+ if (new->c_cflag & PARENB) {
+ mr1 |= (new->c_cflag & PARODD) ?
+ MPC52xx_PSC_MODE_PARODD : MPC52xx_PSC_MODE_PAREVEN;
+ } else
+ mr1 |= MPC52xx_PSC_MODE_PARNONE;
+
+
+ mr2 = 0;
+
+ if (new->c_cflag & CSTOPB)
+ mr2 |= MPC52xx_PSC_MODE_TWO_STOP;
+ else
+ mr2 |= ((new->c_cflag & CSIZE) == CS5) ?
+ MPC52xx_PSC_MODE_ONE_STOP_5_BITS :
+ MPC52xx_PSC_MODE_ONE_STOP;
+
+
+ baud = uart_get_baud_rate(port, new, old, 0, port->uartclk/16);
+ quot = uart_get_divisor(port, baud);
+ ctr = quot & 0xffff;
+
+ /* Get the lock */
+ spin_lock_irqsave(&port->lock, flags);
+
+ /* Update the per-port timeout */
+ uart_update_timeout(port, new->c_cflag, baud);
+
+ /* Do our best to flush TX & RX, so we don't loose anything */
+ /* But we don't wait indefinitly ! */
+ j = 5000000; /* Maximum wait */
+ /* FIXME Can't receive chars since set_termios might be called at early
+ * boot for the console, all stuff is not yet ready to receive at that
+ * time and that just makes the kernel oops */
+ /* while (j-- && mpc52xx_uart_int_rx_chars(port)); */
+ while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP) &&
+ --j)
+ udelay(1);
+
+ if (!j)
+ printk( KERN_ERR "mpc52xx_uart.c: "
+ "Unable to flush RX & TX fifos in-time in set_termios."
+ "Some chars may have been lost.\n" );
+
+ /* Reset the TX & RX */
+ out_8(&psc->command,MPC52xx_PSC_RST_RX);
+ out_8(&psc->command,MPC52xx_PSC_RST_TX);
+
+ /* Send new mode settings */
+ out_8(&psc->command,MPC52xx_PSC_SEL_MODE_REG_1);
+ out_8(&psc->mode,mr1);
+ out_8(&psc->mode,mr2);
+ out_8(&psc->ctur,ctr >> 8);
+ out_8(&psc->ctlr,ctr & 0xff);
+
+ /* Reenable TX & RX */
+ out_8(&psc->command,MPC52xx_PSC_TX_ENABLE);
+ out_8(&psc->command,MPC52xx_PSC_RX_ENABLE);
+
+ /* We're all set, release the lock */
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *
+mpc52xx_uart_type(struct uart_port *port)
+{
+ return port->type == PORT_MPC52xx ? "MPC52xx PSC" : NULL;
+}
+
+static void
+mpc52xx_uart_release_port(struct uart_port *port)
+{
+ if (port->flags & UPF_IOREMAP) { /* remapped by us ? */
+ iounmap(port->membase);
+ port->membase = NULL;
+ }
+}
+
+static int
+mpc52xx_uart_request_port(struct uart_port *port)
+{
+ if (port->flags & UPF_IOREMAP) /* Need to remap ? */
+ port->membase = ioremap(port->mapbase, sizeof(struct mpc52xx_psc));
+
+ return port->membase != NULL ? 0 : -EBUSY;
+}
+
+static void
+mpc52xx_uart_config_port(struct uart_port *port, int flags)
+{
+ if ( (flags & UART_CONFIG_TYPE) &&
+ (mpc52xx_uart_request_port(port) == 0) )
+ port->type = PORT_MPC52xx;
+}
+
+static int
+mpc52xx_uart_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ if ( ser->type != PORT_UNKNOWN && ser->type != PORT_MPC52xx )
+ return -EINVAL;
+
+ if ( (ser->irq != port->irq) ||
+ (ser->io_type != SERIAL_IO_MEM) ||
+ (ser->baud_base != port->uartclk) ||
+ // FIXME Should check addresses/irq as well ?
+ (ser->hub6 != 0 ) )
+ return -EINVAL;
+
+ return 0;
+}
+
+
+static struct uart_ops mpc52xx_uart_ops = {
+ .tx_empty = mpc52xx_uart_tx_empty,
+ .set_mctrl = mpc52xx_uart_set_mctrl,
+ .get_mctrl = mpc52xx_uart_get_mctrl,
+ .stop_tx = mpc52xx_uart_stop_tx,
+ .start_tx = mpc52xx_uart_start_tx,
+ .send_xchar = mpc52xx_uart_send_xchar,
+ .stop_rx = mpc52xx_uart_stop_rx,
+ .enable_ms = mpc52xx_uart_enable_ms,
+ .break_ctl = mpc52xx_uart_break_ctl,
+ .startup = mpc52xx_uart_startup,
+ .shutdown = mpc52xx_uart_shutdown,
+ .set_termios = mpc52xx_uart_set_termios,
+/* .pm = mpc52xx_uart_pm, Not supported yet */
+/* .set_wake = mpc52xx_uart_set_wake, Not supported yet */
+ .type = mpc52xx_uart_type,
+ .release_port = mpc52xx_uart_release_port,
+ .request_port = mpc52xx_uart_request_port,
+ .config_port = mpc52xx_uart_config_port,
+ .verify_port = mpc52xx_uart_verify_port
+};
+
+
+/* ======================================================================== */
+/* Interrupt handling */
+/* ======================================================================== */
+
+static inline int
+mpc52xx_uart_int_rx_chars(struct uart_port *port, struct pt_regs *regs)
+{
+ struct tty_struct *tty = port->info->tty;
+ unsigned char ch;
+ unsigned short status;
+
+ /* While we can read, do so ! */
+ while ( (status = in_be16(&PSC(port)->mpc52xx_psc_status)) &
+ MPC52xx_PSC_SR_RXRDY) {
+
+ /* If we are full, just stop reading */
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+ break;
+
+ /* Get the char */
+ ch = in_8(&PSC(port)->mpc52xx_psc_buffer_8);
+
+ /* Handle sysreq char */
+#ifdef SUPPORT_SYSRQ
+ if (uart_handle_sysrq_char(port, ch, regs)) {
+ port->sysrq = 0;
+ continue;
+ }
+#endif
+
+ /* Store it */
+ *tty->flip.char_buf_ptr = ch;
+ *tty->flip.flag_buf_ptr = 0;
+ port->icount.rx++;
+
+ if ( status & (MPC52xx_PSC_SR_PE |
+ MPC52xx_PSC_SR_FE |
+ MPC52xx_PSC_SR_RB |
+ MPC52xx_PSC_SR_OE) ) {
+
+ if (status & MPC52xx_PSC_SR_RB) {
+ *tty->flip.flag_buf_ptr = TTY_BREAK;
+ uart_handle_break(port);
+ } else if (status & MPC52xx_PSC_SR_PE)
+ *tty->flip.flag_buf_ptr = TTY_PARITY;
+ else if (status & MPC52xx_PSC_SR_FE)
+ *tty->flip.flag_buf_ptr = TTY_FRAME;
+ if (status & MPC52xx_PSC_SR_OE) {
+ /*
+ * Overrun is special, since it's
+ * reported immediately, and doesn't
+ * affect the current character
+ */
+ if (tty->flip.count < (TTY_FLIPBUF_SIZE-1)) {
+ tty->flip.flag_buf_ptr++;
+ tty->flip.char_buf_ptr++;
+ tty->flip.count++;
+ }
+ *tty->flip.flag_buf_ptr = TTY_OVERRUN;
+ }
+
+ /* Clear error condition */
+ out_8(&PSC(port)->command,MPC52xx_PSC_RST_ERR_STAT);
+
+ }
+
+ tty->flip.char_buf_ptr++;
+ tty->flip.flag_buf_ptr++;
+ tty->flip.count++;
+
+ }
+
+ tty_flip_buffer_push(tty);
+
+ return in_be16(&PSC(port)->mpc52xx_psc_status) & MPC52xx_PSC_SR_RXRDY;
+}
+
+static inline int
+mpc52xx_uart_int_tx_chars(struct uart_port *port)
+{
+ struct circ_buf *xmit = &port->info->xmit;
+
+ /* Process out of band chars */
+ if (port->x_char) {
+ out_8(&PSC(port)->mpc52xx_psc_buffer_8, port->x_char);
+ port->icount.tx++;
+ port->x_char = 0;
+ return 1;
+ }
+
+ /* Nothing to do ? */
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ mpc52xx_uart_stop_tx(port,0);
+ return 0;
+ }
+
+ /* Send chars */
+ while (in_be16(&PSC(port)->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXRDY) {
+ out_8(&PSC(port)->mpc52xx_psc_buffer_8, xmit->buf[xmit->tail]);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ port->icount.tx++;
+ if (uart_circ_empty(xmit))
+ break;
+ }
+
+ /* Wake up */
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ /* Maybe we're done after all */
+ if (uart_circ_empty(xmit)) {
+ mpc52xx_uart_stop_tx(port,0);
+ return 0;
+ }
+
+ return 1;
+}
+
+static irqreturn_t
+mpc52xx_uart_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct uart_port *port = (struct uart_port *) dev_id;
+ unsigned long pass = ISR_PASS_LIMIT;
+ unsigned int keepgoing;
+ unsigned short status;
+
+ if ( irq != port->irq ) {
+ printk( KERN_WARNING
+ "mpc52xx_uart_int : " \
+ "Received wrong int %d. Waiting for %d\n",
+ irq, port->irq);
+ return IRQ_NONE;
+ }
+
+ spin_lock(&port->lock);
+
+ /* While we have stuff to do, we continue */
+ do {
+ /* If we don't find anything to do, we stop */
+ keepgoing = 0;
+
+ /* Read status */
+ status = in_be16(&PSC(port)->mpc52xx_psc_isr);
+ status &= port->read_status_mask;
+
+ /* Do we need to receive chars ? */
+ /* For this RX interrupts must be on and some chars waiting */
+ if ( status & MPC52xx_PSC_IMR_RXRDY )
+ keepgoing |= mpc52xx_uart_int_rx_chars(port, regs);
+
+ /* Do we need to send chars ? */
+ /* For this, TX must be ready and TX interrupt enabled */
+ if ( status & MPC52xx_PSC_IMR_TXRDY )
+ keepgoing |= mpc52xx_uart_int_tx_chars(port);
+
+ /* Limit number of iteration */
+ if ( !(--pass) )
+ keepgoing = 0;
+
+ } while (keepgoing);
+
+ spin_unlock(&port->lock);
+
+ return IRQ_HANDLED;
+}
+
+
+/* ======================================================================== */
+/* Console ( if applicable ) */
+/* ======================================================================== */
+
+#ifdef CONFIG_SERIAL_MPC52xx_CONSOLE
+
+static void __init
+mpc52xx_console_get_options(struct uart_port *port,
+ int *baud, int *parity, int *bits, int *flow)
+{
+ struct mpc52xx_psc *psc = PSC(port);
+ unsigned char mr1;
+
+ /* Read the mode registers */
+ out_8(&psc->command,MPC52xx_PSC_SEL_MODE_REG_1);
+ mr1 = in_8(&psc->mode);
+
+ /* CT{U,L}R are write-only ! */
+ *baud = __res.bi_baudrate ?
+ __res.bi_baudrate : CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD;
+
+ /* Parse them */
+ switch (mr1 & MPC52xx_PSC_MODE_BITS_MASK) {
+ case MPC52xx_PSC_MODE_5_BITS: *bits = 5; break;
+ case MPC52xx_PSC_MODE_6_BITS: *bits = 6; break;
+ case MPC52xx_PSC_MODE_7_BITS: *bits = 7; break;
+ case MPC52xx_PSC_MODE_8_BITS:
+ default: *bits = 8;
+ }
+
+ if (mr1 & MPC52xx_PSC_MODE_PARNONE)
+ *parity = 'n';
+ else
+ *parity = mr1 & MPC52xx_PSC_MODE_PARODD ? 'o' : 'e';
+}
+
+static void
+mpc52xx_console_write(struct console *co, const char *s, unsigned int count)
+{
+ struct uart_port *port = &mpc52xx_uart_ports[co->index];
+ struct mpc52xx_psc *psc = PSC(port);
+ unsigned int i, j;
+
+ /* Disable interrupts */
+ out_be16(&psc->mpc52xx_psc_imr, 0);
+
+ /* Wait the TX buffer to be empty */
+ j = 5000000; /* Maximum wait */
+ while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP) &&
+ --j)
+ udelay(1);
+
+ /* Write all the chars */
+ for ( i=0 ; i<count ; i++ ) {
+
+ /* Send the char */
+ out_8(&psc->mpc52xx_psc_buffer_8, *s);
+
+ /* Line return handling */
+ if ( *s++ == '\n' )
+ out_8(&psc->mpc52xx_psc_buffer_8, '\r');
+
+ /* Wait the TX buffer to be empty */
+ j = 20000; /* Maximum wait */
+ while (!(in_be16(&psc->mpc52xx_psc_status) &
+ MPC52xx_PSC_SR_TXEMP) && --j)
+ udelay(1);
+ }
+
+ /* Restore interrupt state */
+ out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask);
+}
+
+static int __init
+mpc52xx_console_setup(struct console *co, char *options)
+{
+ struct uart_port *port = &mpc52xx_uart_ports[co->index];
+
+ int baud = 9600;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ if (co->index < 0 || co->index >= MPC52xx_PSC_MAXNUM)
+ return -EINVAL;
+
+ /* Basic port init. Needed since we use some uart_??? func before
+ * real init for early access */
+ port->lock = SPIN_LOCK_UNLOCKED;
+ port->uartclk = __res.bi_ipbfreq / 2; /* Look at CTLR doc */
+ port->ops = &mpc52xx_uart_ops;
+ port->mapbase = MPC52xx_PSCx(co->index);
+
+ /* We ioremap ourself */
+ port->membase = ioremap(port->mapbase, sizeof(struct mpc52xx_psc));
+ if (port->membase == NULL) {
+ release_mem_region(port->mapbase, sizeof(struct mpc52xx_psc));
+ return -EBUSY;
+ }
+
+ /* Setup the port parameters accoding to options */
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+ else
+ mpc52xx_console_get_options(port, &baud, &parity, &bits, &flow);
+
+ return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+
+extern struct uart_driver mpc52xx_uart_driver;
+
+static struct console mpc52xx_console = {
+ .name = "ttyS",
+ .write = mpc52xx_console_write,
+ .device = uart_console_device,
+ .setup = mpc52xx_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1, /* Specified on the cmdline (e.g. console=ttyS0 ) */
+ .data = &mpc52xx_uart_driver,
+};
+
+
+static int __init
+mpc52xx_console_init(void)
+{
+ register_console(&mpc52xx_console);
+ return 0;
+}
+
+console_initcall(mpc52xx_console_init);
+
+#define MPC52xx_PSC_CONSOLE &mpc52xx_console
+#else
+#define MPC52xx_PSC_CONSOLE NULL
+#endif
+
+
+/* ======================================================================== */
+/* UART Driver */
+/* ======================================================================== */
+
+static struct uart_driver mpc52xx_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = "mpc52xx_psc_uart",
+ .dev_name = "ttyS",
+ .devfs_name = "ttyS",
+ .major = TTY_MAJOR,
+ .minor = 64,
+ .nr = MPC52xx_PSC_MAXNUM,
+ .cons = MPC52xx_PSC_CONSOLE,
+};
+
+
+/* ======================================================================== */
+/* OCP Driver */
+/* ======================================================================== */
+
+static int __devinit
+mpc52xx_uart_probe(struct ocp_device *ocp)
+{
+ struct uart_port *port = NULL;
+ int idx, ret;
+
+ /* Get the corresponding port struct */
+ idx = ocp->def->index;
+ if (idx < 0 || idx >= MPC52xx_PSC_MAXNUM)
+ return -EINVAL;
+
+ port = &mpc52xx_uart_ports[idx];
+
+ /* Init the port structure */
+ port->lock = SPIN_LOCK_UNLOCKED;
+ port->mapbase = ocp->def->paddr;
+ port->irq = ocp->def->irq;
+ port->uartclk = __res.bi_ipbfreq / 2; /* Look at CTLR doc */
+ port->fifosize = 255; /* Should be 512 ! But it can't be */
+ /* stored in a unsigned char */
+ port->iotype = UPIO_MEM;
+ port->flags = UPF_BOOT_AUTOCONF |
+ ( uart_console(port) ? 0 : UPF_IOREMAP );
+ port->line = idx;
+ port->ops = &mpc52xx_uart_ops;
+ port->read_status_mask = 0;
+
+ /* Requests the mem & irqs */
+ /* Unlike other serial drivers, we reserve the resources here, so we
+ * can detect early if multiple drivers uses the same PSC. Special
+ * care must be taken with the console PSC
+ */
+ ret = request_irq(
+ port->irq, mpc52xx_uart_int,
+ SA_INTERRUPT | SA_SAMPLE_RANDOM, "mpc52xx_psc_uart", port);
+ if (ret)
+ goto error;
+
+ ret = request_mem_region(port->mapbase, sizeof(struct mpc52xx_psc),
+ "mpc52xx_psc_uart") != NULL ? 0 : -EBUSY;
+ if (ret)
+ goto free_irq;
+
+ /* Add the port to the uart sub-system */
+ ret = uart_add_one_port(&mpc52xx_uart_driver, port);
+ if (ret)
+ goto release_mem;
+
+ ocp_set_drvdata(ocp, (void*)port);
+
+ return 0;
+
+
+free_irq:
+ free_irq(port->irq, mpc52xx_uart_int);
+
+release_mem:
+ release_mem_region(port->mapbase, sizeof(struct mpc52xx_psc));
+
+error:
+ if (uart_console(port))
+ printk( "mpc52xx_uart.c: Error during resource alloction for "
+ "the console port !!! Check that the console PSC is "
+ "not used by another OCP driver !!!\n" );
+
+ return ret;
+}
+
+static void
+mpc52xx_uart_remove(struct ocp_device *ocp)
+{
+ struct uart_port *port = (struct uart_port *) ocp_get_drvdata(ocp);
+
+ ocp_set_drvdata(ocp, NULL);
+
+ if (port) {
+ uart_remove_one_port(&mpc52xx_uart_driver, port);
+ release_mem_region(port->mapbase, sizeof(struct mpc52xx_psc));
+ free_irq(port->irq, mpc52xx_uart_int);
+ }
+}
+
+#ifdef CONFIG_PM
+static int
+mpc52xx_uart_suspend(struct ocp_device *ocp, u32 state)
+{
+ struct uart_port *port = (struct uart_port *) ocp_get_drvdata(ocp);
+
+ uart_suspend_port(&mpc52xx_uart_driver, port);
+
+ return 0;
+}
+
+static int
+mpc52xx_uart_resume(struct ocp_device *ocp)
+{
+ struct uart_port *port = (struct uart_port *) ocp_get_drvdata(ocp);
+
+ uart_resume_port(&mpc52xx_uart_driver, port);
+
+ return 0;
+}
+#endif
+
+static struct ocp_device_id mpc52xx_uart_ids[] __devinitdata = {
+ { .vendor = OCP_VENDOR_FREESCALE, .function = OCP_FUNC_PSC_UART },
+ { .vendor = OCP_VENDOR_INVALID /* Terminating entry */ }
+};
+
+MODULE_DEVICE_TABLE(ocp, mpc52xx_uart_ids);
+
+static struct ocp_driver mpc52xx_uart_ocp_driver = {
+ .name = "mpc52xx_psc_uart",
+ .id_table = mpc52xx_uart_ids,
+ .probe = mpc52xx_uart_probe,
+ .remove = mpc52xx_uart_remove,
+#ifdef CONFIG_PM
+ .suspend = mpc52xx_uart_suspend,
+ .resume = mpc52xx_uart_resume,
+#endif
+};
+
+
+/* ======================================================================== */
+/* Module */
+/* ======================================================================== */
+
+static int __init
+mpc52xx_uart_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "Serial: MPC52xx PSC driver\n");
+
+ ret = uart_register_driver(&mpc52xx_uart_driver);
+ if (ret)
+ return ret;
+
+ ret = ocp_register_driver(&mpc52xx_uart_ocp_driver);
+
+ return ret;
+}
+
+static void __exit
+mpc52xx_uart_exit(void)
+{
+ ocp_unregister_driver(&mpc52xx_uart_ocp_driver);
+ uart_unregister_driver(&mpc52xx_uart_driver);
+}
+
+
+module_init(mpc52xx_uart_init);
+module_exit(mpc52xx_uart_exit);
+
+MODULE_AUTHOR("Sylvain Munaut <tnt@246tNt.com>");
+MODULE_DESCRIPTION("Freescale MPC52xx PSC UART");
+MODULE_LICENSE("GPL");
--- /dev/null
+/* drivers/serial/serial_lh7a40x.c
+ *
+ * Copyright (C) 2004 Coastal Environmental Systems
+ *
+ * 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.
+ *
+ */
+
+/* Driver for Sharp LH7A40X embedded serial ports
+ *
+ * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ * Based on drivers/serial/amba.c, by Deep Blue Solutions Ltd.
+ *
+ * ---
+ *
+ * This driver supports the embedded UARTs of the Sharp LH7A40X series
+ * CPUs. While similar to the 16550 and other UART chips, there is
+ * nothing close to register compatibility. Moreover, some of the
+ * modem control lines are not available, either in the chip or they
+ * are lacking in the board-level implementation.
+ *
+ * - Use of SIRDIS
+ * For simplicity, we disable the IR functions of any UART whenever
+ * we enable it.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#if defined(CONFIG_SERIAL_LH7A40X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+
+#include <asm/arch/serial.h>
+
+#define DEV_MAJOR 204
+#define DEV_MINOR 16
+#define DEV_NR 3
+
+#define ISR_LOOP_LIMIT 256
+
+#define UR(p,o) _UR ((p)->membase, o)
+#define _UR(b,o) (*((volatile unsigned int*)(((unsigned char*) b) + (o))))
+#define BIT_CLR(p,o,m) UR(p,o) = UR(p,o) & (~(unsigned int)m)
+#define BIT_SET(p,o,m) UR(p,o) = UR(p,o) | ( (unsigned int)m)
+
+#define UART_REG_SIZE 32
+
+#define UARTEN (0x01) /* UART enable */
+#define SIRDIS (0x02) /* Serial IR disable (UART1 only) */
+
+#define RxEmpty (0x10)
+#define TxEmpty (0x80)
+#define TxFull (0x20)
+#define nRxRdy RxEmpty
+#define nTxRdy TxFull
+#define TxBusy (0x08)
+
+#define RxBreak (0x0800)
+#define RxOverrunError (0x0400)
+#define RxParityError (0x0200)
+#define RxFramingError (0x0100)
+#define RxError (RxBreak | RxOverrunError | RxParityError | RxFramingError)
+
+#define DCD (0x04)
+#define DSR (0x02)
+#define CTS (0x01)
+
+#define RxInt (0x01)
+#define TxInt (0x02)
+#define ModemInt (0x04)
+#define RxTimeoutInt (0x08)
+
+#define MSEOI (0x10)
+
+#define WLEN_8 (0x60)
+#define WLEN_7 (0x40)
+#define WLEN_6 (0x20)
+#define WLEN_5 (0x00)
+#define WLEN (0x60) /* Mask for all word-length bits */
+#define STP2 (0x08)
+#define PEN (0x02) /* Parity Enable */
+#define EPS (0x04) /* Even Parity Set */
+#define FEN (0x10) /* FIFO Enable */
+#define BRK (0x01) /* Send Break */
+
+
+struct uart_port_lh7a40x {
+ struct uart_port port;
+ unsigned int statusPrev; /* Most recently read modem status */
+};
+
+static void lh7a40xuart_stop_tx (struct uart_port* port, unsigned int tty_stop)
+{
+ BIT_CLR (port, UART_R_INTEN, TxInt);
+}
+
+static void lh7a40xuart_start_tx (struct uart_port* port,
+ unsigned int tty_start)
+{
+ BIT_SET (port, UART_R_INTEN, TxInt);
+
+ /* *** FIXME: do I need to check for startup of the
+ transmitter? The old driver did, but AMBA
+ doesn't . */
+}
+
+static void lh7a40xuart_stop_rx (struct uart_port* port)
+{
+ BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt);
+}
+
+static void lh7a40xuart_enable_ms (struct uart_port* port)
+{
+ BIT_SET (port, UART_R_INTEN, ModemInt);
+}
+
+static void
+#ifdef SUPPORT_SYSRQ
+lh7a40xuart_rx_chars (struct uart_port* port, struct pt_regs* regs)
+#else
+lh7a40xuart_rx_chars (struct uart_port* port)
+#endif
+{
+ struct tty_struct* tty = port->info->tty;
+ int cbRxMax = 256; /* (Gross) limit on receive */
+ unsigned int data; /* Received data and status */
+
+ while (!(UR (port, UART_R_STATUS) & nRxRdy) && --cbRxMax) {
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ tty->flip.work.func((void*)tty);
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ printk(KERN_WARNING "TTY_DONT_FLIP set\n");
+ return;
+ }
+ }
+
+ data = UR (port, UART_R_DATA);
+
+ *tty->flip.char_buf_ptr = (unsigned char) data;
+ *tty->flip.flag_buf_ptr = TTY_NORMAL;
+ ++port->icount.rx;
+
+ if (data & RxError) { /* Quick check, short-circuit */
+ if (data & RxBreak) {
+ data &= ~(RxFramingError | RxParityError);
+ ++port->icount.brk;
+ if (uart_handle_break (port))
+ continue;
+ }
+ else if (data & RxParityError)
+ ++port->icount.parity;
+ else if (data & RxFramingError)
+ ++port->icount.frame;
+ if (data & RxOverrunError)
+ ++port->icount.overrun;
+
+ /* Mask by termios, leave Rx'd byte */
+ data &= port->read_status_mask | 0xff;
+
+ if (data & RxBreak)
+ *tty->flip.flag_buf_ptr = TTY_BREAK;
+ else if (data & RxParityError)
+ *tty->flip.flag_buf_ptr = TTY_PARITY;
+ else if (data & RxFramingError)
+ *tty->flip.flag_buf_ptr = TTY_FRAME;
+ }
+
+ if (uart_handle_sysrq_char (port, (unsigned char) data, regs))
+ continue;
+
+ if ((data & port->ignore_status_mask) == 0) {
+ ++tty->flip.flag_buf_ptr;
+ ++tty->flip.char_buf_ptr;
+ ++tty->flip.count;
+ }
+ if ((data & RxOverrunError)
+ && tty->flip.count < TTY_FLIPBUF_SIZE) {
+ /*
+ * Overrun is special, since it's reported
+ * immediately, and doesn't affect the current
+ * character
+ */
+ *tty->flip.char_buf_ptr++ = 0;
+ *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
+ ++tty->flip.count;
+ }
+ }
+ tty_flip_buffer_push (tty);
+ return;
+}
+
+static void lh7a40xuart_tx_chars (struct uart_port* port)
+{
+ struct circ_buf* xmit = &port->info->xmit;
+ int cbTxMax = port->fifosize;
+
+ if (port->x_char) {
+ UR (port, UART_R_DATA) = port->x_char;
+ ++port->icount.tx;
+ port->x_char = 0;
+ return;
+ }
+ if (uart_circ_empty (xmit) || uart_tx_stopped (port)) {
+ lh7a40xuart_stop_tx (port, 0);
+ return;
+ }
+
+ /* Unlike the AMBA UART, the lh7a40x UART does not guarantee
+ that at least half of the FIFO is empty. Instead, we check
+ status for every character. Using the AMBA method causes
+ the transmitter to drop characters. */
+
+ do {
+ UR (port, UART_R_DATA) = xmit->buf[xmit->tail];
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ ++port->icount.tx;
+ if (uart_circ_empty(xmit))
+ break;
+ } while (!(UR (port, UART_R_STATUS) & nTxRdy)
+ && cbTxMax--);
+
+ if (uart_circ_chars_pending (xmit) < WAKEUP_CHARS)
+ uart_write_wakeup (port);
+
+ if (uart_circ_empty (xmit))
+ lh7a40xuart_stop_tx (port, 0);
+}
+
+static void lh7a40xuart_modem_status (struct uart_port* port)
+{
+ unsigned int status = UR (port, UART_R_STATUS);
+ unsigned int delta
+ = status ^ ((struct uart_port_lh7a40x*) port)->statusPrev;
+
+ BIT_SET (port, UART_R_RAWISR, MSEOI); /* Clear modem status intr */
+
+ if (!delta) /* Only happens if we missed 2 transitions */
+ return;
+
+ ((struct uart_port_lh7a40x*) port)->statusPrev = status;
+
+ if (delta & DCD)
+ uart_handle_dcd_change (port, status & DCD);
+
+ if (delta & DSR)
+ ++port->icount.dsr;
+
+ if (delta & CTS)
+ uart_handle_cts_change (port, status & CTS);
+
+ wake_up_interruptible (&port->info->delta_msr_wait);
+}
+
+static irqreturn_t lh7a40xuart_int (int irq, void* dev_id,
+ struct pt_regs* regs)
+{
+ struct uart_port* port = dev_id;
+ unsigned int cLoopLimit = ISR_LOOP_LIMIT;
+ unsigned int isr = UR (port, UART_R_ISR);
+
+
+ do {
+ if (isr & (RxInt | RxTimeoutInt))
+#ifdef SUPPORT_SYSRQ
+ lh7a40xuart_rx_chars(port, regs);
+#else
+ lh7a40xuart_rx_chars(port);
+#endif
+ if (isr & ModemInt)
+ lh7a40xuart_modem_status (port);
+ if (isr & TxInt)
+ lh7a40xuart_tx_chars (port);
+
+ if (--cLoopLimit == 0)
+ break;
+
+ isr = UR (port, UART_R_ISR);
+ } while (isr & (RxInt | TxInt | RxTimeoutInt));
+
+ return IRQ_HANDLED;
+}
+
+static unsigned int lh7a40xuart_tx_empty (struct uart_port* port)
+{
+ return (UR (port, UART_R_STATUS) & TxEmpty) ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int lh7a40xuart_get_mctrl (struct uart_port* port)
+{
+ unsigned int result = 0;
+ unsigned int status = UR (port, UART_R_STATUS);
+
+ if (status & DCD)
+ result |= TIOCM_CAR;
+ if (status & DSR)
+ result |= TIOCM_DSR;
+ if (status & CTS)
+ result |= TIOCM_CTS;
+
+ return result;
+}
+
+static void lh7a40xuart_set_mctrl (struct uart_port* port, unsigned int mctrl)
+{
+ /* None of the ports supports DTR. UART1 supports RTS through GPIO. */
+ /* Note, kernel appears to be setting DTR and RTS on console. */
+
+ /* *** FIXME: this deserves more work. There's some work in
+ tracing all of the IO pins. */
+#if 0
+ if( port->mapbase == UART1_PHYS) {
+ gpioRegs_t *gpio = (gpioRegs_t *)IO_ADDRESS(GPIO_PHYS);
+
+ if (mctrl & TIOCM_RTS)
+ gpio->pbdr &= ~GPIOB_UART1_RTS;
+ else
+ gpio->pbdr |= GPIOB_UART1_RTS;
+ }
+#endif
+}
+
+static void lh7a40xuart_break_ctl (struct uart_port* port, int break_state)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ if (break_state == -1)
+ BIT_SET (port, UART_R_FCON, BRK); /* Assert break */
+ else
+ BIT_CLR (port, UART_R_FCON, BRK); /* Deassert break */
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int lh7a40xuart_startup (struct uart_port* port)
+{
+ int retval;
+
+ retval = request_irq (port->irq, lh7a40xuart_int, 0,
+ "serial_lh7a40x", port);
+ if (retval)
+ return retval;
+
+ /* Initial modem control-line settings */
+ ((struct uart_port_lh7a40x*) port)->statusPrev
+ = UR (port, UART_R_STATUS);
+
+ /* There is presently no configuration option to enable IR.
+ Thus, we always disable it. */
+
+ BIT_SET (port, UART_R_CON, UARTEN | SIRDIS);
+ BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt);
+
+ return 0;
+}
+
+static void lh7a40xuart_shutdown (struct uart_port* port)
+{
+ free_irq (port->irq, port);
+ BIT_CLR (port, UART_R_FCON, BRK | FEN);
+ BIT_CLR (port, UART_R_CON, UARTEN);
+}
+
+static void lh7a40xuart_set_termios (struct uart_port* port,
+ struct termios* termios,
+ struct termios* old)
+{
+ unsigned int con;
+ unsigned int inten;
+ unsigned int fcon;
+ unsigned long flags;
+ unsigned int baud;
+ unsigned int quot;
+
+ baud = uart_get_baud_rate (port, termios, old, 8, port->uartclk/16);
+ quot = uart_get_divisor (port, baud); /* -1 performed elsewhere */
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ fcon = WLEN_5;
+ break;
+ case CS6:
+ fcon = WLEN_6;
+ break;
+ case CS7:
+ fcon = WLEN_7;
+ break;
+ case CS8:
+ default:
+ fcon = WLEN_8;
+ break;
+ }
+ if (termios->c_cflag & CSTOPB)
+ fcon |= STP2;
+ if (termios->c_cflag & PARENB) {
+ fcon |= PEN;
+ if (!(termios->c_cflag & PARODD))
+ fcon |= EPS;
+ }
+ if (port->fifosize > 1)
+ fcon |= FEN;
+
+ spin_lock_irqsave (&port->lock, flags);
+
+ uart_update_timeout (port, termios->c_cflag, baud);
+
+ port->read_status_mask = RxOverrunError;
+ if (termios->c_iflag & INPCK)
+ port->read_status_mask |= RxFramingError | RxParityError;
+ if (termios->c_iflag & (BRKINT | PARMRK))
+ port->read_status_mask |= RxBreak;
+
+ /* Figure mask for status we ignore */
+ port->ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |= RxFramingError | RxParityError;
+ if (termios->c_iflag & IGNBRK) {
+ port->ignore_status_mask |= RxBreak;
+ /* Ignore overrun when ignorning parity */
+ /* *** FIXME: is this in the right place? */
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |= RxOverrunError;
+ }
+
+ /* Ignore all receive errors when receive disabled */
+ if ((termios->c_cflag & CREAD) == 0)
+ port->ignore_status_mask |= RxError;
+
+ con = UR (port, UART_R_CON);
+ inten = (UR (port, UART_R_INTEN) & ~ModemInt);
+
+ if (UART_ENABLE_MS (port, termios->c_cflag))
+ inten |= ModemInt;
+
+ BIT_CLR (port, UART_R_CON, UARTEN); /* Disable UART */
+ UR (port, UART_R_INTEN) = 0; /* Disable interrupts */
+ UR (port, UART_R_BRCON) = quot - 1; /* Set baud rate divisor */
+ UR (port, UART_R_FCON) = fcon; /* Set FIFO and frame ctrl */
+ UR (port, UART_R_INTEN) = inten; /* Enable interrupts */
+ UR (port, UART_R_CON) = con; /* Restore UART mode */
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char* lh7a40xuart_type (struct uart_port* port)
+{
+ return port->type == PORT_LH7A40X ? "LH7A40X" : NULL;
+}
+
+static void lh7a40xuart_release_port (struct uart_port* port)
+{
+ release_mem_region (port->mapbase, UART_REG_SIZE);
+}
+
+static int lh7a40xuart_request_port (struct uart_port* port)
+{
+ return request_mem_region (port->mapbase, UART_REG_SIZE,
+ "serial_lh7a40x") != NULL
+ ? 0 : -EBUSY;
+}
+
+static void lh7a40xuart_config_port (struct uart_port* port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE) {
+ port->type = PORT_LH7A40X;
+ lh7a40xuart_request_port (port);
+ }
+}
+
+static int lh7a40xuart_verify_port (struct uart_port* port,
+ struct serial_struct* ser)
+{
+ int ret = 0;
+
+ if (ser->type != PORT_UNKNOWN && ser->type != PORT_LH7A40X)
+ ret = -EINVAL;
+ if (ser->irq < 0 || ser->irq >= NR_IRQS)
+ ret = -EINVAL;
+ if (ser->baud_base < 9600) /* *** FIXME: is this true? */
+ ret = -EINVAL;
+ return ret;
+}
+
+static struct uart_ops lh7a40x_uart_ops = {
+ .tx_empty = lh7a40xuart_tx_empty,
+ .set_mctrl = lh7a40xuart_set_mctrl,
+ .get_mctrl = lh7a40xuart_get_mctrl,
+ .stop_tx = lh7a40xuart_stop_tx,
+ .start_tx = lh7a40xuart_start_tx,
+ .stop_rx = lh7a40xuart_stop_rx,
+ .enable_ms = lh7a40xuart_enable_ms,
+ .break_ctl = lh7a40xuart_break_ctl,
+ .startup = lh7a40xuart_startup,
+ .shutdown = lh7a40xuart_shutdown,
+ .set_termios = lh7a40xuart_set_termios,
+ .type = lh7a40xuart_type,
+ .release_port = lh7a40xuart_release_port,
+ .request_port = lh7a40xuart_request_port,
+ .config_port = lh7a40xuart_config_port,
+ .verify_port = lh7a40xuart_verify_port,
+};
+
+static struct uart_port_lh7a40x lh7a40x_ports[DEV_NR] = {
+ {
+ .port = {
+ .membase = (void*) io_p2v (UART1_PHYS),
+ .mapbase = UART1_PHYS,
+ .iotype = SERIAL_IO_MEM,
+ .irq = IRQ_UART1INTR,
+ .uartclk = 14745600/2,
+ .fifosize = 16,
+ .ops = &lh7a40x_uart_ops,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 0,
+ },
+ },
+ {
+ .port = {
+ .membase = (void*) io_p2v (UART2_PHYS),
+ .mapbase = UART2_PHYS,
+ .iotype = SERIAL_IO_MEM,
+ .irq = IRQ_UART2INTR,
+ .uartclk = 14745600/2,
+ .fifosize = 16,
+ .ops = &lh7a40x_uart_ops,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 1,
+ },
+ },
+ {
+ .port = {
+ .membase = (void*) io_p2v (UART3_PHYS),
+ .mapbase = UART3_PHYS,
+ .iotype = SERIAL_IO_MEM,
+ .irq = IRQ_UART3INTR,
+ .uartclk = 14745600/2,
+ .fifosize = 16,
+ .ops = &lh7a40x_uart_ops,
+ .flags = ASYNC_BOOT_AUTOCONF,
+ .line = 2,
+ },
+ },
+};
+
+#ifndef CONFIG_SERIAL_LH7A40X_CONSOLE
+# define LH7A40X_CONSOLE NULL
+#else
+# define LH7A40X_CONSOLE &lh7a40x_console
+
+
+static void lh7a40xuart_console_write (struct console* co,
+ const char* s,
+ unsigned int count)
+{
+ struct uart_port* port = &lh7a40x_ports[co->index].port;
+ unsigned int con = UR (port, UART_R_CON);
+ unsigned int inten = UR (port, UART_R_INTEN);
+
+
+ UR (port, UART_R_INTEN) = 0; /* Disable all interrupts */
+ BIT_SET (port, UART_R_CON, UARTEN | SIRDIS); /* Enable UART */
+
+ for (; count-- > 0; ++s) {
+ while (UR (port, UART_R_STATUS) & nTxRdy)
+ ;
+ UR (port, UART_R_DATA) = *s;
+ if (*s == '\n') {
+ while ((UR (port, UART_R_STATUS) & TxBusy))
+ ;
+ UR (port, UART_R_DATA) = '\r';
+ }
+ }
+
+ /* Wait until all characters are sent */
+ while (UR (port, UART_R_STATUS) & TxBusy)
+ ;
+
+ /* Restore control and interrupt mask */
+ UR (port, UART_R_CON) = con;
+ UR (port, UART_R_INTEN) = inten;
+}
+
+static void __init lh7a40xuart_console_get_options (struct uart_port* port,
+ int* baud,
+ int* parity,
+ int* bits)
+{
+ if (UR (port, UART_R_CON) & UARTEN) {
+ unsigned int fcon = UR (port, UART_R_FCON);
+ unsigned int quot = UR (port, UART_R_BRCON) + 1;
+
+ switch (fcon & (PEN | EPS)) {
+ default: *parity = 'n'; break;
+ case PEN: *parity = 'o'; break;
+ case PEN | EPS: *parity = 'e'; break;
+ }
+
+ switch (fcon & WLEN) {
+ default:
+ case WLEN_8: *bits = 8; break;
+ case WLEN_7: *bits = 7; break;
+ case WLEN_6: *bits = 6; break;
+ case WLEN_5: *bits = 5; break;
+ }
+
+ *baud = port->uartclk/(16*quot);
+ }
+}
+
+static int __init lh7a40xuart_console_setup (struct console* co, char* options)
+{
+ struct uart_port* port;
+ int baud = 38400;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ if (co->index >= DEV_NR) /* Bounds check on device number */
+ co->index = 0;
+ port = &lh7a40x_ports[co->index].port;
+
+ if (options)
+ uart_parse_options (options, &baud, &parity, &bits, &flow);
+ else
+ lh7a40xuart_console_get_options (port, &baud, &parity, &bits);
+
+ return uart_set_options (port, co, baud, parity, bits, flow);
+}
+
+extern struct uart_driver lh7a40x_reg;
+static struct console lh7a40x_console = {
+ .name = "ttyAM",
+ .write = lh7a40xuart_console_write,
+ .device = uart_console_device,
+ .setup = lh7a40xuart_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &lh7a40x_reg,
+};
+
+static int __init lh7a40xuart_console_init(void)
+{
+ register_console (&lh7a40x_console);
+ return 0;
+}
+
+console_initcall (lh7a40xuart_console_init);
+
+#endif
+
+static struct uart_driver lh7a40x_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = "ttyAM",
+ .dev_name = "ttyAM",
+ .major = DEV_MAJOR,
+ .minor = DEV_MINOR,
+ .nr = DEV_NR,
+ .cons = LH7A40X_CONSOLE,
+};
+
+static int __init lh7a40xuart_init(void)
+{
+ int ret;
+
+ printk (KERN_INFO "serial: LH7A40X serial driver\n");
+
+ ret = uart_register_driver (&lh7a40x_reg);
+
+ if (ret == 0) {
+ int i;
+
+ for (i = 0; i < DEV_NR; i++)
+ uart_add_one_port (&lh7a40x_reg,
+ &lh7a40x_ports[i].port);
+ }
+ return ret;
+}
+
+static void __exit lh7a40xuart_exit(void)
+{
+ int i;
+
+ for (i = 0; i < DEV_NR; i++)
+ uart_remove_one_port (&lh7a40x_reg, &lh7a40x_ports[i].port);
+
+ uart_unregister_driver (&lh7a40x_reg);
+}
+
+module_init (lh7a40xuart_init);
+module_exit (lh7a40xuart_exit);
+
+MODULE_AUTHOR ("Marc Singer");
+MODULE_DESCRIPTION ("Sharp LH7A40X serial port driver");
+MODULE_LICENSE ("GPL");
--- /dev/null
+/*
+ * C-Brick Serial Port (and console) driver for SGI Altix machines.
+ *
+ * This driver is NOT suitable for talking to the l1-controller for
+ * anything other than 'console activities' --- please use the l1
+ * driver for that.
+ *
+ *
+ * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1500 Crittenden Lane,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan
+ */
+
+#include <linux/config.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/module.h>
+#include <linux/sysrq.h>
+#include <linux/circ_buf.h>
+#include <linux/serial_reg.h>
+#include <linux/delay.h> /* for mdelay */
+#include <linux/miscdevice.h>
+#include <linux/serial_core.h>
+
+#include <asm/sn/simulator.h>
+#include <asm/sn/sn2/sn_private.h>
+#include <asm/sn/sn_sal.h>
+
+/* number of characters we can transmit to the SAL console at a time */
+#define SN_SAL_MAX_CHARS 120
+
+/* 64K, when we're asynch, it must be at least printk's LOG_BUF_LEN to
+ * avoid losing chars, (always has to be a power of 2) */
+#define SN_SAL_BUFFER_SIZE (64 * (1 << 10))
+
+#define SN_SAL_UART_FIFO_DEPTH 16
+#define SN_SAL_UART_FIFO_SPEED_CPS 9600/10
+
+/* sn_transmit_chars() calling args */
+#define TRANSMIT_BUFFERED 0
+#define TRANSMIT_RAW 1
+
+/* To use dynamic numbers only and not use the assigned major and minor,
+ * define the following.. */
+/* #define USE_DYNAMIC_MINOR 1 */ /* use dynamic minor number */
+#define USE_DYNAMIC_MINOR 0 /* Don't rely on misc_register dynamic minor */
+
+/* Device name we're using */
+#define DEVICE_NAME "ttySG"
+#define DEVICE_NAME_DYNAMIC "ttySG0" /* need full name for misc_register */
+/* The major/minor we are using, ignored for USE_DYNAMIC_MINOR */
+#define DEVICE_MAJOR 204
+#define DEVICE_MINOR 40
+
+/*
+ * Port definition - this kinda drives it all
+ */
+struct sn_cons_port {
+ struct timer_list sc_timer;
+ struct uart_port sc_port;
+ struct sn_sal_ops {
+ int (*sal_puts_raw) (const char *s, int len);
+ int (*sal_puts) (const char *s, int len);
+ int (*sal_getc) (void);
+ int (*sal_input_pending) (void);
+ void (*sal_wakeup_transmit) (struct sn_cons_port *, int);
+ } *sc_ops;
+ unsigned long sc_interrupt_timeout;
+ int sc_is_asynch;
+};
+
+static struct sn_cons_port sal_console_port;
+
+/* Only used if USE_DYNAMIC_MINOR is set to 1 */
+static struct miscdevice misc; /* used with misc_register for dynamic */
+
+extern u64 master_node_bedrock_address;
+extern void early_sn_setup(void);
+
+#undef DEBUG
+#ifdef DEBUG
+static int sn_debug_printf(const char *fmt, ...);
+#define DPRINTF(x...) sn_debug_printf(x)
+#else
+#define DPRINTF(x...) do { } while (0)
+#endif
+
+/* Prototypes */
+static int snt_hw_puts_raw(const char *, int);
+static int snt_hw_puts_buffered(const char *, int);
+static int snt_poll_getc(void);
+static int snt_poll_input_pending(void);
+static int snt_sim_puts(const char *, int);
+static int snt_sim_getc(void);
+static int snt_sim_input_pending(void);
+static int snt_intr_getc(void);
+static int snt_intr_input_pending(void);
+static void sn_transmit_chars(struct sn_cons_port *, int);
+
+/* A table for polling:
+ */
+static struct sn_sal_ops poll_ops = {
+ .sal_puts_raw = snt_hw_puts_raw,
+ .sal_puts = snt_hw_puts_raw,
+ .sal_getc = snt_poll_getc,
+ .sal_input_pending = snt_poll_input_pending
+};
+
+/* A table for the simulator */
+static struct sn_sal_ops sim_ops = {
+ .sal_puts_raw = snt_sim_puts,
+ .sal_puts = snt_sim_puts,
+ .sal_getc = snt_sim_getc,
+ .sal_input_pending = snt_sim_input_pending
+};
+
+/* A table for interrupts enabled */
+static struct sn_sal_ops intr_ops = {
+ .sal_puts_raw = snt_hw_puts_raw,
+ .sal_puts = snt_hw_puts_buffered,
+ .sal_getc = snt_intr_getc,
+ .sal_input_pending = snt_intr_input_pending,
+ .sal_wakeup_transmit = sn_transmit_chars
+};
+
+/* the console does output in two distinctly different ways:
+ * synchronous (raw) and asynchronous (buffered). initally, early_printk
+ * does synchronous output. any data written goes directly to the SAL
+ * to be output (incidentally, it is internally buffered by the SAL)
+ * after interrupts and timers are initialized and available for use,
+ * the console init code switches to asynchronous output. this is
+ * also the earliest opportunity to begin polling for console input.
+ * after console initialization, console output and tty (serial port)
+ * output is buffered and sent to the SAL asynchronously (either by
+ * timer callback or by UART interrupt) */
+
+
+/* routines for running the console in polling mode */
+
+/**
+ * snt_poll_getc - Get a character from the console in polling mode
+ *
+ */
+static int
+snt_poll_getc(void)
+{
+ int ch;
+
+ ia64_sn_console_getc(&ch);
+ return ch;
+}
+
+/**
+ * snt_poll_input_pending - Check if any input is waiting - polling mode.
+ *
+ */
+static int
+snt_poll_input_pending(void)
+{
+ int status, input;
+
+ status = ia64_sn_console_check(&input);
+ return !status && input;
+}
+
+/* routines for running the console on the simulator */
+
+/**
+ * snt_sim_puts - send to the console, used in simulator mode
+ * @str: String to send
+ * @count: length of string
+ *
+ */
+static int
+snt_sim_puts(const char *str, int count)
+{
+ int counter = count;
+
+#ifdef FLAG_DIRECT_CONSOLE_WRITES
+ /* This is an easy way to pre-pend the output to know whether the output
+ * was done via sal or directly */
+ writeb('[', master_node_bedrock_address + (UART_TX << 3));
+ writeb('+', master_node_bedrock_address + (UART_TX << 3));
+ writeb(']', master_node_bedrock_address + (UART_TX << 3));
+ writeb(' ', master_node_bedrock_address + (UART_TX << 3));
+#endif /* FLAG_DIRECT_CONSOLE_WRITES */
+ while (counter > 0) {
+ writeb(*str, master_node_bedrock_address + (UART_TX << 3));
+ counter--;
+ str++;
+ }
+ return count;
+}
+
+/**
+ * snt_sim_getc - Get character from console in simulator mode
+ *
+ */
+static int
+snt_sim_getc(void)
+{
+ return readb(master_node_bedrock_address + (UART_RX << 3));
+}
+
+/**
+ * snt_sim_input_pending - Check if there is input pending in simulator mode
+ *
+ */
+static int
+snt_sim_input_pending(void)
+{
+ return readb(master_node_bedrock_address +
+ (UART_LSR << 3)) & UART_LSR_DR;
+}
+
+/* routines for an interrupt driven console (normal) */
+
+/**
+ * snt_intr_getc - Get a character from the console, interrupt mode
+ *
+ */
+static int
+snt_intr_getc(void)
+{
+ return ia64_sn_console_readc();
+}
+
+/**
+ * snt_intr_input_pending - Check if input is pending, interrupt mode
+ *
+ */
+static int
+snt_intr_input_pending(void)
+{
+ return ia64_sn_console_intr_status() & SAL_CONSOLE_INTR_RECV;
+}
+
+/* these functions are polled and interrupt */
+
+/**
+ * snt_hw_puts_raw - Send raw string to the console, polled or interrupt mode
+ * @s: String
+ * @len: Length
+ *
+ */
+static int
+snt_hw_puts_raw(const char *s, int len)
+{
+ /* this will call the PROM and not return until this is done */
+ return ia64_sn_console_putb(s, len);
+}
+
+/**
+ * snt_hw_puts_buffered - Send string to console, polled or interrupt mode
+ * @s: String
+ * @len: Length
+ *
+ */
+static int
+snt_hw_puts_buffered(const char *s, int len)
+{
+ /* queue data to the PROM */
+ return ia64_sn_console_xmit_chars((char *)s, len);
+}
+
+/* uart interface structs
+ * These functions are associated with the uart_port that the serial core
+ * infrastructure calls.
+ *
+ * Note: Due to how the console works, many routines are no-ops.
+ */
+
+/**
+ * snp_type - What type of console are we?
+ * @port: Port to operate with (we ignore since we only have one port)
+ *
+ */
+static const char *
+snp_type(struct uart_port *port)
+{
+ return ("SGI SN L1");
+}
+
+/**
+ * snp_tx_empty - Is the transmitter empty? We pretend we're always empty
+ * @port: Port to operate on (we ignore since we only have one port)
+ *
+ */
+static unsigned int
+snp_tx_empty(struct uart_port *port)
+{
+ return 1;
+}
+
+/**
+ * snp_stop_tx - stop the transmitter - no-op for us
+ * @port: Port to operat eon - we ignore - no-op function
+ * @tty_stop: Set to 1 if called via uart_stop
+ *
+ */
+static void
+snp_stop_tx(struct uart_port *port, unsigned int tty_stop)
+{
+}
+
+/**
+ * snp_release_port - Free i/o and resources for port - no-op for us
+ * @port: Port to operate on - we ignore - no-op function
+ *
+ */
+static void
+snp_release_port(struct uart_port *port)
+{
+}
+
+/**
+ * snp_enable_ms - Force modem status interrupts on - no-op for us
+ * @port: Port to operate on - we ignore - no-op function
+ *
+ */
+static void
+snp_enable_ms(struct uart_port *port)
+{
+}
+
+/**
+ * snp_shutdown - shut down the port - free irq and disable - no-op for us
+ * @port: Port to shut down - we ignore
+ *
+ */
+static void
+snp_shutdown(struct uart_port *port)
+{
+}
+
+/**
+ * snp_set_mctrl - set control lines (dtr, rts, etc) - no-op for our console
+ * @port: Port to operate on - we ignore
+ * @mctrl: Lines to set/unset - we ignore
+ *
+ */
+static void
+snp_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+/**
+ * snp_get_mctrl - get contorl line info, we just return a static value
+ * @port: port to operate on - we only have one port so we ignore this
+ *
+ */
+static unsigned int
+snp_get_mctrl(struct uart_port *port)
+{
+ return TIOCM_CAR | TIOCM_RNG | TIOCM_DSR | TIOCM_CTS;
+}
+
+/**
+ * snp_stop_rx - Stop the receiver - we ignor ethis
+ * @port: Port to operate on - we ignore
+ *
+ */
+static void
+snp_stop_rx(struct uart_port *port)
+{
+}
+
+/**
+ * snp_start_tx - Start transmitter
+ * @port: Port to operate on
+ * @tty_stop: Set to 1 if called via uart_start
+ *
+ */
+static void
+snp_start_tx(struct uart_port *port, unsigned int tty_stop)
+{
+ if (sal_console_port.sc_ops->sal_wakeup_transmit)
+ sal_console_port.sc_ops->sal_wakeup_transmit(&sal_console_port, TRANSMIT_BUFFERED);
+
+}
+
+/**
+ * snp_break_ctl - handle breaks - ignored by us
+ * @port: Port to operate on
+ * @break_state: Break state
+ *
+ */
+static void
+snp_break_ctl(struct uart_port *port, int break_state)
+{
+}
+
+/**
+ * snp_startup - Start up the serial port - always return 0 (We're always on)
+ * @port: Port to operate on
+ *
+ */
+static int
+snp_startup(struct uart_port *port)
+{
+ return 0;
+}
+
+/**
+ * snp_set_termios - set termios stuff - we ignore these
+ * @port: port to operate on
+ * @termios: New settings
+ * @termios: Old
+ *
+ */
+static void
+snp_set_termios(struct uart_port *port, struct termios *termios,
+ struct termios *old)
+{
+}
+
+/**
+ * snp_request_port - allocate resources for port - ignored by us
+ * @port: port to operate on
+ *
+ */
+static int
+snp_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+/**
+ * snp_config_port - allocate resources, set up - we ignore, we're always on
+ * @port: Port to operate on
+ * @flags: flags used for port setup
+ *
+ */
+static void
+snp_config_port(struct uart_port *port, int flags)
+{
+}
+
+/* Associate the uart functions above - given to serial core */
+
+static struct uart_ops sn_console_ops = {
+ .tx_empty = snp_tx_empty,
+ .set_mctrl = snp_set_mctrl,
+ .get_mctrl = snp_get_mctrl,
+ .stop_tx = snp_stop_tx,
+ .start_tx = snp_start_tx,
+ .stop_rx = snp_stop_rx,
+ .enable_ms = snp_enable_ms,
+ .break_ctl = snp_break_ctl,
+ .startup = snp_startup,
+ .shutdown = snp_shutdown,
+ .set_termios = snp_set_termios,
+ .pm = NULL,
+ .type = snp_type,
+ .release_port = snp_release_port,
+ .request_port = snp_request_port,
+ .config_port = snp_config_port,
+ .verify_port = NULL,
+};
+
+/* End of uart struct functions and defines */
+
+#ifdef DEBUG
+
+/**
+ * sn_debug_printf - close to hardware debugging printf
+ * @fmt: printf format
+ *
+ * This is as "close to the metal" as we can get, used when the driver
+ * itself may be broken.
+ *
+ */
+static int
+sn_debug_printf(const char *fmt, ...)
+{
+ static char printk_buf[1024];
+ int printed_len;
+ va_list args;
+
+ va_start(args, fmt);
+ printed_len = vsnprintf(printk_buf, sizeof (printk_buf), fmt, args);
+
+ if (!sal_console_port.sc_ops) {
+ if (IS_RUNNING_ON_SIMULATOR())
+ sal_console_port.sc_ops = &sim_ops;
+ else
+ sal_console_port.sc_ops = &poll_ops;
+
+ early_sn_setup();
+ }
+ sal_console_port.sc_ops->sal_puts_raw(printk_buf, printed_len);
+
+ va_end(args);
+ return printed_len;
+}
+#endif /* DEBUG */
+
+/*
+ * Interrupt handling routines.
+ */
+
+
+/**
+ * sn_receive_chars - Grab characters, pass them to tty layer
+ * @port: Port to operate on
+ * @regs: Saved registers (needed by uart_handle_sysrq_char)
+ *
+ * Note: If we're not registered with the serial core infrastructure yet,
+ * we don't try to send characters to it...
+ *
+ */
+static void
+sn_receive_chars(struct sn_cons_port *port, struct pt_regs *regs)
+{
+ int ch;
+ struct tty_struct *tty;
+
+ if (!port) {
+ printk(KERN_ERR "sn_receive_chars - port NULL so can't receieve\n");
+ return;
+ }
+
+ if (!port->sc_ops) {
+ printk(KERN_ERR "sn_receive_chars - port->sc_ops NULL so can't receieve\n");
+ return;
+ }
+
+ if (port->sc_port.info) {
+ /* The serial_core stuffs are initilized, use them */
+ tty = port->sc_port.info->tty;
+ }
+ else {
+ /* Not registered yet - can't pass to tty layer. */
+ tty = NULL;
+ }
+
+ while (port->sc_ops->sal_input_pending()) {
+ ch = port->sc_ops->sal_getc();
+ if (ch < 0) {
+ printk(KERN_ERR "sn_console: An error occured while "
+ "obtaining data from the console (0x%0x)\n", ch);
+ break;
+ }
+#if defined(CONFIG_SERIAL_SGI_L1_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+ if (uart_handle_sysrq_char(&port->sc_port, ch, regs))
+ continue;
+#endif /* CONFIG_SERIAL_SGI_L1_CONSOLE && CONFIG_MAGIC_SYSRQ */
+
+ /* record the character to pass up to the tty layer */
+ if (tty) {
+ *tty->flip.char_buf_ptr = ch;
+ *tty->flip.flag_buf_ptr = TTY_NORMAL;
+ tty->flip.char_buf_ptr++;
+ tty->flip.count++;
+ if (tty->flip.count == TTY_FLIPBUF_SIZE)
+ break;
+ }
+ else {
+ }
+ port->sc_port.icount.rx++;
+ }
+
+ if (tty)
+ tty_flip_buffer_push(tty);
+}
+
+/**
+ * sn_transmit_chars - grab characters from serial core, send off
+ * @port: Port to operate on
+ * @raw: Transmit raw or buffered
+ *
+ * Note: If we're early, before we're registered with serial core, the
+ * writes are going through sn_sal_console_write because that's how
+ * register_console has been set up. We currently could have asynch
+ * polls calling this function due to sn_sal_switch_to_asynch but we can
+ * ignore them until we register with the serial core stuffs.
+ *
+ */
+static void
+sn_transmit_chars(struct sn_cons_port *port, int raw)
+{
+ int xmit_count, tail, head, loops, ii;
+ int result;
+ char *start;
+ struct circ_buf *xmit;
+
+ if (!port)
+ return;
+
+ BUG_ON(!port->sc_is_asynch);
+
+ if (port->sc_port.info) {
+ /* We're initilized, using serial core infrastructure */
+ xmit = &port->sc_port.info->xmit;
+ }
+ else {
+ /* Probably sn_sal_switch_to_asynch has been run but serial core isn't
+ * initilized yet. Just return. Writes are going through
+ * sn_sal_console_write (due to register_console) at this time.
+ */
+ return;
+ }
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(&port->sc_port)) {
+ /* Nothing to do. */
+ return;
+ }
+
+ head = xmit->head;
+ tail = xmit->tail;
+ start = &xmit->buf[tail];
+
+ /* twice around gets the tail to the end of the buffer and
+ * then to the head, if needed */
+ loops = (head < tail) ? 2 : 1;
+
+ for (ii = 0; ii < loops; ii++) {
+ xmit_count = (head < tail) ?
+ (UART_XMIT_SIZE - tail) : (head - tail);
+
+ if (xmit_count > 0) {
+ if (raw == TRANSMIT_RAW)
+ result =
+ port->sc_ops->sal_puts_raw(start,
+ xmit_count);
+ else
+ result =
+ port->sc_ops->sal_puts(start, xmit_count);
+#ifdef DEBUG
+ if (!result)
+ DPRINTF("`");
+#endif
+ if (result > 0) {
+ xmit_count -= result;
+ port->sc_port.icount.tx += result;
+ tail += result;
+ tail &= UART_XMIT_SIZE - 1;
+ xmit->tail = tail;
+ start = &xmit->buf[tail];
+ }
+ }
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&port->sc_port);
+
+ if (uart_circ_empty(xmit))
+ snp_stop_tx(&port->sc_port, 0); /* no-op for us */
+}
+
+/**
+ * sn_sal_interrupt - Handle console interrupts
+ * @irq: irq #, useful for debug statements
+ * @dev_id: our pointer to our port (sn_cons_port which contains the uart port)
+ * @regs: Saved registers, used by sn_receive_chars for uart_handle_sysrq_char
+ *
+ */
+static irqreturn_t
+sn_sal_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct sn_cons_port *port = (struct sn_cons_port *) dev_id;
+ unsigned long flags;
+ int status = ia64_sn_console_intr_status();
+
+ if (!port)
+ return IRQ_NONE;
+
+ spin_lock_irqsave(&port->sc_port.lock, flags);
+ if (status & SAL_CONSOLE_INTR_RECV) {
+ sn_receive_chars(port, regs);
+ }
+ if (status & SAL_CONSOLE_INTR_XMIT) {
+ sn_transmit_chars(port, TRANSMIT_BUFFERED);
+ }
+ spin_unlock_irqrestore(&port->sc_port.lock, flags);
+ return IRQ_HANDLED;
+}
+
+/**
+ * sn_sal_connect_interrupt - Request interrupt, handled by sn_sal_interrupt
+ * @port: Our sn_cons_port (which contains the uart port)
+ *
+ * returns the console irq if interrupt is successfully registered, else 0
+ *
+ */
+static int
+sn_sal_connect_interrupt(struct sn_cons_port *port)
+{
+ if (request_irq(SGI_UART_VECTOR, sn_sal_interrupt, SA_INTERRUPT,
+ "SAL console driver", port) >= 0) {
+ return SGI_UART_VECTOR;
+ }
+
+ printk(KERN_INFO "sn_console: console proceeding in polled mode\n");
+ return 0;
+}
+
+/**
+ * sn_sal_timer_poll - this function handles polled console mode
+ * @data: A pointer to our sn_cons_port (which contains the uart port)
+ *
+ * data is the pointer that init_timer will store for us. This function is
+ * associated with init_timer to see if there is any console traffic.
+ * Obviously not used in interrupt mode
+ *
+ */
+static void
+sn_sal_timer_poll(unsigned long data)
+{
+ struct sn_cons_port *port = (struct sn_cons_port *) data;
+ unsigned long flags;
+
+ if (!port)
+ return;
+
+ if (!port->sc_port.irq) {
+ spin_lock_irqsave(&port->sc_port.lock, flags);
+ sn_receive_chars(port, NULL);
+ sn_transmit_chars(port, TRANSMIT_RAW);
+ spin_unlock_irqrestore(&port->sc_port.lock, flags);
+ mod_timer(&port->sc_timer,
+ jiffies + port->sc_interrupt_timeout);
+ }
+}
+
+/*
+ * Boot-time initialization code
+ */
+
+/**
+ * sn_sal_switch_to_asynch - Switch to async mode (as opposed to synch)
+ * @port: Our sn_cons_port (which contains the uart port)
+ *
+ * So this is used by sn_sal_serial_console_init (early on, before we're
+ * registered with serial core). It's also used by sn_sal_module_init
+ * right after we've registered with serial core. The later only happens
+ * if we didn't already come through here via sn_sal_serial_console_init.
+ *
+ */
+static void __init
+sn_sal_switch_to_asynch(struct sn_cons_port *port)
+{
+ unsigned long flags;
+
+ if (!port)
+ return;
+
+ DPRINTF("sn_console: about to switch to asynchronous console\n");
+
+ /* without early_printk, we may be invoked late enough to race
+ * with other cpus doing console IO at this point, however
+ * console interrupts will never be enabled */
+ spin_lock_irqsave(&port->sc_port.lock, flags);
+
+ /* early_printk invocation may have done this for us */
+ if (!port->sc_ops) {
+ if (IS_RUNNING_ON_SIMULATOR())
+ port->sc_ops = &sim_ops;
+ else
+ port->sc_ops = &poll_ops;
+ }
+
+ /* we can't turn on the console interrupt (as request_irq
+ * calls kmalloc, which isn't set up yet), so we rely on a
+ * timer to poll for input and push data from the console
+ * buffer.
+ */
+ init_timer(&port->sc_timer);
+ port->sc_timer.function = sn_sal_timer_poll;
+ port->sc_timer.data = (unsigned long) port;
+
+ if (IS_RUNNING_ON_SIMULATOR())
+ port->sc_interrupt_timeout = 6;
+ else {
+ /* 960cps / 16 char FIFO = 60HZ
+ * HZ / (SN_SAL_FIFO_SPEED_CPS / SN_SAL_FIFO_DEPTH) */
+ port->sc_interrupt_timeout =
+ HZ * SN_SAL_UART_FIFO_DEPTH / SN_SAL_UART_FIFO_SPEED_CPS;
+ }
+ mod_timer(&port->sc_timer, jiffies + port->sc_interrupt_timeout);
+
+ port->sc_is_asynch = 1;
+ spin_unlock_irqrestore(&port->sc_port.lock, flags);
+}
+
+/**
+ * sn_sal_switch_to_interrupts - Switch to interrupt driven mode
+ * @port: Our sn_cons_port (which contains the uart port)
+ *
+ * In sn_sal_module_init, after we're registered with serial core and
+ * the port is added, this function is called to switch us to interrupt
+ * mode. We were previously in asynch/polling mode (using init_timer).
+ *
+ * We attempt to switch to interrupt mode here by calling
+ * sn_sal_connect_interrupt. If that works out, we enable receive interrupts.
+ */
+static void __init
+sn_sal_switch_to_interrupts(struct sn_cons_port *port)
+{
+ int irq;
+ unsigned long flags;
+
+ if (!port)
+ return;
+
+ DPRINTF("sn_console: switching to interrupt driven console\n");
+
+ spin_lock_irqsave(&port->sc_port.lock, flags);
+
+ irq = sn_sal_connect_interrupt(port);
+
+ if (irq) {
+ port->sc_port.irq = irq;
+ port->sc_ops = &intr_ops;
+
+ /* turn on receive interrupts */
+ ia64_sn_console_intr_enable(SAL_CONSOLE_INTR_RECV);
+ }
+ spin_unlock_irqrestore(&port->sc_port.lock, flags);
+}
+
+/*
+ * Kernel console definitions
+ */
+
+#ifdef CONFIG_SERIAL_SGI_L1_CONSOLE
+static void sn_sal_console_write(struct console *, const char *, unsigned);
+static int __init sn_sal_console_setup(struct console *, char *);
+extern struct uart_driver sal_console_uart;
+extern struct tty_driver *uart_console_device(struct console *, int *);
+
+static struct console sal_console = {
+ .name = DEVICE_NAME,
+ .write = sn_sal_console_write,
+ .device = uart_console_device,
+ .setup = sn_sal_console_setup,
+ .index = -1, /* unspecified */
+ .data = &sal_console_uart,
+};
+
+#define SAL_CONSOLE &sal_console
+#else
+#define SAL_CONSOLE 0
+#endif /* CONFIG_SERIAL_SGI_L1_CONSOLE */
+
+static struct uart_driver sal_console_uart = {
+ .owner = THIS_MODULE,
+ .driver_name = "sn_console",
+ .dev_name = DEVICE_NAME,
+ .major = 0, /* major/minor set at registration time per USE_DYNAMIC_MINOR */
+ .minor = 0,
+ .nr = 1, /* one port */
+ .cons = SAL_CONSOLE,
+};
+
+/**
+ * sn_sal_module_init - When the kernel loads us, get us rolling w/ serial core
+ *
+ * Before this is called, we've been printing kernel messages in a special
+ * early mode not making use of the serial core infrastructure. When our
+ * driver is loaded for real, we register the driver and port with serial
+ * core and try to enable interrupt driven mode.
+ *
+ */
+static int __init
+sn_sal_module_init(void)
+{
+ int retval;
+
+ printk(KERN_INFO "sn_console: Console driver init\n");
+
+ if (!ia64_platform_is("sn2"))
+ return -ENODEV;
+
+ if (USE_DYNAMIC_MINOR == 1) {
+ misc.minor = MISC_DYNAMIC_MINOR;
+ misc.name = DEVICE_NAME_DYNAMIC;
+ retval = misc_register(&misc);
+ if (retval != 0) {
+ printk("Failed to register console device using misc_register.\n");
+ return -ENODEV;
+ }
+ sal_console_uart.major = MISC_MAJOR;
+ sal_console_uart.minor = misc.minor;
+ }
+ else {
+ sal_console_uart.major = DEVICE_MAJOR;
+ sal_console_uart.minor = DEVICE_MINOR;
+ }
+
+ /* We register the driver and the port before switching to interrupts
+ * or async above so the proper uart structures are populated */
+
+ if (uart_register_driver(&sal_console_uart) < 0) {
+ printk("ERROR sn_sal_module_init failed uart_register_driver, line %d\n",
+ __LINE__);
+ return -ENODEV;
+ }
+
+ sal_console_port.sc_port.lock = SPIN_LOCK_UNLOCKED;
+
+ /* Setup the port struct with the minimum needed */
+ sal_console_port.sc_port.membase = (char *)1; /* just needs to be non-zero */
+ sal_console_port.sc_port.type = PORT_16550A;
+ sal_console_port.sc_port.fifosize = SN_SAL_MAX_CHARS;
+ sal_console_port.sc_port.ops = &sn_console_ops;
+ sal_console_port.sc_port.line = 0;
+
+ if (uart_add_one_port(&sal_console_uart, &sal_console_port.sc_port) < 0) {
+ /* error - not sure what I'd do - so I'll do nothing */
+ printk(KERN_ERR "%s: unable to add port\n", __FUNCTION__);
+ }
+
+ /* when this driver is compiled in, the console initialization
+ * will have already switched us into asynchronous operation
+ * before we get here through the module initcalls */
+ if (!sal_console_port.sc_is_asynch) {
+ sn_sal_switch_to_asynch(&sal_console_port);
+ }
+
+ /* at this point (module_init) we can try to turn on interrupts */
+ if (!IS_RUNNING_ON_SIMULATOR()) {
+ sn_sal_switch_to_interrupts(&sal_console_port);
+ }
+ return 0;
+}
+
+/**
+ * sn_sal_module_exit - When we're unloaded, remove the driver/port
+ *
+ */
+static void __exit
+sn_sal_module_exit(void)
+{
+ del_timer_sync(&sal_console_port.sc_timer);
+ uart_remove_one_port(&sal_console_uart, &sal_console_port.sc_port);
+ uart_unregister_driver(&sal_console_uart);
+ misc_deregister(&misc);
+}
+
+module_init(sn_sal_module_init);
+module_exit(sn_sal_module_exit);
+
+#ifdef CONFIG_SERIAL_SGI_L1_CONSOLE
+
+/**
+ * puts_raw_fixed - sn_sal_console_write helper for adding \r's as required
+ * @puts_raw : puts function to do the writing
+ * @s: input string
+ * @count: length
+ *
+ * We need a \r ahead of every \n for direct writes through
+ * ia64_sn_console_putb (what sal_puts_raw below actually does).
+ *
+ */
+
+static void puts_raw_fixed(int (*puts_raw) (const char *s, int len), const char *s, int count)
+{
+ const char *s1;
+
+ /* Output '\r' before each '\n' */
+ while ((s1 = memchr(s, '\n', count)) != NULL) {
+ puts_raw(s, s1 - s);
+ puts_raw("\r\n", 2);
+ count -= s1 + 1 - s;
+ s = s1 + 1;
+ }
+ puts_raw(s, count);
+}
+
+/**
+ * sn_sal_console_write - Print statements before serial core available
+ * @console: Console to operate on - we ignore since we have just one
+ * @s: String to send
+ * @count: length
+ *
+ * This is referenced in the console struct. It is used for early
+ * console printing before we register with serial core and for things
+ * such as kdb. The console_lock must be held when we get here.
+ *
+ * This function has some code for trying to print output even if the lock
+ * is held. We try to cover the case where a lock holder could have died.
+ * We don't use this special case code if we're not registered with serial
+ * core yet. After we're registered with serial core, the only time this
+ * function would be used is for high level kernel output like magic sys req,
+ * kdb, and printk's.
+ */
+static void
+sn_sal_console_write(struct console *co, const char *s, unsigned count)
+{
+ unsigned long flags = 0;
+ struct sn_cons_port *port = &sal_console_port;
+#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT)
+ static int stole_lock = 0;
+#endif
+
+ BUG_ON(!port->sc_is_asynch);
+
+ /* We can't look at the xmit buffer if we're not registered with serial core
+ * yet. So only do the fancy recovery after registering
+ */
+ if (port->sc_port.info) {
+
+ /* somebody really wants this output, might be an
+ * oops, kdb, panic, etc. make sure they get it. */
+#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT)
+ if (spin_is_locked(&port->sc_port.lock)) {
+ int lhead = port->sc_port.info->xmit.head;
+ int ltail = port->sc_port.info->xmit.tail;
+ int counter, got_lock = 0;
+
+ /*
+ * We attempt to determine if someone has died with the
+ * lock. We wait ~20 secs after the head and tail ptrs
+ * stop moving and assume the lock holder is not functional
+ * and plow ahead. If the lock is freed within the time out
+ * period we re-get the lock and go ahead normally. We also
+ * remember if we have plowed ahead so that we don't have
+ * to wait out the time out period again - the asumption
+ * is that we will time out again.
+ */
+
+ for (counter = 0; counter < 150; mdelay(125), counter++) {
+ if (!spin_is_locked(&port->sc_port.lock) || stole_lock) {
+ if (!stole_lock) {
+ spin_lock_irqsave(&port->sc_port.lock, flags);
+ got_lock = 1;
+ }
+ break;
+ }
+ else {
+ /* still locked */
+ if ((lhead != port->sc_port.info->xmit.head) || (ltail != port->sc_port.info->xmit.tail)) {
+ lhead = port->sc_port.info->xmit.head;
+ ltail = port->sc_port.info->xmit.tail;
+ counter = 0;
+ }
+ }
+ }
+ /* flush anything in the serial core xmit buffer, raw */
+ sn_transmit_chars(port, 1);
+ if (got_lock) {
+ spin_unlock_irqrestore(&port->sc_port.lock, flags);
+ stole_lock = 0;
+ }
+ else {
+ /* fell thru */
+ stole_lock = 1;
+ }
+ puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count);
+ }
+ else {
+ stole_lock = 0;
+#endif
+ spin_lock_irqsave(&port->sc_port.lock, flags);
+ sn_transmit_chars(port, 1);
+ spin_unlock_irqrestore(&port->sc_port.lock, flags);
+
+ puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count);
+ }
+ }
+ else {
+ /* Not yet registered with serial core - simple case */
+ puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count);
+ }
+}
+
+
+/**
+ * sn_sal_console_setup - Set up console for early printing
+ * @co: Console to work with
+ * @options: Options to set
+ *
+ * Altix console doesn't do anything with baud rates, etc, anyway.
+ *
+ * This isn't required since not providing the setup function in the
+ * console struct is ok. However, other patches like KDB plop something
+ * here so providing it is easier.
+ *
+ */
+static int __init
+sn_sal_console_setup(struct console *co, char *options)
+{
+ return 0;
+}
+
+/**
+ * sn_sal_console_write_early - simple early output routine
+ * @co - console struct
+ * @s - string to print
+ * @count - count
+ *
+ * Simple function to provide early output, before even
+ * sn_sal_serial_console_init is called. Referenced in the
+ * console struct registerd in sn_serial_console_early_setup.
+ *
+ */
+static void __init
+sn_sal_console_write_early(struct console *co, const char *s, unsigned count)
+{
+ puts_raw_fixed(sal_console_port.sc_ops->sal_puts_raw, s, count);
+}
+
+/* Used for very early console printing - again, before
+ * sn_sal_serial_console_init is run */
+static struct console sal_console_early __initdata = {
+ .name = "sn_sal",
+ .write = sn_sal_console_write_early,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+
+/**
+ * sn_serial_console_early_setup - Sets up early console output support
+ *
+ * Register a console early on... This is for output before even
+ * sn_sal_serial_cosnole_init is called. This function is called from
+ * setup.c. This allows us to do really early polled writes. When
+ * sn_sal_serial_console_init is called, this console is unregistered
+ * and a new one registered.
+ */
+int __init
+sn_serial_console_early_setup(void)
+{
+ if (!ia64_platform_is("sn2"))
+ return -1;
+
+ if (IS_RUNNING_ON_SIMULATOR())
+ sal_console_port.sc_ops = &sim_ops;
+ else
+ sal_console_port.sc_ops = &poll_ops;
+
+ early_sn_setup(); /* Find SAL entry points */
+ register_console(&sal_console_early);
+
+ return 0;
+}
+
+
+/**
+ * sn_sal_serial_console_init - Early console output - set up for register
+ *
+ * This function is called when regular console init happens. Because we
+ * support even earlier console output with sn_serial_console_early_setup
+ * (called from setup.c directly), this function unregisters the really
+ * early console.
+ *
+ * Note: Even if setup.c doesn't register sal_console_early, unregistering
+ * it here doesn't hurt anything.
+ *
+ */
+static int __init
+sn_sal_serial_console_init(void)
+{
+ if (ia64_platform_is("sn2")) {
+ sn_sal_switch_to_asynch(&sal_console_port);
+ DPRINTF ("sn_sal_serial_console_init : register console\n");
+ register_console(&sal_console);
+ unregister_console(&sal_console_early);
+ }
+ return 0;
+}
+
+console_initcall(sn_sal_serial_console_init);
+
+#endif /* CONFIG_SERIAL_SGI_L1_CONSOLE */
cflag = CREAD | HUPCL | CLOCAL;
s = mode;
- baud = simple_strtoul(s, 0, 0);
+ baud = simple_strtoul(s, NULL, 0);
s = strchr(s, ',');
- bits = simple_strtoul(++s, 0, 0);
+ bits = simple_strtoul(++s, NULL, 0);
s = strchr(s, ',');
parity = *(++s);
s = strchr(s, ',');
- stop = simple_strtoul(++s, 0, 0);
+ stop = simple_strtoul(++s, NULL, 0);
s = strchr(s, ',');
/* XXX handshake is not handled here. */
struct usb_interface *data; /* data interface */
struct tty_struct *tty; /* the corresponding tty */
struct urb *ctrlurb, *readurb, *writeurb; /* urbs */
+ u8 *ctrl_buffer, *read_buffer, *write_buffer; /* buffers of urbs */
+ dma_addr_t ctrl_dma, read_dma, write_dma; /* dma handles of buffers */
struct acm_line line; /* line coding (bits, stop, parity) */
struct work_struct work; /* work queue entry for line discipline waking up */
struct tasklet_struct bh; /* rx processing */
+ spinlock_t throttle_lock; /* synchronize throtteling and read callback */
unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
unsigned int ctrlout; /* output control lines (DTR, RTS) */
unsigned int writesize; /* max packet size for the output bulk endpoint */
+ unsigned int readsize,ctrlsize; /* buffer sizes for freeing */
unsigned int used; /* someone has this acm's device open */
unsigned int minor; /* acm minor number */
unsigned char throttle; /* throttled by tty layer */
unsigned char clocal; /* termios CLOCAL */
unsigned char ready_for_write; /* write urb can be used */
+ unsigned char resubmit_to_unthrottle; /* throtteling has disabled the read urb */
+ unsigned int ctrl_caps; /* control capabilities from the class specific header */
};
/* "Union Functional Descriptor" from CDC spec 5.2.3.X */
/* ... and there could be other slave interfaces */
} __attribute__ ((packed));
-#define CDC_UNION_TYPE 0x06
+/* class specific descriptor types */
+#define CDC_CALL_MANAGEMENT_TYPE 0x01
+#define CDC_AC_MANAGEMENT_TYPE 0x02
+#define CDC_UNION_TYPE 0x06
+#define CDC_COUNTRY_TYPE 0x07
+
#define CDC_DATA_INTERFACE_TYPE 0x0a
+
* drivers/usb/core/sysfs.c
*
* (C) Copyright 2002 David Brownell
- * (C) Copyright 2002 Greg Kroah-Hartman
- * (C) Copyright 2002 IBM Corp.
+ * (C) Copyright 2002,2004 Greg Kroah-Hartman
+ * (C) Copyright 2002,2004 IBM Corp.
*
* All of the sysfs file attributes for usb devices and interfaces.
*
usb_descriptor_attr (bDeviceProtocol, "%02x\n")
usb_descriptor_attr (bNumConfigurations, "%d\n")
+static struct attribute *dev_attrs[] = {
+ /* current configuration's attributes */
+ &dev_attr_bNumInterfaces.attr,
+ &dev_attr_bConfigurationValue.attr,
+ &dev_attr_bmAttributes.attr,
+ &dev_attr_bMaxPower.attr,
+ /* device attributes */
+ &dev_attr_idVendor.attr,
+ &dev_attr_idProduct.attr,
+ &dev_attr_bcdDevice.attr,
+ &dev_attr_bDeviceClass.attr,
+ &dev_attr_bDeviceSubClass.attr,
+ &dev_attr_bDeviceProtocol.attr,
+ &dev_attr_bNumConfigurations.attr,
+ &dev_attr_speed.attr,
+ &dev_attr_devnum.attr,
+ &dev_attr_version.attr,
+ &dev_attr_maxchild.attr,
+ NULL,
+};
+static struct attribute_group dev_attr_grp = {
+ .attrs = dev_attrs,
+};
void usb_create_sysfs_dev_files (struct usb_device *udev)
{
struct device *dev = &udev->dev;
- /* current configuration's attributes */
- device_create_file (dev, &dev_attr_bNumInterfaces);
- device_create_file (dev, &dev_attr_bConfigurationValue);
- device_create_file (dev, &dev_attr_bmAttributes);
- device_create_file (dev, &dev_attr_bMaxPower);
-
- /* device attributes */
- device_create_file (dev, &dev_attr_idVendor);
- device_create_file (dev, &dev_attr_idProduct);
- device_create_file (dev, &dev_attr_bcdDevice);
- device_create_file (dev, &dev_attr_bDeviceClass);
- device_create_file (dev, &dev_attr_bDeviceSubClass);
- device_create_file (dev, &dev_attr_bDeviceProtocol);
- device_create_file (dev, &dev_attr_bNumConfigurations);
-
- /* speed varies depending on how you connect the device */
- device_create_file (dev, &dev_attr_speed);
- // FIXME iff there are other speed configs, show how many
+ sysfs_create_group(&dev->kobj, &dev_attr_grp);
if (udev->descriptor.iManufacturer)
device_create_file (dev, &dev_attr_manufacturer);
device_create_file (dev, &dev_attr_product);
if (udev->descriptor.iSerialNumber)
device_create_file (dev, &dev_attr_serial);
+}
+
+void usb_remove_sysfs_dev_files (struct usb_device *udev)
+{
+ struct device *dev = &udev->dev;
- device_create_file (dev, &dev_attr_devnum);
- device_create_file (dev, &dev_attr_version);
- device_create_file (dev, &dev_attr_maxchild);
+ sysfs_remove_group(&dev->kobj, &dev_attr_grp);
+
+ if (udev->descriptor.iManufacturer)
+ device_remove_file(dev, &dev_attr_manufacturer);
+ if (udev->descriptor.iProduct)
+ device_remove_file(dev, &dev_attr_product);
+ if (udev->descriptor.iSerialNumber)
+ device_remove_file(dev, &dev_attr_serial);
}
/* Interface fields */
usb_intf_attr (bInterfaceProtocol, "%02x\n")
usb_intf_attr (iInterface, "%02x\n")
+static struct attribute *intf_attrs[] = {
+ &dev_attr_bInterfaceNumber.attr,
+ &dev_attr_bAlternateSetting.attr,
+ &dev_attr_bNumEndpoints.attr,
+ &dev_attr_bInterfaceClass.attr,
+ &dev_attr_bInterfaceSubClass.attr,
+ &dev_attr_bInterfaceProtocol.attr,
+ &dev_attr_iInterface.attr,
+ NULL,
+};
+static struct attribute_group intf_attr_grp = {
+ .attrs = intf_attrs,
+};
+
void usb_create_sysfs_intf_files (struct usb_interface *intf)
{
- device_create_file (&intf->dev, &dev_attr_bInterfaceNumber);
- device_create_file (&intf->dev, &dev_attr_bAlternateSetting);
- device_create_file (&intf->dev, &dev_attr_bNumEndpoints);
- device_create_file (&intf->dev, &dev_attr_bInterfaceClass);
- device_create_file (&intf->dev, &dev_attr_bInterfaceSubClass);
- device_create_file (&intf->dev, &dev_attr_bInterfaceProtocol);
- device_create_file (&intf->dev, &dev_attr_iInterface);
+ sysfs_create_group(&intf->dev.kobj, &intf_attr_grp);
+}
+
+void usb_remove_sysfs_intf_files (struct usb_interface *intf)
+{
+ sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp);
}
--- /dev/null
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2002 Hewlett-Packard Company
+ *
+ * Bus Glue for Sharp LH7A404
+ *
+ * Written by Christopher Hoover <ch@hpl.hp.com>
+ * Based on fragments of previous driver by Rusell King et al.
+ *
+ * Modified for LH7A404 from ohci-sa1111.c
+ * by Durgesh Pattamatta <pattamattad@sharpsec.com>
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/arch/hardware.h>
+
+
+extern int usb_disabled(void);
+
+/*-------------------------------------------------------------------------*/
+
+static void lh7a404_start_hc(struct platform_device *dev)
+{
+ printk(KERN_DEBUG __FILE__
+ ": starting LH7A404 OHCI USB Controller\n");
+
+ /*
+ * Now, carefully enable the USB clock, and take
+ * the USB host controller out of reset.
+ */
+ CSC_PWRCNT |= CSC_PWRCNT_USBH_EN; /* Enable clock */
+ udelay(1000);
+ USBH_CMDSTATUS = OHCI_HCR;
+
+ printk(KERN_DEBUG __FILE__
+ ": Clock to USB host has been enabled \n");
+}
+
+static void lh7a404_stop_hc(struct platform_device *dev)
+{
+ printk(KERN_DEBUG __FILE__
+ ": stopping LH7A404 OHCI USB Controller\n");
+
+ CSC_PWRCNT &= ~CSC_PWRCNT_USBH_EN; /* Disable clock */
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+
+static irqreturn_t usb_hcd_lh7a404_hcim_irq (int irq, void *__hcd,
+ struct pt_regs * r)
+{
+ struct usb_hcd *hcd = __hcd;
+
+ return usb_hcd_irq(irq, hcd, r);
+}
+
+/*-------------------------------------------------------------------------*/
+
+void usb_hcd_lh7a404_remove (struct usb_hcd *, struct platform_device *);
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+
+/**
+ * usb_hcd_lh7a404_probe - initialize LH7A404-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ */
+int usb_hcd_lh7a404_probe (const struct hc_driver *driver,
+ struct usb_hcd **hcd_out,
+ struct platform_device *dev)
+{
+ int retval;
+ struct usb_hcd *hcd = 0;
+
+ unsigned int *addr = NULL;
+
+ if (!request_mem_region(dev->resource[0].start,
+ dev->resource[0].end
+ - dev->resource[0].start + 1, hcd_name)) {
+ pr_debug("request_mem_region failed");
+ return -EBUSY;
+ }
+
+
+ lh7a404_start_hc(dev);
+
+ addr = ioremap(dev->resource[0].start,
+ dev->resource[0].end
+ - dev->resource[0].start + 1);
+ if (!addr) {
+ pr_debug("ioremap failed");
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+
+ hcd = driver->hcd_alloc ();
+ if (hcd == NULL){
+ pr_debug ("hcd_alloc failed");
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ if(dev->resource[1].flags != IORESOURCE_IRQ){
+ pr_debug ("resource[1] is not IORESOURCE_IRQ");
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ hcd->driver = (struct hc_driver *) driver;
+ hcd->description = driver->description;
+ hcd->irq = dev->resource[1].start;
+ hcd->regs = addr;
+ hcd->self.controller = &dev->dev;
+
+ retval = hcd_buffer_create (hcd);
+ if (retval != 0) {
+ pr_debug ("pool alloc fail");
+ goto err1;
+ }
+
+ retval = request_irq (hcd->irq, usb_hcd_lh7a404_hcim_irq, SA_INTERRUPT,
+ hcd->description, hcd);
+ if (retval != 0) {
+ pr_debug("request_irq failed");
+ retval = -EBUSY;
+ goto err2;
+ }
+
+ pr_debug ("%s (LH7A404) at 0x%p, irq %d",
+ hcd->description, hcd->regs, hcd->irq);
+
+ usb_bus_init (&hcd->self);
+ hcd->self.op = &usb_hcd_operations;
+ hcd->self.hcpriv = (void *) hcd;
+ hcd->self.bus_name = "lh7a404";
+ hcd->product_desc = "LH7A404 OHCI";
+
+ INIT_LIST_HEAD (&hcd->dev_list);
+
+ usb_register_bus (&hcd->self);
+
+ if ((retval = driver->start (hcd)) < 0)
+ {
+ usb_hcd_lh7a404_remove(hcd, dev);
+ return retval;
+ }
+
+ *hcd_out = hcd;
+ return 0;
+
+ err2:
+ hcd_buffer_destroy (hcd);
+ if (hcd)
+ driver->hcd_free(hcd);
+ err1:
+ lh7a404_stop_hc(dev);
+ release_mem_region(dev->resource[0].start,
+ dev->resource[0].end
+ - dev->resource[0].start + 1);
+ return retval;
+}
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_lh7a404_remove - shutdown processing for LH7A404-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_lh7a404_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+void usb_hcd_lh7a404_remove (struct usb_hcd *hcd, struct platform_device *dev)
+{
+ void *base;
+
+ pr_debug ("remove: %s, state %x", hcd->self.bus_name, hcd->state);
+
+ if (in_interrupt ())
+ BUG ();
+
+ hcd->state = USB_STATE_QUIESCING;
+
+ pr_debug ("%s: roothub graceful disconnect", hcd->self.bus_name);
+ usb_disconnect (&hcd->self.root_hub);
+
+ hcd->driver->stop (hcd);
+ hcd->state = USB_STATE_HALT;
+
+ free_irq (hcd->irq, hcd);
+ hcd_buffer_destroy (hcd);
+
+ usb_deregister_bus (&hcd->self);
+
+ base = hcd->regs;
+ hcd->driver->hcd_free (hcd);
+
+ lh7a404_stop_hc(dev);
+ release_mem_region(dev->resource[0].start,
+ dev->resource[0].end
+ - dev->resource[0].start + 1);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int __devinit
+ohci_lh7a404_start (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int ret;
+
+ ohci_dbg (ohci, "ohci_lh7a404_start, ohci:%p", ohci);
+
+ ohci->hcca = dma_alloc_coherent (hcd->self.controller,
+ sizeof *ohci->hcca, &ohci->hcca_dma, 0);
+ if (!ohci->hcca)
+ return -ENOMEM;
+
+ ohci_dbg (ohci, "ohci_lh7a404_start, ohci->hcca:%p",
+ ohci->hcca);
+
+ memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
+
+ if ((ret = ohci_mem_init (ohci)) < 0) {
+ ohci_stop (hcd);
+ return ret;
+ }
+ ohci->regs = hcd->regs;
+
+ if (hc_reset (ohci) < 0) {
+ ohci_stop (hcd);
+ return -ENODEV;
+ }
+
+ if (hc_start (ohci) < 0) {
+ err ("can't start %s", ohci->hcd.self.bus_name);
+ ohci_stop (hcd);
+ return -EBUSY;
+ }
+ create_debug_files (ohci);
+
+#ifdef DEBUG
+ ohci_dump (ohci, 1);
+#endif /*DEBUG*/
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ohci_lh7a404_hc_driver = {
+ .description = hcd_name,
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_USB11,
+
+ /*
+ * basic lifecycle operations
+ */
+ .start = ohci_lh7a404_start,
+#ifdef CONFIG_PM
+ /* suspend: ohci_lh7a404_suspend, -- tbd */
+ /* resume: ohci_lh7a404_resume, -- tbd */
+#endif /*CONFIG_PM*/
+ .stop = ohci_stop,
+
+ /*
+ * memory lifecycle (except per-request)
+ */
+ .hcd_alloc = ohci_hcd_alloc,
+ .hcd_free = ohci_hcd_free,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_hcd_lh7a404_drv_probe(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = NULL;
+ int ret;
+
+ pr_debug ("In ohci_hcd_lh7a404_drv_probe");
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ ret = usb_hcd_lh7a404_probe(&ohci_lh7a404_hc_driver, &hcd, pdev);
+
+ if (ret == 0)
+ dev_set_drvdata(dev, hcd);
+
+ return ret;
+}
+
+static int ohci_hcd_lh7a404_drv_remove(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ usb_hcd_lh7a404_remove(hcd, pdev);
+ dev_set_drvdata(dev, NULL);
+ return 0;
+}
+ /*TBD*/
+/*static int ohci_hcd_lh7a404_drv_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+ return 0;
+}
+static int ohci_hcd_lh7a404_drv_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+
+ return 0;
+}
+*/
+
+static struct device_driver ohci_hcd_lh7a404_driver = {
+ .name = "lh7a404-ohci",
+ .bus = &platform_bus_type,
+ .probe = ohci_hcd_lh7a404_drv_probe,
+ .remove = ohci_hcd_lh7a404_drv_remove,
+ /*.suspend = ohci_hcd_lh7a404_drv_suspend, */
+ /*.resume = ohci_hcd_lh7a404_drv_resume, */
+};
+
+static int __init ohci_hcd_lh7a404_init (void)
+{
+ pr_debug (DRIVER_INFO " (LH7A404)");
+ pr_debug ("block sizes: ed %d td %d\n",
+ sizeof (struct ed), sizeof (struct td));
+
+ return driver_register(&ohci_hcd_lh7a404_driver);
+}
+
+static void __exit ohci_hcd_lh7a404_cleanup (void)
+{
+ driver_unregister(&ohci_hcd_lh7a404_driver);
+}
+
+module_init (ohci_hcd_lh7a404_init);
+module_exit (ohci_hcd_lh7a404_cleanup);
--- /dev/null
+/***************************************************************************
+ * V4L2 driver for SN9C10[12] PC Camera Controllers *
+ * *
+ * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _SN9C102_H_
+#define _SN9C102_H_
+
+#include <linux/version.h>
+#include <linux/usb.h>
+#include <linux/videodev.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/types.h>
+#include <linux/param.h>
+#include <linux/rwsem.h>
+
+#include "sn9c102_sensor.h"
+
+/*****************************************************************************/
+
+#define SN9C102_DEBUG
+#define SN9C102_DEBUG_LEVEL 2
+#define SN9C102_MAX_DEVICES 64
+#define SN9C102_MAX_FRAMES 32
+#define SN9C102_URBS 2
+#define SN9C102_ISO_PACKETS 7
+#define SN9C102_ALTERNATE_SETTING 8
+#define SN9C102_CTRL_TIMEOUT 10*HZ
+
+/*****************************************************************************/
+
+#define SN9C102_MODULE_NAME "V4L2 driver for SN9C10[12] PC Camera Controllers"
+#define SN9C102_MODULE_AUTHOR "(C) 2004 Luca Risolia"
+#define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
+#define SN9C102_MODULE_LICENSE "GPL"
+#define SN9C102_MODULE_VERSION "1:1.01-beta"
+#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 1)
+
+SN9C102_ID_TABLE;
+SN9C102_SENSOR_TABLE;
+
+enum sn9c102_frame_state {
+ F_UNUSED,
+ F_QUEUED,
+ F_GRABBING,
+ F_DONE,
+ F_ERROR,
+};
+
+struct sn9c102_frame_t {
+ void* bufmem;
+ struct v4l2_buffer buf;
+ enum sn9c102_frame_state state;
+ struct list_head frame;
+ unsigned long vma_use_count;
+};
+
+enum sn9c102_dev_state {
+ DEV_INITIALIZED = 0x01,
+ DEV_DISCONNECTED = 0x02,
+ DEV_MISCONFIGURED = 0x04,
+};
+
+enum sn9c102_io_method {
+ IO_NONE,
+ IO_READ,
+ IO_MMAP,
+};
+
+enum sn9c102_stream_state {
+ STREAM_OFF,
+ STREAM_INTERRUPT,
+ STREAM_ON,
+};
+
+struct sn9c102_sysfs_attr {
+ u8 reg, val, i2c_reg, i2c_val;
+};
+
+static DECLARE_MUTEX(sn9c102_sysfs_lock);
+static DECLARE_RWSEM(sn9c102_disconnect);
+
+struct sn9c102_device {
+ struct device dev;
+
+ struct video_device* v4ldev;
+
+ struct sn9c102_sensor* sensor;
+
+ struct usb_device* usbdev;
+ struct urb* urb[SN9C102_URBS];
+ void* transfer_buffer[SN9C102_URBS];
+ u8* control_buffer;
+
+ struct sn9c102_frame_t *frame_current, frame[SN9C102_MAX_FRAMES];
+ struct list_head inqueue, outqueue;
+ u32 frame_count, nbuffers;
+
+ enum sn9c102_io_method io;
+ enum sn9c102_stream_state stream;
+
+ struct sn9c102_sysfs_attr sysfs;
+ u16 reg[32];
+
+ enum sn9c102_dev_state state;
+ u8 users;
+
+ struct semaphore dev_sem, fileop_sem;
+ spinlock_t queue_lock;
+ wait_queue_head_t open, wait_frame, wait_stream;
+};
+
+/*****************************************************************************/
+
+void
+sn9c102_attach_sensor(struct sn9c102_device* cam,
+ struct sn9c102_sensor* sensor)
+{
+ cam->sensor = sensor;
+ cam->sensor->dev = &cam->dev;
+ cam->sensor->usbdev = cam->usbdev;
+}
+
+/*****************************************************************************/
+
+#undef DBG
+#undef KDBG
+#ifdef SN9C102_DEBUG
+# define DBG(level, fmt, args...) \
+{ \
+ if (debug >= (level)) { \
+ if ((level) == 1) \
+ dev_err(&cam->dev, fmt "\n", ## args); \
+ else if ((level) == 2) \
+ dev_info(&cam->dev, fmt "\n", ## args); \
+ else if ((level) >= 3) \
+ dev_info(&cam->dev, "[%s:%d] " fmt "\n", \
+ __FUNCTION__, __LINE__ , ## args); \
+ } \
+}
+# define KDBG(level, fmt, args...) \
+{ \
+ if (debug >= (level)) { \
+ if ((level) == 1 || (level) == 2) \
+ pr_info("sn9c102: " fmt "\n", ## args); \
+ else if ((level) == 3) \
+ pr_debug("sn9c102: [%s:%d] " fmt "\n", __FUNCTION__, \
+ __LINE__ , ## args); \
+ } \
+}
+#else
+# define KDBG(level, fmt, args...) do {;} while(0);
+# define DBG(level, fmt, args...) do {;} while(0);
+#endif
+
+#undef PDBG
+#define PDBG(fmt, args...) \
+dev_info(&cam->dev, "[%s:%d] " fmt "\n", __FUNCTION__, __LINE__ , ## args);
+
+#undef PDBGG
+#define PDBGG(fmt, args...) do {;} while(0); /* placeholder */
+
+#endif /* _SN9C102_H_ */
--- /dev/null
+/***************************************************************************
+ * V4L2 driver for SN9C10[12] PC Camera Controllers *
+ * *
+ * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/stddef.h>
+#include <linux/ioctl.h>
+#include <linux/poll.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/page-flags.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+
+#include "sn9c102.h"
+
+/*****************************************************************************/
+
+MODULE_DEVICE_TABLE(usb, sn9c102_id_table);
+
+MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL);
+MODULE_DESCRIPTION(SN9C102_MODULE_NAME);
+MODULE_VERSION(SN9C102_MODULE_VERSION);
+MODULE_LICENSE(SN9C102_MODULE_LICENSE);
+
+static short video_nr[] = {[0 ... SN9C102_MAX_DEVICES-1] = -1};
+static unsigned int nv;
+module_param_array(video_nr, short, nv, 0444);
+MODULE_PARM_DESC(video_nr,
+ "\n<-1|n[,...]> Specify V4L2 minor mode number."
+ "\n -1 = use next available (default)"
+ "\n n = use minor number n (integer >= 0)"
+ "\nYou can specify up to "__MODULE_STRING(SN9C102_MAX_DEVICES)
+ " cameras this way."
+ "\nFor example:"
+ "\nvideo_nr=-1,2,-1 would assign minor number 2 to"
+ "\nthe second camera and use auto for the first"
+ "\none and for every other camera."
+ "\n");
+
+#ifdef SN9C102_DEBUG
+static unsigned short debug = SN9C102_DEBUG_LEVEL;
+module_param(debug, ushort, 0644);
+MODULE_PARM_DESC(debug,
+ "\n<n> Debugging information level, from 0 to 3:"
+ "\n0 = none (use carefully)"
+ "\n1 = critical errors"
+ "\n2 = significant informations"
+ "\n3 = more verbose messages"
+ "\nLevel 3 is useful for testing only, when only "
+ "one device is used."
+ "\nDefault value is "__MODULE_STRING(SN9C102_DEBUG_LEVEL)"."
+ "\n");
+#endif
+
+/*****************************************************************************/
+
+typedef char sn9c102_sof_header_t[7];
+typedef char sn9c102_eof_header_t[4];
+
+static sn9c102_sof_header_t sn9c102_sof_header[] = {
+ {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x00},
+ {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x01},
+};
+
+/* Number of random bytes that complete the SOF above headers */
+#define SN9C102_SOFLEN 5
+
+static sn9c102_eof_header_t sn9c102_eof_header[] = {
+ {0x00, 0x00, 0x00, 0x00},
+ {0x40, 0x00, 0x00, 0x00},
+ {0x80, 0x00, 0x00, 0x00},
+ {0xc0, 0x00, 0x00, 0x00},
+};
+
+/*****************************************************************************/
+
+static inline unsigned long kvirt_to_pa(unsigned long adr)
+{
+ unsigned long kva, ret;
+
+ kva = (unsigned long)page_address(vmalloc_to_page((void *)adr));
+ kva |= adr & (PAGE_SIZE-1);
+ ret = __pa(kva);
+ return ret;
+}
+
+
+static void* rvmalloc(size_t size)
+{
+ void* mem;
+ unsigned long adr;
+
+ size = PAGE_ALIGN(size);
+
+ mem = vmalloc_32((unsigned long)size);
+ if (!mem)
+ return NULL;
+
+ memset(mem, 0, size);
+
+ adr = (unsigned long)mem;
+ while (size > 0) {
+ SetPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ return mem;
+}
+
+
+static void rvfree(void* mem, size_t size)
+{
+ unsigned long adr;
+
+ if (!mem)
+ return;
+
+ size = PAGE_ALIGN(size);
+
+ adr = (unsigned long)mem;
+ while (size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ vfree(mem);
+}
+
+
+static u32 sn9c102_request_buffers(struct sn9c102_device* cam, u32 count)
+{
+ struct v4l2_pix_format* p = &(cam->sensor->pix_format);
+ const size_t imagesize = (p->width * p->height * p->priv)/8;
+ void* buff = NULL;
+ u32 i;
+
+ if (count > SN9C102_MAX_FRAMES)
+ count = SN9C102_MAX_FRAMES;
+
+ cam->nbuffers = count;
+ while (cam->nbuffers > 0) {
+ if ((buff = rvmalloc(cam->nbuffers * imagesize)))
+ break;
+ cam->nbuffers--;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++) {
+ cam->frame[i].bufmem = buff + i*imagesize;
+ cam->frame[i].buf.index = i;
+ cam->frame[i].buf.m.offset = i*imagesize;
+ cam->frame[i].buf.length = imagesize;
+ cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cam->frame[i].buf.sequence = 0;
+ cam->frame[i].buf.field = V4L2_FIELD_NONE;
+ cam->frame[i].buf.memory = V4L2_MEMORY_MMAP;
+ cam->frame[i].buf.flags = 0;
+ }
+
+ return cam->nbuffers;
+}
+
+
+static void sn9c102_release_buffers(struct sn9c102_device* cam)
+{
+ if (cam->nbuffers) {
+ rvfree(cam->frame[0].bufmem,
+ cam->nbuffers * cam->frame[0].buf.length);
+ cam->nbuffers = 0;
+ }
+}
+
+
+static void sn9c102_empty_framequeues(struct sn9c102_device* cam)
+{
+ u32 i;
+
+ INIT_LIST_HEAD(&cam->inqueue);
+ INIT_LIST_HEAD(&cam->outqueue);
+
+ for (i = 0; i < SN9C102_MAX_FRAMES; i++) {
+ cam->frame[i].state = F_UNUSED;
+ cam->frame[i].buf.bytesused = 0;
+ }
+}
+
+
+static void sn9c102_queue_unusedframes(struct sn9c102_device* cam)
+{
+ unsigned long lock_flags;
+ u32 i;
+
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].state == F_UNUSED) {
+ cam->frame[i].state = F_QUEUED;
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_add_tail(&cam->frame[i].frame, &cam->inqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+ }
+}
+
+/*****************************************************************************/
+
+int sn9c102_write_reg(struct sn9c102_device* cam, u8 value, u16 index)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* buff = cam->control_buffer;
+ int res;
+
+ if (index == 0x18)
+ value = (value & 0xcf) | (cam->reg[0x18] & 0x30);
+
+ *buff = value;
+
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
+ index, 0, buff, 1, SN9C102_CTRL_TIMEOUT);
+ if (res < 0) {
+ DBG(3, "Failed to write a register (value 0x%02X, index "
+ "0x%02X, error %d)", value, index, res)
+ return -1;
+ }
+
+ cam->reg[index] = value;
+
+ return 0;
+}
+
+
+/* NOTE: reading some registers always returns 0 */
+static int sn9c102_read_reg(struct sn9c102_device* cam, u16 index)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* buff = cam->control_buffer;
+ int res;
+
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
+ index, 0, buff, 1, SN9C102_CTRL_TIMEOUT);
+ if (res < 0)
+ DBG(3, "Failed to read a register (index 0x%02X, error %d)",
+ index, res)
+
+ return (res >= 0) ? (int)(*buff) : -1;
+}
+
+
+int sn9c102_pread_reg(struct sn9c102_device* cam, u16 index)
+{
+ if (index > 0x1f)
+ return -EINVAL;
+
+ return cam->reg[index];
+}
+
+
+static int
+sn9c102_i2c_wait(struct sn9c102_device* cam, struct sn9c102_sensor* sensor)
+{
+ int i, r;
+
+ for (i = 1; i <= 5; i++) {
+ r = sn9c102_read_reg(cam, 0x08);
+ if (r < 0)
+ return -EIO;
+ if (r & 0x04)
+ return 0;
+ if (sensor->frequency & SN9C102_I2C_400KHZ)
+ udelay(5*8);
+ else
+ udelay(16*8);
+ }
+ return -EBUSY;
+}
+
+
+static int
+sn9c102_i2c_detect_read_error(struct sn9c102_device* cam,
+ struct sn9c102_sensor* sensor)
+{
+ int r;
+ r = sn9c102_read_reg(cam, 0x08);
+ return (r < 0 || (r >= 0 && !(r & 0x08))) ? -EIO : 0;
+}
+
+
+static int
+sn9c102_i2c_detect_write_error(struct sn9c102_device* cam,
+ struct sn9c102_sensor* sensor)
+{
+ int r;
+ r = sn9c102_read_reg(cam, 0x08);
+ return (r < 0 || (r >= 0 && (r & 0x08))) ? -EIO : 0;
+}
+
+
+int
+sn9c102_i2c_try_read(struct sn9c102_device* cam,
+ struct sn9c102_sensor* sensor, u8 address)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* data = cam->control_buffer;
+ int err = 0, res;
+
+ /* Write cycle - address */
+ data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) |
+ ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | 0x10;
+ data[1] = sensor->slave_write_id;
+ data[2] = address;
+ data[7] = 0x10;
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
+ 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ err += sn9c102_i2c_wait(cam, sensor);
+
+ /* Read cycle - 1 byte */
+ data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) |
+ ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) |
+ 0x10 | 0x02;
+ data[1] = sensor->slave_read_id;
+ data[7] = 0x10;
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
+ 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ err += sn9c102_i2c_wait(cam, sensor);
+
+ /* The read byte will be placed in data[4] */
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
+ 0x0a, 0, data, 5, SN9C102_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ err += sn9c102_i2c_detect_read_error(cam, sensor);
+
+ if (err)
+ DBG(3, "I2C read failed for %s image sensor", sensor->name)
+
+ PDBGG("I2C read: address 0x%02X, value: 0x%02X", address, data[4])
+
+ return err ? -1 : (int)data[4];
+}
+
+
+int
+sn9c102_i2c_try_raw_write(struct sn9c102_device* cam,
+ struct sn9c102_sensor* sensor, u8 n, u8 data0,
+ u8 data1, u8 data2, u8 data3, u8 data4, u8 data5)
+{
+ struct usb_device* udev = cam->usbdev;
+ u8* data = cam->control_buffer;
+ int err = 0, res;
+
+ /* Write cycle. It usually is address + value */
+ data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) |
+ ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0)
+ | ((n - 1) << 4);
+ data[1] = data0;
+ data[2] = data1;
+ data[3] = data2;
+ data[4] = data3;
+ data[5] = data4;
+ data[6] = data5;
+ data[7] = 0x10;
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
+ 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
+ if (res < 0)
+ err += res;
+
+ err += sn9c102_i2c_wait(cam, sensor);
+ err += sn9c102_i2c_detect_write_error(cam, sensor);
+
+ if (err)
+ DBG(3, "I2C write failed for %s image sensor", sensor->name)
+
+ PDBGG("I2C write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, "
+ "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X",
+ n, data0, data1, data2, data3, data4, data5)
+
+ return err ? -1 : 0;
+}
+
+
+int
+sn9c102_i2c_try_write(struct sn9c102_device* cam,
+ struct sn9c102_sensor* sensor, u8 address, u8 value)
+{
+ return sn9c102_i2c_try_raw_write(cam, sensor, 3,
+ sensor->slave_write_id, address,
+ value, 0, 0, 0);
+}
+
+
+int sn9c102_i2c_read(struct sn9c102_device* cam, u8 address)
+{
+ if (!cam->sensor)
+ return -1;
+
+ return sn9c102_i2c_try_read(cam, cam->sensor, address);
+}
+
+
+int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value)
+{
+ if (!cam->sensor)
+ return -1;
+
+ return sn9c102_i2c_try_write(cam, cam->sensor, address, value);
+}
+
+/*****************************************************************************/
+
+static void* sn9c102_find_sof_header(void* mem, size_t len)
+{
+ size_t soflen=sizeof(sn9c102_sof_header_t), SOFLEN=SN9C102_SOFLEN, i;
+ u8 j, n = sizeof(sn9c102_sof_header) / soflen;
+
+ for (i = 0; (len >= soflen+SOFLEN) && (i <= len-soflen-SOFLEN); i++)
+ for (j = 0; j < n; j++)
+ if (!memcmp(mem + i, sn9c102_sof_header[j], soflen))
+ /* Skips the header */
+ return mem + i + soflen + SOFLEN;
+
+ return NULL;
+}
+
+
+static void* sn9c102_find_eof_header(void* mem, size_t len)
+{
+ size_t eoflen = sizeof(sn9c102_eof_header_t), i;
+ unsigned j, n = sizeof(sn9c102_eof_header) / eoflen;
+
+ for (i = 0; (len >= eoflen) && (i <= len - eoflen); i++)
+ for (j = 0; j < n; j++)
+ if (!memcmp(mem + i, sn9c102_eof_header[j], eoflen))
+ return mem + i;
+
+ return NULL;
+}
+
+
+static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
+{
+ struct sn9c102_device* cam = urb->context;
+ struct sn9c102_frame_t** f;
+ unsigned long lock_flags;
+ u8 i;
+ int err = 0;
+
+ if (urb->status == -ENOENT)
+ return;
+
+ f = &cam->frame_current;
+
+ if (cam->stream == STREAM_INTERRUPT) {
+ cam->stream = STREAM_OFF;
+ if ((*f))
+ (*f)->state = F_QUEUED;
+ DBG(3, "Stream interrupted")
+ wake_up_interruptible(&cam->wait_stream);
+ }
+
+ if ((cam->state & DEV_DISCONNECTED)||(cam->state & DEV_MISCONFIGURED))
+ return;
+
+ if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue))
+ goto resubmit_urb;
+
+ if (!(*f))
+ (*f) = list_entry(cam->inqueue.next, struct sn9c102_frame_t,
+ frame);
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ unsigned int img, len, status;
+ void *pos, *sof, *eof;
+
+ len = urb->iso_frame_desc[i].actual_length;
+ status = urb->iso_frame_desc[i].status;
+ pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
+
+ if (status) {
+ DBG(3, "Error in isochronous frame")
+ (*f)->state = F_ERROR;
+ continue;
+ }
+
+ PDBGG("Isochrnous frame: length %u, #%u i", len, i)
+
+ /* NOTE: It is probably correct to assume that SOF and EOF
+ headers do not occur between two consecutive packets,
+ but who knows..Whatever is the truth, this assumption
+ doesn't introduce bugs. */
+
+redo:
+ sof = sn9c102_find_sof_header(pos, len);
+ if (!sof) {
+ eof = sn9c102_find_eof_header(pos, len);
+ if ((*f)->state == F_GRABBING) {
+end_of_frame:
+ img = len;
+
+ if (eof)
+ img = (eof > pos) ? eof - pos - 1 : 0;
+
+ if ((*f)->buf.bytesused+img>(*f)->buf.length) {
+ u32 b = (*f)->buf.bytesused + img -
+ (*f)->buf.length;
+ img = (*f)->buf.length -
+ (*f)->buf.bytesused;
+ DBG(3, "Expected EOF not found: "
+ "video frame cut")
+ if (eof)
+ DBG(3, "Exceeded limit: +%u "
+ "bytes", (unsigned)(b))
+ }
+
+ memcpy((*f)->bufmem + (*f)->buf.bytesused, pos,
+ img);
+
+ if ((*f)->buf.bytesused == 0)
+ do_gettimeofday(&(*f)->buf.timestamp);
+
+ (*f)->buf.bytesused += img;
+
+ if ((*f)->buf.bytesused == (*f)->buf.length) {
+ u32 b = (*f)->buf.bytesused;
+ (*f)->state = F_DONE;
+ (*f)->buf.sequence= ++cam->frame_count;
+ spin_lock_irqsave(&cam->queue_lock,
+ lock_flags);
+ list_move_tail(&(*f)->frame,
+ &cam->outqueue);
+ if (!list_empty(&cam->inqueue))
+ (*f) = list_entry(
+ cam->inqueue.next,
+ struct sn9c102_frame_t,
+ frame );
+ else
+ (*f) = NULL;
+ spin_unlock_irqrestore(&cam->queue_lock
+ , lock_flags);
+ DBG(3, "Video frame captured: "
+ "%lu bytes", (unsigned long)(b))
+
+ if (!(*f))
+ goto resubmit_urb;
+
+ } else if (eof) {
+ (*f)->state = F_ERROR;
+ DBG(3, "Not expected EOF after %lu "
+ "bytes of image data",
+ (unsigned long)((*f)->buf.bytesused))
+ }
+
+ if (sof) /* (1) */
+ goto start_of_frame;
+
+ } else if (eof) {
+ DBG(3, "EOF without SOF")
+ continue;
+
+ } else {
+ PDBGG("Ignoring pointless isochronous frame")
+ continue;
+ }
+
+ } else if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) {
+start_of_frame:
+ (*f)->state = F_GRABBING;
+ (*f)->buf.bytesused = 0;
+ len -= (sof - pos);
+ pos = sof;
+ DBG(3, "SOF detected: new video frame")
+ if (len)
+ goto redo;
+
+ } else if ((*f)->state == F_GRABBING) {
+ eof = sn9c102_find_eof_header(pos, len);
+ if (eof && eof < sof)
+ goto end_of_frame; /* (1) */
+ else {
+ DBG(3, "SOF before expected EOF after %lu "
+ "bytes of image data",
+ (unsigned long)((*f)->buf.bytesused))
+ goto start_of_frame;
+ }
+ }
+ }
+
+resubmit_urb:
+ urb->dev = cam->usbdev;
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0 && err != -EPERM) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "usb_submit_urb() failed")
+ }
+
+ wake_up_interruptible(&cam->wait_frame);
+}
+
+
+static int sn9c102_start_transfer(struct sn9c102_device* cam)
+{
+ struct usb_device *udev = cam->usbdev;
+ struct urb* urb;
+ const unsigned int wMaxPacketSize[] = {0, 128, 256, 384, 512,
+ 680, 800, 900, 1023};
+ const unsigned int psz = wMaxPacketSize[SN9C102_ALTERNATE_SETTING];
+ s8 i, j;
+ int err = 0;
+
+ for (i = 0; i < SN9C102_URBS; i++) {
+ cam->transfer_buffer[i] = kmalloc(SN9C102_ISO_PACKETS * psz,
+ GFP_KERNEL);
+ if (!cam->transfer_buffer[i]) {
+ err = -ENOMEM;
+ DBG(1, "Not enough memory")
+ goto free_buffers;
+ }
+ }
+
+ for (i = 0; i < SN9C102_URBS; i++) {
+ urb = usb_alloc_urb(SN9C102_ISO_PACKETS, GFP_KERNEL);
+ cam->urb[i] = urb;
+ if (!urb) {
+ err = -ENOMEM;
+ DBG(1, "usb_alloc_urb() failed")
+ goto free_urbs;
+ }
+ urb->dev = udev;
+ urb->context = cam;
+ urb->pipe = usb_rcvisocpipe(udev, 1);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->number_of_packets = SN9C102_ISO_PACKETS;
+ urb->complete = sn9c102_urb_complete;
+ urb->transfer_buffer = cam->transfer_buffer[i];
+ urb->transfer_buffer_length = psz * SN9C102_ISO_PACKETS;
+ urb->interval = 1;
+ for (j = 0; j < SN9C102_ISO_PACKETS; j++) {
+ urb->iso_frame_desc[j].offset = psz * j;
+ urb->iso_frame_desc[j].length = psz;
+ }
+ }
+
+ /* Enable video */
+ if (!(cam->reg[0x01] & 0x04)) {
+ err = sn9c102_write_reg(cam, cam->reg[0x01] | 0x04, 0x01);
+ if (err) {
+ err = -EIO;
+ DBG(1, "I/O hardware error")
+ goto free_urbs;
+ }
+ }
+
+ err = usb_set_interface(udev, 0, SN9C102_ALTERNATE_SETTING);
+ if (err) {
+ DBG(1, "usb_set_interface() failed")
+ goto free_urbs;
+ }
+
+ cam->frame_current = NULL;
+
+ for (i = 0; i < SN9C102_URBS; i++) {
+ err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
+ if (err) {
+ for (j = i-1; j >= 0; j--)
+ usb_kill_urb(cam->urb[j]);
+ DBG(1, "usb_submit_urb() failed, error %d", err)
+ goto free_urbs;
+ }
+ }
+
+ return 0;
+
+free_urbs:
+ for (i = 0; (i < SN9C102_URBS) && cam->urb[i]; i++)
+ usb_free_urb(cam->urb[i]);
+
+free_buffers:
+ for (i = 0; (i < SN9C102_URBS) && cam->transfer_buffer[i]; i++)
+ kfree(cam->transfer_buffer[i]);
+
+ return err;
+}
+
+
+static int sn9c102_stop_transfer(struct sn9c102_device* cam)
+{
+ struct usb_device *udev = cam->usbdev;
+ s8 i;
+ int err = 0;
+
+ if (cam->state & DEV_DISCONNECTED)
+ return 0;
+
+ for (i = SN9C102_URBS-1; i >= 0; i--) {
+ usb_kill_urb(cam->urb[i]);
+ usb_free_urb(cam->urb[i]);
+ kfree(cam->transfer_buffer[i]);
+ }
+
+ err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */
+ if (err)
+ DBG(3, "usb_set_interface() failed")
+
+ return err;
+}
+
+/*****************************************************************************/
+
+static u8 sn9c102_strtou8(const char* buff, size_t len, ssize_t* count)
+{
+ char str[5];
+ char* endp;
+ unsigned long val;
+
+ if (len < 4) {
+ strncpy(str, buff, len);
+ str[len+1] = '\0';
+ } else {
+ strncpy(str, buff, 4);
+ str[4] = '\0';
+ }
+
+ val = simple_strtoul(str, &endp, 0);
+
+ *count = 0;
+ if (val <= 0xff)
+ *count = (ssize_t)(endp - str);
+ if ((*count) && (len == *count+1) && (buff[*count] == '\n'))
+ *count += 1;
+
+ return (u8)val;
+}
+
+/* NOTE 1: being inside one of the following methods implies that the v4l
+ device exists for sure (see kobjects and reference counters)
+ NOTE 2: buffers are PAGE_SIZE long */
+
+static ssize_t sn9c102_show_reg(struct class_device* cd, char* buf)
+{
+ struct sn9c102_device* cam;
+ ssize_t count;
+
+ if (down_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ up(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ count = sprintf(buf, "%u\n", cam->sysfs.reg);
+
+ up(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+sn9c102_store_reg(struct class_device* cd, const char* buf, size_t len)
+{
+ struct sn9c102_device* cam;
+ u8 index;
+ ssize_t count;
+
+ if (down_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ up(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ index = sn9c102_strtou8(buf, len, &count);
+ if (index > 0x1f || !count) {
+ up(&sn9c102_sysfs_lock);
+ return -EINVAL;
+ }
+
+ cam->sysfs.reg = index;
+
+ DBG(2, "Moved SN9C10X register index to 0x%02X", cam->sysfs.reg)
+ DBG(3, "Written bytes: %zd", count)
+
+ up(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t sn9c102_show_val(struct class_device* cd, char* buf)
+{
+ struct sn9c102_device* cam;
+ ssize_t count;
+ int val;
+
+ if (down_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ up(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ if ((val = sn9c102_read_reg(cam, cam->sysfs.reg)) < 0) {
+ up(&sn9c102_sysfs_lock);
+ return -EIO;
+ }
+
+ count = sprintf(buf, "%d\n", val);
+
+ DBG(3, "Read bytes: %zd", count)
+
+ up(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+sn9c102_store_val(struct class_device* cd, const char* buf, size_t len)
+{
+ struct sn9c102_device* cam;
+ u8 value;
+ ssize_t count;
+ int err;
+
+ if (down_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ up(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ value = sn9c102_strtou8(buf, len, &count);
+ if (!count) {
+ up(&sn9c102_sysfs_lock);
+ return -EINVAL;
+ }
+
+ err = sn9c102_write_reg(cam, value, cam->sysfs.reg);
+ if (err) {
+ up(&sn9c102_sysfs_lock);
+ return -EIO;
+ }
+
+ DBG(2, "Written SN9C10X reg. 0x%02X, val. 0x%02X",
+ cam->sysfs.reg, value)
+ DBG(3, "Written bytes: %zd", count)
+
+ up(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t sn9c102_show_i2c_reg(struct class_device* cd, char* buf)
+{
+ struct sn9c102_device* cam;
+ ssize_t count;
+
+ if (down_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ up(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg);
+
+ DBG(3, "Read bytes: %zd", count)
+
+ up(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+sn9c102_store_i2c_reg(struct class_device* cd, const char* buf, size_t len)
+{
+ struct sn9c102_device* cam;
+ u8 index;
+ ssize_t count;
+
+ if (down_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ up(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ index = sn9c102_strtou8(buf, len, &count);
+ if (!count) {
+ up(&sn9c102_sysfs_lock);
+ return -EINVAL;
+ }
+
+ cam->sysfs.i2c_reg = index;
+
+ DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg)
+ DBG(3, "Written bytes: %zd", count)
+
+ up(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf)
+{
+ struct sn9c102_device* cam;
+ ssize_t count;
+ int val;
+
+ if (down_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ up(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) {
+ up(&sn9c102_sysfs_lock);
+ return -EIO;
+ }
+
+ count = sprintf(buf, "%d\n", val);
+
+ DBG(3, "Read bytes: %zd", count)
+
+ up(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
+{
+ struct sn9c102_device* cam;
+ u8 value;
+ ssize_t count;
+ int err;
+
+ if (down_interruptible(&sn9c102_sysfs_lock))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(to_video_device(cd));
+ if (!cam) {
+ up(&sn9c102_sysfs_lock);
+ return -ENODEV;
+ }
+
+ value = sn9c102_strtou8(buf, len, &count);
+ if (!count) {
+ up(&sn9c102_sysfs_lock);
+ return -EINVAL;
+ }
+
+ err = sn9c102_i2c_write(cam, cam->sysfs.i2c_reg, value);
+ if (err) {
+ up(&sn9c102_sysfs_lock);
+ return -EIO;
+ }
+
+ DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X",
+ cam->sysfs.i2c_reg, value)
+ DBG(3, "Written bytes: %zd", count)
+
+ up(&sn9c102_sysfs_lock);
+
+ return count;
+}
+
+
+static ssize_t
+sn9c102_store_redblue(struct class_device* cd, const char* buf, size_t len)
+{
+ ssize_t res = 0;
+ u8 value;
+ ssize_t count;
+
+ value = sn9c102_strtou8(buf, len, &count);
+ if (!count)
+ return -EINVAL;
+
+ if ((res = sn9c102_store_reg(cd, "0x10", 4)) >= 0)
+ res = sn9c102_store_val(cd, buf, len);
+
+ return res;
+}
+
+
+static ssize_t
+sn9c102_store_green(struct class_device* cd, const char* buf, size_t len)
+{
+ ssize_t res = 0;
+ u8 value;
+ ssize_t count;
+
+ value = sn9c102_strtou8(buf, len, &count);
+ if (!count || value > 0x0f)
+ return -EINVAL;
+
+ if ((res = sn9c102_store_reg(cd, "0x11", 4)) >= 0)
+ res = sn9c102_store_val(cd, buf, len);
+
+ return res;
+}
+
+
+static CLASS_DEVICE_ATTR(reg, S_IRUGO | S_IWUSR,
+ sn9c102_show_reg, sn9c102_store_reg);
+static CLASS_DEVICE_ATTR(val, S_IRUGO | S_IWUSR,
+ sn9c102_show_val, sn9c102_store_val);
+static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR,
+ sn9c102_show_i2c_reg, sn9c102_store_i2c_reg);
+static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR,
+ sn9c102_show_i2c_val, sn9c102_store_i2c_val);
+static CLASS_DEVICE_ATTR(redblue, S_IWUGO, NULL, sn9c102_store_redblue);
+static CLASS_DEVICE_ATTR(green, S_IWUGO, NULL, sn9c102_store_green);
+
+
+static void sn9c102_create_sysfs(struct sn9c102_device* cam)
+{
+ struct video_device *v4ldev = cam->v4ldev;
+
+ video_device_create_file(v4ldev, &class_device_attr_reg);
+ video_device_create_file(v4ldev, &class_device_attr_val);
+ video_device_create_file(v4ldev, &class_device_attr_redblue);
+ video_device_create_file(v4ldev, &class_device_attr_green);
+ if (cam->sensor->slave_write_id && cam->sensor->slave_read_id) {
+ video_device_create_file(v4ldev, &class_device_attr_i2c_reg);
+ video_device_create_file(v4ldev, &class_device_attr_i2c_val);
+ }
+}
+
+/*****************************************************************************/
+
+static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale)
+{
+ u8 r = 0;
+ int err = 0;
+
+ if (scale == 1)
+ r = cam->reg[0x18] & 0xcf;
+ else if (scale == 2) {
+ r = cam->reg[0x18] & 0xcf;
+ r |= 0x10;
+ } else if (scale == 4)
+ r = cam->reg[0x18] | 0x20;
+
+ err += sn9c102_write_reg(cam, r, 0x18);
+ if (err)
+ return -EIO;
+
+ PDBGG("Scaling factor: %u", scale)
+
+ return 0;
+}
+
+
+static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = cam->sensor;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left),
+ v_start = (u8)(rect->top - s->cropcap.bounds.top),
+ h_size = (u8)(rect->width / 16),
+ v_size = (u8)(rect->height / 16),
+ ae_strx = 0x00,
+ ae_stry = 0x00,
+ ae_endx = h_size / 2,
+ ae_endy = v_size / 2;
+ int err = 0;
+
+ /* These are a sort of stroboscopic signal for some sensors */
+ err += sn9c102_write_reg(cam, h_size, 0x1a);
+ err += sn9c102_write_reg(cam, v_size, 0x1b);
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+ err += sn9c102_write_reg(cam, h_size, 0x15);
+ err += sn9c102_write_reg(cam, v_size, 0x16);
+ err += sn9c102_write_reg(cam, ae_strx, 0x1c);
+ err += sn9c102_write_reg(cam, ae_stry, 0x1d);
+ err += sn9c102_write_reg(cam, ae_endx, 0x1e);
+ err += sn9c102_write_reg(cam, ae_endy, 0x1f);
+ if (err)
+ return -EIO;
+
+ PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size "
+ "%u %u %u %u %u %u", h_start, v_start, h_size, v_size, ho_size,
+ vo_size)
+
+ return 0;
+}
+
+
+static int sn9c102_init(struct sn9c102_device* cam)
+{
+ struct sn9c102_sensor* s = cam->sensor;
+ struct v4l2_control ctrl;
+ struct v4l2_queryctrl *qctrl;
+ struct v4l2_rect* rect;
+ u8 i = 0, n = 0;
+ int err = 0;
+
+ if (!(cam->state & DEV_INITIALIZED)) {
+ init_waitqueue_head(&cam->open);
+ qctrl = s->qctrl;
+ rect = &(s->cropcap.defrect);
+ } else { /* use current values */
+ qctrl = s->_qctrl;
+ rect = &(s->_rect);
+ }
+
+ err += sn9c102_set_scale(cam, rect->width / s->pix_format.width);
+ err += sn9c102_set_crop(cam, rect);
+ if (err)
+ return err;
+
+ if (s->init) {
+ err = s->init(cam);
+ if (err) {
+ DBG(3, "Sensor initialization failed")
+ return err;
+ }
+ }
+
+ if (s->set_crop)
+ if ((err = s->set_crop(cam, rect))) {
+ DBG(3, "set_crop() failed")
+ return err;
+ }
+
+ if (s->set_ctrl) {
+ n = sizeof(s->qctrl) / sizeof(s->qctrl[0]);
+ for (i = 0; i < n; i++)
+ if (s->qctrl[i].id != 0 &&
+ !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) {
+ ctrl.id = s->qctrl[i].id;
+ ctrl.value = qctrl[i].default_value;
+ err = s->set_ctrl(cam, &ctrl);
+ if (err) {
+ DBG(3, "Set control failed")
+ return err;
+ }
+ }
+ }
+
+ if (!(cam->state & DEV_INITIALIZED)) {
+ init_MUTEX(&cam->fileop_sem);
+ spin_lock_init(&cam->queue_lock);
+ init_waitqueue_head(&cam->wait_frame);
+ init_waitqueue_head(&cam->wait_stream);
+ memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl));
+ memcpy(&(s->_rect), &(s->cropcap.defrect),
+ sizeof(struct v4l2_rect));
+ cam->state |= DEV_INITIALIZED;
+ }
+
+ DBG(2, "Initialization succeeded")
+ return 0;
+}
+
+
+static void sn9c102_release_resources(struct sn9c102_device* cam)
+{
+ down(&sn9c102_sysfs_lock);
+
+ DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor)
+ video_set_drvdata(cam->v4ldev, NULL);
+ video_unregister_device(cam->v4ldev);
+
+ up(&sn9c102_sysfs_lock);
+
+ kfree(cam->control_buffer);
+}
+
+/*****************************************************************************/
+
+static int sn9c102_open(struct inode* inode, struct file* filp)
+{
+ struct sn9c102_device* cam;
+ int err = 0;
+
+ /* This the only safe way to prevent race conditions with disconnect */
+ if (!down_read_trylock(&sn9c102_disconnect))
+ return -ERESTARTSYS;
+
+ cam = video_get_drvdata(video_devdata(filp));
+
+ if (down_interruptible(&cam->dev_sem)) {
+ up_read(&sn9c102_disconnect);
+ return -ERESTARTSYS;
+ }
+
+ if (cam->users) {
+ DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor)
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (filp->f_flags & O_NDELAY)) {
+ err = -EWOULDBLOCK;
+ goto out;
+ }
+ up(&cam->dev_sem);
+ err = wait_event_interruptible_exclusive(cam->open,
+ cam->state & DEV_DISCONNECTED
+ || !cam->users);
+ if (err) {
+ up_read(&sn9c102_disconnect);
+ return err;
+ }
+ if (cam->state & DEV_DISCONNECTED) {
+ up_read(&sn9c102_disconnect);
+ return -ENODEV;
+ }
+ down(&cam->dev_sem);
+ }
+
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ err = sn9c102_init(cam);
+ if (err) {
+ DBG(1, "Initialization failed again. "
+ "I will retry on next open().")
+ goto out;
+ }
+ cam->state &= ~DEV_MISCONFIGURED;
+ }
+
+ if ((err = sn9c102_start_transfer(cam)))
+ goto out;
+
+ filp->private_data = cam;
+ cam->users++;
+ cam->io = IO_NONE;
+ cam->stream = STREAM_OFF;
+ cam->nbuffers = 0;
+ cam->frame_count = 0;
+ sn9c102_empty_framequeues(cam);
+
+ DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor)
+
+out:
+ up(&cam->dev_sem);
+ up_read(&sn9c102_disconnect);
+ return err;
+}
+
+
+static int sn9c102_release(struct inode* inode, struct file* filp)
+{
+ struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+
+ down(&cam->dev_sem); /* prevent disconnect() to be called */
+
+ sn9c102_stop_transfer(cam);
+
+ sn9c102_release_buffers(cam);
+
+ if (cam->state & DEV_DISCONNECTED) {
+ sn9c102_release_resources(cam);
+ up(&cam->dev_sem);
+ kfree(cam);
+ return 0;
+ }
+
+ cam->users--;
+ wake_up_interruptible_nr(&cam->open, 1);
+
+ DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor)
+
+ up(&cam->dev_sem);
+
+ return 0;
+}
+
+
+static ssize_t
+sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
+{
+ struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+ struct sn9c102_frame_t* f, * i;
+ unsigned long lock_flags;
+ int err = 0;
+
+ if (down_interruptible(&cam->fileop_sem))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present")
+ up(&cam->fileop_sem);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it again.")
+ up(&cam->fileop_sem);
+ return -EIO;
+ }
+
+ if (cam->io == IO_MMAP) {
+ DBG(3, "Close and open the device again to choose "
+ "the read method")
+ up(&cam->fileop_sem);
+ return -EINVAL;
+ }
+
+ if (cam->io == IO_NONE) {
+ if (!sn9c102_request_buffers(cam, 2)) {
+ DBG(1, "read() failed, not enough memory")
+ up(&cam->fileop_sem);
+ return -ENOMEM;
+ }
+ cam->io = IO_READ;
+ cam->stream = STREAM_ON;
+ sn9c102_queue_unusedframes(cam);
+ }
+
+ if (!count) {
+ up(&cam->fileop_sem);
+ return 0;
+ }
+
+ if (list_empty(&cam->outqueue)) {
+ if (filp->f_flags & O_NONBLOCK) {
+ up(&cam->fileop_sem);
+ return -EAGAIN;
+ }
+ err = wait_event_interruptible
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) );
+ if (err) {
+ up(&cam->fileop_sem);
+ return err;
+ }
+ if (cam->state & DEV_DISCONNECTED) {
+ up(&cam->fileop_sem);
+ return -ENODEV;
+ }
+ }
+
+ f = list_entry(cam->outqueue.prev, struct sn9c102_frame_t, frame);
+
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_for_each_entry(i, &cam->outqueue, frame)
+ i->state = F_UNUSED;
+ INIT_LIST_HEAD(&cam->outqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ sn9c102_queue_unusedframes(cam);
+
+ if (count > f->buf.length)
+ count = f->buf.length;
+
+ if (copy_to_user(buf, f->bufmem, count)) {
+ up(&cam->fileop_sem);
+ return -EFAULT;
+ }
+ *f_pos += count;
+
+ PDBGG("Frame #%lu, bytes read: %zu", (unsigned long)f->buf.index,count)
+
+ up(&cam->fileop_sem);
+
+ return count;
+}
+
+
+static unsigned int sn9c102_poll(struct file *filp, poll_table *wait)
+{
+ struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+ unsigned int mask = 0;
+
+ if (down_interruptible(&cam->fileop_sem))
+ return POLLERR;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present")
+ goto error;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it again.")
+ goto error;
+ }
+
+ if (cam->io == IO_NONE) {
+ if (!sn9c102_request_buffers(cam, 2)) {
+ DBG(1, "poll() failed, not enough memory")
+ goto error;
+ }
+ cam->io = IO_READ;
+ cam->stream = STREAM_ON;
+ }
+
+ if (cam->io == IO_READ)
+ sn9c102_queue_unusedframes(cam);
+
+ poll_wait(filp, &cam->wait_frame, wait);
+
+ if (!list_empty(&cam->outqueue))
+ mask |= POLLIN | POLLRDNORM;
+
+ up(&cam->fileop_sem);
+
+ return mask;
+
+error:
+ up(&cam->fileop_sem);
+ return POLLERR;
+}
+
+
+static void sn9c102_vm_open(struct vm_area_struct* vma)
+{
+ struct sn9c102_frame_t* f = vma->vm_private_data;
+ f->vma_use_count++;
+}
+
+
+static void sn9c102_vm_close(struct vm_area_struct* vma)
+{
+ /* NOTE: buffers are not freed here */
+ struct sn9c102_frame_t* f = vma->vm_private_data;
+ f->vma_use_count--;
+}
+
+
+static struct vm_operations_struct sn9c102_vm_ops = {
+ .open = sn9c102_vm_open,
+ .close = sn9c102_vm_close,
+};
+
+
+static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
+{
+ struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+ unsigned long size = vma->vm_end - vma->vm_start,
+ start = vma->vm_start,
+ pos,
+ page;
+ u32 i;
+
+ if (down_interruptible(&cam->fileop_sem))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present")
+ up(&cam->fileop_sem);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it again.")
+ up(&cam->fileop_sem);
+ return -EIO;
+ }
+
+ if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) ||
+ size != PAGE_ALIGN(cam->frame[0].buf.length)) {
+ up(&cam->fileop_sem);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++) {
+ if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff)
+ break;
+ }
+ if (i == cam->nbuffers) {
+ up(&cam->fileop_sem);
+ return -EINVAL;
+ }
+
+ pos = (unsigned long)cam->frame[i].bufmem;
+ while (size > 0) { /* size is page-aligned */
+ page = kvirt_to_pa(pos);
+ if (remap_page_range(vma, start, page, PAGE_SIZE,
+ vma->vm_page_prot)) {
+ up(&cam->fileop_sem);
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ vma->vm_ops = &sn9c102_vm_ops;
+ vma->vm_flags &= ~VM_IO; /* not I/O memory */
+ vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
+ vma->vm_private_data = &cam->frame[i];
+
+ sn9c102_vm_open(vma);
+
+ up(&cam->fileop_sem);
+
+ return 0;
+}
+
+
+static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
+ unsigned int cmd, void __user * arg)
+{
+ struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+
+ switch (cmd) {
+
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability cap = {
+ .driver = "sn9c102",
+ .version = SN9C102_MODULE_VERSION_CODE,
+ .capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING,
+ };
+
+ strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
+ strlcpy(cap.bus_info, cam->dev.bus_id, sizeof(cap.bus_info));
+
+ if (copy_to_user(arg, &cap, sizeof(cap)))
+ return -EFAULT;
+
+ return 0;
+ }
+
+ case VIDIOC_ENUMINPUT:
+ {
+ struct v4l2_input i;
+
+ if (copy_from_user(&i, arg, sizeof(i)))
+ return -EFAULT;
+
+ if (i.index)
+ return -EINVAL;
+
+ memset(&i, 0, sizeof(i));
+ strcpy(i.name, "USB");
+
+ if (copy_to_user(arg, &i, sizeof(i)))
+ return -EFAULT;
+
+ return 0;
+ }
+
+ case VIDIOC_G_INPUT:
+ case VIDIOC_S_INPUT:
+ {
+ int index;
+
+ if (copy_from_user(&index, arg, sizeof(index)))
+ return -EFAULT;
+
+ if (index != 0)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ case VIDIOC_QUERYCTRL:
+ {
+ struct sn9c102_sensor* s = cam->sensor;
+ struct v4l2_queryctrl qc;
+ u8 i, n;
+
+ if (copy_from_user(&qc, arg, sizeof(qc)))
+ return -EFAULT;
+
+ n = sizeof(s->qctrl) / sizeof(s->qctrl[0]);
+ for (i = 0; i < n; i++)
+ if (qc.id && qc.id == s->qctrl[i].id) {
+ memcpy(&qc, &(s->qctrl[i]), sizeof(qc));
+ if (copy_to_user(arg, &qc, sizeof(qc)))
+ return -EFAULT;
+ return 0;
+ }
+
+ return -EINVAL;
+ }
+
+ case VIDIOC_G_CTRL:
+ {
+ struct sn9c102_sensor* s = cam->sensor;
+ struct v4l2_control ctrl;
+ int err = 0;
+
+ if (!s->get_ctrl)
+ return -EINVAL;
+
+ if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+ return -EFAULT;
+
+ err = s->get_ctrl(cam, &ctrl);
+
+ if (copy_to_user(arg, &ctrl, sizeof(ctrl)))
+ return -EFAULT;
+
+ return err;
+ }
+
+ case VIDIOC_S_CTRL:
+ {
+ struct sn9c102_sensor* s = cam->sensor;
+ struct v4l2_control ctrl;
+ u8 i, n;
+ int err = 0;
+
+ if (!s->set_ctrl)
+ return -EINVAL;
+
+ if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
+ return -EFAULT;
+
+ if ((err = s->set_ctrl(cam, &ctrl)))
+ return err;
+
+ n = sizeof(s->qctrl) / sizeof(s->qctrl[0]);
+ for (i = 0; i < n; i++)
+ if (ctrl.id == s->qctrl[i].id) {
+ s->_qctrl[i].default_value = ctrl.value;
+ break;
+ }
+
+ return 0;
+ }
+
+ case VIDIOC_CROPCAP:
+ {
+ struct v4l2_cropcap* cc = &(cam->sensor->cropcap);
+
+ cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ cc->pixelaspect.numerator = 1;
+ cc->pixelaspect.denominator = 1;
+
+ if (copy_to_user(arg, cc, sizeof(*cc)))
+ return -EFAULT;
+
+ return 0;
+ }
+
+ case VIDIOC_G_CROP:
+ {
+ struct sn9c102_sensor* s = cam->sensor;
+ struct v4l2_crop crop = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ };
+
+ memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect));
+
+ if (copy_to_user(arg, &crop, sizeof(crop)))
+ return -EFAULT;
+
+ return 0;
+ }
+
+ case VIDIOC_S_CROP:
+ {
+ struct sn9c102_sensor* s = cam->sensor;
+ struct v4l2_crop crop;
+ struct v4l2_rect* rect;
+ struct v4l2_rect* bounds = &(s->cropcap.bounds);
+ struct v4l2_pix_format* pix_format = &(s->pix_format);
+ u8 scale;
+ const enum sn9c102_stream_state stream = cam->stream;
+ const u32 nbuffers = cam->nbuffers;
+ u32 i;
+ int err = 0;
+
+ if (copy_from_user(&crop, arg, sizeof(crop)))
+ return -EFAULT;
+
+ rect = &(crop.c);
+
+ if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_S_CROP failed. "
+ "Unmap the buffers first.")
+ return -EINVAL;
+ }
+
+ if (rect->width < 16)
+ rect->width = 16;
+ if (rect->height < 16)
+ rect->height = 16;
+ if (rect->width > bounds->width)
+ rect->width = bounds->width;
+ if (rect->height > bounds->height)
+ rect->height = bounds->height;
+ if (rect->left < bounds->left)
+ rect->left = bounds->left;
+ if (rect->top < bounds->top)
+ rect->top = bounds->top;
+ if (rect->left + rect->width > bounds->left + bounds->width)
+ rect->left = bounds->left+bounds->width - rect->width;
+ if (rect->top + rect->height > bounds->top + bounds->height)
+ rect->top = bounds->top+bounds->height - rect->height;
+
+ rect->width &= ~15L;
+ rect->height &= ~15L;
+
+ { /* calculate the scaling factor */
+ u32 a, b;
+ a = rect->width * rect->height;
+ b = pix_format->width * pix_format->height;
+ scale = b ? (u8)((a / b) <= 1 ? 1 : ((a / b) == 3 ? 2 :
+ ((a / b) > 4 ? 4 : (a / b)))) : 1;
+ }
+
+ if (cam->stream == STREAM_ON) {
+ cam->stream = STREAM_INTERRUPT;
+ err = wait_event_interruptible
+ ( cam->wait_stream,
+ (cam->stream == STREAM_OFF) ||
+ (cam->state & DEV_DISCONNECTED) );
+ if (err) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "The camera is misconfigured. To use "
+ "it, close and open /dev/video%d "
+ "again.", cam->v4ldev->minor)
+ return err;
+ }
+ if (cam->state & DEV_DISCONNECTED)
+ return -ENODEV;
+ }
+
+ if (copy_to_user(arg, &crop, sizeof(crop))) {
+ cam->stream = stream;
+ return -EFAULT;
+ }
+
+ sn9c102_release_buffers(cam);
+
+ err = sn9c102_set_crop(cam, rect);
+ if (s->set_crop)
+ err += s->set_crop(cam, rect);
+ err += sn9c102_set_scale(cam, scale);
+
+ if (err) { /* atomic, no rollback in ioctl() */
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_CROP failed because of hardware "
+ "problems. To use the camera, close and open "
+ "/dev/video%d again.", cam->v4ldev->minor)
+ return err;
+ }
+
+ s->pix_format.width = rect->width/scale;
+ s->pix_format.height = rect->height/scale;
+ memcpy(&(s->_rect), rect, sizeof(*rect));
+
+ if (nbuffers != sn9c102_request_buffers(cam, nbuffers)) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_CROP failed because of not enough "
+ "memory. To use the camera, close and open "
+ "/dev/video%d again.", cam->v4ldev->minor)
+ return -ENOMEM;
+ }
+
+ cam->stream = stream;
+
+ return 0;
+ }
+
+ case VIDIOC_ENUM_FMT:
+ {
+ struct sn9c102_sensor* s = cam->sensor;
+ struct v4l2_fmtdesc fmtd;
+
+ if (copy_from_user(&fmtd, arg, sizeof(fmtd)))
+ return -EFAULT;
+
+ if (fmtd.index != 0)
+ return -EINVAL;
+
+ memset(&fmtd, 0, sizeof(fmtd));
+
+ fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ strcpy(fmtd.description, "bayer rgb");
+ fmtd.pixelformat = s->pix_format.pixelformat;
+
+ if (copy_to_user(arg, &fmtd, sizeof(fmtd)))
+ return -EFAULT;
+
+ return 0;
+ }
+
+ case VIDIOC_G_FMT:
+ {
+ struct v4l2_format format;
+ struct v4l2_pix_format* pfmt = &(cam->sensor->pix_format);
+
+ if (copy_from_user(&format, arg, sizeof(format)))
+ return -EFAULT;
+
+ if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ pfmt->bytesperline = (pfmt->width * pfmt->priv) / 8;
+ pfmt->sizeimage = pfmt->height * pfmt->bytesperline;
+ pfmt->field = V4L2_FIELD_NONE;
+ memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt));
+
+ if (copy_to_user(arg, &format, sizeof(format)))
+ return -EFAULT;
+
+ return 0;
+ }
+
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_S_FMT:
+ {
+ struct sn9c102_sensor* s = cam->sensor;
+ struct v4l2_format format;
+ struct v4l2_pix_format* pix;
+ struct v4l2_pix_format* pfmt = &(s->pix_format);
+ struct v4l2_rect* bounds = &(s->cropcap.bounds);
+ struct v4l2_rect rect;
+ u8 scale;
+ const enum sn9c102_stream_state stream = cam->stream;
+ const u32 nbuffers = cam->nbuffers;
+ u32 i;
+ int err = 0;
+
+ if (copy_from_user(&format, arg, sizeof(format)))
+ return -EFAULT;
+
+ pix = &(format.fmt.pix);
+
+ if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memcpy(&rect, &(s->_rect), sizeof(rect));
+
+ { /* calculate the scaling factor */
+ u32 a, b;
+ a = rect.width * rect.height;
+ b = pix->width * pix->height;
+ scale = b ? (u8)((a / b) <= 1 ? 1 : ((a / b) == 3 ? 2 :
+ ((a / b) > 4 ? 4 : (a / b)))) : 1;
+ }
+
+ rect.width = scale * pix->width;
+ rect.height = scale * pix->height;
+
+ if (rect.width < 16)
+ rect.width = 16;
+ if (rect.height < 16)
+ rect.height = 16;
+ if (rect.width > bounds->left + bounds->width - rect.left)
+ rect.width = bounds->left+bounds->width - rect.left;
+ if (rect.height > bounds->top + bounds->height - rect.top)
+ rect.height = bounds->top + bounds->height - rect.top;
+
+ rect.width &= ~15L;
+ rect.height &= ~15L;
+
+ pix->width = rect.width / scale;
+ pix->height = rect.height / scale;
+
+ pix->pixelformat = pfmt->pixelformat;
+ pix->priv = pfmt->priv; /* bpp */
+ pix->colorspace = pfmt->colorspace;
+ pix->bytesperline = (pix->width * pix->priv) / 8;
+ pix->sizeimage = pix->height * pix->bytesperline;
+ pix->field = V4L2_FIELD_NONE;
+
+ if (cmd == VIDIOC_TRY_FMT)
+ return 0;
+
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_S_FMT failed. "
+ "Unmap the buffers first.")
+ return -EINVAL;
+ }
+
+ if (cam->stream == STREAM_ON) {
+ cam->stream = STREAM_INTERRUPT;
+ err = wait_event_interruptible
+ ( cam->wait_stream,
+ (cam->stream == STREAM_OFF) ||
+ (cam->state & DEV_DISCONNECTED) );
+ if (err) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "The camera is misconfigured. To use "
+ "it, close and open /dev/video%d "
+ "again.", cam->v4ldev->minor)
+ return err;
+ }
+ if (cam->state & DEV_DISCONNECTED)
+ return -ENODEV;
+ }
+
+ if (copy_to_user(arg, &format, sizeof(format))) {
+ cam->stream = stream;
+ return -EFAULT;
+ }
+
+ sn9c102_release_buffers(cam);
+
+ err = sn9c102_set_crop(cam, &rect);
+ if (s->set_crop)
+ err += s->set_crop(cam, &rect);
+ err += sn9c102_set_scale(cam, scale);
+
+ if (err) { /* atomic, no rollback in ioctl() */
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_FMT failed because of hardware "
+ "problems. To use the camera, close and open "
+ "/dev/video%d again.", cam->v4ldev->minor)
+ return err;
+ }
+
+ memcpy(pfmt, pix, sizeof(*pix));
+ memcpy(&(s->_rect), &rect, sizeof(rect));
+
+ if (nbuffers != sn9c102_request_buffers(cam, nbuffers)) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "VIDIOC_S_FMT failed because of not enough "
+ "memory. To use the camera, close and open "
+ "/dev/video%d again.", cam->v4ldev->minor)
+ return -ENOMEM;
+ }
+
+ cam->stream = stream;
+
+ return 0;
+ }
+
+ case VIDIOC_REQBUFS:
+ {
+ struct v4l2_requestbuffers rb;
+ u32 i;
+ int err;
+
+ if (copy_from_user(&rb, arg, sizeof(rb)))
+ return -EFAULT;
+
+ if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ rb.memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ if (cam->io == IO_READ) {
+ DBG(3, "Close and open the device again to choose "
+ "the mmap I/O method")
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cam->nbuffers; i++)
+ if (cam->frame[i].vma_use_count) {
+ DBG(3, "VIDIOC_REQBUFS failed. "
+ "Previous buffers are still mapped.")
+ return -EINVAL;
+ }
+
+ if (cam->stream == STREAM_ON) {
+ cam->stream = STREAM_INTERRUPT;
+ err = wait_event_interruptible
+ ( cam->wait_stream,
+ (cam->stream == STREAM_OFF) ||
+ (cam->state & DEV_DISCONNECTED) );
+ if (err) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "The camera is misconfigured. To use "
+ "it, close and open /dev/video%d "
+ "again.", cam->v4ldev->minor)
+ return err;
+ }
+ if (cam->state & DEV_DISCONNECTED)
+ return -ENODEV;
+ }
+
+ sn9c102_empty_framequeues(cam);
+
+ sn9c102_release_buffers(cam);
+ if (rb.count)
+ rb.count = sn9c102_request_buffers(cam, rb.count);
+
+ if (copy_to_user(arg, &rb, sizeof(rb))) {
+ sn9c102_release_buffers(cam);
+ cam->io = IO_NONE;
+ return -EFAULT;
+ }
+
+ cam->io = rb.count ? IO_MMAP : IO_NONE;
+
+ return 0;
+ }
+
+ case VIDIOC_QUERYBUF:
+ {
+ struct v4l2_buffer b;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ b.index >= cam->nbuffers || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ memcpy(&b, &cam->frame[b.index].buf, sizeof(b));
+
+ if (cam->frame[b.index].vma_use_count)
+ b.flags |= V4L2_BUF_FLAG_MAPPED;
+
+ if (cam->frame[b.index].state == F_DONE)
+ b.flags |= V4L2_BUF_FLAG_DONE;
+ else if (cam->frame[b.index].state != F_UNUSED)
+ b.flags |= V4L2_BUF_FLAG_QUEUED;
+
+ if (copy_to_user(arg, &b, sizeof(b)))
+ return -EFAULT;
+
+ return 0;
+ }
+
+ case VIDIOC_QBUF:
+ {
+ struct v4l2_buffer b;
+ unsigned long lock_flags;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ b.index >= cam->nbuffers || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (cam->frame[b.index].state != F_UNUSED)
+ return -EINVAL;
+
+ cam->frame[b.index].state = F_QUEUED;
+
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ list_add_tail(&cam->frame[b.index].frame, &cam->inqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ PDBGG("Frame #%lu queued", (unsigned long)b.index)
+
+ return 0;
+ }
+
+ case VIDIOC_DQBUF:
+ {
+ struct v4l2_buffer b;
+ struct sn9c102_frame_t *f;
+ unsigned long lock_flags;
+ int err = 0;
+
+ if (copy_from_user(&b, arg, sizeof(b)))
+ return -EFAULT;
+
+ if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP)
+ return -EINVAL;
+
+ if (list_empty(&cam->outqueue)) {
+ if (cam->stream == STREAM_OFF)
+ return -EINVAL;
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ err = wait_event_interruptible
+ ( cam->wait_frame,
+ (!list_empty(&cam->outqueue)) ||
+ (cam->state & DEV_DISCONNECTED) );
+ if (err)
+ return err;
+ if (cam->state & DEV_DISCONNECTED)
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&cam->queue_lock, lock_flags);
+ f = list_entry(cam->outqueue.next, struct sn9c102_frame_t,
+ frame);
+ list_del(&cam->outqueue);
+ spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
+
+ f->state = F_UNUSED;
+
+ memcpy(&b, &f->buf, sizeof(b));
+ if (f->vma_use_count)
+ b.flags |= V4L2_BUF_FLAG_MAPPED;
+
+ if (copy_to_user(arg, &b, sizeof(b)))
+ return -EFAULT;
+
+ PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index)
+
+ return 0;
+ }
+
+ case VIDIOC_STREAMON:
+ {
+ int type;
+
+ if (copy_from_user(&type, arg, sizeof(type)))
+ return -EFAULT;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (list_empty(&cam->inqueue))
+ return -EINVAL;
+
+ cam->stream = STREAM_ON;
+
+ DBG(3, "Stream on")
+
+ return 0;
+ }
+
+ case VIDIOC_STREAMOFF:
+ {
+ int type, err;
+
+ if (copy_from_user(&type, arg, sizeof(type)))
+ return -EFAULT;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
+ return -EINVAL;
+
+ if (cam->stream == STREAM_ON) {
+ cam->stream = STREAM_INTERRUPT;
+ err = wait_event_interruptible
+ ( cam->wait_stream,
+ (cam->stream == STREAM_OFF) ||
+ (cam->state & DEV_DISCONNECTED) );
+ if (err) {
+ cam->state |= DEV_MISCONFIGURED;
+ DBG(1, "The camera is misconfigured. To use "
+ "it, close and open /dev/video%d "
+ "again.", cam->v4ldev->minor)
+ return err;
+ }
+ if (cam->state & DEV_DISCONNECTED)
+ return -ENODEV;
+ }
+
+ sn9c102_empty_framequeues(cam);
+
+ DBG(3, "Stream off")
+
+ return 0;
+ }
+
+ case VIDIOC_G_STD:
+ case VIDIOC_S_STD:
+ case VIDIOC_QUERYSTD:
+ case VIDIOC_ENUMSTD:
+ case VIDIOC_QUERYMENU:
+ case VIDIOC_G_PARM:
+ case VIDIOC_S_PARM:
+ return -EINVAL;
+
+ default:
+ return -EINVAL;
+
+ }
+}
+
+
+static int sn9c102_ioctl(struct inode* inode, struct file* filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+ int err = 0;
+
+ if (down_interruptible(&cam->fileop_sem))
+ return -ERESTARTSYS;
+
+ if (cam->state & DEV_DISCONNECTED) {
+ DBG(1, "Device not present")
+ up(&cam->fileop_sem);
+ return -ENODEV;
+ }
+
+ if (cam->state & DEV_MISCONFIGURED) {
+ DBG(1, "The camera is misconfigured. Close and open it again.")
+ up(&cam->fileop_sem);
+ return -EIO;
+ }
+
+ err = sn9c102_v4l2_ioctl(inode, filp, cmd, (void __user *)arg);
+
+ up(&cam->fileop_sem);
+
+ return err;
+}
+
+
+static struct file_operations sn9c102_fops = {
+ .owner = THIS_MODULE,
+ .open = sn9c102_open,
+ .release = sn9c102_release,
+ .ioctl = sn9c102_ioctl,
+ .read = sn9c102_read,
+ .poll = sn9c102_poll,
+ .mmap = sn9c102_mmap,
+ .llseek = no_llseek,
+};
+
+/*****************************************************************************/
+
+/* It exists a single interface only. We do not need to validate anything. */
+static int
+sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct sn9c102_device* cam;
+ static unsigned int dev_nr = 0;
+ unsigned int i, n;
+ int err = 0, r;
+
+ n = sizeof(sn9c102_id_table)/sizeof(sn9c102_id_table[0]);
+ for (i = 0; i < n-1; i++)
+ if (udev->descriptor.idVendor==sn9c102_id_table[i].idVendor &&
+ udev->descriptor.idProduct==sn9c102_id_table[i].idProduct)
+ break;
+ if (i == n-1)
+ return -ENODEV;
+
+ if (!(cam = kmalloc(sizeof(struct sn9c102_device), GFP_KERNEL)))
+ return -ENOMEM;
+ memset(cam, 0, sizeof(*cam));
+
+ cam->usbdev = udev;
+
+ memcpy(&cam->dev, &udev->dev, sizeof(struct device));
+
+ if (!(cam->control_buffer = kmalloc(8, GFP_KERNEL))) {
+ DBG(1, "kmalloc() failed")
+ err = -ENOMEM;
+ goto fail;
+ }
+ memset(cam->control_buffer, 0, 8);
+
+ if (!(cam->v4ldev = video_device_alloc())) {
+ DBG(1, "video_device_alloc() failed")
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ init_MUTEX(&cam->dev_sem);
+
+ r = sn9c102_read_reg(cam, 0x00);
+ if (r < 0 || r != 0x10) {
+ DBG(1, "Sorry, this is not a SN9C10[12] based camera "
+ "(vid/pid 0x%04X/0x%04X)",
+ sn9c102_id_table[i].idVendor,sn9c102_id_table[i].idProduct)
+ err = -ENODEV;
+ goto fail;
+ }
+
+ DBG(2, "SN9C10[12] PC Camera Controller detected "
+ "(vid/pid 0x%04X/0x%04X)",
+ sn9c102_id_table[i].idVendor, sn9c102_id_table[i].idProduct)
+
+ for (i = 0; sn9c102_sensor_table[i]; i++) {
+ err = sn9c102_sensor_table[i](cam);
+ if (!err)
+ break;
+ }
+
+ if (!err && cam->sensor) {
+ DBG(2, "%s image sensor detected", cam->sensor->name)
+ DBG(3, "Support for %s maintained by %s",
+ cam->sensor->name, cam->sensor->maintainer)
+ } else {
+ DBG(1, "No supported image sensor detected")
+ err = -ENODEV;
+ goto fail;
+ }
+
+ if (sn9c102_init(cam)) {
+ DBG(1, "Initialization failed. I will retry on open().")
+ cam->state |= DEV_MISCONFIGURED;
+ }
+
+ strcpy(cam->v4ldev->name, "SN9C10[12] PC Camera");
+ cam->v4ldev->owner = THIS_MODULE;
+ cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
+ cam->v4ldev->hardware = VID_HARDWARE_SN9C102;
+ cam->v4ldev->fops = &sn9c102_fops;
+ cam->v4ldev->minor = video_nr[dev_nr];
+ cam->v4ldev->release = video_device_release;
+ video_set_drvdata(cam->v4ldev, cam);
+
+ down(&cam->dev_sem);
+
+ err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
+ video_nr[dev_nr]);
+ if (err) {
+ DBG(1, "V4L2 device registration failed")
+ if (err == -ENFILE && video_nr[dev_nr] == -1)
+ DBG(1, "Free /dev/videoX node not found")
+ video_nr[dev_nr] = -1;
+ dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0;
+ up(&cam->dev_sem);
+ goto fail;
+ }
+
+ DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor)
+
+ sn9c102_create_sysfs(cam);
+
+ usb_set_intfdata(intf, cam);
+
+ up(&cam->dev_sem);
+
+ return 0;
+
+fail:
+ if (cam) {
+ kfree(cam->control_buffer);
+ if (cam->v4ldev)
+ video_device_release(cam->v4ldev);
+ kfree(cam);
+ }
+ return err;
+}
+
+
+static void sn9c102_usb_disconnect(struct usb_interface* intf)
+{
+ struct sn9c102_device* cam = usb_get_intfdata(intf);
+
+ if (!cam)
+ return;
+
+ down_write(&sn9c102_disconnect);
+
+ down(&cam->dev_sem);
+
+ DBG(2, "Disconnecting %s...", cam->v4ldev->name)
+
+ wake_up_interruptible_all(&cam->open);
+
+ if (cam->users) {
+ DBG(2, "Device /dev/video%d is open! Deregistration and "
+ "memory deallocation are deferred on close.",
+ cam->v4ldev->minor)
+ cam->state |= DEV_MISCONFIGURED;
+ sn9c102_stop_transfer(cam);
+ cam->state |= DEV_DISCONNECTED;
+ wake_up_interruptible(&cam->wait_frame);
+ wake_up_interruptible(&cam->wait_stream);
+ } else {
+ cam->state |= DEV_DISCONNECTED;
+ sn9c102_release_resources(cam);
+ }
+
+ up(&cam->dev_sem);
+
+ if (!cam->users)
+ kfree(cam);
+
+ up_write(&sn9c102_disconnect);
+}
+
+
+static struct usb_driver sn9c102_usb_driver = {
+ .owner = THIS_MODULE,
+ .name = "sn9c102",
+ .id_table = sn9c102_id_table,
+ .probe = sn9c102_usb_probe,
+ .disconnect = sn9c102_usb_disconnect,
+};
+
+/*****************************************************************************/
+
+static int __init sn9c102_module_init(void)
+{
+ int err = 0;
+
+ KDBG(2, SN9C102_MODULE_NAME " v" SN9C102_MODULE_VERSION)
+ KDBG(3, SN9C102_MODULE_AUTHOR)
+
+ if ((err = usb_register(&sn9c102_usb_driver)))
+ KDBG(1, "usb_register() failed")
+
+ return err;
+}
+
+
+static void __exit sn9c102_module_exit(void)
+{
+ usb_deregister(&sn9c102_usb_driver);
+}
+
+
+module_init(sn9c102_module_init);
+module_exit(sn9c102_module_exit);
--- /dev/null
+/***************************************************************************
+ * Driver for PAS106B image sensor connected to the SN9C10[12] PC Camera *
+ * Controllers *
+ * *
+ * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include <linux/delay.h>
+#include "sn9c102_sensor.h"
+
+
+static struct sn9c102_sensor pas106b;
+
+
+static int pas106b_init(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ err += sn9c102_write_reg(cam, 0x00, 0x10);
+ err += sn9c102_write_reg(cam, 0x00, 0x11);
+ err += sn9c102_write_reg(cam, 0x00, 0x14);
+ err += sn9c102_write_reg(cam, 0x20, 0x17);
+ err += sn9c102_write_reg(cam, 0x20, 0x19);
+ err += sn9c102_write_reg(cam, 0x09, 0x18);
+
+ err += sn9c102_i2c_write(cam, 0x02, 0x0c);
+ err += sn9c102_i2c_write(cam, 0x03, 0x12);
+ err += sn9c102_i2c_write(cam, 0x04, 0x05);
+ err += sn9c102_i2c_write(cam, 0x05, 0x22);
+ err += sn9c102_i2c_write(cam, 0x06, 0xac);
+ err += sn9c102_i2c_write(cam, 0x07, 0x00);
+ err += sn9c102_i2c_write(cam, 0x08, 0x01);
+ err += sn9c102_i2c_write(cam, 0x0a, 0x00);
+ err += sn9c102_i2c_write(cam, 0x0b, 0x00);
+ err += sn9c102_i2c_write(cam, 0x0d, 0x00);
+ err += sn9c102_i2c_write(cam, 0x10, 0x06);
+ err += sn9c102_i2c_write(cam, 0x11, 0x06);
+ err += sn9c102_i2c_write(cam, 0x12, 0x00);
+ err += sn9c102_i2c_write(cam, 0x14, 0x02);
+ err += sn9c102_i2c_write(cam, 0x13, 0x01);
+
+ msleep(400);
+
+ return err;
+}
+
+
+static int pas106b_get_ctrl(struct sn9c102_device* cam,
+ struct v4l2_control* ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_RED_BALANCE:
+ return (ctrl->value = sn9c102_i2c_read(cam, 0x0c))<0 ? -EIO:0;
+ case V4L2_CID_BLUE_BALANCE:
+ return (ctrl->value = sn9c102_i2c_read(cam, 0x09))<0 ? -EIO:0;
+ case V4L2_CID_BRIGHTNESS:
+ return (ctrl->value = sn9c102_i2c_read(cam, 0x0e))<0 ? -EIO:0;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+static int pas106b_set_ctrl(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl)
+{
+ int err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_RED_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x0c, ctrl->value & 0x1f);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ err += sn9c102_i2c_write(cam, 0x09, ctrl->value & 0x1f);
+ break;
+ case V4L2_CID_BRIGHTNESS:
+ err += sn9c102_i2c_write(cam, 0x0e, ctrl->value & 0x1f);
+ break;
+ default:
+ return -EINVAL;
+ }
+ err += sn9c102_i2c_write(cam, 0x13, 0x01);
+
+ return err;
+}
+
+
+static int pas106b_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = &pas106b;
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+
+static struct sn9c102_sensor pas106b = {
+ .name = "PAS106B",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_2WIRES,
+ .slave_read_id = 0x40,
+ .slave_write_id = 0x40,
+ .init = &pas106b_init,
+ .qctrl = {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0x1f,
+ .step = 0x01,
+ .default_value = 0x03,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0x1f,
+ .step = 0x01,
+ .default_value = 0x02,
+ .flags = 0,
+ },
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "brightness",
+ .minimum = 0x00,
+ .maximum = 0x1f,
+ .step = 0x01,
+ .default_value = 0x06,
+ .flags = 0,
+ },
+ },
+ .get_ctrl = &pas106b_get_ctrl,
+ .set_ctrl = &pas106b_set_ctrl,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 352,
+ .height = 288,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 352,
+ .height = 288,
+ },
+ },
+ .set_crop = &pas106b_set_crop,
+ .pix_format = {
+ .width = 352,
+ .height = 288,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8, /* we use this field as 'bits per pixel' */
+ }
+};
+
+
+int sn9c102_probe_pas106b(struct sn9c102_device* cam)
+{
+ int r0 = 0, r1 = 0, err = 0;
+ unsigned int pid = 0;
+
+ /* Minimal initialization to enable the I2C communication
+ NOTE: do NOT change the values! */
+ err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */
+ err += sn9c102_write_reg(cam, 0x00, 0x01); /* sensor power on */
+ err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 48 MHz */
+ if (err)
+ return -EIO;
+
+ r0 = sn9c102_i2c_try_read(cam, &pas106b, 0x00);
+ r1 = sn9c102_i2c_try_read(cam, &pas106b, 0x01);
+
+ if (r0 < 0 || r1 < 0)
+ return -EIO;
+
+ pid = (r0 << 11) | ((r1 & 0xf0) >> 4);
+ if (pid != 0x007)
+ return -ENODEV;
+
+ sn9c102_attach_sensor(cam, &pas106b);
+
+ return 0;
+}
--- /dev/null
+/***************************************************************************
+ * API for image sensors connected to the SN9C10[12] PC Camera Controllers *
+ * *
+ * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _SN9C102_SENSOR_H_
+#define _SN9C102_SENSOR_H_
+
+#include <linux/usb.h>
+#include <linux/videodev.h>
+#include <linux/device.h>
+#include <linux/stddef.h>
+#include <linux/errno.h>
+#include <asm/types.h>
+
+struct sn9c102_device;
+struct sn9c102_sensor;
+
+/*****************************************************************************/
+
+/* OVERVIEW.
+ This is a small interface that allows you to add support for any CCD/CMOS
+ image sensors connected to the SN9C10X bridges. The entire API is documented
+ below. In the most general case, to support a sensor there are three steps
+ you have to follow:
+ 1) define the main "sn9c102_sensor" structure by setting the basic fields;
+ 2) write a probing function to be called by the core module when the USB
+ camera is recognized, then add both the USB ids and the name of that
+ function to the two corresponding tables SENSOR_TABLE and ID_TABLE (see
+ below);
+ 3) implement the methods that you want/need (and fill the rest of the main
+ structure accordingly).
+ "sn9c102_pas106b.c" is an example of all this stuff. Remember that you do
+ NOT need to touch the source code of the core module for the things to work
+ properly, unless you find bugs or flaws in it. Finally, do not forget to
+ read the V4L2 API for completeness. */
+
+/*****************************************************************************/
+
+/* Probing functions: on success, you must attach the sensor to the camera
+ by calling sn9c102_attach_sensor() provided below.
+ To enable the I2C communication, you might need to perform a really basic
+ initialization of the SN9C10X chip by using the write function declared
+ ahead.
+ Functions must return 0 on success, the appropriate error otherwise. */
+extern int sn9c102_probe_pas106b(struct sn9c102_device* cam);
+extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam);
+extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam);
+
+/* Add the above entries to this table. Be sure to add the entry in the right
+ place, since, on failure, the next probing routine is called according to
+ the order of the list below, from top to bottom */
+#define SN9C102_SENSOR_TABLE \
+static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = { \
+ &sn9c102_probe_pas106b, /* strong detection based on SENSOR vid/pid */\
+ &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ \
+ &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ \
+ NULL, \
+};
+
+/* Attach a probed sensor to the camera. */
+extern void
+sn9c102_attach_sensor(struct sn9c102_device* cam,
+ struct sn9c102_sensor* sensor);
+
+/* Each SN9C10X camera has proper PID/VID identifiers. Add them here in case.*/
+#define SN9C102_ID_TABLE \
+static const struct usb_device_id sn9c102_id_table[] = { \
+ { USB_DEVICE(0xc45, 0x6001), }, \
+ { USB_DEVICE(0xc45, 0x6005), }, /* TAS5110C1B */ \
+ { USB_DEVICE(0xc45, 0x6009), }, /* PAS106B */ \
+ { USB_DEVICE(0xc45, 0x600d), }, /* PAS106B */ \
+ { USB_DEVICE(0xc45, 0x6024), }, \
+ { USB_DEVICE(0xc45, 0x6025), }, /* TAS5130D1B Maybe also TAS5110C1B */\
+ { USB_DEVICE(0xc45, 0x6028), }, /* Maybe PAS202B */ \
+ { USB_DEVICE(0xc45, 0x6029), }, \
+ { USB_DEVICE(0xc45, 0x602a), }, /* Maybe HV7131[D|E1] */ \
+ { USB_DEVICE(0xc45, 0x602c), }, /* Maybe OV7620 */ \
+ { USB_DEVICE(0xc45, 0x6030), }, /* Maybe MI03 */ \
+ { USB_DEVICE(0xc45, 0x8001), }, \
+ { } \
+};
+
+/*****************************************************************************/
+
+/* Read/write routines: they always return -1 on error, 0 or the read value
+ otherwise. NOTE that a real read operation is not supported by the SN9C10X
+ chip for some of its registers. To work around this problem, a pseudo-read
+ call is provided instead: it returns the last successfully written value
+ on the register (0 if it has never been written), the usual -1 on error. */
+
+/* The "try" I2C I/O versions are used when probing the sensor */
+extern int sn9c102_i2c_try_write(struct sn9c102_device*,struct sn9c102_sensor*,
+ u8 address, u8 value);
+extern int sn9c102_i2c_try_read(struct sn9c102_device*,struct sn9c102_sensor*,
+ u8 address);
+
+/* This must be used if and only if the sensor doesn't implement the standard
+ I2C protocol, like the TASC sensors. There a number of good reasons why you
+ must use the single-byte versions of this function: do not abuse. It writes
+ n bytes, from data0 to datan, (registers 0x09 - 0x09+n of SN9C10X chip) */
+extern int sn9c102_i2c_try_raw_write(struct sn9c102_device* cam,
+ struct sn9c102_sensor* sensor, u8 n,
+ u8 data0, u8 data1, u8 data2, u8 data3,
+ u8 data4, u8 data5);
+
+/* To be used after the sensor struct has been attached to the camera struct */
+extern int sn9c102_i2c_write(struct sn9c102_device*, u8 address, u8 value);
+extern int sn9c102_i2c_read(struct sn9c102_device*, u8 address);
+
+/* I/O on registers in the bridge. Could be used by the sensor methods too */
+extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index);
+extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index);
+
+/* NOTE: there are no debugging functions here. To uniform the output you must
+ use the dev_info()/dev_warn()/dev_err() macros defined in device.h, already
+ included here, the argument being the struct device 'dev' of the sensor
+ structure. Do NOT use these macros before the sensor is attached or the
+ kernel will crash! However you should not need to notify the user about
+ common errors or other messages, since this is done by the master module. */
+
+/*****************************************************************************/
+
+enum sn9c102_i2c_frequency { /* sensors may support both the frequencies */
+ SN9C102_I2C_100KHZ = 0x01,
+ SN9C102_I2C_400KHZ = 0x02,
+};
+
+enum sn9c102_i2c_interface {
+ SN9C102_I2C_2WIRES,
+ SN9C102_I2C_3WIRES,
+};
+
+struct sn9c102_sensor {
+ char name[32], /* sensor name */
+ maintainer[64]; /* name of the mantainer <email> */
+
+ /* These sensor capabilities must be provided if the SN9C10X controller
+ needs to communicate through the sensor serial interface by using
+ at least one of the i2c functions available */
+ enum sn9c102_i2c_frequency frequency;
+ enum sn9c102_i2c_interface interface;
+
+ /* These identifiers must be provided if the image sensor implements
+ the standard I2C protocol. TASC sensors don't, although they have a
+ serial interface: so this is a case where the "raw" I2C version
+ could be helpful. */
+ u8 slave_read_id, slave_write_id; /* reg. 0x09 */
+
+ /* NOTE: Where not noted,most of the functions below are not mandatory.
+ Set to null if you do not implement them. If implemented,
+ they must return 0 on success, the proper error otherwise. */
+
+ int (*init)(struct sn9c102_device* cam);
+ /* This function is called after the sensor has been attached.
+ It should be used to initialize the sensor only, but may also
+ configure part of the SN9C10X chip if necessary. You don't need to
+ setup picture settings like brightness, contrast, etc.. here, if
+ the corrisponding controls are implemented (see below), since
+ they are adjusted in the core driver by calling the set_ctrl()
+ method after init(), where the arguments are the default values
+ specified in the v4l2_queryctrl list of supported controls;
+ Same suggestions apply for other settings, _if_ the corresponding
+ methods are present; if not, the initialization must configure the
+ sensor according to the default configuration structures below. */
+
+ struct v4l2_queryctrl qctrl[V4L2_CID_LASTP1-V4L2_CID_BASE];
+ /* Optional list of default controls, defined as indicated in the
+ V4L2 API. Menu type controls are not handled by this interface. */
+
+ int (*get_ctrl)(struct sn9c102_device* cam, struct v4l2_control* ctrl);
+ int (*set_ctrl)(struct sn9c102_device* cam,
+ const struct v4l2_control* ctrl);
+ /* You must implement at least the set_ctrl method if you have defined
+ the list above. The returned value must follow the V4L2
+ specifications for the VIDIOC_G|C_CTRL ioctls. V4L2_CID_H|VCENTER
+ are not supported by this driver, so do not implement them. Also,
+ passed values are NOT checked to see if they are out of bounds. */
+
+ struct v4l2_cropcap cropcap;
+ /* Think the image sensor as a grid of R,G,B monochromatic pixels
+ disposed according to a particular Bayer pattern, which describes
+ the complete array of pixels, from (0,0) to (xmax, ymax). We will
+ use this coordinate system from now on. It is assumed the sensor
+ chip can be programmed to capture/transmit a subsection of that
+ array of pixels: we will call this subsection "active window".
+ It is not always true that the largest achievable active window can
+ cover the whole array of pixels. The V4L2 API defines another
+ area called "source rectangle", which, in turn, is a subrectangle of
+ the active window. The SN9C10X chip is always programmed to read the
+ source rectangle.
+ The bounds of both the active window and the source rectangle are
+ specified in the cropcap substructures 'bounds' and 'defrect'.
+ By default, the source rectangle should cover the largest possible
+ area. Again, it is not always true that the largest source rectangle
+ can cover the entire active window, although it is a rare case for
+ the hardware we have. The bounds of the source rectangle _must_ be
+ multiple of 16 and must use the same coordinate system as indicated
+ before; their centers shall align initially.
+ If necessary, the sensor chip must be initialized during init() to
+ set the bounds of the active sensor window; however, by default, it
+ usually covers the largest achievable area (maxwidth x maxheight)
+ of pixels, so no particular initialization is needed, if you have
+ defined the correct default bounds in the structures.
+ See the V4L2 API for further details.
+ NOTE: once you have defined the bounds of the active window
+ (struct cropcap.bounds) you must not change them.anymore.
+ Only 'bounds' and 'defrect' fields are mandatory, other fields
+ will be ignored. */
+
+ int (*set_crop)(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect);
+ /* To be called on VIDIOC_C_SETCROP. The core module always calls a
+ default routine which configures the appropriate SN9C10X regs (also
+ scaling), but you may need to override/adjust specific stuff.
+ 'rect' contains width and height values that are multiple of 16: in
+ case you override the default function, you always have to program
+ the chip to match those values; on error return the corresponding
+ error code without rolling back.
+ NOTE: in case, you must program the SN9C10X chip to get rid of
+ blank pixels or blank lines at the _start_ of each line or
+ frame after each HSYNC or VSYNC, so that the image starts with
+ real RGB data (see regs 0x12,0x13) (having set H_SIZE and,
+ V_SIZE you don't have to care about blank pixels or blank
+ lines at the end of each line or frame). */
+
+ struct v4l2_pix_format pix_format;
+ /* What you have to define here are: initial 'width' and 'height' of
+ the target rectangle, the bayer 'pixelformat' and 'priv' which we'll
+ be used to indicate the number of bits per pixel, 8 or 9.
+ Nothing more.
+ NOTE 1: both 'width' and 'height' _must_ be either 1/1 or 1/2 or 1/4
+ of cropcap.defrect.width and cropcap.defrect.height. I
+ suggest 1/1.
+ NOTE 2: as said above, you have to program the SN9C10X chip to get
+ rid of any blank pixels, so that the output of the sensor
+ matches the RGB bayer sequence (i.e. BGBGBG...GRGRGR). */
+
+ const struct device* dev;
+ /* This is the argument for dev_err(), dev_info() and dev_warn(). It
+ is used for debugging purposes. You must not access the struct
+ before the sensor is attached. */
+
+ const struct usb_device* usbdev;
+ /* Points to the usb_device struct after the sensor is attached.
+ Do not touch unless you know what you are doing. */
+
+ /* Do NOT write to the data below, it's READ ONLY. It is used by the
+ core module to store successfully updated values of the above
+ settings, for rollbacks..etc..in case of errors during atomic I/O */
+ struct v4l2_queryctrl _qctrl[V4L2_CID_LASTP1-V4L2_CID_BASE];
+ struct v4l2_rect _rect;
+};
+
+#endif /* _SN9C102_SENSOR_H_ */
--- /dev/null
+/***************************************************************************
+ * Driver for TAS5110C1B image sensor connected to the SN9C10[12] PC *
+ * Camera Controllers *
+ * *
+ * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+
+
+static struct sn9c102_sensor tas5110c1b;
+
+
+static int tas5110c1b_init(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ err += sn9c102_write_reg(cam, 0x01, 0x01);
+ err += sn9c102_write_reg(cam, 0x44, 0x01);
+ err += sn9c102_write_reg(cam, 0x00, 0x10);
+ err += sn9c102_write_reg(cam, 0x00, 0x11);
+ err += sn9c102_write_reg(cam, 0x00, 0x14);
+ err += sn9c102_write_reg(cam, 0x60, 0x17);
+ err += sn9c102_write_reg(cam, 0x06, 0x18);
+ err += sn9c102_write_reg(cam, 0xcb, 0x19);
+
+ return err;
+}
+
+
+static int tas5110c1b_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = &tas5110c1b;
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+
+static struct sn9c102_sensor tas5110c1b = {
+ .name = "TAS5110C1B",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .init = &tas5110c1b_init,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 352,
+ .height = 288,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 352,
+ .height = 288,
+ },
+ },
+ .set_crop = &tas5110c1b_set_crop,
+ .pix_format = {
+ .width = 352,
+ .height = 288,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ }
+};
+
+
+int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam)
+{
+ /* This sensor has no identifiers, so let's attach it anyway */
+ sn9c102_attach_sensor(cam, &tas5110c1b);
+
+ /* At the moment, only devices whose PID is 0x6005 have this sensor */
+ if (tas5110c1b.usbdev->descriptor.idProduct != 0x6005)
+ return -ENODEV;
+
+ return 0;
+}
--- /dev/null
+/***************************************************************************
+ * Driver for TAS5130D1B image sensor connected to the SN9C10[12] PC *
+ * Camera Controllers *
+ * *
+ * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include "sn9c102_sensor.h"
+
+
+static struct sn9c102_sensor tas5130d1b;
+
+
+static int tas5130d1b_init(struct sn9c102_device* cam)
+{
+ int err = 0;
+
+ err += sn9c102_write_reg(cam, 0x01, 0x01);
+ err += sn9c102_write_reg(cam, 0x20, 0x17);
+ err += sn9c102_write_reg(cam, 0x04, 0x01);
+ err += sn9c102_write_reg(cam, 0x01, 0x10);
+ err += sn9c102_write_reg(cam, 0x00, 0x11);
+ err += sn9c102_write_reg(cam, 0x00, 0x14);
+ err += sn9c102_write_reg(cam, 0x60, 0x17);
+ err += sn9c102_write_reg(cam, 0x07, 0x18);
+ err += sn9c102_write_reg(cam, 0x33, 0x19);
+
+ err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0x40,
+ 0x47, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x02, 0x20,
+ 0xa9, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0xc0,
+ 0x49, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x02, 0x20,
+ 0x6c, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0xc0,
+ 0x08, 0, 0);
+ err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0x20,
+ 0x00, 0, 0);
+
+ err += sn9c102_write_reg(cam, 0x63, 0x19);
+
+ return err;
+}
+
+
+static int tas5130d1b_set_crop(struct sn9c102_device* cam,
+ const struct v4l2_rect* rect)
+{
+ struct sn9c102_sensor* s = &tas5130d1b;
+ int err = 0;
+ u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 104,
+ v_start = (u8)(rect->top - s->cropcap.bounds.top) + 12;
+
+ err += sn9c102_write_reg(cam, h_start, 0x12);
+ err += sn9c102_write_reg(cam, v_start, 0x13);
+
+ return err;
+}
+
+
+static struct sn9c102_sensor tas5130d1b = {
+ .name = "TAS5130D1B",
+ .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+ .frequency = SN9C102_I2C_100KHZ,
+ .interface = SN9C102_I2C_3WIRES,
+ .init = &tas5130d1b_init,
+ .cropcap = {
+ .bounds = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ .defrect = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ },
+ .set_crop = &tas5130d1b_set_crop,
+ .pix_format = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ .priv = 8,
+ }
+};
+
+
+int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam)
+{
+ /* This sensor has no identifiers, so let's attach it anyway */
+ sn9c102_attach_sensor(cam, &tas5130d1b);
+
+ /* At the moment, only devices whose PID is 0x6025 have this sensor */
+ if (tas5130d1b.usbdev->descriptor.idProduct != 0x6025)
+ return -ENODEV;
+
+ dev_info(tas5130d1b.dev, "TAS5130D1B detected, but the support for it "
+ "is disabled at the moment - needs further "
+ "testing -\n");
+
+ return -ENODEV;
+}
--- /dev/null
+/***************************************************************************
+ * Interface for video post-processing functions for the W996[87]CF driver *
+ * for Linux. *
+ * *
+ * Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _W9968CF_VPP_H_
+#define _W9968CF_VPP_H_
+
+#include <linux/module.h>
+#include <asm/types.h>
+
+struct w9968cf_vpp_t {
+ struct module* owner;
+ int (*check_headers)(const unsigned char*, const unsigned long);
+ int (*decode)(const char*, const unsigned long, const unsigned,
+ const unsigned, char*);
+ void (*swap_yuvbytes)(void*, unsigned long);
+ void (*uyvy_to_rgbx)(u8*, unsigned long, u8*, u16, u8);
+ void (*scale_up)(u8*, u8*, u16, u16, u16, u16, u16);
+
+ u8 busy; /* read-only flag: module is/is not in use */
+};
+
+extern int w9968cf_vppmod_register(struct w9968cf_vpp_t*);
+extern int w9968cf_vppmod_deregister(struct w9968cf_vpp_t*);
+
+#endif /* _W9968CF_VPP_H_ */
*
* CAUTION: Generally you should use 0 < degrees < 180 as anything else
* is probably beyond the range of your servo and may damage it.
+ *
+ * Jun 16, 2004: Sean Young <sean@mess.org>
+ * - cleanups
+ * - was using memory after kfree()
*/
#include <linux/config.h>
#define DRIVER_AUTHOR "Sean Young <sean@mess.org>"
#define DRIVER_DESC "USB PhidgetServo Driver"
-#define VENDOR_ID_GLAB 0x06c2
-#define DEVICE_ID_4MOTOR_SERVO_30 0x0038
-#define DEVICE_ID_1MOTOR_SERVO_30 0x0039
+#define VENDOR_ID_GLAB 0x06c2
+#define DEVICE_ID_GLAB_PHIDGETSERVO_QUAD 0x0038
+#define DEVICE_ID_GLAB_PHIDGETSERVO_UNI 0x0039
+
+#define VENDOR_ID_WISEGROUP 0x0925
+#define VENDOR_ID_WISEGROUP_PHIDGETSERVO_QUAD 0x8101
+#define VENDOR_ID_WISEGROUP_PHIDGETSERVO_UNI 0x8104
-#define VENDOR_ID_WISEGROUP 0x0925
-#define DEVICE_ID_1MOTOR_SERVO_20 0x8101
-#define DEVICE_ID_4MOTOR_SERVO_20 0x8104
+#define SERVO_VERSION_30 0x01
+#define SERVO_COUNT_QUAD 0x02
static struct usb_device_id id_table[] = {
- {USB_DEVICE(VENDOR_ID_GLAB, DEVICE_ID_4MOTOR_SERVO_30)},
- {USB_DEVICE(VENDOR_ID_GLAB, DEVICE_ID_1MOTOR_SERVO_30)},
- {USB_DEVICE(VENDOR_ID_WISEGROUP, DEVICE_ID_4MOTOR_SERVO_20)},
- {USB_DEVICE(VENDOR_ID_WISEGROUP, DEVICE_ID_1MOTOR_SERVO_20)},
+ {
+ USB_DEVICE(VENDOR_ID_GLAB, DEVICE_ID_GLAB_PHIDGETSERVO_QUAD),
+ .driver_info = SERVO_VERSION_30 | SERVO_COUNT_QUAD
+ },
+ {
+ USB_DEVICE(VENDOR_ID_GLAB, DEVICE_ID_GLAB_PHIDGETSERVO_UNI),
+ .driver_info = SERVO_VERSION_30
+ },
+ {
+ USB_DEVICE(VENDOR_ID_WISEGROUP,
+ VENDOR_ID_WISEGROUP_PHIDGETSERVO_QUAD),
+ .driver_info = SERVO_COUNT_QUAD
+ },
+ {
+ USB_DEVICE(VENDOR_ID_WISEGROUP,
+ VENDOR_ID_WISEGROUP_PHIDGETSERVO_UNI),
+ .driver_info = 0
+ },
{}
};
struct phidget_servo {
struct usb_device *udev;
- int version;
- int quad_servo;
+ ulong type;
int pulse[4];
int degrees[4];
int minutes[4];
};
-static void
+static int
change_position_v30(struct phidget_servo *servo, int servo_no, int degrees,
int minutes)
{
if (!buffer) {
dev_err(&servo->udev->dev, "%s - out of memory\n",
__FUNCTION__);
- return;
+ return -ENOMEM;
}
/*
retval = usb_control_msg(servo->udev,
usb_sndctrlpipe(servo->udev, 0),
0x09, 0x21, 0x0200, 0x0000, buffer, 6, 2 * HZ);
- if (retval != 6)
- dev_err(&servo->udev->dev, "retval = %d\n", retval);
+
kfree(buffer);
+
+ return retval;
}
-static void
+static int
change_position_v20(struct phidget_servo *servo, int servo_no, int degrees,
int minutes)
{
if (!buffer) {
dev_err(&servo->udev->dev, "%s - out of memory\n",
__FUNCTION__);
- return;
+ return -ENOMEM;
}
/*
retval = usb_control_msg(servo->udev,
usb_sndctrlpipe(servo->udev, 0),
0x09, 0x21, 0x0200, 0x0000, buffer, 2, 2 * HZ);
- if (retval != 2)
- dev_err(&servo->udev->dev, "retval = %d\n", retval);
+
kfree(buffer);
+
+ return retval;
}
#define show_set(value) \
static ssize_t set_servo##value (struct device *dev, \
const char *buf, size_t count) \
{ \
- int degrees, minutes; \
+ int degrees, minutes, retval; \
struct usb_interface *intf = to_usb_interface (dev); \
struct phidget_servo *servo = usb_get_intfdata (intf); \
\
return -EINVAL; \
} \
\
- if (servo->version >= 3) \
- change_position_v30 (servo, value, degrees, minutes); \
+ if (servo->type & SERVO_VERSION_30) \
+ retval = change_position_v30 (servo, value, degrees, \
+ minutes); \
else \
- change_position_v20 (servo, value, degrees, minutes); \
+ retval = change_position_v20 (servo, value, degrees, \
+ minutes); \
\
- return count; \
+ return retval < 0 ? retval : count; \
} \
\
static ssize_t show_servo##value (struct device *dev, char *buf) \
servo_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
- struct phidget_servo *dev = NULL;
+ struct phidget_servo *dev;
dev = kmalloc(sizeof (struct phidget_servo), GFP_KERNEL);
if (dev == NULL) {
memset(dev, 0x00, sizeof (*dev));
dev->udev = usb_get_dev(udev);
- switch (udev->descriptor.idVendor) {
- case VENDOR_ID_WISEGROUP:
- dev->version = 2;
- break;
- case VENDOR_ID_GLAB:
- dev->version = 3;
- break;
- }
- switch (udev->descriptor.idProduct) {
- case DEVICE_ID_4MOTOR_SERVO_20:
- case DEVICE_ID_4MOTOR_SERVO_30:
- dev->quad_servo = 1;
- break;
- case DEVICE_ID_1MOTOR_SERVO_20:
- case DEVICE_ID_1MOTOR_SERVO_30:
- dev->quad_servo = 0;
- break;
- }
-
+ dev->type = id->driver_info;
usb_set_intfdata(interface, dev);
device_create_file(&interface->dev, &dev_attr_servo0);
- if (dev->quad_servo) {
+ if (dev->type & SERVO_COUNT_QUAD) {
device_create_file(&interface->dev, &dev_attr_servo1);
device_create_file(&interface->dev, &dev_attr_servo2);
device_create_file(&interface->dev, &dev_attr_servo3);
}
dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 attached\n",
- dev->quad_servo ? 4 : 1, dev->version);
- if (dev->version == 2)
+ dev->type & SERVO_COUNT_QUAD ? 4 : 1,
+ dev->type & SERVO_VERSION_30 ? 3 : 2);
+
+ if(!(dev->type & SERVO_VERSION_30))
dev_info(&interface->dev,
"WARNING: v2.0 not tested! Please report if it works.\n");
usb_set_intfdata(interface, NULL);
device_remove_file(&interface->dev, &dev_attr_servo0);
- if (dev->quad_servo) {
+ if (dev->type & SERVO_COUNT_QUAD) {
device_remove_file(&interface->dev, &dev_attr_servo1);
device_remove_file(&interface->dev, &dev_attr_servo2);
device_remove_file(&interface->dev, &dev_attr_servo3);
usb_put_dev(dev->udev);
- kfree(dev);
-
dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 detached\n",
- dev->quad_servo ? 4 : 1, dev->version);
+ dev->type & SERVO_COUNT_QUAD ? 4 : 1,
+ dev->type & SERVO_VERSION_30 ? 3 : 2);
+
+ kfree(dev);
}
static struct usb_driver servo_driver = {
static int __init
phidget_servo_init(void)
{
- int retval = 0;
+ int retval;
retval = usb_register(&servo_driver);
if (retval)
info->fix.ypanstep = 0;
} else {
info->fix.ywrapstep = 0;
- if (par->vmode &= FB_VMODE_SMOOTH_XPAN)
+ if (par->vmode & FB_VMODE_SMOOTH_XPAN)
info->fix.xpanstep = 1;
else
info->fix.xpanstep = 16<<maxfmode;
*/
{
- u_long tmp = DIVUL(200E9, amiga_eclock);
+ u_long tmp = DIVUL(200000000000ULL, amiga_eclock);
pixclock[TAG_SHRES] = (tmp + 4) / 8; /* SHRES: 35 ns / 28 MHz */
pixclock[TAG_HIRES] = (tmp + 2) / 4; /* HIRES: 70 ns / 14 MHz */
}
pci_write_config_dword(dp, 4, 0x02800083);
- writeb(3, addr + 0x400784);
+ writeb(3, p->screen_base + 0x400784);
init_asiliant(p, addr);
}
-#ifdef __i386__
+#ifdef CONFIG_X86
static void * __devinit aty128_find_mem_vbios(struct aty128fb_par *par)
{
/* I simplified this code as we used to miss the signatures in
}
return rom_base;
}
-#endif /* __i386__ */
+#endif
#endif /* ndef(__sparc__) */
/* fill in known card constants if pll_block is not available */
#ifndef __sparc__
bios = aty128_map_ROM(par, pdev);
-#ifdef __i386__
+#ifdef CONFIG_X86
if (bios == NULL)
bios = aty128_find_mem_vbios(par);
#endif
case FBIO_ATY128_SET_MIRROR:
if (par->chip_gen != rage_M3)
return -EINVAL;
- rc = get_user(value, (__u32*)arg);
+ rc = get_user(value, (__u32 __user *)arg);
if (rc)
return rc;
par->lcd_on = (value & 0x01) != 0;
if (par->chip_gen != rage_M3)
return -EINVAL;
value = (par->crt_on << 1) | par->lcd_on;
- return put_user(value, (__u32*)arg);
+ return put_user(value, (__u32 __user *)arg);
}
#endif
return -EINVAL;
wait_for_idle(par);
aty128fb_set_par(info);
fb_pan_display(info, &info->var);
- fb_set_cmap(&info->cmap, 1, info);
+ fb_set_cmap(&info->cmap, info);
/* Refresh */
fb_set_suspend(info, 0);
for (m = MIN_M; m <= MAX_M; m++) {
for (n = MIN_N; n <= MAX_N; n++) {
- tempA = (14.31818 * 65536);
+ tempA = 938356; /* 14.31818 * 65536 */
tempA *= (n + 8); /* 43..256 */
tempB = twoToKth * 256;
tempB *= (m + 2); /* 4..32 */
{
struct cg14_par *par = (struct cg14_par *) info->par;
struct cg14_regs *regs = par->regs;
- struct mdi_cfginfo kmdi, *mdii;
+ struct mdi_cfginfo kmdi, __user *mdii;
unsigned long flags;
int cur_mode, mode, ret = 0;
kmdi.mdi_size = par->ramsize;
spin_unlock_irqrestore(&par->lock, flags);
- mdii = (struct mdi_cfginfo *) arg;
+ mdii = (struct mdi_cfginfo __user *) arg;
if (copy_to_user(mdii, &kmdi, sizeof(kmdi)))
ret = -EFAULT;
break;
case MDI_SET_PIXELMODE:
- if (get_user(mode, (int *) arg)) {
+ if (get_user(mode, (int __user *) arg)) {
ret = -EFAULT;
break;
}
case PBOOK_SLEEP_REJECT:
if (save_framebuffer) {
vfree(save_framebuffer);
- save_framebuffer = 0;
+ save_framebuffer = NULL;
}
break;
case PBOOK_SLEEP_NOW:
if (save_framebuffer) {
memcpy(p->screen_base, save_framebuffer, nb);
vfree(save_framebuffer);
- save_framebuffer = 0;
+ save_framebuffer = NULL;
}
chipsfb_blank(0, p);
break;
};
struct fb_var_screeninfo dnfb_var __devinitdata = {
- .xres 1280,
- .yres 1024,
- .xres_virtual 2048,
- .yres_virtual 1024,
- .bits_per_pixel 1,
- .height -1,
- .width -1,
- .vmode FB_VMODE_NONINTERLACED,
+ .xres = 1280,
+ .yres = 1024,
+ .xres_virtual = 2048,
+ .yres_virtual = 1024,
+ .bits_per_pixel = 1,
+ .height = -1,
+ .width = -1,
+ .vmode = FB_VMODE_NONINTERLACED,
};
static struct fb_fix_screeninfo dnfb_fix __devinitdata = {
- .id "Apollo Mono",
- .smem_start (FRAME_BUFFER_START + IO_BASE),
- .smem_len FRAME_BUFFER_LEN,
- .type FB_TYPE_PACKED_PIXELS,
- .visual FB_VISUAL_MONO10,
- .line_length 256,
+ .id = "Apollo Mono",
+ .smem_start = (FRAME_BUFFER_START + IO_BASE),
+ .smem_len = FRAME_BUFFER_LEN,
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_MONO10,
+ .line_length = 256,
};
static int dnfb_blank(int blank, struct fb_info *info)
}
cmap->start = 0;
cmap->len = len;
- fb_copy_cmap(fb_default_cmap(len), cmap, 0);
+ fb_copy_cmap(fb_default_cmap(len), cmap);
return 0;
fail:
* fb_copy_cmap - copy a colormap
* @from: frame buffer colormap structure
* @to: frame buffer colormap structure
- * @fsfromto: determine copy method
*
* Copy contents of colormap from @from to @to.
- *
- * @fsfromto accepts the following integer parameters:
- * 0: memcpy function
- * 1: copy_from_user() function to copy from userspace
- * 2: copy_to_user() function to copy to userspace
- *
*/
-int fb_copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto)
+int fb_copy_cmap(struct fb_cmap *from, struct fb_cmap *to)
{
- int tooff = 0, fromoff = 0;
- int size;
-
- if (to->start > from->start)
- fromoff = to->start-from->start;
- else
- tooff = from->start-to->start;
- size = to->len-tooff;
- if (size > (int) (from->len - fromoff))
- size = from->len-fromoff;
- if (size <= 0)
- return -EINVAL;
- size *= sizeof(u16);
-
- switch (fsfromto) {
- case 0:
+ int tooff = 0, fromoff = 0;
+ int size;
+
+ if (to->start > from->start)
+ fromoff = to->start - from->start;
+ else
+ tooff = from->start - to->start;
+ size = to->len - tooff;
+ if (size > (int) (from->len - fromoff))
+ size = from->len - fromoff;
+ if (size <= 0)
+ return -EINVAL;
+ size *= sizeof(u16);
+
memcpy(to->red+tooff, from->red+fromoff, size);
memcpy(to->green+tooff, from->green+fromoff, size);
memcpy(to->blue+tooff, from->blue+fromoff, size);
if (from->transp && to->transp)
- memcpy(to->transp+tooff, from->transp+fromoff, size);
- break;
- case 1:
- if (copy_from_user(to->red+tooff, from->red+fromoff, size))
- return -EFAULT;
- if (copy_from_user(to->green+tooff, from->green+fromoff, size))
- return -EFAULT;
- if (copy_from_user(to->blue+tooff, from->blue+fromoff, size))
- return -EFAULT;
- if (from->transp && to->transp)
- if (copy_from_user(to->transp+tooff, from->transp+fromoff, size))
- return -EFAULT;
- break;
- case 2:
+ memcpy(to->transp+tooff, from->transp+fromoff, size);
+ return 0;
+}
+
+int fb_cmap_to_user(struct fb_cmap *from, struct fb_cmap_user *to)
+{
+ int tooff = 0, fromoff = 0;
+ int size;
+
+ if (to->start > from->start)
+ fromoff = to->start - from->start;
+ else
+ tooff = from->start - to->start;
+ size = to->len - tooff;
+ if (size > (int) (from->len - fromoff))
+ size = from->len - fromoff;
+ if (size <= 0)
+ return -EINVAL;
+ size *= sizeof(u16);
+
if (copy_to_user(to->red+tooff, from->red+fromoff, size))
return -EFAULT;
if (copy_to_user(to->green+tooff, from->green+fromoff, size))
if (from->transp && to->transp)
if (copy_to_user(to->transp+tooff, from->transp+fromoff, size))
return -EFAULT;
- break;
- }
- return 0;
+ return 0;
}
/**
* fb_set_cmap - set the colormap
* @cmap: frame buffer colormap structure
- * @kspc: boolean, 1 copy local, 0 get_user() function
* @info: frame buffer info structure
*
* Sets the colormap @cmap for a screen of device @info.
*
*/
-int fb_set_cmap(struct fb_cmap *cmap, int kspc, struct fb_info *info)
+int fb_set_cmap(struct fb_cmap *cmap, struct fb_info *info)
{
- int i, start;
- u16 *red, *green, *blue, *transp;
- u_int hred, hgreen, hblue, htransp;
-
- red = cmap->red;
- green = cmap->green;
- blue = cmap->blue;
- transp = cmap->transp;
- start = cmap->start;
-
- if (start < 0 || !info->fbops->fb_setcolreg)
- return -EINVAL;
- for (i = 0; i < cmap->len; i++) {
- if (kspc) {
- hred = *red;
- hgreen = *green;
- hblue = *blue;
- htransp = transp ? *transp : 0xffff;
- } else {
- get_user(hred, red);
- get_user(hgreen, green);
- get_user(hblue, blue);
- if (transp)
- get_user(htransp, transp);
- else
- htransp = 0xffff;
+ int i, start;
+ u16 *red, *green, *blue, *transp;
+ u_int hred, hgreen, hblue, htransp = 0xffff;
+
+ red = cmap->red;
+ green = cmap->green;
+ blue = cmap->blue;
+ transp = cmap->transp;
+ start = cmap->start;
+
+ if (start < 0 || !info->fbops->fb_setcolreg)
+ return -EINVAL;
+ for (i = 0; i < cmap->len; i++) {
+ hred = *red++;
+ hgreen = *green++;
+ hblue = *blue++;
+ if (transp)
+ htransp = *transp++;
+ if (info->fbops->fb_setcolreg(start++,
+ hred, hgreen, hblue, htransp,
+ info))
+ break;
}
- red++;
- green++;
- blue++;
- if (transp)
- transp++;
- if (info->fbops->fb_setcolreg(start++, hred, hgreen, hblue, htransp, info))
- return 0;
- }
- return 0;
+ return 0;
}
+int fb_set_user_cmap(struct fb_cmap_user *cmap, struct fb_info *info)
+{
+ int i, start;
+ u16 __user *red, *green, *blue, *transp;
+ u_int hred, hgreen, hblue, htransp = 0xffff;
+
+ red = cmap->red;
+ green = cmap->green;
+ blue = cmap->blue;
+ transp = cmap->transp;
+ start = cmap->start;
+
+ if (start < 0 || !info->fbops->fb_setcolreg)
+ return -EINVAL;
+ for (i = 0; i < cmap->len; i++, red++, blue++, green++) {
+ if (get_user(hred, red) ||
+ get_user(hgreen, green) ||
+ get_user(hblue, blue) ||
+ (transp && get_user(htransp, transp)))
+ return -EFAULT;
+ if (info->fbops->fb_setcolreg(start++,
+ hred, hgreen, hblue, htransp,
+ info))
+ return 0;
+ if (transp)
+ transp++;
+ }
+ return 0;
+}
/**
* fb_default_cmap - get default colormap
break;
}
} else
- fb_set_cmap(&info->cmap, 1, info);
+ fb_set_cmap(&info->cmap, info);
return 0;
}
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/mach-types.h>
#include <asm/uaccess.h>
#include <asm/arch/bitfield.h>
#include <asm/arch/pxafb.h>
u_int val, ret = 1;
if (regno < fbi->palette_size) {
- val = ((red >> 0) & 0xf800);
- val |= ((green >> 5) & 0x07e0);
- val |= ((blue >> 11) & 0x001f);
-
+ if (fbi->fb.var.grayscale) {
+ val = ((blue >> 8) & 0x00ff);
+ } else {
+ val = ((red >> 0) & 0xf800);
+ val |= ((green >> 5) & 0x07e0);
+ val |= ((blue >> 11) & 0x001f);
+ }
fbi->palette_cpu[regno] = val;
ret = 0;
}
switch (fbi->fb.fix.visual) {
case FB_VISUAL_TRUECOLOR:
/*
- * 12 or 16-bit True Colour. We encode the RGB value
+ * 16-bit True Colour. We encode the RGB value
* according to the RGB bitfield information.
*/
if (regno < 16) {
* The pixel packing format is described on page 7-11 of the
* PXA2XX Developer's Manual.
*/
- if ( var->bits_per_pixel == 16 ) {
+ if (var->bits_per_pixel == 16) {
var->red.offset = 11; var->red.length = 5;
var->green.offset = 5; var->green.length = 6;
var->blue.offset = 0; var->blue.length = 5;
fbi->fb.fix.line_length = var->xres_virtual *
var->bits_per_pixel / 8;
- fbi->palette_size = var->bits_per_pixel == 8 ? 256 : 16;
+ if (var->bits_per_pixel == 16)
+ fbi->palette_size = 0;
+ else
+ fbi->palette_size = var->bits_per_pixel == 1 ? 4 : 1 << var->bits_per_pixel;
palette_mem_size = fbi->palette_size * sizeof(u16);
*/
pxafb_set_truecolor(fbi->fb.fix.visual == FB_VISUAL_TRUECOLOR);
+ if (fbi->fb.var.bits_per_pixel == 16)
+ fb_dealloc_cmap(&fbi->fb.cmap);
+ else
+ fb_alloc_cmap(&fbi->fb.cmap, 1<<fbi->fb.var.bits_per_pixel, 0);
+
pxafb_activate_var(var, fbi);
return 0;
/*
* pxafb_blank():
* Blank the display by setting all palette values to zero. Note, the
- * 12 and 16 bpp modes don't really use the palette, so this will not
+ * 16 bpp mode does not really use the palette, so this will not
* blank the display in all modes.
*/
static int pxafb_blank(int blank, struct fb_info *info)
//TODO if (pxafb_blank_helper) pxafb_blank_helper(blank);
if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR ||
fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
- fb_set_cmap(&fbi->fb.cmap, 1, info);
+ fb_set_cmap(&fbi->fb.cmap, info);
pxafb_schedule_work(fbi, C_ENABLE);
}
return 0;
* the YRES parameter.
*/
lines_per_panel = var->yres;
- if (fbi->lccr0 & LCCR0_SDS)
+ if ((fbi->lccr0 & LCCR0_SDS) == LCCR0_Dual)
lines_per_panel /= 2;
new_regs.lccr2 =
fbi->dmadesc_palette_cpu->fidr = 0;
fbi->dmadesc_palette_cpu->ldcmd = (fbi->palette_size * 2) | LDCMD_PAL;
- if( var->bits_per_pixel < 12)
- {
- /* assume any mode with <12 bpp is palette driven */
- fbi->dmadesc_palette_cpu->fdadr = fbi->dmadesc_fbhigh_dma;
- fbi->dmadesc_fbhigh_cpu->fdadr = fbi->dmadesc_palette_dma;
- fbi->fdadr0 = fbi->dmadesc_palette_dma; /* flips back and forth between pal and fbhigh */
- }
- else
- {
+ if (var->bits_per_pixel == 16) {
/* palette shouldn't be loaded in true-color mode */
fbi->dmadesc_fbhigh_cpu->fdadr = fbi->dmadesc_fbhigh_dma;
fbi->fdadr0 = fbi->dmadesc_fbhigh_dma; /* no pal just fbhigh */
/* init it to something, even though we won't be using it */
fbi->dmadesc_palette_cpu->fdadr = fbi->dmadesc_palette_dma;
+ } else {
+ fbi->dmadesc_palette_cpu->fdadr = fbi->dmadesc_fbhigh_dma;
+ fbi->dmadesc_fbhigh_cpu->fdadr = fbi->dmadesc_palette_dma;
+ fbi->fdadr0 = fbi->dmadesc_palette_dma; /* flips back and forth between pal and fbhigh */
}
#if 0
}
else {
- printk( KERN_ERR "pxafb_setup_gpio: unable to determine bits per pixel\n");
+ printk(KERN_ERR "pxafb_setup_gpio: unable to determine bits per pixel\n");
}
}
struct pxafb_mach_info *inf = dev->platform_data;
/* Alloc the pxafb_info and pseudo_palette in one step */
- fbi = kmalloc(sizeof(struct pxafb_info) + sizeof(u32) * 17, GFP_KERNEL);
+ fbi = kmalloc(sizeof(struct pxafb_info) + sizeof(u32) * 16, GFP_KERNEL);
if (!fbi)
return NULL;
res_specified = 1;
}
done:
- if ( res_specified ) {
- dev_info(dev, "overriding resolution: %dx%x\n", xres, yres);
+ if (res_specified) {
+ dev_info(dev, "overriding resolution: %dx%d\n", xres, yres);
inf->xres = xres; inf->yres = yres;
}
- if ( bpp_specified )
+ if (bpp_specified)
switch (bpp) {
case 1:
case 2:
}
} else if (!strncmp(this_opt, "pixclock:", 9)) {
inf->pixclock = simple_strtoul(this_opt+9, NULL, 0);
- dev_info(dev, "override pixclock: %u\n", inf->pixclock);
+ dev_info(dev, "override pixclock: %ld\n", inf->pixclock);
} else if (!strncmp(this_opt, "left:", 5)) {
inf->left_margin = simple_strtoul(this_opt+5, NULL, 0);
dev_info(dev, "override left: %u\n", inf->left_margin);
inf->vsync_len = simple_strtoul(this_opt+9, NULL, 0);
dev_info(dev, "override vsynclen: %u\n", inf->vsync_len);
} else if (!strncmp(this_opt, "hsync:", 6)) {
- if ( simple_strtoul(this_opt+6, NULL, 0) == 0 ) {
+ if (simple_strtoul(this_opt+6, NULL, 0) == 0) {
dev_info(dev, "override hsync: Active Low\n");
inf->sync &= ~FB_SYNC_HOR_HIGH_ACT;
} else {
inf->sync |= FB_SYNC_HOR_HIGH_ACT;
}
} else if (!strncmp(this_opt, "vsync:", 6)) {
- if ( simple_strtoul(this_opt+6, NULL, 0) == 0 ) {
+ if (simple_strtoul(this_opt+6, NULL, 0) == 0) {
dev_info(dev, "override vsync: Active Low\n");
inf->sync &= ~FB_SYNC_VERT_HIGH_ACT;
} else {
inf->sync |= FB_SYNC_VERT_HIGH_ACT;
}
} else if (!strncmp(this_opt, "dpc:", 4)) {
- if ( simple_strtoul(this_opt+4, NULL, 0) == 0 ) {
+ if (simple_strtoul(this_opt+4, NULL, 0) == 0) {
dev_info(dev, "override double pixel clock: false\n");
inf->lccr3 &= ~LCCR3_DPC;
} else {
inf->lccr3 |= LCCR3_DPC;
}
} else if (!strncmp(this_opt, "outputen:", 9)) {
- if ( simple_strtoul(this_opt+9, NULL, 0) == 0 ) {
+ if (simple_strtoul(this_opt+9, NULL, 0) == 0) {
dev_info(dev, "override output enable: active low\n");
- inf->lccr3 = ( inf->lccr3 & ~LCCR3_OEP ) | LCCR3_OutEnL;
+ inf->lccr3 = (inf->lccr3 & ~LCCR3_OEP) | LCCR3_OutEnL;
} else {
dev_info(dev, "override output enable: active high\n");
- inf->lccr3 = ( inf->lccr3 & ~LCCR3_OEP ) | LCCR3_OutEnH;
+ inf->lccr3 = (inf->lccr3 & ~LCCR3_OEP) | LCCR3_OutEnH;
}
} else if (!strncmp(this_opt, "pixclockpol:", 12)) {
- if ( simple_strtoul(this_opt+12, NULL, 0) == 0 ) {
+ if (simple_strtoul(this_opt+12, NULL, 0) == 0) {
dev_info(dev, "override pixel clock polarity: falling edge\n");
- inf->lccr3 = ( inf->lccr3 & ~LCCR3_PCP ) | LCCR3_PixFlEdg;
+ inf->lccr3 = (inf->lccr3 & ~LCCR3_PCP) | LCCR3_PixFlEdg;
} else {
dev_info(dev, "override pixel clock polarity: rising edge\n");
- inf->lccr3 = ( inf->lccr3 & ~LCCR3_PCP ) | LCCR3_PixRsEdg;
+ inf->lccr3 = (inf->lccr3 & ~LCCR3_PCP) | LCCR3_PixRsEdg;
}
} else if (!strncmp(this_opt, "color", 5)) {
inf->lccr0 = (inf->lccr0 & ~LCCR0_CMS) | LCCR0_Color;
{
struct pxafb_info *fbi;
struct pxafb_mach_info *inf;
- unsigned long flags;
int ret;
dev_dbg(dev, "pxafb_probe\n");
#ifdef CONFIG_FB_PXA_PARAMETERS
ret = pxafb_parse_options(dev, g_options);
- if ( ret < 0 )
+ if (ret < 0)
goto failed;
#endif
/* Check for various illegal bit-combinations. Currently only
* a warning is given. */
- if ( inf->lccr0 & LCCR0_INVALID_CONFIG_MASK )
+ if (inf->lccr0 & LCCR0_INVALID_CONFIG_MASK)
dev_warn(dev, "machine LCCR0 setting contains illegal bits: %08x\n",
inf->lccr0 & LCCR0_INVALID_CONFIG_MASK);
- if ( inf->lccr3 & LCCR3_INVALID_CONFIG_MASK )
+ if (inf->lccr3 & LCCR3_INVALID_CONFIG_MASK)
dev_warn(dev, "machine LCCR3 setting contains illegal bits: %08x\n",
inf->lccr3 & LCCR3_INVALID_CONFIG_MASK);
- if ( inf->lccr0 & LCCR0_DPD &&
- ( ( inf->lccr0 & LCCR0_PAS ) != LCCR0_Pas ||
- ( inf->lccr0 & LCCR0_SDS ) != LCCR0_Sngl ||
- ( inf->lccr0 & LCCR0_CMS ) != LCCR0_Mono ) )
+ if (inf->lccr0 & LCCR0_DPD &&
+ ((inf->lccr0 & LCCR0_PAS) != LCCR0_Pas ||
+ (inf->lccr0 & LCCR0_SDS) != LCCR0_Sngl ||
+ (inf->lccr0 & LCCR0_CMS) != LCCR0_Mono))
dev_warn(dev, "Double Pixel Data (DPD) mode is only valid in passive mono"
" single panel mode\n");
- if ( (inf->lccr0 & LCCR0_PAS) == LCCR0_Act &&
- ( inf->lccr0 & LCCR0_SDS ) == LCCR0_Dual )
+ if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Act &&
+ (inf->lccr0 & LCCR0_SDS) == LCCR0_Dual)
dev_warn(dev, "Dual panel only valid in passive mode\n");
- if ( (inf->lccr0 & LCCR0_PAS ) == LCCR0_Pas &&
- (inf->upper_margin || inf->lower_margin) )
+ if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Pas &&
+ (inf->upper_margin || inf->lower_margin))
dev_warn(dev, "Upper and lower margins must be 0 in passive mode\n");
#endif
goto failed;
}
/* enable LCD controller clock */
- local_irq_save(flags);
- CKEN |= CKEN16_LCD;
- local_irq_restore(flags);
+ pxa_set_cken(CKEN16_LCD, 1);
ret = request_irq(IRQ_LCD, pxafb_handle_irq, SA_INTERRUPT, "LCD", fbi);
if (ret) {
}
static struct device_driver pxafb_driver = {
- .name = "pxafb",
+ .name = "pxa2xx-fb",
.bus = &platform_bus_type,
.probe = pxafb_probe,
#ifdef CONFIG_PM
--- /dev/null
+/*
+ * linux/drivers/video/riva/fbdev-i2c.c - nVidia i2c
+ *
+ * Maintained by Ani Joshi <ajoshi@shell.unixbox.com>
+ *
+ * Copyright 2004 Antonino A. Daplas <adaplas @pol.net>
+ *
+ * Based on radeonfb-i2c.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/fb.h>
+
+#include <asm/io.h>
+
+#include "rivafb.h"
+#include "../edid.h"
+
+#define RIVA_DDC 0x50
+
+static void riva_gpio_setscl(void* data, int state)
+{
+ struct riva_i2c_chan *chan = (struct riva_i2c_chan *)data;
+ struct riva_par *par = chan->par;
+ u32 val;
+
+ VGA_WR08(par->riva.PCIO, 0x3d4, chan->ddc_base + 1);
+ val = VGA_RD08(par->riva.PCIO, 0x3d5) & 0xf0;
+
+ if (state)
+ val |= 0x20;
+ else
+ val &= ~0x20;
+
+ VGA_WR08(par->riva.PCIO, 0x3d4, chan->ddc_base + 1);
+ VGA_WR08(par->riva.PCIO, 0x3d5, val | 0x1);
+}
+
+static void riva_gpio_setsda(void* data, int state)
+{
+ struct riva_i2c_chan *chan = (struct riva_i2c_chan *)data;
+ struct riva_par *par = chan->par;
+ u32 val;
+
+ VGA_WR08(par->riva.PCIO, 0x3d4, chan->ddc_base + 1);
+ val = VGA_RD08(par->riva.PCIO, 0x3d5) & 0xf0;
+
+ if (state)
+ val |= 0x10;
+ else
+ val &= ~0x10;
+
+ VGA_WR08(par->riva.PCIO, 0x3d4, chan->ddc_base + 1);
+ VGA_WR08(par->riva.PCIO, 0x3d5, val | 0x1);
+}
+
+static int riva_gpio_getscl(void* data)
+{
+ struct riva_i2c_chan *chan = (struct riva_i2c_chan *)data;
+ struct riva_par *par = chan->par;
+ u32 val = 0;
+
+ VGA_WR08(par->riva.PCIO, 0x3d4, chan->ddc_base);
+ if (VGA_RD08(par->riva.PCIO, 0x3d5) & 0x04)
+ val = 1;
+
+ val = VGA_RD08(par->riva.PCIO, 0x3d5);
+
+ return val;
+}
+
+static int riva_gpio_getsda(void* data)
+{
+ struct riva_i2c_chan *chan = (struct riva_i2c_chan *)data;
+ struct riva_par *par = chan->par;
+ u32 val = 0;
+
+ VGA_WR08(par->riva.PCIO, 0x3d4, chan->ddc_base);
+ if (VGA_RD08(par->riva.PCIO, 0x3d5) & 0x08)
+ val = 1;
+
+ return val;
+}
+
+#define I2C_ALGO_RIVA 0x0e0000
+static int riva_setup_i2c_bus(struct riva_i2c_chan *chan, const char *name)
+{
+ int rc;
+
+ strcpy(chan->adapter.name, name);
+ chan->adapter.owner = THIS_MODULE;
+ chan->adapter.id = I2C_ALGO_RIVA;
+ chan->adapter.algo_data = &chan->algo;
+ chan->adapter.dev.parent = &chan->par->pdev->dev;
+ chan->algo.setsda = riva_gpio_setsda;
+ chan->algo.setscl = riva_gpio_setscl;
+ chan->algo.getsda = riva_gpio_getsda;
+ chan->algo.getscl = riva_gpio_getscl;
+ chan->algo.udelay = 40;
+ chan->algo.timeout = 20;
+ chan->algo.data = chan;
+
+ i2c_set_adapdata(&chan->adapter, chan);
+
+ /* Raise SCL and SDA */
+ riva_gpio_setsda(chan, 1);
+ riva_gpio_setscl(chan, 1);
+ udelay(20);
+
+ rc = i2c_bit_add_bus(&chan->adapter);
+ if (rc == 0)
+ dev_dbg(&chan->par->pdev->dev, "I2C bus %s registered.\n", name);
+ else
+ dev_warn(&chan->par->pdev->dev, "Failed to register I2C bus %s.\n", name);
+ return rc;
+}
+
+void riva_create_i2c_busses(struct riva_par *par)
+{
+ par->chan[0].par = par;
+ par->chan[1].par = par;
+ par->chan[2].par = par;
+
+ switch (par->riva.Architecture) {
+#if 0 /* no support yet for other nVidia chipsets */
+ par->chan[2].ddc_base = 0x50;
+ riva_setup_i2c_bus(&par->chan[2], "BUS2");
+#endif
+ case NV_ARCH_10:
+ case NV_ARCH_20:
+ case NV_ARCH_04:
+ par->chan[1].ddc_base = 0x36;
+ riva_setup_i2c_bus(&par->chan[1], "BUS1");
+ case NV_ARCH_03:
+ par->chan[0].ddc_base = 0x3e;
+ riva_setup_i2c_bus(&par->chan[0], "BUS0");
+ }
+}
+
+void riva_delete_i2c_busses(struct riva_par *par)
+{
+ if (par->chan[0].par)
+ i2c_bit_del_bus(&par->chan[0].adapter);
+ par->chan[0].par = NULL;
+
+ if (par->chan[1].par)
+ i2c_bit_del_bus(&par->chan[1].adapter);
+ par->chan[1].par = NULL;
+
+}
+
+static u8 *riva_do_probe_i2c_edid(struct riva_i2c_chan *chan)
+{
+ u8 start = 0x0;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = RIVA_DDC,
+ .len = 1,
+ .buf = &start,
+ }, {
+ .addr = RIVA_DDC,
+ .flags = I2C_M_RD,
+ .len = EDID_LENGTH,
+ },
+ };
+ u8 *buf;
+
+ buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
+ if (!buf) {
+ dev_warn(&chan->par->pdev->dev, "Out of memory!\n");
+ return NULL;
+ }
+ msgs[1].buf = buf;
+
+ if (i2c_transfer(&chan->adapter, msgs, 2) == 2)
+ return buf;
+ dev_dbg(&chan->par->pdev->dev, "Unable to read EDID block.\n");
+ kfree(buf);
+ return NULL;
+}
+
+int riva_probe_i2c_connector(struct riva_par *par, int conn, u8 **out_edid)
+{
+ u8 *edid = NULL;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ /* Do the real work */
+ edid = riva_do_probe_i2c_edid(&par->chan[conn-1]);
+ if (edid)
+ break;
+ }
+ if (out_edid)
+ *out_edid = edid;
+ if (!edid)
+ return 1;
+
+ return 0;
+}
+
{
switch(cmd) {
case FBIOGTYPE: {
- struct fbtype *f = (struct fbtype *) arg;
+ struct fbtype __user *f = (struct fbtype __user *) arg;
if (put_user(type, &f->fb_type) ||
__put_user(info->var.yres, &f->fb_height) ||
return 0;
}
case FBIOPUTCMAP_SPARC: {
- struct fbcmap *c = (struct fbcmap *) arg;
+ struct fbcmap __user *c = (struct fbcmap __user *) arg;
struct fb_cmap cmap;
u16 red, green, blue;
- unsigned char *ured, *ugreen, *ublue;
+ unsigned char __user *ured;
+ unsigned char __user *ugreen;
+ unsigned char __user *ublue;
int index, count, i;
if (get_user(index, &c->index) ||
return -EFAULT;
cmap.start = index + i;
- err = fb_set_cmap(&cmap, 0, info);
+ err = fb_set_cmap(&cmap, info);
if (err)
return err;
}
return 0;
}
case FBIOGETCMAP_SPARC: {
- struct fbcmap *c = (struct fbcmap *) arg;
- unsigned char *ured, *ugreen, *ublue;
+ struct fbcmap __user *c = (struct fbcmap __user *) arg;
+ unsigned char __user *ured;
+ unsigned char __user *ugreen;
+ unsigned char __user *ublue;
struct fb_cmap *cmap = &info->cmap;
int index, count, i;
static int valkyriefb_blank(int blank_mode, struct fb_info *info);
static int read_valkyrie_sense(struct fb_info_valkyrie *p);
-static inline int valkyrie_vram_reqd(int video_mode, int color_mode);
static void set_valkyrie_clock(unsigned char *params);
-static inline int valkyrie_par_to_var(struct fb_par_valkyrie *par, struct fb_var_screeninfo *var);
static int valkyrie_var_to_par(struct fb_var_screeninfo *var,
struct fb_par_valkyrie *par, const struct fb_info *fb_info);
return 0;
}
+static inline int valkyrie_par_to_var(struct fb_par_valkyrie *par,
+ struct fb_var_screeninfo *var)
+{
+ return mac_vmode_to_var(par->vmode, par->cmode, var);
+}
+
static int
valkyriefb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
return 0;
}
-static int valkyrie_vram_reqd(int video_mode, int color_mode)
+static inline int valkyrie_vram_reqd(int video_mode, int color_mode)
{
int pitch;
struct valkyrie_regvals *init = valkyrie_reg_init[video_mode-1];
return 0;
}
-static int valkyrie_par_to_var(struct fb_par_valkyrie *par, struct fb_var_screeninfo *var)
-{
- return mac_vmode_to_var(par->vmode, par->cmode, var);
-}
-
static void valkyrie_init_fix(struct fb_fix_screeninfo *fix, struct fb_info_valkyrie *p)
{
memset(fix, 0, sizeof(*fix));
config W1_MATROX
tristate "Matrox G400 transport layer for 1-wire"
- depends on W1
+ depends on W1 && PCI
help
Say Y here if you want to communicate with your 1-wire devices
using Matrox's G400 GPIO pins.
--- /dev/null
+#
+# Makefile for the Dallas's 1-wire bus.
+#
+
+obj-$(CONFIG_W1) += wire.o
+wire-objs := w1.o w1_int.o w1_family.o w1_netlink.o w1_io.o
+
+obj-$(CONFIG_W1_MATROX) += matrox_w1.o
+obj-$(CONFIG_W1_THERM) += w1_therm.o
#include <asm/atomic.h>
#include <asm/types.h>
#include <asm/io.h>
-#include <asm/delay.h>
+#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/list.h>
*/
#include <asm/atomic.h>
-#include <asm/delay.h>
+#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
--- /dev/null
+/*
+ * w1.h
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __W1_H
+#define __W1_H
+
+struct w1_reg_num
+{
+ __u64 family:8,
+ id:48,
+ crc:8;
+};
+
+#ifdef __KERNEL__
+
+#include <linux/completion.h>
+#include <linux/device.h>
+
+#include <net/sock.h>
+
+#include <asm/semaphore.h>
+
+#include "w1_family.h"
+
+#define W1_MAXNAMELEN 32
+#define W1_SLAVE_DATA_SIZE 128
+
+#define W1_SEARCH 0xF0
+#define W1_CONDITIONAL_SEARCH 0xEC
+#define W1_CONVERT_TEMP 0x44
+#define W1_SKIP_ROM 0xCC
+#define W1_READ_SCRATCHPAD 0xBE
+#define W1_READ_ROM 0x33
+#define W1_READ_PSUPPLY 0xB4
+#define W1_MATCH_ROM 0x55
+
+struct w1_slave
+{
+ struct module *owner;
+ unsigned char name[W1_MAXNAMELEN];
+ struct list_head w1_slave_entry;
+ struct w1_reg_num reg_num;
+ atomic_t refcnt;
+ u8 rom[9];
+
+ struct w1_master *master;
+ struct w1_family *family;
+ struct device dev;
+ struct completion dev_released;
+};
+
+struct w1_bus_master
+{
+ unsigned long data;
+
+ u8 (*read_bit)(unsigned long);
+ void (*write_bit)(unsigned long, u8);
+};
+
+struct w1_master
+{
+ struct list_head w1_master_entry;
+ struct module *owner;
+ unsigned char name[W1_MAXNAMELEN];
+ struct list_head slist;
+ int max_slave_count, slave_count;
+ unsigned long attempts;
+ int initialized;
+ u32 id;
+
+ atomic_t refcnt;
+
+ void *priv;
+ int priv_size;
+
+ int need_exit;
+ pid_t kpid;
+ wait_queue_head_t kwait;
+ struct semaphore mutex;
+
+ struct device_driver *driver;
+ struct device dev;
+ struct completion dev_released;
+ struct completion dev_exited;
+
+ struct w1_bus_master *bus_master;
+
+ u32 seq, groups;
+ struct sock *nls;
+};
+
+#endif /* __KERNEL__ */
+
+#endif /* __W1_H */
--- /dev/null
+/*
+ * w1_family.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/spinlock.h>
+#include <linux/list.h>
+
+#include "w1_family.h"
+
+spinlock_t w1_flock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(w1_families);
+
+int w1_register_family(struct w1_family *newf)
+{
+ struct list_head *ent, *n;
+ struct w1_family *f;
+ int ret = 0;
+
+ spin_lock(&w1_flock);
+ list_for_each_safe(ent, n, &w1_families) {
+ f = list_entry(ent, struct w1_family, family_entry);
+
+ if (f->fid == newf->fid) {
+ ret = -EEXIST;
+ break;
+ }
+ }
+
+ if (!ret) {
+ atomic_set(&newf->refcnt, 0);
+ newf->need_exit = 0;
+ list_add_tail(&newf->family_entry, &w1_families);
+ }
+
+ spin_unlock(&w1_flock);
+
+ return ret;
+}
+
+void w1_unregister_family(struct w1_family *fent)
+{
+ struct list_head *ent, *n;
+ struct w1_family *f;
+
+ spin_lock(&w1_flock);
+ list_for_each_safe(ent, n, &w1_families) {
+ f = list_entry(ent, struct w1_family, family_entry);
+
+ if (f->fid == fent->fid) {
+ list_del(&fent->family_entry);
+ break;
+ }
+ }
+
+ fent->need_exit = 1;
+
+ spin_unlock(&w1_flock);
+
+ while (atomic_read(&fent->refcnt))
+ schedule_timeout(10);
+}
+
+/*
+ * Should be called under w1_flock held.
+ */
+struct w1_family * w1_family_registered(u8 fid)
+{
+ struct list_head *ent, *n;
+ struct w1_family *f = NULL;
+ int ret = 0;
+
+ list_for_each_safe(ent, n, &w1_families) {
+ f = list_entry(ent, struct w1_family, family_entry);
+
+ if (f->fid == fid) {
+ ret = 1;
+ break;
+ }
+ }
+
+ return (ret) ? f : NULL;
+}
+
+void w1_family_put(struct w1_family *f)
+{
+ spin_lock(&w1_flock);
+ __w1_family_put(f);
+ spin_unlock(&w1_flock);
+}
+
+void __w1_family_put(struct w1_family *f)
+{
+ if (atomic_dec_and_test(&f->refcnt))
+ f->need_exit = 1;
+}
+
+void w1_family_get(struct w1_family *f)
+{
+ spin_lock(&w1_flock);
+ __w1_family_get(f);
+ spin_unlock(&w1_flock);
+
+}
+
+void __w1_family_get(struct w1_family *f)
+{
+ atomic_inc(&f->refcnt);
+}
+
+EXPORT_SYMBOL(w1_family_get);
+EXPORT_SYMBOL(w1_family_put);
+EXPORT_SYMBOL(__w1_family_get);
+EXPORT_SYMBOL(__w1_family_put);
+EXPORT_SYMBOL(w1_family_registered);
+EXPORT_SYMBOL(w1_unregister_family);
+EXPORT_SYMBOL(w1_register_family);
--- /dev/null
+/*
+ * w1_family.h
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __W1_FAMILY_H
+#define __W1_FAMILY_H
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <asm/atomic.h>
+
+#define W1_FAMILY_DEFAULT 0
+#define W1_FAMILY_THERM 0x10
+#define W1_FAMILY_IBUT 0xff /* ? */
+
+#define MAXNAMELEN 32
+
+struct w1_family_ops
+{
+ ssize_t (* rname)(struct device *, char *);
+ ssize_t (* rbin)(struct kobject *, char *, loff_t, size_t);
+
+ ssize_t (* rval)(struct device *, char *);
+ unsigned char rvalname[MAXNAMELEN];
+};
+
+struct w1_family
+{
+ struct list_head family_entry;
+ u8 fid;
+
+ struct w1_family_ops *fops;
+
+ atomic_t refcnt;
+ u8 need_exit;
+};
+
+extern spinlock_t w1_flock;
+
+void w1_family_get(struct w1_family *);
+void w1_family_put(struct w1_family *);
+void __w1_family_get(struct w1_family *);
+void __w1_family_put(struct w1_family *);
+struct w1_family * w1_family_registered(u8);
+void w1_unregister_family(struct w1_family *);
+int w1_register_family(struct w1_family *);
+
+#endif /* __W1_FAMILY_H */
dev = kmalloc(sizeof(struct w1_master) + sizeof(struct w1_bus_master), GFP_KERNEL);
if (!dev) {
printk(KERN_ERR
- "Failed to allocate %d bytes for new w1 device.\n",
+ "Failed to allocate %zd bytes for new w1 device.\n",
sizeof(struct w1_master));
return NULL;
}
*/
#include <asm/io.h>
-#include <asm/delay.h>
+#include <linux/delay.h>
#include <linux/moduleparam.h>
#include "w1.h"
--- /dev/null
+/*
+ * w1_io.h
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __W1_IO_H
+#define __W1_IO_H
+
+#include "w1.h"
+
+void w1_delay(unsigned long);
+void w1_write_bit(struct w1_master *, int);
+void w1_write_8(struct w1_master *, u8);
+u8 w1_read_bit(struct w1_master *);
+u8 w1_read_8(struct w1_master *);
+int w1_reset_bus(struct w1_master *);
+u8 w1_calc_crc8(u8 *, int);
+
+#endif /* __W1_IO_H */
--- /dev/null
+/*
+ * w1_netlink.c
+ *
+ * Copyright (c) 2003 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+
+#include "w1.h"
+#include "w1_log.h"
+#include "w1_netlink.h"
+
+void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg)
+{
+ unsigned int size;
+ struct sk_buff *skb;
+ struct w1_netlink_msg *data;
+ struct nlmsghdr *nlh;
+
+ size = NLMSG_SPACE(sizeof(struct w1_netlink_msg));
+
+ skb = alloc_skb(size, GFP_ATOMIC);
+ if (!skb) {
+ dev_err(&dev->dev, "skb_alloc() failed.\n");
+ return;
+ }
+
+ nlh = NLMSG_PUT(skb, 0, dev->seq++, NLMSG_DONE, size - sizeof(*nlh));
+
+ data = (struct w1_netlink_msg *)NLMSG_DATA(nlh);
+
+ memcpy(data, msg, sizeof(struct w1_netlink_msg));
+
+ NETLINK_CB(skb).dst_groups = dev->groups;
+ netlink_broadcast(dev->nls, skb, 0, dev->groups, GFP_ATOMIC);
+
+nlmsg_failure:
+ return;
+}
--- /dev/null
+/*
+ * w1_netlink.h
+ *
+ * Copyright (c) 2003 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __W1_NETLINK_H
+#define __W1_NETLINK_H
+
+#include <asm/types.h>
+
+#include "w1.h"
+
+struct w1_netlink_msg
+{
+ union
+ {
+ struct w1_reg_num id;
+ __u64 w1_id;
+ } id;
+ __u64 val;
+};
+
+#ifdef __KERNEL__
+
+void w1_netlink_send(struct w1_master *, struct w1_netlink_msg *);
+
+#endif /* __KERNEL__ */
+#endif /* __W1_NETLINK_H */
--- /dev/null
+/*
+ * w1_therm.c
+ *
+ * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the therms 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 <asm/types.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+
+#include "w1.h"
+#include "w1_io.h"
+#include "w1_int.h"
+#include "w1_family.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
+MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, temperature family.");
+
+static ssize_t w1_therm_read_name(struct device *, char *);
+static ssize_t w1_therm_read_temp(struct device *, char *);
+static ssize_t w1_therm_read_bin(struct kobject *, char *, loff_t, size_t);
+
+static struct w1_family_ops w1_therm_fops = {
+ .rname = &w1_therm_read_name,
+ .rbin = &w1_therm_read_bin,
+ .rval = &w1_therm_read_temp,
+ .rvalname = "temp1_input",
+};
+
+static ssize_t w1_therm_read_name(struct device *dev, char *buf)
+{
+ struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+
+ return sprintf(buf, "%s\n", sl->name);
+}
+
+static ssize_t w1_therm_read_temp(struct device *dev, char *buf)
+{
+ struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+ s16 temp;
+
+ /*
+ * Must be more precise.
+ */
+ temp = 0;
+ temp <<= sl->rom[1] / 2;
+ temp |= sl->rom[0] / 2;
+
+ return sprintf(buf, "%d\n", temp * 1000);
+}
+
+static ssize_t w1_therm_read_bin(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+ struct w1_slave *sl = container_of(container_of(kobj, struct device, kobj),
+ struct w1_slave, dev);
+ struct w1_master *dev = sl->master;
+ u8 rom[9], crc, verdict;
+ size_t icount;
+ int i;
+ u16 temp;
+
+ atomic_inc(&sl->refcnt);
+ if (down_interruptible(&sl->master->mutex)) {
+ count = 0;
+ goto out_dec;
+ }
+
+ if (off > W1_SLAVE_DATA_SIZE) {
+ count = 0;
+ goto out;
+ }
+ if (off + count > W1_SLAVE_DATA_SIZE)
+ count = W1_SLAVE_DATA_SIZE - off;
+
+ icount = count;
+
+ memset(buf, 0, count);
+ memset(rom, 0, sizeof(rom));
+
+ count = 0;
+ verdict = 0;
+ crc = 0;
+ if (!w1_reset_bus(dev)) {
+ u64 id = *(u64 *) & sl->reg_num;
+ int count = 0;
+
+ w1_write_8(dev, W1_MATCH_ROM);
+ for (i = 0; i < 8; ++i)
+ w1_write_8(dev, (id >> i * 8) & 0xff);
+
+ w1_write_8(dev, W1_CONVERT_TEMP);
+
+ while (dev->bus_master->read_bit(dev->bus_master->data) == 0
+ && count < 10) {
+ w1_delay(1);
+ count++;
+ }
+
+ if (count < 10) {
+ if (!w1_reset_bus(dev)) {
+ w1_write_8(dev, W1_MATCH_ROM);
+ for (i = 0; i < 8; ++i)
+ w1_write_8(dev,
+ (id >> i * 8) & 0xff);
+
+ w1_write_8(dev, W1_READ_SCRATCHPAD);
+ for (i = 0; i < 9; ++i)
+ rom[i] = w1_read_8(dev);
+
+ crc = w1_calc_crc8(rom, 8);
+
+ if (rom[8] == crc && rom[0])
+ verdict = 1;
+ }
+ }
+ else
+ dev_warn(&dev->dev,
+ "18S20 doesn't respond to CONVERT_TEMP.\n");
+ }
+
+ for (i = 0; i < 9; ++i)
+ count += snprintf(buf + count, icount - count, "%02x ", rom[i]);
+ count += snprintf(buf + count, icount - count, ": crc=%02x %s\n",
+ crc, (verdict) ? "YES" : "NO");
+ if (verdict)
+ memcpy(sl->rom, rom, sizeof(sl->rom));
+ for (i = 0; i < 9; ++i)
+ count += snprintf(buf + count, icount - count, "%02x ", sl->rom[i]);
+ temp = 0;
+ temp <<= sl->rom[1] / 2;
+ temp |= sl->rom[0] / 2;
+ count += snprintf(buf + count, icount - count, "t=%u\n", temp);
+out:
+ up(&dev->mutex);
+out_dec:
+ atomic_dec(&sl->refcnt);
+
+ return count;
+}
+
+static struct w1_family w1_therm_family = {
+ .fid = W1_FAMILY_THERM,
+ .fops = &w1_therm_fops,
+};
+
+static int __init w1_therm_init(void)
+{
+ return w1_register_family(&w1_therm_family);
+}
+
+static void __exit w1_therm_fini(void)
+{
+ w1_unregister_family(&w1_therm_family);
+}
+
+module_init(w1_therm_init);
+module_exit(w1_therm_fini);
#ifndef _CIFS_FS_SB_H
#define _CIFS_FS_SB_H
+#define CIFS_MOUNT_NO_PERM 1 /* do not do client vfs_perm check */
+#define CIFS_MOUNT_SET_UID 2 /* set current->euid in create etc. */
+
struct cifs_sb_info {
struct cifsTconInfo *tcon; /* primary mount */
struct list_head nested_tcon_q;
gid_t mnt_gid;
mode_t mnt_file_mode;
mode_t mnt_dir_mode;
+ int mnt_cifs_flags;
};
#endif /* _CIFS_FS_SB_H */
void NTLMSSPOWFencrypt(unsigned char passwd[8],
unsigned char *ntlmchalresp, unsigned char p24[24]);
void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24);
-int decode_pw_buffer(char in_buffer[516], char *new_pwrd,
- int new_pwrd_size, __u32 * new_pw_len);
/*
This implements the X/Open SMB password encryption
/*
* fs/cifs/smberr.h
*
- * Copyright (c) International Business Machines Corp., 2002
+ * Copyright (c) International Business Machines Corp., 2002,2004
* Author(s): Steve French (sfrench@us.ibm.com)
*
* See Error Codes section of the SNIA CIFS Specification
#define ERRinvparm 87
#define ERRdiskfull 112
#define ERRinvname 123
+#define ERRinvlevel 124
#define ERRdirnotempty 145
#define ERRnotlocked 158
#define ERRalreadyexists 183
#include "cifsproto.h"
#include "cifs_debug.h"
-int cifs_removexattr(struct dentry * direntry, const char * name)
+#define MAX_EA_VALUE_SIZE 65535
+#define CIFS_XATTR_DOS_ATTRIB "user.DOSATTRIB"
+#define CIFS_XATTR_USER_PREFIX "user."
+#define CIFS_XATTR_SYSTEM_PREFIX "system."
+#define CIFS_XATTR_OS2_PREFIX "OS2." /* BB should check for this someday */
+/* also note could add check for security prefix XATTR_SECURITY_PREFIX */
+
+
+int cifs_removexattr(struct dentry * direntry, const char * ea_name)
{
int rc = -EOPNOTSUPP;
+#ifdef CONFIG_CIFS_XATTR
+ int xid;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ struct super_block * sb;
+ char * full_path;
+
+ if(direntry == NULL)
+ return -EIO;
+ if(direntry->d_inode == NULL)
+ return -EIO;
+ sb = direntry->d_inode->i_sb;
+ if(sb == NULL)
+ return -EIO;
+ xid = GetXid();
+
+ cifs_sb = CIFS_SB(sb);
+ pTcon = cifs_sb->tcon;
+
+ down(&sb->s_vfs_rename_sem);
+ full_path = build_path_from_dentry(direntry);
+ up(&sb->s_vfs_rename_sem);
+ if(full_path == NULL) {
+ FreeXid(xid);
+ return -ENOMEM;
+ }
+ if(ea_name == NULL) {
+ cFYI(1,("Null xattr names not supported"));
+ } else if(strncmp(ea_name,CIFS_XATTR_USER_PREFIX,5)) {
+ cFYI(1,("illegal xattr namespace %s (only user namespace supported)",ea_name));
+ /* BB what if no namespace prefix? */
+ /* Should we just pass them to server, except for
+ system and perhaps security prefixes? */
+ } else {
+ ea_name+=5; /* skip past user. prefix */
+ rc = CIFSSMBSetEA(xid,pTcon,full_path,ea_name,NULL,
+ (__u16)0, cifs_sb->local_nls);
+ }
+ if (full_path)
+ kfree(full_path);
+ FreeXid(xid);
+#endif
return rc;
}
-int cifs_setxattr(struct dentry * direntry, const char * name,
- const void * value, size_t size, int flags)
+int cifs_setxattr(struct dentry * direntry, const char * ea_name,
+ const void * ea_value, size_t value_size, int flags)
{
int rc = -EOPNOTSUPP;
+#ifdef CONFIG_CIFS_XATTR
+ int xid;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ struct super_block * sb;
+ char * full_path;
+
+ if(direntry == NULL)
+ return -EIO;
+ if(direntry->d_inode == NULL)
+ return -EIO;
+ sb = direntry->d_inode->i_sb;
+ if(sb == NULL)
+ return -EIO;
+ xid = GetXid();
+
+ cifs_sb = CIFS_SB(sb);
+ pTcon = cifs_sb->tcon;
+
+ down(&sb->s_vfs_rename_sem);
+ full_path = build_path_from_dentry(direntry);
+ up(&sb->s_vfs_rename_sem);
+ if(full_path == NULL) {
+ FreeXid(xid);
+ return -ENOMEM;
+ }
+ /* return dos attributes as pseudo xattr */
+ /* return alt name if available as pseudo attr */
+
+ /* if proc/fs/cifs/streamstoxattr is set then
+ search server for EAs or streams to
+ returns as xattrs */
+ if(value_size > MAX_EA_VALUE_SIZE) {
+ cFYI(1,("size of EA value too large"));
+ if(full_path)
+ kfree(full_path);
+ FreeXid(xid);
+ return -EOPNOTSUPP;
+ }
+
+ if(ea_name == NULL) {
+ cFYI(1,("Null xattr names not supported"));
+ } else if(strncmp(ea_name,CIFS_XATTR_USER_PREFIX,5)) {
+ cFYI(1,("illegal xattr namespace %s (only user namespace supported)",ea_name));
+ /* BB what if no namespace prefix? */
+ /* Should we just pass them to server, except for
+ system and perhaps security prefixes? */
+ } else {
+ ea_name+=5; /* skip past user. prefix */
+ rc = CIFSSMBSetEA(xid,pTcon,full_path,ea_name,ea_value,
+ (__u16)value_size, cifs_sb->local_nls);
+ }
+ if (full_path)
+ kfree(full_path);
+ FreeXid(xid);
+#endif
return rc;
}
-ssize_t cifs_getxattr(struct dentry * direntry, const char * name,
- void * value, size_t size)
+ssize_t cifs_getxattr(struct dentry * direntry, const char * ea_name,
+ void * ea_value, size_t buf_size)
{
ssize_t rc = -EOPNOTSUPP;
+#ifdef CONFIG_CIFS_XATTR
+ int xid;
+ struct cifs_sb_info *cifs_sb;
+ struct cifsTconInfo *pTcon;
+ struct super_block * sb;
+ char * full_path;
+
+ if(direntry == NULL)
+ return -EIO;
+ if(direntry->d_inode == NULL)
+ return -EIO;
+ sb = direntry->d_inode->i_sb;
+ if(sb == NULL)
+ return -EIO;
+ xid = GetXid();
+
+ cifs_sb = CIFS_SB(sb);
+ pTcon = cifs_sb->tcon;
+
+ down(&sb->s_vfs_rename_sem);
+ full_path = build_path_from_dentry(direntry);
+ up(&sb->s_vfs_rename_sem);
+ if(full_path == NULL) {
+ FreeXid(xid);
+ return -ENOMEM;
+ }
+ /* return dos attributes as pseudo xattr */
+ /* return alt name if available as pseudo attr */
+ if(strncmp(ea_name,CIFS_XATTR_USER_PREFIX,5)) {
+ cFYI(1,("illegal xattr namespace %s (only user namespace supported)",ea_name));
+ /* BB what if no namespace prefix? */
+ /* Should we just pass them to server, except for system? */
+ } else {
+ /* We could add a check here
+ if proc/fs/cifs/streamstoxattr is set then
+ search server for EAs or streams to
+ returns as xattrs */
+ ea_name+=5; /* skip past user. */
+ rc = CIFSSMBQueryEA(xid,pTcon,full_path,ea_name,ea_value,
+ buf_size, cifs_sb->local_nls);
+ }
+ if (full_path)
+ kfree(full_path);
+ FreeXid(xid);
+#endif
return rc;
}
-ssize_t cifs_listxattr(struct dentry * direntry, char * ea_data, size_t ea_size)
+ssize_t cifs_listxattr(struct dentry * direntry, char * data, size_t buf_size)
{
ssize_t rc = -EOPNOTSUPP;
#ifdef CONFIG_CIFS_XATTR
struct cifsTconInfo *pTcon;
struct super_block * sb;
char * full_path;
+
if(direntry == NULL)
return -EIO;
if(direntry->d_inode == NULL)
FreeXid(xid);
return -ENOMEM;
}
- /* return dosattributes as pseudo xattr */
+ /* return dos attributes as pseudo xattr */
/* return alt name if available as pseudo attr */
/* if proc/fs/cifs/streamstoxattr is set then
search server for EAs or streams to
returns as xattrs */
- rc = CIFSSMBQAllEAs(xid,pTcon,full_path,ea_data,ea_size,cifs_sb->local_nls);
+ rc = CIFSSMBQAllEAs(xid,pTcon,full_path,data,buf_size,
+ cifs_sb->local_nls);
+
+ if (full_path)
+ kfree(full_path);
FreeXid(xid);
#endif
return rc;
unsigned chunk_mask = ~(ext2_chunk_size(inode)-1);
unsigned char *types = NULL;
int need_revalidate = (filp->f_version != inode->i_version);
- int ret = 0;
+ int ret;
if (pos > inode->i_size - EXT2_DIR_REC_LEN(1))
- goto done;
+ goto success;
if (EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE))
types = ext2_filetype_table;
le32_to_cpu(de->inode), d_type);
if (over) {
ext2_put_page(page);
- goto done;
+ goto success;
}
}
}
ext2_put_page(page);
}
+success:
+ ret = 0;
done:
filp->f_pos = (n << PAGE_CACHE_SHIFT) | offset;
filp->f_version = inode->i_version;
- return 0;
+ return ret;
}
/*
}
filp->f_version = 0;
+ /* We can only do regular read/write on fifos */
+ filp->f_mode &= (FMODE_READ | FMODE_WRITE);
+
switch (filp->f_mode) {
case 1:
/*
struct hfs_btree_header_rec *head;
struct address_space *mapping;
struct page *page;
- unsigned int shift, size;
+ unsigned int size;
tree = kmalloc(sizeof(*tree), GFP_KERNEL);
if (!tree)
goto fail_page;
if (!tree->node_count)
goto fail_page;
- for (shift = 0; size >>= 1; shift += 1)
- ;
- tree->node_size_shift = shift;
-
+ tree->node_size_shift = ffs(size) - 1;
tree->pages_per_bnode = (tree->node_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
kunmap(page);
struct hfs_btree_header_rec *head;
struct address_space *mapping;
struct page *page;
- unsigned int shift, size;
+ unsigned int size;
tree = kmalloc(sizeof(*tree), GFP_KERNEL);
if (!tree)
goto fail_page;
if (!tree->node_count)
goto fail_page;
- for (shift = 0; size >>= 1; shift += 1)
- ;
- tree->node_size_shift = shift;
+ tree->node_size_shift = ffs(size) - 1;
tree->pages_per_bnode = (tree->node_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
int jffs_register_jffs_proc_dir(int mtd, struct jffs_control *c)
{
struct jffs_partition_dir *part_dir;
- struct proc_dir_entry *part_info = 0;
- struct proc_dir_entry *part_layout = 0;
- struct proc_dir_entry *part_root = 0;
+ struct proc_dir_entry *part_info = NULL;
+ struct proc_dir_entry *part_layout = NULL;
+ struct proc_dir_entry *part_root = NULL;
char name[10];
sprintf(name, "%d", mtd);
int jffs_unregister_jffs_proc_dir(struct jffs_control *c)
{
struct jffs_partition_dir *part_dir = jffs_part_dirs;
- struct jffs_partition_dir *prev_part_dir = 0;
+ struct jffs_partition_dir *prev_part_dir = NULL;
while (part_dir) {
if (part_dir->c == c) {
int count, int *eof, void *data)
{
struct jffs_control *c = (struct jffs_control *) data;
- struct jffs_fm *fm = 0;
- struct jffs_fm *last_fm = 0;
+ struct jffs_fm *fm = NULL;
+ struct jffs_fm *last_fm = NULL;
int len = 0;
/* Get the first item in the list */
* For licensing information, see the file 'LICENCE' in the
* jffs2 directory.
*
- * $Id: compr.h,v 1.5 2004/06/23 16:34:39 havasi Exp $
+ * $Id: compr.h,v 1.6 2004/07/16 15:17:57 dwmw2 Exp $
*
*/
void jffs2_lzo_exit(void);
#endif
-/* Prototypes from proc.c */
-int jffs2_proc_init(void);
-int jffs2_proc_exit(void);
-
#endif /* __JFFS2_COMPR_H__ */
#include <linux/pagemap.h>
#include <linux/mount.h>
#include <linux/vfs.h>
+#include <asm/uaccess.h>
int simple_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat)
mntput(mnt);
}
+ssize_t simple_read_from_buffer(void __user *to, size_t count, loff_t *ppos,
+ const void *from, size_t available)
+{
+ loff_t pos = *ppos;
+ if (pos < 0)
+ return -EINVAL;
+ if (pos >= available)
+ return 0;
+ if (count > available - pos)
+ count = available - pos;
+ if (copy_to_user(to, from + pos, count))
+ return -EFAULT;
+ *ppos = pos + count;
+ return count;
+}
+
EXPORT_SYMBOL(dcache_dir_close);
EXPORT_SYMBOL(dcache_dir_lseek);
EXPORT_SYMBOL(dcache_dir_open);
EXPORT_SYMBOL(simple_statfs);
EXPORT_SYMBOL(simple_sync_file);
EXPORT_SYMBOL(simple_unlink);
+EXPORT_SYMBOL(simple_read_from_buffer);
#include "ncpsign_kernel.h"
-static int _recv(struct socket *sock, unsigned char *ubuf, int size,
- unsigned flags)
+static int _recv(struct socket *sock, void *buf, int size, unsigned flags)
{
- struct iovec iov;
- struct msghdr msg;
-
- iov.iov_base = ubuf;
- iov.iov_len = size;
-
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_control = NULL;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
-
- return sock_recvmsg(sock, &msg, size, flags);
+ struct msghdr msg = {NULL, };
+ struct kvec iov = {buf, size};
+ return kernel_recvmsg(sock, &msg, &iov, 1, size, flags);
}
-static inline int _send(struct socket *sock, const void *buff, int len)
+static inline int do_send(struct socket *sock, struct kvec *vec, int count,
+ int len, unsigned flags)
{
- struct iovec iov;
- struct msghdr msg;
-
- iov.iov_base = (void *) buff;
- iov.iov_len = len;
-
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_control = NULL;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_flags = 0;
+ struct msghdr msg = { .msg_flags = flags };
+ return kernel_sendmsg(sock, &msg, vec, count, len);
+}
- return sock_sendmsg(sock, &msg, len);
+static int _send(struct socket *sock, const void *buff, int len)
+{
+ struct kvec vec;
+ vec.iov_base = (void *) buff;
+ vec.iov_len = len;
+ return do_send(sock, &vec, 1, len, 0);
}
struct ncp_request_reply {
size_t datalen;
int result;
enum { RQ_DONE, RQ_INPROGRESS, RQ_QUEUED, RQ_IDLE } status;
- struct iovec* tx_ciov;
+ struct kvec* tx_ciov;
size_t tx_totallen;
size_t tx_iovlen;
- struct iovec tx_iov[3];
+ struct kvec tx_iov[3];
u_int16_t tx_type;
u_int32_t sign[6];
};
-void ncp_tcp_data_ready(struct sock *sk, int len) {
+void ncp_tcp_data_ready(struct sock *sk, int len)
+{
struct ncp_server *server = sk->sk_user_data;
server->data_ready(sk, len);
schedule_work(&server->rcv.tq);
}
-void ncp_tcp_error_report(struct sock *sk) {
+void ncp_tcp_error_report(struct sock *sk)
+{
struct ncp_server *server = sk->sk_user_data;
server->error_report(sk);
schedule_work(&server->rcv.tq);
}
-void ncp_tcp_write_space(struct sock *sk) {
+void ncp_tcp_write_space(struct sock *sk)
+{
struct ncp_server *server = sk->sk_user_data;
/* We do not need any locking: we first set tx.creq, and then we do sendmsg,
not vice versa... */
server->write_space(sk);
- if (server->tx.creq) {
+ if (server->tx.creq)
schedule_work(&server->tx.tq);
- }
}
-void ncpdgram_timeout_call(unsigned long v) {
+void ncpdgram_timeout_call(unsigned long v)
+{
struct ncp_server *server = (void*)v;
schedule_work(&server->timeout_tq);
}
-static inline void ncp_finish_request(struct ncp_request_reply *req, int result) {
+static inline void ncp_finish_request(struct ncp_request_reply *req, int result)
+{
req->result = result;
req->status = RQ_DONE;
wake_up_all(&req->wq);
}
-static void __abort_ncp_connection(struct ncp_server *server, struct ncp_request_reply *aborted, int err) {
+static void __abort_ncp_connection(struct ncp_server *server, struct ncp_request_reply *aborted, int err)
+{
struct ncp_request_reply *req;
ncp_invalidate_conn(server);
}
}
-static inline int get_conn_number(struct ncp_reply_header *rp) {
+static inline int get_conn_number(struct ncp_reply_header *rp)
+{
return rp->conn_low | (rp->conn_high << 8);
}
-static inline void __ncp_abort_request(struct ncp_server *server, struct ncp_request_reply *req, int err) {
+static inline void __ncp_abort_request(struct ncp_server *server, struct ncp_request_reply *req, int err)
+{
/* If req is done, we got signal, but we also received answer... */
switch (req->status) {
case RQ_IDLE:
}
}
-static inline void ncp_abort_request(struct ncp_server *server, struct ncp_request_reply *req, int err) {
+static inline void ncp_abort_request(struct ncp_server *server, struct ncp_request_reply *req, int err)
+{
down(&server->rcv.creq_sem);
__ncp_abort_request(server, req, err);
up(&server->rcv.creq_sem);
}
-static inline void __ncptcp_abort(struct ncp_server *server) {
+static inline void __ncptcp_abort(struct ncp_server *server)
+{
__abort_ncp_connection(server, NULL, 0);
}
-static int ncpdgram_send(struct socket *sock, struct ncp_request_reply *req) {
- struct msghdr msg;
- struct iovec iov[3];
-
+static int ncpdgram_send(struct socket *sock, struct ncp_request_reply *req)
+{
+ struct kvec vec[3];
/* sock_sendmsg updates iov pointers for us :-( */
- memcpy(iov, req->tx_ciov, req->tx_iovlen * sizeof(iov[0]));
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_control = NULL;
- msg.msg_iov = iov;
- msg.msg_iovlen = req->tx_iovlen;
- msg.msg_flags = MSG_DONTWAIT;
- return sock_sendmsg(sock, &msg, req->tx_totallen);
+ memcpy(vec, req->tx_ciov, req->tx_iovlen * sizeof(vec[0]));
+ return do_send(sock, vec, req->tx_iovlen,
+ req->tx_totallen, MSG_DONTWAIT);
}
-static void __ncptcp_try_send(struct ncp_server *server) {
+static void __ncptcp_try_send(struct ncp_server *server)
+{
struct ncp_request_reply *rq;
- struct msghdr msg;
- struct iovec* iov;
- struct iovec iovc[3];
+ struct kvec *iov;
+ struct kvec iovc[3];
int result;
rq = server->tx.creq;
- if (!rq) {
+ if (!rq)
return;
- }
/* sock_sendmsg updates iov pointers for us :-( */
memcpy(iovc, rq->tx_ciov, rq->tx_iovlen * sizeof(iov[0]));
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_control = NULL;
- msg.msg_iov = iovc;
- msg.msg_iovlen = rq->tx_iovlen;
- msg.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT;
- result = sock_sendmsg(server->ncp_sock, &msg, rq->tx_totallen);
- if (result == -EAGAIN) {
+ result = do_send(server->ncp_sock, iovc, rq->tx_iovlen,
+ rq->tx_totallen, MSG_NOSIGNAL | MSG_DONTWAIT);
+
+ if (result == -EAGAIN)
return;
- }
+
if (result < 0) {
printk(KERN_ERR "ncpfs: tcp: Send failed: %d\n", result);
__ncp_abort_request(server, rq, result);
rq->tx_ciov = iov;
}
-static inline void ncp_init_header(struct ncp_server *server, struct ncp_request_reply *req, struct ncp_request_header *h) {
+static inline void ncp_init_header(struct ncp_server *server, struct ncp_request_reply *req, struct ncp_request_header *h)
+{
req->status = RQ_INPROGRESS;
h->conn_low = server->connection;
h->conn_high = server->connection >> 8;
h->sequence = ++server->sequence;
}
-static void ncpdgram_start_request(struct ncp_server *server, struct ncp_request_reply *req) {
+static void ncpdgram_start_request(struct ncp_server *server, struct ncp_request_reply *req)
+{
size_t signlen;
struct ncp_request_header* h;
#define NCP_TCP_XMIT_VERSION (1)
#define NCP_TCP_RCVD_MAGIC (0x744E6350)
-static void ncptcp_start_request(struct ncp_server *server, struct ncp_request_reply *req) {
+static void ncptcp_start_request(struct ncp_server *server, struct ncp_request_reply *req)
+{
size_t signlen;
struct ncp_request_header* h;
__ncptcp_try_send(server);
}
-static inline void __ncp_start_request(struct ncp_server *server, struct ncp_request_reply *req) {
+static inline void __ncp_start_request(struct ncp_server *server, struct ncp_request_reply *req)
+{
if (server->ncp_sock->type == SOCK_STREAM)
ncptcp_start_request(server, req);
else
ncpdgram_start_request(server, req);
}
-static int ncp_add_request(struct ncp_server *server, struct ncp_request_reply *req) {
+static int ncp_add_request(struct ncp_server *server, struct ncp_request_reply *req)
+{
down(&server->rcv.creq_sem);
if (!ncp_conn_valid(server)) {
up(&server->rcv.creq_sem);
return 0;
}
-static void __ncp_next_request(struct ncp_server *server) {
+static void __ncp_next_request(struct ncp_server *server)
+{
struct ncp_request_reply *req;
server->rcv.creq = NULL;
__ncp_start_request(server, req);
}
-static void info_server(struct ncp_server *server, unsigned int id, const void * data, size_t len) {
+static void info_server(struct ncp_server *server, unsigned int id, const void * data, size_t len)
+{
if (server->info_sock) {
- struct iovec iov[2];
- struct msghdr msg;
+ struct kvec iov[2];
__u32 hdr[2];
hdr[0] = cpu_to_be32(len + 8);
iov[1].iov_base = (void *) data;
iov[1].iov_len = len;
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_control = NULL;
- msg.msg_iov = iov;
- msg.msg_iovlen = 2;
- msg.msg_flags = MSG_NOSIGNAL;
-
- sock_sendmsg(server->info_sock, &msg, len + 8);
+ do_send(server->info_sock, iov, 2, len + 8, MSG_NOSIGNAL);
}
}
-static void __ncpdgram_rcv_proc(void *s) {
+void ncpdgram_rcv_proc(void *s)
+{
struct ncp_server *server = s;
struct socket* sock;
struct ncp_reply_header reply;
int result;
- result = _recv(sock, (void*)&reply, sizeof(reply), MSG_PEEK | MSG_DONTWAIT);
+ result = _recv(sock, &reply, sizeof(reply), MSG_PEEK | MSG_DONTWAIT);
if (result < 0) {
break;
}
up(&server->rcv.creq_sem);
}
drop:;
- _recv(sock, (void*)&reply, sizeof(reply), MSG_DONTWAIT);
+ _recv(sock, &reply, sizeof(reply), MSG_DONTWAIT);
}
}
-void ncpdgram_rcv_proc(void *s) {
- mm_segment_t fs;
- struct ncp_server *server = s;
-
- fs = get_fs();
- set_fs(get_ds());
- __ncpdgram_rcv_proc(server);
- set_fs(fs);
-}
-
-static void __ncpdgram_timeout_proc(struct ncp_server *server) {
+static void __ncpdgram_timeout_proc(struct ncp_server *server)
+{
/* If timer is pending, we are processing another request... */
if (!timer_pending(&server->timeout_tm)) {
struct ncp_request_reply* req;
}
}
-void ncpdgram_timeout_proc(void *s) {
- mm_segment_t fs;
+void ncpdgram_timeout_proc(void *s)
+{
struct ncp_server *server = s;
-
- fs = get_fs();
- set_fs(get_ds());
down(&server->rcv.creq_sem);
__ncpdgram_timeout_proc(server);
up(&server->rcv.creq_sem);
- set_fs(fs);
}
-static inline void ncp_init_req(struct ncp_request_reply* req) {
+static inline void ncp_init_req(struct ncp_request_reply* req)
+{
init_waitqueue_head(&req->wq);
req->status = RQ_IDLE;
}
-static int do_tcp_rcv(struct ncp_server *server, void *buffer, size_t len) {
+static int do_tcp_rcv(struct ncp_server *server, void *buffer, size_t len)
+{
int result;
if (buffer) {
return result;
}
-static int __ncptcp_rcv_proc(struct ncp_server *server) {
+static int __ncptcp_rcv_proc(struct ncp_server *server)
+{
/* We have to check the result, so store the complete header */
while (1) {
int result;
}
}
-void ncp_tcp_rcv_proc(void *s) {
- mm_segment_t fs;
+void ncp_tcp_rcv_proc(void *s)
+{
struct ncp_server *server = s;
- fs = get_fs();
- set_fs(get_ds());
down(&server->rcv.creq_sem);
__ncptcp_rcv_proc(server);
up(&server->rcv.creq_sem);
- set_fs(fs);
- return;
}
-void ncp_tcp_tx_proc(void *s) {
- mm_segment_t fs;
+void ncp_tcp_tx_proc(void *s)
+{
struct ncp_server *server = s;
- fs = get_fs();
- set_fs(get_ds());
down(&server->rcv.creq_sem);
__ncptcp_try_send(server);
up(&server->rcv.creq_sem);
- set_fs(fs);
- return;
}
static int do_ncp_rpc_call(struct ncp_server *server, int size,
ncp_init_req(&req);
req.reply_buf = reply_buf;
req.datalen = max_reply_size;
- req.tx_iov[1].iov_base = (void *) server->packet;
+ req.tx_iov[1].iov_base = server->packet;
req.tx_iov[1].iov_len = size;
req.tx_iovlen = 1;
req.tx_totallen = size;
return -EIO;
}
{
- mm_segment_t fs;
sigset_t old_set;
unsigned long mask, flags;
recalc_sigpending();
spin_unlock_irqrestore(¤t->sighand->siglock, flags);
- fs = get_fs();
- set_fs(get_ds());
-
result = do_ncp_rpc_call(server, size, reply, max_reply_size);
- set_fs(fs);
-
spin_lock_irqsave(¤t->sighand->siglock, flags);
current->blocked = old_set;
recalc_sigpending();
static struct svc_cacherep * nfscache;
static int cache_disabled = 1;
-static int nfsd_cache_append(struct svc_rqst *rqstp, struct iovec *vec);
+static int nfsd_cache_append(struct svc_rqst *rqstp, struct kvec *vec);
/*
* locking for the reply cache:
nfsd_cache_update(struct svc_rqst *rqstp, int cachetype, u32 *statp)
{
struct svc_cacherep *rp;
- struct iovec *resv = &rqstp->rq_res.head[0], *cachv;
+ struct kvec *resv = &rqstp->rq_res.head[0], *cachv;
int len;
if (!(rp = rqstp->rq_cacherep) || cache_disabled)
* keep a refcount....
*/
static int
-nfsd_cache_append(struct svc_rqst *rqstp, struct iovec *data)
+nfsd_cache_append(struct svc_rqst *rqstp, struct kvec *data)
{
- struct iovec *vec = &rqstp->rq_res.head[0];
+ struct kvec *vec = &rqstp->rq_res.head[0];
if (vec->iov_len + data->iov_len > PAGE_SIZE) {
printk(KERN_WARNING "nfsd: cached reply too large (%Zd).\n",
--- /dev/null
+/*
+ * linux/fs/nls_ascii.c
+ *
+ * Charset ascii translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[128] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+};
+
+static unsigned char page00[128] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+};
+
+static unsigned char *page_uni2charset[128] = {
+ page00, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+};
+
+static unsigned char charset2lower[128] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+};
+
+static unsigned char charset2upper[128] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ .charset = "ascii",
+ .uni2char = uni2char,
+ .char2uni = char2uni,
+ .charset2lower = charset2lower,
+ .charset2upper = charset2upper,
+ .owner = THIS_MODULE,
+};
+
+static int __init init_nls_ascii(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_ascii(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_ascii)
+module_exit(exit_nls_ascii)
+
+MODULE_LICENSE("Dual BSD/GPL");
--- /dev/null
+/*
+ * collate.h - Defines for NTFS kernel collation handling. Part of the
+ * Linux-NTFS project.
+ *
+ * Copyright (c) 2004 Anton Altaparmakov
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LINUX_NTFS_COLLATE_H
+#define _LINUX_NTFS_COLLATE_H
+
+#include "types.h"
+#include "volume.h"
+
+static inline BOOL ntfs_is_collation_rule_supported(COLLATION_RULES cr) {
+ /*
+ * FIXME: At the moment we only support COLLATION_BINARY and
+ * COLLATION_NTOFS_ULONG, so we return false for everything else for
+ * now.
+ */
+ if (unlikely(cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG))
+ return FALSE;
+ cr = le32_to_cpu(cr);
+ if (likely(((cr >= 0) && (cr <= 0x02)) ||
+ ((cr >= 0x10) && (cr <= 0x13))))
+ return TRUE;
+ return FALSE;
+}
+
+extern int ntfs_collate(ntfs_volume *vol, COLLATION_RULES cr,
+ const void *data1, const int data1_len,
+ const void *data2, const int data2_len);
+
+#endif /* _LINUX_NTFS_COLLATE_H */
--- /dev/null
+/*
+ * index.c - NTFS kernel index handling. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2004 Anton Altaparmakov
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ntfs.h"
+#include "collate.h"
+#include "index.h"
+
+/**
+ * ntfs_index_ctx_get - allocate and initialize a new index context
+ * @idx_ni: ntfs index inode with which to initialize the context
+ *
+ * Allocate a new index context, initialize it with @idx_ni and return it.
+ * Return NULL if allocation failed.
+ *
+ * Locking: Caller must hold i_sem on the index inode.
+ */
+ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *idx_ni)
+{
+ ntfs_index_context *ictx;
+
+ ictx = kmem_cache_alloc(ntfs_index_ctx_cache, SLAB_NOFS);
+ if (ictx) {
+ ictx->idx_ni = idx_ni;
+ ictx->entry = NULL;
+ ictx->data = NULL;
+ ictx->data_len = 0;
+ ictx->is_in_root = 0;
+ ictx->ir = NULL;
+ ictx->actx = NULL;
+ ictx->base_ni = NULL;
+ ictx->ia = NULL;
+ ictx->page = NULL;
+ }
+ return ictx;
+}
+
+/**
+ * ntfs_index_ctx_put - release an index context
+ * @ictx: index context to free
+ *
+ * Release the index context @ictx, releasing all associated resources.
+ *
+ * Locking: Caller must hold i_sem on the index inode.
+ */
+void ntfs_index_ctx_put(ntfs_index_context *ictx)
+{
+ if (ictx->entry) {
+ if (ictx->is_in_root) {
+ if (ictx->actx)
+ put_attr_search_ctx(ictx->actx);
+ if (ictx->base_ni)
+ unmap_mft_record(ictx->base_ni);
+ } else {
+ struct page *page = ictx->page;
+ if (page) {
+ BUG_ON(!PageLocked(page));
+ unlock_page(page);
+ ntfs_unmap_page(page);
+ }
+ }
+ }
+ kmem_cache_free(ntfs_index_ctx_cache, ictx);
+ return;
+}
+
+/**
+ * ntfs_index_lookup - find a key in an index and return its index entry
+ * @key: [IN] key for which to search in the index
+ * @key_len: [IN] length of @key in bytes
+ * @ictx: [IN/OUT] context describing the index and the returned entry
+ *
+ * Before calling ntfs_index_lookup(), @ictx must have been obtained from a
+ * call to ntfs_index_ctx_get().
+ *
+ * Look for the @key in the index specified by the index lookup context @ictx.
+ * ntfs_index_lookup() walks the contents of the index looking for the @key.
+ *
+ * If the @key is found in the index, 0 is returned and @ictx is setup to
+ * describe the index entry containing the matching @key. @ictx->entry is the
+ * index entry and @ictx->data and @ictx->data_len are the index entry data and
+ * its length in bytes, respectively.
+ *
+ * If the @key is not found in the index, -ENOENT is returned and @ictx is
+ * setup to describe the index entry whose key collates immediately after the
+ * search @key, i.e. this is the position in the index at which an index entry
+ * with a key of @key would need to be inserted.
+ *
+ * If an error occurs return the negative error code and @ictx is left
+ * untouched.
+ *
+ * When finished with the entry and its data, call ntfs_index_ctx_put() to free
+ * the context and other associated resources.
+ *
+ * If the index entry was modified, call flush_dcache_index_entry_page()
+ * immediately after the modification and either ntfs_index_entry_mark_dirty()
+ * or ntfs_index_entry_write() before the call to ntfs_index_ctx_put() to
+ * ensure that the changes are written to disk.
+ *
+ * Locking: - Caller must hold i_sem on the index inode.
+ * - Each page cache page in the index allocation mapping must be
+ * locked whilst being accessed otherwise we may find a corrupt
+ * page due to it being under ->writepage at the moment which
+ * applies the mst protection fixups before writing out and then
+ * removes them again after the write is complete after which it
+ * unlocks the page.
+ */
+int ntfs_index_lookup(const void *key, const int key_len,
+ ntfs_index_context *ictx)
+{
+ ntfs_inode *idx_ni = ictx->idx_ni;
+ ntfs_volume *vol = idx_ni->vol;
+ struct super_block *sb = vol->sb;
+ ntfs_inode *base_ni = idx_ni->ext.base_ntfs_ino;
+ MFT_RECORD *m;
+ INDEX_ROOT *ir;
+ INDEX_ENTRY *ie;
+ INDEX_ALLOCATION *ia;
+ u8 *index_end;
+ attr_search_context *actx;
+ int rc, err = 0;
+ VCN vcn, old_vcn;
+ struct address_space *ia_mapping;
+ struct page *page;
+ u8 *kaddr;
+
+ ntfs_debug("Entering.");
+ BUG_ON(!NInoAttr(idx_ni));
+ BUG_ON(idx_ni->type != AT_INDEX_ALLOCATION);
+ BUG_ON(idx_ni->nr_extents != -1);
+ BUG_ON(!base_ni);
+ BUG_ON(!key);
+ BUG_ON(key_len <= 0);
+ if (!ntfs_is_collation_rule_supported(
+ idx_ni->itype.index.collation_rule)) {
+ ntfs_error(sb, "Index uses unsupported collation rule 0x%x. "
+ "Aborting lookup.", le32_to_cpu(
+ idx_ni->itype.index.collation_rule));
+ return -EOPNOTSUPP;
+ }
+ /* Get hold of the mft record for the index inode. */
+ m = map_mft_record(base_ni);
+ if (unlikely(IS_ERR(m))) {
+ ntfs_error(sb, "map_mft_record() failed with error code %ld.",
+ -PTR_ERR(m));
+ return PTR_ERR(m);
+ }
+ actx = get_attr_search_ctx(base_ni, m);
+ if (unlikely(!actx)) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+ /* Find the index root attribute in the mft record. */
+ if (!lookup_attr(AT_INDEX_ROOT, idx_ni->name, idx_ni->name_len,
+ CASE_SENSITIVE, 0, NULL, 0, actx)) {
+ ntfs_error(sb, "Index root attribute missing in inode 0x%lx.",
+ idx_ni->mft_no);
+ err = -EIO;
+ goto err_out;
+ }
+ /* Get to the index root value (it has been verified in read_inode). */
+ ir = (INDEX_ROOT*)((u8*)actx->attr +
+ le16_to_cpu(actx->attr->data.resident.value_offset));
+ index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length);
+ /* The first index entry. */
+ ie = (INDEX_ENTRY*)((u8*)&ir->index +
+ le32_to_cpu(ir->index.entries_offset));
+ /*
+ * Loop until we exceed valid memory (corruption case) or until we
+ * reach the last entry.
+ */
+ for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) {
+ /* Bounds checks. */
+ if ((u8*)ie < (u8*)actx->mrec || (u8*)ie +
+ sizeof(INDEX_ENTRY_HEADER) > index_end ||
+ (u8*)ie + le16_to_cpu(ie->length) > index_end)
+ goto idx_err_out;
+ /*
+ * The last entry cannot contain a key. It can however contain
+ * a pointer to a child node in the B+tree so we just break out.
+ */
+ if (ie->flags & INDEX_ENTRY_END)
+ break;
+ /* Further bounds checks. */
+ if ((u32)sizeof(INDEX_ENTRY_HEADER) +
+ le16_to_cpu(ie->key_length) >
+ le16_to_cpu(ie->data.vi.data_offset) ||
+ (u32)le16_to_cpu(ie->data.vi.data_offset) +
+ le16_to_cpu(ie->data.vi.data_length) >
+ le16_to_cpu(ie->length))
+ goto idx_err_out;
+ /* If the keys match perfectly, we setup @ictx and return 0. */
+ if ((key_len == le16_to_cpu(ie->key_length)) && !memcmp(key,
+ &ie->key, key_len)) {
+ir_done:
+ ictx->is_in_root = TRUE;
+ ictx->actx = actx;
+ ictx->base_ni = base_ni;
+ ictx->ia = NULL;
+ ictx->page = NULL;
+done:
+ ictx->entry = ie;
+ ictx->data = (u8*)ie +
+ le16_to_cpu(ie->data.vi.data_offset);
+ ictx->data_len = le16_to_cpu(ie->data.vi.data_length);
+ ntfs_debug("Done.");
+ return err;
+ }
+ /*
+ * Not a perfect match, need to do full blown collation so we
+ * know which way in the B+tree we have to go.
+ */
+ rc = ntfs_collate(vol, idx_ni->itype.index.collation_rule, key,
+ key_len, &ie->key, le16_to_cpu(ie->key_length));
+ /*
+ * If @key collates before the key of the current entry, there
+ * is definitely no such key in this index but we might need to
+ * descend into the B+tree so we just break out of the loop.
+ */
+ if (rc == -1)
+ break;
+ /*
+ * A match should never happen as the memcmp() call should have
+ * cought it, but we still treat it correctly.
+ */
+ if (!rc)
+ goto ir_done;
+ /* The keys are not equal, continue the search. */
+ }
+ /*
+ * We have finished with this index without success. Check for the
+ * presence of a child node and if not present setup @ictx and return
+ * -ENOENT.
+ */
+ if (!(ie->flags & INDEX_ENTRY_NODE)) {
+ ntfs_debug("Entry not found.");
+ err = -ENOENT;
+ goto ir_done;
+ } /* Child node present, descend into it. */
+ /* Consistency check: Verify that an index allocation exists. */
+ if (!NInoIndexAllocPresent(idx_ni)) {
+ ntfs_error(sb, "No index allocation attribute but index entry "
+ "requires one. Inode 0x%lx is corrupt or "
+ "driver bug.", idx_ni->mft_no);
+ err = -EIO;
+ goto err_out;
+ }
+ /* Get the starting vcn of the index_block holding the child node. */
+ vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8);
+ ia_mapping = VFS_I(idx_ni)->i_mapping;
+ /*
+ * We are done with the index root and the mft record. Release them,
+ * otherwise we deadlock with ntfs_map_page().
+ */
+ put_attr_search_ctx(actx);
+ unmap_mft_record(base_ni);
+ m = NULL;
+ actx = NULL;
+descend_into_child_node:
+ /*
+ * Convert vcn to index into the index allocation attribute in units
+ * of PAGE_CACHE_SIZE and map the page cache page, reading it from
+ * disk if necessary.
+ */
+ page = ntfs_map_page(ia_mapping, vcn <<
+ idx_ni->itype.index.vcn_size_bits >> PAGE_CACHE_SHIFT);
+ if (IS_ERR(page)) {
+ ntfs_error(sb, "Failed to map index page, error %ld.",
+ -PTR_ERR(page));
+ err = PTR_ERR(page);
+ goto err_out;
+ }
+ lock_page(page);
+ kaddr = (u8*)page_address(page);
+fast_descend_into_child_node:
+ /* Get to the index allocation block. */
+ ia = (INDEX_ALLOCATION*)(kaddr + ((vcn <<
+ idx_ni->itype.index.vcn_size_bits) & ~PAGE_CACHE_MASK));
+ /* Bounds checks. */
+ if ((u8*)ia < kaddr || (u8*)ia > kaddr + PAGE_CACHE_SIZE) {
+ ntfs_error(sb, "Out of bounds check failed. Corrupt inode "
+ "0x%lx or driver bug.", idx_ni->mft_no);
+ err = -EIO;
+ goto unm_err_out;
+ }
+ if (sle64_to_cpu(ia->index_block_vcn) != vcn) {
+ ntfs_error(sb, "Actual VCN (0x%llx) of index buffer is "
+ "different from expected VCN (0x%llx). Inode "
+ "0x%lx is corrupt or driver bug.",
+ (unsigned long long)
+ sle64_to_cpu(ia->index_block_vcn),
+ (unsigned long long)vcn, idx_ni->mft_no);
+ err = -EIO;
+ goto unm_err_out;
+ }
+ if (le32_to_cpu(ia->index.allocated_size) + 0x18 !=
+ idx_ni->itype.index.block_size) {
+ ntfs_error(sb, "Index buffer (VCN 0x%llx) of inode 0x%lx has "
+ "a size (%u) differing from the index "
+ "specified size (%u). Inode is corrupt or "
+ "driver bug.", (unsigned long long)vcn,
+ idx_ni->mft_no,
+ le32_to_cpu(ia->index.allocated_size) + 0x18,
+ idx_ni->itype.index.block_size);
+ err = -EIO;
+ goto unm_err_out;
+ }
+ index_end = (u8*)ia + idx_ni->itype.index.block_size;
+ if (index_end > kaddr + PAGE_CACHE_SIZE) {
+ ntfs_error(sb, "Index buffer (VCN 0x%llx) of inode 0x%lx "
+ "crosses page boundary. Impossible! Cannot "
+ "access! This is probably a bug in the "
+ "driver.", (unsigned long long)vcn,
+ idx_ni->mft_no);
+ err = -EIO;
+ goto unm_err_out;
+ }
+ index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length);
+ if (index_end > (u8*)ia + idx_ni->itype.index.block_size) {
+ ntfs_error(sb, "Size of index buffer (VCN 0x%llx) of inode "
+ "0x%lx exceeds maximum size.",
+ (unsigned long long)vcn, idx_ni->mft_no);
+ err = -EIO;
+ goto unm_err_out;
+ }
+ /* The first index entry. */
+ ie = (INDEX_ENTRY*)((u8*)&ia->index +
+ le32_to_cpu(ia->index.entries_offset));
+ /*
+ * Iterate similar to above big loop but applied to index buffer, thus
+ * loop until we exceed valid memory (corruption case) or until we
+ * reach the last entry.
+ */
+ for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) {
+ /* Bounds checks. */
+ if ((u8*)ie < (u8*)ia || (u8*)ie +
+ sizeof(INDEX_ENTRY_HEADER) > index_end ||
+ (u8*)ie + le16_to_cpu(ie->length) > index_end) {
+ ntfs_error(sb, "Index entry out of bounds in inode "
+ "0x%lx.", idx_ni->mft_no);
+ err = -EIO;
+ goto unm_err_out;
+ }
+ /*
+ * The last entry cannot contain a ket. It can however contain
+ * a pointer to a child node in the B+tree so we just break out.
+ */
+ if (ie->flags & INDEX_ENTRY_END)
+ break;
+ /* Further bounds checks. */
+ if ((u32)sizeof(INDEX_ENTRY_HEADER) +
+ le16_to_cpu(ie->key_length) >
+ le16_to_cpu(ie->data.vi.data_offset) ||
+ (u32)le16_to_cpu(ie->data.vi.data_offset) +
+ le16_to_cpu(ie->data.vi.data_length) >
+ le16_to_cpu(ie->length)) {
+ ntfs_error(sb, "Index entry out of bounds in inode "
+ "0x%lx.", idx_ni->mft_no);
+ err = -EIO;
+ goto unm_err_out;
+ }
+ /* If the keys match perfectly, we setup @ictx and return 0. */
+ if ((key_len == le16_to_cpu(ie->key_length)) && !memcmp(key,
+ &ie->key, key_len)) {
+ia_done:
+ ictx->is_in_root = FALSE;
+ ictx->actx = NULL;
+ ictx->base_ni = NULL;
+ ictx->ia = ia;
+ ictx->page = page;
+ goto done;
+ }
+ /*
+ * Not a perfect match, need to do full blown collation so we
+ * know which way in the B+tree we have to go.
+ */
+ rc = ntfs_collate(vol, idx_ni->itype.index.collation_rule, key,
+ key_len, &ie->key, le16_to_cpu(ie->key_length));
+ /*
+ * If @key collates before the key of the current entry, there
+ * is definitely no such key in this index but we might need to
+ * descend into the B+tree so we just break out of the loop.
+ */
+ if (rc == -1)
+ break;
+ /*
+ * A match should never happen as the memcmp() call should have
+ * cought it, but we still treat it correctly.
+ */
+ if (!rc)
+ goto ia_done;
+ /* The keys are not equal, continue the search. */
+ }
+ /*
+ * We have finished with this index buffer without success. Check for
+ * the presence of a child node and if not present return -ENOENT.
+ */
+ if (!(ie->flags & INDEX_ENTRY_NODE)) {
+ ntfs_debug("Entry not found.");
+ err = -ENOENT;
+ goto ia_done;
+ }
+ if ((ia->index.flags & NODE_MASK) == LEAF_NODE) {
+ ntfs_error(sb, "Index entry with child node found in a leaf "
+ "node in inode 0x%lx.", idx_ni->mft_no);
+ err = -EIO;
+ goto unm_err_out;
+ }
+ /* Child node present, descend into it. */
+ old_vcn = vcn;
+ vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8);
+ if (vcn >= 0) {
+ /*
+ * If vcn is in the same page cache page as old_vcn we recycle
+ * the mapped page.
+ */
+ if (old_vcn << vol->cluster_size_bits >>
+ PAGE_CACHE_SHIFT == vcn <<
+ vol->cluster_size_bits >>
+ PAGE_CACHE_SHIFT)
+ goto fast_descend_into_child_node;
+ unlock_page(page);
+ ntfs_unmap_page(page);
+ goto descend_into_child_node;
+ }
+ ntfs_error(sb, "Negative child node vcn in inode 0x%lx.",
+ idx_ni->mft_no);
+ err = -EIO;
+unm_err_out:
+ unlock_page(page);
+ ntfs_unmap_page(page);
+err_out:
+ if (actx)
+ put_attr_search_ctx(actx);
+ if (m)
+ unmap_mft_record(base_ni);
+ return err;
+idx_err_out:
+ ntfs_error(sb, "Corrupt index. Aborting lookup.");
+ err = -EIO;
+ goto err_out;
+}
+
+#ifdef NTFS_RW
+
+/**
+ * __ntfs_index_entry_mark_dirty - mark an index allocation entry dirty
+ * @ictx: ntfs index context describing the index entry
+ *
+ * NOTE: You want to use fs/ntfs/index.h::ntfs_index_entry_mark_dirty() instead!
+ *
+ * Mark the index allocation entry described by the index entry context @ictx
+ * dirty.
+ *
+ * The index entry must be in an index block belonging to the index allocation
+ * attribute. Mark the buffers belonging to the index record as well as the
+ * page cache page the index block is in dirty. This automatically marks the
+ * VFS inode of the ntfs index inode to which the index entry belongs dirty,
+ * too (I_DIRTY_PAGES) and this in turn ensures the page buffers, and hence the
+ * dirty index block, will be written out to disk later.
+ */
+void __ntfs_index_entry_mark_dirty(ntfs_index_context *ictx)
+{
+ ntfs_inode *ni;
+ struct page *page;
+ struct buffer_head *bh, *head;
+ unsigned int rec_start, rec_end, bh_size, bh_start, bh_end;
+
+ BUG_ON(ictx->is_in_root);
+ ni = ictx->idx_ni;
+ page = ictx->page;
+ BUG_ON(!page_has_buffers(page));
+ /*
+ * If the index block is the same size as the page cache page, set all
+ * the buffers in the page, as well as the page itself, dirty.
+ */
+ if (ni->itype.index.block_size == PAGE_CACHE_SIZE) {
+ __set_page_dirty_buffers(page);
+ return;
+ }
+ /* Set only the buffers in which the index block is located dirty. */
+ rec_start = (unsigned int)((u8*)ictx->ia - (u8*)page_address(page));
+ rec_end = rec_start + ni->itype.index.block_size;
+ bh_size = ni->vol->sb->s_blocksize;
+ bh_start = 0;
+ bh = head = page_buffers(page);
+ do {
+ bh_end = bh_start + bh_size;
+ if ((bh_start >= rec_start) && (bh_end <= rec_end))
+ set_buffer_dirty(bh);
+ bh_start = bh_end;
+ } while ((bh = bh->b_this_page) != head);
+ /* Finally, set the page itself dirty, too. */
+ __set_page_dirty_nobuffers(page);
+}
+
+#endif /* NTFS_RW */
--- /dev/null
+/*
+ * index.h - Defines for NTFS kernel index handling. Part of the Linux-NTFS
+ * project.
+ *
+ * Copyright (c) 2004 Anton Altaparmakov
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LINUX_NTFS_INDEX_H
+#define _LINUX_NTFS_INDEX_H
+
+#include <linux/fs.h>
+
+#include "types.h"
+#include "layout.h"
+#include "inode.h"
+#include "attrib.h"
+#include "mft.h"
+
+/**
+ * @idx_ni: index inode containing the @entry described by this context
+ * @entry: index entry (points into @ir or @ia)
+ * @data: index entry data (points into @entry)
+ * @data_len: length in bytes of @data
+ * @is_in_root: TRUE if @entry is in @ir and FALSE if it is in @ia
+ * @ir: index root if @is_in_root and NULL otherwise
+ * @actx: attribute search context if @is_in_root and NULL otherwise
+ * @base_ni: base inode if @is_in_root and NULL otherwise
+ * @ia: index block if @is_in_root is FALSE and NULL otherwise
+ * @page: page if @is_in_root is FALSE and NULL otherwise
+ *
+ * @idx_ni is the index inode this context belongs to.
+ *
+ * @entry is the index entry described by this context. @data and @data_len
+ * are the index entry data and its length in bytes, respectively. @data
+ * simply points into @entry. This is probably what the user is interested in.
+ *
+ * If @is_in_root is TRUE, @entry is in the index root attribute @ir described
+ * by the attribute search context @actx and the base inode @base_ni. @ia and
+ * @page are NULL in this case.
+ *
+ * If @is_in_root is FALSE, @entry is in the index allocation attribute and @ia
+ * and @page point to the index allocation block and the mapped, locked page it
+ * is in, respectively. @ir, @actx and @base_ni are NULL in this case.
+ *
+ * To obtain a context call ntfs_index_ctx_get().
+ *
+ * We use this context to allow ntfs_index_lookup() to return the found index
+ * @entry and its @data without having to allocate a buffer and copy the @entry
+ * and/or its @data into it.
+ *
+ * When finished with the @entry and its @data, call ntfs_index_ctx_put() to
+ * free the context and other associated resources.
+ *
+ * If the index entry was modified, call flush_dcache_index_entry_page()
+ * immediately after the modification and either ntfs_index_entry_mark_dirty()
+ * or ntfs_index_entry_write() before the call to ntfs_index_ctx_put() to
+ * ensure that the changes are written to disk.
+ */
+typedef struct {
+ ntfs_inode *idx_ni;
+ INDEX_ENTRY *entry;
+ void *data;
+ u16 data_len;
+ BOOL is_in_root;
+ INDEX_ROOT *ir;
+ attr_search_context *actx;
+ ntfs_inode *base_ni;
+ INDEX_ALLOCATION *ia;
+ struct page *page;
+} ntfs_index_context;
+
+extern ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *idx_ni);
+extern void ntfs_index_ctx_put(ntfs_index_context *ictx);
+
+extern int ntfs_index_lookup(const void *key, const int key_len,
+ ntfs_index_context *ictx);
+
+#ifdef NTFS_RW
+
+/**
+ * ntfs_index_entry_flush_dcache_page - flush_dcache_page() for index entries
+ * @ictx: ntfs index context describing the index entry
+ *
+ * Call flush_dcache_page() for the page in which an index entry resides.
+ *
+ * This must be called every time an index entry is modified, just after the
+ * modification.
+ *
+ * If the index entry is in the index root attribute, simply flush the page
+ * containing the mft record containing the index root attribute.
+ *
+ * If the index entry is in an index block belonging to the index allocation
+ * attribute, simply flush the page cache page containing the index block.
+ */
+static inline void ntfs_index_entry_flush_dcache_page(ntfs_index_context *ictx)
+{
+ if (ictx->is_in_root)
+ flush_dcache_mft_record_page(ictx->actx->ntfs_ino);
+ else
+ flush_dcache_page(ictx->page);
+}
+
+extern void __ntfs_index_entry_mark_dirty(ntfs_index_context *ictx);
+
+/**
+ * ntfs_index_entry_mark_dirty - mark an index entry dirty
+ * @ictx: ntfs index context describing the index entry
+ *
+ * Mark the index entry described by the index entry context @ictx dirty.
+ *
+ * If the index entry is in the index root attribute, simply mark the mft
+ * record containing the index root attribute dirty. This ensures the mft
+ * record, and hence the index root attribute, will be written out to disk
+ * later.
+ *
+ * If the index entry is in an index block belonging to the index allocation
+ * attribute, mark the buffers belonging to the index record as well as the
+ * page cache page the index block is in dirty. This automatically marks the
+ * VFS inode of the ntfs index inode to which the index entry belongs dirty,
+ * too (I_DIRTY_PAGES) and this in turn ensures the page buffers, and hence the
+ * dirty index block, will be written out to disk later.
+ */
+static inline void ntfs_index_entry_mark_dirty(ntfs_index_context *ictx)
+{
+ if (ictx->is_in_root)
+ mark_mft_record_dirty(ictx->actx->ntfs_ino);
+ else
+ __ntfs_index_entry_mark_dirty(ictx);
+}
+
+#endif /* NTFS_RW */
+
+#endif /* _LINUX_NTFS_INDEX_H */
--- /dev/null
+/*
+ * quota.c - NTFS kernel quota ($Quota) handling. Part of the Linux-NTFS
+ * project.
+ *
+ * Copyright (c) 2004 Anton Altaparmakov
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef NTFS_RW
+
+#include "ntfs.h"
+#include "index.h"
+#include "quota.h"
+
+/**
+ * ntfs_mark_quotas_out_of_date - mark the quotas out of date on an ntfs volume
+ * @vol: ntfs volume on which to mark the quotas out of date
+ *
+ * Mark the quotas out of date on the ntfs volume @vol and return TRUE on
+ * success and FALSE on error.
+ */
+BOOL ntfs_mark_quotas_out_of_date(ntfs_volume *vol)
+{
+ ntfs_index_context *ictx;
+ QUOTA_CONTROL_ENTRY *qce;
+ const u32 qid = QUOTA_DEFAULTS_ID;
+ int err;
+
+ ntfs_debug("Entering.");
+ if (NVolQuotaOutOfDate(vol))
+ goto done;
+ if (!vol->quota_ino || !vol->quota_q_ino) {
+ ntfs_error(vol->sb, "Quota inodes are not open.");
+ return FALSE;
+ }
+ down(&vol->quota_q_ino->i_sem);
+ ictx = ntfs_index_ctx_get(NTFS_I(vol->quota_q_ino));
+ if (!ictx) {
+ ntfs_error(vol->sb, "Failed to get index context.");
+ return FALSE;
+ }
+ err = ntfs_index_lookup(&qid, sizeof(qid), ictx);
+ if (err) {
+ if (err == -ENOENT)
+ ntfs_error(vol->sb, "Quota defaults entry is not "
+ "present.");
+ else
+ ntfs_error(vol->sb, "Lookup of quota defaults entry "
+ "failed.");
+ goto err_out;
+ }
+ if (ictx->data_len < offsetof(QUOTA_CONTROL_ENTRY, sid)) {
+ ntfs_error(vol->sb, "Quota defaults entry size is invalid. "
+ "Run chkdsk.");
+ goto err_out;
+ }
+ qce = (QUOTA_CONTROL_ENTRY*)ictx->data;
+ if (le32_to_cpu(qce->version) != QUOTA_VERSION) {
+ ntfs_error(vol->sb, "Quota defaults entry version 0x%x is not "
+ "supported.", le32_to_cpu(qce->version));
+ goto err_out;
+ }
+ ntfs_debug("Quota defaults flags = 0x%x.", le32_to_cpu(qce->flags));
+ /* If quotas are already marked out of date, no need to do anything. */
+ if (qce->flags & QUOTA_FLAG_OUT_OF_DATE)
+ goto set_done;
+ /*
+ * If quota tracking is neither requested, nor enabled and there are no
+ * pending deletes, no need to mark the quotas out of date.
+ */
+ if (!(qce->flags & (QUOTA_FLAG_TRACKING_ENABLED |
+ QUOTA_FLAG_TRACKING_REQUESTED |
+ QUOTA_FLAG_PENDING_DELETES)))
+ goto set_done;
+ /*
+ * Set the QUOTA_FLAG_OUT_OF_DATE bit thus marking quotas out of date.
+ * This is verified on WinXP to be sufficient to cause windows to
+ * rescan the volume on boot and update all quota entries.
+ */
+ qce->flags |= QUOTA_FLAG_OUT_OF_DATE;
+ /* Ensure the modified flags are written to disk. */
+ ntfs_index_entry_flush_dcache_page(ictx);
+ ntfs_index_entry_mark_dirty(ictx);
+set_done:
+ ntfs_index_ctx_put(ictx);
+ up(&vol->quota_q_ino->i_sem);
+ /*
+ * We set the flag so we do not try to mark the quotas out of date
+ * again on remount.
+ */
+ NVolSetQuotaOutOfDate(vol);
+done:
+ ntfs_debug("Done.");
+ return TRUE;
+err_out:
+ ntfs_index_ctx_put(ictx);
+ up(&vol->quota_q_ino->i_sem);
+ return FALSE;
+}
+
+#endif /* NTFS_RW */
--- /dev/null
+/*
+ * quota.h - Defines for NTFS kernel quota ($Quota) handling. Part of the
+ * Linux-NTFS project.
+ *
+ * Copyright (c) 2004 Anton Altaparmakov
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LINUX_NTFS_QUOTA_H
+#define _LINUX_NTFS_QUOTA_H
+
+#ifdef NTFS_RW
+
+#include "types.h"
+#include "volume.h"
+
+extern BOOL ntfs_mark_quotas_out_of_date(ntfs_volume *vol);
+
+#endif /* NTFS_RW */
+
+#endif /* _LINUX_NTFS_QUOTA_H */
static struct dentry *openpromfs_lookup(struct inode *, struct dentry *dentry, struct nameidata *nd);
static int openpromfs_unlink (struct inode *, struct dentry *dentry);
-static ssize_t nodenum_read(struct file *file, char *buf,
+static ssize_t nodenum_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
struct inode *inode = file->f_dentry->d_inode;
return count;
}
-static ssize_t property_read(struct file *filp, char *buf,
+static ssize_t property_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
struct inode *inode = filp->f_dentry->d_inode;
i = ((u32)(long)inode->u.generic_ip) >> 16;
if ((u16)((long)inode->u.generic_ip) == aliases) {
if (i >= aliases_nodes)
- p = 0;
+ p = NULL;
else
p = alias_names [i];
} else
return -EIO;
op->value [k] = 0;
if (k) {
- for (s = 0, p = op->value; p < op->value + k; p++) {
+ for (s = NULL, p = op->value; p < op->value + k; p++) {
if ((*p >= ' ' && *p <= '~') || *p == '\n') {
op->flag |= OPP_STRING;
s = p;
return count;
}
-static ssize_t property_write(struct file *filp, const char *buf,
+static ssize_t property_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
int i, j, k;
if (filp->f_pos >= 0xffffff || count >= 0xffffff)
return -EINVAL;
if (!filp->private_data) {
- i = property_read (filp, NULL, 0, 0);
+ i = property_read (filp, NULL, 0, NULL);
if (i)
return i;
}
mask &= mask2;
if (mask) {
*first &= ~mask;
- *first |= simple_strtoul (tmp, 0, 16);
+ *first |= simple_strtoul (tmp, NULL, 16);
op->flag |= OPP_DIRTY;
}
} else {
for (j = 0; j < first_off; j++)
mask >>= 1;
*q &= ~mask;
- *q |= simple_strtoul (tmp,0,16);
+ *q |= simple_strtoul (tmp,NULL,16);
}
buf += 9;
} else if ((q == last - 1) && last_cnt
for (j = 0; j < 8 - last_cnt; j++)
mask <<= 1;
*q &= ~mask;
- *q |= simple_strtoul (tmp, 0, 16);
+ *q |= simple_strtoul (tmp, NULL, 16);
buf += last_cnt;
} else {
char tchars[17]; /* XXX yuck... */
if (copy_from_user(tchars, buf, 16))
return -EFAULT;
- *q = simple_strtoul (tchars, 0, 16);
+ *q = simple_strtoul (tchars, NULL, 16);
buf += 9;
}
}
child = NULL;
while ((child = of_get_next_child(np, child))) {
p = strrchr(child->full_name, '/');
- if (p == 0)
+ if (!p)
p = child->full_name;
else
++p;
lastp = &al->next;
}
of_node_put(child);
- *lastp = 0;
+ *lastp = NULL;
de->subdir = list;
}
struct device_node *root;
if ( !have_of )
return;
- proc_device_tree = proc_mkdir("device-tree", 0);
+ proc_device_tree = proc_mkdir("device-tree", NULL);
if (proc_device_tree == 0)
return;
root = of_find_node_by_path("/");
sema_init(&p->sem, 1);
p->op = op;
file->private_data = p;
+
+ /* SEQ files support lseek, but not pread/pwrite */
+ file->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE);
return 0;
}
EXPORT_SYMBOL(seq_open);
void *p;
int err = 0;
- if (ppos != &file->f_pos)
- return -EPIPE;
-
down(&m->sem);
/* grab buffer if we didn't have one */
if (!m->buf) {
int rq_bytes_sent;
int rq_iovlen;
- struct iovec rq_iov[4];
+ struct kvec rq_iov[4];
int (*rq_setup_read) (struct smb_request *);
void (*rq_callback) (struct smb_request *);
static int
_recvfrom(struct socket *socket, unsigned char *ubuf, int size, unsigned flags)
{
- struct iovec iov;
- struct msghdr msg;
- mm_segment_t fs;
-
- fs = get_fs();
- set_fs(get_ds());
- flags |= MSG_DONTWAIT | MSG_NOSIGNAL;
-
- msg.msg_flags = flags;
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_control = NULL;
- iov.iov_base = ubuf;
- iov.iov_len = size;
-
- size = sock_recvmsg(socket, &msg, size, flags);
-
- set_fs(fs);
- return size;
+ struct kvec iov = {ubuf, size};
+ struct msghdr msg = {.msg_flags = flags};
+ msg.msg_flags |= MSG_DONTWAIT | MSG_NOSIGNAL;
+ return kernel_recvmsg(socket, &msg, &iov, 1, size, msg.msg_flags);
}
/*
}
/*
- * Adjust the iovec to move on 'n' bytes (from nfs/sunrpc)
+ * Adjust the kvec to move on 'n' bytes (from nfs/sunrpc)
*/
static int
-smb_move_iov(struct msghdr *msg, struct iovec *niv, unsigned amount)
+smb_move_iov(struct kvec **data, size_t *num, struct kvec *vec, unsigned amount)
{
- struct iovec *iv = msg->msg_iov;
+ struct kvec *iv = *data;
int i;
int len;
/*
- * Eat any sent iovecs
+ * Eat any sent kvecs
*/
while (iv->iov_len <= amount) {
amount -= iv->iov_len;
iv++;
- msg->msg_iovlen--;
+ (*num)--;
}
/*
* And chew down the partial one
*/
- niv[0].iov_len = iv->iov_len-amount;
- niv[0].iov_base =((unsigned char *)iv->iov_base)+amount;
+ vec[0].iov_len = iv->iov_len-amount;
+ vec[0].iov_base =((unsigned char *)iv->iov_base)+amount;
iv++;
- len = niv[0].iov_len;
+ len = vec[0].iov_len;
/*
* And copy any others
*/
- for (i = 1; i < msg->msg_iovlen; i++) {
- niv[i] = *iv++;
- len += niv[i].iov_len;
+ for (i = 1; i < *num; i++) {
+ vec[i] = *iv++;
+ len += vec[i].iov_len;
}
- msg->msg_iov = niv;
+ *data = vec;
return len;
}
{
struct socket *sock;
unsigned int flags;
- struct iovec iov;
+ struct kvec iov;
struct msghdr msg;
- mm_segment_t fs;
int rlen = smb_len(server->header) - server->smb_read + 4;
int result = -EIO;
+ if (rlen > PAGE_SIZE)
+ rlen = PAGE_SIZE;
+
sock = server_sock(server);
if (!sock)
goto out;
if (sock->sk->sk_state != TCP_ESTABLISHED)
goto out;
- fs = get_fs();
- set_fs(get_ds());
-
flags = MSG_DONTWAIT | MSG_NOSIGNAL;
iov.iov_base = drop_buffer;
iov.iov_len = PAGE_SIZE;
msg.msg_flags = flags;
msg.msg_name = NULL;
msg.msg_namelen = 0;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
msg.msg_control = NULL;
- if (rlen > PAGE_SIZE)
- rlen = PAGE_SIZE;
-
- result = sock_recvmsg(sock, &msg, rlen, flags);
-
- set_fs(fs);
+ result = kernel_recvmsg(sock, &msg, &iov, 1, rlen, flags);
VERBOSE("read: %d\n", result);
if (result < 0) {
{
struct socket *sock;
unsigned int flags;
- struct iovec iov[4];
+ struct kvec iov[4];
+ struct kvec *p = req->rq_iov;
+ size_t num = req->rq_iovlen;
struct msghdr msg;
- mm_segment_t fs;
int rlen;
int result = -EIO;
if (sock->sk->sk_state != TCP_ESTABLISHED)
goto out;
- fs = get_fs();
- set_fs(get_ds());
-
flags = MSG_DONTWAIT | MSG_NOSIGNAL;
msg.msg_flags = flags;
msg.msg_name = NULL;
msg.msg_namelen = 0;
- msg.msg_iov = req->rq_iov;
- msg.msg_iovlen = req->rq_iovlen;
msg.msg_control = NULL;
/* Dont repeat bytes and count available bufferspace */
- rlen = smb_move_iov(&msg, iov, req->rq_bytes_recvd);
+ rlen = smb_move_iov(&p, &num, iov, req->rq_bytes_recvd);
if (req->rq_rlen < rlen)
rlen = req->rq_rlen;
- result = sock_recvmsg(sock, &msg, rlen, flags);
-
- set_fs(fs);
+ result = kernel_recvmsg(sock, &msg, p, num, rlen, flags);
VERBOSE("read: %d\n", result);
if (result < 0) {
int
smb_send_request(struct smb_request *req)
{
- mm_segment_t fs;
struct smb_sb_info *server = req->rq_server;
struct socket *sock;
- struct msghdr msg;
+ struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT};
int slen = req->rq_slen - req->rq_bytes_sent;
int result = -EIO;
- struct iovec iov[4];
+ struct kvec iov[4];
+ struct kvec *p = req->rq_iov;
+ size_t num = req->rq_iovlen;
sock = server_sock(server);
if (!sock)
if (sock->sk->sk_state != TCP_ESTABLISHED)
goto out;
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_control = NULL;
- msg.msg_controllen = 0;
- msg.msg_iov = req->rq_iov;
- msg.msg_iovlen = req->rq_iovlen;
- msg.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT;
-
/* Dont repeat bytes */
if (req->rq_bytes_sent)
- smb_move_iov(&msg, iov, req->rq_bytes_sent);
+ smb_move_iov(&p, &num, iov, req->rq_bytes_sent);
- fs = get_fs();
- set_fs(get_ds());
- result = sock_sendmsg(sock, &msg, slen);
- set_fs(fs);
+ result = kernel_sendmsg(sock, &msg, p, num, slen);
if (result >= 0) {
req->rq_bytes_sent += result;
--- /dev/null
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/highmem.h>
+#include <linux/swap.h>
+
+#include "time.h"
+#include "kmem.h"
+
+#define MAX_VMALLOCS 6
+#define MAX_SLAB_SIZE 0x20000
+
+
+void *
+kmem_alloc(size_t size, int flags)
+{
+ int retries = 0, lflags = kmem_flags_convert(flags);
+ void *ptr;
+
+ do {
+ if (size < MAX_SLAB_SIZE || retries > MAX_VMALLOCS)
+ ptr = kmalloc(size, lflags);
+ else
+ ptr = __vmalloc(size, lflags, PAGE_KERNEL);
+ if (ptr || (flags & (KM_MAYFAIL|KM_NOSLEEP)))
+ return ptr;
+ if (!(++retries % 100))
+ printk(KERN_ERR "possible deadlock in %s (mode:0x%x)\n",
+ __FUNCTION__, lflags);
+ } while (1);
+}
+
+void *
+kmem_zalloc(size_t size, int flags)
+{
+ void *ptr;
+
+ ptr = kmem_alloc(size, flags);
+ if (ptr)
+ memset((char *)ptr, 0, (int)size);
+ return ptr;
+}
+
+void
+kmem_free(void *ptr, size_t size)
+{
+ if (((unsigned long)ptr < VMALLOC_START) ||
+ ((unsigned long)ptr >= VMALLOC_END)) {
+ kfree(ptr);
+ } else {
+ vfree(ptr);
+ }
+}
+
+void *
+kmem_realloc(void *ptr, size_t newsize, size_t oldsize, int flags)
+{
+ void *new;
+
+ new = kmem_alloc(newsize, flags);
+ if (ptr) {
+ if (new)
+ memcpy(new, ptr,
+ ((oldsize < newsize) ? oldsize : newsize));
+ kmem_free(ptr, oldsize);
+ }
+ return new;
+}
+
+void *
+kmem_zone_alloc(kmem_zone_t *zone, int flags)
+{
+ int retries = 0, lflags = kmem_flags_convert(flags);
+ void *ptr;
+
+ do {
+ ptr = kmem_cache_alloc(zone, lflags);
+ if (ptr || (flags & (KM_MAYFAIL|KM_NOSLEEP)))
+ return ptr;
+ if (!(++retries % 100))
+ printk(KERN_ERR "possible deadlock in %s (mode:0x%x)\n",
+ __FUNCTION__, lflags);
+ } while (1);
+}
+
+void *
+kmem_zone_zalloc(kmem_zone_t *zone, int flags)
+{
+ void *ptr;
+
+ ptr = kmem_zone_alloc(zone, flags);
+ if (ptr)
+ memset((char *)ptr, 0, kmem_cache_size(zone));
+ return ptr;
+}
/*
- * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
#ifndef __XFS_SUPPORT_KMEM_H__
#define __XFS_SUPPORT_KMEM_H__
-#include <linux/mm.h>
-#include <linux/highmem.h>
#include <linux/slab.h>
-#include <linux/vmalloc.h>
-
-/*
- * Cutoff point to use vmalloc instead of kmalloc.
- */
-#define MAX_SLAB_SIZE 0x20000
+#include <linux/sched.h>
+#include <linux/mm.h>
/*
- * XFS uses slightly different names for these due to the
- * IRIX heritage.
+ * memory management routines
*/
-#define kmem_zone kmem_cache_s
-#define kmem_zone_t kmem_cache_t
-
#define KM_SLEEP 0x0001
#define KM_NOSLEEP 0x0002
#define KM_NOFS 0x0004
-#define KM_MAYFAIL 0x0005
+#define KM_MAYFAIL 0x0008
+
+#define kmem_zone kmem_cache_s
+#define kmem_zone_t kmem_cache_t
typedef unsigned long xfs_pflags_t;
+#define PFLAGS_TEST_NOIO() (current->flags & PF_NOIO)
#define PFLAGS_TEST_FSTRANS() (current->flags & PF_FSTRANS)
+#define PFLAGS_SET_NOIO() do { \
+ current->flags |= PF_NOIO; \
+} while (0)
+
+#define PFLAGS_CLEAR_NOIO() do { \
+ current->flags &= ~PF_NOIO; \
+} while (0)
+
/* these could be nested, so we save state */
#define PFLAGS_SET_FSTRANS(STATEP) do { \
*(STATEP) = current->flags; \
*(NSTATEP) = *(OSTATEP); \
} while (0)
-static __inline unsigned int
-kmem_flags_convert(int flags)
+static __inline unsigned int kmem_flags_convert(int flags)
{
int lflags;
-#if DEBUG
+#ifdef DEBUG
if (unlikely(flags & ~(KM_SLEEP|KM_NOSLEEP|KM_NOFS|KM_MAYFAIL))) {
printk(KERN_WARNING
"XFS: memory allocation with wrong flags (%x)\n", flags);
/* avoid recusive callbacks to filesystem during transactions */
if (PFLAGS_TEST_FSTRANS() || (flags & KM_NOFS))
lflags &= ~__GFP_FS;
-
- if (!(flags & KM_MAYFAIL))
- lflags |= __GFP_NOFAIL;
- }
-
- return lflags;
-}
-
-static __inline void *
-kmem_alloc(size_t size, int flags)
-{
- if (unlikely(MAX_SLAB_SIZE < size))
- /* Avoid doing filesystem sensitive stuff to get this */
- return __vmalloc(size, kmem_flags_convert(flags), PAGE_KERNEL);
- return kmalloc(size, kmem_flags_convert(flags));
-}
-
-static __inline void *
-kmem_zalloc(size_t size, int flags)
-{
- void *ptr = kmem_alloc(size, flags);
- if (likely(ptr != NULL))
- memset(ptr, 0, size);
- return ptr;
-}
-
-static __inline void
-kmem_free(void *ptr, size_t size)
-{
- if (unlikely((unsigned long)ptr < VMALLOC_START ||
- (unsigned long)ptr >= VMALLOC_END))
- kfree(ptr);
- else
- vfree(ptr);
-}
-
-static __inline void *
-kmem_realloc(void *ptr, size_t newsize, size_t oldsize, int flags)
-{
- void *new = kmem_alloc(newsize, flags);
-
- if (likely(ptr != NULL)) {
- if (likely(new != NULL))
- memcpy(new, ptr, min(oldsize, newsize));
- kmem_free(ptr, oldsize);
}
-
- return new;
+
+ return lflags;
}
static __inline kmem_zone_t *
return kmem_cache_create(zone_name, size, 0, 0, NULL, NULL);
}
-static __inline void *
-kmem_zone_alloc(kmem_zone_t *zone, int flags)
+static __inline void
+kmem_zone_free(kmem_zone_t *zone, void *ptr)
{
- return kmem_cache_alloc(zone, kmem_flags_convert(flags));
+ kmem_cache_free(zone, ptr);
}
-static __inline void *
-kmem_zone_zalloc(kmem_zone_t *zone, int flags)
+static __inline void
+kmem_zone_destroy(kmem_zone_t *zone)
{
- void *ptr = kmem_zone_alloc(zone, flags);
- if (likely(ptr != NULL))
- memset(ptr, 0, kmem_cache_size(zone));
- return ptr;
+ if (zone && kmem_cache_destroy(zone))
+ BUG();
}
-static __inline void
-kmem_zone_free(kmem_zone_t *zone, void *ptr)
+static __inline int
+kmem_zone_shrink(kmem_zone_t *zone)
{
- kmem_cache_free(zone, ptr);
+ return kmem_cache_shrink(zone);
}
+extern void *kmem_zone_zalloc(kmem_zone_t *, int);
+extern void *kmem_zone_alloc(kmem_zone_t *, int);
+
+extern void *kmem_alloc(size_t, int);
+extern void *kmem_realloc(void *, size_t, size_t, int);
+extern void *kmem_zalloc(size_t, int);
+extern void kmem_free(void *, size_t);
+
typedef struct shrinker *kmem_shaker_t;
typedef int (*kmem_shake_func_t)(int, unsigned int);
struct inode *inode,
loff_t offset,
ssize_t count,
- xfs_iomap_t *iomapp,
+ xfs_iomap_t *mapp,
int flags)
{
vnode_t *vp = LINVFS_GET_VP(inode);
- int error, niomaps = 1;
-
- if (((flags & (BMAPI_DIRECT|BMAPI_SYNC)) == BMAPI_DIRECT) &&
- (offset >= i_size_read(inode)))
- count = max_t(ssize_t, count, XFS_WRITE_IO_LOG);
-retry:
- VOP_BMAP(vp, offset, count, flags, iomapp, &niomaps, error);
- if ((error == EAGAIN) || (error == EIO))
- return -error;
- if (unlikely((flags & (BMAPI_WRITE|BMAPI_DIRECT)) ==
- (BMAPI_WRITE|BMAPI_DIRECT) && niomaps &&
- (iomapp->iomap_flags & IOMAP_DELAY))) {
- flags = BMAPI_ALLOCATE;
- goto retry;
- }
- if (flags & (BMAPI_WRITE|BMAPI_ALLOCATE)) {
+ int error, nmaps = 1;
+
+ VOP_BMAP(vp, offset, count, flags, mapp, &nmaps, error);
+ if (!error && (flags & (BMAPI_WRITE|BMAPI_ALLOCATE)))
VMODIFY(vp);
- }
return -error;
}
*fsbs = 0;
bh = head = page_buffers(page);
do {
- if (!buffer_unwritten(bh))
+ if (!buffer_unwritten(bh) || !buffer_uptodate(bh))
break;
if (!xfs_offset_to_map(page, iomapp, p_offset))
break;
xfs_iomap_t *iomapp,
struct writeback_control *wbc,
int startio,
- int all_bh)
+ int all_bh,
+ pgoff_t tlast)
{
- pgoff_t tlast;
struct page *page;
- tlast = (iomapp->iomap_offset + iomapp->iomap_bsize) >> PAGE_CACHE_SHIFT;
- for (; tindex < tlast; tindex++) {
+ for (; tindex <= tlast; tindex++) {
page = xfs_probe_delalloc_page(inode, tindex);
if (!page)
break;
{
struct buffer_head *bh_arr[MAX_BUF_PER_PAGE], *bh, *head;
xfs_iomap_t *iomp, iomap;
- unsigned long p_offset = 0;
- pgoff_t end_index;
loff_t offset;
- unsigned long long end_offset;
+ unsigned long p_offset = 0;
+ __uint64_t end_offset;
+ pgoff_t end_index, last_index, tlast;
int len, err, i, cnt = 0, uptodate = 1;
int flags = startio ? 0 : BMAPI_TRYLOCK;
int page_dirty = 1;
+ int delalloc = 0;
/* Are we off the end of the file ? */
- end_index = i_size_read(inode) >> PAGE_CACHE_SHIFT;
+ offset = i_size_read(inode);
+ end_index = offset >> PAGE_CACHE_SHIFT;
+ last_index = (offset - 1) >> PAGE_CACHE_SHIFT;
if (page->index >= end_index) {
if ((page->index >= end_index + 1) ||
!(i_size_read(inode) & (PAGE_CACHE_SIZE - 1))) {
* extent state conversion transaction on completion.
*/
if (buffer_unwritten(bh)) {
+ if (!startio)
+ continue;
if (!iomp) {
err = xfs_map_blocks(inode, offset, len, &iomap,
BMAPI_READ|BMAPI_IGNSTATE);
iomp = xfs_offset_to_map(page, &iomap,
p_offset);
}
- if (iomp && startio) {
+ if (iomp) {
if (!bh->b_end_io) {
err = xfs_map_unwritten(inode, page,
head, bh, p_offset,
if (err) {
goto error;
}
+ } else {
+ set_bit(BH_Lock, &bh->b_state);
}
+ BUG_ON(!buffer_locked(bh));
bh_arr[cnt++] = bh;
page_dirty = 0;
}
*/
} else if (buffer_delay(bh)) {
if (!iomp) {
+ delalloc = 1;
err = xfs_map_blocks(inode, offset, len, &iomap,
BMAPI_ALLOCATE | flags);
if (err) {
xfs_submit_page(page, bh_arr, cnt);
if (iomp) {
+ tlast = (iomp->iomap_offset + iomp->iomap_bsize - 1) >>
+ PAGE_CACHE_SHIFT;
+ if (delalloc && (tlast > last_index))
+ tlast = last_index;
xfs_cluster_write(inode, page->index + 1, iomp, wbc,
- startio, unmapped);
+ startio, unmapped, tlast);
}
return page_dirty;
*/
STATIC kmem_cache_t *pagebuf_cache;
-STATIC void pagebuf_daemon_wakeup(void);
+STATIC kmem_shaker_t pagebuf_shake;
+STATIC int pagebuf_daemon_wakeup(int, unsigned int);
STATIC void pagebuf_delwri_queue(xfs_buf_t *, int);
STATIC struct workqueue_struct *pagebuf_logio_workqueue;
STATIC struct workqueue_struct *pagebuf_dataio_workqueue;
* But until all the XFS lowlevel code is revamped to
* handle buffer allocation failures we can't do much.
*/
- if (!(++retries % 100)) {
- printk(KERN_ERR "possibly deadlocking in %s\n",
- __FUNCTION__);
- }
+ if (!(++retries % 100))
+ printk(KERN_ERR
+ "possible deadlock in %s (mode:0x%x)\n",
+ __FUNCTION__, gfp_mask);
XFS_STATS_INC(pb_page_retries);
- pagebuf_daemon_wakeup();
+ pagebuf_daemon_wakeup(0, gfp_mask);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(10);
goto retry;
pagebuf_delwri_dequeue(
xfs_buf_t *pb)
{
- PB_TRACE(pb, "delwri_uq", 0);
+ int dequeued = 0;
+
spin_lock(&pbd_delwrite_lock);
- list_del_init(&pb->pb_list);
+ if ((pb->pb_flags & PBF_DELWRI) && !list_empty(&pb->pb_list)) {
+ list_del_init(&pb->pb_list);
+ dequeued = 1;
+ }
pb->pb_flags &= ~PBF_DELWRI;
spin_unlock(&pbd_delwrite_lock);
+
+ if (dequeued)
+ pagebuf_rele(pb);
+
+ PB_TRACE(pb, "delwri_dq", (long)dequeued);
}
STATIC void
STATIC int pagebuf_daemon_active;
STATIC int force_flush;
-STATIC void
-pagebuf_daemon_wakeup(void)
+
+STATIC int
+pagebuf_daemon_wakeup(
+ int priority,
+ unsigned int mask)
{
force_flush = 1;
barrier();
wake_up_process(pagebuf_daemon_task);
+ return 0;
}
STATIC int
{
struct list_head tmp;
unsigned long age;
+ xfs_buftarg_t *target;
xfs_buf_t *pb, *n;
/* Set up the thread */
while (!list_empty(&tmp)) {
pb = list_entry(tmp.next, xfs_buf_t, pb_list);
+ target = pb->pb_target;
+
list_del_init(&pb->pb_list);
pagebuf_iostrategy(pb);
- blk_run_address_space(pb->pb_target->pbr_mapping);
+
+ blk_run_address_space(target->pbr_mapping);
}
if (as_list_len > 0)
pagebuf_cache = kmem_cache_create("xfs_buf_t", sizeof(xfs_buf_t), 0,
SLAB_HWCACHE_ALIGN, NULL, NULL);
if (pagebuf_cache == NULL) {
- printk("pagebuf: couldn't init pagebuf cache\n");
+ printk("XFS: couldn't init xfs_buf_t cache\n");
pagebuf_terminate();
return -ENOMEM;
}
- for (i = 0; i < NHASH; i++) {
- spin_lock_init(&pbhash[i].pb_hash_lock);
- INIT_LIST_HEAD(&pbhash[i].pb_hash);
- }
-
#ifdef PAGEBUF_TRACE
pagebuf_trace_buf = ktrace_alloc(PAGEBUF_TRACE_SIZE, KM_SLEEP);
#endif
pagebuf_daemon_start();
+
+ pagebuf_shake = kmem_shake_register(pagebuf_daemon_wakeup);
+ if (pagebuf_shake == NULL) {
+ pagebuf_terminate();
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < NHASH; i++) {
+ spin_lock_init(&pbhash[i].pb_hash_lock);
+ INIT_LIST_HEAD(&pbhash[i].pb_hash);
+ }
+
return 0;
}
ktrace_free(pagebuf_trace_buf);
#endif
- kmem_cache_destroy(pagebuf_cache);
+ kmem_zone_destroy(pagebuf_cache);
+ kmem_shake_deregister(pagebuf_shake);
}
#define XFS_BUF_ISSTALE(x) ((x)->pb_flags & XFS_B_STALE)
#define XFS_BUF_SUPER_STALE(x) do { \
XFS_BUF_STALE(x); \
- xfs_buf_undelay(x); \
+ pagebuf_delwri_dequeue(x); \
XFS_BUF_DONE(x); \
} while (0)
#define XFS_BUF_MANAGE PBF_FS_MANAGED
#define XFS_BUF_UNMANAGE(x) ((x)->pb_flags &= ~PBF_FS_MANAGED)
-static inline void xfs_buf_undelay(xfs_buf_t *pb)
-{
- if (pb->pb_flags & PBF_DELWRI) {
- if (pb->pb_list.next != &pb->pb_list) {
- pagebuf_delwri_dequeue(pb);
- pagebuf_rele(pb);
- } else {
- pb->pb_flags &= ~PBF_DELWRI;
- }
- }
-}
-
#define XFS_BUF_DELAYWRITE(x) ((x)->pb_flags |= PBF_DELWRI)
-#define XFS_BUF_UNDELAYWRITE(x) xfs_buf_undelay(x)
+#define XFS_BUF_UNDELAYWRITE(x) pagebuf_delwri_dequeue(x)
#define XFS_BUF_ISDELAYWRITE(x) ((x)->pb_flags & PBF_DELWRI)
#define XFS_BUF_ERROR(x,no) pagebuf_ioerror(x,no)
{
bp->pb_fspriv3 = mp;
bp->pb_strat = xfs_bdstrat_cb;
- xfs_buf_undelay(bp);
+ pagebuf_delwri_dequeue(bp);
return pagebuf_iostart(bp, PBF_WRITE | PBF_ASYNC | _PBF_RUN_QUEUES);
}
if (!iowait)
pb->pb_flags |= _PBF_RUN_QUEUES;
- xfs_buf_undelay(pb);
+ pagebuf_delwri_dequeue(pb);
pagebuf_iostrategy(pb);
if (iowait) {
error = pagebuf_iowait(pb);
/*
- * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
#include "xfs_rw.h"
#include <linux/dcache.h>
+#include <linux/smp_lock.h>
static struct vm_operations_struct linvfs_file_vm_ops;
int error;
vnode_t *vp = LINVFS_GET_VP(inode);
- ASSERT(vp);
+ unlock_kernel();
VOP_IOCTL(vp, inode, filp, 0, cmd, arg, error);
VMODIFY(vp);
+ lock_kernel();
/* NOTE: some of the ioctl's return positive #'s as a
* byte count indicating success, such as
int error;
vnode_t *vp = LINVFS_GET_VP(inode);
+ unlock_kernel();
ASSERT(vp);
VOP_IOCTL(vp, inode, filp, IO_INVIS, cmd, arg, error);
VMODIFY(vp);
+ lock_kernel();
/* NOTE: some of the ioctl's return positive #'s as a
* byte count indicating success, such as
* Stub for no-op vnode operations that return error status.
*/
int
-fs_noerr()
+fs_noerr(void)
{
return 0;
}
* Operation unsupported under this file system.
*/
int
-fs_nosys()
+fs_nosys(void)
{
return ENOSYS;
}
*/
/* ARGSUSED */
void
-fs_noval()
+fs_noval(void)
{
}
/*
- * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
# define XFS_TRACE_STRING
#endif
+#ifdef CONFIG_XFS_DMAPI
+# define XFS_DMAPI_STRING "dmapi support, "
+#else
+# define XFS_DMAPI_STRING
+#endif
+
#ifdef DEBUG
# define XFS_DBG_STRING "debug"
#else
XFS_REALTIME_STRING \
XFS_BIGFS_STRING \
XFS_TRACE_STRING \
+ XFS_DMAPI_STRING \
XFS_DBG_STRING /* DBG must be last */
#define LINVFS_GET_VFS(s) \
int write,
struct file *filp,
void *buffer,
- size_t *lenp)
+ size_t *lenp,
+ loff_t *ppos)
{
int c, ret, *valp = ctl->data;
__uint32_t vn_active;
- ret = proc_dointvec_minmax(ctl, write, filp, buffer, lenp);
+ ret = proc_dointvec_minmax(ctl, write, filp, buffer, lenp, ppos);
if (!ret && write && *valp) {
printk("XFS Clearing xfsstats\n");
/* No DMA */
#define MAX_DMA_CHANNELS 0
-/*
- * Only first 64MB of memory can be accessed via PCI.
- * We use GFP_DMA to allocate safe buffers to do map/unmap.
- * This is really ugly and we need a better way of specifying
- * DMA-capable regions of memory.
- */
-static inline void __arch_adjust_zones(int node, unsigned long *zone_size,
- unsigned long *zhole_size)
-{
- unsigned int sz = SZ_64M >> PAGE_SHIFT;
-
- /*
- * Only adjust if > 64M on current system
- */
- if (node || (zone_size[0] <= sz))
- return;
-
- zone_size[1] = zone_size[0] - sz;
- zone_size[0] = sz;
- zhole_size[1] = zhole_size[0];
- zhole_size[0] = 0;
-}
-
-#define arch_adjust_zones(node, size, holes) \
- __arch_adjust_zones(node, size, holes)
-
#endif /* _ASM_ARCH_DMA_H */
#ifndef __ASM_ARCH_MEMORY_H
#define __ASM_ARCH_MEMORY_H
+#include <asm/sizes.h>
+
/*
* Physical DRAM offset.
*/
#define PHYS_OFFSET (0x00000000UL)
+#ifndef __ASSEMBLY__
+
+/*
+ * Only first 64MB of memory can be accessed via PCI.
+ * We use GFP_DMA to allocate safe buffers to do map/unmap.
+ * This is really ugly and we need a better way of specifying
+ * DMA-capable regions of memory.
+ */
+static inline void __arch_adjust_zones(int node, unsigned long *zone_size,
+ unsigned long *zhole_size)
+{
+ unsigned int sz = SZ_64M >> PAGE_SHIFT;
+
+ /*
+ * Only adjust if > 64M on current system
+ */
+ if (node || (zone_size[0] <= sz))
+ return;
+
+ zone_size[1] = zone_size[0] - sz;
+ zone_size[0] = sz;
+ zhole_size[1] = zhole_size[0];
+ zhole_size[0] = 0;
+}
+
+#define arch_adjust_zones(node, size, holes) \
+ __arch_adjust_zones(node, size, holes)
+
+#define ISA_DMA_THRESHOLD (SZ_64M - 1)
+
+#endif
+
/*
* Virtual view <-> DMA view memory address translations
* virt_to_bus: Used to translate the virtual address to an
*/
extern void ixp4xx_map_io(void);
extern void ixp4xx_init_irq(void);
+extern void ixp4xx_init_time(void);
extern void ixp4xx_pci_preinit(void);
struct pci_sys_data;
extern int ixp4xx_setup(int nr, struct pci_sys_data *sys);
extern unsigned long s3c2410_hclk;
extern unsigned long s3c2410_fclk;
+/* external functions for GPIO support
+ *
+ * These allow various different clients to access the same GPIO
+ * registers without conflicting. If your driver only owns the entire
+ * GPIO register, then it is safe to ioremap/__raw_{read|write} to it.
+*/
+
+/* s3c2410_gpio_cfgpin
+ *
+ * set the configuration of the given pin to the value passed.
+ *
+ * eg:
+ * s3c2410_gpio_cfgpin(S3C2410_GPA0, S3C2410_GPA0_ADDR0);
+ * s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1);
+*/
+
+extern void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function);
+
+/* s3c2410_gpio_pullup
+ *
+ * configure the pull-up control on the given pin
+ *
+ * to = 1 => disable the pull-up
+ * 0 => enable the pull-up
+ *
+ * eg;
+ *
+ * s3c2410_gpio_pullup(S3C2410_GPB0, 0);
+ * s3c2410_gpio_pullup(S3C2410_GPE8, 0);
+*/
+
+extern void s3c2410_gpio_pullup(unsigned int pin, unsigned int to);
+
+extern void s3c2410_gpio_setpin(unsigned int pin, unsigned int to);
+
#endif /* __ASSEMBLY__ */
#include <asm/sizes.h>
/* linux/include/asm/hardware/s3c2410/
*
- * Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk>
- * http://www.simtec.co.uk/products/SWLINUX/
+ * Copyright (c) 2003,2004 Simtec Electronics <linux@simtec.co.uk>
+ * http://www.simtec.co.uk/products/SWLINUX/
*
* 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
* 19-06-2003 BJD Created file
* 23-06-2003 BJD Updated GSTATUS registers
* 12-03-2004 BJD Updated include protection
+ * 20-07-2004 BJD Added GPIO pin numbers, added Port A definitions
*/
#ifndef __ASM_ARCH_REGS_GPIO_H
#define __ASM_ARCH_REGS_GPIO_H "$Id: gpio.h,v 1.5 2003/05/19 12:51:08 ben Exp $"
+#define S3C2410_GPIONO(bank,offset) ((bank) + (offset))
+
+#define S3C2410_GPIO_BANKA (32*0)
+#define S3C2410_GPIO_BANKB (32*1)
+#define S3C2410_GPIO_BANKC (32*2)
+#define S3C2410_GPIO_BANKD (32*3)
+#define S3C2410_GPIO_BANKE (32*4)
+#define S3C2410_GPIO_BANKF (32*5)
+#define S3C2410_GPIO_BANKG (32*6)
+#define S3C2410_GPIO_BANKH (32*7)
+
+#define S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C2410_VA_GPIO)
+#define S3C2410_GPIO_OFFSET(pin) ((pin) & 31)
+
+/* general configuration options */
+
+#define S3C2410_GPIO_LEAVE (0xFFFFFFFF)
+
/* configure GPIO ports A..G */
#define S3C2410_GPIOREG(x) ((x) + S3C2410_VA_GPIO)
#define S3C2410_GPACON S3C2410_GPIOREG(0x00)
#define S3C2410_GPADAT S3C2410_GPIOREG(0x04)
+#define S3C2410_GPA0 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 0)
+#define S3C2410_GPA0_OUT (0<<0)
+#define S3C2410_GPA0_ADDR0 (1<<0)
+
+#define S3C2410_GPA1 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 1)
+#define S3C2410_GPA1_OUT (0<<1)
+#define S3C2410_GPA1_ADDR16 (1<<1)
+
+#define S3C2410_GPA2 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 2)
+#define S3C2410_GPA2_OUT (0<<2)
+#define S3C2410_GPA2_ADDR17 (1<<2)
+
+#define S3C2410_GPA3 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 3)
+#define S3C2410_GPA3_OUT (0<<3)
+#define S3C2410_GPA3_ADDR18 (1<<3)
+
+#define S3C2410_GPA4 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 4)
+#define S3C2410_GPA4_OUT (0<<4)
+#define S3C2410_GPA4_ADDR19 (1<<4)
+
+#define S3C2410_GPA5 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 5)
+#define S3C2410_GPA5_OUT (0<<5)
+#define S3C2410_GPA5_ADDR20 (1<<5)
+
+#define S3C2410_GPA6 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 6)
+#define S3C2410_GPA6_OUT (0<<6)
+#define S3C2410_GPA6_ADDR21 (1<<6)
+
+#define S3C2410_GPA7 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 7)
+#define S3C2410_GPA7_OUT (0<<7)
+#define S3C2410_GPA7_ADDR22 (1<<7)
+
+#define S3C2410_GPA8 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 8)
+#define S3C2410_GPA8_OUT (0<<8)
+#define S3C2410_GPA8_ADDR23 (1<<8)
+
+#define S3C2410_GPA9 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 9)
+#define S3C2410_GPA9_OUT (0<<9)
+#define S3C2410_GPA9_ADDR24 (1<<9)
+
+#define S3C2410_GPA10 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 10)
+#define S3C2410_GPA10_OUT (0<<10)
+#define S3C2410_GPA10_ADDR25 (1<<10)
+
+#define S3C2410_GPA11 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 11)
+#define S3C2410_GPA11_OUT (0<<11)
+#define S3C2410_GPA11_ADDR26 (1<<11)
+
+#define S3C2410_GPA12 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 12)
+#define S3C2410_GPA12_OUT (0<<12)
+#define S3C2410_GPA12_nGCS1 (1<<12)
+
+#define S3C2410_GPA13 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 13)
+#define S3C2410_GPA13_OUT (0<<13)
+#define S3C2410_GPA13_nGCS2 (1<<13)
+
+#define S3C2410_GPA14 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 14)
+#define S3C2410_GPA14_OUT (0<<14)
+#define S3C2410_GPA14_nGCS3 (1<<14)
+
+#define S3C2410_GPA15 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 15)
+#define S3C2410_GPA15_OUT (0<<15)
+#define S3C2410_GPA15_nGCS4 (1<<15)
+
+#define S3C2410_GPA16 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 16)
+#define S3C2410_GPA16_OUT (0<<16)
+#define S3C2410_GPA16_nGCS5 (1<<16)
+
+#define S3C2410_GPA17 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 17)
+#define S3C2410_GPA17_OUT (0<<17)
+#define S3C2410_GPA17_CLE (1<<17)
+
+#define S3C2410_GPA18 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 18)
+#define S3C2410_GPA18_OUT (0<<18)
+#define S3C2410_GPA18_ALE (1<<18)
+
+#define S3C2410_GPA19 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 19)
+#define S3C2410_GPA19_OUT (0<<19)
+#define S3C2410_GPA19_nFWE (1<<19)
+
+#define S3C2410_GPA20 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 20)
+#define S3C2410_GPA20_OUT (0<<20)
+#define S3C2410_GPA20_nFRE (1<<20)
+
+#define S3C2410_GPA21 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 21)
+#define S3C2410_GPA21_OUT (0<<21)
+#define S3C2410_GPA21_nRSTOUT (1<<21)
+
+#define S3C2410_GPA22 S3C2410_GPIONO(S3C2410_GPIO_BANKA, 22)
+#define S3C2410_GPA22_OUT (0<<22)
+#define S3C2410_GPA22_nFCE (1<<22)
+
/* 0x08 and 0x0c are reserved */
/* GPB is 10 IO pins, each configured by 2 bits each in GPBCON.
/* no i/o pin in port b can have value 3! */
+#define S3C2410_GPB0 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 0)
#define S3C2410_GPB0_INP (0x00 << 0)
#define S3C2410_GPB0_OUTP (0x01 << 0)
#define S3C2410_GPB0_TOUT0 (0x02 << 0)
+#define S3C2410_GPB1 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 1)
#define S3C2410_GPB1_INP (0x00 << 2)
#define S3C2410_GPB1_OUTP (0x01 << 2)
#define S3C2410_GPB1_TOUT1 (0x02 << 2)
+#define S3C2410_GPB2 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 2)
#define S3C2410_GPB2_INP (0x00 << 4)
#define S3C2410_GPB2_OUTP (0x01 << 4)
#define S3C2410_GPB2_TOUT2 (0x02 << 4)
+#define S3C2410_GPB3 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 3)
#define S3C2410_GPB3_INP (0x00 << 6)
#define S3C2410_GPB3_OUTP (0x01 << 6)
#define S3C2410_GPB3_TOUT3 (0x02 << 6)
+#define S3C2410_GPB4 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 4)
#define S3C2410_GPB4_INP (0x00 << 8)
#define S3C2410_GPB4_OUTP (0x01 << 8)
#define S3C2410_GPB4_TCLK0 (0x02 << 8)
#define S3C2410_GPB4_MASK (0x03 << 8)
+#define S3C2410_GPB5 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 5)
#define S3C2410_GPB5_INP (0x00 << 10)
#define S3C2410_GPB5_OUTP (0x01 << 10)
#define S3C2410_GPB5_nXBACK (0x02 << 10)
+#define S3C2410_GPB6 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 6)
#define S3C2410_GPB6_INP (0x00 << 12)
#define S3C2410_GPB6_OUTP (0x01 << 12)
#define S3C2410_GPB6_nXBREQ (0x02 << 12)
+#define S3C2410_GPB7 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 7)
#define S3C2410_GPB7_INP (0x00 << 14)
#define S3C2410_GPB7_OUTP (0x01 << 14)
#define S3C2410_GPB7_nXDACK1 (0x02 << 14)
+#define S3C2410_GPB8 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 8)
#define S3C2410_GPB8_INP (0x00 << 16)
#define S3C2410_GPB8_OUTP (0x01 << 16)
#define S3C2410_GPB8_nXDREQ1 (0x02 << 16)
+#define S3C2410_GPB9 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 9)
#define S3C2410_GPB9_INP (0x00 << 18)
#define S3C2410_GPB9_OUTP (0x01 << 18)
#define S3C2410_GPB9_nXDACK0 (0x02 << 18)
-#define S3C2410_GPB10_INP (0x00 << 18)
-#define S3C2410_GPB10_OUTP (0x01 << 18)
+#define S3C2410_GPB10 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 10)
+#define S3C2410_GPB10_INP (0x00 << 18)
+#define S3C2410_GPB10_OUTP (0x01 << 18)
#define S3C2410_GPB10_nXDRE0 (0x02 << 18)
/* Port C consits of 16 GPIO/Special function
#define S3C2410_GPCDAT S3C2410_GPIOREG(0x24)
#define S3C2410_GPCUP S3C2410_GPIOREG(0x28)
+#define S3C2410_GPC0 S3C2410_GPIONO(S3C2410_GPIO_BANKC, 0)
#define S3C2410_GPC0_INP (0x00 << 0)
#define S3C2410_GPC0_OUTP (0x01 << 0)
#define S3C2410_GPC0_LEND (0x02 << 0)
+#define S3C2410_GPC1 S3C2410_GPIONO(S3C2410_GPIO_BANKC, 1)
#define S3C2410_GPC1_INP (0x00 << 2)
#define S3C2410_GPC1_OUTP (0x01 << 2)
#define S3C2410_GPC1_VCLK (0x02 << 2)
+#define S3C2410_GPC2 S3C2410_GPIONO(S3C2410_GPIO_BANKC, 2)
#define S3C2410_GPC2_INP (0x00 << 4)
#define S3C2410_GPC2_OUTP (0x01 << 4)
#define S3C2410_GPC2_VLINE (0x02 << 4)
+#define S3C2410_GPC3 S3C2410_GPIONO(S3C2410_GPIO_BANKC, 3)
#define S3C2410_GPC3_INP (0x00 << 6)
#define S3C2410_GPC3_OUTP (0x01 << 6)
#define S3C2410_GPC3_VFRAME (0x02 << 6)
+#define S3C2410_GPC4 S3C2410_GPIONO(S3C2410_GPIO_BANKC, 4)
#define S3C2410_GPC4_INP (0x00 << 8)
#define S3C2410_GPC4_OUTP (0x01 << 8)
#define S3C2410_GPC4_VM (0x02 << 8)
+#define S3C2410_GPC5 S3C2410_GPIONO(S3C2410_GPIO_BANKC, 5)
#define S3C2410_GPC5_INP (0x00 << 10)
#define S3C2410_GPC5_OUTP (0x01 << 10)
#define S3C2410_GPC5_LCDVF0 (0x02 << 10)
+#define S3C2410_GPC6 S3C2410_GPIONO(S3C2410_GPIO_BANKC, 6)
#define S3C2410_GPC6_INP (0x00 << 12)
#define S3C2410_GPC6_OUTP (0x01 << 12)
#define S3C2410_GPC6_LCDVF1 (0x02 << 12)
+#define S3C2410_GPC7 S3C2410_GPIONO(S3C2410_GPIO_BANKC, 7)
#define S3C2410_GPC7_INP (0x00 << 14)
#define S3C2410_GPC7_OUTP (0x01 << 14)
#define S3C2410_GPC7_LCDVF2 (0x02 << 14)
+#define S3C2410_GPC8 S3C2410_GPIONO(S3C2410_GPIO_BANKC, 8)
#define S3C2410_GPC8_INP (0x00 << 16)
#define S3C2410_GPC8_OUTP (0x01 << 16)
#define S3C2410_GPC8_VD0 (0x02 << 16)
+#define S3C2410_GPC9 S3C2410_GPIONO(S3C2410_GPIO_BANKC, 9)
#define S3C2410_GPC9_INP (0x00 << 18)
#define S3C2410_GPC9_OUTP (0x01 << 18)
#define S3C2410_GPC9_VD1 (0x02 << 18)
+#define S3C2410_GPC10 S3C2410_GPIONO(S3C2410_GPIO_BANKC, 10)
#define S3C2410_GPC10_INP (0x00 << 20)
#define S3C2410_GPC10_OUTP (0x01 << 20)
#define S3C2410_GPC10_VD2 (0x02 << 20)
+#define S3C2410_GPC11 S3C2410_GPIONO(S3C2410_GPIO_BANKC, 11)
#define S3C2410_GPC11_INP (0x00 << 22)
#define S3C2410_GPC11_OUTP (0x01 << 22)
#define S3C2410_GPC11_VD3 (0x02 << 22)
+#define S3C2410_GPC12 S3C2410_GPIONO(S3C2410_GPIO_BANKC, 12)
#define S3C2410_GPC12_INP (0x00 << 24)
#define S3C2410_GPC12_OUTP (0x01 << 24)
#define S3C2410_GPC12_VD4 (0x02 << 24)
+#define S3C2410_GPC13 S3C2410_GPIONO(S3C2410_GPIO_BANKC, 13)
#define S3C2410_GPC13_INP (0x00 << 26)
#define S3C2410_GPC13_OUTP (0x01 << 26)
#define S3C2410_GPC13_VD5 (0x02 << 26)
+#define S3C2410_GPC14 S3C2410_GPIONO(S3C2410_GPIO_BANKC, 14)
#define S3C2410_GPC14_INP (0x00 << 28)
#define S3C2410_GPC14_OUTP (0x01 << 28)
#define S3C2410_GPC14_VD6 (0x02 << 28)
+#define S3C2410_GPC15 S3C2410_GPIONO(S3C2410_GPIO_BANKC, 15)
#define S3C2410_GPC15_INP (0x00 << 30)
#define S3C2410_GPC15_OUTP (0x01 << 30)
#define S3C2410_GPC15_VD7 (0x02 << 30)
#define S3C2410_GPDDAT S3C2410_GPIOREG(0x34)
#define S3C2410_GPDUP S3C2410_GPIOREG(0x38)
+#define S3C2410_GPD0 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 0)
#define S3C2410_GPD0_INP (0x00 << 0)
#define S3C2410_GPD0_OUTP (0x01 << 0)
#define S3C2410_GPD0_VD8 (0x02 << 0)
+#define S3C2410_GPD1 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 1)
#define S3C2410_GPD1_INP (0x00 << 2)
#define S3C2410_GPD1_OUTP (0x01 << 2)
#define S3C2410_GPD1_VD9 (0x02 << 2)
+#define S3C2410_GPD2 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 2)
#define S3C2410_GPD2_INP (0x00 << 4)
#define S3C2410_GPD2_OUTP (0x01 << 4)
#define S3C2410_GPD2_VD10 (0x02 << 4)
+#define S3C2410_GPD3 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 3)
#define S3C2410_GPD3_INP (0x00 << 6)
#define S3C2410_GPD3_OUTP (0x01 << 6)
#define S3C2410_GPD3_VD11 (0x02 << 6)
+#define S3C2410_GPD4 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 4)
#define S3C2410_GPD4_INP (0x00 << 8)
#define S3C2410_GPD4_OUTP (0x01 << 8)
#define S3C2410_GPD4_VD12 (0x02 << 8)
+#define S3C2410_GPD5 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 5)
#define S3C2410_GPD5_INP (0x00 << 10)
#define S3C2410_GPD5_OUTP (0x01 << 10)
#define S3C2410_GPD5_VD13 (0x02 << 10)
+#define S3C2410_GPD6 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 6)
#define S3C2410_GPD6_INP (0x00 << 12)
#define S3C2410_GPD6_OUTP (0x01 << 12)
#define S3C2410_GPD6_VD14 (0x02 << 12)
+#define S3C2410_GPD7 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 7)
#define S3C2410_GPD7_INP (0x00 << 14)
#define S3C2410_GPD7_OUTP (0x01 << 14)
#define S3C2410_GPD7_VD15 (0x02 << 14)
+#define S3C2410_GPD8 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 8)
#define S3C2410_GPD8_INP (0x00 << 16)
#define S3C2410_GPD8_OUTP (0x01 << 16)
#define S3C2410_GPD8_VD16 (0x02 << 16)
+#define S3C2410_GPD9 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 9)
#define S3C2410_GPD9_INP (0x00 << 18)
#define S3C2410_GPD9_OUTP (0x01 << 18)
#define S3C2410_GPD9_VD17 (0x02 << 18)
+#define S3C2410_GPD10 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 10)
#define S3C2410_GPD10_INP (0x00 << 20)
#define S3C2410_GPD10_OUTP (0x01 << 20)
#define S3C2410_GPD10_VD18 (0x02 << 20)
+#define S3C2410_GPD11 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 11)
#define S3C2410_GPD11_INP (0x00 << 22)
#define S3C2410_GPD11_OUTP (0x01 << 22)
#define S3C2410_GPD11_VD19 (0x02 << 22)
+#define S3C2410_GPD12 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 12)
#define S3C2410_GPD12_INP (0x00 << 24)
#define S3C2410_GPD12_OUTP (0x01 << 24)
#define S3C2410_GPD12_VD20 (0x02 << 24)
+#define S3C2410_GPD13 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 13)
#define S3C2410_GPD13_INP (0x00 << 26)
#define S3C2410_GPD13_OUTP (0x01 << 26)
#define S3C2410_GPD13_VD21 (0x02 << 26)
+#define S3C2410_GPD14 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 14)
#define S3C2410_GPD14_INP (0x00 << 28)
#define S3C2410_GPD14_OUTP (0x01 << 28)
#define S3C2410_GPD14_VD22 (0x02 << 28)
+#define S3C2410_GPD15 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 15)
#define S3C2410_GPD15_INP (0x00 << 30)
#define S3C2410_GPD15_OUTP (0x01 << 30)
#define S3C2410_GPD15_VD23 (0x02 << 30)
#define S3C2410_GPEDAT S3C2410_GPIOREG(0x44)
#define S3C2410_GPEUP S3C2410_GPIOREG(0x48)
+#define S3C2410_GPE0 S3C2410_GPIONO(S3C2410_GPIO_BANKE, 0)
#define S3C2410_GPE0_INP (0x00 << 0)
#define S3C2410_GPE0_OUTP (0x01 << 0)
#define S3C2410_GPE0_I2SLRCK (0x02 << 0)
#define S3C2410_GPE0_MASK (0x03 << 0)
+#define S3C2410_GPE1 S3C2410_GPIONO(S3C2410_GPIO_BANKE, 1)
#define S3C2410_GPE1_INP (0x00 << 2)
#define S3C2410_GPE1_OUTP (0x01 << 2)
#define S3C2410_GPE1_I2SSCLK (0x02 << 2)
#define S3C2410_GPE1_MASK (0x03 << 2)
+#define S3C2410_GPE2 S3C2410_GPIONO(S3C2410_GPIO_BANKE, 2)
#define S3C2410_GPE2_INP (0x00 << 4)
#define S3C2410_GPE2_OUTP (0x01 << 4)
#define S3C2410_GPE2_CDCLK (0x02 << 4)
+#define S3C2410_GPE3 S3C2410_GPIONO(S3C2410_GPIO_BANKE, 3)
#define S3C2410_GPE3_INP (0x00 << 6)
#define S3C2410_GPE3_OUTP (0x01 << 6)
#define S3C2410_GPE3_I2SSDI (0x02 << 6)
#define S3C2410_GPE3_MASK (0x03 << 6)
+#define S3C2410_GPE4 S3C2410_GPIONO(S3C2410_GPIO_BANKE, 4)
#define S3C2410_GPE4_INP (0x00 << 8)
#define S3C2410_GPE4_OUTP (0x01 << 8)
#define S3C2410_GPE4_I2SSDO (0x02 << 8)
#define S3C2410_GPE4_MASK (0x03 << 8)
+#define S3C2410_GPE5 S3C2410_GPIONO(S3C2410_GPIO_BANKE, 5)
#define S3C2410_GPE5_INP (0x00 << 10)
#define S3C2410_GPE5_OUTP (0x01 << 10)
#define S3C2410_GPE5_SDCLK (0x02 << 10)
+#define S3C2410_GPE6 S3C2410_GPIONO(S3C2410_GPIO_BANKE, 6)
#define S3C2410_GPE6_INP (0x00 << 12)
#define S3C2410_GPE6_OUTP (0x01 << 12)
#define S3C2410_GPE6_SDCLK (0x02 << 12)
+#define S3C2410_GPE7 S3C2410_GPIONO(S3C2410_GPIO_BANKE, 7)
#define S3C2410_GPE7_INP (0x00 << 14)
#define S3C2410_GPE7_OUTP (0x01 << 14)
#define S3C2410_GPE7_SDCMD (0x02 << 14)
+#define S3C2410_GPE8 S3C2410_GPIONO(S3C2410_GPIO_BANKE, 8)
#define S3C2410_GPE8_INP (0x00 << 16)
#define S3C2410_GPE8_OUTP (0x01 << 16)
#define S3C2410_GPE8_SDDAT1 (0x02 << 16)
+#define S3C2410_GPE9 S3C2410_GPIONO(S3C2410_GPIO_BANKE, 9)
#define S3C2410_GPE9_INP (0x00 << 18)
#define S3C2410_GPE9_OUTP (0x01 << 18)
#define S3C2410_GPE9_SDDAT2 (0x02 << 18)
+#define S3C2410_GPE10 S3C2410_GPIONO(S3C2410_GPIO_BANKE, 10)
#define S3C2410_GPE10_INP (0x00 << 20)
#define S3C2410_GPE10_OUTP (0x01 << 20)
#define S3C2410_GPE10_SDDAT3 (0x02 << 20)
+#define S3C2410_GPE11 S3C2410_GPIONO(S3C2410_GPIO_BANKE, 11)
#define S3C2410_GPE11_INP (0x00 << 22)
#define S3C2410_GPE11_OUTP (0x01 << 22)
#define S3C2410_GPE11_SPIMISO0 (0x02 << 22)
+#define S3C2410_GPE12 S3C2410_GPIONO(S3C2410_GPIO_BANKE, 12)
#define S3C2410_GPE12_INP (0x00 << 24)
#define S3C2410_GPE12_OUTP (0x01 << 24)
#define S3C2410_GPE12_SPIMOSI0 (0x02 << 24)
+#define S3C2410_GPE13 S3C2410_GPIONO(S3C2410_GPIO_BANKE, 13)
#define S3C2410_GPE13_INP (0x00 << 26)
#define S3C2410_GPE13_OUTP (0x01 << 26)
#define S3C2410_GPE13_SPICLK0 (0x02 << 26)
+#define S3C2410_GPE14 S3C2410_GPIONO(S3C2410_GPIO_BANKE, 14)
#define S3C2410_GPE14_INP (0x00 << 28)
#define S3C2410_GPE14_OUTP (0x01 << 28)
#define S3C2410_GPE14_IICSCL (0x02 << 28)
#define S3C2410_GPE14_MASK (0x03 << 28)
+#define S3C2410_GPE15 S3C2410_GPIONO(S3C2410_GPIO_BANKE, 15)
#define S3C2410_GPE15_INP (0x00 << 30)
#define S3C2410_GPE15_OUTP (0x01 << 30)
#define S3C2410_GPE15_IICSDA (0x02 << 30)
#define S3C2410_GPFDAT S3C2410_GPIOREG(0x54)
#define S3C2410_GPFUP S3C2410_GPIOREG(0x58)
-
+#define S3C2410_GPF0 S3C2410_GPIONO(S3C2410_GPIO_BANKF, 0)
#define S3C2410_GPF0_INP (0x00 << 0)
#define S3C2410_GPF0_OUTP (0x01 << 0)
#define S3C2410_GPF0_EINT0 (0x02 << 0)
+#define S3C2410_GPF1 S3C2410_GPIONO(S3C2410_GPIO_BANKF, 1)
#define S3C2410_GPF1_INP (0x00 << 2)
#define S3C2410_GPF1_OUTP (0x01 << 2)
#define S3C2410_GPF1_EINT1 (0x02 << 2)
+#define S3C2410_GPF2 S3C2410_GPIONO(S3C2410_GPIO_BANKF, 2)
#define S3C2410_GPF2_INP (0x00 << 4)
#define S3C2410_GPF2_OUTP (0x01 << 4)
#define S3C2410_GPF2_EINT2 (0x02 << 4)
+#define S3C2410_GPF3 S3C2410_GPIONO(S3C2410_GPIO_BANKF, 3)
#define S3C2410_GPF3_INP (0x00 << 6)
#define S3C2410_GPF3_OUTP (0x01 << 6)
#define S3C2410_GPF3_EINT3 (0x02 << 6)
+#define S3C2410_GPF4 S3C2410_GPIONO(S3C2410_GPIO_BANKF, 4)
#define S3C2410_GPF4_INP (0x00 << 8)
#define S3C2410_GPF4_OUTP (0x01 << 8)
#define S3C2410_GPF4_EINT4 (0x02 << 8)
+#define S3C2410_GPF5 S3C2410_GPIONO(S3C2410_GPIO_BANKF, 5)
#define S3C2410_GPF5_INP (0x00 << 10)
#define S3C2410_GPF5_OUTP (0x01 << 10)
#define S3C2410_GPF5_EINT5 (0x02 << 10)
+#define S3C2410_GPF6 S3C2410_GPIONO(S3C2410_GPIO_BANKF, 6)
#define S3C2410_GPF6_INP (0x00 << 12)
#define S3C2410_GPF6_OUTP (0x01 << 12)
#define S3C2410_GPF6_EINT6 (0x02 << 12)
+#define S3C2410_GPF7 S3C2410_GPIONO(S3C2410_GPIO_BANKF, 7)
#define S3C2410_GPF7_INP (0x00 << 14)
#define S3C2410_GPF7_OUTP (0x01 << 14)
#define S3C2410_GPF7_EINT7 (0x02 << 14)
#define S3C2410_GPGDAT S3C2410_GPIOREG(0x64)
#define S3C2410_GPGUP S3C2410_GPIOREG(0x68)
+#define S3C2410_GPG0 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 0)
#define S3C2410_GPG0_INP (0x00 << 0)
#define S3C2410_GPG0_OUTP (0x01 << 0)
#define S3C2410_GPG0_EINT8 (0x02 << 0)
+#define S3C2410_GPG1 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 1)
#define S3C2410_GPG1_INP (0x00 << 2)
#define S3C2410_GPG1_OUTP (0x01 << 2)
#define S3C2410_GPG1_EINT9 (0x02 << 2)
+#define S3C2410_GPG2 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 2)
#define S3C2410_GPG2_INP (0x00 << 4)
#define S3C2410_GPG2_OUTP (0x01 << 4)
#define S3C2410_GPG2_EINT10 (0x02 << 4)
+#define S3C2410_GPG3 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 3)
#define S3C2410_GPG3_INP (0x00 << 6)
#define S3C2410_GPG3_OUTP (0x01 << 6)
#define S3C2410_GPG3_EINT11 (0x02 << 6)
+#define S3C2410_GPG4 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 4)
#define S3C2410_GPG4_INP (0x00 << 8)
#define S3C2410_GPG4_OUTP (0x01 << 8)
#define S3C2410_GPG4_EINT12 (0x02 << 8)
#define S3C2410_GPG4_LCDPWREN (0x03 << 8)
+#define S3C2410_GPG5 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 5)
#define S3C2410_GPG5_INP (0x00 << 10)
#define S3C2410_GPG5_OUTP (0x01 << 10)
#define S3C2410_GPG5_EINT13 (0x02 << 10)
#define S3C2410_GPG5_SPIMISO1 (0x03 << 10)
+#define S3C2410_GPG6 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 6)
#define S3C2410_GPG6_INP (0x00 << 12)
#define S3C2410_GPG6_OUTP (0x01 << 12)
#define S3C2410_GPG6_EINT14 (0x02 << 12)
#define S3C2410_GPG6_SPIMOSI1 (0x03 << 12)
+#define S3C2410_GPG7 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 7)
#define S3C2410_GPG7_INP (0x00 << 14)
#define S3C2410_GPG7_OUTP (0x01 << 14)
#define S3C2410_GPG7_EINT15 (0x02 << 14)
#define S3C2410_GPG7_SPICLK1 (0x03 << 14)
+#define S3C2410_GPG8 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 8)
#define S3C2410_GPG8_INP (0x00 << 16)
#define S3C2410_GPG8_OUTP (0x01 << 16)
#define S3C2410_GPG8_EINT16 (0x02 << 16)
+#define S3C2410_GPG9 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 9)
#define S3C2410_GPG9_INP (0x00 << 18)
#define S3C2410_GPG9_OUTP (0x01 << 18)
#define S3C2410_GPG9_EINT17 (0x02 << 18)
+#define S3C2410_GPG10 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 10)
#define S3C2410_GPG10_INP (0x00 << 20)
#define S3C2410_GPG10_OUTP (0x01 << 20)
#define S3C2410_GPG10_EINT18 (0x02 << 20)
+#define S3C2410_GPG10 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 10)
#define S3C2410_GPG11_INP (0x00 << 22)
#define S3C2410_GPG11_OUTP (0x01 << 22)
#define S3C2410_GPG11_EINT19 (0x02 << 22)
#define S3C2410_GPG11_TCLK1 (0x03 << 22)
+#define S3C2410_GPG10 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 10)
#define S3C2410_GPG12_INP (0x00 << 24)
#define S3C2410_GPG12_OUTP (0x01 << 24)
#define S3C2410_GPG12_EINT18 (0x02 << 24)
#define S3C2410_GPG12_XMON (0x03 << 24)
+#define S3C2410_GPG10 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 10)
#define S3C2410_GPG13_INP (0x00 << 26)
#define S3C2410_GPG13_OUTP (0x01 << 26)
#define S3C2410_GPG13_EINT18 (0x02 << 26)
#define S3C2410_GPG13_nXPON (0x03 << 26)
+#define S3C2410_GPG10 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 10)
#define S3C2410_GPG14_INP (0x00 << 28)
#define S3C2410_GPG14_OUTP (0x01 << 28)
#define S3C2410_GPG14_EINT18 (0x02 << 28)
#define S3C2410_GPG14_YMON (0x03 << 28)
+#define S3C2410_GPG10 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 10)
#define S3C2410_GPG15_INP (0x00 << 30)
#define S3C2410_GPG15_OUTP (0x01 << 30)
#define S3C2410_GPG15_EINT18 (0x02 << 30)
#define S3C2410_GPHDAT S3C2410_GPIOREG(0x74)
#define S3C2410_GPHUP S3C2410_GPIOREG(0x78)
+#define S3C2410_GPH0 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 0)
#define S3C2410_GPH0_INP (0x00 << 0)
#define S3C2410_GPH0_OUTP (0x01 << 0)
#define S3C2410_GPH0_nCTS0 (0x02 << 0)
+#define S3C2410_GPH1 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 1)
#define S3C2410_GPH1_INP (0x00 << 2)
#define S3C2410_GPH1_OUTP (0x01 << 2)
#define S3C2410_GPH1_nRTS0 (0x02 << 2)
+#define S3C2410_GPH2 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 2)
#define S3C2410_GPH2_INP (0x00 << 4)
#define S3C2410_GPH2_OUTP (0x01 << 4)
#define S3C2410_GPH2_TXD0 (0x02 << 4)
+#define S3C2410_GPH3 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 3)
#define S3C2410_GPH3_INP (0x00 << 6)
#define S3C2410_GPH3_OUTP (0x01 << 6)
#define S3C2410_GPH3_RXD0 (0x02 << 6)
+#define S3C2410_GPH4 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 4)
#define S3C2410_GPH4_INP (0x00 << 8)
#define S3C2410_GPH4_OUTP (0x01 << 8)
#define S3C2410_GPH4_TXD1 (0x02 << 8)
+#define S3C2410_GPH5 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 5)
#define S3C2410_GPH5_INP (0x00 << 10)
#define S3C2410_GPH5_OUTP (0x01 << 10)
#define S3C2410_GPH5_RXD1 (0x02 << 10)
+#define S3C2410_GPH6 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 6)
#define S3C2410_GPH6_INP (0x00 << 12)
#define S3C2410_GPH6_OUTP (0x01 << 12)
#define S3C2410_GPH6_TXD2 (0x02 << 12)
#define S3C2410_GPH6_nRTS1 (0x03 << 12)
+#define S3C2410_GPH7 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 7)
#define S3C2410_GPH7_INP (0x00 << 14)
#define S3C2410_GPH7_OUTP (0x01 << 14)
#define S3C2410_GPH7_RXD2 (0x02 << 14)
#define S3C2410_GPH7_nCTS1 (0x03 << 14)
+#define S3C2410_GPH8 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 8)
#define S3C2410_GPH8_INP (0x00 << 16)
#define S3C2410_GPH8_OUTP (0x01 << 16)
#define S3C2410_GPH8_UCLK (0x02 << 16)
-#define S3C2410_GPH9_INP (0x00 << 18)
-#define S3C2410_GPH9_OUTP (0x01 << 18)
-#define S3C2410_GPH9_CLKOUT0 (0x02 << 18)
+#define S3C2410_GPH9 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 9)
+#define S3C2410_GPH9_INP (0x00 << 18)
+#define S3C2410_GPH9_OUTP (0x01 << 18)
+#define S3C2410_GPH9_CLKOUT0 (0x02 << 18)
-#define S3C2410_GPH10_INP (0x00 << 20)
-#define S3C2410_GPH10_OUTP (0x01 << 20)
-#define S3C2410_GPH10_CLKOUT1 (0x02 << 20)
+#define S3C2410_GPH10 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 10)
+#define S3C2410_GPH10_INP (0x00 << 20)
+#define S3C2410_GPH10_OUTP (0x01 << 20)
+#define S3C2410_GPH10_CLKOUT1 (0x02 << 20)
/* miscellaneous control */
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-sa1100/collie.h
+ *
+ * This file contains the hardware specific definitions for Assabet
+ * Only include this file from SA1100-specific files.
+ *
+ * ChangeLog:
+ * 04-06-2001 Lineo Japan, Inc.
+ * 04-16-2001 SHARP Corporation
+ * 07-07-2002 Chris Larson <clarson@digi.com>
+ *
+ */
+#ifndef __ASM_ARCH_COLLIE_H
+#define __ASM_ARCH_COLLIE_H
+
+#include <linux/config.h>
+
+#define CF_BUF_CTRL_BASE 0xF0800000
+#define COLLIE_SCP_REG(adr) (*(volatile unsigned short*)(CF_BUF_CTRL_BASE+(adr)))
+#define COLLIE_SCP_MCR 0x00
+#define COLLIE_SCP_CDR 0x04
+#define COLLIE_SCP_CSR 0x08
+#define COLLIE_SCP_CPR 0x0C
+#define COLLIE_SCP_CCR 0x10
+#define COLLIE_SCP_IRR 0x14
+#define COLLIE_SCP_IRM 0x14
+#define COLLIE_SCP_IMR 0x18
+#define COLLIE_SCP_ISR 0x1C
+#define COLLIE_SCP_GPCR 0x20
+#define COLLIE_SCP_GPWR 0x24
+#define COLLIE_SCP_GPRR 0x28
+#define COLLIE_SCP_REG_MCR COLLIE_SCP_REG(COLLIE_SCP_MCR)
+#define COLLIE_SCP_REG_CDR COLLIE_SCP_REG(COLLIE_SCP_CDR)
+#define COLLIE_SCP_REG_CSR COLLIE_SCP_REG(COLLIE_SCP_CSR)
+#define COLLIE_SCP_REG_CPR COLLIE_SCP_REG(COLLIE_SCP_CPR)
+#define COLLIE_SCP_REG_CCR COLLIE_SCP_REG(COLLIE_SCP_CCR)
+#define COLLIE_SCP_REG_IRR COLLIE_SCP_REG(COLLIE_SCP_IRR)
+#define COLLIE_SCP_REG_IRM COLLIE_SCP_REG(COLLIE_SCP_IRM)
+#define COLLIE_SCP_REG_IMR COLLIE_SCP_REG(COLLIE_SCP_IMR)
+#define COLLIE_SCP_REG_ISR COLLIE_SCP_REG(COLLIE_SCP_ISR)
+#define COLLIE_SCP_REG_GPCR COLLIE_SCP_REG(COLLIE_SCP_GPCR)
+#define COLLIE_SCP_REG_GPWR COLLIE_SCP_REG(COLLIE_SCP_GPWR)
+#define COLLIE_SCP_REG_GPRR COLLIE_SCP_REG(COLLIE_SCP_GPRR)
+
+#define COLLIE_SCP_GPCR_PA19 ( 1 << 9 )
+#define COLLIE_SCP_GPCR_PA18 ( 1 << 8 )
+#define COLLIE_SCP_GPCR_PA17 ( 1 << 7 )
+#define COLLIE_SCP_GPCR_PA16 ( 1 << 6 )
+#define COLLIE_SCP_GPCR_PA15 ( 1 << 5 )
+#define COLLIE_SCP_GPCR_PA14 ( 1 << 4 )
+#define COLLIE_SCP_GPCR_PA13 ( 1 << 3 )
+#define COLLIE_SCP_GPCR_PA12 ( 1 << 2 )
+#define COLLIE_SCP_GPCR_PA11 ( 1 << 1 )
+
+#define COLLIE_SCP_CHARGE_ON COLLIE_SCP_GPCR_PA11
+#define COLLIE_SCP_DIAG_BOOT1 COLLIE_SCP_GPCR_PA12
+#define COLLIE_SCP_DIAG_BOOT2 COLLIE_SCP_GPCR_PA13
+#define COLLIE_SCP_MUTE_L COLLIE_SCP_GPCR_PA14
+#define COLLIE_SCP_MUTE_R COLLIE_SCP_GPCR_PA15
+#define COLLIE_SCP_5VON COLLIE_SCP_GPCR_PA16
+#define COLLIE_SCP_AMP_ON COLLIE_SCP_GPCR_PA17
+#define COLLIE_SCP_VPEN COLLIE_SCP_GPCR_PA18
+#define COLLIE_SCP_LB_VOL_CHG COLLIE_SCP_GPCR_PA19
+
+#define COLLIE_SCP_IO_DIR ( COLLIE_SCP_CHARGE_ON | COLLIE_SCP_MUTE_L | COLLIE_SCP_MUTE_R | \
+ COLLIE_SCP_5VON | COLLIE_SCP_AMP_ON | COLLIE_SCP_VPEN | \
+ COLLIE_SCP_LB_VOL_CHG )
+#define COLLIE_SCP_IO_OUT ( COLLIE_SCP_MUTE_L | COLLIE_SCP_MUTE_R | COLLIE_SCP_VPEN | \
+ COLLIE_SCP_CHARGE_ON )
+
+/* GPIOs for which the generic definition doesn't say much */
+
+#define COLLIE_GPIO_ON_KEY GPIO_GPIO (0)
+#define COLLIE_GPIO_AC_IN GPIO_GPIO (1)
+#define COLLIE_GPIO_CF_IRQ GPIO_GPIO (14)
+#define COLLIE_GPIO_nREMOCON_INT GPIO_GPIO (15)
+#define COLLIE_GPIO_UCB1x00_RESET GPIO_GPIO (16)
+#define COLLIE_GPIO_CO GPIO_GPIO (20)
+#define COLLIE_GPIO_MCP_CLK GPIO_GPIO (21)
+#define COLLIE_GPIO_CF_CD GPIO_GPIO (22)
+#define COLLIE_GPIO_UCB1x00_IRQ GPIO_GPIO (23)
+#define COLLIE_GPIO_WAKEUP GPIO_GPIO (24)
+#define COLLIE_GPIO_GA_INT GPIO_GPIO (25)
+#define COLLIE_GPIO_MAIN_BAT_LOW GPIO_GPIO (26)
+
+/* Interrupts */
+
+#define COLLIE_IRQ_GPIO_ON_KEY IRQ_GPIO0
+#define COLLIE_IRQ_GPIO_AC_IN IRQ_GPIO1
+#define COLLIE_IRQ_GPIO_CF_IRQ IRQ_GPIO14
+#define COLLIE_IRQ_GPIO_nREMOCON_INT IRQ_GPIO15
+#define COLLIE_IRQ_GPIO_CO IRQ_GPIO20
+#define COLLIE_IRQ_GPIO_CF_CD IRQ_GPIO22
+#define COLLIE_IRQ_GPIO_UCB1x00_IRQ IRQ_GPIO23
+#define COLLIE_IRQ_GPIO_WAKEUP IRQ_GPIO24
+#define COLLIE_IRQ_GPIO_GA_INT IRQ_GPIO25
+#define COLLIE_IRQ_GPIO_MAIN_BAT_LOW IRQ_GPIO26
+
+#define COLLIE_LCM_IRQ_GPIO_RTS IRQ_LOCOMO_GPIO0
+#define COLLIE_LCM_IRQ_GPIO_CTS IRQ_LOCOMO_GPIO1
+#define COLLIE_LCM_IRQ_GPIO_DSR IRQ_LOCOMO_GPIO2
+#define COLLIE_LCM_IRQ_GPIO_DTR IRQ_LOCOMO_GPIO3
+#define COLLIE_LCM_IRQ_GPIO_nSD_DETECT IRQ_LOCOMO_GPIO13
+#define COLLIE_LCM_IRQ_GPIO_nSD_WP IRQ_LOCOMO_GPIO14
+
+/*
+ * Flash Memory mappings
+ *
+ */
+
+#define FLASH_MEM_BASE 0xe8ffc000
+#define FLASH_DATA(adr) (*(volatile unsigned int*)(FLASH_MEM_BASE+(adr)))
+#define FLASH_DATA_F(adr) (*(volatile float32 *)(FLASH_MEM_BASE+(adr)))
+#define FLASH_MAGIC_CHG(a,b,c,d) ( ( d << 24 ) | ( c << 16 ) | ( b << 8 ) | a )
+
+// COMADJ
+#define FLASH_COMADJ_MAJIC FLASH_MAGIC_CHG('C','M','A','D')
+#define FLASH_COMADJ_MAGIC_ADR 0x00
+#define FLASH_COMADJ_DATA_ADR 0x04
+
+// TOUCH PANEL
+#define FLASH_TOUCH_MAJIC FLASH_MAGIC_CHG('T','U','C','H')
+#define FLASH_TOUCH_MAGIC_ADR 0x1C
+#define FLASH_TOUCH_XP_DATA_ADR 0x20
+#define FLASH_TOUCH_YP_DATA_ADR 0x24
+#define FLASH_TOUCH_XD_DATA_ADR 0x28
+#define FLASH_TOUCH_YD_DATA_ADR 0x2C
+
+// AD
+#define FLASH_AD_MAJIC FLASH_MAGIC_CHG('B','V','A','D')
+#define FLASH_AD_MAGIC_ADR 0x30
+#define FLASH_AD_DATA_ADR 0x34
+
+/* GPIO's on the TC35143AF (Toshiba Analog Frontend) */
+#define COLLIE_TC35143_GPIO_VERSION0 UCB_IO_0 /* GPIO0=Version */
+#define COLLIE_TC35143_GPIO_TBL_CHK UCB_IO_1 /* GPIO1=TBL_CHK */
+#define COLLIE_TC35143_GPIO_VPEN_ON UCB_IO_2 /* GPIO2=VPNE_ON */
+#define COLLIE_TC35143_GPIO_IR_ON UCB_IO_3 /* GPIO3=IR_ON */
+#define COLLIE_TC35143_GPIO_AMP_ON UCB_IO_4 /* GPIO4=AMP_ON */
+#define COLLIE_TC35143_GPIO_VERSION1 UCB_IO_5 /* GPIO5=Version */
+#define COLLIE_TC35143_GPIO_FS8KLPF UCB_IO_5 /* GPIO5=fs 8k LPF */
+#define COLLIE_TC35143_GPIO_BUZZER_BIAS UCB_IO_6 /* GPIO6=BUZZER BIAS */
+#define COLLIE_TC35143_GPIO_MBAT_ON UCB_IO_7 /* GPIO7=MBAT_ON */
+#define COLLIE_TC35143_GPIO_BBAT_ON UCB_IO_8 /* GPIO8=BBAT_ON */
+#define COLLIE_TC35143_GPIO_TMP_ON UCB_IO_9 /* GPIO9=TMP_ON */
+#define COLLIE_TC35143_GPIO_IN ( UCB_IO_0 | UCB_IO_2 | UCB_IO_5 )
+#define COLLIE_TC35143_GPIO_OUT ( UCB_IO_1 | UCB_IO_3 | UCB_IO_4 | UCB_IO_6 | \
+ UCB_IO_7 | UCB_IO_8 | UCB_IO_9 )
+
+#endif
extern int _find_first_zero_bit_be(void * p, unsigned size);
extern int _find_next_zero_bit_be(void * p, int size, int offset);
extern int _find_first_bit_be(const unsigned long *p, unsigned size);
-extern int _find_next_bit_be(unsigned long *p, int size, int offset);
+extern int _find_next_bit_be(const unsigned long *p, int size, int offset);
/*
* The __* form of bitops are non-atomic and may be reordered.
* Returns success (0) or negative errno.
*/
int clk_set_parent(struct clk *clk, struct clk *parent);
-
+
/**
* clk_get_parent - get the parent clock source for this clock
* @clk: clock source
--- /dev/null
+/*
+ * linux/include/asm-arm/hardware/locomo.h
+ *
+ * This file contains the definitions for the LoCoMo G/A Chip
+ *
+ * (C) Copyright 2004 John Lenz
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * Based on sa1111.h
+ */
+#ifndef _ASM_ARCH_LOCOMO
+#define _ASM_ARCH_LOCOMO
+
+#define locomo_writel(val,addr) ({ *(volatile u16 *)(addr) = (val); })
+#define locomo_readl(addr) (*(volatile u16 *)(addr))
+
+/* LOCOMO version */
+#define LOCOMO_VER 0x00
+
+/* Pin status */
+#define LOCOMO_ST 0x04
+
+/* Pin status */
+#define LOCOMO_C32K 0x08
+
+/* Interrupt controller */
+#define LOCOMO_ICR 0x0C
+
+/* MCS decoder for boot selecting */
+#define LOCOMO_MCSX0 0x10
+#define LOCOMO_MCSX1 0x14
+#define LOCOMO_MCSX2 0x18
+#define LOCOMO_MCSX3 0x1c
+
+/* Touch panel controller */
+#define LOCOMO_ASD 0x20 /* AD start delay */
+#define LOCOMO_HSD 0x28 /* HSYS delay */
+#define LOCOMO_HSC 0x2c /* HSYS period */
+#define LOCOMO_TADC 0x30 /* tablet ADC clock */
+
+/* TFT signal */
+#define LOCOMO_TC 0x38 /* TFT control signal */
+#define LOCOMO_CPSD 0x3c /* CPS delay */
+
+/* Key controller */
+#define LOCOMO_KIB 0x40 /* KIB level */
+#define LOCOMO_KSC 0x44 /* KSTRB control */
+#define LOCOMO_KCMD 0x48 /* KSTRB command */
+#define LOCOMO_KIC 0x4c /* Key interrupt */
+
+/* Audio clock */
+#define LOCOMO_ACC 0x54
+
+/* SPI interface */
+#define LOCOMO_SPIMD 0x60 /* SPI mode setting */
+#define LOCOMO_SPICT 0x64 /* SPI mode control */
+#define LOCOMO_SPIST 0x68 /* SPI status */
+#define LOCOMO_SPIIS 0x70 /* SPI interrupt status */
+#define LOCOMO_SPIWE 0x74 /* SPI interrupt status write enable */
+#define LOCOMO_SPIIE 0x78 /* SPI interrupt enable */
+#define LOCOMO_SPIIR 0x7c /* SPI interrupt request */
+#define LOCOMO_SPITD 0x80 /* SPI transfer data write */
+#define LOCOMO_SPIRD 0x84 /* SPI receive data read */
+#define LOCOMO_SPITS 0x88 /* SPI transfer data shift */
+#define LOCOMO_SPIRS 0x8C /* SPI receive data shift */
+
+#define LOCOMO_SPI_TEND (1 << 3) /* Transfer end bit */
+#define LOCOMO_SPI_OVRN (1 << 2) /* Over Run bit */
+#define LOCOMO_SPI_RFW (1 << 1) /* write buffer bit */
+#define LOCOMO_SPI_RFR (1) /* read buffer bit */
+
+/* GPIO */
+#define LOCOMO_GPD 0x90 /* GPIO direction */
+#define LOCOMO_GPE 0x94 /* GPIO input enable */
+#define LOCOMO_GPL 0x98 /* GPIO level */
+#define LOCOMO_GPO 0x9c /* GPIO out data setteing */
+#define LOCOMO_GRIE 0xa0 /* GPIO rise detection */
+#define LOCOMO_GFIE 0xa4 /* GPIO fall detection */
+#define LOCOMO_GIS 0xa8 /* GPIO edge detection status */
+#define LOCOMO_GWE 0xac /* GPIO status write enable */
+#define LOCOMO_GIE 0xb0 /* GPIO interrupt enable */
+#define LOCOMO_GIR 0xb4 /* GPIO interrupt request */
+
+#define LOCOMO_GPIO0 (1<<0)
+#define LOCOMO_GPIO1 (1<<1)
+#define LOCOMO_GPIO2 (1<<2)
+#define LOCOMO_GPIO3 (1<<3)
+#define LOCOMO_GPIO4 (1<<4)
+#define LOCOMO_GPIO5 (1<<5)
+#define LOCOMO_GPIO6 (1<<6)
+#define LOCOMO_GPIO7 (1<<7)
+#define LOCOMO_GPIO8 (1<<8)
+#define LOCOMO_GPIO9 (1<<9)
+#define LOCOMO_GPIO10 (1<<10)
+#define LOCOMO_GPIO11 (1<<11)
+#define LOCOMO_GPIO12 (1<<12)
+#define LOCOMO_GPIO13 (1<<13)
+#define LOCOMO_GPIO14 (1<<14)
+#define LOCOMO_GPIO15 (1<<15)
+
+/* Front light adjustment controller */
+#define LOCOMO_ALS 0xc8 /* Adjust light cycle */
+#define LOCOMO_ALD 0xcc /* Adjust light duty */
+
+/* PCM audio interface */
+#define LOCOMO_PAIF 0xd0
+
+/* Long time timer */
+#define LOCOMO_LTC 0xd8 /* LTC interrupt setting */
+#define LOCOMO_LTINT 0xdc /* LTC interrupt */
+
+/* DAC control signal for LCD (COMADJ ) */
+#define LOCOMO_DAC 0xe0
+
+/* DAC control */
+#define LOCOMO_DAC_SCLOEB 0x08 /* SCL pin output data */
+#define LOCOMO_DAC_TEST 0x04 /* Test bit */
+#define LOCOMO_DAC_SDA 0x02 /* SDA pin level (read-only) */
+#define LOCOMO_DAC_SDAOEB 0x01 /* SDA pin output data */
+
+/* LED controller */
+#define LOCOMO_LPT0 0xe8 /* LEDPWM0 timer */
+#define LOCOMO_LPT1 0xec /* LEDPWM1 timer */
+
+#define LOCOMO_LPT_TOFH 0x80 /* */
+#define LOCOMO_LPT_TOFL 0x08 /* */
+#define LOCOMO_LPT_TOH(TOH) ((TOH & 0x7) << 4) /* */
+#define LOCOMO_LPT_TOL(TOL) ((TOL & 0x7)) /* */
+
+/* Audio clock */
+#define LOCOMO_ACC_XON 0x80 /* */
+#define LOCOMO_ACC_XEN 0x40 /* */
+#define LOCOMO_ACC_XSEL0 0x00 /* */
+#define LOCOMO_ACC_XSEL1 0x20 /* */
+#define LOCOMO_ACC_MCLKEN 0x10 /* */
+#define LOCOMO_ACC_64FSEN 0x08 /* */
+#define LOCOMO_ACC_CLKSEL000 0x00 /* mclk 2 */
+#define LOCOMO_ACC_CLKSEL001 0x01 /* mclk 3 */
+#define LOCOMO_ACC_CLKSEL010 0x02 /* mclk 4 */
+#define LOCOMO_ACC_CLKSEL011 0x03 /* mclk 6 */
+#define LOCOMO_ACC_CLKSEL100 0x04 /* mclk 8 */
+#define LOCOMO_ACC_CLKSEL101 0x05 /* mclk 12 */
+
+/* PCM audio interface */
+#define LOCOMO_PAIF_SCINV 0x20 /* */
+#define LOCOMO_PAIF_SCEN 0x10 /* */
+#define LOCOMO_PAIF_LRCRST 0x08 /* */
+#define LOCOMO_PAIF_LRCEVE 0x04 /* */
+#define LOCOMO_PAIF_LRCINV 0x02 /* */
+#define LOCOMO_PAIF_LRCEN 0x01 /* */
+
+/* GPIO */
+#define LOCOMO_GPIO(Nb) (0x01 << (Nb)) /* LoCoMo GPIO [0...15] */
+#define LOCOMO_GPIO_RTS LOCOMO_GPIO(0) /* LoCoMo GPIO [0] */
+#define LOCOMO_GPIO_CTS LOCOMO_GPIO(1) /* LoCoMo GPIO [1] */
+#define LOCOMO_GPIO_DSR LOCOMO_GPIO(2) /* LoCoMo GPIO [2] */
+#define LOCOMO_GPIO_DTR LOCOMO_GPIO(3) /* LoCoMo GPIO [3] */
+#define LOCOMO_GPIO_LCD_VSHA_ON LOCOMO_GPIO(4) /* LoCoMo GPIO [4] */
+#define LOCOMO_GPIO_LCD_VSHD_ON LOCOMO_GPIO(5) /* LoCoMo GPIO [5] */
+#define LOCOMO_GPIO_LCD_VEE_ON LOCOMO_GPIO(6) /* LoCoMo GPIO [6] */
+#define LOCOMO_GPIO_LCD_MOD LOCOMO_GPIO(7) /* LoCoMo GPIO [7] */
+#define LOCOMO_GPIO_DAC_ON LOCOMO_GPIO(8) /* LoCoMo GPIO [8] */
+#define LOCOMO_GPIO_FL_VR LOCOMO_GPIO(9) /* LoCoMo GPIO [9] */
+#define LOCOMO_GPIO_DAC_SDATA LOCOMO_GPIO(10) /* LoCoMo GPIO [10] */
+#define LOCOMO_GPIO_DAC_SCK LOCOMO_GPIO(11) /* LoCoMo GPIO [11] */
+#define LOCOMO_GPIO_DAC_SLOAD LOCOMO_GPIO(12) /* LoCoMo GPIO [12] */
+
+extern struct bus_type locomo_bus_type;
+
+struct locomo_dev {
+ struct device dev;
+ unsigned int devid;
+ struct resource res;
+ void *mapbase;
+ unsigned int irq[1];
+ u64 dma_mask;
+};
+
+#define LOCOMO_DEV(_d) container_of((_d), struct locomo_dev, dev)
+
+#define locomo_get_drvdata(d) dev_get_drvdata(&(d)->dev)
+#define locomo_set_drvdata(d,p) dev_set_drvdata(&(d)->dev, p)
+
+struct locomo_driver {
+ struct device_driver drv;
+ unsigned int devid;
+ int (*probe)(struct locomo_dev *);
+ int (*remove)(struct locomo_dev *);
+ int (*suspend)(struct locomo_dev *, u32);
+ int (*resume)(struct locomo_dev *);
+};
+
+#define LOCOMO_DRV(_d) container_of((_d), struct locomo_driver, drv)
+
+#define LOCOMO_DRIVER_NAME(_ldev) ((_ldev)->dev.driver->name)
+
+void locomo_lcd_power(struct locomo_dev *, int, unsigned int);
+
+int locomo_driver_register(struct locomo_driver *);
+void locomo_driver_unregister(struct locomo_driver *);
+
+#endif
* ioremap and friends.
*
* ioremap takes a PCI memory address, as specified in
- * linux/Documentation/IO-mapping.txt.
+ * Documentation/IO-mapping.txt.
*/
extern void * __ioremap(unsigned long, size_t, unsigned long, unsigned long);
extern void __iounmap(void *addr);
* See arch/arm/kernel/sys-arm.c for ugly details..
*/
struct ipc_kludge {
- struct msgbuf *msgp;
+ struct msgbuf __user *msgp;
long msgtyp;
};
--- /dev/null
+/*
+ * linux/include/asm-arm/mach/time.h
+ *
+ * Copyright (C) 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.
+ */
+#ifndef __ASM_ARM_MACH_TIME_H
+#define __ASM_ARM_MACH_TIME_H
+
+extern void (*init_arch_time)(void);
+
+extern int (*set_rtc)(void);
+extern unsigned long(*gettimeoffset)(void);
+
+void timer_tick(struct pt_regs *);
+
+#endif
#define SIG_SETMASK 2 /* for setting the signal mask */
/* Type of a signal handler. */
-typedef void (*__sighandler_t)(int);
+typedef void __signalfn_t(int);
+typedef __signalfn_t __user *__sighandler_t;
+
+typedef void __restorefn_t(void);
+typedef __restorefn_t __user *__sigrestore_t;
#define SIG_DFL ((__sighandler_t)0) /* default signal handling */
#define SIG_IGN ((__sighandler_t)1) /* ignore signal */
__sighandler_t sa_handler;
old_sigset_t sa_mask;
unsigned long sa_flags;
- void (*sa_restorer)(void);
+ __sigrestore_t sa_restorer;
};
struct sigaction {
__sighandler_t sa_handler;
unsigned long sa_flags;
- void (*sa_restorer)(void);
+ __sigrestore_t sa_restorer;
sigset_t sa_mask; /* mask last for extensibility */
};
#endif /* __KERNEL__ */
typedef struct sigaltstack {
- void *ss_sp;
+ void __user *ss_sp;
int ss_flags;
size_t ss_size;
} stack_t;
--- /dev/null
+/*
+ * linux/include/asm-arm/vfpmacros.h
+ *
+ * Assembler-only file containing VFP macros and register definitions.
+ */
+#include "vfp.h"
+
+@ Macros to allow building with old toolkits (with no VFP support)
+ .macro VFPFMRX, rd, sysreg, cond
+ MRC\cond p10, 7, \rd, \sysreg, cr0, 0 @ FMRX \rd, \sysreg
+ .endm
+
+ .macro VFPFMXR, sysreg, rd, cond
+ MCR\cond p10, 7, \rd, \sysreg, cr0, 0 @ FMXR \sysreg, \rd
+ .endm
+
+ @ read all the working registers back into the VFP
+ .macro VFPFLDMIA, base
+ LDC p11, cr0, [\base],#33*4 @ FLDMIAX \base!, {d0-d15}
+ .endm
+
+ @ write all the working registers out of the VFP
+ .macro VFPFSTMIA, base
+ STC p11, cr0, [\base],#33*4 @ FSTMIAX \base!, {d0-d15}
+ .endm
* ioremap and friends.
*
* ioremap takes a PCI memory address, as specified in
- * linux/Documentation/IO-mapping.txt.
+ * Documentation/IO-mapping.txt.
*/
extern void * __ioremap(unsigned long, size_t, unsigned long, unsigned long);
extern void __iounmap(void *addr);
* published by the Free Software Foundation.
*
* Structure passed to kernel to tell it about the
- * hardware it's running on. See linux/Documentation/arm/Setup
+ * hardware it's running on. See Documentation/arm/Setup
* for more info.
*/
#ifndef __ASMARM_SETUP_H
#ifndef _ASM_IRQ_VECTORS_LIMITS_H
#define _ASM_IRQ_VECTORS_LIMITS_H
-#ifdef CONFIG_PCI_USE_VECTOR
+#ifdef CONFIG_PCI_MSI
#define NR_IRQS FIRST_SYSTEM_VECTOR
#define NR_IRQ_VECTORS NR_IRQS
#else
--- /dev/null
+#ifndef _I386_PGTABLE_2LEVEL_DEFS_H
+#define _I386_PGTABLE_2LEVEL_DEFS_H
+
+/*
+ * traditional i386 two-level paging structure:
+ */
+
+#define PGDIR_SHIFT 22
+#define PTRS_PER_PGD 1024
+
+/*
+ * the i386 is two-level, so we don't really have any
+ * PMD directory physically.
+ */
+#define PMD_SHIFT 22
+#define PTRS_PER_PMD 1
+
+#define PTRS_PER_PTE 1024
+
+#endif /* _I386_PGTABLE_2LEVEL_DEFS_H */
--- /dev/null
+#ifndef _I386_PGTABLE_3LEVEL_DEFS_H
+#define _I386_PGTABLE_3LEVEL_DEFS_H
+
+/*
+ * PGDIR_SHIFT determines what a top-level page table entry can map
+ */
+#define PGDIR_SHIFT 30
+#define PTRS_PER_PGD 4
+
+/*
+ * PMD_SHIFT determines the size of the area a middle-level
+ * page table can map
+ */
+#define PMD_SHIFT 21
+#define PTRS_PER_PMD 512
+
+/*
+ * entries per page directory level
+ */
+#define PTRS_PER_PTE 512
+
+#endif /* _I386_PGTABLE_3LEVEL_DEFS_H */
#ifdef CONFIG_IA64_CYCLONE
extern int use_cyclone;
-extern int __init cyclone_setup(char*);
+extern void __init cyclone_setup(void);
#else /* CONFIG_IA64_CYCLONE */
#define use_cyclone 0
-static inline void cyclone_setup(char* s)
+static inline void cyclone_setup(void)
{
printk(KERN_ERR "Cyclone Counter: System not configured"
" w/ CONFIG_IA64_CYCLONE.\n");
#define AT_SYSINFO_EHDR 33
#ifdef __KERNEL__
-struct elf64_hdr;
-extern void ia64_set_personality (struct elf64_hdr *elf_ex, int ibcs2_interpreter);
-#define SET_PERSONALITY(ex, ibcs2) ia64_set_personality(&(ex), ibcs2)
+#define SET_PERSONALITY(ex, ibcs2) set_personality(PER_LINUX)
+#define elf_read_implies_exec(ex, have_pt_gnu_stack) \
+ (!(have_pt_gnu_stack) && ((ex).e_flags & EF_IA_64_LINUX_EXECUTABLE_STACK) != 0)
struct task_struct;
#ifdef CONFIG_IA64_DIG
/* Max 8 Nodes */
#define NODES_SHIFT 3
+#elif defined(CONFIG_IA64_HP_ZX1)
+/* Max 32 Nodes */
+#define NODES_SHIFT 5
#elif defined(CONFIG_IA64_SGI_SN2) || defined(CONFIG_IA64_GENERIC)
/* Max 256 Nodes */
#define NODES_SHIFT 8
#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | \
VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC | \
- (((current->thread.flags & IA64_THREAD_XSTACK) != 0) \
+ (((current->personality & READ_IMPLIES_EXEC) != 0) \
? VM_EXEC : 0))
#endif /* _ASM_IA64_PAGE_H */
#ifndef _ASM_SN_SN2_IO_H
#define _ASM_SN_SN2_IO_H
+#include <linux/compiler.h>
+#include <asm/intrinsics.h>
-extern void * sn_io_addr(unsigned long port); /* Forward definition */
+extern void * sn_io_addr(unsigned long port) __attribute_const__; /* Forward definition */
extern void sn_mmiob(void); /* Forward definition */
-#include <asm/intrinsics.h>
#define __sn_mf_a() ia64_mfa()
#ifndef __M68K_HARDIRQ_H
#define __M68K_HARDIRQ_H
+#include <linux/config.h>
#include <linux/threads.h>
#include <linux/cache.h>
struct fp_ext temp[2];
};
-#if FPU_EMU_DEBUG
+#ifdef FPU_EMU_DEBUG
extern unsigned int fp_debugprint;
#define dprint(bit, fmt, args...) ({ \
#define _MOTOROLA_PGALLOC_H
#include <asm/tlb.h>
+#include <asm/tlbflush.h>
extern pmd_t *get_pointer_table(void);
extern int free_pointer_table(pmd_t *);
atomic_t count;
atomic_t waking;
wait_queue_head_t wait;
-#if WAITQUEUE_DEBUG
+#ifdef WAITQUEUE_DEBUG
long __magic;
#endif
};
-#if WAITQUEUE_DEBUG
+#ifdef WAITQUEUE_DEBUG
# define __SEM_DEBUG_INIT(name) \
, (long)&(name).__magic
#else
{
register struct semaphore *sem1 __asm__ ("%a1") = sem;
-#if WAITQUEUE_DEBUG
+#ifdef WAITQUEUE_DEBUG
CHECK_MAGIC(sem->__magic);
#endif
might_sleep();
register struct semaphore *sem1 __asm__ ("%a1") = sem;
register int result __asm__ ("%d0");
-#if WAITQUEUE_DEBUG
+#ifdef WAITQUEUE_DEBUG
CHECK_MAGIC(sem->__magic);
#endif
might_sleep();
register struct semaphore *sem1 __asm__ ("%a1") = sem;
register int result __asm__ ("%d0");
-#if WAITQUEUE_DEBUG
+#ifdef WAITQUEUE_DEBUG
CHECK_MAGIC(sem->__magic);
#endif
{
register struct semaphore *sem1 __asm__ ("%a1") = sem;
-#if WAITQUEUE_DEBUG
+#ifdef WAITQUEUE_DEBUG
CHECK_MAGIC(sem->__magic);
#endif
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003, 2004 Ralf Baechle
+ */
+#ifndef __ASM_MACH_YOSEMITE_CPU_FEATURE_OVERRIDES_H
+#define __ASM_MACH_YOSEMITE_CPU_FEATURE_OVERRIDES_H
+
+/*
+ * Momentum Jaguar ATX always has the RM9000 processor.
+ */
+#define cpu_has_watch 1
+#define cpu_has_mips16 0
+#define cpu_has_divec 0
+#define cpu_has_vce 0
+#define cpu_has_cache_cdex_p 0
+#define cpu_has_cache_cdex_s 0
+#define cpu_has_prefetch 1
+#define cpu_has_mcheck 0
+#define cpu_has_ejtag 0
+
+#define cpu_has_llsc 1
+#define cpu_has_vtag_icache 0
+#define cpu_has_dc_aliases 0
+#define cpu_has_ic_fills_f_dc 0
+
+#define cpu_has_nofpuex 0
+#define cpu_has_64bits 1
+
+#define cpu_has_subset_pcaches 0
+
+#define cpu_dcache_line_size() 32
+#define cpu_icache_line_size() 32
+#define cpu_scache_line_size() 32
+
+#endif /* __ASM_MACH_YOSEMITE_CPU_FEATURE_OVERRIDES_H */
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2004 by Ralf Baechle
+ */
+#ifndef __ASM_MIPS_MARVELL_H
+#define __ASM_MIPS_MARVELL_H
+
+#include <linux/pci.h>
+
+#include <asm/byteorder.h>
+#include <asm/pci_channel.h>
+
+extern unsigned long marvell_base;
+
+/*
+ * Because of an error/peculiarity in the Galileo chip, we need to swap the
+ * bytes when running bigendian.
+ */
+#define __MV_READ(ofs) \
+ (*(volatile u32 *)(marvell_base+(ofs)))
+#define __MV_WRITE(ofs, data) \
+ do { *(volatile u32 *)(marvell_base+(ofs)) = (data); } while (0)
+
+#define MV_READ(ofs) le32_to_cpu(__MV_READ(ofs))
+#define MV_WRITE(ofs, data) __MV_WRITE(ofs, cpu_to_le32(data))
+
+#define MV_READ_16(ofs) \
+ le16_to_cpu(*(volatile u16 *)(marvell_base+(ofs)))
+#define MV_WRITE_16(ofs, data) \
+ *(volatile u16 *)(marvell_base+(ofs)) = cpu_to_le16(data)
+
+#define MV_READ_8(ofs) \
+ *(volatile u8 *)(marvell_base+(ofs))
+#define MV_WRITE_8(ofs, data) \
+ *(volatile u8 *)(marvell_base+(ofs)) = data
+
+#define MV_SET_REG_BITS(ofs, bits) \
+ (*((volatile u32 *)(marvell_base + (ofs)))) |= ((u32)cpu_to_le32(bits))
+#define MV_RESET_REG_BITS(ofs, bits) \
+ (*((volatile u32 *)(marvell_base + (ofs)))) &= ~((u32)cpu_to_le32(bits))
+
+extern struct pci_ops mv_pci_ops;
+
+struct mv_pci_controller {
+ struct pci_controller pcic;
+
+ /*
+ * GT-64240/MV-64340 specific, per host bus information
+ */
+ unsigned long config_addr;
+ unsigned long config_vreg;
+};
+
+#endif /* __ASM_MIPS_MARVELL_H */
--- /dev/null
+#ifdef __KERNEL__
+#ifndef _MIPS_SETUP_H
+#define _MIPS_SETUP_H
+
+#define COMMAND_LINE_SIZE 256
+
+#endif /* __SETUP_H */
+#endif /* __KERNEL__ */
--- /dev/null
+/*
+ * tb0219.h, Include file for TANBAC TB0219.
+ *
+ * Copyright (C) 2002-2004 Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
+ *
+ * Modified for TANBAC TB0219:
+ * Copyright (C) 2003 Megasolution Inc. <matsu@megasolution.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __TANBAC_TB0219_H
+#define __TANBAC_TB0219_H
+
+#include <asm/vr41xx/vr41xx.h>
+
+/*
+ * General-Purpose I/O Pin Number
+ */
+#define TB0219_PCI_SLOT1_PIN 2
+#define TB0219_PCI_SLOT2_PIN 3
+#define TB0219_PCI_SLOT3_PIN 4
+
+/*
+ * Interrupt Number
+ */
+#define TB0219_PCI_SLOT1_IRQ GIU_IRQ(TB0219_PCI_SLOT1_PIN)
+#define TB0219_PCI_SLOT2_IRQ GIU_IRQ(TB0219_PCI_SLOT2_PIN)
+#define TB0219_PCI_SLOT3_IRQ GIU_IRQ(TB0219_PCI_SLOT3_PIN)
+
+#endif /* __TANBAC_TB0219_H */
#ifdef __LP64__
#define LDREG ldd
#define STREG std
+#define LDREGX ldd,s
#define LDREGM ldd,mb
#define STREGM std,ma
#define RP_OFFSET 16
#else
#define LDREG ldw
#define STREG stw
+#define LDREGX ldwx,s
#define LDREGM ldwm
#define STREGM stwm
#define RP_OFFSET 20
#define FRAME_SIZE 64
#endif
+#ifdef CONFIG_PA20
+#define BL b,l
+#else
+#define BL bl
+#endif
+
#ifdef __ASSEMBLY__
#ifdef __LP64__
depd,z \r, 63-\sa, 64-\sa, \t
.endm
+ /* Shift Right - note the r and t can NOT be the same! */
+ .macro shr r, sa, t
+ extru \r, 31-\sa, 32-\sa, \t
+ .endm
+
+ /* pa20w version of shift right */
+ .macro shrd r, sa, t
+ extrd,u \r, 63-\sa, 64-\sa, \t
+ .endm
+
/* load 32-bit 'value' into 'reg' compensating for the ldil
* sign-extension when running in wide mode.
* WARNING!! neither 'value' nor 'reg' can be expressions
#define virt_to_bus virt_to_phys
#define bus_to_virt phys_to_virt
-/*
- * Change "struct page" to physical address.
- */
-#define page_to_phys(page) ((page - mem_map) << PAGE_SHIFT)
-
/* Memory mapped IO */
extern void * __ioremap(unsigned long offset, unsigned long size, unsigned long flags);
#ifndef _PARISC_MMZONE_H
#define _PARISC_MMZONE_H
+#ifdef CONFIG_DISCONTIGMEM
+
+#define MAX_PHYSMEM_RANGES 8 /* Fix the size for now (current known max is 3) */
+extern int npmem_ranges;
+
struct node_map_data {
pg_data_t pg_data;
- struct page *adj_node_mem_map;
};
extern struct node_map_data node_data[];
-extern unsigned char *chunkmap;
-
-#define BADCHUNK ((unsigned char)0xff)
-#define CHUNKSZ (256*1024*1024)
-#define CHUNKSHIFT 28
-#define CHUNKMASK (~(CHUNKSZ - 1))
-#define CHUNKNUM(paddr) ((paddr) >> CHUNKSHIFT)
#define NODE_DATA(nid) (&node_data[nid].pg_data)
-#define NODE_MEM_MAP(nid) (NODE_DATA(nid)->node_mem_map)
-#define ADJ_NODE_MEM_MAP(nid) (node_data[nid].adj_node_mem_map)
-#define phys_to_page(paddr) \
- (ADJ_NODE_MEM_MAP(chunkmap[CHUNKNUM((paddr))]) \
- + ((paddr) >> PAGE_SHIFT))
+/*
+ * Given a kernel address, find the home node of the underlying memory.
+ */
+#define kvaddr_to_nid(kaddr) pfn_to_nid(__pa(kaddr) >> PAGE_SHIFT)
+
+#define node_mem_map(nid) (NODE_DATA(nid)->node_mem_map)
+#define node_start_pfn(nid) (NODE_DATA(nid)->node_start_pfn)
+#define node_end_pfn(nid) \
+({ \
+ pg_data_t *__pgdat = NODE_DATA(nid); \
+ __pgdat->node_start_pfn + __pgdat->node_spanned_pages; \
+})
+#define node_localnr(pfn, nid) ((pfn) - node_start_pfn(nid))
+
+#define local_mapnr(kvaddr) \
+({ \
+ unsigned long __pfn = __pa(kvaddr) >> PAGE_SHIFT; \
+ (__pfn - node_start_pfn(pfn_to_nid(__pfn))); \
+})
+
+#define pfn_to_page(pfn) \
+({ \
+ unsigned long __pfn = (pfn); \
+ int __node = pfn_to_nid(__pfn); \
+ &node_mem_map(__node)[node_localnr(__pfn,__node)]; \
+})
+
+#define page_to_pfn(pg) \
+({ \
+ struct page *__page = pg; \
+ struct zone *__zone = page_zone(__page); \
+ BUG_ON(__zone == NULL); \
+ (unsigned long)(__page - __zone->zone_mem_map) \
+ + __zone->zone_start_pfn; \
+})
+
+/* We have these possible memory map layouts:
+ * Astro: 0-3.75, 67.75-68, 4-64
+ * zx1: 0-1, 257-260, 4-256
+ * Stretch (N-class): 0-2, 4-32, 34-xxx
+ */
+
+/* Since each 1GB can only belong to one region (node), we can create
+ * an index table for pfn to nid lookup; each entry in pfnnid_map
+ * represents 1GB, and contains the node that the memory belongs to. */
+
+#define PFNNID_SHIFT (30 - PAGE_SHIFT)
+#define PFNNID_MAP_MAX 512 /* support 512GB */
+extern unsigned char pfnnid_map[PFNNID_MAP_MAX];
+
+#ifndef __LP64__
+#define pfn_is_io(pfn) ((pfn & (0xf0000000UL >> PAGE_SHIFT)) == (0xf0000000UL >> PAGE_SHIFT))
+#else
+/* io can be 0xf0f0f0f0f0xxxxxx or 0xfffffffff0000000 */
+#define pfn_is_io(pfn) ((pfn & (0xf000000000000000UL >> PAGE_SHIFT)) == (0xf000000000000000UL >> PAGE_SHIFT))
+#endif
+
+static inline int pfn_to_nid(unsigned long pfn)
+{
+ unsigned int i;
+ unsigned char r;
+
+ if (unlikely(pfn_is_io(pfn)))
+ return 0;
+
+ i = pfn >> PFNNID_SHIFT;
+ BUG_ON(i >= sizeof(pfnnid_map) / sizeof(pfnnid_map[0]));
+ r = pfnnid_map[i];
+ BUG_ON(r == 0xff);
+
+ return (int)r;
+}
-#define virt_to_page(kvaddr) phys_to_page(__pa(kvaddr))
+static inline int pfn_valid(int pfn)
+{
+ int nid = pfn_to_nid(pfn);
-/* This is kind of bogus, need to investigate performance of doing it right */
-#define VALID_PAGE(page) ((page - mem_map) < max_mapnr)
+ if (nid >= 0)
+ return (pfn < node_end_pfn(nid));
+ return 0;
+}
-#endif /* !_PARISC_MMZONE_H */
+#else /* !CONFIG_DISCONTIGMEM */
+#define MAX_PHYSMEM_RANGES 1
+#endif
+#endif /* _PARISC_MMZONE_H */
--- /dev/null
+#ifndef _ASM_MAX_NUMNODES_H
+#define _ASM_MAX_NUMNODES_H
+
+#include <linux/config.h>
+
+/* Max 8 Nodes */
+#define NODES_SHIFT 3
+
+#endif /* _ASM_MAX_NUMNODES_H */
*/
#define PCI_MAX_BUSSES 256
-/* [soapbox on]
-** Who the hell can develop stuff without ASSERT or VASSERT?
-** No one understands all the modules across all platforms.
-** For linux add another dimension - processor architectures.
-**
-** This should be a standard/global macro used liberally
-** in all code. Every respectable engineer I know in HP
-** would support this argument. - grant
-** [soapbox off]
-*/
-#ifdef PCI_DEBUG
-#define ASSERT(expr) \
- if(!(expr)) { \
- printk("\n%s:%d: Assertion " #expr " failed!\n", \
- __FILE__, __LINE__); \
- panic(#expr); \
- }
-#else
-#define ASSERT(expr)
-#endif
-
-
/*
** pci_hba_data (aka H2P_OBJECT in HP/UX)
**
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
- * Copyright (c) Hewlett Packard (Paul Bame <bame@puffin.external.hp.com>)
- * Copyright 2000 (c) Grant Grundler <grundler@puffin.external.hp.com>
+ * Copyright 2000 (c) Hewlett Packard (Paul Bame <bame()spam.parisc-linux.org>)
+ * Copyright 2000,2004 (c) Grant Grundler <grundler()nahspam.parisc-linux.org>
*/
-/* PDC PAT CELL */
#define PDC_PAT_CELL 64L /* Interface for gaining and
* manipulatin g cell state within PD */
#define PDC_PAT_CELL_GET_NUMBER 0L /* Return Cell number */
#define PAT_GNIP 7 /* GNI Reserved Space */
-/* PDC PAT CHASSIS LOG */
-#define PDC_PAT_CHASSIS_LOG 65L /* Platform logging & forward
- ** progress functions */
+/* PDC PAT CHASSIS LOG -- Platform logging & forward progress functions */
+
+#define PDC_PAT_CHASSIS_LOG 65L
#define PDC_PAT_CHASSIS_WRITE_LOG 0L /* Write Log Entry */
#define PDC_PAT_CHASSIS_READ_LOG 1L /* Read Log Entry */
-/* PDC PAT CPU */
-#define PDC_PAT_CPU 67L /* Interface to CPU configuration
- * within the protection domain */
+/* PDC PAT CPU -- CPU configuration within the protection domain */
+
+#define PDC_PAT_CPU 67L
#define PDC_PAT_CPU_INFO 0L /* Return CPU config info */
#define PDC_PAT_CPU_DELETE 1L /* Delete CPU */
#define PDC_PAT_CPU_ADD 2L /* Add CPU */
#define PDC_PAT_CPU_PLUNGE_FABRIC 128L /* Plunge Fabric */
#define PDC_PAT_CPU_UPDATE_CACHE_CLEANSING 129L /* Manipulate Cache
* Cleansing Mode */
-/* PDC PAT EVENT */
+/* PDC PAT EVENT -- Platform Events */
-#define PDC_PAT_EVENT 68L /* Interface to Platform Events */
+#define PDC_PAT_EVENT 68L
#define PDC_PAT_EVENT_GET_CAPS 0L /* Get Capabilities */
#define PDC_PAT_EVENT_SET_MODE 1L /* Set Notification Mode */
#define PDC_PAT_EVENT_SCAN 2L /* Scan Event */
#define PDC_PAT_EVENT_HANDLE 3L /* Handle Event */
#define PDC_PAT_EVENT_GET_NB_CALL 4L /* Get Non-Blocking call Args */
-/* PDC PAT HPMC */
+/* PDC PAT HPMC -- Cause processor to go into spin loop, and wait
+ * for wake up from Monarch Processor.
+ */
-#define PDC_PAT_HPMC 70L /* Cause processor to go into spin
- ** loop, and wait for wake up from
- ** Monarch Processor */
+#define PDC_PAT_HPMC 70L
#define PDC_PAT_HPMC_RENDEZ_CPU 0L /* go into spin loop */
#define PDC_PAT_HPMC_SET_PARAMS 1L /* Allows OS to specify intr which PDC
- * will use to interrupt OS during machine
- * check rendezvous */
+ * will use to interrupt OS during
+ * machine check rendezvous */
/* parameters for PDC_PAT_HPMC_SET_PARAMS: */
#define HPMC_SET_PARAMS_INTR 1L /* Rendezvous Interrupt */
#define HPMC_SET_PARAMS_WAKE 2L /* Wake up processor */
-/* PDC PAT IO */
-#define PDC_PAT_IO 71L /* On-line services for I/O modules */
+/* PDC PAT IO -- On-line services for I/O modules */
+
+#define PDC_PAT_IO 71L
#define PDC_PAT_IO_GET_SLOT_STATUS 5L /* Get Slot Status Info*/
#define PDC_PAT_IO_GET_LOC_FROM_HARDWARE 6L /* Get Physical Location from */
/* Hardware Path */
#define PDC_PAT_IO_GET_PROC_VIEW 29L /* Get Processor view of IO address */
#define PDC_PAT_IO_PROG_SBA_DIR_RANGE 30L /* Program directed range */
-/* PDC PAT MEM */
-#define PDC_PAT_MEM 72L /* Manage memory page deallocation */
+/* PDC PAT MEM -- Manage memory page deallocation */
+
+#define PDC_PAT_MEM 72L
#define PDC_PAT_MEM_PD_INFO 0L /* Return PDT info for PD */
#define PDC_PAT_MEM_PD_CLEAR 1L /* Clear PDT for PD */
#define PDC_PAT_MEM_PD_READ 2L /* Read PDT entries for PD */
#define PDC_PAT_MEM_GET_TBL_SIZE 131L /* Get Memory Table Size */
#define PDC_PAT_MEM_GET_TBL 132L /* Get Memory Table */
-/* PDC PAT NVOLATILE */
-#define PDC_PAT_NVOLATILE 73L /* Access Non-Volatile Memory */
-#define PDC_PAT_NVOLATILE_READ 0L /* Read Non-Volatile Memory */
-#define PDC_PAT_NVOLATILE_WRITE 1L /* Write Non-Volatile Memory */
-#define PDC_PAT_NVOLATILE_GET_SIZE 2L /* Return size of NVM */
-#define PDC_PAT_NVOLATILE_VERIFY 3L /* Verify contents of NVM */
-#define PDC_PAT_NVOLATILE_INIT 4L /* Initialize NVM */
+/* PDC PAT NVOLATILE -- Access Non-Volatile Memory */
+
+#define PDC_PAT_NVOLATILE 73L
+#define PDC_PAT_NVOLATILE_READ 0L /* Read Non-Volatile Memory */
+#define PDC_PAT_NVOLATILE_WRITE 1L /* Write Non-Volatile Memory */
+#define PDC_PAT_NVOLATILE_GET_SIZE 2L /* Return size of NVM */
+#define PDC_PAT_NVOLATILE_VERIFY 3L /* Verify contents of NVM */
+#define PDC_PAT_NVOLATILE_INIT 4L /* Initialize NVM */
+
+/* PDC PAT PD */
+#define PDC_PAT_PD 74L /* Protection Domain Info */
+#define PDC_PAT_PD_GET_ADDR_MAP 0L /* Get Address Map */
+
+/* PDC_PAT_PD_GET_ADDR_MAP entry types */
+#define PAT_MEMORY_DESCRIPTOR 1
+
+/* PDC_PAT_PD_GET_ADDR_MAP memory types */
+#define PAT_MEMTYPE_MEMORY 0
+#define PAT_MEMTYPE_FIRMWARE 4
+
+/* PDC_PAT_PD_GET_ADDR_MAP memory usage */
+#define PAT_MEMUSE_GENERAL 0
+#define PAT_MEMUSE_GI 128
+#define PAT_MEMUSE_GNI 129
+
#ifndef __ASSEMBLY__
#include <linux/types.h>
+#ifdef CONFIG_PARISC64
+#define is_pdc_pat() (PDC_TYPE_PAT == pdc_type)
+extern int pdc_pat_get_irt_size(unsigned long *num_entries, unsigned long cell_num);
+extern int pdc_pat_get_irt(void *r_addr, unsigned long cell_num);
+#else /* ! CONFIG_PARISC64 */
+/* No PAT support for 32-bit kernels...sorry */
+#define is_pdc_pat() (0)
+#define pdc_pat_get_irt_size(num_entries, cell_numn) PDC_BAD_PROC
+#define pdc_pat_get_irt(r_addr, cell_num) PDC_BAD_PROC
+#endif /* ! CONFIG_PARISC64 */
+
+
+struct pdc_pat_cell_num {
+ unsigned long cell_num;
+ unsigned long cell_loc;
+};
+
+struct pdc_pat_cpu_num {
+ unsigned long cpu_num;
+ unsigned long cpu_loc;
+};
+
+struct pdc_pat_pd_addr_map_entry {
+ unsigned char entry_type; /* 1 = Memory Descriptor Entry Type */
+ unsigned char reserve1[5];
+ unsigned char memory_type;
+ unsigned char memory_usage;
+ unsigned long paddr;
+ unsigned int pages; /* Length in 4K pages */
+ unsigned int reserve2;
+ unsigned long cell_map;
+};
+
+/********************************************************************
+* PDC_PAT_CELL[Return Cell Module] memaddr[0] conf_base_addr
+* ----------------------------------------------------------
+* Bit 0 to 51 - conf_base_addr
+* Bit 52 to 62 - reserved
+* Bit 63 - endianess bit
+********************************************************************/
+#define PAT_GET_CBA(value) ((value) & 0xfffffffffffff000UL)
+
+/********************************************************************
+* PDC_PAT_CELL[Return Cell Module] memaddr[1] mod_info
+* ----------------------------------------------------
+* Bit 0 to 7 - entity type
+* 0 = central agent, 1 = processor,
+* 2 = memory controller, 3 = system bus adapter,
+* 4 = local bus adapter, 5 = processor bus converter,
+* 6 = crossbar fabric connect, 7 = fabric interconnect,
+* 8 to 254 reserved, 255 = unknown.
+* Bit 8 to 15 - DVI
+* Bit 16 to 23 - IOC functions
+* Bit 24 to 39 - reserved
+* Bit 40 to 63 - mod_pages
+* number of 4K pages a module occupies starting at conf_base_addr
+********************************************************************/
+#define PAT_GET_ENTITY(value) (((value) >> 56) & 0xffUL)
+#define PAT_GET_DVI(value) (((value) >> 48) & 0xffUL)
+#define PAT_GET_IOC(value) (((value) >> 40) & 0xffUL)
+#define PAT_GET_MOD_PAGES(value)(((value) & 0xffffffUL)
+
+
/*
** PDC_PAT_CELL_GET_INFO return block
*/
/* FIXME: mod[508] should really be a union of the various mod components */
struct pdc_pat_cell_mod_maddr_block { /* PDC_PAT_CELL_MODULE */
- unsigned long cba; /* function 0 configuration space address */
- unsigned long mod_info; /* module information */
- unsigned long mod_location; /* physical location of the module */
- unsigned long mod_path; /* module path (device path - layers) */
+ unsigned long cba; /* func 0 cfg space address */
+ unsigned long mod_info; /* module information */
+ unsigned long mod_location; /* physical location of the module */
+ struct hardware_path mod_path; /* module path (device path - layers) */
unsigned long mod[508]; /* PAT cell module components */
} __attribute__((aligned(8))) ;
typedef struct pdc_pat_cell_mod_maddr_block pdc_pat_cell_mod_maddr_block_t;
-extern int pdc_pat_cell_get_number(void *);
-extern int pdc_pat_cell_module(void *, unsigned long, unsigned long, unsigned long, void *);
+extern int pdc_pat_chassis_send_log(unsigned long status, unsigned long data);
+extern int pdc_pat_cell_get_number(struct pdc_pat_cell_num *cell_info);
+extern int pdc_pat_cell_module(unsigned long *actcnt, unsigned long ploc, unsigned long mod, unsigned long view_type, void *mem_addr);
extern int pdc_pat_cell_num_to_loc(void *, unsigned long);
+extern int pdc_pat_cpu_get_number(struct pdc_pat_cpu_num *cpu_info, void *hpa);
+
+extern int pdc_pat_pd_get_addr_map(unsigned long *actual_len, void *mem_addr, unsigned long count, unsigned long offset);
+
+
+extern int pdc_pat_io_pci_cfg_read(unsigned long pci_addr, int pci_size, u32 *val);
+extern int pdc_pat_io_pci_cfg_write(unsigned long pci_addr, int pci_size, u32 val);
+
+
/* Flag to indicate this is a PAT box...don't use this unless you
** really have to...it might go away some day.
*/
-#ifdef __LP64__
extern int pdc_pat; /* arch/parisc/kernel/inventory.c */
-#endif
/********************************************************************
* PDC_PAT_CELL[Return Cell Module] memaddr[0] conf_base_addr
struct thread_info {
struct task_struct *task; /* main task structure */
struct exec_domain *exec_domain;/* execution domain */
- __u32 flags; /* thread_info flags (see TIF_*) */
- __u32 cpu; /* current CPU */
+ unsigned long flags; /* thread_info flags (see TIF_*) */
mm_segment_t addr_limit; /* user-level address space limit */
- struct restart_block restart_block;
+ __u32 cpu; /* current CPU */
__s32 preempt_count; /* 0=premptable, <0=BUG; will also serve as bh-counter */
+ struct restart_block restart_block;
};
#define INIT_THREAD_INFO(tsk) \
int *src_err, int *dst_err);
#define csum_partial_copy_from_user(src, dst, len, sum, errp) \
- csum_partial_copy_generic((src), (dst), (len), (sum), (errp), 0)
+ csum_partial_copy_generic((src), (dst), (len), (sum), (errp), NULL)
/* FIXME: this needs to be written to really do no check -- Cort */
#define csum_partial_copy_nocheck(src, dst, len, sum) \
- csum_partial_copy_generic((src), (dst), (len), (sum), 0, 0)
+ csum_partial_copy_generic((src), (dst), (len), (sum), NULL, NULL)
/*
* turns a 32-bit partial checksum (e.g. from csum_partial) into a
*/
#define NUM_CPM_HOST_PAGES 2
+static inline long IS_DPERR(const uint offset)
+{
+ return (uint)offset > (uint)-1000L;
+}
/* Export the base address of the communication processor registers
* and dual port ram.
*/
extern cpm_cpm2_t *cpmp; /* Pointer to comm processor */
-extern void *cpm2_dpalloc(uint size, uint align);
-extern int cpm2_dpfree(void *addr);
-extern void *cpm2_dpalloc_fixed(void *addr, uint size, uint allign);
-extern void cpm2_dpdump(void);
-extern unsigned int cpm2_dpram_offset(void *addr);
-extern void *cpm2_dpram_addr(int offset);
-extern void cpm2_setbrg(uint brg, uint rate);
+extern uint cpm_dpalloc(uint size, uint align);
+extern int cpm_dpfree(uint offset);
+extern uint cpm_dpalloc_fixed(uint offset, uint size, uint align);
+extern void cpm_dpdump(void);
+extern void *cpm_dpram_addr(uint offset);
+extern void cpm_setbrg(uint brg, uint rate);
extern void cpm2_fastbrg(uint brg, uint rate, int div16);
/* Buffer descriptors used by many of the CPM protocols.
#define CPU_FTR_NO_DPM 0x00008000
#define CPU_FTR_HAS_HIGH_BATS 0x00010000
#define CPU_FTR_NEED_COHERENT 0x00020000
+#define CPU_FTR_NO_BTIC 0x00040000
#ifdef __ASSEMBLY__
--- /dev/null
+/*
+ * include/asm-ppc/fsl_ocp.h
+ *
+ * Definitions for the on-chip peripherals on Freescale PPC processors
+ *
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ * Copyright 2004 Freescale Semiconductor, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifdef __KERNEL__
+#ifndef __ASM_FS_OCP_H__
+#define __ASM_FS_OCP_H__
+
+/* A table of information for supporting the Gianfar Ethernet Controller
+ * This helps identify which enet controller we are dealing with,
+ * and what type of enet controller it is
+ */
+struct ocp_gfar_data {
+ uint interruptTransmit;
+ uint interruptError;
+ uint interruptReceive;
+ uint interruptPHY;
+ uint flags;
+ uint phyid;
+ uint phyregidx;
+ unsigned char mac_addr[6];
+};
+
+/* Flags in the flags field */
+#define GFAR_HAS_COALESCE 0x20
+#define GFAR_HAS_RMON 0x10
+#define GFAR_HAS_MULTI_INTR 0x08
+#define GFAR_FIRM_SET_MACADDR 0x04
+#define GFAR_HAS_PHY_INTR 0x02 /* if not set use a timer */
+#define GFAR_HAS_GIGABIT 0x01
+
+/* Data structure for I2C support. Just contains a couple flags
+ * to distinguish various I2C implementations*/
+struct ocp_fs_i2c_data {
+ uint flags;
+};
+
+/* Flags for I2C */
+#define FS_I2C_SEPARATE_DFSRR 0x02
+#define FS_I2C_32BIT 0x01
+
+#endif /* __ASM_FS_OCP_H__ */
+#endif /* __KERNEL__ */
BUG_ON(!pte_none(*(kmap_pte+idx)));
#endif
set_pte(kmap_pte+idx, mk_pte(page, kmap_prot));
- flush_tlb_page(0, vaddr);
+ flush_tlb_page(NULL, vaddr);
return (void*) vaddr;
}
* this pte without first remap it
*/
pte_clear(kmap_pte+idx);
- flush_tlb_page(0, vaddr);
+ flush_tlb_page(NULL, vaddr);
#endif
dec_preempt_count();
preempt_check_resched();
--- /dev/null
+/*
+ * include/asm-ppc/mpc52xx.h
+ *
+ * Prototypes, etc. for the Freescale MPC52xx embedded cpu chips
+ * May need to be cleaned as the port goes on ...
+ *
+ *
+ * Maintainer : Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Originally written by Dale Farnsworth <dfarnsworth@mvista.com>
+ * for the 2.4 kernel.
+ *
+ * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2003 MontaVista, Software, Inc.
+ *
+ * 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.
+ */
+
+#ifndef __ASM_MPC52xx_H__
+#define __ASM_MPC52xx_H__
+
+#ifndef __ASSEMBLY__
+#include <asm/ppcboot.h>
+#include <asm/types.h>
+
+struct pt_regs;
+struct ocp_def;
+#endif /* __ASSEMBLY__ */
+
+
+/* ======================================================================== */
+/* Main registers/struct addresses */
+/* ======================================================================== */
+/* Theses are PHYSICAL addresses ! */
+/* TODO : There should be no static mapping, but it's not yet the case, so */
+/* we require a 1:1 mapping */
+
+#define MPC52xx_MBAR 0xf0000000 /* Phys address */
+#define MPC52xx_MBAR_SIZE 0x00010000
+#define MPC52xx_MBAR_VIRT 0xf0000000 /* Virt address */
+
+#define MPC52xx_MMAP_CTL (MPC52xx_MBAR + 0x0000)
+#define MPC52xx_CDM (MPC52xx_MBAR + 0x0200)
+#define MPC52xx_SFTRST (MPC52xx_MBAR + 0x0220)
+#define MPC52xx_SFTRST_BIT 0x01000000
+#define MPC52xx_INTR (MPC52xx_MBAR + 0x0500)
+#define MPC52xx_GPTx(x) (MPC52xx_MBAR + 0x0600 + ((x)<<4))
+#define MPC52xx_RTC (MPC52xx_MBAR + 0x0800)
+#define MPC52xx_MSCAN1 (MPC52xx_MBAR + 0x0900)
+#define MPC52xx_MSCAN2 (MPC52xx_MBAR + 0x0980)
+#define MPC52xx_GPIO (MPC52xx_MBAR + 0x0b00)
+#define MPC52xx_PCI (MPC52xx_MBAR + 0x0d00)
+#define MPC52xx_USB_OHCI (MPC52xx_MBAR + 0x1000)
+#define MPC52xx_SDMA (MPC52xx_MBAR + 0x1200)
+#define MPC52xx_XLB (MPC52xx_MBAR + 0x1f00)
+#define MPC52xx_PSCx(x) (MPC52xx_MBAR + 0x2000 + ((x)<<9))
+#define MPC52xx_PSC1 (MPC52xx_MBAR + 0x2000)
+#define MPC52xx_PSC2 (MPC52xx_MBAR + 0x2200)
+#define MPC52xx_PSC3 (MPC52xx_MBAR + 0x2400)
+#define MPC52xx_PSC4 (MPC52xx_MBAR + 0x2600)
+#define MPC52xx_PSC5 (MPC52xx_MBAR + 0x2800)
+#define MPC52xx_PSC6 (MPC52xx_MBAR + 0x2C00)
+#define MPC52xx_FEC (MPC52xx_MBAR + 0x3000)
+#define MPC52xx_ATA (MPC52xx_MBAR + 0x3a00)
+#define MPC52xx_I2C1 (MPC52xx_MBAR + 0x3d00)
+#define MPC52xx_I2C_MICR (MPC52xx_MBAR + 0x3d20)
+#define MPC52xx_I2C2 (MPC52xx_MBAR + 0x3d40)
+
+/* SRAM used for SDMA */
+#define MPC52xx_SRAM (MPC52xx_MBAR + 0x8000)
+#define MPC52xx_SRAM_SIZE (16*1024)
+#define MPC52xx_SDMA_MAX_TASKS 16
+
+ /* Memory allocation block size */
+#define MPC52xx_SDRAM_UNIT 0x8000 /* 32K byte */
+
+
+/* ======================================================================== */
+/* IRQ mapping */
+/* ======================================================================== */
+/* Be sure to look at mpc52xx_pic.h if you wish for whatever reason to change
+ * this
+ */
+
+#define MPC52xx_CRIT_IRQ_NUM 4
+#define MPC52xx_MAIN_IRQ_NUM 17
+#define MPC52xx_SDMA_IRQ_NUM 17
+#define MPC52xx_PERP_IRQ_NUM 23
+
+#define MPC52xx_CRIT_IRQ_BASE 0
+#define MPC52xx_MAIN_IRQ_BASE (MPC52xx_CRIT_IRQ_BASE + MPC52xx_CRIT_IRQ_NUM)
+#define MPC52xx_SDMA_IRQ_BASE (MPC52xx_MAIN_IRQ_BASE + MPC52xx_MAIN_IRQ_NUM)
+#define MPC52xx_PERP_IRQ_BASE (MPC52xx_SDMA_IRQ_BASE + MPC52xx_SDMA_IRQ_NUM)
+
+#define MPC52xx_IRQ0 (MPC52xx_CRIT_IRQ_BASE + 0)
+#define MPC52xx_SLICE_TIMER_0_IRQ (MPC52xx_CRIT_IRQ_BASE + 1)
+#define MPC52xx_HI_INT_IRQ (MPC52xx_CRIT_IRQ_BASE + 2)
+#define MPC52xx_CCS_IRQ (MPC52xx_CRIT_IRQ_BASE + 3)
+
+#define MPC52xx_IRQ1 (MPC52xx_MAIN_IRQ_BASE + 1)
+#define MPC52xx_IRQ2 (MPC52xx_MAIN_IRQ_BASE + 2)
+#define MPC52xx_IRQ3 (MPC52xx_MAIN_IRQ_BASE + 3)
+
+#define MPC52xx_SDMA_IRQ (MPC52xx_PERP_IRQ_BASE + 0)
+#define MPC52xx_PSC1_IRQ (MPC52xx_PERP_IRQ_BASE + 1)
+#define MPC52xx_PSC2_IRQ (MPC52xx_PERP_IRQ_BASE + 2)
+#define MPC52xx_PSC3_IRQ (MPC52xx_PERP_IRQ_BASE + 3)
+#define MPC52xx_PSC6_IRQ (MPC52xx_PERP_IRQ_BASE + 4)
+#define MPC52xx_IRDA_IRQ (MPC52xx_PERP_IRQ_BASE + 4)
+#define MPC52xx_FEC_IRQ (MPC52xx_PERP_IRQ_BASE + 5)
+#define MPC52xx_USB_IRQ (MPC52xx_PERP_IRQ_BASE + 6)
+#define MPC52xx_ATA_IRQ (MPC52xx_PERP_IRQ_BASE + 7)
+#define MPC52xx_PCI_CNTRL_IRQ (MPC52xx_PERP_IRQ_BASE + 8)
+#define MPC52xx_PCI_SCIRX_IRQ (MPC52xx_PERP_IRQ_BASE + 9)
+#define MPC52xx_PCI_SCITX_IRQ (MPC52xx_PERP_IRQ_BASE + 10)
+#define MPC52xx_PSC4_IRQ (MPC52xx_PERP_IRQ_BASE + 11)
+#define MPC52xx_PSC5_IRQ (MPC52xx_PERP_IRQ_BASE + 12)
+#define MPC52xx_SPI_MODF_IRQ (MPC52xx_PERP_IRQ_BASE + 13)
+#define MPC52xx_SPI_SPIF_IRQ (MPC52xx_PERP_IRQ_BASE + 14)
+#define MPC52xx_I2C1_IRQ (MPC52xx_PERP_IRQ_BASE + 15)
+#define MPC52xx_I2C2_IRQ (MPC52xx_PERP_IRQ_BASE + 16)
+#define MPC52xx_CAN1_IRQ (MPC52xx_PERP_IRQ_BASE + 17)
+#define MPC52xx_CAN2_IRQ (MPC52xx_PERP_IRQ_BASE + 18)
+#define MPC52xx_IR_RX_IRQ (MPC52xx_PERP_IRQ_BASE + 19)
+#define MPC52xx_IR_TX_IRQ (MPC52xx_PERP_IRQ_BASE + 20)
+#define MPC52xx_XLB_ARB_IRQ (MPC52xx_PERP_IRQ_BASE + 21)
+
+
+
+/* ======================================================================== */
+/* Structures mapping of some unit register set */
+/* ======================================================================== */
+
+#ifndef __ASSEMBLY__
+
+/* Memory Mapping Control */
+struct mpc52xx_mmap_ctl {
+ volatile u32 mbar; /* MMAP_CTRL + 0x00 */
+
+ volatile u32 cs0_start; /* MMAP_CTRL + 0x04 */
+ volatile u32 cs0_stop; /* MMAP_CTRL + 0x08 */
+ volatile u32 cs1_start; /* MMAP_CTRL + 0x0c */
+ volatile u32 cs1_stop; /* MMAP_CTRL + 0x10 */
+ volatile u32 cs2_start; /* MMAP_CTRL + 0x14 */
+ volatile u32 cs2_stop; /* MMAP_CTRL + 0x18 */
+ volatile u32 cs3_start; /* MMAP_CTRL + 0x1c */
+ volatile u32 cs3_stop; /* MMAP_CTRL + 0x20 */
+ volatile u32 cs4_start; /* MMAP_CTRL + 0x24 */
+ volatile u32 cs4_stop; /* MMAP_CTRL + 0x28 */
+ volatile u32 cs5_start; /* MMAP_CTRL + 0x2c */
+ volatile u32 cs5_stop; /* MMAP_CTRL + 0x30 */
+
+ volatile u32 sdram0; /* MMAP_CTRL + 0x34 */
+ volatile u32 sdram1; /* MMAP_CTRL + 0X38 */
+
+ volatile u32 reserved[4]; /* MMAP_CTRL + 0x3c .. 0x48 */
+
+ volatile u32 boot_start; /* MMAP_CTRL + 0x4c */
+ volatile u32 boot_stop; /* MMAP_CTRL + 0x50 */
+
+ volatile u32 ipbi_ws_ctrl; /* MMAP_CTRL + 0x54 */
+
+ volatile u32 cs6_start; /* MMAP_CTRL + 0x58 */
+ volatile u32 cs6_stop; /* MMAP_CTRL + 0x5c */
+ volatile u32 cs7_start; /* MMAP_CTRL + 0x60 */
+ volatile u32 cs7_stop; /* MMAP_CTRL + 0x60 */
+};
+
+/* Interrupt controller */
+struct mpc52xx_intr {
+ volatile u32 per_mask; /* INTR + 0x00 */
+ volatile u32 per_pri1; /* INTR + 0x04 */
+ volatile u32 per_pri2; /* INTR + 0x08 */
+ volatile u32 per_pri3; /* INTR + 0x0c */
+ volatile u32 ctrl; /* INTR + 0x10 */
+ volatile u32 main_mask; /* INTR + 0x14 */
+ volatile u32 main_pri1; /* INTR + 0x18 */
+ volatile u32 main_pri2; /* INTR + 0x1c */
+ volatile u32 reserved1; /* INTR + 0x20 */
+ volatile u32 enc_status; /* INTR + 0x24 */
+ volatile u32 crit_status; /* INTR + 0x28 */
+ volatile u32 main_status; /* INTR + 0x2c */
+ volatile u32 per_status; /* INTR + 0x30 */
+ volatile u32 reserved2; /* INTR + 0x34 */
+ volatile u32 per_error; /* INTR + 0x38 */
+};
+
+/* SDMA */
+struct mpc52xx_sdma {
+ volatile u32 taskBar; /* SDMA + 0x00 */
+ volatile u32 currentPointer; /* SDMA + 0x04 */
+ volatile u32 endPointer; /* SDMA + 0x08 */
+ volatile u32 variablePointer;/* SDMA + 0x0c */
+
+ volatile u8 IntVect1; /* SDMA + 0x10 */
+ volatile u8 IntVect2; /* SDMA + 0x11 */
+ volatile u16 PtdCntrl; /* SDMA + 0x12 */
+
+ volatile u32 IntPend; /* SDMA + 0x14 */
+ volatile u32 IntMask; /* SDMA + 0x18 */
+
+ volatile u16 tcr[16]; /* SDMA + 0x1c .. 0x3a */
+
+ volatile u8 ipr[31]; /* SDMA + 0x3c .. 5b */
+
+ volatile u32 res1; /* SDMA + 0x5c */
+ volatile u32 task_size0; /* SDMA + 0x60 */
+ volatile u32 task_size1; /* SDMA + 0x64 */
+ volatile u32 MDEDebug; /* SDMA + 0x68 */
+ volatile u32 ADSDebug; /* SDMA + 0x6c */
+ volatile u32 Value1; /* SDMA + 0x70 */
+ volatile u32 Value2; /* SDMA + 0x74 */
+ volatile u32 Control; /* SDMA + 0x78 */
+ volatile u32 Status; /* SDMA + 0x7c */
+};
+
+/* GPT */
+struct mpc52xx_gpt {
+ volatile u32 mode; /* GPTx + 0x00 */
+ volatile u32 count; /* GPTx + 0x04 */
+ volatile u32 pwm; /* GPTx + 0x08 */
+ volatile u32 status; /* GPTx + 0X0c */
+};
+
+/* RTC */
+struct mpc52xx_rtc {
+ volatile u32 time_set; /* RTC + 0x00 */
+ volatile u32 date_set; /* RTC + 0x04 */
+ volatile u32 stopwatch; /* RTC + 0x08 */
+ volatile u32 int_enable; /* RTC + 0x0c */
+ volatile u32 time; /* RTC + 0x10 */
+ volatile u32 date; /* RTC + 0x14 */
+ volatile u32 stopwatch_intr; /* RTC + 0x18 */
+ volatile u32 bus_error; /* RTC + 0x1c */
+ volatile u32 dividers; /* RTC + 0x20 */
+};
+
+/* GPIO */
+struct mpc52xx_gpio {
+ volatile u32 port_config; /* GPIO + 0x00 */
+ volatile u32 simple_gpioe; /* GPIO + 0x04 */
+ volatile u32 simple_ode; /* GPIO + 0x08 */
+ volatile u32 simple_ddr; /* GPIO + 0x0c */
+ volatile u32 simple_dvo; /* GPIO + 0x10 */
+ volatile u32 simple_ival; /* GPIO + 0x14 */
+ volatile u8 outo_gpioe; /* GPIO + 0x18 */
+ volatile u8 reserved1[3]; /* GPIO + 0x19 */
+ volatile u8 outo_dvo; /* GPIO + 0x1c */
+ volatile u8 reserved2[3]; /* GPIO + 0x1d */
+ volatile u8 sint_gpioe; /* GPIO + 0x20 */
+ volatile u8 reserved3[3]; /* GPIO + 0x21 */
+ volatile u8 sint_ode; /* GPIO + 0x24 */
+ volatile u8 reserved4[3]; /* GPIO + 0x25 */
+ volatile u8 sint_ddr; /* GPIO + 0x28 */
+ volatile u8 reserved5[3]; /* GPIO + 0x29 */
+ volatile u8 sint_dvo; /* GPIO + 0x2c */
+ volatile u8 reserved6[3]; /* GPIO + 0x2d */
+ volatile u8 sint_inten; /* GPIO + 0x30 */
+ volatile u8 reserved7[3]; /* GPIO + 0x31 */
+ volatile u16 sint_itype; /* GPIO + 0x34 */
+ volatile u16 reserved8; /* GPIO + 0x36 */
+ volatile u8 gpio_control; /* GPIO + 0x38 */
+ volatile u8 reserved9[3]; /* GPIO + 0x39 */
+ volatile u8 sint_istat; /* GPIO + 0x3c */
+ volatile u8 sint_ival; /* GPIO + 0x3d */
+ volatile u8 bus_errs; /* GPIO + 0x3e */
+ volatile u8 reserved10; /* GPIO + 0x3f */
+};
+
+#define MPC52xx_GPIO_PSC_CONFIG_UART_WITHOUT_CD 4
+#define MPC52xx_GPIO_PSC_CONFIG_UART_WITH_CD 5
+#define MPC52xx_GPIO_PCI_DIS (1<<15)
+
+/* XLB Bus control */
+struct mpc52xx_xlb {
+ volatile u8 reserved[0x40];
+ volatile u32 config; /* XLB + 0x40 */
+ volatile u32 version; /* XLB + 0x44 */
+ volatile u32 status; /* XLB + 0x48 */
+ volatile u32 int_enable; /* XLB + 0x4c */
+ volatile u32 addr_capture; /* XLB + 0x50 */
+ volatile u32 bus_sig_capture; /* XLB + 0x54 */
+ volatile u32 addr_timeout; /* XLB + 0x58 */
+ volatile u32 data_timeout; /* XLB + 0x5c */
+ volatile u32 bus_act_timeout; /* XLB + 0x60 */
+ volatile u32 master_pri_enable; /* XLB + 0x64 */
+ volatile u32 master_priority; /* XLB + 0x68 */
+ volatile u32 base_address; /* XLB + 0x6c */
+ volatile u32 snoop_window; /* XLB + 0x70 */
+};
+
+
+/* Clock Distribution control */
+struct mpc52xx_cdm {
+ volatile u32 jtag_id; /* MBAR_CDM + 0x00 reg0 read only */
+ volatile u32 rstcfg; /* MBAR_CDM + 0x04 reg1 read only */
+ volatile u32 breadcrumb; /* MBAR_CDM + 0x08 reg2 */
+
+ volatile u8 mem_clk_sel; /* MBAR_CDM + 0x0c reg3 byte0 */
+ volatile u8 xlb_clk_sel; /* MBAR_CDM + 0x0d reg3 byte1 read only */
+ volatile u8 ipb_clk_sel; /* MBAR_CDM + 0x0e reg3 byte2 */
+ volatile u8 pci_clk_sel; /* MBAR_CDM + 0x0f reg3 byte3 */
+
+ volatile u8 ext_48mhz_en; /* MBAR_CDM + 0x10 reg4 byte0 */
+ volatile u8 fd_enable; /* MBAR_CDM + 0x11 reg4 byte1 */
+ volatile u16 fd_counters; /* MBAR_CDM + 0x12 reg4 byte2,3 */
+
+ volatile u32 clk_enables; /* MBAR_CDM + 0x14 reg5 */
+
+ volatile u8 osc_disable; /* MBAR_CDM + 0x18 reg6 byte0 */
+ volatile u8 reserved0[3]; /* MBAR_CDM + 0x19 reg6 byte1,2,3 */
+
+ volatile u8 ccs_sleep_enable;/* MBAR_CDM + 0x1c reg7 byte0 */
+ volatile u8 osc_sleep_enable;/* MBAR_CDM + 0x1d reg7 byte1 */
+ volatile u8 reserved1; /* MBAR_CDM + 0x1e reg7 byte2 */
+ volatile u8 ccs_qreq_test; /* MBAR_CDM + 0x1f reg7 byte3 */
+
+ volatile u8 soft_reset; /* MBAR_CDM + 0x20 u8 byte0 */
+ volatile u8 no_ckstp; /* MBAR_CDM + 0x21 u8 byte0 */
+ volatile u8 reserved2[2]; /* MBAR_CDM + 0x22 u8 byte1,2,3 */
+
+ volatile u8 pll_lock; /* MBAR_CDM + 0x24 reg9 byte0 */
+ volatile u8 pll_looselock; /* MBAR_CDM + 0x25 reg9 byte1 */
+ volatile u8 pll_sm_lockwin; /* MBAR_CDM + 0x26 reg9 byte2 */
+ volatile u8 reserved3; /* MBAR_CDM + 0x27 reg9 byte3 */
+
+ volatile u16 reserved4; /* MBAR_CDM + 0x28 reg10 byte0,1 */
+ volatile u16 mclken_div_psc1;/* MBAR_CDM + 0x2a reg10 byte2,3 */
+
+ volatile u16 reserved5; /* MBAR_CDM + 0x2c reg11 byte0,1 */
+ volatile u16 mclken_div_psc2;/* MBAR_CDM + 0x2e reg11 byte2,3 */
+
+ volatile u16 reserved6; /* MBAR_CDM + 0x30 reg12 byte0,1 */
+ volatile u16 mclken_div_psc3;/* MBAR_CDM + 0x32 reg12 byte2,3 */
+
+ volatile u16 reserved7; /* MBAR_CDM + 0x34 reg13 byte0,1 */
+ volatile u16 mclken_div_psc6;/* MBAR_CDM + 0x36 reg13 byte2,3 */
+};
+
+#endif /* __ASSEMBLY__ */
+
+
+/* ========================================================================= */
+/* Prototypes for MPC52xx syslib */
+/* ========================================================================= */
+
+#ifndef __ASSEMBLY__
+
+extern void mpc52xx_init_irq(void);
+extern int mpc52xx_get_irq(struct pt_regs *regs);
+
+extern unsigned long mpc52xx_find_end_of_memory(void);
+extern void mpc52xx_set_bat(void);
+extern void mpc52xx_map_io(void);
+extern void mpc52xx_restart(char *cmd);
+extern void mpc52xx_halt(void);
+extern void mpc52xx_power_off(void);
+extern void mpc52xx_progress(char *s, unsigned short hex);
+extern void mpc52xx_calibrate_decr(void);
+extern void mpc52xx_add_board_devices(struct ocp_def board_ocp[]);
+
+#endif /* __ASSEMBLY__ */
+
+
+/* ========================================================================= */
+/* Platform configuration */
+/* ========================================================================= */
+
+/* The U-Boot platform information struct */
+extern bd_t __res;
+
+/* Platform options */
+#if defined(CONFIG_LITE5200)
+#include <platforms/lite5200.h>
+#endif
+
+
+#endif /* __ASM_MPC52xx_H__ */
--- /dev/null
+/* include/asm-ppc/mpc8260_pci9.h
+ *
+ * Undefine the PCI read* and in* macros so we can define them as functions
+ * that implement the workaround for the MPC8260 device erratum PCI 9.
+ *
+ * This header file should only be included at the end of include/asm-ppc/io.h
+ * and never included directly anywhere else.
+ *
+ * Author: andy_lowe@mvista.com
+ *
+ * 2003 (c) MontaVista Software, Inc. 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.
+ */
+#ifndef _PPC_IO_H
+#error "Do not include mpc8260_pci9.h directly."
+#endif
+
+#ifdef __KERNEL__
+#ifndef __CONFIG_8260_PCI9_DEFS
+#define __CONFIG_8260_PCI9_DEFS
+
+#undef readb
+#undef readw
+#undef readl
+#undef insb
+#undef insw
+#undef insl
+#undef inb
+#undef inw
+#undef inl
+#undef insw_ns
+#undef insl_ns
+#undef memcpy_fromio
+
+extern int readb(volatile unsigned char *addr);
+extern int readw(volatile unsigned short *addr);
+extern unsigned readl(volatile unsigned *addr);
+extern void insb(unsigned port, void *buf, int ns);
+extern void insw(unsigned port, void *buf, int ns);
+extern void insl(unsigned port, void *buf, int nl);
+extern int inb(unsigned port);
+extern int inw(unsigned port);
+extern unsigned inl(unsigned port);
+extern void insw_ns(unsigned port, void *buf, int ns);
+extern void insl_ns(unsigned port, void *buf, int nl);
+extern void *memcpy_fromio(void *dest, unsigned long src, size_t count);
+
+#endif /* !__CONFIG_8260_PCI9_DEFS */
+#endif /* __KERNEL__ */
#ifdef CONFIG_MPC8540_ADS
#include <platforms/85xx/mpc8540_ads.h>
#endif
+#ifdef CONFIG_MPC8555_CDS
+#include <platforms/85xx/mpc8555_cds.h>
+#endif
+#ifdef CONFIG_MPC8560_ADS
+#include <platforms/85xx/mpc8560_ads.h>
+#endif
+#ifdef CONFIG_SBC8560
+#include <platforms/85xx/sbc8560.h>
+#endif
#define _IO_BASE isa_io_base
#define _ISA_MEM_BASE isa_mem_base
+#ifdef CONFIG_PCI
#define PCI_DRAM_OFFSET pci_dram_offset
+#else
+#define PCI_DRAM_OFFSET 0
+#endif
/*
* The "residual" board information structure the boot loader passes
extern int openpic_get_irq(struct pt_regs *regs);
extern void openpic_reset_processor_phys(u_int cpumask);
extern void openpic_setup_ISU(int isu_num, unsigned long addr);
-extern void openpic_cause_IPI(u_int ipi, u_int cpumask);
+extern void openpic_cause_IPI(u_int ipi, cpumask_t cpumask);
extern void smp_openpic_message_pass(int target, int msg, unsigned long data,
int wait);
extern void openpic_set_k2_cascade(int irq);
--- /dev/null
+/*
+ * include/asm-ppc/ppc4xx_dma.h
+ *
+ * IBM PPC4xx DMA engine library
+ *
+ * Copyright 2000-2004 MontaVista Software Inc.
+ *
+ * Cleaned up a bit more, Matt Porter <mporter@kernel.crashing.org>
+ *
+ * Original code by Armin Kuster <akuster@mvista.com>
+ * and Pete Popov <ppopov@mvista.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.
+ *
+ * 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.
+ */
+
+#ifdef __KERNEL__
+#ifndef __ASMPPC_PPC4xx_DMA_H
+#define __ASMPPC_PPC4xx_DMA_H
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <asm/mmu.h>
+#include <asm/ibm4xx.h>
+
+#undef DEBUG_4xxDMA
+
+#define MAX_PPC4xx_DMA_CHANNELS 4
+
+/* in arch/ppc/kernel/setup.c -- Cort */
+extern unsigned long DMA_MODE_WRITE, DMA_MODE_READ;
+
+/*
+ * Function return status codes
+ * These values are used to indicate whether or not the function
+ * call was successful, or a bad/invalid parameter was passed.
+ */
+#define DMA_STATUS_GOOD 0
+#define DMA_STATUS_BAD_CHANNEL 1
+#define DMA_STATUS_BAD_HANDLE 2
+#define DMA_STATUS_BAD_MODE 3
+#define DMA_STATUS_NULL_POINTER 4
+#define DMA_STATUS_OUT_OF_MEMORY 5
+#define DMA_STATUS_SGL_LIST_EMPTY 6
+#define DMA_STATUS_GENERAL_ERROR 7
+#define DMA_STATUS_CHANNEL_NOTFREE 8
+
+#define DMA_CHANNEL_BUSY 0x80000000
+
+/*
+ * These indicate status as returned from the DMA Status Register.
+ */
+#define DMA_STATUS_NO_ERROR 0
+#define DMA_STATUS_CS 1 /* Count Status */
+#define DMA_STATUS_TS 2 /* Transfer Status */
+#define DMA_STATUS_DMA_ERROR 3 /* DMA Error Occurred */
+#define DMA_STATUS_DMA_BUSY 4 /* The channel is busy */
+
+
+/*
+ * DMA Channel Control Registers
+ */
+
+#ifdef CONFIG_44x
+#define PPC4xx_DMA_64BIT
+#define DMA_CR_OFFSET 1
+#else
+#define DMA_CR_OFFSET 0
+#endif
+
+#define DMA_CE_ENABLE (1<<31) /* DMA Channel Enable */
+#define SET_DMA_CE_ENABLE(x) (((x)&0x1)<<31)
+#define GET_DMA_CE_ENABLE(x) (((x)&DMA_CE_ENABLE)>>31)
+
+#define DMA_CIE_ENABLE (1<<30) /* DMA Channel Interrupt Enable */
+#define SET_DMA_CIE_ENABLE(x) (((x)&0x1)<<30)
+#define GET_DMA_CIE_ENABLE(x) (((x)&DMA_CIE_ENABLE)>>30)
+
+#define DMA_TD (1<<29)
+#define SET_DMA_TD(x) (((x)&0x1)<<29)
+#define GET_DMA_TD(x) (((x)&DMA_TD)>>29)
+
+#define DMA_PL (1<<28) /* Peripheral Location */
+#define SET_DMA_PL(x) (((x)&0x1)<<28)
+#define GET_DMA_PL(x) (((x)&DMA_PL)>>28)
+
+#define EXTERNAL_PERIPHERAL 0
+#define INTERNAL_PERIPHERAL 1
+
+#define SET_DMA_PW(x) (((x)&0x3)<<(26-DMA_CR_OFFSET)) /* Peripheral Width */
+#define DMA_PW_MASK SET_DMA_PW(3)
+#define PW_8 0
+#define PW_16 1
+#define PW_32 2
+#define PW_64 3
+/* FIXME: Add PW_128 support for 440GP DMA block */
+#define GET_DMA_PW(x) (((x)&DMA_PW_MASK)>>(26-DMA_CR_OFFSET))
+
+#define DMA_DAI (1<<(25-DMA_CR_OFFSET)) /* Destination Address Increment */
+#define SET_DMA_DAI(x) (((x)&0x1)<<(25-DMA_CR_OFFSET))
+
+#define DMA_SAI (1<<(24-DMA_CR_OFFSET)) /* Source Address Increment */
+#define SET_DMA_SAI(x) (((x)&0x1)<<(24-DMA_CR_OFFSET))
+
+#define DMA_BEN (1<<(23-DMA_CR_OFFSET)) /* Buffer Enable */
+#define SET_DMA_BEN(x) (((x)&0x1)<<(23-DMA_CR_OFFSET))
+
+#define SET_DMA_TM(x) (((x)&0x3)<<(21-DMA_CR_OFFSET)) /* Transfer Mode */
+#define DMA_TM_MASK SET_DMA_TM(3)
+#define TM_PERIPHERAL 0 /* Peripheral */
+#define TM_RESERVED 1 /* Reserved */
+#define TM_S_MM 2 /* Memory to Memory */
+#define TM_D_MM 3 /* Device Paced Memory to Memory */
+#define GET_DMA_TM(x) (((x)&DMA_TM_MASK)>>(21-DMA_CR_OFFSET))
+
+#define SET_DMA_PSC(x) (((x)&0x3)<<(19-DMA_CR_OFFSET)) /* Peripheral Setup Cycles */
+#define DMA_PSC_MASK SET_DMA_PSC(3)
+#define GET_DMA_PSC(x) (((x)&DMA_PSC_MASK)>>(19-DMA_CR_OFFSET))
+
+#define SET_DMA_PWC(x) (((x)&0x3F)<<(13-DMA_CR_OFFSET)) /* Peripheral Wait Cycles */
+#define DMA_PWC_MASK SET_DMA_PWC(0x3F)
+#define GET_DMA_PWC(x) (((x)&DMA_PWC_MASK)>>(13-DMA_CR_OFFSET))
+
+#define SET_DMA_PHC(x) (((x)&0x7)<<(10-DMA_CR_OFFSET)) /* Peripheral Hold Cycles */
+#define DMA_PHC_MASK SET_DMA_PHC(0x7)
+#define GET_DMA_PHC(x) (((x)&DMA_PHC_MASK)>>(10-DMA_CR_OFFSET))
+
+#define DMA_ETD_OUTPUT (1<<(9-DMA_CR_OFFSET)) /* EOT pin is a TC output */
+#define SET_DMA_ETD(x) (((x)&0x1)<<(9-DMA_CR_OFFSET))
+
+#define DMA_TCE_ENABLE (1<<(8-DMA_CR_OFFSET))
+#define SET_DMA_TCE(x) (((x)&0x1)<<(8-DMA_CR_OFFSET))
+
+#define DMA_DEC (1<<(2) /* Address Decrement */
+#define SET_DMA_DEC(x) (((x)&0x1)<<2)
+#define GET_DMA_DEC(x) (((x)&DMA_DEC)>>2)
+
+/*
+ * Transfer Modes
+ * These modes are defined in a way that makes it possible to
+ * simply "or" in the value in the control register.
+ */
+
+#define DMA_MODE_MM (SET_DMA_TM(TM_S_MM)) /* memory to memory */
+
+ /* Device-paced memory to memory, */
+ /* device is at source address */
+#define DMA_MODE_MM_DEVATSRC (DMA_TD | SET_DMA_TM(TM_D_MM))
+
+ /* Device-paced memory to memory, */
+ /* device is at destination address */
+#define DMA_MODE_MM_DEVATDST (SET_DMA_TM(TM_D_MM))
+
+/* 405gp/440gp */
+#define SET_DMA_PREFETCH(x) (((x)&0x3)<<(4-DMA_CR_OFFSET)) /* Memory Read Prefetch */
+#define DMA_PREFETCH_MASK SET_DMA_PREFETCH(3)
+#define PREFETCH_1 0 /* Prefetch 1 Double Word */
+#define PREFETCH_2 1
+#define PREFETCH_4 2
+#define GET_DMA_PREFETCH(x) (((x)&DMA_PREFETCH_MASK)>>(4-DMA_CR_OFFSET))
+
+#define DMA_PCE (1<<(3-DMA_CR_OFFSET)) /* Parity Check Enable */
+#define SET_DMA_PCE(x) (((x)&0x1)<<(3-DMA_CR_OFFSET))
+#define GET_DMA_PCE(x) (((x)&DMA_PCE)>>(3-DMA_CR_OFFSET))
+
+/* stb3x */
+
+#define DMA_ECE_ENABLE (1<<5)
+#define SET_DMA_ECE(x) (((x)&0x1)<<5)
+#define GET_DMA_ECE(x) (((x)&DMA_ECE_ENABLE)>>5)
+
+#define DMA_TCD_DISABLE (1<<4)
+#define SET_DMA_TCD(x) (((x)&0x1)<<4)
+#define GET_DMA_TCD(x) (((x)&DMA_TCD_DISABLE)>>4)
+
+typedef uint32_t sgl_handle_t;
+
+#ifdef CONFIG_PPC4xx_EDMA
+
+#define SGL_LIST_SIZE 4096
+#define DMA_PPC4xx_SIZE SGL_LIST_SIZE
+
+#define SET_DMA_PRIORITY(x) (((x)&0x3)<<(6-DMA_CR_OFFSET)) /* DMA Channel Priority */
+#define DMA_PRIORITY_MASK SET_DMA_PRIORITY(3)
+#define PRIORITY_LOW 0
+#define PRIORITY_MID_LOW 1
+#define PRIORITY_MID_HIGH 2
+#define PRIORITY_HIGH 3
+#define GET_DMA_PRIORITY(x) (((x)&DMA_PRIORITY_MASK)>>(6-DMA_CR_OFFSET))
+
+/*
+ * DMA Polarity Configuration Register
+ */
+#define DMAReq_ActiveLow(chan) (1<<(31-(chan*3)))
+#define DMAAck_ActiveLow(chan) (1<<(30-(chan*3)))
+#define EOT_ActiveLow(chan) (1<<(29-(chan*3))) /* End of Transfer */
+
+/*
+ * DMA Sleep Mode Register
+ */
+#define SLEEP_MODE_ENABLE (1<<21)
+
+/*
+ * DMA Status Register
+ */
+#define DMA_CS0 (1<<31) /* Terminal Count has been reached */
+#define DMA_CS1 (1<<30)
+#define DMA_CS2 (1<<29)
+#define DMA_CS3 (1<<28)
+
+#define DMA_TS0 (1<<27) /* End of Transfer has been requested */
+#define DMA_TS1 (1<<26)
+#define DMA_TS2 (1<<25)
+#define DMA_TS3 (1<<24)
+
+#define DMA_CH0_ERR (1<<23) /* DMA Chanel 0 Error */
+#define DMA_CH1_ERR (1<<22)
+#define DMA_CH2_ERR (1<<21)
+#define DMA_CH3_ERR (1<<20)
+
+#define DMA_IN_DMA_REQ0 (1<<19) /* Internal DMA Request is pending */
+#define DMA_IN_DMA_REQ1 (1<<18)
+#define DMA_IN_DMA_REQ2 (1<<17)
+#define DMA_IN_DMA_REQ3 (1<<16)
+
+#define DMA_EXT_DMA_REQ0 (1<<15) /* External DMA Request is pending */
+#define DMA_EXT_DMA_REQ1 (1<<14)
+#define DMA_EXT_DMA_REQ2 (1<<13)
+#define DMA_EXT_DMA_REQ3 (1<<12)
+
+#define DMA_CH0_BUSY (1<<11) /* DMA Channel 0 Busy */
+#define DMA_CH1_BUSY (1<<10)
+#define DMA_CH2_BUSY (1<<9)
+#define DMA_CH3_BUSY (1<<8)
+
+#define DMA_SG0 (1<<7) /* DMA Channel 0 Scatter/Gather in progress */
+#define DMA_SG1 (1<<6)
+#define DMA_SG2 (1<<5)
+#define DMA_SG3 (1<<4)
+
+/*
+ * DMA SG Command Register
+ */
+#define SSG_ENABLE(chan) (1<<(31-chan)) /* Start Scatter Gather */
+#define SSG_MASK_ENABLE(chan) (1<<(15-chan)) /* Enable writing to SSG0 bit */
+
+/*
+ * DMA Scatter/Gather Descriptor Bit fields
+ */
+#define SG_LINK (1<<31) /* Link */
+#define SG_TCI_ENABLE (1<<29) /* Enable Terminal Count Interrupt */
+#define SG_ETI_ENABLE (1<<28) /* Enable End of Transfer Interrupt */
+#define SG_ERI_ENABLE (1<<27) /* Enable Error Interrupt */
+#define SG_COUNT_MASK 0xFFFF /* Count Field */
+
+#define SET_DMA_CONTROL \
+ (SET_DMA_CIE_ENABLE(p_init->int_enable) | /* interrupt enable */ \
+ SET_DMA_BEN(p_init->buffer_enable) | /* buffer enable */\
+ SET_DMA_ETD(p_init->etd_output) | /* end of transfer pin */ \
+ SET_DMA_TCE(p_init->tce_enable) | /* terminal count enable */ \
+ SET_DMA_PL(p_init->pl) | /* peripheral location */ \
+ SET_DMA_DAI(p_init->dai) | /* dest addr increment */ \
+ SET_DMA_SAI(p_init->sai) | /* src addr increment */ \
+ SET_DMA_PRIORITY(p_init->cp) | /* channel priority */ \
+ SET_DMA_PW(p_init->pwidth) | /* peripheral/bus width */ \
+ SET_DMA_PSC(p_init->psc) | /* peripheral setup cycles */ \
+ SET_DMA_PWC(p_init->pwc) | /* peripheral wait cycles */ \
+ SET_DMA_PHC(p_init->phc) | /* peripheral hold cycles */ \
+ SET_DMA_PREFETCH(p_init->pf) /* read prefetch */)
+
+#define GET_DMA_POLARITY(chan) (DMAReq_ActiveLow(chan) | DMAAck_ActiveLow(chan) | EOT_ActiveLow(chan))
+
+#elif defined(CONFIG_STBXXX_DMA) /* stb03xxx */
+
+#define DMA_PPC4xx_SIZE 4096
+
+/*
+ * DMA Status Register
+ */
+
+#define SET_DMA_PRIORITY(x) (((x)&0x00800001)) /* DMA Channel Priority */
+#define DMA_PRIORITY_MASK 0x00800001
+#define PRIORITY_LOW 0x00000000
+#define PRIORITY_MID_LOW 0x00000001
+#define PRIORITY_MID_HIGH 0x00800000
+#define PRIORITY_HIGH 0x00800001
+#define GET_DMA_PRIORITY(x) (((((x)&DMA_PRIORITY_MASK) &0x00800000) >> 22 ) | (((x)&DMA_PRIORITY_MASK) &0x00000001))
+
+#define DMA_CS0 (1<<31) /* Terminal Count has been reached */
+#define DMA_CS1 (1<<30)
+#define DMA_CS2 (1<<29)
+#define DMA_CS3 (1<<28)
+
+#define DMA_TS0 (1<<27) /* End of Transfer has been requested */
+#define DMA_TS1 (1<<26)
+#define DMA_TS2 (1<<25)
+#define DMA_TS3 (1<<24)
+
+#define DMA_CH0_ERR (1<<23) /* DMA Chanel 0 Error */
+#define DMA_CH1_ERR (1<<22)
+#define DMA_CH2_ERR (1<<21)
+#define DMA_CH3_ERR (1<<20)
+
+#define DMA_CT0 (1<<19) /* Chained transfere */
+
+#define DMA_IN_DMA_REQ0 (1<<18) /* Internal DMA Request is pending */
+#define DMA_IN_DMA_REQ1 (1<<17)
+#define DMA_IN_DMA_REQ2 (1<<16)
+#define DMA_IN_DMA_REQ3 (1<<15)
+
+#define DMA_EXT_DMA_REQ0 (1<<14) /* External DMA Request is pending */
+#define DMA_EXT_DMA_REQ1 (1<<13)
+#define DMA_EXT_DMA_REQ2 (1<<12)
+#define DMA_EXT_DMA_REQ3 (1<<11)
+
+#define DMA_CH0_BUSY (1<<10) /* DMA Channel 0 Busy */
+#define DMA_CH1_BUSY (1<<9)
+#define DMA_CH2_BUSY (1<<8)
+#define DMA_CH3_BUSY (1<<7)
+
+#define DMA_CT1 (1<<6) /* Chained transfere */
+#define DMA_CT2 (1<<5)
+#define DMA_CT3 (1<<4)
+
+#define DMA_CH_ENABLE (1<<7)
+#define SET_DMA_CH(x) (((x)&0x1)<<7)
+#define GET_DMA_CH(x) (((x)&DMA_CH_ENABLE)>>7)
+
+/* STBx25xxx dma unique */
+/* enable device port on a dma channel
+ * example ext 0 on dma 1
+ */
+
+#define SSP0_RECV 15
+#define SSP0_XMIT 14
+#define EXT_DMA_0 12
+#define SC1_XMIT 11
+#define SC1_RECV 10
+#define EXT_DMA_2 9
+#define EXT_DMA_3 8
+#define SERIAL2_XMIT 7
+#define SERIAL2_RECV 6
+#define SC0_XMIT 5
+#define SC0_RECV 4
+#define SERIAL1_XMIT 3
+#define SERIAL1_RECV 2
+#define SERIAL0_XMIT 1
+#define SERIAL0_RECV 0
+
+#define DMA_CHAN_0 1
+#define DMA_CHAN_1 2
+#define DMA_CHAN_2 3
+#define DMA_CHAN_3 4
+
+/* end STBx25xx */
+
+/*
+ * Bit 30 must be one for Redwoods, otherwise transfers may receive errors.
+ */
+#define DMA_CR_MB0 0x2
+
+#define SET_DMA_CONTROL \
+ (SET_DMA_CIE_ENABLE(p_init->int_enable) | /* interrupt enable */ \
+ SET_DMA_ETD(p_init->etd_output) | /* end of transfer pin */ \
+ SET_DMA_TCE(p_init->tce_enable) | /* terminal count enable */ \
+ SET_DMA_PL(p_init->pl) | /* peripheral location */ \
+ SET_DMA_DAI(p_init->dai) | /* dest addr increment */ \
+ SET_DMA_SAI(p_init->sai) | /* src addr increment */ \
+ SET_DMA_PRIORITY(p_init->cp) | /* channel priority */ \
+ SET_DMA_PW(p_init->pwidth) | /* peripheral/bus width */ \
+ SET_DMA_PSC(p_init->psc) | /* peripheral setup cycles */ \
+ SET_DMA_PWC(p_init->pwc) | /* peripheral wait cycles */ \
+ SET_DMA_PHC(p_init->phc) | /* peripheral hold cycles */ \
+ SET_DMA_TCD(p_init->tcd_disable) | /* TC chain mode disable */ \
+ SET_DMA_ECE(p_init->ece_enable) | /* ECE chanin mode enable */ \
+ SET_DMA_CH(p_init->ch_enable) | /* Chain enable */ \
+ DMA_CR_MB0 /* must be one */)
+
+#define GET_DMA_POLARITY(chan) chan
+
+#endif
+
+typedef struct {
+ unsigned short in_use; /* set when channel is being used, clr when
+ * available.
+ */
+ /*
+ * Valid polarity settings:
+ * DMAReq_ActiveLow(n)
+ * DMAAck_ActiveLow(n)
+ * EOT_ActiveLow(n)
+ *
+ * n is 0 to max dma chans
+ */
+ unsigned int polarity;
+
+ char buffer_enable; /* Boolean: buffer enable */
+ char tce_enable; /* Boolean: terminal count enable */
+ char etd_output; /* Boolean: eot pin is a tc output */
+ char pce; /* Boolean: parity check enable */
+
+ /*
+ * Peripheral location:
+ * INTERNAL_PERIPHERAL (UART0 on the 405GP)
+ * EXTERNAL_PERIPHERAL
+ */
+ char pl; /* internal/external peripheral */
+
+ /*
+ * Valid pwidth settings:
+ * PW_8
+ * PW_16
+ * PW_32
+ * PW_64
+ */
+ unsigned int pwidth;
+
+ char dai; /* Boolean: dst address increment */
+ char sai; /* Boolean: src address increment */
+
+ /*
+ * Valid psc settings: 0-3
+ */
+ unsigned int psc; /* Peripheral Setup Cycles */
+
+ /*
+ * Valid pwc settings:
+ * 0-63
+ */
+ unsigned int pwc; /* Peripheral Wait Cycles */
+
+ /*
+ * Valid phc settings:
+ * 0-7
+ */
+ unsigned int phc; /* Peripheral Hold Cycles */
+
+ /*
+ * Valid cp (channel priority) settings:
+ * PRIORITY_LOW
+ * PRIORITY_MID_LOW
+ * PRIORITY_MID_HIGH
+ * PRIORITY_HIGH
+ */
+ unsigned int cp; /* channel priority */
+
+ /*
+ * Valid pf (memory read prefetch) settings:
+ *
+ * PREFETCH_1
+ * PREFETCH_2
+ * PREFETCH_4
+ */
+ unsigned int pf; /* memory read prefetch */
+
+ /*
+ * Boolean: channel interrupt enable
+ * NOTE: for sgl transfers, only the last descriptor will be setup to
+ * interrupt.
+ */
+ char int_enable;
+
+ char shift; /* easy access to byte_count shift, based on */
+ /* the width of the channel */
+
+ uint32_t control; /* channel control word */
+
+ /* These variabled are used ONLY in single dma transfers */
+ unsigned int mode; /* transfer mode */
+ phys_addr_t addr;
+ char ce; /* channel enable */
+#ifdef CONFIG_STB03xxx
+ char ch_enable;
+ char tcd_disable;
+ char ece_enable;
+ char td; /* transfer direction */
+#endif
+
+} ppc_dma_ch_t;
+
+/*
+ * PPC44x DMA implementations have a slightly different
+ * descriptor layout. Probably moved about due to the
+ * change to 64-bit addresses and link pointer. I don't
+ * know why they didn't just leave control_count after
+ * the dst_addr.
+ */
+#ifdef PPC4xx_DMA_64BIT
+typedef struct {
+ uint32_t control;
+ uint32_t control_count;
+ phys_addr_t src_addr;
+ phys_addr_t dst_addr;
+ phys_addr_t next;
+} ppc_sgl_t;
+#else
+typedef struct {
+ uint32_t control;
+ phys_addr_t src_addr;
+ phys_addr_t dst_addr;
+ uint32_t control_count;
+ uint32_t next;
+} ppc_sgl_t;
+#endif
+
+typedef struct {
+ unsigned int dmanr;
+ uint32_t control; /* channel ctrl word; loaded from each descrptr */
+ uint32_t sgl_control; /* LK, TCI, ETI, and ERI bits in sgl descriptor */
+ dma_addr_t dma_addr; /* dma (physical) address of this list */
+ ppc_sgl_t *phead;
+ dma_addr_t phead_dma;
+ ppc_sgl_t *ptail;
+ dma_addr_t ptail_dma;
+} sgl_list_info_t;
+
+typedef struct {
+ phys_addr_t *src_addr;
+ phys_addr_t *dst_addr;
+ phys_addr_t dma_src_addr;
+ phys_addr_t dma_dst_addr;
+} pci_alloc_desc_t;
+
+extern ppc_dma_ch_t dma_channels[];
+
+/*
+ * The DMA API are in ppc4xx_dma.c and ppc4xx_sgdma.c
+ */
+extern int ppc4xx_init_dma_channel(unsigned int, ppc_dma_ch_t *);
+extern int ppc4xx_get_channel_config(unsigned int, ppc_dma_ch_t *);
+extern int ppc4xx_set_channel_priority(unsigned int, unsigned int);
+extern unsigned int ppc4xx_get_peripheral_width(unsigned int);
+extern void ppc4xx_set_sg_addr(int, phys_addr_t);
+extern int ppc4xx_add_dma_sgl(sgl_handle_t, phys_addr_t, phys_addr_t, unsigned int);
+extern void ppc4xx_enable_dma_sgl(sgl_handle_t);
+extern void ppc4xx_disable_dma_sgl(sgl_handle_t);
+extern int ppc4xx_get_dma_sgl_residue(sgl_handle_t, phys_addr_t *, phys_addr_t *);
+extern int ppc4xx_delete_dma_sgl_element(sgl_handle_t, phys_addr_t *, phys_addr_t *);
+extern int ppc4xx_alloc_dma_handle(sgl_handle_t *, unsigned int, unsigned int);
+extern void ppc4xx_free_dma_handle(sgl_handle_t);
+extern int ppc4xx_get_dma_status(void);
+extern void ppc4xx_set_src_addr(int dmanr, phys_addr_t src_addr);
+extern void ppc4xx_set_dst_addr(int dmanr, phys_addr_t dst_addr);
+extern void ppc4xx_enable_dma(unsigned int dmanr);
+extern void ppc4xx_disable_dma(unsigned int dmanr);
+extern void ppc4xx_set_dma_count(unsigned int dmanr, unsigned int count);
+extern int ppc4xx_get_dma_residue(unsigned int dmanr);
+extern void ppc4xx_set_dma_addr2(unsigned int dmanr, phys_addr_t src_dma_addr,
+ phys_addr_t dst_dma_addr);
+extern int ppc4xx_enable_dma_interrupt(unsigned int dmanr);
+extern int ppc4xx_disable_dma_interrupt(unsigned int dmanr);
+extern int ppc4xx_clr_dma_status(unsigned int dmanr);
+extern int ppc4xx_map_dma_port(unsigned int dmanr, unsigned int ocp_dma,short dma_chan);
+extern int ppc4xx_disable_dma_port(unsigned int dmanr, unsigned int ocp_dma,short dma_chan);
+extern int ppc4xx_set_dma_mode(unsigned int dmanr, unsigned int mode);
+
+/* These are in kernel/dma.c: */
+
+/* reserve a DMA channel */
+extern int request_dma(unsigned int dmanr, const char *device_id);
+/* release it again */
+extern void free_dma(unsigned int dmanr);
+#endif
+#endif /* __KERNEL__ */
--- /dev/null
+/*
+ * include/asm-ppc/rheap.c
+ *
+ * Header file for the implementation of a remote heap.
+ *
+ * Author: Pantelis Antoniou <panto@intracom.gr>
+ *
+ * 2004 (c) INTRACOM S.A. Greece. 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.
+ */
+
+#ifndef __ASM_PPC_RHEAP_H__
+#define __ASM_PPC_RHEAP_H__
+
+#include <linux/list.h>
+
+typedef struct _rh_block {
+ struct list_head list;
+ void *start;
+ int size;
+ const char *owner;
+} rh_block_t;
+
+typedef struct _rh_info {
+ unsigned int alignment;
+ int max_blocks;
+ int empty_slots;
+ rh_block_t *block;
+ struct list_head empty_list;
+ struct list_head free_list;
+ struct list_head taken_list;
+ unsigned int flags;
+} rh_info_t;
+
+#define RHIF_STATIC_INFO 0x1
+#define RHIF_STATIC_BLOCK 0x2
+
+typedef struct rh_stats_t {
+ void *start;
+ int size;
+ const char *owner;
+} rh_stats_t;
+
+#define RHGS_FREE 0
+#define RHGS_TAKEN 1
+
+/* Create a remote heap dynamically */
+extern rh_info_t *rh_create(unsigned int alignment);
+
+/* Destroy a remote heap, created by rh_create() */
+extern void rh_destroy(rh_info_t * info);
+
+/* Initialize in place a remote info block */
+extern void rh_init(rh_info_t * info, unsigned int alignment, int max_blocks,
+ rh_block_t * block);
+
+/* Attach a free region to manage */
+extern int rh_attach_region(rh_info_t * info, void *start, int size);
+
+/* Detach a free region */
+extern void *rh_detach_region(rh_info_t * info, void *start, int size);
+
+/* Allocate the given size from the remote heap */
+extern void *rh_alloc(rh_info_t * info, int size, const char *owner);
+
+/* Allocate the given size from the given address */
+extern void *rh_alloc_fixed(rh_info_t * info, void *start, int size,
+ const char *owner);
+
+/* Free the allocated area */
+extern int rh_free(rh_info_t * info, void *start);
+
+/* Get stats for debugging purposes */
+extern int rh_get_stats(rh_info_t * info, int what, int max_stats,
+ rh_stats_t * stats);
+
+/* Simple dump of remote heap info */
+extern void rh_dump(rh_info_t * info);
+
+/* Set owner of taken block */
+extern int rh_set_owner(rh_info_t * info, void *start, const char *owner);
+
+#endif /* __ASM_PPC_RHEAP_H__ */
#define SIG_SETMASK 2 /* for setting the signal mask */
/* Type of a signal handler. */
-typedef void (*__sighandler_t)(int);
+typedef void __signalfn_t(int);
+typedef __signalfn_t __user *__sighandler_t;
+
+typedef void __restorefn_t(void);
+typedef __restorefn_t __user *__sigrestore_t;
#define SIG_DFL ((__sighandler_t)0) /* default signal handling */
#define SIG_IGN ((__sighandler_t)1) /* ignore signal */
__sighandler_t sa_handler;
old_sigset_t sa_mask;
unsigned long sa_flags;
- void (*sa_restorer)(void);
+ __sigrestore_t sa_restorer;
};
struct sigaction {
__sighandler_t sa_handler;
unsigned long sa_flags;
- void (*sa_restorer)(void);
+ __sigrestore_t sa_restorer;
sigset_t sa_mask; /* mask last for extensibility */
};
};
typedef struct sigaltstack {
- void *ss_sp;
+ void __user *ss_sp;
int ss_flags;
size_t ss_size;
} stack_t;
struct ucontext {
unsigned long uc_flags;
- struct ucontext *uc_link;
+ struct ucontext __user *uc_link;
stack_t uc_stack;
int uc_pad[7];
- struct mcontext *uc_regs; /* points to uc_mcontext field */
+ struct mcontext __user *uc_regs;/* points to uc_mcontext field */
sigset_t uc_sigmask;
/* glibc has 1024-bit signal masks, ours are 64-bit */
int uc_maskext[30];
--- /dev/null
+/*
+ * hvcserver.h
+ * Copyright (C) 2004 Ryan S Arnold, IBM Corporation
+ *
+ * PPC64 virtual I/O console server support.
+ *
+ * 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 _PPC64_HVCSERVER_H
+#define _PPC64_HVCSERVER_H
+
+#include <linux/list.h>
+
+/* Converged Location Code length */
+#define HVCS_CLC_LENGTH 79
+
+struct hvcs_partner_info {
+ struct list_head node;
+ unsigned int unit_address;
+ unsigned int partition_ID;
+ char location_code[HVCS_CLC_LENGTH + 1]; /* CLC + 1 null-term char */
+};
+
+extern int hvcs_free_partner_info(struct list_head *head);
+extern int hvcs_get_partner_info(unsigned int unit_address,
+ struct list_head *head, unsigned long *pi_buff);
+extern int hvcs_register_connection(unsigned int unit_address,
+ unsigned int p_partition_ID, unsigned int p_unit_address);
+extern int hvcs_free_connection(unsigned int unit_address);
+
+#endif /* _PPC64_HVCSERVER_H */
* the PCI memory space in the CPU bus space
*/
unsigned long pci_mem_offset;
- unsigned long pci_io_offset;
struct pci_ops *ops;
volatile unsigned int *cfg_addr;
void xics_setup_cpu(void);
void xics_cause_IPI(int cpu);
+/* first argument is ignored for now*/
+void pSeriesLP_cppr_info(int n_cpu, u8 value);
+
struct xics_ipi_struct {
volatile unsigned long value;
} ____cacheline_aligned;
--- /dev/null
+/*
+ * include/asm-sh/bus-sh.h
+ *
+ * Copyright (C) 2004 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#ifndef __ASM_SH_BUS_SH_H
+#define __ASM_SH_BUS_SH_H
+
+extern struct bus_type sh_bus_types[];
+
+struct sh_dev {
+ struct device dev;
+ char *name;
+ unsigned int dev_id;
+ unsigned int bus_id;
+ struct resource res;
+ void *mapbase;
+ unsigned int irq[6];
+ u64 *dma_mask;
+};
+
+#define to_sh_dev(d) container_of((d), struct sh_dev, dev)
+
+#define sh_get_drvdata(d) dev_get_drvdata(&(d)->dev)
+#define sh_set_drvdata(d,p) dev_set_drvdata(&(d)->dev, (p))
+
+struct sh_driver {
+ struct device_driver drv;
+ unsigned int dev_id;
+ unsigned int bus_id;
+ int (*probe)(struct sh_dev *);
+ int (*remove)(struct sh_dev *);
+ int (*suspend)(struct sh_dev *, u32);
+ int (*resume)(struct sh_dev *);
+};
+
+#define to_sh_driver(d) container_of((d), struct sh_driver, drv)
+#define sh_name(d) ((d)->dev.driver->name)
+
+/*
+ * Device ID numbers for bus types
+ */
+enum {
+ SH_DEV_ID_USB_OHCI,
+};
+
+#define SH_NR_BUSES 1
+#define SH_BUS_NAME_VIRT "shbus"
+
+enum {
+ SH_BUS_VIRT,
+};
+
+/* arch/sh/kernel/cpu/bus.c */
+extern int sh_device_register(struct sh_dev *dev);
+extern void sh_device_unregister(struct sh_dev *dev);
+extern int sh_driver_register(struct sh_driver *drv);
+extern void sh_driver_unregister(struct sh_driver *drv);
+
+#endif /* __ASM_SH_BUS_SH_H */
+
--- /dev/null
+/*
+ * fixmap.h: compile-time virtual memory allocation
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1998 Ingo Molnar
+ *
+ * Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999
+ */
+
+#ifndef _ASM_FIXMAP_H
+#define _ASM_FIXMAP_H
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <asm/page.h>
+#ifdef CONFIG_HIGHMEM
+#include <linux/threads.h>
+#include <asm/kmap_types.h>
+#endif
+
+/*
+ * Here we define all the compile-time 'special' virtual
+ * addresses. The point is to have a constant address at
+ * compile time, but to set the physical address only
+ * in the boot process. We allocate these special addresses
+ * from the end of virtual memory (0xfffff000) backwards.
+ * Also this lets us do fail-safe vmalloc(), we
+ * can guarantee that these special addresses and
+ * vmalloc()-ed addresses never overlap.
+ *
+ * these 'compile-time allocated' memory buffers are
+ * fixed-size 4k pages. (or larger if used with an increment
+ * highger than 1) use fixmap_set(idx,phys) to associate
+ * physical memory with fixmap indices.
+ *
+ * TLB entries of such buffers will not be flushed across
+ * task switches.
+ */
+
+/*
+ * on UP currently we will have no trace of the fixmap mechanizm,
+ * no page table allocations, etc. This might change in the
+ * future, say framebuffers for the console driver(s) could be
+ * fix-mapped?
+ */
+enum fixed_addresses {
+#ifdef CONFIG_HIGHMEM
+ FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */
+ FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,
+#endif
+ __end_of_fixed_addresses
+};
+
+extern void __set_fixmap (enum fixed_addresses idx,
+ unsigned long phys, pgprot_t flags);
+
+#define set_fixmap(idx, phys) \
+ __set_fixmap(idx, phys, PAGE_KERNEL)
+/*
+ * Some hardware wants to get fixmapped without caching.
+ */
+#define set_fixmap_nocache(idx, phys) \
+ __set_fixmap(idx, phys, PAGE_KERNEL_NOCACHE)
+/*
+ * used by vmalloc.c.
+ *
+ * Leave one empty page between vmalloc'ed areas and
+ * the start of the fixmap, and leave one page empty
+ * at the top of mem..
+ */
+#define FIXADDR_TOP (P4SEG - PAGE_SIZE)
+#define FIXADDR_SIZE (__end_of_fixed_addresses << PAGE_SHIFT)
+#define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE)
+
+#define __fix_to_virt(x) (FIXADDR_TOP - ((x) << PAGE_SHIFT))
+#define __virt_to_fix(x) ((FIXADDR_TOP - ((x)&PAGE_MASK)) >> PAGE_SHIFT)
+
+extern void __this_fixmap_does_not_exist(void);
+
+/*
+ * 'index to address' translation. If anyone tries to use the idx
+ * directly without tranlation, we catch the bug with a NULL-deference
+ * kernel oops. Illegal ranges of incoming indices are caught too.
+ */
+static inline unsigned long fix_to_virt(const unsigned int idx)
+{
+ /*
+ * this branch gets completely eliminated after inlining,
+ * except when someone tries to use fixaddr indices in an
+ * illegal way. (such as mixing up address types or using
+ * out-of-range indices).
+ *
+ * If it doesn't get removed, the linker will complain
+ * loudly with a reasonably clear error message..
+ */
+ if (idx >= __end_of_fixed_addresses)
+ __this_fixmap_does_not_exist();
+
+ return __fix_to_virt(idx);
+}
+
+static inline unsigned long virt_to_fix(const unsigned long vaddr)
+{
+ BUG_ON(vaddr >= FIXADDR_TOP || vaddr < FIXADDR_START);
+ return __virt_to_fix(vaddr);
+}
+
+#endif
--- /dev/null
+#ifndef __ASM_SH_HP6XX_IDE_H
+#define __ASM_SH_HP6XX_IDE_H
+
+#define IRQ_CFCARD 93
+#define IRQ_PCMCIA 94
+
+#endif /* __ASM_SH_HP6XX_IDE_H */
+
--- /dev/null
+#ifndef __ASM_SH_RENESAS_HS7751RVOIP_H
+#define __ASM_SH_RENESAS_HS7751RVOIP_H
+
+/*
+ * linux/include/asm-sh/hs7751rvoip/hs7751rvoip.h
+ *
+ * Copyright (C) 2000 Atom Create Engineering Co., Ltd.
+ *
+ * Renesas Technology Sales HS7751RVoIP support
+ */
+
+/* Box specific addresses. */
+
+#define PA_BCR 0xa4000000 /* FPGA */
+#define PA_SLICCNTR1 0xa4000006 /* SLIC PIO Control 1 */
+#define PA_SLICCNTR2 0xa4000008 /* SLIC PIO Control 2 */
+#define PA_DMACNTR 0xa400000a /* USB DMA Control */
+#define PA_INPORTR 0xa400000c /* Input Port Register */
+#define PA_OUTPORTR 0xa400000e /* Output Port Reguster */
+#define PA_VERREG 0xa4000014 /* FPGA Version Register */
+
+#define PA_AREA5_IO 0xb4000000 /* Area 5 IO Memory */
+#define PA_AREA6_IO 0xb8000000 /* Area 6 IO Memory */
+#define PA_IDE_OFFSET 0x1f0 /* CF IDE Offset */
+
+#define IRLCNTR1 (PA_BCR + 0) /* Interrupt Control Register1 */
+#define IRLCNTR2 (PA_BCR + 2) /* Interrupt Control Register2 */
+#define IRLCNTR3 (PA_BCR + 4) /* Interrupt Control Register3 */
+#define IRLCNTR4 (PA_BCR + 16) /* Interrupt Control Register4 */
+#define IRLCNTR5 (PA_BCR + 18) /* Interrupt Control Register5 */
+
+#define IRQ_PCIETH 6 /* PCI Ethernet IRQ */
+#define IRQ_PCIHUB 7 /* PCI Ethernet Hub IRQ */
+#define IRQ_USBCOM 8 /* USB Comunication IRQ */
+#define IRQ_USBCON 9 /* USB Connect IRQ */
+#define IRQ_USBDMA 10 /* USB DMA IRQ */
+#define IRQ_CFCARD 11 /* CF Card IRQ */
+#define IRQ_PCMCIA 12 /* PCMCIA IRQ */
+#define IRQ_PCISLOT 13 /* PCI Slot #1 IRQ */
+#define IRQ_ONHOOK1 0 /* ON HOOK1 IRQ */
+#define IRQ_OFFHOOK1 1 /* OFF HOOK1 IRQ */
+#define IRQ_ONHOOK2 2 /* ON HOOK2 IRQ */
+#define IRQ_OFFHOOK2 3 /* OFF HOOK2 IRQ */
+#define IRQ_RINGING 4 /* Ringing IRQ */
+#define IRQ_CODEC 5 /* CODEC IRQ */
+
+#endif /* __ASM_SH_RENESAS_HS7751RVOIP */
--- /dev/null
+#ifndef __ASM_SH_HS7751RVOIP_IDE_H
+#define __ASM_SH_HS7751RVOIP_IDE_H
+
+/* Nothing to see here.. */
+#include <asm/hs7751rvoip/hs7751rvoip.h>
+
+#endif /* __ASM_SH_HS7751RVOIP_IDE_H */
+
--- /dev/null
+/*
+ * include/asm-sh/hs7751rvoip/hs7751rvoip.h
+ *
+ * Modified version of io_se.h for the hs7751rvoip-specific functions.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * IO functions for an Renesas Technology sales HS7751RVOIP
+ */
+
+#ifndef _ASM_SH_IO_HS7751RVOIP_H
+#define _ASM_SH_IO_HS7751RVOIP_H
+
+#include <asm/io_generic.h>
+
+extern unsigned char hs7751rvoip_inb(unsigned long port);
+extern unsigned short hs7751rvoip_inw(unsigned long port);
+extern unsigned int hs7751rvoip_inl(unsigned long port);
+
+extern void hs7751rvoip_outb(unsigned char value, unsigned long port);
+extern void hs7751rvoip_outw(unsigned short value, unsigned long port);
+extern void hs7751rvoip_outl(unsigned int value, unsigned long port);
+
+extern unsigned char hs7751rvoip_inb_p(unsigned long port);
+extern void hs7751rvoip_outb_p(unsigned char value, unsigned long port);
+
+extern void hs7751rvoip_insb(unsigned long port, void *addr, unsigned long count);
+extern void hs7751rvoip_insw(unsigned long port, void *addr, unsigned long count);
+extern void hs7751rvoip_insl(unsigned long port, void *addr, unsigned long count);
+extern void hs7751rvoip_outsb(unsigned long port, const void *addr, unsigned long count);
+extern void hs7751rvoip_outsw(unsigned long port, const void *addr, unsigned long count);
+extern void hs7751rvoip_outsl(unsigned long port, const void *addr, unsigned long count);
+
+extern void *hs7751rvoip_ioremap(unsigned long offset, unsigned long size);
+
+extern unsigned long hs7751rvoip_isa_port2addr(unsigned long offset);
+
+#endif /* _ASM_SH_IO_HS7751RVOIP_H */
--- /dev/null
+#ifndef __ASM_SH_RTS7751R2D_IDE_H
+#define __ASM_SH_RTS7751R2D_IDE_H
+
+/* Nothing to see here.. */
+#include <asm/rts7751r2d/rts7751r2d.h>
+
+#endif /* __ASM_SH_RTS7751R2D_IDE_H */
+
--- /dev/null
+/*
+ * include/asm-sh/io_rts7751r2d.h
+ *
+ * Modified version of io_se.h for the rts7751r2d-specific functions.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * IO functions for an Renesas Technology sales RTS7751R2D
+ */
+
+#ifndef _ASM_SH_IO_RTS7751R2D_H
+#define _ASM_SH_IO_RTS7751R2D_H
+
+extern unsigned char rts7751r2d_inb(unsigned long port);
+extern unsigned short rts7751r2d_inw(unsigned long port);
+extern unsigned int rts7751r2d_inl(unsigned long port);
+
+extern void rts7751r2d_outb(unsigned char value, unsigned long port);
+extern void rts7751r2d_outw(unsigned short value, unsigned long port);
+extern void rts7751r2d_outl(unsigned int value, unsigned long port);
+
+extern unsigned char rts7751r2d_inb_p(unsigned long port);
+extern void rts7751r2d_outb_p(unsigned char value, unsigned long port);
+
+extern void rts7751r2d_insb(unsigned long port, void *addr, unsigned long count);
+extern void rts7751r2d_insw(unsigned long port, void *addr, unsigned long count);
+extern void rts7751r2d_insl(unsigned long port, void *addr, unsigned long count);
+extern void rts7751r2d_outsb(unsigned long port, const void *addr, unsigned long count);
+extern void rts7751r2d_outsw(unsigned long port, const void *addr, unsigned long count);
+extern void rts7751r2d_outsl(unsigned long port, const void *addr, unsigned long count);
+
+extern void *rts7751r2d_ioremap(unsigned long offset, unsigned long size);
+
+extern unsigned long rts7751r2d_isa_port2addr(unsigned long offset);
+
+#endif /* _ASM_SH_IO_RTS7751R2D_H */
--- /dev/null
+#ifndef __ASM_SH_RENESAS_RTS7751R2D_H
+#define __ASM_SH_RENESAS_RTS7751R2D_H
+
+/*
+ * linux/include/asm-sh/renesas_rts7751r2d.h
+ *
+ * Copyright (C) 2000 Atom Create Engineering Co., Ltd.
+ *
+ * Renesas Technology Sales RTS7751R2D support
+ */
+
+/* Box specific addresses. */
+
+#define PA_BCR 0xa4000000 /* FPGA */
+#define PA_IRLMON 0xa4000002 /* Interrupt Status control */
+#define PA_CFCTL 0xa4000004 /* CF Timing control */
+#define PA_CFPOW 0xa4000006 /* CF Power control */
+#define PA_DISPCTL 0xa4000008 /* Display Timing control */
+#define PA_SDMPOW 0xa400000a /* SD Power control */
+#define PA_RTCCE 0xa400000c /* RTC(9701) Enable control */
+#define PA_PCICD 0xa400000e /* PCI Extention detect control */
+#define PA_VOYAGERRTS 0xa4000020 /* VOYAGER Reset control */
+#if defined(CONFIG_RTS7751R2D_REV11)
+#define PA_AXRST 0xa4000022 /* AX_LAN Reset control */
+#define PA_CFRST 0xa4000024 /* CF Reset control */
+#define PA_ADMRTS 0xa4000026 /* SD Reset control */
+#define PA_EXTRST 0xa4000028 /* Extention Reset control */
+#define PA_CFCDINTCLR 0xa400002a /* CF Insert Interrupt clear */
+#else
+#define PA_CFRST 0xa4000022 /* CF Reset control */
+#define PA_ADMRTS 0xa4000024 /* SD Reset control */
+#define PA_EXTRST 0xa4000026 /* Extention Reset control */
+#define PA_CFCDINTCLR 0xa4000028 /* CF Insert Interrupt clear */
+#define PA_KEYCTLCLR 0xa400002a /* Key Interrupt clear */
+#endif
+#define PA_POWOFF 0xa4000030 /* Board Power OFF control */
+#define PA_VERREG 0xa4000032 /* FPGA Version Register */
+#define PA_INPORT 0xa4000034 /* KEY Input Port control */
+#define PA_OUTPORT 0xa4000036 /* LED control */
+#define PA_DMPORT 0xa4000038 /* DM270 Output Port control */
+
+#define PA_AX88796L 0xaa000400 /* AX88796L Area */
+#define PA_VOYAGER 0xab000000 /* VOYAGER GX Area */
+#define PA_AREA5_IO 0xb4000000 /* Area 5 IO Memory */
+#define PA_AREA6_IO 0xb8000000 /* Area 6 IO Memory */
+#define PA_IDE_OFFSET 0x1f0 /* CF IDE Offset */
+#define AX88796L_IO_BASE 0x1000 /* AX88796L IO Base Address */
+
+#define IRLCNTR1 (PA_BCR + 0) /* Interrupt Control Register1 */
+
+#if defined(CONFIG_RTS7751R2D_REV11)
+#define IRQ_PCIETH 0 /* PCI Ethernet IRQ */
+#define IRQ_CFCARD 1 /* CF Card IRQ */
+#define IRQ_CFINST 2 /* CF Card Insert IRQ */
+#define IRQ_PCMCIA 3 /* PCMCIA IRQ */
+#define IRQ_VOYAGER 4 /* VOYAGER IRQ */
+#define IRQ_ONETH 5 /* On board Ethernet IRQ */
+#else
+#define IRQ_KEYIN 0 /* Key Input IRQ */
+#define IRQ_PCIETH 1 /* PCI Ethernet IRQ */
+#define IRQ_CFCARD 2 /* CF Card IRQ */
+#define IRQ_CFINST 3 /* CF Card Insert IRQ */
+#define IRQ_PCMCIA 4 /* PCMCIA IRQ */
+#define IRQ_VOYAGER 5 /* VOYAGER IRQ */
+#endif
+#define IRQ_RTCALM 6 /* RTC Alarm IRQ */
+#define IRQ_RTCTIME 7 /* RTC Timer IRQ */
+#define IRQ_SDCARD 8 /* SD Card IRQ */
+#define IRQ_PCISLOT1 9 /* PCI Slot #1 IRQ */
+#define IRQ_PCISLOT2 10 /* PCI Slot #2 IRQ */
+#define IRQ_EXTENTION 11 /* EXTn IRQ */
+
+#endif /* __ASM_SH_RENESAS_RTS7751R2D */
--- /dev/null
+/* -------------------------------------------------------------------- */
+/* voyagergx_reg.h */
+/* -------------------------------------------------------------------- */
+/* This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Copyright 2003 (c) Lineo uSolutions,Inc.
+*/
+/* -------------------------------------------------------------------- */
+
+#ifndef _VOYAGER_GX_REG_H
+#define _VOYAGER_GX_REG_H
+
+#define VOYAGER_BASE 0xb3e00000
+#define VOYAGER_USBH_BASE (0x40000 + VOYAGER_BASE)
+#define VOYAGER_UART_BASE (0x30000 + VOYAGER_BASE)
+#define VOYAGER_AC97_BASE (0xa0000 + VOYAGER_BASE)
+
+#define VOYAGER_IRQ_NUM 32
+#define VOYAGER_IRQ_BASE 50
+#define VOYAGER_USBH_IRQ VOYAGER_IRQ_BASE + 6
+#define VOYAGER_8051_IRQ VOYAGER_IRQ_BASE + 10
+#define VOYAGER_UART0_IRQ VOYAGER_IRQ_BASE + 12
+#define VOYAGER_UART1_IRQ VOYAGER_IRQ_BASE + 13
+#define VOYAGER_AC97_IRQ VOYAGER_IRQ_BASE + 17
+
+/* ----- MISC controle register ------------------------------ */
+#define MISC_CTRL (0x000004 + VOYAGER_BASE)
+#define MISC_CTRL_USBCLK_48 (3 << 28)
+#define MISC_CTRL_USBCLK_96 (2 << 28)
+#define MISC_CTRL_USBCLK_CRYSTAL (1 << 28)
+
+/* ----- GPIO[31:0] register --------------------------------- */
+#define GPIO_MUX_LOW (0x000008 + VOYAGER_BASE)
+#define GPIO_MUX_LOW_AC97 0x1F000000
+#define GPIO_MUX_LOW_8051 0x0000ffff
+#define GPIO_MUX_LOW_PWM (1 << 29)
+
+/* ----- GPIO[63:32] register --------------------------------- */
+#define GPIO_MUX_HIGH (0x00000C + VOYAGER_BASE)
+
+/* ----- DRAM controle register ------------------------------- */
+#define DRAM_CTRL (0x000010 + VOYAGER_BASE)
+#define DRAM_CTRL_EMBEDDED (1 << 31)
+#define DRAM_CTRL_CPU_BURST_1 (0 << 28)
+#define DRAM_CTRL_CPU_BURST_2 (1 << 28)
+#define DRAM_CTRL_CPU_BURST_4 (2 << 28)
+#define DRAM_CTRL_CPU_BURST_8 (3 << 28)
+#define DRAM_CTRL_CPU_CAS_LATENCY (1 << 27)
+#define DRAM_CTRL_CPU_SIZE_2 (0 << 24)
+#define DRAM_CTRL_CPU_SIZE_4 (1 << 24)
+#define DRAM_CTRL_CPU_SIZE_64 (4 << 24)
+#define DRAM_CTRL_CPU_SIZE_32 (5 << 24)
+#define DRAM_CTRL_CPU_SIZE_16 (6 << 24)
+#define DRAM_CTRL_CPU_SIZE_8 (7 << 24)
+#define DRAM_CTRL_CPU_COLUMN_SIZE_1024 (0 << 22)
+#define DRAM_CTRL_CPU_COLUMN_SIZE_512 (2 << 22)
+#define DRAM_CTRL_CPU_COLUMN_SIZE_256 (3 << 22)
+#define DRAM_CTRL_CPU_ACTIVE_PRECHARGE (1 << 21)
+#define DRAM_CTRL_CPU_RESET (1 << 20)
+#define DRAM_CTRL_CPU_BANKS (1 << 19)
+#define DRAM_CTRL_CPU_WRITE_PRECHARGE (1 << 18)
+#define DRAM_CTRL_BLOCK_WRITE (1 << 17)
+#define DRAM_CTRL_REFRESH_COMMAND (1 << 16)
+#define DRAM_CTRL_SIZE_4 (0 << 13)
+#define DRAM_CTRL_SIZE_8 (1 << 13)
+#define DRAM_CTRL_SIZE_16 (2 << 13)
+#define DRAM_CTRL_SIZE_32 (3 << 13)
+#define DRAM_CTRL_SIZE_64 (4 << 13)
+#define DRAM_CTRL_SIZE_2 (5 << 13)
+#define DRAM_CTRL_COLUMN_SIZE_256 (0 << 11)
+#define DRAM_CTRL_COLUMN_SIZE_512 (2 << 11)
+#define DRAM_CTRL_COLUMN_SIZE_1024 (3 << 11)
+#define DRAM_CTRL_BLOCK_WRITE_TIME (1 << 10)
+#define DRAM_CTRL_BLOCK_WRITE_PRECHARGE (1 << 9)
+#define DRAM_CTRL_ACTIVE_PRECHARGE (1 << 8)
+#define DRAM_CTRL_RESET (1 << 7)
+#define DRAM_CTRL_REMAIN_ACTIVE (1 << 6)
+#define DRAM_CTRL_BANKS (1 << 1)
+#define DRAM_CTRL_WRITE_PRECHARGE (1 << 0)
+
+/* ----- Arvitration control register -------------------------- */
+#define ARBITRATION_CTRL (0x000014 + VOYAGER_BASE)
+#define ARBITRATION_CTRL_CPUMEM (1 << 29)
+#define ARBITRATION_CTRL_INTMEM (1 << 28)
+#define ARBITRATION_CTRL_USB_OFF (0 << 24)
+#define ARBITRATION_CTRL_USB_PRIORITY_1 (1 << 24)
+#define ARBITRATION_CTRL_USB_PRIORITY_2 (2 << 24)
+#define ARBITRATION_CTRL_USB_PRIORITY_3 (3 << 24)
+#define ARBITRATION_CTRL_USB_PRIORITY_4 (4 << 24)
+#define ARBITRATION_CTRL_USB_PRIORITY_5 (5 << 24)
+#define ARBITRATION_CTRL_USB_PRIORITY_6 (6 << 24)
+#define ARBITRATION_CTRL_USB_PRIORITY_7 (7 << 24)
+#define ARBITRATION_CTRL_PANEL_OFF (0 << 20)
+#define ARBITRATION_CTRL_PANEL_PRIORITY_1 (1 << 20)
+#define ARBITRATION_CTRL_PANEL_PRIORITY_2 (2 << 20)
+#define ARBITRATION_CTRL_PANEL_PRIORITY_3 (3 << 20)
+#define ARBITRATION_CTRL_PANEL_PRIORITY_4 (4 << 20)
+#define ARBITRATION_CTRL_PANEL_PRIORITY_5 (5 << 20)
+#define ARBITRATION_CTRL_PANEL_PRIORITY_6 (6 << 20)
+#define ARBITRATION_CTRL_PANEL_PRIORITY_7 (7 << 20)
+#define ARBITRATION_CTRL_ZVPORT_OFF (0 << 16)
+#define ARBITRATION_CTRL_ZVPORTL_PRIORITY_1 (1 << 16)
+#define ARBITRATION_CTRL_ZVPORTL_PRIORITY_2 (2 << 16)
+#define ARBITRATION_CTRL_ZVPORTL_PRIORITY_3 (3 << 16)
+#define ARBITRATION_CTRL_ZVPORTL_PRIORITY_4 (4 << 16)
+#define ARBITRATION_CTRL_ZVPORTL_PRIORITY_5 (5 << 16)
+#define ARBITRATION_CTRL_ZVPORTL_PRIORITY_6 (6 << 16)
+#define ARBITRATION_CTRL_ZVPORTL_PRIORITY_7 (7 << 16)
+#define ARBITRATION_CTRL_CMD_INTPR_OFF (0 << 12)
+#define ARBITRATION_CTRL_CMD_INTPR_PRIORITY_1 (1 << 12)
+#define ARBITRATION_CTRL_CMD_INTPR_PRIORITY_2 (2 << 12)
+#define ARBITRATION_CTRL_CMD_INTPR_PRIORITY_3 (3 << 12)
+#define ARBITRATION_CTRL_CMD_INTPR_PRIORITY_4 (4 << 12)
+#define ARBITRATION_CTRL_CMD_INTPR_PRIORITY_5 (5 << 12)
+#define ARBITRATION_CTRL_CMD_INTPR_PRIORITY_6 (6 << 12)
+#define ARBITRATION_CTRL_CMD_INTPR_PRIORITY_7 (7 << 12)
+#define ARBITRATION_CTRL_DMA_OFF (0 << 8)
+#define ARBITRATION_CTRL_DMA_PRIORITY_1 (1 << 8)
+#define ARBITRATION_CTRL_DMA_PRIORITY_2 (2 << 8)
+#define ARBITRATION_CTRL_DMA_PRIORITY_3 (3 << 8)
+#define ARBITRATION_CTRL_DMA_PRIORITY_4 (4 << 8)
+#define ARBITRATION_CTRL_DMA_PRIORITY_5 (5 << 8)
+#define ARBITRATION_CTRL_DMA_PRIORITY_6 (6 << 8)
+#define ARBITRATION_CTRL_DMA_PRIORITY_7 (7 << 8)
+#define ARBITRATION_CTRL_VIDEO_OFF (0 << 4)
+#define ARBITRATION_CTRL_VIDEO_PRIORITY_1 (1 << 4)
+#define ARBITRATION_CTRL_VIDEO_PRIORITY_2 (2 << 4)
+#define ARBITRATION_CTRL_VIDEO_PRIORITY_3 (3 << 4)
+#define ARBITRATION_CTRL_VIDEO_PRIORITY_4 (4 << 4)
+#define ARBITRATION_CTRL_VIDEO_PRIORITY_5 (5 << 4)
+#define ARBITRATION_CTRL_VIDEO_PRIORITY_6 (6 << 4)
+#define ARBITRATION_CTRL_VIDEO_PRIORITY_7 (7 << 4)
+#define ARBITRATION_CTRL_CRT_OFF (0 << 0)
+#define ARBITRATION_CTRL_CRT_PRIORITY_1 (1 << 0)
+#define ARBITRATION_CTRL_CRT_PRIORITY_2 (2 << 0)
+#define ARBITRATION_CTRL_CRT_PRIORITY_3 (3 << 0)
+#define ARBITRATION_CTRL_CRT_PRIORITY_4 (4 << 0)
+#define ARBITRATION_CTRL_CRT_PRIORITY_5 (5 << 0)
+#define ARBITRATION_CTRL_CRT_PRIORITY_6 (6 << 0)
+#define ARBITRATION_CTRL_CRT_PRIORITY_7 (7 << 0)
+
+/* ----- Command list status register -------------------------- */
+#define CMD_INTPR_STATUS (0x000024 + VOYAGER_BASE)
+
+/* ----- Interrupt status register ----------------------------- */
+#define INT_STATUS (0x00002c + VOYAGER_BASE)
+#define INT_STATUS_UH (1 << 6)
+#define INT_STATUS_MC (1 << 10)
+#define INT_STATUS_U0 (1 << 12)
+#define INT_STATUS_U1 (1 << 13)
+#define INT_STATUS_AC (1 << 17)
+
+/* ----- Interrupt mask register ------------------------------ */
+#define VOYAGER_INT_MASK (0x000030 + VOYAGER_BASE)
+#define VOYAGER_INT_MASK_AC (1 << 17)
+
+/* ----- Current Gate register ---------------------------------*/
+#define CURRENT_GATE (0x000038 + VOYAGER_BASE)
+
+/* ----- Power mode 0 gate register --------------------------- */
+#define POWER_MODE0_GATE (0x000040 + VOYAGER_BASE)
+#define POWER_MODE0_GATE_G (1 << 6)
+#define POWER_MODE0_GATE_U0 (1 << 7)
+#define POWER_MODE0_GATE_U1 (1 << 8)
+#define POWER_MODE0_GATE_UH (1 << 11)
+#define POWER_MODE0_GATE_AC (1 << 18)
+
+/* ----- Power mode 1 gate register --------------------------- */
+#define POWER_MODE1_GATE (0x000048 + VOYAGER_BASE)
+#define POWER_MODE1_GATE_G (1 << 6)
+#define POWER_MODE1_GATE_U0 (1 << 7)
+#define POWER_MODE1_GATE_U1 (1 << 8)
+#define POWER_MODE1_GATE_UH (1 << 11)
+#define POWER_MODE1_GATE_AC (1 << 18)
+
+/* ----- Power mode 0 clock register -------------------------- */
+#define POWER_MODE0_CLOCK (0x000044 + VOYAGER_BASE)
+
+/* ----- Power mode 1 clock register -------------------------- */
+#define POWER_MODE1_CLOCK (0x00004C + VOYAGER_BASE)
+
+/* ----- Power mode controll register ------------------------- */
+#define POWER_MODE_CTRL (0x000054 + VOYAGER_BASE)
+
+/* ----- Miscellaneous Timing register ------------------------ */
+#define SYSTEM_DRAM_CTRL (0x000068 + VOYAGER_BASE)
+
+/* ----- PWM register ------------------------------------------*/
+#define PWM_0 (0x010020 + VOYAGER_BASE)
+#define PWM_0_HC(x) (((x)&0x0fff)<<20)
+#define PWM_0_LC(x) (((x)&0x0fff)<<8 )
+#define PWM_0_CLK_DEV(x) (((x)&0x000f)<<4 )
+#define PWM_0_EN (1<<0)
+
+/* ----- I2C register ----------------------------------------- */
+#define I2C_BYTECOUNT (0x010040 + VOYAGER_BASE)
+#define I2C_CONTROL (0x010041 + VOYAGER_BASE)
+#define I2C_STATUS (0x010042 + VOYAGER_BASE)
+#define I2C_RESET (0x010042 + VOYAGER_BASE)
+#define I2C_SADDRESS (0x010043 + VOYAGER_BASE)
+#define I2C_DATA (0x010044 + VOYAGER_BASE)
+
+/* ----- Controle register bits ----------------------------------------- */
+#define I2C_CONTROL_E (1 << 0)
+#define I2C_CONTROL_MODE (1 << 1)
+#define I2C_CONTROL_STATUS (1 << 2)
+#define I2C_CONTROL_INT (1 << 4)
+#define I2C_CONTROL_INTACK (1 << 5)
+#define I2C_CONTROL_REPEAT (1 << 6)
+
+/* ----- Status register bits ----------------------------------------- */
+#define I2C_STATUS_BUSY (1 << 0)
+#define I2C_STATUS_ACK (1 << 1)
+#define I2C_STATUS_ERROR (1 << 2)
+#define I2C_STATUS_COMPLETE (1 << 3)
+
+/* ----- Reset register ---------------------------------------------- */
+#define I2C_RESET_ERROR (1 << 2)
+
+/* ----- transmission frequencies ------------------------------------- */
+#define I2C_SADDRESS_SELECT (1 << 0)
+
+/* ----- Display Controll register ----------------------------------------- */
+#define PANEL_DISPLAY_CTRL (0x080000 + VOYAGER_BASE)
+#define PANEL_DISPLAY_CTRL_BIAS (1<<26)
+#define PANEL_PAN_CTRL (0x080004 + VOYAGER_BASE)
+#define PANEL_COLOR_KEY (0x080008 + VOYAGER_BASE)
+#define PANEL_FB_ADDRESS (0x08000C + VOYAGER_BASE)
+#define PANEL_FB_WIDTH (0x080010 + VOYAGER_BASE)
+#define PANEL_WINDOW_WIDTH (0x080014 + VOYAGER_BASE)
+#define PANEL_WINDOW_HEIGHT (0x080018 + VOYAGER_BASE)
+#define PANEL_PLANE_TL (0x08001C + VOYAGER_BASE)
+#define PANEL_PLANE_BR (0x080020 + VOYAGER_BASE)
+#define PANEL_HORIZONTAL_TOTAL (0x080024 + VOYAGER_BASE)
+#define PANEL_HORIZONTAL_SYNC (0x080028 + VOYAGER_BASE)
+#define PANEL_VERTICAL_TOTAL (0x08002C + VOYAGER_BASE)
+#define PANEL_VERTICAL_SYNC (0x080030 + VOYAGER_BASE)
+#define PANEL_CURRENT_LINE (0x080034 + VOYAGER_BASE)
+#define VIDEO_DISPLAY_CTRL (0x080040 + VOYAGER_BASE)
+#define VIDEO_FB_0_ADDRESS (0x080044 + VOYAGER_BASE)
+#define VIDEO_FB_WIDTH (0x080048 + VOYAGER_BASE)
+#define VIDEO_FB_0_LAST_ADDRESS (0x08004C + VOYAGER_BASE)
+#define VIDEO_PLANE_TL (0x080050 + VOYAGER_BASE)
+#define VIDEO_PLANE_BR (0x080054 + VOYAGER_BASE)
+#define VIDEO_SCALE (0x080058 + VOYAGER_BASE)
+#define VIDEO_INITIAL_SCALE (0x08005C + VOYAGER_BASE)
+#define VIDEO_YUV_CONSTANTS (0x080060 + VOYAGER_BASE)
+#define VIDEO_FB_1_ADDRESS (0x080064 + VOYAGER_BASE)
+#define VIDEO_FB_1_LAST_ADDRESS (0x080068 + VOYAGER_BASE)
+#define VIDEO_ALPHA_DISPLAY_CTRL (0x080080 + VOYAGER_BASE)
+#define VIDEO_ALPHA_FB_ADDRESS (0x080084 + VOYAGER_BASE)
+#define VIDEO_ALPHA_FB_WIDTH (0x080088 + VOYAGER_BASE)
+#define VIDEO_ALPHA_FB_LAST_ADDRESS (0x08008C + VOYAGER_BASE)
+#define VIDEO_ALPHA_PLANE_TL (0x080090 + VOYAGER_BASE)
+#define VIDEO_ALPHA_PLANE_BR (0x080094 + VOYAGER_BASE)
+#define VIDEO_ALPHA_SCALE (0x080098 + VOYAGER_BASE)
+#define VIDEO_ALPHA_INITIAL_SCALE (0x08009C + VOYAGER_BASE)
+#define VIDEO_ALPHA_CHROMA_KEY (0x0800A0 + VOYAGER_BASE)
+#define PANEL_HWC_ADDRESS (0x0800F0 + VOYAGER_BASE)
+#define PANEL_HWC_LOCATION (0x0800F4 + VOYAGER_BASE)
+#define PANEL_HWC_COLOR_12 (0x0800F8 + VOYAGER_BASE)
+#define PANEL_HWC_COLOR_3 (0x0800FC + VOYAGER_BASE)
+#define ALPHA_DISPLAY_CTRL (0x080100 + VOYAGER_BASE)
+#define ALPHA_FB_ADDRESS (0x080104 + VOYAGER_BASE)
+#define ALPHA_FB_WIDTH (0x080108 + VOYAGER_BASE)
+#define ALPHA_PLANE_TL (0x08010C + VOYAGER_BASE)
+#define ALPHA_PLANE_BR (0x080110 + VOYAGER_BASE)
+#define ALPHA_CHROMA_KEY (0x080114 + VOYAGER_BASE)
+#define CRT_DISPLAY_CTRL (0x080200 + VOYAGER_BASE)
+#define CRT_FB_ADDRESS (0x080204 + VOYAGER_BASE)
+#define CRT_FB_WIDTH (0x080208 + VOYAGER_BASE)
+#define CRT_HORIZONTAL_TOTAL (0x08020C + VOYAGER_BASE)
+#define CRT_HORIZONTAL_SYNC (0x080210 + VOYAGER_BASE)
+#define CRT_VERTICAL_TOTAL (0x080214 + VOYAGER_BASE)
+#define CRT_VERTICAL_SYNC (0x080218 + VOYAGER_BASE)
+#define CRT_SIGNATURE_ANALYZER (0x08021C + VOYAGER_BASE)
+#define CRT_CURRENT_LINE (0x080220 + VOYAGER_BASE)
+#define CRT_MONITOR_DETECT (0x080224 + VOYAGER_BASE)
+#define CRT_HWC_ADDRESS (0x080230 + VOYAGER_BASE)
+#define CRT_HWC_LOCATION (0x080234 + VOYAGER_BASE)
+#define CRT_HWC_COLOR_12 (0x080238 + VOYAGER_BASE)
+#define CRT_HWC_COLOR_3 (0x08023C + VOYAGER_BASE)
+#define CRT_PALETTE_RAM (0x080400 + VOYAGER_BASE)
+#define PANEL_PALETTE_RAM (0x080800 + VOYAGER_BASE)
+#define VIDEO_PALETTE_RAM (0x080C00 + VOYAGER_BASE)
+
+/* ----- 8051 Controle register ----------------------------------------- */
+#define VOYAGER_8051_BASE (0x000c0000 + VOYAGER_BASE)
+#define VOYAGER_8051_RESET (0x000b0000 + VOYAGER_BASE)
+#define VOYAGER_8051_SELECT (0x000b0004 + VOYAGER_BASE)
+#define VOYAGER_8051_CPU_INT (0x000b000c + VOYAGER_BASE)
+
+/* ----- AC97 Controle register ----------------------------------------- */
+#define AC97_TX_SLOT0 (0x00000000 + VOYAGER_AC97_BASE)
+#define AC97_CONTROL_STATUS (0x00000080 + VOYAGER_AC97_BASE)
+#define AC97C_READ (1 << 19)
+#define AC97C_WD_BIT (1 << 2)
+#define AC97C_INDEX_MASK 0x7f
+/* -------------------------------------------------------------------- */
+
+#endif /* _VOYAGER_GX_REG_H */
--- /dev/null
+/*
+ * include/asm-sh/se7300/io.h
+ *
+ * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp>
+ * IO functions for SH-Mobile(SH7300) SolutionEngine
+ */
+
+#ifndef _ASM_SH_IO_7300SE_H
+#define _ASM_SH_IO_7300SE_H
+
+extern unsigned char sh7300se_inb(unsigned long port);
+extern unsigned short sh7300se_inw(unsigned long port);
+extern unsigned int sh7300se_inl(unsigned long port);
+
+extern void sh7300se_outb(unsigned char value, unsigned long port);
+extern void sh7300se_outw(unsigned short value, unsigned long port);
+extern void sh7300se_outl(unsigned int value, unsigned long port);
+
+extern unsigned char sh7300se_inb_p(unsigned long port);
+extern void sh7300se_outb_p(unsigned char value, unsigned long port);
+
+extern void sh7300se_insb(unsigned long port, void *addr, unsigned long count);
+extern void sh7300se_insw(unsigned long port, void *addr, unsigned long count);
+extern void sh7300se_insl(unsigned long port, void *addr, unsigned long count);
+extern void sh7300se_outsb(unsigned long port, const void *addr, unsigned long count);
+extern void sh7300se_outsw(unsigned long port, const void *addr, unsigned long count);
+extern void sh7300se_outsl(unsigned long port, const void *addr, unsigned long count);
+
+#endif /* _ASM_SH_IO_7300SE_H */
--- /dev/null
+#ifndef __ASM_SH_HITACHI_SE7300_H
+#define __ASM_SH_HITACHI_SE7300_H
+
+/*
+ * linux/include/asm-sh/se/se7300.h
+ *
+ * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp>
+ *
+ * SH-Mobile SolutionEngine 7300 support
+ */
+
+/* Box specific addresses. */
+
+/* Area 0 */
+#define PA_ROM 0x00000000 /* EPROM */
+#define PA_ROM_SIZE 0x00400000 /* EPROM size 4M byte(Actually 2MB) */
+#define PA_FROM 0x00400000 /* Flash ROM */
+#define PA_FROM_SIZE 0x00400000 /* Flash size 4M byte */
+#define PA_SRAM 0x00800000 /* SRAM */
+#define PA_FROM_SIZE 0x00400000 /* SRAM size 4M byte */
+/* Area 1 */
+#define PA_EXT1 0x04000000
+#define PA_EXT1_SIZE 0x04000000
+/* Area 2 */
+#define PA_EXT2 0x08000000
+#define PA_EXT2_SIZE 0x04000000
+/* Area 3 */
+#define PA_SDRAM 0x0c000000
+#define PA_SDRAM_SIZE 0x04000000
+/* Area 4 */
+#define PA_PCIC 0x10000000 /* MR-SHPC-01 PCMCIA */
+#define PA_MRSHPC 0xb03fffe0 /* MR-SHPC-01 PCMCIA controller */
+#define PA_MRSHPC_MW1 0xb0400000 /* MR-SHPC-01 memory window base */
+#define PA_MRSHPC_MW2 0xb0500000 /* MR-SHPC-01 attribute window base */
+#define PA_MRSHPC_IO 0xb0600000 /* MR-SHPC-01 I/O window base */
+#define MRSHPC_OPTION (PA_MRSHPC + 6)
+#define MRSHPC_CSR (PA_MRSHPC + 8)
+#define MRSHPC_ISR (PA_MRSHPC + 10)
+#define MRSHPC_ICR (PA_MRSHPC + 12)
+#define MRSHPC_CPWCR (PA_MRSHPC + 14)
+#define MRSHPC_MW0CR1 (PA_MRSHPC + 16)
+#define MRSHPC_MW1CR1 (PA_MRSHPC + 18)
+#define MRSHPC_IOWCR1 (PA_MRSHPC + 20)
+#define MRSHPC_MW0CR2 (PA_MRSHPC + 22)
+#define MRSHPC_MW1CR2 (PA_MRSHPC + 24)
+#define MRSHPC_IOWCR2 (PA_MRSHPC + 26)
+#define MRSHPC_CDCR (PA_MRSHPC + 28)
+#define MRSHPC_PCIC_INFO (PA_MRSHPC + 30)
+#define PA_LED 0xb0800000 /* LED */
+#define PA_DIPSW 0xb0900000 /* Dip switch 31 */
+#define PA_EPLD_MODESET 0xb0a00000 /* FPGA Mode set register */
+#define PA_EPLD_ST1 0xb0a80000 /* FPGA Interrupt status register1 */
+#define PA_EPLD_ST2 0xb0ac0000 /* FPGA Interrupt status register2 */
+/* Area 5 */
+#define PA_EXT5 0x14000000
+#define PA_EXT5_SIZE 0x04000000
+/* Area 6 */
+#define PA_LCD1 0xb8000000
+#define PA_LCD2 0xb8800000
+
+#endif /* __ASM_SH_HITACHI_SE7300_H */
--- /dev/null
+#ifdef __KERNEL__
+#ifndef _SH_SETUP_H
+#define _SH_SETUP_H
+
+#define COMMAND_LINE_SIZE 256
+
+#endif /* _SH_SETUP_H */
+#endif /* __KERNEL__ */
--- /dev/null
+#ifndef __ASM_SH64_BITOPS_H
+#define __ASM_SH64_BITOPS_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/bitops.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003 Paul Mundt
+ */
+
+#ifdef __KERNEL__
+#include <linux/compiler.h>
+#include <asm/system.h>
+/* For __swab32 */
+#include <asm/byteorder.h>
+
+static __inline__ void set_bit(int nr, volatile void * addr)
+{
+ int mask;
+ volatile unsigned int *a = addr;
+ unsigned long flags;
+
+ a += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ local_irq_save(flags);
+ *a |= mask;
+ local_irq_restore(flags);
+}
+
+static inline void __set_bit(int nr, void *addr)
+{
+ int mask;
+ unsigned int *a = addr;
+
+ a += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ *a |= mask;
+}
+
+/*
+ * clear_bit() doesn't provide any barrier for the compiler.
+ */
+#define smp_mb__before_clear_bit() barrier()
+#define smp_mb__after_clear_bit() barrier()
+static inline void clear_bit(int nr, volatile unsigned long *a)
+{
+ int mask;
+ unsigned long flags;
+
+ a += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ local_irq_save(flags);
+ *a &= ~mask;
+ local_irq_restore(flags);
+}
+
+static inline void __clear_bit(int nr, volatile unsigned long *a)
+{
+ int mask;
+
+ a += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ *a &= ~mask;
+}
+
+static __inline__ void change_bit(int nr, volatile void * addr)
+{
+ int mask;
+ volatile unsigned int *a = addr;
+ unsigned long flags;
+
+ a += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ local_irq_save(flags);
+ *a ^= mask;
+ local_irq_restore(flags);
+}
+
+static __inline__ void __change_bit(int nr, volatile void * addr)
+{
+ int mask;
+ volatile unsigned int *a = addr;
+
+ a += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ *a ^= mask;
+}
+
+static __inline__ int test_and_set_bit(int nr, volatile void * addr)
+{
+ int mask, retval;
+ volatile unsigned int *a = addr;
+ unsigned long flags;
+
+ a += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ local_irq_save(flags);
+ retval = (mask & *a) != 0;
+ *a |= mask;
+ local_irq_restore(flags);
+
+ return retval;
+}
+
+static __inline__ int __test_and_set_bit(int nr, volatile void * addr)
+{
+ int mask, retval;
+ volatile unsigned int *a = addr;
+
+ a += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ retval = (mask & *a) != 0;
+ *a |= mask;
+
+ return retval;
+}
+
+static __inline__ int test_and_clear_bit(int nr, volatile void * addr)
+{
+ int mask, retval;
+ volatile unsigned int *a = addr;
+ unsigned long flags;
+
+ a += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ local_irq_save(flags);
+ retval = (mask & *a) != 0;
+ *a &= ~mask;
+ local_irq_restore(flags);
+
+ return retval;
+}
+
+static __inline__ int __test_and_clear_bit(int nr, volatile void * addr)
+{
+ int mask, retval;
+ volatile unsigned int *a = addr;
+
+ a += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ retval = (mask & *a) != 0;
+ *a &= ~mask;
+
+ return retval;
+}
+
+static __inline__ int test_and_change_bit(int nr, volatile void * addr)
+{
+ int mask, retval;
+ volatile unsigned int *a = addr;
+ unsigned long flags;
+
+ a += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ local_irq_save(flags);
+ retval = (mask & *a) != 0;
+ *a ^= mask;
+ local_irq_restore(flags);
+
+ return retval;
+}
+
+static __inline__ int __test_and_change_bit(int nr, volatile void * addr)
+{
+ int mask, retval;
+ volatile unsigned int *a = addr;
+
+ a += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ retval = (mask & *a) != 0;
+ *a ^= mask;
+
+ return retval;
+}
+
+static __inline__ int test_bit(int nr, const volatile void *addr)
+{
+ return 1UL & (((const volatile unsigned int *) addr)[nr >> 5] >> (nr & 31));
+}
+
+static __inline__ unsigned long ffz(unsigned long word)
+{
+ unsigned long result, __d2, __d3;
+
+ __asm__("gettr tr0, %2\n\t"
+ "pta $+32, tr0\n\t"
+ "andi %1, 1, %3\n\t"
+ "beq %3, r63, tr0\n\t"
+ "pta $+4, tr0\n"
+ "0:\n\t"
+ "shlri.l %1, 1, %1\n\t"
+ "addi %0, 1, %0\n\t"
+ "andi %1, 1, %3\n\t"
+ "beqi %3, 1, tr0\n"
+ "1:\n\t"
+ "ptabs %2, tr0\n\t"
+ : "=r" (result), "=r" (word), "=r" (__d2), "=r" (__d3)
+ : "0" (0L), "1" (word));
+
+ return result;
+}
+
+/**
+ * __ffs - find first bit in word
+ * @word: The word to search
+ *
+ * Undefined if no bit exists, so code should check against 0 first.
+ */
+static inline unsigned long __ffs(unsigned long word)
+{
+ int r = 0;
+
+ if (!word)
+ return 0;
+ if (!(word & 0xffff)) {
+ word >>= 16;
+ r += 16;
+ }
+ if (!(word & 0xff)) {
+ word >>= 8;
+ r += 8;
+ }
+ if (!(word & 0xf)) {
+ word >>= 4;
+ r += 4;
+ }
+ if (!(word & 3)) {
+ word >>= 2;
+ r += 2;
+ }
+ if (!(word & 1)) {
+ word >>= 1;
+ r += 1;
+ }
+ return r;
+}
+
+/**
+ * find_next_bit - find the next set bit in a memory region
+ * @addr: The address to base the search on
+ * @offset: The bitnumber to start searching at
+ * @size: The maximum size to search
+ */
+static inline unsigned long find_next_bit(const unsigned long *addr,
+ unsigned long size, unsigned long offset)
+{
+ unsigned int *p = ((unsigned int *) addr) + (offset >> 5);
+ unsigned int result = offset & ~31UL;
+ unsigned int tmp;
+
+ if (offset >= size)
+ return size;
+ size -= result;
+ offset &= 31UL;
+ if (offset) {
+ tmp = *p++;
+ tmp &= ~0UL << offset;
+ if (size < 32)
+ goto found_first;
+ if (tmp)
+ goto found_middle;
+ size -= 32;
+ result += 32;
+ }
+ while (size >= 32) {
+ if ((tmp = *p++) != 0)
+ goto found_middle;
+ result += 32;
+ size -= 32;
+ }
+ if (!size)
+ return result;
+ tmp = *p;
+
+found_first:
+ tmp &= ~0UL >> (32 - size);
+ if (tmp == 0UL) /* Are any bits set? */
+ return result + size; /* Nope. */
+found_middle:
+ return result + __ffs(tmp);
+}
+
+/**
+ * find_first_bit - find the first set bit in a memory region
+ * @addr: The address to start the search at
+ * @size: The maximum size to search
+ *
+ * Returns the bit-number of the first set bit, not the number of the byte
+ * containing a bit.
+ */
+#define find_first_bit(addr, size) \
+ find_next_bit((addr), (size), 0)
+
+
+static inline int find_next_zero_bit(void *addr, int size, int offset)
+{
+ unsigned long *p = ((unsigned long *) addr) + (offset >> 5);
+ unsigned long result = offset & ~31UL;
+ unsigned long tmp;
+
+ if (offset >= size)
+ return size;
+ size -= result;
+ offset &= 31UL;
+ if (offset) {
+ tmp = *(p++);
+ tmp |= ~0UL >> (32-offset);
+ if (size < 32)
+ goto found_first;
+ if (~tmp)
+ goto found_middle;
+ size -= 32;
+ result += 32;
+ }
+ while (size & ~31UL) {
+ if (~(tmp = *(p++)))
+ goto found_middle;
+ result += 32;
+ size -= 32;
+ }
+ if (!size)
+ return result;
+ tmp = *p;
+
+found_first:
+ tmp |= ~0UL << size;
+found_middle:
+ return result + ffz(tmp);
+}
+
+#define find_first_zero_bit(addr, size) \
+ find_next_zero_bit((addr), (size), 0)
+
+/*
+ * hweightN: returns the hamming weight (i.e. the number
+ * of bits set) of a N-bit word
+ */
+
+#define hweight32(x) generic_hweight32(x)
+#define hweight16(x) generic_hweight16(x)
+#define hweight8(x) generic_hweight8(x)
+
+/*
+ * Every architecture must define this function. It's the fastest
+ * way of searching a 140-bit bitmap where the first 100 bits are
+ * unlikely to be set. It's guaranteed that at least one of the 140
+ * bits is cleared.
+ */
+
+static inline int sched_find_first_bit(unsigned long *b)
+{
+ if (unlikely(b[0]))
+ return __ffs(b[0]);
+ if (unlikely(b[1]))
+ return __ffs(b[1]) + 32;
+ if (unlikely(b[2]))
+ return __ffs(b[2]) + 64;
+ if (b[3])
+ return __ffs(b[3]) + 96;
+ return __ffs(b[4]) + 128;
+}
+
+/*
+ * ffs: find first bit set. This is defined the same way as
+ * the libc and compiler builtin ffs routines, therefore
+ * differs in spirit from the above ffz (man ffs).
+ */
+
+#define ffs(x) generic_ffs(x)
+
+/*
+ * hweightN: returns the hamming weight (i.e. the number
+ * of bits set) of a N-bit word
+ */
+
+#define hweight32(x) generic_hweight32(x)
+#define hweight16(x) generic_hweight16(x)
+#define hweight8(x) generic_hweight8(x)
+
+#ifdef __LITTLE_ENDIAN__
+#define ext2_set_bit(nr, addr) test_and_set_bit((nr), (addr))
+#define ext2_clear_bit(nr, addr) test_and_clear_bit((nr), (addr))
+#define ext2_test_bit(nr, addr) test_bit((nr), (addr))
+#define ext2_find_first_zero_bit(addr, size) find_first_zero_bit((addr), (size))
+#define ext2_find_next_zero_bit(addr, size, offset) \
+ find_next_zero_bit((addr), (size), (offset))
+#else
+static __inline__ int ext2_set_bit(int nr, volatile void * addr)
+{
+ int mask, retval;
+ unsigned long flags;
+ volatile unsigned char *ADDR = (unsigned char *) addr;
+
+ ADDR += nr >> 3;
+ mask = 1 << (nr & 0x07);
+ local_irq_save(flags);
+ retval = (mask & *ADDR) != 0;
+ *ADDR |= mask;
+ local_irq_restore(flags);
+ return retval;
+}
+
+static __inline__ int ext2_clear_bit(int nr, volatile void * addr)
+{
+ int mask, retval;
+ unsigned long flags;
+ volatile unsigned char *ADDR = (unsigned char *) addr;
+
+ ADDR += nr >> 3;
+ mask = 1 << (nr & 0x07);
+ local_irq_save(flags);
+ retval = (mask & *ADDR) != 0;
+ *ADDR &= ~mask;
+ local_irq_restore(flags);
+ return retval;
+}
+
+static __inline__ int ext2_test_bit(int nr, const volatile void * addr)
+{
+ int mask;
+ const volatile unsigned char *ADDR = (const unsigned char *) addr;
+
+ ADDR += nr >> 3;
+ mask = 1 << (nr & 0x07);
+ return ((mask & *ADDR) != 0);
+}
+
+#define ext2_find_first_zero_bit(addr, size) \
+ ext2_find_next_zero_bit((addr), (size), 0)
+
+static __inline__ unsigned long ext2_find_next_zero_bit(void *addr, unsigned long size, unsigned long offset)
+{
+ unsigned long *p = ((unsigned long *) addr) + (offset >> 5);
+ unsigned long result = offset & ~31UL;
+ unsigned long tmp;
+
+ if (offset >= size)
+ return size;
+ size -= result;
+ offset &= 31UL;
+ if(offset) {
+ /* We hold the little endian value in tmp, but then the
+ * shift is illegal. So we could keep a big endian value
+ * in tmp, like this:
+ *
+ * tmp = __swab32(*(p++));
+ * tmp |= ~0UL >> (32-offset);
+ *
+ * but this would decrease preformance, so we change the
+ * shift:
+ */
+ tmp = *(p++);
+ tmp |= __swab32(~0UL >> (32-offset));
+ if(size < 32)
+ goto found_first;
+ if(~tmp)
+ goto found_middle;
+ size -= 32;
+ result += 32;
+ }
+ while(size & ~31UL) {
+ if(~(tmp = *(p++)))
+ goto found_middle;
+ result += 32;
+ size -= 32;
+ }
+ if(!size)
+ return result;
+ tmp = *p;
+
+found_first:
+ /* tmp is little endian, so we would have to swab the shift,
+ * see above. But then we have to swab tmp below for ffz, so
+ * we might as well do this here.
+ */
+ return result + ffz(__swab32(tmp) | (~0UL << size));
+found_middle:
+ return result + ffz(__swab32(tmp));
+}
+#endif
+
+#define ext2_set_bit_atomic(lock, nr, addr) \
+ ({ \
+ int ret; \
+ spin_lock(lock); \
+ ret = ext2_set_bit((nr), (addr)); \
+ spin_unlock(lock); \
+ ret; \
+ })
+
+#define ext2_clear_bit_atomic(lock, nr, addr) \
+ ({ \
+ int ret; \
+ spin_lock(lock); \
+ ret = ext2_clear_bit((nr), (addr)); \
+ spin_unlock(lock); \
+ ret; \
+ })
+
+/* Bitmap functions for the minix filesystem. */
+#define minix_test_and_set_bit(nr,addr) test_and_set_bit(nr,addr)
+#define minix_set_bit(nr,addr) set_bit(nr,addr)
+#define minix_test_and_clear_bit(nr,addr) test_and_clear_bit(nr,addr)
+#define minix_test_bit(nr,addr) test_bit(nr,addr)
+#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size)
+
+#define ffs(x) generic_ffs(x)
+#define fls(x) generic_fls(x)
+
+#endif /* __KERNEL__ */
+
+#endif /* __ASM_SH64_BITOPS_H */
--- /dev/null
+#ifndef __ASM_SH64_BUG_H
+#define __ASM_SH64_BUG_H
+
+#include <asm-sh/bug.h>
+
+#endif /* __ASM_SH64_BUG_H */
+
--- /dev/null
+#ifndef __ASM_SH64_BYTEORDER_H
+#define __ASM_SH64_BYTEORDER_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/byteorder.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ *
+ */
+
+#include <asm/types.h>
+
+static __inline__ __const__ __u32 ___arch__swab32(__u32 x)
+{
+ __asm__("byterev %0, %0\n\t"
+ "shari %0, 32, %0"
+ : "=r" (x)
+ : "0" (x));
+ return x;
+}
+
+static __inline__ __const__ __u16 ___arch__swab16(__u16 x)
+{
+ __asm__("byterev %0, %0\n\t"
+ "shari %0, 48, %0"
+ : "=r" (x)
+ : "0" (x));
+ return x;
+}
+
+#define __arch__swab32(x) ___arch__swab32(x)
+#define __arch__swab16(x) ___arch__swab16(x)
+
+#if !defined(__STRICT_ANSI__) || defined(__KERNEL__)
+# define __BYTEORDER_HAS_U64__
+# define __SWAB_64_THRU_32__
+#endif
+
+#ifdef __LITTLE_ENDIAN__
+#include <linux/byteorder/little_endian.h>
+#else
+#include <linux/byteorder/big_endian.h>
+#endif
+
+#endif /* __ASM_SH64_BYTEORDER_H */
--- /dev/null
+#ifndef __ASM_SH64_CACHEFLUSH_H
+#define __ASM_SH64_CACHEFLUSH_H
+
+#ifndef __ASSEMBLY__
+
+#include <asm/page.h>
+
+struct vm_area_struct;
+struct page;
+struct mm_struct;
+
+extern void flush_cache_all(void);
+extern void flush_cache_mm(struct mm_struct *mm);
+extern void flush_cache_sigtramp(unsigned long start, unsigned long end);
+extern void flush_cache_range(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end);
+extern void flush_cache_page(struct vm_area_struct *vma, unsigned long addr);
+extern void flush_dcache_page(struct page *pg);
+extern void flush_icache_range(unsigned long start, unsigned long end);
+extern void flush_icache_user_range(struct vm_area_struct *vma,
+ struct page *page, unsigned long addr,
+ int len);
+
+#define flush_dcache_mmap_lock(mapping) do { } while (0)
+#define flush_dcache_mmap_unlock(mapping) do { } while (0)
+
+#define flush_cache_vmap(start, end) flush_cache_all()
+#define flush_cache_vunmap(start, end) flush_cache_all()
+
+#define flush_icache_page(vma, page) do { } while (0)
+
+#define copy_to_user_page(vma, page, vaddr, dst, src, len) \
+do { memcpy(dst, src, len); \
+ flush_icache_user_range(vma, page, vaddr, len); \
+} while (0)
+
+#define copy_from_user_page(vma, page, vaddr, dst, src, len) \
+ memcpy(dst, src, len)
+
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ASM_SH64_CACHEFLUSH_H */
+
--- /dev/null
+#ifndef __ASM_SH64_CHECKSUM_H
+#define __ASM_SH64_CHECKSUM_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/checksum.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ *
+ */
+
+#include <asm/registers.h>
+
+/*
+ * computes the checksum of a memory block at buff, length len,
+ * and adds in "sum" (32-bit)
+ *
+ * returns a 32-bit number suitable for feeding into itself
+ * or csum_tcpudp_magic
+ *
+ * this function must be called with even lengths, except
+ * for the last fragment, which may be odd
+ *
+ * it's best to have buff aligned on a 32-bit boundary
+ */
+asmlinkage unsigned int csum_partial(const unsigned char *buff, int len,
+ unsigned int sum);
+
+/*
+ * Note: when you get a NULL pointer exception here this means someone
+ * passed in an incorrect kernel address to one of these functions.
+ *
+ * If you use these functions directly please don't forget the
+ * verify_area().
+ */
+
+
+unsigned int csum_partial_copy_nocheck(const char *src, char *dst, int len,
+ unsigned int sum);
+
+unsigned int csum_partial_copy_from_user(const char *src, char *dst,
+ int len, int sum, int *err_ptr);
+
+/*
+ * These are the old (and unsafe) way of doing checksums, a warning message will be
+ * printed if they are used and an exeption occurs.
+ *
+ * these functions should go away after some time.
+ */
+
+#define csum_partial_copy_fromuser csum_partial_copy
+
+unsigned int csum_partial_copy(const char *src, char *dst, int len,
+ unsigned int sum);
+
+static inline unsigned short csum_fold(unsigned int sum)
+{
+ sum = (sum & 0xffff) + (sum >> 16);
+ sum = (sum & 0xffff) + (sum >> 16);
+ return ~(sum);
+}
+
+unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl);
+
+unsigned long csum_tcpudp_nofold(unsigned long saddr, unsigned long daddr,
+ unsigned short len, unsigned short proto,
+ unsigned int sum);
+
+/*
+ * computes the checksum of the TCP/UDP pseudo-header
+ * returns a 16-bit checksum, already complemented
+ */
+static inline unsigned short int csum_tcpudp_magic(unsigned long saddr,
+ unsigned long daddr,
+ unsigned short len,
+ unsigned short proto,
+ unsigned int sum)
+{
+ return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
+}
+
+/*
+ * this routine is used for miscellaneous IP-like checksums, mainly
+ * in icmp.c
+ */
+static inline unsigned short ip_compute_csum(unsigned char * buff, int len)
+{
+ return csum_fold(csum_partial(buff, len, 0));
+}
+
+#endif /* __ASM_SH64_CHECKSUM_H */
+
--- /dev/null
+#ifndef __ASM_SH_DMA_MAPPING_H
+#define __ASM_SH_DMA_MAPPING_H
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <asm/scatterlist.h>
+#include <asm/io.h>
+
+struct pci_dev;
+extern void *consistent_alloc(struct pci_dev *hwdev, size_t size,
+ dma_addr_t *dma_handle);
+extern void consistent_free(struct pci_dev *hwdev, size_t size,
+ void *vaddr, dma_addr_t dma_handle);
+
+#define dma_supported(dev, mask) (1)
+
+static inline int dma_set_mask(struct device *dev, u64 mask)
+{
+ if (!dev->dma_mask || !dma_supported(dev, mask))
+ return -EIO;
+
+ *dev->dma_mask = mask;
+
+ return 0;
+}
+
+static inline void *dma_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, int flag)
+{
+ return consistent_alloc(NULL, size, dma_handle);
+}
+
+static inline void dma_free_coherent(struct device *dev, size_t size,
+ void *vaddr, dma_addr_t dma_handle)
+{
+ consistent_free(NULL, size, vaddr, dma_handle);
+}
+
+static inline void dma_cache_sync(void *vaddr, size_t size,
+ enum dma_data_direction dir)
+{
+ dma_cache_wback_inv((unsigned long)vaddr, size);
+}
+
+static inline dma_addr_t dma_map_single(struct device *dev,
+ void *ptr, size_t size,
+ enum dma_data_direction dir)
+{
+#if defined(CONFIG_PCI) && !defined(CONFIG_SH_PCIDMA_NONCOHERENT)
+ if (dev->bus == &pci_bus_type)
+ return virt_to_bus(ptr);
+#endif
+ dma_cache_sync(ptr, size, dir);
+
+ return virt_to_bus(ptr);
+}
+
+#define dma_unmap_single(dev, addr, size, dir) do { } while (0)
+
+static inline int dma_map_sg(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction dir)
+{
+ int i;
+
+ for (i = 0; i < nents; i++) {
+#if !defined(CONFIG_PCI) || defined(CONFIG_SH_PCIDMA_NONCOHERENT)
+ dma_cache_sync(page_address(sg[i].page) + sg[i].offset,
+ sg[i].length, dir);
+#endif
+ sg[i].dma_address = page_to_phys(sg[i].page) + sg[i].offset;
+ }
+
+ return nents;
+}
+
+#define dma_unmap_sg(dev, sg, nents, dir) do { } while (0)
+
+static inline dma_addr_t dma_map_page(struct device *dev, struct page *page,
+ unsigned long offset, size_t size,
+ enum dma_data_direction dir)
+{
+ return dma_map_single(dev, page_address(page) + offset, size, dir);
+}
+
+static inline void dma_unmap_page(struct device *dev, dma_addr_t dma_address,
+ size_t size, enum dma_data_direction dir)
+{
+ dma_unmap_single(dev, dma_address, size, dir);
+}
+
+static inline void dma_sync_single(struct device *dev, dma_addr_t dma_handle,
+ size_t size, enum dma_data_direction dir)
+{
+#if defined(CONFIG_PCI) && !defined(CONFIG_SH_PCIDMA_NONCOHERENT)
+ if (dev->bus == &pci_bus_type)
+ return;
+#endif
+ dma_cache_sync(bus_to_virt(dma_handle), size, dir);
+}
+
+static inline void dma_sync_single_range(struct device *dev,
+ dma_addr_t dma_handle,
+ unsigned long offset, size_t size,
+ enum dma_data_direction dir)
+{
+#if defined(CONFIG_PCI) && !defined(CONFIG_SH_PCIDMA_NONCOHERENT)
+ if (dev->bus == &pci_bus_type)
+ return;
+#endif
+ dma_cache_sync(bus_to_virt(dma_handle) + offset, size, dir);
+}
+
+static inline void dma_sync_sg(struct device *dev, struct scatterlist *sg,
+ int nelems, enum dma_data_direction dir)
+{
+ int i;
+
+ for (i = 0; i < nelems; i++) {
+#if !defined(CONFIG_PCI) || defined(CONFIG_SH_PCIDMA_NONCOHERENT)
+ dma_cache_sync(page_address(sg[i].page) + sg[i].offset,
+ sg[i].length, dir);
+#endif
+ sg[i].dma_address = page_to_phys(sg[i].page) + sg[i].offset;
+ }
+}
+
+static inline void dma_sync_single_for_cpu(struct device *dev,
+ dma_addr_t dma_handle, size_t size,
+ enum dma_data_direction dir)
+ __attribute__ ((alias("dma_sync_single")));
+
+static inline void dma_sync_single_for_device(struct device *dev,
+ dma_addr_t dma_handle, size_t size,
+ enum dma_data_direction dir)
+ __attribute__ ((alias("dma_sync_single")));
+
+static inline void dma_sync_sg_for_cpu(struct device *dev,
+ struct scatterlist *sg, int nelems,
+ enum dma_data_direction dir)
+ __attribute__ ((alias("dma_sync_sg")));
+
+static inline void dma_sync_sg_for_device(struct device *dev,
+ struct scatterlist *sg, int nelems,
+ enum dma_data_direction dir)
+ __attribute__ ((alias("dma_sync_sg")));
+
+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(dma_addr_t dma_addr)
+{
+ return dma_addr == 0;
+}
+
+#endif /* __ASM_SH_DMA_MAPPING_H */
+
--- /dev/null
+#ifndef __ASM_SH64_HARDIRQ_H
+#define __ASM_SH64_HARDIRQ_H
+
+#include <asm-sh/hardirq.h>
+
+#endif /* __ASM_SH64_HARDIRQ_H */
+
--- /dev/null
+#ifndef __ASM_SH64_HDREG_H
+#define __ASM_SH64_HDREG_H
+
+#include <asm-generic/hdreg.h>
+
+#endif /* __ASM_SH64_HDREG_H */
--- /dev/null
+#ifndef __ASM_SH64_HW_IRQ_H
+#define __ASM_SH64_HW_IRQ_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/hw_irq.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ *
+ */
+static __inline__ void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) { /* Nothing to do */ }
+
+#endif /* __ASM_SH64_HW_IRQ_H */
--- /dev/null
+/*
+ * linux/include/asm-sh64/ide.h
+ *
+ * Copyright (C) 1994-1996 Linus Torvalds & authors
+ *
+ * sh64 version by Richard Curnow & Paul Mundt
+ */
+
+/*
+ * This file contains the sh64 architecture specific IDE code.
+ */
+
+#ifndef __ASM_SH64_IDE_H
+#define __ASM_SH64_IDE_H
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+
+#ifndef MAX_HWIFS
+#define MAX_HWIFS CONFIG_IDE_MAX_HWIFS
+#endif
+
+#define ide_default_io_ctl(base) (0)
+
+#include <asm-generic/ide_iops.h>
+
+#endif /* __KERNEL__ */
+
+#endif /* __ASM_SH64_IDE_H */
--- /dev/null
+#ifndef __ASM_SH64_IO_H
+#define __ASM_SH64_IO_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/io.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003 Paul Mundt
+ *
+ */
+
+/*
+ * Convention:
+ * read{b,w,l}/write{b,w,l} are for PCI,
+ * while in{b,w,l}/out{b,w,l} are for ISA
+ * These may (will) be platform specific function.
+ *
+ * In addition, we have
+ * ctrl_in{b,w,l}/ctrl_out{b,w,l} for SuperH specific I/O.
+ * which are processor specific. Address should be the result of
+ * onchip_remap();
+ */
+
+#include <asm/cache.h>
+#include <asm/system.h>
+#include <asm/page.h>
+
+#define virt_to_bus virt_to_phys
+#define bus_to_virt phys_to_virt
+#define page_to_bus page_to_phys
+
+/*
+ * Nothing overly special here.. instead of doing the same thing
+ * over and over again, we just define a set of sh64_in/out functions
+ * with an implicit size. The traditional read{b,w,l}/write{b,w,l}
+ * mess is wrapped to this, as are the SH-specific ctrl_in/out routines.
+ */
+static inline unsigned char sh64_in8(unsigned long addr)
+{
+ return *(volatile unsigned char *)addr;
+}
+
+static inline unsigned short sh64_in16(unsigned long addr)
+{
+ return *(volatile unsigned short *)addr;
+}
+
+static inline unsigned long sh64_in32(unsigned long addr)
+{
+ return *(volatile unsigned long *)addr;
+}
+
+static inline unsigned long long sh64_in64(unsigned long addr)
+{
+ return *(volatile unsigned long long *)addr;
+}
+
+static inline void sh64_out8(unsigned char b, unsigned long addr)
+{
+ *(volatile unsigned char *)addr = b;
+ wmb();
+}
+
+static inline void sh64_out16(unsigned short b, unsigned long addr)
+{
+ *(volatile unsigned short *)addr = b;
+ wmb();
+}
+
+static inline void sh64_out32(unsigned long b, unsigned long addr)
+{
+ *(volatile unsigned long *)addr = b;
+ wmb();
+}
+
+static inline void sh64_out64(unsigned long long b, unsigned long addr)
+{
+ *(volatile unsigned long long *)addr = b;
+ wmb();
+}
+
+#define readb(addr) sh64_in8(addr)
+#define readw(addr) sh64_in16(addr)
+#define readl(addr) sh64_in32(addr)
+
+#define writeb(b, addr) sh64_out8(b, addr)
+#define writew(b, addr) sh64_out16(b, addr)
+#define writel(b, addr) sh64_out32(b, addr)
+
+#define ctrl_inb(addr) sh64_in8(addr)
+#define ctrl_inw(addr) sh64_in16(addr)
+#define ctrl_inl(addr) sh64_in32(addr)
+
+#define ctrl_outb(b, addr) sh64_out8(b, addr)
+#define ctrl_outw(b, addr) sh64_out16(b, addr)
+#define ctrl_outl(b, addr) sh64_out32(b, addr)
+
+unsigned long inb(unsigned long port);
+unsigned long inw(unsigned long port);
+unsigned long inl(unsigned long port);
+void outb(unsigned long value, unsigned long port);
+void outw(unsigned long value, unsigned long port);
+void outl(unsigned long value, unsigned long port);
+
+#ifdef __KERNEL__
+
+#ifdef CONFIG_SH_CAYMAN
+extern unsigned long smsc_superio_virt;
+#endif
+#ifdef CONFIG_PCI
+extern unsigned long pciio_virt;
+#endif
+
+#define IO_SPACE_LIMIT 0xffffffff
+
+/*
+ * Change virtual addresses to physical addresses and vv.
+ * These are trivial on the 1:1 Linux/SuperH mapping
+ */
+extern __inline__ unsigned long virt_to_phys(volatile void * address)
+{
+ return __pa(address);
+}
+
+extern __inline__ void * phys_to_virt(unsigned long address)
+{
+ return __va(address);
+}
+
+extern void * __ioremap(unsigned long phys_addr, unsigned long size,
+ unsigned long flags);
+
+extern __inline__ void * ioremap(unsigned long phys_addr, unsigned long size)
+{
+ return __ioremap(phys_addr, size, 1);
+}
+
+extern __inline__ void * ioremap_nocache (unsigned long phys_addr, unsigned long size)
+{
+ return __ioremap(phys_addr, size, 0);
+}
+
+extern void iounmap(void *addr);
+
+unsigned long onchip_remap(unsigned long addr, unsigned long size, const char* name);
+extern void onchip_unmap(unsigned long vaddr);
+
+static __inline__ int check_signature(unsigned long io_addr,
+ const unsigned char *signature, int length)
+{
+ int retval = 0;
+ do {
+ if (readb(io_addr) != *signature)
+ goto out;
+ io_addr++;
+ signature++;
+ length--;
+ } while (length);
+ retval = 1;
+out:
+ return retval;
+}
+
+/*
+ * The caches on some architectures aren't dma-coherent and have need to
+ * handle this in software. There are three types of operations that
+ * can be applied to dma buffers.
+ *
+ * - dma_cache_wback_inv(start, size) makes caches and RAM coherent by
+ * writing the content of the caches back to memory, if necessary.
+ * The function also invalidates the affected part of the caches as
+ * necessary before DMA transfers from outside to memory.
+ * - dma_cache_inv(start, size) invalidates the affected parts of the
+ * caches. Dirty lines of the caches may be written back or simply
+ * be discarded. This operation is necessary before dma operations
+ * to the memory.
+ * - dma_cache_wback(start, size) writes back any dirty lines but does
+ * not invalidate the cache. This can be used before DMA reads from
+ * memory,
+ */
+
+static __inline__ void dma_cache_wback_inv (unsigned long start, unsigned long size)
+{
+ unsigned long s = start & L1_CACHE_ALIGN_MASK;
+ unsigned long e = (start + size) & L1_CACHE_ALIGN_MASK;
+
+ for (; s <= e; s += L1_CACHE_BYTES)
+ asm volatile ("ocbp %0, 0" : : "r" (s));
+}
+
+static __inline__ void dma_cache_inv (unsigned long start, unsigned long size)
+{
+ // Note that caller has to be careful with overzealous
+ // invalidation should there be partial cache lines at the extremities
+ // of the specified range
+ unsigned long s = start & L1_CACHE_ALIGN_MASK;
+ unsigned long e = (start + size) & L1_CACHE_ALIGN_MASK;
+
+ for (; s <= e; s += L1_CACHE_BYTES)
+ asm volatile ("ocbi %0, 0" : : "r" (s));
+}
+
+static __inline__ void dma_cache_wback (unsigned long start, unsigned long size)
+{
+ unsigned long s = start & L1_CACHE_ALIGN_MASK;
+ unsigned long e = (start + size) & L1_CACHE_ALIGN_MASK;
+
+ for (; s <= e; s += L1_CACHE_BYTES)
+ asm volatile ("ocbwb %0, 0" : : "r" (s));
+}
+
+#endif /* __KERNEL__ */
+#endif /* __ASM_SH64_IO_H */
--- /dev/null
+#ifndef __ASM_SH64_IRQ_H
+#define __ASM_SH64_IRQ_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/irq.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ *
+ */
+
+#include <linux/config.h>
+
+/*
+ * Encoded IRQs are not considered worth to be supported.
+ * Main reason is that there's no per-encoded-interrupt
+ * enable/disable mechanism (as there was in SH3/4).
+ * An all enabled/all disabled is worth only if there's
+ * a cascaded IC to disable/enable/ack on. Until such
+ * IC is available there's no such support.
+ *
+ * Presumably Encoded IRQs may use extra IRQs beyond 64,
+ * below. Some logic must be added to cope with IRQ_IRL?
+ * in an exclusive way.
+ *
+ * Priorities are set at Platform level, when IRQ_IRL0-3
+ * are set to 0 Encoding is allowed. Otherwise it's not
+ * allowed.
+ */
+
+/* Independent IRQs */
+#define IRQ_IRL0 0
+#define IRQ_IRL1 1
+#define IRQ_IRL2 2
+#define IRQ_IRL3 3
+
+#define IRQ_INTA 4
+#define IRQ_INTB 5
+#define IRQ_INTC 6
+#define IRQ_INTD 7
+
+#define IRQ_SERR 12
+#define IRQ_ERR 13
+#define IRQ_PWR3 14
+#define IRQ_PWR2 15
+#define IRQ_PWR1 16
+#define IRQ_PWR0 17
+
+#define IRQ_DMTE0 18
+#define IRQ_DMTE1 19
+#define IRQ_DMTE2 20
+#define IRQ_DMTE3 21
+#define IRQ_DAERR 22
+
+#define IRQ_TUNI0 32
+#define IRQ_TUNI1 33
+#define IRQ_TUNI2 34
+#define IRQ_TICPI2 35
+
+#define IRQ_ATI 36
+#define IRQ_PRI 37
+#define IRQ_CUI 38
+
+#define IRQ_ERI 39
+#define IRQ_RXI 40
+#define IRQ_BRI 41
+#define IRQ_TXI 42
+
+#define IRQ_ITI 63
+
+#define NR_INTC_IRQS 64
+
+#ifdef CONFIG_SH_CAYMAN
+#define NR_EXT_IRQS 32
+#define START_EXT_IRQS 64
+
+/* PCI bus 2 uses encoded external interrupts on the Cayman board */
+#define IRQ_P2INTA (START_EXT_IRQS + (3*8) + 0)
+#define IRQ_P2INTB (START_EXT_IRQS + (3*8) + 1)
+#define IRQ_P2INTC (START_EXT_IRQS + (3*8) + 2)
+#define IRQ_P2INTD (START_EXT_IRQS + (3*8) + 3)
+
+#define START_EXT_IRQS 64
+
+#define I8042_KBD_IRQ (START_EXT_IRQS + 2)
+#define I8042_AUX_IRQ (START_EXT_IRQS + 6)
+
+#else
+#define NR_EXT_IRQS 0
+#endif
+
+#define NR_IRQS (NR_INTC_IRQS+NR_EXT_IRQS)
+
+
+/* Default IRQs, fixed */
+#define TIMER_IRQ IRQ_TUNI0
+#define RTC_IRQ IRQ_CUI
+
+/* Default Priorities, Platform may choose differently */
+#define NO_PRIORITY 0 /* Disabled */
+#define TIMER_PRIORITY 2
+#define RTC_PRIORITY TIMER_PRIORITY
+#define SCIF_PRIORITY 3
+#define INTD_PRIORITY 3
+#define IRL3_PRIORITY 4
+#define INTC_PRIORITY 6
+#define IRL2_PRIORITY 7
+#define INTB_PRIORITY 9
+#define IRL1_PRIORITY 10
+#define INTA_PRIORITY 12
+#define IRL0_PRIORITY 13
+#define TOP_PRIORITY 15
+
+extern void disable_irq(unsigned int);
+extern void disable_irq_nosync(unsigned int);
+extern void enable_irq(unsigned int);
+
+extern int intc_evt_to_irq[(0xE20/0x20)+1];
+int intc_irq_describe(char* p, int irq);
+
+#define irq_canonicalize(irq) (irq)
+
+#ifdef CONFIG_SH_CAYMAN
+int cayman_irq_demux(int evt);
+int cayman_irq_describe(char* p, int irq);
+#define irq_demux(x) cayman_irq_demux(x)
+#define irq_describe(p, x) cayman_irq_describe(p, x)
+#else
+#define irq_demux(x) (intc_evt_to_irq[x])
+#define irq_describe(p, x) intc_irq_describe(p, x)
+#endif
+
+/*
+ * Function for "on chip support modules".
+ */
+
+/*
+ * SH-5 supports Priority based interrupts only.
+ * Interrupt priorities are defined at platform level.
+ */
+#define set_ipr_data(a, b, c, d)
+#define make_ipr_irq(a)
+#define make_imask_irq(a)
+
+#endif /* __ASM_SH64_IRQ_H */
--- /dev/null
+/*
+ * linux/include/asm-shmedia/keyboard.h
+ *
+ * Copied from i386 version:
+ * Created 3 Nov 1996 by Geert Uytterhoeven
+ */
+
+/*
+ * This file contains the i386 architecture specific keyboard definitions
+ */
+
+#ifndef __ASM_SH64_KEYBOARD_H
+#define __ASM_SH64_KEYBOARD_H
+
+#ifdef __KERNEL__
+
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_SH_CAYMAN
+#define KEYBOARD_IRQ (START_EXT_IRQS + 2) /* SMSC SuperIO IRQ 1 */
+#endif
+#define DISABLE_KBD_DURING_INTERRUPTS 0
+
+extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode);
+extern int pckbd_getkeycode(unsigned int scancode);
+extern int pckbd_translate(unsigned char scancode, unsigned char *keycode,
+ char raw_mode);
+extern char pckbd_unexpected_up(unsigned char keycode);
+extern void pckbd_leds(unsigned char leds);
+extern void pckbd_init_hw(void);
+extern unsigned char pckbd_sysrq_xlate[128];
+
+#define kbd_setkeycode pckbd_setkeycode
+#define kbd_getkeycode pckbd_getkeycode
+#define kbd_translate pckbd_translate
+#define kbd_unexpected_up pckbd_unexpected_up
+#define kbd_leds pckbd_leds
+#define kbd_init_hw pckbd_init_hw
+#define kbd_sysrq_xlate pckbd_sysrq_xlate
+
+#define SYSRQ_KEY 0x54
+
+/* resource allocation */
+#define kbd_request_region()
+#define kbd_request_irq(handler) request_irq(KEYBOARD_IRQ, handler, 0, \
+ "keyboard", NULL)
+
+/* How to access the keyboard macros on this platform. */
+#define kbd_read_input() inb(KBD_DATA_REG)
+#define kbd_read_status() inb(KBD_STATUS_REG)
+#define kbd_write_output(val) outb(val, KBD_DATA_REG)
+#define kbd_write_command(val) outb(val, KBD_CNTL_REG)
+
+/* Some stoneage hardware needs delays after some operations. */
+#define kbd_pause() do { } while(0)
+
+/*
+ * Machine specific bits for the PS/2 driver
+ */
+
+#ifdef CONFIG_SH_CAYMAN
+#define AUX_IRQ (START_EXT_IRQS + 6) /* SMSC SuperIO IRQ12 */
+#endif
+
+#define aux_request_irq(hand, dev_id) \
+ request_irq(AUX_IRQ, hand, SA_SHIRQ, "PS/2 Mouse", dev_id)
+
+#define aux_free_irq(dev_id) free_irq(AUX_IRQ, dev_id)
+
+#endif /* __KERNEL__ */
+#endif /* __ASM_SH64_KEYBOARD_H */
+
--- /dev/null
+#ifndef __ASM_SH64_MMU_CONTEXT_H
+#define __ASM_SH64_MMU_CONTEXT_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/mmu_context.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003 Paul Mundt
+ *
+ * ASID handling idea taken from MIPS implementation.
+ *
+ */
+
+#ifndef __ASSEMBLY__
+
+/*
+ * Cache of MMU context last used.
+ *
+ * The MMU "context" consists of two things:
+ * (a) TLB cache version (or cycle, top 24 bits of mmu_context_cache)
+ * (b) ASID (Address Space IDentifier, bottom 8 bits of mmu_context_cache)
+ */
+extern unsigned long mmu_context_cache;
+
+#include <linux/config.h>
+#include <asm/page.h>
+
+
+/* Current mm's pgd */
+extern pgd_t *mmu_pdtp_cache;
+
+#define SR_ASID_MASK 0xffffffffff00ffffULL
+#define SR_ASID_SHIFT 16
+
+#define MMU_CONTEXT_ASID_MASK 0x000000ff
+#define MMU_CONTEXT_VERSION_MASK 0xffffff00
+#define MMU_CONTEXT_FIRST_VERSION 0x00000100
+#define NO_CONTEXT 0
+
+/* ASID is 8-bit value, so it can't be 0x100 */
+#define MMU_NO_ASID 0x100
+
+
+/*
+ * Virtual Page Number mask
+ */
+#define MMU_VPN_MASK 0xfffff000
+
+extern __inline__ void
+get_new_mmu_context(struct mm_struct *mm)
+{
+ extern void flush_tlb_all(void);
+ extern void flush_cache_all(void);
+
+ unsigned long mc = ++mmu_context_cache;
+
+ if (!(mc & MMU_CONTEXT_ASID_MASK)) {
+ /* We exhaust ASID of this version.
+ Flush all TLB and start new cycle. */
+ flush_tlb_all();
+ /* We have to flush all caches as ASIDs are
+ used in cache */
+ flush_cache_all();
+ /* Fix version if needed.
+ Note that we avoid version #0/asid #0 to distingush NO_CONTEXT. */
+ if (!mc)
+ mmu_context_cache = mc = MMU_CONTEXT_FIRST_VERSION;
+ }
+ mm->context = mc;
+}
+
+/*
+ * Get MMU context if needed.
+ */
+static __inline__ void
+get_mmu_context(struct mm_struct *mm)
+{
+ if (mm) {
+ unsigned long mc = mmu_context_cache;
+ /* Check if we have old version of context.
+ If it's old, we need to get new context with new version. */
+ if ((mm->context ^ mc) & MMU_CONTEXT_VERSION_MASK)
+ get_new_mmu_context(mm);
+ }
+}
+
+/*
+ * Initialize the context related info for a new mm_struct
+ * instance.
+ */
+static inline int init_new_context(struct task_struct *tsk,
+ struct mm_struct *mm)
+{
+ mm->context = NO_CONTEXT;
+
+ return 0;
+}
+
+/*
+ * Destroy context related info for an mm_struct that is about
+ * to be put to rest.
+ */
+static inline void destroy_context(struct mm_struct *mm)
+{
+ extern void flush_tlb_mm(struct mm_struct *mm);
+
+ /* Well, at least free TLB entries */
+ flush_tlb_mm(mm);
+}
+
+#endif /* __ASSEMBLY__ */
+
+/* Common defines */
+#define TLB_STEP 0x00000010
+#define TLB_PTEH 0x00000000
+#define TLB_PTEL 0x00000008
+
+/* PTEH defines */
+#define PTEH_ASID_SHIFT 2
+#define PTEH_VALID 0x0000000000000001
+#define PTEH_SHARED 0x0000000000000002
+#define PTEH_MATCH_ASID 0x00000000000003ff
+
+#ifndef __ASSEMBLY__
+/* This has to be a common function because the next location to fill
+ * information is shared. */
+extern void __do_tlb_refill(unsigned long address, unsigned long long is_text_not_data, pte_t *pte);
+
+/* Profiling counter. */
+#ifdef CONFIG_SH64_PROC_TLB
+extern unsigned long long calls_to_do_fast_page_fault;
+#endif
+
+static inline unsigned long get_asid(void)
+{
+ unsigned long long sr;
+
+ asm volatile ("getcon " __SR ", %0\n\t"
+ : "=r" (sr));
+
+ sr = (sr >> SR_ASID_SHIFT) & MMU_CONTEXT_ASID_MASK;
+ return (unsigned long) sr;
+}
+
+/* Set ASID into SR */
+static inline void set_asid(unsigned long asid)
+{
+ unsigned long long sr, pc;
+
+ asm volatile ("getcon " __SR ", %0" : "=r" (sr));
+
+ sr = (sr & SR_ASID_MASK) | (asid << SR_ASID_SHIFT);
+
+ /*
+ * It is possible that this function may be inlined and so to avoid
+ * the assembler reporting duplicate symbols we make use of the gas trick
+ * of generating symbols using numerics and forward reference.
+ */
+ asm volatile ("movi 1, %1\n\t"
+ "shlli %1, 28, %1\n\t"
+ "or %0, %1, %1\n\t"
+ "putcon %1, " __SR "\n\t"
+ "putcon %0, " __SSR "\n\t"
+ "movi 1f, %1\n\t"
+ "ori %1, 1 , %1\n\t"
+ "putcon %1, " __SPC "\n\t"
+ "rte\n"
+ "1:\n\t"
+ : "=r" (sr), "=r" (pc) : "0" (sr));
+}
+
+/*
+ * After we have set current->mm to a new value, this activates
+ * the context for the new mm so we see the new mappings.
+ */
+static __inline__ void activate_context(struct mm_struct *mm)
+{
+ get_mmu_context(mm);
+ set_asid(mm->context & MMU_CONTEXT_ASID_MASK);
+}
+
+
+static __inline__ void switch_mm(struct mm_struct *prev,
+ struct mm_struct *next,
+ struct task_struct *tsk)
+{
+ if (prev != next) {
+ mmu_pdtp_cache = next->pgd;
+ activate_context(next);
+ }
+}
+
+#define deactivate_mm(tsk,mm) do { } while (0)
+
+#define activate_mm(prev, next) \
+ switch_mm((prev),(next),NULL)
+
+static inline void
+enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
+{
+}
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ASM_SH64_MMU_CONTEXT_H */
--- /dev/null
+#ifndef __ASM_SH64_PAGE_H
+#define __ASM_SH64_PAGE_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/page.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003, 2004 Paul Mundt
+ *
+ * benedict.gaster@superh.com 19th, 24th July 2002.
+ *
+ * Modified to take account of enabling for D-CACHE support.
+ *
+ */
+
+#include <linux/config.h>
+
+/* PAGE_SHIFT determines the page size */
+#define PAGE_SHIFT 12
+#ifdef __ASSEMBLY__
+#define PAGE_SIZE 4096
+#else
+#define PAGE_SIZE (1UL << PAGE_SHIFT)
+#endif
+#define PAGE_MASK (~(PAGE_SIZE-1))
+#define PTE_MASK PAGE_MASK
+
+#if defined(CONFIG_HUGETLB_PAGE_SIZE_64K)
+#define HPAGE_SHIFT 16
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_1MB)
+#define HPAGE_SHIFT 20
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_512MB)
+#define HPAGE_SHIFT 29
+#endif
+
+#ifdef CONFIG_HUGETLB_PAGE
+#define HPAGE_SIZE (1UL << HPAGE_SHIFT)
+#define HPAGE_MASK (~(HPAGE_SIZE-1))
+#define HUGETLB_PAGE_ORDER (HPAGE_SHIFT-PAGE_SHIFT)
+#endif
+
+#ifdef __KERNEL__
+#ifndef __ASSEMBLY__
+
+extern struct page *mem_map;
+extern void sh64_page_clear(void *page);
+extern void sh64_page_copy(void *from, void *to);
+
+#define clear_page(page) sh64_page_clear(page)
+#define copy_page(to,from) sh64_page_copy(from, to)
+
+#if defined(CONFIG_DCACHE_DISABLED)
+
+#define clear_user_page(page, vaddr, pg) clear_page(page)
+#define copy_user_page(to, from, vaddr, pg) copy_page(to, from)
+
+#else
+
+extern void clear_user_page(void *to, unsigned long address, struct page *pg);
+extern void copy_user_page(void *to, void *from, unsigned long address, struct page *pg);
+
+#endif /* defined(CONFIG_DCACHE_DISABLED) */
+
+/*
+ * These are used to make use of C type-checking..
+ */
+typedef struct { unsigned long long pte; } pte_t;
+typedef struct { unsigned long pmd; } pmd_t;
+typedef struct { unsigned long pgd; } pgd_t;
+typedef struct { unsigned long pgprot; } pgprot_t;
+
+#define pte_val(x) ((x).pte)
+#define pmd_val(x) ((x).pmd)
+#define pgd_val(x) ((x).pgd)
+#define pgprot_val(x) ((x).pgprot)
+
+#define __pte(x) ((pte_t) { (x) } )
+#define __pmd(x) ((pmd_t) { (x) } )
+#define __pgd(x) ((pgd_t) { (x) } )
+#define __pgprot(x) ((pgprot_t) { (x) } )
+
+#endif /* !__ASSEMBLY__ */
+
+/* to align the pointer to the (next) page boundary */
+#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK)
+
+/*
+ * Kconfig defined.
+ */
+#define __MEMORY_START (CONFIG_MEMORY_START)
+#define PAGE_OFFSET (CONFIG_CACHED_MEMORY_OFFSET)
+
+#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
+#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))
+#define MAP_NR(addr) ((__pa(addr)-__MEMORY_START) >> PAGE_SHIFT)
+#define VALID_PAGE(page) ((page - mem_map) < max_mapnr)
+
+#define phys_to_page(phys) (mem_map + (((phys) - __MEMORY_START) >> PAGE_SHIFT))
+#define page_to_phys(page) (((page - mem_map) << PAGE_SHIFT) + __MEMORY_START)
+
+/* PFN start number, because of __MEMORY_START */
+#define PFN_START (__MEMORY_START >> PAGE_SHIFT)
+
+#define pfn_to_page(pfn) (mem_map + (pfn) - PFN_START)
+#define page_to_pfn(page) ((unsigned long)((page) - mem_map) + PFN_START)
+#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
+#define pfn_valid(pfn) (((pfn) - PFN_START) < max_mapnr)
+#define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
+
+#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \
+ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
+
+#ifndef __ASSEMBLY__
+
+/* Pure 2^n version of get_order */
+extern __inline__ int get_order(unsigned long size)
+{
+ int order;
+
+ size = (size-1) >> (PAGE_SHIFT-1);
+ order = -1;
+ do {
+ size >>= 1;
+ order++;
+ } while (size);
+ return order;
+}
+
+#endif
+
+#endif /* __KERNEL__ */
+
+#endif /* __ASM_SH64_PAGE_H */
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/param.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003 Paul Mundt
+ *
+ */
+#ifndef __ASM_SH64_PARAM_H
+#define __ASM_SH64_PARAM_H
+
+#include <linux/config.h>
+
+#ifdef __KERNEL__
+# ifdef CONFIG_SH_WDT
+# define HZ 1000 /* Needed for high-res WOVF */
+# else
+# define HZ 100
+# endif
+# define USER_HZ 100 /* User interfaces are in "ticks" */
+# define CLOCKS_PER_SEC (USER_HZ) /* frequency at which times() counts */
+#endif
+
+#ifndef HZ
+#define HZ 100
+#endif
+
+#define EXEC_PAGESIZE 4096
+
+#ifndef NGROUPS
+#define NGROUPS 32
+#endif
+
+#ifndef NOGROUP
+#define NOGROUP (-1)
+#endif
+
+#define MAXHOSTNAMELEN 64 /* max length of hostname */
+
+#endif /* __ASM_SH64_PARAM_H */
--- /dev/null
+#ifndef __ASM_SH64_PGALLOC_H
+#define __ASM_SH64_PGALLOC_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/pgalloc.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003, 2004 Paul Mundt
+ * Copyright (C) 2003, 2004 Richard Curnow
+ *
+ */
+
+#include <asm/processor.h>
+#include <linux/threads.h>
+#include <linux/mm.h>
+
+#define pgd_quicklist (current_cpu_data.pgd_quick)
+#define pmd_quicklist (current_cpu_data.pmd_quick)
+#define pte_quicklist (current_cpu_data.pte_quick)
+#define pgtable_cache_size (current_cpu_data.pgtable_cache_sz)
+
+static inline void pgd_init(unsigned long page)
+{
+ unsigned long *pgd = (unsigned long *)page;
+ extern pte_t empty_bad_pte_table[PTRS_PER_PTE];
+ int i;
+
+ for (i = 0; i < USER_PTRS_PER_PGD; i++)
+ pgd[i] = (unsigned long)empty_bad_pte_table;
+}
+
+/*
+ * Allocate and free page tables. The xxx_kernel() versions are
+ * used to allocate a kernel page table - this turns on ASN bits
+ * if any.
+ */
+
+extern __inline__ pgd_t *get_pgd_slow(void)
+{
+ unsigned int pgd_size = (USER_PTRS_PER_PGD * sizeof(pgd_t));
+ pgd_t *ret = (pgd_t *)kmalloc(pgd_size, GFP_KERNEL);
+ return ret;
+}
+
+extern __inline__ pgd_t *get_pgd_fast(void)
+{
+ unsigned long *ret;
+
+ if ((ret = pgd_quicklist) != NULL) {
+ pgd_quicklist = (unsigned long *)(*ret);
+ ret[0] = 0;
+ pgtable_cache_size--;
+ } else
+ ret = (unsigned long *)get_pgd_slow();
+
+ if (ret) {
+ memset(ret, 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
+ }
+ return (pgd_t *)ret;
+}
+
+extern __inline__ void free_pgd_fast(pgd_t *pgd)
+{
+ *(unsigned long *)pgd = (unsigned long) pgd_quicklist;
+ pgd_quicklist = (unsigned long *) pgd;
+ pgtable_cache_size++;
+}
+
+extern __inline__ void free_pgd_slow(pgd_t *pgd)
+{
+ kfree((void *)pgd);
+}
+
+extern pte_t *get_pte_slow(pmd_t *pmd, unsigned long address_preadjusted);
+extern pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long address_preadjusted);
+
+extern __inline__ pte_t *get_pte_fast(void)
+{
+ unsigned long *ret;
+
+ if((ret = (unsigned long *)pte_quicklist) != NULL) {
+ pte_quicklist = (unsigned long *)(*ret);
+ ret[0] = ret[1];
+ pgtable_cache_size--;
+ }
+ return (pte_t *)ret;
+}
+
+extern __inline__ void free_pte_fast(pte_t *pte)
+{
+ *(unsigned long *)pte = (unsigned long) pte_quicklist;
+ pte_quicklist = (unsigned long *) pte;
+ pgtable_cache_size++;
+}
+
+static inline void pte_free_kernel(pte_t *pte)
+{
+ free_page((unsigned long)pte);
+}
+
+static inline void pte_free(struct page *pte)
+{
+ __free_page(pte);
+}
+
+static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm,
+ unsigned long address)
+{
+ pte_t *pte;
+
+ pte = (pte_t *)__get_free_page(GFP_KERNEL | __GFP_REPEAT);
+ if (pte)
+ clear_page(pte);
+
+ return pte;
+}
+
+static inline struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address)
+{
+ struct page *pte;
+
+ pte = alloc_pages(GFP_KERNEL|__GFP_REPEAT, 0);
+ if (pte)
+ clear_page(page_address(pte));
+
+ return pte;
+}
+
+#define __pte_free_tlb(tlb,pte) tlb_remove_page((tlb),(pte))
+
+/*
+ * allocating and freeing a pmd is trivial: the 1-entry pmd is
+ * inside the pgd, so has no extra memory associated with it.
+ */
+
+#if defined(CONFIG_SH64_PGTABLE_2_LEVEL)
+
+#define pmd_alloc_one(mm, addr) ({ BUG(); ((pmd_t *)2); })
+#define pmd_free(x) do { } while (0)
+#define pgd_populate(mm, pmd, pte) BUG()
+#define __pte_free_tlb(tlb,pte) tlb_remove_page((tlb),(pte))
+#define __pmd_free_tlb(tlb,pmd) do { } while (0)
+
+#elif defined(CONFIG_SH64_PGTABLE_3_LEVEL)
+
+static __inline__ pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long address)
+{
+ pmd_t *pmd;
+ pmd = (pmd_t *) __get_free_page(GFP_KERNEL|__GFP_REPEAT);
+ if (pmd)
+ clear_page(pmd);
+ return pmd;
+}
+
+static __inline__ void pmd_free(pmd_t *pmd)
+{
+ free_page((unsigned long) pmd);
+}
+
+#define pgd_populate(mm, pgd, pmd) pgd_set(pgd, pmd)
+#define __pmd_free_tlb(tlb,pmd) pmd_free(pmd)
+
+#else
+#error "No defined page table size"
+#endif
+
+#define check_pgt_cache() do { } while (0)
+#define pgd_free(pgd) free_pgd_slow(pgd)
+#define pgd_alloc(mm) get_pgd_fast()
+
+extern int do_check_pgt_cache(int, int);
+
+extern inline void set_pgdir(unsigned long address, pgd_t entry)
+{
+ struct task_struct * p;
+ pgd_t *pgd;
+
+ read_lock(&tasklist_lock);
+ for_each_process(p) {
+ if (!p->mm)
+ continue;
+ *pgd_offset(p->mm,address) = entry;
+ }
+ read_unlock(&tasklist_lock);
+ for (pgd = (pgd_t *)pgd_quicklist; pgd; pgd = (pgd_t *)*(unsigned long *)pgd)
+ pgd[address >> PGDIR_SHIFT] = entry;
+}
+
+#define pmd_populate_kernel(mm, pmd, pte) \
+ set_pmd(pmd, __pmd(_PAGE_TABLE + (unsigned long) (pte)))
+
+static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd,
+ struct page *pte)
+{
+ set_pmd(pmd, __pmd(_PAGE_TABLE + (unsigned long) page_address (pte)));
+}
+
+#endif /* __ASM_SH64_PGALLOC_H */
--- /dev/null
+#ifndef __ASM_SH64_PGTABLE_H
+#define __ASM_SH64_PGTABLE_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/pgtable.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003, 2004 Paul Mundt
+ * Copyright (C) 2003, 2004 Richard Curnow
+ *
+ * This file contains the functions and defines necessary to modify and use
+ * the SuperH page table tree.
+ */
+
+#ifndef __ASSEMBLY__
+#include <asm/processor.h>
+#include <asm/page.h>
+#include <linux/threads.h>
+#include <linux/config.h>
+
+extern void paging_init(void);
+
+/* We provide our own get_unmapped_area to avoid cache synonym issue */
+#define HAVE_ARCH_UNMAPPED_AREA
+
+/*
+ * Basically we have the same two-level (which is the logical three level
+ * Linux page table layout folded) page tables as the i386.
+ */
+
+/*
+ * ZERO_PAGE is a global shared page that is always zero: used
+ * for zero-mapped memory areas etc..
+ */
+extern unsigned char empty_zero_page[PAGE_SIZE];
+#define ZERO_PAGE(vaddr) (mem_map + MAP_NR(empty_zero_page))
+
+#endif /* !__ASSEMBLY__ */
+
+/*
+ * NEFF and NPHYS related defines.
+ * FIXME : These need to be model-dependent. For now this is OK, SH5-101 and SH5-103
+ * implement 32 bits effective and 32 bits physical. But future implementations may
+ * extend beyond this.
+ */
+#define NEFF 32
+#define NEFF_SIGN (1LL << (NEFF - 1))
+#define NEFF_MASK (-1LL << NEFF)
+
+#define NPHYS 32
+#define NPHYS_SIGN (1LL << (NPHYS - 1))
+#define NPHYS_MASK (-1LL << NPHYS)
+
+/* Typically 2-level is sufficient up to 32 bits of virtual address space, beyond
+ that 3-level would be appropriate. */
+#if defined(CONFIG_SH64_PGTABLE_2_LEVEL)
+/* For 4k pages, this contains 512 entries, i.e. 9 bits worth of address. */
+#define PTRS_PER_PTE ((1<<PAGE_SHIFT)/sizeof(unsigned long long))
+#define PTE_MAGNITUDE 3 /* sizeof(unsigned long long) magnit. */
+#define PTE_SHIFT PAGE_SHIFT
+#define PTE_BITS (PAGE_SHIFT - PTE_MAGNITUDE)
+
+/* top level: PMD. */
+#define PGDIR_SHIFT (PTE_SHIFT + PTE_BITS)
+#define PGD_BITS (NEFF - PGDIR_SHIFT)
+#define PTRS_PER_PGD (1<<PGD_BITS)
+
+/* middle level: PMD. This doesn't do anything for the 2-level case. */
+#define PTRS_PER_PMD (1)
+
+#define PGDIR_SIZE (1UL << PGDIR_SHIFT)
+#define PGDIR_MASK (~(PGDIR_SIZE-1))
+#define PMD_SHIFT PGDIR_SHIFT
+#define PMD_SIZE PGDIR_SIZE
+#define PMD_MASK PGDIR_MASK
+
+#elif defined(CONFIG_SH64_PGTABLE_3_LEVEL)
+/*
+ * three-level asymmetric paging structure: PGD is top level.
+ * The asymmetry comes from 32-bit pointers and 64-bit PTEs.
+ */
+/* bottom level: PTE. It's 9 bits = 512 pointers */
+#define PTRS_PER_PTE ((1<<PAGE_SHIFT)/sizeof(unsigned long long))
+#define PTE_MAGNITUDE 3 /* sizeof(unsigned long long) magnit. */
+#define PTE_SHIFT PAGE_SHIFT
+#define PTE_BITS (PAGE_SHIFT - PTE_MAGNITUDE)
+
+/* middle level: PMD. It's 10 bits = 1024 pointers */
+#define PTRS_PER_PMD ((1<<PAGE_SHIFT)/sizeof(unsigned long long *))
+#define PMD_MAGNITUDE 2 /* sizeof(unsigned long long *) magnit. */
+#define PMD_SHIFT (PTE_SHIFT + PTE_BITS)
+#define PMD_BITS (PAGE_SHIFT - PMD_MAGNITUDE)
+
+/* top level: PMD. It's 1 bit = 2 pointers */
+#define PGDIR_SHIFT (PMD_SHIFT + PMD_BITS)
+#define PGD_BITS (NEFF - PGDIR_SHIFT)
+#define PTRS_PER_PGD (1<<PGD_BITS)
+
+#define PMD_SIZE (1UL << PMD_SHIFT)
+#define PMD_MASK (~(PMD_SIZE-1))
+#define PGDIR_SIZE (1UL << PGDIR_SHIFT)
+#define PGDIR_MASK (~(PGDIR_SIZE-1))
+
+#else
+#error "No defined number of page table levels"
+#endif
+
+/*
+ * Error outputs.
+ */
+#define pte_ERROR(e) \
+ printk("%s:%d: bad pte %016Lx.\n", __FILE__, __LINE__, pte_val(e))
+#define pmd_ERROR(e) \
+ printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e))
+#define pgd_ERROR(e) \
+ printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e))
+
+/*
+ * Table setting routines. Used within arch/mm only.
+ */
+#define set_pgd(pgdptr, pgdval) (*(pgdptr) = pgdval)
+#define set_pmd(pmdptr, pmdval) (*(pmdptr) = pmdval)
+
+static __inline__ void set_pte(pte_t *pteptr, pte_t pteval)
+{
+ unsigned long long x = ((unsigned long long) pteval.pte);
+ unsigned long long *xp = (unsigned long long *) pteptr;
+ /*
+ * Sign-extend based on NPHYS.
+ */
+ *(xp) = (x & NPHYS_SIGN) ? (x | NPHYS_MASK) : x;
+}
+
+static __inline__ void pmd_set(pmd_t *pmdp,pte_t *ptep)
+{
+ pmd_val(*pmdp) = (unsigned long) ptep;
+}
+
+/*
+ * PGD defines. Top level.
+ */
+
+/* To find an entry in a generic PGD. */
+#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1))
+#define __pgd_offset(address) pgd_index(address)
+#define pgd_offset(mm, address) ((mm)->pgd+pgd_index(address))
+
+/* To find an entry in a kernel PGD. */
+#define pgd_offset_k(address) pgd_offset(&init_mm, address)
+
+/*
+ * PGD level access routines.
+ *
+ * Note1:
+ * There's no need to use physical addresses since the tree walk is all
+ * in performed in software, until the PTE translation.
+ *
+ * Note 2:
+ * A PGD entry can be uninitialized (_PGD_UNUSED), generically bad,
+ * clear (_PGD_EMPTY), present. When present, lower 3 nibbles contain
+ * _KERNPG_TABLE. Being a kernel virtual pointer also bit 31 must
+ * be 1. Assuming an arbitrary clear value of bit 31 set to 0 and
+ * lower 3 nibbles set to 0xFFF (_PGD_EMPTY) any other value is a
+ * bad pgd that must be notified via printk().
+ *
+ */
+#define _PGD_EMPTY 0x0
+
+#if defined(CONFIG_SH64_PGTABLE_2_LEVEL)
+static inline int pgd_none(pgd_t pgd) { return 0; }
+static inline int pgd_bad(pgd_t pgd) { return 0; }
+#define pgd_present(pgd) ((pgd_val(pgd) & _PAGE_PRESENT) ? 1 : 0)
+#define pgd_clear(xx) do { } while(0)
+
+#elif defined(CONFIG_SH64_PGTABLE_3_LEVEL)
+#define pgd_present(pgd_entry) (1)
+#define pgd_none(pgd_entry) (pgd_val((pgd_entry)) == _PGD_EMPTY)
+/* TODO: Think later about what a useful definition of 'bad' would be now. */
+#define pgd_bad(pgd_entry) (0)
+#define pgd_clear(pgd_entry_p) (set_pgd((pgd_entry_p), __pgd(_PGD_EMPTY)))
+
+#endif
+
+
+#define pgd_page(pgd_entry) ((unsigned long) (pgd_val(pgd_entry) & PAGE_MASK))
+
+/*
+ * PMD defines. Middle level.
+ */
+
+/* PGD to PMD dereferencing */
+#if defined(CONFIG_SH64_PGTABLE_2_LEVEL)
+static inline pmd_t * pmd_offset(pgd_t * dir, unsigned long address)
+{
+ return (pmd_t *) dir;
+}
+#elif defined(CONFIG_SH64_PGTABLE_3_LEVEL)
+#define __pmd_offset(address) \
+ (((address) >> PMD_SHIFT) & (PTRS_PER_PMD-1))
+#define pmd_offset(dir, addr) \
+ ((pmd_t *) ((pgd_val(*(dir))) & PAGE_MASK) + __pmd_offset((addr)))
+#endif
+
+/*
+ * PMD level access routines. Same notes as above.
+ */
+#define _PMD_EMPTY 0x0
+/* Either the PMD is empty or present, it's not paged out */
+#define pmd_present(pmd_entry) (pmd_val(pmd_entry) & _PAGE_PRESENT)
+#define pmd_clear(pmd_entry_p) (set_pmd((pmd_entry_p), __pmd(_PMD_EMPTY)))
+#define pmd_none(pmd_entry) (pmd_val((pmd_entry)) == _PMD_EMPTY)
+#define pmd_bad(pmd_entry) ((pmd_val(pmd_entry) & (~PAGE_MASK & ~_PAGE_USER)) != _KERNPG_TABLE)
+
+#define pmd_page_kernel(pmd_entry) \
+ ((unsigned long) __va(pmd_val(pmd_entry) & PAGE_MASK))
+
+#define pmd_page(pmd) \
+ (virt_to_page(pmd_val(pmd)))
+
+/* PMD to PTE dereferencing */
+#define pte_index(address) \
+ ((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
+
+#define pte_offset_kernel(dir, addr) \
+ ((pte_t *) ((pmd_val(*(dir))) & PAGE_MASK) + pte_index((addr)))
+
+#define pte_offset_map(dir,addr) pte_offset_kernel(dir, addr)
+#define pte_offset_map_nested(dir,addr) pte_offset_kernel(dir, addr)
+#define pte_unmap(pte) do { } while (0)
+#define pte_unmap_nested(pte) do { } while (0)
+
+/* Round it up ! */
+#define USER_PTRS_PER_PGD ((TASK_SIZE+PGDIR_SIZE-1)/PGDIR_SIZE)
+#define FIRST_USER_PGD_NR 0
+
+#ifndef __ASSEMBLY__
+#define VMALLOC_END 0xff000000
+#define VMALLOC_START 0xf0000000
+#define VMALLOC_VMADDR(x) ((unsigned long)(x))
+
+#define IOBASE_VADDR 0xff000000
+#define IOBASE_END 0xffffffff
+
+/*
+ * PTEL coherent flags.
+ * See Chapter 17 ST50 CPU Core Volume 1, Architecture.
+ */
+/* The bits that are required in the SH-5 TLB are placed in the h/w-defined
+ positions, to avoid expensive bit shuffling on every refill. The remaining
+ bits are used for s/w purposes and masked out on each refill.
+
+ Note, the PTE slots are used to hold data of type swp_entry_t when a page is
+ swapped out. Only the _PAGE_PRESENT flag is significant when the page is
+ swapped out, and it must be placed so that it doesn't overlap either the
+ type or offset fields of swp_entry_t. For x86, offset is at [31:8] and type
+ at [6:1], with _PAGE_PRESENT at bit 0 for both pte_t and swp_entry_t. This
+ scheme doesn't map to SH-5 because bit [0] controls cacheability. So bit
+ [2] is used for _PAGE_PRESENT and the type field of swp_entry_t is split
+ into 2 pieces. That is handled by SWP_ENTRY and SWP_TYPE below. */
+#define _PAGE_WT 0x001 /* CB0: if cacheable, 1->write-thru, 0->write-back */
+#define _PAGE_DEVICE 0x001 /* CB0: if uncacheable, 1->device (i.e. no write-combining or reordering at bus level) */
+#define _PAGE_CACHABLE 0x002 /* CB1: uncachable/cachable */
+#define _PAGE_PRESENT 0x004 /* software: page referenced */
+#define _PAGE_FILE 0x004 /* software: only when !present */
+#define _PAGE_SIZE0 0x008 /* SZ0-bit : size of page */
+#define _PAGE_SIZE1 0x010 /* SZ1-bit : size of page */
+#define _PAGE_SHARED 0x020 /* software: reflects PTEH's SH */
+#define _PAGE_READ 0x040 /* PR0-bit : read access allowed */
+#define _PAGE_EXECUTE 0x080 /* PR1-bit : execute access allowed */
+#define _PAGE_WRITE 0x100 /* PR2-bit : write access allowed */
+#define _PAGE_USER 0x200 /* PR3-bit : user space access allowed */
+#define _PAGE_DIRTY 0x400 /* software: page accessed in write */
+#define _PAGE_ACCESSED 0x800 /* software: page referenced */
+
+/* Mask which drops software flags */
+#define _PAGE_FLAGS_HARDWARE_MASK 0xfffffffffffff3dbLL
+/* Flags default: 4KB, Read, Not write, Not execute, Not user */
+#define _PAGE_FLAGS_HARDWARE_DEFAULT 0x0000000000000040LL
+
+/*
+ * HugeTLB support
+ */
+#if defined(CONFIG_HUGETLB_PAGE_SIZE_64K)
+#define _PAGE_SZHUGE (_PAGE_SIZE0)
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_1MB)
+#define _PAGE_SZHUGE (_PAGE_SIZE1)
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_512MB)
+#define _PAGE_SZHUGE (_PAGE_SIZE0 | _PAGE_SIZE1)
+#endif
+
+/*
+ * Default flags for a Kernel page.
+ * This is fundametally also SHARED because the main use of this define
+ * (other than for PGD/PMD entries) is for the VMALLOC pool which is
+ * contextless.
+ *
+ * _PAGE_EXECUTE is required for modules
+ *
+ */
+#define _KERNPG_TABLE (_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | \
+ _PAGE_EXECUTE | \
+ _PAGE_CACHABLE | _PAGE_ACCESSED | _PAGE_DIRTY | \
+ _PAGE_SHARED)
+
+/* Default flags for a User page */
+#define _PAGE_TABLE (_KERNPG_TABLE | _PAGE_USER)
+
+#define _PAGE_CHG_MASK (PTE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
+
+#define PAGE_NONE __pgprot(_PAGE_CACHABLE | _PAGE_ACCESSED)
+#define PAGE_SHARED __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | \
+ _PAGE_CACHABLE | _PAGE_ACCESSED | _PAGE_USER | \
+ _PAGE_SHARED)
+/* We need to include PAGE_EXECUTE in PAGE_COPY because it is the default
+ * protection mode for the stack. */
+#define PAGE_COPY __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_CACHABLE | \
+ _PAGE_ACCESSED | _PAGE_USER | _PAGE_EXECUTE)
+#define PAGE_READONLY __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_CACHABLE | \
+ _PAGE_ACCESSED | _PAGE_USER)
+#define PAGE_KERNEL __pgprot(_KERNPG_TABLE)
+
+
+/*
+ * In ST50 we have full permissions (Read/Write/Execute/Shared).
+ * Just match'em all. These are for mmap(), therefore all at least
+ * User/Cachable/Present/Accessed. No point in making Fault on Write.
+ */
+#define __MMAP_COMMON (_PAGE_PRESENT | _PAGE_USER | _PAGE_CACHABLE | _PAGE_ACCESSED)
+ /* sxwr */
+#define __P000 __pgprot(__MMAP_COMMON)
+#define __P001 __pgprot(__MMAP_COMMON | _PAGE_READ)
+#define __P010 __pgprot(__MMAP_COMMON)
+#define __P011 __pgprot(__MMAP_COMMON | _PAGE_READ)
+#define __P100 __pgprot(__MMAP_COMMON | _PAGE_EXECUTE)
+#define __P101 __pgprot(__MMAP_COMMON | _PAGE_EXECUTE | _PAGE_READ)
+#define __P110 __pgprot(__MMAP_COMMON | _PAGE_EXECUTE)
+#define __P111 __pgprot(__MMAP_COMMON | _PAGE_EXECUTE | _PAGE_READ)
+
+#define __S000 __pgprot(__MMAP_COMMON | _PAGE_SHARED)
+#define __S001 __pgprot(__MMAP_COMMON | _PAGE_SHARED | _PAGE_READ)
+#define __S010 __pgprot(__MMAP_COMMON | _PAGE_SHARED | _PAGE_WRITE)
+#define __S011 __pgprot(__MMAP_COMMON | _PAGE_SHARED | _PAGE_READ | _PAGE_WRITE)
+#define __S100 __pgprot(__MMAP_COMMON | _PAGE_SHARED | _PAGE_EXECUTE)
+#define __S101 __pgprot(__MMAP_COMMON | _PAGE_SHARED | _PAGE_EXECUTE | _PAGE_READ)
+#define __S110 __pgprot(__MMAP_COMMON | _PAGE_SHARED | _PAGE_EXECUTE | _PAGE_WRITE)
+#define __S111 __pgprot(__MMAP_COMMON | _PAGE_SHARED | _PAGE_EXECUTE | _PAGE_READ | _PAGE_WRITE)
+
+/* Make it a device mapping for maximum safety (e.g. for mapping device
+ registers into user-space via /dev/map). */
+#define pgprot_noncached(x) __pgprot(((x).pgprot & ~(_PAGE_CACHABLE)) | _PAGE_DEVICE)
+#define pgprot_writecombine(prot) __pgprot(pgprot_val(prot) & ~_PAGE_CACHABLE)
+
+/*
+ * Handling allocation failures during page table setup.
+ */
+extern void __handle_bad_pmd_kernel(pmd_t * pmd);
+#define __handle_bad_pmd(x) __handle_bad_pmd_kernel(x)
+
+/*
+ * PTE level access routines.
+ *
+ * Note1:
+ * It's the tree walk leaf. This is physical address to be stored.
+ *
+ * Note 2:
+ * Regarding the choice of _PTE_EMPTY:
+
+ We must choose a bit pattern that cannot be valid, whether or not the page
+ is present. bit[2]==1 => present, bit[2]==0 => swapped out. If swapped
+ out, bits [31:8], [6:3], [1:0] are under swapper control, so only bit[7] is
+ left for us to select. If we force bit[7]==0 when swapped out, we could use
+ the combination bit[7,2]=2'b10 to indicate an empty PTE. Alternatively, if
+ we force bit[7]==1 when swapped out, we can use all zeroes to indicate
+ empty. This is convenient, because the page tables get cleared to zero
+ when they are allocated.
+
+ */
+#define _PTE_EMPTY 0x0
+#define pte_present(x) (pte_val(x) & _PAGE_PRESENT)
+#define pte_clear(xp) (set_pte(xp, __pte(_PTE_EMPTY)))
+#define pte_none(x) (pte_val(x) == _PTE_EMPTY)
+
+/*
+ * Some definitions to translate between mem_map, PTEs, and page
+ * addresses:
+ */
+
+/*
+ * Given a PTE, return the index of the mem_map[] entry corresponding
+ * to the page frame the PTE. Get the absolute physical address, make
+ * a relative physical address and translate it to an index.
+ */
+#define pte_pagenr(x) (((unsigned long) (pte_val(x)) - \
+ __MEMORY_START) >> PAGE_SHIFT)
+
+/*
+ * Given a PTE, return the "struct page *".
+ */
+#define pte_page(x) (mem_map + pte_pagenr(x))
+
+/*
+ * Return number of (down rounded) MB corresponding to x pages.
+ */
+#define pages_to_mb(x) ((x) >> (20-PAGE_SHIFT))
+
+
+/*
+ * The following have defined behavior only work if pte_present() is true.
+ */
+static inline int pte_read(pte_t pte) { return pte_val(pte) & _PAGE_READ; }
+static inline int pte_exec(pte_t pte) { return pte_val(pte) & _PAGE_EXECUTE; }
+static inline int pte_dirty(pte_t pte){ return pte_val(pte) & _PAGE_DIRTY; }
+static inline int pte_young(pte_t pte){ return pte_val(pte) & _PAGE_ACCESSED; }
+static inline int pte_file(pte_t pte) { return pte_val(pte) & _PAGE_FILE; }
+static inline int pte_write(pte_t pte){ return pte_val(pte) & _PAGE_WRITE; }
+
+extern inline pte_t pte_rdprotect(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) & ~_PAGE_READ)); return pte; }
+extern inline pte_t pte_wrprotect(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) & ~_PAGE_WRITE)); return pte; }
+extern inline pte_t pte_exprotect(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) & ~_PAGE_EXECUTE)); return pte; }
+extern inline pte_t pte_mkclean(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) & ~_PAGE_DIRTY)); return pte; }
+extern inline pte_t pte_mkold(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) & ~_PAGE_ACCESSED)); return pte; }
+
+extern inline pte_t pte_mkread(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) | _PAGE_READ)); return pte; }
+extern inline pte_t pte_mkwrite(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) | _PAGE_WRITE)); return pte; }
+extern inline pte_t pte_mkexec(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) | _PAGE_EXECUTE)); return pte; }
+extern inline pte_t pte_mkdirty(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) | _PAGE_DIRTY)); return pte; }
+extern inline pte_t pte_mkyoung(pte_t pte) { set_pte(&pte, __pte(pte_val(pte) | _PAGE_ACCESSED)); return pte; }
+
+/*
+ * Conversion functions: convert a page and protection to a page entry.
+ *
+ * extern pte_t mk_pte(struct page *page, pgprot_t pgprot)
+ */
+#define mk_pte(page,pgprot) \
+({ \
+ pte_t __pte; \
+ \
+ set_pte(&__pte, __pte((((page)-mem_map) << PAGE_SHIFT) | \
+ __MEMORY_START | pgprot_val((pgprot)))); \
+ __pte; \
+})
+
+/*
+ * This takes a (absolute) physical page address that is used
+ * by the remapping functions
+ */
+#define mk_pte_phys(physpage, pgprot) \
+({ pte_t __pte; set_pte(&__pte, __pte(physpage | pgprot_val(pgprot))); __pte; })
+
+extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
+{ set_pte(&pte, __pte((pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot))); return pte; }
+
+#define page_pte_prot(page, prot) mk_pte(page, prot)
+#define page_pte(page) page_pte_prot(page, __pgprot(0))
+
+typedef pte_t *pte_addr_t;
+#define pgtable_cache_init() do { } while (0)
+
+extern void update_mmu_cache(struct vm_area_struct * vma,
+ unsigned long address, pte_t pte);
+
+/* Encode and decode a swap entry */
+#define __swp_type(x) (((x).val & 3) + (((x).val >> 1) & 0x3c))
+#define __swp_offset(x) ((x).val >> 8)
+#define __swp_entry(type, offset) ((swp_entry_t) { ((offset << 8) + ((type & 0x3c) << 1) + (type & 3)) })
+#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) })
+#define __swp_entry_to_pte(x) ((pte_t) { (x).val })
+
+/* Encode and decode a nonlinear file mapping entry */
+#define PTE_FILE_MAX_BITS 29
+#define pte_to_pgoff(pte) (pte_val(pte))
+#define pgoff_to_pte(off) ((pte_t) { (off) | _PAGE_FILE })
+
+/* Needs to be defined here and not in linux/mm.h, as it is arch dependent */
+#define PageSkip(page) (0)
+#define kern_addr_valid(addr) (1)
+
+#define io_remap_page_range remap_page_range
+#endif /* !__ASSEMBLY__ */
+
+/*
+ * No page table caches to initialise
+ */
+#define pgtable_cache_init() do { } while (0)
+
+#define pte_pfn(x) (((unsigned long)((x).pte)) >> PAGE_SHIFT)
+#define pfn_pte(pfn, prot) __pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot))
+#define pfn_pmd(pfn, prot) __pmd(((pfn) << PAGE_SHIFT) | pgprot_val(prot))
+
+extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
+
+#include <asm-generic/pgtable.h>
+
+#endif /* __ASM_SH64_PGTABLE_H */
--- /dev/null
+#ifndef __ASM_SH64_PLATFORM_H
+#define __ASM_SH64_PLATFORM_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/platform.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ *
+ * benedict.gaster@superh.com: 3rd May 2002
+ * Added support for ramdisk, removing statically linked romfs at the same time.
+ */
+
+#include <linux/ioport.h>
+#include <asm/irq.h>
+
+
+/*
+ * Platform definition structure.
+ */
+struct sh64_platform {
+ unsigned int readonly_rootfs;
+ unsigned int ramdisk_flags;
+ unsigned int initial_root_dev;
+ unsigned int loader_type;
+ unsigned int initrd_start;
+ unsigned int initrd_size;
+ unsigned int fpu_flags;
+ unsigned int io_res_count;
+ unsigned int kram_res_count;
+ unsigned int xram_res_count;
+ unsigned int rom_res_count;
+ struct resource *io_res_p;
+ struct resource *kram_res_p;
+ struct resource *xram_res_p;
+ struct resource *rom_res_p;
+};
+
+extern struct sh64_platform platform_parms;
+
+extern unsigned long long memory_start, memory_end;
+
+extern unsigned long long fpu_in_use;
+
+extern int platform_int_priority[NR_INTC_IRQS];
+
+#define FPU_FLAGS (platform_parms.fpu_flags)
+#define STANDARD_IO_RESOURCES (platform_parms.io_res_count)
+#define STANDARD_KRAM_RESOURCES (platform_parms.kram_res_count)
+#define STANDARD_XRAM_RESOURCES (platform_parms.xram_res_count)
+#define STANDARD_ROM_RESOURCES (platform_parms.rom_res_count)
+
+/*
+ * Kernel Memory description, Respectively:
+ * code = last but one memory descriptor
+ * data = last memory descriptor
+ */
+#define code_resource (platform_parms.kram_res_p[STANDARD_KRAM_RESOURCES - 2])
+#define data_resource (platform_parms.kram_res_p[STANDARD_KRAM_RESOURCES - 1])
+
+/* Be prepared to 64-bit sign extensions */
+#define PFN_UP(x) ((((x) + PAGE_SIZE-1) >> PAGE_SHIFT) & 0x000fffff)
+#define PFN_DOWN(x) (((x) >> PAGE_SHIFT) & 0x000fffff)
+#define PFN_PHYS(x) ((x) << PAGE_SHIFT)
+
+#endif /* __ASM_SH64_PLATFORM_H */
--- /dev/null
+#ifndef __ASM_SH64_POLL_H
+#define __ASM_SH64_POLL_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/poll.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ *
+ */
+
+/* These are specified by iBCS2 */
+#define POLLIN 0x0001
+#define POLLPRI 0x0002
+#define POLLOUT 0x0004
+#define POLLERR 0x0008
+#define POLLHUP 0x0010
+#define POLLNVAL 0x0020
+
+/* The rest seem to be more-or-less nonstandard. Check them! */
+#define POLLRDNORM 0x0040
+#define POLLRDBAND 0x0080
+#define POLLWRNORM 0x0100
+#define POLLWRBAND 0x0200
+#define POLLMSG 0x0400
+
+struct pollfd {
+ int fd;
+ short events;
+ short revents;
+};
+
+#endif /* __ASM_SH64_POLL_H */
--- /dev/null
+#ifndef __ASM_SH64_PROCESSOR_H
+#define __ASM_SH64_PROCESSOR_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/processor.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003 Paul Mundt
+ * Copyright (C) 2004 Richard Curnow
+ *
+ */
+
+#include <asm/page.h>
+
+#ifndef __ASSEMBLY__
+
+#include <asm/types.h>
+#include <asm/cache.h>
+#include <asm/registers.h>
+#include <linux/threads.h>
+
+/*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ \
+void *pc; \
+unsigned long long __dummy = 0; \
+__asm__("gettr tr0, %1\n\t" \
+ "pta 4, tr0\n\t" \
+ "gettr tr0, %0\n\t" \
+ "ptabs %1, tr0\n\t" \
+ :"=r" (pc), "=r" (__dummy) \
+ : "1" (__dummy)); \
+pc; })
+
+/*
+ * CPU type and hardware bug flags. Kept separately for each CPU.
+ */
+enum cpu_type {
+ CPU_SH5_101,
+ CPU_SH5_103,
+ CPU_SH_NONE
+};
+
+/*
+ * TLB information structure
+ *
+ * Defined for both I and D tlb, per-processor.
+ */
+struct tlb_info {
+ unsigned long long next;
+ unsigned long long first;
+ unsigned long long last;
+
+ unsigned int entries;
+ unsigned int step;
+
+ unsigned long flags;
+};
+
+struct sh_cpuinfo {
+ enum cpu_type type;
+ unsigned long loops_per_jiffy;
+
+ char hard_math;
+
+ unsigned long *pgd_quick;
+ unsigned long *pmd_quick;
+ unsigned long *pte_quick;
+ unsigned long pgtable_cache_sz;
+ unsigned int cpu_clock, master_clock, bus_clock, module_clock;
+
+ /* Cache info */
+ struct cache_info icache;
+ struct cache_info dcache;
+
+ /* TLB info */
+ struct tlb_info itlb;
+ struct tlb_info dtlb;
+};
+
+extern struct sh_cpuinfo boot_cpu_data;
+
+#define cpu_data (&boot_cpu_data)
+#define current_cpu_data boot_cpu_data
+
+#endif
+
+/*
+ * User space process size: 2GB - 4k.
+ */
+#define TASK_SIZE 0x7ffff000UL
+
+/* This decides where the kernel will search for a free chunk of vm
+ * space during mmap's.
+ */
+#define TASK_UNMAPPED_BASE (TASK_SIZE / 3)
+
+/*
+ * Bit of SR register
+ *
+ * FD-bit:
+ * When it's set, it means the processor doesn't have right to use FPU,
+ * and it results exception when the floating operation is executed.
+ *
+ * IMASK-bit:
+ * Interrupt level mask
+ *
+ * STEP-bit:
+ * Single step bit
+ *
+ */
+#define SR_FD 0x00008000
+
+#if defined(CONFIG_SH64_SR_WATCH)
+#define SR_MMU 0x84000000
+#else
+#define SR_MMU 0x80000000
+#endif
+
+#define SR_IMASK 0x000000f0
+#define SR_SSTEP 0x08000000
+
+#ifndef __ASSEMBLY__
+
+/*
+ * FPU structure and data : require 8-byte alignment as we need to access it
+ with fld.p, fst.p
+ */
+
+struct sh_fpu_hard_struct {
+ unsigned long fp_regs[64];
+ unsigned int fpscr;
+ /* long status; * software status information */
+};
+
+#if 0
+/* Dummy fpu emulator */
+struct sh_fpu_soft_struct {
+ unsigned long long fp_regs[32];
+ unsigned int fpscr;
+ unsigned char lookahead;
+ unsigned long entry_pc;
+};
+#endif
+
+union sh_fpu_union {
+ struct sh_fpu_hard_struct hard;
+ /* 'hard' itself only produces 32 bit alignment, yet we need
+ to access it using 64 bit load/store as well. */
+ unsigned long long alignment_dummy;
+};
+
+struct thread_struct {
+ unsigned long sp;
+ unsigned long pc;
+ /* This stores the address of the pt_regs built during a context
+ switch, or of the register save area built for a kernel mode
+ exception. It is used for backtracing the stack of a sleeping task
+ or one that traps in kernel mode. */
+ struct pt_regs *kregs;
+ /* This stores the address of the pt_regs constructed on entry from
+ user mode. It is a fixed value over the lifetime of a process, or
+ NULL for a kernel thread. */
+ struct pt_regs *uregs;
+
+ unsigned long trap_no, error_code;
+ unsigned long address;
+ /* Hardware debugging registers may come here */
+
+ /* floating point info */
+ union sh_fpu_union fpu;
+};
+
+#define INIT_MMAP \
+{ &init_mm, 0, 0, NULL, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC, 1, NULL, NULL }
+
+extern struct pt_regs fake_swapper_regs;
+
+#define INIT_THREAD { \
+ .sp = sizeof(init_stack) + \
+ (long) &init_stack, \
+ .pc = 0, \
+ .kregs = &fake_swapper_regs, \
+ .uregs = NULL, \
+ .trap_no = 0, \
+ .error_code = 0, \
+ .address = 0, \
+ .fpu = { { { 0, } }, } \
+}
+
+/*
+ * Do necessary setup to start up a newly executed thread.
+ */
+#define SR_USER (SR_MMU | SR_FD)
+
+#define start_thread(regs, new_pc, new_sp) \
+ set_fs(USER_DS); \
+ regs->sr = SR_USER; /* User mode. */ \
+ regs->pc = new_pc - 4; /* Compensate syscall exit */ \
+ regs->pc |= 1; /* Set SHmedia ! */ \
+ regs->regs[18] = 0; \
+ regs->regs[15] = new_sp
+
+/* Forward declaration, a strange C thing */
+struct task_struct;
+struct mm_struct;
+
+/* Free all resources held by a thread. */
+extern void release_thread(struct task_struct *);
+/*
+ * create a kernel thread without removing it from tasklists
+ */
+extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
+
+/*
+ * Bus types
+ */
+#define MCA_bus 0
+#define MCA_bus__is_a_macro /* for versions in ksyms.c */
+
+
+/* Copy and release all segment info associated with a VM */
+#define copy_segments(p, mm) do { } while (0)
+#define release_segments(mm) do { } while (0)
+#define forget_segments() do { } while (0)
+#define prepare_to_copy(tsk) do { } while (0)
+/*
+ * FPU lazy state save handling.
+ */
+
+extern __inline__ void release_fpu(void)
+{
+ unsigned long long __dummy;
+
+ /* Set FD flag in SR */
+ __asm__ __volatile__("getcon " __SR ", %0\n\t"
+ "or %0, %1, %0\n\t"
+ "putcon %0, " __SR "\n\t"
+ : "=&r" (__dummy)
+ : "r" (SR_FD));
+}
+
+extern __inline__ void grab_fpu(void)
+{
+ unsigned long long __dummy;
+
+ /* Clear out FD flag in SR */
+ __asm__ __volatile__("getcon " __SR ", %0\n\t"
+ "and %0, %1, %0\n\t"
+ "putcon %0, " __SR "\n\t"
+ : "=&r" (__dummy)
+ : "r" (~SR_FD));
+}
+
+/* Round to nearest, no exceptions on inexact, overflow, underflow,
+ zero-divide, invalid. Configure option for whether to flush denorms to
+ zero, or except if a denorm is encountered. */
+#if defined(CONFIG_SH64_FPU_DENORM_FLUSH)
+#define FPSCR_INIT 0x00040000
+#else
+#define FPSCR_INIT 0x00000000
+#endif
+
+/* Save the current FP regs */
+void fpsave(struct sh_fpu_hard_struct *fpregs);
+
+/* Initialise the FP state of a task */
+void fpinit(struct sh_fpu_hard_struct *fpregs);
+
+extern struct task_struct *last_task_used_math;
+
+/*
+ * Return saved PC of a blocked thread.
+ */
+#define thread_saved_pc(tsk) (tsk->thread.pc)
+
+extern unsigned long get_wchan(struct task_struct *p);
+
+#define KSTK_EIP(tsk) ((tsk)->thread.pc)
+#define KSTK_ESP(tsk) ((tsk)->thread.sp)
+
+#define cpu_relax() do { } while (0)
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ASM_SH64_PROCESSOR_H */
+
--- /dev/null
+#ifndef __ASM_SH64_PTRACE_H
+#define __ASM_SH64_PTRACE_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/ptrace.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ *
+ */
+
+/*
+ * This struct defines the way the registers are stored on the
+ * kernel stack during a system call or other kernel entry.
+ */
+struct pt_regs {
+ unsigned long long pc;
+ unsigned long long sr;
+ unsigned long long syscall_nr;
+ unsigned long long regs[63];
+ unsigned long long tregs[8];
+ unsigned long long pad[2];
+};
+
+#ifdef __KERNEL__
+#define user_mode(regs) (((regs)->sr & 0x40000000)==0)
+#define instruction_pointer(regs) ((regs)->pc)
+extern void show_regs(struct pt_regs *);
+#endif
+
+#define PTRACE_O_TRACESYSGOOD 0x00000001
+
+#endif /* __ASM_SH64_PTRACE_H */
--- /dev/null
+/*
+ * include/asm-sh/serial.h
+ *
+ * Configuration details for 8250, 16450, 16550, etc. serial ports
+ */
+
+#ifndef _ASM_SERIAL_H
+#define _ASM_SERIAL_H
+
+/*
+ * This assumes you have a 1.8432 MHz clock for your UART.
+ *
+ * It'd be nice if someone built a serial card with a 24.576 MHz
+ * clock, since the 16550A is capable of handling a top speed of 1.5
+ * megabits/second; but this requires the faster clock.
+ */
+#define BASE_BAUD ( 1843200 / 16 )
+
+#define RS_TABLE_SIZE 2
+
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
+
+#define STD_SERIAL_PORT_DEFNS \
+ /* UART CLK PORT IRQ FLAGS */ \
+ { 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */ \
+ { 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS } /* ttyS1 */
+
+#define SERIAL_PORT_DFNS STD_SERIAL_PORT_DEFNS
+
+/* XXX: This should be moved ino irq.h */
+#define irq_cannonicalize(x) (x)
+
+#endif /* _ASM_SERIAL_H */
--- /dev/null
+#ifndef __ASM_SH64_SETUP_H
+#define __ASM_SH64_SETUP_H
+
+#define PARAM ((unsigned char *)empty_zero_page)
+#define MOUNT_ROOT_RDONLY (*(unsigned long *) (PARAM+0x000))
+#define RAMDISK_FLAGS (*(unsigned long *) (PARAM+0x004))
+#define ORIG_ROOT_DEV (*(unsigned long *) (PARAM+0x008))
+#define LOADER_TYPE (*(unsigned long *) (PARAM+0x00c))
+#define INITRD_START (*(unsigned long *) (PARAM+0x010))
+#define INITRD_SIZE (*(unsigned long *) (PARAM+0x014))
+
+#define COMMAND_LINE ((char *) (PARAM+256))
+#define COMMAND_LINE_SIZE 256
+
+#endif /* __ASM_SH64_SETUP_H */
+
--- /dev/null
+#ifndef __ASM_SH64_SHMPARAM_H
+#define __ASM_SH64_SHMPARAM_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/shmparam.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ *
+ */
+
+#include <asm/cache.h>
+
+/* attach addr a multiple of this */
+#define SHMLBA (cpu_data->dcache.sets * L1_CACHE_BYTES)
+
+#endif /* __ASM_SH64_SHMPARAM_H */
--- /dev/null
+#ifndef __ASM_SH64_SIGNAL_H
+#define __ASM_SH64_SIGNAL_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/signal.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ *
+ */
+
+#include <linux/types.h>
+#include <asm/processor.h>
+
+/* Avoid too many header ordering problems. */
+struct siginfo;
+
+#define _NSIG 64
+#define _NSIG_BPW 32
+#define _NSIG_WORDS (_NSIG / _NSIG_BPW)
+
+typedef unsigned long old_sigset_t; /* at least 32 bits */
+
+typedef struct {
+ unsigned long sig[_NSIG_WORDS];
+} sigset_t;
+
+#define SIGHUP 1
+#define SIGINT 2
+#define SIGQUIT 3
+#define SIGILL 4
+#define SIGTRAP 5
+#define SIGABRT 6
+#define SIGIOT 6
+#define SIGBUS 7
+#define SIGFPE 8
+#define SIGKILL 9
+#define SIGUSR1 10
+#define SIGSEGV 11
+#define SIGUSR2 12
+#define SIGPIPE 13
+#define SIGALRM 14
+#define SIGTERM 15
+#define SIGSTKFLT 16
+#define SIGCHLD 17
+#define SIGCONT 18
+#define SIGSTOP 19
+#define SIGTSTP 20
+#define SIGTTIN 21
+#define SIGTTOU 22
+#define SIGURG 23
+#define SIGXCPU 24
+#define SIGXFSZ 25
+#define SIGVTALRM 26
+#define SIGPROF 27
+#define SIGWINCH 28
+#define SIGIO 29
+#define SIGPOLL SIGIO
+/*
+#define SIGLOST 29
+*/
+#define SIGPWR 30
+#define SIGSYS 31
+#define SIGUNUSED 31
+
+/* These should not be considered constants from userland. */
+#define SIGRTMIN 32
+#define SIGRTMAX (_NSIG-1)
+
+/*
+ * SA_FLAGS values:
+ *
+ * SA_ONSTACK indicates that a registered stack_t will be used.
+ * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the
+ * SA_RESTART flag to get restarting signals (which were the default long ago)
+ * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop.
+ * SA_RESETHAND clears the handler when the signal is delivered.
+ * SA_NOCLDWAIT flag on SIGCHLD to inhibit zombies.
+ * SA_NODEFER prevents the current signal from being masked in the handler.
+ *
+ * SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single
+ * Unix names RESETHAND and NODEFER respectively.
+ */
+#define SA_NOCLDSTOP 0x00000001
+#define SA_NOCLDWAIT 0x00000002 /* not supported yet */
+#define SA_SIGINFO 0x00000004
+#define SA_ONSTACK 0x08000000
+#define SA_RESTART 0x10000000
+#define SA_NODEFER 0x40000000
+#define SA_RESETHAND 0x80000000
+
+#define SA_NOMASK SA_NODEFER
+#define SA_ONESHOT SA_RESETHAND
+#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */
+
+#define SA_RESTORER 0x04000000
+
+/*
+ * sigaltstack controls
+ */
+#define SS_ONSTACK 1
+#define SS_DISABLE 2
+
+#define MINSIGSTKSZ 2048
+#define SIGSTKSZ THREAD_SIZE
+
+#ifdef __KERNEL__
+
+/*
+ * These values of sa_flags are used only by the kernel as part of the
+ * irq handling routines.
+ *
+ * SA_INTERRUPT is also used by the irq handling routines.
+ * SA_SHIRQ is for shared interrupt support on PCI and EISA.
+ */
+#define SA_PROBE SA_ONESHOT
+#define SA_SAMPLE_RANDOM SA_RESTART
+#define SA_SHIRQ 0x04000000
+#endif
+
+#define SIG_BLOCK 0 /* for blocking signals */
+#define SIG_UNBLOCK 1 /* for unblocking signals */
+#define SIG_SETMASK 2 /* for setting the signal mask */
+
+/* Type of a signal handler. */
+typedef void (*__sighandler_t)(int);
+
+#define SIG_DFL ((__sighandler_t)0) /* default signal handling */
+#define SIG_IGN ((__sighandler_t)1) /* ignore signal */
+#define SIG_ERR ((__sighandler_t)-1) /* error return from signal */
+
+#ifdef __KERNEL__
+struct old_sigaction {
+ __sighandler_t sa_handler;
+ old_sigset_t sa_mask;
+ unsigned long sa_flags;
+ void (*sa_restorer)(void);
+};
+
+struct sigaction {
+ __sighandler_t sa_handler;
+ unsigned long sa_flags;
+ void (*sa_restorer)(void);
+ sigset_t sa_mask; /* mask last for extensibility */
+};
+
+struct k_sigaction {
+ struct sigaction sa;
+};
+#else
+/* Here we must cater to libcs that poke about in kernel headers. */
+
+struct sigaction {
+ union {
+ __sighandler_t _sa_handler;
+ void (*_sa_sigaction)(int, struct siginfo *, void *);
+ } _u;
+ sigset_t sa_mask;
+ unsigned long sa_flags;
+ void (*sa_restorer)(void);
+};
+
+#define sa_handler _u._sa_handler
+#define sa_sigaction _u._sa_sigaction
+
+#endif /* __KERNEL__ */
+
+typedef struct sigaltstack {
+ void *ss_sp;
+ int ss_flags;
+ size_t ss_size;
+} stack_t;
+
+#ifdef __KERNEL__
+#include <asm/sigcontext.h>
+
+#define sigmask(sig) (1UL << ((sig) - 1))
+#define ptrace_signal_deliver(regs, cookie) do { } while (0)
+
+#endif /* __KERNEL__ */
+
+#endif /* __ASM_SH64_SIGNAL_H */
--- /dev/null
+#ifndef __ASM_SH64_SMPLOCK_H
+#define __ASM_SH64_SMPLOCK_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/smplock.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ *
+ */
+
+#include <linux/config.h>
+
+#ifndef CONFIG_SMP
+
+#define lock_kernel() do { } while(0)
+#define unlock_kernel() do { } while(0)
+#define release_kernel_lock(task, cpu, depth) ((depth) = 1)
+#define reacquire_kernel_lock(task, cpu, depth) do { } while(0)
+
+#else
+
+#error "We do not support SMP on SH64 yet"
+/*
+ * Default SMP lock implementation
+ */
+
+#include <linux/interrupt.h>
+#include <asm/spinlock.h>
+
+extern spinlock_t kernel_flag;
+
+/*
+ * Getting the big kernel lock.
+ *
+ * This cannot happen asynchronously,
+ * so we only need to worry about other
+ * CPU's.
+ */
+extern __inline__ void lock_kernel(void)
+{
+ if (!++current->lock_depth)
+ spin_lock(&kernel_flag);
+}
+
+extern __inline__ void unlock_kernel(void)
+{
+ if (--current->lock_depth < 0)
+ spin_unlock(&kernel_flag);
+}
+
+/*
+ * Release global kernel lock and global interrupt lock
+ */
+#define release_kernel_lock(task, cpu) \
+do { \
+ if (task->lock_depth >= 0) \
+ spin_unlock(&kernel_flag); \
+ release_irqlock(cpu); \
+ __sti(); \
+} while (0)
+
+/*
+ * Re-acquire the kernel lock
+ */
+#define reacquire_kernel_lock(task) \
+do { \
+ if (task->lock_depth >= 0) \
+ spin_lock(&kernel_flag); \
+} while (0)
+
+#endif /* CONFIG_SMP */
+
+#endif /* __ASM_SH64_SMPLOCK_H */
--- /dev/null
+#ifndef __ASM_SH_SOFTIRQ_H
+#define __ASM_SH_SOFTIRQ_H
+
+#include <asm/atomic.h>
+#include <asm/hardirq.h>
+
+#define local_bh_disable() \
+do { \
+ local_bh_count(smp_processor_id())++; \
+ barrier(); \
+} while (0)
+
+#define __local_bh_enable() \
+do { \
+ barrier(); \
+ local_bh_count(smp_processor_id())--; \
+} while (0)
+
+#define local_bh_enable() \
+do { \
+ barrier(); \
+ if (!--local_bh_count(smp_processor_id()) \
+ && softirq_pending(smp_processor_id())) { \
+ do_softirq(); \
+ } \
+} while (0)
+
+#define in_softirq() (local_bh_count(smp_processor_id()) != 0)
+
+#endif /* __ASM_SH_SOFTIRQ_H */
--- /dev/null
+#ifndef __ASM_SH64_SYSTEM_H
+#define __ASM_SH64_SYSTEM_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/system.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003 Paul Mundt
+ * Copyright (C) 2004 Richard Curnow
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <asm/registers.h>
+#include <asm/processor.h>
+
+/*
+ * switch_to() should switch tasks to task nr n, first
+ */
+
+typedef struct {
+ unsigned long seg;
+} mm_segment_t;
+
+extern struct task_struct *sh64_switch_to(struct task_struct *prev,
+ struct thread_struct *prev_thread,
+ struct task_struct *next,
+ struct thread_struct *next_thread);
+
+#define switch_to(prev,next,last) \
+ do {\
+ if (last_task_used_math != next) {\
+ struct pt_regs *regs = next->thread.uregs;\
+ if (regs) regs->sr |= SR_FD;\
+ }\
+ last = sh64_switch_to(prev, &prev->thread, next, &next->thread);\
+ } while(0)
+
+#define nop() __asm__ __volatile__ ("nop")
+
+#define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
+
+#define tas(ptr) (xchg((ptr), 1))
+
+extern void __xchg_called_with_bad_pointer(void);
+
+#define mb() __asm__ __volatile__ ("synco": : :"memory")
+#define rmb() mb()
+#define wmb() __asm__ __volatile__ ("synco": : :"memory")
+#define read_barrier_depends() do { } while (0)
+
+#ifdef CONFIG_SMP
+#define smp_mb() mb()
+#define smp_rmb() rmb()
+#define smp_wmb() wmb()
+#define smp_read_barrier_depends() read_barrier_depends()
+#else
+#define smp_mb() barrier()
+#define smp_rmb() barrier()
+#define smp_wmb() barrier()
+#define smp_read_barrier_depends() do { } while (0)
+#endif /* CONFIG_SMP */
+
+#define set_rmb(var, value) do { xchg(&var, value); } while (0)
+#define set_mb(var, value) set_rmb(var, value)
+#define set_wmb(var, value) do { var = value; wmb(); } while (0)
+
+/* Interrupt Control */
+#ifndef HARD_CLI
+#define SR_MASK_L 0x000000f0L
+#define SR_MASK_LL 0x00000000000000f0LL
+#else
+#define SR_MASK_L 0x10000000L
+#define SR_MASK_LL 0x0000000010000000LL
+#endif
+
+static __inline__ void local_irq_enable(void)
+{
+ /* cli/sti based on SR.BL */
+ unsigned long long __dummy0, __dummy1=~SR_MASK_LL;
+
+ __asm__ __volatile__("getcon " __SR ", %0\n\t"
+ "and %0, %1, %0\n\t"
+ "putcon %0, " __SR "\n\t"
+ : "=&r" (__dummy0)
+ : "r" (__dummy1));
+}
+
+static __inline__ void local_irq_disable(void)
+{
+ /* cli/sti based on SR.BL */
+ unsigned long long __dummy0, __dummy1=SR_MASK_LL;
+ __asm__ __volatile__("getcon " __SR ", %0\n\t"
+ "or %0, %1, %0\n\t"
+ "putcon %0, " __SR "\n\t"
+ : "=&r" (__dummy0)
+ : "r" (__dummy1));
+}
+
+#define local_save_flags(x) \
+(__extension__ ({ unsigned long long __dummy=SR_MASK_LL; \
+ __asm__ __volatile__( \
+ "getcon " __SR ", %0\n\t" \
+ "and %0, %1, %0" \
+ : "=&r" (x) \
+ : "r" (__dummy));}))
+
+#define local_irq_save(x) \
+(__extension__ ({ unsigned long long __d2=SR_MASK_LL, __d1; \
+ __asm__ __volatile__( \
+ "getcon " __SR ", %1\n\t" \
+ "or %1, r63, %0\n\t" \
+ "or %1, %2, %1\n\t" \
+ "putcon %1, " __SR "\n\t" \
+ "and %0, %2, %0" \
+ : "=&r" (x), "=&r" (__d1) \
+ : "r" (__d2));}));
+
+#define local_irq_restore(x) do { \
+ if ( ((x) & SR_MASK_L) == 0 ) /* dropping to 0 ? */ \
+ local_irq_enable(); /* yes...re-enable */ \
+} while (0)
+
+#define irqs_disabled() \
+({ \
+ unsigned long flags; \
+ local_save_flags(flags); \
+ (flags != 0); \
+})
+
+extern __inline__ unsigned long xchg_u32(volatile int * m, unsigned long val)
+{
+ unsigned long flags, retval;
+
+ local_irq_save(flags);
+ retval = *m;
+ *m = val;
+ local_irq_restore(flags);
+ return retval;
+}
+
+extern __inline__ unsigned long xchg_u8(volatile unsigned char * m, unsigned long val)
+{
+ unsigned long flags, retval;
+
+ local_irq_save(flags);
+ retval = *m;
+ *m = val & 0xff;
+ local_irq_restore(flags);
+ return retval;
+}
+
+static __inline__ unsigned long __xchg(unsigned long x, volatile void * ptr, int size)
+{
+ switch (size) {
+ case 4:
+ return xchg_u32(ptr, x);
+ break;
+ case 1:
+ return xchg_u8(ptr, x);
+ break;
+ }
+ __xchg_called_with_bad_pointer();
+ return x;
+}
+
+/* XXX
+ * disable hlt during certain critical i/o operations
+ */
+#define HAVE_DISABLE_HLT
+void disable_hlt(void);
+void enable_hlt(void);
+
+
+#define smp_mb() barrier()
+#define smp_rmb() barrier()
+#define smp_wmb() barrier()
+
+#ifdef CONFIG_SH_ALPHANUMERIC
+/* This is only used for debugging. */
+extern void print_seg(char *file,int line);
+#define PLS() print_seg(__FILE__,__LINE__)
+#else /* CONFIG_SH_ALPHANUMERIC */
+#define PLS()
+#endif /* CONFIG_SH_ALPHANUMERIC */
+
+#define PL() printk("@ <%s,%s:%d>\n",__FILE__,__FUNCTION__,__LINE__)
+
+#endif /* __ASM_SH64_SYSTEM_H */
--- /dev/null
+#ifndef __ASM_SH64_TIMEX_H
+#define __ASM_SH64_TIMEX_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/timex.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003 Paul Mundt
+ *
+ * sh-5 architecture timex specifications
+ *
+ */
+
+#define CLOCK_TICK_RATE 1193180 /* Underlying HZ */
+#define CLOCK_TICK_FACTOR 20 /* Factor of both 1000000 and CLOCK_TICK_RATE */
+#define FINETUNE ((((((long)LATCH * HZ - CLOCK_TICK_RATE) << SHIFT_HZ) * \
+ (1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR)) \
+ << (SHIFT_SCALE-SHIFT_HZ)) / HZ)
+
+typedef unsigned long cycles_t;
+
+extern cycles_t cacheflush_time;
+
+static __inline__ cycles_t get_cycles (void)
+{
+ return 0;
+}
+
+#define vxtime_lock() do {} while (0)
+#define vxtime_unlock() do {} while (0)
+
+#endif /* __ASM_SH64_TIMEX_H */
return retval; \
})
-#define __copy_to_user_inatomic __copy_to_user
-#define __copy_from_user_inatomic __copy_from_user
-
/* XXX: Not sure it works well..
should be such that: 4byte clear and the rest. */
extern __kernel_size_t __clear_user(void *addr, __kernel_size_t size);
--- /dev/null
+#ifndef __ASM_SH64_UNISTD_H
+#define __ASM_SH64_UNISTD_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/unistd.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ * Copyright (C) 2003 Paul Mundt
+ * Copyright (C) 2004 Sean McGoogan
+ *
+ * This file contains the system call numbers.
+ *
+ */
+
+#define __NR_setup 0 /* used only by init, to get system going */
+#define __NR_exit 1
+#define __NR_fork 2
+#define __NR_read 3
+#define __NR_write 4
+#define __NR_open 5
+#define __NR_close 6
+#define __NR_waitpid 7
+#define __NR_creat 8
+#define __NR_link 9
+#define __NR_unlink 10
+#define __NR_execve 11
+#define __NR_chdir 12
+#define __NR_time 13
+#define __NR_mknod 14
+#define __NR_chmod 15
+#define __NR_lchown 16
+#define __NR_break 17
+#define __NR_oldstat 18
+#define __NR_lseek 19
+#define __NR_getpid 20
+#define __NR_mount 21
+#define __NR_umount 22
+#define __NR_setuid 23
+#define __NR_getuid 24
+#define __NR_stime 25
+#define __NR_ptrace 26
+#define __NR_alarm 27
+#define __NR_oldfstat 28
+#define __NR_pause 29
+#define __NR_utime 30
+#define __NR_stty 31
+#define __NR_gtty 32
+#define __NR_access 33
+#define __NR_nice 34
+#define __NR_ftime 35
+#define __NR_sync 36
+#define __NR_kill 37
+#define __NR_rename 38
+#define __NR_mkdir 39
+#define __NR_rmdir 40
+#define __NR_dup 41
+#define __NR_pipe 42
+#define __NR_times 43
+#define __NR_prof 44
+#define __NR_brk 45
+#define __NR_setgid 46
+#define __NR_getgid 47
+#define __NR_signal 48
+#define __NR_geteuid 49
+#define __NR_getegid 50
+#define __NR_acct 51
+#define __NR_umount2 52
+#define __NR_lock 53
+#define __NR_ioctl 54
+#define __NR_fcntl 55
+#define __NR_mpx 56
+#define __NR_setpgid 57
+#define __NR_ulimit 58
+#define __NR_oldolduname 59
+#define __NR_umask 60
+#define __NR_chroot 61
+#define __NR_ustat 62
+#define __NR_dup2 63
+#define __NR_getppid 64
+#define __NR_getpgrp 65
+#define __NR_setsid 66
+#define __NR_sigaction 67
+#define __NR_sgetmask 68
+#define __NR_ssetmask 69
+#define __NR_setreuid 70
+#define __NR_setregid 71
+#define __NR_sigsuspend 72
+#define __NR_sigpending 73
+#define __NR_sethostname 74
+#define __NR_setrlimit 75
+#define __NR_getrlimit 76 /* Back compatible 2Gig limited rlimit */
+#define __NR_getrusage 77
+#define __NR_gettimeofday 78
+#define __NR_settimeofday 79
+#define __NR_getgroups 80
+#define __NR_setgroups 81
+#define __NR_select 82
+#define __NR_symlink 83
+#define __NR_oldlstat 84
+#define __NR_readlink 85
+#define __NR_uselib 86
+#define __NR_swapon 87
+#define __NR_reboot 88
+#define __NR_readdir 89
+#define __NR_mmap 90
+#define __NR_munmap 91
+#define __NR_truncate 92
+#define __NR_ftruncate 93
+#define __NR_fchmod 94
+#define __NR_fchown 95
+#define __NR_getpriority 96
+#define __NR_setpriority 97
+#define __NR_profil 98
+#define __NR_statfs 99
+#define __NR_fstatfs 100
+#define __NR_ioperm 101
+#define __NR_socketcall 102 /* old implementation of socket systemcall */
+#define __NR_syslog 103
+#define __NR_setitimer 104
+#define __NR_getitimer 105
+#define __NR_stat 106
+#define __NR_lstat 107
+#define __NR_fstat 108
+#define __NR_olduname 109
+#define __NR_iopl 110
+#define __NR_vhangup 111
+#define __NR_idle 112
+#define __NR_vm86old 113
+#define __NR_wait4 114
+#define __NR_swapoff 115
+#define __NR_sysinfo 116
+#define __NR_ipc 117
+#define __NR_fsync 118
+#define __NR_sigreturn 119
+#define __NR_clone 120
+#define __NR_setdomainname 121
+#define __NR_uname 122
+#define __NR_modify_ldt 123
+#define __NR_adjtimex 124
+#define __NR_mprotect 125
+#define __NR_sigprocmask 126
+#define __NR_create_module 127
+#define __NR_init_module 128
+#define __NR_delete_module 129
+#define __NR_get_kernel_syms 130
+#define __NR_quotactl 131
+#define __NR_getpgid 132
+#define __NR_fchdir 133
+#define __NR_bdflush 134
+#define __NR_sysfs 135
+#define __NR_personality 136
+#define __NR_afs_syscall 137 /* Syscall for Andrew File System */
+#define __NR_setfsuid 138
+#define __NR_setfsgid 139
+#define __NR__llseek 140
+#define __NR_getdents 141
+#define __NR__newselect 142
+#define __NR_flock 143
+#define __NR_msync 144
+#define __NR_readv 145
+#define __NR_writev 146
+#define __NR_getsid 147
+#define __NR_fdatasync 148
+#define __NR__sysctl 149
+#define __NR_mlock 150
+#define __NR_munlock 151
+#define __NR_mlockall 152
+#define __NR_munlockall 153
+#define __NR_sched_setparam 154
+#define __NR_sched_getparam 155
+#define __NR_sched_setscheduler 156
+#define __NR_sched_getscheduler 157
+#define __NR_sched_yield 158
+#define __NR_sched_get_priority_max 159
+#define __NR_sched_get_priority_min 160
+#define __NR_sched_rr_get_interval 161
+#define __NR_nanosleep 162
+#define __NR_mremap 163
+#define __NR_setresuid 164
+#define __NR_getresuid 165
+#define __NR_vm86 166
+#define __NR_query_module 167
+#define __NR_poll 168
+#define __NR_nfsservctl 169
+#define __NR_setresgid 170
+#define __NR_getresgid 171
+#define __NR_prctl 172
+#define __NR_rt_sigreturn 173
+#define __NR_rt_sigaction 174
+#define __NR_rt_sigprocmask 175
+#define __NR_rt_sigpending 176
+#define __NR_rt_sigtimedwait 177
+#define __NR_rt_sigqueueinfo 178
+#define __NR_rt_sigsuspend 179
+#define __NR_pread 180
+#define __NR_pwrite 181
+#define __NR_chown 182
+#define __NR_getcwd 183
+#define __NR_capget 184
+#define __NR_capset 185
+#define __NR_sigaltstack 186
+#define __NR_sendfile 187
+#define __NR_streams1 188 /* some people actually want it */
+#define __NR_streams2 189 /* some people actually want it */
+#define __NR_vfork 190
+#define __NR_ugetrlimit 191 /* SuS compliant getrlimit */
+#define __NR_mmap2 192
+#define __NR_truncate64 193
+#define __NR_ftruncate64 194
+#define __NR_stat64 195
+#define __NR_lstat64 196
+#define __NR_fstat64 197
+#define __NR_lchown32 198
+#define __NR_getuid32 199
+#define __NR_getgid32 200
+#define __NR_geteuid32 201
+#define __NR_getegid32 202
+#define __NR_setreuid32 203
+#define __NR_setregid32 204
+#define __NR_getgroups32 205
+#define __NR_setgroups32 206
+#define __NR_fchown32 207
+#define __NR_setresuid32 208
+#define __NR_getresuid32 209
+#define __NR_setresgid32 210
+#define __NR_getresgid32 211
+#define __NR_chown32 212
+#define __NR_setuid32 213
+#define __NR_setgid32 214
+#define __NR_setfsuid32 215
+#define __NR_setfsgid32 216
+#define __NR_pivot_root 217
+#define __NR_mincore 218
+#define __NR_madvise 219
+
+/* Non-multiplexed socket family */
+#define __NR_socket 220
+#define __NR_bind 221
+#define __NR_connect 222
+#define __NR_listen 223
+#define __NR_accept 224
+#define __NR_getsockname 225
+#define __NR_getpeername 226
+#define __NR_socketpair 227
+#define __NR_send 228
+#define __NR_sendto 229
+#define __NR_recv 230
+#define __NR_recvfrom 231
+#define __NR_shutdown 232
+#define __NR_setsockopt 233
+#define __NR_getsockopt 234
+#define __NR_sendmsg 235
+#define __NR_recvmsg 236
+
+/* Non-multiplexed IPC family */
+#define __NR_semop 237
+#define __NR_semget 238
+#define __NR_semctl 239
+#define __NR_msgsnd 240
+#define __NR_msgrcv 241
+#define __NR_msgget 242
+#define __NR_msgctl 243
+#if 0
+#define __NR_shmatcall 244
+#endif
+#define __NR_shmdt 245
+#define __NR_shmget 246
+#define __NR_shmctl 247
+
+#define __NR_getdents64 248
+#define __NR_fcntl64 249
+/* 223 is unused */
+#define __NR_gettid 252
+#define __NR_readahead 253
+#define __NR_setxattr 254
+#define __NR_lsetxattr 255
+#define __NR_fsetxattr 256
+#define __NR_getxattr 257
+#define __NR_lgetxattr 258
+#define __NR_fgetxattr 269
+#define __NR_listxattr 260
+#define __NR_llistxattr 261
+#define __NR_flistxattr 262
+#define __NR_removexattr 263
+#define __NR_lremovexattr 264
+#define __NR_fremovexattr 265
+#define __NR_tkill 266
+#define __NR_sendfile64 267
+#define __NR_futex 268
+#define __NR_sched_setaffinity 269
+#define __NR_sched_getaffinity 270
+#define __NR_set_thread_area 271
+#define __NR_get_thread_area 272
+#define __NR_io_setup 273
+#define __NR_io_destroy 274
+#define __NR_io_getevents 275
+#define __NR_io_submit 276
+#define __NR_io_cancel 277
+#define __NR_fadvise64 278
+#define __NR_exit_group 280
+
+#define __NR_lookup_dcookie 281
+#define __NR_epoll_create 282
+#define __NR_epoll_ctl 283
+#define __NR_epoll_wait 284
+#define __NR_remap_file_pages 285
+#define __NR_set_tid_address 286
+#define __NR_timer_create 287
+#define __NR_timer_settime (__NR_timer_create+1)
+#define __NR_timer_gettime (__NR_timer_create+2)
+#define __NR_timer_getoverrun (__NR_timer_create+3)
+#define __NR_timer_delete (__NR_timer_create+4)
+#define __NR_clock_settime (__NR_timer_create+5)
+#define __NR_clock_gettime (__NR_timer_create+6)
+#define __NR_clock_getres (__NR_timer_create+7)
+#define __NR_clock_nanosleep (__NR_timer_create+8)
+#define __NR_statfs64 296
+#define __NR_fstatfs64 297
+#define __NR_tgkill 298
+#define __NR_utimes 299
+#define __NR_fadvise64_64 300
+#define __NR_vserver 301
+#define __NR_mbind 302
+#define __NR_get_mempolicy 303
+#define __NR_set_mempolicy 304
+#define __NR_mq_open 305
+#define __NR_mq_unlink (__NR_mq_open+1)
+#define __NR_mq_timedsend (__NR_mq_open+2)
+#define __NR_mq_timedreceive (__NR_mq_open+3)
+#define __NR_mq_notify (__NR_mq_open+4)
+#define __NR_mq_getsetattr (__NR_mq_open+5)
+
+#define NR_syscalls 311
+
+/* user-visible error numbers are in the range -1 - -125: see <asm-sh64/errno.h> */
+
+#define __syscall_return(type, res) \
+do { \
+ /* Note: when returning from kernel the return value is in r9 \
+ ** This prevents conflicts between return value and arg1 \
+ ** when dispatching signal handler, in other words makes \
+ ** life easier in the system call epilogue (see entry.S) \
+ */ \
+ register unsigned long __sr2 __asm__ ("r2") = res; \
+ if ((unsigned long)(res) >= (unsigned long)(-125)) { \
+ errno = -(res); \
+ __sr2 = -1; \
+ } \
+ return (type) (__sr2); \
+} while (0)
+
+/* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */
+
+#define _syscall0(type,name) \
+type name(void) \
+{ \
+register unsigned long __sc0 __asm__ ("r9") = ((0x10 << 16) | __NR_##name); \
+__asm__ __volatile__ ("trapa %1 !\t\t\t" #name "()" \
+ : "=r" (__sc0) \
+ : "r" (__sc0) ); \
+__syscall_return(type,__sc0); \
+}
+
+ /*
+ * The apparent spurious "dummy" assembler comment is *needed*,
+ * as without it, the compiler treats the arg<n> variables
+ * as no longer live just before the asm. The compiler can
+ * then optimize the storage into any registers it wishes.
+ * The additional dummy statement forces the compiler to put
+ * the arguments into the correct registers before the TRAPA.
+ */
+#define _syscall1(type,name,type1,arg1) \
+type name(type1 arg1) \
+{ \
+register unsigned long __sc0 __asm__ ("r9") = ((0x11 << 16) | __NR_##name); \
+register unsigned long __sc2 __asm__ ("r2") = (unsigned long) arg1; \
+__asm__ __volatile__ ("trapa %1 !\t\t\t" #name "(%2)" \
+ : "=r" (__sc0) \
+ : "r" (__sc0), "r" (__sc2)); \
+__asm__ __volatile__ ("!dummy %0 %1" \
+ : \
+ : "r" (__sc0), "r" (__sc2)); \
+__syscall_return(type,__sc0); \
+}
+
+#define _syscall2(type,name,type1,arg1,type2,arg2) \
+type name(type1 arg1,type2 arg2) \
+{ \
+register unsigned long __sc0 __asm__ ("r9") = ((0x12 << 16) | __NR_##name); \
+register unsigned long __sc2 __asm__ ("r2") = (unsigned long) arg1; \
+register unsigned long __sc3 __asm__ ("r3") = (unsigned long) arg2; \
+__asm__ __volatile__ ("trapa %1 !\t\t\t" #name "(%2,%3)" \
+ : "=r" (__sc0) \
+ : "r" (__sc0), "r" (__sc2), "r" (__sc3) ); \
+__asm__ __volatile__ ("!dummy %0 %1 %2" \
+ : \
+ : "r" (__sc0), "r" (__sc2), "r" (__sc3) ); \
+__syscall_return(type,__sc0); \
+}
+
+#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
+type name(type1 arg1,type2 arg2,type3 arg3) \
+{ \
+register unsigned long __sc0 __asm__ ("r9") = ((0x13 << 16) | __NR_##name); \
+register unsigned long __sc2 __asm__ ("r2") = (unsigned long) arg1; \
+register unsigned long __sc3 __asm__ ("r3") = (unsigned long) arg2; \
+register unsigned long __sc4 __asm__ ("r4") = (unsigned long) arg3; \
+__asm__ __volatile__ ("trapa %1 !\t\t\t" #name "(%2,%3,%4)" \
+ : "=r" (__sc0) \
+ : "r" (__sc0), "r" (__sc2), "r" (__sc3), "r" (__sc4) ); \
+__asm__ __volatile__ ("!dummy %0 %1 %2 %3" \
+ : \
+ : "r" (__sc0), "r" (__sc2), "r" (__sc3), "r" (__sc4) ); \
+__syscall_return(type,__sc0); \
+}
+
+#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \
+type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4) \
+{ \
+register unsigned long __sc0 __asm__ ("r9") = ((0x14 << 16) | __NR_##name); \
+register unsigned long __sc2 __asm__ ("r2") = (unsigned long) arg1; \
+register unsigned long __sc3 __asm__ ("r3") = (unsigned long) arg2; \
+register unsigned long __sc4 __asm__ ("r4") = (unsigned long) arg3; \
+register unsigned long __sc5 __asm__ ("r5") = (unsigned long) arg4; \
+__asm__ __volatile__ ("trapa %1 !\t\t\t" #name "(%2,%3,%4,%5)" \
+ : "=r" (__sc0) \
+ : "r" (__sc0), "r" (__sc2), "r" (__sc3), "r" (__sc4), "r" (__sc5) );\
+__asm__ __volatile__ ("!dummy %0 %1 %2 %3 %4" \
+ : \
+ : "r" (__sc0), "r" (__sc2), "r" (__sc3), "r" (__sc4), "r" (__sc5) );\
+__syscall_return(type,__sc0); \
+}
+
+#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5) \
+type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \
+{ \
+register unsigned long __sc0 __asm__ ("r9") = ((0x15 << 16) | __NR_##name); \
+register unsigned long __sc2 __asm__ ("r2") = (unsigned long) arg1; \
+register unsigned long __sc3 __asm__ ("r3") = (unsigned long) arg2; \
+register unsigned long __sc4 __asm__ ("r4") = (unsigned long) arg3; \
+register unsigned long __sc5 __asm__ ("r5") = (unsigned long) arg4; \
+register unsigned long __sc6 __asm__ ("r6") = (unsigned long) arg5; \
+__asm__ __volatile__ ("trapa %1 !\t\t\t" #name "(%2,%3,%4,%5,%6)" \
+ : "=r" (__sc0) \
+ : "r" (__sc0), "r" (__sc2), "r" (__sc3), "r" (__sc4), "r" (__sc5), \
+ "r" (__sc6)); \
+__asm__ __volatile__ ("!dummy %0 %1 %2 %3 %4 %5" \
+ : \
+ : "r" (__sc0), "r" (__sc2), "r" (__sc3), "r" (__sc4), "r" (__sc5), \
+ "r" (__sc6)); \
+__syscall_return(type,__sc0); \
+}
+
+#define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5, type6, arg6) \
+type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6) \
+{ \
+register unsigned long __sc0 __asm__ ("r9") = ((0x16 << 16) | __NR_##name); \
+register unsigned long __sc2 __asm__ ("r2") = (unsigned long) arg1; \
+register unsigned long __sc3 __asm__ ("r3") = (unsigned long) arg2; \
+register unsigned long __sc4 __asm__ ("r4") = (unsigned long) arg3; \
+register unsigned long __sc5 __asm__ ("r5") = (unsigned long) arg4; \
+register unsigned long __sc6 __asm__ ("r6") = (unsigned long) arg5; \
+register unsigned long __sc7 __asm__ ("r7") = (unsigned long) arg6; \
+__asm__ __volatile__ ("trapa %1 !\t\t\t" #name "(%2,%3,%4,%5,%6,%7)" \
+ : "=r" (__sc0) \
+ : "r" (__sc0), "r" (__sc2), "r" (__sc3), "r" (__sc4), "r" (__sc5), \
+ "r" (__sc6), "r" (__sc7)); \
+__asm__ __volatile__ ("!dummy %0 %1 %2 %3 %4 %5 %6" \
+ : \
+ : "r" (__sc0), "r" (__sc2), "r" (__sc3), "r" (__sc4), "r" (__sc5), \
+ "r" (__sc6), "r" (__sc7)); \
+__syscall_return(type,__sc0); \
+}
+
+#ifdef __KERNEL__
+#define __ARCH_WANT_IPC_PARSE_VERSION
+#define __ARCH_WANT_OLD_READDIR
+#define __ARCH_WANT_OLD_STAT
+#define __ARCH_WANT_STAT64
+#define __ARCH_WANT_SYS_ALARM
+#define __ARCH_WANT_SYS_GETHOSTNAME
+#define __ARCH_WANT_SYS_PAUSE
+#define __ARCH_WANT_SYS_SGETMASK
+#define __ARCH_WANT_SYS_SIGNAL
+#define __ARCH_WANT_SYS_TIME
+#define __ARCH_WANT_SYS_UTIME
+#define __ARCH_WANT_SYS_WAITPID
+#define __ARCH_WANT_SYS_SOCKETCALL
+#define __ARCH_WANT_SYS_FADVISE64
+#define __ARCH_WANT_SYS_GETPGRP
+#define __ARCH_WANT_SYS_LLSEEK
+#define __ARCH_WANT_SYS_NICE
+#define __ARCH_WANT_SYS_OLD_GETRLIMIT
+#define __ARCH_WANT_SYS_OLDUMOUNT
+#define __ARCH_WANT_SYS_SIGPENDING
+#define __ARCH_WANT_SYS_SIGPROCMASK
+#define __ARCH_WANT_SYS_RT_SIGACTION
+#endif
+
+#ifdef __KERNEL_SYSCALLS__
+
+/* Copy from sh */
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <asm/ptrace.h>
+
+/*
+ * we need this inline - forking from kernel space will result
+ * in NO COPY ON WRITE (!!!), until an execve is executed. This
+ * is no problem, but for the stack. This is handled by not letting
+ * main() use the stack at all after fork(). Thus, no function
+ * calls - which means inline code for fork too, as otherwise we
+ * would use the stack upon exit from 'fork()'.
+ *
+ * Actually only pause and fork are needed inline, so that there
+ * won't be any messing with the stack from main(), but we define
+ * some others too.
+ */
+#define __NR__exit __NR_exit
+static inline _syscall0(int,pause)
+static inline _syscall1(int,setup,int,magic)
+static inline _syscall0(int,sync)
+static inline _syscall0(pid_t,setsid)
+static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)
+static inline _syscall3(int,read,int,fd,char *,buf,off_t,count)
+static inline _syscall3(off_t,lseek,int,fd,off_t,offset,int,count)
+static inline _syscall1(int,dup,int,fd)
+static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp)
+static inline _syscall3(int,open,const char *,file,int,flag,int,mode)
+static inline _syscall1(int,close,int,fd)
+static inline _syscall1(int,_exit,int,exitcode)
+static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
+static inline _syscall1(int,delete_module,const char *,name)
+
+static inline pid_t wait(int * wait_stat)
+{
+ return waitpid(-1,wait_stat,0);
+}
+#endif
+
+/*
+ * "Conditional" syscalls
+ *
+ * What we want is __attribute__((weak,alias("sys_ni_syscall"))),
+ * but it doesn't work on all toolchains, so we just do it by hand
+ */
+#ifndef cond_syscall
+#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall");
+#endif
+
+#endif /* __ASM_SH64_UNISTD_H */
--- /dev/null
+#ifndef __ASM_SH64_USER_H
+#define __ASM_SH64_USER_H
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * include/asm-sh64/user.h
+ *
+ * Copyright (C) 2000, 2001 Paolo Alberelli
+ *
+ */
+
+#include <linux/types.h>
+#include <asm/processor.h>
+#include <asm/ptrace.h>
+#include <asm/page.h>
+
+/*
+ * Core file format: The core file is written in such a way that gdb
+ * can understand it and provide useful information to the user (under
+ * linux we use the `trad-core' bfd). The file contents are as follows:
+ *
+ * upage: 1 page consisting of a user struct that tells gdb
+ * what is present in the file. Directly after this is a
+ * copy of the task_struct, which is currently not used by gdb,
+ * but it may come in handy at some point. All of the registers
+ * are stored as part of the upage. The upage should always be
+ * only one page long.
+ * data: The data segment follows next. We use current->end_text to
+ * current->brk to pick up all of the user variables, plus any memory
+ * that may have been sbrk'ed. No attempt is made to determine if a
+ * page is demand-zero or if a page is totally unused, we just cover
+ * the entire range. All of the addresses are rounded in such a way
+ * that an integral number of pages is written.
+ * stack: We need the stack information in order to get a meaningful
+ * backtrace. We need to write the data from usp to
+ * current->start_stack, so we round each of these in order to be able
+ * to write an integer number of pages.
+ */
+
+struct user_fpu_struct {
+ unsigned long long fp_regs[32];
+ unsigned int fpscr;
+};
+
+struct user {
+ struct pt_regs regs; /* entire machine state */
+ struct user_fpu_struct fpu; /* Math Co-processor registers */
+ int u_fpvalid; /* True if math co-processor being used */
+ size_t u_tsize; /* text size (pages) */
+ size_t u_dsize; /* data size (pages) */
+ size_t u_ssize; /* stack size (pages) */
+ unsigned long start_code; /* text starting address */
+ unsigned long start_data; /* data starting address */
+ unsigned long start_stack; /* stack starting address */
+ long int signal; /* signal causing core dump */
+ struct regs * u_ar0; /* help gdb find registers */
+ struct user_fpu_struct* u_fpstate; /* Math Co-processor pointer */
+ unsigned long magic; /* identifies a core file */
+ char u_comm[32]; /* user command name */
+};
+
+#define NBPG PAGE_SIZE
+#define UPAGES 1
+#define HOST_TEXT_START_ADDR (u.start_code)
+#define HOST_DATA_START_ADDR (u.start_data)
+#define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG)
+
+#endif /* __ASM_SH64_USER_H */
#ifndef _SPARC_OPENPROMIO_H
#define _SPARC_OPENPROMIO_H
+#include <linux/compiler.h>
#include <linux/ioctl.h>
#include <linux/types.h>
{
int op_nodeid; /* PROM Node ID (value-result) */
int op_namelen; /* Length of op_name. */
- char *op_name; /* Pointer to the property name. */
+ char __user *op_name; /* Pointer to the property name. */
int op_buflen; /* Length of op_buf (value-result) */
- char *op_buf; /* Pointer to buffer. */
+ char __user *op_buf; /* Pointer to buffer. */
};
#define OPIOCGET _IOWR('O', 1, struct opiocdesc)
#ifndef __LINUX_FBIO_H
#define __LINUX_FBIO_H
+#include <linux/compiler.h>
+
/* Constants used for fbio SunOS compatibility */
/* (C) 1996 Miguel de Icaza */
struct fbcmap {
int index; /* first element (0 origin) */
int count;
- unsigned char *red;
- unsigned char *green;
- unsigned char *blue;
+ unsigned char __user *red;
+ unsigned char __user *green;
+ unsigned char __user *blue;
};
#ifdef __KERNEL__
volatile int doing_pdma = 0;
/* This is software state */
-char *pdma_base = 0;
+char *pdma_base = NULL;
unsigned long pdma_areasize;
/* Common routines to all controller types on the Sparc. */
doing_pdma = 0;
if (pdma_base) {
mmu_unlockarea(pdma_base, pdma_areasize);
- pdma_base = 0;
+ pdma_base = NULL;
}
}
} else {
#ifdef CONFIG_PCI
struct linux_ebus *ebus;
- struct linux_ebus_device *edev = 0;
+ struct linux_ebus_device *edev = NULL;
unsigned long config = 0;
unsigned long auxio_reg;
spin_unlock(&mm->page_table_lock);
}
-extern void __flush_tlb_mm(unsigned long, unsigned long);
-
#define deactivate_mm(tsk,mm) do { } while (0)
/* Activate a new MM instance for the current task. */
#ifndef _SPARC64_OPENPROMIO_H
#define _SPARC64_OPENPROMIO_H
+#include <linux/compiler.h>
#include <linux/ioctl.h>
#include <linux/types.h>
{
int op_nodeid; /* PROM Node ID (value-result) */
int op_namelen; /* Length of op_name. */
- char *op_name; /* Pointer to the property name. */
+ char __user *op_name; /* Pointer to the property name. */
int op_buflen; /* Length of op_buf (value-result) */
- char *op_buf; /* Pointer to buffer. */
+ char __user *op_buf; /* Pointer to buffer. */
};
#define OPIOCGET _IOWR('O', 1, struct opiocdesc)
#include <asm/asi.h>
extern void __memmove(void *,const void *,__kernel_size_t);
-extern __kernel_size_t __memcpy(void *,const void *,__kernel_size_t);
extern void *__memset(void *,int,__kernel_size_t);
-extern void *__builtin_memcpy(void *,const void *,__kernel_size_t);
extern void *__builtin_memset(void *,int,__kernel_size_t);
#ifndef EXPORT_SYMTAB_STROPS
#define __HAVE_ARCH_MEMCPY
-static inline void *__constant_memcpy(void *to, const void *from, __kernel_size_t n)
-{
- if(n) {
- if(n <= 32) {
- __builtin_memcpy(to, from, n);
- } else {
- __memcpy(to, from, n);
- }
- }
- return to;
-}
-
-static inline void *__nonconstant_memcpy(void *to, const void *from, __kernel_size_t n)
-{
- __memcpy(to, from, n);
- return to;
-}
-
-#undef memcpy
-#define memcpy(t, f, n) \
-(__builtin_constant_p(n) ? \
- __constant_memcpy((t),(f),(n)) : \
- __nonconstant_memcpy((t),(f),(n)))
+extern void * memcpy(void *,const void *,__kernel_size_t);
#define __HAVE_ARCH_MEMSET
current_thread_info()->kernel_cntd0 += (unsigned int)(__tmp);\
current_thread_info()->kernel_cntd1 += ((__tmp) >> 32); \
} \
+ flush_tlb_pending(); \
save_and_clear_fpu(); \
/* If you are tempted to conditionalize the following */ \
/* so that ASI is only written if it changes, think again. */ \
#ifndef _SPARC64_TLB_H
#define _SPARC64_TLB_H
-#define tlb_flush(tlb) \
-do { if ((tlb)->fullmm) \
- flush_tlb_mm((tlb)->mm);\
-} while (0)
+#include <linux/config.h>
+#include <linux/swap.h>
+#include <asm/pgalloc.h>
+#include <asm/tlbflush.h>
+#include <asm/mmu_context.h>
-#define tlb_start_vma(tlb, vma) \
-do { if (!(tlb)->fullmm) \
- flush_cache_range(vma, vma->vm_start, vma->vm_end); \
-} while (0)
+#define TLB_BATCH_NR 192
-#define tlb_end_vma(tlb, vma) \
-do { if (!(tlb)->fullmm) \
- flush_tlb_range(vma, vma->vm_start, vma->vm_end); \
-} while (0)
+/*
+ * For UP we don't need to worry about TLB flush
+ * and page free order so much..
+ */
+#ifdef CONFIG_SMP
+ #define FREE_PTE_NR 506
+ #define tlb_fast_mode(bp) ((bp)->pages_nr == ~0U)
+#else
+ #define FREE_PTE_NR 1
+ #define tlb_fast_mode(bp) 1
+#endif
-#define __tlb_remove_tlb_entry(tlb, ptep, address) \
- do { } while (0)
+struct mmu_gather {
+ struct mm_struct *mm;
+ unsigned int pages_nr;
+ unsigned int need_flush;
+ unsigned int tlb_frozen;
+ unsigned int tlb_nr;
+ unsigned long freed;
+ unsigned long vaddrs[TLB_BATCH_NR];
+ struct page *pages[FREE_PTE_NR];
+};
-#include <asm-generic/tlb.h>
+DECLARE_PER_CPU(struct mmu_gather, mmu_gathers);
-#define __pmd_free_tlb(tlb, pmd) pmd_free(pmd)
-#define __pte_free_tlb(tlb, pte) pte_free(pte)
+#ifdef CONFIG_SMP
+extern void smp_flush_tlb_pending(struct mm_struct *,
+ unsigned long, unsigned long *);
+#endif
+
+extern void __flush_tlb_pending(unsigned long, unsigned long, unsigned long *);
+extern void flush_tlb_pending(void);
+
+static inline struct mmu_gather *tlb_gather_mmu(struct mm_struct *mm, unsigned int full_mm_flush)
+{
+ struct mmu_gather *mp = &per_cpu(mmu_gathers, smp_processor_id());
+
+ BUG_ON(mp->tlb_nr);
+
+ mp->mm = mm;
+ mp->pages_nr = num_online_cpus() > 1 ? 0U : ~0U;
+ mp->tlb_frozen = full_mm_flush;
+ mp->freed = 0;
+
+ return mp;
+}
+
+
+static inline void tlb_flush_mmu(struct mmu_gather *mp)
+{
+ if (mp->need_flush) {
+ mp->need_flush = 0;
+ if (!tlb_fast_mode(mp)) {
+ free_pages_and_swap_cache(mp->pages, mp->pages_nr);
+ mp->pages_nr = 0;
+ }
+ }
+
+}
+
+#ifdef CONFIG_SMP
+extern void smp_flush_tlb_mm(struct mm_struct *mm);
+#define do_flush_tlb_mm(mm) smp_flush_tlb_mm(mm)
+#else
+#define do_flush_tlb_mm(mm) __flush_tlb_mm(CTX_HWBITS(mm->context), SECONDARY_CONTEXT)
+#endif
+
+static inline void tlb_finish_mmu(struct mmu_gather *mp, unsigned long start, unsigned long end)
+{
+ unsigned long freed = mp->freed;
+ struct mm_struct *mm = mp->mm;
+ unsigned long rss = mm->rss;
+
+ if (rss < freed)
+ freed = rss;
+ mm->rss = rss - freed;
+
+ tlb_flush_mmu(mp);
+
+ if (mp->tlb_frozen) {
+ unsigned long context = mm->context;
+
+ if (CTX_VALID(context))
+ do_flush_tlb_mm(mm);
+ mp->tlb_frozen = 0;
+ } else
+ flush_tlb_pending();
+
+ /* keep the page table cache within bounds */
+ check_pgt_cache();
+}
+
+static inline unsigned int tlb_is_full_mm(struct mmu_gather *mp)
+{
+ return mp->tlb_frozen;
+}
+
+static inline void tlb_remove_page(struct mmu_gather *mp, struct page *page)
+{
+ mp->need_flush = 1;
+ if (tlb_fast_mode(mp)) {
+ free_page_and_swap_cache(page);
+ return;
+ }
+ mp->pages[mp->pages_nr++] = page;
+ if (mp->pages_nr >= FREE_PTE_NR)
+ tlb_flush_mmu(mp);
+}
+
+#define tlb_remove_tlb_entry(mp,ptep,addr) do { } while (0)
+#define pte_free_tlb(mp,ptepage) pte_free(ptepage)
+#define pmd_free_tlb(mp,pmdp) pmd_free(pmdp)
+
+#define tlb_migrate_finish(mm) do { } while (0)
+#define tlb_start_vma(tlb, vma) do { } while (0)
+#define tlb_end_vma(tlb, vma) do { } while (0)
#endif /* _SPARC64_TLB_H */
/* TLB flush operations. */
+extern void flush_tlb_pending(void);
+
+#define flush_tlb_range(vma,start,end) \
+ do { (void)(start); flush_tlb_pending(); } while (0)
+#define flush_tlb_page(vma,addr) flush_tlb_pending()
+#define flush_tlb_mm(mm) flush_tlb_pending()
+
extern void __flush_tlb_all(void);
-extern void __flush_tlb_mm(unsigned long context, unsigned long r);
-extern void __flush_tlb_range(unsigned long context, unsigned long start,
- unsigned long r, unsigned long end,
- unsigned long pgsz, unsigned long size);
extern void __flush_tlb_page(unsigned long context, unsigned long page, unsigned long r);
extern void __flush_tlb_kernel_range(unsigned long start, unsigned long end);
#define flush_tlb_kernel_range(start,end) \
__flush_tlb_kernel_range(start,end)
-#define flush_tlb_mm(__mm) \
-do { if (CTX_VALID((__mm)->context)) \
- __flush_tlb_mm(CTX_HWBITS((__mm)->context), SECONDARY_CONTEXT); \
-} while (0)
-
-#define flush_tlb_range(__vma, start, end) \
-do { if (CTX_VALID((__vma)->vm_mm->context)) { \
- unsigned long __start = (start)&PAGE_MASK; \
- unsigned long __end = PAGE_ALIGN(end); \
- __flush_tlb_range(CTX_HWBITS((__vma)->vm_mm->context), __start, \
- SECONDARY_CONTEXT, __end, PAGE_SIZE, \
- (__end - __start)); \
- } \
-} while (0)
-
-#define flush_tlb_vpte_range(__mm, start, end) \
-do { if (CTX_VALID((__mm)->context)) { \
- unsigned long __start = (start)&PAGE_MASK; \
- unsigned long __end = PAGE_ALIGN(end); \
- __flush_tlb_range(CTX_HWBITS((__mm)->context), __start, \
- SECONDARY_CONTEXT, __end, PAGE_SIZE, \
- (__end - __start)); \
- } \
-} while (0)
-
-#define flush_tlb_page(vma, page) \
-do { struct mm_struct *__mm = (vma)->vm_mm; \
- if (CTX_VALID(__mm->context)) \
- __flush_tlb_page(CTX_HWBITS(__mm->context), (page)&PAGE_MASK, \
- SECONDARY_CONTEXT); \
-} while (0)
-
-#define flush_tlb_vpte_page(mm, addr) \
-do { struct mm_struct *__mm = (mm); \
- if (CTX_VALID(__mm->context)) \
- __flush_tlb_page(CTX_HWBITS(__mm->context), (addr)&PAGE_MASK, \
- SECONDARY_CONTEXT); \
-} while (0)
-
#else /* CONFIG_SMP */
extern void smp_flush_tlb_all(void);
-extern void smp_flush_tlb_mm(struct mm_struct *mm);
-extern void smp_flush_tlb_range(struct mm_struct *mm, unsigned long start,
- unsigned long end);
extern void smp_flush_tlb_kernel_range(unsigned long start, unsigned long end);
-extern void smp_flush_tlb_page(struct mm_struct *mm, unsigned long page);
#define flush_tlb_all() smp_flush_tlb_all()
-#define flush_tlb_mm(mm) smp_flush_tlb_mm(mm)
-#define flush_tlb_range(vma, start, end) \
- smp_flush_tlb_range((vma)->vm_mm, start, end)
-#define flush_tlb_vpte_range(mm, start, end) \
- smp_flush_tlb_range(mm, start, end)
#define flush_tlb_kernel_range(start, end) \
smp_flush_tlb_kernel_range(start, end)
-#define flush_tlb_page(vma, page) \
- smp_flush_tlb_page((vma)->vm_mm, page)
-#define flush_tlb_vpte_page(mm, page) \
- smp_flush_tlb_page((mm), page)
#endif /* ! CONFIG_SMP */
-static __inline__ void flush_tlb_pgtables(struct mm_struct *mm, unsigned long start,
- unsigned long end)
-{
- /* Note the signed type. */
- long s = start, e = end, vpte_base;
- /* Nobody should call us with start below VM hole and end above.
- See if it is really true. */
- BUG_ON(s > e);
-#if 0
- /* Currently free_pgtables guarantees this. */
- s &= PMD_MASK;
- e = (e + PMD_SIZE - 1) & PMD_MASK;
-#endif
- vpte_base = (tlb_type == spitfire ?
- VPTE_BASE_SPITFIRE :
- VPTE_BASE_CHEETAH);
-
- flush_tlb_vpte_range(mm,
- vpte_base + (s >> (PAGE_SHIFT - 3)),
- vpte_base + (e >> (PAGE_SHIFT - 3)));
-}
+extern void flush_tlb_pgtables(struct mm_struct *, unsigned long, unsigned long);
#endif /* _SPARC64_TLBFLUSH_H */
#else
#define SUNOS_SYSCALL_TRAP TRAP(sunos_syscall)
#endif
+#ifdef CONFIG_COMPAT
#define LINUX_32BIT_SYSCALL_TRAP SYSCALL_TRAP(linux_sparc_syscall32, sys_call_table32)
+#else
+#define LINUX_32BIT_SYSCALL_TRAP BTRAP(0x110)
+#endif
#define LINUX_64BIT_SYSCALL_TRAP SYSCALL_TRAP(linux_sparc_syscall, sys_call_table64)
#define GETCC_TRAP TRAP(getcc)
#define SETCC_TRAP TRAP(setcc)
#ifdef CONFIG_X86_IO_APIC
-#ifdef CONFIG_PCI_USE_VECTOR
+#ifdef CONFIG_PCI_MSI
static inline int use_pci_vector(void) {return 1;}
static inline void disable_edge_ioapic_vector(unsigned int vector) { }
static inline void mask_and_ack_level_ioapic_vector(unsigned int vector) { }
#define FIRST_SYSTEM_VECTOR 0xef /* duplicated in hw_irq.h */
-#ifdef CONFIG_PCI_USE_VECTOR
+#ifdef CONFIG_PCI_MSI
#define NR_IRQS FIRST_SYSTEM_VECTOR
#define NR_IRQ_VECTORS NR_IRQS
#else
struct atm_iobuf {
int length;
- void *buffer;
+ void __user *buffer;
};
/* for ATM_GETCIRANGE / ATM_SETCIRANGE */
#define ATM_CI_MAX -1 /* use maximum range of VPI/VCI */
struct atm_cirange {
- char vpi_bits; /* 1..8, ATM_CI_MAX (-1) for maximum */
- char vci_bits; /* 1..16, ATM_CI_MAX (-1) for maximum */
+ signed char vpi_bits; /* 1..8, ATM_CI_MAX (-1) for maximum */
+ signed char vci_bits; /* 1..16, ATM_CI_MAX (-1) for maximum */
};
/* for ATM_SETSC; actually taken from the ATM_VF number space */
*
* ATM Lan Emulation Daemon vs. driver interface
*
- * carnil@cs.tut.fi
+ * mkiiskila@yahoo.com
*
*/
* data structure for /proc/sys/... files
*/
int do_reset_coda_vfs_stats( ctl_table * table, int write, struct file * filp,
- void __user * buffer, size_t * lenp );
+ void __user * buffer, size_t * lenp, loff_t * ppos );
int do_reset_coda_cache_inv_stats( ctl_table * table, int write,
struct file * filp, void __user * buffer,
- size_t * lenp );
+ size_t * lenp, loff_t * ppos );
/* these functions are called to form the content of /proc/fs/coda/... files */
int coda_vfs_stats_get_info( char * buffer, char ** start, off_t offset,
unsigned long vc_pos; /* Cursor address */
/* fonts */
unsigned short vc_hi_font_mask; /* [#] Attribute set for upper 256 chars of font or 0 if not supported */
- struct console_font_op vc_font; /* Current VC font set */
+ struct console_font vc_font; /* Current VC font set */
unsigned short vc_video_erase_char; /* Background erase character */
/* VT terminal data */
unsigned int vc_state; /* Escape sequence parser state */
--- /dev/null
+#ifndef __DMI_H__
+#define __DMI_H__
+
+enum dmi_field {
+ DMI_NONE,
+ DMI_BIOS_VENDOR,
+ DMI_BIOS_VERSION,
+ DMI_BIOS_DATE,
+ DMI_SYS_VENDOR,
+ DMI_PRODUCT_NAME,
+ DMI_PRODUCT_VERSION,
+ DMI_BOARD_VENDOR,
+ DMI_BOARD_NAME,
+ DMI_BOARD_VERSION,
+ DMI_STRING_MAX,
+};
+
+/*
+ * DMI callbacks for problem boards
+ */
+struct dmi_strmatch {
+ u8 slot;
+ char *substr;
+};
+
+struct dmi_system_id {
+ int (*callback)(struct dmi_system_id *);
+ char *ident;
+ struct dmi_strmatch matches[4];
+ void *driver_data;
+};
+
+#define DMI_MATCH(a,b) { a, b }
+
+#if defined(CONFIG_X86) && !defined(CONFIG_X86_64)
+
+extern int dmi_check_system(struct dmi_system_id *list);
+extern char * dmi_get_system_info(int field);
+
+#else
+
+static inline int dmi_check_system(struct dmi_system_id *list) { return 0; }
+static inline char * dmi_get_system_info(int field) { return NULL; }
+
+#endif
+
+#endif /* __DMI_H__ */
void gs_set_termios (struct tty_struct * tty,
struct termios * old_termios);
int gs_init_port(struct gs_port *port);
-int gs_setserial(struct gs_port *port, struct serial_struct *sp);
-int gs_getserial(struct gs_port *port, struct serial_struct *sp);
+int gs_setserial(struct gs_port *port, struct serial_struct __user *sp);
+int gs_getserial(struct gs_port *port, struct serial_struct __user *sp);
void gs_got_break(struct gs_port *port);
extern int gs_debug;
struct gendisk {
int major; /* major number of driver */
int first_minor;
- int minors;
+ int minors; /* maximum number of minors, =1 for
+ * disks that can't be partitioned. */
char disk_name[32]; /* name of major driver */
struct hd_struct **part; /* [indexed by minor] */
struct block_device_operations *fops;
#define HPET_LEG_RT_CNF_MASK (2UL)
#define HPET_ENABLE_CNF_MASK (1UL)
-/*
- * HPET interrupt status register
- */
-
-#define HPET_ISR_CLEAR(HPET, TIMER) \
- (HPET)->hpet_isr |= (1UL << TIMER)
/*
* Timer configuration register
#define HPET_DATA_PLATFORM 0x0001 /* platform call to hpet_alloc */
+static inline void hpet_reserve_timer(struct hpet_data *hd, int timer)
+{
+ hd->hd_state |= (1 << timer);
+ return;
+}
+
int hpet_alloc(struct hpet_data *);
int hpet_register(struct hpet_task *, int);
int hpet_unregister(struct hpet_task *);
#define ICMPV6_MGM_REPORT 131
#define ICMPV6_MGM_REDUCTION 132
-/* definitions for MLDv2 */
-
-#define MLD2_MODE_IS_INCLUDE 1
-#define MLD2_MODE_IS_EXCLUDE 2
-#define MLD2_CHANGE_TO_INCLUDE 3
-#define MLD2_CHANGE_TO_EXCLUDE 4
-#define MLD2_ALLOW_NEW_SOURCES 5
-#define MLD2_BLOCK_OLD_SOURCES 6
+#define ICMPV6_NI_QUERY 139
+#define ICMPV6_NI_REPLY 140
#define ICMPV6_MLD2_REPORT 143
-#define MLD2_ALL_MCR_INIT { { { 0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,0x16 } } }
+
+#define ICMPV6_DHAAD_REQUEST 144
+#define ICMPV6_DHAAD_REPLY 145
+#define ICMPV6_MOBILE_PREFIX_SOL 146
+#define ICMPV6_MOBILE_PREFIX_ADV 147
/*
* Codes for Destination Unreachable
__u32 data[8];
};
+/*
+ * Definitions for MLDv2
+ */
+#define MLD2_MODE_IS_INCLUDE 1
+#define MLD2_MODE_IS_EXCLUDE 2
+#define MLD2_CHANGE_TO_INCLUDE 3
+#define MLD2_CHANGE_TO_EXCLUDE 4
+#define MLD2_ALLOW_NEW_SOURCES 5
+#define MLD2_BLOCK_OLD_SOURCES 6
+
+#define MLD2_ALL_MCR_INIT { { { 0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,0x16 } } }
+
#ifdef __KERNEL__
#include <linux/netdevice.h>
struct packet_type;
struct vlan_collection;
struct vlan_dev_info;
+struct hlist_node;
#include <linux/proc_fs.h> /* for proc_dir_entry */
#include <linux/netdevice.h>
struct vlan_group {
int real_dev_ifindex; /* The ifindex of the ethernet(like) device the vlan is attached to. */
+ struct hlist_node hlist; /* linked list */
struct net_device *vlan_devices[VLAN_GROUP_ARRAY_LEN];
-
- struct vlan_group *next; /* the next in the list */
+ struct rcu_head rcu;
};
struct vlan_priority_tci_mapping {
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
-/* Cannot easily do prefetch unfortunately */
#define hlist_for_each(pos, head) \
for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
pos = pos->next)
#define hlist_for_each_safe(pos, n, head) \
- for (pos = (head)->first; n = pos ? pos->next : 0, pos; \
+ for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
pos = n)
/**
pos && ({ n = pos->next; 1; }) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = n)
+
+/**
+ * hlist_for_each_entry_rcu - iterate over rcu list of given type
+ * @pos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as hlist_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define hlist_for_each_entry_rcu(tpos, pos, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next, ({ smp_read_barrier_depends(); 0; }) )
+
#else
#warning "don't include kernel headers in userspace"
#endif /* __KERNEL__ */
struct mempolicy *mpol_shared_policy_lookup(struct shared_policy *sp,
unsigned long idx);
+extern void numa_default_policy(void);
+extern void numa_policy_init(void);
+
#else
struct mempolicy {};
#define vma_policy(vma) NULL
#define vma_set_policy(vma, pol) do {} while(0)
+static inline void numa_policy_init(void)
+{
+}
+
+static inline void numa_default_policy(void)
+{
+}
+
#endif /* CONFIG_NUMA */
#endif /* __KERNEL__ */
* For boards with physically mapped flash and using
* drivers/mtd/maps/physmap.c mapping driver.
*
- * $Id: physmap.h,v 1.2 2004/07/14 17:48:46 dwmw2 Exp $
+ * $Id: physmap.h,v 1.3 2004/07/21 00:16:15 jwboyer Exp $
*
* Copyright (C) 2003 MontaVista Software Inc.
* Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
/*
* Board needs to specify the exact mapping during their setup time.
*/
-static inline void physmap_configure(unsigned long addr, unsigned long size, int buswidth, void (*set_vpp)(struct map_info *, int) )
+static inline void physmap_configure(unsigned long addr, unsigned long size, int bankwidth, void (*set_vpp)(struct map_info *, int) )
{
physmap_map.phys = addr;
physmap_map.size = size;
- physmap_map.buswidth = buswidth;
+ physmap_map.bankwidth = bankwidth;
physmap_map.set_vpp = set_vpp;
}
unsigned mt_segno; /* the segment to read or write */
unsigned mt_mode; /* modes for read/write (sync/async etc.) */
int mt_result; /* result of r/w request, not of the ioctl */
- void *mt_data; /* User space buffer: must be 29kb */
+ void __user *mt_data; /* User space buffer: must be 29kb */
};
/* get tape capacity (ftape/zftape)
u32 c_vers;
unsigned long c_timestamp;
union {
- struct iovec u_vec;
+ struct kvec u_vec;
u32 u_status;
} c_u;
};
struct svc_fh fh;
__u32 offset;
__u32 count;
- struct iovec vec[RPCSVC_MAXPAGES];
+ struct kvec vec[RPCSVC_MAXPAGES];
int vlen;
};
svc_fh fh;
__u32 offset;
int len;
- struct iovec vec[RPCSVC_MAXPAGES];
+ struct kvec vec[RPCSVC_MAXPAGES];
int vlen;
};
struct svc_fh fh;
__u64 offset;
__u32 count;
- struct iovec vec[RPCSVC_MAXPAGES];
+ struct kvec vec[RPCSVC_MAXPAGES];
int vlen;
};
__u32 count;
int stable;
int len;
- struct iovec vec[RPCSVC_MAXPAGES];
+ struct kvec vec[RPCSVC_MAXPAGES];
int vlen;
};
*/
enum {
MMAP_PAGE_ZERO = 0x0100000,
+ READ_IMPLIES_EXEC = 0x0400000,
ADDR_LIMIT_32BIT = 0x0800000,
SHORT_INODE = 0x1000000,
WHOLE_SECONDS = 0x2000000,
ADDR_LIMIT_3GB = 0x8000000,
};
+/*
+ * Security-relevant compatibility flags that must be
+ * cleared upon setuid or setgid exec:
+ */
+#define PER_CLEAR_ON_SETID (READ_IMPLIES_EXEC)
+
/*
* Personality types.
*
--- /dev/null
+/*
+ * Definitions for MIBs
+ *
+ * Author: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
+ */
+
+#ifndef _LINUX_SNMP_H
+#define _LINUX_SNMP_H
+
+/* ipstats mib definitions */
+/*
+ * RFC 1213: MIB-II
+ * RFC 2011 (updates 1213): SNMPv2-MIB-IP
+ * RFC 2863: Interfaces Group MIB
+ * RFC 2465: IPv6 MIB: General Group
+ * draft-ietf-ipv6-rfc2011-update-10.txt: MIB for IP: IP Statistics Tables
+ */
+enum
+{
+ IPSTATS_MIB_NUM = 0,
+ IPSTATS_MIB_INRECEIVES, /* InReceives */
+ IPSTATS_MIB_INHDRERRORS, /* InHdrErrors */
+ IPSTATS_MIB_INTOOBIGERRORS, /* InTooBigErrors */
+ IPSTATS_MIB_INNOROUTES, /* InNoRoutes */
+ IPSTATS_MIB_INADDRERRORS, /* InAddrErrors */
+ IPSTATS_MIB_INUNKNOWNPROTOS, /* InUnknownProtos */
+ IPSTATS_MIB_INTRUNCATEDPKTS, /* InTruncatedPkts */
+ IPSTATS_MIB_INDISCARDS, /* InDiscards */
+ IPSTATS_MIB_INDELIVERS, /* InDelivers */
+ IPSTATS_MIB_OUTFORWDATAGRAMS, /* OutForwDatagrams */
+ IPSTATS_MIB_OUTREQUESTS, /* OutRequests */
+ IPSTATS_MIB_OUTDISCARDS, /* OutDiscards */
+ IPSTATS_MIB_OUTNOROUTES, /* OutNoRoutes */
+ IPSTATS_MIB_REASMTIMEOUT, /* ReasmTimeout */
+ IPSTATS_MIB_REASMREQDS, /* ReasmReqds */
+ IPSTATS_MIB_REASMOKS, /* ReasmOKs */
+ IPSTATS_MIB_REASMFAILS, /* ReasmFails */
+ IPSTATS_MIB_FRAGOKS, /* FragOKs */
+ IPSTATS_MIB_FRAGFAILS, /* FragFails */
+ IPSTATS_MIB_FRAGCREATES, /* FragCreates */
+ IPSTATS_MIB_INMCASTPKTS, /* InMcastPkts */
+ IPSTATS_MIB_OUTMCASTPKTS, /* OutMcastPkts */
+ __IPSTATS_MIB_MAX
+};
+
+/* icmp mib definitions */
+/*
+ * RFC 1213: MIB-II ICMP Group
+ * RFC 2011 (updates 1213): SNMPv2 MIB for IP: ICMP group
+ */
+enum
+{
+ ICMP_MIB_NUM = 0,
+ ICMP_MIB_INMSGS, /* InMsgs */
+ ICMP_MIB_INERRORS, /* InErrors */
+ ICMP_MIB_INDESTUNREACHS, /* InDestUnreachs */
+ ICMP_MIB_INTIMEEXCDS, /* InTimeExcds */
+ ICMP_MIB_INPARMPROBS, /* InParmProbs */
+ ICMP_MIB_INSRCQUENCHS, /* InSrcQuenchs */
+ ICMP_MIB_INREDIRECTS, /* InRedirects */
+ ICMP_MIB_INECHOS, /* InEchos */
+ ICMP_MIB_INECHOREPS, /* InEchoReps */
+ ICMP_MIB_INTIMESTAMPS, /* InTimestamps */
+ ICMP_MIB_INTIMESTAMPREPS, /* InTimestampReps */
+ ICMP_MIB_INADDRMASKS, /* InAddrMasks */
+ ICMP_MIB_INADDRMASKREPS, /* InAddrMaskReps */
+ ICMP_MIB_OUTMSGS, /* OutMsgs */
+ ICMP_MIB_OUTERRORS, /* OutErrors */
+ ICMP_MIB_OUTDESTUNREACHS, /* OutDestUnreachs */
+ ICMP_MIB_OUTTIMEEXCDS, /* OutTimeExcds */
+ ICMP_MIB_OUTPARMPROBS, /* OutParmProbs */
+ ICMP_MIB_OUTSRCQUENCHS, /* OutSrcQuenchs */
+ ICMP_MIB_OUTREDIRECTS, /* OutRedirects */
+ ICMP_MIB_OUTECHOS, /* OutEchos */
+ ICMP_MIB_OUTECHOREPS, /* OutEchoReps */
+ ICMP_MIB_OUTTIMESTAMPS, /* OutTimestamps */
+ ICMP_MIB_OUTTIMESTAMPREPS, /* OutTimestampReps */
+ ICMP_MIB_OUTADDRMASKS, /* OutAddrMasks */
+ ICMP_MIB_OUTADDRMASKREPS, /* OutAddrMaskReps */
+ __ICMP_MIB_MAX
+};
+
+/* icmp6 mib definitions */
+/*
+ * RFC 2466: ICMPv6-MIB
+ */
+enum
+{
+ ICMP6_MIB_NUM = 0,
+ ICMP6_MIB_INMSGS, /* InMsgs */
+ ICMP6_MIB_INERRORS, /* InErrors */
+ ICMP6_MIB_INDESTUNREACHS, /* InDestUnreachs */
+ ICMP6_MIB_INPKTTOOBIGS, /* InPktTooBigs */
+ ICMP6_MIB_INTIMEEXCDS, /* InTimeExcds */
+ ICMP6_MIB_INPARMPROBLEMS, /* InParmProblems */
+ ICMP6_MIB_INECHOS, /* InEchos */
+ ICMP6_MIB_INECHOREPLIES, /* InEchoReplies */
+ ICMP6_MIB_INGROUPMEMBQUERIES, /* InGroupMembQueries */
+ ICMP6_MIB_INGROUPMEMBRESPONSES, /* InGroupMembResponses */
+ ICMP6_MIB_INGROUPMEMBREDUCTIONS, /* InGroupMembReductions */
+ ICMP6_MIB_INROUTERSOLICITS, /* InRouterSolicits */
+ ICMP6_MIB_INROUTERADVERTISEMENTS, /* InRouterAdvertisements */
+ ICMP6_MIB_INNEIGHBORSOLICITS, /* InNeighborSolicits */
+ ICMP6_MIB_INNEIGHBORADVERTISEMENTS, /* InNeighborAdvertisements */
+ ICMP6_MIB_INREDIRECTS, /* InRedirects */
+ ICMP6_MIB_OUTMSGS, /* OutMsgs */
+ ICMP6_MIB_OUTDESTUNREACHS, /* OutDestUnreachs */
+ ICMP6_MIB_OUTPKTTOOBIGS, /* OutPktTooBigs */
+ ICMP6_MIB_OUTTIMEEXCDS, /* OutTimeExcds */
+ ICMP6_MIB_OUTPARMPROBLEMS, /* OutParmProblems */
+ ICMP6_MIB_OUTECHOREPLIES, /* OutEchoReplies */
+ ICMP6_MIB_OUTROUTERSOLICITS, /* OutRouterSolicits */
+ ICMP6_MIB_OUTNEIGHBORSOLICITS, /* OutNeighborSolicits */
+ ICMP6_MIB_OUTNEIGHBORADVERTISEMENTS, /* OutNeighborAdvertisements */
+ ICMP6_MIB_OUTREDIRECTS, /* OutRedirects */
+ ICMP6_MIB_OUTGROUPMEMBRESPONSES, /* OutGroupMembResponses */
+ ICMP6_MIB_OUTGROUPMEMBREDUCTIONS, /* OutGroupMembReductions */
+ __ICMP6_MIB_MAX
+};
+
+/* tcp mib definitions */
+/*
+ * RFC 1213: MIB-II TCP group
+ * RFC 2012 (updates 1213): SNMPv2-MIB-TCP
+ */
+enum
+{
+ TCP_MIB_NUM = 0,
+ TCP_MIB_RTOALGORITHM, /* RtoAlgorithm */
+ TCP_MIB_RTOMIN, /* RtoMin */
+ TCP_MIB_RTOMAX, /* RtoMax */
+ TCP_MIB_MAXCONN, /* MaxConn */
+ TCP_MIB_ACTIVEOPENS, /* ActiveOpens */
+ TCP_MIB_PASSIVEOPENS, /* PassiveOpens */
+ TCP_MIB_ATTEMPTFAILS, /* AttemptFails */
+ TCP_MIB_ESTABRESETS, /* EstabResets */
+ TCP_MIB_CURRESTAB, /* CurrEstab */
+ TCP_MIB_INSEGS, /* InSegs */
+ TCP_MIB_OUTSEGS, /* OutSegs */
+ TCP_MIB_RETRANSSEGS, /* RetransSegs */
+ TCP_MIB_INERRS, /* InErrs */
+ TCP_MIB_OUTRSTS, /* OutRsts */
+ __TCP_MIB_MAX
+};
+
+/* udp mib definitions */
+/*
+ * RFC 1213: MIB-II UDP group
+ * RFC 2013 (updates 1213): SNMPv2-MIB-UDP
+ */
+enum
+{
+ UDP_MIB_NUM = 0,
+ UDP_MIB_INDATAGRAMS, /* InDatagrams */
+ UDP_MIB_NOPORTS, /* NoPorts */
+ UDP_MIB_INERRORS, /* InErrors */
+ UDP_MIB_OUTDATAGRAMS, /* OutDatagrams */
+ __UDP_MIB_MAX
+};
+
+/* sctp mib definitions */
+/*
+ * draft-ietf-sigtran-sctp-mib-07.txt
+ */
+enum
+{
+ SCTP_MIB_NUM = 0,
+ SCTP_MIB_CURRESTAB, /* CurrEstab */
+ SCTP_MIB_ACTIVEESTABS, /* ActiveEstabs */
+ SCTP_MIB_PASSIVEESTABS, /* PassiveEstabs */
+ SCTP_MIB_ABORTEDS, /* Aborteds */
+ SCTP_MIB_SHUTDOWNS, /* Shutdowns */
+ SCTP_MIB_OUTOFBLUES, /* OutOfBlues */
+ SCTP_MIB_CHECKSUMERRORS, /* ChecksumErrors */
+ SCTP_MIB_OUTCTRLCHUNKS, /* OutCtrlChunks */
+ SCTP_MIB_OUTORDERCHUNKS, /* OutOrderChunks */
+ SCTP_MIB_OUTUNORDERCHUNKS, /* OutUnorderChunks */
+ SCTP_MIB_INCTRLCHUNKS, /* InCtrlChunks */
+ SCTP_MIB_INORDERCHUNKS, /* InOrderChunks */
+ SCTP_MIB_INUNORDERCHUNKS, /* InUnorderChunks */
+ SCTP_MIB_FRAGUSRMSGS, /* FragUsrMsgs */
+ SCTP_MIB_REASMUSRMSGS, /* ReasmUsrMsgs */
+ SCTP_MIB_OUTSCTPPACKS, /* OutSCTPPacks */
+ SCTP_MIB_INSCTPPACKS, /* InSCTPPacks */
+ SCTP_MIB_RTOALGORITHM, /* RtoAlgorithm */
+ SCTP_MIB_RTOMIN, /* RtoMin */
+ SCTP_MIB_RTOMAX, /* RtoMax */
+ SCTP_MIB_RTOINITIAL, /* RtoInitial */
+ SCTP_MIB_VALCOOKIELIFE, /* ValCookieLife */
+ SCTP_MIB_MAXINITRETR, /* MaxInitRetr */
+ __SCTP_MIB_MAX
+};
+
+/* linux mib definitions */
+enum
+{
+ LINUX_MIB_NUM = 0,
+ LINUX_MIB_SYNCOOKIESSENT, /* SyncookiesSent */
+ LINUX_MIB_SYNCOOKIESRECV, /* SyncookiesRecv */
+ LINUX_MIB_SYNCOOKIESFAILED, /* SyncookiesFailed */
+ LINUX_MIB_EMBRYONICRSTS, /* EmbryonicRsts */
+ LINUX_MIB_PRUNECALLED, /* PruneCalled */
+ LINUX_MIB_RCVPRUNED, /* RcvPruned */
+ LINUX_MIB_OFOPRUNED, /* OfoPruned */
+ LINUX_MIB_OUTOFWINDOWICMPS, /* OutOfWindowIcmps */
+ LINUX_MIB_LOCKDROPPEDICMPS, /* LockDroppedIcmps */
+ LINUX_MIB_ARPFILTER, /* ArpFilter */
+ LINUX_MIB_TIMEWAITED, /* TimeWaited */
+ LINUX_MIB_TIMEWAITRECYCLED, /* TimeWaitRecycled */
+ LINUX_MIB_TIMEWAITKILLED, /* TimeWaitKilled */
+ LINUX_MIB_PAWSPASSIVEREJECTED, /* PAWSPassiveRejected */
+ LINUX_MIB_PAWSACTIVEREJECTED, /* PAWSActiveRejected */
+ LINUX_MIB_PAWSESTABREJECTED, /* PAWSEstabRejected */
+ LINUX_MIB_DELAYEDACKS, /* DelayedACKs */
+ LINUX_MIB_DELAYEDACKLOCKED, /* DelayedACKLocked */
+ LINUX_MIB_DELAYEDACKLOST, /* DelayedACKLost */
+ LINUX_MIB_LISTENOVERFLOWS, /* ListenOverflows */
+ LINUX_MIB_LISTENDROPS, /* ListenDrops */
+ LINUX_MIB_TCPPREQUEUED, /* TCPPrequeued */
+ LINUX_MIB_TCPDIRECTCOPYFROMBACKLOG, /* TCPDirectCopyFromBacklog */
+ LINUX_MIB_TCPDIRECTCOPYFROMPREQUEUE, /* TCPDirectCopyFromPrequeue */
+ LINUX_MIB_TCPPREQUEUEDROPPED, /* TCPPrequeueDropped */
+ LINUX_MIB_TCPHPHITS, /* TCPHPHits */
+ LINUX_MIB_TCPHPHITSTOUSER, /* TCPHPHitsToUser */
+ LINUX_MIB_TCPPUREACKS, /* TCPPureAcks */
+ LINUX_MIB_TCPHPACKS, /* TCPHPAcks */
+ LINUX_MIB_TCPRENORECOVERY, /* TCPRenoRecovery */
+ LINUX_MIB_TCPSACKRECOVERY, /* TCPSackRecovery */
+ LINUX_MIB_TCPSACKRENEGING, /* TCPSACKReneging */
+ LINUX_MIB_TCPFACKREORDER, /* TCPFACKReorder */
+ LINUX_MIB_TCPSACKREORDER, /* TCPSACKReorder */
+ LINUX_MIB_TCPRENOREORDER, /* TCPRenoReorder */
+ LINUX_MIB_TCPTSREORDER, /* TCPTSReorder */
+ LINUX_MIB_TCPFULLUNDO, /* TCPFullUndo */
+ LINUX_MIB_TCPPARTIALUNDO, /* TCPPartialUndo */
+ LINUX_MIB_TCPDSACKUNDO, /* TCPDSACKUndo */
+ LINUX_MIB_TCPLOSSUNDO, /* TCPLossUndo */
+ LINUX_MIB_TCPLOSS, /* TCPLoss */
+ LINUX_MIB_TCPLOSTRETRANSMIT, /* TCPLostRetransmit */
+ LINUX_MIB_TCPRENOFAILURES, /* TCPRenoFailures */
+ LINUX_MIB_TCPSACKFAILURES, /* TCPSackFailures */
+ LINUX_MIB_TCPLOSSFAILURES, /* TCPLossFailures */
+ LINUX_MIB_TCPFASTRETRANS, /* TCPFastRetrans */
+ LINUX_MIB_TCPFORWARDRETRANS, /* TCPForwardRetrans */
+ LINUX_MIB_TCPSLOWSTARTRETRANS, /* TCPSlowStartRetrans */
+ LINUX_MIB_TCPTIMEOUTS, /* TCPTimeouts */
+ LINUX_MIB_TCPRENORECOVERYFAIL, /* TCPRenoRecoveryFail */
+ LINUX_MIB_TCPSACKRECOVERYFAIL, /* TCPSackRecoveryFail */
+ LINUX_MIB_TCPSCHEDULERFAILED, /* TCPSchedulerFailed */
+ LINUX_MIB_TCPRCVCOLLAPSED, /* TCPRcvCollapsed */
+ LINUX_MIB_TCPDSACKOLDSENT, /* TCPDSACKOldSent */
+ LINUX_MIB_TCPDSACKOFOSENT, /* TCPDSACKOfoSent */
+ LINUX_MIB_TCPDSACKRECV, /* TCPDSACKRecv */
+ LINUX_MIB_TCPDSACKOFORECV, /* TCPDSACKOfoRecv */
+ LINUX_MIB_TCPABORTONSYN, /* TCPAbortOnSyn */
+ LINUX_MIB_TCPABORTONDATA, /* TCPAbortOnData */
+ LINUX_MIB_TCPABORTONCLOSE, /* TCPAbortOnClose */
+ LINUX_MIB_TCPABORTONMEMORY, /* TCPAbortOnMemory */
+ LINUX_MIB_TCPABORTONTIMEOUT, /* TCPAbortOnTimeout */
+ LINUX_MIB_TCPABORTONLINGER, /* TCPAbortOnLinger */
+ LINUX_MIB_TCPABORTFAILED, /* TCPAbortFailed */
+ LINUX_MIB_TCPMEMORYPRESSURES, /* TCPMemoryPressures */
+ __LINUX_MIB_MAX
+};
+
+#endif /* _LINUX_SNMP_H */
* read responses (that have a header, and some data pages, and possibly
* a tail) and means we can share some client side routines.
*
- * The xdr_buf.head iovec always points to the first page in the rq_*pages
+ * The xdr_buf.head kvec always points to the first page in the rq_*pages
* list. The xdr_buf.pages pointer points to the second page on that
* list. xdr_buf.tail points to the end of the first page.
* This assumes that the non-page part of an rpc reply will fit
*/
#define RPCSVC_MAXPAGES ((RPCSVC_MAXPAYLOAD+PAGE_SIZE-1)/PAGE_SIZE + 2)
-static inline u32 svc_getu32(struct iovec *iov)
+static inline u32 svc_getu32(struct kvec *iov)
{
u32 val, *vp;
vp = iov->iov_base;
iov->iov_len -= sizeof(u32);
return val;
}
-static inline void svc_putu32(struct iovec *iov, u32 val)
+static inline void svc_putu32(struct kvec *iov, u32 val)
{
u32 *vp = iov->iov_base + iov->iov_len;
*vp = val;
xdr_argsize_check(struct svc_rqst *rqstp, u32 *p)
{
char *cp = (char *)p;
- struct iovec *vec = &rqstp->rq_arg.head[0];
+ struct kvec *vec = &rqstp->rq_arg.head[0];
return cp - (char*)vec->iov_base <= vec->iov_len;
}
static inline int
xdr_ressize_check(struct svc_rqst *rqstp, u32 *p)
{
- struct iovec *vec = &rqstp->rq_res.head[0];
+ struct kvec *vec = &rqstp->rq_res.head[0];
char *cp = (char*)p;
vec->iov_len = cp - (char*)vec->iov_base;
* operations and/or has a need for scatter/gather involving pages.
*/
struct xdr_buf {
- struct iovec head[1], /* RPC header + non-page data */
+ struct kvec head[1], /* RPC header + non-page data */
tail[1]; /* Appended after page data */
struct page ** pages; /* Array of contiguous pages */
}
/*
- * Adjust iovec to reflect end of xdr'ed data (RPC client XDR)
+ * Adjust kvec to reflect end of xdr'ed data (RPC client XDR)
*/
static inline int
-xdr_adjust_iovec(struct iovec *iov, u32 *p)
+xdr_adjust_iovec(struct kvec *iov, u32 *p)
{
return iov->iov_len = ((u8 *) p - (u8 *) iov->iov_base);
}
-void xdr_shift_iovec(struct iovec *, int, size_t);
+void xdr_shift_iovec(struct kvec *, int, size_t);
/*
* Maximum number of iov's we use.
/*
* XDR buffer helper functions
*/
-extern int xdr_kmap(struct iovec *, struct xdr_buf *, size_t);
+extern int xdr_kmap(struct kvec *, struct xdr_buf *, size_t);
extern void xdr_kunmap(struct xdr_buf *, size_t);
extern void xdr_shift_buf(struct xdr_buf *, size_t);
extern void _copy_from_pages(char *, struct page **, size_t, size_t);
-extern void xdr_buf_from_iov(struct iovec *, struct xdr_buf *);
+extern void xdr_buf_from_iov(struct kvec *, struct xdr_buf *);
extern int xdr_buf_subsegment(struct xdr_buf *, struct xdr_buf *, int, int);
extern int xdr_buf_read_netobj(struct xdr_buf *, struct xdr_netobj *, int);
extern int read_bytes_from_xdr_buf(struct xdr_buf *buf, int base, void *obj, int len);
struct xdr_buf *buf; /* XDR buffer to read/write */
uint32_t *end; /* end of available buffer space */
- struct iovec *iov; /* pointer to the current iovec */
+ struct kvec *iov; /* pointer to the current kvec */
};
extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p);
--- /dev/null
+/* OmniVision* camera chip driver API
+ *
+ * Copyright (c) 1999-2004 Mark McClelland
+ *
+ * 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. NO WARRANTY OF ANY KIND is expressed or implied.
+ *
+ * * OmniVision is a trademark of OmniVision Technologies, Inc. This driver
+ * is not sponsored or developed by them.
+ */
+
+#ifndef __LINUX_OVCAMCHIP_H
+#define __LINUX_OVCAMCHIP_H
+
+#include <linux/videodev.h>
+#include <linux/i2c.h>
+
+/* Remove these once they are officially defined */
+#ifndef I2C_DRIVERID_OVCAMCHIP
+ #define I2C_DRIVERID_OVCAMCHIP 0xf00f
+#endif
+#ifndef I2C_HW_SMBUS_OV511
+ #define I2C_HW_SMBUS_OV511 0xfe
+#endif
+#ifndef I2C_HW_SMBUS_OV518
+ #define I2C_HW_SMBUS_OV518 0xff
+#endif
+#ifndef I2C_HW_SMBUS_OVFX2
+ #define I2C_HW_SMBUS_OVFX2 0xfd
+#endif
+
+/* --------------------------------- */
+/* ENUMERATIONS */
+/* --------------------------------- */
+
+/* Controls */
+enum {
+ OVCAMCHIP_CID_CONT, /* Contrast */
+ OVCAMCHIP_CID_BRIGHT, /* Brightness */
+ OVCAMCHIP_CID_SAT, /* Saturation */
+ OVCAMCHIP_CID_HUE, /* Hue */
+ OVCAMCHIP_CID_EXP, /* Exposure */
+ OVCAMCHIP_CID_FREQ, /* Light frequency */
+ OVCAMCHIP_CID_BANDFILT, /* Banding filter */
+ OVCAMCHIP_CID_AUTOBRIGHT, /* Auto brightness */
+ OVCAMCHIP_CID_AUTOEXP, /* Auto exposure */
+ OVCAMCHIP_CID_BACKLIGHT, /* Back light compensation */
+ OVCAMCHIP_CID_MIRROR, /* Mirror horizontally */
+};
+
+/* Chip types */
+#define NUM_CC_TYPES 9
+enum {
+ CC_UNKNOWN,
+ CC_OV76BE,
+ CC_OV7610,
+ CC_OV7620,
+ CC_OV7620AE,
+ CC_OV6620,
+ CC_OV6630,
+ CC_OV6630AE,
+ CC_OV6630AF,
+};
+
+/* --------------------------------- */
+/* I2C ADDRESSES */
+/* --------------------------------- */
+
+#define OV7xx0_SID (0x42 >> 1)
+#define OV6xx0_SID (0xC0 >> 1)
+
+/* --------------------------------- */
+/* API */
+/* --------------------------------- */
+
+struct ovcamchip_control {
+ __u32 id;
+ __s32 value;
+};
+
+struct ovcamchip_window {
+ int x;
+ int y;
+ int width;
+ int height;
+ int format;
+ int quarter; /* Scale width and height down 2x */
+
+ /* This stuff will be removed eventually */
+ int clockdiv; /* Clock divisor setting */
+};
+
+/* Commands */
+#define OVCAMCHIP_CMD_Q_SUBTYPE _IOR (0x88, 0x00, int)
+#define OVCAMCHIP_CMD_INITIALIZE _IOW (0x88, 0x01, int)
+/* You must call OVCAMCHIP_CMD_INITIALIZE before any of commands below! */
+#define OVCAMCHIP_CMD_S_CTRL _IOW (0x88, 0x02, struct ovcamchip_control)
+#define OVCAMCHIP_CMD_G_CTRL _IOWR (0x88, 0x03, struct ovcamchip_control)
+#define OVCAMCHIP_CMD_S_MODE _IOW (0x88, 0x04, struct ovcamchip_window)
+#define OVCAMCHIP_MAX_CMD _IO (0x88, 0x3f)
+
+#endif
struct mtd_oob_buf {
uint32_t start;
uint32_t length;
- unsigned char *ptr;
+ unsigned char __user *ptr;
};
#define MTD_ABSENT 0
--- /dev/null
+/*
+ * $Id: mtd-user.h,v 1.2 2004/05/05 14:44:57 dwmw2 Exp $
+ *
+ * MTD ABI header for use by user space only.
+ */
+
+#ifndef __MTD_USER_H__
+#define __MTD_USER_H__
+
+#include <stdint.h>
+
+/* This file is blessed for inclusion by userspace */
+#include <mtd/mtd-abi.h>
+
+typedef struct mtd_info_user mtd_info_t;
+typedef struct erase_info_user erase_info_t;
+typedef struct region_info_user region_info_t;
+typedef struct nand_oobinfo nand_oobinfo_t;
+
+#endif /* __MTD_USER_H__ */
#define ICMP_INC_STATS(field) SNMP_INC_STATS(icmp_statistics, field)
#define ICMP_INC_STATS_BH(field) SNMP_INC_STATS_BH(icmp_statistics, field)
#define ICMP_INC_STATS_USER(field) SNMP_INC_STATS_USER(icmp_statistics, field)
-#define ICMP_INC_STATS_FIELD(offt) \
- (*((unsigned long *) ((void *) \
- per_cpu_ptr(icmp_statistics[!in_softirq()],\
- smp_processor_id()) + offt)))++
-#define ICMP_INC_STATS_BH_FIELD(offt) \
- (*((unsigned long *) ((void *) \
- per_cpu_ptr(icmp_statistics[0], \
- smp_processor_id()) + offt)))++
-#define ICMP_INC_STATS_USER_FIELD(offt) \
- (*((unsigned long *) ((void *) \
- per_cpu_ptr(icmp_statistics[1], \
- smp_processor_id()) + offt)))++
extern void icmp_send(struct sk_buff *skb_in, int type, int code, u32 info);
extern int icmp_rcv(struct sk_buff *skb);
#include <linux/ip.h>
+enum {
+ INET_ECN_NOT_ECT = 0,
+ INET_ECN_ECT_1 = 1,
+ INET_ECN_ECT_0 = 2,
+ INET_ECN_CE = 3,
+ INET_ECN_MASK = 3,
+};
+
static inline int INET_ECN_is_ce(__u8 dsfield)
{
- return (dsfield&3) == 3;
+ return (dsfield & INET_ECN_MASK) == INET_ECN_CE;
}
static inline int INET_ECN_is_not_ce(__u8 dsfield)
{
- return (dsfield&3) == 2;
+ return (dsfield & INET_ECN_MASK) == INET_ECN_ECT_0;
}
static inline int INET_ECN_is_capable(__u8 dsfield)
{
- return (dsfield&2);
+ return (dsfield & INET_ECN_ECT_0);
}
static inline __u8 INET_ECN_encapsulate(__u8 outer, __u8 inner)
{
- outer &= ~3;
+ outer &= ~INET_ECN_MASK;
if (INET_ECN_is_capable(inner))
- outer |= (inner & 3);
+ outer |= (inner & INET_ECN_MASK);
return outer;
}
-#define INET_ECN_xmit(sk) do { inet_sk(sk)->tos |= 2; } while (0)
-#define INET_ECN_dontxmit(sk) do { inet_sk(sk)->tos &= ~3; } while (0)
+#define INET_ECN_xmit(sk) do { inet_sk(sk)->tos |= INET_ECN_ECT_0; } while (0)
+#define INET_ECN_dontxmit(sk) \
+ do { inet_sk(sk)->tos &= ~INET_ECN_MASK; } while (0)
-#define IP6_ECN_flow_init(label) do { \
- (label) &= ~htonl(3<<20); \
+#define IP6_ECN_flow_init(label) do { \
+ (label) &= ~htonl(INET_ECN_MASK << 20); \
} while (0)
-#define IP6_ECN_flow_xmit(sk, label) do { \
- if (INET_ECN_is_capable(inet_sk(sk)->tos)) \
- (label) |= __constant_htons(2 << 4); \
+#define IP6_ECN_flow_xmit(sk, label) do { \
+ if (INET_ECN_is_capable(inet_sk(sk)->tos)) \
+ (label) |= __constant_htons(INET_ECN_ECT_0 << 4); \
} while (0)
static inline void IP_ECN_set_ce(struct iphdr *iph)
u32 check = iph->check;
check += __constant_htons(0xFFFE);
iph->check = check + (check>=0xFFFF);
- iph->tos |= 1;
+ iph->tos |= INET_ECN_CE;
}
static inline void IP_ECN_clear(struct iphdr *iph)
{
- iph->tos &= ~3;
+ iph->tos &= ~INET_ECN_MASK;
}
struct ipv6hdr;
static inline void IP6_ECN_set_ce(struct ipv6hdr *iph)
{
- *(u32*)iph |= htonl(1<<20);
+ *(u32*)iph |= htonl(INET_ECN_CE << 20);
}
static inline void IP6_ECN_clear(struct ipv6hdr *iph)
{
- *(u32*)iph &= ~htonl(3<<20);
+ *(u32*)iph &= ~htonl(INET_ECN_MASK << 20);
}
#define ip6_get_dsfield(iph) ((ntohs(*(u16*)(iph)) >> 4) & 0xFF)
--- /dev/null
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Checksumming functions for IPv6
+ *
+ * Authors: Jorge Cwik, <jorge@laser.satlink.net>
+ * Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ * Borrows very liberally from tcp.c and ip.c, see those
+ * files for more names.
+ *
+ * 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.
+ */
+
+/*
+ * Fixes:
+ *
+ * Ralf Baechle : generic ipv6 checksum
+ * <ralf@waldorf-gmbh.de>
+ */
+
+#ifndef _CHECKSUM_IPV6_H
+#define _CHECKSUM_IPV6_H
+
+#include <asm/types.h>
+#include <asm/byteorder.h>
+#include <net/ip.h>
+#include <asm/checksum.h>
+#include <linux/in6.h>
+
+#ifndef _HAVE_ARCH_IPV6_CSUM
+
+static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr,
+ struct in6_addr *daddr,
+ __u16 len,
+ unsigned short proto,
+ unsigned int csum)
+{
+
+ int carry;
+ __u32 ulen;
+ __u32 uproto;
+
+ csum += saddr->s6_addr32[0];
+ carry = (csum < saddr->s6_addr32[0]);
+ csum += carry;
+
+ csum += saddr->s6_addr32[1];
+ carry = (csum < saddr->s6_addr32[1]);
+ csum += carry;
+
+ csum += saddr->s6_addr32[2];
+ carry = (csum < saddr->s6_addr32[2]);
+ csum += carry;
+
+ csum += saddr->s6_addr32[3];
+ carry = (csum < saddr->s6_addr32[3]);
+ csum += carry;
+
+ csum += daddr->s6_addr32[0];
+ carry = (csum < daddr->s6_addr32[0]);
+ csum += carry;
+
+ csum += daddr->s6_addr32[1];
+ carry = (csum < daddr->s6_addr32[1]);
+ csum += carry;
+
+ csum += daddr->s6_addr32[2];
+ carry = (csum < daddr->s6_addr32[2]);
+ csum += carry;
+
+ csum += daddr->s6_addr32[3];
+ carry = (csum < daddr->s6_addr32[3]);
+ csum += carry;
+
+ ulen = htonl((__u32) len);
+ csum += ulen;
+ carry = (csum < ulen);
+ csum += carry;
+
+ uproto = htonl(proto);
+ csum += uproto;
+ carry = (csum < uproto);
+ csum += carry;
+
+ return csum_fold(csum);
+}
+
+#endif
+#endif
return(irlap_is_primary(self->lsap->lap->irlap));
}
-extern struct irttp_cb *irttp;
-
#endif /* IRTTP_H */
int write,
struct file * filp,
void __user *buffer,
- size_t *lenp);
+ size_t *lenp,
+ loff_t *ppos);
#endif
extern void inet6_ifinfo_notify(int event,
* nr_node & nr_neigh lists, refcounting and locking
*********************************************************************/
-extern struct hlist_head nr_node_list;
-extern struct hlist_head nr_neigh_list;
-
#define nr_node_hold(__nr_node) \
atomic_inc(&((__nr_node)->refcount))
--- /dev/null
+#ifndef __NET_PKT_ACT_H
+#define __NET_PKT_ACT_H
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <net/sock.h>
+#include <net/pkt_sched.h>
+
+#define tca_st(val) (struct tcf_##val *)
+#define PRIV(a,name) ( tca_st(name) (a)->priv)
+
+#if 0 /* control */
+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
+#else
+#define DPRINTK(format,args...)
+#endif
+
+#if 0 /* data */
+#define D2PRINTK(format,args...) printk(KERN_DEBUG format,##args)
+#else
+#define D2PRINTK(format,args...)
+#endif
+
+static __inline__ unsigned
+tcf_hash(u32 index)
+{
+ return index & MY_TAB_MASK;
+}
+
+/* probably move this from being inline
+ * and put into act_generic
+*/
+static inline void
+tcf_hash_destroy(struct tcf_st *p)
+{
+ unsigned h = tcf_hash(p->index);
+ struct tcf_st **p1p;
+
+ for (p1p = &tcf_ht[h]; *p1p; p1p = &(*p1p)->next) {
+ if (*p1p == p) {
+ write_lock_bh(&tcf_t_lock);
+ *p1p = p->next;
+ write_unlock_bh(&tcf_t_lock);
+#ifdef CONFIG_NET_ESTIMATOR
+ qdisc_kill_estimator(&p->stats);
+#endif
+ kfree(p);
+ return;
+ }
+ }
+ BUG_TRAP(0);
+}
+
+static inline int
+tcf_hash_release(struct tcf_st *p, int bind )
+{
+ int ret = 0;
+ if (p) {
+ if (bind) {
+ p->bindcnt--;
+ }
+ p->refcnt--;
+ if(p->bindcnt <=0 && p->refcnt <= 0) {
+ tcf_hash_destroy(p);
+ ret = 1;
+ }
+ }
+ return ret;
+}
+
+static __inline__ int
+tcf_dump_walker(struct sk_buff *skb, struct netlink_callback *cb,
+ struct tc_action *a)
+{
+ struct tcf_st *p;
+ int err =0, index = -1,i= 0, s_i = 0, n_i = 0;
+ struct rtattr *r ;
+
+ read_lock(&tcf_t_lock);
+
+ s_i = cb->args[0];
+
+ for (i = 0; i < MY_TAB_SIZE; i++) {
+ p = tcf_ht[tcf_hash(i)];
+
+ for (; p; p = p->next) {
+ index++;
+ if (index < s_i)
+ continue;
+ a->priv = p;
+ a->order = n_i;
+ r = (struct rtattr*) skb->tail;
+ RTA_PUT(skb, a->order, 0, NULL);
+ err = tcf_action_dump_1(skb, a, 0, 0);
+ if (0 > err) {
+ index--;
+ skb_trim(skb, (u8*)r - skb->data);
+ goto done;
+ }
+ r->rta_len = skb->tail - (u8*)r;
+ n_i++;
+ if (n_i >= TCA_ACT_MAX_PRIO) {
+ goto done;
+ }
+ }
+ }
+done:
+ read_unlock(&tcf_t_lock);
+ if (n_i)
+ cb->args[0] += n_i;
+ return n_i;
+
+rtattr_failure:
+ skb_trim(skb, (u8*)r - skb->data);
+ goto done;
+}
+
+static __inline__ int
+tcf_del_walker(struct sk_buff *skb, struct tc_action *a)
+{
+ struct tcf_st *p, *s_p;
+ struct rtattr *r ;
+ int i= 0, n_i = 0;
+
+ r = (struct rtattr*) skb->tail;
+ RTA_PUT(skb, a->order, 0, NULL);
+ RTA_PUT(skb, TCA_KIND, IFNAMSIZ, a->ops->kind);
+ for (i = 0; i < MY_TAB_SIZE; i++) {
+ p = tcf_ht[tcf_hash(i)];
+
+ while (p != NULL) {
+ s_p = p->next;
+ if (ACT_P_DELETED == tcf_hash_release(p, 0)) {
+ module_put(a->ops->owner);
+ }
+ n_i++;
+ p = s_p;
+ }
+ }
+ RTA_PUT(skb, TCA_FCNT, 4, &n_i);
+ r->rta_len = skb->tail - (u8*)r;
+
+ return n_i;
+rtattr_failure:
+ skb_trim(skb, (u8*)r - skb->data);
+ return -EINVAL;
+}
+
+static __inline__ int
+tcf_generic_walker(struct sk_buff *skb, struct netlink_callback *cb, int type,
+ struct tc_action *a)
+{
+ if (type == RTM_DELACTION) {
+ return tcf_del_walker(skb,a);
+ } else if (type == RTM_GETACTION) {
+ return tcf_dump_walker(skb,cb,a);
+ } else {
+ printk("tcf_generic_walker: unknown action %d\n",type);
+ return -EINVAL;
+ }
+}
+
+static __inline__ struct tcf_st *
+tcf_hash_lookup(u32 index)
+{
+ struct tcf_st *p;
+
+ read_lock(&tcf_t_lock);
+ for (p = tcf_ht[tcf_hash(index)]; p; p = p->next) {
+ if (p->index == index)
+ break;
+ }
+ read_unlock(&tcf_t_lock);
+ return p;
+}
+
+static __inline__ u32
+tcf_hash_new_index(void)
+{
+ do {
+ if (++idx_gen == 0)
+ idx_gen = 1;
+ } while (tcf_hash_lookup(idx_gen));
+
+ return idx_gen;
+}
+
+
+static inline int
+tcf_hash_search(struct tc_action *a, u32 index)
+{
+ struct tcf_st *p = tcf_hash_lookup(index);
+
+ if (p != NULL) {
+ a->priv = p;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+#ifdef CONFIG_NET_ACT_INIT
+static inline struct tcf_st *
+tcf_hash_check(struct tc_st *parm, struct tc_action *a, int ovr, int bind)
+{
+ struct tcf_st *p = NULL;
+ if (parm->index && (p = tcf_hash_lookup(parm->index)) != NULL) {
+ spin_lock(&p->lock);
+ if (bind) {
+ p->bindcnt++;
+ p->refcnt++;
+ }
+ spin_unlock(&p->lock);
+ a->priv = (void *) p;
+ }
+ return p;
+}
+
+static inline struct tcf_st *
+tcf_hash_create(struct tc_st *parm, struct rtattr *est, struct tc_action *a, int size, int ovr, int bind)
+{
+ unsigned h;
+ struct tcf_st *p = NULL;
+
+ p = kmalloc(size, GFP_KERNEL);
+ if (p == NULL)
+ return p;
+
+ memset(p, 0, size);
+ p->refcnt = 1;
+
+ if (bind) {
+ p->bindcnt = 1;
+ }
+
+ spin_lock_init(&p->lock);
+ p->stats_lock = &p->lock;
+ p->index = parm->index ? : tcf_hash_new_index();
+ p->tm.install = jiffies;
+ p->tm.lastuse = jiffies;
+#ifdef CONFIG_NET_ESTIMATOR
+ if (est) {
+ qdisc_new_estimator(&p->stats, p->stats_lock, est);
+ }
+#endif
+ h = tcf_hash(p->index);
+ write_lock_bh(&tcf_t_lock);
+ p->next = tcf_ht[h];
+ tcf_ht[h] = p;
+ write_unlock_bh(&tcf_t_lock);
+
+ a->priv = (void *) p;
+ return p;
+}
+
+static inline struct tcf_st *
+tcf_hash_init(struct tc_st *parm, struct rtattr *est, struct tc_action *a, int size, int ovr, int bind)
+{
+ struct tcf_st *p;
+ p = tcf_hash_check (parm,a,ovr,bind);
+ if (NULL == p) {
+ return tcf_hash_create(parm, est, a, size, ovr, bind);
+ }
+}
+
+#endif
+
+#endif
SCTP_CMD_REPORT_FWDTSN, /* Report new cumulative TSN Ack. */
SCTP_CMD_PROCESS_FWDTSN, /* Skips were reported, so process further. */
SCTP_CMD_CLEAR_INIT_TAG, /* Clears association peer's inittag. */
+ SCTP_CMD_DEL_NON_PRIMARY, /* Removes non-primary peer transports. */
+ SCTP_CMD_T3_RTX_TIMERS_STOP, /* Stops T3-rtx pending timers */
+ SCTP_CMD_FORCE_PRIM_RETRAN, /* Forces retrans. over primary path. */
SCTP_CMD_LAST
} sctp_verb_t;
SCTP_IERROR_BAD_TAG,
SCTP_IERROR_BIG_GAP,
SCTP_IERROR_DUP_TSN,
+ SCTP_IERROR_HIGH_TSN,
+ SCTP_IERROR_IGNORE_TSN,
+ SCTP_IERROR_NO_DATA,
+ SCTP_IERROR_BAD_STREAM,
} sctp_ierror_t;
const struct sctp_chunk *chunk,
sctp_cmd_seq_t *commands,
struct sctp_chunk *err_chunk);
+int sctp_eat_data(const struct sctp_association *asoc,
+ struct sctp_chunk *chunk,
+ sctp_cmd_seq_t *commands);
/* 3rd level prototypes */
__u32 sctp_generate_tag(const struct sctp_endpoint *);
extern void udp_err(struct sk_buff *, u32);
-extern int udp_connect(struct sock *sk,
- struct sockaddr *usin, int addr_len);
extern int udp_sendmsg(struct kiocb *iocb, struct sock *sk,
struct msghdr *msg, size_t len);
--- /dev/null
+#ifndef _SCSI_SCSI_DBG_H
+#define _SCSI_SCSI_DBG_H
+
+struct scsi_cmnd;
+struct scsi_request;
+
+extern void scsi_print_command(struct scsi_cmnd *);
+extern void __scsi_print_command(unsigned char *);
+extern void scsi_print_sense(const char *, struct scsi_cmnd *);
+extern void scsi_print_req_sense(const char *, struct scsi_request *);
+extern void scsi_print_driverbyte(int);
+extern void scsi_print_hostbyte(int);
+extern void scsi_print_status(unsigned char);
+extern int scsi_print_msg(const unsigned char *);
+extern const char *scsi_sense_key_string(unsigned char);
+extern const char *scsi_extd_sense_format(unsigned char, unsigned char);
+
+#endif /* _SCSI_SCSI_DBG_H */
int (*release) (snd_info_entry_t * entry,
unsigned short mode, void *file_private_data);
long (*read) (snd_info_entry_t *entry, void *file_private_data,
- struct file * file, char __user *buf, long count);
+ struct file * file, char __user *buf,
+ unsigned long count, unsigned long pos);
long (*write) (snd_info_entry_t *entry, void *file_private_data,
- struct file * file, const char __user *buf, long count);
+ struct file * file, const char __user *buf,
+ unsigned long count, unsigned long pos);
long long (*llseek) (snd_info_entry_t *entry, void *file_private_data,
struct file * file, long long offset, int orig);
unsigned int (*poll) (snd_info_entry_t *entry, void *file_private_data,
/*
* FIXME
* Ugh, we don't have PCI space, so map readb() and friends to use Zorro space
- * for MMIO accesses. This should make clgenfb work again on Amiga
+ * for MMIO accesses. This should make cirrusfb work again on Amiga
*/
+#undef inb_p
+#undef inw_p
+#undef outb_p
+#undef outw
+#undef readb
+#undef writeb
+#undef writew
#define inb_p(port) 0
#define inw_p(port) 0
#define outb_p(port, val) do { } while (0)
--- /dev/null
+/*
+ * drivers/power/smp.c - Functions for stopping other CPUs.
+ *
+ * Copyright 2004 Pavel Machek <pavel@suse.cz>
+ * Copyright (C) 2002-2003 Nigel Cunningham <ncunningham@clear.net.nz>
+ *
+ * This file is released under the GPLv2.
+ */
+
+#undef DEBUG
+
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/suspend.h>
+#include <linux/module.h>
+#include <asm/atomic.h>
+#include <asm/tlbflush.h>
+
+static atomic_t cpu_counter, freeze;
+
+
+static void smp_pause(void * data)
+{
+ struct saved_context ctxt;
+ __save_processor_state(&ctxt);
+ printk("Sleeping in:\n");
+ dump_stack();
+ atomic_inc(&cpu_counter);
+ while (atomic_read(&freeze)) {
+ /* FIXME: restore takes place at random piece inside this.
+ This should probably be written in assembly, and
+ preserve general-purpose registers, too
+
+ What about stack? We may need to move to new stack here.
+
+ This should better be ran with interrupts disabled.
+ */
+ cpu_relax();
+ barrier();
+ }
+ atomic_dec(&cpu_counter);
+ __restore_processor_state(&ctxt);
+}
+
+cpumask_t oldmask;
+
+void disable_nonboot_cpus(void)
+{
+ printk("Freezing CPUs (at %d)", smp_processor_id());
+ oldmask = current->cpus_allowed;
+ set_cpus_allowed(current, cpumask_of_cpu(0));
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ);
+ printk("...");
+ BUG_ON(smp_processor_id() != 0);
+
+ /* FIXME: for this to work, all the CPUs must be running
+ * "idle" thread (or we deadlock). Is that guaranteed? */
+
+ atomic_set(&cpu_counter, 0);
+ atomic_set(&freeze, 1);
+ smp_call_function(smp_pause, NULL, 0, 0);
+ while (atomic_read(&cpu_counter) < (num_online_cpus() - 1)) {
+ cpu_relax();
+ barrier();
+ }
+ printk("ok\n");
+}
+
+void enable_nonboot_cpus(void)
+{
+ printk("Restarting CPUs");
+ atomic_set(&freeze, 0);
+ while (atomic_read(&cpu_counter)) {
+ cpu_relax();
+ barrier();
+ }
+ printk("...");
+ set_cpus_allowed(current, oldmask);
+ schedule();
+ printk("ok\n");
+
+}
+
+
--- /dev/null
+/*
+ * linux/lib/crc-ccitt.c
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/crc-ccitt.h>
+
+/*
+ * This mysterious table is just the CRC of each possible byte. It can be
+ * computed using the standard bit-at-a-time methods. The polynomial can
+ * be seen in entry 128, 0x8408. This corresponds to x^0 + x^5 + x^12.
+ * Add the implicit x^16, and you have the standard CRC-CCITT.
+ */
+u16 const crc_ccitt_table[256] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+EXPORT_SYMBOL(crc_ccitt_table);
+
+/**
+ * crc_ccitt - recompute the CRC for the data buffer
+ * @crc - previous CRC value
+ * @buffer - data pointer
+ * @len - number of bytes in the buffer
+ */
+u16 crc_ccitt(u16 crc, u8 const *buffer, size_t len)
+{
+ while (len--)
+ crc = crc_ccitt_byte(crc, *buffer++);
+ return crc;
+}
+EXPORT_SYMBOL(crc_ccitt);
+
+MODULE_DESCRIPTION("CRC-CCITT calculations");
+MODULE_LICENSE("GPL");
* process policy.
* default Allocate on the local node first, or when on a VMA
* use the process policy. This is what Linux always did
- * in a NUMA aware kernel and still does by, ahem, default.
+ * in a NUMA aware kernel and still does by, ahem, default.
*
* The process policy is applied for most non interrupt memory allocations
* in that process' context. Interrupts ignore the policies and always
/* Check if all specified nodes are online */
static int nodes_online(unsigned long *nodes)
{
- DECLARE_BITMAP(offline, MAX_NUMNODES);
+ DECLARE_BITMAP(online2, MAX_NUMNODES);
- bitmap_copy(offline, node_online_map, MAX_NUMNODES);
- if (bitmap_empty(offline, MAX_NUMNODES))
- set_bit(0, offline);
- bitmap_complement(offline, MAX_NUMNODES);
- bitmap_and(offline, offline, nodes, MAX_NUMNODES);
- if (!bitmap_empty(offline, MAX_NUMNODES))
+ bitmap_copy(online2, node_online_map, MAX_NUMNODES);
+ if (bitmap_empty(online2, MAX_NUMNODES))
+ set_bit(0, online2);
+ if (!bitmap_subset(nodes, online2, MAX_NUMNODES))
return -EINVAL;
return 0;
}
unsigned long endmask;
--maxnode;
+ bitmap_zero(nodes, MAX_NUMNODES);
+ if (maxnode == 0 || !nmask)
+ return 0;
+
nlongs = BITS_TO_LONGS(maxnode);
if ((maxnode % BITS_PER_LONG) == 0)
endmask = ~0UL;
/* When the user specified more nodes than supported just check
if the non supported part is all zero. */
- if (nmask && nlongs > BITS_TO_LONGS(MAX_NUMNODES)) {
+ if (nlongs > BITS_TO_LONGS(MAX_NUMNODES)) {
for (k = BITS_TO_LONGS(MAX_NUMNODES); k < nlongs; k++) {
unsigned long t;
if (get_user(t, nmask + k))
endmask = ~0UL;
}
- bitmap_zero(nodes, MAX_NUMNODES);
- if (nmask && copy_from_user(nodes, nmask, nlongs*sizeof(unsigned long)))
+ if (copy_from_user(nodes, nmask, nlongs*sizeof(unsigned long)))
return -EFAULT;
nodes[nlongs-1] &= endmask;
return mpol_check_policy(mode, nodes);
/* Allocate a page in interleaved policy.
Own path because it needs to do special accounting. */
-static struct page *alloc_page_interleave(unsigned gfp, unsigned nid)
+static struct page *alloc_page_interleave(unsigned gfp, unsigned order, unsigned nid)
{
struct zonelist *zl;
struct page *page;
BUG_ON(!test_bit(nid, node_online_map));
zl = NODE_DATA(nid)->node_zonelists + (gfp & GFP_ZONEMASK);
- page = __alloc_pages(gfp, 0, zl);
+ page = __alloc_pages(gfp, order, zl);
if (page && page_zone(page) == zl->zones[0]) {
zl->zones[0]->pageset[get_cpu()].interleave_hit++;
put_cpu();
/* fall back to process interleaving */
nid = interleave_nodes(pol);
}
- return alloc_page_interleave(gfp, nid);
+ return alloc_page_interleave(gfp, 0, nid);
}
return __alloc_pages(gfp, 0, zonelist_policy(gfp, pol));
}
* alloc_pages_current - Allocate pages.
*
* @gfp:
- * %GFP_USER user allocation,
+ * %GFP_USER user allocation,
* %GFP_KERNEL kernel allocation,
* %GFP_HIGHMEM highmem allocation,
* %GFP_FS don't call back into a file system.
if (!pol || in_interrupt())
pol = &default_policy;
- if (pol->policy == MPOL_INTERLEAVE && order == 0)
- return alloc_page_interleave(gfp, interleave_nodes(pol));
+ if (pol->policy == MPOL_INTERLEAVE)
+ return alloc_page_interleave(gfp, order, interleave_nodes(pol));
return __alloc_pages(gfp, order, zonelist_policy(gfp, pol));
}
EXPORT_SYMBOL(alloc_pages_current);
up(&p->sem);
}
-static __init int numa_policy_init(void)
+/* assumes fs == KERNEL_DS */
+void __init numa_policy_init(void)
{
policy_cache = kmem_cache_create("numa_policy",
sizeof(struct mempolicy),
sn_cache = kmem_cache_create("shared_policy_node",
sizeof(struct sp_node),
0, SLAB_PANIC, NULL, NULL);
- return 0;
+
+ /* Set interleaving policy for system init. This way not all
+ the data structures allocated at system boot end up in node zero. */
+
+ if (sys_set_mempolicy(MPOL_INTERLEAVE, node_online_map, MAX_NUMNODES) < 0)
+ printk("numa_policy_init: interleaving failed\n");
+}
+
+/* Reset policy of current process to default.
+ * Assumes fs == KERNEL_DS */
+void numa_default_policy(void)
+{
+ sys_set_mempolicy(MPOL_DEFAULT, NULL, 0);
}
-module_init(numa_policy_init);
/*
* This file contains the default values for the opereation of the
* Linux VM subsystem. Fine-tuning documentation can be found in
- * linux/Documentation/sysctl/vm.txt.
+ * Documentation/sysctl/vm.txt.
* Started 18.12.91
* Swap aging added 23.2.95, Stephen Tweedie.
* Buffermem limits added 12.3.98, Rik van Riel.
/* Global VLAN variables */
/* Our listing of VLAN group(s) */
-struct vlan_group *vlan_group_hash[VLAN_GRP_HASH_SIZE];
-spinlock_t vlan_group_lock = SPIN_LOCK_UNLOCKED;
+struct hlist_head vlan_group_hash[VLAN_GRP_HASH_SIZE];
#define vlan_grp_hashfn(IDX) ((((IDX) >> VLAN_GRP_HASH_SHIFT) ^ (IDX)) & VLAN_GRP_HASH_MASK)
static char vlan_fullname[] = "802.1Q VLAN Support";
.func = vlan_skb_recv, /* VLAN receive method */
};
+/* Bits of netdev state that are propogated from real device to virtual */
+#define VLAN_LINK_STATE_MASK \
+ ((1<<__LINK_STATE_PRESENT)|(1<<__LINK_STATE_NOCARRIER))
+
/* End of global variables definitions. */
/*
* references left.
*/
for (i = 0; i < VLAN_GRP_HASH_SIZE; i++) {
- if (vlan_group_hash[i] != NULL)
- BUG();
+ BUG_ON(!hlist_empty(&vlan_group_hash[i]));
}
vlan_proc_cleanup();
module_init(vlan_proto_init);
module_exit(vlan_cleanup_module);
-/* Must be invoked with vlan_group_lock held. */
+/* Must be invoked with RCU read lock (no preempt) */
static struct vlan_group *__vlan_find_group(int real_dev_ifindex)
{
struct vlan_group *grp;
+ struct hlist_node *n;
+ int hash = vlan_grp_hashfn(real_dev_ifindex);
- for (grp = vlan_group_hash[vlan_grp_hashfn(real_dev_ifindex)];
- grp != NULL;
- grp = grp->next) {
+ hlist_for_each_entry_rcu(grp, n, &vlan_group_hash[hash], hlist) {
if (grp->real_dev_ifindex == real_dev_ifindex)
- break;
+ return grp;
}
- return grp;
-}
-
-/* Must hold vlan_group_lock. */
-static void __grp_hash(struct vlan_group *grp)
-{
- struct vlan_group **head;
-
- head = &vlan_group_hash[vlan_grp_hashfn(grp->real_dev_ifindex)];
- grp->next = *head;
- *head = grp;
-}
-
-/* Must hold vlan_group_lock. */
-static void __grp_unhash(struct vlan_group *grp)
-{
- struct vlan_group *next, **pprev;
-
- pprev = &vlan_group_hash[vlan_grp_hashfn(grp->real_dev_ifindex)];
- next = *pprev;
- while (next != grp) {
- pprev = &next->next;
- next = *pprev;
- }
- *pprev = grp->next;
+ return NULL;
}
/* Find the protocol handler. Assumes VID < VLAN_VID_MASK.
*
- * Must be invoked with vlan_group_lock held.
+ * Must be invoked with RCU read lock (no preempt)
*/
struct net_device *__find_vlan_dev(struct net_device *real_dev,
unsigned short VID)
return NULL;
}
+static void vlan_rcu_free(struct rcu_head *rcu)
+{
+ kfree(container_of(rcu, struct vlan_group, rcu));
+}
+
+
/* This returns 0 if everything went fine.
* It will return 1 if the group was killed as a result.
* A negative return indicates failure.
if (vlan_id >= VLAN_VID_MASK)
return -EINVAL;
- spin_lock_bh(&vlan_group_lock);
+ ASSERT_RTNL();
grp = __vlan_find_group(real_dev_ifindex);
- spin_unlock_bh(&vlan_group_lock);
ret = 0;
if (real_dev->features & NETIF_F_HW_VLAN_RX)
real_dev->vlan_rx_register(real_dev, NULL);
- spin_lock_bh(&vlan_group_lock);
- __grp_unhash(grp);
- spin_unlock_bh(&vlan_group_lock);
+ hlist_del_rcu(&grp->hlist);
- /* Free the group, after we have removed it
- * from the hash.
- */
- kfree(grp);
- grp = NULL;
+ /* Free the group, after all cpu's are done. */
+ call_rcu(&grp->rcu, vlan_rcu_free);
+ grp = NULL;
ret = 1;
}
}
new_dev->set_mac_address = vlan_dev_set_mac_address;
new_dev->set_multicast_list = vlan_dev_set_multicast_list;
new_dev->destructor = free_netdev;
+ new_dev->do_ioctl = vlan_dev_ioctl;
}
/* Attach a VLAN device to a mac address (ie Ethernet Card).
struct vlan_group *grp;
struct net_device *new_dev;
struct net_device *real_dev; /* the ethernet device */
- int r;
char name[IFNAMSIZ];
#ifdef VLAN_DEBUG
if (!(real_dev->flags & IFF_UP))
goto out_unlock;
- spin_lock_bh(&vlan_group_lock);
- r = (__find_vlan_dev(real_dev, VLAN_ID) != NULL);
- spin_unlock_bh(&vlan_group_lock);
-
- if (r) {
+ if (__find_vlan_dev(real_dev, VLAN_ID) != NULL) {
/* was already registered. */
printk(VLAN_DBG "%s: ALREADY had VLAN registered\n", __FUNCTION__);
goto out_unlock;
new_dev->flags = real_dev->flags;
new_dev->flags &= ~IFF_UP;
+ new_dev->state = real_dev->state & VLAN_LINK_STATE_MASK;
+
/* need 4 bytes for extra VLAN header info,
* hope the underlying device can handle it.
*/
/* So, got the sucker initialized, now lets place
* it into our local structure.
*/
- spin_lock_bh(&vlan_group_lock);
grp = __vlan_find_group(real_dev->ifindex);
- spin_unlock_bh(&vlan_group_lock);
/* Note, we are running under the RTNL semaphore
* so it cannot "appear" on us.
memset(grp, 0, sizeof(struct vlan_group));
grp->real_dev_ifindex = real_dev->ifindex;
- spin_lock_bh(&vlan_group_lock);
- __grp_hash(grp);
- spin_unlock_bh(&vlan_group_lock);
+ hlist_add_head_rcu(&grp->hlist,
+ &vlan_group_hash[vlan_grp_hashfn(real_dev->ifindex)]);
if (real_dev->features & NETIF_F_HW_VLAN_RX)
real_dev->vlan_rx_register(real_dev, grp);
static int vlan_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
{
- struct net_device *dev = (struct net_device *)(ptr);
- struct vlan_group *grp = NULL;
+ struct net_device *dev = ptr;
+ struct vlan_group *grp = __vlan_find_group(dev->ifindex);
int i, flgs;
- struct net_device *vlandev = NULL;
-
- spin_lock_bh(&vlan_group_lock);
- grp = __vlan_find_group(dev->ifindex);
- spin_unlock_bh(&vlan_group_lock);
+ struct net_device *vlandev;
if (!grp)
goto out;
*/
switch (event) {
- case NETDEV_CHANGEADDR:
- case NETDEV_GOING_DOWN:
- /* Ignore for now */
+ case NETDEV_CHANGE:
+ /* Propogate real device state to vlan devices */
+ flgs = dev->state & VLAN_LINK_STATE_MASK;
+ for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
+ vlandev = grp->vlan_devices[i];
+ if (!vlandev)
+ continue;
+
+ if ((vlandev->state & VLAN_LINK_STATE_MASK) != flgs) {
+ vlandev->state = (vlandev->state &~ VLAN_LINK_STATE_MASK)
+ | flgs;
+ netdev_state_change(vlandev);
+ }
+ }
break;
case NETDEV_DOWN:
ret = unregister_vlan_dev(dev,
VLAN_DEV_INFO(vlandev)->vlan_id);
- dev_put(vlandev);
unregister_netdevice(vlandev);
/* Group was destroyed? */
*
* Lan Emulation client header file
*
- * Marko Kiiskila carnil@cs.tut.fi
+ * Marko Kiiskila mkiiskila@yahoo.com
*
*/
/*
* Lec arp cache
- * Marko Kiiskila carnil@cs.tut.fi
+ * Marko Kiiskila mkiiskila@yahoo.com
*
*/
#ifndef _LEC_ARP_H
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/bitops.h>
+#include <linux/seq_file.h>
/* We are an ethernet device */
#include <linux/if_ether.h>
return 0;
}
-void atm_mpoa_disp_qos(char *page, ssize_t *len)
+/* this is buggered - we need locking for qos_head */
+void atm_mpoa_disp_qos(struct seq_file *m)
{
-
unsigned char *ip;
char ipaddr[16];
struct atm_mpoa_qos *qos;
qos = qos_head;
- *len += sprintf(page + *len, "QoS entries for shortcuts:\n");
- *len += sprintf(page + *len, "IP address\n TX:max_pcr pcr min_pcr max_cdv max_sdu\n RX:max_pcr pcr min_pcr max_cdv max_sdu\n");
+ seq_printf(m, "QoS entries for shortcuts:\n");
+ seq_printf(m, "IP address\n TX:max_pcr pcr min_pcr max_cdv max_sdu\n RX:max_pcr pcr min_pcr max_cdv max_sdu\n");
ipaddr[sizeof(ipaddr)-1] = '\0';
while (qos != NULL) {
ip = (unsigned char *)&qos->ipaddr;
sprintf(ipaddr, "%u.%u.%u.%u", NIPQUAD(ip));
- *len += sprintf(page + *len, "%u.%u.%u.%u\n %-7d %-7d %-7d %-7d %-7d\n %-7d %-7d %-7d %-7d %-7d\n",
+ seq_printf(m, "%u.%u.%u.%u\n %-7d %-7d %-7d %-7d %-7d\n %-7d %-7d %-7d %-7d %-7d\n",
NIPQUAD(ipaddr),
qos->qos.txtp.max_pcr, qos->qos.txtp.pcr, qos->qos.txtp.min_pcr, qos->qos.txtp.max_cdv, qos->qos.txtp.max_sdu,
qos->qos.rxtp.max_pcr, qos->qos.rxtp.pcr, qos->qos.rxtp.min_pcr, qos->qos.rxtp.max_cdv, qos->qos.rxtp.max_sdu);
qos = qos->next;
}
-
- return;
}
static struct net_device *find_lec_by_itfnum(int itf)
int atm_mpoa_delete_qos(struct atm_mpoa_qos *qos);
/* Display QoS entries. This is for the procfs */
-void atm_mpoa_disp_qos(char *page, ssize_t *len);
+struct seq_file;
+void atm_mpoa_disp_qos(struct seq_file *m);
#endif /* _MPC_H_ */
atmvcc->user_back = pvcc;
atmvcc->push = pppoatm_push;
atmvcc->pop = pppoatm_pop;
- (void) try_module_get(THIS_MODULE);
+ __module_get(THIS_MODULE);
return 0;
}
? -EFAULT : 0;
goto done;
case ATM_SETLOOP:
- if (__ATM_LM_XTRMT((int) (long) buf) &&
- __ATM_LM_XTLOC((int) (long) buf) >
- __ATM_LM_XTRMT((int) (long) buf)) {
+ if (__ATM_LM_XTRMT((int) (unsigned long) buf) &&
+ __ATM_LM_XTLOC((int) (unsigned long) buf) >
+ __ATM_LM_XTRMT((int) (unsigned long) buf)) {
error = -EINVAL;
goto done;
}
struct bnep_connlist_req {
__u32 cnum;
- struct bnep_conninfo *ci;
+ struct bnep_conninfo __user *ci;
};
struct bnep_proto_filter {
#define BT_DBG(D...)
#endif
-#define VERSION "1.0"
+#define VERSION "1.2"
static LIST_HEAD(bnep_session_list);
static DECLARE_RWSEM(bnep_session_sem);
static int bnep_send(struct bnep_session *s, void *data, size_t len)
{
struct socket *sock = s->sock;
- struct iovec iv = { data, len };
+ struct kvec iv = { data, len };
- s->msg.msg_iov = &iv;
- s->msg.msg_iovlen = 1;
- return sock_sendmsg(sock, &s->msg, len);
+ return kernel_sendmsg(sock, &s->msg, &iv, 1, len);
}
static int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp)
return bnep_send(s, &rsp, sizeof(rsp));
}
+#ifdef CONFIG_BT_BNEP_PROTO_FILTER
+static inline void bnep_set_default_proto_filter(struct bnep_session *s)
+{
+ /* (IPv4, ARP) */
+ s->proto_filter[0].start = htons(0x0800);
+ s->proto_filter[0].end = htons(0x0806);
+ /* (RARP, AppleTalk) */
+ s->proto_filter[1].start = htons(0x8035);
+ s->proto_filter[1].end = htons(0x80F3);
+ /* (IPX, IPv6) */
+ s->proto_filter[2].start = htons(0x8137);
+ s->proto_filter[2].end = htons(0x86DD);
+}
+#endif
+
static int bnep_ctrl_set_netfilter(struct bnep_session *s, u16 *data, int len)
{
int n;
BT_DBG("proto filter start %d end %d",
f[i].start, f[i].end);
}
+
if (i < BNEP_MAX_PROTO_FILTERS)
memset(f + i, 0, sizeof(*f));
+ if (n == 0)
+ bnep_set_default_proto_filter(s);
+
bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS);
} else {
bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED);
{
struct ethhdr *eh = (void *) skb->data;
struct socket *sock = s->sock;
- struct iovec iv[3];
+ struct kvec iv[3];
int len = 0, il = 0;
u8 type = 0;
goto send;
}
- iv[il++] = (struct iovec) { &type, 1 };
+ iv[il++] = (struct kvec) { &type, 1 };
len++;
if (!memcmp(eh->h_dest, s->eh.h_source, ETH_ALEN))
type = __bnep_tx_types[type];
switch (type) {
case BNEP_COMPRESSED_SRC_ONLY:
- iv[il++] = (struct iovec) { eh->h_source, ETH_ALEN };
+ iv[il++] = (struct kvec) { eh->h_source, ETH_ALEN };
len += ETH_ALEN;
break;
case BNEP_COMPRESSED_DST_ONLY:
- iv[il++] = (struct iovec) { eh->h_dest, ETH_ALEN };
+ iv[il++] = (struct kvec) { eh->h_dest, ETH_ALEN };
len += ETH_ALEN;
break;
}
send:
- iv[il++] = (struct iovec) { skb->data, skb->len };
+ iv[il++] = (struct kvec) { skb->data, skb->len };
len += skb->len;
/* FIXME: linearize skb */
{
- s->msg.msg_iov = iv;
- s->msg.msg_iovlen = il;
- len = sock_sendmsg(sock, &s->msg, len);
+ len = kernel_sendmsg(sock, &s->msg, iv, il, len);
}
kfree_skb(skb);
set_user_nice(current, -15);
current->flags |= PF_NOFREEZE;
- set_fs(KERNEL_DS);
-
init_waitqueue_entry(&wait, current);
add_wait_queue(sk->sk_sleep, &wait);
while (!atomic_read(&s->killed)) {
/* Set default mc filter */
set_bit(bnep_mc_hash(dev->broadcast), (ulong *) &s->mc_filter);
#endif
-
+
#ifdef CONFIG_BT_BNEP_PROTO_FILTER
/* Set default protocol filter */
-
- /* (IPv4, ARP) */
- s->proto_filter[0].start = htons(0x0800);
- s->proto_filter[0].end = htons(0x0806);
- /* (RARP, AppleTalk) */
- s->proto_filter[1].start = htons(0x8035);
- s->proto_filter[1].end = htons(0x80F3);
- /* (IPX, IPv6) */
- s->proto_filter[2].start = htons(0x8137);
- s->proto_filter[2].end = htons(0x86DD);
+ bnep_set_default_proto_filter(s);
#endif
-
+
err = register_netdev(dev);
if (err) {
goto failed;
static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len)
{
struct socket *sock = session->sock;
- struct iovec iv = { data, len };
+ struct kvec iv = { data, len };
struct msghdr msg;
BT_DBG("session %p data %p len %d", session, data, len);
return 0;
memset(&msg, 0, sizeof(msg));
- msg.msg_iovlen = 1;
- msg.msg_iov = &iv;
- return sock_sendmsg(sock, &msg, len);
+ return kernel_sendmsg(sock, &msg, &iv, 1, len);
}
static int cmtp_process_transmit(struct cmtp_session *session)
set_user_nice(current, -15);
current->flags |= PF_NOFREEZE;
- set_fs(KERNEL_DS);
-
init_waitqueue_entry(&wait, current);
add_wait_queue(sk->sk_sleep, &wait);
while (!atomic_read(&session->terminate)) {
--- /dev/null
+config BT_HIDP
+ tristate "HIDP protocol support"
+ depends on BT && BT_L2CAP
+ select INPUT
+ help
+ HIDP (Human Interface Device Protocol) is a transport layer
+ for HID reports. HIDP is required for the Bluetooth Human
+ Interface Device Profile.
+
+ Say Y here to compile HIDP support into the kernel or say M to
+ compile it as module (hidp).
+
static int hidp_send_frame(struct socket *sock, unsigned char *data, int len)
{
- struct iovec iv = { data, len };
+ struct kvec iv = { data, len };
struct msghdr msg;
BT_DBG("sock %p data %p len %d", sock, data, len);
return 0;
memset(&msg, 0, sizeof(msg));
- msg.msg_iovlen = 1;
- msg.msg_iov = &iv;
- return sock_sendmsg(sock, &msg, len);
+ return kernel_sendmsg(sock, &msg, &iv, 1, len);
}
static int hidp_process_transmit(struct hidp_session *session)
set_user_nice(current, -15);
current->flags |= PF_NOFREEZE;
- set_fs(KERNEL_DS);
-
init_waitqueue_entry(&ctrl_wait, current);
init_waitqueue_entry(&intr_wait, current);
add_wait_queue(ctrl_sk->sk_sleep, &ctrl_wait);
--- /dev/null
+/*
+ HIDP implementation for Linux Bluetooth stack (BlueZ).
+ Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation;
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+ SOFTWARE IS DISCLAIMED.
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/ioctl.h>
+#include <linux/file.h>
+#include <linux/init.h>
+#include <net/sock.h>
+
+#include "hidp.h"
+
+#ifndef CONFIG_BT_HIDP_DEBUG
+#undef BT_DBG
+#define BT_DBG(D...)
+#endif
+
+static int hidp_sock_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ BT_DBG("sock %p sk %p", sock, sk);
+
+ if (!sk)
+ return 0;
+
+ sock_orphan(sk);
+ sock_put(sk);
+
+ return 0;
+}
+
+static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *) arg;
+ struct hidp_connadd_req ca;
+ struct hidp_conndel_req cd;
+ struct hidp_connlist_req cl;
+ struct hidp_conninfo ci;
+ struct socket *csock;
+ struct socket *isock;
+ int err;
+
+ BT_DBG("cmd %x arg %lx", cmd, arg);
+
+ switch (cmd) {
+ case HIDPCONNADD:
+ if (!capable(CAP_NET_ADMIN))
+ return -EACCES;
+
+ if (copy_from_user(&ca, argp, sizeof(ca)))
+ return -EFAULT;
+
+ csock = sockfd_lookup(ca.ctrl_sock, &err);
+ if (!csock)
+ return err;
+
+ isock = sockfd_lookup(ca.intr_sock, &err);
+ if (!isock) {
+ fput(csock->file);
+ return err;
+ }
+
+ if (csock->sk->sk_state != BT_CONNECTED || isock->sk->sk_state != BT_CONNECTED) {
+ fput(csock->file);
+ fput(isock->file);
+ return -EBADFD;
+ }
+
+ err = hidp_add_connection(&ca, csock, isock);
+ if (!err) {
+ if (copy_to_user(argp, &ca, sizeof(ca)))
+ err = -EFAULT;
+ } else {
+ fput(csock->file);
+ fput(isock->file);
+ }
+
+ return err;
+
+ case HIDPCONNDEL:
+ if (!capable(CAP_NET_ADMIN))
+ return -EACCES;
+
+ if (copy_from_user(&cd, argp, sizeof(cd)))
+ return -EFAULT;
+
+ return hidp_del_connection(&cd);
+
+ case HIDPGETCONNLIST:
+ if (copy_from_user(&cl, argp, sizeof(cl)))
+ return -EFAULT;
+
+ if (cl.cnum <= 0)
+ return -EINVAL;
+
+ err = hidp_get_connlist(&cl);
+ if (!err && copy_to_user(argp, &cl, sizeof(cl)))
+ return -EFAULT;
+
+ return err;
+
+ case HIDPGETCONNINFO:
+ if (copy_from_user(&ci, argp, sizeof(ci)))
+ return -EFAULT;
+
+ err = hidp_get_conninfo(&ci);
+ if (!err && copy_to_user(argp, &ci, sizeof(ci)))
+ return -EFAULT;
+
+ return err;
+ }
+
+ return -EINVAL;
+}
+
+static struct proto_ops hidp_sock_ops = {
+ .family = PF_BLUETOOTH,
+ .owner = THIS_MODULE,
+ .release = hidp_sock_release,
+ .ioctl = hidp_sock_ioctl,
+ .bind = sock_no_bind,
+ .getname = sock_no_getname,
+ .sendmsg = sock_no_sendmsg,
+ .recvmsg = sock_no_recvmsg,
+ .poll = sock_no_poll,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .connect = sock_no_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .mmap = sock_no_mmap
+};
+
+static int hidp_sock_create(struct socket *sock, int protocol)
+{
+ struct sock *sk;
+
+ BT_DBG("sock %p", sock);
+
+ if (sock->type != SOCK_RAW)
+ return -ESOCKTNOSUPPORT;
+
+ if (!(sk = bt_sock_alloc(sock, PF_BLUETOOTH, 0, GFP_KERNEL)))
+ return -ENOMEM;
+
+ sk_set_owner(sk, THIS_MODULE);
+
+ sock->ops = &hidp_sock_ops;
+
+ sock->state = SS_UNCONNECTED;
+
+ sk->sk_destruct = NULL;
+ sk->sk_protocol = protocol;
+
+ return 0;
+}
+
+static struct net_proto_family hidp_sock_family_ops = {
+ .family = PF_BLUETOOTH,
+ .owner = THIS_MODULE,
+ .create = hidp_sock_create
+};
+
+int __init hidp_init_sockets(void)
+{
+ int err;
+
+ err = bt_sock_register(BTPROTO_HIDP, &hidp_sock_family_ops);
+ if (err < 0)
+ BT_ERR("Can't register HIDP socket");
+
+ return err;
+}
+
+void __exit hidp_cleanup_sockets(void)
+{
+ if (bt_sock_unregister(BTPROTO_HIDP) < 0)
+ BT_ERR("Can't unregister HIDP socket");
+}
goto out;
}
- dst = br_fdb_get(br, dest);
+ dst = __br_fdb_get(br, dest);
if (dst != NULL && dst->is_local) {
if (!passedup)
br_pass_frame_up(br, skb);
else
kfree_skb(skb);
- br_fdb_put(dst);
goto out;
}
if (dst != NULL) {
br_forward(dst->dst, skb);
- br_fdb_put(dst);
goto out;
}
#ifdef CONFIG_SYSCTL
static
int brnf_sysctl_call_tables(ctl_table *ctl, int write, struct file * filp,
- void __user *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
int ret;
- ret = proc_dointvec(ctl, write, filp, buffer, lenp);
+ ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
if (write && *(int *)(ctl->data))
*(int *)(ctl->data) = 1;
.read = brforward_read,
};
-
-/*
- * This is a dummy kset so bridge objects don't cause
- * hotplug events
- */
-struct subsystem bridge_subsys = {
- .kset = { .hotplug_ops = NULL },
-};
-
-void br_sysfs_init(void)
-{
- subsystem_register(&bridge_subsys);
-}
-
-void br_sysfs_fini(void)
-{
- subsystem_unregister(&bridge_subsys);
-}
-
/*
* Add entries in sysfs onto the existing network class device
* for the bridge.
kobject_set_name(&br->ifobj, SYSFS_BRIDGE_PORT_SUBDIR);
br->ifobj.ktype = NULL;
- br->ifobj.kset = &bridge_subsys.kset;
+ br->ifobj.kset = NULL;
br->ifobj.parent = brobj;
err = kobject_register(&br->ifobj);
if (copy_to_user(useraddr, ®s, sizeof(regs)))
goto out;
useraddr += offsetof(struct ethtool_regs, data);
- if (copy_to_user(useraddr, regbuf, reglen))
+ if (copy_to_user(useraddr, regbuf, regs.len))
goto out;
ret = 0;
clear_bit(LW_RUNNING, &linkwatch_flags);
rtnl_shlock();
- rtnl_exlock();
linkwatch_run_queue();
- rtnl_exunlock();
rtnl_shunlock();
}
#include <linux/module.h>
#include <linux/net.h>
#include <linux/signal.h>
+#include <linux/tcp.h>
#include <linux/wait.h>
#include <net/sock.h>
}
EXPORT_SYMBOL(sk_stream_write_space);
+
+/**
+ * sk_stream_wait_connect - Wait for a socket to get into the connected state
+ * @sk - sock to wait on
+ * @timeo_p - for how long to wait
+ *
+ * Must be called with the socket locked.
+ */
+int sk_stream_wait_connect(struct sock *sk, long *timeo_p)
+{
+ struct task_struct *tsk = current;
+ DEFINE_WAIT(wait);
+
+ while (1) {
+ if (sk->sk_err)
+ return sock_error(sk);
+ if ((1 << sk->sk_state) & ~(TCPF_SYN_SENT | TCPF_SYN_RECV))
+ return -EPIPE;
+ if (!*timeo_p)
+ return -EAGAIN;
+ if (signal_pending(tsk))
+ return sock_intr_errno(*timeo_p);
+
+ prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
+ sk->sk_write_pending++;
+ if (sk_wait_event(sk, timeo_p,
+ !((1 << sk->sk_state) &
+ ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT))))
+ break;
+ finish_wait(sk->sk_sleep, &wait);
+ sk->sk_write_pending--;
+ }
+ return 0;
+}
+
+EXPORT_SYMBOL(sk_stream_wait_connect);
+
+/**
+ * sk_stream_closing - Return 1 if we still have things to send in our buffers.
+ * @sk - socket to verify
+ */
+static inline int sk_stream_closing(struct sock *sk)
+{
+ return (1 << sk->sk_state) &
+ (TCPF_FIN_WAIT1 | TCPF_CLOSING | TCPF_LAST_ACK);
+}
+
+void sk_stream_wait_close(struct sock *sk, long timeout)
+{
+ if (timeout) {
+ DEFINE_WAIT(wait);
+
+ do {
+ prepare_to_wait(sk->sk_sleep, &wait,
+ TASK_INTERRUPTIBLE);
+ if (sk_wait_event(sk, &timeout, !sk_stream_closing(sk)))
+ break;
+ } while (!signal_pending(current) && timeout);
+
+ finish_wait(sk->sk_sleep, &wait);
+ }
+}
+
+EXPORT_SYMBOL(sk_stream_wait_close);
+
+/**
+ * sk_stream_wait_memory - Wait for more memory for a socket
+ * @sk - socket to wait for memory
+ * @timeo_p - for how long
+ */
+int sk_stream_wait_memory(struct sock *sk, long *timeo_p)
+{
+ int err = 0;
+ long vm_wait = 0;
+ long current_timeo = *timeo_p;
+ DEFINE_WAIT(wait);
+
+ if (sk_stream_memory_free(sk))
+ current_timeo = vm_wait = (net_random() % (HZ / 5)) + 2;
+
+ while (1) {
+ set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+
+ prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
+
+ if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
+ goto do_error;
+ if (!*timeo_p)
+ goto do_nonblock;
+ if (signal_pending(current))
+ goto do_interrupted;
+ clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+ if (sk_stream_memory_free(sk) && !vm_wait)
+ break;
+
+ set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
+ sk->sk_write_pending++;
+ sk_wait_event(sk, ¤t_timeo, sk_stream_memory_free(sk) &&
+ vm_wait);
+ sk->sk_write_pending--;
+
+ if (vm_wait) {
+ vm_wait -= current_timeo;
+ current_timeo = *timeo_p;
+ if (current_timeo != MAX_SCHEDULE_TIMEOUT &&
+ (current_timeo -= vm_wait) < 0)
+ current_timeo = 0;
+ vm_wait = 0;
+ }
+ *timeo_p = current_timeo;
+ }
+out:
+ finish_wait(sk->sk_sleep, &wait);
+ return err;
+
+do_error:
+ err = -EPIPE;
+ goto out;
+do_nonblock:
+ err = -EAGAIN;
+ goto out;
+do_interrupted:
+ err = sock_intr_errno(*timeo_p);
+ goto out;
+}
+
+EXPORT_SYMBOL(sk_stream_wait_memory);
+
+void sk_stream_rfree(struct sk_buff *skb)
+{
+ struct sock *sk = skb->sk;
+
+ atomic_sub(skb->truesize, &sk->sk_rmem_alloc);
+ sk->sk_forward_alloc += skb->truesize;
+}
+
+EXPORT_SYMBOL(sk_stream_rfree);
+
+int sk_stream_error(struct sock *sk, int flags, int err)
+{
+ if (err == -EPIPE)
+ err = sock_error(sk) ? : -EPIPE;
+ if (err == -EPIPE && !(flags & MSG_NOSIGNAL))
+ send_sig(SIGPIPE, current, 0);
+ return err;
+}
+
+EXPORT_SYMBOL(sk_stream_error);
+
+void __sk_stream_mem_reclaim(struct sock *sk)
+{
+ if (sk->sk_forward_alloc >= SK_STREAM_MEM_QUANTUM) {
+ atomic_sub(sk->sk_forward_alloc / SK_STREAM_MEM_QUANTUM,
+ sk->sk_prot->memory_allocated);
+ sk->sk_forward_alloc &= SK_STREAM_MEM_QUANTUM - 1;
+ if (*sk->sk_prot->memory_pressure &&
+ (atomic_read(sk->sk_prot->memory_allocated) <
+ sk->sk_prot->sysctl_mem[0]))
+ *sk->sk_prot->memory_pressure = 0;
+ }
+}
+
+EXPORT_SYMBOL(__sk_stream_mem_reclaim);
+
+int sk_stream_mem_schedule(struct sock *sk, int size, int kind)
+{
+ int amt = sk_stream_pages(size);
+
+ sk->sk_forward_alloc += amt * SK_STREAM_MEM_QUANTUM;
+ atomic_add(amt, sk->sk_prot->memory_allocated);
+
+ /* Under limit. */
+ if (atomic_read(sk->sk_prot->memory_allocated) < sk->sk_prot->sysctl_mem[0]) {
+ if (*sk->sk_prot->memory_pressure)
+ *sk->sk_prot->memory_pressure = 0;
+ return 1;
+ }
+
+ /* Over hard limit. */
+ if (atomic_read(sk->sk_prot->memory_allocated) > sk->sk_prot->sysctl_mem[2]) {
+ sk->sk_prot->enter_memory_pressure();
+ goto suppress_allocation;
+ }
+
+ /* Under pressure. */
+ if (atomic_read(sk->sk_prot->memory_allocated) > sk->sk_prot->sysctl_mem[1])
+ sk->sk_prot->enter_memory_pressure();
+
+ if (kind) {
+ if (atomic_read(&sk->sk_rmem_alloc) < sk->sk_prot->sysctl_rmem[0])
+ return 1;
+ } else if (sk->sk_wmem_queued < sk->sk_prot->sysctl_wmem[0])
+ return 1;
+
+ if (!*sk->sk_prot->memory_pressure ||
+ sk->sk_prot->sysctl_mem[2] > atomic_read(sk->sk_prot->sockets_allocated) *
+ sk_stream_pages(sk->sk_wmem_queued +
+ atomic_read(&sk->sk_rmem_alloc) +
+ sk->sk_forward_alloc))
+ return 1;
+
+suppress_allocation:
+
+ if (!kind) {
+ sk_stream_moderate_sndbuf(sk);
+
+ /* Fail only if socket is _under_ its sndbuf.
+ * In this case we cannot block, so that we have to fail.
+ */
+ if (sk->sk_wmem_queued + size >= sk->sk_sndbuf)
+ return 1;
+ }
+
+ /* Alas. Undo changes. */
+ sk->sk_forward_alloc -= amt * SK_STREAM_MEM_QUANTUM;
+ atomic_sub(amt, sk->sk_prot->memory_allocated);
+ return 0;
+}
+
+EXPORT_SYMBOL(sk_stream_mem_schedule);
+
+void sk_stream_kill_queues(struct sock *sk)
+{
+ /* First the read buffer. */
+ __skb_queue_purge(&sk->sk_receive_queue);
+
+ /* Next, the error queue. */
+ __skb_queue_purge(&sk->sk_error_queue);
+
+ /* Next, the write queue. */
+ BUG_TRAP(skb_queue_empty(&sk->sk_write_queue));
+
+ /* Account for returned memory. */
+ sk_stream_mem_reclaim(sk);
+
+ BUG_TRAP(!sk->sk_wmem_queued);
+ BUG_TRAP(!sk->sk_forward_alloc);
+
+ /* It is _impossible_ for the backlog to contain anything
+ * when we get here. All user references to this socket
+ * have gone away, only the net layer knows can touch it.
+ */
+}
+
+EXPORT_SYMBOL(sk_stream_kill_queues);
.mode = 0644,
.proc_handler = &proc_dointvec
},
-#ifdef CONFIG_NET_FASTROUTE
- {
- .ctl_name = NET_CORE_FASTROUTE,
- .procname = "netdev_fastroute",
- .data = &netdev_fastroute,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = &proc_dointvec
- },
-#endif
{
.ctl_name = NET_CORE_MSG_COST,
.procname = "message_cost",
static int dn_node_address_handler(ctl_table *table, int write,
struct file *filp,
- void __user *buffer, size_t *lenp)
+ void __user *buffer,
+ size_t *lenp, loff_t *ppos)
{
char addr[DN_ASCBUF_LEN];
size_t len;
dn_address dnaddr;
- if (!*lenp || (filp->f_pos && !write)) {
+ if (!*lenp || (*ppos && !write)) {
*lenp = 0;
return 0;
}
dn_dev_devices_on();
- filp->f_pos += len;
+ *ppos += len;
return 0;
}
return -EFAULT;
*lenp = len;
- filp->f_pos += len;
+ *ppos += len;
return 0;
}
static int dn_def_dev_handler(ctl_table *table, int write,
struct file * filp,
- void __user *buffer, size_t *lenp)
+ void __user *buffer,
+ size_t *lenp, loff_t *ppos)
{
size_t len;
struct net_device *dev;
char devname[17];
- if (!*lenp || (filp->f_pos && !write)) {
+ if (!*lenp || (*ppos && !write)) {
*lenp = 0;
return 0;
}
dev_put(dev);
return -ENODEV;
}
- filp->f_pos += *lenp;
+ *ppos += *lenp;
return 0;
}
return -EFAULT;
*lenp = len;
- filp->f_pos += len;
+ *ppos += len;
return 0;
}
--- /dev/null
+/*
+ * common UDP/RAW code
+ * Linux INET implementation
+ *
+ * Authors:
+ * Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.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/config.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/in.h>
+#include <net/sock.h>
+#include <net/tcp.h>
+#include <net/route.h>
+
+int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+{
+ struct inet_opt *inet = inet_sk(sk);
+ struct sockaddr_in *usin = (struct sockaddr_in *) uaddr;
+ struct rtable *rt;
+ u32 saddr;
+ int oif;
+ int err;
+
+
+ if (addr_len < sizeof(*usin))
+ return -EINVAL;
+
+ if (usin->sin_family != AF_INET)
+ return -EAFNOSUPPORT;
+
+ sk_dst_reset(sk);
+
+ oif = sk->sk_bound_dev_if;
+ saddr = inet->saddr;
+ if (MULTICAST(usin->sin_addr.s_addr)) {
+ if (!oif)
+ oif = inet->mc_index;
+ if (!saddr)
+ saddr = inet->mc_addr;
+ }
+ err = ip_route_connect(&rt, usin->sin_addr.s_addr, saddr,
+ RT_CONN_FLAGS(sk), oif,
+ sk->sk_protocol,
+ inet->sport, usin->sin_port, sk);
+ if (err)
+ return err;
+ if ((rt->rt_flags & RTCF_BROADCAST) && !sock_flag(sk, SOCK_BROADCAST)) {
+ ip_rt_put(rt);
+ return -EACCES;
+ }
+ if (!inet->saddr)
+ inet->saddr = rt->rt_src; /* Update source address */
+ if (!inet->rcv_saddr)
+ inet->rcv_saddr = rt->rt_src;
+ inet->daddr = rt->rt_dst;
+ inet->dport = usin->sin_port;
+ sk->sk_state = TCP_ESTABLISHED;
+ inet->id = jiffies;
+
+ sk_dst_set(sk, &rt->u.dst);
+ return(0);
+}
+
+EXPORT_SYMBOL(ip4_datagram_connect);
+
*/
#include <linux/module.h>
+#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/in.h>
* First port is set to the default port.
*/
static int ports[IP_VS_APP_MAX_PORTS] = {21, 0};
+static int ports_c;
+module_param_array(ports, int, ports_c, 0);
/*
* Debug level
*/
#ifdef CONFIG_IP_VS_DEBUG
static int debug=0;
-MODULE_PARM(debug, "i");
+module_param(debug, int, 0);
#endif
-MODULE_PARM(ports, "1-" __MODULE_STRING(IP_VS_APP_MAX_PORTS) "i");
/* Dummy variable */
static int ip_vs_ftp_pasv;
while (data <= data_limit - 6) {
if (strnicmp(data, "PASV\r\n", 6) == 0) {
/* Passive mode on */
- IP_VS_DBG(1-debug, "got PASV at %d of %d\n",
+ IP_VS_DBG(1-debug, "got PASV at %zd of %zd\n",
data - data_start,
data_limit - data_start);
cp->app_data = &ip_vs_ftp_pasv;
(*pskb)->len - tcphoff,
cp->protocol,
(*pskb)->csum);
- IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%d)\n",
+ IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n",
pp->name, tcph->check,
(char*)&(tcph->check) - (char*)tcph);
}
(*pskb)->csum);
if (udph->check == 0)
udph->check = 0xFFFF;
- IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%d)\n",
+ IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n",
pp->name, udph->check,
(char*)&(udph->check) - (char*)udph);
}
#define MAX_PORTS 8
static int ports[MAX_PORTS];
static int ports_c;
-#ifdef MODULE_PARM
MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
-#endif
static int loose;
MODULE_PARM(loose, "i");
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
MODULE_DESCRIPTION("IRC (DCC) connection tracking helper");
MODULE_LICENSE("GPL");
-#ifdef MODULE_PARM
MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
MODULE_PARM_DESC(ports, "port numbers of IRC servers");
MODULE_PARM(max_dcc_channels, "i");
MODULE_PARM_DESC(max_dcc_channels, "max number of expected DCC channels per IRC session");
MODULE_PARM(dcc_timeout, "i");
MODULE_PARM_DESC(dcc_timeout, "timeout on for unestablished DCC channels");
-#endif
static char *dccprotos[] = { "SEND ", "CHAT ", "MOVE ", "TSEND ", "SCHAT " };
#define MINMATCHLEN 5
#define MAX_PORTS 8
static int ports[MAX_PORTS];
static int ports_c;
-#ifdef MODULE_PARM
MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
MODULE_PARM_DESC(ports, "port numbers of tftp servers");
-#endif
#if 0
#define DEBUGP(format, args...) printk("%s:%s:" format, \
static int ports[MAX_PORTS];
static int ports_c;
-#ifdef MODULE_PARM
MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
-#endif
DECLARE_LOCK_EXTERN(ip_ftp_lock);
MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
MODULE_DESCRIPTION("IRC (DCC) NAT helper");
MODULE_LICENSE("GPL");
-#ifdef MODULE_PARM
MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
MODULE_PARM_DESC(ports, "port numbers of IRC servers");
-#endif
/* protects irc part of conntracks */
DECLARE_LOCK_EXTERN(ip_irc_lock);
static int ports[MAX_PORTS];
static int ports_c = 0;
-#ifdef MODULE_PARM
MODULE_PARM(ports,"1-" __MODULE_STRING(MAX_PORTS) "i");
MODULE_PARM_DESC(ports, "port numbers of tftp servers");
-#endif
#if 0
#define DEBUGP(format, args...) printk("%s:%s:" format, \
--- /dev/null
+/*
+ * iptables module to match inet_addr_type() of an ip.
+ *
+ * Copyright (c) 2004 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/ip.h>
+#include <net/route.h>
+
+#include <linux/netfilter_ipv4/ipt_addrtype.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
+MODULE_DESCRIPTION("iptables addrtype match");
+
+static inline int match_type(u_int32_t addr, u_int16_t mask)
+{
+ return !!(mask & (1 << inet_addr_type(addr)));
+}
+
+static int match(const struct sk_buff *skb, const struct net_device *in,
+ const struct net_device *out, const void *matchinfo,
+ int offset, int *hotdrop)
+{
+ const struct ipt_addrtype_info *info = matchinfo;
+ const struct iphdr *iph = skb->nh.iph;
+ int ret = 1;
+
+ if (info->source)
+ ret &= match_type(iph->saddr, info->source)^info->invert_source;
+ if (info->dest)
+ ret &= match_type(iph->daddr, info->dest)^info->invert_dest;
+
+ return ret;
+}
+
+static int checkentry(const char *tablename, const struct ipt_ip *ip,
+ void *matchinfo, unsigned int matchsize,
+ unsigned int hook_mask)
+{
+ if (matchsize != IPT_ALIGN(sizeof(struct ipt_addrtype_info))) {
+ printk(KERN_ERR "ipt_addrtype: invalid size (%u != %Zu)\n.",
+ matchsize, IPT_ALIGN(sizeof(struct ipt_addrtype_info)));
+ return 0;
+ }
+
+ return 1;
+}
+
+static struct ipt_match addrtype_match = {
+ .name = "addrtype",
+ .match = match,
+ .checkentry = checkentry,
+ .me = THIS_MODULE
+};
+
+static int __init init(void)
+{
+ return ipt_register_match(&addrtype_match);
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_match(&addrtype_match);
+}
+
+module_init(init);
+module_exit(fini);
--- /dev/null
+/* IP tables module for matching the routing realm
+ *
+ * $Id: ipt_realm.c,v 1.3 2004/03/05 13:25:40 laforge Exp $
+ *
+ * (C) 2003 by Sampsa Ranta <sampsa@netsonic.fi>
+ *
+ * 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/skbuff.h>
+#include <linux/netdevice.h>
+#include <net/route.h>
+
+#include <linux/netfilter_ipv4/ipt_realm.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+MODULE_AUTHOR("Sampsa Ranta <sampsa@netsonic.fi>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("iptables realm match");
+
+static int
+match(const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *matchinfo,
+ int offset,
+ int *hotdrop)
+{
+ const struct ipt_realm_info *info = matchinfo;
+ struct dst_entry *dst = skb->dst;
+
+ return (info->id == (dst->tclassid & info->mask)) ^ info->invert;
+}
+
+static int check(const char *tablename,
+ const struct ipt_ip *ip,
+ void *matchinfo,
+ unsigned int matchsize,
+ unsigned int hook_mask)
+{
+ if (hook_mask
+ & ~((1 << NF_IP_POST_ROUTING) | (1 << NF_IP_FORWARD) |
+ (1 << NF_IP_LOCAL_OUT) | (1 << NF_IP_LOCAL_IN))) {
+ printk("ipt_realm: only valid for POST_ROUTING, LOCAL_OUT, "
+ "LOCAL_IN or FORWARD.\n");
+ return 0;
+ }
+ if (matchsize != IPT_ALIGN(sizeof(struct ipt_realm_info))) {
+ printk("ipt_realm: invalid matchsize.\n");
+ return 0;
+ }
+ return 1;
+}
+
+static struct ipt_match realm_match = {
+ .name = "realm",
+ .match = match,
+ .checkentry = check,
+ .me = THIS_MODULE
+};
+
+static int __init init(void)
+{
+ return ipt_register_match(&realm_match);
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_match(&realm_match);
+}
+
+module_init(init);
+module_exit(fini);
;
*mssp = msstab[mssind] + 1;
- NET_INC_STATS_BH(SyncookiesSent);
+ NET_INC_STATS_BH(LINUX_MIB_SYNCOOKIESSENT);
return secure_tcp_syn_cookie(skb->nh.iph->saddr, skb->nh.iph->daddr,
skb->h.th->source, skb->h.th->dest,
if (time_after(jiffies, tp->last_synq_overflow + TCP_TIMEOUT_INIT) ||
(mss = cookie_check(skb, cookie)) == 0) {
- NET_INC_STATS_BH(SyncookiesFailed);
+ NET_INC_STATS_BH(LINUX_MIB_SYNCOOKIESFAILED);
goto out;
}
- NET_INC_STATS_BH(SyncookiesRecv);
+ NET_INC_STATS_BH(LINUX_MIB_SYNCOOKIESRECV);
req = tcp_openreq_alloc();
ret = NULL;
#include <net/inet_ecn.h>
#include <net/ip.h>
#include <net/xfrm.h>
+#include <net/icmp.h>
/* Add encapsulation header.
*
memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
}
+static int xfrm4_tunnel_check_size(struct sk_buff *skb)
+{
+ int mtu, ret = 0;
+ struct dst_entry *dst;
+ struct iphdr *iph = skb->nh.iph;
+
+ if (IPCB(skb)->flags & IPSKB_XFRM_TUNNEL_SIZE)
+ goto out;
+
+ IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE;
+
+ if (!(iph->frag_off & htons(IP_DF)))
+ goto out;
+
+ dst = skb->dst;
+ mtu = dst_pmtu(dst) - dst->header_len - dst->trailer_len;
+ if (skb->len > mtu) {
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
+ ret = -EMSGSIZE;
+ }
+out:
+ return ret;
+}
+
int xfrm4_output(struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
config IPV6_PRIVACY
bool "IPv6: Privacy Extensions (RFC 3041) support"
depends on IPV6
- select CRYPTO
- select CRYPTO_MD5
---help---
Privacy Extensions for Stateless Address Autoconfiguration in IPv6
support. With this option, additional periodically-alter
#include <net/ndisc.h>
#include <net/addrconf.h>
#include <net/transp_v6.h>
+#include <net/ip6_route.h>
#include <linux/errqueue.h>
#include <asm/uaccess.h>
+int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+{
+ struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
+ struct inet_opt *inet = inet_sk(sk);
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct in6_addr *daddr;
+ struct dst_entry *dst;
+ struct flowi fl;
+ struct ip6_flowlabel *flowlabel = NULL;
+ int addr_type;
+ int err;
+
+ if (usin->sin6_family == AF_INET) {
+ if (__ipv6_only_sock(sk))
+ return -EAFNOSUPPORT;
+ err = ip4_datagram_connect(sk, uaddr, addr_len);
+ goto ipv4_connected;
+ }
+
+ if (addr_len < SIN6_LEN_RFC2133)
+ return -EINVAL;
+
+ if (usin->sin6_family != AF_INET6)
+ return -EAFNOSUPPORT;
+
+ memset(&fl, 0, sizeof(fl));
+ if (np->sndflow) {
+ fl.fl6_flowlabel = usin->sin6_flowinfo&IPV6_FLOWINFO_MASK;
+ if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) {
+ flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
+ if (flowlabel == NULL)
+ return -EINVAL;
+ ipv6_addr_copy(&usin->sin6_addr, &flowlabel->dst);
+ }
+ }
+
+ addr_type = ipv6_addr_type(&usin->sin6_addr);
+
+ if (addr_type == IPV6_ADDR_ANY) {
+ /*
+ * connect to self
+ */
+ usin->sin6_addr.s6_addr[15] = 0x01;
+ }
+
+ daddr = &usin->sin6_addr;
+
+ if (addr_type == IPV6_ADDR_MAPPED) {
+ struct sockaddr_in sin;
+
+ if (__ipv6_only_sock(sk)) {
+ err = -ENETUNREACH;
+ goto out;
+ }
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = daddr->s6_addr32[3];
+ sin.sin_port = usin->sin6_port;
+
+ err = ip4_datagram_connect(sk,
+ (struct sockaddr*) &sin,
+ sizeof(sin));
+
+ipv4_connected:
+ if (err)
+ goto out;
+
+ ipv6_addr_set(&np->daddr, 0, 0, htonl(0x0000ffff), inet->daddr);
+
+ if (ipv6_addr_any(&np->saddr)) {
+ ipv6_addr_set(&np->saddr, 0, 0, htonl(0x0000ffff),
+ inet->saddr);
+ }
+
+ if (ipv6_addr_any(&np->rcv_saddr)) {
+ ipv6_addr_set(&np->rcv_saddr, 0, 0, htonl(0x0000ffff),
+ inet->rcv_saddr);
+ }
+ goto out;
+ }
+
+ if (addr_type&IPV6_ADDR_LINKLOCAL) {
+ if (addr_len >= sizeof(struct sockaddr_in6) &&
+ usin->sin6_scope_id) {
+ if (sk->sk_bound_dev_if &&
+ sk->sk_bound_dev_if != usin->sin6_scope_id) {
+ err = -EINVAL;
+ goto out;
+ }
+ sk->sk_bound_dev_if = usin->sin6_scope_id;
+ if (!sk->sk_bound_dev_if &&
+ (addr_type & IPV6_ADDR_MULTICAST))
+ fl.oif = np->mcast_oif;
+ }
+
+ /* Connect to link-local address requires an interface */
+ if (!sk->sk_bound_dev_if) {
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
+ ipv6_addr_copy(&np->daddr, daddr);
+ np->flow_label = fl.fl6_flowlabel;
+
+ inet->dport = usin->sin6_port;
+
+ /*
+ * Check for a route to destination an obtain the
+ * destination cache for it.
+ */
+
+ fl.proto = sk->sk_protocol;
+ ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
+ ipv6_addr_copy(&fl.fl6_src, &np->saddr);
+ fl.oif = sk->sk_bound_dev_if;
+ fl.fl_ip_dport = inet->dport;
+ fl.fl_ip_sport = inet->sport;
+
+ if (!fl.oif && (addr_type&IPV6_ADDR_MULTICAST))
+ fl.oif = np->mcast_oif;
+
+ if (flowlabel) {
+ if (flowlabel->opt && flowlabel->opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *) flowlabel->opt->srcrt;
+ ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
+ }
+ } else if (np->opt && np->opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt;
+ ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
+ }
+
+ err = ip6_dst_lookup(sk, &dst, &fl);
+ if (err)
+ goto out;
+
+ /* source address lookup done in ip6_dst_lookup */
+
+ if (ipv6_addr_any(&np->saddr))
+ ipv6_addr_copy(&np->saddr, &fl.fl6_src);
+
+ if (ipv6_addr_any(&np->rcv_saddr)) {
+ ipv6_addr_copy(&np->rcv_saddr, &fl.fl6_src);
+ inet->rcv_saddr = LOOPBACK4_IPV6;
+ }
+
+ ip6_dst_store(sk, dst,
+ !ipv6_addr_cmp(&fl.fl6_dst, &np->daddr) ?
+ &np->daddr : NULL);
+
+ sk->sk_state = TCP_ESTABLISHED;
+out:
+ fl6_sock_release(flowlabel);
+ return err;
+}
+
void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
u16 port, u32 info, u8 *payload)
{
static struct timer_list ip6_fib_timer = TIMER_INITIALIZER(fib6_run_gc, 0, 0);
-static struct fib6_walker_t fib6_walker_list = {
+struct fib6_walker_t fib6_walker_list = {
.prev = &fib6_walker_list,
.next = &fib6_walker_list,
};
struct xfrm_state *x;
int xfrm_nr = 0;
int decaps = 0;
- int nexthdr = 0;
- u8 *prevhdr = NULL;
+ int nexthdr;
+ unsigned int nhoff;
- ip6_find_1stfragopt(skb, &prevhdr);
- nexthdr = *prevhdr;
- *nhoffp = prevhdr - skb->nh.raw;
+ nhoff = *nhoffp;
+ nexthdr = skb->nh.raw[nhoff];
if ((err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0)
goto drop;
if (nexthdr <= 0)
goto drop_unlock;
+ skb->nh.raw[nhoff] = nexthdr;
+
if (x->props.replay_window)
xfrm_replay_advance(x, seq);
--- /dev/null
+/*
+ * xfrm6_output.c - Common IPsec encapsulation code for IPv6.
+ * Copyright (C) 2002 USAGI/WIDE Project
+ * Copyright (c) 2004 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * 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/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/icmpv6.h>
+#include <net/inet_ecn.h>
+#include <net/ipv6.h>
+#include <net/xfrm.h>
+
+/* Add encapsulation header.
+ *
+ * In transport mode, the IP header and mutable extension headers will be moved
+ * forward to make space for the encapsulation header.
+ *
+ * In tunnel mode, the top IP header will be constructed per RFC 2401.
+ * The following fields in it shall be filled in by x->type->output:
+ * payload_len
+ *
+ * On exit, skb->h will be set to the start of the encapsulation header to be
+ * filled in by x->type->output and skb->nh will be set to the nextheader field
+ * of the extension header directly preceding the encapsulation header, or in
+ * its absence, that of the top IP header. The value of skb->data will always
+ * point to the top IP header.
+ */
+static void xfrm6_encap(struct sk_buff *skb)
+{
+ struct dst_entry *dst = skb->dst;
+ struct xfrm_state *x = dst->xfrm;
+ struct ipv6hdr *iph, *top_iph;
+
+ skb_push(skb, x->props.header_len);
+ iph = skb->nh.ipv6h;
+
+ if (!x->props.mode) {
+ u8 *prevhdr;
+ int hdr_len;
+
+ hdr_len = ip6_find_1stfragopt(skb, &prevhdr);
+ skb->nh.raw = prevhdr - x->props.header_len;
+ skb->h.raw = skb->data + hdr_len;
+ memmove(skb->data, iph, hdr_len);
+ return;
+ }
+
+ skb->nh.raw = skb->data;
+ top_iph = skb->nh.ipv6h;
+ skb->nh.raw = &top_iph->nexthdr;
+ skb->h.ipv6h = top_iph + 1;
+
+ top_iph->version = 6;
+ top_iph->priority = iph->priority;
+ if (x->props.flags & XFRM_STATE_NOECN)
+ IP6_ECN_clear(top_iph);
+ top_iph->flow_lbl[0] = iph->flow_lbl[0];
+ top_iph->flow_lbl[1] = iph->flow_lbl[1];
+ top_iph->flow_lbl[2] = iph->flow_lbl[2];
+ top_iph->nexthdr = IPPROTO_IPV6;
+ top_iph->hop_limit = iph->hop_limit;
+ ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr);
+ ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr);
+}
+
+static int xfrm6_tunnel_check_size(struct sk_buff *skb)
+{
+ int mtu, ret = 0;
+ struct dst_entry *dst = skb->dst;
+
+ mtu = dst_pmtu(dst) - sizeof(struct ipv6hdr);
+ if (mtu < IPV6_MIN_MTU)
+ mtu = IPV6_MIN_MTU;
+
+ if (skb->len > mtu) {
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev);
+ ret = -EMSGSIZE;
+ }
+
+ return ret;
+}
+
+int xfrm6_output(struct sk_buff **pskb)
+{
+ struct sk_buff *skb = *pskb;
+ struct dst_entry *dst = skb->dst;
+ struct xfrm_state *x = dst->xfrm;
+ int err;
+
+ if (skb->ip_summed == CHECKSUM_HW) {
+ err = skb_checksum_help(pskb, 0);
+ skb = *pskb;
+ if (err)
+ goto error_nolock;
+ }
+
+ spin_lock_bh(&x->lock);
+ err = xfrm_state_check(x, skb);
+ if (err)
+ goto error;
+
+ if (x->props.mode) {
+ err = xfrm6_tunnel_check_size(skb);
+ if (err)
+ goto error;
+ }
+
+ xfrm6_encap(skb);
+
+ err = x->type->output(pskb);
+ skb = *pskb;
+ if (err)
+ goto error;
+
+ x->curlft.bytes += skb->len;
+ x->curlft.packets++;
+
+ spin_unlock_bh(&x->lock);
+
+ skb->nh.raw = skb->data;
+
+ if (!(skb->dst = dst_pop(dst))) {
+ err = -EHOSTUNREACH;
+ goto error_nolock;
+ }
+ err = NET_XMIT_BYPASS;
+
+out_exit:
+ return err;
+error:
+ spin_unlock_bh(&x->lock);
+error_nolock:
+ kfree_skb(skb);
+ goto out_exit;
+}
#include <linux/list.h>
#include <net/ip.h>
#include <net/xfrm.h>
-#include <net/icmp.h>
#include <net/ipv6.h>
+#include <net/protocol.h>
#include <linux/ipv6.h>
#include <linux/icmpv6.h>
EXPORT_SYMBOL(xfrm6_tunnel_free_spi);
-int xfrm6_tunnel_check_size(struct sk_buff *skb)
-{
- int mtu, ret = 0;
- struct dst_entry *dst = skb->dst;
-
- mtu = dst_pmtu(dst) - sizeof(struct ipv6hdr);
- if (mtu < IPV6_MIN_MTU)
- mtu = IPV6_MIN_MTU;
-
- if (skb->len > mtu) {
- icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev);
- ret = -EMSGSIZE;
- }
-
- return ret;
-}
-
-EXPORT_SYMBOL(xfrm6_tunnel_check_size);
-
static int xfrm6_tunnel_output(struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
- struct dst_entry *dst = skb->dst;
- struct xfrm_state *x = dst->xfrm;
- struct ipv6hdr *iph, *top_iph;
- int err;
-
- if ((err = xfrm6_tunnel_check_size(skb)) != 0)
- goto error_nolock;
-
- iph = skb->nh.ipv6h;
-
- top_iph = (struct ipv6hdr *)skb_push(skb, x->props.header_len);
- top_iph->version = 6;
- top_iph->priority = iph->priority;
- top_iph->flow_lbl[0] = iph->flow_lbl[0];
- top_iph->flow_lbl[1] = iph->flow_lbl[1];
- top_iph->flow_lbl[2] = iph->flow_lbl[2];
- top_iph->nexthdr = IPPROTO_IPV6;
- top_iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
- top_iph->hop_limit = iph->hop_limit;
- memcpy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr, sizeof(struct in6_addr));
- memcpy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr, sizeof(struct in6_addr));
- skb->nh.raw = skb->data;
- skb->h.raw = skb->nh.raw + sizeof(struct ipv6hdr);
-
- x->curlft.bytes += skb->len;
- x->curlft.packets++;
+ struct ipv6hdr *top_iph;
- spin_unlock_bh(&x->lock);
-
- if ((skb->dst = dst_pop(dst)) == NULL) {
- kfree_skb(skb);
- err = -EHOSTUNREACH;
- goto error_nolock;
- }
-
- return NET_XMIT_BYPASS;
+ top_iph = (struct ipv6hdr *)skb->data;
+ top_iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
-error_nolock:
- kfree_skb(skb);
- return err;
+ return 0;
}
static int xfrm6_tunnel_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb)
* us on that - Jean II */
static int do_devname(ctl_table *table, int write, struct file *filp,
- void __user *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
int ret;
- ret = proc_dostring(table, write, filp, buffer, lenp);
+ ret = proc_dostring(table, write, filp, buffer, lenp, ppos);
if (ret == 0 && write) {
struct ias_value *val;
--- /dev/null
+/*
+ * net/sched/act_api.c Packet action API.
+ *
+ * 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.
+ *
+ * Author: Jamal Hadi Salim
+ *
+ *
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <net/sock.h>
+#include <net/pkt_sched.h>
+
+#if 1 /* control */
+#define DPRINTK(format,args...) printk(KERN_DEBUG format,##args)
+#else
+#define DPRINTK(format,args...)
+#endif
+#if 0 /* data */
+#define D2PRINTK(format,args...) printk(KERN_DEBUG format,##args)
+#else
+#define D2PRINTK(format,args...)
+#endif
+
+static struct tc_action_ops *act_base = NULL;
+static rwlock_t act_mod_lock = RW_LOCK_UNLOCKED;
+
+int tcf_register_action(struct tc_action_ops *act)
+{
+ struct tc_action_ops *a, **ap;
+
+ write_lock(&act_mod_lock);
+ for (ap = &act_base; (a=*ap)!=NULL; ap = &a->next) {
+ if (act->type == a->type || (strcmp(act->kind, a->kind) == 0)) {
+ write_unlock(&act_mod_lock);
+ return -EEXIST;
+ }
+ }
+
+ act->next = NULL;
+ *ap = act;
+
+ write_unlock(&act_mod_lock);
+
+ return 0;
+}
+
+int tcf_unregister_action(struct tc_action_ops *act)
+{
+ struct tc_action_ops *a, **ap;
+ int err = -ENOENT;
+
+ write_lock(&act_mod_lock);
+ for (ap = &act_base; (a=*ap)!=NULL; ap = &a->next)
+ if(a == act)
+ break;
+
+ if (a) {
+ *ap = a->next;
+ a->next = NULL;
+ err = 0;
+ }
+ write_unlock(&act_mod_lock);
+ return err;
+}
+
+/* lookup by name */
+struct tc_action_ops *tc_lookup_action_n(char *kind)
+{
+
+ struct tc_action_ops *a = NULL;
+
+ if (kind) {
+ read_lock(&act_mod_lock);
+ for (a = act_base; a; a = a->next) {
+ if (strcmp(kind,a->kind) == 0) {
+ if (!try_module_get(a->owner)) {
+ read_unlock(&act_mod_lock);
+ return NULL;
+ }
+ break;
+ }
+ }
+ read_unlock(&act_mod_lock);
+ }
+
+ return a;
+}
+
+/* lookup by rtattr */
+struct tc_action_ops *tc_lookup_action(struct rtattr *kind)
+{
+
+ struct tc_action_ops *a = NULL;
+
+ if (kind) {
+ read_lock(&act_mod_lock);
+ for (a = act_base; a; a = a->next) {
+
+ if (strcmp((char*)RTA_DATA(kind),a->kind) == 0){
+ if (!try_module_get(a->owner)) {
+ read_unlock(&act_mod_lock);
+ return NULL;
+ }
+ break;
+ }
+ }
+ read_unlock(&act_mod_lock);
+ }
+
+ return a;
+}
+
+/* lookup by id */
+struct tc_action_ops *tc_lookup_action_id(u32 type)
+{
+ struct tc_action_ops *a = NULL;
+
+ if (type) {
+ read_lock(&act_mod_lock);
+ for (a = act_base; a; a = a->next) {
+ if (a->type == type) {
+ if (!try_module_get(a->owner)) {
+ read_unlock(&act_mod_lock);
+ return NULL;
+ }
+ break;
+ }
+ }
+ read_unlock(&act_mod_lock);
+ }
+
+ return a;
+}
+
+int tcf_action_exec(struct sk_buff *skb,struct tc_action *act)
+{
+
+ struct tc_action *a;
+ int ret = -1;
+
+ if (skb->tc_verd & TC_NCLS) {
+ skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
+ D2PRINTK("(%p)tcf_action_exec: cleared TC_NCLS in %s out %s\n",skb,skb->input_dev?skb->input_dev->name:"xxx",skb->dev->name);
+ return TC_ACT_OK;
+ }
+ while ((a = act) != NULL) {
+repeat:
+ if (a->ops && a->ops->act) {
+ ret = a->ops->act(&skb,a);
+ if (TC_MUNGED & skb->tc_verd) {
+ /* copied already, allow trampling */
+ skb->tc_verd = SET_TC_OK2MUNGE(skb->tc_verd);
+ skb->tc_verd = CLR_TC_MUNGED(skb->tc_verd);
+ }
+
+ if (ret != TC_ACT_PIPE)
+ goto exec_done;
+ if (ret == TC_ACT_REPEAT)
+ goto repeat; /* we need a ttl - JHS */
+
+ }
+ act = a->next;
+ }
+
+exec_done:
+
+ return ret;
+}
+
+void tcf_action_destroy(struct tc_action *act, int bind)
+{
+ struct tc_action *a;
+
+ for (a = act; act; a = act) {
+ if (a && a->ops && a->ops->cleanup) {
+ DPRINTK("tcf_action_destroy destroying %p next %p\n", a,a->next?a->next:NULL);
+ act = act->next;
+ if (ACT_P_DELETED == a->ops->cleanup(a, bind)) {
+ module_put(a->ops->owner);
+ }
+
+ a->ops = NULL;
+ kfree(a);
+ } else { /*FIXME: Remove later - catch insertion bugs*/
+ printk("tcf_action_destroy: BUG? destroying NULL ops \n");
+ if (a) {
+ act = act->next;
+ kfree(a);
+ } else {
+ printk("tcf_action_destroy: BUG? destroying NULL action! \n");
+ break;
+ }
+ }
+ }
+}
+
+int tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
+{
+ int err = -EINVAL;
+
+
+ if ( (NULL == a) || (NULL == a->ops)
+ || (NULL == a->ops->dump) )
+ return err;
+ return a->ops->dump(skb, a, bind, ref);
+
+}
+
+
+int tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
+{
+ int err = -EINVAL;
+ unsigned char *b = skb->tail;
+ struct rtattr *r;
+
+
+ if ( (NULL == a) || (NULL == a->ops)
+ || (NULL == a->ops->dump) || (NULL == a->ops->kind))
+ return err;
+
+
+ RTA_PUT(skb, TCA_KIND, IFNAMSIZ, a->ops->kind);
+ if (tcf_action_copy_stats(skb,a))
+ goto rtattr_failure;
+ r = (struct rtattr*) skb->tail;
+ RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
+ if ((err = tcf_action_dump_old(skb, a, bind, ref)) > 0) {
+ r->rta_len = skb->tail - (u8*)r;
+ return err;
+ }
+
+
+rtattr_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+
+}
+
+int tcf_action_dump(struct sk_buff *skb, struct tc_action *act, int bind, int ref)
+{
+ struct tc_action *a;
+ int err = -EINVAL;
+ unsigned char *b = skb->tail;
+ struct rtattr *r ;
+
+ while ((a = act) != NULL) {
+ r = (struct rtattr*) skb->tail;
+ act = a->next;
+ RTA_PUT(skb, a->order, 0, NULL);
+ err = tcf_action_dump_1(skb, a, bind, ref);
+ if (0 > err)
+ goto rtattr_failure;
+
+ r->rta_len = skb->tail - (u8*)r;
+ }
+
+ return 0;
+
+rtattr_failure:
+ skb_trim(skb, b - skb->data);
+ return -err;
+
+}
+
+int tcf_action_init_1(struct rtattr *rta, struct rtattr *est, struct tc_action *a, char *name, int ovr, int bind )
+{
+ struct tc_action_ops *a_o;
+ char act_name[4 + IFNAMSIZ + 1];
+ struct rtattr *tb[TCA_ACT_MAX+1];
+ struct rtattr *kind = NULL;
+
+ int err = -EINVAL;
+
+ if (NULL == name) {
+ if (rtattr_parse(tb, TCA_ACT_MAX, RTA_DATA(rta), RTA_PAYLOAD(rta))<0)
+ goto err_out;
+ kind = tb[TCA_ACT_KIND-1];
+ if (NULL != kind) {
+ sprintf(act_name, "%s", (char*)RTA_DATA(kind));
+ if (RTA_PAYLOAD(kind) >= IFNAMSIZ) {
+ printk(" Action %s bad\n", (char*)RTA_DATA(kind));
+ goto err_out;
+ }
+
+ } else {
+ printk("Action bad kind\n");
+ goto err_out;
+ }
+ a_o = tc_lookup_action(kind);
+ } else {
+ sprintf(act_name, "%s", name);
+ DPRINTK("tcf_action_init_1: finding %s\n",act_name);
+ a_o = tc_lookup_action_n(name);
+ }
+#ifdef CONFIG_KMOD
+ if (NULL == a_o) {
+ DPRINTK("tcf_action_init_1: trying to load module %s\n",act_name);
+ request_module (act_name);
+ a_o = tc_lookup_action_n(act_name);
+ }
+
+#endif
+ if (NULL == a_o) {
+ printk("failed to find %s\n",act_name);
+ goto err_out;
+ }
+
+ if (NULL == a) {
+ goto err_mod;
+ }
+
+ /* backward compatibility for policer */
+ if (NULL == name) {
+ err = a_o->init(tb[TCA_ACT_OPTIONS-1], est, a, ovr, bind);
+ if (0 > err ) {
+ err = -EINVAL;
+ goto err_mod;
+ }
+ } else {
+ err = a_o->init(rta, est, a, ovr, bind);
+ if (0 > err ) {
+ err = -EINVAL;
+ goto err_mod;
+ }
+ }
+
+ /* module count goes up only when brand new policy is created
+ if it exists and is only bound to in a_o->init() then
+ ACT_P_CREATED is not returned (a zero is).
+ */
+ if (ACT_P_CREATED != err) {
+ module_put(a_o->owner);
+ }
+ a->ops = a_o;
+ DPRINTK("tcf_action_init_1: successfull %s \n",act_name);
+
+ return 0;
+err_mod:
+ module_put(a_o->owner);
+err_out:
+ return err;
+}
+
+int tcf_action_init(struct rtattr *rta, struct rtattr *est, struct tc_action *a, char *name, int ovr , int bind)
+{
+ struct rtattr *tb[TCA_ACT_MAX_PRIO+1];
+ int i;
+ struct tc_action *act = a, *a_s = a;
+
+ int err = -EINVAL;
+
+ if (rtattr_parse(tb, TCA_ACT_MAX_PRIO, RTA_DATA(rta), RTA_PAYLOAD(rta))<0)
+ return err;
+
+ for (i=0; i < TCA_ACT_MAX_PRIO ; i++) {
+ if (tb[i]) {
+ if (NULL == act) {
+ act = kmalloc(sizeof(*act),GFP_KERNEL);
+ if (NULL == act) {
+ err = -ENOMEM;
+ goto bad_ret;
+ }
+ memset(act, 0,sizeof(*act));
+ }
+ act->next = NULL;
+ if (0 > tcf_action_init_1(tb[i],est,act,name,ovr,bind)) {
+ printk("Error processing action order %d\n",i);
+ return err;
+ }
+
+ act->order = i+1;
+ if (a_s != act) {
+ a_s->next = act;
+ a_s = act;
+ }
+ act = NULL;
+ }
+
+ }
+
+ return 0;
+bad_ret:
+ tcf_action_destroy(a, bind);
+ return err;
+}
+
+int tcf_action_copy_stats (struct sk_buff *skb,struct tc_action *a)
+{
+#ifdef CONFIG_KMOD
+ /* place holder */
+#endif
+
+ if (NULL == a->ops || NULL == a->ops->get_stats)
+ return 1;
+
+ return a->ops->get_stats(skb,a);
+}
+
+
+static int
+tca_get_fill(struct sk_buff *skb, struct tc_action *a,
+ u32 pid, u32 seq, unsigned flags, int event, int bind, int ref)
+{
+ struct tcamsg *t;
+ struct nlmsghdr *nlh;
+ unsigned char *b = skb->tail;
+ struct rtattr *x;
+
+ nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*t));
+ nlh->nlmsg_flags = flags;
+ t = NLMSG_DATA(nlh);
+ t->tca_family = AF_UNSPEC;
+
+ x = (struct rtattr*) skb->tail;
+ RTA_PUT(skb, TCA_ACT_TAB, 0, NULL);
+
+ if (0 > tcf_action_dump(skb, a, bind, ref)) {
+ goto rtattr_failure;
+ }
+
+ x->rta_len = skb->tail - (u8*)x;
+
+ nlh->nlmsg_len = skb->tail - b;
+ return skb->len;
+
+rtattr_failure:
+nlmsg_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+static int act_get_notify(u32 pid, struct nlmsghdr *n,
+ struct tc_action *a, int event)
+{
+ struct sk_buff *skb;
+
+ int err = 0;
+
+ skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!skb)
+ return -ENOBUFS;
+
+ if (tca_get_fill(skb, a, pid, n->nlmsg_seq, 0, event, 0, 0) <= 0) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ err = netlink_unicast(rtnl,skb, pid, MSG_DONTWAIT);
+ if (err > 0)
+ err = 0;
+ return err;
+}
+
+int tcf_action_get_1(struct rtattr *rta, struct tc_action *a, struct nlmsghdr *n, u32 pid)
+{
+ struct tc_action_ops *a_o;
+ char act_name[4 + IFNAMSIZ + 1];
+ struct rtattr *tb[TCA_ACT_MAX+1];
+ struct rtattr *kind = NULL;
+ int index;
+
+ int err = -EINVAL;
+
+ if (rtattr_parse(tb, TCA_ACT_MAX, RTA_DATA(rta), RTA_PAYLOAD(rta))<0)
+ goto err_out;
+
+
+ kind = tb[TCA_ACT_KIND-1];
+ if (NULL != kind) {
+ sprintf(act_name, "%s", (char*)RTA_DATA(kind));
+ if (RTA_PAYLOAD(kind) >= IFNAMSIZ) {
+ printk("tcf_action_get_1: action %s bad\n", (char*)RTA_DATA(kind));
+ goto err_out;
+ }
+
+ } else {
+ printk("tcf_action_get_1: action bad kind\n");
+ goto err_out;
+ }
+
+ if (tb[TCA_ACT_INDEX - 1]) {
+ index = *(int *)RTA_DATA(tb[TCA_ACT_INDEX - 1]);
+ } else {
+ printk("tcf_action_get_1: index not received\n");
+ goto err_out;
+ }
+
+ a_o = tc_lookup_action(kind);
+#ifdef CONFIG_KMOD
+ if (NULL == a_o) {
+ request_module (act_name);
+ a_o = tc_lookup_action_n(act_name);
+ }
+
+#endif
+ if (NULL == a_o) {
+ printk("failed to find %s\n",act_name);
+ goto err_out;
+ }
+
+ if (NULL == a) {
+ goto err_mod;
+ }
+
+ a->ops = a_o;
+
+ if (NULL == a_o->lookup || 0 == a_o->lookup(a, index)) {
+ a->ops = NULL;
+ err = -EINVAL;
+ goto err_mod;
+ }
+
+ module_put(a_o->owner);
+ return 0;
+err_mod:
+ module_put(a_o->owner);
+err_out:
+ return err;
+}
+
+void cleanup_a (struct tc_action *act)
+{
+ struct tc_action *a;
+
+ for (a = act; act; a = act) {
+ if (a) {
+ act = act->next;
+ a->ops = NULL;
+ a->priv = NULL;
+ kfree(a);
+ } else {
+ printk("cleanup_a: BUG? empty action\n");
+ }
+ }
+}
+
+struct tc_action_ops *get_ao(struct rtattr *kind, struct tc_action *a)
+{
+ char act_name[4 + IFNAMSIZ + 1];
+ struct tc_action_ops *a_o = NULL;
+
+ if (NULL != kind) {
+ sprintf(act_name, "%s", (char*)RTA_DATA(kind));
+ if (RTA_PAYLOAD(kind) >= IFNAMSIZ) {
+ printk("get_ao: action %s bad\n", (char*)RTA_DATA(kind));
+ return NULL;
+ }
+
+ } else {
+ printk("get_ao: action bad kind\n");
+ return NULL;
+ }
+
+ a_o = tc_lookup_action(kind);
+#ifdef CONFIG_KMOD
+ if (NULL == a_o) {
+ DPRINTK("get_ao: trying to load module %s\n",act_name);
+ request_module (act_name);
+ a_o = tc_lookup_action_n(act_name);
+ }
+#endif
+
+ if (NULL == a_o) {
+ printk("get_ao: failed to find %s\n",act_name);
+ return NULL;
+ }
+
+ a->ops = a_o;
+ return a_o;
+}
+
+struct tc_action *create_a(int i)
+{
+ struct tc_action *act = NULL;
+
+ act = kmalloc(sizeof(*act),GFP_KERNEL);
+ if (NULL == act) { /* grrr .. */
+ printk("create_a: failed to alloc! \n");
+ return NULL;
+ }
+
+ memset(act, 0,sizeof(*act));
+
+ act->order = i;
+
+ return act;
+}
+
+int tca_action_flush(struct rtattr *rta, struct nlmsghdr *n, u32 pid)
+{
+ struct sk_buff *skb;
+ unsigned char *b;
+ struct nlmsghdr *nlh;
+ struct tcamsg *t;
+ struct netlink_callback dcb;
+ struct rtattr *x;
+ struct rtattr *tb[TCA_ACT_MAX+1];
+ struct rtattr *kind = NULL;
+ struct tc_action *a = create_a(0);
+ int err = -EINVAL;
+
+ if (NULL == a) {
+ printk("tca_action_flush: couldnt create tc_action\n");
+ return err;
+ }
+
+ skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!skb) {
+ printk("tca_action_flush: failed skb alloc\n");
+ kfree(a);
+ return -ENOBUFS;
+ }
+
+ b = (unsigned char *)skb->tail;
+
+ if (rtattr_parse(tb, TCA_ACT_MAX, RTA_DATA(rta), RTA_PAYLOAD(rta))<0) {
+ goto err_out;
+ }
+
+ kind = tb[TCA_ACT_KIND-1];
+ if (NULL == get_ao(kind, a)) {
+ goto err_out;
+ }
+
+ nlh = NLMSG_PUT(skb, pid, n->nlmsg_seq, RTM_DELACTION, sizeof (*t));
+ t = NLMSG_DATA(nlh);
+ t->tca_family = AF_UNSPEC;
+
+ x = (struct rtattr *) skb->tail;
+ RTA_PUT(skb, TCA_ACT_TAB, 0, NULL);
+
+ err = a->ops->walk(skb, &dcb, RTM_DELACTION, a);
+ if (0 > err ) {
+ goto rtattr_failure;
+ }
+
+ x->rta_len = skb->tail - (u8 *) x;
+
+ nlh->nlmsg_len = skb->tail - b;
+ nlh->nlmsg_flags |= NLM_F_ROOT;
+ module_put(a->ops->owner);
+ kfree(a);
+ err = rtnetlink_send(skb, pid, RTMGRP_TC, n->nlmsg_flags&NLM_F_ECHO);
+ if (err > 0)
+ return 0;
+
+ return err;
+
+
+rtattr_failure:
+ module_put(a->ops->owner);
+nlmsg_failure:
+err_out:
+ kfree_skb(skb);
+ kfree(a);
+ return err;
+}
+
+int tca_action_gd(struct rtattr *rta, struct nlmsghdr *n, u32 pid, int event )
+{
+
+ int s = 0;
+ int i, ret = 0;
+ struct tc_action *act = NULL;
+ struct rtattr *tb[TCA_ACT_MAX_PRIO+1];
+ struct tc_action *a = NULL, *a_s = NULL;
+
+ if (event != RTM_GETACTION && event != RTM_DELACTION)
+ ret = -EINVAL;
+
+ if (rtattr_parse(tb, TCA_ACT_MAX_PRIO, RTA_DATA(rta), RTA_PAYLOAD(rta))<0) {
+ ret = -EINVAL;
+ goto nlmsg_failure;
+ }
+
+ if (event == RTM_DELACTION && n->nlmsg_flags&NLM_F_ROOT) {
+ if (NULL != tb[0] && NULL == tb[1]) {
+ return tca_action_flush(tb[0],n,pid);
+ }
+ }
+
+ for (i=0; i < TCA_ACT_MAX_PRIO ; i++) {
+
+ if (NULL == tb[i])
+ break;
+
+ act = create_a(i+1);
+ if (NULL != a && a != act) {
+ a->next = act;
+ a = act;
+ } else {
+ a = act;
+ }
+
+ if (!s) {
+ s = 1;
+ a_s = a;
+ }
+
+ ret = tcf_action_get_1(tb[i],act,n,pid);
+ if (ret < 0) {
+ printk("tcf_action_get: failed to get! \n");
+ ret = -EINVAL;
+ goto rtattr_failure;
+ }
+
+ }
+
+
+ if (RTM_GETACTION == event) {
+ ret = act_get_notify(pid, n, a_s, event);
+ } else { /* delete */
+
+ struct sk_buff *skb;
+
+ skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!skb) {
+ ret = -ENOBUFS;
+ goto nlmsg_failure;
+ }
+
+ if (tca_get_fill(skb, a_s, pid, n->nlmsg_seq, 0, event, 0 , 1) <= 0) {
+ kfree_skb(skb);
+ ret = -EINVAL;
+ goto nlmsg_failure;
+ }
+
+ /* now do the delete */
+ tcf_action_destroy(a_s, 0);
+
+ ret = rtnetlink_send(skb, pid, RTMGRP_TC, n->nlmsg_flags&NLM_F_ECHO);
+ if (ret > 0)
+ return 0;
+ return ret;
+ }
+rtattr_failure:
+nlmsg_failure:
+ cleanup_a(a_s);
+ return ret;
+}
+
+
+int tcf_add_notify(struct tc_action *a, u32 pid, u32 seq, int event, unsigned flags)
+{
+ struct tcamsg *t;
+ struct nlmsghdr *nlh;
+ struct sk_buff *skb;
+ struct rtattr *x;
+ unsigned char *b;
+
+
+ int err = 0;
+
+ skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!skb)
+ return -ENOBUFS;
+
+ b = (unsigned char *)skb->tail;
+
+ nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*t));
+ nlh->nlmsg_flags = flags;
+ t = NLMSG_DATA(nlh);
+ t->tca_family = AF_UNSPEC;
+
+ x = (struct rtattr*) skb->tail;
+ RTA_PUT(skb, TCA_ACT_TAB, 0, NULL);
+
+ if (0 > tcf_action_dump(skb, a, 0, 0)) {
+ goto rtattr_failure;
+ }
+
+ x->rta_len = skb->tail - (u8*)x;
+
+ nlh->nlmsg_len = skb->tail - b;
+ NETLINK_CB(skb).dst_groups = RTMGRP_TC;
+
+ err = rtnetlink_send(skb, pid, RTMGRP_TC, flags&NLM_F_ECHO);
+ if (err > 0)
+ err = 0;
+
+ return err;
+
+rtattr_failure:
+nlmsg_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+
+int tcf_action_add(struct rtattr *rta, struct nlmsghdr *n, u32 pid, int ovr )
+{
+ int ret = 0;
+ struct tc_action *act = NULL;
+ struct tc_action *a = NULL;
+ u32 seq = n->nlmsg_seq;
+
+ act = kmalloc(sizeof(*act),GFP_KERNEL);
+ if (NULL == act)
+ return -ENOMEM;
+
+ memset(act, 0, sizeof(*act));
+
+ ret = tcf_action_init(rta, NULL,act,NULL,ovr,0);
+ /* NOTE: We have an all-or-none model
+ * This means that of any of the actions fail
+ * to update then all are undone.
+ * */
+ if (0 > ret) {
+ tcf_action_destroy(act, 0);
+ goto done;
+ }
+
+ /* dump then free all the actions after update; inserted policy
+ * stays intact
+ * */
+ ret = tcf_add_notify(act, pid, seq, RTM_NEWACTION, n->nlmsg_flags);
+ for (a = act; act; a = act) {
+ if (a) {
+ act = act->next;
+ a->ops = NULL;
+ a->priv = NULL;
+ kfree(a);
+ } else {
+ printk("tcf_action_add: BUG? empty action\n");
+ }
+ }
+done:
+
+ return ret;
+}
+
+static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
+{
+ struct rtattr **tca = arg;
+ u32 pid = skb ? NETLINK_CB(skb).pid : 0;
+
+ int ret = 0, ovr = 0;
+
+ if (NULL == tca[TCA_ACT_TAB-1]) {
+ printk("tc_ctl_action: received NO action attribs\n");
+ return -EINVAL;
+ }
+
+ /* n->nlmsg_flags&NLM_F_CREATE
+ * */
+ switch (n->nlmsg_type) {
+ case RTM_NEWACTION:
+ /* we are going to assume all other flags
+ * imply create only if it doesnt exist
+ * Note that CREATE | EXCL implies that
+ * but since we want avoid ambiguity (eg when flags
+ * is zero) then just set this
+ */
+ if (n->nlmsg_flags&NLM_F_REPLACE) {
+ ovr = 1;
+ }
+ ret = tcf_action_add(tca[TCA_ACT_TAB-1], n, pid, ovr);
+ break;
+ case RTM_DELACTION:
+ ret = tca_action_gd(tca[TCA_ACT_TAB-1], n, pid,RTM_DELACTION);
+ break;
+ case RTM_GETACTION:
+ ret = tca_action_gd(tca[TCA_ACT_TAB-1], n, pid,RTM_GETACTION);
+ break;
+ default:
+ printk(" Unknown cmd was detected\n");
+ break;
+ }
+
+ return ret;
+}
+
+char *
+find_dump_kind(struct nlmsghdr *n)
+{
+ struct rtattr *tb1, *tb2[TCA_ACT_MAX+1];
+ struct rtattr *tb[TCA_ACT_MAX_PRIO + 1];
+ struct rtattr *rta[TCAA_MAX + 1];
+ struct rtattr *kind = NULL;
+ int min_len = NLMSG_LENGTH(sizeof (struct tcamsg));
+
+ int attrlen = n->nlmsg_len - NLMSG_ALIGN(min_len);
+ struct rtattr *attr = (void *) n + NLMSG_ALIGN(min_len);
+
+ if (rtattr_parse(rta, TCAA_MAX, attr, attrlen) < 0)
+ return NULL;
+ tb1 = rta[TCA_ACT_TAB - 1];
+ if (NULL == tb1) {
+ return NULL;
+ }
+
+ if (rtattr_parse(tb, TCA_ACT_MAX_PRIO, RTA_DATA(tb1), NLMSG_ALIGN(RTA_PAYLOAD(tb1))) < 0)
+ return NULL;
+ if (NULL == tb[0])
+ return NULL;
+
+ if (rtattr_parse(tb2, TCA_ACT_MAX, RTA_DATA(tb[0]), RTA_PAYLOAD(tb[0]))<0)
+ return NULL;
+ kind = tb2[TCA_ACT_KIND-1];
+
+ return (char *) RTA_DATA(kind);
+}
+
+static int
+tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct nlmsghdr *nlh;
+ unsigned char *b = skb->tail;
+ struct rtattr *x;
+ struct tc_action_ops *a_o;
+ struct tc_action a;
+ int ret = 0;
+
+ struct tcamsg *t = (struct tcamsg *) NLMSG_DATA(cb->nlh);
+ char *kind = find_dump_kind(cb->nlh);
+ if (NULL == kind) {
+ printk("tc_dump_action: action bad kind\n");
+ return 0;
+ }
+
+ a_o = tc_lookup_action_n(kind);
+
+ if (NULL == a_o) {
+ printk("failed to find %s\n", kind);
+ return 0;
+ }
+
+ memset(&a,0,sizeof(struct tc_action));
+ a.ops = a_o;
+
+ if (NULL == a_o->walk) {
+ printk("tc_dump_action: %s !capable of dumping table\n",kind);
+ goto rtattr_failure;
+ }
+
+ nlh = NLMSG_PUT(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, cb->nlh->nlmsg_type, sizeof (*t));
+ t = NLMSG_DATA(nlh);
+ t->tca_family = AF_UNSPEC;
+
+ x = (struct rtattr *) skb->tail;
+ RTA_PUT(skb, TCA_ACT_TAB, 0, NULL);
+
+ ret = a_o->walk(skb, cb, RTM_GETACTION, &a);
+ if (0 > ret ) {
+ goto rtattr_failure;
+ }
+
+ if (ret > 0) {
+ x->rta_len = skb->tail - (u8 *) x;
+ ret = skb->len;
+ } else {
+ skb_trim(skb, (u8*)x - skb->data);
+ }
+
+ nlh->nlmsg_len = skb->tail - b;
+ if (NETLINK_CB(cb->skb).pid && ret)
+ nlh->nlmsg_flags |= NLM_F_MULTI;
+ module_put(a_o->owner);
+ return skb->len;
+
+rtattr_failure:
+nlmsg_failure:
+ module_put(a_o->owner);
+ skb_trim(skb, b - skb->data);
+ return skb->len;
+}
+
+static int __init tc_action_init(void)
+{
+ struct rtnetlink_link *link_p = rtnetlink_links[PF_UNSPEC];
+
+ if (link_p) {
+ link_p[RTM_NEWACTION-RTM_BASE].doit = tc_ctl_action;
+ link_p[RTM_DELACTION-RTM_BASE].doit = tc_ctl_action;
+ link_p[RTM_GETACTION-RTM_BASE].doit = tc_ctl_action;
+ link_p[RTM_GETACTION-RTM_BASE].dumpit = tc_dump_action;
+ }
+
+ printk("TC classifier action (bugs to netdev@oss.sgi.com cc hadi@cyberus.ca)\n");
+ return 0;
+}
+
+subsys_initcall(tc_action_init);
+
+EXPORT_SYMBOL(tcf_register_action);
+EXPORT_SYMBOL(tcf_unregister_action);
+EXPORT_SYMBOL(tcf_action_init_1);
+EXPORT_SYMBOL(tcf_action_init);
+EXPORT_SYMBOL(tcf_action_destroy);
+EXPORT_SYMBOL(tcf_action_exec);
+EXPORT_SYMBOL(tcf_action_copy_stats);
+EXPORT_SYMBOL(tcf_action_dump);
+EXPORT_SYMBOL(tcf_action_dump_1);
+EXPORT_SYMBOL(tcf_action_dump_old);
#endif
-#define PRIV(sch) ((struct dsmark_qdisc_data *) (sch)->data)
+#define PRIV(sch) qdisc_priv(sch)
/*
tcf_destroy(tp);
}
qdisc_destroy(p->q);
- p->q = &noop_qdisc;
kfree(p->mask);
}
static int
bfifo_enqueue(struct sk_buff *skb, struct Qdisc* sch)
{
- struct fifo_sched_data *q = (struct fifo_sched_data *)sch->data;
+ struct fifo_sched_data *q = qdisc_priv(sch);
if (sch->stats.backlog + skb->len <= q->limit) {
__skb_queue_tail(&sch->q, skb);
static int
pfifo_enqueue(struct sk_buff *skb, struct Qdisc* sch)
{
- struct fifo_sched_data *q = (struct fifo_sched_data *)sch->data;
+ struct fifo_sched_data *q = qdisc_priv(sch);
if (sch->q.qlen < q->limit) {
__skb_queue_tail(&sch->q, skb);
static int fifo_init(struct Qdisc *sch, struct rtattr *opt)
{
- struct fifo_sched_data *q = (void*)sch->data;
+ struct fifo_sched_data *q = qdisc_priv(sch);
if (opt == NULL) {
unsigned int limit = sch->dev->tx_queue_len ? : 1;
static int fifo_dump(struct Qdisc *sch, struct sk_buff *skb)
{
- struct fifo_sched_data *q = (void*)sch->data;
+ struct fifo_sched_data *q = qdisc_priv(sch);
unsigned char *b = skb->tail;
struct tc_fifo_qopt opt;
*/
static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch)
{
- struct netem_sched_data *q = (struct netem_sched_data *)sch->data;
+ struct netem_sched_data *q = qdisc_priv(sch);
struct netem_skb_cb *cb = (struct netem_skb_cb *)skb->cb;
psched_time_t now;
long delay;
PSCHED_TADD2(now, delay, cb->time_to_send);
/* Always queue at tail to keep packets in order */
- __skb_queue_tail(&q->delayed, skb);
- sch->q.qlen++;
- sch->stats.bytes += skb->len;
- sch->stats.packets++;
- return 0;
+ if (likely(q->delayed.qlen < q->limit)) {
+ __skb_queue_tail(&q->delayed, skb);
+ sch->q.qlen++;
+ sch->stats.bytes += skb->len;
+ sch->stats.packets++;
+ return 0;
+ }
+
+ sch->stats.drops++;
+ kfree_skb(skb);
+ return NET_XMIT_DROP;
}
/* Requeue packets but don't change time stamp */
static int netem_requeue(struct sk_buff *skb, struct Qdisc *sch)
{
- struct netem_sched_data *q = (struct netem_sched_data *)sch->data;
+ struct netem_sched_data *q = qdisc_priv(sch);
int ret;
if ((ret = q->qdisc->ops->requeue(skb, q->qdisc)) == 0)
static unsigned int netem_drop(struct Qdisc* sch)
{
- struct netem_sched_data *q = (struct netem_sched_data *)sch->data;
+ struct netem_sched_data *q = qdisc_priv(sch);
unsigned int len;
if ((len = q->qdisc->ops->drop(q->qdisc)) != 0) {
*/
static struct sk_buff *netem_dequeue(struct Qdisc *sch)
{
- struct netem_sched_data *q = (struct netem_sched_data *)sch->data;
+ struct netem_sched_data *q = qdisc_priv(sch);
struct sk_buff *skb;
psched_time_t now;
static void netem_reset(struct Qdisc *sch)
{
- struct netem_sched_data *q = (struct netem_sched_data *)sch->data;
+ struct netem_sched_data *q = qdisc_priv(sch);
qdisc_reset(q->qdisc);
skb_queue_purge(&q->delayed);
static int netem_change(struct Qdisc *sch, struct rtattr *opt)
{
- struct netem_sched_data *q = (struct netem_sched_data *)sch->data;
+ struct netem_sched_data *q = qdisc_priv(sch);
struct tc_netem_qopt *qopt = RTA_DATA(opt);
struct Qdisc *child;
int ret;
static int netem_init(struct Qdisc *sch, struct rtattr *opt)
{
- struct netem_sched_data *q = (struct netem_sched_data *)sch->data;
+ struct netem_sched_data *q = qdisc_priv(sch);
if (!opt)
return -EINVAL;
static void netem_destroy(struct Qdisc *sch)
{
- struct netem_sched_data *q = (struct netem_sched_data *)sch->data;
+ struct netem_sched_data *q = qdisc_priv(sch);
del_timer_sync(&q->timer);
+ qdisc_destroy(q->qdisc);
}
static int netem_dump(struct Qdisc *sch, struct sk_buff *skb)
{
- struct netem_sched_data *q = (struct netem_sched_data *)sch->data;
+ struct netem_sched_data *q = qdisc_priv(sch);
unsigned char *b = skb->tail;
struct tc_netem_qopt qopt;
qopt.latency = q->latency;
qopt.jitter = q->jitter;
- qopt.limit = sch->dev->tx_queue_len;
+ qopt.limit = q->limit;
qopt.loss = q->loss;
qopt.gap = q->gap;
return -1;
}
+static int netem_dump_class(struct Qdisc *sch, unsigned long cl,
+ struct sk_buff *skb, struct tcmsg *tcm)
+{
+ struct netem_sched_data *q = qdisc_priv(sch);
+
+ if (cl != 1) /* only one class */
+ return -ENOENT;
+
+ tcm->tcm_handle |= TC_H_MIN(1);
+ tcm->tcm_info = q->qdisc->handle;
+
+ return 0;
+}
+
+static int netem_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
+ struct Qdisc **old)
+{
+ struct netem_sched_data *q = qdisc_priv(sch);
+
+ if (new == NULL)
+ new = &noop_qdisc;
+
+ sch_tree_lock(sch);
+ *old = xchg(&q->qdisc, new);
+ qdisc_reset(*old);
+ sch->q.qlen = 0;
+ sch_tree_unlock(sch);
+
+ return 0;
+}
+
+static struct Qdisc *netem_leaf(struct Qdisc *sch, unsigned long arg)
+{
+ struct netem_sched_data *q = qdisc_priv(sch);
+ return q->qdisc;
+}
+
+static unsigned long netem_get(struct Qdisc *sch, u32 classid)
+{
+ return 1;
+}
+
+static void netem_put(struct Qdisc *sch, unsigned long arg)
+{
+}
+
+static int netem_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
+ struct rtattr **tca, unsigned long *arg)
+{
+ return -ENOSYS;
+}
+
+static int netem_delete(struct Qdisc *sch, unsigned long arg)
+{
+ return -ENOSYS;
+}
+
+static void netem_walk(struct Qdisc *sch, struct qdisc_walker *walker)
+{
+ if (!walker->stop) {
+ if (walker->count >= walker->skip)
+ if (walker->fn(sch, 1, walker) < 0) {
+ walker->stop = 1;
+ return;
+ }
+ walker->count++;
+ }
+}
+
+static struct tcf_proto **netem_find_tcf(struct Qdisc *sch, unsigned long cl)
+{
+ return NULL;
+}
+
+static struct Qdisc_class_ops netem_class_ops = {
+ .graft = netem_graft,
+ .leaf = netem_leaf,
+ .get = netem_get,
+ .put = netem_put,
+ .change = netem_change_class,
+ .delete = netem_delete,
+ .walk = netem_walk,
+ .tcf_chain = netem_find_tcf,
+ .dump = netem_dump_class,
+};
+
static struct Qdisc_ops netem_qdisc_ops = {
.id = "netem",
+ .cl_ops = &netem_class_ops,
.priv_size = sizeof(struct netem_sched_data),
.enqueue = netem_enqueue,
.dequeue = netem_dequeue,
static unsigned int sfq_drop(struct Qdisc *sch)
{
- struct sfq_sched_data *q = (struct sfq_sched_data *)sch->data;
+ struct sfq_sched_data *q = qdisc_priv(sch);
sfq_index d = q->max_depth;
struct sk_buff *skb;
unsigned int len;
static int
sfq_enqueue(struct sk_buff *skb, struct Qdisc* sch)
{
- struct sfq_sched_data *q = (struct sfq_sched_data *)sch->data;
+ struct sfq_sched_data *q = qdisc_priv(sch);
unsigned hash = sfq_hash(q, skb);
sfq_index x;
static int
sfq_requeue(struct sk_buff *skb, struct Qdisc* sch)
{
- struct sfq_sched_data *q = (struct sfq_sched_data *)sch->data;
+ struct sfq_sched_data *q = qdisc_priv(sch);
unsigned hash = sfq_hash(q, skb);
sfq_index x;
static struct sk_buff *
sfq_dequeue(struct Qdisc* sch)
{
- struct sfq_sched_data *q = (struct sfq_sched_data *)sch->data;
+ struct sfq_sched_data *q = qdisc_priv(sch);
struct sk_buff *skb;
sfq_index a, old_a;
static void sfq_perturbation(unsigned long arg)
{
struct Qdisc *sch = (struct Qdisc*)arg;
- struct sfq_sched_data *q = (struct sfq_sched_data *)sch->data;
+ struct sfq_sched_data *q = qdisc_priv(sch);
q->perturbation = net_random()&0x1F;
q->perturb_timer.expires = jiffies + q->perturb_period;
static int sfq_change(struct Qdisc *sch, struct rtattr *opt)
{
- struct sfq_sched_data *q = (struct sfq_sched_data *)sch->data;
+ struct sfq_sched_data *q = qdisc_priv(sch);
struct tc_sfq_qopt *ctl = RTA_DATA(opt);
if (opt->rta_len < RTA_LENGTH(sizeof(*ctl)))
static int sfq_init(struct Qdisc *sch, struct rtattr *opt)
{
- struct sfq_sched_data *q = (struct sfq_sched_data *)sch->data;
+ struct sfq_sched_data *q = qdisc_priv(sch);
int i;
init_timer(&q->perturb_timer);
static void sfq_destroy(struct Qdisc *sch)
{
- struct sfq_sched_data *q = (struct sfq_sched_data *)sch->data;
+ struct sfq_sched_data *q = qdisc_priv(sch);
del_timer(&q->perturb_timer);
}
static int sfq_dump(struct Qdisc *sch, struct sk_buff *skb)
{
- struct sfq_sched_data *q = (struct sfq_sched_data *)sch->data;
+ struct sfq_sched_data *q = qdisc_priv(sch);
unsigned char *b = skb->tail;
struct tc_sfq_qopt opt;
struct sk_buff_head q;
};
-#define NEXT_SLAVE(q) (((struct teql_sched_data*)((q)->data))->next)
+#define NEXT_SLAVE(q) (((struct teql_sched_data*)qdisc_priv(q))->next)
#define FMASK (IFF_BROADCAST|IFF_POINTOPOINT|IFF_BROADCAST)
teql_enqueue(struct sk_buff *skb, struct Qdisc* sch)
{
struct net_device *dev = sch->dev;
- struct teql_sched_data *q = (struct teql_sched_data *)sch->data;
+ struct teql_sched_data *q = qdisc_priv(sch);
__skb_queue_tail(&q->q, skb);
if (q->q.qlen <= dev->tx_queue_len) {
static int
teql_requeue(struct sk_buff *skb, struct Qdisc* sch)
{
- struct teql_sched_data *q = (struct teql_sched_data *)sch->data;
+ struct teql_sched_data *q = qdisc_priv(sch);
__skb_queue_head(&q->q, skb);
return 0;
static struct sk_buff *
teql_dequeue(struct Qdisc* sch)
{
- struct teql_sched_data *dat = (struct teql_sched_data *)sch->data;
+ struct teql_sched_data *dat = qdisc_priv(sch);
struct sk_buff *skb;
skb = __skb_dequeue(&dat->q);
static void
teql_reset(struct Qdisc* sch)
{
- struct teql_sched_data *dat = (struct teql_sched_data *)sch->data;
+ struct teql_sched_data *dat = qdisc_priv(sch);
skb_queue_purge(&dat->q);
sch->q.qlen = 0;
teql_destroy(struct Qdisc* sch)
{
struct Qdisc *q, *prev;
- struct teql_sched_data *dat = (struct teql_sched_data *)sch->data;
+ struct teql_sched_data *dat = qdisc_priv(sch);
struct teql_master *master = dat->m;
if ((prev = master->slaves) != NULL) {
{
struct net_device *dev = sch->dev;
struct teql_master *m = (struct teql_master*)sch->ops;
- struct teql_sched_data *q = (struct teql_sched_data *)sch->data;
+ struct teql_sched_data *q = qdisc_priv(sch);
if (dev->hard_header_len > m->dev->hard_header_len)
return -EINVAL;
static int
__teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, struct net_device *dev)
{
- struct teql_sched_data *q = (void*)dev->qdisc->data;
+ struct teql_sched_data *q = qdisc_priv(dev->qdisc);
struct neighbour *mn = skb->dst->neighbour;
struct neighbour *n = q->ncache;
config IP_SCTP
tristate "The SCTP Protocol (EXPERIMENTAL)"
depends on IPV6 || IPV6=n
+ select CRYPTO if SCTP_HMAC_SHA1 || SCTP_HMAC_MD5
+ select CRYPTO_HMAC if SCTP_HMAC_SHA1 || SCTP_HMAC_MD5
+ select CRYPTO_SHA1 if SCTP_HMAC_SHA1
+ select CRYPTO_MD5 if SCTP_HMAC_MD5
---help---
Stream Control Transmission Protocol
config SCTP_HMAC_SHA1
bool "HMAC-SHA1"
- select CRYPTO
- select CRYPTO_HMAC
- select CRYPTO_SHA1
help
Enable the use of HMAC-SHA1 during association establishment. It
is advised to use either HMAC-MD5 or HMAC-SHA1.
config SCTP_HMAC_MD5
bool "HMAC-MD5"
- select CRYPTO
- select CRYPTO_HMAC
- select CRYPTO_MD5
help
Enable the use of HMAC-MD5 during association establishment. It is
advised to use either HMAC-MD5 or HMAC-SHA1.
if (val != cmp) {
/* CRC failure, dump it. */
- SCTP_INC_STATS_BH(SctpChecksumErrors);
+ SCTP_INC_STATS_BH(SCTP_MIB_CHECKSUMERRORS);
return -1;
}
return 0;
if (skb->pkt_type!=PACKET_HOST)
goto discard_it;
- SCTP_INC_STATS_BH(SctpInSCTPPacks);
+ SCTP_INC_STATS_BH(SCTP_MIB_INSCTPPACKS);
sh = (struct sctphdr *) skb->h.raw;
if (!asoc) {
ep = __sctp_rcv_lookup_endpoint(&dest);
if (sctp_rcv_ootb(skb)) {
- SCTP_INC_STATS_BH(SctpOutOfBlues);
+ SCTP_INC_STATS_BH(SCTP_MIB_OUTOFBLUES);
goto discard_release;
}
}
if (asoc) {
if (ntohl(sctphdr->vtag) != asoc->c.peer_vtag) {
- ICMP_INC_STATS_BH(IcmpInErrors);
+ ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
goto out;
}
sk = asoc->base.sk;
* servers this needs to be solved differently.
*/
if (sock_owned_by_user(sk))
- NET_INC_STATS_BH(LockDroppedIcmps);
+ NET_INC_STATS_BH(LINUX_MIB_LOCKDROPPEDICMPS);
*epp = ep;
*app = asoc;
int err;
if (skb->len < ((iph->ihl << 2) + 8)) {
- ICMP_INC_STATS_BH(IcmpInErrors);
+ ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
return;
}
skb->nh.raw = saveip;
skb->h.raw = savesctp;
if (!sk) {
- ICMP_INC_STATS_BH(IcmpInErrors);
+ ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
return;
}
/* Warning: The sock lock is held. Remember to call
#include <linux/init.h>
#include <net/sctp/sctp.h>
-static char *sctp_snmp_list[] = {
-#define SCTP_SNMP_ENTRY(x) #x
- SCTP_SNMP_ENTRY(SctpCurrEstab),
- SCTP_SNMP_ENTRY(SctpActiveEstabs),
- SCTP_SNMP_ENTRY(SctpPassiveEstabs),
- SCTP_SNMP_ENTRY(SctpAborteds),
- SCTP_SNMP_ENTRY(SctpShutdowns),
- SCTP_SNMP_ENTRY(SctpOutOfBlues),
- SCTP_SNMP_ENTRY(SctpChecksumErrors),
- SCTP_SNMP_ENTRY(SctpOutCtrlChunks),
- SCTP_SNMP_ENTRY(SctpOutOrderChunks),
- SCTP_SNMP_ENTRY(SctpOutUnorderChunks),
- SCTP_SNMP_ENTRY(SctpInCtrlChunks),
- SCTP_SNMP_ENTRY(SctpInOrderChunks),
- SCTP_SNMP_ENTRY(SctpInUnorderChunks),
- SCTP_SNMP_ENTRY(SctpFragUsrMsgs),
- SCTP_SNMP_ENTRY(SctpReasmUsrMsgs),
- SCTP_SNMP_ENTRY(SctpOutSCTPPacks),
- SCTP_SNMP_ENTRY(SctpInSCTPPacks),
-#undef SCTP_SNMP_ENTRY
+struct snmp_mib sctp_snmp_list[] = {
+ SNMP_MIB_ITEM("SctpCurrEstab", SCTP_MIB_CURRESTAB),
+ SNMP_MIB_ITEM("SctpActiveEstabs", SCTP_MIB_ACTIVEESTABS),
+ SNMP_MIB_ITEM("SctpPassiveEstabs", SCTP_MIB_PASSIVEESTABS),
+ SNMP_MIB_ITEM("SctpAborteds", SCTP_MIB_ABORTEDS),
+ SNMP_MIB_ITEM("SctpShutdowns", SCTP_MIB_SHUTDOWNS),
+ SNMP_MIB_ITEM("SctpOutOfBlues", SCTP_MIB_OUTOFBLUES),
+ SNMP_MIB_ITEM("SctpChecksumErrors", SCTP_MIB_CHECKSUMERRORS),
+ SNMP_MIB_ITEM("SctpOutCtrlChunks", SCTP_MIB_OUTCTRLCHUNKS),
+ SNMP_MIB_ITEM("SctpOutOrderChunks", SCTP_MIB_OUTORDERCHUNKS),
+ SNMP_MIB_ITEM("SctpOutUnorderChunks", SCTP_MIB_OUTUNORDERCHUNKS),
+ SNMP_MIB_ITEM("SctpInCtrlChunks", SCTP_MIB_INCTRLCHUNKS),
+ SNMP_MIB_ITEM("SctpInOrderChunks", SCTP_MIB_INORDERCHUNKS),
+ SNMP_MIB_ITEM("SctpInUnorderChunks", SCTP_MIB_INUNORDERCHUNKS),
+ SNMP_MIB_ITEM("SctpFragUsrMsgs", SCTP_MIB_FRAGUSRMSGS),
+ SNMP_MIB_ITEM("SctpReasmUsrMsgs", SCTP_MIB_REASMUSRMSGS),
+ SNMP_MIB_ITEM("SctpOutSCTPPacks", SCTP_MIB_OUTSCTPPACKS),
+ SNMP_MIB_ITEM("SctpInSCTPPacks", SCTP_MIB_INSCTPPACKS),
};
/* Return the current value of a particular entry in the mib by adding its
{
int i;
- for (i = 0; i < sizeof(sctp_snmp_list) / sizeof(char *); i++)
- seq_printf(seq, "%-32s\t%ld\n", sctp_snmp_list[i],
- fold_field((void **)sctp_statistics, i));
+ for (i = 0; sctp_snmp_list[i].name != NULL; i++)
+ seq_printf(seq, "%-32s\t%ld\n", sctp_snmp_list[i].name,
+ fold_field((void **)sctp_statistics,
+ sctp_snmp_list[i].entry));
return 0;
}
}
}
+/* Helper function to stop any pending T3-RTX timers */
+static void sctp_cmd_t3_rtx_timers_stop(sctp_cmd_seq_t *cmds,
+ struct sctp_association *asoc)
+{
+ struct sctp_transport *t;
+ struct list_head *pos;
+
+ list_for_each(pos, &asoc->peer.transport_addr_list) {
+ t = list_entry(pos, struct sctp_transport, transports);
+ if (timer_pending(&t->T3_rtx_timer) &&
+ del_timer(&t->T3_rtx_timer)) {
+ sctp_transport_put(t);
+ }
+ }
+}
+
+
/* Helper function to update the heartbeat timer. */
static void sctp_cmd_hb_timer_update(sctp_cmd_seq_t *cmds,
struct sctp_association *asoc,
return;
}
+/* Helper function to remove the association non-primary peer
+ * transports.
+ */
+static void sctp_cmd_del_non_primary(struct sctp_association *asoc)
+{
+ struct sctp_transport *t;
+ struct list_head *pos;
+ struct list_head *temp;
+
+ list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
+ t = list_entry(pos, struct sctp_transport, transports);
+ if (!sctp_cmp_addr_exact(&t->ipaddr,
+ &asoc->peer.primary_addr)) {
+ sctp_assoc_del_peer(asoc, &t->ipaddr);
+ }
+ }
+
+ return;
+}
+
/* These three macros allow us to pull the debugging code out of the
* main flow of sctp_do_sm() to keep attention focused on the real
* functionality there.
if (cmd->obj.ptr)
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
SCTP_CHUNK(cmd->obj.ptr));
+
+ /* FIXME - Eventually come up with a cleaner way to
+ * enabling COOKIE-ECHO + DATA bundling during
+ * multihoming stale cookie scenarios, the following
+ * command plays with asoc->peer.retran_path to
+ * avoid the problem of sending the COOKIE-ECHO and
+ * DATA in different paths, which could result
+ * in the association being ABORTed if the DATA chunk
+ * is processed first by the server. Checking the
+ * init error counter simply causes this command
+ * to be executed only during failed attempts of
+ * association establishment.
+ */
+ if ((asoc->peer.retran_path !=
+ asoc->peer.primary_path) &&
+ (asoc->counters[SCTP_COUNTER_INIT_ERROR] > 0)) {
+ sctp_add_cmd_sf(commands,
+ SCTP_CMD_FORCE_PRIM_RETRAN,
+ SCTP_NULL());
+ }
+
break;
case SCTP_CMD_GEN_SHUTDOWN:
case SCTP_CMD_CLEAR_INIT_TAG:
asoc->peer.i.init_tag = 0;
break;
+ case SCTP_CMD_DEL_NON_PRIMARY:
+ sctp_cmd_del_non_primary(asoc);
+ break;
+ case SCTP_CMD_T3_RTX_TIMERS_STOP:
+ sctp_cmd_t3_rtx_timers_stop(commands, asoc);
+ break;
+ case SCTP_CMD_FORCE_PRIM_RETRAN:
+ t = asoc->peer.retran_path;
+ asoc->peer.retran_path = asoc->peer.primary_path;
+ error = sctp_outq_uncork(&asoc->outqueue);
+ local_cork = 0;
+ asoc->peer.retran_path = t;
+ break;
default:
printk(KERN_WARNING "Impossible command: %u, %p\n",
cmd->verb, cmd->obj.ptr);
struct svc_program *progp;
struct svc_version *versp = NULL; /* compiler food */
struct svc_procedure *procp = NULL;
- struct iovec * argv = &rqstp->rq_arg.head[0];
- struct iovec * resv = &rqstp->rq_res.head[0];
+ struct kvec * argv = &rqstp->rq_arg.head[0];
+ struct kvec * resv = &rqstp->rq_res.head[0];
kxdrproc_t xdr;
u32 *statp;
u32 dir, prog, vers, proc,
static int
svcauth_null_accept(struct svc_rqst *rqstp, u32 *authp)
{
- struct iovec *argv = &rqstp->rq_arg.head[0];
- struct iovec *resv = &rqstp->rq_res.head[0];
+ struct kvec *argv = &rqstp->rq_arg.head[0];
+ struct kvec *resv = &rqstp->rq_res.head[0];
int rv=0;
struct ip_map key, *ipm;
int
svcauth_unix_accept(struct svc_rqst *rqstp, u32 *authp)
{
- struct iovec *argv = &rqstp->rq_arg.head[0];
- struct iovec *resv = &rqstp->rq_res.head[0];
+ struct kvec *argv = &rqstp->rq_arg.head[0];
+ struct kvec *resv = &rqstp->rq_res.head[0];
struct svc_cred *cred = &rqstp->rq_cred;
u32 slen, i;
int len = argv->iov_len;
xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base,
unsigned int len)
{
- struct iovec *tail = xdr->tail;
+ struct kvec *tail = xdr->tail;
u32 *p;
xdr->pages = pages;
xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset,
struct page **pages, unsigned int base, unsigned int len)
{
- struct iovec *head = xdr->head;
- struct iovec *tail = xdr->tail;
+ struct kvec *head = xdr->head;
+ struct kvec *tail = xdr->tail;
char *buf = (char *)head->iov_base;
unsigned int buflen = head->iov_len;
}
/*
- * Realign the iovec if the server missed out some reply elements
+ * Realign the kvec if the server missed out some reply elements
* (such as post-op attributes,...)
* Note: This is a simple implementation that assumes that
* len <= iov->iov_len !!!
* The RPC header (assumed to be the 1st element in the iov array)
* is not shifted.
*/
-void xdr_shift_iovec(struct iovec *iov, int nr, size_t len)
+void xdr_shift_iovec(struct kvec *iov, int nr, size_t len)
{
- struct iovec *pvec;
+ struct kvec *pvec;
for (pvec = iov + nr - 1; nr > 1; nr--, pvec--) {
- struct iovec *svec = pvec - 1;
+ struct kvec *svec = pvec - 1;
if (len > pvec->iov_len) {
printk(KERN_DEBUG "RPC: Urk! Large shift of short iovec.\n");
}
/*
- * Map a struct xdr_buf into an iovec array.
+ * Map a struct xdr_buf into an kvec array.
*/
-int xdr_kmap(struct iovec *iov_base, struct xdr_buf *xdr, size_t base)
+int xdr_kmap(struct kvec *iov_base, struct xdr_buf *xdr, size_t base)
{
- struct iovec *iov = iov_base;
+ struct kvec *iov = iov_base;
struct page **ppage = xdr->pages;
unsigned int len, pglen = xdr->page_len;
unsigned int len, pglen = xdr->page_len;
int err, ret = 0;
ssize_t (*sendpage)(struct socket *, struct page *, int, size_t, int);
- mm_segment_t oldfs;
len = xdr->head[0].iov_len;
if (base < len || (addr != NULL && base == 0)) {
- struct iovec iov = {
+ struct kvec iov = {
.iov_base = xdr->head[0].iov_base + base,
.iov_len = len - base,
};
.msg_namelen = addrlen,
.msg_flags = msgflags,
};
-
- if (iov.iov_len != 0) {
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- }
if (xdr->len > len)
msg.msg_flags |= MSG_MORE;
- oldfs = get_fs(); set_fs(get_ds());
- err = sock_sendmsg(sock, &msg, iov.iov_len);
- set_fs(oldfs);
+
+ if (iov.iov_len != 0)
+ err = kernel_sendmsg(sock, &msg, &iov, 1, iov.iov_len);
+ else
+ err = kernel_sendmsg(sock, &msg, NULL, 0, 0);
if (ret == 0)
ret = err;
else if (err > 0)
copy_tail:
len = xdr->tail[0].iov_len;
if (base < len) {
- struct iovec iov = {
+ struct kvec iov = {
.iov_base = xdr->tail[0].iov_base + base,
.iov_len = len - base,
};
struct msghdr msg = {
- .msg_iov = &iov,
- .msg_iovlen = 1,
.msg_flags = msgflags,
};
- oldfs = get_fs(); set_fs(get_ds());
- err = sock_sendmsg(sock, &msg, iov.iov_len);
- set_fs(oldfs);
+ err = kernel_sendmsg(sock, &msg, &iov, 1, iov.iov_len);
if (ret == 0)
ret = err;
else if (err > 0)
* @buf: xdr_buf
* @len: bytes to remove from buf->head[0]
*
- * Shrinks XDR buffer's header iovec buf->head[0] by
+ * Shrinks XDR buffer's header kvec buf->head[0] by
* 'len' bytes. The extra data is not lost, but is instead
* moved into the inlined pages and/or the tail.
*/
void
xdr_shrink_bufhead(struct xdr_buf *buf, size_t len)
{
- struct iovec *head, *tail;
+ struct kvec *head, *tail;
size_t copy, offs;
unsigned int pglen = buf->page_len;
void
xdr_shrink_pagelen(struct xdr_buf *buf, size_t len)
{
- struct iovec *tail;
+ struct kvec *tail;
size_t copy;
char *p;
unsigned int pglen = buf->page_len;
* @p: current pointer inside XDR buffer
*
* Note: at the moment the RPC client only passes the length of our
- * scratch buffer in the xdr_buf's header iovec. Previously this
+ * scratch buffer in the xdr_buf's header kvec. Previously this
* meant we needed to call xdr_adjust_iovec() after encoding the
* data. With the new scheme, the xdr_stream manages the details
- * of the buffer length, and takes care of adjusting the iovec
+ * of the buffer length, and takes care of adjusting the kvec
* length for us.
*/
void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p)
{
- struct iovec *iov = buf->head;
+ struct kvec *iov = buf->head;
xdr->buf = buf;
xdr->iov = iov;
*
* Checks that we have enough buffer space to encode 'nbytes' more
* bytes of data. If so, update the total xdr_buf length, and
- * adjust the length of the current iovec.
+ * adjust the length of the current kvec.
*/
uint32_t * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes)
{
unsigned int len)
{
struct xdr_buf *buf = xdr->buf;
- struct iovec *iov = buf->tail;
+ struct kvec *iov = buf->tail;
buf->pages = pages;
buf->page_base = base;
buf->page_len = len;
*/
void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p)
{
- struct iovec *iov = buf->head;
+ struct kvec *iov = buf->head;
unsigned int len = iov->iov_len;
if (len > buf->len)
void xdr_read_pages(struct xdr_stream *xdr, unsigned int len)
{
struct xdr_buf *buf = xdr->buf;
- struct iovec *iov;
+ struct kvec *iov;
ssize_t shift;
unsigned int end;
int padding;
}
EXPORT_SYMBOL(xdr_read_pages);
-static struct iovec empty_iov = {.iov_base = NULL, .iov_len = 0};
+static struct kvec empty_iov = {.iov_base = NULL, .iov_len = 0};
void
-xdr_buf_from_iov(struct iovec *iov, struct xdr_buf *buf)
+xdr_buf_from_iov(struct kvec *iov, struct xdr_buf *buf)
{
buf->head[0] = *iov;
buf->tail[0] = empty_iov;
* length of subiov to zero. Decrements len by length of subiov, sets base
* to zero (or decrements it by length of iov if subiov is empty). */
static void
-iov_subsegment(struct iovec *iov, struct iovec *subiov, int *base, int *len)
+iov_subsegment(struct kvec *iov, struct kvec *subiov, int *base, int *len)
{
if (*base > iov->iov_len) {
subiov->iov_base = NULL;
# IA64 port via Andreas Dilger
# Arm port by Holger Schurig
# Random bits by Matt Mackall <mpm@selenic.com>
+# M68k port by Geert Uytterhoeven and Andreas Schwab
#
# Usage:
-# objdump -d vmlinux | stackcheck_ppc.pl [arch]
+# objdump -d vmlinux | stackcheck.pl [arch]
#
# TODO : Port to all architectures (one regex per arch)
# $1 (first bracket) matches the size of the stack growth
#
# use anything else and feel the pain ;)
+my (@stack, $re, $x, $xs);
{
my $arch = shift;
if ($arch eq "") {
$x = "[0-9a-f]"; # hex character
$xs = "[0-9a-f ]"; # hex character or space
- if ($arch =~ /^arm$/) {
+ if ($arch eq 'arm') {
#c0008ffc: e24dd064 sub sp, sp, #100 ; 0x64
$re = qr/.*sub.*sp, sp, #(([0-9]{2}|[3-9])[0-9]{2})/o;
} elsif ($arch =~ /^i[3456]86$/) {
#c0105234: 81 ec ac 05 00 00 sub $0x5ac,%esp
$re = qr/^.*[as][du][db] \$(0x$x{1,8}),\%esp$/o;
- } elsif ($arch =~ /^ia64$/) {
+ } elsif ($arch eq 'ia64') {
#e0000000044011fc: 01 0f fc 8c adds r12=-384,r12
$re = qr/.*adds.*r12=-(([0-9]{2}|[3-9])[0-9]{2}),r12/o;
- } elsif ($arch =~ /^mips64$/) {
+ } elsif ($arch eq 'm68k') {
+ # 2b6c: 4e56 fb70 linkw %fp,#-1168
+ # 1df770: defc ffe4 addaw #-28,%sp
+ $re = qr/.*(?:linkw %fp,|addaw )#-([0-9]{1,4})(?:,%sp)?$/o;
+ } elsif ($arch eq 'mips64') {
#8800402c: 67bdfff0 daddiu sp,sp,-16
$re = qr/.*daddiu.*sp,sp,-(([0-9]{2}|[3-9])[0-9]{2})/o;
- } elsif ($arch =~ /^mips$/) {
+ } elsif ($arch eq 'mips') {
#88003254: 27bdffe0 addiu sp,sp,-32
$re = qr/.*addiu.*sp,sp,-(([0-9]{2}|[3-9])[0-9]{2})/o;
- } elsif ($arch =~ /^ppc$/) {
+ } elsif ($arch eq 'ppc') {
#c00029f4: 94 21 ff 30 stwu r1,-208(r1)
$re = qr/.*stwu.*r1,-($x{1,8})\(r1\)/o;
- } elsif ($arch =~ /^ppc64$/) {
+ } elsif ($arch eq 'ppc64') {
#XXX
$re = qr/.*stdu.*r1,-($x{1,8})\(r1\)/o;
} elsif ($arch =~ /^s390x?$/) {
}
sub bysize($) {
+ my ($asize, $bsize);
($asize = $a) =~ s/.* +(.*)$/$1/;
($bsize = $b) =~ s/.* +(.*)$/$1/;
$bsize <=> $asize
#
# main()
#
-$funcre = qr/^$x* \<(.*)\>:$/;
-while ($line = <STDIN>) {
+my $funcre = qr/^$x* <(.*)>:$/;
+my $func;
+while (my $line = <STDIN>) {
if ($line =~ m/$funcre/) {
$func = $1;
}
$size += 0x80000000;
}
- $line =~ m/^($xs*).*/;
+ next if $line !~ m/^($xs*)/;
my $addr = $1;
$addr =~ s/ /0/g;
$addr = "0x$addr";
$padlen -= 8;
}
next if ($size < 100);
- $stack[@stack] = "$intro$size\n";
+ push @stack, "$intro$size\n";
}
}
-@sortedstack = sort bysize @stack;
-
-foreach $i (@sortedstack) {
- print("$i");
-}
+print sort bysize @stack;
71, 94, 92, 82, 0, 0, 62, 0, 63, 0,
62, 63, 0, 64, 0, 65, 0, 5, 0, 16,
0, 20, 0, 11, 0, 13, 0, 66, 0, 70,
- 0, 27, 46, 65, 47, 0, 21, 36, 0, 23,
+ 0, 27, 46, 62, 47, 0, 21, 36, 0, 23,
36, 0, 10, 36, 0, 21, 36, 84, 0, 23,
36, 84, 0, 10, 36, 31, 0, 10, 31, 0,
21, 84, 0, 23, 84, 0, 7, 0, 18, 0,
};
static const short yypact[] = {-32768,
- 19,-32768, 175,-32768, 32,-32768,-32768,-32768,-32768,-32768,
+ 15,-32768, 197,-32768, 23,-32768,-32768,-32768,-32768,-32768,
-18,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
--32768, -30,-32768, -26,-32768,-32768,-32768, -32, -10, -2,
--32768,-32768,-32768,-32768, 2, 428,-32768,-32768,-32768,-32768,
--32768,-32768,-32768,-32768,-32768,-32768,-32768, 34, 12, 79,
--32768, 428, 12,-32768, 455, 33,-32768,-32768, 15, 14,
- 35, 29,-32768, 2, -14, -21,-32768,-32768,-32768, 67,
- 31, 37, 127,-32768,-32768, 2,-32768, 54, 60, 66,
- 69,-32768, 14,-32768,-32768, 2,-32768,-32768,-32768, 84,
--32768, 219,-32768,-32768, 70,-32768, 20, 91, 72, 84,
- -20, 74, 81,-32768,-32768,-32768, 86,-32768, 102,-32768,
- 106,-32768,-32768,-32768,-32768,-32768, 109, 108, 348, 112,
- 126, 117,-32768,-32768, 118,-32768, 122,-32768,-32768,-32768,
--32768, 262,-32768, 31,-32768, 131,-32768,-32768,-32768,-32768,
--32768, 7, 120,-32768, -9,-32768,-32768, 392,-32768,-32768,
- 125, 130,-32768,-32768, 132,-32768, 159,-32768,-32768, 305,
--32768,-32768,-32768,-32768,-32768,-32768, 160, 161,-32768,-32768,
- 174,-32768
+-32768, -28,-32768, -25,-32768,-32768,-32768, -26, -22, -12,
+-32768,-32768,-32768,-32768, 49, 493,-32768,-32768,-32768,-32768,
+-32768,-32768,-32768,-32768,-32768,-32768,-32768, 27, -8, 101,
+-32768, 493, -8,-32768, 493, 10,-32768,-32768, 11, 9,
+ 18, 26,-32768, 49, -15, -13,-32768,-32768,-32768, 25,
+ 24, 48, 149,-32768,-32768, 49,-32768, 414, 39, 40,
+ 47,-32768, 9,-32768,-32768, 49,-32768,-32768,-32768, 66,
+-32768, 241,-32768,-32768, 50,-32768, 5, 65, 42, 66,
+ 17, 56, 55,-32768,-32768,-32768, 60,-32768, 75,-32768,
+ 80,-32768,-32768,-32768,-32768,-32768, 81, 82, 370, 85,
+ 98, 89,-32768,-32768, 88,-32768, 91,-32768,-32768,-32768,
+-32768, 284,-32768, 24,-32768, 103,-32768,-32768,-32768,-32768,
+-32768, 8, 43,-32768, 30,-32768,-32768, 457,-32768,-32768,
+ 92, 93,-32768,-32768, 95,-32768, 96,-32768,-32768, 327,
+-32768,-32768,-32768,-32768,-32768,-32768, 99, 104,-32768,-32768,
+ 148,-32768
};
static const short yypgoto[] = {-32768,
- 208,-32768,-32768,-32768, 158,-32768,-32768, 128, 0, -90,
- -36,-32768, 157,-32768, -70,-32768,-32768, -51, -31,-32768,
- -40,-32768, -125,-32768,-32768, 65, -97,-32768,-32768,-32768,
--32768, -19,-32768,-32768, 143,-32768,-32768, 83, 124, 141,
+ 152,-32768,-32768,-32768, 119,-32768,-32768, 94, 0, -55,
+ -35,-32768,-32768,-32768, -69,-32768,-32768, -56, -30,-32768,
+ -76,-32768, -122,-32768,-32768, 29, -62,-32768,-32768,-32768,
+-32768, -17,-32768,-32768, 105,-32768,-32768, 52, 86, 83,
-32768,-32768,-32768
};
-#define YYLAST 495
-
-
-static const short yytable[] = { 67,
- 99, 119, 35, 65, 54, 49, 152, 155, 84, 53,
- 91, 131, 47, 55, 88, 80, 89, 48, 171, 50,
- 125, 9, 159, 50, 92, 132, 99, 81, 99, 69,
- 18, 114, 87, 77, 168, 56, 160, 58, -89, 27,
- 57, 119, 140, 31, 157, 158, 156, 59, 143, 60,
- 58, 76, 142, -89, 60, 126, 127, 119, 129, 96,
- 59, 50, 60, 99, 68, 97, 95, 60, 79, 119,
- 96, 143, 143, 86, 45, 46, 97, 85, 60, 70,
- 106, 98, 67, 6, 7, 8, 9, 10, 11, 12,
- 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
- 110, 24, 25, 26, 27, 28, 111, 126, 31, 93,
- 94, 96, 112, 116, -19, 113, 133, 97, 32, 60,
- 98, -19, -103, 128, -19, 134, -19, 107, 93, -19,
- 88, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- 15, 16, 17, 18, 19, 20, 21, 22, 135, 24,
- 25, 26, 27, 28, 139, 140, 31, 136, 146, 156,
- 147, 148, -19, 154, 149, 142, 32, 60, 150, -19,
- -104, 163, -19, 172, -19, 5, 164, -19, 165, 6,
- 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
- 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
- 27, 28, 29, 30, 31, 166, 169, 170, 4, 75,
- -19, 78, 162, 115, 32, 108, 153, -19, 124, 118,
- -19, 0, -19, 6, 7, 8, 9, 10, 11, 12,
+#define YYLAST 533
+
+
+static const short yytable[] = { 78,
+ 67, 99, 35, 84, 65, 125, 54, 49, 155, 152,
+ 53, 80, 47, 88, 171, 89, 9, 48, 91, 55,
+ 127, 50, 129, 56, 50, 18, 114, 99, 81, 99,
+ 57, 69, 92, 87, 27, 77, 119, 168, 31, -89,
+ 126, 50, 67, 140, 96, 79, 58, 156, 131, 143,
+ 97, 76, 60, 142, -89, 60, 59, 68, 60, 95,
+ 85, 159, 132, 96, 99, 45, 46, 93, 94, 97,
+ 86, 60, 143, 143, 98, 160, 119, 126, 140, 157,
+ 158, 96, 156, 67, 58, 111, 112, 97, 142, 60,
+ 60, 106, 119, 113, 59, 116, 60, 128, 133, 134,
+ 98, 70, 93, 88, 119, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 135, 24, 25, 26, 27, 28, 139, 136,
+ 31, 146, 147, 148, 149, 154, -19, 150, 163, 164,
+ 32, 165, 166, -19, -103, 169, -19, 172, -19, 107,
+ 170, -19, 4, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
- 130, 24, 25, 26, 27, 28, 0, 0, 31, 0,
- 0, 0, 0, -82, 0, 0, 0, 0, 32, 0,
- 0, 0, 151, 0, 0, -82, 6, 7, 8, 9,
- 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
- 20, 21, 22, 0, 24, 25, 26, 27, 28, 0,
- 0, 31, 0, 0, 0, 0, -82, 0, 0, 0,
- 0, 32, 0, 0, 0, 167, 0, 0, -82, 6,
- 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
- 17, 18, 19, 20, 21, 22, 0, 24, 25, 26,
- 27, 28, 0, 0, 31, 0, 0, 0, 0, -82,
- 0, 0, 0, 0, 32, 0, 0, 0, 0, 0,
- 0, -82, 6, 7, 8, 9, 10, 11, 12, 13,
- 14, 15, 16, 17, 18, 19, 20, 21, 22, 0,
- 24, 25, 26, 27, 28, 0, 0, 31, 0, 0,
- 0, 0, 0, 140, 0, 0, 0, 141, 0, 0,
- 0, 0, 0, 142, 0, 60, 6, 7, 8, 9,
- 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
- 20, 21, 22, 0, 24, 25, 26, 27, 28, 0,
- 0, 31, 0, 0, 0, 0, 161, 0, 0, 0,
- 0, 32, 6, 7, 8, 9, 10, 11, 12, 13,
- 14, 15, 16, 17, 18, 19, 20, 21, 22, 0,
- 24, 25, 26, 27, 28, 0, 0, 31, 0, 0,
- 7, 8, 9, 10, 11, 0, 13, 32, 15, 16,
- 0, 18, 19, 20, 0, 22, 0, 24, 25, 26,
- 27, 28, 0, 0, 31, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 32
+ 75, 24, 25, 26, 27, 28, 162, 108, 31, 115,
+ 124, 0, 130, 0, -19, 153, 0, 0, 32, 0,
+ 0, -19, -104, 0, -19, 0, -19, 5, 0, -19,
+ 0, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 0, 0, 0,
+ 0, 0, -19, 0, 0, 0, 32, 0, 0, -19,
+ 0, 118, -19, 0, -19, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 0, 24, 25, 26, 27, 28, 0, 0,
+ 31, 0, 0, 0, 0, -82, 0, 0, 0, 0,
+ 32, 0, 0, 0, 151, 0, 0, -82, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 0, 24, 25, 26, 27,
+ 28, 0, 0, 31, 0, 0, 0, 0, -82, 0,
+ 0, 0, 0, 32, 0, 0, 0, 167, 0, 0,
+ -82, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 0, 24,
+ 25, 26, 27, 28, 0, 0, 31, 0, 0, 0,
+ 0, -82, 0, 0, 0, 0, 32, 0, 0, 0,
+ 0, 0, 0, -82, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 0, 24, 25, 26, 27, 28, 0, 0, 31,
+ 0, 0, 0, 0, 0, 140, 0, 0, 0, 141,
+ 0, 0, 0, 0, 0, 142, 0, 60, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 0, 24, 25, 26, 27,
+ 28, 0, 0, 31, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 32, 0, 0, 0, 0, 0, 0,
+ 110, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 0, 24,
+ 25, 26, 27, 28, 0, 0, 31, 0, 0, 0,
+ 0, 161, 0, 0, 0, 0, 32, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 0, 24, 25, 26, 27, 28,
+ 0, 0, 31, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 32
};
-static const short yycheck[] = { 36,
- 71, 92, 3, 35, 24, 36, 132, 1, 60, 36,
- 32, 32, 31, 46, 29, 1, 31, 36, 0, 50,
- 1, 8, 32, 50, 46, 46, 97, 59, 99, 49,
- 17, 83, 64, 53, 160, 46, 46, 36, 32, 26,
- 43, 132, 36, 30, 142, 143, 40, 46, 119, 48,
- 36, 52, 46, 47, 48, 36, 97, 148, 99, 40,
- 46, 50, 48, 134, 31, 46, 36, 48, 36, 160,
- 40, 142, 143, 45, 43, 44, 46, 43, 48, 1,
- 44, 51, 119, 5, 6, 7, 8, 9, 10, 11,
- 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
- 47, 23, 24, 25, 26, 27, 47, 36, 30, 43,
- 44, 40, 47, 30, 36, 47, 43, 46, 40, 48,
- 51, 43, 44, 33, 46, 45, 48, 1, 43, 51,
- 29, 5, 6, 7, 8, 9, 10, 11, 12, 13,
- 14, 15, 16, 17, 18, 19, 20, 21, 43, 23,
- 24, 25, 26, 27, 47, 36, 30, 49, 47, 40,
- 35, 45, 36, 33, 47, 46, 40, 48, 47, 43,
- 44, 47, 46, 0, 48, 1, 47, 51, 47, 5,
- 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
- 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
- 26, 27, 28, 29, 30, 47, 47, 47, 1, 52,
- 36, 55, 148, 86, 40, 73, 134, 43, 95, 1,
- 46, -1, 48, 5, 6, 7, 8, 9, 10, 11,
+static const short yycheck[] = { 55,
+ 36, 71, 3, 60, 35, 1, 24, 36, 1, 132,
+ 36, 1, 31, 29, 0, 31, 8, 36, 32, 46,
+ 97, 50, 99, 46, 50, 17, 83, 97, 59, 99,
+ 43, 49, 46, 64, 26, 53, 92, 160, 30, 32,
+ 36, 50, 78, 36, 40, 36, 36, 40, 32, 119,
+ 46, 52, 48, 46, 47, 48, 46, 31, 48, 36,
+ 43, 32, 46, 40, 134, 43, 44, 43, 44, 46,
+ 45, 48, 142, 143, 51, 46, 132, 36, 36, 142,
+ 143, 40, 40, 119, 36, 47, 47, 46, 46, 48,
+ 48, 44, 148, 47, 46, 30, 48, 33, 43, 45,
+ 51, 1, 43, 29, 160, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 43, 23, 24, 25, 26, 27, 47, 49,
+ 30, 47, 35, 45, 47, 33, 36, 47, 47, 47,
+ 40, 47, 47, 43, 44, 47, 46, 0, 48, 1,
+ 47, 51, 1, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
- 100, 23, 24, 25, 26, 27, -1, -1, 30, -1,
- -1, -1, -1, 35, -1, -1, -1, -1, 40, -1,
- -1, -1, 1, -1, -1, 47, 5, 6, 7, 8,
- 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
- 19, 20, 21, -1, 23, 24, 25, 26, 27, -1,
- -1, 30, -1, -1, -1, -1, 35, -1, -1, -1,
- -1, 40, -1, -1, -1, 1, -1, -1, 47, 5,
- 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
- 16, 17, 18, 19, 20, 21, -1, 23, 24, 25,
- 26, 27, -1, -1, 30, -1, -1, -1, -1, 35,
- -1, -1, -1, -1, 40, -1, -1, -1, -1, -1,
- -1, 47, 5, 6, 7, 8, 9, 10, 11, 12,
- 13, 14, 15, 16, 17, 18, 19, 20, 21, -1,
- 23, 24, 25, 26, 27, -1, -1, 30, -1, -1,
- -1, -1, -1, 36, -1, -1, -1, 40, -1, -1,
- -1, -1, -1, 46, -1, 48, 5, 6, 7, 8,
- 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
- 19, 20, 21, -1, 23, 24, 25, 26, 27, -1,
- -1, 30, -1, -1, -1, -1, 35, -1, -1, -1,
- -1, 40, 5, 6, 7, 8, 9, 10, 11, 12,
- 13, 14, 15, 16, 17, 18, 19, 20, 21, -1,
- 23, 24, 25, 26, 27, -1, -1, 30, -1, -1,
- 6, 7, 8, 9, 10, -1, 12, 40, 14, 15,
- -1, 17, 18, 19, -1, 21, -1, 23, 24, 25,
- 26, 27, -1, -1, 30, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 40
+ 52, 23, 24, 25, 26, 27, 148, 73, 30, 86,
+ 95, -1, 100, -1, 36, 134, -1, -1, 40, -1,
+ -1, 43, 44, -1, 46, -1, 48, 1, -1, 51,
+ -1, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, -1, -1, -1,
+ -1, -1, 36, -1, -1, -1, 40, -1, -1, 43,
+ -1, 1, 46, -1, 48, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, -1, 23, 24, 25, 26, 27, -1, -1,
+ 30, -1, -1, -1, -1, 35, -1, -1, -1, -1,
+ 40, -1, -1, -1, 1, -1, -1, 47, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, -1, 23, 24, 25, 26,
+ 27, -1, -1, 30, -1, -1, -1, -1, 35, -1,
+ -1, -1, -1, 40, -1, -1, -1, 1, -1, -1,
+ 47, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, -1, 23,
+ 24, 25, 26, 27, -1, -1, 30, -1, -1, -1,
+ -1, 35, -1, -1, -1, -1, 40, -1, -1, -1,
+ -1, -1, -1, 47, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, -1, 23, 24, 25, 26, 27, -1, -1, 30,
+ -1, -1, -1, -1, -1, 36, -1, -1, -1, 40,
+ -1, -1, -1, -1, -1, 46, -1, 48, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, -1, 23, 24, 25, 26,
+ 27, -1, -1, 30, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 40, -1, -1, -1, -1, -1, -1,
+ 47, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, -1, 23,
+ 24, 25, 26, 27, -1, -1, 30, -1, -1, -1,
+ -1, 35, -1, -1, -1, -1, 40, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, -1, 23, 24, 25, 26, 27,
+ -1, -1, 30, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 40
};
/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
#line 3 "/usr/lib/bison.simple"
strncpy(menu_item, item, menu_width);
menu_item[menu_width] = 0;
- j = first_alpha(menu_item, "YyNnMm");
+ j = first_alpha(menu_item, "YyNnMmHh");
/* Clear 'residue' of last item */
wattrset (win, menubox_attr);
if (key < 256 && isalpha(key)) key = tolower(key);
- if (strchr("ynm", key))
+ if (strchr("ynmh", key))
i = max_choice;
else {
for (i = choice+1; i < max_choice; i++) {
- j = first_alpha(items[(scroll+i)*2+1], "YyNnMm");
+ j = first_alpha(items[(scroll+i)*2+1], "YyNnMmHh");
if (key == tolower(items[(scroll+i)*2+1][j]))
break;
}
if (i == max_choice)
for (i = 0; i < max_choice; i++) {
- j = first_alpha(items[(scroll+i)*2+1], "YyNnMm");
+ j = first_alpha(items[(scroll+i)*2+1], "YyNnMmHh");
if (key == tolower(items[(scroll+i)*2+1][j]))
break;
}
--- /dev/null
+#!/bin/sh
+# Generates a small Makefile used in the root of the output
+# directory, to allow make to be started from there.
+# The Makefile also allow for more convinient build of external modules
+
+# Usage
+# $1 - Kernel src directory
+# $2 - Output directory
+# $3 - version
+# $4 - patchlevel
+
+
+cat << EOF
+# Automatically generated by $0: don't edit
+
+VERSION = $3
+PATCHLEVEL = $4
+
+KERNELSRC := $1
+KERNELOUTPUT := $2
+
+MAKEFLAGS += --no-print-directory
+
+all:
+ \$(MAKE) -C \$(KERNELSRC) O=\$(KERNELOUTPUT)
+
+%::
+ \$(MAKE) -C \$(KERNELSRC) O=\$(KERNELOUTPUT) \$@
+
+EOF
+
--- /dev/null
+/* Simple code to turn various tables in an ELF file into alias definitions.
+ * This deals with kernel datastructures where they should be
+ * dealt with: in the kernel source.
+ *
+ * Copyright 2002-2003 Rusty Russell, IBM Corporation
+ * 2003 Kai Germaschewski
+ *
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include "modpost.h"
+
+/* We use the ELF typedefs, since we can't rely on stdint.h being present. */
+
+#if KERNEL_ELFCLASS == ELFCLASS32
+typedef Elf32_Addr kernel_ulong_t;
+#else
+typedef Elf64_Addr kernel_ulong_t;
+#endif
+
+typedef Elf32_Word __u32;
+typedef Elf32_Half __u16;
+typedef unsigned char __u8;
+
+/* Big exception to the "don't include kernel headers into userspace, which
+ * even potentially has different endianness and word sizes, since
+ * we handle those differences explicitly below */
+#include "../../include/linux/mod_devicetable.h"
+
+#define ADD(str, sep, cond, field) \
+do { \
+ strcat(str, sep); \
+ if (cond) \
+ sprintf(str + strlen(str), \
+ sizeof(field) == 1 ? "%02X" : \
+ sizeof(field) == 2 ? "%04X" : \
+ sizeof(field) == 4 ? "%08X" : "", \
+ field); \
+ else \
+ sprintf(str + strlen(str), "*"); \
+} while(0)
+
+/* Looks like "usb:vNpNdlNdhNdcNdscNdpNicNiscNipN" */
+static int do_usb_entry(const char *filename,
+ struct usb_device_id *id, char *alias)
+{
+ id->match_flags = TO_NATIVE(id->match_flags);
+ id->idVendor = TO_NATIVE(id->idVendor);
+ id->idProduct = TO_NATIVE(id->idProduct);
+ id->bcdDevice_lo = TO_NATIVE(id->bcdDevice_lo);
+ id->bcdDevice_hi = TO_NATIVE(id->bcdDevice_hi);
+
+ /*
+ * Some modules (visor) have empty slots as placeholder for
+ * run-time specification that results in catch-all alias
+ */
+ if (!(id->idVendor | id->bDeviceClass | id->bInterfaceClass))
+ return 1;
+
+ strcpy(alias, "usb:");
+ ADD(alias, "v", id->match_flags&USB_DEVICE_ID_MATCH_VENDOR,
+ id->idVendor);
+ ADD(alias, "p", id->match_flags&USB_DEVICE_ID_MATCH_PRODUCT,
+ id->idProduct);
+ ADD(alias, "dl", id->match_flags&USB_DEVICE_ID_MATCH_DEV_LO,
+ id->bcdDevice_lo);
+ ADD(alias, "dh", id->match_flags&USB_DEVICE_ID_MATCH_DEV_HI,
+ id->bcdDevice_hi);
+ ADD(alias, "dc", id->match_flags&USB_DEVICE_ID_MATCH_DEV_CLASS,
+ id->bDeviceClass);
+ ADD(alias, "dsc",
+ id->match_flags&USB_DEVICE_ID_MATCH_DEV_SUBCLASS,
+ id->bDeviceSubClass);
+ ADD(alias, "dp",
+ id->match_flags&USB_DEVICE_ID_MATCH_DEV_PROTOCOL,
+ id->bDeviceProtocol);
+ ADD(alias, "ic",
+ id->match_flags&USB_DEVICE_ID_MATCH_INT_CLASS,
+ id->bInterfaceClass);
+ ADD(alias, "isc",
+ id->match_flags&USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+ id->bInterfaceSubClass);
+ ADD(alias, "ip",
+ id->match_flags&USB_DEVICE_ID_MATCH_INT_PROTOCOL,
+ id->bInterfaceProtocol);
+ return 1;
+}
+
+/* Looks like: ieee1394:venNmoNspNverN */
+static int do_ieee1394_entry(const char *filename,
+ struct ieee1394_device_id *id, char *alias)
+{
+ id->match_flags = TO_NATIVE(id->match_flags);
+ id->vendor_id = TO_NATIVE(id->vendor_id);
+ id->model_id = TO_NATIVE(id->model_id);
+ id->specifier_id = TO_NATIVE(id->specifier_id);
+ id->version = TO_NATIVE(id->version);
+
+ strcpy(alias, "ieee1394:");
+ ADD(alias, "ven", id->match_flags & IEEE1394_MATCH_VENDOR_ID,
+ id->vendor_id);
+ ADD(alias, "mo", id->match_flags & IEEE1394_MATCH_MODEL_ID,
+ id->model_id);
+ ADD(alias, "sp", id->match_flags & IEEE1394_MATCH_SPECIFIER_ID,
+ id->specifier_id);
+ ADD(alias, "ver", id->match_flags & IEEE1394_MATCH_VERSION,
+ id->version);
+
+ return 1;
+}
+
+/* Looks like: pci:vNdNsvNsdNbcNscNiN. */
+static int do_pci_entry(const char *filename,
+ struct pci_device_id *id, char *alias)
+{
+ /* Class field can be divided into these three. */
+ unsigned char baseclass, subclass, interface,
+ baseclass_mask, subclass_mask, interface_mask;
+
+ id->vendor = TO_NATIVE(id->vendor);
+ id->device = TO_NATIVE(id->device);
+ id->subvendor = TO_NATIVE(id->subvendor);
+ id->subdevice = TO_NATIVE(id->subdevice);
+ id->class = TO_NATIVE(id->class);
+ id->class_mask = TO_NATIVE(id->class_mask);
+
+ strcpy(alias, "pci:");
+ ADD(alias, "v", id->vendor != PCI_ANY_ID, id->vendor);
+ ADD(alias, "d", id->device != PCI_ANY_ID, id->device);
+ ADD(alias, "sv", id->subvendor != PCI_ANY_ID, id->subvendor);
+ ADD(alias, "sd", id->subdevice != PCI_ANY_ID, id->subdevice);
+
+ baseclass = (id->class) >> 16;
+ baseclass_mask = (id->class_mask) >> 16;
+ subclass = (id->class) >> 8;
+ subclass_mask = (id->class_mask) >> 8;
+ interface = id->class;
+ interface_mask = id->class_mask;
+
+ if ((baseclass_mask != 0 && baseclass_mask != 0xFF)
+ || (subclass_mask != 0 && subclass_mask != 0xFF)
+ || (interface_mask != 0 && interface_mask != 0xFF)) {
+ fprintf(stderr,
+ "*** Warning: Can't handle masks in %s:%04X\n",
+ filename, id->class_mask);
+ return 0;
+ }
+
+ ADD(alias, "bc", baseclass_mask == 0xFF, baseclass);
+ ADD(alias, "sc", subclass_mask == 0xFF, subclass);
+ ADD(alias, "i", interface_mask == 0xFF, interface);
+ return 1;
+}
+
+/* looks like: "ccw:tNmNdtNdmN" */
+static int do_ccw_entry(const char *filename,
+ struct ccw_device_id *id, char *alias)
+{
+ id->match_flags = TO_NATIVE(id->match_flags);
+ id->cu_type = TO_NATIVE(id->cu_type);
+ id->cu_model = TO_NATIVE(id->cu_model);
+ id->dev_type = TO_NATIVE(id->dev_type);
+ id->dev_model = TO_NATIVE(id->dev_model);
+
+ strcpy(alias, "ccw:");
+ ADD(alias, "t", id->match_flags&CCW_DEVICE_ID_MATCH_CU_TYPE,
+ id->cu_type);
+ ADD(alias, "m", id->match_flags&CCW_DEVICE_ID_MATCH_CU_MODEL,
+ id->cu_model);
+ ADD(alias, "dt", id->match_flags&CCW_DEVICE_ID_MATCH_DEVICE_TYPE,
+ id->dev_type);
+ ADD(alias, "dm", id->match_flags&CCW_DEVICE_ID_MATCH_DEVICE_TYPE,
+ id->dev_model);
+ return 1;
+}
+
+/* looks like: "pnp:dD" */
+static int do_pnp_entry(const char *filename,
+ struct pnp_device_id *id, char *alias)
+{
+ sprintf(alias, "pnp:d%s", id->id);
+ return 1;
+}
+
+/* looks like: "pnp:cCdD..." */
+static int do_pnp_card_entry(const char *filename,
+ struct pnp_card_device_id *id, char *alias)
+{
+ int i;
+
+ sprintf(alias, "pnp:c%s", id->id);
+ for (i = 0; i < PNP_MAX_DEVICES; i++) {
+ if (! *id->devs[i].id)
+ break;
+ sprintf(alias + strlen(alias), "d%s", id->devs[i].id);
+ }
+ return 1;
+}
+
+/* Ignore any prefix, eg. v850 prepends _ */
+static inline int sym_is(const char *symbol, const char *name)
+{
+ const char *match;
+
+ match = strstr(symbol, name);
+ if (!match)
+ return 0;
+ return match[strlen(symbol)] == '\0';
+}
+
+static void do_table(void *symval, unsigned long size,
+ unsigned long id_size,
+ void *function,
+ struct module *mod)
+{
+ unsigned int i;
+ char alias[500];
+ int (*do_entry)(const char *, void *entry, char *alias) = function;
+
+ if (size % id_size || size < id_size) {
+ fprintf(stderr, "*** Warning: %s ids %lu bad size "
+ "(each on %lu)\n", mod->name, size, id_size);
+ }
+ /* Leave last one: it's the terminator. */
+ size -= id_size;
+
+ for (i = 0; i < size; i += id_size) {
+ if (do_entry(mod->name, symval+i, alias)) {
+ /* Always end in a wildcard, for future extension */
+ if (alias[strlen(alias)-1] != '*')
+ strcat(alias, "*");
+ buf_printf(&mod->dev_table_buf,
+ "MODULE_ALIAS(\"%s\");\n", alias);
+ }
+ }
+}
+
+/* Create MODULE_ALIAS() statements.
+ * At this time, we cannot write the actual output C source yet,
+ * so we write into the mod->dev_table_buf buffer. */
+void handle_moddevtable(struct module *mod, struct elf_info *info,
+ Elf_Sym *sym, const char *symname)
+{
+ void *symval;
+
+ /* We're looking for a section relative symbol */
+ if (!sym->st_shndx || sym->st_shndx >= info->hdr->e_shnum)
+ return;
+
+ symval = (void *)info->hdr
+ + info->sechdrs[sym->st_shndx].sh_offset
+ + sym->st_value;
+
+ if (sym_is(symname, "__mod_pci_device_table"))
+ do_table(symval, sym->st_size, sizeof(struct pci_device_id),
+ do_pci_entry, mod);
+ else if (sym_is(symname, "__mod_usb_device_table"))
+ do_table(symval, sym->st_size, sizeof(struct usb_device_id),
+ do_usb_entry, mod);
+ else if (sym_is(symname, "__mod_ieee1394_device_table"))
+ do_table(symval, sym->st_size, sizeof(struct ieee1394_device_id),
+ do_ieee1394_entry, mod);
+ else if (sym_is(symname, "__mod_ccw_device_table"))
+ do_table(symval, sym->st_size, sizeof(struct ccw_device_id),
+ do_ccw_entry, mod);
+ else if (sym_is(symname, "__mod_pnp_device_table"))
+ do_table(symval, sym->st_size, sizeof(struct pnp_device_id),
+ do_pnp_entry, mod);
+ else if (sym_is(symname, "__mod_pnp_card_device_table"))
+ do_table(symval, sym->st_size, sizeof(struct pnp_card_device_id),
+ do_pnp_card_entry, mod);
+}
+
+/* Now add out buffered information to the generated C source */
+void add_moddevtable(struct buffer *buf, struct module *mod)
+{
+ buf_printf(buf, "\n");
+ buf_write(buf, mod->dev_table_buf.p, mod->dev_table_buf.pos);
+ free(mod->dev_table_buf.p);
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <elf.h>
+
+int
+main(int argc, char **argv)
+{
+ unsigned char ei[EI_NIDENT];
+ union { short s; char c[2]; } endian_test;
+
+ if (argc != 2) {
+ fprintf(stderr, "Error: no arch\n");
+ }
+ if (fread(ei, 1, EI_NIDENT, stdin) != EI_NIDENT) {
+ fprintf(stderr, "Error: input truncated\n");
+ return 1;
+ }
+ if (memcmp(ei, ELFMAG, SELFMAG) != 0) {
+ fprintf(stderr, "Error: not ELF\n");
+ return 1;
+ }
+ switch (ei[EI_CLASS]) {
+ case ELFCLASS32:
+ printf("#define KERNEL_ELFCLASS ELFCLASS32\n");
+ break;
+ case ELFCLASS64:
+ printf("#define KERNEL_ELFCLASS ELFCLASS64\n");
+ break;
+ default:
+ abort();
+ }
+ switch (ei[EI_DATA]) {
+ case ELFDATA2LSB:
+ printf("#define KERNEL_ELFDATA ELFDATA2LSB\n");
+ break;
+ case ELFDATA2MSB:
+ printf("#define KERNEL_ELFDATA ELFDATA2MSB\n");
+ break;
+ default:
+ abort();
+ }
+
+ if (sizeof(unsigned long) == 4) {
+ printf("#define HOST_ELFCLASS ELFCLASS32\n");
+ } else if (sizeof(unsigned long) == 8) {
+ printf("#define HOST_ELFCLASS ELFCLASS64\n");
+ }
+
+ endian_test.s = 0x0102;
+ if (memcmp(endian_test.c, "\x01\x02", 2) == 0)
+ printf("#define HOST_ELFDATA ELFDATA2MSB\n");
+ else if (memcmp(endian_test.c, "\x02\x01", 2) == 0)
+ printf("#define HOST_ELFDATA ELFDATA2LSB\n");
+ else
+ abort();
+
+ if ((strcmp(argv[1], "v850") == 0) || (strcmp(argv[1], "h8300") == 0))
+ printf("#define MODULE_SYMBOL_PREFIX \"_\"\n");
+ else
+ printf("#define MODULE_SYMBOL_PREFIX \"\"\n");
+
+ return 0;
+}
+
--- /dev/null
+/* Postprocess module symbol versions
+ *
+ * Copyright 2003 Kai Germaschewski
+ * 2002-2003 Rusty Russell, IBM Corporation
+ *
+ * Based in part on module-init-tools/depmod.c,file2alias
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Usage: modpost vmlinux module1.o module2.o ...
+ */
+
+#include <ctype.h>
+#include "modpost.h"
+
+/* Are we using CONFIG_MODVERSIONS? */
+int modversions = 0;
+/* Warn about undefined symbols? (do so if we have vmlinux) */
+int have_vmlinux = 0;
+
+void
+fatal(const char *fmt, ...)
+{
+ va_list arglist;
+
+ fprintf(stderr, "FATAL: ");
+
+ va_start(arglist, fmt);
+ vfprintf(stderr, fmt, arglist);
+ va_end(arglist);
+
+ exit(1);
+}
+
+void
+warn(const char *fmt, ...)
+{
+ va_list arglist;
+
+ fprintf(stderr, "WARNING: ");
+
+ va_start(arglist, fmt);
+ vfprintf(stderr, fmt, arglist);
+ va_end(arglist);
+}
+
+void *do_nofail(void *ptr, const char *file, int line, const char *expr)
+{
+ if (!ptr) {
+ fatal("Memory allocation failure %s line %d: %s.\n",
+ file, line, expr);
+ }
+ return ptr;
+}
+
+/* A list of all modules we processed */
+
+static struct module *modules;
+
+struct module *
+find_module(char *modname)
+{
+ struct module *mod;
+
+ for (mod = modules; mod; mod = mod->next)
+ if (strcmp(mod->name, modname) == 0)
+ break;
+ return mod;
+}
+
+struct module *
+new_module(char *modname)
+{
+ struct module *mod;
+ char *p, *s;
+
+ mod = NOFAIL(malloc(sizeof(*mod)));
+ memset(mod, 0, sizeof(*mod));
+ p = NOFAIL(strdup(modname));
+
+ /* strip trailing .o */
+ if ((s = strrchr(p, '.')) != NULL)
+ if (strcmp(s, ".o") == 0)
+ *s = '\0';
+
+ /* add to list */
+ mod->name = p;
+ mod->next = modules;
+ modules = mod;
+
+ return mod;
+}
+
+/* A hash of all exported symbols,
+ * struct symbol is also used for lists of unresolved symbols */
+
+#define SYMBOL_HASH_SIZE 1024
+
+struct symbol {
+ struct symbol *next;
+ struct module *module;
+ unsigned int crc;
+ int crc_valid;
+ char name[0];
+};
+
+static struct symbol *symbolhash[SYMBOL_HASH_SIZE];
+
+/* This is based on the hash agorithm from gdbm, via tdb */
+static inline unsigned int tdb_hash(const char *name)
+{
+ unsigned value; /* Used to compute the hash value. */
+ unsigned i; /* Used to cycle through random values. */
+
+ /* Set the initial value from the key size. */
+ for (value = 0x238F13AF * strlen(name), i=0; name[i]; i++)
+ value = (value + (((unsigned char *)name)[i] << (i*5 % 24)));
+
+ return (1103515243 * value + 12345);
+}
+
+/* Allocate a new symbols for use in the hash of exported symbols or
+ * the list of unresolved symbols per module */
+
+struct symbol *
+alloc_symbol(const char *name, struct symbol *next)
+{
+ struct symbol *s = NOFAIL(malloc(sizeof(*s) + strlen(name) + 1));
+
+ memset(s, 0, sizeof(*s));
+ strcpy(s->name, name);
+ s->next = next;
+ return s;
+}
+
+/* For the hash of exported symbols */
+
+void
+new_symbol(const char *name, struct module *module, unsigned int *crc)
+{
+ unsigned int hash;
+ struct symbol *new;
+
+ hash = tdb_hash(name) % SYMBOL_HASH_SIZE;
+ new = symbolhash[hash] = alloc_symbol(name, symbolhash[hash]);
+ new->module = module;
+ if (crc) {
+ new->crc = *crc;
+ new->crc_valid = 1;
+ }
+}
+
+struct symbol *
+find_symbol(const char *name)
+{
+ struct symbol *s;
+
+ /* For our purposes, .foo matches foo. PPC64 needs this. */
+ if (name[0] == '.')
+ name++;
+
+ for (s = symbolhash[tdb_hash(name) % SYMBOL_HASH_SIZE]; s; s=s->next) {
+ if (strcmp(s->name, name) == 0)
+ return s;
+ }
+ return NULL;
+}
+
+/* Add an exported symbol - it may have already been added without a
+ * CRC, in this case just update the CRC */
+void
+add_exported_symbol(const char *name, struct module *module, unsigned int *crc)
+{
+ struct symbol *s = find_symbol(name);
+
+ if (!s) {
+ new_symbol(name, module, crc);
+ return;
+ }
+ if (crc) {
+ s->crc = *crc;
+ s->crc_valid = 1;
+ }
+}
+
+void *
+grab_file(const char *filename, unsigned long *size)
+{
+ struct stat st;
+ void *map;
+ int fd;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0 || fstat(fd, &st) != 0)
+ return NULL;
+
+ *size = st.st_size;
+ map = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+ close(fd);
+
+ if (map == MAP_FAILED)
+ return NULL;
+ return map;
+}
+
+/*
+ Return a copy of the next line in a mmap'ed file.
+ spaces in the beginning of the line is trimmed away.
+ Return a pointer to a static buffer.
+*/
+char*
+get_next_line(unsigned long *pos, void *file, unsigned long size)
+{
+ static char line[4096];
+ int skip = 1;
+ size_t len = 0;
+ char *p = (char *)file + *pos;
+ char *s = line;
+
+ for (; *pos < size ; (*pos)++)
+ {
+ if (skip && isspace(*p)) {
+ p++;
+ continue;
+ }
+ skip = 0;
+ if (*p != '\n' && (*pos < size)) {
+ len++;
+ *s++ = *p++;
+ if (len > 4095)
+ break; /* Too long, stop */
+ } else {
+ /* End of string */
+ *s = '\0';
+ return line;
+ }
+ }
+ /* End of buffer */
+ return NULL;
+}
+
+void
+release_file(void *file, unsigned long size)
+{
+ munmap(file, size);
+}
+
+void
+parse_elf(struct elf_info *info, const char *filename)
+{
+ unsigned int i;
+ Elf_Ehdr *hdr = info->hdr;
+ Elf_Shdr *sechdrs;
+ Elf_Sym *sym;
+
+ hdr = grab_file(filename, &info->size);
+ if (!hdr) {
+ perror(filename);
+ abort();
+ }
+ info->hdr = hdr;
+ if (info->size < sizeof(*hdr))
+ goto truncated;
+
+ /* Fix endianness in ELF header */
+ hdr->e_shoff = TO_NATIVE(hdr->e_shoff);
+ hdr->e_shstrndx = TO_NATIVE(hdr->e_shstrndx);
+ hdr->e_shnum = TO_NATIVE(hdr->e_shnum);
+ hdr->e_machine = TO_NATIVE(hdr->e_machine);
+ sechdrs = (void *)hdr + hdr->e_shoff;
+ info->sechdrs = sechdrs;
+
+ /* Fix endianness in section headers */
+ for (i = 0; i < hdr->e_shnum; i++) {
+ sechdrs[i].sh_type = TO_NATIVE(sechdrs[i].sh_type);
+ sechdrs[i].sh_offset = TO_NATIVE(sechdrs[i].sh_offset);
+ sechdrs[i].sh_size = TO_NATIVE(sechdrs[i].sh_size);
+ sechdrs[i].sh_link = TO_NATIVE(sechdrs[i].sh_link);
+ sechdrs[i].sh_name = TO_NATIVE(sechdrs[i].sh_name);
+ }
+ /* Find symbol table. */
+ for (i = 1; i < hdr->e_shnum; i++) {
+ const char *secstrings
+ = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
+
+ if (sechdrs[i].sh_offset > info->size)
+ goto truncated;
+ if (strcmp(secstrings+sechdrs[i].sh_name, ".modinfo") == 0) {
+ info->modinfo = (void *)hdr + sechdrs[i].sh_offset;
+ info->modinfo_len = sechdrs[i].sh_size;
+ }
+ if (sechdrs[i].sh_type != SHT_SYMTAB)
+ continue;
+
+ info->symtab_start = (void *)hdr + sechdrs[i].sh_offset;
+ info->symtab_stop = (void *)hdr + sechdrs[i].sh_offset
+ + sechdrs[i].sh_size;
+ info->strtab = (void *)hdr +
+ sechdrs[sechdrs[i].sh_link].sh_offset;
+ }
+ if (!info->symtab_start) {
+ fprintf(stderr, "modpost: %s no symtab?\n", filename);
+ abort();
+ }
+ /* Fix endianness in symbols */
+ for (sym = info->symtab_start; sym < info->symtab_stop; sym++) {
+ sym->st_shndx = TO_NATIVE(sym->st_shndx);
+ sym->st_name = TO_NATIVE(sym->st_name);
+ sym->st_value = TO_NATIVE(sym->st_value);
+ sym->st_size = TO_NATIVE(sym->st_size);
+ }
+ return;
+
+ truncated:
+ fprintf(stderr, "modpost: %s is truncated.\n", filename);
+ abort();
+}
+
+void
+parse_elf_finish(struct elf_info *info)
+{
+ release_file(info->hdr, info->size);
+}
+
+#define CRC_PFX MODULE_SYMBOL_PREFIX "__crc_"
+#define KSYMTAB_PFX MODULE_SYMBOL_PREFIX "__ksymtab_"
+
+void
+handle_modversions(struct module *mod, struct elf_info *info,
+ Elf_Sym *sym, const char *symname)
+{
+ unsigned int crc;
+
+ switch (sym->st_shndx) {
+ case SHN_COMMON:
+ fprintf(stderr, "*** Warning: \"%s\" [%s] is COMMON symbol\n",
+ symname, mod->name);
+ break;
+ case SHN_ABS:
+ /* CRC'd symbol */
+ if (memcmp(symname, CRC_PFX, strlen(CRC_PFX)) == 0) {
+ crc = (unsigned int) sym->st_value;
+ add_exported_symbol(symname + strlen(CRC_PFX),
+ mod, &crc);
+ modversions = 1;
+ }
+ break;
+ case SHN_UNDEF:
+ /* undefined symbol */
+ if (ELF_ST_BIND(sym->st_info) != STB_GLOBAL)
+ break;
+ /* ignore global offset table */
+ if (strcmp(symname, "_GLOBAL_OFFSET_TABLE_") == 0)
+ break;
+ /* ignore __this_module, it will be resolved shortly */
+ if (strcmp(symname, MODULE_SYMBOL_PREFIX "__this_module") == 0)
+ break;
+#ifdef STT_REGISTER
+ if (info->hdr->e_machine == EM_SPARC ||
+ info->hdr->e_machine == EM_SPARCV9) {
+ /* Ignore register directives. */
+ if (ELF_ST_TYPE(sym->st_info) == STT_REGISTER)
+ break;
+ }
+#endif
+
+ if (memcmp(symname, MODULE_SYMBOL_PREFIX,
+ strlen(MODULE_SYMBOL_PREFIX)) == 0)
+ mod->unres = alloc_symbol(symname +
+ strlen(MODULE_SYMBOL_PREFIX),
+ mod->unres);
+ break;
+ default:
+ /* All exported symbols */
+ if (memcmp(symname, KSYMTAB_PFX, strlen(KSYMTAB_PFX)) == 0) {
+ add_exported_symbol(symname + strlen(KSYMTAB_PFX),
+ mod, NULL);
+ }
+ break;
+ }
+}
+
+int
+is_vmlinux(const char *modname)
+{
+ const char *myname;
+
+ if ((myname = strrchr(modname, '/')))
+ myname++;
+ else
+ myname = modname;
+
+ return strcmp(myname, "vmlinux") == 0;
+}
+
+void
+read_symbols(char *modname)
+{
+ const char *symname;
+ struct module *mod;
+ struct elf_info info = { };
+ Elf_Sym *sym;
+
+ parse_elf(&info, modname);
+
+ mod = new_module(modname);
+
+ /* When there's no vmlinux, don't print warnings about
+ * unresolved symbols (since there'll be too many ;) */
+ if (is_vmlinux(modname)) {
+ unsigned int fake_crc = 0;
+ have_vmlinux = 1;
+ /* May not have this if !CONFIG_MODULE_UNLOAD: fake it.
+ If it appears, we'll get the real CRC. */
+ add_exported_symbol("cleanup_module", mod, &fake_crc);
+ add_exported_symbol("struct_module", mod, &fake_crc);
+ mod->skip = 1;
+ }
+
+ for (sym = info.symtab_start; sym < info.symtab_stop; sym++) {
+ symname = info.strtab + sym->st_name;
+
+ handle_modversions(mod, &info, sym, symname);
+ handle_moddevtable(mod, &info, sym, symname);
+ }
+ maybe_frob_version(modname, info.modinfo, info.modinfo_len,
+ (void *)info.modinfo - (void *)info.hdr);
+ parse_elf_finish(&info);
+
+ /* Our trick to get versioning for struct_module - it's
+ * never passed as an argument to an exported function, so
+ * the automatic versioning doesn't pick it up, but it's really
+ * important anyhow */
+ if (modversions) {
+ mod->unres = alloc_symbol("struct_module", mod->unres);
+
+ /* Always version init_module and cleanup_module, in
+ * case module doesn't have its own. */
+ mod->unres = alloc_symbol("init_module", mod->unres);
+ mod->unres = alloc_symbol("cleanup_module", mod->unres);
+ }
+}
+
+#define SZ 500
+
+/* We first write the generated file into memory using the
+ * following helper, then compare to the file on disk and
+ * only update the later if anything changed */
+
+void __attribute__((format(printf, 2, 3)))
+buf_printf(struct buffer *buf, const char *fmt, ...)
+{
+ char tmp[SZ];
+ int len;
+ va_list ap;
+
+ va_start(ap, fmt);
+ len = vsnprintf(tmp, SZ, fmt, ap);
+ if (buf->size - buf->pos < len + 1) {
+ buf->size += 128;
+ buf->p = realloc(buf->p, buf->size);
+ }
+ strncpy(buf->p + buf->pos, tmp, len + 1);
+ buf->pos += len;
+ va_end(ap);
+}
+
+void
+buf_write(struct buffer *buf, const char *s, int len)
+{
+ if (buf->size - buf->pos < len) {
+ buf->size += len;
+ buf->p = realloc(buf->p, buf->size);
+ }
+ strncpy(buf->p + buf->pos, s, len);
+ buf->pos += len;
+}
+
+/* Header for the generated file */
+
+void
+add_header(struct buffer *b)
+{
+ buf_printf(b, "#include <linux/module.h>\n");
+ buf_printf(b, "#include <linux/vermagic.h>\n");
+ buf_printf(b, "#include <linux/compiler.h>\n");
+ buf_printf(b, "\n");
+ buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n");
+ buf_printf(b, "\n");
+ buf_printf(b, "#undef unix\n"); /* We have a module called "unix" */
+ buf_printf(b, "struct module __this_module\n");
+ buf_printf(b, "__attribute__((section(\".gnu.linkonce.this_module\"))) = {\n");
+ buf_printf(b, " .name = __stringify(KBUILD_MODNAME),\n");
+ buf_printf(b, " .init = init_module,\n");
+ buf_printf(b, "#ifdef CONFIG_MODULE_UNLOAD\n");
+ buf_printf(b, " .exit = cleanup_module,\n");
+ buf_printf(b, "#endif\n");
+ buf_printf(b, "};\n");
+}
+
+/* Record CRCs for unresolved symbols */
+
+void
+add_versions(struct buffer *b, struct module *mod)
+{
+ struct symbol *s, *exp;
+
+ for (s = mod->unres; s; s = s->next) {
+ exp = find_symbol(s->name);
+ if (!exp || exp->module == mod) {
+ if (have_vmlinux)
+ fprintf(stderr, "*** Warning: \"%s\" [%s.ko] "
+ "undefined!\n", s->name, mod->name);
+ continue;
+ }
+ s->module = exp->module;
+ s->crc_valid = exp->crc_valid;
+ s->crc = exp->crc;
+ }
+
+ if (!modversions)
+ return;
+
+ buf_printf(b, "\n");
+ buf_printf(b, "static const struct modversion_info ____versions[]\n");
+ buf_printf(b, "__attribute_used__\n");
+ buf_printf(b, "__attribute__((section(\"__versions\"))) = {\n");
+
+ for (s = mod->unres; s; s = s->next) {
+ if (!s->module) {
+ continue;
+ }
+ if (!s->crc_valid) {
+ fprintf(stderr, "*** Warning: \"%s\" [%s.ko] "
+ "has no CRC!\n",
+ s->name, mod->name);
+ continue;
+ }
+ buf_printf(b, "\t{ %#8x, \"%s\" },\n", s->crc, s->name);
+ }
+
+ buf_printf(b, "};\n");
+}
+
+void
+add_depends(struct buffer *b, struct module *mod, struct module *modules)
+{
+ struct symbol *s;
+ struct module *m;
+ int first = 1;
+
+ for (m = modules; m; m = m->next) {
+ m->seen = is_vmlinux(m->name);
+ }
+
+ buf_printf(b, "\n");
+ buf_printf(b, "static const char __module_depends[]\n");
+ buf_printf(b, "__attribute_used__\n");
+ buf_printf(b, "__attribute__((section(\".modinfo\"))) =\n");
+ buf_printf(b, "\"depends=");
+ for (s = mod->unres; s; s = s->next) {
+ if (!s->module)
+ continue;
+
+ if (s->module->seen)
+ continue;
+
+ s->module->seen = 1;
+ buf_printf(b, "%s%s", first ? "" : ",",
+ strrchr(s->module->name, '/') + 1);
+ first = 0;
+ }
+ buf_printf(b, "\";\n");
+}
+
+void
+write_if_changed(struct buffer *b, const char *fname)
+{
+ char *tmp;
+ FILE *file;
+ struct stat st;
+
+ file = fopen(fname, "r");
+ if (!file)
+ goto write;
+
+ if (fstat(fileno(file), &st) < 0)
+ goto close_write;
+
+ if (st.st_size != b->pos)
+ goto close_write;
+
+ tmp = NOFAIL(malloc(b->pos));
+ if (fread(tmp, 1, b->pos, file) != b->pos)
+ goto free_write;
+
+ if (memcmp(tmp, b->p, b->pos) != 0)
+ goto free_write;
+
+ free(tmp);
+ fclose(file);
+ return;
+
+ free_write:
+ free(tmp);
+ close_write:
+ fclose(file);
+ write:
+ file = fopen(fname, "w");
+ if (!file) {
+ perror(fname);
+ exit(1);
+ }
+ if (fwrite(b->p, 1, b->pos, file) != b->pos) {
+ perror(fname);
+ exit(1);
+ }
+ fclose(file);
+}
+
+void
+read_dump(const char *fname)
+{
+ unsigned long size, pos = 0;
+ void *file = grab_file(fname, &size);
+ char *line;
+
+ if (!file)
+ /* No symbol versions, silently ignore */
+ return;
+
+ while ((line = get_next_line(&pos, file, size))) {
+ char *symname, *modname, *d;
+ unsigned int crc;
+ struct module *mod;
+
+ if (!(symname = strchr(line, '\t')))
+ goto fail;
+ *symname++ = '\0';
+ if (!(modname = strchr(symname, '\t')))
+ goto fail;
+ *modname++ = '\0';
+ if (strchr(modname, '\t'))
+ goto fail;
+ crc = strtoul(line, &d, 16);
+ if (*symname == '\0' || *modname == '\0' || *d != '\0')
+ goto fail;
+
+ if (!(mod = find_module(modname))) {
+ if (is_vmlinux(modname)) {
+ modversions = 1;
+ have_vmlinux = 1;
+ }
+ mod = new_module(NOFAIL(strdup(modname)));
+ mod->skip = 1;
+ }
+ add_exported_symbol(symname, mod, &crc);
+ }
+ return;
+fail:
+ fatal("parse error in symbol dump file\n");
+}
+
+void
+write_dump(const char *fname)
+{
+ struct buffer buf = { };
+ struct symbol *symbol;
+ int n;
+
+ for (n = 0; n < SYMBOL_HASH_SIZE ; n++) {
+ symbol = symbolhash[n];
+ while (symbol) {
+ symbol = symbol->next;
+ }
+ }
+
+ for (n = 0; n < SYMBOL_HASH_SIZE ; n++) {
+ symbol = symbolhash[n];
+ while (symbol) {
+ buf_printf(&buf, "0x%08x\t%s\t%s\n", symbol->crc,
+ symbol->name, symbol->module->name);
+ symbol = symbol->next;
+ }
+ }
+ write_if_changed(&buf, fname);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct module *mod;
+ struct buffer buf = { };
+ char fname[SZ];
+ char *dump_read = NULL, *dump_write = NULL;
+ int opt;
+
+ while ((opt = getopt(argc, argv, "i:o:")) != -1) {
+ switch(opt) {
+ case 'i':
+ dump_read = optarg;
+ break;
+ case 'o':
+ dump_write = optarg;
+ break;
+ default:
+ exit(1);
+ }
+ }
+
+ if (dump_read)
+ read_dump(dump_read);
+
+ while (optind < argc) {
+ read_symbols(argv[optind++]);
+ }
+
+ for (mod = modules; mod; mod = mod->next) {
+ if (mod->skip)
+ continue;
+
+ buf.pos = 0;
+
+ add_header(&buf);
+ add_versions(&buf, mod);
+ add_depends(&buf, mod, modules);
+ add_moddevtable(&buf, mod);
+
+ sprintf(fname, "%s.mod.c", mod->name);
+ write_if_changed(&buf, fname);
+ }
+
+ if (dump_write)
+ write_dump(dump_write);
+
+ return 0;
+}
+
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <elf.h>
+
+#include "elfconfig.h"
+
+#if KERNEL_ELFCLASS == ELFCLASS32
+
+#define Elf_Ehdr Elf32_Ehdr
+#define Elf_Shdr Elf32_Shdr
+#define Elf_Sym Elf32_Sym
+#define ELF_ST_BIND ELF32_ST_BIND
+#define ELF_ST_TYPE ELF32_ST_TYPE
+
+#else
+
+#define Elf_Ehdr Elf64_Ehdr
+#define Elf_Shdr Elf64_Shdr
+#define Elf_Sym Elf64_Sym
+#define ELF_ST_BIND ELF64_ST_BIND
+#define ELF_ST_TYPE ELF64_ST_TYPE
+
+#endif
+
+#if KERNEL_ELFDATA != HOST_ELFDATA
+
+static inline void __endian(const void *src, void *dest, unsigned int size)
+{
+ unsigned int i;
+ for (i = 0; i < size; i++)
+ ((unsigned char*)dest)[i] = ((unsigned char*)src)[size - i-1];
+}
+
+
+
+#define TO_NATIVE(x) \
+({ \
+ typeof(x) __x; \
+ __endian(&(x), &(__x), sizeof(__x)); \
+ __x; \
+})
+
+#else /* endianness matches */
+
+#define TO_NATIVE(x) (x)
+
+#endif
+
+#define NOFAIL(ptr) do_nofail((ptr), __FILE__, __LINE__, #ptr)
+void *do_nofail(void *ptr, const char *file, int line, const char *expr);
+
+struct buffer {
+ char *p;
+ int pos;
+ int size;
+};
+
+void __attribute__((format(printf, 2, 3)))
+buf_printf(struct buffer *buf, const char *fmt, ...);
+
+void
+buf_write(struct buffer *buf, const char *s, int len);
+
+struct module {
+ struct module *next;
+ const char *name;
+ struct symbol *unres;
+ int seen;
+ int skip;
+ struct buffer dev_table_buf;
+};
+
+struct elf_info {
+ unsigned long size;
+ Elf_Ehdr *hdr;
+ Elf_Shdr *sechdrs;
+ Elf_Sym *symtab_start;
+ Elf_Sym *symtab_stop;
+ const char *strtab;
+ char *modinfo;
+ unsigned int modinfo_len;
+};
+
+void handle_moddevtable(struct module *mod, struct elf_info *info,
+ Elf_Sym *sym, const char *symname);
+
+void add_moddevtable(struct buffer *buf, struct module *mod);
+
+void maybe_frob_version(const char *modfilename,
+ void *modinfo,
+ unsigned long modinfo_len,
+ unsigned long modinfo_offset);
+
+void *grab_file(const char *filename, unsigned long *size);
+char* get_next_line(unsigned long *pos, void *file, unsigned long size);
+void release_file(void *file, unsigned long size);
--- /dev/null
+#include <netinet/in.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include "modpost.h"
+
+/* Parse tag=value strings from .modinfo section */
+static char *next_string(char *string, unsigned long *secsize)
+{
+ /* Skip non-zero chars */
+ while (string[0]) {
+ string++;
+ if ((*secsize)-- <= 1)
+ return NULL;
+ }
+
+ /* Skip any zero padding. */
+ while (!string[0]) {
+ string++;
+ if ((*secsize)-- <= 1)
+ return NULL;
+ }
+ return string;
+}
+
+static char *get_modinfo(void *modinfo, unsigned long modinfo_len,
+ const char *tag)
+{
+ char *p;
+ unsigned int taglen = strlen(tag);
+ unsigned long size = modinfo_len;
+
+ for (p = modinfo; p; p = next_string(p, &size)) {
+ if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=')
+ return p + taglen + 1;
+ }
+ return NULL;
+}
+
+/*
+ * Stolen form Cryptographic API.
+ *
+ * MD4 Message Digest Algorithm (RFC1320).
+ *
+ * Implementation derived from Andrew Tridgell and Steve French's
+ * CIFS MD4 implementation, and the cryptoapi implementation
+ * originally based on the public domain implementation written
+ * by Colin Plumb in 1993.
+ *
+ * Copyright (c) Andrew Tridgell 1997-1998.
+ * Modified by Steve French (sfrench@us.ibm.com) 2002
+ * Copyright (c) Cryptoapi developers.
+ * Copyright (c) 2002 David S. Miller (davem@redhat.com)
+ * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
+ *
+ * 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.
+ *
+ */
+#define MD4_DIGEST_SIZE 16
+#define MD4_HMAC_BLOCK_SIZE 64
+#define MD4_BLOCK_WORDS 16
+#define MD4_HASH_WORDS 4
+
+struct md4_ctx {
+ uint32_t hash[MD4_HASH_WORDS];
+ uint32_t block[MD4_BLOCK_WORDS];
+ uint64_t byte_count;
+};
+
+static inline uint32_t lshift(uint32_t x, unsigned int s)
+{
+ x &= 0xFFFFFFFF;
+ return ((x << s) & 0xFFFFFFFF) | (x >> (32 - s));
+}
+
+static inline uint32_t F(uint32_t x, uint32_t y, uint32_t z)
+{
+ return (x & y) | ((~x) & z);
+}
+
+static inline uint32_t G(uint32_t x, uint32_t y, uint32_t z)
+{
+ return (x & y) | (x & z) | (y & z);
+}
+
+static inline uint32_t H(uint32_t x, uint32_t y, uint32_t z)
+{
+ return x ^ y ^ z;
+}
+
+#define ROUND1(a,b,c,d,k,s) (a = lshift(a + F(b,c,d) + k, s))
+#define ROUND2(a,b,c,d,k,s) (a = lshift(a + G(b,c,d) + k + (uint32_t)0x5A827999,s))
+#define ROUND3(a,b,c,d,k,s) (a = lshift(a + H(b,c,d) + k + (uint32_t)0x6ED9EBA1,s))
+
+/* XXX: this stuff can be optimized */
+static inline void le32_to_cpu_array(uint32_t *buf, unsigned int words)
+{
+ while (words--) {
+ *buf = ntohl(*buf);
+ buf++;
+ }
+}
+
+static inline void cpu_to_le32_array(uint32_t *buf, unsigned int words)
+{
+ while (words--) {
+ *buf = htonl(*buf);
+ buf++;
+ }
+}
+
+static void md4_transform(uint32_t *hash, uint32_t const *in)
+{
+ uint32_t a, b, c, d;
+
+ a = hash[0];
+ b = hash[1];
+ c = hash[2];
+ d = hash[3];
+
+ ROUND1(a, b, c, d, in[0], 3);
+ ROUND1(d, a, b, c, in[1], 7);
+ ROUND1(c, d, a, b, in[2], 11);
+ ROUND1(b, c, d, a, in[3], 19);
+ ROUND1(a, b, c, d, in[4], 3);
+ ROUND1(d, a, b, c, in[5], 7);
+ ROUND1(c, d, a, b, in[6], 11);
+ ROUND1(b, c, d, a, in[7], 19);
+ ROUND1(a, b, c, d, in[8], 3);
+ ROUND1(d, a, b, c, in[9], 7);
+ ROUND1(c, d, a, b, in[10], 11);
+ ROUND1(b, c, d, a, in[11], 19);
+ ROUND1(a, b, c, d, in[12], 3);
+ ROUND1(d, a, b, c, in[13], 7);
+ ROUND1(c, d, a, b, in[14], 11);
+ ROUND1(b, c, d, a, in[15], 19);
+
+ ROUND2(a, b, c, d,in[ 0], 3);
+ ROUND2(d, a, b, c, in[4], 5);
+ ROUND2(c, d, a, b, in[8], 9);
+ ROUND2(b, c, d, a, in[12], 13);
+ ROUND2(a, b, c, d, in[1], 3);
+ ROUND2(d, a, b, c, in[5], 5);
+ ROUND2(c, d, a, b, in[9], 9);
+ ROUND2(b, c, d, a, in[13], 13);
+ ROUND2(a, b, c, d, in[2], 3);
+ ROUND2(d, a, b, c, in[6], 5);
+ ROUND2(c, d, a, b, in[10], 9);
+ ROUND2(b, c, d, a, in[14], 13);
+ ROUND2(a, b, c, d, in[3], 3);
+ ROUND2(d, a, b, c, in[7], 5);
+ ROUND2(c, d, a, b, in[11], 9);
+ ROUND2(b, c, d, a, in[15], 13);
+
+ ROUND3(a, b, c, d,in[ 0], 3);
+ ROUND3(d, a, b, c, in[8], 9);
+ ROUND3(c, d, a, b, in[4], 11);
+ ROUND3(b, c, d, a, in[12], 15);
+ ROUND3(a, b, c, d, in[2], 3);
+ ROUND3(d, a, b, c, in[10], 9);
+ ROUND3(c, d, a, b, in[6], 11);
+ ROUND3(b, c, d, a, in[14], 15);
+ ROUND3(a, b, c, d, in[1], 3);
+ ROUND3(d, a, b, c, in[9], 9);
+ ROUND3(c, d, a, b, in[5], 11);
+ ROUND3(b, c, d, a, in[13], 15);
+ ROUND3(a, b, c, d, in[3], 3);
+ ROUND3(d, a, b, c, in[11], 9);
+ ROUND3(c, d, a, b, in[7], 11);
+ ROUND3(b, c, d, a, in[15], 15);
+
+ hash[0] += a;
+ hash[1] += b;
+ hash[2] += c;
+ hash[3] += d;
+}
+
+static inline void md4_transform_helper(struct md4_ctx *ctx)
+{
+ le32_to_cpu_array(ctx->block, sizeof(ctx->block) / sizeof(uint32_t));
+ md4_transform(ctx->hash, ctx->block);
+}
+
+static void md4_init(struct md4_ctx *mctx)
+{
+ mctx->hash[0] = 0x67452301;
+ mctx->hash[1] = 0xefcdab89;
+ mctx->hash[2] = 0x98badcfe;
+ mctx->hash[3] = 0x10325476;
+ mctx->byte_count = 0;
+}
+
+static void md4_update(struct md4_ctx *mctx,
+ const unsigned char *data, unsigned int len)
+{
+ const uint32_t avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f);
+
+ mctx->byte_count += len;
+
+ if (avail > len) {
+ memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
+ data, len);
+ return;
+ }
+
+ memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
+ data, avail);
+
+ md4_transform_helper(mctx);
+ data += avail;
+ len -= avail;
+
+ while (len >= sizeof(mctx->block)) {
+ memcpy(mctx->block, data, sizeof(mctx->block));
+ md4_transform_helper(mctx);
+ data += sizeof(mctx->block);
+ len -= sizeof(mctx->block);
+ }
+
+ memcpy(mctx->block, data, len);
+}
+
+static void md4_final_ascii(struct md4_ctx *mctx, char *out, unsigned int len)
+{
+ const unsigned int offset = mctx->byte_count & 0x3f;
+ char *p = (char *)mctx->block + offset;
+ int padding = 56 - (offset + 1);
+
+ *p++ = 0x80;
+ if (padding < 0) {
+ memset(p, 0x00, padding + sizeof (uint64_t));
+ md4_transform_helper(mctx);
+ p = (char *)mctx->block;
+ padding = 56;
+ }
+
+ memset(p, 0, padding);
+ mctx->block[14] = mctx->byte_count << 3;
+ mctx->block[15] = mctx->byte_count >> 29;
+ le32_to_cpu_array(mctx->block, (sizeof(mctx->block) -
+ sizeof(uint64_t)) / sizeof(uint32_t));
+ md4_transform(mctx->hash, mctx->block);
+ cpu_to_le32_array(mctx->hash, sizeof(mctx->hash) / sizeof(uint32_t));
+
+ snprintf(out, len, "%08X%08X%08X%08X",
+ mctx->hash[0], mctx->hash[1], mctx->hash[2], mctx->hash[3]);
+}
+
+static inline void add_char(unsigned char c, struct md4_ctx *md)
+{
+ md4_update(md, &c, 1);
+}
+
+static int parse_string(const char *file, unsigned long len,
+ struct md4_ctx *md)
+{
+ unsigned long i;
+
+ add_char(file[0], md);
+ for (i = 1; i < len; i++) {
+ add_char(file[i], md);
+ if (file[i] == '"' && file[i-1] != '\\')
+ break;
+ }
+ return i;
+}
+
+static int parse_comment(const char *file, unsigned long len)
+{
+ unsigned long i;
+
+ for (i = 2; i < len; i++) {
+ if (file[i-1] == '*' && file[i] == '/')
+ break;
+ }
+ return i;
+}
+
+/* FIXME: Handle .s files differently (eg. # starts comments) --RR */
+static int parse_file(const char *fname, struct md4_ctx *md)
+{
+ char *file;
+ unsigned long i, len;
+
+ file = grab_file(fname, &len);
+ if (!file)
+ return 0;
+
+ for (i = 0; i < len; i++) {
+ /* Collapse and ignore \ and CR. */
+ if (file[i] == '\\' && (i+1 < len) && file[i+1] == '\n') {
+ i++;
+ continue;
+ }
+
+ /* Ignore whitespace */
+ if (isspace(file[i]))
+ continue;
+
+ /* Handle strings as whole units */
+ if (file[i] == '"') {
+ i += parse_string(file+i, len - i, md);
+ continue;
+ }
+
+ /* Comments: ignore */
+ if (file[i] == '/' && file[i+1] == '*') {
+ i += parse_comment(file+i, len - i);
+ continue;
+ }
+
+ add_char(file[i], md);
+ }
+ release_file(file, len);
+ return 1;
+}
+
+/* We have dir/file.o. Open dir/.file.o.cmd, look for deps_ line to
+ * figure out source file. */
+static int parse_source_files(const char *objfile, struct md4_ctx *md)
+{
+ char *cmd, *file, *line, *dir;
+ const char *base;
+ unsigned long flen, pos = 0;
+ int dirlen, ret = 0, check_files = 0;
+
+ cmd = NOFAIL(malloc(strlen(objfile) + sizeof("..cmd")));
+
+ base = strrchr(objfile, '/');
+ if (base) {
+ base++;
+ dirlen = base - objfile;
+ sprintf(cmd, "%.*s.%s.cmd", dirlen, objfile, base);
+ } else {
+ dirlen = 0;
+ sprintf(cmd, ".%s.cmd", objfile);
+ }
+ dir = NOFAIL(malloc(dirlen + 1));
+ strncpy(dir, objfile, dirlen);
+ dir[dirlen] = '\0';
+
+ file = grab_file(cmd, &flen);
+ if (!file) {
+ fprintf(stderr, "Warning: could not find %s for %s\n",
+ cmd, objfile);
+ goto out;
+ }
+
+ /* There will be a line like so:
+ deps_drivers/net/dummy.o := \
+ drivers/net/dummy.c \
+ $(wildcard include/config/net/fastroute.h) \
+ include/linux/config.h \
+ $(wildcard include/config/h.h) \
+ include/linux/module.h \
+
+ Sum all files in the same dir or subdirs.
+ */
+ while ((line = get_next_line(&pos, file, flen)) != NULL) {
+ char* p = line;
+ if (strncmp(line, "deps_", sizeof("deps_")-1) == 0) {
+ check_files = 1;
+ continue;
+ }
+ if (!check_files)
+ continue;
+
+ /* Continue until line does not end with '\' */
+ if ( *(p + strlen(p)-1) != '\\')
+ break;
+ /* Terminate line at first space, to get rid of final ' \' */
+ while (*p) {
+ if (isspace(*p)) {
+ *p = '\0';
+ break;
+ }
+ p++;
+ }
+
+ /* Check if this file is in same dir as objfile */
+ if ((strstr(line, dir)+strlen(dir)-1) == strrchr(line, '/')) {
+ if (!parse_file(line, md)) {
+ fprintf(stderr,
+ "Warning: could not open %s: %s\n",
+ line, strerror(errno));
+ goto out_file;
+ }
+
+ }
+
+ }
+
+ /* Everyone parsed OK */
+ ret = 1;
+out_file:
+ release_file(file, flen);
+out:
+ free(dir);
+ free(cmd);
+ return ret;
+}
+
+static int get_version(const char *modname, char sum[])
+{
+ void *file;
+ unsigned long len;
+ int ret = 0;
+ struct md4_ctx md;
+ char *sources, *end, *fname;
+ const char *basename;
+ char filelist[sizeof(".tmp_versions/%s.mod") + strlen(modname)];
+
+ /* Source files for module are in .tmp_versions/modname.mod,
+ after the first line. */
+ if (strrchr(modname, '/'))
+ basename = strrchr(modname, '/') + 1;
+ else
+ basename = modname;
+ sprintf(filelist, ".tmp_versions/%s", basename);
+ /* Truncate .o, add .mod */
+ strcpy(filelist + strlen(filelist)-2, ".mod");
+
+ file = grab_file(filelist, &len);
+ if (!file) {
+ fprintf(stderr, "Warning: could not find versions for %s\n",
+ filelist);
+ return 0;
+ }
+
+ sources = strchr(file, '\n');
+ if (!sources) {
+ fprintf(stderr, "Warning: malformed versions file for %s\n",
+ modname);
+ goto release;
+ }
+
+ sources++;
+ end = strchr(sources, '\n');
+ if (!end) {
+ fprintf(stderr, "Warning: bad ending versions file for %s\n",
+ modname);
+ goto release;
+ }
+ *end = '\0';
+
+ md4_init(&md);
+ for (fname = strtok(sources, " "); fname; fname = strtok(NULL, " ")) {
+ if (!parse_source_files(fname, &md))
+ goto release;
+ }
+
+ /* sum is of form \0<padding>. */
+ md4_final_ascii(&md, sum, 1 + strlen(sum+1));
+ ret = 1;
+release:
+ release_file(file, len);
+ return ret;
+}
+
+static void write_version(const char *filename, const char *sum,
+ unsigned long offset)
+{
+ int fd;
+
+ fd = open(filename, O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "Warning: changing sum in %s failed: %s\n",
+ filename, strerror(errno));
+ return;
+ }
+
+ if (lseek(fd, offset, SEEK_SET) == (off_t)-1) {
+ fprintf(stderr, "Warning: changing sum in %s:%lu failed: %s\n",
+ filename, offset, strerror(errno));
+ goto out;
+ }
+
+ if (write(fd, sum, strlen(sum)+1) != strlen(sum)+1) {
+ fprintf(stderr, "Warning: writing sum in %s failed: %s\n",
+ filename, strerror(errno));
+ goto out;
+ }
+out:
+ close(fd);
+}
+
+void strip_rcs_crap(char *version)
+{
+ unsigned int len, full_len;
+
+ if (strncmp(version, "$Revision", strlen("$Revision")) != 0)
+ return;
+
+ /* Space for version string follows. */
+ full_len = strlen(version) + strlen(version + strlen(version) + 1) + 2;
+
+ /* Move string to start with version number: prefix will be
+ * $Revision$ or $Revision: */
+ len = strlen("$Revision");
+ if (version[len] == ':' || version[len] == '$')
+ len++;
+ while (isspace(version[len]))
+ len++;
+ memmove(version, version+len, full_len-len);
+ full_len -= len;
+
+ /* Preserve up to next whitespace. */
+ len = 0;
+ while (version[len] && !isspace(version[len]))
+ len++;
+ memmove(version + len, version + strlen(version),
+ full_len - strlen(version));
+}
+
+/* If the modinfo contains a "version" value, then set this. */
+void maybe_frob_version(const char *modfilename,
+ void *modinfo,
+ unsigned long modinfo_len,
+ unsigned long modinfo_offset)
+{
+ char *version, *csum;
+
+ version = get_modinfo(modinfo, modinfo_len, "version");
+ if (!version)
+ return;
+
+ /* RCS $Revision gets stripped out. */
+ strip_rcs_crap(version);
+
+ /* Check against double sumversion */
+ if (strchr(version, ' '))
+ return;
+
+ /* Version contains embedded NUL: second half has space for checksum */
+ csum = version + strlen(version);
+ *(csum++) = ' ';
+ if (get_version(modfilename, csum))
+ write_version(modfilename, version,
+ modinfo_offset + (version - (char *)modinfo));
+}
MKSPEC := $(srctree)/scripts/package/mkspec
PREV := set -e; cd ..;
+# rpm-pkg
.PHONY: rpm-pkg rpm
-$(objtree)/kernel.spec: $(MKSPEC)
+$(objtree)/kernel.spec: $(MKSPEC) $(srctree)/Makefile
$(CONFIG_SHELL) $(MKSPEC) > $@
rpm-pkg rpm: $(objtree)/kernel.spec
clean-rule += rm -f $(objtree)/kernel.spec
+# binrpm-pkg
+.PHONY: binrpm-pkg
+$(objtree)/binkernel.spec: $(MKSPEC) $(srctree)/Makefile
+ $(CONFIG_SHELL) $(MKSPEC) prebuilt > $@
+
+binrpm-pkg: $(objtree)/binkernel.spec
+ $(MAKE)
+ set -e; \
+ $(CONFIG_SHELL) $(srctree)/scripts/mkversion > $(objtree)/.tmp_version
+ set -e; \
+ mv -f $(objtree)/.tmp_version $(objtree)/.version
+
+ $(RPM) --define "_builddir $(srctree)" --target $(UTS_MACHINE) -bb $<
+
+clean-rule += rm -f $(objtree)/binkernel.spec
+
# Deb target
# ---------------------------------------------------------------------------
#
# ---------------------------------------------------------------------------
help:
@echo ' rpm-pkg - Build the kernel as an RPM package'
+ @echo ' binrpm-pkg - Build an rpm package containing the compiled kernel & modules'
@echo ' deb-pkg - Build the kernel as an deb package'
# Patched for non-x86 by Opencon (L) 2002 <opencon@rio.skydome.net>
#
+# how we were called determines which rpms we build and how we build them
+if [ "$1" = "prebuilt" ]; then
+ PREBUILT=true
+else
+ PREBUILT=false
+fi
+
# starting to output the spec
if [ "`grep CONFIG_DRM=y .config | cut -f2 -d\=`" = "y" ]; then
PROVIDES=kernel-drm
echo "Group: System Environment/Kernel"
echo "Vendor: The Linux Community"
echo "URL: http://www.kernel.org"
+
+if ! $PREBUILT; then
echo -n "Source: kernel-$VERSION.$PATCHLEVEL.$SUBLEVEL"
echo "$EXTRAVERSION.tar.gz" | sed -e "s/-//g"
+fi
+
echo "BuildRoot: /var/tmp/%{name}-%{PACKAGE_VERSION}-root"
echo "Provides: $PROVIDES"
echo "%define __spec_install_post /usr/lib/rpm/brp-compress || :"
echo "%description"
echo "The Linux Kernel, the operating system core itself"
echo ""
+
+if ! $PREBUILT; then
echo "%prep"
echo "%setup -q"
echo ""
+fi
+
echo "%build"
+
+if ! $PREBUILT; then
echo "make clean && make"
echo ""
+fi
+
echo "%install"
echo 'mkdir -p $RPM_BUILD_ROOT/boot $RPM_BUILD_ROOT/lib $RPM_BUILD_ROOT/lib/modules'
--- /dev/null
+/*
+ * Netlink message type permission tables, for user generated messages.
+ *
+ * Author: James Morris <jmorris@redhat.com>
+ *
+ * Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.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/types.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/if.h>
+#include <linux/netfilter_ipv4/ip_queue.h>
+#include <linux/tcp_diag.h>
+#include <linux/xfrm.h>
+#include <linux/audit.h>
+
+#include "flask.h"
+#include "av_permissions.h"
+
+struct nlmsg_perm
+{
+ u16 nlmsg_type;
+ u32 perm;
+};
+
+static struct nlmsg_perm nlmsg_route_perms[] =
+{
+ { RTM_NEWLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETLINK, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_SETLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_NEWADDR, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELADDR, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETADDR, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWROUTE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELROUTE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETROUTE, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWRULE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELRULE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETRULE, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWQDISC, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELQDISC, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETQDISC, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWPREFIX, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETPREFIX, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_GETMULTICAST, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_GETANYCAST, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+};
+
+static struct nlmsg_perm nlmsg_firewall_perms[] =
+{
+ { IPQM_MODE, NETLINK_FIREWALL_SOCKET__NLMSG_WRITE },
+ { IPQM_VERDICT, NETLINK_FIREWALL_SOCKET__NLMSG_WRITE },
+};
+
+static struct nlmsg_perm nlmsg_tcpdiag_perms[] =
+{
+ { TCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
+};
+
+static struct nlmsg_perm nlmsg_xfrm_perms[] =
+{
+ { XFRM_MSG_NEWSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_DELSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_GETSA, NETLINK_XFRM_SOCKET__NLMSG_READ },
+ { XFRM_MSG_NEWPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_DELPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_GETPOLICY, NETLINK_XFRM_SOCKET__NLMSG_READ },
+ { XFRM_MSG_ALLOCSPI, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_UPDPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_UPDSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+};
+
+static struct nlmsg_perm nlmsg_audit_perms[] =
+{
+ { AUDIT_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ },
+ { AUDIT_SET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_LIST, NETLINK_AUDIT_SOCKET__NLMSG_READ },
+ { AUDIT_ADD, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_LOGIN, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+};
+
+
+static int nlmsg_perm(u16 nlmsg_type, u32 *perm, struct nlmsg_perm *tab, size_t tabsize)
+{
+ int i, err = -EINVAL;
+
+ for (i = 0; i < tabsize/sizeof(struct nlmsg_perm); i++)
+ if (nlmsg_type == tab[i].nlmsg_type) {
+ *perm = tab[i].perm;
+ err = 0;
+ break;
+ }
+
+ return err;
+}
+
+int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
+{
+ int err = 0;
+
+ switch (sclass) {
+ case SECCLASS_NETLINK_ROUTE_SOCKET:
+ err = nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms,
+ sizeof(nlmsg_route_perms));
+ break;
+
+ case SECCLASS_NETLINK_FIREWALL_SOCKET:
+ case NETLINK_IP6_FW:
+ err = nlmsg_perm(nlmsg_type, perm, nlmsg_firewall_perms,
+ sizeof(nlmsg_firewall_perms));
+ break;
+
+ case SECCLASS_NETLINK_TCPDIAG_SOCKET:
+ err = nlmsg_perm(nlmsg_type, perm, nlmsg_tcpdiag_perms,
+ sizeof(nlmsg_tcpdiag_perms));
+ break;
+
+ case SECCLASS_NETLINK_XFRM_SOCKET:
+ err = nlmsg_perm(nlmsg_type, perm, nlmsg_xfrm_perms,
+ sizeof(nlmsg_xfrm_perms));
+ break;
+
+ case SECCLASS_NETLINK_AUDIT_SOCKET:
+ err = nlmsg_perm(nlmsg_type, perm, nlmsg_audit_perms,
+ sizeof(nlmsg_audit_perms));
+ break;
+
+ /* No messaging from userspace, or class unknown/unhandled */
+ default:
+ err = -ENOENT;
+ break;
+ }
+
+ return err;
+}
endmenu
menu "Open Sound System"
- depends on SOUND!=n
+ depends on SOUND!=n && (BROKEN || !SPARC64)
config SOUND_PRIME
tristate "Open Sound System (DEPRECATED)"
struct snd_info_entry *entry;
snd_info_buffer_t *buf;
size_t size = 0;
+ loff_t pos;
data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO);
snd_assert(data != NULL, return -ENXIO);
+ pos = *offset;
+ if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
+ return -EIO;
+ if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
+ return -EIO;
entry = data->entry;
switch (entry->content) {
case SNDRV_INFO_CONTENT_TEXT:
buf = data->rbuffer;
if (buf == NULL)
return -EIO;
- if (file->f_pos >= (long)buf->size)
+ if (pos >= buf->size)
return 0;
- size = buf->size - file->f_pos;
+ size = buf->size - pos;
size = min(count, size);
- if (copy_to_user(buffer, buf->buffer + file->f_pos, size))
+ if (copy_to_user(buffer, buf->buffer + pos, size))
return -EFAULT;
- file->f_pos += size;
break;
case SNDRV_INFO_CONTENT_DATA:
if (entry->c.ops->read)
- return entry->c.ops->read(entry,
+ size = entry->c.ops->read(entry,
data->file_private_data,
- file, buffer, count);
+ file, buffer, count, pos);
break;
}
+ if ((ssize_t) size > 0)
+ *offset = pos + size;
return size;
}
struct snd_info_entry *entry;
snd_info_buffer_t *buf;
size_t size = 0;
+ loff_t pos;
data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO);
snd_assert(data != NULL, return -ENXIO);
entry = data->entry;
+ pos = *offset;
+ if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
+ return -EIO;
+ if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
+ return -EIO;
switch (entry->content) {
case SNDRV_INFO_CONTENT_TEXT:
buf = data->wbuffer;
if (buf == NULL)
return -EIO;
- if (file->f_pos < 0)
- return -EINVAL;
- if (file->f_pos >= (long)buf->len)
+ if (pos >= buf->len)
return -ENOMEM;
- size = buf->len - file->f_pos;
+ size = buf->len - pos;
size = min(count, size);
- if (copy_from_user(buf->buffer + file->f_pos, buffer, size))
+ if (copy_from_user(buf->buffer + pos, buffer, size))
return -EFAULT;
- if ((long)buf->size < file->f_pos + size)
- buf->size = file->f_pos + size;
- file->f_pos += size;
+ if ((long)buf->size < pos + size)
+ buf->size = pos + size;
break;
case SNDRV_INFO_CONTENT_DATA:
if (entry->c.ops->write)
- return entry->c.ops->write(entry,
+ size = entry->c.ops->write(entry,
data->file_private_data,
- file, buffer, count);
+ file, buffer, count, pos);
break;
}
+ if ((ssize_t) size > 0)
+ *offset = pos + size;
return size;
}
const char *snd_pcm_stream_name(snd_pcm_stream_t stream)
{
- snd_assert(stream <= SNDRV_PCM_STREAM_LAST, return 0);
+ snd_assert(stream <= SNDRV_PCM_STREAM_LAST, return NULL);
return snd_pcm_stream_names[stream];
}
const char *snd_pcm_access_name(snd_pcm_access_t access)
{
- snd_assert(access <= SNDRV_PCM_ACCESS_LAST, return 0);
+ snd_assert(access <= SNDRV_PCM_ACCESS_LAST, return NULL);
return snd_pcm_access_names[access];
}
const char *snd_pcm_format_name(snd_pcm_format_t format)
{
- snd_assert(format <= SNDRV_PCM_FORMAT_LAST, return 0);
+ snd_assert(format <= SNDRV_PCM_FORMAT_LAST, return NULL);
return snd_pcm_format_names[format];
}
const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat)
{
- snd_assert(subformat <= SNDRV_PCM_SUBFORMAT_LAST, return 0);
+ snd_assert(subformat <= SNDRV_PCM_SUBFORMAT_LAST, return NULL);
return snd_pcm_subformat_names[subformat];
}
const char *snd_pcm_tstamp_mode_name(snd_pcm_tstamp_t mode)
{
- snd_assert(mode <= SNDRV_PCM_TSTAMP_LAST, return 0);
+ snd_assert(mode <= SNDRV_PCM_TSTAMP_LAST, return NULL);
return snd_pcm_tstamp_mode_names[mode];
}
const char *snd_pcm_state_name(snd_pcm_state_t state)
{
- snd_assert(state <= SNDRV_PCM_STATE_LAST, return 0);
+ snd_assert(state <= SNDRV_PCM_STATE_LAST, return NULL);
return snd_pcm_state_names[state];
}
}
static long snd_opl4_mem_proc_read(snd_info_entry_t *entry, void *file_private_data,
- struct file *file, char __user *_buf, long count)
+ struct file *file, char __user *_buf,
+ unsigned long count, unsigned long pos)
{
opl4_t *opl4 = snd_magic_cast(opl4_t, entry->private_data, return -ENXIO);
long size;
char* buf;
size = count;
- if (file->f_pos + size > entry->size)
- size = entry->size - file->f_pos;
+ if (pos + size > entry->size)
+ size = entry->size - pos;
if (size > 0) {
buf = vmalloc(size);
if (!buf)
return -ENOMEM;
- snd_opl4_read_memory(opl4, buf, file->f_pos, size);
+ snd_opl4_read_memory(opl4, buf, pos, size);
if (copy_to_user(_buf, buf, size)) {
vfree(buf);
return -EFAULT;
}
vfree(buf);
- file->f_pos += size;
return size;
}
return 0;
}
static long snd_opl4_mem_proc_write(snd_info_entry_t *entry, void *file_private_data,
- struct file *file, const char __user *_buf, long count)
+ struct file *file, const char __user *_buf,
+ unsigned long count, unsigned long pos)
{
opl4_t *opl4 = snd_magic_cast(opl4_t, entry->private_data, return -ENXIO);
long size;
char *buf;
size = count;
- if (file->f_pos + size > entry->size)
- size = entry->size - file->f_pos;
+ if (pos + size > entry->size)
+ size = entry->size - pos;
if (size > 0) {
buf = vmalloc(size);
if (!buf)
vfree(buf);
return -EFAULT;
}
- snd_opl4_write_memory(opl4, buf, file->f_pos, size);
+ snd_opl4_write_memory(opl4, buf, pos, size);
vfree(buf);
- file->f_pos += size;
return size;
}
return 0;
struct vx_rmh rmh;
int data_mode;
- *pipep = 0;
+ *pipep = NULL;
vx_init_rmh(&rmh, CMD_RES_PIPE);
vx_set_pipe_cmd_params(&rmh, capture, audioid, num_audio);
#if 0 // NYI
{
snd_pcm_runtime_t *runtime = subs->runtime;
vx_core_t *chip = snd_pcm_substream_chip(subs);
- vx_pipe_t *pipe = 0;
+ vx_pipe_t *pipe = NULL;
unsigned int audio;
int err;
pipe = snd_magic_cast(vx_pipe_t, subs->runtime->private_data, return -EINVAL);
if (--pipe->references == 0) {
- chip->playback_pipes[pipe->number] = 0;
+ chip->playback_pipes[pipe->number] = NULL;
vx_free_pipe(chip, pipe);
}
if (! subs->runtime->private_data)
return -EINVAL;
pipe = snd_magic_cast(vx_pipe_t, subs->runtime->private_data, return -EINVAL);
- chip->capture_pipes[pipe->number] = 0;
+ chip->capture_pipes[pipe->number] = NULL;
pipe_out_monitoring = pipe->monitoring_pipe;
if (pipe_out_monitoring) {
if (--pipe_out_monitoring->references == 0) {
vx_free_pipe(chip, pipe_out_monitoring);
- chip->playback_pipes[pipe->number] = 0;
- pipe->monitoring_pipe = 0;
+ chip->playback_pipes[pipe->number] = NULL;
+ pipe->monitoring_pipe = NULL;
}
}
chip->pcm[pcm->device] = NULL;
if (chip->playback_pipes) {
kfree(chip->playback_pipes);
- chip->playback_pipes = 0;
+ chip->playback_pipes = NULL;
}
if (chip->capture_pipes) {
kfree(chip->capture_pipes);
- chip->capture_pipes = 0;
+ chip->capture_pipes = NULL;
}
}
* Linux Video interface
*/
-static int snd_tea575x_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
+static int snd_tea575x_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long data)
{
struct video_device *dev = video_devdata(file);
tea575x_t *tea = video_get_drvdata(dev);
+ void __user *arg = (void __user *)data;
switch(cmd) {
case VIDIOCGCAP:
}
}
-static int snd_tea575x_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- return video_usercopy(inode, file, cmd, arg, snd_tea575x_do_ioctl);
-}
-
/*
* initialize all the tea575x chips
*/
else
nblock->prev->next = nblock;
up(&alloc->memory_mutex);
- return 0;
+ return NULL;
}
pblock = pblock->next;
}
} gus_proc_private_t;
static long snd_gf1_mem_proc_dump(snd_info_entry_t *entry, void *file_private_data,
- struct file *file, char __user *buf, long count)
+ struct file *file, char __user *buf,
+ unsigned long count, unsigned long pos)
{
long size;
gus_proc_private_t *priv = snd_magic_cast(gus_proc_private_t, entry->private_data, return -ENXIO);
int err;
size = count;
- if (file->f_pos + size > priv->size)
- size = (long)priv->size - file->f_pos;
+ if (pos + size > priv->size)
+ size = (long)priv->size - pos;
if (size > 0) {
- if ((err = snd_gus_dram_read(gus, buf, file->f_pos, size, priv->rom)) < 0)
+ if ((err = snd_gus_dram_read(gus, buf, pos, size, priv->rom)) < 0)
return err;
- file->f_pos += size;
return size;
}
return 0;
emu8k_pcm_t *rec = subs->runtime->private_data;
if (rec)
kfree(rec);
- subs->runtime->private_data = 0;
+ subs->runtime->private_data = NULL;
return 0;
}
runtime->hw.rate_max = 44100;
runtime->hw.channels_max = 2;
snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
- snd_sb8_hw_constraint_rate_channels, 0,
+ snd_sb8_hw_constraint_rate_channels, NULL,
SNDRV_PCM_HW_PARAM_CHANNELS,
SNDRV_PCM_HW_PARAM_RATE, -1);
snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- snd_sb8_hw_constraint_channels_rate, 0,
+ snd_sb8_hw_constraint_channels_rate, NULL,
SNDRV_PCM_HW_PARAM_RATE, -1);
break;
case SB_HW_201:
size_t count, loff_t * ppos)
{
struct ali_state *state = (struct ali_state *) file->private_data;
- struct ali_card *card = state ? state->card : 0;
+ struct ali_card *card = state ? state->card : NULL;
struct dmabuf *dmabuf = &state->dmabuf;
ssize_t ret;
unsigned long flags;
#ifdef DEBUG2
printk("ali_audio: ali_read called, count = %d\n", count);
#endif
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (dmabuf->mapped)
return -ENXIO;
if (dmabuf->enable & DAC_RUNNING)
const char __user *buffer, size_t count, loff_t * ppos)
{
struct ali_state *state = (struct ali_state *) file->private_data;
- struct ali_card *card = state ? state->card : 0;
+ struct ali_card *card = state ? state->card : NULL;
struct dmabuf *dmabuf = &state->dmabuf;
ssize_t ret;
unsigned long flags;
#ifdef DEBUG2
printk("ali_audio: ali_write called, count = %d\n", count);
#endif
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (dmabuf->mapped)
return -ENXIO;
if (dmabuf->enable & ADC_RUNNING)
state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
outl(0x00000000, card->iobase + ALI_INTERRUPTCR);
outl(0x00000000, card->iobase + ALI_INTERRUPTSR);
- return 0;
+ return nonseekable_open(inode, file);
}
static int ali_release(struct inode *inode, struct file *file)
if (card->ac97_codec[i] != NULL
&& card->ac97_codec[i]->dev_mixer == minor) {
file->private_data = card->ac97_codec[i];
- return 0;
+ return nonseekable_open(inode, file);
}
}
return -ENODEV;
static int au1000_open_mixdev(struct inode *inode, struct file *file)
{
file->private_data = &au1000_state;
- return 0;
+ return nonseekable_open(inode, file);
}
static int au1000_release_mixdev(struct inode *inode, struct file *file)
unsigned long flags;
int cnt, usercnt, avail;
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (db->mapped)
return -ENXIO;
if (!access_ok(VERIFY_WRITE, buffer, count))
dbg("write: count=%d", count);
#endif
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (db->mapped)
return -ENXIO;
if (!access_ok(VERIFY_READ, buffer, count))
s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
up(&s->open_sem);
init_MUTEX(&s->sem);
- return 0;
+ return nonseekable_open(inode, file);
}
static int au1000_release(struct inode *inode, struct file *file)
CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
printk(KERN_INFO "cs4281: cs4281_open_mixdev()- 0\n"));
- return 0;
+ return nonseekable_open(inode, file);
}
printk(KERN_INFO "cs4281: cs4281_read()+ %Zu \n", count));
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (s->dma_adc.mapped)
return -ENXIO;
if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
count));
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (s->dma_dac.mapped)
return -ENXIO;
if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s)))
}
CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2,
printk(KERN_INFO "cs4281: cs4281_open()- 0\n"));
- return 0;
+ return nonseekable_open(inode, file);
}
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
ret = 0;
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
ret = 0;
f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ |
FMODE_MIDI_WRITE);
up(&s->open_sem);
- return 0;
+ return nonseekable_open(inode, file);
}
tmp &= 0xFFFF;
tmp |= card->pctl;
CS_DBGOUT(CS_PARMS, 6, printk(
- "cs46xx: start_dac() poke card=0x%.08x tmp=0x%.08x addr=0x%.08x \n",
- (unsigned)card, (unsigned)tmp,
- (unsigned)card->ba1.idx[(BA1_PCTL >> 16) & 3]+(BA1_PCTL&0xffff) ) );
+ "cs46xx: start_dac() poke card=%p tmp=0x%.08x addr=%p \n",
+ card, (unsigned)tmp,
+ card->ba1.idx[(BA1_PCTL >> 16) & 3]+(BA1_PCTL&0xffff) ) );
cs461x_poke(card, BA1_PCTL, tmp);
}
spin_unlock_irqrestore(&card->lock, flags);
memset(dmabuf->rawbuf,
(dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80,
(unsigned)hwptr);
- memset((void *)((unsigned)dmabuf->rawbuf +
- dmabuf->dmasize + hwptr - diff),
+ memset((char *)dmabuf->rawbuf +
+ dmabuf->dmasize + hwptr - diff,
(dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80,
diff - hwptr);
}
unsigned ptr;
int cnt;
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
ret = 0;
unsigned ptr;
int cnt;
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
ret = 0;
CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: CopySamples()+ ") );
CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO
- " dst=0x%x src=0x%x count=%d fmt=0x%x\n",
- (unsigned)dst,(unsigned)src,(unsigned)count,(unsigned)fmt) );
+ " dst=%p src=%p count=%d fmt=0x%x\n",
+ dst,src,count,fmt) );
/*
* See if the data should be output as 8-bit unsigned stereo.
return -ENODEV;
dmabuf = &state->dmabuf;
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (dmabuf->mapped)
return -ENXIO;
if (!access_ok(VERIFY_WRITE, buffer, count))
dmabuf->dmasize,dmabuf->count,buffer,ret) );
if (cs_copy_to_user(state, buffer,
- (void *)((unsigned)dmabuf->rawbuf + swptr), cnt, &copied))
+ (char *)dmabuf->rawbuf + swptr, cnt, &copied))
{
if (!ret) ret = -EFAULT;
goto out;
return -EFAULT;
dmabuf = &state->dmabuf;
- if (ppos != &file->f_pos)
- return -ESPIPE;
-
down(&state->sem);
if (dmabuf->mapped)
{
int ret = 0;
unsigned long size;
- CS_DBGOUT(CS_FUNCTION | CS_PARMS, 2, printk("cs46xx: cs_mmap()+ file=0x%x %s %s\n",
- (unsigned)file, vma->vm_flags & VM_WRITE ? "VM_WRITE" : "",
+ CS_DBGOUT(CS_FUNCTION | CS_PARMS, 2, printk("cs46xx: cs_mmap()+ file=%p %s %s\n",
+ file, vma->vm_flags & VM_WRITE ? "VM_WRITE" : "",
vma->vm_flags & VM_READ ? "VM_READ" : "") );
if (vma->vm_flags & VM_WRITE) {
* use the DAC only.
*/
state = card->states[1];
- if(!(unsigned)state)
- {
+ if (!state) {
ret = -EINVAL;
goto out;
}
{
struct cs_card *card = (struct cs_card *)file->private_data;
struct cs_state *state;
- struct dmabuf *dmabuf=0;
+ struct dmabuf *dmabuf=NULL;
unsigned long flags;
audio_buf_info abinfo;
count_info cinfo;
int ret=0;
unsigned int tmp;
- CS_DBGOUT(CS_OPEN | CS_FUNCTION, 2, printk("cs46xx: cs_open()+ file=0x%x %s %s\n",
- (unsigned)file, file->f_mode & FMODE_WRITE ? "FMODE_WRITE" : "",
+ CS_DBGOUT(CS_OPEN | CS_FUNCTION, 2, printk("cs46xx: cs_open()+ file=%p %s %s\n",
+ file, file->f_mode & FMODE_WRITE ? "FMODE_WRITE" : "",
file->f_mode & FMODE_READ ? "FMODE_READ" : "") );
list_for_each(entry, &cs46xx_devs)
return ret;
}
CS_DBGOUT(CS_OPEN | CS_FUNCTION, 2, printk("cs46xx: cs_open()- 0\n") );
- return 0;
+ return nonseekable_open(inode, file);
}
static int cs_release(struct inode *inode, struct file *file)
struct dmabuf *dmabuf;
struct cs_state *state;
unsigned int tmp;
- CS_DBGOUT(CS_RELEASE | CS_FUNCTION, 2, printk("cs46xx: cs_release()+ file=0x%x %s %s\n",
- (unsigned)file, file->f_mode & FMODE_WRITE ? "FMODE_WRITE" : "",
+ CS_DBGOUT(CS_RELEASE | CS_FUNCTION, 2, printk("cs46xx: cs_release()+ file=%p %s %s\n",
+ file, file->f_mode & FMODE_WRITE ? "FMODE_WRITE" : "",
file->f_mode & FMODE_READ ? "FMODE_READ" : "") );
if (!(file->f_mode & (FMODE_WRITE | FMODE_READ)))
{
unsigned int tmp;
CS_DBGOUT(CS_PM | CS_FUNCTION, 4,
- printk("cs46xx: cs46xx_suspend()+ flags=0x%x s=0x%x\n",
- (unsigned)card->pm.flags,(unsigned)card));
+ printk("cs46xx: cs46xx_suspend()+ flags=0x%x s=%p\n",
+ (unsigned)card->pm.flags,card));
/*
* check the current state, only suspend if IDLE
*/
CS_INC_USE_COUNT(&card->mixer_use_cnt);
CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
printk(KERN_INFO "cs46xx: cs_open_mixdev()- 0\n"));
- return 0;
+ return nonseekable_open(inode, file);
}
static int cs_release_mixdev(struct inode *inode, struct file *file)
CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO
"cs46xx: cs_ac97_init()- codec number %d not found\n",
num_ac97) );
- card->ac97_codec[num_ac97] = 0;
+ card->ac97_codec[num_ac97] = NULL;
break;
}
CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO
card->ac97_codec[num_ac97] = codec;
CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO
- "cs46xx: cs_ac97_init() ac97_codec[%d] set to 0x%x\n",
+ "cs46xx: cs_ac97_init() ac97_codec[%d] set to %p\n",
(unsigned int)num_ac97,
- (unsigned int)codec));
+ codec));
/* if there is no secondary codec at all, don't probe any more */
if (!ready_2nd)
{
card->ba1.name.reg = ioremap_nocache(card->ba1_addr + BA1_SP_REG, CS461X_BA1_REG_SIZE);
CS_DBGOUT(CS_INIT, 4, printk(KERN_INFO
- "cs46xx: card=0x%x card->ba0=0x%.08x\n",(unsigned)card,(unsigned)card->ba0) );
+ "cs46xx: card=%p card->ba0=%p\n",card,card->ba0) );
CS_DBGOUT(CS_INIT, 4, printk(KERN_INFO
- "cs46xx: card->ba1=0x%.08x 0x%.08x 0x%.08x 0x%.08x\n",
- (unsigned)card->ba1.name.data0,
- (unsigned)card->ba1.name.data1,
- (unsigned)card->ba1.name.pmem,
- (unsigned)card->ba1.name.reg) );
+ "cs46xx: card->ba1=%p %p %p %p\n",
+ card->ba1.name.data0,
+ card->ba1.name.data1,
+ card->ba1.name.pmem,
+ card->ba1.name.reg) );
if(card->ba0 == 0 || card->ba1.name.data0 == 0 ||
card->ba1.name.data1 == 0 || card->ba1.name.pmem == 0 ||
if (pmdev)
{
CS_DBGOUT(CS_INIT | CS_PM, 4, printk(KERN_INFO
- "cs46xx: probe() pm_register() succeeded (0x%x).\n",
- (unsigned)pmdev));
+ "cs46xx: probe() pm_register() succeeded (%p).\n",
+ pmdev));
pmdev->data = card;
}
else
{
CS_DBGOUT(CS_INIT | CS_PM | CS_ERROR, 2, printk(KERN_INFO
- "cs46xx: probe() pm_register() failed (0x%x).\n",
- (unsigned)pmdev));
+ "cs46xx: probe() pm_register() failed (%p).\n",
+ pmdev));
card->pm.flags |= CS46XX_PM_NOT_REGISTERED;
}
- CS_DBGOUT(CS_PM, 9, printk(KERN_INFO "cs46xx: pm.flags=0x%x card=0x%x\n",
- (unsigned)card->pm.flags,(unsigned)card));
+ CS_DBGOUT(CS_PM, 9, printk(KERN_INFO "cs46xx: pm.flags=0x%x card=%p\n",
+ (unsigned)card->pm.flags,card));
CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO
"cs46xx: probe()- device allocated successfully\n"));
struct cs_card *card;
CS_DBGOUT(CS_PM, 2, printk(KERN_INFO
- "cs46xx: cs46xx_pm_callback dev=0x%x rqst=0x%x card=%d\n",
- (unsigned)dev,(unsigned)rqst,(unsigned)data));
+ "cs46xx: cs46xx_pm_callback dev=%p rqst=0x%x card=%p\n",
+ dev,(unsigned)rqst,data));
card = (struct cs_card *) dev->data;
if (card) {
switch(rqst) {
*/
static int cs46xx_suspend_tbl(struct pci_dev *pcidev, u32 state);
static int cs46xx_resume_tbl(struct pci_dev *pcidev);
-#define cs_pm_register(a, b, c) 0
+#define cs_pm_register(a, b, c) NULL
#define cs_pm_unregister_all(a)
#define CS46XX_SUSPEND_TBL cs46xx_suspend_tbl
#define CS46XX_RESUME_TBL cs46xx_resume_tbl
/*
- * linux/drivers/sound/dmasound/dmasound_core.c
+ * linux/sound/oss/dmasound/dmasound_core.c
*
*
* OSS/Free compatible Atari TT/Falcon and Amiga DMA sound driver for
return stereo;
}
-static ssize_t sound_copy_translate(TRANS *trans, const u_char *userPtr,
+static ssize_t sound_copy_translate(TRANS *trans, const u_char __user *userPtr,
size_t userCount, u_char frame[],
ssize_t *frameUsed, ssize_t frameLeft)
{
- ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
+ ssize_t (*ct_func)(const u_char __user *, size_t, u_char *, ssize_t *, ssize_t);
switch (dmasound.soft.format) {
case AFMT_MU_LAW:
strlcpy(info.id, dmasound.mach.name2, sizeof(info.id));
strlcpy(info.name, dmasound.mach.name2, sizeof(info.name));
info.modify_counter = mixer.modify_counter;
- if (copy_to_user((int *)arg, &info, sizeof(info)))
+ if (copy_to_user((void __user *)arg, &info, sizeof(info)))
return -EFAULT;
return 0;
}
while (i--)
dmasound.mach.dma_free(sq->buffers[i], size);
kfree(sq->buffers);
- sq->buffers = 0;
+ sq->buffers = NULL;
return -ENOMEM;
}
}
static int sq_setup(struct sound_queue *sq)
{
- int (*setup_func)(void) = 0;
+ int (*setup_func)(void) = NULL;
int hard_frame ;
if (sq->locked) { /* are we already set? - and not changeable */
dmasound.mach.play();
}
-static ssize_t sq_write(struct file *file, const char *src, size_t uLeft,
+static ssize_t sq_write(struct file *file, const char __user *src, size_t uLeft,
loff_t *ppos)
{
ssize_t uWritten = 0;
* it and restart the DMA.
*/
-static ssize_t sq_read(struct file *file, char *dst, size_t uLeft,
+static ssize_t sq_read(struct file *file, char __user *dst, size_t uLeft,
loff_t *ppos)
{
info.fragstotal = write_sq.max_active;
info.fragsize = write_sq.user_frag_size;
info.bytes = info.fragments * info.fragsize;
- if (copy_to_user((void *)arg, &info, sizeof(info)))
+ if (copy_to_user((void __user *)arg, &info, sizeof(info)))
return -EFAULT;
return 0;
} else
return 0;
}
-static ssize_t state_read(struct file *file, char *buf, size_t count,
+static ssize_t state_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
int n = state.len - state.ptr;
/*
- * linux/drivers/sound/dmasound/dmasound_paula.c
+ * linux/sound/oss/dmasound/dmasound_paula.c
*
* Amiga `Paula' DMA Sound Driver
*
- * See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits
+ * See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
* prior to 28/01/2001
*
* 28/01/2001 [0.1] Iain Sandoe
/*
- * linux/drivers/sound/dmasound/dmasound_q40.c
+ * linux/sound/oss/dmasound/dmasound_q40.c
*
* Q40 DMA Sound Driver
*
- * See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits
+ * See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
* prior to 28/01/2001
*
* 28/01/2001 [0.1] Iain Sandoe
{
int rc;
struct tas_biquad_ctrl_t biquad;
+ void __user *argp = (void __user *)arg;
- if (copy_from_user((void *)&biquad, (const void *)arg, sizeof(struct tas_biquad_ctrl_t))) {
+ if (copy_from_user(&biquad, argp, sizeof(struct tas_biquad_ctrl_t))) {
return -EFAULT;
}
rc=tas3001c_read_biquad(self, biquad.channel, biquad.filter, &biquad.data);
if (rc != 0) return rc;
- if (copy_to_user((void *)arg, (const void *)&biquad, sizeof(struct tas_biquad_ctrl_t))) {
+ if (copy_to_user(argp, &biquad, sizeof(struct tas_biquad_ctrl_t))) {
return -EFAULT;
}
int i,j;
char sync_required[2][6];
struct tas_biquad_ctrl_t biquad;
+ struct tas_biquad_ctrl_list_t __user *argp = (void __user *)arg;
memset(sync_required,0,sizeof(sync_required));
- if (copy_from_user((void *)&filter_count,
- (const void *)arg + offsetof(struct tas_biquad_ctrl_list_t,filter_count),
- sizeof(int))) {
+ if (copy_from_user(&filter_count, &argp->filter_count, sizeof(int)))
return -EFAULT;
- }
- if (copy_from_user((void *)&flags,
- (const void *)arg + offsetof(struct tas_biquad_ctrl_list_t,flags),
- sizeof(int))) {
+ if (copy_from_user(&flags, &argp->flags, sizeof(int)))
return -EFAULT;
- }
if (cmd & SIOC_IN) {
}
for (i=0; i < filter_count; i++) {
- if (copy_from_user((void *)&biquad,
- (const void *)arg + offsetof(struct tas_biquad_ctrl_list_t, biquads[i]),
+ if (copy_from_user(&biquad, &argp->biquads[i],
sizeof(struct tas_biquad_ctrl_t))) {
return -EFAULT;
}
rc=tas3001c_read_biquad(self, biquad.channel, biquad.filter, &biquad.data);
if (rc != 0) return rc;
- if (copy_to_user((void *)arg + offsetof(struct tas_biquad_ctrl_list_t, biquads[i]),
- (const void *)&biquad,
+ if (copy_to_user(&argp->biquads[i], &biquad,
sizeof(struct tas_biquad_ctrl_t))) {
return -EFAULT;
}
{
int rc;
struct tas_drce_ctrl_t drce_ctrl;
+ void __user *argp = (void __user *)arg;
- if (copy_from_user((void *)&drce_ctrl,
- (const void *)arg,
- sizeof(struct tas_drce_ctrl_t))) {
+ if (copy_from_user(&drce_ctrl, argp, sizeof(struct tas_drce_ctrl_t)))
return -EFAULT;
- }
#ifdef DEBUG_DRCE
printk("DRCE IOCTL: input [ FLAGS:%x ENABLE:%x THRESH:%x\n",
if (drce_ctrl.flags & TAS_DRCE_THRESHOLD)
drce_ctrl.data.threshold = self->drce_state.threshold;
- if (copy_to_user((void *)arg,
- (const void *)&drce_ctrl,
+ if (copy_to_user(argp, &drce_ctrl,
sizeof(struct tas_drce_ctrl_t))) {
return -EFAULT;
}
u_int cmd,
u_long arg)
{
+ uint __user *argp = (void __user *)arg;
switch (cmd) {
case TAS_READ_EQ:
case TAS_WRITE_EQ:
return tas3001c_eq_list_rw(self, cmd, arg);
case TAS_READ_EQ_FILTER_COUNT:
- put_user(TAS3001C_BIQUAD_FILTER_COUNT, (uint *)(arg));
+ put_user(TAS3001C_BIQUAD_FILTER_COUNT, argp);
return 0;
case TAS_READ_EQ_CHANNEL_COUNT:
- put_user(TAS3001C_BIQUAD_CHANNEL_COUNT, (uint *)(arg));
+ put_user(TAS3001C_BIQUAD_CHANNEL_COUNT, argp);
return 0;
case TAS_READ_DRCE:
return tas3001c_drce_rw(self, cmd, arg);
case TAS_READ_DRCE_CAPS:
- put_user(TAS_DRCE_ENABLE | TAS_DRCE_THRESHOLD, (uint *)(arg));
+ put_user(TAS_DRCE_ENABLE | TAS_DRCE_THRESHOLD, argp);
return 0;
case TAS_READ_DRCE_MIN:
case TAS_READ_DRCE_MAX: {
struct tas_drce_ctrl_t drce_ctrl;
- if (copy_from_user((void *)&drce_ctrl,
- (const void *)arg,
+ if (copy_from_user(&drce_ctrl, argp,
sizeof(struct tas_drce_ctrl_t))) {
return -EFAULT;
}
}
}
- if (copy_to_user((void *)arg,
- (const void *)&drce_ctrl,
+ if (copy_to_user(argp, &drce_ctrl,
sizeof(struct tas_drce_ctrl_t))) {
return -EFAULT;
}
static struct tas_drce_t eqp_0e_2_1_drce = {
.enable = 1,
- .above { .val = 3.0 * (1<<8), .expand = 0 },
- .below { .val = 1.0 * (1<<8), .expand = 0 },
- .threshold -15.33 * (1<<8),
- .energy 2.4 * (1<<12),
- .attack 0.013 * (1<<12),
- .decay 0.212 * (1<<12),
+ .above = { .val = 3.0 * (1<<8), .expand = 0 },
+ .below = { .val = 1.0 * (1<<8), .expand = 0 },
+ .threshold = -15.33 * (1<<8),
+ .energy = 2.4 * (1<<12),
+ .attack = 0.013 * (1<<12),
+ .decay = 0.212 * (1<<12),
};
static struct tas_biquad_ctrl_t eqp_0e_2_1_biquads[]={
u_int cmd,
u_long arg)
{
+ void __user *argp = (void __user *)arg;
int rc;
struct tas_biquad_ctrl_t biquad;
- if (copy_from_user((void *)&biquad, (const void *)arg, sizeof(struct tas_biquad_ctrl_t))) {
+ if (copy_from_user((void *)&biquad, argp, sizeof(struct tas_biquad_ctrl_t))) {
return -EFAULT;
}
rc=tas3004_read_biquad(self, biquad.channel, biquad.filter, &biquad.data);
if (rc != 0) return rc;
- if (copy_to_user((void *)arg, (const void *)&biquad, sizeof(struct tas_biquad_ctrl_t))) {
+ if (copy_to_user(argp, &biquad, sizeof(struct tas_biquad_ctrl_t))) {
return -EFAULT;
}
int i,j;
char sync_required[TAS3004_BIQUAD_CHANNEL_COUNT][TAS3004_BIQUAD_FILTER_COUNT];
struct tas_biquad_ctrl_t biquad;
+ struct tas_biquad_ctrl_list_t __user *argp = (void __user *)arg;
memset(sync_required,0,sizeof(sync_required));
- if (copy_from_user((void *)&filter_count,
- (const void *)arg + offsetof(struct tas_biquad_ctrl_list_t,filter_count),
- sizeof(int))) {
+ if (copy_from_user(&filter_count, &argp->filter_count, sizeof(int)))
return -EFAULT;
- }
- if (copy_from_user((void *)&flags,
- (const void *)arg + offsetof(struct tas_biquad_ctrl_list_t,flags),
- sizeof(int))) {
+ if (copy_from_user(&flags, &argp->flags, sizeof(int)))
return -EFAULT;
- }
if (cmd & SIOC_IN) {
}
for (i=0; i < filter_count; i++) {
- if (copy_from_user((void *)&biquad,
- (const void *)arg + offsetof(struct tas_biquad_ctrl_list_t, biquads[i]),
+ if (copy_from_user(&biquad, &argp->biquads[i],
sizeof(struct tas_biquad_ctrl_t))) {
return -EFAULT;
}
rc=tas3004_read_biquad(self, biquad.channel, biquad.filter, &biquad.data);
if (rc != 0) return rc;
- if (copy_to_user((void *)arg + offsetof(struct tas_biquad_ctrl_list_t, biquads[i]),
- (const void *)&biquad,
+ if (copy_to_user(&argp->biquads[i], &biquad,
sizeof(struct tas_biquad_ctrl_t))) {
return -EFAULT;
}
{
int rc;
struct tas_drce_ctrl_t drce_ctrl;
+ void __user *argp = (void __user *)arg;
- if (copy_from_user((void *)&drce_ctrl,
- (const void *)arg,
- sizeof(struct tas_drce_ctrl_t))) {
+ if (copy_from_user(&drce_ctrl, argp, sizeof(struct tas_drce_ctrl_t)))
return -EFAULT;
- }
#ifdef DEBUG_DRCE
printk("DRCE: input [ FLAGS:%x ENABLE:%x ABOVE:%x/%x BELOW:%x/%x THRESH:%x ENERGY:%x ATTACK:%x DECAY:%x\n",
if (drce_ctrl.flags & TAS_DRCE_DECAY)
drce_ctrl.data.decay = self->drce_state.decay;
- if (copy_to_user((void *)arg,
- (const void *)&drce_ctrl,
+ if (copy_to_user(argp, &drce_ctrl,
sizeof(struct tas_drce_ctrl_t))) {
return -EFAULT;
}
u_int cmd,
u_long arg)
{
+ uint __user *argp = (void __user *)arg;
switch (cmd) {
case TAS_READ_EQ:
case TAS_WRITE_EQ:
return tas3004_eq_list_rw(self, cmd, arg);
case TAS_READ_EQ_FILTER_COUNT:
- put_user(TAS3004_BIQUAD_FILTER_COUNT, (uint *)(arg));
+ put_user(TAS3004_BIQUAD_FILTER_COUNT, argp);
return 0;
case TAS_READ_EQ_CHANNEL_COUNT:
- put_user(TAS3004_BIQUAD_CHANNEL_COUNT, (uint *)(arg));
+ put_user(TAS3004_BIQUAD_CHANNEL_COUNT, argp);
return 0;
case TAS_READ_DRCE:
TAS_DRCE_ENERGY |
TAS_DRCE_ATTACK |
TAS_DRCE_DECAY,
- (uint *)(arg));
+ argp);
return 0;
case TAS_READ_DRCE_MIN:
struct tas_drce_ctrl_t drce_ctrl;
const struct tas_drce_t *drce_copy;
- if (copy_from_user((void *)&drce_ctrl,
- (const void *)arg,
+ if (copy_from_user(&drce_ctrl, argp,
sizeof(struct tas_drce_ctrl_t))) {
return -EFAULT;
}
drce_ctrl.data.decay=drce_copy->decay;
}
- if (copy_to_user((void *)arg,
- (const void *)&drce_ctrl,
+ if (copy_to_user(argp, &drce_ctrl,
sizeof(struct tas_drce_ctrl_t))) {
return -EFAULT;
}
/*
- * linux/drivers/sound/dmasound/trans_16.c
+ * linux/sound/oss/dmasound/trans_16.c
*
* 16 bit translation routines. Only used by Power mac at present.
*
- * See linux/drivers/sound/dmasound/dmasound_core.c for copyright and
+ * See linux/sound/oss/dmasound/dmasound_core.c for copyright and
* history prior to 08/02/2001.
*
* 08/02/2001 Iain Sandoe
static short dmasound_alaw2dma16[] ;
static short dmasound_ulaw2dma16[] ;
-static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ct_law(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
-static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ct_s8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
-static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ct_u8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
-static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ct_s16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
-static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ct_u16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
-static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ctx_law(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
-static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ctx_s8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
-static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ctx_u8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
-static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ctx_s16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
-static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ctx_u16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
-static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ct_s16_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
-static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ct_u16_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static int expand_data; /* Data for expanding */
-static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ct_law(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
}
-static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ct_s8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
}
-static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ct_u8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
}
-static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ct_s16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
userCount >>= (stereo? 2: 1);
used = count = min_t(unsigned long, userCount, frameLeft);
if (!stereo) {
- short *up = (short *) userPtr;
+ short __user *up = (short __user *) userPtr;
while (count > 0) {
short data;
if (get_user(data, up++))
return stereo? used * 4: used * 2;
}
-static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ct_u16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
int stereo = dmasound.soft.stereo;
short *fp = (short *) &frame[*frameUsed];
- short *up = (short *) userPtr;
+ short __user *up = (short __user *) userPtr;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
}
-static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ctx_law(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
return stereo? utotal * 2: utotal;
}
-static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ctx_s8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
}
-static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ctx_u8(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
}
-static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ctx_s16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
unsigned int *p = (unsigned int *) &frame[*frameUsed];
unsigned int data = expand_data;
- unsigned short *up = (unsigned short *) userPtr;
+ unsigned short __user *up = (unsigned short __user *) userPtr;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int stereo = dmasound.soft.stereo;
}
-static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ctx_u16(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
unsigned int *p = (unsigned int *) &frame[*frameUsed];
unsigned int data = expand_data;
- unsigned short *up = (unsigned short *) userPtr;
+ unsigned short __user *up = (unsigned short __user *) userPtr;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int stereo = dmasound.soft.stereo;
/* data in routines... */
-static ssize_t pmac_ct_s8_read(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ct_s8_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
val = *p++;
val = (val * software_input_volume) >> 7;
data = val >> 8;
- if (put_user(data, (u_char *)userPtr++))
+ if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
if (stereo) {
val = *p;
val = (val * software_input_volume) >> 7;
data = val >> 8;
- if (put_user(data, (u_char *)userPtr++))
+ if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
}
p++;
}
-static ssize_t pmac_ct_u8_read(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ct_u8_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
val = *p++;
val = (val * software_input_volume) >> 7;
data = (val >> 8) ^ 0x80;
- if (put_user(data, (u_char *)userPtr++))
+ if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
if (stereo) {
val = *p;
val = (val * software_input_volume) >> 7;
data = (val >> 8) ^ 0x80;
- if (put_user(data, (u_char *)userPtr++))
+ if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
}
p++;
return stereo? used * 2: used;
}
-static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ct_s16_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
int stereo = dmasound.soft.stereo;
short *fp = (short *) &frame[*frameUsed];
- short *up = (short *) userPtr;
+ short __user *up = (short __user *) userPtr;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
return stereo? used * 4: used * 2;
}
-static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ct_u16_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
int stereo = dmasound.soft.stereo;
short *fp = (short *) &frame[*frameUsed];
- short *up = (short *) userPtr;
+ short __user *up = (short __user *) userPtr;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
/* data in routines (reducing speed)... */
-static ssize_t pmac_ctx_s8_read(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ctx_s8_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
p++;
if (bal < 0) {
data = vall >> 8;
- if (put_user(data, (u_char *)userPtr++))
+ if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
if (stereo) {
data = valr >> 8;
- if (put_user(data, (u_char *)userPtr++))
+ if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
}
userCount--;
}
-static ssize_t pmac_ctx_u8_read(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ctx_u8_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
p++;
if (bal < 0) {
data = (vall >> 8) ^ 0x80;
- if (put_user(data, (u_char *)userPtr++))
+ if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
if (stereo) {
data = (valr >> 8) ^ 0x80;
- if (put_user(data, (u_char *)userPtr++))
+ if (put_user(data, (u_char __user *)userPtr++))
return -EFAULT;
}
userCount--;
return stereo? utotal * 2: utotal;
}
-static ssize_t pmac_ctx_s16_read(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ctx_s16_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
int bal = expand_read_bal;
short *fp = (short *) &frame[*frameUsed];
- short *up = (short *) userPtr;
+ short __user *up = (short __user *) userPtr;
int stereo = dmasound.soft.stereo;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
return stereo? utotal * 4: utotal * 2;
}
-static ssize_t pmac_ctx_u16_read(const u_char *userPtr, size_t userCount,
+static ssize_t pmac_ctx_u16_read(const u_char __user *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
int bal = expand_read_bal;
int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
short *fp = (short *) &frame[*frameUsed];
- short *up = (short *) userPtr;
+ short __user *up = (short __user *) userPtr;
int stereo = dmasound.soft.stereo;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
DPD(3, "emu10k1_audio_read(), buffer=%p, count=%d\n", buffer, (u32) count);
- if (ppos != &file->f_pos)
- return -ESPIPE;
-
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
DPD(3, "emu10k1_audio_write(), buffer=%p, count=%d\n", buffer, (u32) count);
- if (ppos != &file->f_pos)
- return -ESPIPE;
-
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
file->private_data = (void *) wave_dev;
- return 0;
+ return nonseekable_open(inode, file);
}
static int emu10k1_audio_release(struct inode *inode, struct file *file)
{
char s[48];
- if (!proc_mkdir ("driver/emu10k1", 0)) {
+ if (!proc_mkdir ("driver/emu10k1", NULL)) {
printk(KERN_ERR "emu10k1: unable to create proc directory driver/emu10k1\n");
goto err_out;
}
sprintf(s, "driver/emu10k1/%s", pci_name(card->pci_dev));
- if (!proc_mkdir (s, 0)) {
+ if (!proc_mkdir (s, NULL)) {
printk(KERN_ERR "emu10k1: unable to create proc directory %s\n", s);
goto err_emu10k1_proc;
}
sprintf(s, "driver/emu10k1/%s/info", pci_name(card->pci_dev));
- if (!create_proc_read_entry (s, 0, 0, emu10k1_info_proc, card)) {
+ if (!create_proc_read_entry (s, 0, NULL, emu10k1_info_proc, card)) {
printk(KERN_ERR "emu10k1: unable to create proc entry %s\n", s);
goto err_dev_proc;
}
if (!card->is_aps) {
sprintf(s, "driver/emu10k1/%s/ac97", pci_name(card->pci_dev));
- if (!create_proc_read_entry (s, 0, 0, ac97_read_proc, card->ac97)) {
+ if (!create_proc_read_entry (s, 0, NULL, ac97_read_proc, card->ac97)) {
printk(KERN_ERR "emu10k1: unable to create proc entry %s\n", s);
goto err_proc_ac97;
}
up(&card->open_sem);
- return 0;
+ return nonseekable_open(inode, file);
}
static int emu10k1_midi_release(struct inode *inode, struct file *file)
DPD(4, "emu10k1_midi_read(), count %#x\n", (u32) count);
- if (pos != &file->f_pos)
- return -ESPIPE;
-
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
DPD(4, "emu10k1_midi_write(), count=%#x\n", (u32) count);
- if (pos != &file->f_pos)
- return -ESPIPE;
-
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
if (card->seq_mididev) {
kfree(card->seq_mididev);
- card->seq_mididev = 0;
+ card->seq_mididev = NULL;
}
}
}
VALIDATE_STATE(s);
file->private_data = s;
- return 0;
+ return nonseekable_open(inode, file);
}
static int es1370_release_mixdev(struct inode *inode, struct file *file)
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (s->dma_adc.mapped)
return -ENXIO;
if (!access_ok(VERIFY_WRITE, buffer, count))
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (s->dma_dac2.mapped)
return -ENXIO;
if (!access_ok(VERIFY_READ, buffer, count))
s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
up(&s->open_sem);
init_MUTEX(&s->sem);
- return 0;
+ return nonseekable_open(inode, file);
}
static int es1370_release(struct inode *inode, struct file *file)
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (s->dma_dac1.mapped)
return -ENXIO;
if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s)))
spin_unlock_irqrestore(&s->lock, flags);
s->open_mode |= FMODE_DAC;
up(&s->open_sem);
- return 0;
+ return nonseekable_open(inode, file);
}
static int es1370_release_dac(struct inode *inode, struct file *file)
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
if (count == 0)
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
if (count == 0)
spin_unlock_irqrestore(&s->lock, flags);
s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE);
up(&s->open_sem);
- return 0;
+ return nonseekable_open(inode, file);
}
static int es1370_midi_release(struct inode *inode, struct file *file)
}
VALIDATE_STATE(s);
file->private_data = s;
- return 0;
+ return nonseekable_open(inode, file);
}
static int es1371_release_mixdev(struct inode *inode, struct file *file)
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (s->dma_adc.mapped)
return -ENXIO;
if (!access_ok(VERIFY_WRITE, buffer, count))
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (s->dma_dac2.mapped)
return -ENXIO;
if (!access_ok(VERIFY_READ, buffer, count))
s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
up(&s->open_sem);
init_MUTEX(&s->sem);
- return 0;
+ return nonseekable_open(inode, file);
}
static int es1371_release(struct inode *inode, struct file *file)
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (s->dma_dac1.mapped)
return -ENXIO;
if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s)))
spin_unlock_irqrestore(&s->lock, flags);
s->open_mode |= FMODE_DAC;
up(&s->open_sem);
- return 0;
+ return nonseekable_open(inode, file);
}
static int es1371_release_dac(struct inode *inode, struct file *file)
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
if (count == 0)
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
if (count == 0)
spin_unlock_irqrestore(&s->lock, flags);
s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE);
up(&s->open_sem);
- return 0;
+ return nonseekable_open(inode, file);
}
static int es1371_midi_release(struct inode *inode, struct file *file)
return -ENODEV;
VALIDATE_STATE(s);
file->private_data = s;
- return 0;
+ return nonseekable_open(inode, file);
}
static int solo1_release_mixdev(struct inode *inode, struct file *file)
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (s->dma_adc.mapped)
return -ENXIO;
if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (s->dma_dac.mapped)
return -ENXIO;
if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s)))
s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
up(&s->open_sem);
prog_codec(s);
- return 0;
+ return nonseekable_open(inode, file);
}
static /*const*/ struct file_operations solo1_audio_fops = {
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
if (count == 0)
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
if (count == 0)
spin_unlock_irqrestore(&s->lock, flags);
s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE);
up(&s->open_sem);
- return 0;
+ return nonseekable_open(inode, file);
}
static int solo1_midi_release(struct inode *inode, struct file *file)
outb(1, s->sbbase+3); /* enable OPL3 */
s->open_mode |= FMODE_DMFM;
up(&s->open_sem);
- return 0;
+ return nonseekable_open(inode, file);
}
static int solo1_dmfm_release(struct inode *inode, struct file *file)
if (file->f_mode & FMODE_READ)
forte_channel_init (forte, &forte->rec);
- return 0;
+ return nonseekable_open(inode, file);
}
unsigned int i = bytes, sz = 0;
unsigned long flags;
- if (ppos != &file->f_pos)
- return -ESPIPE;
-
if (!access_ok (VERIFY_READ, buffer, bytes))
return -EFAULT;
unsigned int i = bytes, sz;
unsigned long flags;
- if (ppos != &file->f_pos)
- return -ESPIPE;
-
if (!access_ok (VERIFY_WRITE, buffer, bytes))
return -EFAULT;
static int __init
forte_proc_init (void)
{
- if (!proc_mkdir ("driver/forte", 0))
+ if (!proc_mkdir ("driver/forte", NULL))
return -EIO;
- if (!create_proc_read_entry ("driver/forte/chip", 0, 0, forte_proc_read, forte)) {
+ if (!create_proc_read_entry ("driver/forte/chip", 0, NULL, forte_proc_read, forte)) {
remove_proc_entry ("driver/forte", NULL);
return -EIO;
}
- if (!create_proc_read_entry("driver/forte/ac97", 0, 0, ac97_read_proc, forte->ac97)) {
+ if (!create_proc_read_entry("driver/forte/ac97", 0, NULL, ac97_read_proc, forte->ac97)) {
remove_proc_entry ("driver/forte/chip", NULL);
remove_proc_entry ("driver/forte", NULL);
return -EIO;
{
gus_wave_init(hw_config);
- request_region(hw_config->io_base, 16, "GUS");
- request_region(hw_config->io_base + 0x100, 12, "GUS"); /* 0x10c-> is MAX */
-
if (sound_alloc_dma(hw_config->dma, "GUS"))
printk(KERN_ERR "gus_card.c: Can't allocate DMA channel %d\n", hw_config->dma);
if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma)
printk(KERN_ERR "GUS: Unsupported IRQ %d\n", irq);
return 0;
}
- if (check_region(hw_config->io_base, 16))
- printk(KERN_ERR "GUS: I/O range conflict (1)\n");
- else if (check_region(hw_config->io_base + 0x100, 16))
- printk(KERN_ERR "GUS: I/O range conflict (2)\n");
- else if (gus_wave_detect(hw_config->io_base))
+ if (gus_wave_detect(hw_config->io_base))
return 1;
#ifndef EXCLUDE_GUS_IODETECT
* Look at the possible base addresses (0x2X0, X=1, 2, 3, 4, 5, 6)
*/
- for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10)
- if (io_addr != hw_config->io_base) /*
- * Already tested
- */
- if (!check_region(io_addr, 16))
- if (!check_region(io_addr + 0x100, 16))
- if (gus_wave_detect(io_addr))
- {
- hw_config->io_base = io_addr;
- return 1;
- }
+ for (io_addr = 0x210; io_addr <= 0x260; io_addr += 0x10) {
+ if (io_addr == hw_config->io_base) /* Already tested */
+ continue;
+ if (gus_wave_detect(io_addr)) {
+ hw_config->io_base = io_addr;
+ return 1;
+ }
+ }
#endif
printk("NO GUS card found !\n");
break;
}
file->private_data = s;
- return 0;
+ return nonseekable_open(inode, file);
}
static int it8172_release_mixdev(struct inode *inode, struct file *file)
unsigned long flags;
int cnt, remainder, avail;
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (db->mapped)
return -ENXIO;
if (!access_ok(VERIFY_WRITE, buffer, count))
unsigned long flags;
int cnt, remainder, avail;
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (db->mapped)
return -ENXIO;
if (!access_ok(VERIFY_READ, buffer, count))
s->open_mode |= (file->f_mode & (FMODE_READ | FMODE_WRITE));
up(&s->open_sem);
- return 0;
+ return nonseekable_open(inode, file);
}
static int it8172_release(struct inode *inode, struct file *file)
if (!card)
return -ENODEV;
file->private_data = card;
- return 0;
+ return nonseekable_open(inode, file);
}
static int ess_release_mixdev(struct inode *inode, struct file *file)
unsigned char *combbuf = NULL;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (s->dma_adc.mapped)
return -ENXIO;
if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1)))
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (s->dma_dac.mapped)
return -ENXIO;
if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
up(&s->open_sem);
- return 0;
+ return nonseekable_open(inode, file);
}
static int
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (s->dma_adc.mapped)
return -ENXIO;
if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1)))
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (s->dma_dac.mapped)
return -ENXIO;
if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
up(&s->open_sem);
spin_unlock_irqrestore(&c->lock, flags);
- return 0;
+ return nonseekable_open(inode, file);
}
static int m3_release(struct inode *inode, struct file *file)
file->private_data = card->ac97;
- return 0;
+ return nonseekable_open(inode, file);
}
static int m3_release_mixdev(struct inode *inode, struct file *file)
char *pinfiji = "Pinnacle/Fiji";
#endif
- if (check_region(dev.io, dev.numio)) {
+ if (!request_region(dev.io, dev.numio, "probing")) {
printk(KERN_ERR LOGNAME ": I/O port conflict\n");
return -ENODEV;
}
- request_region(dev.io, dev.numio, "probing");
if (reset_dsp() < 0) {
release_region(dev.io, dev.numio);
/* Joystick */
pinnacle_devs[3].io0 = joystick_io;
- if (check_region(cfg, 2)) {
+ if (!request_region(cfg, 2, "Pinnacle/Fiji Config")) {
printk(KERN_ERR LOGNAME ": Config port 0x%x conflict\n", cfg);
return -EIO;
}
- request_region(cfg, 2, "Pinnacle/Fiji Config");
if (msnd_pinnacle_cfg_devices(cfg, reset, pinnacle_devs)) {
printk(KERN_ERR LOGNAME ": Device configuration error\n");
release_region(cfg, 2);
break;
}
file->private_data = s;
- return 0;
+ return nonseekable_open(inode, file);
}
static int vrc5477_ac97_release_mixdev(struct inode *inode, struct file *file)
int copyCount;
size_t avail;
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
unsigned long flags;
int copyCount, avail;
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
ret = 0;
struct list_head *list;
struct vrc5477_ac97_state *s;
int ret=0;
-
+
+ nonseekable_open(inode, file);
for (list = devs.next; ; list = list->next) {
if (list == &devs)
return -ENODEV;
DBG(printk("device num %d open\n",devnum));
+ nonseekable_open(in, f);
for (list = devs.next; ; list = list->next) {
if (list == &devs)
return -ENODEV;
if(dma == NULL || (dma->s) == NULL)
return -ENXIO;
- if (ppos != &file->f_pos)
- return -ESPIPE;
-
if (dma->mmapped || !dma->opened)
return -ENXIO;
if(dma == NULL || (dma->s) == NULL)
return -ENXIO;
- if (ppos != &file->f_pos)
- return -ESPIPE;
-
if (dma->mmapped || !dma->opened)
return -ENXIO;
COMM ("mixer open");
+ nonseekable_open(inode, file);
for (list = devs.next; ; list = list->next) {
if (list == &devs)
return -ENODEV;
if (last_devc == NULL)
return 0;
- last_devc = 0;
+ last_devc = NULL;
if (hw_config->io_base <= 0)
{
}
VALIDATE_STATE(s);
file->private_data = s;
- return 0;
+ return nonseekable_open(inode, file);
}
static int sv_release_mixdev(struct inode *inode, struct file *file)
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (s->dma_adc.mapped)
return -ENXIO;
if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1)))
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (s->dma_dac.mapped)
return -ENXIO;
if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
set_fmt(s, fmtm, fmts);
s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
up(&s->open_sem);
- return 0;
+ return nonseekable_open(inode, file);
}
static int sv_release(struct inode *inode, struct file *file)
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
if (count == 0)
int cnt;
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
if (count == 0)
spin_unlock_irqrestore(&s->lock, flags);
s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE);
up(&s->open_sem);
- return 0;
+ return nonseekable_open(inode, file);
}
static int sv_midi_release(struct inode *inode, struct file *file)
outb(1, s->iosynth+3); /* enable OPL3 */
s->open_mode |= FMODE_DMFM;
up(&s->open_sem);
- return 0;
+ return nonseekable_open(inode, file);
}
static int sv_dmfm_release(struct inode *inode, struct file *file)
}
-// ---------------------------------------------------------------------
-
-static loff_t cs4297a_llseek(struct file *file, loff_t offset, int origin)
-{
- return -ESPIPE;
-}
-
-
// ---------------------------------------------------------------------
static int cs4297a_open_mixdev(struct inode *inode, struct file *file)
CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- 0\n"));
- return 0;
+ return nonseekable_open(inode, file);
}
// ******************************************************************************************
static /*const */ struct file_operations cs4297a_mixer_fops = {
.owner = THIS_MODULE,
- .llseek = cs4297a_llseek,
+ .llseek = no_llseek,
.ioctl = cs4297a_ioctl_mixdev,
.open = cs4297a_open_mixdev,
.release = cs4297a_release_mixdev,
printk(KERN_INFO "cs4297a: cs4297a_read()+ %d \n", count));
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (s->dma_adc.mapped)
return -ENXIO;
if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
count));
VALIDATE_STATE(s);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (s->dma_dac.mapped)
return -ENXIO;
if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s)))
}
CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2,
printk(KERN_INFO "cs4297a: cs4297a_open()- 0\n"));
- return 0;
+ return nonseekable_open(inode, file);
}
// ******************************************************************************************
static /*const */ struct file_operations cs4297a_audio_fops = {
.owner = THIS_MODULE,
- .llseek = cs4297a_llseek,
+ .llseek = no_llseek,
.read = cs4297a_read,
.write = cs4297a_write,
.poll = cs4297a_poll,
unsigned int swptr;
int cnt; /* This many to go in this revolution */
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (dmabuf->mapped)
return -ENXIO;
if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
YMFDBGW("ymf_write: count %d\n", count);
- if (ppos != &file->f_pos)
- return -ESPIPE;
if (dmabuf->mapped)
return -ENXIO;
if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
#endif
up(&unit->open_sem);
- return 0;
+ return nonseekable_open(inode, file);
out_nodma:
/*
match:
file->private_data = unit->ac97_codec[i];
- return 0;
+ return nonseekable_open(inode, file);
}
static int ymf_ioctl_mixdev(struct inode *inode, struct file *file,
codec->dma_area_ba = pba;
codec->dma_area_size = size + 0xff;
- if ((off = ((uint) ptr) & 0xff) != 0) {
+ off = (unsigned long)ptr & 0xff;
+ if (off) {
ptr += 0x100 - off;
pba += 0x100 - off;
}
#define TOTAL_SIZE_CODE (0x200*8)
static long snd_emu10k1_fx8010_read(snd_info_entry_t *entry, void *file_private_data,
- struct file *file, char __user *buf, long count)
+ struct file *file, char __user *buf,
+ unsigned long count, unsigned long pos)
{
long size;
emu10k1_t *emu = snd_magic_cast(emu10k1_t, entry->private_data, return -ENXIO);
offset = emu->audigy ? A_FXGPREGBASE : FXGPREGBASE;
}
size = count;
- if (file->f_pos + size > entry->size)
- size = (long)entry->size - file->f_pos;
+ if (pos + size > entry->size)
+ size = (long)entry->size - pos;
if (size > 0) {
unsigned int *tmp;
long res;
unsigned int idx;
if ((tmp = kmalloc(size + 8, GFP_KERNEL)) == NULL)
return -ENOMEM;
- for (idx = 0; idx < ((file->f_pos & 3) + size + 3) >> 2; idx++)
- tmp[idx] = snd_emu10k1_ptr_read(emu, offset + idx + (file->f_pos >> 2), 0);
- if (copy_to_user(buf, ((char *)tmp) + (file->f_pos & 3), size))
+ for (idx = 0; idx < ((pos & 3) + size + 3) >> 2; idx++)
+ tmp[idx] = snd_emu10k1_ptr_read(emu, offset + idx + (pos >> 2), 0);
+ if (copy_to_user(buf, ((char *)tmp) + (pos & 3), size))
res = -EFAULT;
else {
res = size;
- file->f_pos += size;
}
kfree(tmp);
return res;
menu "ALSA PowerMac devices"
depends on SND!=n && PPC
+comment "ALSA PowerMac requires I2C"
+ depends on SND && I2C=n
+
config SND_POWERMAC
tristate "PowerMac (AWACS, DACA, Burgundy, Tumbler, Keywest)"
- depends on SND
+ depends on SND && I2C
select SND_PCM
endmenu