[U-Boot] [PATCH v4 0/7] OMAP: Move SATA to use block driver model and introduce a new framework for PHY management

This series adds support for SATA using the driver model on omap platforms. It is based on the work of Mugunthan V N mugunthanvnm@ti.com in Feb 2016 In the process it adds a new framework to manage PHY devices
changes since v3: * Add a patch to split scsi_scan() as discussed in https://patchwork.ozlabs.org/patch/748210/ * reworked the PHY framework to support multiple port PHY devices (required to be compatible with the device-tree bindings used in linux). Updated the tests as well. * moved away from "generic[_-]phy" to simply use 'phy' instead. Expect for the name of the header file 'generic-phy.h' and for the names of the functions of the API, because it's already used by the Ethernet PHY code. * rebased on top of u-boot-dm/master * removed the patches already taken in u-boot-dm/master
changes since v2: * Added unit tests for generic PHY uclass. * Changed the generic phy API to use a 'struct udevice*' to reference the phy device. * Don't modify anymore the root Makefile. * Updated the Kconfig with more details on the PHY framework. * Use a Unique Lincense Identifier (SPDX) * Use omap5-u-boot.dtsi to mark the ocp2scp bus as compatible with "simple-bus" * Split scsi_scan() in 2 for clarity. The function was becoming too long.
changes since v1: * changed the way the 'old' sata code is compiled out when DM_SCSI is enabled. * added a new framework for PHY management. * added a new driver for the PIPE3 phy and use it in the dwc_ahci driver. * changed the interface of scsi_detect_dev() and moved the call part_init() out of it. * modified the fix to scsi_scan() in order to call scsi_detect_dev() only once. * the max_lun and max_id parameters are now taken from the device-tree. * Updated the defconfig changes to include the PHY driver and its dependencies.
Jean-Jacques Hiblot (7): scsi: dm: split scsi_scan() drivers: phy: add generic PHY framework dm: test: Add tests for the generic PHY uclass drivers: phy: add PIPE3 phy driver dra7: dtsi: mark ocp2scp bus compatible with "simple-bus" drivers: block: dwc_ahci: Implement a driver for Synopsys DWC sata device defconfig: dra7xx_evm: enable CONFIG_BLK and disk driver model for SCSI
arch/arm/dts/omap5-u-boot.dtsi | 4 + arch/sandbox/dts/test.dts | 17 ++ common/scsi.c | 102 ++++++----- configs/dra7xx_evm_defconfig | 12 +- configs/dra7xx_hs_evm_defconfig | 11 +- configs/sandbox_defconfig | 2 + configs/sandbox_noblk_defconfig | 2 + configs/sandbox_spl_defconfig | 3 + drivers/Kconfig | 2 + drivers/Makefile | 2 + drivers/block/Kconfig | 10 ++ drivers/block/Makefile | 1 + drivers/block/dwc_ahci.c | 101 +++++++++++ drivers/phy/Kconfig | 63 +++++++ drivers/phy/Makefile | 4 + drivers/phy/phy-uclass.c | 139 +++++++++++++++ drivers/phy/sandbox-phy.c | 98 +++++++++++ drivers/phy/ti-pipe3-phy.c | 373 ++++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/generic-phy.h | 224 ++++++++++++++++++++++++ test/dm/Makefile | 2 + test/dm/phy.c | 112 ++++++++++++ 22 files changed, 1230 insertions(+), 55 deletions(-) create mode 100644 drivers/block/dwc_ahci.c create mode 100644 drivers/phy/Kconfig create mode 100644 drivers/phy/Makefile create mode 100644 drivers/phy/phy-uclass.c create mode 100644 drivers/phy/sandbox-phy.c create mode 100644 drivers/phy/ti-pipe3-phy.c create mode 100644 include/generic-phy.h create mode 100644 test/dm/phy.c

The DM version of scsi_scan() is becoming a bit long, it can be split: scsi_scan() iterates over the IDs and LUNs and for each id/lun pair calls do_scsi_scan_one() to do the work of: - detecting an attached drive - creating the associated block device if a drive is found.
Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com --- common/scsi.c | 102 ++++++++++++++++++++++++++++------------------------------ 1 file changed, 49 insertions(+), 53 deletions(-)
diff --git a/common/scsi.c b/common/scsi.c index d37222c..c456f5a 100644 --- a/common/scsi.c +++ b/common/scsi.c @@ -549,6 +549,52 @@ removable: * to the user if mode = 1 */ #if defined(CONFIG_DM_SCSI) +static int do_scsi_scan_one(struct udevice *dev, int id, int lun, int mode) +{ + int ret; + struct udevice *bdev; + struct blk_desc bd; + struct blk_desc *bdesc; + char str[10]; + + /* + * detect the scsi driver to get information about its geometry (block + * size, number of blocks) and other parameters (ids, type, ...) + */ + scsi_init_dev_desc_priv(&bd); + if (scsi_detect_dev(id, lun, &bd)) + return -ENODEV; + + /* + * Create only one block device and do detection + * to make sure that there won't be a lot of + * block devices created + */ + snprintf(str, sizeof(str), "id%dlun%d", id, lun); + ret = blk_create_devicef(dev, "scsi_blk", str, IF_TYPE_SCSI, -1, + bd.blksz, bd.blksz * bd.lba, &bdev); + if (ret) { + debug("Can't create device\n"); + return ret; + } + + bdesc = dev_get_uclass_platdata(bdev); + bdesc->target = id; + bdesc->lun = lun; + bdesc->removable = bd.removable; + bdesc->type = bd.type; + memcpy(&bdesc->vendor, &bd.vendor, sizeof(bd.vendor)); + memcpy(&bdesc->product, &bd.product, sizeof(bd.product)); + memcpy(&bdesc->revision, &bd.revision, sizeof(bd.revision)); + part_init(bdesc); + + if (mode == 1) { + printf(" Device %d: ", 0); + dev_print(bdesc); + } + return 0; +} + int scsi_scan(int mode) { unsigned char i, lun; @@ -576,59 +622,9 @@ int scsi_scan(int mode) /* Get controller platdata */ plat = dev_get_platdata(dev);
- for (i = 0; i < plat->max_id; i++) { - for (lun = 0; lun < plat->max_lun; lun++) { - struct udevice *bdev; /* block device */ - /* block device description */ - struct blk_desc _bd; - struct blk_desc *bdesc; - char str[10]; - - scsi_init_dev_desc_priv(&_bd); - ret = scsi_detect_dev(i, lun, &_bd); - if (ret) - /* - * no device detected? - * check the next lun. - */ - continue; - - /* - * Create only one block device and do detection - * to make sure that there won't be a lot of - * block devices created - */ - snprintf(str, sizeof(str), "id%dlun%d", i, lun); - ret = blk_create_devicef(dev, "scsi_blk", - str, IF_TYPE_SCSI, - -1, - _bd.blksz, - _bd.blksz * _bd.lba, - &bdev); - if (ret) { - debug("Can't create device\n"); - return ret; - } - - bdesc = dev_get_uclass_platdata(bdev); - bdesc->target = i; - bdesc->lun = lun; - bdesc->removable = _bd.removable; - bdesc->type = _bd.type; - memcpy(&bdesc->vendor, &_bd.vendor, - sizeof(_bd.vendor)); - memcpy(&bdesc->product, &_bd.product, - sizeof(_bd.product)); - memcpy(&bdesc->revision, &_bd.revision, - sizeof(_bd.revision)); - part_init(bdesc); - - if (mode == 1) { - printf(" Device %d: ", 0); - dev_print(bdesc); - } /* if mode */ - } /* next LUN */ - } + for (i = 0; i < plat->max_id; i++) + for (lun = 0; lun < plat->max_lun; lun++) + do_scsi_scan_one(dev, i, lun, mode); }
return 0;

On 21 April 2017 at 04:08, Jean-Jacques Hiblot jjhiblot@ti.com wrote:
The DM version of scsi_scan() is becoming a bit long, it can be split: scsi_scan() iterates over the IDs and LUNs and for each id/lun pair calls do_scsi_scan_one() to do the work of:
- detecting an attached drive
- creating the associated block device if a drive is found.
Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com
common/scsi.c | 102 ++++++++++++++++++++++++++++------------------------------ 1 file changed, 49 insertions(+), 53 deletions(-)
Reviewed-by: Simon Glass sjg@chromium.org

The PHY framework provides a set of APIs to control a PHY. This API is derived from the linux version of the generic PHY framework. Currently the API supports init(), deinit(), power_on, power_off() and reset(). The framework provides a way to get a reference to a phy from the device-tree.
Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com ---
changes since v3: * make it possible for a phy device to handle multiple ports. The consequence is the intruction of struct phy which is is used as a parameter to all the functions of the API. * Add generic_phy_get_by_index() and generic_phy_get_by_name() to get a reference to a phy port. Removed dm_generic_phy_get(). * Improved the documentation
drivers/Kconfig | 2 + drivers/Makefile | 2 + drivers/phy/Kconfig | 36 ++++++++ drivers/phy/Makefile | 2 + drivers/phy/phy-uclass.c | 139 +++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/generic-phy.h | 224 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 406 insertions(+) create mode 100644 drivers/phy/Kconfig create mode 100644 drivers/phy/Makefile create mode 100644 drivers/phy/phy-uclass.c create mode 100644 include/generic-phy.h
diff --git a/drivers/Kconfig b/drivers/Kconfig index 3e6bbac..1fe5baf 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -90,6 +90,8 @@ source "drivers/video/Kconfig"
source "drivers/watchdog/Kconfig"
+source "drivers/phy/Kconfig" + config PHYS_TO_BUS bool "Custom physical to bus address mapping" help diff --git a/drivers/Makefile b/drivers/Makefile index 5d8baa5..c2700a0 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -5,9 +5,11 @@ obj-$(CONFIG_$(SPL_TPL_)DM) += core/ obj-$(CONFIG_$(SPL_)CLK) += clk/ obj-$(CONFIG_$(SPL_)LED) += led/ +obj-$(CONFIG_$(SPL_)PHY) += phy/ obj-$(CONFIG_$(SPL_)PINCTRL) += pinctrl/ obj-$(CONFIG_$(SPL_)RAM) += ram/
+ ifdef CONFIG_SPL_BUILD
obj-$(CONFIG_SPL_CPU_SUPPORT) += cpu/ diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig new file mode 100644 index 0000000..0a74920 --- /dev/null +++ b/drivers/phy/Kconfig @@ -0,0 +1,36 @@ + +menu "PHY Subsystem" + +config PHY + bool "PHY Core" + depends on DM + help + PHY support. + + This framework is designed to provide a generic interface for PHY + devices. PHY devices are dedicated hardware that handle the physical + layer of the protocols in the OSI model. + PHYs are commonly used for high speed interfaces such as Serial-ATA + or PCI express. + The API provides functions to initialize/deinitialize the + PHY, power on/off the PHY, and reset the PHY. It's meant to be as + compatible as possible with the equivalent framework found in the + linux kernel. + +config SPL_PHY + bool "PHY Core in SPL" + depends on DM + help + PHY support in SPL. + + This framework is designed to provide a generic interface for PHY + devices. PHY devices are dedicated hardware that handle the physical + layer of the protocols (https://en.wikipedia.org/wiki/OSI_model). + PHYs are commonly used for high speed interfaces such as Serial-ATA + or PCI express. + The API provides functions to initialize/deinitialize the + PHY, power on/off the PHY, and reset the PHY. It's meant to be as + compatible as possible with the equivalent framework found in the + linux kernel. + +endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile new file mode 100644 index 0000000..186f07d --- /dev/null +++ b/drivers/phy/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_$(SPL_)PHY) += phy-uclass.o + diff --git a/drivers/phy/phy-uclass.c b/drivers/phy/phy-uclass.c new file mode 100644 index 0000000..0d8bef7 --- /dev/null +++ b/drivers/phy/phy-uclass.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Written by Jean-Jacques Hiblot jjhiblot@ti.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <generic-phy.h> + +DECLARE_GLOBAL_DATA_PTR; + +static inline struct phy_ops *phy_dev_ops(struct udevice *dev) +{ + return (struct phy_ops *)dev->driver->ops; +} + +static int generic_phy_xlate_offs_flags(struct phy *phy, + struct fdtdec_phandle_args *args) +{ + debug("%s(phy=%p)\n", __func__, phy); + + if (args->args_count > 1) { + debug("Invaild args_count: %d\n", args->args_count); + return -EINVAL; + } + + if (args->args_count) + phy->id = args->args[0]; + else + phy->id = 0; + + + return 0; +} + +int generic_phy_get_by_index(struct udevice *dev, int index, + struct phy *phy) +{ + struct fdtdec_phandle_args args; + struct phy_ops *ops; + int ret; + struct udevice *phydev; + + debug("%s(dev=%p, index=%d, phy=%p)\n", __func__, dev, index, phy); + + assert(phy); + ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev_of_offset(dev), + "phys", "#phy-cells", 0, index, + &args); + if (ret) { + debug("%s: fdtdec_parse_phandle_with_args failed: err=%d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_of_offset(UCLASS_PHY, args.node, &phydev); + if (ret) { + debug("%s: uclass_get_device_by_of_offset failed: err=%d\n", + __func__, ret); + return ret; + } + + phy->dev = phydev; + + ops = phy_dev_ops(phydev); + + if (ops->of_xlate) + ret = ops->of_xlate(phy, &args); + else + ret = generic_phy_xlate_offs_flags(phy, &args); + if (ret) { + debug("of_xlate() failed: %d\n", ret); + goto err; + } + + return 0; + +err: + return ret; +} + +int generic_phy_get_by_name(struct udevice *dev, const char *phy_name, + struct phy *phy) +{ + int index; + + debug("%s(dev=%p, name=%s, phy=%p)\n", __func__, dev, phy_name, phy); + + index = fdt_stringlist_search(gd->fdt_blob, dev_of_offset(dev), + "phy-names", phy_name); + if (index < 0) { + debug("fdt_stringlist_search() failed: %d\n", index); + return index; + } + + return generic_phy_get_by_index(dev, index, phy); +} + +int generic_phy_init(struct phy *phy) +{ + struct phy_ops const *ops = phy_dev_ops(phy->dev); + + return ops->init ? ops->init(phy) : 0; +} + +int generic_phy_reset(struct phy *phy) +{ + struct phy_ops const *ops = phy_dev_ops(phy->dev); + + return ops->reset ? ops->reset(phy) : 0; +} + +int generic_phy_exit(struct phy *phy) +{ + struct phy_ops const *ops = phy_dev_ops(phy->dev); + + return ops->exit ? ops->exit(phy) : 0; +} + +int generic_phy_power_on(struct phy *phy) +{ + struct phy_ops const *ops = phy_dev_ops(phy->dev); + + return ops->power_on ? ops->power_on(phy) : 0; +} + +int generic_phy_power_off(struct phy *phy) +{ + struct phy_ops const *ops = phy_dev_ops(phy->dev); + + return ops->power_off ? ops->power_off(phy) : 0; +} + +UCLASS_DRIVER(phy) = { + .id = UCLASS_PHY, + .name = "phy", +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 8c92d0b..996e94f 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -83,6 +83,7 @@ enum uclass_id { UCLASS_VIDEO, /* Video or LCD device */ UCLASS_VIDEO_BRIDGE, /* Video bridge, e.g. DisplayPort to LVDS */ UCLASS_VIDEO_CONSOLE, /* Text console driver for video device */ + UCLASS_PHY, /* Physical Layer (PHY) device */
UCLASS_COUNT, UCLASS_INVALID = -1, diff --git a/include/generic-phy.h b/include/generic-phy.h new file mode 100644 index 0000000..d8cf0c9 --- /dev/null +++ b/include/generic-phy.h @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Written by Jean-Jacques Hiblot jjhiblot@ti.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __GENERIC_PHY_H +#define __GENERIC_PHY_H + + +/** + * struct phy - A handle to (allowing control of) a single phy port. + * + * Clients provide storage for phy handles. The content of the structure is + * managed solely by the PHY API and PHY drivers. A phy struct is + * initialized by "get"ing the phy struct. The phy struct is passed to all + * other phy APIs to identify which PHY port to operate upon. + * + * @dev: The device which implements the PHY port. + * @id: The PHY ID within the provider. + * + */ +struct phy { + struct udevice *dev; + unsigned long id; +}; + +/* + * struct udevice_ops - set of function pointers for phy operations + * @init: operation to be performed for initializing phy (optional) + * @exit: operation to be performed while exiting (optional) + * @reset: reset the phy (optional). + * @power_on: powering on the phy (optional) + * @power_off: powering off the phy (optional) + */ +struct phy_ops { + /** + * of_xlate - Translate a client's device-tree (OF) phy specifier. + * + * The PHY core calls this function as the first step in implementing + * a client's generic_phy_get_by_*() call. + * + * If this function pointer is set to NULL, the PHY core will use a + * default implementation, which assumes #phy-cells = <0> or + * #phy-cells = <1>, and in the later case that the DT cell + * contains a simple integer PHY port ID. + * + * @phy: The phy struct to hold the translation result. + * @args: The phy specifier values from device tree. + * @return 0 if OK, or a negative error code. + */ + int (*of_xlate)(struct phy *phy, + struct fdtdec_phandle_args *args); + + /** + * init - initialize the hardware. + * + * Hardware intialization should not be done in during probe() but + * should be implemented in this init() function. It could be starting + * PLL, taking a controller out of reset, routing, etc. This function + * is typically called only once per PHY port. + * If power_on() is not implemented, it must power up the phy. + * + * @phy: the PHY port to initialize + * @return 0 if OK, or a negative error code. + */ + int (*init)(struct phy *phy); + + /** + * exit - de-initialize the PHY device + * + * Hardware de-intialization should be done here. Every step done in + * init() should be undone here. + * This could be used to suspend the phy to reduce power consumption or + * to put the phy in a known condition before booting the OS (though it + * is NOT called automatically before booting the OS) + * If power_off() is not implemented, it must power down the phy. + * + * @phy: PHY port to be de-initialized + * @return 0 if OK, or a negative error code + */ + int (*exit)(struct phy *phy); + + /** + * reset - resets a PHY device without shutting down + * + * @phy: PHY port to be reset + * + * During runtime, the PHY may need to be reset in order to + * re-establish connection etc without being shut down or exit. + * + * @return 0 if OK, or a negative error code + */ + int (*reset)(struct phy *phy); + + /** + * power_on - power on a PHY device + * + * @phy: PHY port to be powered on + * + * During runtime, the PHY may need to be powered on or off several + * times. This function is used to power on the PHY. It relies on the + * setup done in init(). If init() is not implemented, it must take care + * of setting up the context (PLLs, ...) + * + * @return 0 if OK, or a negative error code + */ + int (*power_on)(struct phy *phy); + + /** + * power_off - power off a PHY device + * + * @phy: PHY port to be powered off + * + * During runtime, the PHY may need to be powered on or off several + * times. This function is used to power off the PHY. Except if + * init()/deinit() are not implemented, it must not de-initialize + * everything. + * + * @return 0 if OK, or a negative error code + */ + int (*power_off)(struct phy *phy); +}; + + +/** + * generic_phy_init() - initialize the PHY port + * + * @phy: the PHY port to initialize + * @return 0 if OK, or a negative error code + */ +int generic_phy_init(struct phy *phy); + +/** + * generic_phy_init() - de-initialize the PHY device + * + * @phy: PHY port to be de-initialized + * @return 0 if OK, or a negative error code + */ +int generic_phy_exit(struct phy *phy); + +/** + * generic_phy_reset() - resets a PHY device without shutting down + * + * @phy: PHY port to be reset + *@return 0 if OK, or a negative error code + */ +int generic_phy_reset(struct phy *phy); + +/** + * generic_phy_power_on() - power on a PHY device + * + * @phy: PHY port to be powered on + * @return 0 if OK, or a negative error code + */ +int generic_phy_power_on(struct phy *phy); + +/** + * generic_phy_power_off() - power off a PHY device + * + * @phy: PHY port to be powered off + * @return 0 if OK, or a negative error code + */ +int generic_phy_power_off(struct phy *phy); + + +/** + * generic_phy_get_by_index() - Get a PHY device by integer index. + * + * @user: the client device + * @index: The index in the list of available PHYs + * @phy: A pointer to the PHY port + * + * This looks up a PHY device for a client device based on its position in the + * list of the possible PHYs. + * + * example: + * usb1: usb_otg_ss@xxx { + * compatible = "xxx"; + * reg = <xxx>; + * . + * . + * phys = <&usb2_phy>, <&usb3_phy>; + * . + * . + * }; + * the USB2 phy can be accessed by passing index '0' and the USB3 phy can + * be accessed by passing index '1' + * + * @return 0 if OK, or a negative error code + */ +int generic_phy_get_by_index(struct udevice *user, int index, + struct phy *phy); + +/** + * generic_phy_get_by_name() - Get a PHY device by its name. + * + * @user: the client device + * @phy_name: The name of the PHY in the list of possible PHYs + * @phy: A pointer to the PHY port + * + * This looks up a PHY device for a client device in the + * list of the possible PHYs based on its name. + * + * example: + * usb1: usb_otg_ss@xxx { + * compatible = "xxx"; + * reg = <xxx>; + * . + * . + * phys = <&usb2_phy>, <&usb3_phy>; + * phy-names = "usb2phy", "usb3phy"; + * . + * . + * }; + * the USB3 phy can be accessed using "usb3phy", and USB2 by using "usb2phy" + * + * @return 0 if OK, or a negative error code + */ +int generic_phy_get_by_name(struct udevice *user, const char *phy_name, + struct phy *phy); + +#endif /*__GENERIC_PHY_H */

Hi Jean-Jacques,
On 21 April 2017 at 04:08, Jean-Jacques Hiblot jjhiblot@ti.com wrote:
The PHY framework provides a set of APIs to control a PHY. This API is derived from the linux version of the generic PHY framework. Currently the API supports init(), deinit(), power_on, power_off() and reset(). The framework provides a way to get a reference to a phy from the device-tree.
Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com
changes since v3:
- make it possible for a phy device to handle multiple ports. The consequence is the intruction of struct phy which is is used as a parameter to all the functions of the API.
- Add generic_phy_get_by_index() and generic_phy_get_by_name() to get a reference to a phy port. Removed dm_generic_phy_get().
- Improved the documentation
drivers/Kconfig | 2 + drivers/Makefile | 2 + drivers/phy/Kconfig | 36 ++++++++ drivers/phy/Makefile | 2 + drivers/phy/phy-uclass.c | 139 +++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/generic-phy.h | 224 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 406 insertions(+) create mode 100644 drivers/phy/Kconfig create mode 100644 drivers/phy/Makefile create mode 100644 drivers/phy/phy-uclass.c create mode 100644 include/generic-phy.h
Reviewed-by: Simon Glass sjg@chromium.org
With some nits below.
This is a really nice piece of work and a great addition to U-Boot.
diff --git a/drivers/Kconfig b/drivers/Kconfig index 3e6bbac..1fe5baf 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -90,6 +90,8 @@ source "drivers/video/Kconfig"
source "drivers/watchdog/Kconfig"
+source "drivers/phy/Kconfig"
Can you please move this up so we retail the alphabetical order?
config PHYS_TO_BUS bool "Custom physical to bus address mapping" help diff --git a/drivers/Makefile b/drivers/Makefile index 5d8baa5..c2700a0 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -5,9 +5,11 @@ obj-$(CONFIG_$(SPL_TPL_)DM) += core/ obj-$(CONFIG_$(SPL_)CLK) += clk/ obj-$(CONFIG_$(SPL_)LED) += led/ +obj-$(CONFIG_$(SPL_)PHY) += phy/ obj-$(CONFIG_$(SPL_)PINCTRL) += pinctrl/ obj-$(CONFIG_$(SPL_)RAM) += ram/
Drop this extra blank line
ifdef CONFIG_SPL_BUILD
obj-$(CONFIG_SPL_CPU_SUPPORT) += cpu/ diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig new file mode 100644 index 0000000..0a74920 --- /dev/null +++ b/drivers/phy/Kconfig @@ -0,0 +1,36 @@
+menu "PHY Subsystem"
+config PHY
bool "PHY Core"
depends on DM
help
PHY support.
This framework is designed to provide a generic interface for PHY
devices. PHY devices are dedicated hardware that handle the physical
layer of the protocols in the OSI model.
PHYs are commonly used for high speed interfaces such as Serial-ATA
or PCI express.
The API provides functions to initialize/deinitialize the
PHY, power on/off the PHY, and reset the PHY. It's meant to be as
compatible as possible with the equivalent framework found in the
linux kernel.
+config SPL_PHY
bool "PHY Core in SPL"
depends on DM
help
PHY support in SPL.
This framework is designed to provide a generic interface for PHY
devices. PHY devices are dedicated hardware that handle the physical
layer of the protocols (https://en.wikipedia.org/wiki/OSI_model).
PHYs are commonly used for high speed interfaces such as Serial-ATA
or PCI express.
The API provides functions to initialize/deinitialize the
PHY, power on/off the PHY, and reset the PHY. It's meant to be as
compatible as possible with the equivalent framework found in the
linux kernel.
+endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile new file mode 100644 index 0000000..186f07d --- /dev/null +++ b/drivers/phy/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_$(SPL_)PHY) += phy-uclass.o
Extra blank line here. Also can you please add your copyright and SPDX license to the Makefile?
diff --git a/drivers/phy/phy-uclass.c b/drivers/phy/phy-uclass.c new file mode 100644 index 0000000..0d8bef7 --- /dev/null +++ b/drivers/phy/phy-uclass.c @@ -0,0 +1,139 @@ +/*
- Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/
- Written by Jean-Jacques Hiblot jjhiblot@ti.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <dm.h> +#include <generic-phy.h>
+DECLARE_GLOBAL_DATA_PTR;
+static inline struct phy_ops *phy_dev_ops(struct udevice *dev) +{
return (struct phy_ops *)dev->driver->ops;
+}
+static int generic_phy_xlate_offs_flags(struct phy *phy,
struct fdtdec_phandle_args *args)
+{
debug("%s(phy=%p)\n", __func__, phy);
if (args->args_count > 1) {
debug("Invaild args_count: %d\n", args->args_count);
return -EINVAL;
}
if (args->args_count)
phy->id = args->args[0];
else
phy->id = 0;
return 0;
+}
+int generic_phy_get_by_index(struct udevice *dev, int index,
struct phy *phy)
+{
struct fdtdec_phandle_args args;
struct phy_ops *ops;
int ret;
struct udevice *phydev;
debug("%s(dev=%p, index=%d, phy=%p)\n", __func__, dev, index, phy);
assert(phy);
ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev_of_offset(dev),
"phys", "#phy-cells", 0, index,
&args);
if (ret) {
debug("%s: fdtdec_parse_phandle_with_args failed: err=%d\n",
__func__, ret);
return ret;
}
ret = uclass_get_device_by_of_offset(UCLASS_PHY, args.node, &phydev);
if (ret) {
debug("%s: uclass_get_device_by_of_offset failed: err=%d\n",
__func__, ret);
return ret;
}
phy->dev = phydev;
ops = phy_dev_ops(phydev);
if (ops->of_xlate)
ret = ops->of_xlate(phy, &args);
else
ret = generic_phy_xlate_offs_flags(phy, &args);
if (ret) {
debug("of_xlate() failed: %d\n", ret);
goto err;
}
return 0;
+err:
return ret;
+}
+int generic_phy_get_by_name(struct udevice *dev, const char *phy_name,
struct phy *phy)
+{
int index;
debug("%s(dev=%p, name=%s, phy=%p)\n", __func__, dev, phy_name, phy);
index = fdt_stringlist_search(gd->fdt_blob, dev_of_offset(dev),
"phy-names", phy_name);
if (index < 0) {
debug("fdt_stringlist_search() failed: %d\n", index);
return index;
}
return generic_phy_get_by_index(dev, index, phy);
+}
+int generic_phy_init(struct phy *phy) +{
struct phy_ops const *ops = phy_dev_ops(phy->dev);
return ops->init ? ops->init(phy) : 0;
+}
+int generic_phy_reset(struct phy *phy) +{
struct phy_ops const *ops = phy_dev_ops(phy->dev);
return ops->reset ? ops->reset(phy) : 0;
+}
+int generic_phy_exit(struct phy *phy) +{
struct phy_ops const *ops = phy_dev_ops(phy->dev);
return ops->exit ? ops->exit(phy) : 0;
+}
+int generic_phy_power_on(struct phy *phy) +{
struct phy_ops const *ops = phy_dev_ops(phy->dev);
return ops->power_on ? ops->power_on(phy) : 0;
+}
+int generic_phy_power_off(struct phy *phy) +{
struct phy_ops const *ops = phy_dev_ops(phy->dev);
return ops->power_off ? ops->power_off(phy) : 0;
+}
+UCLASS_DRIVER(phy) = {
.id = UCLASS_PHY,
.name = "phy",
+}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 8c92d0b..996e94f 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -83,6 +83,7 @@ enum uclass_id { UCLASS_VIDEO, /* Video or LCD device */ UCLASS_VIDEO_BRIDGE, /* Video bridge, e.g. DisplayPort to LVDS */ UCLASS_VIDEO_CONSOLE, /* Text console driver for video device */
UCLASS_PHY, /* Physical Layer (PHY) device */
Please put this before PINCONFIG
UCLASS_COUNT, UCLASS_INVALID = -1,
diff --git a/include/generic-phy.h b/include/generic-phy.h new file mode 100644 index 0000000..d8cf0c9 --- /dev/null +++ b/include/generic-phy.h @@ -0,0 +1,224 @@ +/*
- Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/
- Written by Jean-Jacques Hiblot jjhiblot@ti.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __GENERIC_PHY_H +#define __GENERIC_PHY_H
+/**
- struct phy - A handle to (allowing control of) a single phy port.
- Clients provide storage for phy handles. The content of the structure is
- managed solely by the PHY API and PHY drivers. A phy struct is
- initialized by "get"ing the phy struct. The phy struct is passed to all
- other phy APIs to identify which PHY port to operate upon.
- @dev: The device which implements the PHY port.
- @id: The PHY ID within the provider.
- */
+struct phy {
struct udevice *dev;
unsigned long id;
+};
+/*
- struct udevice_ops - set of function pointers for phy operations
- @init: operation to be performed for initializing phy (optional)
- @exit: operation to be performed while exiting (optional)
- @reset: reset the phy (optional).
- @power_on: powering on the phy (optional)
- @power_off: powering off the phy (optional)
- */
+struct phy_ops {
/**
* of_xlate - Translate a client's device-tree (OF) phy specifier.
*
* The PHY core calls this function as the first step in implementing
* a client's generic_phy_get_by_*() call.
*
* If this function pointer is set to NULL, the PHY core will use a
* default implementation, which assumes #phy-cells = <0> or
* #phy-cells = <1>, and in the later case that the DT cell
* contains a simple integer PHY port ID.
*
* @phy: The phy struct to hold the translation result.
* @args: The phy specifier values from device tree.
* @return 0 if OK, or a negative error code.
*/
int (*of_xlate)(struct phy *phy,
struct fdtdec_phandle_args *args);
/**
* init - initialize the hardware.
*
* Hardware intialization should not be done in during probe() but
* should be implemented in this init() function. It could be starting
* PLL, taking a controller out of reset, routing, etc. This function
* is typically called only once per PHY port.
* If power_on() is not implemented, it must power up the phy.
*
* @phy: the PHY port to initialize
* @return 0 if OK, or a negative error code.
*/
int (*init)(struct phy *phy);
/**
* exit - de-initialize the PHY device
*
* Hardware de-intialization should be done here. Every step done in
* init() should be undone here.
* This could be used to suspend the phy to reduce power consumption or
* to put the phy in a known condition before booting the OS (though it
* is NOT called automatically before booting the OS)
* If power_off() is not implemented, it must power down the phy.
*
* @phy: PHY port to be de-initialized
* @return 0 if OK, or a negative error code
*/
int (*exit)(struct phy *phy);
/**
* reset - resets a PHY device without shutting down
*
* @phy: PHY port to be reset
*
* During runtime, the PHY may need to be reset in order to
* re-establish connection etc without being shut down or exit.
*
* @return 0 if OK, or a negative error code
*/
int (*reset)(struct phy *phy);
/**
* power_on - power on a PHY device
*
* @phy: PHY port to be powered on
*
* During runtime, the PHY may need to be powered on or off several
* times. This function is used to power on the PHY. It relies on the
* setup done in init(). If init() is not implemented, it must take care
* of setting up the context (PLLs, ...)
*
* @return 0 if OK, or a negative error code
*/
int (*power_on)(struct phy *phy);
/**
* power_off - power off a PHY device
*
* @phy: PHY port to be powered off
*
* During runtime, the PHY may need to be powered on or off several
* times. This function is used to power off the PHY. Except if
* init()/deinit() are not implemented, it must not de-initialize
* everything.
*
* @return 0 if OK, or a negative error code
*/
int (*power_off)(struct phy *phy);
+};
+/**
- generic_phy_init() - initialize the PHY port
- @phy: the PHY port to initialize
- @return 0 if OK, or a negative error code
- */
+int generic_phy_init(struct phy *phy);
+/**
- generic_phy_init() - de-initialize the PHY device
- @phy: PHY port to be de-initialized
- @return 0 if OK, or a negative error code
- */
+int generic_phy_exit(struct phy *phy);
+/**
- generic_phy_reset() - resets a PHY device without shutting down
- @phy: PHY port to be reset
- *@return 0 if OK, or a negative error code
- */
+int generic_phy_reset(struct phy *phy);
+/**
- generic_phy_power_on() - power on a PHY device
- @phy: PHY port to be powered on
- @return 0 if OK, or a negative error code
- */
+int generic_phy_power_on(struct phy *phy);
+/**
- generic_phy_power_off() - power off a PHY device
- @phy: PHY port to be powered off
- @return 0 if OK, or a negative error code
- */
+int generic_phy_power_off(struct phy *phy);
+/**
- generic_phy_get_by_index() - Get a PHY device by integer index.
- @user: the client device
- @index: The index in the list of available PHYs
- @phy: A pointer to the PHY port
- This looks up a PHY device for a client device based on its position in the
- list of the possible PHYs.
- example:
- usb1: usb_otg_ss@xxx {
compatible = "xxx";
reg = <xxx>;
- .
- .
- phys = <&usb2_phy>, <&usb3_phy>;
- .
- .
- };
- the USB2 phy can be accessed by passing index '0' and the USB3 phy can
- be accessed by passing index '1'
- @return 0 if OK, or a negative error code
- */
+int generic_phy_get_by_index(struct udevice *user, int index,
struct phy *phy);
+/**
- generic_phy_get_by_name() - Get a PHY device by its name.
- @user: the client device
- @phy_name: The name of the PHY in the list of possible PHYs
- @phy: A pointer to the PHY port
- This looks up a PHY device for a client device in the
- list of the possible PHYs based on its name.
- example:
- usb1: usb_otg_ss@xxx {
compatible = "xxx";
reg = <xxx>;
- .
- .
- phys = <&usb2_phy>, <&usb3_phy>;
- phy-names = "usb2phy", "usb3phy";
- .
- .
- };
- the USB3 phy can be accessed using "usb3phy", and USB2 by using "usb2phy"
- @return 0 if OK, or a negative error code
- */
+int generic_phy_get_by_name(struct udevice *user, const char *phy_name,
struct phy *phy);
+#endif /*__GENERIC_PHY_H */
1.9.1
Regards, Simon

Those tests check: - the ability for a phy-user to get a phy based on its name or its index - the ability of a phy device (provider) to manage multiple ports - the ability to perform operations on the phy (init,deinit,on,off) - the behavior of the uclass when optional operations are not implemented
Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com ---
changes since v3: updated the tests to reflect the changes in the PHY framework : * use several phy devices (provider) * use several phy ports per phy device
arch/sandbox/dts/test.dts | 17 ++++++ configs/sandbox_defconfig | 2 + configs/sandbox_noblk_defconfig | 2 + configs/sandbox_spl_defconfig | 3 ++ drivers/phy/Kconfig | 9 ++++ drivers/phy/Makefile | 1 + drivers/phy/sandbox-phy.c | 98 +++++++++++++++++++++++++++++++++++ test/dm/Makefile | 2 + test/dm/phy.c | 112 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 246 insertions(+) create mode 100644 drivers/phy/sandbox-phy.c create mode 100644 test/dm/phy.c
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index fff175d..c845f0b 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -59,6 +59,23 @@ ping-add = <3>; };
+ phy_provider0: gen_phy@0 { + compatible = "sandbox,phy"; + #phy-cells = <1>; + }; + + phy_provider1: gen_phy@1 { + compatible = "sandbox,phy"; + #phy-cells = <0>; + broken; + }; + + gen_phy_user: gen_phy_user { + compatible = "simple-bus"; + phys = <&phy_provider0 0>, <&phy_provider0 1>, <&phy_provider1>; + phy-names = "phy1", "phy2", "phy3"; + }; + some-bus { #address-cells = <1>; #size-cells = <0>; diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 9814ea3..a7bc08e 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -165,6 +165,8 @@ CONFIG_CONSOLE_ROTATION=y CONFIG_CONSOLE_TRUETYPE=y CONFIG_CONSOLE_TRUETYPE_CANTORAONE=y CONFIG_VIDEO_SANDBOX_SDL=y +CONFIG_GENERIC_PHY=y +CONFIG_PHY_SANDBOX=y CONFIG_CMD_DHRYSTONE=y CONFIG_TPM=y CONFIG_LZ4=y diff --git a/configs/sandbox_noblk_defconfig b/configs/sandbox_noblk_defconfig index bba7443..59a80ef 100644 --- a/configs/sandbox_noblk_defconfig +++ b/configs/sandbox_noblk_defconfig @@ -167,6 +167,8 @@ CONFIG_CONSOLE_ROTATION=y CONFIG_CONSOLE_TRUETYPE=y CONFIG_CONSOLE_TRUETYPE_CANTORAONE=y CONFIG_VIDEO_SANDBOX_SDL=y +CONFIG_GENERIC_PHY=y +CONFIG_PHY_SANDBOX=y CONFIG_CMD_DHRYSTONE=y CONFIG_TPM=y CONFIG_LZ4=y diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index 6fe2125..b1a2209 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -171,6 +171,9 @@ CONFIG_CONSOLE_ROTATION=y CONFIG_CONSOLE_TRUETYPE=y CONFIG_CONSOLE_TRUETYPE_CANTORAONE=y CONFIG_VIDEO_SANDBOX_SDL=y +CONFIG_GENERIC_PHY=y +CONFIG_SPL_GENERIC_PHY=y +CONFIG_PHY_SANDBOX=y CONFIG_CMD_DHRYSTONE=y CONFIG_TPM=y CONFIG_LZ4=y diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 0a74920..1131d46 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -33,4 +33,13 @@ config SPL_PHY compatible as possible with the equivalent framework found in the linux kernel.
+config PHY_SANDBOX + bool "Sandbox PHY support" + depends on SANDBOX + depends on PHY + help + This select a dummy sandbox PHY driver. It used only to implement + the unit tests for the phy framework + + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 186f07d..ca67145 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_$(SPL_)PHY) += phy-uclass.o +obj-$(CONFIG_PHY_SANDBOX) += sandbox-phy.o
diff --git a/drivers/phy/sandbox-phy.c b/drivers/phy/sandbox-phy.c new file mode 100644 index 0000000..892a4a2 --- /dev/null +++ b/drivers/phy/sandbox-phy.c @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Written by Jean-Jacques Hiblot jjhiblot@ti.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <generic-phy.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct sandbox_phy_priv { + bool initialized; + bool on; + bool broken; +}; + +static int sandbox_phy_power_on(struct phy *phy) +{ + struct sandbox_phy_priv *priv = dev_get_priv(phy->dev); + if (!priv->initialized) + return -EIO; + + if (priv->broken) + return -EIO; + + priv->on = true; + return 0; +} + +static int sandbox_phy_power_off(struct phy *phy) +{ + struct sandbox_phy_priv *priv = dev_get_priv(phy->dev); + if (!priv->initialized) + return -EIO; + + if (priv->broken) + return -EIO; + + /* + * for validation purpose, let's says that power off + * works only for PHY 0 + */ + if (phy->id) + return -EIO; + + priv->on = false; + return 0; +} + +static int sandbox_phy_init(struct phy *phy) +{ + struct sandbox_phy_priv *priv = dev_get_priv(phy->dev); + priv->initialized = true; + priv->on = true; + return 0; +} + +static int sandbox_phy_exit(struct phy *phy) +{ + struct sandbox_phy_priv *priv = dev_get_priv(phy->dev); + priv->initialized = false; + priv->on = false; + return 0; +} + +static int sandbox_phy_probe(struct udevice *dev) +{ + struct sandbox_phy_priv *priv = dev_get_priv(dev); + priv->initialized = false; + priv->on = false; + priv->broken = fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev), + "broken"); + return 0; +} + +static struct phy_ops sandbox_phy_ops = { + .power_on = sandbox_phy_power_on, + .power_off = sandbox_phy_power_off, + .init = sandbox_phy_init, + .exit = sandbox_phy_exit, +}; + +static const struct udevice_id sandbox_phy_ids[] = { + { .compatible = "sandbox,phy" }, + { } +}; + +U_BOOT_DRIVER(phy_sandbox) = { + .name = "phy_sandbox", + .id = UCLASS_PHY, + .of_match = sandbox_phy_ids, + .ops = &sandbox_phy_ops, + .probe = sandbox_phy_probe, + .priv_auto_alloc_size = sizeof(struct sandbox_phy_priv), +}; diff --git a/test/dm/Makefile b/test/dm/Makefile index 1885e17..dcd69b3 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_LED) += led.o obj-$(CONFIG_DM_MAILBOX) += mailbox.o obj-$(CONFIG_DM_MMC) += mmc.o obj-$(CONFIG_DM_PCI) += pci.o +obj-$(CONFIG_PHY) += phy.o obj-$(CONFIG_POWER_DOMAIN) += power-domain.o obj-$(CONFIG_RAM) += ram.o obj-y += regmap.o @@ -41,4 +42,5 @@ obj-$(CONFIG_TIMER) += timer.o obj-$(CONFIG_DM_VIDEO) += video.o obj-$(CONFIG_ADC) += adc.o obj-$(CONFIG_SPMI) += spmi.o + endif diff --git a/test/dm/phy.c b/test/dm/phy.c new file mode 100644 index 0000000..811045f --- /dev/null +++ b/test/dm/phy.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Written by Jean-Jacques Hiblot jjhiblot@ti.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <generic-phy.h> +#include <dm/test.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Base test of the phy uclass */ +static int dm_test_phy_base(struct unit_test_state *uts) +{ + struct udevice *dev; + struct phy phy1_method1; + struct phy phy1_method2; + struct phy phy2; + struct phy phy3; + struct udevice *parent; + + /* Get the device using the phy device*/ + ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS, + "gen_phy_user", &parent)); + /* + * Get the same phy port in 2 different ways and compare. + */ + ut_assertok(generic_phy_get_by_name(parent, "phy1", &phy1_method1)) + ut_assertok(generic_phy_get_by_index(parent, 0, &phy1_method2)) + ut_asserteq(phy1_method1.id, phy1_method2.id); + + /* + * Get the second phy port. Check that the same phy provider (device) + * provides this 2nd phy port, but that the IDs are different + */ + ut_assertok(generic_phy_get_by_name(parent, "phy2", &phy2)) + ut_asserteq_ptr(phy1_method2.dev, phy2.dev); + ut_assert(phy1_method1.id != phy2.id); + + /* + * Get the third phy port. Check that the phy provider is different + */ + ut_assertok(generic_phy_get_by_name(parent, "phy3", &phy3)) + ut_assert(phy2.dev != phy3.dev); + + /* Try to get a non-existing phy */ + ut_asserteq(-ENODEV, uclass_get_device(UCLASS_PHY, 3, &dev)); + ut_assert(generic_phy_get_by_name(parent, "phy_not_existing", + &phy1_method1) < 0) + + return 0; +} +DM_TEST(dm_test_phy_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test of the phy uclass using the sandbox phy driver operations */ +static int dm_test_phy_ops(struct unit_test_state *uts) +{ + struct phy phy1; + struct phy phy2; + struct phy phy3; + struct udevice *parent; + + ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS, + "gen_phy_user", &parent)); + + ut_assertok(generic_phy_get_by_name(parent, "phy1", &phy1)); + ut_assertok(generic_phy_get_by_name(parent, "phy2", &phy2)); + ut_assertok(generic_phy_get_by_name(parent, "phy3", &phy3)); + + /* test normal operations */ + ut_assertok(generic_phy_init(&phy1)); + ut_assertok(generic_phy_power_on(&phy1)); + ut_assertok(generic_phy_power_off(&phy1)); + + /* + * test operations after exit(). + * The sandbox phy driver does not allow it. + */ + ut_assertok(generic_phy_exit(&phy1)); + ut_assert(generic_phy_power_on(&phy1) != 0); + ut_assert(generic_phy_power_off(&phy1) != 0); + + /* + * test normal operations again (after re-init) + */ + ut_assertok(generic_phy_init(&phy1)); + ut_assertok(generic_phy_power_on(&phy1)); + ut_assertok(generic_phy_power_off(&phy1)); + + /* + * test calling unimplemented feature. + * The call is expected to succeed + */ + ut_assertok(generic_phy_reset(&phy1)); + + /* PHY2 has a known problem with power off */ + ut_assertok(generic_phy_init(&phy2)); + ut_assertok(generic_phy_power_on(&phy2)); + ut_assert(generic_phy_power_off(&phy2) == -EIO); + + /* PHY3 has a known problem with power off and power on*/ + ut_assertok(generic_phy_init(&phy3)); + ut_assert(generic_phy_power_off(&phy3) == -EIO); + ut_assert(generic_phy_power_off(&phy3) == -EIO); + + return 0; +} +DM_TEST(dm_test_phy_ops, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);

Hi Jean-Jacques,
On 21 April 2017 at 04:08, Jean-Jacques Hiblot jjhiblot@ti.com wrote:
Those tests check:
- the ability for a phy-user to get a phy based on its name or its index
- the ability of a phy device (provider) to manage multiple ports
- the ability to perform operations on the phy (init,deinit,on,off)
- the behavior of the uclass when optional operations are not implemented
Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com
changes since v3: updated the tests to reflect the changes in the PHY framework :
- use several phy devices (provider)
- use several phy ports per phy device
arch/sandbox/dts/test.dts | 17 ++++++ configs/sandbox_defconfig | 2 + configs/sandbox_noblk_defconfig | 2 + configs/sandbox_spl_defconfig | 3 ++ drivers/phy/Kconfig | 9 ++++ drivers/phy/Makefile | 1 + drivers/phy/sandbox-phy.c | 98 +++++++++++++++++++++++++++++++++++ test/dm/Makefile | 2 + test/dm/phy.c | 112 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 246 insertions(+) create mode 100644 drivers/phy/sandbox-phy.c create mode 100644 test/dm/phy.c
Reviewed-by: Simon Glass sjg@chromium.org
Nits below.
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index fff175d..c845f0b 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -59,6 +59,23 @@ ping-add = <3>; };
phy_provider0: gen_phy@0 {
compatible = "sandbox,phy";
#phy-cells = <1>;
};
phy_provider1: gen_phy@1 {
compatible = "sandbox,phy";
#phy-cells = <0>;
broken;
};
gen_phy_user: gen_phy_user {
compatible = "simple-bus";
phys = <&phy_provider0 0>, <&phy_provider0 1>, <&phy_provider1>;
phy-names = "phy1", "phy2", "phy3";
};
some-bus { #address-cells = <1>; #size-cells = <0>;
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 9814ea3..a7bc08e 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -165,6 +165,8 @@ CONFIG_CONSOLE_ROTATION=y CONFIG_CONSOLE_TRUETYPE=y CONFIG_CONSOLE_TRUETYPE_CANTORAONE=y CONFIG_VIDEO_SANDBOX_SDL=y +CONFIG_GENERIC_PHY=y +CONFIG_PHY_SANDBOX=y CONFIG_CMD_DHRYSTONE=y CONFIG_TPM=y CONFIG_LZ4=y diff --git a/configs/sandbox_noblk_defconfig b/configs/sandbox_noblk_defconfig index bba7443..59a80ef 100644 --- a/configs/sandbox_noblk_defconfig +++ b/configs/sandbox_noblk_defconfig @@ -167,6 +167,8 @@ CONFIG_CONSOLE_ROTATION=y CONFIG_CONSOLE_TRUETYPE=y CONFIG_CONSOLE_TRUETYPE_CANTORAONE=y CONFIG_VIDEO_SANDBOX_SDL=y +CONFIG_GENERIC_PHY=y +CONFIG_PHY_SANDBOX=y CONFIG_CMD_DHRYSTONE=y CONFIG_TPM=y CONFIG_LZ4=y diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index 6fe2125..b1a2209 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -171,6 +171,9 @@ CONFIG_CONSOLE_ROTATION=y CONFIG_CONSOLE_TRUETYPE=y CONFIG_CONSOLE_TRUETYPE_CANTORAONE=y CONFIG_VIDEO_SANDBOX_SDL=y +CONFIG_GENERIC_PHY=y +CONFIG_SPL_GENERIC_PHY=y +CONFIG_PHY_SANDBOX=y CONFIG_CMD_DHRYSTONE=y CONFIG_TPM=y CONFIG_LZ4=y diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 0a74920..1131d46 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -33,4 +33,13 @@ config SPL_PHY compatible as possible with the equivalent framework found in the linux kernel.
+config PHY_SANDBOX
bool "Sandbox PHY support"
depends on SANDBOX
depends on PHY
help
This select a dummy sandbox PHY driver. It used only to implement
the unit tests for the phy framework
Drop extra blank line
endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 186f07d..ca67145 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_$(SPL_)PHY) += phy-uclass.o +obj-$(CONFIG_PHY_SANDBOX) += sandbox-phy.o
diff --git a/drivers/phy/sandbox-phy.c b/drivers/phy/sandbox-phy.c new file mode 100644 index 0000000..892a4a2 --- /dev/null +++ b/drivers/phy/sandbox-phy.c @@ -0,0 +1,98 @@ +/*
- Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/
- Written by Jean-Jacques Hiblot jjhiblot@ti.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <dm.h> +#include <generic-phy.h>
+DECLARE_GLOBAL_DATA_PTR;
+struct sandbox_phy_priv {
bool initialized;
bool on;
bool broken;
+};
+static int sandbox_phy_power_on(struct phy *phy) +{
struct sandbox_phy_priv *priv = dev_get_priv(phy->dev);
blank line between decls and code. Please fix below also.
if (!priv->initialized)
return -EIO;
if (priv->broken)
return -EIO;
priv->on = true;
Blank line before return, Please fix below also.
return 0;
+}
+static int sandbox_phy_power_off(struct phy *phy) +{
struct sandbox_phy_priv *priv = dev_get_priv(phy->dev);
if (!priv->initialized)
return -EIO;
if (priv->broken)
return -EIO;
/*
* for validation purpose, let's says that power off
* works only for PHY 0
*/
if (phy->id)
return -EIO;
priv->on = false;
return 0;
+}
+static int sandbox_phy_init(struct phy *phy) +{
struct sandbox_phy_priv *priv = dev_get_priv(phy->dev);
priv->initialized = true;
priv->on = true;
return 0;
+}
+static int sandbox_phy_exit(struct phy *phy) +{
struct sandbox_phy_priv *priv = dev_get_priv(phy->dev);
priv->initialized = false;
priv->on = false;
return 0;
+}
+static int sandbox_phy_probe(struct udevice *dev) +{
struct sandbox_phy_priv *priv = dev_get_priv(dev);
priv->initialized = false;
priv->on = false;
priv->broken = fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
"broken");
return 0;
+}
+static struct phy_ops sandbox_phy_ops = {
.power_on = sandbox_phy_power_on,
.power_off = sandbox_phy_power_off,
.init = sandbox_phy_init,
.exit = sandbox_phy_exit,
+};
+static const struct udevice_id sandbox_phy_ids[] = {
{ .compatible = "sandbox,phy" },
{ }
+};
+U_BOOT_DRIVER(phy_sandbox) = {
.name = "phy_sandbox",
.id = UCLASS_PHY,
.of_match = sandbox_phy_ids,
.ops = &sandbox_phy_ops,
.probe = sandbox_phy_probe,
.priv_auto_alloc_size = sizeof(struct sandbox_phy_priv),
+}; diff --git a/test/dm/Makefile b/test/dm/Makefile index 1885e17..dcd69b3 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_LED) += led.o obj-$(CONFIG_DM_MAILBOX) += mailbox.o obj-$(CONFIG_DM_MMC) += mmc.o obj-$(CONFIG_DM_PCI) += pci.o +obj-$(CONFIG_PHY) += phy.o obj-$(CONFIG_POWER_DOMAIN) += power-domain.o obj-$(CONFIG_RAM) += ram.o obj-y += regmap.o @@ -41,4 +42,5 @@ obj-$(CONFIG_TIMER) += timer.o obj-$(CONFIG_DM_VIDEO) += video.o obj-$(CONFIG_ADC) += adc.o obj-$(CONFIG_SPMI) += spmi.o
Drop extra blank ine
endif diff --git a/test/dm/phy.c b/test/dm/phy.c new file mode 100644 index 0000000..811045f --- /dev/null +++ b/test/dm/phy.c @@ -0,0 +1,112 @@ +/*
- Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/
- Written by Jean-Jacques Hiblot jjhiblot@ti.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <dm.h> +#include <generic-phy.h> +#include <dm/test.h> +#include <test/ut.h>
+DECLARE_GLOBAL_DATA_PTR;
+/* Base test of the phy uclass */ +static int dm_test_phy_base(struct unit_test_state *uts) +{
struct udevice *dev;
struct phy phy1_method1;
struct phy phy1_method2;
struct phy phy2;
struct phy phy3;
struct udevice *parent;
/* Get the device using the phy device*/
ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS,
"gen_phy_user", &parent));
/*
* Get the same phy port in 2 different ways and compare.
*/
ut_assertok(generic_phy_get_by_name(parent, "phy1", &phy1_method1))
ut_assertok(generic_phy_get_by_index(parent, 0, &phy1_method2))
ut_asserteq(phy1_method1.id, phy1_method2.id);
/*
* Get the second phy port. Check that the same phy provider (device)
* provides this 2nd phy port, but that the IDs are different
*/
ut_assertok(generic_phy_get_by_name(parent, "phy2", &phy2))
ut_asserteq_ptr(phy1_method2.dev, phy2.dev);
ut_assert(phy1_method1.id != phy2.id);
/*
* Get the third phy port. Check that the phy provider is different
*/
ut_assertok(generic_phy_get_by_name(parent, "phy3", &phy3))
ut_assert(phy2.dev != phy3.dev);
/* Try to get a non-existing phy */
ut_asserteq(-ENODEV, uclass_get_device(UCLASS_PHY, 3, &dev));
ut_assert(generic_phy_get_by_name(parent, "phy_not_existing",
&phy1_method1) < 0)
return 0;
+} +DM_TEST(dm_test_phy_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+/* Test of the phy uclass using the sandbox phy driver operations */ +static int dm_test_phy_ops(struct unit_test_state *uts) +{
struct phy phy1;
struct phy phy2;
struct phy phy3;
struct udevice *parent;
ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS,
"gen_phy_user", &parent));
ut_assertok(generic_phy_get_by_name(parent, "phy1", &phy1));
ut_assertok(generic_phy_get_by_name(parent, "phy2", &phy2));
ut_assertok(generic_phy_get_by_name(parent, "phy3", &phy3));
/* test normal operations */
ut_assertok(generic_phy_init(&phy1));
ut_assertok(generic_phy_power_on(&phy1));
ut_assertok(generic_phy_power_off(&phy1));
/*
* test operations after exit().
* The sandbox phy driver does not allow it.
*/
ut_assertok(generic_phy_exit(&phy1));
ut_assert(generic_phy_power_on(&phy1) != 0);
ut_assert(generic_phy_power_off(&phy1) != 0);
/*
* test normal operations again (after re-init)
*/
ut_assertok(generic_phy_init(&phy1));
ut_assertok(generic_phy_power_on(&phy1));
ut_assertok(generic_phy_power_off(&phy1));
/*
* test calling unimplemented feature.
* The call is expected to succeed
*/
ut_assertok(generic_phy_reset(&phy1));
/* PHY2 has a known problem with power off */
ut_assertok(generic_phy_init(&phy2));
ut_assertok(generic_phy_power_on(&phy2));
ut_assert(generic_phy_power_off(&phy2) == -EIO);
/* PHY3 has a known problem with power off and power on*/
ut_assertok(generic_phy_init(&phy3));
ut_assert(generic_phy_power_off(&phy3) == -EIO);
ut_assert(generic_phy_power_off(&phy3) == -EIO);
return 0;
+}
+DM_TEST(dm_test_phy_ops, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
1.9.1
This looks like a good set of tests.
Regards, Simon

This phy is found on omap platforms with sata capabilities. Except for the part related to the DM and the PHY framework, the code is basically a copy paste from arch/arm/mach-omap2/pipe3-phy.c
Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com Reviewed-by: Tom Rini trini@konsulko.com Reviewed-by: Simon Glass sjg@chromium.org ---
changes since v3: * removed the reset function * Use init/exit to turn off/on the DPLL
drivers/phy/Kconfig | 18 +++ drivers/phy/Makefile | 1 + drivers/phy/ti-pipe3-phy.c | 373 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 392 insertions(+) create mode 100644 drivers/phy/ti-pipe3-phy.c
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 1131d46..eb85272 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -42,4 +42,22 @@ config PHY_SANDBOX the unit tests for the phy framework
+config PIPE3_PHY + bool "Support omap's PIPE3 PHY" + depends on PHY && ARCH_OMAP2 + help + Support for the omap PIPE3 phy for sata + + This PHY is found on omap devices supporting SATA such as dra7, am57x + and omap5 + +config SPL_PIPE3_PHY + bool "Support omap's PIPE3 PHY in SPL" + depends on SPL_PHY && ARCH_OMAP2 + help + Support for the omap PIPE3 phy for sata in SPL + + This PHY is found on omap devices supporting SATA such as dra7, am57x + and omap5 + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index ca67145..02dbd3d 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_$(SPL_)PHY) += phy-uclass.o obj-$(CONFIG_PHY_SANDBOX) += sandbox-phy.o +obj-$(CONFIG_$(SPL_)PIPE3_PHY) += ti-pipe3-phy.o
diff --git a/drivers/phy/ti-pipe3-phy.c b/drivers/phy/ti-pipe3-phy.c new file mode 100644 index 0000000..ed80f0f --- /dev/null +++ b/drivers/phy/ti-pipe3-phy.c @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Written by Jean-Jacques Hiblot jjhiblot@ti.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <dm/device.h> +#include <generic-phy.h> +#include <asm/io.h> +#include <asm/arch/sys_proto.h> +#include <syscon.h> +#include <regmap.h> + +/* PLLCTRL Registers */ +#define PLL_STATUS 0x00000004 +#define PLL_GO 0x00000008 +#define PLL_CONFIGURATION1 0x0000000C +#define PLL_CONFIGURATION2 0x00000010 +#define PLL_CONFIGURATION3 0x00000014 +#define PLL_CONFIGURATION4 0x00000020 + +#define PLL_REGM_MASK 0x001FFE00 +#define PLL_REGM_SHIFT 9 +#define PLL_REGM_F_MASK 0x0003FFFF +#define PLL_REGM_F_SHIFT 0 +#define PLL_REGN_MASK 0x000001FE +#define PLL_REGN_SHIFT 1 +#define PLL_SELFREQDCO_MASK 0x0000000E +#define PLL_SELFREQDCO_SHIFT 1 +#define PLL_SD_MASK 0x0003FC00 +#define PLL_SD_SHIFT 10 +#define SET_PLL_GO 0x1 +#define PLL_TICOPWDN BIT(16) +#define PLL_LDOPWDN BIT(15) +#define PLL_LOCK 0x2 +#define PLL_IDLE 0x1 + +/* Software rest for the SATA PLL (in CTRL_CORE_SMA_SW_0 register)*/ +#define SATA_PLL_SOFT_RESET (1<<18) + +/* PHY POWER CONTROL Register */ +#define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK 0x003FC000 +#define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT 0xE + +#define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK 0xFFC00000 +#define OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT 0x16 + +#define OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON 0x3 +#define OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF 0x0 + + +#define PLL_IDLE_TIME 100 /* in milliseconds */ +#define PLL_LOCK_TIME 100 /* in milliseconds */ + +struct omap_pipe3 { + void __iomem *pll_ctrl_base; + void __iomem *power_reg; + void __iomem *pll_reset_reg; + struct pipe3_dpll_map *dpll_map; +}; + + +struct pipe3_dpll_params { + u16 m; + u8 n; + u8 freq:3; + u8 sd; + u32 mf; +}; + +struct pipe3_dpll_map { + unsigned long rate; + struct pipe3_dpll_params params; +}; + +static inline u32 omap_pipe3_readl(void __iomem *addr, unsigned offset) +{ + return readl(addr + offset); +} + +static inline void omap_pipe3_writel(void __iomem *addr, unsigned offset, + u32 data) +{ + writel(data, addr + offset); +} + +static struct pipe3_dpll_params *omap_pipe3_get_dpll_params(struct omap_pipe3 + *pipe3) +{ + u32 rate; + struct pipe3_dpll_map *dpll_map = pipe3->dpll_map; + + rate = get_sys_clk_freq(); + + for (; dpll_map->rate; dpll_map++) { + if (rate == dpll_map->rate) + return &dpll_map->params; + } + + printf("%s: No DPLL configuration for %u Hz SYS CLK\n", + __func__, rate); + return NULL; +} + +static int omap_pipe3_wait_lock(struct omap_pipe3 *pipe3) +{ + u32 val; + int timeout = PLL_LOCK_TIME; + + do { + mdelay(1); + val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_STATUS); + if (val & PLL_LOCK) + break; + } while (--timeout); + + if (!(val & PLL_LOCK)) { + printf("%s: DPLL failed to lock\n", __func__); + return -EBUSY; + } + + return 0; +} + +static int omap_pipe3_dpll_program(struct omap_pipe3 *pipe3) +{ + u32 val; + struct pipe3_dpll_params *dpll_params; + + dpll_params = omap_pipe3_get_dpll_params(pipe3); + if (!dpll_params) { + printf("%s: Invalid DPLL parameters\n", __func__); + return -EINVAL; + } + + val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION1); + val &= ~PLL_REGN_MASK; + val |= dpll_params->n << PLL_REGN_SHIFT; + omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION1, val); + + val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION2); + val &= ~PLL_SELFREQDCO_MASK; + val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT; + omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION2, val); + + val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION1); + val &= ~PLL_REGM_MASK; + val |= dpll_params->m << PLL_REGM_SHIFT; + omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION1, val); + + val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION4); + val &= ~PLL_REGM_F_MASK; + val |= dpll_params->mf << PLL_REGM_F_SHIFT; + omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION4, val); + + val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION3); + val &= ~PLL_SD_MASK; + val |= dpll_params->sd << PLL_SD_SHIFT; + omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION3, val); + + omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_GO, SET_PLL_GO); + + return omap_pipe3_wait_lock(pipe3); +} + +static void omap_control_pipe3_power(struct omap_pipe3 *pipe3, int on) +{ + u32 val, rate; + + val = readl(pipe3->power_reg); + + rate = get_sys_clk_freq(); + rate = rate/1000000; + + if (on) { + val &= ~(OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK | + OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK); + val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON << + OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; + val |= rate << + OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT; + } else { + val &= ~OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK; + val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF << + OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; + } + + writel(val, pipe3->power_reg); +} + +static int pipe3_init(struct phy *phy) +{ + int ret; + u32 val; + struct omap_pipe3 *pipe3 = dev_get_priv(phy->dev); + + /* Program the DPLL only if not locked */ + val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_STATUS); + if (!(val & PLL_LOCK)) { + ret = omap_pipe3_dpll_program(pipe3); + if (ret) + return ret; + } else { + /* else just bring it out of IDLE mode */ + val = omap_pipe3_readl(pipe3->pll_ctrl_base, + PLL_CONFIGURATION2); + if (val & PLL_IDLE) { + val &= ~PLL_IDLE; + omap_pipe3_writel(pipe3->pll_ctrl_base, + PLL_CONFIGURATION2, val); + ret = omap_pipe3_wait_lock(pipe3); + if (ret) + return ret; + } + } + return 0; +} + +static int pipe3_power_on(struct phy *phy) +{ + struct omap_pipe3 *pipe3 = dev_get_priv(phy->dev); + + /* Power up the PHY */ + omap_control_pipe3_power(pipe3, 1); + + return 0; +} + +static int pipe3_power_off(struct phy *phy) +{ + struct omap_pipe3 *pipe3 = dev_get_priv(phy->dev); + + /* Power down the PHY */ + omap_control_pipe3_power(pipe3, 0); + + return 0; +} + +static int pipe3_exit(struct phy *phy) +{ + u32 val; + int timeout = PLL_IDLE_TIME; + struct omap_pipe3 *pipe3 = dev_get_priv(phy->dev); + + pipe3_power_off(phy); + + /* Put DPLL in IDLE mode */ + val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_CONFIGURATION2); + val |= PLL_IDLE; + omap_pipe3_writel(pipe3->pll_ctrl_base, PLL_CONFIGURATION2, val); + + /* wait for LDO and Oscillator to power down */ + do { + mdelay(1); + val = omap_pipe3_readl(pipe3->pll_ctrl_base, PLL_STATUS); + if ((val & PLL_TICOPWDN) && (val & PLL_LDOPWDN)) + break; + } while (--timeout); + + if (!(val & PLL_TICOPWDN) || !(val & PLL_LDOPWDN)) { + error("%s: Failed to power down DPLL: PLL_STATUS 0x%x\n", + __func__, val); + return -EBUSY; + } + + val = readl(pipe3->pll_reset_reg); + writel(val | SATA_PLL_SOFT_RESET, pipe3->pll_reset_reg); + mdelay(1); + writel(val & ~SATA_PLL_SOFT_RESET, pipe3->pll_reset_reg); + return 0; +} + +static void *get_reg(struct udevice *dev, const char *name) +{ + struct udevice *syscon; + struct regmap *regmap; + const fdt32_t *cell; + int len, err; + void *base; + + err = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, + name, &syscon); + if (err) { + error("unable to find syscon device for %s (%d)\n", + name, err); + return NULL; + } + + regmap = syscon_get_regmap(syscon); + if (IS_ERR(regmap)) { + error("unable to find regmap for %s (%ld)\n", + name, PTR_ERR(regmap)); + return NULL; + } + + cell = fdt_getprop(gd->fdt_blob, dev->of_offset, name, + &len); + if (len < 2*sizeof(fdt32_t)) { + error("offset not available for %s\n", name); + return NULL; + } + + base = regmap_get_range(regmap, 0); + if (!base) + return NULL; + + return fdtdec_get_number(cell + 1, 1) + base; +} + +static int pipe3_phy_probe(struct udevice *dev) +{ + fdt_addr_t addr; + fdt_size_t sz; + struct omap_pipe3 *pipe3 = dev_get_priv(dev); + + addr = dev_get_addr_size_index(dev, 2, &sz); + if (addr == FDT_ADDR_T_NONE) { + error("missing pll ctrl address\n"); + return -EINVAL; + } + + pipe3->pll_ctrl_base = map_physmem(addr, sz, MAP_NOCACHE); + if (!pipe3->pll_ctrl_base) { + error("unable to remap pll ctrl\n"); + return -EINVAL; + } + + pipe3->power_reg = get_reg(dev, "syscon-phy-power"); + if (!pipe3->power_reg) + return -EINVAL; + + pipe3->pll_reset_reg = get_reg(dev, "syscon-pllreset"); + if (!pipe3->pll_reset_reg) + return -EINVAL; + + pipe3->dpll_map = (struct pipe3_dpll_map *)dev_get_driver_data(dev); + + return 0; +} + +static struct pipe3_dpll_map dpll_map_sata[] = { + {12000000, {1000, 7, 4, 6, 0} }, /* 12 MHz */ + {16800000, {714, 7, 4, 6, 0} }, /* 16.8 MHz */ + {19200000, {625, 7, 4, 6, 0} }, /* 19.2 MHz */ + {20000000, {600, 7, 4, 6, 0} }, /* 20 MHz */ + {26000000, {461, 7, 4, 6, 0} }, /* 26 MHz */ + {38400000, {312, 7, 4, 6, 0} }, /* 38.4 MHz */ + { }, /* Terminator */ +}; + +static const struct udevice_id pipe3_phy_ids[] = { + { .compatible = "ti,phy-pipe3-sata", .data = (ulong)&dpll_map_sata }, + { } +}; + +static struct phy_ops pipe3_phy_ops = { + .init = pipe3_init, + .power_on = pipe3_power_on, + .power_off = pipe3_power_off, + .exit = pipe3_exit, +}; + +U_BOOT_DRIVER(pipe3_phy) = { + .name = "pipe3_phy", + .id = UCLASS_PHY, + .of_match = pipe3_phy_ids, + .ops = &pipe3_phy_ops, + .probe = pipe3_phy_probe, + .priv_auto_alloc_size = sizeof(struct omap_pipe3), +};

This is needed to probe devices under that bus such as the SATA PHY.
Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com Reviewed-by: Tom Rini trini@konsulko.com --- arch/arm/dts/omap5-u-boot.dtsi | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/arch/arm/dts/omap5-u-boot.dtsi b/arch/arm/dts/omap5-u-boot.dtsi index 6305f57..9247314 100644 --- a/arch/arm/dts/omap5-u-boot.dtsi +++ b/arch/arm/dts/omap5-u-boot.dtsi @@ -10,6 +10,10 @@ /{ ocp { u-boot,dm-pre-reloc; + + ocp2scp@4a090000 { + compatible = "ti,omap-ocp2scp", "simple-bus"; + }; }; };

On 21 April 2017 at 04:08, Jean-Jacques Hiblot jjhiblot@ti.com wrote:
This is needed to probe devices under that bus such as the SATA PHY.
Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com Reviewed-by: Tom Rini trini@konsulko.com
arch/arm/dts/omap5-u-boot.dtsi | 4 ++++ 1 file changed, 4 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

Implement a sata driver for Synopsys DWC sata device based on U-boot driver model.
Signed-off-by: Mugunthan V N mugunthanvnm@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com Reviewed-by: Tom Rini trini@konsulko.com Reviewed-by: Simon Glass sjg@chromium.org --- drivers/block/Kconfig | 10 +++++ drivers/block/Makefile | 1 + drivers/block/dwc_ahci.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+) create mode 100644 drivers/block/dwc_ahci.c
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 88e66e2..6cbe145 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -48,4 +48,14 @@ config SATA_CEVA ZynqMP. Support up to 2 external devices. Complient with SATA 3.1 and AHCI 1.3 specifications with hot-plug detect feature.
+ +config DWC_AHCI + bool "Enable Synopsys DWC AHCI driver support" + select SCSI_AHCI + select PHY + depends on DM_SCSI + help + Enable this driver to support Sata devices through + Synopsys DWC AHCI module. + endmenu diff --git a/drivers/block/Makefile b/drivers/block/Makefile index a72feec..cffe498 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -11,6 +11,7 @@ ifndef CONFIG_BLK obj-y += blk_legacy.o endif
+obj-$(CONFIG_DWC_AHCI) += dwc_ahci.o obj-$(CONFIG_AHCI) += ahci-uclass.o obj-$(CONFIG_DM_SCSI) += scsi-uclass.o obj-$(CONFIG_SCSI_AHCI) += ahci.o diff --git a/drivers/block/dwc_ahci.c b/drivers/block/dwc_ahci.c new file mode 100644 index 0000000..d5bb0b8 --- /dev/null +++ b/drivers/block/dwc_ahci.c @@ -0,0 +1,101 @@ +/* + * DWC SATA platform driver + * + * (C) Copyright 2016 + * Texas Instruments Incorporated, <www.ti.com> + * + * Author: Mugunthan V N mugunthanvnm@ti.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <ahci.h> +#include <scsi.h> +#include <sata.h> +#include <asm/arch/sata.h> +#include <asm/io.h> +#include <generic-phy.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct dwc_ahci_priv { + void *base; + void *wrapper_base; +}; + +static int dwc_ahci_ofdata_to_platdata(struct udevice *dev) +{ + struct dwc_ahci_priv *priv = dev_get_priv(dev); + struct scsi_platdata *plat = dev_get_platdata(dev); + fdt_addr_t addr; + + plat->max_id = fdtdec_get_uint(gd->fdt_blob, dev->of_offset, "max-id", + CONFIG_SYS_SCSI_MAX_SCSI_ID); + plat->max_lun = fdtdec_get_uint(gd->fdt_blob, dev->of_offset, + "max-lun", CONFIG_SYS_SCSI_MAX_LUN); + + priv->base = map_physmem(dev_get_addr(dev), sizeof(void *), + MAP_NOCACHE); + + addr = dev_get_addr_index(dev, 1); + if (addr != FDT_ADDR_T_NONE) { + priv->wrapper_base = map_physmem(addr, sizeof(void *), + MAP_NOCACHE); + } else { + priv->wrapper_base = NULL; + } + + return 0; +} + +static int dwc_ahci_probe(struct udevice *dev) +{ + struct dwc_ahci_priv *priv = dev_get_priv(dev); + int ret; + struct phy phy; + + ret = generic_phy_get_by_name(dev, "sata-phy", &phy); + if (ret) { + error("can't get the phy from DT\n"); + return ret; + } + + ret = generic_phy_init(&phy); + if (ret) { + error("unable to initialize the sata phy\n"); + return ret; + } + + ret = generic_phy_power_on(&phy); + if (ret) { + error("unable to power on the sata phy\n"); + return ret; + } + + if (priv->wrapper_base) { + u32 val = TI_SATA_IDLE_NO | TI_SATA_STANDBY_NO; + + /* Enable SATA module, No Idle, No Standby */ + writel(val, priv->wrapper_base + TI_SATA_SYSCONFIG); + } + + return ahci_init(priv->base); +} + +static const struct udevice_id dwc_ahci_ids[] = { + { .compatible = "snps,dwc-ahci" }, + { } +}; + +U_BOOT_DRIVER(dwc_ahci) = { + .name = "dwc_ahci", + .id = UCLASS_SCSI, + .of_match = dwc_ahci_ids, + .ofdata_to_platdata = dwc_ahci_ofdata_to_platdata, + .probe = dwc_ahci_probe, + .priv_auto_alloc_size = sizeof(struct dwc_ahci_priv), + .platdata_auto_alloc_size = sizeof(struct scsi_platdata), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +};

Enable disk driver model for dra7xx_evm as dwc_ahci supports driver model. As a consequence we must also enable CONFIG_BLK and CONFIG_DM_USB.
Signed-off-by: Mugunthan V N mugunthanvnm@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com Reviewed-by: Tom Rini trini@konsulko.com --- configs/dra7xx_evm_defconfig | 12 +++++++++++- configs/dra7xx_hs_evm_defconfig | 11 ++++++++++- configs/sandbox_defconfig | 2 +- configs/sandbox_noblk_defconfig | 2 +- configs/sandbox_spl_defconfig | 4 ++-- 5 files changed, 25 insertions(+), 6 deletions(-)
diff --git a/configs/dra7xx_evm_defconfig b/configs/dra7xx_evm_defconfig index 42f87b3..677a3e6 100644 --- a/configs/dra7xx_evm_defconfig +++ b/configs/dra7xx_evm_defconfig @@ -59,7 +59,13 @@ CONFIG_SPL_OF_CONTROL=y CONFIG_OF_LIST="dra7-evm dra72-evm dra72-evm-revc dra71-evm" CONFIG_DM=y CONFIG_SPL_DM=y -# CONFIG_BLK is not set +CONFIG_REGMAP=y +CONFIG_SPL_REGMAP=y +CONFIG_SYSCON=y +CONFIG_SPL_SYSCON=y +CONFIG_BLK=y +CONFIG_DM_SCSI=y +CONFIG_DWC_AHCI=y CONFIG_DFU_MMC=y CONFIG_DFU_RAM=y CONFIG_DFU_SF=y @@ -89,6 +95,7 @@ CONFIG_TI_QSPI=y CONFIG_TIMER=y CONFIG_OMAP_TIMER=y CONFIG_USB=y +CONFIG_DM_USB=y CONFIG_USB_XHCI_HCD=y CONFIG_USB_XHCI_DWC3=y CONFIG_USB_DWC3=y @@ -101,3 +108,6 @@ CONFIG_USB_GADGET_DOWNLOAD=y CONFIG_G_DNL_MANUFACTURER="Texas Instruments" CONFIG_G_DNL_VENDOR_NUM=0x0451 CONFIG_G_DNL_PRODUCT_NUM=0xd022 +CONFIG_SPL_PHY=y +CONFIG_PIPE3_PHY=y +CONFIG_SPL_PIPE3_PHY=y diff --git a/configs/dra7xx_hs_evm_defconfig b/configs/dra7xx_hs_evm_defconfig index 871604f..5e803b5 100644 --- a/configs/dra7xx_hs_evm_defconfig +++ b/configs/dra7xx_hs_evm_defconfig @@ -64,7 +64,12 @@ CONFIG_SPL_OF_CONTROL=y CONFIG_OF_LIST="dra7-evm dra72-evm dra72-evm-revc dra71-evm" CONFIG_DM=y CONFIG_SPL_DM=y -# CONFIG_BLK is not set +CONFIG_REGMAP=y +CONFIG_SPL_REGMAP=y +CONFIG_SYSCON=y +CONFIG_SPL_SYSCON=y +CONFIG_DM_SCSI=y +CONFIG_DWC_AHCI=y CONFIG_DFU_MMC=y CONFIG_DFU_RAM=y CONFIG_DFU_SF=y @@ -94,6 +99,7 @@ CONFIG_TI_QSPI=y CONFIG_TIMER=y CONFIG_OMAP_TIMER=y CONFIG_USB=y +CONFIG_DM_USB=y CONFIG_USB_XHCI_HCD=y CONFIG_USB_XHCI_DWC3=y CONFIG_USB_DWC3=y @@ -106,3 +112,6 @@ CONFIG_USB_GADGET_DOWNLOAD=y CONFIG_G_DNL_MANUFACTURER="Texas Instruments" CONFIG_G_DNL_VENDOR_NUM=0x0451 CONFIG_G_DNL_PRODUCT_NUM=0xd022 +CONFIG_SPL_PHY=y +CONFIG_PIPE3_PHY=y +CONFIG_SPL_PIPE3_PHY=y diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index a7bc08e..1c32c05 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -165,7 +165,7 @@ CONFIG_CONSOLE_ROTATION=y CONFIG_CONSOLE_TRUETYPE=y CONFIG_CONSOLE_TRUETYPE_CANTORAONE=y CONFIG_VIDEO_SANDBOX_SDL=y -CONFIG_GENERIC_PHY=y +CONFIG_PHY=y CONFIG_PHY_SANDBOX=y CONFIG_CMD_DHRYSTONE=y CONFIG_TPM=y diff --git a/configs/sandbox_noblk_defconfig b/configs/sandbox_noblk_defconfig index 59a80ef..9dfdf81 100644 --- a/configs/sandbox_noblk_defconfig +++ b/configs/sandbox_noblk_defconfig @@ -167,7 +167,7 @@ CONFIG_CONSOLE_ROTATION=y CONFIG_CONSOLE_TRUETYPE=y CONFIG_CONSOLE_TRUETYPE_CANTORAONE=y CONFIG_VIDEO_SANDBOX_SDL=y -CONFIG_GENERIC_PHY=y +CONFIG_PHY=y CONFIG_PHY_SANDBOX=y CONFIG_CMD_DHRYSTONE=y CONFIG_TPM=y diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index b1a2209..2a0f06a 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -171,8 +171,8 @@ CONFIG_CONSOLE_ROTATION=y CONFIG_CONSOLE_TRUETYPE=y CONFIG_CONSOLE_TRUETYPE_CANTORAONE=y CONFIG_VIDEO_SANDBOX_SDL=y -CONFIG_GENERIC_PHY=y -CONFIG_SPL_GENERIC_PHY=y +CONFIG_PHY=y +CONFIG_SPL_PHY=y CONFIG_PHY_SANDBOX=y CONFIG_CMD_DHRYSTONE=y CONFIG_TPM=y

On 21 April 2017 at 04:08, Jean-Jacques Hiblot jjhiblot@ti.com wrote:
Enable disk driver model for dra7xx_evm as dwc_ahci supports driver model. As a consequence we must also enable CONFIG_BLK and CONFIG_DM_USB.
Signed-off-by: Mugunthan V N mugunthanvnm@ti.com Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com Reviewed-by: Tom Rini trini@konsulko.com
configs/dra7xx_evm_defconfig | 12 +++++++++++- configs/dra7xx_hs_evm_defconfig | 11 ++++++++++- configs/sandbox_defconfig | 2 +- configs/sandbox_noblk_defconfig | 2 +- configs/sandbox_spl_defconfig | 4 ++-- 5 files changed, 25 insertions(+), 6 deletions(-)
Reviewed-by: Simon Glass sjg@chromium.org
participants (2)
-
Jean-Jacques Hiblot
-
Simon Glass