.. | .. |
---|
19 | 19 | #include <linux/i2c.h> |
---|
20 | 20 | #include <linux/module.h> |
---|
21 | 21 | #include <linux/regmap.h> |
---|
| 22 | +#include <linux/regulator/consumer.h> |
---|
22 | 23 | #include <linux/slab.h> |
---|
23 | 24 | #include <asm/unaligned.h> |
---|
24 | 25 | |
---|
.. | .. |
---|
59 | 60 | struct clk_si5341_output { |
---|
60 | 61 | struct clk_hw hw; |
---|
61 | 62 | struct clk_si5341 *data; |
---|
| 63 | + struct regulator *vddo_reg; |
---|
62 | 64 | u8 index; |
---|
63 | 65 | }; |
---|
64 | 66 | #define to_clk_si5341_output(_hw) \ |
---|
.. | .. |
---|
84 | 86 | struct clk_si5341_output_config { |
---|
85 | 87 | u8 out_format_drv_bits; |
---|
86 | 88 | u8 out_cm_ampl_bits; |
---|
| 89 | + u8 vdd_sel_bits; |
---|
87 | 90 | bool synth_master; |
---|
88 | 91 | bool always_on; |
---|
89 | 92 | }; |
---|
.. | .. |
---|
135 | 138 | #define SI5341_OUT_MUX_SEL(output) (SI5341_OUT_CONFIG(output) + 3) |
---|
136 | 139 | #define SI5341_OUT_R_REG(output) \ |
---|
137 | 140 | ((output)->data->reg_rdiv_offset[(output)->index]) |
---|
| 141 | + |
---|
| 142 | +#define SI5341_OUT_MUX_VDD_SEL_MASK 0x38 |
---|
138 | 143 | |
---|
139 | 144 | /* Synthesize N divider */ |
---|
140 | 145 | #define SI5341_SYNTH_N_NUM(x) (0x0302 + ((x) * 11)) |
---|
.. | .. |
---|
1250 | 1255 | .volatile_table = &si5341_regmap_volatile, |
---|
1251 | 1256 | }; |
---|
1252 | 1257 | |
---|
1253 | | -static int si5341_dt_parse_dt(struct i2c_client *client, |
---|
1254 | | - struct clk_si5341_output_config *config) |
---|
| 1258 | +static int si5341_dt_parse_dt(struct clk_si5341 *data, |
---|
| 1259 | + struct clk_si5341_output_config *config) |
---|
1255 | 1260 | { |
---|
1256 | 1261 | struct device_node *child; |
---|
1257 | | - struct device_node *np = client->dev.of_node; |
---|
| 1262 | + struct device_node *np = data->i2c_client->dev.of_node; |
---|
1258 | 1263 | u32 num; |
---|
1259 | 1264 | u32 val; |
---|
1260 | 1265 | |
---|
.. | .. |
---|
1263 | 1268 | |
---|
1264 | 1269 | for_each_child_of_node(np, child) { |
---|
1265 | 1270 | if (of_property_read_u32(child, "reg", &num)) { |
---|
1266 | | - dev_err(&client->dev, "missing reg property of %s\n", |
---|
| 1271 | + dev_err(&data->i2c_client->dev, "missing reg property of %s\n", |
---|
1267 | 1272 | child->name); |
---|
1268 | 1273 | goto put_child; |
---|
1269 | 1274 | } |
---|
1270 | 1275 | |
---|
1271 | 1276 | if (num >= SI5341_MAX_NUM_OUTPUTS) { |
---|
1272 | | - dev_err(&client->dev, "invalid clkout %d\n", num); |
---|
| 1277 | + dev_err(&data->i2c_client->dev, "invalid clkout %d\n", num); |
---|
1273 | 1278 | goto put_child; |
---|
1274 | 1279 | } |
---|
1275 | 1280 | |
---|
.. | .. |
---|
1288 | 1293 | config[num].out_format_drv_bits |= 0xc0; |
---|
1289 | 1294 | break; |
---|
1290 | 1295 | default: |
---|
1291 | | - dev_err(&client->dev, |
---|
| 1296 | + dev_err(&data->i2c_client->dev, |
---|
1292 | 1297 | "invalid silabs,format %u for %u\n", |
---|
1293 | 1298 | val, num); |
---|
1294 | 1299 | goto put_child; |
---|
.. | .. |
---|
1301 | 1306 | |
---|
1302 | 1307 | if (!of_property_read_u32(child, "silabs,common-mode", &val)) { |
---|
1303 | 1308 | if (val > 0xf) { |
---|
1304 | | - dev_err(&client->dev, |
---|
| 1309 | + dev_err(&data->i2c_client->dev, |
---|
1305 | 1310 | "invalid silabs,common-mode %u\n", |
---|
1306 | 1311 | val); |
---|
1307 | 1312 | goto put_child; |
---|
.. | .. |
---|
1312 | 1317 | |
---|
1313 | 1318 | if (!of_property_read_u32(child, "silabs,amplitude", &val)) { |
---|
1314 | 1319 | if (val > 0xf) { |
---|
1315 | | - dev_err(&client->dev, |
---|
| 1320 | + dev_err(&data->i2c_client->dev, |
---|
1316 | 1321 | "invalid silabs,amplitude %u\n", |
---|
1317 | 1322 | val); |
---|
1318 | 1323 | goto put_child; |
---|
.. | .. |
---|
1329 | 1334 | |
---|
1330 | 1335 | config[num].always_on = |
---|
1331 | 1336 | of_property_read_bool(child, "always-on"); |
---|
| 1337 | + |
---|
| 1338 | + config[num].vdd_sel_bits = 0x08; |
---|
| 1339 | + if (data->clk[num].vddo_reg) { |
---|
| 1340 | + int vdd = regulator_get_voltage(data->clk[num].vddo_reg); |
---|
| 1341 | + |
---|
| 1342 | + switch (vdd) { |
---|
| 1343 | + case 3300000: |
---|
| 1344 | + config[num].vdd_sel_bits |= 0 << 4; |
---|
| 1345 | + break; |
---|
| 1346 | + case 1800000: |
---|
| 1347 | + config[num].vdd_sel_bits |= 1 << 4; |
---|
| 1348 | + break; |
---|
| 1349 | + case 2500000: |
---|
| 1350 | + config[num].vdd_sel_bits |= 2 << 4; |
---|
| 1351 | + break; |
---|
| 1352 | + default: |
---|
| 1353 | + dev_err(&data->i2c_client->dev, |
---|
| 1354 | + "unsupported vddo voltage %d for %s\n", |
---|
| 1355 | + vdd, child->name); |
---|
| 1356 | + goto put_child; |
---|
| 1357 | + } |
---|
| 1358 | + } else { |
---|
| 1359 | + /* chip seems to default to 2.5V when not set */ |
---|
| 1360 | + dev_warn(&data->i2c_client->dev, |
---|
| 1361 | + "no regulator set, defaulting vdd_sel to 2.5V for %s\n", |
---|
| 1362 | + child->name); |
---|
| 1363 | + config[num].vdd_sel_bits |= 2 << 4; |
---|
| 1364 | + } |
---|
1332 | 1365 | } |
---|
1333 | 1366 | |
---|
1334 | 1367 | return 0; |
---|
.. | .. |
---|
1417 | 1450 | return res; |
---|
1418 | 1451 | } |
---|
1419 | 1452 | |
---|
| 1453 | +static ssize_t input_present_show(struct device *dev, |
---|
| 1454 | + struct device_attribute *attr, |
---|
| 1455 | + char *buf) |
---|
| 1456 | +{ |
---|
| 1457 | + struct clk_si5341 *data = dev_get_drvdata(dev); |
---|
| 1458 | + u32 status; |
---|
| 1459 | + int res = regmap_read(data->regmap, SI5341_STATUS, &status); |
---|
| 1460 | + |
---|
| 1461 | + if (res < 0) |
---|
| 1462 | + return res; |
---|
| 1463 | + res = !(status & SI5341_STATUS_LOSREF); |
---|
| 1464 | + return snprintf(buf, PAGE_SIZE, "%d\n", res); |
---|
| 1465 | +} |
---|
| 1466 | +static DEVICE_ATTR_RO(input_present); |
---|
| 1467 | + |
---|
| 1468 | +static ssize_t input_present_sticky_show(struct device *dev, |
---|
| 1469 | + struct device_attribute *attr, |
---|
| 1470 | + char *buf) |
---|
| 1471 | +{ |
---|
| 1472 | + struct clk_si5341 *data = dev_get_drvdata(dev); |
---|
| 1473 | + u32 status; |
---|
| 1474 | + int res = regmap_read(data->regmap, SI5341_STATUS_STICKY, &status); |
---|
| 1475 | + |
---|
| 1476 | + if (res < 0) |
---|
| 1477 | + return res; |
---|
| 1478 | + res = !(status & SI5341_STATUS_LOSREF); |
---|
| 1479 | + return snprintf(buf, PAGE_SIZE, "%d\n", res); |
---|
| 1480 | +} |
---|
| 1481 | +static DEVICE_ATTR_RO(input_present_sticky); |
---|
| 1482 | + |
---|
| 1483 | +static ssize_t pll_locked_show(struct device *dev, |
---|
| 1484 | + struct device_attribute *attr, |
---|
| 1485 | + char *buf) |
---|
| 1486 | +{ |
---|
| 1487 | + struct clk_si5341 *data = dev_get_drvdata(dev); |
---|
| 1488 | + u32 status; |
---|
| 1489 | + int res = regmap_read(data->regmap, SI5341_STATUS, &status); |
---|
| 1490 | + |
---|
| 1491 | + if (res < 0) |
---|
| 1492 | + return res; |
---|
| 1493 | + res = !(status & SI5341_STATUS_LOL); |
---|
| 1494 | + return snprintf(buf, PAGE_SIZE, "%d\n", res); |
---|
| 1495 | +} |
---|
| 1496 | +static DEVICE_ATTR_RO(pll_locked); |
---|
| 1497 | + |
---|
| 1498 | +static ssize_t pll_locked_sticky_show(struct device *dev, |
---|
| 1499 | + struct device_attribute *attr, |
---|
| 1500 | + char *buf) |
---|
| 1501 | +{ |
---|
| 1502 | + struct clk_si5341 *data = dev_get_drvdata(dev); |
---|
| 1503 | + u32 status; |
---|
| 1504 | + int res = regmap_read(data->regmap, SI5341_STATUS_STICKY, &status); |
---|
| 1505 | + |
---|
| 1506 | + if (res < 0) |
---|
| 1507 | + return res; |
---|
| 1508 | + res = !(status & SI5341_STATUS_LOL); |
---|
| 1509 | + return snprintf(buf, PAGE_SIZE, "%d\n", res); |
---|
| 1510 | +} |
---|
| 1511 | +static DEVICE_ATTR_RO(pll_locked_sticky); |
---|
| 1512 | + |
---|
| 1513 | +static ssize_t clear_sticky_store(struct device *dev, |
---|
| 1514 | + struct device_attribute *attr, |
---|
| 1515 | + const char *buf, size_t count) |
---|
| 1516 | +{ |
---|
| 1517 | + struct clk_si5341 *data = dev_get_drvdata(dev); |
---|
| 1518 | + long val; |
---|
| 1519 | + |
---|
| 1520 | + if (kstrtol(buf, 10, &val)) |
---|
| 1521 | + return -EINVAL; |
---|
| 1522 | + if (val) { |
---|
| 1523 | + int res = regmap_write(data->regmap, SI5341_STATUS_STICKY, 0); |
---|
| 1524 | + |
---|
| 1525 | + if (res < 0) |
---|
| 1526 | + return res; |
---|
| 1527 | + } |
---|
| 1528 | + return count; |
---|
| 1529 | +} |
---|
| 1530 | +static DEVICE_ATTR_WO(clear_sticky); |
---|
| 1531 | + |
---|
| 1532 | +static const struct attribute *si5341_attributes[] = { |
---|
| 1533 | + &dev_attr_input_present.attr, |
---|
| 1534 | + &dev_attr_input_present_sticky.attr, |
---|
| 1535 | + &dev_attr_pll_locked.attr, |
---|
| 1536 | + &dev_attr_pll_locked_sticky.attr, |
---|
| 1537 | + &dev_attr_clear_sticky.attr, |
---|
| 1538 | + NULL |
---|
| 1539 | +}; |
---|
| 1540 | + |
---|
1420 | 1541 | static int si5341_probe(struct i2c_client *client, |
---|
1421 | 1542 | const struct i2c_device_id *id) |
---|
1422 | 1543 | { |
---|
.. | .. |
---|
1424 | 1545 | struct clk_init_data init; |
---|
1425 | 1546 | struct clk *input; |
---|
1426 | 1547 | const char *root_clock_name; |
---|
1427 | | - const char *synth_clock_names[SI5341_NUM_SYNTH]; |
---|
| 1548 | + const char *synth_clock_names[SI5341_NUM_SYNTH] = { NULL }; |
---|
1428 | 1549 | int err; |
---|
1429 | 1550 | unsigned int i; |
---|
1430 | 1551 | struct clk_si5341_output_config config[SI5341_MAX_NUM_OUTPUTS]; |
---|
.. | .. |
---|
1454 | 1575 | } |
---|
1455 | 1576 | } |
---|
1456 | 1577 | |
---|
1457 | | - err = si5341_dt_parse_dt(client, config); |
---|
| 1578 | + for (i = 0; i < SI5341_MAX_NUM_OUTPUTS; ++i) { |
---|
| 1579 | + char reg_name[10]; |
---|
| 1580 | + |
---|
| 1581 | + snprintf(reg_name, sizeof(reg_name), "vddo%d", i); |
---|
| 1582 | + data->clk[i].vddo_reg = devm_regulator_get_optional( |
---|
| 1583 | + &client->dev, reg_name); |
---|
| 1584 | + if (IS_ERR(data->clk[i].vddo_reg)) { |
---|
| 1585 | + err = PTR_ERR(data->clk[i].vddo_reg); |
---|
| 1586 | + data->clk[i].vddo_reg = NULL; |
---|
| 1587 | + if (err == -ENODEV) |
---|
| 1588 | + continue; |
---|
| 1589 | + goto cleanup; |
---|
| 1590 | + } else { |
---|
| 1591 | + err = regulator_enable(data->clk[i].vddo_reg); |
---|
| 1592 | + if (err) { |
---|
| 1593 | + dev_err(&client->dev, |
---|
| 1594 | + "failed to enable %s regulator: %d\n", |
---|
| 1595 | + reg_name, err); |
---|
| 1596 | + data->clk[i].vddo_reg = NULL; |
---|
| 1597 | + goto cleanup; |
---|
| 1598 | + } |
---|
| 1599 | + } |
---|
| 1600 | + } |
---|
| 1601 | + |
---|
| 1602 | + err = si5341_dt_parse_dt(data, config); |
---|
1458 | 1603 | if (err) |
---|
1459 | | - return err; |
---|
| 1604 | + goto cleanup; |
---|
1460 | 1605 | |
---|
1461 | 1606 | if (of_property_read_string(client->dev.of_node, "clock-output-names", |
---|
1462 | 1607 | &init.name)) |
---|
.. | .. |
---|
1464 | 1609 | root_clock_name = init.name; |
---|
1465 | 1610 | |
---|
1466 | 1611 | data->regmap = devm_regmap_init_i2c(client, &si5341_regmap_config); |
---|
1467 | | - if (IS_ERR(data->regmap)) |
---|
1468 | | - return PTR_ERR(data->regmap); |
---|
| 1612 | + if (IS_ERR(data->regmap)) { |
---|
| 1613 | + err = PTR_ERR(data->regmap); |
---|
| 1614 | + goto cleanup; |
---|
| 1615 | + } |
---|
1469 | 1616 | |
---|
1470 | 1617 | i2c_set_clientdata(client, data); |
---|
1471 | 1618 | |
---|
1472 | 1619 | err = si5341_probe_chip_id(data); |
---|
1473 | 1620 | if (err < 0) |
---|
1474 | | - return err; |
---|
| 1621 | + goto cleanup; |
---|
1475 | 1622 | |
---|
1476 | 1623 | if (of_property_read_bool(client->dev.of_node, "silabs,reprogram")) { |
---|
1477 | 1624 | initialization_required = true; |
---|
1478 | 1625 | } else { |
---|
1479 | 1626 | err = si5341_is_programmed_already(data); |
---|
1480 | 1627 | if (err < 0) |
---|
1481 | | - return err; |
---|
| 1628 | + goto cleanup; |
---|
1482 | 1629 | |
---|
1483 | 1630 | initialization_required = !err; |
---|
1484 | 1631 | } |
---|
.. | .. |
---|
1487 | 1634 | /* Populate the regmap cache in preparation for "cache only" */ |
---|
1488 | 1635 | err = si5341_read_settings(data); |
---|
1489 | 1636 | if (err < 0) |
---|
1490 | | - return err; |
---|
| 1637 | + goto cleanup; |
---|
1491 | 1638 | |
---|
1492 | 1639 | err = si5341_send_preamble(data); |
---|
1493 | 1640 | if (err < 0) |
---|
1494 | | - return err; |
---|
| 1641 | + goto cleanup; |
---|
1495 | 1642 | |
---|
1496 | 1643 | /* |
---|
1497 | 1644 | * We intend to send all 'final' register values in a single |
---|
.. | .. |
---|
1504 | 1651 | err = si5341_write_multiple(data, si5341_reg_defaults, |
---|
1505 | 1652 | ARRAY_SIZE(si5341_reg_defaults)); |
---|
1506 | 1653 | if (err < 0) |
---|
1507 | | - return err; |
---|
| 1654 | + goto cleanup; |
---|
1508 | 1655 | } |
---|
1509 | 1656 | |
---|
1510 | 1657 | /* Input must be up and running at this point */ |
---|
1511 | 1658 | err = si5341_clk_select_active_input(data); |
---|
1512 | 1659 | if (err < 0) |
---|
1513 | | - return err; |
---|
| 1660 | + goto cleanup; |
---|
1514 | 1661 | |
---|
1515 | 1662 | if (initialization_required) { |
---|
1516 | 1663 | /* PLL configuration is required */ |
---|
1517 | 1664 | err = si5341_initialize_pll(data); |
---|
1518 | 1665 | if (err < 0) |
---|
1519 | | - return err; |
---|
| 1666 | + goto cleanup; |
---|
1520 | 1667 | } |
---|
1521 | 1668 | |
---|
1522 | 1669 | /* Register the PLL */ |
---|
.. | .. |
---|
1529 | 1676 | err = devm_clk_hw_register(&client->dev, &data->hw); |
---|
1530 | 1677 | if (err) { |
---|
1531 | 1678 | dev_err(&client->dev, "clock registration failed\n"); |
---|
1532 | | - return err; |
---|
| 1679 | + goto cleanup; |
---|
1533 | 1680 | } |
---|
1534 | 1681 | |
---|
1535 | 1682 | init.num_parents = 1; |
---|
.. | .. |
---|
1538 | 1685 | for (i = 0; i < data->num_synth; ++i) { |
---|
1539 | 1686 | synth_clock_names[i] = devm_kasprintf(&client->dev, GFP_KERNEL, |
---|
1540 | 1687 | "%s.N%u", client->dev.of_node->name, i); |
---|
| 1688 | + if (!synth_clock_names[i]) { |
---|
| 1689 | + err = -ENOMEM; |
---|
| 1690 | + goto free_clk_names; |
---|
| 1691 | + } |
---|
1541 | 1692 | init.name = synth_clock_names[i]; |
---|
1542 | 1693 | data->synth[i].index = i; |
---|
1543 | 1694 | data->synth[i].data = data; |
---|
.. | .. |
---|
1546 | 1697 | if (err) { |
---|
1547 | 1698 | dev_err(&client->dev, |
---|
1548 | 1699 | "synth N%u registration failed\n", i); |
---|
| 1700 | + goto free_clk_names; |
---|
1549 | 1701 | } |
---|
1550 | 1702 | } |
---|
1551 | 1703 | |
---|
.. | .. |
---|
1555 | 1707 | for (i = 0; i < data->num_outputs; ++i) { |
---|
1556 | 1708 | init.name = kasprintf(GFP_KERNEL, "%s.%d", |
---|
1557 | 1709 | client->dev.of_node->name, i); |
---|
| 1710 | + if (!init.name) { |
---|
| 1711 | + err = -ENOMEM; |
---|
| 1712 | + goto free_clk_names; |
---|
| 1713 | + } |
---|
1558 | 1714 | init.flags = config[i].synth_master ? CLK_SET_RATE_PARENT : 0; |
---|
1559 | 1715 | data->clk[i].index = i; |
---|
1560 | 1716 | data->clk[i].data = data; |
---|
.. | .. |
---|
1566 | 1722 | regmap_write(data->regmap, |
---|
1567 | 1723 | SI5341_OUT_CM(&data->clk[i]), |
---|
1568 | 1724 | config[i].out_cm_ampl_bits); |
---|
| 1725 | + regmap_update_bits(data->regmap, |
---|
| 1726 | + SI5341_OUT_MUX_SEL(&data->clk[i]), |
---|
| 1727 | + SI5341_OUT_MUX_VDD_SEL_MASK, |
---|
| 1728 | + config[i].vdd_sel_bits); |
---|
1569 | 1729 | } |
---|
1570 | 1730 | err = devm_clk_hw_register(&client->dev, &data->clk[i].hw); |
---|
1571 | 1731 | kfree(init.name); /* clock framework made a copy of the name */ |
---|
1572 | 1732 | if (err) { |
---|
1573 | 1733 | dev_err(&client->dev, |
---|
1574 | 1734 | "output %u registration failed\n", i); |
---|
1575 | | - return err; |
---|
| 1735 | + goto free_clk_names; |
---|
1576 | 1736 | } |
---|
1577 | 1737 | if (config[i].always_on) |
---|
1578 | 1738 | clk_prepare(data->clk[i].hw.clk); |
---|
.. | .. |
---|
1582 | 1742 | data); |
---|
1583 | 1743 | if (err) { |
---|
1584 | 1744 | dev_err(&client->dev, "unable to add clk provider\n"); |
---|
1585 | | - return err; |
---|
| 1745 | + goto free_clk_names; |
---|
1586 | 1746 | } |
---|
1587 | 1747 | |
---|
1588 | 1748 | if (initialization_required) { |
---|
.. | .. |
---|
1590 | 1750 | regcache_cache_only(data->regmap, false); |
---|
1591 | 1751 | err = regcache_sync(data->regmap); |
---|
1592 | 1752 | if (err < 0) |
---|
1593 | | - return err; |
---|
| 1753 | + goto free_clk_names; |
---|
1594 | 1754 | |
---|
1595 | 1755 | err = si5341_finalize_defaults(data); |
---|
1596 | 1756 | if (err < 0) |
---|
1597 | | - return err; |
---|
| 1757 | + goto free_clk_names; |
---|
1598 | 1758 | } |
---|
1599 | 1759 | |
---|
1600 | 1760 | /* wait for device to report input clock present and PLL lock */ |
---|
.. | .. |
---|
1603 | 1763 | 10000, 250000); |
---|
1604 | 1764 | if (err) { |
---|
1605 | 1765 | dev_err(&client->dev, "Error waiting for input clock or PLL lock\n"); |
---|
1606 | | - return err; |
---|
| 1766 | + goto free_clk_names; |
---|
1607 | 1767 | } |
---|
1608 | 1768 | |
---|
1609 | 1769 | /* clear sticky alarm bits from initialization */ |
---|
1610 | 1770 | err = regmap_write(data->regmap, SI5341_STATUS_STICKY, 0); |
---|
1611 | 1771 | if (err) { |
---|
1612 | 1772 | dev_err(&client->dev, "unable to clear sticky status\n"); |
---|
1613 | | - return err; |
---|
| 1773 | + goto free_clk_names; |
---|
1614 | 1774 | } |
---|
1615 | 1775 | |
---|
| 1776 | + err = sysfs_create_files(&client->dev.kobj, si5341_attributes); |
---|
| 1777 | + if (err) |
---|
| 1778 | + dev_err(&client->dev, "unable to create sysfs files\n"); |
---|
| 1779 | + |
---|
| 1780 | +free_clk_names: |
---|
1616 | 1781 | /* Free the names, clk framework makes copies */ |
---|
1617 | 1782 | for (i = 0; i < data->num_synth; ++i) |
---|
1618 | 1783 | devm_kfree(&client->dev, (void *)synth_clock_names[i]); |
---|
| 1784 | + |
---|
| 1785 | +cleanup: |
---|
| 1786 | + if (err) { |
---|
| 1787 | + for (i = 0; i < SI5341_MAX_NUM_OUTPUTS; ++i) { |
---|
| 1788 | + if (data->clk[i].vddo_reg) |
---|
| 1789 | + regulator_disable(data->clk[i].vddo_reg); |
---|
| 1790 | + } |
---|
| 1791 | + } |
---|
| 1792 | + return err; |
---|
| 1793 | +} |
---|
| 1794 | + |
---|
| 1795 | +static int si5341_remove(struct i2c_client *client) |
---|
| 1796 | +{ |
---|
| 1797 | + struct clk_si5341 *data = i2c_get_clientdata(client); |
---|
| 1798 | + int i; |
---|
| 1799 | + |
---|
| 1800 | + sysfs_remove_files(&client->dev.kobj, si5341_attributes); |
---|
| 1801 | + |
---|
| 1802 | + for (i = 0; i < SI5341_MAX_NUM_OUTPUTS; ++i) { |
---|
| 1803 | + if (data->clk[i].vddo_reg) |
---|
| 1804 | + regulator_disable(data->clk[i].vddo_reg); |
---|
| 1805 | + } |
---|
1619 | 1806 | |
---|
1620 | 1807 | return 0; |
---|
1621 | 1808 | } |
---|
.. | .. |
---|
1646 | 1833 | .of_match_table = clk_si5341_of_match, |
---|
1647 | 1834 | }, |
---|
1648 | 1835 | .probe = si5341_probe, |
---|
| 1836 | + .remove = si5341_remove, |
---|
1649 | 1837 | .id_table = si5341_id, |
---|
1650 | 1838 | }; |
---|
1651 | 1839 | module_i2c_driver(si5341_driver); |
---|