summaryrefslogtreecommitdiffstats
path: root/Documentation/ABI/testing/sysfs-bus-usb-devices-usbsevseg
blob: 2f86e4223bfc8d25cc9552fe3865e3ccc964803b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
What:		/sys/bus/usb/.../powered
Date:		August 2008
KernelVersion:	2.6.26
Contact:	Harrison Metzger <harrisonmetz@gmail.com>
Description:	Controls whether the device's display will powered.
		A value of 0 is off and a non-zero value is on.

What:		/sys/bus/usb/.../mode_msb
What:		/sys/bus/usb/.../mode_lsb
Date:		August 2008
KernelVersion:	2.6.26
Contact:	Harrison Metzger <harrisonmetz@gmail.com>
Description:	Controls the devices display mode.
		For a 6 character display the values are

			MSB 0x06; LSB 0x3F, and

		for an 8 character display the values are

			MSB 0x08; LSB 0xFF.

What:		/sys/bus/usb/.../textmode
Date:		August 2008
KernelVersion:	2.6.26
Contact:	Harrison Metzger <harrisonmetz@gmail.com>
Description:	Controls the way the device interprets its text buffer.
		raw:	each character controls its segment manually
		hex:	each character is between 0-15
		ascii:	each character is between '0'-'9' and 'A'-'F'.

What:		/sys/bus/usb/.../text
Date:		August 2008
KernelVersion:	2.6.26
Contact:	Harrison Metzger <harrisonmetz@gmail.com>
Description:	The text (or data) for the device to display

What:		/sys/bus/usb/.../decimals
Date:		August 2008
KernelVersion:	2.6.26
Contact:	Harrison Metzger <harrisonmetz@gmail.com>
Description:	Controls the decimal places on the device.
		To set the nth decimal place, give this field
		the value of ``10 ** n``. Assume this field has
		the value k and has 1 or more decimal places set,
		to set the mth place (where m is not already set),
		change this fields value to ``k + 10 ** m``.
6 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
// SPDX-License-Identifier: GPL-2.0-only
/*
 * This file contains driver for the Cadence Triple Timer Counter Rev 06
 *
 *  Copyright (C) 2011-2013 Xilinx
 *
 * based on arch/mips/kernel/time.c timer driver
 */

#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/sched_clock.h>

/*
 * This driver configures the 2 16/32-bit count-up timers as follows:
 *
 * T1: Timer 1, clocksource for generic timekeeping
 * T2: Timer 2, clockevent source for hrtimers
 * T3: Timer 3, <unused>
 *
 * The input frequency to the timer module for emulation is 2.5MHz which is
 * common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32,
 * the timers are clocked at 78.125KHz (12.8 us resolution).

 * The input frequency to the timer module in silicon is configurable and
 * obtained from device tree. The pre-scaler of 32 is used.
 */

/*
 * Timer Register Offset Definitions of Timer 1, Increment base address by 4
 * and use same offsets for Timer 2
 */
#define TTC_CLK_CNTRL_OFFSET		0x00 /* Clock Control Reg, RW */
#define TTC_CNT_CNTRL_OFFSET		0x0C /* Counter Control Reg, RW */
#define TTC_COUNT_VAL_OFFSET		0x18 /* Counter Value Reg, RO */
#define TTC_INTR_VAL_OFFSET		0x24 /* Interval Count Reg, RW */
#define TTC_ISR_OFFSET		0x54 /* Interrupt Status Reg, RO */
#define TTC_IER_OFFSET		0x60 /* Interrupt Enable Reg, RW */

#define TTC_CNT_CNTRL_DISABLE_MASK	0x1

#define TTC_CLK_CNTRL_CSRC_MASK		(1 << 5)	/* clock source */
#define TTC_CLK_CNTRL_PSV_MASK		0x1e
#define TTC_CLK_CNTRL_PSV_SHIFT		1

/*
 * Setup the timers to use pre-scaling, using a fixed value for now that will
 * work across most input frequency, but it may need to be more dynamic
 */
#define PRESCALE_EXPONENT	11	/* 2 ^ PRESCALE_EXPONENT = PRESCALE */
#define PRESCALE		2048	/* The exponent must match this */
#define CLK_CNTRL_PRESCALE	((PRESCALE_EXPONENT - 1) << 1)
#define CLK_CNTRL_PRESCALE_EN	1
#define CNT_CNTRL_RESET		(1 << 4)

#define MAX_F_ERR 50

/**
 * struct ttc_timer - This definition defines local timer structure
 *
 * @base_addr:	Base address of timer
 * @freq:	Timer input clock frequency
 * @clk:	Associated clock source
 * @clk_rate_change_nb	Notifier block for clock rate changes
 */
struct ttc_timer {
	void __iomem *base_addr;
	unsigned long freq;
	struct clk *clk;
	struct notifier_block clk_rate_change_nb;
};

#define to_ttc_timer(x) \
		container_of(x, struct ttc_timer, clk_rate_change_nb)

struct ttc_timer_clocksource {
	u32			scale_clk_ctrl_reg_old;
	u32			scale_clk_ctrl_reg_new;
	struct ttc_timer	ttc;
	struct clocksource	cs;
};

#define to_ttc_timer_clksrc(x) \
		container_of(x, struct ttc_timer_clocksource, cs)

struct ttc_timer_clockevent {
	struct ttc_timer		ttc;
	struct clock_event_device	ce;
};

#define to_ttc_timer_clkevent(x) \
		container_of(x, struct ttc_timer_clockevent, ce)

static void __iomem *ttc_sched_clock_val_reg;

/**
 * ttc_set_interval - Set the timer interval value
 *
 * @timer:	Pointer to the timer instance
 * @cycles:	Timer interval ticks
 **/
static void ttc_set_interval(struct ttc_timer *timer,
					unsigned long cycles)
{
	u32 ctrl_reg;

	/* Disable the counter, set the counter value  and re-enable counter */
	ctrl_reg = readl_relaxed(timer->base_addr + TTC_CNT_CNTRL_OFFSET);
	ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK;
	writel_relaxed(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);

	writel_relaxed(cycles, timer->base_addr + TTC_INTR_VAL_OFFSET);

	/*
	 * Reset the counter (0x10) so that it starts from 0, one-shot
	 * mode makes this needed for timing to be right.
	 */
	ctrl_reg |= CNT_CNTRL_RESET;
	ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK;
	writel_relaxed(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
}

/**
 * ttc_clock_event_interrupt - Clock event timer interrupt handler
 *
 * @irq:	IRQ number of the Timer
 * @dev_id:	void pointer to the ttc_timer instance
 *
 * returns: Always IRQ_HANDLED - success
 **/
static irqreturn_t ttc_clock_event_interrupt(int irq, void *dev_id)
{
	struct ttc_timer_clockevent *ttce = dev_id;
	struct ttc_timer *timer = &ttce->ttc;

	/* Acknowledge the interrupt and call event handler */
	readl_relaxed(timer->base_addr + TTC_ISR_OFFSET);

	ttce->ce.event_handler(&ttce->ce);

	return IRQ_HANDLED;
}

/**
 * __ttc_clocksource_read - Reads the timer counter register
 *
 * returns: Current timer counter register value
 **/
static u64 __ttc_clocksource_read(struct clocksource *cs)
{
	struct ttc_timer *timer = &to_ttc_timer_clksrc(cs)->ttc;

	return (u64)readl_relaxed(timer->base_addr +
				TTC_COUNT_VAL_OFFSET);
}

static u64 notrace ttc_sched_clock_read(void)
{
	return readl_relaxed(ttc_sched_clock_val_reg);
}

/**
 * ttc_set_next_event - Sets the time interval for next event
 *
 * @cycles:	Timer interval ticks
 * @evt:	Address of clock event instance
 *
 * returns: Always 0 - success
 **/
static int ttc_set_next_event(unsigned long cycles,
					struct clock_event_device *evt)
{
	struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
	struct ttc_timer *timer = &ttce->ttc;

	ttc_set_interval(timer, cycles);
	return 0;
}

/**
 * ttc_set_{shutdown|oneshot|periodic} - Sets the state of timer
 *
 * @evt:	Address of clock event instance
 **/
static int ttc_shutdown(struct clock_event_device *evt)
{
	struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
	struct ttc_timer *timer = &ttce->ttc;
	u32 ctrl_reg;

	ctrl_reg = readl_relaxed(timer->base_addr + TTC_CNT_CNTRL_OFFSET);
	ctrl_reg |= TTC_CNT_CNTRL_DISABLE_MASK;
	writel_relaxed(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
	return 0;
}

static int ttc_set_periodic(struct clock_event_device *evt)
{
	struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
	struct ttc_timer *timer = &ttce->ttc;

	ttc_set_interval(timer,
			 DIV_ROUND_CLOSEST(ttce->ttc.freq, PRESCALE * HZ));
	return 0;
}

static int ttc_resume(struct clock_event_device *evt)
{
	struct ttc_timer_clockevent *ttce = to_ttc_timer_clkevent(evt);
	struct ttc_timer *timer = &ttce->ttc;
	u32 ctrl_reg;

	ctrl_reg = readl_relaxed(timer->base_addr + TTC_CNT_CNTRL_OFFSET);
	ctrl_reg &= ~TTC_CNT_CNTRL_DISABLE_MASK;
	writel_relaxed(ctrl_reg, timer->base_addr + TTC_CNT_CNTRL_OFFSET);
	return 0;
}

static int ttc_rate_change_clocksource_cb(struct notifier_block *nb,
		unsigned long event, void *data)
{
	struct clk_notifier_data *ndata = data;
	struct ttc_timer *ttc = to_ttc_timer(nb);
	struct ttc_timer_clocksource *ttccs = container_of(ttc,
			struct ttc_timer_clocksource, ttc);

	switch (event) {
	case PRE_RATE_CHANGE:
	{
		u32 psv;
		unsigned long factor, rate_low, rate_high;

		if (ndata->new_rate > ndata->old_rate) {
			factor = DIV_ROUND_CLOSEST(ndata->new_rate,
					ndata->old_rate);
			rate_low = ndata->old_rate;
			rate_high = ndata->new_rate;
		} else {
			factor = DIV_ROUND_CLOSEST(ndata->old_rate,
					ndata->new_rate);
			rate_low = ndata->new_rate;
			rate_high = ndata->old_rate;
		}

		if (!is_power_of_2(factor))
				return NOTIFY_BAD;

		if (abs(rate_high - (factor * rate_low)) > MAX_F_ERR)
			return NOTIFY_BAD;

		factor = __ilog2_u32(factor);

		/*
		 * store timer clock ctrl register so we can restore it in case
		 * of an abort.
		 */
		ttccs->scale_clk_ctrl_reg_old =
			readl_relaxed(ttccs->ttc.base_addr +
			TTC_CLK_CNTRL_OFFSET);

		psv = (ttccs->scale_clk_ctrl_reg_old &
				TTC_CLK_CNTRL_PSV_MASK) >>
				TTC_CLK_CNTRL_PSV_SHIFT;
		if (ndata->new_rate < ndata->old_rate)
			psv -= factor;
		else
			psv += factor;

		/* prescaler within legal range? */
		if (psv & ~(TTC_CLK_CNTRL_PSV_MASK >> TTC_CLK_CNTRL_PSV_SHIFT))
			return NOTIFY_BAD;

		ttccs->scale_clk_ctrl_reg_new = ttccs->scale_clk_ctrl_reg_old &
			~TTC_CLK_CNTRL_PSV_MASK;
		ttccs->scale_clk_ctrl_reg_new |= psv << TTC_CLK_CNTRL_PSV_SHIFT;


		/* scale down: adjust divider in post-change notification */
		if (ndata->new_rate < ndata->old_rate)
			return NOTIFY_DONE;

		/* scale up: adjust divider now - before frequency change */
		writel_relaxed(ttccs->scale_clk_ctrl_reg_new,
			       ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
		break;
	}
	case POST_RATE_CHANGE:
		/* scale up: pre-change notification did the adjustment */
		if (ndata->new_rate > ndata->old_rate)
			return NOTIFY_OK;

		/* scale down: adjust divider now - after frequency change */
		writel_relaxed(ttccs->scale_clk_ctrl_reg_new,
			       ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET);
		break;

	case ABORT_RATE_CHANGE:
		/* we have to undo the adjustment in case we scale up */
		if (ndata->new_rate < ndata->old_rate)
			return