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:
Jiri Kosina 2012-06-29 14:45:58 +02:00
commit 59f91e5dd0
221 changed files with 12583 additions and 6104 deletions

View 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).

View file

@ -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.

View 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>;
};
};
};
};

View 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>;
};
};
};

View 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>;
};
};
};

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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));

View 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 */

View file

@ -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);
}

View file

@ -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));

View file

@ -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();

View file

@ -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
/*

View file

@ -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));

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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) {}

View file

@ -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;
}

View file

@ -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"

View file

@ -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
View 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);

View file

@ -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
View 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, &regs->dats);
else
writel(bit, &regs->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(&regs->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, &regs->dirs);
/* Data register after direction, otherwise pullup/down is selected */
if (val)
writel(bit, &regs->dats);
else
writel(bit, &regs->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, &regs->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(&regs->afsela);
if (cfg == PINMUX_TYPE_FUNCTION)
val |= bit;
else
val &= ~bit;
writel(val | bit, &regs->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, &regs->dirs);
writel(bit, &regs->datc);
break;
case PINMUX_TYPE_OUTPUT_HIGH:
writel(bit, &regs->dirs);
writel(bit, &regs->dats);
break;
case PINMUX_TYPE_INPUT:
writel(bit, &regs->dirc);
val = readl(&regs->pdis) | bit;
writel(val, &regs->pdis);
break;
case PINMUX_TYPE_INPUT_PULLUP:
writel(bit, &regs->dirc);
val = readl(&regs->pdis) & ~bit;
writel(val, &regs->pdis);
writel(bit, &regs->dats);
break;
case PINMUX_TYPE_INPUT_PULLDOWN:
writel(bit, &regs->dirc);
val = readl(&regs->pdis) & ~bit;
writel(val, &regs->pdis);
writel(bit, &regs->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(&regs->rimsc) & ~bit;
writel(val, &regs->rimsc);
}
if (chip->irq_type[nr] & IRQ_TYPE_EDGE_FALLING) {
val = readl(&regs->fimsc) & ~bit;
writel(val, &regs->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(&regs->rimsc);
if (type & IRQ_TYPE_EDGE_RISING)
writel(val | bit, &regs->rimsc);
else
writel(val & ~bit, &regs->rimsc);
val = readl(&regs->rimsc);
if (type & IRQ_TYPE_EDGE_FALLING)
writel(val | bit, &regs->fimsc);
else
writel(val & ~bit, &regs->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(&regs->is))) {
nr = __ffs(is);
irq = base + nr;
generic_handle_irq(irq);
writel(1 << nr, &regs->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");

View file

@ -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");

View file

@ -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,

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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");

View file

@ -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)

View file

@ -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,
},
};

View file

@ -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");

View file

@ -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),

View file

@ -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", },

View file

@ -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);

View file

@ -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");

View file

@ -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);
}

View file

@ -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
},
};

View file

@ -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,
},
};

View file

@ -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);

View file

@ -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;
}

View file

@ -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
View 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, &regmap_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");

View 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
View 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, &reg_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, &reg_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");

View file

@ -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
View 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
View 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, &reg_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");

View file

@ -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
View 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
View 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
View 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 */

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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");

View file

@ -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
View 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);

View file

@ -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,

View file

@ -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,
};

View file

@ -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;
}

View file

@ -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, &reg);
tps65910_reg_read(tps65910, TPS65910_INT_STS, &reg);
irq_sts = reg;
tps65910->read(tps65910, TPS65910_INT_STS2, 1, &reg);
tps65910_reg_read(tps65910, TPS65910_INT_STS2, &reg);
irq_sts |= reg << 8;
switch (tps65910_chip_id(tps65910)) {
case TPS65911:
tps65910->read(tps65910, TPS65910_INT_STS3, 1, &reg);
tps65910_reg_read(tps65910, TPS65910_INT_STS3, &reg);
irq_sts |= reg << 16;
}
tps65910->read(tps65910, TPS65910_INT_MSK, 1, &reg);
tps65910_reg_read(tps65910, TPS65910_INT_MSK, &reg);
irq_mask = reg;
tps65910->read(tps65910, TPS65910_INT_MSK2, 1, &reg);
tps65910_reg_read(tps65910, TPS65910_INT_MSK2, &reg);
irq_mask |= reg << 8;
switch (tps65910_chip_id(tps65910)) {
case TPS65911:
tps65910->read(tps65910, TPS65910_INT_MSK3, 1, &reg);
tps65910_reg_read(tps65910, TPS65910_INT_MSK3, &reg);
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, &reg);
tps65910_reg_write(tps65910, TPS65910_INT_STS, reg);
reg = irq_sts & 0xFF;
tps65910->write(tps65910, TPS65910_INT_STS2, 1, &reg);
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, &reg);
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, &reg);
tps65910_reg_read(tps65910, TPS65910_INT_MSK, &reg);
reg_mask = reg;
tps65910->read(tps65910, TPS65910_INT_MSK2, 1, &reg);
tps65910_reg_read(tps65910, TPS65910_INT_MSK2, &reg);
reg_mask |= reg << 8;
switch (tps65910_chip_id(tps65910)) {
case TPS65911:
tps65910->read(tps65910, TPS65910_INT_MSK3, 1, &reg);
tps65910_reg_read(tps65910, TPS65910_INT_MSK3, &reg);
reg_mask |= reg << 16;
}
if (tps65910->irq_mask != reg_mask) {
reg = tps65910->irq_mask & 0xFF;
tps65910->write(tps65910, TPS65910_INT_MSK, 1, &reg);
tps65910_reg_write(tps65910, TPS65910_INT_MSK, reg);
reg = tps65910->irq_mask >> 8 & 0xFF;
tps65910->write(tps65910, TPS65910_INT_MSK2, 1, &reg);
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, &reg);
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,

View file

@ -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,
};

View file

@ -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:

View file

@ -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;
}

View file

@ -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
}

View file

@ -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>");

View file

@ -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) {

View file

@ -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);
}

View file

@ -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);

View file

@ -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);

View file

@ -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, &reg, 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)

View file

@ -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, &reg);
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, &reg);
ret = regmap_read(wm8400->regmap, WM8400_ID, &reg);
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)

View file

@ -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");

View file

@ -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;

View file

@ -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),

View file

@ -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)

View file

@ -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(&reg->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;
}

View file

@ -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)) {

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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 */

View file

@ -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 */

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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");
}

View file

@ -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 */
};

View file

@ -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

View file

@ -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 *
*********************/

View file

@ -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
View 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