162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * phy-brcm-usb.c - Broadcom USB Phy Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015-2017 Broadcom 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/phy/phy.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/soc/brcmstb/brcmstb.h> 1862306a36Sopenharmony_ci#include <dt-bindings/phy/phy.h> 1962306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 2062306a36Sopenharmony_ci#include <linux/suspend.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "phy-brcm-usb-init.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic DEFINE_MUTEX(sysfs_lock); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cienum brcm_usb_phy_id { 2762306a36Sopenharmony_ci BRCM_USB_PHY_2_0 = 0, 2862306a36Sopenharmony_ci BRCM_USB_PHY_3_0, 2962306a36Sopenharmony_ci BRCM_USB_PHY_ID_MAX 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistruct value_to_name_map { 3362306a36Sopenharmony_ci int value; 3462306a36Sopenharmony_ci const char *name; 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct match_chip_info { 3862306a36Sopenharmony_ci void (*init_func)(struct brcm_usb_init_params *params); 3962306a36Sopenharmony_ci u8 required_regs[BRCM_REGS_MAX + 1]; 4062306a36Sopenharmony_ci u8 optional_reg; 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic const struct value_to_name_map brcm_dr_mode_to_name[] = { 4462306a36Sopenharmony_ci { USB_CTLR_MODE_HOST, "host" }, 4562306a36Sopenharmony_ci { USB_CTLR_MODE_DEVICE, "peripheral" }, 4662306a36Sopenharmony_ci { USB_CTLR_MODE_DRD, "drd" }, 4762306a36Sopenharmony_ci { USB_CTLR_MODE_TYPEC_PD, "typec-pd" } 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic const struct value_to_name_map brcm_dual_mode_to_name[] = { 5162306a36Sopenharmony_ci { 0, "host" }, 5262306a36Sopenharmony_ci { 1, "device" }, 5362306a36Sopenharmony_ci { 2, "auto" }, 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistruct brcm_usb_phy { 5762306a36Sopenharmony_ci struct phy *phy; 5862306a36Sopenharmony_ci unsigned int id; 5962306a36Sopenharmony_ci bool inited; 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistruct brcm_usb_phy_data { 6362306a36Sopenharmony_ci struct brcm_usb_init_params ini; 6462306a36Sopenharmony_ci bool has_eohci; 6562306a36Sopenharmony_ci bool has_xhci; 6662306a36Sopenharmony_ci struct clk *usb_20_clk; 6762306a36Sopenharmony_ci struct clk *usb_30_clk; 6862306a36Sopenharmony_ci struct clk *suspend_clk; 6962306a36Sopenharmony_ci struct mutex mutex; /* serialize phy init */ 7062306a36Sopenharmony_ci int init_count; 7162306a36Sopenharmony_ci int wake_irq; 7262306a36Sopenharmony_ci struct brcm_usb_phy phys[BRCM_USB_PHY_ID_MAX]; 7362306a36Sopenharmony_ci struct notifier_block pm_notifier; 7462306a36Sopenharmony_ci bool pm_active; 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic s8 *node_reg_names[BRCM_REGS_MAX] = { 7862306a36Sopenharmony_ci "crtl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio", "bdc_ec" 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int brcm_pm_notifier(struct notifier_block *notifier, 8262306a36Sopenharmony_ci unsigned long pm_event, 8362306a36Sopenharmony_ci void *unused) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct brcm_usb_phy_data *priv = 8662306a36Sopenharmony_ci container_of(notifier, struct brcm_usb_phy_data, pm_notifier); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci switch (pm_event) { 8962306a36Sopenharmony_ci case PM_HIBERNATION_PREPARE: 9062306a36Sopenharmony_ci case PM_SUSPEND_PREPARE: 9162306a36Sopenharmony_ci priv->pm_active = true; 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci case PM_POST_RESTORE: 9462306a36Sopenharmony_ci case PM_POST_HIBERNATION: 9562306a36Sopenharmony_ci case PM_POST_SUSPEND: 9662306a36Sopenharmony_ci priv->pm_active = false; 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci return NOTIFY_DONE; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic irqreturn_t brcm_usb_phy_wake_isr(int irq, void *dev_id) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct device *dev = dev_id; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci pm_wakeup_event(dev, 0); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return IRQ_HANDLED; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic int brcm_usb_phy_init(struct phy *gphy) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct brcm_usb_phy *phy = phy_get_drvdata(gphy); 11462306a36Sopenharmony_ci struct brcm_usb_phy_data *priv = 11562306a36Sopenharmony_ci container_of(phy, struct brcm_usb_phy_data, phys[phy->id]); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (priv->pm_active) 11862306a36Sopenharmony_ci return 0; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* 12162306a36Sopenharmony_ci * Use a lock to make sure a second caller waits until 12262306a36Sopenharmony_ci * the base phy is inited before using it. 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_ci mutex_lock(&priv->mutex); 12562306a36Sopenharmony_ci if (priv->init_count++ == 0) { 12662306a36Sopenharmony_ci clk_prepare_enable(priv->usb_20_clk); 12762306a36Sopenharmony_ci clk_prepare_enable(priv->usb_30_clk); 12862306a36Sopenharmony_ci clk_prepare_enable(priv->suspend_clk); 12962306a36Sopenharmony_ci brcm_usb_init_common(&priv->ini); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 13262306a36Sopenharmony_ci if (phy->id == BRCM_USB_PHY_2_0) 13362306a36Sopenharmony_ci brcm_usb_init_eohci(&priv->ini); 13462306a36Sopenharmony_ci else if (phy->id == BRCM_USB_PHY_3_0) 13562306a36Sopenharmony_ci brcm_usb_init_xhci(&priv->ini); 13662306a36Sopenharmony_ci phy->inited = true; 13762306a36Sopenharmony_ci dev_dbg(&gphy->dev, "INIT, id: %d, total: %d\n", phy->id, 13862306a36Sopenharmony_ci priv->init_count); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int brcm_usb_phy_exit(struct phy *gphy) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct brcm_usb_phy *phy = phy_get_drvdata(gphy); 14662306a36Sopenharmony_ci struct brcm_usb_phy_data *priv = 14762306a36Sopenharmony_ci container_of(phy, struct brcm_usb_phy_data, phys[phy->id]); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (priv->pm_active) 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci dev_dbg(&gphy->dev, "EXIT\n"); 15362306a36Sopenharmony_ci if (phy->id == BRCM_USB_PHY_2_0) 15462306a36Sopenharmony_ci brcm_usb_uninit_eohci(&priv->ini); 15562306a36Sopenharmony_ci if (phy->id == BRCM_USB_PHY_3_0) 15662306a36Sopenharmony_ci brcm_usb_uninit_xhci(&priv->ini); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* If both xhci and eohci are gone, reset everything else */ 15962306a36Sopenharmony_ci mutex_lock(&priv->mutex); 16062306a36Sopenharmony_ci if (--priv->init_count == 0) { 16162306a36Sopenharmony_ci brcm_usb_uninit_common(&priv->ini); 16262306a36Sopenharmony_ci clk_disable_unprepare(priv->usb_20_clk); 16362306a36Sopenharmony_ci clk_disable_unprepare(priv->usb_30_clk); 16462306a36Sopenharmony_ci clk_disable_unprepare(priv->suspend_clk); 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 16762306a36Sopenharmony_ci phy->inited = false; 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic const struct phy_ops brcm_usb_phy_ops = { 17262306a36Sopenharmony_ci .init = brcm_usb_phy_init, 17362306a36Sopenharmony_ci .exit = brcm_usb_phy_exit, 17462306a36Sopenharmony_ci .owner = THIS_MODULE, 17562306a36Sopenharmony_ci}; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic struct phy *brcm_usb_phy_xlate(struct device *dev, 17862306a36Sopenharmony_ci struct of_phandle_args *args) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct brcm_usb_phy_data *data = dev_get_drvdata(dev); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* 18362306a36Sopenharmony_ci * values 0 and 1 are for backward compatibility with 18462306a36Sopenharmony_ci * device tree nodes from older bootloaders. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci switch (args->args[0]) { 18762306a36Sopenharmony_ci case 0: 18862306a36Sopenharmony_ci case PHY_TYPE_USB2: 18962306a36Sopenharmony_ci if (data->phys[BRCM_USB_PHY_2_0].phy) 19062306a36Sopenharmony_ci return data->phys[BRCM_USB_PHY_2_0].phy; 19162306a36Sopenharmony_ci dev_warn(dev, "Error, 2.0 Phy not found\n"); 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci case 1: 19462306a36Sopenharmony_ci case PHY_TYPE_USB3: 19562306a36Sopenharmony_ci if (data->phys[BRCM_USB_PHY_3_0].phy) 19662306a36Sopenharmony_ci return data->phys[BRCM_USB_PHY_3_0].phy; 19762306a36Sopenharmony_ci dev_warn(dev, "Error, 3.0 Phy not found\n"); 19862306a36Sopenharmony_ci break; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic int name_to_value(const struct value_to_name_map *table, int count, 20462306a36Sopenharmony_ci const char *name, int *value) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci int x; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci *value = 0; 20962306a36Sopenharmony_ci for (x = 0; x < count; x++) { 21062306a36Sopenharmony_ci if (sysfs_streq(name, table[x].name)) { 21162306a36Sopenharmony_ci *value = x; 21262306a36Sopenharmony_ci return 0; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci return -EINVAL; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic const char *value_to_name(const struct value_to_name_map *table, int count, 21962306a36Sopenharmony_ci int value) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci if (value >= count) 22262306a36Sopenharmony_ci return "unknown"; 22362306a36Sopenharmony_ci return table[value].name; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic ssize_t dr_mode_show(struct device *dev, 22762306a36Sopenharmony_ci struct device_attribute *attr, 22862306a36Sopenharmony_ci char *buf) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct brcm_usb_phy_data *priv = dev_get_drvdata(dev); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return sprintf(buf, "%s\n", 23362306a36Sopenharmony_ci value_to_name(&brcm_dr_mode_to_name[0], 23462306a36Sopenharmony_ci ARRAY_SIZE(brcm_dr_mode_to_name), 23562306a36Sopenharmony_ci priv->ini.supported_port_modes)); 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(dr_mode); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic ssize_t dual_select_store(struct device *dev, 24062306a36Sopenharmony_ci struct device_attribute *attr, 24162306a36Sopenharmony_ci const char *buf, size_t len) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct brcm_usb_phy_data *priv = dev_get_drvdata(dev); 24462306a36Sopenharmony_ci int value; 24562306a36Sopenharmony_ci int res; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci mutex_lock(&sysfs_lock); 24862306a36Sopenharmony_ci res = name_to_value(&brcm_dual_mode_to_name[0], 24962306a36Sopenharmony_ci ARRAY_SIZE(brcm_dual_mode_to_name), buf, &value); 25062306a36Sopenharmony_ci if (!res) { 25162306a36Sopenharmony_ci priv->ini.port_mode = value; 25262306a36Sopenharmony_ci brcm_usb_set_dual_select(&priv->ini); 25362306a36Sopenharmony_ci res = len; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci mutex_unlock(&sysfs_lock); 25662306a36Sopenharmony_ci return res; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic ssize_t dual_select_show(struct device *dev, 26062306a36Sopenharmony_ci struct device_attribute *attr, 26162306a36Sopenharmony_ci char *buf) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct brcm_usb_phy_data *priv = dev_get_drvdata(dev); 26462306a36Sopenharmony_ci int value; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci mutex_lock(&sysfs_lock); 26762306a36Sopenharmony_ci value = brcm_usb_get_dual_select(&priv->ini); 26862306a36Sopenharmony_ci mutex_unlock(&sysfs_lock); 26962306a36Sopenharmony_ci return sprintf(buf, "%s\n", 27062306a36Sopenharmony_ci value_to_name(&brcm_dual_mode_to_name[0], 27162306a36Sopenharmony_ci ARRAY_SIZE(brcm_dual_mode_to_name), 27262306a36Sopenharmony_ci value)); 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(dual_select); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic struct attribute *brcm_usb_phy_attrs[] = { 27762306a36Sopenharmony_ci &dev_attr_dr_mode.attr, 27862306a36Sopenharmony_ci &dev_attr_dual_select.attr, 27962306a36Sopenharmony_ci NULL 28062306a36Sopenharmony_ci}; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic const struct attribute_group brcm_usb_phy_group = { 28362306a36Sopenharmony_ci .attrs = brcm_usb_phy_attrs, 28462306a36Sopenharmony_ci}; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic const struct match_chip_info chip_info_4908 = { 28762306a36Sopenharmony_ci .init_func = &brcm_usb_dvr_init_4908, 28862306a36Sopenharmony_ci .required_regs = { 28962306a36Sopenharmony_ci BRCM_REGS_CTRL, 29062306a36Sopenharmony_ci BRCM_REGS_XHCI_EC, 29162306a36Sopenharmony_ci -1, 29262306a36Sopenharmony_ci }, 29362306a36Sopenharmony_ci}; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic const struct match_chip_info chip_info_7216 = { 29662306a36Sopenharmony_ci .init_func = &brcm_usb_dvr_init_7216, 29762306a36Sopenharmony_ci .required_regs = { 29862306a36Sopenharmony_ci BRCM_REGS_CTRL, 29962306a36Sopenharmony_ci BRCM_REGS_XHCI_EC, 30062306a36Sopenharmony_ci BRCM_REGS_XHCI_GBL, 30162306a36Sopenharmony_ci -1, 30262306a36Sopenharmony_ci }, 30362306a36Sopenharmony_ci}; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic const struct match_chip_info chip_info_7211b0 = { 30662306a36Sopenharmony_ci .init_func = &brcm_usb_dvr_init_7211b0, 30762306a36Sopenharmony_ci .required_regs = { 30862306a36Sopenharmony_ci BRCM_REGS_CTRL, 30962306a36Sopenharmony_ci BRCM_REGS_XHCI_EC, 31062306a36Sopenharmony_ci BRCM_REGS_XHCI_GBL, 31162306a36Sopenharmony_ci BRCM_REGS_USB_PHY, 31262306a36Sopenharmony_ci BRCM_REGS_USB_MDIO, 31362306a36Sopenharmony_ci -1, 31462306a36Sopenharmony_ci }, 31562306a36Sopenharmony_ci .optional_reg = BRCM_REGS_BDC_EC, 31662306a36Sopenharmony_ci}; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic const struct match_chip_info chip_info_7445 = { 31962306a36Sopenharmony_ci .init_func = &brcm_usb_dvr_init_7445, 32062306a36Sopenharmony_ci .required_regs = { 32162306a36Sopenharmony_ci BRCM_REGS_CTRL, 32262306a36Sopenharmony_ci BRCM_REGS_XHCI_EC, 32362306a36Sopenharmony_ci -1, 32462306a36Sopenharmony_ci }, 32562306a36Sopenharmony_ci}; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic const struct of_device_id brcm_usb_dt_ids[] = { 32862306a36Sopenharmony_ci { 32962306a36Sopenharmony_ci .compatible = "brcm,bcm4908-usb-phy", 33062306a36Sopenharmony_ci .data = &chip_info_4908, 33162306a36Sopenharmony_ci }, 33262306a36Sopenharmony_ci { 33362306a36Sopenharmony_ci .compatible = "brcm,bcm7216-usb-phy", 33462306a36Sopenharmony_ci .data = &chip_info_7216, 33562306a36Sopenharmony_ci }, 33662306a36Sopenharmony_ci { 33762306a36Sopenharmony_ci .compatible = "brcm,bcm7211-usb-phy", 33862306a36Sopenharmony_ci .data = &chip_info_7211b0, 33962306a36Sopenharmony_ci }, 34062306a36Sopenharmony_ci { 34162306a36Sopenharmony_ci .compatible = "brcm,brcmstb-usb-phy", 34262306a36Sopenharmony_ci .data = &chip_info_7445, 34362306a36Sopenharmony_ci }, 34462306a36Sopenharmony_ci { /* sentinel */ } 34562306a36Sopenharmony_ci}; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic int brcm_usb_get_regs(struct platform_device *pdev, 34862306a36Sopenharmony_ci enum brcmusb_reg_sel regs, 34962306a36Sopenharmony_ci struct brcm_usb_init_params *ini, 35062306a36Sopenharmony_ci bool optional) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct resource *res; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* Older DT nodes have ctrl and optional xhci_ec by index only */ 35562306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 35662306a36Sopenharmony_ci node_reg_names[regs]); 35762306a36Sopenharmony_ci if (res == NULL) { 35862306a36Sopenharmony_ci if (regs == BRCM_REGS_CTRL) { 35962306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 36062306a36Sopenharmony_ci } else if (regs == BRCM_REGS_XHCI_EC) { 36162306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 36262306a36Sopenharmony_ci /* XHCI_EC registers are optional */ 36362306a36Sopenharmony_ci if (res == NULL) 36462306a36Sopenharmony_ci return 0; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci if (res == NULL) { 36762306a36Sopenharmony_ci if (optional) { 36862306a36Sopenharmony_ci dev_dbg(&pdev->dev, 36962306a36Sopenharmony_ci "Optional reg %s not found\n", 37062306a36Sopenharmony_ci node_reg_names[regs]); 37162306a36Sopenharmony_ci return 0; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci dev_err(&pdev->dev, "can't get %s base addr\n", 37462306a36Sopenharmony_ci node_reg_names[regs]); 37562306a36Sopenharmony_ci return 1; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci ini->regs[regs] = devm_ioremap_resource(&pdev->dev, res); 37962306a36Sopenharmony_ci if (IS_ERR(ini->regs[regs])) { 38062306a36Sopenharmony_ci dev_err(&pdev->dev, "can't map %s register space\n", 38162306a36Sopenharmony_ci node_reg_names[regs]); 38262306a36Sopenharmony_ci return 1; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci return 0; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic int brcm_usb_phy_dvr_init(struct platform_device *pdev, 38862306a36Sopenharmony_ci struct brcm_usb_phy_data *priv, 38962306a36Sopenharmony_ci struct device_node *dn) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 39262306a36Sopenharmony_ci struct phy *gphy = NULL; 39362306a36Sopenharmony_ci int err; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci priv->usb_20_clk = of_clk_get_by_name(dn, "sw_usb"); 39662306a36Sopenharmony_ci if (IS_ERR(priv->usb_20_clk)) { 39762306a36Sopenharmony_ci if (PTR_ERR(priv->usb_20_clk) == -EPROBE_DEFER) 39862306a36Sopenharmony_ci return -EPROBE_DEFER; 39962306a36Sopenharmony_ci dev_info(dev, "Clock not found in Device Tree\n"); 40062306a36Sopenharmony_ci priv->usb_20_clk = NULL; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci err = clk_prepare_enable(priv->usb_20_clk); 40362306a36Sopenharmony_ci if (err) 40462306a36Sopenharmony_ci return err; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (priv->has_eohci) { 40762306a36Sopenharmony_ci gphy = devm_phy_create(dev, NULL, &brcm_usb_phy_ops); 40862306a36Sopenharmony_ci if (IS_ERR(gphy)) { 40962306a36Sopenharmony_ci dev_err(dev, "failed to create EHCI/OHCI PHY\n"); 41062306a36Sopenharmony_ci return PTR_ERR(gphy); 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci priv->phys[BRCM_USB_PHY_2_0].phy = gphy; 41362306a36Sopenharmony_ci priv->phys[BRCM_USB_PHY_2_0].id = BRCM_USB_PHY_2_0; 41462306a36Sopenharmony_ci phy_set_drvdata(gphy, &priv->phys[BRCM_USB_PHY_2_0]); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (priv->has_xhci) { 41862306a36Sopenharmony_ci gphy = devm_phy_create(dev, NULL, &brcm_usb_phy_ops); 41962306a36Sopenharmony_ci if (IS_ERR(gphy)) { 42062306a36Sopenharmony_ci dev_err(dev, "failed to create XHCI PHY\n"); 42162306a36Sopenharmony_ci return PTR_ERR(gphy); 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci priv->phys[BRCM_USB_PHY_3_0].phy = gphy; 42462306a36Sopenharmony_ci priv->phys[BRCM_USB_PHY_3_0].id = BRCM_USB_PHY_3_0; 42562306a36Sopenharmony_ci phy_set_drvdata(gphy, &priv->phys[BRCM_USB_PHY_3_0]); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci priv->usb_30_clk = of_clk_get_by_name(dn, "sw_usb3"); 42862306a36Sopenharmony_ci if (IS_ERR(priv->usb_30_clk)) { 42962306a36Sopenharmony_ci if (PTR_ERR(priv->usb_30_clk) == -EPROBE_DEFER) 43062306a36Sopenharmony_ci return -EPROBE_DEFER; 43162306a36Sopenharmony_ci dev_info(dev, 43262306a36Sopenharmony_ci "USB3.0 clock not found in Device Tree\n"); 43362306a36Sopenharmony_ci priv->usb_30_clk = NULL; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci err = clk_prepare_enable(priv->usb_30_clk); 43662306a36Sopenharmony_ci if (err) 43762306a36Sopenharmony_ci return err; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci priv->suspend_clk = clk_get(dev, "usb0_freerun"); 44162306a36Sopenharmony_ci if (IS_ERR(priv->suspend_clk)) { 44262306a36Sopenharmony_ci if (PTR_ERR(priv->suspend_clk) == -EPROBE_DEFER) 44362306a36Sopenharmony_ci return -EPROBE_DEFER; 44462306a36Sopenharmony_ci dev_err(dev, "Suspend Clock not found in Device Tree\n"); 44562306a36Sopenharmony_ci priv->suspend_clk = NULL; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci priv->wake_irq = platform_get_irq_byname_optional(pdev, "wake"); 44962306a36Sopenharmony_ci if (priv->wake_irq < 0) 45062306a36Sopenharmony_ci priv->wake_irq = platform_get_irq_byname_optional(pdev, "wakeup"); 45162306a36Sopenharmony_ci if (priv->wake_irq >= 0) { 45262306a36Sopenharmony_ci err = devm_request_irq(dev, priv->wake_irq, 45362306a36Sopenharmony_ci brcm_usb_phy_wake_isr, 0, 45462306a36Sopenharmony_ci dev_name(dev), dev); 45562306a36Sopenharmony_ci if (err < 0) 45662306a36Sopenharmony_ci return err; 45762306a36Sopenharmony_ci device_set_wakeup_capable(dev, 1); 45862306a36Sopenharmony_ci } else { 45962306a36Sopenharmony_ci dev_info(dev, 46062306a36Sopenharmony_ci "Wake interrupt missing, system wake not supported\n"); 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic int brcm_usb_phy_probe(struct platform_device *pdev) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 46962306a36Sopenharmony_ci struct brcm_usb_phy_data *priv; 47062306a36Sopenharmony_ci struct phy_provider *phy_provider; 47162306a36Sopenharmony_ci struct device_node *dn = pdev->dev.of_node; 47262306a36Sopenharmony_ci int err; 47362306a36Sopenharmony_ci const char *mode; 47462306a36Sopenharmony_ci const struct match_chip_info *info; 47562306a36Sopenharmony_ci struct regmap *rmap; 47662306a36Sopenharmony_ci int x; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 47962306a36Sopenharmony_ci if (!priv) 48062306a36Sopenharmony_ci return -ENOMEM; 48162306a36Sopenharmony_ci platform_set_drvdata(pdev, priv); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci priv->ini.family_id = brcmstb_get_family_id(); 48462306a36Sopenharmony_ci priv->ini.product_id = brcmstb_get_product_id(); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci info = of_device_get_match_data(&pdev->dev); 48762306a36Sopenharmony_ci if (!info) 48862306a36Sopenharmony_ci return -ENOENT; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci info->init_func(&priv->ini); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci dev_dbg(dev, "Best mapping table is for %s\n", 49362306a36Sopenharmony_ci priv->ini.family_name); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci of_property_read_u32(dn, "brcm,ipp", &priv->ini.ipp); 49662306a36Sopenharmony_ci of_property_read_u32(dn, "brcm,ioc", &priv->ini.ioc); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci priv->ini.supported_port_modes = USB_CTLR_MODE_HOST; 49962306a36Sopenharmony_ci err = of_property_read_string(dn, "dr_mode", &mode); 50062306a36Sopenharmony_ci if (err == 0) { 50162306a36Sopenharmony_ci name_to_value(&brcm_dr_mode_to_name[0], 50262306a36Sopenharmony_ci ARRAY_SIZE(brcm_dr_mode_to_name), 50362306a36Sopenharmony_ci mode, &priv->ini.supported_port_modes); 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci /* Default port_mode to supported port_modes */ 50662306a36Sopenharmony_ci priv->ini.port_mode = priv->ini.supported_port_modes; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (of_property_read_bool(dn, "brcm,has-xhci")) 50962306a36Sopenharmony_ci priv->has_xhci = true; 51062306a36Sopenharmony_ci if (of_property_read_bool(dn, "brcm,has-eohci")) 51162306a36Sopenharmony_ci priv->has_eohci = true; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci for (x = 0; x < BRCM_REGS_MAX; x++) { 51462306a36Sopenharmony_ci if (info->required_regs[x] >= BRCM_REGS_MAX) 51562306a36Sopenharmony_ci break; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci err = brcm_usb_get_regs(pdev, info->required_regs[x], 51862306a36Sopenharmony_ci &priv->ini, false); 51962306a36Sopenharmony_ci if (err) 52062306a36Sopenharmony_ci return -EINVAL; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci if (info->optional_reg) { 52362306a36Sopenharmony_ci err = brcm_usb_get_regs(pdev, info->optional_reg, 52462306a36Sopenharmony_ci &priv->ini, true); 52562306a36Sopenharmony_ci if (err) 52662306a36Sopenharmony_ci return -EINVAL; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci err = brcm_usb_phy_dvr_init(pdev, priv, dn); 53062306a36Sopenharmony_ci if (err) 53162306a36Sopenharmony_ci return err; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci priv->pm_notifier.notifier_call = brcm_pm_notifier; 53462306a36Sopenharmony_ci register_pm_notifier(&priv->pm_notifier); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci mutex_init(&priv->mutex); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* make sure invert settings are correct */ 53962306a36Sopenharmony_ci brcm_usb_init_ipp(&priv->ini); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* 54262306a36Sopenharmony_ci * Create sysfs entries for mode. 54362306a36Sopenharmony_ci * Remove "dual_select" attribute if not in dual mode 54462306a36Sopenharmony_ci */ 54562306a36Sopenharmony_ci if (priv->ini.supported_port_modes != USB_CTLR_MODE_DRD) 54662306a36Sopenharmony_ci brcm_usb_phy_attrs[1] = NULL; 54762306a36Sopenharmony_ci err = sysfs_create_group(&dev->kobj, &brcm_usb_phy_group); 54862306a36Sopenharmony_ci if (err) 54962306a36Sopenharmony_ci dev_warn(dev, "Error creating sysfs attributes\n"); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* Get piarbctl syscon if it exists */ 55262306a36Sopenharmony_ci rmap = syscon_regmap_lookup_by_phandle(dev->of_node, 55362306a36Sopenharmony_ci "syscon-piarbctl"); 55462306a36Sopenharmony_ci if (IS_ERR(rmap)) 55562306a36Sopenharmony_ci rmap = syscon_regmap_lookup_by_phandle(dev->of_node, 55662306a36Sopenharmony_ci "brcm,syscon-piarbctl"); 55762306a36Sopenharmony_ci if (!IS_ERR(rmap)) 55862306a36Sopenharmony_ci priv->ini.syscon_piarbctl = rmap; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* start with everything off */ 56162306a36Sopenharmony_ci if (priv->has_xhci) 56262306a36Sopenharmony_ci brcm_usb_uninit_xhci(&priv->ini); 56362306a36Sopenharmony_ci if (priv->has_eohci) 56462306a36Sopenharmony_ci brcm_usb_uninit_eohci(&priv->ini); 56562306a36Sopenharmony_ci brcm_usb_uninit_common(&priv->ini); 56662306a36Sopenharmony_ci clk_disable_unprepare(priv->usb_20_clk); 56762306a36Sopenharmony_ci clk_disable_unprepare(priv->usb_30_clk); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci phy_provider = devm_of_phy_provider_register(dev, brcm_usb_phy_xlate); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(phy_provider); 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic void brcm_usb_phy_remove(struct platform_device *pdev) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct brcm_usb_phy_data *priv = dev_get_drvdata(&pdev->dev); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci sysfs_remove_group(&pdev->dev.kobj, &brcm_usb_phy_group); 57962306a36Sopenharmony_ci unregister_pm_notifier(&priv->pm_notifier); 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 58362306a36Sopenharmony_cistatic int brcm_usb_phy_suspend(struct device *dev) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci struct brcm_usb_phy_data *priv = dev_get_drvdata(dev); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (priv->init_count) { 58862306a36Sopenharmony_ci dev_dbg(dev, "SUSPEND\n"); 58962306a36Sopenharmony_ci priv->ini.wake_enabled = device_may_wakeup(dev); 59062306a36Sopenharmony_ci if (priv->phys[BRCM_USB_PHY_3_0].inited) 59162306a36Sopenharmony_ci brcm_usb_uninit_xhci(&priv->ini); 59262306a36Sopenharmony_ci if (priv->phys[BRCM_USB_PHY_2_0].inited) 59362306a36Sopenharmony_ci brcm_usb_uninit_eohci(&priv->ini); 59462306a36Sopenharmony_ci brcm_usb_uninit_common(&priv->ini); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci /* 59762306a36Sopenharmony_ci * Handle the clocks unless needed for wake. This has 59862306a36Sopenharmony_ci * to work for both older XHCI->3.0-clks, EOHCI->2.0-clks 59962306a36Sopenharmony_ci * and newer XHCI->2.0-clks/3.0-clks. 60062306a36Sopenharmony_ci */ 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (!priv->ini.wake_enabled) { 60362306a36Sopenharmony_ci if (priv->phys[BRCM_USB_PHY_3_0].inited) 60462306a36Sopenharmony_ci clk_disable_unprepare(priv->usb_30_clk); 60562306a36Sopenharmony_ci if (priv->phys[BRCM_USB_PHY_2_0].inited || 60662306a36Sopenharmony_ci !priv->has_eohci) 60762306a36Sopenharmony_ci clk_disable_unprepare(priv->usb_20_clk); 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci if (priv->wake_irq >= 0) 61062306a36Sopenharmony_ci enable_irq_wake(priv->wake_irq); 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci return 0; 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic int brcm_usb_phy_resume(struct device *dev) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci struct brcm_usb_phy_data *priv = dev_get_drvdata(dev); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (!priv->ini.wake_enabled) { 62062306a36Sopenharmony_ci clk_prepare_enable(priv->usb_20_clk); 62162306a36Sopenharmony_ci clk_prepare_enable(priv->usb_30_clk); 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci brcm_usb_init_ipp(&priv->ini); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci /* 62662306a36Sopenharmony_ci * Initialize anything that was previously initialized. 62762306a36Sopenharmony_ci * Uninitialize anything that wasn't previously initialized. 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_ci if (priv->init_count) { 63062306a36Sopenharmony_ci dev_dbg(dev, "RESUME\n"); 63162306a36Sopenharmony_ci if (priv->wake_irq >= 0) 63262306a36Sopenharmony_ci disable_irq_wake(priv->wake_irq); 63362306a36Sopenharmony_ci brcm_usb_init_common(&priv->ini); 63462306a36Sopenharmony_ci if (priv->phys[BRCM_USB_PHY_2_0].inited) { 63562306a36Sopenharmony_ci brcm_usb_init_eohci(&priv->ini); 63662306a36Sopenharmony_ci } else if (priv->has_eohci) { 63762306a36Sopenharmony_ci brcm_usb_uninit_eohci(&priv->ini); 63862306a36Sopenharmony_ci clk_disable_unprepare(priv->usb_20_clk); 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci if (priv->phys[BRCM_USB_PHY_3_0].inited) { 64162306a36Sopenharmony_ci brcm_usb_init_xhci(&priv->ini); 64262306a36Sopenharmony_ci } else if (priv->has_xhci) { 64362306a36Sopenharmony_ci brcm_usb_uninit_xhci(&priv->ini); 64462306a36Sopenharmony_ci clk_disable_unprepare(priv->usb_30_clk); 64562306a36Sopenharmony_ci if (!priv->has_eohci) 64662306a36Sopenharmony_ci clk_disable_unprepare(priv->usb_20_clk); 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci } else { 64962306a36Sopenharmony_ci if (priv->has_xhci) 65062306a36Sopenharmony_ci brcm_usb_uninit_xhci(&priv->ini); 65162306a36Sopenharmony_ci if (priv->has_eohci) 65262306a36Sopenharmony_ci brcm_usb_uninit_eohci(&priv->ini); 65362306a36Sopenharmony_ci brcm_usb_uninit_common(&priv->ini); 65462306a36Sopenharmony_ci clk_disable_unprepare(priv->usb_20_clk); 65562306a36Sopenharmony_ci clk_disable_unprepare(priv->usb_30_clk); 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci priv->ini.wake_enabled = false; 65862306a36Sopenharmony_ci return 0; 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cistatic const struct dev_pm_ops brcm_usb_phy_pm_ops = { 66362306a36Sopenharmony_ci SET_LATE_SYSTEM_SLEEP_PM_OPS(brcm_usb_phy_suspend, brcm_usb_phy_resume) 66462306a36Sopenharmony_ci}; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, brcm_usb_dt_ids); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic struct platform_driver brcm_usb_driver = { 66962306a36Sopenharmony_ci .probe = brcm_usb_phy_probe, 67062306a36Sopenharmony_ci .remove_new = brcm_usb_phy_remove, 67162306a36Sopenharmony_ci .driver = { 67262306a36Sopenharmony_ci .name = "brcmstb-usb-phy", 67362306a36Sopenharmony_ci .pm = &brcm_usb_phy_pm_ops, 67462306a36Sopenharmony_ci .of_match_table = brcm_usb_dt_ids, 67562306a36Sopenharmony_ci }, 67662306a36Sopenharmony_ci}; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_cimodule_platform_driver(brcm_usb_driver); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ciMODULE_ALIAS("platform:brcmstb-usb-phy"); 68162306a36Sopenharmony_ciMODULE_AUTHOR("Al Cooper <acooper@broadcom.com>"); 68262306a36Sopenharmony_ciMODULE_DESCRIPTION("BRCM USB PHY driver"); 68362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 684