aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDjamil Elaidi <d-elaidi@ti.com>2011-12-07 11:06:10 +0100
committerZiyann <jaraidaniel@gmail.com>2014-10-01 12:59:34 +0200
commitf533310f9c1088329746aeb384230cd19daa4742 (patch)
treebda312302dde685dbae11230050b1fd06b458ede
parentb8eed623c8609f05efe485ec41ccd04da66b9d20 (diff)
downloadkernel_samsung_tuna-f533310f9c1088329746aeb384230cd19daa4742.zip
kernel_samsung_tuna-f533310f9c1088329746aeb384230cd19daa4742.tar.gz
kernel_samsung_tuna-f533310f9c1088329746aeb384230cd19daa4742.tar.bz2
OMAP4: HSI: Add dynamic OPP change
Added an IOCTL to change HSI FClk to 96MHz or 192MHz. Decision of when to increase/decrease modem speed is up to Link Layer. Constraint : * RX/TX divisors and RX error counters need to be changed after Fclk is changed. * Read/Write operation must not be pending when OPP change request is issued, otherwise OPP change is aborted. * Any Read/Write operation started during HSI OPP change operation will be aborted. Change-Id: I06e15a205e614506994989ec6a40c0383e32776d Signed-off-by: Djamil Elaidi <d-elaidi@ti.com>
-rw-r--r--arch/arm/plat-omap/include/plat/omap_hsi.h8
-rw-r--r--drivers/omap_hsi/hsi-char.c12
-rw-r--r--drivers/omap_hsi/hsi-if.c17
-rw-r--r--drivers/omap_hsi/hsi-if.h2
-rwxr-xr-xdrivers/omap_hsi/hsi_driver.c25
-rwxr-xr-xdrivers/omap_hsi/hsi_driver.h10
-rwxr-xr-xdrivers/omap_hsi/hsi_driver_if.c79
-rw-r--r--include/linux/hsi_char.h2
-rw-r--r--include/linux/hsi_driver_if.h5
9 files changed, 128 insertions, 32 deletions
diff --git a/arch/arm/plat-omap/include/plat/omap_hsi.h b/arch/arm/plat-omap/include/plat/omap_hsi.h
index c64694e..d29062d 100644
--- a/arch/arm/plat-omap/include/plat/omap_hsi.h
+++ b/arch/arm/plat-omap/include/plat/omap_hsi.h
@@ -28,9 +28,13 @@
#ifndef __OMAP_HSI_H__
#define __OMAP_HSI_H__
+#define HSI_FCLK_LOW_SPEED 96000000 /* 96 MHz */
+#define HSI_FCLK_HI_SPEED 192000000 /* 192 MHz */
+#define HSI_FCLK_DPLL_CASCADING 98300000 /* 98.3 MHz */
+
/* Set the HSI Functional Clock to 96MHz.
- * This is to ensure HSI will function even at OPP50. */
-#define HSI_DEFAULT_FCLK 96000000 /* 96 MHz */
+ * Warning : 192MHz will force OPP100 on VDD_CORE (no OPP50 possible) */
+#define HSI_DEFAULT_FCLK HSI_FCLK_LOW_SPEED /* 96 MHz */
#define HSI_PORT_OFFSET 0x1000
diff --git a/drivers/omap_hsi/hsi-char.c b/drivers/omap_hsi/hsi-char.c
index 59193f4..8c0f406 100644
--- a/drivers/omap_hsi/hsi-char.c
+++ b/drivers/omap_hsi/hsi-char.c
@@ -361,6 +361,7 @@ static long hsi_char_ioctl(struct file *file,
int ch = (int)file->private_data;
unsigned int state;
size_t size;
+ unsigned long fclock;
struct hsi_rx_config rx_cfg;
struct hsi_tx_config tx_cfg;
int ret = 0;
@@ -433,6 +434,17 @@ static long hsi_char_ioctl(struct file *file,
if (copy_to_user((void __user *)arg, &size, sizeof(size)))
ret = -EFAULT;
break;
+ case CS_SET_HI_SPEED:
+ if (copy_from_user(&state, (void __user *)arg, sizeof(state)))
+ ret = -EFAULT;
+ else
+ if_hsi_set_hi_speed(ch, state);
+ break;
+ case CS_GET_SPEED:
+ if_hsi_get_speed(ch, &fclock);
+ if (copy_to_user((void __user *)arg, &fclock, sizeof(fclock)))
+ ret = -EFAULT;
+ break;
default:
ret = -ENOIOCTLCMD;
break;
diff --git a/drivers/omap_hsi/hsi-if.c b/drivers/omap_hsi/hsi-if.c
index bb3e03d..8f6002f 100644
--- a/drivers/omap_hsi/hsi-if.c
+++ b/drivers/omap_hsi/hsi-if.c
@@ -335,6 +335,23 @@ void if_hsi_sw_reset(int ch)
spin_unlock_bh(&hsi_iface.lock);
}
+void if_hsi_set_hi_speed(int ch, unsigned int state)
+{
+ struct if_hsi_channel *channel;
+ channel = &hsi_iface.channels[ch];
+
+ spin_lock_bh(&hsi_iface.lock);
+ hsi_ioctl(channel->dev, HSI_IOCTL_SET_HI_SPEED, &state);
+ spin_unlock_bh(&hsi_iface.lock);
+}
+
+void if_hsi_get_speed(int ch, unsigned long *fclock)
+{
+ struct if_hsi_channel *channel;
+ channel = &hsi_iface.channels[ch];
+ hsi_ioctl(channel->dev, HSI_IOCTL_GET_SPEED, fclock);
+}
+
void if_hsi_get_fifo_occupancy(int ch, size_t *occ)
{
struct if_hsi_channel *channel;
diff --git a/drivers/omap_hsi/hsi-if.h b/drivers/omap_hsi/hsi-if.h
index f4bbf76..9cb120e 100644
--- a/drivers/omap_hsi/hsi-if.h
+++ b/drivers/omap_hsi/hsi-if.h
@@ -53,6 +53,8 @@ void if_hsi_set_acwakeline(int ch, unsigned int state);
void if_hsi_get_acwakeline(int ch, unsigned int *state);
void if_hsi_get_cawakeline(int ch, unsigned int *state);
void if_hsi_set_wake_rx_3wires_mode(int ch, unsigned int state);
+void if_hsi_set_hi_speed(int ch, unsigned int state);
+void if_hsi_get_speed(int ch, unsigned long *fclock);
int if_hsi_set_rx(int ch, struct hsi_rx_config *cfg);
void if_hsi_get_rx(int ch, struct hsi_rx_config *cfg);
int if_hsi_set_tx(int ch, struct hsi_tx_config *cfg);
diff --git a/drivers/omap_hsi/hsi_driver.c b/drivers/omap_hsi/hsi_driver.c
index 9efc2e3..a2ed3c2 100755
--- a/drivers/omap_hsi/hsi_driver.c
+++ b/drivers/omap_hsi/hsi_driver.c
@@ -174,7 +174,6 @@ int hsi_port_event_handler(struct hsi_port *p, unsigned int event, void *arg)
struct hsi_channel *hsi_channel;
int ch;
-
if (event == HSI_EVENT_HSR_DATAAVAILABLE) {
/* The data-available event is channel-specific and must not be
* broadcasted
@@ -785,7 +784,8 @@ static int __init hsi_controller_init(struct hsi_dev *hsi_ctrl,
}
hsi_ctrl->max_p = pdata->num_ports;
hsi_ctrl->clock_enabled = false;
- hsi_ctrl->clock_rate = 0;
+ hsi_ctrl->clock_change_ongoing = false;
+ hsi_ctrl->hsi_fclk_current = 0;
hsi_ctrl->in_dma_tasklet = false;
hsi_ctrl->fifo_mapping_strategy = pdata->fifo_mapping_strategy;
hsi_ctrl->dev = &pd->dev;
@@ -835,6 +835,12 @@ static int __init hsi_platform_device_probe(struct platform_device *pd)
return -ENXIO;
}
+ /* Check if mandatory board functions are populated */
+ if (!pdata->device_scale) {
+ dev_err(&pd->dev, "Missing platform device_scale function\n");
+ return -ENOSYS;
+ }
+
hsi_ctrl = kzalloc(sizeof(*hsi_ctrl), GFP_KERNEL);
if (hsi_ctrl == NULL) {
dev_err(&pd->dev, "Could not allocate memory for"
@@ -891,26 +897,21 @@ static int __init hsi_platform_device_probe(struct platform_device *pd)
device_init_wakeup(hsi_ctrl->dev, true);
/* Set the HSI FCLK to default. */
- if (!pdata->device_scale) {
- dev_err(&pd->dev, "%s: No platform device_scale function\n",
- __func__);
- err = -ENXIO;
- goto rollback3;
- }
+ hsi_ctrl->hsi_fclk_req = pdata->default_hsi_fclk;
err = pdata->device_scale(hsi_ctrl->dev, hsi_ctrl->dev,
pdata->default_hsi_fclk);
if (err == -EBUSY) {
+ /* PM framework init is late_initcall, so it may not yet be */
+ /* initialized, so be prepared to retry later on open. */
dev_warn(&pd->dev, "Cannot set HSI FClk to default value: %ld. "
- "Will retry on next open\n",
- pdata->default_hsi_fclk);
+ "Will retry on next open\n", pdata->default_hsi_fclk);
} else if (err) {
dev_err(&pd->dev, "%s: Error %d setting HSI FClk to %ld.\n",
__func__, err, pdata->default_hsi_fclk);
goto rollback3;
} else {
- hsi_ctrl->clock_rate = pdata->default_hsi_fclk;
+ hsi_ctrl->hsi_fclk_current = pdata->default_hsi_fclk;
}
-
/* From here no need for HSI HW access */
hsi_clocks_disable(hsi_ctrl->dev, __func__);
diff --git a/drivers/omap_hsi/hsi_driver.h b/drivers/omap_hsi/hsi_driver.h
index 7ad5655..226fe15 100755
--- a/drivers/omap_hsi/hsi_driver.h
+++ b/drivers/omap_hsi/hsi_driver.h
@@ -174,7 +174,9 @@ struct hsi_port {
* @phy_base: HSI registers base physical address
* @lock: Serializes access to internal data and regs
* @clock_enabled: Indicates if HSI Clocks are ON
- * @clock_rate: Indicates current HSI Fclock speed
+ * @clock_change_ongoing: Indicates if HSI FClk is being changed (OPP change)
+ * @hsi_fclk_req: Indicates what HSI FClk user requested (96MHz/192MHz)
+ * @hsi_fclk_current: Current HSI Fclock
* @gdd_irq: GDD (DMA) irq number
* @fifo_mapping_strategy: Selected strategy for fifo to ports/channels mapping
* @gdd_usecount: Holds the number of ongoning DMA transfers
@@ -182,8 +184,6 @@ struct hsi_port {
* @gdd_chan_count: Number of available DMA channels on the device (must be ^2)
* @in_dma_tasklet: True if DMA tasklet for the controller is currently running
* @set_min_bus_tput: (PM) callback to set minimun bus throuput
- * @clk_notifier_register: (PM) callabck for DVFS support
- * @clk_notifier_unregister: (PM) callabck for DVFS support
* @hsi_nb: (PM) Notification block for DVFS notification chain
* @hsi_gdd_tasklet: Bottom half for DMA Interrupts when clocks are enabled
* @dir: debugfs base directory
@@ -197,7 +197,9 @@ struct hsi_dev { /* HSI_TODO: should be later renamed into hsi_controller*/
unsigned long phy_base;
spinlock_t lock; /* Serializes access to internal data and regs */
bool clock_enabled;
- unsigned long clock_rate;
+ bool clock_change_ongoing;
+ unsigned long hsi_fclk_req;
+ unsigned long hsi_fclk_current;
int gdd_irq;
unsigned int fifo_mapping_strategy;
unsigned int gdd_usecount;
diff --git a/drivers/omap_hsi/hsi_driver_if.c b/drivers/omap_hsi/hsi_driver_if.c
index 82b7239..f7ab392 100755
--- a/drivers/omap_hsi/hsi_driver_if.c
+++ b/drivers/omap_hsi/hsi_driver_if.c
@@ -289,22 +289,10 @@ int hsi_open(struct hsi_device *dev)
return -EINVAL;
}
- if (hsi_ctrl->clock_rate == 0) {
+ if (hsi_ctrl->hsi_fclk_current == 0) {
struct hsi_platform_data *pdata;
pdata = dev_get_platdata(hsi_ctrl->dev);
- if (!pdata) {
- dev_err(dev->device.parent,
- "%s: Port %d Channel %d has no pdata\n",
- __func__, dev->n_p, dev->n_ch);
- return -EINVAL;
- }
- if (!pdata->device_scale) {
- dev_err(dev->device.parent,
- "%s: Undefined platform device_scale function\n",
- __func__);
- return -ENXIO;
- }
/* Retry to set the HSI FCLK to default. */
err = pdata->device_scale(hsi_ctrl->dev, hsi_ctrl->dev,
@@ -318,7 +306,7 @@ int hsi_open(struct hsi_device *dev)
} else {
dev_info(dev->device.parent, "HSI clock is now %ld\n",
pdata->default_hsi_fclk);
- hsi_ctrl->clock_rate = pdata->default_hsi_fclk;
+ hsi_ctrl->hsi_fclk_current = pdata->default_hsi_fclk;
}
}
spin_lock_bh(&hsi_ctrl->lock);
@@ -397,6 +385,11 @@ int hsi_write(struct hsi_device *dev, u32 *addr, unsigned int size)
spin_lock_bh(&hsi_ctrl->lock);
+ if (hsi_ctrl->clock_change_ongoing) {
+ dev_warn(hsi_ctrl->dev, "HSI Fclock change ongoing, retry.\n");
+ spin_unlock_bh(&hsi_ctrl->lock);
+ return -EAGAIN;
+ }
if (pm_runtime_suspended(hsi_ctrl->dev) || !hsi_ctrl->clock_enabled)
dev_dbg(hsi_ctrl->dev,
@@ -466,6 +459,13 @@ int hsi_read(struct hsi_device *dev, u32 *addr, unsigned int size)
hsi_ctrl = ch->hsi_port->hsi_controller;
spin_lock_bh(&hsi_ctrl->lock);
+
+ if (hsi_ctrl->clock_change_ongoing) {
+ dev_warn(hsi_ctrl->dev, "HSI Fclock change ongoing, retry.\n");
+ spin_unlock_bh(&hsi_ctrl->lock);
+ return -EAGAIN;
+ }
+
if (pm_runtime_suspended(hsi_ctrl->dev) || !hsi_ctrl->clock_enabled)
dev_dbg(hsi_ctrl->dev,
"hsi_read with HSI clocks OFF, clock_enabled = %d\n",
@@ -745,6 +745,7 @@ int hsi_ioctl(struct hsi_device *dev, unsigned int command, void *arg)
int err = 0;
int fifo = 0;
u8 ret;
+ struct hsi_platform_data *pdata;
if (unlikely((!dev) ||
(!dev->ch) ||
@@ -953,6 +954,56 @@ int hsi_ioctl(struct hsi_device *dev, unsigned int command, void *arg)
hsi_outl_and(HSI_SET_WAKE_3_WIRES_MASK, base,
HSI_SYS_SET_WAKE_REG(port));
break;
+ case HSI_IOCTL_SET_HI_SPEED:
+ if (!arg) {
+ err = -EINVAL;
+ goto out;
+ }
+ hsi_ctrl->hsi_fclk_req = *(unsigned int *)arg ?
+ HSI_FCLK_HI_SPEED : HSI_FCLK_LOW_SPEED;
+
+ if (hsi_ctrl->hsi_fclk_req == hsi_ctrl->hsi_fclk_current) {
+ dev_dbg(hsi_ctrl->dev, "HSI FClk already @%ldHz\n",
+ hsi_ctrl->hsi_fclk_current);
+ goto out;
+ }
+
+ if (hsi_is_controller_transfer_ongoing(hsi_ctrl)) {
+ err = -EBUSY;
+ goto out;
+ }
+ hsi_ctrl->clock_change_ongoing = true;
+ spin_unlock_bh(&hsi_ctrl->lock);
+
+ pdata = dev_get_platdata(hsi_ctrl->dev);
+
+ /* Set the HSI FCLK to requested value. */
+ err = pdata->device_scale(hsi_ctrl->dev, hsi_ctrl->dev,
+ hsi_ctrl->hsi_fclk_req);
+ if (err < 0) {
+ dev_err(hsi_ctrl->dev, "%s: Cannot set HSI FClk to"
+ " %ldHz, err %d\n", __func__,
+ hsi_ctrl->hsi_fclk_req, err);
+ } else {
+ dev_info(hsi_ctrl->dev, "HSI FClk changed from %ldHz to"
+ " %ldHz\n", hsi_ctrl->hsi_fclk_current,
+ hsi_ctrl->hsi_fclk_req);
+ hsi_ctrl->hsi_fclk_current = hsi_ctrl->hsi_fclk_req;
+ }
+
+ spin_lock_bh(&hsi_ctrl->lock);
+ hsi_ctrl->clock_change_ongoing = false;
+
+ break;
+ case HSI_IOCTL_GET_SPEED:
+ if (!arg) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ *(unsigned long *)arg = hsi_ctrl->hsi_fclk_current;
+ break;
+
default:
err = -ENOIOCTLCMD;
break;
diff --git a/include/linux/hsi_char.h b/include/linux/hsi_char.h
index a85a3b0..1d87bcb 100644
--- a/include/linux/hsi_char.h
+++ b/include/linux/hsi_char.h
@@ -41,6 +41,8 @@
#define CS_GET_FIFO_OCCUPANCY CS_IOR(12, size_t)
#define CS_GET_CAWAKELINE CS_IOR(13, unsigned int)
#define CS_SET_WAKE_RX_3WIRES_MODE CS_IOR(14, unsigned int)
+#define CS_SET_HI_SPEED CS_IOR(15, unsigned int)
+#define CS_GET_SPEED CS_IOW(16, unsigned long)
#define HSI_MODE_SLEEP 0
#define HSI_MODE_STREAM 1
diff --git a/include/linux/hsi_driver_if.h b/include/linux/hsi_driver_if.h
index 6e7a0da..c7afe6b 100644
--- a/include/linux/hsi_driver_if.h
+++ b/include/linux/hsi_driver_if.h
@@ -38,6 +38,9 @@
#define HSI_HSR_DIVISOR_AUTO 0x1000 /* Activate auto Rx */
#define HSI_SSR_DIVISOR_USE_TIMEOUT 0x1001 /* De-activate auto-Rx (SSI) */
+#define HSI_SPEED_LOW_SPEED 0 /* Switch to 96MHz */
+#define HSI_SPEED_HI_SPEED 1 /* Switch to 192MHz */
+
enum {
HSI_EVENT_BREAK_DETECTED = 0,
HSI_EVENT_ERROR,
@@ -64,6 +67,8 @@ enum {
HSI_IOCTL_GET_FIFO_OCCUPANCY, /* Get amount of words in RX FIFO */
HSI_IOCTL_SET_WAKE_RX_3WIRES_MODE, /* Enable RX wakeup 3-wires mode */
HSI_IOCTL_SET_WAKE_RX_4WIRES_MODE, /* Enable RX wakeup 4-wires mode */
+ HSI_IOCTL_SET_HI_SPEED, /* Change HSI Fclock (96MHz/192MHz) */
+ HSI_IOCTL_GET_SPEED, /* Get HSI Fclock (96MHz/192MHz) */
};
/* Forward references */