diff mbox

[3/4] arm: am33xx: DT quirks for am33xx based beaglebone variants

Message ID 1424271576-1952-4-git-send-email-pantelis.antoniou@konsulko.com (mailing list archive)
State New, archived
Headers show

Commit Message

Pantelis Antoniou Feb. 18, 2015, 2:59 p.m. UTC
Implement DT quirks for the am33xx beaglebone boards.

A single boot DTB supports both the beaglebone white and the beaglebone
black, and on boot time the identifying EEPROM is read and the info
is used to modify the live tree to match the board detected.

Refer to Documentation/devicetree/bindings/quirks/am33xx-bone-quirk.txt
for detailed information.

Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
---
 .../bindings/quirks/am33xx-bone-quirk.txt          |  82 ++++
 arch/arm/mach-omap2/Makefile                       |   5 +
 arch/arm/mach-omap2/am33xx-dt-quirks.c             | 498 +++++++++++++++++++++
 arch/arm/mach-omap2/am33xx-dt-quirks.h             |  10 +
 arch/arm/mach-omap2/board-generic.c                |   1 +
 arch/arm/mach-omap2/common.h                       |   8 +
 6 files changed, 604 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/quirks/am33xx-bone-quirk.txt
 create mode 100644 arch/arm/mach-omap2/am33xx-dt-quirks.c
 create mode 100644 arch/arm/mach-omap2/am33xx-dt-quirks.h

Comments

Tony Lindgren Feb. 19, 2015, 6:16 p.m. UTC | #1
* Pantelis Antoniou <pantelis.antoniou@konsulko.com> [150218 07:03]:
> Implement DT quirks for the am33xx beaglebone boards.
> --- /dev/null
> +++ b/arch/arm/mach-omap2/am33xx-dt-quirks.c
...
> +
> +/*
> + * The board IDs for am33xx board are in an I2C EEPROM
> + * We are very early in the boot process so we have to
> + * read the EEPROM directly without using the I2C layer.
> + *
> + * Note that we rely on the bootloader setting up the muxes
> + * (which is the case for u-boot).
> + */
> +
> +/* I2C Status Register (OMAP_I2C_STAT): */
> +#define OMAP_I2C_STAT_XDR	(1 << 14)	/* TX Buffer draining */
> +#define OMAP_I2C_STAT_RDR	(1 << 13)	/* RX Buffer draining */
> +#define OMAP_I2C_STAT_BB	(1 << 12)	/* Bus busy */
> +#define OMAP_I2C_STAT_ROVR	(1 << 11)	/* Receive overrun */
> +#define OMAP_I2C_STAT_XUDF	(1 << 10)	/* Transmit underflow */
> +#define OMAP_I2C_STAT_AAS	(1 << 9)	/* Address as slave */
> +#define OMAP_I2C_STAT_BF	(1 << 8)	/* Bus Free */
> +#define OMAP_I2C_STAT_XRDY	(1 << 4)	/* Transmit data ready */
> +#define OMAP_I2C_STAT_RRDY	(1 << 3)	/* Receive data ready */
> +#define OMAP_I2C_STAT_ARDY	(1 << 2)	/* Register access ready */
> +#define OMAP_I2C_STAT_NACK	(1 << 1)	/* No ack interrupt enable */
> +#define OMAP_I2C_STAT_AL	(1 << 0)	/* Arbitration lost int ena */
...

Uhh I don't like the idea of duplicating the i2c-omap.c driver under
arch/arm.. And in general we should initialize things later rather
than earlier.

What's stopping doing these quirk checks later on time with just
a regular device driver, something like drivers/misc/bbone-quirks.c?

Regards,

Tony
Pantelis Antoniou Feb. 19, 2015, 6:28 p.m. UTC | #2
Hi Tony,

> On Feb 19, 2015, at 20:16 , Tony Lindgren <tony@atomide.com> wrote:
> 
> * Pantelis Antoniou <pantelis.antoniou@konsulko.com> [150218 07:03]:
>> Implement DT quirks for the am33xx beaglebone boards.
>> --- /dev/null
>> +++ b/arch/arm/mach-omap2/am33xx-dt-quirks.c
> ...
>> +
>> +/*
>> + * The board IDs for am33xx board are in an I2C EEPROM
>> + * We are very early in the boot process so we have to
>> + * read the EEPROM directly without using the I2C layer.
>> + *
>> + * Note that we rely on the bootloader setting up the muxes
>> + * (which is the case for u-boot).
>> + */
>> +
>> +/* I2C Status Register (OMAP_I2C_STAT): */
>> +#define OMAP_I2C_STAT_XDR	(1 << 14)	/* TX Buffer draining */
>> +#define OMAP_I2C_STAT_RDR	(1 << 13)	/* RX Buffer draining */
>> +#define OMAP_I2C_STAT_BB	(1 << 12)	/* Bus busy */
>> +#define OMAP_I2C_STAT_ROVR	(1 << 11)	/* Receive overrun */
>> +#define OMAP_I2C_STAT_XUDF	(1 << 10)	/* Transmit underflow */
>> +#define OMAP_I2C_STAT_AAS	(1 << 9)	/* Address as slave */
>> +#define OMAP_I2C_STAT_BF	(1 << 8)	/* Bus Free */
>> +#define OMAP_I2C_STAT_XRDY	(1 << 4)	/* Transmit data ready */
>> +#define OMAP_I2C_STAT_RRDY	(1 << 3)	/* Receive data ready */
>> +#define OMAP_I2C_STAT_ARDY	(1 << 2)	/* Register access ready */
>> +#define OMAP_I2C_STAT_NACK	(1 << 1)	/* No ack interrupt enable */
>> +#define OMAP_I2C_STAT_AL	(1 << 0)	/* Arbitration lost int ena */
> ...
> 
> Uhh I don't like the idea of duplicating the i2c-omap.c driver under
> arch/arm.. And in general we should initialize things later rather
> than earlier.
> 
> What's stopping doing these quirk checks later on time with just
> a regular device driver, something like drivers/misc/bbone-quirks.c?
> 

We have no choice; we are way early in the boot process, right after
the device tree unflattening step.

I’ve toyed with the idea of using early platform devices but the omap-i2c driver
would need some tender love and care to make it work, and I didn’t want to get
bogged down with i2c driver details at this point.

> Regards,
> 
> Tony

Regards

— Pantelis
Tony Lindgren Feb. 19, 2015, 6:36 p.m. UTC | #3
* Pantelis Antoniou <panto@antoniou-consulting.com> [150219 10:32]:
> > On Feb 19, 2015, at 20:16 , Tony Lindgren <tony@atomide.com> wrote:
> > 
> > Uhh I don't like the idea of duplicating the i2c-omap.c driver under
> > arch/arm.. And in general we should initialize things later rather
> > than earlier.
> > 
> > What's stopping doing these quirk checks later on time with just
> > a regular device driver, something like drivers/misc/bbone-quirks.c?
> > 
> 
> We have no choice; we are way early in the boot process, right after
> the device tree unflattening step.

To me it seems the dt patching part should be done with minimal
code before any driver like features..
 
> I’ve toyed with the idea of using early platform devices but the omap-i2c driver
> would need some tender love and care to make it work, and I didn’t want to get
> bogged down with i2c driver details at this point.

..so how about just parse a kernel cmdline for the quirks to apply
based on a version string or similar? That can be easily populated
by u-boot or set manually with setenv.

That leaves out the need for tinkering with i2c super early in
the kernel for revision detection.

Regards,

Tony
Pantelis Antoniou Feb. 19, 2015, 6:44 p.m. UTC | #4
Hi Tony,

> On Feb 19, 2015, at 20:36 , Tony Lindgren <tony@atomide.com> wrote:
> 
> * Pantelis Antoniou <panto@antoniou-consulting.com> [150219 10:32]:
>>> On Feb 19, 2015, at 20:16 , Tony Lindgren <tony@atomide.com> wrote:
>>> 
>>> Uhh I don't like the idea of duplicating the i2c-omap.c driver under
>>> arch/arm.. And in general we should initialize things later rather
>>> than earlier.
>>> 
>>> What's stopping doing these quirk checks later on time with just
>>> a regular device driver, something like drivers/misc/bbone-quirks.c?
>>> 
>> 
>> We have no choice; we are way early in the boot process, right after
>> the device tree unflattening step.
> 
> To me it seems the dt patching part should be done with minimal
> code before any driver like features..
> 

The way it’s done right now is with minimal code. Reading the EEPROM
is required.

>> I’ve toyed with the idea of using early platform devices but the omap-i2c driver
>> would need some tender love and care to make it work, and I didn’t want to get
>> bogged down with i2c driver details at this point.
> 
> ..so how about just parse a kernel cmdline for the quirks to apply
> based on a version string or similar? That can be easily populated
> by u-boot or set manually with setenv.
> 
> That leaves out the need for tinkering with i2c super early in
> the kernel for revision detection.
> 

You assume there’s going to be a bootloader…

> Regards,
> 
> Tony

Regards

— Pantelis
Guenter Roeck Feb. 19, 2015, 6:57 p.m. UTC | #5
On Thu, Feb 19, 2015 at 10:36:00AM -0800, Tony Lindgren wrote:
> * Pantelis Antoniou <panto@antoniou-consulting.com> [150219 10:32]:
> > > On Feb 19, 2015, at 20:16 , Tony Lindgren <tony@atomide.com> wrote:
> > > 
> > > Uhh I don't like the idea of duplicating the i2c-omap.c driver under
> > > arch/arm.. And in general we should initialize things later rather
> > > than earlier.
> > > 
> > > What's stopping doing these quirk checks later on time with just
> > > a regular device driver, something like drivers/misc/bbone-quirks.c?
> > > 
> > 
> > We have no choice; we are way early in the boot process, right after
> > the device tree unflattening step.
> 
> To me it seems the dt patching part should be done with minimal
> code before any driver like features..
>  
> > I’ve toyed with the idea of using early platform devices but the omap-i2c driver
> > would need some tender love and care to make it work, and I didn’t want to get
> > bogged down with i2c driver details at this point.
> 
> ..so how about just parse a kernel cmdline for the quirks to apply
> based on a version string or similar? That can be easily populated
> by u-boot or set manually with setenv.
> 
That would not work for my use case. Again, this is a CPU card
plugged into a carrier card, of which there are several variants.
The CPU card is similar to a Com Express card (not exactly the same,
but the same idea), so it may even be manufactured by a third party.
Actually, in some cases it is. Modifying the kernel command line
based on the carrier card it is connected to is simply not possible.

Guenter
Jon Hunter Feb. 20, 2015, 4:13 p.m. UTC | #6
On 02/19/2015 06:28 PM, Pantelis Antoniou wrote:
> Hi Tony,
> 
>> On Feb 19, 2015, at 20:16 , Tony Lindgren <tony@atomide.com> wrote:
>>
>> * Pantelis Antoniou <pantelis.antoniou@konsulko.com> [150218 07:03]:
>>> Implement DT quirks for the am33xx beaglebone boards.
>>> --- /dev/null
>>> +++ b/arch/arm/mach-omap2/am33xx-dt-quirks.c
>> ...
>>> +
>>> +/*
>>> + * The board IDs for am33xx board are in an I2C EEPROM
>>> + * We are very early in the boot process so we have to
>>> + * read the EEPROM directly without using the I2C layer.
>>> + *
>>> + * Note that we rely on the bootloader setting up the muxes
>>> + * (which is the case for u-boot).
>>> + */
>>> +
>>> +/* I2C Status Register (OMAP_I2C_STAT): */
>>> +#define OMAP_I2C_STAT_XDR	(1 << 14)	/* TX Buffer draining */
>>> +#define OMAP_I2C_STAT_RDR	(1 << 13)	/* RX Buffer draining */
>>> +#define OMAP_I2C_STAT_BB	(1 << 12)	/* Bus busy */
>>> +#define OMAP_I2C_STAT_ROVR	(1 << 11)	/* Receive overrun */
>>> +#define OMAP_I2C_STAT_XUDF	(1 << 10)	/* Transmit underflow */
>>> +#define OMAP_I2C_STAT_AAS	(1 << 9)	/* Address as slave */
>>> +#define OMAP_I2C_STAT_BF	(1 << 8)	/* Bus Free */
>>> +#define OMAP_I2C_STAT_XRDY	(1 << 4)	/* Transmit data ready */
>>> +#define OMAP_I2C_STAT_RRDY	(1 << 3)	/* Receive data ready */
>>> +#define OMAP_I2C_STAT_ARDY	(1 << 2)	/* Register access ready */
>>> +#define OMAP_I2C_STAT_NACK	(1 << 1)	/* No ack interrupt enable */
>>> +#define OMAP_I2C_STAT_AL	(1 << 0)	/* Arbitration lost int ena */
>> ...
>>
>> Uhh I don't like the idea of duplicating the i2c-omap.c driver under
>> arch/arm.. And in general we should initialize things later rather
>> than earlier.
>>
>> What's stopping doing these quirk checks later on time with just
>> a regular device driver, something like drivers/misc/bbone-quirks.c?
>>
> 
> We have no choice; we are way early in the boot process, right after
> the device tree unflattening step.

Can you elaborate with an example of why not? Why can't the overlay
happen at a later stage in the kernel boot as Tony suggests?

One thought would be that ideally devices that are dependent on a
particular board variant would be disabled in the base DT blob until you
know what board you are. However, that assumes that they can be
initialised at a later stage in the boot process and may be for some
regulators or other devices this is not possible. I know you mentioned
some time restrictions for some devices, but I still don't see why it
could not happen later.

Jon
Peter Hurley Feb. 23, 2015, 6:39 p.m. UTC | #7
Hi Pantelis,

On 02/19/2015 01:44 PM, Pantelis Antoniou wrote:
> Hi Tony,
> 
>> On Feb 19, 2015, at 20:36 , Tony Lindgren <tony@atomide.com> wrote:
>>
>> * Pantelis Antoniou <panto@antoniou-consulting.com> [150219 10:32]:
>>>> On Feb 19, 2015, at 20:16 , Tony Lindgren <tony@atomide.com> wrote:
>>>>
>>>> Uhh I don't like the idea of duplicating the i2c-omap.c driver under
>>>> arch/arm.. And in general we should initialize things later rather
>>>> than earlier.
>>>>
>>>> What's stopping doing these quirk checks later on time with just
>>>> a regular device driver, something like drivers/misc/bbone-quirks.c?
>>>>
>>>
>>> We have no choice; we are way early in the boot process, right after
>>> the device tree unflattening step.
>>
>> To me it seems the dt patching part should be done with minimal
>> code before any driver like features..
>>
> 
> The way it’s done right now is with minimal code. Reading the EEPROM
> is required.
> 
>>> I’ve toyed with the idea of using early platform devices but the omap-i2c driver
>>> would need some tender love and care to make it work, and I didn’t want to get
>>> bogged down with i2c driver details at this point.
>>
>> ..so how about just parse a kernel cmdline for the quirks to apply
>> based on a version string or similar? That can be easily populated
>> by u-boot or set manually with setenv.
>>
>> That leaves out the need for tinkering with i2c super early in
>> the kernel for revision detection.
>>
> 
> You assume there’s going to be a bootloader…

So does this patch.

> diff --git a/arch/arm/mach-omap2/am33xx-dt-quirks.c b/arch/arm/mach-omap2/am33xx-dt-quirks.c
[...]
> + * Note that we rely on the bootloader setting up the muxes
> + * (which is the case for u-boot).

Regards,
Peter Hurley
Pantelis Antoniou Feb. 23, 2015, 6:48 p.m. UTC | #8
Hi Peter,

> On Feb 23, 2015, at 20:39 , Peter Hurley <peter@hurleysoftware.com> wrote:
> 
> Hi Pantelis,
> 
> On 02/19/2015 01:44 PM, Pantelis Antoniou wrote:
>> Hi Tony,
>> 
>>> On Feb 19, 2015, at 20:36 , Tony Lindgren <tony@atomide.com> wrote:
>>> 
>>> * Pantelis Antoniou <panto@antoniou-consulting.com> [150219 10:32]:
>>>>> On Feb 19, 2015, at 20:16 , Tony Lindgren <tony@atomide.com> wrote:
>>>>> 
>>>>> Uhh I don't like the idea of duplicating the i2c-omap.c driver under
>>>>> arch/arm.. And in general we should initialize things later rather
>>>>> than earlier.
>>>>> 
>>>>> What's stopping doing these quirk checks later on time with just
>>>>> a regular device driver, something like drivers/misc/bbone-quirks.c?
>>>>> 
>>>> 
>>>> We have no choice; we are way early in the boot process, right after
>>>> the device tree unflattening step.
>>> 
>>> To me it seems the dt patching part should be done with minimal
>>> code before any driver like features..
>>> 
>> 
>> The way it’s done right now is with minimal code. Reading the EEPROM
>> is required.
>> 
>>>> I’ve toyed with the idea of using early platform devices but the omap-i2c driver
>>>> would need some tender love and care to make it work, and I didn’t want to get
>>>> bogged down with i2c driver details at this point.
>>> 
>>> ..so how about just parse a kernel cmdline for the quirks to apply
>>> based on a version string or similar? That can be easily populated
>>> by u-boot or set manually with setenv.
>>> 
>>> That leaves out the need for tinkering with i2c super early in
>>> the kernel for revision detection.
>>> 
>> 
>> You assume there’s going to be a bootloader…
> 
> So does this patch.
> 

Proof of concept, first iteration… The beaglebone is just the prototype stage.

>> diff --git a/arch/arm/mach-omap2/am33xx-dt-quirks.c b/arch/arm/mach-omap2/am33xx-dt-quirks.c
> [...]
>> + * Note that we rely on the bootloader setting up the muxes
>> + * (which is the case for u-boot).
> 
> Regards,
> Peter Hurley
> 

Regards

— Pantelis
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/quirks/am33xx-bone-quirk.txt b/Documentation/devicetree/bindings/quirks/am33xx-bone-quirk.txt
new file mode 100644
index 0000000..239dd8d
--- /dev/null
+++ b/Documentation/devicetree/bindings/quirks/am33xx-bone-quirk.txt
@@ -0,0 +1,82 @@ 
+am33xx Beaglebone White/Black Device Tree Bindings
+--------------------------------------------------
+
+Beaglebone boards that boot with a common device tree blob contain
+a DT quirk entry with the following properties:
+
+Required properties:
+
+- compatible : should be "ti,am33xx-bone-quirk"
+
+Required child node:
+
+- revs : Contains children nodes that identify boards
+
+  Each child node of revs must be of the following form:
+
+  Required properties:
+
+  - board-id : Contain the board ID as it appears in the EEPROM
+  - board-apply : Should contain the phandle of the quirk to apply
+
+  Optional child node:
+
+  - options : Contain a number of properties that identify options
+  that can be enabled or disabled by external means. For Linux the
+  options are selected by parsing the boot command line.
+  The properties contain a tupple of quirk phandles, the first containing
+  the enable quirk and the second the disable quirk. If the phandle is zero
+  then no quirk is applied.
+
+Any other nodes (like the quirk definitions are ignored).
+
+The following example defines two revisions (white/black) with
+board-id A335BONE for the white and A335BNLT for the black.
+For the beaglebone black we also define two options
+no-emmc and no-hdmi which when are present in the boot command line
+the emmc and hdmi are disabled.
+
+	beaglebone-quirks {
+		compatible = "ti,am33xx-bone-quirk";
+		status = "okay";
+
+		revs {
+			white {
+				board-id = "A335BONE";
+				board-apply = <&bonewhite>;
+			};
+			black {
+				board-id = "A335BNLT";
+				board-apply = <&boneblack>;
+				options {
+					no-emmc = <0 &black_enable_emmc>;
+					no-hdmi = <0 &black_enable_hdmi>;
+				};
+			};
+		};
+
+		overlays {
+			bonewhite: bonewhite {
+				/* quirk for white */
+				...
+			};
+
+			boneblack: boneblack {
+				/* quirk for black */
+				...
+			};
+
+			black_enable_hdmi: black_hdmi {
+				/* quirk to enable hdmi */
+				...
+			};
+
+			black_enable_emmc: black_emmc {
+				/* quirk to enable emmc */
+				...
+			};
+		};
+	};
+
+
+
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 5d27dfd..02129e7 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -259,6 +259,11 @@  obj-$(CONFIG_MACH_CRANEBOARD)		+= board-am3517crane.o
 
 obj-$(CONFIG_MACH_SBC3530)		+= board-omap3stalker.o
 
+# DT quirks
+ifneq ($(CONFIG_OF_DYNAMIC),)
+obj-$(CONFIG_SOC_AM33XX)		+= am33xx-dt-quirks.o
+endif
+
 # Platform specific device init code
 
 omap-flash-$(CONFIG_MTD_NAND_OMAP2)	:= board-flash.o
diff --git a/arch/arm/mach-omap2/am33xx-dt-quirks.c b/arch/arm/mach-omap2/am33xx-dt-quirks.c
new file mode 100644
index 0000000..668235c
--- /dev/null
+++ b/arch/arm/mach-omap2/am33xx-dt-quirks.c
@@ -0,0 +1,498 @@ 
+/*
+ * arch/arm/mach-omap2/am33xx-dt-quirks.c
+ *
+ * AM33xx variant DT quirks
+ *
+ * Copyright (C) 2015 Konsulko Group
+ *
+ * Author:
+ *	Pantelis Antoniou <pantelis.antoniou@konsulko.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+
+#include "am33xx-dt-quirks.h"
+
+/*
+ * The board IDs for am33xx board are in an I2C EEPROM
+ * We are very early in the boot process so we have to
+ * read the EEPROM directly without using the I2C layer.
+ *
+ * Note that we rely on the bootloader setting up the muxes
+ * (which is the case for u-boot).
+ */
+
+/* I2C Status Register (OMAP_I2C_STAT): */
+#define OMAP_I2C_STAT_XDR	(1 << 14)	/* TX Buffer draining */
+#define OMAP_I2C_STAT_RDR	(1 << 13)	/* RX Buffer draining */
+#define OMAP_I2C_STAT_BB	(1 << 12)	/* Bus busy */
+#define OMAP_I2C_STAT_ROVR	(1 << 11)	/* Receive overrun */
+#define OMAP_I2C_STAT_XUDF	(1 << 10)	/* Transmit underflow */
+#define OMAP_I2C_STAT_AAS	(1 << 9)	/* Address as slave */
+#define OMAP_I2C_STAT_BF	(1 << 8)	/* Bus Free */
+#define OMAP_I2C_STAT_XRDY	(1 << 4)	/* Transmit data ready */
+#define OMAP_I2C_STAT_RRDY	(1 << 3)	/* Receive data ready */
+#define OMAP_I2C_STAT_ARDY	(1 << 2)	/* Register access ready */
+#define OMAP_I2C_STAT_NACK	(1 << 1)	/* No ack interrupt enable */
+#define OMAP_I2C_STAT_AL	(1 << 0)	/* Arbitration lost int ena */
+
+/* I2C Configuration Register (OMAP_I2C_CON): */
+#define OMAP_I2C_CON_EN		(1 << 15)	/* I2C module enable */
+#define OMAP_I2C_CON_BE		(1 << 14)	/* Big endian mode */
+#define OMAP_I2C_CON_OPMODE_HS	(1 << 12)	/* High Speed support */
+#define OMAP_I2C_CON_STB	(1 << 11)	/* Start byte mode (master) */
+#define OMAP_I2C_CON_MST	(1 << 10)	/* Master/slave mode */
+#define OMAP_I2C_CON_TRX	(1 << 9)	/* TX/RX mode (master only) */
+#define OMAP_I2C_CON_XA		(1 << 8)	/* Expand address */
+#define OMAP_I2C_CON_RM		(1 << 2)	/* Repeat mode (master only) */
+#define OMAP_I2C_CON_STP	(1 << 1)	/* Stop cond (master only) */
+#define OMAP_I2C_CON_STT	(1 << 0)	/* Start condition (master) */
+
+/* register definitions */
+#define I2C_REVNB_LO		0x00
+#define I2C_REVNB_HI		0x04
+#define I2C_SYSC		0x10
+#define I2C_IRQSTATUS_RAW	0x24
+#define I2C_IRQSTATUS		0x28
+#define I2C_IRQENABLE_SET	0x2C
+#define I2C_IRQENABLE_CLR	0x30
+#define I2C_WE			0x34
+#define I2C_DMARXENABLE_SET	0x38
+#define I2C_DMATXENABLE_SET	0x3C
+#define I2C_DMARXENABLE_CLR	0x40
+#define I2C_DMATXENABLE_CLR	0x44
+#define I2C_DMARXWAKE_EN	0x48
+#define I2C_DMATXWAKE_EN	0x4C
+#define I2C_SYSS		0x90
+#define I2C_BUF			0x94
+#define I2C_CNT			0x98
+#define I2C_DATA		0x9C
+#define I2C_CON			0xA4
+#define I2C_OA			0xA8
+#define I2C_SA			0xAC
+#define I2C_PSC			0xB0
+#define I2C_SCLL		0xB4
+#define I2C_SCLH		0xB8
+#define I2C_SYSTEST		0xBC
+#define I2C_BUFSTAT		0xC0
+#define I2C_OA1			0xC4
+#define I2C_OA2			0xC8
+#define I2C_OA3			0xCC
+#define I2C_ACTOA		0xD0
+#define I2C_SBLOCK		0xD4
+
+static inline void i2c_reg_write(void __iomem *base, unsigned int reg, u16 val)
+{
+	writew_relaxed(val, base + reg);
+}
+
+static inline u16 i2c_reg_read(void __iomem *base, unsigned int reg)
+{
+	return readw_relaxed(base + reg);
+}
+
+static void flush_fifo(void __iomem *base)
+{
+	u16 stat;
+
+	while ((stat = i2c_reg_read(base, I2C_IRQSTATUS_RAW))
+			& OMAP_I2C_STAT_RRDY) {
+		(void)i2c_reg_read(base, I2C_DATA);
+		i2c_reg_write(base, I2C_IRQSTATUS, OMAP_I2C_STAT_RRDY);
+		udelay(1000);
+	};
+}
+
+static void wait_delay(void __iomem *base)
+{
+	udelay((10000000 / 100000) * 2);
+}
+
+static int wait_for_bb(void __iomem *base)
+{
+	int timeout;
+	u16 stat;
+
+	timeout = 1000;
+	while (((stat = i2c_reg_read(base, I2C_IRQSTATUS_RAW)) &
+				OMAP_I2C_STAT_BB) && timeout--) {
+		i2c_reg_write(base, I2C_IRQSTATUS, stat);
+		wait_delay(base);
+	}
+
+	if (timeout <= 0) {
+		pr_err("%s: Timeout while waiting for bus\n", __func__);
+		return -1;
+	}
+	i2c_reg_write(base, I2C_IRQSTATUS, 0xffff);
+	return 0;
+}
+
+static u16 wait_for_event(void __iomem *base)
+{
+	u16 status;
+	int timeout = 10000;
+
+	do {
+		wait_delay(base);
+		status = i2c_reg_read(base, I2C_IRQSTATUS_RAW);
+	} while (!(status & (OMAP_I2C_STAT_ROVR | OMAP_I2C_STAT_XUDF |
+		    OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_RRDY |
+		    OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK |
+		    OMAP_I2C_STAT_AL)) &&
+			timeout--);
+
+	if (timeout <= 0) {
+		pr_err("%s: Timeout status=%04x\n", __func__, status);
+		i2c_reg_write(base, I2C_IRQSTATUS, 0xffff);
+		status = 0;
+	}
+
+	return status;
+}
+
+static int i2c_init(void __iomem *base, u16 psc, u16 scll, u16 sclh)
+{
+	int timeout;
+
+	/* init */
+	if (i2c_reg_read(base, I2C_CON) & OMAP_I2C_CON_EN) {
+		i2c_reg_write(base, I2C_CON, 0);
+		for (timeout = 0; timeout <= 50000; timeout += 1000)
+			udelay(1000);
+	}
+
+	/* soft reset */
+	i2c_reg_write(base, I2C_SYSC, 0x02);
+	udelay(1000);
+	i2c_reg_write(base, I2C_CON, OMAP_I2C_CON_EN);
+
+	timeout = 1000;
+	while (!(i2c_reg_read(base, I2C_SYSS) & 0x0001) && timeout--) {
+		if (timeout <= 0) {
+			pr_err("%s: Timeout in soft reset\n", __func__);
+			return -ENODEV;
+		}
+		udelay(1000);
+	}
+
+	i2c_reg_write(base, I2C_CON, 0x0000);
+	i2c_reg_write(base, I2C_PSC, psc);
+	i2c_reg_write(base, I2C_SCLL, scll);
+	i2c_reg_write(base, I2C_SCLH, sclh);
+	i2c_reg_write(base, I2C_CON, 0x8000);
+	udelay(1000);
+
+	i2c_reg_write(base, I2C_OA, 1);
+
+	/* flush fifo */
+	flush_fifo(base);
+	i2c_reg_write(base, I2C_IRQSTATUS, 0xffff);
+
+	return 0;
+}
+
+static int i2c_read(void __iomem *base, u8 chip, u16 addr,
+		unsigned int alen,
+		void *buffer, int len)
+{
+	u8 *s = buffer;
+	unsigned int i;
+	u16 status;
+	int timeout, ret;
+
+	if (alen < 0 || len < 0 || !buffer || alen > 2 ||
+			(addr + len > 0x10000))
+		return -EINVAL;
+
+	if (wait_for_bb(base) != 0) {
+		pr_err("%s: wait for bb fail\n", __func__);
+		return -ENODEV;
+	}
+
+	ret = -EINVAL;
+	i2c_reg_write(base, I2C_SA, chip);
+
+	if (alen) {
+		i2c_reg_write(base, I2C_CNT, alen);
+
+		/* Stop - Start (P-S) */
+		i2c_reg_write(base, I2C_CON, OMAP_I2C_CON_EN |
+			OMAP_I2C_CON_MST | OMAP_I2C_CON_STT |
+			OMAP_I2C_CON_STP | OMAP_I2C_CON_TRX);
+		while (alen > 0) {
+			status = wait_for_event(base);
+			if (status == 0 || (status & OMAP_I2C_STAT_NACK)) {
+				ret = -ENODEV;
+				pr_err("%s: error waiting for addr ACK\n",
+						__func__);
+				goto out;
+			}
+			if (status & OMAP_I2C_STAT_XRDY) {
+				alen--;
+				i2c_reg_write(base, I2C_DATA,
+					(addr >> (8 * alen)) & 0xff);
+				i2c_reg_write(base, I2C_IRQSTATUS,
+						OMAP_I2C_STAT_XRDY);
+			}
+		}
+
+		/* poll ARDY for last byte going out */
+		timeout = 1000;
+		do {
+			status = wait_for_event(base);
+			if (status & OMAP_I2C_STAT_ARDY)
+				break;
+			udelay(1000);
+		} while (timeout--);
+
+		if (timeout <= 0) {
+			ret = -ENODEV;
+			pr_err("%s: timeout waiting for ARDY\n",
+					__func__);
+			goto out;
+		}
+
+		i2c_reg_write(base, I2C_IRQSTATUS, OMAP_I2C_STAT_ARDY);
+
+		wait_delay(base);
+	}
+
+	i2c_reg_write(base, I2C_CNT, len);
+
+	i2c_reg_write(base, I2C_CON, OMAP_I2C_CON_EN | OMAP_I2C_CON_MST |
+		OMAP_I2C_CON_STT | OMAP_I2C_CON_STP);
+
+	i = 0;
+	while (i < len) {
+		status = wait_for_event(base);
+		if (status == 0 || (status & OMAP_I2C_STAT_NACK)) {
+			ret = -ENODEV;
+			pr_err("%s: error waiting for data ACK\n",
+					__func__);
+			goto out;
+		}
+		if (status & OMAP_I2C_STAT_RRDY) {
+			*s++ = (u8)i2c_reg_read(base, I2C_DATA);
+			i2c_reg_write(base, I2C_IRQSTATUS,
+					OMAP_I2C_STAT_RRDY);
+			i++;
+		}
+		if (status & OMAP_I2C_STAT_ARDY) {
+			i2c_reg_write(base, I2C_IRQSTATUS,
+					OMAP_I2C_STAT_ARDY);
+			break;
+		}
+	}
+
+	if (i < len)
+		pr_err("%s: short read (%u < %u)\n", __func__, i, len);
+	ret = i;
+
+out:
+	flush_fifo(base);
+	i2c_reg_write(base, I2C_IRQSTATUS, 0xffff);
+	return ret;
+}
+
+struct am335x_baseboard_id {
+	u8 magic[4];
+	u8 name[8];
+	u8 version[4];
+	u8 serial[12];
+	u8 config[32];
+	u8 mac_addr[3][6];
+};
+
+static int beaglebone_read_header(struct am335x_baseboard_id *hdr)
+{
+	void __iomem *base;
+	u32 magic;
+	int ret;
+
+	/* map I2C0 */
+	base = ioremap((phys_addr_t)0x44E0B000, 0x1000);
+	if (base == NULL) {
+		pr_err("%s: failed to ioremap\n", __func__);
+		return -ENOMEM;
+	}
+
+	ret = i2c_init(base, 0x0000, 0x00ea, 0x00ea);
+	if (ret != 0) {
+		pr_err("%s: i2c_init failed\n", __func__);
+		return ret;
+	}
+
+	/* the EEPROM is at 0x50 */
+	ret = i2c_read(base, 0x50, 0, 2, hdr, sizeof(*hdr));
+	if (ret != sizeof(*hdr)) {
+		pr_err("%s: Failed to read EEPROM\n", __func__);
+		ret = ret >= 0 ? -EINVAL : ret;
+		goto out;
+	}
+
+	print_hex_dump(KERN_DEBUG, "EEPROM: ", DUMP_PREFIX_OFFSET,
+			16, 1, hdr, sizeof(*hdr), true);
+
+	/* magic value is LE */
+	magic = ((u32)hdr->magic[3] << 24) | ((u32)hdr->magic[2] << 16) |
+		((u32)hdr->magic[1] << 8) | hdr->magic[0];
+	if (magic != 0xEE3355AA) {
+		pr_err("%s: Bad EEPROM (0x%08x) %02x %02x %02x %02x\n",
+				__func__, magic,
+				hdr->magic[0], hdr->magic[1],
+				hdr->magic[2], hdr->magic[3]);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = 0;
+
+out:
+	iounmap(base);
+	return ret;
+}
+
+/* find whether a command line argument exists */
+static const char *command_line_arg(const char *bootargs, const char *what)
+{
+	const char *s, *e, *p;
+	int len;
+
+	len = strlen(what);
+	s = bootargs;
+	e = bootargs + strlen(bootargs);
+	for (; s < e && (p = strstr(s, what)) != NULL; s += len) {
+
+		/* if not the first arguments a space must precede */
+		if (p > bootargs && p[-1] != ' ')
+			continue;
+
+		/* skip over what */
+		p += len;
+
+		/* if not the last argument a space must follow */
+		if (p < e && *p != ' ')
+			continue;
+
+		return p;
+	}
+
+	return NULL;
+}
+
+static void __init beaglebone_dt_quirk(void)
+{
+	struct am335x_baseboard_id header;
+	struct device_node *np, *revnp, *child, *optnp, *chosen;
+	struct property *prop;
+	char name[8 + 1];
+	const char *detected_board_id, *board_id, *bootargs;
+	int i, ret;
+	phandle ph;
+
+	np = NULL;
+	revnp = NULL;
+	optnp = NULL;
+	chosen = NULL;
+
+	/* beaglebone quirks */
+	np = of_find_compatible_node(NULL, NULL, "ti,am33xx-bone-quirk");
+	if (!np || !of_device_is_available(np))
+		goto out;
+
+	revnp = of_get_child_by_name(np, "revs");
+	if (!revnp) {
+		pr_err("%s: no revs node at %pO\n", __func__, np);
+		goto out;
+	}
+	child = NULL;
+	optnp = NULL;
+	chosen = NULL;
+
+	ret = beaglebone_read_header(&header);
+	if (ret != 0) {
+		pr_err("%s: Failed to read EEPROM\n", __func__);
+		goto out;
+	}
+	memcpy(name, header.name, sizeof(header.name));
+	name[sizeof(header.name)] = '\0';
+
+	detected_board_id = name;
+
+	pr_debug("%s: Finding quirks for board_id=%s\n", __func__,
+			detected_board_id);
+	for_each_child_of_node(revnp, child) {
+		if (!of_property_read_string(child, "board-id", &board_id) &&
+			!strcmp(board_id, detected_board_id))
+			goto found;
+	}
+	pr_warn("%s: No quirks for board_id=%s\n", __func__, detected_board_id);
+	goto out;
+found:
+	pr_debug("%s: Applying quirks for board_id=%s\n", __func__,
+			board_id);
+	ret = 0;
+	for (i = 0; of_property_read_u32_index(child,
+				"board-apply", i, &ph) == 0; i++) {
+
+		ret = of_quirk_apply_by_phandle(ph);
+		if (ret != 0)
+			break;
+	}
+	if (ret != 0) {
+		pr_err("%s: Failed to apply quirk at %pO\n", __func__, child);
+		goto out;
+	}
+
+	optnp = of_get_child_by_name(child, "options");
+	chosen = of_find_node_by_path("/chosen");
+	bootargs = NULL;
+	if (chosen) {
+		if (of_property_read_string(chosen, "bootargs", &bootargs))
+			bootargs = NULL;
+		of_node_put(chosen);
+	}
+	if (optnp && bootargs) {
+		/* iterate on properties */
+		for_each_property_of_node(optnp, prop) {
+			if (!strcmp(prop->name, "name"))
+				continue;
+
+			if (command_line_arg(bootargs, prop->name))
+				i = 0;
+			else
+				i = 1;
+			if (of_property_read_u32_index(optnp,
+						prop->name, i, &ph) != 0) {
+				pr_err("%s: Failed to get phandle at %pO/%s\n",
+						__func__, optnp, prop->name);
+				continue;
+			}
+			ret = of_quirk_apply_by_phandle(ph);
+			if (ret != 0)
+				break;
+		}
+	}
+
+out:
+	of_node_put(optnp);
+	of_node_put(child);
+	of_node_put(revnp);
+	of_node_put(np);
+}
+
+void __init am33xx_dt_quirk(void)
+{
+	/* Manually issue calls for each supported board variant */
+
+	/* the beaglebone is the one supported board for now */
+	beaglebone_dt_quirk();
+}
diff --git a/arch/arm/mach-omap2/am33xx-dt-quirks.h b/arch/arm/mach-omap2/am33xx-dt-quirks.h
new file mode 100644
index 0000000..8c7dbec
--- /dev/null
+++ b/arch/arm/mach-omap2/am33xx-dt-quirks.h
@@ -0,0 +1,10 @@ 
+#ifndef AM33XX_DT_QUIRKS_H
+#define AM33XX_DT_QUIRKS_H
+
+#if IS_ENABLED(OF_DYNAMIC)
+extern void __init am33xx_dt_quirk(void);
+#else
+#define am33xx_dt_quirk	NULL
+#endif
+
+#endif
diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c
index b61c049..9922c61 100644
--- a/arch/arm/mach-omap2/board-generic.c
+++ b/arch/arm/mach-omap2/board-generic.c
@@ -177,6 +177,7 @@  DT_MACHINE_START(AM33XX_DT, "Generic AM33XX (Flattened Device Tree)")
 	.init_time	= omap3_gptimer_timer_init,
 	.dt_compat	= am33xx_boards_compat,
 	.restart	= am33xx_restart,
+	.dt_quirk	= am33xx_dt_quirk,
 MACHINE_END
 #endif
 
diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h
index 64e44d6..6b4d6e0 100644
--- a/arch/arm/mach-omap2/common.h
+++ b/arch/arm/mach-omap2/common.h
@@ -155,6 +155,14 @@  static inline void am33xx_restart(enum reboot_mode mode, const char *cmd)
 }
 #endif
 
+#ifdef CONFIG_SOC_AM33XX
+void am33xx_dt_quirk(void);
+#else
+static inline void am33xx_dt_quirk(void)
+{
+}
+#endif
+
 #ifdef CONFIG_ARCH_OMAP3
 void omap3xxx_restart(enum reboot_mode mode, const char *cmd);
 #else