forked from Minki/linux
Merge branch 'master' into for-next
Conflicts: include/linux/mmzone.h Synced with Linus' tree so that trivial patch can be applied on top of up-to-date code properly. Reported-by: Stephen Rothwell <sfr@canb.auug.org.au>
This commit is contained in:
commit
59f91e5dd0
15
Documentation/ABI/testing/sysfs-bus-i2c-devices-lm3533
Normal file
15
Documentation/ABI/testing/sysfs-bus-i2c-devices-lm3533
Normal file
@ -0,0 +1,15 @@
|
||||
What: /sys/bus/i2c/devices/.../output_hvled[n]
|
||||
Date: April 2012
|
||||
KernelVersion: 3.5
|
||||
Contact: Johan Hovold <jhovold@gmail.com>
|
||||
Description:
|
||||
Set the controlling backlight device for high-voltage current
|
||||
sink HVLED[n] (n = 1, 2) (0, 1).
|
||||
|
||||
What: /sys/bus/i2c/devices/.../output_lvled[n]
|
||||
Date: April 2012
|
||||
KernelVersion: 3.5
|
||||
Contact: Johan Hovold <jhovold@gmail.com>
|
||||
Description:
|
||||
Set the controlling led device for low-voltage current sink
|
||||
LVLED[n] (n = 1..5) (0..3).
|
@ -184,12 +184,14 @@ behind this approach is that a cgroup that aggressively uses a shared
|
||||
page will eventually get charged for it (once it is uncharged from
|
||||
the cgroup that brought it in -- this will happen on memory pressure).
|
||||
|
||||
But see section 8.2: when moving a task to another cgroup, its pages may
|
||||
be recharged to the new cgroup, if move_charge_at_immigrate has been chosen.
|
||||
|
||||
Exception: If CONFIG_CGROUP_CGROUP_MEM_RES_CTLR_SWAP is not used.
|
||||
When you do swapoff and make swapped-out pages of shmem(tmpfs) to
|
||||
be backed into memory in force, charges for pages are accounted against the
|
||||
caller of swapoff rather than the users of shmem.
|
||||
|
||||
|
||||
2.4 Swap Extension (CONFIG_CGROUP_MEM_RES_CTLR_SWAP)
|
||||
|
||||
Swap Extension allows you to record charge for swap. A swapped-in page is
|
||||
@ -430,17 +432,10 @@ hierarchical_memory_limit - # of bytes of memory limit with regard to hierarchy
|
||||
hierarchical_memsw_limit - # of bytes of memory+swap limit with regard to
|
||||
hierarchy under which memory cgroup is.
|
||||
|
||||
total_cache - sum of all children's "cache"
|
||||
total_rss - sum of all children's "rss"
|
||||
total_mapped_file - sum of all children's "cache"
|
||||
total_pgpgin - sum of all children's "pgpgin"
|
||||
total_pgpgout - sum of all children's "pgpgout"
|
||||
total_swap - sum of all children's "swap"
|
||||
total_inactive_anon - sum of all children's "inactive_anon"
|
||||
total_active_anon - sum of all children's "active_anon"
|
||||
total_inactive_file - sum of all children's "inactive_file"
|
||||
total_active_file - sum of all children's "active_file"
|
||||
total_unevictable - sum of all children's "unevictable"
|
||||
total_<counter> - # hierarchical version of <counter>, which in
|
||||
addition to the cgroup's own value includes the
|
||||
sum of all hierarchical children's values of
|
||||
<counter>, i.e. total_cache
|
||||
|
||||
# The following additional stats are dependent on CONFIG_DEBUG_VM.
|
||||
|
||||
@ -622,8 +617,7 @@ memory cgroup.
|
||||
bit | what type of charges would be moved ?
|
||||
-----+------------------------------------------------------------------------
|
||||
0 | A charge of an anonymous page(or swap of it) used by the target task.
|
||||
| Those pages and swaps must be used only by the target task. You must
|
||||
| enable Swap Extension(see 2.4) to enable move of swap charges.
|
||||
| You must enable Swap Extension(see 2.4) to enable move of swap charges.
|
||||
-----+------------------------------------------------------------------------
|
||||
1 | A charge of file pages(normal file, tmpfs file(e.g. ipc shared memory)
|
||||
| and swaps of tmpfs file) mmapped by the target task. Unlike the case of
|
||||
@ -636,8 +630,6 @@ memory cgroup.
|
||||
|
||||
8.3 TODO
|
||||
|
||||
- Implement madvise(2) to let users decide the vma to be moved or not to be
|
||||
moved.
|
||||
- All of moving charge operations are done under cgroup_mutex. It's not good
|
||||
behavior to hold the mutex too long, so we may need some trick.
|
||||
|
||||
|
60
Documentation/devicetree/bindings/mfd/da9052-i2c.txt
Normal file
60
Documentation/devicetree/bindings/mfd/da9052-i2c.txt
Normal file
@ -0,0 +1,60 @@
|
||||
* Dialog DA9052/53 Power Management Integrated Circuit (PMIC)
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "dlg,da9052", "dlg,da9053-aa",
|
||||
"dlg,da9053-ab", or "dlg,da9053-bb"
|
||||
|
||||
Sub-nodes:
|
||||
- regulators : Contain the regulator nodes. The DA9052/53 regulators are
|
||||
bound using their names as listed below:
|
||||
|
||||
buck0 : regulator BUCK0
|
||||
buck1 : regulator BUCK1
|
||||
buck2 : regulator BUCK2
|
||||
buck3 : regulator BUCK3
|
||||
ldo4 : regulator LDO4
|
||||
ldo5 : regulator LDO5
|
||||
ldo6 : regulator LDO6
|
||||
ldo7 : regulator LDO7
|
||||
ldo8 : regulator LDO8
|
||||
ldo9 : regulator LDO9
|
||||
ldo10 : regulator LDO10
|
||||
ldo11 : regulator LDO11
|
||||
ldo12 : regulator LDO12
|
||||
ldo13 : regulator LDO13
|
||||
|
||||
The bindings details of individual regulator device can be found in:
|
||||
Documentation/devicetree/bindings/regulator/regulator.txt
|
||||
|
||||
Examples:
|
||||
|
||||
i2c@63fc8000 { /* I2C1 */
|
||||
status = "okay";
|
||||
|
||||
pmic: dialog@48 {
|
||||
compatible = "dlg,da9053-aa";
|
||||
reg = <0x48>;
|
||||
|
||||
regulators {
|
||||
buck0 {
|
||||
regulator-min-microvolt = <500000>;
|
||||
regulator-max-microvolt = <2075000>;
|
||||
};
|
||||
|
||||
buck1 {
|
||||
regulator-min-microvolt = <500000>;
|
||||
regulator-max-microvolt = <2075000>;
|
||||
};
|
||||
|
||||
buck2 {
|
||||
regulator-min-microvolt = <925000>;
|
||||
regulator-max-microvolt = <2500000>;
|
||||
};
|
||||
|
||||
buck3 {
|
||||
regulator-min-microvolt = <925000>;
|
||||
regulator-max-microvolt = <2500000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
133
Documentation/devicetree/bindings/mfd/tps65910.txt
Normal file
133
Documentation/devicetree/bindings/mfd/tps65910.txt
Normal file
@ -0,0 +1,133 @@
|
||||
TPS65910 Power Management Integrated Circuit
|
||||
|
||||
Required properties:
|
||||
- compatible: "ti,tps65910" or "ti,tps65911"
|
||||
- reg: I2C slave address
|
||||
- interrupts: the interrupt outputs of the controller
|
||||
- #gpio-cells: number of cells to describe a GPIO, this should be 2.
|
||||
The first cell is the GPIO number.
|
||||
The second cell is used to specify additional options <unused>.
|
||||
- gpio-controller: mark the device as a GPIO controller
|
||||
- #interrupt-cells: the number of cells to describe an IRQ, this should be 2.
|
||||
The first cell is the IRQ number.
|
||||
The second cell is the flags, encoded as the trigger masks from
|
||||
Documentation/devicetree/bindings/interrupts.txt
|
||||
- regulators: This is the list of child nodes that specify the regulator
|
||||
initialization data for defined regulators. Not all regulators for the given
|
||||
device need to be present. The definition for each of these nodes is defined
|
||||
using the standard binding for regulators found at
|
||||
Documentation/devicetree/bindings/regulator/regulator.txt.
|
||||
|
||||
The valid names for regulators are:
|
||||
tps65910: vrtc, vio, vdd1, vdd2, vdd3, vdig1, vdig2, vpll, vdac, vaux1,
|
||||
vaux2, vaux33, vmmc
|
||||
tps65911: vrtc, vio, vdd1, vdd3, vddctrl, ldo1, ldo2, ldo3, ldo4, ldo5,
|
||||
ldo6, ldo7, ldo8
|
||||
|
||||
Optional properties:
|
||||
- ti,vmbch-threshold: (tps65911) main battery charged threshold
|
||||
comparator. (see VMBCH_VSEL in TPS65910 datasheet)
|
||||
- ti,vmbch2-threshold: (tps65911) main battery discharged threshold
|
||||
comparator. (see VMBCH_VSEL in TPS65910 datasheet)
|
||||
- ti,en-gpio-sleep: enable sleep control for gpios
|
||||
There should be 9 entries here, one for each gpio.
|
||||
|
||||
Regulator Optional properties:
|
||||
- ti,regulator-ext-sleep-control: enable external sleep
|
||||
control through external inputs [0 (not enabled), 1 (EN1), 2 (EN2) or 4(EN3)]
|
||||
If this property is not defined, it defaults to 0 (not enabled).
|
||||
|
||||
Example:
|
||||
|
||||
pmu: tps65910@d2 {
|
||||
compatible = "ti,tps65910";
|
||||
reg = <0xd2>;
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = < 0 118 0x04 >;
|
||||
|
||||
#gpio-cells = <2>;
|
||||
gpio-controller;
|
||||
|
||||
#interrupt-cells = <2>;
|
||||
interrupt-controller;
|
||||
|
||||
ti,vmbch-threshold = 0;
|
||||
ti,vmbch2-threshold = 0;
|
||||
|
||||
ti,en-gpio-sleep = <0 0 1 0 0 0 0 0 0>;
|
||||
|
||||
regulators {
|
||||
vdd1_reg: vdd1 {
|
||||
regulator-min-microvolt = < 600000>;
|
||||
regulator-max-microvolt = <1500000>;
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
ti,regulator-ext-sleep-control = <0>;
|
||||
};
|
||||
vdd2_reg: vdd2 {
|
||||
regulator-min-microvolt = < 600000>;
|
||||
regulator-max-microvolt = <1500000>;
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
ti,regulator-ext-sleep-control = <4>;
|
||||
};
|
||||
vddctrl_reg: vddctrl {
|
||||
regulator-min-microvolt = < 600000>;
|
||||
regulator-max-microvolt = <1400000>;
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
ti,regulator-ext-sleep-control = <0>;
|
||||
};
|
||||
vio_reg: vio {
|
||||
regulator-min-microvolt = <1500000>;
|
||||
regulator-max-microvolt = <1800000>;
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
ti,regulator-ext-sleep-control = <1>;
|
||||
};
|
||||
ldo1_reg: ldo1 {
|
||||
regulator-min-microvolt = <1000000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
ti,regulator-ext-sleep-control = <0>;
|
||||
};
|
||||
ldo2_reg: ldo2 {
|
||||
regulator-min-microvolt = <1050000>;
|
||||
regulator-max-microvolt = <1050000>;
|
||||
ti,regulator-ext-sleep-control = <0>;
|
||||
};
|
||||
ldo3_reg: ldo3 {
|
||||
regulator-min-microvolt = <1000000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
ti,regulator-ext-sleep-control = <0>;
|
||||
};
|
||||
ldo4_reg: ldo4 {
|
||||
regulator-min-microvolt = <1000000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-always-on;
|
||||
ti,regulator-ext-sleep-control = <0>;
|
||||
};
|
||||
ldo5_reg: ldo5 {
|
||||
regulator-min-microvolt = <1000000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
ti,regulator-ext-sleep-control = <0>;
|
||||
};
|
||||
ldo6_reg: ldo6 {
|
||||
regulator-min-microvolt = <1200000>;
|
||||
regulator-max-microvolt = <1200000>;
|
||||
ti,regulator-ext-sleep-control = <0>;
|
||||
};
|
||||
ldo7_reg: ldo7 {
|
||||
regulator-min-microvolt = <1200000>;
|
||||
regulator-max-microvolt = <1200000>;
|
||||
regulator-always-on;
|
||||
regulator-boot-on;
|
||||
ti,regulator-ext-sleep-control = <1>;
|
||||
};
|
||||
ldo8_reg: ldo8 {
|
||||
regulator-min-microvolt = <1000000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-always-on;
|
||||
ti,regulator-ext-sleep-control = <1>;
|
||||
};
|
||||
};
|
||||
};
|
62
Documentation/devicetree/bindings/mfd/twl6040.txt
Normal file
62
Documentation/devicetree/bindings/mfd/twl6040.txt
Normal file
@ -0,0 +1,62 @@
|
||||
Texas Instruments TWL6040 family
|
||||
|
||||
The TWL6040s are 8-channel high quality low-power audio codecs providing audio
|
||||
and vibra functionality on OMAP4+ platforms.
|
||||
They are connected ot the host processor via i2c for commands, McPDM for audio
|
||||
data and commands.
|
||||
|
||||
Required properties:
|
||||
- compatible : Must be "ti,twl6040";
|
||||
- reg: must be 0x4b for i2c address
|
||||
- interrupts: twl6040 has one interrupt line connecteded to the main SoC
|
||||
- interrupt-parent: The parent interrupt controller
|
||||
- twl6040,audpwron-gpio: Power on GPIO line for the twl6040
|
||||
|
||||
- vio-supply: Regulator for the twl6040 VIO supply
|
||||
- v2v1-supply: Regulator for the twl6040 V2V1 supply
|
||||
|
||||
Optional properties, nodes:
|
||||
- enable-active-high: To power on the twl6040 during boot.
|
||||
|
||||
Vibra functionality
|
||||
Required properties:
|
||||
- vddvibl-supply: Regulator for the left vibra motor
|
||||
- vddvibr-supply: Regulator for the right vibra motor
|
||||
- vibra { }: Configuration section for vibra parameters containing the following
|
||||
properties:
|
||||
- ti,vibldrv-res: Resistance parameter for left driver
|
||||
- ti,vibrdrv-res: Resistance parameter for right driver
|
||||
- ti,viblmotor-res: Resistance parameter for left motor
|
||||
- ti,viblmotor-res: Resistance parameter for right motor
|
||||
|
||||
Optional properties within vibra { } section:
|
||||
- vddvibl_uV: If the vddvibl default voltage need to be changed
|
||||
- vddvibr_uV: If the vddvibr default voltage need to be changed
|
||||
|
||||
Example:
|
||||
&i2c1 {
|
||||
twl6040: twl@4b {
|
||||
compatible = "ti,twl6040";
|
||||
reg = <0x4b>;
|
||||
|
||||
interrupts = <0 119 4>;
|
||||
interrupt-parent = <&gic>;
|
||||
twl6040,audpwron-gpio = <&gpio4 31 0>;
|
||||
|
||||
vio-supply = <&v1v8>;
|
||||
v2v1-supply = <&v2v1>;
|
||||
enable-active-high;
|
||||
|
||||
/* regulators for vibra motor */
|
||||
vddvibl-supply = <&vbat>;
|
||||
vddvibr-supply = <&vbat>;
|
||||
|
||||
vibra {
|
||||
/* Vibra driver, motor resistance parameters */
|
||||
ti,vibldrv-res = <8>;
|
||||
ti,vibrdrv-res = <3>;
|
||||
ti,viblmotor-res = <10>;
|
||||
ti,vibrmotor-res = <10>;
|
||||
};
|
||||
};
|
||||
};
|
@ -60,7 +60,6 @@ ata *);
|
||||
ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
|
||||
ssize_t (*listxattr) (struct dentry *, char *, size_t);
|
||||
int (*removexattr) (struct dentry *, const char *);
|
||||
void (*truncate_range)(struct inode *, loff_t, loff_t);
|
||||
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len);
|
||||
|
||||
locking rules:
|
||||
@ -87,7 +86,6 @@ setxattr: yes
|
||||
getxattr: no
|
||||
listxattr: no
|
||||
removexattr: yes
|
||||
truncate_range: yes
|
||||
fiemap: no
|
||||
Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_mutex on
|
||||
victim.
|
||||
|
@ -743,6 +743,7 @@ Committed_AS: 100056 kB
|
||||
VmallocTotal: 112216 kB
|
||||
VmallocUsed: 428 kB
|
||||
VmallocChunk: 111088 kB
|
||||
AnonHugePages: 49152 kB
|
||||
|
||||
MemTotal: Total usable ram (i.e. physical ram minus a few reserved
|
||||
bits and the kernel binary code)
|
||||
@ -776,6 +777,7 @@ VmallocChunk: 111088 kB
|
||||
Dirty: Memory which is waiting to get written back to the disk
|
||||
Writeback: Memory which is actively being written back to the disk
|
||||
AnonPages: Non-file backed pages mapped into userspace page tables
|
||||
AnonHugePages: Non-file backed huge pages mapped into userspace page tables
|
||||
Mapped: files which have been mmaped, such as libraries
|
||||
Slab: in-kernel data structures cache
|
||||
SReclaimable: Part of Slab, that might be reclaimed, such as caches
|
||||
|
@ -363,7 +363,6 @@ struct inode_operations {
|
||||
ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
|
||||
ssize_t (*listxattr) (struct dentry *, char *, size_t);
|
||||
int (*removexattr) (struct dentry *, const char *);
|
||||
void (*truncate_range)(struct inode *, loff_t, loff_t);
|
||||
};
|
||||
|
||||
Again, all methods are called without any locks being held, unless
|
||||
@ -472,9 +471,6 @@ otherwise noted.
|
||||
removexattr: called by the VFS to remove an extended attribute from
|
||||
a file. This method is called by removexattr(2) system call.
|
||||
|
||||
truncate_range: a method provided by the underlying filesystem to truncate a
|
||||
range of blocks , i.e. punch a hole somewhere in a file.
|
||||
|
||||
|
||||
The Address Space Object
|
||||
========================
|
||||
@ -760,7 +756,7 @@ struct file_operations
|
||||
----------------------
|
||||
|
||||
This describes how the VFS can manipulate an open file. As of kernel
|
||||
2.6.22, the following members are defined:
|
||||
3.5, the following members are defined:
|
||||
|
||||
struct file_operations {
|
||||
struct module *owner;
|
||||
@ -790,6 +786,8 @@ struct file_operations {
|
||||
int (*flock) (struct file *, int, struct file_lock *);
|
||||
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, size_t, unsigned int);
|
||||
ssize_t (*splice_read)(struct file *, struct pipe_inode_info *, size_t, unsigned int);
|
||||
int (*setlease)(struct file *, long arg, struct file_lock **);
|
||||
long (*fallocate)(struct file *, int mode, loff_t offset, loff_t len);
|
||||
};
|
||||
|
||||
Again, all methods are called without any locks being held, unless
|
||||
@ -858,6 +856,11 @@ otherwise noted.
|
||||
splice_read: called by the VFS to splice data from file to a pipe. This
|
||||
method is used by the splice(2) system call
|
||||
|
||||
setlease: called by the VFS to set or release a file lock lease.
|
||||
setlease has the file_lock_lock held and must not sleep.
|
||||
|
||||
fallocate: called by the VFS to preallocate blocks or punch a hole.
|
||||
|
||||
Note that the file operations are implemented by the specific
|
||||
filesystem in which the inode resides. When opening a device node
|
||||
(character or block special) most filesystems will call special
|
||||
|
@ -166,6 +166,68 @@ behavior. So to make them effective you need to restart any
|
||||
application that could have been using hugepages. This also applies to
|
||||
the regions registered in khugepaged.
|
||||
|
||||
== Monitoring usage ==
|
||||
|
||||
The number of transparent huge pages currently used by the system is
|
||||
available by reading the AnonHugePages field in /proc/meminfo. To
|
||||
identify what applications are using transparent huge pages, it is
|
||||
necessary to read /proc/PID/smaps and count the AnonHugePages fields
|
||||
for each mapping. Note that reading the smaps file is expensive and
|
||||
reading it frequently will incur overhead.
|
||||
|
||||
There are a number of counters in /proc/vmstat that may be used to
|
||||
monitor how successfully the system is providing huge pages for use.
|
||||
|
||||
thp_fault_alloc is incremented every time a huge page is successfully
|
||||
allocated to handle a page fault. This applies to both the
|
||||
first time a page is faulted and for COW faults.
|
||||
|
||||
thp_collapse_alloc is incremented by khugepaged when it has found
|
||||
a range of pages to collapse into one huge page and has
|
||||
successfully allocated a new huge page to store the data.
|
||||
|
||||
thp_fault_fallback is incremented if a page fault fails to allocate
|
||||
a huge page and instead falls back to using small pages.
|
||||
|
||||
thp_collapse_alloc_failed is incremented if khugepaged found a range
|
||||
of pages that should be collapsed into one huge page but failed
|
||||
the allocation.
|
||||
|
||||
thp_split is incremented every time a huge page is split into base
|
||||
pages. This can happen for a variety of reasons but a common
|
||||
reason is that a huge page is old and is being reclaimed.
|
||||
|
||||
As the system ages, allocating huge pages may be expensive as the
|
||||
system uses memory compaction to copy data around memory to free a
|
||||
huge page for use. There are some counters in /proc/vmstat to help
|
||||
monitor this overhead.
|
||||
|
||||
compact_stall is incremented every time a process stalls to run
|
||||
memory compaction so that a huge page is free for use.
|
||||
|
||||
compact_success is incremented if the system compacted memory and
|
||||
freed a huge page for use.
|
||||
|
||||
compact_fail is incremented if the system tries to compact memory
|
||||
but failed.
|
||||
|
||||
compact_pages_moved is incremented each time a page is moved. If
|
||||
this value is increasing rapidly, it implies that the system
|
||||
is copying a lot of data to satisfy the huge page allocation.
|
||||
It is possible that the cost of copying exceeds any savings
|
||||
from reduced TLB misses.
|
||||
|
||||
compact_pagemigrate_failed is incremented when the underlying mechanism
|
||||
for moving a page failed.
|
||||
|
||||
compact_blocks_moved is incremented each time memory compaction examines
|
||||
a huge page aligned range of pages.
|
||||
|
||||
It is possible to establish how long the stalls were using the function
|
||||
tracer to record how long was spent in __alloc_pages_nodemask and
|
||||
using the mm_page_alloc tracepoint to identify which allocations were
|
||||
for huge pages.
|
||||
|
||||
== get_user_pages and follow_page ==
|
||||
|
||||
get_user_pages and follow_page if run on a hugepage, will return the
|
||||
|
@ -3382,6 +3382,12 @@ W: http://www.developer.ibm.com/welcome/netfinity/serveraid.html
|
||||
S: Supported
|
||||
F: drivers/scsi/ips.*
|
||||
|
||||
ICH LPC AND GPIO DRIVER
|
||||
M: Peter Tyser <ptyser@xes-inc.com>
|
||||
S: Maintained
|
||||
F: drivers/mfd/lpc_ich.c
|
||||
F: drivers/gpio/gpio-ich.c
|
||||
|
||||
IDE SUBSYSTEM
|
||||
M: "David S. Miller" <davem@davemloft.net>
|
||||
L: linux-ide@vger.kernel.org
|
||||
|
@ -206,7 +206,7 @@ static struct resource ab8500_resources[] = {
|
||||
};
|
||||
|
||||
struct platform_device ab8500_device = {
|
||||
.name = "ab8500-i2c",
|
||||
.name = "ab8500-core",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.platform_data = &ab8500_platdata,
|
||||
|
@ -40,6 +40,7 @@ config CRIS
|
||||
bool
|
||||
default y
|
||||
select HAVE_IDE
|
||||
select GENERIC_ATOMIC64
|
||||
select HAVE_GENERIC_HARDIRQS
|
||||
select GENERIC_IRQ_SHOW
|
||||
select GENERIC_IOMAP
|
||||
|
@ -31,6 +31,56 @@ static inline void native_set_pte(pte_t *ptep, pte_t pte)
|
||||
ptep->pte_low = pte.pte_low;
|
||||
}
|
||||
|
||||
#define pmd_read_atomic pmd_read_atomic
|
||||
/*
|
||||
* pte_offset_map_lock on 32bit PAE kernels was reading the pmd_t with
|
||||
* a "*pmdp" dereference done by gcc. Problem is, in certain places
|
||||
* where pte_offset_map_lock is called, concurrent page faults are
|
||||
* allowed, if the mmap_sem is hold for reading. An example is mincore
|
||||
* vs page faults vs MADV_DONTNEED. On the page fault side
|
||||
* pmd_populate rightfully does a set_64bit, but if we're reading the
|
||||
* pmd_t with a "*pmdp" on the mincore side, a SMP race can happen
|
||||
* because gcc will not read the 64bit of the pmd atomically. To fix
|
||||
* this all places running pmd_offset_map_lock() while holding the
|
||||
* mmap_sem in read mode, shall read the pmdp pointer using this
|
||||
* function to know if the pmd is null nor not, and in turn to know if
|
||||
* they can run pmd_offset_map_lock or pmd_trans_huge or other pmd
|
||||
* operations.
|
||||
*
|
||||
* Without THP if the mmap_sem is hold for reading, the
|
||||
* pmd can only transition from null to not null while pmd_read_atomic runs.
|
||||
* So there's no need of literally reading it atomically.
|
||||
*
|
||||
* With THP if the mmap_sem is hold for reading, the pmd can become
|
||||
* THP or null or point to a pte (and in turn become "stable") at any
|
||||
* time under pmd_read_atomic, so it's mandatory to read it atomically
|
||||
* with cmpxchg8b.
|
||||
*/
|
||||
#ifndef CONFIG_TRANSPARENT_HUGEPAGE
|
||||
static inline pmd_t pmd_read_atomic(pmd_t *pmdp)
|
||||
{
|
||||
pmdval_t ret;
|
||||
u32 *tmp = (u32 *)pmdp;
|
||||
|
||||
ret = (pmdval_t) (*tmp);
|
||||
if (ret) {
|
||||
/*
|
||||
* If the low part is null, we must not read the high part
|
||||
* or we can end up with a partial pmd.
|
||||
*/
|
||||
smp_rmb();
|
||||
ret |= ((pmdval_t)*(tmp + 1)) << 32;
|
||||
}
|
||||
|
||||
return (pmd_t) { ret };
|
||||
}
|
||||
#else /* CONFIG_TRANSPARENT_HUGEPAGE */
|
||||
static inline pmd_t pmd_read_atomic(pmd_t *pmdp)
|
||||
{
|
||||
return (pmd_t) { atomic64_read((atomic64_t *)pmdp) };
|
||||
}
|
||||
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
|
||||
|
||||
static inline void native_set_pte_atomic(pte_t *ptep, pte_t pte)
|
||||
{
|
||||
set_64bit((unsigned long long *)(ptep), native_pte_val(pte));
|
||||
|
12
arch/x86/include/asm/sta2x11.h
Normal file
12
arch/x86/include/asm/sta2x11.h
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Header file for STMicroelectronics ConneXt (STA2X11) IOHub
|
||||
*/
|
||||
#ifndef __ASM_STA2X11_H
|
||||
#define __ASM_STA2X11_H
|
||||
|
||||
#include <linux/pci.h>
|
||||
|
||||
/* This needs to be called from the MFD to configure its sub-devices */
|
||||
struct sta2x11_instance *sta2x11_get_instance(struct pci_dev *pdev);
|
||||
|
||||
#endif /* __ASM_STA2X11_H */
|
@ -113,7 +113,9 @@ static void __init __e820_add_region(struct e820map *e820x, u64 start, u64 size,
|
||||
int x = e820x->nr_map;
|
||||
|
||||
if (x >= ARRAY_SIZE(e820x->map)) {
|
||||
printk(KERN_ERR "Ooops! Too many entries in the memory map!\n");
|
||||
printk(KERN_ERR "e820: too many entries; ignoring [mem %#010llx-%#010llx]\n",
|
||||
(unsigned long long) start,
|
||||
(unsigned long long) (start + size - 1));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -133,19 +135,19 @@ static void __init e820_print_type(u32 type)
|
||||
switch (type) {
|
||||
case E820_RAM:
|
||||
case E820_RESERVED_KERN:
|
||||
printk(KERN_CONT "(usable)");
|
||||
printk(KERN_CONT "usable");
|
||||
break;
|
||||
case E820_RESERVED:
|
||||
printk(KERN_CONT "(reserved)");
|
||||
printk(KERN_CONT "reserved");
|
||||
break;
|
||||
case E820_ACPI:
|
||||
printk(KERN_CONT "(ACPI data)");
|
||||
printk(KERN_CONT "ACPI data");
|
||||
break;
|
||||
case E820_NVS:
|
||||
printk(KERN_CONT "(ACPI NVS)");
|
||||
printk(KERN_CONT "ACPI NVS");
|
||||
break;
|
||||
case E820_UNUSABLE:
|
||||
printk(KERN_CONT "(unusable)");
|
||||
printk(KERN_CONT "unusable");
|
||||
break;
|
||||
default:
|
||||
printk(KERN_CONT "type %u", type);
|
||||
@ -158,10 +160,10 @@ void __init e820_print_map(char *who)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < e820.nr_map; i++) {
|
||||
printk(KERN_INFO " %s: %016Lx - %016Lx ", who,
|
||||
printk(KERN_INFO "%s: [mem %#018Lx-%#018Lx] ", who,
|
||||
(unsigned long long) e820.map[i].addr,
|
||||
(unsigned long long)
|
||||
(e820.map[i].addr + e820.map[i].size));
|
||||
(e820.map[i].addr + e820.map[i].size - 1));
|
||||
e820_print_type(e820.map[i].type);
|
||||
printk(KERN_CONT "\n");
|
||||
}
|
||||
@ -428,9 +430,8 @@ static u64 __init __e820_update_range(struct e820map *e820x, u64 start,
|
||||
size = ULLONG_MAX - start;
|
||||
|
||||
end = start + size;
|
||||
printk(KERN_DEBUG "e820 update range: %016Lx - %016Lx ",
|
||||
(unsigned long long) start,
|
||||
(unsigned long long) end);
|
||||
printk(KERN_DEBUG "e820: update [mem %#010Lx-%#010Lx] ",
|
||||
(unsigned long long) start, (unsigned long long) (end - 1));
|
||||
e820_print_type(old_type);
|
||||
printk(KERN_CONT " ==> ");
|
||||
e820_print_type(new_type);
|
||||
@ -509,9 +510,8 @@ u64 __init e820_remove_range(u64 start, u64 size, unsigned old_type,
|
||||
size = ULLONG_MAX - start;
|
||||
|
||||
end = start + size;
|
||||
printk(KERN_DEBUG "e820 remove range: %016Lx - %016Lx ",
|
||||
(unsigned long long) start,
|
||||
(unsigned long long) end);
|
||||
printk(KERN_DEBUG "e820: remove [mem %#010Lx-%#010Lx] ",
|
||||
(unsigned long long) start, (unsigned long long) (end - 1));
|
||||
if (checktype)
|
||||
e820_print_type(old_type);
|
||||
printk(KERN_CONT "\n");
|
||||
@ -567,7 +567,7 @@ void __init update_e820(void)
|
||||
if (sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &nr_map))
|
||||
return;
|
||||
e820.nr_map = nr_map;
|
||||
printk(KERN_INFO "modified physical RAM map:\n");
|
||||
printk(KERN_INFO "e820: modified physical RAM map:\n");
|
||||
e820_print_map("modified");
|
||||
}
|
||||
static void __init update_e820_saved(void)
|
||||
@ -637,8 +637,8 @@ __init void e820_setup_gap(void)
|
||||
if (!found) {
|
||||
gapstart = (max_pfn << PAGE_SHIFT) + 1024*1024;
|
||||
printk(KERN_ERR
|
||||
"PCI: Warning: Cannot find a gap in the 32bit address range\n"
|
||||
"PCI: Unassigned devices with 32bit resource registers may break!\n");
|
||||
"e820: cannot find a gap in the 32bit address range\n"
|
||||
"e820: PCI devices with unassigned 32bit BARs may break!\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -648,8 +648,8 @@ __init void e820_setup_gap(void)
|
||||
pci_mem_start = gapstart;
|
||||
|
||||
printk(KERN_INFO
|
||||
"Allocating PCI resources starting at %lx (gap: %lx:%lx)\n",
|
||||
pci_mem_start, gapstart, gapsize);
|
||||
"e820: [mem %#010lx-%#010lx] available for PCI devices\n",
|
||||
gapstart, gapstart + gapsize - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -667,7 +667,7 @@ void __init parse_e820_ext(struct setup_data *sdata)
|
||||
extmap = (struct e820entry *)(sdata->data);
|
||||
__append_e820_map(extmap, entries);
|
||||
sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map);
|
||||
printk(KERN_INFO "extended physical RAM map:\n");
|
||||
printk(KERN_INFO "e820: extended physical RAM map:\n");
|
||||
e820_print_map("extended");
|
||||
}
|
||||
|
||||
@ -734,7 +734,7 @@ u64 __init early_reserve_e820(u64 size, u64 align)
|
||||
addr = __memblock_alloc_base(size, align, MEMBLOCK_ALLOC_ACCESSIBLE);
|
||||
if (addr) {
|
||||
e820_update_range_saved(addr, size, E820_RAM, E820_RESERVED);
|
||||
printk(KERN_INFO "update e820_saved for early_reserve_e820\n");
|
||||
printk(KERN_INFO "e820: update e820_saved for early_reserve_e820\n");
|
||||
update_e820_saved();
|
||||
}
|
||||
|
||||
@ -784,7 +784,7 @@ static unsigned long __init e820_end_pfn(unsigned long limit_pfn, unsigned type)
|
||||
if (last_pfn > max_arch_pfn)
|
||||
last_pfn = max_arch_pfn;
|
||||
|
||||
printk(KERN_INFO "last_pfn = %#lx max_arch_pfn = %#lx\n",
|
||||
printk(KERN_INFO "e820: last_pfn = %#lx max_arch_pfn = %#lx\n",
|
||||
last_pfn, max_arch_pfn);
|
||||
return last_pfn;
|
||||
}
|
||||
@ -888,7 +888,7 @@ void __init finish_e820_parsing(void)
|
||||
early_panic("Invalid user supplied memory map");
|
||||
e820.nr_map = nr;
|
||||
|
||||
printk(KERN_INFO "user-defined physical RAM map:\n");
|
||||
printk(KERN_INFO "e820: user-defined physical RAM map:\n");
|
||||
e820_print_map("user");
|
||||
}
|
||||
}
|
||||
@ -996,8 +996,9 @@ void __init e820_reserve_resources_late(void)
|
||||
end = MAX_RESOURCE_SIZE;
|
||||
if (start >= end)
|
||||
continue;
|
||||
printk(KERN_DEBUG "reserve RAM buffer: %016llx - %016llx ",
|
||||
start, end);
|
||||
printk(KERN_DEBUG
|
||||
"e820: reserve RAM buffer [mem %#010llx-%#010llx]\n",
|
||||
start, end);
|
||||
reserve_region_with_split(&iomem_resource, start, end,
|
||||
"RAM buffer");
|
||||
}
|
||||
@ -1047,7 +1048,7 @@ void __init setup_memory_map(void)
|
||||
|
||||
who = x86_init.resources.memory_setup();
|
||||
memcpy(&e820_saved, &e820, sizeof(struct e820map));
|
||||
printk(KERN_INFO "BIOS-provided physical RAM map:\n");
|
||||
printk(KERN_INFO "e820: BIOS-provided physical RAM map:\n");
|
||||
e820_print_map(who);
|
||||
}
|
||||
|
||||
|
@ -568,8 +568,8 @@ static int __init smp_scan_config(unsigned long base, unsigned long length)
|
||||
struct mpf_intel *mpf;
|
||||
unsigned long mem;
|
||||
|
||||
apic_printk(APIC_VERBOSE, "Scan SMP from %p for %ld bytes.\n",
|
||||
bp, length);
|
||||
apic_printk(APIC_VERBOSE, "Scan for SMP in [mem %#010lx-%#010lx]\n",
|
||||
base, base + length - 1);
|
||||
BUILD_BUG_ON(sizeof(*mpf) != 16);
|
||||
|
||||
while (length > 0) {
|
||||
@ -584,8 +584,10 @@ static int __init smp_scan_config(unsigned long base, unsigned long length)
|
||||
#endif
|
||||
mpf_found = mpf;
|
||||
|
||||
printk(KERN_INFO "found SMP MP-table at [%p] %llx\n",
|
||||
mpf, (u64)virt_to_phys(mpf));
|
||||
printk(KERN_INFO "found SMP MP-table at [mem %#010llx-%#010llx] mapped at [%p]\n",
|
||||
(unsigned long long) virt_to_phys(mpf),
|
||||
(unsigned long long) virt_to_phys(mpf) +
|
||||
sizeof(*mpf) - 1, mpf);
|
||||
|
||||
mem = virt_to_phys(mpf);
|
||||
memblock_reserve(mem, sizeof(*mpf));
|
||||
|
@ -334,8 +334,8 @@ static void __init relocate_initrd(void)
|
||||
memblock_reserve(ramdisk_here, area_size);
|
||||
initrd_start = ramdisk_here + PAGE_OFFSET;
|
||||
initrd_end = initrd_start + ramdisk_size;
|
||||
printk(KERN_INFO "Allocated new RAMDISK: %08llx - %08llx\n",
|
||||
ramdisk_here, ramdisk_here + ramdisk_size);
|
||||
printk(KERN_INFO "Allocated new RAMDISK: [mem %#010llx-%#010llx]\n",
|
||||
ramdisk_here, ramdisk_here + ramdisk_size - 1);
|
||||
|
||||
q = (char *)initrd_start;
|
||||
|
||||
@ -366,8 +366,8 @@ static void __init relocate_initrd(void)
|
||||
/* high pages is not converted by early_res_to_bootmem */
|
||||
ramdisk_image = boot_params.hdr.ramdisk_image;
|
||||
ramdisk_size = boot_params.hdr.ramdisk_size;
|
||||
printk(KERN_INFO "Move RAMDISK from %016llx - %016llx to"
|
||||
" %08llx - %08llx\n",
|
||||
printk(KERN_INFO "Move RAMDISK from [mem %#010llx-%#010llx] to"
|
||||
" [mem %#010llx-%#010llx]\n",
|
||||
ramdisk_image, ramdisk_image + ramdisk_size - 1,
|
||||
ramdisk_here, ramdisk_here + ramdisk_size - 1);
|
||||
}
|
||||
@ -392,8 +392,8 @@ static void __init reserve_initrd(void)
|
||||
ramdisk_size, end_of_lowmem>>1);
|
||||
}
|
||||
|
||||
printk(KERN_INFO "RAMDISK: %08llx - %08llx\n", ramdisk_image,
|
||||
ramdisk_end);
|
||||
printk(KERN_INFO "RAMDISK: [mem %#010llx-%#010llx]\n", ramdisk_image,
|
||||
ramdisk_end - 1);
|
||||
|
||||
|
||||
if (ramdisk_end <= end_of_lowmem) {
|
||||
@ -906,8 +906,8 @@ void __init setup_arch(char **cmdline_p)
|
||||
setup_bios_corruption_check();
|
||||
#endif
|
||||
|
||||
printk(KERN_DEBUG "initial memory mapped : 0 - %08lx\n",
|
||||
max_pfn_mapped<<PAGE_SHIFT);
|
||||
printk(KERN_DEBUG "initial memory mapped: [mem 0x00000000-%#010lx]\n",
|
||||
(max_pfn_mapped<<PAGE_SHIFT) - 1);
|
||||
|
||||
setup_trampolines();
|
||||
|
||||
|
@ -84,8 +84,9 @@ static void __init find_early_table_space(struct map_range *mr, unsigned long en
|
||||
pgt_buf_end = pgt_buf_start;
|
||||
pgt_buf_top = pgt_buf_start + (tables >> PAGE_SHIFT);
|
||||
|
||||
printk(KERN_DEBUG "kernel direct mapping tables up to %lx @ %lx-%lx\n",
|
||||
end, pgt_buf_start << PAGE_SHIFT, pgt_buf_top << PAGE_SHIFT);
|
||||
printk(KERN_DEBUG "kernel direct mapping tables up to %#lx @ [mem %#010lx-%#010lx]\n",
|
||||
end - 1, pgt_buf_start << PAGE_SHIFT,
|
||||
(pgt_buf_top << PAGE_SHIFT) - 1);
|
||||
}
|
||||
|
||||
void __init native_pagetable_reserve(u64 start, u64 end)
|
||||
@ -132,7 +133,8 @@ unsigned long __init_refok init_memory_mapping(unsigned long start,
|
||||
int nr_range, i;
|
||||
int use_pse, use_gbpages;
|
||||
|
||||
printk(KERN_INFO "init_memory_mapping: %016lx-%016lx\n", start, end);
|
||||
printk(KERN_INFO "init_memory_mapping: [mem %#010lx-%#010lx]\n",
|
||||
start, end - 1);
|
||||
|
||||
#if defined(CONFIG_DEBUG_PAGEALLOC) || defined(CONFIG_KMEMCHECK)
|
||||
/*
|
||||
@ -251,8 +253,8 @@ unsigned long __init_refok init_memory_mapping(unsigned long start,
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_range; i++)
|
||||
printk(KERN_DEBUG " %010lx - %010lx page %s\n",
|
||||
mr[i].start, mr[i].end,
|
||||
printk(KERN_DEBUG " [mem %#010lx-%#010lx] page %s\n",
|
||||
mr[i].start, mr[i].end - 1,
|
||||
(mr[i].page_size_mask & (1<<PG_LEVEL_1G))?"1G":(
|
||||
(mr[i].page_size_mask & (1<<PG_LEVEL_2M))?"2M":"4k"));
|
||||
|
||||
@ -350,8 +352,8 @@ void free_init_pages(char *what, unsigned long begin, unsigned long end)
|
||||
* create a kernel page fault:
|
||||
*/
|
||||
#ifdef CONFIG_DEBUG_PAGEALLOC
|
||||
printk(KERN_INFO "debug: unmapping init memory %08lx..%08lx\n",
|
||||
begin, end);
|
||||
printk(KERN_INFO "debug: unmapping init [mem %#010lx-%#010lx]\n",
|
||||
begin, end - 1);
|
||||
set_memory_np(begin, (end - begin) >> PAGE_SHIFT);
|
||||
#else
|
||||
/*
|
||||
|
@ -141,8 +141,8 @@ static int __init numa_add_memblk_to(int nid, u64 start, u64 end,
|
||||
|
||||
/* whine about and ignore invalid blks */
|
||||
if (start > end || nid < 0 || nid >= MAX_NUMNODES) {
|
||||
pr_warning("NUMA: Warning: invalid memblk node %d (%Lx-%Lx)\n",
|
||||
nid, start, end);
|
||||
pr_warning("NUMA: Warning: invalid memblk node %d [mem %#010Lx-%#010Lx]\n",
|
||||
nid, start, end - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -210,8 +210,8 @@ static void __init setup_node_data(int nid, u64 start, u64 end)
|
||||
|
||||
start = roundup(start, ZONE_ALIGN);
|
||||
|
||||
printk(KERN_INFO "Initmem setup node %d %016Lx-%016Lx\n",
|
||||
nid, start, end);
|
||||
printk(KERN_INFO "Initmem setup node %d [mem %#010Lx-%#010Lx]\n",
|
||||
nid, start, end - 1);
|
||||
|
||||
/*
|
||||
* Allocate node data. Try remap allocator first, node-local
|
||||
@ -232,7 +232,7 @@ static void __init setup_node_data(int nid, u64 start, u64 end)
|
||||
}
|
||||
|
||||
/* report and initialize */
|
||||
printk(KERN_INFO " NODE_DATA [%016Lx - %016Lx]%s\n",
|
||||
printk(KERN_INFO " NODE_DATA [mem %#010Lx-%#010Lx]%s\n",
|
||||
nd_pa, nd_pa + nd_size - 1, remapped ? " (remapped)" : "");
|
||||
tnid = early_pfn_to_nid(nd_pa >> PAGE_SHIFT);
|
||||
if (!remapped && tnid != nid)
|
||||
@ -291,14 +291,14 @@ int __init numa_cleanup_meminfo(struct numa_meminfo *mi)
|
||||
*/
|
||||
if (bi->end > bj->start && bi->start < bj->end) {
|
||||
if (bi->nid != bj->nid) {
|
||||
pr_err("NUMA: node %d (%Lx-%Lx) overlaps with node %d (%Lx-%Lx)\n",
|
||||
bi->nid, bi->start, bi->end,
|
||||
bj->nid, bj->start, bj->end);
|
||||
pr_err("NUMA: node %d [mem %#010Lx-%#010Lx] overlaps with node %d [mem %#010Lx-%#010Lx]\n",
|
||||
bi->nid, bi->start, bi->end - 1,
|
||||
bj->nid, bj->start, bj->end - 1);
|
||||
return -EINVAL;
|
||||
}
|
||||
pr_warning("NUMA: Warning: node %d (%Lx-%Lx) overlaps with itself (%Lx-%Lx)\n",
|
||||
bi->nid, bi->start, bi->end,
|
||||
bj->start, bj->end);
|
||||
pr_warning("NUMA: Warning: node %d [mem %#010Lx-%#010Lx] overlaps with itself [mem %#010Lx-%#010Lx]\n",
|
||||
bi->nid, bi->start, bi->end - 1,
|
||||
bj->start, bj->end - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -320,9 +320,9 @@ int __init numa_cleanup_meminfo(struct numa_meminfo *mi)
|
||||
}
|
||||
if (k < mi->nr_blks)
|
||||
continue;
|
||||
printk(KERN_INFO "NUMA: Node %d [%Lx,%Lx) + [%Lx,%Lx) -> [%Lx,%Lx)\n",
|
||||
bi->nid, bi->start, bi->end, bj->start, bj->end,
|
||||
start, end);
|
||||
printk(KERN_INFO "NUMA: Node %d [mem %#010Lx-%#010Lx] + [mem %#010Lx-%#010Lx] -> [mem %#010Lx-%#010Lx]\n",
|
||||
bi->nid, bi->start, bi->end - 1, bj->start,
|
||||
bj->end - 1, start, end - 1);
|
||||
bi->start = start;
|
||||
bi->end = end;
|
||||
numa_remove_memblk_from(j--, mi);
|
||||
@ -616,8 +616,8 @@ static int __init dummy_numa_init(void)
|
||||
{
|
||||
printk(KERN_INFO "%s\n",
|
||||
numa_off ? "NUMA turned off" : "No NUMA configuration found");
|
||||
printk(KERN_INFO "Faking a node at %016Lx-%016Lx\n",
|
||||
0LLU, PFN_PHYS(max_pfn));
|
||||
printk(KERN_INFO "Faking a node at [mem %#018Lx-%#018Lx]\n",
|
||||
0LLU, PFN_PHYS(max_pfn) - 1);
|
||||
|
||||
node_set(0, numa_nodes_parsed);
|
||||
numa_add_memblk(0, 0, PFN_PHYS(max_pfn));
|
||||
|
@ -68,8 +68,8 @@ static int __init emu_setup_memblk(struct numa_meminfo *ei,
|
||||
numa_remove_memblk_from(phys_blk, pi);
|
||||
}
|
||||
|
||||
printk(KERN_INFO "Faking node %d at %016Lx-%016Lx (%LuMB)\n", nid,
|
||||
eb->start, eb->end, (eb->end - eb->start) >> 20);
|
||||
printk(KERN_INFO "Faking node %d at [mem %#018Lx-%#018Lx] (%LuMB)\n",
|
||||
nid, eb->start, eb->end - 1, (eb->end - eb->start) >> 20);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -209,9 +209,8 @@ static int reserve_ram_pages_type(u64 start, u64 end, unsigned long req_type,
|
||||
page = pfn_to_page(pfn);
|
||||
type = get_page_memtype(page);
|
||||
if (type != -1) {
|
||||
printk(KERN_INFO "reserve_ram_pages_type failed "
|
||||
"0x%Lx-0x%Lx, track 0x%lx, req 0x%lx\n",
|
||||
start, end, type, req_type);
|
||||
printk(KERN_INFO "reserve_ram_pages_type failed [mem %#010Lx-%#010Lx], track 0x%lx, req 0x%lx\n",
|
||||
start, end - 1, type, req_type);
|
||||
if (new_type)
|
||||
*new_type = type;
|
||||
|
||||
@ -314,9 +313,9 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type,
|
||||
|
||||
err = rbt_memtype_check_insert(new, new_type);
|
||||
if (err) {
|
||||
printk(KERN_INFO "reserve_memtype failed 0x%Lx-0x%Lx, "
|
||||
"track %s, req %s\n",
|
||||
start, end, cattr_name(new->type), cattr_name(req_type));
|
||||
printk(KERN_INFO "reserve_memtype failed [mem %#010Lx-%#010Lx], track %s, req %s\n",
|
||||
start, end - 1,
|
||||
cattr_name(new->type), cattr_name(req_type));
|
||||
kfree(new);
|
||||
spin_unlock(&memtype_lock);
|
||||
|
||||
@ -325,8 +324,8 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type,
|
||||
|
||||
spin_unlock(&memtype_lock);
|
||||
|
||||
dprintk("reserve_memtype added 0x%Lx-0x%Lx, track %s, req %s, ret %s\n",
|
||||
start, end, cattr_name(new->type), cattr_name(req_type),
|
||||
dprintk("reserve_memtype added [mem %#010Lx-%#010Lx], track %s, req %s, ret %s\n",
|
||||
start, end - 1, cattr_name(new->type), cattr_name(req_type),
|
||||
new_type ? cattr_name(*new_type) : "-");
|
||||
|
||||
return err;
|
||||
@ -360,14 +359,14 @@ int free_memtype(u64 start, u64 end)
|
||||
spin_unlock(&memtype_lock);
|
||||
|
||||
if (!entry) {
|
||||
printk(KERN_INFO "%s:%d freeing invalid memtype %Lx-%Lx\n",
|
||||
current->comm, current->pid, start, end);
|
||||
printk(KERN_INFO "%s:%d freeing invalid memtype [mem %#010Lx-%#010Lx]\n",
|
||||
current->comm, current->pid, start, end - 1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
kfree(entry);
|
||||
|
||||
dprintk("free_memtype request 0x%Lx-0x%Lx\n", start, end);
|
||||
dprintk("free_memtype request [mem %#010Lx-%#010Lx]\n", start, end - 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -491,9 +490,8 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size)
|
||||
|
||||
while (cursor < to) {
|
||||
if (!devmem_is_allowed(pfn)) {
|
||||
printk(KERN_INFO
|
||||
"Program %s tried to access /dev/mem between %Lx->%Lx.\n",
|
||||
current->comm, from, to);
|
||||
printk(KERN_INFO "Program %s tried to access /dev/mem between [mem %#010Lx-%#010Lx]\n",
|
||||
current->comm, from, to - 1);
|
||||
return 0;
|
||||
}
|
||||
cursor += PAGE_SIZE;
|
||||
@ -554,12 +552,11 @@ int kernel_map_sync_memtype(u64 base, unsigned long size, unsigned long flags)
|
||||
size;
|
||||
|
||||
if (ioremap_change_attr((unsigned long)__va(base), id_sz, flags) < 0) {
|
||||
printk(KERN_INFO
|
||||
"%s:%d ioremap_change_attr failed %s "
|
||||
"for %Lx-%Lx\n",
|
||||
printk(KERN_INFO "%s:%d ioremap_change_attr failed %s "
|
||||
"for [mem %#010Lx-%#010Lx]\n",
|
||||
current->comm, current->pid,
|
||||
cattr_name(flags),
|
||||
base, (unsigned long long)(base + size));
|
||||
base, (unsigned long long)(base + size-1));
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
@ -591,12 +588,11 @@ static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t *vma_prot,
|
||||
|
||||
flags = lookup_memtype(paddr);
|
||||
if (want_flags != flags) {
|
||||
printk(KERN_WARNING
|
||||
"%s:%d map pfn RAM range req %s for %Lx-%Lx, got %s\n",
|
||||
printk(KERN_WARNING "%s:%d map pfn RAM range req %s for [mem %#010Lx-%#010Lx], got %s\n",
|
||||
current->comm, current->pid,
|
||||
cattr_name(want_flags),
|
||||
(unsigned long long)paddr,
|
||||
(unsigned long long)(paddr + size),
|
||||
(unsigned long long)(paddr + size - 1),
|
||||
cattr_name(flags));
|
||||
*vma_prot = __pgprot((pgprot_val(*vma_prot) &
|
||||
(~_PAGE_CACHE_MASK)) |
|
||||
@ -614,11 +610,11 @@ static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t *vma_prot,
|
||||
!is_new_memtype_allowed(paddr, size, want_flags, flags)) {
|
||||
free_memtype(paddr, paddr + size);
|
||||
printk(KERN_ERR "%s:%d map pfn expected mapping type %s"
|
||||
" for %Lx-%Lx, got %s\n",
|
||||
" for [mem %#010Lx-%#010Lx], got %s\n",
|
||||
current->comm, current->pid,
|
||||
cattr_name(want_flags),
|
||||
(unsigned long long)paddr,
|
||||
(unsigned long long)(paddr + size),
|
||||
(unsigned long long)(paddr + size - 1),
|
||||
cattr_name(flags));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -176,8 +176,9 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
|
||||
return;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "SRAT: Node %u PXM %u %Lx-%Lx\n", node, pxm,
|
||||
start, end);
|
||||
printk(KERN_INFO "SRAT: Node %u PXM %u [mem %#010Lx-%#010Lx]\n",
|
||||
node, pxm,
|
||||
(unsigned long long) start, (unsigned long long) end - 1);
|
||||
}
|
||||
|
||||
void __init acpi_numa_arch_fixup(void) {}
|
||||
|
@ -592,11 +592,9 @@ static ssize_t print_nodes_state(enum node_states state, char *buf)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = nodelist_scnprintf(buf, PAGE_SIZE, node_states[state]);
|
||||
if (n > 0 && PAGE_SIZE > n + 1) {
|
||||
*(buf + n++) = '\n';
|
||||
*(buf + n++) = '\0';
|
||||
}
|
||||
n = nodelist_scnprintf(buf, PAGE_SIZE-2, node_states[state]);
|
||||
buf[n++] = '\n';
|
||||
buf[n] = '\0';
|
||||
return n;
|
||||
}
|
||||
|
||||
|
@ -167,6 +167,14 @@ config GPIO_PXA
|
||||
help
|
||||
Say yes here to support the PXA GPIO device
|
||||
|
||||
config GPIO_STA2X11
|
||||
bool "STA2x11/ConneXt GPIO support"
|
||||
depends on MFD_STA2X11
|
||||
select GENERIC_IRQ_CHIP
|
||||
help
|
||||
Say yes here to support the STA2x11/ConneXt GPIO device.
|
||||
The GPIO module has 128 GPIO pins with alternate functions.
|
||||
|
||||
config GPIO_XILINX
|
||||
bool "Xilinx GPIO support"
|
||||
depends on PPC_OF || MICROBLAZE
|
||||
@ -180,13 +188,13 @@ config GPIO_VR41XX
|
||||
Say yes here to support the NEC VR4100 series General-purpose I/O Uint
|
||||
|
||||
config GPIO_SCH
|
||||
tristate "Intel SCH/TunnelCreek GPIO"
|
||||
tristate "Intel SCH/TunnelCreek/Centerton GPIO"
|
||||
depends on PCI && X86
|
||||
select MFD_CORE
|
||||
select LPC_SCH
|
||||
help
|
||||
Say yes here to support GPIO interface on Intel Poulsbo SCH
|
||||
or Intel Tunnel Creek processor.
|
||||
Say yes here to support GPIO interface on Intel Poulsbo SCH,
|
||||
Intel Tunnel Creek processor or Intel Centerton processor.
|
||||
The Intel SCH contains a total of 14 GPIO pins. Ten GPIOs are
|
||||
powered by the core power rail and are turned off during sleep
|
||||
modes (S3 and higher). The remaining four GPIOs are powered by
|
||||
@ -195,6 +203,22 @@ config GPIO_SCH
|
||||
system from the Suspend-to-RAM state.
|
||||
The Intel Tunnel Creek processor has 5 GPIOs powered by the
|
||||
core power rail and 9 from suspend power supply.
|
||||
The Intel Centerton processor has a total of 30 GPIO pins.
|
||||
Twenty-one are powered by the core power rail and 9 from the
|
||||
suspend power supply.
|
||||
|
||||
config GPIO_ICH
|
||||
tristate "Intel ICH GPIO"
|
||||
depends on PCI && X86
|
||||
select MFD_CORE
|
||||
select LPC_ICH
|
||||
help
|
||||
Say yes here to support the GPIO functionality of a number of Intel
|
||||
ICH-based chipsets. Currently supported devices: ICH6, ICH7, ICH8
|
||||
ICH9, ICH10, Series 5/3400 (eg Ibex Peak), Series 6/C200 (eg
|
||||
Cougar Point), NM10 (Tiger Point), and 3100 (Whitmore Lake).
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config GPIO_VX855
|
||||
tristate "VIA VX855/VX875 GPIO"
|
||||
|
@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_DAVINCI) += gpio-davinci.o
|
||||
obj-$(CONFIG_GPIO_EM) += gpio-em.o
|
||||
obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o
|
||||
obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o
|
||||
obj-$(CONFIG_GPIO_ICH) += gpio-ich.o
|
||||
obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o
|
||||
obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o
|
||||
obj-$(CONFIG_ARCH_KS8695) += gpio-ks8695.o
|
||||
@ -51,6 +52,7 @@ obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o
|
||||
obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
|
||||
obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
|
||||
obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o
|
||||
obj-$(CONFIG_GPIO_STA2X11) += gpio-sta2x11.o
|
||||
obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
|
||||
obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o
|
||||
obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
|
||||
|
419
drivers/gpio/gpio-ich.c
Normal file
419
drivers/gpio/gpio-ich.c
Normal file
@ -0,0 +1,419 @@
|
||||
/*
|
||||
* Intel ICH6-10, Series 5 and 6 GPIO driver
|
||||
*
|
||||
* Copyright (C) 2010 Extreme Engineering Solutions.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/lpc_ich.h>
|
||||
|
||||
#define DRV_NAME "gpio_ich"
|
||||
|
||||
/*
|
||||
* GPIO register offsets in GPIO I/O space.
|
||||
* Each chunk of 32 GPIOs is manipulated via its own USE_SELx, IO_SELx, and
|
||||
* LVLx registers. Logic in the read/write functions takes a register and
|
||||
* an absolute bit number and determines the proper register offset and bit
|
||||
* number in that register. For example, to read the value of GPIO bit 50
|
||||
* the code would access offset ichx_regs[2(=GPIO_LVL)][1(=50/32)],
|
||||
* bit 18 (50%32).
|
||||
*/
|
||||
enum GPIO_REG {
|
||||
GPIO_USE_SEL = 0,
|
||||
GPIO_IO_SEL,
|
||||
GPIO_LVL,
|
||||
};
|
||||
|
||||
static const u8 ichx_regs[3][3] = {
|
||||
{0x00, 0x30, 0x40}, /* USE_SEL[1-3] offsets */
|
||||
{0x04, 0x34, 0x44}, /* IO_SEL[1-3] offsets */
|
||||
{0x0c, 0x38, 0x48}, /* LVL[1-3] offsets */
|
||||
};
|
||||
|
||||
#define ICHX_WRITE(val, reg, base_res) outl(val, (reg) + (base_res)->start)
|
||||
#define ICHX_READ(reg, base_res) inl((reg) + (base_res)->start)
|
||||
|
||||
struct ichx_desc {
|
||||
/* Max GPIO pins the chipset can have */
|
||||
uint ngpio;
|
||||
|
||||
/* Whether the chipset has GPIO in GPE0_STS in the PM IO region */
|
||||
bool uses_gpe0;
|
||||
|
||||
/* USE_SEL is bogus on some chipsets, eg 3100 */
|
||||
u32 use_sel_ignore[3];
|
||||
|
||||
/* Some chipsets have quirks, let these use their own request/get */
|
||||
int (*request)(struct gpio_chip *chip, unsigned offset);
|
||||
int (*get)(struct gpio_chip *chip, unsigned offset);
|
||||
};
|
||||
|
||||
static struct {
|
||||
spinlock_t lock;
|
||||
struct platform_device *dev;
|
||||
struct gpio_chip chip;
|
||||
struct resource *gpio_base; /* GPIO IO base */
|
||||
struct resource *pm_base; /* Power Mangagment IO base */
|
||||
struct ichx_desc *desc; /* Pointer to chipset-specific description */
|
||||
u32 orig_gpio_ctrl; /* Orig CTRL value, used to restore on exit */
|
||||
} ichx_priv;
|
||||
|
||||
static int modparam_gpiobase = -1; /* dynamic */
|
||||
module_param_named(gpiobase, modparam_gpiobase, int, 0444);
|
||||
MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, "
|
||||
"which is the default.");
|
||||
|
||||
static int ichx_write_bit(int reg, unsigned nr, int val, int verify)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 data, tmp;
|
||||
int reg_nr = nr / 32;
|
||||
int bit = nr & 0x1f;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&ichx_priv.lock, flags);
|
||||
|
||||
data = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base);
|
||||
if (val)
|
||||
data |= 1 << bit;
|
||||
else
|
||||
data &= ~(1 << bit);
|
||||
ICHX_WRITE(data, ichx_regs[reg][reg_nr], ichx_priv.gpio_base);
|
||||
tmp = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base);
|
||||
if (verify && data != tmp)
|
||||
ret = -EPERM;
|
||||
|
||||
spin_unlock_irqrestore(&ichx_priv.lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ichx_read_bit(int reg, unsigned nr)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 data;
|
||||
int reg_nr = nr / 32;
|
||||
int bit = nr & 0x1f;
|
||||
|
||||
spin_lock_irqsave(&ichx_priv.lock, flags);
|
||||
|
||||
data = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base);
|
||||
|
||||
spin_unlock_irqrestore(&ichx_priv.lock, flags);
|
||||
|
||||
return data & (1 << bit) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
|
||||
{
|
||||
/*
|
||||
* Try setting pin as an input and verify it worked since many pins
|
||||
* are output-only.
|
||||
*/
|
||||
if (ichx_write_bit(GPIO_IO_SEL, nr, 1, 1))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
|
||||
int val)
|
||||
{
|
||||
/* Set GPIO output value. */
|
||||
ichx_write_bit(GPIO_LVL, nr, val, 0);
|
||||
|
||||
/*
|
||||
* Try setting pin as an output and verify it worked since many pins
|
||||
* are input-only.
|
||||
*/
|
||||
if (ichx_write_bit(GPIO_IO_SEL, nr, 0, 1))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ichx_gpio_get(struct gpio_chip *chip, unsigned nr)
|
||||
{
|
||||
return ichx_read_bit(GPIO_LVL, nr);
|
||||
}
|
||||
|
||||
static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 data;
|
||||
|
||||
/*
|
||||
* GPI 0 - 15 need to be read from the power management registers on
|
||||
* a ICH6/3100 bridge.
|
||||
*/
|
||||
if (nr < 16) {
|
||||
if (!ichx_priv.pm_base)
|
||||
return -ENXIO;
|
||||
|
||||
spin_lock_irqsave(&ichx_priv.lock, flags);
|
||||
|
||||
/* GPI 0 - 15 are latched, write 1 to clear*/
|
||||
ICHX_WRITE(1 << (16 + nr), 0, ichx_priv.pm_base);
|
||||
data = ICHX_READ(0, ichx_priv.pm_base);
|
||||
|
||||
spin_unlock_irqrestore(&ichx_priv.lock, flags);
|
||||
|
||||
return (data >> 16) & (1 << nr) ? 1 : 0;
|
||||
} else {
|
||||
return ichx_gpio_get(chip, nr);
|
||||
}
|
||||
}
|
||||
|
||||
static int ichx_gpio_request(struct gpio_chip *chip, unsigned nr)
|
||||
{
|
||||
/*
|
||||
* Note we assume the BIOS properly set a bridge's USE value. Some
|
||||
* chips (eg Intel 3100) have bogus USE values though, so first see if
|
||||
* the chipset's USE value can be trusted for this specific bit.
|
||||
* If it can't be trusted, assume that the pin can be used as a GPIO.
|
||||
*/
|
||||
if (ichx_priv.desc->use_sel_ignore[nr / 32] & (1 << (nr & 0x1f)))
|
||||
return 1;
|
||||
|
||||
return ichx_read_bit(GPIO_USE_SEL, nr) ? 0 : -ENODEV;
|
||||
}
|
||||
|
||||
static int ich6_gpio_request(struct gpio_chip *chip, unsigned nr)
|
||||
{
|
||||
/*
|
||||
* Fixups for bits 16 and 17 are necessary on the Intel ICH6/3100
|
||||
* bridge as they are controlled by USE register bits 0 and 1. See
|
||||
* "Table 704 GPIO_USE_SEL1 register" in the i3100 datasheet for
|
||||
* additional info.
|
||||
*/
|
||||
if (nr == 16 || nr == 17)
|
||||
nr -= 16;
|
||||
|
||||
return ichx_gpio_request(chip, nr);
|
||||
}
|
||||
|
||||
static void ichx_gpio_set(struct gpio_chip *chip, unsigned nr, int val)
|
||||
{
|
||||
ichx_write_bit(GPIO_LVL, nr, val, 0);
|
||||
}
|
||||
|
||||
static void __devinit ichx_gpiolib_setup(struct gpio_chip *chip)
|
||||
{
|
||||
chip->owner = THIS_MODULE;
|
||||
chip->label = DRV_NAME;
|
||||
chip->dev = &ichx_priv.dev->dev;
|
||||
|
||||
/* Allow chip-specific overrides of request()/get() */
|
||||
chip->request = ichx_priv.desc->request ?
|
||||
ichx_priv.desc->request : ichx_gpio_request;
|
||||
chip->get = ichx_priv.desc->get ?
|
||||
ichx_priv.desc->get : ichx_gpio_get;
|
||||
|
||||
chip->set = ichx_gpio_set;
|
||||
chip->direction_input = ichx_gpio_direction_input;
|
||||
chip->direction_output = ichx_gpio_direction_output;
|
||||
chip->base = modparam_gpiobase;
|
||||
chip->ngpio = ichx_priv.desc->ngpio;
|
||||
chip->can_sleep = 0;
|
||||
chip->dbg_show = NULL;
|
||||
}
|
||||
|
||||
/* ICH6-based, 631xesb-based */
|
||||
static struct ichx_desc ich6_desc = {
|
||||
/* Bridges using the ICH6 controller need fixups for GPIO 0 - 17 */
|
||||
.request = ich6_gpio_request,
|
||||
.get = ich6_gpio_get,
|
||||
|
||||
/* GPIO 0-15 are read in the GPE0_STS PM register */
|
||||
.uses_gpe0 = true,
|
||||
|
||||
.ngpio = 50,
|
||||
};
|
||||
|
||||
/* Intel 3100 */
|
||||
static struct ichx_desc i3100_desc = {
|
||||
/*
|
||||
* Bits 16,17, 20 of USE_SEL and bit 16 of USE_SEL2 always read 0 on
|
||||
* the Intel 3100. See "Table 712. GPIO Summary Table" of 3100
|
||||
* Datasheet for more info.
|
||||
*/
|
||||
.use_sel_ignore = {0x00130000, 0x00010000, 0x0},
|
||||
|
||||
/* The 3100 needs fixups for GPIO 0 - 17 */
|
||||
.request = ich6_gpio_request,
|
||||
.get = ich6_gpio_get,
|
||||
|
||||
/* GPIO 0-15 are read in the GPE0_STS PM register */
|
||||
.uses_gpe0 = true,
|
||||
|
||||
.ngpio = 50,
|
||||
};
|
||||
|
||||
/* ICH7 and ICH8-based */
|
||||
static struct ichx_desc ich7_desc = {
|
||||
.ngpio = 50,
|
||||
};
|
||||
|
||||
/* ICH9-based */
|
||||
static struct ichx_desc ich9_desc = {
|
||||
.ngpio = 61,
|
||||
};
|
||||
|
||||
/* ICH10-based - Consumer/corporate versions have different amount of GPIO */
|
||||
static struct ichx_desc ich10_cons_desc = {
|
||||
.ngpio = 61,
|
||||
};
|
||||
static struct ichx_desc ich10_corp_desc = {
|
||||
.ngpio = 72,
|
||||
};
|
||||
|
||||
/* Intel 5 series, 6 series, 3400 series, and C200 series */
|
||||
static struct ichx_desc intel5_desc = {
|
||||
.ngpio = 76,
|
||||
};
|
||||
|
||||
static int __devinit ichx_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res_base, *res_pm;
|
||||
int err;
|
||||
struct lpc_ich_info *ich_info = pdev->dev.platform_data;
|
||||
|
||||
if (!ich_info)
|
||||
return -ENODEV;
|
||||
|
||||
ichx_priv.dev = pdev;
|
||||
|
||||
switch (ich_info->gpio_version) {
|
||||
case ICH_I3100_GPIO:
|
||||
ichx_priv.desc = &i3100_desc;
|
||||
break;
|
||||
case ICH_V5_GPIO:
|
||||
ichx_priv.desc = &intel5_desc;
|
||||
break;
|
||||
case ICH_V6_GPIO:
|
||||
ichx_priv.desc = &ich6_desc;
|
||||
break;
|
||||
case ICH_V7_GPIO:
|
||||
ichx_priv.desc = &ich7_desc;
|
||||
break;
|
||||
case ICH_V9_GPIO:
|
||||
ichx_priv.desc = &ich9_desc;
|
||||
break;
|
||||
case ICH_V10CORP_GPIO:
|
||||
ichx_priv.desc = &ich10_corp_desc;
|
||||
break;
|
||||
case ICH_V10CONS_GPIO:
|
||||
ichx_priv.desc = &ich10_cons_desc;
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
res_base = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPIO);
|
||||
if (!res_base || !res_base->start || !res_base->end)
|
||||
return -ENODEV;
|
||||
|
||||
if (!request_region(res_base->start, resource_size(res_base),
|
||||
pdev->name))
|
||||
return -EBUSY;
|
||||
|
||||
ichx_priv.gpio_base = res_base;
|
||||
|
||||
/*
|
||||
* If necessary, determine the I/O address of ACPI/power management
|
||||
* registers which are needed to read the the GPE0 register for GPI pins
|
||||
* 0 - 15 on some chipsets.
|
||||
*/
|
||||
if (!ichx_priv.desc->uses_gpe0)
|
||||
goto init;
|
||||
|
||||
res_pm = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPE0);
|
||||
if (!res_pm) {
|
||||
pr_warn("ACPI BAR is unavailable, GPI 0 - 15 unavailable\n");
|
||||
goto init;
|
||||
}
|
||||
|
||||
if (!request_region(res_pm->start, resource_size(res_pm),
|
||||
pdev->name)) {
|
||||
pr_warn("ACPI BAR is busy, GPI 0 - 15 unavailable\n");
|
||||
goto init;
|
||||
}
|
||||
|
||||
ichx_priv.pm_base = res_pm;
|
||||
|
||||
init:
|
||||
ichx_gpiolib_setup(&ichx_priv.chip);
|
||||
err = gpiochip_add(&ichx_priv.chip);
|
||||
if (err) {
|
||||
pr_err("Failed to register GPIOs\n");
|
||||
goto add_err;
|
||||
}
|
||||
|
||||
pr_info("GPIO from %d to %d on %s\n", ichx_priv.chip.base,
|
||||
ichx_priv.chip.base + ichx_priv.chip.ngpio - 1, DRV_NAME);
|
||||
|
||||
return 0;
|
||||
|
||||
add_err:
|
||||
release_region(ichx_priv.gpio_base->start,
|
||||
resource_size(ichx_priv.gpio_base));
|
||||
if (ichx_priv.pm_base)
|
||||
release_region(ichx_priv.pm_base->start,
|
||||
resource_size(ichx_priv.pm_base));
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit ichx_gpio_remove(struct platform_device *pdev)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = gpiochip_remove(&ichx_priv.chip);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "%s failed, %d\n",
|
||||
"gpiochip_remove()", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
release_region(ichx_priv.gpio_base->start,
|
||||
resource_size(ichx_priv.gpio_base));
|
||||
if (ichx_priv.pm_base)
|
||||
release_region(ichx_priv.pm_base->start,
|
||||
resource_size(ichx_priv.pm_base));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ichx_gpio_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = DRV_NAME,
|
||||
},
|
||||
.probe = ichx_gpio_probe,
|
||||
.remove = __devexit_p(ichx_gpio_remove),
|
||||
};
|
||||
|
||||
module_platform_driver(ichx_gpio_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Tyser <ptyser@xes-inc.com>");
|
||||
MODULE_DESCRIPTION("GPIO interface for Intel ICH series");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:"DRV_NAME);
|
@ -232,6 +232,14 @@ static int __devinit sch_gpio_probe(struct platform_device *pdev)
|
||||
sch_gpio_resume.ngpio = 9;
|
||||
break;
|
||||
|
||||
case PCI_DEVICE_ID_INTEL_CENTERTON_ILB:
|
||||
sch_gpio_core.base = 0;
|
||||
sch_gpio_core.ngpio = 21;
|
||||
|
||||
sch_gpio_resume.base = 21;
|
||||
sch_gpio_resume.ngpio = 9;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
435
drivers/gpio/gpio-sta2x11.c
Normal file
435
drivers/gpio/gpio-sta2x11.c
Normal file
@ -0,0 +1,435 @@
|
||||
/*
|
||||
* STMicroelectronics ConneXt (STA2X11) GPIO driver
|
||||
*
|
||||
* Copyright 2012 ST Microelectronics (Alessandro Rubini)
|
||||
* Based on gpio-ml-ioh.c, Copyright 2010 OKI Semiconductors Ltd.
|
||||
* Also based on previous sta2x11 work, Copyright 2011 Wind River Systems, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/sta2x11-mfd.h>
|
||||
|
||||
struct gsta_regs {
|
||||
u32 dat; /* 0x00 */
|
||||
u32 dats;
|
||||
u32 datc;
|
||||
u32 pdis;
|
||||
u32 dir; /* 0x10 */
|
||||
u32 dirs;
|
||||
u32 dirc;
|
||||
u32 unused_1c;
|
||||
u32 afsela; /* 0x20 */
|
||||
u32 unused_24[7];
|
||||
u32 rimsc; /* 0x40 */
|
||||
u32 fimsc;
|
||||
u32 is;
|
||||
u32 ic;
|
||||
};
|
||||
|
||||
struct gsta_gpio {
|
||||
spinlock_t lock;
|
||||
struct device *dev;
|
||||
void __iomem *reg_base;
|
||||
struct gsta_regs __iomem *regs[GSTA_NR_BLOCKS];
|
||||
struct gpio_chip gpio;
|
||||
int irq_base;
|
||||
/* FIXME: save the whole config here (AF, ...) */
|
||||
unsigned irq_type[GSTA_NR_GPIO];
|
||||
};
|
||||
|
||||
static inline struct gsta_regs __iomem *__regs(struct gsta_gpio *chip, int nr)
|
||||
{
|
||||
return chip->regs[nr / GSTA_GPIO_PER_BLOCK];
|
||||
}
|
||||
|
||||
static inline u32 __bit(int nr)
|
||||
{
|
||||
return 1U << (nr % GSTA_GPIO_PER_BLOCK);
|
||||
}
|
||||
|
||||
/*
|
||||
* gpio methods
|
||||
*/
|
||||
|
||||
static void gsta_gpio_set(struct gpio_chip *gpio, unsigned nr, int val)
|
||||
{
|
||||
struct gsta_gpio *chip = container_of(gpio, struct gsta_gpio, gpio);
|
||||
struct gsta_regs __iomem *regs = __regs(chip, nr);
|
||||
u32 bit = __bit(nr);
|
||||
|
||||
if (val)
|
||||
writel(bit, ®s->dats);
|
||||
else
|
||||
writel(bit, ®s->datc);
|
||||
}
|
||||
|
||||
static int gsta_gpio_get(struct gpio_chip *gpio, unsigned nr)
|
||||
{
|
||||
struct gsta_gpio *chip = container_of(gpio, struct gsta_gpio, gpio);
|
||||
struct gsta_regs __iomem *regs = __regs(chip, nr);
|
||||
u32 bit = __bit(nr);
|
||||
|
||||
return readl(®s->dat) & bit;
|
||||
}
|
||||
|
||||
static int gsta_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
|
||||
int val)
|
||||
{
|
||||
struct gsta_gpio *chip = container_of(gpio, struct gsta_gpio, gpio);
|
||||
struct gsta_regs __iomem *regs = __regs(chip, nr);
|
||||
u32 bit = __bit(nr);
|
||||
|
||||
writel(bit, ®s->dirs);
|
||||
/* Data register after direction, otherwise pullup/down is selected */
|
||||
if (val)
|
||||
writel(bit, ®s->dats);
|
||||
else
|
||||
writel(bit, ®s->datc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gsta_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
|
||||
{
|
||||
struct gsta_gpio *chip = container_of(gpio, struct gsta_gpio, gpio);
|
||||
struct gsta_regs __iomem *regs = __regs(chip, nr);
|
||||
u32 bit = __bit(nr);
|
||||
|
||||
writel(bit, ®s->dirc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gsta_gpio_to_irq(struct gpio_chip *gpio, unsigned offset)
|
||||
{
|
||||
struct gsta_gpio *chip = container_of(gpio, struct gsta_gpio, gpio);
|
||||
return chip->irq_base + offset;
|
||||
}
|
||||
|
||||
static void gsta_gpio_setup(struct gsta_gpio *chip) /* called from probe */
|
||||
{
|
||||
struct gpio_chip *gpio = &chip->gpio;
|
||||
|
||||
/*
|
||||
* ARCH_NR_GPIOS is currently 256 and dynamic allocation starts
|
||||
* from the end. However, for compatibility, we need the first
|
||||
* ConneXt device to start from gpio 0: it's the main chipset
|
||||
* on most boards so documents and drivers assume gpio0..gpio127
|
||||
*/
|
||||
static int gpio_base;
|
||||
|
||||
gpio->label = dev_name(chip->dev);
|
||||
gpio->owner = THIS_MODULE;
|
||||
gpio->direction_input = gsta_gpio_direction_input;
|
||||
gpio->get = gsta_gpio_get;
|
||||
gpio->direction_output = gsta_gpio_direction_output;
|
||||
gpio->set = gsta_gpio_set;
|
||||
gpio->dbg_show = NULL;
|
||||
gpio->base = gpio_base;
|
||||
gpio->ngpio = GSTA_NR_GPIO;
|
||||
gpio->can_sleep = 0;
|
||||
gpio->to_irq = gsta_gpio_to_irq;
|
||||
|
||||
/*
|
||||
* After the first device, turn to dynamic gpio numbers.
|
||||
* For example, with ARCH_NR_GPIOS = 256 we can fit two cards
|
||||
*/
|
||||
if (!gpio_base)
|
||||
gpio_base = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Special method: alternate functions and pullup/pulldown. This is only
|
||||
* invoked on startup to configure gpio's according to platform data.
|
||||
* FIXME : this functionality shall be managed (and exported to other drivers)
|
||||
* via the pin control subsystem.
|
||||
*/
|
||||
static void gsta_set_config(struct gsta_gpio *chip, int nr, unsigned cfg)
|
||||
{
|
||||
struct gsta_regs __iomem *regs = __regs(chip, nr);
|
||||
unsigned long flags;
|
||||
u32 bit = __bit(nr);
|
||||
u32 val;
|
||||
int err = 0;
|
||||
|
||||
pr_info("%s: %p %i %i\n", __func__, chip, nr, cfg);
|
||||
|
||||
if (cfg == PINMUX_TYPE_NONE)
|
||||
return;
|
||||
|
||||
/* Alternate function or not? */
|
||||
spin_lock_irqsave(&chip->lock, flags);
|
||||
val = readl(®s->afsela);
|
||||
if (cfg == PINMUX_TYPE_FUNCTION)
|
||||
val |= bit;
|
||||
else
|
||||
val &= ~bit;
|
||||
writel(val | bit, ®s->afsela);
|
||||
if (cfg == PINMUX_TYPE_FUNCTION) {
|
||||
spin_unlock_irqrestore(&chip->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
/* not alternate function: set details */
|
||||
switch (cfg) {
|
||||
case PINMUX_TYPE_OUTPUT_LOW:
|
||||
writel(bit, ®s->dirs);
|
||||
writel(bit, ®s->datc);
|
||||
break;
|
||||
case PINMUX_TYPE_OUTPUT_HIGH:
|
||||
writel(bit, ®s->dirs);
|
||||
writel(bit, ®s->dats);
|
||||
break;
|
||||
case PINMUX_TYPE_INPUT:
|
||||
writel(bit, ®s->dirc);
|
||||
val = readl(®s->pdis) | bit;
|
||||
writel(val, ®s->pdis);
|
||||
break;
|
||||
case PINMUX_TYPE_INPUT_PULLUP:
|
||||
writel(bit, ®s->dirc);
|
||||
val = readl(®s->pdis) & ~bit;
|
||||
writel(val, ®s->pdis);
|
||||
writel(bit, ®s->dats);
|
||||
break;
|
||||
case PINMUX_TYPE_INPUT_PULLDOWN:
|
||||
writel(bit, ®s->dirc);
|
||||
val = readl(®s->pdis) & ~bit;
|
||||
writel(val, ®s->pdis);
|
||||
writel(bit, ®s->datc);
|
||||
break;
|
||||
default:
|
||||
err = 1;
|
||||
}
|
||||
spin_unlock_irqrestore(&chip->lock, flags);
|
||||
if (err)
|
||||
pr_err("%s: chip %p, pin %i, cfg %i is invalid\n",
|
||||
__func__, chip, nr, cfg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Irq methods
|
||||
*/
|
||||
|
||||
static void gsta_irq_disable(struct irq_data *data)
|
||||
{
|
||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
|
||||
struct gsta_gpio *chip = gc->private;
|
||||
int nr = data->irq - chip->irq_base;
|
||||
struct gsta_regs __iomem *regs = __regs(chip, nr);
|
||||
u32 bit = __bit(nr);
|
||||
u32 val;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&chip->lock, flags);
|
||||
if (chip->irq_type[nr] & IRQ_TYPE_EDGE_RISING) {
|
||||
val = readl(®s->rimsc) & ~bit;
|
||||
writel(val, ®s->rimsc);
|
||||
}
|
||||
if (chip->irq_type[nr] & IRQ_TYPE_EDGE_FALLING) {
|
||||
val = readl(®s->fimsc) & ~bit;
|
||||
writel(val, ®s->fimsc);
|
||||
}
|
||||
spin_unlock_irqrestore(&chip->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
static void gsta_irq_enable(struct irq_data *data)
|
||||
{
|
||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
|
||||
struct gsta_gpio *chip = gc->private;
|
||||
int nr = data->irq - chip->irq_base;
|
||||
struct gsta_regs __iomem *regs = __regs(chip, nr);
|
||||
u32 bit = __bit(nr);
|
||||
u32 val;
|
||||
int type;
|
||||
unsigned long flags;
|
||||
|
||||
type = chip->irq_type[nr];
|
||||
|
||||
spin_lock_irqsave(&chip->lock, flags);
|
||||
val = readl(®s->rimsc);
|
||||
if (type & IRQ_TYPE_EDGE_RISING)
|
||||
writel(val | bit, ®s->rimsc);
|
||||
else
|
||||
writel(val & ~bit, ®s->rimsc);
|
||||
val = readl(®s->rimsc);
|
||||
if (type & IRQ_TYPE_EDGE_FALLING)
|
||||
writel(val | bit, ®s->fimsc);
|
||||
else
|
||||
writel(val & ~bit, ®s->fimsc);
|
||||
spin_unlock_irqrestore(&chip->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
static int gsta_irq_type(struct irq_data *d, unsigned int type)
|
||||
{
|
||||
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
||||
struct gsta_gpio *chip = gc->private;
|
||||
int nr = d->irq - chip->irq_base;
|
||||
|
||||
/* We only support edge interrupts */
|
||||
if (!(type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))) {
|
||||
pr_debug("%s: unsupported type 0x%x\n", __func__, type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chip->irq_type[nr] = type; /* used for enable/disable */
|
||||
|
||||
gsta_irq_enable(d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t gsta_gpio_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct gsta_gpio *chip = dev_id;
|
||||
struct gsta_regs __iomem *regs;
|
||||
u32 is;
|
||||
int i, nr, base;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
for (i = 0; i < GSTA_NR_BLOCKS; i++) {
|
||||
regs = chip->regs[i];
|
||||
base = chip->irq_base + i * GSTA_GPIO_PER_BLOCK;
|
||||
while ((is = readl(®s->is))) {
|
||||
nr = __ffs(is);
|
||||
irq = base + nr;
|
||||
generic_handle_irq(irq);
|
||||
writel(1 << nr, ®s->ic);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __devinit void gsta_alloc_irq_chip(struct gsta_gpio *chip)
|
||||
{
|
||||
struct irq_chip_generic *gc;
|
||||
struct irq_chip_type *ct;
|
||||
|
||||
gc = irq_alloc_generic_chip(KBUILD_MODNAME, 1, chip->irq_base,
|
||||
chip->reg_base, handle_simple_irq);
|
||||
gc->private = chip;
|
||||
ct = gc->chip_types;
|
||||
|
||||
ct->chip.irq_set_type = gsta_irq_type;
|
||||
ct->chip.irq_disable = gsta_irq_disable;
|
||||
ct->chip.irq_enable = gsta_irq_enable;
|
||||
|
||||
/* FIXME: this makes at most 32 interrupts. Request 0 by now */
|
||||
irq_setup_generic_chip(gc, 0 /* IRQ_MSK(GSTA_GPIO_PER_BLOCK) */, 0,
|
||||
IRQ_NOREQUEST | IRQ_NOPROBE, 0);
|
||||
|
||||
/* Set up all all 128 interrupts: code from setup_generic_chip */
|
||||
{
|
||||
struct irq_chip_type *ct = gc->chip_types;
|
||||
int i, j;
|
||||
for (j = 0; j < GSTA_NR_GPIO; j++) {
|
||||
i = chip->irq_base + j;
|
||||
irq_set_chip_and_handler(i, &ct->chip, ct->handler);
|
||||
irq_set_chip_data(i, gc);
|
||||
irq_modify_status(i, IRQ_NOREQUEST | IRQ_NOPROBE, 0);
|
||||
}
|
||||
gc->irq_cnt = i - gc->irq_base;
|
||||
}
|
||||
}
|
||||
|
||||
/* The platform device used here is instantiated by the MFD device */
|
||||
static int __devinit gsta_probe(struct platform_device *dev)
|
||||
{
|
||||
int i, err;
|
||||
struct pci_dev *pdev;
|
||||
struct sta2x11_gpio_pdata *gpio_pdata;
|
||||
struct gsta_gpio *chip;
|
||||
struct resource *res;
|
||||
|
||||
pdev = *(struct pci_dev **)(dev->dev.platform_data);
|
||||
gpio_pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
if (gpio_pdata == NULL)
|
||||
dev_err(&dev->dev, "no gpio config\n");
|
||||
pr_debug("gpio config: %p\n", gpio_pdata);
|
||||
|
||||
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
||||
|
||||
chip = devm_kzalloc(&dev->dev, sizeof(*chip), GFP_KERNEL);
|
||||
chip->dev = &dev->dev;
|
||||
chip->reg_base = devm_request_and_ioremap(&dev->dev, res);
|
||||
|
||||
for (i = 0; i < GSTA_NR_BLOCKS; i++) {
|
||||
chip->regs[i] = chip->reg_base + i * 4096;
|
||||
/* disable all irqs */
|
||||
writel(0, &chip->regs[i]->rimsc);
|
||||
writel(0, &chip->regs[i]->fimsc);
|
||||
writel(~0, &chip->regs[i]->ic);
|
||||
}
|
||||
spin_lock_init(&chip->lock);
|
||||
gsta_gpio_setup(chip);
|
||||
for (i = 0; i < GSTA_NR_GPIO; i++)
|
||||
gsta_set_config(chip, i, gpio_pdata->pinconfig[i]);
|
||||
|
||||
/* 384 was used in previous code: be compatible for other drivers */
|
||||
err = irq_alloc_descs(-1, 384, GSTA_NR_GPIO, NUMA_NO_NODE);
|
||||
if (err < 0) {
|
||||
dev_warn(&dev->dev, "sta2x11 gpio: Can't get irq base (%i)\n",
|
||||
-err);
|
||||
return err;
|
||||
}
|
||||
chip->irq_base = err;
|
||||
gsta_alloc_irq_chip(chip);
|
||||
|
||||
err = request_irq(pdev->irq, gsta_gpio_handler,
|
||||
IRQF_SHARED, KBUILD_MODNAME, chip);
|
||||
if (err < 0) {
|
||||
dev_err(&dev->dev, "sta2x11 gpio: Can't request irq (%i)\n",
|
||||
-err);
|
||||
goto err_free_descs;
|
||||
}
|
||||
|
||||
err = gpiochip_add(&chip->gpio);
|
||||
if (err < 0) {
|
||||
dev_err(&dev->dev, "sta2x11 gpio: Can't register (%i)\n",
|
||||
-err);
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
platform_set_drvdata(dev, chip);
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(pdev->irq, chip);
|
||||
err_free_descs:
|
||||
irq_free_descs(chip->irq_base, GSTA_NR_GPIO);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct platform_driver sta2x11_gpio_platform_driver = {
|
||||
.driver = {
|
||||
.name = "sta2x11-gpio",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = gsta_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(sta2x11_gpio_platform_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("sta2x11_gpio GPIO driver");
|
@ -18,14 +18,27 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/tps65910.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
struct tps65910_gpio {
|
||||
struct gpio_chip gpio_chip;
|
||||
struct tps65910 *tps65910;
|
||||
};
|
||||
|
||||
static inline struct tps65910_gpio *to_tps65910_gpio(struct gpio_chip *chip)
|
||||
{
|
||||
return container_of(chip, struct tps65910_gpio, gpio_chip);
|
||||
}
|
||||
|
||||
static int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset)
|
||||
{
|
||||
struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio);
|
||||
uint8_t val;
|
||||
struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc);
|
||||
struct tps65910 *tps65910 = tps65910_gpio->tps65910;
|
||||
unsigned int val;
|
||||
|
||||
tps65910->read(tps65910, TPS65910_GPIO0 + offset, 1, &val);
|
||||
tps65910_reg_read(tps65910, TPS65910_GPIO0 + offset, &val);
|
||||
|
||||
if (val & GPIO_STS_MASK)
|
||||
return 1;
|
||||
@ -36,83 +49,170 @@ static int tps65910_gpio_get(struct gpio_chip *gc, unsigned offset)
|
||||
static void tps65910_gpio_set(struct gpio_chip *gc, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio);
|
||||
struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc);
|
||||
struct tps65910 *tps65910 = tps65910_gpio->tps65910;
|
||||
|
||||
if (value)
|
||||
tps65910_set_bits(tps65910, TPS65910_GPIO0 + offset,
|
||||
tps65910_reg_set_bits(tps65910, TPS65910_GPIO0 + offset,
|
||||
GPIO_SET_MASK);
|
||||
else
|
||||
tps65910_clear_bits(tps65910, TPS65910_GPIO0 + offset,
|
||||
tps65910_reg_clear_bits(tps65910, TPS65910_GPIO0 + offset,
|
||||
GPIO_SET_MASK);
|
||||
}
|
||||
|
||||
static int tps65910_gpio_output(struct gpio_chip *gc, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio);
|
||||
struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc);
|
||||
struct tps65910 *tps65910 = tps65910_gpio->tps65910;
|
||||
|
||||
/* Set the initial value */
|
||||
tps65910_gpio_set(gc, offset, value);
|
||||
|
||||
return tps65910_set_bits(tps65910, TPS65910_GPIO0 + offset,
|
||||
return tps65910_reg_set_bits(tps65910, TPS65910_GPIO0 + offset,
|
||||
GPIO_CFG_MASK);
|
||||
}
|
||||
|
||||
static int tps65910_gpio_input(struct gpio_chip *gc, unsigned offset)
|
||||
{
|
||||
struct tps65910 *tps65910 = container_of(gc, struct tps65910, gpio);
|
||||
struct tps65910_gpio *tps65910_gpio = to_tps65910_gpio(gc);
|
||||
struct tps65910 *tps65910 = tps65910_gpio->tps65910;
|
||||
|
||||
return tps65910_clear_bits(tps65910, TPS65910_GPIO0 + offset,
|
||||
return tps65910_reg_clear_bits(tps65910, TPS65910_GPIO0 + offset,
|
||||
GPIO_CFG_MASK);
|
||||
}
|
||||
|
||||
void tps65910_gpio_init(struct tps65910 *tps65910, int gpio_base)
|
||||
#ifdef CONFIG_OF
|
||||
static struct tps65910_board *tps65910_parse_dt_for_gpio(struct device *dev,
|
||||
struct tps65910 *tps65910, int chip_ngpio)
|
||||
{
|
||||
struct tps65910_board *tps65910_board = tps65910->of_plat_data;
|
||||
unsigned int prop_array[TPS6591X_MAX_NUM_GPIO];
|
||||
int ngpio = min(chip_ngpio, TPS6591X_MAX_NUM_GPIO);
|
||||
int ret;
|
||||
struct tps65910_board *board_data;
|
||||
int idx;
|
||||
|
||||
if (!gpio_base)
|
||||
return;
|
||||
tps65910_board->gpio_base = -1;
|
||||
ret = of_property_read_u32_array(tps65910->dev->of_node,
|
||||
"ti,en-gpio-sleep", prop_array, ngpio);
|
||||
if (ret < 0) {
|
||||
dev_dbg(dev, "ti,en-gpio-sleep not specified\n");
|
||||
return tps65910_board;
|
||||
}
|
||||
|
||||
tps65910->gpio.owner = THIS_MODULE;
|
||||
tps65910->gpio.label = tps65910->i2c_client->name;
|
||||
tps65910->gpio.dev = tps65910->dev;
|
||||
tps65910->gpio.base = gpio_base;
|
||||
for (idx = 0; idx < ngpio; idx++)
|
||||
tps65910_board->en_gpio_sleep[idx] = (prop_array[idx] != 0);
|
||||
|
||||
return tps65910_board;
|
||||
}
|
||||
#else
|
||||
static struct tps65910_board *tps65910_parse_dt_for_gpio(struct device *dev,
|
||||
struct tps65910 *tps65910, int chip_ngpio)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __devinit tps65910_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct tps65910_board *pdata = dev_get_platdata(tps65910->dev);
|
||||
struct tps65910_gpio *tps65910_gpio;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
tps65910_gpio = devm_kzalloc(&pdev->dev,
|
||||
sizeof(*tps65910_gpio), GFP_KERNEL);
|
||||
if (!tps65910_gpio) {
|
||||
dev_err(&pdev->dev, "Could not allocate tps65910_gpio\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tps65910_gpio->tps65910 = tps65910;
|
||||
|
||||
tps65910_gpio->gpio_chip.owner = THIS_MODULE;
|
||||
tps65910_gpio->gpio_chip.label = tps65910->i2c_client->name;
|
||||
|
||||
switch(tps65910_chip_id(tps65910)) {
|
||||
case TPS65910:
|
||||
tps65910->gpio.ngpio = TPS65910_NUM_GPIO;
|
||||
tps65910_gpio->gpio_chip.ngpio = TPS65910_NUM_GPIO;
|
||||
break;
|
||||
case TPS65911:
|
||||
tps65910->gpio.ngpio = TPS65911_NUM_GPIO;
|
||||
tps65910_gpio->gpio_chip.ngpio = TPS65911_NUM_GPIO;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
tps65910->gpio.can_sleep = 1;
|
||||
tps65910_gpio->gpio_chip.can_sleep = 1;
|
||||
tps65910_gpio->gpio_chip.direction_input = tps65910_gpio_input;
|
||||
tps65910_gpio->gpio_chip.direction_output = tps65910_gpio_output;
|
||||
tps65910_gpio->gpio_chip.set = tps65910_gpio_set;
|
||||
tps65910_gpio->gpio_chip.get = tps65910_gpio_get;
|
||||
tps65910_gpio->gpio_chip.dev = &pdev->dev;
|
||||
if (pdata && pdata->gpio_base)
|
||||
tps65910_gpio->gpio_chip.base = pdata->gpio_base;
|
||||
else
|
||||
tps65910_gpio->gpio_chip.base = -1;
|
||||
|
||||
tps65910->gpio.direction_input = tps65910_gpio_input;
|
||||
tps65910->gpio.direction_output = tps65910_gpio_output;
|
||||
tps65910->gpio.set = tps65910_gpio_set;
|
||||
tps65910->gpio.get = tps65910_gpio_get;
|
||||
if (!pdata && tps65910->dev->of_node)
|
||||
pdata = tps65910_parse_dt_for_gpio(&pdev->dev, tps65910,
|
||||
tps65910_gpio->gpio_chip.ngpio);
|
||||
|
||||
/* Configure sleep control for gpios */
|
||||
board_data = dev_get_platdata(tps65910->dev);
|
||||
if (board_data) {
|
||||
int i;
|
||||
for (i = 0; i < tps65910->gpio.ngpio; ++i) {
|
||||
if (board_data->en_gpio_sleep[i]) {
|
||||
ret = tps65910_set_bits(tps65910,
|
||||
TPS65910_GPIO0 + i, GPIO_SLEEP_MASK);
|
||||
if (ret < 0)
|
||||
dev_warn(tps65910->dev,
|
||||
"GPIO Sleep setting failed\n");
|
||||
}
|
||||
}
|
||||
if (!pdata)
|
||||
goto skip_init;
|
||||
|
||||
/* Configure sleep control for gpios if provided */
|
||||
for (i = 0; i < tps65910_gpio->gpio_chip.ngpio; ++i) {
|
||||
if (!pdata->en_gpio_sleep[i])
|
||||
continue;
|
||||
|
||||
ret = tps65910_reg_set_bits(tps65910,
|
||||
TPS65910_GPIO0 + i, GPIO_SLEEP_MASK);
|
||||
if (ret < 0)
|
||||
dev_warn(tps65910->dev,
|
||||
"GPIO Sleep setting failed with err %d\n", ret);
|
||||
}
|
||||
|
||||
ret = gpiochip_add(&tps65910->gpio);
|
||||
skip_init:
|
||||
ret = gpiochip_add(&tps65910_gpio->gpio_chip);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
dev_warn(tps65910->dev, "GPIO registration failed: %d\n", ret);
|
||||
platform_set_drvdata(pdev, tps65910_gpio);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit tps65910_gpio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tps65910_gpio *tps65910_gpio = platform_get_drvdata(pdev);
|
||||
|
||||
return gpiochip_remove(&tps65910_gpio->gpio_chip);
|
||||
}
|
||||
|
||||
static struct platform_driver tps65910_gpio_driver = {
|
||||
.driver.name = "tps65910-gpio",
|
||||
.driver.owner = THIS_MODULE,
|
||||
.probe = tps65910_gpio_probe,
|
||||
.remove = __devexit_p(tps65910_gpio_remove),
|
||||
};
|
||||
|
||||
static int __init tps65910_gpio_init(void)
|
||||
{
|
||||
return platform_driver_register(&tps65910_gpio_driver);
|
||||
}
|
||||
subsys_initcall(tps65910_gpio_init);
|
||||
|
||||
static void __exit tps65910_gpio_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&tps65910_gpio_driver);
|
||||
}
|
||||
module_exit(tps65910_gpio_exit);
|
||||
|
||||
MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
|
||||
MODULE_AUTHOR("Jorge Eduardo Candelaria jedu@slimlogic.co.uk>");
|
||||
MODULE_DESCRIPTION("GPIO interface for TPS65910/TPS6511 PMICs");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:tps65910-gpio");
|
||||
|
@ -102,10 +102,8 @@ static int wm831x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||
struct wm831x_gpio *wm831x_gpio = to_wm831x_gpio(chip);
|
||||
struct wm831x *wm831x = wm831x_gpio->wm831x;
|
||||
|
||||
if (!wm831x->irq_base)
|
||||
return -EINVAL;
|
||||
|
||||
return wm831x->irq_base + WM831X_IRQ_GPIO_1 + offset;
|
||||
return irq_create_mapping(wm831x->irq_domain,
|
||||
WM831X_IRQ_GPIO_1 + offset);
|
||||
}
|
||||
|
||||
static int wm831x_gpio_set_debounce(struct gpio_chip *chip, unsigned offset,
|
||||
|
@ -73,7 +73,7 @@ static int __devinit wm831x_on_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
|
||||
struct wm831x_on *wm831x_on;
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
int irq = wm831x_irq(wm831x, platform_get_irq(pdev, 0));
|
||||
int ret;
|
||||
|
||||
wm831x_on = kzalloc(sizeof(struct wm831x_on), GFP_KERNEL);
|
||||
|
@ -260,15 +260,16 @@ static __devinit int wm831x_ts_probe(struct platform_device *pdev)
|
||||
* If we have a direct IRQ use it, otherwise use the interrupt
|
||||
* from the WM831x IRQ controller.
|
||||
*/
|
||||
wm831x_ts->data_irq = wm831x_irq(wm831x,
|
||||
platform_get_irq_byname(pdev,
|
||||
"TCHDATA"));
|
||||
if (pdata && pdata->data_irq)
|
||||
wm831x_ts->data_irq = pdata->data_irq;
|
||||
else
|
||||
wm831x_ts->data_irq = platform_get_irq_byname(pdev, "TCHDATA");
|
||||
|
||||
wm831x_ts->pd_irq = wm831x_irq(wm831x,
|
||||
platform_get_irq_byname(pdev, "TCHPD"));
|
||||
if (pdata && pdata->pd_irq)
|
||||
wm831x_ts->pd_irq = pdata->pd_irq;
|
||||
else
|
||||
wm831x_ts->pd_irq = platform_get_irq_byname(pdev, "TCHPD");
|
||||
|
||||
if (pdata)
|
||||
wm831x_ts->pressure = pdata->pressure;
|
||||
|
@ -106,6 +106,19 @@ config UCB1400_CORE
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ucb1400_core.
|
||||
|
||||
config MFD_LM3533
|
||||
tristate "LM3533 Lighting Power chip"
|
||||
depends on I2C
|
||||
select MFD_CORE
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say yes here to enable support for National Semiconductor / TI
|
||||
LM3533 Lighting Power chips.
|
||||
|
||||
This driver provides common support for accessing the device;
|
||||
additional drivers must be enabled in order to use the LED,
|
||||
backlight or ambient-light-sensor functionality of the device.
|
||||
|
||||
config TPS6105X
|
||||
tristate "TPS61050/61052 Boost Converters"
|
||||
depends on I2C
|
||||
@ -177,8 +190,8 @@ config MFD_TPS65910
|
||||
bool "TPS65910 Power Management chip"
|
||||
depends on I2C=y && GPIOLIB
|
||||
select MFD_CORE
|
||||
select GPIO_TPS65910
|
||||
select REGMAP_I2C
|
||||
select IRQ_DOMAIN
|
||||
help
|
||||
if you say yes here you get support for the TPS65910 series of
|
||||
Power Management chips.
|
||||
@ -409,6 +422,19 @@ config PMIC_ADP5520
|
||||
individual components like LCD backlight, LEDs, GPIOs and Kepad
|
||||
under the corresponding menus.
|
||||
|
||||
config MFD_MAX77693
|
||||
bool "Maxim Semiconductor MAX77693 PMIC Support"
|
||||
depends on I2C=y && GENERIC_HARDIRQS
|
||||
select MFD_CORE
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say yes here to support for Maxim Semiconductor MAX77693.
|
||||
This is a companion Power Management IC with Flash, Haptic, Charger,
|
||||
and MUIC(Micro USB Interface Controller) controls on chip.
|
||||
This driver provides common support for accessing the device;
|
||||
additional drivers must be enabled in order to use the functionality
|
||||
of the device.
|
||||
|
||||
config MFD_MAX8925
|
||||
bool "Maxim Semiconductor MAX8925 PMIC Support"
|
||||
depends on I2C=y && GENERIC_HARDIRQS
|
||||
@ -454,9 +480,9 @@ config MFD_S5M_CORE
|
||||
of the device
|
||||
|
||||
config MFD_WM8400
|
||||
tristate "Support Wolfson Microelectronics WM8400"
|
||||
bool "Support Wolfson Microelectronics WM8400"
|
||||
select MFD_CORE
|
||||
depends on I2C
|
||||
depends on I2C=y
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Support for the Wolfson Microelecronics WM8400 PMIC and audio
|
||||
@ -473,6 +499,7 @@ config MFD_WM831X_I2C
|
||||
select MFD_CORE
|
||||
select MFD_WM831X
|
||||
select REGMAP_I2C
|
||||
select IRQ_DOMAIN
|
||||
depends on I2C=y && GENERIC_HARDIRQS
|
||||
help
|
||||
Support for the Wolfson Microelecronics WM831x and WM832x PMICs
|
||||
@ -485,6 +512,7 @@ config MFD_WM831X_SPI
|
||||
select MFD_CORE
|
||||
select MFD_WM831X
|
||||
select REGMAP_SPI
|
||||
select IRQ_DOMAIN
|
||||
depends on SPI_MASTER && GENERIC_HARDIRQS
|
||||
help
|
||||
Support for the Wolfson Microelecronics WM831x and WM832x PMICs
|
||||
@ -597,17 +625,32 @@ config MFD_MC13783
|
||||
tristate
|
||||
|
||||
config MFD_MC13XXX
|
||||
tristate "Support Freescale MC13783 and MC13892"
|
||||
depends on SPI_MASTER
|
||||
tristate
|
||||
depends on SPI_MASTER || I2C
|
||||
select MFD_CORE
|
||||
select MFD_MC13783
|
||||
help
|
||||
Support for the Freescale (Atlas) PMIC and audio CODECs
|
||||
MC13783 and MC13892.
|
||||
This driver provides common support for accessing the device,
|
||||
Enable support for the Freescale MC13783 and MC13892 PMICs.
|
||||
This driver provides common support for accessing the device,
|
||||
additional drivers must be enabled in order to use the
|
||||
functionality of the device.
|
||||
|
||||
config MFD_MC13XXX_SPI
|
||||
tristate "Freescale MC13783 and MC13892 SPI interface"
|
||||
depends on SPI_MASTER
|
||||
select REGMAP_SPI
|
||||
select MFD_MC13XXX
|
||||
help
|
||||
Select this if your MC13xxx is connected via an SPI bus.
|
||||
|
||||
config MFD_MC13XXX_I2C
|
||||
tristate "Freescale MC13892 I2C interface"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
select MFD_MC13XXX
|
||||
help
|
||||
Select this if your MC13xxx is connected via an I2C bus.
|
||||
|
||||
config ABX500_CORE
|
||||
bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions"
|
||||
default y if ARCH_U300 || ARCH_U8500
|
||||
@ -651,7 +694,7 @@ config EZX_PCAP
|
||||
|
||||
config AB8500_CORE
|
||||
bool "ST-Ericsson AB8500 Mixed Signal Power Management chip"
|
||||
depends on GENERIC_HARDIRQS && ABX500_CORE
|
||||
depends on GENERIC_HARDIRQS && ABX500_CORE && MFD_DB8500_PRCMU
|
||||
select MFD_CORE
|
||||
help
|
||||
Select this option to enable access to AB8500 power management
|
||||
@ -722,6 +765,16 @@ config LPC_SCH
|
||||
LPC bridge function of the Intel SCH provides support for
|
||||
System Management Bus and General Purpose I/O.
|
||||
|
||||
config LPC_ICH
|
||||
tristate "Intel ICH LPC"
|
||||
depends on PCI
|
||||
select MFD_CORE
|
||||
help
|
||||
The LPC bridge function of the Intel ICH provides support for
|
||||
many functional units. This driver provides needed support for
|
||||
other drivers to control these functions, currently GPIO and
|
||||
watchdog.
|
||||
|
||||
config MFD_RDC321X
|
||||
tristate "Support for RDC-R321x southbridge"
|
||||
select MFD_CORE
|
||||
@ -854,6 +907,11 @@ config MFD_RC5T583
|
||||
Additional drivers must be enabled in order to use the
|
||||
different functionality of the device.
|
||||
|
||||
config MFD_STA2X11
|
||||
bool "STA2X11 multi function device support"
|
||||
depends on STA2X11
|
||||
select MFD_CORE
|
||||
|
||||
config MFD_ANATOP
|
||||
bool "Support for Freescale i.MX on-chip ANATOP controller"
|
||||
depends on SOC_IMX6Q
|
||||
|
@ -15,6 +15,7 @@ obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o
|
||||
obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o
|
||||
obj-$(CONFIG_MFD_TI_SSP) += ti-ssp.o
|
||||
|
||||
obj-$(CONFIG_MFD_STA2X11) += sta2x11-mfd.o
|
||||
obj-$(CONFIG_MFD_STMPE) += stmpe.o
|
||||
obj-$(CONFIG_STMPE_I2C) += stmpe-i2c.o
|
||||
obj-$(CONFIG_STMPE_SPI) += stmpe-spi.o
|
||||
@ -54,6 +55,8 @@ obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o
|
||||
obj-$(CONFIG_TWL6040_CORE) += twl6040-core.o twl6040-irq.o
|
||||
|
||||
obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
|
||||
obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o
|
||||
obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o
|
||||
|
||||
obj-$(CONFIG_MFD_CORE) += mfd-core.o
|
||||
|
||||
@ -75,6 +78,7 @@ obj-$(CONFIG_PMIC_DA9052) += da9052-core.o
|
||||
obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o
|
||||
obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o
|
||||
|
||||
obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o
|
||||
max8925-objs := max8925-core.o max8925-i2c.o
|
||||
obj-$(CONFIG_MFD_MAX8925) += max8925.o
|
||||
obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o
|
||||
@ -87,15 +91,15 @@ obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o
|
||||
obj-$(CONFIG_ABX500_CORE) += abx500-core.o
|
||||
obj-$(CONFIG_AB3100_CORE) += ab3100-core.o
|
||||
obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
|
||||
obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o
|
||||
obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o
|
||||
obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
|
||||
obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o
|
||||
# ab8500-i2c need to come after db8500-prcmu (which provides the channel)
|
||||
obj-$(CONFIG_AB8500_I2C_CORE) += ab8500-i2c.o
|
||||
# ab8500-core need to come after db8500-prcmu (which provides the channel)
|
||||
obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o
|
||||
obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o
|
||||
obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
|
||||
obj-$(CONFIG_LPC_SCH) += lpc_sch.o
|
||||
obj-$(CONFIG_LPC_ICH) += lpc_ich.o
|
||||
obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o
|
||||
obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o
|
||||
obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o
|
||||
|
@ -18,7 +18,10 @@
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/abx500.h>
|
||||
#include <linux/mfd/abx500/ab8500.h>
|
||||
#include <linux/mfd/dbx500-prcmu.h>
|
||||
#include <linux/regulator/ab8500.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
/*
|
||||
* Interrupt register offsets
|
||||
@ -91,12 +94,24 @@
|
||||
#define AB8500_IT_MASK23_REG 0x56
|
||||
#define AB8500_IT_MASK24_REG 0x57
|
||||
|
||||
/*
|
||||
* latch hierarchy registers
|
||||
*/
|
||||
#define AB8500_IT_LATCHHIER1_REG 0x60
|
||||
#define AB8500_IT_LATCHHIER2_REG 0x61
|
||||
#define AB8500_IT_LATCHHIER3_REG 0x62
|
||||
|
||||
#define AB8500_IT_LATCHHIER_NUM 3
|
||||
|
||||
#define AB8500_REV_REG 0x80
|
||||
#define AB8500_IC_NAME_REG 0x82
|
||||
#define AB8500_SWITCH_OFF_STATUS 0x00
|
||||
|
||||
#define AB8500_TURN_ON_STATUS 0x00
|
||||
|
||||
static bool no_bm; /* No battery management */
|
||||
module_param(no_bm, bool, S_IRUGO);
|
||||
|
||||
#define AB9540_MODEM_CTRL2_REG 0x23
|
||||
#define AB9540_MODEM_CTRL2_SWDBBRSTN_BIT BIT(2)
|
||||
|
||||
@ -125,6 +140,41 @@ static const char ab8500_version_str[][7] = {
|
||||
[AB8500_VERSION_AB8540] = "AB8540",
|
||||
};
|
||||
|
||||
static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = prcmu_abb_write((u8)(addr >> 8), (u8)(addr & 0xFF), &data, 1);
|
||||
if (ret < 0)
|
||||
dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ab8500_i2c_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask,
|
||||
u8 data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = prcmu_abb_write_masked((u8)(addr >> 8), (u8)(addr & 0xFF), &data,
|
||||
&mask, 1);
|
||||
if (ret < 0)
|
||||
dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr)
|
||||
{
|
||||
int ret;
|
||||
u8 data;
|
||||
|
||||
ret = prcmu_abb_read((u8)(addr >> 8), (u8)(addr & 0xFF), &data, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
return (int)data;
|
||||
}
|
||||
|
||||
static int ab8500_get_chip_id(struct device *dev)
|
||||
{
|
||||
struct ab8500 *ab8500;
|
||||
@ -161,9 +211,13 @@ static int set_register_interruptible(struct ab8500 *ab8500, u8 bank,
|
||||
static int ab8500_set_register(struct device *dev, u8 bank,
|
||||
u8 reg, u8 value)
|
||||
{
|
||||
int ret;
|
||||
struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
|
||||
|
||||
return set_register_interruptible(ab8500, bank, reg, value);
|
||||
atomic_inc(&ab8500->transfer_ongoing);
|
||||
ret = set_register_interruptible(ab8500, bank, reg, value);
|
||||
atomic_dec(&ab8500->transfer_ongoing);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_register_interruptible(struct ab8500 *ab8500, u8 bank,
|
||||
@ -192,9 +246,13 @@ static int get_register_interruptible(struct ab8500 *ab8500, u8 bank,
|
||||
static int ab8500_get_register(struct device *dev, u8 bank,
|
||||
u8 reg, u8 *value)
|
||||
{
|
||||
int ret;
|
||||
struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
|
||||
|
||||
return get_register_interruptible(ab8500, bank, reg, value);
|
||||
atomic_inc(&ab8500->transfer_ongoing);
|
||||
ret = get_register_interruptible(ab8500, bank, reg, value);
|
||||
atomic_dec(&ab8500->transfer_ongoing);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mask_and_set_register_interruptible(struct ab8500 *ab8500, u8 bank,
|
||||
@ -241,11 +299,14 @@ out:
|
||||
static int ab8500_mask_and_set_register(struct device *dev,
|
||||
u8 bank, u8 reg, u8 bitmask, u8 bitvalues)
|
||||
{
|
||||
int ret;
|
||||
struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
|
||||
|
||||
return mask_and_set_register_interruptible(ab8500, bank, reg,
|
||||
bitmask, bitvalues);
|
||||
|
||||
atomic_inc(&ab8500->transfer_ongoing);
|
||||
ret= mask_and_set_register_interruptible(ab8500, bank, reg,
|
||||
bitmask, bitvalues);
|
||||
atomic_dec(&ab8500->transfer_ongoing);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct abx500_ops ab8500_ops = {
|
||||
@ -264,6 +325,7 @@ static void ab8500_irq_lock(struct irq_data *data)
|
||||
struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data);
|
||||
|
||||
mutex_lock(&ab8500->irq_lock);
|
||||
atomic_inc(&ab8500->transfer_ongoing);
|
||||
}
|
||||
|
||||
static void ab8500_irq_sync_unlock(struct irq_data *data)
|
||||
@ -292,7 +354,7 @@ static void ab8500_irq_sync_unlock(struct irq_data *data)
|
||||
reg = AB8500_IT_MASK1_REG + ab8500->irq_reg_offset[i];
|
||||
set_register_interruptible(ab8500, AB8500_INTERRUPT, reg, new);
|
||||
}
|
||||
|
||||
atomic_dec(&ab8500->transfer_ongoing);
|
||||
mutex_unlock(&ab8500->irq_lock);
|
||||
}
|
||||
|
||||
@ -325,6 +387,90 @@ static struct irq_chip ab8500_irq_chip = {
|
||||
.irq_unmask = ab8500_irq_unmask,
|
||||
};
|
||||
|
||||
static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500,
|
||||
int latch_offset, u8 latch_val)
|
||||
{
|
||||
int int_bit = __ffs(latch_val);
|
||||
int line, i;
|
||||
|
||||
do {
|
||||
int_bit = __ffs(latch_val);
|
||||
|
||||
for (i = 0; i < ab8500->mask_size; i++)
|
||||
if (ab8500->irq_reg_offset[i] == latch_offset)
|
||||
break;
|
||||
|
||||
if (i >= ab8500->mask_size) {
|
||||
dev_err(ab8500->dev, "Register offset 0x%2x not declared\n",
|
||||
latch_offset);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
line = (i << 3) + int_bit;
|
||||
latch_val &= ~(1 << int_bit);
|
||||
|
||||
handle_nested_irq(ab8500->irq_base + line);
|
||||
} while (latch_val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ab8500_handle_hierarchical_latch(struct ab8500 *ab8500,
|
||||
int hier_offset, u8 hier_val)
|
||||
{
|
||||
int latch_bit, status;
|
||||
u8 latch_offset, latch_val;
|
||||
|
||||
do {
|
||||
latch_bit = __ffs(hier_val);
|
||||
latch_offset = (hier_offset << 3) + latch_bit;
|
||||
|
||||
/* Fix inconsistent ITFromLatch25 bit mapping... */
|
||||
if (unlikely(latch_offset == 17))
|
||||
latch_offset = 24;
|
||||
|
||||
status = get_register_interruptible(ab8500,
|
||||
AB8500_INTERRUPT,
|
||||
AB8500_IT_LATCH1_REG + latch_offset,
|
||||
&latch_val);
|
||||
if (status < 0 || latch_val == 0)
|
||||
goto discard;
|
||||
|
||||
status = ab8500_handle_hierarchical_line(ab8500,
|
||||
latch_offset, latch_val);
|
||||
if (status < 0)
|
||||
return status;
|
||||
discard:
|
||||
hier_val &= ~(1 << latch_bit);
|
||||
} while (hier_val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t ab8500_hierarchical_irq(int irq, void *dev)
|
||||
{
|
||||
struct ab8500 *ab8500 = dev;
|
||||
u8 i;
|
||||
|
||||
dev_vdbg(ab8500->dev, "interrupt\n");
|
||||
|
||||
/* Hierarchical interrupt version */
|
||||
for (i = 0; i < AB8500_IT_LATCHHIER_NUM; i++) {
|
||||
int status;
|
||||
u8 hier_val;
|
||||
|
||||
status = get_register_interruptible(ab8500, AB8500_INTERRUPT,
|
||||
AB8500_IT_LATCHHIER1_REG + i, &hier_val);
|
||||
if (status < 0 || hier_val == 0)
|
||||
continue;
|
||||
|
||||
status = ab8500_handle_hierarchical_latch(ab8500, i, hier_val);
|
||||
if (status < 0)
|
||||
break;
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t ab8500_irq(int irq, void *dev)
|
||||
{
|
||||
struct ab8500 *ab8500 = dev;
|
||||
@ -332,6 +478,8 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
|
||||
|
||||
dev_vdbg(ab8500->dev, "interrupt\n");
|
||||
|
||||
atomic_inc(&ab8500->transfer_ongoing);
|
||||
|
||||
for (i = 0; i < ab8500->mask_size; i++) {
|
||||
int regoffset = ab8500->irq_reg_offset[i];
|
||||
int status;
|
||||
@ -355,9 +503,10 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
|
||||
|
||||
handle_nested_irq(ab8500->irq_base + line);
|
||||
value &= ~(1 << bit);
|
||||
|
||||
} while (value);
|
||||
}
|
||||
|
||||
atomic_dec(&ab8500->transfer_ongoing);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -411,6 +560,14 @@ static void ab8500_irq_remove(struct ab8500 *ab8500)
|
||||
}
|
||||
}
|
||||
|
||||
int ab8500_suspend(struct ab8500 *ab8500)
|
||||
{
|
||||
if (atomic_read(&ab8500->transfer_ongoing))
|
||||
return -EINVAL;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* AB8500 GPIO Resources */
|
||||
static struct resource __devinitdata ab8500_gpio_resources[] = {
|
||||
{
|
||||
@ -744,6 +901,39 @@ static struct resource __devinitdata ab8500_usb_resources[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource __devinitdata ab8505_iddet_resources[] = {
|
||||
{
|
||||
.name = "KeyDeglitch",
|
||||
.start = AB8505_INT_KEYDEGLITCH,
|
||||
.end = AB8505_INT_KEYDEGLITCH,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
.name = "KP",
|
||||
.start = AB8505_INT_KP,
|
||||
.end = AB8505_INT_KP,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
.name = "IKP",
|
||||
.start = AB8505_INT_IKP,
|
||||
.end = AB8505_INT_IKP,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
.name = "IKR",
|
||||
.start = AB8505_INT_IKR,
|
||||
.end = AB8505_INT_IKR,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
{
|
||||
.name = "KeyStuck",
|
||||
.start = AB8505_INT_KEYSTUCK,
|
||||
.end = AB8505_INT_KEYSTUCK,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource __devinitdata ab8500_temp_resources[] = {
|
||||
{
|
||||
.name = "AB8500_TEMP_WARM",
|
||||
@ -777,35 +967,11 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = {
|
||||
.num_resources = ARRAY_SIZE(ab8500_rtc_resources),
|
||||
.resources = ab8500_rtc_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-charger",
|
||||
.num_resources = ARRAY_SIZE(ab8500_charger_resources),
|
||||
.resources = ab8500_charger_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-btemp",
|
||||
.num_resources = ARRAY_SIZE(ab8500_btemp_resources),
|
||||
.resources = ab8500_btemp_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-fg",
|
||||
.num_resources = ARRAY_SIZE(ab8500_fg_resources),
|
||||
.resources = ab8500_fg_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-chargalg",
|
||||
.num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
|
||||
.resources = ab8500_chargalg_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-acc-det",
|
||||
.num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
|
||||
.resources = ab8500_av_acc_detect_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-codec",
|
||||
},
|
||||
|
||||
{
|
||||
.name = "ab8500-poweron-key",
|
||||
.num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources),
|
||||
@ -834,6 +1000,29 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell __devinitdata ab8500_bm_devs[] = {
|
||||
{
|
||||
.name = "ab8500-charger",
|
||||
.num_resources = ARRAY_SIZE(ab8500_charger_resources),
|
||||
.resources = ab8500_charger_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-btemp",
|
||||
.num_resources = ARRAY_SIZE(ab8500_btemp_resources),
|
||||
.resources = ab8500_btemp_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-fg",
|
||||
.num_resources = ARRAY_SIZE(ab8500_fg_resources),
|
||||
.resources = ab8500_fg_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-chargalg",
|
||||
.num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
|
||||
.resources = ab8500_chargalg_resources,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell __devinitdata ab8500_devs[] = {
|
||||
{
|
||||
.name = "ab8500-gpio",
|
||||
@ -845,6 +1034,9 @@ static struct mfd_cell __devinitdata ab8500_devs[] = {
|
||||
.num_resources = ARRAY_SIZE(ab8500_usb_resources),
|
||||
.resources = ab8500_usb_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab8500-codec",
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell __devinitdata ab9540_devs[] = {
|
||||
@ -858,6 +1050,18 @@ static struct mfd_cell __devinitdata ab9540_devs[] = {
|
||||
.num_resources = ARRAY_SIZE(ab8500_usb_resources),
|
||||
.resources = ab8500_usb_resources,
|
||||
},
|
||||
{
|
||||
.name = "ab9540-codec",
|
||||
},
|
||||
};
|
||||
|
||||
/* Device list common to ab9540 and ab8505 */
|
||||
static struct mfd_cell __devinitdata ab9540_ab8505_devs[] = {
|
||||
{
|
||||
.name = "ab-iddet",
|
||||
.num_resources = ARRAY_SIZE(ab8505_iddet_resources),
|
||||
.resources = ab8505_iddet_resources,
|
||||
},
|
||||
};
|
||||
|
||||
static ssize_t show_chip_id(struct device *dev,
|
||||
@ -1003,18 +1207,66 @@ static struct attribute_group ab9540_attr_group = {
|
||||
.attrs = ab9540_sysfs_entries,
|
||||
};
|
||||
|
||||
int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version)
|
||||
static const struct of_device_id ab8500_match[] = {
|
||||
{
|
||||
.compatible = "stericsson,ab8500",
|
||||
.data = (void *)AB8500_VERSION_AB8500,
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static int __devinit ab8500_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev);
|
||||
struct ab8500_platform_data *plat = dev_get_platdata(&pdev->dev);
|
||||
const struct platform_device_id *platid = platform_get_device_id(pdev);
|
||||
enum ab8500_version version = AB8500_VERSION_UNDEFINED;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct ab8500 *ab8500;
|
||||
struct resource *resource;
|
||||
int ret;
|
||||
int i;
|
||||
u8 value;
|
||||
|
||||
ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL);
|
||||
if (!ab8500)
|
||||
return -ENOMEM;
|
||||
|
||||
if (plat)
|
||||
ab8500->irq_base = plat->irq_base;
|
||||
else if (np)
|
||||
ret = of_property_read_u32(np, "stericsson,irq-base", &ab8500->irq_base);
|
||||
|
||||
if (!ab8500->irq_base) {
|
||||
dev_info(&pdev->dev, "couldn't find irq-base\n");
|
||||
ret = -EINVAL;
|
||||
goto out_free_ab8500;
|
||||
}
|
||||
|
||||
ab8500->dev = &pdev->dev;
|
||||
|
||||
resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!resource) {
|
||||
ret = -ENODEV;
|
||||
goto out_free_ab8500;
|
||||
}
|
||||
|
||||
ab8500->irq = resource->start;
|
||||
|
||||
ab8500->read = ab8500_i2c_read;
|
||||
ab8500->write = ab8500_i2c_write;
|
||||
ab8500->write_masked = ab8500_i2c_write_masked;
|
||||
|
||||
mutex_init(&ab8500->lock);
|
||||
mutex_init(&ab8500->irq_lock);
|
||||
atomic_set(&ab8500->transfer_ongoing, 0);
|
||||
|
||||
platform_set_drvdata(pdev, ab8500);
|
||||
|
||||
if (platid)
|
||||
version = platid->driver_data;
|
||||
else if (np)
|
||||
version = (unsigned int)
|
||||
of_match_device(ab8500_match, &pdev->dev)->data;
|
||||
|
||||
if (version != AB8500_VERSION_UNDEFINED)
|
||||
ab8500->version = version;
|
||||
@ -1022,7 +1274,7 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version)
|
||||
ret = get_register_interruptible(ab8500, AB8500_MISC,
|
||||
AB8500_IC_NAME_REG, &value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto out_free_ab8500;
|
||||
|
||||
ab8500->version = value;
|
||||
}
|
||||
@ -1030,7 +1282,7 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version)
|
||||
ret = get_register_interruptible(ab8500, AB8500_MISC,
|
||||
AB8500_REV_REG, &value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto out_free_ab8500;
|
||||
|
||||
ab8500->chip_id = value;
|
||||
|
||||
@ -1105,30 +1357,57 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version)
|
||||
if (ret)
|
||||
goto out_freeoldmask;
|
||||
|
||||
ret = request_threaded_irq(ab8500->irq, NULL, ab8500_irq,
|
||||
IRQF_ONESHOT | IRQF_NO_SUSPEND,
|
||||
"ab8500", ab8500);
|
||||
/* Activate this feature only in ab9540 */
|
||||
/* till tests are done on ab8500 1p2 or later*/
|
||||
if (is_ab9540(ab8500))
|
||||
ret = request_threaded_irq(ab8500->irq, NULL,
|
||||
ab8500_hierarchical_irq,
|
||||
IRQF_ONESHOT | IRQF_NO_SUSPEND,
|
||||
"ab8500", ab8500);
|
||||
else
|
||||
ret = request_threaded_irq(ab8500->irq, NULL,
|
||||
ab8500_irq,
|
||||
IRQF_ONESHOT | IRQF_NO_SUSPEND,
|
||||
"ab8500", ab8500);
|
||||
if (ret)
|
||||
goto out_removeirq;
|
||||
}
|
||||
|
||||
ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
|
||||
ARRAY_SIZE(abx500_common_devs), NULL,
|
||||
ab8500->irq_base);
|
||||
if (!np) {
|
||||
ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
|
||||
ARRAY_SIZE(abx500_common_devs), NULL,
|
||||
ab8500->irq_base);
|
||||
|
||||
if (ret)
|
||||
goto out_freeirq;
|
||||
if (ret)
|
||||
goto out_freeirq;
|
||||
|
||||
if (is_ab9540(ab8500))
|
||||
ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
|
||||
ARRAY_SIZE(ab9540_devs), NULL,
|
||||
ab8500->irq_base);
|
||||
else
|
||||
ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
|
||||
ARRAY_SIZE(ab9540_devs), NULL,
|
||||
ab8500->irq_base);
|
||||
if (ret)
|
||||
goto out_freeirq;
|
||||
if (is_ab9540(ab8500))
|
||||
ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
|
||||
ARRAY_SIZE(ab9540_devs), NULL,
|
||||
ab8500->irq_base);
|
||||
else
|
||||
ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
|
||||
ARRAY_SIZE(ab8500_devs), NULL,
|
||||
ab8500->irq_base);
|
||||
if (ret)
|
||||
goto out_freeirq;
|
||||
|
||||
if (is_ab9540(ab8500) || is_ab8505(ab8500))
|
||||
ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs,
|
||||
ARRAY_SIZE(ab9540_ab8505_devs), NULL,
|
||||
ab8500->irq_base);
|
||||
if (ret)
|
||||
goto out_freeirq;
|
||||
}
|
||||
|
||||
if (!no_bm) {
|
||||
/* Add battery management devices */
|
||||
ret = mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs,
|
||||
ARRAY_SIZE(ab8500_bm_devs), NULL,
|
||||
ab8500->irq_base);
|
||||
if (ret)
|
||||
dev_err(ab8500->dev, "error adding bm devices\n");
|
||||
}
|
||||
|
||||
if (is_ab9540(ab8500))
|
||||
ret = sysfs_create_group(&ab8500->dev->kobj,
|
||||
@ -1151,12 +1430,16 @@ out_freeoldmask:
|
||||
kfree(ab8500->oldmask);
|
||||
out_freemask:
|
||||
kfree(ab8500->mask);
|
||||
out_free_ab8500:
|
||||
kfree(ab8500);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __devexit ab8500_exit(struct ab8500 *ab8500)
|
||||
static int __devexit ab8500_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ab8500 *ab8500 = platform_get_drvdata(pdev);
|
||||
|
||||
if (is_ab9540(ab8500))
|
||||
sysfs_remove_group(&ab8500->dev->kobj, &ab9540_attr_group);
|
||||
else
|
||||
@ -1168,10 +1451,42 @@ int __devexit ab8500_exit(struct ab8500 *ab8500)
|
||||
}
|
||||
kfree(ab8500->oldmask);
|
||||
kfree(ab8500->mask);
|
||||
kfree(ab8500);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id ab8500_id[] = {
|
||||
{ "ab8500-core", AB8500_VERSION_AB8500 },
|
||||
{ "ab8505-i2c", AB8500_VERSION_AB8505 },
|
||||
{ "ab9540-i2c", AB8500_VERSION_AB9540 },
|
||||
{ "ab8540-i2c", AB8500_VERSION_AB8540 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver ab8500_core_driver = {
|
||||
.driver = {
|
||||
.name = "ab8500-core",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = ab8500_match,
|
||||
},
|
||||
.probe = ab8500_probe,
|
||||
.remove = __devexit_p(ab8500_remove),
|
||||
.id_table = ab8500_id,
|
||||
};
|
||||
|
||||
static int __init ab8500_core_init(void)
|
||||
{
|
||||
return platform_driver_register(&ab8500_core_driver);
|
||||
}
|
||||
|
||||
static void __exit ab8500_core_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ab8500_core_driver);
|
||||
}
|
||||
arch_initcall(ab8500_core_init);
|
||||
module_exit(ab8500_core_exit);
|
||||
|
||||
MODULE_AUTHOR("Mattias Wallin, Srinidhi Kasagar, Rabin Vincent");
|
||||
MODULE_DESCRIPTION("AB8500 MFD core");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -608,10 +608,16 @@ static int __devexit ab8500_debug_remove(struct platform_device *plf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ab8500_debug_match[] = {
|
||||
{ .compatible = "stericsson,ab8500-debug", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver ab8500_debug_driver = {
|
||||
.driver = {
|
||||
.name = "ab8500-debug",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = ab8500_debug_match,
|
||||
},
|
||||
.probe = ab8500_debug_probe,
|
||||
.remove = __devexit_p(ab8500_debug_remove)
|
||||
|
@ -584,7 +584,7 @@ static int __devinit ab8500_gpadc_probe(struct platform_device *pdev)
|
||||
|
||||
gpadc->irq = platform_get_irq_byname(pdev, "SW_CONV_END");
|
||||
if (gpadc->irq < 0) {
|
||||
dev_err(gpadc->dev, "failed to get platform irq-%d\n",
|
||||
dev_err(&pdev->dev, "failed to get platform irq-%d\n",
|
||||
gpadc->irq);
|
||||
ret = gpadc->irq;
|
||||
goto fail;
|
||||
@ -648,12 +648,18 @@ static int __devexit ab8500_gpadc_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ab8500_gpadc_match[] = {
|
||||
{ .compatible = "stericsson,ab8500-gpadc", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver ab8500_gpadc_driver = {
|
||||
.probe = ab8500_gpadc_probe,
|
||||
.remove = __devexit_p(ab8500_gpadc_remove),
|
||||
.driver = {
|
||||
.name = "ab8500-gpadc",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = ab8500_gpadc_match,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1,128 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
* Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson.
|
||||
* License Terms: GNU General Public License v2
|
||||
* This file was based on drivers/mfd/ab8500-spi.c
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/abx500/ab8500.h>
|
||||
#include <linux/mfd/dbx500-prcmu.h>
|
||||
|
||||
static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = prcmu_abb_write((u8)(addr >> 8), (u8)(addr & 0xFF), &data, 1);
|
||||
if (ret < 0)
|
||||
dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ab8500_i2c_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask,
|
||||
u8 data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = prcmu_abb_write_masked((u8)(addr >> 8), (u8)(addr & 0xFF), &data,
|
||||
&mask, 1);
|
||||
if (ret < 0)
|
||||
dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr)
|
||||
{
|
||||
int ret;
|
||||
u8 data;
|
||||
|
||||
ret = prcmu_abb_read((u8)(addr >> 8), (u8)(addr & 0xFF), &data, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(ab8500->dev, "prcmu i2c error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
return (int)data;
|
||||
}
|
||||
|
||||
static int __devinit ab8500_i2c_probe(struct platform_device *plf)
|
||||
{
|
||||
const struct platform_device_id *platid = platform_get_device_id(plf);
|
||||
struct ab8500 *ab8500;
|
||||
struct resource *resource;
|
||||
int ret;
|
||||
|
||||
ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL);
|
||||
if (!ab8500)
|
||||
return -ENOMEM;
|
||||
|
||||
ab8500->dev = &plf->dev;
|
||||
|
||||
resource = platform_get_resource(plf, IORESOURCE_IRQ, 0);
|
||||
if (!resource) {
|
||||
kfree(ab8500);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ab8500->irq = resource->start;
|
||||
|
||||
ab8500->read = ab8500_i2c_read;
|
||||
ab8500->write = ab8500_i2c_write;
|
||||
ab8500->write_masked = ab8500_i2c_write_masked;
|
||||
|
||||
platform_set_drvdata(plf, ab8500);
|
||||
|
||||
ret = ab8500_init(ab8500, platid->driver_data);
|
||||
if (ret)
|
||||
kfree(ab8500);
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit ab8500_i2c_remove(struct platform_device *plf)
|
||||
{
|
||||
struct ab8500 *ab8500 = platform_get_drvdata(plf);
|
||||
|
||||
ab8500_exit(ab8500);
|
||||
kfree(ab8500);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id ab8500_id[] = {
|
||||
{ "ab8500-i2c", AB8500_VERSION_AB8500 },
|
||||
{ "ab8505-i2c", AB8500_VERSION_AB8505 },
|
||||
{ "ab9540-i2c", AB8500_VERSION_AB9540 },
|
||||
{ "ab8540-i2c", AB8500_VERSION_AB8540 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver ab8500_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "ab8500-i2c",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ab8500_i2c_probe,
|
||||
.remove = __devexit_p(ab8500_i2c_remove),
|
||||
.id_table = ab8500_id,
|
||||
};
|
||||
|
||||
static int __init ab8500_i2c_init(void)
|
||||
{
|
||||
return platform_driver_register(&ab8500_i2c_driver);
|
||||
}
|
||||
|
||||
static void __exit ab8500_i2c_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ab8500_i2c_driver);
|
||||
}
|
||||
arch_initcall(ab8500_i2c_init);
|
||||
module_exit(ab8500_i2c_exit);
|
||||
|
||||
MODULE_AUTHOR("Mattias WALLIN <mattias.wallin@stericsson.com");
|
||||
MODULE_DESCRIPTION("AB8500 Core access via PRCMU I2C");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -61,10 +61,16 @@ static int __devexit ab8500_sysctrl_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ab8500_sysctrl_match[] = {
|
||||
{ .compatible = "stericsson,ab8500-sysctrl", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver ab8500_sysctrl_driver = {
|
||||
.driver = {
|
||||
.name = "ab8500-sysctrl",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = ab8500_sysctrl_match,
|
||||
},
|
||||
.probe = ab8500_sysctrl_probe,
|
||||
.remove = __devexit_p(ab8500_sysctrl_remove),
|
||||
|
@ -41,39 +41,26 @@
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/mfd/anatop.h>
|
||||
|
||||
u32 anatop_get_bits(struct anatop *adata, u32 addr, int bit_shift,
|
||||
int bit_width)
|
||||
u32 anatop_read_reg(struct anatop *adata, u32 addr)
|
||||
{
|
||||
u32 val, mask;
|
||||
|
||||
if (bit_width == 32)
|
||||
mask = ~0;
|
||||
else
|
||||
mask = (1 << bit_width) - 1;
|
||||
|
||||
val = readl(adata->ioreg + addr);
|
||||
val = (val >> bit_shift) & mask;
|
||||
|
||||
return val;
|
||||
return readl(adata->ioreg + addr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(anatop_get_bits);
|
||||
EXPORT_SYMBOL_GPL(anatop_read_reg);
|
||||
|
||||
void anatop_set_bits(struct anatop *adata, u32 addr, int bit_shift,
|
||||
int bit_width, u32 data)
|
||||
void anatop_write_reg(struct anatop *adata, u32 addr, u32 data, u32 mask)
|
||||
{
|
||||
u32 val, mask;
|
||||
u32 val;
|
||||
|
||||
if (bit_width == 32)
|
||||
mask = ~0;
|
||||
else
|
||||
mask = (1 << bit_width) - 1;
|
||||
data &= mask;
|
||||
|
||||
spin_lock(&adata->reglock);
|
||||
val = readl(adata->ioreg + addr) & ~(mask << bit_shift);
|
||||
writel((data << bit_shift) | val, adata->ioreg + addr);
|
||||
val = readl(adata->ioreg + addr);
|
||||
val &= ~mask;
|
||||
val |= data;
|
||||
writel(val, adata->ioreg + addr);
|
||||
spin_unlock(&adata->reglock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(anatop_set_bits);
|
||||
EXPORT_SYMBOL_GPL(anatop_write_reg);
|
||||
|
||||
static const struct of_device_id of_anatop_match[] = {
|
||||
{ .compatible = "fsl,imx6q-anatop", },
|
||||
|
@ -353,12 +353,28 @@ static int asic3_gpio_irq_type(struct irq_data *data, unsigned int type)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asic3_gpio_irq_set_wake(struct irq_data *data, unsigned int on)
|
||||
{
|
||||
struct asic3 *asic = irq_data_get_irq_chip_data(data);
|
||||
u32 bank, index;
|
||||
u16 bit;
|
||||
|
||||
bank = asic3_irq_to_bank(asic, data->irq);
|
||||
index = asic3_irq_to_index(asic, data->irq);
|
||||
bit = 1<<index;
|
||||
|
||||
asic3_set_register(asic, bank + ASIC3_GPIO_SLEEP_MASK, bit, !on);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_chip asic3_gpio_irq_chip = {
|
||||
.name = "ASIC3-GPIO",
|
||||
.irq_ack = asic3_mask_gpio_irq,
|
||||
.irq_mask = asic3_mask_gpio_irq,
|
||||
.irq_unmask = asic3_unmask_gpio_irq,
|
||||
.irq_set_type = asic3_gpio_irq_type,
|
||||
.irq_set_wake = asic3_gpio_irq_set_wake,
|
||||
};
|
||||
|
||||
static struct irq_chip asic3_irq_chip = {
|
||||
@ -529,7 +545,7 @@ static int asic3_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct asic3 *asic = container_of(chip, struct asic3, gpio);
|
||||
|
||||
return (offset < ASIC3_NUM_GPIOS) ? asic->irq_base + offset : -ENXIO;
|
||||
return asic->irq_base + offset;
|
||||
}
|
||||
|
||||
static __init int asic3_gpio_probe(struct platform_device *pdev,
|
||||
@ -894,10 +910,13 @@ static int __init asic3_mfd_probe(struct platform_device *pdev,
|
||||
asic3_mmc_resources[0].start >>= asic->bus_shift;
|
||||
asic3_mmc_resources[0].end >>= asic->bus_shift;
|
||||
|
||||
ret = mfd_add_devices(&pdev->dev, pdev->id,
|
||||
if (pdata->clock_rate) {
|
||||
ds1wm_pdata.clock_rate = pdata->clock_rate;
|
||||
ret = mfd_add_devices(&pdev->dev, pdev->id,
|
||||
&asic3_cell_ds1wm, 1, mem, asic->irq_base);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (mem_sdio && (irq >= 0)) {
|
||||
ret = mfd_add_devices(&pdev->dev, pdev->id,
|
||||
@ -1000,6 +1019,9 @@ static int __init asic3_probe(struct platform_device *pdev)
|
||||
|
||||
asic3_mfd_probe(pdev, pdata, mem);
|
||||
|
||||
asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
|
||||
(ASIC3_EXTCF_CF0_BUF_EN|ASIC3_EXTCF_CF0_PWAIT_EN), 1);
|
||||
|
||||
dev_info(asic->dev, "ASIC3 Core driver\n");
|
||||
|
||||
return 0;
|
||||
@ -1021,6 +1043,9 @@ static int __devexit asic3_remove(struct platform_device *pdev)
|
||||
int ret;
|
||||
struct asic3 *asic = platform_get_drvdata(pdev);
|
||||
|
||||
asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
|
||||
(ASIC3_EXTCF_CF0_BUF_EN|ASIC3_EXTCF_CF0_PWAIT_EN), 0);
|
||||
|
||||
asic3_mfd_remove(pdev);
|
||||
|
||||
ret = asic3_gpio_remove(pdev);
|
||||
|
@ -186,18 +186,7 @@ static struct pci_driver cs5535_mfd_driver = {
|
||||
.remove = __devexit_p(cs5535_mfd_remove),
|
||||
};
|
||||
|
||||
static int __init cs5535_mfd_init(void)
|
||||
{
|
||||
return pci_register_driver(&cs5535_mfd_driver);
|
||||
}
|
||||
|
||||
static void __exit cs5535_mfd_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&cs5535_mfd_driver);
|
||||
}
|
||||
|
||||
module_init(cs5535_mfd_init);
|
||||
module_exit(cs5535_mfd_exit);
|
||||
module_pci_driver(cs5535_mfd_driver);
|
||||
|
||||
MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>");
|
||||
MODULE_DESCRIPTION("MFD driver for CS5535/CS5536 southbridge's ISA PCI device");
|
||||
|
@ -318,6 +318,135 @@ static bool da9052_reg_volatile(struct device *dev, unsigned int reg)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TBAT look-up table is computed from the R90 reg (8 bit register)
|
||||
* reading as below. The battery temperature is in milliCentigrade
|
||||
* TBAT = (1/(t1+1/298) - 273) * 1000 mC
|
||||
* where t1 = (1/B)* ln(( ADCval * 2.5)/(R25*ITBAT*255))
|
||||
* Default values are R25 = 10e3, B = 3380, ITBAT = 50e-6
|
||||
* Example:
|
||||
* R25=10E3, B=3380, ITBAT=50e-6, ADCVAL=62d calculates
|
||||
* TBAT = 20015 mili degrees Centrigrade
|
||||
*
|
||||
*/
|
||||
static const int32_t tbat_lookup[255] = {
|
||||
183258, 144221, 124334, 111336, 101826, 94397, 88343, 83257,
|
||||
78889, 75071, 71688, 68656, 65914, 63414, 61120, 59001,
|
||||
570366, 55204, 53490, 51881, 50364, 48931, 47574, 46285,
|
||||
45059, 43889, 42772, 41703, 40678, 39694, 38748, 37838,
|
||||
36961, 36115, 35297, 34507, 33743, 33002, 32284, 31588,
|
||||
30911, 30254, 29615, 28994, 28389, 27799, 27225, 26664,
|
||||
26117, 25584, 25062, 24553, 24054, 23567, 23091, 22624,
|
||||
22167, 21719, 21281, 20851, 20429, 20015, 19610, 19211,
|
||||
18820, 18436, 18058, 17688, 17323, 16965, 16612, 16266,
|
||||
15925, 15589, 15259, 14933, 14613, 14298, 13987, 13681,
|
||||
13379, 13082, 12788, 12499, 12214, 11933, 11655, 11382,
|
||||
11112, 10845, 10582, 10322, 10066, 9812, 9562, 9315,
|
||||
9071, 8830, 8591, 8356, 8123, 7893, 7665, 7440,
|
||||
7218, 6998, 6780, 6565, 6352, 6141, 5933, 5726,
|
||||
5522, 5320, 5120, 4922, 4726, 4532, 4340, 4149,
|
||||
3961, 3774, 3589, 3406, 3225, 3045, 2867, 2690,
|
||||
2516, 2342, 2170, 2000, 1831, 1664, 1498, 1334,
|
||||
1171, 1009, 849, 690, 532, 376, 221, 67,
|
||||
-84, -236, -386, -535, -683, -830, -975, -1119,
|
||||
-1263, -1405, -1546, -1686, -1825, -1964, -2101, -2237,
|
||||
-2372, -2506, -2639, -2771, -2902, -3033, -3162, -3291,
|
||||
-3418, -3545, -3671, -3796, -3920, -4044, -4166, -4288,
|
||||
-4409, -4529, -4649, -4767, -4885, -5002, -5119, -5235,
|
||||
-5349, -5464, -5577, -5690, -5802, -5913, -6024, -6134,
|
||||
-6244, -6352, -6461, -6568, -6675, -6781, -6887, -6992,
|
||||
-7096, -7200, -7303, -7406, -7508, -7609, -7710, -7810,
|
||||
-7910, -8009, -8108, -8206, -8304, -8401, -8497, -8593,
|
||||
-8689, -8784, -8878, -8972, -9066, -9159, -9251, -9343,
|
||||
-9435, -9526, -9617, -9707, -9796, -9886, -9975, -10063,
|
||||
-10151, -10238, -10325, -10412, -10839, -10923, -11007, -11090,
|
||||
-11173, -11256, -11338, -11420, -11501, -11583, -11663, -11744,
|
||||
-11823, -11903, -11982
|
||||
};
|
||||
|
||||
static const u8 chan_mux[DA9052_ADC_VBBAT + 1] = {
|
||||
[DA9052_ADC_VDDOUT] = DA9052_ADC_MAN_MUXSEL_VDDOUT,
|
||||
[DA9052_ADC_ICH] = DA9052_ADC_MAN_MUXSEL_ICH,
|
||||
[DA9052_ADC_TBAT] = DA9052_ADC_MAN_MUXSEL_TBAT,
|
||||
[DA9052_ADC_VBAT] = DA9052_ADC_MAN_MUXSEL_VBAT,
|
||||
[DA9052_ADC_IN4] = DA9052_ADC_MAN_MUXSEL_AD4,
|
||||
[DA9052_ADC_IN5] = DA9052_ADC_MAN_MUXSEL_AD5,
|
||||
[DA9052_ADC_IN6] = DA9052_ADC_MAN_MUXSEL_AD6,
|
||||
[DA9052_ADC_VBBAT] = DA9052_ADC_MAN_MUXSEL_VBBAT
|
||||
};
|
||||
|
||||
int da9052_adc_manual_read(struct da9052 *da9052, unsigned char channel)
|
||||
{
|
||||
int ret;
|
||||
unsigned short calc_data;
|
||||
unsigned short data;
|
||||
unsigned char mux_sel;
|
||||
|
||||
if (channel > DA9052_ADC_VBBAT)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&da9052->auxadc_lock);
|
||||
|
||||
/* Channel gets activated on enabling the Conversion bit */
|
||||
mux_sel = chan_mux[channel] | DA9052_ADC_MAN_MAN_CONV;
|
||||
|
||||
ret = da9052_reg_write(da9052, DA9052_ADC_MAN_REG, mux_sel);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
/* Wait for an interrupt */
|
||||
if (!wait_for_completion_timeout(&da9052->done,
|
||||
msecs_to_jiffies(500))) {
|
||||
dev_err(da9052->dev,
|
||||
"timeout waiting for ADC conversion interrupt\n");
|
||||
ret = -ETIMEDOUT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = da9052_reg_read(da9052, DA9052_ADC_RES_H_REG);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
calc_data = (unsigned short)ret;
|
||||
data = calc_data << 2;
|
||||
|
||||
ret = da9052_reg_read(da9052, DA9052_ADC_RES_L_REG);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
calc_data = (unsigned short)(ret & DA9052_ADC_RES_LSB);
|
||||
data |= calc_data;
|
||||
|
||||
ret = data;
|
||||
|
||||
err:
|
||||
mutex_unlock(&da9052->auxadc_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(da9052_adc_manual_read);
|
||||
|
||||
static irqreturn_t da9052_auxadc_irq(int irq, void *irq_data)
|
||||
{
|
||||
struct da9052 *da9052 = irq_data;
|
||||
|
||||
complete(&da9052->done);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int da9052_adc_read_temp(struct da9052 *da9052)
|
||||
{
|
||||
int tbat;
|
||||
|
||||
tbat = da9052_reg_read(da9052, DA9052_TBAT_RES_REG);
|
||||
if (tbat <= 0)
|
||||
return tbat;
|
||||
|
||||
/* ARRAY_SIZE check is not needed since TBAT is a 8-bit register */
|
||||
return tbat_lookup[tbat - 1];
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(da9052_adc_read_temp);
|
||||
|
||||
static struct resource da9052_rtc_resource = {
|
||||
.name = "ALM",
|
||||
.start = DA9052_IRQ_ALARM,
|
||||
@ -646,6 +775,9 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id)
|
||||
struct irq_desc *desc;
|
||||
int ret;
|
||||
|
||||
mutex_init(&da9052->auxadc_lock);
|
||||
init_completion(&da9052->done);
|
||||
|
||||
if (pdata && pdata->init != NULL)
|
||||
pdata->init(da9052);
|
||||
|
||||
@ -665,6 +797,12 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id)
|
||||
|
||||
da9052->irq_base = regmap_irq_chip_get_base(da9052->irq_data);
|
||||
|
||||
ret = request_threaded_irq(DA9052_IRQ_ADC_EOM, NULL, da9052_auxadc_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
"adc irq", da9052);
|
||||
if (ret != 0)
|
||||
dev_err(da9052->dev, "DA9052 ADC IRQ failed ret=%d\n", ret);
|
||||
|
||||
ret = mfd_add_devices(da9052->dev, -1, da9052_subdev_info,
|
||||
ARRAY_SIZE(da9052_subdev_info), NULL, 0);
|
||||
if (ret)
|
||||
@ -673,6 +811,7 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id)
|
||||
return 0;
|
||||
|
||||
err:
|
||||
free_irq(DA9052_IRQ_ADC_EOM, da9052);
|
||||
mfd_remove_devices(da9052->dev);
|
||||
regmap_err:
|
||||
return ret;
|
||||
@ -680,6 +819,7 @@ regmap_err:
|
||||
|
||||
void da9052_device_exit(struct da9052 *da9052)
|
||||
{
|
||||
free_irq(DA9052_IRQ_ADC_EOM, da9052);
|
||||
regmap_del_irq_chip(da9052->chip_irq, da9052->irq_data);
|
||||
mfd_remove_devices(da9052->dev);
|
||||
}
|
||||
|
@ -22,6 +22,11 @@
|
||||
#include <linux/mfd/da9052/da9052.h>
|
||||
#include <linux/mfd/da9052/reg.h>
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#endif
|
||||
|
||||
static int da9052_i2c_enable_multiwrite(struct da9052 *da9052)
|
||||
{
|
||||
int reg_val, ret;
|
||||
@ -41,65 +46,6 @@ static int da9052_i2c_enable_multiwrite(struct da9052 *da9052)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit da9052_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct da9052 *da9052;
|
||||
int ret;
|
||||
|
||||
da9052 = kzalloc(sizeof(struct da9052), GFP_KERNEL);
|
||||
if (!da9052)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||
dev_info(&client->dev, "Error in %s:i2c_check_functionality\n",
|
||||
__func__);
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
da9052->dev = &client->dev;
|
||||
da9052->chip_irq = client->irq;
|
||||
|
||||
i2c_set_clientdata(client, da9052);
|
||||
|
||||
da9052->regmap = regmap_init_i2c(client, &da9052_regmap_config);
|
||||
if (IS_ERR(da9052->regmap)) {
|
||||
ret = PTR_ERR(da9052->regmap);
|
||||
dev_err(&client->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = da9052_i2c_enable_multiwrite(da9052);
|
||||
if (ret < 0)
|
||||
goto err_regmap;
|
||||
|
||||
ret = da9052_device_init(da9052, id->driver_data);
|
||||
if (ret != 0)
|
||||
goto err_regmap;
|
||||
|
||||
return 0;
|
||||
|
||||
err_regmap:
|
||||
regmap_exit(da9052->regmap);
|
||||
err:
|
||||
kfree(da9052);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit da9052_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct da9052 *da9052 = i2c_get_clientdata(client);
|
||||
|
||||
da9052_device_exit(da9052);
|
||||
regmap_exit(da9052->regmap);
|
||||
kfree(da9052);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_device_id da9052_i2c_id[] = {
|
||||
{"da9052", DA9052},
|
||||
{"da9053-aa", DA9053_AA},
|
||||
@ -108,6 +54,81 @@ static struct i2c_device_id da9052_i2c_id[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id dialog_dt_ids[] = {
|
||||
{ .compatible = "dlg,da9052", .data = &da9052_i2c_id[0] },
|
||||
{ .compatible = "dlg,da9053-aa", .data = &da9052_i2c_id[1] },
|
||||
{ .compatible = "dlg,da9053-ab", .data = &da9052_i2c_id[2] },
|
||||
{ .compatible = "dlg,da9053-bb", .data = &da9052_i2c_id[3] },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
#endif
|
||||
|
||||
static int __devinit da9052_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct da9052 *da9052;
|
||||
int ret;
|
||||
|
||||
da9052 = devm_kzalloc(&client->dev, sizeof(struct da9052), GFP_KERNEL);
|
||||
if (!da9052)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||
dev_info(&client->dev, "Error in %s:i2c_check_functionality\n",
|
||||
__func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
da9052->dev = &client->dev;
|
||||
da9052->chip_irq = client->irq;
|
||||
|
||||
i2c_set_clientdata(client, da9052);
|
||||
|
||||
da9052->regmap = devm_regmap_init_i2c(client, &da9052_regmap_config);
|
||||
if (IS_ERR(da9052->regmap)) {
|
||||
ret = PTR_ERR(da9052->regmap);
|
||||
dev_err(&client->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = da9052_i2c_enable_multiwrite(da9052);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
if (!id) {
|
||||
struct device_node *np = client->dev.of_node;
|
||||
const struct of_device_id *deviceid;
|
||||
|
||||
deviceid = of_match_node(dialog_dt_ids, np);
|
||||
id = (const struct i2c_device_id *)deviceid->data;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!id) {
|
||||
ret = -ENODEV;
|
||||
dev_err(&client->dev, "id is null.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = da9052_device_init(da9052, id->driver_data);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit da9052_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct da9052 *da9052 = i2c_get_clientdata(client);
|
||||
|
||||
da9052_device_exit(da9052);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver da9052_i2c_driver = {
|
||||
.probe = da9052_i2c_probe,
|
||||
.remove = __devexit_p(da9052_i2c_remove),
|
||||
@ -115,6 +136,9 @@ static struct i2c_driver da9052_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "da9052",
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_OF
|
||||
.of_match_table = dialog_dt_ids,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -25,8 +25,9 @@ static int __devinit da9052_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct da9052 *da9052 = kzalloc(sizeof(struct da9052), GFP_KERNEL);
|
||||
struct da9052 *da9052;
|
||||
|
||||
da9052 = devm_kzalloc(&spi->dev, sizeof(struct da9052), GFP_KERNEL);
|
||||
if (!da9052)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -42,25 +43,19 @@ static int __devinit da9052_spi_probe(struct spi_device *spi)
|
||||
da9052_regmap_config.read_flag_mask = 1;
|
||||
da9052_regmap_config.write_flag_mask = 0;
|
||||
|
||||
da9052->regmap = regmap_init_spi(spi, &da9052_regmap_config);
|
||||
da9052->regmap = devm_regmap_init_spi(spi, &da9052_regmap_config);
|
||||
if (IS_ERR(da9052->regmap)) {
|
||||
ret = PTR_ERR(da9052->regmap);
|
||||
dev_err(&spi->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
goto err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = da9052_device_init(da9052, id->driver_data);
|
||||
if (ret != 0)
|
||||
goto err_regmap;
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
|
||||
err_regmap:
|
||||
regmap_exit(da9052->regmap);
|
||||
err:
|
||||
kfree(da9052);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit da9052_spi_remove(struct spi_device *spi)
|
||||
@ -68,9 +63,6 @@ static int __devexit da9052_spi_remove(struct spi_device *spi)
|
||||
struct da9052 *da9052 = dev_get_drvdata(&spi->dev);
|
||||
|
||||
da9052_device_exit(da9052);
|
||||
regmap_exit(da9052->regmap);
|
||||
kfree(da9052);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -88,7 +80,6 @@ static struct spi_driver da9052_spi_driver = {
|
||||
.id_table = da9052_spi_id,
|
||||
.driver = {
|
||||
.name = "da9052",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
@ -2720,6 +2720,7 @@ static struct regulator_consumer_supply db8500_vape_consumers[] = {
|
||||
REGULATOR_SUPPLY("v-i2c", "nmk-i2c.1"),
|
||||
REGULATOR_SUPPLY("v-i2c", "nmk-i2c.2"),
|
||||
REGULATOR_SUPPLY("v-i2c", "nmk-i2c.3"),
|
||||
REGULATOR_SUPPLY("v-i2c", "nmk-i2c.4"),
|
||||
/* "v-mmc" changed to "vcore" in the mainline kernel */
|
||||
REGULATOR_SUPPLY("vcore", "sdi0"),
|
||||
REGULATOR_SUPPLY("vcore", "sdi1"),
|
||||
@ -2958,9 +2959,10 @@ static struct mfd_cell db8500_prcmu_devs[] = {
|
||||
* prcmu_fw_init - arch init call for the Linux PRCMU fw init logic
|
||||
*
|
||||
*/
|
||||
static int __init db8500_prcmu_probe(struct platform_device *pdev)
|
||||
static int __devinit db8500_prcmu_probe(struct platform_device *pdev)
|
||||
{
|
||||
int err = 0;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
int irq = 0, err = 0;
|
||||
|
||||
if (ux500_is_svp())
|
||||
return -ENODEV;
|
||||
@ -2970,8 +2972,14 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev)
|
||||
/* Clean up the mailbox interrupts after pre-kernel code. */
|
||||
writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);
|
||||
|
||||
err = request_threaded_irq(IRQ_DB8500_PRCMU1, prcmu_irq_handler,
|
||||
prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL);
|
||||
if (np)
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
|
||||
if (!np || irq <= 0)
|
||||
irq = IRQ_DB8500_PRCMU1;
|
||||
|
||||
err = request_threaded_irq(irq, prcmu_irq_handler,
|
||||
prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL);
|
||||
if (err < 0) {
|
||||
pr_err("prcmu: Failed to allocate IRQ_DB8500_PRCMU1.\n");
|
||||
err = -EBUSY;
|
||||
@ -2981,14 +2989,16 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev)
|
||||
if (cpu_is_u8500v20_or_later())
|
||||
prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
|
||||
|
||||
err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs,
|
||||
ARRAY_SIZE(db8500_prcmu_devs), NULL,
|
||||
0);
|
||||
if (!np) {
|
||||
err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs,
|
||||
ARRAY_SIZE(db8500_prcmu_devs), NULL, 0);
|
||||
if (err) {
|
||||
pr_err("prcmu: Failed to add subdevices\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (err)
|
||||
pr_err("prcmu: Failed to add subdevices\n");
|
||||
else
|
||||
pr_info("DB8500 PRCMU initialized\n");
|
||||
pr_info("DB8500 PRCMU initialized\n");
|
||||
|
||||
no_irq_return:
|
||||
return err;
|
||||
@ -2999,11 +3009,12 @@ static struct platform_driver db8500_prcmu_driver = {
|
||||
.name = "db8500-prcmu",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = db8500_prcmu_probe,
|
||||
};
|
||||
|
||||
static int __init db8500_prcmu_init(void)
|
||||
{
|
||||
return platform_driver_probe(&db8500_prcmu_driver, db8500_prcmu_probe);
|
||||
return platform_driver_register(&db8500_prcmu_driver);
|
||||
}
|
||||
|
||||
arch_initcall(db8500_prcmu_init);
|
||||
|
@ -406,7 +406,7 @@ static int __devinit intel_msic_probe(struct platform_device *pdev)
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
msic = kzalloc(sizeof(*msic), GFP_KERNEL);
|
||||
msic = devm_kzalloc(&pdev->dev, sizeof(*msic), GFP_KERNEL);
|
||||
if (!msic)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -421,21 +421,13 @@ static int __devinit intel_msic_probe(struct platform_device *pdev)
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "failed to get SRAM iomem resource\n");
|
||||
ret = -ENODEV;
|
||||
goto fail_free_msic;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
res = request_mem_region(res->start, resource_size(res), pdev->name);
|
||||
if (!res) {
|
||||
ret = -EBUSY;
|
||||
goto fail_free_msic;
|
||||
}
|
||||
|
||||
msic->irq_base = ioremap_nocache(res->start, resource_size(res));
|
||||
msic->irq_base = devm_request_and_ioremap(&pdev->dev, res);
|
||||
if (!msic->irq_base) {
|
||||
dev_err(&pdev->dev, "failed to map SRAM memory\n");
|
||||
ret = -ENOMEM;
|
||||
goto fail_release_region;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, msic);
|
||||
@ -443,7 +435,7 @@ static int __devinit intel_msic_probe(struct platform_device *pdev)
|
||||
ret = intel_msic_init_devices(msic);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize MSIC devices\n");
|
||||
goto fail_unmap_mem;
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "Intel MSIC version %c%d (vendor %#x)\n",
|
||||
@ -451,27 +443,14 @@ static int __devinit intel_msic_probe(struct platform_device *pdev)
|
||||
msic->vendor);
|
||||
|
||||
return 0;
|
||||
|
||||
fail_unmap_mem:
|
||||
iounmap(msic->irq_base);
|
||||
fail_release_region:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
fail_free_msic:
|
||||
kfree(msic);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit intel_msic_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct intel_msic *msic = platform_get_drvdata(pdev);
|
||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
intel_msic_remove_devices(msic);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
iounmap(msic->irq_base);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
kfree(msic);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -283,23 +283,8 @@ static struct pci_driver cmodio_pci_driver = {
|
||||
.remove = __devexit_p(cmodio_pci_remove),
|
||||
};
|
||||
|
||||
/*
|
||||
* Module Init / Exit
|
||||
*/
|
||||
|
||||
static int __init cmodio_init(void)
|
||||
{
|
||||
return pci_register_driver(&cmodio_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit cmodio_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&cmodio_pci_driver);
|
||||
}
|
||||
module_pci_driver(cmodio_pci_driver);
|
||||
|
||||
MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
|
||||
MODULE_DESCRIPTION("Janz CMOD-IO PCI MODULbus Carrier Board Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(cmodio_init);
|
||||
module_exit(cmodio_exit);
|
||||
|
667
drivers/mfd/lm3533-core.c
Normal file
667
drivers/mfd/lm3533-core.c
Normal file
@ -0,0 +1,667 @@
|
||||
/*
|
||||
* lm3533-core.c -- LM3533 Core
|
||||
*
|
||||
* Copyright (C) 2011-2012 Texas Instruments
|
||||
*
|
||||
* Author: Johan Hovold <jhovold@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <linux/mfd/lm3533.h>
|
||||
|
||||
|
||||
#define LM3533_BOOST_OVP_MASK 0x06
|
||||
#define LM3533_BOOST_OVP_SHIFT 1
|
||||
|
||||
#define LM3533_BOOST_FREQ_MASK 0x01
|
||||
#define LM3533_BOOST_FREQ_SHIFT 0
|
||||
|
||||
#define LM3533_BL_ID_MASK 1
|
||||
#define LM3533_LED_ID_MASK 3
|
||||
#define LM3533_BL_ID_MAX 1
|
||||
#define LM3533_LED_ID_MAX 3
|
||||
|
||||
#define LM3533_HVLED_ID_MAX 2
|
||||
#define LM3533_LVLED_ID_MAX 5
|
||||
|
||||
#define LM3533_REG_OUTPUT_CONF1 0x10
|
||||
#define LM3533_REG_OUTPUT_CONF2 0x11
|
||||
#define LM3533_REG_BOOST_PWM 0x2c
|
||||
|
||||
#define LM3533_REG_MAX 0xb2
|
||||
|
||||
|
||||
static struct mfd_cell lm3533_als_devs[] = {
|
||||
{
|
||||
.name = "lm3533-als",
|
||||
.id = -1,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell lm3533_bl_devs[] = {
|
||||
{
|
||||
.name = "lm3533-backlight",
|
||||
.id = 0,
|
||||
},
|
||||
{
|
||||
.name = "lm3533-backlight",
|
||||
.id = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static struct mfd_cell lm3533_led_devs[] = {
|
||||
{
|
||||
.name = "lm3533-leds",
|
||||
.id = 0,
|
||||
},
|
||||
{
|
||||
.name = "lm3533-leds",
|
||||
.id = 1,
|
||||
},
|
||||
{
|
||||
.name = "lm3533-leds",
|
||||
.id = 2,
|
||||
},
|
||||
{
|
||||
.name = "lm3533-leds",
|
||||
.id = 3,
|
||||
},
|
||||
};
|
||||
|
||||
int lm3533_read(struct lm3533 *lm3533, u8 reg, u8 *val)
|
||||
{
|
||||
int tmp;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(lm3533->regmap, reg, &tmp);
|
||||
if (ret < 0) {
|
||||
dev_err(lm3533->dev, "failed to read register %02x: %d\n",
|
||||
reg, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = tmp;
|
||||
|
||||
dev_dbg(lm3533->dev, "read [%02x]: %02x\n", reg, *val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lm3533_read);
|
||||
|
||||
int lm3533_write(struct lm3533 *lm3533, u8 reg, u8 val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(lm3533->dev, "write [%02x]: %02x\n", reg, val);
|
||||
|
||||
ret = regmap_write(lm3533->regmap, reg, val);
|
||||
if (ret < 0) {
|
||||
dev_err(lm3533->dev, "failed to write register %02x: %d\n",
|
||||
reg, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lm3533_write);
|
||||
|
||||
int lm3533_update(struct lm3533 *lm3533, u8 reg, u8 val, u8 mask)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(lm3533->dev, "update [%02x]: %02x/%02x\n", reg, val, mask);
|
||||
|
||||
ret = regmap_update_bits(lm3533->regmap, reg, mask, val);
|
||||
if (ret < 0) {
|
||||
dev_err(lm3533->dev, "failed to update register %02x: %d\n",
|
||||
reg, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lm3533_update);
|
||||
|
||||
static int lm3533_set_boost_freq(struct lm3533 *lm3533,
|
||||
enum lm3533_boost_freq freq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = lm3533_update(lm3533, LM3533_REG_BOOST_PWM,
|
||||
freq << LM3533_BOOST_FREQ_SHIFT,
|
||||
LM3533_BOOST_FREQ_MASK);
|
||||
if (ret)
|
||||
dev_err(lm3533->dev, "failed to set boost frequency\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int lm3533_set_boost_ovp(struct lm3533 *lm3533,
|
||||
enum lm3533_boost_ovp ovp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = lm3533_update(lm3533, LM3533_REG_BOOST_PWM,
|
||||
ovp << LM3533_BOOST_OVP_SHIFT,
|
||||
LM3533_BOOST_OVP_MASK);
|
||||
if (ret)
|
||||
dev_err(lm3533->dev, "failed to set boost ovp\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* HVLED output config -- output hvled controlled by backlight bl
|
||||
*/
|
||||
static int lm3533_set_hvled_config(struct lm3533 *lm3533, u8 hvled, u8 bl)
|
||||
{
|
||||
u8 val;
|
||||
u8 mask;
|
||||
int shift;
|
||||
int ret;
|
||||
|
||||
if (hvled == 0 || hvled > LM3533_HVLED_ID_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (bl > LM3533_BL_ID_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
shift = hvled - 1;
|
||||
mask = LM3533_BL_ID_MASK << shift;
|
||||
val = bl << shift;
|
||||
|
||||
ret = lm3533_update(lm3533, LM3533_REG_OUTPUT_CONF1, val, mask);
|
||||
if (ret)
|
||||
dev_err(lm3533->dev, "failed to set hvled config\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* LVLED output config -- output lvled controlled by LED led
|
||||
*/
|
||||
static int lm3533_set_lvled_config(struct lm3533 *lm3533, u8 lvled, u8 led)
|
||||
{
|
||||
u8 reg;
|
||||
u8 val;
|
||||
u8 mask;
|
||||
int shift;
|
||||
int ret;
|
||||
|
||||
if (lvled == 0 || lvled > LM3533_LVLED_ID_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (led > LM3533_LED_ID_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (lvled < 4) {
|
||||
reg = LM3533_REG_OUTPUT_CONF1;
|
||||
shift = 2 * lvled;
|
||||
} else {
|
||||
reg = LM3533_REG_OUTPUT_CONF2;
|
||||
shift = 2 * (lvled - 4);
|
||||
}
|
||||
|
||||
mask = LM3533_LED_ID_MASK << shift;
|
||||
val = led << shift;
|
||||
|
||||
ret = lm3533_update(lm3533, reg, val, mask);
|
||||
if (ret)
|
||||
dev_err(lm3533->dev, "failed to set lvled config\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void lm3533_enable(struct lm3533 *lm3533)
|
||||
{
|
||||
if (gpio_is_valid(lm3533->gpio_hwen))
|
||||
gpio_set_value(lm3533->gpio_hwen, 1);
|
||||
}
|
||||
|
||||
static void lm3533_disable(struct lm3533 *lm3533)
|
||||
{
|
||||
if (gpio_is_valid(lm3533->gpio_hwen))
|
||||
gpio_set_value(lm3533->gpio_hwen, 0);
|
||||
}
|
||||
|
||||
enum lm3533_attribute_type {
|
||||
LM3533_ATTR_TYPE_BACKLIGHT,
|
||||
LM3533_ATTR_TYPE_LED,
|
||||
};
|
||||
|
||||
struct lm3533_device_attribute {
|
||||
struct device_attribute dev_attr;
|
||||
enum lm3533_attribute_type type;
|
||||
union {
|
||||
struct {
|
||||
u8 id;
|
||||
} output;
|
||||
} u;
|
||||
};
|
||||
|
||||
#define to_lm3533_dev_attr(_attr) \
|
||||
container_of(_attr, struct lm3533_device_attribute, dev_attr)
|
||||
|
||||
static ssize_t show_output(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct lm3533 *lm3533 = dev_get_drvdata(dev);
|
||||
struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(attr);
|
||||
int id = lattr->u.output.id;
|
||||
u8 reg;
|
||||
u8 val;
|
||||
u8 mask;
|
||||
int shift;
|
||||
int ret;
|
||||
|
||||
if (lattr->type == LM3533_ATTR_TYPE_BACKLIGHT) {
|
||||
reg = LM3533_REG_OUTPUT_CONF1;
|
||||
shift = id - 1;
|
||||
mask = LM3533_BL_ID_MASK << shift;
|
||||
} else {
|
||||
if (id < 4) {
|
||||
reg = LM3533_REG_OUTPUT_CONF1;
|
||||
shift = 2 * id;
|
||||
} else {
|
||||
reg = LM3533_REG_OUTPUT_CONF2;
|
||||
shift = 2 * (id - 4);
|
||||
}
|
||||
mask = LM3533_LED_ID_MASK << shift;
|
||||
}
|
||||
|
||||
ret = lm3533_read(lm3533, reg, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = (val & mask) >> shift;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%u\n", val);
|
||||
}
|
||||
|
||||
static ssize_t store_output(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct lm3533 *lm3533 = dev_get_drvdata(dev);
|
||||
struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(attr);
|
||||
int id = lattr->u.output.id;
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
if (kstrtou8(buf, 0, &val))
|
||||
return -EINVAL;
|
||||
|
||||
if (lattr->type == LM3533_ATTR_TYPE_BACKLIGHT)
|
||||
ret = lm3533_set_hvled_config(lm3533, id, val);
|
||||
else
|
||||
ret = lm3533_set_lvled_config(lm3533, id, val);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
#define LM3533_OUTPUT_ATTR(_name, _mode, _show, _store, _type, _id) \
|
||||
struct lm3533_device_attribute lm3533_dev_attr_##_name = \
|
||||
{ .dev_attr = __ATTR(_name, _mode, _show, _store), \
|
||||
.type = _type, \
|
||||
.u.output = { .id = _id }, }
|
||||
|
||||
#define LM3533_OUTPUT_ATTR_RW(_name, _type, _id) \
|
||||
LM3533_OUTPUT_ATTR(output_##_name, S_IRUGO | S_IWUSR, \
|
||||
show_output, store_output, _type, _id)
|
||||
|
||||
#define LM3533_OUTPUT_HVLED_ATTR_RW(_nr) \
|
||||
LM3533_OUTPUT_ATTR_RW(hvled##_nr, LM3533_ATTR_TYPE_BACKLIGHT, _nr)
|
||||
#define LM3533_OUTPUT_LVLED_ATTR_RW(_nr) \
|
||||
LM3533_OUTPUT_ATTR_RW(lvled##_nr, LM3533_ATTR_TYPE_LED, _nr)
|
||||
/*
|
||||
* Output config:
|
||||
*
|
||||
* output_hvled<nr> 0-1
|
||||
* output_lvled<nr> 0-3
|
||||
*/
|
||||
static LM3533_OUTPUT_HVLED_ATTR_RW(1);
|
||||
static LM3533_OUTPUT_HVLED_ATTR_RW(2);
|
||||
static LM3533_OUTPUT_LVLED_ATTR_RW(1);
|
||||
static LM3533_OUTPUT_LVLED_ATTR_RW(2);
|
||||
static LM3533_OUTPUT_LVLED_ATTR_RW(3);
|
||||
static LM3533_OUTPUT_LVLED_ATTR_RW(4);
|
||||
static LM3533_OUTPUT_LVLED_ATTR_RW(5);
|
||||
|
||||
static struct attribute *lm3533_attributes[] = {
|
||||
&lm3533_dev_attr_output_hvled1.dev_attr.attr,
|
||||
&lm3533_dev_attr_output_hvled2.dev_attr.attr,
|
||||
&lm3533_dev_attr_output_lvled1.dev_attr.attr,
|
||||
&lm3533_dev_attr_output_lvled2.dev_attr.attr,
|
||||
&lm3533_dev_attr_output_lvled3.dev_attr.attr,
|
||||
&lm3533_dev_attr_output_lvled4.dev_attr.attr,
|
||||
&lm3533_dev_attr_output_lvled5.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
#define to_dev_attr(_attr) \
|
||||
container_of(_attr, struct device_attribute, attr)
|
||||
|
||||
static umode_t lm3533_attr_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct lm3533 *lm3533 = dev_get_drvdata(dev);
|
||||
struct device_attribute *dattr = to_dev_attr(attr);
|
||||
struct lm3533_device_attribute *lattr = to_lm3533_dev_attr(dattr);
|
||||
enum lm3533_attribute_type type = lattr->type;
|
||||
umode_t mode = attr->mode;
|
||||
|
||||
if (!lm3533->have_backlights && type == LM3533_ATTR_TYPE_BACKLIGHT)
|
||||
mode = 0;
|
||||
else if (!lm3533->have_leds && type == LM3533_ATTR_TYPE_LED)
|
||||
mode = 0;
|
||||
|
||||
return mode;
|
||||
};
|
||||
|
||||
static struct attribute_group lm3533_attribute_group = {
|
||||
.is_visible = lm3533_attr_is_visible,
|
||||
.attrs = lm3533_attributes
|
||||
};
|
||||
|
||||
static int __devinit lm3533_device_als_init(struct lm3533 *lm3533)
|
||||
{
|
||||
struct lm3533_platform_data *pdata = lm3533->dev->platform_data;
|
||||
int ret;
|
||||
|
||||
if (!pdata->als)
|
||||
return 0;
|
||||
|
||||
lm3533_als_devs[0].platform_data = pdata->als;
|
||||
lm3533_als_devs[0].pdata_size = sizeof(*pdata->als);
|
||||
|
||||
ret = mfd_add_devices(lm3533->dev, 0, lm3533_als_devs, 1, NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(lm3533->dev, "failed to add ALS device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
lm3533->have_als = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit lm3533_device_bl_init(struct lm3533 *lm3533)
|
||||
{
|
||||
struct lm3533_platform_data *pdata = lm3533->dev->platform_data;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
if (!pdata->backlights || pdata->num_backlights == 0)
|
||||
return 0;
|
||||
|
||||
if (pdata->num_backlights > ARRAY_SIZE(lm3533_bl_devs))
|
||||
pdata->num_backlights = ARRAY_SIZE(lm3533_bl_devs);
|
||||
|
||||
for (i = 0; i < pdata->num_backlights; ++i) {
|
||||
lm3533_bl_devs[i].platform_data = &pdata->backlights[i];
|
||||
lm3533_bl_devs[i].pdata_size = sizeof(pdata->backlights[i]);
|
||||
}
|
||||
|
||||
ret = mfd_add_devices(lm3533->dev, 0, lm3533_bl_devs,
|
||||
pdata->num_backlights, NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(lm3533->dev, "failed to add backlight devices\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
lm3533->have_backlights = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit lm3533_device_led_init(struct lm3533 *lm3533)
|
||||
{
|
||||
struct lm3533_platform_data *pdata = lm3533->dev->platform_data;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
if (!pdata->leds || pdata->num_leds == 0)
|
||||
return 0;
|
||||
|
||||
if (pdata->num_leds > ARRAY_SIZE(lm3533_led_devs))
|
||||
pdata->num_leds = ARRAY_SIZE(lm3533_led_devs);
|
||||
|
||||
for (i = 0; i < pdata->num_leds; ++i) {
|
||||
lm3533_led_devs[i].platform_data = &pdata->leds[i];
|
||||
lm3533_led_devs[i].pdata_size = sizeof(pdata->leds[i]);
|
||||
}
|
||||
|
||||
ret = mfd_add_devices(lm3533->dev, 0, lm3533_led_devs,
|
||||
pdata->num_leds, NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(lm3533->dev, "failed to add LED devices\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
lm3533->have_leds = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit lm3533_device_setup(struct lm3533 *lm3533,
|
||||
struct lm3533_platform_data *pdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = lm3533_set_boost_freq(lm3533, pdata->boost_freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = lm3533_set_boost_ovp(lm3533, pdata->boost_ovp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit lm3533_device_init(struct lm3533 *lm3533)
|
||||
{
|
||||
struct lm3533_platform_data *pdata = lm3533->dev->platform_data;
|
||||
int ret;
|
||||
|
||||
dev_dbg(lm3533->dev, "%s\n", __func__);
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(lm3533->dev, "no platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
lm3533->gpio_hwen = pdata->gpio_hwen;
|
||||
|
||||
dev_set_drvdata(lm3533->dev, lm3533);
|
||||
|
||||
if (gpio_is_valid(lm3533->gpio_hwen)) {
|
||||
ret = gpio_request_one(lm3533->gpio_hwen, GPIOF_OUT_INIT_LOW,
|
||||
"lm3533-hwen");
|
||||
if (ret < 0) {
|
||||
dev_err(lm3533->dev,
|
||||
"failed to request HWEN GPIO %d\n",
|
||||
lm3533->gpio_hwen);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
lm3533_enable(lm3533);
|
||||
|
||||
ret = lm3533_device_setup(lm3533, pdata);
|
||||
if (ret)
|
||||
goto err_disable;
|
||||
|
||||
lm3533_device_als_init(lm3533);
|
||||
lm3533_device_bl_init(lm3533);
|
||||
lm3533_device_led_init(lm3533);
|
||||
|
||||
ret = sysfs_create_group(&lm3533->dev->kobj, &lm3533_attribute_group);
|
||||
if (ret < 0) {
|
||||
dev_err(lm3533->dev, "failed to create sysfs attributes\n");
|
||||
goto err_unregister;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister:
|
||||
mfd_remove_devices(lm3533->dev);
|
||||
err_disable:
|
||||
lm3533_disable(lm3533);
|
||||
if (gpio_is_valid(lm3533->gpio_hwen))
|
||||
gpio_free(lm3533->gpio_hwen);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __devexit lm3533_device_exit(struct lm3533 *lm3533)
|
||||
{
|
||||
dev_dbg(lm3533->dev, "%s\n", __func__);
|
||||
|
||||
sysfs_remove_group(&lm3533->dev->kobj, &lm3533_attribute_group);
|
||||
|
||||
mfd_remove_devices(lm3533->dev);
|
||||
lm3533_disable(lm3533);
|
||||
if (gpio_is_valid(lm3533->gpio_hwen))
|
||||
gpio_free(lm3533->gpio_hwen);
|
||||
}
|
||||
|
||||
static bool lm3533_readable_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0x10 ... 0x2c:
|
||||
case 0x30 ... 0x38:
|
||||
case 0x40 ... 0x45:
|
||||
case 0x50 ... 0x57:
|
||||
case 0x60 ... 0x6e:
|
||||
case 0x70 ... 0x75:
|
||||
case 0x80 ... 0x85:
|
||||
case 0x90 ... 0x95:
|
||||
case 0xa0 ... 0xa5:
|
||||
case 0xb0 ... 0xb2:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool lm3533_volatile_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0x34 ... 0x36: /* zone */
|
||||
case 0x37 ... 0x38: /* adc */
|
||||
case 0xb0 ... 0xb1: /* fault */
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool lm3533_precious_register(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0x34: /* zone */
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static struct regmap_config regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = LM3533_REG_MAX,
|
||||
.readable_reg = lm3533_readable_register,
|
||||
.volatile_reg = lm3533_volatile_register,
|
||||
.precious_reg = lm3533_precious_register,
|
||||
};
|
||||
|
||||
static int __devinit lm3533_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct lm3533 *lm3533;
|
||||
int ret;
|
||||
|
||||
dev_dbg(&i2c->dev, "%s\n", __func__);
|
||||
|
||||
lm3533 = devm_kzalloc(&i2c->dev, sizeof(*lm3533), GFP_KERNEL);
|
||||
if (!lm3533)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(i2c, lm3533);
|
||||
|
||||
lm3533->regmap = devm_regmap_init_i2c(i2c, ®map_config);
|
||||
if (IS_ERR(lm3533->regmap))
|
||||
return PTR_ERR(lm3533->regmap);
|
||||
|
||||
lm3533->dev = &i2c->dev;
|
||||
lm3533->irq = i2c->irq;
|
||||
|
||||
ret = lm3533_device_init(lm3533);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit lm3533_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
struct lm3533 *lm3533 = i2c_get_clientdata(i2c);
|
||||
|
||||
dev_dbg(&i2c->dev, "%s\n", __func__);
|
||||
|
||||
lm3533_device_exit(lm3533);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id lm3533_i2c_ids[] = {
|
||||
{ "lm3533", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lm3533_i2c_ids);
|
||||
|
||||
static struct i2c_driver lm3533_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "lm3533",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = lm3533_i2c_ids,
|
||||
.probe = lm3533_i2c_probe,
|
||||
.remove = __devexit_p(lm3533_i2c_remove),
|
||||
};
|
||||
|
||||
static int __init lm3533_i2c_init(void)
|
||||
{
|
||||
return i2c_add_driver(&lm3533_i2c_driver);
|
||||
}
|
||||
subsys_initcall(lm3533_i2c_init);
|
||||
|
||||
static void __exit lm3533_i2c_exit(void)
|
||||
{
|
||||
i2c_del_driver(&lm3533_i2c_driver);
|
||||
}
|
||||
module_exit(lm3533_i2c_exit);
|
||||
|
||||
MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>");
|
||||
MODULE_DESCRIPTION("LM3533 Core");
|
||||
MODULE_LICENSE("GPL");
|
148
drivers/mfd/lm3533-ctrlbank.c
Normal file
148
drivers/mfd/lm3533-ctrlbank.c
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* lm3533-ctrlbank.c -- LM3533 Generic Control Bank interface
|
||||
*
|
||||
* Copyright (C) 2011-2012 Texas Instruments
|
||||
*
|
||||
* Author: Johan Hovold <jhovold@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/mfd/lm3533.h>
|
||||
|
||||
|
||||
#define LM3533_MAX_CURRENT_MIN 5000
|
||||
#define LM3533_MAX_CURRENT_MAX 29800
|
||||
#define LM3533_MAX_CURRENT_STEP 800
|
||||
|
||||
#define LM3533_BRIGHTNESS_MAX 255
|
||||
#define LM3533_PWM_MAX 0x3f
|
||||
|
||||
#define LM3533_REG_PWM_BASE 0x14
|
||||
#define LM3533_REG_MAX_CURRENT_BASE 0x1f
|
||||
#define LM3533_REG_CTRLBANK_ENABLE 0x27
|
||||
#define LM3533_REG_BRIGHTNESS_BASE 0x40
|
||||
|
||||
|
||||
static inline u8 lm3533_ctrlbank_get_reg(struct lm3533_ctrlbank *cb, u8 base)
|
||||
{
|
||||
return base + cb->id;
|
||||
}
|
||||
|
||||
int lm3533_ctrlbank_enable(struct lm3533_ctrlbank *cb)
|
||||
{
|
||||
u8 mask;
|
||||
int ret;
|
||||
|
||||
dev_dbg(cb->dev, "%s - %d\n", __func__, cb->id);
|
||||
|
||||
mask = 1 << cb->id;
|
||||
ret = lm3533_update(cb->lm3533, LM3533_REG_CTRLBANK_ENABLE,
|
||||
mask, mask);
|
||||
if (ret)
|
||||
dev_err(cb->dev, "failed to enable ctrlbank %d\n", cb->id);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lm3533_ctrlbank_enable);
|
||||
|
||||
int lm3533_ctrlbank_disable(struct lm3533_ctrlbank *cb)
|
||||
{
|
||||
u8 mask;
|
||||
int ret;
|
||||
|
||||
dev_dbg(cb->dev, "%s - %d\n", __func__, cb->id);
|
||||
|
||||
mask = 1 << cb->id;
|
||||
ret = lm3533_update(cb->lm3533, LM3533_REG_CTRLBANK_ENABLE, 0, mask);
|
||||
if (ret)
|
||||
dev_err(cb->dev, "failed to disable ctrlbank %d\n", cb->id);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lm3533_ctrlbank_disable);
|
||||
|
||||
/*
|
||||
* Full-scale current.
|
||||
*
|
||||
* imax 5000 - 29800 uA (800 uA step)
|
||||
*/
|
||||
int lm3533_ctrlbank_set_max_current(struct lm3533_ctrlbank *cb, u16 imax)
|
||||
{
|
||||
u8 reg;
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
if (imax < LM3533_MAX_CURRENT_MIN || imax > LM3533_MAX_CURRENT_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
val = (imax - LM3533_MAX_CURRENT_MIN) / LM3533_MAX_CURRENT_STEP;
|
||||
|
||||
reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_MAX_CURRENT_BASE);
|
||||
ret = lm3533_write(cb->lm3533, reg, val);
|
||||
if (ret)
|
||||
dev_err(cb->dev, "failed to set max current\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lm3533_ctrlbank_set_max_current);
|
||||
|
||||
#define lm3533_ctrlbank_set(_name, _NAME) \
|
||||
int lm3533_ctrlbank_set_##_name(struct lm3533_ctrlbank *cb, u8 val) \
|
||||
{ \
|
||||
u8 reg; \
|
||||
int ret; \
|
||||
\
|
||||
if (val > LM3533_##_NAME##_MAX) \
|
||||
return -EINVAL; \
|
||||
\
|
||||
reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_##_NAME##_BASE); \
|
||||
ret = lm3533_write(cb->lm3533, reg, val); \
|
||||
if (ret) \
|
||||
dev_err(cb->dev, "failed to set " #_name "\n"); \
|
||||
\
|
||||
return ret; \
|
||||
} \
|
||||
EXPORT_SYMBOL_GPL(lm3533_ctrlbank_set_##_name);
|
||||
|
||||
#define lm3533_ctrlbank_get(_name, _NAME) \
|
||||
int lm3533_ctrlbank_get_##_name(struct lm3533_ctrlbank *cb, u8 *val) \
|
||||
{ \
|
||||
u8 reg; \
|
||||
int ret; \
|
||||
\
|
||||
reg = lm3533_ctrlbank_get_reg(cb, LM3533_REG_##_NAME##_BASE); \
|
||||
ret = lm3533_read(cb->lm3533, reg, val); \
|
||||
if (ret) \
|
||||
dev_err(cb->dev, "failed to get " #_name "\n"); \
|
||||
\
|
||||
return ret; \
|
||||
} \
|
||||
EXPORT_SYMBOL_GPL(lm3533_ctrlbank_get_##_name);
|
||||
|
||||
lm3533_ctrlbank_set(brightness, BRIGHTNESS);
|
||||
lm3533_ctrlbank_get(brightness, BRIGHTNESS);
|
||||
|
||||
/*
|
||||
* PWM-input control mask:
|
||||
*
|
||||
* bit 5 - PWM-input enabled in Zone 4
|
||||
* bit 4 - PWM-input enabled in Zone 3
|
||||
* bit 3 - PWM-input enabled in Zone 2
|
||||
* bit 2 - PWM-input enabled in Zone 1
|
||||
* bit 1 - PWM-input enabled in Zone 0
|
||||
* bit 0 - PWM-input enabled
|
||||
*/
|
||||
lm3533_ctrlbank_set(pwm, PWM);
|
||||
lm3533_ctrlbank_get(pwm, PWM);
|
||||
|
||||
|
||||
MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>");
|
||||
MODULE_DESCRIPTION("LM3533 Control Bank interface");
|
||||
MODULE_LICENSE("GPL");
|
888
drivers/mfd/lpc_ich.c
Normal file
888
drivers/mfd/lpc_ich.c
Normal file
@ -0,0 +1,888 @@
|
||||
/*
|
||||
* lpc_ich.c - LPC interface for Intel ICH
|
||||
*
|
||||
* LPC bridge function of the Intel ICH contains many other
|
||||
* functional units, such as Interrupt controllers, Timers,
|
||||
* Power Management, System Management, GPIO, RTC, and LPC
|
||||
* Configuration Registers.
|
||||
*
|
||||
* This driver is derived from lpc_sch.
|
||||
|
||||
* Copyright (c) 2011 Extreme Engineering Solution, Inc.
|
||||
* Author: Aaron Sierra <asierra@xes-inc.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* This driver supports the following I/O Controller hubs:
|
||||
* (See the intel documentation on http://developer.intel.com.)
|
||||
* document number 290655-003, 290677-014: 82801AA (ICH), 82801AB (ICHO)
|
||||
* document number 290687-002, 298242-027: 82801BA (ICH2)
|
||||
* document number 290733-003, 290739-013: 82801CA (ICH3-S)
|
||||
* document number 290716-001, 290718-007: 82801CAM (ICH3-M)
|
||||
* document number 290744-001, 290745-025: 82801DB (ICH4)
|
||||
* document number 252337-001, 252663-008: 82801DBM (ICH4-M)
|
||||
* document number 273599-001, 273645-002: 82801E (C-ICH)
|
||||
* document number 252516-001, 252517-028: 82801EB (ICH5), 82801ER (ICH5R)
|
||||
* document number 300641-004, 300884-013: 6300ESB
|
||||
* document number 301473-002, 301474-026: 82801F (ICH6)
|
||||
* document number 313082-001, 313075-006: 631xESB, 632xESB
|
||||
* document number 307013-003, 307014-024: 82801G (ICH7)
|
||||
* document number 322896-001, 322897-001: NM10
|
||||
* document number 313056-003, 313057-017: 82801H (ICH8)
|
||||
* document number 316972-004, 316973-012: 82801I (ICH9)
|
||||
* document number 319973-002, 319974-002: 82801J (ICH10)
|
||||
* document number 322169-001, 322170-003: 5 Series, 3400 Series (PCH)
|
||||
* document number 320066-003, 320257-008: EP80597 (IICH)
|
||||
* document number 324645-001, 324646-001: Cougar Point (CPT)
|
||||
* document number TBD : Patsburg (PBG)
|
||||
* document number TBD : DH89xxCC
|
||||
* document number TBD : Panther Point
|
||||
* document number TBD : Lynx Point
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/lpc_ich.h>
|
||||
|
||||
#define ACPIBASE 0x40
|
||||
#define ACPIBASE_GPE_OFF 0x28
|
||||
#define ACPIBASE_GPE_END 0x2f
|
||||
#define ACPIBASE_SMI_OFF 0x30
|
||||
#define ACPIBASE_SMI_END 0x33
|
||||
#define ACPIBASE_TCO_OFF 0x60
|
||||
#define ACPIBASE_TCO_END 0x7f
|
||||
#define ACPICTRL 0x44
|
||||
|
||||
#define ACPIBASE_GCS_OFF 0x3410
|
||||
#define ACPIBASE_GCS_END 0x3414
|
||||
|
||||
#define GPIOBASE 0x48
|
||||
#define GPIOCTRL 0x4C
|
||||
|
||||
#define RCBABASE 0xf0
|
||||
|
||||
#define wdt_io_res(i) wdt_res(0, i)
|
||||
#define wdt_mem_res(i) wdt_res(ICH_RES_MEM_OFF, i)
|
||||
#define wdt_res(b, i) (&wdt_ich_res[(b) + (i)])
|
||||
|
||||
static int lpc_ich_acpi_save = -1;
|
||||
static int lpc_ich_gpio_save = -1;
|
||||
|
||||
static struct resource wdt_ich_res[] = {
|
||||
/* ACPI - TCO */
|
||||
{
|
||||
.flags = IORESOURCE_IO,
|
||||
},
|
||||
/* ACPI - SMI */
|
||||
{
|
||||
.flags = IORESOURCE_IO,
|
||||
},
|
||||
/* GCS */
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource gpio_ich_res[] = {
|
||||
/* GPIO */
|
||||
{
|
||||
.flags = IORESOURCE_IO,
|
||||
},
|
||||
/* ACPI - GPE0 */
|
||||
{
|
||||
.flags = IORESOURCE_IO,
|
||||
},
|
||||
};
|
||||
|
||||
enum lpc_cells {
|
||||
LPC_WDT = 0,
|
||||
LPC_GPIO,
|
||||
};
|
||||
|
||||
static struct mfd_cell lpc_ich_cells[] = {
|
||||
[LPC_WDT] = {
|
||||
.name = "iTCO_wdt",
|
||||
.num_resources = ARRAY_SIZE(wdt_ich_res),
|
||||
.resources = wdt_ich_res,
|
||||
.ignore_resource_conflicts = true,
|
||||
},
|
||||
[LPC_GPIO] = {
|
||||
.name = "gpio_ich",
|
||||
.num_resources = ARRAY_SIZE(gpio_ich_res),
|
||||
.resources = gpio_ich_res,
|
||||
.ignore_resource_conflicts = true,
|
||||
},
|
||||
};
|
||||
|
||||
/* chipset related info */
|
||||
enum lpc_chipsets {
|
||||
LPC_ICH = 0, /* ICH */
|
||||
LPC_ICH0, /* ICH0 */
|
||||
LPC_ICH2, /* ICH2 */
|
||||
LPC_ICH2M, /* ICH2-M */
|
||||
LPC_ICH3, /* ICH3-S */
|
||||
LPC_ICH3M, /* ICH3-M */
|
||||
LPC_ICH4, /* ICH4 */
|
||||
LPC_ICH4M, /* ICH4-M */
|
||||
LPC_CICH, /* C-ICH */
|
||||
LPC_ICH5, /* ICH5 & ICH5R */
|
||||
LPC_6300ESB, /* 6300ESB */
|
||||
LPC_ICH6, /* ICH6 & ICH6R */
|
||||
LPC_ICH6M, /* ICH6-M */
|
||||
LPC_ICH6W, /* ICH6W & ICH6RW */
|
||||
LPC_631XESB, /* 631xESB/632xESB */
|
||||
LPC_ICH7, /* ICH7 & ICH7R */
|
||||
LPC_ICH7DH, /* ICH7DH */
|
||||
LPC_ICH7M, /* ICH7-M & ICH7-U */
|
||||
LPC_ICH7MDH, /* ICH7-M DH */
|
||||
LPC_NM10, /* NM10 */
|
||||
LPC_ICH8, /* ICH8 & ICH8R */
|
||||
LPC_ICH8DH, /* ICH8DH */
|
||||
LPC_ICH8DO, /* ICH8DO */
|
||||
LPC_ICH8M, /* ICH8M */
|
||||
LPC_ICH8ME, /* ICH8M-E */
|
||||
LPC_ICH9, /* ICH9 */
|
||||
LPC_ICH9R, /* ICH9R */
|
||||
LPC_ICH9DH, /* ICH9DH */
|
||||
LPC_ICH9DO, /* ICH9DO */
|
||||
LPC_ICH9M, /* ICH9M */
|
||||
LPC_ICH9ME, /* ICH9M-E */
|
||||
LPC_ICH10, /* ICH10 */
|
||||
LPC_ICH10R, /* ICH10R */
|
||||
LPC_ICH10D, /* ICH10D */
|
||||
LPC_ICH10DO, /* ICH10DO */
|
||||
LPC_PCH, /* PCH Desktop Full Featured */
|
||||
LPC_PCHM, /* PCH Mobile Full Featured */
|
||||
LPC_P55, /* P55 */
|
||||
LPC_PM55, /* PM55 */
|
||||
LPC_H55, /* H55 */
|
||||
LPC_QM57, /* QM57 */
|
||||
LPC_H57, /* H57 */
|
||||
LPC_HM55, /* HM55 */
|
||||
LPC_Q57, /* Q57 */
|
||||
LPC_HM57, /* HM57 */
|
||||
LPC_PCHMSFF, /* PCH Mobile SFF Full Featured */
|
||||
LPC_QS57, /* QS57 */
|
||||
LPC_3400, /* 3400 */
|
||||
LPC_3420, /* 3420 */
|
||||
LPC_3450, /* 3450 */
|
||||
LPC_EP80579, /* EP80579 */
|
||||
LPC_CPT, /* Cougar Point */
|
||||
LPC_CPTD, /* Cougar Point Desktop */
|
||||
LPC_CPTM, /* Cougar Point Mobile */
|
||||
LPC_PBG, /* Patsburg */
|
||||
LPC_DH89XXCC, /* DH89xxCC */
|
||||
LPC_PPT, /* Panther Point */
|
||||
LPC_LPT, /* Lynx Point */
|
||||
};
|
||||
|
||||
struct lpc_ich_info lpc_chipset_info[] __devinitdata = {
|
||||
[LPC_ICH] = {
|
||||
.name = "ICH",
|
||||
.iTCO_version = 1,
|
||||
},
|
||||
[LPC_ICH0] = {
|
||||
.name = "ICH0",
|
||||
.iTCO_version = 1,
|
||||
},
|
||||
[LPC_ICH2] = {
|
||||
.name = "ICH2",
|
||||
.iTCO_version = 1,
|
||||
},
|
||||
[LPC_ICH2M] = {
|
||||
.name = "ICH2-M",
|
||||
.iTCO_version = 1,
|
||||
},
|
||||
[LPC_ICH3] = {
|
||||
.name = "ICH3-S",
|
||||
.iTCO_version = 1,
|
||||
},
|
||||
[LPC_ICH3M] = {
|
||||
.name = "ICH3-M",
|
||||
.iTCO_version = 1,
|
||||
},
|
||||
[LPC_ICH4] = {
|
||||
.name = "ICH4",
|
||||
.iTCO_version = 1,
|
||||
},
|
||||
[LPC_ICH4M] = {
|
||||
.name = "ICH4-M",
|
||||
.iTCO_version = 1,
|
||||
},
|
||||
[LPC_CICH] = {
|
||||
.name = "C-ICH",
|
||||
.iTCO_version = 1,
|
||||
},
|
||||
[LPC_ICH5] = {
|
||||
.name = "ICH5 or ICH5R",
|
||||
.iTCO_version = 1,
|
||||
},
|
||||
[LPC_6300ESB] = {
|
||||
.name = "6300ESB",
|
||||
.iTCO_version = 1,
|
||||
},
|
||||
[LPC_ICH6] = {
|
||||
.name = "ICH6 or ICH6R",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V6_GPIO,
|
||||
},
|
||||
[LPC_ICH6M] = {
|
||||
.name = "ICH6-M",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V6_GPIO,
|
||||
},
|
||||
[LPC_ICH6W] = {
|
||||
.name = "ICH6W or ICH6RW",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V6_GPIO,
|
||||
},
|
||||
[LPC_631XESB] = {
|
||||
.name = "631xESB/632xESB",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V6_GPIO,
|
||||
},
|
||||
[LPC_ICH7] = {
|
||||
.name = "ICH7 or ICH7R",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V7_GPIO,
|
||||
},
|
||||
[LPC_ICH7DH] = {
|
||||
.name = "ICH7DH",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V7_GPIO,
|
||||
},
|
||||
[LPC_ICH7M] = {
|
||||
.name = "ICH7-M or ICH7-U",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V7_GPIO,
|
||||
},
|
||||
[LPC_ICH7MDH] = {
|
||||
.name = "ICH7-M DH",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V7_GPIO,
|
||||
},
|
||||
[LPC_NM10] = {
|
||||
.name = "NM10",
|
||||
.iTCO_version = 2,
|
||||
},
|
||||
[LPC_ICH8] = {
|
||||
.name = "ICH8 or ICH8R",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V7_GPIO,
|
||||
},
|
||||
[LPC_ICH8DH] = {
|
||||
.name = "ICH8DH",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V7_GPIO,
|
||||
},
|
||||
[LPC_ICH8DO] = {
|
||||
.name = "ICH8DO",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V7_GPIO,
|
||||
},
|
||||
[LPC_ICH8M] = {
|
||||
.name = "ICH8M",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V7_GPIO,
|
||||
},
|
||||
[LPC_ICH8ME] = {
|
||||
.name = "ICH8M-E",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V7_GPIO,
|
||||
},
|
||||
[LPC_ICH9] = {
|
||||
.name = "ICH9",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V9_GPIO,
|
||||
},
|
||||
[LPC_ICH9R] = {
|
||||
.name = "ICH9R",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V9_GPIO,
|
||||
},
|
||||
[LPC_ICH9DH] = {
|
||||
.name = "ICH9DH",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V9_GPIO,
|
||||
},
|
||||
[LPC_ICH9DO] = {
|
||||
.name = "ICH9DO",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V9_GPIO,
|
||||
},
|
||||
[LPC_ICH9M] = {
|
||||
.name = "ICH9M",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V9_GPIO,
|
||||
},
|
||||
[LPC_ICH9ME] = {
|
||||
.name = "ICH9M-E",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V9_GPIO,
|
||||
},
|
||||
[LPC_ICH10] = {
|
||||
.name = "ICH10",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V10CONS_GPIO,
|
||||
},
|
||||
[LPC_ICH10R] = {
|
||||
.name = "ICH10R",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V10CONS_GPIO,
|
||||
},
|
||||
[LPC_ICH10D] = {
|
||||
.name = "ICH10D",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V10CORP_GPIO,
|
||||
},
|
||||
[LPC_ICH10DO] = {
|
||||
.name = "ICH10DO",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V10CORP_GPIO,
|
||||
},
|
||||
[LPC_PCH] = {
|
||||
.name = "PCH Desktop Full Featured",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V5_GPIO,
|
||||
},
|
||||
[LPC_PCHM] = {
|
||||
.name = "PCH Mobile Full Featured",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V5_GPIO,
|
||||
},
|
||||
[LPC_P55] = {
|
||||
.name = "P55",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V5_GPIO,
|
||||
},
|
||||
[LPC_PM55] = {
|
||||
.name = "PM55",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V5_GPIO,
|
||||
},
|
||||
[LPC_H55] = {
|
||||
.name = "H55",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V5_GPIO,
|
||||
},
|
||||
[LPC_QM57] = {
|
||||
.name = "QM57",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V5_GPIO,
|
||||
},
|
||||
[LPC_H57] = {
|
||||
.name = "H57",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V5_GPIO,
|
||||
},
|
||||
[LPC_HM55] = {
|
||||
.name = "HM55",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V5_GPIO,
|
||||
},
|
||||
[LPC_Q57] = {
|
||||
.name = "Q57",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V5_GPIO,
|
||||
},
|
||||
[LPC_HM57] = {
|
||||
.name = "HM57",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V5_GPIO,
|
||||
},
|
||||
[LPC_PCHMSFF] = {
|
||||
.name = "PCH Mobile SFF Full Featured",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V5_GPIO,
|
||||
},
|
||||
[LPC_QS57] = {
|
||||
.name = "QS57",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V5_GPIO,
|
||||
},
|
||||
[LPC_3400] = {
|
||||
.name = "3400",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V5_GPIO,
|
||||
},
|
||||
[LPC_3420] = {
|
||||
.name = "3420",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V5_GPIO,
|
||||
},
|
||||
[LPC_3450] = {
|
||||
.name = "3450",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V5_GPIO,
|
||||
},
|
||||
[LPC_EP80579] = {
|
||||
.name = "EP80579",
|
||||
.iTCO_version = 2,
|
||||
},
|
||||
[LPC_CPT] = {
|
||||
.name = "Cougar Point",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V5_GPIO,
|
||||
},
|
||||
[LPC_CPTD] = {
|
||||
.name = "Cougar Point Desktop",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V5_GPIO,
|
||||
},
|
||||
[LPC_CPTM] = {
|
||||
.name = "Cougar Point Mobile",
|
||||
.iTCO_version = 2,
|
||||
.gpio_version = ICH_V5_GPIO,
|
||||
},
|
||||
[LPC_PBG] = {
|
||||
.name = "Patsburg",
|
||||
.iTCO_version = 2,
|
||||
},
|
||||
[LPC_DH89XXCC] = {
|
||||
.name = "DH89xxCC",
|
||||
.iTCO_version = 2,
|
||||
},
|
||||
[LPC_PPT] = {
|
||||
.name = "Panther Point",
|
||||
.iTCO_version = 2,
|
||||
},
|
||||
[LPC_LPT] = {
|
||||
.name = "Lynx Point",
|
||||
.iTCO_version = 2,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* This data only exists for exporting the supported PCI ids
|
||||
* via MODULE_DEVICE_TABLE. We do not actually register a
|
||||
* pci_driver, because the I/O Controller Hub has also other
|
||||
* functions that probably will be registered by other drivers.
|
||||
*/
|
||||
static DEFINE_PCI_DEVICE_TABLE(lpc_ich_ids) = {
|
||||
{ PCI_VDEVICE(INTEL, 0x2410), LPC_ICH},
|
||||
{ PCI_VDEVICE(INTEL, 0x2420), LPC_ICH0},
|
||||
{ PCI_VDEVICE(INTEL, 0x2440), LPC_ICH2},
|
||||
{ PCI_VDEVICE(INTEL, 0x244c), LPC_ICH2M},
|
||||
{ PCI_VDEVICE(INTEL, 0x2480), LPC_ICH3},
|
||||
{ PCI_VDEVICE(INTEL, 0x248c), LPC_ICH3M},
|
||||
{ PCI_VDEVICE(INTEL, 0x24c0), LPC_ICH4},
|
||||
{ PCI_VDEVICE(INTEL, 0x24cc), LPC_ICH4M},
|
||||
{ PCI_VDEVICE(INTEL, 0x2450), LPC_CICH},
|
||||
{ PCI_VDEVICE(INTEL, 0x24d0), LPC_ICH5},
|
||||
{ PCI_VDEVICE(INTEL, 0x25a1), LPC_6300ESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2640), LPC_ICH6},
|
||||
{ PCI_VDEVICE(INTEL, 0x2641), LPC_ICH6M},
|
||||
{ PCI_VDEVICE(INTEL, 0x2642), LPC_ICH6W},
|
||||
{ PCI_VDEVICE(INTEL, 0x2670), LPC_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2671), LPC_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2672), LPC_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2673), LPC_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2674), LPC_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2675), LPC_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2676), LPC_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2677), LPC_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2678), LPC_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2679), LPC_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x267a), LPC_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x267b), LPC_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x267c), LPC_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x267d), LPC_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x267e), LPC_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x267f), LPC_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x27b8), LPC_ICH7},
|
||||
{ PCI_VDEVICE(INTEL, 0x27b0), LPC_ICH7DH},
|
||||
{ PCI_VDEVICE(INTEL, 0x27b9), LPC_ICH7M},
|
||||
{ PCI_VDEVICE(INTEL, 0x27bd), LPC_ICH7MDH},
|
||||
{ PCI_VDEVICE(INTEL, 0x27bc), LPC_NM10},
|
||||
{ PCI_VDEVICE(INTEL, 0x2810), LPC_ICH8},
|
||||
{ PCI_VDEVICE(INTEL, 0x2812), LPC_ICH8DH},
|
||||
{ PCI_VDEVICE(INTEL, 0x2814), LPC_ICH8DO},
|
||||
{ PCI_VDEVICE(INTEL, 0x2815), LPC_ICH8M},
|
||||
{ PCI_VDEVICE(INTEL, 0x2811), LPC_ICH8ME},
|
||||
{ PCI_VDEVICE(INTEL, 0x2918), LPC_ICH9},
|
||||
{ PCI_VDEVICE(INTEL, 0x2916), LPC_ICH9R},
|
||||
{ PCI_VDEVICE(INTEL, 0x2912), LPC_ICH9DH},
|
||||
{ PCI_VDEVICE(INTEL, 0x2914), LPC_ICH9DO},
|
||||
{ PCI_VDEVICE(INTEL, 0x2919), LPC_ICH9M},
|
||||
{ PCI_VDEVICE(INTEL, 0x2917), LPC_ICH9ME},
|
||||
{ PCI_VDEVICE(INTEL, 0x3a18), LPC_ICH10},
|
||||
{ PCI_VDEVICE(INTEL, 0x3a16), LPC_ICH10R},
|
||||
{ PCI_VDEVICE(INTEL, 0x3a1a), LPC_ICH10D},
|
||||
{ PCI_VDEVICE(INTEL, 0x3a14), LPC_ICH10DO},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b00), LPC_PCH},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b01), LPC_PCHM},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b02), LPC_P55},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b03), LPC_PM55},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b06), LPC_H55},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b07), LPC_QM57},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b08), LPC_H57},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b09), LPC_HM55},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b0a), LPC_Q57},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b0b), LPC_HM57},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b0d), LPC_PCHMSFF},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b0f), LPC_QS57},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b12), LPC_3400},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b14), LPC_3420},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b16), LPC_3450},
|
||||
{ PCI_VDEVICE(INTEL, 0x5031), LPC_EP80579},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c41), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c42), LPC_CPTD},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c43), LPC_CPTM},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c44), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c45), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c46), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c47), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c48), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c49), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c4a), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c4b), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c4c), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c4d), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c4e), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c4f), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c50), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c51), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c52), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c53), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c54), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c55), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c56), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c57), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c58), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c59), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c5a), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c5b), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c5c), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c5d), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c5e), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c5f), LPC_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1d40), LPC_PBG},
|
||||
{ PCI_VDEVICE(INTEL, 0x1d41), LPC_PBG},
|
||||
{ PCI_VDEVICE(INTEL, 0x2310), LPC_DH89XXCC},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e40), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e41), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e42), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e43), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e44), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e45), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e46), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e47), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e48), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e49), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e4a), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e4b), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e4c), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e4d), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e4e), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e4f), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e50), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e51), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e52), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e53), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e54), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e55), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e56), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e57), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e58), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e59), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e5a), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e5b), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e5c), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e5d), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e5e), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e5f), LPC_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c40), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c41), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c42), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c43), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c44), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c45), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c46), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c47), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c48), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c49), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c4a), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c4b), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c4c), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c4d), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c4e), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c4f), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c50), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c51), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c52), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c53), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c54), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c55), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c56), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c57), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c58), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c59), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c5a), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c5b), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c5c), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c5d), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c5e), LPC_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c5f), LPC_LPT},
|
||||
{ 0, }, /* End of list */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, lpc_ich_ids);
|
||||
|
||||
static void lpc_ich_restore_config_space(struct pci_dev *dev)
|
||||
{
|
||||
if (lpc_ich_acpi_save >= 0) {
|
||||
pci_write_config_byte(dev, ACPICTRL, lpc_ich_acpi_save);
|
||||
lpc_ich_acpi_save = -1;
|
||||
}
|
||||
|
||||
if (lpc_ich_gpio_save >= 0) {
|
||||
pci_write_config_byte(dev, GPIOCTRL, lpc_ich_gpio_save);
|
||||
lpc_ich_gpio_save = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void __devinit lpc_ich_enable_acpi_space(struct pci_dev *dev)
|
||||
{
|
||||
u8 reg_save;
|
||||
|
||||
pci_read_config_byte(dev, ACPICTRL, ®_save);
|
||||
pci_write_config_byte(dev, ACPICTRL, reg_save | 0x10);
|
||||
lpc_ich_acpi_save = reg_save;
|
||||
}
|
||||
|
||||
static void __devinit lpc_ich_enable_gpio_space(struct pci_dev *dev)
|
||||
{
|
||||
u8 reg_save;
|
||||
|
||||
pci_read_config_byte(dev, GPIOCTRL, ®_save);
|
||||
pci_write_config_byte(dev, GPIOCTRL, reg_save | 0x10);
|
||||
lpc_ich_gpio_save = reg_save;
|
||||
}
|
||||
|
||||
static void __devinit lpc_ich_finalize_cell(struct mfd_cell *cell,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
cell->platform_data = &lpc_chipset_info[id->driver_data];
|
||||
cell->pdata_size = sizeof(struct lpc_ich_info);
|
||||
}
|
||||
|
||||
static int __devinit lpc_ich_init_gpio(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
u32 base_addr_cfg;
|
||||
u32 base_addr;
|
||||
int ret;
|
||||
bool acpi_conflict = false;
|
||||
struct resource *res;
|
||||
|
||||
/* Setup power management base register */
|
||||
pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg);
|
||||
base_addr = base_addr_cfg & 0x0000ff80;
|
||||
if (!base_addr) {
|
||||
dev_err(&dev->dev, "I/O space for ACPI uninitialized\n");
|
||||
lpc_ich_cells[LPC_GPIO].num_resources--;
|
||||
goto gpe0_done;
|
||||
}
|
||||
|
||||
res = &gpio_ich_res[ICH_RES_GPE0];
|
||||
res->start = base_addr + ACPIBASE_GPE_OFF;
|
||||
res->end = base_addr + ACPIBASE_GPE_END;
|
||||
ret = acpi_check_resource_conflict(res);
|
||||
if (ret) {
|
||||
/*
|
||||
* This isn't fatal for the GPIO, but we have to make sure that
|
||||
* the platform_device subsystem doesn't see this resource
|
||||
* or it will register an invalid region.
|
||||
*/
|
||||
lpc_ich_cells[LPC_GPIO].num_resources--;
|
||||
acpi_conflict = true;
|
||||
} else {
|
||||
lpc_ich_enable_acpi_space(dev);
|
||||
}
|
||||
|
||||
gpe0_done:
|
||||
/* Setup GPIO base register */
|
||||
pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
|
||||
base_addr = base_addr_cfg & 0x0000ff80;
|
||||
if (!base_addr) {
|
||||
dev_err(&dev->dev, "I/O space for GPIO uninitialized\n");
|
||||
ret = -ENODEV;
|
||||
goto gpio_done;
|
||||
}
|
||||
|
||||
/* Older devices provide fewer GPIO and have a smaller resource size. */
|
||||
res = &gpio_ich_res[ICH_RES_GPIO];
|
||||
res->start = base_addr;
|
||||
switch (lpc_chipset_info[id->driver_data].gpio_version) {
|
||||
case ICH_V5_GPIO:
|
||||
case ICH_V10CORP_GPIO:
|
||||
res->end = res->start + 128 - 1;
|
||||
break;
|
||||
default:
|
||||
res->end = res->start + 64 - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = acpi_check_resource_conflict(res);
|
||||
if (ret) {
|
||||
/* this isn't necessarily fatal for the GPIO */
|
||||
acpi_conflict = true;
|
||||
goto gpio_done;
|
||||
}
|
||||
lpc_ich_enable_gpio_space(dev);
|
||||
|
||||
lpc_ich_finalize_cell(&lpc_ich_cells[LPC_GPIO], id);
|
||||
ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_GPIO],
|
||||
1, NULL, 0);
|
||||
|
||||
gpio_done:
|
||||
if (acpi_conflict)
|
||||
pr_warn("Resource conflict(s) found affecting %s\n",
|
||||
lpc_ich_cells[LPC_GPIO].name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devinit lpc_ich_init_wdt(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
u32 base_addr_cfg;
|
||||
u32 base_addr;
|
||||
int ret;
|
||||
bool acpi_conflict = false;
|
||||
struct resource *res;
|
||||
|
||||
/* Setup power management base register */
|
||||
pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg);
|
||||
base_addr = base_addr_cfg & 0x0000ff80;
|
||||
if (!base_addr) {
|
||||
dev_err(&dev->dev, "I/O space for ACPI uninitialized\n");
|
||||
ret = -ENODEV;
|
||||
goto wdt_done;
|
||||
}
|
||||
|
||||
res = wdt_io_res(ICH_RES_IO_TCO);
|
||||
res->start = base_addr + ACPIBASE_TCO_OFF;
|
||||
res->end = base_addr + ACPIBASE_TCO_END;
|
||||
ret = acpi_check_resource_conflict(res);
|
||||
if (ret) {
|
||||
acpi_conflict = true;
|
||||
goto wdt_done;
|
||||
}
|
||||
|
||||
res = wdt_io_res(ICH_RES_IO_SMI);
|
||||
res->start = base_addr + ACPIBASE_SMI_OFF;
|
||||
res->end = base_addr + ACPIBASE_SMI_END;
|
||||
ret = acpi_check_resource_conflict(res);
|
||||
if (ret) {
|
||||
acpi_conflict = true;
|
||||
goto wdt_done;
|
||||
}
|
||||
lpc_ich_enable_acpi_space(dev);
|
||||
|
||||
/*
|
||||
* Get the Memory-Mapped GCS register. To get access to it
|
||||
* we have to read RCBA from PCI Config space 0xf0 and use
|
||||
* it as base. GCS = RCBA + ICH6_GCS(0x3410).
|
||||
*/
|
||||
if (lpc_chipset_info[id->driver_data].iTCO_version == 2) {
|
||||
pci_read_config_dword(dev, RCBABASE, &base_addr_cfg);
|
||||
base_addr = base_addr_cfg & 0xffffc000;
|
||||
if (!(base_addr_cfg & 1)) {
|
||||
pr_err("RCBA is disabled by hardware/BIOS, "
|
||||
"device disabled\n");
|
||||
ret = -ENODEV;
|
||||
goto wdt_done;
|
||||
}
|
||||
res = wdt_mem_res(ICH_RES_MEM_GCS);
|
||||
res->start = base_addr + ACPIBASE_GCS_OFF;
|
||||
res->end = base_addr + ACPIBASE_GCS_END;
|
||||
ret = acpi_check_resource_conflict(res);
|
||||
if (ret) {
|
||||
acpi_conflict = true;
|
||||
goto wdt_done;
|
||||
}
|
||||
}
|
||||
|
||||
lpc_ich_finalize_cell(&lpc_ich_cells[LPC_WDT], id);
|
||||
ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_WDT],
|
||||
1, NULL, 0);
|
||||
|
||||
wdt_done:
|
||||
if (acpi_conflict)
|
||||
pr_warn("Resource conflict(s) found affecting %s\n",
|
||||
lpc_ich_cells[LPC_WDT].name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devinit lpc_ich_probe(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
bool cell_added = false;
|
||||
|
||||
ret = lpc_ich_init_wdt(dev, id);
|
||||
if (!ret)
|
||||
cell_added = true;
|
||||
|
||||
ret = lpc_ich_init_gpio(dev, id);
|
||||
if (!ret)
|
||||
cell_added = true;
|
||||
|
||||
/*
|
||||
* We only care if at least one or none of the cells registered
|
||||
* successfully.
|
||||
*/
|
||||
if (!cell_added) {
|
||||
lpc_ich_restore_config_space(dev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __devexit lpc_ich_remove(struct pci_dev *dev)
|
||||
{
|
||||
mfd_remove_devices(&dev->dev);
|
||||
lpc_ich_restore_config_space(dev);
|
||||
}
|
||||
|
||||
static struct pci_driver lpc_ich_driver = {
|
||||
.name = "lpc_ich",
|
||||
.id_table = lpc_ich_ids,
|
||||
.probe = lpc_ich_probe,
|
||||
.remove = __devexit_p(lpc_ich_remove),
|
||||
};
|
||||
|
||||
static int __init lpc_ich_init(void)
|
||||
{
|
||||
return pci_register_driver(&lpc_ich_driver);
|
||||
}
|
||||
|
||||
static void __exit lpc_ich_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&lpc_ich_driver);
|
||||
}
|
||||
|
||||
module_init(lpc_ich_init);
|
||||
module_exit(lpc_ich_exit);
|
||||
|
||||
MODULE_AUTHOR("Aaron Sierra <asierra@xes-inc.com>");
|
||||
MODULE_DESCRIPTION("LPC interface for Intel ICH");
|
||||
MODULE_LICENSE("GPL");
|
@ -36,6 +36,7 @@
|
||||
|
||||
#define GPIOBASE 0x44
|
||||
#define GPIO_IO_SIZE 64
|
||||
#define GPIO_IO_SIZE_CENTERTON 128
|
||||
|
||||
#define WDTBASE 0x84
|
||||
#define WDT_IO_SIZE 64
|
||||
@ -68,7 +69,7 @@ static struct resource wdt_sch_resource = {
|
||||
|
||||
static struct mfd_cell tunnelcreek_cells[] = {
|
||||
{
|
||||
.name = "tunnelcreek_wdt",
|
||||
.name = "ie6xx_wdt",
|
||||
.num_resources = 1,
|
||||
.resources = &wdt_sch_resource,
|
||||
},
|
||||
@ -77,6 +78,7 @@ static struct mfd_cell tunnelcreek_cells[] = {
|
||||
static DEFINE_PCI_DEVICE_TABLE(lpc_sch_ids) = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CENTERTON_ILB) },
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, lpc_sch_ids);
|
||||
@ -115,7 +117,11 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev,
|
||||
}
|
||||
|
||||
gpio_sch_resource.start = base_addr;
|
||||
gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
|
||||
|
||||
if (id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB)
|
||||
gpio_sch_resource.end = base_addr + GPIO_IO_SIZE_CENTERTON - 1;
|
||||
else
|
||||
gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
|
||||
|
||||
for (i=0; i < ARRAY_SIZE(lpc_sch_cells); i++)
|
||||
lpc_sch_cells[i].id = id->device;
|
||||
@ -125,7 +131,8 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev,
|
||||
if (ret)
|
||||
goto out_dev;
|
||||
|
||||
if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC) {
|
||||
if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC
|
||||
|| id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) {
|
||||
pci_read_config_dword(dev, WDTBASE, &base_addr_cfg);
|
||||
if (!(base_addr_cfg & (1 << 31))) {
|
||||
dev_err(&dev->dev, "Decode of the WDT I/O range disabled\n");
|
||||
@ -167,18 +174,7 @@ static struct pci_driver lpc_sch_driver = {
|
||||
.remove = __devexit_p(lpc_sch_remove),
|
||||
};
|
||||
|
||||
static int __init lpc_sch_init(void)
|
||||
{
|
||||
return pci_register_driver(&lpc_sch_driver);
|
||||
}
|
||||
|
||||
static void __exit lpc_sch_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&lpc_sch_driver);
|
||||
}
|
||||
|
||||
module_init(lpc_sch_init);
|
||||
module_exit(lpc_sch_exit);
|
||||
module_pci_driver(lpc_sch_driver);
|
||||
|
||||
MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
|
||||
MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH");
|
||||
|
309
drivers/mfd/max77693-irq.c
Normal file
309
drivers/mfd/max77693-irq.c
Normal file
@ -0,0 +1,309 @@
|
||||
/*
|
||||
* max77693-irq.c - Interrupt controller support for MAX77693
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics Co.Ltd
|
||||
* SangYoung Son <hello.son@samsung.com>
|
||||
*
|
||||
* This program is not provided / owned by Maxim Integrated Products.
|
||||
*
|
||||
* 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 driver is based on max8997-irq.c
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/mfd/max77693.h>
|
||||
#include <linux/mfd/max77693-private.h>
|
||||
|
||||
static const u8 max77693_mask_reg[] = {
|
||||
[LED_INT] = MAX77693_LED_REG_FLASH_INT_MASK,
|
||||
[TOPSYS_INT] = MAX77693_PMIC_REG_TOPSYS_INT_MASK,
|
||||
[CHG_INT] = MAX77693_CHG_REG_CHG_INT_MASK,
|
||||
[MUIC_INT1] = MAX77693_MUIC_REG_INTMASK1,
|
||||
[MUIC_INT2] = MAX77693_MUIC_REG_INTMASK2,
|
||||
[MUIC_INT3] = MAX77693_MUIC_REG_INTMASK3,
|
||||
};
|
||||
|
||||
static struct regmap *max77693_get_regmap(struct max77693_dev *max77693,
|
||||
enum max77693_irq_source src)
|
||||
{
|
||||
switch (src) {
|
||||
case LED_INT ... CHG_INT:
|
||||
return max77693->regmap;
|
||||
case MUIC_INT1 ... MUIC_INT3:
|
||||
return max77693->regmap_muic;
|
||||
default:
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
struct max77693_irq_data {
|
||||
int mask;
|
||||
enum max77693_irq_source group;
|
||||
};
|
||||
|
||||
#define DECLARE_IRQ(idx, _group, _mask) \
|
||||
[(idx)] = { .group = (_group), .mask = (_mask) }
|
||||
static const struct max77693_irq_data max77693_irqs[] = {
|
||||
DECLARE_IRQ(MAX77693_LED_IRQ_FLED2_OPEN, LED_INT, 1 << 0),
|
||||
DECLARE_IRQ(MAX77693_LED_IRQ_FLED2_SHORT, LED_INT, 1 << 1),
|
||||
DECLARE_IRQ(MAX77693_LED_IRQ_FLED1_OPEN, LED_INT, 1 << 2),
|
||||
DECLARE_IRQ(MAX77693_LED_IRQ_FLED1_SHORT, LED_INT, 1 << 3),
|
||||
DECLARE_IRQ(MAX77693_LED_IRQ_MAX_FLASH, LED_INT, 1 << 4),
|
||||
|
||||
DECLARE_IRQ(MAX77693_TOPSYS_IRQ_T120C_INT, TOPSYS_INT, 1 << 0),
|
||||
DECLARE_IRQ(MAX77693_TOPSYS_IRQ_T140C_INT, TOPSYS_INT, 1 << 1),
|
||||
DECLARE_IRQ(MAX77693_TOPSYS_IRQ_LOWSYS_INT, TOPSYS_INT, 1 << 3),
|
||||
|
||||
DECLARE_IRQ(MAX77693_CHG_IRQ_BYP_I, CHG_INT, 1 << 0),
|
||||
DECLARE_IRQ(MAX77693_CHG_IRQ_THM_I, CHG_INT, 1 << 2),
|
||||
DECLARE_IRQ(MAX77693_CHG_IRQ_BAT_I, CHG_INT, 1 << 3),
|
||||
DECLARE_IRQ(MAX77693_CHG_IRQ_CHG_I, CHG_INT, 1 << 4),
|
||||
DECLARE_IRQ(MAX77693_CHG_IRQ_CHGIN_I, CHG_INT, 1 << 6),
|
||||
|
||||
DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC, MUIC_INT1, 1 << 0),
|
||||
DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC_LOW, MUIC_INT1, 1 << 1),
|
||||
DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC_ERR, MUIC_INT1, 1 << 2),
|
||||
DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC1K, MUIC_INT1, 1 << 3),
|
||||
|
||||
DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_CHGTYP, MUIC_INT2, 1 << 0),
|
||||
DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_CHGDETREUN, MUIC_INT2, 1 << 1),
|
||||
DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_DCDTMR, MUIC_INT2, 1 << 2),
|
||||
DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_DXOVP, MUIC_INT2, 1 << 3),
|
||||
DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_VBVOLT, MUIC_INT2, 1 << 4),
|
||||
DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_VIDRM, MUIC_INT2, 1 << 5),
|
||||
|
||||
DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_EOC, MUIC_INT3, 1 << 0),
|
||||
DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_CGMBC, MUIC_INT3, 1 << 1),
|
||||
DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_OVP, MUIC_INT3, 1 << 2),
|
||||
DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_MBCCHG_ERR, MUIC_INT3, 1 << 3),
|
||||
DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_CHG_ENABLED, MUIC_INT3, 1 << 4),
|
||||
DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_BAT_DET, MUIC_INT3, 1 << 5),
|
||||
};
|
||||
|
||||
static void max77693_irq_lock(struct irq_data *data)
|
||||
{
|
||||
struct max77693_dev *max77693 = irq_get_chip_data(data->irq);
|
||||
|
||||
mutex_lock(&max77693->irqlock);
|
||||
}
|
||||
|
||||
static void max77693_irq_sync_unlock(struct irq_data *data)
|
||||
{
|
||||
struct max77693_dev *max77693 = irq_get_chip_data(data->irq);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) {
|
||||
u8 mask_reg = max77693_mask_reg[i];
|
||||
struct regmap *map = max77693_get_regmap(max77693, i);
|
||||
|
||||
if (mask_reg == MAX77693_REG_INVALID ||
|
||||
IS_ERR_OR_NULL(map))
|
||||
continue;
|
||||
max77693->irq_masks_cache[i] = max77693->irq_masks_cur[i];
|
||||
|
||||
max77693_write_reg(map, max77693_mask_reg[i],
|
||||
max77693->irq_masks_cur[i]);
|
||||
}
|
||||
|
||||
mutex_unlock(&max77693->irqlock);
|
||||
}
|
||||
|
||||
static const inline struct max77693_irq_data *
|
||||
irq_to_max77693_irq(struct max77693_dev *max77693, int irq)
|
||||
{
|
||||
return &max77693_irqs[irq];
|
||||
}
|
||||
|
||||
static void max77693_irq_mask(struct irq_data *data)
|
||||
{
|
||||
struct max77693_dev *max77693 = irq_get_chip_data(data->irq);
|
||||
const struct max77693_irq_data *irq_data =
|
||||
irq_to_max77693_irq(max77693, data->irq);
|
||||
|
||||
if (irq_data->group >= MUIC_INT1 && irq_data->group <= MUIC_INT3)
|
||||
max77693->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
|
||||
else
|
||||
max77693->irq_masks_cur[irq_data->group] |= irq_data->mask;
|
||||
}
|
||||
|
||||
static void max77693_irq_unmask(struct irq_data *data)
|
||||
{
|
||||
struct max77693_dev *max77693 = irq_get_chip_data(data->irq);
|
||||
const struct max77693_irq_data *irq_data =
|
||||
irq_to_max77693_irq(max77693, data->irq);
|
||||
|
||||
if (irq_data->group >= MUIC_INT1 && irq_data->group <= MUIC_INT3)
|
||||
max77693->irq_masks_cur[irq_data->group] |= irq_data->mask;
|
||||
else
|
||||
max77693->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
|
||||
}
|
||||
|
||||
static struct irq_chip max77693_irq_chip = {
|
||||
.name = "max77693",
|
||||
.irq_bus_lock = max77693_irq_lock,
|
||||
.irq_bus_sync_unlock = max77693_irq_sync_unlock,
|
||||
.irq_mask = max77693_irq_mask,
|
||||
.irq_unmask = max77693_irq_unmask,
|
||||
};
|
||||
|
||||
#define MAX77693_IRQSRC_CHG (1 << 0)
|
||||
#define MAX77693_IRQSRC_TOP (1 << 1)
|
||||
#define MAX77693_IRQSRC_FLASH (1 << 2)
|
||||
#define MAX77693_IRQSRC_MUIC (1 << 3)
|
||||
static irqreturn_t max77693_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct max77693_dev *max77693 = data;
|
||||
u8 irq_reg[MAX77693_IRQ_GROUP_NR] = {};
|
||||
u8 irq_src;
|
||||
int ret;
|
||||
int i, cur_irq;
|
||||
|
||||
ret = max77693_read_reg(max77693->regmap, MAX77693_PMIC_REG_INTSRC,
|
||||
&irq_src);
|
||||
if (ret < 0) {
|
||||
dev_err(max77693->dev, "Failed to read interrupt source: %d\n",
|
||||
ret);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (irq_src & MAX77693_IRQSRC_CHG)
|
||||
/* CHG_INT */
|
||||
ret = max77693_read_reg(max77693->regmap, MAX77693_CHG_REG_CHG_INT,
|
||||
&irq_reg[CHG_INT]);
|
||||
|
||||
if (irq_src & MAX77693_IRQSRC_TOP)
|
||||
/* TOPSYS_INT */
|
||||
ret = max77693_read_reg(max77693->regmap,
|
||||
MAX77693_PMIC_REG_TOPSYS_INT, &irq_reg[TOPSYS_INT]);
|
||||
|
||||
if (irq_src & MAX77693_IRQSRC_FLASH)
|
||||
/* LED_INT */
|
||||
ret = max77693_read_reg(max77693->regmap,
|
||||
MAX77693_LED_REG_FLASH_INT, &irq_reg[LED_INT]);
|
||||
|
||||
if (irq_src & MAX77693_IRQSRC_MUIC)
|
||||
/* MUIC INT1 ~ INT3 */
|
||||
max77693_bulk_read(max77693->regmap, MAX77693_MUIC_REG_INT1,
|
||||
MAX77693_NUM_IRQ_MUIC_REGS, &irq_reg[MUIC_INT1]);
|
||||
|
||||
/* Apply masking */
|
||||
for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) {
|
||||
if (i >= MUIC_INT1 && i <= MUIC_INT3)
|
||||
irq_reg[i] &= max77693->irq_masks_cur[i];
|
||||
else
|
||||
irq_reg[i] &= ~max77693->irq_masks_cur[i];
|
||||
}
|
||||
|
||||
/* Report */
|
||||
for (i = 0; i < MAX77693_IRQ_NR; i++) {
|
||||
if (irq_reg[max77693_irqs[i].group] & max77693_irqs[i].mask) {
|
||||
cur_irq = irq_find_mapping(max77693->irq_domain, i);
|
||||
if (cur_irq)
|
||||
handle_nested_irq(cur_irq);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int max77693_irq_resume(struct max77693_dev *max77693)
|
||||
{
|
||||
if (max77693->irq)
|
||||
max77693_irq_thread(0, max77693);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77693_irq_domain_map(struct irq_domain *d, unsigned int irq,
|
||||
irq_hw_number_t hw)
|
||||
{
|
||||
struct max77693_dev *max77693 = d->host_data;
|
||||
|
||||
irq_set_chip_data(irq, max77693);
|
||||
irq_set_chip_and_handler(irq, &max77693_irq_chip, handle_edge_irq);
|
||||
irq_set_nested_thread(irq, 1);
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, IRQF_VALID);
|
||||
#else
|
||||
irq_set_noprobe(irq);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops max77693_irq_domain_ops = {
|
||||
.map = max77693_irq_domain_map,
|
||||
};
|
||||
|
||||
int max77693_irq_init(struct max77693_dev *max77693)
|
||||
{
|
||||
struct irq_domain *domain;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
mutex_init(&max77693->irqlock);
|
||||
|
||||
/* Mask individual interrupt sources */
|
||||
for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) {
|
||||
struct regmap *map;
|
||||
/* MUIC IRQ 0:MASK 1:NOT MASK */
|
||||
/* Other IRQ 1:MASK 0:NOT MASK */
|
||||
if (i >= MUIC_INT1 && i <= MUIC_INT3) {
|
||||
max77693->irq_masks_cur[i] = 0x00;
|
||||
max77693->irq_masks_cache[i] = 0x00;
|
||||
} else {
|
||||
max77693->irq_masks_cur[i] = 0xff;
|
||||
max77693->irq_masks_cache[i] = 0xff;
|
||||
}
|
||||
map = max77693_get_regmap(max77693, i);
|
||||
|
||||
if (IS_ERR_OR_NULL(map))
|
||||
continue;
|
||||
if (max77693_mask_reg[i] == MAX77693_REG_INVALID)
|
||||
continue;
|
||||
if (i >= MUIC_INT1 && i <= MUIC_INT3)
|
||||
max77693_write_reg(map, max77693_mask_reg[i], 0x00);
|
||||
else
|
||||
max77693_write_reg(map, max77693_mask_reg[i], 0xff);
|
||||
}
|
||||
|
||||
domain = irq_domain_add_linear(NULL, MAX77693_IRQ_NR,
|
||||
&max77693_irq_domain_ops, max77693);
|
||||
if (!domain) {
|
||||
dev_err(max77693->dev, "could not create irq domain\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
max77693->irq_domain = domain;
|
||||
|
||||
ret = request_threaded_irq(max77693->irq, NULL, max77693_irq_thread,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"max77693-irq", max77693);
|
||||
|
||||
if (ret)
|
||||
dev_err(max77693->dev, "Failed to request IRQ %d: %d\n",
|
||||
max77693->irq, ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void max77693_irq_exit(struct max77693_dev *max77693)
|
||||
{
|
||||
if (max77693->irq)
|
||||
free_irq(max77693->irq, max77693);
|
||||
}
|
249
drivers/mfd/max77693.c
Normal file
249
drivers/mfd/max77693.c
Normal file
@ -0,0 +1,249 @@
|
||||
/*
|
||||
* max77693.c - mfd core driver for the MAX 77693
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
* SangYoung Son <hello.son@smasung.com>
|
||||
*
|
||||
* This program is not provided / owned by Maxim Integrated Products.
|
||||
*
|
||||
* 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 driver is based on max8997.c
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/max77693.h>
|
||||
#include <linux/mfd/max77693-private.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define I2C_ADDR_PMIC (0xCC >> 1) /* Charger, Flash LED */
|
||||
#define I2C_ADDR_MUIC (0x4A >> 1)
|
||||
#define I2C_ADDR_HAPTIC (0x90 >> 1)
|
||||
|
||||
static struct mfd_cell max77693_devs[] = {
|
||||
{ .name = "max77693-pmic", },
|
||||
{ .name = "max77693-charger", },
|
||||
{ .name = "max77693-flash", },
|
||||
{ .name = "max77693-muic", },
|
||||
{ .name = "max77693-haptic", },
|
||||
};
|
||||
|
||||
int max77693_read_reg(struct regmap *map, u8 reg, u8 *dest)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(map, reg, &val);
|
||||
*dest = val;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(max77693_read_reg);
|
||||
|
||||
int max77693_bulk_read(struct regmap *map, u8 reg, int count, u8 *buf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(map, reg, buf, count);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(max77693_bulk_read);
|
||||
|
||||
int max77693_write_reg(struct regmap *map, u8 reg, u8 value)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(map, reg, value);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(max77693_write_reg);
|
||||
|
||||
int max77693_bulk_write(struct regmap *map, u8 reg, int count, u8 *buf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_write(map, reg, buf, count);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(max77693_bulk_write);
|
||||
|
||||
int max77693_update_reg(struct regmap *map, u8 reg, u8 val, u8 mask)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(map, reg, mask, val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(max77693_update_reg);
|
||||
|
||||
static const struct regmap_config max77693_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = MAX77693_PMIC_REG_END,
|
||||
};
|
||||
|
||||
static int max77693_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct max77693_dev *max77693;
|
||||
struct max77693_platform_data *pdata = i2c->dev.platform_data;
|
||||
u8 reg_data;
|
||||
int ret = 0;
|
||||
|
||||
max77693 = devm_kzalloc(&i2c->dev,
|
||||
sizeof(struct max77693_dev), GFP_KERNEL);
|
||||
if (max77693 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
max77693->regmap = devm_regmap_init_i2c(i2c, &max77693_regmap_config);
|
||||
if (IS_ERR(max77693->regmap)) {
|
||||
ret = PTR_ERR(max77693->regmap);
|
||||
dev_err(max77693->dev,"failed to allocate register map: %d\n",
|
||||
ret);
|
||||
goto err_regmap;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(i2c, max77693);
|
||||
max77693->dev = &i2c->dev;
|
||||
max77693->i2c = i2c;
|
||||
max77693->irq = i2c->irq;
|
||||
max77693->type = id->driver_data;
|
||||
|
||||
if (!pdata)
|
||||
goto err_regmap;
|
||||
|
||||
max77693->wakeup = pdata->wakeup;
|
||||
|
||||
mutex_init(&max77693->iolock);
|
||||
|
||||
if (max77693_read_reg(max77693->regmap,
|
||||
MAX77693_PMIC_REG_PMIC_ID2, ®_data) < 0) {
|
||||
dev_err(max77693->dev, "device not found on this channel\n");
|
||||
ret = -ENODEV;
|
||||
goto err_regmap;
|
||||
} else
|
||||
dev_info(max77693->dev, "device ID: 0x%x\n", reg_data);
|
||||
|
||||
max77693->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC);
|
||||
i2c_set_clientdata(max77693->muic, max77693);
|
||||
|
||||
max77693->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC);
|
||||
i2c_set_clientdata(max77693->haptic, max77693);
|
||||
|
||||
ret = max77693_irq_init(max77693);
|
||||
if (ret < 0)
|
||||
goto err_mfd;
|
||||
|
||||
pm_runtime_set_active(max77693->dev);
|
||||
|
||||
ret = mfd_add_devices(max77693->dev, -1, max77693_devs,
|
||||
ARRAY_SIZE(max77693_devs), NULL, 0);
|
||||
if (ret < 0)
|
||||
goto err_mfd;
|
||||
|
||||
device_init_wakeup(max77693->dev, pdata->wakeup);
|
||||
|
||||
return ret;
|
||||
|
||||
err_mfd:
|
||||
i2c_unregister_device(max77693->muic);
|
||||
i2c_unregister_device(max77693->haptic);
|
||||
err_regmap:
|
||||
kfree(max77693);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max77693_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
|
||||
|
||||
mfd_remove_devices(max77693->dev);
|
||||
i2c_unregister_device(max77693->muic);
|
||||
i2c_unregister_device(max77693->haptic);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max77693_i2c_id[] = {
|
||||
{ "max77693", TYPE_MAX77693 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max77693_i2c_id);
|
||||
|
||||
static int max77693_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
|
||||
struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
irq_set_irq_wake(max77693->irq, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max77693_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
|
||||
struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
irq_set_irq_wake(max77693->irq, 0);
|
||||
return max77693_irq_resume(max77693);
|
||||
}
|
||||
|
||||
const struct dev_pm_ops max77693_pm = {
|
||||
.suspend = max77693_suspend,
|
||||
.resume = max77693_resume,
|
||||
};
|
||||
|
||||
static struct i2c_driver max77693_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "max77693",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &max77693_pm,
|
||||
},
|
||||
.probe = max77693_i2c_probe,
|
||||
.remove = max77693_i2c_remove,
|
||||
.id_table = max77693_i2c_id,
|
||||
};
|
||||
|
||||
static int __init max77693_i2c_init(void)
|
||||
{
|
||||
return i2c_add_driver(&max77693_i2c_driver);
|
||||
}
|
||||
/* init early so consumer devices can complete system boot */
|
||||
subsys_initcall(max77693_i2c_init);
|
||||
|
||||
static void __exit max77693_i2c_exit(void)
|
||||
{
|
||||
i2c_del_driver(&max77693_i2c_driver);
|
||||
}
|
||||
module_exit(max77693_i2c_exit);
|
||||
|
||||
MODULE_DESCRIPTION("MAXIM 77693 multi-function core driver");
|
||||
MODULE_AUTHOR("SangYoung, Son <hello.son@samsung.com>");
|
||||
MODULE_LICENSE("GPL");
|
@ -15,24 +15,13 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/mc13xxx.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
struct mc13xxx {
|
||||
struct spi_device *spidev;
|
||||
struct mutex lock;
|
||||
int irq;
|
||||
int flags;
|
||||
|
||||
irq_handler_t irqhandler[MC13XXX_NUM_IRQ];
|
||||
void *irqdata[MC13XXX_NUM_IRQ];
|
||||
|
||||
int adcflags;
|
||||
};
|
||||
#include "mc13xxx.h"
|
||||
|
||||
#define MC13XXX_IRQSTAT0 0
|
||||
#define MC13XXX_IRQSTAT0_ADCDONEI (1 << 0)
|
||||
@ -139,34 +128,29 @@ struct mc13xxx {
|
||||
|
||||
#define MC13XXX_ADC2 45
|
||||
|
||||
#define MC13XXX_NUMREGS 0x3f
|
||||
|
||||
void mc13xxx_lock(struct mc13xxx *mc13xxx)
|
||||
{
|
||||
if (!mutex_trylock(&mc13xxx->lock)) {
|
||||
dev_dbg(&mc13xxx->spidev->dev, "wait for %s from %pf\n",
|
||||
dev_dbg(mc13xxx->dev, "wait for %s from %pf\n",
|
||||
__func__, __builtin_return_address(0));
|
||||
|
||||
mutex_lock(&mc13xxx->lock);
|
||||
}
|
||||
dev_dbg(&mc13xxx->spidev->dev, "%s from %pf\n",
|
||||
dev_dbg(mc13xxx->dev, "%s from %pf\n",
|
||||
__func__, __builtin_return_address(0));
|
||||
}
|
||||
EXPORT_SYMBOL(mc13xxx_lock);
|
||||
|
||||
void mc13xxx_unlock(struct mc13xxx *mc13xxx)
|
||||
{
|
||||
dev_dbg(&mc13xxx->spidev->dev, "%s from %pf\n",
|
||||
dev_dbg(mc13xxx->dev, "%s from %pf\n",
|
||||
__func__, __builtin_return_address(0));
|
||||
mutex_unlock(&mc13xxx->lock);
|
||||
}
|
||||
EXPORT_SYMBOL(mc13xxx_unlock);
|
||||
|
||||
#define MC13XXX_REGOFFSET_SHIFT 25
|
||||
int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val)
|
||||
{
|
||||
struct spi_transfer t;
|
||||
struct spi_message m;
|
||||
int ret;
|
||||
|
||||
BUG_ON(!mutex_is_locked(&mc13xxx->lock));
|
||||
@ -174,84 +158,35 @@ int mc13xxx_reg_read(struct mc13xxx *mc13xxx, unsigned int offset, u32 *val)
|
||||
if (offset > MC13XXX_NUMREGS)
|
||||
return -EINVAL;
|
||||
|
||||
*val = offset << MC13XXX_REGOFFSET_SHIFT;
|
||||
ret = regmap_read(mc13xxx->regmap, offset, val);
|
||||
dev_vdbg(mc13xxx->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
|
||||
|
||||
memset(&t, 0, sizeof(t));
|
||||
|
||||
t.tx_buf = val;
|
||||
t.rx_buf = val;
|
||||
t.len = sizeof(u32);
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t, &m);
|
||||
|
||||
ret = spi_sync(mc13xxx->spidev, &m);
|
||||
|
||||
/* error in message.status implies error return from spi_sync */
|
||||
BUG_ON(!ret && m.status);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val &= 0xffffff;
|
||||
|
||||
dev_vdbg(&mc13xxx->spidev->dev, "[0x%02x] -> 0x%06x\n", offset, *val);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mc13xxx_reg_read);
|
||||
|
||||
int mc13xxx_reg_write(struct mc13xxx *mc13xxx, unsigned int offset, u32 val)
|
||||
{
|
||||
u32 buf;
|
||||
struct spi_transfer t;
|
||||
struct spi_message m;
|
||||
int ret;
|
||||
|
||||
BUG_ON(!mutex_is_locked(&mc13xxx->lock));
|
||||
|
||||
dev_vdbg(&mc13xxx->spidev->dev, "[0x%02x] <- 0x%06x\n", offset, val);
|
||||
dev_vdbg(mc13xxx->dev, "[0x%02x] <- 0x%06x\n", offset, val);
|
||||
|
||||
if (offset > MC13XXX_NUMREGS || val > 0xffffff)
|
||||
return -EINVAL;
|
||||
|
||||
buf = 1 << 31 | offset << MC13XXX_REGOFFSET_SHIFT | val;
|
||||
|
||||
memset(&t, 0, sizeof(t));
|
||||
|
||||
t.tx_buf = &buf;
|
||||
t.rx_buf = &buf;
|
||||
t.len = sizeof(u32);
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t, &m);
|
||||
|
||||
ret = spi_sync(mc13xxx->spidev, &m);
|
||||
|
||||
BUG_ON(!ret && m.status);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
return regmap_write(mc13xxx->regmap, offset, val);
|
||||
}
|
||||
EXPORT_SYMBOL(mc13xxx_reg_write);
|
||||
|
||||
int mc13xxx_reg_rmw(struct mc13xxx *mc13xxx, unsigned int offset,
|
||||
u32 mask, u32 val)
|
||||
{
|
||||
int ret;
|
||||
u32 valread;
|
||||
|
||||
BUG_ON(!mutex_is_locked(&mc13xxx->lock));
|
||||
BUG_ON(val & ~mask);
|
||||
dev_vdbg(mc13xxx->dev, "[0x%02x] <- 0x%06x (mask: 0x%06x)\n",
|
||||
offset, val, mask);
|
||||
|
||||
ret = mc13xxx_reg_read(mc13xxx, offset, &valread);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
valread = (valread & ~mask) | val;
|
||||
|
||||
return mc13xxx_reg_write(mc13xxx, offset, valread);
|
||||
return regmap_update_bits(mc13xxx->regmap, offset, mask, val);
|
||||
}
|
||||
EXPORT_SYMBOL(mc13xxx_reg_rmw);
|
||||
|
||||
@ -439,7 +374,7 @@ static int mc13xxx_irq_handle(struct mc13xxx *mc13xxx,
|
||||
if (handled == IRQ_HANDLED)
|
||||
num_handled++;
|
||||
} else {
|
||||
dev_err(&mc13xxx->spidev->dev,
|
||||
dev_err(mc13xxx->dev,
|
||||
"BUG: irq %u but no handler\n",
|
||||
baseirq + irq);
|
||||
|
||||
@ -475,25 +410,23 @@ static irqreturn_t mc13xxx_irq_thread(int irq, void *data)
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
||||
|
||||
enum mc13xxx_id {
|
||||
MC13XXX_ID_MC13783,
|
||||
MC13XXX_ID_MC13892,
|
||||
MC13XXX_ID_INVALID,
|
||||
};
|
||||
|
||||
static const char *mc13xxx_chipname[] = {
|
||||
[MC13XXX_ID_MC13783] = "mc13783",
|
||||
[MC13XXX_ID_MC13892] = "mc13892",
|
||||
};
|
||||
|
||||
#define maskval(reg, mask) (((reg) & (mask)) >> __ffs(mask))
|
||||
static int mc13xxx_identify(struct mc13xxx *mc13xxx, enum mc13xxx_id *id)
|
||||
static int mc13xxx_identify(struct mc13xxx *mc13xxx)
|
||||
{
|
||||
u32 icid;
|
||||
u32 revision;
|
||||
const char *name;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Get the generation ID from register 46, as apparently some older
|
||||
* IC revisions only have this info at this location. Newer ICs seem to
|
||||
* have both.
|
||||
*/
|
||||
ret = mc13xxx_reg_read(mc13xxx, 46, &icid);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -502,26 +435,23 @@ static int mc13xxx_identify(struct mc13xxx *mc13xxx, enum mc13xxx_id *id)
|
||||
|
||||
switch (icid) {
|
||||
case 2:
|
||||
*id = MC13XXX_ID_MC13783;
|
||||
name = "mc13783";
|
||||
mc13xxx->ictype = MC13XXX_ID_MC13783;
|
||||
break;
|
||||
case 7:
|
||||
*id = MC13XXX_ID_MC13892;
|
||||
name = "mc13892";
|
||||
mc13xxx->ictype = MC13XXX_ID_MC13892;
|
||||
break;
|
||||
default:
|
||||
*id = MC13XXX_ID_INVALID;
|
||||
mc13xxx->ictype = MC13XXX_ID_INVALID;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*id == MC13XXX_ID_MC13783 || *id == MC13XXX_ID_MC13892) {
|
||||
if (mc13xxx->ictype == MC13XXX_ID_MC13783 ||
|
||||
mc13xxx->ictype == MC13XXX_ID_MC13892) {
|
||||
ret = mc13xxx_reg_read(mc13xxx, MC13XXX_REVISION, &revision);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(&mc13xxx->spidev->dev, "%s: rev: %d.%d, "
|
||||
dev_info(mc13xxx->dev, "%s: rev: %d.%d, "
|
||||
"fin: %d, fab: %d, icid: %d/%d\n",
|
||||
mc13xxx_chipname[*id],
|
||||
mc13xxx_chipname[mc13xxx->ictype],
|
||||
maskval(revision, MC13XXX_REVISION_REVFULL),
|
||||
maskval(revision, MC13XXX_REVISION_REVMETAL),
|
||||
maskval(revision, MC13XXX_REVISION_FIN),
|
||||
@ -530,26 +460,12 @@ static int mc13xxx_identify(struct mc13xxx *mc13xxx, enum mc13xxx_id *id)
|
||||
maskval(revision, MC13XXX_REVISION_ICIDCODE));
|
||||
}
|
||||
|
||||
if (*id != MC13XXX_ID_INVALID) {
|
||||
const struct spi_device_id *devid =
|
||||
spi_get_device_id(mc13xxx->spidev);
|
||||
if (!devid || devid->driver_data != *id)
|
||||
dev_warn(&mc13xxx->spidev->dev, "device id doesn't "
|
||||
"match auto detection!\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
return (mc13xxx->ictype == MC13XXX_ID_INVALID) ? -ENODEV : 0;
|
||||
}
|
||||
|
||||
static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx)
|
||||
{
|
||||
const struct spi_device_id *devid =
|
||||
spi_get_device_id(mc13xxx->spidev);
|
||||
|
||||
if (!devid)
|
||||
return NULL;
|
||||
|
||||
return mc13xxx_chipname[devid->driver_data];
|
||||
return mc13xxx_chipname[mc13xxx->ictype];
|
||||
}
|
||||
|
||||
int mc13xxx_get_flags(struct mc13xxx *mc13xxx)
|
||||
@ -592,7 +508,7 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode,
|
||||
};
|
||||
init_completion(&adcdone_data.done);
|
||||
|
||||
dev_dbg(&mc13xxx->spidev->dev, "%s\n", __func__);
|
||||
dev_dbg(mc13xxx->dev, "%s\n", __func__);
|
||||
|
||||
mc13xxx_lock(mc13xxx);
|
||||
|
||||
@ -637,7 +553,8 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode,
|
||||
adc1 |= ato << MC13783_ADC1_ATO_SHIFT;
|
||||
if (atox)
|
||||
adc1 |= MC13783_ADC1_ATOX;
|
||||
dev_dbg(&mc13xxx->spidev->dev, "%s: request irq\n", __func__);
|
||||
|
||||
dev_dbg(mc13xxx->dev, "%s: request irq\n", __func__);
|
||||
mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_ADCDONE,
|
||||
mc13xxx_handler_adcdone, __func__, &adcdone_data);
|
||||
mc13xxx_irq_ack(mc13xxx, MC13XXX_IRQ_ADCDONE);
|
||||
@ -695,7 +612,7 @@ static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx,
|
||||
if (!cell.name)
|
||||
return -ENOMEM;
|
||||
|
||||
return mfd_add_devices(&mc13xxx->spidev->dev, -1, &cell, 1, NULL, 0);
|
||||
return mfd_add_devices(mc13xxx->dev, -1, &cell, 1, NULL, 0);
|
||||
}
|
||||
|
||||
static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format)
|
||||
@ -706,7 +623,7 @@ static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format)
|
||||
#ifdef CONFIG_OF
|
||||
static int mc13xxx_probe_flags_dt(struct mc13xxx *mc13xxx)
|
||||
{
|
||||
struct device_node *np = mc13xxx->spidev->dev.of_node;
|
||||
struct device_node *np = mc13xxx->dev->of_node;
|
||||
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
@ -732,55 +649,15 @@ static inline int mc13xxx_probe_flags_dt(struct mc13xxx *mc13xxx)
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct spi_device_id mc13xxx_device_id[] = {
|
||||
{
|
||||
.name = "mc13783",
|
||||
.driver_data = MC13XXX_ID_MC13783,
|
||||
}, {
|
||||
.name = "mc13892",
|
||||
.driver_data = MC13XXX_ID_MC13892,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, mc13xxx_device_id);
|
||||
|
||||
static const struct of_device_id mc13xxx_dt_ids[] = {
|
||||
{ .compatible = "fsl,mc13783", .data = (void *) MC13XXX_ID_MC13783, },
|
||||
{ .compatible = "fsl,mc13892", .data = (void *) MC13XXX_ID_MC13892, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids);
|
||||
|
||||
static int mc13xxx_probe(struct spi_device *spi)
|
||||
int mc13xxx_common_init(struct mc13xxx *mc13xxx,
|
||||
struct mc13xxx_platform_data *pdata, int irq)
|
||||
{
|
||||
const struct of_device_id *of_id;
|
||||
struct spi_driver *sdrv = to_spi_driver(spi->dev.driver);
|
||||
struct mc13xxx *mc13xxx;
|
||||
struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev);
|
||||
enum mc13xxx_id id;
|
||||
int ret;
|
||||
|
||||
of_id = of_match_device(mc13xxx_dt_ids, &spi->dev);
|
||||
if (of_id)
|
||||
sdrv->id_table = &mc13xxx_device_id[(enum mc13xxx_id) of_id->data];
|
||||
|
||||
mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
|
||||
if (!mc13xxx)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&spi->dev, mc13xxx);
|
||||
spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
|
||||
spi->bits_per_word = 32;
|
||||
spi_setup(spi);
|
||||
|
||||
mc13xxx->spidev = spi;
|
||||
|
||||
mutex_init(&mc13xxx->lock);
|
||||
mc13xxx_lock(mc13xxx);
|
||||
|
||||
ret = mc13xxx_identify(mc13xxx, &id);
|
||||
if (ret || id == MC13XXX_ID_INVALID)
|
||||
ret = mc13xxx_identify(mc13xxx);
|
||||
if (ret)
|
||||
goto err_revision;
|
||||
|
||||
/* mask all irqs */
|
||||
@ -792,18 +669,19 @@ static int mc13xxx_probe(struct spi_device *spi)
|
||||
if (ret)
|
||||
goto err_mask;
|
||||
|
||||
ret = request_threaded_irq(spi->irq, NULL, mc13xxx_irq_thread,
|
||||
ret = request_threaded_irq(irq, NULL, mc13xxx_irq_thread,
|
||||
IRQF_ONESHOT | IRQF_TRIGGER_HIGH, "mc13xxx", mc13xxx);
|
||||
|
||||
if (ret) {
|
||||
err_mask:
|
||||
err_revision:
|
||||
mc13xxx_unlock(mc13xxx);
|
||||
dev_set_drvdata(&spi->dev, NULL);
|
||||
kfree(mc13xxx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mc13xxx->irq = irq;
|
||||
|
||||
mc13xxx_unlock(mc13xxx);
|
||||
|
||||
if (mc13xxx_probe_flags_dt(mc13xxx) < 0 && pdata)
|
||||
@ -838,42 +716,19 @@ err_revision:
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mc13xxx_common_init);
|
||||
|
||||
static int __devexit mc13xxx_remove(struct spi_device *spi)
|
||||
void mc13xxx_common_cleanup(struct mc13xxx *mc13xxx)
|
||||
{
|
||||
struct mc13xxx *mc13xxx = dev_get_drvdata(&spi->dev);
|
||||
free_irq(mc13xxx->irq, mc13xxx);
|
||||
|
||||
free_irq(mc13xxx->spidev->irq, mc13xxx);
|
||||
mfd_remove_devices(mc13xxx->dev);
|
||||
|
||||
mfd_remove_devices(&spi->dev);
|
||||
regmap_exit(mc13xxx->regmap);
|
||||
|
||||
kfree(mc13xxx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver mc13xxx_driver = {
|
||||
.id_table = mc13xxx_device_id,
|
||||
.driver = {
|
||||
.name = "mc13xxx",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = mc13xxx_dt_ids,
|
||||
},
|
||||
.probe = mc13xxx_probe,
|
||||
.remove = __devexit_p(mc13xxx_remove),
|
||||
};
|
||||
|
||||
static int __init mc13xxx_init(void)
|
||||
{
|
||||
return spi_register_driver(&mc13xxx_driver);
|
||||
}
|
||||
subsys_initcall(mc13xxx_init);
|
||||
|
||||
static void __exit mc13xxx_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&mc13xxx_driver);
|
||||
}
|
||||
module_exit(mc13xxx_exit);
|
||||
EXPORT_SYMBOL_GPL(mc13xxx_common_cleanup);
|
||||
|
||||
MODULE_DESCRIPTION("Core driver for Freescale MC13XXX PMIC");
|
||||
MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
|
||||
|
128
drivers/mfd/mc13xxx-i2c.c
Normal file
128
drivers/mfd/mc13xxx-i2c.c
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2009-2010 Creative Product Design
|
||||
* Marc Reilly marc@cpdesign.com.au
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/mc13xxx.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include "mc13xxx.h"
|
||||
|
||||
static const struct i2c_device_id mc13xxx_i2c_device_id[] = {
|
||||
{
|
||||
.name = "mc13892",
|
||||
.driver_data = MC13XXX_ID_MC13892,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mc13xxx_i2c_device_id);
|
||||
|
||||
static const struct of_device_id mc13xxx_dt_ids[] = {
|
||||
{
|
||||
.compatible = "fsl,mc13892",
|
||||
.data = (void *) &mc13xxx_i2c_device_id[0],
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids);
|
||||
|
||||
static struct regmap_config mc13xxx_regmap_i2c_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 24,
|
||||
|
||||
.max_register = MC13XXX_NUMREGS,
|
||||
|
||||
.cache_type = REGCACHE_NONE,
|
||||
};
|
||||
|
||||
static int mc13xxx_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
const struct of_device_id *of_id;
|
||||
struct i2c_driver *idrv = to_i2c_driver(client->dev.driver);
|
||||
struct mc13xxx *mc13xxx;
|
||||
struct mc13xxx_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
int ret;
|
||||
|
||||
of_id = of_match_device(mc13xxx_dt_ids, &client->dev);
|
||||
if (of_id)
|
||||
idrv->id_table = (const struct i2c_device_id*) of_id->data;
|
||||
|
||||
mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
|
||||
if (!mc13xxx)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&client->dev, mc13xxx);
|
||||
|
||||
mc13xxx->dev = &client->dev;
|
||||
mutex_init(&mc13xxx->lock);
|
||||
|
||||
mc13xxx->regmap = regmap_init_i2c(client, &mc13xxx_regmap_i2c_config);
|
||||
if (IS_ERR(mc13xxx->regmap)) {
|
||||
ret = PTR_ERR(mc13xxx->regmap);
|
||||
dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n",
|
||||
ret);
|
||||
dev_set_drvdata(&client->dev, NULL);
|
||||
kfree(mc13xxx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mc13xxx_common_init(mc13xxx, pdata, client->irq);
|
||||
|
||||
if (ret == 0 && (id->driver_data != mc13xxx->ictype))
|
||||
dev_warn(mc13xxx->dev,
|
||||
"device id doesn't match auto detection!\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit mc13xxx_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct mc13xxx *mc13xxx = dev_get_drvdata(&client->dev);
|
||||
|
||||
mc13xxx_common_cleanup(mc13xxx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver mc13xxx_i2c_driver = {
|
||||
.id_table = mc13xxx_i2c_device_id,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "mc13xxx",
|
||||
.of_match_table = mc13xxx_dt_ids,
|
||||
},
|
||||
.probe = mc13xxx_i2c_probe,
|
||||
.remove = __devexit_p(mc13xxx_i2c_remove),
|
||||
};
|
||||
|
||||
static int __init mc13xxx_i2c_init(void)
|
||||
{
|
||||
return i2c_add_driver(&mc13xxx_i2c_driver);
|
||||
}
|
||||
subsys_initcall(mc13xxx_i2c_init);
|
||||
|
||||
static void __exit mc13xxx_i2c_exit(void)
|
||||
{
|
||||
i2c_del_driver(&mc13xxx_i2c_driver);
|
||||
}
|
||||
module_exit(mc13xxx_i2c_exit);
|
||||
|
||||
MODULE_DESCRIPTION("i2c driver for Freescale MC13XXX PMIC");
|
||||
MODULE_AUTHOR("Marc Reilly <marc@cpdesign.com.au");
|
||||
MODULE_LICENSE("GPL v2");
|
140
drivers/mfd/mc13xxx-spi.c
Normal file
140
drivers/mfd/mc13xxx-spi.c
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright 2009-2010 Pengutronix
|
||||
* Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
|
||||
*
|
||||
* loosely based on an earlier driver that has
|
||||
* Copyright 2009 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/mc13xxx.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "mc13xxx.h"
|
||||
|
||||
static const struct spi_device_id mc13xxx_device_id[] = {
|
||||
{
|
||||
.name = "mc13783",
|
||||
.driver_data = MC13XXX_ID_MC13783,
|
||||
}, {
|
||||
.name = "mc13892",
|
||||
.driver_data = MC13XXX_ID_MC13892,
|
||||
}, {
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, mc13xxx_device_id);
|
||||
|
||||
static const struct of_device_id mc13xxx_dt_ids[] = {
|
||||
{ .compatible = "fsl,mc13783", .data = (void *) MC13XXX_ID_MC13783, },
|
||||
{ .compatible = "fsl,mc13892", .data = (void *) MC13XXX_ID_MC13892, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids);
|
||||
|
||||
static struct regmap_config mc13xxx_regmap_spi_config = {
|
||||
.reg_bits = 7,
|
||||
.pad_bits = 1,
|
||||
.val_bits = 24,
|
||||
|
||||
.max_register = MC13XXX_NUMREGS,
|
||||
|
||||
.cache_type = REGCACHE_NONE,
|
||||
};
|
||||
|
||||
static int mc13xxx_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct of_device_id *of_id;
|
||||
struct spi_driver *sdrv = to_spi_driver(spi->dev.driver);
|
||||
struct mc13xxx *mc13xxx;
|
||||
struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev);
|
||||
int ret;
|
||||
|
||||
of_id = of_match_device(mc13xxx_dt_ids, &spi->dev);
|
||||
if (of_id)
|
||||
sdrv->id_table = &mc13xxx_device_id[(enum mc13xxx_id) of_id->data];
|
||||
|
||||
mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
|
||||
if (!mc13xxx)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&spi->dev, mc13xxx);
|
||||
spi->mode = SPI_MODE_0 | SPI_CS_HIGH;
|
||||
spi->bits_per_word = 32;
|
||||
|
||||
mc13xxx->dev = &spi->dev;
|
||||
mutex_init(&mc13xxx->lock);
|
||||
|
||||
mc13xxx->regmap = regmap_init_spi(spi, &mc13xxx_regmap_spi_config);
|
||||
if (IS_ERR(mc13xxx->regmap)) {
|
||||
ret = PTR_ERR(mc13xxx->regmap);
|
||||
dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n",
|
||||
ret);
|
||||
dev_set_drvdata(&spi->dev, NULL);
|
||||
kfree(mc13xxx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mc13xxx_common_init(mc13xxx, pdata, spi->irq);
|
||||
|
||||
if (ret) {
|
||||
dev_set_drvdata(&spi->dev, NULL);
|
||||
} else {
|
||||
const struct spi_device_id *devid =
|
||||
spi_get_device_id(spi);
|
||||
if (!devid || devid->driver_data != mc13xxx->ictype)
|
||||
dev_warn(mc13xxx->dev,
|
||||
"device id doesn't match auto detection!\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit mc13xxx_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct mc13xxx *mc13xxx = dev_get_drvdata(&spi->dev);
|
||||
|
||||
mc13xxx_common_cleanup(mc13xxx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver mc13xxx_spi_driver = {
|
||||
.id_table = mc13xxx_device_id,
|
||||
.driver = {
|
||||
.name = "mc13xxx",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = mc13xxx_dt_ids,
|
||||
},
|
||||
.probe = mc13xxx_spi_probe,
|
||||
.remove = __devexit_p(mc13xxx_spi_remove),
|
||||
};
|
||||
|
||||
static int __init mc13xxx_init(void)
|
||||
{
|
||||
return spi_register_driver(&mc13xxx_spi_driver);
|
||||
}
|
||||
subsys_initcall(mc13xxx_init);
|
||||
|
||||
static void __exit mc13xxx_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&mc13xxx_spi_driver);
|
||||
}
|
||||
module_exit(mc13xxx_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Core driver for Freescale MC13XXX PMIC");
|
||||
MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
|
||||
MODULE_LICENSE("GPL v2");
|
45
drivers/mfd/mc13xxx.h
Normal file
45
drivers/mfd/mc13xxx.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2012 Creative Product Design
|
||||
* Marc Reilly <marc@cpdesign.com.au>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License version 2 as published by the
|
||||
* Free Software Foundation.
|
||||
*/
|
||||
#ifndef __DRIVERS_MFD_MC13XXX_H
|
||||
#define __DRIVERS_MFD_MC13XXX_H
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/mc13xxx.h>
|
||||
|
||||
enum mc13xxx_id {
|
||||
MC13XXX_ID_MC13783,
|
||||
MC13XXX_ID_MC13892,
|
||||
MC13XXX_ID_INVALID,
|
||||
};
|
||||
|
||||
#define MC13XXX_NUMREGS 0x3f
|
||||
|
||||
struct mc13xxx {
|
||||
struct regmap *regmap;
|
||||
|
||||
struct device *dev;
|
||||
enum mc13xxx_id ictype;
|
||||
|
||||
struct mutex lock;
|
||||
int irq;
|
||||
int flags;
|
||||
|
||||
irq_handler_t irqhandler[MC13XXX_NUM_IRQ];
|
||||
void *irqdata[MC13XXX_NUM_IRQ];
|
||||
|
||||
int adcflags;
|
||||
};
|
||||
|
||||
int mc13xxx_common_init(struct mc13xxx *mc13xxx,
|
||||
struct mc13xxx_platform_data *pdata, int irq);
|
||||
|
||||
void mc13xxx_common_cleanup(struct mc13xxx *mc13xxx);
|
||||
|
||||
#endif /* __DRIVERS_MFD_MC13XXX_H */
|
@ -204,7 +204,7 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
pcf = kzalloc(sizeof(*pcf), GFP_KERNEL);
|
||||
pcf = devm_kzalloc(&client->dev, sizeof(*pcf), GFP_KERNEL);
|
||||
if (!pcf)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -212,12 +212,11 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
|
||||
|
||||
mutex_init(&pcf->lock);
|
||||
|
||||
pcf->regmap = regmap_init_i2c(client, &pcf50633_regmap_config);
|
||||
pcf->regmap = devm_regmap_init_i2c(client, &pcf50633_regmap_config);
|
||||
if (IS_ERR(pcf->regmap)) {
|
||||
ret = PTR_ERR(pcf->regmap);
|
||||
dev_err(pcf->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
goto err_free;
|
||||
dev_err(pcf->dev, "Failed to allocate register map: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, pcf);
|
||||
@ -228,7 +227,7 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
|
||||
if (version < 0 || variant < 0) {
|
||||
dev_err(pcf->dev, "Unable to probe pcf50633\n");
|
||||
ret = -ENODEV;
|
||||
goto err_regmap;
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(pcf->dev, "Probed device version %d variant %d\n",
|
||||
@ -237,16 +236,11 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
|
||||
pcf50633_irq_init(pcf, client->irq);
|
||||
|
||||
/* Create sub devices */
|
||||
pcf50633_client_dev_register(pcf, "pcf50633-input",
|
||||
&pcf->input_pdev);
|
||||
pcf50633_client_dev_register(pcf, "pcf50633-rtc",
|
||||
&pcf->rtc_pdev);
|
||||
pcf50633_client_dev_register(pcf, "pcf50633-mbc",
|
||||
&pcf->mbc_pdev);
|
||||
pcf50633_client_dev_register(pcf, "pcf50633-adc",
|
||||
&pcf->adc_pdev);
|
||||
pcf50633_client_dev_register(pcf, "pcf50633-backlight",
|
||||
&pcf->bl_pdev);
|
||||
pcf50633_client_dev_register(pcf, "pcf50633-input", &pcf->input_pdev);
|
||||
pcf50633_client_dev_register(pcf, "pcf50633-rtc", &pcf->rtc_pdev);
|
||||
pcf50633_client_dev_register(pcf, "pcf50633-mbc", &pcf->mbc_pdev);
|
||||
pcf50633_client_dev_register(pcf, "pcf50633-adc", &pcf->adc_pdev);
|
||||
pcf50633_client_dev_register(pcf, "pcf50633-backlight", &pcf->bl_pdev);
|
||||
|
||||
|
||||
for (i = 0; i < PCF50633_NUM_REGULATORS; i++) {
|
||||
@ -274,13 +268,6 @@ static int __devinit pcf50633_probe(struct i2c_client *client,
|
||||
pdata->probe_done(pcf);
|
||||
|
||||
return 0;
|
||||
|
||||
err_regmap:
|
||||
regmap_exit(pcf->regmap);
|
||||
err_free:
|
||||
kfree(pcf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit pcf50633_remove(struct i2c_client *client)
|
||||
@ -300,9 +287,6 @@ static int __devexit pcf50633_remove(struct i2c_client *client)
|
||||
for (i = 0; i < PCF50633_NUM_REGULATORS; i++)
|
||||
platform_device_unregister(pcf->regulator_pdev[i]);
|
||||
|
||||
regmap_exit(pcf->regmap);
|
||||
kfree(pcf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,7 @@ static struct deepsleep_control_data deepsleep_data[] = {
|
||||
(RC5T583_EXT_PWRREQ1_CONTROL | RC5T583_EXT_PWRREQ2_CONTROL)
|
||||
|
||||
static struct mfd_cell rc5t583_subdevs[] = {
|
||||
{.name = "rc5t583-gpio",},
|
||||
{.name = "rc5t583-regulator",},
|
||||
{.name = "rc5t583-rtc", },
|
||||
{.name = "rc5t583-key", }
|
||||
@ -267,7 +268,7 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c,
|
||||
rc5t583->dev = &i2c->dev;
|
||||
i2c_set_clientdata(i2c, rc5t583);
|
||||
|
||||
rc5t583->regmap = regmap_init_i2c(i2c, &rc5t583_regmap_config);
|
||||
rc5t583->regmap = devm_regmap_init_i2c(i2c, &rc5t583_regmap_config);
|
||||
if (IS_ERR(rc5t583->regmap)) {
|
||||
ret = PTR_ERR(rc5t583->regmap);
|
||||
dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret);
|
||||
@ -276,7 +277,7 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c,
|
||||
|
||||
ret = rc5t583_clear_ext_power_req(rc5t583, pdata);
|
||||
if (ret < 0)
|
||||
goto err_irq_init;
|
||||
return ret;
|
||||
|
||||
if (i2c->irq) {
|
||||
ret = rc5t583_irq_init(rc5t583, i2c->irq, pdata->irq_base);
|
||||
@ -299,8 +300,6 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c,
|
||||
err_add_devs:
|
||||
if (irq_init_success)
|
||||
rc5t583_irq_exit(rc5t583);
|
||||
err_irq_init:
|
||||
regmap_exit(rc5t583->regmap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -310,7 +309,6 @@ static int __devexit rc5t583_i2c_remove(struct i2c_client *i2c)
|
||||
|
||||
mfd_remove_devices(rc5t583->dev);
|
||||
rc5t583_irq_exit(rc5t583);
|
||||
regmap_exit(rc5t583->regmap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -108,18 +108,7 @@ static struct pci_driver rdc321x_sb_driver = {
|
||||
.remove = __devexit_p(rdc321x_sb_remove),
|
||||
};
|
||||
|
||||
static int __init rdc321x_sb_init(void)
|
||||
{
|
||||
return pci_register_driver(&rdc321x_sb_driver);
|
||||
}
|
||||
|
||||
static void __exit rdc321x_sb_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&rdc321x_sb_driver);
|
||||
}
|
||||
|
||||
module_init(rdc321x_sb_init);
|
||||
module_exit(rdc321x_sb_exit);
|
||||
module_pci_driver(rdc321x_sb_driver);
|
||||
|
||||
MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -114,12 +114,12 @@ static int s5m87xx_i2c_probe(struct i2c_client *i2c,
|
||||
s5m87xx->wakeup = pdata->wakeup;
|
||||
}
|
||||
|
||||
s5m87xx->regmap = regmap_init_i2c(i2c, &s5m_regmap_config);
|
||||
s5m87xx->regmap = devm_regmap_init_i2c(i2c, &s5m_regmap_config);
|
||||
if (IS_ERR(s5m87xx->regmap)) {
|
||||
ret = PTR_ERR(s5m87xx->regmap);
|
||||
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
goto err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
s5m87xx->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
|
||||
@ -159,7 +159,6 @@ err:
|
||||
mfd_remove_devices(s5m87xx->dev);
|
||||
s5m_irq_exit(s5m87xx);
|
||||
i2c_unregister_device(s5m87xx->rtc);
|
||||
regmap_exit(s5m87xx->regmap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -170,7 +169,6 @@ static int s5m87xx_i2c_remove(struct i2c_client *i2c)
|
||||
mfd_remove_devices(s5m87xx->dev);
|
||||
s5m_irq_exit(s5m87xx);
|
||||
i2c_unregister_device(s5m87xx->rtc);
|
||||
regmap_exit(s5m87xx->regmap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
467
drivers/mfd/sta2x11-mfd.c
Normal file
467
drivers/mfd/sta2x11-mfd.c
Normal file
@ -0,0 +1,467 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Wind River Systems, Inc.
|
||||
* Copyright (c) 2011 ST Microelectronics (Alessandro Rubini)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/sta2x11-mfd.h>
|
||||
|
||||
#include <asm/sta2x11.h>
|
||||
|
||||
/* This describes STA2X11 MFD chip for us, we may have several */
|
||||
struct sta2x11_mfd {
|
||||
struct sta2x11_instance *instance;
|
||||
spinlock_t lock;
|
||||
struct list_head list;
|
||||
void __iomem *sctl_regs;
|
||||
void __iomem *apbreg_regs;
|
||||
};
|
||||
|
||||
static LIST_HEAD(sta2x11_mfd_list);
|
||||
|
||||
/* Three functions to act on the list */
|
||||
static struct sta2x11_mfd *sta2x11_mfd_find(struct pci_dev *pdev)
|
||||
{
|
||||
struct sta2x11_instance *instance;
|
||||
struct sta2x11_mfd *mfd;
|
||||
|
||||
if (!pdev && !list_empty(&sta2x11_mfd_list)) {
|
||||
pr_warning("%s: Unspecified device, "
|
||||
"using first instance\n", __func__);
|
||||
return list_entry(sta2x11_mfd_list.next,
|
||||
struct sta2x11_mfd, list);
|
||||
}
|
||||
|
||||
instance = sta2x11_get_instance(pdev);
|
||||
if (!instance)
|
||||
return NULL;
|
||||
list_for_each_entry(mfd, &sta2x11_mfd_list, list) {
|
||||
if (mfd->instance == instance)
|
||||
return mfd;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int __devinit sta2x11_mfd_add(struct pci_dev *pdev, gfp_t flags)
|
||||
{
|
||||
struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev);
|
||||
struct sta2x11_instance *instance;
|
||||
|
||||
if (mfd)
|
||||
return -EBUSY;
|
||||
instance = sta2x11_get_instance(pdev);
|
||||
if (!instance)
|
||||
return -EINVAL;
|
||||
mfd = kzalloc(sizeof(*mfd), flags);
|
||||
if (!mfd)
|
||||
return -ENOMEM;
|
||||
INIT_LIST_HEAD(&mfd->list);
|
||||
spin_lock_init(&mfd->lock);
|
||||
mfd->instance = instance;
|
||||
list_add(&mfd->list, &sta2x11_mfd_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit mfd_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev);
|
||||
|
||||
if (!mfd)
|
||||
return -ENODEV;
|
||||
list_del(&mfd->list);
|
||||
kfree(mfd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* These two functions are exported and are not expected to fail */
|
||||
u32 sta2x11_sctl_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val)
|
||||
{
|
||||
struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev);
|
||||
u32 r;
|
||||
unsigned long flags;
|
||||
|
||||
if (!mfd) {
|
||||
dev_warn(&pdev->dev, ": can't access sctl regs\n");
|
||||
return 0;
|
||||
}
|
||||
if (!mfd->sctl_regs) {
|
||||
dev_warn(&pdev->dev, ": system ctl not initialized\n");
|
||||
return 0;
|
||||
}
|
||||
spin_lock_irqsave(&mfd->lock, flags);
|
||||
r = readl(mfd->sctl_regs + reg);
|
||||
r &= ~mask;
|
||||
r |= val;
|
||||
if (mask)
|
||||
writel(r, mfd->sctl_regs + reg);
|
||||
spin_unlock_irqrestore(&mfd->lock, flags);
|
||||
return r;
|
||||
}
|
||||
EXPORT_SYMBOL(sta2x11_sctl_mask);
|
||||
|
||||
u32 sta2x11_apbreg_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val)
|
||||
{
|
||||
struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev);
|
||||
u32 r;
|
||||
unsigned long flags;
|
||||
|
||||
if (!mfd) {
|
||||
dev_warn(&pdev->dev, ": can't access apb regs\n");
|
||||
return 0;
|
||||
}
|
||||
if (!mfd->apbreg_regs) {
|
||||
dev_warn(&pdev->dev, ": apb bridge not initialized\n");
|
||||
return 0;
|
||||
}
|
||||
spin_lock_irqsave(&mfd->lock, flags);
|
||||
r = readl(mfd->apbreg_regs + reg);
|
||||
r &= ~mask;
|
||||
r |= val;
|
||||
if (mask)
|
||||
writel(r, mfd->apbreg_regs + reg);
|
||||
spin_unlock_irqrestore(&mfd->lock, flags);
|
||||
return r;
|
||||
}
|
||||
EXPORT_SYMBOL(sta2x11_apbreg_mask);
|
||||
|
||||
/* Two debugfs files, for our registers (FIXME: one instance only) */
|
||||
#define REG(regname) {.name = #regname, .offset = SCTL_ ## regname}
|
||||
static struct debugfs_reg32 sta2x11_sctl_regs[] = {
|
||||
REG(SCCTL), REG(ARMCFG), REG(SCPLLCTL), REG(SCPLLFCTRL),
|
||||
REG(SCRESFRACT), REG(SCRESCTRL1), REG(SCRESXTRL2), REG(SCPEREN0),
|
||||
REG(SCPEREN1), REG(SCPEREN2), REG(SCGRST), REG(SCPCIPMCR1),
|
||||
REG(SCPCIPMCR2), REG(SCPCIPMSR1), REG(SCPCIPMSR2), REG(SCPCIPMSR3),
|
||||
REG(SCINTREN), REG(SCRISR), REG(SCCLKSTAT0), REG(SCCLKSTAT1),
|
||||
REG(SCCLKSTAT2), REG(SCRSTSTA),
|
||||
};
|
||||
#undef REG
|
||||
|
||||
static struct debugfs_regset32 sctl_regset = {
|
||||
.regs = sta2x11_sctl_regs,
|
||||
.nregs = ARRAY_SIZE(sta2x11_sctl_regs),
|
||||
};
|
||||
|
||||
#define REG(regname) {.name = #regname, .offset = regname}
|
||||
static struct debugfs_reg32 sta2x11_apbreg_regs[] = {
|
||||
REG(APBREG_BSR), REG(APBREG_PAER), REG(APBREG_PWAC), REG(APBREG_PRAC),
|
||||
REG(APBREG_PCG), REG(APBREG_PUR), REG(APBREG_EMU_PCG),
|
||||
};
|
||||
#undef REG
|
||||
|
||||
static struct debugfs_regset32 apbreg_regset = {
|
||||
.regs = sta2x11_apbreg_regs,
|
||||
.nregs = ARRAY_SIZE(sta2x11_apbreg_regs),
|
||||
};
|
||||
|
||||
static struct dentry *sta2x11_sctl_debugfs;
|
||||
static struct dentry *sta2x11_apbreg_debugfs;
|
||||
|
||||
/* Probe for the two platform devices */
|
||||
static int sta2x11_sctl_probe(struct platform_device *dev)
|
||||
{
|
||||
struct pci_dev **pdev;
|
||||
struct sta2x11_mfd *mfd;
|
||||
struct resource *res;
|
||||
|
||||
pdev = dev->dev.platform_data;
|
||||
mfd = sta2x11_mfd_find(*pdev);
|
||||
if (!mfd)
|
||||
return -ENODEV;
|
||||
|
||||
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!request_mem_region(res->start, resource_size(res),
|
||||
"sta2x11-sctl"))
|
||||
return -EBUSY;
|
||||
|
||||
mfd->sctl_regs = ioremap(res->start, resource_size(res));
|
||||
if (!mfd->sctl_regs) {
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
return -ENOMEM;
|
||||
}
|
||||
sctl_regset.base = mfd->sctl_regs;
|
||||
sta2x11_sctl_debugfs = debugfs_create_regset32("sta2x11-sctl",
|
||||
S_IFREG | S_IRUGO,
|
||||
NULL, &sctl_regset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sta2x11_apbreg_probe(struct platform_device *dev)
|
||||
{
|
||||
struct pci_dev **pdev;
|
||||
struct sta2x11_mfd *mfd;
|
||||
struct resource *res;
|
||||
|
||||
pdev = dev->dev.platform_data;
|
||||
dev_dbg(&dev->dev, "%s: pdata is %p\n", __func__, pdev);
|
||||
dev_dbg(&dev->dev, "%s: *pdata is %p\n", __func__, *pdev);
|
||||
|
||||
mfd = sta2x11_mfd_find(*pdev);
|
||||
if (!mfd)
|
||||
return -ENODEV;
|
||||
|
||||
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!request_mem_region(res->start, resource_size(res),
|
||||
"sta2x11-apbreg"))
|
||||
return -EBUSY;
|
||||
|
||||
mfd->apbreg_regs = ioremap(res->start, resource_size(res));
|
||||
if (!mfd->apbreg_regs) {
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
return -ENOMEM;
|
||||
}
|
||||
dev_dbg(&dev->dev, "%s: regbase %p\n", __func__, mfd->apbreg_regs);
|
||||
|
||||
apbreg_regset.base = mfd->apbreg_regs;
|
||||
sta2x11_apbreg_debugfs = debugfs_create_regset32("sta2x11-apbreg",
|
||||
S_IFREG | S_IRUGO,
|
||||
NULL, &apbreg_regset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The two platform drivers */
|
||||
static struct platform_driver sta2x11_sctl_platform_driver = {
|
||||
.driver = {
|
||||
.name = "sta2x11-sctl",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = sta2x11_sctl_probe,
|
||||
};
|
||||
|
||||
static int __init sta2x11_sctl_init(void)
|
||||
{
|
||||
pr_info("%s\n", __func__);
|
||||
return platform_driver_register(&sta2x11_sctl_platform_driver);
|
||||
}
|
||||
|
||||
static struct platform_driver sta2x11_platform_driver = {
|
||||
.driver = {
|
||||
.name = "sta2x11-apbreg",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = sta2x11_apbreg_probe,
|
||||
};
|
||||
|
||||
static int __init sta2x11_apbreg_init(void)
|
||||
{
|
||||
pr_info("%s\n", __func__);
|
||||
return platform_driver_register(&sta2x11_platform_driver);
|
||||
}
|
||||
|
||||
/*
|
||||
* What follows is the PCI device that hosts the above two pdevs.
|
||||
* Each logic block is 4kB and they are all consecutive: we use this info.
|
||||
*/
|
||||
|
||||
/* Bar 0 */
|
||||
enum bar0_cells {
|
||||
STA2X11_GPIO_0 = 0,
|
||||
STA2X11_GPIO_1,
|
||||
STA2X11_GPIO_2,
|
||||
STA2X11_GPIO_3,
|
||||
STA2X11_SCTL,
|
||||
STA2X11_SCR,
|
||||
STA2X11_TIME,
|
||||
};
|
||||
/* Bar 1 */
|
||||
enum bar1_cells {
|
||||
STA2X11_APBREG = 0,
|
||||
};
|
||||
#define CELL_4K(_name, _cell) { \
|
||||
.name = _name, \
|
||||
.start = _cell * 4096, .end = _cell * 4096 + 4095, \
|
||||
.flags = IORESOURCE_MEM, \
|
||||
}
|
||||
|
||||
static const __devinitconst struct resource gpio_resources[] = {
|
||||
{
|
||||
.name = "sta2x11_gpio", /* 4 consecutive cells, 1 driver */
|
||||
.start = 0,
|
||||
.end = (4 * 4096) - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
}
|
||||
};
|
||||
static const __devinitconst struct resource sctl_resources[] = {
|
||||
CELL_4K("sta2x11-sctl", STA2X11_SCTL),
|
||||
};
|
||||
static const __devinitconst struct resource scr_resources[] = {
|
||||
CELL_4K("sta2x11-scr", STA2X11_SCR),
|
||||
};
|
||||
static const __devinitconst struct resource time_resources[] = {
|
||||
CELL_4K("sta2x11-time", STA2X11_TIME),
|
||||
};
|
||||
|
||||
static const __devinitconst struct resource apbreg_resources[] = {
|
||||
CELL_4K("sta2x11-apbreg", STA2X11_APBREG),
|
||||
};
|
||||
|
||||
#define DEV(_name, _r) \
|
||||
{ .name = _name, .num_resources = ARRAY_SIZE(_r), .resources = _r, }
|
||||
|
||||
static __devinitdata struct mfd_cell sta2x11_mfd_bar0[] = {
|
||||
DEV("sta2x11-gpio", gpio_resources), /* offset 0: we add pdata later */
|
||||
DEV("sta2x11-sctl", sctl_resources),
|
||||
DEV("sta2x11-scr", scr_resources),
|
||||
DEV("sta2x11-time", time_resources),
|
||||
};
|
||||
|
||||
static __devinitdata struct mfd_cell sta2x11_mfd_bar1[] = {
|
||||
DEV("sta2x11-apbreg", apbreg_resources),
|
||||
};
|
||||
|
||||
static int sta2x11_mfd_suspend(struct pci_dev *pdev, pm_message_t state)
|
||||
{
|
||||
pci_save_state(pdev);
|
||||
pci_disable_device(pdev);
|
||||
pci_set_power_state(pdev, pci_choose_state(pdev, state));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sta2x11_mfd_resume(struct pci_dev *pdev)
|
||||
{
|
||||
int err;
|
||||
|
||||
pci_set_power_state(pdev, 0);
|
||||
err = pci_enable_device(pdev);
|
||||
if (err)
|
||||
return err;
|
||||
pci_restore_state(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit sta2x11_mfd_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *pci_id)
|
||||
{
|
||||
int err, i;
|
||||
struct sta2x11_gpio_pdata *gpio_data;
|
||||
|
||||
dev_info(&pdev->dev, "%s\n", __func__);
|
||||
|
||||
err = pci_enable_device(pdev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Can't enable device.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = pci_enable_msi(pdev);
|
||||
if (err)
|
||||
dev_info(&pdev->dev, "Enable msi failed\n");
|
||||
|
||||
/* Read gpio config data as pci device's platform data */
|
||||
gpio_data = dev_get_platdata(&pdev->dev);
|
||||
if (!gpio_data)
|
||||
dev_warn(&pdev->dev, "no gpio configuration\n");
|
||||
|
||||
dev_dbg(&pdev->dev, "%s, gpio_data = %p (%p)\n", __func__,
|
||||
gpio_data, &gpio_data);
|
||||
dev_dbg(&pdev->dev, "%s, pdev = %p (%p)\n", __func__,
|
||||
pdev, &pdev);
|
||||
|
||||
/* platform data is the pci device for all of them */
|
||||
for (i = 0; i < ARRAY_SIZE(sta2x11_mfd_bar0); i++) {
|
||||
sta2x11_mfd_bar0[i].pdata_size = sizeof(pdev);
|
||||
sta2x11_mfd_bar0[i].platform_data = &pdev;
|
||||
}
|
||||
sta2x11_mfd_bar1[0].pdata_size = sizeof(pdev);
|
||||
sta2x11_mfd_bar1[0].platform_data = &pdev;
|
||||
|
||||
/* Record this pdev before mfd_add_devices: their probe looks for it */
|
||||
sta2x11_mfd_add(pdev, GFP_ATOMIC);
|
||||
|
||||
|
||||
err = mfd_add_devices(&pdev->dev, -1,
|
||||
sta2x11_mfd_bar0,
|
||||
ARRAY_SIZE(sta2x11_mfd_bar0),
|
||||
&pdev->resource[0],
|
||||
0);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "mfd_add_devices[0] failed: %d\n", err);
|
||||
goto err_disable;
|
||||
}
|
||||
|
||||
err = mfd_add_devices(&pdev->dev, -1,
|
||||
sta2x11_mfd_bar1,
|
||||
ARRAY_SIZE(sta2x11_mfd_bar1),
|
||||
&pdev->resource[1],
|
||||
0);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "mfd_add_devices[1] failed: %d\n", err);
|
||||
goto err_disable;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_disable:
|
||||
mfd_remove_devices(&pdev->dev);
|
||||
pci_disable_device(pdev);
|
||||
pci_disable_msi(pdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(sta2x11_mfd_tbl) = {
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_GPIO)},
|
||||
{0,},
|
||||
};
|
||||
|
||||
static struct pci_driver sta2x11_mfd_driver = {
|
||||
.name = "sta2x11-mfd",
|
||||
.id_table = sta2x11_mfd_tbl,
|
||||
.probe = sta2x11_mfd_probe,
|
||||
.suspend = sta2x11_mfd_suspend,
|
||||
.resume = sta2x11_mfd_resume,
|
||||
};
|
||||
|
||||
static int __init sta2x11_mfd_init(void)
|
||||
{
|
||||
pr_info("%s\n", __func__);
|
||||
return pci_register_driver(&sta2x11_mfd_driver);
|
||||
}
|
||||
|
||||
/*
|
||||
* All of this must be ready before "normal" devices like MMCI appear.
|
||||
* But MFD (the pci device) can't be too early. The following choice
|
||||
* prepares platform drivers very early and probe the PCI device later,
|
||||
* but before other PCI devices.
|
||||
*/
|
||||
subsys_initcall(sta2x11_apbreg_init);
|
||||
subsys_initcall(sta2x11_sctl_init);
|
||||
rootfs_initcall(sta2x11_mfd_init);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Wind River");
|
||||
MODULE_DESCRIPTION("STA2x11 mfd for GPIO, SCTL and APBREG");
|
||||
MODULE_DEVICE_TABLE(pci, sta2x11_mfd_tbl);
|
@ -122,7 +122,6 @@ MODULE_DEVICE_TABLE(spi, stmpe_id);
|
||||
static struct spi_driver stmpe_spi_driver = {
|
||||
.driver = {
|
||||
.name = "stmpe-spi",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &stmpe_dev_pm_ops,
|
||||
|
@ -283,27 +283,24 @@ static int __devinit tps65090_i2c_probe(struct i2c_client *client,
|
||||
}
|
||||
}
|
||||
|
||||
tps65090->rmap = regmap_init_i2c(tps65090->client,
|
||||
&tps65090_regmap_config);
|
||||
tps65090->rmap = devm_regmap_init_i2c(tps65090->client,
|
||||
&tps65090_regmap_config);
|
||||
if (IS_ERR(tps65090->rmap)) {
|
||||
dev_err(&client->dev, "regmap_init failed with err: %ld\n",
|
||||
PTR_ERR(tps65090->rmap));
|
||||
ret = PTR_ERR(tps65090->rmap);
|
||||
dev_err(&client->dev, "regmap_init failed with err: %d\n", ret);
|
||||
goto err_irq_exit;
|
||||
};
|
||||
}
|
||||
|
||||
ret = mfd_add_devices(tps65090->dev, -1, tps65090s,
|
||||
ARRAY_SIZE(tps65090s), NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "add mfd devices failed with err: %d\n",
|
||||
ret);
|
||||
goto err_regmap_exit;
|
||||
goto err_irq_exit;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_regmap_exit:
|
||||
regmap_exit(tps65090->rmap);
|
||||
|
||||
err_irq_exit:
|
||||
if (client->irq)
|
||||
free_irq(client->irq, tps65090);
|
||||
@ -316,29 +313,34 @@ static int __devexit tps65090_i2c_remove(struct i2c_client *client)
|
||||
struct tps65090 *tps65090 = i2c_get_clientdata(client);
|
||||
|
||||
mfd_remove_devices(tps65090->dev);
|
||||
regmap_exit(tps65090->rmap);
|
||||
if (client->irq)
|
||||
free_irq(client->irq, tps65090);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int tps65090_i2c_suspend(struct i2c_client *client, pm_message_t state)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tps65090_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
if (client->irq)
|
||||
disable_irq(client->irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tps65090_i2c_resume(struct i2c_client *client)
|
||||
static int tps65090_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
if (client->irq)
|
||||
enable_irq(client->irq);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops tps65090_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(tps65090_suspend, tps65090_resume)
|
||||
};
|
||||
|
||||
static const struct i2c_device_id tps65090_id_table[] = {
|
||||
{ "tps65090", 0 },
|
||||
{ },
|
||||
@ -349,13 +351,10 @@ static struct i2c_driver tps65090_driver = {
|
||||
.driver = {
|
||||
.name = "tps65090",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &tps65090_pm_ops,
|
||||
},
|
||||
.probe = tps65090_i2c_probe,
|
||||
.remove = __devexit_p(tps65090_i2c_remove),
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = tps65090_i2c_suspend,
|
||||
.resume = tps65090_i2c_resume,
|
||||
#endif
|
||||
.id_table = tps65090_id_table,
|
||||
};
|
||||
|
||||
|
@ -96,7 +96,7 @@ EXPORT_SYMBOL_GPL(tps65217_reg_write);
|
||||
* @val: Value to write.
|
||||
* @level: Password protected level
|
||||
*/
|
||||
int tps65217_update_bits(struct tps65217 *tps, unsigned int reg,
|
||||
static int tps65217_update_bits(struct tps65217 *tps, unsigned int reg,
|
||||
unsigned int mask, unsigned int val, unsigned int level)
|
||||
{
|
||||
int ret;
|
||||
@ -150,7 +150,7 @@ static int __devinit tps65217_probe(struct i2c_client *client,
|
||||
return -ENOMEM;
|
||||
|
||||
tps->pdata = pdata;
|
||||
tps->regmap = regmap_init_i2c(client, &tps65217_regmap_config);
|
||||
tps->regmap = devm_regmap_init_i2c(client, &tps65217_regmap_config);
|
||||
if (IS_ERR(tps->regmap)) {
|
||||
ret = PTR_ERR(tps->regmap);
|
||||
dev_err(tps->dev, "Failed to allocate register map: %d\n",
|
||||
@ -163,9 +163,9 @@ static int __devinit tps65217_probe(struct i2c_client *client,
|
||||
|
||||
ret = tps65217_reg_read(tps, TPS65217_REG_CHIPID, &version);
|
||||
if (ret < 0) {
|
||||
dev_err(tps->dev, "Failed to read revision"
|
||||
" register: %d\n", ret);
|
||||
goto err_regmap;
|
||||
dev_err(tps->dev, "Failed to read revision register: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(tps->dev, "TPS65217 ID %#x version 1.%d\n",
|
||||
@ -190,11 +190,6 @@ static int __devinit tps65217_probe(struct i2c_client *client,
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_regmap:
|
||||
regmap_exit(tps->regmap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit tps65217_remove(struct i2c_client *client)
|
||||
@ -205,8 +200,6 @@ static int __devexit tps65217_remove(struct i2c_client *client)
|
||||
for (i = 0; i < TPS65217_NUM_REGULATOR; i++)
|
||||
platform_device_unregister(tps->regulator_pdev[i]);
|
||||
|
||||
regmap_exit(tps->regmap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -20,15 +20,10 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/mfd/tps65910.h>
|
||||
|
||||
static inline int irq_to_tps65910_irq(struct tps65910 *tps65910,
|
||||
int irq)
|
||||
{
|
||||
return (irq - tps65910->irq_base);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a threaded IRQ handler so can access I2C/SPI. Since all
|
||||
* interrupts are clear on read the IRQ line will be reasserted and
|
||||
@ -41,28 +36,28 @@ static inline int irq_to_tps65910_irq(struct tps65910 *tps65910,
|
||||
static irqreturn_t tps65910_irq(int irq, void *irq_data)
|
||||
{
|
||||
struct tps65910 *tps65910 = irq_data;
|
||||
unsigned int reg;
|
||||
u32 irq_sts;
|
||||
u32 irq_mask;
|
||||
u8 reg;
|
||||
int i;
|
||||
|
||||
tps65910->read(tps65910, TPS65910_INT_STS, 1, ®);
|
||||
tps65910_reg_read(tps65910, TPS65910_INT_STS, ®);
|
||||
irq_sts = reg;
|
||||
tps65910->read(tps65910, TPS65910_INT_STS2, 1, ®);
|
||||
tps65910_reg_read(tps65910, TPS65910_INT_STS2, ®);
|
||||
irq_sts |= reg << 8;
|
||||
switch (tps65910_chip_id(tps65910)) {
|
||||
case TPS65911:
|
||||
tps65910->read(tps65910, TPS65910_INT_STS3, 1, ®);
|
||||
tps65910_reg_read(tps65910, TPS65910_INT_STS3, ®);
|
||||
irq_sts |= reg << 16;
|
||||
}
|
||||
|
||||
tps65910->read(tps65910, TPS65910_INT_MSK, 1, ®);
|
||||
tps65910_reg_read(tps65910, TPS65910_INT_MSK, ®);
|
||||
irq_mask = reg;
|
||||
tps65910->read(tps65910, TPS65910_INT_MSK2, 1, ®);
|
||||
tps65910_reg_read(tps65910, TPS65910_INT_MSK2, ®);
|
||||
irq_mask |= reg << 8;
|
||||
switch (tps65910_chip_id(tps65910)) {
|
||||
case TPS65911:
|
||||
tps65910->read(tps65910, TPS65910_INT_MSK3, 1, ®);
|
||||
tps65910_reg_read(tps65910, TPS65910_INT_MSK3, ®);
|
||||
irq_mask |= reg << 16;
|
||||
}
|
||||
|
||||
@ -76,19 +71,19 @@ static irqreturn_t tps65910_irq(int irq, void *irq_data)
|
||||
if (!(irq_sts & (1 << i)))
|
||||
continue;
|
||||
|
||||
handle_nested_irq(tps65910->irq_base + i);
|
||||
handle_nested_irq(irq_find_mapping(tps65910->domain, i));
|
||||
}
|
||||
|
||||
/* Write the STS register back to clear IRQs we handled */
|
||||
reg = irq_sts & 0xFF;
|
||||
irq_sts >>= 8;
|
||||
tps65910->write(tps65910, TPS65910_INT_STS, 1, ®);
|
||||
tps65910_reg_write(tps65910, TPS65910_INT_STS, reg);
|
||||
reg = irq_sts & 0xFF;
|
||||
tps65910->write(tps65910, TPS65910_INT_STS2, 1, ®);
|
||||
tps65910_reg_write(tps65910, TPS65910_INT_STS2, reg);
|
||||
switch (tps65910_chip_id(tps65910)) {
|
||||
case TPS65911:
|
||||
reg = irq_sts >> 8;
|
||||
tps65910->write(tps65910, TPS65910_INT_STS3, 1, ®);
|
||||
tps65910_reg_write(tps65910, TPS65910_INT_STS3, reg);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
@ -105,27 +100,27 @@ static void tps65910_irq_sync_unlock(struct irq_data *data)
|
||||
{
|
||||
struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
|
||||
u32 reg_mask;
|
||||
u8 reg;
|
||||
unsigned int reg;
|
||||
|
||||
tps65910->read(tps65910, TPS65910_INT_MSK, 1, ®);
|
||||
tps65910_reg_read(tps65910, TPS65910_INT_MSK, ®);
|
||||
reg_mask = reg;
|
||||
tps65910->read(tps65910, TPS65910_INT_MSK2, 1, ®);
|
||||
tps65910_reg_read(tps65910, TPS65910_INT_MSK2, ®);
|
||||
reg_mask |= reg << 8;
|
||||
switch (tps65910_chip_id(tps65910)) {
|
||||
case TPS65911:
|
||||
tps65910->read(tps65910, TPS65910_INT_MSK3, 1, ®);
|
||||
tps65910_reg_read(tps65910, TPS65910_INT_MSK3, ®);
|
||||
reg_mask |= reg << 16;
|
||||
}
|
||||
|
||||
if (tps65910->irq_mask != reg_mask) {
|
||||
reg = tps65910->irq_mask & 0xFF;
|
||||
tps65910->write(tps65910, TPS65910_INT_MSK, 1, ®);
|
||||
tps65910_reg_write(tps65910, TPS65910_INT_MSK, reg);
|
||||
reg = tps65910->irq_mask >> 8 & 0xFF;
|
||||
tps65910->write(tps65910, TPS65910_INT_MSK2, 1, ®);
|
||||
tps65910_reg_write(tps65910, TPS65910_INT_MSK2, reg);
|
||||
switch (tps65910_chip_id(tps65910)) {
|
||||
case TPS65911:
|
||||
reg = tps65910->irq_mask >> 16;
|
||||
tps65910->write(tps65910, TPS65910_INT_MSK3, 1, ®);
|
||||
tps65910_reg_write(tps65910, TPS65910_INT_MSK3, reg);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&tps65910->irq_lock);
|
||||
@ -135,14 +130,14 @@ static void tps65910_irq_enable(struct irq_data *data)
|
||||
{
|
||||
struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
|
||||
|
||||
tps65910->irq_mask &= ~( 1 << irq_to_tps65910_irq(tps65910, data->irq));
|
||||
tps65910->irq_mask &= ~(1 << data->hwirq);
|
||||
}
|
||||
|
||||
static void tps65910_irq_disable(struct irq_data *data)
|
||||
{
|
||||
struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data);
|
||||
|
||||
tps65910->irq_mask |= ( 1 << irq_to_tps65910_irq(tps65910, data->irq));
|
||||
tps65910->irq_mask |= (1 << data->hwirq);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
@ -164,10 +159,35 @@ static struct irq_chip tps65910_irq_chip = {
|
||||
.irq_set_wake = tps65910_irq_set_wake,
|
||||
};
|
||||
|
||||
static int tps65910_irq_map(struct irq_domain *h, unsigned int virq,
|
||||
irq_hw_number_t hw)
|
||||
{
|
||||
struct tps65910 *tps65910 = h->host_data;
|
||||
|
||||
irq_set_chip_data(virq, tps65910);
|
||||
irq_set_chip_and_handler(virq, &tps65910_irq_chip, handle_edge_irq);
|
||||
irq_set_nested_thread(virq, 1);
|
||||
|
||||
/* ARM needs us to explicitly flag the IRQ as valid
|
||||
* and will set them noprobe when we do so. */
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(virq, IRQF_VALID);
|
||||
#else
|
||||
irq_set_noprobe(virq);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops tps65910_domain_ops = {
|
||||
.map = tps65910_irq_map,
|
||||
.xlate = irq_domain_xlate_twocell,
|
||||
};
|
||||
|
||||
int tps65910_irq_init(struct tps65910 *tps65910, int irq,
|
||||
struct tps65910_platform_data *pdata)
|
||||
{
|
||||
int ret, cur_irq;
|
||||
int ret;
|
||||
int flags = IRQF_ONESHOT;
|
||||
|
||||
if (!irq) {
|
||||
@ -175,17 +195,11 @@ int tps65910_irq_init(struct tps65910 *tps65910, int irq,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!pdata || !pdata->irq_base) {
|
||||
dev_warn(tps65910->dev, "No interrupt support, no IRQ base\n");
|
||||
if (!pdata) {
|
||||
dev_warn(tps65910->dev, "No interrupt support, no pdata\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tps65910->irq_mask = 0xFFFFFF;
|
||||
|
||||
mutex_init(&tps65910->irq_lock);
|
||||
tps65910->chip_irq = irq;
|
||||
tps65910->irq_base = pdata->irq_base;
|
||||
|
||||
switch (tps65910_chip_id(tps65910)) {
|
||||
case TPS65910:
|
||||
tps65910->irq_num = TPS65910_NUM_IRQ;
|
||||
@ -195,22 +209,36 @@ int tps65910_irq_init(struct tps65910 *tps65910, int irq,
|
||||
break;
|
||||
}
|
||||
|
||||
/* Register with genirq */
|
||||
for (cur_irq = tps65910->irq_base;
|
||||
cur_irq < tps65910->irq_num + tps65910->irq_base;
|
||||
cur_irq++) {
|
||||
irq_set_chip_data(cur_irq, tps65910);
|
||||
irq_set_chip_and_handler(cur_irq, &tps65910_irq_chip,
|
||||
handle_edge_irq);
|
||||
irq_set_nested_thread(cur_irq, 1);
|
||||
if (pdata->irq_base > 0) {
|
||||
pdata->irq_base = irq_alloc_descs(pdata->irq_base, 0,
|
||||
tps65910->irq_num, -1);
|
||||
if (pdata->irq_base < 0) {
|
||||
dev_warn(tps65910->dev, "Failed to alloc IRQs: %d\n",
|
||||
pdata->irq_base);
|
||||
return pdata->irq_base;
|
||||
}
|
||||
}
|
||||
|
||||
/* ARM needs us to explicitly flag the IRQ as valid
|
||||
* and will set them noprobe when we do so. */
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(cur_irq, IRQF_VALID);
|
||||
#else
|
||||
irq_set_noprobe(cur_irq);
|
||||
#endif
|
||||
tps65910->irq_mask = 0xFFFFFF;
|
||||
|
||||
mutex_init(&tps65910->irq_lock);
|
||||
tps65910->chip_irq = irq;
|
||||
tps65910->irq_base = pdata->irq_base;
|
||||
|
||||
if (pdata->irq_base > 0)
|
||||
tps65910->domain = irq_domain_add_legacy(tps65910->dev->of_node,
|
||||
tps65910->irq_num,
|
||||
pdata->irq_base,
|
||||
0,
|
||||
&tps65910_domain_ops, tps65910);
|
||||
else
|
||||
tps65910->domain = irq_domain_add_linear(tps65910->dev->of_node,
|
||||
tps65910->irq_num,
|
||||
&tps65910_domain_ops, tps65910);
|
||||
|
||||
if (!tps65910->domain) {
|
||||
dev_err(tps65910->dev, "Failed to create IRQ domain\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = request_threaded_irq(irq, NULL, tps65910_irq, flags,
|
||||
|
@ -19,12 +19,15 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/tps65910.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
static struct mfd_cell tps65910s[] = {
|
||||
{
|
||||
.name = "tps65910-gpio",
|
||||
},
|
||||
{
|
||||
.name = "tps65910-pmic",
|
||||
},
|
||||
@ -37,30 +40,6 @@ static struct mfd_cell tps65910s[] = {
|
||||
};
|
||||
|
||||
|
||||
static int tps65910_i2c_read(struct tps65910 *tps65910, u8 reg,
|
||||
int bytes, void *dest)
|
||||
{
|
||||
return regmap_bulk_read(tps65910->regmap, reg, dest, bytes);
|
||||
}
|
||||
|
||||
static int tps65910_i2c_write(struct tps65910 *tps65910, u8 reg,
|
||||
int bytes, void *src)
|
||||
{
|
||||
return regmap_bulk_write(tps65910->regmap, reg, src, bytes);
|
||||
}
|
||||
|
||||
int tps65910_set_bits(struct tps65910 *tps65910, u8 reg, u8 mask)
|
||||
{
|
||||
return regmap_update_bits(tps65910->regmap, reg, mask, mask);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tps65910_set_bits);
|
||||
|
||||
int tps65910_clear_bits(struct tps65910 *tps65910, u8 reg, u8 mask)
|
||||
{
|
||||
return regmap_update_bits(tps65910->regmap, reg, mask, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tps65910_clear_bits);
|
||||
|
||||
static bool is_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
struct tps65910 *tps65910 = dev_get_drvdata(dev);
|
||||
@ -85,80 +64,197 @@ static const struct regmap_config tps65910_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.volatile_reg = is_volatile_reg,
|
||||
.max_register = TPS65910_MAX_REGISTER,
|
||||
.num_reg_defaults_raw = TPS65910_MAX_REGISTER,
|
||||
.max_register = TPS65910_MAX_REGISTER - 1,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static int tps65910_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
static int __devinit tps65910_sleepinit(struct tps65910 *tps65910,
|
||||
struct tps65910_board *pmic_pdata)
|
||||
{
|
||||
struct device *dev = NULL;
|
||||
int ret = 0;
|
||||
|
||||
dev = tps65910->dev;
|
||||
|
||||
if (!pmic_pdata->en_dev_slp)
|
||||
return 0;
|
||||
|
||||
/* enabling SLEEP device state */
|
||||
ret = tps65910_reg_set_bits(tps65910, TPS65910_DEVCTRL,
|
||||
DEVCTRL_DEV_SLP_MASK);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "set dev_slp failed: %d\n", ret);
|
||||
goto err_sleep_init;
|
||||
}
|
||||
|
||||
/* Return if there is no sleep keepon data. */
|
||||
if (!pmic_pdata->slp_keepon)
|
||||
return 0;
|
||||
|
||||
if (pmic_pdata->slp_keepon->therm_keepon) {
|
||||
ret = tps65910_reg_set_bits(tps65910,
|
||||
TPS65910_SLEEP_KEEP_RES_ON,
|
||||
SLEEP_KEEP_RES_ON_THERM_KEEPON_MASK);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "set therm_keepon failed: %d\n", ret);
|
||||
goto disable_dev_slp;
|
||||
}
|
||||
}
|
||||
|
||||
if (pmic_pdata->slp_keepon->clkout32k_keepon) {
|
||||
ret = tps65910_reg_set_bits(tps65910,
|
||||
TPS65910_SLEEP_KEEP_RES_ON,
|
||||
SLEEP_KEEP_RES_ON_CLKOUT32K_KEEPON_MASK);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "set clkout32k_keepon failed: %d\n", ret);
|
||||
goto disable_dev_slp;
|
||||
}
|
||||
}
|
||||
|
||||
if (pmic_pdata->slp_keepon->i2chs_keepon) {
|
||||
ret = tps65910_reg_set_bits(tps65910,
|
||||
TPS65910_SLEEP_KEEP_RES_ON,
|
||||
SLEEP_KEEP_RES_ON_I2CHS_KEEPON_MASK);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "set i2chs_keepon failed: %d\n", ret);
|
||||
goto disable_dev_slp;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
disable_dev_slp:
|
||||
tps65910_reg_clear_bits(tps65910, TPS65910_DEVCTRL,
|
||||
DEVCTRL_DEV_SLP_MASK);
|
||||
|
||||
err_sleep_init:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id tps65910_of_match[] = {
|
||||
{ .compatible = "ti,tps65910", .data = (void *)TPS65910},
|
||||
{ .compatible = "ti,tps65911", .data = (void *)TPS65911},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tps65910_of_match);
|
||||
|
||||
static struct tps65910_board *tps65910_parse_dt(struct i2c_client *client,
|
||||
int *chip_id)
|
||||
{
|
||||
struct device_node *np = client->dev.of_node;
|
||||
struct tps65910_board *board_info;
|
||||
unsigned int prop;
|
||||
const struct of_device_id *match;
|
||||
int ret = 0;
|
||||
|
||||
match = of_match_device(tps65910_of_match, &client->dev);
|
||||
if (!match) {
|
||||
dev_err(&client->dev, "Failed to find matching dt id\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*chip_id = (int)match->data;
|
||||
|
||||
board_info = devm_kzalloc(&client->dev, sizeof(*board_info),
|
||||
GFP_KERNEL);
|
||||
if (!board_info) {
|
||||
dev_err(&client->dev, "Failed to allocate pdata\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "ti,vmbch-threshold", &prop);
|
||||
if (!ret)
|
||||
board_info->vmbch_threshold = prop;
|
||||
else if (*chip_id == TPS65911)
|
||||
dev_warn(&client->dev, "VMBCH-Threshold not specified");
|
||||
|
||||
ret = of_property_read_u32(np, "ti,vmbch2-threshold", &prop);
|
||||
if (!ret)
|
||||
board_info->vmbch2_threshold = prop;
|
||||
else if (*chip_id == TPS65911)
|
||||
dev_warn(&client->dev, "VMBCH2-Threshold not specified");
|
||||
|
||||
board_info->irq = client->irq;
|
||||
board_info->irq_base = -1;
|
||||
|
||||
return board_info;
|
||||
}
|
||||
#else
|
||||
static inline
|
||||
struct tps65910_board *tps65910_parse_dt(struct i2c_client *client,
|
||||
int *chip_id)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static __devinit int tps65910_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct tps65910 *tps65910;
|
||||
struct tps65910_board *pmic_plat_data;
|
||||
struct tps65910_board *of_pmic_plat_data = NULL;
|
||||
struct tps65910_platform_data *init_data;
|
||||
int ret = 0;
|
||||
int chip_id = id->driver_data;
|
||||
|
||||
pmic_plat_data = dev_get_platdata(&i2c->dev);
|
||||
|
||||
if (!pmic_plat_data && i2c->dev.of_node) {
|
||||
pmic_plat_data = tps65910_parse_dt(i2c, &chip_id);
|
||||
of_pmic_plat_data = pmic_plat_data;
|
||||
}
|
||||
|
||||
if (!pmic_plat_data)
|
||||
return -EINVAL;
|
||||
|
||||
init_data = kzalloc(sizeof(struct tps65910_platform_data), GFP_KERNEL);
|
||||
init_data = devm_kzalloc(&i2c->dev, sizeof(*init_data), GFP_KERNEL);
|
||||
if (init_data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
tps65910 = kzalloc(sizeof(struct tps65910), GFP_KERNEL);
|
||||
if (tps65910 == NULL) {
|
||||
kfree(init_data);
|
||||
tps65910 = devm_kzalloc(&i2c->dev, sizeof(*tps65910), GFP_KERNEL);
|
||||
if (tps65910 == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tps65910->of_plat_data = of_pmic_plat_data;
|
||||
i2c_set_clientdata(i2c, tps65910);
|
||||
tps65910->dev = &i2c->dev;
|
||||
tps65910->i2c_client = i2c;
|
||||
tps65910->id = id->driver_data;
|
||||
tps65910->read = tps65910_i2c_read;
|
||||
tps65910->write = tps65910_i2c_write;
|
||||
tps65910->id = chip_id;
|
||||
mutex_init(&tps65910->io_mutex);
|
||||
|
||||
tps65910->regmap = regmap_init_i2c(i2c, &tps65910_regmap_config);
|
||||
tps65910->regmap = devm_regmap_init_i2c(i2c, &tps65910_regmap_config);
|
||||
if (IS_ERR(tps65910->regmap)) {
|
||||
ret = PTR_ERR(tps65910->regmap);
|
||||
dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret);
|
||||
goto regmap_err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mfd_add_devices(tps65910->dev, -1,
|
||||
tps65910s, ARRAY_SIZE(tps65910s),
|
||||
NULL, 0);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c->dev, "mfd_add_devices failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
init_data->irq = pmic_plat_data->irq;
|
||||
init_data->irq_base = pmic_plat_data->irq_base;
|
||||
|
||||
tps65910_gpio_init(tps65910, pmic_plat_data->gpio_base);
|
||||
|
||||
tps65910_irq_init(tps65910, init_data->irq, init_data);
|
||||
|
||||
kfree(init_data);
|
||||
return ret;
|
||||
tps65910_sleepinit(tps65910, pmic_plat_data);
|
||||
|
||||
err:
|
||||
regmap_exit(tps65910->regmap);
|
||||
regmap_err:
|
||||
kfree(tps65910);
|
||||
kfree(init_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tps65910_i2c_remove(struct i2c_client *i2c)
|
||||
static __devexit int tps65910_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
struct tps65910 *tps65910 = i2c_get_clientdata(i2c);
|
||||
|
||||
tps65910_irq_exit(tps65910);
|
||||
mfd_remove_devices(tps65910->dev);
|
||||
regmap_exit(tps65910->regmap);
|
||||
kfree(tps65910);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -175,9 +271,10 @@ static struct i2c_driver tps65910_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "tps65910",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(tps65910_of_match),
|
||||
},
|
||||
.probe = tps65910_i2c_probe,
|
||||
.remove = tps65910_i2c_remove,
|
||||
.remove = __devexit_p(tps65910_i2c_remove),
|
||||
.id_table = tps65910_i2c_id,
|
||||
};
|
||||
|
||||
|
@ -757,6 +757,7 @@ int twl4030_init_irq(struct device *dev, int irq_num)
|
||||
dev_err(dev, "could not claim irq%d: %d\n", irq_num, status);
|
||||
goto fail_rqirq;
|
||||
}
|
||||
enable_irq_wake(irq_num);
|
||||
|
||||
return irq_base;
|
||||
fail_rqirq:
|
||||
|
@ -27,7 +27,12 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
@ -35,8 +40,24 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/twl6040.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define VIBRACTRL_MEMBER(reg) ((reg == TWL6040_REG_VIBCTLL) ? 0 : 1)
|
||||
#define TWL6040_NUM_SUPPLIES (2)
|
||||
|
||||
static bool twl6040_has_vibra(struct twl6040_platform_data *pdata,
|
||||
struct device_node *node)
|
||||
{
|
||||
if (pdata && pdata->vibra)
|
||||
return true;
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
if (of_find_node_by_name(node, "vibra"))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg)
|
||||
{
|
||||
@ -502,17 +523,18 @@ static int __devinit twl6040_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct twl6040_platform_data *pdata = client->dev.platform_data;
|
||||
struct device_node *node = client->dev.of_node;
|
||||
struct twl6040 *twl6040;
|
||||
struct mfd_cell *cell = NULL;
|
||||
int ret, children = 0;
|
||||
int irq, ret, children = 0;
|
||||
|
||||
if (!pdata) {
|
||||
if (!pdata && !node) {
|
||||
dev_err(&client->dev, "Platform data is missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* In order to operate correctly we need valid interrupt config */
|
||||
if (!client->irq || !pdata->irq_base) {
|
||||
if (!client->irq) {
|
||||
dev_err(&client->dev, "Invalid IRQ configuration\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -524,7 +546,7 @@ static int __devinit twl6040_probe(struct i2c_client *client,
|
||||
goto err;
|
||||
}
|
||||
|
||||
twl6040->regmap = regmap_init_i2c(client, &twl6040_regmap_config);
|
||||
twl6040->regmap = devm_regmap_init_i2c(client, &twl6040_regmap_config);
|
||||
if (IS_ERR(twl6040->regmap)) {
|
||||
ret = PTR_ERR(twl6040->regmap);
|
||||
goto err;
|
||||
@ -532,9 +554,23 @@ static int __devinit twl6040_probe(struct i2c_client *client,
|
||||
|
||||
i2c_set_clientdata(client, twl6040);
|
||||
|
||||
twl6040->supplies[0].supply = "vio";
|
||||
twl6040->supplies[1].supply = "v2v1";
|
||||
ret = regulator_bulk_get(&client->dev, TWL6040_NUM_SUPPLIES,
|
||||
twl6040->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(&client->dev, "Failed to get supplies: %d\n", ret);
|
||||
goto regulator_get_err;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
|
||||
if (ret != 0) {
|
||||
dev_err(&client->dev, "Failed to enable supplies: %d\n", ret);
|
||||
goto power_err;
|
||||
}
|
||||
|
||||
twl6040->dev = &client->dev;
|
||||
twl6040->irq = client->irq;
|
||||
twl6040->irq_base = pdata->irq_base;
|
||||
|
||||
mutex_init(&twl6040->mutex);
|
||||
mutex_init(&twl6040->io_mutex);
|
||||
@ -543,22 +579,26 @@ static int __devinit twl6040_probe(struct i2c_client *client,
|
||||
twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
|
||||
|
||||
/* ERRATA: Automatic power-up is not possible in ES1.0 */
|
||||
if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0)
|
||||
twl6040->audpwron = pdata->audpwron_gpio;
|
||||
else
|
||||
if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0) {
|
||||
if (pdata)
|
||||
twl6040->audpwron = pdata->audpwron_gpio;
|
||||
else
|
||||
twl6040->audpwron = of_get_named_gpio(node,
|
||||
"ti,audpwron-gpio", 0);
|
||||
} else
|
||||
twl6040->audpwron = -EINVAL;
|
||||
|
||||
if (gpio_is_valid(twl6040->audpwron)) {
|
||||
ret = gpio_request_one(twl6040->audpwron, GPIOF_OUT_INIT_LOW,
|
||||
"audpwron");
|
||||
if (ret)
|
||||
goto gpio1_err;
|
||||
goto gpio_err;
|
||||
}
|
||||
|
||||
/* codec interrupt */
|
||||
ret = twl6040_irq_init(twl6040);
|
||||
if (ret)
|
||||
goto gpio2_err;
|
||||
goto irq_init_err;
|
||||
|
||||
ret = request_threaded_irq(twl6040->irq_base + TWL6040_IRQ_READY,
|
||||
NULL, twl6040_naudint_handler, 0,
|
||||
@ -572,22 +612,27 @@ static int __devinit twl6040_probe(struct i2c_client *client,
|
||||
/* dual-access registers controlled by I2C only */
|
||||
twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL);
|
||||
|
||||
if (pdata->codec) {
|
||||
int irq = twl6040->irq_base + TWL6040_IRQ_PLUG;
|
||||
|
||||
cell = &twl6040->cells[children];
|
||||
cell->name = "twl6040-codec";
|
||||
twl6040_codec_rsrc[0].start = irq;
|
||||
twl6040_codec_rsrc[0].end = irq;
|
||||
cell->resources = twl6040_codec_rsrc;
|
||||
cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc);
|
||||
/*
|
||||
* The main functionality of twl6040 to provide audio on OMAP4+ systems.
|
||||
* We can add the ASoC codec child whenever this driver has been loaded.
|
||||
* The ASoC codec can work without pdata, pass the platform_data only if
|
||||
* it has been provided.
|
||||
*/
|
||||
irq = twl6040->irq_base + TWL6040_IRQ_PLUG;
|
||||
cell = &twl6040->cells[children];
|
||||
cell->name = "twl6040-codec";
|
||||
twl6040_codec_rsrc[0].start = irq;
|
||||
twl6040_codec_rsrc[0].end = irq;
|
||||
cell->resources = twl6040_codec_rsrc;
|
||||
cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc);
|
||||
if (pdata && pdata->codec) {
|
||||
cell->platform_data = pdata->codec;
|
||||
cell->pdata_size = sizeof(*pdata->codec);
|
||||
children++;
|
||||
}
|
||||
children++;
|
||||
|
||||
if (pdata->vibra) {
|
||||
int irq = twl6040->irq_base + TWL6040_IRQ_VIB;
|
||||
if (twl6040_has_vibra(pdata, node)) {
|
||||
irq = twl6040->irq_base + TWL6040_IRQ_VIB;
|
||||
|
||||
cell = &twl6040->cells[children];
|
||||
cell->name = "twl6040-vibra";
|
||||
@ -596,21 +641,17 @@ static int __devinit twl6040_probe(struct i2c_client *client,
|
||||
cell->resources = twl6040_vibra_rsrc;
|
||||
cell->num_resources = ARRAY_SIZE(twl6040_vibra_rsrc);
|
||||
|
||||
cell->platform_data = pdata->vibra;
|
||||
cell->pdata_size = sizeof(*pdata->vibra);
|
||||
if (pdata && pdata->vibra) {
|
||||
cell->platform_data = pdata->vibra;
|
||||
cell->pdata_size = sizeof(*pdata->vibra);
|
||||
}
|
||||
children++;
|
||||
}
|
||||
|
||||
if (children) {
|
||||
ret = mfd_add_devices(&client->dev, -1, twl6040->cells,
|
||||
children, NULL, 0);
|
||||
if (ret)
|
||||
goto mfd_err;
|
||||
} else {
|
||||
dev_err(&client->dev, "No platform data found for children\n");
|
||||
ret = -ENODEV;
|
||||
ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children,
|
||||
NULL, 0);
|
||||
if (ret)
|
||||
goto mfd_err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@ -618,12 +659,15 @@ mfd_err:
|
||||
free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040);
|
||||
irq_err:
|
||||
twl6040_irq_exit(twl6040);
|
||||
gpio2_err:
|
||||
irq_init_err:
|
||||
if (gpio_is_valid(twl6040->audpwron))
|
||||
gpio_free(twl6040->audpwron);
|
||||
gpio1_err:
|
||||
gpio_err:
|
||||
regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
|
||||
power_err:
|
||||
regulator_bulk_free(TWL6040_NUM_SUPPLIES, twl6040->supplies);
|
||||
regulator_get_err:
|
||||
i2c_set_clientdata(client, NULL);
|
||||
regmap_exit(twl6040->regmap);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
@ -643,7 +687,9 @@ static int __devexit twl6040_remove(struct i2c_client *client)
|
||||
|
||||
mfd_remove_devices(&client->dev);
|
||||
i2c_set_clientdata(client, NULL);
|
||||
regmap_exit(twl6040->regmap);
|
||||
|
||||
regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies);
|
||||
regulator_bulk_free(TWL6040_NUM_SUPPLIES, twl6040->supplies);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -23,7 +23,10 @@
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/twl6040.h>
|
||||
@ -138,7 +141,8 @@ static irqreturn_t twl6040_irq_thread(int irq, void *data)
|
||||
|
||||
int twl6040_irq_init(struct twl6040 *twl6040)
|
||||
{
|
||||
int cur_irq, ret;
|
||||
struct device_node *node = twl6040->dev->of_node;
|
||||
int i, nr_irqs, irq_base, ret;
|
||||
u8 val;
|
||||
|
||||
mutex_init(&twl6040->irq_mutex);
|
||||
@ -148,21 +152,31 @@ int twl6040_irq_init(struct twl6040 *twl6040)
|
||||
twl6040->irq_masks_cache = TWL6040_ALLINT_MSK;
|
||||
twl6040_reg_write(twl6040, TWL6040_REG_INTMR, TWL6040_ALLINT_MSK);
|
||||
|
||||
nr_irqs = ARRAY_SIZE(twl6040_irqs);
|
||||
|
||||
irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0);
|
||||
if (IS_ERR_VALUE(irq_base)) {
|
||||
dev_err(twl6040->dev, "Fail to allocate IRQ descs\n");
|
||||
return irq_base;
|
||||
}
|
||||
twl6040->irq_base = irq_base;
|
||||
|
||||
irq_domain_add_legacy(node, ARRAY_SIZE(twl6040_irqs), irq_base, 0,
|
||||
&irq_domain_simple_ops, NULL);
|
||||
|
||||
/* Register them with genirq */
|
||||
for (cur_irq = twl6040->irq_base;
|
||||
cur_irq < twl6040->irq_base + ARRAY_SIZE(twl6040_irqs);
|
||||
cur_irq++) {
|
||||
irq_set_chip_data(cur_irq, twl6040);
|
||||
irq_set_chip_and_handler(cur_irq, &twl6040_irq_chip,
|
||||
for (i = irq_base; i < irq_base + nr_irqs; i++) {
|
||||
irq_set_chip_data(i, twl6040);
|
||||
irq_set_chip_and_handler(i, &twl6040_irq_chip,
|
||||
handle_level_irq);
|
||||
irq_set_nested_thread(cur_irq, 1);
|
||||
irq_set_nested_thread(i, 1);
|
||||
|
||||
/* ARM needs us to explicitly flag the IRQ as valid
|
||||
* and will set them noprobe when we do so. */
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(cur_irq, IRQF_VALID);
|
||||
set_irq_flags(i, IRQF_VALID);
|
||||
#else
|
||||
irq_set_noprobe(cur_irq);
|
||||
irq_set_noprobe(i);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -131,17 +131,7 @@ static struct pci_driver vx855_pci_driver = {
|
||||
.remove = __devexit_p(vx855_remove),
|
||||
};
|
||||
|
||||
static int vx855_init(void)
|
||||
{
|
||||
return pci_register_driver(&vx855_pci_driver);
|
||||
}
|
||||
module_init(vx855_init);
|
||||
|
||||
static void vx855_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&vx855_pci_driver);
|
||||
}
|
||||
module_exit(vx855_exit);
|
||||
module_pci_driver(vx855_pci_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>");
|
||||
|
@ -280,11 +280,11 @@ void wm831x_auxadc_init(struct wm831x *wm831x)
|
||||
mutex_init(&wm831x->auxadc_lock);
|
||||
INIT_LIST_HEAD(&wm831x->auxadc_pending);
|
||||
|
||||
if (wm831x->irq && wm831x->irq_base) {
|
||||
if (wm831x->irq) {
|
||||
wm831x->auxadc_read = wm831x_auxadc_read_irq;
|
||||
|
||||
ret = request_threaded_irq(wm831x->irq_base +
|
||||
WM831X_IRQ_AUXADC_DATA,
|
||||
ret = request_threaded_irq(wm831x_irq(wm831x,
|
||||
WM831X_IRQ_AUXADC_DATA),
|
||||
NULL, wm831x_auxadc_irq, 0,
|
||||
"auxadc", wm831x);
|
||||
if (ret < 0) {
|
||||
|
@ -614,8 +614,15 @@ int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm831x_set_bits);
|
||||
|
||||
static struct resource wm831x_io_parent = {
|
||||
.start = 0,
|
||||
.end = 0xffffffff,
|
||||
.flags = IORESOURCE_IO,
|
||||
};
|
||||
|
||||
static struct resource wm831x_dcdc1_resources[] = {
|
||||
{
|
||||
.parent = &wm831x_io_parent,
|
||||
.start = WM831X_DC1_CONTROL_1,
|
||||
.end = WM831X_DC1_DVS_CONTROL,
|
||||
.flags = IORESOURCE_IO,
|
||||
@ -637,6 +644,7 @@ static struct resource wm831x_dcdc1_resources[] = {
|
||||
|
||||
static struct resource wm831x_dcdc2_resources[] = {
|
||||
{
|
||||
.parent = &wm831x_io_parent,
|
||||
.start = WM831X_DC2_CONTROL_1,
|
||||
.end = WM831X_DC2_DVS_CONTROL,
|
||||
.flags = IORESOURCE_IO,
|
||||
@ -657,6 +665,7 @@ static struct resource wm831x_dcdc2_resources[] = {
|
||||
|
||||
static struct resource wm831x_dcdc3_resources[] = {
|
||||
{
|
||||
.parent = &wm831x_io_parent,
|
||||
.start = WM831X_DC3_CONTROL_1,
|
||||
.end = WM831X_DC3_SLEEP_CONTROL,
|
||||
.flags = IORESOURCE_IO,
|
||||
@ -671,6 +680,7 @@ static struct resource wm831x_dcdc3_resources[] = {
|
||||
|
||||
static struct resource wm831x_dcdc4_resources[] = {
|
||||
{
|
||||
.parent = &wm831x_io_parent,
|
||||
.start = WM831X_DC4_CONTROL,
|
||||
.end = WM831X_DC4_SLEEP_CONTROL,
|
||||
.flags = IORESOURCE_IO,
|
||||
@ -685,6 +695,7 @@ static struct resource wm831x_dcdc4_resources[] = {
|
||||
|
||||
static struct resource wm8320_dcdc4_buck_resources[] = {
|
||||
{
|
||||
.parent = &wm831x_io_parent,
|
||||
.start = WM831X_DC4_CONTROL,
|
||||
.end = WM832X_DC4_SLEEP_CONTROL,
|
||||
.flags = IORESOURCE_IO,
|
||||
@ -707,6 +718,7 @@ static struct resource wm831x_gpio_resources[] = {
|
||||
|
||||
static struct resource wm831x_isink1_resources[] = {
|
||||
{
|
||||
.parent = &wm831x_io_parent,
|
||||
.start = WM831X_CURRENT_SINK_1,
|
||||
.end = WM831X_CURRENT_SINK_1,
|
||||
.flags = IORESOURCE_IO,
|
||||
@ -720,6 +732,7 @@ static struct resource wm831x_isink1_resources[] = {
|
||||
|
||||
static struct resource wm831x_isink2_resources[] = {
|
||||
{
|
||||
.parent = &wm831x_io_parent,
|
||||
.start = WM831X_CURRENT_SINK_2,
|
||||
.end = WM831X_CURRENT_SINK_2,
|
||||
.flags = IORESOURCE_IO,
|
||||
@ -733,6 +746,7 @@ static struct resource wm831x_isink2_resources[] = {
|
||||
|
||||
static struct resource wm831x_ldo1_resources[] = {
|
||||
{
|
||||
.parent = &wm831x_io_parent,
|
||||
.start = WM831X_LDO1_CONTROL,
|
||||
.end = WM831X_LDO1_SLEEP_CONTROL,
|
||||
.flags = IORESOURCE_IO,
|
||||
@ -747,6 +761,7 @@ static struct resource wm831x_ldo1_resources[] = {
|
||||
|
||||
static struct resource wm831x_ldo2_resources[] = {
|
||||
{
|
||||
.parent = &wm831x_io_parent,
|
||||
.start = WM831X_LDO2_CONTROL,
|
||||
.end = WM831X_LDO2_SLEEP_CONTROL,
|
||||
.flags = IORESOURCE_IO,
|
||||
@ -761,6 +776,7 @@ static struct resource wm831x_ldo2_resources[] = {
|
||||
|
||||
static struct resource wm831x_ldo3_resources[] = {
|
||||
{
|
||||
.parent = &wm831x_io_parent,
|
||||
.start = WM831X_LDO3_CONTROL,
|
||||
.end = WM831X_LDO3_SLEEP_CONTROL,
|
||||
.flags = IORESOURCE_IO,
|
||||
@ -775,6 +791,7 @@ static struct resource wm831x_ldo3_resources[] = {
|
||||
|
||||
static struct resource wm831x_ldo4_resources[] = {
|
||||
{
|
||||
.parent = &wm831x_io_parent,
|
||||
.start = WM831X_LDO4_CONTROL,
|
||||
.end = WM831X_LDO4_SLEEP_CONTROL,
|
||||
.flags = IORESOURCE_IO,
|
||||
@ -789,6 +806,7 @@ static struct resource wm831x_ldo4_resources[] = {
|
||||
|
||||
static struct resource wm831x_ldo5_resources[] = {
|
||||
{
|
||||
.parent = &wm831x_io_parent,
|
||||
.start = WM831X_LDO5_CONTROL,
|
||||
.end = WM831X_LDO5_SLEEP_CONTROL,
|
||||
.flags = IORESOURCE_IO,
|
||||
@ -803,6 +821,7 @@ static struct resource wm831x_ldo5_resources[] = {
|
||||
|
||||
static struct resource wm831x_ldo6_resources[] = {
|
||||
{
|
||||
.parent = &wm831x_io_parent,
|
||||
.start = WM831X_LDO6_CONTROL,
|
||||
.end = WM831X_LDO6_SLEEP_CONTROL,
|
||||
.flags = IORESOURCE_IO,
|
||||
@ -817,6 +836,7 @@ static struct resource wm831x_ldo6_resources[] = {
|
||||
|
||||
static struct resource wm831x_ldo7_resources[] = {
|
||||
{
|
||||
.parent = &wm831x_io_parent,
|
||||
.start = WM831X_LDO7_CONTROL,
|
||||
.end = WM831X_LDO7_SLEEP_CONTROL,
|
||||
.flags = IORESOURCE_IO,
|
||||
@ -831,6 +851,7 @@ static struct resource wm831x_ldo7_resources[] = {
|
||||
|
||||
static struct resource wm831x_ldo8_resources[] = {
|
||||
{
|
||||
.parent = &wm831x_io_parent,
|
||||
.start = WM831X_LDO8_CONTROL,
|
||||
.end = WM831X_LDO8_SLEEP_CONTROL,
|
||||
.flags = IORESOURCE_IO,
|
||||
@ -845,6 +866,7 @@ static struct resource wm831x_ldo8_resources[] = {
|
||||
|
||||
static struct resource wm831x_ldo9_resources[] = {
|
||||
{
|
||||
.parent = &wm831x_io_parent,
|
||||
.start = WM831X_LDO9_CONTROL,
|
||||
.end = WM831X_LDO9_SLEEP_CONTROL,
|
||||
.flags = IORESOURCE_IO,
|
||||
@ -859,6 +881,7 @@ static struct resource wm831x_ldo9_resources[] = {
|
||||
|
||||
static struct resource wm831x_ldo10_resources[] = {
|
||||
{
|
||||
.parent = &wm831x_io_parent,
|
||||
.start = WM831X_LDO10_CONTROL,
|
||||
.end = WM831X_LDO10_SLEEP_CONTROL,
|
||||
.flags = IORESOURCE_IO,
|
||||
@ -873,6 +896,7 @@ static struct resource wm831x_ldo10_resources[] = {
|
||||
|
||||
static struct resource wm831x_ldo11_resources[] = {
|
||||
{
|
||||
.parent = &wm831x_io_parent,
|
||||
.start = WM831X_LDO11_ON_CONTROL,
|
||||
.end = WM831X_LDO11_SLEEP_CONTROL,
|
||||
.flags = IORESOURCE_IO,
|
||||
@ -974,6 +998,7 @@ static struct resource wm831x_rtc_resources[] = {
|
||||
|
||||
static struct resource wm831x_status1_resources[] = {
|
||||
{
|
||||
.parent = &wm831x_io_parent,
|
||||
.start = WM831X_STATUS_LED_1,
|
||||
.end = WM831X_STATUS_LED_1,
|
||||
.flags = IORESOURCE_IO,
|
||||
@ -982,6 +1007,7 @@ static struct resource wm831x_status1_resources[] = {
|
||||
|
||||
static struct resource wm831x_status2_resources[] = {
|
||||
{
|
||||
.parent = &wm831x_io_parent,
|
||||
.start = WM831X_STATUS_LED_2,
|
||||
.end = WM831X_STATUS_LED_2,
|
||||
.flags = IORESOURCE_IO,
|
||||
@ -1787,27 +1813,27 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||
case WM8310:
|
||||
ret = mfd_add_devices(wm831x->dev, wm831x_num,
|
||||
wm8310_devs, ARRAY_SIZE(wm8310_devs),
|
||||
NULL, wm831x->irq_base);
|
||||
NULL, 0);
|
||||
break;
|
||||
|
||||
case WM8311:
|
||||
ret = mfd_add_devices(wm831x->dev, wm831x_num,
|
||||
wm8311_devs, ARRAY_SIZE(wm8311_devs),
|
||||
NULL, wm831x->irq_base);
|
||||
NULL, 0);
|
||||
if (!pdata || !pdata->disable_touch)
|
||||
mfd_add_devices(wm831x->dev, wm831x_num,
|
||||
touch_devs, ARRAY_SIZE(touch_devs),
|
||||
NULL, wm831x->irq_base);
|
||||
NULL, 0);
|
||||
break;
|
||||
|
||||
case WM8312:
|
||||
ret = mfd_add_devices(wm831x->dev, wm831x_num,
|
||||
wm8312_devs, ARRAY_SIZE(wm8312_devs),
|
||||
NULL, wm831x->irq_base);
|
||||
NULL, 0);
|
||||
if (!pdata || !pdata->disable_touch)
|
||||
mfd_add_devices(wm831x->dev, wm831x_num,
|
||||
touch_devs, ARRAY_SIZE(touch_devs),
|
||||
NULL, wm831x->irq_base);
|
||||
NULL, 0);
|
||||
break;
|
||||
|
||||
case WM8320:
|
||||
@ -1816,7 +1842,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||
case WM8326:
|
||||
ret = mfd_add_devices(wm831x->dev, wm831x_num,
|
||||
wm8320_devs, ARRAY_SIZE(wm8320_devs),
|
||||
NULL, wm831x->irq_base);
|
||||
NULL, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -1841,7 +1867,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||
if (ret & WM831X_XTAL_ENA) {
|
||||
ret = mfd_add_devices(wm831x->dev, wm831x_num,
|
||||
rtc_devs, ARRAY_SIZE(rtc_devs),
|
||||
NULL, wm831x->irq_base);
|
||||
NULL, 0);
|
||||
if (ret != 0) {
|
||||
dev_err(wm831x->dev, "Failed to add RTC: %d\n", ret);
|
||||
goto err_irq;
|
||||
@ -1854,7 +1880,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
|
||||
/* Treat errors as non-critical */
|
||||
ret = mfd_add_devices(wm831x->dev, wm831x_num, backlight_devs,
|
||||
ARRAY_SIZE(backlight_devs), NULL,
|
||||
wm831x->irq_base);
|
||||
0);
|
||||
if (ret < 0)
|
||||
dev_err(wm831x->dev, "Failed to add backlight: %d\n",
|
||||
ret);
|
||||
@ -1883,8 +1909,7 @@ void wm831x_device_exit(struct wm831x *wm831x)
|
||||
{
|
||||
wm831x_otp_exit(wm831x);
|
||||
mfd_remove_devices(wm831x->dev);
|
||||
if (wm831x->irq_base)
|
||||
free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x);
|
||||
free_irq(wm831x_irq(wm831x, WM831X_IRQ_AUXADC_DATA), wm831x);
|
||||
wm831x_irq_exit(wm831x);
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/irq.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqdomain.h>
|
||||
|
||||
#include <linux/mfd/wm831x/core.h>
|
||||
#include <linux/mfd/wm831x/pdata.h>
|
||||
@ -328,7 +329,7 @@ static inline int irq_data_to_status_reg(struct wm831x_irq_data *irq_data)
|
||||
static inline struct wm831x_irq_data *irq_to_wm831x_irq(struct wm831x *wm831x,
|
||||
int irq)
|
||||
{
|
||||
return &wm831x_irqs[irq - wm831x->irq_base];
|
||||
return &wm831x_irqs[irq];
|
||||
}
|
||||
|
||||
static void wm831x_irq_lock(struct irq_data *data)
|
||||
@ -374,7 +375,7 @@ static void wm831x_irq_enable(struct irq_data *data)
|
||||
{
|
||||
struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
|
||||
struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x,
|
||||
data->irq);
|
||||
data->hwirq);
|
||||
|
||||
wm831x->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
|
||||
}
|
||||
@ -383,7 +384,7 @@ static void wm831x_irq_disable(struct irq_data *data)
|
||||
{
|
||||
struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
|
||||
struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x,
|
||||
data->irq);
|
||||
data->hwirq);
|
||||
|
||||
wm831x->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
|
||||
}
|
||||
@ -393,7 +394,7 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type)
|
||||
struct wm831x *wm831x = irq_data_get_irq_chip_data(data);
|
||||
int irq;
|
||||
|
||||
irq = data->irq - wm831x->irq_base;
|
||||
irq = data->hwirq;
|
||||
|
||||
if (irq < WM831X_IRQ_GPIO_1 || irq > WM831X_IRQ_GPIO_11) {
|
||||
/* Ignore internal-only IRQs */
|
||||
@ -412,22 +413,25 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type)
|
||||
* do the update here as we can be called with the bus lock
|
||||
* held.
|
||||
*/
|
||||
wm831x->gpio_level_low[irq] = false;
|
||||
wm831x->gpio_level_high[irq] = false;
|
||||
switch (type) {
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_INT_MODE;
|
||||
wm831x->gpio_level[irq] = false;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL;
|
||||
wm831x->gpio_level[irq] = false;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
wm831x->gpio_update[irq] = 0x10000;
|
||||
wm831x->gpio_level[irq] = false;
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL;
|
||||
wm831x->gpio_level[irq] = true;
|
||||
wm831x->gpio_level_high[irq] = true;
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
wm831x->gpio_update[irq] = 0x10000;
|
||||
wm831x->gpio_level_low[irq] = true;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -469,9 +473,11 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
|
||||
* descriptors.
|
||||
*/
|
||||
if (primary & WM831X_TCHPD_INT)
|
||||
handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHPD);
|
||||
handle_nested_irq(irq_find_mapping(wm831x->irq_domain,
|
||||
WM831X_IRQ_TCHPD));
|
||||
if (primary & WM831X_TCHDATA_INT)
|
||||
handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHDATA);
|
||||
handle_nested_irq(irq_find_mapping(wm831x->irq_domain,
|
||||
WM831X_IRQ_TCHDATA));
|
||||
primary &= ~(WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(wm831x_irqs); i++) {
|
||||
@ -507,16 +513,29 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
|
||||
}
|
||||
|
||||
if (*status & wm831x_irqs[i].mask)
|
||||
handle_nested_irq(wm831x->irq_base + i);
|
||||
handle_nested_irq(irq_find_mapping(wm831x->irq_domain,
|
||||
i));
|
||||
|
||||
/* Simulate an edge triggered IRQ by polling the input
|
||||
* status. This is sucky but improves interoperability.
|
||||
*/
|
||||
if (primary == WM831X_GP_INT &&
|
||||
wm831x->gpio_level[i - WM831X_IRQ_GPIO_1]) {
|
||||
wm831x->gpio_level_high[i - WM831X_IRQ_GPIO_1]) {
|
||||
ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL);
|
||||
while (ret & 1 << (i - WM831X_IRQ_GPIO_1)) {
|
||||
handle_nested_irq(wm831x->irq_base + i);
|
||||
handle_nested_irq(irq_find_mapping(wm831x->irq_domain,
|
||||
i));
|
||||
ret = wm831x_reg_read(wm831x,
|
||||
WM831X_GPIO_LEVEL);
|
||||
}
|
||||
}
|
||||
|
||||
if (primary == WM831X_GP_INT &&
|
||||
wm831x->gpio_level_low[i - WM831X_IRQ_GPIO_1]) {
|
||||
ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL);
|
||||
while (!(ret & 1 << (i - WM831X_IRQ_GPIO_1))) {
|
||||
handle_nested_irq(irq_find_mapping(wm831x->irq_domain,
|
||||
i));
|
||||
ret = wm831x_reg_read(wm831x,
|
||||
WM831X_GPIO_LEVEL);
|
||||
}
|
||||
@ -527,10 +546,34 @@ out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int wm831x_irq_map(struct irq_domain *h, unsigned int virq,
|
||||
irq_hw_number_t hw)
|
||||
{
|
||||
irq_set_chip_data(virq, h->host_data);
|
||||
irq_set_chip_and_handler(virq, &wm831x_irq_chip, handle_edge_irq);
|
||||
irq_set_nested_thread(virq, 1);
|
||||
|
||||
/* ARM needs us to explicitly flag the IRQ as valid
|
||||
* and will set them noprobe when we do so. */
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(virq, IRQF_VALID);
|
||||
#else
|
||||
irq_set_noprobe(virq);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops wm831x_irq_domain_ops = {
|
||||
.map = wm831x_irq_map,
|
||||
.xlate = irq_domain_xlate_twocell,
|
||||
};
|
||||
|
||||
int wm831x_irq_init(struct wm831x *wm831x, int irq)
|
||||
{
|
||||
struct wm831x_pdata *pdata = wm831x->dev->platform_data;
|
||||
int i, cur_irq, ret;
|
||||
struct irq_domain *domain;
|
||||
int i, ret, irq_base;
|
||||
|
||||
mutex_init(&wm831x->irq_lock);
|
||||
|
||||
@ -543,18 +586,33 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
|
||||
}
|
||||
|
||||
/* Try to dynamically allocate IRQs if no base is specified */
|
||||
if (!pdata || !pdata->irq_base)
|
||||
wm831x->irq_base = -1;
|
||||
else
|
||||
wm831x->irq_base = pdata->irq_base;
|
||||
|
||||
wm831x->irq_base = irq_alloc_descs(wm831x->irq_base, 0,
|
||||
if (pdata && pdata->irq_base) {
|
||||
irq_base = irq_alloc_descs(pdata->irq_base, 0,
|
||||
WM831X_NUM_IRQS, 0);
|
||||
if (wm831x->irq_base < 0) {
|
||||
dev_warn(wm831x->dev, "Failed to allocate IRQs: %d\n",
|
||||
wm831x->irq_base);
|
||||
wm831x->irq_base = 0;
|
||||
return 0;
|
||||
if (irq_base < 0) {
|
||||
dev_warn(wm831x->dev, "Failed to allocate IRQs: %d\n",
|
||||
irq_base);
|
||||
irq_base = 0;
|
||||
}
|
||||
} else {
|
||||
irq_base = 0;
|
||||
}
|
||||
|
||||
if (irq_base)
|
||||
domain = irq_domain_add_legacy(wm831x->dev->of_node,
|
||||
ARRAY_SIZE(wm831x_irqs),
|
||||
irq_base, 0,
|
||||
&wm831x_irq_domain_ops,
|
||||
wm831x);
|
||||
else
|
||||
domain = irq_domain_add_linear(wm831x->dev->of_node,
|
||||
ARRAY_SIZE(wm831x_irqs),
|
||||
&wm831x_irq_domain_ops,
|
||||
wm831x);
|
||||
|
||||
if (!domain) {
|
||||
dev_warn(wm831x->dev, "Failed to allocate IRQ domain\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pdata && pdata->irq_cmos)
|
||||
@ -565,38 +623,22 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq)
|
||||
wm831x_set_bits(wm831x, WM831X_IRQ_CONFIG,
|
||||
WM831X_IRQ_OD, i);
|
||||
|
||||
/* Try to flag /IRQ as a wake source; there are a number of
|
||||
* unconditional wake sources in the PMIC so this isn't
|
||||
* conditional but we don't actually care *too* much if it
|
||||
* fails.
|
||||
*/
|
||||
ret = enable_irq_wake(irq);
|
||||
if (ret != 0) {
|
||||
dev_warn(wm831x->dev, "Can't enable IRQ as wake source: %d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
wm831x->irq = irq;
|
||||
|
||||
/* Register them with genirq */
|
||||
for (cur_irq = wm831x->irq_base;
|
||||
cur_irq < ARRAY_SIZE(wm831x_irqs) + wm831x->irq_base;
|
||||
cur_irq++) {
|
||||
irq_set_chip_data(cur_irq, wm831x);
|
||||
irq_set_chip_and_handler(cur_irq, &wm831x_irq_chip,
|
||||
handle_edge_irq);
|
||||
irq_set_nested_thread(cur_irq, 1);
|
||||
|
||||
/* ARM needs us to explicitly flag the IRQ as valid
|
||||
* and will set them noprobe when we do so. */
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(cur_irq, IRQF_VALID);
|
||||
#else
|
||||
irq_set_noprobe(cur_irq);
|
||||
#endif
|
||||
}
|
||||
wm831x->irq_domain = domain;
|
||||
|
||||
if (irq) {
|
||||
/* Try to flag /IRQ as a wake source; there are a number of
|
||||
* unconditional wake sources in the PMIC so this isn't
|
||||
* conditional but we don't actually care *too* much if it
|
||||
* fails.
|
||||
*/
|
||||
ret = enable_irq_wake(irq);
|
||||
if (ret != 0) {
|
||||
dev_warn(wm831x->dev,
|
||||
"Can't enable IRQ as wake source: %d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
ret = request_threaded_irq(irq, NULL, wm831x_irq_thread,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
"wm831x", wm831x);
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <linux/mfd/wm8350/core.h>
|
||||
@ -74,7 +75,7 @@ static int wm8350_phys_read(struct wm8350 *wm8350, u8 reg, int num_regs,
|
||||
int bytes = num_regs * 2;
|
||||
|
||||
dev_dbg(wm8350->dev, "volatile read\n");
|
||||
ret = wm8350->read_dev(wm8350, reg, bytes, (char *)dest);
|
||||
ret = regmap_raw_read(wm8350->regmap, reg, dest, bytes);
|
||||
|
||||
for (i = reg; i < reg + num_regs; i++) {
|
||||
/* Cache is CPU endian */
|
||||
@ -96,9 +97,6 @@ static int wm8350_read(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *dest)
|
||||
int ret = 0;
|
||||
int bytes = num_regs * 2;
|
||||
|
||||
if (wm8350->read_dev == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) {
|
||||
dev_err(wm8350->dev, "invalid reg %x\n",
|
||||
reg + num_regs - 1);
|
||||
@ -149,9 +147,6 @@ static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src)
|
||||
int end = reg + num_regs;
|
||||
int bytes = num_regs * 2;
|
||||
|
||||
if (wm8350->write_dev == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) {
|
||||
dev_err(wm8350->dev, "invalid reg %x\n",
|
||||
reg + num_regs - 1);
|
||||
@ -182,7 +177,7 @@ static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src)
|
||||
}
|
||||
|
||||
/* Actually write it out */
|
||||
return wm8350->write_dev(wm8350, reg, bytes, (char *)src);
|
||||
return regmap_raw_write(wm8350->regmap, reg, src, bytes);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -515,9 +510,8 @@ static int wm8350_create_cache(struct wm8350 *wm8350, int type, int mode)
|
||||
* a PMIC so the device many not be in a virgin state and we
|
||||
* can't rely on the silicon values.
|
||||
*/
|
||||
ret = wm8350->read_dev(wm8350, 0,
|
||||
sizeof(u16) * (WM8350_MAX_REGISTER + 1),
|
||||
wm8350->reg_cache);
|
||||
ret = regmap_raw_read(wm8350->regmap, 0, wm8350->reg_cache,
|
||||
sizeof(u16) * (WM8350_MAX_REGISTER + 1));
|
||||
if (ret < 0) {
|
||||
dev_err(wm8350->dev,
|
||||
"failed to read initial cache values\n");
|
||||
@ -570,35 +564,30 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
|
||||
struct wm8350_platform_data *pdata)
|
||||
{
|
||||
int ret;
|
||||
u16 id1, id2, mask_rev;
|
||||
u16 cust_id, mode, chip_rev;
|
||||
unsigned int id1, id2, mask_rev;
|
||||
unsigned int cust_id, mode, chip_rev;
|
||||
|
||||
dev_set_drvdata(wm8350->dev, wm8350);
|
||||
|
||||
/* get WM8350 revision and config mode */
|
||||
ret = wm8350->read_dev(wm8350, WM8350_RESET_ID, sizeof(id1), &id1);
|
||||
ret = regmap_read(wm8350->regmap, WM8350_RESET_ID, &id1);
|
||||
if (ret != 0) {
|
||||
dev_err(wm8350->dev, "Failed to read ID: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = wm8350->read_dev(wm8350, WM8350_ID, sizeof(id2), &id2);
|
||||
ret = regmap_read(wm8350->regmap, WM8350_ID, &id2);
|
||||
if (ret != 0) {
|
||||
dev_err(wm8350->dev, "Failed to read ID: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = wm8350->read_dev(wm8350, WM8350_REVISION, sizeof(mask_rev),
|
||||
&mask_rev);
|
||||
ret = regmap_read(wm8350->regmap, WM8350_REVISION, &mask_rev);
|
||||
if (ret != 0) {
|
||||
dev_err(wm8350->dev, "Failed to read revision: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
id1 = be16_to_cpu(id1);
|
||||
id2 = be16_to_cpu(id2);
|
||||
mask_rev = be16_to_cpu(mask_rev);
|
||||
|
||||
if (id1 != 0x6143) {
|
||||
dev_err(wm8350->dev,
|
||||
"Device with ID %x is not a WM8350\n", id1);
|
||||
|
@ -15,47 +15,18 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/wm8350/core.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
static int wm8350_i2c_read_device(struct wm8350 *wm8350, char reg,
|
||||
int bytes, void *dest)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_master_send(wm8350->i2c_client, ®, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = i2c_master_recv(wm8350->i2c_client, dest, bytes);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != bytes)
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8350_i2c_write_device(struct wm8350 *wm8350, char reg,
|
||||
int bytes, void *src)
|
||||
{
|
||||
/* we add 1 byte for device register */
|
||||
u8 msg[(WM8350_MAX_REGISTER << 1) + 1];
|
||||
int ret;
|
||||
|
||||
if (bytes > ((WM8350_MAX_REGISTER << 1) + 1))
|
||||
return -EINVAL;
|
||||
|
||||
msg[0] = reg;
|
||||
memcpy(&msg[1], src, bytes);
|
||||
ret = i2c_master_send(wm8350->i2c_client, msg, bytes + 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != bytes + 1)
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
static const struct regmap_config wm8350_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
};
|
||||
|
||||
static int wm8350_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
@ -67,20 +38,18 @@ static int wm8350_i2c_probe(struct i2c_client *i2c,
|
||||
if (wm8350 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
wm8350->regmap = devm_regmap_init_i2c(i2c, &wm8350_regmap);
|
||||
if (IS_ERR(wm8350->regmap)) {
|
||||
ret = PTR_ERR(wm8350->regmap);
|
||||
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(i2c, wm8350);
|
||||
wm8350->dev = &i2c->dev;
|
||||
wm8350->i2c_client = i2c;
|
||||
wm8350->read_dev = wm8350_i2c_read_device;
|
||||
wm8350->write_dev = wm8350_i2c_write_device;
|
||||
|
||||
ret = wm8350_device_init(wm8350, i2c->irq, i2c->dev.platform_data);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
return ret;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
return wm8350_device_init(wm8350, i2c->irq, i2c->dev.platform_data);
|
||||
}
|
||||
|
||||
static int wm8350_i2c_remove(struct i2c_client *i2c)
|
||||
|
@ -23,136 +23,16 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
static struct {
|
||||
u16 readable; /* Mask of readable bits */
|
||||
u16 writable; /* Mask of writable bits */
|
||||
u16 vol; /* Mask of volatile bits */
|
||||
int is_codec; /* Register controlled by codec reset */
|
||||
u16 default_val; /* Value on reset */
|
||||
} reg_data[] = {
|
||||
{ 0xFFFF, 0xFFFF, 0x0000, 0, 0x6172 }, /* R0 */
|
||||
{ 0x7000, 0x0000, 0x8000, 0, 0x0000 }, /* R1 */
|
||||
{ 0xFF17, 0xFF17, 0x0000, 0, 0x0000 }, /* R2 */
|
||||
{ 0xEBF3, 0xEBF3, 0x0000, 1, 0x6000 }, /* R3 */
|
||||
{ 0x3CF3, 0x3CF3, 0x0000, 1, 0x0000 }, /* R4 */
|
||||
{ 0xF1F8, 0xF1F8, 0x0000, 1, 0x4050 }, /* R5 */
|
||||
{ 0xFC1F, 0xFC1F, 0x0000, 1, 0x4000 }, /* R6 */
|
||||
{ 0xDFDE, 0xDFDE, 0x0000, 1, 0x01C8 }, /* R7 */
|
||||
{ 0xFCFC, 0xFCFC, 0x0000, 1, 0x0000 }, /* R8 */
|
||||
{ 0xEFFF, 0xEFFF, 0x0000, 1, 0x0040 }, /* R9 */
|
||||
{ 0xEFFF, 0xEFFF, 0x0000, 1, 0x0040 }, /* R10 */
|
||||
{ 0x27F7, 0x27F7, 0x0000, 1, 0x0004 }, /* R11 */
|
||||
{ 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R12 */
|
||||
{ 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R13 */
|
||||
{ 0x1FEF, 0x1FEF, 0x0000, 1, 0x0000 }, /* R14 */
|
||||
{ 0x0163, 0x0163, 0x0000, 1, 0x0100 }, /* R15 */
|
||||
{ 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R16 */
|
||||
{ 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R17 */
|
||||
{ 0x1FFF, 0x0FFF, 0x0000, 1, 0x0000 }, /* R18 */
|
||||
{ 0xFFFF, 0xFFFF, 0x0000, 1, 0x1000 }, /* R19 */
|
||||
{ 0xFFFF, 0xFFFF, 0x0000, 1, 0x1010 }, /* R20 */
|
||||
{ 0xFFFF, 0xFFFF, 0x0000, 1, 0x1010 }, /* R21 */
|
||||
{ 0x0FDD, 0x0FDD, 0x0000, 1, 0x8000 }, /* R22 */
|
||||
{ 0x1FFF, 0x1FFF, 0x0000, 1, 0x0800 }, /* R23 */
|
||||
{ 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R24 */
|
||||
{ 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R25 */
|
||||
{ 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R26 */
|
||||
{ 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R27 */
|
||||
{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R28 */
|
||||
{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R29 */
|
||||
{ 0x0000, 0x0077, 0x0000, 1, 0x0066 }, /* R30 */
|
||||
{ 0x0000, 0x0033, 0x0000, 1, 0x0022 }, /* R31 */
|
||||
{ 0x0000, 0x01FF, 0x0000, 1, 0x0079 }, /* R32 */
|
||||
{ 0x0000, 0x01FF, 0x0000, 1, 0x0079 }, /* R33 */
|
||||
{ 0x0000, 0x0003, 0x0000, 1, 0x0003 }, /* R34 */
|
||||
{ 0x0000, 0x01FF, 0x0000, 1, 0x0003 }, /* R35 */
|
||||
{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R36 */
|
||||
{ 0x0000, 0x003F, 0x0000, 1, 0x0100 }, /* R37 */
|
||||
{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R38 */
|
||||
{ 0x0000, 0x000F, 0x0000, 0, 0x0000 }, /* R39 */
|
||||
{ 0x0000, 0x00FF, 0x0000, 1, 0x0000 }, /* R40 */
|
||||
{ 0x0000, 0x01B7, 0x0000, 1, 0x0000 }, /* R41 */
|
||||
{ 0x0000, 0x01B7, 0x0000, 1, 0x0000 }, /* R42 */
|
||||
{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R43 */
|
||||
{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R44 */
|
||||
{ 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R45 */
|
||||
{ 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R46 */
|
||||
{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R47 */
|
||||
{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R48 */
|
||||
{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R49 */
|
||||
{ 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R50 */
|
||||
{ 0x0000, 0x01B3, 0x0000, 1, 0x0180 }, /* R51 */
|
||||
{ 0x0000, 0x0077, 0x0000, 1, 0x0000 }, /* R52 */
|
||||
{ 0x0000, 0x0077, 0x0000, 1, 0x0000 }, /* R53 */
|
||||
{ 0x0000, 0x00FF, 0x0000, 1, 0x0000 }, /* R54 */
|
||||
{ 0x0000, 0x0001, 0x0000, 1, 0x0000 }, /* R55 */
|
||||
{ 0x0000, 0x003F, 0x0000, 1, 0x0000 }, /* R56 */
|
||||
{ 0x0000, 0x004F, 0x0000, 1, 0x0000 }, /* R57 */
|
||||
{ 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R58 */
|
||||
{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R59 */
|
||||
{ 0x1FFF, 0x1FFF, 0x0000, 1, 0x0000 }, /* R60 */
|
||||
{ 0xFFFF, 0xFFFF, 0x0000, 1, 0x0000 }, /* R61 */
|
||||
{ 0x03FF, 0x03FF, 0x0000, 1, 0x0000 }, /* R62 */
|
||||
{ 0x007F, 0x007F, 0x0000, 1, 0x0000 }, /* R63 */
|
||||
{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R64 */
|
||||
{ 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R65 */
|
||||
{ 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R66 */
|
||||
{ 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R67 */
|
||||
{ 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R68 */
|
||||
{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R69 */
|
||||
{ 0xFFFF, 0xFFFF, 0x0000, 0, 0x4400 }, /* R70 */
|
||||
{ 0x23FF, 0x23FF, 0x0000, 0, 0x0000 }, /* R71 */
|
||||
{ 0xFFFF, 0xFFFF, 0x0000, 0, 0x4400 }, /* R72 */
|
||||
{ 0x23FF, 0x23FF, 0x0000, 0, 0x0000 }, /* R73 */
|
||||
{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R74 */
|
||||
{ 0x000E, 0x000E, 0x0000, 0, 0x0008 }, /* R75 */
|
||||
{ 0xE00F, 0xE00F, 0x0000, 0, 0x0000 }, /* R76 */
|
||||
{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R77 */
|
||||
{ 0x03C0, 0x03C0, 0x0000, 0, 0x02C0 }, /* R78 */
|
||||
{ 0xFFFF, 0x0000, 0xffff, 0, 0x0000 }, /* R79 */
|
||||
{ 0xFFFF, 0xFFFF, 0x0000, 0, 0x0000 }, /* R80 */
|
||||
{ 0xFFFF, 0x0000, 0xffff, 0, 0x0000 }, /* R81 */
|
||||
{ 0x2BFF, 0x0000, 0xffff, 0, 0x0000 }, /* R82 */
|
||||
{ 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R83 */
|
||||
{ 0x80FF, 0x80FF, 0x0000, 0, 0x00ff }, /* R84 */
|
||||
};
|
||||
|
||||
static int wm8400_read(struct wm8400 *wm8400, u8 reg, int num_regs, u16 *dest)
|
||||
static bool wm8400_volatile(struct device *dev, unsigned int reg)
|
||||
{
|
||||
int i, ret = 0;
|
||||
|
||||
BUG_ON(reg + num_regs > ARRAY_SIZE(wm8400->reg_cache));
|
||||
|
||||
/* If there are any volatile reads then read back the entire block */
|
||||
for (i = reg; i < reg + num_regs; i++)
|
||||
if (reg_data[i].vol) {
|
||||
ret = regmap_bulk_read(wm8400->regmap, reg, dest,
|
||||
num_regs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Otherwise use the cache */
|
||||
memcpy(dest, &wm8400->reg_cache[reg], num_regs * sizeof(u16));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs,
|
||||
u16 *src)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
BUG_ON(reg + num_regs > ARRAY_SIZE(wm8400->reg_cache));
|
||||
|
||||
for (i = 0; i < num_regs; i++) {
|
||||
BUG_ON(!reg_data[reg + i].writable);
|
||||
wm8400->reg_cache[reg + i] = src[i];
|
||||
ret = regmap_write(wm8400->regmap, reg, src[i]);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
switch (reg) {
|
||||
case WM8400_INTERRUPT_STATUS_1:
|
||||
case WM8400_INTERRUPT_LEVELS:
|
||||
case WM8400_SHUTDOWN_REASON:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -165,13 +45,12 @@ static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs,
|
||||
*/
|
||||
u16 wm8400_reg_read(struct wm8400 *wm8400, u8 reg)
|
||||
{
|
||||
u16 val;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&wm8400->io_lock);
|
||||
|
||||
wm8400_read(wm8400, reg, 1, &val);
|
||||
|
||||
mutex_unlock(&wm8400->io_lock);
|
||||
ret = regmap_read(wm8400->regmap, reg, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return val;
|
||||
}
|
||||
@ -179,63 +58,10 @@ EXPORT_SYMBOL_GPL(wm8400_reg_read);
|
||||
|
||||
int wm8400_block_read(struct wm8400 *wm8400, u8 reg, int count, u16 *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&wm8400->io_lock);
|
||||
|
||||
ret = wm8400_read(wm8400, reg, count, data);
|
||||
|
||||
mutex_unlock(&wm8400->io_lock);
|
||||
|
||||
return ret;
|
||||
return regmap_bulk_read(wm8400->regmap, reg, data, count);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8400_block_read);
|
||||
|
||||
/**
|
||||
* wm8400_set_bits - Bitmask write
|
||||
*
|
||||
* @wm8400: Pointer to wm8400 control structure
|
||||
* @reg: Register to access
|
||||
* @mask: Mask of bits to change
|
||||
* @val: Value to set for masked bits
|
||||
*/
|
||||
int wm8400_set_bits(struct wm8400 *wm8400, u8 reg, u16 mask, u16 val)
|
||||
{
|
||||
u16 tmp;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&wm8400->io_lock);
|
||||
|
||||
ret = wm8400_read(wm8400, reg, 1, &tmp);
|
||||
tmp = (tmp & ~mask) | val;
|
||||
if (ret == 0)
|
||||
ret = wm8400_write(wm8400, reg, 1, &tmp);
|
||||
|
||||
mutex_unlock(&wm8400->io_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8400_set_bits);
|
||||
|
||||
/**
|
||||
* wm8400_reset_codec_reg_cache - Reset cached codec registers to
|
||||
* their default values.
|
||||
*/
|
||||
void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400)
|
||||
{
|
||||
int i;
|
||||
|
||||
mutex_lock(&wm8400->io_lock);
|
||||
|
||||
/* Reset all codec registers to their initial value */
|
||||
for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++)
|
||||
if (reg_data[i].is_codec)
|
||||
wm8400->reg_cache[i] = reg_data[i].default_val;
|
||||
|
||||
mutex_unlock(&wm8400->io_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8400_reset_codec_reg_cache);
|
||||
|
||||
static int wm8400_register_codec(struct wm8400 *wm8400)
|
||||
{
|
||||
struct mfd_cell cell = {
|
||||
@ -257,44 +83,24 @@ static int wm8400_register_codec(struct wm8400 *wm8400)
|
||||
static int wm8400_init(struct wm8400 *wm8400,
|
||||
struct wm8400_platform_data *pdata)
|
||||
{
|
||||
u16 reg;
|
||||
int ret, i;
|
||||
|
||||
mutex_init(&wm8400->io_lock);
|
||||
unsigned int reg;
|
||||
int ret;
|
||||
|
||||
dev_set_drvdata(wm8400->dev, wm8400);
|
||||
|
||||
/* Check that this is actually a WM8400 */
|
||||
ret = regmap_read(wm8400->regmap, WM8400_RESET_ID, &i);
|
||||
ret = regmap_read(wm8400->regmap, WM8400_RESET_ID, ®);
|
||||
if (ret != 0) {
|
||||
dev_err(wm8400->dev, "Chip ID register read failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
if (i != reg_data[WM8400_RESET_ID].default_val) {
|
||||
dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n", i);
|
||||
if (reg != 0x6172) {
|
||||
dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n",
|
||||
reg);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* We don't know what state the hardware is in and since this
|
||||
* is a PMIC we can't reset it safely so initialise the register
|
||||
* cache from the hardware.
|
||||
*/
|
||||
ret = regmap_raw_read(wm8400->regmap, 0, wm8400->reg_cache,
|
||||
ARRAY_SIZE(wm8400->reg_cache));
|
||||
if (ret != 0) {
|
||||
dev_err(wm8400->dev, "Register cache read failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++)
|
||||
wm8400->reg_cache[i] = be16_to_cpu(wm8400->reg_cache[i]);
|
||||
|
||||
/* If the codec is in reset use hard coded values */
|
||||
if (!(wm8400->reg_cache[WM8400_POWER_MANAGEMENT_1] & WM8400_CODEC_ENA))
|
||||
for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++)
|
||||
if (reg_data[i].is_codec)
|
||||
wm8400->reg_cache[i] = reg_data[i].default_val;
|
||||
|
||||
ret = wm8400_read(wm8400, WM8400_ID, 1, ®);
|
||||
ret = regmap_read(wm8400->regmap, WM8400_ID, ®);
|
||||
if (ret != 0) {
|
||||
dev_err(wm8400->dev, "ID register read failed: %d\n", ret);
|
||||
return ret;
|
||||
@ -334,8 +140,22 @@ static const struct regmap_config wm8400_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
.max_register = WM8400_REGISTER_COUNT - 1,
|
||||
|
||||
.volatile_reg = wm8400_volatile,
|
||||
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
/**
|
||||
* wm8400_reset_codec_reg_cache - Reset cached codec registers to
|
||||
* their default values.
|
||||
*/
|
||||
void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400)
|
||||
{
|
||||
regmap_reinit_cache(wm8400->regmap, &wm8400_regmap_config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm8400_reset_codec_reg_cache);
|
||||
|
||||
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
|
||||
static int wm8400_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
|
@ -500,7 +500,8 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
|
||||
ret);
|
||||
goto err_enable;
|
||||
}
|
||||
wm8994->revision = ret;
|
||||
wm8994->revision = ret & WM8994_CHIP_REV_MASK;
|
||||
wm8994->cust_id = (ret & WM8994_CUST_ID_MASK) >> WM8994_CUST_ID_SHIFT;
|
||||
|
||||
switch (wm8994->type) {
|
||||
case WM8994:
|
||||
@ -553,8 +554,8 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
|
||||
break;
|
||||
}
|
||||
|
||||
dev_info(wm8994->dev, "%s revision %c\n", devname,
|
||||
'A' + wm8994->revision);
|
||||
dev_info(wm8994->dev, "%s revision %c CUST_ID %02x\n", devname,
|
||||
'A' + wm8994->revision, wm8994->cust_id);
|
||||
|
||||
switch (wm8994->type) {
|
||||
case WM1811:
|
||||
@ -732,23 +733,7 @@ static struct i2c_driver wm8994_i2c_driver = {
|
||||
.id_table = wm8994_i2c_id,
|
||||
};
|
||||
|
||||
static int __init wm8994_i2c_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_add_driver(&wm8994_i2c_driver);
|
||||
if (ret != 0)
|
||||
pr_err("Failed to register wm8994 I2C driver: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
module_init(wm8994_i2c_init);
|
||||
|
||||
static void __exit wm8994_i2c_exit(void)
|
||||
{
|
||||
i2c_del_driver(&wm8994_i2c_driver);
|
||||
}
|
||||
module_exit(wm8994_i2c_exit);
|
||||
module_i2c_driver(wm8994_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Core support for the WM8994 audio CODEC");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -1122,7 +1122,6 @@ static bool wm8994_volatile_register(struct device *dev, unsigned int reg)
|
||||
case WM8994_RATE_STATUS:
|
||||
case WM8958_MIC_DETECT_3:
|
||||
case WM8994_DC_SERVO_4E:
|
||||
case WM8994_CHIP_REVISION:
|
||||
case WM8994_INTERRUPT_STATUS_1:
|
||||
case WM8994_INTERRUPT_STATUS_2:
|
||||
return true;
|
||||
|
@ -142,10 +142,16 @@ static int __devexit ab8500_pwm_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ab8500_pwm_match[] = {
|
||||
{ .compatible = "stericsson,ab8500-pwm", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver ab8500_pwm_driver = {
|
||||
.driver = {
|
||||
.name = "ab8500-pwm",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = ab8500_pwm_match,
|
||||
},
|
||||
.probe = ab8500_pwm_probe,
|
||||
.remove = __devexit_p(ab8500_pwm_remove),
|
||||
|
@ -565,7 +565,7 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
|
||||
goto err_usb;
|
||||
}
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "SYSLO");
|
||||
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
|
||||
ret = request_threaded_irq(irq, NULL, wm831x_syslo_irq,
|
||||
IRQF_TRIGGER_RISING, "System power low",
|
||||
power);
|
||||
@ -575,7 +575,7 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
|
||||
goto err_battery;
|
||||
}
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "PWR SRC");
|
||||
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
|
||||
ret = request_threaded_irq(irq, NULL, wm831x_pwr_src_irq,
|
||||
IRQF_TRIGGER_RISING, "Power source",
|
||||
power);
|
||||
@ -586,7 +586,9 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
|
||||
irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
|
||||
irq = wm831x_irq(wm831x,
|
||||
platform_get_irq_byname(pdev,
|
||||
wm831x_bat_irqs[i]));
|
||||
ret = request_threaded_irq(irq, NULL, wm831x_bat_irq,
|
||||
IRQF_TRIGGER_RISING,
|
||||
wm831x_bat_irqs[i],
|
||||
@ -606,10 +608,10 @@ err_bat_irq:
|
||||
irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
|
||||
free_irq(irq, power);
|
||||
}
|
||||
irq = platform_get_irq_byname(pdev, "PWR SRC");
|
||||
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
|
||||
free_irq(irq, power);
|
||||
err_syslo:
|
||||
irq = platform_get_irq_byname(pdev, "SYSLO");
|
||||
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
|
||||
free_irq(irq, power);
|
||||
err_battery:
|
||||
if (power->have_battery)
|
||||
@ -626,17 +628,20 @@ err_kmalloc:
|
||||
static __devexit int wm831x_power_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct wm831x_power *wm831x_power = platform_get_drvdata(pdev);
|
||||
struct wm831x *wm831x = wm831x_power->wm831x;
|
||||
int irq, i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
|
||||
irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
|
||||
irq = wm831x_irq(wm831x,
|
||||
platform_get_irq_byname(pdev,
|
||||
wm831x_bat_irqs[i]));
|
||||
free_irq(irq, wm831x_power);
|
||||
}
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "PWR SRC");
|
||||
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "PWR SRC"));
|
||||
free_irq(irq, wm831x_power);
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "SYSLO");
|
||||
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "SYSLO"));
|
||||
free_irq(irq, wm831x_power);
|
||||
|
||||
if (wm831x_power->have_battery)
|
||||
|
@ -47,7 +47,7 @@ static int anatop_set_voltage(struct regulator_dev *reg, int min_uV,
|
||||
int max_uV, unsigned *selector)
|
||||
{
|
||||
struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg);
|
||||
u32 val, sel;
|
||||
u32 val, sel, mask;
|
||||
int uv;
|
||||
|
||||
uv = min_uV;
|
||||
@ -71,11 +71,10 @@ static int anatop_set_voltage(struct regulator_dev *reg, int min_uV,
|
||||
val = anatop_reg->min_bit_val + sel;
|
||||
*selector = sel;
|
||||
dev_dbg(®->dev, "%s: calculated val %d\n", __func__, val);
|
||||
anatop_set_bits(anatop_reg->mfd,
|
||||
anatop_reg->control_reg,
|
||||
anatop_reg->vol_bit_shift,
|
||||
anatop_reg->vol_bit_width,
|
||||
val);
|
||||
mask = ((1 << anatop_reg->vol_bit_width) - 1) <<
|
||||
anatop_reg->vol_bit_shift;
|
||||
val <<= anatop_reg->vol_bit_shift;
|
||||
anatop_write_reg(anatop_reg->mfd, anatop_reg->control_reg, val, mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -88,10 +87,9 @@ static int anatop_get_voltage_sel(struct regulator_dev *reg)
|
||||
if (!anatop_reg->control_reg)
|
||||
return -ENOTSUPP;
|
||||
|
||||
val = anatop_get_bits(anatop_reg->mfd,
|
||||
anatop_reg->control_reg,
|
||||
anatop_reg->vol_bit_shift,
|
||||
anatop_reg->vol_bit_width);
|
||||
val = anatop_read_reg(anatop_reg->mfd, anatop_reg->control_reg);
|
||||
val = (val & ((1 << anatop_reg->vol_bit_width) - 1)) >>
|
||||
anatop_reg->vol_bit_shift;
|
||||
|
||||
return val - anatop_reg->min_bit_val;
|
||||
}
|
||||
|
@ -331,21 +331,16 @@ struct tps65910_reg {
|
||||
|
||||
static inline int tps65910_read(struct tps65910_reg *pmic, u8 reg)
|
||||
{
|
||||
u8 val;
|
||||
unsigned int val;
|
||||
int err;
|
||||
|
||||
err = pmic->mfd->read(pmic->mfd, reg, 1, &val);
|
||||
err = tps65910_reg_read(pmic->mfd, reg, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline int tps65910_write(struct tps65910_reg *pmic, u8 reg, u8 val)
|
||||
{
|
||||
return pmic->mfd->write(pmic->mfd, reg, 1, &val);
|
||||
}
|
||||
|
||||
static int tps65910_modify_bits(struct tps65910_reg *pmic, u8 reg,
|
||||
u8 set_mask, u8 clear_mask)
|
||||
{
|
||||
@ -362,7 +357,7 @@ static int tps65910_modify_bits(struct tps65910_reg *pmic, u8 reg,
|
||||
|
||||
data &= ~clear_mask;
|
||||
data |= set_mask;
|
||||
err = tps65910_write(pmic, reg, data);
|
||||
err = tps65910_reg_write(pmic->mfd, reg, data);
|
||||
if (err)
|
||||
dev_err(pmic->mfd->dev, "Write for reg 0x%x failed\n", reg);
|
||||
|
||||
@ -371,7 +366,7 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tps65910_reg_read(struct tps65910_reg *pmic, u8 reg)
|
||||
static int tps65910_reg_read_locked(struct tps65910_reg *pmic, u8 reg)
|
||||
{
|
||||
int data;
|
||||
|
||||
@ -385,13 +380,13 @@ static int tps65910_reg_read(struct tps65910_reg *pmic, u8 reg)
|
||||
return data;
|
||||
}
|
||||
|
||||
static int tps65910_reg_write(struct tps65910_reg *pmic, u8 reg, u8 val)
|
||||
static int tps65910_reg_write_locked(struct tps65910_reg *pmic, u8 reg, u8 val)
|
||||
{
|
||||
int err;
|
||||
|
||||
mutex_lock(&pmic->mutex);
|
||||
|
||||
err = tps65910_write(pmic, reg, val);
|
||||
err = tps65910_reg_write(pmic->mfd, reg, val);
|
||||
if (err < 0)
|
||||
dev_err(pmic->mfd->dev, "Write for reg 0x%x failed\n", reg);
|
||||
|
||||
@ -490,9 +485,9 @@ static int tps65910_set_mode(struct regulator_dev *dev, unsigned int mode)
|
||||
LDO_ST_MODE_BIT);
|
||||
case REGULATOR_MODE_IDLE:
|
||||
value = LDO_ST_ON_BIT | LDO_ST_MODE_BIT;
|
||||
return tps65910_set_bits(mfd, reg, value);
|
||||
return tps65910_reg_set_bits(mfd, reg, value);
|
||||
case REGULATOR_MODE_STANDBY:
|
||||
return tps65910_clear_bits(mfd, reg, LDO_ST_ON_BIT);
|
||||
return tps65910_reg_clear_bits(mfd, reg, LDO_ST_ON_BIT);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
@ -507,7 +502,7 @@ static unsigned int tps65910_get_mode(struct regulator_dev *dev)
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
value = tps65910_reg_read(pmic, reg);
|
||||
value = tps65910_reg_read_locked(pmic, reg);
|
||||
if (value < 0)
|
||||
return value;
|
||||
|
||||
@ -527,28 +522,28 @@ static int tps65910_get_voltage_dcdc_sel(struct regulator_dev *dev)
|
||||
|
||||
switch (id) {
|
||||
case TPS65910_REG_VDD1:
|
||||
opvsel = tps65910_reg_read(pmic, TPS65910_VDD1_OP);
|
||||
mult = tps65910_reg_read(pmic, TPS65910_VDD1);
|
||||
opvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD1_OP);
|
||||
mult = tps65910_reg_read_locked(pmic, TPS65910_VDD1);
|
||||
mult = (mult & VDD1_VGAIN_SEL_MASK) >> VDD1_VGAIN_SEL_SHIFT;
|
||||
srvsel = tps65910_reg_read(pmic, TPS65910_VDD1_SR);
|
||||
srvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD1_SR);
|
||||
sr = opvsel & VDD1_OP_CMD_MASK;
|
||||
opvsel &= VDD1_OP_SEL_MASK;
|
||||
srvsel &= VDD1_SR_SEL_MASK;
|
||||
vselmax = 75;
|
||||
break;
|
||||
case TPS65910_REG_VDD2:
|
||||
opvsel = tps65910_reg_read(pmic, TPS65910_VDD2_OP);
|
||||
mult = tps65910_reg_read(pmic, TPS65910_VDD2);
|
||||
opvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD2_OP);
|
||||
mult = tps65910_reg_read_locked(pmic, TPS65910_VDD2);
|
||||
mult = (mult & VDD2_VGAIN_SEL_MASK) >> VDD2_VGAIN_SEL_SHIFT;
|
||||
srvsel = tps65910_reg_read(pmic, TPS65910_VDD2_SR);
|
||||
srvsel = tps65910_reg_read_locked(pmic, TPS65910_VDD2_SR);
|
||||
sr = opvsel & VDD2_OP_CMD_MASK;
|
||||
opvsel &= VDD2_OP_SEL_MASK;
|
||||
srvsel &= VDD2_SR_SEL_MASK;
|
||||
vselmax = 75;
|
||||
break;
|
||||
case TPS65911_REG_VDDCTRL:
|
||||
opvsel = tps65910_reg_read(pmic, TPS65911_VDDCTRL_OP);
|
||||
srvsel = tps65910_reg_read(pmic, TPS65911_VDDCTRL_SR);
|
||||
opvsel = tps65910_reg_read_locked(pmic, TPS65911_VDDCTRL_OP);
|
||||
srvsel = tps65910_reg_read_locked(pmic, TPS65911_VDDCTRL_SR);
|
||||
sr = opvsel & VDDCTRL_OP_CMD_MASK;
|
||||
opvsel &= VDDCTRL_OP_SEL_MASK;
|
||||
srvsel &= VDDCTRL_SR_SEL_MASK;
|
||||
@ -588,7 +583,7 @@ static int tps65910_get_voltage_sel(struct regulator_dev *dev)
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
value = tps65910_reg_read(pmic, reg);
|
||||
value = tps65910_reg_read_locked(pmic, reg);
|
||||
if (value < 0)
|
||||
return value;
|
||||
|
||||
@ -625,7 +620,7 @@ static int tps65911_get_voltage_sel(struct regulator_dev *dev)
|
||||
|
||||
reg = pmic->get_ctrl_reg(id);
|
||||
|
||||
value = tps65910_reg_read(pmic, reg);
|
||||
value = tps65910_reg_read_locked(pmic, reg);
|
||||
|
||||
switch (id) {
|
||||
case TPS65911_REG_LDO1:
|
||||
@ -670,7 +665,7 @@ static int tps65910_set_voltage_dcdc_sel(struct regulator_dev *dev,
|
||||
tps65910_modify_bits(pmic, TPS65910_VDD1,
|
||||
(dcdc_mult << VDD1_VGAIN_SEL_SHIFT),
|
||||
VDD1_VGAIN_SEL_MASK);
|
||||
tps65910_reg_write(pmic, TPS65910_VDD1_OP, vsel);
|
||||
tps65910_reg_write_locked(pmic, TPS65910_VDD1_OP, vsel);
|
||||
break;
|
||||
case TPS65910_REG_VDD2:
|
||||
dcdc_mult = (selector / VDD1_2_NUM_VOLT_FINE) + 1;
|
||||
@ -681,11 +676,11 @@ static int tps65910_set_voltage_dcdc_sel(struct regulator_dev *dev,
|
||||
tps65910_modify_bits(pmic, TPS65910_VDD2,
|
||||
(dcdc_mult << VDD2_VGAIN_SEL_SHIFT),
|
||||
VDD1_VGAIN_SEL_MASK);
|
||||
tps65910_reg_write(pmic, TPS65910_VDD2_OP, vsel);
|
||||
tps65910_reg_write_locked(pmic, TPS65910_VDD2_OP, vsel);
|
||||
break;
|
||||
case TPS65911_REG_VDDCTRL:
|
||||
vsel = selector + 3;
|
||||
tps65910_reg_write(pmic, TPS65911_VDDCTRL_OP, vsel);
|
||||
tps65910_reg_write_locked(pmic, TPS65911_VDDCTRL_OP, vsel);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -936,10 +931,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic,
|
||||
|
||||
/* External EN1 control */
|
||||
if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN1)
|
||||
ret = tps65910_set_bits(mfd,
|
||||
ret = tps65910_reg_set_bits(mfd,
|
||||
TPS65910_EN1_LDO_ASS + regoffs, bit_pos);
|
||||
else
|
||||
ret = tps65910_clear_bits(mfd,
|
||||
ret = tps65910_reg_clear_bits(mfd,
|
||||
TPS65910_EN1_LDO_ASS + regoffs, bit_pos);
|
||||
if (ret < 0) {
|
||||
dev_err(mfd->dev,
|
||||
@ -949,10 +944,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic,
|
||||
|
||||
/* External EN2 control */
|
||||
if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN2)
|
||||
ret = tps65910_set_bits(mfd,
|
||||
ret = tps65910_reg_set_bits(mfd,
|
||||
TPS65910_EN2_LDO_ASS + regoffs, bit_pos);
|
||||
else
|
||||
ret = tps65910_clear_bits(mfd,
|
||||
ret = tps65910_reg_clear_bits(mfd,
|
||||
TPS65910_EN2_LDO_ASS + regoffs, bit_pos);
|
||||
if (ret < 0) {
|
||||
dev_err(mfd->dev,
|
||||
@ -964,10 +959,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic,
|
||||
if ((tps65910_chip_id(mfd) == TPS65910) &&
|
||||
(id >= TPS65910_REG_VDIG1)) {
|
||||
if (ext_sleep_config & TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3)
|
||||
ret = tps65910_set_bits(mfd,
|
||||
ret = tps65910_reg_set_bits(mfd,
|
||||
TPS65910_EN3_LDO_ASS + regoffs, bit_pos);
|
||||
else
|
||||
ret = tps65910_clear_bits(mfd,
|
||||
ret = tps65910_reg_clear_bits(mfd,
|
||||
TPS65910_EN3_LDO_ASS + regoffs, bit_pos);
|
||||
if (ret < 0) {
|
||||
dev_err(mfd->dev,
|
||||
@ -979,10 +974,10 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic,
|
||||
/* Return if no external control is selected */
|
||||
if (!(ext_sleep_config & EXT_SLEEP_CONTROL)) {
|
||||
/* Clear all sleep controls */
|
||||
ret = tps65910_clear_bits(mfd,
|
||||
ret = tps65910_reg_clear_bits(mfd,
|
||||
TPS65910_SLEEP_KEEP_LDO_ON + regoffs, bit_pos);
|
||||
if (!ret)
|
||||
ret = tps65910_clear_bits(mfd,
|
||||
ret = tps65910_reg_clear_bits(mfd,
|
||||
TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos);
|
||||
if (ret < 0)
|
||||
dev_err(mfd->dev,
|
||||
@ -1001,32 +996,33 @@ static int tps65910_set_ext_sleep_config(struct tps65910_reg *pmic,
|
||||
(tps65910_chip_id(mfd) == TPS65911))) {
|
||||
int op_reg_add = pmic->get_ctrl_reg(id) + 1;
|
||||
int sr_reg_add = pmic->get_ctrl_reg(id) + 2;
|
||||
int opvsel = tps65910_reg_read(pmic, op_reg_add);
|
||||
int srvsel = tps65910_reg_read(pmic, sr_reg_add);
|
||||
int opvsel = tps65910_reg_read_locked(pmic, op_reg_add);
|
||||
int srvsel = tps65910_reg_read_locked(pmic, sr_reg_add);
|
||||
if (opvsel & VDD1_OP_CMD_MASK) {
|
||||
u8 reg_val = srvsel & VDD1_OP_SEL_MASK;
|
||||
ret = tps65910_reg_write(pmic, op_reg_add, reg_val);
|
||||
ret = tps65910_reg_write_locked(pmic, op_reg_add,
|
||||
reg_val);
|
||||
if (ret < 0) {
|
||||
dev_err(mfd->dev,
|
||||
"Error in configuring op register\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ret = tps65910_reg_write(pmic, sr_reg_add, 0);
|
||||
ret = tps65910_reg_write_locked(pmic, sr_reg_add, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(mfd->dev, "Error in settting sr register\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = tps65910_clear_bits(mfd,
|
||||
ret = tps65910_reg_clear_bits(mfd,
|
||||
TPS65910_SLEEP_KEEP_LDO_ON + regoffs, bit_pos);
|
||||
if (!ret) {
|
||||
if (ext_sleep_config & TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP)
|
||||
ret = tps65910_set_bits(mfd,
|
||||
ret = tps65910_reg_set_bits(mfd,
|
||||
TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos);
|
||||
else
|
||||
ret = tps65910_clear_bits(mfd,
|
||||
ret = tps65910_reg_clear_bits(mfd,
|
||||
TPS65910_SLEEP_SET_LDO_OFF + regoffs, bit_pos);
|
||||
}
|
||||
if (ret < 0)
|
||||
@ -1177,7 +1173,7 @@ static __devinit int tps65910_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, pmic);
|
||||
|
||||
/* Give control of all register to control port */
|
||||
tps65910_set_bits(pmic->mfd, TPS65910_DEVCTRL,
|
||||
tps65910_reg_set_bits(pmic->mfd, TPS65910_DEVCTRL,
|
||||
DEVCTRL_SR_CTL_I2C_SEL_MASK);
|
||||
|
||||
switch(tps65910_chip_id(tps65910)) {
|
||||
|
@ -535,7 +535,7 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
|
||||
goto err;
|
||||
}
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "UV");
|
||||
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
|
||||
ret = request_threaded_irq(irq, NULL, wm831x_dcdc_uv_irq,
|
||||
IRQF_TRIGGER_RISING, dcdc->name, dcdc);
|
||||
if (ret != 0) {
|
||||
@ -544,7 +544,7 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "HC");
|
||||
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC"));
|
||||
ret = request_threaded_irq(irq, NULL, wm831x_dcdc_oc_irq,
|
||||
IRQF_TRIGGER_RISING, dcdc->name, dcdc);
|
||||
if (ret != 0) {
|
||||
@ -558,7 +558,8 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
err_uv:
|
||||
free_irq(platform_get_irq_byname(pdev, "UV"), dcdc);
|
||||
free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")),
|
||||
dcdc);
|
||||
err_regulator:
|
||||
regulator_unregister(dcdc->regulator);
|
||||
err:
|
||||
@ -570,11 +571,14 @@ err:
|
||||
static __devexit int wm831x_buckv_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
|
||||
struct wm831x *wm831x = dcdc->wm831x;
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
free_irq(platform_get_irq_byname(pdev, "HC"), dcdc);
|
||||
free_irq(platform_get_irq_byname(pdev, "UV"), dcdc);
|
||||
free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC")),
|
||||
dcdc);
|
||||
free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")),
|
||||
dcdc);
|
||||
regulator_unregister(dcdc->regulator);
|
||||
if (dcdc->dvs_gpio)
|
||||
gpio_free(dcdc->dvs_gpio);
|
||||
@ -726,7 +730,7 @@ static __devinit int wm831x_buckp_probe(struct platform_device *pdev)
|
||||
goto err;
|
||||
}
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "UV");
|
||||
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
|
||||
ret = request_threaded_irq(irq, NULL, wm831x_dcdc_uv_irq,
|
||||
IRQF_TRIGGER_RISING, dcdc->name, dcdc);
|
||||
if (ret != 0) {
|
||||
@ -751,7 +755,8 @@ static __devexit int wm831x_buckp_remove(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
free_irq(platform_get_irq_byname(pdev, "UV"), dcdc);
|
||||
free_irq(wm831x_irq(dcdc->wm831x, platform_get_irq_byname(pdev, "UV")),
|
||||
dcdc);
|
||||
regulator_unregister(dcdc->regulator);
|
||||
|
||||
return 0;
|
||||
@ -859,7 +864,7 @@ static __devinit int wm831x_boostp_probe(struct platform_device *pdev)
|
||||
goto err;
|
||||
}
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "UV");
|
||||
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
|
||||
ret = request_threaded_irq(irq, NULL, wm831x_dcdc_uv_irq,
|
||||
IRQF_TRIGGER_RISING, dcdc->name,
|
||||
dcdc);
|
||||
@ -885,7 +890,8 @@ static __devexit int wm831x_boostp_remove(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
free_irq(platform_get_irq_byname(pdev, "UV"), dcdc);
|
||||
free_irq(wm831x_irq(dcdc->wm831x, platform_get_irq_byname(pdev, "UV")),
|
||||
dcdc);
|
||||
regulator_unregister(dcdc->regulator);
|
||||
|
||||
return 0;
|
||||
|
@ -202,7 +202,7 @@ static __devinit int wm831x_isink_probe(struct platform_device *pdev)
|
||||
goto err;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
irq = wm831x_irq(wm831x, platform_get_irq(pdev, 0));
|
||||
ret = request_threaded_irq(irq, NULL, wm831x_isink_irq,
|
||||
IRQF_TRIGGER_RISING, isink->name, isink);
|
||||
if (ret != 0) {
|
||||
@ -227,7 +227,7 @@ static __devexit int wm831x_isink_remove(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
free_irq(platform_get_irq(pdev, 0), isink);
|
||||
free_irq(wm831x_irq(isink->wm831x, platform_get_irq(pdev, 0)), isink);
|
||||
|
||||
regulator_unregister(isink->regulator);
|
||||
|
||||
|
@ -321,7 +321,7 @@ static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev)
|
||||
goto err;
|
||||
}
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "UV");
|
||||
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
|
||||
ret = request_threaded_irq(irq, NULL, wm831x_ldo_uv_irq,
|
||||
IRQF_TRIGGER_RISING, ldo->name,
|
||||
ldo);
|
||||
@ -347,7 +347,8 @@ static __devexit int wm831x_gp_ldo_remove(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
free_irq(platform_get_irq_byname(pdev, "UV"), ldo);
|
||||
free_irq(wm831x_irq(ldo->wm831x,
|
||||
platform_get_irq_byname(pdev, "UV")), ldo);
|
||||
regulator_unregister(ldo->regulator);
|
||||
|
||||
return 0;
|
||||
@ -582,7 +583,7 @@ static __devinit int wm831x_aldo_probe(struct platform_device *pdev)
|
||||
goto err;
|
||||
}
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "UV");
|
||||
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
|
||||
ret = request_threaded_irq(irq, NULL, wm831x_ldo_uv_irq,
|
||||
IRQF_TRIGGER_RISING, ldo->name, ldo);
|
||||
if (ret != 0) {
|
||||
@ -605,7 +606,8 @@ static __devexit int wm831x_aldo_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct wm831x_ldo *ldo = platform_get_drvdata(pdev);
|
||||
|
||||
free_irq(platform_get_irq_byname(pdev, "UV"), ldo);
|
||||
free_irq(wm831x_irq(ldo->wm831x, platform_get_irq_byname(pdev, "UV")),
|
||||
ldo);
|
||||
regulator_unregister(ldo->regulator);
|
||||
|
||||
return 0;
|
||||
|
@ -396,7 +396,7 @@ static int wm831x_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
|
||||
struct wm831x_rtc *wm831x_rtc;
|
||||
int alm_irq = platform_get_irq_byname(pdev, "ALM");
|
||||
int alm_irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "ALM"));
|
||||
int ret = 0;
|
||||
|
||||
wm831x_rtc = devm_kzalloc(&pdev->dev, sizeof(*wm831x_rtc), GFP_KERNEL);
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/falloc.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/mm.h>
|
||||
@ -363,11 +364,12 @@ static int ashmem_shrink(struct shrinker *s, struct shrink_control *sc)
|
||||
|
||||
mutex_lock(&ashmem_mutex);
|
||||
list_for_each_entry_safe(range, next, &ashmem_lru_list, lru) {
|
||||
struct inode *inode = range->asma->file->f_dentry->d_inode;
|
||||
loff_t start = range->pgstart * PAGE_SIZE;
|
||||
loff_t end = (range->pgend + 1) * PAGE_SIZE - 1;
|
||||
loff_t end = (range->pgend + 1) * PAGE_SIZE;
|
||||
|
||||
vmtruncate_range(inode, start, end);
|
||||
do_fallocate(range->asma->file,
|
||||
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
|
||||
start, end - start);
|
||||
range->purged = ASHMEM_WAS_PURGED;
|
||||
lru_del(range);
|
||||
|
||||
|
@ -633,7 +633,6 @@ static int ptmx_open(struct inode *inode, struct file *filp)
|
||||
mutex_unlock(&devpts_mutex);
|
||||
|
||||
mutex_lock(&tty_mutex);
|
||||
mutex_lock(&devpts_mutex);
|
||||
tty = tty_init_dev(ptm_driver, index);
|
||||
|
||||
if (IS_ERR(tty)) {
|
||||
@ -643,7 +642,6 @@ static int ptmx_open(struct inode *inode, struct file *filp)
|
||||
|
||||
/* The tty returned here is locked so we can safely
|
||||
drop the mutex */
|
||||
mutex_unlock(&devpts_mutex);
|
||||
mutex_unlock(&tty_mutex);
|
||||
|
||||
set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
|
||||
|
@ -894,6 +894,23 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
|
||||
tty_ldisc_enable(tty);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tty_ldisc_kill(struct tty_struct *tty)
|
||||
{
|
||||
mutex_lock(&tty->ldisc_mutex);
|
||||
/*
|
||||
* Now kill off the ldisc
|
||||
*/
|
||||
tty_ldisc_close(tty, tty->ldisc);
|
||||
tty_ldisc_put(tty->ldisc);
|
||||
/* Force an oops if we mess this up */
|
||||
tty->ldisc = NULL;
|
||||
|
||||
/* Ensure the next open requests the N_TTY ldisc */
|
||||
tty_set_termios_ldisc(tty, N_TTY);
|
||||
mutex_unlock(&tty->ldisc_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* tty_ldisc_release - release line discipline
|
||||
* @tty: tty being shut down
|
||||
@ -912,27 +929,19 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
|
||||
* race with the set_ldisc code path.
|
||||
*/
|
||||
|
||||
tty_unlock(tty);
|
||||
tty_unlock_pair(tty, o_tty);
|
||||
tty_ldisc_halt(tty);
|
||||
tty_ldisc_flush_works(tty);
|
||||
tty_lock(tty);
|
||||
if (o_tty) {
|
||||
tty_ldisc_halt(o_tty);
|
||||
tty_ldisc_flush_works(o_tty);
|
||||
}
|
||||
tty_lock_pair(tty, o_tty);
|
||||
|
||||
mutex_lock(&tty->ldisc_mutex);
|
||||
/*
|
||||
* Now kill off the ldisc
|
||||
*/
|
||||
tty_ldisc_close(tty, tty->ldisc);
|
||||
tty_ldisc_put(tty->ldisc);
|
||||
/* Force an oops if we mess this up */
|
||||
tty->ldisc = NULL;
|
||||
|
||||
/* Ensure the next open requests the N_TTY ldisc */
|
||||
tty_set_termios_ldisc(tty, N_TTY);
|
||||
mutex_unlock(&tty->ldisc_mutex);
|
||||
|
||||
/* This will need doing differently if we need to lock */
|
||||
tty_ldisc_kill(tty);
|
||||
if (o_tty)
|
||||
tty_ldisc_release(o_tty, NULL);
|
||||
tty_ldisc_kill(o_tty);
|
||||
|
||||
/* And the memory resources remaining (buffers, termios) will be
|
||||
disposed of when the kref hits zero */
|
||||
|
@ -565,6 +565,7 @@ config INTEL_SCU_WATCHDOG
|
||||
config ITCO_WDT
|
||||
tristate "Intel TCO Timer/Watchdog"
|
||||
depends on (X86 || IA64) && PCI
|
||||
select LPC_ICH
|
||||
---help---
|
||||
Hardware driver for the intel TCO timer based watchdog devices.
|
||||
These drivers are included in the Intel 82801 I/O Controller
|
||||
|
@ -1,8 +1,8 @@
|
||||
/* iTCO Vendor Specific Support hooks */
|
||||
#ifdef CONFIG_ITCO_VENDOR_SUPPORT
|
||||
extern void iTCO_vendor_pre_start(unsigned long, unsigned int);
|
||||
extern void iTCO_vendor_pre_stop(unsigned long);
|
||||
extern void iTCO_vendor_pre_keepalive(unsigned long, unsigned int);
|
||||
extern void iTCO_vendor_pre_start(struct resource *, unsigned int);
|
||||
extern void iTCO_vendor_pre_stop(struct resource *);
|
||||
extern void iTCO_vendor_pre_keepalive(struct resource *, unsigned int);
|
||||
extern void iTCO_vendor_pre_set_heartbeat(unsigned int);
|
||||
extern int iTCO_vendor_check_noreboot_on(void);
|
||||
#else
|
||||
|
@ -35,11 +35,6 @@
|
||||
|
||||
#include "iTCO_vendor.h"
|
||||
|
||||
/* iTCO defines */
|
||||
#define SMI_EN (acpibase + 0x30) /* SMI Control and Enable Register */
|
||||
#define TCOBASE (acpibase + 0x60) /* TCO base address */
|
||||
#define TCO1_STS (TCOBASE + 0x04) /* TCO1 Status Register */
|
||||
|
||||
/* List of vendor support modes */
|
||||
/* SuperMicro Pentium 3 Era 370SSE+-OEM1/P3TSSE */
|
||||
#define SUPERMICRO_OLD_BOARD 1
|
||||
@ -82,24 +77,24 @@ MODULE_PARM_DESC(vendorsupport, "iTCO vendor specific support mode, default="
|
||||
* 20.6 seconds.
|
||||
*/
|
||||
|
||||
static void supermicro_old_pre_start(unsigned long acpibase)
|
||||
static void supermicro_old_pre_start(struct resource *smires)
|
||||
{
|
||||
unsigned long val32;
|
||||
|
||||
/* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */
|
||||
val32 = inl(SMI_EN);
|
||||
val32 = inl(smires->start);
|
||||
val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */
|
||||
outl(val32, SMI_EN); /* Needed to activate watchdog */
|
||||
outl(val32, smires->start); /* Needed to activate watchdog */
|
||||
}
|
||||
|
||||
static void supermicro_old_pre_stop(unsigned long acpibase)
|
||||
static void supermicro_old_pre_stop(struct resource *smires)
|
||||
{
|
||||
unsigned long val32;
|
||||
|
||||
/* Bit 13: TCO_EN -> 1 = Enables the TCO logic to generate SMI# */
|
||||
val32 = inl(SMI_EN);
|
||||
val32 = inl(smires->start);
|
||||
val32 |= 0x00002000; /* Turn on SMI clearing watchdog */
|
||||
outl(val32, SMI_EN); /* Needed to deactivate watchdog */
|
||||
outl(val32, smires->start); /* Needed to deactivate watchdog */
|
||||
}
|
||||
|
||||
/*
|
||||
@ -270,66 +265,66 @@ static void supermicro_new_pre_set_heartbeat(unsigned int heartbeat)
|
||||
* Don't use this fix if you don't need to!!!
|
||||
*/
|
||||
|
||||
static void broken_bios_start(unsigned long acpibase)
|
||||
static void broken_bios_start(struct resource *smires)
|
||||
{
|
||||
unsigned long val32;
|
||||
|
||||
val32 = inl(SMI_EN);
|
||||
val32 = inl(smires->start);
|
||||
/* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI#
|
||||
Bit 0: GBL_SMI_EN -> 0 = No SMI# will be generated by ICH. */
|
||||
val32 &= 0xffffdffe;
|
||||
outl(val32, SMI_EN);
|
||||
outl(val32, smires->start);
|
||||
}
|
||||
|
||||
static void broken_bios_stop(unsigned long acpibase)
|
||||
static void broken_bios_stop(struct resource *smires)
|
||||
{
|
||||
unsigned long val32;
|
||||
|
||||
val32 = inl(SMI_EN);
|
||||
val32 = inl(smires->start);
|
||||
/* Bit 13: TCO_EN -> 1 = Enables TCO logic generating an SMI#
|
||||
Bit 0: GBL_SMI_EN -> 1 = Turn global SMI on again. */
|
||||
val32 |= 0x00002001;
|
||||
outl(val32, SMI_EN);
|
||||
outl(val32, smires->start);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic Support Functions
|
||||
*/
|
||||
|
||||
void iTCO_vendor_pre_start(unsigned long acpibase,
|
||||
void iTCO_vendor_pre_start(struct resource *smires,
|
||||
unsigned int heartbeat)
|
||||
{
|
||||
switch (vendorsupport) {
|
||||
case SUPERMICRO_OLD_BOARD:
|
||||
supermicro_old_pre_start(acpibase);
|
||||
supermicro_old_pre_start(smires);
|
||||
break;
|
||||
case SUPERMICRO_NEW_BOARD:
|
||||
supermicro_new_pre_start(heartbeat);
|
||||
break;
|
||||
case BROKEN_BIOS:
|
||||
broken_bios_start(acpibase);
|
||||
broken_bios_start(smires);
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(iTCO_vendor_pre_start);
|
||||
|
||||
void iTCO_vendor_pre_stop(unsigned long acpibase)
|
||||
void iTCO_vendor_pre_stop(struct resource *smires)
|
||||
{
|
||||
switch (vendorsupport) {
|
||||
case SUPERMICRO_OLD_BOARD:
|
||||
supermicro_old_pre_stop(acpibase);
|
||||
supermicro_old_pre_stop(smires);
|
||||
break;
|
||||
case SUPERMICRO_NEW_BOARD:
|
||||
supermicro_new_pre_stop();
|
||||
break;
|
||||
case BROKEN_BIOS:
|
||||
broken_bios_stop(acpibase);
|
||||
broken_bios_stop(smires);
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(iTCO_vendor_pre_stop);
|
||||
|
||||
void iTCO_vendor_pre_keepalive(unsigned long acpibase, unsigned int heartbeat)
|
||||
void iTCO_vendor_pre_keepalive(struct resource *smires, unsigned int heartbeat)
|
||||
{
|
||||
if (vendorsupport == SUPERMICRO_NEW_BOARD)
|
||||
supermicro_new_pre_set_heartbeat(heartbeat);
|
||||
|
@ -66,316 +66,16 @@
|
||||
#include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
|
||||
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
|
||||
#include <linux/io.h> /* For inb/outb/... */
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/lpc_ich.h>
|
||||
|
||||
#include "iTCO_vendor.h"
|
||||
|
||||
/* TCO related info */
|
||||
enum iTCO_chipsets {
|
||||
TCO_ICH = 0, /* ICH */
|
||||
TCO_ICH0, /* ICH0 */
|
||||
TCO_ICH2, /* ICH2 */
|
||||
TCO_ICH2M, /* ICH2-M */
|
||||
TCO_ICH3, /* ICH3-S */
|
||||
TCO_ICH3M, /* ICH3-M */
|
||||
TCO_ICH4, /* ICH4 */
|
||||
TCO_ICH4M, /* ICH4-M */
|
||||
TCO_CICH, /* C-ICH */
|
||||
TCO_ICH5, /* ICH5 & ICH5R */
|
||||
TCO_6300ESB, /* 6300ESB */
|
||||
TCO_ICH6, /* ICH6 & ICH6R */
|
||||
TCO_ICH6M, /* ICH6-M */
|
||||
TCO_ICH6W, /* ICH6W & ICH6RW */
|
||||
TCO_631XESB, /* 631xESB/632xESB */
|
||||
TCO_ICH7, /* ICH7 & ICH7R */
|
||||
TCO_ICH7DH, /* ICH7DH */
|
||||
TCO_ICH7M, /* ICH7-M & ICH7-U */
|
||||
TCO_ICH7MDH, /* ICH7-M DH */
|
||||
TCO_NM10, /* NM10 */
|
||||
TCO_ICH8, /* ICH8 & ICH8R */
|
||||
TCO_ICH8DH, /* ICH8DH */
|
||||
TCO_ICH8DO, /* ICH8DO */
|
||||
TCO_ICH8M, /* ICH8M */
|
||||
TCO_ICH8ME, /* ICH8M-E */
|
||||
TCO_ICH9, /* ICH9 */
|
||||
TCO_ICH9R, /* ICH9R */
|
||||
TCO_ICH9DH, /* ICH9DH */
|
||||
TCO_ICH9DO, /* ICH9DO */
|
||||
TCO_ICH9M, /* ICH9M */
|
||||
TCO_ICH9ME, /* ICH9M-E */
|
||||
TCO_ICH10, /* ICH10 */
|
||||
TCO_ICH10R, /* ICH10R */
|
||||
TCO_ICH10D, /* ICH10D */
|
||||
TCO_ICH10DO, /* ICH10DO */
|
||||
TCO_PCH, /* PCH Desktop Full Featured */
|
||||
TCO_PCHM, /* PCH Mobile Full Featured */
|
||||
TCO_P55, /* P55 */
|
||||
TCO_PM55, /* PM55 */
|
||||
TCO_H55, /* H55 */
|
||||
TCO_QM57, /* QM57 */
|
||||
TCO_H57, /* H57 */
|
||||
TCO_HM55, /* HM55 */
|
||||
TCO_Q57, /* Q57 */
|
||||
TCO_HM57, /* HM57 */
|
||||
TCO_PCHMSFF, /* PCH Mobile SFF Full Featured */
|
||||
TCO_QS57, /* QS57 */
|
||||
TCO_3400, /* 3400 */
|
||||
TCO_3420, /* 3420 */
|
||||
TCO_3450, /* 3450 */
|
||||
TCO_EP80579, /* EP80579 */
|
||||
TCO_CPT, /* Cougar Point */
|
||||
TCO_CPTD, /* Cougar Point Desktop */
|
||||
TCO_CPTM, /* Cougar Point Mobile */
|
||||
TCO_PBG, /* Patsburg */
|
||||
TCO_DH89XXCC, /* DH89xxCC */
|
||||
TCO_PPT, /* Panther Point */
|
||||
TCO_LPT, /* Lynx Point */
|
||||
};
|
||||
|
||||
static struct {
|
||||
char *name;
|
||||
unsigned int iTCO_version;
|
||||
} iTCO_chipset_info[] __devinitdata = {
|
||||
{"ICH", 1},
|
||||
{"ICH0", 1},
|
||||
{"ICH2", 1},
|
||||
{"ICH2-M", 1},
|
||||
{"ICH3-S", 1},
|
||||
{"ICH3-M", 1},
|
||||
{"ICH4", 1},
|
||||
{"ICH4-M", 1},
|
||||
{"C-ICH", 1},
|
||||
{"ICH5 or ICH5R", 1},
|
||||
{"6300ESB", 1},
|
||||
{"ICH6 or ICH6R", 2},
|
||||
{"ICH6-M", 2},
|
||||
{"ICH6W or ICH6RW", 2},
|
||||
{"631xESB/632xESB", 2},
|
||||
{"ICH7 or ICH7R", 2},
|
||||
{"ICH7DH", 2},
|
||||
{"ICH7-M or ICH7-U", 2},
|
||||
{"ICH7-M DH", 2},
|
||||
{"NM10", 2},
|
||||
{"ICH8 or ICH8R", 2},
|
||||
{"ICH8DH", 2},
|
||||
{"ICH8DO", 2},
|
||||
{"ICH8M", 2},
|
||||
{"ICH8M-E", 2},
|
||||
{"ICH9", 2},
|
||||
{"ICH9R", 2},
|
||||
{"ICH9DH", 2},
|
||||
{"ICH9DO", 2},
|
||||
{"ICH9M", 2},
|
||||
{"ICH9M-E", 2},
|
||||
{"ICH10", 2},
|
||||
{"ICH10R", 2},
|
||||
{"ICH10D", 2},
|
||||
{"ICH10DO", 2},
|
||||
{"PCH Desktop Full Featured", 2},
|
||||
{"PCH Mobile Full Featured", 2},
|
||||
{"P55", 2},
|
||||
{"PM55", 2},
|
||||
{"H55", 2},
|
||||
{"QM57", 2},
|
||||
{"H57", 2},
|
||||
{"HM55", 2},
|
||||
{"Q57", 2},
|
||||
{"HM57", 2},
|
||||
{"PCH Mobile SFF Full Featured", 2},
|
||||
{"QS57", 2},
|
||||
{"3400", 2},
|
||||
{"3420", 2},
|
||||
{"3450", 2},
|
||||
{"EP80579", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point Desktop", 2},
|
||||
{"Cougar Point Mobile", 2},
|
||||
{"Patsburg", 2},
|
||||
{"DH89xxCC", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Lynx Point", 2},
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
/*
|
||||
* This data only exists for exporting the supported PCI ids
|
||||
* via MODULE_DEVICE_TABLE. We do not actually register a
|
||||
* pci_driver, because the I/O Controller Hub has also other
|
||||
* functions that probably will be registered by other drivers.
|
||||
*/
|
||||
static DEFINE_PCI_DEVICE_TABLE(iTCO_wdt_pci_tbl) = {
|
||||
{ PCI_VDEVICE(INTEL, 0x2410), TCO_ICH},
|
||||
{ PCI_VDEVICE(INTEL, 0x2420), TCO_ICH0},
|
||||
{ PCI_VDEVICE(INTEL, 0x2440), TCO_ICH2},
|
||||
{ PCI_VDEVICE(INTEL, 0x244c), TCO_ICH2M},
|
||||
{ PCI_VDEVICE(INTEL, 0x2480), TCO_ICH3},
|
||||
{ PCI_VDEVICE(INTEL, 0x248c), TCO_ICH3M},
|
||||
{ PCI_VDEVICE(INTEL, 0x24c0), TCO_ICH4},
|
||||
{ PCI_VDEVICE(INTEL, 0x24cc), TCO_ICH4M},
|
||||
{ PCI_VDEVICE(INTEL, 0x2450), TCO_CICH},
|
||||
{ PCI_VDEVICE(INTEL, 0x24d0), TCO_ICH5},
|
||||
{ PCI_VDEVICE(INTEL, 0x25a1), TCO_6300ESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2640), TCO_ICH6},
|
||||
{ PCI_VDEVICE(INTEL, 0x2641), TCO_ICH6M},
|
||||
{ PCI_VDEVICE(INTEL, 0x2642), TCO_ICH6W},
|
||||
{ PCI_VDEVICE(INTEL, 0x2670), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2671), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2672), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2673), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2674), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2675), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2676), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2677), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2678), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2679), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x267a), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x267b), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x267c), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x267d), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x267e), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x267f), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x27b8), TCO_ICH7},
|
||||
{ PCI_VDEVICE(INTEL, 0x27b0), TCO_ICH7DH},
|
||||
{ PCI_VDEVICE(INTEL, 0x27b9), TCO_ICH7M},
|
||||
{ PCI_VDEVICE(INTEL, 0x27bd), TCO_ICH7MDH},
|
||||
{ PCI_VDEVICE(INTEL, 0x27bc), TCO_NM10},
|
||||
{ PCI_VDEVICE(INTEL, 0x2810), TCO_ICH8},
|
||||
{ PCI_VDEVICE(INTEL, 0x2812), TCO_ICH8DH},
|
||||
{ PCI_VDEVICE(INTEL, 0x2814), TCO_ICH8DO},
|
||||
{ PCI_VDEVICE(INTEL, 0x2815), TCO_ICH8M},
|
||||
{ PCI_VDEVICE(INTEL, 0x2811), TCO_ICH8ME},
|
||||
{ PCI_VDEVICE(INTEL, 0x2918), TCO_ICH9},
|
||||
{ PCI_VDEVICE(INTEL, 0x2916), TCO_ICH9R},
|
||||
{ PCI_VDEVICE(INTEL, 0x2912), TCO_ICH9DH},
|
||||
{ PCI_VDEVICE(INTEL, 0x2914), TCO_ICH9DO},
|
||||
{ PCI_VDEVICE(INTEL, 0x2919), TCO_ICH9M},
|
||||
{ PCI_VDEVICE(INTEL, 0x2917), TCO_ICH9ME},
|
||||
{ PCI_VDEVICE(INTEL, 0x3a18), TCO_ICH10},
|
||||
{ PCI_VDEVICE(INTEL, 0x3a16), TCO_ICH10R},
|
||||
{ PCI_VDEVICE(INTEL, 0x3a1a), TCO_ICH10D},
|
||||
{ PCI_VDEVICE(INTEL, 0x3a14), TCO_ICH10DO},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b00), TCO_PCH},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b01), TCO_PCHM},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b02), TCO_P55},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b03), TCO_PM55},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b06), TCO_H55},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b07), TCO_QM57},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b08), TCO_H57},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b09), TCO_HM55},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b0a), TCO_Q57},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b0b), TCO_HM57},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b0d), TCO_PCHMSFF},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b0f), TCO_QS57},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b12), TCO_3400},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b14), TCO_3420},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b16), TCO_3450},
|
||||
{ PCI_VDEVICE(INTEL, 0x5031), TCO_EP80579},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c41), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c42), TCO_CPTD},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c43), TCO_CPTM},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c44), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c45), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c46), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c47), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c48), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c49), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c4a), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c4b), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c4c), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c4d), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c4e), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c4f), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c50), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c51), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c52), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c53), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c54), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c55), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c56), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c57), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c58), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c59), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c5a), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c5b), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c5c), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c5d), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c5e), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c5f), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1d40), TCO_PBG},
|
||||
{ PCI_VDEVICE(INTEL, 0x1d41), TCO_PBG},
|
||||
{ PCI_VDEVICE(INTEL, 0x2310), TCO_DH89XXCC},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e40), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e41), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e42), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e43), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e44), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e45), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e46), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e47), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e48), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e49), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e4a), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e4b), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e4c), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e4d), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e4e), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e4f), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e50), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e51), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e52), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e53), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e54), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e55), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e56), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e57), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e58), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e59), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e5a), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e5b), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e5c), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e5d), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e5e), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e5f), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c40), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c41), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c42), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c43), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c44), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c45), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c46), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c47), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c48), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c49), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c4a), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c4b), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c4c), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c4d), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c4e), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c4f), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c50), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c51), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c52), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c53), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c54), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c55), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c56), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c57), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c58), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c59), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c5a), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c5b), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c5c), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c5d), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c5e), TCO_LPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x8c5f), TCO_LPT},
|
||||
{ 0, }, /* End of list */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl);
|
||||
|
||||
/* Address definitions for the TCO */
|
||||
/* TCO base address */
|
||||
#define TCOBASE (iTCO_wdt_private.ACPIBASE + 0x60)
|
||||
#define TCOBASE (iTCO_wdt_private.tco_res->start)
|
||||
/* SMI Control and Enable Register */
|
||||
#define SMI_EN (iTCO_wdt_private.ACPIBASE + 0x30)
|
||||
#define SMI_EN (iTCO_wdt_private.smi_res->start)
|
||||
|
||||
#define TCO_RLD (TCOBASE + 0x00) /* TCO Timer Reload and Curr. Value */
|
||||
#define TCOv1_TMR (TCOBASE + 0x01) /* TCOv1 Timer Initial Value */
|
||||
@ -393,19 +93,18 @@ static char expect_release;
|
||||
static struct { /* this is private data for the iTCO_wdt device */
|
||||
/* TCO version/generation */
|
||||
unsigned int iTCO_version;
|
||||
/* The device's ACPIBASE address (TCOBASE = ACPIBASE+0x60) */
|
||||
unsigned long ACPIBASE;
|
||||
struct resource *tco_res;
|
||||
struct resource *smi_res;
|
||||
struct resource *gcs_res;
|
||||
/* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2)*/
|
||||
unsigned long __iomem *gcs;
|
||||
/* the lock for io operations */
|
||||
spinlock_t io_lock;
|
||||
struct platform_device *dev;
|
||||
/* the PCI-device */
|
||||
struct pci_dev *pdev;
|
||||
} iTCO_wdt_private;
|
||||
|
||||
/* the watchdog platform device */
|
||||
static struct platform_device *iTCO_wdt_platform_device;
|
||||
|
||||
/* module parameters */
|
||||
#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */
|
||||
static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */
|
||||
@ -485,7 +184,7 @@ static int iTCO_wdt_start(void)
|
||||
|
||||
spin_lock(&iTCO_wdt_private.io_lock);
|
||||
|
||||
iTCO_vendor_pre_start(iTCO_wdt_private.ACPIBASE, heartbeat);
|
||||
iTCO_vendor_pre_start(iTCO_wdt_private.smi_res, heartbeat);
|
||||
|
||||
/* disable chipset's NO_REBOOT bit */
|
||||
if (iTCO_wdt_unset_NO_REBOOT_bit()) {
|
||||
@ -519,7 +218,7 @@ static int iTCO_wdt_stop(void)
|
||||
|
||||
spin_lock(&iTCO_wdt_private.io_lock);
|
||||
|
||||
iTCO_vendor_pre_stop(iTCO_wdt_private.ACPIBASE);
|
||||
iTCO_vendor_pre_stop(iTCO_wdt_private.smi_res);
|
||||
|
||||
/* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */
|
||||
val = inw(TCO1_CNT);
|
||||
@ -541,7 +240,7 @@ static int iTCO_wdt_keepalive(void)
|
||||
{
|
||||
spin_lock(&iTCO_wdt_private.io_lock);
|
||||
|
||||
iTCO_vendor_pre_keepalive(iTCO_wdt_private.ACPIBASE, heartbeat);
|
||||
iTCO_vendor_pre_keepalive(iTCO_wdt_private.smi_res, heartbeat);
|
||||
|
||||
/* Reload the timer by writing to the TCO Timer Counter register */
|
||||
if (iTCO_wdt_private.iTCO_version == 2)
|
||||
@ -786,83 +485,120 @@ static struct miscdevice iTCO_wdt_miscdev = {
|
||||
* Init & exit routines
|
||||
*/
|
||||
|
||||
static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent, struct platform_device *dev)
|
||||
static void __devexit iTCO_wdt_cleanup(void)
|
||||
{
|
||||
int ret;
|
||||
u32 base_address;
|
||||
unsigned long RCBA;
|
||||
/* Stop the timer before we leave */
|
||||
if (!nowayout)
|
||||
iTCO_wdt_stop();
|
||||
|
||||
/* Deregister */
|
||||
misc_deregister(&iTCO_wdt_miscdev);
|
||||
|
||||
/* release resources */
|
||||
release_region(iTCO_wdt_private.tco_res->start,
|
||||
resource_size(iTCO_wdt_private.tco_res));
|
||||
release_region(iTCO_wdt_private.smi_res->start,
|
||||
resource_size(iTCO_wdt_private.smi_res));
|
||||
if (iTCO_wdt_private.iTCO_version == 2) {
|
||||
iounmap(iTCO_wdt_private.gcs);
|
||||
release_mem_region(iTCO_wdt_private.gcs_res->start,
|
||||
resource_size(iTCO_wdt_private.gcs_res));
|
||||
}
|
||||
|
||||
iTCO_wdt_private.tco_res = NULL;
|
||||
iTCO_wdt_private.smi_res = NULL;
|
||||
iTCO_wdt_private.gcs_res = NULL;
|
||||
iTCO_wdt_private.gcs = NULL;
|
||||
}
|
||||
|
||||
static int __devinit iTCO_wdt_probe(struct platform_device *dev)
|
||||
{
|
||||
int ret = -ENODEV;
|
||||
unsigned long val32;
|
||||
struct lpc_ich_info *ich_info = dev->dev.platform_data;
|
||||
|
||||
if (!ich_info)
|
||||
goto out;
|
||||
|
||||
spin_lock_init(&iTCO_wdt_private.io_lock);
|
||||
|
||||
iTCO_wdt_private.tco_res =
|
||||
platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_TCO);
|
||||
if (!iTCO_wdt_private.tco_res)
|
||||
goto out;
|
||||
|
||||
iTCO_wdt_private.smi_res =
|
||||
platform_get_resource(dev, IORESOURCE_IO, ICH_RES_IO_SMI);
|
||||
if (!iTCO_wdt_private.smi_res)
|
||||
goto out;
|
||||
|
||||
iTCO_wdt_private.iTCO_version = ich_info->iTCO_version;
|
||||
iTCO_wdt_private.dev = dev;
|
||||
iTCO_wdt_private.pdev = to_pci_dev(dev->dev.parent);
|
||||
|
||||
/*
|
||||
* Find the ACPI/PM base I/O address which is the base
|
||||
* for the TCO registers (TCOBASE=ACPIBASE + 0x60)
|
||||
* ACPIBASE is bits [15:7] from 0x40-0x43
|
||||
* Get the Memory-Mapped GCS register, we need it for the
|
||||
* NO_REBOOT flag (TCO v2).
|
||||
*/
|
||||
pci_read_config_dword(pdev, 0x40, &base_address);
|
||||
base_address &= 0x0000ff80;
|
||||
if (base_address == 0x00000000) {
|
||||
/* Something's wrong here, ACPIBASE has to be set */
|
||||
pr_err("failed to get TCOBASE address, device disabled by hardware/BIOS\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
iTCO_wdt_private.iTCO_version =
|
||||
iTCO_chipset_info[ent->driver_data].iTCO_version;
|
||||
iTCO_wdt_private.ACPIBASE = base_address;
|
||||
iTCO_wdt_private.pdev = pdev;
|
||||
|
||||
/* Get the Memory-Mapped GCS register, we need it for the
|
||||
NO_REBOOT flag (TCO v2). To get access to it you have to
|
||||
read RCBA from PCI Config space 0xf0 and use it as base.
|
||||
GCS = RCBA + ICH6_GCS(0x3410). */
|
||||
if (iTCO_wdt_private.iTCO_version == 2) {
|
||||
pci_read_config_dword(pdev, 0xf0, &base_address);
|
||||
if ((base_address & 1) == 0) {
|
||||
pr_err("RCBA is disabled by hardware/BIOS, device disabled\n");
|
||||
ret = -ENODEV;
|
||||
iTCO_wdt_private.gcs_res = platform_get_resource(dev,
|
||||
IORESOURCE_MEM,
|
||||
ICH_RES_MEM_GCS);
|
||||
|
||||
if (!iTCO_wdt_private.gcs_res)
|
||||
goto out;
|
||||
|
||||
if (!request_mem_region(iTCO_wdt_private.gcs_res->start,
|
||||
resource_size(iTCO_wdt_private.gcs_res), dev->name)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
RCBA = base_address & 0xffffc000;
|
||||
iTCO_wdt_private.gcs = ioremap((RCBA + 0x3410), 4);
|
||||
iTCO_wdt_private.gcs = ioremap(iTCO_wdt_private.gcs_res->start,
|
||||
resource_size(iTCO_wdt_private.gcs_res));
|
||||
if (!iTCO_wdt_private.gcs) {
|
||||
ret = -EIO;
|
||||
goto unreg_gcs;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check chipset's NO_REBOOT bit */
|
||||
if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) {
|
||||
pr_info("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n");
|
||||
ret = -ENODEV; /* Cannot reset NO_REBOOT bit */
|
||||
goto out_unmap;
|
||||
goto unmap_gcs;
|
||||
}
|
||||
|
||||
/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
|
||||
iTCO_wdt_set_NO_REBOOT_bit();
|
||||
|
||||
/* The TCO logic uses the TCO_EN bit in the SMI_EN register */
|
||||
if (!request_region(SMI_EN, 4, "iTCO_wdt")) {
|
||||
pr_err("I/O address 0x%04lx already in use, device disabled\n",
|
||||
if (!request_region(iTCO_wdt_private.smi_res->start,
|
||||
resource_size(iTCO_wdt_private.smi_res), dev->name)) {
|
||||
pr_err("I/O address 0x%04llx already in use, device disabled\n",
|
||||
SMI_EN);
|
||||
ret = -EIO;
|
||||
goto out_unmap;
|
||||
ret = -EBUSY;
|
||||
goto unmap_gcs;
|
||||
}
|
||||
if (turn_SMI_watchdog_clear_off >= iTCO_wdt_private.iTCO_version) {
|
||||
/* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */
|
||||
/*
|
||||
* Bit 13: TCO_EN -> 0
|
||||
* Disables TCO logic generating an SMI#
|
||||
*/
|
||||
val32 = inl(SMI_EN);
|
||||
val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */
|
||||
outl(val32, SMI_EN);
|
||||
}
|
||||
|
||||
/* The TCO I/O registers reside in a 32-byte range pointed to
|
||||
by the TCOBASE value */
|
||||
if (!request_region(TCOBASE, 0x20, "iTCO_wdt")) {
|
||||
pr_err("I/O address 0x%04lx already in use, device disabled\n",
|
||||
if (!request_region(iTCO_wdt_private.tco_res->start,
|
||||
resource_size(iTCO_wdt_private.tco_res), dev->name)) {
|
||||
pr_err("I/O address 0x%04llx already in use, device disabled\n",
|
||||
TCOBASE);
|
||||
ret = -EIO;
|
||||
goto unreg_smi_en;
|
||||
ret = -EBUSY;
|
||||
goto unreg_smi;
|
||||
}
|
||||
|
||||
pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04lx)\n",
|
||||
iTCO_chipset_info[ent->driver_data].name,
|
||||
iTCO_chipset_info[ent->driver_data].iTCO_version,
|
||||
TCOBASE);
|
||||
pr_info("Found a %s TCO device (Version=%d, TCOBASE=0x%04llx)\n",
|
||||
ich_info->name, ich_info->iTCO_version, TCOBASE);
|
||||
|
||||
/* Clear out the (probably old) status */
|
||||
outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */
|
||||
@ -883,7 +619,7 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
|
||||
if (ret != 0) {
|
||||
pr_err("cannot register miscdev on minor=%d (err=%d)\n",
|
||||
WATCHDOG_MINOR, ret);
|
||||
goto unreg_region;
|
||||
goto unreg_tco;
|
||||
}
|
||||
|
||||
pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
|
||||
@ -891,62 +627,31 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
|
||||
|
||||
return 0;
|
||||
|
||||
unreg_region:
|
||||
release_region(TCOBASE, 0x20);
|
||||
unreg_smi_en:
|
||||
release_region(SMI_EN, 4);
|
||||
out_unmap:
|
||||
unreg_tco:
|
||||
release_region(iTCO_wdt_private.tco_res->start,
|
||||
resource_size(iTCO_wdt_private.tco_res));
|
||||
unreg_smi:
|
||||
release_region(iTCO_wdt_private.smi_res->start,
|
||||
resource_size(iTCO_wdt_private.smi_res));
|
||||
unmap_gcs:
|
||||
if (iTCO_wdt_private.iTCO_version == 2)
|
||||
iounmap(iTCO_wdt_private.gcs);
|
||||
unreg_gcs:
|
||||
if (iTCO_wdt_private.iTCO_version == 2)
|
||||
release_mem_region(iTCO_wdt_private.gcs_res->start,
|
||||
resource_size(iTCO_wdt_private.gcs_res));
|
||||
out:
|
||||
iTCO_wdt_private.ACPIBASE = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __devexit iTCO_wdt_cleanup(void)
|
||||
{
|
||||
/* Stop the timer before we leave */
|
||||
if (!nowayout)
|
||||
iTCO_wdt_stop();
|
||||
|
||||
/* Deregister */
|
||||
misc_deregister(&iTCO_wdt_miscdev);
|
||||
release_region(TCOBASE, 0x20);
|
||||
release_region(SMI_EN, 4);
|
||||
if (iTCO_wdt_private.iTCO_version == 2)
|
||||
iounmap(iTCO_wdt_private.gcs);
|
||||
pci_dev_put(iTCO_wdt_private.pdev);
|
||||
iTCO_wdt_private.ACPIBASE = 0;
|
||||
}
|
||||
|
||||
static int __devinit iTCO_wdt_probe(struct platform_device *dev)
|
||||
{
|
||||
int ret = -ENODEV;
|
||||
int found = 0;
|
||||
struct pci_dev *pdev = NULL;
|
||||
const struct pci_device_id *ent;
|
||||
|
||||
spin_lock_init(&iTCO_wdt_private.io_lock);
|
||||
|
||||
for_each_pci_dev(pdev) {
|
||||
ent = pci_match_id(iTCO_wdt_pci_tbl, pdev);
|
||||
if (ent) {
|
||||
found++;
|
||||
ret = iTCO_wdt_init(pdev, ent, dev);
|
||||
if (!ret)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
pr_info("No device detected\n");
|
||||
iTCO_wdt_private.tco_res = NULL;
|
||||
iTCO_wdt_private.smi_res = NULL;
|
||||
iTCO_wdt_private.gcs_res = NULL;
|
||||
iTCO_wdt_private.gcs = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit iTCO_wdt_remove(struct platform_device *dev)
|
||||
{
|
||||
if (iTCO_wdt_private.ACPIBASE)
|
||||
if (iTCO_wdt_private.tco_res || iTCO_wdt_private.smi_res)
|
||||
iTCO_wdt_cleanup();
|
||||
|
||||
return 0;
|
||||
@ -977,23 +682,11 @@ static int __init iTCO_wdt_init_module(void)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
iTCO_wdt_platform_device = platform_device_register_simple(DRV_NAME,
|
||||
-1, NULL, 0);
|
||||
if (IS_ERR(iTCO_wdt_platform_device)) {
|
||||
err = PTR_ERR(iTCO_wdt_platform_device);
|
||||
goto unreg_platform_driver;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unreg_platform_driver:
|
||||
platform_driver_unregister(&iTCO_wdt_driver);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit iTCO_wdt_cleanup_module(void)
|
||||
{
|
||||
platform_device_unregister(iTCO_wdt_platform_device);
|
||||
platform_driver_unregister(&iTCO_wdt_driver);
|
||||
pr_info("Watchdog Module Unloaded\n");
|
||||
}
|
||||
|
@ -292,7 +292,6 @@ static const struct inode_operations bad_inode_ops =
|
||||
.getxattr = bad_inode_getxattr,
|
||||
.listxattr = bad_inode_listxattr,
|
||||
.removexattr = bad_inode_removexattr,
|
||||
/* truncate_range returns void */
|
||||
};
|
||||
|
||||
|
||||
|
@ -16,5 +16,5 @@
|
||||
libore-y := ore.o ore_raid.o
|
||||
obj-$(CONFIG_ORE) += libore.o
|
||||
|
||||
exofs-y := inode.o file.o symlink.o namei.o dir.o super.o
|
||||
exofs-y := inode.o file.o symlink.o namei.o dir.o super.o sys.o
|
||||
obj-$(CONFIG_EXOFS_FS) += exofs.o
|
||||
|
@ -56,6 +56,9 @@
|
||||
struct exofs_dev {
|
||||
struct ore_dev ored;
|
||||
unsigned did;
|
||||
unsigned urilen;
|
||||
uint8_t *uri;
|
||||
struct kobject ed_kobj;
|
||||
};
|
||||
/*
|
||||
* our extension to the in-memory superblock
|
||||
@ -73,6 +76,7 @@ struct exofs_sb_info {
|
||||
struct ore_layout layout; /* Default files layout */
|
||||
struct ore_comp one_comp; /* id & cred of partition id=0*/
|
||||
struct ore_components oc; /* comps for the partition */
|
||||
struct kobject s_kobj; /* holds per-sbi kobject */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -176,6 +180,16 @@ void exofs_make_credential(u8 cred_a[OSD_CAP_LEN],
|
||||
const struct osd_obj_id *obj);
|
||||
int exofs_sbi_write_stats(struct exofs_sb_info *sbi);
|
||||
|
||||
/* sys.c */
|
||||
int exofs_sysfs_init(void);
|
||||
void exofs_sysfs_uninit(void);
|
||||
int exofs_sysfs_sb_add(struct exofs_sb_info *sbi,
|
||||
struct exofs_dt_device_info *dt_dev);
|
||||
void exofs_sysfs_sb_del(struct exofs_sb_info *sbi);
|
||||
int exofs_sysfs_odev_add(struct exofs_dev *edev,
|
||||
struct exofs_sb_info *sbi);
|
||||
void exofs_sysfs_dbg_print(void);
|
||||
|
||||
/*********************
|
||||
* operation vectors *
|
||||
*********************/
|
||||
|
@ -472,6 +472,7 @@ static void exofs_put_super(struct super_block *sb)
|
||||
_exofs_print_device("Unmounting", NULL, ore_comp_dev(&sbi->oc, 0),
|
||||
sbi->one_comp.obj.partition);
|
||||
|
||||
exofs_sysfs_sb_del(sbi);
|
||||
bdi_destroy(&sbi->bdi);
|
||||
exofs_free_sbi(sbi);
|
||||
sb->s_fs_info = NULL;
|
||||
@ -632,6 +633,12 @@ static int exofs_read_lookup_dev_table(struct exofs_sb_info *sbi,
|
||||
memcpy(&sbi->oc.ods[numdevs], &sbi->oc.ods[0],
|
||||
(numdevs - 1) * sizeof(sbi->oc.ods[0]));
|
||||
|
||||
/* create sysfs subdir under which we put the device table
|
||||
* And cluster layout. A Superblock is identified by the string:
|
||||
* "dev[0].osdname"_"pid"
|
||||
*/
|
||||
exofs_sysfs_sb_add(sbi, &dt->dt_dev_table[0]);
|
||||
|
||||
for (i = 0; i < numdevs; i++) {
|
||||
struct exofs_fscb fscb;
|
||||
struct osd_dev_info odi;
|
||||
@ -657,6 +664,7 @@ static int exofs_read_lookup_dev_table(struct exofs_sb_info *sbi,
|
||||
eds[i].ored.od = fscb_od;
|
||||
++sbi->oc.numdevs;
|
||||
fscb_od = NULL;
|
||||
exofs_sysfs_odev_add(&eds[i], sbi);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -682,6 +690,7 @@ static int exofs_read_lookup_dev_table(struct exofs_sb_info *sbi,
|
||||
odi.osdname);
|
||||
goto out;
|
||||
}
|
||||
exofs_sysfs_odev_add(&eds[i], sbi);
|
||||
|
||||
/* TODO: verify other information is correct and FS-uuid
|
||||
* matches. Benny what did you say about device table
|
||||
@ -745,7 +754,6 @@ static int exofs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
sbi->one_comp.obj.partition = opts->pid;
|
||||
sbi->one_comp.obj.id = 0;
|
||||
exofs_make_credential(sbi->one_comp.cred, &sbi->one_comp.obj);
|
||||
sbi->oc.numdevs = 1;
|
||||
sbi->oc.single_comp = EC_SINGLE_COMP;
|
||||
sbi->oc.comps = &sbi->one_comp;
|
||||
|
||||
@ -804,6 +812,7 @@ static int exofs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
goto free_sbi;
|
||||
|
||||
ore_comp_set_dev(&sbi->oc, 0, od);
|
||||
sbi->oc.numdevs = 1;
|
||||
}
|
||||
|
||||
__sbi_read_stats(sbi);
|
||||
@ -844,6 +853,7 @@ static int exofs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
goto free_sbi;
|
||||
}
|
||||
|
||||
exofs_sysfs_dbg_print();
|
||||
_exofs_print_device("Mounting", opts->dev_name,
|
||||
ore_comp_dev(&sbi->oc, 0),
|
||||
sbi->one_comp.obj.partition);
|
||||
@ -1023,6 +1033,9 @@ static int __init init_exofs(void)
|
||||
if (err)
|
||||
goto out_d;
|
||||
|
||||
/* We don't fail if sysfs creation failed */
|
||||
exofs_sysfs_init();
|
||||
|
||||
return 0;
|
||||
out_d:
|
||||
destroy_inodecache();
|
||||
@ -1032,6 +1045,7 @@ out:
|
||||
|
||||
static void __exit exit_exofs(void)
|
||||
{
|
||||
exofs_sysfs_uninit();
|
||||
unregister_filesystem(&exofs_type);
|
||||
destroy_inodecache();
|
||||
}
|
||||
|
200
fs/exofs/sys.c
Normal file
200
fs/exofs/sys.c
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (C) 2012
|
||||
* Sachin Bhamare <sbhamare@panasas.com>
|
||||
* Boaz Harrosh <bharrosh@panasas.com>
|
||||
*
|
||||
* This file is part of exofs.
|
||||
*
|
||||
* exofs is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* exofs 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 exofs; if not, write to the:
|
||||
* Free Software Foundation <licensing@fsf.org>
|
||||
*/
|
||||
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include "exofs.h"
|
||||
|
||||
struct odev_attr {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct exofs_dev *, char *);
|
||||
ssize_t (*store)(struct exofs_dev *, const char *, size_t);
|
||||
};
|
||||
|
||||
static ssize_t odev_attr_show(struct kobject *kobj, struct attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct exofs_dev *edp = container_of(kobj, struct exofs_dev, ed_kobj);
|
||||
struct odev_attr *a = container_of(attr, struct odev_attr, attr);
|
||||
|
||||
return a->show ? a->show(edp, buf) : 0;
|
||||
}
|
||||
|
||||
static ssize_t odev_attr_store(struct kobject *kobj, struct attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct exofs_dev *edp = container_of(kobj, struct exofs_dev, ed_kobj);
|
||||
struct odev_attr *a = container_of(attr, struct odev_attr, attr);
|
||||
|
||||
return a->store ? a->store(edp, buf, len) : len;
|
||||
}
|
||||
|
||||
static const struct sysfs_ops odev_attr_ops = {
|
||||
.show = odev_attr_show,
|
||||
.store = odev_attr_store,
|
||||
};
|
||||
|
||||
|
||||
static struct kset *exofs_kset;
|
||||
|
||||
static ssize_t osdname_show(struct exofs_dev *edp, char *buf)
|
||||
{
|
||||
struct osd_dev *odev = edp->ored.od;
|
||||
const struct osd_dev_info *odi = osduld_device_info(odev);
|
||||
|
||||
return snprintf(buf, odi->osdname_len + 1, "%s", odi->osdname);
|
||||
}
|
||||
|
||||
static ssize_t systemid_show(struct exofs_dev *edp, char *buf)
|
||||
{
|
||||
struct osd_dev *odev = edp->ored.od;
|
||||
const struct osd_dev_info *odi = osduld_device_info(odev);
|
||||
|
||||
memcpy(buf, odi->systemid, odi->systemid_len);
|
||||
return odi->systemid_len;
|
||||
}
|
||||
|
||||
static ssize_t uri_show(struct exofs_dev *edp, char *buf)
|
||||
{
|
||||
return snprintf(buf, edp->urilen, "%s", edp->uri);
|
||||
}
|
||||
|
||||
static ssize_t uri_store(struct exofs_dev *edp, const char *buf, size_t len)
|
||||
{
|
||||
edp->urilen = strlen(buf) + 1;
|
||||
edp->uri = krealloc(edp->uri, edp->urilen, GFP_KERNEL);
|
||||
strncpy(edp->uri, buf, edp->urilen);
|
||||
return edp->urilen;
|
||||
}
|
||||
|
||||
#define OSD_ATTR(name, mode, show, store) \
|
||||
static struct odev_attr odev_attr_##name = \
|
||||
__ATTR(name, mode, show, store)
|
||||
|
||||
OSD_ATTR(osdname, S_IRUGO, osdname_show, NULL);
|
||||
OSD_ATTR(systemid, S_IRUGO, systemid_show, NULL);
|
||||
OSD_ATTR(uri, S_IRWXU, uri_show, uri_store);
|
||||
|
||||
static struct attribute *odev_attrs[] = {
|
||||
&odev_attr_osdname.attr,
|
||||
&odev_attr_systemid.attr,
|
||||
&odev_attr_uri.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct kobj_type odev_ktype = {
|
||||
.default_attrs = odev_attrs,
|
||||
.sysfs_ops = &odev_attr_ops,
|
||||
};
|
||||
|
||||
static struct kobj_type uuid_ktype = {
|
||||
};
|
||||
|
||||
void exofs_sysfs_dbg_print()
|
||||
{
|
||||
#ifdef CONFIG_EXOFS_DEBUG
|
||||
struct kobject *k_name, *k_tmp;
|
||||
|
||||
list_for_each_entry_safe(k_name, k_tmp, &exofs_kset->list, entry) {
|
||||
printk(KERN_INFO "%s: name %s ref %d\n",
|
||||
__func__, kobject_name(k_name),
|
||||
(int)atomic_read(&k_name->kref.refcount));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/*
|
||||
* This function removes all kobjects under exofs_kset
|
||||
* At the end of it, exofs_kset kobject will have a refcount
|
||||
* of 1 which gets decremented only on exofs module unload
|
||||
*/
|
||||
void exofs_sysfs_sb_del(struct exofs_sb_info *sbi)
|
||||
{
|
||||
struct kobject *k_name, *k_tmp;
|
||||
struct kobject *s_kobj = &sbi->s_kobj;
|
||||
|
||||
list_for_each_entry_safe(k_name, k_tmp, &exofs_kset->list, entry) {
|
||||
/* Remove all that are children of this SBI */
|
||||
if (k_name->parent == s_kobj)
|
||||
kobject_put(k_name);
|
||||
}
|
||||
kobject_put(s_kobj);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function creates sysfs entries to hold the current exofs cluster
|
||||
* instance (uniquely identified by osdname,pid tuple).
|
||||
* This function gets called once per exofs mount instance.
|
||||
*/
|
||||
int exofs_sysfs_sb_add(struct exofs_sb_info *sbi,
|
||||
struct exofs_dt_device_info *dt_dev)
|
||||
{
|
||||
struct kobject *s_kobj;
|
||||
int retval = 0;
|
||||
uint64_t pid = sbi->one_comp.obj.partition;
|
||||
|
||||
/* allocate new uuid dirent */
|
||||
s_kobj = &sbi->s_kobj;
|
||||
s_kobj->kset = exofs_kset;
|
||||
retval = kobject_init_and_add(s_kobj, &uuid_ktype,
|
||||
&exofs_kset->kobj, "%s_%llx", dt_dev->osdname, pid);
|
||||
if (retval) {
|
||||
EXOFS_ERR("ERROR: Failed to create sysfs entry for "
|
||||
"uuid-%s_%llx => %d\n", dt_dev->osdname, pid, retval);
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exofs_sysfs_odev_add(struct exofs_dev *edev, struct exofs_sb_info *sbi)
|
||||
{
|
||||
struct kobject *d_kobj;
|
||||
int retval = 0;
|
||||
|
||||
/* create osd device group which contains following attributes
|
||||
* osdname, systemid & uri
|
||||
*/
|
||||
d_kobj = &edev->ed_kobj;
|
||||
d_kobj->kset = exofs_kset;
|
||||
retval = kobject_init_and_add(d_kobj, &odev_ktype,
|
||||
&sbi->s_kobj, "dev%u", edev->did);
|
||||
if (retval) {
|
||||
EXOFS_ERR("ERROR: Failed to create sysfs entry for "
|
||||
"device dev%u\n", edev->did);
|
||||
return retval;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exofs_sysfs_init(void)
|
||||
{
|
||||
exofs_kset = kset_create_and_add("exofs", NULL, fs_kobj);
|
||||
if (!exofs_kset) {
|
||||
EXOFS_ERR("ERROR: kset_create_and_add exofs failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void exofs_sysfs_uninit(void)
|
||||
{
|
||||
kset_unregister(exofs_kset);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user