[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <202101100801.9l0sf3d7-lkp@intel.com>
Date: Sun, 10 Jan 2021 08:39:21 +0800
From: kernel test robot <lkp@...el.com>
To: AngeloGioacchino Del Regno
<angelogioacchino.delregno@...ainline.org>,
linux-arm-msm@...r.kernel.org
Cc: kbuild-all@...ts.01.org, konrad.dybcio@...ainline.org,
marijn.suijten@...ainline.org, martin.botka@...ainline.org,
phone-devel@...r.kernel.org, linux-kernel@...r.kernel.org,
robh+dt@...nel.org, rjw@...ysocki.net, viresh.kumar@...aro.org,
nks@...wful.org
Subject: Re: [PATCH v2 09/15] soc: qcom: Add support for Core Power Reduction
v3, v4 and Hardened
Hi AngeloGioacchino,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on pm/linux-next]
[also build test WARNING on robh/for-next linux/master linus/master v5.11-rc2 next-20210108]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/AngeloGioacchino-Del-Regno/Enable-CPRh-3-4-CPU-Scaling-on-various-QCOM-SoCs/20210110-021002
base: https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
config: arm64-allyesconfig (attached as .config)
compiler: aarch64-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/f7b151e3b7c4b04b3333508e6ff13738de070041
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review AngeloGioacchino-Del-Regno/Enable-CPRh-3-4-CPU-Scaling-on-various-QCOM-SoCs/20210110-021002
git checkout f7b151e3b7c4b04b3333508e6ff13738de070041
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=arm64
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@...el.com>
All warnings (new ones prefixed by >>):
drivers/soc/qcom/cpr3.c: In function 'cpr3_corner_init':
>> drivers/soc/qcom/cpr3.c:1673:17: warning: variable 'min_quot_val' set but not used [-Wunused-but-set-variable]
1673 | int ring_osc, min_quot_val;
| ^~~~~~~~~~~~
>> drivers/soc/qcom/cpr3.c:1316:7: warning: variable 'apply_scaling' set but not used [-Wunused-but-set-variable]
1316 | bool apply_scaling;
| ^~~~~~~~~~~~~
vim +/min_quot_val +1673 drivers/soc/qcom/cpr3.c
1284
1285 /**
1286 * cpr3_corner_init - Calculate and set-up corners for the CPR HW
1287 * @thread: Structure holding CPR thread-specific parameters
1288 *
1289 * This function calculates all the corner parameters by comparing
1290 * and interpolating the values read from the various set-points
1291 * read from the fuses (also called "fuse corners") to generate and
1292 * program to the CPR a lookup table that describes each voltage
1293 * step, mapped to a performance level (or corner number).
1294 *
1295 * It also programs other essential parameters on the CPR and - if
1296 * we are dealing with CPR-Hardened, it will also enable the internal
1297 * interface between the Operating State Manager (OSM) and the CPRh
1298 * in order to achieve CPU DVFS.
1299 *
1300 * Returns: Zero for success, negative number on error
1301 */
1302 static int cpr3_corner_init(struct cpr_thread *thread)
1303 {
1304 struct cpr_drv *drv = thread->drv;
1305 const struct cpr_desc *desc = drv->desc;
1306 const struct cpr_thread_desc *tdesc = thread->desc;
1307 const struct cpr_fuse *fuses = thread->cpr_fuses;
1308 int i, ret, total_corners, extra_corners, level, scaling = 0;
1309 unsigned int fnum, fc;
1310 const char *quot_offset;
1311 const struct fuse_corner_data *fdata;
1312 struct fuse_corner *fuse, *prev_fuse;
1313 struct corner *corner, *prev_corner, *end;
1314 struct corner_data *cdata;
1315 struct dev_pm_opp *opp;
1316 bool apply_scaling;
1317 unsigned long freq;
1318 u32 ring_osc_mask = CPR3_RO_MASK, min_quotient = U32_MAX;
1319
1320 corner = thread->corners;
1321 prev_corner = &thread->corners[0];
1322 end = &corner[thread->num_corners - 1];
1323
1324 cdata = devm_kcalloc(drv->dev,
1325 thread->num_corners + drv->extra_corners,
1326 sizeof(struct corner_data),
1327 GFP_KERNEL);
1328 if (!cdata)
1329 return -ENOMEM;
1330
1331 for (level = 1; level <= thread->num_corners; level++) {
1332 opp = dev_pm_opp_find_level_exact(&thread->pd.dev, level);
1333 if (IS_ERR(opp))
1334 return -EINVAL;
1335
1336 /*
1337 * If there is only one specified qcom,opp-fuse-level, then
1338 * it is assumed that this only one is global and valid for
1339 * all IDs, so try to get the specific one but, on failure,
1340 * go for the global one.
1341 */
1342 fc = cpr_get_fuse_corner(opp, thread->id);
1343 if (fc == 0) {
1344 fc = cpr_get_fuse_corner(opp, 0);
1345 if (fc == 0) {
1346 dev_err(drv->dev,
1347 "qcom,opp-fuse-level is missing!\n");
1348 dev_pm_opp_put(opp);
1349 return -EINVAL;
1350 }
1351 }
1352 fnum = fc - 1;
1353
1354 freq = cpr_get_opp_hz_for_req(opp, thread->attached_cpu_dev);
1355 if (!freq) {
1356 thread->num_corners = max(level - 1, 0);
1357 end = &thread->corners[thread->num_corners - 1];
1358 break;
1359 }
1360
1361 /*
1362 * If any post-vadj (open/closed loop) is not specified, then
1363 * it's zero, meaning that it is not required for this corner.
1364 */
1365 cpr_get_corner_post_vadj(opp, thread->id,
1366 &cdata[level - 1].oloop_vadj,
1367 &cdata[level - 1].cloop_vadj);
1368 cdata[level - 1].fuse_corner = fnum;
1369 cdata[level - 1].freq = freq;
1370
1371 fuse = &thread->fuse_corners[fnum];
1372 dev_dbg(drv->dev, "freq: %lu level: %u fuse level: %u\n",
1373 freq, dev_pm_opp_get_level(opp) - 1, fnum);
1374 if (freq > fuse->max_freq)
1375 fuse->max_freq = freq;
1376 dev_pm_opp_put(opp);
1377
1378 /*
1379 * Make sure that the frequencies in the table are in ascending
1380 * order, as this is critical for the algorithm to work.
1381 */
1382 if (cdata[level - 2].freq > freq) {
1383 dev_err(drv->dev,
1384 "Frequency table not in ascending order.\n");
1385 return -EINVAL;
1386 }
1387 }
1388
1389 if (thread->num_corners < 2) {
1390 dev_err(drv->dev, "need at least 2 OPPs to use CPR\n");
1391 return -EINVAL;
1392 }
1393
1394 /*
1395 * Get the quotient adjustment scaling factor, according to:
1396 *
1397 * scaling = min(1000 * (QUOT(corner_N) - QUOT(corner_N-1))
1398 * / (freq(corner_N) - freq(corner_N-1)), max_factor)
1399 *
1400 * QUOT(corner_N): quotient read from fuse for fuse corner N
1401 * QUOT(corner_N-1): quotient read from fuse for fuse corner (N - 1)
1402 * freq(corner_N): max frequency in MHz supported by fuse corner N
1403 * freq(corner_N-1): max frequency in MHz supported by fuse corner
1404 * (N - 1)
1405 *
1406 * Then walk through the corners mapped to each fuse corner
1407 * and calculate the quotient adjustment for each one using the
1408 * following formula:
1409 *
1410 * quot_adjust = (freq_max - freq_corner) * scaling / 1000
1411 *
1412 * freq_max: max frequency in MHz supported by the fuse corner
1413 * freq_corner: frequency in MHz corresponding to the corner
1414 * scaling: calculated from above equation
1415 *
1416 *
1417 * + +
1418 * | v |
1419 * q | f c o | f c
1420 * u | c l | c
1421 * o | f t | f
1422 * t | c a | c
1423 * | c f g | c f
1424 * | e |
1425 * +--------------- +----------------
1426 * 0 1 2 3 4 5 6 0 1 2 3 4 5 6
1427 * corner corner
1428 *
1429 * c = corner
1430 * f = fuse corner
1431 *
1432 */
1433 for (apply_scaling = false, i = 0; corner <= end; corner++, i++) {
1434 unsigned long freq_diff_mhz;
1435 int ro_fac, vadj, prev_quot;
1436
1437 fnum = cdata[i].fuse_corner;
1438 fdata = &tdesc->fuse_corner_data[fnum];
1439 quot_offset = fuses[fnum].quotient_offset;
1440 fuse = &thread->fuse_corners[fnum];
1441 ring_osc_mask &= (u16)(~BIT(fuse->ring_osc_idx));
1442 if (fnum)
1443 prev_fuse = &thread->fuse_corners[fnum - 1];
1444 else
1445 prev_fuse = NULL;
1446
1447 corner->fuse_corner = fuse;
1448 corner->freq = cdata[i].freq;
1449 corner->uV = fuse->uV;
1450
1451 if (prev_fuse) {
1452 if (prev_fuse->ring_osc_idx == fuse->ring_osc_idx)
1453 quot_offset = NULL;
1454
1455 scaling = cpr_calculate_scaling(quot_offset, drv->dev,
1456 fdata, corner);
1457 if (scaling < 0)
1458 return scaling;
1459
1460 freq_diff_mhz = fuse->max_freq - corner->freq;
1461 do_div(freq_diff_mhz, 1000000); /* now in MHz */
1462
1463 corner->quot_adjust = scaling * freq_diff_mhz;
1464 do_div(corner->quot_adjust, 1000);
1465
1466 /* Fine-tune QUOT (closed-loop) based on fixed values */
1467 ro_fac = cpr_get_ro_factor(tdesc, fnum, fuse->ring_osc_idx);
1468 vadj = cdata[i].cloop_vadj;
1469 corner->quot_adjust -= cpr3_adjust_quot(ro_fac, vadj);
1470 dev_vdbg(drv->dev,
1471 "Quot fine-tuning to %d for post-vadj=%d\n",
1472 corner->quot_adjust, vadj);
1473
1474 /*
1475 * Make sure that we scale (up) monotonically.
1476 * P.S.: Fuse quots can never be descending.
1477 */
1478 prev_quot = prev_corner->fuse_corner->quot;
1479 prev_quot -= prev_corner->quot_adjust;
1480 if (fuse->quot - corner->quot_adjust < prev_quot) {
1481 int new_adj = prev_corner->fuse_corner->quot;
1482 new_adj -= fuse->quot;
1483 dev_vdbg(drv->dev,
1484 "Monotonic increase forced: %d->%d\n",
1485 corner->quot_adjust, new_adj);
1486 corner->quot_adjust = new_adj;
1487 }
1488
1489 corner->uV = cpr_interpolate(corner,
1490 drv->vreg_step, fdata);
1491 } else if (corner->freq == fuse->max_freq) {
1492 /* This is a fuse corner; don't scale anything */
1493 apply_scaling = false;
1494 }
1495 /* Negative fuse quotients are nonsense. */
1496 if (fuse->quot < corner->quot_adjust)
1497 return -EINVAL;
1498
1499 min_quotient = min(min_quotient,
1500 (u32)(fuse->quot - corner->quot_adjust));
1501
1502 /* Fine-tune voltages (open-loop) based on fixed values */
1503 corner->uV += cdata[i].oloop_vadj;
1504 dev_dbg(drv->dev,
1505 "Voltage fine-tuning to %d for post-vadj=%d\n",
1506 corner->uV, cdata[i].oloop_vadj);
1507
1508 corner->max_uV = fuse->max_uV;
1509 corner->min_uV = fuse->min_uV;
1510 corner->uV = clamp(corner->uV, corner->min_uV, corner->max_uV);
1511 dev_vdbg(drv->dev, "Clamped after interpolation: [%d %d %d]\n",
1512 corner->min_uV, corner->uV, corner->max_uV);
1513
1514 /* Make sure that we scale monotonically here, too. */
1515 if (corner->uV < prev_corner->uV)
1516 corner->uV = prev_corner->uV;
1517
1518 corner->last_uV = corner->uV;
1519
1520 /* Reduce the ceiling voltage if needed */
1521 if (desc->reduce_to_corner_uV && corner->uV < corner->max_uV)
1522 corner->max_uV = corner->uV;
1523 else if (desc->reduce_to_fuse_uV && fuse->uV < corner->max_uV)
1524 corner->max_uV = max(corner->min_uV, fuse->uV);
1525
1526 corner->min_uV = max(corner->max_uV - fdata->range_uV,
1527 corner->min_uV);
1528
1529 /*
1530 * Adjust per-corner floor and ceiling voltages so that
1531 * they do not overlap the memory Array Power Mux (APM)
1532 * nor the Memory Accelerator (MEM-ACC) threshold voltages.
1533 */
1534 if (desc->apm_threshold)
1535 cpr3_restrict_corner(corner, desc->apm_threshold,
1536 desc->apm_hysteresis,
1537 drv->vreg_step);
1538 if (desc->mem_acc_threshold)
1539 cpr3_restrict_corner(corner, desc->mem_acc_threshold,
1540 0, drv->vreg_step);
1541
1542 prev_corner = corner;
1543 dev_dbg(drv->dev, "corner %d: [%d %d %d] scaling %d quot %d\n", i,
1544 corner->min_uV, corner->uV, corner->max_uV, scaling,
1545 fuse->quot - corner->quot_adjust);
1546 }
1547
1548 /* Additional setup for CPRh only */
1549 if (desc->cpr_type < CTRL_TYPE_CPRH)
1550 return 0;
1551
1552 /* If the OPPs can't be adjusted, programming the CPRh is useless */
1553 ret = cprh_corner_adjust_opps(thread);
1554 if (ret) {
1555 dev_err(drv->dev, "Cannot adjust CPU OPP voltages: %d\n", ret);
1556 return ret;
1557 }
1558
1559 total_corners = thread->num_corners;
1560 extra_corners = drv->extra_corners;
1561
1562 /* If the APM extra corner exists, add it now. */
1563 if (desc->apm_crossover && desc->apm_threshold && extra_corners) {
1564 /* Program the APM crossover corner on the CPR-Hardened */
1565 thread->corners[total_corners].uV = desc->apm_crossover;
1566 thread->corners[total_corners].min_uV = desc->apm_crossover;
1567 thread->corners[total_corners].max_uV = desc->apm_crossover;
1568 thread->corners[total_corners].is_open_loop = true;
1569
1570 /*
1571 * Also add and disable an opp with zero frequency: other
1572 * drivers will need to know what is the APM *threshold*
1573 * voltage.
1574 */
1575 ret = dev_pm_opp_add(thread->attached_cpu_dev, 0,
1576 desc->apm_threshold);
1577 if (ret)
1578 return ret;
1579
1580 ret = dev_pm_opp_disable(thread->attached_cpu_dev, 0);
1581 if (ret)
1582 return ret;
1583
1584 dev_dbg(drv->dev, "corner %d (APM): [%d %d %d] Open-Loop\n",
1585 total_corners, desc->apm_crossover,
1586 desc->apm_crossover, desc->apm_crossover);
1587
1588 total_corners++;
1589 extra_corners--;
1590 }
1591
1592 if (desc->mem_acc_threshold && extra_corners) {
1593 /* Program the Memory Accelerator threshold corner to CPRh */
1594 thread->corners[total_corners].uV = desc->mem_acc_threshold;
1595 thread->corners[total_corners].min_uV = desc->mem_acc_threshold;
1596 thread->corners[total_corners].max_uV = desc->mem_acc_threshold;
1597 thread->corners[total_corners].is_open_loop = true;
1598
1599 /*
1600 * Add and disable an opp with zero frequency: other
1601 * drivers will also need to know about any mem-acc
1602 * threshold to respect.
1603 */
1604 ret = dev_pm_opp_add(thread->attached_cpu_dev, 1,
1605 desc->mem_acc_threshold);
1606 if (ret)
1607 return ret;
1608
1609 ret = dev_pm_opp_disable(thread->attached_cpu_dev, 1);
1610 if (ret)
1611 return ret;
1612
1613 dev_dbg(drv->dev, "corner %d (MEMACC): [%d %d %d] Open-Loop\n",
1614 total_corners, desc->mem_acc_threshold,
1615 desc->mem_acc_threshold, desc->mem_acc_threshold);
1616
1617 total_corners++;
1618 extra_corners--;
1619 }
1620
1621 /*
1622 * If there are any extra corners left, it means that even though we
1623 * expect to fill in both APM and MEM-ACC crossovers, one couldn't
1624 * satisfy requirements, which means that the specified parameters
1625 * are wrong: in this case, inform the user and bail out, otherwise
1626 * if we go on writing the (invalid) table to the CPR-Hardened, the
1627 * hardware (in this case, the CPU) will surely freeze and crash.
1628 */
1629 if (unlikely(extra_corners)) {
1630 dev_err(drv->dev, "APM/MEM-ACC corners: bad parameters.\n");
1631 return -EINVAL;
1632 }
1633 /* Reassign extra_corners, as we have to exclude delta_quot for them */
1634 extra_corners = drv->extra_corners;
1635
1636 /* Disable the interface between OSM and CPRh */
1637 cpr_masked_write(thread, drv->reg_ctl,
1638 CPRH_CTL_OSM_ENABLED, 0);
1639
1640 /* Program the GCNT before unmasking ring oscillator(s) */
1641 for (i = 0; i < CPR3_RO_COUNT; i++) {
1642 if (!(ring_osc_mask & BIT(i))) {
1643 cpr_write(thread, CPR3_REG_GCNT(i), drv->gcnt);
1644 dev_vdbg(drv->dev, "RO%d gcnt=%d\n", i, drv->gcnt);
1645 }
1646 }
1647
1648 /*
1649 * Unmask the ring oscillator(s) that we're going to use: it seems
1650 * to be mandatory to do this *before* sending the rest of the
1651 * CPRhardened specific configuration.
1652 */
1653 dev_dbg(drv->dev,
1654 "Unmasking ring oscillators with mask 0x%x\n", ring_osc_mask);
1655 cpr_write(thread, CPR3_REG_RO_MASK(tdesc->hw_tid), ring_osc_mask);
1656
1657 /* Setup minimum quotients for ring oscillators */
1658 for (i = 0; i < CPR3_RO_COUNT; i++) {
1659 u32 tgt_quot_reg = CPR3_REG_TARGET_QUOT(tdesc->hw_tid, i);
1660 u32 tgt_quot_val = 0;
1661
1662 if (!(ring_osc_mask & BIT(i)))
1663 tgt_quot_val = min_quotient;
1664
1665 cpr_write(thread, tgt_quot_reg, tgt_quot_val);
1666 dev_vdbg(drv->dev,
1667 "Programmed min quotient %u for Ring Oscillator %d\n",
1668 tgt_quot_val, tgt_quot_reg);
1669 }
1670
1671 for (i = 0; i < total_corners; i++) {
1672 int volt_oloop_steps, volt_floor_steps, delta_quot_steps;
> 1673 int ring_osc, min_quot_val;
1674 u32 val;
1675
1676 fnum = cdata[i].fuse_corner;
1677 fuse = &thread->fuse_corners[fnum];
1678
1679 val = thread->corners[i].uV - desc->cpr_base_voltage;
1680 volt_oloop_steps = DIV_ROUND_UP(val, drv->vreg_step);
1681
1682 val = thread->corners[i].min_uV - desc->cpr_base_voltage;
1683 volt_floor_steps = DIV_ROUND_UP(val, drv->vreg_step);
1684
1685 if (i >= (total_corners - extra_corners)) {
1686 delta_quot_steps = 0;
1687 min_quot_val = 0;
1688 } else {
1689 min_quot_val = min_quotient;
1690 val = fuse->quot - thread->corners[i].quot_adjust;
1691 val -= min_quotient;
1692 delta_quot_steps = DIV_ROUND_UP(val,
1693 CPRH_DELTA_QUOT_STEP_FACTOR);
1694 }
1695
1696 /*
1697 * If we are accessing corners that are not used as
1698 * an active DCVS set-point, then always select RO 0
1699 * and zero out the delta quotient.
1700 */
1701 if (i >= thread->num_corners) {
1702 ring_osc = 0;
1703 delta_quot_steps = 0;
1704 } else {
1705 ring_osc = fuse->ring_osc_idx;
1706 }
1707
1708 if (volt_oloop_steps > CPRH_CORNER_INIT_VOLTAGE_MAX_VALUE ||
1709 volt_floor_steps > CPRH_CORNER_FLOOR_VOLTAGE_MAX_VALUE ||
1710 delta_quot_steps > CPRH_CORNER_QUOT_DELTA_MAX_VALUE) {
1711 dev_err(drv->dev,
1712 "Invalid cfg: oloop=%d, floor=%d, delta=%d\n",
1713 volt_oloop_steps, volt_floor_steps,
1714 delta_quot_steps);
1715 return -EINVAL;
1716 }
1717 /* Green light: Go, Go, Go! */
1718
1719 /* Set number of open-loop steps */
1720 val = volt_oloop_steps << CPRH_CORNER_INIT_VOLTAGE_SHIFT;
1721 val &= CPRH_CORNER_INIT_VOLTAGE_MASK;
1722
1723 /* Set number of floor voltage steps */
1724 val |= (volt_floor_steps << CPRH_CORNER_FLOOR_VOLTAGE_SHIFT) &
1725 CPRH_CORNER_FLOOR_VOLTAGE_MASK;
1726
1727 /* Set number of target quotient delta steps */
1728 val |= (delta_quot_steps << CPRH_CORNER_QUOT_DELTA_SHIFT) &
1729 CPRH_CORNER_QUOT_DELTA_MASK;
1730
1731 /* Select ring oscillator for this corner */
1732 val |= (ring_osc << CPRH_CORNER_RO_SEL_SHIFT) &
1733 CPRH_CORNER_RO_SEL_MASK;
1734
1735 /* Open loop corner is usually APM/ACC crossover */
1736 if (thread->corners[i].is_open_loop) {
1737 dev_dbg(drv->dev,
1738 "Disabling Closed-Loop on corner %d\n", i);
1739 val |= CPRH_CORNER_CPR_CL_DISABLE;
1740 }
1741 cpr_write(thread, CPRH_REG_CORNER(drv, tdesc->hw_tid, i), val);
1742
1743 dev_dbg(drv->dev,
1744 "steps [%d]: open-loop %d, floor %d, delta_quot %d\n",
1745 i, volt_oloop_steps, volt_floor_steps,
1746 delta_quot_steps);
1747 }
1748
1749 /* YAY! Setup is done! Enable the internal loop to start CPR. */
1750 cpr_masked_write(thread, CPR3_REG_CPR_CTL,
1751 CPR3_CPR_CTL_LOOP_EN_MASK,
1752 CPR3_CPR_CTL_LOOP_EN_MASK);
1753
1754 /*
1755 * Make sure that all the writes go through before enabling
1756 * internal communication between the OSM and the CPRh
1757 * controllers, otherwise there is a high risk of hardware
1758 * lockups due to under-voltage for the selected CPU clock.
1759 *
1760 * Please note that the CPR-hardened gets set-up in Linux but
1761 * then gets actually used in firmware (and only by the OSM);
1762 * after handing it off we will have no more control on it,
1763 * hence the only way to get things up properly for sure here
1764 * is a write barrier.
1765 */
1766 wmb();
1767
1768 /* Enable the interface between OSM and CPRh */
1769 cpr_masked_write(thread, drv->reg_ctl,
1770 CPRH_CTL_OSM_ENABLED,
1771 CPRH_CTL_OSM_ENABLED);
1772
1773 /* On success, free cdata manually */
1774 devm_kfree(drv->dev, cdata);
1775 return 0;
1776 }
1777
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Download attachment ".config.gz" of type "application/gzip" (76497 bytes)
Powered by blists - more mailing lists