0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017 #include <linux/clk.h>
0018 #include <linux/io.h>
0019 #include <linux/module.h>
0020 #include <linux/phy/phy.h>
0021 #include <linux/platform_device.h>
0022 #include <linux/reset.h>
0023
0024
0025 enum usbh_regs {
0026 USBH_BRT_CONTROL1 = 0,
0027 USBH_BRT_CONTROL2,
0028 USBH_BRT_STATUS1,
0029 USBH_BRT_STATUS2,
0030 USBH_UTMI_CONTROL1,
0031 #define USBH_UC1_DEV_MODE_SEL BIT(0)
0032 USBH_TEST_PORT_CONTROL,
0033 USBH_PLL_CONTROL1,
0034 #define USBH_PLLC_REFCLKSEL_SHIFT 0
0035 #define USBH_PLLC_REFCLKSEL_MASK (0x3 << USBH_PLLC_REFCLKSEL_SHIFT)
0036 #define USBH_PLLC_CLKSEL_SHIFT 2
0037 #define USBH_PLLC_CLKSEL_MASK (0x3 << USBH_PLLC_CLKSEL_MASK)
0038 #define USBH_PLLC_XTAL_PWRDWNB BIT(4)
0039 #define USBH_PLLC_PLL_PWRDWNB BIT(5)
0040 #define USBH_PLLC_PLL_CALEN BIT(6)
0041 #define USBH_PLLC_PHYPLL_BYP BIT(7)
0042 #define USBH_PLLC_PLL_RESET BIT(8)
0043 #define USBH_PLLC_PLL_IDDQ_PWRDN BIT(9)
0044 #define USBH_PLLC_PLL_PWRDN_DELAY BIT(10)
0045 #define USBH_6318_PLLC_PLL_SUSPEND_EN BIT(27)
0046 #define USBH_6318_PLLC_PHYPLL_BYP BIT(29)
0047 #define USBH_6318_PLLC_PLL_RESET BIT(30)
0048 #define USBH_6318_PLLC_PLL_IDDQ_PWRDN BIT(31)
0049 USBH_SWAP_CONTROL,
0050 #define USBH_SC_OHCI_DATA_SWAP BIT(0)
0051 #define USBH_SC_OHCI_ENDIAN_SWAP BIT(1)
0052 #define USBH_SC_OHCI_LOGICAL_ADDR_EN BIT(2)
0053 #define USBH_SC_EHCI_DATA_SWAP BIT(3)
0054 #define USBH_SC_EHCI_ENDIAN_SWAP BIT(4)
0055 #define USBH_SC_EHCI_LOGICAL_ADDR_EN BIT(5)
0056 #define USBH_SC_USB_DEVICE_SEL BIT(6)
0057 USBH_GENERIC_CONTROL,
0058 #define USBH_GC_PLL_SUSPEND_EN BIT(1)
0059 USBH_FRAME_ADJUST_VALUE,
0060 USBH_SETUP,
0061 #define USBH_S_IOC BIT(4)
0062 #define USBH_S_IPP BIT(5)
0063 USBH_MDIO,
0064 USBH_MDIO32,
0065 USBH_USB_SIM_CONTROL,
0066 #define USBH_USC_LADDR_SEL BIT(5)
0067
0068 __USBH_ENUM_SIZE
0069 };
0070
0071 struct bcm63xx_usbh_phy_variant {
0072
0073 long regs[__USBH_ENUM_SIZE];
0074
0075
0076 u32 power_pllc_clr;
0077 u32 power_pllc_set;
0078
0079
0080 u32 setup_clr;
0081 u32 setup_set;
0082
0083
0084 u32 swapctl_dev_set;
0085
0086
0087 u32 tpc_val;
0088
0089
0090 u32 usc_set;
0091
0092
0093 u32 utmictl1_dev_set;
0094 };
0095
0096 struct bcm63xx_usbh_phy {
0097 void __iomem *base;
0098 struct clk *usbh_clk;
0099 struct clk *usb_ref_clk;
0100 struct reset_control *reset;
0101 const struct bcm63xx_usbh_phy_variant *variant;
0102 bool device_mode;
0103 };
0104
0105 static const struct bcm63xx_usbh_phy_variant usbh_bcm6318 = {
0106 .regs = {
0107 [USBH_BRT_CONTROL1] = -1,
0108 [USBH_BRT_CONTROL2] = -1,
0109 [USBH_BRT_STATUS1] = -1,
0110 [USBH_BRT_STATUS2] = -1,
0111 [USBH_UTMI_CONTROL1] = 0x2c,
0112 [USBH_TEST_PORT_CONTROL] = 0x1c,
0113 [USBH_PLL_CONTROL1] = 0x04,
0114 [USBH_SWAP_CONTROL] = 0x0c,
0115 [USBH_GENERIC_CONTROL] = -1,
0116 [USBH_FRAME_ADJUST_VALUE] = 0x08,
0117 [USBH_SETUP] = 0x00,
0118 [USBH_MDIO] = 0x14,
0119 [USBH_MDIO32] = 0x18,
0120 [USBH_USB_SIM_CONTROL] = 0x20,
0121 },
0122 .power_pllc_clr = USBH_6318_PLLC_PLL_IDDQ_PWRDN,
0123 .power_pllc_set = USBH_6318_PLLC_PLL_SUSPEND_EN,
0124 .setup_set = USBH_S_IOC,
0125 .swapctl_dev_set = USBH_SC_USB_DEVICE_SEL,
0126 .usc_set = USBH_USC_LADDR_SEL,
0127 .utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL,
0128 };
0129
0130 static const struct bcm63xx_usbh_phy_variant usbh_bcm6328 = {
0131 .regs = {
0132 [USBH_BRT_CONTROL1] = 0x00,
0133 [USBH_BRT_CONTROL2] = 0x04,
0134 [USBH_BRT_STATUS1] = 0x08,
0135 [USBH_BRT_STATUS2] = 0x0c,
0136 [USBH_UTMI_CONTROL1] = 0x10,
0137 [USBH_TEST_PORT_CONTROL] = 0x14,
0138 [USBH_PLL_CONTROL1] = 0x18,
0139 [USBH_SWAP_CONTROL] = 0x1c,
0140 [USBH_GENERIC_CONTROL] = 0x20,
0141 [USBH_FRAME_ADJUST_VALUE] = 0x24,
0142 [USBH_SETUP] = 0x28,
0143 [USBH_MDIO] = 0x2c,
0144 [USBH_MDIO32] = 0x30,
0145 [USBH_USB_SIM_CONTROL] = 0x34,
0146 },
0147 .setup_set = USBH_S_IOC,
0148 .swapctl_dev_set = USBH_SC_USB_DEVICE_SEL,
0149 .utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL,
0150 };
0151
0152 static const struct bcm63xx_usbh_phy_variant usbh_bcm6358 = {
0153 .regs = {
0154 [USBH_BRT_CONTROL1] = -1,
0155 [USBH_BRT_CONTROL2] = -1,
0156 [USBH_BRT_STATUS1] = -1,
0157 [USBH_BRT_STATUS2] = -1,
0158 [USBH_UTMI_CONTROL1] = -1,
0159 [USBH_TEST_PORT_CONTROL] = 0x24,
0160 [USBH_PLL_CONTROL1] = -1,
0161 [USBH_SWAP_CONTROL] = 0x00,
0162 [USBH_GENERIC_CONTROL] = -1,
0163 [USBH_FRAME_ADJUST_VALUE] = -1,
0164 [USBH_SETUP] = -1,
0165 [USBH_MDIO] = -1,
0166 [USBH_MDIO32] = -1,
0167 [USBH_USB_SIM_CONTROL] = -1,
0168 },
0169
0170
0171
0172
0173
0174 .tpc_val = 0x1c0020,
0175 };
0176
0177 static const struct bcm63xx_usbh_phy_variant usbh_bcm6368 = {
0178 .regs = {
0179 [USBH_BRT_CONTROL1] = 0x00,
0180 [USBH_BRT_CONTROL2] = 0x04,
0181 [USBH_BRT_STATUS1] = 0x08,
0182 [USBH_BRT_STATUS2] = 0x0c,
0183 [USBH_UTMI_CONTROL1] = 0x10,
0184 [USBH_TEST_PORT_CONTROL] = 0x14,
0185 [USBH_PLL_CONTROL1] = 0x18,
0186 [USBH_SWAP_CONTROL] = 0x1c,
0187 [USBH_GENERIC_CONTROL] = -1,
0188 [USBH_FRAME_ADJUST_VALUE] = 0x24,
0189 [USBH_SETUP] = 0x28,
0190 [USBH_MDIO] = 0x2c,
0191 [USBH_MDIO32] = 0x30,
0192 [USBH_USB_SIM_CONTROL] = 0x34,
0193 },
0194 .power_pllc_clr = USBH_PLLC_PLL_IDDQ_PWRDN | USBH_PLLC_PLL_PWRDN_DELAY,
0195 .setup_set = USBH_S_IOC,
0196 .swapctl_dev_set = USBH_SC_USB_DEVICE_SEL,
0197 .utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL,
0198 };
0199
0200 static const struct bcm63xx_usbh_phy_variant usbh_bcm63268 = {
0201 .regs = {
0202 [USBH_BRT_CONTROL1] = 0x00,
0203 [USBH_BRT_CONTROL2] = 0x04,
0204 [USBH_BRT_STATUS1] = 0x08,
0205 [USBH_BRT_STATUS2] = 0x0c,
0206 [USBH_UTMI_CONTROL1] = 0x10,
0207 [USBH_TEST_PORT_CONTROL] = 0x14,
0208 [USBH_PLL_CONTROL1] = 0x18,
0209 [USBH_SWAP_CONTROL] = 0x1c,
0210 [USBH_GENERIC_CONTROL] = 0x20,
0211 [USBH_FRAME_ADJUST_VALUE] = 0x24,
0212 [USBH_SETUP] = 0x28,
0213 [USBH_MDIO] = 0x2c,
0214 [USBH_MDIO32] = 0x30,
0215 [USBH_USB_SIM_CONTROL] = 0x34,
0216 },
0217 .power_pllc_clr = USBH_PLLC_PLL_IDDQ_PWRDN | USBH_PLLC_PLL_PWRDN_DELAY,
0218 .setup_clr = USBH_S_IPP,
0219 .setup_set = USBH_S_IOC,
0220 .swapctl_dev_set = USBH_SC_USB_DEVICE_SEL,
0221 .utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL,
0222 };
0223
0224 static inline bool usbh_has_reg(struct bcm63xx_usbh_phy *usbh, int reg)
0225 {
0226 return (usbh->variant->regs[reg] >= 0);
0227 }
0228
0229 static inline u32 usbh_readl(struct bcm63xx_usbh_phy *usbh, int reg)
0230 {
0231 return __raw_readl(usbh->base + usbh->variant->regs[reg]);
0232 }
0233
0234 static inline void usbh_writel(struct bcm63xx_usbh_phy *usbh, int reg,
0235 u32 value)
0236 {
0237 __raw_writel(value, usbh->base + usbh->variant->regs[reg]);
0238 }
0239
0240 static int bcm63xx_usbh_phy_init(struct phy *phy)
0241 {
0242 struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy);
0243 int ret;
0244
0245 ret = clk_prepare_enable(usbh->usbh_clk);
0246 if (ret) {
0247 dev_err(&phy->dev, "unable to enable usbh clock: %d\n", ret);
0248 return ret;
0249 }
0250
0251 ret = clk_prepare_enable(usbh->usb_ref_clk);
0252 if (ret) {
0253 dev_err(&phy->dev, "unable to enable usb_ref clock: %d\n", ret);
0254 clk_disable_unprepare(usbh->usbh_clk);
0255 return ret;
0256 }
0257
0258 ret = reset_control_reset(usbh->reset);
0259 if (ret) {
0260 dev_err(&phy->dev, "unable to reset device: %d\n", ret);
0261 clk_disable_unprepare(usbh->usb_ref_clk);
0262 clk_disable_unprepare(usbh->usbh_clk);
0263 return ret;
0264 }
0265
0266
0267 if (usbh_has_reg(usbh, USBH_SWAP_CONTROL)) {
0268 u32 val = usbh_readl(usbh, USBH_SWAP_CONTROL);
0269
0270 val |= USBH_SC_EHCI_DATA_SWAP;
0271 val &= ~USBH_SC_EHCI_ENDIAN_SWAP;
0272
0273 val |= USBH_SC_OHCI_DATA_SWAP;
0274 val &= ~USBH_SC_OHCI_ENDIAN_SWAP;
0275
0276 if (usbh->device_mode && usbh->variant->swapctl_dev_set)
0277 val |= usbh->variant->swapctl_dev_set;
0278
0279 usbh_writel(usbh, USBH_SWAP_CONTROL, val);
0280 }
0281
0282 if (usbh_has_reg(usbh, USBH_SETUP)) {
0283 u32 val = usbh_readl(usbh, USBH_SETUP);
0284
0285 val |= usbh->variant->setup_set;
0286 val &= ~usbh->variant->setup_clr;
0287
0288 usbh_writel(usbh, USBH_SETUP, val);
0289 }
0290
0291 if (usbh_has_reg(usbh, USBH_USB_SIM_CONTROL)) {
0292 u32 val = usbh_readl(usbh, USBH_USB_SIM_CONTROL);
0293
0294 val |= usbh->variant->usc_set;
0295
0296 usbh_writel(usbh, USBH_USB_SIM_CONTROL, val);
0297 }
0298
0299 if (usbh->variant->tpc_val &&
0300 usbh_has_reg(usbh, USBH_TEST_PORT_CONTROL))
0301 usbh_writel(usbh, USBH_TEST_PORT_CONTROL,
0302 usbh->variant->tpc_val);
0303
0304 if (usbh->device_mode &&
0305 usbh_has_reg(usbh, USBH_UTMI_CONTROL1) &&
0306 usbh->variant->utmictl1_dev_set) {
0307 u32 val = usbh_readl(usbh, USBH_UTMI_CONTROL1);
0308
0309 val |= usbh->variant->utmictl1_dev_set;
0310
0311 usbh_writel(usbh, USBH_UTMI_CONTROL1, val);
0312 }
0313
0314 return 0;
0315 }
0316
0317 static int bcm63xx_usbh_phy_power_on(struct phy *phy)
0318 {
0319 struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy);
0320
0321 if (usbh_has_reg(usbh, USBH_PLL_CONTROL1)) {
0322 u32 val = usbh_readl(usbh, USBH_PLL_CONTROL1);
0323
0324 val |= usbh->variant->power_pllc_set;
0325 val &= ~usbh->variant->power_pllc_clr;
0326
0327 usbh_writel(usbh, USBH_PLL_CONTROL1, val);
0328 }
0329
0330 return 0;
0331 }
0332
0333 static int bcm63xx_usbh_phy_power_off(struct phy *phy)
0334 {
0335 struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy);
0336
0337 if (usbh_has_reg(usbh, USBH_PLL_CONTROL1)) {
0338 u32 val = usbh_readl(usbh, USBH_PLL_CONTROL1);
0339
0340 val &= ~usbh->variant->power_pllc_set;
0341 val |= usbh->variant->power_pllc_clr;
0342
0343 usbh_writel(usbh, USBH_PLL_CONTROL1, val);
0344 }
0345
0346 return 0;
0347 }
0348
0349 static int bcm63xx_usbh_phy_exit(struct phy *phy)
0350 {
0351 struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy);
0352
0353 clk_disable_unprepare(usbh->usbh_clk);
0354 clk_disable_unprepare(usbh->usb_ref_clk);
0355
0356 return 0;
0357 }
0358
0359 static const struct phy_ops bcm63xx_usbh_phy_ops = {
0360 .exit = bcm63xx_usbh_phy_exit,
0361 .init = bcm63xx_usbh_phy_init,
0362 .power_off = bcm63xx_usbh_phy_power_off,
0363 .power_on = bcm63xx_usbh_phy_power_on,
0364 .owner = THIS_MODULE,
0365 };
0366
0367 static struct phy *bcm63xx_usbh_phy_xlate(struct device *dev,
0368 struct of_phandle_args *args)
0369 {
0370 struct bcm63xx_usbh_phy *usbh = dev_get_drvdata(dev);
0371
0372 usbh->device_mode = !!args->args[0];
0373
0374 return of_phy_simple_xlate(dev, args);
0375 }
0376
0377 static int __init bcm63xx_usbh_phy_probe(struct platform_device *pdev)
0378 {
0379 struct device *dev = &pdev->dev;
0380 struct bcm63xx_usbh_phy *usbh;
0381 const struct bcm63xx_usbh_phy_variant *variant;
0382 struct phy *phy;
0383 struct phy_provider *phy_provider;
0384
0385 usbh = devm_kzalloc(dev, sizeof(*usbh), GFP_KERNEL);
0386 if (!usbh)
0387 return -ENOMEM;
0388
0389 variant = device_get_match_data(dev);
0390 if (!variant)
0391 return -EINVAL;
0392 usbh->variant = variant;
0393
0394 usbh->base = devm_platform_ioremap_resource(pdev, 0);
0395 if (IS_ERR(usbh->base))
0396 return PTR_ERR(usbh->base);
0397
0398 usbh->reset = devm_reset_control_get_exclusive(dev, NULL);
0399 if (IS_ERR(usbh->reset)) {
0400 if (PTR_ERR(usbh->reset) != -EPROBE_DEFER)
0401 dev_err(dev, "failed to get reset\n");
0402 return PTR_ERR(usbh->reset);
0403 }
0404
0405 usbh->usbh_clk = devm_clk_get_optional(dev, "usbh");
0406 if (IS_ERR(usbh->usbh_clk))
0407 return PTR_ERR(usbh->usbh_clk);
0408
0409 usbh->usb_ref_clk = devm_clk_get_optional(dev, "usb_ref");
0410 if (IS_ERR(usbh->usb_ref_clk))
0411 return PTR_ERR(usbh->usb_ref_clk);
0412
0413 phy = devm_phy_create(dev, NULL, &bcm63xx_usbh_phy_ops);
0414 if (IS_ERR(phy)) {
0415 dev_err(dev, "failed to create PHY\n");
0416 return PTR_ERR(phy);
0417 }
0418
0419 platform_set_drvdata(pdev, usbh);
0420 phy_set_drvdata(phy, usbh);
0421
0422 phy_provider = devm_of_phy_provider_register(dev,
0423 bcm63xx_usbh_phy_xlate);
0424 if (IS_ERR(phy_provider)) {
0425 dev_err(dev, "failed to register PHY provider\n");
0426 return PTR_ERR(phy_provider);
0427 }
0428
0429 dev_dbg(dev, "Registered BCM63xx USB PHY driver\n");
0430
0431 return 0;
0432 }
0433
0434 static const struct of_device_id bcm63xx_usbh_phy_ids[] __initconst = {
0435 { .compatible = "brcm,bcm6318-usbh-phy", .data = &usbh_bcm6318 },
0436 { .compatible = "brcm,bcm6328-usbh-phy", .data = &usbh_bcm6328 },
0437 { .compatible = "brcm,bcm6358-usbh-phy", .data = &usbh_bcm6358 },
0438 { .compatible = "brcm,bcm6362-usbh-phy", .data = &usbh_bcm6368 },
0439 { .compatible = "brcm,bcm6368-usbh-phy", .data = &usbh_bcm6368 },
0440 { .compatible = "brcm,bcm63268-usbh-phy", .data = &usbh_bcm63268 },
0441 { }
0442 };
0443 MODULE_DEVICE_TABLE(of, bcm63xx_usbh_phy_ids);
0444
0445 static struct platform_driver bcm63xx_usbh_phy_driver __refdata = {
0446 .driver = {
0447 .name = "bcm63xx-usbh-phy",
0448 .of_match_table = bcm63xx_usbh_phy_ids,
0449 },
0450 .probe = bcm63xx_usbh_phy_probe,
0451 };
0452 module_platform_driver(bcm63xx_usbh_phy_driver);
0453
0454 MODULE_DESCRIPTION("BCM63xx USBH PHY driver");
0455 MODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>");
0456 MODULE_AUTHOR("Simon Arlott");
0457 MODULE_LICENSE("GPL");