forked from torvalds/linux
Permalink
Show file tree
Hide file tree
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
4 changed files
with
368 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,215 @@ | ||
| // SPDX-License-Identifier: GPL-2.0+ | ||
|
|
||
| #include <dt-bindings/phy/phy.h> | ||
| #include <linux/init.h> | ||
| #include <linux/io.h> | ||
| #include <linux/interrupt.h> | ||
| #include <linux/kernel.h> | ||
| #include <linux/of_address.h> | ||
| #include <linux/of_irq.h> | ||
| #include <linux/phy/phy.h> | ||
| #include <linux/platform_device.h> | ||
| #include <linux/mfd/syscon.h> | ||
| #include <linux/regmap.h> | ||
| #include <linux/usb/mstar_usbc.h> | ||
| #include <linux/usb/mstar_utmi.h> | ||
|
|
||
| #define NUM_PORTS 2 | ||
| #define PORT_UHC 0 | ||
| #define PORT_OTG 1 | ||
|
|
||
| struct msc313_usb_phy { | ||
| struct device *dev; | ||
| struct phy *ports[NUM_PORTS]; | ||
| struct regmap *utmi; | ||
| struct regmap *usbc; | ||
| }; | ||
|
|
||
| static int msc313_usb_phy_init(struct phy *phy) | ||
| { | ||
| struct msc313_usb_phy *msc313_usb_phy; | ||
|
|
||
| msc313_usb_phy = phy_get_drvdata(phy); | ||
| if (!msc313_usb_phy) | ||
| return -ENODEV; | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| static const struct phy_ops msc313_usb_phy_ops = { | ||
| .init = msc313_usb_phy_init, | ||
| .owner = THIS_MODULE, | ||
| }; | ||
|
|
||
| static struct phy *msc313_usb_phy_xlate(struct device *dev, | ||
| struct of_phandle_args *args) | ||
| { | ||
| struct msc313_usb_phy *msc313_usb_phy = dev_get_drvdata(dev); | ||
| int port = args->args[0]; | ||
|
|
||
| printk("getting port %d\n", port); | ||
|
|
||
| if (!msc313_usb_phy) | ||
| return ERR_PTR(-ENODEV); | ||
| if(port >= NUM_PORTS) | ||
| return ERR_PTR(-ENODEV); | ||
|
|
||
| return msc313_usb_phy->ports[port]; | ||
| } | ||
|
|
||
| static irqreturn_t msc313_usb_phy_irq(int irq, void *data) | ||
| { | ||
| struct msc313_usb_phy *phy = data; | ||
| printk("int\n"); | ||
| regmap_write(phy->usbc, MSTAR_USBC_REG_INTSTS, MSTAR_USBC_INT_MASK); | ||
| return IRQ_HANDLED; | ||
| } | ||
|
|
||
| static void msc313_usb_phy_switch_port(struct msc313_usb_phy *phy) | ||
| { | ||
| dev_info(phy->dev, "Switching to UHC port\n"); | ||
| regmap_update_bits(phy->usbc, MSTAR_USBC_REG_PRTCTRL, | ||
| MSTAR_PRTCTRL_OTG | MSTAR_PRTCTRL_UHC, 0); | ||
| regmap_update_bits(phy->usbc, MSTAR_USBC_REG_PRTCTRL, | ||
| MSTAR_PRTCTRL_UHC, MSTAR_PRTCTRL_UHC); | ||
| // not sure what this is, might be MAC power down | ||
| //regmap_update_bits(msc313_usb_phy->usbc, MSTAR_USBC_REG_RSTCTRL, | ||
| // BIT(MSTAR_RSTCTRL_VBUSVALID), | ||
| // 0); | ||
|
|
||
| } | ||
|
|
||
| static void msc313_usb_phy_do_calibration(struct msc313_usb_phy *phy){ | ||
| unsigned int calval; | ||
| dev_info(phy->dev, "starting calibration..."); | ||
| regmap_update_bits(phy->utmi, MSTAR_UTMI_REG_CAL, | ||
| MSTAR_UTMI_REG_CAL_START, MSTAR_UTMI_REG_CAL_START); | ||
| mdelay(1); | ||
| regmap_update_bits(phy->utmi, MSTAR_UTMI_REG_CAL, | ||
| MSTAR_UTMI_REG_CAL_START, 0); | ||
| if(regmap_read_poll_timeout(phy->utmi, MSTAR_UTMI_REG_CAL, | ||
| calval, calval & MSTAR_UTMI_REG_CAL_END, 0, 1000000)){ | ||
| dev_info(phy->dev, "calibration timeout"); | ||
| } | ||
| else { | ||
| calval >>= MSTAR_UTMI_REG_CAL_DATA_SHIFT; | ||
| if(calval > 0 && calval < 0xfff) | ||
| dev_info(phy->dev, "calibration finished."); | ||
| else | ||
| dev_warn(phy->dev, "calibration failed."); | ||
| } | ||
| } | ||
|
|
||
| static int msc313_usb_phy_probe(struct platform_device *pdev) | ||
| { | ||
| struct device *dev = &pdev->dev; | ||
| struct phy_provider *phy_provider; | ||
| struct msc313_usb_phy *msc313_usb_phy; | ||
| int i, irq; | ||
| bool swap; | ||
|
|
||
| msc313_usb_phy = devm_kzalloc(dev, sizeof(*msc313_usb_phy), GFP_KERNEL); | ||
| if (!msc313_usb_phy) | ||
| return -ENOMEM; | ||
|
|
||
| msc313_usb_phy->dev = dev; | ||
|
|
||
| msc313_usb_phy->utmi = syscon_regmap_lookup_by_phandle(dev->of_node, "mstar,utmi"); | ||
| if(IS_ERR(msc313_usb_phy->utmi)){ | ||
| return PTR_ERR(msc313_usb_phy->utmi); | ||
| } | ||
|
|
||
| msc313_usb_phy->usbc = syscon_regmap_lookup_by_phandle(dev->of_node, "mstar,usbc"); | ||
| if (IS_ERR(msc313_usb_phy->usbc)) | ||
| return PTR_ERR(msc313_usb_phy->usbc); | ||
|
|
||
| irq = irq_of_parse_and_map(pdev->dev.of_node, 0); | ||
| if (!irq){ | ||
| dev_warn(&pdev->dev, "no interrupt provided"); | ||
| } | ||
|
|
||
| // hack for m5, these seem to be the reset values for i3 | ||
| regmap_write(msc313_usb_phy->usbc, MSTAR_USBC_REG_RSTCTRL, | ||
| 0x228); | ||
| regmap_write(msc313_usb_phy->utmi, MSTAR_UTMI_REG_PLL_TEST1, | ||
| 0x2088); | ||
| regmap_write(msc313_usb_phy->utmi, MSTAR_UTMI_REG_PLL_TEST0, | ||
| 0x8051); | ||
| regmap_write(msc313_usb_phy->utmi, MSTAR_UTMI_REG_CONFIG, | ||
| 0x2084); | ||
|
|
||
| //FIXME for some reason this doesn't update the registers. | ||
| // some voodoo that is enabled for the msc313 in the vendor sdk | ||
| regmap_write(msc313_usb_phy->usbc, MSTAR_USBC_REG_MIUCFG0, | ||
| 0x0); | ||
| regmap_write(msc313_usb_phy->usbc, MSTAR_USBC_REG_MIUCFG1, | ||
| 0xffff); | ||
| regmap_write(msc313_usb_phy->usbc, MSTAR_USBC_REG_MIUCFG2, | ||
| BIT(8) | 0xff); | ||
|
|
||
| // clear any existing interrupts and then enable | ||
| // the interrupt | ||
| regmap_write(msc313_usb_phy->usbc, MSTAR_USBC_REG_INTEN, | ||
| MSTAR_USBC_INT_MASK); | ||
| regmap_write(msc313_usb_phy->usbc, MSTAR_USBC_REG_INTSTS, | ||
| MSTAR_USBC_INT_MASK); | ||
| if(irq){ | ||
| devm_request_irq(&pdev->dev, irq, msc313_usb_phy_irq, IRQF_SHARED, | ||
| dev_name(&pdev->dev), msc313_usb_phy); | ||
| } | ||
|
|
||
| // power up hacks | ||
| regmap_write(msc313_usb_phy->utmi, REG_CLKCTRL, 0x0c2f); | ||
| regmap_write(msc313_usb_phy->utmi, REG_CLKCTRL, 0x040f); | ||
| regmap_write(msc313_usb_phy->utmi, REG_PWRCTRL, 0x7f05); | ||
|
|
||
| msc313_usb_phy_switch_port(msc313_usb_phy); | ||
|
|
||
| regmap_write(msc313_usb_phy->utmi, REG_CLKCTRL, 0x0426); | ||
| regmap_write(msc313_usb_phy->utmi, REG_PWRCTRL, 0x6bc3); | ||
| regmap_write(msc313_usb_phy->utmi, REG_PWRCTRL, 0x69c3); | ||
| regmap_write(msc313_usb_phy->utmi, REG_PWRCTRL, 0x0001); | ||
|
|
||
| regmap_write(msc313_usb_phy->utmi, REG_EYESETTING1, 0x0210); | ||
| regmap_write(msc313_usb_phy->utmi, REG_EYESETTING2, 0x8100); | ||
|
|
||
|
|
||
| msc313_usb_phy_do_calibration(msc313_usb_phy); | ||
|
|
||
| swap = of_property_read_bool(dev->of_node, "mstar,utmi-dxswap"); | ||
|
|
||
| if(swap) | ||
| dev_info(dev, "enabling data line swap"); | ||
| regmap_update_bits(msc313_usb_phy->utmi, MSTAR_UTMI_REG_CLKINV, | ||
| MSTAR_UTMI_REG_CLKINV_DPDNSWP, swap ? MSTAR_UTMI_REG_CLKINV_DPDNSWP : 0); | ||
|
|
||
| //regmap_update_bits(msc313_usb_phy->regmap, REG_PWRCTRL, PWRCTRL_UPLL_PDN, 0); | ||
|
|
||
| for(i = 0; i < NUM_PORTS; i++){ | ||
| msc313_usb_phy->ports[i] = devm_phy_create(dev, NULL, &msc313_usb_phy_ops); | ||
| if (IS_ERR(msc313_usb_phy->ports[i])) { | ||
| return PTR_ERR(msc313_usb_phy->ports[i]); | ||
| } | ||
| phy_set_drvdata(msc313_usb_phy->ports[i], msc313_usb_phy); | ||
| } | ||
|
|
||
| dev_set_drvdata(dev, msc313_usb_phy); | ||
|
|
||
| phy_provider = devm_of_phy_provider_register(&pdev->dev, | ||
| msc313_usb_phy_xlate); | ||
| return PTR_ERR_OR_ZERO(phy_provider); | ||
| } | ||
|
|
||
| static const struct of_device_id msc313_usb_phy_ids[] = { | ||
| { .compatible = "mstar,msc313-usb-phy", }, | ||
| { /* end of list */ }, | ||
| }; | ||
|
|
||
| static struct platform_driver msc313_usb_phy_driver = { | ||
| .probe = msc313_usb_phy_probe, | ||
| .driver = { | ||
| .of_match_table = msc313_usb_phy_ids, | ||
| .name = "msc313-usb-phy", | ||
| } | ||
| }; | ||
| builtin_platform_driver(msc313_usb_phy_driver); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| // SPDX-License-Identifier: GPL-2.0+ | ||
| /* USBC | ||
| * | ||
| * otg - musb controller | ||
| * uhc - ehci host controller | ||
| * | ||
| * 0x00 - reset control | ||
| * reset value msc313 0x228 | ||
| * ssc8336 0x200 | ||
| * | 9 | 8 | ||
| * |vbusvalid| vbus_sel | ||
| * 6 | 5 | 3 | 2 | 1 | 0 | ||
| * otg xiu | uhc xiu | reg suspend | otg_rst | uhc_rst | usb_rst | ||
| * | ||
| * 0x04 - port control | ||
| * 4 | 3 | 1 | 0 | ||
| * idpullup_ctrl | interrupt polarity | portctrl_otg | portctrl_uhc | ||
| * | ||
| * 0x08 - interrupt enable | ||
| * 3 | 2 | 1 | 0 | ||
| * id | bval | aval | vbus | ||
| * | ||
| * 0x0c - interrupt status | ||
| * - order same as above | ||
| * | ||
| * 0x10 - utmi signal | ||
| * 15 - 13 | 12 | 11 | 10 | 9 | 8 | ||
| * | rxactive | rxvalid | rxvalidh | rxerror | txready | ||
| * 7 - 6 | | | | | 0 | ||
| * dm/dp | | | bvalid | avalid | vbusvalid | ||
| * | ||
| * 0x14 - power enable | ||
| * 0x18 - power status | ||
| * | ||
| * MIU select? | ||
| * 0x28 - miu sel 0 - set to 0x00 by the vendor code | ||
| * 0x2c - miu sel 1 - set to 0xff by the vendor code | ||
| * 0x2d - miu sel 2 - set to 0xff by the vendor code | ||
| * 0x30 - miu sel 3 - set to 0xff by the vendor code | ||
| * 0x31 | ||
| * 0 | ||
| * miu partition - set to 1 by the vendor code | ||
| * | ||
| * 0x101 - vendor sdk says setting bits 0,1,2,3 and 7 here "improve the efficiency of usb access miu when system is busy" | ||
| */ | ||
|
|
||
| #define MSTAR_USBC_REG_RSTCTRL 0x00 | ||
| #define MSTAR_USBC_REG_PRTCTRL 0x04 | ||
| #define MSTAR_USBC_REG_INTEN 0x08 | ||
| #define MSTAR_USBC_REG_INTSTS 0x0c | ||
|
|
||
| #define MSTAR_USBC_REG_MIUCFG0 0x28 // miu sel 0 | ||
| #define MSTAR_USBC_REG_MIUCFG1 0x2c // miu sel 1 + 2 | ||
| #define MSTAR_USBC_REG_MIUCFG2 0x30 // miu sel 3 + miu partition | ||
|
|
||
| #define MSTAR_RSTCTRL_USB_RST (1 << 0) | ||
| #define MSTAR_RSTCTRL_UHC_RST (1 << 1) | ||
| #define MSTAR_RSTCTRL_OTG_RST (1 << 2) | ||
| #define MSTAR_RSTCTRL_REG_SUSPEND (1 << 3) | ||
| #define MSTAR_RSTCTRL_UHC_XIU (1 << 5) | ||
| #define MSTAR_RSTCTRL_OTG_XIU (1 << 6) | ||
|
|
||
| #define MSTAR_RSTCTRL_VBUSVALID 9 | ||
|
|
||
| #define MSTAR_PRTCTRL_UHC (1 << 0) | ||
| #define MSTAR_PRTCTRL_OTG (1 << 1) | ||
|
|
||
| #define MSTAR_USBC_INT_VBUS 0 | ||
| #define MSTAR_USBC_INT_AVAL 1 | ||
| #define MSTAR_USBC_INT_BVAL 2 | ||
| #define MSTAR_USBC_INT_ID 3 | ||
| #define MSTAR_USBC_INT_MASK (BIT(MSTAR_USBC_INT_VBUS)|BIT(MSTAR_USBC_INT_AVAL)|BIT(MSTAR_USBC_INT_BVAL)|BIT(MSTAR_USBC_INT_ID)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| // SPDX-License-Identifier: GPL-2.0+ | ||
|
|
||
| /* | ||
| * utmi registers | ||
| * | ||
| * 0x0 - pwrctrl | ||
| * reset value msc313 - 0x7f03 | ||
| * ssc8336 - 0xff05 | ||
| * | ||
| * 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | ||
| * pdn | iref_pdn | vbusdet_pdn | fl_xcvr_pdn | hs_preamp_pdn | hs_ted_pdn | upll_pdn | hs_dm_pdn | ||
| * 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ||
| * r_dm_pden | r_dp_pden | r_pumode | dm_puen | dp_puen | ref power down | term override mode | pdn override mode | ||
| * | ||
| * | ||
| * 0x4 - config | ||
| * reset value msc313 - 0x2084 | ||
| * ssc8336 - 0x9080 | ||
| * | ||
| * magic value ssc8336 - 0x2084 - from dashcam app | ||
| * | ||
| * 3 | 2 | 1 | 0 | ||
| * override value to control NRZI mode | clk12_sel | fsls_sel | sel_ovr | ||
| * | 0 - 12mhz | | | ||
| * | 1 - "from digital synthesizer" | | | ||
| * | ||
| * 0x10 - clkctrl | ||
| * | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | ||
| * | | | | clk214_syn_en | force_pll_on | clk_ctl_override | xtal12_en | all_pass | ||
| * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ||
| * | pd_bg_current | clktest_en | | | ? | ? | utmi_clk120_en | utmi_clk_en | ||
| * | ||
| * | ||
| * 0x14 - clkinv | ||
| * | ||
| * 13 | | ||
| * dp/dn swap | | ||
| * | ||
| * | ||
| * 0x40 - PLL_TEST[15:0] | ||
| * 0x44 - PLL_TEST[31:16] | ||
| * 0x48 - PLL_TEST[39:32] | ||
| * | ||
| * From SSC8336 | ||
| * 0x2088 0x8051 | ||
| * | ||
| * 0x58 - utmi eye setting 2c | ||
| * 0x59 - utmi eye setting 2d | ||
| * 0x5c - utmi eye setting 2e | ||
| * 0x5d - utmi eye setting 2f | ||
| * | ||
| * 0x60 - | ||
| * | ||
| * 0x78 - calibration | ||
| * 15 - 4 | 3 | 2 | 1 | 0 | ||
| * calibration detail data | | power good | ca_end | ca_start | ||
| * | ||
| * 0x1c | ||
| * | ||
| */ | ||
|
|
||
| #define REG_PWRCTRL 0x0 | ||
| #define MSTAR_UTMI_REG_CONFIG 0x4 | ||
| #define REG_CLKCTRL 0x10 | ||
| #define MSTAR_UTMI_REG_CLKINV 0x14 | ||
| #define MSTAR_UTMI_REG_CLKINV_DPDNSWP BIT(13) | ||
| #define PWRCTRL_REG_PDN 15 | ||
| #define PWRCTRL_UPLL_PDN 9 | ||
| #define PWRCTRL_VREF_PDN 2 | ||
| #define MSTAR_UTMI_REG_PLL_TEST0 0x40 | ||
| #define MSTAR_UTMI_REG_PLL_TEST1 0x44 | ||
| #define MSTAR_UTMI_REG_PLL_TEST2 0x48 | ||
| #define REG_EYESETTING1 0x58 | ||
| #define REG_EYESETTING2 0x5c | ||
| #define MSTAR_UTMI_REG_CAL 0x78 | ||
| #define MSTAR_UTMI_REG_CAL_START BIT(0) | ||
| #define MSTAR_UTMI_REG_CAL_END BIT(1) | ||
| #define MSTAR_UTMI_REG_CAL_DATA_SHIFT 4 | ||
|
|
||
| static struct reg_field mstar_utmi_pwrctrl_pwrdwn_field = REG_FIELD(REG_PWRCTRL, PWRCTRL_REG_PDN, PWRCTRL_REG_PDN); |