diff mbox series

[v5,3/5] mtd: Add support for HyperBus memory devices

Message ID 20190609103227.24875-4-vigneshr@ti.com (mailing list archive)
State New, archived
Headers show
Series MTD: Add Initial Hyperbus support | expand

Commit Message

Vignesh Raghavendra June 9, 2019, 10:32 a.m. UTC
Cypress' HyperBus is Low Signal Count, High Performance Double Data Rate
Bus interface between a host system master and one or more slave
interfaces. HyperBus is used to connect microprocessor, microcontroller,
or ASIC devices with random access NOR flash memory (called HyperFlash)
or self refresh DRAM (called HyperRAM).

Its a 8-bit data bus (DQ[7:0]) with  Read-Write Data Strobe (RWDS)
signal and either Single-ended clock(3.0V parts) or Differential clock
(1.8V parts). It uses ChipSelect lines to select b/w multiple slaves.
At bus level, it follows a separate protocol described in HyperBus
specification[1].

HyperFlash follows CFI AMD/Fujitsu Extended Command Set (0x0002) similar
to that of existing parallel NORs. Since HyperBus is x8 DDR bus,
its equivalent to x16 parallel NOR flash wrt bits per clock cycle. But
HyperBus operates at >166MHz frequencies.
HyperRAM provides direct random read/write access to flash memory
array.

But, HyperBus memory controllers seem to abstract implementation details
and expose a simple MMIO interface to access connected flash.

Add support for registering HyperFlash devices with MTD framework. MTD
maps framework along with CFI chip support framework are used to support
communicating with flash.

Framework is modelled along the lines of spi-nor framework. HyperBus
memory controller (HBMC) drivers calls hyperbus_register_device() to
register a single HyperFlash device. HyperFlash core parses MMIO access
information from DT, sets up the map_info struct, probes CFI flash and
registers it with MTD framework.

Some HBMC masters need calibration/training sequence[3] to be carried
out, in order for DLL inside the controller to lock, by reading a known
string/pattern. This is done by repeatedly reading CFI Query
Identification String. Calibration needs to be done before trying to detect
flash as part of CFI flash probe.

HyperRAM is not supported at the moment.

HyperBus specification can be found at[1]
HyperFlash datasheet can be found at[2]

[1] https://www.cypress.com/file/213356/download
[2] https://www.cypress.com/file/213346/download
[3] http://www.ti.com/lit/ug/spruid7b/spruid7b.pdf
    Table 12-5741. HyperFlash Access Sequence

Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
---
v5: No change

 MAINTAINERS                          |   7 +
 drivers/mtd/Kconfig                  |   2 +
 drivers/mtd/Makefile                 |   1 +
 drivers/mtd/hyperbus/Kconfig         |  11 ++
 drivers/mtd/hyperbus/Makefile        |   3 +
 drivers/mtd/hyperbus/hyperbus-core.c | 191 +++++++++++++++++++++++++++
 include/linux/mtd/hyperbus.h         |  91 +++++++++++++
 7 files changed, 306 insertions(+)
 create mode 100644 drivers/mtd/hyperbus/Kconfig
 create mode 100644 drivers/mtd/hyperbus/Makefile
 create mode 100644 drivers/mtd/hyperbus/hyperbus-core.c
 create mode 100644 include/linux/mtd/hyperbus.h

Comments

Sergei Shtylyov June 10, 2019, 5:57 p.m. UTC | #1
On 06/09/2019 01:32 PM, Vignesh Raghavendra wrote:

> Cypress' HyperBus is Low Signal Count, High Performance Double Data Rate
> Bus interface between a host system master and one or more slave
> interfaces. HyperBus is used to connect microprocessor, microcontroller,
> or ASIC devices with random access NOR flash memory (called HyperFlash)
> or self refresh DRAM (called HyperRAM).
> 
> Its a 8-bit data bus (DQ[7:0]) with  Read-Write Data Strobe (RWDS)
> signal and either Single-ended clock(3.0V parts) or Differential clock
> (1.8V parts). It uses ChipSelect lines to select b/w multiple slaves.
> At bus level, it follows a separate protocol described in HyperBus
> specification[1].
> 
> HyperFlash follows CFI AMD/Fujitsu Extended Command Set (0x0002) similar
> to that of existing parallel NORs. Since HyperBus is x8 DDR bus,
> its equivalent to x16 parallel NOR flash wrt bits per clock cycle. But
> HyperBus operates at >166MHz frequencies.
> HyperRAM provides direct random read/write access to flash memory
> array.
> 
> But, HyperBus memory controllers seem to abstract implementation details
> and expose a simple MMIO interface to access connected flash.
> 
> Add support for registering HyperFlash devices with MTD framework. MTD
> maps framework along with CFI chip support framework are used to support
> communicating with flash.
> 
> Framework is modelled along the lines of spi-nor framework. HyperBus
> memory controller (HBMC) drivers calls hyperbus_register_device() to
> register a single HyperFlash device. HyperFlash core parses MMIO access
> information from DT, sets up the map_info struct, probes CFI flash and
> registers it with MTD framework.
> 
> Some HBMC masters need calibration/training sequence[3] to be carried
> out, in order for DLL inside the controller to lock, by reading a known
> string/pattern. This is done by repeatedly reading CFI Query
> Identification String. Calibration needs to be done before trying to detect
> flash as part of CFI flash probe.
> 
> HyperRAM is not supported at the moment.
> 
> HyperBus specification can be found at[1]
> HyperFlash datasheet can be found at[2]
> 
> [1] https://www.cypress.com/file/213356/download
> [2] https://www.cypress.com/file/213346/download
> [3] http://www.ti.com/lit/ug/spruid7b/spruid7b.pdf
>     Table 12-5741. HyperFlash Access Sequence
> 
> Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
[...]
> diff --git a/drivers/mtd/hyperbus/hyperbus-core.c b/drivers/mtd/hyperbus/hyperbus-core.c
> new file mode 100644
> index 000000000000..df1f75e10b1a
> --- /dev/null
> +++ b/drivers/mtd/hyperbus/hyperbus-core.c
> @@ -0,0 +1,191 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//
> +// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
> +// Author: Vignesh Raghavendra <vigneshr@ti.com>
> +
> +#include <linux/err.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mtd/hyperbus.h>
> +#include <linux/mtd/map.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/cfi.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/types.h>
> +
> +#define HYPERBUS_CALIB_COUNT 25

   Mhm, I think I've already protested about this being #define'd here...

[...]
> +int hyperbus_register_device(struct hyperbus_device *hbdev)
> +{
> +	const struct hyperbus_ops *ops;
> +	struct hyperbus_ctlr *ctlr;
> +	struct device_node *np;
> +	struct map_info *map;
> +	struct resource res;
> +	struct device *dev;
> +	int ret;
> +
> +	if (!hbdev || !hbdev->np || !hbdev->ctlr || !hbdev->ctlr->dev) {
> +		pr_err("hyperbus: please fill all the necessary fields!\n");
> +		return -EINVAL;
> +	}
> +
> +	np = hbdev->np;
> +	ctlr = hbdev->ctlr;
> +	if (!of_device_is_compatible(np, "cypress,hyperflash"))
> +		return -ENODEV;
> +
> +	hbdev->memtype = HYPERFLASH;
> +
> +	if (of_address_to_resource(np, 0, &res))
> +		return -EINVAL;

   Why not just propagate the error upstream (yeah, I've noticed that
it only can be -EINVAL)?

[...]
> diff --git a/include/linux/mtd/hyperbus.h b/include/linux/mtd/hyperbus.h
> new file mode 100644
> index 000000000000..ee2eefd822c9
> --- /dev/null
> +++ b/include/linux/mtd/hyperbus.h
> @@ -0,0 +1,91 @@
> +/* SPDX-License-Identifier: GPL-2.0
> + *
> + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
> + */
> +
> +#ifndef __LINUX_MTD_HYPERBUS_H__
> +#define __LINUX_MTD_HYPERBUS_H__
> +
> +#include <linux/mtd/map.h>
> +
> +enum hyperbus_memtype {
> +	HYPERFLASH,
> +	HYPERRAM,
> +};
> +
> +/**
> + * struct hyperbus_device - struct representing HyperBus slave device
> + * @map: map_info struct for accessing MMIO HyperBus flash memory
> + * @np:	pointer to HyperBus slave device node
          ^
   Space needed here, not tab.

> + * @mtd: pointer to MTD struct
> + * @ctlr: pointer to HyperBus controller struct
> + * @memtype: type of memory device: HyperFlash or HyperRAM
> + * @registered: flag to indicate whether device is registered with MTD core
> + */
> +
> +struct hyperbus_device {
> +	struct map_info map;
> +	struct device_node *np;
> +	struct mtd_info *mtd;
> +	struct hyperbus_ctlr *ctlr;
> +	enum hyperbus_memtype memtype;
> +	bool registered;
> +};
> +
> +/**
> + * struct hyperbus_ops - struct representing custom HyperBus operations
> + * @read16: read 16 bit of data, usually from register/ID-CFI space
> + * @write16: write 16 bit of data, usually to register/ID-CFI space

   Usually? How to differ the register/memory transfers if both are possible?

> + * @copy_from: copy data from flash memory
> + * @copy_to: copy data to flash memory
> + * @calibrate: calibrate HyperBus controller
> + */
> +
> +struct hyperbus_ops {
> +	u16 (*read16)(struct hyperbus_device *hbdev, unsigned long addr);
> +	void (*write16)(struct hyperbus_device *hbdev,
> +			unsigned long addr, u16 val);
> +	void (*copy_from)(struct hyperbus_device *hbdev, void *to,
> +			  unsigned long from, ssize_t len);
> +	void (*copy_to)(struct hyperbus_device *dev, unsigned long to,
> +			const void *from, ssize_t len);
> +	int (*calibrate)(struct hyperbus_device *dev);
> +};
> +
> +/**
> + * struct hyperbus_ctlr - struct representing HyperBus controller
> + * @calibrated: flag to indicate ctlr calibration sequence is complete
> + * @ops: HyperBus controller ops

   What about @dev?

> + */
> +struct hyperbus_ctlr {
> +	struct device *dev;
> +	bool calibrated;
> +
> +	const struct hyperbus_ops *ops;
> +};
[...]

MBR, Sergei
Vignesh Raghavendra June 11, 2019, 11:57 a.m. UTC | #2
On 10/06/19 11:27 PM, Sergei Shtylyov wrote:
> On 06/09/2019 01:32 PM, Vignesh Raghavendra wrote:
> 
>> Cypress' HyperBus is Low Signal Count, High Performance Double Data Rate
>> Bus interface between a host system master and one or more slave
>> interfaces. HyperBus is used to connect microprocessor, microcontroller,
>> or ASIC devices with random access NOR flash memory (called HyperFlash)
>> or self refresh DRAM (called HyperRAM).
>>
>> Its a 8-bit data bus (DQ[7:0]) with  Read-Write Data Strobe (RWDS)
>> signal and either Single-ended clock(3.0V parts) or Differential clock
>> (1.8V parts). It uses ChipSelect lines to select b/w multiple slaves.
>> At bus level, it follows a separate protocol described in HyperBus
>> specification[1].
>>
>> HyperFlash follows CFI AMD/Fujitsu Extended Command Set (0x0002) similar
>> to that of existing parallel NORs. Since HyperBus is x8 DDR bus,
>> its equivalent to x16 parallel NOR flash wrt bits per clock cycle. But
>> HyperBus operates at >166MHz frequencies.
>> HyperRAM provides direct random read/write access to flash memory
>> array.
>>
>> But, HyperBus memory controllers seem to abstract implementation details
>> and expose a simple MMIO interface to access connected flash.
>>
>> Add support for registering HyperFlash devices with MTD framework. MTD
>> maps framework along with CFI chip support framework are used to support
>> communicating with flash.
>>
>> Framework is modelled along the lines of spi-nor framework. HyperBus
>> memory controller (HBMC) drivers calls hyperbus_register_device() to
>> register a single HyperFlash device. HyperFlash core parses MMIO access
>> information from DT, sets up the map_info struct, probes CFI flash and
>> registers it with MTD framework.
>>
>> Some HBMC masters need calibration/training sequence[3] to be carried
>> out, in order for DLL inside the controller to lock, by reading a known
>> string/pattern. This is done by repeatedly reading CFI Query
>> Identification String. Calibration needs to be done before trying to detect
>> flash as part of CFI flash probe.
>>
>> HyperRAM is not supported at the moment.
>>
>> HyperBus specification can be found at[1]
>> HyperFlash datasheet can be found at[2]
>>
>> [1] https://www.cypress.com/file/213356/download
>> [2] https://www.cypress.com/file/213346/download
>> [3] http://www.ti.com/lit/ug/spruid7b/spruid7b.pdf
>>     Table 12-5741. HyperFlash Access Sequence
>>
>> Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
> [...]
>> diff --git a/drivers/mtd/hyperbus/hyperbus-core.c b/drivers/mtd/hyperbus/hyperbus-core.c
>> new file mode 100644
>> index 000000000000..df1f75e10b1a
>> --- /dev/null
>> +++ b/drivers/mtd/hyperbus/hyperbus-core.c
>> @@ -0,0 +1,191 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +//
>> +// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
>> +// Author: Vignesh Raghavendra <vigneshr@ti.com>
>> +
>> +#include <linux/err.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/mtd/hyperbus.h>
>> +#include <linux/mtd/map.h>
>> +#include <linux/mtd/mtd.h>
>> +#include <linux/mtd/cfi.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/types.h>
>> +
>> +#define HYPERBUS_CALIB_COUNT 25
> 
>    Mhm, I think I've already protested about this being #define'd here...
> 

I thought you had agreed that default optional calibration routine can
be part of core code and thus this #define.

Anyways, what is your preference here? Drop the constant and use a local
variable in hyperbus_calibrate()?
Or are you suggesting to move hyperbus_calibrate() TI's specific driver?


> [...]
>> +int hyperbus_register_device(struct hyperbus_device *hbdev)
>> +{
>> +	const struct hyperbus_ops *ops;
>> +	struct hyperbus_ctlr *ctlr;
>> +	struct device_node *np;
>> +	struct map_info *map;
>> +	struct resource res;
>> +	struct device *dev;
>> +	int ret;
>> +
>> +	if (!hbdev || !hbdev->np || !hbdev->ctlr || !hbdev->ctlr->dev) {
>> +		pr_err("hyperbus: please fill all the necessary fields!\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	np = hbdev->np;
>> +	ctlr = hbdev->ctlr;
>> +	if (!of_device_is_compatible(np, "cypress,hyperflash"))
>> +		return -ENODEV;
>> +
>> +	hbdev->memtype = HYPERFLASH;
>> +
>> +	if (of_address_to_resource(np, 0, &res))
>> +		return -EINVAL;
> 
>    Why not just propagate the error upstream (yeah, I've noticed that
> it only can be -EINVAL)?
> 

Ok.

> [...]
>> diff --git a/include/linux/mtd/hyperbus.h b/include/linux/mtd/hyperbus.h
>> new file mode 100644
>> index 000000000000..ee2eefd822c9
>> --- /dev/null
>> +++ b/include/linux/mtd/hyperbus.h
>> @@ -0,0 +1,91 @@
>> +/* SPDX-License-Identifier: GPL-2.0
>> + *
>> + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
>> + */
>> +
>> +#ifndef __LINUX_MTD_HYPERBUS_H__
>> +#define __LINUX_MTD_HYPERBUS_H__
>> +
>> +#include <linux/mtd/map.h>
>> +
>> +enum hyperbus_memtype {
>> +	HYPERFLASH,
>> +	HYPERRAM,
>> +};
>> +
>> +/**
>> + * struct hyperbus_device - struct representing HyperBus slave device
>> + * @map: map_info struct for accessing MMIO HyperBus flash memory
>> + * @np:	pointer to HyperBus slave device node
>           ^
>    Space needed here, not tab.
> 

Ok

>> + * @mtd: pointer to MTD struct
>> + * @ctlr: pointer to HyperBus controller struct
>> + * @memtype: type of memory device: HyperFlash or HyperRAM
>> + * @registered: flag to indicate whether device is registered with MTD core
>> + */
>> +
>> +struct hyperbus_device {
>> +	struct map_info map;
>> +	struct device_node *np;
>> +	struct mtd_info *mtd;
>> +	struct hyperbus_ctlr *ctlr;
>> +	enum hyperbus_memtype memtype;
>> +	bool registered;
>> +};
>> +
>> +/**
>> + * struct hyperbus_ops - struct representing custom HyperBus operations
>> + * @read16: read 16 bit of data, usually from register/ID-CFI space
>> + * @write16: write 16 bit of data, usually to register/ID-CFI space
> 
>    Usually? How to differ the register/memory transfers if both are possible?
> 

CFI + map framework does not provide a way to differentiate b/w reg
access vs memory access. read16()/write16() is used to either access
registers or for sending various cmds like lock/unlock etc or for
programming a single word.
For regular read/writes copy_from() and copy_to() are used.

Looking at HyperBus protocol, controllers would not need to
differentiate b/w registers vs memory transfers for HyperFlash devices.
So, I think I can drop read16/write16 and redirect these calls to
copy_from()/copy_to()


I mainly added these functions keeping HyperRAM in mind. Idea was
drivers would look at hyperbus_device->memtype and set to register
access mode for HyperRAM in case of write16()/read16(). Looks like the
interface is not intuitive enough
So, will drop these and add it back when adding HyperRAM support.

Does that work for your HW as well?

>> + * @copy_from: copy data from flash memory
>> + * @copy_to: copy data to flash memory
>> + * @calibrate: calibrate HyperBus controller
>> + */
>> +
>> +struct hyperbus_ops {
>> +	u16 (*read16)(struct hyperbus_device *hbdev, unsigned long addr);
>> +	void (*write16)(struct hyperbus_device *hbdev,
>> +			unsigned long addr, u16 val);
>> +	void (*copy_from)(struct hyperbus_device *hbdev, void *to,
>> +			  unsigned long from, ssize_t len);
>> +	void (*copy_to)(struct hyperbus_device *dev, unsigned long to,
>> +			const void *from, ssize_t len);
>> +	int (*calibrate)(struct hyperbus_device *dev);
>> +};
>> +
>> +/**
>> + * struct hyperbus_ctlr - struct representing HyperBus controller
>> + * @calibrated: flag to indicate ctlr calibration sequence is complete
>> + * @ops: HyperBus controller ops
> 
>    What about @dev?
> 

Will add.

>> + */
>> +struct hyperbus_ctlr {
>> +	struct device *dev;
>> +	bool calibrated;
>> +
>> +	const struct hyperbus_ops *ops;
>> +};
> [...]
> 
> MBR, Sergei
>
Vignesh Raghavendra June 17, 2019, 1:56 p.m. UTC | #3
On 11/06/19 5:27 PM, Vignesh Raghavendra wrote:
> 
> 
> On 10/06/19 11:27 PM, Sergei Shtylyov wrote:
>> On 06/09/2019 01:32 PM, Vignesh Raghavendra wrote:
>>
>>> Cypress' HyperBus is Low Signal Count, High Performance Double Data Rate
>>> Bus interface between a host system master and one or more slave
>>> interfaces. HyperBus is used to connect microprocessor, microcontroller,
>>> or ASIC devices with random access NOR flash memory (called HyperFlash)
>>> or self refresh DRAM (called HyperRAM).
>>>
>>> Its a 8-bit data bus (DQ[7:0]) with  Read-Write Data Strobe (RWDS)
>>> signal and either Single-ended clock(3.0V parts) or Differential clock
>>> (1.8V parts). It uses ChipSelect lines to select b/w multiple slaves.
>>> At bus level, it follows a separate protocol described in HyperBus
>>> specification[1].
>>>
>>> HyperFlash follows CFI AMD/Fujitsu Extended Command Set (0x0002) similar
>>> to that of existing parallel NORs. Since HyperBus is x8 DDR bus,
>>> its equivalent to x16 parallel NOR flash wrt bits per clock cycle. But
>>> HyperBus operates at >166MHz frequencies.
>>> HyperRAM provides direct random read/write access to flash memory
>>> array.
>>>
>>> But, HyperBus memory controllers seem to abstract implementation details
>>> and expose a simple MMIO interface to access connected flash.
>>>
>>> Add support for registering HyperFlash devices with MTD framework. MTD
>>> maps framework along with CFI chip support framework are used to support
>>> communicating with flash.
>>>
>>> Framework is modelled along the lines of spi-nor framework. HyperBus
>>> memory controller (HBMC) drivers calls hyperbus_register_device() to
>>> register a single HyperFlash device. HyperFlash core parses MMIO access
>>> information from DT, sets up the map_info struct, probes CFI flash and
>>> registers it with MTD framework.
>>>
>>> Some HBMC masters need calibration/training sequence[3] to be carried
>>> out, in order for DLL inside the controller to lock, by reading a known
>>> string/pattern. This is done by repeatedly reading CFI Query
>>> Identification String. Calibration needs to be done before trying to detect
>>> flash as part of CFI flash probe.
>>>
>>> HyperRAM is not supported at the moment.
>>>
>>> HyperBus specification can be found at[1]
>>> HyperFlash datasheet can be found at[2]
>>>
>>> [1] https://www.cypress.com/file/213356/download
>>> [2] https://www.cypress.com/file/213346/download
>>> [3] http://www.ti.com/lit/ug/spruid7b/spruid7b.pdf
>>>     Table 12-5741. HyperFlash Access Sequence
>>>
>>> Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
>> [...]
>>> diff --git a/drivers/mtd/hyperbus/hyperbus-core.c b/drivers/mtd/hyperbus/hyperbus-core.c
>>> new file mode 100644
>>> index 000000000000..df1f75e10b1a
>>> --- /dev/null
>>> +++ b/drivers/mtd/hyperbus/hyperbus-core.c
>>> @@ -0,0 +1,191 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +//
>>> +// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
>>> +// Author: Vignesh Raghavendra <vigneshr@ti.com>
>>> +
>>> +#include <linux/err.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/module.h>
>>> +#include <linux/mtd/hyperbus.h>
>>> +#include <linux/mtd/map.h>
>>> +#include <linux/mtd/mtd.h>
>>> +#include <linux/mtd/cfi.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_address.h>
>>> +#include <linux/types.h>
>>> +
>>> +#define HYPERBUS_CALIB_COUNT 25
>>
>>    Mhm, I think I've already protested about this being #define'd here...
>>
> 
> I thought you had agreed that default optional calibration routine can
> be part of core code and thus this #define.
> 
> Anyways, what is your preference here? Drop the constant and use a local
> variable in hyperbus_calibrate()?
> Or are you suggesting to move hyperbus_calibrate() TI's specific driver?
> 
> 
>> [...]
>>> +int hyperbus_register_device(struct hyperbus_device *hbdev)
>>> +{
>>> +	const struct hyperbus_ops *ops;
>>> +	struct hyperbus_ctlr *ctlr;
>>> +	struct device_node *np;
>>> +	struct map_info *map;
>>> +	struct resource res;
>>> +	struct device *dev;
>>> +	int ret;
>>> +
>>> +	if (!hbdev || !hbdev->np || !hbdev->ctlr || !hbdev->ctlr->dev) {
>>> +		pr_err("hyperbus: please fill all the necessary fields!\n");
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	np = hbdev->np;
>>> +	ctlr = hbdev->ctlr;
>>> +	if (!of_device_is_compatible(np, "cypress,hyperflash"))
>>> +		return -ENODEV;
>>> +
>>> +	hbdev->memtype = HYPERFLASH;
>>> +
>>> +	if (of_address_to_resource(np, 0, &res))
>>> +		return -EINVAL;
>>
>>    Why not just propagate the error upstream (yeah, I've noticed that
>> it only can be -EINVAL)?
>>
> 
> Ok.
> 
>> [...]
>>> diff --git a/include/linux/mtd/hyperbus.h b/include/linux/mtd/hyperbus.h
>>> new file mode 100644
>>> index 000000000000..ee2eefd822c9
>>> --- /dev/null
>>> +++ b/include/linux/mtd/hyperbus.h
>>> @@ -0,0 +1,91 @@
>>> +/* SPDX-License-Identifier: GPL-2.0
>>> + *
>>> + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
>>> + */
>>> +
>>> +#ifndef __LINUX_MTD_HYPERBUS_H__
>>> +#define __LINUX_MTD_HYPERBUS_H__
>>> +
>>> +#include <linux/mtd/map.h>
>>> +
>>> +enum hyperbus_memtype {
>>> +	HYPERFLASH,
>>> +	HYPERRAM,
>>> +};
>>> +
>>> +/**
>>> + * struct hyperbus_device - struct representing HyperBus slave device
>>> + * @map: map_info struct for accessing MMIO HyperBus flash memory
>>> + * @np:	pointer to HyperBus slave device node
>>           ^
>>    Space needed here, not tab.
>>
> 
> Ok
> 
>>> + * @mtd: pointer to MTD struct
>>> + * @ctlr: pointer to HyperBus controller struct
>>> + * @memtype: type of memory device: HyperFlash or HyperRAM
>>> + * @registered: flag to indicate whether device is registered with MTD core
>>> + */
>>> +
>>> +struct hyperbus_device {
>>> +	struct map_info map;
>>> +	struct device_node *np;
>>> +	struct mtd_info *mtd;
>>> +	struct hyperbus_ctlr *ctlr;
>>> +	enum hyperbus_memtype memtype;
>>> +	bool registered;
>>> +};
>>> +
>>> +/**
>>> + * struct hyperbus_ops - struct representing custom HyperBus operations
>>> + * @read16: read 16 bit of data, usually from register/ID-CFI space
>>> + * @write16: write 16 bit of data, usually to register/ID-CFI space
>>
>>    Usually? How to differ the register/memory transfers if both are possible?
>>
> 
> CFI + map framework does not provide a way to differentiate b/w reg
> access vs memory access. read16()/write16() is used to either access
> registers or for sending various cmds like lock/unlock etc or for
> programming a single word.
> For regular read/writes copy_from() and copy_to() are used.
> 
> Looking at HyperBus protocol, controllers would not need to
> differentiate b/w registers vs memory transfers for HyperFlash devices.
> So, I think I can drop read16/write16 and redirect these calls to
> copy_from()/copy_to()
> 

Sorry, I realized read16/write16() is required to meet 16bit burst
necessary when writing/reading from HyperFlash non data space or sending
CFI command sequences. Will update the description to reflect the same
and clarify not be confused with register space access bit in HyperBus
protocol.

Regards
Vignesh

> 
> I mainly added these functions keeping HyperRAM in mind. Idea was
> drivers would look at hyperbus_device->memtype and set to register
> access mode for HyperRAM in case of write16()/read16(). Looks like the
> interface is not intuitive enough
> So, will drop these and add it back when adding HyperRAM support.
> 
> Does that work for your HW as well?
> 
>>> + * @copy_from: copy data from flash memory
>>> + * @copy_to: copy data to flash memory
>>> + * @calibrate: calibrate HyperBus controller
>>> + */
>>> +
>>> +struct hyperbus_ops {
>>> +	u16 (*read16)(struct hyperbus_device *hbdev, unsigned long addr);
>>> +	void (*write16)(struct hyperbus_device *hbdev,
>>> +			unsigned long addr, u16 val);
>>> +	void (*copy_from)(struct hyperbus_device *hbdev, void *to,
>>> +			  unsigned long from, ssize_t len);
>>> +	void (*copy_to)(struct hyperbus_device *dev, unsigned long to,
>>> +			const void *from, ssize_t len);
>>> +	int (*calibrate)(struct hyperbus_device *dev);
>>> +};
>>> +
>>> +/**
>>> + * struct hyperbus_ctlr - struct representing HyperBus controller
>>> + * @calibrated: flag to indicate ctlr calibration sequence is complete
>>> + * @ops: HyperBus controller ops
>>
>>    What about @dev?
>>
> 
> Will add.
> 
>>> + */
>>> +struct hyperbus_ctlr {
>>> +	struct device *dev;
>>> +	bool calibrated;
>>> +
>>> +	const struct hyperbus_ops *ops;
>>> +};
>> [...]
>>
>> MBR, Sergei
>>
>
Sergei Shtylyov June 17, 2019, 7:45 p.m. UTC | #4
Hello!

On 06/11/2019 02:57 PM, Vignesh Raghavendra wrote:

>>> Cypress' HyperBus is Low Signal Count, High Performance Double Data Rate
>>> Bus interface between a host system master and one or more slave
>>> interfaces. HyperBus is used to connect microprocessor, microcontroller,
>>> or ASIC devices with random access NOR flash memory (called HyperFlash)
>>> or self refresh DRAM (called HyperRAM).
>>>
>>> Its a 8-bit data bus (DQ[7:0]) with  Read-Write Data Strobe (RWDS)
>>> signal and either Single-ended clock(3.0V parts) or Differential clock
>>> (1.8V parts). It uses ChipSelect lines to select b/w multiple slaves.
>>> At bus level, it follows a separate protocol described in HyperBus
>>> specification[1].
>>>
>>> HyperFlash follows CFI AMD/Fujitsu Extended Command Set (0x0002) similar
>>> to that of existing parallel NORs. Since HyperBus is x8 DDR bus,
>>> its equivalent to x16 parallel NOR flash wrt bits per clock cycle. But
>>> HyperBus operates at >166MHz frequencies.
>>> HyperRAM provides direct random read/write access to flash memory
>>> array.
>>>
>>> But, HyperBus memory controllers seem to abstract implementation details
>>> and expose a simple MMIO interface to access connected flash.
>>>
>>> Add support for registering HyperFlash devices with MTD framework. MTD
>>> maps framework along with CFI chip support framework are used to support
>>> communicating with flash.
>>>
>>> Framework is modelled along the lines of spi-nor framework. HyperBus
>>> memory controller (HBMC) drivers calls hyperbus_register_device() to
>>> register a single HyperFlash device. HyperFlash core parses MMIO access
>>> information from DT, sets up the map_info struct, probes CFI flash and
>>> registers it with MTD framework.
>>>
>>> Some HBMC masters need calibration/training sequence[3] to be carried
>>> out, in order for DLL inside the controller to lock, by reading a known
>>> string/pattern. This is done by repeatedly reading CFI Query
>>> Identification String. Calibration needs to be done before trying to detect
>>> flash as part of CFI flash probe.
>>>
>>> HyperRAM is not supported at the moment.
>>>
>>> HyperBus specification can be found at[1]
>>> HyperFlash datasheet can be found at[2]
>>>
>>> [1] https://www.cypress.com/file/213356/download
>>> [2] https://www.cypress.com/file/213346/download
>>> [3] http://www.ti.com/lit/ug/spruid7b/spruid7b.pdf
>>>     Table 12-5741. HyperFlash Access Sequence
>>>
>>> Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
>> [...]
>>> diff --git a/drivers/mtd/hyperbus/hyperbus-core.c b/drivers/mtd/hyperbus/hyperbus-core.c
>>> new file mode 100644
>>> index 000000000000..df1f75e10b1a
>>> --- /dev/null
>>> +++ b/drivers/mtd/hyperbus/hyperbus-core.c
>>> @@ -0,0 +1,191 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +//
>>> +// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
>>> +// Author: Vignesh Raghavendra <vigneshr@ti.com>
>>> +
>>> +#include <linux/err.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/module.h>
>>> +#include <linux/mtd/hyperbus.h>
>>> +#include <linux/mtd/map.h>
>>> +#include <linux/mtd/mtd.h>
>>> +#include <linux/mtd/cfi.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_address.h>
>>> +#include <linux/types.h>
>>> +
>>> +#define HYPERBUS_CALIB_COUNT 25
>>
>>    Mhm, I think I've already protested about this being #define'd here...
> 
> I thought you had agreed that default optional calibration routine can
> be part of core code and thus this #define.
> 
> Anyways, what is your preference here? Drop the constant and use a local
> variable in hyperbus_calibrate()?
> Or are you suggesting to move hyperbus_calibrate() TI's specific driver?

   I'm just not comfortable with the common HF code using quite an arbitrary
constant...

>> [...]
>>> diff --git a/include/linux/mtd/hyperbus.h b/include/linux/mtd/hyperbus.h
>>> new file mode 100644
>>> index 000000000000..ee2eefd822c9
>>> --- /dev/null
>>> +++ b/include/linux/mtd/hyperbus.h
>>> @@ -0,0 +1,91 @@
[...]
>>> + * @mtd: pointer to MTD struct
>>> + * @ctlr: pointer to HyperBus controller struct
>>> + * @memtype: type of memory device: HyperFlash or HyperRAM
>>> + * @registered: flag to indicate whether device is registered with MTD core
>>> + */
>>> +
>>> +struct hyperbus_device {
>>> +	struct map_info map;
>>> +	struct device_node *np;
>>> +	struct mtd_info *mtd;
>>> +	struct hyperbus_ctlr *ctlr;
>>> +	enum hyperbus_memtype memtype;
>>> +	bool registered;
>>> +};
>>> +
>>> +/**
>>> + * struct hyperbus_ops - struct representing custom HyperBus operations
>>> + * @read16: read 16 bit of data, usually from register/ID-CFI space
>>> + * @write16: write 16 bit of data, usually to register/ID-CFI space
>>
>>    Usually? How to differ the register/memory transfers if both are possible?

> CFI + map framework does not provide a way to differentiate b/w reg
> access vs memory access. read16()/write16() is used to either access
> registers or for sending various cmds like lock/unlock etc or for
> programming a single word.
> For regular read/writes copy_from() and copy_to() are used.

   In my case only copy_from() would exist -- no proper acceleration for
writes...

> Looking at HyperBus protocol, controllers would not need to
> differentiate b/w registers vs memory transfers for HyperFlash devices.
> So, I think I can drop read16/write16 and redirect these calls to
> copy_from()/copy_to()

   Doubt it, frankly speaking.

> I mainly added these functions keeping HyperRAM in mind. Idea was
> drivers would look at hyperbus_device->memtype and set to register
> access mode for HyperRAM in case of write16()/read16(). Looks like the
> interface is not intuitive enough
> So, will drop these and add it back when adding HyperRAM support.
> 
> Does that work for your HW as well?

   Don't think so...

   However, my HyperFlash driver could make use of the following #define's in
the HyperBus header:

#define HF_CMD_CA47		BIT(7)	/* Read */
#define HF_CMD_CA46		BIT(6)	/* Register space */
#define HF_CMD_CA45		BIT(5)	/* Linear burst */

#define HF_CMD_READ_REG		(HF_CMD_CA47 | HF_CMD_CA46)
#define HF_CMD_READ_MEM		HF_CMD_CA47
#define HF_CMD_WRITE_REG	HF_CMD_CA46
#define HF_CMD_WRITE_MEM	0

MBR, Sergei
Vignesh Raghavendra June 18, 2019, 7:38 a.m. UTC | #5
On 18/06/19 1:15 AM, Sergei Shtylyov wrote:
> Hello!
> 
> On 06/11/2019 02:57 PM, Vignesh Raghavendra wrote:
> 
>>>> Cypress' HyperBus is Low Signal Count, High Performance Double Data Rate
>>>> Bus interface between a host system master and one or more slave
>>>> interfaces. HyperBus is used to connect microprocessor, microcontroller,
>>>> or ASIC devices with random access NOR flash memory (called HyperFlash)
>>>> or self refresh DRAM (called HyperRAM).
>>>>
>>>> Its a 8-bit data bus (DQ[7:0]) with  Read-Write Data Strobe (RWDS)
>>>> signal and either Single-ended clock(3.0V parts) or Differential clock
>>>> (1.8V parts). It uses ChipSelect lines to select b/w multiple slaves.
>>>> At bus level, it follows a separate protocol described in HyperBus
>>>> specification[1].
>>>>
>>>> HyperFlash follows CFI AMD/Fujitsu Extended Command Set (0x0002) similar
>>>> to that of existing parallel NORs. Since HyperBus is x8 DDR bus,
>>>> its equivalent to x16 parallel NOR flash wrt bits per clock cycle. But
>>>> HyperBus operates at >166MHz frequencies.
>>>> HyperRAM provides direct random read/write access to flash memory
>>>> array.
>>>>
>>>> But, HyperBus memory controllers seem to abstract implementation details
>>>> and expose a simple MMIO interface to access connected flash.
>>>>
>>>> Add support for registering HyperFlash devices with MTD framework. MTD
>>>> maps framework along with CFI chip support framework are used to support
>>>> communicating with flash.
>>>>
>>>> Framework is modelled along the lines of spi-nor framework. HyperBus
>>>> memory controller (HBMC) drivers calls hyperbus_register_device() to
>>>> register a single HyperFlash device. HyperFlash core parses MMIO access
>>>> information from DT, sets up the map_info struct, probes CFI flash and
>>>> registers it with MTD framework.
>>>>
>>>> Some HBMC masters need calibration/training sequence[3] to be carried
>>>> out, in order for DLL inside the controller to lock, by reading a known
>>>> string/pattern. This is done by repeatedly reading CFI Query
>>>> Identification String. Calibration needs to be done before trying to detect
>>>> flash as part of CFI flash probe.
>>>>
>>>> HyperRAM is not supported at the moment.
>>>>
>>>> HyperBus specification can be found at[1]
>>>> HyperFlash datasheet can be found at[2]
>>>>
>>>> [1] https://www.cypress.com/file/213356/download
>>>> [2] https://www.cypress.com/file/213346/download
>>>> [3] http://www.ti.com/lit/ug/spruid7b/spruid7b.pdf
>>>>     Table 12-5741. HyperFlash Access Sequence
>>>>
>>>> Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
>>> [...]
>>>> diff --git a/drivers/mtd/hyperbus/hyperbus-core.c b/drivers/mtd/hyperbus/hyperbus-core.c
>>>> new file mode 100644
>>>> index 000000000000..df1f75e10b1a
>>>> --- /dev/null
>>>> +++ b/drivers/mtd/hyperbus/hyperbus-core.c
>>>> @@ -0,0 +1,191 @@
>>>> +// SPDX-License-Identifier: GPL-2.0
>>>> +//
>>>> +// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
>>>> +// Author: Vignesh Raghavendra <vigneshr@ti.com>
>>>> +
>>>> +#include <linux/err.h>
>>>> +#include <linux/kernel.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/mtd/hyperbus.h>
>>>> +#include <linux/mtd/map.h>
>>>> +#include <linux/mtd/mtd.h>
>>>> +#include <linux/mtd/cfi.h>
>>>> +#include <linux/of.h>
>>>> +#include <linux/of_address.h>
>>>> +#include <linux/types.h>
>>>> +
>>>> +#define HYPERBUS_CALIB_COUNT 25
>>>
>>>    Mhm, I think I've already protested about this being #define'd here...
>>
>> I thought you had agreed that default optional calibration routine can
>> be part of core code and thus this #define.
>>
>> Anyways, what is your preference here? Drop the constant and use a local
>> variable in hyperbus_calibrate()?
>> Or are you suggesting to move hyperbus_calibrate() TI's specific driver?
> 
>    I'm just not comfortable with the common HF code using quite an arbitrary
> constant...
> 

Ok, I will move the code over to TI driver. We can always bring it back
to core code if more drivers need it.

>>> [...]
>>>> diff --git a/include/linux/mtd/hyperbus.h b/include/linux/mtd/hyperbus.h
>>>> new file mode 100644
>>>> index 000000000000..ee2eefd822c9
>>>> --- /dev/null
>>>> +++ b/include/linux/mtd/hyperbus.h
>>>> @@ -0,0 +1,91 @@
> [...]
>>>> + * @mtd: pointer to MTD struct
>>>> + * @ctlr: pointer to HyperBus controller struct
>>>> + * @memtype: type of memory device: HyperFlash or HyperRAM
>>>> + * @registered: flag to indicate whether device is registered with MTD core
>>>> + */
>>>> +
>>>> +struct hyperbus_device {
>>>> +	struct map_info map;
>>>> +	struct device_node *np;
>>>> +	struct mtd_info *mtd;
>>>> +	struct hyperbus_ctlr *ctlr;
>>>> +	enum hyperbus_memtype memtype;
>>>> +	bool registered;
>>>> +};
>>>> +
>>>> +/**
>>>> + * struct hyperbus_ops - struct representing custom HyperBus operations
>>>> + * @read16: read 16 bit of data, usually from register/ID-CFI space
>>>> + * @write16: write 16 bit of data, usually to register/ID-CFI space
>>>
>>>    Usually? How to differ the register/memory transfers if both are possible?
> 
>> CFI + map framework does not provide a way to differentiate b/w reg
>> access vs memory access. read16()/write16() is used to either access
>> registers or for sending various cmds like lock/unlock etc or for
>> programming a single word.
>> For regular read/writes copy_from() and copy_to() are used.
> 
>    In my case only copy_from() would exist -- no proper acceleration for
> writes...
> 

Actually copy_to() is not used by cfi_cmdset_0002.c, its always
write16() that used to program flash.
This is something I want to extend support to, so as to use DMA for
writes as well because I see that writes seem extremely slow at least on
my platform.

>> Looking at HyperBus protocol, controllers would not need to
>> differentiate b/w registers vs memory transfers for HyperFlash devices.
>> So, I think I can drop read16/write16 and redirect these calls to
>> copy_from()/copy_to()
> 
>    Doubt it, frankly speaking.

Sorry for confusion, as I said above, we do need to keep write16().
Also, copy_to maps to memcpy_toio in case of simple_map which may not
use 16 bit IO accessors. So write16() cannot be mapped to copy_to()

So we need at least write16(), copy_from() and mostly copy_to() (for
accelerating writes). So, lets keep this simple and have all map ops
including read16() as is.


> 
>> I mainly added these functions keeping HyperRAM in mind. Idea was
>> drivers would look at hyperbus_device->memtype and set to register
>> access mode for HyperRAM in case of write16()/read16(). Looks like the
>> interface is not intuitive enough
>> So, will drop these and add it back when adding HyperRAM support.
>>
>> Does that work for your HW as well?
> 
>    Don't think so...
> 
>    However, my HyperFlash driver could make use of the following #define's in
> the HyperBus header:
> 
> #define HF_CMD_CA47		BIT(7)	/* Read */
> #define HF_CMD_CA46		BIT(6)	/* Register space */
> #define HF_CMD_CA45		BIT(5)	/* Linear burst */
> 
> #define HF_CMD_READ_REG		(HF_CMD_CA47 | HF_CMD_CA46)

This will come into play for HyperRAM, not a care about for HyperFlash

> #define HF_CMD_READ_MEM		HF_CMD_CA47

For HyperFlash, this would be the only bit that needs to be set for read
along with HF_CMD_CA45 for linear burst.

> #define HF_CMD_WRITE_REG	HF_CMD_CA46
> #define HF_CMD_WRITE_MEM	0
> 
> MBR, Sergei
>
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 7ed4d54956a2..258cf0d9eb8f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7338,6 +7338,13 @@  F:	include/asm-generic/mshyperv.h
 F:	tools/hv/
 F:	Documentation/ABI/stable/sysfs-bus-vmbus
 
+HYPERBUS SUPPORT
+M:	Vignesh Raghavendra <vigneshr@ti.com>
+S:	Supported
+F:	drivers/mtd/hyperbus/
+F:	include/linux/mtd/hyperbus.h
+F:	Documentation/devicetree/bindings/mtd/cypress,hyperflash.txt
+
 HYPERVISOR VIRTUAL CONSOLE DRIVER
 L:	linuxppc-dev@lists.ozlabs.org
 S:	Odd Fixes
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index fb31a7f649a3..80a6e2dcd085 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -274,4 +274,6 @@  source "drivers/mtd/spi-nor/Kconfig"
 
 source "drivers/mtd/ubi/Kconfig"
 
+source "drivers/mtd/hyperbus/Kconfig"
+
 endif # MTD
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index 806287e80e84..62d649a959e2 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -34,3 +34,4 @@  obj-y		+= chips/ lpddr/ maps/ devices/ nand/ tests/
 
 obj-$(CONFIG_MTD_SPI_NOR)	+= spi-nor/
 obj-$(CONFIG_MTD_UBI)		+= ubi/
+obj-$(CONFIG_MTD_HYPERBUS)	+= hyperbus/
diff --git a/drivers/mtd/hyperbus/Kconfig b/drivers/mtd/hyperbus/Kconfig
new file mode 100644
index 000000000000..98147e28caa0
--- /dev/null
+++ b/drivers/mtd/hyperbus/Kconfig
@@ -0,0 +1,11 @@ 
+menuconfig MTD_HYPERBUS
+	tristate "HyperBus support"
+	select MTD_CFI
+	select MTD_MAP_BANK_WIDTH_2
+	select MTD_CFI_AMDSTD
+	select MTD_COMPLEX_MAPPINGS
+	help
+	  This is the framework for the HyperBus which can be used by
+	  the HyperBus Controller driver to communicate with
+	  HyperFlash. See Cypress HyperBus specification for more
+	  details
diff --git a/drivers/mtd/hyperbus/Makefile b/drivers/mtd/hyperbus/Makefile
new file mode 100644
index 000000000000..ca61dedd730d
--- /dev/null
+++ b/drivers/mtd/hyperbus/Makefile
@@ -0,0 +1,3 @@ 
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_MTD_HYPERBUS)	+= hyperbus-core.o
diff --git a/drivers/mtd/hyperbus/hyperbus-core.c b/drivers/mtd/hyperbus/hyperbus-core.c
new file mode 100644
index 000000000000..df1f75e10b1a
--- /dev/null
+++ b/drivers/mtd/hyperbus/hyperbus-core.c
@@ -0,0 +1,191 @@ 
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+// Author: Vignesh Raghavendra <vigneshr@ti.com>
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mtd/hyperbus.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/cfi.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/types.h>
+
+#define HYPERBUS_CALIB_COUNT 25
+
+static struct hyperbus_device *map_to_hbdev(struct map_info *map)
+{
+	return container_of(map, struct hyperbus_device, map);
+}
+
+static map_word hyperbus_read16(struct map_info *map, unsigned long addr)
+{
+	struct hyperbus_device *hbdev = map_to_hbdev(map);
+	struct hyperbus_ctlr *ctlr = hbdev->ctlr;
+	map_word read_data;
+
+	read_data.x[0] = ctlr->ops->read16(hbdev, addr);
+
+	return read_data;
+}
+
+static void hyperbus_write16(struct map_info *map, map_word d,
+			     unsigned long addr)
+{
+	struct hyperbus_device *hbdev = map_to_hbdev(map);
+	struct hyperbus_ctlr *ctlr = hbdev->ctlr;
+
+	ctlr->ops->write16(hbdev, addr, d.x[0]);
+}
+
+static void hyperbus_copy_from(struct map_info *map, void *to,
+			       unsigned long from, ssize_t len)
+{
+	struct hyperbus_device *hbdev = map_to_hbdev(map);
+	struct hyperbus_ctlr *ctlr = hbdev->ctlr;
+
+	ctlr->ops->copy_from(hbdev, to, from, len);
+}
+
+static void hyperbus_copy_to(struct map_info *map, unsigned long to,
+			     const void *from, ssize_t len)
+{
+	struct hyperbus_device *hbdev = map_to_hbdev(map);
+	struct hyperbus_ctlr *ctlr = hbdev->ctlr;
+
+	ctlr->ops->copy_to(hbdev, to, from, len);
+}
+
+/* Default calibration routine for use by HyperBus controller.
+ * Controller is calibrated by repeatedly reading known pattern ("QRY"
+ * string from CFI space)
+ * Lets ensure "QRY" string is read correctly at least 5 times to ensure
+ * stability of the DLL lock.
+ */
+int hyperbus_calibrate(struct hyperbus_device *hbdev)
+{
+	struct map_info *map = &hbdev->map;
+	struct cfi_private cfi;
+	int count = HYPERBUS_CALIB_COUNT;
+	int pass_count = 0;
+	int ret;
+
+	cfi.interleave = 1;
+	cfi.device_type = CFI_DEVICETYPE_X16;
+	cfi_send_gen_cmd(0xF0, 0, 0, map, &cfi, cfi.device_type, NULL);
+	cfi_send_gen_cmd(0x98, 0x55, 0, map, &cfi, cfi.device_type, NULL);
+
+	while (count--) {
+		ret = cfi_qry_present(map, 0, &cfi);
+		if (ret)
+			pass_count++;
+		else
+			pass_count = 0;
+		if (pass_count == 5)
+			break;
+	}
+
+	cfi_qry_mode_off(0, map, &cfi);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(hyperbus_calibrate);
+
+int hyperbus_register_device(struct hyperbus_device *hbdev)
+{
+	const struct hyperbus_ops *ops;
+	struct hyperbus_ctlr *ctlr;
+	struct device_node *np;
+	struct map_info *map;
+	struct resource res;
+	struct device *dev;
+	int ret;
+
+	if (!hbdev || !hbdev->np || !hbdev->ctlr || !hbdev->ctlr->dev) {
+		pr_err("hyperbus: please fill all the necessary fields!\n");
+		return -EINVAL;
+	}
+
+	np = hbdev->np;
+	ctlr = hbdev->ctlr;
+	if (!of_device_is_compatible(np, "cypress,hyperflash"))
+		return -ENODEV;
+
+	hbdev->memtype = HYPERFLASH;
+
+	if (of_address_to_resource(np, 0, &res))
+		return -EINVAL;
+
+	dev = ctlr->dev;
+	map = &hbdev->map;
+	map->size = resource_size(&res);
+	map->virt = devm_ioremap_resource(dev, &res);
+	if (IS_ERR(map->virt))
+		return PTR_ERR(map->virt);
+
+	map->name = dev_name(dev);
+	map->bankwidth = 2;
+	map->device_node = np;
+
+	simple_map_init(map);
+	ops = ctlr->ops;
+	if (ops) {
+		if (ops->read16)
+			map->read = hyperbus_read16;
+		if (ops->write16)
+			map->write = hyperbus_write16;
+		if (ops->copy_to)
+			map->copy_to = hyperbus_copy_to;
+		if (ops->copy_from)
+			map->copy_from = hyperbus_copy_from;
+
+		if (ops->calibrate && !ctlr->calibrated) {
+			ret = ops->calibrate(hbdev);
+			if (!ret) {
+				dev_err(dev, "Calibration failed\n");
+				return -ENODEV;
+			}
+			ctlr->calibrated = true;
+		}
+	}
+
+	hbdev->mtd = do_map_probe("cfi_probe", map);
+	if (!hbdev->mtd) {
+		dev_err(dev, "probing of hyperbus device failed\n");
+		return -ENODEV;
+	}
+
+	hbdev->mtd->dev.parent = dev;
+	mtd_set_of_node(hbdev->mtd, np);
+
+	ret = mtd_device_register(hbdev->mtd, NULL, 0);
+	if (ret) {
+		dev_err(dev, "failed to register mtd device\n");
+		map_destroy(hbdev->mtd);
+		return ret;
+	}
+	hbdev->registered = true;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(hyperbus_register_device);
+
+int hyperbus_unregister_device(struct hyperbus_device *hbdev)
+{
+	int ret = 0;
+
+	if (hbdev && hbdev->mtd && hbdev->registered) {
+		ret = mtd_device_unregister(hbdev->mtd);
+		map_destroy(hbdev->mtd);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(hyperbus_unregister_device);
+
+MODULE_DESCRIPTION("HyperBus Framework");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Vignesh Raghavendra <vigneshr@ti.com>");
diff --git a/include/linux/mtd/hyperbus.h b/include/linux/mtd/hyperbus.h
new file mode 100644
index 000000000000..ee2eefd822c9
--- /dev/null
+++ b/include/linux/mtd/hyperbus.h
@@ -0,0 +1,91 @@ 
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+#ifndef __LINUX_MTD_HYPERBUS_H__
+#define __LINUX_MTD_HYPERBUS_H__
+
+#include <linux/mtd/map.h>
+
+enum hyperbus_memtype {
+	HYPERFLASH,
+	HYPERRAM,
+};
+
+/**
+ * struct hyperbus_device - struct representing HyperBus slave device
+ * @map: map_info struct for accessing MMIO HyperBus flash memory
+ * @np:	pointer to HyperBus slave device node
+ * @mtd: pointer to MTD struct
+ * @ctlr: pointer to HyperBus controller struct
+ * @memtype: type of memory device: HyperFlash or HyperRAM
+ * @registered: flag to indicate whether device is registered with MTD core
+ */
+
+struct hyperbus_device {
+	struct map_info map;
+	struct device_node *np;
+	struct mtd_info *mtd;
+	struct hyperbus_ctlr *ctlr;
+	enum hyperbus_memtype memtype;
+	bool registered;
+};
+
+/**
+ * struct hyperbus_ops - struct representing custom HyperBus operations
+ * @read16: read 16 bit of data, usually from register/ID-CFI space
+ * @write16: write 16 bit of data, usually to register/ID-CFI space
+ * @copy_from: copy data from flash memory
+ * @copy_to: copy data to flash memory
+ * @calibrate: calibrate HyperBus controller
+ */
+
+struct hyperbus_ops {
+	u16 (*read16)(struct hyperbus_device *hbdev, unsigned long addr);
+	void (*write16)(struct hyperbus_device *hbdev,
+			unsigned long addr, u16 val);
+	void (*copy_from)(struct hyperbus_device *hbdev, void *to,
+			  unsigned long from, ssize_t len);
+	void (*copy_to)(struct hyperbus_device *dev, unsigned long to,
+			const void *from, ssize_t len);
+	int (*calibrate)(struct hyperbus_device *dev);
+};
+
+/**
+ * struct hyperbus_ctlr - struct representing HyperBus controller
+ * @calibrated: flag to indicate ctlr calibration sequence is complete
+ * @ops: HyperBus controller ops
+ */
+struct hyperbus_ctlr {
+	struct device *dev;
+	bool calibrated;
+
+	const struct hyperbus_ops *ops;
+};
+
+/**
+ * hyperbus_calibrate - default calibration routine for use by HyperBus ctlr.
+ * @hbdev: hyperbus_device to be used for calibration
+ *
+ * Return: 1 for success, 0 for failure.
+ */
+int hyperbus_calibrate(struct hyperbus_device *hbdev);
+
+/**
+ * hyperbus_register_device - probe and register a HyperBus slave memory device
+ * @hbdev: hyperbus_device struct with dev, np and ctlr field populated
+ *
+ * Return: 0 for success, others for failure.
+ */
+int hyperbus_register_device(struct hyperbus_device *hbdev);
+
+/**
+ * hyperbus_unregister_device - deregister HyperBus slave memory device
+ * @hbdev: hyperbus_device to be unregistered
+ *
+ * Return: 0 for success, others for failure.
+ */
+int hyperbus_unregister_device(struct hyperbus_device *hbdev);
+
+#endif /* __LINUX_MTD_HYPERBUS_H__ */