* [PATCH 03/36] arch/powerpc: Remove unnecessary casts of private_data
From: Joe Perches @ 2010-07-12 20:49 UTC (permalink / raw)
To: Jiri Kosina
Cc: kvm, Marcelo Tosatti, Alexander Graf, kvm-ppc, linux-kernel,
linuxppc-dev, Paul Mackerras, Avi Kivity
In-Reply-To: <cover.1278967120.git.joe@perches.com>
Signed-off-by: Joe Perches <joe@perches.com>
---
arch/powerpc/kvm/timing.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/kvm/timing.c b/arch/powerpc/kvm/timing.c
index 7037855..46fa04f 100644
--- a/arch/powerpc/kvm/timing.c
+++ b/arch/powerpc/kvm/timing.c
@@ -182,7 +182,7 @@ static ssize_t kvmppc_exit_timing_write(struct file *file,
}
if (c == 'c') {
- struct seq_file *seqf = (struct seq_file *)file->private_data;
+ struct seq_file *seqf = file->private_data;
struct kvm_vcpu *vcpu = seqf->private;
/* Write does not affect our buffers previously generated with
* show. seq_file is locked here to prevent races of init with
--
1.7.1.337.g6068.dirty
^ permalink raw reply related
* Re: [PATCH 0/7] De-couple sysfs memory directories from memory sections
From: Bodo Eggert @ 2010-07-12 19:30 UTC (permalink / raw)
To: Nathan Fontenot, linuxppc-dev, linux-kernel
In-Reply-To: <f5t7d-2ZJ-23@gated-at.bofh.it>
Nathan Fontenot <nfont@austin.ibm.com> wrote:
> The file 'split' allows for splitting the
> directory in two, with each new directory covering half as many
> memory sections as the previous directory.
Just some random thoughts:
1) Why is it needed/helpful?
2) If it is needed, why not take an int to split after n entries?
^ permalink raw reply
* Re: kernel boot stuck at udbg_putc_cpm()
From: Shawn Jin @ 2010-07-12 18:23 UTC (permalink / raw)
To: Scott Wood; +Cc: ppcdev
In-Reply-To: <AANLkTinRCzjFdozxBqCS91deuzVRvenFYq21XVpQnzBh@mail.gmail.com>
For some reason. This email was rejected. Resending...
On Sun, Jul 11, 2010 at 11:26 PM, Shawn Jin <shawnxjin@gmail.com> wrote:
> You're probably getting to the point where udbg is disabled because the
> real serial driver is trying to take over -- and something's going
> wrong with the real serial port driver. =A0Check to make sure the brg
> config is correct (both the input clock and the baud rate you're trying
> to switch to). =A0Commenting out the call to cpm_set_brg can be
> a quick way of determining if that's the problem.
It seems that the last CP command RESTART_TX never completes in the
cpm_uart_console_setup(). I commented out the writes to brgc1 in
cpm_setbrg() in cpm1.c so that the brgc1 value stays the same as
previously set.
The registers related to SMC1 are dumped below before the last
RESTART_TX command. The CPCR was 0x0090. But after issuing the
RESTART_TX command the CPCR kept at 0x0691. Is there any other obvious
reason for CPM not completing the command? It got to be something
related to the settings.
BDI>rd cpcr
cpcr =A0 =A0 =A0 =A0 =A0 : 0x0090 =A0 =A0 =A0144
BDI>rd rccr
rccr =A0 =A0 =A0 =A0 =A0 : 0x0001 =A0 =A0 =A01
BDI>rd rmds
rmds =A0 =A0 =A0 =A0 =A0 : 0x00 =A0 =A0 =A0 =A00
BDI>rd rctr1
rctr1 =A0 =A0 =A0 =A0 =A0: 0x0000 =A0 =A0 =A00
BDI>rd rctr2
rctr2 =A0 =A0 =A0 =A0 =A0: 0x0000 =A0 =A0 =A00
BDI>rd rctr3
rctr3 =A0 =A0 =A0 =A0 =A0: 0x802e =A0 =A0 =A0-32722
BDI>rd rctr4
rctr4 =A0 =A0 =A0 =A0 =A0: 0x802c =A0 =A0 =A0-32724
BDI>rd rter
rter =A0 =A0 =A0 =A0 =A0 : 0x0000 =A0 =A0 =A00
BDI>rd rtmr
rtmr =A0 =A0 =A0 =A0 =A0 : 0x0000 =A0 =A0 =A00
BDI>rd brgc1
brgc1 =A0 =A0 =A0 =A0 =A0: 0x00010618 =A067096
BDI>rd smcmr1
smcmr1 =A0 =A0 =A0 =A0 : 0x4823 =A0 =A0 =A018467
BDI>rd smce1
smce1 =A0 =A0 =A0 =A0 =A0: 0x00 =A0 =A0 =A0 =A00
BDI>rd smcm1
smcm1 =A0 =A0 =A0 =A0 =A0: 0x00 =A0 =A0 =A0 =A00
Thanks,
-Shawn.
^ permalink raw reply
* [PATCH 7/7] Enable multiple memory sections per sysfs memory directory for powerpc/pseries
From: Nathan Fontenot @ 2010-07-12 15:48 UTC (permalink / raw)
To: linux-kernel; +Cc: linuxppc-dev
In-Reply-To: <4C3B3446.5090302@austin.ibm.com>
This patch updates the powerpc/pseries code to initialize
the memory sysfs directory creation to create sysfs directories
that each cover an LMB's worth of memory.
Signed-off-by; Nathan Fontenot <nfont@austin.ibm.ocm>
---
arch/powerpc/platforms/pseries/hotplug-memory.c | 66 +++++++++++++++++++-----
1 file changed, 53 insertions(+), 13 deletions(-)
Index: linux-2.6/arch/powerpc/platforms/pseries/hotplug-memory.c
===================================================================
--- linux-2.6.orig/arch/powerpc/platforms/pseries/hotplug-memory.c 2010-07-09 14:36:52.000000000 -0500
+++ linux-2.6/arch/powerpc/platforms/pseries/hotplug-memory.c 2010-07-09 14:38:25.000000000 -0500
@@ -17,6 +17,54 @@
#include <asm/pSeries_reconfig.h>
#include <asm/sparsemem.h>
+static u32 get_lmb_size(void)
+{
+ struct device_node *np;
+ unsigned int lmb_size = 0;
+
+ np = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
+ if (np) {
+ const unsigned int *size;
+
+ size = of_get_property(np, "ibm,lmb-size", NULL);
+ lmb_size = size ? *size : 0;
+
+ of_node_put(np);
+ } else {
+ const unsigned int *regs;
+
+ np = of_find_node_by_path("/memory@0");
+ if (np) {
+ regs = of_get_property(np, "reg", NULL);
+ lmb_size = regs ? regs[3] : 0;
+ of_node_put(np);
+ }
+
+ if (lmb_size) {
+ /* We now know the size of memory@0, use this to find
+ * the first lmb and get its size.
+ */
+ char buf[64];
+
+ sprintf(buf, "/memory@%x", lmb_size);
+ np = of_find_node_by_path(buf);
+ if (np) {
+ regs = of_get_property(np, "reg", NULL);
+ lmb_size = regs ? regs[3] : 0;
+ of_node_put(np);
+ } else
+ lmb_size = 0;
+ }
+ }
+
+ return lmb_size;
+}
+
+u32 memory_block_size(void)
+{
+ return get_lmb_size();
+}
+
static int pseries_remove_lmb(unsigned long base, unsigned int lmb_size)
{
unsigned long start, start_pfn;
@@ -127,30 +175,22 @@
static int pseries_drconf_memory(unsigned long *base, unsigned int action)
{
- struct device_node *np;
- const unsigned long *lmb_size;
+ unsigned long lmb_size;
int rc;
- np = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
- if (!np)
+ lmb_size = get_lmb_size();
+ if (!lmb_size)
return -EINVAL;
- lmb_size = of_get_property(np, "ibm,lmb-size", NULL);
- if (!lmb_size) {
- of_node_put(np);
- return -EINVAL;
- }
-
if (action == PSERIES_DRCONF_MEM_ADD) {
- rc = lmb_add(*base, *lmb_size);
+ rc = lmb_add(*base, lmb_size);
rc = (rc < 0) ? -EINVAL : 0;
} else if (action == PSERIES_DRCONF_MEM_REMOVE) {
- rc = pseries_remove_lmb(*base, *lmb_size);
+ rc = pseries_remove_lmb(*base, lmb_size);
} else {
rc = -EINVAL;
}
- of_node_put(np);
return rc;
}
^ permalink raw reply
* [PATCH 6/7] Update sysfs node routines for new sysfs memory directories
From: Nathan Fontenot @ 2010-07-12 15:47 UTC (permalink / raw)
To: linux-kernel; +Cc: linuxppc-dev
In-Reply-To: <4C3B3446.5090302@austin.ibm.com>
This patch updates the node sysfs directory routines that create
links to the memory sections under each node. This update makes
the node code aware that a memory sysfs directory can cover
multiple memory sections.
Signed-off-by: Nathan Fontenot <nfont@austin.ibm.com>
---
drivers/base/node.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
Index: linux-2.6/drivers/base/node.c
===================================================================
--- linux-2.6.orig/drivers/base/node.c 2010-07-09 14:36:53.000000000 -0500
+++ linux-2.6/drivers/base/node.c 2010-07-09 14:38:22.000000000 -0500
@@ -346,8 +346,10 @@
return -EFAULT;
if (!node_online(nid))
return 0;
- sect_start_pfn = section_nr_to_pfn(mem_blk->phys_index);
- sect_end_pfn = sect_start_pfn + PAGES_PER_SECTION - 1;
+
+ sect_start_pfn = section_nr_to_pfn(mem_blk->start_phys_index);
+ sect_end_pfn = section_nr_to_pfn(mem_blk->end_phys_index);
+ sect_end_pfn += PAGES_PER_SECTION - 1;
for (pfn = sect_start_pfn; pfn <= sect_end_pfn; pfn++) {
int page_nid;
@@ -383,8 +385,10 @@
if (!unlinked_nodes)
return -ENOMEM;
nodes_clear(*unlinked_nodes);
- sect_start_pfn = section_nr_to_pfn(mem_blk->phys_index);
- sect_end_pfn = sect_start_pfn + PAGES_PER_SECTION - 1;
+
+ sect_start_pfn = section_nr_to_pfn(mem_blk->start_phys_index);
+ sect_end_pfn = section_nr_to_pfn(mem_blk->end_phys_index);
+ sect_end_pfn += PAGES_PER_SECTION - 1;
for (pfn = sect_start_pfn; pfn <= sect_end_pfn; pfn++) {
int nid;
^ permalink raw reply
* [PATCH 5/7] update the mutex name in the memory_block struct
From: Nathan Fontenot @ 2010-07-12 15:46 UTC (permalink / raw)
To: linux-kernel; +Cc: linuxppc-dev
In-Reply-To: <4C3B3446.5090302@austin.ibm.com>
This patch updates the name of the memory_block mutex
since it is now used for more than just gating changes
to the status of the memory section coveerd by the memory
sysfs directory.
Signed-off-by: Nathan Fontenot <nfont@austin.ibm.com>
---
drivers/base/memory.c | 20 ++++++++++----------
include/linux/memory.h | 9 +--------
2 files changed, 11 insertions(+), 18 deletions(-)
Index: linux-2.6/drivers/base/memory.c
===================================================================
--- linux-2.6.orig/drivers/base/memory.c 2010-07-09 14:38:09.000000000 -0500
+++ linux-2.6/drivers/base/memory.c 2010-07-09 14:38:18.000000000 -0500
@@ -242,7 +242,7 @@
struct list_head *pos;
int ret = 0;
- mutex_lock(&mem->state_mutex);
+ mutex_lock(&mem->mutex);
list_for_each(pos, &mem->sections) {
mbs = list_entry(pos, struct memory_block_section, next);
@@ -272,7 +272,7 @@
if (!ret)
mem->state = to_state;
- mutex_unlock(&mem->state_mutex);
+ mutex_unlock(&mem->mutex);
return ret;
}
@@ -352,7 +352,7 @@
if (list_is_singular(&mem->sections))
return -EINVAL;
- mutex_lock(&mem->state_mutex);
+ mutex_lock(&mem->mutex);
list_for_each(pos, &mem->sections) {
mbs = list_entry(pos, struct memory_block_section, next);
@@ -370,11 +370,11 @@
if (!new_mem_blk)
return -ENOMEM;
- mutex_init(&new_mem_blk->state_mutex);
+ mutex_init(&new_mem_blk->mutex);
INIT_LIST_HEAD(&new_mem_blk->sections);
new_mem_blk->state = mem->state;
- mutex_lock(&new_mem_blk->state_mutex);
+ mutex_lock(&new_mem_blk->mutex);
new_blk_total = total_scns / 2;
new_blk_min = max_scn_nr - new_blk_total + 1;
@@ -395,8 +395,8 @@
update_memory_block_phys_indexes(mem);
update_memory_block_phys_indexes(new_mem_blk);
- mutex_unlock(&new_mem_blk->state_mutex);
- mutex_unlock(&mem->state_mutex);
+ mutex_unlock(&new_mem_blk->mutex);
+ mutex_unlock(&mem->mutex);
return count;
}
@@ -653,7 +653,7 @@
return -ENOMEM;
mem->state = state;
- mutex_init(&mem->state_mutex);
+ mutex_init(&mem->mutex);
start_pfn = section_nr_to_pfn(__section_nr(section));
mem->phys_device = arch_get_memory_phys_device(start_pfn);
INIT_LIST_HEAD(&mem->sections);
@@ -680,7 +680,7 @@
int section_nr = __section_nr(section);
mem = find_memory_block(section);
- mutex_lock(&mem->state_mutex);
+ mutex_lock(&mem->mutex);
/* remove the specified section */
list_for_each_safe(pos, tmp, &mem->sections) {
@@ -692,7 +692,7 @@
}
}
- mutex_unlock(&mem->state_mutex);
+ mutex_unlock(&mem->mutex);
if (list_empty(&mem->sections)) {
unregister_mem_sect_under_nodes(mem);
Index: linux-2.6/include/linux/memory.h
===================================================================
--- linux-2.6.orig/include/linux/memory.h 2010-07-09 14:36:54.000000000 -0500
+++ linux-2.6/include/linux/memory.h 2010-07-09 14:38:18.000000000 -0500
@@ -31,14 +31,7 @@
unsigned long state;
unsigned long start_phys_index;
unsigned long end_phys_index;
-
- /*
- * This serializes all state change requests. It isn't
- * held during creation because the control files are
- * created long after the critical areas during
- * initialization.
- */
- struct mutex state_mutex;
+ struct mutex mutex;
int phys_device; /* to which fru does this belong? */
void *hw; /* optional pointer to fw/hw data */
int (*phys_callback)(struct memory_block *);
^ permalink raw reply
* [PATCH 4/7] Allow sysfs memory directories to be split
From: Nathan Fontenot @ 2010-07-12 15:45 UTC (permalink / raw)
To: linux-kernel; +Cc: linuxppc-dev
In-Reply-To: <4C3B3446.5090302@austin.ibm.com>
This patch introduces the new 'split' file in each memory sysfs
directory and the associated routines needed to handle splitting
a directory.
Signed-off-by; Nathan Fontenot <nfont@austin.ibm.com>
---
drivers/base/memory.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 98 insertions(+), 1 deletion(-)
Index: linux-2.6/drivers/base/memory.c
===================================================================
--- linux-2.6.orig/drivers/base/memory.c 2010-07-09 14:23:20.000000000 -0500
+++ linux-2.6/drivers/base/memory.c 2010-07-09 14:38:09.000000000 -0500
@@ -32,6 +32,9 @@
static int sections_per_block;
+static int register_memory(struct memory_block *, struct mem_section *,
+ int, enum mem_add_context);
+
static inline int base_memory_block_id(int section_nr)
{
return (section_nr / sections_per_block) * sections_per_block;
@@ -309,11 +312,100 @@
return sprintf(buf, "%d\n", mem->phys_device);
}
+static void update_memory_block_phys_indexes(struct memory_block *mem)
+{
+ struct list_head *pos;
+ struct memory_block_section *mbs;
+ unsigned long min_index = 0xffffffff;
+ unsigned long max_index = 0;
+
+ list_for_each(pos, &mem->sections) {
+ mbs = list_entry(pos, struct memory_block_section, next);
+
+ if (mbs->phys_index < min_index)
+ min_index = mbs->phys_index;
+
+ if (mbs->phys_index > max_index)
+ max_index = mbs->phys_index;
+ }
+
+ mem->start_phys_index = min_index;
+ mem->end_phys_index = max_index;
+}
+
+static ssize_t
+store_mem_split_block(struct sys_device *dev, struct sysdev_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct memory_block *mem, *new_mem_blk;
+ struct memory_block_section *mbs;
+ struct list_head *pos, *tmp;
+ struct mem_section *section;
+ int min_scn_nr = 0;
+ int max_scn_nr = 0;
+ int total_scns = 0;
+ int new_blk_min, new_blk_total;
+ int ret = -EINVAL;
+
+ mem = container_of(dev, struct memory_block, sysdev);
+
+ if (list_is_singular(&mem->sections))
+ return -EINVAL;
+
+ mutex_lock(&mem->state_mutex);
+
+ list_for_each(pos, &mem->sections) {
+ mbs = list_entry(pos, struct memory_block_section, next);
+
+ total_scns++;
+
+ if (min_scn_nr > mbs->phys_index)
+ min_scn_nr = mbs->phys_index;
+
+ if (max_scn_nr < mbs->phys_index)
+ max_scn_nr = mbs->phys_index;
+ }
+
+ new_mem_blk = kzalloc(sizeof(*new_mem_blk), GFP_KERNEL);
+ if (!new_mem_blk)
+ return -ENOMEM;
+
+ mutex_init(&new_mem_blk->state_mutex);
+ INIT_LIST_HEAD(&new_mem_blk->sections);
+ new_mem_blk->state = mem->state;
+
+ mutex_lock(&new_mem_blk->state_mutex);
+
+ new_blk_total = total_scns / 2;
+ new_blk_min = max_scn_nr - new_blk_total + 1;
+
+ section = __nr_to_section(new_blk_min);
+ ret = register_memory(new_mem_blk, section, 0, HOTPLUG);
+
+ list_for_each_safe(pos, tmp, &mem->sections) {
+ unsigned long scn_nr;
+
+ mbs = list_entry(pos, struct memory_block_section, next);
+ scn_nr = mbs->phys_index;
+
+ if ((scn_nr >= new_blk_min) && (scn_nr <= max_scn_nr))
+ list_move(&mbs->next, &new_mem_blk->sections);
+ }
+
+ update_memory_block_phys_indexes(mem);
+ update_memory_block_phys_indexes(new_mem_blk);
+
+ mutex_unlock(&new_mem_blk->state_mutex);
+ mutex_unlock(&mem->state_mutex);
+ return count;
+}
+
static SYSDEV_ATTR(phys_index, 0444, show_mem_phys_index, NULL);
static SYSDEV_ATTR(end_phys_index, 0444, show_mem_end_phys_index, NULL);
static SYSDEV_ATTR(state, 0644, show_mem_state, store_mem_state);
static SYSDEV_ATTR(phys_device, 0444, show_phys_device, NULL);
static SYSDEV_ATTR(removable, 0444, show_mem_removable, NULL);
+static SYSDEV_ATTR(split, 0200, NULL, store_mem_split_block);
#define mem_create_simple_file(mem, attr_name) \
sysdev_create_file(&mem->sysdev, &attr_##attr_name)
@@ -343,6 +435,8 @@
ret = mem_create_simple_file(memory, phys_device);
if (!ret)
ret = mem_create_simple_file(memory, removable);
+ if (!ret)
+ ret = mem_create_simple_file(memory, split);
if (!ret) {
if (context == HOTPLUG)
ret = register_mem_sect_under_node(memory, nid);
@@ -361,6 +455,7 @@
mem_remove_simple_file(memory, state);
mem_remove_simple_file(memory, phys_device);
mem_remove_simple_file(memory, removable);
+ mem_remove_simple_file(memory, split);
/* drop the ref. we got in remove_memory_block() */
kobject_put(&memory->sysdev.kobj);
@@ -568,8 +663,10 @@
kobject_put(&mem->sysdev.kobj);
}
- if (!ret)
+ if (!ret) {
ret = add_mem_block_section(mem, __section_nr(section), state);
+ update_memory_block_phys_indexes(mem);
+ }
return ret;
}
^ permalink raw reply
* [PATCH 3/7] Update the [register,unregister]_memory routines
From: Nathan Fontenot @ 2010-07-12 15:44 UTC (permalink / raw)
To: linux-kernel; +Cc: linuxppc-dev
In-Reply-To: <4C3B3446.5090302@austin.ibm.com>
This patch moves the register/unregister_memory routines to
avoid a forward declaration. It also moves the sysfs file
creation and deletion for each directory into the register/
unregister routines to avoid duplicating it with these updates.
Signed-off-by: Nathan Fontenot <nfont@austin.ibm.com>
---
drivers/base/memory.c | 93 +++++++++++++++++++++++++-------------------------
1 file changed, 48 insertions(+), 45 deletions(-)
Index: linux-2.6/drivers/base/memory.c
===================================================================
--- linux-2.6.orig/drivers/base/memory.c 2010-07-09 14:23:17.000000000 -0500
+++ linux-2.6/drivers/base/memory.c 2010-07-09 14:23:20.000000000 -0500
@@ -87,31 +87,6 @@
EXPORT_SYMBOL(unregister_memory_isolate_notifier);
/*
- * register_memory - Setup a sysfs device for a memory block
- */
-static
-int register_memory(struct memory_block *memory, struct mem_section *section)
-{
- int error;
-
- memory->sysdev.cls = &memory_sysdev_class;
- memory->sysdev.id = __section_nr(section);
-
- error = sysdev_register(&memory->sysdev);
- return error;
-}
-
-static void
-unregister_memory(struct memory_block *memory)
-{
- BUG_ON(memory->sysdev.cls != &memory_sysdev_class);
-
- /* drop the ref. we got in remove_memory_block() */
- kobject_put(&memory->sysdev.kobj);
- sysdev_unregister(&memory->sysdev);
-}
-
-/*
* use this as the physical section index that this memsection
* uses.
*/
@@ -346,6 +321,53 @@
sysdev_remove_file(&mem->sysdev, &attr_##attr_name)
/*
+ * register_memory - Setup a sysfs device for a memory block
+ */
+static
+int register_memory(struct memory_block *memory, struct mem_section *section,
+ int nid, enum mem_add_context context)
+{
+ int ret;
+
+ memory->sysdev.cls = &memory_sysdev_class;
+ memory->sysdev.id = __section_nr(section);
+
+ ret = sysdev_register(&memory->sysdev);
+ if (!ret)
+ ret = mem_create_simple_file(memory, phys_index);
+ if (!ret)
+ ret = mem_create_simple_file(memory, end_phys_index);
+ if (!ret)
+ ret = mem_create_simple_file(memory, state);
+ if (!ret)
+ ret = mem_create_simple_file(memory, phys_device);
+ if (!ret)
+ ret = mem_create_simple_file(memory, removable);
+ if (!ret) {
+ if (context == HOTPLUG)
+ ret = register_mem_sect_under_node(memory, nid);
+ }
+
+ return ret;
+}
+
+static void
+unregister_memory(struct memory_block *memory)
+{
+ BUG_ON(memory->sysdev.cls != &memory_sysdev_class);
+
+ mem_remove_simple_file(memory, phys_index);
+ mem_remove_simple_file(memory, end_phys_index);
+ mem_remove_simple_file(memory, state);
+ mem_remove_simple_file(memory, phys_device);
+ mem_remove_simple_file(memory, removable);
+
+ /* drop the ref. we got in remove_memory_block() */
+ kobject_put(&memory->sysdev.kobj);
+ sysdev_unregister(&memory->sysdev);
+}
+
+/*
* Block size attribute stuff
*/
static ssize_t
@@ -541,21 +563,7 @@
mem->phys_device = arch_get_memory_phys_device(start_pfn);
INIT_LIST_HEAD(&mem->sections);
- ret = register_memory(mem, section);
- if (!ret)
- ret = mem_create_simple_file(mem, phys_index);
- if (!ret)
- ret = mem_create_simple_file(mem, end_phys_index);
- if (!ret)
- ret = mem_create_simple_file(mem, state);
- if (!ret)
- ret = mem_create_simple_file(mem, phys_device);
- if (!ret)
- ret = mem_create_simple_file(mem, removable);
- if (!ret) {
- if (context == HOTPLUG)
- ret = register_mem_sect_under_node(mem, nid);
- }
+ ret = register_memory(mem, section, nid, context);
} else {
kobject_put(&mem->sysdev.kobj);
}
@@ -591,11 +599,6 @@
if (list_empty(&mem->sections)) {
unregister_mem_sect_under_nodes(mem);
- mem_remove_simple_file(mem, phys_index);
- mem_remove_simple_file(mem, end_phys_index);
- mem_remove_simple_file(mem, state);
- mem_remove_simple_file(mem, phys_device);
- mem_remove_simple_file(mem, removable);
unregister_memory(mem);
kfree(mem);
}
^ permalink raw reply
* [PATCH 2/7] Create the new 'end_phys_index' file
From: Nathan Fontenot @ 2010-07-12 15:43 UTC (permalink / raw)
To: linux-kernel; +Cc: linuxppc-dev
In-Reply-To: <4C3B3446.5090302@austin.ibm.com>
This patch adds a new 'end_phys_index' file to each memory sysfs
directory to report the physical index of the last memory section
coverd by the sysfs directory.
Signed -off-by: Nathan Fontenot <nfont@austin.ibm.com>
---
drivers/base/memory.c | 14 +++++++++++++-
include/linux/memory.h | 3 +++
2 files changed, 16 insertions(+), 1 deletion(-)
Index: linux-2.6/drivers/base/memory.c
===================================================================
--- linux-2.6.orig/drivers/base/memory.c 2010-07-09 14:23:09.000000000 -0500
+++ linux-2.6/drivers/base/memory.c 2010-07-09 14:23:17.000000000 -0500
@@ -121,7 +121,15 @@
{
struct memory_block *mem =
container_of(dev, struct memory_block, sysdev);
- return sprintf(buf, "%08lx\n", mem->phys_index);
+ return sprintf(buf, "%08lx\n", mem->start_phys_index);
+}
+
+static ssize_t show_mem_end_phys_index(struct sys_device *dev,
+ struct sysdev_attribute *attr, char *buf)
+{
+ struct memory_block *mem =
+ container_of(dev, struct memory_block, sysdev);
+ return sprintf(buf, "%08lx\n", mem->end_phys_index);
}
/*
@@ -327,6 +335,7 @@
}
static SYSDEV_ATTR(phys_index, 0444, show_mem_phys_index, NULL);
+static SYSDEV_ATTR(end_phys_index, 0444, show_mem_end_phys_index, NULL);
static SYSDEV_ATTR(state, 0644, show_mem_state, store_mem_state);
static SYSDEV_ATTR(phys_device, 0444, show_phys_device, NULL);
static SYSDEV_ATTR(removable, 0444, show_mem_removable, NULL);
@@ -536,6 +545,8 @@
if (!ret)
ret = mem_create_simple_file(mem, phys_index);
if (!ret)
+ ret = mem_create_simple_file(mem, end_phys_index);
+ if (!ret)
ret = mem_create_simple_file(mem, state);
if (!ret)
ret = mem_create_simple_file(mem, phys_device);
@@ -581,6 +592,7 @@
if (list_empty(&mem->sections)) {
unregister_mem_sect_under_nodes(mem);
mem_remove_simple_file(mem, phys_index);
+ mem_remove_simple_file(mem, end_phys_index);
mem_remove_simple_file(mem, state);
mem_remove_simple_file(mem, phys_device);
mem_remove_simple_file(mem, removable);
Index: linux-2.6/include/linux/memory.h
===================================================================
--- linux-2.6.orig/include/linux/memory.h 2010-07-09 14:22:44.000000000 -0500
+++ linux-2.6/include/linux/memory.h 2010-07-09 14:23:17.000000000 -0500
@@ -29,6 +29,9 @@
struct memory_block {
unsigned long state;
+ unsigned long start_phys_index;
+ unsigned long end_phys_index;
+
/*
* This serializes all state change requests. It isn't
* held during creation because the control files are
^ permalink raw reply
* [PATCH 1/7] Split the memory_block structure
From: Nathan Fontenot @ 2010-07-12 15:42 UTC (permalink / raw)
To: linux-kernel; +Cc: linuxppc-dev
In-Reply-To: <4C3B3446.5090302@austin.ibm.com>
This patch splits the memory_block struct into a memory_block
struct to cover each sysfs directory and a new memory_block_section
struct for each memory section covered by the sysfs directory.
This also updates the routine handling memory_block creation
and manipulation to use these updated structures.
Signed -off-by: Nathan Fontenot <nfont@austin.ibm.com>
---
drivers/base/memory.c | 228 +++++++++++++++++++++++++++++++++++--------------
include/linux/memory.h | 11 +-
2 files changed, 172 insertions(+), 67 deletions(-)
Index: linux-2.6/drivers/base/memory.c
===================================================================
--- linux-2.6.orig/drivers/base/memory.c 2010-07-08 11:27:21.000000000 -0500
+++ linux-2.6/drivers/base/memory.c 2010-07-09 14:23:09.000000000 -0500
@@ -28,6 +28,14 @@
#include <asm/uaccess.h>
#define MEMORY_CLASS_NAME "memory"
+#define MIN_MEMORY_BLOCK_SIZE (1 << SECTION_SIZE_BITS)
+
+static int sections_per_block;
+
+static inline int base_memory_block_id(int section_nr)
+{
+ return (section_nr / sections_per_block) * sections_per_block;
+}
static struct sysdev_class memory_sysdev_class = {
.name = MEMORY_CLASS_NAME,
@@ -94,10 +102,9 @@
}
static void
-unregister_memory(struct memory_block *memory, struct mem_section *section)
+unregister_memory(struct memory_block *memory)
{
BUG_ON(memory->sysdev.cls != &memory_sysdev_class);
- BUG_ON(memory->sysdev.id != __section_nr(section));
/* drop the ref. we got in remove_memory_block() */
kobject_put(&memory->sysdev.kobj);
@@ -123,13 +130,20 @@
static ssize_t show_mem_removable(struct sys_device *dev,
struct sysdev_attribute *attr, char *buf)
{
- unsigned long start_pfn;
- int ret;
- struct memory_block *mem =
- container_of(dev, struct memory_block, sysdev);
+ struct list_head *pos, *tmp;
+ struct memory_block *mem;
+ int ret = 1;
+
+ mem = container_of(dev, struct memory_block, sysdev);
+ list_for_each_safe(pos, tmp, &mem->sections) {
+ struct memory_block_section *mbs;
+ unsigned long start_pfn;
+
+ mbs = list_entry(pos, struct memory_block_section, next);
+ start_pfn = section_nr_to_pfn(mbs->phys_index);
+ ret &= is_mem_section_removable(start_pfn, PAGES_PER_SECTION);
+ }
- start_pfn = section_nr_to_pfn(mem->phys_index);
- ret = is_mem_section_removable(start_pfn, PAGES_PER_SECTION);
return sprintf(buf, "%d\n", ret);
}
@@ -182,16 +196,16 @@
* OK to have direct references to sparsemem variables in here.
*/
static int
-memory_block_action(struct memory_block *mem, unsigned long action)
+memory_block_action(struct memory_block_section *mbs, unsigned long action)
{
int i;
unsigned long psection;
unsigned long start_pfn, start_paddr;
struct page *first_page;
int ret;
- int old_state = mem->state;
ot-option-to-disable-memory-hotplug.patch
+ int old_state = mbs->state;
- psection = mem->phys_index;
+ psection = mbs->phys_index;
first_page = pfn_to_page(psection << PFN_SECTION_SHIFT);
/*
@@ -217,18 +231,18 @@
ret = online_pages(start_pfn, PAGES_PER_SECTION);
break;
case MEM_OFFLINE:
- mem->state = MEM_GOING_OFFLINE;
+ mbs->state = MEM_GOING_OFFLINE;
start_paddr = page_to_pfn(first_page) << PAGE_SHIFT;
ret = remove_memory(start_paddr,
PAGES_PER_SECTION << PAGE_SHIFT);
if (ret) {
- mem->state = old_state;
+ mbs->state = old_state;
break;
}
break;
default:
WARN(1, KERN_WARNING "%s(%p, %ld) unknown action: %ld\n",
- __func__, mem, action, action);
+ __func__, mbs, action, action);
ret = -EINVAL;
}
@@ -238,19 +252,40 @@
static int memory_block_change_state(struct memory_block *mem,
unsigned long to_state, unsigned long from_state_req)
{
+ struct memory_block_section *mbs;
+ struct list_head *pos;
int ret = 0;
+
mutex_lock(&mem->state_mutex);
- if (mem->state != from_state_req) {
- ret = -EINVAL;
- goto out;
+ list_for_each(pos, &mem->sections) {
+ mbs = list_entry(pos, struct memory_block_section, next);
+
+ if (mbs->state != from_state_req)
+ continue;
+
+ ret = memory_block_action(mbs, to_state);
+ if (ret)
+ break;
+ }
+
+ if (ret) {
+ list_for_each(pos, &mem->sections) {
+ mbs = list_entry(pos, struct memory_block_section,
+ next);
+
+ if (mbs->state == from_state_req)
+ continue;
+
+ if (memory_block_action(mbs, to_state))
+ printk(KERN_ERR "Could not re-enable memory "
+ "section %lx\n", mbs->phys_index);
+ }
}
- ret = memory_block_action(mem, to_state);
if (!ret)
mem->state = to_state;
-out:
mutex_unlock(&mem->state_mutex);
return ret;
}
@@ -260,20 +295,15 @@
struct sysdev_attribute *attr, const char *buf, size_t count)
{
struct memory_block *mem;
- unsigned int phys_section_nr;
int ret = -EINVAL;
mem = container_of(dev, struct memory_block, sysdev);
- phys_section_nr = mem->phys_index;
-
- if (!present_section_nr(phys_section_nr))
- goto out;
if (!strncmp(buf, "online", min((int)count, 6)))
ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE);
else if(!strncmp(buf, "offline", min((int)count, 7)))
ret = memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE);
-out:
+
if (ret)
return ret;
return count;
@@ -435,39 +465,6 @@
return 0;
}
-static int add_memory_block(int nid, struct mem_section *section,
- unsigned long state, enum mem_add_context context)
-{
- struct memory_block *mem = kzalloc(sizeof(*mem), GFP_KERNEL);
- unsigned long start_pfn;
- int ret = 0;
-
- if (!mem)
- return -ENOMEM;
-
- mem->phys_index = __section_nr(section);
- mem->state = state;
- mutex_init(&mem->state_mutex);
- start_pfn = section_nr_to_pfn(mem->phys_index);
- mem->phys_device = arch_get_memory_phys_device(start_pfn);
-
- ret = register_memory(mem, section);
- if (!ret)
- ret = mem_create_simple_file(mem, phys_index);
- if (!ret)
- ret = mem_create_simple_file(mem, state);
- if (!ret)
- ret = mem_create_simple_file(mem, phys_device);
- if (!ret)
- ret = mem_create_simple_file(mem, removable);
- if (!ret) {
- if (context == HOTPLUG)
- ret = register_mem_sect_under_node(mem, nid);
- }
-
- return ret;
-}
-
/*
* For now, we have a linear search to go find the appropriate
* memory_block corresponding to a particular phys_index. If
@@ -482,12 +479,13 @@
struct sys_device *sysdev;
struct memory_block *mem;
char name[sizeof(MEMORY_CLASS_NAME) + 9 + 1];
+ int block_id = base_memory_block_id(__section_nr(section));
/*
* This only works because we know that section == sysdev->id
* slightly redundant with sysdev_register()
*/
- sprintf(&name[0], "%s%d", MEMORY_CLASS_NAME, __section_nr(section));
+ sprintf(&name[0], "%s%d", MEMORY_CLASS_NAME, block_id);
kobj = kset_find_obj(&memory_sysdev_class.kset, name);
if (!kobj)
@@ -498,19 +496,97 @@
return mem;
}
+static int add_mem_block_section(struct memory_block *mem,
+ int section_nr, unsigned long state)
+{
+ struct memory_block_section *mbs;
+
+ mbs = kzalloc(sizeof(*mbs), GFP_KERNEL);
+ if (!mbs)
+ return -ENOMEM;
+
+ mbs->phys_index = section_nr;
+ mbs->state = state;
+
+ list_add(&mbs->next, &mem->sections);
+ return 0;
+}
+
+static int add_memory_block(int nid, struct mem_section *section,
+ unsigned long state, enum mem_add_context context)
+{
+ struct memory_block *mem;
+ int ret = 0;
+
+ mem = find_memory_block(section);
+ if (!mem) {
+ unsigned long start_pfn;
+
+ mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+ if (!mem)
+ return -ENOMEM;
+
+ mem->state = state;
+ mutex_init(&mem->state_mutex);
+ start_pfn = section_nr_to_pfn(__section_nr(section));
+ mem->phys_device = arch_get_memory_phys_device(start_pfn);
+ INIT_LIST_HEAD(&mem->sections);
+
+ ret = register_memory(mem, section);
+ if (!ret)
+ ret = mem_create_simple_file(mem, phys_index);
+ if (!ret)
+ ret = mem_create_simple_file(mem, state);
+ if (!ret)
+ ret = mem_create_simple_file(mem, phys_device);
+ if (!ret)
+ ret = mem_create_simple_file(mem, removable);
+ if (!ret) {
+ if (context == HOTPLUG)
+ ret = register_mem_sect_under_node(mem, nid);
+ }
+ } else {
+ kobject_put(&mem->sysdev.kobj);
+ }
+
+ if (!ret)
+ ret = add_mem_block_section(mem, __section_nr(section), state);
+
+ return ret;
+}
int remove_memory_block(unsigned long node_id, struct mem_section *section,
int phys_device)
{
struct memory_block *mem;
+ struct memory_block_section *mbs;
+ struct list_head *pos, *tmp;
+ int section_nr = __section_nr(section);
mem = find_memory_block(section);
- unregister_mem_sect_under_nodes(mem);
- mem_remove_simple_file(mem, phys_index);
- mem_remove_simple_file(mem, state);
- mem_remove_simple_file(mem, phys_device);
- mem_remove_simple_file(mem, removable);
- unregister_memory(mem, section);
+ mutex_lock(&mem->state_mutex);
+
+ /* remove the specified section */
+ list_for_each_safe(pos, tmp, &mem->sections) {
+ mbs = list_entry(pos, struct memory_block_section, next);
+
+ if (mbs->phys_index == section_nr) {
+ list_del(&mbs->next);
+ kfree(mbs);
+ }
+ }
+
+ mutex_unlock(&mem->state_mutex);
+
+ if (list_empty(&mem->sections)) {
+ unregister_mem_sect_under_nodes(mem);
+ mem_remove_simple_file(mem, phys_index);
+ mem_remove_simple_file(mem, state);
+ mem_remove_simple_file(mem, phys_device);
+ mem_remove_simple_file(mem, removable);
+ unregister_memory(mem);
+ kfree(mem);
+ }
return 0;
}
@@ -532,6 +608,24 @@
return remove_memory_block(0, section, 0);
}
+u32 __weak memory_block_size(void)
+{
+ return MIN_MEMORY_BLOCK_SIZE;
+}
+
+static u32 get_memory_block_size(void)
+{
+ u32 blk_sz;
+
+ blk_sz = memory_block_size();
+
+ /* Validate blk_sz is a power of 2 and not less than section size */
+ if ((blk_sz & (blk_sz - 1)) || (blk_sz < MIN_MEMORY_BLOCK_SIZE))
+ blk_sz = MIN_MEMORY_BLOCK_SIZE;
+
+ return blk_sz;
+}
+
/*
* Initialize the sysfs support for memory devices...
*/
@@ -540,12 +634,16 @@
unsigned int i;
int ret;
int err;
+ int block_sz;
memory_sysdev_class.kset.uevent_ops = &memory_uevent_ops;
ret = sysdev_class_register(&memory_sysdev_class);
if (ret)
goto out;
+ block_sz = get_memory_block_size();
+ sections_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE;
+
/*
* Create entries for memory sections that were found
* during boot and have been initialized
Index: linux-2.6/include/linux/memory.h
===================================================================
--- linux-2.6.orig/include/linux/memory.h 2010-07-08 11:27:21.000000000 -0500
+++ linux-2.6/include/linux/memory.h 2010-07-09 14:22:44.000000000 -0500
@@ -19,9 +19,15 @@
#include <linux/node.h>
#include <linux/compiler.h>
#include <linux/mutex.h>
+#include <linux/list.h>
-struct memory_block {
+struct memory_block_section {
+ unsigned long state;
unsigned long phys_index;
+ struct list_head next;
+};
+
+struct memory_block {
unsigned long state;
/*
* This serializes all state change requests. It isn't
@@ -34,6 +40,7 @@
void *hw; /* optional pointer to fw/hw data */
int (*phys_callback)(struct memory_block *);
struct sys_device sysdev;
+ struct list_head sections;
};
int arch_get_memory_phys_device(unsigned long start_pfn);
@@ -113,7 +120,7 @@
extern int remove_memory_block(unsigned long, struct mem_section *, int);
extern int memory_notify(unsigned long val, void *v);
extern int memory_isolate_notify(unsigned long val, void *v);
-extern struct memory_block *find_memory_block(unsigned long);
+extern struct memory_block *find_memory_block(struct mem_section *);
extern int memory_is_hidden(struct mem_section *);
#define CONFIG_MEM_BLOCK_SIZE (PAGES_PER_SECTION<<PAGE_SHIFT)
enum mem_add_context { BOOT, HOTPLUG };
^ permalink raw reply
* [PATCH 0/7] De-couple sysfs memory directories from memory sections
From: Nathan Fontenot @ 2010-07-12 15:27 UTC (permalink / raw)
To: linux-kernel; +Cc: linuxppc-dev
This set of patches de-couples the idea that there is a single
directory in sysfs for each memory section. The intent of the
patches is to reduce the number of sysfs directories created to
resolve a boot-time performance issue. On very large systems
boot time are getting very long (as seen on powerpc hardware)
due to the enormous number of sysfs directories being created.
On a system with 1 TB of memory we create ~63,000 directories.
For even larger systems boot times are being measured in hours.
This set of patches allows for each directory created in sysfs
to cover more than one memory section. The default behavior for
sysfs directory creation is the same, in that each directory
represents a single memory section. This update also adds to
new files to each sysfs memory directory. The file 'end_phys_index'
contains the physical id of the last memory section covered by
the directory. The file 'split' allows for splitting the
directory in two, with each new directory covering half as many
memory sections as the previous directory.
Thanks,
Nathan Fontenot
^ permalink raw reply
* RE: [PATCH 14/27] KVM: PPC: Magic Page BookE support
From: Liu Yu-B13201 @ 2010-07-12 11:24 UTC (permalink / raw)
To: Alexander Graf, kvm-ppc; +Cc: linuxppc-dev, KVM list
In-Reply-To: <1277980982-12433-15-git-send-email-agraf@suse.de>
=20
> -----Original Message-----
> From: kvm-owner@vger.kernel.org=20
> [mailto:kvm-owner@vger.kernel.org] On Behalf Of Alexander Graf
> Sent: Thursday, July 01, 2010 6:43 PM
> To: kvm-ppc@vger.kernel.org
> Cc: KVM list; linuxppc-dev
> Subject: [PATCH 14/27] KVM: PPC: Magic Page BookE support
>=20
> As we now have Book3s support for the magic page, we also=20
> need BookE to
> join in on the party.
>=20
> This patch implements generic magic page logic for BookE and specific
> TLB logic for e500. I didn't have any 440 around, so I didn't dare to
> blindly try and write up broken code.
>=20
> Signed-off-by: Alexander Graf <agraf@suse.de>
> ---
> arch/powerpc/kvm/booke.c | 29 +++++++++++++++++++++++++++++
> arch/powerpc/kvm/e500_tlb.c | 19 +++++++++++++++++--
> 2 files changed, 46 insertions(+), 2 deletions(-)
>=20
> diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
> index 0f8ff9d..9609207 100644
> --- a/arch/powerpc/kvm/booke.c
> +++ b/arch/powerpc/kvm/booke.c
> @@ -380,6 +406,9 @@ int kvmppc_handle_exit(struct kvm_run=20
> *run, struct kvm_vcpu *vcpu,
> gpa_t gpaddr;
> gfn_t gfn;
> =20
> + if (kvmppc_dtlb_magic_page(vcpu, eaddr))
> + break;
> +
> /* Check the guest TLB. */
> gtlb_index =3D kvmppc_mmu_dtlb_index(vcpu, eaddr);
> if (gtlb_index < 0) {
How about moving this part into tlb search fail path?
^ permalink raw reply
* Re: [rtc-linux] [PATCH v2] rtc: add support for DS3232 RTC
From: Tiefei zang @ 2010-07-12 9:05 UTC (permalink / raw)
To: rtc-linux; +Cc: linuxppc-dev, Andrew Morton, B11780, Mingkai.hu
In-Reply-To: <AANLkTik6T0GCXwID86fP0DR5GHwrb6F73korUTIItx2M@mail.gmail.com>
On Mon, Jul 12, 2010 at 4:30 PM, Wan ZongShun <mcuos.com@gmail.com> wrote:
> 2010/7/12 Tiefei zang <tiefei.zang@gmail.com>:
>> On Mon, Jul 12, 2010 at 3:08 PM, Wan ZongShun <mcuos.com@gmail.com> wrote:
>>> 2010/7/12 Roy Zang <tie-fei.zang@freescale.com>:
>>>> This patch adds the driver for RTC chip DS3232 via I2C bus.
>>>>
>>>
>>> I noted this patch is the second version, maybe you can describe which
>>> modifications you have done here.
>>> and add [PATCH v2] to mail subject.
>> I do not think the modification comparing previous version should go
>> into the commit message.
>> I buy in your comments in original version patch and update:
>> 1. add rtc_valid_tm to check return value.
>> 2. remove ioctl function, which do not used and tested.
>> 3. add __devinit for ds3232 probe
>> 4. put request irq after driver registration.
>>
>
> Okay.
> Which git tree do you want to merge your patch?
I can see you add Andrew in the mail loop.
I think Andrew's test tree should be OK for this patch.
I just updated the patch. So if there is no problem, please pick up the v3:
http://patchwork.ozlabs.org/patch/58584/
Thanks.
Roy
^ permalink raw reply
* Re: [rtc-linux] [PATCH v2] rtc: add support for DS3232 RTC
From: Tiefei zang @ 2010-07-12 8:41 UTC (permalink / raw)
To: rtc-linux; +Cc: linuxppc-dev, Andrew Morton, B11780, Mingkai.hu
In-Reply-To: <AANLkTinBY5Gc_PGE-bsbkCHYB0pEApKm0-N-GEljQTNJ@mail.gmail.com>
On Mon, Jul 12, 2010 at 4:37 PM, Wan ZongShun <mcuos.com@gmail.com> wrote:
> 2010/7/12 Tiefei zang <tiefei.zang@gmail.com>:
>> On Mon, Jul 12, 2010 at 3:08 PM, Wan ZongShun <mcuos.com@gmail.com> wrot=
e:
>>> 2010/7/12 Roy Zang <tie-fei.zang@freescale.com>:
>>>> This patch adds the driver for RTC chip DS3232 via I2C bus.
>>>>
>>>
>>> I noted this patch is the second version, maybe you can describe which
>>> modifications you have done here.
>>> and add [PATCH v2] to mail subject.
>> I do not think the modification comparing previous version should go
>> into the commit message.
>> I buy in your comments in original version patch and update:
>> 1. add rtc_valid_tm to check return value.
>> 2. remove ioctl function, which do not used and tested.
>> 3. add __devinit for ds3232 probe
>> 4. put request irq after driver registration.
>>
>>>
>>>> Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
>>>> Signed-off-by: Jingchang Lu <b22599@freescale.com>
>>>> Signed-off-by: Srikanth Srinivasan <srikanth.srinivasan@freescale.com>
>>>> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
>>>> ---
>>>> Tested on MPC8536DS and P4080DS board
>>>>
>>>> =A0drivers/rtc/Kconfig =A0 =A0 =A0| =A0 11 ++
>>>> =A0drivers/rtc/Makefile =A0 =A0 | =A0 =A01 +
>>>> =A0drivers/rtc/rtc-ds3232.c | =A0427 +++++++++++++++++++++++++++++++++=
+++++++++++++
>>>> =A03 files changed, 439 insertions(+), 0 deletions(-)
>>>> =A0create mode 100644 drivers/rtc/rtc-ds3232.c
>>>>
>>>> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
>>>> index 6a13037..13c2fdb 100644
>>>> --- a/drivers/rtc/Kconfig
>>>> +++ b/drivers/rtc/Kconfig
>>>> @@ -166,6 +166,17 @@ config RTC_DRV_DS1672
>>>> =A0 =A0 =A0 =A0 =A0This driver can also be built as a module. If so, t=
he module
>>>> =A0 =A0 =A0 =A0 =A0will be called rtc-ds1672.
>>>>
>>>> +config RTC_DRV_DS3232
>>>> + =A0 =A0 =A0 tristate "Dallas/Maxim DS3232"
>>>> + =A0 =A0 =A0 depends on RTC_CLASS && I2C
>>>> + =A0 =A0 =A0 help
>>>> + =A0 =A0 =A0 =A0 If you say yes here you get support for Dallas Semic=
onductor
>>>> + =A0 =A0 =A0 =A0 DS3232 real-time clock chips. =A0If an interrupt is =
associated
>>>> + =A0 =A0 =A0 =A0 with the device, the alarm functionality is supporte=
d.
>>>> +
>>>> + =A0 =A0 =A0 =A0 This driver can also be built as a module. =A0If so,=
the module
>>>> + =A0 =A0 =A0 =A0 will be called rtc-ds3232.
>>>> +
>>>> =A0config RTC_DRV_MAX6900
>>>> =A0 =A0 =A0 =A0tristate "Maxim MAX6900"
>>>> =A0 =A0 =A0 =A0help
>>>> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
>>>> index 44ef194..0af190c 100644
>>>> --- a/drivers/rtc/Makefile
>>>> +++ b/drivers/rtc/Makefile
>>>> @@ -39,6 +39,7 @@ obj-$(CONFIG_RTC_DRV_DS1511) =A0+=3D rtc-ds1511.o
>>>> =A0obj-$(CONFIG_RTC_DRV_DS1553) =A0 +=3D rtc-ds1553.o
>>>> =A0obj-$(CONFIG_RTC_DRV_DS1672) =A0 +=3D rtc-ds1672.o
>>>> =A0obj-$(CONFIG_RTC_DRV_DS1742) =A0 +=3D rtc-ds1742.o
>>>> +obj-$(CONFIG_RTC_DRV_DS3232) =A0 +=3D rtc-ds3232.o
>>>> =A0obj-$(CONFIG_RTC_DRV_DS3234) =A0 +=3D rtc-ds3234.o
>>>> =A0obj-$(CONFIG_RTC_DRV_EFI) =A0 =A0 =A0+=3D rtc-efi.o
>>>> =A0obj-$(CONFIG_RTC_DRV_EP93XX) =A0 +=3D rtc-ep93xx.o
>>>> diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
>>>> new file mode 100644
>>>> index 0000000..e36ec1c
>>>> --- /dev/null
>>>> +++ b/drivers/rtc/rtc-ds3232.c
>>>> @@ -0,0 +1,427 @@
>>>> +/*
>>>> + * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over=
I2C
>>>> + *
>>>> + * Copyright (C) 2009-2010 Freescale Semiconductor.
>>>> + *
>>>> + * This program is free software; you can redistribute =A0it and/or m=
odify it
>>>> + * under =A0the terms of =A0the GNU General =A0Public License as publ=
ished by the
>>>> + * Free Software Foundation; =A0either version 2 of the =A0License, o=
r (at your
>>>> + * option) any later version.
>>>> + */
>>>> +/*
>>>> + * It would be more efficient to use i2c msgs/i2c_transfer directly b=
ut, as
>>>> + * recommened in .../Documentation/i2c/writing-clients section
>>>> + * "Sending and receiving", using SMBus level communication is prefer=
red.
>>>> + */
>>>> +
>>>> +#include <linux/kernel.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/interrupt.h>
>>>> +#include <linux/i2c.h>
>>>> +#include <linux/rtc.h>
>>>> +#include <linux/bcd.h>
>>>> +#include <linux/workqueue.h>
>>>> +#include <linux/slab.h>
>>>> +
>>>> +#define DS3232_REG_SECONDS =A0 =A0 0x00
>>>> +#define DS3232_REG_MINUTES =A0 =A0 0x01
>>>> +#define DS3232_REG_HOURS =A0 =A0 =A0 0x02
>>>> +#define DS3232_REG_AMPM =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A00x02
>>>> +#define DS3232_REG_DAY =A0 =A0 =A0 =A0 0x03
>>>> +#define DS3232_REG_DATE =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A00x04
>>>> +#define DS3232_REG_MONTH =A0 =A0 =A0 0x05
>>>> +#define DS3232_REG_CENTURY =A0 =A0 0x05
>>>> +#define DS3232_REG_YEAR =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A00x06
>>>> +#define DS3232_REG_ALARM1 =A0 =A0 =A0 =A0 0x07 /* Alarm 1 BASE */
>>>> +#define DS3232_REG_ALARM2 =A0 =A0 =A0 =A0 0x0B /* Alarm 2 BASE */
>>>> +#define DS3232_REG_CR =A0 =A0 =A0 =A0 =A00x0E =A0 =A0/* Control regis=
ter */
>>>> +# =A0 =A0 =A0define DS3232_REG_CR_nEOSC =A0 =A0 =A0 =A00x80
>>>> +# =A0 =A0 =A0 define DS3232_REG_CR_INTCN =A0 =A0 =A0 =A00x04
>>>> +# =A0 =A0 =A0 define DS3232_REG_CR_A2IE =A0 =A0 =A0 =A00x02
>>>> +# =A0 =A0 =A0 define DS3232_REG_CR_A1IE =A0 =A0 =A0 =A00x01
>>>> +
>>>> +#define DS3232_REG_SR =A00x0F =A0 =A0/* control/status register */
>>>> +# =A0 =A0 =A0define DS3232_REG_SR_OSF =A0 0x80
>>>> +# =A0 =A0 =A0 define DS3232_REG_SR_BSY =A0 0x04
>>>> +# =A0 =A0 =A0 define DS3232_REG_SR_A2F =A0 0x02
>>>> +# =A0 =A0 =A0 define DS3232_REG_SR_A1F =A0 0x01
>>>> +
>>>> +struct ds3232 {
>>>> + =A0 =A0 =A0 struct i2c_client *client;
>>>> + =A0 =A0 =A0 struct rtc_device *rtc;
>>>> + =A0 =A0 =A0 struct work_struct work;
>>>> +
>>>> + =A0 =A0 =A0 /* The mutex protects alarm operations, and prevents a r=
ace
>>>> + =A0 =A0 =A0 =A0* between the enable_irq() in the workqueue and the f=
ree_irq()
>>>> + =A0 =A0 =A0 =A0* in the remove function.
>>>> + =A0 =A0 =A0 =A0*/
>>>> + =A0 =A0 =A0 struct mutex mutex;
>>>> + =A0 =A0 =A0 int exiting;
>>>> +};
>>>> +
>>>> +static struct i2c_driver ds3232_driver;
>>>> +
>>>> +static int ds3232_check_rtc_status(struct i2c_client *client)
>>>> +{
>>>> + =A0 =A0 =A0 int ret =3D 0;
>>>> + =A0 =A0 =A0 int control, stat;
>>>> +
>>>> + =A0 =A0 =A0 stat =3D i2c_smbus_read_byte_data(client, DS3232_REG_SR)=
;
>>>> + =A0 =A0 =A0 if (stat < 0)
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return stat;
>>>> +
>>>> + =A0 =A0 =A0 if (stat & DS3232_REG_SR_OSF)
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_warn(&client->dev,
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "oscilla=
tor discontinuity flagged, "
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "time un=
reliable\n");
>>>> +
>>>> + =A0 =A0 =A0 stat &=3D ~(DS3232_REG_SR_OSF | DS3232_REG_SR_A1F | DS32=
32_REG_SR_A2F);
>>>> +
>>>> + =A0 =A0 =A0 ret =3D i2c_smbus_write_byte_data(client, DS3232_REG_SR,=
stat);
>>>> + =A0 =A0 =A0 if (ret < 0)
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return ret;
>>>> +
>>>> + =A0 =A0 =A0 /* If the alarm is pending, clear it before requesting
>>>> + =A0 =A0 =A0 =A0* the interrupt, so an interrupt event isn't reported
>>>> + =A0 =A0 =A0 =A0* before everything is initialized.
>>>> + =A0 =A0 =A0 =A0*/
>>>> +
>>>> + =A0 =A0 =A0 control =3D i2c_smbus_read_byte_data(client, DS3232_REG_=
CR);
>>>> + =A0 =A0 =A0 if (control < 0)
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return control;
>>>> +
>>>> + =A0 =A0 =A0 control &=3D ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE);
>>>> + =A0 =A0 =A0 control |=3D DS3232_REG_CR_INTCN;
>>>> +
>>>> + =A0 =A0 =A0 return i2c_smbus_write_byte_data(client, DS3232_REG_CR, =
control);
>>>> +}
>>>> +
>>>> +static int ds3232_read_time(struct device *dev, struct rtc_time *time=
)
>>>> +{
>>>> + =A0 =A0 =A0 struct i2c_client *client =3D to_i2c_client(dev);
>>>> + =A0 =A0 =A0 int ret;
>>>> + =A0 =A0 =A0 u8 buf[7];
>>>> + =A0 =A0 =A0 unsigned int year, month, day, hour, minute, second;
>>>> + =A0 =A0 =A0 unsigned int week, twelve_hr, am_pm;
>>>> + =A0 =A0 =A0 unsigned int century, add_century =3D 0;
>>>> +
>>>> + =A0 =A0 =A0 ret =3D i2c_smbus_read_i2c_block_data(client, DS3232_REG=
_SECONDS, 7, buf);
>>>> +
>>>> + =A0 =A0 =A0 if (ret < 0)
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return ret;
>>>> + =A0 =A0 =A0 if (ret < 7)
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EIO;
>>>> +
>>>> + =A0 =A0 =A0 second =3D buf[0];
>>>> + =A0 =A0 =A0 minute =3D buf[1];
>>>> + =A0 =A0 =A0 hour =3D buf[2];
>>>> + =A0 =A0 =A0 week =3D buf[3];
>>>> + =A0 =A0 =A0 day =3D buf[4];
>>>> + =A0 =A0 =A0 month =3D buf[5];
>>>> + =A0 =A0 =A0 year =3D buf[6];
>>>> +
>>>> + =A0 =A0 =A0 /* Extract additional information for AM/PM and century =
*/
>>>> +
>>>> + =A0 =A0 =A0 twelve_hr =3D hour & 0x40;
>>>> + =A0 =A0 =A0 am_pm =3D hour & 0x20;
>>>> + =A0 =A0 =A0 century =3D month & 0x80;
>>>> +
>>>> + =A0 =A0 =A0 /* Write to rtc_time structure */
>>>> +
>>>> + =A0 =A0 =A0 time->tm_sec =3D bcd2bin(second);
>>>> + =A0 =A0 =A0 time->tm_min =3D bcd2bin(minute);
>>>> + =A0 =A0 =A0 if (twelve_hr) {
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Convert to 24 hr */
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (am_pm)
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 time->tm_hour =3D bcd2bi=
n(hour & 0x1F) + 12;
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 time->tm_hour =3D bcd2bi=
n(hour & 0x1F);
>>>> + =A0 =A0 =A0 } else {
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 time->tm_hour =3D bcd2bin(hour);
>>>> + =A0 =A0 =A0 }
>>>> +
>>>> + =A0 =A0 =A0 time->tm_wday =3D bcd2bin(week);
>>>> + =A0 =A0 =A0 time->tm_mday =3D bcd2bin(day);
>>>> + =A0 =A0 =A0 time->tm_mon =3D bcd2bin(month & 0x7F);
>>>> + =A0 =A0 =A0 if (century)
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 add_century =3D 100;
>>>> +
>>>> + =A0 =A0 =A0 time->tm_year =3D bcd2bin(year) + add_century;
>>>> +
>>>> + =A0 =A0 =A0 return rtc_valid_tm(time);
>>>> +}
>>>> +
>>>> +static int ds3232_set_time(struct device *dev, struct rtc_time *time)
>>>> +{
>>>> + =A0 =A0 =A0 struct i2c_client *client =3D to_i2c_client(dev);
>>>> + =A0 =A0 =A0 u8 buf[7];
>>>> +
>>>> + =A0 =A0 =A0 /* Extract time from rtc_time and load into ds3232*/
>>>> +
>>>> + =A0 =A0 =A0 buf[0] =3D bin2bcd(time->tm_sec);
>>>> + =A0 =A0 =A0 buf[1] =3D bin2bcd(time->tm_min);
>>>> + =A0 =A0 =A0 buf[2] =3D bin2bcd(time->tm_hour);
>>>> + =A0 =A0 =A0 buf[3] =3D bin2bcd(time->tm_wday); /* Day of the week */
>>>> + =A0 =A0 =A0 buf[4] =3D bin2bcd(time->tm_mday); /* Date */
>>>> + =A0 =A0 =A0 buf[5] =3D bin2bcd(time->tm_mon);
>>>> + =A0 =A0 =A0 if (time->tm_year >=3D 100) {
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 buf[5] |=3D 0x80;
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 buf[6] =3D bin2bcd(time->tm_year - 100);
>>>> + =A0 =A0 =A0 } else {
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 buf[6] =3D bin2bcd(time->tm_year);
>>>> + =A0 =A0 =A0 }
>>>> +
>>>> + =A0 =A0 =A0 return i2c_smbus_write_i2c_block_data(client,
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0 DS3232_REG_SECONDS, 7, buf);
>>>> +}
>>>> +
>>>> +/*
>>>> + * DS3232 has two alarm, we only use alarm1
>>>> + * According to linux specification, only support one-shot alarm
>>>> + * no periodic alarm mode
>>>> + */
>>>> +static int ds3232_read_alarm(struct device *dev, struct rtc_wkalrm *a=
larm)
>>>> +{
>>>> + =A0 =A0 =A0 struct i2c_client *client =3D to_i2c_client(dev);
>>>> + =A0 =A0 =A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(client);
>>>> + =A0 =A0 =A0 int control, stat;
>>>> + =A0 =A0 =A0 int ret =3D 0;
>>>> + =A0 =A0 =A0 u8 buf[4];
>>>> +
>>>> + =A0 =A0 =A0 mutex_lock(&ds3232->mutex);
>>>> + =A0 =A0 =A0 stat =3D ret =3D i2c_smbus_read_byte_data(client, DS3232=
_REG_SR);
>>>> + =A0 =A0 =A0 if (stat < 0)
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
>>>> +
>>>> + =A0 =A0 =A0 control =3D ret =3D i2c_smbus_read_byte_data(client, DS3=
232_REG_CR);
>>>> + =A0 =A0 =A0 if (control < 0)
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
>>>> +
>>>> + =A0 =A0 =A0 ret =3D i2c_smbus_read_i2c_block_data(client, DS3232_REG=
_ALARM1, 4, buf);
>>>> + =A0 =A0 =A0 if (ret < 0)
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
>>>> +
>>>> + =A0 =A0 =A0 alarm->time.tm_sec =3D bcd2bin(buf[0] & 0x7F);
>>>> + =A0 =A0 =A0 alarm->time.tm_min =3D bcd2bin(buf[1] & 0x7F);
>>>> + =A0 =A0 =A0 alarm->time.tm_hour =3D bcd2bin(buf[2] & 0x7F);
>>>> + =A0 =A0 =A0 alarm->time.tm_mday =3D bcd2bin(buf[3] & 0x7F);
>>>> +
>>>> + =A0 =A0 =A0 alarm->time.tm_mon =3D -1;
>>>> + =A0 =A0 =A0 alarm->time.tm_year =3D -1;
>>>> + =A0 =A0 =A0 alarm->time.tm_wday =3D -1;
>>>> + =A0 =A0 =A0 alarm->time.tm_yday =3D -1;
>>>> + =A0 =A0 =A0 alarm->time.tm_isdst =3D -1;
>>>> +
>>>> + =A0 =A0 =A0 alarm->enabled =3D !!(control & DS3232_REG_CR_A1IE);
>>>> + =A0 =A0 =A0 alarm->pending =3D !!(stat & DS3232_REG_SR_A1F);
>>>> +out:
>>>> + =A0 =A0 =A0 mutex_unlock(&ds3232->mutex);
>>>> +
>>>> + =A0 =A0 =A0 return ret;
>>>> +}
>>>> +
>>>> +/*
>>>> + * linux rtc-module does not support wday alarm
>>>> + * and only 24h time mode supported indeed
>>>> + */
>>>> +static int ds3232_set_alarm(struct device *dev, struct rtc_wkalrm *al=
arm)
>>>> +{
>>>> + =A0 =A0 =A0 struct i2c_client *client =3D to_i2c_client(dev);
>>>> + =A0 =A0 =A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(client);
>>>> + =A0 =A0 =A0 int control, stat;
>>>> + =A0 =A0 =A0 int ret =3D 0;
>>>> + =A0 =A0 =A0 u8 buf[4];
>>>> +
>>>> + =A0 =A0 =A0 if (client->irq <=3D 0)
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
>>>> +
>>>> + =A0 =A0 =A0 mutex_lock(&ds3232->mutex);
>>>> +
>>>> + =A0 =A0 =A0 buf[0] =3D bin2bcd(alarm->time.tm_sec);
>>>> + =A0 =A0 =A0 buf[1] =3D bin2bcd(alarm->time.tm_min);
>>>> + =A0 =A0 =A0 buf[2] =3D bin2bcd(alarm->time.tm_hour);
>>>> + =A0 =A0 =A0 buf[3] =3D bin2bcd(alarm->time.tm_mday);
>>>> +
>>>> + =A0 =A0 =A0 /* clear alarm interrupt enable bit */
>>>> + =A0 =A0 =A0 ret =3D control =3D i2c_smbus_read_byte_data(client, DS3=
232_REG_CR);
>>>> + =A0 =A0 =A0 if (ret < 0)
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
>>>> + =A0 =A0 =A0 control &=3D ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE);
>>>> + =A0 =A0 =A0 ret =3D i2c_smbus_write_byte_data(client, DS3232_REG_CR,=
control);
>>>> + =A0 =A0 =A0 if (ret < 0)
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
>>>> +
>>>> + =A0 =A0 =A0 /* clear any pending alarm flag */
>>>> + =A0 =A0 =A0 stat =3D i2c_smbus_read_byte_data(client, DS3232_REG_SR)=
;
>>>> + =A0 =A0 =A0 if (stat < 0)
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return stat;
>>>> +
>>>> + =A0 =A0 =A0 stat &=3D ~(DS3232_REG_SR_A1F | DS3232_REG_SR_A2F);
>>>> +
>>>> + =A0 =A0 =A0 ret =3D i2c_smbus_write_byte_data(client, DS3232_REG_SR,=
stat);
>>>> + =A0 =A0 =A0 if (ret < 0)
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return ret;
>>>> +
>>>> + =A0 =A0 =A0 ret =3D i2c_smbus_write_i2c_block_data(client,
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0 =A0 DS3232_REG_ALARM1, 4, buf);
>>>> +
>>>> + =A0 =A0 =A0 if (alarm->enabled) {
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 control |=3D DS3232_REG_CR_A1IE;
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D i2c_smbus_write_byte_data(client=
, DS3232_REG_CR, control);
>>>> + =A0 =A0 =A0 }
>>>> +out:
>>>> + =A0 =A0 =A0 mutex_unlock(&ds3232->mutex);
>>>> + =A0 =A0 =A0 return ret;
>>>> +}
>>>> +
>>>> +static irqreturn_t ds3232_irq(int irq, void *dev_id)
>>>> +{
>>>> + =A0 =A0 =A0 struct i2c_client *client =3D dev_id;
>>>> + =A0 =A0 =A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(client);
>>>> +
>>>> + =A0 =A0 =A0 disable_irq_nosync(irq);
>>>> + =A0 =A0 =A0 schedule_work(&ds3232->work);
>>>> + =A0 =A0 =A0 return IRQ_HANDLED;
>>>> +}
>>>> +
>>>> +static void ds3232_work(struct work_struct *work)
>>>> +{
>>>> + =A0 =A0 =A0 struct ds3232 *ds3232 =3D container_of(work, struct ds32=
32, work);
>>>> + =A0 =A0 =A0 struct i2c_client *client =3D ds3232->client;
>>>> + =A0 =A0 =A0 int stat, control;
>>>> +
>>>> + =A0 =A0 =A0 mutex_lock(&ds3232->mutex);
>>>> +
>>>> + =A0 =A0 =A0 stat =3D i2c_smbus_read_byte_data(client, DS3232_REG_SR)=
;
>>>> + =A0 =A0 =A0 if (stat < 0)
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto unlock;
>>>> +
>>>> + =A0 =A0 =A0 if (stat & DS3232_REG_SR_A1F) {
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 control =3D i2c_smbus_read_byte_data(cli=
ent, DS3232_REG_CR);
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (control < 0)
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* disable alarm1 interrupt */
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 control &=3D ~(DS3232_REG_CR_A1IE);
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 i2c_smbus_write_byte_data(client, DS3232=
_REG_CR, control);
>>>> +
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* clear the alarm pend flag */
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 stat &=3D ~DS3232_REG_SR_A1F;
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 i2c_smbus_write_byte_data(client, DS3232=
_REG_SR, stat);
>>>> +
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rtc_update_irq(ds3232->rtc, 1, RTC_AF | =
RTC_IRQF);
>>>> + =A0 =A0 =A0 }
>>>> +
>>>> +out:
>>>> + =A0 =A0 =A0 if (!ds3232->exiting)
>>>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 enable_irq(client->irq);
>>>> +unlock:
>>>> + =A0 =A0 =A0 mutex_unlock(&ds3232->mutex);
>>>> +}
>>>> +
>>>> +static const struct rtc_class_ops ds3232_rtc_ops =3D {
>>>> + =A0 =A0 =A0 .read_time =3D ds3232_read_time,
>>>> + =A0 =A0 =A0 .set_time =3D ds3232_set_time,
>>>> + =A0 =A0 =A0 .read_alarm =3D ds3232_read_alarm,
>>>> + =A0 =A0 =A0 .set_alarm =3D ds3232_set_alarm,
>>>> +};
>>>
>>> I sew that you have discarded the .ioctl function, which is used to
>>> enable/disable the alarm irq in previous driver patch.
>>> but,at the same time, you didnot implement .alarm_irq_enable function
>>> , so is there no need to enable or disable the alarm irq bit in
>>> current version patch?
>> Right. This is not needed in current patch. Alarm is not used and tested=
.
>> Thanks.
>
> Sorry for forgetting to comment this,
> If the alarm is not used, please remove all the alarm parts, should
> not we make sure all drivers submitted to linux mainline
> can work good?
>
> When you want to use the alarm, you can send one patch to add it again.
This makes sense to me. I will send a new patch.
Roy
^ permalink raw reply
* Re: [rtc-linux] [PATCH v2] rtc: add support for DS3232 RTC
From: Wan ZongShun @ 2010-07-12 8:37 UTC (permalink / raw)
To: rtc-linux, Andrew Morton; +Cc: Mingkai.hu, linuxppc-dev, B11780
In-Reply-To: <AANLkTikv5coqQM4duVmexbOM6iIh-IG-wiCqSqjPHzV3@mail.gmail.com>
2010/7/12 Tiefei zang <tiefei.zang@gmail.com>:
> On Mon, Jul 12, 2010 at 3:08 PM, Wan ZongShun <mcuos.com@gmail.com> wrote=
:
>> 2010/7/12 Roy Zang <tie-fei.zang@freescale.com>:
>>> This patch adds the driver for RTC chip DS3232 via I2C bus.
>>>
>>
>> I noted this patch is the second version, maybe you can describe which
>> modifications you have done here.
>> and add [PATCH v2] to mail subject.
> I do not think the modification comparing previous version should go
> into the commit message.
> I buy in your comments in original version patch and update:
> 1. add rtc_valid_tm to check return value.
> 2. remove ioctl function, which do not used and tested.
> 3. add __devinit for ds3232 probe
> 4. put request irq after driver registration.
>
>>
>>> Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
>>> Signed-off-by: Jingchang Lu <b22599@freescale.com>
>>> Signed-off-by: Srikanth Srinivasan <srikanth.srinivasan@freescale.com>
>>> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
>>> ---
>>> Tested on MPC8536DS and P4080DS board
>>>
>>> =C2=A0drivers/rtc/Kconfig =C2=A0 =C2=A0 =C2=A0| =C2=A0 11 ++
>>> =C2=A0drivers/rtc/Makefile =C2=A0 =C2=A0 | =C2=A0 =C2=A01 +
>>> =C2=A0drivers/rtc/rtc-ds3232.c | =C2=A0427 ++++++++++++++++++++++++++++=
++++++++++++++++++
>>> =C2=A03 files changed, 439 insertions(+), 0 deletions(-)
>>> =C2=A0create mode 100644 drivers/rtc/rtc-ds3232.c
>>>
>>> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
>>> index 6a13037..13c2fdb 100644
>>> --- a/drivers/rtc/Kconfig
>>> +++ b/drivers/rtc/Kconfig
>>> @@ -166,6 +166,17 @@ config RTC_DRV_DS1672
>>> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0This driver can also be built as a mo=
dule. If so, the module
>>> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0will be called rtc-ds1672.
>>>
>>> +config RTC_DRV_DS3232
>>> + =C2=A0 =C2=A0 =C2=A0 tristate "Dallas/Maxim DS3232"
>>> + =C2=A0 =C2=A0 =C2=A0 depends on RTC_CLASS && I2C
>>> + =C2=A0 =C2=A0 =C2=A0 help
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 If you say yes here you get support for D=
allas Semiconductor
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 DS3232 real-time clock chips. =C2=A0If an=
interrupt is associated
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 with the device, the alarm functionality =
is supported.
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 This driver can also be built as a module=
. =C2=A0If so, the module
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 will be called rtc-ds3232.
>>> +
>>> =C2=A0config RTC_DRV_MAX6900
>>> =C2=A0 =C2=A0 =C2=A0 =C2=A0tristate "Maxim MAX6900"
>>> =C2=A0 =C2=A0 =C2=A0 =C2=A0help
>>> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
>>> index 44ef194..0af190c 100644
>>> --- a/drivers/rtc/Makefile
>>> +++ b/drivers/rtc/Makefile
>>> @@ -39,6 +39,7 @@ obj-$(CONFIG_RTC_DRV_DS1511) =C2=A0+=3D rtc-ds1511.o
>>> =C2=A0obj-$(CONFIG_RTC_DRV_DS1553) =C2=A0 +=3D rtc-ds1553.o
>>> =C2=A0obj-$(CONFIG_RTC_DRV_DS1672) =C2=A0 +=3D rtc-ds1672.o
>>> =C2=A0obj-$(CONFIG_RTC_DRV_DS1742) =C2=A0 +=3D rtc-ds1742.o
>>> +obj-$(CONFIG_RTC_DRV_DS3232) =C2=A0 +=3D rtc-ds3232.o
>>> =C2=A0obj-$(CONFIG_RTC_DRV_DS3234) =C2=A0 +=3D rtc-ds3234.o
>>> =C2=A0obj-$(CONFIG_RTC_DRV_EFI) =C2=A0 =C2=A0 =C2=A0+=3D rtc-efi.o
>>> =C2=A0obj-$(CONFIG_RTC_DRV_EP93XX) =C2=A0 +=3D rtc-ep93xx.o
>>> diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
>>> new file mode 100644
>>> index 0000000..e36ec1c
>>> --- /dev/null
>>> +++ b/drivers/rtc/rtc-ds3232.c
>>> @@ -0,0 +1,427 @@
>>> +/*
>>> + * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over =
I2C
>>> + *
>>> + * Copyright (C) 2009-2010 Freescale Semiconductor.
>>> + *
>>> + * This program is free software; you can redistribute =C2=A0it and/or=
modify it
>>> + * under =C2=A0the terms of =C2=A0the GNU General =C2=A0Public License=
as published by the
>>> + * Free Software Foundation; =C2=A0either version 2 of the =C2=A0Licen=
se, or (at your
>>> + * option) any later version.
>>> + */
>>> +/*
>>> + * It would be more efficient to use i2c msgs/i2c_transfer directly bu=
t, as
>>> + * recommened in .../Documentation/i2c/writing-clients section
>>> + * "Sending and receiving", using SMBus level communication is preferr=
ed.
>>> + */
>>> +
>>> +#include <linux/kernel.h>
>>> +#include <linux/module.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/i2c.h>
>>> +#include <linux/rtc.h>
>>> +#include <linux/bcd.h>
>>> +#include <linux/workqueue.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#define DS3232_REG_SECONDS =C2=A0 =C2=A0 0x00
>>> +#define DS3232_REG_MINUTES =C2=A0 =C2=A0 0x01
>>> +#define DS3232_REG_HOURS =C2=A0 =C2=A0 =C2=A0 0x02
>>> +#define DS3232_REG_AMPM =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A00x02
>>> +#define DS3232_REG_DAY =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x03
>>> +#define DS3232_REG_DATE =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A00x04
>>> +#define DS3232_REG_MONTH =C2=A0 =C2=A0 =C2=A0 0x05
>>> +#define DS3232_REG_CENTURY =C2=A0 =C2=A0 0x05
>>> +#define DS3232_REG_YEAR =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A00x06
>>> +#define DS3232_REG_ALARM1 =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x07 /* Alarm 1 =
BASE */
>>> +#define DS3232_REG_ALARM2 =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x0B /* Alarm 2 =
BASE */
>>> +#define DS3232_REG_CR =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x0E =C2=A0 =
=C2=A0/* Control register */
>>> +# =C2=A0 =C2=A0 =C2=A0define DS3232_REG_CR_nEOSC =C2=A0 =C2=A0 =C2=A0 =
=C2=A00x80
>>> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_CR_INTCN =C2=A0 =C2=A0 =C2=A0=
=C2=A00x04
>>> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_CR_A2IE =C2=A0 =C2=A0 =C2=A0 =
=C2=A00x02
>>> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_CR_A1IE =C2=A0 =C2=A0 =C2=A0 =
=C2=A00x01
>>> +
>>> +#define DS3232_REG_SR =C2=A00x0F =C2=A0 =C2=A0/* control/status regist=
er */
>>> +# =C2=A0 =C2=A0 =C2=A0define DS3232_REG_SR_OSF =C2=A0 0x80
>>> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_SR_BSY =C2=A0 0x04
>>> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_SR_A2F =C2=A0 0x02
>>> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_SR_A1F =C2=A0 0x01
>>> +
>>> +struct ds3232 {
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client;
>>> + =C2=A0 =C2=A0 =C2=A0 struct rtc_device *rtc;
>>> + =C2=A0 =C2=A0 =C2=A0 struct work_struct work;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* The mutex protects alarm operations, and prev=
ents a race
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* between the enable_irq() in the workqueu=
e and the free_irq()
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* in the remove function.
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
>>> + =C2=A0 =C2=A0 =C2=A0 struct mutex mutex;
>>> + =C2=A0 =C2=A0 =C2=A0 int exiting;
>>> +};
>>> +
>>> +static struct i2c_driver ds3232_driver;
>>> +
>>> +static int ds3232_check_rtc_status(struct i2c_client *client)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 int ret =3D 0;
>>> + =C2=A0 =C2=A0 =C2=A0 int control, stat;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 stat =3D i2c_smbus_read_byte_data(client, DS3232=
_REG_SR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (stat < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return stat;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 if (stat & DS3232_REG_SR_OSF)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_warn(&client->de=
v,
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "oscillator discontinuity flagged, "
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "time unreliable\n");
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 stat &=3D ~(DS3232_REG_SR_OSF | DS3232_REG_SR_A1=
F | DS3232_REG_SR_A2F);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_write_byte_data(client, DS3232=
_REG_SR, stat);
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* If the alarm is pending, clear it before requ=
esting
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* the interrupt, so an interrupt event isn=
't reported
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* before everything is initialized.
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 control =3D i2c_smbus_read_byte_data(client, DS3=
232_REG_CR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (control < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return control;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 control &=3D ~(DS3232_REG_CR_A1IE | DS3232_REG_C=
R_A2IE);
>>> + =C2=A0 =C2=A0 =C2=A0 control |=3D DS3232_REG_CR_INTCN;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 return i2c_smbus_write_byte_data(client, DS3232_=
REG_CR, control);
>>> +}
>>> +
>>> +static int ds3232_read_time(struct device *dev, struct rtc_time *time)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D to_i2c_client(dev)=
;
>>> + =C2=A0 =C2=A0 =C2=A0 int ret;
>>> + =C2=A0 =C2=A0 =C2=A0 u8 buf[7];
>>> + =C2=A0 =C2=A0 =C2=A0 unsigned int year, month, day, hour, minute, sec=
ond;
>>> + =C2=A0 =C2=A0 =C2=A0 unsigned int week, twelve_hr, am_pm;
>>> + =C2=A0 =C2=A0 =C2=A0 unsigned int century, add_century =3D 0;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_read_i2c_block_data(client, DS=
3232_REG_SECONDS, 7, buf);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret;
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 7)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -EIO;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 second =3D buf[0];
>>> + =C2=A0 =C2=A0 =C2=A0 minute =3D buf[1];
>>> + =C2=A0 =C2=A0 =C2=A0 hour =3D buf[2];
>>> + =C2=A0 =C2=A0 =C2=A0 week =3D buf[3];
>>> + =C2=A0 =C2=A0 =C2=A0 day =3D buf[4];
>>> + =C2=A0 =C2=A0 =C2=A0 month =3D buf[5];
>>> + =C2=A0 =C2=A0 =C2=A0 year =3D buf[6];
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* Extract additional information for AM/PM and =
century */
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 twelve_hr =3D hour & 0x40;
>>> + =C2=A0 =C2=A0 =C2=A0 am_pm =3D hour & 0x20;
>>> + =C2=A0 =C2=A0 =C2=A0 century =3D month & 0x80;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* Write to rtc_time structure */
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 time->tm_sec =3D bcd2bin(second);
>>> + =C2=A0 =C2=A0 =C2=A0 time->tm_min =3D bcd2bin(minute);
>>> + =C2=A0 =C2=A0 =C2=A0 if (twelve_hr) {
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Convert to 24 hr =
*/
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (am_pm)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 time->tm_hour =3D bcd2bin(hour & 0x1F) + 12;
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 else
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 time->tm_hour =3D bcd2bin(hour & 0x1F);
>>> + =C2=A0 =C2=A0 =C2=A0 } else {
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 time->tm_hour =3D bc=
d2bin(hour);
>>> + =C2=A0 =C2=A0 =C2=A0 }
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 time->tm_wday =3D bcd2bin(week);
>>> + =C2=A0 =C2=A0 =C2=A0 time->tm_mday =3D bcd2bin(day);
>>> + =C2=A0 =C2=A0 =C2=A0 time->tm_mon =3D bcd2bin(month & 0x7F);
>>> + =C2=A0 =C2=A0 =C2=A0 if (century)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 add_century =3D 100;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 time->tm_year =3D bcd2bin(year) + add_century;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 return rtc_valid_tm(time);
>>> +}
>>> +
>>> +static int ds3232_set_time(struct device *dev, struct rtc_time *time)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D to_i2c_client(dev)=
;
>>> + =C2=A0 =C2=A0 =C2=A0 u8 buf[7];
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* Extract time from rtc_time and load into ds32=
32*/
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 buf[0] =3D bin2bcd(time->tm_sec);
>>> + =C2=A0 =C2=A0 =C2=A0 buf[1] =3D bin2bcd(time->tm_min);
>>> + =C2=A0 =C2=A0 =C2=A0 buf[2] =3D bin2bcd(time->tm_hour);
>>> + =C2=A0 =C2=A0 =C2=A0 buf[3] =3D bin2bcd(time->tm_wday); /* Day of the=
week */
>>> + =C2=A0 =C2=A0 =C2=A0 buf[4] =3D bin2bcd(time->tm_mday); /* Date */
>>> + =C2=A0 =C2=A0 =C2=A0 buf[5] =3D bin2bcd(time->tm_mon);
>>> + =C2=A0 =C2=A0 =C2=A0 if (time->tm_year >=3D 100) {
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 buf[5] |=3D 0x80;
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 buf[6] =3D bin2bcd(t=
ime->tm_year - 100);
>>> + =C2=A0 =C2=A0 =C2=A0 } else {
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 buf[6] =3D bin2bcd(t=
ime->tm_year);
>>> + =C2=A0 =C2=A0 =C2=A0 }
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 return i2c_smbus_write_i2c_block_data(client,
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 DS3232_REG_SECONDS, 7, buf);
>>> +}
>>> +
>>> +/*
>>> + * DS3232 has two alarm, we only use alarm1
>>> + * According to linux specification, only support one-shot alarm
>>> + * no periodic alarm mode
>>> + */
>>> +static int ds3232_read_alarm(struct device *dev, struct rtc_wkalrm *al=
arm)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D to_i2c_client(dev)=
;
>>> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(cli=
ent);
>>> + =C2=A0 =C2=A0 =C2=A0 int control, stat;
>>> + =C2=A0 =C2=A0 =C2=A0 int ret =3D 0;
>>> + =C2=A0 =C2=A0 =C2=A0 u8 buf[4];
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&ds3232->mutex);
>>> + =C2=A0 =C2=A0 =C2=A0 stat =3D ret =3D i2c_smbus_read_byte_data(client=
, DS3232_REG_SR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (stat < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 control =3D ret =3D i2c_smbus_read_byte_data(cli=
ent, DS3232_REG_CR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (control < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_read_i2c_block_data(client, DS=
3232_REG_ALARM1, 4, buf);
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_sec =3D bcd2bin(buf[0] & 0x7F);
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_min =3D bcd2bin(buf[1] & 0x7F);
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_hour =3D bcd2bin(buf[2] & 0x7F);
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_mday =3D bcd2bin(buf[3] & 0x7F);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_mon =3D -1;
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_year =3D -1;
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_wday =3D -1;
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_yday =3D -1;
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_isdst =3D -1;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->enabled =3D !!(control & DS3232_REG_CR_A1=
IE);
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->pending =3D !!(stat & DS3232_REG_SR_A1F);
>>> +out:
>>> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&ds3232->mutex);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 return ret;
>>> +}
>>> +
>>> +/*
>>> + * linux rtc-module does not support wday alarm
>>> + * and only 24h time mode supported indeed
>>> + */
>>> +static int ds3232_set_alarm(struct device *dev, struct rtc_wkalrm *ala=
rm)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D to_i2c_client(dev)=
;
>>> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(cli=
ent);
>>> + =C2=A0 =C2=A0 =C2=A0 int control, stat;
>>> + =C2=A0 =C2=A0 =C2=A0 int ret =3D 0;
>>> + =C2=A0 =C2=A0 =C2=A0 u8 buf[4];
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 if (client->irq <=3D 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -EINVAL;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&ds3232->mutex);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 buf[0] =3D bin2bcd(alarm->time.tm_sec);
>>> + =C2=A0 =C2=A0 =C2=A0 buf[1] =3D bin2bcd(alarm->time.tm_min);
>>> + =C2=A0 =C2=A0 =C2=A0 buf[2] =3D bin2bcd(alarm->time.tm_hour);
>>> + =C2=A0 =C2=A0 =C2=A0 buf[3] =3D bin2bcd(alarm->time.tm_mday);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* clear alarm interrupt enable bit */
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D control =3D i2c_smbus_read_byte_data(cli=
ent, DS3232_REG_CR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
>>> + =C2=A0 =C2=A0 =C2=A0 control &=3D ~(DS3232_REG_CR_A1IE | DS3232_REG_C=
R_A2IE);
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_write_byte_data(client, DS3232=
_REG_CR, control);
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* clear any pending alarm flag */
>>> + =C2=A0 =C2=A0 =C2=A0 stat =3D i2c_smbus_read_byte_data(client, DS3232=
_REG_SR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (stat < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return stat;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 stat &=3D ~(DS3232_REG_SR_A1F | DS3232_REG_SR_A2=
F);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_write_byte_data(client, DS3232=
_REG_SR, stat);
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_write_i2c_block_data(client,
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 DS3232_REG_ALARM1, 4, buf);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 if (alarm->enabled) {
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 control |=3D DS3232_=
REG_CR_A1IE;
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_wr=
ite_byte_data(client, DS3232_REG_CR, control);
>>> + =C2=A0 =C2=A0 =C2=A0 }
>>> +out:
>>> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&ds3232->mutex);
>>> + =C2=A0 =C2=A0 =C2=A0 return ret;
>>> +}
>>> +
>>> +static irqreturn_t ds3232_irq(int irq, void *dev_id)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D dev_id;
>>> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(cli=
ent);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 disable_irq_nosync(irq);
>>> + =C2=A0 =C2=A0 =C2=A0 schedule_work(&ds3232->work);
>>> + =C2=A0 =C2=A0 =C2=A0 return IRQ_HANDLED;
>>> +}
>>> +
>>> +static void ds3232_work(struct work_struct *work)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D container_of(work, str=
uct ds3232, work);
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D ds3232->client;
>>> + =C2=A0 =C2=A0 =C2=A0 int stat, control;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&ds3232->mutex);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 stat =3D i2c_smbus_read_byte_data(client, DS3232=
_REG_SR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (stat < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto unlock;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 if (stat & DS3232_REG_SR_A1F) {
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 control =3D i2c_smbu=
s_read_byte_data(client, DS3232_REG_CR);
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (control < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 goto out;
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* disable alarm1 in=
terrupt */
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 control &=3D ~(DS323=
2_REG_CR_A1IE);
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 i2c_smbus_write_byte=
_data(client, DS3232_REG_CR, control);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* clear the alarm p=
end flag */
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 stat &=3D ~DS3232_RE=
G_SR_A1F;
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 i2c_smbus_write_byte=
_data(client, DS3232_REG_SR, stat);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 rtc_update_irq(ds323=
2->rtc, 1, RTC_AF | RTC_IRQF);
>>> + =C2=A0 =C2=A0 =C2=A0 }
>>> +
>>> +out:
>>> + =C2=A0 =C2=A0 =C2=A0 if (!ds3232->exiting)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 enable_irq(client->i=
rq);
>>> +unlock:
>>> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&ds3232->mutex);
>>> +}
>>> +
>>> +static const struct rtc_class_ops ds3232_rtc_ops =3D {
>>> + =C2=A0 =C2=A0 =C2=A0 .read_time =3D ds3232_read_time,
>>> + =C2=A0 =C2=A0 =C2=A0 .set_time =3D ds3232_set_time,
>>> + =C2=A0 =C2=A0 =C2=A0 .read_alarm =3D ds3232_read_alarm,
>>> + =C2=A0 =C2=A0 =C2=A0 .set_alarm =3D ds3232_set_alarm,
>>> +};
>>
>> I sew that you have discarded the .ioctl function, which is used to
>> enable/disable the alarm irq in previous driver patch.
>> but,at the same time, you didnot implement .alarm_irq_enable function
>> , so is there no need to enable or disable the alarm irq bit in
>> current version patch?
> Right. This is not needed in current patch. Alarm is not used and tested.
> Thanks.
Sorry for forgetting to comment this,
If the alarm is not used, please remove all the alarm parts, should
not we make sure all drivers submitted to linux mainline
can work good?
When you want to use the alarm, you can send one patch to add it again.
> Roy
>
> --
> You received this message because you are subscribed to "rtc-linux".
> Membership options at http://groups.google.com/group/rtc-linux .
> Please read http://groups.google.com/group/rtc-linux/web/checklist
> before submitting a driver.
--=20
*linux-arm-kernel mailing list
mail addr:linux-arm-kernel@lists.infradead.org
you can subscribe by:
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
* linux-arm-NUC900 mailing list
mail addr:NUC900@googlegroups.com
main web: https://groups.google.com/group/NUC900
you can subscribe it by sending me mail:
mcuos.com@gmail.com
^ permalink raw reply
* Re: [rtc-linux] [PATCH v2] rtc: add support for DS3232 RTC
From: Wan ZongShun @ 2010-07-12 8:30 UTC (permalink / raw)
To: rtc-linux; +Cc: linuxppc-dev, Andrew Morton, B11780, Mingkai.hu
In-Reply-To: <AANLkTikv5coqQM4duVmexbOM6iIh-IG-wiCqSqjPHzV3@mail.gmail.com>
2010/7/12 Tiefei zang <tiefei.zang@gmail.com>:
> On Mon, Jul 12, 2010 at 3:08 PM, Wan ZongShun <mcuos.com@gmail.com> wrote=
:
>> 2010/7/12 Roy Zang <tie-fei.zang@freescale.com>:
>>> This patch adds the driver for RTC chip DS3232 via I2C bus.
>>>
>>
>> I noted this patch is the second version, maybe you can describe which
>> modifications you have done here.
>> and add [PATCH v2] to mail subject.
> I do not think the modification comparing previous version should go
> into the commit message.
> I buy in your comments in original version patch and update:
> 1. add rtc_valid_tm to check return value.
> 2. remove ioctl function, which do not used and tested.
> 3. add __devinit for ds3232 probe
> 4. put request irq after driver registration.
>
Okay.
Which git tree do you want to merge your patch?
>>
>>> Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
>>> Signed-off-by: Jingchang Lu <b22599@freescale.com>
>>> Signed-off-by: Srikanth Srinivasan <srikanth.srinivasan@freescale.com>
>>> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
>>> ---
>>> Tested on MPC8536DS and P4080DS board
>>>
>>> =C2=A0drivers/rtc/Kconfig =C2=A0 =C2=A0 =C2=A0| =C2=A0 11 ++
>>> =C2=A0drivers/rtc/Makefile =C2=A0 =C2=A0 | =C2=A0 =C2=A01 +
>>> =C2=A0drivers/rtc/rtc-ds3232.c | =C2=A0427 ++++++++++++++++++++++++++++=
++++++++++++++++++
>>> =C2=A03 files changed, 439 insertions(+), 0 deletions(-)
>>> =C2=A0create mode 100644 drivers/rtc/rtc-ds3232.c
>>>
>>> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
>>> index 6a13037..13c2fdb 100644
>>> --- a/drivers/rtc/Kconfig
>>> +++ b/drivers/rtc/Kconfig
>>> @@ -166,6 +166,17 @@ config RTC_DRV_DS1672
>>> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0This driver can also be built as a mo=
dule. If so, the module
>>> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0will be called rtc-ds1672.
>>>
>>> +config RTC_DRV_DS3232
>>> + =C2=A0 =C2=A0 =C2=A0 tristate "Dallas/Maxim DS3232"
>>> + =C2=A0 =C2=A0 =C2=A0 depends on RTC_CLASS && I2C
>>> + =C2=A0 =C2=A0 =C2=A0 help
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 If you say yes here you get support for D=
allas Semiconductor
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 DS3232 real-time clock chips. =C2=A0If an=
interrupt is associated
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 with the device, the alarm functionality =
is supported.
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 This driver can also be built as a module=
. =C2=A0If so, the module
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 will be called rtc-ds3232.
>>> +
>>> =C2=A0config RTC_DRV_MAX6900
>>> =C2=A0 =C2=A0 =C2=A0 =C2=A0tristate "Maxim MAX6900"
>>> =C2=A0 =C2=A0 =C2=A0 =C2=A0help
>>> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
>>> index 44ef194..0af190c 100644
>>> --- a/drivers/rtc/Makefile
>>> +++ b/drivers/rtc/Makefile
>>> @@ -39,6 +39,7 @@ obj-$(CONFIG_RTC_DRV_DS1511) =C2=A0+=3D rtc-ds1511.o
>>> =C2=A0obj-$(CONFIG_RTC_DRV_DS1553) =C2=A0 +=3D rtc-ds1553.o
>>> =C2=A0obj-$(CONFIG_RTC_DRV_DS1672) =C2=A0 +=3D rtc-ds1672.o
>>> =C2=A0obj-$(CONFIG_RTC_DRV_DS1742) =C2=A0 +=3D rtc-ds1742.o
>>> +obj-$(CONFIG_RTC_DRV_DS3232) =C2=A0 +=3D rtc-ds3232.o
>>> =C2=A0obj-$(CONFIG_RTC_DRV_DS3234) =C2=A0 +=3D rtc-ds3234.o
>>> =C2=A0obj-$(CONFIG_RTC_DRV_EFI) =C2=A0 =C2=A0 =C2=A0+=3D rtc-efi.o
>>> =C2=A0obj-$(CONFIG_RTC_DRV_EP93XX) =C2=A0 +=3D rtc-ep93xx.o
>>> diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
>>> new file mode 100644
>>> index 0000000..e36ec1c
>>> --- /dev/null
>>> +++ b/drivers/rtc/rtc-ds3232.c
>>> @@ -0,0 +1,427 @@
>>> +/*
>>> + * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over =
I2C
>>> + *
>>> + * Copyright (C) 2009-2010 Freescale Semiconductor.
>>> + *
>>> + * This program is free software; you can redistribute =C2=A0it and/or=
modify it
>>> + * under =C2=A0the terms of =C2=A0the GNU General =C2=A0Public License=
as published by the
>>> + * Free Software Foundation; =C2=A0either version 2 of the =C2=A0Licen=
se, or (at your
>>> + * option) any later version.
>>> + */
>>> +/*
>>> + * It would be more efficient to use i2c msgs/i2c_transfer directly bu=
t, as
>>> + * recommened in .../Documentation/i2c/writing-clients section
>>> + * "Sending and receiving", using SMBus level communication is preferr=
ed.
>>> + */
>>> +
>>> +#include <linux/kernel.h>
>>> +#include <linux/module.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/i2c.h>
>>> +#include <linux/rtc.h>
>>> +#include <linux/bcd.h>
>>> +#include <linux/workqueue.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#define DS3232_REG_SECONDS =C2=A0 =C2=A0 0x00
>>> +#define DS3232_REG_MINUTES =C2=A0 =C2=A0 0x01
>>> +#define DS3232_REG_HOURS =C2=A0 =C2=A0 =C2=A0 0x02
>>> +#define DS3232_REG_AMPM =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A00x02
>>> +#define DS3232_REG_DAY =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x03
>>> +#define DS3232_REG_DATE =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A00x04
>>> +#define DS3232_REG_MONTH =C2=A0 =C2=A0 =C2=A0 0x05
>>> +#define DS3232_REG_CENTURY =C2=A0 =C2=A0 0x05
>>> +#define DS3232_REG_YEAR =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A00x06
>>> +#define DS3232_REG_ALARM1 =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x07 /* Alarm 1 =
BASE */
>>> +#define DS3232_REG_ALARM2 =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x0B /* Alarm 2 =
BASE */
>>> +#define DS3232_REG_CR =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x0E =C2=A0 =
=C2=A0/* Control register */
>>> +# =C2=A0 =C2=A0 =C2=A0define DS3232_REG_CR_nEOSC =C2=A0 =C2=A0 =C2=A0 =
=C2=A00x80
>>> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_CR_INTCN =C2=A0 =C2=A0 =C2=A0=
=C2=A00x04
>>> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_CR_A2IE =C2=A0 =C2=A0 =C2=A0 =
=C2=A00x02
>>> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_CR_A1IE =C2=A0 =C2=A0 =C2=A0 =
=C2=A00x01
>>> +
>>> +#define DS3232_REG_SR =C2=A00x0F =C2=A0 =C2=A0/* control/status regist=
er */
>>> +# =C2=A0 =C2=A0 =C2=A0define DS3232_REG_SR_OSF =C2=A0 0x80
>>> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_SR_BSY =C2=A0 0x04
>>> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_SR_A2F =C2=A0 0x02
>>> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_SR_A1F =C2=A0 0x01
>>> +
>>> +struct ds3232 {
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client;
>>> + =C2=A0 =C2=A0 =C2=A0 struct rtc_device *rtc;
>>> + =C2=A0 =C2=A0 =C2=A0 struct work_struct work;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* The mutex protects alarm operations, and prev=
ents a race
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* between the enable_irq() in the workqueu=
e and the free_irq()
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* in the remove function.
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
>>> + =C2=A0 =C2=A0 =C2=A0 struct mutex mutex;
>>> + =C2=A0 =C2=A0 =C2=A0 int exiting;
>>> +};
>>> +
>>> +static struct i2c_driver ds3232_driver;
>>> +
>>> +static int ds3232_check_rtc_status(struct i2c_client *client)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 int ret =3D 0;
>>> + =C2=A0 =C2=A0 =C2=A0 int control, stat;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 stat =3D i2c_smbus_read_byte_data(client, DS3232=
_REG_SR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (stat < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return stat;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 if (stat & DS3232_REG_SR_OSF)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_warn(&client->de=
v,
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "oscillator discontinuity flagged, "
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "time unreliable\n");
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 stat &=3D ~(DS3232_REG_SR_OSF | DS3232_REG_SR_A1=
F | DS3232_REG_SR_A2F);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_write_byte_data(client, DS3232=
_REG_SR, stat);
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* If the alarm is pending, clear it before requ=
esting
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* the interrupt, so an interrupt event isn=
't reported
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* before everything is initialized.
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 control =3D i2c_smbus_read_byte_data(client, DS3=
232_REG_CR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (control < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return control;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 control &=3D ~(DS3232_REG_CR_A1IE | DS3232_REG_C=
R_A2IE);
>>> + =C2=A0 =C2=A0 =C2=A0 control |=3D DS3232_REG_CR_INTCN;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 return i2c_smbus_write_byte_data(client, DS3232_=
REG_CR, control);
>>> +}
>>> +
>>> +static int ds3232_read_time(struct device *dev, struct rtc_time *time)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D to_i2c_client(dev)=
;
>>> + =C2=A0 =C2=A0 =C2=A0 int ret;
>>> + =C2=A0 =C2=A0 =C2=A0 u8 buf[7];
>>> + =C2=A0 =C2=A0 =C2=A0 unsigned int year, month, day, hour, minute, sec=
ond;
>>> + =C2=A0 =C2=A0 =C2=A0 unsigned int week, twelve_hr, am_pm;
>>> + =C2=A0 =C2=A0 =C2=A0 unsigned int century, add_century =3D 0;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_read_i2c_block_data(client, DS=
3232_REG_SECONDS, 7, buf);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret;
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 7)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -EIO;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 second =3D buf[0];
>>> + =C2=A0 =C2=A0 =C2=A0 minute =3D buf[1];
>>> + =C2=A0 =C2=A0 =C2=A0 hour =3D buf[2];
>>> + =C2=A0 =C2=A0 =C2=A0 week =3D buf[3];
>>> + =C2=A0 =C2=A0 =C2=A0 day =3D buf[4];
>>> + =C2=A0 =C2=A0 =C2=A0 month =3D buf[5];
>>> + =C2=A0 =C2=A0 =C2=A0 year =3D buf[6];
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* Extract additional information for AM/PM and =
century */
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 twelve_hr =3D hour & 0x40;
>>> + =C2=A0 =C2=A0 =C2=A0 am_pm =3D hour & 0x20;
>>> + =C2=A0 =C2=A0 =C2=A0 century =3D month & 0x80;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* Write to rtc_time structure */
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 time->tm_sec =3D bcd2bin(second);
>>> + =C2=A0 =C2=A0 =C2=A0 time->tm_min =3D bcd2bin(minute);
>>> + =C2=A0 =C2=A0 =C2=A0 if (twelve_hr) {
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Convert to 24 hr =
*/
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (am_pm)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 time->tm_hour =3D bcd2bin(hour & 0x1F) + 12;
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 else
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 time->tm_hour =3D bcd2bin(hour & 0x1F);
>>> + =C2=A0 =C2=A0 =C2=A0 } else {
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 time->tm_hour =3D bc=
d2bin(hour);
>>> + =C2=A0 =C2=A0 =C2=A0 }
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 time->tm_wday =3D bcd2bin(week);
>>> + =C2=A0 =C2=A0 =C2=A0 time->tm_mday =3D bcd2bin(day);
>>> + =C2=A0 =C2=A0 =C2=A0 time->tm_mon =3D bcd2bin(month & 0x7F);
>>> + =C2=A0 =C2=A0 =C2=A0 if (century)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 add_century =3D 100;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 time->tm_year =3D bcd2bin(year) + add_century;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 return rtc_valid_tm(time);
>>> +}
>>> +
>>> +static int ds3232_set_time(struct device *dev, struct rtc_time *time)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D to_i2c_client(dev)=
;
>>> + =C2=A0 =C2=A0 =C2=A0 u8 buf[7];
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* Extract time from rtc_time and load into ds32=
32*/
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 buf[0] =3D bin2bcd(time->tm_sec);
>>> + =C2=A0 =C2=A0 =C2=A0 buf[1] =3D bin2bcd(time->tm_min);
>>> + =C2=A0 =C2=A0 =C2=A0 buf[2] =3D bin2bcd(time->tm_hour);
>>> + =C2=A0 =C2=A0 =C2=A0 buf[3] =3D bin2bcd(time->tm_wday); /* Day of the=
week */
>>> + =C2=A0 =C2=A0 =C2=A0 buf[4] =3D bin2bcd(time->tm_mday); /* Date */
>>> + =C2=A0 =C2=A0 =C2=A0 buf[5] =3D bin2bcd(time->tm_mon);
>>> + =C2=A0 =C2=A0 =C2=A0 if (time->tm_year >=3D 100) {
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 buf[5] |=3D 0x80;
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 buf[6] =3D bin2bcd(t=
ime->tm_year - 100);
>>> + =C2=A0 =C2=A0 =C2=A0 } else {
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 buf[6] =3D bin2bcd(t=
ime->tm_year);
>>> + =C2=A0 =C2=A0 =C2=A0 }
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 return i2c_smbus_write_i2c_block_data(client,
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 DS3232_REG_SECONDS, 7, buf);
>>> +}
>>> +
>>> +/*
>>> + * DS3232 has two alarm, we only use alarm1
>>> + * According to linux specification, only support one-shot alarm
>>> + * no periodic alarm mode
>>> + */
>>> +static int ds3232_read_alarm(struct device *dev, struct rtc_wkalrm *al=
arm)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D to_i2c_client(dev)=
;
>>> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(cli=
ent);
>>> + =C2=A0 =C2=A0 =C2=A0 int control, stat;
>>> + =C2=A0 =C2=A0 =C2=A0 int ret =3D 0;
>>> + =C2=A0 =C2=A0 =C2=A0 u8 buf[4];
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&ds3232->mutex);
>>> + =C2=A0 =C2=A0 =C2=A0 stat =3D ret =3D i2c_smbus_read_byte_data(client=
, DS3232_REG_SR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (stat < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 control =3D ret =3D i2c_smbus_read_byte_data(cli=
ent, DS3232_REG_CR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (control < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_read_i2c_block_data(client, DS=
3232_REG_ALARM1, 4, buf);
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_sec =3D bcd2bin(buf[0] & 0x7F);
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_min =3D bcd2bin(buf[1] & 0x7F);
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_hour =3D bcd2bin(buf[2] & 0x7F);
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_mday =3D bcd2bin(buf[3] & 0x7F);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_mon =3D -1;
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_year =3D -1;
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_wday =3D -1;
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_yday =3D -1;
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_isdst =3D -1;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->enabled =3D !!(control & DS3232_REG_CR_A1=
IE);
>>> + =C2=A0 =C2=A0 =C2=A0 alarm->pending =3D !!(stat & DS3232_REG_SR_A1F);
>>> +out:
>>> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&ds3232->mutex);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 return ret;
>>> +}
>>> +
>>> +/*
>>> + * linux rtc-module does not support wday alarm
>>> + * and only 24h time mode supported indeed
>>> + */
>>> +static int ds3232_set_alarm(struct device *dev, struct rtc_wkalrm *ala=
rm)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D to_i2c_client(dev)=
;
>>> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(cli=
ent);
>>> + =C2=A0 =C2=A0 =C2=A0 int control, stat;
>>> + =C2=A0 =C2=A0 =C2=A0 int ret =3D 0;
>>> + =C2=A0 =C2=A0 =C2=A0 u8 buf[4];
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 if (client->irq <=3D 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -EINVAL;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&ds3232->mutex);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 buf[0] =3D bin2bcd(alarm->time.tm_sec);
>>> + =C2=A0 =C2=A0 =C2=A0 buf[1] =3D bin2bcd(alarm->time.tm_min);
>>> + =C2=A0 =C2=A0 =C2=A0 buf[2] =3D bin2bcd(alarm->time.tm_hour);
>>> + =C2=A0 =C2=A0 =C2=A0 buf[3] =3D bin2bcd(alarm->time.tm_mday);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* clear alarm interrupt enable bit */
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D control =3D i2c_smbus_read_byte_data(cli=
ent, DS3232_REG_CR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
>>> + =C2=A0 =C2=A0 =C2=A0 control &=3D ~(DS3232_REG_CR_A1IE | DS3232_REG_C=
R_A2IE);
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_write_byte_data(client, DS3232=
_REG_CR, control);
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 /* clear any pending alarm flag */
>>> + =C2=A0 =C2=A0 =C2=A0 stat =3D i2c_smbus_read_byte_data(client, DS3232=
_REG_SR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (stat < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return stat;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 stat &=3D ~(DS3232_REG_SR_A1F | DS3232_REG_SR_A2=
F);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_write_byte_data(client, DS3232=
_REG_SR, stat);
>>> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_write_i2c_block_data(client,
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 DS3232_REG_ALARM1, 4, buf);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 if (alarm->enabled) {
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 control |=3D DS3232_=
REG_CR_A1IE;
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_wr=
ite_byte_data(client, DS3232_REG_CR, control);
>>> + =C2=A0 =C2=A0 =C2=A0 }
>>> +out:
>>> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&ds3232->mutex);
>>> + =C2=A0 =C2=A0 =C2=A0 return ret;
>>> +}
>>> +
>>> +static irqreturn_t ds3232_irq(int irq, void *dev_id)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D dev_id;
>>> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(cli=
ent);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 disable_irq_nosync(irq);
>>> + =C2=A0 =C2=A0 =C2=A0 schedule_work(&ds3232->work);
>>> + =C2=A0 =C2=A0 =C2=A0 return IRQ_HANDLED;
>>> +}
>>> +
>>> +static void ds3232_work(struct work_struct *work)
>>> +{
>>> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D container_of(work, str=
uct ds3232, work);
>>> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D ds3232->client;
>>> + =C2=A0 =C2=A0 =C2=A0 int stat, control;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&ds3232->mutex);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 stat =3D i2c_smbus_read_byte_data(client, DS3232=
_REG_SR);
>>> + =C2=A0 =C2=A0 =C2=A0 if (stat < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto unlock;
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 if (stat & DS3232_REG_SR_A1F) {
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 control =3D i2c_smbu=
s_read_byte_data(client, DS3232_REG_CR);
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (control < 0)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 goto out;
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* disable alarm1 in=
terrupt */
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 control &=3D ~(DS323=
2_REG_CR_A1IE);
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 i2c_smbus_write_byte=
_data(client, DS3232_REG_CR, control);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* clear the alarm p=
end flag */
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 stat &=3D ~DS3232_RE=
G_SR_A1F;
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 i2c_smbus_write_byte=
_data(client, DS3232_REG_SR, stat);
>>> +
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 rtc_update_irq(ds323=
2->rtc, 1, RTC_AF | RTC_IRQF);
>>> + =C2=A0 =C2=A0 =C2=A0 }
>>> +
>>> +out:
>>> + =C2=A0 =C2=A0 =C2=A0 if (!ds3232->exiting)
>>> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 enable_irq(client->i=
rq);
>>> +unlock:
>>> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&ds3232->mutex);
>>> +}
>>> +
>>> +static const struct rtc_class_ops ds3232_rtc_ops =3D {
>>> + =C2=A0 =C2=A0 =C2=A0 .read_time =3D ds3232_read_time,
>>> + =C2=A0 =C2=A0 =C2=A0 .set_time =3D ds3232_set_time,
>>> + =C2=A0 =C2=A0 =C2=A0 .read_alarm =3D ds3232_read_alarm,
>>> + =C2=A0 =C2=A0 =C2=A0 .set_alarm =3D ds3232_set_alarm,
>>> +};
>>
>> I sew that you have discarded the .ioctl function, which is used to
>> enable/disable the alarm irq in previous driver patch.
>> but,at the same time, you didnot implement .alarm_irq_enable function
>> , so is there no need to enable or disable the alarm irq bit in
>> current version patch?
> Right. This is not needed in current patch. Alarm is not used and tested.
> Thanks.
> Roy
>
> --
> You received this message because you are subscribed to "rtc-linux".
> Membership options at http://groups.google.com/group/rtc-linux .
> Please read http://groups.google.com/group/rtc-linux/web/checklist
> before submitting a driver.
--=20
*linux-arm-kernel mailing list
mail addr:linux-arm-kernel@lists.infradead.org
you can subscribe by:
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
* linux-arm-NUC900 mailing list
mail addr:NUC900@googlegroups.com
main web: https://groups.google.com/group/NUC900
you can subscribe it by sending me mail:
mcuos.com@gmail.com
^ permalink raw reply
* Re: [rtc-linux] [PATCH v2] rtc: add support for DS3232 RTC
From: Tiefei zang @ 2010-07-12 8:12 UTC (permalink / raw)
To: rtc-linux; +Cc: linuxppc-dev, Andrew Morton, B11780, Mingkai.hu
In-Reply-To: <AANLkTikK7wsnSVa9Ww5swaAR828edz_zuiBHzV4WZjqw@mail.gmail.com>
On Mon, Jul 12, 2010 at 3:08 PM, Wan ZongShun <mcuos.com@gmail.com> wrote:
> 2010/7/12 Roy Zang <tie-fei.zang@freescale.com>:
>> This patch adds the driver for RTC chip DS3232 via I2C bus.
>>
>
> I noted this patch is the second version, maybe you can describe which
> modifications you have done here.
> and add [PATCH v2] to mail subject.
I do not think the modification comparing previous version should go
into the commit message.
I buy in your comments in original version patch and update:
1. add rtc_valid_tm to check return value.
2. remove ioctl function, which do not used and tested.
3. add __devinit for ds3232 probe
4. put request irq after driver registration.
>
>> Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
>> Signed-off-by: Jingchang Lu <b22599@freescale.com>
>> Signed-off-by: Srikanth Srinivasan <srikanth.srinivasan@freescale.com>
>> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
>> ---
>> Tested on MPC8536DS and P4080DS board
>>
>> =A0drivers/rtc/Kconfig =A0 =A0 =A0| =A0 11 ++
>> =A0drivers/rtc/Makefile =A0 =A0 | =A0 =A01 +
>> =A0drivers/rtc/rtc-ds3232.c | =A0427 +++++++++++++++++++++++++++++++++++=
+++++++++++
>> =A03 files changed, 439 insertions(+), 0 deletions(-)
>> =A0create mode 100644 drivers/rtc/rtc-ds3232.c
>>
>> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
>> index 6a13037..13c2fdb 100644
>> --- a/drivers/rtc/Kconfig
>> +++ b/drivers/rtc/Kconfig
>> @@ -166,6 +166,17 @@ config RTC_DRV_DS1672
>> =A0 =A0 =A0 =A0 =A0This driver can also be built as a module. If so, the=
module
>> =A0 =A0 =A0 =A0 =A0will be called rtc-ds1672.
>>
>> +config RTC_DRV_DS3232
>> + =A0 =A0 =A0 tristate "Dallas/Maxim DS3232"
>> + =A0 =A0 =A0 depends on RTC_CLASS && I2C
>> + =A0 =A0 =A0 help
>> + =A0 =A0 =A0 =A0 If you say yes here you get support for Dallas Semicon=
ductor
>> + =A0 =A0 =A0 =A0 DS3232 real-time clock chips. =A0If an interrupt is as=
sociated
>> + =A0 =A0 =A0 =A0 with the device, the alarm functionality is supported.
>> +
>> + =A0 =A0 =A0 =A0 This driver can also be built as a module. =A0If so, t=
he module
>> + =A0 =A0 =A0 =A0 will be called rtc-ds3232.
>> +
>> =A0config RTC_DRV_MAX6900
>> =A0 =A0 =A0 =A0tristate "Maxim MAX6900"
>> =A0 =A0 =A0 =A0help
>> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
>> index 44ef194..0af190c 100644
>> --- a/drivers/rtc/Makefile
>> +++ b/drivers/rtc/Makefile
>> @@ -39,6 +39,7 @@ obj-$(CONFIG_RTC_DRV_DS1511) =A0+=3D rtc-ds1511.o
>> =A0obj-$(CONFIG_RTC_DRV_DS1553) =A0 +=3D rtc-ds1553.o
>> =A0obj-$(CONFIG_RTC_DRV_DS1672) =A0 +=3D rtc-ds1672.o
>> =A0obj-$(CONFIG_RTC_DRV_DS1742) =A0 +=3D rtc-ds1742.o
>> +obj-$(CONFIG_RTC_DRV_DS3232) =A0 +=3D rtc-ds3232.o
>> =A0obj-$(CONFIG_RTC_DRV_DS3234) =A0 +=3D rtc-ds3234.o
>> =A0obj-$(CONFIG_RTC_DRV_EFI) =A0 =A0 =A0+=3D rtc-efi.o
>> =A0obj-$(CONFIG_RTC_DRV_EP93XX) =A0 +=3D rtc-ep93xx.o
>> diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
>> new file mode 100644
>> index 0000000..e36ec1c
>> --- /dev/null
>> +++ b/drivers/rtc/rtc-ds3232.c
>> @@ -0,0 +1,427 @@
>> +/*
>> + * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over I=
2C
>> + *
>> + * Copyright (C) 2009-2010 Freescale Semiconductor.
>> + *
>> + * This program is free software; you can redistribute =A0it and/or mod=
ify it
>> + * under =A0the terms of =A0the GNU General =A0Public License as publis=
hed by the
>> + * Free Software Foundation; =A0either version 2 of the =A0License, or =
(at your
>> + * option) any later version.
>> + */
>> +/*
>> + * It would be more efficient to use i2c msgs/i2c_transfer directly but=
, as
>> + * recommened in .../Documentation/i2c/writing-clients section
>> + * "Sending and receiving", using SMBus level communication is preferre=
d.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/i2c.h>
>> +#include <linux/rtc.h>
>> +#include <linux/bcd.h>
>> +#include <linux/workqueue.h>
>> +#include <linux/slab.h>
>> +
>> +#define DS3232_REG_SECONDS =A0 =A0 0x00
>> +#define DS3232_REG_MINUTES =A0 =A0 0x01
>> +#define DS3232_REG_HOURS =A0 =A0 =A0 0x02
>> +#define DS3232_REG_AMPM =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A00x02
>> +#define DS3232_REG_DAY =A0 =A0 =A0 =A0 0x03
>> +#define DS3232_REG_DATE =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A00x04
>> +#define DS3232_REG_MONTH =A0 =A0 =A0 0x05
>> +#define DS3232_REG_CENTURY =A0 =A0 0x05
>> +#define DS3232_REG_YEAR =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A00x06
>> +#define DS3232_REG_ALARM1 =A0 =A0 =A0 =A0 0x07 /* Alarm 1 BASE */
>> +#define DS3232_REG_ALARM2 =A0 =A0 =A0 =A0 0x0B /* Alarm 2 BASE */
>> +#define DS3232_REG_CR =A0 =A0 =A0 =A0 =A00x0E =A0 =A0/* Control registe=
r */
>> +# =A0 =A0 =A0define DS3232_REG_CR_nEOSC =A0 =A0 =A0 =A00x80
>> +# =A0 =A0 =A0 define DS3232_REG_CR_INTCN =A0 =A0 =A0 =A00x04
>> +# =A0 =A0 =A0 define DS3232_REG_CR_A2IE =A0 =A0 =A0 =A00x02
>> +# =A0 =A0 =A0 define DS3232_REG_CR_A1IE =A0 =A0 =A0 =A00x01
>> +
>> +#define DS3232_REG_SR =A00x0F =A0 =A0/* control/status register */
>> +# =A0 =A0 =A0define DS3232_REG_SR_OSF =A0 0x80
>> +# =A0 =A0 =A0 define DS3232_REG_SR_BSY =A0 0x04
>> +# =A0 =A0 =A0 define DS3232_REG_SR_A2F =A0 0x02
>> +# =A0 =A0 =A0 define DS3232_REG_SR_A1F =A0 0x01
>> +
>> +struct ds3232 {
>> + =A0 =A0 =A0 struct i2c_client *client;
>> + =A0 =A0 =A0 struct rtc_device *rtc;
>> + =A0 =A0 =A0 struct work_struct work;
>> +
>> + =A0 =A0 =A0 /* The mutex protects alarm operations, and prevents a rac=
e
>> + =A0 =A0 =A0 =A0* between the enable_irq() in the workqueue and the fre=
e_irq()
>> + =A0 =A0 =A0 =A0* in the remove function.
>> + =A0 =A0 =A0 =A0*/
>> + =A0 =A0 =A0 struct mutex mutex;
>> + =A0 =A0 =A0 int exiting;
>> +};
>> +
>> +static struct i2c_driver ds3232_driver;
>> +
>> +static int ds3232_check_rtc_status(struct i2c_client *client)
>> +{
>> + =A0 =A0 =A0 int ret =3D 0;
>> + =A0 =A0 =A0 int control, stat;
>> +
>> + =A0 =A0 =A0 stat =3D i2c_smbus_read_byte_data(client, DS3232_REG_SR);
>> + =A0 =A0 =A0 if (stat < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return stat;
>> +
>> + =A0 =A0 =A0 if (stat & DS3232_REG_SR_OSF)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_warn(&client->dev,
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "oscillato=
r discontinuity flagged, "
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "time unre=
liable\n");
>> +
>> + =A0 =A0 =A0 stat &=3D ~(DS3232_REG_SR_OSF | DS3232_REG_SR_A1F | DS3232=
_REG_SR_A2F);
>> +
>> + =A0 =A0 =A0 ret =3D i2c_smbus_write_byte_data(client, DS3232_REG_SR, s=
tat);
>> + =A0 =A0 =A0 if (ret < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return ret;
>> +
>> + =A0 =A0 =A0 /* If the alarm is pending, clear it before requesting
>> + =A0 =A0 =A0 =A0* the interrupt, so an interrupt event isn't reported
>> + =A0 =A0 =A0 =A0* before everything is initialized.
>> + =A0 =A0 =A0 =A0*/
>> +
>> + =A0 =A0 =A0 control =3D i2c_smbus_read_byte_data(client, DS3232_REG_CR=
);
>> + =A0 =A0 =A0 if (control < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return control;
>> +
>> + =A0 =A0 =A0 control &=3D ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE);
>> + =A0 =A0 =A0 control |=3D DS3232_REG_CR_INTCN;
>> +
>> + =A0 =A0 =A0 return i2c_smbus_write_byte_data(client, DS3232_REG_CR, co=
ntrol);
>> +}
>> +
>> +static int ds3232_read_time(struct device *dev, struct rtc_time *time)
>> +{
>> + =A0 =A0 =A0 struct i2c_client *client =3D to_i2c_client(dev);
>> + =A0 =A0 =A0 int ret;
>> + =A0 =A0 =A0 u8 buf[7];
>> + =A0 =A0 =A0 unsigned int year, month, day, hour, minute, second;
>> + =A0 =A0 =A0 unsigned int week, twelve_hr, am_pm;
>> + =A0 =A0 =A0 unsigned int century, add_century =3D 0;
>> +
>> + =A0 =A0 =A0 ret =3D i2c_smbus_read_i2c_block_data(client, DS3232_REG_S=
ECONDS, 7, buf);
>> +
>> + =A0 =A0 =A0 if (ret < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return ret;
>> + =A0 =A0 =A0 if (ret < 7)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EIO;
>> +
>> + =A0 =A0 =A0 second =3D buf[0];
>> + =A0 =A0 =A0 minute =3D buf[1];
>> + =A0 =A0 =A0 hour =3D buf[2];
>> + =A0 =A0 =A0 week =3D buf[3];
>> + =A0 =A0 =A0 day =3D buf[4];
>> + =A0 =A0 =A0 month =3D buf[5];
>> + =A0 =A0 =A0 year =3D buf[6];
>> +
>> + =A0 =A0 =A0 /* Extract additional information for AM/PM and century */
>> +
>> + =A0 =A0 =A0 twelve_hr =3D hour & 0x40;
>> + =A0 =A0 =A0 am_pm =3D hour & 0x20;
>> + =A0 =A0 =A0 century =3D month & 0x80;
>> +
>> + =A0 =A0 =A0 /* Write to rtc_time structure */
>> +
>> + =A0 =A0 =A0 time->tm_sec =3D bcd2bin(second);
>> + =A0 =A0 =A0 time->tm_min =3D bcd2bin(minute);
>> + =A0 =A0 =A0 if (twelve_hr) {
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Convert to 24 hr */
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (am_pm)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 time->tm_hour =3D bcd2bin(=
hour & 0x1F) + 12;
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 else
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 time->tm_hour =3D bcd2bin(=
hour & 0x1F);
>> + =A0 =A0 =A0 } else {
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 time->tm_hour =3D bcd2bin(hour);
>> + =A0 =A0 =A0 }
>> +
>> + =A0 =A0 =A0 time->tm_wday =3D bcd2bin(week);
>> + =A0 =A0 =A0 time->tm_mday =3D bcd2bin(day);
>> + =A0 =A0 =A0 time->tm_mon =3D bcd2bin(month & 0x7F);
>> + =A0 =A0 =A0 if (century)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 add_century =3D 100;
>> +
>> + =A0 =A0 =A0 time->tm_year =3D bcd2bin(year) + add_century;
>> +
>> + =A0 =A0 =A0 return rtc_valid_tm(time);
>> +}
>> +
>> +static int ds3232_set_time(struct device *dev, struct rtc_time *time)
>> +{
>> + =A0 =A0 =A0 struct i2c_client *client =3D to_i2c_client(dev);
>> + =A0 =A0 =A0 u8 buf[7];
>> +
>> + =A0 =A0 =A0 /* Extract time from rtc_time and load into ds3232*/
>> +
>> + =A0 =A0 =A0 buf[0] =3D bin2bcd(time->tm_sec);
>> + =A0 =A0 =A0 buf[1] =3D bin2bcd(time->tm_min);
>> + =A0 =A0 =A0 buf[2] =3D bin2bcd(time->tm_hour);
>> + =A0 =A0 =A0 buf[3] =3D bin2bcd(time->tm_wday); /* Day of the week */
>> + =A0 =A0 =A0 buf[4] =3D bin2bcd(time->tm_mday); /* Date */
>> + =A0 =A0 =A0 buf[5] =3D bin2bcd(time->tm_mon);
>> + =A0 =A0 =A0 if (time->tm_year >=3D 100) {
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 buf[5] |=3D 0x80;
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 buf[6] =3D bin2bcd(time->tm_year - 100);
>> + =A0 =A0 =A0 } else {
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 buf[6] =3D bin2bcd(time->tm_year);
>> + =A0 =A0 =A0 }
>> +
>> + =A0 =A0 =A0 return i2c_smbus_write_i2c_block_data(client,
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0 DS3232_REG_SECONDS, 7, buf);
>> +}
>> +
>> +/*
>> + * DS3232 has two alarm, we only use alarm1
>> + * According to linux specification, only support one-shot alarm
>> + * no periodic alarm mode
>> + */
>> +static int ds3232_read_alarm(struct device *dev, struct rtc_wkalrm *ala=
rm)
>> +{
>> + =A0 =A0 =A0 struct i2c_client *client =3D to_i2c_client(dev);
>> + =A0 =A0 =A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(client);
>> + =A0 =A0 =A0 int control, stat;
>> + =A0 =A0 =A0 int ret =3D 0;
>> + =A0 =A0 =A0 u8 buf[4];
>> +
>> + =A0 =A0 =A0 mutex_lock(&ds3232->mutex);
>> + =A0 =A0 =A0 stat =3D ret =3D i2c_smbus_read_byte_data(client, DS3232_R=
EG_SR);
>> + =A0 =A0 =A0 if (stat < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
>> +
>> + =A0 =A0 =A0 control =3D ret =3D i2c_smbus_read_byte_data(client, DS323=
2_REG_CR);
>> + =A0 =A0 =A0 if (control < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
>> +
>> + =A0 =A0 =A0 ret =3D i2c_smbus_read_i2c_block_data(client, DS3232_REG_A=
LARM1, 4, buf);
>> + =A0 =A0 =A0 if (ret < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
>> +
>> + =A0 =A0 =A0 alarm->time.tm_sec =3D bcd2bin(buf[0] & 0x7F);
>> + =A0 =A0 =A0 alarm->time.tm_min =3D bcd2bin(buf[1] & 0x7F);
>> + =A0 =A0 =A0 alarm->time.tm_hour =3D bcd2bin(buf[2] & 0x7F);
>> + =A0 =A0 =A0 alarm->time.tm_mday =3D bcd2bin(buf[3] & 0x7F);
>> +
>> + =A0 =A0 =A0 alarm->time.tm_mon =3D -1;
>> + =A0 =A0 =A0 alarm->time.tm_year =3D -1;
>> + =A0 =A0 =A0 alarm->time.tm_wday =3D -1;
>> + =A0 =A0 =A0 alarm->time.tm_yday =3D -1;
>> + =A0 =A0 =A0 alarm->time.tm_isdst =3D -1;
>> +
>> + =A0 =A0 =A0 alarm->enabled =3D !!(control & DS3232_REG_CR_A1IE);
>> + =A0 =A0 =A0 alarm->pending =3D !!(stat & DS3232_REG_SR_A1F);
>> +out:
>> + =A0 =A0 =A0 mutex_unlock(&ds3232->mutex);
>> +
>> + =A0 =A0 =A0 return ret;
>> +}
>> +
>> +/*
>> + * linux rtc-module does not support wday alarm
>> + * and only 24h time mode supported indeed
>> + */
>> +static int ds3232_set_alarm(struct device *dev, struct rtc_wkalrm *alar=
m)
>> +{
>> + =A0 =A0 =A0 struct i2c_client *client =3D to_i2c_client(dev);
>> + =A0 =A0 =A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(client);
>> + =A0 =A0 =A0 int control, stat;
>> + =A0 =A0 =A0 int ret =3D 0;
>> + =A0 =A0 =A0 u8 buf[4];
>> +
>> + =A0 =A0 =A0 if (client->irq <=3D 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
>> +
>> + =A0 =A0 =A0 mutex_lock(&ds3232->mutex);
>> +
>> + =A0 =A0 =A0 buf[0] =3D bin2bcd(alarm->time.tm_sec);
>> + =A0 =A0 =A0 buf[1] =3D bin2bcd(alarm->time.tm_min);
>> + =A0 =A0 =A0 buf[2] =3D bin2bcd(alarm->time.tm_hour);
>> + =A0 =A0 =A0 buf[3] =3D bin2bcd(alarm->time.tm_mday);
>> +
>> + =A0 =A0 =A0 /* clear alarm interrupt enable bit */
>> + =A0 =A0 =A0 ret =3D control =3D i2c_smbus_read_byte_data(client, DS323=
2_REG_CR);
>> + =A0 =A0 =A0 if (ret < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
>> + =A0 =A0 =A0 control &=3D ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE);
>> + =A0 =A0 =A0 ret =3D i2c_smbus_write_byte_data(client, DS3232_REG_CR, c=
ontrol);
>> + =A0 =A0 =A0 if (ret < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
>> +
>> + =A0 =A0 =A0 /* clear any pending alarm flag */
>> + =A0 =A0 =A0 stat =3D i2c_smbus_read_byte_data(client, DS3232_REG_SR);
>> + =A0 =A0 =A0 if (stat < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return stat;
>> +
>> + =A0 =A0 =A0 stat &=3D ~(DS3232_REG_SR_A1F | DS3232_REG_SR_A2F);
>> +
>> + =A0 =A0 =A0 ret =3D i2c_smbus_write_byte_data(client, DS3232_REG_SR, s=
tat);
>> + =A0 =A0 =A0 if (ret < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return ret;
>> +
>> + =A0 =A0 =A0 ret =3D i2c_smbus_write_i2c_block_data(client,
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0 =A0 =A0 =A0 DS3232_REG_ALARM1, 4, buf);
>> +
>> + =A0 =A0 =A0 if (alarm->enabled) {
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 control |=3D DS3232_REG_CR_A1IE;
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D i2c_smbus_write_byte_data(client, =
DS3232_REG_CR, control);
>> + =A0 =A0 =A0 }
>> +out:
>> + =A0 =A0 =A0 mutex_unlock(&ds3232->mutex);
>> + =A0 =A0 =A0 return ret;
>> +}
>> +
>> +static irqreturn_t ds3232_irq(int irq, void *dev_id)
>> +{
>> + =A0 =A0 =A0 struct i2c_client *client =3D dev_id;
>> + =A0 =A0 =A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(client);
>> +
>> + =A0 =A0 =A0 disable_irq_nosync(irq);
>> + =A0 =A0 =A0 schedule_work(&ds3232->work);
>> + =A0 =A0 =A0 return IRQ_HANDLED;
>> +}
>> +
>> +static void ds3232_work(struct work_struct *work)
>> +{
>> + =A0 =A0 =A0 struct ds3232 *ds3232 =3D container_of(work, struct ds3232=
, work);
>> + =A0 =A0 =A0 struct i2c_client *client =3D ds3232->client;
>> + =A0 =A0 =A0 int stat, control;
>> +
>> + =A0 =A0 =A0 mutex_lock(&ds3232->mutex);
>> +
>> + =A0 =A0 =A0 stat =3D i2c_smbus_read_byte_data(client, DS3232_REG_SR);
>> + =A0 =A0 =A0 if (stat < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto unlock;
>> +
>> + =A0 =A0 =A0 if (stat & DS3232_REG_SR_A1F) {
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 control =3D i2c_smbus_read_byte_data(clien=
t, DS3232_REG_CR);
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (control < 0)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto out;
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* disable alarm1 interrupt */
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 control &=3D ~(DS3232_REG_CR_A1IE);
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 i2c_smbus_write_byte_data(client, DS3232_R=
EG_CR, control);
>> +
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* clear the alarm pend flag */
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 stat &=3D ~DS3232_REG_SR_A1F;
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 i2c_smbus_write_byte_data(client, DS3232_R=
EG_SR, stat);
>> +
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 rtc_update_irq(ds3232->rtc, 1, RTC_AF | RT=
C_IRQF);
>> + =A0 =A0 =A0 }
>> +
>> +out:
>> + =A0 =A0 =A0 if (!ds3232->exiting)
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 enable_irq(client->irq);
>> +unlock:
>> + =A0 =A0 =A0 mutex_unlock(&ds3232->mutex);
>> +}
>> +
>> +static const struct rtc_class_ops ds3232_rtc_ops =3D {
>> + =A0 =A0 =A0 .read_time =3D ds3232_read_time,
>> + =A0 =A0 =A0 .set_time =3D ds3232_set_time,
>> + =A0 =A0 =A0 .read_alarm =3D ds3232_read_alarm,
>> + =A0 =A0 =A0 .set_alarm =3D ds3232_set_alarm,
>> +};
>
> I sew that you have discarded the .ioctl function, which is used to
> enable/disable the alarm irq in previous driver patch.
> but,at the same time, you didnot implement .alarm_irq_enable function
> , so is there no need to enable or disable the alarm irq bit in
> current version patch?
Right. This is not needed in current patch. Alarm is not used and tested.
Thanks.
Roy
^ permalink raw reply
* Re: [rtc-linux] [PATCH v2] rtc: add support for DS3232 RTC
From: Wan ZongShun @ 2010-07-12 7:08 UTC (permalink / raw)
To: rtc-linux, Andrew Morton; +Cc: Mingkai.hu, linuxppc-dev, B11780
In-Reply-To: <1278909366-15903-1-git-send-email-tie-fei.zang@freescale.com>
2010/7/12 Roy Zang <tie-fei.zang@freescale.com>:
> This patch adds the driver for RTC chip DS3232 via I2C bus.
>
I noted this patch is the second version, maybe you can describe which
modifications you have done here.
and add [PATCH v2] to mail subject.
> Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
> Signed-off-by: Jingchang Lu <b22599@freescale.com>
> Signed-off-by: Srikanth Srinivasan <srikanth.srinivasan@freescale.com>
> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
> ---
> Tested on MPC8536DS and P4080DS board
>
> =C2=A0drivers/rtc/Kconfig =C2=A0 =C2=A0 =C2=A0| =C2=A0 11 ++
> =C2=A0drivers/rtc/Makefile =C2=A0 =C2=A0 | =C2=A0 =C2=A01 +
> =C2=A0drivers/rtc/rtc-ds3232.c | =C2=A0427 ++++++++++++++++++++++++++++++=
++++++++++++++++
> =C2=A03 files changed, 439 insertions(+), 0 deletions(-)
> =C2=A0create mode 100644 drivers/rtc/rtc-ds3232.c
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 6a13037..13c2fdb 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -166,6 +166,17 @@ config RTC_DRV_DS1672
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0This driver can also be built as a modu=
le. If so, the module
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0will be called rtc-ds1672.
>
> +config RTC_DRV_DS3232
> + =C2=A0 =C2=A0 =C2=A0 tristate "Dallas/Maxim DS3232"
> + =C2=A0 =C2=A0 =C2=A0 depends on RTC_CLASS && I2C
> + =C2=A0 =C2=A0 =C2=A0 help
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 If you say yes here you get support for Dal=
las Semiconductor
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 DS3232 real-time clock chips. =C2=A0If an i=
nterrupt is associated
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 with the device, the alarm functionality is=
supported.
> +
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 This driver can also be built as a module. =
=C2=A0If so, the module
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 will be called rtc-ds3232.
> +
> =C2=A0config RTC_DRV_MAX6900
> =C2=A0 =C2=A0 =C2=A0 =C2=A0tristate "Maxim MAX6900"
> =C2=A0 =C2=A0 =C2=A0 =C2=A0help
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 44ef194..0af190c 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -39,6 +39,7 @@ obj-$(CONFIG_RTC_DRV_DS1511) =C2=A0+=3D rtc-ds1511.o
> =C2=A0obj-$(CONFIG_RTC_DRV_DS1553) =C2=A0 +=3D rtc-ds1553.o
> =C2=A0obj-$(CONFIG_RTC_DRV_DS1672) =C2=A0 +=3D rtc-ds1672.o
> =C2=A0obj-$(CONFIG_RTC_DRV_DS1742) =C2=A0 +=3D rtc-ds1742.o
> +obj-$(CONFIG_RTC_DRV_DS3232) =C2=A0 +=3D rtc-ds3232.o
> =C2=A0obj-$(CONFIG_RTC_DRV_DS3234) =C2=A0 +=3D rtc-ds3234.o
> =C2=A0obj-$(CONFIG_RTC_DRV_EFI) =C2=A0 =C2=A0 =C2=A0+=3D rtc-efi.o
> =C2=A0obj-$(CONFIG_RTC_DRV_EP93XX) =C2=A0 +=3D rtc-ep93xx.o
> diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
> new file mode 100644
> index 0000000..e36ec1c
> --- /dev/null
> +++ b/drivers/rtc/rtc-ds3232.c
> @@ -0,0 +1,427 @@
> +/*
> + * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over I2=
C
> + *
> + * Copyright (C) 2009-2010 Freescale Semiconductor.
> + *
> + * This program is free software; you can redistribute =C2=A0it and/or m=
odify it
> + * under =C2=A0the terms of =C2=A0the GNU General =C2=A0Public License a=
s published by the
> + * Free Software Foundation; =C2=A0either version 2 of the =C2=A0License=
, or (at your
> + * option) any later version.
> + */
> +/*
> + * It would be more efficient to use i2c msgs/i2c_transfer directly but,=
as
> + * recommened in .../Documentation/i2c/writing-clients section
> + * "Sending and receiving", using SMBus level communication is preferred=
.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/rtc.h>
> +#include <linux/bcd.h>
> +#include <linux/workqueue.h>
> +#include <linux/slab.h>
> +
> +#define DS3232_REG_SECONDS =C2=A0 =C2=A0 0x00
> +#define DS3232_REG_MINUTES =C2=A0 =C2=A0 0x01
> +#define DS3232_REG_HOURS =C2=A0 =C2=A0 =C2=A0 0x02
> +#define DS3232_REG_AMPM =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A00x02
> +#define DS3232_REG_DAY =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x03
> +#define DS3232_REG_DATE =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A00x04
> +#define DS3232_REG_MONTH =C2=A0 =C2=A0 =C2=A0 0x05
> +#define DS3232_REG_CENTURY =C2=A0 =C2=A0 0x05
> +#define DS3232_REG_YEAR =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A00x06
> +#define DS3232_REG_ALARM1 =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x07 /* Alarm 1 BA=
SE */
> +#define DS3232_REG_ALARM2 =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x0B /* Alarm 2 BA=
SE */
> +#define DS3232_REG_CR =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x0E =C2=A0 =C2=
=A0/* Control register */
> +# =C2=A0 =C2=A0 =C2=A0define DS3232_REG_CR_nEOSC =C2=A0 =C2=A0 =C2=A0 =
=C2=A00x80
> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_CR_INTCN =C2=A0 =C2=A0 =C2=A0 =
=C2=A00x04
> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_CR_A2IE =C2=A0 =C2=A0 =C2=A0 =
=C2=A00x02
> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_CR_A1IE =C2=A0 =C2=A0 =C2=A0 =
=C2=A00x01
> +
> +#define DS3232_REG_SR =C2=A00x0F =C2=A0 =C2=A0/* control/status register=
*/
> +# =C2=A0 =C2=A0 =C2=A0define DS3232_REG_SR_OSF =C2=A0 0x80
> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_SR_BSY =C2=A0 0x04
> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_SR_A2F =C2=A0 0x02
> +# =C2=A0 =C2=A0 =C2=A0 define DS3232_REG_SR_A1F =C2=A0 0x01
> +
> +struct ds3232 {
> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client;
> + =C2=A0 =C2=A0 =C2=A0 struct rtc_device *rtc;
> + =C2=A0 =C2=A0 =C2=A0 struct work_struct work;
> +
> + =C2=A0 =C2=A0 =C2=A0 /* The mutex protects alarm operations, and preven=
ts a race
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* between the enable_irq() in the workqueue =
and the free_irq()
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* in the remove function.
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
> + =C2=A0 =C2=A0 =C2=A0 struct mutex mutex;
> + =C2=A0 =C2=A0 =C2=A0 int exiting;
> +};
> +
> +static struct i2c_driver ds3232_driver;
> +
> +static int ds3232_check_rtc_status(struct i2c_client *client)
> +{
> + =C2=A0 =C2=A0 =C2=A0 int ret =3D 0;
> + =C2=A0 =C2=A0 =C2=A0 int control, stat;
> +
> + =C2=A0 =C2=A0 =C2=A0 stat =3D i2c_smbus_read_byte_data(client, DS3232_R=
EG_SR);
> + =C2=A0 =C2=A0 =C2=A0 if (stat < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return stat;
> +
> + =C2=A0 =C2=A0 =C2=A0 if (stat & DS3232_REG_SR_OSF)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_warn(&client->dev,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "oscillator discontinuity flagged, "
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "time unreliable\n");
> +
> + =C2=A0 =C2=A0 =C2=A0 stat &=3D ~(DS3232_REG_SR_OSF | DS3232_REG_SR_A1F =
| DS3232_REG_SR_A2F);
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_write_byte_data(client, DS3232_R=
EG_SR, stat);
> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret;
> +
> + =C2=A0 =C2=A0 =C2=A0 /* If the alarm is pending, clear it before reques=
ting
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* the interrupt, so an interrupt event isn't=
reported
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* before everything is initialized.
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
> +
> + =C2=A0 =C2=A0 =C2=A0 control =3D i2c_smbus_read_byte_data(client, DS323=
2_REG_CR);
> + =C2=A0 =C2=A0 =C2=A0 if (control < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return control;
> +
> + =C2=A0 =C2=A0 =C2=A0 control &=3D ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_=
A2IE);
> + =C2=A0 =C2=A0 =C2=A0 control |=3D DS3232_REG_CR_INTCN;
> +
> + =C2=A0 =C2=A0 =C2=A0 return i2c_smbus_write_byte_data(client, DS3232_RE=
G_CR, control);
> +}
> +
> +static int ds3232_read_time(struct device *dev, struct rtc_time *time)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D to_i2c_client(dev);
> + =C2=A0 =C2=A0 =C2=A0 int ret;
> + =C2=A0 =C2=A0 =C2=A0 u8 buf[7];
> + =C2=A0 =C2=A0 =C2=A0 unsigned int year, month, day, hour, minute, secon=
d;
> + =C2=A0 =C2=A0 =C2=A0 unsigned int week, twelve_hr, am_pm;
> + =C2=A0 =C2=A0 =C2=A0 unsigned int century, add_century =3D 0;
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_read_i2c_block_data(client, DS32=
32_REG_SECONDS, 7, buf);
> +
> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret;
> + =C2=A0 =C2=A0 =C2=A0 if (ret < 7)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -EIO;
> +
> + =C2=A0 =C2=A0 =C2=A0 second =3D buf[0];
> + =C2=A0 =C2=A0 =C2=A0 minute =3D buf[1];
> + =C2=A0 =C2=A0 =C2=A0 hour =3D buf[2];
> + =C2=A0 =C2=A0 =C2=A0 week =3D buf[3];
> + =C2=A0 =C2=A0 =C2=A0 day =3D buf[4];
> + =C2=A0 =C2=A0 =C2=A0 month =3D buf[5];
> + =C2=A0 =C2=A0 =C2=A0 year =3D buf[6];
> +
> + =C2=A0 =C2=A0 =C2=A0 /* Extract additional information for AM/PM and ce=
ntury */
> +
> + =C2=A0 =C2=A0 =C2=A0 twelve_hr =3D hour & 0x40;
> + =C2=A0 =C2=A0 =C2=A0 am_pm =3D hour & 0x20;
> + =C2=A0 =C2=A0 =C2=A0 century =3D month & 0x80;
> +
> + =C2=A0 =C2=A0 =C2=A0 /* Write to rtc_time structure */
> +
> + =C2=A0 =C2=A0 =C2=A0 time->tm_sec =3D bcd2bin(second);
> + =C2=A0 =C2=A0 =C2=A0 time->tm_min =3D bcd2bin(minute);
> + =C2=A0 =C2=A0 =C2=A0 if (twelve_hr) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Convert to 24 hr */
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (am_pm)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 time->tm_hour =3D bcd2bin(hour & 0x1F) + 12;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 else
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 time->tm_hour =3D bcd2bin(hour & 0x1F);
> + =C2=A0 =C2=A0 =C2=A0 } else {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 time->tm_hour =3D bcd2=
bin(hour);
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 time->tm_wday =3D bcd2bin(week);
> + =C2=A0 =C2=A0 =C2=A0 time->tm_mday =3D bcd2bin(day);
> + =C2=A0 =C2=A0 =C2=A0 time->tm_mon =3D bcd2bin(month & 0x7F);
> + =C2=A0 =C2=A0 =C2=A0 if (century)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 add_century =3D 100;
> +
> + =C2=A0 =C2=A0 =C2=A0 time->tm_year =3D bcd2bin(year) + add_century;
> +
> + =C2=A0 =C2=A0 =C2=A0 return rtc_valid_tm(time);
> +}
> +
> +static int ds3232_set_time(struct device *dev, struct rtc_time *time)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D to_i2c_client(dev);
> + =C2=A0 =C2=A0 =C2=A0 u8 buf[7];
> +
> + =C2=A0 =C2=A0 =C2=A0 /* Extract time from rtc_time and load into ds3232=
*/
> +
> + =C2=A0 =C2=A0 =C2=A0 buf[0] =3D bin2bcd(time->tm_sec);
> + =C2=A0 =C2=A0 =C2=A0 buf[1] =3D bin2bcd(time->tm_min);
> + =C2=A0 =C2=A0 =C2=A0 buf[2] =3D bin2bcd(time->tm_hour);
> + =C2=A0 =C2=A0 =C2=A0 buf[3] =3D bin2bcd(time->tm_wday); /* Day of the w=
eek */
> + =C2=A0 =C2=A0 =C2=A0 buf[4] =3D bin2bcd(time->tm_mday); /* Date */
> + =C2=A0 =C2=A0 =C2=A0 buf[5] =3D bin2bcd(time->tm_mon);
> + =C2=A0 =C2=A0 =C2=A0 if (time->tm_year >=3D 100) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 buf[5] |=3D 0x80;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 buf[6] =3D bin2bcd(tim=
e->tm_year - 100);
> + =C2=A0 =C2=A0 =C2=A0 } else {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 buf[6] =3D bin2bcd(tim=
e->tm_year);
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 return i2c_smbus_write_i2c_block_data(client,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 DS3232_REG_SECONDS, 7, buf);
> +}
> +
> +/*
> + * DS3232 has two alarm, we only use alarm1
> + * According to linux specification, only support one-shot alarm
> + * no periodic alarm mode
> + */
> +static int ds3232_read_alarm(struct device *dev, struct rtc_wkalrm *alar=
m)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D to_i2c_client(dev);
> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(clien=
t);
> + =C2=A0 =C2=A0 =C2=A0 int control, stat;
> + =C2=A0 =C2=A0 =C2=A0 int ret =3D 0;
> + =C2=A0 =C2=A0 =C2=A0 u8 buf[4];
> +
> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&ds3232->mutex);
> + =C2=A0 =C2=A0 =C2=A0 stat =3D ret =3D i2c_smbus_read_byte_data(client, =
DS3232_REG_SR);
> + =C2=A0 =C2=A0 =C2=A0 if (stat < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
> +
> + =C2=A0 =C2=A0 =C2=A0 control =3D ret =3D i2c_smbus_read_byte_data(clien=
t, DS3232_REG_CR);
> + =C2=A0 =C2=A0 =C2=A0 if (control < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_read_i2c_block_data(client, DS32=
32_REG_ALARM1, 4, buf);
> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
> +
> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_sec =3D bcd2bin(buf[0] & 0x7F);
> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_min =3D bcd2bin(buf[1] & 0x7F);
> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_hour =3D bcd2bin(buf[2] & 0x7F);
> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_mday =3D bcd2bin(buf[3] & 0x7F);
> +
> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_mon =3D -1;
> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_year =3D -1;
> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_wday =3D -1;
> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_yday =3D -1;
> + =C2=A0 =C2=A0 =C2=A0 alarm->time.tm_isdst =3D -1;
> +
> + =C2=A0 =C2=A0 =C2=A0 alarm->enabled =3D !!(control & DS3232_REG_CR_A1IE=
);
> + =C2=A0 =C2=A0 =C2=A0 alarm->pending =3D !!(stat & DS3232_REG_SR_A1F);
> +out:
> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&ds3232->mutex);
> +
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +/*
> + * linux rtc-module does not support wday alarm
> + * and only 24h time mode supported indeed
> + */
> +static int ds3232_set_alarm(struct device *dev, struct rtc_wkalrm *alarm=
)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D to_i2c_client(dev);
> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(clien=
t);
> + =C2=A0 =C2=A0 =C2=A0 int control, stat;
> + =C2=A0 =C2=A0 =C2=A0 int ret =3D 0;
> + =C2=A0 =C2=A0 =C2=A0 u8 buf[4];
> +
> + =C2=A0 =C2=A0 =C2=A0 if (client->irq <=3D 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -EINVAL;
> +
> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&ds3232->mutex);
> +
> + =C2=A0 =C2=A0 =C2=A0 buf[0] =3D bin2bcd(alarm->time.tm_sec);
> + =C2=A0 =C2=A0 =C2=A0 buf[1] =3D bin2bcd(alarm->time.tm_min);
> + =C2=A0 =C2=A0 =C2=A0 buf[2] =3D bin2bcd(alarm->time.tm_hour);
> + =C2=A0 =C2=A0 =C2=A0 buf[3] =3D bin2bcd(alarm->time.tm_mday);
> +
> + =C2=A0 =C2=A0 =C2=A0 /* clear alarm interrupt enable bit */
> + =C2=A0 =C2=A0 =C2=A0 ret =3D control =3D i2c_smbus_read_byte_data(clien=
t, DS3232_REG_CR);
> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
> + =C2=A0 =C2=A0 =C2=A0 control &=3D ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_=
A2IE);
> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_write_byte_data(client, DS3232_R=
EG_CR, control);
> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out;
> +
> + =C2=A0 =C2=A0 =C2=A0 /* clear any pending alarm flag */
> + =C2=A0 =C2=A0 =C2=A0 stat =3D i2c_smbus_read_byte_data(client, DS3232_R=
EG_SR);
> + =C2=A0 =C2=A0 =C2=A0 if (stat < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return stat;
> +
> + =C2=A0 =C2=A0 =C2=A0 stat &=3D ~(DS3232_REG_SR_A1F | DS3232_REG_SR_A2F)=
;
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_write_byte_data(client, DS3232_R=
EG_SR, stat);
> + =C2=A0 =C2=A0 =C2=A0 if (ret < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return ret;
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_write_i2c_block_data(client,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =C2=A0 DS3232_REG_ALARM1, 4, buf);
> +
> + =C2=A0 =C2=A0 =C2=A0 if (alarm->enabled) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 control |=3D DS3232_RE=
G_CR_A1IE;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D i2c_smbus_writ=
e_byte_data(client, DS3232_REG_CR, control);
> + =C2=A0 =C2=A0 =C2=A0 }
> +out:
> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&ds3232->mutex);
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +static irqreturn_t ds3232_irq(int irq, void *dev_id)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D dev_id;
> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(clien=
t);
> +
> + =C2=A0 =C2=A0 =C2=A0 disable_irq_nosync(irq);
> + =C2=A0 =C2=A0 =C2=A0 schedule_work(&ds3232->work);
> + =C2=A0 =C2=A0 =C2=A0 return IRQ_HANDLED;
> +}
> +
> +static void ds3232_work(struct work_struct *work)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D container_of(work, struc=
t ds3232, work);
> + =C2=A0 =C2=A0 =C2=A0 struct i2c_client *client =3D ds3232->client;
> + =C2=A0 =C2=A0 =C2=A0 int stat, control;
> +
> + =C2=A0 =C2=A0 =C2=A0 mutex_lock(&ds3232->mutex);
> +
> + =C2=A0 =C2=A0 =C2=A0 stat =3D i2c_smbus_read_byte_data(client, DS3232_R=
EG_SR);
> + =C2=A0 =C2=A0 =C2=A0 if (stat < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto unlock;
> +
> + =C2=A0 =C2=A0 =C2=A0 if (stat & DS3232_REG_SR_A1F) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 control =3D i2c_smbus_=
read_byte_data(client, DS3232_REG_CR);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (control < 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 goto out;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* disable alarm1 inte=
rrupt */
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 control &=3D ~(DS3232_=
REG_CR_A1IE);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 i2c_smbus_write_byte_d=
ata(client, DS3232_REG_CR, control);
> +
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* clear the alarm pen=
d flag */
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 stat &=3D ~DS3232_REG_=
SR_A1F;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 i2c_smbus_write_byte_d=
ata(client, DS3232_REG_SR, stat);
> +
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 rtc_update_irq(ds3232-=
>rtc, 1, RTC_AF | RTC_IRQF);
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> +out:
> + =C2=A0 =C2=A0 =C2=A0 if (!ds3232->exiting)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 enable_irq(client->irq=
);
> +unlock:
> + =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&ds3232->mutex);
> +}
> +
> +static const struct rtc_class_ops ds3232_rtc_ops =3D {
> + =C2=A0 =C2=A0 =C2=A0 .read_time =3D ds3232_read_time,
> + =C2=A0 =C2=A0 =C2=A0 .set_time =3D ds3232_set_time,
> + =C2=A0 =C2=A0 =C2=A0 .read_alarm =3D ds3232_read_alarm,
> + =C2=A0 =C2=A0 =C2=A0 .set_alarm =3D ds3232_set_alarm,
> +};
I sew that you have discarded the .ioctl function, which is used to
enable/disable the alarm irq in previous driver patch.
but,at the same time, you didnot implement .alarm_irq_enable function
, so is there no need to enable or disable the alarm irq bit in
current version patch?
> +
> +static int __devinit ds3232_probe(struct i2c_client *client,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 const struct i2c_devic=
e_id *id)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232;
> + =C2=A0 =C2=A0 =C2=A0 int ret;
> +
> + =C2=A0 =C2=A0 =C2=A0 ds3232 =3D kzalloc(sizeof(struct ds3232), GFP_KERN=
EL);
> + =C2=A0 =C2=A0 =C2=A0 if (!ds3232)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return -ENOMEM;
> +
> + =C2=A0 =C2=A0 =C2=A0 ds3232->client =3D client;
> + =C2=A0 =C2=A0 =C2=A0 i2c_set_clientdata(client, ds3232);
> +
> + =C2=A0 =C2=A0 =C2=A0 INIT_WORK(&ds3232->work, ds3232_work);
> + =C2=A0 =C2=A0 =C2=A0 mutex_init(&ds3232->mutex);
> +
> + =C2=A0 =C2=A0 =C2=A0 ret =3D ds3232_check_rtc_status(client);
> + =C2=A0 =C2=A0 =C2=A0 if (ret)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out_free;
> +
> + =C2=A0 =C2=A0 =C2=A0 ds3232->rtc =3D rtc_device_register(client->name, =
&client->dev,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 &ds32=
32_rtc_ops, THIS_MODULE);
> + =C2=A0 =C2=A0 =C2=A0 if (IS_ERR(ds3232->rtc)) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D PTR_ERR(ds3232=
->rtc);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 dev_err(&client->dev, =
"unable to register the class device\n");
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 goto out_irq;
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 if (client->irq >=3D 0) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ret =3D request_irq(cl=
ient->irq, ds3232_irq, 0,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0"ds3232", client);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (ret) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 dev_err(&client->dev, "unable to request IRQ\n");
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =
=C2=A0 goto out_free;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 return 0;
> +
> +out_irq:
> + =C2=A0 =C2=A0 =C2=A0 if (client->irq >=3D 0)
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 free_irq(client->irq, =
client);
> +
> +out_free:
> + =C2=A0 =C2=A0 =C2=A0 i2c_set_clientdata(client, NULL);
> + =C2=A0 =C2=A0 =C2=A0 kfree(ds3232);
> + =C2=A0 =C2=A0 =C2=A0 return ret;
> +}
> +
> +static int __devexit ds3232_remove(struct i2c_client *client)
> +{
> + =C2=A0 =C2=A0 =C2=A0 struct ds3232 *ds3232 =3D i2c_get_clientdata(clien=
t);
> +
> + =C2=A0 =C2=A0 =C2=A0 if (client->irq >=3D 0) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mutex_lock(&ds3232->mu=
tex);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ds3232->exiting =3D 1;
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mutex_unlock(&ds3232->=
mutex);
> +
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 free_irq(client->irq, =
client);
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 flush_scheduled_work()=
;
> + =C2=A0 =C2=A0 =C2=A0 }
> +
> + =C2=A0 =C2=A0 =C2=A0 rtc_device_unregister(ds3232->rtc);
> + =C2=A0 =C2=A0 =C2=A0 i2c_set_clientdata(client, NULL);
> + =C2=A0 =C2=A0 =C2=A0 kfree(ds3232);
> + =C2=A0 =C2=A0 =C2=A0 return 0;
> +}
> +
> +static const struct i2c_device_id ds3232_id[] =3D {
> + =C2=A0 =C2=A0 =C2=A0 { "ds3232", 0 },
> + =C2=A0 =C2=A0 =C2=A0 { }
> +};
> +MODULE_DEVICE_TABLE(i2c, ds3232_id);
> +
> +static struct i2c_driver ds3232_driver =3D {
> + =C2=A0 =C2=A0 =C2=A0 .driver =3D {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 .name =3D "rtc-ds3232"=
,
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 .owner =3D THIS_MODULE=
,
> + =C2=A0 =C2=A0 =C2=A0 },
> + =C2=A0 =C2=A0 =C2=A0 .probe =3D ds3232_probe,
> + =C2=A0 =C2=A0 =C2=A0 .remove =3D __devexit_p(ds3232_remove),
> + =C2=A0 =C2=A0 =C2=A0 .id_table =3D ds3232_id,
> +};
> +
> +static int __init ds3232_init(void)
> +{
> + =C2=A0 =C2=A0 =C2=A0 return i2c_add_driver(&ds3232_driver);
> +}
> +
> +static void __exit ds3232_exit(void)
> +{
> + =C2=A0 =C2=A0 =C2=A0 i2c_del_driver(&ds3232_driver);
> +}
> +
> +module_init(ds3232_init);
> +module_exit(ds3232_exit);
> +
> +MODULE_AUTHOR("Srikanth Srinivasan <srikanth.srinivasan@freescale.com>")=
;
> +MODULE_DESCRIPTION("Maxim/Dallas DS3232 RTC Driver");
> +MODULE_LICENSE("GPL");
> --
> 1.5.6.5
>
> --
> You received this message because you are subscribed to "rtc-linux".
> Membership options at http://groups.google.com/group/rtc-linux .
> Please read http://groups.google.com/group/rtc-linux/web/checklist
> before submitting a driver.
--=20
*linux-arm-kernel mailing list
mail addr:linux-arm-kernel@lists.infradead.org
you can subscribe by:
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
* linux-arm-NUC900 mailing list
mail addr:NUC900@googlegroups.com
main web: https://groups.google.com/group/NUC900
you can subscribe it by sending me mail:
mcuos.com@gmail.com
^ permalink raw reply
* Re: kernel boot stuck at udbg_putc_cpm()
From: Shawn Jin @ 2010-07-12 6:26 UTC (permalink / raw)
To: Scott Wood; +Cc: ppcdev
In-Reply-To: <20100709105946.68832b3e@schlenkerla.am.freescale.net>
> You're probably getting to the point where udbg is disabled because the
> real serial driver is trying to take over -- and something's going
> wrong with the real serial port driver. =A0Check to make sure the brg
> config is correct (both the input clock and the baud rate you're trying
> to switch to). =A0Commenting out the call to cpm_set_brg can be
> a quick way of determining if that's the problem.
It seems that the last CP command RESTART_TX never completes in the
cpm_uart_console_setup(). I commented out the writes to brgc1 in
cpm_setbrg() in cpm1.c so that the brgc1 value stays the same as
previously set.
The registers related to SMC1 are dumped below before the last
RESTART_TX command. The CPCR was 0x0090. But after issuing the
RESTART_TX command the CPCR kept at 0x0691. Is there any other obvious
reason for CPM not completing the command? It got to be something
related to the settings.
BDI>rd cpcr
cpcr : 0x0090 144
BDI>rd rccr
rccr : 0x0001 1
BDI>rd rmds
rmds : 0x00 0
BDI>rd rctr1
rctr1 : 0x0000 0
BDI>rd rctr2
rctr2 : 0x0000 0
BDI>rd rctr3
rctr3 : 0x802e -32722
BDI>rd rctr4
rctr4 : 0x802c -32724
BDI>rd rter
rter : 0x0000 0
BDI>rd rtmr
rtmr : 0x0000 0
BDI>rd brgc1
brgc1 : 0x00010618 67096
BDI>rd smcmr1
smcmr1 : 0x4823 18467
BDI>rd smce1
smce1 : 0x00 0
BDI>rd smcm1
smcm1 : 0x00 0
Thanks,
-Shawn.
^ permalink raw reply
* [PATCH v2] rtc: add support for DS3232 RTC
From: Roy Zang @ 2010-07-12 4:36 UTC (permalink / raw)
To: rtc-linux; +Cc: Mingkai.hu, linuxppc-dev, B11780
This patch adds the driver for RTC chip DS3232 via I2C bus.
Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
Signed-off-by: Jingchang Lu <b22599@freescale.com>
Signed-off-by: Srikanth Srinivasan <srikanth.srinivasan@freescale.com>
Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
---
Tested on MPC8536DS and P4080DS board
drivers/rtc/Kconfig | 11 ++
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-ds3232.c | 427 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 439 insertions(+), 0 deletions(-)
create mode 100644 drivers/rtc/rtc-ds3232.c
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 6a13037..13c2fdb 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -166,6 +166,17 @@ config RTC_DRV_DS1672
This driver can also be built as a module. If so, the module
will be called rtc-ds1672.
+config RTC_DRV_DS3232
+ tristate "Dallas/Maxim DS3232"
+ depends on RTC_CLASS && I2C
+ help
+ If you say yes here you get support for Dallas Semiconductor
+ DS3232 real-time clock chips. If an interrupt is associated
+ with the device, the alarm functionality is supported.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds3232.
+
config RTC_DRV_MAX6900
tristate "Maxim MAX6900"
help
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 44ef194..0af190c 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o
obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
+obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o
obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
new file mode 100644
index 0000000..e36ec1c
--- /dev/null
+++ b/drivers/rtc/rtc-ds3232.c
@@ -0,0 +1,427 @@
+/*
+ * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over I2C
+ *
+ * Copyright (C) 2009-2010 Freescale Semiconductor.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+/*
+ * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
+ * recommened in .../Documentation/i2c/writing-clients section
+ * "Sending and receiving", using SMBus level communication is preferred.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+
+#define DS3232_REG_SECONDS 0x00
+#define DS3232_REG_MINUTES 0x01
+#define DS3232_REG_HOURS 0x02
+#define DS3232_REG_AMPM 0x02
+#define DS3232_REG_DAY 0x03
+#define DS3232_REG_DATE 0x04
+#define DS3232_REG_MONTH 0x05
+#define DS3232_REG_CENTURY 0x05
+#define DS3232_REG_YEAR 0x06
+#define DS3232_REG_ALARM1 0x07 /* Alarm 1 BASE */
+#define DS3232_REG_ALARM2 0x0B /* Alarm 2 BASE */
+#define DS3232_REG_CR 0x0E /* Control register */
+# define DS3232_REG_CR_nEOSC 0x80
+# define DS3232_REG_CR_INTCN 0x04
+# define DS3232_REG_CR_A2IE 0x02
+# define DS3232_REG_CR_A1IE 0x01
+
+#define DS3232_REG_SR 0x0F /* control/status register */
+# define DS3232_REG_SR_OSF 0x80
+# define DS3232_REG_SR_BSY 0x04
+# define DS3232_REG_SR_A2F 0x02
+# define DS3232_REG_SR_A1F 0x01
+
+struct ds3232 {
+ struct i2c_client *client;
+ struct rtc_device *rtc;
+ struct work_struct work;
+
+ /* The mutex protects alarm operations, and prevents a race
+ * between the enable_irq() in the workqueue and the free_irq()
+ * in the remove function.
+ */
+ struct mutex mutex;
+ int exiting;
+};
+
+static struct i2c_driver ds3232_driver;
+
+static int ds3232_check_rtc_status(struct i2c_client *client)
+{
+ int ret = 0;
+ int control, stat;
+
+ stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+ if (stat < 0)
+ return stat;
+
+ if (stat & DS3232_REG_SR_OSF)
+ dev_warn(&client->dev,
+ "oscillator discontinuity flagged, "
+ "time unreliable\n");
+
+ stat &= ~(DS3232_REG_SR_OSF | DS3232_REG_SR_A1F | DS3232_REG_SR_A2F);
+
+ ret = i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
+ if (ret < 0)
+ return ret;
+
+ /* If the alarm is pending, clear it before requesting
+ * the interrupt, so an interrupt event isn't reported
+ * before everything is initialized.
+ */
+
+ control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+ if (control < 0)
+ return control;
+
+ control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE);
+ control |= DS3232_REG_CR_INTCN;
+
+ return i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+}
+
+static int ds3232_read_time(struct device *dev, struct rtc_time *time)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret;
+ u8 buf[7];
+ unsigned int year, month, day, hour, minute, second;
+ unsigned int week, twelve_hr, am_pm;
+ unsigned int century, add_century = 0;
+
+ ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_SECONDS, 7, buf);
+
+ if (ret < 0)
+ return ret;
+ if (ret < 7)
+ return -EIO;
+
+ second = buf[0];
+ minute = buf[1];
+ hour = buf[2];
+ week = buf[3];
+ day = buf[4];
+ month = buf[5];
+ year = buf[6];
+
+ /* Extract additional information for AM/PM and century */
+
+ twelve_hr = hour & 0x40;
+ am_pm = hour & 0x20;
+ century = month & 0x80;
+
+ /* Write to rtc_time structure */
+
+ time->tm_sec = bcd2bin(second);
+ time->tm_min = bcd2bin(minute);
+ if (twelve_hr) {
+ /* Convert to 24 hr */
+ if (am_pm)
+ time->tm_hour = bcd2bin(hour & 0x1F) + 12;
+ else
+ time->tm_hour = bcd2bin(hour & 0x1F);
+ } else {
+ time->tm_hour = bcd2bin(hour);
+ }
+
+ time->tm_wday = bcd2bin(week);
+ time->tm_mday = bcd2bin(day);
+ time->tm_mon = bcd2bin(month & 0x7F);
+ if (century)
+ add_century = 100;
+
+ time->tm_year = bcd2bin(year) + add_century;
+
+ return rtc_valid_tm(time);
+}
+
+static int ds3232_set_time(struct device *dev, struct rtc_time *time)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u8 buf[7];
+
+ /* Extract time from rtc_time and load into ds3232*/
+
+ buf[0] = bin2bcd(time->tm_sec);
+ buf[1] = bin2bcd(time->tm_min);
+ buf[2] = bin2bcd(time->tm_hour);
+ buf[3] = bin2bcd(time->tm_wday); /* Day of the week */
+ buf[4] = bin2bcd(time->tm_mday); /* Date */
+ buf[5] = bin2bcd(time->tm_mon);
+ if (time->tm_year >= 100) {
+ buf[5] |= 0x80;
+ buf[6] = bin2bcd(time->tm_year - 100);
+ } else {
+ buf[6] = bin2bcd(time->tm_year);
+ }
+
+ return i2c_smbus_write_i2c_block_data(client,
+ DS3232_REG_SECONDS, 7, buf);
+}
+
+/*
+ * DS3232 has two alarm, we only use alarm1
+ * According to linux specification, only support one-shot alarm
+ * no periodic alarm mode
+ */
+static int ds3232_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+ int control, stat;
+ int ret = 0;
+ u8 buf[4];
+
+ mutex_lock(&ds3232->mutex);
+ stat = ret = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+ if (stat < 0)
+ goto out;
+
+ control = ret = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+ if (control < 0)
+ goto out;
+
+ ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf);
+ if (ret < 0)
+ goto out;
+
+ alarm->time.tm_sec = bcd2bin(buf[0] & 0x7F);
+ alarm->time.tm_min = bcd2bin(buf[1] & 0x7F);
+ alarm->time.tm_hour = bcd2bin(buf[2] & 0x7F);
+ alarm->time.tm_mday = bcd2bin(buf[3] & 0x7F);
+
+ alarm->time.tm_mon = -1;
+ alarm->time.tm_year = -1;
+ alarm->time.tm_wday = -1;
+ alarm->time.tm_yday = -1;
+ alarm->time.tm_isdst = -1;
+
+ alarm->enabled = !!(control & DS3232_REG_CR_A1IE);
+ alarm->pending = !!(stat & DS3232_REG_SR_A1F);
+out:
+ mutex_unlock(&ds3232->mutex);
+
+ return ret;
+}
+
+/*
+ * linux rtc-module does not support wday alarm
+ * and only 24h time mode supported indeed
+ */
+static int ds3232_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+ int control, stat;
+ int ret = 0;
+ u8 buf[4];
+
+ if (client->irq <= 0)
+ return -EINVAL;
+
+ mutex_lock(&ds3232->mutex);
+
+ buf[0] = bin2bcd(alarm->time.tm_sec);
+ buf[1] = bin2bcd(alarm->time.tm_min);
+ buf[2] = bin2bcd(alarm->time.tm_hour);
+ buf[3] = bin2bcd(alarm->time.tm_mday);
+
+ /* clear alarm interrupt enable bit */
+ ret = control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+ if (ret < 0)
+ goto out;
+ control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE);
+ ret = i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+ if (ret < 0)
+ goto out;
+
+ /* clear any pending alarm flag */
+ stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+ if (stat < 0)
+ return stat;
+
+ stat &= ~(DS3232_REG_SR_A1F | DS3232_REG_SR_A2F);
+
+ ret = i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_i2c_block_data(client,
+ DS3232_REG_ALARM1, 4, buf);
+
+ if (alarm->enabled) {
+ control |= DS3232_REG_CR_A1IE;
+ ret = i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+ }
+out:
+ mutex_unlock(&ds3232->mutex);
+ return ret;
+}
+
+static irqreturn_t ds3232_irq(int irq, void *dev_id)
+{
+ struct i2c_client *client = dev_id;
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+
+ disable_irq_nosync(irq);
+ schedule_work(&ds3232->work);
+ return IRQ_HANDLED;
+}
+
+static void ds3232_work(struct work_struct *work)
+{
+ struct ds3232 *ds3232 = container_of(work, struct ds3232, work);
+ struct i2c_client *client = ds3232->client;
+ int stat, control;
+
+ mutex_lock(&ds3232->mutex);
+
+ stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+ if (stat < 0)
+ goto unlock;
+
+ if (stat & DS3232_REG_SR_A1F) {
+ control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+ if (control < 0)
+ goto out;
+ /* disable alarm1 interrupt */
+ control &= ~(DS3232_REG_CR_A1IE);
+ i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+
+ /* clear the alarm pend flag */
+ stat &= ~DS3232_REG_SR_A1F;
+ i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
+
+ rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF);
+ }
+
+out:
+ if (!ds3232->exiting)
+ enable_irq(client->irq);
+unlock:
+ mutex_unlock(&ds3232->mutex);
+}
+
+static const struct rtc_class_ops ds3232_rtc_ops = {
+ .read_time = ds3232_read_time,
+ .set_time = ds3232_set_time,
+ .read_alarm = ds3232_read_alarm,
+ .set_alarm = ds3232_set_alarm,
+};
+
+static int __devinit ds3232_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ds3232 *ds3232;
+ int ret;
+
+ ds3232 = kzalloc(sizeof(struct ds3232), GFP_KERNEL);
+ if (!ds3232)
+ return -ENOMEM;
+
+ ds3232->client = client;
+ i2c_set_clientdata(client, ds3232);
+
+ INIT_WORK(&ds3232->work, ds3232_work);
+ mutex_init(&ds3232->mutex);
+
+ ret = ds3232_check_rtc_status(client);
+ if (ret)
+ goto out_free;
+
+ ds3232->rtc = rtc_device_register(client->name, &client->dev,
+ &ds3232_rtc_ops, THIS_MODULE);
+ if (IS_ERR(ds3232->rtc)) {
+ ret = PTR_ERR(ds3232->rtc);
+ dev_err(&client->dev, "unable to register the class device\n");
+ goto out_irq;
+ }
+
+ if (client->irq >= 0) {
+ ret = request_irq(client->irq, ds3232_irq, 0,
+ "ds3232", client);
+ if (ret) {
+ dev_err(&client->dev, "unable to request IRQ\n");
+ goto out_free;
+ }
+ }
+
+ return 0;
+
+out_irq:
+ if (client->irq >= 0)
+ free_irq(client->irq, client);
+
+out_free:
+ i2c_set_clientdata(client, NULL);
+ kfree(ds3232);
+ return ret;
+}
+
+static int __devexit ds3232_remove(struct i2c_client *client)
+{
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+
+ if (client->irq >= 0) {
+ mutex_lock(&ds3232->mutex);
+ ds3232->exiting = 1;
+ mutex_unlock(&ds3232->mutex);
+
+ free_irq(client->irq, client);
+ flush_scheduled_work();
+ }
+
+ rtc_device_unregister(ds3232->rtc);
+ i2c_set_clientdata(client, NULL);
+ kfree(ds3232);
+ return 0;
+}
+
+static const struct i2c_device_id ds3232_id[] = {
+ { "ds3232", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ds3232_id);
+
+static struct i2c_driver ds3232_driver = {
+ .driver = {
+ .name = "rtc-ds3232",
+ .owner = THIS_MODULE,
+ },
+ .probe = ds3232_probe,
+ .remove = __devexit_p(ds3232_remove),
+ .id_table = ds3232_id,
+};
+
+static int __init ds3232_init(void)
+{
+ return i2c_add_driver(&ds3232_driver);
+}
+
+static void __exit ds3232_exit(void)
+{
+ i2c_del_driver(&ds3232_driver);
+}
+
+module_init(ds3232_init);
+module_exit(ds3232_exit);
+
+MODULE_AUTHOR("Srikanth Srinivasan <srikanth.srinivasan@freescale.com>");
+MODULE_DESCRIPTION("Maxim/Dallas DS3232 RTC Driver");
+MODULE_LICENSE("GPL");
--
1.5.6.5
^ permalink raw reply related
* [PATCH 00/12] autoconvert trivial BKL users to private mutex
From: Arnd Bergmann @ 2010-07-11 21:18 UTC (permalink / raw)
To: linux-kernel
Cc: devel, Jesper Nilsson, linux-usb, Arnd Bergmann, Corey Minyard,
linux-cris-kernel, Frederic Weisbecker, Greg Kroah-Hartman,
Karsten Keil, Mauro Carvalho Chehab, linux-scsi, linuxppc-dev,
James E.J. Bottomley, linux-mtd, John Kacur, netdev, linux-media,
openipmi-developer, David S. Miller, David Woodhouse
This is a repost of an earlier patch to remove
those users of the big kernel lock that can be
converted to a mutex using a simple script.
The only use of the BKL is in file operations
that are called without any other lock, so the
new mutex is the top-level serialization
and cannot introduce any AB-BA deadlock.
Please apply to the respective maintainer trees
if the patches look good.
Arnd Bergmann (12):
staging: autoconvert trivial BKL users to private mutex
isdn: autoconvert trivial BKL users to private mutex
scsi: autoconvert trivial BKL users to private mutex
media: autoconvert trivial BKL users to private mutex
usb: autoconvert trivial BKL users to private mutex
net: autoconvert trivial BKL users to private mutex
cris: autoconvert trivial BKL users to private mutex
sbus: autoconvert trivial BKL users to private mutex
mtd: autoconvert trivial BKL users to private mutex
mac: autoconvert trivial BKL users to private mutex
ipmi: autoconvert trivial BKL users to private mutex
drivers: autoconvert trivial BKL users to private mutex
arch/cris/arch-v10/drivers/eeprom.c | 2 -
arch/cris/arch-v10/drivers/i2c.c | 2 -
arch/cris/arch-v32/drivers/cryptocop.c | 2 -
arch/cris/arch-v32/drivers/i2c.c | 12 ++++----
drivers/block/paride/pg.c | 7 ++--
drivers/block/paride/pt.c | 19 ++++++------
drivers/char/apm-emulation.c | 11 ++++---
drivers/char/applicom.c | 9 +++--
drivers/char/ds1302.c | 15 +++++----
drivers/char/ds1620.c | 8 ++--
drivers/char/dsp56k.c | 27 +++++++++--------
drivers/char/dtlk.c | 8 ++--
drivers/char/generic_nvram.c | 7 ++--
drivers/char/genrtc.c | 13 ++++----
drivers/char/i8k.c | 7 ++--
drivers/char/ip2/ip2main.c | 8 ++--
drivers/char/ipmi/ipmi_devintf.c | 14 ++++----
drivers/char/ipmi/ipmi_watchdog.c | 8 ++--
drivers/char/lp.c | 15 +++++----
drivers/char/mbcs.c | 8 ++--
drivers/char/mmtimer.c | 7 ++--
drivers/char/mwave/mwavedd.c | 44 ++++++++++++++--------------
drivers/char/nvram.c | 11 ++++---
drivers/char/nwflash.c | 12 ++++----
drivers/char/pcmcia/cm4000_cs.c | 11 ++++---
drivers/char/pcmcia/cm4040_cs.c | 7 ++--
drivers/char/ppdev.c | 8 ++--
drivers/char/rio/rio_linux.c | 7 ++--
drivers/char/snsc.c | 9 +++--
drivers/char/toshiba.c | 9 +++--
drivers/char/viotape.c | 11 ++++---
drivers/char/xilinx_hwicap/xilinx_hwicap.c | 6 ++--
drivers/hwmon/fschmd.c | 6 ++--
drivers/hwmon/w83793.c | 6 ++--
drivers/input/misc/hp_sdc_rtc.c | 7 ++--
drivers/isdn/capi/capi.c | 6 ++--
drivers/isdn/divert/divert_procfs.c | 7 ++--
drivers/isdn/hardware/eicon/divamnt.c | 7 ++--
drivers/isdn/hardware/eicon/divasi.c | 2 -
drivers/isdn/hardware/eicon/divasmain.c | 2 -
drivers/isdn/hysdn/hysdn_procconf.c | 21 +++++++------
drivers/isdn/hysdn/hysdn_proclog.c | 15 +++++----
drivers/isdn/i4l/isdn_common.c | 27 +++++++++--------
drivers/isdn/mISDN/timerdev.c | 7 ++--
drivers/macintosh/adb.c | 10 +++---
drivers/macintosh/smu.c | 6 ++--
drivers/macintosh/via-pmu.c | 11 ++++---
drivers/media/dvb/bt8xx/dst_ca.c | 7 ++--
drivers/media/video/cx88/cx88-blackbird.c | 13 ++++----
drivers/media/video/dabusb.c | 18 ++++++------
drivers/media/video/se401.c | 9 +++--
drivers/media/video/stradis.c | 9 +++--
drivers/media/video/usbvideo/vicam.c | 14 ++++----
drivers/message/fusion/mptctl.c | 15 +++++----
drivers/message/i2o/i2o_config.c | 23 +++++++-------
drivers/misc/phantom.c | 11 ++++---
drivers/mtd/mtdchar.c | 15 +++++----
drivers/net/ppp_generic.c | 19 ++++++------
drivers/net/wan/cosa.c | 10 +++---
drivers/pci/hotplug/cpqphp_sysfs.c | 13 ++++----
drivers/rtc/rtc-m41t80.c | 13 ++++----
drivers/sbus/char/display7seg.c | 8 ++--
drivers/sbus/char/envctrl.c | 2 -
drivers/sbus/char/flash.c | 15 +++++----
drivers/sbus/char/openprom.c | 15 +++++----
drivers/sbus/char/uctrl.c | 7 ++--
drivers/scsi/3w-9xxx.c | 7 ++--
drivers/scsi/3w-sas.c | 7 ++--
drivers/scsi/3w-xxxx.c | 9 ++---
drivers/scsi/aacraid/linit.c | 15 +++++----
drivers/scsi/ch.c | 8 ++--
drivers/scsi/dpt_i2o.c | 18 ++++++------
drivers/scsi/gdth.c | 11 ++++---
drivers/scsi/megaraid.c | 8 ++--
drivers/scsi/megaraid/megaraid_mm.c | 8 ++--
drivers/scsi/megaraid/megaraid_sas.c | 2 -
drivers/scsi/mpt2sas/mpt2sas_ctl.c | 11 ++++---
drivers/scsi/osst.c | 15 +++++----
drivers/scsi/scsi_tgt_if.c | 2 -
drivers/scsi/sg.c | 11 ++++---
drivers/staging/crystalhd/crystalhd_lnx.c | 9 +++--
drivers/staging/dt3155/dt3155_drv.c | 6 ++-
drivers/staging/vme/devices/vme_user.c | 7 ++--
drivers/telephony/ixj.c | 7 ++--
drivers/usb/gadget/printer.c | 7 ++--
drivers/usb/misc/iowarrior.c | 15 +++++----
drivers/usb/misc/rio500.c | 15 +++++----
drivers/usb/misc/usblcd.c | 16 +++++-----
drivers/watchdog/cpwd.c | 15 +++++----
fs/hfsplus/ioctl.c | 11 ++++---
net/wanrouter/wanmain.c | 7 ++--
net/wanrouter/wanproc.c | 7 ++--
92 files changed, 505 insertions(+), 469 deletions(-)
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Corey Minyard <minyard@acm.org>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: David Woodhouse <David.Woodhouse@intel.com>
Cc: Greg Kroah-Hartman <gregkh@suse.de>
Cc: "James E.J. Bottomley" <James.Bottomley@suse.de>
Cc: Jesper Nilsson <jesper.nilsson@axis.com>
Cc: Karsten Keil <isdn@linux-pingi.de>
Cc: Mauro Carvalho Chehab <mchehab@infradead.org>
Cc: netdev@vger.kernel.org
Cc: openipmi-developer@lists.sourceforge.net
Cc: devel@driverdev.osuosl.org
Cc: linux-cris-kernel@axis.com
Cc: linux-media@vger.kernel.org
Cc: linux-mtd@lists.infradead.org
Cc: linuxppc-dev@ozlabs.org
Cc: linux-scsi@vger.kernel.org
Cc: linux-usb@vger.kernel.org
^ permalink raw reply
* [PATCH 10/12] mac: autoconvert trivial BKL users to private mutex
From: Arnd Bergmann @ 2010-07-11 21:19 UTC (permalink / raw)
To: linux-kernel; +Cc: John Kacur, Frederic Weisbecker, linuxppc-dev, Arnd Bergmann
In-Reply-To: <1278883143-29035-1-git-send-email-arnd@arndb.de>
All these files use the big kernel lock in a trivial
way to serialize their private file operations,
typically resulting from an earlier semi-automatic
pushdown from VFS.
None of these drivers appears to want to lock against
other code, and they all use the BKL as the top-level
lock in their file operations, meaning that there
is no lock-order inversion problem.
Consequently, we can remove the BKL completely,
replacing it with a per-file mutex in every case.
Using a scripted approach means we can avoid
typos.
file=$1
name=$2
if grep -q lock_kernel ${file} ; then
if grep -q 'include.*linux.mutex.h' ${file} ; then
sed -i '/include.*<linux\/smp_lock.h>/d' ${file}
else
sed -i 's/include.*<linux\/smp_lock.h>.*$/include <linux\/mutex.h>/g' ${file}
fi
sed -i ${file} \
-e "/^#include.*linux.mutex.h/,$ {
1,/^\(static\|int\|long\)/ {
/^\(static\|int\|long\)/istatic DEFINE_MUTEX(${name}_mutex);
} }" \
-e "s/\(un\)*lock_kernel\>[ ]*()/mutex_\1lock(\&${name}_mutex)/g" \
-e '/[ ]*cycle_kernel_lock();/d'
else
sed -i -e '/include.*\<smp_lock.h\>/d' ${file} \
-e '/cycle_kernel_lock()/d'
fi
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: linuxppc-dev@ozlabs.org
---
drivers/macintosh/adb.c | 10 +++++-----
drivers/macintosh/smu.c | 6 +++---
drivers/macintosh/via-pmu.c | 11 ++++++-----
3 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c
index 1c4ee6e..e75e3be 100644
--- a/drivers/macintosh/adb.c
+++ b/drivers/macintosh/adb.c
@@ -24,7 +24,6 @@
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/sched.h>
-#include <linux/smp_lock.h>
#include <linux/adb.h>
#include <linux/cuda.h>
#include <linux/pmu.h>
@@ -55,6 +54,7 @@ extern struct adb_driver adb_iop_driver;
extern struct adb_driver via_pmu_driver;
extern struct adb_driver macio_adb_driver;
+static DEFINE_MUTEX(adb_mutex);
static struct adb_driver *adb_driver_list[] = {
#ifdef CONFIG_ADB_MACII
&via_macii_driver,
@@ -647,7 +647,7 @@ static int adb_open(struct inode *inode, struct file *file)
struct adbdev_state *state;
int ret = 0;
- lock_kernel();
+ mutex_lock(&adb_mutex);
if (iminor(inode) > 0 || adb_controller == NULL) {
ret = -ENXIO;
goto out;
@@ -665,7 +665,7 @@ static int adb_open(struct inode *inode, struct file *file)
state->inuse = 1;
out:
- unlock_kernel();
+ mutex_unlock(&adb_mutex);
return ret;
}
@@ -674,7 +674,7 @@ static int adb_release(struct inode *inode, struct file *file)
struct adbdev_state *state = file->private_data;
unsigned long flags;
- lock_kernel();
+ mutex_lock(&adb_mutex);
if (state) {
file->private_data = NULL;
spin_lock_irqsave(&state->lock, flags);
@@ -687,7 +687,7 @@ static int adb_release(struct inode *inode, struct file *file)
spin_unlock_irqrestore(&state->lock, flags);
}
}
- unlock_kernel();
+ mutex_unlock(&adb_mutex);
return 0;
}
diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c
index 2506c95..8775dd4 100644
--- a/drivers/macintosh/smu.c
+++ b/drivers/macintosh/smu.c
@@ -19,7 +19,6 @@
* the userland interface
*/
-#include <linux/smp_lock.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/device.h>
@@ -97,6 +96,7 @@ struct smu_device {
* I don't think there will ever be more than one SMU, so
* for now, just hard code that
*/
+static DEFINE_MUTEX(smu_mutex);
static struct smu_device *smu;
static DEFINE_MUTEX(smu_part_access);
static int smu_irq_inited;
@@ -1095,12 +1095,12 @@ static int smu_open(struct inode *inode, struct file *file)
pp->mode = smu_file_commands;
init_waitqueue_head(&pp->wait);
- lock_kernel();
+ mutex_lock(&smu_mutex);
spin_lock_irqsave(&smu_clist_lock, flags);
list_add(&pp->list, &smu_clist);
spin_unlock_irqrestore(&smu_clist_lock, flags);
file->private_data = pp;
- unlock_kernel();
+ mutex_unlock(&smu_mutex);
return 0;
}
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
index 3d4fc0f..28e4822 100644
--- a/drivers/macintosh/via-pmu.c
+++ b/drivers/macintosh/via-pmu.c
@@ -18,7 +18,7 @@
*
*/
#include <stdarg.h>
-#include <linux/smp_lock.h>
+#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
@@ -72,6 +72,7 @@
/* How many iterations between battery polls */
#define BATTERY_POLLING_COUNT 2
+static DEFINE_MUTEX(pmu_info_proc_mutex);
static volatile unsigned char __iomem *via;
/* VIA registers - spaced 0x200 bytes apart */
@@ -2076,7 +2077,7 @@ pmu_open(struct inode *inode, struct file *file)
pp->rb_get = pp->rb_put = 0;
spin_lock_init(&pp->lock);
init_waitqueue_head(&pp->wait);
- lock_kernel();
+ mutex_lock(&pmu_info_proc_mutex);
spin_lock_irqsave(&all_pvt_lock, flags);
#if defined(CONFIG_INPUT_ADBHID) && defined(CONFIG_PMAC_BACKLIGHT)
pp->backlight_locker = 0;
@@ -2084,7 +2085,7 @@ pmu_open(struct inode *inode, struct file *file)
list_add(&pp->list, &all_pmu_pvt);
spin_unlock_irqrestore(&all_pvt_lock, flags);
file->private_data = pp;
- unlock_kernel();
+ mutex_unlock(&pmu_info_proc_mutex);
return 0;
}
@@ -2341,9 +2342,9 @@ static long pmu_unlocked_ioctl(struct file *filp,
{
int ret;
- lock_kernel();
+ mutex_lock(&pmu_info_proc_mutex);
ret = pmu_ioctl(filp, cmd, arg);
- unlock_kernel();
+ mutex_unlock(&pmu_info_proc_mutex);
return ret;
}
--
1.7.1
^ permalink raw reply related
* Re: [PATCH 1/2] edac: mpc85xx: Fix MPC85xx dependency
From: Kumar Gala @ 2010-07-11 16:20 UTC (permalink / raw)
To: Anton Vorontsov
Cc: Peter Tyser, linux-kernel, Dave Jiang, linuxppc-dev,
Doug Thompson, Andrew Morton
In-Reply-To: <20100708045627.GA32489@oksana.dev.rtsoft.ru>
On Jul 7, 2010, at 11:56 PM, Anton Vorontsov wrote:
> On Wed, Jul 07, 2010 at 02:45:02PM -0700, Andrew Morton wrote:
>> On Fri, 2 Jul 2010 16:41:11 +0400
>> Anton Vorontsov <avorontsov@mvista.com> wrote:
>>
>>> Since commit 5753c082f66eca5be81f6bda85c1718c5eea6ada ("powerpc/85xx:
>>> Kconfig cleanup"), there is no MPC85xx Kconfig symbol anymore, so the
>>> driver became non-selectable.
>>
>> hm. 5753c082f66eca5be81f6bda85c1718c5eea6ada got merged into mainline
>> six months ago. How come nobody noticed?
>
> Dunno. Well, it's hard to notice these sorts of things until
> somebody actually needs this driver on MPC85xx platform. :-)
>
>>> This patch fixes the issue by switching to PPC_85xx symbol.
>>>
>>> Signed-off-by: Anton Vorontsov <avorontsov@mvista.com>
>>> ---
> [...]
>>> - depends on EDAC_MM_EDAC && FSL_SOC && (PPC_83xx || MPC85xx)
>>> + depends on EDAC_MM_EDAC && FSL_SOC && (PPC_83xx || PPC_85xx)
>>> help
>>> Support for error detection and correction on the Freescale
>>> MPC8349, MPC8560, MPC8540, MPC8548
>>
>> I suppose we shold scoot this into 2.6.35 and mark it for -stable
>> backporting. All very odd.
>
> Yeah, -stable 2.6.{33,34} sounds good.
>
> Thanks.
we should add this to mpc85xx_defconfig so we build by default.
- k
^ permalink raw reply
* [git pull] Please pull powerpc.git merge branch
From: Kumar Gala @ 2010-07-11 16:18 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev
* a few build fixes and a bug fix for 2.6.35
The following changes since
commit e467e104bb7482170b79f516d2025e7cfcaaa733:
Linus Torvalds (1):
Merge branch 'for-linus' of git://git.kernel.org/.../roland/infiniband
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/galak/powerpc.git merge
Anton Vorontsov (3):
powerpc/cpm: Reintroduce global spi_pram struct (fixes build issue)
powerpc/cpm1: Fix build with various CONFIG_*_UCODE_PATCH combinations
powerpc/cpm1: Mark micropatch code/data static and __init
Matthew McClintock (1):
powerpc/fsl-booke: Fix address issue when using relocatable kernels
arch/powerpc/include/asm/cpm.h | 24 ++++++++++++++++++++
arch/powerpc/include/asm/cpm1.h | 3 +-
arch/powerpc/kernel/fsl_booke_entry_mapping.S | 4 +--
arch/powerpc/sysdev/micropatch.c | 30 +++++++++++++++----------
drivers/spi/spi_mpc8xxx.c | 22 ------------------
5 files changed, 45 insertions(+), 38 deletions(-)
^ permalink raw reply
* Re: [PATCH 3/3] powerpc/cpm1: Mark micropatch code/data static and __init
From: Kumar Gala @ 2010-07-11 16:07 UTC (permalink / raw)
To: Anton Vorontsov; +Cc: LEROY Christophe, linuxppc-dev, Scott Wood
In-Reply-To: <20100708171616.GC11621@oksana.dev.rtsoft.ru>
On Jul 8, 2010, at 12:16 PM, Anton Vorontsov wrote:
> This saves runtime memory and fixes lots of sparse warnings like this:
>
> CHECK arch/powerpc/sysdev/micropatch.c
> arch/powerpc/sysdev/micropatch.c:27:6: warning: symbol 'patch_2000'
> was not declared. Should it be static?
> arch/powerpc/sysdev/micropatch.c:146:6: warning: symbol 'patch_2f00'
> was not declared. Should it be static?
> ...
>
> Signed-off-by: Anton Vorontsov <avorontsov@mvista.com>
> ---
> arch/powerpc/include/asm/cpm1.h | 3 ++-
> arch/powerpc/sysdev/micropatch.c | 18 +++++++++---------
> 2 files changed, 11 insertions(+), 10 deletions(-)
applied to merge
- k
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox