The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/arm/ti/twl/twl_clks.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2012
    5  *      Ben Gray <bgray@freebsd.org>.
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   20  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
   21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD$");
   32 
   33 /*
   34  * Texas Instruments TWL4030/TWL5030/TWL60x0/TPS659x0 Power Management.
   35  *
   36  * This driver covers the external clocks, allows for enabling &
   37  * disabling their output.
   38  *
   39  *
   40  *
   41  * FLATTENED DEVICE TREE (FDT)
   42  * Startup override settings can be specified in the FDT, if they are they
   43  * should be under the twl parent device and take the following form:
   44  *
   45  *    external-clocks = "name1", "state1",
   46  *                      "name2", "state2",
   47  *                      etc;
   48  *
   49  * Each override should be a pair, the first entry is the name of the clock
   50  * the second is the state to set, possible strings are either "on" or "off".
   51  *
   52  */
   53 
   54 #include <sys/param.h>
   55 #include <sys/systm.h>
   56 #include <sys/kernel.h>
   57 #include <sys/lock.h>
   58 #include <sys/module.h>
   59 #include <sys/bus.h>
   60 #include <sys/resource.h>
   61 #include <sys/rman.h>
   62 #include <sys/sysctl.h>
   63 #include <sys/sx.h>
   64 #include <sys/malloc.h>
   65 
   66 #include <machine/bus.h>
   67 #include <machine/resource.h>
   68 #include <machine/intr.h>
   69 
   70 #include <dev/ofw/openfirm.h>
   71 #include <dev/ofw/ofw_bus.h>
   72 
   73 #include "twl.h"
   74 #include "twl_clks.h"
   75 
   76 static int twl_clks_debug = 1;
   77 
   78 /*
   79  * Power Groups bits for the 4030 and 6030 devices
   80  */
   81 #define TWL4030_P3_GRP          0x80    /* Peripherals, power group */
   82 #define TWL4030_P2_GRP          0x40    /* Modem power group */
   83 #define TWL4030_P1_GRP          0x20    /* Application power group (FreeBSD control) */
   84 
   85 #define TWL6030_P3_GRP          0x04    /* Modem power group */
   86 #define TWL6030_P2_GRP          0x02    /* Connectivity power group */
   87 #define TWL6030_P1_GRP          0x01    /* Application power group (FreeBSD control) */
   88 
   89 /*
   90  * Register offsets within a clk regulator register set
   91  */
   92 #define TWL_CLKS_GRP            0x00    /* Regulator GRP register */
   93 #define TWL_CLKS_STATE          0x02    /* TWL6030 only */
   94 
   95 /**
   96  *  Support voltage regulators for the different IC's
   97  */
   98 struct twl_clock {
   99         const char      *name;
  100         uint8_t         subdev;
  101         uint8_t         regbase;
  102 };
  103 
  104 static const struct twl_clock twl4030_clocks[] = {
  105         { "32kclkout", 0, 0x8e },
  106         { NULL, 0, 0x00 } 
  107 };
  108 
  109 static const struct twl_clock twl6030_clocks[] = {
  110         { "clk32kg",     0, 0xbc },
  111         { "clk32kao",    0, 0xb9 },
  112         { "clk32kaudio", 0, 0xbf },
  113         { NULL, 0, 0x00 } 
  114 };
  115 
  116 #define TWL_CLKS_MAX_NAMELEN  32
  117 
  118 struct twl_clk_entry {
  119         LIST_ENTRY(twl_clk_entry) link;
  120         struct sysctl_oid *oid;
  121         char                   name[TWL_CLKS_MAX_NAMELEN];
  122         uint8_t            sub_dev;  /* the sub-device number for the clock */
  123         uint8_t            reg_off;  /* register base address of the clock */
  124 };
  125 
  126 struct twl_clks_softc {
  127         device_t           sc_dev;   /* twl_clk device */
  128         device_t           sc_pdev;  /* parent device (twl) */
  129         struct sx          sc_sx;    /* internal locking */
  130         struct intr_config_hook sc_init_hook;
  131         LIST_HEAD(twl_clk_list, twl_clk_entry) sc_clks_list;
  132 };
  133 
  134 /**
  135  *      Macros for driver shared locking
  136  */
  137 #define TWL_CLKS_XLOCK(_sc)                     sx_xlock(&(_sc)->sc_sx)
  138 #define TWL_CLKS_XUNLOCK(_sc)           sx_xunlock(&(_sc)->sc_sx)
  139 #define TWL_CLKS_SLOCK(_sc)                     sx_slock(&(_sc)->sc_sx)
  140 #define TWL_CLKS_SUNLOCK(_sc)           sx_sunlock(&(_sc)->sc_sx)
  141 #define TWL_CLKS_LOCK_INIT(_sc)         sx_init(&(_sc)->sc_sx, "twl_clks")
  142 #define TWL_CLKS_LOCK_DESTROY(_sc)      sx_destroy(&(_sc)->sc_sx);
  143 
  144 #define TWL_CLKS_ASSERT_LOCKED(_sc)     sx_assert(&(_sc)->sc_sx, SA_LOCKED);
  145 
  146 #define TWL_CLKS_LOCK_UPGRADE(_sc)               \
  147         do {                                         \
  148                 while (!sx_try_upgrade(&(_sc)->sc_sx))   \
  149                         pause("twl_clks_ex", (hz / 100));    \
  150         } while(0)
  151 #define TWL_CLKS_LOCK_DOWNGRADE(_sc)    sx_downgrade(&(_sc)->sc_sx);
  152 
  153 /**
  154  *      twl_clks_read_1 - read single register from the TWL device
  155  *      twl_clks_write_1 - writes a single register in the TWL device
  156  *      @sc: device context
  157  *      @clk: the clock device we're reading from / writing to
  158  *      @off: offset within the clock's register set
  159  *      @val: the value to write or a pointer to a variable to store the result
  160  *
  161  *      RETURNS:
  162  *      Zero on success or an error code on failure.
  163  */
  164 static inline int
  165 twl_clks_read_1(struct twl_clks_softc *sc, struct twl_clk_entry *clk,
  166         uint8_t off, uint8_t *val)
  167 {
  168         return (twl_read(sc->sc_pdev, clk->sub_dev, clk->reg_off + off, val, 1));
  169 }
  170 
  171 static inline int
  172 twl_clks_write_1(struct twl_clks_softc *sc, struct twl_clk_entry *clk,
  173         uint8_t off, uint8_t val)
  174 {
  175         return (twl_write(sc->sc_pdev, clk->sub_dev, clk->reg_off + off, &val, 1));
  176 }
  177 
  178 /**
  179  *      twl_clks_is_enabled - determines if a clock is enabled
  180  *      @dev: TWL CLK device
  181  *      @name: the name of the clock
  182  *      @enabled: upon return will contain the 'enabled' state
  183  *
  184  *      LOCKING:
  185  *      Internally the function takes and releases the TWL lock.
  186  *
  187  *      RETURNS:
  188  *      Zero on success or a negative error code on failure.
  189  */
  190 int
  191 twl_clks_is_enabled(device_t dev, const char *name, int *enabled)
  192 {
  193         struct twl_clks_softc *sc = device_get_softc(dev);
  194         struct twl_clk_entry *clk;
  195         int found = 0;
  196         int err;
  197         uint8_t grp, state;
  198 
  199         TWL_CLKS_SLOCK(sc);
  200 
  201         LIST_FOREACH(clk, &sc->sc_clks_list, link) {
  202                 if (strcmp(clk->name, name) == 0) {
  203                         found = 1;
  204                         break;
  205                 }
  206         }
  207 
  208         if (!found) {
  209                 TWL_CLKS_SUNLOCK(sc);
  210                 return (EINVAL);
  211         }
  212 
  213         if (twl_is_4030(sc->sc_pdev)) {
  214                 err = twl_clks_read_1(sc, clk, TWL_CLKS_GRP, &grp);
  215                 if (!err)
  216                         *enabled = (grp & TWL4030_P1_GRP);
  217 
  218         } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) {
  219                 TWL_CLKS_LOCK_UPGRADE(sc);
  220 
  221                 /* Check the clock is in the application group */
  222                 if (twl_is_6030(sc->sc_pdev)) {
  223                         err = twl_clks_read_1(sc, clk, TWL_CLKS_GRP, &grp);
  224                         if (err) {
  225                                 TWL_CLKS_LOCK_DOWNGRADE(sc);
  226                                 goto done;
  227                         }
  228                         
  229                         if (!(grp & TWL6030_P1_GRP)) {
  230                                 TWL_CLKS_LOCK_DOWNGRADE(sc);
  231                                 *enabled = 0; /* disabled */
  232                                 goto done;
  233                         }
  234                 }
  235 
  236                 /* Read the application mode state and verify it's ON */
  237                 err = twl_clks_read_1(sc, clk, TWL_CLKS_STATE, &state);
  238                 if (!err)
  239                         *enabled = ((state & 0x0C) == 0x04);
  240                         
  241                 TWL_CLKS_LOCK_DOWNGRADE(sc);
  242 
  243         } else {
  244                 err = EINVAL;
  245         }
  246 
  247 done:
  248         TWL_CLKS_SUNLOCK(sc);
  249         return (err);
  250 }
  251 
  252 /**
  253  *      twl_clks_set_state - enables/disables a clock output
  254  *      @sc: device context
  255  *      @clk: the clock entry to enable/disable
  256  *      @enable: non-zero the clock is enabled, zero the clock is disabled
  257  *
  258  *      LOCKING:
  259  *      The TWL CLK lock must be held before this function is called.
  260  *
  261  *      RETURNS:
  262  *      Zero on success or an error code on failure.
  263  */
  264 static int
  265 twl_clks_set_state(struct twl_clks_softc *sc, struct twl_clk_entry *clk,
  266         int enable)
  267 {
  268         int xlocked;
  269         int err;
  270         uint8_t grp;
  271 
  272         TWL_CLKS_ASSERT_LOCKED(sc);
  273 
  274         /* Upgrade the lock to exclusive because about to perform read-mod-write */
  275         xlocked = sx_xlocked(&sc->sc_sx);
  276         if (!xlocked)
  277                 TWL_CLKS_LOCK_UPGRADE(sc);
  278 
  279         err = twl_clks_read_1(sc, clk, TWL_CLKS_GRP, &grp);
  280         if (err)
  281                 goto done;
  282 
  283         if (twl_is_4030(sc->sc_pdev)) {
  284                 /* On the TWL4030 we just need to ensure the clock is in the right
  285                  * power domain, don't need to turn on explicitly like TWL6030.
  286                  */
  287                 if (enable)
  288                         grp |= TWL4030_P1_GRP;
  289                 else
  290                         grp &= ~(TWL4030_P1_GRP | TWL4030_P2_GRP | TWL4030_P3_GRP);
  291                 
  292                 err = twl_clks_write_1(sc, clk, TWL_CLKS_GRP, grp);
  293 
  294         } else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev)) {
  295                 /* Make sure the clock belongs to at least the APP power group */
  296                 if (twl_is_6030(sc->sc_pdev) && !(grp & TWL6030_P1_GRP)) {
  297                         grp |= TWL6030_P1_GRP;
  298                         err = twl_clks_write_1(sc, clk, TWL_CLKS_GRP, grp);
  299                         if (err)
  300                                 goto done;
  301                 }
  302 
  303                 /* On TWL6030 we need to make sure we disable power for all groups */
  304                 if (twl_is_6030(sc->sc_pdev))
  305                         grp = TWL6030_P1_GRP | TWL6030_P2_GRP | TWL6030_P3_GRP;
  306                 else
  307                         grp = 0x00;
  308 
  309                 /* Set the state of the clock */
  310                 if (enable)
  311                         err = twl_clks_write_1(sc, clk, TWL_CLKS_STATE, (grp << 5) | 0x01);
  312                 else
  313                         err = twl_clks_write_1(sc, clk, TWL_CLKS_STATE, (grp << 5));
  314 
  315         } else {
  316                 
  317                 err = EINVAL;
  318         }
  319 
  320 done:
  321         if (!xlocked)
  322                 TWL_CLKS_LOCK_DOWNGRADE(sc);
  323 
  324         if ((twl_clks_debug > 1) && !err)
  325                 device_printf(sc->sc_dev, "%s : %sabled\n", clk->name,
  326                         enable ? "en" : "dis");
  327 
  328         return (err);
  329 }
  330 
  331 /**
  332  *      twl_clks_disable - disables a clock output
  333  *      @dev: TWL clk device
  334 *       @name: the name of the clock
  335  *
  336  *      LOCKING:
  337  *      Internally the function takes and releases the TWL lock.
  338  *
  339  *      RETURNS:
  340 *       Zero on success or an error code on failure.
  341  */
  342 int
  343 twl_clks_disable(device_t dev, const char *name)
  344 {
  345         struct twl_clks_softc *sc = device_get_softc(dev);
  346         struct twl_clk_entry *clk;
  347         int err = EINVAL;
  348 
  349         TWL_CLKS_SLOCK(sc);
  350 
  351         LIST_FOREACH(clk, &sc->sc_clks_list, link) {
  352                 if (strcmp(clk->name, name) == 0) {
  353                         err = twl_clks_set_state(sc, clk, 0);
  354                         break;
  355                 }
  356         }
  357 
  358         TWL_CLKS_SUNLOCK(sc);
  359         return (err);
  360 }
  361 
  362 /**
  363  *      twl_clks_enable - enables a clock output
  364  *      @dev: TWL clk device
  365  *      @name: the name of the clock
  366  *
  367  *      LOCKING:
  368  *      Internally the function takes and releases the TWL CLKS lock.
  369  *
  370  *      RETURNS:
  371  *      Zero on success or an error code on failure.
  372  */
  373 int
  374 twl_clks_enable(device_t dev, const char *name)
  375 {
  376         struct twl_clks_softc *sc = device_get_softc(dev);
  377         struct twl_clk_entry *clk;
  378         int err = EINVAL;
  379 
  380         TWL_CLKS_SLOCK(sc);
  381 
  382         LIST_FOREACH(clk, &sc->sc_clks_list, link) {
  383                 if (strcmp(clk->name, name) == 0) {
  384                         err = twl_clks_set_state(sc, clk, 1);
  385                         break;
  386                 }
  387         }
  388 
  389         TWL_CLKS_SUNLOCK(sc);
  390         return (err);
  391 }
  392 
  393 /**
  394  *      twl_clks_sysctl_clock - reads the state of the clock
  395  *      @SYSCTL_HANDLER_ARGS: arguments for the callback
  396  *
  397  *      Returns the clock status; disabled is zero and enabled is non-zero.
  398  *
  399  *      LOCKING:
  400  *      It's expected the TWL lock is held while this function is called.
  401  *
  402  *      RETURNS:
  403  *      EIO if device is not present, otherwise 0 is returned.
  404  */
  405 static int
  406 twl_clks_sysctl_clock(SYSCTL_HANDLER_ARGS)
  407 {
  408         struct twl_clks_softc *sc = (struct twl_clks_softc*)arg1;
  409         int err;
  410         int enabled = 0;
  411 
  412         if ((err = twl_clks_is_enabled(sc->sc_dev, oidp->oid_name, &enabled)) != 0)
  413                 return err;
  414 
  415         return sysctl_handle_int(oidp, &enabled, 0, req);
  416 }
  417 
  418 /**
  419  *      twl_clks_add_clock - adds single clock sysctls for the device
  420  *      @sc: device soft context
  421  *      @name: the name of the regulator
  422  *      @nsub: the number of the subdevice
  423  *      @regbase: the base address of the clocks registers
  424  *
  425  *      Adds a single clock to the device and also a sysctl interface for 
  426  *      querying it's status.
  427  *
  428  *      LOCKING:
  429  *      It's expected the exclusive lock is held while this function is called.
  430  *
  431  *      RETURNS:
  432  *      Pointer to the new clock entry on success, otherwise NULL on failure.
  433  */
  434 static struct twl_clk_entry*
  435 twl_clks_add_clock(struct twl_clks_softc *sc, const char *name,
  436         uint8_t nsub, uint8_t regbase)
  437 {
  438         struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
  439         struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
  440         struct twl_clk_entry *new;
  441 
  442         TWL_CLKS_ASSERT_LOCKED(sc);
  443 
  444         new = malloc(sizeof(struct twl_clk_entry), M_DEVBUF, M_NOWAIT | M_ZERO);
  445         if (new == NULL)
  446                 return (NULL);
  447 
  448         strncpy(new->name, name, TWL_CLKS_MAX_NAMELEN);
  449         new->name[TWL_CLKS_MAX_NAMELEN - 1] = '\0';
  450 
  451         new->sub_dev = nsub;
  452         new->reg_off = regbase;
  453 
  454         /* Add a sysctl entry for the clock */
  455         new->oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, name,
  456             CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0,
  457             twl_clks_sysctl_clock, "I", "external clock");
  458 
  459         /* Finally add the regulator to list of supported regulators */
  460         LIST_INSERT_HEAD(&sc->sc_clks_list, new, link);
  461 
  462         return (new);
  463 }
  464 
  465 /**
  466  *      twl_clks_add_clocks - populates the internal list of clocks
  467  *      @sc: device soft context
  468  *      @chip: the name of the chip used in the hints
  469  *      @clks the list of clocks supported by the device
  470  *
  471  *      Loops over the list of clocks and adds them to the device context. Also
  472  *      scans the FDT to determine if there are any clocks that should be
  473  *      enabled/disabled automatically.
  474  *
  475  *      LOCKING:
  476  *      Internally takes the exclusive lock while adding the clocks to the
  477  *      device context.
  478  *
  479  *      RETURNS:
  480  *      Always returns 0.
  481  */
  482 static int
  483 twl_clks_add_clocks(struct twl_clks_softc *sc, const struct twl_clock *clks)
  484 {
  485         int err;
  486         const struct twl_clock *walker;
  487         struct twl_clk_entry *entry;
  488         phandle_t child;
  489         char rnames[256];
  490         char *name, *state;
  491         int len = 0, prop_len;
  492         int enable;
  493 
  494         TWL_CLKS_XLOCK(sc);
  495 
  496         /* Add the regulators from the list */
  497         walker = &clks[0];
  498         while (walker->name != NULL) {
  499                 /* Add the regulator to the list */
  500                 entry = twl_clks_add_clock(sc, walker->name, walker->subdev,
  501                     walker->regbase);
  502                 if (entry == NULL)
  503                         continue;
  504 
  505                 walker++;
  506         }
  507 
  508         /* Check for any FDT settings that need to be applied */
  509         child = ofw_bus_get_node(sc->sc_pdev);
  510         if (child) {
  511                 prop_len = OF_getprop(child, "external-clocks", rnames, sizeof(rnames));
  512                 while (len < prop_len) {
  513                         name = rnames + len;
  514                         len += strlen(name) + 1;
  515                         if ((len >= prop_len) || (name[0] == '\0'))
  516                                 break;
  517                         
  518                         state = rnames + len;
  519                         len += strlen(state) + 1;
  520                         if (state[0] == '\0')
  521                                 break;
  522                         
  523                         enable = !strncmp(state, "on", 2);
  524                         
  525                         LIST_FOREACH(entry, &sc->sc_clks_list, link) {
  526                                 if (strcmp(entry->name, name) == 0) {
  527                                         twl_clks_set_state(sc, entry, enable);
  528                                         break;
  529                                 }
  530                         }
  531                 }
  532         }
  533 
  534         TWL_CLKS_XUNLOCK(sc);
  535 
  536         if (twl_clks_debug) {
  537                 LIST_FOREACH(entry, &sc->sc_clks_list, link) {
  538                         err = twl_clks_is_enabled(sc->sc_dev, entry->name, &enable);
  539                         if (!err)
  540                                 device_printf(sc->sc_dev, "%s : %s\n", entry->name,
  541                                         enable ? "on" : "off");
  542                 }
  543         }
  544 
  545         return (0);
  546 }
  547 
  548 /**
  549  *      twl_clks_init - initialises the list of clocks
  550  *      @dev: the twl_clks device
  551  *
  552  *      This function is called as an intrhook once interrupts have been enabled,
  553  *      this is done so that the driver has the option to enable/disable a clock
  554  *      based on settings providied in the FDT.
  555  *
  556  *      LOCKING:
  557  *      May takes the exclusive lock in the function.
  558  */
  559 static void
  560 twl_clks_init(void *dev)
  561 {
  562         struct twl_clks_softc *sc;
  563 
  564         sc = device_get_softc((device_t)dev);
  565 
  566         if (twl_is_4030(sc->sc_pdev))
  567                 twl_clks_add_clocks(sc, twl4030_clocks);
  568         else if (twl_is_6030(sc->sc_pdev) || twl_is_6025(sc->sc_pdev))
  569                 twl_clks_add_clocks(sc, twl6030_clocks);
  570 
  571         config_intrhook_disestablish(&sc->sc_init_hook);
  572 }
  573 
  574 static int
  575 twl_clks_probe(device_t dev)
  576 {
  577         if (twl_is_4030(device_get_parent(dev)))
  578                 device_set_desc(dev, "TI TWL4030 PMIC External Clocks");
  579         else if (twl_is_6025(device_get_parent(dev)) ||
  580                  twl_is_6030(device_get_parent(dev)))
  581                 device_set_desc(dev, "TI TWL6025/TWL6030 PMIC External Clocks");
  582         else
  583                 return (ENXIO);
  584 
  585         return (0);
  586 }
  587 
  588 static int
  589 twl_clks_attach(device_t dev)
  590 {
  591         struct twl_clks_softc *sc;
  592 
  593         sc = device_get_softc(dev);
  594         sc->sc_dev = dev;
  595         sc->sc_pdev = device_get_parent(dev);
  596 
  597         TWL_CLKS_LOCK_INIT(sc);
  598 
  599         LIST_INIT(&sc->sc_clks_list);
  600 
  601         sc->sc_init_hook.ich_func = twl_clks_init;
  602         sc->sc_init_hook.ich_arg = dev;
  603 
  604         if (config_intrhook_establish(&sc->sc_init_hook) != 0)
  605                 return (ENOMEM);
  606 
  607         return (0);
  608 }
  609 
  610 static int
  611 twl_clks_detach(device_t dev)
  612 {
  613         struct twl_clks_softc *sc;
  614         struct twl_clk_entry *clk;
  615         struct twl_clk_entry *tmp;
  616 
  617         sc = device_get_softc(dev);
  618 
  619         TWL_CLKS_XLOCK(sc);
  620 
  621         LIST_FOREACH_SAFE(clk, &sc->sc_clks_list, link, tmp) {
  622                 LIST_REMOVE(clk, link);
  623                 sysctl_remove_oid(clk->oid, 1, 0);
  624                 free(clk, M_DEVBUF);
  625         }
  626 
  627         TWL_CLKS_XUNLOCK(sc);
  628 
  629         TWL_CLKS_LOCK_DESTROY(sc);
  630 
  631         return (0);
  632 }
  633 
  634 static device_method_t twl_clks_methods[] = {
  635         DEVMETHOD(device_probe,         twl_clks_probe),
  636         DEVMETHOD(device_attach,        twl_clks_attach),
  637         DEVMETHOD(device_detach,        twl_clks_detach),
  638 
  639         {0, 0},
  640 };
  641 
  642 static driver_t twl_clks_driver = {
  643         "twl_clks",
  644         twl_clks_methods,
  645         sizeof(struct twl_clks_softc),
  646 };
  647 
  648 DRIVER_MODULE(twl_clks, twl, twl_clks_driver, 0, 0);
  649 MODULE_VERSION(twl_clks, 1);

Cache object: 118a141769fa2736b99144cc63ca6aa6


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.