RE: [PATCH 1/3] ti-st: use device handles and add device tree binding

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




Ping on this patchset

> -----Original Message-----
> From: Reizer, Eyal
> Sent: Wednesday, December 23, 2015 1:38 PM
> To: 'devicetree@xxxxxxxxxxxxxxx'; 'linux-omap@xxxxxxxxxxxxxxx'; 'linux-arm-
> kernel@xxxxxxxxxxxxxxxxxxx'
> Cc: 'robh+dt@xxxxxxxxxx'; 'pawel.moll@xxxxxxx'; 'mark.rutland@xxxxxxx';
> 'ijc+devicetree@xxxxxxxxxxxxxx'; 'galak@xxxxxxxxxxxxxx';
> 'tony@xxxxxxxxxxx'; 'linux@xxxxxxxxxxxxxxxx'
> Subject: [PATCH 1/3] ti-st: use device handles and add device tree binding
> 
> - Add support for getting the platform data which includes the uart
>   used and gpio pin used for enable from device tree.
> 
> - Fix the implementation for using device handle for the uart and
>   gpiod for the enable pin, instead of device name (as string) used
>   for the uart and pio number which are both bad practice.
> 
> Signed-off-by: Eyal Reizer <eyalr@xxxxxx>
> ---
>  Documentation/devicetree/bindings/misc/ti-st.txt |   42 ++++++
>  arch/arm/mach-omap2/pdata-quirks.c               |   16 ++-
>  drivers/misc/ti-st/st_kim.c                      |  159 ++++++++++++++++------
>  drivers/misc/ti-st/st_ll.c                       |   16 ++-
>  include/linux/ti_wilink_st.h                     |   13 +-
>  5 files changed, 190 insertions(+), 56 deletions(-)  create mode 100644
> Documentation/devicetree/bindings/misc/ti-st.txt
> 
> diff --git a/Documentation/devicetree/bindings/misc/ti-st.txt
> b/Documentation/devicetree/bindings/misc/ti-st.txt
> new file mode 100644
> index 0000000..4490da6
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/misc/ti-st.txt
> @@ -0,0 +1,42 @@
> +TI Wilink 6/7/8 (wl12xx/wl18xx) Shared transport driver
> +
> +TI’s Wireless Connectivity chips support Bluetooth (BT), WiFi, and GPS
> +technology cores in a single die.
> +
> +Such a multi-core combo chip will be interfaced to the application
> +processor using a single physical port (like UART).
> +
> +Shared Transport (ST) software enables BT and GPS protocols or software
> +components to interact with their respective cores over single physical port.
> +ST uses logical channels, over physical transport, to communicate with
> +individual cores.
> +
> +Logical channels 1, 2, 3, and 4 are used for BT packets, channel 8 for
> +FM, channel 9 for GPS and channels 30, 31, 32, and 33 are used for Chip
> +Power Management (PM).
> +
> +This node provides properties for passing parameters to the ti shared
> +transport driver.
> +
> +Required properties:
> + - compatible: should be the following:
> +    * "kim" - ti-st parameters
> +
> +Optional properties:
> + - nshutdown-gpios : specifies attributes for gpio ping used for enabling
> +	the bluetooth,gps and FM sub systems
> + - serial-device : the phandle for the phisical uart used for interacting
> + 	with the wilink device
> + - flow_cntrl : Indicates if uart flow control is used
> + - flow_cntrl : uart baud rate in BPS
> +
> +Example:
> +
> +kim {
> +	compatible = "kim";
> +	nshutdown-gpios = <&gpio3 21 GPIO_ACTIVE_HIGH>;
> +	serial-device = <&uart1>;
> +	flow_cntrl = <1>;
> +	flow_cntrl = <3000000>;
> +};
> +
> diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-
> omap2/pdata-quirks.c
> index 5814477..b516fdc 100644
> --- a/arch/arm/mach-omap2/pdata-quirks.c
> +++ b/arch/arm/mach-omap2/pdata-quirks.c
> @@ -10,6 +10,7 @@
>  #include <linux/clk.h>
>  #include <linux/davinci_emac.h>
>  #include <linux/gpio.h>
> +#include <linux/gpio/machine.h>
>  #include <linux/init.h>
>  #include <linux/kernel.h>
>  #include <linux/of_platform.h>
> @@ -135,11 +136,18 @@ static void __init
> omap3_sbc_t3530_legacy_init(void)
>  	omap3_sbc_t3x_usb_hub_init(167, "sb-t35 usb hub");  }
> 
> +struct gpiod_lookup_table bt_gpios_table = {
> +	.dev_id = "kim",
> +		.table = {
> +		GPIO_LOOKUP("gpio4", 9, "nshutdown", GPIO_ACTIVE_HIGH),
> +		{ },
> +	},
> +};
> +
>  static struct ti_st_plat_data wilink_pdata = {
> -	.nshutdown_gpio = 137,
> -	.dev_name = "/dev/ttyO1",
> +	.dev_addr = 0x48022000, /* uart1 */
>  	.flow_cntrl = 1,
> -	.baud_rate = 300000,
> +	.baud_rate = 3000000,
>  };
> 
>  static struct platform_device wl18xx_device = { @@ -157,12 +165,14 @@
> static struct platform_device btwilink_device = {
> 
>  static void __init omap3_igep0020_rev_f_legacy_init(void)
>  {
> +	gpiod_add_lookup_table(&bt_gpios_table);
>  	platform_device_register(&wl18xx_device);
>  	platform_device_register(&btwilink_device);
>  }
> 
>  static void __init omap3_igep0030_rev_g_legacy_init(void)
>  {
> +	gpiod_add_lookup_table(&bt_gpios_table);
>  	platform_device_register(&wl18xx_device);
>  	platform_device_register(&btwilink_device);
>  }
> diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c index
> 71b6455..61d4f054 100644
> --- a/drivers/misc/ti-st/st_kim.c
> +++ b/drivers/misc/ti-st/st_kim.c
> @@ -36,6 +36,8 @@
>  #include <linux/skbuff.h>
>  #include <linux/ti_wilink_st.h>
>  #include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> 
>  #define MAX_ST_DEVICES	3	/* Imagine 1 on each UART for now */
>  static struct platform_device *st_kim_devices[MAX_ST_DEVICES]; @@ -43,6
> +45,9 @@ static struct platform_device *st_kim_devices[MAX_ST_DEVICES];
> /***********************************************************
> ***********/
>  /* internal functions */
> 
> +struct ti_st_plat_data	*dt_pdata;
> +static struct ti_st_plat_data *get_platform_data(struct device *dev);
> +
>  /**
>   * st_get_plat_device -
>   *	function which returns the reference to the platform device
> @@ -464,7 +469,12 @@ long st_kim_start(void *kim_data)
>  	struct kim_data_s	*kim_gdata = (struct kim_data_s *)kim_data;
> 
>  	pr_info(" %s", __func__);
> -	pdata = kim_gdata->kim_pdev->dev.platform_data;
> +	if (kim_gdata->kim_pdev->dev.of_node) {
> +		pr_debug("use device tree data");
> +		pdata = dt_pdata;
> +	} else {
> +		pdata = kim_gdata->kim_pdev->dev.platform_data;
> +	}
> 
>  	do {
>  		/* platform specific enabling code here */ @@ -472,9 +482,9
> @@ long st_kim_start(void *kim_data)
>  			pdata->chip_enable(kim_gdata);
> 
>  		/* Configure BT nShutdown to HIGH state */
> -		gpio_set_value_cansleep(kim_gdata->nshutdown,
> GPIO_LOW);
> +		gpiod_set_value_cansleep(kim_gdata->nshutdown,
> GPIO_LOW);
>  		mdelay(5);	/* FIXME: a proper toggle */
> -		gpio_set_value_cansleep(kim_gdata->nshutdown,
> GPIO_HIGH);
> +		gpiod_set_value_cansleep(kim_gdata->nshutdown,
> GPIO_HIGH);
>  		mdelay(100);
>  		/* re-initialize the completion */
>  		reinit_completion(&kim_gdata->ldisc_installed);
> @@ -524,11 +534,15 @@ long st_kim_stop(void *kim_data)  {
>  	long err = 0;
>  	struct kim_data_s	*kim_gdata = (struct kim_data_s *)kim_data;
> -	struct ti_st_plat_data	*pdata =
> -		kim_gdata->kim_pdev->dev.platform_data;
> +	struct ti_st_plat_data	*pdata;
>  	struct tty_struct	*tty = kim_gdata->core_data->tty;
> 
>  	reinit_completion(&kim_gdata->ldisc_installed);
> +	if (kim_gdata->kim_pdev->dev.of_node) {
> +		pr_debug("use device tree data");
> +		pdata = dt_pdata;
> +	} else
> +		pdata = kim_gdata->kim_pdev->dev.platform_data;
> 
>  	if (tty) {	/* can be called before ldisc is installed */
>  		/* Flush any pending characters in the driver and discipline.
> */ @@ -550,11 +564,11 @@ long st_kim_stop(void *kim_data)
>  	}
> 
>  	/* By default configure BT nShutdown to LOW state */
> -	gpio_set_value_cansleep(kim_gdata->nshutdown, GPIO_LOW);
> +	gpiod_set_value_cansleep(kim_gdata->nshutdown, GPIO_LOW);
>  	mdelay(1);
> -	gpio_set_value_cansleep(kim_gdata->nshutdown, GPIO_HIGH);
> +	gpiod_set_value_cansleep(kim_gdata->nshutdown, GPIO_HIGH);
>  	mdelay(1);
> -	gpio_set_value_cansleep(kim_gdata->nshutdown, GPIO_LOW);
> +	gpiod_set_value_cansleep(kim_gdata->nshutdown, GPIO_LOW);
> 
>  	/* platform specific disable */
>  	if (pdata->chip_disable)
> @@ -590,32 +604,43 @@ static ssize_t show_install(struct device *dev,  }
> 
>  #ifdef DEBUG
> -static ssize_t store_dev_name(struct device *dev,
> -		struct device_attribute *attr, const char *buf, size_t count)
> +static ssize_t store_dev_addr(struct device *dev,
> +			      struct device_attribute *attr,
> +			      const char *buf, size_t count)
>  {
> +	int rc;
> +
>  	struct kim_data_s *kim_data = dev_get_drvdata(dev);
> -	pr_debug("storing dev name >%s<", buf);
> -	strncpy(kim_data->dev_name, buf, count);
> -	pr_debug("stored dev name >%s<", kim_data->dev_name);
> +	pr_debug("storing dev address >%s<", buf);
> +	rc = kstrtou32(buf, 0, &kim_data->dev_addr);
> +	if (rc)
> +		return rc;
> +
> +	pr_debug("stored dev address >%x<", kim_data->dev_addr);
>  	return count;
>  }
> 
>  static ssize_t store_baud_rate(struct device *dev,
>  		struct device_attribute *attr, const char *buf, size_t count)  {
> +	int rc;
> +
>  	struct kim_data_s *kim_data = dev_get_drvdata(dev);
>  	pr_debug("storing baud rate >%s<", buf);
> -	sscanf(buf, "%ld", &kim_data->baud_rate);
> -	pr_debug("stored baud rate >%ld<", kim_data->baud_rate);
> +	rc = kstrtou32(buf, 0, &kim_data->baud_rate);
> +	if (rc)
> +		return rc;
> +
> +	pr_debug("stored baud rate >%x<", kim_data->baud_rate);
>  	return count;
>  }
>  #endif	/* if DEBUG */
> 
> -static ssize_t show_dev_name(struct device *dev,
> -		struct device_attribute *attr, char *buf)
> +static ssize_t show_dev_addr(struct device *dev,
> +			     struct device_attribute *attr, char *buf)
>  {
>  	struct kim_data_s *kim_data = dev_get_drvdata(dev);
> -	return sprintf(buf, "%s\n", kim_data->dev_name);
> +	return sprintf(buf, "%x\n", kim_data->dev_addr);
>  }
> 
>  static ssize_t show_baud_rate(struct device *dev, @@ -636,11 +661,11 @@
> static ssize_t show_flow_cntrl(struct device *dev,  static struct kobj_attribute
> ldisc_install =  __ATTR(install, 0444, (void *)show_install, NULL);
> 
> -static struct kobj_attribute uart_dev_name =
> +static struct kobj_attribute uart_dev_addr =
>  #ifdef DEBUG	/* TODO: move this to debug-fs if possible */
> -__ATTR(dev_name, 0644, (void *)show_dev_name, (void
> *)store_dev_name);
> +__ATTR(dev_addr, 0644, (void *)show_dev_addr, (void *)store_dev_addr);
>  #else
> -__ATTR(dev_name, 0444, (void *)show_dev_name, NULL);
> +__ATTR(dev_addr, 0444, (void *)show_dev_addr, NULL);
>  #endif
> 
>  static struct kobj_attribute uart_baud_rate = @@ -655,7 +680,7 @@
> __ATTR(flow_cntrl, 0444, (void *)show_flow_cntrl, NULL);
> 
>  static struct attribute *uim_attrs[] = {
>  	&ldisc_install.attr,
> -	&uart_dev_name.attr,
> +	&uart_dev_addr.attr,
>  	&uart_baud_rate.attr,
>  	&uart_flow_cntrl.attr,
>  	NULL,
> @@ -721,13 +746,54 @@ static const struct file_operations
> list_debugfs_fops = {
>   * board-*.c file
>   */
> 
> +static const struct of_device_id kim_of_match[] = { {
> +	.compatible = "kim",
> +	},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, kim_of_match);
> +
> +static struct ti_st_plat_data *get_platform_data(struct device *dev) {
> +	struct device_node *np = dev->of_node;
> +	struct device_node *serial_node;
> +
> +	dt_pdata = kzalloc(sizeof(*dt_pdata), GFP_KERNEL);
> +	if (!dt_pdata)
> +		return NULL;
> +
> +	serial_node = of_parse_phandle(np, "serial-device", 0);
> +	if (serial_node) {
> +		pr_info("using serial device %s\n", serial_node->full_name);
> +		of_property_read_u32(serial_node, "reg", &dt_pdata-
> >dev_addr);
> +	} else {
> +		dev_err(dev, "Serial device missing");
> +	}
> +
> +	of_property_read_u32(np, "flow_cntrl", &dt_pdata->flow_cntrl);
> +	of_property_read_u32(np, "baud_rate", &dt_pdata->baud_rate);
> +
> +	return dt_pdata;
> +}
> +
>  static struct dentry *kim_debugfs_dir;
>  static int kim_probe(struct platform_device *pdev)  {
>  	struct kim_data_s	*kim_gdata;
> -	struct ti_st_plat_data	*pdata = pdev->dev.platform_data;
> +	struct ti_st_plat_data	*pdata;
>  	int err;
> 
> +	if (pdev->dev.of_node)
> +		pdata = get_platform_data(&pdev->dev);
> +	else
> +		pdata = pdev->dev.platform_data;
> +
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "Platform Data is missing\n");
> +		return -ENXIO;
> +	}
> +
>  	if ((pdev->id != -1) && (pdev->id < MAX_ST_DEVICES)) {
>  		/* multiple devices could exist */
>  		st_kim_devices[pdev->id] = pdev;
> @@ -735,7 +801,6 @@ static int kim_probe(struct platform_device *pdev)
>  		/* platform's sure about existence of 1 device */
>  		st_kim_devices[0] = pdev;
>  	}
> -
>  	kim_gdata = kzalloc(sizeof(struct kim_data_s), GFP_ATOMIC);
>  	if (!kim_gdata) {
>  		pr_err("no mem to allocate");
> @@ -753,19 +818,14 @@ static int kim_probe(struct platform_device
> *pdev)
>  	kim_gdata->core_data->kim_data = kim_gdata;
> 
>  	/* Claim the chip enable nShutdown gpio from the system */
> -	kim_gdata->nshutdown = pdata->nshutdown_gpio;
> -	err = gpio_request(kim_gdata->nshutdown, "kim");
> -	if (unlikely(err)) {
> -		pr_err(" gpio %d request failed ", kim_gdata->nshutdown);
> -		return err;
> +	kim_gdata->nshutdown = devm_gpiod_get(&pdev->dev,
> "nshutdown",
> +					      GPIOD_OUT_HIGH);
> +	if (IS_ERR(kim_gdata->nshutdown)) {
> +		err = PTR_ERR(kim_gdata->nshutdown);
> +		dev_err(&pdev->dev, "unable to claim gpio
> \"nshutdown\"\n");
> +		goto err_core_init;
>  	}
> 
> -	/* Configure nShutdown GPIO as output=0 */
> -	err = gpio_direction_output(kim_gdata->nshutdown, 0);
> -	if (unlikely(err)) {
> -		pr_err(" unable to configure gpio %d", kim_gdata-
> >nshutdown);
> -		return err;
> -	}
>  	/* get reference of pdev for request_firmware
>  	 */
>  	kim_gdata->kim_pdev = pdev;
> @@ -779,7 +839,7 @@ static int kim_probe(struct platform_device *pdev)
>  	}
> 
>  	/* copying platform data */
> -	strncpy(kim_gdata->dev_name, pdata->dev_name,
> UART_DEV_NAME_LEN);
> +	kim_gdata->dev_addr = pdata->dev_addr;
>  	kim_gdata->flow_cntrl = pdata->flow_cntrl;
>  	kim_gdata->baud_rate = pdata->baud_rate;
>  	pr_info("sysfs entries created\n");
> @@ -808,16 +868,17 @@ err_core_init:
>  static int kim_remove(struct platform_device *pdev)  {
>  	/* free the GPIOs requested */
> -	struct ti_st_plat_data	*pdata = pdev->dev.platform_data;
> +	struct ti_st_plat_data	*pdata;
>  	struct kim_data_s	*kim_gdata;
> 
> -	kim_gdata = platform_get_drvdata(pdev);
> +	if (pdev->dev.of_node) {
> +		pr_debug("use device tree data");
> +		pdata = dt_pdata;
> +	} else {
> +		pdata = pdev->dev.platform_data;
> +	}
> 
> -	/* Free the Bluetooth/FM/GPIO
> -	 * nShutdown gpio from the system
> -	 */
> -	gpio_free(pdata->nshutdown_gpio);
> -	pr_info("nshutdown GPIO Freed");
> +	kim_gdata = platform_get_drvdata(pdev);
> 
>  	debugfs_remove_recursive(kim_debugfs_dir);
>  	sysfs_remove_group(&pdev->dev.kobj, &uim_attr_grp); @@ -828,12
> +889,21 @@ static int kim_remove(struct platform_device *pdev)
> 
>  	kfree(kim_gdata);
>  	kim_gdata = NULL;
> +	kfree(dt_pdata);
> +	dt_pdata = NULL;
>  	return 0;
>  }
> 
>  static int kim_suspend(struct platform_device *pdev, pm_message_t state)  {
> -	struct ti_st_plat_data	*pdata = pdev->dev.platform_data;
> +	struct ti_st_plat_data	*pdata;
> +
> +	if (pdev->dev.of_node) {
> +		pr_debug("use device tree data");
> +		pdata = dt_pdata;
> +	} else {
> +		pdata = pdev->dev.platform_data;
> +	}
> 
>  	if (pdata->suspend)
>  		return pdata->suspend(pdev, state);
> @@ -860,6 +930,7 @@ static struct platform_driver kim_platform_driver = {
>  	.resume = kim_resume,
>  	.driver = {
>  		.name = "kim",
> +		.of_match_table = of_match_ptr(kim_of_match),
>  	},
>  };
> 
> diff --git a/drivers/misc/ti-st/st_ll.c b/drivers/misc/ti-st/st_ll.c index
> 93b4d67..d68b427 100644
> --- a/drivers/misc/ti-st/st_ll.c
> +++ b/drivers/misc/ti-st/st_ll.c
> @@ -53,7 +53,13 @@ static void ll_device_want_to_sleep(struct st_data_s
> *st_data)
> 
>  	/* communicate to platform about chip asleep */
>  	kim_data = st_data->kim_data;
> -	pdata = kim_data->kim_pdev->dev.platform_data;
> +	if (kim_data->kim_pdev->dev.of_node) {
> +		pr_debug("use device tree data");
> +		pdata = dt_pdata;
> +	} else {
> +		pdata = kim_data->kim_pdev->dev.platform_data;
> +	}
> +
>  	if (pdata->chip_asleep)
>  		pdata->chip_asleep(NULL);
>  }
> @@ -86,7 +92,13 @@ static void ll_device_want_to_wakeup(struct st_data_s
> *st_data)
> 
>  	/* communicate to platform about chip wakeup */
>  	kim_data = st_data->kim_data;
> -	pdata = kim_data->kim_pdev->dev.platform_data;
> +	if (kim_data->kim_pdev->dev.of_node) {
> +		pr_debug("use device tree data");
> +		pdata = dt_pdata;
> +	} else {
> +		pdata = kim_data->kim_pdev->dev.platform_data;
> +	}
> +
>  	if (pdata->chip_awake)
>  		pdata->chip_awake(NULL);
>  }
> diff --git a/include/linux/ti_wilink_st.h b/include/linux/ti_wilink_st.h index
> 0a0d568..e01e530 100644
> --- a/include/linux/ti_wilink_st.h
> +++ b/include/linux/ti_wilink_st.h
> @@ -86,6 +86,7 @@ struct st_proto_s {
>  extern long st_register(struct st_proto_s *);  extern long st_unregister(struct
> st_proto_s *);
> 
> +extern struct ti_st_plat_data   *dt_pdata;
> 
>  /*
>   * header information used by st_core.c @@ -206,7 +207,7 @@ void
> gps_chrdrv_stub_init(void);
>  /* time in msec to wait for
>   * line discipline to be installed
>   */
> -#define LDISC_TIME	1000
> +#define LDISC_TIME	1500
>  #define CMD_RESP_TIME	800
>  #define CMD_WR_TIME	5000
>  #define MAKEWORD(a, b)  ((unsigned short)(((unsigned char)(a)) \ @@ -
> 231,7 +232,6 @@ struct chip_version {
>  	unsigned short maj_ver;
>  };
> 
> -#define UART_DEV_NAME_LEN 32
>  /**
>   * struct kim_data_s - the KIM internal data, embedded as the
>   *	platform's drv data. One for each ST device in the system.
> @@ -262,14 +262,14 @@ struct kim_data_s {
>  	struct completion kim_rcvd, ldisc_installed;
>  	char resp_buffer[30];
>  	const struct firmware *fw_entry;
> -	unsigned nshutdown;
> +	struct gpio_desc *nshutdown;
>  	unsigned long rx_state;
>  	unsigned long rx_count;
>  	struct sk_buff *rx_skb;
>  	struct st_data_s *core_data;
>  	struct chip_version version;
>  	unsigned char ldisc_install;
> -	unsigned char dev_name[UART_DEV_NAME_LEN + 1];
> +	unsigned dev_addr;
>  	unsigned flow_cntrl;
>  	unsigned baud_rate;
>  };
> @@ -418,7 +418,7 @@ struct gps_event_hdr {
>   * struct ti_st_plat_data - platform data shared between ST driver and
>   *	platform specific board file which adds the ST device.
>   * @nshutdown_gpio: Host's GPIO line to which chip's BT_EN is connected.
> - * @dev_name: The UART/TTY name to which chip is interfaced. (eg:
> /dev/ttyS1)
> + * @dev_addr: Memory address of UART peripheral to which chip is
> + interfaced
>   * @flow_cntrl: Should always be 1, since UART's CTS/RTS is used for PM
>   *	purposes.
>   * @baud_rate: The baud rate supported by the Host UART controller, this
> will @@ -437,8 +437,7 @@ struct gps_event_hdr {
>   *
>   */
>  struct ti_st_plat_data {
> -	u32 nshutdown_gpio;
> -	unsigned char dev_name[UART_DEV_NAME_LEN]; /* uart name */
> +	u32 dev_addr; /* uart address */
>  	u32 flow_cntrl; /* flow control flag */
>  	u32 baud_rate;
>  	int (*suspend)(struct platform_device *, pm_message_t);
> --
> 1.7.9.5

��.n��������+%������w��{.n����z�{��ܨ}���Ơz�j:+v�����w����ޙ��&�)ߡ�a����z�ޗ���ݢj��w�f




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]
  Powered by Linux