LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [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

* Re: [PATCH] powerpc/fsl-booke: Fix address issue when using relocatable kernels
From: Kumar Gala @ 2010-07-11 16:07 UTC (permalink / raw)
  To: Matthew McClintock; +Cc: linuxppc-dev
In-Reply-To: <1277840527-18977-1-git-send-email-msm@freescale.com>


On Jun 29, 2010, at 2:42 PM, Matthew McClintock wrote:

> When booting a relocatable kernel it needs to jump to the correct
> start address, which for BookE parts is usually unchanged
> regardless of the physical memory offset.
> 
> Recent changes cause problems with how we calculate the start
> address, it was always adding the RMO into the start address
> which is incorrect. This patch only adds in the RMO offset
> if we are in the kexec code path, as it needs the RMO to work
> correctly.
> 
> Instead of adding the RMO offset in in the common code path, we
> can just set r6 to the RMO offset in the kexec code path instead
> of to zero, and finally perform the masking in the common code
> path
> 
> Signed-off-by: Matthew McClintock <msm@freescale.com>
> ---
> arch/powerpc/kernel/fsl_booke_entry_mapping.S |    4 +---
> 1 files changed, 1 insertions(+), 3 deletions(-)

applied to merge

- k

^ permalink raw reply

* Re: [PATCH 2/3] powerpc/cpm1: Fix build with various CONFIG_*_UCODE_PATCH combinations
From: Kumar Gala @ 2010-07-11 16:07 UTC (permalink / raw)
  To: Anton Vorontsov; +Cc: LEROY Christophe, linuxppc-dev, Scott Wood
In-Reply-To: <20100708171614.GB11621@oksana.dev.rtsoft.ru>


On Jul 8, 2010, at 12:16 PM, Anton Vorontsov wrote:

> Warnings are treated as errors for arch/powerpc code, so build fails
> with CONFIG_I2C_SPI_UCODE_PATCH=y:
> 
>    CC      arch/powerpc/sysdev/micropatch.o
>  cc1: warnings being treated as errors
>  arch/powerpc/sysdev/micropatch.c: In function 'cpm_load_patch':
>  arch/powerpc/sysdev/micropatch.c:630: warning: unused variable 'smp'
>  make[1]: *** [arch/powerpc/sysdev/micropatch.o] Error 1
> 
> And with CONFIG_USB_SOF_UCODE_PATCH=y:
> 
>  CC      arch/powerpc/sysdev/micropatch.o
>  cc1: warnings being treated as errors
>  arch/powerpc/sysdev/micropatch.c: In function 'cpm_load_patch':
>  arch/powerpc/sysdev/micropatch.c:629: warning: unused variable 'spp'
>  arch/powerpc/sysdev/micropatch.c:628: warning: unused variable 'iip'
>  make[1]: *** [arch/powerpc/sysdev/micropatch.o] Error 1
> 
> This patch fixes these issues by introducing proper #ifdefs.
> 
> Cc: <stable@kernel.org> [ .33, .34 ]
> Signed-off-by: Anton Vorontsov <avorontsov@mvista.com>
> ---
> arch/powerpc/sysdev/micropatch.c |    5 +++++
> 1 files changed, 5 insertions(+), 0 deletions(-)

applied to merge

- k

^ permalink raw reply

* Re: [PATCH 1/3] powerpc/cpm: Reintroduce global spi_pram struct (fixes build issue)
From: Kumar Gala @ 2010-07-11 16:07 UTC (permalink / raw)
  To: Anton Vorontsov; +Cc: LEROY Christophe, linuxppc-dev, Scott Wood
In-Reply-To: <20100708171610.GA11621@oksana.dev.rtsoft.ru>


On Jul 8, 2010, at 12:16 PM, Anton Vorontsov wrote:

> spi_t was removed in commit 644b2a680ccc51a9ec4d6beb12e9d47d2dee98e2
> ("powerpc/cpm: Remove SPI defines and spi structs"), the commit =
assumed
> that spi_t isn't used anywhere outside of the spi_mpc8xxx driver. But
> it appears that the struct is needed for micropatch code. So, let's
> reintroduce the struct.
>=20
> Fixes the following build issue:
>=20
>    CC      arch/powerpc/sysdev/micropatch.o
>  micropatch.c: In function 'cpm_load_patch':
>  micropatch.c:629: error: expected '=3D', ',', ';', 'asm' or =
'__attribute__' before '*' token
>  micropatch.c:629: error: 'spp' undeclared (first use in this =
function)
>  micropatch.c:629: error: (Each undeclared identifier is reported only =
once
>  micropatch.c:629: error: for each function it appears in.)
>=20
> Reported-by: LEROY Christophe <christophe.leroy@c-s.fr>
> Reported-by: Tony Breeds <tony@bakeyournoodle.com>
> Cc: <stable@kernel.org> [ .33, .34 ]
> Signed-off-by: Anton Vorontsov <avorontsov@mvista.com>
> ---

applied to merge

- k=

^ permalink raw reply

* C67x00 USB Host
From: A C @ 2010-07-11 13:26 UTC (permalink / raw)
  To: linuxppc-dev

[-- Attachment #1: Type: text/plain, Size: 695 bytes --]

Hello,
I am currently trying to use the C67x00 driver in the vanilla branch of
Linux. I use it in coprocessor hpi mode (GPIO30 and GPIO31 =0) There is
something that I do not understand in the code.
In the probe() function there is a call to the function hpi_reg_init(). In
this function the interrupts are no more routed from HPI.
Then, in the probe function, the ll_reset() is called, there is a send in
the mailbox, and the response should be an interupt. But I do not receveive
any interrupt. I think it is normal because as I said they are no more
routed from the HPI. So I think I miss something.
Someone could tell me where the interrupt are routed towards HPI ?
Thanks by advance
Adrien

[-- Attachment #2: Type: text/html, Size: 742 bytes --]

^ permalink raw reply

* Re: [RT,RFC] Hacks allowing -rt to run on POWER7 / Powerpc.
From: Milton Miller @ 2010-07-11  7:49 UTC (permalink / raw)
  To: Will Schmidt; +Cc: Darren Hart, rt-users, LKML, linuxppc-dev, Thomas Gleixner
In-Reply-To: <1278701701.24737.19.camel@lexx>


On Fri, 09 Jul 2010 about 08:55:01 -0000, Will Schmidt wrote:
> We've been seeing some issues with userspace randomly SIGSEGV'ing while
> running the -RT kernels on POWER7 based systems.   After lots of
> debugging, head scratching, and experimental changes to the code, the
> problem has been narrowed down such that we can avoid the problems by
> disabling the TLB batching.
> 
> After some input from Ben and further debug, we've found that the
> restoration of the batch->active value near the end of __switch_to()
> seems to be the key.    ( The -RT related  changes within
> arch/powerpc/kernel/processor.c __switch_to()  do the equivalent of a
> arch_leave_lazy_mmu_mode() before calling _switch, use a hadbatch flag
> to indicate if batching was active, and then restore that batch->active
> value on the way out after the call to _switch_to.    That particular
> code is in the -RT branch, and not found in mainline  )
> 
> Deferring to Ben (or others in the know) for whether this is the proper
> solution or if there is something deeper, but.. 


I looked at the patch and noticed 2 changes:
	1) the batch is checked and cleared after local_irq_save
	2) enabling the batch is skipped

I talked to Will and had him try moving the local_irq_save above the
check for the active batch.  That alone did not seem to be enough.
However, he confirmed that we are setting batch to active when it is
already active in lazy_mmu_enter, meaning that batching is being turned
on recursively.  I suggested debug to check that irqs are off after the
restore when re-enabling when our debug session timed out.

milton

> 
> diff -aurp linux-2.6.33.5-rt23.orig/arch/powerpc/kernel/process.c linux-2.6.33.5-rt23.exp/arch/powerpc/kernel/process.c
> --- linux-2.6.33.5-rt23.orig/arch/powerpc/kernel/process.c	2010-06-21 11:41:34.402513904 -0500
> +++ linux-2.6.33.5-rt23.exp/arch/powerpc/kernel/process.c	2010-07-09 13:15:13.533269904 -0500
> @@ -304,10 +304,6 @@ struct task_struct *__switch_to(struct t
>  	struct thread_struct *new_thread, *old_thread;
>  	unsigned long flags;
>  	struct task_struct *last;
> -#if defined(CONFIG_PPC64) && defined (CONFIG_PREEMPT_RT)
> -	struct ppc64_tlb_batch *batch;
> -	int hadbatch;
> -#endif
>  
>  #ifdef CONFIG_SMP
>  	/* avoid complexity of lazy save/restore of fpu
> @@ -401,16 +397,6 @@ struct task_struct *__switch_to(struct t
>  		new_thread->start_tb = current_tb;
>  	}
>  
> -#ifdef CONFIG_PREEMPT_RT
> -	batch = &__get_cpu_var(ppc64_tlb_batch);
> -	if (batch->active) {
> -		hadbatch = 1;
> -		if (batch->index) {
> -			__flush_tlb_pending(batch);
> -		}
> -		batch->active = 0;
> -	}
> -#endif /* #ifdef CONFIG_PREEMPT_RT */
>  #endif
>  
>  	local_irq_save(flags);
> @@ -425,16 +411,13 @@ struct task_struct *__switch_to(struct t
>  	 * of sync. Hard disable here.
>  	 */
>  	hard_irq_disable();
> -	last = _switch(old_thread, new_thread);
> -
> -	local_irq_restore(flags);
>  
>  #if defined(CONFIG_PPC64) && defined(CONFIG_PREEMPT_RT)
> -	if (hadbatch) {
> -		batch = &__get_cpu_var(ppc64_tlb_batch);
> -		batch->active = 1;
> -	}
> +	arch_leave_lazy_mmu_mode();
>  #endif
> +	last = _switch(old_thread, new_thread);
> +
> +	local_irq_restore(flags);
>  
>  	return last;
>  }

^ permalink raw reply

* Re: arch/powerpc/lib/copy_32.S: Use alternate memcpy for MPC512x and MPC52xx
From: Milton Miller @ 2010-07-11  7:40 UTC (permalink / raw)
  To: Steve Deiters; +Cc: Albrecht Dreß, linuxppc-dev, David Woodhouse
In-Reply-To: <181804936ABC2349BE503168465576460F272CA4@exchserver.basler.com>


Steve wrote:
> These processors will corrupt data if accessing the local bus with
> unaligned addresses. This version fixes the typical case of copying from
> Flash on the local bus by keeping the source address always aligned.

As Dave said in May of 2008[1], the map driver is advertising xip access
to userspace so jffs2 is taking the XIP path and doing straight memcpy.

My reading of the mtd code says this is because map_is_linear() is true
a cfi cmdset 0001 nor flash will create a point function which jffs2
calls and it finds it will map the whole fs, so it just reads at will
from the direct mapping.

If the point fails to map the whole device, then jffs2 will use the
mtd mapping driver to copy to a buffer, and the default mapping drivers
will use memcpy_fromio to fill this buffer.

Steve, can you verify this by setting phys to NO_XIP in the mapping
driver and setting CONFIG_MTD_COMPLEX_MAPPINGS?  This is just a test,
as it is aparent this driver needs to have a custom mapping driver
that handles the alignment and read/modfiy/write issues -- the existing
memcopy_*io assumes byte writes are always ok which is not the case for
this processor and bus.


[1] http://thread.gmane.org/gmane.linux.drivers.mtd/21521
(found from the link in Albrecht's reply)

milton

^ permalink raw reply

* Re: Problems mapping OCM memory from 460EX to user space
From: Ayman El-Khashab @ 2010-07-11  4:14 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20100707194846.GA724@crust.elkhashab.com>

On 7/7/2010 2:48 PM, ayman@austin.rr.com wrote:
>   rc = io_remap_pfn_range(vma, PLB_OCM_BASE_ADDR>>PAGE_SHIFT, vma->vm_start, len, vma->vm_page_prot);
>
> I am fairly certain the physical address is correct, I've verified that the
> TLB entries in u-boot look ok.  Any tips on how to make this work?
>
>    

I found / fixed the issue.  The first problem is that start and offset 
are swapped in the io_remap_pfn_range invocation.  The 2nd was that
there were some TLB changes that were needed to map the OCM.

^ permalink raw reply

* [PATCH, RT, RFC]  Hacks allowing -rt to run on POWER7 / Powerpc.
From: Will Schmidt @ 2010-07-09 18:55 UTC (permalink / raw)
  To: rt-users, Thomas Gleixner, Benjamin Herrenschmidt
  Cc: linuxppc-dev, Darren Hart, will_schmidt, LKML

[PATCH, RT, RFC]  Hacks allowing -rt to run on POWER7 / Powerpc.

We've been seeing some issues with userspace randomly SIGSEGV'ing while
running the -RT kernels on POWER7 based systems.   After lots of
debugging, head scratching, and experimental changes to the code, the
problem has been narrowed down such that we can avoid the problems by
disabling the TLB batching.

After some input from Ben and further debug, we've found that the
restoration of the batch->active value near the end of __switch_to()
seems to be the key.    ( The -RT related  changes within
arch/powerpc/kernel/processor.c __switch_to()  do the equivalent of a
arch_leave_lazy_mmu_mode() before calling _switch, use a hadbatch flag
to indicate if batching was active, and then restore that batch->active
value on the way out after the call to _switch_to.    That particular
code is in the -RT branch, and not found in mainline  )

Deferring to Ben (or others in the know) for whether this is the proper
solution or if there is something deeper, but.. 
IF the right answer is to simply disable the restoration of
batch->active, the rest of the CONFIG_PREEMPT_RT changes in
__switch_to() should then be replaceable with a single call to
arch_leave_lazy_mmu_mode().

The patch here is what I am currently running with, on both POWER6 and
POWER7 systems, successfully.


Signed-off-by:   Will Schmidt <will_schmidt@vnet.ibm.com>
CC:    Ben Herrenschmidt  <benh@kernel.crashing.org>
CC:    Thomas Gleixner <tglx@linutronix.de>

---
diff -aurp linux-2.6.33.5-rt23.orig/arch/powerpc/kernel/process.c linux-2.6.33.5-rt23.exp/arch/powerpc/kernel/process.c
--- linux-2.6.33.5-rt23.orig/arch/powerpc/kernel/process.c	2010-06-21 11:41:34.402513904 -0500
+++ linux-2.6.33.5-rt23.exp/arch/powerpc/kernel/process.c	2010-07-09 13:15:13.533269904 -0500
@@ -304,10 +304,6 @@ struct task_struct *__switch_to(struct t
 	struct thread_struct *new_thread, *old_thread;
 	unsigned long flags;
 	struct task_struct *last;
-#if defined(CONFIG_PPC64) && defined (CONFIG_PREEMPT_RT)
-	struct ppc64_tlb_batch *batch;
-	int hadbatch;
-#endif
 
 #ifdef CONFIG_SMP
 	/* avoid complexity of lazy save/restore of fpu
@@ -401,16 +397,6 @@ struct task_struct *__switch_to(struct t
 		new_thread->start_tb = current_tb;
 	}
 
-#ifdef CONFIG_PREEMPT_RT
-	batch = &__get_cpu_var(ppc64_tlb_batch);
-	if (batch->active) {
-		hadbatch = 1;
-		if (batch->index) {
-			__flush_tlb_pending(batch);
-		}
-		batch->active = 0;
-	}
-#endif /* #ifdef CONFIG_PREEMPT_RT */
 #endif
 
 	local_irq_save(flags);
@@ -425,16 +411,13 @@ struct task_struct *__switch_to(struct t
 	 * of sync. Hard disable here.
 	 */
 	hard_irq_disable();
-	last = _switch(old_thread, new_thread);
-
-	local_irq_restore(flags);
 
 #if defined(CONFIG_PPC64) && defined(CONFIG_PREEMPT_RT)
-	if (hadbatch) {
-		batch = &__get_cpu_var(ppc64_tlb_batch);
-		batch->active = 1;
-	}
+	arch_leave_lazy_mmu_mode();
 #endif
+	last = _switch(old_thread, new_thread);
+
+	local_irq_restore(flags);
 
 	return last;
 }

^ permalink raw reply

* Re: [PATCH] arch/powerpc/lib/copy_32.S: Use alternate memcpy for MPC512x and MPC52xx
From: Scott Wood @ 2010-07-09 16:18 UTC (permalink / raw)
  To: Segher Boessenkool
  Cc: Albrecht Dreß, Steve Deiters, linuxppc-dev, David Woodhouse
In-Reply-To: <E0D806F7-A109-4AAF-9899-429659E2C3D9@kernel.crashing.org>

On Fri, 9 Jul 2010 14:59:09 +0200
Segher Boessenkool <segher@kernel.crashing.org> wrote:

> >>> Actually, this is something which might need closer attention -
> >>> and maybe some support in the device tree indicating which read or
> >>> write width a device can accept?
> >>
> >> There already is "device-width"; the drivers never should use any
> >> other access width unless they *know* that will work.
> >
> > Wouldn't you want to use "bank-width" instead?
> 
> We were talking about single devices.  But, sure, when you have
> multiple devices in parallel the driver needs to know about that.
> 
> > It would be nice to have a device tree property that can specify
> > that all access widths supported by the CPU will work, though.
> 
> Oh please no.  A device binding should not depend on what CPU there
> is in the system.  There could be multiple CPUs of different
> architectures, even.

What I meant by that was that the flash interface was claiming that it
is not the limiting factor in which access widths are useable -- it
would be a way to claim that it is as flexible as ordinary memory in
that regard.

If there is a transaction size that is capable of being presented to
this component that it cannot handle, it would not present this
property.

> To figure out how to access a device, the driver looks at the device's
> node, and all its parent nodes (or asks generic code to do that, or
> platform code).

"looks" or "should look"? :-)

If there are transaction sizes supported by the CPU that won't work
with a given device through no fault of that device (or the interface
to that device for which we don't have a separate node), then in
theory, yes, it should be described at a higher level.

In reality, device tree parsing code is not AI, so rather than say "the
driver looks at this and figures it out" it would be better to provide
a more specific proposal of how a device tree might express this and
what the driver would look for, if you think the simple solution is
not expressive enough.

-Scott

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox