Skip to content
Permalink
Browse files
ARM:mstar: usb phy
  • Loading branch information
fifteenhex committed Jul 19, 2021
1 parent 6d6f48b commit 9ee3ebbbe6937e1aa664962e61484a9dc6ed2523
Show file tree
Hide file tree
Showing 4 changed files with 368 additions and 0 deletions.
@@ -10,6 +10,7 @@ obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o
obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
obj-$(CONFIG_USB_LGM_PHY) += phy-lgm-usb.o
obj-$(CONFIG_ARCH_MSTARV7) += phy-msc313-usb.o
obj-y += allwinner/ \
amlogic/ \
broadcom/ \
@@ -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);
@@ -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))
@@ -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);

0 comments on commit 9ee3ebb

Please sign in to comment.