Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: host: ohci-at91: add support to enter suspend using SMC

When Linux is running under OP-TEE, the SFR is set as secured and thus
the AT91_OHCIICR_USB_SUSPEND register isn't accessible. Add a SMC to
do the appropriate call to suspend the controller.
The SMC id is fetched from the device-tree property
"microchip,suspend-smc-id". if present, then the syscon regmap is not
used to enter suspend and a SMC is issued.

Reviewed-by: Claudiu Beznea <claudiu.beznea@microchip.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Clément Léger <clement.leger@bootlin.com>
Link: https://lore.kernel.org/r/20220607133454.727063-1-clement.leger@bootlin.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Clément Léger and committed by
Greg Kroah-Hartman
1e073e3e 302970b4

+46 -23
+46 -23
drivers/usb/host/ohci-at91.c
··· 13 13 * This file is licenced under the GPL. 14 14 */ 15 15 16 + #include <linux/arm-smccc.h> 16 17 #include <linux/clk.h> 17 18 #include <linux/dma-mapping.h> 18 19 #include <linux/gpio/consumer.h> ··· 56 55 bool clocked; 57 56 bool wakeup; /* Saved wake-up state for resume */ 58 57 struct regmap *sfr_regmap; 58 + u32 suspend_smc_id; 59 59 }; 60 60 /* interface and function clocks; sometimes also an AHB clock */ 61 61 ··· 136 134 /*-------------------------------------------------------------------------*/ 137 135 138 136 static void usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *); 137 + 138 + static u32 at91_dt_suspend_smc(struct device *dev) 139 + { 140 + u32 suspend_smc_id; 141 + 142 + if (!dev->of_node) 143 + return 0; 144 + 145 + if (of_property_read_u32(dev->of_node, "microchip,suspend-smc-id", &suspend_smc_id)) 146 + return 0; 147 + 148 + return suspend_smc_id; 149 + } 139 150 140 151 static struct regmap *at91_dt_syscon_sfr(void) 141 152 { ··· 230 215 goto err; 231 216 } 232 217 233 - ohci_at91->sfr_regmap = at91_dt_syscon_sfr(); 234 - if (!ohci_at91->sfr_regmap) 235 - dev_dbg(dev, "failed to find sfr node\n"); 218 + ohci_at91->suspend_smc_id = at91_dt_suspend_smc(dev); 219 + if (!ohci_at91->suspend_smc_id) { 220 + dev_dbg(dev, "failed to find sfr suspend smc id, using regmap\n"); 221 + ohci_at91->sfr_regmap = at91_dt_syscon_sfr(); 222 + if (!ohci_at91->sfr_regmap) 223 + dev_dbg(dev, "failed to find sfr node\n"); 224 + } 236 225 237 226 board = hcd->self.controller->platform_data; 238 227 ohci = hcd_to_ohci(hcd); ··· 322 303 return length; 323 304 } 324 305 325 - static int ohci_at91_port_suspend(struct regmap *regmap, u8 set) 306 + static int ohci_at91_port_suspend(struct ohci_at91_priv *ohci_at91, u8 set) 326 307 { 308 + struct regmap *regmap = ohci_at91->sfr_regmap; 327 309 u32 regval; 328 310 int ret; 329 311 330 - if (!regmap) 331 - return 0; 312 + if (ohci_at91->suspend_smc_id) { 313 + struct arm_smccc_res res; 332 314 333 - ret = regmap_read(regmap, AT91_SFR_OHCIICR, &regval); 334 - if (ret) 335 - return ret; 315 + arm_smccc_smc(ohci_at91->suspend_smc_id, set, 0, 0, 0, 0, 0, 0, &res); 316 + if (res.a0) 317 + return -EINVAL; 318 + } else if (regmap) { 319 + ret = regmap_read(regmap, AT91_SFR_OHCIICR, &regval); 320 + if (ret) 321 + return ret; 336 322 337 - if (set) 338 - regval |= AT91_OHCIICR_USB_SUSPEND; 339 - else 340 - regval &= ~AT91_OHCIICR_USB_SUSPEND; 323 + if (set) 324 + regval |= AT91_OHCIICR_USB_SUSPEND; 325 + else 326 + regval &= ~AT91_OHCIICR_USB_SUSPEND; 341 327 342 - regmap_write(regmap, AT91_SFR_OHCIICR, regval); 328 + regmap_write(regmap, AT91_SFR_OHCIICR, regval); 329 + } 343 330 344 331 return 0; 345 332 } ··· 382 357 383 358 case USB_PORT_FEAT_SUSPEND: 384 359 dev_dbg(hcd->self.controller, "SetPortFeat: SUSPEND\n"); 385 - if (valid_port(wIndex) && ohci_at91->sfr_regmap) { 386 - ohci_at91_port_suspend(ohci_at91->sfr_regmap, 387 - 1); 360 + if (valid_port(wIndex)) { 361 + ohci_at91_port_suspend(ohci_at91, 1); 388 362 return 0; 389 363 } 390 364 break; ··· 424 400 425 401 case USB_PORT_FEAT_SUSPEND: 426 402 dev_dbg(hcd->self.controller, "ClearPortFeature: SUSPEND\n"); 427 - if (valid_port(wIndex) && ohci_at91->sfr_regmap) { 428 - ohci_at91_port_suspend(ohci_at91->sfr_regmap, 429 - 0); 403 + if (valid_port(wIndex)) { 404 + ohci_at91_port_suspend(ohci_at91, 0); 430 405 return 0; 431 406 } 432 407 break; ··· 653 630 /* flush the writes */ 654 631 (void) ohci_readl (ohci, &ohci->regs->control); 655 632 msleep(1); 656 - ohci_at91_port_suspend(ohci_at91->sfr_regmap, 1); 633 + ohci_at91_port_suspend(ohci_at91, 1); 657 634 at91_stop_clock(ohci_at91); 658 635 } else { 659 - ohci_at91_port_suspend(ohci_at91->sfr_regmap, 1); 636 + ohci_at91_port_suspend(ohci_at91, 1); 660 637 } 661 638 662 639 return ret; ··· 668 645 struct usb_hcd *hcd = dev_get_drvdata(dev); 669 646 struct ohci_at91_priv *ohci_at91 = hcd_to_ohci_at91_priv(hcd); 670 647 671 - ohci_at91_port_suspend(ohci_at91->sfr_regmap, 0); 648 + ohci_at91_port_suspend(ohci_at91, 0); 672 649 673 650 if (ohci_at91->wakeup) 674 651 disable_irq_wake(hcd->irq);