linux/drivers/clk/idt/clk-idt8t49n24x-debugfs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/* clk-idt8t49n24x-debugfs.c - Debugfs support for 8T49N24x
   3 *
   4 * Copyright (C) 2018, Integrated Device Technology, Inc. <david.cater@idt.com>
   5 *
   6 * See https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html
   7 * This program is distributed "AS IS" and  WITHOUT ANY WARRANTY;
   8 * including the implied warranties of MERCHANTABILITY, FITNESS FOR
   9 * A PARTICULAR PURPOSE, or NON-INFRINGEMENT.
  10 */
  11
  12#include <linux/debugfs.h>
  13#include <linux/i2c.h>
  14#include <linux/regmap.h>
  15#include <linux/slab.h>
  16
  17#include "clk-idt8t49n24x-debugfs.h"
  18
  19static struct clk_idt24x_chip *idt24x_chip_fordebugfs;
  20
  21static int idt24x_read_all_settings(
  22        struct clk_idt24x_chip *chip, char *output_buffer, int count)
  23{
  24        u8 settings[NUM_CONFIG_REGISTERS];
  25        int err = 0;
  26        int x;
  27
  28        err = regmap_bulk_read(
  29                chip->regmap, 0x0, settings, NUM_CONFIG_REGISTERS);
  30        if (!err) {
  31                output_buffer[0] = '\0';
  32                for (x = 0; x < ARRAY_SIZE(settings); x++) {
  33                        char dbg[4];
  34
  35                        if ((strlen(output_buffer) + 4) > count)
  36                                return -EINVAL;
  37                        sprintf(dbg, "%02x ", settings[x]);
  38                        strcat(output_buffer, dbg);
  39                }
  40        }
  41        return err;
  42}
  43
  44/**
  45 * idt24x_debugfs_writer_action - Write handler for the "action" debugfs file.
  46 * @fp:                 file pointer
  47 * @user_buffer:        buffer of text written to file
  48 * @count:              size of text in buffer
  49 * @position:           pass in current position, return new position
  50 *
  51 * Return: result of call to simple_write_to_buffer
  52 *
  53 * Use the "action" file as a trigger for setting all requested
  54 * rates. The driver doesn't get any notification when the files
  55 * representing the Qx outputs are written to, so something else is
  56 * needed to notify the driver that the device should be udpated.
  57 *
  58 * It doesn't matter what you write to the action debugs file. When the
  59 * handler is called, the device will be updated.
  60 */
  61static ssize_t idt24x_debugfs_writer_action(
  62        struct file *fp, const char __user *user_buffer,
  63        size_t count, loff_t *position)
  64{
  65        int err = 0;
  66        int x;
  67        u32 freq;
  68        bool needs_update = true;
  69        struct i2c_client *client = idt24x_chip_fordebugfs->i2c_client;
  70
  71        if (count > DEBUGFS_BUFFER_LENGTH)
  72                return -EINVAL;
  73
  74        for (x = 0; x < NUM_OUTPUTS; x++) {
  75                freq = idt24x_chip_fordebugfs->clk[x].debug_freq;
  76                if (freq) {
  77                        needs_update = false;
  78                        dev_dbg(&client->dev,
  79                                "%s: calling clk_set_rate with debug frequency for Q%i",
  80                                __func__, x);
  81                        err = clk_set_rate(
  82                                idt24x_chip_fordebugfs->clk[x].hw.clk, freq);
  83                        if (err) {
  84                                dev_err(&client->dev,
  85                                        "error calling clk_set_rate for Q%i (%i)\n",
  86                                        x, err);
  87                        }
  88                } else {
  89                        needs_update = true;
  90                        idt24x_chip_fordebugfs->clk[x].requested = 0;
  91                        dev_dbg(&client->dev,
  92                                "%s: debug frequency for Q%i not set; make sure clock is disabled",
  93                                __func__, x);
  94                }
  95        }
  96
  97        if (needs_update) {
  98                dev_dbg(&client->dev,
  99                        "%s: calling idt24x_set_frequency to ensure any clocks that should be disabled are turned off.",
 100                        __func__);
 101                err = idt24x_set_frequency(idt24x_chip_fordebugfs);
 102                if (err) {
 103                        dev_err(&idt24x_chip_fordebugfs->i2c_client->dev,
 104                                "%s: error calling idt24x_set_frequency (%i)\n",
 105                                __func__, err);
 106                        return err;
 107                }
 108        }
 109
 110        return simple_write_to_buffer(
 111                idt24x_chip_fordebugfs->dbg_cache, DEBUGFS_BUFFER_LENGTH,
 112                position, user_buffer, count);
 113}
 114
 115/**
 116 * idt24x_debugfs_reader_action - Read the "action" debugfs file.
 117 * @fp:                 file pointer
 118 * @user_buffer:        buffer of text written to file
 119 * @count:              size of text in buffer
 120 * @position:           pass in current position, return new position
 121 *
 122 * Return: whatever was last written to the "action" debugfs file.
 123 */
 124static ssize_t idt24x_debugfs_reader_action(
 125        struct file *fp, char __user *user_buffer, size_t count,
 126        loff_t *position)
 127{
 128        return simple_read_from_buffer(
 129                user_buffer, count, position, idt24x_chip_fordebugfs->dbg_cache,
 130                DEBUGFS_BUFFER_LENGTH);
 131}
 132
 133/**
 134 * idt24x_debugfs_reader_map - display the current registers on the device
 135 * @fp:                 file pointer
 136 * @user_buffer:        buffer of text written to file
 137 * @count:              size of text in buffer
 138 * @position:           pass in current position, return new position
 139 *
 140 * Reads the current register map from the attached chip via I2C and
 141 * returns it.
 142 *
 143 * Return: result of call to simple_read_from_buffer
 144 */
 145static ssize_t idt24x_debugfs_reader_map(
 146        struct file *fp, char __user *user_buffer, size_t count,
 147        loff_t *position)
 148{
 149        int err = 0;
 150        char *buf = kzalloc(5000, GFP_KERNEL);
 151
 152        dev_dbg(&idt24x_chip_fordebugfs->i2c_client->dev,
 153                "calling idt24x_read_all_settings (count: %zu)\n", count);
 154        err = idt24x_read_all_settings(idt24x_chip_fordebugfs, buf, 5000);
 155        if (err) {
 156                dev_err(&idt24x_chip_fordebugfs->i2c_client->dev,
 157                        "error calling idt24x_read_all_settings (%i)\n", err);
 158                return 0;
 159        }
 160        /* TMGCDR-1456. We're returning 1 byte too few. */
 161        err = simple_read_from_buffer(
 162                user_buffer, count, position, buf, strlen(buf));
 163        kfree(buf);
 164        return err;
 165}
 166
 167/**
 168 * idt24x_handle_i2c_debug_token - process "token" written to the i2c file
 169 * @dev:        pointer to device structure
 170 * @token:      pointer to current char being examined
 171 * @reg:        pass in current register, or return register from token.
 172 * @val:        resulting array of bytes being parsed
 173 * @nextbyte:   position in val array to store next byte
 174 *
 175 * Utility function to operate on the current "token" (from within a
 176 * space-delimited string) written to the i2c debugfs file. It will
 177 * either be a register offset or a byte to be added to the val array.
 178 * If it is added to the val array, auto-increment nextbyte.
 179 *
 180 * Return:      0 for success
 181 */
 182static int idt24x_handle_i2c_debug_token(
 183        const struct device *dev, char *token, unsigned int *reg,
 184        u8 val[], u16 *nextbyte)
 185{
 186        int err = 0;
 187
 188        dev_dbg(dev, "got token (%s)\n", token);
 189        if (*reg == -1) {
 190                err = kstrtouint(token, 16, reg);
 191                if (!err)
 192                        dev_dbg(dev, "hex register address == 0x%x\n", *reg);
 193        } else {
 194                u8 temp;
 195
 196                err = kstrtou8(token, 16, &temp);
 197                if (!err) {
 198                        dev_dbg(dev, "data byte == 0x%x\n", temp);
 199                        val[*nextbyte] = temp;
 200                        *nextbyte += 1;
 201                }
 202        }
 203        if (err == -ERANGE)
 204                dev_err(dev, "ERANGE error when parsing data\n");
 205        else if (err == -EINVAL)
 206                dev_err(dev, "EINVAL error when parsing data\n");
 207        else if (err)
 208                dev_err(dev, "error when parsing data: %i\n", err);
 209        return err;
 210}
 211
 212/**
 213 * idt24x_debugfs_writer_i2c - debugfs handler for i2c file
 214 * @fp:                 file pointer
 215 * @user_buffer:        buffer of text written to file
 216 * @count:              size of text in buffer
 217 * @position:           pass in current position, return new position
 218 *
 219 * Handler for the "i2c" debugfs file. Write to this file to write bytes
 220 * via I2C to a particular offset.
 221 *
 222 * Usage: echo 006c 01 02 0D FF > i2c
 223 *
 224 * First 4 chars are the 2-byte i2c register offset. Then follow that
 225 * with a sequence of 2-char bytes in hex format that you want to write
 226 * starting at that offset.
 227 *
 228 * Return: result of simple_write_to_buffer
 229 */
 230static ssize_t idt24x_debugfs_writer_i2c(struct file *fp,
 231                                         const char __user *user_buffer,
 232                                         size_t count, loff_t *position)
 233{
 234        int err = 0;
 235        int x = 0;
 236        int start = 0;
 237        ssize_t written;
 238        unsigned int reg = -1;
 239        u8 val[WRITE_BLOCK_SIZE];
 240        u16 nextbyte = 0;
 241        char token[16];
 242
 243        if (count > DEBUGFS_BUFFER_LENGTH)
 244                return -EINVAL;
 245
 246        written = simple_write_to_buffer(
 247                idt24x_chip_fordebugfs->dbg_cache, DEBUGFS_BUFFER_LENGTH,
 248                position, user_buffer, count);
 249        if (written != count) {
 250                dev_dbg(&idt24x_chip_fordebugfs->i2c_client->dev,
 251                        "write count != expected count");
 252                return written;
 253        }
 254
 255        for (x = 0; x < count; x++) {
 256                token[x - start] = idt24x_chip_fordebugfs->dbg_cache[x];
 257                if (idt24x_chip_fordebugfs->dbg_cache[x] == ' ') {
 258                        token[x - start] = '\0';
 259                        err = idt24x_handle_i2c_debug_token(
 260                                &idt24x_chip_fordebugfs->i2c_client->dev,
 261                                token, &reg, val, &nextbyte);
 262                        if (err)
 263                                break;
 264                        start = x + 1;
 265                }
 266        }
 267
 268        /* handle the last token */
 269        if (!err) {
 270                token[count - start] = '\0';
 271                err = idt24x_handle_i2c_debug_token(
 272                        &idt24x_chip_fordebugfs->i2c_client->dev, token, &reg,
 273                        val, &nextbyte);
 274        }
 275
 276        if (!err && reg != -1 && nextbyte > 0) {
 277                err = i2cwritebulk(
 278                        idt24x_chip_fordebugfs->i2c_client,
 279                        idt24x_chip_fordebugfs->regmap,
 280                        reg, val, nextbyte);
 281                if (err) {
 282                        dev_err(&idt24x_chip_fordebugfs->i2c_client->dev,
 283                                "error writing data chip (%i)\n", err);
 284                        return err;
 285                }
 286                dev_dbg(&idt24x_chip_fordebugfs->i2c_client->dev,
 287                        "successfully wrote i2c data to chip");
 288        }
 289
 290        return written;
 291}
 292
 293static const struct file_operations idt24x_fops_debug_action = {
 294        .read = idt24x_debugfs_reader_action,
 295        .write = idt24x_debugfs_writer_action,
 296};
 297
 298static const struct file_operations idt24x_fops_debug_map = {
 299        .read = idt24x_debugfs_reader_map
 300};
 301
 302static const struct file_operations idt24x_fops_debug_i2c = {
 303        .write = idt24x_debugfs_writer_i2c,
 304};
 305
 306/**
 307 * idt24x_expose_via_debugfs - Set up all debugfs files
 308 * @client:     pointer to i2c_client structure
 309 * @chip:       Device data structure
 310 *
 311 * Sets up all debugfs files to use for debugging the driver.
 312 * Return: error code. 0 if success or debugfs doesn't appear to be enabled.
 313 */
 314int idt24x_expose_via_debugfs(struct i2c_client *client,
 315                              struct clk_idt24x_chip *chip)
 316{
 317        int output_num;
 318
 319        /*
 320         * create root directory in /sys/kernel/debugfs
 321         */
 322        chip->debugfs_dirroot = debugfs_create_dir("idt24x", NULL);
 323        if (!chip->debugfs_dirroot) {
 324                /* debugfs probably not enabled. Don't fail the probe. */
 325                return 0;
 326        }
 327
 328        /*
 329         * create files in the root directory. This requires read and
 330         * write file operations
 331         */
 332        chip->debugfs_fileaction = debugfs_create_file(
 333                "action", 0644, chip->debugfs_dirroot, NULL,
 334                &idt24x_fops_debug_action);
 335        if (!chip->debugfs_fileaction) {
 336                dev_err(&client->dev,
 337                        "%s: error creating action file", __func__);
 338                return (-ENODEV);
 339        }
 340
 341        chip->debugfs_map = debugfs_create_file(
 342                "map", 0444, chip->debugfs_dirroot, NULL,
 343                &idt24x_fops_debug_map);
 344        if (!chip->debugfs_map) {
 345                dev_err(&client->dev,
 346                        "%s: error creating map file", __func__);
 347                return (-ENODEV);
 348        }
 349
 350        for (output_num = 0; output_num < NUM_OUTPUTS; output_num++) {
 351                char name[5];
 352
 353                sprintf(name, "q%d", output_num);
 354                debugfs_create_u64(name, 0644, chip->debugfs_dirroot,
 355                                   &chip->clk[output_num].debug_freq);
 356        }
 357
 358        chip->debugfs_filei2c = debugfs_create_file(
 359                "i2c", 0644, chip->debugfs_dirroot, NULL,
 360                &idt24x_fops_debug_i2c);
 361        if (!chip->debugfs_filei2c) {
 362                dev_err(&client->dev,
 363                        "%s: error creating i2c file", __func__);
 364                return (-ENODEV);
 365        }
 366
 367        dev_dbg(&client->dev, "%s: success", __func__);
 368        idt24x_chip_fordebugfs = chip;
 369        return 0;
 370}
 371
 372void idt24x_cleanup_debugfs(struct clk_idt24x_chip *chip)
 373{
 374        debugfs_remove_recursive(chip->debugfs_dirroot);
 375}
 376