1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Driver for Synopsys DesignWare Cores Mobile Storage Host Controller 4 * 5 * Copyright (C) 2018 Synaptics Incorporated 6 * 7 * Author: Jisheng Zhang <jszhang@kernel.org> 8 */ 9 10 #include <linux/acpi.h> 11 #include <linux/clk.h> 12 #include <linux/dma-mapping.h> 13 #include <linux/iopoll.h> 14 #include <linux/kernel.h> 15 #include <linux/module.h> 16 #include <linux/of.h> 17 #include <linux/of_device.h> 18 #include <linux/reset.h> 19 #include <linux/sizes.h> 20 21 #include "sdhci-pltfm.h" 22 23 #define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16) 24 25 /* DWCMSHC specific Mode Select value */ 26 #define DWCMSHC_CTRL_HS400 0x7 27 28 /* DWC IP vendor area 1 pointer */ 29 #define DWCMSHC_P_VENDOR_AREA1 0xe8 30 #define DWCMSHC_AREA1_MASK GENMASK(11, 0) 31 /* Offset inside the vendor area 1 */ 32 #define DWCMSHC_HOST_CTRL3 0x8 33 #define DWCMSHC_EMMC_CONTROL 0x2c 34 #define DWCMSHC_ENHANCED_STROBE BIT(8) 35 #define DWCMSHC_EMMC_ATCTRL 0x40 36 37 /* Rockchip specific Registers */ 38 #define DWCMSHC_EMMC_DLL_CTRL 0x800 39 #define DWCMSHC_EMMC_DLL_RXCLK 0x804 40 #define DWCMSHC_EMMC_DLL_TXCLK 0x808 41 #define DWCMSHC_EMMC_DLL_STRBIN 0x80c 42 #define DLL_STRBIN_TAPNUM_FROM_SW BIT(24) 43 #define DWCMSHC_EMMC_DLL_STATUS0 0x840 44 #define DWCMSHC_EMMC_DLL_START BIT(0) 45 #define DWCMSHC_EMMC_DLL_LOCKED BIT(8) 46 #define DWCMSHC_EMMC_DLL_TIMEOUT BIT(9) 47 #define DWCMSHC_EMMC_DLL_RXCLK_SRCSEL 29 48 #define DWCMSHC_EMMC_DLL_START_POINT 16 49 #define DWCMSHC_EMMC_DLL_INC 8 50 #define DWCMSHC_EMMC_DLL_DLYENA BIT(27) 51 #define DLL_TXCLK_TAPNUM_DEFAULT 0x8 52 #define DLL_STRBIN_TAPNUM_DEFAULT 0x8 53 #define DLL_TXCLK_TAPNUM_FROM_SW BIT(24) 54 #define DLL_RXCLK_NO_INVERTER 1 55 #define DLL_RXCLK_INVERTER 0 56 #define DLL_LOCK_WO_TMOUT(x) \ 57 ((((x) & DWCMSHC_EMMC_DLL_LOCKED) == DWCMSHC_EMMC_DLL_LOCKED) && \ 58 (((x) & DWCMSHC_EMMC_DLL_TIMEOUT) == 0)) 59 #define RK35xx_MAX_CLKS 3 60 61 #define BOUNDARY_OK(addr, len) \ 62 ((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1))) 63 64 struct rk35xx_priv { 65 /* Rockchip specified optional clocks */ 66 struct clk_bulk_data rockchip_clks[RK35xx_MAX_CLKS]; 67 struct reset_control *reset; 68 u8 txclk_tapnum; 69 }; 70 71 struct dwcmshc_priv { 72 struct clk *bus_clk; 73 int vendor_specific_area1; /* P_VENDOR_SPECIFIC_AREA reg */ 74 void *priv; /* pointer to SoC private stuff */ 75 }; 76 77 /* 78 * If DMA addr spans 128MB boundary, we split the DMA transfer into two 79 * so that each DMA transfer doesn't exceed the boundary. 80 */ 81 static void dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc, 82 dma_addr_t addr, int len, unsigned int cmd) 83 { 84 int tmplen, offset; 85 86 if (likely(!len || BOUNDARY_OK(addr, len))) { 87 sdhci_adma_write_desc(host, desc, addr, len, cmd); 88 return; 89 } 90 91 offset = addr & (SZ_128M - 1); 92 tmplen = SZ_128M - offset; 93 sdhci_adma_write_desc(host, desc, addr, tmplen, cmd); 94 95 addr += tmplen; 96 len -= tmplen; 97 sdhci_adma_write_desc(host, desc, addr, len, cmd); 98 } 99 100 static unsigned int dwcmshc_get_max_clock(struct sdhci_host *host) 101 { 102 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 103 104 if (pltfm_host->clk) 105 return sdhci_pltfm_clk_get_max_clock(host); 106 else 107 return pltfm_host->clock; 108 } 109 110 static void dwcmshc_check_auto_cmd23(struct mmc_host *mmc, 111 struct mmc_request *mrq) 112 { 113 struct sdhci_host *host = mmc_priv(mmc); 114 115 /* 116 * No matter V4 is enabled or not, ARGUMENT2 register is 32-bit 117 * block count register which doesn't support stuff bits of 118 * CMD23 argument on dwcmsch host controller. 119 */ 120 if (mrq->sbc && (mrq->sbc->arg & SDHCI_DWCMSHC_ARG2_STUFF)) 121 host->flags &= ~SDHCI_AUTO_CMD23; 122 else 123 host->flags |= SDHCI_AUTO_CMD23; 124 } 125 126 static void dwcmshc_request(struct mmc_host *mmc, struct mmc_request *mrq) 127 { 128 dwcmshc_check_auto_cmd23(mmc, mrq); 129 130 sdhci_request(mmc, mrq); 131 } 132 133 static void dwcmshc_set_uhs_signaling(struct sdhci_host *host, 134 unsigned int timing) 135 { 136 u16 ctrl_2; 137 138 ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); 139 /* Select Bus Speed Mode for host */ 140 ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; 141 if ((timing == MMC_TIMING_MMC_HS200) || 142 (timing == MMC_TIMING_UHS_SDR104)) 143 ctrl_2 |= SDHCI_CTRL_UHS_SDR104; 144 else if (timing == MMC_TIMING_UHS_SDR12) 145 ctrl_2 |= SDHCI_CTRL_UHS_SDR12; 146 else if ((timing == MMC_TIMING_UHS_SDR25) || 147 (timing == MMC_TIMING_MMC_HS)) 148 ctrl_2 |= SDHCI_CTRL_UHS_SDR25; 149 else if (timing == MMC_TIMING_UHS_SDR50) 150 ctrl_2 |= SDHCI_CTRL_UHS_SDR50; 151 else if ((timing == MMC_TIMING_UHS_DDR50) || 152 (timing == MMC_TIMING_MMC_DDR52)) 153 ctrl_2 |= SDHCI_CTRL_UHS_DDR50; 154 else if (timing == MMC_TIMING_MMC_HS400) 155 ctrl_2 |= DWCMSHC_CTRL_HS400; 156 sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); 157 } 158 159 static void dwcmshc_hs400_enhanced_strobe(struct mmc_host *mmc, 160 struct mmc_ios *ios) 161 { 162 u32 vendor; 163 struct sdhci_host *host = mmc_priv(mmc); 164 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 165 struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); 166 int reg = priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL; 167 168 vendor = sdhci_readl(host, reg); 169 if (ios->enhanced_strobe) 170 vendor |= DWCMSHC_ENHANCED_STROBE; 171 else 172 vendor &= ~DWCMSHC_ENHANCED_STROBE; 173 174 sdhci_writel(host, vendor, reg); 175 } 176 177 static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock) 178 { 179 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 180 struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); 181 struct rk35xx_priv *priv = dwc_priv->priv; 182 u8 txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT; 183 u32 extra, reg; 184 int err; 185 186 host->mmc->actual_clock = 0; 187 188 /* 189 * DO NOT TOUCH THIS SETTING. RX clk inverter unit is enabled 190 * by default, but it shouldn't be enabled. We should anyway 191 * disable it before issuing any cmds. 192 */ 193 extra = DWCMSHC_EMMC_DLL_DLYENA | 194 DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL; 195 sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK); 196 197 if (clock == 0) 198 return; 199 200 /* Rockchip platform only support 375KHz for identify mode */ 201 if (clock <= 400000) 202 clock = 375000; 203 204 err = clk_set_rate(pltfm_host->clk, clock); 205 if (err) 206 dev_err(mmc_dev(host->mmc), "fail to set clock %d", clock); 207 208 sdhci_set_clock(host, clock); 209 210 /* Disable cmd conflict check */ 211 reg = dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3; 212 extra = sdhci_readl(host, reg); 213 extra &= ~BIT(0); 214 sdhci_writel(host, extra, reg); 215 216 if (clock <= 400000) { 217 /* Disable DLL to reset sample clock */ 218 sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL); 219 return; 220 } 221 222 /* Reset DLL */ 223 sdhci_writel(host, BIT(1), DWCMSHC_EMMC_DLL_CTRL); 224 udelay(1); 225 sdhci_writel(host, 0x0, DWCMSHC_EMMC_DLL_CTRL); 226 227 /* Init DLL settings */ 228 extra = 0x5 << DWCMSHC_EMMC_DLL_START_POINT | 229 0x2 << DWCMSHC_EMMC_DLL_INC | 230 DWCMSHC_EMMC_DLL_START; 231 sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_CTRL); 232 err = readl_poll_timeout(host->ioaddr + DWCMSHC_EMMC_DLL_STATUS0, 233 extra, DLL_LOCK_WO_TMOUT(extra), 1, 234 500 * USEC_PER_MSEC); 235 if (err) { 236 dev_err(mmc_dev(host->mmc), "DLL lock timeout!\n"); 237 return; 238 } 239 240 extra = 0x1 << 16 | /* tune clock stop en */ 241 0x2 << 17 | /* pre-change delay */ 242 0x3 << 19; /* post-change delay */ 243 sdhci_writel(host, extra, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); 244 245 if (host->mmc->ios.timing == MMC_TIMING_MMC_HS200 || 246 host->mmc->ios.timing == MMC_TIMING_MMC_HS400) 247 txclk_tapnum = priv->txclk_tapnum; 248 249 extra = DWCMSHC_EMMC_DLL_DLYENA | 250 DLL_TXCLK_TAPNUM_FROM_SW | 251 txclk_tapnum; 252 sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_TXCLK); 253 254 extra = DWCMSHC_EMMC_DLL_DLYENA | 255 DLL_STRBIN_TAPNUM_DEFAULT | 256 DLL_STRBIN_TAPNUM_FROM_SW; 257 sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); 258 } 259 260 static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask) 261 { 262 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 263 struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); 264 struct rk35xx_priv *priv = dwc_priv->priv; 265 266 if (mask & SDHCI_RESET_ALL && priv->reset) { 267 reset_control_assert(priv->reset); 268 udelay(1); 269 reset_control_deassert(priv->reset); 270 } 271 272 sdhci_reset(host, mask); 273 } 274 275 static const struct sdhci_ops sdhci_dwcmshc_ops = { 276 .set_clock = sdhci_set_clock, 277 .set_bus_width = sdhci_set_bus_width, 278 .set_uhs_signaling = dwcmshc_set_uhs_signaling, 279 .get_max_clock = dwcmshc_get_max_clock, 280 .reset = sdhci_reset, 281 .adma_write_desc = dwcmshc_adma_write_desc, 282 }; 283 284 static const struct sdhci_ops sdhci_dwcmshc_rk35xx_ops = { 285 .set_clock = dwcmshc_rk3568_set_clock, 286 .set_bus_width = sdhci_set_bus_width, 287 .set_uhs_signaling = dwcmshc_set_uhs_signaling, 288 .get_max_clock = sdhci_pltfm_clk_get_max_clock, 289 .reset = rk35xx_sdhci_reset, 290 .adma_write_desc = dwcmshc_adma_write_desc, 291 }; 292 293 static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = { 294 .ops = &sdhci_dwcmshc_ops, 295 .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 296 .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, 297 }; 298 299 #ifdef CONFIG_ACPI 300 static const struct sdhci_pltfm_data sdhci_dwcmshc_bf3_pdata = { 301 .ops = &sdhci_dwcmshc_ops, 302 .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, 303 .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | 304 SDHCI_QUIRK2_ACMD23_BROKEN, 305 }; 306 #endif 307 308 static const struct sdhci_pltfm_data sdhci_dwcmshc_rk35xx_pdata = { 309 .ops = &sdhci_dwcmshc_rk35xx_ops, 310 .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | 311 SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, 312 .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | 313 SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, 314 }; 315 316 static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) 317 { 318 int err; 319 struct rk35xx_priv *priv = dwc_priv->priv; 320 321 priv->reset = devm_reset_control_array_get_optional_exclusive(mmc_dev(host->mmc)); 322 if (IS_ERR(priv->reset)) { 323 err = PTR_ERR(priv->reset); 324 dev_err(mmc_dev(host->mmc), "failed to get reset control %d\n", err); 325 return err; 326 } 327 328 priv->rockchip_clks[0].id = "axi"; 329 priv->rockchip_clks[1].id = "block"; 330 priv->rockchip_clks[2].id = "timer"; 331 err = devm_clk_bulk_get_optional(mmc_dev(host->mmc), RK35xx_MAX_CLKS, 332 priv->rockchip_clks); 333 if (err) { 334 dev_err(mmc_dev(host->mmc), "failed to get clocks %d\n", err); 335 return err; 336 } 337 338 err = clk_bulk_prepare_enable(RK35xx_MAX_CLKS, priv->rockchip_clks); 339 if (err) { 340 dev_err(mmc_dev(host->mmc), "failed to enable clocks %d\n", err); 341 return err; 342 } 343 344 if (of_property_read_u8(mmc_dev(host->mmc)->of_node, "rockchip,txclk-tapnum", 345 &priv->txclk_tapnum)) 346 priv->txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT; 347 348 /* Disable cmd conflict check */ 349 sdhci_writel(host, 0x0, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3); 350 /* Reset previous settings */ 351 sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK); 352 sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_STRBIN); 353 354 return 0; 355 } 356 357 static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { 358 { 359 .compatible = "rockchip,rk3568-dwcmshc", 360 .data = &sdhci_dwcmshc_rk35xx_pdata, 361 }, 362 { 363 .compatible = "snps,dwcmshc-sdhci", 364 .data = &sdhci_dwcmshc_pdata, 365 }, 366 {}, 367 }; 368 MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids); 369 370 #ifdef CONFIG_ACPI 371 static const struct acpi_device_id sdhci_dwcmshc_acpi_ids[] = { 372 { 373 .id = "MLNXBF30", 374 .driver_data = (kernel_ulong_t)&sdhci_dwcmshc_bf3_pdata, 375 }, 376 {} 377 }; 378 #endif 379 380 static int dwcmshc_probe(struct platform_device *pdev) 381 { 382 struct device *dev = &pdev->dev; 383 struct sdhci_pltfm_host *pltfm_host; 384 struct sdhci_host *host; 385 struct dwcmshc_priv *priv; 386 struct rk35xx_priv *rk_priv = NULL; 387 const struct sdhci_pltfm_data *pltfm_data; 388 int err; 389 u32 extra; 390 391 pltfm_data = device_get_match_data(&pdev->dev); 392 if (!pltfm_data) { 393 dev_err(&pdev->dev, "Error: No device match data found\n"); 394 return -ENODEV; 395 } 396 397 host = sdhci_pltfm_init(pdev, pltfm_data, 398 sizeof(struct dwcmshc_priv)); 399 if (IS_ERR(host)) 400 return PTR_ERR(host); 401 402 /* 403 * extra adma table cnt for cross 128M boundary handling. 404 */ 405 extra = DIV_ROUND_UP_ULL(dma_get_required_mask(dev), SZ_128M); 406 if (extra > SDHCI_MAX_SEGS) 407 extra = SDHCI_MAX_SEGS; 408 host->adma_table_cnt += extra; 409 410 pltfm_host = sdhci_priv(host); 411 priv = sdhci_pltfm_priv(pltfm_host); 412 413 if (dev->of_node) { 414 pltfm_host->clk = devm_clk_get(dev, "core"); 415 if (IS_ERR(pltfm_host->clk)) { 416 err = PTR_ERR(pltfm_host->clk); 417 dev_err(dev, "failed to get core clk: %d\n", err); 418 goto free_pltfm; 419 } 420 err = clk_prepare_enable(pltfm_host->clk); 421 if (err) 422 goto free_pltfm; 423 424 priv->bus_clk = devm_clk_get(dev, "bus"); 425 if (!IS_ERR(priv->bus_clk)) 426 clk_prepare_enable(priv->bus_clk); 427 } 428 429 err = mmc_of_parse(host->mmc); 430 if (err) 431 goto err_clk; 432 433 sdhci_get_of_property(pdev); 434 435 priv->vendor_specific_area1 = 436 sdhci_readl(host, DWCMSHC_P_VENDOR_AREA1) & DWCMSHC_AREA1_MASK; 437 438 host->mmc_host_ops.request = dwcmshc_request; 439 host->mmc_host_ops.hs400_enhanced_strobe = dwcmshc_hs400_enhanced_strobe; 440 441 if (pltfm_data == &sdhci_dwcmshc_rk35xx_pdata) { 442 rk_priv = devm_kzalloc(&pdev->dev, sizeof(struct rk35xx_priv), GFP_KERNEL); 443 if (!rk_priv) { 444 err = -ENOMEM; 445 goto err_clk; 446 } 447 448 priv->priv = rk_priv; 449 450 err = dwcmshc_rk35xx_init(host, priv); 451 if (err) 452 goto err_clk; 453 } 454 455 host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; 456 457 err = sdhci_add_host(host); 458 if (err) 459 goto err_clk; 460 461 return 0; 462 463 err_clk: 464 clk_disable_unprepare(pltfm_host->clk); 465 clk_disable_unprepare(priv->bus_clk); 466 if (rk_priv) 467 clk_bulk_disable_unprepare(RK35xx_MAX_CLKS, 468 rk_priv->rockchip_clks); 469 free_pltfm: 470 sdhci_pltfm_free(pdev); 471 return err; 472 } 473 474 static int dwcmshc_remove(struct platform_device *pdev) 475 { 476 struct sdhci_host *host = platform_get_drvdata(pdev); 477 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 478 struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); 479 struct rk35xx_priv *rk_priv = priv->priv; 480 481 sdhci_remove_host(host, 0); 482 483 clk_disable_unprepare(pltfm_host->clk); 484 clk_disable_unprepare(priv->bus_clk); 485 if (rk_priv) 486 clk_bulk_disable_unprepare(RK35xx_MAX_CLKS, 487 rk_priv->rockchip_clks); 488 sdhci_pltfm_free(pdev); 489 490 return 0; 491 } 492 493 #ifdef CONFIG_PM_SLEEP 494 static int dwcmshc_suspend(struct device *dev) 495 { 496 struct sdhci_host *host = dev_get_drvdata(dev); 497 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 498 struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); 499 struct rk35xx_priv *rk_priv = priv->priv; 500 int ret; 501 502 ret = sdhci_suspend_host(host); 503 if (ret) 504 return ret; 505 506 clk_disable_unprepare(pltfm_host->clk); 507 if (!IS_ERR(priv->bus_clk)) 508 clk_disable_unprepare(priv->bus_clk); 509 510 if (rk_priv) 511 clk_bulk_disable_unprepare(RK35xx_MAX_CLKS, 512 rk_priv->rockchip_clks); 513 514 return ret; 515 } 516 517 static int dwcmshc_resume(struct device *dev) 518 { 519 struct sdhci_host *host = dev_get_drvdata(dev); 520 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 521 struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); 522 struct rk35xx_priv *rk_priv = priv->priv; 523 int ret; 524 525 ret = clk_prepare_enable(pltfm_host->clk); 526 if (ret) 527 return ret; 528 529 if (!IS_ERR(priv->bus_clk)) { 530 ret = clk_prepare_enable(priv->bus_clk); 531 if (ret) 532 return ret; 533 } 534 535 if (rk_priv) { 536 ret = clk_bulk_prepare_enable(RK35xx_MAX_CLKS, 537 rk_priv->rockchip_clks); 538 if (ret) 539 return ret; 540 } 541 542 return sdhci_resume_host(host); 543 } 544 #endif 545 546 static SIMPLE_DEV_PM_OPS(dwcmshc_pmops, dwcmshc_suspend, dwcmshc_resume); 547 548 static struct platform_driver sdhci_dwcmshc_driver = { 549 .driver = { 550 .name = "sdhci-dwcmshc", 551 .probe_type = PROBE_PREFER_ASYNCHRONOUS, 552 .of_match_table = sdhci_dwcmshc_dt_ids, 553 .acpi_match_table = ACPI_PTR(sdhci_dwcmshc_acpi_ids), 554 .pm = &dwcmshc_pmops, 555 }, 556 .probe = dwcmshc_probe, 557 .remove = dwcmshc_remove, 558 }; 559 module_platform_driver(sdhci_dwcmshc_driver); 560 561 MODULE_DESCRIPTION("SDHCI platform driver for Synopsys DWC MSHC"); 562 MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>"); 563 MODULE_LICENSE("GPL v2"); 564